From 81fc559802f54c97b87ccc50959fee1856a02c7f Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Thu, 17 May 2018 00:20:55 -0700 Subject: [PATCH] Add initial ATA identify support to AHCI driver --- src/kernel/ahci/hba.cpp | 6 ++- src/kernel/ahci/port.cpp | 94 +++++++++++++++++++++++++++++++++++++++- src/kernel/ahci/port.h | 16 ++++++- 3 files changed, 111 insertions(+), 5 deletions(-) diff --git a/src/kernel/ahci/hba.cpp b/src/kernel/ahci/hba.cpp index 5eb28f4..017dc3d 100644 --- a/src/kernel/ahci/hba.cpp +++ b/src/kernel/ahci/hba.cpp @@ -94,8 +94,10 @@ hba::hba(pci_device *device) } for (auto &p : m_ports) { - if (p.get_state() == port::state::active && - p.get_type() == sata_signature::sata_drive) { + if (!p.active()) continue; + + if (p.get_type() == sata_signature::sata_drive) { + p.identify_async(); if (fs::partition::load(&p) == 0) dm.register_block_device(&p); } diff --git a/src/kernel/ahci/port.cpp b/src/kernel/ahci/port.cpp index 52e3eac..49b3007 100644 --- a/src/kernel/ahci/port.cpp +++ b/src/kernel/ahci/port.cpp @@ -267,6 +267,7 @@ port::read_async(uint64_t offset, size_t length, void *dest) cmd_table &cmdt = m_cmd_table[slot]; fis_register_h2d *fis = reinterpret_cast(&cmdt.cmd_fis); + kutil::memset(fis, 0, sizeof(fis_register_h2d)); fis->type = fis_type::register_h2d; fis->pm_port = 0x80; // set command register flag fis->command = ata_cmd::read_dma_ext; @@ -317,6 +318,31 @@ port::read(uint64_t offset, size_t length, void *dest) return count; } +int +port::identify_async() +{ + int slot = make_command(512); + if (slot < 0) + return 0; + + cmd_table &cmdt = m_cmd_table[slot]; + + fis_register_h2d *fis = reinterpret_cast(&cmdt.cmd_fis); + kutil::memset(fis, 0, sizeof(fis_register_h2d)); + fis->type = fis_type::register_h2d; + fis->pm_port = 0x80; // set command register flag + fis->command = ata_cmd::identify; + + m_pending[slot].type = command_type::identify; + m_pending[slot].offset = 0; + m_pending[slot].count = 0; + m_pending[slot].data = 0; + if(issue_command(slot)) + return slot; + else + return -1; +} + bool port::issue_command(int slot) { @@ -346,8 +372,8 @@ port::handle_interrupt() if (m_data->interrupt_status & 0x40000000) { log::error(logs::driver, "AHCI task file error"); - // TODO: clean up! - return; + dump(); + kassert(0, "Task file error"); } log::debug(logs::driver, "AHCI interrupt status: %08lx %08lx", @@ -362,6 +388,9 @@ port::handle_interrupt() case command_type::read: finish_read(i); break; + case command_type::identify: + finish_identify(i); + break; default: break; } @@ -403,6 +432,67 @@ port::finish_read(int slot) m_pending[slot].data = nullptr; } +static void +ident_strcpy(uint16_t *from, int words, char *dest) +{ + for (int i = 0; i < words; ++i) { + *dest++ = *from >> 8; + *dest++ = *from & 0xff; + from++; + } + *dest = 0; +} + +void +port::finish_identify(int slot) +{ + page_manager *pm = page_manager::get(); + cmd_table &cmdt = m_cmd_table[slot]; + cmd_list_entry &ent = m_cmd_list[slot]; + + kassert(ent.prd_table_length == 1, "AHCI identify used multiple PRDs"); + + size_t prd_len = (cmdt.entries[0].byte_count & 0x7fffffff) + 1; + + addr_t phys = + static_cast(cmdt.entries[0].data_base_low) | + static_cast(cmdt.entries[0].data_base_high) << 32; + + log::debug(logs::driver, "Reading ident PRD:"); + + uint16_t *mem = reinterpret_cast(pm->offset_virt(phys)); + char string[41]; + + ident_strcpy(&mem[10], 10, &string[0]); + log::debug(logs::driver, " Device serial: %s", string); + + ident_strcpy(&mem[23], 4, &string[0]); + log::debug(logs::driver, " Device version: %s", string); + + ident_strcpy(&mem[27], 20, &string[0]); + log::debug(logs::driver, " Device model: %s", string); + + uint32_t sectors = mem[60] | (mem[61] << 16); + log::debug(logs::driver, " Max sectors: %xh", sectors); + + uint16_t lb_size = mem[106]; + log::debug(logs::driver, " lsects per psect: %d %s %s", 1 << (lb_size & 0xf), + lb_size & 0x20 ? "multiple logical per physical" : "", + lb_size & 0x10 ? "physical > 512b" : ""); + + uint32_t b_per_ls = 2 * (mem[117] | (mem[118] << 16)); + log::debug(logs::driver, " b per lsect: %d", b_per_ls); + + /* + for (int i=0; i<256; i += 4) + log::debug(logs::driver, " %3d: %04x %3d: %04x %3d: %04x %3d: %04x", + i, mem[i], i+1, mem[i+1], i+2, mem[i+2], i+3, mem[i+3]); + */ + + pm->unmap_pages(mem, page_count(prd_len)); + m_pending[slot].type = command_type::none; +} + void port::free_command(int slot) { diff --git a/src/kernel/ahci/port.h b/src/kernel/ahci/port.h index a645941..68dfc81 100644 --- a/src/kernel/ahci/port.h +++ b/src/kernel/ahci/port.h @@ -41,6 +41,10 @@ public: /// \returns An enum representing the state inline state get_state() const { return m_state; } + /// Check if this device is active + /// \returns True if the device state is active + inline bool active() const { return m_state == state::active; } + /// Get the type signature of this device /// \returns An enum representing the type of device inline sata_signature get_type() const { return m_type; } @@ -71,6 +75,10 @@ public: /// \returns The number of bytes read virtual size_t read(uint64_t offset, size_t length, void *dest); + /// Start an identify operation for the drive. + /// \returns A handle to the read operation, or -1 on error + int identify_async(); + /// Handle an incoming interrupt void handle_interrupt(); @@ -102,6 +110,12 @@ private: /// \arg slot The command slot that the read command used void finish_read(int slot); + /// Finish an identify command started by `identify_async()`. + /// This will free the structures allocated, so `free_command()` is + /// not necessary. + /// \arg slot The command slot that the read command used + void finish_identify(int slot); + sata_signature m_type; uint8_t m_index; state m_state; @@ -112,7 +126,7 @@ private: cmd_list_entry *m_cmd_list; cmd_table *m_cmd_table; - enum class command_type : uint8_t { none, read, write, finished }; + enum class command_type : uint8_t { none, read, write, identify, finished }; struct pending {