Hallo,
lange ist es her, dass ich mich damit beschäftigt habe. Allerdings juckt es mich in den Fingern mal wieder was zu machen. Da ich mich mal mit was anderem als vorher 32-bit beschäftigen wollte, habe ich mich an einen 64-bit Feldversuch gehalten. Dabei habe ich folgendes Tutorial gelesen und versucht mein eigenes Ding zu machen:
http://wiki.osdev.org/64-bit_Higher_Half_Kernel_with_GRUB_2 Zum kompilieren und linken des codes habe ich mir unter mingw einen cross-compiler nach der Anleitung im Wiki kompiliert.
Beim zwischendurch erstellen des Kernels wird immer folgender Fehler von ld ausgespuckt:
./obj/bootstrap.S.o: In function `asmKernelStartupGdt':
src/arch/x86-64/bootstrap.S:(.boot+0x86): relocation truncated to fit: R_X86_64_32 against `.text'
Jetzt bin ich mir allerdings nicht so sicher, ob das ganze so funktioniert/funktionieren kann, wie ich es gerne hätte. Ich würde gerne den assembler startup code in eine einzige Datei packen. Zuerst kommt also der 32-Bit Code, der eine temporäre GDT anlegt und das Paging für den 64-bit Modus aktiviert. Dann sollte zum 64-bit Startup-Code gesprungen werden, der in der gleichen Datei liegt, und schließlich die initKernel vom C-Code aufruft.
Kann ich das überhaupt so machen? Oder muss ich die Dateien doch auftrennen? Oder läuft da bei mir generell etwas schief? Ich bin, was die Fehlermeldung von ld angeht dezent ratlos.
Mein momentaner startup-code sieht wie folgt aus:
[BITS 32]
; just an own section for multiboot header in cause of a may
; be very large kernel at the future!
[SECTION .multiboot]
; some defines needed for multiboot header start
MULTIBOOT2_HEADER_MAGIC equ 0xe85250d6
GRUB_MULTIBOOT_ARCHITECTURE_I386 equ 0
MULTIBOOT_HEADER_TAG_FRAMEBUFFER equ 8
MULTIBOOT_HEADER_TAG_OPTIONAL equ 1
MULTIBOOT_HEADER_TAG_END equ 0
; align 64 bits boundary
ALIGN 8
; multiboot header
multibootHeaderStart:
; magic number for grub2
dd MULTIBOOT2_HEADER_MAGIC
; architecture: i386
dd GRUB_MULTIBOOT_ARCHITECTURE_I386
; Header length
dd multibootHeaderEnd - multibootHeaderStart
; Grub2 checksum
dd -(MULTIBOOT2_HEADER_MAGIC + GRUB_MULTIBOOT_ARCHITECTURE_I386 + (multibootHeaderEnd - multibootHeaderStart))
; enable vbe framebuffer (1024 * 768)
framebufferTagStart:
dw MULTIBOOT_HEADER_TAG_FRAMEBUFFER
dw MULTIBOOT_HEADER_TAG_OPTIONAL
dd framebufferTagEnd - framebufferTagStart
dd 1024
dd 768
dd 32
framebufferTagEnd:
dw MULTIBOOT_HEADER_TAG_END
dw 0
dd 8
multibootHeaderEnd:
; just an own section for startup code
[SECTION .boot]
; global declaration needed for linking
[GLOBAL asmKernelStartup]
; variables for holding the multiboot informations
[EXTERN varMultibootHeaderMagic]
[EXTERN varMultibootHeaderAddress]
[EXTERN varBootstrapStack]
[EXTERN varBootsrapPml4]
[EXTERN varBootstrapPdt]
[EXTERN varBootstrapPd]
; Initialise simple paging, initialise gdt and jump to 64 bit code
asmKernelStartup:
; disable interrupts
cli
; save multiboot header and address for longmode entry later
mov [varMultibootHeaderMagic], eax
mov [varMultibootHeaderAddress], ebx
; load initial gdt
lgdt [Gdtr1]
jmp 0x08:asmKernelStartupGdt
asmKernelStartupGdt:
; reset eflags
push dword 0
popf
; set registers after loading gdt
mov eax, 0x10
mov ds, ax
mov ss, ax
; initialise bootstrap stack
mov esp, varBootstrapStack
; check for available cpuid
pushfd ; Store the FLAGS-register.
pop eax ; Restore the A-register.
mov ecx, eax ; Set the C-register to the A-register.
xor eax, 1 << 21 ; Flip the ID-bit, which is bit 21.
push eax ; Store the A-register.
popfd ; Restore the FLAGS-register.
pushfd ; Store the FLAGS-register.
pop eax ; Restore the A-register.
push ecx ; Store the C-register.
popfd ; Restore the FLAGS-register.
xor eax, ecx ; Do a XOR-operation on the A-register and the C-register.
jz .noCpuid ; The zero flag is set => no CPUID!
; check for extended cpuid functions
mov eax, 0x80000000 ; Set the A-register to 0x80000000.
cpuid ; CPU identification.
cmp eax, 0x80000001 ; Compare the A-register with 0x80000001.
jb .noLongmodeSupport ; It is less => there is no long mode support!
; check for longmode available
mov eax, 0x80000001 ; Set the A-register to 0x80000001.
cpuid ; CPU identification.
test eax, 1 << 29 ; Test if the LM-bit, which is bit 29, is set in the D-register.
jz .noLongmodeSupport ; They aren't => there is no long mode support!
; check for page address extension
mov eax, 0x80000001 ; Set the A-register to 0x80000001.
cpuid ; CPU identification.
test edx, 1 << 6 ; Test if the PAE-bit, which is bit 6, is set in the D-register.
jz .noLongmodeSupport ; They aren't => there is no long mode support!
; check for page size extension
mov eax, 0x80000001 ; Set the A-Register
cpuid ; CPU identification
test edx, 1 << 3 ; Test if the PSE-bit, which is bit 3, is set in the D-register
jz .noLongmodeSupport ; They aren't => there is no long mode support!
; set up initial paging!
call setupInitialPaging
mov eax, Gdtr2
lgdt [eax]
; jump to long mode entry point!
jmp 0x08:asmLongmodeEntry
.noLongmodeSupport:
; TODO: print error message
jmp .errorEndlessLoop
.noCpuid:
; TODO: print error message
jmp .errorEndlessLoop
.errorEndlessLoop:
jmp .errorEndlessLoop
setupInitialPaging:
ret
; Some error messages for previous checks!
msgNoCpuid: db 'cpuid is not supported',13,10,0
msgNoLongmodeSupport: db 'long mode (64bit) is not supported',13,10,0
TmpGdt:
DQ 0x0000000000000000
DQ 0x00CF9A000000FFFF
DQ 0x00CF92000000FFFF
DQ 0x0000000000000000
DQ 0x00A09A0000000000
DQ 0x00A0920000000000
Gdtr1:
DW 23
DD TmpGdt
Gdtr2:
DW 23
DD TmpGdt + 24
DD 0
[BITS 64]
[SECTION .text]
; kernel startup c-code
[EXTERN initKernel]
; long mode kernel startup initialises stack and calls c-code
asmLongmodeEntry:
; initialise new kernel stack
mov rsp, kernelStack
; push multiboot data
mov rax, [varMultibootHeaderAddress]
mov rbx, [varMultibootHeaderMagic]
push rax
push rbx
; call c kernel
mov rax, initKernel
call [rax]
; disable interrupts and jump to never ending loop when initKernel returns
cli
jmp .longmodeEntryHalt
; hang within endless loop!
.longmodeEntryHalt:
jmp .longmodeEntryHalt
[SECTION .bss]
resb 8192
kernelStack:
Mein linker script sieht wie folgt aus:
OUTPUT_FORMAT(elf64-x86-64)
OUTPUT_ARCH(i386:x86-64)
ENTRY(asmKernelStartup)
KERNEL_PHYSICAL_BASE = 0x0000000000100000;
KERNEL_VIRTUAL_BASE = 0xFFFFFFFF80000000;
KERNEL_VIRTUAL_TO_PHYSICAL = KERNEL_VIRTUAL_BASE - KERNEL_PHYSICAL_BASE;
SECTIONS
{
. = KERNEL_PHYSICAL_BASE;
/* define variables for kernels start address */
kernelVirtualStartAddress = . + KERNEL_VIRTUAL_TO_PHYSICAL;
kernelPhysicalStartAddress = .;
/* kernels virtual base */
kernelVirtualBase = KERNEL_VIRTUAL_BASE;
/* the multiboot header MUST come early enough in the output object file */
.boot :
{
*(.multiboot*)
*(.boot*)
. = ALIGN(0x1000);
varMultibootHeaderMagic = .;
. += 0x0020;
varMultibootHeaderAddress = .;
. += 0x0020;
. = ALIGN(0x1000);
varBootsrapPml4 = .;
. += 0x1000;
varBootstrapPdt = .;
. += 0x1000;
varBootstrapPd = .;
. += 0x1000;
. += 0x8000;
varBootstrapStack = .;
}
. += KERNEL_VIRTUAL_TO_PHYSICAL;
/* code section and read only data (e.g. strings within c-part) */
.text ALIGN(0x1000) : AT(ADDR(.text) - KERNEL_VIRTUAL_TO_PHYSICAL)
{
*(.text*)
*(.gnu.linkonce.t*)
}
/* Initialised data - should be page aligned */
.data ALIGN(0x1000) : AT(ADDR(.data) - KERNEL_VIRTUAL_TO_PHYSICAL)
{
*(.data*)
*(.gnu.linkonce.d*)
}
/* Read only data - should be page aligned too */
.rodata ALIGN(0x1000) : AT(ADDR(.rodata) - KERNEL_VIRTUAL_TO_PHYSICAL)
{
*(.rodata*)
*(.gnu.linkonce.r*)
}
/* Uninitialised data */
.bss : AT(ADDR(.bss) - KERNEL_VIRTUAL_TO_PHYSICAL)
{
*(.bss)
*(COMMON)
*(.gnu.linkonce.b*)
}
/* define variable for kernels end address */
kernelVirtualEndAddress = .;
kernelPhysicalEndAddress = . - KERNEL_VIRTUAL_TO_PHYSICAL;
/* discard comment sections */
/DISCARD/ :
{
*(.comment)
*(.eh_frame*)
}
}