Page 1 of 1
Confusion with the GDT trick
Posted: Sat Apr 06, 2013 1:40 pm
by ruoferrac
Hello,
I've been trying to implement the GDT trick with this tutorial:
http://wiki.osdev.org/Higher_Half_With_GDT but qemu crashes before my code is reached:
Code: Select all
qemu: fatal: Trying to execute code outside RAM or ROM at 0xc0101060
There is one thing I don't understand and which I suspect is the reason of the crash.
In the linker script the entry point is set to "start", However "start", being in the .text section, is located at around 0xc0100000. I don't have that much memory, how could the pc jump on that address?
If I put start in the .setup section (located at 0x100000), it works as expected (at least the pc reaches start), did I misunderstood something ?
Note: I have the same result (crash) when using the code from the tutorial
Re: Confusion with the GDT trick
Posted: Sat Apr 06, 2013 3:02 pm
by Nessphoro
Don't use it.
It is archaic - high-half with paging is much cleaner and better.
Re: Confusion with the GDT trick
Posted: Sat Apr 06, 2013 3:16 pm
by ruoferrac
Suppose I don't use the trick itself, I still don't understand how the kernel could boot with its entry point located somewhere near 0xc0100000.
Other kernels seem to use the same kind of link script (e.g.:
http://littleosbook.github.io/#higher-h ... ker-script, even if this particular one appears incorrect to me since all AT(...)s have the same parameters).
Re: Confusion with the GDT trick
Posted: Sat Apr 06, 2013 4:31 pm
by Nessphoro
Oh - if thats the question.
Well it the kernel thinks it is at 0xC0000000.
But the physical address is somewhere above 1MiB.
So GRUB (Or whatever you're using) jumps to that location. And GDT (in this case) makes it look like the kernel is running 0xC0000000
But I think you did something wrong - or using old grub.
Re: Confusion with the GDT trick
Posted: Sat Apr 06, 2013 5:18 pm
by Combuster
ELF files are capable of having a different physical and linking address. I haven't seen any stories about GRUB not dealing with it properly.
Beyond that, you can write code that doesn't care where it runs in memory, and that code can make sure that either paging gets enabled or the GDT trick configured so the remainder of the code that does care can deal with it.
Re: Confusion with the GDT trick
Posted: Sat Apr 06, 2013 8:05 pm
by Nessphoro
Combuster wrote:ELF files are capable of having a different physical and linking address. I haven't seen any stories about GRUB not dealing with it properly.
Beyond that, you can write code that doesn't care where it runs in memory, and that code can make sure that either paging gets enabled or the GDT trick configured so the remainder of the code that does care can deal with it.
From Wiki
Grub Error 7: Loading below 1MB is not supported
Many older versions of GRUB ignore the 'physical address hint' of the ELF sections. Try to make sure you are using at least GRUB v 0.94.
Re: Confusion with the GDT trick
Posted: Sun Apr 07, 2013 2:49 am
by Combuster
As if someone is still using buggy software from 2003 or earlier
Re: Confusion with the GDT trick
Posted: Sun Apr 07, 2013 6:15 am
by ruoferrac
Nessphoro wrote:
But I think you did something wrong - or using old grub.
I was indeed using a 2 years old version of qemu (qemu -kernel kernel.bin). I updated and it solved my problem, thank you
However I can't get my kernel to jump on the higher-half. I know you guys don't like these "find my bug" requests, but I'm a bit desperate and I've run out of ideas on how to debug.
Identity mapping seems OK since I don't crash after enabling paging, but a triple-fault is raised at the next jump. I suspect my higher-half mapping is wrong, but I went over it dozens of times and it seems correct to me.
Code: Select all
; multiboot
MBOOT_FLAG_MEMALIGN equ (1<<0) ; load kernel on a page boundary
MBOOT_FLAG_MEMINFO equ (1<<1) ; provide memory information
MBOOT_HDR_MAGIC equ 0x1BADB002
MBOOT_HDR_FLAGS equ (MBOOT_FLAG_MEMALIGN|MBOOT_FLAG_MEMINFO)
MBOOT_HDR_CHECKSUM equ -(MBOOT_HDR_MAGIC + MBOOT_HDR_FLAGS)
[BITS 32]
[GLOBAL mboot]
[GLOBAL start]
SECTION .text.multiboot
ALIGN 4
multiboot_header:
dd MBOOT_HDR_MAGIC
dd MBOOT_HDR_FLAGS
dd MBOOT_HDR_CHECKSUM
KERNEL_VMA_BASE equ 0xc0000000
[EXTERN __kernel_start]
[EXTERN __kernel_end]
SECTION .text.start
start:
cli
; Enable PSE
mov eax, cr4
or eax, 0x10
mov cr4, eax
; Identity mapping with PSE
mov eax, 0|0x83 ; PSE + present + rw
mov [pagedirectory - KERNEL_VMA_BASE], eax
; Higher half mapping
mov eax, 0x03 + pagetable0 - KERNEL_VMA_BASE
mov [pagedirectory - KERNEL_VMA_BASE + (0xc0100000 >> 22)], eax
; Map the kernel
mov eax, pagetable0 - KERNEL_VMA_BASE ; virtual address
mov ebx, __kernel_start
mov ecx, __kernel_end
mov esi, KERNEL_VMA_BASE
.mapkernel:
mov edx, ebx ; physical address
sub edx, esi
or edx, 0x03
mov [eax], edx
add eax, 0x04
add ebx, 0x1000
cmp ebx, ecx
jne .mapkernel
; Install page-directory
mov eax, pagedirectory - KERNEL_VMA_BASE
mov cr3, eax
; Enable paging
mov eax, cr0
or eax, 0x80000000
mov cr0, eax
; OK here
mov eax, callmain
jmp eax ; <-- KO
[EXTERN kmain]
callmain:
jmp $
SECTION .bss nobits
[GLOBAL pagedirectory]
pagedirectory: resb 0x1000
pagetable0: resb 0x1000
Code: Select all
ENTRY (start)
KERNEL_VMA_BASE = 0xc0000000;
SECTIONS
{
. = 0x00100000;
. += KERNEL_VMA_BASE;
__kernel_start = ALIGN(0x1000);
.text ALIGN(0x1000) : AT(ADDR(.text) - KERNEL_VMA_BASE)
{
*(.text.multiboot)
*(.text.start)
*(.text)
}
.rodata ALIGN (0x1000) : AT(ADDR(.rodata) - KERNEL_VMA_BASE)
{
*(.rodata)
}
.data ALIGN(0x1000) : AT(ADDR(.data) - KERNEL_VMA_BASE)
{
*(.data)
}
.bss ALIGN(0x1000) : AT(ADDR(.bss) - KERNEL_VMA_BASE)
{
*(.bss)
}
__kernel_end = ALIGN (0x1000);
}
It is meant to do the same thing as described here:
http://wiki.osdev.org/Higher_Half_Kerne ... Bootloader except that I used PSE to identity map.
Re: Confusion with the GDT trick
Posted: Sun Apr 07, 2013 9:22 am
by ruoferrac
I narrowed down the problem :
Code: Select all
$ nm kernel.bin | grep __kernel_
c0100000 A __kernel_end
c0100000 A __kernel_start
These two symbols have the same value... Given my link script, I don't see how this is possible.
Putting the definition of __kernel_end in the .bss block fixes this.
Do you know why I get the same values ?
EDIT:
After some digging, __kernel_end was set to the incorrect value because of this:
Code: Select all
kernel.bin: $(OBJ) link.ld
$(LD) $(LDFLAGS) -o $@ $^
link.ld would be in $^ and it would induce this very weird behaviour...