【问题标题】:Reading second stage bootloader from FAT floppy image从 FAT 软盘映像读取第二阶段引导加载程序
【发布时间】:2020-10-21 06:54:37
【问题描述】:

我正在尝试开发一个带有自定义引导加载程序的小型操作系统。我在 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:

您可能会从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

EDIT2 这是组织的完整项目的 repo:https://github.com/Bonfra04/BonsOS

【问题讨论】:

  • @MichaelPetch 感谢您的反馈,我刚刚编辑了帖子,提供了最少的代码来重现
  • @MichaelPetch 不太确定我点击了什么我对这个网站有点陌生.. 无论如何我删除了你的评论(?)我回答“只包含一个简单的带有 bios 的打印功能;刚刚添加"
  • @MichaelPetch 在 boot.asm 的第一行中,我提供了 mem.inc 的重要部分(Mem.Loader1.Segment,Mem.Stack.Top)
  • @MichaelPetch 你找到什么了吗?我在帖子中添加了该项目的 github 存储库的链接

标签: assembly x86 bootloader osdev fat


【解决方案1】:

使用像 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 被读入内存的段:偏移地址。应该是:

mov bx, Mem.Loader1.Segment
mov bp, secondStage
mov si, ImageName
call LoadFile

您似乎将FindFile 修改为使用ROOT_OFFSET,而LoadFatLoadRoot 使用ROOT_SEG。您的代码最终会为 CMPSB 指令提供不正确的 DS:SIES: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 中运行它时,我得到以下输出:

【讨论】:

    猜你喜欢
    • 2013-05-05
    • 2016-03-22
    • 2019-08-24
    • 2023-02-21
    • 2019-07-08
    • 2011-09-30
    • 2011-12-04
    • 2016-02-14
    • 2014-07-16
    相关资源
    最近更新 更多