# HG changeset patch # User Paul Boddie # Date 1651619768 -7200 # Node ID 74c69ab2320e8af11682e1ff62c88b5dabf0cf9a # Parent a66c7ca81849b914a505586381d05acabd5e39c4 Introduced various abstractions for the stack and program environment. diff -r a66c7ca81849 -r 74c69ab2320e tests/dstest_exec.cc --- a/tests/dstest_exec.cc Tue May 03 01:01:30 2022 +0200 +++ b/tests/dstest_exec.cc Wed May 04 01:16:08 2022 +0200 @@ -50,6 +50,36 @@ +/* UTCB properties. + See: moe/server/src/loader.cc */ + +enum ipc_exec_utcb +{ + Default_max_threads = 16, +#ifdef ARCH_mips + Utcb_area_start = 0x73000000, +#else + Utcb_area_start = 0xb3000000, +#endif +}; + +static l4_fpage_t get_utcb_fpage() +{ + /* UTCB location and size. */ + + l4_addr_t utcb_start = Utcb_area_start; + int utcb_log2size = page_order(Default_max_threads * L4_UTCB_OFFSET); + + /* Round up to at least one page. */ + + if (utcb_log2size < L4_PAGESHIFT) + utcb_log2size = L4_PAGESHIFT; + + return l4_fpage(utcb_start, utcb_log2size, 0); +} + + + /* Region data structures. */ class Region @@ -395,136 +425,373 @@ -static l4_addr_t populate_stack(Segment &stack, int argc, char *argv[], l4_fpage_t utcb_fpage) -{ - /* Populate stack with additional capabilities. */ - - l4re_env_cap_entry_t *stack_env_cap = (l4re_env_cap_entry_t *) (stack.buf + stack.size); +/* Capability mapping definitions for the new task. */ - /* Special invalid/terminating environment capability entry. */ - - *(--stack_env_cap) = l4re_env_cap_entry_t(); - - printf("Stack region end: %p\n", stack_env_cap); - - l4_addr_t caps_start = (l4_addr_t) stack_env_cap; - l4_umword_t *stack_element = (l4_umword_t *) stack_env_cap; +struct mapped_cap +{ + l4_umword_t index; + l4_cap_idx_t cap; + unsigned char rights; +}; - /* Populate stack with argument values. */ +static long map_capabilities(l4_cap_idx_t task, struct mapped_cap mapped_caps[]) +{ + long err = L4_EOK; + int i = 0; - char *stack_arg = (char *) stack_element; - char *stack_arg_top = stack_arg; - int arg; - - for (arg = argc - 1; arg >= 0; arg--) + while (l4_is_valid_cap(mapped_caps[i].cap) && !err) { - char *stack_arg_last = stack_arg; - - stack_arg -= round(strlen(argv[arg]) + 1, sizeof(l4_umword_t)); - - memset(stack_arg, 0, stack_arg_last - stack_arg); - memcpy(stack_arg, argv[arg], strlen(argv[arg])); - - printf("Stack L4 program argument: %p / %lx\n", stack_arg, stack.region_address(stack_arg)); + err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, + l4_obj_fpage(mapped_caps[i].cap, 0, mapped_caps[i].rights), + l4_map_obj_control(mapped_caps[i].index, L4_MAP_ITEM_MAP))); + i++; } - stack_element = (l4_umword_t *) stack_arg; + return err; +} + + + +/* A stack abstraction. */ + +class Stack +{ + struct auxv_entry + { + l4_umword_t key, value; + }; + +protected: + Segment &_segment; + + /* Next element pointer. */ + + l4_umword_t *_element; + + /* Stack section properties. */ + + l4_addr_t _caps; + char *_arg_top, *_env_top; + char *_auxv_end; + int _env_entries; + +public: + /* Start address and environment structure. */ + + l4_addr_t start; + l4re_env_t *env; + l4_fpage_t utcb_fpage; + + /* Initialise a stack in a memory segment. */ + + explicit Stack(Segment &segment, l4_fpage_t utcb_fpage) + : _segment(segment), utcb_fpage(utcb_fpage) + { + _element = (l4_umword_t *) (segment.buf + segment.size); + } + + /* Push any additional initial capabilities. */ + + void push_cap_entries(l4re_env_cap_entry_t *entries) + { + l4re_env_cap_entry_t *entry = (l4re_env_cap_entry_t *) _element; + + *(--entry) = l4re_env_cap_entry_t(); + + while ((entries != NULL) && (entries->cap != L4_INVALID_CAP)) + *(--entry) = *entries; + + _caps = (l4_addr_t) entry; + _element = (l4_umword_t *) entry; + } + + /* Push environment values in reverse order. */ + + void push_string(char *s) + { + char *arg = (char *) _element; + char *arg_last = arg; + + arg -= round(strlen(s) + 1, sizeof(l4_umword_t)); + + memset(arg, 0, arg_last - arg); + memcpy(arg, s, strlen(s)); + + _element = (l4_umword_t *) arg; + } + + /* Push environment values in reverse order. */ + + void push_env(char *envp[]) + { + _env_top = (char *) _element; + _env_entries = 0; + + for (; *envp != NULL; envp++, _env_entries++) + push_string(*envp); + } + + /* Push argument values in reverse order. */ + + void push_args(int argc, char *argv[]) + { + _arg_top = (char *) _element; + + for (int i = 0; i < argc; i++) + push_string(argv[i]); + } /* Loader flags, debugging flags, and the KIP capability index. See: generate_l4aux in Remote_app_model */ - *(--stack_element) = 0; - *(--stack_element) = 0; - *(--stack_element) = 0x14 << L4_CAP_SHIFT; + void push_l4re_flags() + { + *(--_element) = 0; + *(--_element) = 0; + *(--_element) = 0x14 << L4_CAP_SHIFT; + } + + void push_l4re_env() + { + env = (l4re_env_t *) _element; + env--; - printf("Stack L4 aux elements: %p / %lx\n", stack_element, stack.region_address((char *) stack_element)); + env->factory = L4_BASE_FACTORY_CAP; + env->main_thread = L4_BASE_THREAD_CAP; + env->log = L4_BASE_LOG_CAP; + env->scheduler = L4_BASE_SCHEDULER_CAP; + env->rm = 0x11 << L4_CAP_SHIFT; + env->mem_alloc = 0x12 << L4_CAP_SHIFT; + env->first_free_cap = 0x15; + env->caps = (l4re_env_cap_entry_t *) (_segment.region_address(_caps)); + env->utcb_area = utcb_fpage; + env->first_free_utcb = l4_fpage_memaddr(utcb_fpage) + L4_UTCB_OFFSET; - /* Populate stack with standard capabilities. */ - - l4re_env_t *env = (l4re_env_t *) stack_element; + _element = (l4_umword_t *) env; + } - env--; - env->factory = L4_BASE_FACTORY_CAP; - env->main_thread = L4_BASE_THREAD_CAP; - env->log = L4_BASE_LOG_CAP; - env->scheduler = L4_BASE_SCHEDULER_CAP; - env->rm = 0x11 << L4_CAP_SHIFT; - env->mem_alloc = 0x12 << L4_CAP_SHIFT; - env->first_free_cap = 0x15; - env->caps = (l4re_env_cap_entry_t *) (stack.region_address(caps_start)); - env->utcb_area = utcb_fpage; - env->first_free_utcb = l4_fpage_memaddr(utcb_fpage) + L4_UTCB_OFFSET; + /* Push the auxiliary vector. */ + + void push_auxv() + { + _auxv_end = (char *) _element; + + struct auxv_entry *auxv_base = (struct auxv_entry *) _element; + + /* AUXV NULL. */ + + *(--auxv_base) = {0, 0}; - stack_element = (l4_umword_t *) env; + /* L4Re global environment pointer. */ - /* Populate stack with AUXV. */ - - /* AUXV NULL. */ + *(--auxv_base) = {0xf1, _segment.region_address((char *) env)}; - *(--stack_element) = 0; - *(--stack_element) = 0; + /* Apparently required entries. + NOTE: The user/group identifiers should be obtained from the broader + environment. */ - /* L4Re global environment pointer. */ - - *(--stack_element) = stack.region_address((char *) env); - *(--stack_element) = 0xf1; + *(--auxv_base) = {AT_PAGESZ, L4_PAGESIZE}; + *(--auxv_base) = {AT_UID, 0}; + *(--auxv_base) = {AT_EUID, 0}; + *(--auxv_base) = {AT_GID, 0}; + *(--auxv_base) = {AT_EGID, 0}; - /* Apparently required entries. */ + _element = (l4_umword_t *) auxv_base; + } + + /* Fill the stack in reverse with an address, returning the size of the + value. */ - *(--stack_element) = L4_PAGESIZE; - *(--stack_element) = AT_PAGESZ; + offset_t write_address(char *arg, char **addr, char *s) + { + offset_t size = round(strlen(s) + 1, sizeof(l4_umword_t)); - *(--stack_element) = 0; - *(--stack_element) = AT_UID; + *addr = (char *) _segment.region_address(arg - size); + return size; + } - *(--stack_element) = 0; - *(--stack_element) = AT_EUID; + /* Populate stack with environment pointers, employing a pointer ordering + that is the reverse of the value ordering. */ + + void push_envp(char *envp[]) + { + /* Write the terminating element. */ - *(--stack_element) = 0; - *(--stack_element) = AT_GID; + *(--_element) = 0; - *(--stack_element) = 0; - *(--stack_element) = AT_EGID; + /* Reserve space and fill the stack from the top inwards. */ + + char **ep = (char **) (_element - _env_entries); + char *arg = _env_top; - l4_addr_t stack_data_end = (l4_addr_t) stack_element; + for (; *envp != NULL; envp++, ep++) + arg -= write_address(arg, ep, *envp); - printf("Stack L4 data: %lx / %lx\n", stack_data_end, stack.region_address(stack_data_end)); + _element -= _env_entries; + } - /* No environment pointers. */ + /* Populate stack with argument pointers and count, employing a pointer + ordering that is the reverse of the value ordering. */ - *(--stack_element) = 0; + void push_argv(int argc, char *argv[]) + { + /* Write the terminating element. */ - /* Populate stack with argument pointers and count. */ + *(--_element) = 0; - *(--stack_element) = 0; + /* Reserve space and fill the stack from the top inwards. */ + + char **ap = (char **) (_element - argc); + char *arg = _arg_top; - stack_arg = stack_arg_top; + for (int i = 0; i < argc; i++) + arg -= write_address(arg, &ap[i], argv[i]); - for (arg = argc - 1; arg >= 0; arg--) - { - stack_arg -= round(strlen(argv[arg]) + 1, sizeof(l4_umword_t)); - *(--stack_element) = (l4_umword_t) (stack.region_address(stack_arg)); + /* Write the count. */ + + _element -= argc; + + *(--_element) = argc; } - *(--stack_element) = argc; + /* Adjust the stack alignment and return the stack address. */ + + l4_addr_t align_stack() + { + char *current = (char *) _element; + char *adjusted = Ldr::adjust_sp(current, NULL); + + if (adjusted != current) + memmove(adjusted, (const void *) current, _auxv_end - current); + + _element = (l4_umword_t *) adjusted; + + return _segment.region_address(adjusted); + } + + /* Populate stack with additional capabilities. */ - char *stack_adjusted = Ldr::adjust_sp((char *) stack_element, NULL); + void populate(int argc, char *argv[], char *envp[]) + { + push_cap_entries(NULL); + + /* Populate stack with environment and argument values. */ + + push_env(envp); + push_args(argc, argv); + + /* Push L4Re flags, environment and auxiliary vector. */ + + push_l4re_flags(); + push_l4re_env(); + push_auxv(); + + /* Push environment and argument pointers. */ - /* Adjust the stack alignment. */ + push_envp(envp); + push_argv(argc, argv); + + /* Adjust the stack alignment. */ + + start = align_stack(); + } +}; + + - if (stack_adjusted != (char *) stack_element) - memmove(stack_adjusted, (const void *) stack_element, stack_data_end - (l4_addr_t) stack_element); +class Environment +{ +protected: + Stack &_st; + l4_cap_idx_t task = L4_INVALID_CAP, thread = L4_INVALID_CAP; + l4_cap_idx_t _pager_cap = 0x10 << L4_CAP_SHIFT; + +public: + explicit Environment(Stack &st) + : _st(st) + { + } + + long create_task() + { + task = ipc_cap_alloc(); + + if (l4_is_invalid_cap(task)) + return -L4_ENOMEM; + + return l4_error(l4_factory_create_task(l4re_env()->factory, task, _st.utcb_fpage)); + } - l4_umword_t *stack_adjusted_element = (l4_umword_t *) stack_adjusted; + long create_thread() + { + thread = ipc_cap_alloc(); + + if (l4_is_invalid_cap(thread)) + return -L4_ENOMEM; + + return l4_error(l4_factory_create_thread(l4re_env()->factory, thread)); + } + + /* Configure the task environment. */ - printf("%ld %lx %lx\n", stack_adjusted_element[0], stack_adjusted_element[1], stack_adjusted_element[2]); + long configure(l4_cap_idx_t server) + { + /* Map the KIP into the task. */ + + l4_addr_t kip_start = (l4_addr_t) l4re_kip(); + + long err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, + l4_fpage(kip_start, L4_PAGESHIFT, L4_FPAGE_RX), + kip_start)); + + if (err) + return err; + + /* Define capability mappings for the new task. */ + + l4re_env_t *env = _st.env; - l4_addr_t stack_start = stack.region_address(stack_adjusted); + struct mapped_cap mapped_caps[] = { + {L4_BASE_TASK_CAP, task, L4_CAP_FPAGE_RWS}, + {_pager_cap, server, L4_CAP_FPAGE_RWS}, + {env->rm, server, L4_CAP_FPAGE_RWS}, + {env->main_thread, thread, L4_CAP_FPAGE_RWS}, + {env->factory, l4re_env()->factory, L4_CAP_FPAGE_RWS}, + {env->log, l4re_env()->log, L4_CAP_FPAGE_RWS}, + {env->scheduler, l4re_env()->scheduler, L4_CAP_FPAGE_RWS}, + {env->mem_alloc, l4re_env()->mem_alloc, L4_CAP_FPAGE_RWS}, + {0, L4_INVALID_CAP, 0}, + }; + + return map_capabilities(task, mapped_caps); + } + + /* Initialise the thread. */ - printf("Stack L4 start: %p / %lx\n", stack_adjusted, stack.region_address(stack_adjusted)); + long thread_init(l4_addr_t program_start) + { + l4_thread_control_start(); + l4_thread_control_pager(_pager_cap); + l4_thread_control_exc_handler(_pager_cap); + l4_thread_control_bind((l4_utcb_t *) l4_fpage_memaddr(_st.utcb_fpage), task); + + long err = l4_error(l4_thread_control_commit(thread)); + + if (err) + return err; - return stack_start; -} + return l4_error(l4_thread_ex_regs(thread, program_start, _st.start, 0)); + } + + /* Start the thread. */ + + long thread_start() + { + l4_sched_param_t sp = l4_sched_param(L4RE_MAIN_THREAD_PRIO); + + return l4_error(l4_scheduler_run_thread(l4re_env()->scheduler, thread, &sp)); + } +}; @@ -550,48 +817,6 @@ -/* UTCB properties. - See: moe/server/src/loader.cc */ - -enum ipc_exec_utcb -{ - Default_max_threads = 16, -#ifdef ARCH_mips - Utcb_area_start = 0x73000000, -#else - Utcb_area_start = 0xb3000000, -#endif -}; - - - -/* Capability mapping definitions for the new task. */ - -struct mapped_cap -{ - l4_cap_idx_t cap; - unsigned char rights; - l4_umword_t spot; -}; - -static long map_capabilities(l4_cap_idx_t task, struct mapped_cap mapped_caps[]) -{ - long err = L4_EOK; - int i = 0; - - while (l4_is_valid_cap(mapped_caps[i].cap) && !err) - { - err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, - l4_obj_fpage(mapped_caps[i].cap, 0, mapped_caps[i].rights), - l4_map_obj_control(mapped_caps[i].spot, L4_MAP_ITEM_MAP))); - i++; - } - - return err; -} - - - int main(int argc, char *argv[]) { long err; @@ -602,21 +827,6 @@ return 1; } - /* Allocate capabilities for the task and thread. */ - - l4_cap_idx_t caps[2]; - - err = ipc_cap_alloc_many(caps, 2); - - if (err) - { - printf("Could not allocate capabilities.\n"); - return 1; - } - - l4_cap_idx_t &task = caps[0]; - l4_cap_idx_t &thread = caps[1]; - /* Obtain the payload as a dataspace. */ file_t *file = client_open(argv[1], O_RDONLY); @@ -634,7 +844,7 @@ Segment program(0x1000000, 0x28326, L4_FPAGE_RWX, 0, 0x28326); Segment data(0x1029360, 0x8068, L4_FPAGE_RW, 0x28360, 0x2058); - Segment stack(0x80000000, 16 * L4_PAGESIZE, L4_FPAGE_RW); + Segment stack(0x80000000 - 16 * L4_PAGESIZE, 16 * L4_PAGESIZE, L4_FPAGE_RW); Segment *segments[] = {&program, &data, &stack, NULL}; Segment **segment; @@ -657,37 +867,20 @@ } } - if (memcmp(program.buf + program_start - program.region_base, "\x31\xed", 2)) - { - printf("Did not find expected instructions at start.\n"); - return 1; - } - - /* UTCB location and size. */ - - l4_addr_t utcb_start = Utcb_area_start; - int utcb_log2size = page_order(Default_max_threads * L4_UTCB_OFFSET); - - /* Round up to at least one page. */ - - if (utcb_log2size < L4_PAGESHIFT) - utcb_log2size = L4_PAGESHIFT; - - l4_fpage_t utcb_fpage = l4_fpage(utcb_start, utcb_log2size, 0); - - /* KIP allocation. */ - - l4_addr_t kip_start = (l4_addr_t) l4re_kip(); - - printf("KIP at %lx.\n", kip_start); - /* Populate the stack with argument and environment details. */ - l4_addr_t stack_start = populate_stack(stack, argc - 1, argv + 1, utcb_fpage); + Stack st(stack, get_utcb_fpage()); + Environment environment(st); + + /* NOTE: Environment vector is currently not defined. */ + + char *envp[] = {NULL}; + + st.populate(argc - 1, argv + 1, envp); /* Create a new task and thread. */ - err = l4_error(l4_factory_create_task(l4re_env()->factory, task, utcb_fpage)); + err = environment.create_task(); if (err) { @@ -695,7 +888,7 @@ return 1; } - err = l4_error(l4_factory_create_thread(l4re_env()->factory, thread)); + err = environment.create_thread(); if (err) { @@ -703,6 +896,14 @@ return 1; } + err = environment.thread_init(program_start); + + if (err) + { + printf("Could not configure thread.\n"); + return 1; + } + /* Start the pager. */ ipc_server_config_type config; @@ -733,77 +934,22 @@ return 1; } - /* Define capability mappings for the new task. */ + /* Configure the environment for the task, specifying the pager (and exception + handler plus region mapper). */ - struct mapped_cap mapped_caps[] = { - {config.server, L4_CAP_FPAGE_RWS, 0x10 << L4_CAP_SHIFT}, - {config.server, L4_CAP_FPAGE_RWS, 0x11 << L4_CAP_SHIFT}, - {task, L4_CAP_FPAGE_RWS, L4_BASE_TASK_CAP}, - {thread, L4_CAP_FPAGE_RWS, L4_BASE_THREAD_CAP}, - {l4re_env()->factory, L4_CAP_FPAGE_RWS, L4_BASE_FACTORY_CAP}, - {l4re_env()->log, L4_CAP_FPAGE_RWS, L4_BASE_LOG_CAP}, - {l4re_env()->scheduler, L4_CAP_FPAGE_RWS, L4_BASE_SCHEDULER_CAP}, - {l4re_env()->mem_alloc, L4_CAP_FPAGE_RWS, 0x12 << L4_CAP_SHIFT}, - {L4_INVALID_CAP, 0, 0}, - }; - - err = map_capabilities(task, mapped_caps); + err = environment.configure(config.server); if (err) { - printf("Could not capabilities into task.\n"); - return 1; - } - - /* Map the KIP into the task. */ - - err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, - l4_fpage(kip_start, L4_PAGESHIFT, L4_FPAGE_RX), - kip_start)); - - if (err) - { - printf("Could not map KIP into task.\n"); - return 1; - } - - /* Configure the thread with the region manager acting as pager and exception - handler. The UTCB will be situated at an address supported by a dataspace - attached to the new task. */ - - printf("Configure thread...\n"); - - l4_thread_control_start(); - l4_thread_control_pager(0x10 << L4_CAP_SHIFT); - l4_thread_control_exc_handler(0x10 << L4_CAP_SHIFT); - l4_thread_control_bind((l4_utcb_t *) utcb_start, task); - err = l4_error(l4_thread_control_commit(thread)); - - if (err) - { - printf("Could not configure thread.\n"); + printf("Could not map capabilities into task.\n"); return 1; } /* Start the new thread. */ - printf("Schedule thread...\n"); - - printf("Stack at 0x%lx mapped to region at 0x%lx.\n", stack_start, stack.region_base); - - err = l4_error(l4_thread_ex_regs(thread, program_start, stack_start, 0)); - - if (err) - { - printf("Could not set thread registers.\n"); - return 1; - } - printf("Run thread...\n"); - l4_sched_param_t sp = l4_sched_param(L4RE_MAIN_THREAD_PRIO); - - err = l4_error(l4_scheduler_run_thread(l4re_env()->scheduler, thread, &sp)); + err = environment.thread_start(); if (err) {