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 ((1UL << 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 offset_t last = increment; 60 61 while (1) 62 { 63 if (value < increment) 64 return round(value, last); 65 66 last = increment; 67 increment *= 2; 68 } 69 } 70 71 /* Return 'value' rounded down (or truncated) to the nearest 'increment'. */ 72 73 offset_t trunc(offset_t value, offset_t increment) 74 { 75 return (value / increment) * increment; 76 } 77 78 /* Return 'value' rounded down (or truncated) to the nearest multiple of 79 'increment'. */ 80 81 offset_t trunc_multiple(offset_t value, offset_t increment) 82 { 83 offset_t last = increment; 84 85 while (1) 86 { 87 if (value < increment) 88 return trunc(value, last); 89 90 last = increment; 91 increment *= 2; 92 } 93 } 94 95 /* Find the maximum size aligned region within the region from 'start' to (but 96 not including) 'end', with the given initial 'increment'. */ 97 98 offset_t max_multiple(offset_t start, offset_t end, offset_t increment) 99 { 100 /* The largest possible aligned region is derived from the region size. */ 101 102 offset_t size = trunc_multiple(end - start, increment); 103 104 /* Apply the alignment to the start. */ 105 106 offset_t aligned = round(start, size); 107 108 /* If the region is aligned, return the size. */ 109 110 if (aligned == start) 111 return size; 112 113 /* If the region is not aligned to the current size, recalculate the aligned 114 size. */ 115 116 offset_t aligned_size; 117 118 do 119 { 120 aligned_size = trunc_multiple(end - aligned, increment); 121 size /= 2; 122 123 /* Determine whether a smaller alignment could yield a larger aligned 124 size. */ 125 126 if (aligned_size >= size) 127 return aligned_size; 128 129 aligned = round(start, size); 130 } 131 while (aligned > start); 132 133 return aligned_size; 134 } 135 136 // vim: tabstop=4 expandtab shiftwidth=4