11da177e4SLinus Torvalds/* $Id: wuf.S,v 1.39 2000/01/08 16:38:18 anton Exp $ 21da177e4SLinus Torvalds * wuf.S: Window underflow trap handler for the Sparc. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 1995 David S. Miller 51da177e4SLinus Torvalds */ 61da177e4SLinus Torvalds 71da177e4SLinus Torvalds#include <asm/contregs.h> 81da177e4SLinus Torvalds#include <asm/page.h> 91da177e4SLinus Torvalds#include <asm/ptrace.h> 101da177e4SLinus Torvalds#include <asm/psr.h> 111da177e4SLinus Torvalds#include <asm/smp.h> 121da177e4SLinus Torvalds#include <asm/asi.h> 131da177e4SLinus Torvalds#include <asm/winmacro.h> 141da177e4SLinus Torvalds#include <asm/asmmacro.h> 151da177e4SLinus Torvalds#include <asm/thread_info.h> 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds/* Just like the overflow handler we define macros for registers 181da177e4SLinus Torvalds * with fixed meanings in this routine. 191da177e4SLinus Torvalds */ 201da177e4SLinus Torvalds#define t_psr l0 211da177e4SLinus Torvalds#define t_pc l1 221da177e4SLinus Torvalds#define t_npc l2 231da177e4SLinus Torvalds#define t_wim l3 241da177e4SLinus Torvalds/* Don't touch the above registers or else you die horribly... */ 251da177e4SLinus Torvalds 261da177e4SLinus Torvalds/* Now macros for the available scratch registers in this routine. */ 271da177e4SLinus Torvalds#define twin_tmp1 l4 281da177e4SLinus Torvalds#define twin_tmp2 l5 291da177e4SLinus Torvalds 301da177e4SLinus Torvalds#define curptr g6 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds .text 331da177e4SLinus Torvalds .align 4 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds /* The trap entry point has executed the following: 361da177e4SLinus Torvalds * 371da177e4SLinus Torvalds * rd %psr, %l0 381da177e4SLinus Torvalds * rd %wim, %l3 391da177e4SLinus Torvalds * b fill_window_entry 401da177e4SLinus Torvalds * andcc %l0, PSR_PS, %g0 411da177e4SLinus Torvalds */ 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds /* Datum current_thread_info->uwinmask contains at all times a bitmask 441da177e4SLinus Torvalds * where if any user windows are active, at least one bit will 451da177e4SLinus Torvalds * be set in to mask. If no user windows are active, the bitmask 461da177e4SLinus Torvalds * will be all zeroes. 471da177e4SLinus Torvalds */ 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds /* To get an idea of what has just happened to cause this 501da177e4SLinus Torvalds * trap take a look at this diagram: 511da177e4SLinus Torvalds * 521da177e4SLinus Torvalds * 1 2 3 4 <-- Window number 531da177e4SLinus Torvalds * ---------- 541da177e4SLinus Torvalds * T O W I <-- Symbolic name 551da177e4SLinus Torvalds * 561da177e4SLinus Torvalds * O == the window that execution was in when 571da177e4SLinus Torvalds * the restore was attempted 581da177e4SLinus Torvalds * 591da177e4SLinus Torvalds * T == the trap itself has save'd us into this 601da177e4SLinus Torvalds * window 611da177e4SLinus Torvalds * 621da177e4SLinus Torvalds * W == this window is the one which is now invalid 631da177e4SLinus Torvalds * and must be made valid plus loaded from the 641da177e4SLinus Torvalds * stack 651da177e4SLinus Torvalds * 661da177e4SLinus Torvalds * I == this window will be the invalid one when we 671da177e4SLinus Torvalds * are done and return from trap if successful 681da177e4SLinus Torvalds */ 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds /* BEGINNING OF PATCH INSTRUCTIONS */ 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds /* On 7-window Sparc the boot code patches fnwin_patch1 731da177e4SLinus Torvalds * with the following instruction. 741da177e4SLinus Torvalds */ 751da177e4SLinus Torvalds .globl fnwin_patch1_7win, fnwin_patch2_7win 761da177e4SLinus Torvaldsfnwin_patch1_7win: srl %t_wim, 6, %twin_tmp2 771da177e4SLinus Torvaldsfnwin_patch2_7win: and %twin_tmp1, 0x7f, %twin_tmp1 781da177e4SLinus Torvalds /* END OF PATCH INSTRUCTIONS */ 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds .globl fill_window_entry, fnwin_patch1, fnwin_patch2 811da177e4SLinus Torvaldsfill_window_entry: 821da177e4SLinus Torvalds /* LOCATION: Window 'T' */ 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds /* Compute what the new %wim is going to be if we retrieve 851da177e4SLinus Torvalds * the proper window off of the stack. 861da177e4SLinus Torvalds */ 871da177e4SLinus Torvalds sll %t_wim, 1, %twin_tmp1 881da177e4SLinus Torvaldsfnwin_patch1: srl %t_wim, 7, %twin_tmp2 891da177e4SLinus Torvalds or %twin_tmp1, %twin_tmp2, %twin_tmp1 901da177e4SLinus Torvaldsfnwin_patch2: and %twin_tmp1, 0xff, %twin_tmp1 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds wr %twin_tmp1, 0x0, %wim /* Make window 'I' invalid */ 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds andcc %t_psr, PSR_PS, %g0 951da177e4SLinus Torvalds be fwin_from_user 961da177e4SLinus Torvalds restore %g0, %g0, %g0 /* Restore to window 'O' */ 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds /* Trapped from kernel, we trust that the kernel does not 991da177e4SLinus Torvalds * 'over restore' sorta speak and just grab the window 1001da177e4SLinus Torvalds * from the stack and return. Easy enough. 1011da177e4SLinus Torvalds */ 1021da177e4SLinus Torvaldsfwin_from_kernel: 1031da177e4SLinus Torvalds /* LOCATION: Window 'O' */ 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds restore %g0, %g0, %g0 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds /* LOCATION: Window 'W' */ 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds LOAD_WINDOW(sp) /* Load it up */ 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds /* Spin the wheel... */ 1121da177e4SLinus Torvalds save %g0, %g0, %g0 1131da177e4SLinus Torvalds save %g0, %g0, %g0 1141da177e4SLinus Torvalds /* I'd like to buy a vowel please... */ 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds /* LOCATION: Window 'T' */ 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds /* Now preserve the condition codes in %psr, pause, and 1191da177e4SLinus Torvalds * return from trap. This is the simplest case of all. 1201da177e4SLinus Torvalds */ 1211da177e4SLinus Torvalds wr %t_psr, 0x0, %psr 1221da177e4SLinus Torvalds WRITE_PAUSE 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds jmp %t_pc 1251da177e4SLinus Torvalds rett %t_npc 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvaldsfwin_from_user: 1281da177e4SLinus Torvalds /* LOCATION: Window 'O' */ 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds restore %g0, %g0, %g0 /* Restore to window 'W' */ 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds /* LOCATION: Window 'W' */ 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds /* Branch to the architecture specific stack validation 1351da177e4SLinus Torvalds * routine. They can be found below... 1361da177e4SLinus Torvalds */ 1371da177e4SLinus Torvalds .globl fwin_mmu_patchme 1381da177e4SLinus Torvaldsfwin_mmu_patchme: b sun4c_fwin_stackchk 1391da177e4SLinus Torvalds andcc %sp, 0x7, %g0 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds#define STACK_OFFSET (THREAD_SIZE - TRACEREG_SZ - STACKFRAME_SZ) 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvaldsfwin_user_stack_is_bolixed: 1441da177e4SLinus Torvalds /* LOCATION: Window 'W' */ 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds /* Place a pt_regs frame on the kernel stack, save back 1471da177e4SLinus Torvalds * to the trap window and call c-code to deal with this. 1481da177e4SLinus Torvalds */ 1491da177e4SLinus Torvalds LOAD_CURRENT(l4, l5) 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds sethi %hi(STACK_OFFSET), %l5 1521da177e4SLinus Torvalds or %l5, %lo(STACK_OFFSET), %l5 1531da177e4SLinus Torvalds add %l4, %l5, %l5 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds /* Store globals into pt_regs frame. */ 1561da177e4SLinus Torvalds STORE_PT_GLOBALS(l5) 1571da177e4SLinus Torvalds STORE_PT_YREG(l5, g3) 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds /* Save current in a global while we change windows. */ 1601da177e4SLinus Torvalds mov %l4, %curptr 1611da177e4SLinus Torvalds 1621da177e4SLinus Torvalds save %g0, %g0, %g0 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds /* LOCATION: Window 'O' */ 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds rd %psr, %g3 /* Read %psr in live user window */ 1671da177e4SLinus Torvalds mov %fp, %g4 /* Save bogus frame pointer. */ 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds save %g0, %g0, %g0 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds /* LOCATION: Window 'T' */ 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds sethi %hi(STACK_OFFSET), %l5 1741da177e4SLinus Torvalds or %l5, %lo(STACK_OFFSET), %l5 1751da177e4SLinus Torvalds add %curptr, %l5, %sp 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds /* Build rest of pt_regs. */ 1781da177e4SLinus Torvalds STORE_PT_INS(sp) 1791da177e4SLinus Torvalds STORE_PT_PRIV(sp, t_psr, t_pc, t_npc) 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds /* re-set trap time %wim value */ 1821da177e4SLinus Torvalds wr %t_wim, 0x0, %wim 1831da177e4SLinus Torvalds 1841da177e4SLinus Torvalds /* Fix users window mask and buffer save count. */ 1851da177e4SLinus Torvalds mov 0x1, %g5 1861da177e4SLinus Torvalds sll %g5, %g3, %g5 1871da177e4SLinus Torvalds st %g5, [%curptr + TI_UWINMASK] ! one live user window still 1881da177e4SLinus Torvalds st %g0, [%curptr + TI_W_SAVED] ! no windows in the buffer 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds wr %t_psr, PSR_ET, %psr ! enable traps 1911da177e4SLinus Torvalds nop 1921da177e4SLinus Torvalds call window_underflow_fault 1931da177e4SLinus Torvalds mov %g4, %o0 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds b ret_trap_entry 1961da177e4SLinus Torvalds clr %l6 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvaldsfwin_user_stack_is_ok: 1991da177e4SLinus Torvalds /* LOCATION: Window 'W' */ 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds /* The users stack area is kosher and mapped, load the 2021da177e4SLinus Torvalds * window and fall through to the finish up routine. 2031da177e4SLinus Torvalds */ 2041da177e4SLinus Torvalds LOAD_WINDOW(sp) 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds /* Round and round she goes... */ 2071da177e4SLinus Torvalds save %g0, %g0, %g0 /* Save to window 'O' */ 2081da177e4SLinus Torvalds save %g0, %g0, %g0 /* Save to window 'T' */ 2091da177e4SLinus Torvalds /* Where she'll trap nobody knows... */ 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds /* LOCATION: Window 'T' */ 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvaldsfwin_user_finish_up: 2141da177e4SLinus Torvalds /* LOCATION: Window 'T' */ 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds wr %t_psr, 0x0, %psr 2171da177e4SLinus Torvalds WRITE_PAUSE 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds jmp %t_pc 2201da177e4SLinus Torvalds rett %t_npc 2211da177e4SLinus Torvalds 2221da177e4SLinus Torvalds /* Here come the architecture specific checks for stack. 2231da177e4SLinus Torvalds * mappings. Note that unlike the window overflow handler 2241da177e4SLinus Torvalds * we only need to check whether the user can read from 2251da177e4SLinus Torvalds * the appropriate addresses. Also note that we are in 2261da177e4SLinus Torvalds * an invalid window which will be loaded, and this means 2271da177e4SLinus Torvalds * that until we actually load the window up we are free 2281da177e4SLinus Torvalds * to use any of the local registers contained within. 2291da177e4SLinus Torvalds * 2301da177e4SLinus Torvalds * On success these routine branch to fwin_user_stack_is_ok 2311da177e4SLinus Torvalds * if the area at %sp is user readable and the window still 2321da177e4SLinus Torvalds * needs to be loaded, else fwin_user_finish_up if the 2331da177e4SLinus Torvalds * routine has done the loading itself. On failure (bogus 2341da177e4SLinus Torvalds * user stack) the routine shall branch to the label called 2351da177e4SLinus Torvalds * fwin_user_stack_is_bolixed. 2361da177e4SLinus Torvalds * 2371da177e4SLinus Torvalds * Contrary to the arch-specific window overflow stack 2381da177e4SLinus Torvalds * check routines in wof.S, these routines are free to use 2391da177e4SLinus Torvalds * any of the local registers they want to as this window 2401da177e4SLinus Torvalds * does not belong to anyone at this point, however the 2411da177e4SLinus Torvalds * outs and ins are still verboten as they are part of 2421da177e4SLinus Torvalds * 'someone elses' window possibly. 2431da177e4SLinus Torvalds */ 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds .align 4 2461da177e4SLinus Torvalds .globl sun4c_fwin_stackchk 2471da177e4SLinus Torvaldssun4c_fwin_stackchk: 2481da177e4SLinus Torvalds /* LOCATION: Window 'W' */ 2491da177e4SLinus Torvalds 2501da177e4SLinus Torvalds /* Caller did 'andcc %sp, 0x7, %g0' */ 2511da177e4SLinus Torvalds be 1f 2521da177e4SLinus Torvalds and %sp, 0xfff, %l0 ! delay slot 2531da177e4SLinus Torvalds 2541da177e4SLinus Torvalds b,a fwin_user_stack_is_bolixed 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds /* See if we have to check the sanity of one page or two */ 2571da177e4SLinus Torvalds1: 2581da177e4SLinus Torvalds add %l0, 0x38, %l0 2591da177e4SLinus Torvalds sra %sp, 29, %l5 2601da177e4SLinus Torvalds add %l5, 0x1, %l5 2611da177e4SLinus Torvalds andncc %l5, 0x1, %g0 2621da177e4SLinus Torvalds be 1f 2631da177e4SLinus Torvalds andncc %l0, 0xff8, %g0 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds b,a fwin_user_stack_is_bolixed /* %sp is in vma hole, yuck */ 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds1: 2681da177e4SLinus Torvalds be sun4c_fwin_onepage /* Only one page to check */ 2691da177e4SLinus Torvalds lda [%sp] ASI_PTE, %l1 2701da177e4SLinus Torvaldssun4c_fwin_twopages: 2711da177e4SLinus Torvalds add %sp, 0x38, %l0 2721da177e4SLinus Torvalds sra %l0, 29, %l5 2731da177e4SLinus Torvalds add %l5, 0x1, %l5 2741da177e4SLinus Torvalds andncc %l5, 0x1, %g0 2751da177e4SLinus Torvalds be 1f 2761da177e4SLinus Torvalds lda [%l0] ASI_PTE, %l1 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds b,a fwin_user_stack_is_bolixed /* Second page in vma hole */ 2791da177e4SLinus Torvalds 2801da177e4SLinus Torvalds1: 2811da177e4SLinus Torvalds srl %l1, 29, %l1 2821da177e4SLinus Torvalds andcc %l1, 0x4, %g0 2831da177e4SLinus Torvalds bne sun4c_fwin_onepage 2841da177e4SLinus Torvalds lda [%sp] ASI_PTE, %l1 2851da177e4SLinus Torvalds 2861da177e4SLinus Torvalds b,a fwin_user_stack_is_bolixed /* Second page has bad perms */ 2871da177e4SLinus Torvalds 2881da177e4SLinus Torvaldssun4c_fwin_onepage: 2891da177e4SLinus Torvalds srl %l1, 29, %l1 2901da177e4SLinus Torvalds andcc %l1, 0x4, %g0 2911da177e4SLinus Torvalds bne fwin_user_stack_is_ok 2921da177e4SLinus Torvalds nop 2931da177e4SLinus Torvalds 2941da177e4SLinus Torvalds /* A page had bad page permissions, losing... */ 2951da177e4SLinus Torvalds b,a fwin_user_stack_is_bolixed 2961da177e4SLinus Torvalds 2971da177e4SLinus Torvalds .globl srmmu_fwin_stackchk 2981da177e4SLinus Torvaldssrmmu_fwin_stackchk: 2991da177e4SLinus Torvalds /* LOCATION: Window 'W' */ 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds /* Caller did 'andcc %sp, 0x7, %g0' */ 3021da177e4SLinus Torvalds bne fwin_user_stack_is_bolixed 3031da177e4SLinus Torvalds sethi %hi(PAGE_OFFSET), %l5 3041da177e4SLinus Torvalds 3051da177e4SLinus Torvalds /* Check if the users stack is in kernel vma, then our 3061da177e4SLinus Torvalds * trial and error technique below would succeed for 3071da177e4SLinus Torvalds * the 'wrong' reason. 3081da177e4SLinus Torvalds */ 3091da177e4SLinus Torvalds mov AC_M_SFSR, %l4 3101da177e4SLinus Torvalds cmp %l5, %sp 3111da177e4SLinus Torvalds bleu fwin_user_stack_is_bolixed 3121da177e4SLinus Torvalds lda [%l4] ASI_M_MMUREGS, %g0 ! clear fault status 3131da177e4SLinus Torvalds 3141da177e4SLinus Torvalds /* The technique is, turn off faults on this processor, 3151da177e4SLinus Torvalds * just let the load rip, then check the sfsr to see if 3161da177e4SLinus Torvalds * a fault did occur. Then we turn on fault traps again 3171da177e4SLinus Torvalds * and branch conditionally based upon what happened. 3181da177e4SLinus Torvalds */ 3191da177e4SLinus Torvalds lda [%g0] ASI_M_MMUREGS, %l5 ! read mmu-ctrl reg 3201da177e4SLinus Torvalds or %l5, 0x2, %l5 ! turn on no-fault bit 3211da177e4SLinus Torvalds sta %l5, [%g0] ASI_M_MMUREGS ! store it 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds /* Cross fingers and go for it. */ 3241da177e4SLinus Torvalds LOAD_WINDOW(sp) 3251da177e4SLinus Torvalds 3261da177e4SLinus Torvalds /* A penny 'saved'... */ 3271da177e4SLinus Torvalds save %g0, %g0, %g0 3281da177e4SLinus Torvalds save %g0, %g0, %g0 3291da177e4SLinus Torvalds /* Is a BADTRAP earned... */ 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds /* LOCATION: Window 'T' */ 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds lda [%g0] ASI_M_MMUREGS, %twin_tmp1 ! load mmu-ctrl again 3341da177e4SLinus Torvalds andn %twin_tmp1, 0x2, %twin_tmp1 ! clear no-fault bit 3351da177e4SLinus Torvalds sta %twin_tmp1, [%g0] ASI_M_MMUREGS ! store it 3361da177e4SLinus Torvalds 3371da177e4SLinus Torvalds mov AC_M_SFAR, %twin_tmp2 3381da177e4SLinus Torvalds lda [%twin_tmp2] ASI_M_MMUREGS, %g0 ! read fault address 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds mov AC_M_SFSR, %twin_tmp2 3411da177e4SLinus Torvalds lda [%twin_tmp2] ASI_M_MMUREGS, %twin_tmp2 ! read fault status 3421da177e4SLinus Torvalds andcc %twin_tmp2, 0x2, %g0 ! did fault occur? 3431da177e4SLinus Torvalds 3441da177e4SLinus Torvalds bne 1f ! yep, cleanup 3451da177e4SLinus Torvalds nop 3461da177e4SLinus Torvalds 3471da177e4SLinus Torvalds wr %t_psr, 0x0, %psr 3481da177e4SLinus Torvalds nop 3491da177e4SLinus Torvalds b fwin_user_finish_up + 0x4 3501da177e4SLinus Torvalds nop 3511da177e4SLinus Torvalds 3521da177e4SLinus Torvalds /* Did I ever tell you about my window lobotomy? 3531da177e4SLinus Torvalds * anyways... fwin_user_stack_is_bolixed expects 3541da177e4SLinus Torvalds * to be in window 'W' so make it happy or else 3551da177e4SLinus Torvalds * we watchdog badly. 3561da177e4SLinus Torvalds */ 3571da177e4SLinus Torvalds1: 3581da177e4SLinus Torvalds restore %g0, %g0, %g0 3591da177e4SLinus Torvalds b fwin_user_stack_is_bolixed ! oh well 3601da177e4SLinus Torvalds restore %g0, %g0, %g0 361