void run_benchmark(void *faulter_fn, void *handler_fn, seL4_CPtr done_ep) { int error = sel4utils_start_thread(&fault_handler, (sel4utils_thread_entry_fn) handler_fn, (void *) N_HANDLER_ARGS, (void *) handler_argv, true); ZF_LOGF_IF(error, "Failed to start handler"); if (config_set(CONFIG_KERNEL_RT)) { /* convert the fault handler to passive */ ZF_LOGD("Waiting to convert handler to passive"); seL4_Wait(done_ep, NULL); ZF_LOGD("unbound sc\n"); error = api_sc_unbind(fault_handler.sched_context.cptr); ZF_LOGF_IF(error, "Failed to convert to passive"); } error = sel4utils_start_thread(&faulter, (sel4utils_thread_entry_fn) faulter_fn, (void *) N_FAULTER_ARGS, (void *) faulter_argv, true); ZF_LOGF_IF(error, "Failed to start faulter"); /* benchmark runs */ benchmark_wait_children(done_ep, "faulter", 1); if (config_set(CONFIG_KERNEL_RT)) { /* convert the fault handler to active */ ZF_LOGD("Rebound sc\n"); error = api_sc_bind(fault_handler.sched_context.cptr, fault_handler.tcb.cptr); ZF_LOGF_IF(error, "Failed to convert to active"); } benchmark_wait_children(done_ep, "fault handler", 1); error = seL4_TCB_Suspend(faulter.tcb.cptr); ZF_LOGF_IF(error, "Failed to suspend faulter"); error = seL4_TCB_Suspend(fault_handler.tcb.cptr); ZF_LOGF_IF(error, "Failed to suspend fault handler"); }
/** * Start a thrd */ void thrd_start(thrd_env_t *env, thrd_t *thread, thrd_fn_t entry_point, seL4_Word arg0, seL4_Word arg1, seL4_Word arg2, seL4_Word arg3) { UNUSED int error; seL4_CPtr local_endpoint; if (thread->is_process) { /* copy the local endpoint */ cspacepath_t path; vka_cspace_make_path(&env->vka, thread->local_endpoint.cptr, &path); local_endpoint = sel4utils_copy_cap_to_process(&thread->process, path); } else { local_endpoint = thread->local_endpoint.cptr; } /* If we are starting a process then the first two args are to get us * through the standard 'main' function and end up in invoke_thrd * if we are starting a regular thread then these will be ignored */ create_args(thread->args_strings, thread->args, THRDS_TOTAL_ARGS, 0, invoke_thrd, (seL4_Word) entry_point, local_endpoint, arg0, arg1, arg2, arg3); if (thread->is_process) { thread->process.entry_point = (void*)_start; thread->process.sysinfo = (uintptr_t)sel4_vsyscall; error = sel4utils_spawn_process_v(&thread->process, &env->vka, &env->vspace, THRDS_TOTAL_ARGS, thread->args, 1); assert(error == 0); } else { error = sel4utils_start_thread(&thread->thread, invoke_thrd, (void *) THRDS_TOTAL_ARGS, (void *) thread->args, 1); assert(error == 0); } }
/* Creates a new thread for an IRQ server */ struct irq_server_thread* irq_server_thread_new(vspace_t* vspace, vka_t* vka, seL4_CPtr cspace, seL4_Word priority, simple_t *simple, seL4_Word label, seL4_CPtr sep) { struct irq_server_thread* st; int err; /* Allocate memory for the structure */ st = (struct irq_server_thread*)malloc(sizeof(*st)); if (st == NULL) { return NULL; } st->node = irq_server_node_new(0, MASK(NIRQS_PER_NODE)); if (st->node == NULL) { free(st); return NULL; } /* Initialise structure */ st->delivery_sep = sep; st->label = label; st->next = NULL; /* Create an endpoint to listen on */ err = vka_alloc_notification(vka, &st->notification); if (err) { ZF_LOGE("Failed to allocate IRQ notification endpoint for IRQ server thread\n"); return NULL; } st->node->notification = st->notification.cptr; /* Create the IRQ thread */ err = sel4utils_configure_thread(vka, vspace, vspace, seL4_CapNull, priority, cspace, seL4_NilData, &st->thread); if (err) { ZF_LOGE("Failed to configure IRQ server thread\n"); return NULL; } /* Start the thread */ err = sel4utils_start_thread(&st->thread, (void*)_irq_thread_entry, st, NULL, 1); if (err) { ZF_LOGE("Failed to start IRQ server thread\n"); return NULL; } return st; }
seL4_Error serial_server_parent_spawn_thread(simple_t *parent_simple, vka_t *parent_vka, vspace_t *parent_vspace, uint8_t priority) { const size_t shmem_max_size = SERIAL_SERVER_SHMEM_MAX_SIZE; seL4_Error error; size_t shmem_max_n_pages; cspacepath_t parent_cspace_cspath; seL4_MessageInfo_t tag; if (parent_simple == NULL || parent_vka == NULL || parent_vspace == NULL) { return seL4_InvalidArgument; } memset(get_serial_server(), 0, sizeof(serial_server_context_t)); /* Get a CPtr to the parent's root cnode. */ shmem_max_n_pages = BYTES_TO_4K_PAGES(shmem_max_size); vka_cspace_make_path(parent_vka, 0, &parent_cspace_cspath); get_serial_server()->server_vka = parent_vka; get_serial_server()->server_vspace = parent_vspace; get_serial_server()->server_cspace = parent_cspace_cspath.root; get_serial_server()->server_simple = parent_simple; /* Allocate the Endpoint that the server will be listening on. */ error = vka_alloc_endpoint(parent_vka, &get_serial_server()->server_ep_obj); if (error != 0) { ZF_LOGE(SERSERVP"spawn_thread: failed to alloc endpoint, err=%d.", error); return error; } /* And also allocate a badged copy of the Server's endpoint that the Parent * can use to send to the Server. This is used to allow the Server to report * back to the Parent on whether or not the Server successfully bound to a * platform serial driver. * * This badged endpoint will be reused by the library as the Parent's badged * Endpoint cap, if the Parent itself ever chooses to connect() to the * Server later on. */ get_serial_server()->parent_badge_value = serial_server_badge_value_alloc(); if (get_serial_server()->parent_badge_value == SERIAL_SERVER_BADGE_VALUE_EMPTY) { error = seL4_NotEnoughMemory; goto out; } error = vka_mint_object(parent_vka, &get_serial_server()->server_ep_obj, &get_serial_server()->_badged_server_ep_cspath, seL4_AllRights, seL4_CapData_Badge_new(get_serial_server()->parent_badge_value)); if (error != 0) { ZF_LOGE(SERSERVP"spawn_thread: Failed to mint badged Endpoint cap to " "server.\n" "\tParent cannot confirm Server thread successfully spawned."); goto out; } /* Allocate enough Cnode slots in our CSpace to enable us to receive * frame caps from our clients, sufficient to cover "shmem_max_size". * The problem here is that we're sort of forced to assume that we get * these slots contiguously. If they're not, we have a problem. * * If a client tries to send us too many frames, we respond with an error, * and indicate our shmem_max_size in the SSMSGREG_RESPONSE * message register. */ get_serial_server()->frame_cap_recv_cspaths = calloc(shmem_max_n_pages, sizeof(cspacepath_t)); if (get_serial_server()->frame_cap_recv_cspaths == NULL) { error = seL4_NotEnoughMemory; goto out; } for (size_t i = 0; i < shmem_max_n_pages; i++) { error = vka_cspace_alloc_path(parent_vka, &get_serial_server()->frame_cap_recv_cspaths[i]); if (error != 0) { ZF_LOGE(SERSERVP"spawn_thread: Failed to alloc enough cnode slots " "to receive shmem frame caps equal to %d bytes.", shmem_max_size); goto out; } } error = sel4utils_configure_thread(parent_vka, parent_vspace, parent_vspace, get_serial_server()->server_ep_obj.cptr, priority, parent_cspace_cspath.root, seL4_NilData, &get_serial_server()->server_thread); if (error != 0) { ZF_LOGE(SERSERVP"spawn_thread: sel4utils_configure_thread failed " "with %d.", error); goto out; } error = sel4utils_start_thread(&get_serial_server()->server_thread, &serial_server_main, NULL, NULL, 1); if (error != 0) { ZF_LOGE(SERSERVP"spawn_thread: sel4utils_start_thread failed with " "%d.", error); goto out; } /* When the Server is spawned, it will reply to tell us whether or not it * successfully bound itself to the platform serial device. Block here * and wait for that reply. */ seL4_SetMR(SSMSGREG_FUNC, FUNC_SERVER_SPAWN_SYNC_REQ); tag = seL4_MessageInfo_new(0, 0, 0, SSMSGREG_SPAWN_SYNC_REQ_END); tag = seL4_Call(get_serial_server()->_badged_server_ep_cspath.capPtr, tag); /* Did all go well with the server? */ if (seL4_GetMR(SSMSGREG_FUNC) != FUNC_SERVER_SPAWN_SYNC_ACK) { ZF_LOGE(SERSERVP"spawn_thread: Server thread sync message after spawn " "was not a SYNC_ACK as expected."); error = seL4_InvalidArgument; goto out; } error = seL4_MessageInfo_get_label(tag); if (error != 0) { ZF_LOGE(SERSERVP"spawn_thread: Server thread failed to bind to the " "platform serial device."); goto out; } get_serial_server()->shmem_max_size = shmem_max_size; get_serial_server()->shmem_max_n_pages = shmem_max_n_pages; return 0; out: if (get_serial_server()->frame_cap_recv_cspaths != NULL) { for (size_t i = 0; i < shmem_max_n_pages; i++) { /* Since the array was allocated with calloc(), it was zero'd out. So * those indexes that didn't get allocated will have NULL in them. * Break early on the first index that has NULL. */ if (get_serial_server()->frame_cap_recv_cspaths[i].capPtr == 0) { break; } vka_cspace_free_path(parent_vka, get_serial_server()->frame_cap_recv_cspaths[i]); } } free(get_serial_server()->frame_cap_recv_cspaths); if (get_serial_server()->_badged_server_ep_cspath.capPtr != 0) { vka_cspace_free_path(parent_vka, get_serial_server()->_badged_server_ep_cspath); } if (get_serial_server()->parent_badge_value != SERIAL_SERVER_BADGE_VALUE_EMPTY) { serial_server_badge_value_free(get_serial_server()->parent_badge_value); } vka_free_object(parent_vka, &get_serial_server()->server_ep_obj); return error; }
int main(int argc, char **argv) { env_t *env; irquser_results_t *results; vka_object_t endpoint = {0}; static size_t object_freq[seL4_ObjectTypeCount] = { [seL4_TCBObject] = 2, [seL4_EndpointObject] = 1, #ifdef CONFIG_KERNEL_RT [seL4_SchedContextObject] = 2, [seL4_ReplyObject] = 2 #endif }; env = benchmark_get_env(argc, argv, sizeof(irquser_results_t), object_freq); benchmark_init_timer(env); results = (irquser_results_t *) env->results; if (vka_alloc_endpoint(&env->slab_vka, &endpoint) != 0) { ZF_LOGF("Failed to allocate endpoint\n"); } /* set up globals */ done_ep = endpoint.cptr; timer = &env->timer; timer_signal = env->ntfn.cptr; int error = ltimer_reset(&env->timer.ltimer); ZF_LOGF_IF(error, "Failed to start timer"); error = ltimer_set_timeout(&env->timer.ltimer, INTERRUPT_PERIOD_NS, TIMEOUT_PERIODIC); ZF_LOGF_IF(error, "Failed to configure timer"); sel4bench_init(); sel4utils_thread_t ticker, spinner; /* measurement overhead */ ccnt_t start, end; for (int i = 0; i < N_RUNS; i++) { SEL4BENCH_READ_CCNT(start); SEL4BENCH_READ_CCNT(end); results->overheads[i] = end - start; } /* create a frame for the shared time variable so we can share it between processes */ ccnt_t *local_current_time = (ccnt_t *) vspace_new_pages(&env->vspace, seL4_AllRights, 1, seL4_PageBits); if (local_current_time == NULL) { ZF_LOGF("Failed to allocate page"); } /* first run the benchmark between two threads in the current address space */ benchmark_configure_thread(env, endpoint.cptr, seL4_MaxPrio - 1, "ticker", &ticker); benchmark_configure_thread(env, endpoint.cptr, seL4_MaxPrio - 2, "spinner", &spinner); error = sel4utils_start_thread(&ticker, (sel4utils_thread_entry_fn) ticker_fn, (void *) results->thread_results, (void *) local_current_time, true); if (error) { ZF_LOGF("Failed to start ticker"); } char strings[1][WORD_STRING_SIZE]; char *spinner_argv[1]; sel4utils_create_word_args(strings, spinner_argv, 1, (seL4_Word) local_current_time); error = sel4utils_start_thread(&spinner, (sel4utils_thread_entry_fn) spinner_fn, (void *) 1, (void *) spinner_argv, true); assert(!error); benchmark_wait_children(endpoint.cptr, "child of irq-user", 1); /* stop spinner thread */ error = seL4_TCB_Suspend(spinner.tcb.cptr); assert(error == seL4_NoError); error = seL4_TCB_Suspend(ticker.tcb.cptr); assert(error == seL4_NoError); /* now run the benchmark again, but run the spinner in another address space */ /* restart ticker */ error = sel4utils_start_thread(&ticker, (sel4utils_thread_entry_fn) ticker_fn, (void *) results->process_results, (void *) local_current_time, true); assert(!error); sel4utils_process_t spinner_process; benchmark_shallow_clone_process(env, &spinner_process, seL4_MaxPrio - 2, spinner_fn, "spinner"); /* share the current time variable with the spinner process */ void *current_time_remote = vspace_share_mem(&env->vspace, &spinner_process.vspace, (void *) local_current_time, 1, seL4_PageBits, seL4_AllRights, true); assert(current_time_remote != NULL); /* start the spinner process */ sel4utils_create_word_args(strings, spinner_argv, 1, (seL4_Word) current_time_remote); error = sel4utils_spawn_process(&spinner_process, &env->slab_vka, &env->vspace, 1, spinner_argv, 1); if (error) { ZF_LOGF("Failed to start spinner process"); } benchmark_wait_children(endpoint.cptr, "child of irq-user", 1); /* done -> results are stored in shared memory so we can now return */ benchmark_finished(EXIT_SUCCESS); return 0; }