Having trouble switching back to real mode

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
jrh2365
Posts: 3
Joined: Thu Jan 27, 2011 7:36 am

Having trouble switching back to real mode

Post by jrh2365 »

I'm trying to switch from protected mode to real mode. The ouput I'm currently getting is "write_virtual_checks(): write beyond limit, r/w" repeatedly.

I am using GRUB and El-Torito to make a bootable CD.

Here is my code. GRUB loads my kernel at 0x100000. For testing, I'm trying to move a 'hlt' (0xF4) instruction below the 1 MB mark (currently using 0x7A00), and then jump to it into real mode. _main just installs the GDT, based on http://www.osdever.net/bkerndev/Docs/gdt.htm

Code: Select all

	extern _main
	call _main

	mov byte [0x7A00], 0xF4
	
	cli
	
	mov		eax, 0x20 ; real mode data segment
	mov		ds, eax
	mov		es, eax
	mov		fs, eax
	mov		gs, eax
	mov		ss, eax
	
; clear Protect Enable bit
	mov		eax, cr0
	and		al, ~0x01
	mov		cr0, eax
	
; jmp into real mode at 0x7A00 somehow?
	;db 0xEA
	;dw 0x7A00
	;dw 0x18
	jmp 0x18:0x7A00
MY GDT is filled like this, basing the real mode segments on http://www.sudleyplace.com/pmtorm.html

Code: Select all

	gdt_set_gate(0, 0, 0, 0, 0);				// 0x00
	gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // 0x08 - code segment
	gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // 0x10 - data segment
	gdt_set_gate(3, 0, 0x000FFFFF, 0x9A, 0x0F);	// 0x18 - real mode code segment
	gdt_set_gate(4, 0, 0x000FFFFF, 0x92, 0x0F);	// 0x20 - real mode data segment
The 'info gdt' command in Bochs give me the following:

Code: Select all

Global Descriptor Table (base=0x0000000000104000, limit=39):
GDT[0x00]=??? descriptor hi=0x00000000, lo=0x00000000
GDT[0x01]=Code segment, base=0x00000000, limit=0xffffffff, Execute/Read, Accessed, 32-bit
GDT[0x02]=Data segment, base=0x00000000, limit=0xffffffff, Read/Write, Accessed
GDT[0x03]=Code segment, base=0x00000000, limit=0x000fffff, Execute/Read, 16-bit
GDT[0x04]=Data segment, base=0x00000000, limit=0x000fffff, Read/Write, Accessed
From what I can tell, GRUB does not modify the IDT, so it should already be how I need it.


What am I doing wrong? Thank you.

[EDIT]
Just one more little bit of additional information: printing the memory at 0x7A00 shows that the 'hlt' instruction is there, I'm just not getting there properly.
x /2bx 0x7A00
0x0000000000007a00 <bogus+ 0>: 0xf4 0x00
User avatar
xenos
Member
Member
Posts: 1121
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: Having trouble switching back to real mode

Post by xenos »

jrh2365 wrote:

Code: Select all

	jmp 0x18:0x7A00
I guess you need a real mode segment:offset address here - your protected mode selector 0x18 won't work anymore.
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Having trouble switching back to real mode

Post by Combuster »

Disabling PE in 32-bit mode locks up actual processors (but not bochs) and really should not be done. What you should do instead is the following:

- clear interrupts
- restore hardware to real-mode defaults
- far jump to 16-bit protected mode code segment with a 64k limit
- reload segment registers with a 64k limit (and not with a limit of 1M like you do now!)
- disable PE
- far jump again to make the segment register match the base (like XenOS said, you are in real mode now)
- reload remaining segment registers
- enable interrupts (note that any bios call might try to enable them so everything should be all set before that)
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
jrh2365
Posts: 3
Joined: Thu Jan 27, 2011 7:36 am

Re: Having trouble switching back to real mode

Post by jrh2365 »

Thanks, it seems to be working correctly now :). Does this seem correct to you?

Code: Select all

	; install gdt
	...

	cli
	
	; data segment (16 bit)
	mov ax, 0x20
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax	
	
	; code segment (16 bit)
	jmp 0x18:0x7A00

Code: Select all

	[BITS 16]
	org 0x7A00

	; disable PE
	mov eax, cr0
	and al, ~0x01
	mov cr0, eax

	xor eax, eax
	mov ds, eax
	mov es, eax
	mov fs, eax
	mov gs, eax
	mov ss, eax

	jmp 0x00:RealMode
	RealMode:
	sti
	...
GDT:

Code: Select all

	gdt_set_gate(0, 0x00000000, 0x00000000, 0x00, 0x00);	// 0x00
	gdt_set_gate(1, 0x00000000, 0xFFFFFFFF, 0x9A, 0xCF);	// 0x08 - code segment
	gdt_set_gate(2, 0x00000000, 0xFFFFFFFF, 0x92, 0xCF);	// 0x10 - data segment
	gdt_set_gate(3, 0x00000000, 0x0000FFFF, 0x9A, 0x0F);	// 0x18 - code segment (16 bit)
	gdt_set_gate(4, 0x00000000, 0x0000FFFF, 0x92, 0x0F);	// 0x20 - data segment (16 bit)
Though I should probably find a better way to load that 16 bit code below the 64k mark :roll: (any suggestions?):

Code: Select all

	mov dword [0x7A00], 0x24C0200F
	mov dword [0x7A04], 0xC0220FFE
	mov dword [0x7A08], 0x8EC03166
	mov dword [0x7A0C], 0x8EC08ED8
	mov dword [0x7A10], 0x8EE88EE0
	mov dword [0x7A14], 0x7A1AEAD0
	mov dword [0x7A18], 0xBEFB0000
	mov dword [0x7A1C], 0x0EB47A2B
	mov dword [0x7A20], 0x74C008AC
	mov dword [0x7A24], 0xEB10CD04
	mov dword [0x7A28], 0x48F4FAF7
	mov dword [0x7A2C], 0x6F6C6C65
	mov dword [0x7A30], 0x726F5720
	mov dword [0x7A34], 0x0021646C
Thanks again for the help.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Having trouble switching back to real mode

Post by Combuster »

A hack to include (real mode) assembly code inside something else, is to just put in a block, and subtract a constant (start_of_code_label - where_code_should_run) from each memory access. You can then just copy the assembled code as data to the right place and it works

Writing position-independent code works too.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
jrh2365
Posts: 3
Joined: Thu Jan 27, 2011 7:36 am

Re: Having trouble switching back to real mode

Post by jrh2365 »

Thanks. I'm now subtracting a constant from the memory accesses, and moving the code over as shown below. Much nicer :)

Code: Select all

ALIGN 4
RealMode_Start:
...
ALIGN 4
RealMode_End:

Code: Select all

mov esi, RealMode_Start
mov edi, 0x7A00
mov ecx, (RealMode_End - RealMode_Start) / 4
rep movsd
Post Reply