1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/libexec/lib/src/process_creating.cc Thu Feb 23 23:49:26 2023 +0100
1.3 @@ -0,0 +1,324 @@
1.4 +/*
1.5 + * Support for executing code in new tasks and threads.
1.6 + *
1.7 + * Copyright (C) 2022, 2023 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License as
1.11 + * published by the Free Software Foundation; either version 2 of
1.12 + * the License, or (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 + * Boston, MA 02110-1301, USA
1.23 + */
1.24 +
1.25 +#include <l4/re/env.h>
1.26 +
1.27 +#include <ipc/cap_alloc.h>
1.28 +#include <ipc/map.h>
1.29 +
1.30 +#include <stdio.h>
1.31 +
1.32 +#include <pthread-l4.h>
1.33 +#include <pthread.h>
1.34 +
1.35 +#include "parent_pager_object_server.h"
1.36 +#include "process_creating.h"
1.37 +
1.38 +
1.39 +
1.40 +/* Process stack configuration. */
1.41 +
1.42 +static const offset_t initial_stack_size = 16 * L4_PAGESIZE;
1.43 +
1.44 +
1.45 +
1.46 +/* Initialise the process creator with the details of a region mapper. */
1.47 +
1.48 +ProcessCreating::ProcessCreating(const char *rm_filename)
1.49 +: _rm_filename(rm_filename),
1.50 + _exec_pager(0, 10 * L4_PAGESIZE),
1.51 + _rm_stack(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW),
1.52 + _program_stack(Utcb_area_start - initial_stack_size * 2, initial_stack_size, L4_FPAGE_RW)
1.53 +{
1.54 +}
1.55 +
1.56 +/* Start the system pager in a separate thread. */
1.57 +
1.58 +long ProcessCreating::start_pager()
1.59 +{
1.60 + pthread_t pager_thread;
1.61 + pthread_attr_t attr;
1.62 +
1.63 + pthread_attr_init(&attr);
1.64 + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1.65 +
1.66 + ipc_server_init_for(&_config, ParentPagerObject, &_exec_pager);
1.67 +
1.68 + long err = pthread_create(&pager_thread, &attr, ipc_server_start_mainloop, &_config);
1.69 +
1.70 + if (err)
1.71 + return err;
1.72 +
1.73 + return ipc_server_start_config_thread(&_config, pthread_l4_cap(pager_thread));
1.74 +}
1.75 +
1.76 +/* Initialise the memory segments of the region mapper. These are mapped into
1.77 + this task so that we may access them, allowing the external pager in this
1.78 + task to use them. */
1.79 +
1.80 +long ProcessCreating::init_region_mapper()
1.81 +{
1.82 + long err = exec_get_payload(_rm_filename, &_rm_payload, true);
1.83 +
1.84 + if (err)
1.85 + return err;
1.86 +
1.87 + return _rm_stack.allocate(true);
1.88 +}
1.89 +
1.90 +/* Initialise the memory segments of the actual program. These are not mapped
1.91 + into this task, instead being accessed by the region mapper in the new
1.92 + task. */
1.93 +
1.94 +long ProcessCreating::init_program(file_t *file)
1.95 +{
1.96 + long err = exec_get_payload_file(file, &_program_payload, false);
1.97 +
1.98 + if (err)
1.99 + return err;
1.100 +
1.101 + return _program_stack.allocate(true);
1.102 +}
1.103 +
1.104 +/* Initialise an external system-level pager serving the region mapper in a
1.105 + created task. The allocated regions requested by the region mapper are
1.106 + constrained to an area of memory that must not overlap with the area reserved
1.107 + for the program being run. */
1.108 +
1.109 +long ProcessCreating::init_external_pager()
1.110 +{
1.111 + /* Initialise pager regions for the region mapper. */
1.112 +
1.113 + for (unsigned int i = 0; i < _rm_payload->segments(); i++)
1.114 + {
1.115 + if (_rm_payload->segment(i)->loadable())
1.116 + _exec_pager.add(_rm_payload->segment(i)->region());
1.117 + }
1.118 +
1.119 + /* Include the region mapper's stack region. */
1.120 +
1.121 + _exec_pager.add(_rm_stack.region());
1.122 +
1.123 + /* Start the pager in a separate thread. */
1.124 +
1.125 + return start_pager();
1.126 +}
1.127 +
1.128 +/* Configure the environment for the task. */
1.129 +
1.130 +long ProcessCreating::configure_task()
1.131 +{
1.132 + long err = _process.configure_task();
1.133 +
1.134 + if (err)
1.135 + return err;
1.136 +
1.137 + return _process.set_parent(_config.server);
1.138 +}
1.139 +
1.140 +/* Create an unbound IPC gate for the region mapper and allocate it in the
1.141 + created process. */
1.142 +
1.143 +long ProcessCreating::create_ipc_gate()
1.144 +{
1.145 + _ipc_gate_cap = _process.allocate_cap();
1.146 + _ipc_gate = ipc_cap_alloc();
1.147 +
1.148 + if (l4_is_invalid_cap(_ipc_gate))
1.149 + return -L4_ENOMEM;
1.150 +
1.151 + return l4_error(l4_factory_create_gate(l4re_env()->factory, _ipc_gate, L4_INVALID_CAP, 0));
1.152 +}
1.153 +
1.154 +/* Initialise and assign a region in a list to the created process. */
1.155 +
1.156 +void ProcessCreating::init_region(struct exec_region *regions,
1.157 + struct ipc_mapped_cap *mapped_caps,
1.158 + struct exec_region &r, unsigned int &index)
1.159 +{
1.160 + l4_cap_idx_t mapped_cap = _process.allocate_cap();
1.161 +
1.162 + mapped_caps[index] = (struct ipc_mapped_cap) {mapped_cap, r.ds, L4_CAP_FPAGE_RWS, 0};
1.163 +
1.164 + /* Change the region definition to use the allocated capability in the created
1.165 + process. */
1.166 +
1.167 + regions[index] = r;
1.168 + regions[index].ds = mapped_cap;
1.169 + index++;
1.170 +}
1.171 +
1.172 +/* Initialise the region mapper with details of the payload program regions
1.173 + and of the associated capabilities, configure the region mapper thread,
1.174 + populate its stack, and start the thread. */
1.175 +
1.176 +long ProcessCreating::start_region_mapper()
1.177 +{
1.178 + /* Define regions employing dataspaces to provide program segments. */
1.179 +
1.180 + struct exec_region rm_regions[_rm_payload->segments() + 2];
1.181 +
1.182 + /* Define capabilities for mapping, including region dataspace capabilities,
1.183 + the stack dataspace capability, and the server capability. */
1.184 +
1.185 + struct ipc_mapped_cap rm_mapped_caps[_rm_payload->segments() + 3];
1.186 +
1.187 + /* Here, the arrays are sized for the maximum number of regions and
1.188 + capabilities, but in practice only the loadable segments are used, leaving
1.189 + fewer elements utilised. A terminating entry is employed to indicate the
1.190 + limit of utilised elements. */
1.191 +
1.192 + unsigned int rm_index = 0;
1.193 +
1.194 + for (unsigned int i = 0; i < _program_payload->segments(); i++)
1.195 + {
1.196 + Segment *s = _program_payload->segment(i);
1.197 +
1.198 + if (s->loadable())
1.199 + init_region(rm_regions, rm_mapped_caps, s->exec_region(), rm_index);
1.200 + }
1.201 +
1.202 + /* Introduce the stack region and capability. */
1.203 +
1.204 + init_region(rm_regions, rm_mapped_caps, _program_stack.exec_region(), rm_index);
1.205 +
1.206 + /* Terminate the region array. */
1.207 +
1.208 + rm_regions[rm_index] = (struct exec_region) {0, 0, 0, L4_INVALID_CAP};
1.209 +
1.210 + /* Introduce the server capability and terminate the capability array. */
1.211 +
1.212 + rm_mapped_caps[rm_index++] = (struct ipc_mapped_cap) {_ipc_gate_cap, _ipc_gate, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS};
1.213 + rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {0, L4_INVALID_CAP, 0, 0};
1.214 +
1.215 + /* Map these additional capabilities. */
1.216 +
1.217 + _process.map_capabilities(rm_mapped_caps, false);
1.218 +
1.219 + /* Define the IPC gate as an initial capability to be acquired by the region
1.220 + mapper via the l4re_env API. The capability index is assigned above when
1.221 + mapping the capability and encoded in the entry below. */
1.222 +
1.223 + l4re_env_cap_entry_t rm_init_caps[] = {
1.224 + l4re_env_cap_entry_t("server", _ipc_gate_cap, L4_CAP_FPAGE_RWS),
1.225 + l4re_env_cap_entry_t()
1.226 + };
1.227 +
1.228 + /* NOTE: Environment vector is currently not defined. */
1.229 +
1.230 + const char *envp[] = {NULL};
1.231 +
1.232 + /* Configure the environment for the thread, specifying the pager (and
1.233 + exception handler plus region mapper). */
1.234 +
1.235 + long err = _process.configure_thread(_config.server);
1.236 +
1.237 + if (err)
1.238 + return err;
1.239 +
1.240 + /* Populate a thread stack with argument and environment details for the
1.241 + region mapper, plus the initial server capability and region details. */
1.242 +
1.243 + const char *argv[] = {_rm_filename};
1.244 + Stack rm_st(_rm_stack);
1.245 +
1.246 + rm_st.set_init_caps(rm_init_caps);
1.247 + rm_st.set_regions(rm_regions);
1.248 + rm_st.populate(1, argv, envp);
1.249 +
1.250 + /* Start the region mapper thread in the appropriate stack. */
1.251 +
1.252 + return _process.thread_start(_rm_payload->entry_point(), rm_st);
1.253 +}
1.254 +
1.255 +/* Configure a thread for a program, populate its stack, and start the
1.256 + thread. */
1.257 +
1.258 +long ProcessCreating::start_program(int argc, const char *argv[])
1.259 +{
1.260 + /* NOTE: Environment vector is currently not defined. */
1.261 +
1.262 + const char *envp[] = {NULL};
1.263 +
1.264 + /* Configure the environment for the thread, specifying the pager (and
1.265 + exception handler plus region mapper). */
1.266 +
1.267 + long err = _process.configure_thread(_ipc_gate, _ipc_gate_cap);
1.268 +
1.269 + if (err)
1.270 + return err;
1.271 +
1.272 + /* Populate a thread stack with argument and environment details for the
1.273 + actual program. The server capability should be assigned to the region
1.274 + mapper capability slot already. */
1.275 +
1.276 + Stack program_st(_program_stack);
1.277 +
1.278 + program_st.populate(argc, argv, envp);
1.279 +
1.280 + /* Start the program thread in the appropriate stack. */
1.281 +
1.282 + return _process.thread_start(_program_payload->entry_point(), program_st);
1.283 +}
1.284 +
1.285 +/* Start a new process for the given payload, providing the indicated program
1.286 + arguments. */
1.287 +
1.288 +long ProcessCreating::start(file_t *file, int argc, const char *argv[])
1.289 +{
1.290 + long err;
1.291 +
1.292 + err = init_region_mapper();
1.293 + if (err)
1.294 + return err;
1.295 +
1.296 + err = init_program(file);
1.297 + if (err)
1.298 + return err;
1.299 +
1.300 + err = init_external_pager();
1.301 + if (err)
1.302 + return err;
1.303 +
1.304 + err = configure_task();
1.305 + if (err)
1.306 + return err;
1.307 +
1.308 + err = create_ipc_gate();
1.309 + if (err)
1.310 + return err;
1.311 +
1.312 + err = start_region_mapper();
1.313 + if (err)
1.314 + return err;
1.315 +
1.316 + return start_program(argc, argv);
1.317 +}
1.318 +
1.319 +/* Set the given notifier on the system-level pager for a process. */
1.320 +
1.321 +void ProcessCreating::set_notifier(Notifier *notifier)
1.322 +{
1.323 + _exec_pager.set_notifier(notifier);
1.324 +}
1.325 +
1.326 +/* vim: tabstop=2 expandtab shiftwidth=2
1.327 +*/