I'm using DOS to boot up and start my application test.exe. This program starts the BSP (Bootstrap Processor) in real mode and accesses the APIC table at FEE0:0000
to enable the SVI (Spurious vector interrupt) at offset 0x0F0
and send an INIT-SIPI-SIPI
sequence using both ICR_low
(offset 0x300) and ICR_high
(offset 0x310). The BSP enters inside a loop jmp $ to stop executing and lets the APs (Application Processor) execute code at address 0000:8000
and print a character.
It seems the messages aren't being sent to the APs because I don't see any of them print anything to the display..
I'm using FreeDos in real mode. To compile I'm using FASM (flat assembler)
I used OsDev manual that includes the code I'm using to test (with some modifications) as simple as possible, to see if I could get it working. I also referred to the Intel programmers manual and other specs as well as tutorial at Code Project.
I'm only trying to wake the APs up and execute some simple code. All examples that I found enter into unreal mode, protected mode, long mode or are focused in multicore processing. I'm only writing this code to understand how it works.
My code is:
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
I expect the BSP enters into an infinite loop and the APs execute code at 0000:8000 and print 'A' at video memory.
11/06/2019 Hello everybody!
Now I have a code that can access protected mode. Because I'm with difficult to move to unreal mode I decided to stay in protected mode and enable all cores by this way.
It is a simple code but how Michael Petch said, I tried to do it in a bootloader situation.
Here is the code:
"format binary as '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!"
Now I'm searching for a delay routine to send init and sipi messages. I think this is the problem because this is not working yet.
The BSP prints letter "A" at position 10, and anyone should print another letter at position 20, but only "A" is printed.
Any ideas to help me while I'm searching how to put it to work?
Thanks in advance.
OBS: now I learned how to use the "qemu" emulator and I'm simulating all inside it.
SECOND EDIT: THE CODE WORKS. I'm using qemu emulator with only 1 core. When I use with 2 or more cores, code works!!
You need to use "qemu-system-x86_64 -cpu 486 -smp 2 'path'" without quotes.
12/06/2019 I've tried to run it in a real computer but it only do a reset loop. Have anyone a clue about it?
14/06/2019 Hello! I'm here again! I deal with this big problem about linear addressing inside DOS and I solve it with a previous .exe program that copies kernel.bin (program that send INIT-SIPI-SIPI) to a 0xXXXXXXXX address. Inside kernel.bin I put "org 0xXXXXXXXX", now I do not need to solve all pointers I use. Now INIT-SIPI-SIPI sequence is working.
Link: Switch from protected mode to real mode in a DOS EXE program
Another thing that I need to do is exit protected mode before exit program. If I do not do that, DOS crash. So I used the link above to solve linear addressing (by copying most part of the code to a known memory position) and return control to DOS.
It was funny because I put AP cores in a loop printing "Hello from another core" on the screen and the BSP exit program and goes back to dos. No matter what you do, the message can not be cleaned.
Know I will work on a simple trampoline code to put cores in different positions and executing 4 counter routines. It is the beginning of a function to wake up cores and give them some work. After I will implement MP and MDAT table detection to do by the right way.
Thanks!
LGDT
instruction: #54773058 – Syncopate