// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from memtest86+ vmem.c // // vmem.c - MemTest-86 // // Virtual memory handling (PAE) // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include "boot.h" #include "cpuid.h" #include "vmem.h" //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ static uintptr_t mapped_window = 2; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ static void load_pdbr() { void *page_table; if (cpuid_info.flags.lm == 1) { page_table = pml4; } else { page_table = pdp; } __asm__ __volatile__( #ifdef __x86_64__ "movq %0, %%cr3\n\t" #else "movl %0, %%cr3\n\t" #endif : : "r" (page_table) : "rax" ); } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ uintptr_t map_framebuffer(uintptr_t base_addr, size_t size) { uintptr_t first_page = base_addr >> VM_PAGE_SHIFT; uintptr_t last_page = (base_addr + size - 1) >> VM_PAGE_SHIFT; if (first_page >= VM_PAGE_C(3,GB) && last_page < VM_PAGE_C(4,GB)) { // No mapping required. return base_addr; } // Compute the page table entries. uintptr_t page = first_page; for (int i = 0; i < 512; i++) { pd3[i] = (page << VM_PAGE_SHIFT) + 0x83; if (++page > last_page) break; } // Reload the PDBR to flush any remnants of the old mapping. load_pdbr(); // Return the mapped address. return ADDR_C(3,GB) + base_addr % VM_PAGE_SIZE; } bool map_window(uintptr_t start_page) { uintptr_t window = start_page >> (30 - PAGE_SHIFT); if (window < 2) { // Less than 2 GB so no mapping is required. return true; } if (cpuid_info.flags.pae == 0) { // No PAE, so we can only access 4GB. if (window < 4) { mapped_window = window; return true; } return false; } if (cpuid_info.flags.lm == 0 && (start_page >= PAGE_C(64,GB))) { // Fail, we want an address that is out of bounds // for PAE and no long mode (ie. 32 bit CPU). return false; } // Compute the page table entries. for (uintptr_t i = 0; i < 512; i++) { pd2[i] = ((uint64_t)window << 30) + (i << VM_PAGE_SHIFT) + 0x83; } // Reload the PDBR to flush any remnants of the old mapping. load_pdbr(); mapped_window = window; return true; } void *first_word_mapping(uintptr_t page) { void *result; if (page < PAGE_C(2,GB)) { // If the address is less than 2GB, it is directly mapped. result = (void *)(page << PAGE_SHIFT); } else { // Otherwise it is mapped to the third GB. uintptr_t alias = PAGE_C(2,GB) + page % PAGE_C(1,GB); result = (void *)(alias << PAGE_SHIFT); } return result; } void *last_word_mapping(uintptr_t page, size_t word_size) { return (uint8_t *)first_word_mapping(page) + (PAGE_SIZE - word_size); } uintptr_t page_of(void *addr) { uintptr_t page = (uintptr_t)addr >> PAGE_SHIFT; if (page >= PAGE_C(2,GB)) { page = page % PAGE_C(1,GB); page += mapped_window << (30 - PAGE_SHIFT); } return page; }