【问题标题】:Assembly Bootloader Not Jumping to Kernel程序集引导加载程序不跳转到内核
【发布时间】:2014-10-24 07:53:43
【问题描述】:

出于学习目的,我正在尝试用汇编语言编写引导加载程序和内核。当我组装我的代码并用它启动虚拟机时,引导加载程序似乎工作正常,但内核永远不会启动。我在想我跳到了错误的指令,但不知道我需要做哪些更正来解决我的问题。我也有可能试图错误地制作软盘,但我认为这不是问题。当我在十六进制编辑器中查看图像文件时,它们似乎已正确附加。另一个原因可能是从软盘中读取了错误的扇区。我正在尝试让内核在引导加载程序之后立即进入该扇区并从中读取。

要构建和运行此代码,我在 Windows Vista x64 中执行以下操作:

nasm bootloader_2.asm -f bin -o bootloader_2.bin 
nasm kernel_2.asm -f bin -o kernel.bin 
partcopy bootloader_2.bin bootloader_2.img 0d 511d 
partcopy kernel_2.bin kernel_2.img 0d 511d 
copy bootloader.img /b + kernel.img POS_2.img

然后我使用 Oracle VM Virtual Box 将 POS_2.img 挂载为软盘驱动器,并在来宾系统上运行它。

结果是

Patrick 的引导加载程序已启动。 软盘已重置。 读取内核扇区 内核扇区加载

。而且内核永远不会启动。

这是我的代码

bootloader_2.asm

bits 16
org 0x7C00
boot: jmp loader

; OEM Parameter block / BIOS Parameter block (wtf is this for?)
times 0Bh-$+boot DB 0
bpbBytesPerSector:        DW 512
bpbSectorsPerCluster:    DB 1
bpbReservedSectors:        DW 1
bpbNumberOfFATs:        DB 2
bpbRootEntries:            DW 224
bpbTotalSectors:        DW 2880
bpbMedia:                DB 0xF0
bpbSectorsPerFAT:        DW 9
bpbSectorsPerTrack:        DW 18
bpbHeadsPerCylinder:    DW 2
bpbHiddenSectors:        DD 0
bpbTotalSectorsBig:        DD 0
bsDriveNumber:            DB 0
bsUnused:                DB 0
bsExtBootSignature:        DB 0x29
bsSerialNumber:            DD 0xa0a1a2a3
bsVolumeLabel:            DB "MOS FLOPPY"
bsFileSystem:            DB "FAT12"
; END PARAMETER BLOCK

; ----- Variables -----

started db "Patrick's Bootloader Started...", 0x0D, 0x0A, 0
floppy_reset_done db "Floppy has been reset.", 0x0D, 0x0A, 0
loading_msg db "Reading Kernel Sector", 0x0D, 0x0A, 0
loading_sucess db "Kernel Sector Loaded", 0x0D, 0x0A, 0
done db "Bootloader Done.", 0x0D, 0x0A, 0

; ----- End Variables -----

; ----- Calls -----

reset_floppy:
    mov ah, 0
    mov dl, 0
    int 0x13
    jc reset_floppy
    mov si, floppy_reset_done
    call print_string
    ret

read_kernel:
    mov si, loading_msg
    call print_string
    mov si, 0x0

    mov ax, 0x1000    ; setting up the address to read into
    mov es, ax        ; moving the value in to es
    xor bx, bx        ; clearing bx
    mov ah, 0x02    ; floppy function
    mov al, 1        ; read 1 sector
    mov ch, 1        ; track
    mov cl, 2        ; sector to read
    mov dh, 0        ; head number
    mov dl, 0        ; drive number

    int 0x13        ; BIOS Interrupt Call

    jc read_kernel

    mov si, loading_sucess
    call print_string

    ret

print_string:
    lodsb
    or al, al
    jz .done
    mov ah, 0x0E
    int 0x10
    jmp print_string
.done:
    ret

; input is ax, cx is destroyed    
print_hex:
    mov cx, 4
    .next_digit:
        push cx
        mov cl, 4
        rol ax, cl
        push ax
        and al, 0x0F
        add al, '0'
        cmp al, '9'
        jle .not_a_leter
        add al, 'A'-'9'-1
    .not_a_leter:
        mov ah, 0x0E
        int 0x10
        pop ax
        pop cx
        loop .next_digit
    ret

; ----- End of Calls -----

; ===== Bootloader Main =====

loader:
    mov si, started
    call print_string
    call reset_floppy
    call read_kernel

    jmp 0x1000:0x0

    mov si, done        ; never reached. Intentional for debugging
    call print_string    ; these lines failure to produce a result tell us that the jmp was attempted

; ===== End of Bootloader Main =====

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

kernel_2.asm

kernel:
    jmp k_main

welcome_msg db "Welcome to Patrick's Operating System!", 0x0D, 0x0A, 0

print_string:
    lodsb
    or al, al
    jz .done
    mov ah, 0x0E
    int 0x10
    jmp print_string
.done:
    ret

k_main:

    mov si, welcome_msg
    call print_string

    .k_main_loop:

    jmp .k_main_loop

cli
hlt

times 512-($-$$) db 0

【问题讨论】:

  • 您是否尝试在跳转到内核之前插入断点以确保正确的数据已加载到正确的地址?
  • 我尝试了其他可能提供更多信息的方法。我重写了引导加载程序以使其更加模块化,并在我进行 int 0x13 调用后包含一个打印出 AH 寄存器的调用。这样做还会在中断调用之后,但在跳转到内核之前,在混合中添加更多指令。尝试读取内核扇区后出现 0Eh 错误。你知道这意味着什么吗?我确实改变了为此构建它的方式。我正在填充内核文件,并通过一个包含引导加载程序和内核的文件使用 NASM,然后以 1023d 的限制进行部分复制。
  • 在开始编写操作系统之前,您可能需要熟悉一些调试工具。

标签: assembly operating-system kernel bootloader


【解决方案1】:

冒着指出显而易见的风险,您的脚本没有复制正确的文件。在某些地方您引用kernel.bin,而在其他地方引用kernel_2.bin。试试这个:

nasm bootloader_2.asm -f bin -o bootloader_2.bin 
nasm kernel_2.asm -f bin -o kernel_2.bin 
partcopy bootloader_2.bin bootloader_2.img 0d 511d 
partcopy kernel_2.bin kernel_2.img 0d 511d 
copy bootloader.img /b + kernel_2.img POS_2.img

您可能还会发现使用dd 代替这些东西更容易。它作为Cygwin 的一部分提供,具有可以写入文件或物理设备(如真正的软盘)的优势。

另外,您应该考虑使用Makefile 而不是脚本。这将有助于您进一步扩展项目。

也就是说,代码存在三个基本问题。首先,要加载的下一个扇区在柱面 0,而不是柱面 1。其次,代码不能简单地将ret 安全地传递给内核——你必须明确地跳转到那里。这部分代码将如下所示:

read_kernel:
    mov si, loading_msg
    call print_string
    mov si, 0x0

    mov ax, 0x1000   ; setting up the address to read into
    mov es, ax       ; moving the value in to es
    xor bx, bx       ; clearing bx
    mov ah, 0x02     ; floppy function
    mov al, 1        ; read 1 sector
    mov ch, 0        ; cylinder
    mov cl, 2        ; sector to read
    mov dh, 0        ; head number
    mov dl, 0        ; drive number

    int 0x13         ; BIOS Interrupt Call

    jc read_kernel
    push es          ; either push the address and retf or use far jmp
    push bx          ;

    mov si, loading_sucess
    call print_string

    ; jmp  0x1000:0  ; alternative to push/retf is simple long jump

    retf

第三个问题是内核。您尚未更改 DS 寄存器以指向新的偏移量,因此即使您访问该代码,它也不会打印您想要的字符串。将该代码更改为如下所示:

k_main:
    push cs          ; save the cs reg
    pop  ds          ; use as ds also

    mov si, welcome_msg
    call print_string

    .k_main_loop:

    jmp .k_main_loop

【讨论】:

  • 那些只是拼写错误。
  • 错字在编程中很重要。
  • 他们是帖子中的拼写错误,而不是执行。在更友好的说明中,非常感谢您回答我的问题。我还需要一段时间才能试用您推荐的更改,但我一定会在明天进行研究。我目前正在虚拟机上安装 Debian 以切换到更友好的开发环境。
  • 我很兴奋,等不及了。我根据您的建议重建了我的代码,现在一切正常!再次感谢!
猜你喜欢
  • 1970-01-01
  • 2015-12-18
  • 2014-04-23
  • 2015-01-19
  • 2020-09-10
  • 1970-01-01
  • 2017-11-03
  • 1970-01-01
  • 2019-10-24
相关资源
最近更新 更多