1 /* 2 * A system pager implementation residing in a separate task. 3 * 4 * Copyright (C) 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 <l4/re/env.h> 23 #include <l4/sys/task.h> 24 #include <l4/util/util.h> 25 26 #include <ipc/cap_alloc.h> 27 #include <ipc/map.h> 28 #include <ipc/mem_ipc.h> 29 #include <mem/memory_utils.h> 30 #include <systypes/base.h> 31 32 #include <stdio.h> 33 34 #include "external_pager.h" 35 #include "parent_pager_object_server.h" 36 37 38 39 #define DEBUG 0 40 41 42 43 /* A simple system pager also acting as a region mapper. */ 44 45 ExternalPager::ExternalPager(address_t start, address_t end) 46 : ExecPager(start, end) 47 { 48 } 49 50 ipc_server_default_config_type ExternalPager::config() 51 { 52 return config_ParentPagerObject; 53 } 54 55 56 57 /* Close the pager. */ 58 59 void ExternalPager::close() 60 { 61 #if DEBUG 62 printf("External pager closing...\n"); 63 #endif 64 65 /* Remove pager regions to avoid unmapping them twice. */ 66 67 remove(_rm_stack->region()); 68 69 for (unsigned int i = 0; i < _rm_payload->segments(); i++) 70 remove(_rm_payload->segment(i)->region()); 71 72 /* Delete the pager resources. */ 73 74 if (_rm_payload != NULL) 75 delete _rm_payload; 76 77 if (_rm_stack != NULL) 78 delete _rm_stack; 79 80 /* Unmap all remaining regions. */ 81 82 AvailableMemoryArea::iterator it; 83 84 for (it = _area.begin(); it != _area.end(); it++) 85 { 86 MemoryArea *r = *it; 87 88 if (r->is_mapped()) 89 ipc_detach_dataspace((void *) r->dataspace_start()); 90 } 91 92 /* Free all capabilities. */ 93 94 Capabilities::iterator itc; 95 96 for (itc = _dataspaces.begin(); itc != _dataspaces.end(); itc++) 97 ipc_cap_free_um(*itc); 98 99 /* Notify the monitor. */ 100 101 if (_monitor != NULL) 102 _monitor->pager_ended(); 103 } 104 105 106 107 /* Capability management. */ 108 109 void ExternalPager::set_pager(l4_cap_idx_t cap, l4_cap_idx_t mapped_cap) 110 { 111 _pager = cap; 112 _mapped_pager = mapped_cap; 113 } 114 115 void ExternalPager::set_parent(l4_cap_idx_t cap, l4_cap_idx_t mapped_cap) 116 { 117 _parent = cap; 118 _mapped_parent = mapped_cap; 119 } 120 121 void ExternalPager::set_task(l4_cap_idx_t cap, l4_cap_idx_t mapped_cap) 122 { 123 _task = cap; 124 _mapped_task = mapped_cap; 125 } 126 127 void ExternalPager::set_thread(l4_cap_idx_t cap, l4_cap_idx_t mapped_cap) 128 { 129 _thread = cap; 130 _mapped_thread = mapped_cap; 131 } 132 133 134 135 /* Lifecycle management. */ 136 137 void ExternalPager::set_monitor(ProcessMonitor *monitor) 138 { 139 _monitor = monitor; 140 } 141 142 143 144 /* Manage pager resources. */ 145 146 void ExternalPager::set_payload(Payload *payload) 147 { 148 _rm_payload = payload; 149 } 150 151 void ExternalPager::set_stack(ExplicitSegment *stack) 152 { 153 _rm_stack = stack; 154 } 155 156 157 158 /* Handle a general exception. */ 159 160 long ExternalPager::exception(l4_exc_regs_t regs, l4_snd_fpage_t *region) 161 { 162 (void) region; 163 164 printf("ExternalPager::exception(...) -> pfa = %lx, pc = %lx\n", l4_utcb_exc_pfa(®s), l4_utcb_exc_pc(®s)); 165 166 while (1) 167 l4_sleep_forever(); 168 169 return L4_EOK; 170 } 171 172 /* Handle a page fault using any configured regions. */ 173 174 long ExternalPager::page_fault(l4_umword_t pfa, l4_umword_t pc, l4_snd_fpage_t *region) 175 { 176 l4_umword_t addr = pfa & ~7UL, flags = pfa & 7; 177 178 #if DEBUG 179 printf("page_fault(%lx, %lx) -> %lx (%lx) -> ", pfa, pc, addr, flags); 180 #endif 181 182 /* Obtain a region supporting the fault address. */ 183 184 MemoryArea *r, *parent; 185 long err = _area.find(addr, &r, &parent); 186 187 if (!err) 188 { 189 l4_addr_t page_addr = trunc(addr, L4_PAGESIZE); 190 map_flags_t map_flags = map_flags_for_fault(flags); 191 192 region->fpage = l4_fpage(r->dataspace_start() + (page_addr - r->area_start()), L4_PAGESHIFT, map_flags & r->flags()); 193 region->snd_base = page_addr; 194 195 #if DEBUG 196 printf("%lx...%lx from %lx...%lx offset %lx size %d rights %x ds %lx\n", 197 r->area_start(), region->snd_base, 198 r->dataspace_start(), l4_fpage_memaddr(region->fpage), 199 addr - r->area_start(), 200 l4_fpage_size(region->fpage), 201 l4_fpage_rights(region->fpage), 202 r->dataspace()); 203 204 printf("%lx -> ", addr); 205 206 for (unsigned int i = 0; i < sizeof(l4_umword_t); i++) 207 printf("%02x", *((unsigned char *)(r->dataspace_start() + (addr - r->area_start()) + i))); 208 209 printf("\n"); 210 #endif 211 212 if (r->flags() & L4RE_RM_F_W) 213 l4_touch_rw((const void *) (r->dataspace_start() + (page_addr - r->area_start())), L4_PAGESIZE); 214 else 215 l4_touch_ro((const void *) (r->dataspace_start() + (page_addr - r->area_start())), L4_PAGESIZE); 216 217 return L4_EOK; 218 } 219 220 printf("ExternalPager: not mapped at %lx for pc %lx\n", addr, pc); 221 222 return err; 223 } 224 225 /* Attach a region for provision when page faults occur. This is required in 226 the initialisation of a program by the C library which requires a region 227 mapper. */ 228 229 long ExternalPager::attach(address_t *start, address_t size, map_flags_t flags, 230 l4_cap_idx_t ds, address_t offset, 231 unsigned char align) 232 { 233 // NOTE: Determine the purpose of offset. 234 235 (void) offset; 236 237 MemoryArea *area; 238 long err = ExecPager::find(start, &size, flags, align, &area); 239 240 /* Without an error, attach the dataspace. */ 241 242 if (!err) 243 { 244 /* Attach the provided dataspace within this task. 245 246 This is only done in this implementation to support the paging 247 mechanism. In a region mapper residing within the actual task, the 248 dataspace's map operation would be invoked to obtain mappings. */ 249 250 l4_addr_t ds_start; 251 err = ipc_attach_dataspace(ds, size, (void **) &ds_start); 252 253 if (err) 254 return err; 255 256 l4_touch_rw((const void *) ds_start, size); 257 258 MappedRegion r(*start, *start + size, flags & L4RE_DS_F_RIGHTS_MASK, ds, ds_start); 259 area->add(r); 260 261 /* Record dataspaces separately. */ 262 263 _dataspaces.insert(ds); 264 } 265 266 /* Discard the imported dataspace if its region cannot be accommodated. */ 267 268 else 269 ipc_cap_free_um(ds); 270 271 return err; 272 } 273 274 275 276 /* Receive signals from a task. */ 277 278 long ExternalPager::signal(unsigned long sig, unsigned long val) 279 { 280 (void) val; 281 282 /* Handle the termination event of the internal pager. */ 283 284 if (sig == 0) 285 { 286 printf("Signal from internal pager.\n"); 287 288 /* For some reason, threads cannot be released by the process, so they are 289 also unmapped on its behalf. */ 290 291 if (l4_is_valid_cap(_thread)) 292 { 293 ipc_cap_free_um(_thread); 294 ipc_unmap_capability(_task, _mapped_thread); 295 _thread = L4_INVALID_CAP; 296 297 /* Parent and pager/region mapper. Freeing the pager after the thread 298 should avoid warnings about invalid pager capabilities. */ 299 300 ipc_unmap_capability(_task, _mapped_parent); 301 ipc_unmap_capability(_task, _mapped_pager); 302 } 303 } 304 305 return L4_EOK; 306 } 307 308 /* vim: tabstop=2 expandtab shiftwidth=2 309 */