1/* 2 * Copyright 2017, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(DATA61_BSD) 11 */ 12 13#include <autoconf.h> 14#include <sel4utils/gen_config.h> 15 16#include <inttypes.h> 17#include <stdio.h> 18#include <stdlib.h> 19#include <string.h> 20 21#include <sel4/sel4.h> 22#include <vka/vka.h> 23#include <vka/object.h> 24#include <vspace/vspace.h> 25#include <sel4runtime.h> 26#include <sel4utils/api.h> 27#include <sel4utils/mapping.h> 28#include <sel4utils/thread.h> 29#include <sel4utils/util.h> 30#include <sel4utils/arch/util.h> 31#include <sel4utils/helpers.h> 32#include <utils/stack.h> 33 34 35int sel4utils_configure_thread(vka_t *vka, vspace_t *parent, vspace_t *alloc, seL4_CPtr fault_endpoint, 36 seL4_CNode cspace, seL4_Word cspace_root_data, sel4utils_thread_t *res) 37{ 38 39 sel4utils_thread_config_t config = {0}; 40 config = thread_config_fault_endpoint(config, fault_endpoint); 41 config = thread_config_cspace(config, cspace, cspace_root_data); 42 config = thread_config_create_reply(config); 43 return sel4utils_configure_thread_config(vka, parent, alloc, config, res); 44} 45 46int sel4utils_configure_thread_config(vka_t *vka, vspace_t *parent, vspace_t *alloc, 47 sel4utils_thread_config_t config, sel4utils_thread_t *res) 48{ 49 memset(res, 0, sizeof(sel4utils_thread_t)); 50 51 int error = vka_alloc_tcb(vka, &res->tcb); 52 if (error == -1) { 53 ZF_LOGE("vka_alloc tcb failed"); 54 sel4utils_clean_up_thread(vka, alloc, res); 55 return -1; 56 } 57 58 if (!config.no_ipc_buffer) { 59 res->ipc_buffer_addr = (seL4_Word) vspace_new_ipc_buffer(alloc, &res->ipc_buffer); 60 61 if (res->ipc_buffer_addr == 0) { 62 ZF_LOGE("ipc buffer allocation failed"); 63 return -1; 64 } 65 } 66 67 if (config_set(CONFIG_KERNEL_MCS) && config.create_reply) { 68 if (vka_alloc_reply(vka, &res->reply)) { 69 ZF_LOGE("Failed to allocate reply"); 70 sel4utils_clean_up_thread(vka, alloc, res); 71 return -1; 72 } 73 res->own_reply = true; 74 } else { 75 res->reply.cptr = config.reply; 76 } 77 78 if (config.sched_params.create_sc) { 79 if (!config_set(CONFIG_KERNEL_MCS)) { 80 ZF_LOGE("Cannot create a scheduling context on a non-RT kernel"); 81 sel4utils_clean_up_thread(vka, alloc, res); 82 return -1; 83 } 84 85 /* allocate a scheduling context */ 86 if (vka_alloc_sched_context(vka, &res->sched_context)) { 87 ZF_LOGE("Failed to allocate sched context"); 88 sel4utils_clean_up_thread(vka, alloc, res); 89 return -1; 90 } 91 92 /* configure the scheduling context */ 93 if (config_set(CONFIG_KERNEL_MCS)) { 94 error = api_sched_ctrl_configure(config.sched_params.sched_ctrl, res->sched_context.cptr, 95 config.sched_params.budget, config.sched_params.period, 96 config.sched_params.extra_refills, config.sched_params.badge); 97 } 98 if (error != seL4_NoError) { 99 ZF_LOGE("Failed to configure sched context"); 100 sel4utils_clean_up_thread(vka, alloc, res); 101 return -1; 102 } 103 res->own_sc = true; 104 } else { 105 res->sched_context.cptr = config.sched_params.sched_context; 106 } 107 seL4_Word null_cap_data = seL4_NilData; 108 error = api_tcb_configure(res->tcb.cptr, config.fault_endpoint, 109 seL4_CapNull, 110 res->sched_context.cptr, 111 config.cspace, 112 config.cspace_root_data, vspace_get_root(alloc), 113 null_cap_data, res->ipc_buffer_addr, res->ipc_buffer); 114 115 if (error != seL4_NoError) { 116 ZF_LOGE("TCB configure failed with seL4 error code %d", error); 117 sel4utils_clean_up_thread(vka, alloc, res); 118 return -1; 119 } 120 121 /* only set the prio fields if the value is > 0. As we just allocated the 122 * TCB above, the prio and mcp are already 0. */ 123 if (config.sched_params.mcp) { 124 error = seL4_TCB_SetMCPriority(res->tcb.cptr, config.sched_params.auth, 125 config.sched_params.mcp); 126 if (error) { 127 ZF_LOGE("Failed to set mcpriority, %d", error); 128 return -1; 129 } 130 } 131 132 if (config.sched_params.priority) { 133 error = seL4_TCB_SetPriority(res->tcb.cptr, config.sched_params.auth, 134 config.sched_params.priority); 135 if (error) { 136 ZF_LOGE("Failed to set priority, %d", error); 137 sel4utils_clean_up_thread(vka, alloc, res); 138 return -1; 139 } 140 } 141 142 if (config.custom_stack_size) { 143 res->stack_size = config.stack_size; 144 } else { 145 res->stack_size = BYTES_TO_4K_PAGES(CONFIG_SEL4UTILS_STACK_SIZE); 146 } 147 148 if (res->stack_size > 0) { 149 res->stack_top = vspace_new_sized_stack(alloc, res->stack_size); 150 151 if (res->stack_top == NULL) { 152 ZF_LOGE("Stack allocation failed!"); 153 sel4utils_clean_up_thread(vka, alloc, res); 154 return -1; 155 } 156 157 res->initial_stack_pointer = res->stack_top; 158 } 159 160 return 0; 161} 162 163int sel4utils_start_thread(sel4utils_thread_t *thread, sel4utils_thread_entry_fn entry_point, 164 void *arg0, void *arg1, int resume) 165{ 166 seL4_UserContext context = {0}; 167 size_t context_size = sizeof(seL4_UserContext) / sizeof(seL4_Word); 168 169 size_t tls_size = sel4runtime_get_tls_size(); 170 /* make sure we're not going to use too much of the stack */ 171 if (tls_size > thread->stack_size * PAGE_SIZE_4K / 8) { 172 ZF_LOGE("TLS would use more than 1/8th of the application stack %zu/%zu", tls_size, thread->stack_size); 173 return -1; 174 } 175 uintptr_t tls_base = (uintptr_t)thread->initial_stack_pointer - tls_size; 176 uintptr_t tp = (uintptr_t)sel4runtime_write_tls_image((void *)tls_base); 177 seL4_IPCBuffer *ipc_buffer_addr = (void *)thread->ipc_buffer_addr; 178 sel4runtime_set_tls_variable(tp, __sel4_ipc_buffer, ipc_buffer_addr); 179 180 uintptr_t aligned_stack_pointer = ALIGN_DOWN(tls_base, STACK_CALL_ALIGNMENT); 181 182 int error = sel4utils_arch_init_local_context(entry_point, arg0, arg1, 183 (void *) thread->ipc_buffer_addr, 184 (void *) aligned_stack_pointer, 185 &context); 186 if (error) { 187 return error; 188 } 189 190 error = seL4_TCB_WriteRegisters(thread->tcb.cptr, false, 0, context_size, &context); 191 if (error) { 192 return error; 193 } 194 195 error = seL4_TCB_SetTLSBase(thread->tcb.cptr, tp); 196 if (error) { 197 return error; 198 } 199 200 if (resume) { 201 return seL4_TCB_Resume(thread->tcb.cptr); 202 } 203 return 0; 204} 205 206void sel4utils_clean_up_thread(vka_t *vka, vspace_t *alloc, sel4utils_thread_t *thread) 207{ 208 if (thread->tcb.cptr != 0) { 209 vka_free_object(vka, &thread->tcb); 210 } 211 212 if (thread->ipc_buffer_addr != 0) { 213 vspace_free_ipc_buffer(alloc, (seL4_Word *) thread->ipc_buffer_addr); 214 } 215 216 if (thread->stack_top != 0) { 217 vspace_free_sized_stack(alloc, thread->stack_top, thread->stack_size); 218 } 219 220 if (thread->own_sc && thread->sched_context.cptr != 0) { 221 vka_free_object(vka, &thread->sched_context); 222 } 223 224 if (thread->own_reply && thread->reply.cptr != 0) { 225 vka_free_object(vka, &thread->reply); 226 } 227 228 memset(thread, 0, sizeof(sel4utils_thread_t)); 229} 230 231void sel4utils_print_fault_message(seL4_MessageInfo_t tag, const char *thread_name) 232{ 233 seL4_Fault_t fault = seL4_getFault(tag); 234 235 switch (seL4_Fault_get_seL4_FaultType(fault)) { 236 case seL4_Fault_VMFault: 237 assert(seL4_MessageInfo_get_length(tag) == seL4_VMFault_Length); 238 printf("%sPagefault from [%s]: %s %s at PC: %p vaddr: %p, FSR %p%s\n", 239 COLOR_ERROR, 240 thread_name, 241 sel4utils_is_read_fault() ? "read" : "write", 242 seL4_Fault_VMFault_get_PrefetchFault(fault) ? "prefetch fault" : "fault", 243 (void *)seL4_Fault_VMFault_get_IP(fault), 244 (void *)seL4_Fault_VMFault_get_Addr(fault), 245 (void *)seL4_Fault_VMFault_get_FSR(fault), 246 COLOR_NORMAL); 247 break; 248 249 case seL4_Fault_UnknownSyscall: 250 assert(seL4_MessageInfo_get_length(tag) == seL4_UnknownSyscall_Length); 251 printf("%sBad syscall from [%s]: scno %"PRIuPTR" at PC: %p%s\n", 252 COLOR_ERROR, 253 thread_name, 254 seL4_Fault_UnknownSyscall_get_Syscall(fault), 255 (void *) seL4_Fault_UnknownSyscall_get_FaultIP(fault), 256 COLOR_NORMAL 257 ); 258 259 break; 260 261 case seL4_Fault_UserException: 262 assert(seL4_MessageInfo_get_length(tag) == seL4_UserException_Length); 263 printf("%sInvalid instruction from [%s] at PC: %p%s\n", 264 COLOR_ERROR, 265 thread_name, 266 (void *)seL4_Fault_UserException_get_FaultIP(fault), 267 COLOR_NORMAL); 268 break; 269 270 case seL4_Fault_CapFault: 271 printf("%sCap fault from [%s] in phase %s\nPC = %p\nCPtr = %p%s\n", 272 COLOR_ERROR, thread_name, 273 seL4_Fault_CapFault_get_InRecvPhase(fault) ? "receive" : "send", 274 (void *) seL4_Fault_CapFault_get_IP(fault), 275 (void *) seL4_Fault_CapFault_get_Addr(fault), 276 COLOR_NORMAL); 277 break; 278#ifdef CONFIG_KERNEL_MCS 279 case seL4_Fault_Timeout: 280 printf("Timeout fault from %s\n", thread_name); 281 break; 282#endif 283 284 default: 285 /* What? Why are we here? What just happened? */ 286 printf("Unknown fault from [%s]: %"PRIuPTR" (length = %"PRIuPTR")\n", thread_name, seL4_MessageInfo_get_label(tag), seL4_MessageInfo_get_length(tag)); 287 break; 288 } 289} 290 291static int 292fault_handler(char *name, seL4_CPtr endpoint) 293{ 294 seL4_MessageInfo_t info; 295 while (1) { 296 /* sleep so other things can run */ 297 info = api_wait(endpoint, NULL); 298 sel4utils_print_fault_message(info, name); 299 } 300 return 0; 301} 302 303int 304sel4utils_start_fault_handler(seL4_CPtr fault_endpoint, vka_t *vka, vspace_t *vspace, 305 seL4_CPtr cspace, seL4_Word cap_data, char *name, 306 sel4utils_thread_t *res) 307{ 308 int error = sel4utils_configure_thread(vka, vspace, vspace, 0, cspace, 309 cap_data, res); 310 311 if (error) { 312 ZF_LOGE("Failed to configure fault handling thread\n"); 313 return -1; 314 } 315 316 return sel4utils_start_thread(res, (sel4utils_thread_entry_fn)fault_handler, name, 317 (void *) fault_endpoint, 1); 318} 319 320int 321sel4utils_checkpoint_thread(sel4utils_thread_t *thread, sel4utils_checkpoint_t *checkpoint, bool suspend) 322{ 323 assert(checkpoint != NULL); 324 325 int error = seL4_TCB_ReadRegisters(thread->tcb.cptr, suspend, 0, sizeof(seL4_UserContext) / sizeof(seL4_Word), 326 &checkpoint->regs); 327 if (error) { 328 ZF_LOGE("Failed to read registers of tcb while checkpointing\n"); 329 return error; 330 } 331 332 checkpoint->sp = sel4utils_get_sp(checkpoint->regs); 333#ifdef CONFIG_ARCH_X86_64 334 if (config_set(CONFIG_SYSENTER)) { 335 /* on x64, using sysenter, the kernel ABI expects rcx to be set to rsp, 336 * and rdx to be set to the fault instruction. Simulate this behaviour here 337 * before resuming. Note that this will only work for threads checkpointed 338 * at sysenter, e.g. while in a system call (eg seL4_Recv). */ 339 checkpoint->regs.rcx = checkpoint->regs.rsp; 340 checkpoint->regs.rdx = checkpoint->regs.rip; 341 } else if (config_set(CONFIG_SYSCALL)) { 342 /* on x64, using syscall, when a thread is in the kernel, 343 * sp is stored in rbx. So use rbx for stack calculations */ 344 checkpoint->sp = checkpoint->regs.rbx; 345 } 346#endif /* CONFIG_ARCH_X86_64 */ 347 348 size_t stack_size = (uintptr_t) thread->stack_top - checkpoint->sp; 349 checkpoint->stack = malloc(stack_size); 350 if (checkpoint->stack == NULL) { 351 ZF_LOGE("Failed to malloc stack of size %zu\n", stack_size); 352 return -1; 353 } 354 355 memcpy(checkpoint->stack, (void *) checkpoint->sp, stack_size); 356 checkpoint->thread = thread; 357 358 return error; 359} 360 361int 362sel4utils_checkpoint_restore(sel4utils_checkpoint_t *checkpoint, bool free_memory, bool resume) 363{ 364 assert(checkpoint != NULL); 365 366 size_t stack_size = (uintptr_t) checkpoint->thread->stack_top - checkpoint->sp; 367 memcpy((void *) checkpoint->sp, checkpoint->stack, stack_size); 368 369 int error = seL4_TCB_WriteRegisters(checkpoint->thread->tcb.cptr, resume, 0, 370 sizeof(seL4_UserContext) / sizeof (seL4_Word), 371 &checkpoint->regs); 372 if (error) { 373 ZF_LOGE("Failed to restore registers of tcb while restoring checkpoint\n"); 374 return error; 375 } 376 377 if (free_memory) { 378 sel4utils_free_checkpoint(checkpoint); 379 } 380 381 return error; 382} 383 384void 385sel4utils_free_checkpoint(sel4utils_checkpoint_t *checkpoint) 386{ 387 free(checkpoint->stack); 388} 389 390int sel4utils_set_sched_affinity(sel4utils_thread_t *thread, sched_params_t params) { 391#if CONFIG_MAX_NUM_NODES > 1 392#ifdef CONFIG_KERNEL_MCS 393 return api_sched_ctrl_configure(params.sched_ctrl, thread->sched_context.cptr, params.budget, params.period, 394 params.extra_refills, params.badge); 395 396#else 397 return seL4_TCB_SetAffinity(thread->tcb.cptr, params.core); 398#endif 399#else 400 return -ENOSYS; 401#endif 402} 403