c - load_seg_reg(ES,0xfffc): invalid segment when trying to run kernel code from boot sector

标签 c operating-system kernel bochs memory-segmentation

所以我构建了一个简单的操作系统,并且在从引导扇区调用内核代码时遇到了上述错误。我已经挣扎了几天了,仍然处于死胡同。

代码在调用 KERNEL_OFFSET 时失败。我知道它失败了,因为我试图跳到无效的段,但我不知道如何跳到哪里。 我还注意到它并没有真正读取磁盘。我的 load_kernel 代码要求读取 10 个扇区,但似乎只读取了。 该程序能够进入保护模式并能够加载我需要从磁盘加载的内容,尽管加载行为很奇怪。

我还显示了我的 makefile,因为我怀疑我可能没有正确链接。顺便说一句,我正在运行 OSX,所以我不知道我使用的 shell 命令是否正确。

我已经在这几天了,任何帮助将不胜感激。谢谢

启动扇区

;
; 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

切换到下午

[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

最佳答案

问题出在 makefile 以及我如何链接和创建 temp/kernel.bin 二进制文件。

规则应该是:

clang -ffreestanding -m32 kernel_entry.o kernel.o $(OBJECTS) -o temp/kernel_temp.o
gobjcopy -O binary temp/kernel_temp.o temp/kernel.bin

相反。我需要创建一个链接了 kernel_entry.o 和 kernel.o 的临时对象文件,然后从临时对象文件中创建一个二进制文件。如果您运行的是 OSX,您可能必须执行类似的操作,因为我们无法访问所有精美的 linux ld 选项。

关于c - load_seg_reg(ES,0xfffc): invalid segment when trying to run kernel code from boot sector,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41320122/

相关文章:

c - 带 2 补码的额外字节

c - 什么时候可以将 C 中的函数作为系统调用来调用

linux - 如何查看编译后的linux内核?

C,指向结构中函数的指针,警告 "assignment from incompatible pointer type"

c++ - VOID NTAPI 是什么意思,我在哪里可以找到它的引用资料?

c - 如何在 Linux 中重现接受错误

linux - 中断上下文中的页面错误

c - Linux 内核中的 major() 和 minor() 函数

string - 如何从 &str 转换为 *const i8 *without* libstd 和 libcore?

c - 使用哪种 Winapi listview 类型