Page 1 of 1

Paging in assembly

Posted: Tue Jul 03, 2012 6:04 pm
by blm768
I've been working on making a 32-bit assembly bootstrapper to sit between GRUB and a 64-bit kernel. I've made some progress, but I'm stuck at page setup. I can create the page directories/tables, and they all seem to be aligned and filled properly, but QEMU resets when I actually enable paging. I've tried digging around the forum with no results.
Currently, my code just identity pages the first two MiB of memory, which should be plenty to hold the bootstrapper.
Here's my code:

Code: Select all

BITS 32

MODULEALIGN        equ        1<<0
MEMINFO            equ        1<<1
FLAGS              equ        MODULEALIGN | MEMINFO
MAGIC              equ        0x1BADB002
CHECKSUM           equ        -(MAGIC + FLAGS)

PAGEDIRPTABLESIZE equ 4 * 8; 4 64-bit ints
PAGEDIRSIZE equ 512 * 8
PAGETABLESIZE equ 512 * 8
PAGESIZE equ 4096

BOOT32MAPSIZE equ 512 * PAGESIZE

section .text      ; Next is the Grub Multiboot Header

align PAGESIZE
multiboot_header:
       dd MAGIC
       dd FLAGS
       dd CHECKSUM

STACKSIZE equ 0x4000

boot:
	mov esp, STACKSIZE+stack

	push eax
	push ebx

	lgdt [gdtr]
	mov ax, 0x10
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax

	call init_paging

	;eax, ebx are already on the stack.
	;call main

init_paging:
	;Set up basic structure
	;To do: consolidate movs?
	mov eax, page_dir
	mov [page_dir_ptable], eax
	mov eax, page_table
	or eax, 3
	mov [page_dir], eax

	;Identity map
	mov ecx, BOOT32MAPSIZE - PAGESIZE
	mov edi, page_table + PAGETABLESIZE - 8
	map_identity:
		mov eax, ecx
		or eax, 3
		mov [edi], eax
		sub edi, 8
		sub ecx, PAGESIZE
		jnz map_identity
		mov eax, ecx
		or eax, 3
		mov [edi], eax

	;Enable PAE and paging
	cli
	mov eax, cr4
	or eax, 1 << 5
	mov cr4, eax
	mov eax, page_dir_ptable
	mov cr3, eax
	mov eax, cr0
	or eax, 1 << 31
	mov cr0, eax

	stop:
		hlt
		jmp stop

	ret

;;----;;
;;Data;;
;;----;;

section .data
align PAGESIZE

gdtr:
	dw gdt_end - gdt - 1 ; size of the GDT
	dd gdt ; linear address of GDT

gdt:
	dd 0, 0
	db 0xFF, 0xFF, 0, 0, 0, 0x9A, 0xC0 | 0xF, 0
	db 0xFF, 0xFF, 0, 0, 0, 0x92, 0xC0 | 0xF, 0
gdt_end:

section .bss
align PAGESIZE

stack:
	resb STACKSIZE

page_dir:
	resb PAGEDIRSIZE

page_table:
	resb PAGETABLESIZE

page_dir_ptable:
	resb PAGEDIRPTABLESIZE

Re: Paging in assembly

Posted: Tue Jul 03, 2012 10:24 pm
by bluemoon
blm768 wrote:I've been working on making a 32-bit assembly bootstrapper to sit between GRUB and a 64-bit kernel.
1. Long mode uses PML4(4 x 512 entries). you are using 32-bit PAE.
2. grub is upset you abuse the stack before it is usable. (you just set ESP but SS is undefined in second line)
3. You didn't perform iret(or the like) to refresh CS after LGDT

I didn't find the actual bug for the reset issue, but a few advice:
1. I heard latest grub support booting 64-bit directly, you may try that.
2. or you may roll your own boot loader, imo this provide much flexibility, one benefit is you can detect cpu and then decide to load 32bit or 64-bit files, with grub you're already in protected mode before you can detect anything.

Re: Paging in assembly

Posted: Tue Jul 03, 2012 10:48 pm
by jbemmel
You are not setting the present bit after mov eax, page_dir (missing or eax, 3 like you do for the next level)

Also, you are only setting the lower 32 bits of each 64-bit PTE. If the upper bits are not 0, you are mapping random memory locations. Since those pages are in the .bss section, I think they are not being cleared automatically

Re: Paging in assembly

Posted: Tue Jul 03, 2012 10:53 pm
by blm768
Thanks for the advice; I'll see if I can find how to get GRUB to do a direct 64-bit boot. If I can't, I'll probably modify my code to match your suggestions. I'd rather not mess with making my own bootloader yet because I want to keep GRUB's flexibility and preserve the ability to boot alongside other OSes, but that might be a project to take on after I get my kernel and userland operational.
Since those pages are in the .bss section, I think they are not being cleared automatically.
I looked at the Multiboot spec, and it seems to say that .bss is set to zero by the bootloader.

Edit:

I think I've fixed all the issues that were pointed out, but I'm still having no luck. I'll attach my modified code in case anyone can find another problem, and I'll keep messing with it and see what I can find.

At least my code is more solid now. That's always a satisfying feeling.

Another edit:
I've had qemu dump registers to a log file on each reset, and it appears that the base of the code segment is getting set to 000f0000 instead of 0 at some point before I enable paging. I'm not sure what is causing this; my GDT looks fine to me. There also seem to be logged resets after I run the page-table setup even if I don't actually enable paging, but the CPU doesn't enter an infinite reset loop unless I actually enable it. Is this normal? Is the last logged reset just the system "powering down?"

Re: Paging in assembly

Posted: Wed Jul 04, 2012 7:53 am
by jbemmel
You forgot to "Enable long mode by setting the EFER.LME flag in MSR 0xC0000080" (see http://wiki.osdev.org/X86-64)

Re: Paging in assembly

Posted: Wed Jul 04, 2012 9:36 am
by blm768
jbemmel wrote:You forgot to "Enable long mode by setting the EFER.LME flag in MSR 0xC0000080" (see http://wiki.osdev.org/X86-64)
I'm planning on enabling long mode after I've done more setup in 32-bit mode. Isn't the PML4 setup binary-compatible with 32-bit PAE until I actually branch into long mode?

Re: Paging in assembly

Posted: Wed Jul 04, 2012 9:42 am
by bluemoon
blm768 wrote:Isn't the PML4 setup binary-compatible with 32-bit PAE until I actually branch into long mode?
No.

PAE32 is 4x512x512 entries
PML4 is 512x512x512x512 entries

Re: Paging in assembly

Posted: Wed Jul 04, 2012 9:53 am
by Griwes
But you can actually setup long mode and execute with no change in environment in compatibility mode... Just setup long mode paging, enable it, ??? and profit. We should start dropping protected mode support anyway...

Re: Paging in assembly

Posted: Wed Jul 04, 2012 10:27 am
by blm768
bluemoon wrote:
blm768 wrote:Isn't the PML4 setup binary-compatible with 32-bit PAE until I actually branch into long mode?
No.

PAE32 is 4x512x512 entries
PML4 is 512x512x512x512 entries
I'm starting to get it now. The wiki said to load the the PML4 (which, from what I recall, is the second level of the table) into CR3, but I guess it meant "load the PML4-style PDPT into CR3."

Edit:
It looks like I either misread the docs or read a page with an error. It looks like the PML4 is the first level. That still doesn't explain why my current code breaks (it should be valid 32-bit PAE paging), but hopefully this will fix it.

Another edit:
It seems to be working now! Thanks for the help!