1/* 2 * Copyright 2016, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(D61_BSD) 11 */ 12 13/*! @file 14 @brief Process selfloader system application for RefOS. 15 16 Selfloader is a tiny system application that starts in the same address space as the application 17 being boosted. Selfloader then acts on behalf of the starting process, setting up its vspace and 18 loading the ELF segments before jumping into the entry point of the process. In order to avoid 19 vspace address conflicts with the booting process, the Sselfloader process linker script compiles 20 it into a very high RefOS reserved region, away from the vspace of the booting process. 21 22 See \ref Startup 23*/ 24 25#include <string.h> 26#include "selfloader.h" 27 28#include <elf/elf.h> 29#include <refos/refos.h> 30#include <refos/share.h> 31#include <refos/vmlayout.h> 32#include <refos-util/init.h> 33#include <refos-util/walloc.h> 34#include <refos-io/morecore.h> 35#include <refos-io/stdio.h> 36#include <utils/arith.h> 37 38/*! Global self-loader state. */ 39struct sl_state selfloaderState; 40const char* dprintfServerName = "SELFLOADER"; 41int dprintfServerColour = 36; 42 43/*! A little mini morecore region for the selfloader, since the libraries selfloader use would 44 like to have malloc() available. */ 45static char slMiniMorecoreRegion[SELFLOADER_MINI_MORECORE_REGION_SIZE]; 46 47/*! The maximum length of the ELF header we support. */ 48#define SELFLOADER_ELF_HEADER_SIZE REFOS_PAGE_SIZE 49 50/*! @brief Selfloader's system call table. */ 51extern uintptr_t __vsyscall_ptr; 52 53/*! @brief The spawned process uses this system call table */ 54extern long (*syscall_table[])(va_list); 55 56// TODO(xmc): standardise this with a macro. 57/*! Helper function to round up to the next page boundary. */ 58static int 59sl_roundup_page(int addr) 60{ 61 return REFOS_PAGE_ALIGN(addr) + ((addr) % REFOS_PAGE_SIZE ? REFOS_PAGE_SIZE : 0); 62} 63 64/*! @brief Open the ELF header dataspace, and map it so we can read it. 65 @param fsSession Resolved mount point and session of ELF header dataspace. 66 @return ESUCCESS on success, refos_err_t otherwise. 67 */ 68static int 69sl_setup_elf_header(serv_connection_t* fsSession) 70{ 71 assert(fsSession && fsSession->serverSession); 72 assert(fsSession->serverMountPoint.success); 73 74 dvprintf("Opening file [%s] on mountpoint [%s]..\n", 75 fsSession->serverMountPoint.dspaceName, 76 fsSession->serverMountPoint.nameservPathPrefix); 77 78 selfloaderState.elfFileHeader = data_open_map(fsSession->serverSession, 79 fsSession->serverMountPoint.dspaceName, 0, 0, SELFLOADER_ELF_HEADER_SIZE, -1); 80 if (selfloaderState.elfFileHeader.err != ESUCCESS) { 81 ROS_ERROR("sl_setup_elf_header failed to open file."); 82 return selfloaderState.elfFileHeader.err; 83 } 84 return ESUCCESS; 85} 86 87/*! @brief Helper function to map out a zero vspace segment. Simply opens an anonymous dataspace 88 and maps it at the right vaddr. 89 @param start The start / base of the zero segment region window. 90 @param size The size of the zero segment dataspace. 91 @param windowSize The size of zero segment region window. Could be slightly different to the 92 dataspace region, due to page alignment. 93 @param out[out] Optional output struct to store windows & dataspace capabilities. If this is 94 set to NULL, the created window and dataspace caps are simply used then 95 discarded. 96 @return ESUCCESS on success, refos_err_t otherwise. 97 */ 98static int 99sl_create_zero_segment(seL4_Word start, seL4_Word size, seL4_Word windowSize, sl_dataspace_t *out) 100{ 101 int error = EINVALID; 102 103 sl_dataspace_t *elfSegment = out; 104 if (!elfSegment) { 105 elfSegment = &selfloaderState.elfSegment; 106 } 107 assert(!elfSegment->dataspace); 108 assert(!elfSegment->window); 109 110 dprintf("Loading zero segment 0x%08x --> 0x%08x \n", start, start + windowSize); 111 112 /* Open a anon ram dataspace on procserv. */ 113 dvprintf(" Creating zero segment dataspace ...\n"); 114 elfSegment->dataspace = data_open(REFOS_PROCSERV_EP, "anon", 0, 0, size, &error); 115 if (error != ESUCCESS) { 116 ROS_ERROR("Failed to open zero segment anon dspace."); 117 return error; 118 } 119 120 /* Create the zero-initalised window for this segment. */ 121 dvprintf(" Creating zero segment window ...\n"); 122 elfSegment->window = proc_create_mem_window(start, windowSize); 123 if (!elfSegment->window || ROS_ERRNO() != ESUCCESS) { 124 ROS_ERROR("Failed to create ELF zero segment window."); 125 return error; 126 } 127 128 /* Map the zero dataspace into the window. */ 129 dvprintf(" Data mapping zero segment ...\n"); 130 error = data_datamap(REFOS_PROCSERV_EP, elfSegment->dataspace, elfSegment->window, 0); 131 if (error != ESUCCESS) { 132 ROS_ERROR("Failed to datamap ELF zero segment."); 133 return error; 134 } 135 136 elfSegment->vaddr = (uint32_t) start; 137 elfSegment->size = (uint32_t) windowSize; 138 if (!out) { 139 /* Delete our caps as we no longer need them. */ 140 csfree_delete(elfSegment->dataspace); 141 csfree_delete(elfSegment->window); 142 elfSegment->dataspace = 0; 143 elfSegment->window = 0; 144 elfSegment->vaddr = 0; 145 elfSegment->size = 0; 146 } 147 148 return ESUCCESS; 149} 150 151/*! @brief Load an ELF segment region into current vspace. 152 @param si The ELF segment infor structure, read from the ELF header. 153 @param fsSession The dataserver session containing ELF file contents. 154 @return ESUCCESS on success, refos_err_t otherwise. 155*/ 156static int 157sl_elf_load_region(struct sl_elf_segment_info si, serv_connection_t* fsSession) 158{ 159 int error; 160 assert(fsSession); 161 162 dprintf("Loading elf segment 0x%08x --> 0x%08x \n", 163 (unsigned int)si.vaddr, (unsigned int)(si.vaddr + si.segmentSize)); 164 165 data_mapping_t *elfFile = &selfloaderState.elfFileHeader; 166 sl_dataspace_t *elfSegment = &selfloaderState.elfSegment; 167 assert(elfFile->err == ESUCCESS && elfFile->vaddr != NULL); 168 assert(elfSegment->dataspace == 0 && elfSegment->window == 0); 169 170 /* Calculate the page-alignment correction offset. */ 171 seL4_Word alignCorrectionOffset = si.vaddr - REFOS_PAGE_ALIGN(si.vaddr); 172 if (alignCorrectionOffset > si.source) { 173 ROS_ERROR("Invalid elf segment vaddr."); 174 return EINVALID; 175 } 176 177 /* Open an anon ram dataspace on procserv. */ 178 dvprintf(" Opening dataspace...\n"); 179 elfSegment->dataspace = data_open(REFOS_PROCSERV_EP, "anon", 0, 0, si.fileSize, &error); 180 if (error != ESUCCESS) { 181 ROS_ERROR("Failed to open ELF segment anon dataspace."); 182 return error; 183 } 184 185 /* Initialise segment content with ELF content. */ 186 dvprintf(" Initialising dataspace contents...\n"); 187 error = data_init_data(fsSession->serverSession, elfSegment->dataspace, elfFile->dataspace, 188 si.source - alignCorrectionOffset); 189 if (error) { 190 ROS_ERROR("Failed to init data for ELF segment."); 191 return error; 192 } 193 194 /* Calculate the page-aligned window end position. */ 195 int windowEnd = sl_roundup_page(si.vaddr + si.fileSize); 196 assert(windowEnd > si.vaddr && si.vaddr + si.fileSize <= windowEnd); 197 int windowSize = windowEnd - si.vaddr; 198 assert(((si.vaddr + windowSize) % REFOS_PAGE_SIZE) == 0); 199 200 /* Create the file-initialised window for this data initialised segment anon dspace. */ 201 dvprintf(" Creating memory window ..."); 202 elfSegment->window = proc_create_mem_window(REFOS_PAGE_ALIGN(si.vaddr), windowSize); 203 if (!elfSegment->window || ROS_ERRNO() != ESUCCESS) { 204 ROS_ERROR("Failed to create ELF segment window."); 205 return ROS_ERRNO(); 206 } 207 208 /* Map the correct region of the file into this segment. */ 209 dvprintf(" Mapping dataspace ...\n"); 210 error = data_datamap(REFOS_PROCSERV_EP, elfSegment->dataspace, elfSegment->window, 0); 211 if (error) { 212 ROS_ERROR("Failed to datamap ELF initialised segment anon dataspace."); 213 return error; 214 } 215 216 /* Clean up the capabilities to this segment so we can re-use the structure. */ 217 csfree_delete(elfSegment->window); 218 csfree_delete(elfSegment->dataspace); 219 elfSegment->window = 0; 220 elfSegment->dataspace = 0; 221 222 if (si.segmentSize < si.fileSize) { 223 ROS_ERROR("Invalid segment size in ELF file. Check for corruption."); 224 return EINVALID; 225 } 226 if (windowSize >= si.segmentSize) { 227 return ESUCCESS; 228 } 229 230 /* Fill out the remaining un-initialised portion of the segment with a zero dataspace. */ 231 seL4_Word zeroSegment = si.segmentSize - windowSize; 232 assert(zeroSegment > 0); 233 zeroSegment = sl_roundup_page(zeroSegment); 234 error = sl_create_zero_segment(si.vaddr + windowSize, zeroSegment, zeroSegment, NULL); 235 if (error) { 236 return error; 237 } 238 239 return ESUCCESS; 240} 241 242/*! @brief Load given ELF file into the current vspace. 243 @param file Pointer to mapped ELF header contents. 244 @param fsSession The dataserver session containing the ELF tile. 245 @return ESUCCESS on success, refos_err_t otherwise. 246*/ 247static int 248sl_elf_load(char *file, serv_connection_t* fsSession) 249{ 250 int error; 251 assert(fsSession); 252 253 /* Ensure that the ELF file looks sane. */ 254 error = elf_checkFile(file); 255 if (error) { 256 ROS_ERROR("ELF header check error."); 257 return error; 258 } 259 260 /* Look at header and loop through all segments. */ 261 struct sl_elf_segment_info si = {}; 262 int numHeaders = elf_getNumProgramHeaders(file); 263 for (int i = 0; i < numHeaders; i++) { 264 /* Skip non-loadable segments (such as debugging data). */ 265 if (elf_getProgramHeaderType(file, i) != PT_LOAD) { 266 continue; 267 } 268 269 /* Fetch information about this segment. */ 270 si.source = elf_getProgramHeaderOffset(file, i); 271 si.fileSize = elf_getProgramHeaderFileSize(file, i); 272 si.segmentSize = elf_getProgramHeaderMemorySize(file, i); 273 si.vaddr = elf_getProgramHeaderVaddr(file, i); 274 si.flags = elf_getProgramHeaderFlags(file, i); 275 276 error = sl_elf_load_region(si, fsSession); 277 if (error) { 278 ROS_ERROR("Failed to load ELF region."); 279 return error; 280 } 281 } 282 283 /* Save the end of the program. */ 284 selfloaderState.endOfProgram = si.vaddr + si.segmentSize; 285 selfloaderState.heapRegionStart = REFOS_PAGE_ALIGN(selfloaderState.endOfProgram + 0x2800); 286 287 /* Create and map zeroed stack segment. */ 288 error = sl_create_zero_segment(PROCESS_STACK_BOT, PROCESS_RLIMIT_STACK, PROCESS_RLIMIT_STACK, 289 &selfloaderState.stackRegion); 290 if (error) { 291 return error; 292 } 293 294 /* Create and map zeroed heap segment. */ 295 error = sl_create_zero_segment(selfloaderState.heapRegionStart, PROCESS_HEAP_INITIAL_SIZE, 296 PROCESS_HEAP_INITIAL_SIZE, &selfloaderState.heapRegion); 297 if (error) { 298 return error; 299 } 300 301 return ESUCCESS; 302} 303 304/*! @brief Load boot & ELF information into static address. 305 Load information about the process and its ELF regions into the pre-allocated static 306 buffer, for the process to read after we jump into it. This done so the process knows where 307 its own ELF segment ends, and where its heap region can start. 308*/ 309static void 310sl_setup_bootinfo_buffer(void) 311{ 312 struct sl_procinfo_s *procInfo = refos_static_param_procinfo(); 313 procInfo->magic = SELFLOADER_PROCINFO_MAGIC; 314 procInfo->elfSegmentEnd = (uint32_t) selfloaderState.endOfProgram; 315 procInfo->heapRegion = selfloaderState.heapRegion; 316 procInfo->stackRegion = selfloaderState.stackRegion; 317} 318 319/*! @brief Push onto the stack. 320 @param stack_top The current top of the stack. 321 @param buf The buffer to be pushed onto the stack. 322 @param n The size of the buffer to be pushed onto the stack. 323 @return The new top of the stack. 324*/ 325static uintptr_t stack_write(uintptr_t stack_top, void *buf, size_t n) 326{ 327 uintptr_t new_stack_top = stack_top - n; 328 memcpy((void*)new_stack_top, buf, n); 329 return new_stack_top; 330} 331 332/*! @brief Push a constant onto the stack. 333 @param stack_top The current top of the stack. 334 @param value The value to be pushed onto the stack. 335 @return The new top of the stack. 336*/ 337static uintptr_t stack_write_constant(uintptr_t stack_top, long value) { 338 return stack_write(stack_top, &value, sizeof(value)); 339} 340 341/*! @brief Push arguments onto the stack. 342 @param stack_top The current top of the stack. 343 @param data_len The number of arguments to push to the stack. 344 @param dest_data Pointer to the arguments to be pushed to the stack. 345 @param dest_data When the function returns contains each of the arguments to be pushed. 346 @return The new top of the stack. 347*/ 348static uintptr_t stack_copy_args(uintptr_t stack_top, int data_len, char *data[], uintptr_t *dest_data) { 349 uintptr_t new_stack_top = stack_top; 350 for (int i = 0; i < data_len; i++) { 351 new_stack_top = stack_write(new_stack_top, data[i], strlen(data[i]) + 1); 352 dest_data[i] = new_stack_top; 353 new_stack_top = ROUND_DOWN(new_stack_top, 4); 354 } 355 return new_stack_top; 356} 357 358/*! @brief Initialise the stack for how musllibc expects it. 359 @param elf The mapped ELF header containing entry point to jump into. 360 @param stack_top The current top of the stack. 361 @return The new top of the stack. 362*/ 363static uintptr_t system_v_init(char *elf, uintptr_t stack_top) 364{ 365 uintptr_t sysinfo = __vsyscall_ptr; 366 void *ipc_buffer = seL4_GetIPCBuffer(); 367 368 char ipc_buf_env[ENV_STR_SIZE]; 369 snprintf(ipc_buf_env, ENV_STR_SIZE, "IPCBUFFER=%p", ipc_buffer); 370 371 /* Future Work 3: 372 How the selfloader bootstraps user processes needs to be modified further. Changes were 373 made to accomodate the new way that muslc expects process's stacks to be set up when 374 processes start, but the one part of this that still needs to changed is how user processes 375 find their system call table. Currently the selfloader sets up user processes so that 376 the selfloader's system call table is used by user processes by passing the address of the 377 selfloader's system call table to the user processes via the user process's environment 378 variables. Ideally, user processes would use their own system call table. 379 */ 380 381 char system_call_table_env[ENV_STR_SIZE]; 382 snprintf(system_call_table_env, ENV_STR_SIZE, "SYSTABLE=%x", (unsigned int) syscall_table); 383 384 char *envp[] = {ipc_buf_env, system_call_table_env}; 385 int envc = 2; 386 uintptr_t dest_envp[envc]; 387 Elf_auxv_t auxv[] = { {.a_type = AT_SYSINFO, .a_un = {sysinfo} } }; 388 389 stack_top = stack_copy_args(stack_top, envc, envp, dest_envp); 390 391 size_t stack_size = 5 * sizeof(seL4_Word) + /* constants */ 392 sizeof(dest_envp) + /* aux */ 393 sizeof(auxv[0]); /* env */ 394 395 uintptr_t future_stack_top = stack_top - stack_size; 396 uintptr_t rounded_future_stack_top = ROUND_DOWN(future_stack_top, sizeof(seL4_Word) * 2); 397 ptrdiff_t stack_rounding = future_stack_top - rounded_future_stack_top; 398 stack_top -= stack_rounding; 399 400 /* NULL terminate aux */ 401 stack_top = stack_write_constant(stack_top, 0); 402 stack_top = stack_write_constant(stack_top, 0); 403 404 /* Write aux */ 405 stack_top = stack_write(stack_top, auxv, sizeof(auxv[0])); 406 407 /* NULL terminate env */ 408 stack_top = stack_write_constant(stack_top, 0); 409 410 /* Write env */ 411 stack_top = stack_write(stack_top, dest_envp, sizeof(dest_envp)); 412 413 /* NULL terimnate args (we don't have any) */ 414 stack_top = stack_write_constant(stack_top, 0); 415 416 /* Write argument count (we don't have any args) */ 417 stack_top = stack_write_constant(stack_top, 0); 418 419 return stack_top; 420} 421 422/*! @brief Jumps into loaded ELF program in current vspace. 423 @param file The mapped ELF header containing entry point to jump into. 424 */ 425static inline void 426sl_elf_start(char *file) 427{ 428 seL4_Word entryPoint = elf_getEntryPoint(file); 429 seL4_Word stackPointer = PROCESS_STACK_BOT + PROCESS_RLIMIT_STACK; 430 431 /* Future Work 3: 432 How the selfloader bootstraps user processes needs to be modified further. Changes were 433 made to accomodate the new way that muslc expects process's stacks to be set up when 434 processes start, but the one part of this that still needs to changed is how user processes 435 find their system call table. Currently the selfloader sets up user processes so that 436 the selfloader's system call table is used by user processes by passing the address of the 437 selfloader's system call table to the user processes via the user process's environment 438 variables. Ideally, user processes would use their own system call table. 439 */ 440 441 stackPointer = system_v_init(file, stackPointer); 442 443 dprintf("=============== Jumping into ELF program ==================\n"); 444 445 #ifdef ARCH_ARM 446 asm volatile ("mov sp, %0" :: "r" (stackPointer)); 447 asm volatile ("mov pc, %0" :: "r" (entryPoint)); 448 #elif defined(ARCH_IA32) 449 asm volatile ("movl %0, %%esp" :: "r" (stackPointer)); 450 asm volatile ("jmp %0 " :: "r" (entryPoint)); 451 #else 452 #error "Unknown architecture." 453 #endif /* ARCH_ARM */ 454} 455 456/*! @brief Fake time generator used by dprintf(). */ 457uint32_t 458faketime() { 459 static uint32_t faketime = 0; 460 return faketime++; 461} 462 463/*! @brief Selfloader main function. */ 464int 465main(void) 466{ 467 /* Future Work 4: 468 Eventually RefOS should be changed so that processes that are started 469 by the process server do not require that the their system call table be 470 explicitly referenced in the code like this. Without expliciting referencing 471 __vsyscall_ptr in main(), the compiler optimizes away __vsyscall_ptr 472 and then processes started by the process server can't find their system call 473 table. Each of the four places in RefOS where this explicit reference is 474 required is affected by a custom linker script (linker.lds), so it is possible 475 that the custom linker script (and then likely other things too) needs to be 476 modified. Also note that the ROS_ERROR() and assert() inside this if statement 477 would not actually be able to execute if __vsyscall_ptr() were ever not set. 478 The purpose of these calls to ROS_ERROR() and assert() is to show future 479 developers that __vsyscall_ptr needs to be defined. 480 */ 481 if (! __vsyscall_ptr) { 482 ROS_ERROR("Selfloader could not find system call table."); 483 assert("!Selfloader could not find system call table."); 484 return 0; 485 } 486 487 refos_seL4_debug_override_stdout(); 488 dprintf(COLOUR_C "--- Starting RefOS process selfloader ---\n" COLOUR_RESET); 489 refosio_setup_morecore_override(slMiniMorecoreRegion, SELFLOADER_MINI_MORECORE_REGION_SIZE); 490 refos_initialise_selfloader(); 491 int error = 0; 492 493 /* Sanity check the given ELF file path. */ 494 char *filePath = refos_static_param(); 495 if (!filePath || strlen(filePath) == 0) { 496 ROS_ERROR("Invalid file path."); 497 return EINVALIDPARAM; 498 } 499 500 /* Connect to the file server. */ 501 dprintf(" Connect to the server for [%s]\n", filePath); 502 selfloaderState.fileservConnection = serv_connect(filePath); 503 if (selfloaderState.fileservConnection.error != ESUCCESS) { 504 ROS_ERROR("Error while connecting to file server.\n"); 505 return error; 506 } 507 508 /* Now map the ELF file header for reading. */ 509 dprintf(" Mapping the ELF header for reading... [%s]\n", filePath); 510 error = sl_setup_elf_header(&selfloaderState.fileservConnection); 511 if (error) { 512 ROS_ERROR("Failed to open ELF header.") 513 return error; 514 } 515 516 /* Read and load the sections of the ELF file. */ 517 error = sl_elf_load(selfloaderState.elfFileHeader.vaddr, &selfloaderState.fileservConnection); 518 if (error) { 519 return error; 520 } 521 522 /* We don't need the file server session any more. */ 523 serv_disconnect(&selfloaderState.fileservConnection); 524 525 /* Set up bootinfo and jump into ELF entry! */ 526 sl_setup_bootinfo_buffer(); 527 sl_elf_start(selfloaderState.elfFileHeader.vaddr); 528 529 ROS_ERROR("ERROR: Should not ever be here!\n"); 530 assert(!"Something is wrong. Should not be here."); 531 while(1); 532 533 return 0; 534} 535