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