Suche…


Parameter

LAPIC-Register Adresse (relativ zu APIC BASE )
Lokale APIC-ID-Registrierung + 20h
Störendes Interrupt-Vektorregister + 0f0h
Interrupt-Befehlsregister (ICR); Bits 0–31 + 300h
Interrupt-Befehlsregister (ICR); Bits 32–63 + 310h

Bemerkungen

Um auf die LAPIC-Register zugreifen zu können, muss ein Segment in der Lage sein, den Adressbereich ab APIC Base (in IA32_APIC_BASE ) zu erreichen.
Diese Adresse ist verschiebbar und kann theoretisch so eingestellt werden, dass sie irgendwo im unteren Speicherbereich liegt, wodurch der Bereich im realen Modus adressierbar ist.

Die Lese- / Schreibzyklen zum LAPIC-Bereich werden jedoch nicht zur Busschnittstelleneinheit weitergeleitet, wodurch jeder Zugriff auf die dahinterliegenden Adressen verdeckt wird.

Es wird davon ausgegangen, dass der Leser mit dem Unreal-Modus vertraut ist, da er in einigen Beispielen verwendet wird.

Es ist auch notwendig mit:

  • Umgang mit dem Unterschied zwischen logischen und physischen Adressen 1
  • Real-Modus- Segmentierung.
  • Speicher-Aliasing ist die Fähigkeit, verschiedene logische Adressen für dieselbe physikalische Adresse zu verwenden
  • Absolut, relativ, weit, nahe, ruft und springt.
  • NASM-Assembler , insbesondere dass die ORG Direktive global ist. Das Aufteilen des Codes in mehrere Dateien vereinfacht die Codierung erheblich, da es möglich ist, verschiedenen Abschnitten unterschiedliche ORGs zu geben.

Schließlich nehmen wir an, dass die CPU über einen Local Advanced Programmable Interrupt Controller ( LAPIC ) verfügt.
Wenn aus dem Kontext mehrdeutig, bedeutet APIC immer LAPIC (nicht IOAPIC oder xAPIC im Allgemeinen).


Verweise:

Bitfelder
Störendes Interrupt-Vektorregister
Befehlsregister unterbrechen
Lokale APIC-ID-Registrierung
IA32_APIC_BASE
MSR-Name Adresse
IA32_APIC_BASE 1bh

1 Wenn Paging verwendet wird, werden auch virtuelle Adressen verwendet.

Wachen Sie alle Prozessoren auf

In diesem Beispiel wird jeder Application Processor (AP) aktiviert und veranlasst, dass er zusammen mit dem Bootstrap-Prozessor (BSP) seine LAPIC-ID anzeigt.

; 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

Es gibt zwei Hauptschritte, die auszuführen sind:

1. Wecken der APs
Dies wird erreicht, indem allen APs eine INIT-SIPI-SIPI (ISS) -Sequenz zugeführt wird .

Der BSP, der die ISS-Sequenz sendet, verwendet als Kürzel die Abkürzung Alle ohne self und richtet damit alle APs an.

Ein SIPI (Startup Inter Processor Interrupt) wird von allen CPUs, die zum Zeitpunkt des Empfangs geweckt werden, ignoriert. Daher wird das zweite SIPI ignoriert, wenn die erste zum Auslösen der Zielprozessoren ausreicht. Es wird von Intel aus Kompatibilitätsgründen empfohlen.

Ein SIPI enthält einen Vektor , dieser hat eine ähnliche Bedeutung, unterscheidet sich jedoch in der Praxis absolut von einem Interrupt-Vektor (auch als Interrupt-Nummer bezeichnet).
Der Vektor ist eine 8-Bit-Zahl mit dem Wert V (in Basis 16 als vv dargestellt ), wodurch die CPU Anweisungen an der physikalischen Adresse 0vv000h ausführt .
Wir rufen 0vv000h die Wake-Up - Adresse (WA).
Die WA wird an einer Grenze von 4 KB (oder Seite) erzwungen.

Wir verwenden 08h als V , die WA ist dann 08000h , 400h Bytes nach dem Bootloader.

Dies gibt den APs die Kontrolle.

2. Initialisieren und Unterscheiden der APs
Es ist notwendig, einen ausführbaren Code bei der WA zu haben. Der Bootloader ist um 7 Uhr 00h , also müssen wir etwas Code an der Seitengrenze verschieben.

Beim Schreiben der Nutzdaten ist zu beachten, dass jeder Zugriff auf eine gemeinsam genutzte Ressource geschützt oder differenziert werden muss.
Eine gemeinsame Ressource ist der Stack . Wenn wir den Stack naiv initialisieren, verwenden alle APs denselben Stack!

Der erste Schritt besteht darin, verschiedene Stapeladressen zu verwenden, wodurch der Stapel differenziert wird.
Dies erreichen wir, indem wir für jede CPU eine eindeutige, auf Null basierende Nummer vergeben. Diese Nummer, wir nennen sie Index , wird zur Unterscheidung des Stacks und der Zeile verwendet, in der die CPU ihre APIC-ID schreibt.

Die Stack-Adresse für jede CPU ist 800h: (Index * 1000h), wodurch jeder AP 64 KB Stack erhält .
Die Zeilennummer für jede CPU ist index , der Zeiger in den Textpuffer ist also 80 * 2 * index .

Um den Index zu generieren, wird eine lock cmpxchg verwendet, um ein WORD atomar zu inkrementieren und zurückzugeben.

Abschließende Notizen

  • Mit einem Schreibzugriff auf Port 80h wird eine Verzögerung von 1 µs erzeugt.
  • unrealmode ist eine weit verbreitete Routine und kann auch nach dem Aufwachen aufgerufen werden.
  • Die BSP springt auch zur WA.

Bildschirmfoto

Von Bochs mit 8 Prozessoren

Screenshot mit acht Prozessoren



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow