#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern struct bootinfo *bi; extern coreid_t my_core_id; extern struct aos_urpc urpc_to_bsp; static struct spawninfo *spawn_process_list = NULL; // All PIDs are allocated on the BSP core. // TODO: handle "freeing" of PIDs in case we learn how to detect stopped child processes static domainid_t spawn_next_pid = 1; #define MAX_PID 1023 static char *process_names[1024]; errval_t allocate_pid (const char *process_name, domainid_t *pid) { if (spawn_next_pid > MAX_PID) return SPAWN_ERR_OUT_OF_PIDS; char *name_copy = strdup(process_name); if (name_copy == NULL) return LIB_ERR_MALLOC_FAIL; process_names[spawn_next_pid] = name_copy; debug_printf("Allocated pid %"PRIuDOMAINID" for process \"%s\".\n", spawn_next_pid, process_name); *pid = spawn_next_pid; spawn_next_pid++; return SYS_ERR_OK; } errval_t get_process_name (domainid_t pid, char *process_name, size_t *len) { if (pid == 0 || pid >= spawn_next_pid) return PROC_MGMT_ERR_DOMAIN_TABLE_FIND; size_t actual_len = strlen(process_names[pid]) + 1; if (actual_len > *len) return PROC_MGMT_ERR_NAME_BUF_TOO_SMALL; *len = actual_len; memcpy(process_name, process_names[pid], actual_len); return SYS_ERR_OK; } errval_t get_all_pids (domainid_t *pids, size_t *count) { if (*count < spawn_next_pid - 1) return PROC_MGMT_ERR_PID_BUF_TOO_SMALL; *count = spawn_next_pid - 1; for (size_t i = 0; i < *count; i++) { pids[i] = i + 1; } return SYS_ERR_OK; } /** * \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; } static void handle_child_recv(void *arg) { errval_t err; struct spawninfo *si = (struct spawninfo *)arg; struct lmp_recv_msg msg = LMP_RECV_MSG_INIT; struct capref cap; err = lmp_chan_recv(&si->init_chan, &msg, &cap); assert(err_is_ok(err)); switch(msg.words[0]){ case RPC_MTYPE_CHILD_ENDPOINT: debug_printf("SPAWN: Received child init_chan endpoint\n"); si->init_chan.remote_cap = cap; err = lmp_chan_alloc_recv_slot(&si->init_chan); if (err_is_fail(err)) { USER_PANIC_ERR(err, "Failed to allocate recv slot"); } err = lmp_chan_send1(&si->init_chan, LMP_FLAG_YIELD | LMP_FLAG_SYNC, NULL_CAP, SYS_ERR_OK); if (err_is_fail(err)) { USER_PANIC_ERR(err, "Failed to send reply"); } break; default: USER_PANIC("Unknown rpc message type."); break; } rpc_server_register_recv(&si->rpc_server); } struct temp_mapping { struct temp_mapping *next; void *addr; }; struct allocate_state { struct paging_state *paging_state; struct temp_mapping *temp_mapping_head; }; 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; // 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; // Keep track of the mapped regions, so you can unmap them later. struct temp_mapping *tm = malloc(sizeof(struct temp_mapping)); if (tm == NULL) return LIB_ERR_MALLOC_FAIL; tm->next = st->temp_mapping_head; tm->addr = *ret; st->temp_mapping_head = tm; *ret += base - sv; return SYS_ERR_OK; } static size_t spawn_serialice_vspace_pt_size(struct pt_t *pt) { size_t sum = 0; sum += sizeof(struct pt_t); if(pt->children != NULL) { // children array sum += PT_CHILD_ARRAY_SIZE; for(size_t i = 0; i < PTABLE_ENTRIES; ++i) { if(pt->children[i] != NULL) { sum += spawn_serialice_vspace_pt_size(pt->children[i]); } } } return sum; } // places pt as first thing in buf, then copies all else and increments curr_buf accordingly static errval_t spawn_serialize_pt(struct spawninfo *si, struct pt_t *pt, void *self_buf, void *child_buf, size_t *curr_offset, cslot_t *pagecn_slot) { errval_t err; if (*pagecn_slot >= L2_CNODE_SLOTS - 2) { return SPAWN_ERR_SERIALISE_VSPACE_TOO_LARGE; } // write pt to buffer struct pt_t *pt_child = (struct pt_t *) (self_buf + *curr_offset); memcpy(pt_child, pt, sizeof(struct pt_t)); *curr_offset += sizeof(struct pt_t); // copy capability references to child if(!capref_is_null(pt->cap_pt)) { pt_child->cap_pt.cnode = si->cspace_l2_cnode_pagecn; pt_child->cap_pt.slot = *pagecn_slot; *pagecn_slot = *pagecn_slot + 1; err = cap_copy(pt_child->cap_pt, pt->cap_pt); if (err_is_fail(err)) { return err_push(err, LIB_ERR_CAP_COPY_FAIL); } pt_child->cap_pt.cnode = cnode_page; } if(!capref_is_null(pt->cap_mapping)) { pt_child->cap_mapping.cnode = si->cspace_l2_cnode_pagecn; pt_child->cap_mapping.slot = *pagecn_slot; *pagecn_slot = *pagecn_slot + 1; err = cap_copy(pt_child->cap_mapping, pt->cap_mapping); if (err_is_fail(err)) { return err_push(err, LIB_ERR_CAP_COPY_FAIL); } pt_child->cap_mapping.cnode = cnode_page; } if (pt->children == NULL) { assert(pt_child->children == NULL); return SYS_ERR_OK; } // serialize all children pt_child->children = (struct pt_t **)(child_buf + *curr_offset); struct pt_t ** children_self = (struct pt_t **)(self_buf + *curr_offset); *curr_offset += PT_CHILD_ARRAY_SIZE; for(size_t i = 0; i < PTABLE_ENTRIES; ++i) { if (pt->children[i] == NULL) { children_self[i] = NULL; } else { children_self[i] = (struct pt_t *)(child_buf + *curr_offset); err = spawn_serialize_pt(si, pt->children[i], self_buf, child_buf, curr_offset, pagecn_slot); if (err_is_fail(err)) { return err; } } } return SYS_ERR_OK; } // serializes the vspace in a way that does not require deserialization // vspace_buf->|l0_pt*,vaddr_head*,data....| // so the child can just take the two pointers and store them into its current paging_state static errval_t spawn_serialize_vspace(struct spawninfo *si, struct paging_state *paging_state, struct spawn_domain_params *domain_params) { errval_t err; // first calculate required vspace size size_t pt_space = spawn_serialice_vspace_pt_size(&paging_state->l0_pt); size_t vaddr_space = 0; for(struct pt_vaddr_reg_t *cur_vaddr = &paging_state->vaddr_head; cur_vaddr != NULL; cur_vaddr = cur_vaddr->next) { vaddr_space += sizeof(struct pt_vaddr_reg_t); } size_t pointer_space = 2 * sizeof(void *); size_t total_space = pointer_space + pt_space + vaddr_space; size_t reserve_space = // additional structs for the vaddr allocation that is required 2 * sizeof(struct pt_vaddr_reg_t) + // space for potential l1,l2,l3 page tables 6 * (sizeof(struct pt_t) + PT_CHILD_ARRAY_SIZE) + // space for the page table entries when mapping up to 512 contiguous pages 2 * sizeof(struct pt_t); total_space = ROUND_UP(total_space + reserve_space, BASE_PAGE_SIZE); // require this limit to restrict the ammount of additionally reserved space if(total_space > LARGE_PAGE_SIZE) { return SPAWN_ERR_SERIALISE_VSPACE_TOO_LARGE; } // allocate memory to store the state void *vspace_buf_self; void *vspace_buf_child; err = frame_create(si->cspace_cap_vspace, total_space, NULL); if (err_is_fail(err)) { return err_push(err, SPAWN_ERR_CREATE_VSPACE_BUF_FRAME); } err = paging_map_frame_attr( get_current_paging_state(), &vspace_buf_self, total_space, si->cspace_cap_vspace, VREGION_FLAGS_READ_WRITE ); if (err_is_fail(err)) return err_push(err, SPAWN_ERR_MAP_VSPACE_BUF_TO_SELF); err = paging_map_frame_attr( paging_state, &vspace_buf_child, total_space, si->cspace_cap_vspace, VREGION_FLAGS_READ_WRITE ); if (err_is_fail(err)) return err_push(err, SPAWN_ERR_MAP_VSPACE_BUF_TO_NEW); // first store all the page table structs cslot_t pagecn_slot = PAGECN_SLOT_VROOT + 1; size_t curr_offset = 2 * sizeof(void *); *(void **) vspace_buf_self = vspace_buf_child + curr_offset; err = spawn_serialize_pt(si, &paging_state->l0_pt, vspace_buf_self, vspace_buf_child, &curr_offset, &pagecn_slot); if(err_is_fail(err)) { return err; } // then store all the vaddr region structs *(void **)(vspace_buf_self + sizeof(void *)) = vspace_buf_child + curr_offset; for(struct pt_vaddr_reg_t *cur_vaddr = &paging_state->vaddr_head; cur_vaddr != NULL; cur_vaddr = cur_vaddr->next) { struct pt_vaddr_reg_t *vaddr_child = (struct pt_vaddr_reg_t *)(vspace_buf_self + curr_offset); memcpy(vaddr_child, cur_vaddr, sizeof(struct pt_vaddr_reg_t)); curr_offset += sizeof(struct pt_vaddr_reg_t); if(cur_vaddr->next != NULL) { vaddr_child->next = (struct pt_vaddr_reg_t *)(vspace_buf_child + curr_offset); } } // unmap in self err = paging_unmap(get_current_paging_state(), vspace_buf_self); if (err_is_fail(err)) { return err_push(err, LIB_ERR_PMAP_UNMAP); } assert(curr_offset <= total_space); // debug_printf("SPAWN: Passing vspace info at 0x%lx\n", vspace_buf_child); domain_params->vspace_buf = vspace_buf_child; domain_params->vspace_buf_len = total_space; return SYS_ERR_OK; } static errval_t spawn_load_elf_from_fs(char *path, char **elf_base, size_t *elf_bytes) { int res; debug_printf("[spawn_load_elf_from_fs] %s\n", path); // open the file FILE *f = fopen(path, "r"); if (f == NULL) return FS_ERR_OPEN; // get the number of bytes in the elf res = fseek (f , 0 , SEEK_END); if (res != 0) return FS_ERR_INVALID_FH; *elf_bytes = ftell(f); // go back to the start so we can load to memory rewind (f); // allocate the memory to load the elf *elf_base = malloc(*elf_bytes); if(*elf_base == NULL) return LIB_ERR_MALLOC_FAIL; // load the elf to memory debug_printf("[spawn_load_elf_from_fs] reading elf file with %lu bytes\n", *elf_bytes); size_t read_bytes = fread(*elf_base, 1, *elf_bytes, f); if (read_bytes != *elf_bytes) return FS_ERR_READ; // close the file res = fclose(f); if (res != 0) return FS_ERR_CLOSE; return SYS_ERR_OK; } static errval_t spawn_load_elf_from_multiboot(char *name, char **elf_base, size_t *elf_bytes) { errval_t err; debug_printf("[spawn_load_elf_from_multiboot] %s\n", name); struct mem_region *module = multiboot_find_module(bi, name); if (module == NULL) return SPAWN_ERR_FIND_MODULE; *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 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: multiboot mapped %s, size: %"PRIuPTR", magic: %"PRIx8" %c%c%c\n", name, *elf_bytes, (*elf_base)[0], (*elf_base)[1], (*elf_base)[2], (*elf_base)[3]); 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; if(argc == 0) { debug_printf("[spawn_load_argv] argc == 0 (no arguments)\n"); return SPAWN_ERR_FIND_MODULE; } assert(argc < MAX_CMDLINE_ARGS); // Allocate pid if (my_core_id == 0) { err = allocate_pid(argv[0], pid); if (err_is_fail(err)) return err_push(err, PROC_MGMT_ERR_ALLOCATE_PID); } else { size_t name_len = strlen(argv[0]) + 1; if (name_len > RPC_SHARED_SIZE) return PROC_MGMT_ERR_NAME_TOO_LONG; memcpy(urpc_to_bsp.shared_mem, argv[0], name_len); uintptr_t pid_ret; err = do_aos_urpc( &urpc_to_bsp, RPC_MTYPE_ALLOCATE_PID, NULL_CAP, name_len, 0, 0, NULL, NULL, &pid_ret, NULL ); if (err_is_fail(err)) return err_push(err, PROC_MGMT_ERR_ALLOCATE_PID); *pid = pid_ret; } si->pid = *pid; // - Initialize the spawn_info struct // TODO // copy name to spawninfo struct // - 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"); // if we get an absolute path then we load from the file system, otherwise from the multiboot bool is_multiboot = argv[0][0] != '/'; char *elf_base = NULL; size_t elf_bytes = 0; if (is_multiboot) { err = spawn_load_elf_from_multiboot(argv[0], &elf_base, &elf_bytes); } else { err = spawn_load_elf_from_fs(argv[0], &elf_base, &elf_bytes); } if(err_is_fail(err)) return err; assert(elf_base != NULL); assert(elf_bytes != 0); // - 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_initep.cnode = si->cspace_l2_cnode_taskcn; si->cspace_cap_initep.slot = TASKCN_SLOT_INITEP; 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; si->cspace_cap_vspace.cnode = si->cspace_l2_cnode_taskcn; si->cspace_cap_vspace.slot = TASKCN_SLOT_VSPACE; si->cspace_cap_irq.cnode = si->cspace_l2_cnode_taskcn; si->cspace_cap_irq.slot = TASKCN_SLOT_IRQ; 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 err = cap_copy(si->cspace_cap_rootcn, si->cspace_l1_cnode_cap); if (err_is_fail(err)) return err_push(err, LIB_ERR_CAP_COPY_FAIL); err = slot_alloc(&si->dispatcher); if (err_is_fail(err)) return err_push(err, LIB_ERR_SLOT_ALLOC); err = dispatcher_create(si->dispatcher); if (err_is_fail(err)) return err; err = cap_copy(si->cspace_cap_dispatcher, si->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_push(err, SPAWN_ERR_CREATE_SELFEP); err = cap_copy(si->cspace_cap_irq, cap_irq); if (err_is_fail(err)) return err; // give the child an endpoint to talk to init lmp_chan_init(&si->init_chan); err = endpoint_create(DEFAULT_LMP_BUF_WORDS, &si->init_chan.local_cap, &si->init_chan.endpoint); if (err_is_fail(err)) return err_push(err, LIB_ERR_ENDPOINT_CREATE); err = cap_copy(si->cspace_cap_initep, si->init_chan.local_cap); if (err_is_fail(err)) return err_push(err, LIB_ERR_CAP_COPY_FAIL); si->init_chan.remote_cap = NULL_CAP; // Set up capability arguments. This would ideally be passed somehow as an argument to spawn instead. size_t arg0_base = 0; size_t arg0_size = 0; size_t arg1_base = 0; size_t arg1_size = 0; if (strcmp(argv[0], "enet") == 0) { arg0_base = IMX8X_ENET_BASE; arg0_size = IMX8X_ENET_SIZE; } else if (strcmp(argv[0], "shelly") == 0) { arg0_base = IMX8X_UART3_BASE; arg0_size = IMX8X_UART_SIZE; arg1_base = IMX8X_GIC_DIST_BASE; arg1_size = IMX8X_GIC_DIST_SIZE; } if (strcmp(argv[0], "block_driver_server") == 0) { arg0_base = IMX8X_SDHC2_BASE; arg0_size = IMX8X_SDHC_SIZE; } //TODO: also map GIC (interrupt controller) for shelly si->cspace_l2_cnode_argcn = NULL_CNODE; if (arg0_base != 0) { err = cnode_create_foreign_l2(si->cspace_l1_cnode_cap, ROOTCN_SLOT_ARGCN, &si->cspace_l2_cnode_argcn); if (err_is_fail(err)) return err; struct capref arg0_cap = { .cnode = si->cspace_l2_cnode_argcn, .slot = 0 }; // char buf[256]; // debug_print_cap_at_capref(buf, 256, cap_io); // debug_printf(buf); // printf("\n"); // debug_printf("offset=%x\n", arg0_base - IMX8X_START_DEV_RANGE); err = cap_retype(arg0_cap, cap_io, (arg0_base - IMX8X_START_DEV_RANGE), ObjType_DevFrame, arg0_size, 1); if (err_is_fail(err)) return err_push(err, LIB_ERR_CAP_RETYPE); } if (arg1_base != 0) { struct capref arg1_cap = { .cnode = si->cspace_l2_cnode_argcn, .slot = 1 }; err = cap_retype(arg1_cap, cap_io, (arg1_base - IMX8X_START_DEV_RANGE), ObjType_DevFrame, arg1_size, 1); if (err_is_fail(err)) return err_push(err, LIB_ERR_CAP_RETYPE); } // - 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; struct paging_state *child_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, VADDR_LOWEST_NON_NULL, si->vspace_cap_l0_pagetable, get_default_slot_allocator()); // we use the parent's slab allocators because we want to later free the child paging state's memory child_paging_state->pt_slabs = get_current_paging_state()->pt_slabs; child_paging_state->pt_children_slabs = get_current_paging_state()->pt_children_slabs; 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, .temp_mapping_head = NULL, }; 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; } // debug_printf("SPAWN: self paging state after loading:\n"); // pt_print_state(get_current_paging_state()); if (is_multiboot) { err = paging_unmap(get_current_paging_state(), elf_base); if (err_is_fail(err)) return err_push(err, LIB_ERR_PMAP_UNMAP); } else { // cleanup elf buffer free(elf_base); } while (st.temp_mapping_head != NULL) { struct temp_mapping *tm = st.temp_mapping_head; err = paging_unmap(get_current_paging_state(), tm->addr); if (err_is_fail(err)) return err_push(err, LIB_ERR_PMAP_UNMAP); st.temp_mapping_head = tm->next; free(tm); } // debug_printf("SPAWN: self paging state after unmapping:\n"); // pt_print_state(get_current_paging_state()); // - 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; // Create shared memory for RPC err = frame_alloc(&si->rpc_shared_frame, RPC_SHARED_SIZE, NULL); if (err_is_fail(err)) return err_push(err, LIB_ERR_FRAME_ALLOC); void *rpc_shared_memory; err = paging_map_frame_attr( get_current_paging_state(), &rpc_shared_memory, RPC_SHARED_SIZE, si->rpc_shared_frame, VREGION_FLAGS_READ_WRITE); if (err_is_fail(err)) return err; err = paging_map_frame_attr( child_paging_state, &domain_params->rpc_shared_memory, RPC_SHARED_SIZE, si->rpc_shared_frame, VREGION_FLAGS_READ_WRITE); if (err_is_fail(err)) return err; err = spawn_serialize_vspace(si, child_paging_state, domain_params); if (err_is_fail(err)) { return err_push(err, SPAWN_ERR_SERIALISE_VSPACE); } paging_free_slabs(child_paging_state); // so we don't use it after serializing child_paging_state = NULL; domain_params->argc = argc; 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(arguments_page_in_self + offset, argv[i], 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 registers_set_param(enabled_area, (uint64_t) arguments_page_in_child); err = paging_unmap(get_current_paging_state(), (void *)handle); if (err_is_fail(err)) return err_push(err, LIB_ERR_PMAP_UNMAP); err = paging_unmap(get_current_paging_state(), arguments_page_in_self); if (err_is_fail(err)) return err_push(err, LIB_ERR_PMAP_UNMAP); rpc_server_init(&si->rpc_server, &si->init_chan, rpc_shared_memory); // Receive the child's endpoint for the init channel err = lmp_chan_alloc_recv_slot(&si->init_chan); if (err_is_fail(err)) return err_push(err, LIB_ERR_LMP_ALLOC_RECV_SLOT); err = lmp_chan_register_recv(&si->init_chan, get_default_waitset(), MKCLOSURE(handle_child_recv, si)); if (err_is_fail(err)) return err; // - Make the new dispatcher runnable err = invoke_dispatcher( si->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); // add to process list si->next = spawn_process_list; spawn_process_list = si; return SYS_ERR_OK; } void spawn_parse_cmd(char *cmdline, int *argc, char **argv){ *argc = 0; char *token = strtok(cmdline, " "); while (token != NULL) { argv[*argc] = token; token = strtok(NULL, " "); *argc += 1; } } /** * (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); if(c_arguments_str == NULL) return SPAWN_ERR_MALFORMED_SPAWND_RECORD; // 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; spawn_parse_cmd(raw_arguments_str, &argc, argv); // - Call spawn_load_argv err = spawn_load_argv(argc, argv, si, pid); if (err_is_fail(err)) return err; return SYS_ERR_OK; }