1// Copyright 2018 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7#include <inttypes.h>
8#include <sys/types.h>
9
10#include <vm/bootreserve.h>
11
12#include "vm_priv.h"
13
14#include <trace.h>
15#include <vm/pmm.h>
16
17#define LOCAL_TRACE MAX(VM_GLOBAL_TRACE, 0)
18
19static const size_t NUM_RESERVES = 16;
20static reserve_range_t res[NUM_RESERVES];
21static size_t res_idx;
22
23void boot_reserve_init() {
24    // add the kernel to the boot reserve list
25    boot_reserve_add_range(get_kernel_base_phys(), get_kernel_size());
26}
27
28zx_status_t boot_reserve_add_range(paddr_t pa, size_t len) {
29    dprintf(INFO, "PMM: boot reserve add [%#" PRIxPTR ", %#" PRIxPTR "]\n", pa, pa + len - 1);
30
31    if (res_idx == NUM_RESERVES) {
32        panic("too many boot reservations\n");
33    }
34
35    // insert into the list, sorted
36    paddr_t end = pa + len - 1;
37    DEBUG_ASSERT(end > pa);
38    for (size_t i = 0; i < res_idx; i++) {
39        if (Intersects(res[i].pa, res[i].len, pa, len)) {
40            // we have a problem that we are not equipped to handle right now
41            panic("boot_reserve_add_range: pa %#" PRIxPTR " len %zx intersects existing range\n",
42                  pa, len);
43        }
44
45        if (res[i].pa > end) {
46            // insert before this one
47            memmove(&res[i + 1], &res[i], (res_idx - i) * sizeof(res[0]));
48            res[i].pa = pa;
49            res[i].len = len;
50            res_idx++;
51            return ZX_OK;
52        }
53    }
54
55    // insert it at the end
56    res[res_idx].pa = pa;
57    res[res_idx].len = len;
58    res_idx++;
59    return ZX_OK;
60}
61
62// iterate through the reserved ranges and mark them as WIRED in the pmm
63void boot_reserve_wire() {
64    static list_node reserved_page_list = LIST_INITIAL_VALUE(reserved_page_list);
65
66    for (size_t i = 0; i < res_idx; i++) {
67        dprintf(INFO, "PMM: boot reserve marking WIRED [%#" PRIxPTR ", %#" PRIxPTR "]\n",
68                res[i].pa, res[i].pa + res[i].len - 1);
69
70        size_t pages = ROUNDUP_PAGE_SIZE(res[i].len) / PAGE_SIZE;
71        zx_status_t status = pmm_alloc_range(res[i].pa, pages, &reserved_page_list);
72        if (status != ZX_OK) {
73            printf("PMM: unable to reserve reserved range [%#" PRIxPTR ", %#" PRIxPTR "]\n",
74                   res[i].pa, res[i].pa + res[i].len - 1);
75            continue; // this is probably fatal but go ahead and continue
76        }
77    }
78
79    // mark all of the pages we allocated as WIRED
80    vm_page_t* p;
81    list_for_every_entry (&reserved_page_list, p, vm_page_t, queue_node) {
82        p->state = VM_PAGE_STATE_WIRED;
83    }
84}
85
86static paddr_t upper_align(paddr_t range_pa, size_t range_len, size_t len) {
87    return (range_pa + range_len - len);
88}
89
90zx_status_t boot_reserve_range_search(paddr_t range_pa, size_t range_len, size_t alloc_len,
91                                      reserve_range_t* alloc_range) {
92    LTRACEF("range pa %#" PRIxPTR " len %#zx alloc_len %#zx\n", range_pa, range_len, alloc_len);
93
94    paddr_t alloc_pa = upper_align(range_pa, range_len, alloc_len);
95
96retry:
97    // see if it intersects any reserved range
98    LTRACEF("trying alloc range %#" PRIxPTR " len %#zx\n", alloc_pa, alloc_len);
99    for (size_t i = 0; i < res_idx; i++) {
100        if (Intersects(res[i].pa, res[i].len, alloc_pa, alloc_len)) {
101            // it intersects this range, move the search spot back to just before it and try again
102            LTRACEF("alloc range %#" PRIxPTR " len %zx intersects with reserve range\n", alloc_pa, alloc_len);
103            alloc_pa = res[i].pa - alloc_len;
104
105            LTRACEF("moving and retrying at %#" PRIxPTR "\n", alloc_pa);
106
107            // make sure this still works with our input constraints
108            if (alloc_pa < range_pa) {
109                LTRACEF("failed to allocate\n");
110                return ZX_ERR_NO_MEMORY;
111            }
112
113            goto retry;
114        }
115    }
116
117    // fell off the list without retrying, must have suceeded
118    LTRACEF("returning [%#" PRIxPTR ", %#" PRIxPTR "]\n",
119            alloc_pa, alloc_pa + alloc_len - 1);
120
121    *alloc_range = {alloc_pa, alloc_len};
122    return ZX_OK;
123}
124