1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
2 | /* |
3 | * FPU support code, moved here from head.S so that it can be used |
4 | * by chips which use other head-whatever.S files. |
5 | * |
6 | * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) |
7 | * Copyright (C) 1996 Cort Dougan <cort@cs.nmt.edu> |
8 | * Copyright (C) 1996 Paul Mackerras. |
9 | * Copyright (C) 1997 Dan Malek (dmalek@jlc.net). |
10 | */ |
11 | |
12 | #include <linux/export.h> |
13 | #include <asm/reg.h> |
14 | #include <asm/page.h> |
15 | #include <asm/mmu.h> |
16 | #include <asm/cputable.h> |
17 | #include <asm/cache.h> |
18 | #include <asm/thread_info.h> |
19 | #include <asm/ppc_asm.h> |
20 | #include <asm/asm-offsets.h> |
21 | #include <asm/ptrace.h> |
22 | #include <asm/asm-compat.h> |
23 | #include <asm/feature-fixups.h> |
24 | |
25 | #ifdef CONFIG_VSX |
26 | #define __REST_1FPVSR(n,c,base) \ |
27 | BEGIN_FTR_SECTION \ |
28 | b 2f; \ |
29 | END_FTR_SECTION_IFSET(CPU_FTR_VSX); \ |
30 | REST_FPR(n,base); \ |
31 | b 3f; \ |
32 | 2: REST_VSR(n,c,base); \ |
33 | 3: |
34 | |
35 | #define __REST_32FPVSRS(n,c,base) \ |
36 | BEGIN_FTR_SECTION \ |
37 | b 2f; \ |
38 | END_FTR_SECTION_IFSET(CPU_FTR_VSX); \ |
39 | REST_32FPRS(n,base); \ |
40 | b 3f; \ |
41 | 2: REST_32VSRS(n,c,base); \ |
42 | 3: |
43 | |
44 | #define __SAVE_32FPVSRS(n,c,base) \ |
45 | BEGIN_FTR_SECTION \ |
46 | b 2f; \ |
47 | END_FTR_SECTION_IFSET(CPU_FTR_VSX); \ |
48 | SAVE_32FPRS(n,base); \ |
49 | b 3f; \ |
50 | 2: SAVE_32VSRS(n,c,base); \ |
51 | 3: |
52 | #else |
53 | #define __REST_1FPVSR(n,b,base) REST_FPR(n, base) |
54 | #define __REST_32FPVSRS(n,b,base) REST_32FPRS(n, base) |
55 | #define __SAVE_32FPVSRS(n,b,base) SAVE_32FPRS(n, base) |
56 | #endif |
57 | #define REST_1FPVSR(n,c,base) __REST_1FPVSR(n,__REG_##c,__REG_##base) |
58 | #define REST_32FPVSRS(n,c,base) __REST_32FPVSRS(n,__REG_##c,__REG_##base) |
59 | #define SAVE_32FPVSRS(n,c,base) __SAVE_32FPVSRS(n,__REG_##c,__REG_##base) |
60 | |
61 | /* |
62 | * Load state from memory into FP registers including FPSCR. |
63 | * Assumes the caller has enabled FP in the MSR. |
64 | */ |
65 | _GLOBAL(load_fp_state) |
66 | lfd fr0,FPSTATE_FPSCR(r3) |
67 | MTFSF_L(fr0) |
68 | REST_32FPVSRS(0, R4, R3) |
69 | blr |
70 | EXPORT_SYMBOL(load_fp_state) |
71 | _ASM_NOKPROBE_SYMBOL(load_fp_state); /* used by restore_math */ |
72 | |
73 | /* |
74 | * Store FP state into memory, including FPSCR |
75 | * Assumes the caller has enabled FP in the MSR. |
76 | */ |
77 | _GLOBAL(store_fp_state) |
78 | SAVE_32FPVSRS(0, R4, R3) |
79 | mffs fr0 |
80 | stfd fr0,FPSTATE_FPSCR(r3) |
81 | REST_1FPVSR(0, R4, R3) |
82 | blr |
83 | EXPORT_SYMBOL(store_fp_state) |
84 | |
85 | /* |
86 | * This task wants to use the FPU now. |
87 | * On UP, disable FP for the task which had the FPU previously, |
88 | * and save its floating-point registers in its thread_struct. |
89 | * Load up this task's FP registers from its thread_struct, |
90 | * enable the FPU for the current task and return to the task. |
91 | * Note that on 32-bit this can only use registers that will be |
92 | * restored by fast_exception_return, i.e. r3 - r6, r10 and r11. |
93 | */ |
94 | _GLOBAL(load_up_fpu) |
95 | mfmsr r5 |
96 | #ifdef CONFIG_PPC_BOOK3S_64 |
97 | /* interrupt doesn't set MSR[RI] and HPT can fault on current access */ |
98 | ori r5,r5,MSR_FP|MSR_RI |
99 | #else |
100 | ori r5,r5,MSR_FP |
101 | #endif |
102 | #ifdef CONFIG_VSX |
103 | BEGIN_FTR_SECTION |
104 | oris r5,r5,MSR_VSX@h |
105 | END_FTR_SECTION_IFSET(CPU_FTR_VSX) |
106 | #endif |
107 | MTMSRD(r5) /* enable use of fpu now */ |
108 | isync |
109 | /* enable use of FP after return */ |
110 | #ifdef CONFIG_PPC32 |
111 | addi r5,r2,THREAD |
112 | lwz r4,THREAD_FPEXC_MODE(r5) |
113 | ori r9,r9,MSR_FP /* enable FP for current */ |
114 | or r9,r9,r4 |
115 | #else |
116 | ld r4,PACACURRENT(r13) |
117 | addi r5,r4,THREAD /* Get THREAD */ |
118 | lwz r4,THREAD_FPEXC_MODE(r5) |
119 | ori r12,r12,MSR_FP |
120 | or r12,r12,r4 |
121 | std r12,_MSR(r1) |
122 | #ifdef CONFIG_PPC_BOOK3S_64 |
123 | li r4,0 |
124 | stb r4,PACASRR_VALID(r13) |
125 | #endif |
126 | #endif |
127 | li r4,1 |
128 | stb r4,THREAD_LOAD_FP(r5) |
129 | addi r10,r5,THREAD_FPSTATE |
130 | lfd fr0,FPSTATE_FPSCR(r10) |
131 | MTFSF_L(fr0) |
132 | REST_32FPVSRS(0, R4, R10) |
133 | /* restore registers and return */ |
134 | /* we haven't used ctr or xer or lr */ |
135 | blr |
136 | _ASM_NOKPROBE_SYMBOL(load_up_fpu) |
137 | |
138 | /* |
139 | * save_fpu(tsk) |
140 | * Save the floating-point registers in its thread_struct. |
141 | * Enables the FPU for use in the kernel on return. |
142 | */ |
143 | _GLOBAL(save_fpu) |
144 | addi r3,r3,THREAD /* want THREAD of task */ |
145 | PPC_LL r6,THREAD_FPSAVEAREA(r3) |
146 | PPC_LL r5,PT_REGS(r3) |
147 | PPC_LCMPI 0,r6,0 |
148 | bne 2f |
149 | addi r6,r3,THREAD_FPSTATE |
150 | 2: SAVE_32FPVSRS(0, R4, R6) |
151 | mffs fr0 |
152 | stfd fr0,FPSTATE_FPSCR(r6) |
153 | REST_1FPVSR(0, R4, R6) |
154 | blr |
155 | |