根据这个教程,创建一个简单的操作系统并切换到保护模式非常简单,只需要使用以下代码而无需执行其他已知操作,例如启用A20……
无论如何,我是这个领域的新手,我按照他们的说明编写了以下代码,并根据此SO所提供的修改进行了修改。
代码结构: 这个简单的操作系统应该按以下方式简要加载:
1. 加载/读取15个扇区 2. 启用GDT 3. 切换到保护模式(并打印“成功进入32位保护模式”) 4. 加载内核并打印“X”
然而,仿真器仍在重新启动。请查看完整的代码。
bootloader.asm
为了确保我们进入了受保护模式: writing_video_mode.asm
为了运行它,我正在使用带有以下命令的qemu:
无论如何,我是这个领域的新手,我按照他们的说明编写了以下代码,并根据此SO所提供的修改进行了修改。
代码结构: 这个简单的操作系统应该按以下方式简要加载:
1. 加载/读取15个扇区 2. 启用GDT 3. 切换到保护模式(并打印“成功进入32位保护模式”) 4. 加载内核并打印“X”
然而,仿真器仍在重新启动。请查看完整的代码。
bootloader.asm
[bits 16]
[org 0x7C00]
KERNEL_OFFSET equ 0x1000
xor ax, ax
mov ds, ax
mov es, ax
mov [BOOT_DRIVE], dl
mov ax, 0x07E0 ; End of stack
cli
mov ss, ax
mov sp, 0x1200 ; Size of Stack. By this, we assume that stack starts at 9000h
; of size 1200h and ends at 7E00h to avoid being overwritten.
sti
call load_kernel
call switch_to_pm
jmp $
%include "src/functions/disk_load.asm"
load_kernel:
mov bx, KERNEL_OFFSET
mov dh, 15
mov dl, [BOOT_DRIVE]
call disk_load
ret
; Global variables
BOOT_DRIVE db 0
SECTORS db 0
MSG_PROT_MODE db "Successfully landed in 32-bit Protected Mode" , 0
%include "src/functions/gdt.asm"
%include "src/functions/switch_to_pm.asm"
[ bits 32]
; This is where we arrive after switching to and initialising protected mode.
BEGIN_PM:
mov ebx , MSG_PROT_MODE
call print_string_pm ; Use our 32 - bit print routine.
;call KERNEL_OFFSET ; Now jump to the address of our loaded
; kernel code , assume the brace position ,
; and cross your fingers. Here we go !
jmp $ ; Hang.
%include "src/functions/writing_video_mode.asm"
; Bootsector padding
times 510-($-$$) db 0
dw 0xAA55
; 15 sector padding
times 15*256 dw 0xDADA
disk_load.asm
disk_load:
mov [SECTORS], dh
mov ch, 0x00 ;C=0
mov dh, 0x00 ;H=0
mov cl, 0x02 ;S=2
next_group:
mov di, 5 ;Max 5 tries
again:
mov ah, 0x02 ;Read sectors
mov al, [SECTORS]
int 0x13
jc maybe_retry
sub [SECTORS], al ;Remaining sectors
jz ready
mov cl, 0x01 ;Always sector 1
xor dh, 1 ;Next head on diskette!
jnz next_group
inc ch ;Next cylinder
jmp next_group
maybe_retry:
mov ah, 0x00 ;Reset diskdrive
int 0x13
dec di
jnz again
jmp disk_error
ready:
ret
disk_error:
mov ah, 0x0e
mov al, 'Y'
int 0x10
jmp $
DISK_ERROR_MSG db "Disk read error!", 0
gdt.asm
gdt_start:
gdt_null:
dd 0x0 ; ’ dd ’ means define double word ( i.e. 4 bytes )
dd 0x0
gdt_code:
dw 0xffff
dw 0x0
db 0x0
db 10011010b ; 1 st flags , type flags
db 11001111b ; 2 nd flags , Limit ( bits 16 -19)
db 0x0
gdt_data:
dw 0xffff
dw 0x0
db 0x0
db 10010010b ; 1 st flags , type flags
db 11001111b ; 2 nd flags , Limit ( bits 16 -19)
db 0x0
gdt_end:
gdt_descriptor:
dw gdt_end - gdt_start - 1
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
switch_to_pm.asm
[ 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 , 0x90000
mov esp , ebp
call BEGIN_PM
为了确保我们进入了受保护模式: writing_video_mode.asm
[ bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
print_string_pm:
push eax
push ebx
push edx
mov edx , VIDEO_MEMORY ; Set edx to the start of vid mem.
print_string_pm_loop:
mov al, [ebx]
mov ah, WHITE_ON_BLACK
cmp al, 0
je print_string_pm_done
mov [edx], ax
add ebx, 1
add edx, 2
jmp print_string_pm_loop
print_string_pm_done:
pop edx
pop ebx
pop eax
ret
kernel.c
void main () {
char * video_memory = (char *) 0xb8000;
*video_memory = 'X';
}
顺便说一下,我正在使用这个Makefile:
all: bootloader.bin kernel.bin
bootloader.bin: src/bootloader.asm
nasm src/bootloader.asm -f bin -o output/bootloader.bin
kernel.o: src/kernel/kernel.c
gcc -ffreestanding -c src/kernel/kernel.c -o output/kernel.o -m32
kernel.bin: kernel.o
ld -o output/kernel.bin -Ttext 0x1000 --oformat binary output/kernel.o -melf_i386
clean:
rm -f output/*.* output/*
为了将它移动到闪存中,我使用以下命令:
cat output/bootloader.bin output/kernel.bin > os-image
sudo dd if=os-image of=/dev/sdb bs=512 conv=notrunc && sync
为了运行它,我正在使用带有以下命令的qemu:
qemu-system-i386 -hda /dev/sdb
注意:/dev/sdb是我的闪存驱动器。
问题:
实际上,当在bootloader.asm
中禁用/注释call KERNEL_OFFSET
时,代码会进入保护模式(即打印“成功进入32位保护模式”)。但是启用此行时,它开始引导和重启。
我希望我已经提供了所有必要的信息。对我来说,远程跳转不应该以这种方式完成。任何评论都将不胜感激。
bochs
中的调试器)来查看发生了什么。 - Jestertimes 15*256 dw 0xDADA
。你创建了一个具有 512 字节引导扇区的映像。然后,你用另外 15 个扇区进行填充,最后使用cat output/bootloader.bin output/kernel.bin > os-image
将两者连接起来。你的内核将被放置在前 16 个扇区之外。 - Michael Petch