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

5 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
Run Code Online (Sandbox Code Playgroud)

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)
}
Run Code Online (Sandbox Code Playgroud)

我编译所有内容如下:

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

希望有人可以为我看一下 - 代码片段不是很长.

谢谢.

Dev*_*lar 10

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

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

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

这个想法是你编译kernel.c成一个单独的二进制文件,即"第二阶段".在第一阶段,在现有的436个字节,然后使用BIOS(或EFI)来加载从硬盘驱动器上的特定点(第二阶段,因为你将不能够分区表和文件系统解析添加到第一阶段),然后跳转到刚刚加载的代码.由于第二阶段不在同一种大小限制之下,它可以继续做正确的事情,即解析分区信息,找到"home"分区,解析其文件系统,然后加载并解析实际内核二进制.

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

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

编辑:当然,这都不是模拟器冻结的实际原因.i386-elf-gcc是一个为32位保护模式生成代码的编译器,假设是"平坦"的内存模型,即从零开始的代码/数据段.你loader.s是16位实模式代码(如BITS 16部分所述),它不会激活保护模式,也不会将段寄存器初始化为GCC所期望的值,然后继续跳转到由GCC生成的代码.假设...... BAM.