程序集引导加载程序未跳转到内核

标签 assembly operating-system kernel bootloader

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

为了构建并运行此代码,我在 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 作为软盘驱动器安装,并在 guest 系统上运行它。

结果是

Patrick's Bootloader Started. Floppy has been reset. Reading Kernel Sector Kernel Sector Loaded

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

这是我的代码

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

最佳答案

冒着指出显而易见的风险,您的脚本没有复制正确的文件。在某些地方您引用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

关于程序集引导加载程序未跳转到内核,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25582905/

相关文章:

c - MIPS 汇编到 C

c - C 中的系统函数调用

python - 检查路径是否以分离结束

c - 从/proc/pid/stat中的tty_nr属性获取次设备号

linux - 如何使用模块中的 ls 命令?

macos - Sierra 上的 kprintf(内核 printf)日志在哪里?

ioremap 后的内存访问非常慢

macos - 后端错误 : 32-bit absolute addressing is not supported in 64-bit mode

assembly - 单步调试时编译后的代码与汇编代码不同

assembly - MASM 汇编中的中缀到后缀转换