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