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@6 | 43 | } |
paul@6 | 44 | |
paul@53 | 45 | /* Flush data to the file. */ |
paul@53 | 46 | |
paul@53 | 47 | long Pager::flush(offset_t populated_size, offset_t *size) |
paul@53 | 48 | { |
paul@55 | 49 | _mapper->flush_all(_start, populated_size); |
paul@53 | 50 | |
paul@55 | 51 | *size = _mapper->get_data_size(); |
paul@55 | 52 | return L4_EOK; |
paul@55 | 53 | } |
paul@53 | 54 | |
paul@55 | 55 | /* Resize the underlying file. */ |
paul@53 | 56 | |
paul@55 | 57 | long Pager::resize(offset_t *size) |
paul@55 | 58 | { |
paul@55 | 59 | _mapper->set_data_size(*size); |
paul@55 | 60 | |
paul@55 | 61 | *size = _mapper->get_data_size(); |
paul@53 | 62 | return L4_EOK; |
paul@53 | 63 | } |
paul@53 | 64 | |
paul@7 | 65 | /* Expose a region of the file. */ |
paul@7 | 66 | |
paul@332 | 67 | long Pager::mmap(offset_t position, offset_t length, int masked, |
paul@332 | 68 | offset_t *start_pos, offset_t *end_pos, offset_t *size) |
paul@6 | 69 | { |
paul@90 | 70 | /* Define region characteristics. */ |
paul@90 | 71 | |
paul@7 | 72 | _start = trunc(position, PAGE_SIZE); |
paul@7 | 73 | _size = round(position + length, PAGE_SIZE) - _start; |
paul@6 | 74 | |
paul@90 | 75 | /* Return the start and end positions plus populated extent. */ |
paul@90 | 76 | |
paul@7 | 77 | *start_pos = _start; |
paul@7 | 78 | *end_pos = _start + _size; |
paul@90 | 79 | *size = _mapper->get_data_size(); |
paul@7 | 80 | |
paul@332 | 81 | /* Permit masking of mapped regions. */ |
paul@332 | 82 | |
paul@332 | 83 | if (masked) |
paul@332 | 84 | { |
paul@332 | 85 | _start_visible = position; |
paul@332 | 86 | _end_visible = position + length; |
paul@332 | 87 | _is_masked = (*start_pos != _start_visible) || (*end_pos != _end_visible); |
paul@332 | 88 | } |
paul@332 | 89 | else |
paul@332 | 90 | _is_masked = false; |
paul@332 | 91 | |
paul@7 | 92 | return L4_EOK; |
paul@6 | 93 | } |
paul@6 | 94 | |
paul@6 | 95 | /* Map a flexpage corresponding to the dataspace 'offset' involving a 'hot_spot' |
paul@6 | 96 | (flexpage offset). */ |
paul@6 | 97 | |
paul@121 | 98 | long Pager::map(offset_t offset, address_t hot_spot, map_flags_t flags, |
paul@90 | 99 | l4_snd_fpage_t *region) |
paul@6 | 100 | { |
paul@6 | 101 | offset_t file_offset = _start + offset; |
paul@6 | 102 | offset_t max_offset = _start + _size; |
paul@87 | 103 | |
paul@332 | 104 | /* Prevent access beyond that defined by the pager. |
paul@332 | 105 | NOTE: Permitting executable requests here. This needs to be configured |
paul@332 | 106 | when opening the pager or by another means. */ |
paul@87 | 107 | |
paul@331 | 108 | if (flags & (~(_flags | L4RE_DS_F_X))) |
paul@87 | 109 | return -L4_EACCESS; |
paul@87 | 110 | |
paul@11 | 111 | Flexpage *flexpage = _mapper->get(file_offset, flags); |
paul@6 | 112 | |
paul@332 | 113 | /* Determine if the flexpage should be masked. */ |
paul@332 | 114 | |
paul@332 | 115 | Flexpage *issued_flexpage = get_masked_flexpage(flexpage); |
paul@332 | 116 | |
paul@6 | 117 | /* Issue the flexpage via the IPC system. */ |
paul@6 | 118 | |
paul@332 | 119 | long err = ipc_prepare_flexpage(issued_flexpage, file_offset, max_offset, |
paul@332 | 120 | hot_spot, flags, region); |
paul@6 | 121 | |
paul@37 | 122 | if (!err) |
paul@87 | 123 | err = complete_Dataspace_map(*region); |
paul@6 | 124 | |
paul@6 | 125 | /* After the flexpage is issued, it is queued for future reuse. */ |
paul@6 | 126 | |
paul@6 | 127 | _mapper->queue(flexpage); |
paul@6 | 128 | |
paul@37 | 129 | if (err) |
paul@87 | 130 | return err; |
paul@37 | 131 | |
paul@6 | 132 | return IPC_MESSAGE_SENT; |
paul@6 | 133 | } |
paul@6 | 134 | |
paul@6 | 135 | /* Return the total size of the data. */ |
paul@6 | 136 | |
paul@6 | 137 | offset_t Pager::get_data_size() |
paul@6 | 138 | { |
paul@6 | 139 | return _mapper->get_data_size(); |
paul@6 | 140 | } |
paul@6 | 141 | |
paul@332 | 142 | /* Detect flexpages with masked content, introducing separate flexpages |
paul@332 | 143 | offering masked content from the same region. */ |
paul@332 | 144 | |
paul@332 | 145 | Flexpage *Pager::get_masked_flexpage(Flexpage *flexpage) |
paul@332 | 146 | { |
paul@332 | 147 | /* If not masking content, return the original flexpage. */ |
paul@332 | 148 | |
paul@332 | 149 | if (!_is_masked) |
paul@332 | 150 | return flexpage; |
paul@332 | 151 | |
paul@332 | 152 | /* Determine whether the flexpage involves the limits of the visible |
paul@332 | 153 | region. */ |
paul@332 | 154 | |
paul@332 | 155 | bool has_start = flexpage->supports_position(_start_visible) && |
paul@332 | 156 | flexpage->base_offset != _start_visible; |
paul@332 | 157 | bool has_end = flexpage->supports_position(_end_visible); |
paul@332 | 158 | bool has_zero = (flexpage->base_offset >= _end_visible) || |
paul@332 | 159 | (flexpage->base_offset + flexpage->size < _start_visible); |
paul@332 | 160 | |
paul@332 | 161 | /* Return the original flexpage within the visible limits. */ |
paul@332 | 162 | |
paul@332 | 163 | if (!has_start && !has_end && !has_zero) |
paul@332 | 164 | return flexpage; |
paul@332 | 165 | |
paul@332 | 166 | /* Allocate and populate a region in one of the preallocated flexpages for |
paul@332 | 167 | masked content. |
paul@332 | 168 | NOTE: A general copy-on-write solution will maintain a collection of |
paul@332 | 169 | flexpages. */ |
paul@332 | 170 | |
paul@332 | 171 | Flexpage &masked = has_start ? _start_flexpage : |
paul@332 | 172 | has_end ? _end_flexpage : |
paul@332 | 173 | _zero_flexpage; |
paul@332 | 174 | |
paul@332 | 175 | allocate_region(flexpage, masked); |
paul@332 | 176 | populate_region(flexpage, masked, has_start, has_end); |
paul@332 | 177 | |
paul@332 | 178 | /* Associate the preallocated flexpage with the original flexpage. */ |
paul@332 | 179 | |
paul@332 | 180 | flexpage->associate(&masked); |
paul@332 | 181 | return &masked; |
paul@332 | 182 | } |
paul@332 | 183 | |
paul@332 | 184 | void Pager::allocate_region(Flexpage *flexpage, Flexpage &masked) |
paul@332 | 185 | { |
paul@332 | 186 | offset_t needed = flexpage->region->size(); |
paul@332 | 187 | |
paul@332 | 188 | /* Attempt to re-use an existing region. */ |
paul@332 | 189 | |
paul@332 | 190 | if (masked.region != NULL) |
paul@332 | 191 | { |
paul@332 | 192 | if (masked.region->size() == needed) |
paul@332 | 193 | return; |
paul@332 | 194 | else |
paul@332 | 195 | _memory.release(masked.region); |
paul@332 | 196 | } |
paul@332 | 197 | |
paul@332 | 198 | /* Set the region in the flexpage. */ |
paul@332 | 199 | |
paul@332 | 200 | masked.set_region(_memory.region(needed)); |
paul@332 | 201 | masked.reset(flexpage->base_offset); |
paul@332 | 202 | } |
paul@332 | 203 | |
paul@332 | 204 | void Pager::populate_region(Flexpage *flexpage, Flexpage &masked, |
paul@332 | 205 | bool has_start, bool has_end) |
paul@332 | 206 | { |
paul@332 | 207 | /* Without any limits involved, provide a zero flexpage. */ |
paul@332 | 208 | |
paul@332 | 209 | if (!has_start && !has_end) |
paul@332 | 210 | { |
paul@332 | 211 | memset((void *) masked.region->start, 0, masked.size); |
paul@332 | 212 | return; |
paul@332 | 213 | } |
paul@332 | 214 | |
paul@332 | 215 | /* Determine the content limits given start and/or end offsets. */ |
paul@332 | 216 | |
paul@332 | 217 | offset_t start_offset = has_start ? _start_visible - masked.base_offset : 0; |
paul@332 | 218 | offset_t end_offset = has_end ? _end_visible - masked.base_offset : masked.size; |
paul@332 | 219 | |
paul@332 | 220 | if (has_start) |
paul@332 | 221 | memset((void *) masked.region->start, 0, start_offset); |
paul@332 | 222 | |
paul@332 | 223 | memcpy((void *) (masked.region->start + start_offset), |
paul@332 | 224 | (const void *) (flexpage->region->start + start_offset), |
paul@332 | 225 | end_offset - start_offset); |
paul@332 | 226 | |
paul@332 | 227 | if (has_end) |
paul@332 | 228 | memset((void *) (masked.region->start + end_offset), 0, masked.size - end_offset); |
paul@332 | 229 | } |
paul@332 | 230 | |
paul@6 | 231 | // vim: tabstop=4 expandtab shiftwidth=4 |