【问题标题】:Bootloader - Loading assembly kernel, problems with memory addresses and copying dataBootloader - 加载程序集内核、内存地址问题和复制数据
【发布时间】:2015-02-26 04:17:24
【问题描述】:

我正在尝试完成我的操作系统项目的第一部分。这部分涉及通过模拟器 qemu 运行引导加载程序程序集文件,并让该引导加载程序加载并执行任意数量(希望超过 512 字节)的内核。

我在完成此操作时遇到了问题,我无法准确指出,但我可能已将其缩小到两件事。

第一,我缺乏对内存地址的理解,以及使用堆栈来加载我的内核并执行它。

二,我将我的两个文件(引导加载程序和内核文件)错误地复制到软盘映像中。

这是我第一部分的代码,即引导加载程序。 boot.asm

;;;
;;; Header information
;;; 

BITS 16

ORG 0

START:  jmp MAIN

;;;
;;; Parameters
;;; 

SectorsPerTrack     dw  18
HeadsPerTrack       dw  2
BytesPerSector      dw  512
DriveNumber         db  0

;;;
;;; Variables
;;; 

absSector           db  0x00
absHead             db  0x00
absTrack            db  0x00

;;;
;;; Print function
;;; Input:
;;; - SI => Null terminated string
;;; Output:
;;; - None
;;; 

PRINT:
    lodsb
    or al, al
    jz PRINTDONE
    int 10h
    jmp PRINT
PRINTDONE:
    ret

;;;
;;; LBA to CHS
;;; Input:
;;; - AX => LBA address
;;; - SectorsPerTrack => Sectors per track
;;; - HeadsPerTrack => Heads per track
;;; Output:
;;; - absSector => CHS sector address
;;; - absHead => CHS head address
;;; - absTrack => CHS track address
;;; 

LBACHS:
    xor dx, dx
    div WORD [SectorsPerTrack]
    inc dl
    mov BYTE [absSector], dl
    xor dx, dx
    div WORD [HeadsPerTrack]
    mov BYTE [absHead], dl
    mov BYTE [absTrack], al
    ret

;;;
;;; Read sectors
;;; Input:
;;; - CX => Number of sectors to read
;;; - AX => LBA address to start from
;;; Output:
;;; - ES:BX => Loaded sector address:offset
;;; 

READSECTORS:
READSECTORSMAIN:    
          mov     di, 0x0005
READSECTORSLOOP:    
          push    ax
          push    bx
          push    cx
          call    LBACHS
          mov     ah, 0x02
          mov     al, 0x01
          mov     ch, BYTE [absTrack]
          mov     cl, BYTE [absSector]
          mov     dh, BYTE [absHead]
          mov     dl, BYTE [DriveNumber]
          int     0x13
          jnc     READSECTORSDONE
          xor     ax, ax
          int     0x13
          dec     di
          pop     cx
          pop     bx
          pop     ax
          jnz     READSECTORSLOOP
          int     0x18
READSECTORSDONE:    
          pop     cx
          pop     bx
          pop     ax
          add     bx, WORD [BytesPerSector]
          inc     ax
          loop    READSECTORSMAIN
          ret

;;;
;;; Main section
;;; 

MAIN:
    cli                 ; Move registers for offset of BIOS 0x07C0 load point
    mov ax, 0x07C0      ; OFFSET
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov ax, 0x0000      ; Initialize the stack
    mov ss, ax
    mov sp, 0xFFFF
    sti

    mov ax, 0x01        ; LBA number 1 for sector number 2
    mov cx, 0x01        ; Read one sector from the floppy disk

    call READSECTORS    ; Call the read sectors function

    jmp [es:bx]         ; Address ES offset BX returned from read sectors

;;;
;;; Footer information
;;; 

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

这是我的第二部分代码,内核。 kernel.asm

BITS 16

ORG 0

START:  jmp MAIN

message db 'Kernel Loaded', 13, 10, 0

PRINT:
    lodsb
    or al, al
    jz PRINTDONE
    int 10h
    jmp PRINT
PRINTDONE:
    ret

MAIN:
    mov si, message
    call PRINT

    jmp $

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

这是我在 Ubuntu Desktop x64 上运行的编译、复制和模拟文件及其二进制文件的方式。

nasm -f bin -o boot.bin boot.asm
nasm -f bin -o kernel.bin kernel.asm
cat kernel.bin >> boot.bin
dd status=noxfer conv=notrunc if=boot.bin of=floppy.fda
qemu-system-x86_64 -fda floppy.fda

如果您尝试运行、编译和模拟它。你会看到内核永远不会被加载,也不会打印出它应该加载的内容。

我不知道除此之外该怎么做,我尝试研究如何将第二阶段引导加载程序正确加载到软盘映像中,但未能成功。在从引导加载程序加载内核时我可能做错了什么,我也遇到了障碍。

如果您有任何建议或修复,我们将不胜感激。另外,如果我的代码中有任何错误,或者您需要澄清任何内容以便更好地帮助我,请不要犹豫。

编辑

如果您需要参考,这里是我用来创建此代码的教程。

http://www.brokenthorn.com/Resources/

http://www.osdever.net/tutorials/view/lba-to-chs

http://www.osdever.net/tutorials/view/loading-sectors

【问题讨论】:

    标签: assembly kernel bootloader


    【解决方案1】:

    int 13/02 需要 es:bx 作为输入,但您只设置了 esbx 未初始化。您应该将其设置为要加载数据的位置,注意不要覆盖您自己的引导扇区。因此,mov bx, 512 听起来是个好主意。

    另一个问题是您的READSECTORS 返回bx 的最终值,即下一个可用地址而不是原始加载地址。

    另外,jmp [es:bx] 不会跳转到es:bx,而是会从该地址获取一个指针并跳转到该地址指向的任何地方。您还需要考虑您的内核也使用了org 0,因此您应该使用0 的偏移量进行远跳。如果您在引导扇区之后加载内核,这意味着jmp 0x7e0:0 应该可以解决问题。刚刚发现您的内核也希望设置ds

    PS:你应该设置一个调试环境,否则你将很难修复问题。


    这是一个工作版本:

    ;;;
    ;;; Header information
    ;;;
    
    BITS 16
    
    ORG 0
    
    START:  jmp MAIN
    
    ;;;
    ;;; Parameters
    ;;;
    
    SectorsPerTrack     dw  18
    HeadsPerTrack       dw  2
    BytesPerSector      dw  512
    DriveNumber         db  0
    
    ;;;
    ;;; Variables
    ;;;
    
    absSector           db  0x00
    absHead             db  0x00
    absTrack            db  0x00
    
    ;;;
    ;;; Print function
    ;;; Input:
    ;;; - SI => Null terminated string
    ;;; Output:
    ;;; - None
    ;;;
    
    PRINT:
        lodsb
        or al, al
        jz PRINTDONE
        mov ah, 0eh
        int 10h
        jmp PRINT
    PRINTDONE:
        ret
    
    ;;;
    ;;; LBA to CHS
    ;;; Input:
    ;;; - AX => LBA address
    ;;; - SectorsPerTrack => Sectors per track
    ;;; - HeadsPerTrack => Heads per track
    ;;; Output:
    ;;; - absSector => CHS sector address
    ;;; - absHead => CHS head address
    ;;; - absTrack => CHS track address
    ;;;
    
    LBACHS:
        xor dx, dx
        div WORD [SectorsPerTrack]
        inc dl
        mov BYTE [absSector], dl
        xor dx, dx
        div WORD [HeadsPerTrack]
        mov BYTE [absHead], dl
        mov BYTE [absTrack], al
        ret
    
    ;;;
    ;;; Read sectors
    ;;; Input:
    ;;; - CX => Number of sectors to read
    ;;; - AX => LBA address to start from
    ;;; Output:
    ;;; - ES:BX => Loaded sector address:offset
    ;;;
    
    READSECTORS:
    READSECTORSMAIN:
              mov     di, 0x0005
    READSECTORSLOOP:
              push    ax
              push    bx
              push    cx
              call    LBACHS
              mov     ah, 0x02
              mov     al, 0x01
              mov     ch, BYTE [absTrack]
              mov     cl, BYTE [absSector]
              mov     dh, BYTE [absHead]
              mov     dl, BYTE [DriveNumber]
              int     0x13
              jnc     READSECTORSDONE
              xor     ax, ax
              int     0x13
              dec     di
              pop     cx
              pop     bx
              pop     ax
              jnz     READSECTORSLOOP
              int     0x18
    READSECTORSDONE:
              pop     cx
              pop     bx
              pop     ax
              add     bx, WORD [BytesPerSector]
              inc     ax
              loop    READSECTORSMAIN
              ret
    
    ;;;
    ;;; Main section
    ;;;
    
    MAIN:
        cli                 ; Move registers for offset of BIOS 0x07C0 load point
        mov ax, 0x07C0      ; OFFSET
        mov ds, ax
        mov es, ax
        mov fs, ax
        mov gs, ax
    
        mov ax, 0x0000      ; Initialize the stack
        mov ss, ax
        mov sp, 0xFFFF
        sti
    
        mov ax, 0x01        ; LBA number 1 for sector number 2
        mov cx, 0x01        ; Read one sector from the floppy disk
        mov bx, 0x200
    
        call READSECTORS    ; Call the read sectors function
    
        jmp 0x7e0:0
    
    ;;;
    ;;; Footer information
    ;;;
    
    times 510-($-$$) db 0
    dw 0xAA55
    

    内核:

    BITS 16
    
    ORG 0
    
    START:  jmp MAIN
    
    message db 'Kernel Loaded', 13, 10, 0
    
    PRINT:
        lodsb
        or al, al
        jz PRINTDONE
        mov ah, 0eh
        int 10h
        jmp PRINT
    PRINTDONE:
        ret
    
    MAIN:
        mov ax, cs
        mov ds, ax
        mov si, message
        call PRINT
    
        jmp $
    
    times 512-($-$$) db 0
    

    【讨论】:

    • 非常感谢!这完全有效,但有一个问题。我还需要打电话给 READSECTORS 吗?或者,那现在什么都不做?
    • 非常感谢,你跳转到的地址怎么是 0x7e0:0。如果引导加载程序的 BIOS 源是 0x0000:0x7C00,那么下一个扇区不是在 0x0000:0x7E00 吗?
    • 是的。这是相同的物理地址,但使用了 0 的偏移量,因为您的代码需要这样。
    【解决方案2】:

    示例代码使用 0 的 org,它可能应该是 7c00h 的 org。 PC 在十六进制 0000:7c00 处加载分区或引导扇区。在 Microsoft 分区扇区的情况下,代码将自身向下移动到十六进制 0000:0600,跳转到 0000:0600 之后的某个点以继续,并最终在十六进制 0000:7c00 处加载引导扇区。

    另外,您使用的 nasm 版本是否生成 16 位实模式代码?

    【讨论】:

    • 我知道原点应该是 0x7C00,你可以用我的原点 0 代替它,它仍然不会像预期的那样运行。我在编辑中用“OFFSET”标记了一个代码部分,以向您展示我在哪里通过向上移动寄存器来纠正 0x7C00 的偏移量。至于另一个问题,我几乎肯定我的 nasm 版本正在生成 16 位实模式代码。我使用 Ubuntu 的“apt-get”存储库(sudo apt-get install nasm)安装了它,我看到的每个教程都使用我相同的编译行。我还在新编辑中添加了用于创建此代码的教程。
    • 汇编器和链接器是否生成 .COM 或其他形式的二进制文件,而不是带有段信息的可执行文件(如 .EXE)?如果你在Windows(32位)上运行,你可以打开一个dos控制台窗口并使用debug读取偏移0100h处的二进制文件,然后使用“u”命令查看代码。
    • 不,除了 .asm 文件中的 .bin 文件之外,它不会生成任何文件,然后将内核 .bin 文件的内容添加到 boot.bin 文件中,然后模拟 boot.bin文件作为软盘映像。我在 Ubuntu Desktop x64 上运行,而不是 Windows x32。
    • ah 设置为PRINT 例程中的某个值可能是个不错的主意...
    猜你喜欢
    • 1970-01-01
    • 2012-12-06
    • 2019-07-24
    • 1970-01-01
    • 2016-02-08
    • 2013-03-10
    • 2011-06-30
    • 1970-01-01
    • 2012-02-12
    相关资源
    最近更新 更多