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