Page 1 of 1

Robinson's GDT trick: VirtualBox crashes, Bochs works

Posted: Fri Apr 12, 2013 6:07 pm
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?

Re: Fake GDT memory access problems

Posted: Wed Apr 24, 2013 3:39 am
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?

Re: Fake GDT memory access problems

Posted: Wed Apr 24, 2013 3:59 pm
by greyOne
Does this thing here look familiar?

Code: Select all

*(.rodata)

Re: Fake GDT memory access problems

Posted: Wed Apr 24, 2013 5:16 pm
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;
}

Re: Fake GDT memory access problems

Posted: Wed Apr 24, 2013 6:56 pm
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.

Re: Fake GDT memory access problems

Posted: Thu Apr 25, 2013 4:55 am
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'
	; ...

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

Posted: Thu Apr 25, 2013 9:03 am
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.

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

Posted: Thu Apr 25, 2013 9:10 am
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.

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

Posted: Thu Apr 25, 2013 9:23 am
by Jezze
I might be off here but isnt paging already enabled by Grub?

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

Posted: Thu Apr 25, 2013 9:29 am
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.

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

Posted: Thu Apr 25, 2013 9:31 am
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.

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

Posted: Tue Sep 17, 2013 12:41 am
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

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

Posted: Tue Sep 17, 2013 8:26 pm
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.

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

Posted: Tue Sep 17, 2013 11:49 pm
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.