18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 38c2ecf20Sopenharmony_ci * Licensed under the GPL 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/mm.h> 78c2ecf20Sopenharmony_ci#include <linux/sched.h> 88c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 98c2ecf20Sopenharmony_ci#include <asm/ptrace-abi.h> 108c2ecf20Sopenharmony_ci#include <skas.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ciextern int arch_switch_tls(struct task_struct *to); 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_civoid arch_switch_to(struct task_struct *to) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci int err = arch_switch_tls(to); 178c2ecf20Sopenharmony_ci if (!err) 188c2ecf20Sopenharmony_ci return; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci if (err != -EINVAL) 218c2ecf20Sopenharmony_ci printk(KERN_WARNING "arch_switch_tls failed, errno %d, " 228c2ecf20Sopenharmony_ci "not EINVAL\n", -err); 238c2ecf20Sopenharmony_ci else 248c2ecf20Sopenharmony_ci printk(KERN_WARNING "arch_switch_tls failed, errno = EINVAL\n"); 258c2ecf20Sopenharmony_ci} 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciint is_syscall(unsigned long addr) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci unsigned short instr; 308c2ecf20Sopenharmony_ci int n; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci n = copy_from_user(&instr, (void __user *) addr, sizeof(instr)); 338c2ecf20Sopenharmony_ci if (n) { 348c2ecf20Sopenharmony_ci /* access_process_vm() grants access to vsyscall and stub, 358c2ecf20Sopenharmony_ci * while copy_from_user doesn't. Maybe access_process_vm is 368c2ecf20Sopenharmony_ci * slow, but that doesn't matter, since it will be called only 378c2ecf20Sopenharmony_ci * in case of singlestepping, if copy_from_user failed. 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci n = access_process_vm(current, addr, &instr, sizeof(instr), 408c2ecf20Sopenharmony_ci FOLL_FORCE); 418c2ecf20Sopenharmony_ci if (n != sizeof(instr)) { 428c2ecf20Sopenharmony_ci printk(KERN_ERR "is_syscall : failed to read " 438c2ecf20Sopenharmony_ci "instruction from 0x%lx\n", addr); 448c2ecf20Sopenharmony_ci return 1; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci /* int 0x80 or sysenter */ 488c2ecf20Sopenharmony_ci return (instr == 0x80cd) || (instr == 0x340f); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* determines which flags the user has access to. */ 528c2ecf20Sopenharmony_ci/* 1 = access 0 = no access */ 538c2ecf20Sopenharmony_ci#define FLAG_MASK 0x00044dd5 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic const int reg_offsets[] = { 568c2ecf20Sopenharmony_ci [EBX] = HOST_BX, 578c2ecf20Sopenharmony_ci [ECX] = HOST_CX, 588c2ecf20Sopenharmony_ci [EDX] = HOST_DX, 598c2ecf20Sopenharmony_ci [ESI] = HOST_SI, 608c2ecf20Sopenharmony_ci [EDI] = HOST_DI, 618c2ecf20Sopenharmony_ci [EBP] = HOST_BP, 628c2ecf20Sopenharmony_ci [EAX] = HOST_AX, 638c2ecf20Sopenharmony_ci [DS] = HOST_DS, 648c2ecf20Sopenharmony_ci [ES] = HOST_ES, 658c2ecf20Sopenharmony_ci [FS] = HOST_FS, 668c2ecf20Sopenharmony_ci [GS] = HOST_GS, 678c2ecf20Sopenharmony_ci [EIP] = HOST_IP, 688c2ecf20Sopenharmony_ci [CS] = HOST_CS, 698c2ecf20Sopenharmony_ci [EFL] = HOST_EFLAGS, 708c2ecf20Sopenharmony_ci [UESP] = HOST_SP, 718c2ecf20Sopenharmony_ci [SS] = HOST_SS, 728c2ecf20Sopenharmony_ci [ORIG_EAX] = HOST_ORIG_AX, 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ciint putreg(struct task_struct *child, int regno, unsigned long value) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci regno >>= 2; 788c2ecf20Sopenharmony_ci switch (regno) { 798c2ecf20Sopenharmony_ci case EBX: 808c2ecf20Sopenharmony_ci case ECX: 818c2ecf20Sopenharmony_ci case EDX: 828c2ecf20Sopenharmony_ci case ESI: 838c2ecf20Sopenharmony_ci case EDI: 848c2ecf20Sopenharmony_ci case EBP: 858c2ecf20Sopenharmony_ci case EAX: 868c2ecf20Sopenharmony_ci case EIP: 878c2ecf20Sopenharmony_ci case UESP: 888c2ecf20Sopenharmony_ci break; 898c2ecf20Sopenharmony_ci case ORIG_EAX: 908c2ecf20Sopenharmony_ci /* Update the syscall number. */ 918c2ecf20Sopenharmony_ci UPT_SYSCALL_NR(&child->thread.regs.regs) = value; 928c2ecf20Sopenharmony_ci break; 938c2ecf20Sopenharmony_ci case FS: 948c2ecf20Sopenharmony_ci if (value && (value & 3) != 3) 958c2ecf20Sopenharmony_ci return -EIO; 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci case GS: 988c2ecf20Sopenharmony_ci if (value && (value & 3) != 3) 998c2ecf20Sopenharmony_ci return -EIO; 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci case DS: 1028c2ecf20Sopenharmony_ci case ES: 1038c2ecf20Sopenharmony_ci if (value && (value & 3) != 3) 1048c2ecf20Sopenharmony_ci return -EIO; 1058c2ecf20Sopenharmony_ci value &= 0xffff; 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci case SS: 1088c2ecf20Sopenharmony_ci case CS: 1098c2ecf20Sopenharmony_ci if ((value & 3) != 3) 1108c2ecf20Sopenharmony_ci return -EIO; 1118c2ecf20Sopenharmony_ci value &= 0xffff; 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci case EFL: 1148c2ecf20Sopenharmony_ci value &= FLAG_MASK; 1158c2ecf20Sopenharmony_ci child->thread.regs.regs.gp[HOST_EFLAGS] |= value; 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci default : 1188c2ecf20Sopenharmony_ci panic("Bad register in putreg() : %d\n", regno); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci child->thread.regs.regs.gp[reg_offsets[regno]] = value; 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ciint poke_user(struct task_struct *child, long addr, long data) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci if ((addr & 3) || addr < 0) 1278c2ecf20Sopenharmony_ci return -EIO; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (addr < MAX_REG_OFFSET) 1308c2ecf20Sopenharmony_ci return putreg(child, addr, data); 1318c2ecf20Sopenharmony_ci else if ((addr >= offsetof(struct user, u_debugreg[0])) && 1328c2ecf20Sopenharmony_ci (addr <= offsetof(struct user, u_debugreg[7]))) { 1338c2ecf20Sopenharmony_ci addr -= offsetof(struct user, u_debugreg[0]); 1348c2ecf20Sopenharmony_ci addr = addr >> 2; 1358c2ecf20Sopenharmony_ci if ((addr == 4) || (addr == 5)) 1368c2ecf20Sopenharmony_ci return -EIO; 1378c2ecf20Sopenharmony_ci child->thread.arch.debugregs[addr] = data; 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci return -EIO; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ciunsigned long getreg(struct task_struct *child, int regno) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci unsigned long mask = ~0UL; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci regno >>= 2; 1488c2ecf20Sopenharmony_ci switch (regno) { 1498c2ecf20Sopenharmony_ci case FS: 1508c2ecf20Sopenharmony_ci case GS: 1518c2ecf20Sopenharmony_ci case DS: 1528c2ecf20Sopenharmony_ci case ES: 1538c2ecf20Sopenharmony_ci case SS: 1548c2ecf20Sopenharmony_ci case CS: 1558c2ecf20Sopenharmony_ci mask = 0xffff; 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci case EIP: 1588c2ecf20Sopenharmony_ci case UESP: 1598c2ecf20Sopenharmony_ci case EAX: 1608c2ecf20Sopenharmony_ci case EBX: 1618c2ecf20Sopenharmony_ci case ECX: 1628c2ecf20Sopenharmony_ci case EDX: 1638c2ecf20Sopenharmony_ci case ESI: 1648c2ecf20Sopenharmony_ci case EDI: 1658c2ecf20Sopenharmony_ci case EBP: 1668c2ecf20Sopenharmony_ci case EFL: 1678c2ecf20Sopenharmony_ci case ORIG_EAX: 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci default: 1708c2ecf20Sopenharmony_ci panic("Bad register in getreg() : %d\n", regno); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci return mask & child->thread.regs.regs.gp[reg_offsets[regno]]; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/* read the word at location addr in the USER area. */ 1768c2ecf20Sopenharmony_ciint peek_user(struct task_struct *child, long addr, long data) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci unsigned long tmp; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if ((addr & 3) || addr < 0) 1818c2ecf20Sopenharmony_ci return -EIO; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci tmp = 0; /* Default return condition */ 1848c2ecf20Sopenharmony_ci if (addr < MAX_REG_OFFSET) { 1858c2ecf20Sopenharmony_ci tmp = getreg(child, addr); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci else if ((addr >= offsetof(struct user, u_debugreg[0])) && 1888c2ecf20Sopenharmony_ci (addr <= offsetof(struct user, u_debugreg[7]))) { 1898c2ecf20Sopenharmony_ci addr -= offsetof(struct user, u_debugreg[0]); 1908c2ecf20Sopenharmony_ci addr = addr >> 2; 1918c2ecf20Sopenharmony_ci tmp = child->thread.arch.debugregs[addr]; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci return put_user(tmp, (unsigned long __user *) data); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci int err, n, cpu = task_cpu(child); 1998c2ecf20Sopenharmony_ci struct user_i387_struct fpregs; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci err = save_i387_registers(userspace_pid[cpu], 2028c2ecf20Sopenharmony_ci (unsigned long *) &fpregs); 2038c2ecf20Sopenharmony_ci if (err) 2048c2ecf20Sopenharmony_ci return err; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci n = copy_to_user(buf, &fpregs, sizeof(fpregs)); 2078c2ecf20Sopenharmony_ci if(n > 0) 2088c2ecf20Sopenharmony_ci return -EFAULT; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return n; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int set_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci int n, cpu = task_cpu(child); 2168c2ecf20Sopenharmony_ci struct user_i387_struct fpregs; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci n = copy_from_user(&fpregs, buf, sizeof(fpregs)); 2198c2ecf20Sopenharmony_ci if (n > 0) 2208c2ecf20Sopenharmony_ci return -EFAULT; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return restore_i387_registers(userspace_pid[cpu], 2238c2ecf20Sopenharmony_ci (unsigned long *) &fpregs); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int get_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *child) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci int err, n, cpu = task_cpu(child); 2298c2ecf20Sopenharmony_ci struct user_fxsr_struct fpregs; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci err = save_fpx_registers(userspace_pid[cpu], (unsigned long *) &fpregs); 2328c2ecf20Sopenharmony_ci if (err) 2338c2ecf20Sopenharmony_ci return err; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci n = copy_to_user(buf, &fpregs, sizeof(fpregs)); 2368c2ecf20Sopenharmony_ci if(n > 0) 2378c2ecf20Sopenharmony_ci return -EFAULT; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return n; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int set_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *child) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci int n, cpu = task_cpu(child); 2458c2ecf20Sopenharmony_ci struct user_fxsr_struct fpregs; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci n = copy_from_user(&fpregs, buf, sizeof(fpregs)); 2488c2ecf20Sopenharmony_ci if (n > 0) 2498c2ecf20Sopenharmony_ci return -EFAULT; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return restore_fpx_registers(userspace_pid[cpu], 2528c2ecf20Sopenharmony_ci (unsigned long *) &fpregs); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cilong subarch_ptrace(struct task_struct *child, long request, 2568c2ecf20Sopenharmony_ci unsigned long addr, unsigned long data) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci int ret = -EIO; 2598c2ecf20Sopenharmony_ci void __user *datap = (void __user *) data; 2608c2ecf20Sopenharmony_ci switch (request) { 2618c2ecf20Sopenharmony_ci case PTRACE_GETFPREGS: /* Get the child FPU state. */ 2628c2ecf20Sopenharmony_ci ret = get_fpregs(datap, child); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci case PTRACE_SETFPREGS: /* Set the child FPU state. */ 2658c2ecf20Sopenharmony_ci ret = set_fpregs(datap, child); 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci case PTRACE_GETFPXREGS: /* Get the child FPU state. */ 2688c2ecf20Sopenharmony_ci ret = get_fpxregs(datap, child); 2698c2ecf20Sopenharmony_ci break; 2708c2ecf20Sopenharmony_ci case PTRACE_SETFPXREGS: /* Set the child FPU state. */ 2718c2ecf20Sopenharmony_ci ret = set_fpxregs(datap, child); 2728c2ecf20Sopenharmony_ci break; 2738c2ecf20Sopenharmony_ci default: 2748c2ecf20Sopenharmony_ci ret = -EIO; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci} 278