18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * wuf.S: Window underflow trap handler for the Sparc. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1995 David S. Miller 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <asm/contregs.h> 98c2ecf20Sopenharmony_ci#include <asm/page.h> 108c2ecf20Sopenharmony_ci#include <asm/ptrace.h> 118c2ecf20Sopenharmony_ci#include <asm/psr.h> 128c2ecf20Sopenharmony_ci#include <asm/smp.h> 138c2ecf20Sopenharmony_ci#include <asm/asi.h> 148c2ecf20Sopenharmony_ci#include <asm/winmacro.h> 158c2ecf20Sopenharmony_ci#include <asm/asmmacro.h> 168c2ecf20Sopenharmony_ci#include <asm/thread_info.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* Just like the overflow handler we define macros for registers 198c2ecf20Sopenharmony_ci * with fixed meanings in this routine. 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci#define t_psr l0 228c2ecf20Sopenharmony_ci#define t_pc l1 238c2ecf20Sopenharmony_ci#define t_npc l2 248c2ecf20Sopenharmony_ci#define t_wim l3 258c2ecf20Sopenharmony_ci/* Don't touch the above registers or else you die horribly... */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Now macros for the available scratch registers in this routine. */ 288c2ecf20Sopenharmony_ci#define twin_tmp1 l4 298c2ecf20Sopenharmony_ci#define twin_tmp2 l5 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define curptr g6 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci .text 348c2ecf20Sopenharmony_ci .align 4 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci /* The trap entry point has executed the following: 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * rd %psr, %l0 398c2ecf20Sopenharmony_ci * rd %wim, %l3 408c2ecf20Sopenharmony_ci * b fill_window_entry 418c2ecf20Sopenharmony_ci * andcc %l0, PSR_PS, %g0 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci /* Datum current_thread_info->uwinmask contains at all times a bitmask 458c2ecf20Sopenharmony_ci * where if any user windows are active, at least one bit will 468c2ecf20Sopenharmony_ci * be set in to mask. If no user windows are active, the bitmask 478c2ecf20Sopenharmony_ci * will be all zeroes. 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* To get an idea of what has just happened to cause this 518c2ecf20Sopenharmony_ci * trap take a look at this diagram: 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * 1 2 3 4 <-- Window number 548c2ecf20Sopenharmony_ci * ---------- 558c2ecf20Sopenharmony_ci * T O W I <-- Symbolic name 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * O == the window that execution was in when 588c2ecf20Sopenharmony_ci * the restore was attempted 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * T == the trap itself has save'd us into this 618c2ecf20Sopenharmony_ci * window 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * W == this window is the one which is now invalid 648c2ecf20Sopenharmony_ci * and must be made valid plus loaded from the 658c2ecf20Sopenharmony_ci * stack 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci * I == this window will be the invalid one when we 688c2ecf20Sopenharmony_ci * are done and return from trap if successful 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* BEGINNING OF PATCH INSTRUCTIONS */ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* On 7-window Sparc the boot code patches fnwin_patch1 748c2ecf20Sopenharmony_ci * with the following instruction. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ci .globl fnwin_patch1_7win, fnwin_patch2_7win 778c2ecf20Sopenharmony_cifnwin_patch1_7win: srl %t_wim, 6, %twin_tmp2 788c2ecf20Sopenharmony_cifnwin_patch2_7win: and %twin_tmp1, 0x7f, %twin_tmp1 798c2ecf20Sopenharmony_ci /* END OF PATCH INSTRUCTIONS */ 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci .globl fill_window_entry, fnwin_patch1, fnwin_patch2 828c2ecf20Sopenharmony_cifill_window_entry: 838c2ecf20Sopenharmony_ci /* LOCATION: Window 'T' */ 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* Compute what the new %wim is going to be if we retrieve 868c2ecf20Sopenharmony_ci * the proper window off of the stack. 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_ci sll %t_wim, 1, %twin_tmp1 898c2ecf20Sopenharmony_cifnwin_patch1: srl %t_wim, 7, %twin_tmp2 908c2ecf20Sopenharmony_ci or %twin_tmp1, %twin_tmp2, %twin_tmp1 918c2ecf20Sopenharmony_cifnwin_patch2: and %twin_tmp1, 0xff, %twin_tmp1 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci wr %twin_tmp1, 0x0, %wim /* Make window 'I' invalid */ 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci andcc %t_psr, PSR_PS, %g0 968c2ecf20Sopenharmony_ci be fwin_from_user 978c2ecf20Sopenharmony_ci restore %g0, %g0, %g0 /* Restore to window 'O' */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Trapped from kernel, we trust that the kernel does not 1008c2ecf20Sopenharmony_ci * 'over restore' sorta speak and just grab the window 1018c2ecf20Sopenharmony_ci * from the stack and return. Easy enough. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_cifwin_from_kernel: 1048c2ecf20Sopenharmony_ci /* LOCATION: Window 'O' */ 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci restore %g0, %g0, %g0 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* LOCATION: Window 'W' */ 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci LOAD_WINDOW(sp) /* Load it up */ 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Spin the wheel... */ 1138c2ecf20Sopenharmony_ci save %g0, %g0, %g0 1148c2ecf20Sopenharmony_ci save %g0, %g0, %g0 1158c2ecf20Sopenharmony_ci /* I'd like to buy a vowel please... */ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* LOCATION: Window 'T' */ 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* Now preserve the condition codes in %psr, pause, and 1208c2ecf20Sopenharmony_ci * return from trap. This is the simplest case of all. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci wr %t_psr, 0x0, %psr 1238c2ecf20Sopenharmony_ci WRITE_PAUSE 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci jmp %t_pc 1268c2ecf20Sopenharmony_ci rett %t_npc 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cifwin_from_user: 1298c2ecf20Sopenharmony_ci /* LOCATION: Window 'O' */ 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci restore %g0, %g0, %g0 /* Restore to window 'W' */ 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* LOCATION: Window 'W' */ 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Branch to the stack validation routine */ 1368c2ecf20Sopenharmony_ci b srmmu_fwin_stackchk 1378c2ecf20Sopenharmony_ci andcc %sp, 0x7, %g0 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#define STACK_OFFSET (THREAD_SIZE - TRACEREG_SZ - STACKFRAME_SZ) 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cifwin_user_stack_is_bolixed: 1428c2ecf20Sopenharmony_ci /* LOCATION: Window 'W' */ 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* Place a pt_regs frame on the kernel stack, save back 1458c2ecf20Sopenharmony_ci * to the trap window and call c-code to deal with this. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci LOAD_CURRENT(l4, l5) 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci sethi %hi(STACK_OFFSET), %l5 1508c2ecf20Sopenharmony_ci or %l5, %lo(STACK_OFFSET), %l5 1518c2ecf20Sopenharmony_ci add %l4, %l5, %l5 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Store globals into pt_regs frame. */ 1548c2ecf20Sopenharmony_ci STORE_PT_GLOBALS(l5) 1558c2ecf20Sopenharmony_ci STORE_PT_YREG(l5, g3) 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* Save current in a global while we change windows. */ 1588c2ecf20Sopenharmony_ci mov %l4, %curptr 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci save %g0, %g0, %g0 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* LOCATION: Window 'O' */ 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci rd %psr, %g3 /* Read %psr in live user window */ 1658c2ecf20Sopenharmony_ci mov %fp, %g4 /* Save bogus frame pointer. */ 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci save %g0, %g0, %g0 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* LOCATION: Window 'T' */ 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci sethi %hi(STACK_OFFSET), %l5 1728c2ecf20Sopenharmony_ci or %l5, %lo(STACK_OFFSET), %l5 1738c2ecf20Sopenharmony_ci add %curptr, %l5, %sp 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Build rest of pt_regs. */ 1768c2ecf20Sopenharmony_ci STORE_PT_INS(sp) 1778c2ecf20Sopenharmony_ci STORE_PT_PRIV(sp, t_psr, t_pc, t_npc) 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* re-set trap time %wim value */ 1808c2ecf20Sopenharmony_ci wr %t_wim, 0x0, %wim 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* Fix users window mask and buffer save count. */ 1838c2ecf20Sopenharmony_ci mov 0x1, %g5 1848c2ecf20Sopenharmony_ci sll %g5, %g3, %g5 1858c2ecf20Sopenharmony_ci st %g5, [%curptr + TI_UWINMASK] ! one live user window still 1868c2ecf20Sopenharmony_ci st %g0, [%curptr + TI_W_SAVED] ! no windows in the buffer 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci wr %t_psr, PSR_ET, %psr ! enable traps 1898c2ecf20Sopenharmony_ci nop 1908c2ecf20Sopenharmony_ci call window_underflow_fault 1918c2ecf20Sopenharmony_ci mov %g4, %o0 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci b ret_trap_entry 1948c2ecf20Sopenharmony_ci clr %l6 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cifwin_user_stack_is_ok: 1978c2ecf20Sopenharmony_ci /* LOCATION: Window 'W' */ 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* The users stack area is kosher and mapped, load the 2008c2ecf20Sopenharmony_ci * window and fall through to the finish up routine. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci LOAD_WINDOW(sp) 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* Round and round she goes... */ 2058c2ecf20Sopenharmony_ci save %g0, %g0, %g0 /* Save to window 'O' */ 2068c2ecf20Sopenharmony_ci save %g0, %g0, %g0 /* Save to window 'T' */ 2078c2ecf20Sopenharmony_ci /* Where she'll trap nobody knows... */ 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* LOCATION: Window 'T' */ 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cifwin_user_finish_up: 2128c2ecf20Sopenharmony_ci /* LOCATION: Window 'T' */ 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci wr %t_psr, 0x0, %psr 2158c2ecf20Sopenharmony_ci WRITE_PAUSE 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci jmp %t_pc 2188c2ecf20Sopenharmony_ci rett %t_npc 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* Here come the architecture specific checks for stack. 2218c2ecf20Sopenharmony_ci * mappings. Note that unlike the window overflow handler 2228c2ecf20Sopenharmony_ci * we only need to check whether the user can read from 2238c2ecf20Sopenharmony_ci * the appropriate addresses. Also note that we are in 2248c2ecf20Sopenharmony_ci * an invalid window which will be loaded, and this means 2258c2ecf20Sopenharmony_ci * that until we actually load the window up we are free 2268c2ecf20Sopenharmony_ci * to use any of the local registers contained within. 2278c2ecf20Sopenharmony_ci * 2288c2ecf20Sopenharmony_ci * On success these routine branch to fwin_user_stack_is_ok 2298c2ecf20Sopenharmony_ci * if the area at %sp is user readable and the window still 2308c2ecf20Sopenharmony_ci * needs to be loaded, else fwin_user_finish_up if the 2318c2ecf20Sopenharmony_ci * routine has done the loading itself. On failure (bogus 2328c2ecf20Sopenharmony_ci * user stack) the routine shall branch to the label called 2338c2ecf20Sopenharmony_ci * fwin_user_stack_is_bolixed. 2348c2ecf20Sopenharmony_ci * 2358c2ecf20Sopenharmony_ci * Contrary to the arch-specific window overflow stack 2368c2ecf20Sopenharmony_ci * check routines in wof.S, these routines are free to use 2378c2ecf20Sopenharmony_ci * any of the local registers they want to as this window 2388c2ecf20Sopenharmony_ci * does not belong to anyone at this point, however the 2398c2ecf20Sopenharmony_ci * outs and ins are still verboten as they are part of 2408c2ecf20Sopenharmony_ci * 'someone elses' window possibly. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci .globl srmmu_fwin_stackchk 2448c2ecf20Sopenharmony_cisrmmu_fwin_stackchk: 2458c2ecf20Sopenharmony_ci /* LOCATION: Window 'W' */ 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Caller did 'andcc %sp, 0x7, %g0' */ 2488c2ecf20Sopenharmony_ci bne fwin_user_stack_is_bolixed 2498c2ecf20Sopenharmony_ci sethi %hi(PAGE_OFFSET), %l5 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* Check if the users stack is in kernel vma, then our 2528c2ecf20Sopenharmony_ci * trial and error technique below would succeed for 2538c2ecf20Sopenharmony_ci * the 'wrong' reason. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci mov AC_M_SFSR, %l4 2568c2ecf20Sopenharmony_ci cmp %l5, %sp 2578c2ecf20Sopenharmony_ci bleu fwin_user_stack_is_bolixed 2588c2ecf20Sopenharmony_ciLEON_PI( lda [%l4] ASI_LEON_MMUREGS, %g0) ! clear fault status 2598c2ecf20Sopenharmony_ciSUN_PI_( lda [%l4] ASI_M_MMUREGS, %g0) ! clear fault status 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* The technique is, turn off faults on this processor, 2628c2ecf20Sopenharmony_ci * just let the load rip, then check the sfsr to see if 2638c2ecf20Sopenharmony_ci * a fault did occur. Then we turn on fault traps again 2648c2ecf20Sopenharmony_ci * and branch conditionally based upon what happened. 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ciLEON_PI(lda [%g0] ASI_LEON_MMUREGS, %l5) ! read mmu-ctrl reg 2678c2ecf20Sopenharmony_ciSUN_PI_(lda [%g0] ASI_M_MMUREGS, %l5) ! read mmu-ctrl reg 2688c2ecf20Sopenharmony_ci or %l5, 0x2, %l5 ! turn on no-fault bit 2698c2ecf20Sopenharmony_ciLEON_PI(sta %l5, [%g0] ASI_LEON_MMUREGS) ! store it 2708c2ecf20Sopenharmony_ciSUN_PI_(sta %l5, [%g0] ASI_M_MMUREGS) ! store it 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* Cross fingers and go for it. */ 2738c2ecf20Sopenharmony_ci LOAD_WINDOW(sp) 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* A penny 'saved'... */ 2768c2ecf20Sopenharmony_ci save %g0, %g0, %g0 2778c2ecf20Sopenharmony_ci save %g0, %g0, %g0 2788c2ecf20Sopenharmony_ci /* Is a BADTRAP earned... */ 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* LOCATION: Window 'T' */ 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ciLEON_PI(lda [%g0] ASI_LEON_MMUREGS, %twin_tmp1) ! load mmu-ctrl again 2838c2ecf20Sopenharmony_ciSUN_PI_(lda [%g0] ASI_M_MMUREGS, %twin_tmp1) ! load mmu-ctrl again 2848c2ecf20Sopenharmony_ci andn %twin_tmp1, 0x2, %twin_tmp1 ! clear no-fault bit 2858c2ecf20Sopenharmony_ciLEON_PI(sta %twin_tmp1, [%g0] ASI_LEON_MMUREGS) ! store it 2868c2ecf20Sopenharmony_ciSUN_PI_(sta %twin_tmp1, [%g0] ASI_M_MMUREGS) ! store it 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci mov AC_M_SFAR, %twin_tmp2 2898c2ecf20Sopenharmony_ciLEON_PI(lda [%twin_tmp2] ASI_LEON_MMUREGS, %g0) ! read fault address 2908c2ecf20Sopenharmony_ciSUN_PI_(lda [%twin_tmp2] ASI_M_MMUREGS, %g0) ! read fault address 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci mov AC_M_SFSR, %twin_tmp2 2938c2ecf20Sopenharmony_ciLEON_PI(lda [%twin_tmp2] ASI_LEON_MMUREGS, %twin_tmp2) ! read fault status 2948c2ecf20Sopenharmony_ciSUN_PI_(lda [%twin_tmp2] ASI_M_MMUREGS, %twin_tmp2) ! read fault status 2958c2ecf20Sopenharmony_ci andcc %twin_tmp2, 0x2, %g0 ! did fault occur? 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci bne 1f ! yep, cleanup 2988c2ecf20Sopenharmony_ci nop 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci wr %t_psr, 0x0, %psr 3018c2ecf20Sopenharmony_ci nop 3028c2ecf20Sopenharmony_ci b fwin_user_finish_up + 0x4 3038c2ecf20Sopenharmony_ci nop 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* Did I ever tell you about my window lobotomy? 3068c2ecf20Sopenharmony_ci * anyways... fwin_user_stack_is_bolixed expects 3078c2ecf20Sopenharmony_ci * to be in window 'W' so make it happy or else 3088c2ecf20Sopenharmony_ci * we watchdog badly. 3098c2ecf20Sopenharmony_ci */ 3108c2ecf20Sopenharmony_ci1: 3118c2ecf20Sopenharmony_ci restore %g0, %g0, %g0 3128c2ecf20Sopenharmony_ci b fwin_user_stack_is_bolixed ! oh well 3138c2ecf20Sopenharmony_ci restore %g0, %g0, %g0 314