Intel x86 Assembly Language & Microarchitecture
Gestión multiprocesador
Buscar..
Parámetros
| Registro LAPIC | Dirección (Relativa a APIC BASE ) |
|---|---|
| Registro local de IDIC APIC | + 20h |
| Registro de vectores de interrupciones espurias | + 0f0h |
| Registro de comando de interrupción (ICR); bits 0-31 | + 300h |
| Registro de comando de interrupción (ICR); bits 32-63 | + 310h |
Observaciones
Para acceder a los registros LAPIC, un segmento debe poder alcanzar el rango de direcciones comenzando en APIC Base (en IA32_APIC_BASE ).
Esta dirección es reubicable y, en teoría, se puede configurar para que apunte a algún lugar en la memoria inferior, lo que hace que el rango sea direccionable en modo real.
Sin embargo, los ciclos de lectura / escritura en el rango LAPIC no se propagan a la Unidad de Interfaz de Bus, lo que enmascara cualquier acceso a las direcciones "detrás".
Se supone que el lector está familiarizado con el modo irreal , ya que se utilizará en algún ejemplo.
También es necesario ser competente con:
- Manejando la diferencia entre direcciones lógicas y físicas 1
- Segmentación en modo real .
- Alias de memoria, id. Es la capacidad de usar diferentes direcciones lógicas para la misma dirección física
- Absoluta, relativa, lejana, cercana a las llamadas y saltos.
- El ensamblador NASM , particularmente que la directiva
ORGes global. La división del código en varios archivos simplifica en gran medida la codificación, ya que será posible dar diferentes ORG a diferentes secciones .
Finalmente, asumimos que la CPU tiene un controlador de interrupción programable avanzado local ( LAPIC ).
Si es ambiguo desde el contexto, APIC siempre significa LAPIC (e no IOAPIC, o xAPIC en general).
Referencias:
| Campos de bits |
|---|
![]() |
![]() |
![]() |
![]() |
| Nombre de MSR | Dirección |
|---|---|
| IA32_APIC_BASE | 1bh |
1 Si se utilizará la paginación, las direcciones virtuales también entran en juego.
Despierta todos los procesadores
Este ejemplo activará todos los Procesadores de aplicaciones (AP) y los hará, junto con el Procesador Bootstrap (BSP), mostrar su ID de LAPIC.
; 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
Hay dos pasos principales para realizar:
1. Despertando los APs
Esto se logra mediante la inserción de una secuencia INIT-SIPI-SIPI (ISS) en todos los AP.
El BSP que enviará la secuencia ISS utilizando como destino la abreviatura Todo excluido , con lo que se dirigirá a todos los puntos de acceso.
Todas las CPU que están activadas al momento de su recepción ignoran un SIPI (Interrupción de Inter Interruptor de Inicio), por lo que el segundo SIPI se ignora si el primero es suficiente para activar los procesadores de destino. Es asesorado por Intel por razones de compatibilidad.
Un SIPI contiene un vector , es similar en significado, pero absolutamente diferente en la práctica , a un vector de interrupción (también conocido como número de interrupción).
El vector es un número de 8 bits, de valor V (representado como vv en la base 16), que hace que la CPU comience a ejecutar instrucciones en la dirección física 0vv000h .
Llamaremos a 0vv000h la dirección de Wake-up (WA).
El WA se fuerza en un límite de 4 KB (o página).
Usaremos 08h como V , el WA es entonces 08000h , 400h bytes después del cargador de arranque.
Esto le da control a los APs.
2. Inicializando y diferenciando los APs.
Es necesario tener un código ejecutable en el WA. El gestor de arranque está a 7c00h , por lo que necesitamos reubicar algo de código en el límite de la página.
Lo primero que debe recordar al escribir la carga útil es que cualquier acceso a un recurso compartido debe estar protegido o diferenciado.
Un recurso compartido común es la pila , si inicializamos la pila de forma ingenua, ¡todos los AP terminarán usando la misma pila!
El primer paso es utilizar diferentes direcciones de pila, diferenciando así la pila.
Lo logramos asignando un número único, basado en cero, para cada CPU. Este número, lo llamaremos índice , se usa para diferenciar la pila y la línea donde la CPU escribirá su ID de APIC.
La dirección de pila para cada CPU es 800h: (índice * 1000h) que proporciona a cada AP 64 KB de pila.
El número de línea para cada CPU es el índice , el puntero al búfer de texto es, por lo tanto, el índice 80 * 2 *.
Para generar el índice, se utiliza un lock cmpxchg para incrementar y devolver una PALABRA atómicamente.
Notas finales
- Se utiliza una escritura en el puerto 80h para generar un retraso de 1 µs.
-
unrealmodees una rutina lejana, por lo que también puede llamarse después del despertar. - El BSP también salta a la WA.
Captura de pantalla
De Bochs con 8 procesadores.




