From c9277e4b120324987e5e6c2de4118ede7cac505d Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sat, 12 May 2018 13:55:09 -0700 Subject: [PATCH] Split ahci read into separate functions --- src/kernel/ahci/ata.h | 10 +++ src/kernel/ahci/hba.cpp | 16 +++- src/kernel/ahci/port.cpp | 155 ++++++++++++++++++++++++++------------- src/kernel/ahci/port.h | 46 ++++++++++-- 4 files changed, 170 insertions(+), 57 deletions(-) diff --git a/src/kernel/ahci/ata.h b/src/kernel/ahci/ata.h index e8f4212..7ec6f5d 100644 --- a/src/kernel/ahci/ata.h +++ b/src/kernel/ahci/ata.h @@ -51,6 +51,16 @@ enum class ata_cmd : uint8_t }; +enum class sata_signature : uint32_t +{ + none = 0x00000000, + + sata_drive = 0x00000101, + satapi_drive = 0xeb140101, + enclosure = 0xc33c0101, + port_muxer = 0x96690101 +}; + } // namespace ahci IS_BITFIELD(ahci::ata_status); diff --git a/src/kernel/ahci/hba.cpp b/src/kernel/ahci/hba.cpp index 2b5bd48..e5b0050 100644 --- a/src/kernel/ahci/hba.cpp +++ b/src/kernel/ahci/hba.cpp @@ -1,5 +1,6 @@ #include #include "ahci/hba.h" +#include "console.h" #include "log.h" #include "page_manager.h" #include "pci.h" @@ -68,8 +69,19 @@ hba::hba(pci_device *device) for (unsigned i = 0; i < ports; ++i) { bool impl = ((m_data->port_impl & (1 << i)) != 0); port &p = m_ports.emplace(i, kutil::offset_pointer(pd, 0x80 * i), impl); - if (p.get_state() == port::state::active) - p.read(1, 0x1000); + if (p.get_state() == port::state::active) { + uint8_t buf[512]; + p.read(1, sizeof(buf), buf); + + console *cons = console::get(); + uint8_t *p = &buf[0]; + for (int i = 0; i < 8; ++i) { + for (int j = 0; j < 16; ++j) { + cons->printf(" %02x", *p++); + } + cons->putc('\n'); + } + } } } diff --git a/src/kernel/ahci/port.cpp b/src/kernel/ahci/port.cpp index d8614ba..404c2ac 100644 --- a/src/kernel/ahci/port.cpp +++ b/src/kernel/ahci/port.cpp @@ -4,7 +4,6 @@ #include "ahci/ata.h" #include "ahci/fis.h" #include "ahci/port.h" -#include "console.h" #include "io.h" #include "log.h" #include "page_manager.h" @@ -76,14 +75,6 @@ enum class port_cmd : uint32_t none = 0x00000000 }; -enum class sata_signature : uint32_t -{ - sata_drive = 0x00000101, - satapi_drive = 0xeb140101, - enclosure = 0xc33c0101, - port_muxer = 0x96690101 -}; - struct port_data { @@ -121,6 +112,7 @@ struct port_data port::port(uint8_t index, port_data *data, bool impl) : m_index(index), + m_type(sata_signature::none), m_state(state::unimpl), m_data(data), m_fis(nullptr), @@ -130,8 +122,6 @@ port::port(uint8_t index, port_data *data, bool impl) : if (impl) { m_state = state::inactive; update(); - if (m_state == state::active) - rebase(); } } @@ -153,13 +143,17 @@ port::update() if (detected == 0x3 && power == 0x1) { m_state = state::active; + m_type = m_data->signature; const char *name = - m_data->signature == sata_signature::sata_drive ? "SATA" : - m_data->signature == sata_signature::satapi_drive ? "SATAPI" : + m_type == sata_signature::sata_drive ? "SATA" : + m_type == sata_signature::satapi_drive ? "SATAPI" : "Other"; log::info(logs::driver, "Found device type %s at port %d", name, m_index); + + rebase(); + m_pending.set_size(32); } else { m_state = state::inactive; } @@ -194,15 +188,21 @@ port::stop_commands() m_data->command &= ~port_cmd::fis_recv; } -bool -port::read(uint64_t sector, size_t length) +int +port::make_command(size_t length) { - m_data->interrupt_status = ~0u; + int slot = -1; + uint32_t used_slots = (m_data->serial_active | m_data->cmd_issue); + for (int i = 0; i < 32; ++i) { + if ((used_slots & (1 << i)) == 0) { + slot = i; + break; + } + } - int slot = get_cmd_slot(); if (slot < 0) { log::info(logs::driver, "AHCI could not get a free command slot."); - return false; + return -1; } page_manager *pm = page_manager::get(); @@ -213,17 +213,12 @@ port::read(uint64_t sector, size_t length) kutil::memset(&cmdt, 0, sizeof(cmd_table) + max_prd_count * sizeof(prdt_entry)); - ent.flags = cmd_list_fis_size(sizeof(fis_register_h2d)); - - void *buffers[32]; - size_t remaining = length; for (int i = 0; i < max_prd_count; ++i) { size_t prd_len = std::min(remaining, 0x200000ul); remaining -= prd_len; void *mem = pm->map_offset_pages(page_count(prd_len)); - buffers[i] = mem; kutil::memset(mem, 0xaf, prd_len); addr_t phys = pm->offset_phys(mem); @@ -238,6 +233,21 @@ port::read(uint64_t sector, size_t length) } } + + log::debug(logs::driver, "Created command, slot %d, %d PRD entries.", + slot, ent.prd_table_length); + return slot; +} + +bool +port::read(uint64_t sector, size_t length, void *dest) +{ + int slot = make_command(length); + if (slot < 0) + return false; + + cmd_table &cmdt = m_cmd_table[slot]; + fis_register_h2d *fis = reinterpret_cast(&cmdt.cmd_fis); fis->type = fis_type::register_h2d; fis->pm_port = 0x80; // set command register flag @@ -255,26 +265,28 @@ port::read(uint64_t sector, size_t length) fis->count0 = (count ) & 0xff; fis->count1 = (count >> 8) & 0xff; - log::debug(logs::driver, "Reading %d sectors, starting from %d (0x%lx), using %d PRD entries.", - count, sector, sector*512, ent.prd_table_length); - log::debug(logs::driver, " lba: %02x %02x %02x %02x %02x %02x", - fis->lba0, fis->lba1, fis->lba2, fis->lba3, fis->lba4, fis->lba5); + log::debug(logs::driver, "Reading %d sectors, starting from %d (0x%lx)", + count, sector, sector*512); + m_pending[slot].type = command_type::read; + m_pending[slot].data = dest; + return issue_command(slot); +} + +bool +port::issue_command(int slot) +{ const int max_tries = 10; int tries = 0; while (busy()) { if (++tries == max_tries) { log::warn(logs::driver, "AHCI port was busy too long"); + free_command(slot); return false; } io_wait(); } - if (tries == max_tries) { - // TODO: clean up!!! - return false; - } - // Set bit in CI. Note that only new bits should be written, not // previous state. m_data->cmd_issue = (1 << slot); @@ -290,27 +302,82 @@ port::read(uint64_t sector, size_t length) io_wait(); } + // This is where interrupt handler would begin + // TODO: handle other states in interrupt_status + if (m_data->interrupt_status & 0x40000000) { log::error(logs::driver, "AHCI task file error"); // TODO: clean up! return false; } - log::warn(logs::driver, "AHCI read status: %08lx %08lx", + log::debug(logs::driver, "AHCI interrupt status: %08lx %08lx", m_data->interrupt_status, m_data->serial_error); - console *cons = console::get(); - uint8_t *p = (uint8_t *)buffers[0]; - for (int i = 0; i < 8; ++i) { - for (int j = 0; j < 16; ++j) { - cons->printf(" %02x", *p++); + uint32_t ci = m_data->cmd_issue; + for (int i = 0; i < 32; ++i) { + if (ci & (1 << i)) continue; + + pending &p = m_pending[i]; + switch (p.type) { + case command_type::read: + finish_read(i); + break; + default: + break; } - cons->putc('\n'); + p.type = command_type::none; + p.data = nullptr; } return true; } +void +port::finish_read(int slot) +{ + page_manager *pm = page_manager::get(); + cmd_table &cmdt = m_cmd_table[slot]; + cmd_list_entry &ent = m_cmd_list[slot]; + + void *p = m_pending[slot].data; + for (int i = 0; i < ent.prd_table_length; ++i) { + size_t prd_len = (cmdt.entries[i].byte_count & 0x7fffffff) + 1; + + addr_t phys = + static_cast(cmdt.entries[i].data_base_low) | + static_cast(cmdt.entries[i].data_base_high) << 32; + + void *mem = pm->offset_virt(phys); + kutil::memcpy(p, mem, prd_len); + p = kutil::offset_pointer(p, prd_len); + + pm->unmap_pages(mem, page_count(prd_len)); + } + +} + +void +port::free_command(int slot) +{ + page_manager *pm = page_manager::get(); + + cmd_table &cmdt = m_cmd_table[slot]; + cmd_list_entry &ent = m_cmd_list[slot]; + + for (int i = 0; i < ent.prd_table_length; ++i) { + size_t prd_len = (cmdt.entries[i].byte_count & 0x7fffffff) + 1; + + addr_t phys = + static_cast(cmdt.entries[i].data_base_low) | + static_cast(cmdt.entries[i].data_base_high) << 32; + void *mem = pm->offset_virt(phys); + pm->unmap_pages(mem, page_count(prd_len)); + } + + ent.prd_table_length = max_prd_count; +} + void port::rebase() { @@ -368,14 +435,4 @@ port::rebase() start_commands(); } -int -port::get_cmd_slot() -{ - uint32_t used = (m_data->serial_active | m_data->cmd_issue); - for (int i = 0; i < 32; ++i) - if ((used & (1 << i)) == 0) return i; - - return -1; -} - } // namespace ahci diff --git a/src/kernel/ahci/port.h b/src/kernel/ahci/port.h index ed825b5..6fdf9cd 100644 --- a/src/kernel/ahci/port.h +++ b/src/kernel/ahci/port.h @@ -3,11 +3,13 @@ /// Definition for AHCI ports #include #include +#include "kutil/vector.h" namespace ahci { struct cmd_list_entry; struct cmd_table; +enum class sata_signature : uint32_t; enum class port_cmd : uint32_t; struct port_data; @@ -25,12 +27,16 @@ public: /// Destructor ~port(); - enum class state { unimpl, inactive, active }; + enum class state : uint8_t { unimpl, inactive, active }; /// Get the current state of this device /// \returns An enum representing the state state get_state() const { return m_state; } + /// Get the type signature of this device + /// \returns An enum representing the type of device + sata_signature get_type() const { return m_type; } + /// Update the state of this object from the register data void update(); @@ -46,25 +52,53 @@ public: /// Read data from the drive. /// \arg sector Starting sector to read /// \arg length Number of bytes to read + /// \arg dest A buffer where the data will be placed /// \returns True if the command succeeded - bool read(uint64_t sector, size_t length); + bool read(uint64_t sector, size_t length, void *dest); private: /// Rebase the port command structures to a new location in system /// memory, to be allocated from the page manager. void rebase(); - /// Get a free command slot - /// \returns The index of the command slot, or -1 if none available - int get_cmd_slot(); + /// Initialize a command structure + /// \arg length The number of bytes of data needed in the PRDs + /// \returns The index of the command slot, or -1 if none available + int make_command(size_t length); + /// Send a constructed command to the hardware + /// \arg slot The index of the command slot used + /// \returns True if the command was successfully sent + bool issue_command(int slot); + + /// Free the data structures allocated by command creation + /// \arg slot The index of the command slot used + void free_command(int slot); + + /// Finish a read command started by `read()`. This will + /// free the structures allocated, so `free_command()` is + /// not necessary. + /// \arg slot The command slot that the read command used + void finish_read(int slot); + + sata_signature m_type; uint8_t m_index; state m_state; - port_data *m_data; + port_data *m_data; void *m_fis; cmd_list_entry *m_cmd_list; cmd_table *m_cmd_table; + + enum class command_type : uint8_t { none, read, write }; + + struct pending + { + command_type type; + void *data; + }; + + kutil::vector m_pending; }; } // namespace ahci