📄 locore.s
字号:
STRAP(0xb3) STRAP(0xb4) STRAP(0xb5) STRAP(0xb6) STRAP(0xb7) STRAP(0xb8) STRAP(0xb9) STRAP(0xba) STRAP(0xbb) STRAP(0xbc) STRAP(0xbd) STRAP(0xbe) STRAP(0xbf) STRAP(0xc0) STRAP(0xc1) STRAP(0xc2) STRAP(0xc3) STRAP(0xc4) STRAP(0xc5) STRAP(0xc6) STRAP(0xc7) STRAP(0xc8) STRAP(0xc9) STRAP(0xca) STRAP(0xcb) STRAP(0xcc) STRAP(0xcd) STRAP(0xce) STRAP(0xcf) STRAP(0xd0) STRAP(0xd1) STRAP(0xd2) STRAP(0xd3) STRAP(0xd4) STRAP(0xd5) STRAP(0xd6) STRAP(0xd7) STRAP(0xd8) STRAP(0xd9) STRAP(0xda) STRAP(0xdb) STRAP(0xdc) STRAP(0xdd) STRAP(0xde) STRAP(0xdf) STRAP(0xe0) STRAP(0xe1) STRAP(0xe2) STRAP(0xe3) STRAP(0xe4) STRAP(0xe5) STRAP(0xe6) STRAP(0xe7) STRAP(0xe8) STRAP(0xe9) STRAP(0xea) STRAP(0xeb) STRAP(0xec) STRAP(0xed) STRAP(0xee) STRAP(0xef) STRAP(0xf0) STRAP(0xf1) STRAP(0xf2) STRAP(0xf3) STRAP(0xf4) STRAP(0xf5) STRAP(0xf6) STRAP(0xf7) STRAP(0xf8) STRAP(0xf9) STRAP(0xfa) STRAP(0xfb) STRAP(0xfc) STRAP(0xfd) STRAP(0xfe) STRAP(0xff) /* the message buffer is always mapped */_msgbufmapped: .word 1#ifdef DEBUG/* * A hardware red zone is impossible. We simulate one in software by * keeping a `red zone' pointer; if %sp becomes less than this, we panic. * This is expensive and is only enabled when debugging. */#define REDSIZE (8*96) /* some room for bouncing */#define REDSTACK 2048 /* size of `panic: stack overflow' region */ .data_redzone: .word _idle_u + REDSIZE_redstack: .skip REDSTACK .textLpanic_red: .asciz "stack overflow" ALIGN /* set stack pointer redzone to base+minstack; alters base */#define SET_SP_REDZONE(base, tmp) \ add base, REDSIZE, base; \ sethi %hi(_redzone), tmp; \ st base, [tmp + %lo(_redzone)] /* variant with a constant */#define SET_SP_REDZONE_CONST(const, tmp1, tmp2) \ set (const) + REDSIZE, tmp1; \ sethi %hi(_redzone), tmp2; \ st tmp1, [tmp2 + %lo(_redzone)] /* check stack pointer against redzone (uses two temps) */#define CHECK_SP_REDZONE(t1, t2) \ sethi %hi(_redzone), t1; \ ld [t1 + %lo(_redzone)], t2; \ cmp %sp, t2; /* if sp >= t2, not in red zone */ \ bgeu 7f; nop; /* and can continue normally */ \ /* move to panic stack */ \ st %g0, [t1 + %lo(_redzone)]; \ set _redstack + REDSTACK - 96, %sp; \ /* prevent panic() from lowering ipl */ \ sethi %hi(_panicstr), t2; \ set Lpanic_red, t2; \ st t2, [t1 + %lo(_panicstr)]; \ rd %psr, t1; /* t1 = splhigh() */ \ or t1, PSR_PIL, t2; \ wr t2, 0, %psr; \ wr t2, PSR_ET, %psr; /* turn on traps */ \ nop; nop; nop; \ save %sp, -96, %sp; /* preserve current window */ \ sethi %hi(Lpanic_red), %o0; \ call _panic; or %o0, %lo(Lpanic_red), %o0; \7:#else#define SET_SP_REDZONE(base, tmp)#define SET_SP_REDZONE_CONST(const, t1, t2)#define CHECK_SP_REDZONE(t1, t2)#endif/* * The window code must verify user stack addresses before using them. * A user stack pointer is invalid if: * - it is not on an 8 byte boundary; * - its pages (a register window, being 64 bytes, can occupy * two pages) are not readable or writable. * We define three separate macros here for testing user stack addresses. * * PTE_OF_ADDR locates a PTE, branching to a `bad address' * handler if the stack pointer points into the hole in the * address space (i.e., top 3 bits are not either all 1 or all 0); * CMP_PTE_USER_READ compares the located PTE against `user read' mode; * CMP_PTE_USER_WRITE compares the located PTE against `user write' mode. * The compares give `equal' if read or write is OK. * * Note that the user stack pointer usually points into high addresses * (top 3 bits all 1), so that is what we check first. * * The code below also assumes that PTE_OF_ADDR is safe in a delay * slot; it is, at it merely sets its `pte' register to a temporary value. */ /* input: addr, output: pte; aux: bad address label */#define PTE_OF_ADDR(addr, pte, bad) \ sra addr, PG_VSHIFT, pte; \ cmp pte, -1; \ be,a 1f; andn addr, 4095, pte; \ tst pte; \ bne bad; EMPTY; \ andn addr, 4095, pte; \1: /* input: pte; output: condition codes */#define CMP_PTE_USER_READ(pte) \ lda [pte] ASI_PTE, pte; \ srl pte, PG_PROTSHIFT, pte; \ andn pte, (PG_W >> PG_PROTSHIFT), pte; \ cmp pte, PG_PROTUREAD /* input: pte; output: condition codes */#define CMP_PTE_USER_WRITE(pte) \ lda [pte] ASI_PTE, pte; \ srl pte, PG_PROTSHIFT, pte; \ cmp pte, PG_PROTUWRITE/* * The calculations in PTE_OF_ADDR and CMP_PTE_USER_* are rather slow: * in particular, according to Gordon Irlam of the University of Adelaide * in Australia, these consume at least 18 cycles on an SS1 and 37 on an * SS2. Hence, we try to avoid them in the common case. * * A chunk of 64 bytes is on a single page if and only if: * * ((base + 64 - 1) & ~4095) == (base & ~4095) * * Equivalently (and faster to test), the low order bits (base & 4095) must * be small enough so that the sum (base + 63) does not carry out into the * upper page-address bits, i.e., * * (base & 4095) < (4096 - 63) * * so we allow testing that here. This macro is also assumed to be safe * in a delay slot (modulo overwriting its temporary). */#define SLT_IF_1PAGE_RW(addr, tmp) \ and addr, 4095, tmp; \ cmp tmp, (4096 - 63)/* * Every trap that enables traps must set up stack space. * If the trap is from user mode, this involves switching to the kernel * stack for the current process, and we must also set cpcb->pcb_uw * so that the window overflow handler can tell user windows from kernel * windows. * * The number of user windows is: * * cpcb->pcb_uw = (cpcb->pcb_wim - 1 - CWP) % nwindows * * (where pcb_wim = log2(current %wim) and CWP = low 5 bits of %psr). * We compute this expression by table lookup in uwtab[CWP - pcb_wim], * which has been set up as: * * for i in [-nwin+1 .. nwin-1] * uwtab[i] = (nwin - 1 - i) % nwin; * * (If you do not believe this works, try it for yourself.) * * We also keep one or two more tables: * * for i in 0..nwin-1 * wmask[i] = 1 << ((i + 1) % nwindows); * * wmask[CWP] tells whether a `rett' would return into the invalid window. */ .data .skip 32 ! alignment byte & negative indiciesuwtab: .skip 32 ! u_char uwtab[-31..31];wmask: .skip 32 ! u_char wmask[0..31]; .text/* * Things begin to grow uglier.... * * Each trap handler may (always) be running in the trap window. * If this is the case, it cannot enable further traps until it writes * the register windows into the stack (or, if the stack is no good, * the current pcb). * * ASSUMPTIONS: TRAP_SETUP() is called with: * %l0 = %psr * %l1 = return pc * %l2 = return npc * %l3 = (some value that must not be altered) * which means we have 4 registers to work with. * * The `stackspace' argument is the number of stack bytes to allocate * for register-saving, and must be at least -64 (and typically more, * for global registers and %y). * * Trapframes should use -CCFSZ-80. (80 = sizeof(struct trapframe); * see trap.h. This basically means EVERYONE. Interrupt frames could * get away with less, but currently do not.) * * The basic outline here is: * * if (trap came from kernel mode) { * if (we are in the trap window) * save it away; * %sp = %fp - stackspace; * } else { * compute the number of user windows; * if (we are in the trap window) * save it away; * %sp = (top of kernel stack) - stackspace; * } * * Again, the number of user windows is: * * cpcb->pcb_uw = (cpcb->pcb_wim - 1 - CWP) % nwindows * * (where pcb_wim = log2(current %wim) and CWP is the low 5 bits of %psr), * and this is computed as `uwtab[CWP - pcb_wim]'. * * NOTE: if you change this code, you will have to look carefully * at the window overflow and underflow handlers and make sure they * have similar changes made as needed. */#define CALL_CLEAN_TRAP_WINDOW \ sethi %hi(clean_trap_window), %l7; \ jmpl %l7 + %lo(clean_trap_window), %l4; \ mov %g7, %l7 /* save %g7 in %l7 for clean_trap_window */#define TRAP_SETUP(stackspace) \ rd %wim, %l4; \ mov 1, %l5; \ sll %l5, %l0, %l5; \ btst PSR_PS, %l0; \ bz 1f; \ btst %l5, %l4; \ /* came from kernel mode; cond codes indicate trap window */ \ bz,a 3f; \ add %fp, stackspace, %sp; /* want to just set %sp */ \ CALL_CLEAN_TRAP_WINDOW; /* but maybe need to clean first */ \ b 3f; \ add %fp, stackspace, %sp; \1: \ /* came from user mode: compute pcb_nw */ \ sethi %hi(_cpcb), %l6; \ ld [%l6 + %lo(_cpcb)], %l6; \ ld [%l6 + PCB_WIM], %l5; \ and %l0, 31, %l4; \ sub %l4, %l5, %l5; \ set uwtab, %l4; \ ldub [%l4 + %l5], %l5; \ st %l5, [%l6 + PCB_UW]; \ /* cond codes still indicate whether in trap window */ \ bz,a 2f; \ sethi %hi(UPAGES*NBPG+(stackspace)), %l5; \ /* yes, in trap window; must clean it */ \ CALL_CLEAN_TRAP_WINDOW; \ sethi %hi(_cpcb), %l6; \ ld [%l6 + %lo(_cpcb)], %l6; \ sethi %hi(UPAGES*NBPG+(stackspace)), %l5; \2: \ /* trap window is (now) clean: set %sp */ \ or %l5, %lo(UPAGES*NBPG+(stackspace)), %l5; \ add %l6, %l5, %sp; \ SET_SP_REDZONE(%l6, %l5); \3: \ CHECK_SP_REDZONE(%l6, %l5)/* * Interrupt setup is almost exactly like trap setup, but we need to * go to the interrupt stack if (a) we came from user mode or (b) we * came from kernel mode on the kernel stack. */#define INTR_SETUP(stackspace) \ rd %wim, %l4; \ mov 1, %l5; \ sll %l5, %l0, %l5; \ btst PSR_PS, %l0; \ bz 1f; \ btst %l5, %l4; \ /* came from kernel mode; cond codes still indicate trap window */ \ bz,a 0f; \ sethi %hi(_eintstack), %l7; \ CALL_CLEAN_TRAP_WINDOW; \ sethi %hi(_eintstack), %l7; \0: /* now if %fp >= eintstack, we were on the kernel stack */ \ cmp %fp, %l7; \ bge,a 3f; \ add %l7, stackspace, %sp; /* so switch to intstack */ \ b 4f; \ add %fp, stackspace, %sp; /* else stay on intstack */ \1: \ /* came from user mode: compute pcb_nw */ \ sethi %hi(_cpcb), %l6; \ ld [%l6 + %lo(_cpcb)], %l6; \ ld [%l6 + PCB_WIM], %l5; \ and %l0, 31, %l4; \ sub %l4, %l5, %l5; \ set uwtab, %l4; \ ldub [%l4 + %l5], %l5; \ st %l5, [%l6 + PCB_UW]; \ /* cond codes still indicate whether in trap window */ \ bz,a 2f; \ sethi %hi(_eintstack), %l7; \ /* yes, in trap window; must save regs */ \ CALL_CLEAN_TRAP_WINDOW; \ sethi %hi(_eintstack), %l7; \2: \ add %l7, stackspace, %sp; \3: \ SET_SP_REDZONE_CONST(_intstack, %l6, %l5); \4: \ CHECK_SP_REDZONE(%l6, %l5)/* * Handler for making the trap window shiny clean. * * On entry: * cpcb->pcb_nw = number of user windows * %l0 = %psr * %l1 must not be clobbered * %l2 must not be clobbered * %l3 must not be clobbered * %l4 = address for `return' * %l7 = saved %g7 (we put this in a delay slot above, to save work) * * On return: * %wim has changed, along with cpcb->pcb_wim * %g7 has been restored * * Normally, we push only one window. */clean_trap_window: mov %g5, %l5 ! save %g5 mov %g6, %l6 ! ... and %g6/* mov %g7, %l7 ! ... and %g7 (already done for us) */ sethi %hi(_cpcb), %g6 ! get current pcb ld [%g6 + %lo(_cpcb)], %g6 /* Figure out whether it is a user window (cpcb->pcb_uw > 0). */ ld [%g6 + PCB_UW], %g7 deccc %g7 bge ctw_user save %g0, %g0, %g0 ! in any case, enter window to save /* The window to be pushed is a kernel window. */ std %l0, [%sp + (0*8)]ctw_merge: std %l2, [%sp + (1*8)] std %l4, [%sp + (2*8)] std %l6, [%sp + (3*8)] std %i0, [%sp + (4*8)] std %i2, [%sp + (5*8)] std %i4, [%sp + (6*8)] std %i6, [%sp + (7*8)] /* Set up new window invalid mask, and update cpcb->pcb_wim. */ rd %psr, %g7 ! g7 = (junk << 5) + new_cwp mov 1, %g5 ! g5 = 1 << new_cwp; sll %g5, %g7, %g5 wr %g5, 0, %wim ! setwim(g5); and %g7, 31, %g7 ! cpcb->pcb_wim = g7 & 31; st %g7, [%g6 + PCB_WIM] nop restore ! back to trap window mov %l5, %g5 ! restore g5 mov %l6, %g6 ! ... and g6 jmp %l4 + 8 ! return to caller mov %l7, %g7 ! ... and g7 /* NOTREACHED */ctw_user: /* * The window to be pushed is a user window. * We must verify the stack pointer (alignment & permissions). * See comments above definition of PTE_OF_ADDR. */ st %g7, [%g6 + PCB_UW] ! cpcb->pcb_uw--; btst 7, %sp ! if not aligned, bne ctw_invalid ! choke on it EMPTY PTE_OF_ADDR(%sp, %g7, ctw_invalid) CMP_PTE_USER_WRITE(%g7) ! likewise if not writable bne ctw_invalid EMPTY SLT_IF_1PAGE_RW(%sp, %g7) bl,a ctw_merge ! all ok if only 1 std %l0, [%sp] add %sp, 7*8, %g5 ! check last addr too PTE_OF_ADDR(%g5, %g7, ctw_invalid) CMP_PTE_USER_WRITE(%g7) be,a ctw_merge ! all ok: store <l0,l1> and merge std %l0, [%sp]
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -