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[];
33m_multiboot(multiboot),
34m_pml4_root_address((uint64_t*)
p4_table),
37 Logger::INFO() <<
"Setting up Physical Memory Manager\n";
50 m_mmap_tag = m_multiboot->
mmap();
54 if(entry->type != MULTIBOOT_MEMORY_AVAILABLE)
62 m_memory_size = (m_mmap->
addr + m_mmap->
len);
63 m_bitmap_size = m_memory_size /
PAGE_SIZE + 1;
64 m_total_entries = m_bitmap_size /
ROW_BITS + 1;
65 Logger::DEBUG() <<
"Memory Info: size = " << (int) (m_memory_size / 1024 / 1024) <<
"mb, bitmap size = 0x" << (uint64_t) m_bitmap_size <<
", total entries = " << (
int) m_total_entries <<
", page size = 0x"<< (uint64_t)
PAGE_SIZE <<
"\n";
69 for(uint64_t physical_address = 0; physical_address < (m_mmap->
addr + m_mmap->
len); physical_address +=
PAGE_SIZE)
81PhysicalMemoryManager::~PhysicalMemoryManager() =
default;
98 uint32_t kernel_entries = (m_kernel_start_page /
PAGE_SIZE) + 1;
99 if((((uint32_t) (m_kernel_start_page)) %
PAGE_SIZE) != 0)
106 uint64_t mem_end = m_mmap->
addr + m_mmap->
len;
110 if(entry->type <= MULTIBOOT_MEMORY_AVAILABLE)
114 if(entry->addr >= mem_end)
117 reserve(entry->addr, entry->len,
"MMap");
123 if(tag->type != MULTIBOOT_TAG_TYPE_MODULE)
127 auto*
module = (struct multiboot_tag_module*) tag;
128 reserve(module->mod_start, module->mod_end - module->mod_start,
"Module");
134 size_t tags_size = multiboot->
end_address - start_tag + ((end_tag->size + 7) & ~7);
135 reserve(start_tag, tags_size,
"Tags");
169 return (size + page_size - 1) & ~(page_size - 1);
198 void* address = (
void*) ((uintptr_t) m_mmap->
addr + (m_setup_frames *
PAGE_SIZE));
206 ASSERT(m_used_frames < m_bitmap_size,
"No more frames available\n");
208 for(uint32_t row = 0; row < m_total_entries; ++row) {
211 if(m_bit_map[row] == 0xFFFFFFFFFFFFFFFF)
214 for(uint32_t column = 0; column <
ROW_BITS; ++column) {
217 if(m_bit_map[row] & (1ULL << column))
221 m_bit_map[row] |= (1ULL << column);
228 uint64_t frame_address = (row *
ROW_BITS) + column;
235 return (
void*)frame_address;
240 ASSERT(
false,
"Frame not found\n");
256 uint64_t frame_address = (uint64_t) address /
PAGE_SIZE;
257 m_bit_map[frame_address /
ROW_BITS] &= ~(1ULL << (frame_address %
ROW_BITS));
276 uint32_t start_row = 0;
277 uint32_t start_column = 0;
278 size_t adjacent_frames = 0;
280 for(uint32_t row = 0; row < m_total_entries; ++row) {
283 if(m_bit_map[row] == 0xFFFFFFFFFFFFFFFF) {
288 for(uint32_t column = 0; column <
ROW_BITS; ++column) {
291 if(m_bit_map[row] & (1ULL << column)) {
297 if(adjacent_frames == 0) {
299 start_column = column;
304 if(adjacent_frames != frame_count)
308 m_used_frames += frame_count;
309 for(uint32_t i = 0; i < frame_count; ++i) {
312 uint32_t index = start_row + (start_column + i) /
ROW_BITS;
313 uint32_t bit = (start_column + i) %
ROW_BITS;
316 ASSERT(index < m_total_entries && bit <
ROW_BITS,
"Index out of bounds\n");
319 m_bit_map[index] |= (1ULL << bit);
324 return (
void*) (start_address + (start_row *
ROW_BITS + start_column) *
PAGE_SIZE);
331 ASSERT(
false,
"Cannot allocate that much memory\n");
345 uint64_t frame_address = start_address /
PAGE_SIZE;
348 if(frame_address >= m_bitmap_size)
355 m_used_frames -= frame_count;
356 for(uint32_t i = 0; i < frame_count; ++i)
357 m_bit_map[(frame_address + i) /
ROW_BITS] &= ~(1ULL << ((frame_address + i) %
ROW_BITS));
370pml_t* PhysicalMemoryManager::get_higher_half_table(uint64_t index, uint64_t index2, uint64_t index3) {
372 return (
pml_t*) (0xFFFF000000000000 | ((510UL << 39) | (index3 << 30) | (index2 << 21) | (index << 12)));
384pml_t* PhysicalMemoryManager::get_or_create_table(
pml_t* table,
size_t index,
size_t flags) {
387 if(table->entries[index].present)
392 table->entries[index] = create_page_table_entry((uint64_t) new_table, flags);
395 new_table = (uint64_t*)
to_dm_region((uintptr_t) new_table);
400 return (
pml_t*) new_table;
412pml_t* PhysicalMemoryManager::get_and_create_table(
pml_t* parent_table, uint64_t table_index,
pml_t* table) {
417 if(parent_table->entries[table_index].present)
422 parent_table->entries[table_index] = create_page_table_entry((uint64_t) new_table, PRESENT | WRITE);
452 pml_t* pdpr_table =
nullptr;
453 pml_t* pd_table =
nullptr;
454 pml_t* pt_table =
nullptr;
458 pdpr_table = get_and_create_table(pml4_table, pml4_index, get_higher_half_table(pml4_index));
459 pd_table = get_and_create_table(pdpr_table, pdpr_index, get_higher_half_table(pdpr_index, pml4_index));
460 pt_table = get_and_create_table(pd_table, pd_index, get_higher_half_table(pd_index, pdpr_index, pml4_index));
463 pdpr_table = get_or_create_table(pml4_table, pml4_index, flags);
464 pd_table = get_or_create_table(pdpr_table, pdpr_index, flags);
465 pt_table = get_or_create_table(pd_table, pd_index, flags);
469 return &pt_table->entries[pt_index];
479uint64_t PhysicalMemoryManager::physical_address_of_entry(
pte_t* entry) {
481 return entry->physical_address << 12;
496 return map(physical_address, virtual_address, flags, m_pml4_root_address);
515 pte_t* pte = get_entry(virtual_address, (
pml_t*) pml4_table);
517 return virtual_address;
520 *pte = create_page_table_entry((uint64_t) physical_address, flags);
523 asm volatile(
"invlpg (%0)"::
"r" (virtual_address) :
"memory");
525 return virtual_address;
582 map(physical_address, physical_address, flags);
593 unmap(virtual_address, m_pml4_root_address);
605 pte_t* pte = get_entry(virtual_address, (
pml_t*) pml4_root);
612 pte->present =
false;
615 asm volatile(
"invlpg (%0)"::
"r" (virtual_address) :
"memory");
639 for(
int i = 0; i < 512; i++)
652pte_t PhysicalMemoryManager::create_page_table_entry(uintptr_t address,
size_t flags)
const {
655 .present = (flags &
PRESENT) != 0,
656 .write = (flags & WRITE) != 0,
657 .user = (flags &
USER) != 0,
658 .write_through = (flags & WRITE_THROUGH) != 0,
660 .accessed = (flags & ACCESSED) != 0,
661 .dirty = (flags &
DIRTY) != 0,
662 .huge_page = (flags & HUGE_PAGE) != 0,
663 .global = (flags &
GLOBAL) != 0,
665 .physical_address = address >> 12,
669 if(m_nx_allowed && (flags & NO_EXECUTE)) {
670 auto page_raw = (uint64_t) &page;
672 page = *(
pte_t*) page_raw;
683void PhysicalMemoryManager::initialise_bit_map() {
686 uint64_t limit = m_kernel_start_page;
690 (multiboot_uint8_t*) m_mmap_tag +
692 (
unsigned long) entry + m_mmap_tag->
entry_size)) {
695 if(entry->type != MULTIBOOT_MEMORY_AVAILABLE || entry->len > limit)
698 size_t space = entry->len;
702 if(entry->addr < limit) {
703 offset = limit - entry->addr;
708 ASSERT(space >= (m_bitmap_size / 8 + 1),
"Not enough space for the bitmap (too big)\n");
711 m_bit_map = (uint64_t*)
to_dm_region(entry->addr + offset);
716 ASSERT(m_bit_map !=
nullptr,
"No space for the bitmap (no region)\n");
719 for(uint32_t i = 0; i < m_total_entries; ++i)
722 Logger::DEBUG() <<
"Bitmap: location: 0x" << (uint64_t) m_bit_map <<
" - 0x"
723 << (uint64_t) (m_bit_map + m_bitmap_size / 8) <<
" (range of 0x" << (uint64_t) m_bitmap_size / 8
735 return m_pml4_root_address;
745 return m_memory_size;
789 ASSERT(address < m_memory_size,
"Attempt to reserve address bigger then the memory can contain: 0x%x\n", address);
790 ASSERT(address + size < m_memory_size,
"Attempt to reserve region bigger then the memory can contain: 0x%x-0x%x\n",
791 address, address + size);
798 if(aligned_address < address)
799 size += address - aligned_address;
804 uint64_t frame_index = aligned_address /
PAGE_SIZE;
807 for(
size_t i = 0; i < page_count; ++i)
808 m_bit_map[(frame_index + i) /
ROW_BITS] |= (1ULL << ((frame_index + i) %
ROW_BITS));
811 m_used_frames += page_count;
815 Logger::DEBUG() <<
"Reserved Address for " << type <<
": 0x" << aligned_address <<
" - 0x" << aligned_address + size
816 <<
" (length of 0x" << size <<
")\n";
830 pte_t* entry = get_entry(virtual_address, (
pml_t*) pml4_root);
848 pte_t* entry = get_entry(virtual_address, (
pml_t*) pml4_root);
854 *entry = create_page_table_entry(physical_address_of_entry(entry), flags);
857 asm volatile(
"invlpg (%0)"::
"r" (virtual_address) :
"memory");
889 return (
void*) physical_address;
905 return (
void*) virtual_address;
920 return (
void*) physical_address;
935 return (
void*) physical_address;
950 return (
void*) virtual_address;
962 return virtual_address & (1l << 62);
static LogRecord DEBUG()
Gets the active logger for debug logs.
static LogRecord INFO()
Gets the active logger for info logs.
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. (Rounds up to the next multiple of 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, given a page size to a power of 2.
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.
unsigned long end_address
The end address of the multiboot info struct.
multiboot_tag * start_tag() const
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...
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.