📄 entry.s
字号:
#define TRY \ .section __ex_table, "a"; \ .word 66f, 67f; \ .text; \66:#define CATCH \67:ENTRY(fast_syscall_xtensa) xsr a3, EXCSAVE_1 # restore a3, excsave1 s32i a7, a2, PT_AREG7 # we need an additional register movi a7, 4 # sizeof(unsigned int) access_ok a3, a7, a0, a2, .Leac # a0: scratch reg, a2: sp addi a6, a6, -1 # assuming SYS_XTENSA_ATOMIC_SET = 1 _bgeui a6, SYS_XTENSA_COUNT - 1, .Lill _bnei a6, SYS_XTENSA_ATOMIC_CMP_SWP - 1, .Lnswp /* Fall through for ATOMIC_CMP_SWP. */.Lswp: /* Atomic compare and swap */TRY l32i a0, a3, 0 # read old value bne a0, a4, 1f # same as old value? jumpTRY s32i a5, a3, 0 # different, modify value l32i a7, a2, PT_AREG7 # restore a7 l32i a0, a2, PT_AREG0 # restore a0 movi a2, 1 # and return 1 addi a6, a6, 1 # restore a6 (really necessary?) rfe1: l32i a7, a2, PT_AREG7 # restore a7 l32i a0, a2, PT_AREG0 # restore a0 movi a2, 0 # return 0 (note that we cannot set addi a6, a6, 1 # restore a6 (really necessary?) rfe.Lnswp: /* Atomic set, add, and exg_add. */TRY l32i a7, a3, 0 # orig add a0, a4, a7 # + arg moveqz a0, a4, a6 # setTRY s32i a0, a3, 0 # write new value mov a0, a2 mov a2, a7 l32i a7, a0, PT_AREG7 # restore a7 l32i a0, a0, PT_AREG0 # restore a0 addi a6, a6, 1 # restore a6 (really necessary?) rfeCATCH.Leac: l32i a7, a2, PT_AREG7 # restore a7 l32i a0, a2, PT_AREG0 # restore a0 movi a2, -EFAULT rfe.Lill: l32i a7, a2, PT_AREG0 # restore a7 l32i a0, a2, PT_AREG0 # restore a0 movi a2, -EINVAL rfe/* fast_syscall_spill_registers. * * Entry condition: * * a0: trashed, original value saved on stack (PT_AREG0) * a1: a1 * a2: new stack pointer, original in DEPC * a3: dispatch table * depc: a2, original value saved on stack (PT_DEPC) * excsave_1: a3 * * Note: We assume the stack pointer is EXC_TABLE_KSTK in the fixup handler. * Note: We don't need to save a2 in depc (return value) */ENTRY(fast_syscall_spill_registers) /* Register a FIXUP handler (pass current wb as a parameter) */ movi a0, fast_syscall_spill_registers_fixup s32i a0, a3, EXC_TABLE_FIXUP rsr a0, WINDOWBASE s32i a0, a3, EXC_TABLE_PARAM /* Save a3 and SAR on stack. */ rsr a0, SAR xsr a3, EXCSAVE_1 # restore a3 and excsave_1 s32i a0, a2, PT_AREG4 # store SAR to PT_AREG4 s32i a3, a2, PT_AREG3 /* The spill routine might clobber a7, a11, and a15. */ s32i a7, a2, PT_AREG5 s32i a11, a2, PT_AREG6 s32i a15, a2, PT_AREG7 call0 _spill_registers # destroys a3, DEPC, and SAR /* Advance PC, restore registers and SAR, and return from exception. */ l32i a3, a2, PT_AREG4 l32i a0, a2, PT_AREG0 wsr a3, SAR l32i a3, a2, PT_AREG3 /* Restore clobbered registers. */ l32i a7, a2, PT_AREG5 l32i a11, a2, PT_AREG6 l32i a15, a2, PT_AREG7 movi a2, 0 rfe/* Fixup handler. * * We get here if the spill routine causes an exception, e.g. tlb miss. * We basically restore WINDOWBASE and WINDOWSTART to the condition when * we entered the spill routine and jump to the user exception handler. * * a0: value of depc, original value in depc * a2: trashed, original value in EXC_TABLE_DOUBLE_SAVE * a3: exctable, original value in excsave1 */fast_syscall_spill_registers_fixup: rsr a2, WINDOWBASE # get current windowbase (a2 is saved) xsr a0, DEPC # restore depc and a0 ssl a2 # set shift (32 - WB) /* We need to make sure the current registers (a0-a3) are preserved. * To do this, we simply set the bit for the current window frame * in WS, so that the exception handlers save them to the task stack. */ rsr a3, EXCSAVE_1 # get spill-mask slli a2, a3, 1 # shift left by one slli a3, a2, 32-WSBITS src a2, a2, a3 # a1 = xxwww1yyxxxwww1yy...... wsr a2, WINDOWSTART # set corrected windowstart movi a3, exc_table l32i a2, a3, EXC_TABLE_DOUBLE_SAVE # restore a2 l32i a3, a3, EXC_TABLE_PARAM # original WB (in user task) /* Return to the original (user task) WINDOWBASE. * We leave the following frame behind: * a0, a1, a2 same * a3: trashed (saved in excsave_1) * depc: depc (we have to return to that address) * excsave_1: a3 */ wsr a3, WINDOWBASE rsync /* We are now in the original frame when we entered _spill_registers: * a0: return address * a1: used, stack pointer * a2: kernel stack pointer * a3: available, saved in EXCSAVE_1 * depc: exception address * excsave: a3 * Note: This frame might be the same as above. */#ifdef SIGNAL_HANDLING_IN_DOUBLE_EXCEPTION /* Restore registers we precautiously saved. * We have the value of the 'right' a3 */ l32i a7, a2, PT_AREG5 l32i a11, a2, PT_AREG6 l32i a15, a2, PT_AREG7#endif /* Setup stack pointer. */ addi a2, a2, -PT_USER_SIZE s32i a0, a2, PT_AREG0 /* Make sure we return to this fixup handler. */ movi a3, fast_syscall_spill_registers_fixup_return s32i a3, a2, PT_DEPC # setup depc /* Jump to the exception handler. */ movi a3, exc_table rsr a0, EXCCAUSE addx4 a0, a0, a3 # find entry in table l32i a0, a0, EXC_TABLE_FAST_USER # load handler jx a0fast_syscall_spill_registers_fixup_return: /* When we return here, all registers have been restored (a2: DEPC) */ wsr a2, DEPC # exception address /* Restore fixup handler. */ xsr a3, EXCSAVE_1 movi a2, fast_syscall_spill_registers_fixup s32i a2, a3, EXC_TABLE_FIXUP rsr a2, WINDOWBASE s32i a2, a3, EXC_TABLE_PARAM l32i a2, a3, EXC_TABLE_KSTK#ifdef SIGNAL_HANDLING_IN_DOUBLE_EXCEPTION /* Save registers again that might be clobbered. */ s32i a7, a2, PT_AREG5 s32i a11, a2, PT_AREG6 s32i a15, a2, PT_AREG7#endif /* Load WB at the time the exception occurred. */ rsr a3, SAR # WB is still in SAR neg a3, a3 wsr a3, WINDOWBASE rsync /* Restore a3 and return. */ movi a3, exc_table xsr a3, EXCSAVE_1 rfde/* * spill all registers. * * This is not a real function. The following conditions must be met: * * - must be called with call0. * - uses DEPC, a3 and SAR. * - the last 'valid' register of each frame are clobbered. * - the caller must have registered a fixup handler * (or be inside a critical section) * - PS_EXCM must be set (PS_WOE cleared?) */ENTRY(_spill_registers) /* * Rotate ws so that the current windowbase is at bit 0. * Assume ws = xxxwww1yy (www1 current window frame). * Rotate ws right so that a2 = yyxxxwww1. */ wsr a2, DEPC # preserve a2 rsr a2, WINDOWBASE rsr a3, WINDOWSTART ssr a2 # holds WB slli a2, a3, WSBITS or a3, a3, a2 # a2 = xxxwww1yyxxxwww1yy srl a3, a3 /* We are done if there are no more than the current register frame. */ extui a3, a3, 1, WSBITS-2 # a3 = 0yyxxxwww movi a2, (1 << (WSBITS-1)) _beqz a3, .Lnospill # only one active frame? jump /* We want 1 at the top, so that we return to the current windowbase */ or a3, a3, a2 # 1yyxxxwww /* Skip empty frames - get 'oldest' WINDOWSTART-bit. */ wsr a3, WINDOWSTART # save shifted windowstart neg a2, a3 and a3, a2, a3 # first bit set from right: 000010000 ffs_ws a2, a3 # a2: shifts to skip empty frames movi a3, WSBITS sub a2, a3, a2 # WSBITS-a2:number of 0-bits from right ssr a2 # save in SAR for later. rsr a3, WINDOWBASE add a3, a3, a2 rsr a2, DEPC # restore a2 wsr a3, WINDOWBASE rsync rsr a3, WINDOWSTART srl a3, a3 # shift windowstart /* WB is now just one frame below the oldest frame in the register window. WS is shifted so the oldest frame is in bit 0, thus, WB and WS differ by one 4-register frame. */ /* Save frames. Depending what call was used (call4, call8, call12), * we have to save 4,8. or 12 registers. */ _bbsi.l a3, 1, .Lc4 _bbsi.l a3, 2, .Lc8 /* Special case: we have a call12-frame starting at a4. */ _bbci.l a3, 3, .Lc12 # bit 3 shouldn't be zero! (Jump to Lc12 first) s32e a4, a1, -16 # a1 is valid with an empty spill area l32e a4, a5, -12 s32e a8, a4, -48 mov a8, a4 l32e a4, a1, -16 j .Lc12c.Lloop: _bbsi.l a3, 1, .Lc4 _bbci.l a3, 2, .Lc12.Lc8: s32e a4, a13, -16 l32e a4, a5, -12 s32e a8, a4, -32 s32e a5, a13, -12 s32e a6, a13, -8 s32e a7, a13, -4 s32e a9, a4, -28 s32e a10, a4, -24 s32e a11, a4, -20 srli a11, a3, 2 # shift windowbase by 2 rotw 2 _bnei a3, 1, .Lloop.Lexit: /* Done. Do the final rotation, set WS, and return. */ rotw 1 rsr a3, WINDOWBASE ssl a3 movi a3, 1 sll a3, a3 wsr a3, WINDOWSTART.Lnospill: jx a0.Lc4: s32e a4, a9, -16 s32e a5, a9, -12 s32e a6, a9, -8 s32e a7, a9, -4 srli a7, a3, 1 rotw 1 _bnei a3, 1, .Lloop j .Lexit.Lc12: _bbci.l a3, 3, .Linvalid_mask # bit 2 shouldn't be zero! /* 12-register frame (call12) */ l32e a2, a5, -12 s32e a8, a2, -48 mov a8, a2.Lc12c: s32e a9, a8, -44 s32e a10, a8, -40 s32e a11, a8, -36 s32e a12, a8, -32 s32e a13, a8, -28 s32e a14, a8, -24 s32e a15, a8, -20 srli a15, a3, 3 /* The stack pointer for a4..a7 is out of reach, so we rotate the * window, grab the stackpointer, and rotate back. * Alternatively, we could also use the following approach, but that * makes the fixup routine much more complicated: * rotw 1 * s32e a0, a13, -16 * ... * rotw 2 */ rotw 1 mov a5, a13 rotw -1 s32e a4, a9, -16 s32e a5, a9, -12 s32e a6, a9, -8 s32e a7, a9, -4 rotw 3 _beqi a3, 1, .Lexit j .Lloop.Linvalid_mask: /* We get here because of an unrecoverable error in the window * registers. If we are in user space, we kill the application, * however, this condition is unrecoverable in kernel space. */ rsr a0, PS _bbci.l a0, PS_UM_BIT, 1f /* User space: Setup a dummy frame and kill application. * Note: We assume EXC_TABLE_KSTK contains a valid stack pointer. */ movi a0, 1 movi a1, 0 wsr a0, WINDOWSTART wsr a1, WINDOWBASE rsync movi a0, 0 movi a3, exc_table l32i a1, a3, EXC_TABLE_KSTK wsr a3, EXCSAVE_1 movi a4, (1 << PS_WOE_BIT) | 1 wsr a4, PS rsync movi a6, SIGSEGV movi a4, do_exit callx4 a41: /* Kernel space: PANIC! */ wsr a0, EXCSAVE_1 movi a0, unrecoverable_exception callx0 a0 # should not return1: j 1b/* * We should never get here. Bail out! */ENTRY(fast_second_level_miss_double_kernel)1: movi a0, unrecoverable_exception callx0 a0 # should not return1: j 1b/* First-level entry handler for user, kernel, and double 2nd-level * TLB miss exceptions. Note that for now, user and kernel miss * exceptions share the same entry point and are handled identically. * * An old, less-efficient C version of this function used to exist. * We include it below, interleaved as comments, for reference. * * Entry condition: * * a0: trashed, original value saved on stack (PT_AREG0) * a1: a1 * a2: new stack pointer, original in DEPC * a3: dispatch table * depc: a2, original value saved on stack (PT_DEPC) * excsave_1: a3 * * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception */ENTRY(fast_second_level_miss) /* Save a1. Note: we don't expect a double exception. */ s32i a1, a2, PT_AREG1 /* We need to map the page of PTEs for the user task. Find * the pointer to that page. Also, it's possible for tsk->mm * to be NULL while tsk->active_mm is nonzero if we faulted on * a vmalloc address. In that rare case, we must use * active_mm instead to avoid a fault in this handler. See * * http://mail.nl.linux.org/linux-mm/2002-08/msg00258.html * (or search Internet on "mm vs. active_mm") * * if (!mm) * mm = tsk->active_mm; * pgd = pgd_offset (mm, regs->excvaddr); * pmd = pmd_offset (pgd, regs->excvaddr); * pmdval = *pmd; */ GET_CURRENT(a1,a2) l32i a0, a1, TASK_MM # tsk->mm beqz a0, 9f /* We deliberately destroy a3 that holds the exception table. */8: rsr a3, EXCVADDR # fault address _PGD_OFFSET(a0, a3, a1) l32i a0, a0, 0 # read pmdval beqz a0, 2f /* Read ptevaddr and convert to top of page-table page. * * vpnval = read_ptevaddr_register() & PAGE_MASK; * vpnval += DTLB_WAY_PGTABLE; * pteval = mk_pte (virt_to_page(pmd_val(pmdval)), PAGE_KERNEL); * write_dtlb_entry (pteval, vpnval); * * The messy computation for 'pteval' above really simplifies * into the following: * * pteval = ((pmdval - PAGE_OFFSET) & PAGE_MASK) | PAGE_DIRECTORY */ movi a1, -PAGE_OFFSET add a0, a0, a1 # pmdval - PAGE_OFFSET extui a1, a0, 0, PAGE_SHIFT # ... & PAGE_MASK xor a0, a0, a1 movi a1, _PAGE_DIRECTORY or a0, a0, a1 # ... | PAGE_DIRECTORY /* * We utilize all three wired-ways (7-9) to hold pmd translations. * Memory regions are mapped to the DTLBs according to bits 28 and 29. * This allows to map the three most common regions to three different * DTLBs:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -