forked from fysnet/i440fx
-
Notifications
You must be signed in to change notification settings - Fork 0
/
apic.asm
481 lines (425 loc) · 15.8 KB
/
apic.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
comment |*******************************************************************
* Copyright (c) 1984-2025 Forever Young Software Benjamin David Lunt *
* *
* i440FX BIOS ROM v1.0 *
* FILE: apic.asm *
* *
* This code is freeware, not public domain. Please use respectfully. *
* *
* You may: *
* - use this code for learning purposes only. *
* - use this code in your own Operating System development. *
* - distribute any code that you produce pertaining to this code *
* as long as it is for learning purposes only, not for profit, *
* and you give credit where credit is due. *
* *
* You may NOT: *
* - distribute this code for any purpose other than listed above. *
* - distribute this code for profit. *
* *
* You MUST: *
* - include this whole comment block at the top of this file. *
* - include contact information to where the original source is located. *
* https://github.com/fysnet/i440fx *
* *
* DESCRIPTION: *
* apic include file *
* *
* BUILT WITH: NewBasic Assembler *
* http://www.fysnet/newbasic.htm *
* NBASM ver 00.27.14 *
* Command line: nbasm i440fx /z<enter> *
* *
* Last Updated: 3 Jan 2025 *
* *
****************************************************************************
* Notes: *
* *
* *
***************************************************************************|
comment ^
todo: If we don't specify to QEMU to not use the APIC, it won't
fire an interrupt via this 8259.
Since we support an APIC via this BIOS, we need to use it instead.
IA32_APIC_BASE equ 0x1B
; eax,0
; cpuid
; if eax > 0
; cpuid
; edx: bit 9, = apic present
; edx: bit 5, = msr register is present
; clear bits 11:10 to disable the APIC
mov ecx,IA32_APIC_BASE
rdmsr
;and eax,0xFFFF0000
; io apic is at 0xFEC00000
; do we have an APIC installed?
test dword [EBDA_DATA->cpuid_features],CPUID_APIC
jz short smp_probe_done
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; call the APIC code first. If found, we return
; and disable the 8259.
; else, we enable and use the 8259.
call init_apic
jc short @f
; the apic was found and initialized. Mask all
; interrupts on the 8259 and return
mov al,0xFF
out PORT_PIC_MASTER_DATA,al
out PORT_PIC_SLAVE_DATA,al
ret
; todo: we already enabled it before
; make sure the apic is enabled
;mov esi,APIC_BASE_ADDR
;or dword fs:[esi+APIC_SVR],APIC_ENABLED
^
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; initialize the APIC
; on entry:
; ds -> EBDA
; on return
; carry set if no apic found
; destroys none
init_apic proc near uses eax ecx esi
mov esi,APIC_BASE_ADDR
mov eax,fs:[esi+APIC_REG_VER]
mov ecx,eax
; check the version
; Pentium and above return 0x10 to 0x14
and al,0xFF
cmp al,0x10
jb init_apic_error
cmp al,0x14
ja init_apic_error
mov [EBDA_DATA->apic_version],al
; count of lvt entries are in bits 23:16
;and ecx,0x00FF0000
shr ecx,16
mov [EBDA_DATA->apic_lvt_entries],cl
; get the id (bits 31:24)
mov eax,fs:[esi+APIC_REG_ID]
shr eax,24
mov [EBDA_DATA->apic_id],al
push ds
push si
xor ah,ah
mov al,[EBDA_DATA->apic_lvt_entries]
push ax
mov al,[EBDA_DATA->apic_version]
push ax
mov al,[EBDA_DATA->apic_id]
push ax
mov ax,BIOS_BASE2
mov ds,ax
mov si,offset apic_found_str
call bios_printf
add sp,6
pop si
pop ds
; local destination register
mov dword fs:[esi+APIC_REG_LDR],0x00000000
; destination format register
mov dword fs:[esi+APIC_REG_DFR],0xFFFFFFFF
; task priority register (disable softint delivery)
mov dword fs:[esi+APIC_REG_TRP],0x00000020
; timer interrupt vector (disable timer interrupts)
mov dword fs:[esi+APIC_REG_TIMER],0x00010000
; performance counter interrupt (disable performance counter interrupts)
mov dword fs:[esi+APIC_REG_PERFORM],0x00010000
; local interrupt 0, 1
; enable normal external interrupts)
mov dword fs:[esi+APIC_REG_LINT0],0x00008700
; enable normal NMI processing)
mov dword fs:[esi+APIC_REG_LINT1],0x00000400
; error interrupt (disable error interrupts)
mov dword fs:[esi+APIC_REG_LERROR],0x00010000
; thermal sensor (if present)
cmp byte [EBDA_DATA->apic_lvt_entries],6
jb short @f
mov dword fs:[esi+APIC_REG_THERM],0x00000000 ; ((0 << 16) | (0 << 8))
; now enable the APIC, and give it a spurious interrupt vector
@@: mov ax,0x7F ; interrtupt 7Fh
mov bx,offset int7F_handler
mov cx,0xE000
call set_int_vector
; enable and set spurious (P6 must have bits 3:0 = 1111b)
mov dword fs:[esi+APIC_REG_SIV],0x0000017F
; we found an apic
clc
ret
; either we didn't find an apic, or it
; didn't initialize correctly
init_apic_error:
stc ; no apic found
ret
init_apic endp
IOAPIC_HANDLER_LEN equ 10
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; IO APIC's handlers.
; These simply jmp to the 'normal' IVT
; These should all be only IOAPIC_HANDLER_LEN bytes each
int50_handler:
pushf
call far offset int08_handler,BIOS_BASE
call apic_eoi
iret
int51_handler:
pushf
call far offset int09_handler,BIOS_BASE
call apic_eoi
iret
int52_handler:
pushf
call far offset int08_handler,BIOS_BASE ; we overide IRQ 2 to IRQ 0
call apic_eoi
iret
int53_handler:
pushf
call far offset int0B_handler,BIOS_BASE
call apic_eoi
iret
int54_handler:
pushf
call far offset int0C_handler,BIOS_BASE
call apic_eoi
iret
int55_handler:
pushf
call far offset int0D_handler,BIOS_BASE
call apic_eoi
iret
int56_handler:
pushf
call far offset int0E_handler,BIOS_BASE
call apic_eoi
iret
int57_handler:
pushf
call far offset int0F_handler_0,BIOS_BASE
call apic_eoi
iret
int58_handler:
pushf
call far offset int70_handler,BIOS_BASE
call apic_eoi
iret
int59_handler:
pushf
call far offset int71_handler,BIOS_BASE
call apic_eoi
iret
int5A_handler:
pushf
call far offset int72_handler,BIOS_BASE
call apic_eoi
iret
int5B_handler:
pushf
call far offset int73_handler,BIOS_BASE
call apic_eoi
iret
int5C_handler:
pushf
call far offset int74_handler,BIOS_BASE
call apic_eoi
iret
int5D_handler:
pushf
call far offset int75_handler,BIOS_BASE
call apic_eoi
iret
int5E_handler:
pushf
call far offset int76_handler,BIOS_BASE
call apic_eoi
iret
int5F_handler:
pushf
call far offset int77_handler_0,BIOS_BASE
call apic_eoi
iret
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; APIC Spourious interrupt vector
; simply returns, no IOE needed
; on entry:
; nothing
; on return
; nothing
; destroys none
int7F_handler:
iret
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; APIC EOI
; on entry:
; nothing
; on return
; nothing
; destroys none
apic_eoi proc near uses esi
mov esi,APIC_BASE_ADDR
mov dword fs:[esi+APIC_REG_EOI],0
ret
apic_eoi endp
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; initialize the IOAPIC
; on entry:
; ds -> EBDA
; on return
; carry set if no ioapic found
; destroys none
init_ioapic proc near uses eax ebx ecx esi
mov esi,IOAPIC_BASE_ADDR
mov al,0 ; id
shl eax,24
mov ebx,IOAPIC_REG_ID
call ioapic_write
call ioapic_read
shr eax,24
mov [EBDA_DATA->ioapic_id],al
mov ebx,IOAPIC_REG_VER
call ioapic_read
mov [EBDA_DATA->ioapic_ver],al
shr eax,16
inc ax
mov [EBDA_DATA->ioapic_entries],al
; count of entries must be at least 16
cmp al,16
jb short init_ioapic_error
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; update the BDA's interrupt vector table
; vectors 0x50 -> 0x5F
mov ax,0x50
mov bx,offset int50_handler
mov cx,16
@@: push cx
mov cx,BIOS_BASE
call set_int_vector
pop cx
add bx,IOAPIC_HANDLER_LEN
inc ax
loop @b
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
mov ecx,((~((1<<14) | (1<<13) | (1<<12) | (1<<6) | (1<<2) | (1<<1))) << 16) ; 1000111110111001_0000000000000000b ; bit = 1 = masked, 0 = unmasked
mov ebx,IOAPIC_REG_REDIR ; start at 0x10
mov eax,0x50 ; start at 0x50
@@: push ecx
push eax
;or eax,((0 << 8) | \ ; delivery mode: fixed
; (0 << 11) | \ ; destination mode: phys
; (0 << 13) | \ ; active: high
; (0 << 15)); ; trigger: edge
and ecx,(1 << 16) ; get mask/unmask bit
or eax,ecx
call ioapic_write
inc ebx
movzx byte eax,[EBDA_DATA->ioapic_id]
shl eax,(56-32) ; id in bits 31:24 of high dword
call ioapic_write
inc ebx
pop eax
pop ecx
shr ecx,1
; increment to next
inc eax
cmp eax,0x60
jb short @b
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; the apic was found and initialized. Mask all
; interrupts on the 8259
mov al,0xFF
out PORT_PIC_MASTER_DATA,al
out PORT_PIC_SLAVE_DATA,al
clc
ret
init_ioapic_error:
stc
ret
init_ioapic endp
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; 'disable' the APIC
; on entry:
; nothing
; on return
; nothing
; destroys none
apic_disable proc near uses eax esi es
call bios_get_ebda
mov es,ax
; do we have an APIC installed?
test dword es:[EBDA_DATA->cpuid_features],CPUID_APIC
jz short @f
; software disable the apic by clearing bit 8
; in the Spurious-Interrupt Vector register
mov esi,APIC_BASE_ADDR
mov eax,fs:[esi+APIC_REG_SIV]
and ah,0xFE
mov fs:[esi+APIC_REG_SIV],eax
@@: ret
apic_disable endp
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; 'disable' the IO APIC and restore the 8259
; on entry:
; nothing
; on return
; nothing
; destroys none
ioapic_disable proc near uses eax ebx esi es
call bios_get_ebda
mov es,ax
; do we have an APIC installed?
test dword es:[EBDA_DATA->cpuid_features],CPUID_APIC
jz short ioapic_disable_done
; we need to change all IO APIC interrupts to ExtINT (111b)
; so the 8259 will handle them.
mov esi,IOAPIC_BASE_ADDR
mov ebx,IOAPIC_REG_REDIR ; start at 0x10
xor eax,eax ; start at 0x00
@@: push eax
or eax,((111b << 8) | \ ; delivery mode: ExtINT (8259a)
(0 << 11) | \ ; destination mode: phys
(0 << 13) | \ ; active: high
(0 << 15) | \ ; trigger: edge
(1 << 16)); ; masked (let the 8259a handle it)
call ioapic_write
inc ebx
movzx byte eax,es:[EBDA_DATA->ioapic_id]
shl eax,(56-32) ; id in bits 31:24 of high dword
call ioapic_write
inc ebx
pop eax
inc eax
cmp eax,0x10
jb short @b
ioapic_disable_done:
; initialize the 8259 PIC
call init_pic
ret
ioapic_disable endp
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; read a register of the IOAPIC
; on entry:
; esi = IOAPIC base
; ebx = address
; on return
; eax = value read
; destroys none
ioapic_read proc near
mov fs:[esi+APIC_REG_SEL],ebx
mov eax,fs:[esi+APIC_REG_DATA]
ret
ioapic_read endp
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; read a register of the IOAPIC
; on entry:
; esi = IOAPIC base
; ebx = address
; eax = value to write
; on return
; nothing
; destroys none
ioapic_write proc near
mov fs:[esi+APIC_REG_SEL],ebx
mov fs:[esi+APIC_REG_DATA],eax
ret
ioapic_write endp
.end