assembly - 从 FAT 软盘镜像读取第二阶段引导加载程序

标签 assembly x86 bootloader osdev fat

我正在尝试使用自定义引导加载程序开发一个小型操作系统。我在 OSDEV 方面有一点经验,但没有那么多......我的问题是第一阶段引导加载程序没有从磁盘加载秒数。这是 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:
您可能会认出来自 MonkOS 的一些代码。和 Brokenthorn因为我所有的知识都来自他们和 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
编辑2
这是组织的完整项目的存储库:https://github.com/Bonfra04/BonsOS

最佳答案

使用像 BOCHS 这样的调试器
我强烈建议使用 BOCHS 来调试实模式代码,尤其是引导加载程序和内核开发的早期阶段。在 *nix 类型系统上,您可以使用以下命令启动 BOCHS:

bochs -f /dev/null 'floppya: 1_44=BonsOS.img, status=inserted' 'boot: a'
然后在引导加载程序的开始处设置一个断点并开始执行:
b 0x7c00
c
使用BOCHS进行步进的说明;追踪;显示段寄存器;显示通用寄存器等可以在 BOCHS documentation 中找到。 .

问题
似乎在某些时候您更改了代码以不同方式处理段并引入了一些错误。同样,您正在搜索错误的文件名。 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 被读入内存的segment:offset 地址。它应该是:
mov bx, Mem.Loader1.Segment
mov bp, secondStage
mov si, ImageName
call LoadFile
好像你修改了FindFile使用 ROOT_OFFSETLoadFatLoadRoot您使用 ROOT_SEG .您的代码最终会导致 CMPSB 的 DS:SI 和 ES:DI 值不正确指令,因此您最终会从错误的内存地址进行字符串比较。我修改了你的 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
然后需要把.LOAD_IMAGE_PRE开头的调整EDI和EAX的2行去掉所以它应该从:
.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因为您使用了从引导加载程序的接近跳转来达到此目的,并且引导加载程序使用的是 0x07c0 的 CS。这意味着 stage2 所需的偏移量 (ORG) 仍然相对于 0x07c0,这就是我使用 0x200 的原因。 0x07c0:0x0200 是物理地址 0x07e00,它是引导加载程序之后的物理地址。
当我在 BOCHS 中运行它时,我得到以下输出:
enter image description here

关于assembly - 从 FAT 软盘镜像读取第二阶段引导加载程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62666445/

相关文章:

c - 图形驱动程序 "hello world"示例?

assembly - 什么是 K3D x86 ISA 扩展?

assembly - 如何通过JMP在MBR中重定位代码?

assembly - NASM 中的 "push BYTE 0x80"和 "warning: signed byte value exceeds bounds"

linux - 可以在一台双核计算机上结合 Linux(一个核心)和准系统固件(第二个核心)吗?

assembly - X86:保护模式、GDT、IDT

assembly - DosBox如何修复字符属性?

assembly - 为什么80x87指令集采用 "stack-based"设计?

linux-kernel - 获取通过引导加载程序 (grub) 传递的内核参数

ARM CPU 模式 SVC 指令