Max OS 0.3
Loading...
Searching...
No Matches
virtual.cpp
Go to the documentation of this file.
1
11#include <memory/virtual.h>
12#include <common/logger.h>
13#include <processes/scheduler.h>
14
15using namespace MaxOS::memory;
16using namespace MaxOS::common;
17using namespace MaxOS::processes;
18using namespace MaxOS::processes::resources;
19
26
27 // Set the kernel flag
28 bool is_kernel = MemoryManager::s_kernel_memory_manager == nullptr;
29 if (!is_kernel) {
30
31 // Get a new pml4 table
32 m_pml4_root_physical_address = (uint64_t*)PhysicalMemoryManager::s_current_manager->allocate_frame();
33 m_pml4_root_address = (uint64_t*)PhysicalMemoryManager::to_dm_region((uint64_t)m_pml4_root_physical_address);
34
35 // Clear the table
36 PhysicalMemoryManager::clean_page_table(m_pml4_root_address);
37
38 // Map the higher half of the kernel (p4 256 - 511)
39 for (size_t i = 256; i < 512; i++) {
40
41 // Recursive Map the pml4 table (so that we can access the new pml4 table later on)
42 if (i == 510) {
43 m_pml4_root_address[i] = (uint64_t)m_pml4_root_physical_address | PRESENT | WRITE;
44 continue;
45 }
46
47 // Set the new pml4 table to the old (kernel) pml4 table
49
50 }
51
52 // Load the userspace pml4
53 asm volatile("mov %0, %%cr3"::"r"((uint64_t)m_pml4_root_physical_address) : "memory");
54
55 } else {
57 m_pml4_root_physical_address = (uint64_t*)PhysicalMemoryManager::to_lower_region((uint64_t)m_pml4_root_address);
58 }
59
60 // Set up the first region
61 new_region();
62 m_first_region = m_current_region;
63
64 // Calculate the next available address (kernel needs to reserve space for the higher half)
66}
67
72
73 // Free all the frames used by the VMM
74 virtual_memory_region_t* region = m_first_region;
75
76 // Loop through the regions
77 while (region != nullptr) {
78
79 // Loop through the chunks
80 for (size_t i = 0; i < CHUNKS_PER_PAGE; i++) {
81
82 // Have reached the end?
83 if (i == m_current_chunk && region == m_current_region)
84 break;
85
86 // Cant free shared frames that may be in use by another VMM
87 if (region->chunks[i].flags & SHARED)
88 continue;
89
90 // Free each page in the chunk
91 size_t pages = PhysicalMemoryManager::size_to_frames(region->chunks[i].size);
92 for (size_t j = 0; j < pages; j++) {
93
94 // Convert the virtual address to a physical address and free it
95 physical_address_t* frame = PhysicalMemoryManager::s_current_manager->get_physical_address((virtual_address_t*)(region->chunks[i].start_address + j * PAGE_SIZE), m_pml4_root_address);
96 if (frame != nullptr)
98
99 }
100 }
101
102 region = region->next;
103 }
104}
105
113void* VirtualMemoryManager::allocate(size_t requested_size, size_t flags) {
114
115 size_t actual_size;
116 return allocate(0, requested_size, actual_size, flags);
117}
118
127void* VirtualMemoryManager::allocate(size_t requested_size, size_t& actual_size, size_t flags) {
128
129 return allocate(0, requested_size, actual_size, flags);
130}
131
132
142void* VirtualMemoryManager::allocate(uint64_t address, size_t requested_size, size_t& actual_size, size_t flags) {
143
144 // Make sure allocating something
145 if (requested_size == 0)
146 return nullptr;
147
148 // If specific address is given
149 if (address != 0) {
150
151 // Make sure isn't already allocated
152 if (address < m_next_available_address)
153 return nullptr;
154
155 // Make sure its aligned
157 return nullptr;
158
159 }
160
161 // Make sure the size is aligned
162 actual_size = PhysicalMemoryManager::align_up_to_page(requested_size, PAGE_SIZE);
163
164 // Check the free list for a chunk (if not asking for a specific address)
165 free_chunk_t* reusable_chunk = address == 0 ? find_and_remove_free_chunk(actual_size) : nullptr;
166 if (reusable_chunk != nullptr) {
167
168 // If the chunk is being reserved then the old memory needs to be unmapped (as the owner of this reserved region will perform the mapping as required)
169 uintptr_t chunk_start = reusable_chunk->start_address;
170 if (flags & RESERVE) {
171
172 // Unmap the memory
173 size_t pages = PhysicalMemoryManager::size_to_frames(actual_size);
174 for (size_t i = 0; i < pages; i++) {
175
176 // Free the frame
178 if (frame != nullptr)
180
181 // Unmap the page
182 auto page = (virtual_address_t*)(chunk_start + (i * PAGE_SIZE));
183 if (page != nullptr)
184 PhysicalMemoryManager::s_current_manager->unmap(page, m_pml4_root_address);
185
186 }
187 }
188
189 // Ensure there is enough space if no reusable chunk is found
190 if (m_current_chunk >= CHUNKS_PER_PAGE)
191 new_region();
192
193 // Allocate the memory
194 virtual_memory_chunk_t* chunk = &m_current_region->chunks[m_current_chunk++];
195 chunk->size = actual_size;
196 chunk->flags = flags;
197 chunk->start_address = chunk_start;
198
199 // Return the address
200 return (void*)chunk_start;
201 }
202
203 // Is there space in the current region
204 if (m_current_chunk >= CHUNKS_PER_PAGE)
205 new_region();
206
207 // If needed to allocate at a specific address, fill with free memory up to that address to prevent fragmentation
208 if (address != 0)
209 fill_up_to_address(address, flags, false);
210
211 // fill_up_to_address() may have filled the last slot.
212 if (m_current_chunk >= CHUNKS_PER_PAGE)
213 new_region();
214
215 // Allocate the memory
216 virtual_memory_chunk_t* chunk = &m_current_region->chunks[m_current_chunk];
217 chunk->size = actual_size;
218 chunk->flags = flags;
219 chunk->start_address = m_next_available_address;
220
221 // Update the next available address
222 m_next_available_address += actual_size;
223 m_current_chunk++;
224
225 // If just reserving the space don't map it
226 if (flags & RESERVE)
227 return (void*)chunk->start_address;
228
229 // Map the memory
230 size_t pages = PhysicalMemoryManager::size_to_frames(actual_size);
231 for (size_t i = 0; i < pages; i++) {
232
233 // Allocate a new frame
235 ASSERT(frame != nullptr, "Failed to allocate frame (from current region)\n");
236
237 // Map the frame
239
240 }
241
242 // Return the address
243 return (void*)chunk->start_address;
244}
245
249void VirtualMemoryManager::new_region() {
250
251 // Space for the new region
253 ASSERT(new_region_physical != nullptr, "Failed to allocate new VMM region\n");
254
255 // Align the new region
256 auto* new_region = (virtual_memory_region_t*)PhysicalMemoryManager::align_to_page((uint64_t)m_current_region + PAGE_SIZE);
257
258 // Map the new region
259 PhysicalMemoryManager::s_current_manager->map(new_region_physical, (virtual_address_t*)new_region, PRESENT | WRITE, m_pml4_root_address);
260 new_region->next = nullptr;
261
262 // Clear the new region
263 for (size_t i = 0; i < CHUNKS_PER_PAGE; i++) {
264 new_region->chunks[i].size = 0;
265 new_region->chunks[i].flags = 0;
266 new_region->chunks[i].start_address = 0;
267 }
268
269 // Set the current region
270 if (m_current_region)
271 m_current_region->next = new_region;
272 m_current_chunk = 0;
273 m_current_region = new_region;
274}
275
282void VirtualMemoryManager::free(void* address, bool reusable_pages) {
283
284 // Make sure freeing something
285 if (address == nullptr)
286 return;
287
288 // Find the chunk
289 virtual_memory_region_t* region = m_first_region;
290 virtual_memory_chunk_t* chunk = nullptr;
291 while (region != nullptr) {
292
293 // Loop through the chunks
294 for (size_t i = 0; i < CHUNKS_PER_PAGE; i++) {
295
296 // Check if the address is in the chunk
297 if (region->chunks[i].start_address == (uintptr_t)address) {
298 chunk = &region->chunks[i];
299 break;
300 }
301 }
302
303 // If the chunk was found
304 if (chunk != nullptr)
305 break;
306
307 // Move to the next region
308 region = region->next;
309 }
310
311 // Make sure the chunk was found
312 if (chunk == nullptr)
313 return;
314
315 // No longer using a shared memory chunk
316 if (chunk->flags & VirtualFlags::SHARED) {
317
318 // Find the resource
319 for (const auto& resource : GlobalScheduler::current_process()->resource_manager.resources()) {
320 if (resource.second->type() != resource_type_t::SHARED_MEMORY)
321 continue;
322
323 // Skip shared memory that points elsewhere TODO: this is vaddr so get paddr and comapre?
324 auto shared = (SharedMemory*)resource.second;
325 if ((void*)shared->physical_address() != address)
326 continue;
327
328 // Close the resource
330 }
331 }
332
333 // Can only re use pages we own
334 if (reusable_pages && !(chunk->flags & (SHARED | RESERVE)))
335 add_free_chunk(chunk->start_address, chunk->size);
336
337 // Clear the chunk
338 chunk->size = 0;
339 chunk->flags = 0;
340 chunk->start_address = 0;
341}
342
349
350 // Loop through all the regions and add up the size of the allocated chunks
351 size_t result = 0;
352
353 // Iterate through the regions
354 virtual_memory_region_t* region = m_first_region;
355 while (region != nullptr) {
356
357 // Loop through the chunks
358 for (size_t i = 0; i < CHUNKS_PER_PAGE; i++) {
359
360 // Check if the address is in the chunk
361 if (region->chunks[i].size != 0)
362 result += region->chunks[i].size;
363 }
364
365 // Move to the next region
366 region = region->next;
367 }
368
369 return result;
370}
371
378void VirtualMemoryManager::add_free_chunk(uintptr_t start_address, size_t size) {
379
380 // Create the new chunk
381 auto* new_chunk = (free_chunk_t*)start_address;
382 new_chunk->start_address = start_address;
383 new_chunk->size = size;
384 new_chunk->next = m_free_chunks;
385
386 // Set the new chunk
387 m_free_chunks = new_chunk;
388}
389
398free_chunk_t* VirtualMemoryManager::find_and_remove_free_chunk(size_t size) {
399
400 // Find the chunk
401 free_chunk_t* current = m_free_chunks;
402 free_chunk_t* previous = nullptr;
403 while (current != nullptr) {
404
405 // Check if the chunk is big enough
406 if (current->size >= size) {
407
408 // Remove the chunk
409 if (previous != nullptr) {
410 previous->next = current->next;
411 } else {
412 m_free_chunks = current->next;
413 }
414
415 // Return the chunk
416 return current;
417
418 }
419
420 // Move to the next chunk
421 previous = current;
422 current = current->next;
423 }
424
425 // No chunk found
426 return nullptr;
427
428}
429
441virtual_memory_chunk_t* VirtualMemoryManager::find_containing_chunk(const void* start_address, size_t size) {
442
443 // Search all the regions for the virtual chunk containing the range
444 virtual_memory_region_t* region = m_first_region;
445 while (region != nullptr) {
446 for (size_t i = 0; i < CHUNKS_PER_PAGE; i++) {
447
448 // Get the chunk info
449 auto chunk = region->chunks[i];
450 auto chunk_start = (void*)chunk.start_address;
451 auto chunk_end = (void*)(chunk.start_address + chunk.size);
452
453 // Does the region start in this chunk?
454 if (!(start_address >= chunk_start && start_address <= chunk_end))
455 continue;
456
457 // Can this chunk contain the whole region?
458 if (!(start_address + size <= chunk_end))
459 continue;
460
461 return &region->chunks[i];
462
463 }
464 region = region->next;
465 }
466
467 // Not found
468 return nullptr;
469}
470
477
478 return m_pml4_root_physical_address;
479}
480
488
489 // Get the shared memory block
491
492 // Load the shared memory
493 if (block != nullptr)
494 return load_shared_memory(block->physical_address(), block->size());
495
496 return nullptr;
497}
498
506void* VirtualMemoryManager::load_shared_memory(uintptr_t physical_address, size_t size) {
507
508 // Make sure there is somthing to map
509 if (size == 0 || physical_address == 0)
510 return nullptr;
511
512 // Load it into physical memory
513 return load_physical_into_address_space(physical_address, size, SHARED);
514}
515
524void* VirtualMemoryManager::load_physical_into_address_space(uintptr_t physical_address, size_t size, size_t flags) {
525
526 // Reserve some space
527 void* address = allocate(size, flags | RESERVE);
528
529 // Map the shared memory
530 size_t pages = PhysicalMemoryManager::size_to_frames(size);
531 for (size_t i = 0; i < pages; i++)
532 PhysicalMemoryManager::s_current_manager->map((physical_address_t*)(physical_address + (i * PAGE_SIZE)), (virtual_address_t*)((uintptr_t)address + (i * PAGE_SIZE)), PRESENT | WRITE, m_pml4_root_address);
533
534
535 // All done
536 return address;
537}
538
552void* VirtualMemoryManager::load_range_from_process(VirtualMemoryManager* other_vmm, const void* start_address, size_t size) {
553
554 // Check bounds
555 if (!other_vmm || !start_address || size == 0)
556 return nullptr;
557
558 // Convert given region to be page orientated
559 size_t aligned_start = PhysicalMemoryManager::align_direct_to_page((size_t)start_address);
560 size_t offset = (size_t)start_address - aligned_start;
561 size_t total_size = offset + size;
562 size_t paged_size = PhysicalMemoryManager::align_up_to_page(total_size, PAGE_SIZE);
563
564 // Reserve space to receive the copy
565 auto dest = allocate(paged_size, RESERVE);
566 if (!dest)
567 return nullptr;
568
569 // Find the pages that contains the chunk
570 size_t range_pages = paged_size / PAGE_SIZE;
571
572 // Map each page in the range
573 for (size_t i = 0; i < range_pages; i++) {
574 uintptr_t virtual_page = aligned_start + i * PAGE_SIZE;
575
576 // Haven't reached the start of the region
577 if (virtual_page + PAGE_SIZE <= aligned_start)
578 continue;
579
580 // Dont copy pages past the region
581 if (virtual_page >= aligned_start + paged_size)
582 break;
583
584 // Map the page into this vmm
586 if (frame == nullptr)
587 continue;
588
589 PhysicalMemoryManager::s_current_manager->map(frame, (virtual_address_t*)(dest + virtual_page - aligned_start), PRESENT | WRITE, m_pml4_root_address);
590 }
591
592 // Return where the region actually starts, not the chunk
593 return dest + offset;
594}
595
602void VirtualMemoryManager::unload_range_from_process(const void* start_address, size_t size) {
603
604 // Check bounds
605 if (!start_address || size == 0)
606 return;
607
608 // Convert given region to be page orientated
609 size_t aligned_start = PhysicalMemoryManager::align_direct_to_page((size_t)start_address);
610 size_t offset = (size_t)start_address - aligned_start;
611 size_t total_size = offset + size;
612 size_t paged_size = PhysicalMemoryManager::align_up_to_page(total_size, PAGE_SIZE);
613
614 // Find the chunk that contains the region
615 auto containing_chunk = find_containing_chunk(start_address, size);
616 size_t chunk_pages = PhysicalMemoryManager::size_to_frames(containing_chunk->size);
617
618 // Unmap each page in the range
619 for (size_t i = 0; i < chunk_pages; i++) {
620 uintptr_t virtual_page = containing_chunk->start_address + i * PAGE_SIZE;
621
622 // Haven't reached the start of the region
623 if (virtual_page + PAGE_SIZE <= aligned_start)
624 continue;
625
626 // Dont unmap pages past the region
627 if (virtual_page >= aligned_start + paged_size)
628 break;
629
630 // Free the page
631 PhysicalMemoryManager::s_current_manager->unmap((virtual_address_t*)virtual_page, m_pml4_root_address);
632 }
633
634 // Free the whole region
635 free((void*)aligned_start, false);
636}
637
647void VirtualMemoryManager::fill_up_to_address(uintptr_t address, size_t flags, bool mark_used) {
648
649 // Make sure the address is aligned
650 address = PhysicalMemoryManager::align_to_page(address);
651
652 // Make sure the address is not before the next available address
653 ASSERT(address >= m_next_available_address, "FAILED TO FILL: Address is before the next available address - 0x%x < 0x%x\n", address, m_next_available_address);
654
655 // Calculate the size
656 size_t size = address - m_next_available_address;
657
658 // Allocate the memory
659 virtual_memory_chunk_t* chunk = &m_current_region->chunks[m_current_chunk];
660 chunk->size = size;
661 chunk->flags = flags;
662 chunk->start_address = m_next_available_address;
663
664 // Update the next available address
665 m_next_available_address += size;
666 m_current_chunk++;
667
668 // Map the memory
669 size_t pages = PhysicalMemoryManager::size_to_frames(size);
670 for (size_t i = 0; i < pages; i++) {
671
672 // Allocate a new frame
674 ASSERT(frame != nullptr, "Failed to allocate frame (from fill up)\n");
675
676 // Map the frame
677 PhysicalMemoryManager::s_current_manager->map(frame, (virtual_address_t*)(chunk->start_address + i * PAGE_SIZE), PRESENT | WRITE, m_pml4_root_address);
678
679 }
680
681 // Mark as free if needed
682 if (!mark_used)
683 free((void*)chunk->start_address);
684}
685
692
693 // Make sure the address is valid
694 if (m_pml4_root_address == nullptr)
695 return nullptr;
696
697 // Return the address
698 return m_pml4_root_address;
699}
static MemoryManager * s_kernel_memory_manager
The memory manager for any kernel processes and all kernel allocations.
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...
Definition physical.cpp:929
static size_t align_direct_to_page(size_t size)
Aligns a address to the page size.
Definition physical.cpp:764
void unmap(virtual_address_t *virtual_address)
Unmaps a virtual address using the kernel's pml4 table.
Definition physical.cpp:590
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)
Definition physical.cpp:155
uint64_t memory_size() const
Gets total the memory size available for use (allocated or not)
Definition physical.cpp:743
static void clean_page_table(uint64_t *table)
Cleans a page table (fills it with 0 or null entries)
Definition physical.cpp:636
virtual_address_t * map(virtual_address_t *virtual_address, size_t flags)
Allocates a physical address to a virtual address.
Definition physical.cpp:535
uint64_t * pml4_root_address()
Gets the pml4 root address for the kernel.
Definition physical.cpp:733
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...
Definition physical.cpp:898
static bool check_aligned(size_t size)
Checks if an address is aligned.
Definition physical.cpp:178
static PhysicalMemoryManager * s_current_manager
The current physical memory manager in use.
Definition physical.h:183
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.
Definition physical.cpp:167
static size_t size_to_frames(size_t size)
Converts a size to the number of frames.
Definition physical.cpp:144
void free_frame(void *address)
Frees a frame in the bit map.
Definition physical.cpp:250
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)
Definition physical.cpp:828
void * allocate_frame()
Allocates a physical page of memory, if the PMM is not initalise it will use the anon memory instead ...
Definition physical.cpp:188
Manages the virtual memory of the system and provides functions to allocate and free memory in the vi...
Definition virtual.h:104
VirtualMemoryManager()
Construct a new Virtual Memory Manager object and set up the initial page tables (kernel mapped into ...
Definition virtual.cpp:25
void * load_physical_into_address_space(uintptr_t physical_address, size_t size, size_t flags)
Load physical memory into the VMM's address space.
Definition virtual.cpp:524
void free(void *address, bool reusable_pages=true)
Free a chunk of virtual memory.
Definition virtual.cpp:282
void * allocate(size_t requested_size, size_t &actual_size, size_t flags)
Allocate a new chunk of virtual memory.
Definition virtual.cpp:127
void * allocate(size_t size, size_t flags)
Allocate a new chunk of virtual memory.
Definition virtual.cpp:113
void unload_range_from_process(const void *start_address, size_t size)
Unload a shared range of pages from this vmm.
Definition virtual.cpp:602
size_t memory_used()
Returns the amount of memory used.
Definition virtual.cpp:348
uint64_t * pml4_root_address_physical()
Get the physical address of the PML4 root.
Definition virtual.cpp:476
~VirtualMemoryManager()
Destroy the Virtual Memory Manager object and free all used pages.
Definition virtual.cpp:71
uint64_t * pml4_root_address()
Get the virtual address of the PML4 root.
Definition virtual.cpp:691
void * load_range_from_process(VirtualMemoryManager *other_vmm, const void *start_address, size_t size)
Loads the pages from a virtual memory region in another vmm into this one.
Definition virtual.cpp:552
void * load_shared_memory(const string &name)
Load shared memory into the VMM's address space.
Definition virtual.cpp:487
static Process * current_process()
Gets the process on the currently executing core.
resources::ResourceManager resource_manager
The manger for resources used by this process.
Definition process.h:122
A block memory that is mapped into multiple processes.
Definition ipc.h:45
Resource * get_resource(uint64_t handle)
Gets a resource based on the handle id.
Definition resource.cpp:282
void close_resource(uint64_t handle, size_t flags)
Un registers the resource and then closes it.
Definition resource.cpp:262
common::Map< uint64_t, Resource * > resources()
Get the resources currently open.
Definition resource.cpp:223
Defines a Logger class for logging messages with different severity levels to multiple output streams...
void virtual_address_t
A type representing a virtual address (used for readability)
Definition physical.h:30
constexpr uint64_t HIGHER_HALF_DIRECT_MAP
Where the map of physical memory to higher half starts.
Definition physical.h:99
void physical_address_t
A type representing a physical address (used for readability)
Definition physical.h:31
constexpr uint64_t PAGE_SIZE
The size of a page (4KB)
Definition physical.h:92
@ PRESENT
The page is present in memory.
Definition physical.h:44
@ WRITE
Memory in this page is writable.
Definition physical.h:45
Defines a GlobalScheduler and Scheduler for managing processes and threads.
A chunk of memory that has been freed and is available for allocation. A node in a linked list.
Definition virtual.h:71
size_t size
The size of the chunk's memory region in bytes.
Definition virtual.h:74
uintptr_t start_address
The first virtual address that this chunk covers.
Definition virtual.h:73
struct FreeChunk * next
Pointer to the next free chunk in the list (not sequential in memory)
Definition virtual.h:75
A container for a region of virtual memory that has been allocated by the Virtual Memory Manager.
Definition virtual.h:54
size_t flags
The flags for the memory (see VirtualFlags)
Definition virtual.h:58
uintptr_t start_address
The first virtual address that this chunk covers.
Definition virtual.h:56
size_t size
The size of the chunk's memory region in bytes.
Definition virtual.h:57
Defines a VirtualMemoryManager class for managing virtual memory allocation and mapping to physical m...
struct PACKED MaxOS::memory::VirtualMemoryRegion virtual_memory_region_t
Alias for VirtualMemoryRegion struct.
@ SHARED
The memory is shared between multiple processes.
Definition virtual.h:41
@ RESERVE
Reserve the memory but do not map any physical memory to it.
Definition virtual.h:40
constexpr size_t CHUNKS_PER_PAGE
Number of chunks per virtual memory region (limited to fit in one page)
Definition virtual.h:79
constexpr size_t VMM_RESERVED
The starting address for the VMM to start allocating from when in higher half (otherwise can cause co...
Definition virtual.h:80