#include #include #include #include #include #include #include #include #include #include #include #include extern struct bootinfo *bi; extern coreid_t my_core_id; struct spawninfo *spawn_process_list = NULL; /** * \brief Set the base address of the .got (Global Offset Table) section of the ELF binary * * \param arch_load_info This must be the base address of the .got section (local to the * child's VSpace). Must not be NULL. * \param handle The handle for the new dispatcher that is to be spawned. Must not be NULL. * \param enabled_area The "resume enabled" register set. Must not be NULL. * \param disabled_area The "resume disabled" register set. Must not be NULL. */ __attribute__((__used__)) static void armv8_set_registers(void *arch_load_info, dispatcher_handle_t handle, arch_registers_state_t *enabled_area, arch_registers_state_t *disabled_area) { assert(arch_load_info != NULL); uintptr_t got_base = (uintptr_t) arch_load_info; struct dispatcher_shared_aarch64 * disp_arm = get_dispatcher_shared_aarch64(handle); disp_arm->got_base = got_base; enabled_area->regs[REG_OFFSET(PIC_REGISTER)] = got_base; disabled_area->regs[REG_OFFSET(PIC_REGISTER)] = got_base; } struct allocate_state { // TODO: add fields struct paging_state *paging_state; }; static errval_t elf_allocate( void *state, genvaddr_t base, size_t size, uint32_t flags, void **ret ) { errval_t err; struct allocate_state *st = state; lvaddr_t sv = ROUND_DOWN((lvaddr_t)base, BASE_PAGE_SIZE); lvaddr_t lv = ROUND_UP((lvaddr_t)base + size, BASE_PAGE_SIZE); // Allocate a frame struct capref frame; err = slot_alloc(&frame); if (err_is_fail(err)) return err_push(err, LIB_ERR_SLOT_ALLOC); size_t alloc_bytes; err = frame_create(frame, lv - sv, &alloc_bytes); if (err_is_fail(err)) { slot_free(frame); return err_push(err, LIB_ERR_FRAME_ALLOC); } // Map into current address space err = paging_map_frame_attr( get_current_paging_state(), ret, alloc_bytes, frame, VREGION_FLAGS_READ_WRITE ); if (err_is_fail(err)) return err; *ret += base - sv; // translate elf flags to paging flags uint32_t paging_flags = 0; if (flags & PF_X) paging_flags |= KPI_PAGING_FLAGS_EXECUTE; if (flags & PF_W) paging_flags |= KPI_PAGING_FLAGS_WRITE; if (flags & PF_R) paging_flags |= KPI_PAGING_FLAGS_READ; // Map into child address space // debug_printf("SPAWN: allocate base:%"PRIxPTR", size: %"PRIxPTR", flags: %"PRIu32"\n", // base, size, paging_flags); err = paging_map_fixed_attr( st->paging_state, sv, frame, alloc_bytes, paging_flags ); if (err_is_fail(err)) return err; // TODO: Keep track of the mapped regions, so you can unmap them later. return SYS_ERR_OK; } /** * (M2): Implement this function. * \brief Spawn a new dispatcher called 'argv[0]' with 'argc' arguments. * * This function spawns a new dispatcher running the ELF binary called * 'argv[0]' with 'argc' - 1 additional arguments. It fills out 'si' * and 'pid'. * * \param argc The number of command line arguments. Must be > 0. * \param argv An array storing 'argc' command line arguments. * \param si A pointer to the spawninfo struct representing * the child. It will be filled out by this function. Must not be NULL. * \param pid A pointer to a domainid_t variable that will be * assigned to by this function. Must not be NULL. * \return Either SYS_ERR_OK if no error occured or an error * indicating what went wrong otherwise. */ errval_t spawn_load_argv(int argc, char *argv[], struct spawninfo *si, domainid_t *pid) { errval_t err; // - Initialize the spawn_info struct // TODO // - Get the module from the multiboot image // and map it (take a look at multiboot.c) debug_printf("[spawn_load_argv]: argc: %d, argv: ", argc); for (int i = 0; i < argc; ++i) { printf("\"%s\", ", argv[i]); } printf("\n"); assert(argc > 0); assert(argc < MAX_CMDLINE_ARGS); struct mem_region *module = multiboot_find_module(bi, argv[0]); if (module == NULL) return SPAWN_ERR_FIND_MODULE; size_t elf_bytes = module->mrmod_size; // Get the frame of the ELF struct capref child_frame = { .cnode = cnode_module, .slot = module->mrmod_slot, }; struct capability c; err = cap_direct_identify(child_frame, &c); if (err_is_fail(err)) return err; gensize_t frame_size = get_size(&c); if (frame_size < elf_bytes) return SPAWN_ERR_MODULE_FRAME_TOO_SMALL; // Map the ELF into the current address space char *elf_base; err = paging_map_frame_attr( get_current_paging_state(), (void **)&elf_base, frame_size, child_frame, VREGION_FLAGS_READ ); if (err_is_fail(err)) return err_push(err, SPAWN_ERR_MAP_MODULE); debug_printf("SPAWN: mapped %s, size: %"PRIuPTR", magic: %"PRIx8" %c%c%c\n", argv[0], elf_bytes, elf_base[0], elf_base[1], elf_base[2], elf_base[3]); // - Setup the child's cspace // afeer: script page 84/85 err = cnode_create_l1(&si->cspace_l1_cnode_cap, &si->cspace_l1_cnode_info); if (err_is_fail(err)) return err; err = cnode_create_foreign_l2(si->cspace_l1_cnode_cap, ROOTCN_SLOT_TASKCN, &si->cspace_l2_cnode_taskcn); if (err_is_fail(err)) return err; // afeer: (script page 84) is this correct? si->cspace_cap_selfep.cnode = si->cspace_l2_cnode_taskcn; si->cspace_cap_selfep.slot = TASKCN_SLOT_SELFEP; si->cspace_cap_dispatcher.cnode = si->cspace_l2_cnode_taskcn; si->cspace_cap_dispatcher.slot = TASKCN_SLOT_DISPATCHER; si->cspace_cap_rootcn.cnode = si->cspace_l2_cnode_taskcn; si->cspace_cap_rootcn.slot = TASKCN_SLOT_ROOTCN; si->cspace_cap_dispframe.cnode = si->cspace_l2_cnode_taskcn; si->cspace_cap_dispframe.slot = TASKCN_SLOT_DISPFRAME; si->cspace_cap_argspage.cnode = si->cspace_l2_cnode_taskcn; si->cspace_cap_argspage.slot = TASKCN_SLOT_ARGSPAGE; err = cnode_create_foreign_l2(si->cspace_l1_cnode_cap, ROOTCN_SLOT_SLOT_ALLOC0, &si->cspace_l2_cnode_slot_alloc_0); if (err_is_fail(err)) return err; err = cnode_create_foreign_l2(si->cspace_l1_cnode_cap, ROOTCN_SLOT_SLOT_ALLOC1, &si->cspace_l2_cnode_slot_alloc_1); if (err_is_fail(err)) return err; err = cnode_create_foreign_l2(si->cspace_l1_cnode_cap, ROOTCN_SLOT_SLOT_ALLOC2, &si->cspace_l2_cnode_slot_alloc_2); if (err_is_fail(err)) return err; err = cnode_create_foreign_l2(si->cspace_l1_cnode_cap, ROOTCN_SLOT_BASE_PAGE_CN, &si->cspace_l2_cnode_base_pagecn); if (err_is_fail(err)) return err; err = cnode_create_foreign_l2(si->cspace_l1_cnode_cap, ROOTCN_SLOT_PAGECN, &si->cspace_l2_cnode_pagecn); if (err_is_fail(err)) return err; // afeer: populate some capabilities struct capref dispatcher; err = slot_alloc(&dispatcher); if (err_is_fail(err)) return err_push(err, LIB_ERR_SLOT_ALLOC); err = dispatcher_create(dispatcher); if (err_is_fail(err)) return err; err = cap_copy(si->cspace_cap_dispatcher, dispatcher); if (err_is_fail(err)) return err_push(err, LIB_ERR_CAP_COPY_FAIL); err = cap_retype(si->cspace_cap_selfep, si->cspace_cap_dispatcher, 0, ObjType_EndPointLMP, 0, 1); if (err_is_fail(err)) return err; // - Setup the child's vspace // afeer: create level 0 page table // the l0 page table is in the first slot (PAGECN_SLOT_VROOT) of the pagecn cnode. // lower-level pagetables and mappings are stored on different slots of the pagecn cnode. si->vspace_cap_l0_pagetable.cnode = si->cspace_l2_cnode_pagecn; si->vspace_cap_l0_pagetable.slot = PAGECN_SLOT_VROOT; err = vnode_create(si->vspace_cap_l0_pagetable, ObjType_VNode_AARCH64_l0); if (err_is_fail(err)) return err; // rueegges: initialize the foreign paging state struct paging_state child_paging_state; // 64 * 1024 is enough to catch null pointers and also enough to not conflict with child starting paging_alloc at VADDR_OFFSET err = paging_init_state_foreign(&child_paging_state, 64 * 1024, si->vspace_cap_l0_pagetable, get_default_slot_allocator()); if (err_is_fail(err)) return err_push(err, SPAWN_ERR_PAGING_INIT); // - Load the ELF binary struct allocate_state st = { .paging_state = &child_paging_state, }; genvaddr_t entry; err = elf_load(EM_AARCH64, elf_allocate, &st, (lvaddr_t)elf_base, elf_bytes, &entry); if (err_is_fail(err)) return err_push(err, SPAWN_ERR_ELF_LOAD); uint64_t got_addr = 0; struct Elf64_Shdr* got_shdr = elf64_find_section_header_name((lvaddr_t)elf_base, elf_bytes, ".got"); if (got_shdr) { got_addr = got_shdr->sh_addr; } // - Setup the dispatcher err = frame_create(si->cspace_cap_dispframe, DISPATCHER_FRAME_SIZE, NULL); if (err_is_fail(err)) { return err_push(err, SPAWN_ERR_CREATE_DISPATCHER_FRAME); } dispatcher_handle_t handle; err = paging_map_frame_attr( get_current_paging_state(), (void **)&handle, DISPATCHER_FRAME_SIZE, si->cspace_cap_dispframe, VREGION_FLAGS_READ_WRITE ); if (err_is_fail(err)) return err_push(err, SPAWN_ERR_MAP_DISPATCHER_TO_SELF); dispatcher_handle_t handle_child; err = paging_map_frame_attr( &child_paging_state, (void **)&handle_child, DISPATCHER_FRAME_SIZE, si->cspace_cap_dispframe, VREGION_FLAGS_READ_WRITE ); if (err_is_fail(err)) return err_push(err, SPAWN_ERR_MAP_DISPATCHER_TO_NEW); struct dispatcher_shared_generic *disp = get_dispatcher_shared_generic(handle); struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle); arch_registers_state_t *enabled_area = dispatcher_get_enabled_save_area(handle); arch_registers_state_t *disabled_area = dispatcher_get_disabled_save_area(handle); // core id of the process disp_gen->core_id = disp_get_core_id(); // Virtual address of the dispatcher frame in child's VSpace disp->udisp = handle_child; // Start in disabled mode disp->disabled = 1; // A name (for debugging) strncpy(disp->name, argv[0], DISP_NAME_LEN); // Set program counter (where it should start to execute) disabled_area->named.pc = entry; // Initialize offset registers // got_addr is the address of the .got in the child's VSpace armv8_set_registers((void *)got_addr, handle, enabled_area, disabled_area); // we won't use error handling frames disp_gen->eh_frame = 0; disp_gen->eh_frame_size = 0; disp_gen->eh_frame_hdr = 0; disp_gen->eh_frame_hdr_size = 0; // - Setup the environment // afeer: create a frame (=?= page) for the arguments err = frame_create(si->cspace_cap_argspage, ARGS_SIZE, NULL); if (err_is_fail(err)) return err_push(err, SPAWN_ERR_CREATE_ARGSPG); // afeer: map frame to self char * arguments_page_in_self; err = paging_map_frame_attr( get_current_paging_state(), (void **) &arguments_page_in_self, ARGS_SIZE, si->cspace_cap_argspage, VREGION_FLAGS_READ_WRITE); if (err_is_fail(err)) return err_push(err, SPAWN_ERR_MAP_ARGSPG_TO_SELF); // afeer: zero the page content memset(arguments_page_in_self, 0, ARGS_SIZE); // afeer: map frame to new process lvaddr_t arguments_page_in_child; err = paging_map_frame_attr( &child_paging_state, (void **) &arguments_page_in_child, ARGS_SIZE, si->cspace_cap_argspage, VREGION_FLAGS_READ_WRITE); if (err_is_fail(err)) return err_push(err, SPAWN_ERR_MAP_ARGSPG_TO_NEW); // afeer: put the arguments into the argspage struct spawn_domain_params * domain_params = (struct spawn_domain_params *) arguments_page_in_self; domain_params->argc = argc; // afeer: TODO: what do i put here? do we need to set these fields to some value? // domain_params->vspace_buf = NULL; // domain_params->vspace_buf_len = 0; // domain_params->tls_init_base = NULL; // domain_params->tls_init_len = 0; // domain_params->tls_total_len = 0; // domain_params->pagesize = BASE_PAGE_SIZE; size_t offset = sizeof(struct spawn_domain_params); for (int i = 0; i < argc; ++i) { // afeer: put the argument into page in self size_t argument_length = strlen(argv[i]); strncpy(argv[i], arguments_page_in_self + offset, argument_length); // afeer: specify the child-space address of the argument domain_params->argv[i] = (char *) arguments_page_in_child + offset; offset += argument_length + 1; } assert(offset <= ARGS_SIZE); // afeer: null-terminate the arg list domain_params->argv[argc] = NULL; domain_params->envp[0] = NULL; // afeer: script page 104: set the first register using registers_set_param(arch_registers_state_t) registers_set_param(enabled_area, (uint64_t) arguments_page_in_child); // - Make the new dispatcher runnable err = invoke_dispatcher( dispatcher, cap_dispatcher, si->cspace_l1_cnode_cap, si->vspace_cap_l0_pagetable, si->cspace_cap_dispframe, true ); if (err_is_fail(err)) return err_push(err, SPAWN_ERR_RUN); return SYS_ERR_OK; } /** * (M2): Implement this function. * \brief Spawn a new dispatcher executing 'binary_name' * * \param binary_name The name of the binary. * \param si A pointer to a spawninfo struct that will be * filled out by spawn_load_by_name. Must not be NULL. * \param pid A pointer to a domainid_t that will be * filled out by spawn_load_by_name. Must not be NULL. * * \return Either SYS_ERR_OK if no error occured or an error * indicating what went wrong otherwise. */ errval_t spawn_load_by_name(char *binary_name, struct spawninfo * si, domainid_t *pid) { errval_t err; // - Get the mem_region from the multiboot image struct mem_region * module = multiboot_find_module(bi, binary_name); if (module == NULL) return SPAWN_ERR_FIND_MODULE; // - Fill in argc/argv from the multiboot command line const char * c_arguments_str = multiboot_module_opts(module); // printf("c_arguments_str: %s\n", c_arguments_str); char raw_arguments_str[strlen(c_arguments_str)]; strcpy(raw_arguments_str, c_arguments_str); // printf("raw_arguments_str: %s\n", raw_arguments_str); // afeer: explode the raw_arguments_str into argv char * argv[MAX_CMDLINE_ARGS]; int argc = 0; char * token = strtok(raw_arguments_str, " "); while (token != NULL) { argv[argc] = token; token = strtok(NULL, " "); argc += 1; } // - Call spawn_load_argv err = spawn_load_argv(argc, argv, si, pid); if (err_is_fail(err)) return err; // add to process list if (spawn_process_list != NULL) { si->next = spawn_process_list->next; } spawn_process_list = si; return SYS_ERR_OK; }