Page 1 of 2

Kernel Paging and Linker Script

Posted: Wed Feb 02, 2011 7:40 am
by pranjas
Hi,

I was trying to enable paging the other day, but i just got stuck at one place. The whole paging thing is OK with me, i get the structures and all the bit twidles you gotta do but what about the linker script?

So currently I don't have paging enabled.. and my linker script looks like the following

Code: Select all

OUTPUT_FORMAT("binary");
ENTRY(_boot_entry);
codestart = 0x100000;
virtual = 0xC0000000; /* I want to use a higher half kernel but can't seem to get it right*/
. = codestart;    /* you need to set this up to the kernel text. */
SECTIONS
{
.text : {
code = . ;
*(.text);
. = ALIGN(4096);
 codeend = ABSOLUTE(.); /* make sure you use absolute otherwise it'll be relative to current section which is text section */
}


/*. = codeend ; you need to set the current location couter to the absolute value of location counter where the code ended */

.data : {  /* ADDR returns the absolute Virtual Memory Address (VMA) of a named section*/
data = ABSOLUTE(.);
*(.data);
. = ALIGN(4096);
dataend = ABSOLUTE(.);
_dataend = dataend;
}

/*. = dataend ;  same way setup location counter for the bss segment */
.bss :
{ bss = ABSOLUTE(.);
*(.bss);
. = ALIGN(4096);
bssend = ABSOLUTE(.);
}
}
I hope my newbie skills don't offend anyone, but how do i make sure that paging gets enabled when i use the virtual address?
I'm using the AOUT kludge / grub specification for loading my kernel so i need to know the start and end address of my sections. Probably need them anyway so i can move to user land correctly.

Problems i'm facing...
---->the kernel according to this script is made to be executed starting 1MB. But i want it to appear at 3Gigs so what i want is code instructions to have addresses starting from that location correct?

---->if i make the VMA to be virtual(variable in my script above), then i believe the code emitted will be having that instruction address right? however i don't have that much memory so that thing is gonna give me a triple correct?

---->Now since initially the kernel will need to be able to work with physical memory, how can i make sure the switch occurs after i've initialized my pgd and pgt for the kernel? I read Tim Robinson's GDT trick, so do i have to use it. I don't want to introduce another section in linker script, but if that's the only way then i'll have to.

---->What is the purpose of LMA then in the linker script? Really really confused...on VMA and LMA part. Read online docs from redhat on the linker scripts but this thing doesn't make any sense to me.

Please help...

Re: Kernel Paging and Linker Script

Posted: Wed Feb 02, 2011 7:53 am
by Muneer
Hi ,

Well if you don't want to use the GDT trick, then there is an alternative. Enable Temperory Paging in your bootLoader (or whatever) just before making your jump to 0xC0000000. Identity map the current location of the code and map 1MB to 3GB. Link the
kernel to 0xC0000000 using the script. This is how I do. You can change your PageDirectory at a later stage in your kernel.Make sure that you Identity Map the current executing location, otherwise it will result in immediate Triple fault.


you can include this in your linker script

Code: Select all

phys = 0xC0000000;

and remove both

Code: Select all

codestart = 0x100000;
virtual = 0xC0000000; /* I want to use a higher half kernel but can't seem to get it right*/

Re: Kernel Paging and Linker Script

Posted: Wed Feb 02, 2011 8:08 am
by pranjas
Well that's the thing,
to enable paging i need to load the physical address of pgd and ptes...

but if i use the location counter as 3 Gigs in the beginning won't my data section addresses will be placed there? I'm not sure if i'm right but that's how my understanding of this currently.

Re: Kernel Paging and Linker Script

Posted: Wed Feb 02, 2011 8:09 am
by pranjas
Hmm... Just thought of this after posting,
while loading the cr3 i subtract from the addresses of my pdg and ptes 3 Gigs? Will that work?

Re: Kernel Paging and Linker Script

Posted: Wed Feb 02, 2011 8:20 am
by Muneer
to enable paging i need to load the physical address of pgd and ptes...
Yes you should.
but if i use the location counter as 3 Gigs in the beginning won't my data section addresses will be placed there? I'm not sure if i'm right but that's how my understanding of this currently.
Yes, if you link your kernel to 3GB, then your data segment will be relative to this 3GB.
while loading the cr3 i subtract from the addresses of my pdg and ptes 3 Gigs? Will that work?
Why should you subtract?

Re: Kernel Paging and Linker Script

Posted: Wed Feb 02, 2011 8:26 am
by pranjas
3 Gigs is the base VMA i want the kernel to have but grub would load it at 1MB (can i change that?).

I definitely dun have 3 Gigs on my test laptop. So if i subtract 3 Gigs (since data section would now be somewhere above 3 Gigs (correct?), if I subtract 3 Gigs it'll tell me how far i'm from the base.) Now since grub is loading my kernel starting 1 MB , i'll add 1MB to this difference to get the correct physical address of the pgd. Is this correct? I'm sorry for asking so many questions... but jst had too many of the triples.. driving me crazy!

Re: Kernel Paging and Linker Script

Posted: Wed Feb 02, 2011 8:33 am
by Muneer
You really dont have to do that. just compile your kernel and link it to 0xC0000000. Let grub load this to 1 MB physical. Later enable paging with the 1MB mapped to 3GB along with identity mapping the current position. You really dont want 3GB installed. x86 can map to any address to 3GB and access it as such.
3 Gigs is the base VMA i want the kernel to have but grub would load it at 1MB (can i change that?)
I use my own bootloader. I don't know about GRUB.
'm sorry for asking so many questions... but jst had too many of the triples.. driving me crazy!
No question is unimportant(though this dont correspond to Basic Req.). We are all here to learn. :D

Re: Kernel Paging and Linker Script

Posted: Wed Feb 02, 2011 8:42 am
by pranjas
hmm... ok but i need to know why will this work?

From my OS theory, the processor will fetch instructions from the memory (which in our case would be addresses above 3Gig)

Now our kernel (text+data) are loaded starting 1MB, so when processor fetches this address (EIP) and starts to execute this, shouldn't it result in a triple?

The AOUT kludge must be having the entry_point address somewhere in 3 Gigs correct? but when nothing is there and as i said there's not 3 Gigs installed how come this will work?

Re: Kernel Paging and Linker Script

Posted: Wed Feb 02, 2011 8:51 am
by neon
Hello,

You can just have the kernel remap itself to 3GB and call itself there. The kernels base should be 3GB (or wherever its virtual address base should be) but loaded at 1MB physical as noted above. This is possible using position-independent code.

This is what we do, anyways and works for any loader.

Re: Kernel Paging and Linker Script

Posted: Wed Feb 02, 2011 8:58 am
by Muneer
Now our kernel (text+data) are loaded starting 1MB, so when processor fetches this address (EIP) and starts to execute this, shouldn't it result in a triple?
In your scenario, yes it should result in a triple fault. Get some workaround for this like having your first codes before your first jump put the kernel in paging mode like..

Code: Select all

Mov [0x1000 + 0xC00] , 0x2003     ; I didnt check the offset 0xC00 is correct. But you get it?
Mov [0x2000] , 0x100003             ;  Map First page 1MB to Virtual 3 GB
Mov [0x2004] , 0x101003             ; Map second page at 1MB .....
Mov [0x2008] , 0x102003
Mov [0x200C] , 0x103003
...
Mov dword[0x1000] , 0x7003       ; Identity map the current locations PD
Mov [0x7000] , 0x100000              ; same for Page Table

Mov Eax , 0x1000
Mov Cr3 , Eax 
Dont know if this would work but try it.

Re: Kernel Paging and Linker Script

Posted: Wed Feb 02, 2011 9:03 am
by pranjas
Ok,
I now have physical set as 0xC0000000

mbchk reports that its multiboot compliant
but when grub tries to load it i get error saying loading below 1MB isn't supported. Any thoughts?

Re: Kernel Paging and Linker Script

Posted: Wed Feb 02, 2011 12:50 pm
by neon
Hello,

Not quite sure what it is that you mean by "I now have physical set as 0xC0000000". What physical address? Are you sure you want 0xC0000000 to be used as a physical address in a machine that has < 3GB of physical memory?

I suspect there is a misunderstanding in following provided directions. I support trying a lower half kernel first as well - after all, you will need that as the first step anyways.

Re: Kernel Paging and Linker Script

Posted: Thu Feb 03, 2011 7:19 am
by Tosi
I had troubles with a higher-half kernel at first, but after reading the section on linker scripts in the binutils documentation and the intel manuals I figured out what my problem was. The whole idea is really simple, and doesn't require a lot of arcane kludges. You're using the AOUT kludge, however, so things might be very different. With an ELF kernel, I got it working like this.

In your linker script, you need to specify where the virtual as well as the physical addresses are for all of your sections. The best way to do this is with the AT() directive. I have something like this in my linker script:

Code: Select all

ENTRY(StartMeUp)
phys_base = 0x00100000;
virt_base  = 0xC0000000;

...

SECTIONS
{
      . = phys_base + virt_base;

      .text : AT(ADDR(.text) - virt_base)
      {
             *(.text)
      }
}
Then in your startup code, when you enable paging, remember to calculate that cr3 takes the physical address of a page directory. Also, you might need initially two entries in the page directory, one identity mapping the kernel, and the other mapping it to the virtual address you intend to load from. Then you need to force a far jump to a pure virtual address to make sure EIP has a virtual address instead of a physical one.

Code: Select all

...
SECTION .text
StartMeUp:
      ...
      mov ecx, (page_directory - VIRT_BASE)  ; Get the physical address of the page directory
      mov cr3, ecx
      ; Enable paging here (or cr0 with 0x80000000)
      ...
      mov ecx, StartAtVirtual                        ; Force the assembler to make a far jump by doing it indirectly
      jmp ecx
StartAtVirtualAddr:
      ; Rest of setup here

Re: Kernel Paging and Linker Script

Posted: Tue May 15, 2012 12:00 am
by vjain20
Hi,

I have a related question. How can I link different portions of my code to different addresses ?
I have the startup code in assembly while the code for setting up GDT and paging in C.
I would like to link all the code that comes before the paging code to 1MB and all the code thereafter to
3Gb so that I don't have to subtract 3GB from all the addresses. I don't have any problem doing it the way suggested in this post
but I just want to do it out of interest. One of the versions of linux kernel also did the same.
This also makes me wonder that if I have an org directive in an assembly file which specifies an address and
and in the linker script the location counter is set to some other address which will override the other - ther org or the location counter
?

Thanks
Vaibhav Jain

Re: Kernel Paging and Linker Script

Posted: Tue May 15, 2012 5:44 am
by Rudster816
vjain20 wrote:Hi,

I have a related question. How can I link different portions of my code to different addresses ?
I have the startup code in assembly while the code for setting up GDT and paging in C.
I would like to link all the code that comes before the paging code to 1MB and all the code thereafter to
3Gb so that I don't have to subtract 3GB from all the addresses. I don't have any problem doing it the way suggested in this post
but I just want to do it out of interest. One of the versions of linux kernel also did the same.
This also makes me wonder that if I have an org directive in an assembly file which specifies an address and
and in the linker script the location counter is set to some other address which will override the other - ther org or the location counter
?

Thanks
Vaibhav Jain
In the future, you should just start a new thread and link to an older one if it has something relevant to your question.

The easiest, and probably the most robust way to do higher half in 32 bit mode is to use the GDT trick found here.

To use a linker script, you have to separate your code into two sections, one that runs at the physical address, and the other at its higher half virtual address. You just need to make your linker script do something like this:

Code: Select all

ENTRY (loader)

SECTIONS
{
    . = 0x100000;
	
    .nopaging ALIGN (0x1000) :
    {
	*(.nopaging)
    }
	
    . = 0xC0000000

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

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

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

    .bss :
    {
        *(COMMON)
        *(.bss)
    }
}
This will make the "ORG" for any function inside of the nopaging section 0x100000 (or whatever you make). You can then declare function in GCC with the section attribute set to nopaging like so,

Code: Select all

__attribute__ ((section ("nopaging")
and the function should be put inside of that section. Normal functions are still placed in the .text section like usual and will have the base for that section will be 0xC0000000.

There are couple of problems with this though. You cannot access anything (functions or data) from the different code sections because they have different bases. E.g. trying to access any piece of global data inside the nopaging section like shown will use the wrong address's. The same thing would happen if you tried to call a function inside of the nopaging section from a normal function (or vice versa). An easy way around it is to just adjust the address of all the data you access from your page\GDT setup functions, and never call them after you turn on paging.


I do believe that you'll have to adjust the script above to work with GRUB though because I think as is, the physical base's will be different for each section. To GRUB this will appear as if it needs to load something at the physical address 0xC0000000, which it won't do, nor would you want it to. Off the top of my head I don't know how to fix this, but I'm sure Google knows.