因此,我正在构建一个简单的操作系统,并且在从引导扇区调用内核代码时遇到了上述错误。我花了几天的时间挣扎,仍然处于死胡同。
代码在调用 KERNEL_OFFSET 时失败。我知道它失败了,因为我试图跳到一个无效的段,但我不知道如何以及在哪里。我还注意到它并没有真正读取磁盘。我的 load_kernel 代码要求读取 10 个扇区,但似乎只被读取了。该程序能够进入保护模式并能够加载我需要它从磁盘加载的内容,尽管加载行为很奇怪。
我还展示了我的makefile,因为我怀疑我可能没有正确链接东西。如果我使用的 shell 命令是正确的,我正在运行 OSX 顺便说一句。
我已经在这几天了,任何帮助将不胜感激。谢谢
引导扇区
;
; A simple boot sector program.
; BIOS stores the boot drive in DL
;
[org 0x7c00]
[bits 16]
mov [BOOT_DRIVE], dl ; Move boot drive info to memory.
; Setup stack to a position we know is free.
mov bp, 0x9000
mov sp, bp
; Output a nice message.
mov dx, REAL_MODE_MSG
call PrintString16
; Load the kernel.
mov dx, [BOOT_DRIVE]
call LoadKernel
mov dx, LOADED_KERNEL_MSG
call PrintString16
; Switch to protected mode.
; Note, we never break from Protected mode.
call SwitchToProtectedMode
jmp $
; Including some useful routines.
%include "string_utils_16.s"
%include "gdt.s"
%include "string_utils.s"
%include "load_kernel.s"
%include "switch_to_pm.s"
[bits 32]
BeginProtectedMode:
mov edx, PROT_MODE_MSG
call PrintString
; This call should, theoritecally, run the instructions we just loaded.
; AKA, the C code.
; KERNEL_OFFSET is defined in load_kernel.s
call KERNEL_OFFSET
jmp $
BOOT_DRIVE db 0
LOADED_KERNEL_MSG db "Loaded kernel with no errors!", 0
REAL_MODE_MSG db "Welcome! Started in 16-bit Real Mode!", 0
PROT_MODE_MSG db "Now running in 32-bit Protected Mode", 0
times 510 -( $ - $$ ) db 0
dw 0xaa55
switch_to_pm
[bits 16]
SwitchToProtectedMode:
cli
lgdt [gdt_descriptor]
; To actually switch to 32 bit mode, set LSB of cr0 to 1
; Can't touch the cr0 register directly so gotta do it the hard way.
mov eax, cr0
or eax, 0x1
mov cr0, eax
; Technically, after that last move instruction, we're in 32 bit mode BUT
; the CPU may have been doing work in between all this, since, the CPU can do
; certain things in parallel if its got different circuitry to do those things,
; which it probably does. Few of the things the CPU could do in parallel is
; fetch, decode and execute. We dont want the CPU to be fetching the next
; instruction while our stuff is happening (those next things that will be
; fetched probably wont work in 32 bit mode) and we want it to finish whatever
; it is currently executing. so, we're going to do a far jump to
; somewhere so that the CPU cannot make any extrapolations on what to fetch next
; and anything being executed can finish executing.
jmp CODE_SEGMENT:init_pm
[bits 32]
; Initialize segment registers. In 32 bit mode, they all point to an entry in
; the GDT.
init_pm:
mov ax, DATA_SEGMENT
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
; Define the stack somewhere we're sure has free memory.
mov ebp, 0x90000
mov esp, ebp
call BeginProtectedMode
加载内核
[bits 16]
KERNEL_OFFSET equ 0x1000
; Will load the kernel into memory.
; dl will contain the boot drive.
LoadKernel:
push dx ; Save the boot drive info
mov dx, LOAD_KERNEL_MSG
call PrintString16
pop dx ; Get it back. DL contains boot drive.
mov bx, KERNEL_OFFSET ; Where we want kernel to be loaded to.
mov dh, 10 ; How many sectors to load
; dl is also a parameter but we already have it.
call LoadFromDisk
ret
%include "disk_load.s"
LOAD_KERNEL_MSG db "Loading kernel...", 0
核心
// Simple kernel.
#include "screen.h"
void main() {
print("It worked!");
// char* video = (char*) 0xb800;
// video[0] = 'S';
}
生成文件
INCLUDE = include/
# List is expanded when used not when declared.
OBJECTS = $(wildcard temp/*.o)
# -------------------- Build the os_image
os_image.bin : temp/boot_sect.bin temp/kernel.bin
cat temp/boot_sect.bin temp/kernel.bin > os_image.bin
# -------------------- Build the boot sector image
temp/boot_sect.bin : boot/boot_sect.s
nasm boot/boot_sect.s -i boot/ -f bin -o temp/boot_sect.bin
# -------------------- Build kernel image
temp/kernel.bin : kernel.o kernel_entry.o
clang -ffreestanding -m32 kernel_entry.o $(OBJECTS) kernel.o -o temp/kernel.bin
# -------------------- Build object files
kernel.o : kernel/kernel.c temp/screen.o
clang -ffreestanding -m32 -c -I $(INCLUDE) kernel/kernel.c -o kernel.o
kernel_entry.o : kernel/kernel_entry.s
nasm -f macho -o kernel_entry.o kernel/kernel_entry.s
# -------------------- Build driver objects
temp/screen.o : include/screen.h drivers/screen.c temp/low_level.o
clang -ffreestanding -m32 -c -I $(INCLUDE) drivers/screen.c -o temp/screen.o
temp/low_level.o : include/low_level.h kernel/low_level.c
clang -ffreestanding -m32 -c -I $(INCLUDE) kernel/low_level.c -o temp/low_level.o
clean :
rm *.o
rm ./temp/*.o
rm ./temp/*.bin
rm *.bin
相关 bochs 控制台转储
00014040953i[BIOS ] Booting from 0000:7c00
00014479618i[FDD ] partial read() on floppy image returns 172/512
00014561257i[MEM0 ] allocate_block: block=0x10 used 0x3 of 0x20
00014561312e[CPU0 ] load_seg_reg(ES, 0xfffc): invalid segment
00014561312e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
00014561312e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x08)
00014561312i[CPU0 ] CPU is in protected mode (active)
00014561312i[CPU0 ] CS.mode = 32 bit
00014561312i[CPU0 ] SS.mode = 32 bit
00014561312i[CPU0 ] EFER = 0x00000000
00014561312i[CPU0 ] | EAX=0008fffc EBX=00001000 ECX=00090003 EDX=ffff0136
00014561312i[CPU0 ] | ESP=0008fff8 EBP=00090003 ESI=000e0000 EDI=00007d2d
00014561312i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf SF zf AF pf CF
00014561312i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00014561312i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 ffffffff 1 1
00014561312i[CPU0 ] | DS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00014561312i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00014561312i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00014561312i[CPU0 ] | FS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00014561312i[CPU0 ] | GS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00014561312i[CPU0 ] | EIP=0000107c (0000107c)
00014561312i[CPU0 ] | CR0=0x60000011 CR2=0x00000000
00014561312i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
(0).[14561312] [0x000000000000107c] 0008:000000000000107c (unk. ctxt): pop es ; 07
00014561312e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting