| /* | |
| * Copyright 2017, Data61 | |
| * Commonwealth Scientific and Industrial Research Organisation (CSIRO) | |
| * ABN 41 687 119 230. | |
| * | |
| * This software may be distributed and modified according to the terms of | |
| * the BSD 2-Clause license. Note that NO WARRANTY is provided. | |
| * See "LICENSE_BSD2.txt" for details. | |
| * | |
| * @TAG(DATA61_BSD) | |
| */ | |
| #include <autoconf.h> | |
| #include <sel4utils/gen_config.h> | |
| #define _GNU_SOURCE | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <stdio.h> | |
| #include <inttypes.h> | |
| #include <elf/elf.h> | |
| #include <cpio/cpio.h> | |
| #include <sel4/sel4.h> | |
| #include <vka/object.h> | |
| #include <vka/capops.h> | |
| #include <stdarg.h> | |
| #include <sel4runtime/auxv.h> | |
| #include <sel4utils/vspace.h> | |
| #include <sel4utils/process.h> | |
| #include <sel4utils/util.h> | |
| #include <sel4utils/elf.h> | |
| #include <sel4utils/mapping.h> | |
| #include <sel4utils/helpers.h> | |
| /* This library works with our cpio set up in the build system */ | |
| extern char _cpio_archive[]; | |
| extern char _cpio_archive_end[]; | |
| void sel4utils_allocated_object(void *cookie, vka_object_t object) | |
| { | |
| static bool recurse = false; | |
| if (recurse) { | |
| ZF_LOGF("VSPACE RECURSION ON MALLOC, YOU ARE DEAD\n"); | |
| } | |
| recurse = true; | |
| sel4utils_process_t *process = cookie; | |
| object_node_t *node = malloc(sizeof(object_node_t)); | |
| assert(node != NULL); | |
| node->object = object; | |
| node->next = process->allocated_object_list_head; | |
| process->allocated_object_list_head = node; | |
| recurse = false; | |
| } | |
| static void clear_objects(sel4utils_process_t *process, vka_t *vka) | |
| { | |
| assert(process != NULL); | |
| assert(vka != NULL); | |
| while (process->allocated_object_list_head != NULL) { | |
| object_node_t *prev = process->allocated_object_list_head; | |
| process->allocated_object_list_head = prev->next; | |
| vka_free_object(vka, &prev->object); | |
| free(prev); | |
| } | |
| } | |
| static int next_free_slot(sel4utils_process_t *process, cspacepath_t *dest) | |
| { | |
| if (process->cspace_next_free >= (BIT(process->cspace_size))) { | |
| ZF_LOGE("Can't allocate slot, cspace is full.\n"); | |
| return -1; | |
| } | |
| dest->root = process->cspace.cptr; | |
| dest->capPtr = process->cspace_next_free; | |
| dest->capDepth = process->cspace_size; | |
| return 0; | |
| } | |
| static void allocate_next_slot(sel4utils_process_t *process) | |
| { | |
| assert(process->cspace_next_free < (BIT(process->cspace_size))); | |
| process->cspace_next_free++; | |
| } | |
| void sel4utils_create_word_args(char strings[][WORD_STRING_SIZE], char *argv[], int argc, ...) | |
| { | |
| va_list args; | |
| va_start(args, argc); | |
| for (int i = 0; i < argc; i++) { | |
| seL4_Word arg = va_arg(args, seL4_Word); | |
| argv[i] = strings[i]; | |
| snprintf(argv[i], WORD_STRING_SIZE, "%"PRIuPTR"", arg); | |
| } | |
| va_end(args); | |
| } | |
| seL4_CPtr sel4utils_mint_cap_to_process(sel4utils_process_t *process, cspacepath_t src, seL4_CapRights_t rights, | |
| seL4_Word data) | |
| { | |
| cspacepath_t dest = { 0 }; | |
| if (next_free_slot(process, &dest) == -1) { | |
| return 0; | |
| } | |
| int error = vka_cnode_mint(&dest, &src, rights, data); | |
| if (error != seL4_NoError) { | |
| ZF_LOGE("Failed to mint cap\n"); | |
| return 0; | |
| } | |
| /* success */ | |
| allocate_next_slot(process); | |
| return dest.capPtr; | |
| } | |
| seL4_CPtr sel4utils_copy_path_to_process(sel4utils_process_t *process, cspacepath_t src) | |
| { | |
| cspacepath_t dest = { 0 }; | |
| if (next_free_slot(process, &dest) == -1) { | |
| return 0; | |
| } | |
| int error = vka_cnode_copy(&dest, &src, seL4_AllRights); | |
| if (error != seL4_NoError) { | |
| ZF_LOGE("Failed to copy cap\n"); | |
| return 0; | |
| } | |
| /* success */ | |
| allocate_next_slot(process); | |
| return dest.capPtr; | |
| } | |
| /* copy a cap to a process, returning the cptr in the process' cspace */ | |
| seL4_CPtr sel4utils_copy_cap_to_process(sel4utils_process_t *process, vka_t *vka, seL4_CPtr cap) | |
| { | |
| cspacepath_t path; | |
| vka_cspace_make_path(vka, cap, &path); | |
| return sel4utils_copy_path_to_process(process, path); | |
| } | |
| seL4_CPtr sel4utils_move_cap_to_process(sel4utils_process_t *process, cspacepath_t src, vka_t *from_vka) | |
| { | |
| cspacepath_t dest = { 0 }; | |
| if (next_free_slot(process, &dest) == -1) { | |
| return 0; | |
| } | |
| int error = vka_cnode_move(&dest, &src); | |
| if (error != seL4_NoError) { | |
| ZF_LOGE("Failed to move cap\n"); | |
| return 0; | |
| } | |
| /* If we have been passed a vka to free a cspace slot */ | |
| if (from_vka != NULL) { | |
| /* free slot in previous vka */ | |
| vka_cspace_free(from_vka, src.capPtr); | |
| } | |
| /* success */ | |
| allocate_next_slot(process); | |
| return dest.capPtr; | |
| } | |
| int sel4utils_stack_write(vspace_t *current_vspace, vspace_t *target_vspace, | |
| vka_t *vka, void *buf, size_t len, uintptr_t *initial_stack_pointer) | |
| { | |
| size_t remaining = len; | |
| size_t written = 0; | |
| uintptr_t new_stack_pointer = (*initial_stack_pointer) - len; | |
| uintptr_t current_dest = new_stack_pointer; | |
| while (remaining > 0) { | |
| /* How many can we write on the current page ? */ | |
| size_t towrite = MIN(PAGE_SIZE_4K - (current_dest % PAGE_SIZE_4K), remaining); | |
| assert(towrite != 0); | |
| /* Get the cap */ | |
| seL4_CPtr frame = vspace_get_cap(target_vspace, (void *)PAGE_ALIGN_4K(current_dest)); | |
| if (!frame) { | |
| return -1; | |
| } | |
| /* map it in */ | |
| void *mapping = sel4utils_dup_and_map(vka, current_vspace, frame, seL4_PageBits); | |
| if (!mapping) { | |
| return -1; | |
| } | |
| /* Copy the portion */ | |
| memcpy(mapping + (current_dest % PAGE_SIZE_4K), buf + written, towrite); | |
| /* Unmap */ | |
| sel4utils_unmap_dup(vka, current_vspace, mapping, seL4_PageBits); | |
| remaining -= towrite; | |
| written += towrite; | |
| current_dest += towrite; | |
| } | |
| *initial_stack_pointer = new_stack_pointer; | |
| return 0; | |
| } | |
| static int sel4utils_stack_write_constant(vspace_t *current_vspace, vspace_t *target_vspace, | |
| vka_t *vka, long value, uintptr_t *initial_stack_pointer) | |
| { | |
| return sel4utils_stack_write(current_vspace, target_vspace, vka, &value, sizeof(value), initial_stack_pointer); | |
| } | |
| static int sel4utils_stack_copy_args(vspace_t *current_vspace, vspace_t *target_vspace, | |
| vka_t *vka, int argc, char *argv[], uintptr_t *dest_argv, uintptr_t *initial_stack_pointer) | |
| { | |
| for (int i = 0; i < argc; i++) { | |
| int error = sel4utils_stack_write(current_vspace, target_vspace, vka, argv[i], strlen(argv[i]) + 1, | |
| initial_stack_pointer); | |
| if (error) { | |
| return error; | |
| } | |
| dest_argv[i] = *initial_stack_pointer; | |
| *initial_stack_pointer = ROUND_DOWN(*initial_stack_pointer, 4); | |
| } | |
| return 0; | |
| } | |
| int sel4utils_spawn_process(sel4utils_process_t *process, vka_t *vka, vspace_t *vspace, int argc, | |
| char *argv[], int resume) | |
| { | |
| uintptr_t initial_stack_pointer = (uintptr_t)process->thread.stack_top - sizeof(seL4_Word); | |
| uintptr_t new_process_argv = 0; | |
| int error; | |
| /* write all the strings into the stack */ | |
| if (argc > 0) { | |
| uintptr_t dest_argv[argc]; | |
| /* Copy over the user arguments */ | |
| error = sel4utils_stack_copy_args(vspace, &process->vspace, vka, argc, argv, dest_argv, &initial_stack_pointer); | |
| if (error) { | |
| return -1; | |
| } | |
| /* Put the new argv array on as well */ | |
| error = sel4utils_stack_write(vspace, &process->vspace, vka, dest_argv, sizeof(dest_argv), &initial_stack_pointer); | |
| if (error) { | |
| return -1; | |
| } | |
| new_process_argv = initial_stack_pointer; | |
| } | |
| /* move the stack pointer down to a place we can write to. | |
| * to be compatible with as many architectures as possible | |
| * we need to ensure double word alignment */ | |
| initial_stack_pointer = ALIGN_DOWN(initial_stack_pointer - sizeof(seL4_Word), STACK_CALL_ALIGNMENT); | |
| seL4_UserContext context = {0}; | |
| size_t context_size = sizeof(seL4_UserContext) / sizeof(seL4_Word); | |
| error = sel4utils_arch_init_context_with_args(process->entry_point, (void *)(uintptr_t)argc, | |
| (void *) new_process_argv, | |
| (void *) process->thread.ipc_buffer_addr, false, | |
| (void *) initial_stack_pointer, | |
| &context, vka, vspace, &process->vspace); | |
| if (error) { | |
| return error; | |
| } | |
| process->thread.initial_stack_pointer = (void *) initial_stack_pointer; | |
| return seL4_TCB_WriteRegisters(process->thread.tcb.cptr, resume, 0, context_size, &context); | |
| } | |
| int sel4utils_spawn_process_v(sel4utils_process_t *process, vka_t *vka, vspace_t *vspace, int argc, | |
| char *argv[], int resume) | |
| { | |
| /* define an envp and auxp */ | |
| int error; | |
| int envc = 0; | |
| char *envp[] = {}; | |
| uintptr_t initial_stack_pointer = (uintptr_t) process->thread.stack_top - sizeof(seL4_Word); | |
| /* Copy the elf headers */ | |
| uintptr_t at_phdr; | |
| error = sel4utils_stack_write(vspace, &process->vspace, vka, process->elf_phdrs, | |
| process->num_elf_phdrs * sizeof(Elf_Phdr), &initial_stack_pointer); | |
| if (error) { | |
| return -1; | |
| } | |
| at_phdr = initial_stack_pointer; | |
| /* initialize of aux vectors */ | |
| int auxc = 6; | |
| Elf_auxv_t auxv[7]; | |
| auxv[0].a_type = AT_PAGESZ; | |
| auxv[0].a_un.a_val = process->pagesz; | |
| auxv[1].a_type = AT_PHDR; | |
| auxv[1].a_un.a_val = at_phdr; | |
| auxv[2].a_type = AT_PHNUM; | |
| auxv[2].a_un.a_val = process->num_elf_phdrs; | |
| auxv[3].a_type = AT_PHENT; | |
| auxv[3].a_un.a_val = sizeof(Elf_Phdr); | |
| auxv[4].a_type = AT_SEL4_IPC_BUFFER_PTR; | |
| auxv[4].a_un.a_val = (uintptr_t) process->thread.ipc_buffer_addr; | |
| auxv[5].a_type = AT_SEL4_TCB; | |
| auxv[5].a_un.a_val = process->dest_tcb_cptr; | |
| if (process->sysinfo) { | |
| auxv[6].a_type = AT_SYSINFO; | |
| auxv[6].a_un.a_val = process->sysinfo; | |
| auxc++; | |
| } | |
| seL4_UserContext context = {0}; | |
| uintptr_t dest_argv[argc]; | |
| uintptr_t dest_envp[envc]; | |
| /* write all the strings into the stack */ | |
| /* Copy over the user arguments */ | |
| error = sel4utils_stack_copy_args(vspace, &process->vspace, vka, argc, argv, dest_argv, &initial_stack_pointer); | |
| if (error) { | |
| return -1; | |
| } | |
| /* copy the environment */ | |
| error = sel4utils_stack_copy_args(vspace, &process->vspace, vka, envc, envp, dest_envp, &initial_stack_pointer); | |
| if (error) { | |
| return -1; | |
| } | |
| /* we need to make sure the stack is aligned to a double word boundary after we push on everything else | |
| * below this point. First, work out how much we are going to push */ | |
| size_t to_push = 5 * sizeof(seL4_Word) + /* constants */ | |
| sizeof(auxv[0]) * auxc + /* aux */ | |
| sizeof(dest_argv) + /* args */ | |
| sizeof(dest_envp); /* env */ | |
| uintptr_t hypothetical_stack_pointer = initial_stack_pointer - to_push; | |
| uintptr_t rounded_stack_pointer = ALIGN_DOWN(hypothetical_stack_pointer, STACK_CALL_ALIGNMENT); | |
| ptrdiff_t stack_rounding = hypothetical_stack_pointer - rounded_stack_pointer; | |
| initial_stack_pointer -= stack_rounding; | |
| /* construct initial stack frame */ | |
| /* Null terminate aux */ | |
| error = sel4utils_stack_write_constant(vspace, &process->vspace, vka, 0, &initial_stack_pointer); | |
| if (error) { | |
| return -1; | |
| } | |
| error = sel4utils_stack_write_constant(vspace, &process->vspace, vka, 0, &initial_stack_pointer); | |
| if (error) { | |
| return -1; | |
| } | |
| /* write aux */ | |
| error = sel4utils_stack_write(vspace, &process->vspace, vka, auxv, sizeof(auxv[0]) * auxc, &initial_stack_pointer); | |
| if (error) { | |
| return -1; | |
| } | |
| /* Null terminate environment */ | |
| error = sel4utils_stack_write_constant(vspace, &process->vspace, vka, 0, &initial_stack_pointer); | |
| if (error) { | |
| return -1; | |
| } | |
| /* write environment */ | |
| error = sel4utils_stack_write(vspace, &process->vspace, vka, dest_envp, sizeof(dest_envp), &initial_stack_pointer); | |
| if (error) { | |
| return -1; | |
| } | |
| /* Null terminate arguments */ | |
| error = sel4utils_stack_write_constant(vspace, &process->vspace, vka, 0, &initial_stack_pointer); | |
| if (error) { | |
| return -1; | |
| } | |
| /* write arguments */ | |
| error = sel4utils_stack_write(vspace, &process->vspace, vka, dest_argv, sizeof(dest_argv), &initial_stack_pointer); | |
| if (error) { | |
| return -1; | |
| } | |
| /* Push argument count */ | |
| error = sel4utils_stack_write_constant(vspace, &process->vspace, vka, argc, &initial_stack_pointer); | |
| if (error) { | |
| return -1; | |
| } | |
| ZF_LOGD("Starting process at %p, stack %p\n", process->entry_point, (void *) initial_stack_pointer); | |
| assert(initial_stack_pointer % (2 * sizeof(seL4_Word)) == 0); | |
| error = sel4utils_arch_init_context(process->entry_point, (void *) initial_stack_pointer, &context); | |
| if (error) { | |
| return error; | |
| } | |
| process->thread.initial_stack_pointer = (void *) initial_stack_pointer; | |
| /* Write the registers */ | |
| return seL4_TCB_WriteRegisters(process->thread.tcb.cptr, resume, 0, sizeof(context) / sizeof(seL4_Word), | |
| &context); | |
| } | |
| int sel4utils_configure_process(sel4utils_process_t *process, vka_t *vka, | |
| vspace_t *vspace, const char *image_name) | |
| { | |
| sel4utils_process_config_t config = process_config_default(image_name, seL4_CapInitThreadASIDPool); | |
| return sel4utils_configure_process_custom(process, vka, vspace, config); | |
| } | |
| static int create_reservations(vspace_t *vspace, int num, sel4utils_elf_region_t regions[]) | |
| { | |
| for (int i = 0; i < num; i++) { | |
| sel4utils_elf_region_t *region = ®ions[i]; | |
| region->reservation = vspace_reserve_range_at(vspace, region->elf_vstart, | |
| region->size, region->rights, region->cacheable); | |
| if (region->reservation.res == NULL) { | |
| ZF_LOGE("Failed to create region\n"); | |
| for (int j = i - 1; j >= 0; j--) { | |
| vspace_free_reservation(vspace, regions[i].reservation); | |
| } | |
| return -1; | |
| } | |
| } | |
| return 0; | |
| } | |
| static seL4_CPtr get_asid_pool(seL4_CPtr asid_pool) | |
| { | |
| if (asid_pool == 0) { | |
| ZF_LOGW("This method will fail if run in a thread that is not in the root server cspace\n"); | |
| asid_pool = seL4_CapInitThreadASIDPool; | |
| } | |
| return asid_pool; | |
| } | |
| static seL4_CPtr assign_asid_pool(seL4_CPtr asid_pool, seL4_CPtr pd) | |
| { | |
| int error = seL4_ARCH_ASIDPool_Assign(get_asid_pool(asid_pool), pd); | |
| if (error) { | |
| ZF_LOGE("Failed to assign asid pool\n"); | |
| } | |
| return error; | |
| } | |
| static int create_cspace(vka_t *vka, int size_bits, sel4utils_process_t *process, | |
| seL4_Word cspace_root_data, seL4_CPtr asid_pool) | |
| { | |
| /* create a cspace */ | |
| int error = vka_alloc_cnode_object(vka, size_bits, &process->cspace); | |
| if (error) { | |
| ZF_LOGE("Failed to create cspace: %d\n", error); | |
| return error; | |
| } | |
| process->cspace_size = size_bits; | |
| /* first slot is always 1, never allocate 0 as a cslot */ | |
| process->cspace_next_free = 1; | |
| /* mint the cnode cap into the process cspace */ | |
| cspacepath_t src; | |
| vka_cspace_make_path(vka, process->cspace.cptr, &src); | |
| UNUSED seL4_CPtr slot = sel4utils_mint_cap_to_process(process, src, seL4_AllRights, cspace_root_data); | |
| assert(slot == SEL4UTILS_CNODE_SLOT); | |
| /* copy fault endpoint cap into process cspace */ | |
| if (process->fault_endpoint.cptr != 0) { | |
| vka_cspace_make_path(vka, process->fault_endpoint.cptr, &src); | |
| slot = sel4utils_copy_path_to_process(process, src); | |
| assert(slot == SEL4UTILS_ENDPOINT_SLOT); | |
| } else { | |
| /* no fault endpoint, update slot so next will work */ | |
| allocate_next_slot(process); | |
| } | |
| /* copy page directory cap into process cspace */ | |
| vka_cspace_make_path(vka, process->pd.cptr, &src); | |
| slot = sel4utils_copy_path_to_process(process, src); | |
| assert(slot == SEL4UTILS_PD_SLOT); | |
| if (!config_set(CONFIG_X86_64)) { | |
| vka_cspace_make_path(vka, get_asid_pool(asid_pool), &src); | |
| slot = sel4utils_copy_path_to_process(process, src); | |
| } else { | |
| allocate_next_slot(process); | |
| } | |
| assert(slot == SEL4UTILS_ASID_POOL_SLOT); | |
| return 0; | |
| } | |
| static int create_fault_endpoint(vka_t *vka, sel4utils_process_t *process) | |
| { | |
| /* create a fault endpoint and put it into the cspace */ | |
| int error = vka_alloc_endpoint(vka, &process->fault_endpoint); | |
| process->own_ep = true; | |
| if (error) { | |
| ZF_LOGE("Failed to allocate fault endpoint: %d\n", error); | |
| return error; | |
| } | |
| return 0; | |
| } | |
| int sel4utils_configure_process_custom(sel4utils_process_t *process, vka_t *vka, | |
| vspace_t *spawner_vspace, sel4utils_process_config_t config) | |
| { | |
| int error; | |
| sel4utils_alloc_data_t *data = NULL; | |
| memset(process, 0, sizeof(sel4utils_process_t)); | |
| seL4_Word cspace_root_data = api_make_guard_skip_word(seL4_WordBits - config.one_level_cspace_size_bits); | |
| /* create a page directory */ | |
| process->own_vspace = config.create_vspace; | |
| if (config.create_vspace) { | |
| error = vka_alloc_vspace_root(vka, &process->pd); | |
| if (error) { | |
| ZF_LOGE("Failed to allocate page directory for new process: %d\n", error); | |
| goto error; | |
| } | |
| /* assign an asid pool */ | |
| if (!config_set(CONFIG_X86_64) && | |
| assign_asid_pool(config.asid_pool, process->pd.cptr) != seL4_NoError) { | |
| goto error; | |
| } | |
| } else { | |
| process->pd = config.page_dir; | |
| } | |
| if (config.create_fault_endpoint) { | |
| if (create_fault_endpoint(vka, process) != 0) { | |
| goto error; | |
| } | |
| } else { | |
| process->fault_endpoint = config.fault_endpoint; | |
| } | |
| process->own_cspace = config.create_cspace; | |
| if (config.create_cspace) { | |
| if (create_cspace(vka, config.one_level_cspace_size_bits, process, cspace_root_data, | |
| config.asid_pool) != 0) { | |
| goto error; | |
| } | |
| } else { | |
| process->cspace = config.cnode; | |
| } | |
| /* create a vspace */ | |
| if (config.create_vspace) { | |
| sel4utils_get_vspace(spawner_vspace, &process->vspace, &process->data, vka, process->pd.cptr, | |
| sel4utils_allocated_object, (void *) process); | |
| if (config.num_reservations > 0) { | |
| if (create_reservations(&process->vspace, config.num_reservations, | |
| config.reservations)) { | |
| goto error; | |
| } | |
| } | |
| } else { | |
| memcpy(&process->vspace, config.vspace, sizeof(process->vspace)); | |
| } | |
| /* finally elf load */ | |
| if (config.is_elf) { | |
| unsigned long size; | |
| unsigned long cpio_len = _cpio_archive_end - _cpio_archive; | |
| char *file = cpio_get_file(_cpio_archive, cpio_len, config.image_name, &size); | |
| elf_t elf; | |
| elf_newFile(file, size, &elf); | |
| if (config.do_elf_load) { | |
| process->entry_point = sel4utils_elf_load(&process->vspace, spawner_vspace, vka, vka, &elf); | |
| } else { | |
| process->num_elf_regions = sel4utils_elf_num_regions(&elf); | |
| process->elf_regions = calloc(process->num_elf_regions, sizeof(*process->elf_regions)); | |
| if (!process->elf_regions) { | |
| ZF_LOGE("Failed to allocate memory for elf region information"); | |
| goto error; | |
| } | |
| process->entry_point = sel4utils_elf_reserve(&process->vspace, &elf, process->elf_regions); | |
| } | |
| if (process->entry_point == NULL) { | |
| ZF_LOGE("Failed to load elf file\n"); | |
| goto error; | |
| } | |
| process->sysinfo = sel4utils_elf_get_vsyscall(&elf); | |
| /* Retrieve the ELF phdrs */ | |
| process->num_elf_phdrs = sel4utils_elf_num_phdrs(&elf); | |
| process->elf_phdrs = calloc(process->num_elf_phdrs, sizeof(Elf_Phdr)); | |
| if (!process->elf_phdrs) { | |
| ZF_LOGE("Failed to allocate memory for elf phdr information"); | |
| goto error; | |
| } | |
| sel4utils_elf_read_phdrs(&elf, process->num_elf_phdrs, process->elf_phdrs); | |
| } else { | |
| process->entry_point = config.entry_point; | |
| process->sysinfo = config.sysinfo; | |
| } | |
| /* select the default page size of machine this process is running on */ | |
| process->pagesz = PAGE_SIZE_4K; | |
| /* create the thread, do this *after* elf-loading so that we don't clobber | |
| * the required virtual memory*/ | |
| sel4utils_thread_config_t thread_config = {0}; | |
| thread_config = thread_config_cspace(thread_config, process->cspace.cptr, cspace_root_data); | |
| if (config_set(CONFIG_KERNEL_MCS)) { | |
| /* on the MCS kernel, use the fault endpoint in the current cspace */ | |
| thread_config = thread_config_fault_endpoint(thread_config, process->fault_endpoint.cptr); | |
| } else if (process->fault_endpoint.cptr != 0) { | |
| /* on the master kernel, the fault ep must be in the cspace of the process */ | |
| thread_config = thread_config_fault_endpoint(thread_config, SEL4UTILS_ENDPOINT_SLOT); | |
| } | |
| thread_config.sched_params = config.sched_params; | |
| thread_config.create_reply = config.create_cspace; | |
| error = sel4utils_configure_thread_config(vka, spawner_vspace, &process->vspace, thread_config, | |
| &process->thread); | |
| if (error) { | |
| ZF_LOGE("ERROR: failed to configure thread for new process %d\n", error); | |
| goto error; | |
| } | |
| /* copy tcb cap to cspace */ | |
| if (config.create_cspace) { | |
| cspacepath_t src; | |
| vka_cspace_make_path(vka, process->thread.tcb.cptr, &src); | |
| UNUSED seL4_CPtr slot = sel4utils_copy_path_to_process(process, src); | |
| assert(slot == SEL4UTILS_TCB_SLOT); | |
| process->dest_tcb_cptr = SEL4UTILS_TCB_SLOT; | |
| } else { | |
| process->dest_tcb_cptr = config.dest_cspace_tcb_cptr; | |
| } | |
| if (config.create_cspace) { | |
| if (config_set(CONFIG_KERNEL_MCS)) { | |
| seL4_CPtr UNUSED slot = sel4utils_copy_cap_to_process(process, vka, process->thread.sched_context.cptr); | |
| assert(slot == SEL4UTILS_SCHED_CONTEXT_SLOT); | |
| slot = sel4utils_copy_cap_to_process(process, vka, process->thread.reply.cptr); | |
| assert(slot == SEL4UTILS_REPLY_SLOT); | |
| } else { | |
| /* skip the sc slot */ | |
| allocate_next_slot(process); | |
| /* skip the reply object slot */ | |
| allocate_next_slot(process); | |
| } | |
| } | |
| return 0; | |
| error: | |
| /* try to clean up */ | |
| if (config.create_fault_endpoint && process->fault_endpoint.cptr != 0) { | |
| vka_free_object(vka, &process->fault_endpoint); | |
| } | |
| if (config.create_cspace && process->cspace.cptr != 0) { | |
| vka_free_object(vka, &process->cspace); | |
| } | |
| if (config.create_vspace && process->pd.cptr != 0) { | |
| vka_free_object(vka, &process->pd); | |
| if (process->vspace.data != 0) { | |
| ZF_LOGE("Could not clean up vspace\n"); | |
| } | |
| } | |
| if (process->elf_regions) { | |
| free(process->elf_regions); | |
| } | |
| if (process->elf_phdrs) { | |
| free(process->elf_phdrs); | |
| } | |
| if (data != NULL) { | |
| free(data); | |
| } | |
| return -1; | |
| } | |
| void sel4utils_destroy_process(sel4utils_process_t *process, vka_t *vka) | |
| { | |
| /* destroy the cnode */ | |
| if (process->own_cspace) { | |
| cspacepath_t path; | |
| vka_cspace_make_path(vka, process->cspace.cptr, &path); | |
| /* need to revoke the cnode to remove any self references that would keep the object | |
| * alive when we try to delete it */ | |
| vka_cnode_revoke(&path); | |
| vka_free_object(vka, &process->cspace); | |
| } | |
| /* destroy the thread */ | |
| sel4utils_clean_up_thread(vka, &process->vspace, &process->thread); | |
| /* tear down the vspace */ | |
| if (process->own_vspace) { | |
| vspace_tear_down(&process->vspace, VSPACE_FREE); | |
| /* free any objects created by the vspace */ | |
| clear_objects(process, vka); | |
| } | |
| /* destroy the endpoint */ | |
| if (process->own_ep && process->fault_endpoint.cptr != 0) { | |
| vka_free_object(vka, &process->fault_endpoint); | |
| } | |
| /* destroy the page directory */ | |
| if (process->own_vspace) { | |
| vka_free_object(vka, &process->pd); | |
| } | |
| /* Free elf information */ | |
| if (process->elf_regions) { | |
| free(process->elf_regions); | |
| } | |
| if (process->elf_phdrs) { | |
| free(process->elf_phdrs); | |
| } | |
| } | |
| seL4_CPtr sel4utils_process_init_cap(void *data, seL4_CPtr cap) | |
| { | |
| switch (cap) { | |
| case seL4_CapInitThreadTCB: | |
| return SEL4UTILS_TCB_SLOT; | |
| case seL4_CapInitThreadCNode: | |
| return SEL4UTILS_CNODE_SLOT; | |
| case seL4_CapInitThreadVSpace: | |
| return SEL4UTILS_PD_SLOT; | |
| case seL4_CapInitThreadASIDPool: | |
| return SEL4UTILS_ASID_POOL_SLOT; | |
| #ifdef CONFIG_KERNEL_MCS | |
| case seL4_CapInitThreadSC: | |
| return SEL4UTILS_SCHED_CONTEXT_SLOT; | |
| #endif | |
| default: | |
| ZF_LOGE("sel4utils does not copy this cap (%zu) to new processes", cap); | |
| return seL4_CapNull; | |
| } | |
| }; | |
| int sel4utils_copy_timer_caps_to_process(timer_objects_t *to, timer_objects_t *from, vka_t *vka, | |
| sel4utils_process_t *process) | |
| { | |
| if (to == NULL || from == NULL || vka == NULL || process == NULL) { | |
| ZF_LOGE("Invalid argument (is null): to: %p, from: %p, vka: %p, process: %p", to, from, vka, process); | |
| return EINVAL; | |
| } | |
| /* struct deep copy */ | |
| *to = *from; | |
| /* copy irq caps */ | |
| for (size_t i = 0; i < to->nirqs; i++) { | |
| to->irqs[i].handler_path.capPtr = sel4utils_copy_cap_to_process(process, | |
| vka, from->irqs[i].handler_path.capPtr); | |
| } | |
| /* copy pmem ut frame caps */ | |
| for (size_t i = 0; i < to->nobjs; i++) { | |
| to->objs[i].obj.cptr = sel4utils_copy_cap_to_process(process, | |
| vka, from->objs[i].obj.cptr); | |
| } | |
| return 0; | |
| } |