862 lines
31 KiB
C
862 lines
31 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 <aos/aos_urpc.h>
|
|
#include <barrelfish_kpi/paging_arm_v8.h>
|
|
#include <barrelfish_kpi/domain_params.h>
|
|
#include <spawn/multiboot.h>
|
|
#include <spawn/argv.h>
|
|
|
|
#include <maps/imx8x_map.h>
|
|
#include <maps/qemu_map.h>
|
|
|
|
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;
|
|
}
|