assembly - 是否可以使用 INIT-SIPI-SIPI 序列唤醒所有内核处于实模式的英特尔内核?

标签 assembly x86 dos multicore fasm

我正在使用 DOS 启动并启动我的应用程序 test.exe。该程序以实模式启动 BSP(引导处理器),并访问位于 FEE0:0000 处的 APIC 表,以在偏移量 0x0F0 处启用 SVI(虚假向量中断)并发送一个使用 ICR_low(偏移量 0x300)和 ICR_high(偏移量 0x310)的 INIT-SIPI-SIPI 序列。 BSP 进入循环 jmp $ 来停止执行,并让 AP(应用程序处理器)执行地址 0000:8000 处的代码并打印一个字符。

消息似乎没有发送到 AP,因为我没有看到它们在显示屏上打印任何内容。

我在实模式下使用 FreeDos。为了编译,我使用FASM(平面汇编器)

我使用了OsDev手册,其中包括我用来测试(经过一些修改)的代码,尽可能简单,看看我是否可以让它工作。我还引用了英特尔程序员手册和其他规范以及 Code Project 上的教程。

我只是想唤醒 AP 并执行一些简单的代码。我发现的所有示例都进入虚幻模式、保护模式、长模式或专注于多核处理。我编写这段代码只是为了了解它是如何工作的。

我的代码是:

    format MZ  

    USE16 

    start:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    xor sp, sp
    cld
    ;Clear screen                
       mov ax, 03h
       int 10h
    ;Move payload to the desired address
       mov si, payload
       mov cx, payload_end-payload + 1
       mov bx,es
       mov ax,7c0h
       mov es,ax
       mov di,400h                 ;07c0:400 = 8000h
       rep movsb
       mov es,bx
    ;Enable APIC table
       call enable_lapic
    ; 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
      ;Para teste de acordar nucleos
      jmp 0000h:8000h ;voltar esse depois


    ;Payload é o código que será movido para o endereço físico 0x08000
    payload:
      mov ax, cs
      mov ds, ax
      xor sp, sp
      cld
    ;Only print letter 'A' directly to video memory
      mov cx,0b800h
      mov es,cx
      mov di,00h
      mov al,41h
      stosb
      cli    
      hlt    
    payload_end:

    enable_lapic:
      mov ecx, IA32_APIC_BASE_MSR
      rdmsr
      or ah, 08h ;Enable global APIC flag
      wrmsr
      and ah, 0f0h ; Mask to obtain APIC_Base address
      mov DWORD [APIC_BASE], eax ;Save it
      shr eax,16
      mov bx,fs
      mov fs,ax
      mov ecx, DWORD [fs:APIC_REG_SIV] ;Load value from SIV (FEE0:00F0) to ecx
      or ch, 01h    ;bit8: APIC SOFTWARE enable/disable
      mov DWORD [fs:APIC_REG_SIV], ecx ;Save it
      mov fs,bx
      ret

    IA32_APIC_BASE_MSR = 1bh
    APIC_REG_SIV       = 0f0h
    APIC_REG_ICR_LOW   = 300h
    APIC_REG_ICR_HIGH  = 310h
    APIC_REG_ID        = 20h

    APIC_BASE         dd 00h

    ;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     = 10000
      WAIT_200_us    = 200

    lapic_send_init:
      mov eax, DWORD [APIC_BASE]
      xor ebx, ebx
      shr eax,16
      mov cx,fs
      mov fs,ax
      mov DWORD [fs:APIC_REG_ICR_HIGH], ebx
      mov ebx, 0c4500h
      mov DWORD [fs:APIC_REG_ICR_LOW], ebx  ;Writing the low DWORD sent the IPI
      mov fs,cx
      ret

    lapic_send_sipi:
      mov eax, DWORD [APIC_BASE]
      xor ebx, ebx
      shr eax,16
      mov cx,fs
      mov fs,ax
      mov DWORD [fs:APIC_REG_ICR_HIGH], ebx
      mov ebx, 0c4608h
      mov DWORD [fs:APIC_REG_ICR_LOW], ebx  ;Writing the low DWORD sent the IPI
      mov fs,cx
      ret

我预计 BSP 会进入无限循环,AP 在 0000:8000 执行代码并在视频内存中打印“A”。

2019年11月6日 大家好!

现在我有了可以访问保护模式的代码。因为我很难转移到虚幻模式,所以我决定保持保护模式并通过这种方式启用所有核心。

这是一个简单的代码,但正如 Michael Petch 所说,我尝试在引导加载程序的情况下执行它。

这是代码:

“将二进制格式设置为‘bin’

use16

org 0x7C00

boot:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    xor sp, sp

   ;Clear screen
   ; mov ax, 03h
   ; int 10h

   ;Set VGA text mode 3
    mov ax,0x3
    int 0x10

   ;Move payload to the desired address
    mov si, payload
    mov cx, payload_end-payload + 1
    ;mov si,boot2
    ;mov cx,boot2_end-boot2+1
    mov bx,es
    mov ax,7c0h
    mov es,ax
    mov di,400h                 ;07c0:400 = 8000h
    rep movsb
    mov es,bx

    ;jmp 0000h:8000h

    call enableA20Line

    call enterProtectedMode

use32

    ;Enable the APIC
     call enable_lapic

    ;INIT
     call lapic_send_init
     ;mov cx, WAIT_10_ms
     ;call us_wait
     .Verify1:
        PAUSE
        MOV EBX,[APIC_BASE]
        MOV EAX,[EBX+0x300];
        SHR EAX,12
        TEST EAX,1
        JNZ .Verify1
     MOV EDI,[APIC_BASE]
     ADD EDI,0xB0
     MOV dword [EDI],0

    ;SIPI
     call lapic_send_sipi
     ;mov cx, WAIT_200_us
     ;call us_wait
     .Verify2:
        PAUSE
        MOV EBX,[APIC_BASE]
        MOV EAX,[EBX+0x300];
        SHR EAX,12
        TEST EAX,1
        JNZ .Verify2
     MOV EDI,[APIC_BASE]
     ADD EDI,0xB0
     MOV dword [EDI],0

    ;SIPI
     call lapic_send_sipi
     ;mov cx, WAIT_200_us
     ;call us_wait
     .Verify3:
        PAUSE
        MOV EBX,[APIC_BASE]
        MOV EAX,[EBX+0x300];
        SHR EAX,12
        TEST EAX,1
        JNZ .Verify3
     MOV EDI,[APIC_BASE]
     ADD EDI,0xB0
     MOV dword [EDI],0

    ;mov eax,0x8000
    ;jmp DWORD[eax]
    ;jmp boot2
    ;jmp 0x8000
    ;jmp $
    ;cli
    ;hlt
    mov eax,0x000b8010
    mov dword[eax],0e41h
    cli
    hlt

use16

enableA20Line:
    mov ax,0x2401
    int 0x15 ;enable A20 bit
    ret

enterProtectedMode:
    lgdt[gdt_pointer]
    mov eax,cr0
    or eax,0x1 ;set the protected mode bit on special cpu reg CR0
    mov cr0,eax

    jmp CODE_SEG:exit ;long jump to the code segment
    exit:
    ret

gdt_pointer:
    dw gdt_end - gdt_start
    dd gdt_start
CODE_SEG = gdt_code - gdt_start
DATA_SEG = gdt_data - gdt_start

gdt_start:
    dq 0x0        ;NULL segment
gdt_code:
    dw 0xFFFF
    dw 0x0
    db 0x0
    db 10011010b
    db 11001111b
    db 0x0
gdt_data:
    dw 0xFFFF
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0
gdt_end:

;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     = 10000
  WAIT_200_us    = 200

use32

enable_lapic:
  mov ecx, IA32_APIC_BASE_MSR
  rdmsr
  or ah, 08h        ;bit11: APIC GLOBAL Enable/Disable
  wrmsr

  and ah, 0f0h
  mov DWORD [APIC_BASE], eax

  mov ecx, DWORD [eax+APIC_REG_SIV]
  ;or ch, 01h                                ;bit8: APIC SOFTWARE enable/disable
  or edx,01FFh
  mov DWORD [eax+APIC_REG_SIV], ecx

  mov DWORD[eax+0B0h],00h
  ret

lapic_send_init:
  mov eax, DWORD [APIC_BASE]

  xor ebx, ebx
  mov DWORD [eax+APIC_REG_ICR_HIGH], ebx

  mov ebx, 0c4500h
  mov DWORD [eax+APIC_REG_ICR_LOW], ebx  ;Writing the low DWORD sent the IPI
  ret

lapic_send_sipi:
  mov eax, DWORD [APIC_BASE]

  xor ebx, ebx
  mov DWORD [eax+APIC_REG_ICR_HIGH], ebx

  mov ebx, 0c4608h
  mov DWORD [eax+APIC_REG_ICR_LOW], ebx  ;Writing the low DWORD sent the IPI
  ret

 IA32_APIC_BASE_MSR = 1bh

 APIC_REG_SIV       = 0f0h

 APIC_REG_ICR_LOW   = 300h
 APIC_REG_ICR_HIGH  = 310h

 APIC_REG_ID        = 20h

 APIC_BASE        dd  00h



boot2:
    mov ax,DATA_SEG
    mov ds,ax
    mov es,ax
    mov fs,ax
    mov gs,ax
    mov ss,ax

    mov esi,hello2
    mov ebx,0b8000h
    .loop:
        lodsb
        or al,al
        jz halt
        or eax,0x0100
        mov word[ebx],ax
        add ebx,2
        jmp .loop
halt:
    cli
    hlt
    hello2: db "Hello world!",0
boot2_end:

use16

payload:
    mov ax,cs
    mov ds,ax
    xor sp,sp

    mov ax,0b800h
    mov es,ax
    mov di,20h
    mov ax,0e45h
    mov [es:di],al

    cli
    hlt
    ;jmp $

payload_end:

times 510 - ($-$$) db 0 ; pad remaining 510 bytes with zeroes
dw 0xaa55 ; magic bootloader magic - marks this 512 byte sector bootable!" 

现在我正在寻找一个延迟例程来发送 init 和 sipi 消息。我认为这就是问题所在,因为这还不起作用。

BSP 在位置 10 处打印字母“A”,任何人都应该在位置 20 处打印另一个字母,但只打印“A”。

在我搜索如何将其投入使用时,有什么想法可以帮助我吗?

提前致谢。

OBS:现在我学会了如何使用“qemu”模拟器,并且正在模拟其中的所有内容。

第二次编辑:代码有效。我使用的 qemu 模拟器只有 1 个核心。当我使用 2 个或更多内核时,代码可以工作!!

您需要使用不带引号的“qemu-system-x86_64 -cpu 486 -smp 2 'path'”。

2019年6月12日 我尝试在真实的计算机中运行它,但它只执行重置循环。 有人知道吗?

2019年6月14日 你好!我又来了! 我处理了这个有关 DOS 内部线性寻址的大问题,并使用以前的 .exe 程序来解决它,该程序将 kernel.bin(发送 INIT-SIPI-SIPI 的程序)复制到 0xXXXXXXXX 地址。在 kernel.bin 中我放入了“org 0xXXXXXXXX”,现在我不需要解析我使用的所有指针。现在 INIT-SIPI-SIPI 序列正在工作。

链接:Switch from protected mode to real mode in a DOS EXE program

我需要做的另一件事是在退出程序之前退出保护模式。如果我不这样做,DOS 就会崩溃。因此,我使用上面的链接来解决线性寻址(通过将大部分代码复制到已知的内存位置)并将控制权返回给 DOS。

这很有趣,因为我将 AP 核心放入循环中,在屏幕上打印“Hello from another core”,然后 BSP 退出程序并返回到 dos。无论您做什么,该消息都无法清除。

知道我将编写一个简单的蹦床代码,将内核放在不同的位置并执行 4 个计数器例程。这是唤醒核心并给它们一些工作的函数的开始。之后我将通过正确的方式实现MP和MDAT表检测。

谢谢!

最佳答案

Is it possible to wake up intel cores with INIT-SIPI-SIPI sequence with all cores in real mode?

是的(也许)。有 2 个选项:

a) 如果 CPU 支持 x2APIC,那么您可以启用它并使用 MSR 发送 INIT-SIPI-SIPI 序列(无需访问实模式下无法访问的地址处的内存映射寄存器) .

b) 对于 xAPIC;可以更改本地 APIC 使用的地址(通过写入 APIC_BASE MSR),以便可以在实模式下访问它。然而,这需要格外小心,因为本地 APIC 不应放置在已在使用中的任何位置,并且您在实模式下可以访问的所有空间很可能已在使用中。为了解决这个问题,您可能需要“特定于芯片组”的代码来修改访问的路由位置(到 RAM,到 PCI 总线,..),然后使用代码来重新配置 MTRR 以适应。 APIC_BASE MSR 也有点“特定于 CPU”(80486 上不存在,其他供应商的 CPU 上可能不存在)。注意:我不认为这个选项合理或实用(特别是对于需要在多台计算机上运行的代码)。

注意:您应该只启动固件认为存在的 CPU(并且不应向有故障和禁用的 CPU 广播 INIT-SIPI-SIPI 序列);并且您很可能无法在实模式下访问 ACPI 表(需要找出存在哪些 CPU)。出于这个原因(因为在不使用保护模式的情况下启动其他 CPU 是没有意义的)我的答案应该被视为“仅用于学术目的”。

关于assembly - 是否可以使用 INIT-SIPI-SIPI 序列唤醒所有内核处于实模式的英特尔内核?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56319227/

相关文章:

c - 寻找GCD的汇编语言程序—— float 异常

c - 在 x86-64 处理器上,什么软件可见的处理器状态需要进入 jmp_buf?

assembly - “MOV AH, 4CH”用汇编语言表示什么意思?

file - 当我使用 int 21h 函数时,我的 asm 代码写入垃圾字节

assembly - x86 程序集 - 编码相对 jmp

c - GCC 中的汇编代码未在 Atmel Studio 中针对 AVR 环境进行编译

assembly - objdump 输出中的 data16 是什么意思?

assembly - 为什么我的裸机 hello world 代码无法在 x86_64 中启动

c++ - 我可以在 Visual Studio 2008 中从 Win32 解决方案配置(反之亦然)编译 x64 exe 吗?