1 /* 2 * Memory quantity calculation utilities. 3 * 4 * Copyright (C) 2021, 2022 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/util/bitops.h> /* l4util_log2 */ 23 24 #include "memory_utils.h" 25 26 27 28 /* Return page 'n' for the configured page size. */ 29 30 offset_t page(unsigned int n) 31 { 32 return PAGE_SIZE * n; 33 } 34 35 /* Return the order of 'size', where 2 ** order yields the size. For sizes not 36 corresponding to 2 ** order precisely, a larger order is returned. */ 37 38 unsigned int page_order(offset_t size) 39 { 40 unsigned int exp = l4util_log2(size); 41 42 if ((1ULL << exp) < size) 43 return exp + 1; 44 else 45 return exp; 46 } 47 48 /* Return 'value' rounded up to the nearest 'increment'. */ 49 50 offset_t round(offset_t value, offset_t increment) 51 { 52 return trunc(value + increment - 1, increment); 53 } 54 55 /* Return 'value' rounded up to the nearest multiple of 'increment'. */ 56 57 offset_t round_multiple(offset_t value, offset_t increment) 58 { 59 if (increment == 0) 60 return 0; 61 62 offset_t last = increment; 63 64 while (1) 65 { 66 if (value < increment) 67 return round(value, last); 68 69 last = increment; 70 increment *= 2; 71 } 72 } 73 74 /* Return 'value' rounded down (or truncated) to the nearest 'increment'. */ 75 76 offset_t trunc(offset_t value, offset_t increment) 77 { 78 return (value / increment) * increment; 79 } 80 81 /* Return 'value' rounded down (or truncated) to the nearest multiple of 82 'increment'. */ 83 84 offset_t trunc_multiple(offset_t value, offset_t increment) 85 { 86 if (increment == 0) 87 return 0; 88 89 offset_t last = increment; 90 91 while (1) 92 { 93 if (value < increment) 94 return trunc(value, last); 95 96 last = increment; 97 increment *= 2; 98 } 99 } 100 101 /* Find the maximum size aligned region within the region from 'start' to (but 102 not including) 'end', with the given initial 'increment'. */ 103 104 offset_t max_multiple(offset_t start, offset_t end, offset_t increment) 105 { 106 /* The largest possible aligned region is derived from the region size. */ 107 108 offset_t size = trunc_multiple(end - start, increment); 109 110 /* Apply the alignment to the start. */ 111 112 offset_t aligned = round(start, size); 113 114 /* If the region is aligned, return the size. */ 115 116 if (aligned == start) 117 return size; 118 119 /* If the region is not aligned to the current size, recalculate the aligned 120 size. */ 121 122 offset_t aligned_size; 123 124 do 125 { 126 aligned_size = trunc_multiple(end - aligned, increment); 127 size /= 2; 128 129 /* Determine whether a smaller alignment could yield a larger aligned 130 size. */ 131 132 if (aligned_size >= size) 133 return aligned_size; 134 135 aligned = round(start, size); 136 } 137 while (aligned > start); 138 139 return aligned_size; 140 } 141 142 // vim: tabstop=4 expandtab shiftwidth=4