XHCI Interrupts not working

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
devc1
Member
Member
Posts: 461
Joined: Fri Feb 11, 2022 4:55 am

XHCI Interrupts not working

Post by devc1 »

So I want to make a driver for XHCI since modern hardware does not seem to really need a driver for other HCIs, and I already wrote a minimal EHCI driver that enumerates USB devices (I think more than a year ago) and displays their names.
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;
}


This is the functions that setup pcie etc... (experimented with a fixed interrupt number temporarely and APICID 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);
}
User avatar
BenLunt
Member
Member
Posts: 1029
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: XHCI Interrupts not working

Post by BenLunt »

devc1 wrote: Sun Jul 20, 2025 2:35 pm I reset XHC, setup those buffers (I don't even know well what they are for) TRBs, etc...
Hi,

You aren't going to get very many interrupts if you don't have a valid set of rings with valid TRBs in them. Especially the Interrupters. :-)
So if you are looking for interrupts, I suggest you find out what "they are for" and then try to see if you get any interrupts.

- Ben
https://www.fysnet.net/the_universal_serial_bus.htm
devc1
Member
Member
Posts: 461
Joined: Fri Feb 11, 2022 4:55 am

Re: XHCI Interrupts not working

Post by devc1 »

I just want to have a port status change event like in ehci but I get absolutely 0 interrupts, I can enumerate a device connected to port 0 and even when typing in qemu info usb it shows a usb flash drive 5000Mb/s.
User avatar
bellezzasolo
Member
Member
Posts: 163
Joined: Sun Feb 20, 2011 2:01 pm

Re: XHCI Interrupts not working

Post by bellezzasolo »

devc1 wrote: Tue Jul 22, 2025 10:22 am I just want to have a port status change event like in ehci but I get absolutely 0 interrupts, I can enumerate a device connected to port 0 and even when typing in qemu info usb it shows a usb flash drive 5000Mb/s.
All interrupts like port status get sent through an interrupter event ring, if that's not configured, no interrupts for you.

This is my implementation, definitely got interrupts from the xHC.
https://github.com/ChaiSoft/ChaiOS/blob ... l/xhci.cpp
Whoever said you can't do OS development on Windows?
https://github.com/ChaiSoft/ChaiOS
User avatar
BenLunt
Member
Member
Posts: 1029
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: XHCI Interrupts not working

Post by BenLunt »

Have you ran your code through Bochs? If you turn on BX_DEBUGs for the xHCI, it will log all of the access to/from the xHCI as well as errors.
For example, if you try to insert a command in the Command Ring without the Command Ring enabled, it will log an error.
Maybe this will help you find out why you are not receiving any interrupts.

Bochs can be found at https://bochs.sourceforge.io/ and https://github.com/bochs-emu/bochs

Ben
- https://www.fysnet.net/osdesign_book_series.htm
devc1
Member
Member
Posts: 461
Joined: Fri Feb 11, 2022 4:55 am

Re: XHCI Interrupts not working

Post by devc1 »

I found out why (after reading the pcie spec), I found out I toggled the wrong bit in MSI-X. Now interrupts work, I receive event trbs, I do enable slot command and receive command completion code 1 with status 0x10000000 (I think it means slotId 1). I’m trying to do address device, and it returns completion code 5.

How to build a basic input context to address device (let’s say for a QEMU Flash drive 5000mb/s)?
I suppose that DeviceSlot is 1 (experimenting with first port 0) and send command (addressdevice<<10 | 1<<24 | cycle)

Then I do doorbell[0] = 0;
Then I get interrupt with completion code 5.

Honestly Basic Usb support maybe feels much easier than I thaught, especially if I figure out how to build this input context :)

However talking about Bochs, does it support Uefi? Because my Os for now is UEFI bootable only.
devc1
Member
Member
Posts: 461
Joined: Fri Feb 11, 2022 4:55 am

Re: XHCI Interrupts not working

Post by devc1 »

Update: Now set device context works, u just have to follow the xhci spec. Returns command completion status 1 which I think means success.

I use the device slot returned after enable slot trb with control>>24

However when I send a get device descriptor trb and ring doorbell[1] = 1 (for control endpoint) and device slot 1 I get no interrupts.
User avatar
BenLunt
Member
Member
Posts: 1029
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: XHCI Interrupts not working

Post by BenLunt »

I'm glad you are making progress. That's good.
devc1 wrote: Thu Jul 24, 2025 5:37 am However talking about Bochs, does it support Uefi? Because my Os for now is UEFI bootable only.
Have a look at this thread: viewtopic.php?p=287915#p287915

Ben
- https://www.fysnet.net/fontedit/index.htm
devc1
Member
Member
Posts: 461
Joined: Fri Feb 11, 2022 4:55 am

Re: XHCI Interrupts not working

Post by devc1 »

Now i get completion code 1 in device address cmd then i set device context address in dcbaa[slotid] .

I do a basic getdevicedescriptor and I get no interrupts.
I tried even waiting a second before reading device descriptor (in case processed but no interrupts) and I get nothing.

I assume slot id = 1 and only test the first port (port index 0 which is portid 1) Im using qemu with 2 5000mb/s flash drives to test this,

I check 64 bit addressing capability set to 1, and contextSize in HCCPARAMS1 set to 0 in Qemu meaning 32 byte input control context.

When I write the transfer ring with trbs, I ring doorbell[1]=1; assuming slot Id is 1 as I get in the interrupt, and =1 meaning control endpoint?

Here is the code:

Code: Select all


void KCALL XhciSetupInputContext(INPUTCONTEXT* ctx, UINT32 port_number, UINT64 ep0_dequeue_phys){

    ctx->ControlContext.AddContextFlags = 3;
    ctx->ControlContext.DropContextFlags = 0;
    // sizeof(ctx->ControlContext)
    ctx->SlotContext.LastContextIndex=1;
    ctx->SlotContext.RootHubPortNumber=1;

    ctx->EndpointContext[0].EndpointType=4;
    ctx->EndpointContext[0].MaxPacketSize=64;
    ctx->EndpointContext[0].EpState=1;
    ctx->EndpointContext[0].TrDequeuePtr = ep0_dequeue_phys | 1;
    // ctx->EndpointContext[1].Interval = 100;
    // ctx->EndpointContext[0].AverageTrbLength = 64;

}


void KCALL XhciPortReset(XHCI* xhc, UINT8 Port) {
    // xhc->OpRegs->PORTSC[Port] |= (1 << 4);
    xhc->OpRegs->PORTSC[Port] |= (1 << 4);

            // KConOut(L"USBSTS %x INT0 %x", Port, xhc->OpRegs->USBSTS, xhc->RuntimeRegs->INTERRUPTER[0].IMAN);

    for (int i = 0; i < 10000; i++) {
        if (!(xhc->OpRegs->PORTSC[Port] & (1 << 4))) {
            // xhc->OpRegs->PORTSC[Port] |= (1 << 1) | (1 << 3);
            KConOut(L"Port#%d reset successful USBSTS %x INT0 %x", Port, xhc->OpRegs->USBSTS, xhc->RuntimeRegs->INTERRUPTER[0].IMAN);
            
            TrbPush(xhc->CmdRingContext, 0, 0, (TRB_TYPE_ENABLE_SLOT << 10) | (PORTSC_SPEED(xhc->OpRegs->PORTSC[Port]) << 16));
            xhc->Doorbell[0]=0;
            
            
            XHCI_TRB_CONTEXT* ctx = TrbContextCreate();

            UINT64 Ep0DequeueTrb = (UINT64)ctx->TransferRing;
            INPUTCONTEXT* Context = kmAllocatePhysicalPages(1);
            EnhancedMemClr(Context, 0x1000);
            XhciSetupInputContext(Context, 0, (UINT64)ctx->TransferRing);


            
            kStall(_MSTONANO(500));
            TrbPush(xhc->CmdRingContext, (UINT64)Context, 0, (TRB_TYPE_ADDRESS_DEVICE << 10) | (1 << 24));
            
            xhc->Doorbell[0]=0;
            
            kStall(_MSTONANO(500));
            xhc->Dcbaa[0] = (UINT64)&Context->SlotContext;
            
            KConOut(L"USB Device address %d", Context->SlotContext.UsbDeviceAddress);
            USB_SETUP_PACKET SetupPacket = {0};
            SetupPacket.bmRequestType =0x80;
            SetupPacket.bRequest = 6;
            SetupPacket.wValue = (1 << 8); // descriptor type 1 index 0
            SetupPacket.wLength = sizeof(USB_DEVICE_DESCRIPTOR);
            SetupPacket.wIndex = 0;
            TrbPush(ctx, *(UINT64*)&SetupPacket, 0, (TRB_TYPE_SETUP_STAGE << 10) | TRB_IDT);
            USB_DEVICE_DESCRIPTOR* DeviceDescriptor = kmAllocatePhysicalPages(1);
            TrbPush(ctx, (UINT64)DeviceDescriptor, sizeof(*DeviceDescriptor), (TRB_TYPE_DATA_STAGE << 10) | TRB_IOC | (TRB_DIR_IN));
            TrbPush(ctx, 0, 0, (TRB_TYPE_STATUS_STAGE << 10) | TRB_DIR_OUT | TRB_IOC);

            xhc->Doorbell[0] = 1;
            kStall(_MSTONANO(1000));
            KConOut(L"Device Class %d SubClass %d Protocol %d", DeviceDescriptor->bDeviceClass, DeviceDescriptor->bDeviceSubClass, DeviceDescriptor->bDeviceProtocol);
            XhciInfo(xhc);
            while(1) __halt();
            return;
        }
        
        kStall(_MICROTONANO(100));
    }
    KConOut(L"Port#%d reset failed", Port);
}


devc1
Member
Member
Posts: 461
Joined: Fri Feb 11, 2022 4:55 am

Re: XHCI Interrupts not working

Post by devc1 »

Update, I made it work on Qemu, I can read device name etc… using control transfer (Another time, the spec helped)

But now On real hardware it keeps giving command completion code 9 on enable slot command then if I let it proceed to address device (with slot = 0 which obviously is invalid) it returns completion code 11.

I ring doorbell on each transfer.

It has a field SlotType I enumerate those at startup using the extendedpointer, it is generally set to 0 as far as I’ve seen.

I even set Crcr again after run stop. Everything works fine on Qemu, the port disables after that enable slot error but the other ports stay working.

Any one has a fix?
devc1
Member
Member
Posts: 461
Joined: Fri Feb 11, 2022 4:55 am

Re: XHCI Interrupts not working

Post by devc1 »

Hello, Now I got boot protocol mouse and keyboard working on both qemu and vmware, I noticed vmware only reports an HID Mouse, is the keyboard in another controller?

no one still replied I cant even send a successful enable slot on real hardware it returns completion code 9 (I think it means no slot available) I did reset the xhc before doing enable slot and I wait for like 200ms before leaving reset port function if reset is successfull.

If Anyone got xhci working on real hardware it would be very nice to give me some help!

The context size is 64 byte on real hardware, I wrote the function to try to handle that. Other than that I think enable slot does not need an input context pointer.
User avatar
BenLunt
Member
Member
Posts: 1029
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: XHCI Interrupts not working

Post by BenLunt »

I am still wondering if you tried in Bochs?

If you want the latest binary, you can go to the Actions tab (https://github.com/bochs-emu/Bochs/actions) and download the latest binary for your platform.
Then you will need a bochsrc.txt file. You can get an example at https://github.com/bochs-emu/Bochs/blob ... s/.bochsrc
If you modify the following line:

Code: Select all

debug: action=ignore
to

Code: Select all

debug: action=ignore, usb_xhci=report
Bochs will log all access to the xHCI controller to the log file. Then if you add "debug" shown here:

Code: Select all

usb_xhci: enabled=1, port1=disk:"hd.img", options1="debug, speed:super"
Bochs will log all access to the emulated thumb drive to the log file as well.

Have a look at:
https://www.fysnet.net/bochs/documentation.php
https://www.fysnet.net/bochs/user/bochs ... T-USB-XHCI
https://www.fysnet.net/bochs/user/bochs ... ml#AEN3523

You can further see each and every packet sent using the USB Debugger option.

https://www.fysnet.net/bochs/user/bochs ... ugger.html

I have only tested and used the Win32/64 version of the USB Debugger, but there is a port that a fellow Bochs Developer ported for other platforms using an add-in, GTK I believe, though I have never used it, but I fully trust the developer who added it.

I wrote the Bochs USB Emulation with the OS Developer in mind. If you set all of these debug options, you will get more log information than you ever thought you needed. Any errors you send to the controller or the device attached (within reason) will be logged and you can see where these errors occur.

So, please don't think I have been ignoring you. I have simply pointed you to an invaluable tool, and you have not stated whether you have tried that tool or not.

Ben
- https://www.fysnet.net/the_universal_serial_bus.htm
devc1
Member
Member
Posts: 461
Joined: Fri Feb 11, 2022 4:55 am

Re: XHCI Interrupts not working

Post by devc1 »

My OS Only Boots in UEFI. So I Don't think that Bochs will work for me.
User avatar
BenLunt
Member
Member
Posts: 1029
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: XHCI Interrupts not working

Post by BenLunt »

devc1 wrote: Sat Aug 02, 2025 9:49 am My OS Only Boots in UEFI. So I Don't think that Bochs will work for me.
Well I am sorry your feel that way. I wish you good luck in your efforts and hope you solve this issue soon.

I would just like to add that I have successful booted my projects many times using Bochs and UEFI.

Edit: I have instructions on how to use UEFI in Bochs at https://www.fysnet.net/blog/2025/08/

Ben
- https://www.fysnet.net/leanfs/index.php
devc1
Member
Member
Posts: 461
Joined: Fri Feb 11, 2022 4:55 am

Re: XHCI Interrupts not working

Post by devc1 »

I used qemu xhci debugging at first and I was able to fix some problems but the real hardware problem persists.
Here is how I setup trbs and send a control transfer (my initial problem is that enable slot returns error code 9)

Code: Select all


XHCI_TRB_CONTEXT* KCALL TrbContextCreate(XHCI* xhc, IN OPT UINT32 SlotId) {
    XHCI_TRB_CONTEXT* ctx = kmAllocatePhysicalPages(2);

    ctx->TransferRing = ctx->__Ring;
    EnhancedMemClr(ctx->TransferRing, 0x1000);
    // During ring initialization
    ctx->TransferRing[255].Parameter = (UINT64)ctx->TransferRing; // ring base address
    ctx->TransferRing[255].Status = 0;
    ctx->TransferRing[255].Control = (TRB_TYPE_LINK << 10) | 2 /*Toggle cycle bit*/ | (1);
    ctx->Index =0;
    ctx->Cycle = 1;
    ctx->xhc = xhc;
    ctx->SlotId = SlotId;
    return ctx;
}

void KCALL TrbPush(XHCI_TRB_CONTEXT* ctx, UINT64 param, UINT32 status, UINT32 control) {
    if(ctx->Index == 255) {
        ctx->TransferRing[255].Control = (TRB_TYPE_LINK << 10) | 2 | (ctx->Cycle);
        ctx->Cycle ^= 1;
        ctx->Index=0;
    }
    XHCI_TRB* trb = ctx->__Ring + ctx->Index;
    trb->Parameter = param;
    trb->Status = status;
    trb->Control = control | (ctx->Cycle);
    ctx->LastTrb = trb;
    ctx->Index++;
}
#include <intrin.h>
UINT64 KCALL TrbCmdRingRequest(XHCI* xhc, UINT64 param, UINT32 Status, UINT32 Control) {
    KConOut(L"ACQUIRING LOCK");
    // kStall(_MSTONANO(500));
    UINT64 Flags = KAcquireSpinLock(&xhc->CmdRingSpinLock);
    KConOut(L"ACQUIRED LOCK");

    xhc->CmdRingWaitingTask = ktCurrentTask();
    TrbPush(xhc->CmdRingContext, param, Status, Control | TRB_IOC);
    // xhc->ReturnedCtl = DRIVER_CODE_WAITING_FOR_TRANSFER;
    _mm_sfence();
    ktIoPrepare();
    xhc->Doorbell[0]=0;
    KConOut(L"WAITING FOR IO");

    ktIoWait();
    KConOut(L"Done IO");

    KReleaseSpinLock(&xhc->CmdRingSpinLock, Flags);
    return xhc->ReturnedCtl;
}

Here is how I send enable slot

Code: Select all


   UINT8 Port = PortId-1;
    UINT32 SlotId = (UINT32)TrbCmdRingRequest(xhc, 0, 0,(TRB_TYPE_ENABLE_SLOT << 10) | (0 << 16));
            KConOut(L"Returned slot#%d for port#%d (ID %d)", SlotId, Port, PortId);
   

Post Reply