Higher Half Kernel: Difference between revisions

[unchecked revision][unchecked revision]
Content deleted Content added
No edit summary
Revamped
Line 1:
{{Convert}}
 
It is traditional (and usuallygenerally approved as a ~GoodThing)good to have your kernel mapped in every user process. Linux, for instance (and many other unices) residesreside at the virtual addresses =''0xC0000000..0xffffffff='' of every address space, leaving the range =''0x00000000..0xbfffffff='' for user code, data, stacks, libraries, etc.
Kernels that have such design are said to be "in the higher half" by opposition to kernels that use lowest virtual addresses for themselves, and leave higher addresses for the applications.
 
Advantages of a HigherHalfKernelhigher half kernel are:
* Easier to set up VM86 processes since the region below 1MB is userspace.
* More generically, user applications are not dependent of how many memory is kernel space (yourYour app can be linked for 0x400000 regardless of whether kernel is at =''0xC0000000='', =''0x80000000='' or =''0xE0000000='' ...), which makes ABIsABI's nicer.
* If your OS also supports 64-bits, 32-bit applications will be able to use the full 32-bit address space in the 64-bit version.
* 'mnemonic' invalid pointers such as =''0xcafebabe='', =''0xdeadbeef='', =''0xdeadc0de='', etc. can be used.
 
Related thread: [I wrote a simple HigherHalf kernel|Forum:8726]
 
==Initialization==
 
ItTo setup a higher half kernel, you have to map your kernel to the appropriate virtual address. How to do this basically depends on *'''when*''' you'd like your kernel to believe it's in the higher end, and *'''when*''' you set up paging.
 
!! How can i place my kernel in the higher half?
 
===Custom Bootloader===
It basically depends on *when* you'd like your kernel to believe it's in the higher end and *when* you set up paging.
 
The easiest way is to load your kernel to any physical location you wish (for instance in the lowest 1MB) and prepare page tables that will perform the appropriate translation. Let's say you loaded your kernel starting at =''0x00010000='' up to =''0x0009ffff='' and want it to appear at =''0xC0010000=''.
! With your own bootloader
 
# *Pick 3 4096-aligned addresses where you'll put your page directory and system tables. Wipe them (with zeroes).
The easiest way is to load your kernel to any physical location you wish (for instance in the lowest 1MB) and prepare page tables that will perform the appropriate translation. Let's say you loaded your kernel starting at =0x00010000= up to =0x0009ffff= and want it to appear at =0xC0010000=.
# *Fill the lowest 256 entries of one table to set up IdentityPaging[[Identity Paging]] for at least the BIOS and your bootloader (I'd even use 1:1 mapping for the whole lowest 1MB if I were you :)).
# *In the other table, fill entry #0x10 with =''0x00010003='', entry #0x11 with =''0x00011003='' ... (forFor as much pages as your kernel has.).
# *Fill the entry #0 of the directory with the address of the first table (and make it present).
# *Fill the entry #768 of the directory with the address of the second table (and make it present).
 
When switching to ProtectedMode[[Protected Mode]], use this AsmExampleassembly example:
# Pick 3 4096-aligned addresses where you'll put your page directory and system tables. Wipe them (with zeroes).
# Fill the lowest 256 entries of one table to set up IdentityPaging for at least the BIOS and your bootloader (I'd even use 1:1 mapping for the whole lowest 1MB if I were you :)).
# In the other table, fill entry #0x10 with =0x00010003=, entry #0x11 with =0x00011003= ... (for as much pages as your kernel has).
# Fill the entry #0 of the directory with the address of the first table (and make it present).
# Fill the entry #768 of the directory with the address of the second table (and make it present).
 
mov eax, physical_address_of_the_directory
When switching to ProtectedMode, use this AsmExample:
mov cr3,eax
mov eax,cr0
or eax,0x80000001 ; enables both pmode and paging
mov cr0,eax
 
<verbatim>
mov eax, physical_address_of_the_directory
mov cr3,eax
mov eax,cr0
or eax,0x80000001 ; enables both pmode and paging
mov cr0,eax
</verbatim>
 
! ===[TimRobinson's GDT trick|http://www.osdever.net/tutorials/pdf/memory1.pdf Tim Robinson's GDT Trick]===
 
If we don't want to enable paging right from the start, it is still possible to have your kernel appearing in the higher half by using an appropriate base for the code and data segments. Say we have loaded the kernel at ''0x10000'' and we want it to appear at ''0xC0000000''. All we need is to find a base _X_ such as _X_+''0xC0000000'' == ''0x10000''. The bootloader will then initialize the [[GDT]] with cs.base == 0x40010000 == ds.base. This also means that special care must be taken for vram access, as ''(void*)0xb8000'' is now somewhere above 1GB. Either use a special 0-based additional data-segment or use
<verbatim>
#define logical_to_physical(x) (((void*)x)+0x40010000)
#define physical_to_logical(x) (((void*)x)-0x40010000)
 
#define logical_to_physical(x) (((void*)x)+0x40010000)
short* vram=physical_to_logical(0xb8000)
#define physical_to_logical(x) (((void*)x)-0x40010000)
</verbatim>
short* vram=physical_to_logical(0xb8000)
 
When you eventually enable paging, create the page tables as mentioned above (keeping in mind that you need to undo the address conversion again, e.g.
<verbatim>
pgentry *pagedirectory=physical_to_logical(0x9D000);
pgentry *lowesttable=physical_to_logical(0x9C000);
pgentry *kerneltable=physical_to_logical(0x9B000);
 
pgentry *pagedirectory=physical_to_logical(0x9D000);
// prepare lowesttable and kerneltable
pgentry *lowesttable=physical_to_logical(0x9C000);
pagedirectory[0]=mkpgentry(0x9C000,PG_PRESENT|whatever);
pgentry *kerneltable=physical_to_logical(0x9B000);
pagedirectory[0xC0000000>>12]=mkpgentry(0x9B000,PG_PRESENT|PG_SYSTEM|whatever);
set_cr3_and_update_gdt(0x9D000);
/ /* prepare lowesttable and kerneltable */
</verbatim>
pagedirectory[0]=mkpgentry(0x9C000,PG_PRESENT|whatever);
pagedirectory[0xC0000000>>12]=mkpgentry(0x9B000,PG_PRESENT|PG_SYSTEM|whatever);
set_cr3_and_update_gdt(0x9D000);
 
Along with setting CR3's value, you'll have to clear the base of all the previous segments and reload segment registers so that, at the exit of ''set_cr3_and_update_gdt'', any memory references now use the 0-based code and data segment and that the page tables perform the translation required to still make ''0x10000'' appear at ''0xC0000000''.
 
 
! ===With [[GRUB]] (if you want to set up paging right fromFrom the start)Start===
 
GRUB will load your kernel at the desired physical target address, but it will leave segments in the [[GDT]] with a 0 base and it will not enable paging.
 
! With [GRUB] (if you don't want to set up paging right from the start)
 
===With GRUB Later On===
Even with GRUB it's still possible to use TimRobinson's GDT trick: just set up a "fake" GDT then jump to your C code and enable paging. See HigherHalfWithGdt
 
Even with GRUB it's still possible to use TimRobinson's GDT trick: just set up a "fake" GDT then jump to your C code and enable [[paging]]. See HigherHalfWithGdt[[Higher Half With Gdt]]
!! Do you have a Higher half BareBones somewhere?
 
 
Yes. Here it is: HigherHalfBareBones
==See Also==
*[http://www.osdev.org/phpBB2/viewtopic.php?t=11160 I wrote a simple HigherHalf kernel]