vit*_*lho 7 x86 assembly multicore dos fasm
我正在使用 DOS 启动并启动我的应用程序test.exe。该程序以实模式启动 BSP(引导处理器)并访问 APIC 表FEE0:0000以启用偏移量处的 SVI(伪向量中断)0x0F0并INIT-SIPI-SIPI使用ICR_low(偏移量 0x300)和ICR_high(偏移量 0x310)发送序列。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
Run Code Online (Sandbox Code Playgroud)
我希望 BSP 进入无限循环,AP 在 0000:8000 执行代码并在视频内存中打印“A”。
11/06/2019 大家好!
现在我有一个可以访问保护模式的代码。因为我很难进入虚幻模式,所以我决定保持保护模式并通过这种方式启用所有内核。
这是一个简单的代码,但 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!"
Run Code Online (Sandbox Code Playgroud)
现在我正在寻找一个延迟例程来发送 init 和 sipi 消息。我认为这是问题所在,因为这还没有奏效。
BSP 在位置 10 打印字母“A”,任何人都应该在位置 20 打印另一个字母,但只打印“A”。
在我寻找如何让它发挥作用时,有什么想法可以帮助我吗?
提前致谢。
OBS:现在我学会了如何使用“qemu”模拟器,我正在模拟里面的所有内容。
第二次编辑:代码有效。我正在使用只有 1 个内核的 qemu 模拟器。当我使用 2 个或更多内核时,代码有效!!
您需要使用不带引号的“qemu-system-x86_64 -cpu 486 -smp 2 'path'”。
2019 年 6 月 12 日我尝试在真正的计算机上运行它,但它只执行重置循环。有人知道吗?
14/06/2019 你好!我又来了!我处理了有关 DOS 内部线性寻址的这个大问题,并使用以前的 .exe 程序解决了该问题,该程序将 kernel.bin(发送 INIT-SIPI-SIPI 的程序)复制到 0xXXXXXXXX 地址。在 kernel.bin 里面我放了“org 0xXXXXXXXX”,现在我不需要解决我使用的所有指针。现在 INIT-SIPI-SIPI 序列正在工作。
我需要做的另一件事是在退出程序之前退出保护模式。如果我不这样做,DOS 就会崩溃。所以我使用上面的链接来解决线性寻址(通过将大部分代码复制到已知的内存位置)并将控制权返回给 DOS。
这很有趣,因为我将 AP 核心放在一个循环中,在屏幕上打印“来自另一个核心的你好”,然后 BSP 退出程序并返回到 dos。无论您做什么,都无法清除消息。
知道我将编写一个简单的蹦床代码,将内核放在不同的位置并执行 4 个计数器例程。这是唤醒内核并为它们提供一些工作的功能的开始。之后我会以正确的方式实现 MP 和 MDAT 表检测。
谢谢!
是否可以使用 INIT-SIPI-SIPI 序列唤醒所有内核处于实模式的英特尔内核?
也许吧)。有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 是没有意义的)我的答案应该被视为“仅用于学术目的”。
| 归档时间: |
|
| 查看次数: |
688 次 |
| 最近记录: |