AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
mm.c
Go to the documentation of this file.
1
9
10#include <core/system.h>
11#include <core/panic.h>
12#include <core/arch.h>
13#include <core/string.h>
14
15#include <boot/boot.h>
16
17#include <mm/pmap.h>
18#include <mm/mm.h>
19#include <mm/vm.h>
20#include <mm/buddy.h>
21
22#include <sys/sched.h>
23
24/* a page structure for every page of RAM, up to its end (mm_setup) */
25static struct vm_page *pages;
26static size_t pages_nr;
27#define PAGE(addr) (pages[(addr)/PAGE_SIZE])
28
29/* pages[] tracks memory, not what is past it (device memory, such as a
30 * framebuffer mapped by a process): no reference counts for those */
31#define TRACKED(addr) ((addr)/PAGE_SIZE < pages_nr)
32
40{
41 if (TRACKED(paddr))
42 PAGE(paddr).ref++;
43}
44
51{
52 if (TRACKED(paddr))
53 PAGE(paddr).ref--;
54}
55
63{
64 return TRACKED(paddr) ? PAGE(paddr).ref : 0;
65}
66
79{
80 /* a page above 16 MiB, or below once there's none left there (nothing
81 * needs memory an ISA device can reach) */
83
84 if (paddr == (paddr_t) -1)
86
87 if (paddr == (paddr_t) -1)
88 return NULL;
89
90 struct vm_page *vm_page = &PAGE(paddr);
91
92 memset(vm_page, 0, sizeof(struct vm_page));
94
95 return vm_page;
96}
97
104{
105 /* to the zone it came from (an address past RAM, buddy_free leaves
106 * alone) */
108 buddy_free(zone, paddr, PAGE_SIZE);
109}
110
122int mm_page_map(struct pmap *pmap, vaddr_t vaddr, paddr_t paddr, int flags)
123{
124 return pmap_add(pmap, vaddr, paddr, flags);
125}
126
134int mm_page_unmap(struct pmap *pmap, vaddr_t vaddr)
135{
136 /* is a page mapped there? */
138
139 if (paddr) {
140 /* the architecture's page tables */
141 pmap_remove(pmap, vaddr, vaddr + PAGE_SIZE);
142 return 0;
143 }
144
145 return -EINVAL;
146}
147
161int mm_map(struct pmap *pmap, paddr_t paddr, vaddr_t vaddr, size_t size, int flags)
162{
163 int alloc = !paddr;
164
165 vaddr_t endaddr = PAGE_ROUND(vaddr + size);
166 vaddr = PAGE_ALIGN(vaddr);
168
169 size_t nr = (endaddr - vaddr) / PAGE_SIZE;
170
171 while (nr--) {
172 paddr_t phys = arch_page_get_mapping(pmap, vaddr);
173
174 if (!phys) {
175 if (alloc) {
176 struct vm_page *vm_page = mm_page_alloc();
177
178 if (!vm_page)
179 return -ENOMEM;
180
182 }
183
184 int err = mm_page_map(pmap, vaddr, paddr, flags);
185
186 if (err) {
187 if (alloc)
189
190 return err;
191 }
192 }
193
194 vaddr += PAGE_SIZE;
195 paddr += PAGE_SIZE;
196 }
197
198 return 0;
199}
200
208void mm_unmap_full(struct pmap *pmap, vaddr_t vaddr, size_t size)
209{
210 vaddr_t start = PAGE_ALIGN(vaddr);
211 vaddr_t end = PAGE_ROUND(vaddr + size);
212
213 size_t nr = (end - start)/PAGE_SIZE;
214
215 while (nr--) {
216 mm_page_unmap(pmap, start);
217 start += PAGE_SIZE;
218 }
219}
220
221/*
222 * Boot memory: the structures whose size depends on how much RAM there is
223 * (pages[], the buddy allocator's bitmaps). They go after the kernel,
224 * where the boot loader left nothing, and are taken before the buddy
225 * allocator knows what is free; then they are marked used. They are
226 * reached through the boot mapping (VMA()).
227 */
229
238void *mm_boot_alloc(size_t size, size_t align)
239{
240 paddr_t paddr = (boot_mem_next + align - 1) & ~(paddr_t) (align - 1);
241
242 if (paddr + size > boot_mem_end)
243 panic("mm: out of boot memory");
244
245 boot_mem_next = paddr + size;
246
247 void *ptr = (void *) VMA((uintptr_t) paddr);
248 memset(ptr, 0, size);
249 return ptr;
250}
251
255static paddr_t boot_left(struct boot *boot, paddr_t start, size_t size)
256{
257 paddr_t end = start + size;
258
259 for (int i = 0; i < boot->modules_count; ++i) {
260 paddr_t s = boot->modules[i].paddr;
261 paddr_t e = s + boot->modules[i].size;
262
263 if (start < e && end > s)
264 return e;
265 }
266
267 /* the kernel's symbols and string table, the command line */
268 paddr_t r[3][2] = {{0, 0}, {0, 0}, {0, 0}};
269
270 if (boot->symtab) {
271 r[0][0] = LMA((uintptr_t) boot->symtab->sh_addr);
272 r[0][1] = r[0][0] + boot->symtab->sh_size;
273 }
274
275 if (boot->strtab) {
276 r[1][0] = LMA((uintptr_t) boot->strtab->sh_addr);
277 r[1][1] = r[1][0] + boot->strtab->sh_size;
278 }
279
280 if (boot->cmdline) {
281 r[2][0] = LMA((uintptr_t) boot->cmdline);
282 r[2][1] = r[2][0] + strlen(boot->cmdline) + 1;
283 }
284
285 for (int i = 0; i < 3; ++i)
286 if (r[i][1] && start < r[i][1] && end > r[i][0])
287 return r[i][1];
288
289 return 0;
290}
291
294static int boot_usable(struct boot *boot, paddr_t start, size_t size)
295{
296 for (int i = 0; i < boot->mmap_count; ++i)
297 if (boot->mmap[i].type == MMAP_USABLE &&
298 boot->mmap[i].start <= start && start + size <= boot->mmap[i].end)
299 return 1;
300
301 return 0;
302}
303
306static void boot_mem_place(struct boot *boot, size_t size)
307{
308 extern char kernel_end;
309 paddr_t start = PAGE_ROUND((uintptr_t) &kernel_end), end;
310
311 while ((end = boot_left(boot, start, size)))
312 start = PAGE_ROUND(end);
313
314 if (start + size > ARCH_BOOT_MAPPED || !boot_usable(boot, start, size))
315 panic("mm: no room for the page structures after the kernel");
316
318 boot_mem_end = start + size;
319}
320
324{
325 paddr_t at = 0;
326
327 while (at < ram_end) {
328 /* the usable region that starts the lowest at or after `at` */
329 paddr_t next = ram_end, next_end = ram_end;
330
331 for (int i = 0; i < boot->mmap_count; ++i) {
332 mmap_t *m = &boot->mmap[i];
333
334 if (m->type != MMAP_USABLE || m->end <= at)
335 continue;
336
337 paddr_t s = m->start < at ? at : m->start;
338
339 if (s < next) {
340 next = s;
341 next_end = m->end;
342 }
343 }
344
345 if (next > at)
346 buddy_set_unusable(at, next - at);
347
348 at = next_end;
349 }
350
351 for (int i = 0; i < boot->mmap_count; ++i)
352 if (boot->mmap[i].type != MMAP_USABLE && boot->mmap[i].start < ram_end)
354 MIN(boot->mmap[i].end, ram_end) - boot->mmap[i].start);
355}
356
365void mm_setup(struct boot *boot)
366{
367 /* the end of RAM, and how much there is */
368 paddr_t ram_end = 0;
369 uint64_t ram = 0;
370
371 for (int i = 0; i < boot->mmap_count; ++i) {
372 if (boot->mmap[i].type == MMAP_USABLE) {
373 ram += boot->mmap[i].end - boot->mmap[i].start;
374 ram_end = MAX(ram_end, boot->mmap[i].end);
375 }
376 }
377
378 /* no memory map: as much as the boot loader says there is, from 0 */
379 if (!ram_end) {
380 ram_end = (paddr_t) boot->total_mem * 1024;
381 ram = ram_end;
382 }
383
384#ifdef ARCH_RAM_MAX
385 /* only what the kernel maps can be used (i386's direct map) */
386 if (ram_end > ARCH_RAM_MAX) {
387 ram = 0;
388
389 for (int i = 0; i < boot->mmap_count; ++i)
390 if (boot->mmap[i].type == MMAP_USABLE && boot->mmap[i].start < ARCH_RAM_MAX)
391 ram += MIN(boot->mmap[i].end, ARCH_RAM_MAX) - boot->mmap[i].start;
392
393 printk("kernel: only the first %ld MiB are used\n", (uint64_t) (ARCH_RAM_MAX >> 20));
394 ram_end = ARCH_RAM_MAX;
395 }
396#endif
397
398 /* the buddy allocator works in blocks of BUDDY_MAX_BS, so round up to
399 * the next one; what's past RAM is set unusable (mm_mark_unusable()),
400 * and only RAM is handed out (buddy_reserved_done()) */
401 ram_end = (ram_end + BUDDY_MAX_BS - 1) & ~(paddr_t) (BUDDY_MAX_BS - 1);
402
403 printk("kernel: %ld MiB of memory, up to %p\n", ram >> 20, ram_end);
404
406
407 /* room for the page structures and the buddy allocator's bitmaps
408 * (at most two bits per page, over all its orders) */
409 size_t pages_size = pages_nr * sizeof(struct vm_page);
410 boot_mem_place(boot, PAGE_ROUND(pages_size + pages_nr / 4 + 0x10000));
411
412 pages = mm_boot_alloc(pages_size, sizeof(uintptr_t));
413
415
416 /* never hand out physical page 0: 0 means "no page" (no mapping) */
418
421
422 /* the modules (the RAM disk) */
423 for (int i = 0; i < boot->modules_count; ++i) {
424 module_t *mod = &boot->modules[i];
425 buddy_set_unusable(mod->paddr, mod->size);
426 }
427
428 /* what /proc/meminfo reports as the total */
429 extern size_t k_total_mem;
430 k_total_mem = ram;
431
433}
#define PAGE_SIZE
Definition system.h:7
uint64_t paddr_t
Definition mm.h:39
#define ARCH_BOOT_MAPPED
Definition mm.h:23
#define LMA(obj)
Definition mm.h:37
#define VMA(obj)
Definition mm.h:36
uint64_t vaddr_t
Definition mm.h:40
#define panic(s)
Definition panic.h:15
size_t k_total_mem
Definition buddy.c:20
char kernel_end
@ MMAP_USABLE
Definition boot.h:23
#define MIN(a, b)
Definition system.h:11
#define MAX(a, b)
Definition system.h:10
void mm_page_decref(paddr_t paddr)
Drops a reference to a page of RAM.
Definition mm.c:50
#define PAGE_ALIGN(ptr)
Definition mm.h:9
#define PAGE_ROUND(ptr)
Definition mm.h:10
struct vm_page * mm_page_alloc(void)
Allocates a free page of physical memory.
Definition mm.c:78
int mm_page_map(struct pmap *pmap, vaddr_t vaddr, paddr_t paddr, int flags)
Maps a page into an address space.
Definition mm.c:122
int mm_map(struct pmap *pmap, paddr_t paddr, vaddr_t vaddr, size_t size, int flags)
Maps a range of an address space, page by page.
Definition mm.c:161
size_t mm_page_ref(paddr_t paddr)
Returns how many references a page of RAM has.
Definition mm.c:62
void mm_page_dealloc(paddr_t paddr)
Frees a page of physical memory, back to the zone it came from.
Definition mm.c:103
void mm_unmap_full(struct pmap *pmap, vaddr_t vaddr, size_t size)
Unmaps every page of a range of an address space.
Definition mm.c:208
void mm_page_incref(paddr_t paddr)
Adds a reference to a page of RAM.
Definition mm.c:39
void mm_setup(struct boot *boot)
Sets up physical memory at boot, from the boot loader's memory map: the page structures,...
Definition mm.c:365
void * memset(void *dst, int c, size_t n)
Sets n bytes to c.
Definition libc.c:23
void buddy_free(int zone, paddr_t paddr, size_t size)
Frees a block that buddy_alloc() gave.
Definition buddy.c:183
void buddy_set_unusable(paddr_t paddr, size_t size)
Marks a range of physical memory as not free, at boot, before the first allocation: the kernel,...
Definition buddy.c:212
#define BUDDY_MAX_BS
Definition buddy.h:12
#define BUDDY_ZONE_DMA
Definition buddy.h:16
paddr_t buddy_alloc(int zone, size_t size)
Allocates a block of physical memory from a zone, rounded up to a power of two pages.
Definition buddy.c:149
uintptr_t buddy_zone_offset[]
Definition buddy.c:49
#define BUDDY_ZONE_NORMAL
Definition buddy.h:17
int buddy_setup(size_t total_mem)
Sets up the allocator at boot: a bitmap for each zone and order, every top-order block of RAM free,...
Definition buddy.c:328
int mm_page_unmap(struct pmap *pmap, vaddr_t vaddr)
Unmaps a page from an address space.
Definition mm.c:134
static void mm_mark_unusable(struct boot *boot, paddr_t ram_end)
Marks as used what isn't usable RAM below ram_end: the regions the memory map reserves,...
Definition mm.c:323
void * mm_boot_alloc(size_t size, size_t align)
Allocates boot memory, zeroed, before the buddy allocator is set up.
Definition mm.c:238
static void boot_mem_place(struct boot *boot, size_t size)
Places the boot memory: at the first place after the kernel that is RAM, free, and in the boot mappin...
Definition mm.c:306
static paddr_t boot_mem_next
Definition mm.c:228
static struct vm_page * pages
Definition mm.c:25
static paddr_t boot_left(struct boot *boot, paddr_t start, size_t size)
Returns the end of whatever the boot loader left in memory (modules, the kernel's symbols,...
Definition mm.c:255
static paddr_t boot_mem_end
Definition mm.c:228
#define TRACKED(addr)
Definition mm.c:31
static int boot_usable(struct boot *boot, paddr_t start, size_t size)
Returns whether [start, start + size) is all in one usable region of the boot loader's memory map.
Definition mm.c:294
#define PAGE(addr)
Definition mm.c:27
static size_t pages_nr
Definition mm.c:26
static paddr_t boot_mem_start
Definition mm.c:228
int pmap_add(struct pmap *pmap, vaddr_t va, paddr_t pa, uint32_t flags)
Maps a page in an address space, replacing what was mapped there.
Definition pmap.c:307
paddr_t arch_page_get_mapping(struct pmap *pmap, vaddr_t vaddr)
Finds the page mapped at a virtual address of an address space.
Definition pmap.c:431
static paddr_t ram_end(void)
Returns the end of the RAM that the boot loader found.
Definition pmap.c:182
void arch_mm_setup(void)
Finishes setting up memory at boot, once mm_setup() has set up the page structures and the buddy allo...
Definition pmap.c:514
void pmap_remove(struct pmap *pmap, vaddr_t sva, vaddr_t eva)
Unmaps the pages of a range of an address space, and flushes them from the TLBs that may have them.
Definition pmap.c:337
int printk(const char *fmt,...)
Writes a kernel message, to the message buffer and the consoles.
Definition printk.c:249
#define NULL
Definition stddef.h:27
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
unsigned long long int uint64_t
Definition stddef.h:23
Definition boot.h:35
int mmap_count
Definition boot.h:40
char * cmdline
Definition boot.h:36
module_t * modules
Definition boot.h:41
elf_shdr_t * symtab
Definition boot.h:49
uintptr_t total_mem
Definition boot.h:37
elf_shdr_t * strtab
Definition boot.h:52
mmap_t * mmap
Definition boot.h:42
int modules_count
Definition boot.h:39
elf32_addr sh_addr
Definition elf.h:173
elf32_word sh_size
Definition elf.h:175
Definition boot.h:28
uintptr_t end
Definition boot.h:31
enum mmap_type type
Definition boot.h:29
uintptr_t start
Definition boot.h:30
size_t size
Definition boot.h:17
uintptr_t paddr
Definition boot.h:16
An address space's page tables, on x86.
Definition pmap.h:9
A page of physical memory (pages[] has one for every page of RAM).
Definition vm.h:150
paddr_t paddr
its physical address
Definition vm.h:151