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 #include <l4/util/bitops.h> /* l4util_log2 */ 28 29 #include <fsclient/client.h> 30 #include <ipc/cap_alloc.h> 31 #include <ipc/mem_ipc.h> 32 #include <ipc/server.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 int find_region_exp(offset_t size) 132 { 133 int exp = l4util_log2(size); 134 135 if ((1UL << exp) < size) 136 return exp + 1; 137 else 138 return exp; 139 } 140 141 static offset_t find_region_size(offset_t size) 142 { 143 return 1 << find_region_exp(size); 144 } 145 146 int main(int argc, char *argv[]) 147 { 148 long err; 149 150 if (argc < 2) 151 { 152 printf("Need a program to run.\n"); 153 return 1; 154 } 155 156 /* Allocate capabilities for the task and thread. */ 157 158 l4_cap_idx_t task, thread; 159 160 task = ipc_cap_alloc(); 161 162 if (l4_is_invalid_cap(task)) 163 { 164 printf("Could not allocate task.\n"); 165 return 1; 166 } 167 168 thread = ipc_cap_alloc(); 169 170 if (l4_is_invalid_cap(thread)) 171 { 172 printf("Could not allocate thread.\n"); 173 return 1; 174 } 175 176 /* Obtain the payload as a dataspace. */ 177 178 file_t *file = client_open(argv[1], O_RDONLY); 179 180 if (file == NULL) 181 { 182 printf("Could not read file: %s\n", argv[1]); 183 return 1; 184 } 185 186 /* Copy the entire payload to a new dataspace. */ 187 188 char *buf; 189 offset_t nread; 190 offset_t region_size = find_region_size(4000000); 191 l4re_ds_t region_ds; 192 193 err = ipc_allocate_align(region_size, L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RWX, 194 find_region_exp(4000000), (void **) &buf, ®ion_ds); 195 196 if (err) 197 { 198 printf("Could not reserve memory.\n"); 199 return 1; 200 } 201 202 printf("Reading from file into %p.\n", buf); 203 204 nread = client_read(file, buf, 4000000); 205 206 printf("Read %ld from file.\n", nread); 207 208 if (memcmp(buf + 0xae3, "\x31\xed", 2)) 209 { 210 printf("Did not find expected instructions at start.\n"); 211 return 1; 212 } 213 214 /* UTCB location and size. */ 215 216 l4_addr_t utcb_start = Utcb_area_start; 217 int utcb_log2size = find_region_exp(Default_max_threads * L4_UTCB_OFFSET); 218 219 /* Round up to at least one page. */ 220 221 if (utcb_log2size < L4_PAGESHIFT) 222 utcb_log2size = L4_PAGESHIFT; 223 224 /* KIP allocation. */ 225 226 l4_addr_t kip_start = (l4_addr_t) l4re_kip(); 227 228 printf("KIP at %lx.\n", kip_start); 229 230 /* Create a new task and thread. */ 231 232 l4_fpage_t utcb_fpage = l4_fpage(utcb_start, utcb_log2size, 0); 233 234 err = l4_error(l4_factory_create_task(l4re_env()->factory, task, utcb_fpage)); 235 236 if (err) 237 { 238 printf("Could not create task.\n"); 239 return 1; 240 } 241 242 err = l4_error(l4_factory_create_thread(l4re_env()->factory, thread)); 243 244 if (err) 245 { 246 printf("Could not create thread.\n"); 247 return 1; 248 } 249 250 /* Start the pager. */ 251 252 ipc_server_config_type config; 253 pthread_t pager_thread; 254 pthread_attr_t attr; 255 256 pthread_attr_init(&attr); 257 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 258 259 init_pager(&config, (l4_addr_t) buf, find_region_exp(file->size), 0x1000000); 260 261 err = pthread_create(&pager_thread, &attr, ipc_server_start_mainloop, &config); 262 263 if (err) 264 { 265 printf("Could not start pager thread.\n"); 266 return 1; 267 } 268 269 err = start_pager(&config, pager_thread); 270 271 if (err) 272 { 273 printf("Could not start pager.\n"); 274 return 1; 275 } 276 277 /* Map the pager capability into the region manager/mapper slot. */ 278 279 err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, 280 l4_obj_fpage(config.server, 0, L4_CAP_FPAGE_RWS), 281 l4_map_obj_control(0x10 << L4_CAP_SHIFT, L4_MAP_ITEM_MAP))); 282 283 if (err) 284 { 285 printf("Could not map pager capability into task.\n"); 286 return 1; 287 } 288 289 /* Map the KIP into the task. */ 290 291 err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, 292 l4_fpage(kip_start, L4_PAGESHIFT, L4_FPAGE_RX), 293 kip_start)); 294 295 if (err) 296 { 297 printf("Could not map KIP into task.\n"); 298 return 1; 299 } 300 301 err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, 302 l4_obj_fpage(task, 0, L4_CAP_FPAGE_RWS), 303 l4_map_obj_control(L4_BASE_TASK_CAP, L4_MAP_ITEM_MAP))); 304 305 if (err) 306 { 307 printf("Could not map task capability into task.\n"); 308 return 1; 309 } 310 311 err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, 312 l4_obj_fpage(thread, 0, L4_CAP_FPAGE_RWS), 313 l4_map_obj_control(L4_BASE_THREAD_CAP, L4_MAP_ITEM_MAP))); 314 315 if (err) 316 { 317 printf("Could not map thread capability into task.\n"); 318 return 1; 319 } 320 321 err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, 322 l4_obj_fpage(l4re_env()->factory, 0, L4_CAP_FPAGE_RWS), 323 l4_map_obj_control(L4_BASE_FACTORY_CAP, L4_MAP_ITEM_MAP))); 324 325 if (err) 326 { 327 printf("Could not map factory capability into task.\n"); 328 return 1; 329 } 330 331 err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, 332 l4_obj_fpage(l4re_env()->log, 0, L4_CAP_FPAGE_RWS), 333 l4_map_obj_control(L4_BASE_LOG_CAP, L4_MAP_ITEM_MAP))); 334 335 if (err) 336 { 337 printf("Could not map log capability into task.\n"); 338 return 1; 339 } 340 341 err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, 342 l4_obj_fpage(l4re_env()->scheduler, 0, L4_CAP_FPAGE_RWS), 343 l4_map_obj_control(L4_BASE_SCHEDULER_CAP, L4_MAP_ITEM_MAP))); 344 345 if (err) 346 { 347 printf("Could not map scheduler capability into task.\n"); 348 return 1; 349 } 350 351 /* Configure the thread with the region manager acting as pager and exception 352 handler. The UTCB will be situated at an address supported by a dataspace 353 attached to the new task. */ 354 355 printf("Configure thread...\n"); 356 357 l4_thread_control_start(); 358 l4_thread_control_pager(0x10 << L4_CAP_SHIFT); 359 l4_thread_control_exc_handler(0x10 << L4_CAP_SHIFT); 360 l4_thread_control_bind((l4_utcb_t *) utcb_start, task); 361 err = l4_error(l4_thread_control_commit(thread)); 362 363 if (err) 364 { 365 printf("Could not configure thread.\n"); 366 return 1; 367 } 368 369 /* Map the payload into the new task. */ 370 371 printf("Map %p with size %ld (2 ** %d).\n", buf, file->size, find_region_exp(file->size)); 372 373 l4_fpage_t payload_fpage = l4_fpage((l4_addr_t) buf, find_region_exp(file->size), L4_FPAGE_RX); 374 375 err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, payload_fpage, 0x1000000)); 376 377 if (err) 378 { 379 printf("Could not map payload into task.\n"); 380 return 1; 381 } 382 383 /* Start the new thread. */ 384 385 printf("Schedule thread...\n"); 386 387 err = l4_error(l4_thread_ex_regs(thread, 0x1000ae3, 0x2000000 /* stack top */, 0)); 388 389 if (err) 390 { 391 printf("Could not set thread registers.\n"); 392 return 1; 393 } 394 395 printf("Run thread...\n"); 396 397 l4_sched_param_t sp = l4_sched_param(L4RE_MAIN_THREAD_PRIO); 398 399 err = l4_error(l4_scheduler_run_thread(l4re_env()->scheduler, thread, &sp)); 400 401 if (err) 402 { 403 printf("Could not run thread.\n"); 404 return 1; 405 } 406 407 printf("Finished.\n"); 408 while (1); 409 410 return 0; 411 } 412 413 /* vim: tabstop=2 expandtab shiftwidth=2 414 */