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