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