简体   繁体   中英

32-bit assembly bootloader works in VM/qemu but not on a real PC

I coded this small bootloader that prints a single character to the screen in 32-bit protected mode:

bits 16
org 0x7c00

jmp boot
times 3-($-$$) db 0x90   ; Support 2 or 3 byte encoded JMPs before BPB.

; Dos 4.0 EBPB 1.44MB floppy
OEMname:           db    "mkfs.fat"  ; mkfs.fat is what OEMname mkdosfs uses
bytesPerSector:    dw    512
sectPerCluster:    db    1
reservedSectors:   dw    1
numFAT:            db    2
numRootDirEntries: dw    224
numSectors:        dw    2880
mediaType:         db    0xf0
numFATsectors:     dw    9
sectorsPerTrack:   dw    18
numHeads:          dw    2
numHiddenSectors:  dd    0
numSectorsHuge:    dd    0
driveNum:          db    0
reserved:          db    0
signature:         db    0x29
volumeID:          dd    0x2d7e5a1a
volumeLabel:       db    "NO NAME    "
fileSysType:       db    "FAT12   "

boot:
mov ax, 0x2401
int 0x15 ; enable A20 bit
mov ax, 0x0003 ; change the video mode to 0x03
int 0x10

lgdt [gdt_pointer] ; load the gdt table
mov eax, cr0 
or eax, 0x1 ; set the protected mode bit on special CPU reg cr0
mov cr0, eax
jmp code_seg:main ; long jump to the code segment code_seg

gdt_start:
dq 0x0

gdt_code:
dw 0xFFFF
dw 0
db 0
db 0b10011010
db 0b11001111
db 0

gdt_data:
dw 0xFFFF
dw 0
db 0
db 0b10010010
db 0b11001111
db 0

gdt_end:

gdt_pointer:
dw gdt_end - gdt_start
dd gdt_start

code_seg equ gdt_code - gdt_start
data_seg equ gdt_data - gdt_start

bits 32

main:
mov ax, data_seg
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov ebp, 0x90000
mov esp, ebp

mov ebx, 0xb8000
mov al, '!'
mov ah, 0b00001111
mov word[ebx], ax
cli
jmp $

times 510 - ($-$$) db 0
dw 0xaa55

This code works fine on a VM, but it causes a couple of screen flashes and a reboot on a real machine (I have tested this on two PCs). Changing a few lines (deleting the "bits 32", "or eax, 0x1", and replacing "jmp code_seg:main" with "jmp main") to use 16 bit mode instead of 32 bit protected causes the code to work just fine. However, when I use 32-bit mode, it fails. Why is this? Thanks in advance.

The problem was that you didn't initialise ds to zero. You're using the following lgdt instruction:

    lgdt [gdt_pointer] ; load the gdt table

This memory access implicitly uses the default segment, which is ds for an address without bp . You're using org 7C00h so you want ds as zero. However, it is valid for the ROM-BIOS loader to leave any possible value in ds when it transfers control to your loader. The solution is to add the following prior to the lgdt instruction:

    xor ax, ax
    mov ds, ax

This zeros the ax register then uses it to initialise ds to zero.

This solution is also provided in Michael Petch's General Tips for Bootloader Development .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM