Split ahci read into separate functions
This commit is contained in:
@@ -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
|
} // namespace ahci
|
||||||
|
|
||||||
IS_BITFIELD(ahci::ata_status);
|
IS_BITFIELD(ahci::ata_status);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "ahci/hba.h"
|
#include "ahci/hba.h"
|
||||||
|
#include "console.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "page_manager.h"
|
#include "page_manager.h"
|
||||||
#include "pci.h"
|
#include "pci.h"
|
||||||
@@ -68,8 +69,19 @@ hba::hba(pci_device *device)
|
|||||||
for (unsigned i = 0; i < ports; ++i) {
|
for (unsigned i = 0; i < ports; ++i) {
|
||||||
bool impl = ((m_data->port_impl & (1 << i)) != 0);
|
bool impl = ((m_data->port_impl & (1 << i)) != 0);
|
||||||
port &p = m_ports.emplace(i, kutil::offset_pointer(pd, 0x80 * i), impl);
|
port &p = m_ports.emplace(i, kutil::offset_pointer(pd, 0x80 * i), impl);
|
||||||
if (p.get_state() == port::state::active)
|
if (p.get_state() == port::state::active) {
|
||||||
p.read(1, 0x1000);
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include "ahci/ata.h"
|
#include "ahci/ata.h"
|
||||||
#include "ahci/fis.h"
|
#include "ahci/fis.h"
|
||||||
#include "ahci/port.h"
|
#include "ahci/port.h"
|
||||||
#include "console.h"
|
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "page_manager.h"
|
#include "page_manager.h"
|
||||||
@@ -76,14 +75,6 @@ enum class port_cmd : uint32_t
|
|||||||
none = 0x00000000
|
none = 0x00000000
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class sata_signature : uint32_t
|
|
||||||
{
|
|
||||||
sata_drive = 0x00000101,
|
|
||||||
satapi_drive = 0xeb140101,
|
|
||||||
enclosure = 0xc33c0101,
|
|
||||||
port_muxer = 0x96690101
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct port_data
|
struct port_data
|
||||||
{
|
{
|
||||||
@@ -121,6 +112,7 @@ struct port_data
|
|||||||
|
|
||||||
port::port(uint8_t index, port_data *data, bool impl) :
|
port::port(uint8_t index, port_data *data, bool impl) :
|
||||||
m_index(index),
|
m_index(index),
|
||||||
|
m_type(sata_signature::none),
|
||||||
m_state(state::unimpl),
|
m_state(state::unimpl),
|
||||||
m_data(data),
|
m_data(data),
|
||||||
m_fis(nullptr),
|
m_fis(nullptr),
|
||||||
@@ -130,8 +122,6 @@ port::port(uint8_t index, port_data *data, bool impl) :
|
|||||||
if (impl) {
|
if (impl) {
|
||||||
m_state = state::inactive;
|
m_state = state::inactive;
|
||||||
update();
|
update();
|
||||||
if (m_state == state::active)
|
|
||||||
rebase();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,13 +143,17 @@ port::update()
|
|||||||
|
|
||||||
if (detected == 0x3 && power == 0x1) {
|
if (detected == 0x3 && power == 0x1) {
|
||||||
m_state = state::active;
|
m_state = state::active;
|
||||||
|
m_type = m_data->signature;
|
||||||
|
|
||||||
const char *name =
|
const char *name =
|
||||||
m_data->signature == sata_signature::sata_drive ? "SATA" :
|
m_type == sata_signature::sata_drive ? "SATA" :
|
||||||
m_data->signature == sata_signature::satapi_drive ? "SATAPI" :
|
m_type == sata_signature::satapi_drive ? "SATAPI" :
|
||||||
"Other";
|
"Other";
|
||||||
|
|
||||||
log::info(logs::driver, "Found device type %s at port %d", name, m_index);
|
log::info(logs::driver, "Found device type %s at port %d", name, m_index);
|
||||||
|
|
||||||
|
rebase();
|
||||||
|
m_pending.set_size(32);
|
||||||
} else {
|
} else {
|
||||||
m_state = state::inactive;
|
m_state = state::inactive;
|
||||||
}
|
}
|
||||||
@@ -194,15 +188,21 @@ port::stop_commands()
|
|||||||
m_data->command &= ~port_cmd::fis_recv;
|
m_data->command &= ~port_cmd::fis_recv;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
int
|
||||||
port::read(uint64_t sector, size_t length)
|
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) {
|
if (slot < 0) {
|
||||||
log::info(logs::driver, "AHCI could not get a free command slot.");
|
log::info(logs::driver, "AHCI could not get a free command slot.");
|
||||||
return false;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
page_manager *pm = page_manager::get();
|
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) +
|
kutil::memset(&cmdt, 0, sizeof(cmd_table) +
|
||||||
max_prd_count * sizeof(prdt_entry));
|
max_prd_count * sizeof(prdt_entry));
|
||||||
|
|
||||||
ent.flags = cmd_list_fis_size(sizeof(fis_register_h2d));
|
|
||||||
|
|
||||||
void *buffers[32];
|
|
||||||
|
|
||||||
size_t remaining = length;
|
size_t remaining = length;
|
||||||
for (int i = 0; i < max_prd_count; ++i) {
|
for (int i = 0; i < max_prd_count; ++i) {
|
||||||
size_t prd_len = std::min(remaining, 0x200000ul);
|
size_t prd_len = std::min(remaining, 0x200000ul);
|
||||||
remaining -= prd_len;
|
remaining -= prd_len;
|
||||||
|
|
||||||
void *mem = pm->map_offset_pages(page_count(prd_len));
|
void *mem = pm->map_offset_pages(page_count(prd_len));
|
||||||
buffers[i] = mem;
|
|
||||||
kutil::memset(mem, 0xaf, prd_len);
|
kutil::memset(mem, 0xaf, prd_len);
|
||||||
|
|
||||||
addr_t phys = pm->offset_phys(mem);
|
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<fis_register_h2d *>(&cmdt.cmd_fis);
|
fis_register_h2d *fis = reinterpret_cast<fis_register_h2d *>(&cmdt.cmd_fis);
|
||||||
fis->type = fis_type::register_h2d;
|
fis->type = fis_type::register_h2d;
|
||||||
fis->pm_port = 0x80; // set command register flag
|
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->count0 = (count ) & 0xff;
|
||||||
fis->count1 = (count >> 8) & 0xff;
|
fis->count1 = (count >> 8) & 0xff;
|
||||||
|
|
||||||
log::debug(logs::driver, "Reading %d sectors, starting from %d (0x%lx), using %d PRD entries.",
|
log::debug(logs::driver, "Reading %d sectors, starting from %d (0x%lx)",
|
||||||
count, sector, sector*512, ent.prd_table_length);
|
count, sector, sector*512);
|
||||||
log::debug(logs::driver, " lba: %02x %02x %02x %02x %02x %02x",
|
|
||||||
fis->lba0, fis->lba1, fis->lba2, fis->lba3, fis->lba4, fis->lba5);
|
|
||||||
|
|
||||||
|
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;
|
const int max_tries = 10;
|
||||||
int tries = 0;
|
int tries = 0;
|
||||||
while (busy()) {
|
while (busy()) {
|
||||||
if (++tries == max_tries) {
|
if (++tries == max_tries) {
|
||||||
log::warn(logs::driver, "AHCI port was busy too long");
|
log::warn(logs::driver, "AHCI port was busy too long");
|
||||||
|
free_command(slot);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
io_wait();
|
io_wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tries == max_tries) {
|
|
||||||
// TODO: clean up!!!
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set bit in CI. Note that only new bits should be written, not
|
// Set bit in CI. Note that only new bits should be written, not
|
||||||
// previous state.
|
// previous state.
|
||||||
m_data->cmd_issue = (1 << slot);
|
m_data->cmd_issue = (1 << slot);
|
||||||
@@ -290,27 +302,82 @@ port::read(uint64_t sector, size_t length)
|
|||||||
io_wait();
|
io_wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is where interrupt handler would begin
|
||||||
|
// TODO: handle other states in interrupt_status
|
||||||
|
|
||||||
if (m_data->interrupt_status & 0x40000000) {
|
if (m_data->interrupt_status & 0x40000000) {
|
||||||
log::error(logs::driver, "AHCI task file error");
|
log::error(logs::driver, "AHCI task file error");
|
||||||
// TODO: clean up!
|
// TODO: clean up!
|
||||||
return false;
|
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);
|
m_data->interrupt_status, m_data->serial_error);
|
||||||
|
|
||||||
console *cons = console::get();
|
uint32_t ci = m_data->cmd_issue;
|
||||||
uint8_t *p = (uint8_t *)buffers[0];
|
for (int i = 0; i < 32; ++i) {
|
||||||
for (int i = 0; i < 8; ++i) {
|
if (ci & (1 << i)) continue;
|
||||||
for (int j = 0; j < 16; ++j) {
|
|
||||||
cons->printf(" %02x", *p++);
|
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;
|
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<addr_t>(cmdt.entries[i].data_base_low) |
|
||||||
|
static_cast<addr_t>(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<addr_t>(cmdt.entries[i].data_base_low) |
|
||||||
|
static_cast<addr_t>(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
|
void
|
||||||
port::rebase()
|
port::rebase()
|
||||||
{
|
{
|
||||||
@@ -368,14 +435,4 @@ port::rebase()
|
|||||||
start_commands();
|
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
|
} // namespace ahci
|
||||||
|
|||||||
@@ -3,11 +3,13 @@
|
|||||||
/// Definition for AHCI ports
|
/// Definition for AHCI ports
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "kutil/vector.h"
|
||||||
|
|
||||||
namespace ahci {
|
namespace ahci {
|
||||||
|
|
||||||
struct cmd_list_entry;
|
struct cmd_list_entry;
|
||||||
struct cmd_table;
|
struct cmd_table;
|
||||||
|
enum class sata_signature : uint32_t;
|
||||||
enum class port_cmd : uint32_t;
|
enum class port_cmd : uint32_t;
|
||||||
struct port_data;
|
struct port_data;
|
||||||
|
|
||||||
@@ -25,12 +27,16 @@ public:
|
|||||||
/// Destructor
|
/// Destructor
|
||||||
~port();
|
~port();
|
||||||
|
|
||||||
enum class state { unimpl, inactive, active };
|
enum class state : uint8_t { unimpl, inactive, active };
|
||||||
|
|
||||||
/// Get the current state of this device
|
/// Get the current state of this device
|
||||||
/// \returns An enum representing the state
|
/// \returns An enum representing the state
|
||||||
state get_state() const { return m_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
|
/// Update the state of this object from the register data
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
@@ -46,25 +52,53 @@ public:
|
|||||||
/// Read data from the drive.
|
/// Read data from the drive.
|
||||||
/// \arg sector Starting sector to read
|
/// \arg sector Starting sector to read
|
||||||
/// \arg length Number of bytes 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
|
/// \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:
|
private:
|
||||||
/// Rebase the port command structures to a new location in system
|
/// Rebase the port command structures to a new location in system
|
||||||
/// memory, to be allocated from the page manager.
|
/// memory, to be allocated from the page manager.
|
||||||
void rebase();
|
void rebase();
|
||||||
|
|
||||||
/// Get a free command slot
|
/// Initialize a command structure
|
||||||
/// \returns The index of the command slot, or -1 if none available
|
/// \arg length The number of bytes of data needed in the PRDs
|
||||||
int get_cmd_slot();
|
/// \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;
|
uint8_t m_index;
|
||||||
state m_state;
|
state m_state;
|
||||||
port_data *m_data;
|
|
||||||
|
|
||||||
|
port_data *m_data;
|
||||||
void *m_fis;
|
void *m_fis;
|
||||||
cmd_list_entry *m_cmd_list;
|
cmd_list_entry *m_cmd_list;
|
||||||
cmd_table *m_cmd_table;
|
cmd_table *m_cmd_table;
|
||||||
|
|
||||||
|
enum class command_type : uint8_t { none, read, write };
|
||||||
|
|
||||||
|
struct pending
|
||||||
|
{
|
||||||
|
command_type type;
|
||||||
|
void *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
kutil::vector<pending> m_pending;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ahci
|
} // namespace ahci
|
||||||
|
|||||||
Reference in New Issue
Block a user