Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions kernel/input/xhci.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,22 +93,53 @@ 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))){
kprintfv("[xHCI] Wrong capabilities list");
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;
}

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);
Expand Down
59 changes: 39 additions & 20 deletions kernel/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand All @@ -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
Expand Down
8 changes: 7 additions & 1 deletion kernel/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
10 changes: 9 additions & 1 deletion run
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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