home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / amiga / os / kludge03.tz / kludge03 / mk74 / user / threads / stack.c < prev    next >
C/C++ Source or Header  |  1992-02-26  |  10KB  |  383 lines

  1. /* 
  2.  * Mach Operating System
  3.  * Copyright (c) 1991,1990 Carnegie Mellon University
  4.  * All Rights Reserved.
  5.  * 
  6.  * Permission to use, copy, modify and distribute this software and its
  7.  * documentation is hereby granted, provided that both the copyright
  8.  * notice and this permission notice appear in all copies of the
  9.  * software, derivative works or modified versions, and any portions
  10.  * thereof, and that both notices appear in supporting documentation.
  11.  * 
  12.  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
  13.  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
  14.  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
  15.  * 
  16.  * Carnegie Mellon requests users of this software to return to
  17.  * 
  18.  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
  19.  *  School of Computer Science
  20.  *  Carnegie Mellon University
  21.  *  Pittsburgh PA 15213-3890
  22.  * 
  23.  * any improvements or extensions that they make and grant Carnegie Mellon
  24.  * the rights to redistribute these changes.
  25.  */
  26. /*
  27.  * HISTORY
  28.  * $Log:    stack.c,v $
  29.  * Revision 2.13  92/01/14  16:48:54  rpd
  30.  *     Fixed addr_range_check to deallocate the object port from vm_region.
  31.  *     [92/01/14            rpd]
  32.  * 
  33.  * Revision 2.12  92/01/03  20:37:10  dbg
  34.  *     Export cthread_stack_size, and use it if non-zero instead of
  35.  *     probing the stack.  Fix error in deallocating unused initial
  36.  *     stack (STACK_GROWTH_UP case).
  37.  *     [91/08/28            dbg]
  38.  * 
  39.  * Revision 2.11  91/07/31  18:39:34  dbg
  40.  *     Fix some bad stack references (stack direction).
  41.  *     [91/07/30  17:36:50  dbg]
  42.  * 
  43.  * Revision 2.10  91/05/14  17:58:49  mrt
  44.  *     Correcting copyright
  45.  * 
  46.  * Revision 2.9  91/02/14  14:21:08  mrt
  47.  *     Added new Mach copyright
  48.  *     [91/02/13  12:41:35  mrt]
  49.  * 
  50.  * Revision 2.8  90/11/05  18:10:46  rpd
  51.  *     Added cproc_stack_base.  Add stack_fork_child().
  52.  *     [90/11/01            rwd]
  53.  * 
  54.  * Revision 2.7  90/11/05  14:37:51  rpd
  55.  *     Fixed addr_range_check for new vm_region semantics.
  56.  *     [90/11/02            rpd]
  57.  * 
  58.  * Revision 2.6  90/10/12  13:07:34  rpd
  59.  *     Deal with positively growing stacks.
  60.  *     [90/10/10            rwd]
  61.  *     Deal with initial user stacks that are not perfectly aligned.
  62.  *     [90/09/26  11:51:46  rwd]
  63.  * 
  64.  *     Leave extra stack page around in case it is needed before we
  65.  *    switch stacks.
  66.  *     [90/09/25            rwd]
  67.  * 
  68.  * Revision 2.5  90/08/07  14:31:46  rpd
  69.  *     Removed RCS keyword nonsense.
  70.  * 
  71.  * Revision 2.4  90/06/02  15:14:18  rpd
  72.  *     Moved cthread_sp to machine-dependent files.
  73.  *     [90/04/24            rpd]
  74.  *     Converted to new IPC.
  75.  *     [90/03/20  20:56:35  rpd]
  76.  * 
  77.  * Revision 2.3  90/01/19  14:37:34  rwd
  78.  *     Move self pointer to top of stack
  79.  *     [89/12/12            rwd]
  80.  * 
  81.  * Revision 2.2  89/12/08  19:49:52  rwd
  82.  *     Back out change from af.
  83.  *     [89/12/08            rwd]
  84.  * 
  85.  * Revision 2.1.1.3  89/12/06  12:54:17  rwd
  86.  *     Gap fix from af
  87.  *     [89/12/06            rwd]
  88.  * 
  89.  * Revision 2.1.1.2  89/11/21  15:01:40  rwd
  90.  *     Add RED_ZONE ifdef.
  91.  *     [89/11/20            rwd]
  92.  * 
  93.  * Revision 2.1.1.1  89/10/24  13:00:44  rwd
  94.  *     Remove conditionals.
  95.  *     [89/10/23            rwd]
  96.  * 
  97.  * Revision 2.1  89/08/03  17:10:05  rwd
  98.  * Created.
  99.  * 
  100.  * 18-Jan-89  David Golub (dbg) at Carnegie-Mellon University
  101.  *    Altered for stand-alone use:
  102.  *    use vm_region to probe for the bottom of the initial thread's
  103.  *    stack.
  104.  *
  105.  *
  106.  * 01-Dec-87  Eric Cooper (ecc) at Carnegie Mellon University
  107.  *    Changed cthread stack allocation to use aligned stacks
  108.  *    and store self pointer at base of stack.
  109.  *    Added inline expansion for cthread_sp() function.
  110.  */
  111. /*
  112.  *     File:     stack.c
  113.  *    Author:    Eric Cooper, Carnegie Mellon University
  114.  *    Date:    Dec, 1987
  115.  *
  116.  *     C Thread stack allocation.
  117.  *
  118.  */
  119.  
  120. #include <cthreads.h>
  121. #include "cthread_internals.h"
  122.  
  123. #define    BYTES_TO_PAGES(b)    (((b) + vm_page_size - 1) / vm_page_size)
  124.  
  125. int cthread_stack_mask;
  126. vm_size_t cthread_stack_size;
  127. private vm_address_t next_stack_base;
  128.  
  129. vm_offset_t cproc_stack_base();    /* forward */
  130.  
  131. /*
  132.  * Set up a stack segment for a thread.
  133.  * Segment has a red zone (invalid page)
  134.  * for early detection of stack overflow.
  135.  * The cproc_self pointer is stored at the top.
  136.  *
  137.  *    --------- (high address)
  138.  *    | self    |
  139.  *    |  ...    |
  140.  *    |    |
  141.  *    | stack    |
  142.  *    |    |
  143.  *    |  ...    |
  144.  *    |    |
  145.  *    ---------
  146.  *    |    |
  147.  *    |invalid|
  148.  *    |    |
  149.  *    --------- (stack base)
  150.  *    --------- (low address)
  151.  *
  152.  * or the reverse, if the stack grows up.
  153.  */
  154.  
  155. private void
  156. setup_stack(p, base)
  157.     register cproc_t p;
  158.     register vm_address_t base;
  159. {
  160.     register kern_return_t r;
  161.  
  162.     p->stack_base = base;
  163.     /*
  164.      * Stack size is segment size minus size of self pointer
  165.      */
  166.     p->stack_size = cthread_stack_size;
  167.     /*
  168.      * Protect red zone.
  169.      */
  170. #ifdef RED_ZONE
  171.     MACH_CALL(vm_protect(mach_task_self(), base + vm_page_size, vm_page_size, FALSE, VM_PROT_NONE), r);
  172. #endif RED_ZONE
  173.     /*
  174.      * Store self pointer.
  175.      */
  176.     *(cproc_t *)&ur_cthread_ptr(base) = p;
  177. }
  178.  
  179. vm_offset_t
  180. addr_range_check(start_addr, end_addr, desired_protection)
  181.     vm_offset_t    start_addr, end_addr;
  182.     vm_prot_t    desired_protection;
  183. {
  184.     register vm_offset_t    addr;
  185.  
  186.     addr = start_addr;
  187.     while (addr < end_addr) {
  188.         vm_offset_t        r_addr;
  189.         vm_size_t        r_size;
  190.         vm_prot_t        r_protection,
  191.                 r_max_protection;
  192.         vm_inherit_t    r_inheritance;
  193.         boolean_t        r_is_shared;
  194.         memory_object_name_t    r_object_name;
  195.         vm_offset_t        r_offset;
  196.         kern_return_t    kr;
  197.  
  198.         r_addr = addr;
  199.         kr = vm_region(mach_task_self(), &r_addr, &r_size,
  200.                &r_protection, &r_max_protection, &r_inheritance,
  201.                &r_is_shared, &r_object_name, &r_offset);
  202.         if ((kr == KERN_SUCCESS) && MACH_PORT_VALID(r_object_name))
  203.         (void) mach_port_deallocate(mach_task_self(), r_object_name);
  204.  
  205.         if ((kr != KERN_SUCCESS) ||
  206.         (r_addr > addr) ||
  207.         ((r_protection & desired_protection) != desired_protection))
  208.         return (0);
  209.         addr = r_addr + r_size;
  210.     }
  211.     return (addr);
  212. }
  213.  
  214. /*
  215.  * Probe for bottom and top of stack.
  216.  * Assume:
  217.  * 1. stack grows DOWN
  218.  * 2. There is an unallocated region below the stack.
  219.  */
  220. void
  221. probe_stack(stack_bottom, stack_top)
  222.     vm_offset_t    *stack_bottom;
  223.     vm_offset_t    *stack_top;
  224. {
  225.     /*
  226.      * Since vm_region returns the region starting at
  227.      * or ABOVE the given address, we cannot use it
  228.      * directly to search downwards.  However, we
  229.      * also want a size that is the closest power of
  230.      * 2 to the stack size (so we can mask off the stack
  231.      * address and get the stack base).  So we probe
  232.      * in increasing powers of 2 until we find a gap
  233.      * in the stack.
  234.      */
  235.     vm_offset_t    start_addr, end_addr;
  236.     vm_offset_t    last_start_addr, last_end_addr;
  237.     vm_size_t    stack_size;
  238.  
  239.     /*
  240.      * Start with a page
  241.      */
  242.     start_addr = cthread_sp() & ~(vm_page_size - 1);
  243.     end_addr   = start_addr + vm_page_size;
  244.  
  245.     stack_size = vm_page_size;
  246.  
  247.     /*
  248.      * Increase the tentative stack size, by doubling each
  249.      * time, until we have exceeded the stack (some of the
  250.      * range is not valid).
  251.      */
  252.     do {
  253.         /*
  254.          * Save last addresses
  255.          */
  256.         last_start_addr = start_addr;
  257.         last_end_addr   = end_addr;
  258.  
  259.         /*
  260.          * Double the stack size
  261.          */
  262.         stack_size <<= 1;
  263.         start_addr = end_addr - stack_size;
  264.  
  265.         /*
  266.          * Check that the entire range exists and is writable
  267.          */
  268.     } while (end_addr = (addr_range_check(start_addr,
  269.                   end_addr,
  270.                   VM_PROT_READ|VM_PROT_WRITE)));
  271.     /*
  272.      * Back off to previous power of 2.
  273.      */
  274.     *stack_bottom = last_start_addr;
  275.     *stack_top = last_end_addr;
  276. }
  277.  
  278. vm_offset_t
  279. stack_init(p)
  280.     cproc_t p;
  281. {
  282.     vm_offset_t    stack_bottom,
  283.             stack_top,
  284.             start;
  285.     vm_size_t    size;
  286.     kern_return_t    r;
  287.  
  288.     void alloc_stack();
  289.  
  290.     /*
  291.      * Probe for bottom and top of stack, as a power-of-2 size.
  292.      */
  293.     probe_stack(&stack_bottom, &stack_top);
  294.  
  295.     /*
  296.      * Use the stack size found for the Cthread stack size,
  297.      * if not already specified.
  298.      */
  299.     if (cthread_stack_size == 0)
  300.         cthread_stack_size = stack_top - stack_bottom;
  301. #ifdef    STACK_GROWTH_UP
  302.     cthread_stack_mask = ~(cthread_stack_size - 1);
  303. #else    STACK_GROWTH_UP
  304.     cthread_stack_mask = cthread_stack_size - 1;
  305. #endif    STACK_GROWTH_UP
  306.  
  307.     /*
  308.      * Guess at first available region for stack.
  309.      */
  310.     next_stack_base = 0;
  311.  
  312.     /*
  313.      * Set up stack for main thread.
  314.      */
  315.     alloc_stack(p);
  316.  
  317.     /*
  318.      * Delete rest of old stack.
  319.      */
  320.  
  321. #ifdef    STACK_GROWTH_UP
  322.     start = (cthread_sp() | (vm_page_size - 1)) + 1 + vm_page_size;
  323.     size = stack_top - start;
  324. #else    STACK_GROWTH_UP
  325.     start = stack_bottom;
  326.     size = (cthread_sp() & ~(vm_page_size - 1)) - stack_bottom - 
  327.            vm_page_size;
  328. #endif    STACK_GROWTH_UP
  329.     MACH_CALL(vm_deallocate(mach_task_self(),start,size),r);
  330.  
  331.     /*
  332.      * Return new stack; it gets passed back to the caller
  333.      * of cthread_init who must switch to it.
  334.      */
  335.     return cproc_stack_base(p, sizeof(ur_cthread_t *));
  336. }
  337.  
  338. /*
  339.  * Allocate a stack segment for a thread.
  340.  * Stacks are never deallocated.
  341.  *
  342.  * The variable next_stack_base is used to align stacks.
  343.  * It may be updated by several threads in parallel,
  344.  * but mutual exclusion is unnecessary: at worst,
  345.  * the vm_allocate will fail and the thread will try again.
  346.  */
  347.  
  348. void
  349. alloc_stack(p)
  350.     cproc_t p;
  351. {
  352.     vm_address_t base = next_stack_base;
  353.  
  354.     for (base = next_stack_base;
  355.          vm_allocate(mach_task_self(), &base, cthread_stack_size, FALSE) != KERN_SUCCESS;
  356.          base += cthread_stack_size)
  357.         ;
  358.     next_stack_base = base + cthread_stack_size;
  359.     setup_stack(p, base);
  360. }
  361.  
  362. vm_offset_t
  363. cproc_stack_base(cproc, offset)
  364.     register cproc_t cproc;
  365.     register int offset;
  366. {
  367. #ifdef    STACK_GROWTH_UP
  368.     return (cproc->stack_base + offset);
  369. #else    STACK_GROWTH_UP
  370.     return (cproc->stack_base + cproc->stack_size - offset);
  371. #endif    STACK_GROWTH_UP
  372.  
  373. }
  374.  
  375. void stack_fork_child()
  376. /*
  377.  * Called in the child after a fork().  Resets stack data structures to
  378.  * coincide with the reality that we now have a single cproc and cthread.
  379.  */
  380. {
  381.     next_stack_base = 0;
  382. }
  383.