サーチ…


パラメーター

LAPICレジスタ住所( APIC BASE基準にして
ローカルAPIC ID登録 + 20時間
スプリアス割り込みベクタレジスタ + 0f0h
割り込みコマンドレジスタ(ICR);ビット0-31 + 300h
割り込みコマンドレジスタ(ICR);ビット32-63 + 310h

備考

LAPICレジスタにアクセスするには、セグメントがAPICベースIA32_APIC_BASE内 )から始まるアドレス範囲に到達できる必要があります。
このアドレスはリロケータブルであり、理論的には下位メモリのどこかを指すように設定できるため、範囲をリアルモードでアドレス可能にすることができます。

しかし、LAPIC範囲への読み出し/書き込みサイクルは、バスインターフェースユニットに伝搬され 、それにより、その「背後にある」アドレスへのアクセスをマスクする。

読者はUnrealモードに精通していると仮定しています 。これは、いくつかの例で使用されるためです。

それにも堪能であることが必要です。

  • 論理アドレスと物理アドレスの差異の処理1
  • リアルモードセグメンテーション。
  • メモリエイリアシング、id est同じ物理アドレスに対して異なる論理アドレスを使用する能力
  • 絶対的な、相対的な、遠くの、近くの呼び出しとジャンプ。
  • NASMアセンブラ 、特にORG指令はグローバルです。コードを複数のファイルに分割することにより、異なるセクションに異なるORGを与えることが可能になるので、コードを大幅に簡略化します。

最後に、CPUにはLAPICLocal Advanced Programmable Interrupt Controller )が搭載されているものとします。
コンテキストから曖昧な場合、APICは常にLAPICを意味します(一般的にはIOAPICまたはxAPICではありません)。


参考文献:

ビットフィールド
スプリアス割り込みベクタレジスタ
割り込みコマンドレジスタ
ローカルAPIC ID登録
IA32_APIC_BASE
MSR名住所
IA32_APIC_BASE 1h

1ページングを使用する場合は、 仮想アドレスも有効になります。

すべてのプロセッサを起動する

この例では、 アプリケーションプロセッサ (AP)ごとに起動し、 ブートストラッププロセッサ (BSP)と共にLAPIC IDを表示します。

; 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

実行する主な2つの手順があります。

1. APのスリープ
これは、すべてのAPにINIT-SIPI-SIPI (ISS)シーケンスを保証することによって実現されます。

宛先としてISSシーケンスを送信するBSP。すべて自己を除外し 、すべてのAPを対象とします。

SIPI(Startup Inter Processor Interrupt)は、受信するまでに目覚めたすべてのCPUによって無視されるため、最初のプロセッサがターゲットプロセッサのウェイクアップに十分な場合は、2番目のSIPIは無視されます。互換性の理由により、インテルからアドバイスを受けています。

SIPIにはベクトルが含まれていますが、これは意味が似ていますが、実際にまったく異なっています。割り込みベクタ(割り込み番号とも呼ばれます)です。
このベクトルは、CPUが物理アドレス0vv000hで命令の実行を開始させる値V (基数16のvvで表される)の8ビットの数値です
0vv000hを ウェイクアップアドレス (WA)と呼びます。
WAは4KiB(またはページ)境界に強制されます。

08hをVとし、WAはブートローダーの後の08000h 、400hバイトです。

これは、APに制御を与えます。

2. APの初期化と差別化
WAには実行可能コードが必要です。ブートローダーは7c00hです 。そのため、ページ境界でコードを再配置する必要があります。

ペイロードを書くときに覚えておくべき最初のことは、共有リソースへのアクセスがすべて保護されているか、差別化されているかということです。
共通の共有リソースがスタックです。スタックを単純に初期化すると、すべてのAPが同じスタックを使用して終了します。

最初のステップでは、異なるスタックアドレスを使用して、スタックを区別します。
各CPUに固有の番号(ゼロベース)を割り当てることで、これを達成します。この番号はインデックスと呼ばれ、スタックとCPUのAPIC IDを書き込む行を区別するために使用されます。

各CPUのスタックアドレスは800hです:( インデックス * 1000h)スタックの各AP 64KiBを与えます。
各CPUの行番号はインデックスであり、したがってテキストバッファへのポインタは80 * 2 * indexです。

インデックスを生成するために、 lock cmpxchgを使用して、ワードをアトミ​​ックにインクリメントして返します。

最終ノート

  • ポート80hへの書き込みは、1μsの遅延を生成するために使用されます。
  • unrealmodeは遠くのルーチンなので、起床後も呼び出すことができます。
  • BSPもWAにジャンプします。

スクリーンショット

8プロセッサ搭載のBochsから

8つのプロセッサによるスクリーンショット



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow