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

简单C引导程序/内核的问题,c,x86,kernel,nasm,osdev,C,X86,Kernel,Nasm,Osdev,最近我开始对编写自己的非常基本的操作系统感兴趣。 我写了(好吧,复制了)一些基本的程序集,建立了一个堆栈,做了一些基本的事情,这看起来很好,但是试图将C引入到混合中却把一切都搞砸了 我有两个主要的项目文件:loader.s(它是创建堆栈并调用我的C函数的NASM)和kernel.C(它包含基本的C函数) 我目前的问题是,当我运行kernel.bin文件时,QEMU会冻结。我猜我的代码有很多错误——也许这个问题不适合StackOverflow格式,因为它非常特殊。我的项目文件如下: loader.

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

我有两个主要的项目文件:loader.s(它是创建堆栈并调用我的C函数的NASM)和kernel.C(它包含基本的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
#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)
}
kernel.c:

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
#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)
}
#包括
无效(无效)
{
无符号字符*vidmem=(字符*)0xB8000;//视频内存地址
vidmem[0]=65;//字符“A”
vidmem[1]=0x07;//浅灰色(7)在黑色(0)上
}
我把所有的东西都编译成这样:

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

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

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

然后进行如下测试:

qemu-system-x86_64-kernel.bin

希望有人能帮我看一下——代码片段并不长

谢谢。

天哪,从哪里开始?(瑞斯,是你吗?)

loader.s
中的代码进入主引导记录(MBR)。然而,MBR也保存硬盘驱动器的分区表。因此,一旦组装了
loader.s
,就必须将其与MBR合并:来自
loader.s
的代码,来自MBR的分区表。如果您只是将
loader.s
代码复制到MBR中,则会终止硬盘驱动器的分区。为了正确地进行合并,您必须知道分区表在MBR中的确切位置

进入MBR的
loader.s
的输出称为“第一阶段引导加载程序”。由于上述原因,在第一阶段中只有436个字节。在这一点上,你不能做的一件事是在上面加上一些C编译器输出(即使二进制文件大于一个扇区MBR),然后将其复制到硬盘上。虽然它可能会在旧硬盘上暂时工作,但现代硬盘在扇区1之后会携带更多的分区信息,这些信息会被复制破坏

其思想是将
kernel.c
编译成一个单独的二进制文件,即“第二阶段”。第一阶段(436字节可用)使用BIOS(或EFI)从硬盘上的特定点加载第二阶段(因为无法将分区表和文件系统解析添加到第一阶段),然后跳转到刚刚加载的代码。由于第二阶段不受相同的大小限制,因此它可以继续做正确的事情,即解析分区信息,找到“主”分区,解析其文件系统,然后加载和解析实际的内核二进制文件

我希望你们知道我是在低地球轨道上观察这一切的。Bootloading是一个非常复杂的过程,没有人希望在一次发布中详细介绍它。因此,有专门针对这些主题的网站,如。但需要注意的是:这种开发需要有经验的程序员、能够进行专业级研究、以明智的方式提出问题并承担自己的责任的人。由于这些技能现在普遍下降,如果你处理不当,操作系统开发网站会有脾气暴躁的倾向。(*)

(*):或者他们向你扔未注释的源代码,就像我刚写完这篇文章时dwalter所做的那样。;-)


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

那不是应该是平面二进制吗?什么解析/加载ELF文件?BIN是一种平面二进制格式,所以我猜是这样的?我不知道。我只是把我的垃圾桶直接扔给QEMU,告诉它是一个使用“-kernel”的内核(因为把它当作一个完整的内核和引导加载程序,而不使用“-kernel”和将magic boot sig用于512字节的段是不可行的,因为某些原因,即使在我看来它应该这样做),并抱着最好的希望(这显然不适合我).这段代码看起来很熟悉,有可能是《赤骨教程》的一部分吗?有一部分是基于这段代码的,是的。[作为旁注:如果我没记错的话,使用那里的确切代码实际上产生了与我在使用该代码时遇到的问题相同或类似的问题——但我怀疑这可能是一个环境问题,因为这可能表明,我在其他机器上尝试过BIN fine(以及该教程中的代码的IMG),它也有同样的问题]@joesavage:OSDev裸体教程基于这样一个假设,即您在第一和第二阶段使用GRUB引导加载程序(如教程本身所述)。因此,这里没有涉及16位代码,链接器脚本的目标是生成一个ELF文件——也就是说,该教程针对的是一个与“第一阶段”方法完全不同的环境。@DevSolar:你说得对,我这边的未记录代码没有多大帮助。我将删除它,并可能将其与文档一起重新发布。或者OP应该看看。@Joesavage1:资源建议是