Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* windows.c: Routines to deal with register window management
0003  *            at the C-code level.
0004  *
0005  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
0006  */
0007 
0008 #include <linux/kernel.h>
0009 #include <linux/sched.h>
0010 #include <linux/string.h>
0011 #include <linux/mm.h>
0012 #include <linux/smp.h>
0013 
0014 #include <asm/cacheflush.h>
0015 #include <linux/uaccess.h>
0016 
0017 #include "kernel.h"
0018 
0019 /* Do save's until all user register windows are out of the cpu. */
0020 void flush_user_windows(void)
0021 {
0022     register int ctr asm("g5");
0023 
0024     ctr = 0;
0025     __asm__ __volatile__(
0026         "\n1:\n\t"
0027         "ld [%%g6 + %2], %%g4\n\t"
0028         "orcc   %%g0, %%g4, %%g0\n\t"
0029         "add    %0, 1, %0\n\t"
0030         "bne    1b\n\t"
0031         " save  %%sp, -64, %%sp\n"
0032         "2:\n\t"
0033         "subcc  %0, 1, %0\n\t"
0034         "bne    2b\n\t"
0035         " restore %%g0, %%g0, %%g0\n"
0036     : "=&r" (ctr)
0037     : "0" (ctr),
0038       "i" ((const unsigned long)TI_UWINMASK)
0039     : "g4", "cc");
0040 }
0041 
0042 static inline void shift_window_buffer(int first_win, int last_win, struct thread_info *tp)
0043 {
0044     int i;
0045 
0046     for(i = first_win; i < last_win; i++) {
0047         tp->rwbuf_stkptrs[i] = tp->rwbuf_stkptrs[i+1];
0048         memcpy(&tp->reg_window[i], &tp->reg_window[i+1], sizeof(struct reg_window32));
0049     }
0050 }
0051 
0052 /* Place as many of the user's current register windows 
0053  * on the stack that we can.  Even if the %sp is unaligned
0054  * we still copy the window there, the only case that we don't
0055  * succeed is if the %sp points to a bum mapping altogether.
0056  * setup_frame() and do_sigreturn() use this before shifting
0057  * the user stack around.  Future instruction and hardware
0058  * bug workaround routines will need this functionality as
0059  * well.
0060  */
0061 void synchronize_user_stack(void)
0062 {
0063     struct thread_info *tp = current_thread_info();
0064     int window;
0065 
0066     flush_user_windows();
0067     if(!tp->w_saved)
0068         return;
0069 
0070     /* Ok, there is some dirty work to do. */
0071     for(window = tp->w_saved - 1; window >= 0; window--) {
0072         unsigned long sp = tp->rwbuf_stkptrs[window];
0073 
0074         /* Ok, let it rip. */
0075         if (copy_to_user((char __user *) sp, &tp->reg_window[window],
0076                  sizeof(struct reg_window32)))
0077             continue;
0078 
0079         shift_window_buffer(window, tp->w_saved - 1, tp);
0080         tp->w_saved--;
0081     }
0082 }
0083 
0084 #if 0
0085 /* An optimization. */
0086 static inline void copy_aligned_window(void *dest, const void *src)
0087 {
0088     __asm__ __volatile__("ldd [%1], %%g2\n\t"
0089                  "ldd [%1 + 0x8], %%g4\n\t"
0090                  "std %%g2, [%0]\n\t"
0091                  "std %%g4, [%0 + 0x8]\n\t"
0092                  "ldd [%1 + 0x10], %%g2\n\t"
0093                  "ldd [%1 + 0x18], %%g4\n\t"
0094                  "std %%g2, [%0 + 0x10]\n\t"
0095                  "std %%g4, [%0 + 0x18]\n\t"
0096                  "ldd [%1 + 0x20], %%g2\n\t"
0097                  "ldd [%1 + 0x28], %%g4\n\t"
0098                  "std %%g2, [%0 + 0x20]\n\t"
0099                  "std %%g4, [%0 + 0x28]\n\t"
0100                  "ldd [%1 + 0x30], %%g2\n\t"
0101                  "ldd [%1 + 0x38], %%g4\n\t"
0102                  "std %%g2, [%0 + 0x30]\n\t"
0103                  "std %%g4, [%0 + 0x38]\n\t" : :
0104                  "r" (dest), "r" (src) :
0105                  "g2", "g3", "g4", "g5");
0106 }
0107 #endif
0108 
0109 /* Try to push the windows in a threads window buffer to the
0110  * user stack.  Unaligned %sp's are not allowed here.
0111  */
0112 
0113 void try_to_clear_window_buffer(struct pt_regs *regs, int who)
0114 {
0115     struct thread_info *tp = current_thread_info();
0116     int window;
0117 
0118     flush_user_windows();
0119     for(window = 0; window < tp->w_saved; window++) {
0120         unsigned long sp = tp->rwbuf_stkptrs[window];
0121 
0122         if ((sp & 7) ||
0123             copy_to_user((char __user *) sp, &tp->reg_window[window],
0124                  sizeof(struct reg_window32))) {
0125             force_exit_sig(SIGILL);
0126             return;
0127         }
0128     }
0129     tp->w_saved = 0;
0130 }