PatchworkOS  19e446b
A non-POSIX operating system.
Loading...
Searching...
No Matches
pmm.c
Go to the documentation of this file.
2#include <kernel/mem/pmm.h>
3
4#include <kernel/config.h>
6#include <kernel/log/log.h>
7#include <kernel/log/panic.h>
8#include <kernel/sync/lock.h>
9
10#include <boot/boot_info.h>
11#include <stddef.h>
12#include <stdint.h>
13#include <sys/bitmap.h>
14
15#include <errno.h>
16#include <string.h>
17#include <sys/math.h>
18#include <sys/proc.h>
19
20static const char* efiMemTypeToString[] = {
21 "reserved",
22 "loader code",
23 "loader data",
24 "boot services code",
25 "boot services data",
26 "runtime services code",
27 "runtime services data",
28 "conventional",
29 "unusable",
30 "acpi reclaim",
31 "acpi memory nvs",
32 "io",
33 "io port space",
34 "pal code",
35 "persistent",
36};
37
38static page_t* pages = NULL;
39
41static size_t location = FREE_PAGE_MAX;
42
44
45static pfn_t highest = 0;
46static size_t total = 0;
47static size_t avail = 0;
48
50
51static bool pmm_is_mem_avail(EFI_MEMORY_TYPE type)
52{
53 if (!boot_is_mem_ram(type))
54 {
55 return false;
56 }
57
58 switch (type)
59 {
60 case EfiConventionalMemory:
61 case EfiLoaderCode:
62 // EfiLoaderData is intentionally not included here as it's freed later in `init()`.
63 case EfiBootServicesCode:
64 case EfiBootServicesData:
65 return true;
66 default:
67 return false;
68 }
69}
70
71static inline void pmm_stack_push(pfn_t pfn)
72{
73 if (stack == NULL || location == 0)
74 {
75 page_stack_t* page = PFN_TO_VIRT(pfn);
76 page->next = stack;
77 stack = page;
79
80 return;
81 }
82
83 stack->pages[--location] = pfn;
84}
85
86static inline pfn_t pmm_stack_pop(void)
87{
89 {
90 if (stack == NULL)
91 {
92 return ERR;
93 }
94
95 page_stack_t* page = stack;
96 stack = stack->next;
97 location = (stack == NULL) ? FREE_PAGE_MAX : 0;
98 return VIRT_TO_PFN(page);
99 }
100
101 return stack->pages[location++];
102}
103
104static inline pfn_t pmm_bitmap_set(size_t count, pfn_t maxPfn, pfn_t alignPfn)
105{
106 alignPfn = MAX(alignPfn, 1);
107 maxPfn = MIN(maxPfn, CONFIG_PMM_BITMAP_MAX_ADDR / PAGE_SIZE);
108
109 size_t index = bitmap_find_clear_region_and_set(&bitmap, 0, maxPfn, count, alignPfn);
110 if (index == bitmap.length)
111 {
112 return ERR;
113 }
114
115 return (pfn_t)index;
116}
117
118static inline void pmm_bitmap_clear(pfn_t pfn, size_t pageAmount)
119{
120 bitmap_clear_range(&bitmap, pfn, pfn + pageAmount);
121}
122
123static void pmm_free_unlocked(pfn_t pfn)
124{
125 page_t* page = &pages[pfn];
126 assert(page->ref > 0);
127 if (--page->ref > 0)
128 {
129 return;
130 }
131
133 {
134 pmm_stack_push(pfn);
135 }
136 else
137 {
138 pmm_bitmap_clear(pfn, 1);
139 }
140
141 avail++;
142}
143
144static void pmm_free_region_unlocked(pfn_t pfn, size_t count)
145{
146 for (size_t i = 0; i < count; i++)
147 {
148 pmm_free_unlocked(pfn + i);
149 }
150}
151
153{
154 LOG_INFO("UEFI-provided memory map\n");
155
156 for (size_t i = 0; i < map->length; i++)
157 {
158 const EFI_MEMORY_DESCRIPTOR* desc = BOOT_MEMORY_MAP_GET_DESCRIPTOR(map, i);
159
160 if (boot_is_mem_ram(desc->Type))
161 {
162 total += desc->NumberOfPages;
163 }
164
165 pfn_t endPfn = VIRT_TO_PFN(desc->VirtualStart) + desc->NumberOfPages;
166 highest = MAX(highest, endPfn);
167 }
168
169 LOG_INFO("page amount %llu\n", total);
170}
171
173{
174 size_t size = highest * sizeof(page_t);
175 for (size_t i = 0; i < map->length; i++)
176 {
177 const EFI_MEMORY_DESCRIPTOR* desc = BOOT_MEMORY_MAP_GET_DESCRIPTOR(map, i);
178 if (desc->Type == EfiConventionalMemory && desc->NumberOfPages >= BYTES_TO_PAGES(size))
179 {
180 pages = (page_t*)desc->VirtualStart;
181 LOG_INFO("pages [%p-%p]\n", pages, (uintptr_t)pages + size);
182 return;
183 }
184 }
185
186 panic(NULL, "Failed to allocate pmm refs array");
187}
188
190{
191 pfn_t pagesPfn = VIRT_TO_PFN(pages);
192
193 for (size_t i = 0; i < map->length; i++)
194 {
195 const EFI_MEMORY_DESCRIPTOR* desc = BOOT_MEMORY_MAP_GET_DESCRIPTOR(map, i);
196
197 pfn_t pfn = VIRT_TO_PFN(desc->VirtualStart);
198 size_t amount = desc->NumberOfPages;
199
200 if (pfn == pagesPfn)
201 {
202 // Skip the pages array.
203 size_t toSkip = BYTES_TO_PAGES(highest * sizeof(page_t));
204 pfn += toSkip;
205 amount -= toSkip;
206 }
207
208 if (pmm_is_mem_avail(desc->Type))
209 {
210#ifndef NDEBUG
211 // Clear the memory to deliberately cause corruption if the memory is actually being used.
212 memset(PFN_TO_VIRT(pfn), 0xCC, amount * PAGE_SIZE);
213#endif
214 for (size_t j = 0; j < amount; j++)
215 {
216 page_t* page = &pages[pfn + j];
217 page->ref = 1;
218 }
219
220 pmm_free_region_unlocked(pfn, amount);
221 }
222 else
223 {
224 for (size_t j = 0; j < amount; j++)
225 {
226 page_t* page = &pages[pfn + j];
227 page->ref = UINT16_MAX;
228 }
229
230 LOG_INFO("reserve [%p-%p] pages=%d type=%s\n", PFN_TO_VIRT(pfn), PFN_TO_VIRT(pfn + amount), amount,
231 efiMemTypeToString[desc->Type]);
232 }
233 }
234
235 LOG_INFO("memory %llu MB (usable %llu MB reserved %llu MB)\n", (total * PAGE_SIZE) / 1000000,
236 (pmm_avail_pages() * PAGE_SIZE) / 1000000, ((total - pmm_avail_pages()) * PAGE_SIZE) / 1000000);
237}
238
239void pmm_init(void)
240{
243
247}
248
250{
252 pfn_t pfn = pmm_stack_pop();
253 if (pfn == ERR)
254 {
256 }
257
258 if (pfn != ERR)
259 {
260 page_t* page = &pages[pfn];
261 assert(page->ref == 0);
262 page->ref = 1;
263 avail--;
264 }
266
267 if (pfn == ERR)
268 {
269 LOG_WARN("out of memory in pmm_alloc()\n");
270 }
271
272 return pfn;
273}
274
276{
278 for (size_t i = 0; i < count; i++)
279 {
280 pfns[i] = pmm_stack_pop();
281 if (pfns[i] == ERR)
282 {
284 }
285
286 if (pfns[i] == ERR)
287 {
288 LOG_WARN("out of memory in pmm_alloc_pages()\n");
289 for (size_t j = 0; j < i; j++)
290 {
291 if (pfns[j] >= CONFIG_PMM_BITMAP_MAX_ADDR / PAGE_SIZE)
292 {
293 pmm_stack_push(pfns[j]);
294 }
295 else
296 {
297 pmm_bitmap_clear(pfns[j], 1);
298 }
299 }
301 return ERR;
302 }
303 }
304
305 for (size_t i = 0; i < count; i++)
306 {
307 page_t* page = &pages[pfns[i]];
308 assert(page->ref == 0);
309 page->ref = 1;
310 }
311
312 avail -= count;
314 return 0;
315}
316
317pfn_t pmm_alloc_bitmap(size_t count, pfn_t maxPfn, pfn_t alignPfn)
318{
320 pfn_t pfn = pmm_bitmap_set(count, maxPfn, alignPfn);
321 if (pfn != ERR)
322 {
323 for (size_t i = 0; i < count; i++)
324 {
325 page_t* page = &pages[pfn + i];
326 assert(page->ref == 0);
327 page->ref = 1;
328 }
329 avail -= count;
330 }
332
333 if (pfn == ERR)
334 {
335 LOG_WARN("out of memory in pmm_alloc_bitmap()\n");
336 }
337
338 return pfn;
339}
340
342{
346}
347
348void pmm_free_pages(pfn_t* pfns, size_t count)
349{
351 for (size_t i = 0; i < count; i++)
352 {
353 pmm_free_unlocked(pfns[i]);
354 }
356}
357
358void pmm_free_region(pfn_t pfn, size_t count)
359{
363}
364
366{
368 for (size_t i = 0; i < count; i++)
369 {
370 page_t* page = &pages[pfn + i];
371 if (page->ref == 0 || page->ref == UINT16_MAX)
372 {
373 for (size_t j = 0; j < i; j++)
374 {
375 pages[pfn + j].ref--;
376 }
378 return ERR;
379 }
380 page->ref++;
381 }
382 uint64_t ret = pages[pfn].ref;
384 return ret;
385}
386
387size_t pmm_total_pages(void)
388{
390 size_t ret = total;
392 return ret;
393}
394
395size_t pmm_avail_pages(void)
396{
398 size_t ret = avail;
400 return ret;
401}
402
403size_t pmm_used_pages(void)
404{
406 size_t ret = total - avail;
408 return ret;
409}
#define assert(expression)
Definition assert.h:29
boot_memory_map_t * map
Definition main.c:241
boot_info_t * bootInfo
Definition boot_info.c:14
static UNUSED_FUNC bool boot_is_mem_ram(EFI_MEMORY_TYPE type)
Definition boot_info.h:24
#define BOOT_MEMORY_MAP_GET_DESCRIPTOR(map, index)
Definition boot_info.h:52
#define CONFIG_PMM_BITMAP_MAX_ADDR
Maximum bitmap allocator address.
Definition config.h:136
boot_info_t * boot_info_get(void)
Gets the boot info structure.
Definition boot_info.c:16
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:292
#define LOG_WARN(format,...)
Definition log.h:92
#define LOG_INFO(format,...)
Definition log.h:91
#define VIRT_TO_PFN(_addr)
Convert a identity mapped higher half virtual address to its PFN.
#define PFN_TO_VIRT(_pfn)
Convert a PFN to its identity mapped higher half virtual address.
size_t pfn_t
Page Frame Number type.
void pmm_free_pages(pfn_t *pfns, size_t count)
Free multiple pages of physical memory.
Definition pmm.c:348
uint64_t pmm_alloc_pages(pfn_t *pfns, size_t count)
Allocate multiple pages of physical memory.
Definition pmm.c:275
#define FREE_PAGE_MAX
Maximum number of free pages that can be stored in a free page.
Definition pmm.h:49
size_t pmm_total_pages(void)
Get the total number of physical pages.
Definition pmm.c:387
void pmm_free(pfn_t pfn)
Free a single page of physical memory.
Definition pmm.c:341
uint64_t pmm_ref_inc(pfn_t pfn, size_t count)
Increment the reference count of a physical region.
Definition pmm.c:365
pfn_t pmm_alloc(void)
Allocate a single page of physical memory.
Definition pmm.c:249
size_t pmm_used_pages(void)
Get the number of used physical pages.
Definition pmm.c:403
size_t pmm_avail_pages(void)
Get the number of available physical pages.
Definition pmm.c:395
void pmm_free_region(pfn_t pfn, size_t count)
Free a contiguous region of physical memory.
Definition pmm.c:358
pfn_t pmm_alloc_bitmap(size_t count, pfn_t maxPfn, pfn_t alignPfn)
Allocate a contiguous region of physical memory using the bitmap.
Definition pmm.c:317
void pmm_init(void)
Read the boot info memory map and initialize the PMM.
Definition pmm.c:239
#define LOCK_CREATE()
Create a lock initializer.
Definition lock.h:69
static void lock_release(lock_t *lock)
Releases a lock.
Definition lock.h:175
static void lock_acquire(lock_t *lock)
Acquires a lock, blocking until it is available.
Definition lock.h:96
#define BITMAP_CREATE_ONE(name, bits)
Define and create a one-initialized bitmap and its buffer.
Definition bitmap.h:93
static uint64_t bitmap_find_clear_region_and_set(bitmap_t *map, uint64_t minIdx, uintptr_t maxIdx, uint64_t length, uint64_t alignment)
Find a clear region of specified length and alignment, and set it.
Definition bitmap.h:392
static void bitmap_clear_range(bitmap_t *map, uint64_t low, uint64_t high)
Clear a range of bits in the bitmap.
Definition bitmap.h:264
#define MIN(x, y)
Definition math.h:18
#define MAX(x, y)
Definition math.h:17
#define BYTES_TO_PAGES(amount)
Convert a size in bytes to pages.
Definition proc.h:107
#define NULL
Pointer error value.
Definition NULL.h:25
#define ERR
Integer error value.
Definition ERR.h:17
#define PAGE_SIZE
The size of a memory page in bytes.
Definition PAGE_SIZE.h:8
static bool pmm_is_mem_avail(EFI_MEMORY_TYPE type)
Definition pmm.c:51
static page_t * pages
Definition pmm.c:38
static pfn_t pmm_stack_pop(void)
Definition pmm.c:86
static size_t avail
Definition pmm.c:47
static size_t total
Definition pmm.c:46
static page_stack_t * stack
Definition pmm.c:40
static void pmm_bitmap_clear(pfn_t pfn, size_t pageAmount)
Definition pmm.c:118
static lock_t lock
Definition pmm.c:49
static pfn_t highest
Definition pmm.c:45
static void pmm_free_unlocked(pfn_t pfn)
Definition pmm.c:123
static void pmm_free_region_unlocked(pfn_t pfn, size_t count)
Definition pmm.c:144
static void pmm_init_refs(const boot_memory_map_t *map)
Definition pmm.c:172
static void pmm_load_memory(const boot_memory_map_t *map)
Definition pmm.c:189
static void pmm_stack_push(pfn_t pfn)
Definition pmm.c:71
static pfn_t pmm_bitmap_set(size_t count, pfn_t maxPfn, pfn_t alignPfn)
Definition pmm.c:104
static size_t location
Definition pmm.c:41
static const char * efiMemTypeToString[]
Definition pmm.c:20
static void pmm_detect_memory(const boot_memory_map_t *map)
Definition pmm.c:152
static atomic_long count
Definition main.c:11
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
#define UINT16_MAX
Definition stdint.h:66
__UINTPTR_TYPE__ uintptr_t
Definition stdint.h:43
_PUBLIC void * memset(void *s, int c, size_t n)
Definition memset.c:4
boot_memory_t memory
Definition boot_info.h:106
boot_memory_map_t map
Definition boot_info.h:95
A simple ticket lock implementation.
Definition lock.h:44
Stored in free pages to form a stack of free pages.
Definition pmm.h:56
struct page_stack * next
Definition pmm.h:57
pfn_t pages[FREE_PAGE_MAX]
Definition pmm.h:58
Page metadata structure.
Definition pmm.h:42
uint16_t ref
Definition pmm.h:43