Changeset 4954


Ignore:
Timestamp:
Aug 18, 2006, 8:29:04 AM (18 years ago)
Author:
Gary Byers
Message:

Copy some of the Mach exception code from the PPC version and tweak it
a little; needs more work.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/ccl/lisp-kernel/x86-exceptions.c

    r4859 r4954  
    793793  SIGRETURN(context);
    794794}
     795
     796#ifdef DARWIN
     797#endif
    795798
    796799#ifdef LINUX
     
    14281431  return status;
    14291432}
     1433
     1434#ifdef DARWIN
     1435
     1436#define TCR_FROM_EXCEPTION_PORT(p) ((TCR *)((natural)p))
     1437#define TCR_TO_EXCEPTION_PORT(tcr) ((mach_port_t)((natural)(tcr)))
     1438
     1439pthread_mutex_t _mach_exception_lock, *mach_exception_lock;
     1440
     1441
     1442#define LISP_EXCEPTIONS_HANDLED_MASK \
     1443 (EXC_MASK_SOFTWARE | EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC)
     1444
     1445/* (logcount LISP_EXCEPTIONS_HANDLED_MASK) */
     1446#define NUM_LISP_EXCEPTIONS_HANDLED 4
     1447
     1448typedef struct {
     1449  int foreign_exception_port_count;
     1450  exception_mask_t         masks[NUM_LISP_EXCEPTIONS_HANDLED];
     1451  mach_port_t              ports[NUM_LISP_EXCEPTIONS_HANDLED];
     1452  exception_behavior_t behaviors[NUM_LISP_EXCEPTIONS_HANDLED];
     1453  thread_state_flavor_t  flavors[NUM_LISP_EXCEPTIONS_HANDLED];
     1454} MACH_foreign_exception_state;
     1455
     1456
     1457
     1458
     1459/*
     1460  Mach's exception mechanism works a little better than its signal
     1461  mechanism (and, not incidentally, it gets along with GDB a lot
     1462  better.
     1463
     1464  Initially, we install an exception handler to handle each native
     1465  thread's exceptions.  This process involves creating a distinguished
     1466  thread which listens for kernel exception messages on a set of
     1467  0 or more thread exception ports.  As threads are created, they're
     1468  added to that port set; a thread's exception port is destroyed
     1469  (and therefore removed from the port set) when the thread exits.
     1470
     1471  A few exceptions can be handled directly in the handler thread;
     1472  others require that we resume the user thread (and that the
     1473  exception thread resumes listening for exceptions.)  The user
     1474  thread might eventually want to return to the original context
     1475  (possibly modified somewhat.)
     1476
     1477  As it turns out, the simplest way to force the faulting user
     1478  thread to handle its own exceptions is to do pretty much what
     1479  signal() does: the exception handlng thread sets up a sigcontext
     1480  on the user thread's stack and forces the user thread to resume
     1481  execution as if a signal handler had been called with that
     1482  context as an argument.  We can use a distinguished UUO at a
     1483  distinguished address to do something like sigreturn(); that'll
     1484  have the effect of resuming the user thread's execution in
     1485  the (pseudo-) signal context.
     1486
     1487  Since:
     1488    a) we have miles of code in C and in Lisp that knows how to
     1489    deal with Linux sigcontexts
     1490    b) Linux sigcontexts contain a little more useful information
     1491    (the DAR, DSISR, etc.) than their Darwin counterparts
     1492    c) we have to create a sigcontext ourselves when calling out
     1493    to the user thread: we aren't really generating a signal, just
     1494    leveraging existing signal-handling code.
     1495
     1496  we create a Linux sigcontext struct.
     1497
     1498  Simple ?  Hopefully from the outside it is ...
     1499
     1500  We want the process of passing a thread's own context to it to
     1501  appear to be atomic: in particular, we don't want the GC to suspend
     1502  a thread that's had an exception but has not yet had its user-level
     1503  exception handler called, and we don't want the thread's exception
     1504  context to be modified by a GC while the Mach handler thread is
     1505  copying it around.  On Linux (and on Jaguar), we avoid this issue
     1506  because (a) the kernel sets up the user-level signal handler and
     1507  (b) the signal handler blocks signals (including the signal used
     1508  by the GC to suspend threads) until tcr->xframe is set up.
     1509
     1510  The GC and the Mach server thread therefore contend for the lock
     1511  "mach_exception_lock".  The Mach server thread holds the lock
     1512  when copying exception information between the kernel and the
     1513  user thread; the GC holds this lock during most of its execution
     1514  (delaying exception processing until it can be done without
     1515  GC interference.)
     1516
     1517*/
     1518
     1519#ifdef PPC64
     1520#define C_REDZONE_LEN           320
     1521#define C_STK_ALIGN             32
     1522#else
     1523#define C_REDZONE_LEN           224
     1524#define C_STK_ALIGN             16
     1525#endif
     1526#define C_PARAMSAVE_LEN         64
     1527#define C_LINKAGE_LEN           48
     1528
     1529#define TRUNC_DOWN(a,b,c)  (((((natural)a)-(b))/(c)) * (c))
     1530
     1531void
     1532fatal_mach_error(char *format, ...);
     1533
     1534#define MACH_CHECK_ERROR(context,x) if (x != KERN_SUCCESS) {fatal_mach_error("Mach error while %s : %d", context, x);}
     1535
     1536
     1537void
     1538restore_mach_thread_state(mach_port_t thread, ExceptionInformation *pseudosigcontext)
     1539{
     1540  int i, j;
     1541  kern_return_t kret;
     1542#if WORD_SIZE == 64
     1543  struct mcontext64 *mc = UC_MCONTEXT(pseudosigcontext);
     1544#else
     1545  struct mcontext * mc = UC_MCONTEXT(pseudosigcontext);
     1546#endif
     1547
     1548  /* Set the thread's FP state from the pseudosigcontext */
     1549  kret = thread_set_state(thread,
     1550                          X86_FLOAT_STATE,
     1551                          (thread_state_t)&(mc->fs),
     1552                          X86_FLOAT_STATE_COUNT);
     1553
     1554  MACH_CHECK_ERROR("setting thread FP state", kret);
     1555
     1556  /* The thread'll be as good as new ... */
     1557#ifdef PPC64
     1558  kret = thread_set_state(thread,
     1559                          X86_THREAD_STATE64,
     1560                          (thread_state_t)&(mc->ss),
     1561                          X86_THREAD_STATE64_COUNT);
     1562#else
     1563  kret = thread_set_state(thread,
     1564                          X86_THREAD_STATE32,
     1565                          (thread_state_t)&(mc->ss),
     1566                          X86_THREAD_STATE32_COUNT);
     1567#endif
     1568  MACH_CHECK_ERROR("setting thread state", kret);
     1569
     1570
     1571/* This code runs in the exception handling thread, in response
     1572   to an attempt to execute the UU0 at "pseudo_sigreturn" (e.g.,
     1573   in response to a call to pseudo_sigreturn() from the specified
     1574   user thread.
     1575   Find that context (the user thread's R3 points to it), then
     1576   use that context to set the user thread's state.  When this
     1577   function's caller returns, the Mach kernel will resume the
     1578   user thread.
     1579*/
     1580
     1581kern_return_t
     1582do_pseudo_sigreturn(mach_port_t thread, TCR *tcr)
     1583{
     1584  ExceptionInformation *xp;
     1585
     1586#ifdef DEBUG_MACH_EXCEPTIONS
     1587  fprintf(stderr, "doing pseudo_sigreturn for 0x%x\n",tcr);
     1588#endif
     1589  xp = tcr->pending_exception_context;
     1590  if (xp) {
     1591    tcr->pending_exception_context = NULL;
     1592    tcr->valence = TCR_STATE_LISP;
     1593    restore_mach_thread_state(thread, xp);
     1594    raise_pending_interrupt(tcr);
     1595  } else {
     1596    Bug(NULL, "no xp here!\n");
     1597  }
     1598#ifdef DEBUG_MACH_EXCEPTIONS
     1599  fprintf(stderr, "did pseudo_sigreturn for 0x%x\n",tcr);
     1600#endif
     1601  return KERN_SUCCESS;
     1602
     1603
     1604ExceptionInformation *
     1605create_thread_context_frame(mach_port_t thread,
     1606                            natural *new_stack_top)
     1607{
     1608#ifdef X8664
     1609  x86_thread_state64_t ts;
     1610#else
     1611  x86_thread_state_t ts;
     1612#endif
     1613  mach_msg_type_number_t thread_state_count;
     1614  kern_return_t result;
     1615  int i,j;
     1616  ExceptionInformation *pseudosigcontext;
     1617#ifdef X8664
     1618  struct mcontext64 *mc;
     1619#else
     1620  struct mcontext *mc;
     1621#endif
     1622  natural stackp, backlink;
     1623
     1624#ifdef X8664
     1625  thread_state_count = X86_THREAD_STATE64_COUNT;
     1626  result = thread_get_state(thread,
     1627                            X86_THREAD_STATE64,
     1628                            (thread_state_t)&ts,
     1629                            &thread_state_count);
     1630#else
     1631  thread_state_count = MACHINE_THREAD_STATE_COUNT;
     1632  result = thread_get_state(thread,
     1633                            X86_THREAD_STATE,   /* GPRs, some SPRs  */
     1634                            (thread_state_t)&ts,
     1635                            &thread_state_count);
     1636#endif
     1637 
     1638  if (result != KERN_SUCCESS) {
     1639    get_tcr(true);
     1640    Bug(NULL, "Exception thread can't obtain thread state, Mach result = %d", result);
     1641  }
     1642  stackp = ts.r1;
     1643  backlink = stackp;
     1644  stackp = TRUNC_DOWN(stackp, C_REDZONE_LEN, C_STK_ALIGN);
     1645  stackp -= sizeof(*pseudosigcontext);
     1646  pseudosigcontext = (ExceptionInformation *) ptr_from_lispobj(stackp);
     1647
     1648  stackp = TRUNC_DOWN(stackp, sizeof(*mc), C_STK_ALIGN);
     1649#ifdef X8664
     1650  mc = (struct mcontext64 *) ptr_from_lispobj(stackp);
     1651#else
     1652  mc = (struct mcontext *) ptr_from_lispobj(stackp);
     1653#endif
     1654  bcopy(&ts,&(mc->ss),sizeof(ts));
     1655
     1656  thread_state_count = X86_FLOAT_STATE_COUNT;
     1657  thread_get_state(thread,
     1658                   X86_FLOAT_STATE,
     1659                   (thread_state_t)&(mc->fs),
     1660                   &thread_state_count);
     1661
     1662
     1663#ifdef X8664
     1664  thread_state_count = X86_EXCEPTION_STATE64_COUNT;
     1665#else
     1666  thread_state_count = X86_EXCEPTION_STATE_COUNT;
     1667#endif
     1668  thread_get_state(thread,
     1669#ifdef X8664
     1670                   X86_EXCEPTION_STATE64,
     1671#else
     1672                   X86_EXCEPTION_STATE,
     1673#endif
     1674                   (thread_state_t)&(mc->es),
     1675                   &thread_state_count);
     1676
     1677
     1678  UC_MCONTEXT(pseudosigcontext) = mc;
     1679  stackp = TRUNC_DOWN(stackp, C_PARAMSAVE_LEN, C_STK_ALIGN);
     1680  stackp -= C_LINKAGE_LEN;
     1681  *(natural *)ptr_from_lispobj(stackp) = backlink;
     1682  if (new_stack_top) {
     1683    *new_stack_top = stackp;
     1684  }
     1685  return pseudosigcontext;
     1686}
     1687
     1688/*
     1689  This code sets up the user thread so that it executes a "pseudo-signal
     1690  handler" function when it resumes.  Create a linux sigcontext struct
     1691  on the thread's stack and pass it as an argument to the pseudo-signal
     1692  handler.
     1693
     1694  Things are set up so that the handler "returns to" pseudo_sigreturn(),
     1695  which will restore the thread's context.
     1696
     1697  If the handler invokes code that throws (or otherwise never sigreturn()'s
     1698  to the context), that's fine.
     1699
     1700  Actually, check that: throw (and variants) may need to be careful and
     1701  pop the tcr's xframe list until it's younger than any frame being
     1702  entered.
     1703*/
     1704
     1705int
     1706setup_signal_frame(mach_port_t thread,
     1707                   void *handler_address,
     1708                   int signum,
     1709                   int code,
     1710                   TCR *tcr)
     1711{
     1712#ifdef X8664
     1713  x86_thread_state64_t ts;
     1714#else
     1715  x86_thread_state_t ts;
     1716#endif
     1717  mach_msg_type_number_t thread_state_count;
     1718  ExceptionInformation *pseudosigcontext;
     1719  int i, j;
     1720  kern_return_t result;
     1721  natural stackp;
     1722
     1723#ifdef DEBUG_MACH_EXCEPTIONS
     1724  fprintf(stderr,"Setting up exception handling for 0x%x\n", tcr);
     1725#endif
     1726  pseudosigcontext = create_thread_context_frame(thread, &stackp);
     1727  pseudosigcontext->uc_onstack = 0;
     1728  pseudosigcontext->uc_sigmask = (sigset_t) 0;
     1729  tcr->pending_exception_context = pseudosigcontext;
     1730  tcr->valence = TCR_STATE_EXCEPTION_WAIT;
     1731 
     1732
     1733  /*
     1734     It seems like we've created a  sigcontext on the thread's
     1735     stack.  Set things up so that we call the handler (with appropriate
     1736     args) when the thread's resumed.
     1737  */
     1738
     1739  ts.srr0 = (natural) handler_address;
     1740  ts.srr1 = (int) xpMSR(pseudosigcontext) & ~MSR_FE0_FE1_MASK;
     1741  ts.r1 = stackp;
     1742  ts.r3 = signum;
     1743  ts.r4 = (natural)pseudosigcontext;
     1744  ts.r5 = (natural)tcr;
     1745  ts.lr = (natural)pseudo_sigreturn;
     1746
     1747
     1748#ifdef X8664
     1749  ts.r13 = xpGPR(pseudosigcontext,13);
     1750  thread_set_state(thread,
     1751                   X86_THREAD_STATE64,
     1752                   (thread_state_t)&ts,
     1753                   X86_THREAD_STATE64_COUNT);
     1754#else
     1755  thread_set_state(thread,
     1756                   MACHINE_THREAD_STATE,
     1757                   (thread_state_t)&ts,
     1758                   MACHINE_THREAD_STATE_COUNT);
     1759#endif
     1760#ifdef DEBUG_MACH_EXCEPTIONS
     1761  fprintf(stderr,"Set up exception context for 0x%x at 0x%x\n", tcr, tcr->pending_exception_context);
     1762#endif
     1763  return 0;
     1764}
     1765
     1766
     1767void
     1768pseudo_signal_handler(int signum, ExceptionInformation *context, TCR *tcr)
     1769{
     1770  signal_handler(signum, NULL, context, tcr);
     1771}
     1772
     1773
     1774int
     1775thread_set_fp_exceptions_enabled(mach_port_t thread, Boolean enabled)
     1776{
     1777#ifdef X8664
     1778  x86_thread_state64_t ts;
     1779#else
     1780  x86_thread_state_t ts;
     1781#endif
     1782  mach_msg_type_number_t thread_state_count;
     1783
     1784#ifdef X8664
     1785  thread_state_count = X86_THREAD_STATE64_COUNT;
     1786#else
     1787  thread_state_count = X86_THREAD_STATE_COUNT;
     1788#endif
     1789  thread_get_state(thread,
     1790#ifdef X8664
     1791                   X86_THREAD_STATE64,  /* GPRs, some SPRs  */
     1792#else
     1793                   X86_THREAD_STATE,    /* GPRs, some SPRs  */
     1794#endif
     1795                   (thread_state_t)&ts,
     1796                   &thread_state_count);
     1797  if (enabled) {
     1798    ts.srr1 |= MSR_FE0_FE1_MASK;
     1799  } else {
     1800    ts.srr1 &= ~MSR_FE0_FE1_MASK;
     1801  }
     1802  /*
     1803     Hack-o-rama warning (isn't it about time for such a warning?):
     1804     pthread_kill() seems to want to lose the MSR's FE0/FE1 bits.
     1805     Our handler for lisp's use of pthread_kill() pushes a phony
     1806     lisp frame on the stack and force the context to resume at
     1807     the UUO in enable_fp_exceptions(); the "saveLR" field of that
     1808     lisp frame contains the -real- address that process_interrupt
     1809     should have returned to, and the fact that it's in a lisp
     1810     frame should convince the GC to notice that address if it
     1811     runs in the tiny time window between returning from our
     1812     interrupt handler and ... here.
     1813     If the top frame on the stack is a lisp frame, discard it
     1814     and set ts.srr0 to the saveLR field in that frame.  Otherwise,
     1815     just adjust ts.srr0 to skip over the UUO.
     1816  */
     1817  {
     1818    lisp_frame *tos = (lisp_frame *)ts.r1,
     1819      *next_frame = tos->backlink;
     1820   
     1821    if (tos == (next_frame -1)) {
     1822      ts.srr0 = tos->savelr;
     1823      ts.r1 = (LispObj) next_frame;
     1824    } else {
     1825      ts.srr0 += 4;
     1826    }
     1827  }
     1828  thread_set_state(thread,
     1829#ifdef X8664
     1830                   X86_THREAD_STATE64,  /* GPRs, some SPRs  */
     1831#else
     1832                   X86_THREAD_STATE,    /* GPRs, some SPRs  */
     1833#endif
     1834                   (thread_state_t)&ts,
     1835#ifdef X8664
     1836                   X86_THREAD_STATE64_COUNT
     1837#else
     1838                   X86_THREAD_STATE_COUNT
     1839#endif
     1840                   );
     1841
     1842  return 0;
     1843}
     1844
     1845/*
     1846  This function runs in the exception handling thread.  It's
     1847  called (by this precise name) from the library function "exc_server()"
     1848  when the thread's exception ports are set up.  (exc_server() is called
     1849  via mach_msg_server(), which is a function that waits for and dispatches
     1850  on exception messages from the Mach kernel.)
     1851
     1852  This checks to see if the exception was caused by a pseudo_sigreturn()
     1853  UUO; if so, it arranges for the thread to have its state restored
     1854  from the specified context.
     1855
     1856  Otherwise, it tries to map the exception to a signal number and
     1857  arranges that the thread run a "pseudo signal handler" to handle
     1858  the exception.
     1859
     1860  Some exceptions could and should be handled here directly.
     1861*/
     1862
     1863kern_return_t
     1864catch_exception_raise(mach_port_t exception_port,
     1865                      mach_port_t thread,
     1866                      mach_port_t task,
     1867                      exception_type_t exception,
     1868                      exception_data_t code_vector,
     1869                      mach_msg_type_number_t code_count)
     1870{
     1871  int signum = 0, code = *code_vector, code1;
     1872  TCR *tcr = TCR_FROM_EXCEPTION_PORT(exception_port);
     1873  kern_return_t kret;
     1874
     1875#ifdef DEBUG_MACH_EXCEPTIONS
     1876  fprintf(stderr, "obtaining Mach exception lock in exception thread\n");
     1877#endif
     1878
     1879  if (pthread_mutex_trylock(mach_exception_lock) == 0) {
     1880    if (tcr->flags & (1<<TCR_FLAG_BIT_PENDING_EXCEPTION)) {
     1881      CLR_TCR_FLAG(tcr,TCR_FLAG_BIT_PENDING_EXCEPTION);
     1882    }
     1883    if ((exception == EXC_BAD_INSTRUCTION) &&
     1884        (code_vector[0] == EXC_X86_UNIPL_INST) &&
     1885        (((code1 = code_vector[1]) == (int)pseudo_sigreturn) ||
     1886         (code1 == (int)enable_fp_exceptions) ||
     1887         (code1 == (int)disable_fp_exceptions))) {
     1888      if (code1 == (int)pseudo_sigreturn) {
     1889        kret = do_pseudo_sigreturn(thread, tcr);
     1890#if 0
     1891      fprintf(stderr, "Exception return in 0x%x\n",tcr);
     1892#endif
     1893       
     1894      } else if (code1 == (int)enable_fp_exceptions) {
     1895        kret = thread_set_fp_exceptions_enabled(thread, true);
     1896      } else kret =  thread_set_fp_exceptions_enabled(thread, false);
     1897    } else if (tcr->flags & (1<<TCR_FLAG_BIT_PROPAGATE_EXCEPTION)) {
     1898      CLR_TCR_FLAG(tcr,TCR_FLAG_BIT_PROPAGATE_EXCEPTION);
     1899      kret = 17;
     1900    } else {
     1901      switch (exception) {
     1902      case EXC_BAD_ACCESS:
     1903        signum = SIGSEGV;
     1904        break;
     1905       
     1906      case EXC_BAD_INSTRUCTION:
     1907        signum = SIGILL;
     1908        break;
     1909     
     1910      case EXC_SOFTWARE:
     1911          signum = SIGILL;
     1912        break;
     1913     
     1914      case EXC_ARITHMETIC:
     1915        signum = SIGFPE;
     1916        break;
     1917
     1918      default:
     1919        break;
     1920      }
     1921      if (signum) {
     1922        kret = setup_signal_frame(thread,
     1923                                  (void *)pseudo_signal_handler,
     1924                                  signum,
     1925                                  code,
     1926                                  tcr);
     1927#if 0
     1928      fprintf(stderr, "Setup pseudosignal handling in 0x%x\n",tcr);
     1929#endif
     1930
     1931      } else {
     1932        kret = 17;
     1933      }
     1934    }
     1935#ifdef DEBUG_MACH_EXCEPTIONS
     1936    fprintf(stderr, "releasing Mach exception lock in exception thread\n");
     1937#endif
     1938    pthread_mutex_unlock(mach_exception_lock);
     1939  } else {
     1940    SET_TCR_FLAG(tcr,TCR_FLAG_BIT_PENDING_EXCEPTION);
     1941#if 0
     1942    fprintf(stderr, "deferring pending exception in 0x%x\n", tcr);
     1943#endif
     1944    kret = 0;
     1945  }
     1946  return kret;
     1947}
     1948
     1949
     1950
     1951typedef struct {
     1952  mach_msg_header_t Head;
     1953  /* start of the kernel processed data */
     1954  mach_msg_body_t msgh_body;
     1955  mach_msg_port_descriptor_t thread;
     1956  mach_msg_port_descriptor_t task;
     1957  /* end of the kernel processed data */
     1958  NDR_record_t NDR;
     1959  exception_type_t exception;
     1960  mach_msg_type_number_t codeCnt;
     1961  integer_t code[2];
     1962  mach_msg_trailer_t trailer;
     1963} exceptionRequest;
     1964
     1965
     1966boolean_t
     1967openmcl_exc_server(mach_msg_header_t *in, mach_msg_header_t *out)
     1968{
     1969  static NDR_record_t _NDR = {0};
     1970  kern_return_t handled;
     1971  mig_reply_error_t *reply = (mig_reply_error_t *) out;
     1972  exceptionRequest *req = (exceptionRequest *) in;
     1973
     1974  reply->NDR = _NDR;
     1975
     1976  out->msgh_bits = in->msgh_bits & MACH_MSGH_BITS_REMOTE_MASK;
     1977  out->msgh_remote_port = in->msgh_remote_port;
     1978  out->msgh_size = sizeof(mach_msg_header_t)+(3 * sizeof(unsigned));
     1979  out->msgh_local_port = MACH_PORT_NULL;
     1980  out->msgh_id = in->msgh_id+100;
     1981
     1982  /* Could handle other exception flavors in the range 2401-2403 */
     1983
     1984
     1985  if (in->msgh_id != 2401) {
     1986    reply->RetCode = MIG_BAD_ID;
     1987    return FALSE;
     1988  }
     1989  handled = catch_exception_raise(req->Head.msgh_local_port,
     1990                                  req->thread.name,
     1991                                  req->task.name,
     1992                                  req->exception,
     1993                                  req->code,
     1994                                  req->codeCnt);
     1995  reply->RetCode = handled;
     1996  return TRUE;
     1997}
     1998
     1999/*
     2000  The initial function for an exception-handling thread.
     2001*/
     2002
     2003void *
     2004exception_handler_proc(void *arg)
     2005{
     2006  extern boolean_t exc_server();
     2007  mach_port_t p = TCR_TO_EXCEPTION_PORT(arg);
     2008
     2009  mach_msg_server(openmcl_exc_server, 2048, p, 0);
     2010  /* Should never return. */
     2011  abort();
     2012}
     2013
     2014
     2015
     2016mach_port_t
     2017mach_exception_port_set()
     2018{
     2019  static mach_port_t __exception_port_set = MACH_PORT_NULL;
     2020  kern_return_t kret; 
     2021  if (__exception_port_set == MACH_PORT_NULL) {
     2022    mach_exception_lock = &_mach_exception_lock;
     2023    pthread_mutex_init(mach_exception_lock, NULL);
     2024
     2025    kret = mach_port_allocate(mach_task_self(),
     2026                              MACH_PORT_RIGHT_PORT_SET,
     2027                              &__exception_port_set);
     2028    MACH_CHECK_ERROR("allocating thread exception_ports",kret);
     2029    create_system_thread(0,
     2030                         NULL,
     2031                         exception_handler_proc,
     2032                         (void *)((natural)__exception_port_set));
     2033  }
     2034  return __exception_port_set;
     2035}
     2036
     2037/*
     2038  Setup a new thread to handle those exceptions specified by
     2039  the mask "which".  This involves creating a special Mach
     2040  message port, telling the Mach kernel to send exception
     2041  messages for the calling thread to that port, and setting
     2042  up a handler thread which listens for and responds to
     2043  those messages.
     2044
     2045*/
     2046
     2047/*
     2048  Establish the lisp thread's TCR as its exception port, and determine
     2049  whether any other ports have been established by foreign code for
     2050  exceptions that lisp cares about.
     2051
     2052  If this happens at all, it should happen on return from foreign
     2053  code and on entry to lisp code via a callback.
     2054
     2055  This is a lot of trouble (and overhead) to support Java, or other
     2056  embeddable systems that clobber their caller's thread exception ports.
     2057 
     2058*/
     2059kern_return_t
     2060tcr_establish_exception_port(TCR *tcr, mach_port_t thread)
     2061{
     2062  kern_return_t kret;
     2063  MACH_foreign_exception_state *fxs = (MACH_foreign_exception_state *)tcr->native_thread_info;
     2064  int i;
     2065  unsigned n = NUM_LISP_EXCEPTIONS_HANDLED;
     2066  mach_port_t lisp_port = TCR_TO_EXCEPTION_PORT(tcr), foreign_port;
     2067  exception_mask_t mask = 0;
     2068
     2069  kret = thread_swap_exception_ports(thread,
     2070                                     LISP_EXCEPTIONS_HANDLED_MASK,
     2071                                     lisp_port,
     2072                                     EXCEPTION_DEFAULT,
     2073                                     THREAD_STATE_NONE,
     2074                                     fxs->masks,
     2075                                     &n,
     2076                                     fxs->ports,
     2077                                     fxs->behaviors,
     2078                                     fxs->flavors);
     2079  if (kret == KERN_SUCCESS) {
     2080    fxs->foreign_exception_port_count = n;
     2081    for (i = 0; i < n; i ++) {
     2082      foreign_port = fxs->ports[i];
     2083
     2084      if ((foreign_port != lisp_port) &&
     2085          (foreign_port != MACH_PORT_NULL)) {
     2086        mask |= fxs->masks[i];
     2087      }
     2088    }
     2089    tcr->foreign_exception_status = (int) mask;
     2090  }
     2091  return kret;
     2092}
     2093
     2094kern_return_t
     2095tcr_establish_lisp_exception_port(TCR *tcr)
     2096{
     2097  return tcr_establish_exception_port(tcr, (mach_port_t)((natural)tcr->native_thread_id));
     2098}
     2099
     2100/*
     2101  Do this when calling out to or returning from foreign code, if
     2102  any conflicting foreign exception ports were established when we
     2103  last entered lisp code.
     2104*/
     2105kern_return_t
     2106restore_foreign_exception_ports(TCR *tcr)
     2107{
     2108  exception_mask_t m = (exception_mask_t) tcr->foreign_exception_status;
     2109 
     2110  if (m) {
     2111    MACH_foreign_exception_state *fxs  =
     2112      (MACH_foreign_exception_state *) tcr->native_thread_info;
     2113    int i, n = fxs->foreign_exception_port_count;
     2114    exception_mask_t tm;
     2115
     2116    for (i = 0; i < n; i++) {
     2117      if ((tm = fxs->masks[i]) & m) {
     2118        thread_set_exception_ports((mach_port_t)((natural)tcr->native_thread_id),
     2119                                   tm,
     2120                                   fxs->ports[i],
     2121                                   fxs->behaviors[i],
     2122                                   fxs->flavors[i]);
     2123      }
     2124    }
     2125  }
     2126}
     2127                                   
     2128
     2129/*
     2130  This assumes that a Mach port (to be used as the thread's exception port) whose
     2131  "name" matches the TCR's 32-bit address has already been allocated.
     2132*/
     2133
     2134kern_return_t
     2135setup_mach_exception_handling(TCR *tcr)
     2136{
     2137  mach_port_t
     2138    thread_exception_port = TCR_TO_EXCEPTION_PORT(tcr),
     2139    target_thread = pthread_mach_thread_np((pthread_t)ptr_from_lispobj(tcr->osid)),
     2140    task_self = mach_task_self();
     2141  kern_return_t kret;
     2142
     2143  kret = mach_port_insert_right(task_self,
     2144                                thread_exception_port,
     2145                                thread_exception_port,
     2146                                MACH_MSG_TYPE_MAKE_SEND);
     2147  MACH_CHECK_ERROR("adding send right to exception_port",kret);
     2148
     2149  kret = tcr_establish_exception_port(tcr, (mach_port_t)((natural) tcr->native_thread_id));
     2150  if (kret == KERN_SUCCESS) {
     2151    mach_port_t exception_port_set = mach_exception_port_set();
     2152
     2153    kret = mach_port_move_member(task_self,
     2154                                 thread_exception_port,
     2155                                 exception_port_set);
     2156  }
     2157  return kret;
     2158}
     2159
     2160void
     2161darwin_exception_init(TCR *tcr)
     2162{
     2163  void tcr_monitor_exception_handling(TCR*, Boolean);
     2164  kern_return_t kret;
     2165  MACH_foreign_exception_state *fxs =
     2166    calloc(1, sizeof(MACH_foreign_exception_state));
     2167 
     2168  tcr->native_thread_info = (void *) fxs;
     2169
     2170  if ((kret = setup_mach_exception_handling(tcr))
     2171      != KERN_SUCCESS) {
     2172    fprintf(stderr, "Couldn't setup exception handler - error = %d\n", kret);
     2173    terminate_lisp();
     2174  }
     2175  lisp_global(LISP_EXIT_HOOK) = (LispObj) restore_foreign_exception_ports;
     2176  lisp_global(LISP_RETURN_HOOK) = (LispObj) tcr_establish_lisp_exception_port;
     2177}
     2178
     2179/*
     2180  The tcr is the "name" of the corresponding thread's exception port.
     2181  Destroying the port should remove it from all port sets of which it's
     2182  a member (notably, the exception port set.)
     2183*/
     2184void
     2185darwin_exception_cleanup(TCR *tcr)
     2186{
     2187  void *fxs = tcr->native_thread_info;
     2188  extern Boolean use_mach_exception_handling;
     2189
     2190  if (fxs) {
     2191    tcr->native_thread_info = NULL;
     2192    free(fxs);
     2193  }
     2194  if (use_mach_exception_handling) {
     2195    mach_port_deallocate(mach_task_self(),TCR_TO_EXCEPTION_PORT(tcr));
     2196    mach_port_destroy(mach_task_self(),TCR_TO_EXCEPTION_PORT(tcr));
     2197  }
     2198}
     2199
     2200
     2201Boolean
     2202suspend_mach_thread(mach_port_t mach_thread)
     2203{
     2204  kern_return_t status;
     2205  Boolean aborted = false;
     2206 
     2207  do {
     2208    aborted = false;
     2209    status = thread_suspend(mach_thread);
     2210    if (status == KERN_SUCCESS) {
     2211      status = thread_abort_safely(mach_thread);
     2212      if (status == KERN_SUCCESS) {
     2213        aborted = true;
     2214      } else {
     2215        fprintf(stderr, "abort failed on thread = 0x%x\n",mach_thread);
     2216        thread_resume(mach_thread);
     2217      }
     2218    } else {
     2219      return false;
     2220    }
     2221  } while (! aborted);
     2222  return true;
     2223}
     2224
     2225/*
     2226  Only do this if pthread_kill indicated that the pthread isn't
     2227  listening to signals anymore, as can happen as soon as pthread_exit()
     2228  is called on Darwin.  The thread could still call out to lisp as it
     2229  is exiting, so we need another way to suspend it in this case.
     2230*/
     2231Boolean
     2232mach_suspend_tcr(TCR *tcr)
     2233{
     2234  mach_port_t mach_thread = (mach_port_t)((natural)( tcr->native_thread_id));
     2235  ExceptionInformation *pseudosigcontext;
     2236  Boolean result = false;
     2237 
     2238  result = suspend_mach_thread(mach_thread);
     2239  if (result) {
     2240    pseudosigcontext = create_thread_context_frame(mach_thread, NULL);
     2241    pseudosigcontext->uc_onstack = 0;
     2242    pseudosigcontext->uc_sigmask = (sigset_t) 0;
     2243    tcr->suspend_context = pseudosigcontext;
     2244  }
     2245  return result;
     2246}
     2247
     2248void
     2249mach_resume_tcr(TCR *tcr)
     2250{
     2251  ExceptionInformation *xp;
     2252  mach_port_t mach_thread = (mach_port_t)((natural)(tcr->native_thread_id));
     2253 
     2254  xp = tcr->suspend_context;
     2255#ifdef DEBUG_MACH_EXCEPTIONS
     2256  fprintf(stderr, "resuming TCR 0x%x, pending_exception_context = 0x%x\n",
     2257          tcr, tcr->pending_exception_context);
     2258#endif
     2259  tcr->suspend_context = NULL;
     2260  restore_mach_thread_state(mach_thread, xp);
     2261#ifdef DEBUG_MACH_EXCEPTIONS
     2262  fprintf(stderr, "restored state in TCR 0x%x, pending_exception_context = 0x%x\n",
     2263          tcr, tcr->pending_exception_context);
     2264#endif
     2265  thread_resume(mach_thread);
     2266}
     2267
     2268void
     2269fatal_mach_error(char *format, ...)
     2270{
     2271  va_list args;
     2272  char s[512];
     2273 
     2274
     2275  va_start(args, format);
     2276  vsnprintf(s, sizeof(s),format, args);
     2277  va_end(args);
     2278
     2279  Fatal("Mach error", s);
     2280}
     2281
     2282
     2283
     2284
     2285#endif
Note: See TracChangeset for help on using the changeset viewer.