// Copyright 2018 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "trampoline.h" #include #include #include #include #include #include #include // Set up minimal page tables for 64-bit mode. These map the low // 4G of address space directly to the low 4G of physical memory. static uint32_t get_cr0(void) { uint32_t cr0; __asm__("mov %%cr0, %0" : "=r"(cr0)); return cr0; } static void set_cr0(uint32_t cr0) { __asm__ volatile("mov %0, %%cr0" :: "r"(cr0)); } static void set_cr3(void* cr3) { __asm__ volatile("mov %0, %%cr3" :: "r"(cr3) : "memory"); } static uint32_t get_cr4(void) { uint32_t cr4; __asm__("mov %%cr4, %0" : "=r"(cr4)); return cr4; } static void set_cr4(uint32_t cr4) { __asm__ volatile("mov %0, %%cr4" :: "r"(cr4)); } static uint64_t read_msr(uint32_t msr) { uint64_t value; __asm__("rdmsr" : "=A"(value) : "c"(msr)); return value; } static void write_msr(uint32_t msr, uint64_t value) { __asm__ volatile("wrmsr" :: "c"(msr), "A"(value)); } static bool have_page1gb; typedef union page_table { alignas(4096) uint64_t pml4e[512]; alignas(4096) uint64_t pdpte[512]; alignas(4096) uint64_t pde[512]; } page_table_t; static page_table_t* page_table_memory_start; static page_table_t* page_table_memory_end; static page_table_t* get_page_table(void) { if (unlikely(page_table_memory_start >= page_table_memory_end)) { panic("ran out of page table memory"); } return page_table_memory_start++; } static uint64_t get_pdpte(unsigned int idx) { // Each PDPTE covers 1G. if (have_page1gb) { // A single entry direct-maps 1G. return (((uint64_t)idx << 30) | X86_MMU_PG_P | X86_MMU_PG_RW | X86_MMU_PG_PS); } else { // A single entry indirects through a PDE table. page_table_t* pdpt = get_page_table(); for (size_t i = 0; i < 512; ++i) { // Each PDE covers 2M. pdpt->pde[i] = (((uint64_t)i << 21) | X86_MMU_PG_P | X86_MMU_PG_RW | X86_MMU_PG_PS); } return ((uint64_t)(uintptr_t)pdpt | X86_MMU_PG_P | X86_MMU_PG_RW); } } // The whole PDPT covers 512G, so we only need the one. static uint64_t get_pml4e(void) { page_table_t* pdpt = get_page_table(); // Each PDPTE covers 1G, so we need four of those. for (unsigned int i = 0; i < 4; ++i) { pdpt->pdpte[i] = get_pdpte(i); } memset(&pdpt->pdpte[4], 0, sizeof(*pdpt) - offsetof(page_table_t, pdpte[4])); return (uint64_t)(uintptr_t)pdpt | X86_MMU_PG_P | X86_MMU_PG_RW; } static page_table_t* get_pml4(void) { page_table_t* pml4 = get_page_table(); // The top-level PML4 just needs one PML4E to point to the PDPT. pml4->pml4e[0] = get_pml4e(); memset(&pml4->pml4e[1], 0, sizeof(*pml4) - offsetof(page_table_t, pml4e[1])); return pml4; } void enable_64bit_paging(uintptr_t start, uintptr_t end) { // Use available memory for page tables. page_table_memory_start = (void*)((start + 4096 - 1) & -4096u); page_table_memory_end = (void*)(end & -4096u); // Determine if 1G pages are available. { uint32_t a, b, c, d; __cpuid(0x80000001u, a, b, c, d); have_page1gb = (d & (1u << 26)) != 0; } // Use the 64-bit (PAE) page table format. // This is required in 64-bit (Long) mode. set_cr4(get_cr4() | X86_CR4_PAE); // Enable 64-bit (Long) mode. write_msr(X86_MSR_IA32_EFER, read_msr(X86_MSR_IA32_EFER) | X86_EFER_LME); // Install the page tables. set_cr3(get_pml4()); // Enable paging. // Hereafter we're using the direct-mapped page tables just built. set_cr0(get_cr0() | X86_CR0_PG); }