c - 汇编和 C 中的内核开发

标签 c assembly x86 osdev bochs

我正在尝试使用 bochs 在 Linux 上使用汇编和 C 创建一个简单的内核。每次我尝试从汇编代码调用 c 代码时,仿真器都会给我一个错误并重置模拟。当我以 16 位实模式启动时,我使用这段代码

global _start

[bits 16]

_start:

mov [BOOT_DRIVE] , dl

mov bp , 0x7000
mov sp , bp

mov bx , 0x0000 ;load 5 sectors to 0x0000(ES):0x9000(BX)
mov es , bx
mov bx , KERNEL_OFFSET
mov dh , 15
mov dl , [BOOT_DRIVE]
call disk_load

mov dx , [es:KERNEL_OFFSET]
call print_hex

call switch_to_pm

jmp $

切换到保护模式后,这是代码

[bits 32]
extern kmain

begin_pm:

;print a char to vram for testing and it is printed, this means the switch has suceeded
mov ebx , VRAM_ADDRESS
mov al , 'H'
mov [ebx] , al
mov al , 0x07
inc ebx
mov [ebx] , al

call kmain

jmp $

而 c 函数基本上什么都不做,只是一个空函数

关于我正在使用的命令我用这些

nasm $asm_file_name.asm -f elf -o $asm_file_name.o
gcc -ffreestanding -c $c_file_name.c -o $c_file_name.o
ld -o $c_file_name.bin -Ttext 0x7c00 $asm_file_name.o $c_file_name.o --oformat binary
dd status=noxfer conv=notrunc if=$c_file_name.bin of=$floppy_name.img

我不知道问题出在哪里。有什么想法吗?

注意: 我正在使用以下 GDT 和 switch_to_pm 函数

    ;GDT
gdt_start:

    gdt_null:
        dd 0x0
        dd 0x0

    gdt_code: ;the code segment descriptor
        ; base = 0x0 , limit = 0xfffff ,
        ; 1 st flags : ( present )1 ( privilege )00 ( descriptor type )1 -> 1001 b
        ; type flags : ( code )1 ( conforming )0 ( readable )1 ( accessed )0 -> 1010 b
        ; 2 nd flags : ( granularity )1 (32 - bit default )1 (64 - bit seg )0 ( AVL )0 -> 1100 b
        dw 0xffff
        ; Limit (bits 0 -15)
        dw 0x0
        ; Base (bits 0 -15)
        db 0x0
        ; Base ( bits 16 -23)
        db 10011010b ; 1st flags , type flags
        db 11001111b ; 2nd flags , Limit (bits 16 -19)
        db 0x0
        ; Base ( bits 24 -31)

    gdt_data: ; the data segment descriptor
        ; Same as code segment except for the type flags :
        ; type flags : ( code )0 ( expand down )0 ( writable )1 ( accessed )0 -> 0010 b
        dw 0xffff
        ; Limit ( bits 0 -15)
        dw 0x0
        ; Base ( bits 0 -15)
        db 0x0
        ; Base ( bits 16 -23)
        db 10010010b ; 1 st flags , type flags
        db 11001111b ; 2 nd flags , Limit ( bits 16 -19)
        db 0x0
        ; Base ( bits 24 -31)

    gdt_end:
    ; The reason for putting a label at the end of the
    ; GDT is so we can have the assembler calculate
    ; the size of the GDT for the GDT decriptor ( below )
    ; GDT descriptior
    gdt_descriptor:
        dw gdt_end - gdt_start - 1
        dd gdt_start

    ; Size of our GDT , always less one
    ; of the true size
    ; Start address of our GDT
    ; Define some handy constants for the GDT segment descriptor offsets , which
    ; are what segment registers must contain when in protected mode. For example ,
    ; when we set DS = 0 x10 in PM , the CPU knows that we mean it to use the
    ; segment described at offset 0 x10 ( i.e. 16 bytes ) in our GDT , which in our
    ; case is the DATA segment (0 x0 -> NULL ; 0 x08 -> CODE ; 0 x10 -> DATA )
    CODE_SEG equ gdt_code - gdt_start
    DATA_SEG equ gdt_data - gdt_start
[bits 16]

switch_to_pm:

cli
lgdt[gdt_descriptor]

mov eax , cr0
or eax , 0x1
mov cr0 , eax

jmp CODE_SEG:init_pm

[bits 32]

init_pm:

mov ax , DATA_SEG
mov ds , ax
mov ss , ax
mov es , ax
mov fs , ax
mov gs , ax

mov ebp , 0x9000
mov esp , ebp

jmp begin_pm

最佳答案

call kmain 将尝试在该地址找到函数,就好像整个代码是在 0x7c00 处加载的一样。但是,整个代码从磁盘加载到 0x9000。因此,您需要要求链接器修复它用作 kmain 的地址。

或者甚至更好,正如@AlexeyFrunze 所建议的那样,从第二个扇区开始将代码加载到内存中紧跟在 BIOS 加载第一个扇区之后的位置。

关于c - 汇编和 C 中的内核开发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16119534/

相关文章:

assembly - x86汇编寄存器和mov指令以及跳转偏移量

char **s 与 char *s[],何时以及如何使用它们?

c - C 中 if/else 语句的问题

c - 设置硬件外设中的寄存器

程序集弹出空堆栈

assembly - x86 函数必须保留哪些寄存器?

visual-studio-2008 - 如何让我的 VS2008 x86 安装程序在 x64 上安装 x64 程序集?

c - 全局变量的意外初始化

assembly - 在 YASM 或 NASM 程序集中执行退格键

assembly - 二进制补码快速方法