/* Copyright (C) 2005 David Decotigny Copyright (C) 2000-2004, The KOS team 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include "cpu_context.h" /** * Here is the definition of a CPU context for IA32 processors. This * is a SOS convention, not a specification given by the IA32 * spec. However there is a strong constraint related to the x86 * interrupt handling specification: the top of the stack MUST be * compatible with the 'iret' instruction, ie there must be the * err_code (might be 0), eip, cs and eflags of the destination * context in that order (see Intel x86 specs vol 3, figure 5-4). * * @note IMPORTANT: This definition MUST be consistent with the way * the registers are stored on the stack in * irq_wrappers.S/exception_wrappers.S !!! Hence the constraint above. */ struct sos_cpu_state { /* (Lower addresses) */ /* These are SOS convention */ sos_ui16_t gs; sos_ui16_t fs; sos_ui16_t es; sos_ui16_t ds; sos_ui16_t cpl0_ss; /* This is ALWAYS the Stack Segment of the Kernel context (CPL0) of the interrupted thread, even for a user thread */ sos_ui16_t alignment_padding; /* unused */ sos_ui32_t eax; sos_ui32_t ebx; sos_ui32_t ecx; sos_ui32_t edx; sos_ui32_t esi; sos_ui32_t edi; sos_ui32_t ebp; /* MUST NEVER CHANGE (dependent on the IA32 iret instruction) */ sos_ui32_t error_code; sos_vaddr_t eip; sos_ui32_t cs; /* 32bits according to the specs ! However, the CS register is really 16bits long */ sos_ui32_t eflags; /* (Higher addresses) */ } __attribute__((packed)); /** * The CS value pushed on the stack by the CPU upon interrupt, and * needed by the iret instruction, is 32bits long while the real CPU * CS register is 16bits only: this macro simply retrieves the CPU * "CS" register value from the CS value pushed on the stack by the * CPU upon interrupt. * * The remaining 16bits pushed by the CPU should be considered * "reserved" and architecture dependent. IMHO, the specs don't say * anything about them. Considering that some architectures generate * non-zero values for these 16bits (at least Cyrix), we'd better * ignore them. */ #define GET_CPU_CS_REGISTER_VALUE(pushed_ui32_cs_value) \ ( (pushed_ui32_cs_value) & 0xffff ) /** * Structure of an interrupted Kernel thread's context */ struct sos_cpu_kstate { struct sos_cpu_state regs; } __attribute__((packed)); /** * Structure of an interrupted User thread's context. This is almost * the same as a kernel context, except that 2 additional values are * pushed on the stack before the eflags/cs/eip of the interrupted * context: the stack configuration of the interrupted user context. * * @see Section 6.4.1 of Intel x86 vol 1 */ struct sos_cpu_ustate { struct sos_cpu_state regs; struct { sos_ui32_t cpl3_esp; sos_ui16_t cpl3_ss; }; } __attribute__((packed)); /* * Structure of a Task State Segment on the x86 Architecture. * * @see Intel x86 spec vol 3, figure 6-2 * * @note Such a data structure should not cross any page boundary (see * end of section 6.2.1 of Intel spec vol 3). This is the reason why * we tell gcc to align it on a 128B boundary (its size is 104B, which * is <= 128). */ struct x86_tss { /** * Intel provides a way for a task to switch to another in an * automatic way (call gates). In this case, the back_link field * stores the source TSS of the context switch. This allows to * easily implement coroutines, task backtracking, ... In SOS we * don't use TSS for the context switch purpouse, so we always * ignore this field. * (+0) */ sos_ui16_t back_link; sos_ui16_t reserved1; /* CPL0 saved context. (+4) */ sos_vaddr_t esp0; sos_ui16_t ss0; sos_ui16_t reserved2; /* CPL1 saved context. (+12) */ sos_vaddr_t esp1; sos_ui16_t ss1; sos_ui16_t reserved3; /* CPL2 saved context. (+20) */ sos_vaddr_t esp2; sos_ui16_t ss2; sos_ui16_t reserved4; /* Interrupted context's saved registers. (+28) */ sos_vaddr_t cr3; sos_vaddr_t eip; sos_ui32_t eflags; sos_ui32_t eax; sos_ui32_t ecx; sos_ui32_t edx; sos_ui32_t ebx; sos_ui32_t esp; sos_ui32_t ebp; sos_ui32_t esi; sos_ui32_t edi; /* +72 */ sos_ui16_t es; sos_ui16_t reserved5; /* +76 */ sos_ui16_t cs; sos_ui16_t reserved6; /* +80 */ sos_ui16_t ss; sos_ui16_t reserved7; /* +84 */ sos_ui16_t ds; sos_ui16_t reserved8; /* +88 */ sos_ui16_t fs; sos_ui16_t reserved9; /* +92 */ sos_ui16_t gs; sos_ui16_t reserved10; /* +96 */ sos_ui16_t ldtr; sos_ui16_t reserved11; /* +100 */ sos_ui16_t debug_trap_flag :1; sos_ui16_t reserved12 :15; sos_ui16_t iomap_base_addr; /* 104 */ } __attribute__((packed, aligned(128))); static struct x86_tss kernel_tss; sos_ret_t sos_cpu_context_subsystem_setup() { /* Reset the kernel TSS */ memset(&kernel_tss, 0x0, sizeof(kernel_tss)); /** * Now setup the kernel TSS. * * Considering the privilege change method we choose (cpl3 -> cpl0 * through a software interrupt), we don't need to initialize a * full-fledged TSS. See section 6.4.1 of Intel x86 vol 1. Actually, * only a correct value for the kernel esp and ss are required (aka * "ss0" and "esp0" fields). Since the esp0 will have to be updated * at privilege change time, we don't have to set it up now. */ kernel_tss.ss0 = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA); /* Register this TSS into the gdt */ sos_gdt_register_kernel_tss((sos_vaddr_t) &kernel_tss); return SOS_OK; } /** * THE main operation of a kernel thread. This routine calls the * kernel thread function start_func and calls exit_func when * start_func returns. */ static void core_routine (sos_cpu_kstate_function_arg1_t *start_func, sos_ui32_t start_arg, sos_cpu_kstate_function_arg1_t *exit_func, sos_ui32_t exit_arg) __attribute__((noreturn)); static void core_routine (sos_cpu_kstate_function_arg1_t *start_func, sos_ui32_t start_arg, sos_cpu_kstate_function_arg1_t *exit_func, sos_ui32_t exit_arg) { start_func(start_arg); exit_func(exit_arg); SOS_ASSERT_FATAL(! "The exit function of the thread should NOT return !"); for(;;); } sos_ret_t sos_cpu_kstate_init(struct sos_cpu_state **ctxt, sos_cpu_kstate_function_arg1_t *start_func, sos_ui32_t start_arg, sos_vaddr_t stack_bottom, sos_size_t stack_size, sos_cpu_kstate_function_arg1_t *exit_func, sos_ui32_t exit_arg) { /* We are initializing a Kernel thread's context */ struct sos_cpu_kstate *kctxt; /* This is a critical internal function, so that it is assumed that the caller knows what he does: we legitimally assume that values for ctxt, start_func, stack_* and exit_func are allways VALID ! */ /* Setup the stack. * * On x86, the stack goes downward. Each frame is configured this * way (higher addresses first): * * - (optional unused space. As of gcc 3.3, this space is 24 bytes) * - arg n * - arg n-1 * - ... * - arg 1 * - return instruction address: The address the function returns to * once finished * - local variables * * The remaining of the code should be read from the end upward to * understand how the processor will handle it. */ sos_vaddr_t tmp_vaddr = stack_bottom + stack_size; sos_ui32_t *stack = (sos_ui32_t*)tmp_vaddr; /* If needed, poison the stack */ #ifdef SOS_CPU_STATE_DETECT_UNINIT_KERNEL_VARS memset((void*)stack_bottom, SOS_CPU_STATE_STACK_POISON, stack_size); #elif defined(SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW) sos_cpu_state_prepare_detect_kernel_stack_overflow(stack_bottom, stack_size); #endif /* Simulate a call to the core_routine() function: prepare its arguments */ *(--stack) = exit_arg; *(--stack) = (sos_ui32_t)exit_func; *(--stack) = start_arg; *(--stack) = (sos_ui32_t)start_func; *(--stack) = 0; /* Return address of core_routine => force page fault */ /* * Setup the initial context structure, so that the CPU will execute * the function core_routine() once this new context has been * restored on CPU */ /* Compute the base address of the structure, which must be located below the previous elements */ tmp_vaddr = ((sos_vaddr_t)stack) - sizeof(struct sos_cpu_kstate); kctxt = (struct sos_cpu_kstate*)tmp_vaddr; /* Initialize the CPU context structure */ memset(kctxt, 0x0, sizeof(struct sos_cpu_kstate)); /* Tell the CPU context structure that the first instruction to execute will be that of the core_routine() function */ kctxt->regs.eip = (sos_ui32_t)core_routine; /* Setup the segment registers */ kctxt->regs.cs = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE); /* Code */ kctxt->regs.ds = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA); /* Data */ kctxt->regs.es = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA); /* Data */ kctxt->regs.cpl0_ss = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA); /* Stack */ /* fs and gs unused for the moment. */ /* The newly created context is initially interruptible */ kctxt->regs.eflags = (1 << 9); /* set IF bit */ /* Finally, update the generic kernel/user thread context */ *ctxt = (struct sos_cpu_state*) kctxt; return SOS_OK; } sos_ret_t sos_cpu_ustate_init(struct sos_cpu_state **ctxt, sos_uaddr_t user_start_PC, sos_ui32_t user_start_arg1, sos_ui32_t user_start_arg2, sos_uaddr_t user_initial_SP, sos_vaddr_t kernel_stack_bottom, sos_size_t kernel_stack_size) { /* We are initializing a User thread's context */ struct sos_cpu_ustate *uctxt; /* This is a critical internal function, so that it is assumed that the caller knows what he does: we legitimally assume that values for ctxt, etc. are allways VALID ! */ /* Compute the address of the CPU state to restore on CPU when switching to this new user thread */ sos_vaddr_t uctxt_vaddr = kernel_stack_bottom + kernel_stack_size - sizeof(struct sos_cpu_ustate); uctxt = (struct sos_cpu_ustate*)uctxt_vaddr; /* If needed, poison the kernel stack */ #ifdef SOS_CPU_STATE_DETECT_UNINIT_KERNEL_VARS memset((void*)kernel_stack_bottom, SOS_CPU_STATE_STACK_POISON, kernel_stack_size); #elif defined(SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW) sos_cpu_state_prepare_detect_kernel_stack_overflow(kernel_stack_bottom, kernel_stack_size); #endif /* * Setup the initial context structure, so that the CPU will restore * the initial registers' value for the user thread. The * user thread argument is passed in the EAX register. */ memset(uctxt, 0x0, sizeof(struct sos_cpu_ustate)); /* Tell the CPU context structure that the first instruction to execute will be located at user_start_PC (in user space) */ uctxt->regs.eip = (sos_ui32_t)user_start_PC; /* Tell the CPU where will be the user stack */ uctxt->cpl3_esp = user_initial_SP; /* The parameter to the start function is not passed by the stack to avoid a possible page fault */ uctxt->regs.eax = user_start_arg1; uctxt->regs.ebx = user_start_arg2; /* Setup the segment registers */ uctxt->regs.cs = SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UCODE); /* Code */ uctxt->regs.ds = SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UDATA); /* Data */ uctxt->regs.es = SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UDATA); /* Data */ uctxt->cpl3_ss = SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UDATA); /* User Stack */ /* We need also to update the segment for the kernel stack segment. It will be used when this context will be restored on CPU: initially it will be executing in kernel mode and will switch immediatly to user mode */ uctxt->regs.cpl0_ss = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA); /* Kernel Stack */ /* fs and gs unused for the moment. */ /* The newly created context is initially interruptible */ uctxt->regs.eflags = (1 << 9); /* set IF bit */ /* Finally, update the generic kernel/user thread context */ *ctxt = (struct sos_cpu_state*) uctxt; return SOS_OK; } sos_ret_t sos_cpu_context_is_in_user_mode(const struct sos_cpu_state *ctxt) { /* An interrupted user thread has its CS register set to that of the User code segment */ switch (GET_CPU_CS_REGISTER_VALUE(ctxt->cs)) { case SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UCODE): return TRUE; break; case SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE): return FALSE; break; default: SOS_FATAL_ERROR("Invalid saved context Code segment register: 0x%x (k=%x, u=%x) !", (unsigned) GET_CPU_CS_REGISTER_VALUE(ctxt->cs), SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE), SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UCODE)); break; } /* Should never get here */ return -SOS_EFATAL; } #if defined(SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW) void sos_cpu_state_prepare_detect_kernel_stack_overflow(const struct sos_cpu_state *ctxt, sos_vaddr_t stack_bottom, sos_size_t stack_size) { sos_size_t poison_size = SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW; if (poison_size > stack_size) poison_size = stack_size; memset((void*)stack_bottom, SOS_CPU_STATE_STACK_POISON, poison_size); } void sos_cpu_state_detect_kernel_stack_overflow(const struct sos_cpu_state *ctxt, sos_vaddr_t stack_bottom, sos_size_t stack_size) { unsigned char *c; int i; /* On SOS, "ctxt" corresponds to the address of the esp register of the saved context in Kernel mode (always, even for the interrupted context of a user thread). Here we make sure that this stack pointer is within the allowed stack area */ SOS_ASSERT_FATAL(((sos_vaddr_t)ctxt) >= stack_bottom); SOS_ASSERT_FATAL(((sos_vaddr_t)ctxt) + sizeof(struct sos_cpu_kstate) <= stack_bottom + stack_size); /* Check that the bottom of the stack has not been altered */ for (c = (unsigned char*) stack_bottom, i = 0 ; (i < SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW) && (i < stack_size) ; c++, i++) { SOS_ASSERT_FATAL(SOS_CPU_STATE_STACK_POISON == *c); } } #endif /* ======================================================================= * Public Accessor functions */ sos_vaddr_t sos_cpu_context_get_PC(const struct sos_cpu_state *ctxt) { SOS_ASSERT_FATAL(NULL != ctxt); /* This is the PC of the interrupted context (ie kernel or user context). */ return ctxt->eip; } sos_vaddr_t sos_cpu_context_get_SP(const struct sos_cpu_state *ctxt) { SOS_ASSERT_FATAL(NULL != ctxt); /* 'ctxt' corresponds to the SP of the interrupted context, in Kernel mode. We have to test whether the original interrupted context was that of a kernel or user thread */ if (TRUE == sos_cpu_context_is_in_user_mode(ctxt)) { struct sos_cpu_ustate * uctxt = (struct sos_cpu_ustate*)ctxt; return uctxt->cpl3_esp; } /* On SOS, "ctxt" corresponds to the address of the esp register of the saved context in Kernel mode (always, even for the interrupted context of a user thread). */ return (sos_vaddr_t)ctxt; } sos_ret_t sos_cpu_context_set_EX_return_address(struct sos_cpu_state *ctxt, sos_vaddr_t ret_vaddr) { ctxt->eip = ret_vaddr; return SOS_OK; } void sos_cpu_context_dump(const struct sos_cpu_state *ctxt) { char buf[128]; snprintf(buf, sizeof(buf), "CPU: eip=%x esp0=%x eflags=%x cs=%x ds=%x ss0=%x err=%x", (unsigned)ctxt->eip, (unsigned)ctxt, (unsigned)ctxt->eflags, (unsigned)GET_CPU_CS_REGISTER_VALUE(ctxt->cs), (unsigned)ctxt->ds, (unsigned)ctxt->cpl0_ss, (unsigned)ctxt->error_code); if (TRUE == sos_cpu_context_is_in_user_mode(ctxt)) { struct sos_cpu_ustate * uctxt = (struct sos_cpu_ustate*)ctxt; snprintf(buf, sizeof(buf), "%s esp3=%x ss3=%x", buf, (unsigned)uctxt->cpl3_esp, (unsigned)uctxt->cpl3_ss); } else snprintf(buf, sizeof(buf), "%s [KERNEL MODE]", buf); sos_bochs_putstring(buf); sos_bochs_putstring("\n"); sos_x86_videomem_putstring(23, 0, SOS_X86_VIDEO_FG_BLACK | SOS_X86_VIDEO_BG_LTGRAY, buf); } /* ======================================================================= * Public Accessor functions TO BE USED ONLY BY Exception handlers */ sos_ui32_t sos_cpu_context_get_EX_info(const struct sos_cpu_state *ctxt) { SOS_ASSERT_FATAL(NULL != ctxt); return ctxt->error_code; } sos_vaddr_t sos_cpu_context_get_EX_faulting_vaddr(const struct sos_cpu_state *ctxt) { sos_ui32_t cr2; /* * See Intel Vol 3 (section 5.14): the address of the faulting * virtual address of a page fault is stored in the cr2 * register. * * Actually, we do not store the cr2 register in a saved * kernel thread's context. So we retrieve the cr2's value directly * from the processor. The value we retrieve in an exception handler * is actually the correct one because an exception is synchronous * with the code causing the fault, and cannot be interrupted since * the IDT entries in SOS are "interrupt gates" (ie IRQ are * disabled). */ asm volatile ("movl %%cr2, %0" :"=r"(cr2) : ); return cr2; } /* ======================================================================= * Public Accessor functions TO BE USED ONLY BY the SYSCALL handler */ /* * By convention, the USER SOS programs always pass 4 arguments to the * kernel syscall handler: in eax/../edx. For less arguments, the * unused registers are filled with 0s. For more arguments, the 4th * syscall parameter gives the address of the array containing the * remaining arguments. In any case, eax corresponds to the syscall * IDentifier. */ inline sos_ret_t sos_syscall_get3args(const struct sos_cpu_state *user_ctxt, /* out */unsigned int *arg1, /* out */unsigned int *arg2, /* out */unsigned int *arg3) { *arg1 = user_ctxt->ebx; *arg2 = user_ctxt->ecx; *arg3 = user_ctxt->edx; return SOS_OK; } sos_ret_t sos_syscall_get1arg(const struct sos_cpu_state *user_ctxt, /* out */unsigned int *arg1) { unsigned int unused; return sos_syscall_get3args(user_ctxt, arg1, & unused, & unused); } sos_ret_t sos_syscall_get2args(const struct sos_cpu_state *user_ctxt, /* out */unsigned int *arg1, /* out */unsigned int *arg2) { unsigned int unused; return sos_syscall_get3args(user_ctxt, arg1, arg2, & unused); } /* * sos_syscall_get3args() is defined in cpu_context.c because it needs * to know the structure of a struct spu_state */ sos_ret_t sos_syscall_get4args(const struct sos_cpu_state *user_ctxt, /* out */unsigned int *arg1, /* out */unsigned int *arg2, /* out */unsigned int *arg3, /* out */unsigned int *arg4) { sos_uaddr_t uaddr_other_args; unsigned int other_args[2]; sos_ret_t retval; /* Retrieve the 3 arguments. The last one is an array containing the remaining arguments */ retval = sos_syscall_get3args(user_ctxt, arg1, arg2, (unsigned int *)& uaddr_other_args); if (SOS_OK != retval) return retval; /* Copy the array containing the remaining arguments from user space */ retval = sos_memcpy_from_user((sos_vaddr_t)other_args, (sos_uaddr_t)uaddr_other_args, sizeof(other_args)); if (sizeof(other_args) != retval) return -SOS_EFAULT; *arg3 = other_args[0]; *arg4 = other_args[1]; return SOS_OK; } sos_ret_t sos_syscall_get5args(const struct sos_cpu_state *user_ctxt, /* out */unsigned int *arg1, /* out */unsigned int *arg2, /* out */unsigned int *arg3, /* out */unsigned int *arg4, /* out */unsigned int *arg5) { sos_uaddr_t uaddr_other_args; unsigned int other_args[3]; sos_ret_t retval; /* Retrieve the 3 arguments. The last one is an array containing the remaining arguments */ retval = sos_syscall_get3args(user_ctxt, arg1, arg2, (unsigned int *)& uaddr_other_args); if (SOS_OK != retval) return retval; /* Copy the array containing the remaining arguments from user space */ retval = sos_memcpy_from_user((sos_vaddr_t)other_args, (sos_uaddr_t)uaddr_other_args, sizeof(other_args)); if (sizeof(other_args) != retval) return -SOS_EFAULT; *arg3 = other_args[0]; *arg4 = other_args[1]; *arg5 = other_args[2]; return SOS_OK; } sos_ret_t sos_syscall_get6args(const struct sos_cpu_state *user_ctxt, /* out */unsigned int *arg1, /* out */unsigned int *arg2, /* out */unsigned int *arg3, /* out */unsigned int *arg4, /* out */unsigned int *arg5, /* out */unsigned int *arg6) { sos_uaddr_t uaddr_other_args; unsigned int other_args[4]; sos_ret_t retval; /* Retrieve the 3 arguments. The last one is an array containing the remaining arguments */ retval = sos_syscall_get3args(user_ctxt, arg1, arg2, (unsigned int *)& uaddr_other_args); if (SOS_OK != retval) return retval; /* Copy the array containing the remaining arguments from user space */ retval = sos_memcpy_from_user((sos_vaddr_t)other_args, (sos_uaddr_t)uaddr_other_args, sizeof(other_args)); if (sizeof(other_args) != retval) return -SOS_EFAULT; *arg3 = other_args[0]; *arg4 = other_args[1]; *arg5 = other_args[2]; *arg6 = other_args[3]; return SOS_OK; } sos_ret_t sos_syscall_get7args(const struct sos_cpu_state *user_ctxt, /* out */unsigned int *arg1, /* out */unsigned int *arg2, /* out */unsigned int *arg3, /* out */unsigned int *arg4, /* out */unsigned int *arg5, /* out */unsigned int *arg6, /* out */unsigned int *arg7) { sos_uaddr_t uaddr_other_args; unsigned int other_args[5]; sos_ret_t retval; /* Retrieve the 3 arguments. The last one is an array containing the remaining arguments */ retval = sos_syscall_get3args(user_ctxt, arg1, arg2, (unsigned int *)& uaddr_other_args); if (SOS_OK != retval) return retval; /* Copy the array containing the remaining arguments from user space */ retval = sos_memcpy_from_user((sos_vaddr_t)other_args, (sos_uaddr_t)uaddr_other_args, sizeof(other_args)); if (sizeof(other_args) != retval) return -SOS_EFAULT; *arg3 = other_args[0]; *arg4 = other_args[1]; *arg5 = other_args[2]; *arg6 = other_args[3]; *arg7 = other_args[4]; return SOS_OK; } sos_ret_t sos_syscall_get8args(const struct sos_cpu_state *user_ctxt, /* out */unsigned int *arg1, /* out */unsigned int *arg2, /* out */unsigned int *arg3, /* out */unsigned int *arg4, /* out */unsigned int *arg5, /* out */unsigned int *arg6, /* out */unsigned int *arg7, /* out */unsigned int *arg8) { sos_uaddr_t uaddr_other_args; unsigned int other_args[6]; sos_ret_t retval; /* Retrieve the 3 arguments. The last one is an array containing the remaining arguments */ retval = sos_syscall_get3args(user_ctxt, arg1, arg2, (unsigned int *)& uaddr_other_args); if (SOS_OK != retval) return retval; /* Copy the array containing the remaining arguments from user space */ retval = sos_memcpy_from_user((sos_vaddr_t)other_args, (sos_uaddr_t)uaddr_other_args, sizeof(other_args)); if (sizeof(other_args) != retval) return -SOS_EFAULT; *arg3 = other_args[0]; *arg4 = other_args[1]; *arg5 = other_args[2]; *arg6 = other_args[3]; *arg7 = other_args[4]; *arg8 = other_args[5]; return SOS_OK; } /* ======================================================================= * Backtrace facility. To be used for DEBUGging purpose ONLY. */ sos_ui32_t sos_backtrace(const struct sos_cpu_state *cpu_state, sos_ui32_t max_depth, sos_vaddr_t stack_bottom, sos_size_t stack_size, sos_backtrace_callback_t * backtracer, void *custom_arg) { int depth; sos_vaddr_t callee_PC, caller_frame; /* Cannot backtrace an interrupted user thread ! */ if ((NULL != cpu_state) && (TRUE == sos_cpu_context_is_in_user_mode(cpu_state))) { return 0; } /* * Layout of a frame on the x86 (compiler=gcc): * * funcA calls funcB calls funcC * * .... * funcB Argument 2 * funcB Argument 1 * funcA Return eip * frameB: funcA ebp (ie previous stack frame) * .... * (funcB local variables) * .... * funcC Argument 2 * funcC Argument 1 * funcB Return eip * frameC: funcB ebp (ie previous stack frame == A0) <---- a frame address * .... * (funcC local variables) * .... * * The presence of "ebp" on the stack depends on 2 things: * + the compiler is gcc * + the source is compiled WITHOUT the -fomit-frame-pointer option * In the absence of "ebp", chances are high that the value pushed * at that address is outside the stack boundaries, meaning that the * function will return -SOS_ENOSUP. */ if (cpu_state) { callee_PC = cpu_state->eip; caller_frame = cpu_state->ebp; } else { /* Skip the sos_backtrace() frame */ callee_PC = (sos_vaddr_t)__builtin_return_address(0); caller_frame = (sos_vaddr_t)__builtin_frame_address(1); } for(depth=0 ; depth < max_depth ; depth ++) { /* Call the callback */ backtracer(callee_PC, caller_frame + 8, depth, custom_arg); /* If the frame address is funky, don't go further */ if ( (caller_frame < stack_bottom) || (caller_frame + 4 >= stack_bottom + stack_size) ) return depth; /* Go to caller frame */ callee_PC = *((sos_vaddr_t*) (caller_frame + 4)); caller_frame = *((sos_vaddr_t*) caller_frame); } return depth; } /* ************************************************************* * Function to manage the TSS. This function is not really "public": * it is reserved to the assembler routines defined in * cpu_context_switch.S * * Update the kernel stack address so that the IRQ, syscalls and * exception return in a correct stack location when coming back into * kernel mode. */ void sos_cpu_context_update_kernel_tss(struct sos_cpu_state *next_ctxt) { /* next_ctxt corresponds to an interrupted user thread ? */ if (sos_cpu_context_is_in_user_mode(next_ctxt)) { /* * Yes: "next_ctxt" is an interrupted user thread => we are * going to switch to user mode ! Setup the stack address so * that the user thread "next_ctxt" can come back to the correct * stack location when returning in kernel mode. * * This stack location corresponds to the SP of the next user * thread once its context has been transferred on the CPU, ie * once the CPU has executed all the pop/iret instruction of the * context switch with privilege change. */ kernel_tss.esp0 = ((sos_vaddr_t)next_ctxt) + sizeof(struct sos_cpu_ustate); /* Note: no need to protect this agains IRQ because IRQs are not allowed to update it by themselves, and they are not allowed to block */ } else { /* No: No need to update kernel TSS when we stay in kernel mode */ } }