简单C引导程序/内核的问题

时间:2012-06-12 13:55:28

标签: c x86 kernel nasm osdev

最近我对编写自己真正非常基本的操作系统感兴趣。 我写了(好吧,复制了)一些基本的程序集,它建立了一个堆栈并做了一些基本的事情,这似乎工作正常,但是试图将C引入混合中已经搞砸了所有事情。

我有两个主要的项目文件:loader.s,它是一些创建堆栈并调用我的C函数的NASM,以及包含基本C函数的kernel.c。

我目前的问题基本上是当我运行kernel.bin文件时QEMU冻结了。我猜我的代码有很多问题 - 由于它的极端特异性,这个问题可能不适合StackOverflow格式。我的项目文件如下:

loader.s:

BITS 16                         ; 16 Bits

extern kmain                    ; Our 'proper' kernel function in C

loader:
    mov ax, 07C0h           ; Move the starting address [7C00h] into 'ax'
    add ax, 32              ; Leave 32 16 byte blocks [200h] for the 512 code segment
    mov ss, ax              ; Set 'stack segment' to the start of our stack
    mov sp, 4096            ; Set the stack pointer to the end of our stack [4096 bytes in size]

    mov ax, 07C0h           ; Use 'ax' to set 'ds'
    mov ds, ax              ; Set data segment to where we're loaded
    mov es, ax              ; Set our extra segment

    call kmain              ; Call the kernel proper

    cli                     ; Clear ints

    jmp $                   ; Hang


; Since putting these in and booting the image without '-kernel' can't find
; a bootable device, we'll comment these out for now and run the ROM with
; the '-kernel' flag in QEMU
        ;times 510-($-$$) db 0          ; Pad remained of our boot sector with 0s
        ;dw 0xAA55                      ; The standard 'magic word' boot sig

kernel.c:

#include <stdint.h>

void kmain(void)
{
        unsigned char *vidmem = (char*)0xB8000; //Video memory address
        vidmem[0] = 65; //The character 'A'
        vidmem[1] = 0x07; //Light grey (7) on black (0)
}

我编译所有内容:

  

nasm -f elf -o loader.o loader.s

     

i386-elf-gcc -I / usr / include -o kernel.o -c kernel.c -Wall -nostdlib -fno-builtin -nostartfiles -nodefaultlibs

     

i386-elf-ld -T linker.ld -o kernel.bin loader.o kernel.o

然后进行测试:

  

qemu-system-x86_64 -kernel kernel.bin

希望有人可以帮我查看一下 - 代码片段不会太长。

感谢。

1 个答案:

答案 0 :(得分:10)

天哪,从哪里开始? (哎呀,是你吗?)

来自loader.s的代码进入主引导记录(MBR)。但是,MBR还保存硬盘驱动器的分区表。所以,一旦你汇编 loader.s,你必须将它与MBR合并:来自loader.s的代码,来自MBR的分区表。如果您只是将loader.s代码复制到MBR中,就会杀死硬盘驱动器的分区。要正确执行合并,您必须知道MBR中分区表的确切位置...

进入MBR的loader.s输出称为&#34;第一阶段引导加载程序&#34;。由于上述情况,您在第一阶段只有436个字节。此时无法做的一件事就是将一些C编译器输出置于其上(即使您的二进制文件大于一个扇区,即MBR)并将其复制到硬盘驱动器。虽然可能暂时在旧硬盘驱动器上工作,但是现代的硬盘驱动器在第1区以后会携带更多分区信息,这会被复制破坏。

我们的想法是将kernel.c编译成单独的二进制文件,&#34;第二阶段&#34;。 第一个阶段,在可用的436个字节中,然后使用BIOS(或EFI)从硬盘驱动器上的特定点加载第二阶段(因为你赢了&# 39;能够将分区表和文件系统解析添加到第一阶段),然后跳转到刚刚加载的代码。由于第二阶段不受相同类型的大小限制,因此它可以继续执行正确的事情,即解析分区信息,找到&#34; home&#34 ;分区,解析其文件系统,然后加载并解析实际内核二进制文件。

我希望你知道我正在从低地球轨道看这一切。引导加载是涉及过程的一个问题,没有人希望在一个SO帖子中详细说明它。因此,有专门针对这些主题的网站,如OSDev。但要注意:这种开发需要经验丰富的程序员,能够进行专业级研究的人,以聪明的方式提问,并承担自己的重量。由于这些技能现在普遍下降,如果你错误地接近它,操作系统开发网站会有一种脾气暴躁的反应倾向。(*)

(*):或者他们向你扔掉了没有注释的来源,就像dwalter在完成这篇文章时所做的那样。 ; - )

编辑当然,这也不是模拟器冻结的实际原因。 i386-elf-gcc是一个为32位保护模式生成代码的编译器,假设&#34;平坦&#34;存储器模型,即从零开始的代码/数据段。您的loader.s是16位实模式代码(如BITS 16部分所述),激活保护模式,将段寄存器初始化为GCC预期的值,然后继续跳转到GCC在错误假设下生成的代码... BAM。