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