How to exit to real mode correctly?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
cjxgm
Posts: 5
Joined: Sat Apr 30, 2011 12:15 am
Location: Quzhou, Zhejiang, China
Contact:

How to exit to real mode correctly?

Post 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.
Where there is a hacker, there is art. Fork me!
User avatar
bluemoon
Member
Member
Posts: 1761
Joined: Wed Dec 01, 2010 3:41 am
Location: Hong Kong

Re: How to exit to real mode correctly?

Post 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.
User avatar
Jezze
Member
Member
Posts: 395
Joined: Thu Jul 26, 2007 1:53 am
Libera.chat IRC: jfu
Contact:

Re: How to exit to real mode correctly?

Post by Jezze »

If I remember correctly grub legacy does not support that feature without a patch right?
Fudge - Simplicity, clarity and speed.
http://github.com/Jezze/fudge/
User avatar
bluemoon
Member
Member
Posts: 1761
Joined: Wed Dec 01, 2010 3:41 am
Location: Hong Kong

Re: How to exit to real mode correctly?

Post by bluemoon »

Yes, the patch is available, so I consider it is a solution.
egos
Member
Member
Posts: 612
Joined: Fri Nov 16, 2007 1:59 pm

Re: How to exit to real mode correctly?

Post 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.
If you have seen bad English in my words, tell me what's wrong, please.
User avatar
turdus
Member
Member
Posts: 496
Joined: Tue Feb 08, 2011 1:58 pm

Re: How to exit to real mode correctly?

Post 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.
egos
Member
Member
Posts: 612
Joined: Fri Nov 16, 2007 1:59 pm

Re: How to exit to real mode correctly?

Post 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
If you have seen bad English in my words, tell me what's wrong, please.
Post Reply