Intel x86 Assembly Language & Microarchitecture
Beheer van meerdere processoren
Zoeken…
parameters
| LAPIC register | Adres (ten opzichte van APIC BASE ) |
|---|---|
| Lokaal APIC ID-register | + 20h |
| Valse vectorregistratie onderbreken | + 0f0h |
| Interrupt Command Register (ICR); bits 0-31 | + 300h |
| Interrupt Command Register (ICR); bits 32-63 | + 310h |
Opmerkingen
Om toegang te krijgen tot de LAPIC-registers moet een segment het adresbereik kunnen bereiken dat begint bij APIC Base (in IA32_APIC_BASE ).
Dit adres is verplaatsbaar en kan in theorie worden ingesteld om ergens in het lagere geheugen te wijzen, waardoor het bereik in de echte modus adresseerbaar wordt.
De lees- / schrijfcycli naar het LAPIC-bereik worden echter niet doorgegeven aan de Bus-interface-eenheid, waardoor toegang tot de adressen "erachter" wordt gemaskeerd.
Er wordt aangenomen dat de lezer bekend is met de modus Onwerkelijk , omdat deze in een voorbeeld wordt gebruikt.
Het is ook noodzakelijk om bekwaam te zijn met:
- Omgaan met het verschil tussen logische en fysieke adressen 1
- Real-modus segmentatie.
- Geheugenaliasing, id est de mogelijkheid om verschillende logische adressen te gebruiken voor hetzelfde fysieke adres
- Absoluut, relatief, ver, bijna telefoontjes en sprongen.
- NASM-assembler , met name dat de
ORGrichtlijn wereldwijd is. Het splitsen van de code in meerdere bestanden vereenvoudigt de codering aanzienlijk, omdat het mogelijk is om verschillende sectie verschillende ORG's te geven .
Ten slotte gaan we ervan uit dat de CPU een Local Advanced Programmable Interrupt Controller ( LAPIC ) heeft.
Indien dubbelzinnig vanuit de context, betekent APIC altijd LAPIC (e niet IOAPIC, of xAPIC in het algemeen).
Referenties:
- Hoofdstuk 8 en 10 van Intel-handleidingen .
| bitveld |
|---|
![]() |
![]() |
![]() |
![]() |
| MSR-naam | Adres |
|---|---|
| IA32_APIC_BASE | 1BH |
1 Als paging wordt gebruikt, spelen ook virtuele adressen een rol.
Maak alle processors wakker
In dit voorbeeld wordt elke Application Processor (AP) gewekt en worden ze, samen met de Bootstrap Processor (BSP), hun LAPIC ID weergegeven.
; Assemble boot sector and insert it into a 1.44MiB floppy image
;
; nasm -f bin boot.asm -o boot.bin
; dd if=/dev/zero of=disk.img bs=512 count=2880
; dd if=boot.bin of=disk.img bs=512 conv=notrunc
BITS 16
; Bootloader starts at segment:offset 07c0h:0000h
section bootloader, vstart=0000h
jmp 7c0h:__START__
__START__:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
xor sp, sp
cld
;Clear screen
mov ax, 03h
int 10h
;Set limit of 4GiB and base 0 for FS and GS
call 7c0h:unrealmode
;Enable the APIC
call enable_lapic
;Move the payload to the expected address
mov si, payload_start_abs
mov cx, payload_end-payload + 1
mov di, 400h ;7c0h:400h = 8000h
rep movsb
;Wakeup the other APs
;INIT
call lapic_send_init
mov cx, WAIT_10_ms
call us_wait
;SIPI
call lapic_send_sipi
mov cx, WAIT_200_us
call us_wait
;SIPI
call lapic_send_sipi
;Jump to the payload
jmp 0000h:8000h
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
; Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
;CX = Wait (in ms) Max 65536 us (=0 on input)
us_wait:
mov dx, 80h ;POST Diagnose port, 1us per IO
xor si, si
rep outsb
ret
WAIT_10_ms EQU 10000
WAIT_200_us EQU 200
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
; Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
enable_lapic:
;Enable the APIC globally
;On P6 CPU once this flag is set to 0, it cannot be set back to 16
;Without an HARD RESET
mov ecx, IA32_APIC_BASE_MSR
rdmsr
or ah, 08h ;bit11: APIC GLOBAL Enable/Disable
wrmsr
;Mask off lower 12 bits to get the APIC base address
and ah, 0f0h
mov DWORD [APIC_BASE], eax
;Newer processors enables the APIC through the Spurious Interrupt Vector register
mov ecx, DWORD [fs: eax + APIC_REG_SIV]
or ch, 01h ;bit8: APIC SOFTWARE enable/disable
mov DWORD [fs: eax+APIC_REG_SIV], ecx
ret
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
; Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
lapic_send_sipi:
mov eax, DWORD [APIC_BASE]
;Destination field is set to 0 has we will use a shorthand
xor ebx, ebx
mov DWORD [fs: eax+APIC_REG_ICR_HIGH], ebx
;Vector: 08h (Will make the CPU execute instruction ad address 08000h)
;Delivery mode: Startup
;Destination mode: ignored (0)
;Level: ignored (1)
;Trigger mode: ignored (0)
;Shorthand: All excluding self (3)
mov ebx, 0c4608h
mov DWORD [fs: eax+APIC_REG_ICR_LOW], ebx ;Writing the low DWORD sent the IPI
ret
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
; Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
lapic_send_init:
mov eax, DWORD [APIC_BASE]
;Destination field is set to 0 has we will use a shorthand
xor ebx, ebx
mov DWORD [fs: eax+APIC_REG_ICR_HIGH], ebx
;Vector: 00h
;Delivery mode: Startup
;Destination mode: ignored (0)
;Level: ignored (1)
;Trigger mode: ignored (0)
;Shorthand: All excluding self (3)
mov ebx, 0c4500h
mov DWORD [fs: eax+APIC_REG_ICR_LOW], ebx ;Writing the low DWORD sent the IPI
ret
IA32_APIC_BASE_MSR EQU 1bh
APIC_REG_SIV EQU 0f0h
APIC_REG_ICR_LOW EQU 300h
APIC_REG_ICR_HIGH EQU 310h
APIC_REG_ID EQU 20h
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
; Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
APIC_BASE dd 00h
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
; Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
unrealmode:
lgdt [cs:GDT]
cli
mov eax, cr0
or ax, 01h
mov cr0, eax
mov bx, 08h
mov fs, bx
mov gs, bx
and ax, 0fffeh
mov cr0, eax
sti
;IMPORTAT: This call is FAR!
;So it can be called from everywhere
retf
GDT:
dw 0fh
dd GDT + 7c00h
dw 00h
dd 0000ffffh
dd 00cf9200h
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
; Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
payload_start_abs:
; payload starts at segment:offset 0800h:0000h
section payload, vstart=0000h, align=1
payload:
;IMPORTANT NOTE: Here we are in a "new" CPU every state we set before is no
;more present here (except for the BSP, but we handler every processor with
;the same code).
jmp 800h: __RESTART__
__RESTART__:
mov ax, cs
mov ds, ax
xor sp, sp
cld
;IMPORTANT: We can't use the stack yet. Every CPU is pointing to the same stack!
;Get an unique id
mov ax, WORD [counter]
.try:
mov bx, ax
inc bx
lock cmpxchg WORD [counter], bx
jnz .try
mov cx, ax ;Save this unique id
;Stack segment = CS + unique id * 1000
shl ax, 12
mov bx, cs
add ax, bx
mov ss, ax
;Text buffer
push 0b800h
pop es
;Set unreal mode again
call 7c0h:unrealmode
;Use GS for old variables
mov ax, 7c0h
mov gs, ax
;Calculate text row
mov ax, cx
mov bx, 160d ;80 * 2
mul bx
mov di, ax
;Get LAPIC id
mov ebx, DWORD [gs:APIC_BASE]
mov edx, DWORD [fs:ebx + APIC_REG_ID]
shr edx, 24d
call itoa8
cli
hlt
;DL = Number
;DI = ptr to text buffer
itoa8:
mov bx, dx
shr bx, 0fh
mov al, BYTE [bx + digits]
mov ah, 09h
stosw
mov bx, dx
and bx, 0fh
mov al, BYTE [bx + digits]
mov ah, 09h
stosw
ret
digits db "0123456789abcdef"
counter dw 0
payload_end:
; Boot signature is at physical offset 01feh of
; the boot sector
section bootsig, start=01feh
dw 0aa55h
Er zijn twee belangrijke stappen om uit te voeren:
1. De AP's wakker maken
Dit wordt bereikt door een INIT-SIPI-SIPI (ISS) -reeks toe te voegen aan alle AP's.
De BSP die de ISS-reeks verzendt met als bestemming de steno Alles exclusief zichzelf , waardoor alle AP's worden getarget.
Een SIPI (Startup Inter Processor Interrupt) wordt genegeerd door alle CPU's die worden gewekt tegen de tijd dat ze deze ontvangen, dus de tweede SIPI wordt genegeerd als de eerste voldoende is om de doelprocessors te activeren. Het wordt geadviseerd door Intel om compatibiliteitsredenen.
Een SIPI bevat een vector , deze heeft een soortgelijke betekenis, maar is in de praktijk absoluut anders dan een interruptvector (ook wel interruptnummer genoemd).
De vector is een 8-bits getal met de waarde V (weergegeven als vv in base 16), waardoor de CPU instructies begint uit te voeren op het fysieke adres 0vv000h .
We zullen 0vv000h het Wake-up-adres (WA) noemen.
De WA wordt gedwongen op een 4KiB (of pagina) grens.
We gebruiken 08h als V , de WA is dan 08000h , 400h bytes na de bootloader.
Dit geeft controle aan de AP's.
2. Initialiseren en differentiëren van de AP's
Het is noodzakelijk om een uitvoerbare code te hebben bij de WA. De bootloader is om 7c00h , dus we moeten wat code verplaatsen op de paginagrens.
Het eerste dat u moet onthouden bij het schrijven van de lading is dat elke toegang tot een gedeelde bron moet worden beschermd of gedifferentieerd.
Een gemeenschappelijke gedeelde bron is de stapel . Als we de stapel naïef initialiseren, gebruiken alle AP's dezelfde stapel!
De eerste stap wordt vervolgens met behulp van verschillende adressen stapel, waardoor de stapel differentiëren.
We bereiken dat door een uniek nummer, op nul gebaseerd, voor elke CPU toe te wijzen. Dit nummer, we noemen het index , wordt gebruikt voor het differentiëren van de stapel en de lijn waar de CPU zijn APIC-ID zal schrijven.
Het stapeladres voor elke CPU is 800 uur: (index * 1000 uur) geeft elke AP 64 KB aan stapel.
Het regelnummer voor elke CPU is index , de aanwijzer in de tekstbuffer is dus 80 * 2 * index .
Om de index te genereren, wordt een lock cmpxchg gebruikt om een WOORD atomair te verhogen en terug te geven.
Slotopmerkingen
- Een schrijven naar poort 80h wordt gebruikt om een vertraging van 1 µs te genereren.
-
unrealmodeis een verre routine, dus het kan ook worden opgeroepen na het wakker worden. - De BSP springt ook naar de WA.
screenshot
Van Bochs met 8 processors




