Page 1 of 1

x86_64 kernel page table misalignment

Posted: Mon Sep 06, 2010 4:04 pm
by computafreak
I get the feeling I'm missing something blatantly obvious here. The problem I would appreciate help with is that when I specify an ALIGN 0x1000 directive in front of a page table declaration in NASM, the address I end up with isn't aligned. It's always precisely 0x30 bytes too high an address. This crops up every time, and looking in the linker map confirms that the PML4 base is at the address I see in Bochs debugger. For these reasons, I suspect that my linker script is at fault; however, comparing it to the one used by Xomb and the one provided as a base in Creating a 64-bit kernel doesn't appear to yield any obvious faults. All three of them seem to do so same thing, with the only practical difference being that my linker script shuffles around with sections, not files.

I've cut out a great deal of my code which performs the checks for long mode, and ended up with a set of 3 files which I've included below. As you can probably tell, I create a new section at the 1 MiB mark which contains the long mode and kernel bootstrap; everything else gets linked at 0xFFFF800000100000, which my code will probably jump to (assuming I've not messed up with the paging structures; it's a little difficult for me to verify in Bochs at the moment)

LinkScript.ld:

Code: Select all

OUTPUT_FORMAT(elf64-x86-64)
ENTRY(asmEntry)
LINEAR_ADDRESS = 0x100000;
VIRTUAL_ADDRESS = 0xFFFF800000000000;

SECTIONS
{
	. = LINEAR_ADDRESS;

	textStart = .;

	.pmode ALIGN(0x1000) :
	{
		*(.multiboot*)
		*(.pmode.gdt*)
		*(.pmode.text*)
	}


	. += VIRTUAL_ADDRESS;

	.text ALIGN(0x1000) : AT(ADDR(.text) - VIRTUAL_ADDRESS)
	{
		*(.text)
		*(.rodata*)
	}
	textEnd = .;

	dataStart = .;
	.data ALIGN(0x1000) : AT(ADDR(.data) - VIRTUAL_ADDRESS)
	{
		constructorStart = .;
		*(.ctor*)
		constructorEnd = .;
		*(.data)
	}
	dataEnd = .;

	bssStart = .;
	.bss ALIGN(0x1000) : AT(ADDR(.bss) - VIRTUAL_ADDRESS)
	{
		startBss = .;
		*(COMMON)
		*(.bss)
		endBss = .;
	}
	bssEnd = .;

	/DISCARD/ :
	{
		*(.eh_frame)
	}
	end = .;
}
LongMode.s:

Code: Select all

GLOBAL asmEntry
EXTERN textStart
EXTERN bssStart
EXTERN bssEnd
EXTERN HigherHalfEntry

%include "Multitasking/Paging.s"

; Basic GrUB information
ModuleAlign equ 1
MemoryMap	equ 2
AOUTKludge	equ (1 << 16)
Flags		equ ModuleAlign | MemoryMap | AOUTKludge
Magic		equ 0x1BADB002
Checksum	equ -(Magic + Flags)

[BITS 32]

section .multiboot
align 0x4
	MultibootHeader:
		dd Magic
		dd Flags
		dd Checksum
		dd (MultibootHeader)	; header_addr
		dd (textStart)			; load_addr
		dd (bssStart - VirtualAddressBase)			; load_end_addr
		dd (bssEnd - VirtualAddressBase)			; bss_end_addr
		dd (asmEntry)			; entry_addr

section .pmode.gdt

align 0x8
	gdt:
		dq 0x0000000000000000	; Standard NULL entry
		dq 0x00AF9A000000FFFF	; 64-bit kernel code
		dq 0x00AF93000000FFFF	; 64-bit kernel data
	gdtEnd:
align 0x8
gdtPtr:
	dw gdtEnd - gdt - 1
	dq gdt

section .pmode.text
; GrUB will jump here, so it needs to be 32-bit code
asmEntry:
	; First, make certain that an interrupt won't arrive and mess up the boot code
	cli

	mov ecx, cr0
	and ecx, 0x7FFFFFFF
	mov cr0, ecx

	; Enable bit 5 (PAE) in CR4
	mov ecx, cr4
	bts ecx, 5
	mov cr4, ecx

	; Switch on long mode in the necessary MSR
	mov ecx, 0xC0000080
	rdmsr
	bts eax, 8
	wrmsr
xchg bx, bx
	mov ecx, (pml4Base - VirtualAddressBase)	; Load ECX with the physical address of the boot page directory
	mov cr3, ecx

	mov ecx, cr0
	bts ecx, 31
	mov cr0, ecx

	lgdt [gdtPtr]
	; After this jump, the CPU is in long mode
	jmp 0x8:(HigherHalfEntry - VirtualAddressBase)
Multitasking/Paging.s:

Code: Select all

%ifndef Paging_S
%define Paging_S

; This file just contains the basic structures necessary to get the higher half operating
; It also stores the first page directory entries (including recursive page mapping)

VirtualAddressBase				equ 0xFFFF800000000000
VirtualAddressBasePML4			equ (VirtualAddressBase >> 40) & 0x1FF
VirtualAddressBasePDPT			equ (VirtualAddressBase >> 31) & 0x1FF
VirtualAddressBasePageDirectory	equ (VirtualAddressBase >> 22) & 0x1FF
VirtualAddressBasePageTable		equ (VirtualAddressBase >> 13) & 0x1FF

section .data

; All this just creates a simple mapping for the bootstrap code
; pageTable gets reused so that it can create another mapping later

align 0x1000
; Using this page table allows me to map any virtual address to 0
; 512 entries in the page table, resulting in each page table representing 2 MiB
pageTable:
	%assign i 0
	%rep 512
		dq i | 11b
		%assign i i+0x1000
	%endrep

align 0x1000
pageDirectory:
	dq pageTable - VirtualAddressBase + 11b
	%rep 511
		dq 0
	%endrep

; 512 entries in the PDPT
align 0x1000
pdpt:
	dq pageDirectory - VirtualAddressBase + 11b
	%assign i 0
	%rep 511
		dq 0
	%endrep

align 0x1000
higherHalfPageDirectory:
	times (VirtualAddressBasePageTable) dq 0
	dq (pageTable - VirtualAddressBase + 11b)
	times (512 - VirtualAddressBasePageTable - 1) dq 0

; 512 entries in the PDPT
align 0x1000
higherHalfPDPT:
	times (VirtualAddressBasePageDirectory) dq 0
	dq (higherHalfPageDirectory - VirtualAddressBase + 11b)
	times (512 - VirtualAddressBasePageDirectory) dq 0

; There are 512 entries in the PML4
align 0x1000
	pml4Base:
		dq pdpt - VirtualAddressBase + 11b
		times 254 dq 0
		dq (higherHalfPDPT - VirtualAddressBase + 11b)
		times 255 dq 0
		dq pdpt - VirtualAddressBase + 11b
%endif
In case it helps, I compile my assembly using 'nasm -f elf64 -I ./Source/' and link using 'x86_64-elf-ld -T "LinkScript.ld" -Map "LinkerMap.txt" -o kernel.elf $(OBJ_FILES)'.

Re: x86_64 kernel page table misalignment

Posted: Mon Sep 06, 2010 5:26 pm
by gerryg400

Code: Select all

 dataStart = .;
   .data ALIGN(0x1000) : AT(ADDR(.data) - VIRTUAL_ADDRESS)
   {
      constructorStart = .;
      *(.ctor*)
      constructorEnd = .;
      *(.data)
   }
   dataEnd = .;
You are aligning the pages within the .data section, but the .data section is not aligned because the .ctor comes before it. Try

Code: Select all

 dataStart = .;
   .data ALIGN(0x1000) : AT(ADDR(.data) - VIRTUAL_ADDRESS)
   {
      *(.data)
      constructorStart = .;
      *(.ctor*)
      constructorEnd = .;
   }
   dataEnd = .;

Re: x86_64 kernel page table misalignment

Posted: Mon Sep 06, 2010 11:56 pm
by computafreak
That's page-aligned my PML4 structure, and fixed one or two other little things which were being strange. Looking back at one of the examples that I linked to, they've also put their data section before the constructors. A little confused as to how I missed that; thank you.