assembly - Int 0x13 failing without error on real hardware, but working in QEMU - Stack Overflow

I currently have a basic bootloader setup to load my kernel with some debug statements:; Set offsets[

I currently have a basic bootloader setup to load my kernel with some debug statements:

; Set offsets
[ 0x7c00]
KERNEL_OFFSET equ 0x2000

[bits 16]

; Setup Stack
mov bp, 0x1000
mov sp, bp

mov bx, RM_BOOT_MSG
call print_rm
call nl_rm

; Load kernel
mov bx, KERNEL_OFFSET  ; Read from disk and store in KERNEL_OFFSET
mov dh, 10             ; Read X sectors
call load_disk

mov bx, KERNEL_LOAD_MSG
call print_rm
call nl_rm

mov bx, KERNEL_OFFSET
call print_rm_hex
call nl_rm

mov bx, [KERNEL_OFFSET]
call print_rm_hex
call nl_rm

mov bh, 0
mov bl, dl
call print_rm_hex
call nl_rm

call KERNEL_OFFSET    ; Begin entering kernel

jmp $

%include "boot/disk.asm"
%include "boot/32b_gdt.asm"

RM_BOOT_MSG: dw "16-bit RM", 0
KERNEL_LOAD_MSG: dw "Kernel Successfully Loaded", 0

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

Here is my load_disk function:

; Disk
; See this for all int 0x13 notes: .html

; AH = 02
; AL = number of sectors to read    (1-128 dec.)
; CH = track/cylinder number  (0-1023 dec., see below)
; CL = sector number  (1-17 dec.)
; DH = head number  (0-15 dec.)
; DL = drive number (0=A:, 1=2nd floppy, 80h=drive 0, 81h=drive 1)
; ES:BX = pointer to buffer

; on return:
;   AH = status  (see INT 13,STATUS)
;   AL = number of sectors read
;   CF = 0 if successful
;      = 1 if error

; Note 0x01 is the boot sector, 0x02 is the first free one

[bits 16]

; Load "dh" sectors for drive "dl" at es:bx
load_disk:
    pusha
    push dx

    mov ah, 0x02

    mov al, dh

    mov ch, 0x00

    mov cl, 0x02

    mov dh, 0x00

    int 0x13
    jc .disk_error

    pop dx
    cmp al, dh
    jne .sector_error
    
    popa
    ret

    .disk_error:
        mov bx, DISK_ERR_MSG
        call print_rm

        mov dh, ah
        call print_rm_hex
        call nl_rm

        jmp .fail_loop

    .sector_error:
        mov bx, SECTOR_ERR_MSG
        call print_rm
        call nl_rm

    .fail_loop:
        jmp $

DISK_ERR_MSG: dw "Error loading from disk: ", 0
SECTOR_ERR_MSG: dw "Incorrect number of sectors read", 0

%include "boot/print_rm.asm"

When run via

qemu-system-i386 -s -usb -drive format=raw, file=\.\PHYSICALDRIVE2

it works as expected with no errors, specifically outputting

Kernel Successfully Loaded
0x2000 (the Kernel offset)
0xC6BB (what is stored at 0x2000)
0x0080 (the drive)

On real hardware, it outputs instead

Kernel Successfully Loaded
0x2000 (the Kernel offset)
0x519B (what is stored at 0x2000)
0x0080 (the drive)

i.e. it fails to load the correct data, causing call KERNEL_OFFSET to fail (it may be noted that when call load_disk is commented out, it still prints 0x519B as the data stored at 0x2000).

Any suggestions on how to resolve this difference between QEMU and real hardware would be greatly appreciated.

I currently have a basic bootloader setup to load my kernel with some debug statements:

; Set offsets
[ 0x7c00]
KERNEL_OFFSET equ 0x2000

[bits 16]

; Setup Stack
mov bp, 0x1000
mov sp, bp

mov bx, RM_BOOT_MSG
call print_rm
call nl_rm

; Load kernel
mov bx, KERNEL_OFFSET  ; Read from disk and store in KERNEL_OFFSET
mov dh, 10             ; Read X sectors
call load_disk

mov bx, KERNEL_LOAD_MSG
call print_rm
call nl_rm

mov bx, KERNEL_OFFSET
call print_rm_hex
call nl_rm

mov bx, [KERNEL_OFFSET]
call print_rm_hex
call nl_rm

mov bh, 0
mov bl, dl
call print_rm_hex
call nl_rm

call KERNEL_OFFSET    ; Begin entering kernel

jmp $

%include "boot/disk.asm"
%include "boot/32b_gdt.asm"

RM_BOOT_MSG: dw "16-bit RM", 0
KERNEL_LOAD_MSG: dw "Kernel Successfully Loaded", 0

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

Here is my load_disk function:

; Disk
; See this for all int 0x13 notes: https://stanislavs./helppc/int_13-2.html

; AH = 02
; AL = number of sectors to read    (1-128 dec.)
; CH = track/cylinder number  (0-1023 dec., see below)
; CL = sector number  (1-17 dec.)
; DH = head number  (0-15 dec.)
; DL = drive number (0=A:, 1=2nd floppy, 80h=drive 0, 81h=drive 1)
; ES:BX = pointer to buffer

; on return:
;   AH = status  (see INT 13,STATUS)
;   AL = number of sectors read
;   CF = 0 if successful
;      = 1 if error

; Note 0x01 is the boot sector, 0x02 is the first free one

[bits 16]

; Load "dh" sectors for drive "dl" at es:bx
load_disk:
    pusha
    push dx

    mov ah, 0x02

    mov al, dh

    mov ch, 0x00

    mov cl, 0x02

    mov dh, 0x00

    int 0x13
    jc .disk_error

    pop dx
    cmp al, dh
    jne .sector_error
    
    popa
    ret

    .disk_error:
        mov bx, DISK_ERR_MSG
        call print_rm

        mov dh, ah
        call print_rm_hex
        call nl_rm

        jmp .fail_loop

    .sector_error:
        mov bx, SECTOR_ERR_MSG
        call print_rm
        call nl_rm

    .fail_loop:
        jmp $

DISK_ERR_MSG: dw "Error loading from disk: ", 0
SECTOR_ERR_MSG: dw "Incorrect number of sectors read", 0

%include "boot/print_rm.asm"

When run via

qemu-system-i386 -s -usb -drive format=raw, file=\.\PHYSICALDRIVE2

it works as expected with no errors, specifically outputting

Kernel Successfully Loaded
0x2000 (the Kernel offset)
0xC6BB (what is stored at 0x2000)
0x0080 (the drive)

On real hardware, it outputs instead

Kernel Successfully Loaded
0x2000 (the Kernel offset)
0x519B (what is stored at 0x2000)
0x0080 (the drive)

i.e. it fails to load the correct data, causing call KERNEL_OFFSET to fail (it may be noted that when call load_disk is commented out, it still prints 0x519B as the data stored at 0x2000).

Any suggestions on how to resolve this difference between QEMU and real hardware would be greatly appreciated.

Share Improve this question edited Apr 12 at 22:22 Sep Roland 40k10 gold badges48 silver badges89 bronze badges asked Jan 26 at 23:30 MichaelT572MichaelT572 1902 silver badges12 bronze badges 2
  • 3 In general one thing is that you assume that the segment registers like DS and ES have zero in them. You really can't rely on the value of any register (except DL with boot drive) being what you want. This also applies to SS:SP. You set SP but SS isn't set. It appears you want to really load the segment registers DS/ES/SS with 0x0000 at the start. THe reason it may work in QEMU is that it happens to set the segment to 0x00 but on real hardware BIOSes that may not be the case. – Michael Petch Commented Jan 26 at 23:36
  • 1 You may wish to read my bootloader tips in this answer: stackoverflow/a/32705076/3857942 . If you are booting on real hardware with USB using FDD emulation you could be running into another issue that is also discussed in that answer (item 7) – Michael Petch Commented Jan 26 at 23:38
Add a comment  | 

2 Answers 2

Reset to default 2
  • Your current code works under the presumption that the segment registers DS and ES are zero. This might always be the case when running under Qemu, but in many other environments you need to setup the segment registers yourself.
  • Your current code also sets up the SP register without making sure that it properly combines with the SS segment register. Once you load the SS register it is important that you load SP in the very next instruction. The architecture makes sure this sequence cannot get interrupted, thereby assuring the SS:SP pair is perfectly valid.

So, and this is different from your self-answer, what you need is:

[ 0x7C00]
KERNEL_OFFSET equ 0x2000

[bits 16]

; Setup stack and segment registers
mov bp, 0x1000
xor ax, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax      ; Keep together, and
mov sp, bp      ; in this very order

Do note that once you start including references to the FS and GS segment registers, the computer is supposed to be at least 80386. This immediately entails that a construct like:

mov   bh, 0
mov   bl, dl
call  print_rm_hex

would better be replaced with:

movzx bx, dl
call  print_rm_hex

The load_disk function has a further error in:

mov   dh, ah
call  print_rm_hex

where the input to print_rm_hex really must go to BX, so write:

movzx bx, ah
call  print_rm_hex

RM_BOOT_MSG: dw "16-bit RM", 0
KERNEL_LOAD_MSG: dw "Kernel Successfully Loaded", 0
DISK_ERR_MSG: dw "Error loading from disk: ", 0
SECTOR_ERR_MSG: dw "Incorrect number of sectors read", 0

Don't use dw for your messages (BIOS doesn't deal with unicode). The strings themselves will get stored correctly, but each terminating zero would be wasting an extra byte. A bootsector is restricted to just 512 bytes, so every byte counts.

RM_BOOT_MSG:     db "16-bit RM", 0
KERNEL_LOAD_MSG: db "Kernel Successfully Loaded", 0
DISK_ERR_MSG:    db "Error loading from disk: ", 0
SECTOR_ERR_MSG:  db "Incorrect number of sectors read", 0

As noted by Michael Petch, this is resolved by ensuring relevant segment registers are set to 0

mov ax, 0
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745318392a4622326.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信