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