mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 08:24:32 -08:00
Add initial ATA identify support to AHCI driver
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<fis_register_h2d *>(&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<fis_register_h2d *>(&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<addr_t>(cmdt.entries[0].data_base_low) |
|
||||
static_cast<addr_t>(cmdt.entries[0].data_base_high) << 32;
|
||||
|
||||
log::debug(logs::driver, "Reading ident PRD:");
|
||||
|
||||
uint16_t *mem = reinterpret_cast<uint16_t *>(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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user