1.1 --- a/conf/dstest_exec.cfg Mon Jun 13 00:58:21 2022 +0200
1.2 +++ b/conf/dstest_exec.cfg Mon Jun 13 17:32:24 2022 +0200
1.3 @@ -47,4 +47,4 @@
1.4 },
1.5 log = { "client", "g" },
1.6 },
1.7 - "rom/dstest_exec", "home/paulb/dstest_exec_payload", "hello", "world");
1.8 + "rom/dstest_exec", "home/paulb/dstest_region_mapper", "home/paulb/dstest_exec_payload", "hello", "world");
2.1 --- a/libexec/include/exec/process.h Mon Jun 13 00:58:21 2022 +0200
2.2 +++ b/libexec/include/exec/process.h Mon Jun 13 17:32:24 2022 +0200
2.3 @@ -77,15 +77,11 @@
2.4
2.5 l4_addr_t _utcb_start;
2.6
2.7 - /* Common environment details. */
2.8 + /* Common and thread environment details. */
2.9
2.10 l4re_aux_t _aux;
2.11 l4re_env_t _env;
2.12
2.13 - /* Mapped capability details. */
2.14 -
2.15 - unsigned int _num_mapped_caps = 0;
2.16 -
2.17 /* Task and thread initialisation. */
2.18
2.19 long create_task();
2.20 @@ -95,6 +91,8 @@
2.21 public:
2.22 explicit Process(int reserved_threads = 1);
2.23
2.24 + l4_cap_idx_t allocate_cap();
2.25 +
2.26 long configure_task();
2.27
2.28 long configure_thread(l4_cap_idx_t server);
3.1 --- a/libexec/lib/src/process.cc Mon Jun 13 00:58:21 2022 +0200
3.2 +++ b/libexec/lib/src/process.cc Mon Jun 13 17:32:24 2022 +0200
3.3 @@ -61,15 +61,18 @@
3.4 /* Populate the common initial environment for the threads. */
3.5
3.6 _env.factory = L4_BASE_FACTORY_CAP;
3.7 - _env.main_thread = L4_BASE_THREAD_CAP;
3.8 _env.log = L4_BASE_LOG_CAP;
3.9 _env.scheduler = L4_BASE_SCHEDULER_CAP;
3.10 - _env.rm = L4_EXEC_RM_CAP;
3.11 _env.mem_alloc = L4_EXEC_MA_CAP;
3.12 - _env.first_free_cap = L4_EXEC_FIRST_FREE_CAP_INDEX;
3.13 _env.utcb_area = utcb_fpage;
3.14 _env.first_free_utcb = l4_fpage_memaddr(utcb_fpage) + reserved_threads * L4_UTCB_OFFSET;
3.15
3.16 + /* Capability details that are updated for each thread. */
3.17 +
3.18 + _env.main_thread = L4_BASE_THREAD_CAP;
3.19 + _env.rm = L4_EXEC_RM_CAP;
3.20 + _env.first_free_cap = L4_EXEC_FIRST_FREE_CAP_INDEX;
3.21 +
3.22 /* Populate auxiliary information. */
3.23
3.24 _aux.kip_ds = L4_EXEC_KIP_CAP;
3.25 @@ -77,6 +80,13 @@
3.26 _aux.ldr_flags = 0;
3.27 }
3.28
3.29 +/* Capability index allocation. */
3.30 +
3.31 +l4_cap_idx_t Process::allocate_cap()
3.32 +{
3.33 + return (_env.first_free_cap++ << L4_CAP_SHIFT);
3.34 +}
3.35 +
3.36 /* Task and thread initialisation. */
3.37
3.38 long Process::create_task()
3.39 @@ -137,8 +147,12 @@
3.40
3.41 long Process::configure_thread(l4_cap_idx_t server)
3.42 {
3.43 + /* Employ a distinct region mapper for each thread's environment, this acting
3.44 + as pager. */
3.45 +
3.46 + _env.rm = allocate_cap();
3.47 +
3.48 struct ipc_mapped_cap mapped_caps[] = {
3.49 - {L4_EXEC_PAGER_CAP, server, L4_CAP_FPAGE_RWS},
3.50 {_env.rm, server, L4_CAP_FPAGE_RWS},
3.51 {0, L4_INVALID_CAP, 0},
3.52 };
3.53 @@ -151,7 +165,13 @@
3.54 long Process::map_capabilities(struct ipc_mapped_cap mapped_caps[],
3.55 bool to_count)
3.56 {
3.57 - return ipc_map_capabilities(_task, mapped_caps, to_count ? &_num_mapped_caps : NULL);
3.58 + unsigned int num_mapped_caps;
3.59 + long err = ipc_map_capabilities(_task, mapped_caps, to_count ? &num_mapped_caps : NULL);
3.60 +
3.61 + if (to_count)
3.62 + _env.first_free_cap += num_mapped_caps;
3.63 +
3.64 + return err;
3.65 }
3.66
3.67 /* Create, initialise and start a thread. */
3.68 @@ -169,8 +189,8 @@
3.69 /* Initialise the thread with pager, UTCB and task details. */
3.70
3.71 l4_thread_control_start();
3.72 - l4_thread_control_pager(L4_EXEC_PAGER_CAP);
3.73 - l4_thread_control_exc_handler(L4_EXEC_PAGER_CAP);
3.74 + l4_thread_control_pager(_env.rm);
3.75 + l4_thread_control_exc_handler(_env.rm);
3.76 l4_thread_control_bind((l4_utcb_t *) _utcb_start, _task);
3.77
3.78 err = l4_error(l4_thread_control_commit(thread));
3.79 @@ -181,14 +201,12 @@
3.80 return err;
3.81 }
3.82
3.83 - /* Map the thread capability to the task. */
3.84 + /* Map the thread capability to the task using a distinct capability index. */
3.85 +
3.86 + _env.main_thread = allocate_cap();
3.87
3.88 ipc_map_capability(_task, (struct ipc_mapped_cap) {_env.main_thread, thread, L4_CAP_FPAGE_RWS});
3.89
3.90 - /* Update the environment for any mapped capabilities. */
3.91 -
3.92 - _env.first_free_cap = L4_EXEC_FIRST_FREE_CAP_INDEX + _num_mapped_caps;
3.93 -
3.94 /* Populate the initial environment in the thread. */
3.95
3.96 st.set_l4re_aux(&_aux);
4.1 --- a/libipc/lib/src/map.c Mon Jun 13 00:58:21 2022 +0200
4.2 +++ b/libipc/lib/src/map.c Mon Jun 13 17:32:24 2022 +0200
4.3 @@ -49,7 +49,7 @@
4.4 err = ipc_map_capability(task, mapped_caps[i]);
4.5
4.6 if (count != NULL)
4.7 - *count = i + 1;
4.8 + *count = i;
4.9
4.10 return err;
4.11 }
5.1 --- a/tests/dstest_exec.cc Mon Jun 13 00:58:21 2022 +0200
5.2 +++ b/tests/dstest_exec.cc Mon Jun 13 17:32:24 2022 +0200
5.3 @@ -24,9 +24,11 @@
5.4 #include <l4/util/util.h>
5.5
5.6 #include <exec/elf.h>
5.7 +#include <exec/external_pager.h>
5.8 #include <exec/memory.h>
5.9 -#include <exec/external_pager.h>
5.10 #include <exec/process.h>
5.11 +#include <ipc/cap_alloc.h>
5.12 +#include <ipc/map.h>
5.13 #include <ipc/server.h>
5.14
5.15 #include <stdio.h>
5.16 @@ -70,38 +72,63 @@
5.17 {
5.18 long err;
5.19
5.20 - if (argc < 2)
5.21 + if (argc < 3)
5.22 {
5.23 - printf("Need a program to run.\n");
5.24 + printf("Need a program to run as the region mapper and a main program.\n");
5.25 + return 1;
5.26 + }
5.27 +
5.28 + /* Define the different payloads. */
5.29 +
5.30 + char *rm_filename = argv[1];
5.31 + char *program_filename = argv[2];
5.32 +
5.33 + /* Initialise the memory segments of the region mapper. These are mapped into
5.34 + this task so that we may access them. */
5.35 +
5.36 + ExplicitSegment rm_stack(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW);
5.37 + Payload *rm_payload;
5.38 +
5.39 + if (exec_get_payload(rm_filename, &rm_payload, true))
5.40 + {
5.41 + printf("Could not initialise region mapper: %s\n", rm_filename);
5.42 + return 1;
5.43 + }
5.44 +
5.45 + if (rm_stack.allocate(true))
5.46 + {
5.47 + printf("Could not allocate region mapper stack.\n");
5.48 return 1;
5.49 }
5.50
5.51 - /* Initialise the memory of the new task. */
5.52 + /* Initialise the memory segments of the actual program. These are not mapped
5.53 + into this task, instead being accessed by the region mapper in the new
5.54 + task. */
5.55
5.56 - ExplicitSegment stack(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW);
5.57 - Payload *payload;
5.58 + ExplicitSegment program_stack(Utcb_area_start - initial_stack_size * 2, initial_stack_size, L4_FPAGE_RW);
5.59 + Payload *program_payload;
5.60
5.61 - if (exec_get_payload(argv[1], &payload, true))
5.62 + if (exec_get_payload(program_filename, &program_payload, false))
5.63 {
5.64 - printf("Could not initialise program.\n");
5.65 + printf("Could not initialise program: %s\n", program_filename);
5.66 return 1;
5.67 }
5.68
5.69 - if (stack.allocate(true))
5.70 + if (program_stack.allocate(true))
5.71 {
5.72 - printf("Could not allocate stack.\n");
5.73 + printf("Could not allocate program stack.\n");
5.74 return 1;
5.75 }
5.76
5.77 - /* Initialise pager regions. */
5.78 + /* Initialise pager regions for the region mapper. */
5.79
5.80 - for (unsigned int i = 0; i < payload->segments(); i++)
5.81 + for (unsigned int i = 0; i < rm_payload->segments(); i++)
5.82 {
5.83 - if (payload->segment(i)->loadable())
5.84 - exec_pager.add(payload->segment(i)->region());
5.85 + if (rm_payload->segment(i)->loadable())
5.86 + exec_pager.add(rm_payload->segment(i)->region());
5.87 }
5.88
5.89 - exec_pager.add(stack.region());
5.90 + exec_pager.add(rm_stack.region());
5.91
5.92 /* Start the pager in a separate thread. */
5.93
5.94 @@ -117,10 +144,9 @@
5.95 return 1;
5.96 }
5.97
5.98 - /* Configure the environment for the task, specifying the pager (and exception
5.99 - handler plus region mapper). */
5.100 + /* Configure the environment for the task, reserving two threads. */
5.101
5.102 - Process process;
5.103 + Process process(2);
5.104
5.105 err = process.configure_task();
5.106
5.107 @@ -130,6 +156,9 @@
5.108 return 1;
5.109 }
5.110
5.111 + /* Configure the environment for the thread, specifying the pager (and
5.112 + exception handler plus region mapper). */
5.113 +
5.114 err = process.configure_thread(config.server);
5.115
5.116 if (err)
5.117 @@ -138,25 +167,143 @@
5.118 return 1;
5.119 }
5.120
5.121 - /* Populate a thread stack with argument and environment details. */
5.122 + /* Create an unbound IPC gate for the region mapper. */
5.123 +
5.124 + l4_cap_idx_t ipc_gate = ipc_cap_alloc();
5.125 +
5.126 + if (l4_is_invalid_cap(ipc_gate))
5.127 + {
5.128 + printf("Could not allocate IPC gate capability.\n");
5.129 + return 1;
5.130 + }
5.131 +
5.132 + err = l4_error(l4_factory_create_gate(l4re_env()->factory, ipc_gate, L4_INVALID_CAP, 0));
5.133 +
5.134 + if (err)
5.135 + {
5.136 + printf("Could not create IPC gate.\n");
5.137 + return 1;
5.138 + }
5.139 +
5.140 + /* Define regions employing dataspaces to provide program segments.
5.141 +
5.142 + Define capabilities for mapping, including region dataspace capabilities,
5.143 + the stack dataspace capability, and the server capability.
5.144 +
5.145 + Here, the arrays are sized for the maximum number of regions and
5.146 + capabilities, but in practice only the loadable segments are used, leaving
5.147 + fewer elements utilised. A terminating entry is employed to indicate the
5.148 + limit of utilised elements. */
5.149 +
5.150 + struct exec_region rm_regions[rm_payload->segments() + 2];
5.151 + struct ipc_mapped_cap rm_mapped_caps[rm_payload->segments() + 3];
5.152 + unsigned int rm_index = 0;
5.153 +
5.154 + for (unsigned int i = 0; i < rm_payload->segments(); i++)
5.155 + {
5.156 + Segment *s = rm_payload->segment(i);
5.157
5.158 - Stack program_st(stack);
5.159 + if (s->loadable())
5.160 + {
5.161 + rm_regions[rm_index] = s->exec_region();
5.162 + rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {process.allocate_cap(), rm_regions[rm_index].ds, L4_CAP_FPAGE_RWS};
5.163 + rm_index++;
5.164 + }
5.165 + }
5.166 +
5.167 + /* Introduce the stack region and capability. */
5.168 +
5.169 + rm_regions[rm_index] = program_stack.exec_region();
5.170 + rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {process.allocate_cap(), program_stack.exec_region().ds, L4_CAP_FPAGE_RWS};
5.171 + rm_index++;
5.172 +
5.173 + /* Terminate the region array. */
5.174 +
5.175 + rm_regions[rm_index] = (struct exec_region) {0, 0, 0, L4_INVALID_CAP};
5.176 +
5.177 + /* Introduce the server capability. */
5.178 +
5.179 + l4_cap_idx_t ipc_gate_cap = process.allocate_cap();
5.180 +
5.181 + rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {ipc_gate_cap, ipc_gate, L4_CAP_FPAGE_RWS};
5.182 + rm_index++;
5.183 +
5.184 + /* Terminate the capability array. */
5.185 +
5.186 + rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {0, L4_INVALID_CAP, 0};
5.187 +
5.188 + /* Map these additional capabilities. */
5.189 +
5.190 + printf("Map additional capabilities...\n");
5.191 +
5.192 + process.map_capabilities(rm_mapped_caps);
5.193 +
5.194 + /* Define the IPC gate as an initial capability to be acquired by the region
5.195 + mapper via the l4re_env API. The capability index is assigned above when
5.196 + mapping the capability and encoded in the entry below. */
5.197 +
5.198 + l4re_env_cap_entry_t rm_init_caps[] = {
5.199 + l4re_env_cap_entry_t("server", ipc_gate_cap, L4_CAP_FPAGE_RWS),
5.200 + l4re_env_cap_entry_t()
5.201 + };
5.202
5.203 /* NOTE: Environment vector is currently not defined. */
5.204
5.205 char *envp[] = {NULL};
5.206
5.207 - program_st.populate(argc - 1, argv + 1, envp);
5.208 + /* Populate a thread stack with argument and environment details for the
5.209 + region mapper, plus the initial server capability and region details. */
5.210
5.211 - /* Start the new thread in the given stack. */
5.212 + printf("Populating region mapper stack...\n");
5.213 +
5.214 + Stack rm_st(rm_stack);
5.215
5.216 - printf("Run thread...\n");
5.217 + rm_st.set_init_caps(rm_init_caps);
5.218 + rm_st.set_regions(rm_regions);
5.219 + rm_st.populate(1, argv + 1, envp);
5.220
5.221 - err = process.thread_start(payload->entry_point(), program_st);
5.222 + /* Start the region mapper thread in the appropriate stack. */
5.223 +
5.224 + printf("Run region mapper thread...\n");
5.225 +
5.226 + err = process.thread_start(rm_payload->entry_point(), rm_st);
5.227
5.228 if (err)
5.229 {
5.230 - printf("Could not run thread.\n");
5.231 + printf("Could not run thread for region mapper.\n");
5.232 + return 1;
5.233 + }
5.234 +
5.235 + /* Configure the environment for the thread, specifying the pager (and
5.236 + exception handler plus region mapper). */
5.237 +
5.238 + err = process.configure_thread(ipc_gate);
5.239 +
5.240 + if (err)
5.241 + {
5.242 + printf("Could not configure task.\n");
5.243 + return 1;
5.244 + }
5.245 +
5.246 + /* Populate a thread stack with argument and environment details for the
5.247 + actual program. The server capability should be assigned to the region
5.248 + mapper capability slot already. */
5.249 +
5.250 + printf("Populating program stack...\n");
5.251 +
5.252 + Stack program_st(program_stack);
5.253 +
5.254 + program_st.populate(argc - 2, argv + 2, envp);
5.255 +
5.256 + /* Start the program thread in the appropriate stack. */
5.257 +
5.258 + printf("Run program thread...\n");
5.259 +
5.260 + err = process.thread_start(program_payload->entry_point(), program_st);
5.261 +
5.262 + if (err)
5.263 + {
5.264 + printf("Could not run thread for program.\n");
5.265 return 1;
5.266 }
5.267