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