1 /* 2 * A flexpage abstraction. 3 * 4 * Copyright (C) 2021, 2022, 2023, 2024 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 <algorithm> 23 24 #include "flexpage.h" 25 #include "ipc.h" 26 27 28 29 /* Virtual destructor required for introspection. */ 30 31 Flexpage::~Flexpage() 32 { 33 } 34 35 /* Reset the flexpage using 'offset', being the file offset. */ 36 37 void Flexpage::reset(offset_t offset) 38 { 39 if (region == NULL) 40 return; 41 42 _counter = 0; 43 _flags = 0; 44 45 /* By definition (see "Flexible-Sized Page Objects - Object-Orientation 46 in Operation Systems"), flexpages are aligned to multiples of their 47 size. 48 49 The size of the flexpage depends on the amount of space around the 50 accessed page. It cannot exceed the size of the memory region. */ 51 52 size = max_multiple(region->start, region->end, PAGE_SIZE); 53 54 /* The base address of the flexpage is computed from the region start 55 and flexpage size. It will be no lower than the region start. 56 57 Sent flexpages may use higher bases due to receive window constraints, 58 these being communicated by the "hot spot". */ 59 60 base_addr = round(region->start, size); 61 62 /* Get the file offset for the base of the flexpage. This will be a 63 multiple of the flexpage size for alignment purposes. */ 64 65 base_offset = trunc(offset, size); 66 67 /* The page being accessed is relative to the base. 68 (This is transient information recording the initialising access 69 details.) */ 70 71 page_offset = trunc(offset - base_offset, PAGE_SIZE); 72 page_addr = base_addr + page_offset; 73 74 /* Where a physical address is available, also provide the base address in 75 physical address space. Note that a physical address of zero is not 76 considered as indicating such an address. */ 77 78 if (region->physical_start) 79 base_addr_physical = region->physical_start + (base_addr - region->start); 80 else 81 base_addr_physical = 0; 82 } 83 84 /* Set a region. */ 85 86 void Flexpage::set_region(Region *region) 87 { 88 this->region = region; 89 } 90 91 /* Decrement the usage counter, returning whether the flexpage is now no longer 92 used. */ 93 94 bool Flexpage::decrement() 95 { 96 if (_counter) 97 { 98 _counter--; 99 return _counter == 0; 100 } 101 else 102 return 0; 103 } 104 105 /* Increment the usage counter. */ 106 107 void Flexpage::increment() 108 { 109 _counter++; 110 } 111 112 /* Unmap and invalidate the flexpage, meaning that it should not now be in 113 use. */ 114 115 void Flexpage::invalidate() 116 { 117 _counter = 0; 118 ipc_unmap_flexpage(this); 119 120 /* Unmap and invalidate all derived flexpages. */ 121 122 disassociate(); 123 } 124 125 /* Return whether the flexpage is in use and is therefore valid. */ 126 127 bool Flexpage::valid() 128 { 129 return _counter != 0; 130 } 131 132 /* Return whether the flexpage supports the given file 'position'. */ 133 134 bool Flexpage::supports_position(offset_t position) 135 { 136 return (base_offset <= position) && (position < (base_offset + size)); 137 } 138 139 /* Upgrade the flags involved with this flexpage. This is used to track the 140 maximal flags employed by the different pagers, with the result being used in 141 unmap operations. */ 142 143 void Flexpage::upgrade(map_flags_t flags) 144 { 145 if (flags && (flags != _flags)) 146 _flags |= flags; 147 } 148 149 /* Return whether the flexpage has been modified due to write access having been 150 granted for any user of the page. */ 151 152 bool Flexpage::modified() 153 { 154 return _flags & L4RE_DS_F_W; 155 } 156 157 /* Return a "send" flexpage for an access to 'offset' by positioning it relative 158 to 'hot_spot' for the receive flexpage window. */ 159 160 SendFlexpage Flexpage::to_send(offset_t offset, offset_t hot_spot, 161 map_flags_t flags, offset_t max_offset) 162 { 163 /* The dataspace offset of the flexpage base is a multiple of the flexpage 164 size. */ 165 166 offset_t receive_base_offset = trunc(offset, size); 167 168 /* The offset of the accessed page within the flexpage is constrained by the 169 current flexpage size. */ 170 171 offset_t page_offset = trunc(offset - receive_base_offset, PAGE_SIZE); 172 173 /* The receive flexpage offset (hot spot) must be constrained to the 174 flexpage, both the size and the start. */ 175 176 offset_t receive_size; 177 178 if (max_offset) 179 { 180 receive_size = trunc_multiple(max_offset - receive_base_offset, PAGE_SIZE); 181 receive_size = std::min(size, receive_size); 182 } 183 else 184 receive_size = size; 185 186 if (!receive_size) 187 return SendFlexpage(base_addr, page_order(0), flags); 188 189 /* Employ the page-aligned hot spot for compatibility with the page 190 offset, thus handling any non-aligned values sent in map requests. */ 191 192 offset_t hot_spot_page = trunc(hot_spot, PAGE_SIZE); 193 offset_t receive_page_offset = hot_spot_page % receive_size; 194 195 while ((receive_size > PAGE_SIZE) && (receive_page_offset != page_offset)) 196 { 197 receive_size /= 2; 198 receive_page_offset = hot_spot_page % receive_size; 199 } 200 201 /* The flexpage base address is adjusted using the difference in page 202 offsets. Where the receive flexpage offset is constained further, the 203 base address will be raised to become closer to the accessed page. */ 204 205 offset_t adjustment = page_offset - receive_page_offset; 206 207 return SendFlexpage(base_addr + adjustment, page_order(receive_size), flags); 208 } 209 210 /* Return a representation of the flexpage for unmapping. */ 211 212 SendFlexpage Flexpage::to_unmap() 213 { 214 return SendFlexpage(base_addr, page_order(size), L4_FPAGE_RWX); 215 } 216 217 /* Associate another flexpage with this flexpage. */ 218 219 void Flexpage::associate(Flexpage *flexpage) 220 { 221 derived.push_back(flexpage); 222 } 223 224 /* Invalidate and unmap all derived flexpages. */ 225 226 void Flexpage::disassociate() 227 { 228 DerivedFlexpages::iterator it; 229 230 for (it = derived.begin(); it != derived.end(); it++) 231 (*it)->invalidate(); 232 233 derived.clear(); 234 } 235 236 // vim: tabstop=4 expandtab shiftwidth=4