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