# HG changeset patch # User Paul Boddie # Date 1676569523 -3600 # Node ID 0ca26cb6e94d7df1ee76de0d38f07748abbc7977 # Parent 8d5e22dc717acd4b237427c612ead6efeb4bac19 Moved process creation functionality into libexec. diff -r 8d5e22dc717a -r 0ca26cb6e94d libexec/include/exec/process_creator.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libexec/include/exec/process_creator.h Thu Feb 16 18:45:23 2023 +0100 @@ -0,0 +1,89 @@ +/* + * Support for executing code in new tasks and threads. + * + * Copyright (C) 2022, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + + + +/* Process creator. */ + +class ProcessCreator +{ +protected: + const char *_rm_filename; + + /* External pager configuration. */ + + ExternalPager _exec_pager; + ipc_server_config_type _config; + + Process _process; + + /* Stack and payload descriptions. */ + + ExplicitSegment _rm_stack; + Payload *_rm_payload; + + ExplicitSegment _program_stack; + Payload *_program_payload; + + /* IPC gate for communication within the created task, plus allocated + capability. */ + + l4_cap_idx_t _ipc_gate, _ipc_gate_cap; + + /* Utility methods. */ + + long start_pager(); + + long init_region_mapper(); + + long init_program(file_t *file); + + long init_external_pager(); + + long configure_task(); + + long create_ipc_gate(); + + void init_region(struct exec_region *regions, + struct ipc_mapped_cap *mapped_caps, + struct exec_region &r, unsigned int &index); + + long start_region_mapper(); + + long start_program(int argc, const char *argv[]); + +public: + explicit ProcessCreator(const char *rm_filename); + + long start(file_t *file, int argc, const char *argv[]); +}; + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r 8d5e22dc717a -r 0ca26cb6e94d libexec/include/exec/stack.h --- a/libexec/include/exec/stack.h Thu Feb 16 18:44:41 2023 +0100 +++ b/libexec/include/exec/stack.h Thu Feb 16 18:45:23 2023 +0100 @@ -68,11 +68,11 @@ /* Internal stack population methods. */ - void push_string(char *s); + void push_string(const char *s); - void push_env(char *envp[]); + void push_env(const char *envp[]); - void push_args(int argc, char *argv[]); + void push_args(int argc, const char *argv[]); void push_l4re_aux(); @@ -84,11 +84,11 @@ void push_regions(); - offset_t write_address(char *arg, char **addr, char *s); + offset_t write_address(char *arg, char **addr, const char *s); - void push_envp(char *envp[]); + void push_envp(const char *envp[]); - void push_argv(int argc, char *argv[]); + void push_argv(int argc, const char *argv[]); l4_addr_t align_stack(); @@ -103,7 +103,7 @@ void set_l4re_env(l4re_env_t *env); - void populate(int argc, char *argv[], char *envp[]); + void populate(int argc, const char *argv[], const char *envp[]); l4_addr_t start_address(); }; diff -r 8d5e22dc717a -r 0ca26cb6e94d libexec/lib/src/Makefile --- a/libexec/lib/src/Makefile Thu Feb 16 18:44:41 2023 +0100 +++ b/libexec/lib/src/Makefile Thu Feb 16 18:45:23 2023 +0100 @@ -30,7 +30,8 @@ PLAIN_SRC_CC = \ common.cc elf.cc external_pager.cc \ internal_pager.cc memory.cc pager.cc \ - process.cc segment.cc stack.cc + process.cc process_creator.cc segment.cc \ + stack.cc # Normal definitions. diff -r 8d5e22dc717a -r 0ca26cb6e94d libexec/lib/src/memory.cc --- a/libexec/lib/src/memory.cc Thu Feb 16 18:44:41 2023 +0100 +++ b/libexec/lib/src/memory.cc Thu Feb 16 18:45:23 2023 +0100 @@ -37,6 +37,12 @@ { file_t *file = client_open(filename, O_RDONLY); + if (file == NULL) + return -L4_EIO; + + if (!client_opened(file)) + return file->error; + return exec_get_payload_file(file, payload, attach); } diff -r 8d5e22dc717a -r 0ca26cb6e94d libexec/lib/src/process_creator.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libexec/lib/src/process_creator.cc Thu Feb 16 18:45:23 2023 +0100 @@ -0,0 +1,310 @@ +/* + * Support for executing code in new tasks and threads. + * + * Copyright (C) 2022, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include + +#include +#include + +#include + +#include +#include + +#include "parent_pager_object_server.h" +#include "process_creator.h" + + + +/* Process stack configuration. */ + +static const offset_t initial_stack_size = 16 * L4_PAGESIZE; + + + +/* Initialise the process creator with the details of a region mapper. */ + +ProcessCreator::ProcessCreator(const char *rm_filename) +: _rm_filename(rm_filename), + _exec_pager(0, 10 * L4_PAGESIZE), + _rm_stack(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW), + _program_stack(Utcb_area_start - initial_stack_size * 2, initial_stack_size, L4_FPAGE_RW) +{ +} + +/* Start the system pager in a separate thread. */ + +long ProcessCreator::start_pager() +{ + pthread_t pager_thread; + pthread_attr_t attr; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + ipc_server_init_for(&_config, ParentPagerObject, &_exec_pager); + + long err = pthread_create(&pager_thread, &attr, ipc_server_start_mainloop, &_config); + + if (err) + return err; + + return ipc_server_start_config_thread(&_config, pthread_l4_cap(pager_thread)); +} + +/* Initialise the memory segments of the region mapper. These are mapped into + this task so that we may access them, allowing the external pager in this + task to use them. */ + +long ProcessCreator::init_region_mapper() +{ + long err = exec_get_payload(_rm_filename, &_rm_payload, true); + + if (err) + return err; + + return _rm_stack.allocate(true); +} + +/* Initialise the memory segments of the actual program. These are not mapped + into this task, instead being accessed by the region mapper in the new + task. */ + +long ProcessCreator::init_program(file_t *file) +{ + long err = exec_get_payload_file(file, &_program_payload, false); + + if (err) + return err; + + return _program_stack.allocate(true); +} + +/* Initialise an external system-level pager serving the region mapper in a + created task. The allocated regions requested by the region mapper are + constrained to an area of memory that must not overlap with the area reserved + for the program being run. */ + +long ProcessCreator::init_external_pager() +{ + /* Initialise pager regions for the region mapper. */ + + for (unsigned int i = 0; i < _rm_payload->segments(); i++) + { + if (_rm_payload->segment(i)->loadable()) + _exec_pager.add(_rm_payload->segment(i)->region()); + } + + /* Include the region mapper's stack region. */ + + _exec_pager.add(_rm_stack.region()); + + /* Start the pager in a separate thread. */ + + return start_pager(); +} + +/* Configure the environment for the task. */ + +long ProcessCreator::configure_task() +{ + long err = _process.configure_task(); + + if (err) + return err; + + return _process.set_parent(_config.server); +} + +/* Create an unbound IPC gate for the region mapper and allocate it in the + created process. */ + +long ProcessCreator::create_ipc_gate() +{ + _ipc_gate_cap = _process.allocate_cap(); + _ipc_gate = ipc_cap_alloc(); + + if (l4_is_invalid_cap(_ipc_gate)) + return -L4_ENOMEM; + + return l4_error(l4_factory_create_gate(l4re_env()->factory, _ipc_gate, L4_INVALID_CAP, 0)); +} + +/* Initialise and assign a region in a list to the created process. */ + +void ProcessCreator::init_region(struct exec_region *regions, + struct ipc_mapped_cap *mapped_caps, + struct exec_region &r, unsigned int &index) +{ + l4_cap_idx_t mapped_cap = _process.allocate_cap(); + + mapped_caps[index] = (struct ipc_mapped_cap) {mapped_cap, r.ds, L4_CAP_FPAGE_RWS, 0}; + + /* Change the region definition to use the allocated capability in the created + process. */ + + regions[index] = r; + regions[index].ds = mapped_cap; + index++; +} + +/* Initialise the region mapper with details of the payload program regions + and of the associated capabilities. */ + +long ProcessCreator::start_region_mapper() +{ + /* Define regions employing dataspaces to provide program segments. */ + + struct exec_region rm_regions[_rm_payload->segments() + 2]; + + /* Define capabilities for mapping, including region dataspace capabilities, + the stack dataspace capability, and the server capability. */ + + struct ipc_mapped_cap rm_mapped_caps[_rm_payload->segments() + 3]; + + /* Here, the arrays are sized for the maximum number of regions and + capabilities, but in practice only the loadable segments are used, leaving + fewer elements utilised. A terminating entry is employed to indicate the + limit of utilised elements. */ + + unsigned int rm_index = 0; + + for (unsigned int i = 0; i < _program_payload->segments(); i++) + { + Segment *s = _program_payload->segment(i); + + if (s->loadable()) + init_region(rm_regions, rm_mapped_caps, s->exec_region(), rm_index); + } + + /* Introduce the stack region and capability. */ + + init_region(rm_regions, rm_mapped_caps, _program_stack.exec_region(), rm_index); + + /* Terminate the region array. */ + + rm_regions[rm_index] = (struct exec_region) {0, 0, 0, L4_INVALID_CAP}; + + /* Introduce the server capability and terminate the capability array. */ + + rm_mapped_caps[rm_index++] = (struct ipc_mapped_cap) {_ipc_gate_cap, _ipc_gate, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}; + rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {0, L4_INVALID_CAP, 0, 0}; + + /* Map these additional capabilities. */ + + _process.map_capabilities(rm_mapped_caps, false); + + /* Define the IPC gate as an initial capability to be acquired by the region + mapper via the l4re_env API. The capability index is assigned above when + mapping the capability and encoded in the entry below. */ + + l4re_env_cap_entry_t rm_init_caps[] = { + l4re_env_cap_entry_t("server", _ipc_gate_cap, L4_CAP_FPAGE_RWS), + l4re_env_cap_entry_t() + }; + + /* NOTE: Environment vector is currently not defined. */ + + const char *envp[] = {NULL}; + + /* Configure the environment for the thread, specifying the pager (and + exception handler plus region mapper). */ + + long err = _process.configure_thread(_config.server); + + if (err) + return err; + + /* Populate a thread stack with argument and environment details for the + region mapper, plus the initial server capability and region details. */ + + const char *argv[] = {_rm_filename}; + Stack rm_st(_rm_stack); + + rm_st.set_init_caps(rm_init_caps); + rm_st.set_regions(rm_regions); + rm_st.populate(1, argv, envp); + + /* Start the region mapper thread in the appropriate stack. */ + + return _process.thread_start(_rm_payload->entry_point(), rm_st); +} + +long ProcessCreator::start_program(int argc, const char *argv[]) +{ + /* NOTE: Environment vector is currently not defined. */ + + const char *envp[] = {NULL}; + + /* Configure the environment for the thread, specifying the pager (and + exception handler plus region mapper). */ + + long err = _process.configure_thread(_ipc_gate, _ipc_gate_cap); + + if (err) + return err; + + /* Populate a thread stack with argument and environment details for the + actual program. The server capability should be assigned to the region + mapper capability slot already. */ + + Stack program_st(_program_stack); + + program_st.populate(argc, argv, envp); + + /* Start the program thread in the appropriate stack. */ + + return _process.thread_start(_program_payload->entry_point(), program_st); +} + +long ProcessCreator::start(file_t *file, int argc, const char *argv[]) +{ + long err; + + err = init_region_mapper(); + if (err) + return err; + + err = init_program(file); + if (err) + return err; + + err = init_external_pager(); + if (err) + return err; + + err = configure_task(); + if (err) + return err; + + err = create_ipc_gate(); + if (err) + return err; + + err = start_region_mapper(); + if (err) + return err; + + return start_program(argc, argv); +} + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r 8d5e22dc717a -r 0ca26cb6e94d libexec/lib/src/stack.cc --- a/libexec/lib/src/stack.cc Thu Feb 16 18:44:41 2023 +0100 +++ b/libexec/lib/src/stack.cc Thu Feb 16 18:45:23 2023 +0100 @@ -97,7 +97,7 @@ /* Push environment values in reverse order. */ -void Stack::push_string(char *s) +void Stack::push_string(const char *s) { char *arg = (char *) _element; char *arg_last = arg; @@ -112,7 +112,7 @@ /* Push environment values in reverse order. */ -void Stack::push_env(char *envp[]) +void Stack::push_env(const char *envp[]) { _env_top = (char *) _element; _env_entries = 0; @@ -123,7 +123,7 @@ /* Push argument values in reverse order. */ -void Stack::push_args(int argc, char *argv[]) +void Stack::push_args(int argc, const char *argv[]) { _arg_top = (char *) _element; @@ -211,7 +211,7 @@ /* Fill the stack in reverse with an address, returning the size of the value. */ -offset_t Stack::write_address(char *arg, char **addr, char *s) +offset_t Stack::write_address(char *arg, char **addr, const char *s) { offset_t size = round(strlen(s) + 1, sizeof(l4_umword_t)); @@ -222,7 +222,7 @@ /* Populate stack with environment pointers, employing a pointer ordering that is the reverse of the value ordering. */ -void Stack::push_envp(char *envp[]) +void Stack::push_envp(const char *envp[]) { /* Write the terminating element. */ @@ -242,7 +242,7 @@ /* Populate stack with argument pointers and count, employing a pointer ordering that is the reverse of the value ordering. */ -void Stack::push_argv(int argc, char *argv[]) +void Stack::push_argv(int argc, const char *argv[]) { /* Write the terminating element. */ @@ -280,7 +280,7 @@ /* Populate the stack with arguments and initial environment. */ -void Stack::populate(int argc, char *argv[], char *envp[]) +void Stack::populate(int argc, const char *argv[], const char *envp[]) { /* Populate stack with any regions and capabilities. */ diff -r 8d5e22dc717a -r 0ca26cb6e94d tests/dstest_exec.cc --- a/tests/dstest_exec.cc Thu Feb 16 18:44:41 2023 +0100 +++ b/tests/dstest_exec.cc Thu Feb 16 18:45:23 2023 +0100 @@ -1,7 +1,7 @@ /* * Support for executing code in new tasks and threads. * - * Copyright (C) 2022 Paul Boddie + * Copyright (C) 2022, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,54 +23,11 @@ #include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include -#include -#include - -#include "parent_pager_object_server.h" - - - -/* External system-level pager for the region mapper in a created task. The - allocated regions requested by the region mapper are constrained to an area - of memory that must not overlap with the area reserved for the program being - run. */ - -static ExternalPager exec_pager(0, 10 * L4_PAGESIZE); - -static const offset_t initial_stack_size = 16 * L4_PAGESIZE; - - - -/* Start the system pager in a separate thread. */ - -static long start_pager(ipc_server_config_type &config) -{ - pthread_t pager_thread; - pthread_attr_t attr; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - ipc_server_init_for(&config, ParentPagerObject, &exec_pager); - - long err = pthread_create(&pager_thread, &attr, ipc_server_start_mainloop, &config); - - if (err) - return err; - - return ipc_server_start_config_thread(&config, pthread_l4_cap(pager_thread)); -} - int main(int argc, char *argv[]) @@ -79,251 +36,27 @@ if (argc < 3) { - printf("Need a program to run as the region mapper and a main program.\n"); + printf("Need a region mapper and the actual program to run.\n"); return 1; } - /* Define the different payloads. */ - char *rm_filename = argv[1]; char *program_filename = argv[2]; - /* Initialise the memory segments of the region mapper. These are mapped into - this task so that we may access them. */ - - ExplicitSegment rm_stack(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW); - Payload *rm_payload; - - if (exec_get_payload(rm_filename, &rm_payload, true)) - { - printf("Could not initialise region mapper: %s\n", rm_filename); - return 1; - } - - if (rm_stack.allocate(true)) - { - printf("Could not allocate region mapper stack.\n"); - return 1; - } - - /* Initialise the memory segments of the actual program. These are not mapped - into this task, instead being accessed by the region mapper in the new - task. */ - - ExplicitSegment program_stack(Utcb_area_start - initial_stack_size * 2, initial_stack_size, L4_FPAGE_RW); - Payload *program_payload; - - if (exec_get_payload(program_filename, &program_payload, false)) - { - printf("Could not initialise program: %s\n", program_filename); - return 1; - } - - if (program_stack.allocate(true)) - { - printf("Could not allocate program stack.\n"); - return 1; - } - - /* Initialise pager regions for the region mapper. */ - - for (unsigned int i = 0; i < rm_payload->segments(); i++) - { - if (rm_payload->segment(i)->loadable()) - exec_pager.add(rm_payload->segment(i)->region()); - } - - exec_pager.add(rm_stack.region()); - - /* Start the pager in a separate thread. */ - - ipc_server_config_type config; - - printf("Starting pager thread...\n"); + ProcessCreator creator(rm_filename); + file_t *program_file = client_open(program_filename, O_RDONLY); - err = start_pager(config); - - if (err) - { - printf("Could not start pager.\n"); - return 1; - } - - /* Configure the environment for the task, reserving two threads. */ - - Process process; - - err = process.configure_task(); - - if (err) - { - printf("Could not configure task.\n"); - return 1; - } - - /* Configure the environment for the thread, specifying the pager (and - exception handler plus region mapper). */ - - err = process.configure_thread(config.server); - - if (err) + if (!client_opened(program_file)) { - printf("Could not configure thread.\n"); - return 1; - } - - err = process.set_parent(config.server); - - if (err) - { - printf("Could not map parent to task.\n"); - return 1; - } - - /* Create an unbound IPC gate for the region mapper. */ - - l4_cap_idx_t ipc_gate = ipc_cap_alloc(); - - if (l4_is_invalid_cap(ipc_gate)) - { - printf("Could not allocate IPC gate capability.\n"); - return 1; - } - - err = l4_error(l4_factory_create_gate(l4re_env()->factory, ipc_gate, L4_INVALID_CAP, 0)); - - if (err) - { - printf("Could not create IPC gate.\n"); + printf("Could not open program file.\n"); return 1; } - /* Define regions employing dataspaces to provide program segments. - - Define capabilities for mapping, including region dataspace capabilities, - the stack dataspace capability, and the server capability. - - Here, the arrays are sized for the maximum number of regions and - capabilities, but in practice only the loadable segments are used, leaving - fewer elements utilised. A terminating entry is employed to indicate the - limit of utilised elements. */ - - struct exec_region rm_regions[rm_payload->segments() + 2]; - struct ipc_mapped_cap rm_mapped_caps[rm_payload->segments() + 3]; - l4_cap_idx_t mapped_cap; - unsigned int rm_index = 0; - - for (unsigned int i = 0; i < program_payload->segments(); i++) - { - Segment *s = program_payload->segment(i); - - if (s->loadable()) - { - mapped_cap = process.allocate_cap(); - rm_regions[rm_index] = s->exec_region(); - rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {mapped_cap, rm_regions[rm_index].ds, L4_CAP_FPAGE_RWS, 0}; - rm_regions[rm_index].ds = mapped_cap; - rm_index++; - } - } - - /* Introduce the stack region and capability. */ - - mapped_cap = process.allocate_cap(); - rm_regions[rm_index] = program_stack.exec_region(); - rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {mapped_cap, program_stack.exec_region().ds, L4_CAP_FPAGE_RWS, 0}; - rm_regions[rm_index].ds = mapped_cap; - rm_index++; - - /* Terminate the region array. */ - - rm_regions[rm_index] = (struct exec_region) {0, 0, 0, L4_INVALID_CAP}; - - /* Introduce the server capability. */ - - l4_cap_idx_t ipc_gate_cap = process.allocate_cap(); - - printf("Mapping %lx to %lx in task.\n", ipc_gate, ipc_gate_cap); - - rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {ipc_gate_cap, ipc_gate, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}; - rm_index++; - - /* Terminate the capability array. */ - - rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {0, L4_INVALID_CAP, 0, 0}; - - /* Map these additional capabilities. */ - - printf("Map additional capabilities...\n"); - - process.map_capabilities(rm_mapped_caps, false); - - /* Define the IPC gate as an initial capability to be acquired by the region - mapper via the l4re_env API. The capability index is assigned above when - mapping the capability and encoded in the entry below. */ - - l4re_env_cap_entry_t rm_init_caps[] = { - l4re_env_cap_entry_t("server", ipc_gate_cap, L4_CAP_FPAGE_RWS), - l4re_env_cap_entry_t() - }; - - /* NOTE: Environment vector is currently not defined. */ - - char *envp[] = {NULL}; - - /* Populate a thread stack with argument and environment details for the - region mapper, plus the initial server capability and region details. */ - - printf("Populating region mapper stack...\n"); - - Stack rm_st(rm_stack); - - rm_st.set_init_caps(rm_init_caps); - rm_st.set_regions(rm_regions); - rm_st.populate(1, argv + 1, envp); - - /* Start the region mapper thread in the appropriate stack. */ - - printf("Run region mapper thread...\n"); - - err = process.thread_start(rm_payload->entry_point(), rm_st); + err = creator.start(program_file, argc - 2, (const char **) argv + 2); if (err) { - printf("Could not run thread for region mapper.\n"); - return 1; - } - - /* Configure the environment for the thread, specifying the pager (and - exception handler plus region mapper). */ - - err = process.configure_thread(ipc_gate, ipc_gate_cap); - - if (err) - { - printf("Could not configure task.\n"); - return 1; - } - - /* Populate a thread stack with argument and environment details for the - actual program. The server capability should be assigned to the region - mapper capability slot already. */ - - printf("Populating program stack...\n"); - - Stack program_st(program_stack); - - program_st.populate(argc - 2, argv + 2, envp); - - /* Start the program thread in the appropriate stack. */ - - printf("Run program thread...\n"); - - err = process.thread_start(program_payload->entry_point(), program_st); - - if (err) - { - printf("Could not run thread for program: %s\n", l4sys_errtostr(err)); + printf("Could not start process: %s\n", l4sys_errtostr(err)); return 1; }