diff --git a/drivers/ata.c b/drivers/ata.c index 21327d4..1e491f2 100644 --- a/drivers/ata.c +++ b/drivers/ata.c @@ -1,7 +1,10 @@ -#include "depthos/dev.h" -#include "depthos/heap.h" -#include "depthos/idt.h" +#include #include +#include +#include +#include +#include +#include #include #include #include @@ -9,15 +12,29 @@ struct ata_dev_impl { struct ata_port *port; + struct ata_identify *info; int drive; }; #if CONFIG_ATA_LOG_ENABLE == 1 -#define ata_log(...) ata_log(__VA_ARGS__); +#define ata_log(...) klogf(__VA_ARGS__); #else #define ata_log(...) #endif +#define BOUNDS_CHECK(dev, sector, n) \ + if (sector >= IMPL(dev)->info->current_sector_capacity) { \ + ata_log("trying to access with out-of-bounds sector: %d >= %d", sector, \ + IMPL(dev)->info->current_sector_capacity); \ + return 0; \ + } else if (sector + n >= IMPL(dev)->info->current_sector_capacity) { \ + ata_log("trying to access with partially in-bounds sector: %d + %d >= %d", \ + sector, n, IMPL(dev)->info->current_sector_capacity); \ + n = IMPL(dev)->info->current_sector_capacity - sector; \ + } +// #define BOUNDS_CHECK(dev, sector, n) assert(sector + n < +// IMPL(dev)->info->current_sector_capacity); + #define IMPL(dev) ((struct ata_dev_impl *)dev->impl) void ata_io_wait(struct ata_port *port, bool alternate) { // XXX: should an IRQ be used? @@ -32,12 +49,15 @@ static void ata_poll(struct ata_port *port, bool alternate, bool data) { do { v = inb(alternate ? port->ctl_base + ATA_REG_ALT_STAT : port->io_base + ATA_REG_STAT); - // ata_log("ata wait: %x (%d)", v, - // (v & ATA_STATUS_BSY == 0 && v & ATA_STATUS_DRQ != 0)); - } while (v & ATA_STATUS_BSY || (data && v & ATA_STATUS_DRQ != 0)); - if (v & ATA_STATUS_ERR) { - panicf("ATA drive (ctl=%d, io=%d) is not working properly.", port->ctl_base, + ata_log("ata poll: bsy=%d drq=%d err=%d dfa=%d", (v & ATA_STATUS_BSY) != 0, + (v & ATA_STATUS_DRQ) != 0, (v & ATA_STATUS_ERR) != 0, + (v & ATA_STATUS_DFA) != 0); + } while (((v & ATA_STATUS_BSY) || (data && ((v & ATA_STATUS_DRQ) == 0))) && + ((v & ATA_STATUS_ERR) == 0) && ((v & ATA_STATUS_DFA) == 0)); + ata_log("stopped polling"); + if (v & ATA_STATUS_ERR || v & ATA_STATUS_DFA) { + panicf("ATA drive (ctl=%x, io=%x) is not working properly.", port->ctl_base, port->io_base); } } @@ -148,25 +168,29 @@ void ata_pio_prepare_transaction(struct ata_port *port, size_t lba, outb(port->io_base + ATA_REG_CMD, write ? ATA_CMD_WRITE : ATA_CMD_READ); } -void ata_pio_read(struct ata_port *port, uint16_t *buf, size_t lba, - uint8_t sector_count) { - +int ata_pio_read(struct ata_port *port, uint16_t *buf, size_t lba, + uint8_t sector_count) { + ata_poll(port, false, false); ata_pio_prepare_transaction(port, lba, sector_count, false); ata_log("ata: reading %ld sectors at %ld", sector_count, lba); for (int i = 0; i < sector_count; i++) { - ata_poll(port, false, true); + ata_poll(port, false, true); // TODO: error logging + ata_log("began reading"); for (int j = 0; j < 256; j++) { buf[i * 256 + j] = inw(port->io_base + ATA_REG_DATA); } + ata_log("stopped reading"); ata_io_wait(port, false); } + return 0; } -void ata_pio_write(struct ata_port *port, uint16_t *buf, size_t lba, - uint8_t sector_count) { - ata_pio_prepare_transaction(port, lba, sector_count, true); +int ata_pio_write(struct ata_port *port, uint16_t *buf, size_t lba, + uint8_t sector_count) { ata_poll(port, false, false); + ata_pio_prepare_transaction(port, lba, sector_count, true); + ata_poll(port, false, false); // TODO: error logging uint8_t sec = 0; for (int i = 0; i < sector_count; i++) { @@ -178,10 +202,14 @@ void ata_pio_write(struct ata_port *port, uint16_t *buf, size_t lba, ata_poll(port, false, true); ata_log("write status: 0x%x", ata_read_status(port, false)); } + return 0; } int ata_write(struct device *dev, void *buf, unsigned long count, off_t *offset) { + BOUNDS_CHECK(dev, *offset, count); + if (count == 0) + return 0; ata_drive_select(IMPL(dev)->port, IMPL(dev)->drive); ata_pio_write(IMPL(dev)->port, buf, *offset, count); *offset += count; @@ -190,9 +218,15 @@ int ata_write(struct device *dev, void *buf, unsigned long count, int ata_read(struct device *dev, void *buf, unsigned long count, off_t *offset) { + BOUNDS_CHECK(dev, *offset, count); + if (count == 0) + return 0; ata_drive_select(IMPL(dev)->port, IMPL(dev)->drive); + ata_log("attempting to do a pio read. offset: %ld count: %ld", *offset, + count); ata_pio_read(IMPL(dev)->port, buf, *offset, count); *offset += count; + ata_log("read successful"); return count; } @@ -224,12 +258,17 @@ void ata_init() { ata_primary_port = create_ata_port(0x3F6, 0x1F0); ata_secondary_port = create_ata_port(0x376, 0x170); int i = 0; + struct ata_identify *info; #define ATA_REGDEV(P, D) \ - if (ata_identify(P, D)) { \ + if (info = ata_identify(P, D)) { \ char *buf = kmalloc(8); \ + memset(buf, 0, 8); \ itoa(i++, 10, buf + strlen("ata")); \ memcpy(buf, "ata", strlen("ata")); \ - devfs_register(buf, create_ata_device(P, D)); \ + struct device *dev = create_ata_device(P, D); \ + IMPL(dev)->info = info; \ + dev->name = buf; \ + register_device(dev); \ } ATA_REGDEV(ata_primary_port, 0); diff --git a/include/depthos/ata.h b/include/depthos/ata.h new file mode 100644 index 0000000..6a72e3f --- /dev/null +++ b/include/depthos/ata.h @@ -0,0 +1,121 @@ +#pragma once + +#include +#include +#include + +// typedef enum { +// ATA_DEV_UNKNOWN, +// ATA_DEV_PATA, +// ATA_DEV_SATA, +// ATA_DEV_PATAPI, +// ATA_DEV_SATAPI +// } ata_dev_t; + +#define ATA_REG_DATA 0 +#define ATA_REG_ERROR 1 +#define ATA_REG_FEATURES 1 +#define ATA_REG_SECTOR_COUNT 2 +#define ATA_REG_LBA_LOW 3 +#define ATA_REG_LBA_MID 4 +#define ATA_REG_LBA_HI 5 +#define ATA_REG_DRIVE_HEAD 6 // Drive/head register +#define ATA_REG_STAT 7 +#define ATA_REG_CMD 7 + +#define ATA_REG_DEV_CTL 0 +#define ATA_REG_ALT_STAT 0 + +#define ATA_STATUS_BSY 1 << 7 // Busy, prepating to send or receive data. +#define ATA_STATUS_RDY 1 << 6 // Drive is on +#define ATA_STATUS_DFA 1 << 5 // Drive fault +#define ATA_STATUS_SRV 1 << 4 // Overlapped mode service request +#define ATA_STATUS_DRQ 1 << 3 // Data is ready +#define ATA_STATUS_COR 1 << 2 // Corrected data. Always 0 +#define ATA_STATUS_IDX 1 << 1 // Index. Always 0 +#define ATA_STATUS_ERR 1 << 0 // Error + +#define ATA_CMD_IDENTIFY 0xEC +#define ATA_CMD_FLUSH 0xE7 +#define ATA_CMD_READ 0x20 +#define ATA_CMD_WRITE 0x30 + +struct ata_identify { + struct { + uint16_t reserved1 : 1; + uint16_t obsolete1 : 1; + uint16_t response_incomplete : 1; + uint16_t obsolete2 : 3; + uint16_t fixed_device : 1; + uint16_t removable_media : 1; + uint16_t obsolete3 : 7; + uint16_t atapi : 1; + } general_info; + uint16_t num_cylinders; + uint16_t specific_configuration; + uint16_t num_heads; + uint16_t retired1[2]; + uint16_t num_sectors_per_track; + uint16_t vendor_unique1[3]; + char serial_number[20]; + uint16_t deprecated[2]; + uint16_t obsolete1; + char firmware_revision[8]; + char model_number[40]; + uint16_t maximum_block_transfer; + struct { + uint16_t feature_supported : 1; + uint16_t reserved : 15; + } trusted_computing; + struct { + uint16_t vendor_unique1 : 8; + uint16_t obsolete1 : 2; + uint16_t iordy_toggleable : 1; + uint16_t iordy_support : 1; + uint16_t obsolete2 : 1; + uint16_t standy_timer_support : 1; + uint16_t obsolete3 : 2; + } capabilities; + uint16_t reserved1; + uint8_t vendor_unique2[2]; + uint8_t obsolete; + uint8_t pio_data_transfer_cycle_timing_mode; + uint16_t valid_fields : 3; + uint16_t reserved2 : 5; + uint16_t freefall_control_sensitivity : 8; + uint16_t num_current_cylinders; + uint16_t num_current_heads; + uint16_t current_sectors_per_track; + uint32_t current_sector_capacity; + uint8_t sectors_per_interrupt; + uint8_t multi_sectors_setting_valid : 1; + uint8_t reserved3 : 3; + uint16_t sanitize_feature_supported : 1; + uint8_t crypto_scramble_ext_command_supported : 1; + uint8_t overwrite_ext_command_supported : 1; + uint8_t block_erase_ext_command_supported : 1; + uint32_t max_addressable_sectors; +} __pack; + +typedef struct ata_identify ata_identify_data_t; + +struct ata_port { + uint16_t io_base, ctl_base; + uint8_t current_drive_head; +}; + +ata_identify_data_t *ata_identify(struct ata_port *dev, int drive); +void ata_reset(struct ata_port *dev); +void ata_drive_select(struct ata_port *dev, int drive); +int ata_get_selected_drive(struct ata_port *dev); + +void ata_io_wait(struct ata_port *dev, bool alternate); +int ata_pio_read(struct ata_port *dev, uint16_t *buf, size_t lba, + uint8_t sector_count); +int ata_pio_write(struct ata_port *dev, uint16_t *buf, size_t lba, + uint8_t sector_count); + +struct device *create_ata_device(struct ata_port *port, int drive); +struct ata_port *create_ata_port(uint16_t ctl, uint16_t io); + +void ata_init();