188278ca2SAdrian Bunk/* 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 134*28de2f73SSam Ravnborg /* Branch to the stack validation routine */ 135*28de2f73SSam Ravnborg b srmmu_fwin_stackchk 1361da177e4SLinus Torvalds andcc %sp, 0x7, %g0 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds#define STACK_OFFSET (THREAD_SIZE - TRACEREG_SZ - STACKFRAME_SZ) 1391da177e4SLinus Torvalds 1401da177e4SLinus Torvaldsfwin_user_stack_is_bolixed: 1411da177e4SLinus Torvalds /* LOCATION: Window 'W' */ 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds /* Place a pt_regs frame on the kernel stack, save back 1441da177e4SLinus Torvalds * to the trap window and call c-code to deal with this. 1451da177e4SLinus Torvalds */ 1461da177e4SLinus Torvalds LOAD_CURRENT(l4, l5) 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds sethi %hi(STACK_OFFSET), %l5 1491da177e4SLinus Torvalds or %l5, %lo(STACK_OFFSET), %l5 1501da177e4SLinus Torvalds add %l4, %l5, %l5 1511da177e4SLinus Torvalds 1521da177e4SLinus Torvalds /* Store globals into pt_regs frame. */ 1531da177e4SLinus Torvalds STORE_PT_GLOBALS(l5) 1541da177e4SLinus Torvalds STORE_PT_YREG(l5, g3) 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds /* Save current in a global while we change windows. */ 1571da177e4SLinus Torvalds mov %l4, %curptr 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds save %g0, %g0, %g0 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds /* LOCATION: Window 'O' */ 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds rd %psr, %g3 /* Read %psr in live user window */ 1641da177e4SLinus Torvalds mov %fp, %g4 /* Save bogus frame pointer. */ 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds save %g0, %g0, %g0 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds /* LOCATION: Window 'T' */ 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds sethi %hi(STACK_OFFSET), %l5 1711da177e4SLinus Torvalds or %l5, %lo(STACK_OFFSET), %l5 1721da177e4SLinus Torvalds add %curptr, %l5, %sp 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds /* Build rest of pt_regs. */ 1751da177e4SLinus Torvalds STORE_PT_INS(sp) 1761da177e4SLinus Torvalds STORE_PT_PRIV(sp, t_psr, t_pc, t_npc) 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds /* re-set trap time %wim value */ 1791da177e4SLinus Torvalds wr %t_wim, 0x0, %wim 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds /* Fix users window mask and buffer save count. */ 1821da177e4SLinus Torvalds mov 0x1, %g5 1831da177e4SLinus Torvalds sll %g5, %g3, %g5 1841da177e4SLinus Torvalds st %g5, [%curptr + TI_UWINMASK] ! one live user window still 1851da177e4SLinus Torvalds st %g0, [%curptr + TI_W_SAVED] ! no windows in the buffer 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds wr %t_psr, PSR_ET, %psr ! enable traps 1881da177e4SLinus Torvalds nop 1891da177e4SLinus Torvalds call window_underflow_fault 1901da177e4SLinus Torvalds mov %g4, %o0 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds b ret_trap_entry 1931da177e4SLinus Torvalds clr %l6 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvaldsfwin_user_stack_is_ok: 1961da177e4SLinus Torvalds /* LOCATION: Window 'W' */ 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds /* The users stack area is kosher and mapped, load the 1991da177e4SLinus Torvalds * window and fall through to the finish up routine. 2001da177e4SLinus Torvalds */ 2011da177e4SLinus Torvalds LOAD_WINDOW(sp) 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds /* Round and round she goes... */ 2041da177e4SLinus Torvalds save %g0, %g0, %g0 /* Save to window 'O' */ 2051da177e4SLinus Torvalds save %g0, %g0, %g0 /* Save to window 'T' */ 2061da177e4SLinus Torvalds /* Where she'll trap nobody knows... */ 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds /* LOCATION: Window 'T' */ 2091da177e4SLinus Torvalds 2101da177e4SLinus Torvaldsfwin_user_finish_up: 2111da177e4SLinus Torvalds /* LOCATION: Window 'T' */ 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds wr %t_psr, 0x0, %psr 2141da177e4SLinus Torvalds WRITE_PAUSE 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds jmp %t_pc 2171da177e4SLinus Torvalds rett %t_npc 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds /* Here come the architecture specific checks for stack. 2201da177e4SLinus Torvalds * mappings. Note that unlike the window overflow handler 2211da177e4SLinus Torvalds * we only need to check whether the user can read from 2221da177e4SLinus Torvalds * the appropriate addresses. Also note that we are in 2231da177e4SLinus Torvalds * an invalid window which will be loaded, and this means 2241da177e4SLinus Torvalds * that until we actually load the window up we are free 2251da177e4SLinus Torvalds * to use any of the local registers contained within. 2261da177e4SLinus Torvalds * 2271da177e4SLinus Torvalds * On success these routine branch to fwin_user_stack_is_ok 2281da177e4SLinus Torvalds * if the area at %sp is user readable and the window still 2291da177e4SLinus Torvalds * needs to be loaded, else fwin_user_finish_up if the 2301da177e4SLinus Torvalds * routine has done the loading itself. On failure (bogus 2311da177e4SLinus Torvalds * user stack) the routine shall branch to the label called 2321da177e4SLinus Torvalds * fwin_user_stack_is_bolixed. 2331da177e4SLinus Torvalds * 2341da177e4SLinus Torvalds * Contrary to the arch-specific window overflow stack 2351da177e4SLinus Torvalds * check routines in wof.S, these routines are free to use 2361da177e4SLinus Torvalds * any of the local registers they want to as this window 2371da177e4SLinus Torvalds * does not belong to anyone at this point, however the 2381da177e4SLinus Torvalds * outs and ins are still verboten as they are part of 2391da177e4SLinus Torvalds * 'someone elses' window possibly. 2401da177e4SLinus Torvalds */ 2411da177e4SLinus Torvalds 2421da177e4SLinus Torvalds .globl srmmu_fwin_stackchk 2431da177e4SLinus Torvaldssrmmu_fwin_stackchk: 2441da177e4SLinus Torvalds /* LOCATION: Window 'W' */ 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds /* Caller did 'andcc %sp, 0x7, %g0' */ 2471da177e4SLinus Torvalds bne fwin_user_stack_is_bolixed 2481da177e4SLinus Torvalds sethi %hi(PAGE_OFFSET), %l5 2491da177e4SLinus Torvalds 2501da177e4SLinus Torvalds /* Check if the users stack is in kernel vma, then our 2511da177e4SLinus Torvalds * trial and error technique below would succeed for 2521da177e4SLinus Torvalds * the 'wrong' reason. 2531da177e4SLinus Torvalds */ 2541da177e4SLinus Torvalds mov AC_M_SFSR, %l4 2551da177e4SLinus Torvalds cmp %l5, %sp 2561da177e4SLinus Torvalds bleu fwin_user_stack_is_bolixed 2571da177e4SLinus Torvalds lda [%l4] ASI_M_MMUREGS, %g0 ! clear fault status 2581da177e4SLinus Torvalds 2591da177e4SLinus Torvalds /* The technique is, turn off faults on this processor, 2601da177e4SLinus Torvalds * just let the load rip, then check the sfsr to see if 2611da177e4SLinus Torvalds * a fault did occur. Then we turn on fault traps again 2621da177e4SLinus Torvalds * and branch conditionally based upon what happened. 2631da177e4SLinus Torvalds */ 2641da177e4SLinus Torvalds lda [%g0] ASI_M_MMUREGS, %l5 ! read mmu-ctrl reg 2651da177e4SLinus Torvalds or %l5, 0x2, %l5 ! turn on no-fault bit 2661da177e4SLinus Torvalds sta %l5, [%g0] ASI_M_MMUREGS ! store it 2671da177e4SLinus Torvalds 2681da177e4SLinus Torvalds /* Cross fingers and go for it. */ 2691da177e4SLinus Torvalds LOAD_WINDOW(sp) 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds /* A penny 'saved'... */ 2721da177e4SLinus Torvalds save %g0, %g0, %g0 2731da177e4SLinus Torvalds save %g0, %g0, %g0 2741da177e4SLinus Torvalds /* Is a BADTRAP earned... */ 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds /* LOCATION: Window 'T' */ 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds lda [%g0] ASI_M_MMUREGS, %twin_tmp1 ! load mmu-ctrl again 2791da177e4SLinus Torvalds andn %twin_tmp1, 0x2, %twin_tmp1 ! clear no-fault bit 2801da177e4SLinus Torvalds sta %twin_tmp1, [%g0] ASI_M_MMUREGS ! store it 2811da177e4SLinus Torvalds 2821da177e4SLinus Torvalds mov AC_M_SFAR, %twin_tmp2 2831da177e4SLinus Torvalds lda [%twin_tmp2] ASI_M_MMUREGS, %g0 ! read fault address 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds mov AC_M_SFSR, %twin_tmp2 2861da177e4SLinus Torvalds lda [%twin_tmp2] ASI_M_MMUREGS, %twin_tmp2 ! read fault status 2871da177e4SLinus Torvalds andcc %twin_tmp2, 0x2, %g0 ! did fault occur? 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds bne 1f ! yep, cleanup 2901da177e4SLinus Torvalds nop 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds wr %t_psr, 0x0, %psr 2931da177e4SLinus Torvalds nop 2941da177e4SLinus Torvalds b fwin_user_finish_up + 0x4 2951da177e4SLinus Torvalds nop 2961da177e4SLinus Torvalds 2971da177e4SLinus Torvalds /* Did I ever tell you about my window lobotomy? 2981da177e4SLinus Torvalds * anyways... fwin_user_stack_is_bolixed expects 2991da177e4SLinus Torvalds * to be in window 'W' so make it happy or else 3001da177e4SLinus Torvalds * we watchdog badly. 3011da177e4SLinus Torvalds */ 3021da177e4SLinus Torvalds1: 3031da177e4SLinus Torvalds restore %g0, %g0, %g0 3041da177e4SLinus Torvalds b fwin_user_stack_is_bolixed ! oh well 3051da177e4SLinus Torvalds restore %g0, %g0, %g0 306