Ooh boy, someone has a very wrong conception of paging here. Get some sleep. Then we can talk about what processor mode you are in. Because you are using multiboot, you must still be in 32-bit mode, so this snippet doesn't do anything, because you already are in protected mode:
Code:
mov rax, cr0
or eax, 1
mov cr0, rax
I'm guessing you are compiling your code for 64-bit mode, and this will not work with multiboot. I have my kernel in 64-bit mode, but I tell GRUB that's a module, and I have a 32-bit loader I give as kernel to GRUB. The actual kernel is an ELF file, so loading it is actually simple, because I know where it is supposed to go. And I can just tell GRUB to page-align it.
Next problem is the paging structures: Those are all one page in size, or 512 64-bit entities. And except for the PML4, there is more than one of each. I have a very simple allocator in the loader kernel: It allocates memory from the low memory that should be free. Reserving the first and last page of the initial 640 kB still gives you 158 pages. So what it does is:
Code:
static uintptr_t hwm = 0x1000;
void *alloc_page(void) {
void *r = (void*)hwm;
if (hwm == 0x9f000)
panic("Out of conv. memory while booting");
hwm += 0x1000;
return r;
}
Then writing an mmap function is actually pretty simple:
Code:
uint64_t *next_level(uint64_t *this_level, size_t next_num)
{
uint64_t *r;
if (!(this_level[next_num] & 1) {
r = alloc_page();
memset(t, 0, 4096);
this_level[next_num] = (uintptr_t)r | 3;
} else
r = (void*)(this_level[next_num] & -4096ull);
return r;
}
static uint64_t *pml4;
void mmap(uint64_t physaddr, uint64_t virtaddr, size_t len, uint64_t attr)
{
uint64_t *pdpt, *pdt, *pt;
size_t pdpn, pdn, ptn, pn;
if (!pml4) {
pml4 = alloc_page();
memset(pml4, 0, 4096);
}
assert((physaddr & 0xfff) == (virtaddr & 0xfff));
len += physaddr & 4095;
physaddr &= -4096ull;
virtaddr &= -4096ull;
len = (len + 4095) & -4096ul;
for (; len; len -= 4096, virtaddr += 4096, physaddr += 4096) {
pn = (virtaddr >> 12) & 0x1ff;
ptn = (virtaddr >> 21) & 0x1ff;
pdn = (virtaddr >> 30) & 0x1ff;
pdpn = (virtaddr >> 39) & 0x1ff;
pdpt = next_level(pml4, pdpn);
pdt = next_level(pdpt, pdn);
pt = next_level(pdt, ptn);
pt[pn] = physaddr | attr | 1;
}
}
Now to draw it all together, the actual mapping code:
Code:
size_t pn = eh->e_phnum;
for (Elf64_Phdr *ph = (void*)(base + eh->e_phoff); pn; pn--; ph = (void*)((char*)ph + eh->e_phentsize)) {
if (ph->p_type == PT_LOAD) {
mmap(base + ph->p_offset, ph->p_vaddr, ph->p_filesz, get_attr(ph->p_flags));
size_t n = (ph->p_memsz - ph->p_filesz) >> 12;
if (n) {
void *p = alloc_page();
for (size_t i = 1; i < n; i++) alloc_page();
mmap(p, (ph->p_vaddr + ph->p_filesz + 4095) & -4096ull, get_attr(ph->p_flags));
}
}
}
void *stack = alloc_page(); alloc_page(); alloc_page(); alloc_page();
mmap(stack, 0xffffffff00000000 - 4*4096, 4*4096, PF_WRITE | PF_NX);
uint64_t stack_top = prepare_kernel_stack(stack, 4*4096, mb_info);
extern char trampoline_start[], trampoline_end[];
mmap((uintptr_t)trampoline_start, (uintptr_t)trampoline_start, trampoline_end - trampoline_start, 0);
go64(pml4, eh->e_entry & 0xffffffff, stack_top);
And finally:
Code:
go64:
mov eax, cr4
or eax, 0x20
mov cr4, eax
mov eax, [esp+4]
mov cr3, eax
mov ecx, 0xc0000080
rdmsr
or eax, 0x100
wrmsr
mov edx, [esp + 8]
mov edi, [esp+12]
mov esi, [esp+16]
mov eax, cr0
or eax, 0x80000001
global trampoline_start
trampoline_start:
mov cr0, eax
jmp CSEG64:continue
bits 64
continue:
mov esp, esi
shl rsp, 32
mov edi, edi
or rsp, rdi
xor eax, eax
not eax
shl rax, 32
or rdx, rax
jmp rdx
global trampoline_end
trampoline_end: