1 /* 2 * Support for executing code in new tasks and threads. 3 * 4 * Copyright (C) 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 <l4/re/env.h> 23 #include <l4/sys/err.h> 24 #include <l4/sys/factory.h> 25 #include <l4/sys/task.h> 26 #include <l4/sys/thread.h> 27 28 #include <fsclient/client.h> 29 #include <ipc/cap_alloc.h> 30 #include <ipc/mem_ipc.h> 31 #include <ipc/server.h> 32 #include <mem/memory_utils.h> 33 #include <systypes/fcntl.h> 34 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 39 #include <pthread-l4.h> 40 #include <pthread.h> 41 42 #include "system_pager_interface.h" 43 #include "system_pager_server.h" 44 45 46 47 /* A simple system pager. */ 48 49 class ExecPager : public SystemPager 50 { 51 public: 52 l4_addr_t buf, buf_start; 53 unsigned int buf_log2size; 54 55 virtual long exception(l4_exc_regs_t regs, 56 l4_snd_fpage_t *region); 57 58 virtual long page_fault(l4_umword_t pfa, l4_umword_t pc, 59 l4_snd_fpage_t *region); 60 }; 61 62 long ExecPager::exception(l4_exc_regs_t regs, l4_snd_fpage_t *region) 63 { 64 (void) region; 65 66 printf("exception(...) -> pfa = %lx, pc = %lx\n", l4_utcb_exc_pfa(®s), l4_utcb_exc_pc(®s)); 67 return L4_EOK; 68 } 69 70 long ExecPager::page_fault(l4_umword_t pfa, l4_umword_t pc, l4_snd_fpage_t *region) 71 { 72 l4_umword_t addr = pfa & ~7UL, flags = pfa & 7; 73 74 printf("page_fault(%lx, %lx) -> %lx (%lx)...\n", pfa, pc, addr, flags); 75 76 if ((addr >= buf_start) && (addr < buf_start + (1UL << buf_log2size))) 77 { 78 region->fpage = l4_fpage(buf, buf_log2size, L4_FPAGE_RX); 79 region->snd_base = buf_start; 80 81 return L4_EOK; 82 } 83 84 return -L4_ENOMEM; 85 } 86 87 88 89 static ExecPager exec_pager; 90 91 static void init_pager(ipc_server_config_type *config, l4_addr_t buf, 92 unsigned int buf_log2size, l4_addr_t buf_start) 93 { 94 exec_pager.buf = buf; 95 exec_pager.buf_log2size = buf_log2size; 96 exec_pager.buf_start = buf_start; 97 98 ipc_server_init_config(config); 99 100 config->expected_items = SystemPager_expected_items; 101 config->handler = (ipc_server_handler_type) handle_SystemPager; 102 config->handler_obj = static_cast<SystemPager *>(&exec_pager); 103 } 104 105 static long start_pager(ipc_server_config_type *config, pthread_t thread) 106 { 107 config->config_thread = 1; 108 config->thread = pthread_l4_cap(thread); 109 110 printf("Starting pager thread...\n"); 111 return ipc_server_start_config(config); 112 } 113 114 115 116 /* UTCB properties. 117 See: moe/server/src/loader.cc */ 118 119 enum ipc_exec_utcb 120 { 121 Default_max_threads = 16, 122 #ifdef ARCH_mips 123 Utcb_area_start = 0x73000000, 124 #else 125 Utcb_area_start = 0xb3000000, 126 #endif 127 }; 128 129 130 131 static offset_t find_region_size(offset_t size) 132 { 133 return 1 << page_order(size); 134 } 135 136 137 138 /* Capability mapping definitions for the new task. */ 139 140 struct mapped_cap 141 { 142 l4_cap_idx_t cap; 143 unsigned char rights; 144 l4_umword_t spot; 145 }; 146 147 static long map_capabilities(l4_cap_idx_t task, struct mapped_cap mapped_caps[]) 148 { 149 long err = L4_EOK; 150 int i = 0; 151 152 while (l4_is_valid_cap(mapped_caps[i].cap) && !err) 153 { 154 err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, 155 l4_obj_fpage(mapped_caps[i].cap, 0, mapped_caps[i].rights), 156 l4_map_obj_control(mapped_caps[i].spot, L4_MAP_ITEM_MAP))); 157 i++; 158 } 159 160 return err; 161 } 162 163 164 165 int main(int argc, char *argv[]) 166 { 167 long err; 168 169 if (argc < 2) 170 { 171 printf("Need a program to run.\n"); 172 return 1; 173 } 174 175 /* Allocate capabilities for the task and thread. */ 176 177 l4_cap_idx_t caps[2]; 178 179 err = ipc_cap_alloc_many(caps, 2); 180 181 if (err) 182 { 183 printf("Could not allocate capabilities.\n"); 184 return 1; 185 } 186 187 l4_cap_idx_t &task = caps[0]; 188 l4_cap_idx_t &thread = caps[1]; 189 190 /* Obtain the payload as a dataspace. */ 191 192 file_t *file = client_open(argv[1], O_RDONLY); 193 194 if (file == NULL) 195 { 196 printf("Could not read file: %s\n", argv[1]); 197 return 1; 198 } 199 200 /* Copy the entire payload to a new dataspace. */ 201 202 char *buf; 203 offset_t nread; 204 offset_t region_size = find_region_size(4000000); 205 l4re_ds_t region_ds; 206 207 err = ipc_allocate_align(region_size, L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RWX, 208 page_order(4000000), (void **) &buf, ®ion_ds); 209 210 if (err) 211 { 212 printf("Could not reserve memory.\n"); 213 return 1; 214 } 215 216 printf("Reading from file into %p.\n", buf); 217 218 nread = client_read(file, buf, 4000000); 219 220 printf("Read %ld from file.\n", nread); 221 222 if (memcmp(buf + 0xae3, "\x31\xed", 2)) 223 { 224 printf("Did not find expected instructions at start.\n"); 225 return 1; 226 } 227 228 /* UTCB location and size. */ 229 230 l4_addr_t utcb_start = Utcb_area_start; 231 int utcb_log2size = page_order(Default_max_threads * L4_UTCB_OFFSET); 232 233 /* Round up to at least one page. */ 234 235 if (utcb_log2size < L4_PAGESHIFT) 236 utcb_log2size = L4_PAGESHIFT; 237 238 /* KIP allocation. */ 239 240 l4_addr_t kip_start = (l4_addr_t) l4re_kip(); 241 242 printf("KIP at %lx.\n", kip_start); 243 244 /* Create a new task and thread. */ 245 246 l4_fpage_t utcb_fpage = l4_fpage(utcb_start, utcb_log2size, 0); 247 248 err = l4_error(l4_factory_create_task(l4re_env()->factory, task, utcb_fpage)); 249 250 if (err) 251 { 252 printf("Could not create task.\n"); 253 return 1; 254 } 255 256 err = l4_error(l4_factory_create_thread(l4re_env()->factory, thread)); 257 258 if (err) 259 { 260 printf("Could not create thread.\n"); 261 return 1; 262 } 263 264 /* Start the pager. */ 265 266 ipc_server_config_type config; 267 pthread_t pager_thread; 268 pthread_attr_t attr; 269 270 pthread_attr_init(&attr); 271 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 272 273 init_pager(&config, (l4_addr_t) buf, page_order(file->size), 0x1000000); 274 275 err = pthread_create(&pager_thread, &attr, ipc_server_start_mainloop, &config); 276 277 if (err) 278 { 279 printf("Could not start pager thread.\n"); 280 return 1; 281 } 282 283 err = start_pager(&config, pager_thread); 284 285 if (err) 286 { 287 printf("Could not start pager.\n"); 288 return 1; 289 } 290 291 /* Map the pager capability into the region manager/mapper slot. */ 292 293 err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, 294 l4_obj_fpage(config.server, 0, L4_CAP_FPAGE_RWS), 295 l4_map_obj_control(0x10 << L4_CAP_SHIFT, L4_MAP_ITEM_MAP))); 296 297 if (err) 298 { 299 printf("Could not map pager capability into task.\n"); 300 return 1; 301 } 302 303 /* Map the KIP into the task. */ 304 305 err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, 306 l4_fpage(kip_start, L4_PAGESHIFT, L4_FPAGE_RX), 307 kip_start)); 308 309 if (err) 310 { 311 printf("Could not map KIP into task.\n"); 312 return 1; 313 } 314 315 /* Define capability mappings for the new task. */ 316 317 struct mapped_cap mapped_caps[] = { 318 {task, L4_CAP_FPAGE_RWS, L4_BASE_TASK_CAP}, 319 {thread, L4_CAP_FPAGE_RWS, L4_BASE_THREAD_CAP}, 320 {l4re_env()->factory, L4_CAP_FPAGE_RWS, L4_BASE_FACTORY_CAP}, 321 {l4re_env()->log, L4_CAP_FPAGE_RWS, L4_BASE_LOG_CAP}, 322 {l4re_env()->scheduler, L4_CAP_FPAGE_RWS, L4_BASE_SCHEDULER_CAP}, 323 {L4_INVALID_CAP, 0, 0}, 324 }; 325 326 err = map_capabilities(task, mapped_caps); 327 328 if (err) 329 { 330 printf("Could not capabilities into task.\n"); 331 return 1; 332 } 333 334 /* Configure the thread with the region manager acting as pager and exception 335 handler. The UTCB will be situated at an address supported by a dataspace 336 attached to the new task. */ 337 338 printf("Configure thread...\n"); 339 340 l4_thread_control_start(); 341 l4_thread_control_pager(0x10 << L4_CAP_SHIFT); 342 l4_thread_control_exc_handler(0x10 << L4_CAP_SHIFT); 343 l4_thread_control_bind((l4_utcb_t *) utcb_start, task); 344 err = l4_error(l4_thread_control_commit(thread)); 345 346 if (err) 347 { 348 printf("Could not configure thread.\n"); 349 return 1; 350 } 351 352 /* Map the payload into the new task. */ 353 354 printf("Map %p with size %ld (2 ** %d).\n", buf, file->size, page_order(file->size)); 355 356 l4_fpage_t payload_fpage = l4_fpage((l4_addr_t) buf, page_order(file->size), L4_FPAGE_RX); 357 358 err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, payload_fpage, 0x1000000)); 359 360 if (err) 361 { 362 printf("Could not map payload into task.\n"); 363 return 1; 364 } 365 366 /* Start the new thread. */ 367 368 printf("Schedule thread...\n"); 369 370 err = l4_error(l4_thread_ex_regs(thread, 0x1000ae3, 0x2000000 /* stack top */, 0)); 371 372 if (err) 373 { 374 printf("Could not set thread registers.\n"); 375 return 1; 376 } 377 378 printf("Run thread...\n"); 379 380 l4_sched_param_t sp = l4_sched_param(L4RE_MAIN_THREAD_PRIO); 381 382 err = l4_error(l4_scheduler_run_thread(l4re_env()->scheduler, thread, &sp)); 383 384 if (err) 385 { 386 printf("Could not run thread.\n"); 387 return 1; 388 } 389 390 printf("Finished.\n"); 391 while (1); 392 393 return 0; 394 } 395 396 /* vim: tabstop=2 expandtab shiftwidth=2 397 */