Real computer reboots while setting ds in long mode
Posted: Sat Aug 03, 2024 9:14 am
I use multiboot2 as the bootloader. After getting control from it I switch to long mode, and set the segment registers ds, es, etc. to the 64-bit segment selector. On virtual machines, it works all well but on 2 real computers i've tested on, it leads to a reboot at just the instruction to set ds: putting hlt before it makes the computer hang normally.
The segment descriptors look fine, the page tables are correctly set, I couldn't think of any other possible cause. Does anyone have ideas?
Tested vm: qemu6.2.0, vmware17 and bochs2.8
entry.s, in nasm syntax, 'entry32' is the entry point:
The segment descriptors look fine, the page tables are correctly set, I couldn't think of any other possible cause. Does anyone have ideas?
Tested vm: qemu6.2.0, vmware17 and bochs2.8
entry.s, in nasm syntax, 'entry32' is the entry point:
Code: Select all
section .entry align=16
%macro Descriptor 3
dw %2 & 0FFFFh
dw %1 & 0FFFFh
db ( %1 >> 16) & 0FFh
dw (( %2 >> 8) & 0F00h) | ( %3 & 0F0FFh)
db ( %1 >> 24) & 0FFh
%endmacro
SEG_RPL equ 1
DPL equ 0x20
SEG64 equ 0x2000
SEG32 equ 0x4000
SEG_PG4K equ 0x8000
SEG_P equ 0x80
SEG_SEG equ 0x10
SEG_AC equ 0x1 ;accessed
SEG_RW equ 0x2
SEG_DR equ 0x4 ;direction
SEG_EX equ 0x8
SEG_BASE equ SEG_P|SEG_SEG
SEG_DATA equ SEG_BASE|SEG_RW
SEG_CODE equ SEG_BASE|SEG_RW|SEG_EX
SEG_DATA32 equ SEG_DATA|SEG32|SEG_PG4K
SEG_CODE32 equ SEG_CODE|SEG32|SEG_PG4K
SEG_DATA64 equ SEG_DATA|SEG32 ; ?
SEG_CODE64 equ SEG_CODE|SEG64
SEG_CODE16 equ SEG_CODE
GDT_DEST_PHY equ 0x40000
IDT_PHY equ 0x41000
RM16_ENTRY_PHY equ 0x4000
GDT:
Descriptor 0,0,0
Descriptor 0,0xfffff,SEG_CODE64
Descriptor 0,0,SEG_DATA64
Descriptor 0,0,SEG_DATA64+DPL*3
Descriptor 0,0,SEG_CODE64+DPL*3
Descriptor 0,0xfffff,SEG_DATA32
Descriptor 0,0xfffff,SEG_CODE32
Descriptor 0,0xfffff,SEG_DATA32+DPL*3
Descriptor 0,0xfffff,SEG_CODE32+DPL*3
Descriptor 0,0xffff,SEG_CODE16
align 8
GDTR32:
dw 0xff
dd GDT-$$+0x100000
bits 32
PML4_64_PHY equ 0x20000
PDPT_64_PHY equ 0x21000
PDPTS_64_PHY equ 0x22000 ; bootstub pdpt
PD0_64_PHY equ 0x23000
PT0_64_PHY equ 0x24000
PDS_64_PHY equ 0x25000
BOOT_ARG_PHY equ 0x2c000
BA_VIDEO_OFF equ 0x200
BA_CMDLINE_OFF equ 0x380
BA_ARDS_OFF equ 0x400 ; bootargs ARDS offset
BA_XSDP_OFF equ 0x500
BA_XSDP_AVAIL_OFF equ 8
BA_XSDP_PHY_OFF equ 12
BA_ARDS_COUNT_OFF equ 0
BA_CMDLINE_LEN_OFF equ 4
BA_ARCHPAG_OFF equ 0x14
BA_ARCHPHY_OFF equ 0x10
STACK1_PHY equ 0x10000
STACK_RM_PHY equ 0xe000
align 16
extern entry32
entry32:
; reset 32bit environment
lgdt [GDTR32-$$+0x100000]
jmp 0x30: .next32-$$+0x100000
.next32:
mov ax, 0x28
mov ds, ax
mov es, ax
mov ss, ax
mov esp, STACK1_PHY
mov ebp, BOOT_ARG_PHY
; Parse Multiboot2 info
multiboot_magic equ 0xe85250d6 ; Multiboot 2 magic number
; 设置Multiboot 2 magic number和info结构指针
mov eax, multiboot_magic
; 检查Multiboot 2 magic number
cmp eax, 0xe85250d6
jne invalid_multiboot ; 如果不匹配,跳转到错误处理
; 解析Multiboot 2信息
add ebx, 8
parse_tags:
mov eax, [ebx] ; 读取tag的类型字段
cmp eax, 0 ; 如果为0,表示Tag结束
je end_parse_tags
; 解析内存表信息 (Tag 6)
cmp eax, 6
je parse_memory_tag
; 解析内核命令行 (Tag 1)
cmp eax, 1
je parse_cmdline_tag
; 解析framebuffer信息 (Tag 8)
cmp eax, 8
je parse_framebuffer_tag
cmp eax, 3
je parse_archive_tag
cmp eax, 14
je parse_rsdp_tag
cmp eax, 15
je parse_xsdp_tag
jmp next_tag ; 跳转到下一个tag
parse_memory_tag:
mov ecx, [ebx + 4] ; 获取内存表的大小
sub ecx, 32
mov edx, ecx
shr ecx, 2 ; /4
lea esi, [ebx + 16]
lea edi, [ebp + BA_ARDS_OFF]
rep movsd ; move once per 4byte
mov eax, 0xaaaaaaab ; /24
mul edx
shr edx, 4
mov [ebp + BA_ARDS_COUNT_OFF], edx
jmp next_tag
parse_cmdline_tag:
mov esi, ebx
mov ecx, [ebx + 4] ; 获取命令行字符串的length
sub ecx, 8
lea edi, [ebp + BA_CMDLINE_OFF]
rep movsb
jmp next_tag
parse_framebuffer_tag:
lea esi, [ebx + 8]
lea edi, [ebp + BA_VIDEO_OFF]
mov ecx, 21
rep movsb
jmp next_tag
parse_archive_tag:
mov esi, [ebx + 8]
mov ecx, [ebx + 12]
sub ecx, esi
add ecx, 0xfff
shr ecx, 12
mov [ebp + BA_ARCHPHY_OFF], esi
mov [ebp + BA_ARCHPAG_OFF], ecx
jmp next_tag
parse_rsdp_tag:
mov edi, [ebp + BA_XSDP_AVAIL_OFF]
test edi, edi ; prioritize xsdp
jnz next_tag
parse_xsdp_tag:
lea esi, [ebx + 8]
mov ecx, [ebx + 4]
lea edi, [ebp + BA_XSDP_OFF]
rep movsb
lea edi, [ebp + BA_XSDP_AVAIL_OFF]
mov dword [edi], 1
next_tag:
add ebx, [ebx + 4] ; 跳到下一个tag
add ebx, 7
and ebx, 0xfffffff8 ; align ebx to 8 (tags start at 8-aligned addr)
jmp parse_tags
end_parse_tags:
; find xsdp ourselves if not provided
mov eax, [ebp + BA_XSDP_AVAIL_OFF]
test eax, eax
jnz skip_find_rsdp
call find_rsdp32
mov [ebp + BA_XSDP_PHY_OFF], eax
jmp skip_find_rsdp
find_rsdp32:
; ...
ret
find_rsdp_tag32:
; ...
ret
skip_find_rsdp:
; clear screen
mov eax, 0x07000700
mov edi, 0xb8000
mov ecx, 80*25*2
rep stosd
; copy GDT
mov esi, GDT-$$+0x100000
mov edi, GDT_DEST_PHY
mov ecx, 10*8/4
rep movsd
; setup page tables
mov eax,PML4_64_PHY
mov cr3,eax
xor ecx, ecx ; ecx = 0
; zeroize page table memory !
mov dword [eax+0xff8],0x3+PML4_64_PHY ; trick on self-refing
mov [eax+0xffc], ecx
mov dword [eax+0xfe8],0x3+PDPT_64_PHY
mov [eax+0xfec], ecx
mov ebx,PDPT_64_PHY
mov dword [ebx],0x3+PD0_64_PHY
mov [ebx+4], ecx
mov ebx,PD0_64_PHY
mov dword [ebx],0x3+PT0_64_PHY
mov [ebx+4], ecx
mov dword [eax],0x3+PDPTS_64_PHY
mov [eax+4], ecx
mov ebx,PDPTS_64_PHY
mov dword [ebx],0x3+PDS_64_PHY
mov [ebx+4], ecx
mov ebx,PDS_64_PHY
mov dword [ebx],0x83
mov [ebx+4], ecx
xor ebp, ebp ; used by fill_pt32
mov edi,PT0_64_PHY
mov eax,0x100003 ; kernel
mov ecx,128
call _boot_fill_pt32
mov eax,3 ; low 0.5m mem
mov ecx,128
call _boot_fill_pt32
; enter 64bit
cli
mov eax,cr4
or eax,0x2b0
mov cr4,eax
mov ecx,0xc0000080
rdmsr
or eax,0x901
wrmsr
mov eax,cr0
or eax, 0x80000000
mov cr0,eax
jmp 8: entry64-$$+0x100000
_boot_fill_pt32:
stosd
add edi,4
mov [edi], ebp
add eax,0x1000
loop _boot_fill_pt32
ret
align 8
GDTR64:
dw 0x3ff
dq 0xfffffe8000080000+GDT_DEST_PHY
IDTR64:
dw 0xfff
dq 0xfffffe8000080000+IDT_PHY
bits 64
entry64:
lgdt [rel GDTR64]
lidt [rel IDTR64]
mov ax,16
mov ds,ax ; <--- The Exact Crash Point
mov es,ax
mov ss,ax
mov fs,ax
mov gs,ax
mov rbx, 0xfffffe80_00000000
mov rax, rbx
add rax, .higher-$$
jmp rax
.higher:
or rbx, 0x80000 ; add
lea rsp, [rbx + STACK1_PHY]
or rbx, BOOT_ARG_PHY ; add
mov rdi, rbx
extern kmain
call kmain
invalid_multiboot:
_boot_error:
xchg bx, bx
cli
hlt