Page 1 of 2

Linking for Higher Half kernel with GDT trick

Posted: Tue Aug 02, 2005 10:23 pm
by Crazed123
Hi. I've actually been doing quite well on the kernel, I've gotten a physical page manager that can allocate, free and even forever reserve pages (the last I do because of "reserved" pages in the GRUB memory map, the allocator will refuse to touch 'em), so I decided to try and implement paging. I got some paging code that I think I can debug myself once it gets called, but the issue now is that I want to map my kernel into the higher half (precisely to start at 0xC0100000), and I've decided to use the GDT trick from TimRobinson. I understand it and my segments get set up like so:

Code: Select all

{Set up the GDT.}
SetGDTEntry(0,0,0,0,0);
//Ring 0 segments.  First Data, then Code.
SetGDTEntry(1,0,$FFFFFFFF,$92,$CF);
SetGDTEntry(2,0,$FFFFFFFF,$9A,$CF);
//Ring 1 segments.  First Data, then Code.
SetGDTEntry(3,0,$FFFFFFFF,$B2,$CF);
SetGDTEntry(4,0,$FFFFFFFF,$BA,$CF);
//Ring 2 segments.  First Data, then Code.
SetGDTEntry(5,0,$FFFFFFFF,$D2,$CF);
SetGDTEntry(6,0,$FFFFFFFF,$DA,$CF);
//Ring 3 segments.  First Data, then Code.
SetGDTEntry(7,0,$FFFFFFFF,$F2,$CF);
SetGDTEntry(8,0,$FFFFFFFF,$FA,$CF);
//Segments with a special base that changes $100000 (physical kernel load address) into $C0100000 (virtual kernel load address in higher half.
SetGDTEntry(9,$40000000,$FFFFFFFF,$92,$CF);
SetGDTEntry($A,$40000000,$FFFFFFFF,$9A,$CF);
//Set the GDT pointer.
pGDT.wLimit:= sizeof(TGDTEntry) * 11 - 1;
pGDT.pBase:= @geGDT[0];
//Set the GDT and segment registers.
SetGDT();
SetSegmentRegisters(9 * $8,$A * $8);
All of which is very standard except for segments 9 and 10, which are the GDT trick segments. The issue is this: if I don't link for a higher half kernel the thing triple-faults inside of SetSegmentRegisters() on a simple goto instruction meant to make the code segment register switch, the issue being that with the installation of the new segments the address of the goto point is no longer valid because it isn't higher half. However, if I switch my linker script to:

Code: Select all

ENTRY(_start)
OUTPUT_FORMAT("elf32-i386")

SECTIONS
{
    . = 0xC0100000;

    .text ALIGN (0x1000) : AT(ADDR(.text) - 0xC0000000)
    { 
        *(.text)
        *(.rodata)
    }

    .data ALIGN (0x1000) : AT(ADDR(.data) - 0xC0000000)
    {
   *(.data)
    }

    .bss ALIGN (0x1000) : AT(ADDR(.bss) - 0xC0000000)
    {
        _sbss = .;
        *(COMMON)
        *(.bss)
        _ebss = .;
    }

    KernelEnd = .;
}
Everything gets linked to the proper virtual address, but the kernel dies on initialization code (Yes, this must be called. It's compiler added and all it does is call and return a few times, but it can't be removed) that finds itself linked for a higher-half address, but currently loaded at 0x100000.

I need a way to load the kernel to the point that it can switch the segment registers, but still have it work beyond that. [Foamy the squirrel]What shall I do O gurus of computer wisdom?[/Foamy the squirrel]

Re:Linking for Higher Half kernel with GDT trick

Posted: Wed Aug 03, 2005 3:38 am
by Pype.Clicker
Thou should light a candle to St Dijkstra and pray to be forgiven of using Pascal instead of C/ASM :P

Nah, seriously, if you need Tim's trick, you need to set it up in the ASM stub that prepares the CPU for your kernel (e.g. prior any pascal code is executed). Tim's idea is that you have to set up a temporary environment that already works with 0xC010000 offsets _even before you enable paging_. This has of course to be done _before_ any code attempt to use 0xC+ offsets, and any pascal code trying to access a global variable will likely contain such offset.

Re:Linking for Higher Half kernel with GDT trick

Posted: Wed Aug 03, 2005 8:17 am
by Crazed123
OK, got it to load the GDT correctly, but it can't seem to jmp into the new code segment without triple-faulting. The point is that after _finish_segment_loads it should be able to address things using the trick GDT, right?

Code: Select all

_gdt_trick:
gdt_entry_0 dw 0xFFFF ; wLowLimit
dw 0x0000 ; wLowBase
db 0x00 ; btMiddleBase
db 0x92 ; btAccess
db 0xCF ; btGranularity
db 0x40 ; btHighBase
dw 0xFFFF ; wLowLimit
dw 0x0000 ; wLowBase
db 0x00 ; btMiddleBase
db 0x9A ; btAccess
db 0xCF ; btGranularity
db 0x40 ; btHighBase
gdt_pointer dw 24
dd _gdt_trick + 0x40000000

_kstart:
   mov esp,_kernel_stack+KSTKSZ
   mov ecx,lwMagic
   mov [ecx],eax
   mov ecx,pMBInfo
   mov [ecx],ebx
   call PASCALMAIN
   cli
        hlt

_finish_segment_loads:
   mov ax,0x08
   mov ds,ax
   mov es,ax
   mov fs,ax
   mov gs,ax
   jmp _kstart

_start:
   lgdt [gdt_pointer+0x40000000]
   jmp 0x10:_finish_segment_loads

Re:Linking for Higher Half kernel with GDT trick

Posted: Wed Aug 03, 2005 10:59 am
by JoeKayzA
Hmm, took a look at your code. Assuming 0x10 is your trick code segment, and 0x08 is data, this can't work as long as _start and _finish_segment_loads are linked to the same base address. (Since you posted them in the same file, I assume they're linked the same)

You'd have to put the loading of the gdt, as well as the far jmp instruction into a seperate file which you link to 0x100000 (where the bootloader places your kernel) and everything else gets linked to 0xC0100000 (which will become your virtual base address). IIRC, you could accomplish this quite fine with a modified linker script, there was a thread around recently that showed how it works.

cheers Joe

Re:Linking for Higher Half kernel with GDT trick

Posted: Wed Aug 03, 2005 3:09 pm
by Crazed123
Why doesnt _finish_segment_loads work if it's linked to the same base address as _start? Way I see it, the jmp should put us in the new code segment with the 0xCXXXXXXX addresses being valid.

Re:Linking for Higher Half kernel with GDT trick

Posted: Wed Aug 03, 2005 6:57 pm
by AR
Did you try running it in Bochs and stepping through to make sure it loads the GDT correctly?

Re:Linking for Higher Half kernel with GDT trick

Posted: Wed Aug 03, 2005 7:49 pm
by Crazed123
Yes, the GDT loads correctly. A thought, but gdtr is always a purely physical address, right?

Re:Linking for Higher Half kernel with GDT trick

Posted: Wed Aug 03, 2005 10:26 pm
by AR
I'm not so sure, I'll need to check later when I have access to my own code but I'm pretty sure the IDT and GDT are linear addresses (On top of paging/segmentation), segmentation seems to operate on top of paging as well as odd as that may seem.

Did you try the "info gdt" command to display the contents?

Re:Linking for Higher Half kernel with GDT trick

Posted: Wed Aug 03, 2005 10:28 pm
by Crazed123
Yes, and that gives me a clue. The GDT should invalidate its own address in the register when the new segments are set. So if I set it with the physical address, then again with the newly valid virtual address...

Assembler stub created and seeming to work somewhat.

Code: Select all

[BITS 32]

extern PASCALMAIN

global _kernel_start
global SEGMENTATION_VIRTUAL_BASE

SEGMENTATION_VIRTUAL_BASE equ 0x40000000

_gdt_trick:
gdt_entry_0 dw 0x0000 ; wLowLimit
dw 0x0000 ; wLowBase
db 0x00 ; btMiddleBase
db 0x00 ; btAccess
db 0x00 ; btGranularity
db 0x00 ; btHighBase
dw 0xFFFF ; wLowLimit
dw 0x0000 ; wLowBase
db 0x00 ; btMiddleBase
db 0x92 ; btAccess
db 0xCF ; btGranularity
db 0x40 ; btHighBase
dw 0xFFFF ; wLowLimit
dw 0x0000 ; wLowBase
db 0x00 ; btMiddleBase
db 0x9A ; btAccess
db 0xCF ; btGranularity
db 0x40 ; btHighBase
gdt_pointer dw 24
dd _gdt_trick + 0x40000000

_kernel_start:
   lgdt [gdt_pointer+0x40000000]
   mov ax,0x08
   mov ss,ax
   mov ds,ax
   mov es,ax
   mov fs,ax
   mov gs,ax
   lgdt [gdt_pointer]
   call 0x10:PASCALMAIN
   ret
Yes, segment 0x08 is data and segment 0x10 is code. This seems to work to the point of calling the Pascal kernel.

Re:Linking for Higher Half kernel with GDT trick

Posted: Wed Aug 03, 2005 11:12 pm
by JoeKayzA
Crazed123 wrote: Why doesnt _finish_segment_loads work if it's linked to the same base address as _start? Way I see it, the jmp should put us in the new code segment with the 0xCXXXXXXX addresses being valid.
Ok, sorry, I noticed that you linked this whole file to 0xC0100000, right? (And add 0x40000000 to all globals before the gdt gets loaded) Then it should work this way. Unfortunately, I can't see anything wrong from this point on....
Maybe I'll give it a test when I'm at home to find out more.

cheers Joe

Re:Linking for Higher Half kernel with GDT trick

Posted: Wed Aug 03, 2005 11:57 pm
by Crazed123
Yeah, with some slight modifications the code above actually works. After this post I'll edit it to include the changes I made.

The issue now comes when I try to transfer control to the kernel's own GDT.

Code: Select all

procedure SetSegmentRegisters(wData,wCode: word); assembler;
asm
 push dword wCode
 push dword offset @loadedcs
 retf
@loadedcs:
 mov ax,wData
 mov ss,ax
 mov ds,ax
 mov es,ax
 mov fs,ax
 mov gs,ax
end ['AX'];
Upon executing the retf the thing triplefaults claiming that DPL>CPL, but the real issue is that it's only pushing a word when it pushes wCode for some reason, and other data sits along side it and gets mixed up with it. Only reason I'm asking about it here is that this didn't happen before. Back in the days of not using the segmentation trick it would just push a dword reading "0x00000010" like it's supposed to.

Re:Linking for Higher Half kernel with GDT trick

Posted: Fri Aug 05, 2005 7:26 am
by Warrior
Is it possible to have a higher half kernel withought the GDT trick? Only thing that comes to mind is mapping it to virtual 0xC000000 in every processes address space.

Maybe i'm totally off or maybe I'm repeating what someone already has mentioned before but clarification on this would be appreciated, thanks.

Re:Linking for Higher Half kernel with GDT trick

Posted: Fri Aug 05, 2005 7:29 am
by Pype.Clicker
Nelson wrote: Is it possible to have a higher half kernel withought the GDT trick? Only thing that comes to mind is mapping it to virtual 0xC000000 in every processes address space.
Yes, it is. It requires that your "ASM setup code" is able to set up the pages tables straight away. If your design allow you to do that (e.g. you know where to find aligned memory for those pages), fine. go ahead: it'll be much less trouble.

Re:Linking for Higher Half kernel with GDT trick

Posted: Fri Aug 05, 2005 7:49 am
by Warrior
Okay thanks for the verification. Going to read up on
some of my notes and start on that right away.

Re:Linking for Higher Half kernel with GDT trick

Posted: Fri Aug 05, 2005 8:25 am
by Colonel Kernel
There's a HigherHalfBareBones example in the FAQ that doesn't use the GDT trick.