diff options
| author | Shaohua Li <shaohua.li@intel.com> | 2008-02-28 03:47:50 -0500 |
|---|---|---|
| committer | Tony Luck <tony.luck@intel.com> | 2008-03-12 19:26:23 -0400 |
| commit | c70f8f68676866d778564de337bec6b8734c3850 (patch) | |
| tree | 527ddecf32f9e055698f21b60f6a250fca5d9a3c | |
| parent | baadac8b10c5ac15ce3d26b68fa266c8889b163f (diff) | |
[IA64] regset: 64-bit support
This is the 64-bit regset implementation under IA64. Basically register
read/write, which is derived from current ptrace register read/write.
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
| -rw-r--r-- | arch/ia64/kernel/ptrace.c | 690 | ||||
| -rw-r--r-- | include/asm-ia64/elf.h | 24 |
2 files changed, 714 insertions, 0 deletions
diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index ab784ec4319d..7136c7811efc 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> |
| @@ -1626,3 +1631,688 @@ syscall_trace_leave (long arg0, long arg1, long arg2, long arg3, | |||
| 1626 | if (test_thread_flag(TIF_RESTORE_RSE)) | 1631 | if (test_thread_flag(TIF_RESTORE_RSE)) |
| 1627 | ia64_sync_krbs(); | 1632 | ia64_sync_krbs(); |
| 1628 | } | 1633 | } |
| 1634 | |||
| 1635 | /* Utrace implementation starts here */ | ||
| 1636 | struct regset_get { | ||
| 1637 | void *kbuf; | ||
| 1638 | void __user *ubuf; | ||
| 1639 | }; | ||
| 1640 | |||
| 1641 | struct regset_set { | ||
| 1642 | const void *kbuf; | ||
| 1643 | const void __user *ubuf; | ||
| 1644 | }; | ||
| 1645 | |||
| 1646 | struct regset_getset { | ||
| 1647 | struct task_struct *target; | ||
| 1648 | const struct user_regset *regset; | ||
| 1649 | union { | ||
| 1650 | struct regset_get get; | ||
| 1651 | struct regset_set set; | ||
| 1652 | } u; | ||
| 1653 | unsigned int pos; | ||
| 1654 | unsigned int count; | ||
| 1655 | int ret; | ||
| 1656 | }; | ||
| 1657 | |||
| 1658 | static int | ||
| 1659 | access_elf_gpreg(struct task_struct *target, struct unw_frame_info *info, | ||
| 1660 | unsigned long addr, unsigned long *data, int write_access) | ||
| 1661 | { | ||
| 1662 | struct pt_regs *pt; | ||
| 1663 | unsigned long *ptr = NULL; | ||
| 1664 | int ret; | ||
| 1665 | char nat = 0; | ||
| 1666 | |||
| 1667 | pt = task_pt_regs(target); | ||
| 1668 | switch (addr) { | ||
| 1669 | case ELF_GR_OFFSET(1): | ||
| 1670 | ptr = &pt->r1; | ||
| 1671 | break; | ||
| 1672 | case ELF_GR_OFFSET(2): | ||
| 1673 | case ELF_GR_OFFSET(3): | ||
| 1674 | ptr = (void *)&pt->r2 + (addr - ELF_GR_OFFSET(2)); | ||
| 1675 | break; | ||
| 1676 | case ELF_GR_OFFSET(4) ... ELF_GR_OFFSET(7): | ||
| 1677 | if (write_access) { | ||
| 1678 | /* read NaT bit first: */ | ||
| 1679 | unsigned long dummy; | ||
| 1680 | |||
| 1681 | ret = unw_get_gr(info, addr/8, &dummy, &nat); | ||
| 1682 | if (ret < 0) | ||
| 1683 | return ret; | ||
| 1684 | } | ||
| 1685 | return unw_access_gr(info, addr/8, data, &nat, write_access); | ||
| 1686 | case ELF_GR_OFFSET(8) ... ELF_GR_OFFSET(11): | ||
| 1687 | ptr = (void *)&pt->r8 + addr - ELF_GR_OFFSET(8); | ||
| 1688 | break; | ||
| 1689 | case ELF_GR_OFFSET(12): | ||
| 1690 | case ELF_GR_OFFSET(13): | ||
| 1691 | ptr = (void *)&pt->r12 + addr - ELF_GR_OFFSET(12); | ||
| 1692 | break; | ||
| 1693 | case ELF_GR_OFFSET(14): | ||
| 1694 | ptr = &pt->r14; | ||
| 1695 | break; | ||
| 1696 | case ELF_GR_OFFSET(15): | ||
| 1697 | ptr = &pt->r15; | ||
| 1698 | } | ||
| 1699 | if (write_access) | ||
| 1700 | *ptr = *data; | ||
| 1701 | else | ||
| 1702 | *data = *ptr; | ||
| 1703 | return 0; | ||
| 1704 | } | ||
| 1705 | |||
| 1706 | static int | ||
| 1707 | access_elf_breg(struct task_struct *target, struct unw_frame_info *info, | ||
| 1708 | unsigned long addr, unsigned long *data, int write_access) | ||
| 1709 | { | ||
| 1710 | struct pt_regs *pt; | ||
| 1711 | unsigned long *ptr = NULL; | ||
| 1712 | |||
| 1713 | pt = task_pt_regs(target); | ||
| 1714 | switch (addr) { | ||
| 1715 | case ELF_BR_OFFSET(0): | ||
| 1716 | ptr = &pt->b0; | ||
| 1717 | break; | ||
| 1718 | case ELF_BR_OFFSET(1) ... ELF_BR_OFFSET(5): | ||
| 1719 | return unw_access_br(info, (addr - ELF_BR_OFFSET(0))/8, | ||
| 1720 | data, write_access); | ||
| 1721 | case ELF_BR_OFFSET(6): | ||
| 1722 | ptr = &pt->b6; | ||
| 1723 | break; | ||
| 1724 | case ELF_BR_OFFSET(7): | ||
| 1725 | ptr = &pt->b7; | ||
| 1726 | } | ||
| 1727 | if (write_access) | ||
| 1728 | *ptr = *data; | ||
| 1729 | else | ||
| 1730 | *data = *ptr; | ||
| 1731 | return 0; | ||
| 1732 | } | ||
| 1733 | |||
| 1734 | static int | ||
| 1735 | access_elf_areg(struct task_struct *target, struct unw_frame_info *info, | ||
| 1736 | unsigned long addr, unsigned long *data, int write_access) | ||
| 1737 | { | ||
| 1738 | struct pt_regs *pt; | ||
| 1739 | unsigned long cfm, urbs_end; | ||
| 1740 | unsigned long *ptr = NULL; | ||
| 1741 | |||
| 1742 | pt = task_pt_regs(target); | ||
| 1743 | if (addr >= ELF_AR_RSC_OFFSET && addr <= ELF_AR_SSD_OFFSET) { | ||
| 1744 | switch (addr) { | ||
| 1745 | case ELF_AR_RSC_OFFSET: | ||
| 1746 | /* force PL3 */ | ||
| 1747 | if (write_access) | ||
| 1748 | pt->ar_rsc = *data | (3 << 2); | ||
| 1749 | else | ||
| 1750 | *data = pt->ar_rsc; | ||
| 1751 | return 0; | ||
| 1752 | case ELF_AR_BSP_OFFSET: | ||
| 1753 | /* | ||
| 1754 | * By convention, we use PT_AR_BSP to refer to | ||
| 1755 | * the end of the user-level backing store. | ||
| 1756 | * Use ia64_rse_skip_regs(PT_AR_BSP, -CFM.sof) | ||
| 1757 | * to get the real value of ar.bsp at the time | ||
| 1758 | * the kernel was entered. | ||
| 1759 | * | ||
| 1760 | * Furthermore, when changing the contents of | ||
| 1761 | * PT_AR_BSP (or PT_CFM) while the task is | ||
| 1762 | * blocked in a system call, convert the state | ||
| 1763 | * so that the non-system-call exit | ||
| 1764 | * path is used. This ensures that the proper | ||
| 1765 | * state will be picked up when resuming | ||
| 1766 | * execution. However, it *also* means that | ||
| 1767 | * once we write PT_AR_BSP/PT_CFM, it won't be | ||
| 1768 | * possible to modify the syscall arguments of | ||
| 1769 | * the pending system call any longer. This | ||
| 1770 | * shouldn't be an issue because modifying | ||
| 1771 | * PT_AR_BSP/PT_CFM generally implies that | ||
| 1772 | * we're either abandoning the pending system | ||
| 1773 | * call or that we defer it's re-execution | ||
| 1774 | * (e.g., due to GDB doing an inferior | ||
| 1775 | * function call). | ||
| 1776 | */ | ||
| 1777 | urbs_end = ia64_get_user_rbs_end(target, pt, &cfm); | ||
| 1778 | if (write_access) { | ||
| 1779 | if (*data != urbs_end) { | ||
| 1780 | if (in_syscall(pt)) | ||
| 1781 | convert_to_non_syscall(target, | ||
| 1782 | pt, | ||
| 1783 | cfm); | ||
| 1784 | /* | ||
| 1785 | * Simulate user-level write | ||
| 1786 | * of ar.bsp: | ||
| 1787 | */ | ||
| 1788 | pt->loadrs = 0; | ||
| 1789 | pt->ar_bspstore = *data; | ||
| 1790 | } | ||
| 1791 | } else | ||
| 1792 | *data = urbs_end; | ||
| 1793 | return 0; | ||
| 1794 | case ELF_AR_BSPSTORE_OFFSET: | ||
| 1795 | ptr = &pt->ar_bspstore; | ||
| 1796 | break; | ||
| 1797 | case ELF_AR_RNAT_OFFSET: | ||
| 1798 | ptr = &pt->ar_rnat; | ||
| 1799 | break; | ||
| 1800 | case ELF_AR_CCV_OFFSET: | ||
| 1801 | ptr = &pt->ar_ccv; | ||
| 1802 | break; | ||
| 1803 | case ELF_AR_UNAT_OFFSET: | ||
| 1804 | ptr = &pt->ar_unat; | ||
| 1805 | break; | ||
| 1806 | case ELF_AR_FPSR_OFFSET: | ||
| 1807 | ptr = &pt->ar_fpsr; | ||
| 1808 | break; | ||
| 1809 | case ELF_AR_PFS_OFFSET: | ||
| 1810 | ptr = &pt->ar_pfs; | ||
| 1811 | break; | ||
| 1812 | case ELF_AR_LC_OFFSET: | ||
| 1813 | return unw_access_ar(info, UNW_AR_LC, data, | ||
| 1814 | write_access); | ||
| 1815 | case ELF_AR_EC_OFFSET: | ||
| 1816 | return unw_access_ar(info, UNW_AR_EC, data, | ||
| 1817 | write_access); | ||
| 1818 | case ELF_AR_CSD_OFFSET: | ||
| 1819 | ptr = &pt->ar_csd; | ||
| 1820 | break; | ||
| 1821 | case ELF_AR_SSD_OFFSET: | ||
| 1822 | ptr = &pt->ar_ssd; | ||
| 1823 | } | ||
| 1824 | } else if (addr >= ELF_CR_IIP_OFFSET && addr <= ELF_CR_IPSR_OFFSET) { | ||
| 1825 | switch (addr) { | ||
| 1826 | case ELF_CR_IIP_OFFSET: | ||
| 1827 | ptr = &pt->cr_iip; | ||
| 1828 | break; | ||
| 1829 | case ELF_CFM_OFFSET: | ||
| 1830 | urbs_end = ia64_get_user_rbs_end(target, pt, &cfm); | ||
| 1831 | if (write_access) { | ||
| 1832 | if (((cfm ^ *data) & PFM_MASK) != 0) { | ||
| 1833 | if (in_syscall(pt)) | ||
| 1834 | convert_to_non_syscall(target, | ||
| 1835 | pt, | ||
| 1836 | cfm); | ||
| 1837 | pt->cr_ifs = ((pt->cr_ifs & ~PFM_MASK) | ||
| 1838 | | (*data & PFM_MASK)); | ||
| 1839 | } | ||
| 1840 | } else | ||
| 1841 | *data = cfm; | ||
| 1842 | return 0; | ||
| 1843 | case ELF_CR_IPSR_OFFSET: | ||
| 1844 | if (write_access) { | ||
| 1845 | unsigned long tmp = *data; | ||
| 1846 | /* psr.ri==3 is a reserved value: SDM 2:25 */ | ||
| 1847 | if ((tmp & IA64_PSR_RI) == IA64_PSR_RI) | ||
| 1848 | tmp &= ~IA64_PSR_RI; | ||
| 1849 | pt->cr_ipsr = ((tmp & IPSR_MASK) | ||
| 1850 | | (pt->cr_ipsr & ~IPSR_MASK)); | ||
| 1851 | } else | ||
| 1852 | *data = (pt->cr_ipsr & IPSR_MASK); | ||
| 1853 | return 0; | ||
| 1854 | } | ||
| 1855 | } else if (addr == ELF_NAT_OFFSET) | ||
| 1856 | return access_nat_bits(target, pt, info, | ||
| 1857 | data, write_access); | ||
| 1858 | else if (addr == ELF_PR_OFFSET) | ||
| 1859 | ptr = &pt->pr; | ||
| 1860 | else | ||
| 1861 | return -1; | ||
| 1862 | |||
| 1863 | if (write_access) | ||
| 1864 | *ptr = *data; | ||
| 1865 | else | ||
| 1866 | *data = *ptr; | ||
| 1867 | |||
| 1868 | return 0; | ||
| 1869 | } | ||
| 1870 | |||
| 1871 | static int | ||
| 1872 | access_elf_reg(struct task_struct *target, struct unw_frame_info *info, | ||
| 1873 | unsigned long addr, unsigned long *data, int write_access) | ||
| 1874 | { | ||
| 1875 | if (addr >= ELF_GR_OFFSET(1) && addr <= ELF_GR_OFFSET(15)) | ||
| 1876 | return access_elf_gpreg(target, info, addr, data, write_access); | ||
| 1877 | else if (addr >= ELF_BR_OFFSET(0) && addr <= ELF_BR_OFFSET(7)) | ||
| 1878 | return access_elf_breg(target, info, addr, data, write_access); | ||
| 1879 | else | ||
| 1880 | return access_elf_areg(target, info, addr, data, write_access); | ||
| 1881 | } | ||
| 1882 | |||
| 1883 | void do_gpregs_get(struct unw_frame_info *info, void *arg) | ||
| 1884 | { | ||
| 1885 | struct pt_regs *pt; | ||
| 1886 | struct regset_getset *dst = arg; | ||
| 1887 | elf_greg_t tmp[16]; | ||
| 1888 | unsigned int i, index, min_copy; | ||
| 1889 | |||
| 1890 | if (unw_unwind_to_user(info) < 0) | ||
| 1891 | return; | ||
| 1892 | |||
| 1893 | /* | ||
| 1894 | * coredump format: | ||
| 1895 | * r0-r31 | ||
| 1896 | * NaT bits (for r0-r31; bit N == 1 iff rN is a NaT) | ||
| 1897 | * predicate registers (p0-p63) | ||
| 1898 | * b0-b7 | ||
| 1899 | * ip cfm user-mask | ||
| 1900 | * ar.rsc ar.bsp ar.bspstore ar.rnat | ||
| 1901 | * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec | ||
| 1902 | */ | ||
| 1903 | |||
| 1904 | |||
| 1905 | /* Skip r0 */ | ||
| 1906 | if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) { | ||
| 1907 | dst->ret = user_regset_copyout_zero(&dst->pos, &dst->count, | ||
| 1908 | &dst->u.get.kbuf, | ||
| 1909 | &dst->u.get.ubuf, | ||
| 1910 | 0, ELF_GR_OFFSET(1)); | ||
| 1911 | if (dst->ret || dst->count == 0) | ||
| 1912 | return; | ||
| 1913 | } | ||
| 1914 | |||
| 1915 | /* gr1 - gr15 */ | ||
| 1916 | if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) { | ||
| 1917 | index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t); | ||
| 1918 | min_copy = ELF_GR_OFFSET(16) > (dst->pos + dst->count) ? | ||
| 1919 | (dst->pos + dst->count) : ELF_GR_OFFSET(16); | ||
| 1920 | for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), | ||
| 1921 | index++) | ||
| 1922 | if (access_elf_reg(dst->target, info, i, | ||
| 1923 | &tmp[index], 0) < 0) { | ||
| 1924 | dst->ret = -EIO; | ||
| 1925 | return; | ||
| 1926 | } | ||
| 1927 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
| 1928 | &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, | ||
| 1929 | ELF_GR_OFFSET(1), ELF_GR_OFFSET(16)); | ||
| 1930 | if (dst->ret || dst->count == 0) | ||
| 1931 | return; | ||
| 1932 | } | ||
| 1933 | |||
| 1934 | /* r16-r31 */ | ||
| 1935 | if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) { | ||
| 1936 | pt = task_pt_regs(dst->target); | ||
| 1937 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
| 1938 | &dst->u.get.kbuf, &dst->u.get.ubuf, &pt->r16, | ||
| 1939 | ELF_GR_OFFSET(16), ELF_NAT_OFFSET); | ||
| 1940 | if (dst->ret || dst->count == 0) | ||
| 1941 | return; | ||
| 1942 | } | ||
| 1943 | |||
| 1944 | /* nat, pr, b0 - b7 */ | ||
| 1945 | if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) { | ||
| 1946 | index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t); | ||
| 1947 | min_copy = ELF_CR_IIP_OFFSET > (dst->pos + dst->count) ? | ||
| 1948 | (dst->pos + dst->count) : ELF_CR_IIP_OFFSET; | ||
| 1949 | for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), | ||
| 1950 | index++) | ||
| 1951 | if (access_elf_reg(dst->target, info, i, | ||
| 1952 | &tmp[index], 0) < 0) { | ||
| 1953 | dst->ret = -EIO; | ||
| 1954 | return; | ||
| 1955 | } | ||
| 1956 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
| 1957 | &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, | ||
| 1958 | ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET); | ||
| 1959 | if (dst->ret || dst->count == 0) | ||
| 1960 | return; | ||
| 1961 | } | ||
| 1962 | |||
| 1963 | /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat | ||
| 1964 | * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd | ||
| 1965 | */ | ||
| 1966 | if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) { | ||
| 1967 | index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t); | ||
| 1968 | min_copy = ELF_AR_END_OFFSET > (dst->pos + dst->count) ? | ||
| 1969 | (dst->pos + dst->count) : ELF_AR_END_OFFSET; | ||
| 1970 | for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), | ||
| 1971 | index++) | ||
| 1972 | if (access_elf_reg(dst->target, info, i, | ||
| 1973 | &tmp[index], 0) < 0) { | ||
| 1974 | dst->ret = -EIO; | ||
| 1975 | return; | ||
| 1976 | } | ||
| 1977 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
| 1978 | &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, | ||
| 1979 | ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET); | ||
| 1980 | } | ||
| 1981 | } | ||
| 1982 | |||
| 1983 | void do_gpregs_set(struct unw_frame_info *info, void *arg) | ||
| 1984 | { | ||
| 1985 | struct pt_regs *pt; | ||
| 1986 | struct regset_getset *dst = arg; | ||
| 1987 | elf_greg_t tmp[16]; | ||
| 1988 | unsigned int i, index; | ||
| 1989 | |||
| 1990 | if (unw_unwind_to_user(info) < 0) | ||
| 1991 | return; | ||
| 1992 | |||
| 1993 | /* Skip r0 */ | ||
| 1994 | if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) { | ||
| 1995 | dst->ret = user_regset_copyin_ignore(&dst->pos, &dst->count, | ||
| 1996 | &dst->u.set.kbuf, | ||
| 1997 | &dst->u.set.ubuf, | ||
| 1998 | 0, ELF_GR_OFFSET(1)); | ||
| 1999 | if (dst->ret || dst->count == 0) | ||
| 2000 | return; | ||
| 2001 | } | ||
| 2002 | |||
| 2003 | /* gr1-gr15 */ | ||
| 2004 | if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) { | ||
| 2005 | i = dst->pos; | ||
| 2006 | index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t); | ||
| 2007 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 2008 | &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, | ||
| 2009 | ELF_GR_OFFSET(1), ELF_GR_OFFSET(16)); | ||
| 2010 | if (dst->ret) | ||
| 2011 | return; | ||
| 2012 | for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++) | ||
| 2013 | if (access_elf_reg(dst->target, info, i, | ||
| 2014 | &tmp[index], 1) < 0) { | ||
| 2015 | dst->ret = -EIO; | ||
| 2016 | return; | ||
| 2017 | } | ||
| 2018 | if (dst->count == 0) | ||
| 2019 | return; | ||
| 2020 | } | ||
| 2021 | |||
| 2022 | /* gr16-gr31 */ | ||
| 2023 | if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) { | ||
| 2024 | pt = task_pt_regs(dst->target); | ||
| 2025 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 2026 | &dst->u.set.kbuf, &dst->u.set.ubuf, &pt->r16, | ||
| 2027 | ELF_GR_OFFSET(16), ELF_NAT_OFFSET); | ||
| 2028 | if (dst->ret || dst->count == 0) | ||
| 2029 | return; | ||
| 2030 | } | ||
| 2031 | |||
| 2032 | /* nat, pr, b0 - b7 */ | ||
| 2033 | if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) { | ||
| 2034 | i = dst->pos; | ||
| 2035 | index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t); | ||
| 2036 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 2037 | &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, | ||
| 2038 | ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET); | ||
| 2039 | if (dst->ret) | ||
| 2040 | return; | ||
| 2041 | for (; i < dst->pos; i += sizeof(elf_greg_t), index++) | ||
| 2042 | if (access_elf_reg(dst->target, info, i, | ||
| 2043 | &tmp[index], 1) < 0) { | ||
| 2044 | dst->ret = -EIO; | ||
| 2045 | return; | ||
| 2046 | } | ||
| 2047 | if (dst->count == 0) | ||
| 2048 | return; | ||
| 2049 | } | ||
| 2050 | |||
| 2051 | /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat | ||
| 2052 | * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd | ||
| 2053 | */ | ||
| 2054 | if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) { | ||
| 2055 | i = dst->pos; | ||
| 2056 | index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t); | ||
| 2057 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 2058 | &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, | ||
| 2059 | ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET); | ||
| 2060 | if (dst->ret) | ||
| 2061 | return; | ||
| 2062 | for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++) | ||
| 2063 | if (access_elf_reg(dst->target, info, i, | ||
| 2064 | &tmp[index], 1) < 0) { | ||
| 2065 | dst->ret = -EIO; | ||
| 2066 | return; | ||
| 2067 | } | ||
| 2068 | } | ||
| 2069 | } | ||
| 2070 | |||
| 2071 | #define ELF_FP_OFFSET(i) (i * sizeof(elf_fpreg_t)) | ||
| 2072 | |||
| 2073 | void do_fpregs_get(struct unw_frame_info *info, void *arg) | ||
| 2074 | { | ||
| 2075 | struct regset_getset *dst = arg; | ||
| 2076 | struct task_struct *task = dst->target; | ||
| 2077 | elf_fpreg_t tmp[30]; | ||
| 2078 | int index, min_copy, i; | ||
| 2079 | |||
| 2080 | if (unw_unwind_to_user(info) < 0) | ||
| 2081 | return; | ||
| 2082 | |||
| 2083 | /* Skip pos 0 and 1 */ | ||
| 2084 | if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(2)) { | ||
| 2085 | dst->ret = user_regset_copyout_zero(&dst->pos, &dst->count, | ||
| 2086 | &dst->u.get.kbuf, | ||
| 2087 | &dst->u.get.ubuf, | ||
| 2088 | 0, ELF_FP_OFFSET(2)); | ||
| 2089 | if (dst->count == 0 || dst->ret) | ||
| 2090 | return; | ||
| 2091 | } | ||
| 2092 | |||
| 2093 | /* fr2-fr31 */ | ||
| 2094 | if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(32)) { | ||
| 2095 | index = (dst->pos - ELF_FP_OFFSET(2)) / sizeof(elf_fpreg_t); | ||
| 2096 | |||
| 2097 | min_copy = min(((unsigned int)ELF_FP_OFFSET(32)), | ||
| 2098 | dst->pos + dst->count); | ||
| 2099 | for (i = dst->pos; i < min_copy; i += sizeof(elf_fpreg_t), | ||
| 2100 | index++) | ||
| 2101 | if (unw_get_fr(info, i / sizeof(elf_fpreg_t), | ||
| 2102 | &tmp[index])) { | ||
| 2103 | dst->ret = -EIO; | ||
| 2104 | return; | ||
| 2105 | } | ||
| 2106 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
| 2107 | &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, | ||
| 2108 | ELF_FP_OFFSET(2), ELF_FP_OFFSET(32)); | ||
| 2109 | if (dst->count == 0 || dst->ret) | ||
| 2110 | return; | ||
| 2111 | } | ||
| 2112 | |||
| 2113 | /* fph */ | ||
| 2114 | if (dst->count > 0) { | ||
| 2115 | ia64_flush_fph(dst->target); | ||
| 2116 | if (task->thread.flags & IA64_THREAD_FPH_VALID) | ||
| 2117 | dst->ret = user_regset_copyout( | ||
| 2118 | &dst->pos, &dst->count, | ||
| 2119 | &dst->u.get.kbuf, &dst->u.get.ubuf, | ||
| 2120 | &dst->target->thread.fph, | ||
| 2121 | ELF_FP_OFFSET(32), -1); | ||
| 2122 | else | ||
| 2123 | /* Zero fill instead. */ | ||
| 2124 | dst->ret = user_regset_copyout_zero( | ||
| 2125 | &dst->pos, &dst->count, | ||
| 2126 | &dst->u.get.kbuf, &dst->u.get.ubuf, | ||
| 2127 | ELF_FP_OFFSET(32), -1); | ||
| 2128 | } | ||
| 2129 | } | ||
| 2130 | |||
| 2131 | void do_fpregs_set(struct unw_frame_info *info, void *arg) | ||
| 2132 | { | ||
| 2133 | struct regset_getset *dst = arg; | ||
| 2134 | elf_fpreg_t fpreg, tmp[30]; | ||
| 2135 | int index, start, end; | ||
| 2136 | |||
| 2137 | if (unw_unwind_to_user(info) < 0) | ||
| 2138 | return; | ||
| 2139 | |||
| 2140 | /* Skip pos 0 and 1 */ | ||
| 2141 | if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(2)) { | ||
| 2142 | dst->ret = user_regset_copyin_ignore(&dst->pos, &dst->count, | ||
| 2143 | &dst->u.set.kbuf, | ||
| 2144 | &dst->u.set.ubuf, | ||
| 2145 | 0, ELF_FP_OFFSET(2)); | ||
| 2146 | if (dst->count == 0 || dst->ret) | ||
| 2147 | return; | ||
| 2148 | } | ||
| 2149 | |||
| 2150 | /* fr2-fr31 */ | ||
| 2151 | if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(32)) { | ||
| 2152 | start = dst->pos; | ||
| 2153 | end = min(((unsigned int)ELF_FP_OFFSET(32)), | ||
| 2154 | dst->pos + dst->count); | ||
| 2155 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 2156 | &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, | ||
| 2157 | ELF_FP_OFFSET(2), ELF_FP_OFFSET(32)); | ||
| 2158 | if (dst->ret) | ||
| 2159 | return; | ||
| 2160 | |||
| 2161 | if (start & 0xF) { /* only write high part */ | ||
| 2162 | if (unw_get_fr(info, start / sizeof(elf_fpreg_t), | ||
| 2163 | &fpreg)) { | ||
| 2164 | dst->ret = -EIO; | ||
| 2165 | return; | ||
| 2166 | } | ||
| 2167 | tmp[start / sizeof(elf_fpreg_t) - 2].u.bits[0] | ||
| 2168 | = fpreg.u.bits[0]; | ||
| 2169 | start &= ~0xFUL; | ||
| 2170 | } | ||
| 2171 | if (end & 0xF) { /* only write low part */ | ||
| 2172 | if (unw_get_fr(info, end / sizeof(elf_fpreg_t), | ||
| 2173 | &fpreg)) { | ||
| 2174 | dst->ret = -EIO; | ||
| 2175 | return; | ||
| 2176 | } | ||
| 2177 | tmp[end / sizeof(elf_fpreg_t) - 2].u.bits[1] | ||
| 2178 | = fpreg.u.bits[1]; | ||
| 2179 | end = (end + 0xF) & ~0xFUL; | ||
| 2180 | } | ||
| 2181 | |||
| 2182 | for ( ; start < end ; start += sizeof(elf_fpreg_t)) { | ||
| 2183 | index = start / sizeof(elf_fpreg_t); | ||
| 2184 | if (unw_set_fr(info, index, tmp[index - 2])) { | ||
| 2185 | dst->ret = -EIO; | ||
| 2186 | return; | ||
| 2187 | } | ||
| 2188 | } | ||
| 2189 | if (dst->ret || dst->count == 0) | ||
| 2190 | return; | ||
| 2191 | } | ||
| 2192 | |||
| 2193 | /* fph */ | ||
| 2194 | if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(128)) { | ||
| 2195 | ia64_sync_fph(dst->target); | ||
| 2196 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 2197 | &dst->u.set.kbuf, | ||
| 2198 | &dst->u.set.ubuf, | ||
| 2199 | &dst->target->thread.fph, | ||
| 2200 | ELF_FP_OFFSET(32), -1); | ||
| 2201 | } | ||
| 2202 | } | ||
| 2203 | |||
| 2204 | static int | ||
| 2205 | do_regset_call(void (*call)(struct unw_frame_info *, void *), | ||
| 2206 | struct task_struct *target, | ||
| 2207 | const struct user_regset *regset, | ||
| 2208 | unsigned int pos, unsigned int count, | ||
| 2209 | const void *kbuf, const void __user *ubuf) | ||
| 2210 | { | ||
| 2211 | struct regset_getset info = { .target = target, .regset = regset, | ||
| 2212 | .pos = pos, .count = count, | ||
| 2213 | .u.set = { .kbuf = kbuf, .ubuf = ubuf }, | ||
| 2214 | .ret = 0 }; | ||
| 2215 | |||
| 2216 | if (target == current) | ||
| 2217 | unw_init_running(call, &info); | ||
| 2218 | else { | ||
| 2219 | struct unw_frame_info ufi; | ||
| 2220 | memset(&ufi, 0, sizeof(ufi)); | ||
| 2221 | unw_init_from_blocked_task(&ufi, target); | ||
| 2222 | (*call)(&ufi, &info); | ||
| 2223 | } | ||
| 2224 | |||
| 2225 | return info.ret; | ||
| 2226 | } | ||
| 2227 | |||
| 2228 | static int | ||
| 2229 | gpregs_get(struct task_struct *target, | ||
| 2230 | const struct user_regset *regset, | ||
| 2231 | unsigned int pos, unsigned int count, | ||
| 2232 | void *kbuf, void __user *ubuf) | ||
| 2233 | { | ||
| 2234 | return do_regset_call(do_gpregs_get, target, regset, pos, count, | ||
| 2235 | kbuf, ubuf); | ||
| 2236 | } | ||
| 2237 | |||
| 2238 | static int gpregs_set(struct task_struct *target, | ||
| 2239 | const struct user_regset *regset, | ||
| 2240 | unsigned int pos, unsigned int count, | ||
| 2241 | const void *kbuf, const void __user *ubuf) | ||
| 2242 | { | ||
| 2243 | return do_regset_call(do_gpregs_set, target, regset, pos, count, | ||
| 2244 | kbuf, ubuf); | ||
| 2245 | } | ||
| 2246 | |||
| 2247 | static void do_gpregs_writeback(struct unw_frame_info *info, void *arg) | ||
| 2248 | { | ||
| 2249 | do_sync_rbs(info, ia64_sync_user_rbs); | ||
| 2250 | } | ||
| 2251 | |||
| 2252 | /* | ||
| 2253 | * This is called to write back the register backing store. | ||
| 2254 | * ptrace does this before it stops, so that a tracer reading the user | ||
| 2255 | * memory after the thread stops will get the current register data. | ||
| 2256 | */ | ||
| 2257 | static int | ||
| 2258 | gpregs_writeback(struct task_struct *target, | ||
| 2259 | const struct user_regset *regset, | ||
| 2260 | int now) | ||
| 2261 | { | ||
| 2262 | if (test_and_set_tsk_thread_flag(target, TIF_RESTORE_RSE)) | ||
| 2263 | return 0; | ||
| 2264 | tsk_set_notify_resume(target); | ||
| 2265 | return do_regset_call(do_gpregs_writeback, target, regset, 0, 0, | ||
| 2266 | NULL, NULL); | ||
| 2267 | } | ||
| 2268 | |||
| 2269 | static int | ||
| 2270 | fpregs_active(struct task_struct *target, const struct user_regset *regset) | ||
| 2271 | { | ||
| 2272 | return (target->thread.flags & IA64_THREAD_FPH_VALID) ? 128 : 32; | ||
| 2273 | } | ||
| 2274 | |||
| 2275 | static int fpregs_get(struct task_struct *target, | ||
| 2276 | const struct user_regset *regset, | ||
| 2277 | unsigned int pos, unsigned int count, | ||
| 2278 | void *kbuf, void __user *ubuf) | ||
| 2279 | { | ||
| 2280 | return do_regset_call(do_fpregs_get, target, regset, pos, count, | ||
| 2281 | kbuf, ubuf); | ||
| 2282 | } | ||
| 2283 | |||
| 2284 | static int fpregs_set(struct task_struct *target, | ||
| 2285 | const struct user_regset *regset, | ||
| 2286 | unsigned int pos, unsigned int count, | ||
| 2287 | const void *kbuf, const void __user *ubuf) | ||
| 2288 | { | ||
| 2289 | return do_regset_call(do_fpregs_set, target, regset, pos, count, | ||
| 2290 | kbuf, ubuf); | ||
| 2291 | } | ||
| 2292 | |||
| 2293 | static const struct user_regset native_regsets[] = { | ||
| 2294 | { | ||
| 2295 | .core_note_type = NT_PRSTATUS, | ||
| 2296 | .n = ELF_NGREG, | ||
| 2297 | .size = sizeof(elf_greg_t), .align = sizeof(elf_greg_t), | ||
| 2298 | .get = gpregs_get, .set = gpregs_set, | ||
| 2299 | .writeback = gpregs_writeback | ||
| 2300 | }, | ||
| 2301 | { | ||
| 2302 | .core_note_type = NT_PRFPREG, | ||
| 2303 | .n = ELF_NFPREG, | ||
| 2304 | .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t), | ||
| 2305 | .get = fpregs_get, .set = fpregs_set, .active = fpregs_active | ||
| 2306 | }, | ||
| 2307 | }; | ||
| 2308 | |||
| 2309 | static const struct user_regset_view user_ia64_view = { | ||
| 2310 | .name = "ia64", | ||
| 2311 | .e_machine = EM_IA_64, | ||
| 2312 | .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets) | ||
| 2313 | }; | ||
| 2314 | |||
| 2315 | const struct user_regset_view *task_user_regset_view(struct task_struct *tsk) | ||
| 2316 | { | ||
| 2317 | return &user_ia64_view; | ||
| 2318 | } | ||
diff --git a/include/asm-ia64/elf.h b/include/asm-ia64/elf.h index f8e83eca67a2..064cf7dcea8e 100644 --- a/include/asm-ia64/elf.h +++ b/include/asm-ia64/elf.h | |||
| @@ -154,6 +154,30 @@ extern void ia64_init_addr_space (void); | |||
| 154 | #define ELF_NGREG 128 /* we really need just 72 but let's leave some headroom... */ | 154 | #define ELF_NGREG 128 /* we really need just 72 but let's leave some headroom... */ |
| 155 | #define ELF_NFPREG 128 /* f0 and f1 could be omitted, but so what... */ | 155 | #define ELF_NFPREG 128 /* f0 and f1 could be omitted, but so what... */ |
| 156 | 156 | ||
| 157 | /* elf_gregset_t register offsets */ | ||
| 158 | #define ELF_GR_0_OFFSET 0 | ||
| 159 | #define ELF_NAT_OFFSET (32 * sizeof(elf_greg_t)) | ||
| 160 | #define ELF_PR_OFFSET (33 * sizeof(elf_greg_t)) | ||
| 161 | #define ELF_BR_0_OFFSET (34 * sizeof(elf_greg_t)) | ||
| 162 | #define ELF_CR_IIP_OFFSET (42 * sizeof(elf_greg_t)) | ||
| 163 | #define ELF_CFM_OFFSET (43 * sizeof(elf_greg_t)) | ||
| 164 | #define ELF_CR_IPSR_OFFSET (44 * sizeof(elf_greg_t)) | ||
| 165 | #define ELF_GR_OFFSET(i) (ELF_GR_0_OFFSET + i * sizeof(elf_greg_t)) | ||
| 166 | #define ELF_BR_OFFSET(i) (ELF_BR_0_OFFSET + i * sizeof(elf_greg_t)) | ||
| 167 | #define ELF_AR_RSC_OFFSET (45 * sizeof(elf_greg_t)) | ||
| 168 | #define ELF_AR_BSP_OFFSET (46 * sizeof(elf_greg_t)) | ||
| 169 | #define ELF_AR_BSPSTORE_OFFSET (47 * sizeof(elf_greg_t)) | ||
| 170 | #define ELF_AR_RNAT_OFFSET (48 * sizeof(elf_greg_t)) | ||
| 171 | #define ELF_AR_CCV_OFFSET (49 * sizeof(elf_greg_t)) | ||
| 172 | #define ELF_AR_UNAT_OFFSET (50 * sizeof(elf_greg_t)) | ||
| 173 | #define ELF_AR_FPSR_OFFSET (51 * sizeof(elf_greg_t)) | ||
| 174 | #define ELF_AR_PFS_OFFSET (52 * sizeof(elf_greg_t)) | ||
| 175 | #define ELF_AR_LC_OFFSET (53 * sizeof(elf_greg_t)) | ||
| 176 | #define ELF_AR_EC_OFFSET (54 * sizeof(elf_greg_t)) | ||
| 177 | #define ELF_AR_CSD_OFFSET (55 * sizeof(elf_greg_t)) | ||
| 178 | #define ELF_AR_SSD_OFFSET (56 * sizeof(elf_greg_t)) | ||
| 179 | #define ELF_AR_END_OFFSET (57 * sizeof(elf_greg_t)) | ||
| 180 | |||
| 157 | typedef unsigned long elf_fpxregset_t; | 181 | typedef unsigned long elf_fpxregset_t; |
| 158 | 182 | ||
| 159 | typedef unsigned long elf_greg_t; | 183 | typedef unsigned long elf_greg_t; |
