13using namespace MaxOS::memory;
14using namespace MaxOS::system;
15using namespace MaxOS::common;
17volatile extern PAGE_ALIGNED
unsigned char p4_table[];
18volatile extern PAGE_ALIGNED
unsigned char p3_table[];
19volatile extern PAGE_ALIGNED
unsigned char p3_table_hh[];
20volatile extern PAGE_ALIGNED
unsigned char p2_table[];
21volatile extern PAGE_ALIGNED
unsigned char p1_table[];
37 Logger::INFO() <<
"Setting up Physical Memory Manager\n";
50 m_mmap_tag = m_multiboot->
mmap();
54 (
unsigned long) entry + m_mmap_tag->
entry_size)) {
57 if(entry->type != MULTIBOOT_MEMORY_AVAILABLE)
65 m_memory_size = (m_mmap->
addr + m_mmap->
len);
66 m_bitmap_size = m_memory_size /
PAGE_SIZE + 1;
67 m_total_entries = m_bitmap_size /
ROW_BITS + 1;
68 Logger::DEBUG() <<
"Memory Info: size = " << (
int) (m_memory_size / 1024 / 1024) <<
"mb, bitmap size = 0x"
69 << (
uint64_t) m_bitmap_size <<
", total entries = " << (
int) m_total_entries <<
", page size = 0x"
88PhysicalMemoryManager::~PhysicalMemoryManager() =
default;
117 (
unsigned long) entry + m_mmap_tag->
entry_size)) {
120 if(entry->type <= MULTIBOOT_MEMORY_AVAILABLE)
127 reserve(entry->addr, entry->len,
"MMap");
133 ((
tag->size + 7) & ~7))) {
135 if(
tag->type != MULTIBOOT_TAG_TYPE_MODULE)
139 auto*
module = (struct multiboot_tag_module*) tag;
218 ASSERT(m_used_frames < m_bitmap_size,
"No more frames available\n");
223 if(m_bit_map[
row] == 0xFFFFFFFFFFFFFFF)
229 if(m_bit_map[
row] & (1ULL << column))
233 m_bit_map[
row] |= (1ULL << column);
246 ASSERT(
false,
"Frame not found\n");
288 if(m_bit_map[
row] == 0xFFFFFFFFFFFFFFF)
294 if(m_bit_map[
row] & (1ULL << column)) {
322 m_bit_map[index] |= (1ULL <<
bit);
334 ASSERT(
false,
"Cannot allocate that much memory\n");
375 return (
pml_t*) (0xFFFF000000000000 | ((510UL << 39) | (
index3 << 30) | (
index2 << 21) | (index << 12)));
387pml_t* PhysicalMemoryManager::get_or_create_table(
pml_t* table,
size_t index,
size_t flags) {
390 if(table->entries[index].present)
482uint64_t PhysicalMemoryManager::physical_address_of_entry(
pte_t* entry) {
484 return entry->physical_address << 12;
499 return map(physical_address, virtual_address, flags, m_pml4_root_address);
520 return virtual_address;
523 *
pte = create_page_table_entry((
uint64_t) physical_address, flags);
526 asm volatile(
"invlpg (%0)"::
"r" (virtual_address) :
"memory");
528 return virtual_address;
585 map(physical_address, physical_address, flags);
596 unmap(virtual_address, m_pml4_root_address);
615 pte->present =
false;
618 asm volatile(
"invlpg (%0)"::
"r" (virtual_address) :
"memory");
642 for(
int i = 0; i < 512; i++)
655pte_t PhysicalMemoryManager::create_page_table_entry(
uintptr_t address,
size_t flags)
const {
658 .present = (flags &
PRESENT) != 0,
659 .write = (flags & WRITE) != 0,
660 .user = (flags &
USER) != 0,
661 .write_through = (flags & WRITE_THROUGH) != 0,
663 .accessed = (flags & ACCESSED) != 0,
664 .dirty = (flags &
DIRTY) != 0,
665 .huge_page = (flags & HUGE_PAGE) != 0,
666 .global = (flags &
GLOBAL) != 0,
668 .physical_address = address >> 12,
672 if(m_nx_allowed && (flags & NO_EXECUTE)) {
686void PhysicalMemoryManager::initialise_bit_map() {
689 uint64_t limit = m_kernel_start_page;
695 (
unsigned long) entry + m_mmap_tag->
entry_size)) {
698 if(entry->type != MULTIBOOT_MEMORY_AVAILABLE || entry->len > limit)
701 size_t space = entry->len;
705 if(entry->addr < limit) {
706 offset = limit - entry->addr;
711 ASSERT(space >= (m_bitmap_size / 8 + 1),
"Not enough space for the bitmap (too big)\n");
719 ASSERT(m_bit_map !=
nullptr,
"No space for the bitmap (no region)\n");
722 for(
uint32_t i = 0; i < m_total_entries; ++i)
726 << (
uint64_t) (m_bit_map + m_bitmap_size / 8) <<
" (range of 0x" << (
uint64_t) m_bitmap_size / 8
738 return m_pml4_root_address;
748 return m_memory_size;
792 ASSERT(address < m_memory_size,
"Attempt to reserve address bigger then the memory can contain: 0x%x\n", address);
793 ASSERT(address + size < m_memory_size,
"Attempt to reserve region bigger then the memory can contain: 0x%x-0x%x\n",
794 address, address + size);
819 <<
" (length of 0x" << size <<
")\n";
855 *entry = create_page_table_entry(physical_address_of_entry(entry), flags);
858 asm volatile(
"invlpg (%0)"::
"r" (virtual_address) :
"memory");
890 return (
void*) physical_address;
906 return (
void*) virtual_address;
921 return (
void*) physical_address;
936 return (
void*) physical_address;
951 return (
void*) virtual_address;
963 return virtual_address & (1l << 62);
static Logger DEBUG()
Gets active logger set to DEBUG level.
static Logger INFO()
Gets active logger set to info level.
Stores the left, top, width and height of a rectangle.
void lock()
Lock the spinlock once it is available.
void unlock()
Unlock the spinlock.
static void * to_dm_region(uintptr_t physical_address)
Converts a physical address to a direct map region address if it is in the lower region using the hig...
static void unmap_lower_kernel()
Unmaps the kernel physical memory from the lower half that was set up during the kernel boot.
void unmap_area(virtual_address_t *virtual_address_start, size_t length)
Unmaps an area of virtual memory.
static size_t align_direct_to_page(size_t size)
Aligns a address to the page size.
static void * to_io_region(uintptr_t physical_address)
Converts a physical address to an IO region address if it is in the lower region using the higher hal...
void reserve(uint64_t address)
Reserves a physical address.
void map_area(virtual_address_t *virtual_address_start, size_t length, size_t flags)
Allocates a new area physical memory to a area virtual address.
void unmap(virtual_address_t *virtual_address)
Unmaps a virtual address using the kernel's pml4 table.
static size_t align_to_page(size_t size)
Aligns a size to the page size.
PhysicalMemoryManager(system::Multiboot *multiboot)
Constructs a PhysicalMemoryManager. Unmaps the lower kernel and sets up the bitmap for physical memor...
uint64_t memory_size() const
Gets total the memory size available for use (allocated or not)
static void clean_page_table(uint64_t *table)
Cleans a page table (fills it with 0 or null entries)
virtual_address_t * map(virtual_address_t *virtual_address, size_t flags)
Allocates a physical address to a virtual address.
uint64_t * pml4_root_address()
Gets the pml4 root address for the kernel.
static void * to_lower_region(uintptr_t virtual_address)
Converts a virtual address to a lower region address if it is in the higher region using the higher h...
static void * to_higher_region(uintptr_t physical_address)
Converts a physical address to a higher region address if it is in the lower region using the higher ...
bool is_mapped(uintptr_t physical_address, uintptr_t virtual_address, uint64_t *pml4_root)
Checks if a physical address is mapped to a virtual address.
static void * from_dm_region(uintptr_t physical_address)
Converts a direct map region address to a physical address if it is in the higher region using the hi...
void change_page_flags(virtual_address_t *virtual_address, size_t flags, uint64_t *pml4_root)
Changes the flags of a page.
static bool check_aligned(size_t size)
Checks if an address is aligned.
static PhysicalMemoryManager * s_current_manager
The current physical memory manager in use.
static size_t align_up_to_page(size_t size, size_t s_page_size)
Aligns a size up to the page size.
void identity_map(physical_address_t *physical_address, size_t flags)
Maps a physical address to its virtual address counter-part.
static size_t size_to_frames(size_t size)
Converts a size to the number of frames.
void free_area(uint64_t start_address, size_t size)
Frees an area of physical memory.
void reserve_kernel_regions(system::Multiboot *multiboot)
Reserves the kernel, multiboot modules, mmap regions and higher half mapping regions in the physical ...
static bool in_higher_region(uintptr_t virtual_address)
Checks if a virtual address is in the higher region.
uint64_t memory_used() const
Gets the memory currently used.
void free_frame(void *address)
Frees a frame in the bit map.
physical_address_t * get_physical_address(virtual_address_t *virtual_address, uint64_t *pml4_root)
Gets the physical address from a virtual address (if it exists)
void * allocate_area(uint64_t start_address, size_t size)
Allocate an area of physical memory (ie reserve it)
void * allocate_frame()
Allocates a physical page of memory, if the PMM is not initalise it will use the anon memory instead ...
static bool check_nx()
Checks if the No Execute page flag is supported.
Parses and provides access to Multiboot 2 information.
multiboot_tag_mmap * mmap()
Get the module tag.
Defines Central Processing Unit (CPU) structures and functions for managing CPU state and features.
Defines a Logger class for logging messages with different severity levels to multiple output streams...
#define ASSERT(condition, format,...)
If the specified condition is not met then the kernel will crash with the specified message.
volatile PAGE_ALIGNED unsigned char p3_table_hh[]
The PDPT table for higher half direct map setup in loader.s.
volatile PAGE_ALIGNED unsigned char p3_table[]
The PDPT table setup in loader.s.
volatile PAGE_ALIGNED unsigned char p4_table[]
The PML4 table setup in loader.s.
volatile PAGE_ALIGNED unsigned char p2_table[]
The PD table setup in loader.s.
volatile PAGE_ALIGNED unsigned char p1_table[]
The PT table setup in loader.s.
uint64_t _kernel_physical_end
The physical address where the kernel ends.
uint64_t _kernel_end
The virtual address of where the kernel ends.
uint64_t _kernel_size
The length of the memory region used by the kernel code.
Defines a PhysicalMemoryManager class for managing physical memory allocation and deallocation of pag...
constexpr uint64_t HIGHER_HALF_OFFSET
Where higher half memory usable space starts.
struct PACKED MaxOS::memory::PageMapLevel pml_t
Alias for PageMapLevel struct.
void virtual_address_t
A type representing a virtual address (used for readability)
constexpr uint64_t HIGHER_HALF_DIRECT_MAP
Where the map of physical memory to higher half starts.
#define PML4_GET_INDEX(ADDR)
Get the PML4 index from a virtual address.
struct PACKED MaxOS::memory::PageTableEntry pte_t
Alias for PageTableEntry struct.
#define PML3_GET_INDEX(ADDR)
Get the PDPT index from a virtual address.
#define PML1_GET_INDEX(ADDR)
Get the PT index from a virtual address.
constexpr uint64_t HIGHER_HALF_KERNEL_OFFSET
Where the kernel is mapped in higher half memory.
constexpr uint64_t HIGHER_HALF_MEM_OFFSET
Where higher half memory starts.
void physical_address_t
A type representing a physical address (used for readability)
constexpr uint8_t ROW_BITS
The number of bits in the bitmap row.
constexpr uint64_t PAGE_SIZE
The size of a page (4KB)
@ PRESENT
The page is present in memory.
@ WRITE
Memory in this page is writable.
@ GLOBAL
The page can be shared between processes.
@ DIRTY
This page has been written to.
@ USER
This page is accessible from user mode.
@ CACHE_DISABLED
Dont let the CPU cache this page.
@ NO_EXECUTE
Dont let the CPU execute code on this page.
#define PML2_GET_INDEX(ADDR)
Get the PDP index from a virtual address.
An entry in the memory map containing information about a region of memory.
multiboot_uint64_t addr
The starting address of the memory region.
multiboot_uint64_t len
The length of the memory region.
multiboot_uint32_t size
*copydoc multiboot_tag::size
struct multiboot_mmap_entry entries[0]
The memory map entries.
multiboot_uint32_t entry_size
The size of each entry.
Common header for all multiboot info tags.