| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Signal handling for 32bit PPC and 32bit tasks on 64bit PPC |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 10 | 11 | * Derived from "arch/i386/kernel/signal.c" |
|---|
| 11 | 12 | * Copyright (C) 1991, 1992 Linus Torvalds |
|---|
| 12 | 13 | * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson |
|---|
| 13 | | - * |
|---|
| 14 | | - * This program is free software; you can redistribute it and/or |
|---|
| 15 | | - * modify it under the terms of the GNU General Public License |
|---|
| 16 | | - * as published by the Free Software Foundation; either version |
|---|
| 17 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 18 | 14 | */ |
|---|
| 19 | 15 | |
|---|
| 20 | 16 | #include <linux/sched.h> |
|---|
| .. | .. |
|---|
| 51 | 47 | #include <asm/unistd.h> |
|---|
| 52 | 48 | #else |
|---|
| 53 | 49 | #include <asm/ucontext.h> |
|---|
| 54 | | -#include <asm/pgtable.h> |
|---|
| 55 | 50 | #endif |
|---|
| 56 | 51 | |
|---|
| 57 | 52 | #include "signal.h" |
|---|
| .. | .. |
|---|
| 107 | 102 | struct mcontext __user *frame) |
|---|
| 108 | 103 | { |
|---|
| 109 | 104 | elf_greg_t64 *gregs = (elf_greg_t64 *)regs; |
|---|
| 110 | | - int i; |
|---|
| 111 | | - /* Force usr to alway see softe as 1 (interrupts enabled) */ |
|---|
| 112 | | - elf_greg_t64 softe = 0x1; |
|---|
| 105 | + int val, i; |
|---|
| 113 | 106 | |
|---|
| 114 | 107 | WARN_ON(!FULL_REGS(regs)); |
|---|
| 115 | 108 | |
|---|
| 116 | 109 | for (i = 0; i <= PT_RESULT; i ++) { |
|---|
| 117 | | - if (i == 14 && !FULL_REGS(regs)) |
|---|
| 118 | | - i = 32; |
|---|
| 119 | | - if ( i == PT_SOFTE) { |
|---|
| 120 | | - if(__put_user((unsigned int)softe, &frame->mc_gregs[i])) |
|---|
| 121 | | - return -EFAULT; |
|---|
| 122 | | - else |
|---|
| 123 | | - continue; |
|---|
| 124 | | - } |
|---|
| 125 | | - if (__put_user((unsigned int)gregs[i], &frame->mc_gregs[i])) |
|---|
| 110 | + /* Force usr to alway see softe as 1 (interrupts enabled) */ |
|---|
| 111 | + if (i == PT_SOFTE) |
|---|
| 112 | + val = 1; |
|---|
| 113 | + else |
|---|
| 114 | + val = gregs[i]; |
|---|
| 115 | + |
|---|
| 116 | + if (__put_user(val, &frame->mc_gregs[i])) |
|---|
| 126 | 117 | return -EFAULT; |
|---|
| 127 | 118 | } |
|---|
| 128 | 119 | return 0; |
|---|
| .. | .. |
|---|
| 239 | 230 | int abigap[56]; |
|---|
| 240 | 231 | }; |
|---|
| 241 | 232 | |
|---|
| 242 | | -#ifdef CONFIG_VSX |
|---|
| 243 | | -unsigned long copy_fpr_to_user(void __user *to, |
|---|
| 244 | | - struct task_struct *task) |
|---|
| 245 | | -{ |
|---|
| 246 | | - u64 buf[ELF_NFPREG]; |
|---|
| 247 | | - int i; |
|---|
| 248 | | - |
|---|
| 249 | | - /* save FPR copy to local buffer then write to the thread_struct */ |
|---|
| 250 | | - for (i = 0; i < (ELF_NFPREG - 1) ; i++) |
|---|
| 251 | | - buf[i] = task->thread.TS_FPR(i); |
|---|
| 252 | | - buf[i] = task->thread.fp_state.fpscr; |
|---|
| 253 | | - return __copy_to_user(to, buf, ELF_NFPREG * sizeof(double)); |
|---|
| 254 | | -} |
|---|
| 255 | | - |
|---|
| 256 | | -unsigned long copy_fpr_from_user(struct task_struct *task, |
|---|
| 257 | | - void __user *from) |
|---|
| 258 | | -{ |
|---|
| 259 | | - u64 buf[ELF_NFPREG]; |
|---|
| 260 | | - int i; |
|---|
| 261 | | - |
|---|
| 262 | | - if (__copy_from_user(buf, from, ELF_NFPREG * sizeof(double))) |
|---|
| 263 | | - return 1; |
|---|
| 264 | | - for (i = 0; i < (ELF_NFPREG - 1) ; i++) |
|---|
| 265 | | - task->thread.TS_FPR(i) = buf[i]; |
|---|
| 266 | | - task->thread.fp_state.fpscr = buf[i]; |
|---|
| 267 | | - |
|---|
| 268 | | - return 0; |
|---|
| 269 | | -} |
|---|
| 270 | | - |
|---|
| 271 | | -unsigned long copy_vsx_to_user(void __user *to, |
|---|
| 272 | | - struct task_struct *task) |
|---|
| 273 | | -{ |
|---|
| 274 | | - u64 buf[ELF_NVSRHALFREG]; |
|---|
| 275 | | - int i; |
|---|
| 276 | | - |
|---|
| 277 | | - /* save FPR copy to local buffer then write to the thread_struct */ |
|---|
| 278 | | - for (i = 0; i < ELF_NVSRHALFREG; i++) |
|---|
| 279 | | - buf[i] = task->thread.fp_state.fpr[i][TS_VSRLOWOFFSET]; |
|---|
| 280 | | - return __copy_to_user(to, buf, ELF_NVSRHALFREG * sizeof(double)); |
|---|
| 281 | | -} |
|---|
| 282 | | - |
|---|
| 283 | | -unsigned long copy_vsx_from_user(struct task_struct *task, |
|---|
| 284 | | - void __user *from) |
|---|
| 285 | | -{ |
|---|
| 286 | | - u64 buf[ELF_NVSRHALFREG]; |
|---|
| 287 | | - int i; |
|---|
| 288 | | - |
|---|
| 289 | | - if (__copy_from_user(buf, from, ELF_NVSRHALFREG * sizeof(double))) |
|---|
| 290 | | - return 1; |
|---|
| 291 | | - for (i = 0; i < ELF_NVSRHALFREG ; i++) |
|---|
| 292 | | - task->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = buf[i]; |
|---|
| 293 | | - return 0; |
|---|
| 294 | | -} |
|---|
| 295 | | - |
|---|
| 296 | | -#ifdef CONFIG_PPC_TRANSACTIONAL_MEM |
|---|
| 297 | | -unsigned long copy_ckfpr_to_user(void __user *to, |
|---|
| 298 | | - struct task_struct *task) |
|---|
| 299 | | -{ |
|---|
| 300 | | - u64 buf[ELF_NFPREG]; |
|---|
| 301 | | - int i; |
|---|
| 302 | | - |
|---|
| 303 | | - /* save FPR copy to local buffer then write to the thread_struct */ |
|---|
| 304 | | - for (i = 0; i < (ELF_NFPREG - 1) ; i++) |
|---|
| 305 | | - buf[i] = task->thread.TS_CKFPR(i); |
|---|
| 306 | | - buf[i] = task->thread.ckfp_state.fpscr; |
|---|
| 307 | | - return __copy_to_user(to, buf, ELF_NFPREG * sizeof(double)); |
|---|
| 308 | | -} |
|---|
| 309 | | - |
|---|
| 310 | | -unsigned long copy_ckfpr_from_user(struct task_struct *task, |
|---|
| 311 | | - void __user *from) |
|---|
| 312 | | -{ |
|---|
| 313 | | - u64 buf[ELF_NFPREG]; |
|---|
| 314 | | - int i; |
|---|
| 315 | | - |
|---|
| 316 | | - if (__copy_from_user(buf, from, ELF_NFPREG * sizeof(double))) |
|---|
| 317 | | - return 1; |
|---|
| 318 | | - for (i = 0; i < (ELF_NFPREG - 1) ; i++) |
|---|
| 319 | | - task->thread.TS_CKFPR(i) = buf[i]; |
|---|
| 320 | | - task->thread.ckfp_state.fpscr = buf[i]; |
|---|
| 321 | | - |
|---|
| 322 | | - return 0; |
|---|
| 323 | | -} |
|---|
| 324 | | - |
|---|
| 325 | | -unsigned long copy_ckvsx_to_user(void __user *to, |
|---|
| 326 | | - struct task_struct *task) |
|---|
| 327 | | -{ |
|---|
| 328 | | - u64 buf[ELF_NVSRHALFREG]; |
|---|
| 329 | | - int i; |
|---|
| 330 | | - |
|---|
| 331 | | - /* save FPR copy to local buffer then write to the thread_struct */ |
|---|
| 332 | | - for (i = 0; i < ELF_NVSRHALFREG; i++) |
|---|
| 333 | | - buf[i] = task->thread.ckfp_state.fpr[i][TS_VSRLOWOFFSET]; |
|---|
| 334 | | - return __copy_to_user(to, buf, ELF_NVSRHALFREG * sizeof(double)); |
|---|
| 335 | | -} |
|---|
| 336 | | - |
|---|
| 337 | | -unsigned long copy_ckvsx_from_user(struct task_struct *task, |
|---|
| 338 | | - void __user *from) |
|---|
| 339 | | -{ |
|---|
| 340 | | - u64 buf[ELF_NVSRHALFREG]; |
|---|
| 341 | | - int i; |
|---|
| 342 | | - |
|---|
| 343 | | - if (__copy_from_user(buf, from, ELF_NVSRHALFREG * sizeof(double))) |
|---|
| 344 | | - return 1; |
|---|
| 345 | | - for (i = 0; i < ELF_NVSRHALFREG ; i++) |
|---|
| 346 | | - task->thread.ckfp_state.fpr[i][TS_VSRLOWOFFSET] = buf[i]; |
|---|
| 347 | | - return 0; |
|---|
| 348 | | -} |
|---|
| 349 | | -#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ |
|---|
| 350 | | -#else |
|---|
| 351 | | -inline unsigned long copy_fpr_to_user(void __user *to, |
|---|
| 352 | | - struct task_struct *task) |
|---|
| 353 | | -{ |
|---|
| 354 | | - return __copy_to_user(to, task->thread.fp_state.fpr, |
|---|
| 355 | | - ELF_NFPREG * sizeof(double)); |
|---|
| 356 | | -} |
|---|
| 357 | | - |
|---|
| 358 | | -inline unsigned long copy_fpr_from_user(struct task_struct *task, |
|---|
| 359 | | - void __user *from) |
|---|
| 360 | | -{ |
|---|
| 361 | | - return __copy_from_user(task->thread.fp_state.fpr, from, |
|---|
| 362 | | - ELF_NFPREG * sizeof(double)); |
|---|
| 363 | | -} |
|---|
| 364 | | - |
|---|
| 365 | | -#ifdef CONFIG_PPC_TRANSACTIONAL_MEM |
|---|
| 366 | | -inline unsigned long copy_ckfpr_to_user(void __user *to, |
|---|
| 367 | | - struct task_struct *task) |
|---|
| 368 | | -{ |
|---|
| 369 | | - return __copy_to_user(to, task->thread.ckfp_state.fpr, |
|---|
| 370 | | - ELF_NFPREG * sizeof(double)); |
|---|
| 371 | | -} |
|---|
| 372 | | - |
|---|
| 373 | | -inline unsigned long copy_ckfpr_from_user(struct task_struct *task, |
|---|
| 374 | | - void __user *from) |
|---|
| 375 | | -{ |
|---|
| 376 | | - return __copy_from_user(task->thread.ckfp_state.fpr, from, |
|---|
| 377 | | - ELF_NFPREG * sizeof(double)); |
|---|
| 378 | | -} |
|---|
| 379 | | -#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ |
|---|
| 380 | | -#endif |
|---|
| 381 | | - |
|---|
| 382 | 233 | /* |
|---|
| 383 | 234 | * Save the current user registers on the user stack. |
|---|
| 384 | 235 | * We only save the altivec/spe registers if the process has used |
|---|
| .. | .. |
|---|
| 470 | 321 | return 1; |
|---|
| 471 | 322 | |
|---|
| 472 | 323 | if (sigret) { |
|---|
| 473 | | - /* Set up the sigreturn trampoline: li r0,sigret; sc */ |
|---|
| 474 | | - if (__put_user(0x38000000UL + sigret, &frame->tramp[0]) |
|---|
| 475 | | - || __put_user(0x44000002UL, &frame->tramp[1])) |
|---|
| 324 | + /* Set up the sigreturn trampoline: li 0,sigret; sc */ |
|---|
| 325 | + if (__put_user(PPC_INST_ADDI + sigret, &frame->tramp[0]) |
|---|
| 326 | + || __put_user(PPC_INST_SC, &frame->tramp[1])) |
|---|
| 476 | 327 | return 1; |
|---|
| 477 | 328 | flush_icache_range((unsigned long) &frame->tramp[0], |
|---|
| 478 | 329 | (unsigned long) &frame->tramp[2]); |
|---|
| .. | .. |
|---|
| 611 | 462 | if (__put_user(msr, &frame->mc_gregs[PT_MSR])) |
|---|
| 612 | 463 | return 1; |
|---|
| 613 | 464 | if (sigret) { |
|---|
| 614 | | - /* Set up the sigreturn trampoline: li r0,sigret; sc */ |
|---|
| 615 | | - if (__put_user(0x38000000UL + sigret, &frame->tramp[0]) |
|---|
| 616 | | - || __put_user(0x44000002UL, &frame->tramp[1])) |
|---|
| 465 | + /* Set up the sigreturn trampoline: li 0,sigret; sc */ |
|---|
| 466 | + if (__put_user(PPC_INST_ADDI + sigret, &frame->tramp[0]) |
|---|
| 467 | + || __put_user(PPC_INST_SC, &frame->tramp[1])) |
|---|
| 617 | 468 | return 1; |
|---|
| 618 | 469 | flush_icache_range((unsigned long) &frame->tramp[0], |
|---|
| 619 | 470 | (unsigned long) &frame->tramp[2]); |
|---|
| .. | .. |
|---|
| 644 | 495 | if (!sig) |
|---|
| 645 | 496 | save_r2 = (unsigned int)regs->gpr[2]; |
|---|
| 646 | 497 | err = restore_general_regs(regs, sr); |
|---|
| 647 | | - regs->trap = 0; |
|---|
| 498 | + set_trap_norestart(regs); |
|---|
| 648 | 499 | err |= __get_user(msr, &sr->mc_gregs[PT_MSR]); |
|---|
| 649 | 500 | if (!sig) |
|---|
| 650 | 501 | regs->gpr[2] = (unsigned long) save_r2; |
|---|
| .. | .. |
|---|
| 1013 | 864 | #else |
|---|
| 1014 | 865 | if (__get_user(mcp, &ucp->uc_regs)) |
|---|
| 1015 | 866 | return -EFAULT; |
|---|
| 1016 | | - if (!access_ok(VERIFY_READ, mcp, sizeof(*mcp))) |
|---|
| 867 | + if (!access_ok(mcp, sizeof(*mcp))) |
|---|
| 1017 | 868 | return -EFAULT; |
|---|
| 1018 | 869 | #endif |
|---|
| 1019 | 870 | set_current_blocked(&set); |
|---|
| .. | .. |
|---|
| 1116 | 967 | */ |
|---|
| 1117 | 968 | mctx = (struct mcontext __user *) |
|---|
| 1118 | 969 | ((unsigned long) &old_ctx->uc_mcontext & ~0xfUL); |
|---|
| 1119 | | - if (!access_ok(VERIFY_WRITE, old_ctx, ctx_size) |
|---|
| 970 | + if (!access_ok(old_ctx, ctx_size) |
|---|
| 1120 | 971 | || save_user_regs(regs, mctx, NULL, 0, ctx_has_vsx_region) |
|---|
| 1121 | 972 | || put_sigset_t(&old_ctx->uc_sigmask, ¤t->blocked) |
|---|
| 1122 | 973 | || __put_user(to_user_ptr(mctx), &old_ctx->uc_regs)) |
|---|
| .. | .. |
|---|
| 1124 | 975 | } |
|---|
| 1125 | 976 | if (new_ctx == NULL) |
|---|
| 1126 | 977 | return 0; |
|---|
| 1127 | | - if (!access_ok(VERIFY_READ, new_ctx, ctx_size) || |
|---|
| 978 | + if (!access_ok(new_ctx, ctx_size) || |
|---|
| 1128 | 979 | fault_in_pages_readable((u8 __user *)new_ctx, ctx_size)) |
|---|
| 1129 | 980 | return -EFAULT; |
|---|
| 1130 | 981 | |
|---|
| .. | .. |
|---|
| 1154 | 1005 | { |
|---|
| 1155 | 1006 | struct rt_sigframe __user *rt_sf; |
|---|
| 1156 | 1007 | struct pt_regs *regs = current_pt_regs(); |
|---|
| 1008 | + int tm_restore = 0; |
|---|
| 1157 | 1009 | #ifdef CONFIG_PPC_TRANSACTIONAL_MEM |
|---|
| 1158 | 1010 | struct ucontext __user *uc_transact; |
|---|
| 1159 | 1011 | unsigned long msr_hi; |
|---|
| 1160 | 1012 | unsigned long tmp; |
|---|
| 1161 | | - int tm_restore = 0; |
|---|
| 1162 | 1013 | #endif |
|---|
| 1163 | 1014 | /* Always make any pending restarted system calls return -EINTR */ |
|---|
| 1164 | 1015 | current->restart_block.fn = do_no_restart_syscall; |
|---|
| 1165 | 1016 | |
|---|
| 1166 | 1017 | rt_sf = (struct rt_sigframe __user *) |
|---|
| 1167 | 1018 | (regs->gpr[1] + __SIGNAL_FRAMESIZE + 16); |
|---|
| 1168 | | - if (!access_ok(VERIFY_READ, rt_sf, sizeof(*rt_sf))) |
|---|
| 1019 | + if (!access_ok(rt_sf, sizeof(*rt_sf))) |
|---|
| 1169 | 1020 | goto bad; |
|---|
| 1170 | 1021 | |
|---|
| 1171 | 1022 | #ifdef CONFIG_PPC_TRANSACTIONAL_MEM |
|---|
| .. | .. |
|---|
| 1209 | 1060 | goto bad; |
|---|
| 1210 | 1061 | } |
|---|
| 1211 | 1062 | } |
|---|
| 1212 | | - if (!tm_restore) |
|---|
| 1213 | | - /* Fall through, for non-TM restore */ |
|---|
| 1063 | + if (!tm_restore) { |
|---|
| 1064 | + /* |
|---|
| 1065 | + * Unset regs->msr because ucontext MSR TS is not |
|---|
| 1066 | + * set, and recheckpoint was not called. This avoid |
|---|
| 1067 | + * hitting a TM Bad thing at RFID |
|---|
| 1068 | + */ |
|---|
| 1069 | + regs->msr &= ~MSR_TS_MASK; |
|---|
| 1070 | + } |
|---|
| 1071 | + /* Fall through, for non-TM restore */ |
|---|
| 1214 | 1072 | #endif |
|---|
| 1215 | | - if (do_setcontext(&rt_sf->uc, regs, 1)) |
|---|
| 1216 | | - goto bad; |
|---|
| 1073 | + if (!tm_restore) |
|---|
| 1074 | + if (do_setcontext(&rt_sf->uc, regs, 1)) |
|---|
| 1075 | + goto bad; |
|---|
| 1217 | 1076 | |
|---|
| 1218 | 1077 | /* |
|---|
| 1219 | 1078 | * It's not clear whether or why it is desirable to save the |
|---|
| .. | .. |
|---|
| 1240 | 1099 | current->comm, current->pid, |
|---|
| 1241 | 1100 | rt_sf, regs->nip, regs->link); |
|---|
| 1242 | 1101 | |
|---|
| 1243 | | - force_sig(SIGSEGV, current); |
|---|
| 1102 | + force_sig(SIGSEGV); |
|---|
| 1244 | 1103 | return 0; |
|---|
| 1245 | 1104 | } |
|---|
| 1246 | 1105 | |
|---|
| .. | .. |
|---|
| 1306 | 1165 | current->thread.debug.dbcr0 = new_dbcr0; |
|---|
| 1307 | 1166 | #endif |
|---|
| 1308 | 1167 | |
|---|
| 1309 | | - if (!access_ok(VERIFY_READ, ctx, sizeof(*ctx)) || |
|---|
| 1168 | + if (!access_ok(ctx, sizeof(*ctx)) || |
|---|
| 1310 | 1169 | fault_in_pages_readable((u8 __user *)ctx, sizeof(*ctx))) |
|---|
| 1311 | 1170 | return -EFAULT; |
|---|
| 1312 | 1171 | |
|---|
| .. | .. |
|---|
| 1329 | 1188 | current->comm, current->pid, |
|---|
| 1330 | 1189 | ctx, regs->nip, regs->link); |
|---|
| 1331 | 1190 | |
|---|
| 1332 | | - force_sig(SIGSEGV, current); |
|---|
| 1191 | + force_sig(SIGSEGV); |
|---|
| 1333 | 1192 | goto out; |
|---|
| 1334 | 1193 | } |
|---|
| 1335 | 1194 | |
|---|
| .. | .. |
|---|
| 1495 | 1354 | { |
|---|
| 1496 | 1355 | sr = (struct mcontext __user *)from_user_ptr(sigctx.regs); |
|---|
| 1497 | 1356 | addr = sr; |
|---|
| 1498 | | - if (!access_ok(VERIFY_READ, sr, sizeof(*sr)) |
|---|
| 1357 | + if (!access_ok(sr, sizeof(*sr)) |
|---|
| 1499 | 1358 | || restore_user_regs(regs, sr, 1)) |
|---|
| 1500 | 1359 | goto badframe; |
|---|
| 1501 | 1360 | } |
|---|
| .. | .. |
|---|
| 1511 | 1370 | current->comm, current->pid, |
|---|
| 1512 | 1371 | addr, regs->nip, regs->link); |
|---|
| 1513 | 1372 | |
|---|
| 1514 | | - force_sig(SIGSEGV, current); |
|---|
| 1373 | + force_sig(SIGSEGV); |
|---|
| 1515 | 1374 | return 0; |
|---|
| 1516 | 1375 | } |
|---|