diff options
author | Anton Kling <anton@kling.gg> | 2023-10-30 22:12:14 +0100 |
---|---|---|
committer | Anton Kling <anton@kling.gg> | 2023-10-31 00:18:38 +0100 |
commit | 8a9208612eec8ddae4c418485d848ecfa0613699 (patch) | |
tree | 2f4b29200c2f0c19ae52f45bdb9b38a41b356e30 /drivers/ata.c | |
parent | ca76600acc8bf7a02346efa5bd8f17072210ec01 (diff) |
Meta: Move kernel and userland to their own folders.
This is to allow both the kernel and the userland to share certain
header files and to make the folder structure a bit more clear.
Diffstat (limited to 'drivers/ata.c')
-rw-r--r-- | drivers/ata.c | 253 |
1 files changed, 0 insertions, 253 deletions
diff --git a/drivers/ata.c b/drivers/ata.c deleted file mode 100644 index fd9b504..0000000 --- a/drivers/ata.c +++ /dev/null @@ -1,253 +0,0 @@ -#include <assert.h> -#include <cpu/io.h> -#include <drivers/ata.h> -#include <drivers/pit.h> - -#define PRIMARY_BUS_BASEPORT 0x1F0 -#define SECONDAY_BUS_BASEPORT 0x170 - -#define PRIMARY_BUS_IRQ 14 -#define SECONDAY_BUS_IRQ 15 - -#define STATUS_PORT 7 -#define COMMAND_PORT 7 -#define DRIVE_SELECT 6 -#define LBAhi 5 -#define LBAmid 4 -#define LBAlo 3 -#define SECTOR_COUNT 2 -#define DATA_PORT 0 - -#define IDENTIFY 0xEC -#define READ_SECTORS 0x20 -#define WRITE_SECTORS 0x30 -#define CACHE_FLUSH 0xE7 - -#define STATUS_BSY ((1 << 7)) -#define STATUS_DF ((1 << 5)) -#define STATUS_DRQ ((1 << 3)) -#define STATUS_ERR ((1 << 0)) - -uint32_t io_base; - -unsigned char read_buffer[SECTOR_SIZE]; - -void select_drive(uint8_t master_slave) { - outb(io_base + DRIVE_SELECT, (master_slave) ? 0xA0 : 0xB0); -} - -int identify(int master_slave) { - select_drive(master_slave); - outb(io_base + SECTOR_COUNT, 0); - outb(io_base + LBAlo, 0); - outb(io_base + LBAmid, 0); - outb(io_base + LBAhi, 0); - outb(io_base + COMMAND_PORT, IDENTIFY); - if (0 == inb(io_base + STATUS_PORT)) - return 0; // Drive does not exist - - for (; 0 != (inb(io_base + STATUS_PORT) & STATUS_BSY);) - ; - - // Because of some ATAPI drives that do not - // follow spec, at this point we need to check - // the LBAmid and LBAhi ports to see if they are - // non-zero. If so, the drive is not ATA, and we - // should stop polling. - if (0 != inb(io_base + LBAmid) || 0 != inb(io_base + LBAhi)) { - klog("Drive is not ATA.", LOG_ERROR); - return -1; - } - - for (uint16_t status;;) { - status = inb(io_base + STATUS_PORT); - - if (1 == (status & STATUS_ERR)) { - klog("Drive ERR set.", LOG_ERROR); - return -2; - } - - if ((status & STATUS_DRQ)) - break; - } - - // The data is ready to read from the Data - // port (0x1F0). Read 256 16-bit values, - // and store them. - // TODO: This returns some intreasting information. - // https://wiki.osdev.org/ATA_PIO_Mode#Interesting_information_returned_by_IDENTIFY - uint16_t array[256]; - rep_insw(1 * SECTOR_SIZE / 16, io_base + DATA_PORT, array); - return 1; -} - -int poll_status(void) { - for (uint16_t status;;) { - // Read the Regular Status port until... - // We read this 15 times to give some - // time for the drive to catch up. - for (int n = 0; n < 15; n++) - status = inb(io_base + STATUS_PORT); - - // ERR or - // DF sets - if ((status & STATUS_ERR) || (status & STATUS_DF)) { - klog("Drive error set.", LOG_ERROR); - return 0; - } - - // BSY clears - // DRQ sets - if (0 == (status & STATUS_BSY) && 0 != (status & STATUS_DRQ)) - break; - } - return 1; -} - -// Instructions from: https://wiki.osdev.org/ATA_PIO_Mode#28_bit_PIO -void __attribute__((optimize("O0"))) -setup_drive_for_command(uint32_t lba, uint32_t sector_count) { - // 1. Send 0xE0 for the "master" or 0xF0 for - // the "slave", ORed with the highest 4 bits - // of the LBA to port 0x1F6 - outb(io_base + DRIVE_SELECT, 0xE0 | (0 << 4) | ((lba >> 24) & 0x0F)); - - // 2. Send a NULL byte to port 0x1F1, if you - // like (it is ignored and wastes - // lots of CPU time) - - // NOP - - // 3. Send the sectorcount to port 0x1F2 - outb(io_base + SECTOR_COUNT, sector_count); - - // 4. Send the low 8 bits of the LBA to port 0x1F3 - outb(io_base + LBAlo, (lba >> 0) & 0xFF); - - // 5. Send the next 8 bits of the LBA to port 0x1F4 - outb(io_base + LBAmid, (lba >> 8) & 0xFF); - - // 6. Send the next 8 bits of the LBA to port 0x1F5 - outb(io_base + LBAhi, (lba >> 16) & 0xFF); -} - -void delayed_rep_outsw(size_t n, uint16_t port, volatile uint8_t *buffer) { - for (volatile size_t i = 0; i < n; i++) { - outsw(port, (uint32_t)buffer); - buffer += 2; - // outsw(port, buffer); - } -} - -void __attribute__((optimize("O0"))) -ata_write_lba28(uint32_t lba, uint32_t sector_count, - volatile const uint8_t *buffer) { - setup_drive_for_command(lba, sector_count); - - outb(io_base + COMMAND_PORT, WRITE_SECTORS); - - for (volatile uint32_t i = 0; i < sector_count; i++) { - if (!poll_status()) { - // FIXME: Fail properly - for (;;) - ; - } - - delayed_rep_outsw(256, io_base + DATA_PORT, - (void *)((uint32_t)buffer + i * 256)); - } - - // Cache flush - outb(io_base + COMMAND_PORT, CACHE_FLUSH); - - // Wait for BSY to clear - for (;;) { - uint16_t status = inb(io_base + STATUS_PORT); - if (!(status & STATUS_BSY)) - break; - } -} - -// Instructions from: https://wiki.osdev.org/ATA_PIO_Mode#28_bit_PIO -void __attribute__((optimize("O0"))) -ata_read_lba28(uint32_t lba, uint32_t sector_count, volatile void *address) { - // Step 1-6 is done in this function. - setup_drive_for_command(lba, sector_count); - - // 7. Send the "READ SECTORS" command to port 0x17F - outb(io_base + COMMAND_PORT, READ_SECTORS); - - // 8. Wait for an IRQ or poll. - - // This step can be found in the for loop - - // 9. Transfer 256 16-bit values, a uint16_t at a time, - // into your buffer from I/O port 0x1F0 - for (volatile uint32_t i = 0; i < sector_count; i++) { - // 10. Then loop back to waiting for the next IRQ - // or poll again for each successive sector. - // 8. Wait for an IRQ or poll. - if (!poll_status()) { - // FIXME: Fail properly - for (;;) - ; - } - rep_insw(256, io_base + DATA_PORT, (void *)((uint32_t)address + i * 256)); - } -} - -void ata_init(void) { - io_base = PRIMARY_BUS_BASEPORT; - - // Before sending any data to the IO ports, - // read the Regular Status byte. The value - // 0xFF is an illegal status value, and - // indicates that the bus has no drives - if (0xFF == inb(io_base + STATUS_PORT)) { - klog("Bus has no drives", LOG_ERROR); - } - - // Issue IDENTIFY command - select_drive(1); -} - -void __attribute__((optimize("O0"))) -read_lba(uint32_t lba, void *address, size_t size, size_t offset) { - uintptr_t ptr = (uintptr_t)address; - lba += offset / SECTOR_SIZE; - offset = offset % SECTOR_SIZE; - asm("cli"); - size_t total_read = 0; - for (int i = 0; size > 0; i++) { - uint32_t read_len = - (SECTOR_SIZE < (size + offset)) ? (SECTOR_SIZE - offset) : size; - ata_read_lba28(lba + i, 1, read_buffer); - memcpy((void *)ptr, read_buffer + offset, read_len); - size -= read_len; - total_read += read_len; - ptr += read_len; - offset = 0; - } -} - -void write_lba(uint32_t lba, volatile void *address, size_t size, - size_t offset) { - uintptr_t ptr = (uintptr_t)address; - lba += offset / SECTOR_SIZE; - offset = offset % SECTOR_SIZE; - size_t total_write = 0; - uint8_t sector_buffer[512]; - for (int i = 0; size > 0; i++) { - ata_read_lba28(lba + i, 1, sector_buffer); - uint32_t write_len = - (SECTOR_SIZE < (size + offset)) ? (SECTOR_SIZE - offset) : size; - - memcpy(sector_buffer + offset, (void *)ptr, write_len); - ata_write_lba28(lba + i, 1, sector_buffer); - - size -= write_len; - total_write += write_len; - ptr += write_len; - offset = 0; - } -} |