Commit 106ec87921a41752777781f073092301d4477567
1 parent
951f1351
initial MIPS signal handling (initial patch by Raphael Rigo)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2031 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
4 changed files
with
376 additions
and
3 deletions
linux-user/main.c
... | ... | @@ -1302,6 +1302,7 @@ void cpu_loop(CPUMIPSState *env) |
1302 | 1302 | case EXCP_SYSCALL: |
1303 | 1303 | { |
1304 | 1304 | syscall_num = env->gpr[2] - 4000; |
1305 | + env->PC += 4; | |
1305 | 1306 | if (syscall_num >= sizeof(mips_syscall_args)) { |
1306 | 1307 | ret = -ENOSYS; |
1307 | 1308 | } else { |
... | ... | @@ -1328,7 +1329,6 @@ void cpu_loop(CPUMIPSState *env) |
1328 | 1329 | arg5, |
1329 | 1330 | arg6); |
1330 | 1331 | } |
1331 | - env->PC += 4; | |
1332 | 1332 | if ((unsigned int)ret >= (unsigned int)(-1133)) { |
1333 | 1333 | env->gpr[7] = 1; /* error flag */ |
1334 | 1334 | ret = -ret; |
... | ... | @@ -1347,6 +1347,9 @@ void cpu_loop(CPUMIPSState *env) |
1347 | 1347 | info.si_code = 0; |
1348 | 1348 | queue_signal(info.si_signo, &info); |
1349 | 1349 | break; |
1350 | + case EXCP_INTERRUPT: | |
1351 | + /* just indicate that signals should be handled asap */ | |
1352 | + break; | |
1350 | 1353 | default: |
1351 | 1354 | // error: |
1352 | 1355 | fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", | ... | ... |
linux-user/signal.c
... | ... | @@ -432,13 +432,17 @@ int do_sigaction(int sig, const struct target_sigaction *act, |
432 | 432 | if (oact) { |
433 | 433 | oact->_sa_handler = tswapl(k->sa._sa_handler); |
434 | 434 | oact->sa_flags = tswapl(k->sa.sa_flags); |
435 | - oact->sa_restorer = tswapl(k->sa.sa_restorer); | |
435 | + #if !defined(TARGET_MIPS) | |
436 | + oact->sa_restorer = tswapl(k->sa.sa_restorer); | |
437 | + #endif | |
436 | 438 | oact->sa_mask = k->sa.sa_mask; |
437 | 439 | } |
438 | 440 | if (act) { |
439 | 441 | k->sa._sa_handler = tswapl(act->_sa_handler); |
440 | 442 | k->sa.sa_flags = tswapl(act->sa_flags); |
441 | - k->sa.sa_restorer = tswapl(act->sa_restorer); | |
443 | + #if !defined(TARGET_MIPS) | |
444 | + k->sa.sa_restorer = tswapl(act->sa_restorer); | |
445 | + #endif | |
442 | 446 | k->sa.sa_mask = act->sa_mask; |
443 | 447 | |
444 | 448 | /* we update the host linux signal state */ |
... | ... | @@ -1618,6 +1622,334 @@ long do_rt_sigreturn(CPUState *env) |
1618 | 1622 | return -ENOSYS; |
1619 | 1623 | } |
1620 | 1624 | |
1625 | +#elif defined(TARGET_MIPS) | |
1626 | + | |
1627 | +struct target_sigcontext { | |
1628 | + uint32_t sc_regmask; /* Unused */ | |
1629 | + uint32_t sc_status; | |
1630 | + uint64_t sc_pc; | |
1631 | + uint64_t sc_regs[32]; | |
1632 | + uint64_t sc_fpregs[32]; | |
1633 | + uint32_t sc_ownedfp; /* Unused */ | |
1634 | + uint32_t sc_fpc_csr; | |
1635 | + uint32_t sc_fpc_eir; /* Unused */ | |
1636 | + uint32_t sc_used_math; | |
1637 | + uint32_t sc_dsp; /* dsp status, was sc_ssflags */ | |
1638 | + uint64_t sc_mdhi; | |
1639 | + uint64_t sc_mdlo; | |
1640 | + target_ulong sc_hi1; /* Was sc_cause */ | |
1641 | + target_ulong sc_lo1; /* Was sc_badvaddr */ | |
1642 | + target_ulong sc_hi2; /* Was sc_sigset[4] */ | |
1643 | + target_ulong sc_lo2; | |
1644 | + target_ulong sc_hi3; | |
1645 | + target_ulong sc_lo3; | |
1646 | +}; | |
1647 | + | |
1648 | +struct sigframe { | |
1649 | + uint32_t sf_ass[4]; /* argument save space for o32 */ | |
1650 | + uint32_t sf_code[2]; /* signal trampoline */ | |
1651 | + struct target_sigcontext sf_sc; | |
1652 | + target_sigset_t sf_mask; | |
1653 | +}; | |
1654 | + | |
1655 | +/* Install trampoline to jump back from signal handler */ | |
1656 | +static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall) | |
1657 | +{ | |
1658 | + int err; | |
1659 | + | |
1660 | + /* | |
1661 | + * Set up the return code ... | |
1662 | + * | |
1663 | + * li v0, __NR__foo_sigreturn | |
1664 | + * syscall | |
1665 | + */ | |
1666 | + | |
1667 | + err = __put_user(0x24020000 + syscall, tramp + 0); | |
1668 | + err |= __put_user(0x0000000c , tramp + 1); | |
1669 | + /* flush_cache_sigtramp((unsigned long) tramp); */ | |
1670 | + return err; | |
1671 | +} | |
1672 | + | |
1673 | +static inline int | |
1674 | +setup_sigcontext(CPUState *regs, struct target_sigcontext *sc) | |
1675 | +{ | |
1676 | + int err = 0; | |
1677 | + | |
1678 | + err |= __put_user(regs->PC, &sc->sc_pc); | |
1679 | + | |
1680 | + #define save_gp_reg(i) do { \ | |
1681 | + err |= __put_user(regs->gpr[i], &sc->sc_regs[i]); \ | |
1682 | + } while(0) | |
1683 | + __put_user(0, &sc->sc_regs[0]); save_gp_reg(1); save_gp_reg(2); | |
1684 | + save_gp_reg(3); save_gp_reg(4); save_gp_reg(5); save_gp_reg(6); | |
1685 | + save_gp_reg(7); save_gp_reg(8); save_gp_reg(9); save_gp_reg(10); | |
1686 | + save_gp_reg(11); save_gp_reg(12); save_gp_reg(13); save_gp_reg(14); | |
1687 | + save_gp_reg(15); save_gp_reg(16); save_gp_reg(17); save_gp_reg(18); | |
1688 | + save_gp_reg(19); save_gp_reg(20); save_gp_reg(21); save_gp_reg(22); | |
1689 | + save_gp_reg(23); save_gp_reg(24); save_gp_reg(25); save_gp_reg(26); | |
1690 | + save_gp_reg(27); save_gp_reg(28); save_gp_reg(29); save_gp_reg(30); | |
1691 | + save_gp_reg(31); | |
1692 | + #undef save_gp_reg | |
1693 | + | |
1694 | + err |= __put_user(regs->HI, &sc->sc_mdhi); | |
1695 | + err |= __put_user(regs->LO, &sc->sc_mdlo); | |
1696 | + | |
1697 | + /* Not used yet, but might be useful if we ever have DSP suppport */ | |
1698 | +#if 0 | |
1699 | + if (cpu_has_dsp) { | |
1700 | + err |= __put_user(mfhi1(), &sc->sc_hi1); | |
1701 | + err |= __put_user(mflo1(), &sc->sc_lo1); | |
1702 | + err |= __put_user(mfhi2(), &sc->sc_hi2); | |
1703 | + err |= __put_user(mflo2(), &sc->sc_lo2); | |
1704 | + err |= __put_user(mfhi3(), &sc->sc_hi3); | |
1705 | + err |= __put_user(mflo3(), &sc->sc_lo3); | |
1706 | + err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp); | |
1707 | + } | |
1708 | + /* same with 64 bit */ | |
1709 | + #ifdef CONFIG_64BIT | |
1710 | + err |= __put_user(regs->hi, &sc->sc_hi[0]); | |
1711 | + err |= __put_user(regs->lo, &sc->sc_lo[0]); | |
1712 | + if (cpu_has_dsp) { | |
1713 | + err |= __put_user(mfhi1(), &sc->sc_hi[1]); | |
1714 | + err |= __put_user(mflo1(), &sc->sc_lo[1]); | |
1715 | + err |= __put_user(mfhi2(), &sc->sc_hi[2]); | |
1716 | + err |= __put_user(mflo2(), &sc->sc_lo[2]); | |
1717 | + err |= __put_user(mfhi3(), &sc->sc_hi[3]); | |
1718 | + err |= __put_user(mflo3(), &sc->sc_lo[3]); | |
1719 | + err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp); | |
1720 | + } | |
1721 | + #endif | |
1722 | + | |
1723 | + | |
1724 | + #endif | |
1725 | + | |
1726 | + | |
1727 | + #if 0 | |
1728 | + err |= __put_user(!!used_math(), &sc->sc_used_math); | |
1729 | + | |
1730 | + if (!used_math()) | |
1731 | + goto out; | |
1732 | + | |
1733 | + /* | |
1734 | + * Save FPU state to signal context. Signal handler will "inherit" | |
1735 | + * current FPU state. | |
1736 | + */ | |
1737 | + preempt_disable(); | |
1738 | + | |
1739 | + if (!is_fpu_owner()) { | |
1740 | + own_fpu(); | |
1741 | + restore_fp(current); | |
1742 | + } | |
1743 | + err |= save_fp_context(sc); | |
1744 | + | |
1745 | + preempt_enable(); | |
1746 | + out: | |
1747 | +#endif | |
1748 | + return err; | |
1749 | +} | |
1750 | + | |
1751 | +static inline int | |
1752 | +restore_sigcontext(CPUState *regs, struct target_sigcontext *sc) | |
1753 | +{ | |
1754 | + int err = 0; | |
1755 | + | |
1756 | + err |= __get_user(regs->CP0_EPC, &sc->sc_pc); | |
1757 | + | |
1758 | + err |= __get_user(regs->HI, &sc->sc_mdhi); | |
1759 | + err |= __get_user(regs->LO, &sc->sc_mdlo); | |
1760 | + | |
1761 | + #define restore_gp_reg(i) do { \ | |
1762 | + err |= __get_user(regs->gpr[i], &sc->sc_regs[i]); \ | |
1763 | + } while(0) | |
1764 | + restore_gp_reg( 1); restore_gp_reg( 2); restore_gp_reg( 3); | |
1765 | + restore_gp_reg( 4); restore_gp_reg( 5); restore_gp_reg( 6); | |
1766 | + restore_gp_reg( 7); restore_gp_reg( 8); restore_gp_reg( 9); | |
1767 | + restore_gp_reg(10); restore_gp_reg(11); restore_gp_reg(12); | |
1768 | + restore_gp_reg(13); restore_gp_reg(14); restore_gp_reg(15); | |
1769 | + restore_gp_reg(16); restore_gp_reg(17); restore_gp_reg(18); | |
1770 | + restore_gp_reg(19); restore_gp_reg(20); restore_gp_reg(21); | |
1771 | + restore_gp_reg(22); restore_gp_reg(23); restore_gp_reg(24); | |
1772 | + restore_gp_reg(25); restore_gp_reg(26); restore_gp_reg(27); | |
1773 | + restore_gp_reg(28); restore_gp_reg(29); restore_gp_reg(30); | |
1774 | + restore_gp_reg(31); | |
1775 | + #undef restore_gp_reg | |
1776 | + | |
1777 | +#if 0 | |
1778 | + if (cpu_has_dsp) { | |
1779 | + err |= __get_user(treg, &sc->sc_hi1); mthi1(treg); | |
1780 | + err |= __get_user(treg, &sc->sc_lo1); mtlo1(treg); | |
1781 | + err |= __get_user(treg, &sc->sc_hi2); mthi2(treg); | |
1782 | + err |= __get_user(treg, &sc->sc_lo2); mtlo2(treg); | |
1783 | + err |= __get_user(treg, &sc->sc_hi3); mthi3(treg); | |
1784 | + err |= __get_user(treg, &sc->sc_lo3); mtlo3(treg); | |
1785 | + err |= __get_user(treg, &sc->sc_dsp); wrdsp(treg, DSP_MASK); | |
1786 | + } | |
1787 | + #ifdef CONFIG_64BIT | |
1788 | + err |= __get_user(regs->hi, &sc->sc_hi[0]); | |
1789 | + err |= __get_user(regs->lo, &sc->sc_lo[0]); | |
1790 | + if (cpu_has_dsp) { | |
1791 | + err |= __get_user(treg, &sc->sc_hi[1]); mthi1(treg); | |
1792 | + err |= __get_user(treg, &sc->sc_lo[1]); mthi1(treg); | |
1793 | + err |= __get_user(treg, &sc->sc_hi[2]); mthi2(treg); | |
1794 | + err |= __get_user(treg, &sc->sc_lo[2]); mthi2(treg); | |
1795 | + err |= __get_user(treg, &sc->sc_hi[3]); mthi3(treg); | |
1796 | + err |= __get_user(treg, &sc->sc_lo[3]); mthi3(treg); | |
1797 | + err |= __get_user(treg, &sc->sc_dsp); wrdsp(treg, DSP_MASK); | |
1798 | + } | |
1799 | + #endif | |
1800 | + | |
1801 | + err |= __get_user(used_math, &sc->sc_used_math); | |
1802 | + conditional_used_math(used_math); | |
1803 | + | |
1804 | + preempt_disable(); | |
1805 | + | |
1806 | + if (used_math()) { | |
1807 | + /* restore fpu context if we have used it before */ | |
1808 | + own_fpu(); | |
1809 | + err |= restore_fp_context(sc); | |
1810 | + } else { | |
1811 | + /* signal handler may have used FPU. Give it up. */ | |
1812 | + lose_fpu(); | |
1813 | + } | |
1814 | + | |
1815 | + preempt_enable(); | |
1816 | +#endif | |
1817 | + return err; | |
1818 | +} | |
1819 | +/* | |
1820 | + * Determine which stack to use.. | |
1821 | + */ | |
1822 | +static inline void * | |
1823 | +get_sigframe(struct emulated_sigaction *ka, CPUState *regs, size_t frame_size) | |
1824 | +{ | |
1825 | + unsigned long sp; | |
1826 | + | |
1827 | + /* Default to using normal stack */ | |
1828 | + sp = regs->gpr[29]; | |
1829 | + | |
1830 | + /* | |
1831 | + * FPU emulator may have it's own trampoline active just | |
1832 | + * above the user stack, 16-bytes before the next lowest | |
1833 | + * 16 byte boundary. Try to avoid trashing it. | |
1834 | + */ | |
1835 | + sp -= 32; | |
1836 | + | |
1837 | +#if 0 | |
1838 | + /* This is the X/Open sanctioned signal stack switching. */ | |
1839 | + if ((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags (sp) == 0)) | |
1840 | + sp = current->sas_ss_sp + current->sas_ss_size; | |
1841 | +#endif | |
1842 | + | |
1843 | + return g2h((sp - frame_size) & ~7); | |
1844 | +} | |
1845 | + | |
1846 | +static void setup_frame(int sig, struct emulated_sigaction * ka, | |
1847 | + target_sigset_t *set, CPUState *regs) | |
1848 | +{ | |
1849 | + struct sigframe *frame; | |
1850 | + int i; | |
1851 | + | |
1852 | + frame = get_sigframe(ka, regs, sizeof(*frame)); | |
1853 | + if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) | |
1854 | + goto give_sigsegv; | |
1855 | + | |
1856 | + install_sigtramp(frame->sf_code, TARGET_NR_sigreturn); | |
1857 | + | |
1858 | + if(setup_sigcontext(regs, &frame->sf_sc)) | |
1859 | + goto give_sigsegv; | |
1860 | + | |
1861 | + for(i = 0; i < TARGET_NSIG_WORDS; i++) { | |
1862 | + if(__put_user(set->sig[i], &frame->sf_mask.sig[i])) | |
1863 | + goto give_sigsegv; | |
1864 | + } | |
1865 | + | |
1866 | + /* | |
1867 | + * Arguments to signal handler: | |
1868 | + * | |
1869 | + * a0 = signal number | |
1870 | + * a1 = 0 (should be cause) | |
1871 | + * a2 = pointer to struct sigcontext | |
1872 | + * | |
1873 | + * $25 and PC point to the signal handler, $29 points to the | |
1874 | + * struct sigframe. | |
1875 | + */ | |
1876 | + regs->gpr[ 4] = sig; | |
1877 | + regs->gpr[ 5] = 0; | |
1878 | + regs->gpr[ 6] = h2g(&frame->sf_sc); | |
1879 | + regs->gpr[29] = h2g(frame); | |
1880 | + regs->gpr[31] = h2g(frame->sf_code); | |
1881 | + /* The original kernel code sets CP0_EPC to the handler | |
1882 | + * since it returns to userland using eret | |
1883 | + * we cannot do this here, and we must set PC directly */ | |
1884 | + regs->PC = regs->gpr[25] = ka->sa._sa_handler; | |
1885 | + return; | |
1886 | + | |
1887 | +give_sigsegv: | |
1888 | + force_sig(TARGET_SIGSEGV/*, current*/); | |
1889 | + return; | |
1890 | +} | |
1891 | + | |
1892 | +long do_sigreturn(CPUState *regs) | |
1893 | +{ | |
1894 | + struct sigframe *frame; | |
1895 | + sigset_t blocked; | |
1896 | + target_sigset_t target_set; | |
1897 | + int i; | |
1898 | + | |
1899 | +#if defined(DEBUG_SIGNAL) | |
1900 | + fprintf(stderr, "do_sigreturn\n"); | |
1901 | +#endif | |
1902 | + frame = (struct sigframe *) regs->gpr[29]; | |
1903 | + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | |
1904 | + goto badframe; | |
1905 | + | |
1906 | + for(i = 0; i < TARGET_NSIG_WORDS; i++) { | |
1907 | + if(__get_user(target_set.sig[i], &frame->sf_mask.sig[i])) | |
1908 | + goto badframe; | |
1909 | + } | |
1910 | + | |
1911 | + target_to_host_sigset_internal(&blocked, &target_set); | |
1912 | + sigprocmask(SIG_SETMASK, &blocked, NULL); | |
1913 | + | |
1914 | + if (restore_sigcontext(regs, &frame->sf_sc)) | |
1915 | + goto badframe; | |
1916 | + | |
1917 | +#if 0 | |
1918 | + /* | |
1919 | + * Don't let your children do this ... | |
1920 | + */ | |
1921 | + __asm__ __volatile__( | |
1922 | + "move\t$29, %0\n\t" | |
1923 | + "j\tsyscall_exit" | |
1924 | + :/* no outputs */ | |
1925 | + :"r" (®s)); | |
1926 | + /* Unreached */ | |
1927 | +#endif | |
1928 | + | |
1929 | + regs->PC = regs->CP0_EPC; | |
1930 | + /* I am not sure this is right, but it seems to work | |
1931 | + * maybe a problem with nested signals ? */ | |
1932 | + regs->CP0_EPC = 0; | |
1933 | + return 0; | |
1934 | + | |
1935 | +badframe: | |
1936 | + force_sig(TARGET_SIGSEGV/*, current*/); | |
1937 | + return 0; | |
1938 | + | |
1939 | +} | |
1940 | + | |
1941 | +static void setup_rt_frame(int sig, struct emulated_sigaction *ka, | |
1942 | + target_siginfo_t *info, | |
1943 | + target_sigset_t *set, CPUState *env) | |
1944 | +{ | |
1945 | + fprintf(stderr, "setup_rt_frame: not implemented\n"); | |
1946 | +} | |
1947 | + | |
1948 | +long do_rt_sigreturn(CPUState *env) | |
1949 | +{ | |
1950 | + fprintf(stderr, "do_rt_sigreturn: not implemented\n"); | |
1951 | + return -ENOSYS; | |
1952 | +} | |
1621 | 1953 | |
1622 | 1954 | #else |
1623 | 1955 | ... | ... |
linux-user/syscall.c
... | ... | @@ -2246,6 +2246,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, |
2246 | 2246 | break; |
2247 | 2247 | case TARGET_NR_sigaction: |
2248 | 2248 | { |
2249 | + #if !defined(TARGET_MIPS) | |
2249 | 2250 | struct target_old_sigaction *old_act; |
2250 | 2251 | struct target_sigaction act, oact, *pact; |
2251 | 2252 | if (arg2) { |
... | ... | @@ -2268,6 +2269,33 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, |
2268 | 2269 | old_act->sa_restorer = oact.sa_restorer; |
2269 | 2270 | unlock_user_struct(old_act, arg3, 1); |
2270 | 2271 | } |
2272 | + #else | |
2273 | + struct target_sigaction act, oact, *pact, *old_act; | |
2274 | + | |
2275 | + if (arg2) { | |
2276 | + lock_user_struct(old_act, arg2, 1); | |
2277 | + act._sa_handler = old_act->_sa_handler; | |
2278 | + target_siginitset(&act.sa_mask, old_act->sa_mask.sig[0]); | |
2279 | + act.sa_flags = old_act->sa_flags; | |
2280 | + unlock_user_struct(old_act, arg2, 0); | |
2281 | + pact = &act; | |
2282 | + } else { | |
2283 | + pact = NULL; | |
2284 | + } | |
2285 | + | |
2286 | + ret = get_errno(do_sigaction(arg1, pact, &oact)); | |
2287 | + | |
2288 | + if (!is_error(ret) && arg3) { | |
2289 | + lock_user_struct(old_act, arg3, 0); | |
2290 | + old_act->_sa_handler = oact._sa_handler; | |
2291 | + old_act->sa_flags = oact.sa_flags; | |
2292 | + old_act->sa_mask.sig[0] = oact.sa_mask.sig[0]; | |
2293 | + old_act->sa_mask.sig[1] = 0; | |
2294 | + old_act->sa_mask.sig[2] = 0; | |
2295 | + old_act->sa_mask.sig[3] = 0; | |
2296 | + unlock_user_struct(old_act, arg3, 1); | |
2297 | + } | |
2298 | + #endif | |
2271 | 2299 | } |
2272 | 2300 | break; |
2273 | 2301 | case TARGET_NR_rt_sigaction: | ... | ... |
linux-user/syscall_defs.h
... | ... | @@ -448,6 +448,15 @@ int do_sigaction(int sig, const struct target_sigaction *act, |
448 | 448 | |
449 | 449 | #endif |
450 | 450 | |
451 | +#if defined(TARGET_MIPS) | |
452 | + | |
453 | +struct target_sigaction { | |
454 | + target_ulong sa_flags; | |
455 | + target_ulong _sa_handler; | |
456 | + target_sigset_t sa_mask; | |
457 | +}; | |
458 | + | |
459 | +#else | |
451 | 460 | struct target_old_sigaction { |
452 | 461 | target_ulong _sa_handler; |
453 | 462 | target_ulong sa_mask; |
... | ... | @@ -461,6 +470,7 @@ struct target_sigaction { |
461 | 470 | target_ulong sa_restorer; |
462 | 471 | target_sigset_t sa_mask; |
463 | 472 | }; |
473 | +#endif | |
464 | 474 | |
465 | 475 | typedef union target_sigval { |
466 | 476 | int sival_int; | ... | ... |