1 /* 2 * Support for initialising programs in new tasks and threads. 3 * 4 * Copyright (C) 2022, 2023, 2024 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/sys/err.h> 23 #include <l4/sys/scheduler.h> 24 25 #include <exec/process.h> 26 #include <ipc/cap_alloc.h> 27 #include <ipc/map.h> 28 #include <mem/memory_utils.h> 29 30 31 32 /* Obtain a flexpage defining the UTCB area location and size in a new task. */ 33 34 static l4_fpage_t get_utcb_fpage(unsigned int page, unsigned int pages) 35 { 36 /* UTCB location and size. */ 37 38 int utcb_log2size = page_order(Default_max_threads * L4_UTCB_OFFSET); 39 40 /* Round up to at least one page. */ 41 42 if (utcb_log2size < L4_PAGESHIFT) 43 utcb_log2size = L4_PAGESHIFT; 44 45 utcb_log2size = page_order(pages * (1UL << utcb_log2size)); 46 47 return l4_fpage(Utcb_area_start + page * (1UL << utcb_log2size), utcb_log2size, 0); 48 } 49 50 51 52 /* Initialise a new process, this being an abstraction for a new task with some 53 threads. */ 54 55 Process::Process() 56 { 57 reset(); 58 } 59 60 void Process::reset() 61 { 62 _thread_number = 0; 63 64 /* Populate the common initial environment for the threads. */ 65 66 _env.factory = L4_BASE_FACTORY_CAP; 67 _env.log = L4_BASE_LOG_CAP; 68 _env.scheduler = L4_BASE_SCHEDULER_CAP; 69 _env.mem_alloc = L4_EXEC_MA_CAP; 70 71 /* Capability details that are updated for each thread. Note that the region 72 mapper is redefined, but it would traditionally employ the given index. */ 73 74 _env.main_thread = L4_BASE_THREAD_CAP; 75 _env.rm = L4_EXEC_RM_CAP; 76 _env.first_free_cap = L4_EXEC_FIRST_FREE_CAP_INDEX; 77 78 /* Populate auxiliary information. */ 79 80 _aux.kip_ds = L4_EXEC_KIP_CAP; 81 _aux.dbg_lvl = 0; 82 _aux.ldr_flags = 0; 83 } 84 85 /* Capability index allocation. */ 86 87 l4_cap_idx_t Process::allocate_cap() 88 { 89 return (_env.first_free_cap++ << L4_CAP_SHIFT); 90 } 91 92 /* Task and thread initialisation. */ 93 94 long Process::create_task(unsigned int threads) 95 { 96 /* Reset the process if it has already been used. */ 97 98 if (_thread_number) 99 reset(); 100 101 _task = ipc_cap_alloc(); 102 103 if (l4_is_invalid_cap(_task)) 104 return -L4_ENOMEM; 105 106 return l4_error(l4_factory_create_task(l4re_env()->factory, _task, get_utcb_fpage(0, threads))); 107 } 108 109 long Process::create_thread(l4_cap_idx_t *thread) 110 { 111 *thread = ipc_cap_alloc(); 112 113 if (l4_is_invalid_cap(*thread)) 114 return -L4_ENOMEM; 115 116 return l4_error(l4_factory_create_thread(l4re_env()->factory, *thread)); 117 } 118 119 /* Configure the task environment. */ 120 121 long Process::configure_task(l4_cap_idx_t *task, l4_cap_idx_t *mapped_task, unsigned int threads) 122 { 123 long err = create_task(threads); 124 125 if (err) 126 return err; 127 128 /* Map the KIP into the task. */ 129 130 l4_addr_t kip_start = (l4_addr_t) l4re_kip(); 131 132 err = l4_error(l4_task_map(_task, L4RE_THIS_TASK_CAP, 133 l4_fpage(kip_start, L4_PAGESHIFT, L4_FPAGE_RX), 134 kip_start)); 135 136 if (err) 137 return err; 138 139 /* Define capability mappings for the new task. */ 140 141 struct ipc_mapped_cap mapped_caps[] = { 142 {L4_BASE_TASK_CAP, _task, L4_CAP_FPAGE_RWS, 0}, 143 {_env.factory, l4re_env()->factory, L4_CAP_FPAGE_RWS, 0}, 144 {_env.log, l4re_env()->log, L4_CAP_FPAGE_RWS, 0}, 145 {_env.scheduler, l4re_env()->scheduler, L4_CAP_FPAGE_RWS, 0}, 146 {_env.mem_alloc, l4re_env()->mem_alloc, L4_CAP_FPAGE_RWS, 0}, 147 {L4_INVALID_CAP, L4_INVALID_CAP, 0, 0}, 148 }; 149 150 /* Return the capability details for the task. */ 151 152 *task = _task; 153 *mapped_task = L4_BASE_TASK_CAP; 154 155 return map_capabilities(mapped_caps, false); 156 } 157 158 /* Set the pager/region mapper of the new thread, using the given capability, 159 returning its capability details in the new task. */ 160 161 long Process::set_pager(l4_cap_idx_t rm, l4_cap_idx_t *mapped_rm) 162 { 163 /* Employ a distinct region mapper for each thread's environment, this acting 164 as pager. */ 165 166 if ((mapped_rm != NULL) && l4_is_valid_cap(*mapped_rm)) 167 { 168 _env.rm = *mapped_rm; 169 return L4_EOK; 170 } 171 else 172 { 173 _env.rm = allocate_cap(); 174 *mapped_rm = _env.rm; 175 return ipc_map_capability(_task, (struct ipc_mapped_cap) {_env.rm, rm, 176 L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}); 177 } 178 } 179 180 /* Set the parent of the new thread, using the default parent capability index 181 if no mapped capability is provided. */ 182 183 long Process::set_parent(l4_cap_idx_t parent, l4_cap_idx_t *mapped_parent) 184 { 185 if ((mapped_parent != NULL) && l4_is_valid_cap(*mapped_parent)) 186 { 187 _env.parent = *mapped_parent; 188 return L4_EOK; 189 } 190 else 191 { 192 _env.parent = L4_EXEC_PARENT_CAP; 193 *mapped_parent = _env.parent; 194 return ipc_map_capability(_task, (struct ipc_mapped_cap) {_env.parent, 195 parent, L4_CAP_FPAGE_RWS, 0}); 196 } 197 } 198 199 /* Map capabilities into the task, counting them if indicated. If capability 200 indexes are obtained using the allocate_cap method, then they do not need to 201 be counted again. */ 202 203 long Process::map_capabilities(struct ipc_mapped_cap mapped_caps[], 204 bool to_count) 205 { 206 unsigned int num_mapped_caps; 207 long err = ipc_map_capabilities(_task, mapped_caps, to_count ? &num_mapped_caps : NULL); 208 209 if (to_count) 210 _env.first_free_cap += num_mapped_caps; 211 212 return err; 213 } 214 215 /* Create, initialise and start a thread. */ 216 217 long Process::thread_start(l4_addr_t program_start, Stack &st, 218 l4_cap_idx_t *thread, l4_cap_idx_t *mapped_thread) 219 { 220 long err; 221 222 err = create_thread(thread); 223 224 if (err) 225 return err; 226 227 /* Obtain UTCB area details for the thread. */ 228 229 l4_fpage_t utcb_fpage = get_utcb_fpage(_thread_number, 1); 230 231 _env.utcb_area = utcb_fpage; 232 _env.first_free_utcb = l4_fpage_memaddr(utcb_fpage) + L4_UTCB_OFFSET; 233 234 /* Initialise the thread with pager, UTCB and task details. */ 235 236 l4_thread_control_start(); 237 l4_thread_control_pager(_env.rm); 238 l4_thread_control_exc_handler(_env.rm); 239 l4_thread_control_bind((l4_utcb_t *) l4_fpage_memaddr(_env.utcb_area), _task); 240 241 err = l4_error(l4_thread_control_commit(*thread)); 242 243 if (err) 244 { 245 ipc_cap_free(*thread); 246 return err; 247 } 248 249 /* Map the thread capability to the task using a distinct capability index. */ 250 251 _env.main_thread = allocate_cap(); 252 253 ipc_map_capability(_task, (struct ipc_mapped_cap) {_env.main_thread, *thread, L4_CAP_FPAGE_RWS, 0}); 254 255 /* Populate the initial environment in the thread. */ 256 257 st.set_l4re_aux(&_aux); 258 st.set_l4re_env(&_env); 259 260 /* Reserve some extra space for capabilities used by this thread. 261 NOTE: The capability allocator is probably not able to avoid conflicts. */ 262 263 _env.first_free_cap += 0x20; 264 265 /* Set the start details. */ 266 267 err = l4_error(l4_thread_ex_regs(*thread, program_start, st.start_address(), 0)); 268 269 if (err) 270 return err; 271 272 /* Select a new thread. */ 273 274 _thread_number++; 275 276 /* Start the thread. */ 277 278 l4_sched_param_t sp = l4_sched_param(L4RE_MAIN_THREAD_PRIO); 279 280 *mapped_thread = _env.main_thread; 281 return l4_error(l4_scheduler_run_thread(l4re_env()->scheduler, *thread, &sp)); 282 } 283 284 /* vim: tabstop=2 expandtab shiftwidth=2 285 */