我试图在linux上使用bochs使用汇编和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加载第一个扇区之后的内存位置。