DIY Bootloader 没有做它应该做的事情

DIY Bootloader isn't doing what it should do

提问人:Fabboy 提问时间:9/14/2023 最后编辑:phuclvFabboy 更新时间:9/14/2023 访问量:51

问:

不久前,我在教程的帮助下编写了一个引导加载程序。但它太复杂了,我几乎什么都不懂。所以今天我开始用我的知识和谷歌来制作我自己的引导加载程序。没什么大不了的,只是一个引导加载程序,它激活了我能找到和编程的东西,然后将电源传递给内核。

我的目标是稍微玩一下视频缓冲区,所以我一开始就激活了它。然后我被告知,在将电源传递给内核之前,我应该切换到保护模式,这就是问题开始的地方,因为我不知道该怎么做。出于调试原因,我制作了 2 个标签,这些标签只是在屏幕上画点,但有时我只有一个,在当前状态下我只有一个黑屏。

我目前的代码:

[bits 16]
[org 0x7c00]

start:
    cli

    ; Initialize stack
    mov ax, 0x8000
    mov ss, ax
    xor sp, sp

    ; Initialize data segments
    xor ax, ax
    mov ds, ax
    mov es, ax

    ; Switch to Video Mode
    mov ax, 0x0013
    int 0x10   

    call debug 

    
    lgdt [gdtr]
    
    jmp 0x08:skip 

[bits 32]
skip:

    mov eax, cr0
    or al, 1
    mov cr0, eax    

    mov ax, 0x10 
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax 
    jmp 0x08:0x1000 

debug:
    mov ax, 0xA000
    mov es, ax
    mov di, 320*100 + 100
    mov al, 0x0F
    stosb

debug2:
    mov ax, 0xA000
    mov es, ax
    mov di, 320*100 + 300
    mov al, 0x0F
    stosb


section .gdtr
gdtr:
    dw 0x17FF
gdt:
    dd 0x00000000, 0x00000000
    dd 0x00FFFFFF, 0x00CF9A00

; Boot signature
times 422-($-$$) db 0
dw 0xAA55

软盘加载成功。

在这里也许可以发现我的其他错误: kernel 文件夹中的 linker.ld:

ENTRY(kernel_main)
SECTIONS {
    . = 0x1000;

    .text : {
        *(.text)
    }
    .data : {
        *(.data)
    }
    .bss : {
        *(.bss)
    }
}

kernel 文件夹中的 entry.asm:

section .text
global _start
extern kernel_main

_start:
    ; Initialize stack
    mov esp, mainFnStack + 4096 ; Point to the top of the stack space

    ; Draw dot for debugging
    mov ax, 0xA000
    mov es, ax
    mov di, 320*100 + 200
    mov al, 0x0F
    stosb

    ; Call main kernel function
    call kernel_main

    ; Infinite loop to halt execution
    jmp $

section .bss
mainFnStack resb 4096  ; Allocate 4096 bytes for the stack

如果你能帮助我,我将不胜感激。

我正在使用 QEMU,但我无法将 GDB 与 QEMU 结合使用。


编辑:

以下是引导加载程序的 Makefile:

ASMC = nasm
FLAGS = -f bin -O3
SRC = boot.asm
DEST = ../../bin/boot.bin

all: $(DEST)

$(DEST): $(SRC)
    $(ASMC) $(FLAGS) $< -o $@

clean:
    rm -f $(DEST)

内核的 makefile:

CC = gcc
LD = ld
ASMC = nasm
CFLAGS = -ffreestanding -c
ASMFLAGS = -f elf64
LDFLAGS = -o ../../bin/kernel.bin -T linker.ld
SRC_C = $(wildcard *.c */*.c)
SRC_ASM = $(wildcard *.asm */*.asm)
OBJ_C = $(patsubst %.c, obj/%.o, $(SRC_C))
OBJ_ASM = $(patsubst %.asm, obj/%.o, $(SRC_ASM))

all: ../../bin/kernel.bin

../../bin/kernel.bin: $(OBJ_C) $(OBJ_ASM)
    $(LD) $(LDFLAGS) $(OBJ_ASM) $(OBJ_C)

obj/%.o: %.c
    @mkdir -p $(@D)
    $(CC) $(CFLAGS) $< -o $@

obj/%.o: %.asm
    @mkdir -p $(@D)
    $(ASMC) $(ASMFLAGS) $< -o $@

clean:
    rm -rf obj ../../bin/kernel.bin

项目根目录中的 Makefile:

all:
    $(MAKE) -C src/kernel
    $(MAKE) -C src/bootloader
    dd if=/dev/zero of=bin/floppy.img bs=1k count=1440 
    dd if=bin/boot.bin of=bin/floppy.img conv=notrunc seek=0
    dd if=bin/kernel.bin of=bin/floppy.img bs=512 seek=1 conv=notrunc



clean:
    $(MAKE) -C src/kernel clean
    $(MAKE) -C src/bootloader clean
    rm -f bin/floppy.img

如果我填充到 510,我会得到一个大约 550 字节的二进制文件,或多或少

汇编 x86 qemu 引导加载程序 低级

评论

0赞 ecm 9/14/2023
还请显示用于生成和运行程序的命令。乍一看,您没有从函数返回,您从未加载过内核,更不用说在加载器中重新定位内核的任何其他部分了,您的 GDT 只包含一个描述符,并且 0AA55h 签名之前的指令应该填充到 510 字节而不是 422。debugtimes
0赞 DarkAtom 9/14/2023
您在实模式下发生,它会跳转到 32 位函数。此标签后面的第一条指令 , 将被翻译为 ,后跟 和 。我真的不确定为什么这不会出现三重错误......但这肯定会造成混乱。call debugmov ax, 0xA000mov eax, 0xC08EA000mov edi, 0x0FB07D64stosb
0赞 Fabboy 9/14/2023
@ecm我编辑了我的问题并添加了我使用的 Makefile
2赞 DarkAtom 9/14/2023
@ecm “为什么这些指令中的任何一个都会出错?”我并不是说这些特定指令中的任何一个都会出错,但据我了解,OP 根本没有出现三重错误,只是一个黑屏。我认为这种混乱在某些时候一定是错的......遇到无限循环必须有多幸运?最有可能的是 抛出 ,我们不知道BIOS是用什么做的。#UD
1赞 DarkAtom 9/14/2023
@ecm 这是一个公平的观点,但不是有效的操作码。代码的问题在于它永远无法执行 GDT 的限制字段。上帝知道它在哪里(除非是0xFFFF),它可能永远不会恢复并执行随机操作码,正如你所说。如果不是这个错误(因为GDT肯定不应该是字节长),它很可能会触发。FF FFFF FFFF 17call near [bx]bx0x1800int 6

答:

0赞 DarkAtom 9/14/2023 #1

这是切换到保护模式的程序:

[bits 16]
[org 0x7c00]

start:
    cli

    ; Initialize stack
    mov ax, 0x8000
    mov ss, ax
    xor sp, sp

    ; Initialize data segments
    xor ax, ax
    mov ds, ax
    mov es, ax

    ; Switch to Video Mode
    mov ax, 0x0013
    int 0x10   

    ;; call debug ;; not needed
    
    lgdt [gdtr]
    
    ;; jmp 0x08:skip ;; not this early

    mov ax, 0x2401 ; enable A20 (might already be done but who knows)
    int 0x15

    cli ; I noticed that some virtual machines set FLAGS.IF after that BIOS call

    mov eax, cr0
    or al, 1
    mov cr0, eax
    jmp 0x8:skip

[bits 32]
skip:
    mov ax, 0x10 
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax 
    jmp 0x08:0x1000 ; I assume you want your code to continue here, but if this code is the only one loaded, it will jump to nothing (a.k.a. junk bytes)

debug: ; these are designed to run in Protected Mode
    mov byte [0xA0000 + 32100], 0x0F
    ret

debug2:
    mov byte [0xA0000 + 32300], 0x0F
    ret

gdtr:
    dw 23
    dd gdt
gdt:
    dd 0x00000000, 0x00000000
    dd 0x0000FFFF, 0x00CF9A00 ; code segment 0x08
    dd 0x0000FFFF, 0x00CF9200 ; data segment 0x10

; Boot signature
times 510-($-$$) db 0
dw 0xAA55

但是,我应该提一下,如果您这么早切换到保护模式,您将不会走得太远,因为您受到引导扇区 512 字节的限制。在加载能够直接与硬盘驱动器通信的驱动程序之前,无论您喜欢与否,都必须使用它(并且,由于它是BIOS调用,因此仅在实模式下可用)。在实模式下还需要做其他事情,例如使用获取内存映射,但这只是一个起点。int 0x13int 0x15, eax=0xE820

评论

0赞 Fabboy 9/14/2023
这对你有用吗?看起来它崩溃了,QEMU 尝试重新启动它。但谢谢,我试着让它运行
0赞 DarkAtom 9/14/2023
当然,它崩溃了。它跳转到 ,它不包含任何内容。正如我最后所说,您将不得不从Real Mode的硬盘驱动器加载更多内容。0x8:0x1000
0赞 Fabboy 9/14/2023
我错过了这个。这是这个块还是需要更多? 看起来很糟糕,我不知道该怎么做asm ; Read Kernel from the disk mov ah, 0x02 ; read sectors from drive function mov al, 0x01 ; number of sectors to read mov ch, 0x00 ; cylinder number mov cl, 0x02 ; sector number, start from the second sector mov dh, 0x00 ; head number mov dl, 0x80 ; drive number, 0x80 for the first disk mov bx, 0x1000 ; destination address in memory mov es, bx xor bx, bx ; offset 0 int 0x13
0赞 DarkAtom 9/14/2023
不要复制粘贴代码块。试着理解它们。是的,您想用来从硬盘驱动器加载内容。但是您需要计算要读取的扇区的 CHS 地址(为此,您需要知道您的文件在驱动器上的位置)。此外,指定硬盘驱动器,但在您提到的问题中您使用的是软盘,这需要 .是一个很好的资源。int 0x13, ah=2dl=0x80dl=0
0赞 Fabboy 9/14/2023
我正在尽力理解它。是的,我使用 Floppy,我刚刚看到并更改了它。但我不知道如何“计算扇区的 CHS 地址”。