diff --git a/src/drivers.asm b/src/drivers.asm index 4a662c2..af77598 100644 --- a/src/drivers.asm +++ b/src/drivers.asm @@ -9,6 +9,7 @@ ; Internal %include "drivers/apic.asm" %include "drivers/ioapic.asm" +%include "drivers/msi.asm" %include "drivers/ps2.asm" %include "drivers/serial.asm" %include "drivers/timer.asm" diff --git a/src/drivers/bus/xhci.asm b/src/drivers/bus/xhci.asm index ea743b4..654f093 100644 --- a/src/drivers/bus/xhci.asm +++ b/src/drivers/bus/xhci.asm @@ -27,77 +27,11 @@ xhci_init: bts eax, 1 ; Enable Memory Space call os_bus_write ; Write updated Status/Command - ; Check for MSI-X in PCI Capabilities -xhci_init_msix_check: - mov dl, 1 - call os_bus_read ; Read register 1 for Status/Command - bt eax, 20 ; Check bit 4 of the Status word (31:16) - jnc xhci_init_error ; If if doesn't exist then bail out - mov dl, 13 - call os_bus_read ; Read register 13 for the Capabilities Pointer (7:0) - and al, 0xFC ; Clear the bottom two bits as they are reserved -xhci_init_msix_check_cap_next: - shr al, 2 ; Quick divide by 4 - mov dl, al - call os_bus_read - cmp al, 0x11 - je xhci_init_msix -xhci_init_msix_check_cap_next_offset: - shr eax, 8 ; Shift pointer to AL - cmp al, 0x00 ; End of linked list? - jne xhci_init_msix_check_cap_next ; If not, continue reading - jmp xhci_init_msi_check ; Otherwise bail out and check for MSI -xhci_init_msix: - push rdx - ; Enable MSI-X, Mask it, Get Table Size - ; Example MSI-X Entry (From QEMU xHCI Controller) - ; 000FA011 <- Cap ID 0x11 (MSI-X), next ptr 0xA0, message control 0x000F - Table size is bits 10:0 so 0x0F - ; 00003000 <- BIR (2:0) is 0x0 so BAR0, Table Offset (31:3) - 8-byte aligned so clear low 3 bits - 0x3000 in this case - ; 00003800 <- Pending Bit BIR (2:0) and Pending Bit Offset (31:3) - 0x3800 in this case - ; Message Control - Enable (15), Function Mask (14), Table Size (10:0) - call os_bus_read - mov ecx, eax ; Save for Table Size - bts eax, 31 ; Enable MSIX - bts eax, 30 ; Set Function Mask - call os_bus_write - shr ecx, 16 ; Shift Message Control to low 16-bits - and cx, 0x7FF ; Keep bits 10:0 - ; Read the BIR and Table Offset - push rdx - add dl, 1 - call os_bus_read - mov ebx, eax ; EBX for the Table Offset - and ebx, 0xFFFFFFF8 ; Clear bits 2:0 - and eax, 0x00000007 ; Keep bits 2:0 for the BIR - add al, 0x04 ; Add offset to start of BARs - mov dl, al - call os_bus_read ; Read the BAR address - add rax, rbx ; Add offset to base - sub rax, 0x04 - mov rdi, rax - pop rdx - ; Configure MSI-X Table - add cx, 1 ; Table Size is 0-indexed - mov ebx, 0x000040A0 ; Trigger Mode (15), Level (14), Delivery Mode (10:8), Vector (7:0) -xhci_init_msix_entry: - mov rax, [os_LocalAPICAddress] ; 0xFEE for bits 31:20, Dest (19:12), RH (3), DM (2) - stosd ; Store Message Address Low - shr rax, 32 ; Rotate the high bits to EAX - stosd ; Store Message Address High - mov eax, ebx - inc ebx - stosd ; Store Message Data - xor eax, eax ; Bits 31:1 are reserved, Masked (0) - 1 for masked - stosd ; Store Vector Control - dec cx - cmp cx, 0 - jne xhci_init_msix_entry - ; Unmask MSI-X - pop rdx - call os_bus_read - btr eax, 30 ; Clear Function Mask - call os_bus_write - jmp xhci_init_msix_msi_done + ; Configure MSI-X (if available) + ; TODO - Keep track of used vectors and increment as needed + mov al, 0xA0 + call msix_init + jnc xhci_init_msix_msi_done ; Check for MSI in PCI Capabilities xhci_init_msi_check: @@ -347,7 +281,7 @@ xhci_reset_build_scratchpad: ; ├──────────────────────────────┴───────┤ ; | Ring Segment Base Address Hi | ; ├──────────────────┬───────────────────┤ - ; | RsvdZ | Ring Segment Size | + ; | RsvdZ | Ring Segment Size | ; ├──────────────────┴───────────────────┤ ; | RsvdZ | ; └──────────────────────────────────────┘ @@ -1862,7 +1796,7 @@ xHCI_CC_STALL_ERROR equ 6 xHCI_CC_RESOURCE_ERROR equ 7 xHCI_CC_BANDWIDTH_ERROR equ 8 xHCI_CC_NO_SLOTS_ERROR equ 9 -xHCI_CC_INVALID_STREAM_TYPE_ERROR equ 10 +xHCI_CC_INVALID_STREAM_TYPE_ERROR equ 10 xHCI_CC_SLOT_NOT_ENABLED_ERROR equ 11 xHCI_CC_EP_NOT_ENABLED_ERROR equ 12 xHCI_CC_SHORT_PACKET equ 13 diff --git a/src/drivers/msi.asm b/src/drivers/msi.asm new file mode 100644 index 0000000..db1fd7e --- /dev/null +++ b/src/drivers/msi.asm @@ -0,0 +1,196 @@ +; ============================================================================= +; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems +; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT +; +; Message Signaled Interrupts (MSI-X and MSI) +; ============================================================================= + + +; ----------------------------------------------------------------------------- +; Initialize MSI-X for a device +; IN: RDX = Packed Bus address (as per syscalls/bus.asm) +; AL = Start Vector +; OUT: Carry flag (clear on success, set on error) +; ----------------------------------------------------------------------------- +; Message Control - Enable (15), Function Mask (14), Table Size (10:0) +; +; Example MSI-X Entry (From QEMU xHCI Controller) +; 000FA011 <- Cap ID 0x11 (MSI-X), next ptr 0xA0, message control 0x000F - Table size is bits 10:0 so 0x0F +; 00003000 <- BIR (2:0) is 0x0 so BAR0, Table Offset (31:3) - 8-byte aligned so clear low 3 bits - 0x3000 in this case +; 00003800 <- Pending Bit BIR (2:0) and Pending Bit Offset (31:3) - 0x3800 in this case +; +; Example MSI-X Entry (From QEMU Virtio-Net) +; 00038411 <- Cap ID 0x11 (MSI-X), next ptr 0x84, message control 0x0003 - Table size is bits 10:0 so 3 (n-1 so table size is actually 4) +; 00000001 <- BIR (2:0) is 0x1 so BAR1, Table Offset (31:3) - 8-byte aligned so clear low 3 bits - 0x0 in this case +; 00000801 <- Pending Bit BIR (2:0) is 0x1 so BAR1 and Pending Bit Offset (31:3) is 0x800 +; +; Resulting MSI-X table entry in memory should look similar to: +; 0xXXXXXXXX: FEE00000 00000000 000040XX 00000000 +msix_init: + push r8 + push rdi + push rdx + push rcx + push rbx + push rax + + mov r8b, al + + ; Check for MSI-X in PCI Capabilities + mov cl, 0x11 ; PCI Capability ID for MSI-X + call os_bus_cap_check + jc msix_init_error ; os_bus_cap_check sets carry flag is the cap isn't found + + push rdx ; Save packed bus address + + ; Enable MSI-X, Mask it, Get Table Size +msix_init_enable: + call os_bus_read + mov ecx, eax ; Save for Table Size + bts eax, 31 ; Enable MSI-X + bts eax, 30 ; Set Function Mask + call os_bus_write + shr ecx, 16 ; Shift Message Control to low 16-bits + and cx, 0x7FF ; Keep bits 10:0 + ; Read the BIR and Table Offset + push rdx + add dl, 1 + call os_bus_read + mov ebx, eax ; EBX for the Table Offset + and ebx, 0xFFFFFFF8 ; Clear bits 2:0 + and eax, 0x00000007 ; Keep bits 2:0 for the BIR + + add al, 0x04 ; Add offset to start of BARs + mov dl, al + call os_bus_read ; Read the BAR address + +; TODO - Read BAR properly +; push rcx ; Save RCX as os_bus_read_bar returns a value in it +; call os_bus_read_bar ; Read the BAR address +; pop rcx + + add rax, rbx ; Add offset to base + and eax, 0xFFFFFFF8 ; Clear bits 2:0 of a 32-bit BAR + mov rdi, rax + pop rdx + + ; Configure MSI-X Table + add cx, 1 ; Table Size is 0-indexed + xor ebx, ebx ; Trigger Mode (15), Level (14), Delivery Mode (10:8), Vector (7:0) + mov bl, r8b ; Store start vector +msix_init_create_entry: + mov rax, [os_LocalAPICAddress] ; 0xFEE for bits 31:20, Dest (19:12), RH (3), DM (2) + stosd ; Store Message Address Low + shr rax, 32 ; Rotate the high bits to EAX + stosd ; Store Message Address High + mov eax, ebx + inc ebx + stosd ; Store Message Data + xor eax, eax ; Bits 31:1 are reserved, Masked (0) - 1 for masked + stosd ; Store Vector Control + dec cx + cmp cx, 0 + jne msix_init_create_entry + + ; Unmask MSI-X via bus + pop rdx ; Restore packed bus address + call os_bus_read + btr eax, 30 ; Clear Function Mask + call os_bus_write + + pop rax + pop rbx + pop rcx + pop rdx + pop rdi + pop r8 + clc ; Clear the carry flag + ret + +msix_init_error: + pop rax + pop rbx + pop rcx + pop rdx + pop rdi + pop r8 + stc ; Set the carry flag + ret +; ----------------------------------------------------------------------------- + + +; ----------------------------------------------------------------------------- +; Initialize MSI for a device +; IN: RDX = Packed Bus address (as per syscalls/bus.asm) +; AL = Start Vector +; OUT: Carry flag (clear on success, set on error) +; ----------------------------------------------------------------------------- +; Example MSI Entry (From Intel test system) +; 00869005 <- Cap ID 0x05 (MSI), next ptr 0x90, message control 0x0x0086 (64-bit, MMC 8) +; 00000000 <- Message Address Low +; 00000000 <- Message Address High +; 00000000 <- Message Data (15:0) +; 00000000 <- Mask (only exists if Per-vector masking is enabled) +; 00000000 <- Pending (only exists if Per-vector masking is enabled) +; Message Control - Per-vector masking (8), 64-bit (7), Multiple Message Enable (6:4), Multiple Message Capable (3:1), Enable (0) +; MME/MMC 000b = 1, 001b = 2, 010b = 4, 011b = 8, 100b = 16, 101b = 32 +; Todo - Test bit 7, Check Multiple Message Capable, copy to Multiple Message Enable +msi_init: + push rdx + push rcx + push rbx + push rax + + mov bl, al + + ; Check for MSI in PCI Capabilities + mov cl, 0x05 ; PCI Capability ID for MSI + call os_bus_cap_check + jc msi_init_error + + ; Enable MSI +msi_init_enable: + push rdx + add dl, 1 + mov rax, [os_LocalAPICAddress] ; 0xFEE for bits 31:20, Dest (19:12), RH (3), DM (2) + call os_bus_write ; Store Message Address Low + add dl, 1 + shr rax, 32 ; Rotate the high bits to EAX + call os_bus_write ; Store Message Address High + add dl, 1 + mov eax, 0x00004000 ; Trigger Mode (15), Level (14), Delivery Mode (10:8), Vector (7:0) + mov al, bl ; Store start vector + call os_bus_write ; Store Message Data + sub dl, 3 + call os_bus_read ; Get Message Control + bts eax, 21 ; Debug - See MME to 8 + bts eax, 20 ; Debug - See MME to 8 + bts eax, 16 ; Set Enable + call os_bus_write ; Update Message Control + + ; Unmask MSI via bus + pop rdx + call os_bus_read + btr eax, 30 ; Clear Function Mask + call os_bus_write + +msi_init_done: + pop rax + pop rbx + pop rcx + pop rdx + clc ; Clear the carry flag + ret + +msi_init_error: + pop rax + pop rbx + pop rcx + pop rdx + stc ; Set the carry flag + ret +; ----------------------------------------------------------------------------- + + +; ============================================================================= +; EOF \ No newline at end of file diff --git a/src/drivers/net/virtio-net.asm b/src/drivers/net/virtio-net.asm index 3eaf585..934689a 100644 --- a/src/drivers/net/virtio-net.asm +++ b/src/drivers/net/virtio-net.asm @@ -25,7 +25,7 @@ net_virtio_init: mov ax, 0x1AF4 ; Driver tag for virtio-net stosw - push rdi ; Used in msi-x init + push rdi ; Save offset into net_table add rdi, 14 ; Get the Base Memory Address of the device @@ -40,9 +40,32 @@ net_virtio_init: mov dl, 0x01 ; Read Status/Command call os_bus_read bts eax, 10 ; Set Interrupt Disable + bts eax, 2 ; Enable Bus Master bts eax, 1 ; Enable Memory Space call os_bus_write + ; Configure MSI-X (if available) + ; TODO - Keep track of used vectors and increment as needed + mov al, 0xB0 + call msix_init + jc net_virtio_init_skip_int + + ; Create gate(s) in the IDT + push rdi + mov edi, 0xB0 + mov rax, net_virtio_int + call create_gate + pop rdi + + ; Set flag for interrupts enabled in net_table + push rdi + sub rdi, 0x1C + mov ax, 0x0001 ; Set flag for nt_interrupt + stosw + pop rdi + +net_virtio_init_skip_int: + ; Get required values from PCI Capabilities mov dl, 1 call os_bus_read ; Read register 1 for Status/Command @@ -58,60 +81,9 @@ virtio_net_init_cap_next: call os_bus_read cmp al, VIRTIO_PCI_CAP_VENDOR_CFG je virtio_net_init_cap - cmp al, 0x11 - je virtio_net_init_msix shr eax, 8 jmp virtio_net_init_cap_next_offset -virtio_net_init_msix: - push rdx - - ; Enable MSI-X, Mask it, Get Table Size - call os_bus_read - mov ecx, eax ; Save for Table Size - bts eax, 31 ; Enable MSIX - bts eax, 30 ; Set Function Mask - call os_bus_write - shr ecx, 16 ; Shift Message Control to low 16-bits - and cx, 0x7FF ; Keep bits 10:0 - - ; Read the BIR and Table Offset - push rdx - add dl, 1 - call os_bus_read - mov ebx, eax ; EBX for the Table Offset - and ebx, 0xFFFFFFF8 ; Clear bits 2:0 - and eax, 0x00000007 ; Keep bits 2:0 for the BIR - add al, 0x04 ; Add offset to start of BARs - mov dl, al - call os_bus_read ; Read the BAR address - add rax, rbx ; Add offset to base - mov rdi, rax - pop rdx - - ; Configure MSI-X Table - add cx, 1 ; Table Size is 0-indexed -virtio_net_init_msix_entry: - mov rax, [os_LocalAPICAddress] ; 0xFEE for bits 31:20, Dest (19:12), RH (3), DM (2) - stosd ; Store Message Address Low - shr rax, 32 ; Rotate the high bits to EAX - stosd ; Store Message Address High - mov eax, 0x000040AB ; Trigger Mode (15), Level (14), Delivery Mode (10:8), Vector (7:0) - stosd ; Store Message Data - xor eax, eax ; Bits 31:1 are reserved, Masked (0) - 1 for masked - stosd ; Store Vector Control - dec cx - cmp cx, 0 - jne virtio_net_init_msix_entry - pop rdx - - ; Unmask MSI-X - call os_bus_read - btc eax, 30 ; Clear Function Mask - call os_bus_write - - jmp virtio_net_init_cap_next_offset - virtio_net_init_cap: rol eax, 8 ; Move Virtio cfg_type to AL cmp al, VIRTIO_PCI_CAP_COMMON_CFG @@ -291,7 +263,7 @@ virtio_net_init_reset_wait: ; reading and possibly writing the device’s virtio configuration space ; population of virtqueues - mov ax, 0x0000 + mov ax, 0xFFFF ; Disable config interrupts mov [rsi+VIRTIO_CONFIG_MSIX_VECTOR], ax ; Set up Queue 0 (Receive) @@ -315,9 +287,8 @@ virtio_net_init_reset_wait: rol rax, 32 mov [rsi+VIRTIO_QUEUE_DEVICE+8], eax rol rax, 32 - mov ax, 0x0001 + mov ax, 0x0000 ; MSI-X index 0 mov [rsi+VIRTIO_QUEUE_MSIX_VECTOR], ax - mov ax, [rsi+VIRTIO_QUEUE_MSIX_VECTOR] mov ax, 1 mov [rsi+VIRTIO_QUEUE_ENABLE], ax @@ -575,6 +546,24 @@ net_virtio_poll_nodata: ; ----------------------------------------------------------------------------- +; ----------------------------------------------------------------------------- +; Virtio-net Interrupt +align 8 +net_virtio_int: + push rcx + push rax + + ; Acknowledge the interrupt + mov ecx, APIC_EOI + xor eax, eax + call os_apic_write + + pop rax + pop rcx + iretq +; ----------------------------------------------------------------------------- + + ; Variables virtio_net_notify_offset: dq 0 virtio_net_notify_offset_multiplier: dq 0 diff --git a/src/syscalls/bus.asm b/src/syscalls/bus.asm index d6ce67f..82de5df 100644 --- a/src/syscalls/bus.asm +++ b/src/syscalls/bus.asm @@ -139,5 +139,45 @@ os_bus_read_bar_io: ; ----------------------------------------------------------------------------- +; ----------------------------------------------------------------------------- +; Check for a device capability +; IN: RDX = Packed Bus address (as per syscalls/bus.asm) +; CL = Capability +; OUT: RDX = Packed Bus address +; Carry clear = Capability found +; Carry set = Capability not found +os_bus_cap_check: + push rax + + mov dl, 1 + call os_bus_read ; Read register 1 for Status/Command + bt eax, 20 ; Check bit 4 of the Status word (31:16) + jnc os_bus_cap_check_error ; If if doesn't exist then bail out + mov dl, 13 + call os_bus_read ; Read register 13 for the Capabilities Pointer (7:0) + and al, 0xFC ; Clear the bottom two bits as they are reserved +os_bus_cap_check_next: + shr al, 2 ; Quick divide by 4 + mov dl, al + call os_bus_read + cmp al, cl + je os_bus_cap_check_done +os_bus_cap_check_next_offset: + shr eax, 8 ; Shift pointer to AL + cmp al, 0x00 ; End of linked list? + jne os_bus_cap_check_next ; If not, continue reading, otherwise fall through to error + +os_bus_cap_check_error: + pop rax + stc ; Set the carry flag + ret + +os_bus_cap_check_done: + pop rax + clc ; Clear the carry flag + ret +; ----------------------------------------------------------------------------- + + ; ============================================================================= ; EOF diff --git a/src/sysvar.asm b/src/sysvar.asm index e1231ec..552007a 100644 --- a/src/sysvar.asm +++ b/src/sysvar.asm @@ -149,6 +149,7 @@ os_PacketBuffers: equ os_SystemVariables + 0xC000 ; 16KiB ; net_table values (per device - 128 bytes) nt_ID: equ 0x00 ; 16-bit Driver ID nt_lock: equ 0x02 ; 16-bit Lock for b_net_tx +nt_interrupt: equ 0x04 ; 16-bit Interrupts enabled flag nt_MAC: equ 0x08 ; 48-bit MAC Address nt_base: equ 0x10 ; 64-bit Base MMIO nt_config: equ 0x18 ; 64-bit Config function address