|  | // Copyright 2016 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "bootdata.h" | 
|  | #include "bootfs.h" | 
|  | #include "loader-service.h" | 
|  | #include "option.h" | 
|  | #include "userboot-elf.h" | 
|  | #include "util.h" | 
|  |  | 
|  | #pragma GCC visibility push(hidden) | 
|  |  | 
|  | #include <zircon/stack.h> | 
|  | #include <zircon/syscalls.h> | 
|  | #include <zircon/syscalls/log.h> | 
|  | #include <runtime/message.h> | 
|  | #include <runtime/processargs.h> | 
|  | #include <stdalign.h> | 
|  | #include <stdnoreturn.h> | 
|  | #include <string.h> | 
|  | #include <sys/param.h> | 
|  |  | 
|  | #pragma GCC visibility pop | 
|  |  | 
|  | #define SHUTDOWN_COMMAND "poweroff" | 
|  | #define STACK_VMO_NAME "userboot-child-initial-stack" | 
|  |  | 
|  | static noreturn void do_shutdown(zx_handle_t log, zx_handle_t rroot) { | 
|  | printl(log, "Process exited.  Executing \"" SHUTDOWN_COMMAND "\"."); | 
|  | zx_debug_send_command(rroot, SHUTDOWN_COMMAND, strlen(SHUTDOWN_COMMAND)); | 
|  | printl(log, "still here after shutdown!"); | 
|  | while (true) | 
|  | __builtin_trap(); | 
|  | } | 
|  |  | 
|  | static void load_child_process(zx_handle_t log, | 
|  | const struct options* o, struct bootfs* bootfs, | 
|  | zx_handle_t vdso_vmo, zx_handle_t proc, | 
|  | zx_handle_t vmar, zx_handle_t thread, | 
|  | zx_handle_t to_child, | 
|  | zx_vaddr_t* entry, zx_vaddr_t* vdso_base, | 
|  | size_t* stack_size, zx_handle_t* loader_svc) { | 
|  | // Examine the bootfs image and find the requested file in it. | 
|  | // This will handle a PT_INTERP by doing a second lookup in bootfs. | 
|  | *entry = elf_load_bootfs(log, bootfs, proc, vmar, thread, | 
|  | o->value[OPTION_FILENAME], to_child, stack_size, | 
|  | loader_svc); | 
|  |  | 
|  | // Now load the vDSO into the child, so it has access to system calls. | 
|  | *vdso_base = elf_load_vmo(log, vmar, vdso_vmo); | 
|  | } | 
|  |  | 
|  | // Reserve roughly the low half of the address space, so the initial | 
|  | // process can use sanitizers that need to allocate shadow memory there. | 
|  | // The reservation VMAR is kept around just long enough to make sure all | 
|  | // the initial allocations (mapping in the initial ELF object, and | 
|  | // allocating the initial stack) stay out of this area, and then destroyed. | 
|  | // The process's own allocations can then use the full address space; if | 
|  | // it's using a sanitizer, it will set up its shadow memory first thing. | 
|  | static zx_handle_t reserve_low_address_space(zx_handle_t log, | 
|  | zx_handle_t root_vmar) { | 
|  | zx_info_vmar_t info; | 
|  | check(log, zx_object_get_info(root_vmar, ZX_INFO_VMAR, | 
|  | &info, sizeof(info), NULL, NULL), | 
|  | "zx_object_get_info failed on child root VMAR handle"); | 
|  | zx_handle_t vmar; | 
|  | uintptr_t addr; | 
|  | size_t reserve_size = | 
|  | (((info.base + info.len) / 2) + PAGE_SIZE - 1) & -PAGE_SIZE; | 
|  | zx_status_t status = zx_vmar_allocate(root_vmar, 0, | 
|  | reserve_size - info.base, | 
|  | ZX_VM_FLAG_SPECIFIC, &vmar, &addr); | 
|  | check(log, status, | 
|  | "zx_vmar_allocate failed for low address space reservation"); | 
|  | if (addr != info.base) | 
|  | fail(log, "zx_vmar_allocate gave wrong address?!?"); | 
|  | return vmar; | 
|  | } | 
|  |  | 
|  | enum { | 
|  | EXTRA_HANDLE_BOOTFS, | 
|  | EXTRA_HANDLE_COUNT | 
|  | }; | 
|  |  | 
|  | // This is the main logic: | 
|  | // 1. Read the kernel's bootstrap message. | 
|  | // 2. Load up the child process from ELF file(s) on the bootfs. | 
|  | // 3. Create the initial thread and allocate a stack for it. | 
|  | // 4. Load up a channel with the zx_proc_args_t message for the child. | 
|  | // 5. Start the child process running. | 
|  | // 6. Optionally, wait for it to exit and then shut down. | 
|  | static noreturn void bootstrap(zx_handle_t log, zx_handle_t bootstrap_pipe) { | 
|  | // Sample the bootstrap message to see how big it is. | 
|  | uint32_t nbytes; | 
|  | uint32_t nhandles; | 
|  |  | 
|  | zx_status_t status = zxr_message_size(bootstrap_pipe, &nbytes, &nhandles); | 
|  | check(log, status, "zxr_message_size failed on bootstrap pipe!"); | 
|  |  | 
|  | // Read the bootstrap message from the kernel. | 
|  | ZXR_PROCESSARGS_BUFFER(buffer, | 
|  | nbytes + EXTRA_HANDLE_COUNT * sizeof(uint32_t)); | 
|  | zx_handle_t handles[nhandles + EXTRA_HANDLE_COUNT]; | 
|  | zx_proc_args_t* pargs; | 
|  | uint32_t* handle_info; | 
|  | status = zxr_processargs_read(bootstrap_pipe, | 
|  | buffer, nbytes, handles, nhandles, | 
|  | &pargs, &handle_info); | 
|  | check(log, status, "zxr_processargs_read failed on bootstrap message!"); | 
|  |  | 
|  | // All done with the channel from the kernel now.  Let it go. | 
|  | zx_handle_close(bootstrap_pipe); | 
|  |  | 
|  | // We're adding some extra handles, so we have to rearrange the | 
|  | // incoming message buffer to make space for their info slots. | 
|  | if (pargs->args_off != 0 || pargs->args_num != 0) { | 
|  | fail(log, "unexpected bootstrap message layout: args"); | 
|  | } | 
|  | if (pargs->environ_off != (pargs->handle_info_off + | 
|  | nhandles * sizeof(uint32_t))) { | 
|  | fail(log, "unexpected bootstrap message layout: environ"); | 
|  | } | 
|  | const size_t environ_size = nbytes - pargs->environ_off; | 
|  | pargs->environ_off += EXTRA_HANDLE_COUNT * sizeof(uint32_t); | 
|  | memmove(&buffer[pargs->environ_off], | 
|  | &buffer[pargs->handle_info_off + nhandles * sizeof(uint32_t)], | 
|  | environ_size); | 
|  | nbytes += EXTRA_HANDLE_COUNT * sizeof(uint32_t); | 
|  |  | 
|  | // Extract the environment (aka kernel command line) strings. | 
|  | char* environ[pargs->environ_num + 1]; | 
|  | status = zxr_processargs_strings(buffer, nbytes, NULL, environ, NULL); | 
|  | check(log, status, | 
|  | "zxr_processargs_strings failed on bootstrap message"); | 
|  |  | 
|  | // Process the kernel command line, which gives us options and also | 
|  | // becomes the environment strings for our child. | 
|  | struct options o; | 
|  | parse_options(log, &o, environ); | 
|  |  | 
|  | zx_handle_t resource_root = ZX_HANDLE_INVALID; | 
|  | zx_handle_t bootdata_vmo = ZX_HANDLE_INVALID; | 
|  | zx_handle_t vdso_vmo = ZX_HANDLE_INVALID; | 
|  | zx_handle_t job = ZX_HANDLE_INVALID; | 
|  | zx_handle_t* proc_handle_loc = NULL; | 
|  | zx_handle_t* vmar_root_handle_loc = NULL; | 
|  | zx_handle_t* thread_handle_loc = NULL; | 
|  | zx_handle_t* stack_vmo_handle_loc = NULL; | 
|  | for (uint32_t i = 0; i < nhandles; ++i) { | 
|  | switch (handle_info[i]) { | 
|  | case PA_HND(PA_VMO_VDSO, 0): | 
|  | vdso_vmo = handles[i]; | 
|  | break; | 
|  | case PA_HND(PA_PROC_SELF, 0): | 
|  | proc_handle_loc = &handles[i]; | 
|  | break; | 
|  | case PA_HND(PA_VMAR_ROOT, 0): | 
|  | vmar_root_handle_loc = &handles[i]; | 
|  | break; | 
|  | case PA_HND(PA_THREAD_SELF, 0): | 
|  | thread_handle_loc = &handles[i]; | 
|  | break; | 
|  | case PA_HND(PA_VMO_STACK, 0): | 
|  | stack_vmo_handle_loc = &handles[i]; | 
|  | break; | 
|  | case PA_HND(PA_RESOURCE, 0): | 
|  | resource_root = handles[i]; | 
|  | break; | 
|  | case PA_HND(PA_JOB_DEFAULT, 0): | 
|  | job = handles[i]; | 
|  | break; | 
|  | case PA_HND(PA_VMO_BOOTDATA, 0): | 
|  | if (bootdata_vmo == ZX_HANDLE_INVALID) { | 
|  | bootdata_vmo = handles[i]; | 
|  | zx_object_set_property(bootdata_vmo, ZX_PROP_NAME, "bootdata", 8); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (vdso_vmo == ZX_HANDLE_INVALID) | 
|  | fail(log, "no vDSO handle in bootstrap message"); | 
|  | if (resource_root == ZX_HANDLE_INVALID) | 
|  | fail(log, "no resource handle in bootstrap message"); | 
|  | if (job == ZX_HANDLE_INVALID) | 
|  | fail(log, "no job handle in bootstrap message"); | 
|  | if (vmar_root_handle_loc == NULL) | 
|  | fail(log, "no vmar root handle in bootstrap message"); | 
|  | if (bootdata_vmo == ZX_HANDLE_INVALID) | 
|  | fail(log, "no bootdata VMO in bootstrap message"); | 
|  |  | 
|  | // Hang on to our own process handle.  If we closed it, our process | 
|  | // would be killed.  Exiting will clean it up. | 
|  | __UNUSED const zx_handle_t proc_self = *proc_handle_loc; | 
|  | const zx_handle_t vmar_self = *vmar_root_handle_loc; | 
|  |  | 
|  | // Hang on to the resource root handle. | 
|  | zx_handle_t root_resource_handle; | 
|  | status = zx_handle_duplicate(resource_root, ZX_RIGHT_SAME_RIGHTS, | 
|  | &root_resource_handle); | 
|  | if (status < 0) | 
|  | fail(log, "zx_handle_duplicate failed: %d", status); | 
|  |  | 
|  | // Locate the first bootfs bootdata section and decompress it. | 
|  | // We need it to load devmgr and libc from. | 
|  | // Later bootfs sections will be processed by devmgr. | 
|  | zx_handle_t bootfs_vmo = bootdata_get_bootfs(log, vmar_self, bootdata_vmo); | 
|  |  | 
|  | // Pass the decompressed bootfs VMO on. | 
|  | handles[nhandles + EXTRA_HANDLE_BOOTFS] = bootfs_vmo; | 
|  | handle_info[nhandles + EXTRA_HANDLE_BOOTFS] = | 
|  | PA_HND(PA_VMO_BOOTFS, 0); | 
|  |  | 
|  | // Map in the bootfs so we can look for files in it. | 
|  | struct bootfs bootfs; | 
|  | bootfs_mount(vmar_self, log, bootfs_vmo, &bootfs); | 
|  |  | 
|  | // Make the channel for the bootstrap message. | 
|  | zx_handle_t to_child; | 
|  | zx_handle_t child_start_handle; | 
|  | status = zx_channel_create(0, &to_child, &child_start_handle); | 
|  | check(log, status, "zx_channel_create failed"); | 
|  |  | 
|  | const char* filename = o.value[OPTION_FILENAME]; | 
|  | zx_handle_t proc; | 
|  | zx_handle_t vmar; | 
|  | status = zx_process_create(job, filename, strlen(filename), 0, | 
|  | &proc, &vmar); | 
|  | if (status < 0) | 
|  | fail(log, "zx_process_create failed: %d", status); | 
|  |  | 
|  | zx_handle_t reserve_vmar = reserve_low_address_space(log, vmar); | 
|  |  | 
|  | // Create the initial thread in the new process | 
|  | zx_handle_t thread; | 
|  | status = zx_thread_create(proc, filename, strlen(filename), 0, &thread); | 
|  | if (status < 0) | 
|  | fail(log, "zx_thread_create failed: %d", status); | 
|  |  | 
|  | zx_vaddr_t entry, vdso_base; | 
|  | size_t stack_size = ZIRCON_DEFAULT_STACK_SIZE; | 
|  | zx_handle_t loader_service_channel = ZX_HANDLE_INVALID; | 
|  | load_child_process(log, &o, &bootfs, vdso_vmo, proc, vmar, | 
|  | thread, to_child, &entry, &vdso_base, &stack_size, | 
|  | &loader_service_channel); | 
|  |  | 
|  | // Allocate the stack for the child. | 
|  | stack_size = (stack_size + PAGE_SIZE - 1) & -PAGE_SIZE; | 
|  | zx_handle_t stack_vmo; | 
|  | status = zx_vmo_create(stack_size, 0, &stack_vmo); | 
|  | if (status < 0) | 
|  | fail(log, "zx_vmo_create failed for child stack: %d", status); | 
|  | zx_object_set_property(stack_vmo, ZX_PROP_NAME, | 
|  | STACK_VMO_NAME, sizeof(STACK_VMO_NAME) - 1); | 
|  | zx_vaddr_t stack_base; | 
|  | status = zx_vmar_map(vmar, 0, stack_vmo, 0, stack_size, | 
|  | ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE, | 
|  | &stack_base); | 
|  | check(log, status, "zx_vmar_map failed for child stack"); | 
|  | uintptr_t sp = compute_initial_stack_pointer(stack_base, stack_size); | 
|  | if (stack_vmo_handle_loc != NULL) { | 
|  | // This is our own stack VMO handle, but we don't need it for anything. | 
|  | if (*stack_vmo_handle_loc != ZX_HANDLE_INVALID) | 
|  | zx_handle_close(*stack_vmo_handle_loc); | 
|  | *stack_vmo_handle_loc = stack_vmo; | 
|  | } else { | 
|  | zx_handle_close(stack_vmo); | 
|  | } | 
|  |  | 
|  | // We're done doing mappings, so clear out the reservation VMAR. | 
|  | check(log, zx_vmar_destroy(reserve_vmar), | 
|  | "zx_vmar_destroy failed on reservation VMAR handle"); | 
|  | check(log, zx_handle_close(reserve_vmar), | 
|  | "zx_handle_close failed on reservation VMAR handle"); | 
|  |  | 
|  | // Reuse the slot for the child's handle. | 
|  | status = zx_handle_duplicate(proc, ZX_RIGHT_SAME_RIGHTS, proc_handle_loc); | 
|  | if (status < 0) | 
|  | fail(log, "zx_handle_duplicate failed on child process handle: %d", status); | 
|  |  | 
|  | if (thread_handle_loc != NULL) { | 
|  | // Reuse the slot for the child's handle. | 
|  | // NOTE: Leaks the current thread handle the same way as the process handle. | 
|  | status = zx_handle_duplicate(thread, ZX_RIGHT_SAME_RIGHTS, | 
|  | thread_handle_loc); | 
|  | if (status < 0) | 
|  | fail(log, "zx_handle_duplicate failed on child thread handle: %d", status); | 
|  | } | 
|  |  | 
|  | // Reuse the slot for the child's root VMAR handle.  We don't need to hold | 
|  | // a reference to this, so just pass ours to the child. | 
|  | *vmar_root_handle_loc = vmar; | 
|  |  | 
|  | // Now send the bootstrap message, consuming both our VMO handles. We also | 
|  | // send the job handle, which in the future means that we can't create more | 
|  | // processes from here on. | 
|  | status = zx_channel_write(to_child, 0, buffer, nbytes, | 
|  | handles, nhandles + EXTRA_HANDLE_COUNT); | 
|  | check(log, status, "zx_channel_write to child failed"); | 
|  | status = zx_handle_close(to_child); | 
|  | check(log, status, "zx_handle_close failed on channel handle"); | 
|  |  | 
|  | // Start the process going. | 
|  | status = zx_process_start(proc, thread, entry, sp, | 
|  | child_start_handle, vdso_base); | 
|  | check(log, status, "zx_process_start failed"); | 
|  | status = zx_handle_close(thread); | 
|  | check(log, status, "zx_handle_close failed on thread handle"); | 
|  |  | 
|  | printl(log, "process %s started.", o.value[OPTION_FILENAME]); | 
|  |  | 
|  | // Now become the loader service for as long as that's needed. | 
|  | if (loader_service_channel != ZX_HANDLE_INVALID) | 
|  | loader_service(log, &bootfs, loader_service_channel); | 
|  |  | 
|  | // All done with bootfs! | 
|  | bootfs_unmount(vmar_self, log, &bootfs); | 
|  |  | 
|  | if (o.value[OPTION_SHUTDOWN] != NULL) { | 
|  | printl(log, "Waiting for %s to exit...", o.value[OPTION_FILENAME]); | 
|  | status = zx_object_wait_one( | 
|  | proc, ZX_PROCESS_TERMINATED, ZX_TIME_INFINITE, NULL); | 
|  | check(log, status, "zx_object_wait_one on process failed"); | 
|  | do_shutdown(log, root_resource_handle); | 
|  | } | 
|  |  | 
|  | // Now we've accomplished our purpose in life, and we can die happy. | 
|  |  | 
|  | status = zx_handle_close(proc); | 
|  | check(log, status, "zx_handle_close failed on process handle"); | 
|  |  | 
|  | printl(log, "finished!"); | 
|  | zx_process_exit(0); | 
|  | } | 
|  |  | 
|  | // This is the entry point for the whole show, the very first bit of code | 
|  | // to run in user mode. | 
|  | noreturn void _start(void* start_arg) { | 
|  | zx_handle_t log = ZX_HANDLE_INVALID; | 
|  | zx_log_create(0, &log); | 
|  | if (log == ZX_HANDLE_INVALID) | 
|  | printl(log, "zx_log_create failed, using zx_debug_write instead"); | 
|  |  | 
|  | zx_handle_t bootstrap_pipe = (uintptr_t)start_arg; | 
|  | bootstrap(log, bootstrap_pipe); | 
|  | } |