1 // Copyright 2016 The Fuchsia Authors
2 // Copyright (c) 2014 Travis Geiselbrecht
3 //
4 // Use of this source code is governed by a MIT-style
5 // license that can be found in the LICENSE file or at
6 // https://opensource.org/licenses/MIT
7 
8 #include <vm/bootalloc.h>
9 
10 #include "vm_priv.h"
11 #include <stdint.h>
12 #include <stdlib.h>
13 #include <sys/types.h>
14 #include <trace.h>
15 #include <vm/physmap.h>
16 #include <vm/pmm.h>
17 #include <vm/vm.h>
18 
19 #define LOCAL_TRACE MAX(VM_GLOBAL_TRACE, 0)
20 
21 // Simple boot time allocator that starts by allocating physical memory off
22 // the end of wherever the kernel is loaded in physical space.
23 //
24 // Pointers are returned from the kernel's physmap
25 
26 // store the start and current pointer to the boot allocator in physical address
27 paddr_t boot_alloc_start;
28 paddr_t boot_alloc_end;
29 
30 // run in physical space without the mmu set up, so by computing the address of _end
31 // and saving it, we've effectively computed the physical address of the end of the
32 // kernel.
33 __NO_SAFESTACK
boot_alloc_init()34 void boot_alloc_init() {
35     boot_alloc_start = reinterpret_cast<paddr_t>(_end);
36     // TODO(ZX-2563): This is a compile-time no-op that defeats any compiler
37     // optimizations based on its knowledge/assumption that `&_end` is a
38     // constant here that equals the `&_end` constant as computed elsewhere.
39     // Without this, the compiler can see that boot_alloc_start is never set to
40     // any other value and replace code that uses the boot_alloc_start value
41     // with code that computes `&_end` on the spot.  What the compiler doesn't
42     // know is that this `&_end` is crucially a PC-relative computation when
43     // the PC is a (low) physical address.  Later code that uses
44     // boot_alloc_start will be running at a kernel (high) virtual address and
45     // so its `&_end` will be nowhere near the same value.  The compiler isn't
46     // wrong to do this sort of optimization when it can and other such cases
47     // will eventually arise.  So long-term we'll need more thorough
48     // compile-time separation of the early boot code that runs in physical
49     // space from normal kernel code.  For now, this asm generates no
50     // additional code but tells the compiler that it has no idea what value
51     // boot_alloc_start might take, so it has to compute the `&_end` value now.
52     __asm__(""
53             : "=g"(boot_alloc_start)
54             : "0"(boot_alloc_start));
55     boot_alloc_end = reinterpret_cast<paddr_t>(_end);
56 }
57 
boot_alloc_reserve(paddr_t start,size_t len)58 void boot_alloc_reserve(paddr_t start, size_t len) {
59     uintptr_t end = ALIGN((start + len), PAGE_SIZE);
60 
61     if (end >= boot_alloc_start) {
62         if ((start > boot_alloc_start) &&
63             ((start - boot_alloc_start) > (128 * 1024 * 1024))) {
64             // if we've got 128MB of space, that's good enough
65             // it's possible that the start may be *way* far up
66             // (gigabytes) and there may not be space after it...
67             return;
68         }
69         boot_alloc_start = boot_alloc_end = end;
70     }
71 }
72 
boot_alloc_mem(size_t len)73 void* boot_alloc_mem(size_t len) {
74     uintptr_t ptr;
75 
76     ptr = ALIGN(boot_alloc_end, 8);
77     boot_alloc_end = (ptr + ALIGN(len, 8));
78 
79     LTRACEF("len %zu, phys ptr %#" PRIxPTR " ptr %p\n", len, ptr, paddr_to_physmap(ptr));
80 
81     return paddr_to_physmap(ptr);
82 }
83 
84 // called from arch start.S
85 // run in physical space without the mmu set up, so stick to basic, relocatable code
86 __NO_SAFESTACK
boot_alloc_page_phys()87 paddr_t boot_alloc_page_phys() {
88     paddr_t ptr = ALIGN(boot_alloc_end, PAGE_SIZE);
89     boot_alloc_end = ptr + PAGE_SIZE;
90 
91     return ptr;
92 }
93