972
edits
[unchecked revision] | [unchecked revision] |
(→Compiling: Do not use SSE ops, or several #UD and #NM exceptions will get triggered) |
m (Bot: Replace deprecated source tag with syntaxhighlight) |
||
(27 intermediate revisions by 18 users not shown) | |||
Line 1:
{{Rating|
{{In_Progress}}
{{Disputed|Talk:Creating_a_64-bit_kernel#Disputed}}
== Prerequisites ==
Line 5 ⟶ 7:
Make sure that you have the following done before proceeding:
* Have [[GCC_Cross-Compiler|built a cross-compiler]] for the x86_64-elf target.
* Read up on long mode and how to [[X86-64|initialize/use it]] (if you intend on not using a 64-bit aware bootloader or rolling your own).
* Decide now on how to load your kernel - your own bootloader, [[GRUB]] (with separate loader executable),
▲* Decide now on how to load your kernel - your own bootloader, [[GRUB]] (with separate loader executable), or GRUB2 (elf64 + 32-bit bootstrap code)
== The Main Kernel ==
The kernel should run in a uniform environment. Let's make this simple for now...
===
<
void
{
/* What goes here is up to you */
}
</syntaxhighlight>
== Compiling ==
Line 25 ⟶ 26:
Linking will be done later...
<
x86_64-elf-gcc -ffreestanding -mcmodel=large -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -c foo.c -o foo.o
</syntaxhighlight>
The -mcmodel=large argument enables us to run the kernel at any 64-bit virtual memory address we want
You will need to instruct GCC not to use the the AMD64 ABI 128-byte 'red zone', which resides below the stack pointer, or your kernel will be ''interrupt unsafe''. Check this [http://forum.osdev.org/viewtopic.php?t=21720 thread] on the forums for extra context.
Line 46 ⟶ 37:
== Linking ==
The kernel will be linked as an
=== link.ld ===
▲ ENTRY(kmain)
SECTIONS
{
Line 70 ⟶ 60:
}
.
{
_ehframe = .;
*(.
. = ALIGN(4096);
}
Line 101 ⟶ 91:
Feel free to edit this linker script to suit your needs. Set ENTRY(...) to your entry function, and KERNEL_VMA to your base virtual address.
<
'''Note''': Obviously there is no bootstrap assembly yet, which is the hard part of starting out, and you can't link without it.
== Loading ==
Before you can actually use your kernel, you need to deal with the hard job of loading it. Here are your
=== With your own boot loader ===
Line 121 ⟶ 111:
* Enter Long Mode by far jump to the kernel entry point in (virtual) memory
=== With a
Open Source boot loaders written for long mode kernels already exist, you don't have to reinvent the wheel. Unlike GRUB (which does not support switching to long mode), bootloaders such as [[Limine]] or [[BOOTBOOT]] can load your 64-bit kernel directly by doing all the things listed in the previous section (and more). It saves you the struggle to write and properly link bootstrap code or to implement your own boot loader entirely from scratch. Therefore using a 64-bit aware bootloader is a nice and easy, reasonably bullet-proof, choice for beginners.
This requires the use of [[GRUB]] or another multiboot1-compliant loader. This may be the most error free of the three.▼
=== With legacy GRUB ===
'''Note''': The advise in this section is bit questionable in its current form.
See [[Creating_a_64-bit_kernel_using_a_separate_loader|Creating a 64-bit kernel using a separate loader]]
▲This requires the use of [[GRUB]] or another multiboot1-compliant loader. This may be the most error free of the
A quick rundown:
Line 131 ⟶ 127:
* Enter Long Mode by far jump to the kernel entry point
Note that this code has to be
Also remember to set the text section to start at 0x100000 (-Ttext 0x100000) when linking your loader.
Line 144 ⟶ 137:
=== With a 32-bit bootstrap in your kernel ===
This requires the use of any ELF64-compatible loader that loads into protected-mode (GRUB2, or patched GRUB Legacy). This may be the simplest in the long run, but is hell to set up (well, it was for me - but I saved you some work ;).▼
▲This requires the use of any ELF64-compatible loader that loads into protected-mode (GRUB2, or patched GRUB Legacy). This may be the simplest in the long run, but is
Note that GRUB2, which implements [http://download.savannah.gnu.org/releases-noredirect/grub/phcoder/multiboot.pdf Multiboot 2], does not support switching into long mode.
First, create an assembly file like the following, which will set up virtual addressing and long mode:
Line 194 ⟶ 187:
multiboot <kernel executable>
}
==== With Visual C++ ====
The technique for creating a 64 bit kernel with a 32 bit bootstrap is similar to GCC. You need to create an assembly bootstrap with nasm (masm may work, but the author uses nasm).
Note that this stub '''must''' be assembled to a ''64'' bit object file (-f win64). Your stub then has a BITS 32 directive.
Note that, although nasm will not complain about this, Microsoft link will. It complains about address relocations, due to the memory model settings (/LARGEADDRESSAWARE, which is required for /DRIVER).
As such, you need a method of generating the correct 32 bit code, while fooling link into generating a 64 bit relocation. Here is a macro for you:
<syntaxhighlight lang='asm'>
;Encode 32 bit moves without the ADDR32 issue
%macro mov_abs32 2
%if %1==eax
db 0xB8
%elif %1==ebx
db 0xBB
%elif %1==ecx
db 0xB9
%elif %1==edx
db 0xBA
%elif %1==edi
db 0xBF
%elif %1==esi
db 0xBE
%elif %1==ebp
db 0xBD
%elif %1==esp
db 0xBC
%else
%error "Unknown register"
%endif
dq %2+(0x90909090 << 32)
%endmacro
;This translates as mov %1, %2 NOP NOP NOP NOP
;Example usage:
;mov_abs32 eax, (gdtr-KADDR_OFFSET)
;lgdt [eax]
</syntaxhighlight>
== Possible Problems ==
You may experience some problems. Fix them '''immediately''' or risk spending a lot of time debugging later...
=== My kernel is way too big
Try each of the following, in order:
* Make sure you're compiling with the -nostdlib -nodefaultlibs options▼
* You can try changing the OUTPUT_FORMAT to elf64-little▼
* Try cross-compiling the '''latest''' version of binutils and gcc▼
* Try linking your kernel with the option "-z max-page-size=0x1000" to force the linker to use 4kb pages.
▲* Make sure you're compiling with the -nostdlib option (equivalent to passing the both -nodefaultlibs and -nostartfiles options).
▲* You can try changing the OUTPUT_FORMAT to elf64-little.
▲* Try cross-compiling the '''latest''' version of binutils and gcc.
== Kernel Virtual Memory ==
Line 212 ⟶ 240:
Another approach is to treat the kernel address space as any other address space and dynamically map its regions. This provides the advantage of simplifying the page allocator by avoiding the need of physical memory 'zones': all physical RAM is available for any part of the kernel. An example of this approach is mapping the kernel to 0xfffffff800000000 as usual. Below that virtual address you put a large mapping for the entire physical address space, and use the virtual 0xfffffff800000000 -> 0xffffffffffffffff region above kernel memory area as a temporary mappings space.
==
===
* [[X86-64]]
===Forum Threads
* [http://forum.osdev.org/viewtopic.php?f=8&t=16779 Creating a 64-bit Kernel Tutorial] about this article
* [http://forum.osdev.org/viewtopic.php?p=170634 Linker-script writers beware
* [http://forum.osdev.org/viewtopic.php?t=21720 Long-mode Kernels and the AMD64 ABI 'Red Zone'] on the 'red zone' and its major effect on interrupt handling
* [http://forum.osdev.org/viewtopic.php?f=1&p=136701 Leaving long mode] to protected mode
▲* [http://forum.osdev.org/viewtopic.php?p=170634 Linker-script writers beware]: COMMON Symbols
* [http://forum.osdev.org/viewtopic.php?f=1&t=17213 Switching from long mode to compatibility mode]
[[Category:Tutorials|Creating a 64-bit kernel]]
[[Category:X86-64|Creating a 64-bit kernel]]
[[Category:Kernel]]
|