【问题标题】:how do I load my kernel from my bootloader?如何从引导加载程序加载内核?
【发布时间】:2021-10-12 16:51:56
【问题描述】:

我正在尝试制作一个操作系统。我刚刚完成了引导加载程序,但是我在加载内核时遇到了问题。

Boot.asm:

section .boot
bits 16
global boot
boot:
    mov ax, 0x2401
    int 0x15

    mov ax, 0x3
    int 0x10

    mov [disk],dl

    mov ah, 0x2    ;read sectors
    mov al, 6      ;sectors to read
    mov ch, 0      ;cylinder idx
    mov dh, 0      ;head idx
    mov cl, 2      ;sector idx
    mov dl, [disk] ;disk idx
    mov bx, copy_target;target pointer
    int 0x13
    cli
    lgdt [gdt_pointer]
    mov eax, cr0
    or eax,0x1
    mov cr0, eax
    mov ax, DATA_SEG
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    jmp CODE_SEG:boot2
gdt_start:
    dq 0x0
gdt_code:
    dw 0xFFFF
    dw 0x0
    db 0x0
    db 10011010b
    db 11001111b
    db 0x0
gdt_data:
    dw 0xFFFF
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0
gdt_end:
gdt_pointer:
    dw gdt_end - gdt_start
    dd gdt_start
disk:
    db 0x0
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

times 510 - ($-$$) db 0
dw 0xaa55
copy_target:
bits 32
    hello: db "Hello more than 512 bytes world!!",0
boot2:
    mov esi,hello
    mov ebx,0xb8000
.loop:
    lodsb
    or al,al
    jz halt
    or eax,0x0F00
    mov word [ebx], ax
    add ebx,2
    jmp .loop
halt:
    mov esp,kernel_stack_top
    extern kzos
    call kzos
    cli
    hlt

section .bss
align 4
kernel_stack_bottom: equ $
    resb 16384 ; 16 KB
kernel_stack_top:

kzos.cpp

extern "C" void kzos()
{
    const short color = 0x0F00;
    const char* hello = "Kernel test!";
    short* vga = (short*)0xb8000;
    for (int i = 0; i<16;++i)
        vga[i+80] = color | hello[i];
}

我得到的错误是“extern kzos”,错误显示为“boot.asm:77: error: binary output format does not support external references”

我尝试添加“extern kzos.cpp”,但随后我得到“boot.asm:77: error: symbol `kmain' undefined” 如果我在调用函数中添加“.cpp”,我会得到“boot4.asm:77: error: binary output format does not support external references”

我正在使用 Nasm 编译到 bin 和 qemu 来运行它。

【问题讨论】:

  • 请描述您的工具链。什么汇编器,什么编译器等
  • 你没有展示你是如何构建东西的,但是如果你没有将它们链接在一起,那么你需要找到一种方法来自己提供kzos的地址。例如。您可以在内核的开头放置一个指针或跳转。
  • How to make the kernel for my bootloader? 有一个在保护模式下调用 C 的工作示例,从以实模式启动的引导加载程序。您没有准确显示您正在尝试运行哪些命令会生成这些链接错误。
  • 我正在运行 linux mint、nasm 和 qemu。我使用的命令是:nasm -fbin boot.asm -o boot.bin.

标签: c++ assembly x86-16 bootloader


【解决方案1】:

输出格式为bin,平面格式,即

不支持重定位

因此call kzos 的位置/地址在代码渲染后丢失
任何链接实用程序都将无法按顺序定位 call kzos'
从 kzos.cpp 将其链接到 extern "C" void kzos() 的新地址

【讨论】:

    【解决方案2】:

    我刚刚完成了引导加载程序,...

    不,你没有。至少有一半的引导加载程序代码用于处理“不应该”发生的错误(例如检查 BIOS 是否无法启用 A20,或者 BIOS 表示从磁盘读取数据时出现问题)并以某种方式处理这些情况- 至少,通过提供可以帮助他们解决问题的用户信息(并确定它是硬件故障还是操作系统安装方式的问题或......),所以用户不会想知道为什么他们整台计算机都无法使用(这样您就不会遇到错误报告说“无法启动”而没有任何有用信息)。

    如何从引导加载程序加载内核?

    查找内核所在位置(其在磁盘上的位置及其大小)的选择是:

    a) 将内核放入某种普通文件系统中,并让引导加载程序支持该文件系统(例如,找到正确的目录条目并从文件的目录条目中获取文件数据的位置)。请注意,这很复杂(例如,您将有很多错误处理,以防文件系统的结构损坏等)

    b) 将内核放置在某种目的设计的文件系统中或某种目的设计的普通文件系统的“特殊/保留区域”中。这可以像存储在第一个扇区中的“偏移量和长度”结构表一样简单,其中内核文件始终是该表中的第一个条目。

    c) 使用类似 NASM 中的incbin 指令将内核的二进制文件直接包含到引导加载程序中。在这种情况下,您可以使用标签来确定内核文件的大小,例如:

    kernel_start:
        incbin 'kernel.bin'
    kernel_end:
    

    在这种情况下,您可以根据引导加载程序在磁盘上的位置确定内核在磁盘上的位置,并计算它有多少扇区(例如(kernel_end - kernel_start + SECTOR_SIZE-1)/SECTOR_SIZE)。当然,这是非常不灵活的(例如,如果不组装然后重新安装引导加载程序,您将无法轻松更新内核)。

    一旦您确定了内核在磁盘上的位置和大小;你需要将它加载到内存中的某个地方。请注意,这可能取决于您选择用于内核的可执行文件格式;并且可能涉及加载可执行文件的标头并对其进行解析以找出文件的哪些部分应该放在内存中的位置(并设置文件中不存在的诸如“.bss”之类的内容)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-10
      • 2015-06-02
      • 2012-03-02
      • 1970-01-01
      • 1970-01-01
      • 2015-01-19
      • 2011-02-01
      • 2020-05-31
      相关资源
      最近更新 更多