My kernel now uses X2APIC by default.
I reset XHC, setup those buffers (I don't even know well what they are for) TRBs, etc...
Enable interrupts, clear interrupt status, loop through the ports and reset the ports that are connected (I was testing and just was resetting all the ports hoping for a single Interrupts).
I test on QEMU, (it has MSIx BAR0 Off 3000) No interrupts
then on a laptop (uses MSI 64 Bit) No interrupts.
then on the desktop I code with (Not remembering correctly) but no interrupts.
I'm using X2APIC Now.
I map the whole IDT, so if any interrupt happens it will have its handler, and ACPI Shutdown button press already works fine. and I can press multiple times and the irq service shows a message.
Code: Select all
void XhciSendEnableSlotCommand(XHCI* xhc) {
UINT32 index = xhc->CmdRingIndex;
XHCI_TRB* trb = &xhc->CmdRing[index];
trb->Parameter = 0;
trb->Status = 0;
trb->Control = (TRB_TYPE_ENABLE_SLOT << 10) | (xhc->CycleState ? 1 : 0);
xhc->CmdRingIndex++;
if (xhc->CmdRingIndex >= CMD_RING_SIZE) {
xhc->CmdRingIndex = 0;
xhc->CycleState = !xhc->CycleState;
}
((volatile UINT32*)((UINT8*)xhc->MmioBase + xhc->CapRegs->DBOFF))[0] = 0;
}
BOOLEAN XhcReset(XHCI* xhc) {
UINT32 Cmd = xhc->OpRegs->USBCMD;
xhc->OpRegs->USBCMD = Cmd | USBCMD_HCRST;
for (UINT64 i = 0; i < 10000; i++) {
if (!(xhc->OpRegs->USBCMD & USBCMD_HCRST))
return TRUE;
kStall(_MICROTONANO(100));
}
return FALSE;
}
BOOLEAN XhcStart(XHCI* xhc) {
if (!(xhc->OpRegs->USBSTS & USBSTS_HCH)) return TRUE;
xhc->OpRegs->USBCMD |= USBCMD_RS;
for (int i = 0; i < 10000; i++) {
if (!(xhc->OpRegs->USBSTS & USBSTS_HCH)) return TRUE;
kStall(_MICROTONANO(100));
}
return FALSE;
}
void KCALL XhciPortReset(XHCI* xhc, UINT8 Port) {
xhc->OpRegs->PORTSC[Port] |= (1 << 4);
for (int i = 0; i < 10000; i++) {
if (!(xhc->OpRegs->PORTSC[Port] & (1 << 4))) {
// xhc->OpRegs->PORTSC[Port] |= (1 << 1) | (1 << 3);
XhciSendEnableSlotCommand(xhc);
return;
}
kStall(_MICROTONANO(100));
}
KConOut(L"Port#%d reset failed", Port);
}
STS KCALL XhciIrqHandler(UINT32 Irq, XHCI* xhc) {
KConOut(L"XHC Interrupt");
return 0;
}
STS KCALL XhciDeviceDetectEvent(UINT64 DetectCode, void* Context, void* Pci) {
PcieInitDevice(Pci);
void* base = (void*)XhciReadMmioBase(Pci);
if (!base) return 2;
KConOut(L"XHCI Base %lx", base);
XHCI* xhc = kvAllocate(sizeof(XHCI));
xhc->MmioBase = kvMapMemory(base, 0x1000, PAGE_PRESENT | PAGE_RW | PAGE4KB_UNCACHEABLE);
xhc->CapRegs = (XHCI_CAP_REGS*)xhc->MmioBase;
xhc->OpRegs = (XHCI_OP_REGS*)((UINT8*)xhc->MmioBase + xhc->CapRegs->CAPLENGTH);
xhc->MaxPorts = (xhc->CapRegs->HCSPARAMS1 >> 24) & 0xFF;
KConOut(L"HCSPARAM1 %x %d CAPLEN %d", xhc->CapRegs->HCSPARAMS1, xhc->CapRegs->HCSPARAMS1 >> 24, xhc->CapRegs->CAPLENGTH);
UINT32 Rtsoff = xhc->CapRegs->RTSOFF & ~0x1F;
xhc->RuntimeRegs = (XHCI_RUNTIME_REGS*)((UINT8*)xhc->MmioBase + Rtsoff);
xhc->CycleState = 1;
xhc->CmdRingIndex = 0;
if (!XhcReset(xhc)) return 2;
if (PcieEnableMsiX(Pci, XhciIrqHandler, xhc)) {
if (PcieEnableMsi(Pci, XhciIrqHandler, xhc)) return 4;
}
xhc->OpRegs->USBSTS = xhc->OpRegs->USBSTS;
xhc->CmdRing = kmAllocatePhysicalPages(1);
EnhancedMemClr(xhc->CmdRing, 0x1000);
xhc->OpRegs->CRCR = (UINT64)xhc->CmdRing | (xhc->CycleState ? CRCR_CYCLE_BIT : 0);
xhc->EvRing = kmAllocatePhysicalPages(1);
EnhancedMemClr(xhc->EvRing, 0x1000);
xhc->Erst = kmAllocatePhysicalPages(1);
xhc->Erst->SegmentBaseAddress = (UINT64)xhc->EvRing;
xhc->Erst->SegmentSize = 256;
xhc->Erst->Reserved = 0;
xhc->RuntimeRegs->INTERRUPTER[0].ERSTSZ = 1;
xhc->RuntimeRegs->INTERRUPTER[0].ERSTBA = (UINT64)xhc->Erst;
xhc->RuntimeRegs->INTERRUPTER[0].ERDP = (UINT64)xhc->EvRing | (1ULL << 3);
xhc->Dcbaa = kmAllocatePhysicalPages(1);
EnhancedMemClr(xhc->Dcbaa, 0x1000);
xhc->OpRegs->DCBAAP = (UINT64)xhc->Dcbaa;
KConOut(L"Starting xhc");
if (!XhcStart(xhc)) return 3;
xhc->OpRegs->USBCMD |= USBCMD_INTE;
*((volatile UINT32*)((UINT8*)xhc->MmioBase + 0x38)) = 0x3F; // USBINTR
*((volatile UINT32*)((UINT8*)xhc->MmioBase + 0x3C)) = 0; // IMOD
*((volatile UINT32*)((UINT8*)xhc->MmioBase + 0x40)) = IMGMT_INTR_ENABLE; // IMGMT
KConOut(L"xhc maxports %d", xhc->MaxPorts);
for (int i = 0; i < xhc->MaxPorts; i++) {
UINT32 PortStatus = xhc->OpRegs->PORTSC[i];
KConOut(L"PORT#%d STATUS : %x", i, PortStatus);
XhciPortReset(xhc, i);
}
while (1) __halt();
return 0;
}
Code: Select all
DDKLIB void DDKAPI PcieInitDevice(void* PcieBase) {
// Set bus muster
PcieWrite16(PcieBase, 0x4, PcieRead16(PcieBase, 0x4) | (3 << 1));
}
DDKLIB STS DDKAPI PcieEnableMsi(void* PcieBase, KIRQHANDLER Handler, void* Context){
if(!(PcieRead8(PcieBase, PCI_STATUS) & (1 << 4))) return 2; // Capabilites are not supported
// Enable PCI Interrupts
// Remove Interrupt Disable (0x400)
PcieWrite16(PcieBase, PCI_COMMAND, (PcieRead16(PcieBase, PCI_COMMAND) & ~0x400));
UINT8 Cptr = PcieRead8(PcieBase, PCI_CAPABILITYPTR) & ~3;
while(Cptr) {
UINT8 CapId = PcieRead8(PcieBase, Cptr + PCI_CAPABILITY_ID);
if(CapId == PCI_CAPABILITY_MSI) {
KConOut(L"Found MSI Capability");
// Allocate an interrupt
UINT8 Iv = 0x40;
UINT64 ProcessorId = 0;
STS s;
// if(NERROR((s = KeInstallInterruptHandler(&Iv, &ProcessorId, 0, Handler, Context)))) {
// return s;
// }
// PROCESSOR* cpu = KeGetProcessorById(ProcessorId);
// PROCESSOR_IDENTIFICATION_DATA Id;
// KeProcessorReadIdentificationData(cpu, &Id);
// KDebugPrint("MSI APIC ID %x ACPI ID %x", ProcessorId, Id.AcpiId);
// ProcessorId = Id.AcpiId;
UINT64 Address = (__readmsr(0x1B)/*APIC Base MSR*/ & ((UINT64)~0xFFF)) | (ProcessorId << 12);
if((PcieRead16(PcieBase, Cptr + MSI_MESSAGE_CONTROL) & 0x80)) {
// Use MSI64
PcieWrite64(PcieBase, Cptr + MSI_MESSAGE_ADDRESS, Address);
PcieWrite32(PcieBase, Cptr + MSI64_MESSAGE_DATA, Iv | (1 << 14));
PcieWrite32(PcieBase, Cptr + MSI64_MASK, 0);
KConOut(L"Done MSI 64 bit");
} else {
// Use MSI32
PcieWrite32(PcieBase, Cptr + MSI_MESSAGE_ADDRESS, Address);
PcieWrite32(PcieBase, Cptr + MSI_MESSAGE_DATA, Iv | (1 << 14));
PcieWrite32(PcieBase, Cptr + MSI_MASK, 0);
KConOut(L"Done MSI 32 bit");
}
// Enable MSI
PcieWrite16(PcieBase, Cptr + MSI_MESSAGE_CONTROL, (PcieRead16(PcieBase, Cptr + MSI_MESSAGE_CONTROL)) | 1);
return 0;
}
Cptr = PcieRead8(PcieBase, Cptr + PCI_CAPABILITY_NEXT) & ~3;
}
KConOut(L"MSI Not found");
return 2;
}
#include <intrin.h>
DDKLIB STS DDKAPI PcieEnableMsiX(void* PcieBase, KIRQHANDLER Handler, void* Context){
if(!(PcieRead8(PcieBase, PCI_STATUS) & (1 << 4))) return 2; // Capabilites are not supported
// Enable PCI Interrupts
// Remove Interrupt Disable (0x400)
PcieWrite16(PcieBase, PCI_COMMAND, (PcieRead16(PcieBase, PCI_COMMAND) & ~0x400));
UINT8 Cptr = PcieRead8(PcieBase, PCI_CAPABILITYPTR) & ~3;
while(Cptr) {
UINT8 CapId = PcieRead8(PcieBase, Cptr + PCI_CAPABILITY_ID);
if(CapId == PCI_CAPABILITY_MSIX) {
KConOut(L"Found MSI-X Capability");
// Allocate an interrupt
UINT8 Iv = 0x40;
UINT64 ProcessorId = 0;
STS s;
UINT32 TableInfo = PcieRead32(PcieBase, Cptr + MSI_X_TABLE_OFFSET);
UINT8 TableBar = TableInfo & 0x7;
UINT32 TableOffset = TableInfo & ~0x7;
KConOut(L"MSI-X Table bar : %d Off %x , Phys Addr %lx", (int)TableBar, TableOffset, PcieReadBaseAddress(PcieBase, TableBar));
void* TableAddress = kvMapMemory(PcieReadBaseAddress(PcieBase, TableBar), 0x10, PAGE_PRESENT | PAGE_RW | PAGE4KB_UNCACHEABLE);
volatile UINT32* MsixTable = (UINT32*)((UINT8*)TableAddress + TableOffset);
UINT64 MsgAddress = (__readmsr(0x1B)/*APIC Base MSR*/ & ((UINT64)~0xFFFULL)) | (ProcessorId << 32);
KConOut(L"MSG ADDR %lx", MsgAddress);
UINT64 MsgData = (Iv) | (1 << 14);
MsixTable[0] = (UINT32)(MsgAddress & 0xFFFFFFFF);
MsixTable[1] = (UINT32)(MsgAddress >> 32);
MsixTable[2] = MsgData;
MsixTable[3] = 0; // unmasked
PcieWrite16(PcieBase, Cptr + MSI_X_CONTROL_OFFSET, PcieRead16(PcieBase, Cptr + MSI_X_CONTROL_OFFSET) | 1);
KConOut(L"MSI-X Successfully enabled.");
return 0;
}
Cptr = PcieRead8(PcieBase, Cptr + PCI_CAPABILITY_NEXT) & ~3;
}
KConOut(L"MSI-X Not found");
return 2;
}
DDKLIB void* DDKAPI PcieReadBaseAddress(void* Pcie, UINT32 BarIndex){
UINT64 Bar = (UINT64)PcieRead32(Pcie, PCI_BAR + (BarIndex << 2));
if((Bar & PCI_BAR_64BIT)) {
Bar |= (UINT64)PcieRead32(Pcie, PCI_BAR + 4 + (BarIndex << 2)) << 32;
}
return (void*)(Bar & ~0xFULL);
}


