diff options
Diffstat (limited to 'arch/ia64/kernel/ptrace.c')
-rw-r--r-- | arch/ia64/kernel/ptrace.c | 1217 |
1 files changed, 895 insertions, 322 deletions
diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index ab784ec4319d..2a9943b5947f 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c | |||
@@ -3,6 +3,9 @@ | |||
3 | * | 3 | * |
4 | * Copyright (C) 1999-2005 Hewlett-Packard Co | 4 | * Copyright (C) 1999-2005 Hewlett-Packard Co |
5 | * David Mosberger-Tang <davidm@hpl.hp.com> | 5 | * David Mosberger-Tang <davidm@hpl.hp.com> |
6 | * Copyright (C) 2006 Intel Co | ||
7 | * 2006-08-12 - IA64 Native Utrace implementation support added by | ||
8 | * Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> | ||
6 | * | 9 | * |
7 | * Derived from the x86 and Alpha versions. | 10 | * Derived from the x86 and Alpha versions. |
8 | */ | 11 | */ |
@@ -17,6 +20,8 @@ | |||
17 | #include <linux/security.h> | 20 | #include <linux/security.h> |
18 | #include <linux/audit.h> | 21 | #include <linux/audit.h> |
19 | #include <linux/signal.h> | 22 | #include <linux/signal.h> |
23 | #include <linux/regset.h> | ||
24 | #include <linux/elf.h> | ||
20 | 25 | ||
21 | #include <asm/pgtable.h> | 26 | #include <asm/pgtable.h> |
22 | #include <asm/processor.h> | 27 | #include <asm/processor.h> |
@@ -740,25 +745,6 @@ ia64_sync_fph (struct task_struct *task) | |||
740 | psr->dfh = 1; | 745 | psr->dfh = 1; |
741 | } | 746 | } |
742 | 747 | ||
743 | static int | ||
744 | access_fr (struct unw_frame_info *info, int regnum, int hi, | ||
745 | unsigned long *data, int write_access) | ||
746 | { | ||
747 | struct ia64_fpreg fpval; | ||
748 | int ret; | ||
749 | |||
750 | ret = unw_get_fr(info, regnum, &fpval); | ||
751 | if (ret < 0) | ||
752 | return ret; | ||
753 | |||
754 | if (write_access) { | ||
755 | fpval.u.bits[hi] = *data; | ||
756 | ret = unw_set_fr(info, regnum, fpval); | ||
757 | } else | ||
758 | *data = fpval.u.bits[hi]; | ||
759 | return ret; | ||
760 | } | ||
761 | |||
762 | /* | 748 | /* |
763 | * Change the machine-state of CHILD such that it will return via the normal | 749 | * Change the machine-state of CHILD such that it will return via the normal |
764 | * kernel exit-path, rather than the syscall-exit path. | 750 | * kernel exit-path, rather than the syscall-exit path. |
@@ -860,309 +846,7 @@ access_nat_bits (struct task_struct *child, struct pt_regs *pt, | |||
860 | 846 | ||
861 | static int | 847 | static int |
862 | access_uarea (struct task_struct *child, unsigned long addr, | 848 | access_uarea (struct task_struct *child, unsigned long addr, |
863 | unsigned long *data, int write_access) | 849 | unsigned long *data, int write_access); |
864 | { | ||
865 | unsigned long *ptr, regnum, urbs_end, cfm; | ||
866 | struct switch_stack *sw; | ||
867 | struct pt_regs *pt; | ||
868 | # define pt_reg_addr(pt, reg) ((void *) \ | ||
869 | ((unsigned long) (pt) \ | ||
870 | + offsetof(struct pt_regs, reg))) | ||
871 | |||
872 | |||
873 | pt = task_pt_regs(child); | ||
874 | sw = (struct switch_stack *) (child->thread.ksp + 16); | ||
875 | |||
876 | if ((addr & 0x7) != 0) { | ||
877 | dprintk("ptrace: unaligned register address 0x%lx\n", addr); | ||
878 | return -1; | ||
879 | } | ||
880 | |||
881 | if (addr < PT_F127 + 16) { | ||
882 | /* accessing fph */ | ||
883 | if (write_access) | ||
884 | ia64_sync_fph(child); | ||
885 | else | ||
886 | ia64_flush_fph(child); | ||
887 | ptr = (unsigned long *) | ||
888 | ((unsigned long) &child->thread.fph + addr); | ||
889 | } else if ((addr >= PT_F10) && (addr < PT_F11 + 16)) { | ||
890 | /* scratch registers untouched by kernel (saved in pt_regs) */ | ||
891 | ptr = pt_reg_addr(pt, f10) + (addr - PT_F10); | ||
892 | } else if (addr >= PT_F12 && addr < PT_F15 + 16) { | ||
893 | /* | ||
894 | * Scratch registers untouched by kernel (saved in | ||
895 | * switch_stack). | ||
896 | */ | ||
897 | ptr = (unsigned long *) ((long) sw | ||
898 | + (addr - PT_NAT_BITS - 32)); | ||
899 | } else if (addr < PT_AR_LC + 8) { | ||
900 | /* preserved state: */ | ||
901 | struct unw_frame_info info; | ||
902 | char nat = 0; | ||
903 | int ret; | ||
904 | |||
905 | unw_init_from_blocked_task(&info, child); | ||
906 | if (unw_unwind_to_user(&info) < 0) | ||
907 | return -1; | ||
908 | |||
909 | switch (addr) { | ||
910 | case PT_NAT_BITS: | ||
911 | return access_nat_bits(child, pt, &info, | ||
912 | data, write_access); | ||
913 | |||
914 | case PT_R4: case PT_R5: case PT_R6: case PT_R7: | ||
915 | if (write_access) { | ||
916 | /* read NaT bit first: */ | ||
917 | unsigned long dummy; | ||
918 | |||
919 | ret = unw_get_gr(&info, (addr - PT_R4)/8 + 4, | ||
920 | &dummy, &nat); | ||
921 | if (ret < 0) | ||
922 | return ret; | ||
923 | } | ||
924 | return unw_access_gr(&info, (addr - PT_R4)/8 + 4, data, | ||
925 | &nat, write_access); | ||
926 | |||
927 | case PT_B1: case PT_B2: case PT_B3: | ||
928 | case PT_B4: case PT_B5: | ||
929 | return unw_access_br(&info, (addr - PT_B1)/8 + 1, data, | ||
930 | write_access); | ||
931 | |||
932 | case PT_AR_EC: | ||
933 | return unw_access_ar(&info, UNW_AR_EC, data, | ||
934 | write_access); | ||
935 | |||
936 | case PT_AR_LC: | ||
937 | return unw_access_ar(&info, UNW_AR_LC, data, | ||
938 | write_access); | ||
939 | |||
940 | default: | ||
941 | if (addr >= PT_F2 && addr < PT_F5 + 16) | ||
942 | return access_fr(&info, (addr - PT_F2)/16 + 2, | ||
943 | (addr & 8) != 0, data, | ||
944 | write_access); | ||
945 | else if (addr >= PT_F16 && addr < PT_F31 + 16) | ||
946 | return access_fr(&info, | ||
947 | (addr - PT_F16)/16 + 16, | ||
948 | (addr & 8) != 0, | ||
949 | data, write_access); | ||
950 | else { | ||
951 | dprintk("ptrace: rejecting access to register " | ||
952 | "address 0x%lx\n", addr); | ||
953 | return -1; | ||
954 | } | ||
955 | } | ||
956 | } else if (addr < PT_F9+16) { | ||
957 | /* scratch state */ | ||
958 | switch (addr) { | ||
959 | case PT_AR_BSP: | ||
960 | /* | ||
961 | * By convention, we use PT_AR_BSP to refer to | ||
962 | * the end of the user-level backing store. | ||
963 | * Use ia64_rse_skip_regs(PT_AR_BSP, -CFM.sof) | ||
964 | * to get the real value of ar.bsp at the time | ||
965 | * the kernel was entered. | ||
966 | * | ||
967 | * Furthermore, when changing the contents of | ||
968 | * PT_AR_BSP (or PT_CFM) while the task is | ||
969 | * blocked in a system call, convert the state | ||
970 | * so that the non-system-call exit | ||
971 | * path is used. This ensures that the proper | ||
972 | * state will be picked up when resuming | ||
973 | * execution. However, it *also* means that | ||
974 | * once we write PT_AR_BSP/PT_CFM, it won't be | ||
975 | * possible to modify the syscall arguments of | ||
976 | * the pending system call any longer. This | ||
977 | * shouldn't be an issue because modifying | ||
978 | * PT_AR_BSP/PT_CFM generally implies that | ||
979 | * we're either abandoning the pending system | ||
980 | * call or that we defer it's re-execution | ||
981 | * (e.g., due to GDB doing an inferior | ||
982 | * function call). | ||
983 | */ | ||
984 | urbs_end = ia64_get_user_rbs_end(child, pt, &cfm); | ||
985 | if (write_access) { | ||
986 | if (*data != urbs_end) { | ||
987 | if (in_syscall(pt)) | ||
988 | convert_to_non_syscall(child, | ||
989 | pt, | ||
990 | cfm); | ||
991 | /* | ||
992 | * Simulate user-level write | ||
993 | * of ar.bsp: | ||
994 | */ | ||
995 | pt->loadrs = 0; | ||
996 | pt->ar_bspstore = *data; | ||
997 | } | ||
998 | } else | ||
999 | *data = urbs_end; | ||
1000 | return 0; | ||
1001 | |||
1002 | case PT_CFM: | ||
1003 | urbs_end = ia64_get_user_rbs_end(child, pt, &cfm); | ||
1004 | if (write_access) { | ||
1005 | if (((cfm ^ *data) & PFM_MASK) != 0) { | ||
1006 | if (in_syscall(pt)) | ||
1007 | convert_to_non_syscall(child, | ||
1008 | pt, | ||
1009 | cfm); | ||
1010 | pt->cr_ifs = ((pt->cr_ifs & ~PFM_MASK) | ||
1011 | | (*data & PFM_MASK)); | ||
1012 | } | ||
1013 | } else | ||
1014 | *data = cfm; | ||
1015 | return 0; | ||
1016 | |||
1017 | case PT_CR_IPSR: | ||
1018 | if (write_access) { | ||
1019 | unsigned long tmp = *data; | ||
1020 | /* psr.ri==3 is a reserved value: SDM 2:25 */ | ||
1021 | if ((tmp & IA64_PSR_RI) == IA64_PSR_RI) | ||
1022 | tmp &= ~IA64_PSR_RI; | ||
1023 | pt->cr_ipsr = ((tmp & IPSR_MASK) | ||
1024 | | (pt->cr_ipsr & ~IPSR_MASK)); | ||
1025 | } else | ||
1026 | *data = (pt->cr_ipsr & IPSR_MASK); | ||
1027 | return 0; | ||
1028 | |||
1029 | case PT_AR_RSC: | ||
1030 | if (write_access) | ||
1031 | pt->ar_rsc = *data | (3 << 2); /* force PL3 */ | ||
1032 | else | ||
1033 | *data = pt->ar_rsc; | ||
1034 | return 0; | ||
1035 | |||
1036 | case PT_AR_RNAT: | ||
1037 | ptr = pt_reg_addr(pt, ar_rnat); | ||
1038 | break; | ||
1039 | case PT_R1: | ||
1040 | ptr = pt_reg_addr(pt, r1); | ||
1041 | break; | ||
1042 | case PT_R2: case PT_R3: | ||
1043 | ptr = pt_reg_addr(pt, r2) + (addr - PT_R2); | ||
1044 | break; | ||
1045 | case PT_R8: case PT_R9: case PT_R10: case PT_R11: | ||
1046 | ptr = pt_reg_addr(pt, r8) + (addr - PT_R8); | ||
1047 | break; | ||
1048 | case PT_R12: case PT_R13: | ||
1049 | ptr = pt_reg_addr(pt, r12) + (addr - PT_R12); | ||
1050 | break; | ||
1051 | case PT_R14: | ||
1052 | ptr = pt_reg_addr(pt, r14); | ||
1053 | break; | ||
1054 | case PT_R15: | ||
1055 | ptr = pt_reg_addr(pt, r15); | ||
1056 | break; | ||
1057 | case PT_R16: case PT_R17: case PT_R18: case PT_R19: | ||
1058 | case PT_R20: case PT_R21: case PT_R22: case PT_R23: | ||
1059 | case PT_R24: case PT_R25: case PT_R26: case PT_R27: | ||
1060 | case PT_R28: case PT_R29: case PT_R30: case PT_R31: | ||
1061 | ptr = pt_reg_addr(pt, r16) + (addr - PT_R16); | ||
1062 | break; | ||
1063 | case PT_B0: | ||
1064 | ptr = pt_reg_addr(pt, b0); | ||
1065 | break; | ||
1066 | case PT_B6: | ||
1067 | ptr = pt_reg_addr(pt, b6); | ||
1068 | break; | ||
1069 | case PT_B7: | ||
1070 | ptr = pt_reg_addr(pt, b7); | ||
1071 | break; | ||
1072 | case PT_F6: case PT_F6+8: case PT_F7: case PT_F7+8: | ||
1073 | case PT_F8: case PT_F8+8: case PT_F9: case PT_F9+8: | ||
1074 | ptr = pt_reg_addr(pt, f6) + (addr - PT_F6); | ||
1075 | break; | ||
1076 | case PT_AR_BSPSTORE: | ||
1077 | ptr = pt_reg_addr(pt, ar_bspstore); | ||
1078 | break; | ||
1079 | case PT_AR_UNAT: | ||
1080 | ptr = pt_reg_addr(pt, ar_unat); | ||
1081 | break; | ||
1082 | case PT_AR_PFS: | ||
1083 | ptr = pt_reg_addr(pt, ar_pfs); | ||
1084 | break; | ||
1085 | case PT_AR_CCV: | ||
1086 | ptr = pt_reg_addr(pt, ar_ccv); | ||
1087 | break; | ||
1088 | case PT_AR_FPSR: | ||
1089 | ptr = pt_reg_addr(pt, ar_fpsr); | ||
1090 | break; | ||
1091 | case PT_CR_IIP: | ||
1092 | ptr = pt_reg_addr(pt, cr_iip); | ||
1093 | break; | ||
1094 | case PT_PR: | ||
1095 | ptr = pt_reg_addr(pt, pr); | ||
1096 | break; | ||
1097 | /* scratch register */ | ||
1098 | |||
1099 | default: | ||
1100 | /* disallow accessing anything else... */ | ||
1101 | dprintk("ptrace: rejecting access to register " | ||
1102 | "address 0x%lx\n", addr); | ||
1103 | return -1; | ||
1104 | } | ||
1105 | } else if (addr <= PT_AR_SSD) { | ||
1106 | ptr = pt_reg_addr(pt, ar_csd) + (addr - PT_AR_CSD); | ||
1107 | } else { | ||
1108 | /* access debug registers */ | ||
1109 | |||
1110 | if (addr >= PT_IBR) { | ||
1111 | regnum = (addr - PT_IBR) >> 3; | ||
1112 | ptr = &child->thread.ibr[0]; | ||
1113 | } else { | ||
1114 | regnum = (addr - PT_DBR) >> 3; | ||
1115 | ptr = &child->thread.dbr[0]; | ||
1116 | } | ||
1117 | |||
1118 | if (regnum >= 8) { | ||
1119 | dprintk("ptrace: rejecting access to register " | ||
1120 | "address 0x%lx\n", addr); | ||
1121 | return -1; | ||
1122 | } | ||
1123 | #ifdef CONFIG_PERFMON | ||
1124 | /* | ||
1125 | * Check if debug registers are used by perfmon. This | ||
1126 | * test must be done once we know that we can do the | ||
1127 | * operation, i.e. the arguments are all valid, but | ||
1128 | * before we start modifying the state. | ||
1129 | * | ||
1130 | * Perfmon needs to keep a count of how many processes | ||
1131 | * are trying to modify the debug registers for system | ||
1132 | * wide monitoring sessions. | ||
1133 | * | ||
1134 | * We also include read access here, because they may | ||
1135 | * cause the PMU-installed debug register state | ||
1136 | * (dbr[], ibr[]) to be reset. The two arrays are also | ||
1137 | * used by perfmon, but we do not use | ||
1138 | * IA64_THREAD_DBG_VALID. The registers are restored | ||
1139 | * by the PMU context switch code. | ||
1140 | */ | ||
1141 | if (pfm_use_debug_registers(child)) return -1; | ||
1142 | #endif | ||
1143 | |||
1144 | if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) { | ||
1145 | child->thread.flags |= IA64_THREAD_DBG_VALID; | ||
1146 | memset(child->thread.dbr, 0, | ||
1147 | sizeof(child->thread.dbr)); | ||
1148 | memset(child->thread.ibr, 0, | ||
1149 | sizeof(child->thread.ibr)); | ||
1150 | } | ||
1151 | |||
1152 | ptr += regnum; | ||
1153 | |||
1154 | if ((regnum & 1) && write_access) { | ||
1155 | /* don't let the user set kernel-level breakpoints: */ | ||
1156 | *ptr = *data & ~(7UL << 56); | ||
1157 | return 0; | ||
1158 | } | ||
1159 | } | ||
1160 | if (write_access) | ||
1161 | *ptr = *data; | ||
1162 | else | ||
1163 | *data = *ptr; | ||
1164 | return 0; | ||
1165 | } | ||
1166 | 850 | ||
1167 | static long | 851 | static long |
1168 | ptrace_getregs (struct task_struct *child, struct pt_all_user_regs __user *ppr) | 852 | ptrace_getregs (struct task_struct *child, struct pt_all_user_regs __user *ppr) |
@@ -1626,3 +1310,892 @@ syscall_trace_leave (long arg0, long arg1, long arg2, long arg3, | |||
1626 | if (test_thread_flag(TIF_RESTORE_RSE)) | 1310 | if (test_thread_flag(TIF_RESTORE_RSE)) |
1627 | ia64_sync_krbs(); | 1311 | ia64_sync_krbs(); |
1628 | } | 1312 | } |
1313 | |||
1314 | /* Utrace implementation starts here */ | ||
1315 | struct regset_get { | ||
1316 | void *kbuf; | ||
1317 | void __user *ubuf; | ||
1318 | }; | ||
1319 | |||
1320 | struct regset_set { | ||
1321 | const void *kbuf; | ||
1322 | const void __user *ubuf; | ||
1323 | }; | ||
1324 | |||
1325 | struct regset_getset { | ||
1326 | struct task_struct *target; | ||
1327 | const struct user_regset *regset; | ||
1328 | union { | ||
1329 | struct regset_get get; | ||
1330 | struct regset_set set; | ||
1331 | } u; | ||
1332 | unsigned int pos; | ||
1333 | unsigned int count; | ||
1334 | int ret; | ||
1335 | }; | ||
1336 | |||
1337 | static int | ||
1338 | access_elf_gpreg(struct task_struct *target, struct unw_frame_info *info, | ||
1339 | unsigned long addr, unsigned long *data, int write_access) | ||
1340 | { | ||
1341 | struct pt_regs *pt; | ||
1342 | unsigned long *ptr = NULL; | ||
1343 | int ret; | ||
1344 | char nat = 0; | ||
1345 | |||
1346 | pt = task_pt_regs(target); | ||
1347 | switch (addr) { | ||
1348 | case ELF_GR_OFFSET(1): | ||
1349 | ptr = &pt->r1; | ||
1350 | break; | ||
1351 | case ELF_GR_OFFSET(2): | ||
1352 | case ELF_GR_OFFSET(3): | ||
1353 | ptr = (void *)&pt->r2 + (addr - ELF_GR_OFFSET(2)); | ||
1354 | break; | ||
1355 | case ELF_GR_OFFSET(4) ... ELF_GR_OFFSET(7): | ||
1356 | if (write_access) { | ||
1357 | /* read NaT bit first: */ | ||
1358 | unsigned long dummy; | ||
1359 | |||
1360 | ret = unw_get_gr(info, addr/8, &dummy, &nat); | ||
1361 | if (ret < 0) | ||
1362 | return ret; | ||
1363 | } | ||
1364 | return unw_access_gr(info, addr/8, data, &nat, write_access); | ||
1365 | case ELF_GR_OFFSET(8) ... ELF_GR_OFFSET(11): | ||
1366 | ptr = (void *)&pt->r8 + addr - ELF_GR_OFFSET(8); | ||
1367 | break; | ||
1368 | case ELF_GR_OFFSET(12): | ||
1369 | case ELF_GR_OFFSET(13): | ||
1370 | ptr = (void *)&pt->r12 + addr - ELF_GR_OFFSET(12); | ||
1371 | break; | ||
1372 | case ELF_GR_OFFSET(14): | ||
1373 | ptr = &pt->r14; | ||
1374 | break; | ||
1375 | case ELF_GR_OFFSET(15): | ||
1376 | ptr = &pt->r15; | ||
1377 | } | ||
1378 | if (write_access) | ||
1379 | *ptr = *data; | ||
1380 | else | ||
1381 | *data = *ptr; | ||
1382 | return 0; | ||
1383 | } | ||
1384 | |||
1385 | static int | ||
1386 | access_elf_breg(struct task_struct *target, struct unw_frame_info *info, | ||
1387 | unsigned long addr, unsigned long *data, int write_access) | ||
1388 | { | ||
1389 | struct pt_regs *pt; | ||
1390 | unsigned long *ptr = NULL; | ||
1391 | |||
1392 | pt = task_pt_regs(target); | ||
1393 | switch (addr) { | ||
1394 | case ELF_BR_OFFSET(0): | ||
1395 | ptr = &pt->b0; | ||
1396 | break; | ||
1397 | case ELF_BR_OFFSET(1) ... ELF_BR_OFFSET(5): | ||
1398 | return unw_access_br(info, (addr - ELF_BR_OFFSET(0))/8, | ||
1399 | data, write_access); | ||
1400 | case ELF_BR_OFFSET(6): | ||
1401 | ptr = &pt->b6; | ||
1402 | break; | ||
1403 | case ELF_BR_OFFSET(7): | ||
1404 | ptr = &pt->b7; | ||
1405 | } | ||
1406 | if (write_access) | ||
1407 | *ptr = *data; | ||
1408 | else | ||
1409 | *data = *ptr; | ||
1410 | return 0; | ||
1411 | } | ||
1412 | |||
1413 | static int | ||
1414 | access_elf_areg(struct task_struct *target, struct unw_frame_info *info, | ||
1415 | unsigned long addr, unsigned long *data, int write_access) | ||
1416 | { | ||
1417 | struct pt_regs *pt; | ||
1418 | unsigned long cfm, urbs_end; | ||
1419 | unsigned long *ptr = NULL; | ||
1420 | |||
1421 | pt = task_pt_regs(target); | ||
1422 | if (addr >= ELF_AR_RSC_OFFSET && addr <= ELF_AR_SSD_OFFSET) { | ||
1423 | switch (addr) { | ||
1424 | case ELF_AR_RSC_OFFSET: | ||
1425 | /* force PL3 */ | ||
1426 | if (write_access) | ||
1427 | pt->ar_rsc = *data | (3 << 2); | ||
1428 | else | ||
1429 | *data = pt->ar_rsc; | ||
1430 | return 0; | ||
1431 | case ELF_AR_BSP_OFFSET: | ||
1432 | /* | ||
1433 | * By convention, we use PT_AR_BSP to refer to | ||
1434 | * the end of the user-level backing store. | ||
1435 | * Use ia64_rse_skip_regs(PT_AR_BSP, -CFM.sof) | ||
1436 | * to get the real value of ar.bsp at the time | ||
1437 | * the kernel was entered. | ||
1438 | * | ||
1439 | * Furthermore, when changing the contents of | ||
1440 | * PT_AR_BSP (or PT_CFM) while the task is | ||
1441 | * blocked in a system call, convert the state | ||
1442 | * so that the non-system-call exit | ||
1443 | * path is used. This ensures that the proper | ||
1444 | * state will be picked up when resuming | ||
1445 | * execution. However, it *also* means that | ||
1446 | * once we write PT_AR_BSP/PT_CFM, it won't be | ||
1447 | * possible to modify the syscall arguments of | ||
1448 | * the pending system call any longer. This | ||
1449 | * shouldn't be an issue because modifying | ||
1450 | * PT_AR_BSP/PT_CFM generally implies that | ||
1451 | * we're either abandoning the pending system | ||
1452 | * call or that we defer it's re-execution | ||
1453 | * (e.g., due to GDB doing an inferior | ||
1454 | * function call). | ||
1455 | */ | ||
1456 | urbs_end = ia64_get_user_rbs_end(target, pt, &cfm); | ||
1457 | if (write_access) { | ||
1458 | if (*data != urbs_end) { | ||
1459 | if (in_syscall(pt)) | ||
1460 | convert_to_non_syscall(target, | ||
1461 | pt, | ||
1462 | cfm); | ||
1463 | /* | ||
1464 | * Simulate user-level write | ||
1465 | * of ar.bsp: | ||
1466 | */ | ||
1467 | pt->loadrs = 0; | ||
1468 | pt->ar_bspstore = *data; | ||
1469 | } | ||
1470 | } else | ||
1471 | *data = urbs_end; | ||
1472 | return 0; | ||
1473 | case ELF_AR_BSPSTORE_OFFSET: | ||
1474 | ptr = &pt->ar_bspstore; | ||
1475 | break; | ||
1476 | case ELF_AR_RNAT_OFFSET: | ||
1477 | ptr = &pt->ar_rnat; | ||
1478 | break; | ||
1479 | case ELF_AR_CCV_OFFSET: | ||
1480 | ptr = &pt->ar_ccv; | ||
1481 | break; | ||
1482 | case ELF_AR_UNAT_OFFSET: | ||
1483 | ptr = &pt->ar_unat; | ||
1484 | break; | ||
1485 | case ELF_AR_FPSR_OFFSET: | ||
1486 | ptr = &pt->ar_fpsr; | ||
1487 | break; | ||
1488 | case ELF_AR_PFS_OFFSET: | ||
1489 | ptr = &pt->ar_pfs; | ||
1490 | break; | ||
1491 | case ELF_AR_LC_OFFSET: | ||
1492 | return unw_access_ar(info, UNW_AR_LC, data, | ||
1493 | write_access); | ||
1494 | case ELF_AR_EC_OFFSET: | ||
1495 | return unw_access_ar(info, UNW_AR_EC, data, | ||
1496 | write_access); | ||
1497 | case ELF_AR_CSD_OFFSET: | ||
1498 | ptr = &pt->ar_csd; | ||
1499 | break; | ||
1500 | case ELF_AR_SSD_OFFSET: | ||
1501 | ptr = &pt->ar_ssd; | ||
1502 | } | ||
1503 | } else if (addr >= ELF_CR_IIP_OFFSET && addr <= ELF_CR_IPSR_OFFSET) { | ||
1504 | switch (addr) { | ||
1505 | case ELF_CR_IIP_OFFSET: | ||
1506 | ptr = &pt->cr_iip; | ||
1507 | break; | ||
1508 | case ELF_CFM_OFFSET: | ||
1509 | urbs_end = ia64_get_user_rbs_end(target, pt, &cfm); | ||
1510 | if (write_access) { | ||
1511 | if (((cfm ^ *data) & PFM_MASK) != 0) { | ||
1512 | if (in_syscall(pt)) | ||
1513 | convert_to_non_syscall(target, | ||
1514 | pt, | ||
1515 | cfm); | ||
1516 | pt->cr_ifs = ((pt->cr_ifs & ~PFM_MASK) | ||
1517 | | (*data & PFM_MASK)); | ||
1518 | } | ||
1519 | } else | ||
1520 | *data = cfm; | ||
1521 | return 0; | ||
1522 | case ELF_CR_IPSR_OFFSET: | ||
1523 | if (write_access) { | ||
1524 | unsigned long tmp = *data; | ||
1525 | /* psr.ri==3 is a reserved value: SDM 2:25 */ | ||
1526 | if ((tmp & IA64_PSR_RI) == IA64_PSR_RI) | ||
1527 | tmp &= ~IA64_PSR_RI; | ||
1528 | pt->cr_ipsr = ((tmp & IPSR_MASK) | ||
1529 | | (pt->cr_ipsr & ~IPSR_MASK)); | ||
1530 | } else | ||
1531 | *data = (pt->cr_ipsr & IPSR_MASK); | ||
1532 | return 0; | ||
1533 | } | ||
1534 | } else if (addr == ELF_NAT_OFFSET) | ||
1535 | return access_nat_bits(target, pt, info, | ||
1536 | data, write_access); | ||
1537 | else if (addr == ELF_PR_OFFSET) | ||
1538 | ptr = &pt->pr; | ||
1539 | else | ||
1540 | return -1; | ||
1541 | |||
1542 | if (write_access) | ||
1543 | *ptr = *data; | ||
1544 | else | ||
1545 | *data = *ptr; | ||
1546 | |||
1547 | return 0; | ||
1548 | } | ||
1549 | |||
1550 | static int | ||
1551 | access_elf_reg(struct task_struct *target, struct unw_frame_info *info, | ||
1552 | unsigned long addr, unsigned long *data, int write_access) | ||
1553 | { | ||
1554 | if (addr >= ELF_GR_OFFSET(1) && addr <= ELF_GR_OFFSET(15)) | ||
1555 | return access_elf_gpreg(target, info, addr, data, write_access); | ||
1556 | else if (addr >= ELF_BR_OFFSET(0) && addr <= ELF_BR_OFFSET(7)) | ||
1557 | return access_elf_breg(target, info, addr, data, write_access); | ||
1558 | else | ||
1559 | return access_elf_areg(target, info, addr, data, write_access); | ||
1560 | } | ||
1561 | |||
1562 | void do_gpregs_get(struct unw_frame_info *info, void *arg) | ||
1563 | { | ||
1564 | struct pt_regs *pt; | ||
1565 | struct regset_getset *dst = arg; | ||
1566 | elf_greg_t tmp[16]; | ||
1567 | unsigned int i, index, min_copy; | ||
1568 | |||
1569 | if (unw_unwind_to_user(info) < 0) | ||
1570 | return; | ||
1571 | |||
1572 | /* | ||
1573 | * coredump format: | ||
1574 | * r0-r31 | ||
1575 | * NaT bits (for r0-r31; bit N == 1 iff rN is a NaT) | ||
1576 | * predicate registers (p0-p63) | ||
1577 | * b0-b7 | ||
1578 | * ip cfm user-mask | ||
1579 | * ar.rsc ar.bsp ar.bspstore ar.rnat | ||
1580 | * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec | ||
1581 | */ | ||
1582 | |||
1583 | |||
1584 | /* Skip r0 */ | ||
1585 | if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) { | ||
1586 | dst->ret = user_regset_copyout_zero(&dst->pos, &dst->count, | ||
1587 | &dst->u.get.kbuf, | ||
1588 | &dst->u.get.ubuf, | ||
1589 | 0, ELF_GR_OFFSET(1)); | ||
1590 | if (dst->ret || dst->count == 0) | ||
1591 | return; | ||
1592 | } | ||
1593 | |||
1594 | /* gr1 - gr15 */ | ||
1595 | if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) { | ||
1596 | index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t); | ||
1597 | min_copy = ELF_GR_OFFSET(16) > (dst->pos + dst->count) ? | ||
1598 | (dst->pos + dst->count) : ELF_GR_OFFSET(16); | ||
1599 | for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), | ||
1600 | index++) | ||
1601 | if (access_elf_reg(dst->target, info, i, | ||
1602 | &tmp[index], 0) < 0) { | ||
1603 | dst->ret = -EIO; | ||
1604 | return; | ||
1605 | } | ||
1606 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
1607 | &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, | ||
1608 | ELF_GR_OFFSET(1), ELF_GR_OFFSET(16)); | ||
1609 | if (dst->ret || dst->count == 0) | ||
1610 | return; | ||
1611 | } | ||
1612 | |||
1613 | /* r16-r31 */ | ||
1614 | if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) { | ||
1615 | pt = task_pt_regs(dst->target); | ||
1616 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
1617 | &dst->u.get.kbuf, &dst->u.get.ubuf, &pt->r16, | ||
1618 | ELF_GR_OFFSET(16), ELF_NAT_OFFSET); | ||
1619 | if (dst->ret || dst->count == 0) | ||
1620 | return; | ||
1621 | } | ||
1622 | |||
1623 | /* nat, pr, b0 - b7 */ | ||
1624 | if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) { | ||
1625 | index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t); | ||
1626 | min_copy = ELF_CR_IIP_OFFSET > (dst->pos + dst->count) ? | ||
1627 | (dst->pos + dst->count) : ELF_CR_IIP_OFFSET; | ||
1628 | for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), | ||
1629 | index++) | ||
1630 | if (access_elf_reg(dst->target, info, i, | ||
1631 | &tmp[index], 0) < 0) { | ||
1632 | dst->ret = -EIO; | ||
1633 | return; | ||
1634 | } | ||
1635 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
1636 | &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, | ||
1637 | ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET); | ||
1638 | if (dst->ret || dst->count == 0) | ||
1639 | return; | ||
1640 | } | ||
1641 | |||
1642 | /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat | ||
1643 | * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd | ||
1644 | */ | ||
1645 | if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) { | ||
1646 | index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t); | ||
1647 | min_copy = ELF_AR_END_OFFSET > (dst->pos + dst->count) ? | ||
1648 | (dst->pos + dst->count) : ELF_AR_END_OFFSET; | ||
1649 | for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), | ||
1650 | index++) | ||
1651 | if (access_elf_reg(dst->target, info, i, | ||
1652 | &tmp[index], 0) < 0) { | ||
1653 | dst->ret = -EIO; | ||
1654 | return; | ||
1655 | } | ||
1656 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
1657 | &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, | ||
1658 | ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET); | ||
1659 | } | ||
1660 | } | ||
1661 | |||
1662 | void do_gpregs_set(struct unw_frame_info *info, void *arg) | ||
1663 | { | ||
1664 | struct pt_regs *pt; | ||
1665 | struct regset_getset *dst = arg; | ||
1666 | elf_greg_t tmp[16]; | ||
1667 | unsigned int i, index; | ||
1668 | |||
1669 | if (unw_unwind_to_user(info) < 0) | ||
1670 | return; | ||
1671 | |||
1672 | /* Skip r0 */ | ||
1673 | if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) { | ||
1674 | dst->ret = user_regset_copyin_ignore(&dst->pos, &dst->count, | ||
1675 | &dst->u.set.kbuf, | ||
1676 | &dst->u.set.ubuf, | ||
1677 | 0, ELF_GR_OFFSET(1)); | ||
1678 | if (dst->ret || dst->count == 0) | ||
1679 | return; | ||
1680 | } | ||
1681 | |||
1682 | /* gr1-gr15 */ | ||
1683 | if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) { | ||
1684 | i = dst->pos; | ||
1685 | index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t); | ||
1686 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
1687 | &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, | ||
1688 | ELF_GR_OFFSET(1), ELF_GR_OFFSET(16)); | ||
1689 | if (dst->ret) | ||
1690 | return; | ||
1691 | for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++) | ||
1692 | if (access_elf_reg(dst->target, info, i, | ||
1693 | &tmp[index], 1) < 0) { | ||
1694 | dst->ret = -EIO; | ||
1695 | return; | ||
1696 | } | ||
1697 | if (dst->count == 0) | ||
1698 | return; | ||
1699 | } | ||
1700 | |||
1701 | /* gr16-gr31 */ | ||
1702 | if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) { | ||
1703 | pt = task_pt_regs(dst->target); | ||
1704 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
1705 | &dst->u.set.kbuf, &dst->u.set.ubuf, &pt->r16, | ||
1706 | ELF_GR_OFFSET(16), ELF_NAT_OFFSET); | ||
1707 | if (dst->ret || dst->count == 0) | ||
1708 | return; | ||
1709 | } | ||
1710 | |||
1711 | /* nat, pr, b0 - b7 */ | ||
1712 | if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) { | ||
1713 | i = dst->pos; | ||
1714 | index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t); | ||
1715 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
1716 | &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, | ||
1717 | ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET); | ||
1718 | if (dst->ret) | ||
1719 | return; | ||
1720 | for (; i < dst->pos; i += sizeof(elf_greg_t), index++) | ||
1721 | if (access_elf_reg(dst->target, info, i, | ||
1722 | &tmp[index], 1) < 0) { | ||
1723 | dst->ret = -EIO; | ||
1724 | return; | ||
1725 | } | ||
1726 | if (dst->count == 0) | ||
1727 | return; | ||
1728 | } | ||
1729 | |||
1730 | /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat | ||
1731 | * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd | ||
1732 | */ | ||
1733 | if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) { | ||
1734 | i = dst->pos; | ||
1735 | index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t); | ||
1736 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
1737 | &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, | ||
1738 | ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET); | ||
1739 | if (dst->ret) | ||
1740 | return; | ||
1741 | for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++) | ||
1742 | if (access_elf_reg(dst->target, info, i, | ||
1743 | &tmp[index], 1) < 0) { | ||
1744 | dst->ret = -EIO; | ||
1745 | return; | ||
1746 | } | ||
1747 | } | ||
1748 | } | ||
1749 | |||
1750 | #define ELF_FP_OFFSET(i) (i * sizeof(elf_fpreg_t)) | ||
1751 | |||
1752 | void do_fpregs_get(struct unw_frame_info *info, void *arg) | ||
1753 | { | ||
1754 | struct regset_getset *dst = arg; | ||
1755 | struct task_struct *task = dst->target; | ||
1756 | elf_fpreg_t tmp[30]; | ||
1757 | int index, min_copy, i; | ||
1758 | |||
1759 | if (unw_unwind_to_user(info) < 0) | ||
1760 | return; | ||
1761 | |||
1762 | /* Skip pos 0 and 1 */ | ||
1763 | if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(2)) { | ||
1764 | dst->ret = user_regset_copyout_zero(&dst->pos, &dst->count, | ||
1765 | &dst->u.get.kbuf, | ||
1766 | &dst->u.get.ubuf, | ||
1767 | 0, ELF_FP_OFFSET(2)); | ||
1768 | if (dst->count == 0 || dst->ret) | ||
1769 | return; | ||
1770 | } | ||
1771 | |||
1772 | /* fr2-fr31 */ | ||
1773 | if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(32)) { | ||
1774 | index = (dst->pos - ELF_FP_OFFSET(2)) / sizeof(elf_fpreg_t); | ||
1775 | |||
1776 | min_copy = min(((unsigned int)ELF_FP_OFFSET(32)), | ||
1777 | dst->pos + dst->count); | ||
1778 | for (i = dst->pos; i < min_copy; i += sizeof(elf_fpreg_t), | ||
1779 | index++) | ||
1780 | if (unw_get_fr(info, i / sizeof(elf_fpreg_t), | ||
1781 | &tmp[index])) { | ||
1782 | dst->ret = -EIO; | ||
1783 | return; | ||
1784 | } | ||
1785 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
1786 | &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, | ||
1787 | ELF_FP_OFFSET(2), ELF_FP_OFFSET(32)); | ||
1788 | if (dst->count == 0 || dst->ret) | ||
1789 | return; | ||
1790 | } | ||
1791 | |||
1792 | /* fph */ | ||
1793 | if (dst->count > 0) { | ||
1794 | ia64_flush_fph(dst->target); | ||
1795 | if (task->thread.flags & IA64_THREAD_FPH_VALID) | ||
1796 | dst->ret = user_regset_copyout( | ||
1797 | &dst->pos, &dst->count, | ||
1798 | &dst->u.get.kbuf, &dst->u.get.ubuf, | ||
1799 | &dst->target->thread.fph, | ||
1800 | ELF_FP_OFFSET(32), -1); | ||
1801 | else | ||
1802 | /* Zero fill instead. */ | ||
1803 | dst->ret = user_regset_copyout_zero( | ||
1804 | &dst->pos, &dst->count, | ||
1805 | &dst->u.get.kbuf, &dst->u.get.ubuf, | ||
1806 | ELF_FP_OFFSET(32), -1); | ||
1807 | } | ||
1808 | } | ||
1809 | |||
1810 | void do_fpregs_set(struct unw_frame_info *info, void *arg) | ||
1811 | { | ||
1812 | struct regset_getset *dst = arg; | ||
1813 | elf_fpreg_t fpreg, tmp[30]; | ||
1814 | int index, start, end; | ||
1815 | |||
1816 | if (unw_unwind_to_user(info) < 0) | ||
1817 | return; | ||
1818 | |||
1819 | /* Skip pos 0 and 1 */ | ||
1820 | if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(2)) { | ||
1821 | dst->ret = user_regset_copyin_ignore(&dst->pos, &dst->count, | ||
1822 | &dst->u.set.kbuf, | ||
1823 | &dst->u.set.ubuf, | ||
1824 | 0, ELF_FP_OFFSET(2)); | ||
1825 | if (dst->count == 0 || dst->ret) | ||
1826 | return; | ||
1827 | } | ||
1828 | |||
1829 | /* fr2-fr31 */ | ||
1830 | if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(32)) { | ||
1831 | start = dst->pos; | ||
1832 | end = min(((unsigned int)ELF_FP_OFFSET(32)), | ||
1833 | dst->pos + dst->count); | ||
1834 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
1835 | &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, | ||
1836 | ELF_FP_OFFSET(2), ELF_FP_OFFSET(32)); | ||
1837 | if (dst->ret) | ||
1838 | return; | ||
1839 | |||
1840 | if (start & 0xF) { /* only write high part */ | ||
1841 | if (unw_get_fr(info, start / sizeof(elf_fpreg_t), | ||
1842 | &fpreg)) { | ||
1843 | dst->ret = -EIO; | ||
1844 | return; | ||
1845 | } | ||
1846 | tmp[start / sizeof(elf_fpreg_t) - 2].u.bits[0] | ||
1847 | = fpreg.u.bits[0]; | ||
1848 | start &= ~0xFUL; | ||
1849 | } | ||
1850 | if (end & 0xF) { /* only write low part */ | ||
1851 | if (unw_get_fr(info, end / sizeof(elf_fpreg_t), | ||
1852 | &fpreg)) { | ||
1853 | dst->ret = -EIO; | ||
1854 | return; | ||
1855 | } | ||
1856 | tmp[end / sizeof(elf_fpreg_t) - 2].u.bits[1] | ||
1857 | = fpreg.u.bits[1]; | ||
1858 | end = (end + 0xF) & ~0xFUL; | ||
1859 | } | ||
1860 | |||
1861 | for ( ; start < end ; start += sizeof(elf_fpreg_t)) { | ||
1862 | index = start / sizeof(elf_fpreg_t); | ||
1863 | if (unw_set_fr(info, index, tmp[index - 2])) { | ||
1864 | dst->ret = -EIO; | ||
1865 | return; | ||
1866 | } | ||
1867 | } | ||
1868 | if (dst->ret || dst->count == 0) | ||
1869 | return; | ||
1870 | } | ||
1871 | |||
1872 | /* fph */ | ||
1873 | if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(128)) { | ||
1874 | ia64_sync_fph(dst->target); | ||
1875 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
1876 | &dst->u.set.kbuf, | ||
1877 | &dst->u.set.ubuf, | ||
1878 | &dst->target->thread.fph, | ||
1879 | ELF_FP_OFFSET(32), -1); | ||
1880 | } | ||
1881 | } | ||
1882 | |||
1883 | static int | ||
1884 | do_regset_call(void (*call)(struct unw_frame_info *, void *), | ||
1885 | struct task_struct *target, | ||
1886 | const struct user_regset *regset, | ||
1887 | unsigned int pos, unsigned int count, | ||
1888 | const void *kbuf, const void __user *ubuf) | ||
1889 | { | ||
1890 | struct regset_getset info = { .target = target, .regset = regset, | ||
1891 | .pos = pos, .count = count, | ||
1892 | .u.set = { .kbuf = kbuf, .ubuf = ubuf }, | ||
1893 | .ret = 0 }; | ||
1894 | |||
1895 | if (target == current) | ||
1896 | unw_init_running(call, &info); | ||
1897 | else { | ||
1898 | struct unw_frame_info ufi; | ||
1899 | memset(&ufi, 0, sizeof(ufi)); | ||
1900 | unw_init_from_blocked_task(&ufi, target); | ||
1901 | (*call)(&ufi, &info); | ||
1902 | } | ||
1903 | |||
1904 | return info.ret; | ||
1905 | } | ||
1906 | |||
1907 | static int | ||
1908 | gpregs_get(struct task_struct *target, | ||
1909 | const struct user_regset *regset, | ||
1910 | unsigned int pos, unsigned int count, | ||
1911 | void *kbuf, void __user *ubuf) | ||
1912 | { | ||
1913 | return do_regset_call(do_gpregs_get, target, regset, pos, count, | ||
1914 | kbuf, ubuf); | ||
1915 | } | ||
1916 | |||
1917 | static int gpregs_set(struct task_struct *target, | ||
1918 | const struct user_regset *regset, | ||
1919 | unsigned int pos, unsigned int count, | ||
1920 | const void *kbuf, const void __user *ubuf) | ||
1921 | { | ||
1922 | return do_regset_call(do_gpregs_set, target, regset, pos, count, | ||
1923 | kbuf, ubuf); | ||
1924 | } | ||
1925 | |||
1926 | static void do_gpregs_writeback(struct unw_frame_info *info, void *arg) | ||
1927 | { | ||
1928 | do_sync_rbs(info, ia64_sync_user_rbs); | ||
1929 | } | ||
1930 | |||
1931 | /* | ||
1932 | * This is called to write back the register backing store. | ||
1933 | * ptrace does this before it stops, so that a tracer reading the user | ||
1934 | * memory after the thread stops will get the current register data. | ||
1935 | */ | ||
1936 | static int | ||
1937 | gpregs_writeback(struct task_struct *target, | ||
1938 | const struct user_regset *regset, | ||
1939 | int now) | ||
1940 | { | ||
1941 | if (test_and_set_tsk_thread_flag(target, TIF_RESTORE_RSE)) | ||
1942 | return 0; | ||
1943 | tsk_set_notify_resume(target); | ||
1944 | return do_regset_call(do_gpregs_writeback, target, regset, 0, 0, | ||
1945 | NULL, NULL); | ||
1946 | } | ||
1947 | |||
1948 | static int | ||
1949 | fpregs_active(struct task_struct *target, const struct user_regset *regset) | ||
1950 | { | ||
1951 | return (target->thread.flags & IA64_THREAD_FPH_VALID) ? 128 : 32; | ||
1952 | } | ||
1953 | |||
1954 | static int fpregs_get(struct task_struct *target, | ||
1955 | const struct user_regset *regset, | ||
1956 | unsigned int pos, unsigned int count, | ||
1957 | void *kbuf, void __user *ubuf) | ||
1958 | { | ||
1959 | return do_regset_call(do_fpregs_get, target, regset, pos, count, | ||
1960 | kbuf, ubuf); | ||
1961 | } | ||
1962 | |||
1963 | static int fpregs_set(struct task_struct *target, | ||
1964 | const struct user_regset *regset, | ||
1965 | unsigned int pos, unsigned int count, | ||
1966 | const void *kbuf, const void __user *ubuf) | ||
1967 | { | ||
1968 | return do_regset_call(do_fpregs_set, target, regset, pos, count, | ||
1969 | kbuf, ubuf); | ||
1970 | } | ||
1971 | |||
1972 | static int | ||
1973 | access_uarea(struct task_struct *child, unsigned long addr, | ||
1974 | unsigned long *data, int write_access) | ||
1975 | { | ||
1976 | unsigned int pos = -1; /* an invalid value */ | ||
1977 | int ret; | ||
1978 | unsigned long *ptr, regnum; | ||
1979 | |||
1980 | if ((addr & 0x7) != 0) { | ||
1981 | dprintk("ptrace: unaligned register address 0x%lx\n", addr); | ||
1982 | return -1; | ||
1983 | } | ||
1984 | if ((addr >= PT_NAT_BITS + 8 && addr < PT_F2) || | ||
1985 | (addr >= PT_R7 + 8 && addr < PT_B1) || | ||
1986 | (addr >= PT_AR_LC + 8 && addr < PT_CR_IPSR) || | ||
1987 | (addr >= PT_AR_SSD + 8 && addr < PT_DBR)) { | ||
1988 | dprintk("ptrace: rejecting access to register " | ||
1989 | "address 0x%lx\n", addr); | ||
1990 | return -1; | ||
1991 | } | ||
1992 | |||
1993 | switch (addr) { | ||
1994 | case PT_F32 ... (PT_F127 + 15): | ||
1995 | pos = addr - PT_F32 + ELF_FP_OFFSET(32); | ||
1996 | break; | ||
1997 | case PT_F2 ... (PT_F5 + 15): | ||
1998 | pos = addr - PT_F2 + ELF_FP_OFFSET(2); | ||
1999 | break; | ||
2000 | case PT_F10 ... (PT_F31 + 15): | ||
2001 | pos = addr - PT_F10 + ELF_FP_OFFSET(10); | ||
2002 | break; | ||
2003 | case PT_F6 ... (PT_F9 + 15): | ||
2004 | pos = addr - PT_F6 + ELF_FP_OFFSET(6); | ||
2005 | break; | ||
2006 | } | ||
2007 | |||
2008 | if (pos != -1) { | ||
2009 | if (write_access) | ||
2010 | ret = fpregs_set(child, NULL, pos, | ||
2011 | sizeof(unsigned long), data, NULL); | ||
2012 | else | ||
2013 | ret = fpregs_get(child, NULL, pos, | ||
2014 | sizeof(unsigned long), data, NULL); | ||
2015 | if (ret != 0) | ||
2016 | return -1; | ||
2017 | return 0; | ||
2018 | } | ||
2019 | |||
2020 | switch (addr) { | ||
2021 | case PT_NAT_BITS: | ||
2022 | pos = ELF_NAT_OFFSET; | ||
2023 | break; | ||
2024 | case PT_R4 ... PT_R7: | ||
2025 | pos = addr - PT_R4 + ELF_GR_OFFSET(4); | ||
2026 | break; | ||
2027 | case PT_B1 ... PT_B5: | ||
2028 | pos = addr - PT_B1 + ELF_BR_OFFSET(1); | ||
2029 | break; | ||
2030 | case PT_AR_EC: | ||
2031 | pos = ELF_AR_EC_OFFSET; | ||
2032 | break; | ||
2033 | case PT_AR_LC: | ||
2034 | pos = ELF_AR_LC_OFFSET; | ||
2035 | break; | ||
2036 | case PT_CR_IPSR: | ||
2037 | pos = ELF_CR_IPSR_OFFSET; | ||
2038 | break; | ||
2039 | case PT_CR_IIP: | ||
2040 | pos = ELF_CR_IIP_OFFSET; | ||
2041 | break; | ||
2042 | case PT_CFM: | ||
2043 | pos = ELF_CFM_OFFSET; | ||
2044 | break; | ||
2045 | case PT_AR_UNAT: | ||
2046 | pos = ELF_AR_UNAT_OFFSET; | ||
2047 | break; | ||
2048 | case PT_AR_PFS: | ||
2049 | pos = ELF_AR_PFS_OFFSET; | ||
2050 | break; | ||
2051 | case PT_AR_RSC: | ||
2052 | pos = ELF_AR_RSC_OFFSET; | ||
2053 | break; | ||
2054 | case PT_AR_RNAT: | ||
2055 | pos = ELF_AR_RNAT_OFFSET; | ||
2056 | break; | ||
2057 | case PT_AR_BSPSTORE: | ||
2058 | pos = ELF_AR_BSPSTORE_OFFSET; | ||
2059 | break; | ||
2060 | case PT_PR: | ||
2061 | pos = ELF_PR_OFFSET; | ||
2062 | break; | ||
2063 | case PT_B6: | ||
2064 | pos = ELF_BR_OFFSET(6); | ||
2065 | break; | ||
2066 | case PT_AR_BSP: | ||
2067 | pos = ELF_AR_BSP_OFFSET; | ||
2068 | break; | ||
2069 | case PT_R1 ... PT_R3: | ||
2070 | pos = addr - PT_R1 + ELF_GR_OFFSET(1); | ||
2071 | break; | ||
2072 | case PT_R12 ... PT_R15: | ||
2073 | pos = addr - PT_R12 + ELF_GR_OFFSET(12); | ||
2074 | break; | ||
2075 | case PT_R8 ... PT_R11: | ||
2076 | pos = addr - PT_R8 + ELF_GR_OFFSET(8); | ||
2077 | break; | ||
2078 | case PT_R16 ... PT_R31: | ||
2079 | pos = addr - PT_R16 + ELF_GR_OFFSET(16); | ||
2080 | break; | ||
2081 | case PT_AR_CCV: | ||
2082 | pos = ELF_AR_CCV_OFFSET; | ||
2083 | break; | ||
2084 | case PT_AR_FPSR: | ||
2085 | pos = ELF_AR_FPSR_OFFSET; | ||
2086 | break; | ||
2087 | case PT_B0: | ||
2088 | pos = ELF_BR_OFFSET(0); | ||
2089 | break; | ||
2090 | case PT_B7: | ||
2091 | pos = ELF_BR_OFFSET(7); | ||
2092 | break; | ||
2093 | case PT_AR_CSD: | ||
2094 | pos = ELF_AR_CSD_OFFSET; | ||
2095 | break; | ||
2096 | case PT_AR_SSD: | ||
2097 | pos = ELF_AR_SSD_OFFSET; | ||
2098 | break; | ||
2099 | } | ||
2100 | |||
2101 | if (pos != -1) { | ||
2102 | if (write_access) | ||
2103 | ret = gpregs_set(child, NULL, pos, | ||
2104 | sizeof(unsigned long), data, NULL); | ||
2105 | else | ||
2106 | ret = gpregs_get(child, NULL, pos, | ||
2107 | sizeof(unsigned long), data, NULL); | ||
2108 | if (ret != 0) | ||
2109 | return -1; | ||
2110 | return 0; | ||
2111 | } | ||
2112 | |||
2113 | /* access debug registers */ | ||
2114 | if (addr >= PT_IBR) { | ||
2115 | regnum = (addr - PT_IBR) >> 3; | ||
2116 | ptr = &child->thread.ibr[0]; | ||
2117 | } else { | ||
2118 | regnum = (addr - PT_DBR) >> 3; | ||
2119 | ptr = &child->thread.dbr[0]; | ||
2120 | } | ||
2121 | |||
2122 | if (regnum >= 8) { | ||
2123 | dprintk("ptrace: rejecting access to register " | ||
2124 | "address 0x%lx\n", addr); | ||
2125 | return -1; | ||
2126 | } | ||
2127 | #ifdef CONFIG_PERFMON | ||
2128 | /* | ||
2129 | * Check if debug registers are used by perfmon. This | ||
2130 | * test must be done once we know that we can do the | ||
2131 | * operation, i.e. the arguments are all valid, but | ||
2132 | * before we start modifying the state. | ||
2133 | * | ||
2134 | * Perfmon needs to keep a count of how many processes | ||
2135 | * are trying to modify the debug registers for system | ||
2136 | * wide monitoring sessions. | ||
2137 | * | ||
2138 | * We also include read access here, because they may | ||
2139 | * cause the PMU-installed debug register state | ||
2140 | * (dbr[], ibr[]) to be reset. The two arrays are also | ||
2141 | * used by perfmon, but we do not use | ||
2142 | * IA64_THREAD_DBG_VALID. The registers are restored | ||
2143 | * by the PMU context switch code. | ||
2144 | */ | ||
2145 | if (pfm_use_debug_registers(child)) | ||
2146 | return -1; | ||
2147 | #endif | ||
2148 | |||
2149 | if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) { | ||
2150 | child->thread.flags |= IA64_THREAD_DBG_VALID; | ||
2151 | memset(child->thread.dbr, 0, | ||
2152 | sizeof(child->thread.dbr)); | ||
2153 | memset(child->thread.ibr, 0, | ||
2154 | sizeof(child->thread.ibr)); | ||
2155 | } | ||
2156 | |||
2157 | ptr += regnum; | ||
2158 | |||
2159 | if ((regnum & 1) && write_access) { | ||
2160 | /* don't let the user set kernel-level breakpoints: */ | ||
2161 | *ptr = *data & ~(7UL << 56); | ||
2162 | return 0; | ||
2163 | } | ||
2164 | if (write_access) | ||
2165 | *ptr = *data; | ||
2166 | else | ||
2167 | *data = *ptr; | ||
2168 | return 0; | ||
2169 | } | ||
2170 | |||
2171 | static const struct user_regset native_regsets[] = { | ||
2172 | { | ||
2173 | .core_note_type = NT_PRSTATUS, | ||
2174 | .n = ELF_NGREG, | ||
2175 | .size = sizeof(elf_greg_t), .align = sizeof(elf_greg_t), | ||
2176 | .get = gpregs_get, .set = gpregs_set, | ||
2177 | .writeback = gpregs_writeback | ||
2178 | }, | ||
2179 | { | ||
2180 | .core_note_type = NT_PRFPREG, | ||
2181 | .n = ELF_NFPREG, | ||
2182 | .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t), | ||
2183 | .get = fpregs_get, .set = fpregs_set, .active = fpregs_active | ||
2184 | }, | ||
2185 | }; | ||
2186 | |||
2187 | static const struct user_regset_view user_ia64_view = { | ||
2188 | .name = "ia64", | ||
2189 | .e_machine = EM_IA_64, | ||
2190 | .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets) | ||
2191 | }; | ||
2192 | |||
2193 | const struct user_regset_view *task_user_regset_view(struct task_struct *tsk) | ||
2194 | { | ||
2195 | #ifdef CONFIG_IA32_SUPPORT | ||
2196 | extern const struct user_regset_view user_ia32_view; | ||
2197 | if (IS_IA32_PROCESS(task_pt_regs(tsk))) | ||
2198 | return &user_ia32_view; | ||
2199 | #endif | ||
2200 | return &user_ia64_view; | ||
2201 | } | ||