수색…


매개 변수

LAPIC 레지스터 주소 ( APIC BASE 관련 )
로컬 APIC ID 등록 + 20 시간
가짜 인터럽트 벡터 레지스터 + 0f0h
인터럽트 명령 레지스터 (ICR); 비트 0-31 + 300 시간
인터럽트 명령 레지스터 (ICR); 비트 32-63 + 310h

비고

LAPIC 레지스터에 액세스하려면 세그먼트가 APIC베이스 ( IA32_APIC_BASE )에서 시작하는 주소 범위에 도달 할 수 있어야합니다.
이 주소는 재배치 가능하며 이론적으로 하위 메모리의 어딘가를 가리 키도록 설정 될 수 있으므로 범위를 실제 모드에서 주소 지정 가능하게 만듭니다.

LAPIC 범위에 대한 읽기 / 쓰기 사이클은 버스 인터페이스 장치에 전달 되지 않으므로 버스 인터페이스 장치의 "뒤에있는"주소에 대한 모든 액세스를 마스킹합니다.

독자는 Unreal 모드에 익숙하다고 가정합니다. 일부 예제에서 사용되기 때문입니다.

다음과도 능숙해질 필요가 있습니다.

  • 논리적 주소와 물리적 주소의 차이 처리 1
  • 리얼 모드 세분화.
  • 메모리 별명 지정, 동일한 물리적 주소에 대해 서로 다른 논리 주소를 사용할 수 있는지 여부
  • 절대, 상대, 멀리, 가까운 전화 및 점프.
  • NASM 어셈블러 , 특히 ORG 지시문은 전역 적입니다. 다른 섹션 서로 다른 여러 조직을 제공 할 수있을 것으로 여러 파일에 코드를 분할하면 크게 코딩을 단순화합니다.

마지막으로 CPU에 LAPIC ( Local Advanced Programmable Interrupt Controller )가 있다고 가정합니다.
컨텍스트에서 모호한 경우 APIC은 항상 LAPIC을 의미합니다 (일반적으로 IOAPIC 또는 xAPIC 아님).


참고 문헌 :

비트 필드
가짜 인터럽트 벡터 레지스터
인터럽트 명령 레지스터
로컬 APIC ID 등록
IA32_APIC_BASE
MSR 이름 주소
IA32_APIC_BASE 1 시간

1 페이징을 사용하면 가상 주소도 작동합니다.

모든 프로세서 깨우기

이 예제는 모든 AP ( Application Processor)를 깨우고 BSP ( Bootstrap Processor) 와 함께 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

수행 할 두 가지 주요 단계가 있습니다.

1. AP 깨우기
이것은 모든 AP에 INIT-SIPI-SIPI (ISS) 시퀀스를 보증함으로써 성취됩니다.

ISS 시퀀스를 목적지로 사용하여 속기를 모두 포함 하는 BSP. 자기를 제외한 모든 AP를 대상으로합니다.

SIPI (Startup Inter Processor Interrupt)는 수신 할 때까지 깨어있는 모든 CPU에서 무시되므로 첫 번째 SIPI가 대상 프로세서를 깨우는 데 충분하면 두 번째 SIPI가 무시됩니다. 호환성에 대한 이유는 인텔에서 권장합니다.

SIPI에는 벡터 가 포함되어 있습니다. 이것은 의미는 비슷 하지만 실제로 는 인터럽트 벡터 (인터럽트 번호) 와 완전히 다릅니다 .
벡터는 값 V (기본 16에서 vv 로 표시됨)의 8 비트 숫자로 CPU가 실제 주소 0vv000h 에서 명령 실행을 시작하게합니다.
우리는 0vv000hWake-up address (WA)라고 부를 것입니다.
WA는 4KiB (또는 페이지) 경계에서 강제 실행됩니다.

우리는 V를 08h로 사용하고, WA는 부트 로더 이후 0800h , 400h 바이트입니다.

이렇게하면 AP에 제어 권한이 부여됩니다.

2. AP 초기화 및 차별화
WA에 실행 코드가 있어야합니다. 부트 로더는 7c00h 이므로 페이지 경계에서 일부 코드를 재배치해야합니다.

페이로드를 작성할 때 가장 먼저 기억해야 할 점은 공유 리소스에 대한 액세스는 보호되거나 차별화되어야한다는 것입니다.
일반적인 공유 리소스는 스택입니다 . 스택을 원래대로 초기화하면 모든 AP가 같은 스택을 사용하게됩니다!

첫 번째 단계에서는 다른 스택 주소를 사용하므로 스택을 구별 합니다.
각 CPU에 대해 0부터 시작하는 고유 번호를 할당하여이를 수행합니다. 이 숫자는 인덱스 라고 부르며 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