Robinson's GDT trick: VirtualBox crashes, Bochs works

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
Virtlink
Member
Member
Posts: 34
Joined: Thu Jun 05, 2008 3:53 pm
Location: The Netherlands
Contact:

Robinson's GDT trick: VirtualBox crashes, Bochs works

Post by Virtlink »

I'm confused about the GDT trick and accessing physical memory (such as video RAM) before enabling paging. When I immediately enable paging and load the final GDT, everything is just fine. But because I want to access all physical memory at the start, I don't want to enable paging just yet.

If I don't immediate enable paging, I can't even write a single character to the video memory. It works in Bochs, but somehow I manage to crash the VMWare Player and VirtualBox emulators. The latter gives VERR_EM_INTERNAL_DISAS_ERROR and VERR_REM_VIRTUAL_CPU_ERROR Guru Meditation errors in the log.

The relevant bits from my linker script:

Code: Select all

ENTRY(KernelEntry)
SECTIONS
{
	. = 0x00100000;
	.multiboot ALIGN(0x1000) :
	{
		*(.multiboot)
	}
	.setup :
	{
		*(.setup)
	}
	. += 0xC0000000;
	.text ALIGN(0x1000) : AT(ADDR(.text) - 0xC0000000)
	{
		*(.text)
		*(.gnu.linkonce.t*)
	}
	...
This is the relevant ASM code:

Code: Select all

[BITS 32]
section .setup
global Temporary_GDT
Temporary_GDT:
	dw gdt_end - gdt - 1			; The size of the GDT
	dd gdt							; The linear address of the GDT

gdt:
	dd 0, 0							; NULL GDT
	; Code selector 0x08: base 0x40000000, limit 0xFFFFFFFF, type 0x9A, granularity 0xCF
	db 0xFF, 0xFF, 0, 0, 0, 10011010b, 11001111b, 0x40
	; Data selector 0x10: base 0x40000000, limit 0xFFFFFFFF, type 0x92, granularity 0xCF
	db 0xFF, 0xFF, 0, 0, 0, 10010010b, 11001111b, 0x40
gdt_end:

section .text
	cli
	lgdt [Temporary_GDT]
	mov ax, 0x10
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax
	jmp 0x08:HigherHalf
HigherHalf:
	mov esp, KernelStack
	call KernelMain
	; ...
And this is where everything fails except in Bochs, where it prints a white 'A' at the top-left corner of the screen:

Code: Select all

extern "C" void KernelMain()
{
	// Normal VideoRAM address minus 0x40000000, the fake GDT base; or: 0xC00B8000
	uint16* videoRam = (uint16*)(0xB8000 - 0x40000000);
	videoRam[0] = 0x0F00 + 'A';
	while (true);
}

When I postpone writing anything to the screen until after I've enabled paging and the new GDT, then everything works just fine. I don't want to enable paging just yet. What am I doing wrong?
Last edited by Virtlink on Thu Apr 25, 2013 9:11 am, edited 2 times in total.
Virtlink
Member
Member
Posts: 34
Joined: Thu Jun 05, 2008 3:53 pm
Location: The Netherlands
Contact:

Re: Fake GDT memory access problems

Post by Virtlink »

This is the state from Bochs (in which it works), but the state from VirtualBox is the same:

Code: Select all

CPU is in protected mode (active)
CS.mode = 32 bit
SS.mode = 32 bit
EFER   = 0x00000000
| EAX=00000f55  EBX=0002cd80  ECX=0000001f  EDX=c00b8000
| ESP=c010cfe0  EBP=c010cff8  ESI=0002cd80  EDI=2badb002
| IOPL=0 id vip vif ac vm rf nt of df if tf SF zf AF PF CF
| SEG sltr(index|ti|rpl)     base    limit G D
|  CS:0008( 0001| 0|  0) 40000000 ffffffff 1 1
|  DS:0010( 0002| 0|  0) 40000000 ffffffff 1 1
|  SS:0010( 0002| 0|  0) 40000000 ffffffff 1 1
|  ES:0010( 0002| 0|  0) 40000000 ffffffff 1 1
|  FS:0010( 0002| 0|  0) 40000000 ffffffff 1 1
|  GS:0010( 0002| 0|  0) 40000000 ffffffff 1 1
| EIP=c0101a9f (c0101a9f)
| CR0=0x60000011 CR2=0x00000000
| CR3=0x00000000 CR4=0x00000000
I can overwrite the first byte of my kernel (at virtual 0xC0100000, physical 0x100000), but I get the same fault when I try to overwrite the byte just before it (at virtual 0xC00FFFFF, physical 0xFFFFF). Of course, there may not be valid memory there just before my kernel, I'm not sure.

Any suggestions as to what the problem might be, or what I should test to find out?
greyOne
Member
Member
Posts: 58
Joined: Sun Feb 03, 2013 10:38 pm
Location: Canada

Re: Fake GDT memory access problems

Post by greyOne »

Does this thing here look familiar?

Code: Select all

*(.rodata)
Virtlink
Member
Member
Posts: 34
Joined: Thu Jun 05, 2008 3:53 pm
Location: The Netherlands
Contact:

Re: Fake GDT memory access problems

Post by Virtlink »

Yes, and I have it in my linker script somewhere. How is .rodata related to not being able to write to video memory?

Code: Select all

ENTRY(KernelEntry)

VIRT_BASE = 0xC0000000;

SECTIONS
{
	. = 0x00100000;

	.multiboot ALIGN(0x1000) :
	{
		*(.multiboot)
	}

	.setup :
	{
		*(.setup)
	}

	. += VIRT_BASE;

	.text ALIGN(0x1000) : AT(ADDR(.text) - VIRT_BASE)
	{
		*(.text)
		*(.gnu.linkonce.t*)
	}

	.rodata ALIGN(0x1000) : AT(ADDR(.rodata) - VIRT_BASE)
	{
		ctors_start = .;
		*(SORT(.ctors*))
		ctors_end = .;

		dtors_start = .;
		*(SORT(.dtors*))
		dtors_end = .;

		*(.rodata*)
		*(.gnu.linkonce.r*)
	}

	.data ALIGN(0x1000) : AT(ADDR(.data) - VIRT_BASE)
	{
		*(.data)
		*(.gnu.linkonce.d*)
	}

	.bss : AT(ADDR(.bss) - VIRT_BASE)
	{
		sbss = .;
		*(COMMON)
		*(.bss)
		*(.gnu.linkonce.b*)
		ebss = .;
	}

	/DISCARD/ :
	{
		*(.comment)
		*(.eh_frame)
	}
	
	KernelEnd = . - VIRT_BASE;
}
greyOne
Member
Member
Posts: 58
Joined: Sun Feb 03, 2013 10:38 pm
Location: Canada

Re: Fake GDT memory access problems

Post by greyOne »

Virtlink wrote:Yes, and I have it in my linker script somewhere. How is .rodata related to not being able to write to video memory?
~snip
I had an issue like it at one point.
In any case, have you enabled paging?
You need to map 0xC00B8000 if you did.
Virtlink
Member
Member
Posts: 34
Joined: Thu Jun 05, 2008 3:53 pm
Location: The Netherlands
Contact:

Re: Fake GDT memory access problems

Post by Virtlink »

I don't want to enable paging yet, because I need some time to gather all the multiboot information and loaded modules from all over physical memory. If I do enable paging, everything runs fine, but then I can't easily access the multiboot information (e.g. when loaded by GRUB2 at high physical memory addresses).

Your issue from the other thread seems similar to my issue, but I get a different VirtualBox error.

This works like a charm:

Code: Select all

KernelEntry:
	cli
	mov [0xB8000], word 0x0F58  ; Print 'X'
	
	lgdt [Temporary_GDT]
	mov ax, 0x10
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax
	jmp 0x08:HigherHalf
HigherHalf:
	; ...
And this works only in Bochs, but not in VirtualBox:

Code: Select all

KernelEntry:
	cli
	
	lgdt [Temporary_GDT]
	mov ax, 0x10
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax
	jmp 0x08:HigherHalf
HigherHalf:
	mov [0xC00B8000], word 0x0F58  ; Print 'X'
	; ...
User avatar
Griwes
Member
Member
Posts: 374
Joined: Sat Jul 30, 2011 10:07 am
Libera.chat IRC: Griwes
Location: Wrocław/Racibórz, Poland
Contact:

Re: Robinson's GDT trick: VirtualBox crashes, Bochs works

Post by Griwes »

But because I want to access all physical memory at the start, so I don't want to enable paging just yet.
Putting aside that this is grammatically incorrect... that makes no sense at all. GDT trick is a trick, a silly hack for people who have no idea how to use paging properly, and thus shouldn't be used. And you can easily access entire physical memory using paging, hence the argument is silly and void.
Reaver Project :: Repository :: Ohloh project page
<klange> This is a horror story about what happens when you need a hammer and all you have is the skulls of the damned.
<drake1> as long as the lock is read and modified by atomic operations
Virtlink
Member
Member
Posts: 34
Joined: Thu Jun 05, 2008 3:53 pm
Location: The Netherlands
Contact:

Re: Robinson's GDT trick: VirtualBox crashes, Bochs works

Post by Virtlink »

Well, just imagine I didn't write 'but' and 'so', and - just for silly fun - pretend I know how to use paging properly. When using GRUB, how do you suppose to get a higher-half kernel working without the GDT trick and without adding yet another stage to the already two-stage GRUB bootloading process? And while you're at it, you might want to write it down on the Higher Half Kernel wIki page since all it talks about is using the GDT trick with GRUB.
User avatar
Jezze
Member
Member
Posts: 395
Joined: Thu Jul 26, 2007 1:53 am
Libera.chat IRC: jfu
Contact:

Re: Robinson's GDT trick: VirtualBox crashes, Bochs works

Post by Jezze »

I might be off here but isnt paging already enabled by Grub?
Fudge - Simplicity, clarity and speed.
http://github.com/Jezze/fudge/
Virtlink
Member
Member
Posts: 34
Joined: Thu Jun 05, 2008 3:53 pm
Location: The Netherlands
Contact:

Re: Robinson's GDT trick: VirtualBox crashes, Bochs works

Post by Virtlink »

Jezze wrote:I might be off here but isnt paging already enabled by Grub?
According to the Multiboot 0.6.96 specification, the machine state must have CR0 bit 31 (Paging bit) set to 0. So no, paging is not enabled by GRUB, as GRUB is a Multiboot-compliant bootloader and my kernel is a Multiboot kernel.
User avatar
xenos
Member
Member
Posts: 1121
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: Robinson's GDT trick: VirtualBox crashes, Bochs works

Post by xenos »

(I should have posted this here...)

You could simply set up paging in a small assembly stub, in which you correct all addresses "by hand" (i.e. add 0xC0000000 where necessary), before you run any C code. This is what my code is doing:

http://xenos.svn.sourceforge.net/viewvc ... iew=markup

Lines 21 - 56 set up paging. tabPGDIR and tabPGTAB are the virtual (higher half) addresses of the initial page directory and first page table.
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
User avatar
mduft
Member
Member
Posts: 46
Joined: Thu Jun 05, 2008 9:23 am
Location: Austria

Re: Robinson's GDT trick: VirtualBox crashes, Bochs works

Post by mduft »

Hey. For early and more or less easy enabling of paging, you could have a look at what i have at https://github.com/mduft/tachyon3. The "config/common-layout.ld" script contains a lot of linker magic to get all code where it belongs both physically and virtually :) thus the code does not need any calculating of addresses, as the linker does it right. Also "src/boot.S" enables paging quite early, after loading a bootstrap GDT and PML4, while jumping to long mode at the same time. The GDT is in "src/gdt.S" and the PML4 in "src/paging.S".

All the used hardcoded structures are replaced at runtime with dynamic counterparts (see "dyngdt.c", "vmem.c", "vmem_mgmt.c", ...), so they are really just to bootstrap the kernel. This approach makes things way easier as i don't care about TSS, etc.

HTH, Markus
devsau
Member
Member
Posts: 35
Joined: Thu Jun 13, 2013 12:07 pm

Re: Robinson's GDT trick: VirtualBox crashes, Bochs works

Post by devsau »

why is this called a 'trick' I am just curious.. surely it is nothing more then 'arduous use of segmentation'?

maybe i missed something.
User avatar
mduft
Member
Member
Posts: 46
Joined: Thu Jun 05, 2008 9:23 am
Location: Austria

Re: Robinson's GDT trick: VirtualBox crashes, Bochs works

Post by mduft »

sure, you're right :) it's just a way to get ppls going fast. however i personally feel that it was /VERY/ helpful for my understanding of x86_64 that i did it "right" (? ;)) in the first place.
Post Reply