我学会了如何使用简单的基于DOS的引导加载程序切换到保护模式。该加载程序将kernel.bin
加载到缓冲区中,并将缓冲区复制到100000h(内核为8KiB)。然后将控制权转移到内核。当我从内核返回并尝试切换到实模式时,遇到了问题。
我的FASM汇编代码(类似于我之前的Stackoverflow问题)如下:
format MZ
push cs
pop ds
mov eax,cs
shl eax,4
mov [AdresSegmentuProgramu_32],eax ;Calculating real mode segment
add eax,gdt_table
mov [gdtr+2],eax
mov ax,[AdresSegmentuProgramu_32+2]
mov [code_realsegment_descriptor+3],ax
mov al,[AdresSegmentuProgramu_32+1] ;Setting 16-bit descriptors to return from protected mode
mov [code_realsegment_descriptor+2],al
mov ax,[AdresSegmentuProgramu_32+2]
mov [data_realsegment_descriptor+3],ax
mov al,[AdresSegmentuProgramu_32+1]
mov [data_realsegment_descriptor+2],al
mov ax,3d00h
mov dx,NazwaPliku
int 21h
mov bx,ax
mov ax,3f00h
mov cx,8192
mov dx,KernelGDOS32
int 21h
mov ax,3e00h
int 21h
lgdt [gdtr]
mov eax,[AdresSegmentuProgramu_32]
add eax, pmode_entry ;Far jump to reset CS and jump to simple code
mov [AdresSegmentu_PMODE_entry],eax
mov [far_jump],eax
mov eax,[AdresSegmentuProgramu_32]
add eax,KernelGDOS32
mov [AdresSegmentu_KernelGDOS_32],eax
cli ; Turn off interrupts
mov eax,cr0
or eax,1 ;Switch to PMODE
mov cr0,eax
; Move code that was between turning on protectedmode bit and the FAR JMP
; to an earlier point.
; We need to do a FAR JMP but with a 32-bit offset and a 16-bit selector
; rather than a 16-bit offset and 16-bit selector. A 16-bit offset will
; not work for any address greater than 64KiB. Use FWORD modifier to allow
; a 32-bit offset in the indirect address for the JMP FAR.
; FWORD = 48 bit address (32+16)
mov edi,[AdresSegmentu_KernelGDOS_32]
jmp far fword [ds:far_jump]
far_jump:
dd 0
dw 08h ; Selector 0x08
gdtr: dw 128
dd 0
ldtr:
dw 0x3ff
dd 0
AdresSegmentuProgramu_32 : dd 0
AdresSegmentu_PMODE_entry dd 0
AdresSegmentu_KernelGDOS_32 dd 0
NazwaPliku db 'kernel.bin',0
use32
align 4
gdt_table:
dq 0 ;NULL Descriptor
code_descriptor:
dw 0ffffh
dw 0 ;8h
db 0
db 09ah
db 11001111b
db 0
data_descriptor: ;I prefer flat mode. There are two descriptors for data and code points to 0.
dw 0ffffh
dw 0
db 0 ;10h
db 092h
db 11001111b
db 0
stack_descriptor:
dw 0ffffh
dw 0
db 0 ;18h
db 096h
db 11001111b
db 0
code_realsegment_descriptor:
dw 0ffffh ;20h
dw 0
db 0
db 09ah
db 10001111b
db 0
data_realsegment_descriptor:
dw 0ffffh
dw 0
db 0
db 092h ;28h
db 10001111b
db 0
stack_realsegment_descriptor : dq 0
TIMES 8 dq 0
KernelGDOS32 : TIMES 8192 db 0 ;Jądro naszego systemu który zostanie załadowany
do obszaru pamięci powyżej 1 MB.
pmode_entry:
;this section works correctly
mov ebx,100000h
mov ax, 10h ;Set DS to a 32-bit data selector
mov ds, ax
mov ax,18h ;Set SS to a 32-bit stack selector
mov ss,ax
mov esi,100000h
xor cx,cx
petla_glowna:
mov al,[edi]
mov [esi],al
inc esi
inc edi
cmp cx,8192
inc cx
jne petla_glowna
mov esp,200000h
call dword ebx
mov eax,0
mov cr3,eax
use16
;but i want to switch to real mode cpu generates protection fault
;I was reading osdev.org instructions and still nothing
mov ax,0x28
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
mov sp,16000
jmp far 0x20:entry16
entry16:
mov eax,cr0
mov eax,0 ;back to real mode
mov cr0,eax
jmp dword 0:rmode ;far jump
rmode:
mov sp,16000
mov ax,0
mov ds,ax
mov es,ax
mov fs,ax
mov es,ax
mov ss,ax
lidt [ldtr]
sti
mov ax,4c00h ;Exit process
int 21h