1 /* 2 * Generic pager functionality. 3 * 4 * Copyright (C) 2021, 2022, 2023 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 <mem/ipc.h> 23 24 #include "dataspace_server.h" 25 #include "pager.h" 26 27 #include "copied_page_mapper.h" 28 #include "masked_page_mapper.h" 29 30 31 32 /* Initialise the pager with a page mapper and the given flags controlling 33 access to a file. */ 34 35 Pager::Pager(GenericPageMapper *mapper, map_flags_t map_flags) 36 : _start(0), _size(0), 37 _mapper(mapper), _mapper_base(mapper), _mapper_masked(NULL), 38 _mapper_copied(NULL), 39 _map_flags(map_flags) 40 { 41 } 42 43 /* Close the pager. */ 44 45 void Pager::close() 46 { 47 if (_mapper_masked != NULL) 48 { 49 delete _mapper_masked; 50 _mapper_masked = NULL; 51 } 52 53 if (_mapper_copied != NULL) 54 { 55 delete _mapper_copied; 56 _mapper_copied = NULL; 57 } 58 } 59 60 /* Flush data to the file. */ 61 62 long Pager::flush(offset_t populated_size, offset_t *size) 63 { 64 _mapper->flush_all(_start, populated_size); 65 66 *size = _mapper->get_data_size(); 67 return L4_EOK; 68 } 69 70 /* Resize the file. This will only resize the underlying file where masking and 71 copy-on-write mapping is not being performed. */ 72 73 long Pager::resize(offset_t *size) 74 { 75 _mapper->set_data_size(*size); 76 77 *size = _mapper->get_data_size(); 78 return L4_EOK; 79 } 80 81 /* Expose a region of the file. */ 82 83 long Pager::mmap(offset_t position, offset_t length, 84 offset_t start_visible, offset_t end_visible, 85 offset_t *start_pos, offset_t *end_pos, offset_t *size) 86 { 87 /* Define region characteristics. */ 88 89 _start = trunc(position, PAGE_SIZE); 90 _size = round(position + length, PAGE_SIZE) - _start; 91 92 /* Return the start and end positions plus file size. */ 93 94 *start_pos = _start; 95 *end_pos = _start + _size; 96 *size = _mapper_base->get_data_size(); 97 98 /* Permit masking of mapped regions. */ 99 100 if ((start_visible || end_visible) && 101 ((*start_pos != start_visible) || (*end_pos != end_visible))) 102 { 103 /* Introduce the masked page and copied page mappers. */ 104 105 _mapper_masked = new MaskedPageMapper(_mapper_base, start_visible, end_visible); 106 _mapper_copied = new CopiedPageMapper(_mapper_masked); 107 _mapper = _mapper_copied; 108 } 109 else 110 _mapper = _mapper_base; 111 112 return L4_EOK; 113 } 114 115 /* Map a flexpage corresponding to the dataspace 'offset' involving a 'hot_spot' 116 (flexpage offset). */ 117 118 long Pager::map(offset_t offset, map_address_t hot_spot, map_flags_t map_flags, 119 l4_snd_fpage_t *region) 120 { 121 offset_t file_offset = _start + offset; 122 offset_t max_offset = _start + _size; 123 124 /* Prevent access beyond that defined by the pager. 125 NOTE: Permitting executable requests here. This needs to be configured 126 when opening the pager or by another means. */ 127 128 if (map_flags & (~(_map_flags | L4RE_DS_F_X))) 129 return -L4_EACCESS; 130 131 /* Obtain a flexpage from the page mapper. */ 132 133 Flexpage *flexpage = _mapper->get(file_offset, map_flags); 134 135 /* Issue the flexpage via the IPC system. */ 136 137 long err = ipc_prepare_flexpage(flexpage, file_offset, max_offset, 138 hot_spot, map_flags, region); 139 140 if (!err) 141 err = complete_Dataspace_map(*region); 142 143 /* After the obtained flexpage is issued, it is queued for future reuse, if 144 appropriate for the mapper concerned. */ 145 146 _mapper->queue(flexpage); 147 148 if (err) 149 return err; 150 151 return IPC_MESSAGE_SENT; 152 } 153 154 /* Return the total size of the data. */ 155 156 offset_t Pager::get_data_size() 157 { 158 return _mapper->get_data_size(); 159 } 160 161 // vim: tabstop=4 expandtab shiftwidth=4