TITLE   "Runtime Stack Checking"
;++
;
; Copyright (c) 2000 Microsoft Corporation
;
; Module Name:
;
;   chkstk.s
;
; Abstract:
;
;   This module implements runtime stack checking.
;
; Author:
;
;   David N. Cutler (davec) 20-Oct-2000
;
; Environment:
;
;   Any mode.
;
;--

include ksamd64.inc

        subttl  "Check Stack"
;++
;
; ULONG64
; __chkstk (
;     VOID
;     )
;
; 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 rax and it must be preserved. Registers r10
;        and r11 used by this function and are not preserved.
;
;        The typical calling sequence from the prologue is:
;
;        mov    rax, allocation-size    ; set requested stack frame size
;        call   __chkstk                ; check stack page allocation
;        sub    rsp, rax                ; allocate stack frame
;
; Arguments:
;
;   None.
;
; Implicit Arguments:
;
;   Allocation (rax) - Supplies the size of the allocation on the stack.
;
; Return Value:
;
;   The allocation size is returned as the function value.
;
;--

        LEAF_ENTRY __chkstk, _TEXT$00

ifdef NTOS_KERNEL_RUNTIME

;
; Kernel components should never allocate more than 512 bytes on the kernel
; stack.
;

if DBG

        cmp     rax, 512                ; check if less than 512 bytes
        jbe     short cs05              ; if be, less than 512 bytes
        int     3                       ; break into debugger

endif

cs05:   ret                             ; return

else

        lea     r10, 8[rsp]             ; compute requested stack address
        sub     r10, rax                ;

;
; 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.
;

        mov     r11, gs:[TeStackLimit]  ; get current stack limit
        cmp     r10, r11                ; check if stack within limits
        jae     short cs20              ; if ae, stack within limits

;
; The new stack address is not within the currently allocated stack. Probe
; pages downward in the stack until all pages have been allocated or a stack
; overflow occurs in which case an exception will be raised.
;

        and     r10w, not (PAGE_SIZE - 1) ; round down new stack address
cs10:   lea     r11, (-PAGE_SIZE)[r11]  ; get next lower page address
        mov     byte ptr [r11], 0       ; probe stack address
        cmp     r10, r11                ; check if end of probe range
        jne     short cs10              ; if ne, not end of probe range
cs20:   ret                             ; return

endif

        LEAF_END __chkstk, _TEXT$00

        end