不久前,我在教程的帮助下编写了一个引导加载程序。但它太复杂了,我几乎什么都听不懂。所以今天我开始利用我的知识和谷歌来制作我自己的引导加载程序。没什么大不了的,只是一个引导加载程序,它激活我能找到的东西并进行编程,然后将电源传递给内核。
我的目标是稍微玩一下视频缓冲区,所以我一开始就激活了它。然后我被告知,在将权力传递给内核之前,我应该切换到保护模式,这就是问题开始的地方,因为我不知道到底该怎么做。出于调试原因,我制作了 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
软盘加载成功。
在这里也许可以发现我的其他错误: 内核文件夹中的linker.ld:
ENTRY(kernel_main)
SECTIONS {
. = 0x1000;
.text : {
*(.text)
}
.data : {
*(.data)
}
.bss : {
*(.bss)
}
}
内核文件夹中的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 字节大小的二进制文件
这是切换到保护模式的程序:
[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 字节的限制。在加载能够直接与硬盘驱动器通信的驱动程序之前,无论您是否喜欢,都必须使用
int 0x13
(并且,由于它是 BIOS 调用,因此仅在实模式下可用)。在实模式下还需要完成其他事情,例如使用 int 0x15, eax=0xE820
获取内存映射,但这只是一个起点。