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 :wink:

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...