Page 1 of 1
How to exit to real mode correctly?
Posted: Tue Oct 02, 2012 5:04 am
by cjxgm
My kernel is loaded by GRUB. Then it does a full switch to pmode(e.g. install irq handlers, reset GDT, etc.). But I need it leave pmode for switching video modes by using bios calls. So, I wrote the code:
Code: Select all
; vim: ft=nasm noet ts=4 sw=4 sts=0
[bits 32]
[global bit16_init]
[global bit16_int]
; copy the 16bit codes into 0000:7c00h
;;;; this works correctly.
bit16_init:
mov edi, 7c00h
mov esi, do16_int
mov ecx, do16_int_len
repnz movsb
ret
bit16_int:
cli
jmp 18h:7c00h ; 3 * 8h = 4th descriptor in gdt = code 16-bit
ret
[bits 16]
do16_int:
mov eax, cr0
xor al, 1
mov cr0, eax
jmp dword 0:.real - do16_int + 7c00h ;; no `org' directive can be used in elf
.real:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 7c00h - 1
; disable A20
in al, 92h
xor al, 10b
out 92h, al
sti
mov ax, 0b800h
mov gs, ax
mov al, 'A'
mov ah, 01110000b
mov [gs:0], ax
;;;; things work fine till here
; read key, just for testing the bios call
xor ah, ah
int 16h
;;;; both qemu and bochs crashed before reaching here
mov [gs:0], ax
jmp $
do16_int_len equ $-do16_int
It moves the 16bit codes into 0000:7c00 and then jump to it. But the bios call make bochs and qemu crash.
I wonder if there is anything more to do with leaving pmode. I need you guys' help.
Thanks in advance. I apologize for my bad English.
Re: How to exit to real mode correctly?
Posted: Tue Oct 02, 2012 5:33 am
by bluemoon
cjxgm wrote:But I need it leave pmode for switching video modes by using bios calls
No you don't. Switching from protected mode to real mode is extremely messly, you basically undo everything you(or grub) did from switching to protected mode - and depends on how much to undo, some devices may not work gracefully.
Back to the original goal, there are three scenarios:
A) switch video mode upon OS start.
B) switch video mode when OS is running.
C) Provide full support - ie native driver.
A) there are a few methods:
1. If you using grub, use it for mode switching - grub supported it.
2. Use your own boot code and call BIOS while still in real mode
B) can be avoided by a reboot, it is usually not required until like version 3.0 of your OS, until then you would have a native driver for mode switching and totally forget about BIOS.
C) Native driver is the long term solution, however it require huge effort not only to write code, but testing and maintenance.
For starter, narrow down your supported video card to one or two (I recommend Bochs VGA), and write native driver for it - a SVGA driver with mode switch and work on LFB should be trivial.
Re: How to exit to real mode correctly?
Posted: Tue Oct 02, 2012 6:54 am
by Jezze
If I remember correctly grub legacy does not support that feature without a patch right?
Re: How to exit to real mode correctly?
Posted: Tue Oct 02, 2012 6:56 am
by bluemoon
Yes, the patch is available, so I consider it is a solution.
Re: How to exit to real mode correctly?
Posted: Tue Oct 02, 2012 7:56 am
by egos
My kernel has PM entry point for Multiboot-compliant boot loaders but it uses RM for initialization so it switches to RM when it gets control in PM. It's not a problem. Just be careful when you get info from boot loader. Take all info that you need before you will write something to outside of your kernel image in memory, and so on.
cjxgm, your code is incorrect. Take into account that part of your code changes position, implement all steps described in Intel manuals to switch to RM, and so on. And I think that disabling A20 is not necessary.
Re: How to exit to real mode correctly?
Posted: Wed Oct 03, 2012 6:07 pm
by turdus
Here's my minimalistic solution in my bootloader with a typical usecase. Note that I did not touch the memory 0-0x500 since boot. My 2nd loader works in the first real mode segment (code+stack limited to 0x800-0x7C00), so I switch to prot mode as soon as possible, and but bss outside of that region. Interrupts are enabled only in real mode.
Code: Select all
;*********************************************************************
;* Setup real mode *
;*********************************************************************
macro hardware_realmode
{
USE32
call near hardware_realmodefunc
USE16
}
USE32
hardware_realmodefunc:
cli
;get return address
pop ebp
;save stack pointer
mov dword [hardware_protstack], esp
jmp CODE_BOOT:.back ;load 16 bit mode segment into cs
USE16
.back:
mov eax, CR0
and al, 0FEh ;switching back to real mode
mov CR0, eax
xor ax, ax
mov ds, ax ;load registers 2nd turn
mov es, ax
mov ss, ax
jmp 0:.back2
.back2:
mov sp, word [hardware_realstack]
sti
jmp bp
;*********************************************************************
;* Call a hardware api (BIOS call) to read a sector in prot mode *
;*********************************************************************
;edx:eax sector, edi:pointer
macro hardware_readsector
{
call hardware_readsectorfunc
}
USE32
hardware_readsectorfunc:
push esi
push edi
;load sector somewhere in low memory (first 64k)
add eax, dword [bda_bootsec]
adc edx, dword [bda_bootsec+4]
mov dword [lbapacket.sect0], eax
mov dword [lbapacket.sect1], edx
hardware_realmode
mov ah, byte 42h
mov dl, byte [bda_bootdev]
mov si, lbapacket
int 13h
hardware_protmode
pop edi
push edi
;and copy to addr where it wanted to be (maybe in high memory)
mov esi, dword [lbapacket.addr0]
mov ecx, 128
repnz movsd
pop edi
pop esi
ret
USE16
It may seem inefficient to switch to real mode for every 1KiB, but on real machine it will load a few MiBs in a blink of an eye. If you want a pure real mode solution (with legendary 640k limit): add 32 to es segment for every loaded sector, and have the BIOS load them in the right place.
Re: How to exit to real mode correctly?
Posted: Thu Oct 04, 2012 2:30 am
by egos
And what about initializing IDTR? Multiboot Spec. does not guarantee that IDTR holds valid IVT descriptor. Look at my simple example:
Code: Select all
include "desc.inc"
include "misc.inc"
MBH_MAGIC equ 1BADB002h
MBH_FLAGS equ 10000h
MBH_LOAD_ADDR equ 100000h
MBF_BOOTDEV equ 2
struc MBINFO
{
.flags dd ?
.basemem dd ?
.highmem dd ?
.bootdev dd ?
.paramstr dd ?
.modcount dd ?
.modlist dd ?
}
virtual at 0
MBINFO MBINFO
end virtual
org 8000h
xor bx,bx
cli
mov ss,bx
mov sp,$$
sti
mov ds,bx
jmp 0:@f
align 4
header:
dd MBH_MAGIC
dd MBH_FLAGS
dd -MBH_MAGIC-MBH_FLAGS
dd MBH_LOAD_ADDR-$$+header
dd MBH_LOAD_ADDR
dd MBH_LOAD_ADDR-$$+FS_IMAGE
dd MBH_LOAD_ADDR-$$+FS_IMAGE
dd MBH_LOAD_ADDR-$$+entry32
entry16:
mov ax,DATA16
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
lidt [IDTR]
mov eax,cr0
and al,0FEh
mov cr0,eax
jmp 0:$$
@@:
; mov ax,3
; int 10h
call putstr
db 13,10,"Hello World!",32,0
@@:
mov bx,7
mov ah,0Eh
push si
int 10h
putstr:
pop si
mov al,[si]
inc si
and al,al
jg short @b
@@:
hlt
jmp @b
use32
entry32:
; sub eax,2BADB002h
; jnz $
xor edx,edx
test byte [ebx+MBINFO.flags],MBF_BOOTDEV
jz @f
mov dx,word [ebx+MBINFO.bootdev+2]
xchg dl,dh
inc dh
@@:
cld
mov esi,MBH_LOAD_ADDR
mov edi,$$
mov ecx,(FS_IMAGE-$$)/4
rep movsd
lgdt [GDTR]
jmp CODE16:entry16
align 2
GDTR:
dw GDT_SIZE-1
dd GDT
IDTR:
dw 3FFh
dd 0
align 8
GDT:
dq 0
set CODE16,$-GDT
desc 0,0FFFFh,DF_CODE+DF_DUALACTION
set DATA16,$-GDT
desc 0,0FFFFh,DF_DATA+DF_DUALACTION
set GDT_SIZE,$-GDT
rb 3FFh - ($+1) and 3FFh
dw 0AA55h
label FS_IMAGE