Further parse AHCI information.

Send an IDENTIFY command to each drive and set up a hook to handle
interrupts.
This commit is contained in:
Drew Galbraith 2023-06-12 19:20:51 -07:00
parent 4e1888bd24
commit 0f0e39d1e9
25 changed files with 721 additions and 90 deletions

View file

@ -9,13 +9,30 @@ namespace {
const uint64_t kSataPciPhys = 0xB00FA000;
const uint64_t kPciSize = 0x1000;
const uint64_t kGhc_InteruptEnable = 0x2;
void interrupt_thread(void* void_driver) {
AhciDriver* driver = static_cast<AhciDriver*>(void_driver);
dbgln("this %lx", driver);
driver->InterruptLoop();
crash("Driver returned from interrupt loop", Z_ERR_UNIMPLEMENTED);
}
} // namespace
z_err_t AhciDriver::Init() {
RET_ERR(LoadPciDeviceHeader());
RET_ERR(LoadCapabilities());
dbgln("ABAR: %x", pci_device_header_->abar);
dbgln("Interrupt line: %x", pci_device_header_->interrupt_line);
dbgln("Interrupt pin: %x", pci_device_header_->interrupt_pin);
RET_ERR(RegisterIrq());
RET_ERR(LoadHbaRegisters());
dbgln("Version: %x", ahci_hba_->version);
ahci_hba_->global_host_control |= kGhc_InteruptEnable;
RET_ERR(LoadDevices());
DumpCapabilities();
DumpPorts();
return Z_OK;
@ -26,7 +43,7 @@ void AhciDriver::DumpCapabilities() {
uint32_t caps = ahci_hba_->capabilities;
dbgln("Num Ports: %u", (caps & 0x1F) + 1);
dbgln("Num Command Slots: %u", (caps & 0x1F00) >> 8);
dbgln("Num Command Slots: %u", ((caps & 0x1F00) >> 8) + 1);
if (caps & 0x20) {
dbgln("External SATA");
}
@ -97,33 +114,33 @@ void AhciDriver::DumpCapabilities() {
if (caps & 0x10) {
dbgln("Aggressive device sleep management");
}
dbgln("Control %x", ahci_hba_->global_host_control);
}
void AhciDriver::DumpPorts() {
dbgln("Ports implemented %x", ahci_hba_->port_implemented);
uint64_t port_index = 0;
uint32_t ports_implemented = ahci_hba_->port_implemented;
while (ports_implemented) {
if (!(ports_implemented & 0x1)) {
ports_implemented >>= 1;
port_index++;
for (uint64_t i = 0; i < 6; i++) {
AhciDevice& dev = devices_[i];
if (!dev.IsInit()) {
continue;
}
uint64_t port_addr =
reinterpret_cast<uint64_t>(ahci_hba_) + 0x100 + (0x80 * port_index);
AhciPort* port = reinterpret_cast<AhciPort*>(port_addr);
dbgln("");
dbgln("Port %u:", port_index);
dbgln("Comlist: %lx", port->command_list_base);
dbgln("FIS: %lx", port->fis_base);
dbgln("Command: %x", port->command);
dbgln("Signature: %x", port->signature);
dbgln("SATA status: %x", port->sata_status);
dbgln("Port %u:", i);
dev.DumpInfo();
}
}
ports_implemented >>= 1;
port_index++;
void AhciDriver::InterruptLoop() {
dbgln("this %lx", this);
while (true) {
uint64_t type, bytes, caps;
check(ZPortRecv(irq_port_cap_, 0, 0, 0, 0, &type, &bytes, &caps));
for (uint64_t i = 0; i < 6; i++) {
if (devices_[i].IsInit()) {
devices_[i].HandleIrq();
}
}
}
}
@ -137,6 +154,46 @@ z_err_t AhciDriver::LoadPciDeviceHeader() {
return Z_OK;
}
z_err_t AhciDriver::LoadCapabilities() {
if (!(pci_device_header_->status_reg & 0x10)) {
dbgln("No caps!");
return Z_ERR_INVALID;
}
uint8_t* base = reinterpret_cast<uint8_t*>(pci_device_header_);
uint16_t offset = pci_device_header_->cap_ptr;
do {
uint16_t* cap = reinterpret_cast<uint16_t*>(base + offset);
switch (*cap & 0xFF) {
case 0x01:
dbgln("Power Management");
break;
case 0x05:
dbgln("MSI");
break;
case 0x12:
dbgln("SATA");
break;
default:
dbgln("Unrecognized cap");
break;
}
offset = (*cap & 0xFF00) >> 8;
} while (offset);
return Z_OK;
}
z_err_t AhciDriver::RegisterIrq() {
if (pci_device_header_->interrupt_pin == 0) {
crash("Can't register IRQ without a pin num", Z_INVALID);
}
uint64_t irq_num = Z_IRQ_PCI_BASE + pci_device_header_->interrupt_pin - 1;
RET_ERR(ZIrqRegister(irq_num, &irq_port_cap_));
dbgln("this %lx", this);
irq_thread_ = Thread(interrupt_thread, this);
return Z_OK;
}
z_err_t AhciDriver::LoadHbaRegisters() {
uint64_t vmmo_cap;
RET_ERR(
@ -147,3 +204,22 @@ z_err_t AhciDriver::LoadHbaRegisters() {
ahci_hba_ = reinterpret_cast<AhciHba*>(vaddr);
return Z_OK;
}
z_err_t AhciDriver::LoadDevices() {
// FIXME: Don't set this up so we hardcode 6 devices.
for (uint8_t i = 0; i < 6; i++) {
if (!(ahci_hba_->port_implemented & (1 << i))) {
continue;
}
uint64_t port_addr =
reinterpret_cast<uint64_t>(ahci_hba_) + 0x100 + (0x80 * i);
devices_[i] = AhciDevice(reinterpret_cast<AhciPort*>(port_addr));
if (!devices_[i].IsInit()) {
continue;
}
dbgln("Identify %u", i);
uint16_t* identify;
devices_[i].SendIdentify(&identify);
}
return Z_OK;
}