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