5

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

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

我目前的问题本质上是当我运行 kernel.bin 文件时 QEMU 冻结了。我猜我的代码有很多问题——也许这个问题并不适合 StackOverflow 格式,因为它非常特殊。我的项目文件如下:

装载机.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

内核.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

希望有人可以帮我看看这个——代码片段不是很长。

谢谢。

4

1 回答 1

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 预期的值,然后在 false 下继续跳转到 GCC 生成的代码假设... BAM。

于 2012-06-12T14:35:23.600 回答