引导加载程序未加载内核

Bootloader is Not loading the kernel

我正在开发我的操作系统。

我在启动 OS 时出错。

错误是:

KERNEL.BIN not found!

代码如下:

Boot.asm

; The Aqua_Seven_OS Operating System bootloader
; ==================================================================


    BITS 16

    jmp short bootloader_start  ; Jump past disk description section
    nop             ; Pad out before disk description


; ------------------------------------------------------------------
; Disk description table, to make it a valid floppy
; Note: some of these values are hard-coded in the source!
; Values are those used by IBM for 1.44 MB, 3.5" diskette

OEMLabel        db "MIKEBOOT"   ; Disk label
BytesPerSector      dw 512      ; Bytes per sector
SectorsPerCluster   db 1        ; Sectors per cluster
ReservedForBoot     dw 1        ; Reserved sectors for boot record
NumberOfFats        db 2        ; Number of copies of the FAT
RootDirEntries      dw 224      ; Number of entries in root dir
                    ; (224 * 32 = 7168 = 14 sectors to read)
LogicalSectors      dw 2880     ; Number of logical sectors
MediumByte      db 0F0h     ; Medium descriptor byte
SectorsPerFat       dw 9        ; Sectors per FAT
SectorsPerTrack     dw 18       ; Sectors per track (36/cylinder)
Sides           dw 2        ; Number of sides/heads
HiddenSectors       dd 0        ; Number of hidden sectors
LargeSectors        dd 0        ; Number of LBA sectors
DriveNo         dw 0        ; Drive No: 0
Signature       db 41       ; Drive signature: 41 for floppy
VolumeID        dd 00000000h    ; Volume ID: any number
VolumeLabel     db "MIKEOS     "; Volume Label: any 11 chars
FileSystem      db "FAT12   "   ; File system type: don't change!


; ------------------------------------------------------------------
; Main bootloader code

bootloader_start:
    mov ax, 07C0h           ; Set up 4K of stack space above buffer
    add ax, 544         ; 8k buffer = 512 paragraphs + 32 paragraphs (loader)
    cli             ; Disable interrupts while changing stack
    mov ss, ax
    mov sp, 4096
    sti             ; Restore interrupts

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

    ; NOTE: A few early BIOSes are reported to improperly set DL

    cmp dl, 0
    je no_change
    mov [bootdev], dl       ; Save boot device number
    mov ah, 8           ; Get drive parameters
    int 13h
    jc fatal_disk_error
    and cx, 3Fh         ; Maximum sector number
    mov [SectorsPerTrack], cx   ; Sector numbers start at 1
    movzx dx, dh            ; Maximum head number
    add dx, 1           ; Head numbers start at 0 - add 1 for total
    mov [Sides], dx

no_change:
    mov eax, 0          ; Needed for some older BIOSes


; First, we need to load the root directory from the disk. Technical details:
; Start of root = ReservedForBoot + NumberOfFats * SectorsPerFat = logical 19
; Number of root = RootDirEntries * 32 bytes/entry / 512 bytes/sector = 14
; Start of user data = (start of root) + (number of root) = logical 33

floppy_ok:              ; Ready to read first block of data
    mov ax, 19          ; Root dir starts at logical sector 19
    call l2hts

    mov si, buffer          ; Set ES:BX to point to our buffer (see end of code)
    mov bx, ds
    mov es, bx
    mov bx, si

    mov ah, 2           ; Params for int 13h: read floppy sectors
    mov al, 14          ; And read 14 of them

    pusha               ; Prepare to enter loop


read_root_dir:
    popa                ; In case registers are altered by int 13h
    pusha

    stc             ; A few BIOSes do not set properly on error
    int 13h             ; Read sectors using BIOS

    jnc search_dir          ; If read went OK, skip ahead
    call reset_floppy       ; Otherwise, reset floppy controller and try again
    jnc read_root_dir       ; Floppy reset OK?

    jmp reboot          ; If not, fatal double error


search_dir:
    popa

    mov ax, ds          ; Root dir is now in [buffer]
    mov es, ax          ; Set DI to this info
    mov di, buffer

    mov cx, word [RootDirEntries]   ; Search all (224) entries
    mov ax, 0           ; Searching at offset 0


next_root_entry:
    xchg cx, dx         ; We use CX in the inner loop...

    mov si, kern_filename       ; Start searching for kernel filename
    mov cx, 11
    rep cmpsb
    je found_file_to_load       ; Pointer DI will be at offset 11

    add ax, 32          ; Bump searched entries by 1 (32 bytes per entry)

    mov di, buffer          ; Point to next entry
    add di, ax

    xchg dx, cx         ; Get the original CX back
    loop next_root_entry

    mov si, file_not_found      ; If kernel is not found, bail out
    call print_string
    jmp reboot


found_file_to_load:         ; Fetch cluster and load FAT into RAM
    mov ax, word [es:di+0Fh]    ; Offset 11 + 15 = 26, contains 1st cluster
    mov word [cluster], ax

    mov ax, 1           ; Sector 1 = first sector of first FAT
    call l2hts

    mov di, buffer          ; ES:BX points to our buffer
    mov bx, di

    mov ah, 2           ; int 13h params: read (FAT) sectors
    mov al, 9           ; All 9 sectors of 1st FAT

    pusha               ; Prepare to enter loop


read_fat:
    popa                ; In case registers are altered by int 13h
    pusha

    stc
    int 13h             ; Read sectors using the BIOS

    jnc read_fat_ok         ; If read went OK, skip ahead
    call reset_floppy       ; Otherwise, reset floppy controller and try again
    jnc read_fat            ; Floppy reset OK?

; ******************************************************************
fatal_disk_error:
; ******************************************************************
    mov si, disk_error      ; If not, print error message and reboot
    call print_string
    jmp reboot          ; Fatal double error


read_fat_ok:
    popa

    mov ax, 2000h           ; Segment where we'll load the kernel
    mov es, ax
    mov bx, 0

    mov ah, 2           ; int 13h floppy read params
    mov al, 1

    push ax             ; Save in case we (or int calls) lose it


; Now we must load the FAT from the disk. Here's how we find out where it starts:
; FAT cluster 0 = media descriptor = 0F0h
; FAT cluster 1 = filler cluster = 0FFh
; Cluster start = ((cluster number) - 2) * SectorsPerCluster + (start of user)
;               = (cluster number) + 31

load_file_sector:
    mov ax, word [cluster]      ; Convert sector to logical
    add ax, 31

    call l2hts          ; Make appropriate params for int 13h

    mov ax, 2000h           ; Set buffer past what we've already read
    mov es, ax
    mov bx, word [pointer]

    pop ax              ; Save in case we (or int calls) lose it
    push ax

    stc
    int 13h

    jnc calculate_next_cluster  ; If there's no error...

    call reset_floppy       ; Otherwise, reset floppy and retry
    jmp load_file_sector


    ; In the FAT, cluster values are stored in 12 bits, so we have to
    ; do a bit of maths to work out whether we're dealing with a byte
    ; and 4 bits of the next byte -- or the last 4 bits of one byte
    ; and then the subsequent byte!

calculate_next_cluster:
    mov ax, [cluster]
    mov dx, 0
    mov bx, 3
    mul bx
    mov bx, 2
    div bx              ; DX = [cluster] mod 2
    mov si, buffer
    add si, ax          ; AX = word in FAT for the 12 bit entry
    mov ax, word [ds:si]

    or dx, dx           ; If DX = 0 [cluster] is even; if DX = 1 then it's odd

    jz even             ; If [cluster] is even, drop last 4 bits of word
                    ; with next cluster; if odd, drop first 4 bits

odd:
    shr ax, 4           ; Shift out first 4 bits (they belong to another entry)
    jmp short next_cluster_cont


even:
    and ax, 0FFFh           ; Mask out final 4 bits


next_cluster_cont:
    mov word [cluster], ax      ; Store cluster

    cmp ax, 0FF8h           ; FF8h = end of file marker in FAT12
    jae end

    add word [pointer], 512     ; Increase buffer pointer 1 sector length
    jmp load_file_sector


end:                    ; We've got the file to load!
    pop ax              ; Clean up the stack (AX was pushed earlier)
    mov dl, byte [bootdev]      ; Provide kernel with boot device info

    jmp 2000h:0000h         ; Jump to entry point of loaded kernel!


; ------------------------------------------------------------------
; BOOTLOADER SUBROUTINES

reboot:
    mov ax, 0
    int 16h             ; Wait for keystroke
    mov ax, 0
    int 19h             ; Reboot the system


print_string:               ; Output string in SI to screen
    pusha

    mov ah, 0Eh         ; int 10h teletype function

.repeat:
    lodsb               ; Get char from string
    cmp al, 0
    je .done            ; If char is zero, end of string
    int 10h             ; Otherwise, print it
    jmp short .repeat

.done:
    popa
    ret


reset_floppy:       ; IN: [bootdev] = boot device; OUT: carry set on error
    push ax
    push dx
    mov ax, 0
    mov dl, byte [bootdev]
    stc
    int 13h
    pop dx
    pop ax
    ret


l2hts:          ; Calculate head, track and sector settings for int 13h
            ; IN: logical sector in AX, OUT: correct registers for int 13h
    push bx
    push ax

    mov bx, ax          ; Save logical sector

    mov dx, 0           ; First the sector
    div word [SectorsPerTrack]
    add dl, 01h         ; Physical sectors start at 1
    mov cl, dl          ; Sectors belong in CL for int 13h
    mov ax, bx

    mov dx, 0           ; Now calculate the head
    div word [SectorsPerTrack]
    mov dx, 0
    div word [Sides]
    mov dh, dl          ; Head/side
    mov ch, al          ; Track

    pop ax
    pop bx

    mov dl, byte [bootdev]      ; Set correct device

    ret


; ------------------------------------------------------------------
; STRINGS AND VARIABLES

    kern_filename   db "KERNEL  BIN"    ; kernel filename

    disk_error  db "Floppy error! Press any key...", 0
    file_not_found  db "KERNEL.BIN not found!", 0

    bootdev     db 0    ; Boot device number
    cluster     dw 0    ; Cluster of the file we want to load
    pointer     dw 0    ; Pointer into Buffer, for loading kernel


; ------------------------------------------------------------------
; END OF BOOT SECTOR AND BUFFER START

    times 510-($-$$) db 0   ; Pad remainder of boot sector with zeros
    dw 0AA55h       ; Boot signature (DO NOT CHANGE!)


buffer:             ; Disk buffer begins (8k after this, stack starts)


; ==================================================================

我在网上找到了启动代码。

Kernel.asm


[bits 16]



   mov ax, 0  ; set up segments
   mov ds, ax
   mov es, ax
   mov ss, ax     ; setup stack
   mov sp, 0x7C00 ; stack grows downwards from 0x7C00

   mov ah, 9
   mov bl, 9
   mov cx, 11 
   int 10h
   mov DX, boota
   int 21H

   mov si, boota         ;Print boot message
   call print_string

 mainloop:
   mov si, prompt
   call print_string          ; prompt db ">"

   mov di, buffer
   call get_string

   mov si, buffer
   cmp byte [si], 0  ; blank line?
   je mainloop       ; yes, ignore it

   mov si, buffer
   mov di, cmd_reb  ; "reboot" command
   call strcmp
   jc .reboot

   mov si, buffer
   mov di, cmd_shut  ; "shutdown" command
   call strcmp
   jc .shut

   mov si,badcommand
   call print_string     ; What if there is no such command?
   jmp mainloop        ; jmp mainloop :-)

 .reboot:                           ; reboot function
   db 0x0ea 
   dw 0x0000 
   dw 0xffff

 .shut:                                   ;shutdown function

   mov al, 02h      
   mov ah, 00h      
   int 10h

   mov si, shutm
   call print_string

   mov ax, 0x1000
   mov ax, ss
   mov sp, 0xf000
   mov ax, 0x5307
   mov bx, 0x0001
   mov cx, 0x0003
   int 0x15

   ; ------Variables-------------
 shutm  db 'The MCOS shutdowned sucessfully. Now you may power off the computer.', 0x0d, 0x0a, 0
 boota db 'Booting MotoSoft COS 1...', 0x0d, 0x0a, 0
 buffer times 64 db 0
 prompt db '>', 0
 badcommand db 'Bad command entered.', 0x0D, 0x0A, 0 
 cmd_reb db 'reboot', 0


 ; ================
 ; calls start here
 ; ================

 print_string:
   lodsb        ; grab a byte from SI

   or al, al  ; logical or AL by itself
   jz .done   ; if the result is zero, get out

   mov ah, 0x0E
   int 0x10      ; otherwise, print out the character!

   jmp print_string

 .done:
   ret

 get_string:
   xor cl, cl

 .loop:
   mov ah, 0
   int 0x16   ; wait for keypress

   cmp al, 0x08    ; backspace pressed?
   je .backspace   ; yes, handle it

   cmp al, 0x0D  ; enter pressed?
   je .done      ; yes, we're done

   cmp cl, 0x3F  ; 63 chars inputted?
   je .loop      ; yes, only let in backspace and enter
   mov ah, 0x0E
   int 0x10      ; print out character

   stosb  ; put character in buffer
   inc cl
   jmp .loop

 .backspace:
   cmp cl, 0    ; beginning of string?
   je .loop ; yes, ignore the key

   dec di
   mov byte [di], 0 ; delete character
   dec cl       ; decrement counter as well

   mov ah, 0x0E
   mov al, 0x08
   int 10h      ; backspace on the screen

   mov al, ' '
   int 10h      ; blank character out

   mov al, 0x08
   int 10h      ; backspace again

   jmp .loop    ; go to the main loop

 .done:
   mov al, 0    ; null terminator
   stosb

   mov ah, 0x0E
   mov al, 0x0D
   int 0x10
   mov al, 0x0A
   int 0x10     ; newline

   ret

 strcmp:
 .loop:
   mov al, [si]   ; grab a byte from SI
   mov bl, [di]   ; grab a byte from DI
   cmp al, bl     ; are they equal?
   jne .notequal  ; nope, we're done.

   cmp al, 0  ; are both bytes (they were equal before) null?
   je .done   ; yes, we're done.

   inc di     ; increment DI
   inc si     ; increment SI
   jmp .loop  ; loop!

 .notequal:
   clc  ; not equal, clear the carry flag
   ret

 .done:     
   stc  ; equal, set the carry flag
   ret


cmd_shut db 'shutdwon', 0```

我从网上得到了所有这些代码,我正在制作我的爱好 OS。

以及编译代码:-

nasm -f bin -o boot.bin boot.asm
nasm -f bin -o kernel.bin kernel.asm
dd if=/dev/zero of=disk.img bs=512 count=2880
dd if=boot.bin of=disk.img bs=512 conv=notrunc
dd if=kernel.bin of=disk.img bs=512 seek=1 conv=notrunc

当我启动 OS 时,它显示

KERNEL.BIN not found!

错误图片:

如果您打算使用 MIKEOS 引导加载程序,那么您需要创建一个 FAT12 格式的 1.44MiB 软盘映像以与 boot.asm 中的卷引导记录 (VBR) 兼容。然后,您需要将 KERNEL.BIN 复制到该图像中。

这个过程对于 mtools 包来说非常简单。 mtoolsonline documentation。此答案不是使用 mtools 的完整指南,但足以完成您需要启动的操作。如果您使用的是 Debian 或基于 Debian 的发行版,例如 Ubuntu,您可以以 root 用户身份安装 mtools 软件包:

apt-get install mtools

mtools软件包自带一个mformat程序,可以创建1.44MiB的磁盘镜像;将其格式化为 FAT12;并让它自动将您的 boot.bin 程序放在第一个扇区。命令将是:

mformat -f1440 -B boot.bin -C -i disk.img

这应该创建一个名为 disk.img 的 1.44MiB 磁盘映像文件。要将 KERNEL.BIN 之类的文件复制到 disk.img 中,您可以使用以下命令:

mcopy -D o -i disk.img KERNEL.BIN ::/

-D o 选项强制 mcopy 覆盖文件(如果存在)。 -i disk.img 指定磁盘映像文件的名称 KERNEL.BIN 是我们要复制到映像中的文件(源文件)。 :: 是一个特殊的设备名称,用来指代磁盘映像本身。 / 是根目录。命令末尾的 ::/ 是放置文件的目标位置 - 在磁盘映像的根目录中。

您可以使用mdir命令获取根目录的文件列表。

mdir -i disk.img ::/

这类似于 DOS 中的 dir /。输出看起来像:

 Volume in drive : has no label
 Volume Serial Number is 6F67-C526
Directory for ::/

KERNEL   BIN        74 2019-12-20  13:25
        1 file                   74 bytes
                          1 457 152 bytes free

我创建了一个 kernel.asm 来代替你的,因为你的问题超出了你的问题范围。我提供这个作为示例,应该可以工作:

ORG 0x0000

start:
    mov ax, 0x2000             ; Mike OS'es bootloader loads us at 0x2000:0x0000
    mov ds, ax                 ; Set DS = ES = 0x2000
    mov es, ax

    ; Stack just below 0x2000:0x0000 starting at 0x1000:0x0000.
    ; First push will set SS:SP to 0x1000:0xfffe because SP will wrap.
    mov ax, 0x1000
    mov ss, ax
    xor sp, sp

    cld                        ; Clear Direction Flag (DF=0 is forward string movement)

    mov si, testCodeStr
    call print_string

    cli
.end_loop:
    hlt
    jmp .end_loop

testCodeStr: db 0x0d, 0x0a, "KERNEL.BIN loaded and running...", 0

; Function: print_string
;           Display a string to the console on display page 0
;
; Inputs:   SI = Offset of address to print
; Clobbers: AX, BX, SI

print_string:
    mov ah, 0x0e                ; BIOS tty Print
    xor bx, bx                  ; Set display page to 0 (BL)
    jmp .getch
.repeat:
    int 0x10                    ; print character
.getch:
    lodsb                       ; Get character from string
    test al,al                  ; Have we reached end of string?
    jnz .repeat                 ;     if not process next character
.end:
    ret

此代码旨在与 MIKEOS 的引导加载程序一起工作,该引导加载程序从内存中的 0x2000:0x0000 加载和 运行s 内核。我在顶部使用 ORG 0x0000 并将 DSES 设置为 0x2000 以对应我们在内存中的加载位置。我还将 SS:SP 设置为 0x1000:00000 的初始值。第一次将 16 位值压入堆栈时 SS:SP 将是 0x1000:0xFFFE(物理地址 0x1FFFE),就在 [=129 处的内核加载地址下方=](物理地址0x20000)。

在 QEMU 中组装并构建磁盘映像并 运行 使用以下命令时:

nasm -f bin boot.asm -o boot.bin
nasm -f bin kernel.asm -o KERNEL.BIN
mformat -f1440 -B boot.bin -C -i disk.img
mcopy -D o -i disk.img KERNEL.BIN ::/

qemu-system-i386 -fda disk.img

输出应类似于:


内核问题

kernel.asm的主要问题是:

  • 错误地设置了 DS 段寄存器。 MIKEOS 在内存中的 0x2000:0x0000 (segment:offset) 加载你的内核。您需要一个匹配偏移部分的 ORG。您将要加载带有段部分 (0x2000) 的 DS(如果需要的话是 ES)。

  • 在使用像 LODS/STOS/SCAS 这样的字符串指令之前,您需要确保方向标志 (DF) 的设置符合您的预期。使用 CLD 将为向前移动设置 DF=0,这是您的代码所期望的。

  • 看来你也调用了int 21h来打印。代码似乎是 DOS 程序遗留下来的,您打算将其删除。

  • 您在 cmd_shut 字符串中拼错了 shutdown。您错误地将其拼写为 shutdwon.

  • 不清楚你想用这段代码做什么:

      mov ah, 9      ; Unclear what you were trying to do here?
      mov bl, 9
      mov cx, 11
      int 10h        ; ????
    

您不妨考虑删除它。

您的代码开头可以修改为:

[bits 16]
ORG 0x0000

   mov ax, 0x2000 ; set up segments to match CS=0x2000
   mov ds, ax
   mov es, ax
   xor ax, ax
   mov ss, ax     ; set stack segment to 0x0000
   mov sp, 0x7C00 ; stack grows downwards from 0x0000:0x7C00
   cld            ; Clear Direction Flag (DF=0 is forward string movement)    

   mov ah, 9      ; Unclear what you were trying to do here?
   mov bl, 9
   mov cx, 11
   int 10h        ; ????

   mov si, boota  ; Print boot message
   call print_string
   [snip]

您拼写 shutdown 不正确,因此无法将其正确识别为命令。应该是:

cmd_shut db 'shutdown', 0

经过这些更改,当 运行:

时,代码看起来像这样