[Denali] Refactore interrupt handling.

This commit is contained in:
Drew Galbraith 2023-12-08 11:11:20 -08:00
parent 5a18d7d559
commit b3bc1c44d7
3 changed files with 70 additions and 27 deletions

View File

@ -58,6 +58,12 @@ struct AhciHba {
const uint32_t kCommand_FIS_Receive_Enable = (1 << 4); const uint32_t kCommand_FIS_Receive_Enable = (1 << 4);
const uint32_t kCommand_Start = 1; const uint32_t kCommand_Start = 1;
const uint32_t kInterrupt_D2H_FIS = 1;
const uint32_t kInterrupt_PIO_FIS = (1 << 1);
const uint32_t kInterrupt_DMA_FIS = (1 << 2);
const uint32_t kInterrupt_DeviceBits_FIS = (1 << 3);
const uint32_t kInterrupt_Unknown_FIS = (1 << 4);
struct AhciPort { struct AhciPort {
uint64_t command_list_base; uint64_t command_list_base;
uint64_t fis_base; uint64_t fis_base;
@ -237,7 +243,13 @@ struct DeviceToHostRegisterFis {
uint32_t reserved3; uint32_t reserved3;
} __attribute__((packed)); } __attribute__((packed));
struct SetDeviceBitsFis { struct SetDeviceBitsFis {
uint8_t fis_type;
uint8_t pmport_and_i;
uint8_t status;
uint8_t error;
uint32_t reserved;
} __attribute__((packed)); } __attribute__((packed));
struct ReceivedFis { struct ReceivedFis {

View File

@ -34,7 +34,7 @@ glcr::ErrorOr<glcr::UniquePtr<AhciController>> AhciController::Init(
} }
glcr::ErrorOr<AhciDevice*> AhciController::GetDevice(uint64_t id) { glcr::ErrorOr<AhciDevice*> AhciController::GetDevice(uint64_t id) {
if (id >= 32) { if (id >= num_ports_) {
return glcr::INVALID_ARGUMENT; return glcr::INVALID_ARGUMENT;
} }
@ -140,9 +140,8 @@ void AhciController::InterruptLoop() {
while (true) { while (true) {
uint64_t bytes, caps; uint64_t bytes, caps;
check(ZPortRecv(irq_port_cap_, &bytes, nullptr, &caps, nullptr)); check(ZPortRecv(irq_port_cap_, &bytes, nullptr, &caps, nullptr));
for (uint64_t i = 0; i < 32; i++) { for (uint64_t i = 0; i < num_ports_; i++) {
if (!devices_[i].empty() && devices_[i]->IsInit() && if (!devices_[i].empty() && (ahci_hba_->interrupt_status & (1 << i))) {
(ahci_hba_->interrupt_status & (1 << i))) {
devices_[i]->HandleIrq(); devices_[i]->HandleIrq();
ahci_hba_->interrupt_status &= ~(1 << i); ahci_hba_->interrupt_status &= ~(1 << i);
} }

View File

@ -34,7 +34,9 @@ AhciDevice::AhciDevice(AhciPort* port) : port_struct_(port) {
(paddr + 0x500) + (0x100 * i); (paddr + 0x500) + (0x100 * i);
commands_[i] = nullptr; commands_[i] = nullptr;
} }
port_struct_->interrupt_enable = 0xFFFFFFFF; port_struct_->interrupt_enable =
kInterrupt_D2H_FIS | kInterrupt_PIO_FIS | kInterrupt_DMA_FIS |
kInterrupt_DeviceBits_FIS | kInterrupt_Unknown_FIS;
port_struct_->sata_error = -1; port_struct_->sata_error = -1;
port_struct_->command |= kCommand_Start; port_struct_->command |= kCommand_Start;
} }
@ -77,48 +79,78 @@ void AhciDevice::DumpInfo() {
dbgln("Int enable: {x}", port_struct_->interrupt_enable); dbgln("Int enable: {x}", port_struct_->interrupt_enable);
} }
bool CheckFisType(FIS_TYPE expected, uint8_t actual) {
if (expected == actual) {
return true;
}
dbgln("BAD FIS TYPE (exp,act): {x}, {x}", static_cast<uint64_t>(expected),
static_cast<uint64_t>(actual));
return false;
}
void AhciDevice::HandleIrq() { void AhciDevice::HandleIrq() {
uint32_t int_status = port_struct_->interrupt_status; uint32_t int_status = port_struct_->interrupt_status;
// FIXME: Probably only clear the interrupts we know how to handle.
port_struct_->interrupt_status = int_status; port_struct_->interrupt_status = int_status;
uint32_t commands_finished = commands_issued_ & ~port_struct_->command_issue; bool has_error = false;
if (int_status & kInterrupt_D2H_FIS) {
// FIXME: Pass error codes to the callback. dbgln("D2H Received");
for (uint64_t i = 0; i < 32; i++) {
if (commands_finished & (1 << i)) {
commands_issued_ &= ~(1 << i);
commands_[i]->SignalComplete();
}
}
// TODO: Do something with this information.
if (int_status & 0x1) {
// Device to host. // Device to host.
volatile DeviceToHostRegisterFis& fis = volatile DeviceToHostRegisterFis& fis =
received_fis_->device_to_host_register_fis; received_fis_->device_to_host_register_fis;
if (fis.fis_type != FIS_TYPE_REG_D2H) { if (!CheckFisType(FIS_TYPE_REG_D2H, fis.fis_type)) {
dbgln("BAD FIS TYPE (exp,act): {x}, {x}",
static_cast<uint64_t>(FIS_TYPE_REG_D2H),
static_cast<uint64_t>(fis.fis_type));
return; return;
} }
if (fis.error) { if (fis.error) {
dbgln("D2H err: {x}", fis.error); dbgln("D2H err: {x}", fis.error);
dbgln("status: {x}", fis.status); dbgln("status: {x}", fis.status);
has_error = true;
} }
} }
if (int_status & 0x2) { if (int_status & kInterrupt_PIO_FIS) {
dbgln("PIO Received");
// PIO. // PIO.
volatile PioSetupFis& fis = received_fis_->pio_set_fis; volatile PioSetupFis& fis = received_fis_->pio_set_fis;
if (fis.fis_type != FIS_TYPE_PIO_SETUP) { if (!CheckFisType(FIS_TYPE_PIO_SETUP, fis.fis_type)) {
dbgln("BAD FIS TYPE (exp,act): {x}, {x}",
static_cast<uint64_t>(FIS_TYPE_PIO_SETUP),
static_cast<uint64_t>(fis.fis_type));
return; return;
} }
if (fis.error) { if (fis.error) {
dbgln("PIO err: {x}", fis.error); dbgln("PIO err: {x}", fis.error);
dbgln("status: {x}", fis.status);
has_error = true;
}
}
if (int_status & kInterrupt_DMA_FIS) {
dbgln("DMA Received");
volatile DmaFis& fis = received_fis_->dma_fis;
if (!CheckFisType(FIS_TYPE_DMA_SETUP, fis.fis_type)) {
return;
}
// TODO: Actually do something with this FIS.
}
if (int_status & kInterrupt_DeviceBits_FIS) {
dbgln("Device Bits Received");
volatile SetDeviceBitsFis& fis = received_fis_->set_device_bits_fis;
if (!CheckFisType(FIS_TYPE_DEV_BITS, fis.fis_type)) {
return;
}
if (fis.error) {
dbgln("SetDeviceBits err: {x}", fis.error);
dbgln("status: {x}", fis.status);
has_error = true;
}
}
if (int_status & kInterrupt_Unknown_FIS) {
dbgln("Unknown FIS recieved, type: {x}", received_fis_->unknown_fis[0]);
}
uint32_t commands_finished = commands_issued_ & ~port_struct_->command_issue;
for (uint64_t i = 0; i < 32; i++) {
if (commands_finished & (1 << i)) {
commands_issued_ &= ~(1 << i);
// FIXME: Pass error codes to the callback.
commands_[i]->SignalComplete();
commands_[i] = nullptr;
} }
} }
} }