paul@93 | 1 | /* |
paul@93 | 2 | * Generic pager functionality. |
paul@93 | 3 | * |
paul@332 | 4 | * Copyright (C) 2021, 2022 Paul Boddie <paul@boddie.org.uk> |
paul@93 | 5 | * |
paul@93 | 6 | * This program is free software; you can redistribute it and/or |
paul@93 | 7 | * modify it under the terms of the GNU General Public License as |
paul@93 | 8 | * published by the Free Software Foundation; either version 2 of |
paul@93 | 9 | * the License, or (at your option) any later version. |
paul@93 | 10 | * |
paul@93 | 11 | * This program is distributed in the hope that it will be useful, |
paul@93 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paul@93 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paul@93 | 14 | * GNU General Public License for more details. |
paul@93 | 15 | * |
paul@93 | 16 | * You should have received a copy of the GNU General Public License |
paul@93 | 17 | * along with this program; if not, write to the Free Software |
paul@93 | 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, |
paul@93 | 19 | * Boston, MA 02110-1301, USA |
paul@93 | 20 | */ |
paul@93 | 21 | |
paul@6 | 22 | #include "dataspace_server.h" |
paul@6 | 23 | #include "ipc.h" |
paul@6 | 24 | #include "pager.h" |
paul@6 | 25 | |
paul@332 | 26 | #include <string.h> |
paul@332 | 27 | |
paul@6 | 28 | |
paul@6 | 29 | |
paul@86 | 30 | /* Initialise the pager with a page mapper and the given flags controlling |
paul@86 | 31 | access to a file. */ |
paul@86 | 32 | |
paul@121 | 33 | Pager::Pager(PageMapper *mapper, map_flags_t flags) |
paul@332 | 34 | : _start(0), _size(0), _mapper(mapper), _flags(flags), |
paul@332 | 35 | _is_masked(false), _start_visible(0), _end_visible(0) |
paul@6 | 36 | { |
paul@6 | 37 | } |
paul@6 | 38 | |
paul@79 | 39 | /* Close the pager. */ |
paul@79 | 40 | |
paul@6 | 41 | void Pager::close() |
paul@6 | 42 | { |
paul@340 | 43 | /* NOTE: Need to deallocate any allocated regions and flexpages. */ |
paul@6 | 44 | } |
paul@6 | 45 | |
paul@53 | 46 | /* Flush data to the file. */ |
paul@53 | 47 | |
paul@53 | 48 | long Pager::flush(offset_t populated_size, offset_t *size) |
paul@53 | 49 | { |
paul@55 | 50 | _mapper->flush_all(_start, populated_size); |
paul@53 | 51 | |
paul@55 | 52 | *size = _mapper->get_data_size(); |
paul@55 | 53 | return L4_EOK; |
paul@55 | 54 | } |
paul@53 | 55 | |
paul@55 | 56 | /* Resize the underlying file. */ |
paul@53 | 57 | |
paul@55 | 58 | long Pager::resize(offset_t *size) |
paul@55 | 59 | { |
paul@55 | 60 | _mapper->set_data_size(*size); |
paul@55 | 61 | |
paul@55 | 62 | *size = _mapper->get_data_size(); |
paul@53 | 63 | return L4_EOK; |
paul@53 | 64 | } |
paul@53 | 65 | |
paul@7 | 66 | /* Expose a region of the file. */ |
paul@7 | 67 | |
paul@339 | 68 | long Pager::mmap(offset_t position, offset_t length, |
paul@339 | 69 | offset_t start_visible, offset_t end_visible, |
paul@332 | 70 | offset_t *start_pos, offset_t *end_pos, offset_t *size) |
paul@6 | 71 | { |
paul@90 | 72 | /* Define region characteristics. */ |
paul@90 | 73 | |
paul@7 | 74 | _start = trunc(position, PAGE_SIZE); |
paul@7 | 75 | _size = round(position + length, PAGE_SIZE) - _start; |
paul@6 | 76 | |
paul@90 | 77 | /* Return the start and end positions plus populated extent. */ |
paul@90 | 78 | |
paul@7 | 79 | *start_pos = _start; |
paul@7 | 80 | *end_pos = _start + _size; |
paul@90 | 81 | *size = _mapper->get_data_size(); |
paul@7 | 82 | |
paul@332 | 83 | /* Permit masking of mapped regions. */ |
paul@332 | 84 | |
paul@339 | 85 | if (start_visible || end_visible) |
paul@332 | 86 | { |
paul@339 | 87 | _start_visible = start_visible; |
paul@339 | 88 | _end_visible = end_visible; |
paul@332 | 89 | _is_masked = (*start_pos != _start_visible) || (*end_pos != _end_visible); |
paul@332 | 90 | } |
paul@332 | 91 | else |
paul@332 | 92 | _is_masked = false; |
paul@332 | 93 | |
paul@7 | 94 | return L4_EOK; |
paul@6 | 95 | } |
paul@6 | 96 | |
paul@6 | 97 | /* Map a flexpage corresponding to the dataspace 'offset' involving a 'hot_spot' |
paul@6 | 98 | (flexpage offset). */ |
paul@6 | 99 | |
paul@121 | 100 | long Pager::map(offset_t offset, address_t hot_spot, map_flags_t flags, |
paul@90 | 101 | l4_snd_fpage_t *region) |
paul@6 | 102 | { |
paul@6 | 103 | offset_t file_offset = _start + offset; |
paul@6 | 104 | offset_t max_offset = _start + _size; |
paul@87 | 105 | |
paul@332 | 106 | /* Prevent access beyond that defined by the pager. |
paul@332 | 107 | NOTE: Permitting executable requests here. This needs to be configured |
paul@332 | 108 | when opening the pager or by another means. */ |
paul@87 | 109 | |
paul@331 | 110 | if (flags & (~(_flags | L4RE_DS_F_X))) |
paul@87 | 111 | return -L4_EACCESS; |
paul@87 | 112 | |
paul@340 | 113 | Flexpage *issued_flexpage = NULL, *obtained_flexpage = NULL; |
paul@340 | 114 | |
paul@340 | 115 | /* Obtain any replicated flexpage. |
paul@340 | 116 | NOTE: An additional condition should be introduced to test for the |
paul@340 | 117 | relevance of replicated flexpages. */ |
paul@340 | 118 | |
paul@340 | 119 | if (flags & L4RE_DS_F_W) |
paul@340 | 120 | issued_flexpage = _map.find(file_offset); |
paul@340 | 121 | |
paul@340 | 122 | /* Without any replicated flexpage, obtain one for the shared, underlying |
paul@340 | 123 | file data. */ |
paul@6 | 124 | |
paul@340 | 125 | if (issued_flexpage == NULL) |
paul@340 | 126 | { |
paul@340 | 127 | obtained_flexpage = _mapper->get(file_offset, flags); |
paul@340 | 128 | |
paul@340 | 129 | /* Determine if the flexpage should be masked. */ |
paul@332 | 130 | |
paul@340 | 131 | issued_flexpage = get_masked_flexpage(obtained_flexpage); |
paul@340 | 132 | |
paul@340 | 133 | /* Determine if the flexpage should be replicated. */ |
paul@340 | 134 | |
paul@340 | 135 | issued_flexpage = get_replicated_flexpage(issued_flexpage, flags); |
paul@340 | 136 | } |
paul@332 | 137 | |
paul@6 | 138 | /* Issue the flexpage via the IPC system. */ |
paul@6 | 139 | |
paul@332 | 140 | long err = ipc_prepare_flexpage(issued_flexpage, file_offset, max_offset, |
paul@332 | 141 | hot_spot, flags, region); |
paul@6 | 142 | |
paul@37 | 143 | if (!err) |
paul@87 | 144 | err = complete_Dataspace_map(*region); |
paul@6 | 145 | |
paul@340 | 146 | /* After the obtained flexpage is issued, it is queued for future reuse. */ |
paul@6 | 147 | |
paul@340 | 148 | if (obtained_flexpage != NULL) |
paul@340 | 149 | _mapper->queue(obtained_flexpage); |
paul@6 | 150 | |
paul@37 | 151 | if (err) |
paul@87 | 152 | return err; |
paul@37 | 153 | |
paul@6 | 154 | return IPC_MESSAGE_SENT; |
paul@6 | 155 | } |
paul@6 | 156 | |
paul@6 | 157 | /* Return the total size of the data. */ |
paul@6 | 158 | |
paul@6 | 159 | offset_t Pager::get_data_size() |
paul@6 | 160 | { |
paul@6 | 161 | return _mapper->get_data_size(); |
paul@6 | 162 | } |
paul@6 | 163 | |
paul@332 | 164 | /* Detect flexpages with masked content, introducing separate flexpages |
paul@332 | 165 | offering masked content from the same region. */ |
paul@332 | 166 | |
paul@332 | 167 | Flexpage *Pager::get_masked_flexpage(Flexpage *flexpage) |
paul@332 | 168 | { |
paul@332 | 169 | /* If not masking content, return the original flexpage. */ |
paul@332 | 170 | |
paul@332 | 171 | if (!_is_masked) |
paul@332 | 172 | return flexpage; |
paul@332 | 173 | |
paul@332 | 174 | /* Determine whether the flexpage involves the limits of the visible |
paul@340 | 175 | region or is beyond such limits. */ |
paul@332 | 176 | |
paul@332 | 177 | bool has_start = flexpage->supports_position(_start_visible) && |
paul@332 | 178 | flexpage->base_offset != _start_visible; |
paul@332 | 179 | bool has_end = flexpage->supports_position(_end_visible); |
paul@332 | 180 | bool has_zero = (flexpage->base_offset >= _end_visible) || |
paul@332 | 181 | (flexpage->base_offset + flexpage->size < _start_visible); |
paul@332 | 182 | |
paul@332 | 183 | /* Return the original flexpage within the visible limits. */ |
paul@332 | 184 | |
paul@332 | 185 | if (!has_start && !has_end && !has_zero) |
paul@332 | 186 | return flexpage; |
paul@332 | 187 | |
paul@332 | 188 | /* Allocate and populate a region in one of the preallocated flexpages for |
paul@340 | 189 | masked content. */ |
paul@332 | 190 | |
paul@332 | 191 | Flexpage &masked = has_start ? _start_flexpage : |
paul@332 | 192 | has_end ? _end_flexpage : |
paul@332 | 193 | _zero_flexpage; |
paul@332 | 194 | |
paul@332 | 195 | allocate_region(flexpage, masked); |
paul@332 | 196 | populate_region(flexpage, masked, has_start, has_end); |
paul@332 | 197 | |
paul@332 | 198 | /* Associate the preallocated flexpage with the original flexpage. */ |
paul@332 | 199 | |
paul@332 | 200 | flexpage->associate(&masked); |
paul@340 | 201 | |
paul@332 | 202 | return &masked; |
paul@332 | 203 | } |
paul@332 | 204 | |
paul@332 | 205 | void Pager::allocate_region(Flexpage *flexpage, Flexpage &masked) |
paul@332 | 206 | { |
paul@332 | 207 | offset_t needed = flexpage->region->size(); |
paul@332 | 208 | |
paul@332 | 209 | /* Attempt to re-use an existing region. */ |
paul@332 | 210 | |
paul@332 | 211 | if (masked.region != NULL) |
paul@332 | 212 | { |
paul@332 | 213 | if (masked.region->size() == needed) |
paul@332 | 214 | return; |
paul@332 | 215 | else |
paul@332 | 216 | _memory.release(masked.region); |
paul@332 | 217 | } |
paul@332 | 218 | |
paul@332 | 219 | /* Set the region in the flexpage. */ |
paul@332 | 220 | |
paul@332 | 221 | masked.set_region(_memory.region(needed)); |
paul@332 | 222 | masked.reset(flexpage->base_offset); |
paul@332 | 223 | } |
paul@332 | 224 | |
paul@332 | 225 | void Pager::populate_region(Flexpage *flexpage, Flexpage &masked, |
paul@332 | 226 | bool has_start, bool has_end) |
paul@332 | 227 | { |
paul@332 | 228 | /* Without any limits involved, provide a zero flexpage. */ |
paul@332 | 229 | |
paul@332 | 230 | if (!has_start && !has_end) |
paul@332 | 231 | { |
paul@332 | 232 | memset((void *) masked.region->start, 0, masked.size); |
paul@332 | 233 | return; |
paul@332 | 234 | } |
paul@332 | 235 | |
paul@332 | 236 | /* Determine the content limits given start and/or end offsets. */ |
paul@332 | 237 | |
paul@332 | 238 | offset_t start_offset = has_start ? _start_visible - masked.base_offset : 0; |
paul@332 | 239 | offset_t end_offset = has_end ? _end_visible - masked.base_offset : masked.size; |
paul@332 | 240 | |
paul@332 | 241 | if (has_start) |
paul@332 | 242 | memset((void *) masked.region->start, 0, start_offset); |
paul@332 | 243 | |
paul@332 | 244 | memcpy((void *) (masked.region->start + start_offset), |
paul@332 | 245 | (const void *) (flexpage->region->start + start_offset), |
paul@332 | 246 | end_offset - start_offset); |
paul@332 | 247 | |
paul@332 | 248 | if (has_end) |
paul@332 | 249 | memset((void *) (masked.region->start + end_offset), 0, masked.size - end_offset); |
paul@332 | 250 | } |
paul@332 | 251 | |
paul@340 | 252 | /* Detect flexpages with replicated content, introducing separate flexpages |
paul@340 | 253 | offering replicated content from the same region. */ |
paul@340 | 254 | |
paul@340 | 255 | Flexpage *Pager::get_replicated_flexpage(Flexpage *flexpage, map_flags_t flags) |
paul@340 | 256 | { |
paul@340 | 257 | /* NOTE: An additional condition should be introduced to test for the |
paul@340 | 258 | relevance of replicated flexpages. For now, detect copy-on-write |
paul@340 | 259 | situations. */ |
paul@340 | 260 | |
paul@340 | 261 | if (_is_masked && (flags & L4RE_DS_F_W)) |
paul@340 | 262 | { |
paul@340 | 263 | Region *region = _memory.region(flexpage->size); |
paul@340 | 264 | Flexpage *replicated = new Flexpage(region); |
paul@340 | 265 | |
paul@340 | 266 | replicated->reset(flexpage->base_offset); |
paul@340 | 267 | |
paul@340 | 268 | memcpy((void *) region->start, (const void *) flexpage->region->start, flexpage->size); |
paul@340 | 269 | _map.insert(replicated); |
paul@340 | 270 | return replicated; |
paul@340 | 271 | } |
paul@340 | 272 | else |
paul@340 | 273 | return flexpage; |
paul@340 | 274 | } |
paul@340 | 275 | |
paul@6 | 276 | // vim: tabstop=4 expandtab shiftwidth=4 |