Kernel Paging and Linker Script

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
pranjas
Member
Member
Posts: 25
Joined: Sun Jan 16, 2011 9:18 pm

Kernel Paging and Linker Script

Post 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...
User avatar
Muneer
Member
Member
Posts: 104
Joined: Tue Nov 02, 2010 2:05 am
Location: India

Re: Kernel Paging and Linker Script

Post 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*/
Even the smallest person could change the course of the future - Lord Of The Rings.

In the end all that matters is what you have done - Alexander.

Even after a decade oh god those still gives me the shivers.
pranjas
Member
Member
Posts: 25
Joined: Sun Jan 16, 2011 9:18 pm

Re: Kernel Paging and Linker Script

Post 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.
pranjas
Member
Member
Posts: 25
Joined: Sun Jan 16, 2011 9:18 pm

Re: Kernel Paging and Linker Script

Post 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?
User avatar
Muneer
Member
Member
Posts: 104
Joined: Tue Nov 02, 2010 2:05 am
Location: India

Re: Kernel Paging and Linker Script

Post 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?
Even the smallest person could change the course of the future - Lord Of The Rings.

In the end all that matters is what you have done - Alexander.

Even after a decade oh god those still gives me the shivers.
pranjas
Member
Member
Posts: 25
Joined: Sun Jan 16, 2011 9:18 pm

Re: Kernel Paging and Linker Script

Post 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!
User avatar
Muneer
Member
Member
Posts: 104
Joined: Tue Nov 02, 2010 2:05 am
Location: India

Re: Kernel Paging and Linker Script

Post 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
Even the smallest person could change the course of the future - Lord Of The Rings.

In the end all that matters is what you have done - Alexander.

Even after a decade oh god those still gives me the shivers.
pranjas
Member
Member
Posts: 25
Joined: Sun Jan 16, 2011 9:18 pm

Re: Kernel Paging and Linker Script

Post 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?
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Re: Kernel Paging and Linker Script

Post 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.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
User avatar
Muneer
Member
Member
Posts: 104
Joined: Tue Nov 02, 2010 2:05 am
Location: India

Re: Kernel Paging and Linker Script

Post 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.
Even the smallest person could change the course of the future - Lord Of The Rings.

In the end all that matters is what you have done - Alexander.

Even after a decade oh god those still gives me the shivers.
pranjas
Member
Member
Posts: 25
Joined: Sun Jan 16, 2011 9:18 pm

Re: Kernel Paging and Linker Script

Post 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?
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Re: Kernel Paging and Linker Script

Post 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.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
Tosi
Member
Member
Posts: 255
Joined: Tue Jun 15, 2010 9:27 am
Location: Flyover State, United States
Contact:

Re: Kernel Paging and Linker Script

Post 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
vjain20
Member
Member
Posts: 73
Joined: Wed Apr 04, 2012 9:12 pm

Re: Kernel Paging and Linker Script

Post 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
- Thanks
Vaibhav jain
Rudster816
Member
Member
Posts: 141
Joined: Thu Jun 17, 2010 2:36 am

Re: Kernel Paging and Linker Script

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