加载引导程序的第二阶段并启动它。

4
最近我在尝试理解引导程序的工作原理。我正在使用nasm汇编语言编写我的加载器,并通过bochs和软盘镜像进行测试。
第一阶段和第二阶段的编译二进制文件通过复制合并成一个镜像。这个镜像正是我想要的。第一阶段代码有512字节(包括魔数)且可以成功加载,第二阶段代码有512字节且在第二个扇区。
但我认为我的问题是将扇区加载到内存并跳转到它。我的代码有什么问题吗?
Stage1.asm
BITS 16
start:
    mov ax, 07C0h    ; Set up 4K stack space after this bootloader
    add ax, 288        ; (4096 + 512) / 16 bytes per paragraph
    mov ss, ax
    mov sp, 4096

    mov ax, 07C0h    ;Set data segment to where we're loaded
    mov ds, ax

    mov si,s_version
    call print_string

    ; ## Load stage2
    mov si,s_loading
    call print_string

    xor ax,ax
    xor bx,bx
    xor cx,cx
    xor dx,dx

    ;read 2nd sector
    mov ah,02h
    mov al,1    ;read 1
    mov ch,0    ;on track 0
    mov cl,2    ;2nd sector
    mov dh,0    ;head 1
    mov dl,0    ;from floppy a    

    mov bx,09C0h;destination segment
    mov es,bx

    mov bx,0    ;destination offset

    int 13h ;<-- Fails right here

    mov si,s_sector
    call print_string

    ;print number of read sectors
    add ax, 48
    mov ah, 0Eh
    int 10h
    mov al, 21
    mov ah, 0Eh
    int 10h

    ;print the sector's magicnumber (debugging purposes)
    mov al, [09C0h+511]
    int 10h

    xor ax,ax
    int 16h

    mov si,s_jumping
    call print_string

    call word 09C0h:0000h

; #### print a string from si
print_string:
    push ax
    push bx
    mov ah, 0Eh
    .repeat:
        lodsb
        cmp al, 0
        je .exit
        int 10h
    jmp .repeat
    .exit:
    pop bx
    pop ax
ret
; ****

; #### define strings    
s_version    db 'VeOS 0.0.0.1',10,13,0
s_loading    db 'Loading Stage2...',10,13,0
s_sector    db 'Loading sector...',10,13,0
s_jumping    db 'Passing control to Stage2.',10,13,0
; ****

;fillup with zeros
times 510-($-$$) db 0
;boot signature
dw 0xAA55

stage2.asm

BITS 16
start:
    mov ax, 09C0h   ; Set up 4K stack space after this bootloader
    add ax, 288     ; (4096 + 512) / 16 bytes per paragraph
    mov ss, ax
    mov sp, 4096

    mov ax, 09C0h   ;Set data segment to where we're loaded
    mov ds, ax

    mov ah, 0Eh
    mov al, 21      ;"!"
    int 10h

    mov ah, 00h
    int 16h

    jmp $


times 511-($-$$) db 0
;Magicnumber for debugging
db 0x41

我仔细搜索了一下,没有找到确切的方法来将扇区加载到RAM并跳转到它。甚至我的程序中也没有找到第二个扇区的Magicnumber。
如果只是地址计算错误,那就太好了。
更新: 当前源代码,标有死锁的行。出于纯粹的偏执狂,我将所有4个主寄存器都设置为0。
更新2: 再次更新版本。在设置寄存器和发出int 13h之间没有进行任何操作。
1个回答

3
更新:除下面的内容外,当您加载时还会覆盖堆栈!
您的堆栈位于07C0h+288:4096,即08E0h:1000h=09E0h:0000h,而您正在读取09C0h:0000和512字节前进(结束于09E0h:0000h),覆盖了堆栈。要么移动堆栈,要么读取到其他位置。请参见osdev.org的内存映射以获得灵感。
很抱歉我不知道好的逐步调试器。我只在代码中放置了一个jmp $-2指令,并使用QEMU内置调试器在适当的点上执行“info registers”。我认为Bochs可能有类似的工具。
我看到三个(2.5)问题(虽然可能还有更多):
;read 2nd sector
    mov ah,02h
    mov al,1    ;read 1
    mov ch,0    ;on track 0
    mov cl,1    ;2nd sector
    mov dl,0    ;from floppy a
    mov bx,09C0h ;destination
    mov es,bx
    int 13h
  • 您将bx留作09c0h,因此它将读取为09C0h:09C0h而不是09C0h:0000h
  • 您说您正在从第二个扇区中读取(cl = 1),但您实际上是从第一个扇区中读取!请参见Ralph Brown
  • 您没有设置dh(磁头)。在Bochs中可能可以,但我无法立即回想起除了dl被设置为驱动器编号之外保证的初始条件

这是我用于小型测试内核的引导程序(部分内容摘自osdev.org的各个部分,可能存在我的错误,请谨慎比较/复制)。 (我保留dl,因为它包含引导驱动器,所以您不必硬编码它)。

        bits 16
        org 0x7c00

Start: jmp EntryPoint

PrintString16:
        pusha
.PrintLoop:
        lodsb
        or al, al
        jz .PrintDone
        mov ah, 0xe
        int 0x10
        jmp .PrintLoop
.PrintDone:
        popa
        ret

EntryPoint:
        xor ax, ax
        mov ss, ax
        mov ds, ax
        mov sp, 0x7c00
.DiskReset:
        mov ah, 0
        int 0x13
        jc .DiskReset
        mov ax, 0x50 ; load to 0x500 linear address. It has unused space up to 0x7bff
        mov es, ax
        xor bx, bx
        mov ax, 0x023B ; count = 0x3b = 59, the maximum (while still leaving soom room for the stack and the boot sector code we're currently running)
        mov cx, 0x0002
        xor dh, dh ; leave dl intact
        int 0x13
        jnc .ReadDone
        mov si, ReadError
        call PrintString16
        jmp .DiskReset
.ReadDone:
        ;jmp 0x50:0x0  ;jump to stage 2 loaded at 0x500

        cli
        xor ax, ax
        mov ds, ax
        mov es, ax
        mov ax, 0x9000
        mov ss, ax
        mov sp, 0xffff 
        sti

        mov si, HelloMsg
        call PrintString16

        ; Disable interrupts until safely in protected mode
        cli

        ; Install GDT
        lgdt [toc]

        ; Enable A20
        mov al, 0xdd
        out 0x64, al

        mov si, GoPMode
        call PrintString16

        ; enable protected mode
        mov eax, cr0 
        or eax, 1
        mov cr0, eax

        jmp 0x8:PmodeStart      
        bits 32
PmodeStart:
        ; setup stack and datasegments
        mov ax, 0x10
        mov ds, ax
        mov es, ax
        mov fs, ax
        mov gs, ax
        mov ss, ax

        ; Setup stack at 0x90000
        mov esp, 0x90000

        ; Jump to C-code
        jmp 0x8:0x500

        ; Reboot if C-code returns
Reboot:
        mov word [0x472], 0x1234
        jmp 0x8:0xffff0


ReadError db 'Read error - retrying...', 13, 10, 0
HelloMsg db 'Loading...',0
GoPMode db 'Entering protected mode..',0
gdt_data: 
        dd 0                            ; null descriptor
        dd 0 

; gdt code:                             ; code descriptor
        dw 0FFFFh                       ; limit low
        dw 0                            ; base low
        db 0                            ; base middle
        db 10011010b                    ; access
        db 11001111b                    ; granularity
        db 0                            ; base high

; gdt data:                             ; data descriptor
        dw 0FFFFh                       ; limit low (Same as code)10:56 AM 7/8/2007
        dw 0                            ; base low
        db 0                            ; base middle
        db 10010010b                    ; access
        db 11001111b                    ; granularity
        db 0                            ; base high

end_of_gdt:
toc: 
        dw end_of_gdt - gdt_data - 1    ; limit (Size of GDT)
        dd gdt_data                     ; base of GDT


times 510 - ($-$$) db 0 ; pad to 512 bytees, will also warn if we exceed 512 bytes

        dw 0xAA55 ; boot signature

请参见上面更新后的源代码。 - user988106
你可能是出于纯粹的偏执,将所有寄存器都清零了,但在发出 int 13h 命令之前却调用了 print_string 函数。这可能会导致问题 :) - user786653
据我所知,print_string 中使用的 10h 中断的 0Eh 函数不会修改寄存器。为了更加安全,我甚至将 ax-dx 推出并在 print_string 完成后弹出它们。但仍然在 int 13h 处出现“Reading”状态的死锁。 - user988106
它至少需要参数bl(http://www.ctyme.com/intr/rb-0106.htm),我有其他来源说`bh`值被忽略,所以我认为你也需要保存和恢复该寄存器。无论如何,如果你删除那个调用,它能正常工作吗? - user786653
现在在设置int 13h的寄存器和发出它之间没有任何调用。仍然是一样的。我会再次为您更新代码。 有没有好的逐步调试器建议? - user988106
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接