//      TITLE("Runtime Stack Checking")
//++
//
// Copyright (c) 1991  Microsoft Corporation
// Copyright (c) 1992  Digital Equipment Corporation
//
// Module Name:
//
//    chkstk.s
//
// Abstract:
//
//    This module implements runtime stack checking.
//
// Author:
//
//    David N. Cutler (davec) 14-Mar-1991
//
// Environment:
//
//    Any mode.
//
// Revision History:
//
//    Thomas Van Baak (tvb) 7-May-1992
//
//        Adapted for Alpha AXP.
//
//--

#include "ksalpha.h"

        SBTTL("Check Stack")
//++
//
// ULONG
// _RtlCheckStack (
//    IN ULONG Allocation
//    )
//
// Routine Description:
//
//    This function provides runtime stack checking for local allocations
//    that are more than a page and for storage dynamically allocated with
//    the alloca function. Stack checking consists of probing downward in
//    the stack a page at a time. If the current stack commitment is exceeded,
//    then the system will automatically attempts to expand the stack. If the
//    attempt succeeds, then another page is committed. Otherwise, a stack
//    overflow exception is raised. It is the responsibility of the caller to
//    handle this exception.
//
//    N.B. This routine is called using a non-standard calling sequence since
//       it is typically called from within the prologue. The allocation size
//       argument is in register t12 and it must be preserved. Register t11
//       may contain the callers saved ra and it must be preserved. The choice
//       of these registers is hard-coded into the acc C compiler. Register v0
//       may contain a static link pointer (exception handlers) and so it must
//       be preserved. Since this function is called from within the prolog,
//       the a' registers must be preserved, as well as all the s' registers.
//       Registers t8, t9, and t10 are used by this function and are not
//       preserved.
//
//       The typical calling sequence from the prologue is:
//
//           mov   ra, t11              // save return address
//           ldil  t12, SIZE            // set requested stack frame size
//           bsr   ra, _RtlCheckStack   // check stack page allocation
//           subq  sp, t12, sp          // allocate stack frame
//           mov   t11, ra              // restore return address
//
// Arguments:
//
//    Allocation (t12) - Supplies the size of the allocation on the stack.
//
// Return Value:
//
//    None.
//
//--

        LEAF_ENTRY(_RtlCheckStack)

        subq    sp, t12, t8             // compute requested new stack address
        mov     v0, t10                 // save v0 since the PALcode uses it
        bgt     sp, 10f                 // if sp>0, then running on user stack

//
// Running on kernel stack - compute stack limit from initial kernel stack.
//

        GET_INITIAL_KERNEL_STACK        // (PALcode) result in v0

        lda     t9, -KERNEL_STACK_SIZE(v0) // compute low limit of kernel stack
        br      zero, 20f               // finish in common code

//
// Running on user stack - get stack limit from thread environment block.
//

10:     GET_THREAD_ENVIRONMENT_BLOCK    // (PALcode) put TEB address in v0

        ldl     t9, TeStackLimit(v0)    // get low limit of user stack address

//
// The requested bottom of the stack is in t8.
// The current low limit of the stack is in t9.
//
// If the new stack address is greater than the current stack limit, then the
// pages have already been allocated, and nothing further needs to be done.
//

20:     mov     t10, v0                 // restore v0, no further PAL calls
        cmpult  t8, t9, t10             // t8<t9? new stack base within limit?
        beq     t10, 40f                // if eq [false], then t8>=t9, so yes

        ldil    t10, ~(PAGE_SIZE - 1)   // round down new stack address
        and     t8, t10, t8             //   to next page boundary

//
// Go down one page, touch one quadword in it, and repeat until we reach the
// new stack limit.
//

30:     lda     t9, -PAGE_SIZE(t9)      // compute next address to check
        stq     zero, 0(t9)             // probe stack address with a write
        cmpeq   t8, t9, t10             // t8=t9? at the low limit yet?
        beq     t10, 30b                // if eq [false], more pages to probe

40:     ret     zero, (ra)              // return

        .end    _RtlCheckStack
