From a4651ca9d93d633c6a5e372faeaab41f68f99126 Mon Sep 17 00:00:00 2001 From: Rick Rongen Date: Fri, 5 Feb 2021 23:23:26 +0100 Subject: [PATCH] feat: implemented ide read write operations --- CMakeLists.txt | 2 + kernel/cpu/timer.c | 1 + kernel/drivers/ide.c | 321 +++++++++++++++++++++++++++++++++-------- kernel/drivers/ide.h | 4 + kernel/drivers/pci.c | 2 +- kernel/drivers/pci.h | 4 +- kernel/drivers/ports.c | 7 + kernel/drivers/ports.h | 2 + kernel/kernel.c | 5 +- 9 files changed, 286 insertions(+), 62 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a873ef1..e85b298 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,8 @@ FILE(GLOB_RECURSE kernel_src kernel/**.c) FILE(GLOB_RECURSE kernel_asm kernel/**.S) FILE(GLOB_RECURSE boot_asm boot/boot.S) +#add_compile_definitions(IDE_ENABLE_INTERRUPT) + add_executable(my-kernel.bin ${kernel_src} ${kernel_asm} ${boot_asm}) set_source_files_properties(${kernel_src} PROPERTIES LANGUAGE C COMPILE_FLAGS "") set_target_properties(my-kernel.bin PROPERTIES LINKER_LANGUAGE C PREFIX "" SUFFIX "" LINK_FLAGS "") diff --git a/kernel/cpu/timer.c b/kernel/cpu/timer.c index 8fe967f..fb7fe1b 100644 --- a/kernel/cpu/timer.c +++ b/kernel/cpu/timer.c @@ -7,6 +7,7 @@ #include #include #include +#include #include // https://wiki.osdev.org/PIT diff --git a/kernel/drivers/ide.c b/kernel/drivers/ide.c index b683495..499d3be 100644 --- a/kernel/drivers/ide.c +++ b/kernel/drivers/ide.c @@ -60,12 +60,21 @@ #define ATA_IDENT_COMMANDSETS 164 #define ATA_IDENT_MAX_LBA_EXT 200 +#define ATA_CAP_LBA 0x200 + +#define ATA_LBA28_MAX 0x10000000 + +#define ATA_HDDDEVSEL_CHS 0b00000000 +#define ATA_HDDDEVSEL_LBA 0b01000000 +#define ATA_HDDDEVSEL_DEFAULT 0b10100000 + #define IDE_ATA 0x00 #define IDE_ATAPI 0x01 #define ATA_MASTER 0x00 #define ATA_SLAVE 0x01 +// base #define ATA_REG_DATA 0x00 #define ATA_REG_ERROR 0x01 #define ATA_REG_FEATURES 0x01 @@ -76,13 +85,17 @@ #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 + +// ?? +#define ATA_REG_SECCOUNT1 0x12 +#define ATA_REG_LBA3 0x13 +#define ATA_REG_LBA4 0x14 +#define ATA_REG_LBA5 0x15 + +// ctrl +#define ATA_REG_CONTROL 0x22 +#define ATA_REG_ALTSTATUS 0x22 +#define ATA_REG_DEVADDRESS 0x23 // Channels: #define ATA_PRIMARY 0x00 @@ -121,61 +134,87 @@ void ide_write(u8 channel, u8 reg, u8 data); u8 ide_read(u8 channel, u8 reg) { u8 result; - if (reg > 0x07 && reg < 0x0C) + if (reg & 0x10) { 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) + result = port_byte_in(channels[channel].base + (reg & 0xF)); ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); + } else if (reg & 0x20) { + result = port_byte_in(channels[channel].ctrl + (reg & 0xF)); + } else { + result = port_byte_in(channels[channel].base + reg); + } +// 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); // todo this case is not handled in new code +// 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) + if (reg & 0x10) { 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) + port_byte_out(channels[channel].base + (reg & 0xF), data); ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); + } else if (reg & 0x20) { + port_byte_out(channels[channel].ctrl + (reg & 0xF), data); + } else { + port_byte_out(channels[channel].base + reg, 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); // todo this case is not handled +// 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) { +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) { + if (reg & 0x10) { 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) { + port_double_word_in_repeat(channels[channel].base + (reg & 0xF), buffer, quads); ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); + } else if (reg & 0x20) { + port_double_word_in_repeat(channels[channel].base + (reg & 0xF), buffer, quads); + } else { + port_double_word_in_repeat(channels[channel].base + reg, buffer, quads); } +// if (reg > 0x07 && reg < 0x0C) { +// ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN); +// } + // this assembly is probably necessary when reading paging +// asm("pushw %es; movw %ds, %ax; movw %ax, %es" ); +// 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) { +unsigned char ide_polling(unsigned char channel, u8 advanced_check) { // (I) Delay 400 nanosecond for BSY to be set: // ------------------------------------------------- @@ -297,14 +336,27 @@ u8 ide_pci_validate(const pci_device *device) { return PCI_VALIDATE_OK; } -u8 ide_pci_initialize(pci_device *device) { +void ide_fix_bar(bar_info *bar, u32 default_address, u32 size) { + if (bar->address == 0x0) { + // no need to actually write ti back + bar->address = default_address; + bar->prefetchable = 0; + bar->type = 0; + bar->is_io_space = 1; + bar->size = size; + } +} + +bool ide_pci_init_channels(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; +#ifdef IDE_ENABLE_INTERRUPT + k_panics("NOT SUPPORTED"); +#else + pci_config_write_byte(device->bus, device->slot, device->func, PCI_CONFIG_INTERRUPT_LINE, + PCI_INTERRUPT_LINE_DISABLED); +#endif } - pci_init_bar(device, 0); pci_init_bar(device, 1); pci_init_bar(device, 2); @@ -317,15 +369,27 @@ u8 ide_pci_initialize(pci_device *device) { || !device->bar3.present || !device->bar4.present) { k_panics("IDE Missing bars"); - return PCI_INIT_FAIL; + return false; } + ide_fix_bar(&device->bar0, 0x1F0, 8); + ide_fix_bar(&device->bar1, 0x3F6, 4); + ide_fix_bar(&device->bar2, 0x170, 8); + ide_fix_bar(&device->bar3, 0x376, 4); - 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].base = device->bar0.address; + channels[ATA_PRIMARY].ctrl = device->bar1.address; + channels[ATA_SECONDARY].base = device->bar2.address; + channels[ATA_SECONDARY].ctrl = device->bar3.address; channels[ATA_PRIMARY].bmide = device->bar4.address + 0; // Bus Master IDE channels[ATA_SECONDARY].bmide = device->bar4.address + 8; // Bus Master IDE + return true; +} + +u8 ide_pci_initialize(pci_device *device) { + + if (!ide_pci_init_channels(device)) { + return PCI_INIT_FAIL; + } // disable IRQ ide_write(ATA_PRIMARY, ATA_REG_CONTROL, 2); @@ -366,19 +430,20 @@ u8 ide_pci_initialize(pci_device *device) { unsigned char cl = ide_read(i, ATA_REG_LBA1); unsigned char ch = ide_read(i, ATA_REG_LBA2); - if (cl == 0x14 && ch == 0xEB) + if (cl == 0x14 && ch == 0xEB) { type = IDE_ATAPI; - else if (cl == 0x69 && ch == 0x96) + } else if (cl == 0x69 && ch == 0x96) { type = IDE_ATAPI; - else + } 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); + ide_read_buffer(i, ATA_REG_DATA, (unsigned int *) ide_buf, 128); // (VI) Read Device Parameters: ide_devices[count].reserved = 1; @@ -411,12 +476,12 @@ u8 ide_pci_initialize(pci_device *device) { 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); + itoa(ide_devices[i].size / 1024 / 2, tmp, 10); kprint("Found "); kprint((const char *[]) {"ATA", "ATAPI"}[ide_devices[i].type]); kprint(" Drive "); kprint(tmp); - kprint(" - "); + kprint("MB - "); kprint(ide_devices[i].model); kprint("\n"); } @@ -427,3 +492,141 @@ u8 ide_pci_initialize(pci_device *device) { void ide_register() { pci_register_driver(&ide_pci_driver); } + +u8 ide_read_ata_access(u8 direction, u8 drive, u32 lba, u8 numsects, void *target) { + u8 lba_mode /* 0: CHS, 1:LBA28, 2: LBA48 */, dma /* 0: No DMA, 1: DMA */, cmd; + u8 lba_io[6]; + u8 channel = ide_devices[drive].channel; + u8 slavebit = ide_devices[drive].drive; + u32 bus = channels[channel].base; + u32 words = 256; + u16 cyl, i; + u8 head, sect, err; + + ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN = (ide_irq_invoked = 0x0) + 0x02); + + // setup LBA data + if (lba >= ATA_LBA28_MAX) { // Sure Drive should support LBA in this case, or you are + // giving a wrong LBA. + // LBA48: + lba_mode = 2; + lba_io[0] = (lba & 0x000000FF) >> 0; + lba_io[1] = (lba & 0x0000FF00) >> 8; + lba_io[2] = (lba & 0x00FF0000) >> 16; + lba_io[3] = (lba & 0xFF000000) >> 24; + lba_io[4] = 0; // LBA28 is integer, so 32-bits are enough to access 2TB. + lba_io[5] = 0; // LBA28 is integer, so 32-bits are enough to access 2TB. + head = 0; // Lower 4-bits of HDDEVSEL are not used here. + } else if (ide_devices[drive].capabilities & ATA_CAP_LBA) { // Drive supports LBA? + // LBA28: + lba_mode = 1; + lba_io[0] = (lba & 0x00000FF) >> 0; + lba_io[1] = (lba & 0x000FF00) >> 8; + lba_io[2] = (lba & 0x0FF0000) >> 16; + lba_io[3] = 0; // These Registers are not used here. + lba_io[4] = 0; // These Registers are not used here. + lba_io[5] = 0; // These Registers are not used here. + head = (lba & 0xF000000) >> 24; + } else { + // CHS: + lba_mode = 0; + sect = (lba % 63) + 1; + cyl = (lba + 1 - sect) / (16 * 63); + lba_io[0] = sect; + lba_io[1] = (cyl >> 0) & 0xFF; + lba_io[2] = (cyl >> 8) & 0xFF; + lba_io[3] = 0; + lba_io[4] = 0; + lba_io[5] = 0; + head = (lba + 1 - sect) % (16 * 63) / (63); // Head number is written to HDDEVSEL lower 4-bits. + } + + // check if DMA is available + dma = 0; // maybe later + + // wait for the drive to have time for us + while (ide_read(channel, ATA_REG_STATUS) & ATA_SR_BSY) {} + + // clear error + ide_write(channel, ATA_REG_ERROR, 0); + + if (lba_mode == 0) { + ide_write(channel, + ATA_REG_HDDEVSEL, + ATA_HDDDEVSEL_DEFAULT | ATA_HDDDEVSEL_CHS | (slavebit << 4) | head); // Drive & CHS. + } else { + ide_write(channel, + ATA_REG_HDDEVSEL, + ATA_HDDDEVSEL_DEFAULT | ATA_HDDDEVSEL_LBA | (slavebit << 4) | head); // Drive & LBA + } + + // store data + if (lba_mode == 2) { + ide_write(channel, ATA_REG_SECCOUNT1, 0); + ide_write(channel, ATA_REG_LBA3, lba_io[3]); + ide_write(channel, ATA_REG_LBA4, lba_io[4]); + ide_write(channel, ATA_REG_LBA5, lba_io[5]); + } + ide_write(channel, ATA_REG_SECCOUNT0, numsects); + ide_write(channel, ATA_REG_LBA0, lba_io[0]); + ide_write(channel, ATA_REG_LBA1, lba_io[1]); + ide_write(channel, ATA_REG_LBA2, lba_io[2]); + + // pick correct ccommand + if (lba_mode == 0 && dma == 0 && direction == 0) cmd = ATA_CMD_READ_PIO; + if (lba_mode == 1 && dma == 0 && direction == 0) cmd = ATA_CMD_READ_PIO; + if (lba_mode == 2 && dma == 0 && direction == 0) cmd = ATA_CMD_READ_PIO_EXT; +// if (lba_mode == 0 && dma == 1 && direction == 0) cmd = ATA_CMD_READ_DMA; +// if (lba_mode == 1 && dma == 1 && direction == 0) cmd = ATA_CMD_READ_DMA; +// if (lba_mode == 2 && dma == 1 && direction == 0) cmd = ATA_CMD_READ_DMA_EXT; + if (lba_mode == 0 && dma == 0 && direction == 1) cmd = ATA_CMD_WRITE_PIO; + if (lba_mode == 1 && dma == 0 && direction == 1) cmd = ATA_CMD_WRITE_PIO; + if (lba_mode == 2 && dma == 0 && direction == 1) cmd = ATA_CMD_WRITE_PIO_EXT; +// if (lba_mode == 0 && dma == 1 && direction == 1) cmd = ATA_CMD_WRITE_DMA; +// if (lba_mode == 1 && dma == 1 && direction == 1) cmd = ATA_CMD_WRITE_DMA; +// if (lba_mode == 2 && dma == 1 && direction == 1) cmd = ATA_CMD_WRITE_DMA_EXT; + ide_write(channel, ATA_REG_COMMAND, cmd); // Send the Command. + + void* cur_addr = target; + // read response + if (dma) { + if (direction == 0); + // DMA Read. + else; + // DMA Write. + } else { + if (direction == 0) { + // PIO Read. + for (i = 0; i < numsects; i++) { + if ((err = ide_polling(channel, 1))) { + ide_print_error(drive, err); + return err; // Polling, set error and exit if there is. + } + port_word_in_repeat(bus, cur_addr, words); + cur_addr += (words * 2); + } + } else { + // PIO Write. + for (i = 0; i < numsects; i++) { + ide_polling(channel, 0); // Polling. + port_word_out_repeat(bus, cur_addr, words); + cur_addr += (words * 2); + } + ide_write(channel, ATA_REG_COMMAND, (char[]) {ATA_CMD_CACHE_FLUSH, + ATA_CMD_CACHE_FLUSH, + ATA_CMD_CACHE_FLUSH_EXT}[lba_mode]); + ide_polling(channel, 0); // Polling. + } + } + + return 0; +} + +u8 ide_read_access(u8 direction, u8 drive, u32 lba, u8 numsects, void *target) { + if (drive > 3 + || ide_devices[drive].reserved == 1 + || ide_devices[drive].type == IDE_ATAPI) { + k_panics("Not supported device read operation"); + } + return ide_read_ata_access(direction, drive, lba, numsects, target); +} diff --git a/kernel/drivers/ide.h b/kernel/drivers/ide.h index f29159d..82fb692 100644 --- a/kernel/drivers/ide.h +++ b/kernel/drivers/ide.h @@ -5,6 +5,10 @@ #ifndef NEW_KERNEL_IDE_H #define NEW_KERNEL_IDE_H +#include + void ide_register(); +u8 ide_read_access(u8 direction, u8 drive, u32 lba, u8 numsects, void *target); + #endif //NEW_KERNEL_IDE_H diff --git a/kernel/drivers/pci.c b/kernel/drivers/pci.c index abad59e..c3295af 100644 --- a/kernel/drivers/pci.c +++ b/kernel/drivers/pci.c @@ -280,7 +280,7 @@ void pci_init_bar(pci_device *device, u8 bar_index) { bar->address = original_address & 0xFFFFFFF0; bar->is_io_space = 0; bar->type = (original_address & 0b110) >> 1; - bar->prefetchable = (original_address & 0b111000) >> 3; + bar->prefetchable = (original_address & 0b1000) >> 3; } // todo identify conflicts, move to different address pci_config_write_double_word(device->bus, device->slot, device->func, offset, original_address); diff --git a/kernel/drivers/pci.h b/kernel/drivers/pci.h index 2025cf5..7f9bf43 100644 --- a/kernel/drivers/pci.h +++ b/kernel/drivers/pci.h @@ -55,6 +55,8 @@ #define PCI_CONFIG_MAX_GRANT 0x3E #define PCI_CONFIG_MAX_LATENCY 0x3F +#define PCI_INTERRUPT_LINE_DISABLED 0xff + typedef struct pci_driver pci_driver; typedef struct pci_device pci_device; @@ -80,7 +82,7 @@ typedef struct { u8 present: 1; u8 is_io_space: 1; u8 type: 2; - u8 prefetchable: 3; + u8 prefetchable: 1; } bar_info; typedef struct pci_device { diff --git a/kernel/drivers/ports.c b/kernel/drivers/ports.c index 75172aa..accf09e 100644 --- a/kernel/drivers/ports.c +++ b/kernel/drivers/ports.c @@ -41,6 +41,13 @@ void port_double_word_out(unsigned short port, unsigned int data) { __asm__("out %%eax, %%dx" : : "a" (data), "d" (port)); } +void port_word_out_repeat(unsigned short port, unsigned short *data, int buffer_size) { + asm("rep outsw" + : "+S"(data), "+c"(buffer_size) + : "d"(port) + : "memory"); +} + void port_word_in_repeat(unsigned short port, unsigned short *data, int buffer_size) { asm("rep insw" : "+D"(data), "+c"(buffer_size) diff --git a/kernel/drivers/ports.h b/kernel/drivers/ports.h index d5edcd4..8f438c1 100644 --- a/kernel/drivers/ports.h +++ b/kernel/drivers/ports.h @@ -56,6 +56,8 @@ unsigned int port_double_word_in(unsigned int port); void port_double_word_out(unsigned short port, unsigned int data); +void port_word_out_repeat(unsigned short port, unsigned short *data, int buffer_size); + 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); diff --git a/kernel/kernel.c b/kernel/kernel.c index 6a4faac..97b2f05 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -152,10 +152,13 @@ void kmain(multiboot_info_t *multiboot_info) { // init done, enable interrupts __asm__ __volatile__("sti"); // init timer for future task switching - init_timer(50); + init_timer(1000); // setup PS/2 keyboard init_keyboard(); + u8* tmp = malloc(4096); + ide_read_access(0, 0, 0, 16, tmp); + // enter main loop while (true) { main_loop();