1 /* Copyright (c) 2009,13 The Regents of the University of California
2 * Barret Rhoden <brho@cs.berkeley.edu>
3 * See LICENSE for details.
5 * Arch independent physical memory and page table management.
7 * For page allocation, check out the family of page_alloc files. */
22 #include <multiboot.h>
26 physaddr_t max_pmem = 0; /* Total amount of physical memory (bytes) */
27 physaddr_t max_paddr = 0; /* Maximum addressable physical address */
28 size_t max_nr_pages = 0; /* Number of addressable physical memory pages */
29 struct page *pages = 0;
30 struct multiboot_info *multiboot_kaddr = 0;
31 uintptr_t boot_freemem = 0;
32 uintptr_t boot_freelimit = 0;
34 static size_t sizeof_mboot_mmentry(struct multiboot_mmap_entry *entry)
36 /* Careful - len is a uint64 (need to cast down for 32 bit) */
37 return (size_t)(entry->len);
40 static void adjust_max_pmem(struct multiboot_mmap_entry *entry, void *data)
42 if (entry->type != MULTIBOOT_MEMORY_AVAILABLE)
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));
48 static void kpages_arena_init(void)
52 kpages_pg = arena_alloc(base_arena, PGSIZE, MEM_WAIT);
53 kpages_arena = arena_builder(kpages_pg, "kpages", PGSIZE, arena_alloc,
54 arena_free, base_arena, 8 * PGSIZE);
58 * @brief Initializes physical memory. Determines the pmem layout, sets up the
59 * base and kpages arenas, and turns on virtual memory/page tables.
61 * Regarding max_pmem vs max_paddr and max_nr_pages: max_pmem is the largest
62 * physical address that is in a FREE region. It includes RESERVED regions that
63 * are below this point. max_paddr is the largest physical address, <=
64 * max_pmem, that the KERNBASE mapping can map. It too may include reserved
65 * ranges. The 'pages' array will track all physical pages up to max_paddr.
66 * There are max_nr_pages of them. On 64 bit systems, max_pmem == max_paddr. */
67 void pmem_init(struct multiboot_info *mbi)
69 mboot_detect_memory(mbi);
70 mboot_print_mmap(mbi);
71 /* adjust the max memory based on the mmaps, since the old detection doesn't
72 * help much on 64 bit systems */
73 mboot_foreach_mmap(mbi, adjust_max_pmem, 0);
74 /* KERN_VMAP_TOP - KERNBASE is the max amount of virtual addresses we can
75 * use for the physical memory mapping (aka - the KERNBASE mapping).
76 * Should't be an issue on 64b, but is usually for 32 bit. */
77 max_paddr = MIN(max_pmem, KERN_VMAP_TOP - KERNBASE);
78 /* Note not all of this memory is free. */
79 max_nr_pages = max_paddr / PGSIZE;
80 printk("Max physical RAM (appx, bytes): %lu\n", max_pmem);
81 printk("Max addressable physical RAM (appx): %lu\n", max_paddr);
82 printk("Highest page number (including reserved): %lu\n", max_nr_pages);
83 /* We should init the page structs, but zeroing happens to work, except for
84 * the sems. Those are init'd by the page cache before they are used. */
85 pages = (struct page*)boot_zalloc(max_nr_pages * sizeof(struct page),
88 /* kpages will use some of the basic slab caches. kmem_cache_init needs to
89 * not do memory allocations (which it doesn't, and it can base_alloc()). */
92 printk("Base arena total mem: %lu\n", arena_amt_total(base_arena));
95 static_assert(PROCINFO_NUM_PAGES*PGSIZE <= PTSIZE);
96 static_assert(PROCDATA_NUM_PAGES*PGSIZE <= PTSIZE);
99 static void set_largest_freezone(struct multiboot_mmap_entry *entry, void *data)
101 struct multiboot_mmap_entry **boot_zone =
102 (struct multiboot_mmap_entry**)data;
104 if (entry->type != MULTIBOOT_MEMORY_AVAILABLE)
106 if (!*boot_zone || (sizeof_mboot_mmentry(entry) >
107 sizeof_mboot_mmentry(*boot_zone)))
111 /* Initialize boot freemem and its limit.
113 * "end" is a symbol marking the end of the kernel. This covers anything linked
114 * in with the kernel (KFS, etc). However, 'end' is a kernel load address,
115 * which differs from kernbase addrs in 64 bit. We need to use the kernbase
116 * mapping for anything dynamic (because it could go beyond 1 GB).
118 * Ideally, we'll use the largest mmap zone, as reported by multiboot. If we
119 * don't have one (riscv), we'll just use the memory after the kernel.
121 * If we do have a zone, there is a chance we've already used some of it (for
122 * the kernel, etc). We'll use the lowest address in the zone that is
123 * greater than "end" (and adjust the limit accordingly). */
124 static void boot_alloc_init(void)
127 uintptr_t boot_zone_start, boot_zone_end;
128 uintptr_t end_kva = (uintptr_t)KBASEADDR(end);
129 struct multiboot_mmap_entry *boot_zone = 0;
131 /* Find our largest mmap_entry; that will set bootzone */
132 mboot_foreach_mmap(multiboot_kaddr, set_largest_freezone, &boot_zone);
134 boot_zone_start = (uintptr_t)KADDR(boot_zone->addr);
135 /* one issue for 32b is that the boot_zone_end could be beyond max_paddr
136 * and even wrap-around. Do the min check as a uint64_t. The result
137 * should be a safe, unwrapped 32/64b when cast to physaddr_t. */
138 boot_zone_end = (uintptr_t)KADDR(MIN(boot_zone->addr + boot_zone->len,
139 (uint64_t)max_paddr));
140 /* using KERNBASE (kva, btw) which covers the kernel and anything before
141 * it (like the stuff below EXTPHYSMEM on x86) */
142 if (regions_collide_unsafe(KERNBASE, end_kva,
143 boot_zone_start, boot_zone_end))
144 boot_freemem = end_kva;
146 boot_freemem = boot_zone_start;
147 boot_freelimit = boot_zone_end;
149 boot_freemem = end_kva;
150 boot_freelimit = max_paddr + KERNBASE;
152 printd("boot_zone: %p, paddr base: 0x%llx, paddr len: 0x%llx\n", boot_zone,
153 boot_zone ? boot_zone->addr : 0,
154 boot_zone ? boot_zone->len : 0);
155 printd("boot_freemem: %p, boot_freelimit %p\n", boot_freemem,
159 /* Low-level allocator, used before page_alloc is on. Returns size bytes,
160 * aligned to align (should be a power of 2). Retval is a kernbase addr. Will
161 * panic on failure. */
162 void *boot_alloc(size_t amt, size_t align)
168 boot_freemem = ROUNDUP(boot_freemem, align);
169 retval = boot_freemem;
170 if (boot_freemem + amt > boot_freelimit){
171 printk("boot_alloc: boot_freemem is 0x%x\n", boot_freemem);
172 printk("boot_alloc: amt is %d\n", amt);
173 printk("boot_freelimit is 0x%x\n", boot_freelimit);
174 printk("boot_freemem + amt is > boot_freelimit\n");
175 panic("Out of memory in boot alloc, you fool!\n");
178 printd("boot alloc from %p to %p\n", retval, boot_freemem);
179 /* multiboot info probably won't ever conflict with our boot alloc */
180 if (mboot_region_collides(multiboot_kaddr, retval, boot_freemem))
181 panic("boot allocation could clobber multiboot info! Get help!");
182 return (void*)retval;
185 void *boot_zalloc(size_t amt, size_t align)
187 /* boot_alloc panics on failure */
188 void *v = boot_alloc(amt, align);
194 * @brief Map the physical page 'pp' into the virtual address 'va' in page
197 * Map the physical page 'pp' at virtual address 'va'.
198 * The permissions (the low 12 bits) of the page table
199 * entry should be set to 'perm|PTE_P'.
202 * - If there is already a page mapped at 'va', it is page_remove()d.
203 * - If necessary, on demand, allocates a page table and inserts it into
205 * - This saves your refcnt in the pgdir (refcnts going away soon).
206 * - The TLB must be invalidated if a page was formerly present at 'va'.
207 * (this is handled in page_remove)
209 * No support for jumbos here. We will need to be careful when trying to
210 * insert regular pages into something that was already jumbo. We will
211 * also need to be careful with our overloading of the PTE_PS and
214 * @param[in] pgdir the page directory to insert the page into
215 * @param[in] pp a pointr to the page struct representing the
216 * physical page that should be inserted.
217 * @param[in] va the virtual address where the page should be
219 * @param[in] perm the permition bits with which to set up the
222 * @return ESUCCESS on success
223 * @return -ENOMEM if a page table could not be allocated
224 * into which the page should be inserted
227 int page_insert(pgdir_t pgdir, struct page *page, void *va, int perm)
229 pte_t pte = pgdir_walk(pgdir, va, 1);
230 if (!pte_walk_okay(pte))
232 /* Leftover from older times, but we no longer suppor this: */
233 assert(!pte_is_mapped(pte));
234 pte_write(pte, page2pa(page), perm);
239 * @brief Return the page mapped at virtual address 'va' in
240 * page directory 'pgdir'.
242 * If pte_store is not NULL, then we store in it the address
243 * of the pte for this page. This is used by page_remove
244 * but should not be used by other callers.
246 * For jumbos, right now this returns the first Page* in the 4MB range
248 * @param[in] pgdir the page directory from which we should do the lookup
249 * @param[in] va the virtual address of the page we are looking up
250 * @param[out] pte_store the address of the page table entry for the returned page
252 * @return PAGE the page mapped at virtual address 'va'
253 * @return NULL No mapping exists at virtual address 'va', or it's paged out
255 page_t *page_lookup(pgdir_t pgdir, void *va, pte_t *pte_store)
257 pte_t pte = pgdir_walk(pgdir, va, 0);
258 if (!pte_walk_okay(pte) || !pte_is_mapped(pte))
262 return pa2page(pte_get_paddr(pte));
266 * @brief Unmaps the physical page at virtual address 'va' in page directory
269 * If there is no physical page at that address, this function silently
273 * - The ref count on the physical page is decrement when the page is removed
274 * - The physical page is freed if the refcount reaches 0.
275 * - The pg table entry corresponding to 'va' is set to 0.
276 * (if such a PTE exists)
277 * - The TLB is invalidated if an entry is removes from the pg dir/pg table.
279 * This may be wonky wrt Jumbo pages and decref.
281 * @param pgdir the page directory from with the page sholuld be removed
282 * @param va the virtual address at which the page we are trying to
284 * TODO: consider deprecating this, or at least changing how it works with TLBs.
285 * Might want to have the caller need to manage the TLB. Also note it is used
286 * in env_user_mem_free, minus the walk. */
287 void page_remove(pgdir_t pgdir, void *va)
292 pte = pgdir_walk(pgdir,va,0);
293 if (!pte_walk_okay(pte) || pte_is_unmapped(pte))
296 if (pte_is_mapped(pte)) {
297 /* TODO: (TLB) need to do a shootdown, inval sucks. And might want to
298 * manage the TLB / free pages differently. (like by the caller).
299 * Careful about the proc/memory lock here. */
300 page = pa2page(pte_get_paddr(pte));
302 tlb_invalidate(pgdir, va);
304 } else if (pte_is_paged_out(pte)) {
305 /* TODO: (SWAP) need to free this from the swap */
306 panic("Swapping not supported!");
312 * @brief Invalidate a TLB entry, but only if the page tables being
313 * edited are the ones currently in use by the processor.
315 * TODO: (TLB) Need to sort this for cross core lovin'
317 * @param pgdir the page directory assocaited with the tlb entry
318 * we are trying to invalidate
319 * @param va the virtual address associated with the tlb entry
320 * we are trying to invalidate
322 void tlb_invalidate(pgdir_t pgdir, void *va)
324 // Flush the entry only if we're modifying the current address space.
325 // For now, there is only one address space, so always invalidate.
329 static void __tlb_global(uint32_t srcid, long a0, long a1, long a2)
334 /* Does a global TLB flush on all cores. */
335 void tlb_shootdown_global(void)
340 /* TODO: consider a helper for broadcast messages, though note that we're
341 * doing our flush immediately, which our caller expects from us before it
343 for (int i = 0; i < num_cores; i++) {
346 send_kernel_message(i, __tlb_global, 0, 0, 0, KMSG_IMMEDIATE);
350 /* Helper, returns true if any part of (start1, end1) is within (start2, end2).
351 * Equality of endpoints (like end1 == start2) is okay.
352 * Assumes no wrap-around. */
353 bool regions_collide_unsafe(uintptr_t start1, uintptr_t end1,
354 uintptr_t start2, uintptr_t end2)
356 if (start1 <= start2) {