1+ ; =============================================================================
2+ ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems
3+ ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT
4+ ;
5+ ; Message Signaled Interrupts (MSI-X and MSI)
6+ ; =============================================================================
7+
8+
9+ ; -----------------------------------------------------------------------------
10+ ; Initialize MSI-X for a device
11+ ; IN: RDX = Packed Bus address (as per syscalls/bus.asm)
12+ ; AL = Start Vector
13+ msix_init:
14+ push r8
15+ push rdx
16+ push rcx
17+ push rbx
18+ push rax
19+
20+ mov r8b , al
21+
22+ ; Check for MSI-X in PCI Capabilities
23+ mov cl , 0x11 ; PCI Capability ID for MSI-X
24+ call os_bus_cap_check
25+ jc msix_init_error
26+
27+ ; Enable MSI-X
28+ msix_init_enable:
29+ push rdx
30+ ; Enable MSI-X, Mask it, Get Table Size
31+ ; Example MSI-X Entry (From QEMU xHCI Controller)
32+ ; 000FA011 <- Cap ID 0x11 (MSI-X), next ptr 0xA0, message control 0x000F - Table size is bits 10:0 so 0x0F
33+ ; 00003000 <- BIR (2:0) is 0x0 so BAR0, Table Offset (31:3) - 8-byte aligned so clear low 3 bits - 0x3000 in this case
34+ ; 00003800 <- Pending Bit BIR (2:0) and Pending Bit Offset (31:3) - 0x3800 in this case
35+ ; Message Control - Enable (15), Function Mask (14), Table Size (10:0)
36+ call os_bus_read
37+ mov ecx , eax ; Save for Table Size
38+ bts eax , 31 ; Enable MSI-X
39+ bts eax , 30 ; Set Function Mask
40+ call os_bus_write
41+ shr ecx , 16 ; Shift Message Control to low 16-bits
42+ and cx , 0x7FF ; Keep bits 10:0
43+ ; Read the BIR and Table Offset
44+ push rdx
45+ add dl , 1
46+ call os_bus_read
47+ mov ebx , eax ; EBX for the Table Offset
48+ and ebx , 0xFFFFFFF8 ; Clear bits 2:0
49+ and eax , 0x00000007 ; Keep bits 2:0 for the BIR
50+ add al , 0x04 ; Add offset to start of BARs
51+ mov dl , al
52+ call os_bus_read ; Read the BAR address
53+ add rax , rbx ; Add offset to base
54+ sub rax , 0x04
55+ mov rdi , rax
56+ pop rdx
57+ ; Configure MSI-X Table
58+ add cx , 1 ; Table Size is 0-indexed
59+ mov ebx , 0x00004000 ; Trigger Mode (15), Level (14), Delivery Mode (10:8), Vector (7:0)
60+ mov bl , r8b ; Store start vector
61+ msix_init_create_entry:
62+ mov rax , [ os_LocalAPICAddress ] ; 0xFEE for bits 31:20, Dest (19:12), RH (3), DM (2)
63+ stosd ; Store Message Address Low
64+ shr rax , 32 ; Rotate the high bits to EAX
65+ stosd ; Store Message Address High
66+ mov eax , ebx
67+ inc ebx
68+ stosd ; Store Message Data
69+ xor eax , eax ; Bits 31:1 are reserved, Masked (0) - 1 for masked
70+ stosd ; Store Vector Control
71+ dec cx
72+ cmp cx , 0
73+ jne msix_init_create_entry
74+
75+ ; Unmask MSI-X via bus
76+ pop rdx
77+ call os_bus_read
78+ btr eax , 30 ; Clear Function Mask
79+ call os_bus_write
80+
81+ pop rax
82+ pop rbx
83+ pop rcx
84+ pop rdx
85+ pop r8
86+ clc ; Clear the carry flag
87+ ret
88+
89+ msix_init_error:
90+ pop rax
91+ pop rbx
92+ pop rcx
93+ pop rdx
94+ pop r8
95+ stc ; Set the carry flag
96+ ret
97+ ; -----------------------------------------------------------------------------
98+
99+
100+ ; -----------------------------------------------------------------------------
101+ ; Initialize MSI for a device
102+ ; IN: RDX = Packed Bus address (as per syscalls/bus.asm)
103+ ; AL = Start Vector
104+ msi_init:
105+ push rdx
106+ push rcx
107+ push rbx
108+ push rax
109+
110+ mov bl , al
111+
112+ ; Check for MSI in PCI Capabilities
113+ mov cl , 0x05 ; PCI Capability ID for MSI
114+ call os_bus_cap_check
115+ jc msi_init_error
116+
117+ ; Enable MSI
118+ msi_init_enable:
119+ push rdx
120+ ; Enable MSI
121+ ; Example MSI Entry (From Intel test system)
122+ ; 00869005 <- Cap ID 0x05 (MSI), next ptr 0x90, message control 0x0x0086 (64-bit, MMC 8)
123+ ; 00000000 <- Message Address Low
124+ ; 00000000 <- Message Address High
125+ ; 00000000 <- Message Data (15:0)
126+ ; 00000000 <- Mask (only exists if Per-vector masking is enabled)
127+ ; 00000000 <- Pending (only exists if Per-vector masking is enabled)
128+ ; Message Control - Per-vector masking (8), 64-bit (7), Multiple Message Enable (6:4), Multiple Message Capable (3:1), Enable (0)
129+ ; MME/MMC 000b = 1, 001b = 2, 010b = 4, 011b = 8, 100b = 16, 101b = 32
130+ ; Todo - Test bit 7, Check Multiple Message Capable, copy to Multiple Message Enable
131+ add dl , 1
132+ mov rax , [ os_LocalAPICAddress ] ; 0xFEE for bits 31:20, Dest (19:12), RH (3), DM (2)
133+ call os_bus_write ; Store Message Address Low
134+ add dl , 1
135+ shr rax , 32 ; Rotate the high bits to EAX
136+ call os_bus_write ; Store Message Address High
137+ add dl , 1
138+ mov eax , 0x00004000 ; Trigger Mode (15), Level (14), Delivery Mode (10:8), Vector (7:0)
139+ mov al , bl ; Store start vector
140+ call os_bus_write ; Store Message Data
141+ sub dl , 3
142+ call os_bus_read ; Get Message Control
143+ bts eax , 21 ; Debug - See MME to 8
144+ bts eax , 20 ; Debug - See MME to 8
145+ bts eax , 16 ; Set Enable
146+ call os_bus_write ; Update Message Control
147+
148+ ; Unmask MSI via bus
149+ pop rdx
150+ call os_bus_read
151+ btr eax , 30 ; Clear Function Mask
152+ call os_bus_write
153+
154+ msi_init_done:
155+ pop rax
156+ pop rbx
157+ pop rcx
158+ pop rdx
159+ clc ; Clear the carry flag
160+ ret
161+
162+ msi_init_error:
163+ pop rax
164+ pop rbx
165+ pop rcx
166+ pop rdx
167+ stc ; Set the carry flag
168+ ret
169+ ; -----------------------------------------------------------------------------
170+
171+
172+ ; =============================================================================
173+ ; EOF
0 commit comments