2

我正在尝试使用 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
4

1 回答 1

1

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

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

于 2013-04-20T12:01:08.670 回答