From 60226a241906521f6f3c8f35cd77e455f2040e7e Mon Sep 17 00:00:00 2001 From: Totto16 Date: Thu, 12 Jun 2025 14:34:35 +0200 Subject: [PATCH 1/2] feat: detect msi settings capability for qemu-xhci device and run it accordingly --- run | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/run b/run index c1c67916..39fec972 100755 --- a/run +++ b/run @@ -7,6 +7,14 @@ if [ "$1" = "debug" ]; then ARGS="-monitor unix:/tmp/qemu-monitor-socket,server,nowait -s -S" fi +MSI_CAPABILITIES="" + +XHCI_CAPABILITIES="$(qemu-system-aarch64 -device qemu-xhci,help)" + +if echo "$XHCI_CAPABILITIES" | grep -q "msi "; then + MSI_CAPABILITIES="msi=on,msix=off," +fi + qemu-system-aarch64 \ -M virt \ -cpu cortex-a72 \ @@ -17,7 +25,7 @@ qemu-system-aarch64 \ -serial mon:stdio \ -drive file=disk.img,if=none,format=raw,id=hd0 \ -device virtio-blk-pci,drive=hd0 \ --device qemu-xhci,msi=on,msix=off,id=usb \ +-device qemu-xhci,${MSI_CAPABILITIES}id=usb \ -device usb-kbd,bus=usb.0 \ -d guest_errors \ $ARGS From eeb0b71c35ac6f3b29f512354d891a6210a1790d Mon Sep 17 00:00:00 2001 From: Totto16 Date: Thu, 12 Jun 2025 15:59:12 +0200 Subject: [PATCH 2/2] fix: make msix work - check return value of pci_setup_msi, if it fails try to use msix - intilizate pci_setup_bar before using msix, as msix uses the bars memory address - add support for multiple interrupts in the pci_setup_msix function, as msix supports that --- kernel/input/xhci.c | 35 +++++++++++++++++++++++++-- kernel/pci.c | 59 ++++++++++++++++++++++++++++++--------------- kernel/pci.h | 8 +++++- 3 files changed, 79 insertions(+), 23 deletions(-) diff --git a/kernel/input/xhci.c b/kernel/input/xhci.c index 73c74c94..ffb41b78 100644 --- a/kernel/input/xhci.c +++ b/kernel/input/xhci.c @@ -93,6 +93,26 @@ void make_ring_link(trb* ring, bool cycle){ make_ring_link_control(ring, cycle); } + +uint8_t xhci_init_interrupts(uint64_t pci_addr){ + bool msi_ok = pci_setup_msi(pci_addr, XHCI_IRQ); + + if(msi_ok){ + return 1; + } + +#define MSIX_IRQ_LENGTH 1 + + msix_irq_line irq_lines[MSIX_IRQ_LENGTH] = {(msix_irq_line){.addr_offset=0,.irq_num=XHCI_IRQ}}; + + bool msix_ok = pci_setup_msix(pci_addr, irq_lines, MSIX_IRQ_LENGTH); + if(msix_ok){ + return 2; + } + + return 0; +} + bool xhci_init(xhci_device *xhci, uint64_t pci_addr) { kprintfv("[xHCI] init"); if (!(read16(pci_addr + 0x06) & (1 << 4))){ @@ -100,8 +120,6 @@ bool xhci_init(xhci_device *xhci, uint64_t pci_addr) { return false; } - pci_setup_msi(pci_addr, XHCI_IRQ); - if (!pci_setup_bar(pci_addr, 0, &xhci->mmio, &xhci->mmio_size)){ kprintfv("[xHCI] BARs not set up"); return false; @@ -109,6 +127,19 @@ bool xhci_init(xhci_device *xhci, uint64_t pci_addr) { pci_register(xhci->mmio, xhci->mmio_size); + uint8_t interrupts_ok = xhci_init_interrupts(pci_addr); + switch(interrupts_ok){ + case 0: + kprintf_raw("[xHCI] Failed to setup interrupts\n"); + return false; + case 2: + kprintf_raw("[xHCI] Interrupts setup with MSI-X"); + break; + default: + kprintf_raw("[xHCI] Interrupts setup with MSI"); + break; + } + pci_enable_device(pci_addr); kprintfv("[xHCI] BARs set up @ %x (%x)",xhci->mmio,xhci->mmio_size); diff --git a/kernel/pci.c b/kernel/pci.c index 54f749c2..8505b3e1 100644 --- a/kernel/pci.c +++ b/kernel/pci.c @@ -17,6 +17,12 @@ #define PCI_COMMAND_MEMORY 0x2 #define PCI_COMMAND_BUS 0x4 +#define PCI_BAR_BASE_OFFSET 0x10 + +#define PCI_CAPABILITY_MSI 0x05 +#define PCI_CAPABILITY_PCIE 0x10 +#define PCI_CAPABILITY_MSIX 0x11 + static uint64_t pci_base; #define NINIT pci_base == 0x0 @@ -198,25 +204,19 @@ uint64_t pci_get_bar_address(uint64_t base, uint8_t offset, uint8_t index){ } uint64_t pci_read_address_bar(uintptr_t pci_addr, uint32_t bar_index){ - uint64_t bar_addr = pci_get_bar_address(pci_addr, 0x10, bar_index); + uint64_t bar_addr = pci_get_bar_address(pci_addr, PCI_BAR_BASE_OFFSET, bar_index); uint32_t original = read32(bar_addr); uint64_t full = original; if ((original & 0x6) == 0x4) { - uint64_t bar_addr_hi = pci_get_bar_address(pci_addr, 0x10, bar_index+1); + uint64_t bar_addr_hi = pci_get_bar_address(pci_addr, PCI_BAR_BASE_OFFSET, bar_index+1); uint64_t original_hi = read32(bar_addr_hi); full |= original_hi << 32; } return full & ~0xF; } -void debug_read_bar(uint64_t base, uint8_t offset, uint8_t index){ - uint64_t addr = pci_get_bar_address(base, offset, index); - uint64_t val = read32(addr); - kprintf("[PCI] Reading@%x (%i) content: ", addr, index, val); -} - uint64_t pci_setup_bar(uint64_t pci_addr, uint32_t bar_index, uint64_t *mmio_start, uint64_t *mmio_size) { - uint64_t bar_addr = pci_get_bar_address(pci_addr, 0x10, bar_index); + uint64_t bar_addr = pci_get_bar_address(pci_addr, PCI_BAR_BASE_OFFSET, bar_index); uint32_t original = read32(bar_addr); write32(bar_addr, 0xFFFFFFFF); @@ -225,7 +225,7 @@ uint64_t pci_setup_bar(uint64_t pci_addr, uint32_t bar_index, uint64_t *mmio_sta uint64_t size; if ((original & 0x6) == 0x4) { - uint64_t bar_addr_hi = pci_get_bar_address(pci_addr, 0x10, bar_index+1); + uint64_t bar_addr_hi = pci_get_bar_address(pci_addr, PCI_BAR_BASE_OFFSET, bar_index+1); uint32_t original_hi = read32(bar_addr_hi); kprintfv("[PCI] Original second bar %x",original_hi); @@ -314,7 +314,7 @@ bool pci_setup_msi(uint64_t pci_addr, uint8_t irq_line) { uint8_t cap_ptr = read8(pci_addr + 0x34); while (cap_ptr) { uint8_t cap_id = read8(pci_addr + cap_ptr); - if (cap_id == 0x05) { // MSI + if (cap_id == PCI_CAPABILITY_MSI) { // MSI uint16_t msg_ctrl = read16(pci_addr + cap_ptr + 0x2); bool is_64bit = msg_ctrl & (1 << 7); @@ -325,7 +325,7 @@ bool pci_setup_msi(uint64_t pci_addr, uint8_t irq_line) { offset += 4; } - write16(pci_addr + cap_ptr + offset, 50 + irq_line); + write16(pci_addr + cap_ptr + offset, MSI_OFFSET + irq_line); msg_ctrl |= 1; // enable MSI write16(pci_addr + cap_ptr + 0x2, msg_ctrl); return true; @@ -342,27 +342,46 @@ typedef struct { uint32_t vector_control; } msix_table_entry; -bool pci_setup_msix(uint64_t pci_addr, uint8_t irq_line) { +bool pci_setup_msix(uint64_t pci_addr, msix_irq_line* irq_lines, uint8_t line_size) { uint8_t cap_ptr = read8(pci_addr + 0x34); while (cap_ptr) { uint8_t cap_id = read8(pci_addr + cap_ptr); - if (cap_id == 0x11) { // MSI-X + if (cap_id == PCI_CAPABILITY_MSIX) { // MSI-X uint16_t msg_ctrl = read16(pci_addr + cap_ptr + 0x2); msg_ctrl &= ~(1 << 15); // Clear MSI-X Enable bit write16(pci_addr + cap_ptr + 0x2, msg_ctrl); + uint16_t table_size = (msg_ctrl & 0x07FF) +1; // takes the 11 rightmost bits, its value is N-1, so add 1 to it for the full size + + if(line_size > table_size){ + kprintf_raw("[PCI] MSI-X only supports %i interrupts, but you tried to add %i interrupts", table_size, line_size); + return false; + } + uint32_t table_offset = read32(pci_addr + cap_ptr + 0x4); uint8_t bir = table_offset & 0x7; uint32_t table_addr_offset = table_offset & ~0x7; uint64_t table_addr = pci_read_address_bar(pci_addr, bir); + + if(!table_addr){ + kprintf_raw("[PCI] MSI-X setup error: Table address is null. Did you setup the bar memory before calling this function?"); + return false; + } - msix_table_entry *msix_entry = (msix_table_entry *)(uintptr_t)(table_addr + table_addr_offset); - - msix_entry->msg_addr_low = 0x8020040; - msix_entry->msg_addr_high = 0; - msix_entry->msg_data = 50 + irq_line; - msix_entry->vector_control = 0; + msix_table_entry *msix_start = (msix_table_entry *)(uintptr_t)(table_addr + table_addr_offset); + + for (uint32_t i = 0; i < line_size; i++){ + msix_table_entry *msix_entry = msix_start + i; + + msix_irq_line irq_line = irq_lines[i]; + uint64_t addr_full = 0x8020040 + irq_line.addr_offset; + + msix_entry->msg_addr_low = addr_full & 0xFFFFFFFF; + msix_entry->msg_addr_high = addr_full >> 32; + msix_entry->msg_data = MSI_OFFSET + irq_line.irq_num; + msix_entry->vector_control = msix_entry->vector_control & ~0x1; // all bits other then the last one are reserved, so don't chnage it, just set the last bit to 0 + } msg_ctrl |= (1 << 15); // MSI-X Enable msg_ctrl &= ~(1 << 14); // Clear Function Mask diff --git a/kernel/pci.h b/kernel/pci.h index a7a4d840..2c8faf17 100644 --- a/kernel/pci.h +++ b/kernel/pci.h @@ -23,7 +23,13 @@ void pci_register(uint64_t mmio_addr, uint64_t mmio_size); void pci_enable_verbose(); bool pci_setup_msi(uint64_t pci_addr, uint8_t irq_vector); -bool pci_setup_msix(uint64_t pci_addr, uint8_t irq_line); + +typedef struct { + uint32_t addr_offset; + uint8_t irq_num; +} msix_irq_line; + +bool pci_setup_msix(uint64_t pci_addr, msix_irq_line* irq_lines, uint8_t line_size); #ifdef __cplusplus }