aos/lib/spawn/spawn.c
2022-03-23 13:35:02 +00:00

416 lines
15 KiB
C

#include <ctype.h>
#include <string.h>
#include <aos/aos.h>
#include <spawn/spawn.h>
#include <elf/elf.h>
#include <aos/dispatcher_arch.h>
#include <aos/lmp_chan.h>
#include <aos/aos_rpc.h>
#include <barrelfish_kpi/paging_arm_v8.h>
#include <barrelfish_kpi/domain_params.h>
#include <spawn/multiboot.h>
#include <spawn/argv.h>
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(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 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
si->next = spawn_process_list;
spawn_process_list = si;
return SYS_ERR_OK;
}