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