从FAT软盘镜像中读取第二阶段引导程序

3
我正在尝试开发一个带有自定义引导程序的小型操作系统。我在操作系统开发方面有一些经验,但不是很多...我的问题是第一阶段的引导程序无法从磁盘加载第二阶段。这是boot.asm文件:
org 0
bits 16

jmp boot

%include "include/fat12.inc"
%include "include/io.inc"

Mem.Loader1 equ 0x00007c00
Mem.Loader1.Size equ 0x00000200
Mem.Loader1.Segment equ Mem.Loader1 >> 4
Mem.Stack.Top equ 0x00007c00

boot: jmp Mem.Loader1.Segment : .init
.init:

cli

; adjust segment registers
mov ax, cs
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax

; create stack
xor ax, ax
mov ss, ax
mov sp, Mem.Stack.Top

sti

call LoadRoot
xor ebx, ebx
mov bp, secondStage
mov si, ImageName
call LoadFile
cmp ax, 0
je secondStage
BiosPrint msgBooting
jmp $
 
msgBooting db "E", 0
ImageName db "loader bin"
 
times 510-($-$$) db 0
dw 0xAA55

secondStage:

您可能会从MonkOSBrokenthorn中识别出一些代码片段,因为我所有的知识都来自它们和OSDevWiki

引导加载程序无法找到loader.bin文件并打印出“E”。 我绝对确定LoadRootLoadFile可行,因为我已经从上一个工程中复制了它们,而那个工程运行良好,但如果需要,我可以在这里添加它们。

在使用nasm汇编文件后,我将创建一个软盘映像:

dd if=/dev/zero of=BonsOS.img bs=1024 count=1440
/sbin/mkfs.msdos BonsOS.img
mcopy -i BonsOS.img ./bin/boot/loader.bin ::/
dd if=./bin/boot/boot.bin of=BonsOS.img seek=0 count=1 conv=notrunc

最后运行。
qemu-system-x86_64 -fda BonsOS.img -m 512M -no-reboot -no-shutdown

如何解决“文件未找到”问题?
编辑:

fat12.inc:

;*******************************************************
;
;   Fat12.inc
;       FAT12 filesystem for 3-1/2 floppies
;
;   OS Development Series
;*******************************************************

%ifndef __FAT12_INC_67343546FDCC56AAB872_INCLUDED__
%define __FAT12_INC_67343546FDCC56AAB872_INCLUDED__

bits    16

%include "include/floppy16.inc"                 ; the erm.. floppy driver

%define ROOT_OFFSET 0x2e00
%define FAT_SEG 0x2c0
%define ROOT_SEG 0x2e0

;*******************************************
; LoadRoot ()
;   - Load Root Directory Table to 0x7e00
;*******************************************

LoadRoot:

    pusha                           ; store registers
    push    es

     ; compute size of root directory and store in "cx"
     
    xor     cx, cx                      ; clear registers
    xor     dx, dx
    mov     ax, 32                  ; 32 byte directory entry
    mul     WORD [bpbRootEntries]               ; total size of directory
    div     WORD [bpbBytesPerSector]            ; sectors used by directory
    xchg    ax, cx                      ; move into AX

     ; compute location of root directory and store in "ax"
     
    mov     al, BYTE [bpbNumberOfFATs]          ; number of FATs
    mul     WORD [bpbSectorsPerFAT]             ; sectors used by FATs
    add     ax, WORD [bpbReservedSectors]
    mov     WORD [datasector], ax               ; base of root directory
    add     WORD [datasector], cx

     ; read root directory into 0x7e00
 
    push    word ROOT_SEG
    pop     es
    mov     bx, 0                               ; copy root dir
    call    ReadSectors                         ; read in directory table
    pop     es
    popa                                        ; restore registers and return
    ret

;*******************************************
; LoadFAT ()
;   - Loads FAT table to 0x7c00
;
;   Parm/ ES:DI => Root Directory Table
;*******************************************

LoadFAT:

    pusha                           ; store registers
    push    es

     ; compute size of FAT and store in "cx"
     
    xor     ax, ax
    mov     al, BYTE [bpbNumberOfFATs]          ; number of FATs
    mul     WORD [bpbSectorsPerFAT]             ; sectors used by FATs
    mov     cx, ax

     ; compute location of FAT and store in "ax"

    mov     ax, WORD [bpbReservedSectors]

     ; read FAT into memory (Overwrite our bootloader at 0x7c00)

    push    word FAT_SEG
    pop     es
    xor     bx, bx
    call    ReadSectors
    pop     es
    popa                            ; restore registers and return
    ret
    
;*******************************************
; FindFile ()
;   - Search for filename in root table
;
; parm/ DS:SI => File name
; ret/ AX => File index number in directory table. -1 if error
;*******************************************

FindFile:

    push    cx                      ; store registers
    push    dx
    push    bx
    mov bx, si                      ; copy filename for later

     ; browse root directory for binary image

    mov     cx, WORD [bpbRootEntries]           ; load loop counter
    mov     di, ROOT_OFFSET                     ; locate first root entry at 1 MB mark
    cld                         ; clear direction flag

.LOOP:
    push    cx
    mov     cx, 11                  ; eleven character name. Image name is in SI
    mov si, bx                      ; image name is in BX
    push    di
     rep  cmpsb                         ; test for entry match
    pop     di
    je      .Found
    pop     cx
    add     di, 32                  ; queue next directory entry
    loop    .LOOP

.NotFound:
    pop bx                      ; restore registers and return
    pop dx
    pop cx
    mov ax, -1                      ; set error code
    ret

.Found:
    pop ax                      ; return value into AX contains entry of file
    pop bx                      ; restore registers and return
    pop dx
    pop cx
    ret

;*******************************************
; LoadFile ()
;   - Load file
; parm/ ES:SI => File to load
; parm/ EBX:BP => Buffer to load file to
; ret/ AX => -1 on error, 0 on success
; ret/ CX => number of sectors read
;*******************************************

LoadFile:

    xor ecx, ecx        ; size of file in sectors
    push    ecx

.FIND_FILE:

    push    bx          ; BX=>BP points to buffer to write to; store it for later
    push    bp
    call    FindFile        ; find our file. ES:SI contains our filename
    cmp ax, -1
    jne .LOAD_IMAGE_PRE
    pop bp
    pop bx
    pop ecx
    mov ax, -1
    ret

.LOAD_IMAGE_PRE:

    sub edi, ROOT_OFFSET
    sub eax, ROOT_OFFSET

    ; get starting cluster

    push    word ROOT_SEG       ;root segment loc
    pop es
    mov dx, WORD [es:di + 0x001A]; DI points to file entry in root directory table. Refrence the table...
    mov WORD [cluster], dx  ; file's first cluster
    pop bx          ; get location to write to so we dont screw up the stack
    pop es
    push    bx          ; store location for later again
    push    es
    call    LoadFAT

.LOAD_IMAGE:

    ; load the cluster

    mov ax, WORD [cluster]  ; cluster to read
    pop es          ; bx:bp=es:bx
    pop bx
    call    ClusterLBA
    xor cx, cx
    mov     cl, BYTE [bpbSectorsPerCluster]
    call    ReadSectors
    pop ecx
    inc ecx         ; add one more sector to counter
    push    ecx
    push    bx
    push    es
    mov ax, FAT_SEG     ;start reading from fat
    mov es, ax
    xor bx, bx

    ; get next cluster

    mov     ax, WORD [cluster]  ; identify current cluster
    mov     cx, ax          ; copy current cluster
    mov     dx, ax
    shr     dx, 0x0001      ; divide by two
    add     cx, dx          ; sum for (3/2)

    mov bx, 0           ;location of fat in memory
    add bx, cx
    mov dx, WORD [es:bx]
    test    ax, 0x0001      ; test for odd or even cluster
    jnz .ODD_CLUSTER

.EVEN_CLUSTER:

    and dx, 0000111111111111b   ; take low 12 bits
    jmp .DONE

.ODD_CLUSTER:

    shr dx, 0x0004      ; take high 12 bits

.DONE:

    mov WORD [cluster], dx
    cmp dx, 0x0ff0      ; test for end of file marker
    jb  .LOAD_IMAGE

.SUCCESS:
    pop es
    pop bx
    pop ecx
    xor ax, ax
    ret

%endif      ;__FAT12_INC_67343546FDCC56AAB872_INCLUDED__

这需要依赖于 floppy16.inc 文件:

;*******************************************************
;
;   Floppy16.inc
;       Floppy drive interface routines
;
;   OS Development Series
;*******************************************************

%ifndef __FLOPPY16_INC_67343546FDCC56AAB872_INCLUDED__
%define __FLOPPY16_INC_67343546FDCC56AAB872_INCLUDED__

bits    16

bpbOEM          db "My OS   "
bpbBytesPerSector:      DW 512
bpbSectorsPerCluster:   DB 1
bpbReservedSectors:     DW 1
bpbNumberOfFATs:    DB 2
bpbRootEntries:     DW 224
bpbTotalSectors:    DW 2880
bpbMedia:       DB 0xf0  ;; 0xF1
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   "

datasector  dw 0x0000
cluster     dw 0x0000

absoluteSector db 0x00
absoluteHead   db 0x00
absoluteTrack  db 0x00

;************************************************;
; Convert CHS to LBA
; LBA = (cluster - 2) * sectors per cluster
;************************************************;

ClusterLBA:
          sub     ax, 0x0002                          ; zero base cluster number
          xor     cx, cx
          mov     cl, BYTE [bpbSectorsPerCluster]     ; convert byte to word
          mul     cx
          add     ax, WORD [datasector]               ; base data sector
          ret

;************************************************;
; Convert LBA to CHS
; AX=>LBA Address to convert
;
; absolute sector = (logical sector / sectors per track) + 1
; absolute head   = (logical sector / sectors per track) MOD number of heads
; absolute track  = logical sector / (sectors per track * number of heads)
;
;************************************************;

LBACHS:
          xor     dx, dx                              ; prepare dx:ax for operation
          div     WORD [bpbSectorsPerTrack]           ; calculate
          inc     dl                                  ; adjust for sector 0
          mov     BYTE [absoluteSector], dl
          xor     dx, dx                              ; prepare dx:ax for operation
          div     WORD [bpbHeadsPerCylinder]          ; calculate
          mov     BYTE [absoluteHead], dl
          mov     BYTE [absoluteTrack], al
          ret


;************************************************;
; Reads a series of sectors
; CX=>Number of sectors to read
; AX=>Starting sector
; ES:EBX=>Buffer to read to
;************************************************;

ReadSectors:
     .MAIN
          mov     di, 0x0005                          ; five retries for error
     .SECTORLOOP
          push    ax
          push    bx
          push    cx
          call    LBACHS                              ; convert starting sector to CHS
          mov     ah, 0x02                            ; BIOS read sector
          mov     al, 0x01                            ; read one sector
          mov     ch, BYTE [absoluteTrack]            ; track
          mov     cl, BYTE [absoluteSector]           ; sector
          mov     dh, BYTE [absoluteHead]             ; head
          mov     dl, BYTE [bsDriveNumber]            ; drive
          int     0x13                                ; invoke BIOS
          jnc     .SUCCESS                            ; test for read error
          xor     ax, ax                              ; BIOS reset disk
          int     0x13                                ; invoke BIOS
          dec     di                                  ; decrement error counter
          pop     cx
          pop     bx
          pop     ax
          jnz     .SECTORLOOP                         ; attempt to read again
          int     0x18
     .SUCCESS
          pop     cx
          pop     bx
          pop     ax
          add     bx, WORD [bpbBytesPerSector]        ; queue next buffer
          inc     ax                                  ; queue next sector
          loop    .MAIN                               ; read next sector
          ret

%endif      ;__FLOPPY16_INC_67343546FDCC56AAB872_INCLUDED__

这两个文件不是我的代码,它们来自Brokenthorn的解释。
最后是io.inc:
;************************;
; Parameters:            ;
;   si => string pointer ;
;************************;
bits 16
%macro BiosPrint 1
    mov si, word %1
    call _BiosPrint
%endmacro

_BiosPrint:
    pusha
    .loop:
        lodsb
        or al, al
        jz .done
        mov ah, 0x0E
        int 0x10
        jmp .loop
    .done:
    popa
    ret

这是完整项目的仓库链接:https://github.com/Bonfra04/BonsOS

@MichaelPetch 感谢您的反馈,我刚刚编辑了帖子,提供了最小的代码以便复现。 - Bonfra
@MichaelPetch 我不太确定我点击了什么,我对这个网站还比较新.. 无论如何,关于你的评论,我回复说“只包含一个简单的打印函数和生物信息;刚刚添加的。” - Bonfra
@MichaelPetch 在 boot.asm 的开头几行中,我已经提供了 mem.inc 的重要部分(Mem.Loader1.Segment, Mem.Stack.Top)。 - Bonfra
@MichaelPetch 你有找到什么吗?我在帖子中添加了一个项目的GitHub存储库链接。 - Bonfra
1个回答

2

使用像BOCHS这样的调试器

我强烈建议使用BOCHS来调试实模式代码,特别是引导程序和内核开发的早期阶段。在*nix类型的系统中,你可以这样启动BOCHS:

bochs -f /dev/null 'floppya: 1_44=BonsOS.img, status=inserted' 'boot: a'

然后,在引导加载程序的开头设置断点,并使用以下命令开始执行:
b 0x7c00
c

BOCHS文档中可以找到有关使用BOCHS进行步进、跟踪、显示段寄存器、显示通用寄存器等操作的说明。

问题

看起来在某个时候,您更改了代码以不同的方式处理段,并引入了一些错误。同时,您正在搜索错误的文件名。FAT12文件名都是大写字母,并且长度为11个字节(文件名的8个字符填充空格,后跟3个字母的扩展名)。您的代码正在寻找:

ImageName db "loader bin"    

当它应该是:
ImageName db "LOADER  BIN"  ; 2 spaces between LOADER and BIN

调用LoadFile时,您需要这样设置调用方式:
xor ebx, ebx
mov bp, secondStage
mov si, ImageName
call LoadFile

“BX:BP” 应该是 stage2 被读入内存的段偏移地址。它应该是:
mov bx, Mem.Loader1.Segment
mov bp, secondStage
mov si, ImageName
call LoadFile

似乎您修改了 FindFile 以使用 ROOT_OFFSET,而在 LoadFatLoadRoot 中则使用了 ROOT_SEG。由于您的代码最终出现了错误的 DS:SIES:DI 值用于 CMPSB 指令,因此会从错误的内存地址进行字符串比较。我修改了您的 FindFile 代码以使用 ROOT_SEG,结果如下所示:

FindFile:

    push    es                           ; Save ES
    push    cx                           ; store registers
    push    dx
    push    bx
    mov bx, si                           ; copy filename for later

     ; browse root directory for binary image
    mov ax, ROOT_SEG                     ; Set ES to ROOT_SEG not 0
    mov es, ax

    mov     cx, WORD [bpbRootEntries]    ; load loop counter
    xor     di, di                       ; Start at 0 offset from ES (ROOT_SEG)
; Remove   mov     di, ROOT_OFFSET       ; locate first root entry
    cld                                  ; clear direction flag

.LOOP:
    push    cx
    mov     cx, 11                       ; eleven character name. Image name is in SI
    mov si, bx                           ; image name is in BX
    push    di
     rep  cmpsb                          ; test for entry match
    pop     di
    je      .Found
    pop     cx
    add     di, 32                       ; queue next directory entry
    loop    .LOOP

.NotFound:
    pop bx                               ; restore registers and return
    pop dx
    pop cx
    pop es                               ; Restore ES
    mov ax, -1                           ; set error code
    ret

.Found:
    pop ax                               ; return value into AX contains entry of file
    pop bx                               ; restore registers and return
    pop dx
    pop cx
    pop es                               ; Restore ES
    ret

然后,您需要删除调整 EDI 和 EAX 的 2 行代码,这些代码位于 .LOAD_IMAGE_PRE 的开头,因此它应该以以下方式开始:
.LOAD_IMAGE_PRE:

    ; get starting cluster

    push    word ROOT_SEG                ; root segment loc

你没有提供一个 loader.asm 文件,所以我用这个作为测试的示例:
org 0x200
bits 16

jmp start

%include "include/io.inc" 

start:
    BiosPrint hello
    jmp $

hello: db "Hello, world!", 0

我使用了org 0x200,因为你在引导加载程序中使用了近跳转来到这里,而你的引导加载程序使用的是CS 0x07c0。这意味着stage2所需的偏移量(ORG)仍相对于0x07c0,因此我使用0x200。0x07c0:0x0200是物理地址0x07e00,即引导加载程序后面的物理地址。

当我在BOCHS中运行它时,得到以下输出:

enter image description here


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