From b156509da1b01dd4435f211809317081f2fb92ff Mon Sep 17 00:00:00 2001 From: Rick Rongen Date: Thu, 4 Feb 2021 23:15:02 +0100 Subject: [PATCH] first version of ide controller --- kernel/cpu/timer.c | 7 + kernel/cpu/timer.h | 1 + kernel/drivers/ide.c | 397 ++++++++++++++++++++++++++++++++++++++++- kernel/drivers/pci.c | 154 +++++++++------- kernel/drivers/pci.h | 64 ++++++- kernel/drivers/ports.c | 14 ++ kernel/drivers/ports.h | 3 + 7 files changed, 572 insertions(+), 68 deletions(-) diff --git a/kernel/cpu/timer.c b/kernel/cpu/timer.c index 407cde3..8fe967f 100644 --- a/kernel/cpu/timer.c +++ b/kernel/cpu/timer.c @@ -37,6 +37,13 @@ static void timer_callback(registers_t regs) { tick++; } +void sleep(u32 milliseconds) { + u32 done = tick + milliseconds; + while (done != milliseconds) { + k_wait_for_interrupt(); + } +} + void print_current_tick() { char msg[32]; memset(msg, 0, 32); diff --git a/kernel/cpu/timer.h b/kernel/cpu/timer.h index 5bdc92a..d5ccd75 100644 --- a/kernel/cpu/timer.h +++ b/kernel/cpu/timer.h @@ -9,5 +9,6 @@ int init_timer(u32 freq); void print_current_tick(); +void sleep(u32 milliseconds); #endif //MY_KERNEL_TIMER_H diff --git a/kernel/drivers/ide.c b/kernel/drivers/ide.c index 1404ce6..b683495 100644 --- a/kernel/drivers/ide.c +++ b/kernel/drivers/ide.c @@ -1,17 +1,279 @@ // // Created by rick on 03-02-21. // +// https://wiki.osdev.org/PCI_IDE_Controller #include "ide.h" +#include "ports.h" #include #include #include #include +#include +#include +#include -const char* ide_pci_driver_name = "pci-ide"; +#define ATA_SR_BSY 0x80 // Busy +#define ATA_SR_DRDY 0x40 // Drive ready +#define ATA_SR_DF 0x20 // Drive write fault +#define ATA_SR_DSC 0x10 // Drive seek complete +#define ATA_SR_DRQ 0x08 // Data request ready +#define ATA_SR_CORR 0x04 // Corrected data +#define ATA_SR_IDX 0x02 // Index +#define ATA_SR_ERR 0x01 // Error + +#define ATA_ER_BBK 0x80 // Bad block +#define ATA_ER_UNC 0x40 // Uncorrectable data +#define ATA_ER_MC 0x20 // Media changed +#define ATA_ER_IDNF 0x10 // ID mark not found +#define ATA_ER_MCR 0x08 // Media change request +#define ATA_ER_ABRT 0x04 // Command aborted +#define ATA_ER_TK0NF 0x02 // Track 0 not found +#define ATA_ER_AMNF 0x01 // No address mark + +#define ATA_CMD_READ_PIO 0x20 +#define ATA_CMD_READ_PIO_EXT 0x24 +#define ATA_CMD_READ_DMA 0xC8 +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_WRITE_PIO 0x30 +#define ATA_CMD_WRITE_PIO_EXT 0x34 +#define ATA_CMD_WRITE_DMA 0xCA +#define ATA_CMD_WRITE_DMA_EXT 0x35 +#define ATA_CMD_CACHE_FLUSH 0xE7 +#define ATA_CMD_CACHE_FLUSH_EXT 0xEA +#define ATA_CMD_PACKET 0xA0 +#define ATA_CMD_IDENTIFY_PACKET 0xA1 +#define ATA_CMD_IDENTIFY 0xEC + +#define ATAPI_CMD_READ 0xA8 +#define ATAPI_CMD_EJECT 0x1B + +#define ATA_IDENT_DEVICETYPE 0 +#define ATA_IDENT_CYLINDERS 2 +#define ATA_IDENT_HEADS 6 +#define ATA_IDENT_SECTORS 12 +#define ATA_IDENT_SERIAL 20 +#define ATA_IDENT_MODEL 54 +#define ATA_IDENT_CAPABILITIES 98 +#define ATA_IDENT_FIELDVALID 106 +#define ATA_IDENT_MAX_LBA 120 +#define ATA_IDENT_COMMANDSETS 164 +#define ATA_IDENT_MAX_LBA_EXT 200 + +#define IDE_ATA 0x00 +#define IDE_ATAPI 0x01 + +#define ATA_MASTER 0x00 +#define ATA_SLAVE 0x01 + +#define ATA_REG_DATA 0x00 +#define ATA_REG_ERROR 0x01 +#define ATA_REG_FEATURES 0x01 +#define ATA_REG_SECCOUNT0 0x02 +#define ATA_REG_LBA0 0x03 +#define ATA_REG_LBA1 0x04 +#define ATA_REG_LBA2 0x05 +#define ATA_REG_HDDEVSEL 0x06 +#define ATA_REG_COMMAND 0x07 +#define ATA_REG_STATUS 0x07 +#define ATA_REG_SECCOUNT1 0x08 +#define ATA_REG_LBA3 0x09 +#define ATA_REG_LBA4 0x0A +#define ATA_REG_LBA5 0x0B +#define ATA_REG_CONTROL 0x0C +#define ATA_REG_ALTSTATUS 0x0C +#define ATA_REG_DEVADDRESS 0x0D + +// Channels: +#define ATA_PRIMARY 0x00 +#define ATA_SECONDARY 0x01 + +// Directions: +#define ATA_READ 0x00 +#define ATA_WRITE 0x01 + +struct ide_channel_registers { + unsigned short base; // I/O Base. + unsigned short ctrl; // Control Base + unsigned short bmide; // Bus Master IDE + unsigned char nIEN; // nIEN (No Interrupt); +} channels[2]; + +unsigned char ide_buf[2048] = {0}; +unsigned static char ide_irq_invoked = 0; +unsigned static char atapi_packet[12] = {0xA8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +struct ide_device { + unsigned char reserved; // 0 (Empty) or 1 (This Drive really exists). + unsigned char channel; // 0 (Primary Channel) or 1 (Secondary Channel). + unsigned char drive; // 0 (Master Drive) or 1 (Slave Drive). + unsigned short type; // 0: ATA, 1:ATAPI. + unsigned short signature; // Drive Signature + unsigned short capabilities;// Features. + unsigned int commandSets; // Command Sets Supported. + unsigned int size; // Size in Sectors. + unsigned char model[41]; // Model in string. +} ide_devices[4]; + +u8 ide_read(u8 channel, u8 reg); + +void ide_write(u8 channel, u8 reg, u8 data); + +u8 ide_read(u8 channel, u8 reg) { + u8 result; + if (reg > 0x07 && reg < 0x0C) + ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN); + if (reg < 0x08) + result = port_byte_in(channels[channel].base + reg - 0x00); + else if (reg < 0x0C) + result = port_byte_in(channels[channel].base + reg - 0x06); + else if (reg < 0x0E) + result = port_byte_in(channels[channel].ctrl + reg - 0x0A); + else if (reg < 0x16) + result = port_byte_in(channels[channel].bmide + reg - 0x0E); + if (reg > 0x07 && reg < 0x0C) + ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); + return result; +} + +void ide_write(u8 channel, u8 reg, u8 data) { + if (reg > 0x07 && reg < 0x0C) + ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN); + if (reg < 0x08) + port_byte_out(channels[channel].base + reg - 0x00, data); + else if (reg < 0x0C) + port_byte_out(channels[channel].base + reg - 0x06, data); + else if (reg < 0x0E) + port_byte_out(channels[channel].ctrl + reg - 0x0A, data); + else if (reg < 0x16) + port_byte_out(channels[channel].bmide + reg - 0x0E, data); + if (reg > 0x07 && reg < 0x0C) + ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); +} + +void ide_read_buffer(unsigned char channel, unsigned char reg, unsigned int* buffer, unsigned int quads) { + /* WARNING: This code contains a serious bug. The inline assembly trashes ES and + * ESP for all of the code the compiler generates between the inline + * assembly blocks. + */ + if (reg > 0x07 && reg < 0x0C) { + ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN); + } +// asm("pushw %es; movw %ds, %ax; movw %ax, %es" : : : "es", "esp"); + if (reg < 0x08) { + port_double_word_in_repeat(channels[channel].base + reg - 0x00, buffer, quads); + } else if (reg < 0x0C) { + port_double_word_in_repeat(channels[channel].base + reg - 0x06, buffer, quads); + } else if (reg < 0x0E) { + port_double_word_in_repeat(channels[channel].ctrl + reg - 0x0A, buffer, quads); + } else if (reg < 0x16) { + port_double_word_in_repeat(channels[channel].bmide + reg - 0x0E, buffer, quads); + } +// asm("popw %es;"); + if (reg > 0x07 && reg < 0x0C) { + ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); + } +} + +unsigned char ide_polling(unsigned char channel, unsigned int advanced_check) { + + // (I) Delay 400 nanosecond for BSY to be set: + // ------------------------------------------------- + for (int i = 0; i < 4; i++) + ide_read(channel, ATA_REG_ALTSTATUS); // Reading the Alternate Status port wastes 100ns; loop four times. + + // (II) Wait for BSY to be cleared: + // ------------------------------------------------- + while (ide_read(channel, ATA_REG_STATUS) & ATA_SR_BSY); // Wait for BSY to be zero. + + if (advanced_check) { + unsigned char state = ide_read(channel, ATA_REG_STATUS); // Read Status Register. + + // (III) Check For Errors: + // ------------------------------------------------- + if (state & ATA_SR_ERR) + return 2; // Error. + + // (IV) Check If Device fault: + // ------------------------------------------------- + if (state & ATA_SR_DF) + return 1; // Device Fault. + + // (V) Check DRQ: + // ------------------------------------------------- + // BSY = 0; DF = 0; ERR = 0 so we should check for DRQ now. + if ((state & ATA_SR_DRQ) == 0) + return 3; // DRQ should be set + + } + + return 0; // No Error. + +} + +unsigned char ide_print_error(unsigned int drive, unsigned char err) { + if (err == 0) + return err; + + kprint("IDE:"); + if (err == 1) { + kprint("- Device Fault\n "); + err = 19; + } else if (err == 2) { + unsigned char st = ide_read(ide_devices[drive].channel, ATA_REG_ERROR); + if (st & ATA_ER_AMNF) { + kprint("- No Address Mark Found\n "); + err = 7; + } + if (st & ATA_ER_TK0NF) { + kprint("- No Media or Media Error\n "); + err = 3; + } + if (st & ATA_ER_ABRT) { + kprint("- Command Aborted\n "); + err = 20; + } + if (st & ATA_ER_MCR) { + kprint("- No Media or Media Error\n "); + err = 3; + } + if (st & ATA_ER_IDNF) { + kprint("- ID mark not Found\n "); + err = 21; + } + if (st & ATA_ER_MC) { + kprint("- No Media or Media Error\n "); + err = 3; + } + if (st & ATA_ER_UNC) { + kprint("- Uncorrectable Data Error\n "); + err = 22; + } + if (st & ATA_ER_BBK) { + kprint("- Bad Sectors\n "); + err = 13; + } + } else if (err == 3) { + kprint("- Reads Nothing\n "); + err = 23; + } else if (err == 4) { + kprint("- Write Protected\n "); + err = 8; + } + kprint(" - ["); + kprint((const char *[]) {"Primary", "Secondary"}[ide_devices[drive].channel]); + kprint(" "); + kprint((const char *[]) {"Master", "Slave"}[ide_devices[drive].drive]); + kprint("] "); + kprint(&ide_devices[drive].model); + kprint("\n"); + + return err; +} u8 ide_pci_validate(const pci_device *device); -u8 ide_pci_initialize(const pci_device *device); + +u8 ide_pci_initialize(pci_device *device); const pci_driver ide_pci_driver = { .name = "pci-ide", @@ -27,16 +289,139 @@ const pci_driver ide_pci_driver = { u8 ide_pci_validate(const pci_device *device) { if (device->class != PCI_CLASS_MASS_STORAGE - || device->subclass != PCI_SUB_CLASS_IDE) { + || device->subclass != PCI_SUB_CLASS_IDE + || (device->programInterface != 0x8A && device->programInterface != 0x80)) { return PCI_VALIDATE_FAIL; } // todo other validations return PCI_VALIDATE_OK; } -u8 ide_pci_initialize(const pci_device *device) { - kprint("IDE registered"); - return PCI_VALIDATE_OK; +u8 ide_pci_initialize(pci_device *device) { + pci_config_write_byte(device->bus, device->slot, device->func, PCI_CONFIG_INTERRUPT_LINE, 0xFE); + if (pci_config_read_byte(device->bus, device->slot, device->func, PCI_CONFIG_INTERRUPT_LINE) == 0xFE) { + // todo +// k_panics("Interrupt line"); +// return PCI_INIT_FAIL; + } + + pci_init_bar(device, 0); + pci_init_bar(device, 1); + pci_init_bar(device, 2); + pci_init_bar(device, 3); + pci_init_bar(device, 4); + + if (!device->bar0.present + || !device->bar1.present + || !device->bar2.present + || !device->bar3.present + || !device->bar4.present) { + k_panics("IDE Missing bars"); + return PCI_INIT_FAIL; + } + + channels[ATA_PRIMARY].base = device->bar0.address + 0x1F0; + channels[ATA_PRIMARY].ctrl = device->bar1.address + 0x3F6; + channels[ATA_SECONDARY].base = device->bar2.address + 0x170; + channels[ATA_SECONDARY].ctrl = device->bar3.address + 0x376; + channels[ATA_PRIMARY].bmide = device->bar4.address + 0; // Bus Master IDE + channels[ATA_SECONDARY].bmide = device->bar4.address + 8; // Bus Master IDE + + // disable IRQ + ide_write(ATA_PRIMARY, ATA_REG_CONTROL, 2); + ide_write(ATA_SECONDARY, ATA_REG_CONTROL, 2); + + int count = 0; +// 3- Detect ATA-ATAPI Devices: + for (int i = 0; i < 2; i++) + for (int j = 0; j < 2; j++) { + + unsigned char err = 0, type = IDE_ATA, status; + ide_devices[count].reserved = 0; // Assuming that no drive here. + + // (I) Select Drive: + ide_write(i, ATA_REG_HDDEVSEL, 0xA0 | (j << 4)); // Select Drive. + sleep(1); // Wait 1ms for drive select to work. + + // (II) Send ATA Identify Command: + ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY); + sleep(1); // This function should be implemented in your OS. which waits for 1 ms. + // it is based on System Timer Device Driver. + + // (III) Polling: + if (ide_read(i, ATA_REG_STATUS) == 0) continue; // If Status = 0, No Device. + + while (1) { + status = ide_read(i, ATA_REG_STATUS); + if ((status & ATA_SR_ERR)) { + err = 1; + break; + } // If Err, Device is not ATA. + if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break; // Everything is right. + } + + // (IV) Probe for ATAPI Devices: + + if (err != 0) { + unsigned char cl = ide_read(i, ATA_REG_LBA1); + unsigned char ch = ide_read(i, ATA_REG_LBA2); + + if (cl == 0x14 && ch == 0xEB) + type = IDE_ATAPI; + else if (cl == 0x69 && ch == 0x96) + type = IDE_ATAPI; + else + continue; // Unknown Type (may not be a device). + + ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET); + sleep(1); + } + + // (V) Read Identification Space of the Device: + ide_read_buffer(i, ATA_REG_DATA, (unsigned int*) ide_buf, 128); + + // (VI) Read Device Parameters: + ide_devices[count].reserved = 1; + ide_devices[count].type = type; + ide_devices[count].channel = i; + ide_devices[count].drive = j; + ide_devices[count].signature = *((unsigned short *) (ide_buf + ATA_IDENT_DEVICETYPE)); + ide_devices[count].capabilities = *((unsigned short *) (ide_buf + ATA_IDENT_CAPABILITIES)); + ide_devices[count].commandSets = *((unsigned int *) (ide_buf + ATA_IDENT_COMMANDSETS)); + + // (VII) Get Size: + if (ide_devices[count].commandSets & (1 << 26)) + // Device uses 48-Bit Addressing: + ide_devices[count].size = *((unsigned int *) (ide_buf + ATA_IDENT_MAX_LBA_EXT)); + else + // Device uses CHS or 28-bit Addressing: + ide_devices[count].size = *((unsigned int *) (ide_buf + ATA_IDENT_MAX_LBA)); + + // (VIII) String indicates model of device (like Western Digital HDD and SONY DVD-RW...): + for (int k = 0; k < 40; k += 2) { + ide_devices[count].model[k] = ide_buf[ATA_IDENT_MODEL + k + 1]; + ide_devices[count].model[k + 1] = ide_buf[ATA_IDENT_MODEL + k]; + } + ide_devices[count].model[40] = 0; // Terminate String. + + count++; + } + + // 4- Print Summary: + for (int i = 0; i < 4; i++) { + if (ide_devices[i].reserved == 1) { + char tmp[64]; + itoa(ide_devices[i].size / 1024 / 1024 / 2, tmp, 10); + kprint("Found "); + kprint((const char *[]) {"ATA", "ATAPI"}[ide_devices[i].type]); + kprint(" Drive "); + kprint(tmp); + kprint(" - "); + kprint(ide_devices[i].model); + kprint("\n"); + } + } + return PCI_INIT_OK; } void ide_register() { diff --git a/kernel/drivers/pci.c b/kernel/drivers/pci.c index c53d416..abad59e 100644 --- a/kernel/drivers/pci.c +++ b/kernel/drivers/pci.c @@ -1,6 +1,7 @@ // // Created by rick on 02-02-21. // +// https://wiki.osdev.org/PCI #include "pci.h" @@ -17,42 +18,17 @@ #define PCI_CONFIG_SHIFT_FUNC_NUMBER 8 #define PCI_CONFIG_LINE_0 0x00 -#define PCI_CONFIG_VENDOR_ID 0x00 -#define PCI_CONFIG_DEVICE_ID 0x02 #define PCI_CONFIG_LINE_1 0x01 -#define PCI_CONFIG_COMMAND 0x04 -#define PCI_CONFIG_STATUS 0x06 #define PCI_CONFIG_LINE_2 0x08 -#define PCI_CONFIG_REVISION_ID 0x08 -#define PCI_CONFIG_PROG_IF 0x09 -#define PCI_CONFIG_SUBCLASS 0x0A -#define PCI_CONFIG_CLASS_CODE 0x0B #define PCI_CONFIG_LINE_3 0x0C -#define PCI_CONFIG_CACHE_LINE_SIZE 0x0C -#define PCI_CONFIG_LATENCY_TIMER 0x0D -#define PCI_CONFIG_HEADER_TYPE 0x0E -#define PCI_CONFIG_BIST 0x0F -#define PCI_CONFIG_BAR0 0x10 -#define PCI_CONFIG_BAR1 0x14 -#define PCI_CONFIG_BAR2 0x18 -#define PCI_CONFIG_BAR3 0x1C -#define PCI_CONFIG_BAR4 0x20 -#define PCI_CONFIG_BAR5 0x24 -#define PCI_CONFIG_CARDBUS_CIS_P 0x28 -#define PCI_CONFIG_SUBSYSTEM_VENDOR_ID 0x2C -#define PCI_CONFIG_SUBSYSTEM_ID 0x2E -#define PCI_CONFIG_EXPANSION_ROM_ADDR 0x30 -#define PCI_CONFIG_CAP_POINTER 0x34 -#define PCI_CONFIG_INTERRUPT_LINE 0x3C -#define PCI_CONFIG_INTERRUPT_PIN 0x3D -#define PCI_CONFIG_MAX_GRANT 0x3E -#define PCI_CONFIG_MAX_LATENCY 0x3F -#define PCI_HEADER_TYPE_MULTI_FUNC 0x80 #define MAX_PCI_DRIVERS 64 #define MAX_PCI_DEVICES 64 +#define MASK_BAR_IOSPACE 0xFFFFFFFC +#define MASK_BAR_MEMSPACE 0xFFFFFFF0 + const pci_driver *pci_drivers[MAX_PCI_DRIVERS]; int last_pci_device_index = 0; pci_device pci_devices[MAX_PCI_DEVICES]; @@ -68,61 +44,58 @@ u32 pci_register_driver(const pci_driver *pci_driver) { return PCI_REGISTER_ERR_FULL; } +u32 pci_config_address(u8 bus, u8 slot, u8 func, u8 offset) { + return PCI_CONFIG_ENABLE + | ((u32) bus << PCI_CONFIG_SHIFT_BUS_NUMBER) + | ((u32) slot << PCI_CONFIG_SHIFT_DEV_NUMBER) + | ((u32) func << PCI_CONFIG_SHIFT_FUNC_NUMBER) + | (offset & 0xFC); +} u32 pci_config_read_double_word(u8 bus, u8 slot, u8 func, u8 offset) { - u32 address = PCI_CONFIG_ENABLE - | ((u32) bus << PCI_CONFIG_SHIFT_BUS_NUMBER) - | ((u32) slot << PCI_CONFIG_SHIFT_DEV_NUMBER) - | ((u32) func << PCI_CONFIG_SHIFT_FUNC_NUMBER) - | (offset & 0xFC); + u32 address = pci_config_address(bus, slot, func, offset); port_double_word_out(PORT_PCI_CONFIG_ADDRESS, address); return port_double_word_in(PORT_PCI_CONFIG_DATA); } u16 pci_config_read_word(u8 bus, u8 slot, u8 func, u8 offset) { - u32 address = PCI_CONFIG_ENABLE - | ((u32) bus << PCI_CONFIG_SHIFT_BUS_NUMBER) - | ((u32) slot << PCI_CONFIG_SHIFT_DEV_NUMBER) - | ((u32) func << PCI_CONFIG_SHIFT_FUNC_NUMBER) - | (offset & 0xFC); + u32 address = pci_config_address(bus, slot, func, offset); port_double_word_out(PORT_PCI_CONFIG_ADDRESS, address); return port_double_word_in(PORT_PCI_CONFIG_DATA) >> ((offset & 2) * 8) & 0xFFFF; } u8 pci_config_read_byte(u8 bus, u8 slot, u8 func, u8 offset) { - u32 address = PCI_CONFIG_ENABLE - | ((u32) bus << PCI_CONFIG_SHIFT_BUS_NUMBER) - | ((u32) slot << PCI_CONFIG_SHIFT_DEV_NUMBER) - | ((u32) func << PCI_CONFIG_SHIFT_FUNC_NUMBER) - | (offset & 0xFC); + u32 address = pci_config_address(bus, slot, func, offset); port_double_word_out(PORT_PCI_CONFIG_ADDRESS, address); return port_double_word_in(PORT_PCI_CONFIG_DATA) >> ((offset & 0b11) * 8) & 0xFF; } +void pci_config_write_double_word(u8 bus, u8 slot, u8 func, u8 offset, u32 value) { + u32 address = pci_config_address(bus, slot, func, offset); + port_double_word_out(PORT_PCI_CONFIG_ADDRESS, address); + port_double_word_out(PORT_PCI_CONFIG_DATA, value); +} + +void pci_config_write_word(u8 bus, u8 slot, u8 func, u8 offset, u16 value) { + u32 address = pci_config_address(bus, slot, func, offset); + port_double_word_out(PORT_PCI_CONFIG_ADDRESS, address); + port_word_out(PORT_PCI_CONFIG_DATA, value); +} + +void pci_config_write_byte(u8 bus, u8 slot, u8 func, u8 offset, u8 value) { + u32 address = pci_config_address(bus, slot, func, offset); + port_double_word_out(PORT_PCI_CONFIG_ADDRESS, address); + port_byte_out(PORT_PCI_CONFIG_DATA, value); +} + u16 pci_get_vendor_id(u8 bus, u8 slot, u8 func) { return pci_config_read_word(bus, slot, func, PCI_CONFIG_VENDOR_ID); } -u16 pci_get_device_id(u8 bus, u8 slot, u8 func) { - return pci_config_read_word(bus, slot, func, PCI_CONFIG_DEVICE_ID); -} - -u8 pci_get_prog_if(u8 bus, u8 slot, u8 func) { - return pci_config_read_byte(bus, slot, func, PCI_CONFIG_PROG_IF); -} - u8 pci_get_header_type(u8 bus, u8 slot, u8 func) { return pci_config_read_byte(bus, slot, func, PCI_CONFIG_HEADER_TYPE); } -u8 pci_get_class_code(u8 bus, u8 slot, u8 func) { - return pci_config_read_byte(bus, slot, func, PCI_CONFIG_CLASS_CODE); -} - -u8 pci_get_subclass_code(u8 bus, u8 slot, u8 func) { - return pci_config_read_byte(bus, slot, func, PCI_CONFIG_SUBCLASS); -} - void print_u8(u8 val) { char buf[3]; itoa(val, buf, 16); @@ -187,6 +160,7 @@ void pci_check_bus(u8 bus) { pci_check_device(bus, device); } } + void pci_sort_drivers() { // todo } @@ -253,6 +227,64 @@ void pci_print_info() { } } -// todo https://wiki.osdev.org/PCI -// todo https://wiki.osdev.org/PCI_IDE_Controller +void pci_init_bar(pci_device *device, u8 bar_index) { + if (device->headerType != 0x00) { + k_panics("Only header 0x00 supported for now"); + return; + } + u8 offset = 0; + bar_info *bar; + switch (bar_index) { + case 0: + offset = PCI_CONFIG_BAR0; + bar = &device->bar0; + break; + case 1: + offset = PCI_CONFIG_BAR1; + bar = &device->bar1; + break; + case 2: + offset = PCI_CONFIG_BAR2; + bar = &device->bar2; + break; + case 3: + offset = PCI_CONFIG_BAR3; + bar = &device->bar3; + break; + case 4: + offset = PCI_CONFIG_BAR4; + bar = &device->bar4; + break; + case 5: + offset = PCI_CONFIG_BAR5; + bar = &device->bar5; + break; + default: + k_panics("Bar index too high"); + return; + } + + + u32 original_address = pci_config_read_double_word(device->bus, device->slot, device->func, offset); + pci_config_write_double_word(device->bus, device->slot, device->func, offset, 0xFFFFFFFF); + u32 masked_size = pci_config_read_double_word(device->bus, device->slot, device->func, offset); + + if (original_address & 0x1) { + // IO Space + bar->size = ~(masked_size & MASK_BAR_IOSPACE) + 1; + bar->address = original_address & MASK_BAR_IOSPACE; + bar->is_io_space = 1; + } else { + // Memory space + bar->size = ~(masked_size & MASK_BAR_MEMSPACE) + 1; + bar->address = original_address & 0xFFFFFFF0; + bar->is_io_space = 0; + bar->type = (original_address & 0b110) >> 1; + bar->prefetchable = (original_address & 0b111000) >> 3; + } + // todo identify conflicts, move to different address + pci_config_write_double_word(device->bus, device->slot, device->func, offset, original_address); + bar->present = 1; +} + // todo https://wiki.osdev.org/Universal_Serial_Bus if i dare \ No newline at end of file diff --git a/kernel/drivers/pci.h b/kernel/drivers/pci.h index 5abe0c7..2025cf5 100644 --- a/kernel/drivers/pci.h +++ b/kernel/drivers/pci.h @@ -21,11 +21,45 @@ #define PCI_INIT_OK 0 #define PCI_INIT_FAIL 1 +#define PCI_HEADER_TYPE_MULTI_FUNC 0x80 +#define PCI_HEADER_TYPE_ENDPOINT 0x00 +#define PCI_HEADER_TYPE_PCI_PCI_BRIDGE 0x01 +#define PCI_HEADER_TYPE_PCI_CARDBUS_BRIDGE 0x02 + + +#define PCI_CONFIG_VENDOR_ID 0x00 +#define PCI_CONFIG_DEVICE_ID 0x02 +#define PCI_CONFIG_COMMAND 0x04 +#define PCI_CONFIG_STATUS 0x06 +#define PCI_CONFIG_REVISION_ID 0x08 +#define PCI_CONFIG_PROG_IF 0x09 +#define PCI_CONFIG_SUBCLASS 0x0A +#define PCI_CONFIG_CLASS_CODE 0x0B +#define PCI_CONFIG_CACHE_LINE_SIZE 0x0C +#define PCI_CONFIG_LATENCY_TIMER 0x0D +#define PCI_CONFIG_HEADER_TYPE 0x0E +#define PCI_CONFIG_BIST 0x0F +#define PCI_CONFIG_BAR0 0x10 +#define PCI_CONFIG_BAR1 0x14 +#define PCI_CONFIG_BAR2 0x18 +#define PCI_CONFIG_BAR3 0x1C +#define PCI_CONFIG_BAR4 0x20 +#define PCI_CONFIG_BAR5 0x24 +#define PCI_CONFIG_CARDBUS_CIS_P 0x28 +#define PCI_CONFIG_SUBSYSTEM_VENDOR_ID 0x2C +#define PCI_CONFIG_SUBSYSTEM_ID 0x2E +#define PCI_CONFIG_EXPANSION_ROM_ADDR 0x30 +#define PCI_CONFIG_CAP_POINTER 0x34 +#define PCI_CONFIG_INTERRUPT_LINE 0x3C +#define PCI_CONFIG_INTERRUPT_PIN 0x3D +#define PCI_CONFIG_MAX_GRANT 0x3E +#define PCI_CONFIG_MAX_LATENCY 0x3F + typedef struct pci_driver pci_driver; typedef struct pci_device pci_device; typedef u8 (*pci_driver_validate)(const pci_device *); -typedef u8 (*pci_driver_initialize)(const pci_device *); +typedef u8 (*pci_driver_initialize)(pci_device *); typedef struct pci_driver { const char *name; @@ -40,6 +74,15 @@ typedef struct pci_driver { pci_driver_initialize initialize; } pci_driver; +typedef struct { + u32 address; + u32 size; + u8 present: 1; + u8 is_io_space: 1; + u8 type: 2; + u8 prefetchable: 3; +} bar_info; + typedef struct pci_device { u8 bus; u8 slot; @@ -69,6 +112,12 @@ typedef struct pci_device { }; u32 config_line_3; }; + bar_info bar0; + bar_info bar1; + bar_info bar2; + bar_info bar3; + bar_info bar4; + bar_info bar5; const pci_driver *pci_driver; struct { u8 present: 1; @@ -88,4 +137,17 @@ void pci_init_drivers(); void pci_scan(); +u32 pci_config_read_double_word(u8 bus, u8 slot, u8 func, u8 offset); + +u16 pci_config_read_word(u8 bus, u8 slot, u8 func, u8 offset); + +u8 pci_config_read_byte(u8 bus, u8 slot, u8 func, u8 offset); + +void pci_config_write_double_word(u8 bus, u8 slot, u8 func, u8 offset, u32 value); + +void pci_config_write_word(u8 bus, u8 slot, u8 func, u8 offset, u16 value); + +void pci_config_write_byte(u8 bus, u8 slot, u8 func, u8 offset, u8 value); + +void pci_init_bar(pci_device *device, u8 bar_index); #endif //NEW_KERNEL_PCI_H diff --git a/kernel/drivers/ports.c b/kernel/drivers/ports.c index 4c7ad14..75172aa 100644 --- a/kernel/drivers/ports.c +++ b/kernel/drivers/ports.c @@ -40,3 +40,17 @@ unsigned int port_double_word_in(unsigned int port) { void port_double_word_out(unsigned short port, unsigned int data) { __asm__("out %%eax, %%dx" : : "a" (data), "d" (port)); } + +void port_word_in_repeat(unsigned short port, unsigned short *data, int buffer_size) { + asm("rep insw" + : "+D"(data), "+c"(buffer_size) + : "d"(port) + : "memory"); +} + +void port_double_word_in_repeat(unsigned short port, unsigned int *data, int buffer_size) { + asm("rep insl" + : "+D"(data), "+c"(buffer_size) + : "d"(port) + : "memory"); +} \ No newline at end of file diff --git a/kernel/drivers/ports.h b/kernel/drivers/ports.h index 28f341e..d5edcd4 100644 --- a/kernel/drivers/ports.h +++ b/kernel/drivers/ports.h @@ -56,4 +56,7 @@ unsigned int port_double_word_in(unsigned int port); void port_double_word_out(unsigned short port, unsigned int data); +void port_word_in_repeat(unsigned short port, unsigned short *data, int buffer_size); + +void port_double_word_in_repeat(unsigned short port, unsigned int *data, int buffer_size); #endif