123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- /*
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2021-7-14 JasonHu first version
- */
- #include <rthw.h>
- #include <stddef.h>
- #include <rtconfig.h>
- #include <rtdbg.h>
- #ifdef ARCH_MM_MMU
- #include <stackframe.h>
- #include <interrupt.h>
- #include <segment.h>
- #include <mmu.h>
- #include <page.h>
- #include <lwp_mm_area.h>
- #include <lwp_user_mm.h>
- #include <lwp_arch.h>
- #ifdef RT_USING_SIGNALS
- #include <lwp_signal.h>
- #endif /* RT_USING_SIGNALS */
- extern size_t g_mmu_table[];
- int arch_expand_user_stack(void *addr)
- {
- int ret = 0;
- size_t stack_addr = (size_t)addr;
- stack_addr &= ~PAGE_OFFSET_MASK;
- if ((stack_addr >= (size_t)USER_STACK_VSTART) && (stack_addr < (size_t)USER_STACK_VEND))
- {
- void *map = lwp_map_user(lwp_self(), (void *)stack_addr, PAGE_SIZE, RT_FALSE);
- if (map || lwp_user_accessable(addr, 1))
- {
- ret = 1; /* map success */
- }
- else /* map failed, send signal SIGSEGV */
- {
- #ifdef RT_USING_SIGNALS
- dbg_log(DBG_ERROR, "[fault] thread %s mapped addr %p failed!\n", rt_thread_self()->parent.name, addr);
- lwp_thread_kill(rt_thread_self(), SIGSEGV);
- ret = 1; /* return 1, will return back to intr, then check exit */
- #endif
- }
- }
- else /* not stack, send signal SIGSEGV */
- {
- #ifdef RT_USING_SIGNALS
- dbg_log(DBG_ERROR, "[fault] thread %s access unmapped addr %p!\n", rt_thread_self()->parent.name, addr);
- lwp_thread_kill(rt_thread_self(), SIGSEGV);
- ret = 1; /* return 1, will return back to intr, then check exit */
- #endif
- }
- return ret;
- }
- void *get_thread_kernel_stack_top(rt_thread_t thread)
- {
- return RT_NULL;
- }
- /**
- * don't support this in i386, it's ok!
- */
- void *arch_get_user_sp()
- {
- return RT_NULL;
- }
- int arch_user_space_init(struct rt_lwp *lwp)
- {
- rt_size_t *mmu_table;
- mmu_table = (rt_size_t *)rt_pages_alloc(0);
- if (!mmu_table)
- {
- return -1;
- }
- rt_memset(mmu_table, 0, ARCH_PAGE_SIZE);
- lwp->end_heap = USER_HEAP_VADDR;
- memcpy(mmu_table, g_mmu_table, ARCH_PAGE_SIZE / 4);
- memset((rt_uint8_t *)mmu_table + ARCH_PAGE_SIZE / 4, 0, ARCH_PAGE_SIZE / 4 * 3);
- rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, mmu_table, ARCH_PAGE_SIZE);
- if (rt_hw_mmu_map_init(&lwp->mmu_info, (void*)USER_VADDR_START, USER_VADDR_TOP - USER_VADDR_START, mmu_table, PV_OFFSET) < 0)
- {
- rt_pages_free(mmu_table, 0);
- return -1;
- }
- return 0;
- }
- void *arch_kernel_mmu_table_get(void)
- {
- return (void *)((char *)g_mmu_table);
- }
- void arch_user_space_vtable_free(struct rt_lwp *lwp)
- {
- if (lwp && lwp->mmu_info.vtable)
- {
- rt_pages_free(lwp->mmu_info.vtable, 0);
- lwp->mmu_info.vtable = NULL;
- }
- }
- void arch_set_thread_area(void *p)
- {
- rt_hw_seg_tls_set((rt_ubase_t) p);
- rt_thread_t cur = rt_thread_self();
- cur->thread_idr = p; /* update thread idr after first set */
- }
- void *arch_get_tidr(void)
- {
- rt_thread_t cur = rt_thread_self();
- if (!cur->lwp) /* no lwp, don't get thread idr from tls seg */
- return NULL;
- return (void *)rt_hw_seg_tls_get(); /* get thread idr from tls seg */
- }
- void arch_set_tidr(void *p)
- {
- rt_thread_t cur = rt_thread_self();
- if (!cur->lwp) /* no lwp, don't set thread idr to tls seg */
- return;
- rt_hw_seg_tls_set((rt_ubase_t) p); /* set tls seg addr as thread idr */
- }
- static void lwp_user_stack_init(rt_hw_stack_frame_t *frame)
- {
- frame->ds = frame->es = USER_DATA_SEL;
- frame->cs = USER_CODE_SEL;
- frame->ss = USER_STACK_SEL;
- frame->gs = USER_TLS_SEL;
- frame->fs = 0; /* unused */
- frame->edi = frame->esi = \
- frame->ebp = frame->esp_dummy = 0;
- frame->eax = frame->ebx = \
- frame->ecx = frame->edx = 0;
- frame->error_code = 0;
- frame->vec_no = 0;
- frame->eflags = (EFLAGS_MBS | EFLAGS_IF_1 | EFLAGS_IOPL_3);
- }
- extern void lwp_switch_to_user(void *frame);
- /**
- * user entry, set frame.
- * at the end of execute, we need enter user mode,
- * in x86, we can set stack, arg, text entry in a stack frame,
- * then pop then into register, final use iret to switch kernel mode to user mode.
- */
- void arch_start_umode(void *args, const void *text, void *ustack, void *k_stack)
- {
- rt_uint8_t *stk = k_stack;
- stk -= sizeof(struct rt_hw_stack_frame);
- struct rt_hw_stack_frame *frame = (struct rt_hw_stack_frame *)stk;
- lwp_user_stack_init(frame);
- frame->esp = (rt_uint32_t)ustack - 32;
- frame->ebx = (rt_uint32_t)args;
- frame->eip = (rt_uint32_t)text;
- lwp_switch_to_user(frame);
- /* should never return */
- }
- void lwp_exec_user(void *args, void *kernel_stack, void *user_entry)
- {
- arch_start_umode(args, (const void *)user_entry, (void *)USER_STACK_VEND, kernel_stack);
- }
- extern void lwp_thread_return();
- extern void lwp_thread_return_end();
- static void *lwp_copy_return_code_to_user_stack(void *ustack)
- {
- size_t size = (size_t)lwp_thread_return_end - (size_t)lwp_thread_return;
- void *retcode = (void *)((size_t)ustack - size);
- memcpy(retcode, (void *)lwp_thread_return, size);
- return retcode;
- }
- /**
- * when called sys_thread_create, need create a thread, after thread stared, will come here,
- * like arch_start_umode, will enter user mode, but we must set thread exit function. it looks like:
- * void func(void *arg)
- * {
- * ...
- * }
- * when thread func return, we must call exit code to exit thread, or not the program runs away.
- * so we need copy exit code to user and call exit code when func return.
- */
- void arch_crt_start_umode(void *args, const void *text, void *ustack, void *k_stack)
- {
- RT_ASSERT(ustack != NULL);
- rt_uint8_t *stk;
- stk = (rt_uint8_t *)((rt_uint8_t *)k_stack + sizeof(rt_ubase_t));
- stk = (rt_uint8_t *)RT_ALIGN_DOWN(((rt_ubase_t)stk), sizeof(rt_ubase_t));
- stk -= sizeof(struct rt_hw_stack_frame);
- struct rt_hw_stack_frame *frame = (struct rt_hw_stack_frame *)stk;
- lwp_user_stack_init(frame);
- /* make user thread stack */
- unsigned long *retcode = lwp_copy_return_code_to_user_stack(ustack); /* copy ret code */
- unsigned long *retstack = (unsigned long *)RT_ALIGN_DOWN(((rt_ubase_t)retcode), sizeof(rt_ubase_t));
- /**
- * x86 call stack
- *
- * retcode here
- *
- * arg n
- * arg n - 1
- * ...
- * arg 2
- * arg 1
- * arg 0
- * eip (caller return addr, point to retcode)
- * esp
- */
- *(--retstack) = (unsigned long) args; /* arg */
- *(--retstack) = (unsigned long) retcode; /* ret eip */
- frame->esp = (rt_uint32_t)retstack;
- frame->eip = (rt_uint32_t)text;
- lwp_switch_to_user(frame);
- /* should never return */
- }
- rt_thread_t rt_thread_sp_to_thread(void *spmember_addr)
- {
- return (rt_thread_t)(((rt_ubase_t)spmember_addr) - (offsetof(struct rt_thread, sp)));
- }
- /**
- * set exec context for fork/clone.
- * user_stack(unused)
- */
- void arch_set_thread_context(void *exit_addr, void *new_thread_stack, void *user_stack, void **thread_sp)
- {
- /**
- * thread kernel stack was set to tss.esp0, when intrrupt/syscall occur,
- * the stack frame will store in kernel stack top, so we can get the stack
- * frame by kernel stack top.
- */
- rt_hw_stack_frame_t *frame = (rt_hw_stack_frame_t *)((rt_ubase_t)new_thread_stack - sizeof(rt_hw_stack_frame_t));
- frame->eax = 0; /* child return 0 */
- rt_hw_context_t *context = (rt_hw_context_t *) (((rt_uint32_t *)frame) - HW_CONTEXT_MEMBER_NR);
- context->eip = (void *)exit_addr; /* when thread started, jump to intr exit for enter user mode */
- context->ebp = context->ebx = context->esi = context->edi = 0;
- /**
- * set sp as the address of first member of rt_hw_context,
- * when scheduler call switch, pop stack from context stack.
- */
- *thread_sp = (void *)&context->ebp;
- /**
- * after set context, the stack like this:
- *
- * -----------
- * stack frame| eax = 0
- * -----------
- * context(only HW_CONTEXT_MEMBER_NR)| eip = rt_hw_intr_exit
- * -----------
- * thread sp | to <- rt_hw_context_switch(from, to)
- * -----------
- */
- }
- #ifdef RT_USING_SIGNALS
- #define SIGNAL_RET_CODE_SIZE 16
- struct rt_signal_frame
- {
- char *ret_addr; /* return addr when handler return */
- int signo; /* signal for user handler arg */
- rt_hw_stack_frame_t frame; /* save kernel signal stack */
- char ret_code[SIGNAL_RET_CODE_SIZE]; /* save return code */
- };
- typedef struct rt_signal_frame rt_signal_frame_t;
- extern void lwp_signal_return();
- extern void lwp_signal_return_end();
- void lwp_try_do_signal(rt_hw_stack_frame_t *frame)
- {
- if (!lwp_signal_check())
- return;
- /* 1. backup signal mask */
- int signal = lwp_signal_backup((void *) frame->esp, (void *) frame->eip, (void *) frame->eflags);
- /* 2. get signal handler */
- lwp_sighandler_t handler = lwp_sighandler_get(signal);
- if (handler == RT_NULL) /* no handler, ignore */
- {
- lwp_signal_restore();
- return;
- }
- rt_base_t level = rt_hw_interrupt_disable();
- /* 3. backup frame */
- rt_signal_frame_t *sig_frame = (rt_signal_frame_t *)((frame->esp - sizeof(rt_signal_frame_t)) & -8UL);
- memcpy(&sig_frame->frame, frame, sizeof(rt_hw_stack_frame_t));
- sig_frame->signo = signal;
- /**
- * 4. copy user return code into user stack
- *
- * save current frame on user stack. the user stack like:
- *
- * ----------
- * user code stack
- * ----------+ -> esp before enter kernel
- * signal frame
- * ----------+ -> esp when handle signal handler
- * signal handler stack
- * ----------
- */
- size_t ret_code_size = (size_t)lwp_signal_return_end - (size_t)lwp_signal_return;
- memcpy(sig_frame->ret_code, (void *)lwp_signal_return, ret_code_size);
- sig_frame->ret_addr = sig_frame->ret_code;
- /* 5. jmp to user execute handler, update frame register info */
- lwp_user_stack_init(frame);
- frame->eip = (rt_uint32_t) handler;
- frame->esp = (rt_uint32_t) sig_frame;
- rt_hw_interrupt_enable(level);
- }
- void lwp_signal_do_return(rt_hw_stack_frame_t *frame)
- {
- /**
- * ASSUME: in x86, each stack push and pop element is 4 byte. so STACK_ELEM_SIZE = sizeof(int) => 4.
- * when signal handler return, the stack move to the buttom of signal frame.
- * but return will pop eip from esp, then {esp += STACK_ELEM_SIZE}, thus {esp = (signal frame) + STACK_ELEM_SIZE}.
- * so {(signal frame) = esp - STACK_ELEM_SIZE}
- */
- rt_signal_frame_t *sig_frame = (rt_signal_frame_t *)(frame->esp - sizeof(rt_uint32_t));
- memcpy(frame, &sig_frame->frame, sizeof(rt_hw_stack_frame_t));
- /**
- * restore signal info, but don't use rt_user_context,
- * we use sig_frame to restore stack frame
- */
- lwp_signal_restore();
- }
- #endif /* RT_USING_SIGNALS */
- #endif /* ARCH_MM_MMU */
|