[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_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 {
uint64_t command_list_base;
uint64_t fis_base;
@ -237,7 +243,13 @@ struct DeviceToHostRegisterFis {
uint32_t reserved3;
} __attribute__((packed));
struct SetDeviceBitsFis {
uint8_t fis_type;
uint8_t pmport_and_i;
uint8_t status;
uint8_t error;
uint32_t reserved;
} __attribute__((packed));
struct ReceivedFis {

View File

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

View File

@ -34,7 +34,9 @@ AhciDevice::AhciDevice(AhciPort* port) : port_struct_(port) {
(paddr + 0x500) + (0x100 * i);
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_->command |= kCommand_Start;
}
@ -77,48 +79,78 @@ void AhciDevice::DumpInfo() {
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() {
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;
uint32_t commands_finished = commands_issued_ & ~port_struct_->command_issue;
// FIXME: Pass error codes to the callback.
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) {
bool has_error = false;
if (int_status & kInterrupt_D2H_FIS) {
dbgln("D2H Received");
// Device to host.
volatile DeviceToHostRegisterFis& fis =
received_fis_->device_to_host_register_fis;
if (fis.fis_type != FIS_TYPE_REG_D2H) {
dbgln("BAD FIS TYPE (exp,act): {x}, {x}",
static_cast<uint64_t>(FIS_TYPE_REG_D2H),
static_cast<uint64_t>(fis.fis_type));
if (!CheckFisType(FIS_TYPE_REG_D2H, fis.fis_type)) {
return;
}
if (fis.error) {
dbgln("D2H err: {x}", fis.error);
dbgln("status: {x}", fis.status);
has_error = true;
}
}
if (int_status & 0x2) {
if (int_status & kInterrupt_PIO_FIS) {
dbgln("PIO Received");
// PIO.
volatile PioSetupFis& fis = received_fis_->pio_set_fis;
if (fis.fis_type != FIS_TYPE_PIO_SETUP) {
dbgln("BAD FIS TYPE (exp,act): {x}, {x}",
static_cast<uint64_t>(FIS_TYPE_PIO_SETUP),
static_cast<uint64_t>(fis.fis_type));
if (!CheckFisType(FIS_TYPE_PIO_SETUP, fis.fis_type)) {
return;
}
if (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;
}
}
}