Adds epoch_*sec() helpers
[akaros.git] / kern / src / pmap.c
1 /* Copyright (c) 2009,13 The Regents of the University of California
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details. 
4  *
5  * Arch independent physical memory and page table management.
6  *
7  * For page allocation, check out the family of page_alloc files. */
8
9 #include <arch/arch.h>
10 #include <arch/mmu.h>
11
12 #include <error.h>
13
14 #include <kmalloc.h>
15 #include <atomic.h>
16 #include <string.h>
17 #include <assert.h>
18 #include <pmap.h>
19 #include <kclock.h>
20 #include <process.h>
21 #include <stdio.h>
22 #include <mm.h>
23 #include <multiboot.h>
24
25 physaddr_t max_pmem = 0;        /* Total amount of physical memory (bytes) */
26 physaddr_t max_paddr = 0;       /* Maximum addressable physical address */
27 size_t max_nr_pages = 0;        /* Number of addressable physical memory pages */
28 size_t nr_free_pages = 0;       /* TODO: actually track this, after init */
29 struct page *pages = 0;
30 struct multiboot_info *multiboot_kaddr = 0;
31 uintptr_t boot_freemem = 0;
32 uintptr_t boot_freelimit = 0;
33
34 static size_t sizeof_mboot_mmentry(struct multiboot_mmap_entry *entry)
35 {
36         /* Careful - len is a uint64 (need to cast down for 32 bit) */
37         return (size_t)(entry->len);
38 }
39
40 static void adjust_max_pmem(struct multiboot_mmap_entry *entry, void *data)
41 {
42         if (entry->type != MULTIBOOT_MEMORY_AVAILABLE)
43                 return;
44         /* Careful - addr + len is a uint64 (need to cast down for 32 bit) */
45         max_pmem = MAX(max_pmem, (size_t)(entry->addr + entry->len));
46 }
47
48 /**
49  * @brief Initializes physical memory.  Determines the pmem layout, sets up the
50  * array of physical pages and memory free list, and turns on virtual
51  * memory/page tables.
52  *
53  * Regarding max_pmem vs max_paddr and max_nr_pages: max_pmem is the largest
54  * physical address that is in a FREE region.  It includes RESERVED regions that
55  * are below this point.  max_paddr is the largest physical address, <=
56  * max_pmem, that the KERNBASE mapping can map.  It too may include reserved
57  * ranges.  The 'pages' array will track all physical pages up to max_paddr.
58  * There are max_nr_pages of them.  On 64 bit systems, max_pmem == max_paddr. */
59 void pmem_init(struct multiboot_info *mbi)
60 {
61         mboot_detect_memory(mbi);
62         mboot_print_mmap(mbi);
63         /* adjust the max memory based on the mmaps, since the old detection doesn't
64          * help much on 64 bit systems */
65         mboot_foreach_mmap(mbi, adjust_max_pmem, 0);
66         /* KERN_VMAP_TOP - KERNBASE is the max amount of virtual addresses we can
67          * use for the physical memory mapping (aka - the KERNBASE mapping).
68          * Should't be an issue on 64b, but is usually for 32 bit. */
69         max_paddr = MIN(max_pmem, KERN_VMAP_TOP - KERNBASE);
70         /* Note not all of this memory is free. */
71         max_nr_pages = max_paddr / PGSIZE;
72         printk("Max physical RAM (appx, bytes): %lu\n", max_pmem);
73         printk("Max addressable physical RAM (appx): %lu\n", max_paddr);
74         printk("Highest page number (including reserved): %lu\n", max_nr_pages);
75         pages = (struct page*)boot_zalloc(max_nr_pages * sizeof(struct page),
76                                           PGSIZE);
77         page_alloc_init(mbi);
78         vm_init();
79
80         static_assert(PROCINFO_NUM_PAGES*PGSIZE <= PTSIZE);
81         static_assert(PROCDATA_NUM_PAGES*PGSIZE <= PTSIZE);
82 }
83
84 static void set_largest_freezone(struct multiboot_mmap_entry *entry, void *data)
85 {
86         struct multiboot_mmap_entry **boot_zone =
87                (struct multiboot_mmap_entry**)data;
88
89         if (entry->type != MULTIBOOT_MEMORY_AVAILABLE)
90                 return;
91         if (!*boot_zone || (sizeof_mboot_mmentry(entry) >
92                            sizeof_mboot_mmentry(*boot_zone)))
93                 *boot_zone = entry;
94 }
95
96 /* Initialize boot freemem and its limit.
97  *
98  * "end" is a symbol marking the end of the kernel.  This covers anything linked
99  * in with the kernel (KFS, etc).  However, 'end' is a kernel load address,
100  * which differs from kernbase addrs in 64 bit.  We need to use the kernbase
101  * mapping for anything dynamic (because it could go beyond 1 GB). 
102  *
103  * Ideally, we'll use the largest mmap zone, as reported by multiboot.  If we
104  * don't have one (riscv), we'll just use the memory after the kernel.
105  *
106  * If we do have a zone, there is a chance we've already used some of it (for
107  * the kernel, etc).  We'll use the lowest address in the zone that is
108  * greater than "end" (and adjust the limit accordingly).  */
109 static void boot_alloc_init(void)
110 {
111         extern char end[];
112         uintptr_t boot_zone_start, boot_zone_end;
113         uintptr_t end_kva = (uintptr_t)KBASEADDR(end);
114         struct multiboot_mmap_entry *boot_zone = 0;
115
116         /* Find our largest mmap_entry; that will set bootzone */
117         mboot_foreach_mmap(multiboot_kaddr, set_largest_freezone, &boot_zone);
118         if (boot_zone) {
119                 boot_zone_start = (uintptr_t)KADDR(boot_zone->addr);
120                 /* one issue for 32b is that the boot_zone_end could be beyond max_paddr
121                  * and even wrap-around.  Do the min check as a uint64_t.  The result
122                  * should be a safe, unwrapped 32/64b when cast to physaddr_t. */
123                 boot_zone_end = (uintptr_t)KADDR(MIN(boot_zone->addr + boot_zone->len,
124                                                  (uint64_t)max_paddr));
125                 /* using KERNBASE (kva, btw) which covers the kernel and anything before
126                  * it (like the stuff below EXTPHYSMEM on x86) */
127                 if (regions_collide_unsafe(KERNBASE, end_kva,
128                                            boot_zone_start, boot_zone_end))
129                         boot_freemem = end_kva;
130                 else
131                         boot_freemem = boot_zone_start;
132                 boot_freelimit = boot_zone_end;
133         } else {
134                 boot_freemem = end_kva;
135                 boot_freelimit = max_paddr + KERNBASE;
136         }
137         printd("boot_zone: %p, paddr base: 0x%llx, paddr len: 0x%llx\n", boot_zone,
138                boot_zone ? boot_zone->addr : 0,
139                boot_zone ? boot_zone->len : 0);
140         printd("boot_freemem: %p, boot_freelimit %p\n", boot_freemem,
141                boot_freelimit);
142 }
143
144 /* Low-level allocator, used before page_alloc is on.  Returns size bytes,
145  * aligned to align (should be a power of 2).  Retval is a kernbase addr.  Will
146  * panic on failure. */
147 void *boot_alloc(size_t amt, size_t align)
148 {
149         uintptr_t retval;
150
151         if (!boot_freemem)
152                 boot_alloc_init();
153         boot_freemem = ROUNDUP(boot_freemem, align);
154         retval = boot_freemem;
155         if (boot_freemem + amt > boot_freelimit){
156                 printk("boot_alloc: boot_freemem is 0x%x\n", boot_freemem);
157                 printk("boot_alloc: amt is %d\n", amt);
158                 printk("boot_freelimit is 0x%x\n", boot_freelimit);
159                 printk("boot_freemem + amt is > boot_freelimit\n");
160                 panic("Out of memory in boot alloc, you fool!\n");
161         }
162         boot_freemem += amt;
163         printd("boot alloc from %p to %p\n", retval, boot_freemem);
164         /* multiboot info probably won't ever conflict with our boot alloc */
165         if (mboot_region_collides(multiboot_kaddr, retval, boot_freemem))
166                 panic("boot allocation could clobber multiboot info!  Get help!");
167         return (void*)retval;
168 }
169
170 void *boot_zalloc(size_t amt, size_t align)
171 {
172         /* boot_alloc panics on failure */
173         void *v = boot_alloc(amt, align);
174         memset(v, 0, amt);
175         return v;
176 }
177
178 /** 
179  * @brief Map the physical page 'pp' into the virtual address 'va' in page
180  *        directory 'pgdir'
181  *
182  * Map the physical page 'pp' at virtual address 'va'.
183  * The permissions (the low 12 bits) of the page table
184  * entry should be set to 'perm|PTE_P'.
185  * 
186  * Details:
187  *   - If there is already a page mapped at 'va', it is page_remove()d.
188  *   - If necessary, on demand, allocates a page table and inserts it into 
189  *     'pgdir'.
190  *   - page_incref() should be called if the insertion succeeds. 
191  *   - The TLB must be invalidated if a page was formerly present at 'va'.
192  *     (this is handled in page_remove)
193  *
194  * No support for jumbos here.  We will need to be careful when trying to
195  * insert regular pages into something that was already jumbo.  We will
196  * also need to be careful with our overloading of the PTE_PS and 
197  * PTE_PAT flags...
198  *
199  * @param[in] pgdir the page directory to insert the page into
200  * @param[in] pp    a pointr to the page struct representing the
201  *                  physical page that should be inserted.
202  * @param[in] va    the virtual address where the page should be
203  *                  inserted.
204  * @param[in] perm  the permition bits with which to set up the 
205  *                  virtual mapping.
206  *
207  * @return ESUCCESS  on success
208  * @return -ENOMEM   if a page table could not be allocated
209  *                   into which the page should be inserted
210  *
211  */
212 int page_insert(pde_t *pgdir, struct page *page, void *va, int perm) 
213 {
214         pte_t* pte = pgdir_walk(pgdir, va, 1);
215         if (!pte)
216                 return -ENOMEM;
217         /* Two things here:  First, we need to up the ref count of the page we want
218          * to insert in case it is already mapped at va.  In that case we don't want
219          * page_remove to ultimately free it, and then for us to continue as if pp
220          * wasn't freed. (moral = up the ref asap) */
221         kref_get(&page->pg_kref, 1);
222         /* Careful, page remove handles the cases where the page is PAGED_OUT. */
223         if (!PAGE_UNMAPPED(*pte))
224                 page_remove(pgdir, va);
225         *pte = PTE(page2ppn(page), PTE_P | perm);
226         return 0;
227 }
228
229 /**
230  * @brief Return the page mapped at virtual address 'va' in 
231  * page directory 'pgdir'.
232  *
233  * If pte_store is not NULL, then we store in it the address
234  * of the pte for this page.  This is used by page_remove
235  * but should not be used by other callers.
236  *
237  * For jumbos, right now this returns the first Page* in the 4MB range
238  *
239  * @param[in]  pgdir     the page directory from which we should do the lookup
240  * @param[in]  va        the virtual address of the page we are looking up
241  * @param[out] pte_store the address of the page table entry for the returned page
242  *
243  * @return PAGE the page mapped at virtual address 'va'
244  * @return NULL No mapping exists at virtual address 'va', or it's paged out
245  */
246 page_t *page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
247 {
248         pte_t* pte = pgdir_walk(pgdir, va, 0);
249         if (!pte || !PAGE_PRESENT(*pte))
250                 return 0;
251         if (pte_store)
252                 *pte_store = pte;
253         return pa2page(PTE_ADDR(*pte));
254 }
255
256 /**
257  * @brief Unmaps the physical page at virtual address 'va' in page directory
258  * 'pgdir'.
259  *
260  * If there is no physical page at that address, this function silently 
261  * does nothing.
262  *
263  * Details:
264  *   - The ref count on the physical page is decrement when the page is removed
265  *   - The physical page is freed if the refcount reaches 0.
266  *   - The pg table entry corresponding to 'va' is set to 0.
267  *     (if such a PTE exists)
268  *   - The TLB is invalidated if an entry is removes from the pg dir/pg table.
269  *
270  * This may be wonky wrt Jumbo pages and decref.  
271  *
272  * @param pgdir the page directory from with the page sholuld be removed
273  * @param va    the virtual address at which the page we are trying to 
274  *              remove is mapped
275  * TODO: consider deprecating this, or at least changing how it works with TLBs.
276  * Might want to have the caller need to manage the TLB.  Also note it is used
277  * in env_user_mem_free, minus the walk. */
278 void page_remove(pde_t *pgdir, void *va)
279 {
280         pte_t *pte;
281         page_t *page;
282
283         pte = pgdir_walk(pgdir,va,0);
284         if (!pte || PAGE_UNMAPPED(*pte))
285                 return;
286
287         if (PAGE_PRESENT(*pte)) {
288                 /* TODO: (TLB) need to do a shootdown, inval sucks.  And might want to
289                  * manage the TLB / free pages differently. (like by the caller).
290                  * Careful about the proc/memory lock here. */
291                 page = ppn2page(PTE2PPN(*pte));
292                 *pte = 0;
293                 tlb_invalidate(pgdir, va);
294                 page_decref(page);
295         } else if (PAGE_PAGED_OUT(*pte)) {
296                 /* TODO: (SWAP) need to free this from the swap */
297                 panic("Swapping not supported!");
298                 *pte = 0;
299         }
300 }
301
302 /**
303  * @brief Invalidate a TLB entry, but only if the page tables being
304  * edited are the ones currently in use by the processor.
305  *
306  * TODO: (TLB) Need to sort this for cross core lovin'
307  *
308  * @param pgdir the page directory assocaited with the tlb entry 
309  *              we are trying to invalidate
310  * @param va    the virtual address associated with the tlb entry
311  *              we are trying to invalidate
312  */
313 void tlb_invalidate(pde_t *pgdir, void *va)
314 {
315         // Flush the entry only if we're modifying the current address space.
316         // For now, there is only one address space, so always invalidate.
317         invlpg(va);
318 }
319
320 /* Helper, returns true if any part of (start1, end1) is within (start2, end2).
321  * Equality of endpoints (like end1 == start2) is okay.
322  * Assumes no wrap-around. */
323 bool regions_collide_unsafe(uintptr_t start1, uintptr_t end1, 
324                             uintptr_t start2, uintptr_t end2)
325 {
326         if (start1 <= start2) {
327                 if (end1 <= start2)
328                         return FALSE;
329                 return TRUE;
330         } else {
331                 if (end2 <= start1)
332                         return FALSE;
333                 return TRUE;
334         }
335 }
336
337 void print_free_mem(void)
338 {
339         static uint8_t *bm = 0;
340         /* racy, but this is debugging code */
341         if (!bm)
342                 bm = kzmalloc((max_nr_pages + 1) / 8, 0);
343
344         long x = 0;
345         for (int i = 0; i < max_nr_pages; i++) {
346                 if (page_is_free(i)) {
347                         x++;
348                         SET_BITMASK_BIT(bm, i);
349                 } else {
350                         if (GET_BITMASK_BIT(bm, i)) {
351                                 print_pageinfo(ppn2page(i));
352                                 CLR_BITMASK_BIT(bm, i);
353                         }
354                 }
355         }
356         printk("Nr Free pages: %lld\n", x);
357 }