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