//	   TITLE("C-Runtime Stack Checking")
//++
//
// Copyright (c) 1993,1994	IBM Corporation
//
// Module Name:
//
//	 chkstk.s
//
// Abstract:
//
//	 This module implements runtime stack checking.
//
// Author:
//
//	 Mark D. Johnson
//
// Environment:
//
//	 User/Kernel mode.
//
// Revision History:
//
//--

#include <ksppc.h>


	SBTTL("Check Stack")
//++
//
// VOID _RtlCheckStack(in ULONG n_alloc)
//
// 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 attempt to expand the stack. If the
//	 attempt succeeds, then another page is committed. Otherwise, a stack
//	 overflow exception is raised. It is the responsiblity of the caller to
//	 handle this exception.
//
//	 Two entry points are supported.  The first/standard entry point
//	 calls for n_alloc to be passed as single argument (in r.3).  In
//	 the second case, the single argument is the negative of the amount
//	 requested (the amount by which to decrement the stack pointer), and
//	 is passed in r.12.
//
//	 Registers r.0 and r.11 are modified.
//
// Arguments:
//
//	 n_alloc(r.3/r.12) - Supplies the size of the allocation on the stack.
//				With standard entry, passed in r.3.  In alternate
//				entry, passed in r.12.  In the latter case (r.12)
//				the value supplied is the quanitity by which to
//				decrement r.sp (a negative value).
//
// Return Value:
//
//	 None.
//
// Assumptions:
//
//	 The value of Teb_Ptr is provided in r.13.
//
//	 The bottom of stack "bot" (r.11) is multiple of PAGE_SIZE.
//

//
//			low
//		    :
//		|   :	 
//              |       |
//		|	|
//		|.......|<--- r.sp + r.12 - (PAGE_SIZE-1)
//		|   ^	|
//		|   |< -|- - - - - - always < PAGE_SIZE
//		|   v	|
//		+-------+<--- bot - m*PAGE_SIZE
//              |       |
//		|   :	|
//		|	|
//		+-------+<--- r.sp + r.12
//		|	|
//		|	|
//              |       |
//		|   :   |
//		|   :   |
//		|   :   |
//		|   :   |
//		|       |
//		|       |
//		|       |
//		|       |
//		+=======+<--- bottom of stack "bot" (r.11)
//		|       |
//		|       |
//		|       |
//		|   :   |
//		|   :   |
//		|   :   |
//		|       |
//		|       |
//		+-------+<--- r.sp
//		|	|
//		|	|
//		|   :	|
//		|   :
//		|   :	high
//		    :		m is count for add'l pages to commit
//
//
//		m:=max(0,{bot-[(r.sp+r.12)-(PAGE_SIZE-1)]}/PAGE_SIZE)
//		  =max(0,bot-[r.12-(PAGE_SIZE-1)+r.sp])/PAGE_SIZE
//
//	Operands in expression [r.12-(PAGE_SIZE-1)+r.sp) are known upon entry
//	to routine.	This intermediate quantity is calculated early in r.0,
//	behind user/kernel mode test.


	.text
	.align 5

	// want entry for _RtlCheckStack aligned on a 2**5-1 byte boundary so that
	// more commonly used (internal) entry _RtlCheckStack.12 is aligned on
	// 2**5 byte (cache-line) boundary ... and most often used code fits in
	// single cache-line

	or r.11, r.11, r.11;	or r.11, r.11, r.11
	or r.11, r.11, r.11;	or r.11, r.11, r.11

        or r.11, r.11, r.11;    or r.11, r.11, r.11 
	or r.11, r.11, r.11;    or r.11, r.11, r.11

        or r.11, r.11, r.11;    or r.11, r.11, r.11
	or r.11, r.11, r.11;    or r.11, r.11, r.11

        or r.11, r.11, r.11;    or r.11, r.11, r.11
	or r.11, r.11, r.11

	 LEAF_ENTRY(_RtlCheckStack)

	neg	r.12,r.3

	 ALTERNATE_ENTRY(_RtlCheckStack.12)

	cmpwi	r.sp,0			// user or kernel mode?

	// partial comp of num pages to commit moved here for performance

	subi	 r.0,r.12,(PAGE_SIZE-1)
	add	  r.0,r.0,r.sp	    

	bge+	 UsrMode		 // sp >=0 indicates user mode

	// kernel mode, KIPCR is system mode macro to get KiPcr

	KIPCR(r.11)
	lwz	r.11,PcInitialStack(r.11)
	subi	 r.11,r.11,KERNEL_STACK_SIZE
	b	    CommonCode

UsrMode:

	// r.13 contains the TEB pointer

	lwz	  r.11,TeStackLimit(r.13)	// Get low stack address

CommonCode:

	// r.11 contains computed "bot" of stack

	sub	r.0,r.11,r.0	
	srawi.	r.0,r.0,PAGE_SHIFT	// Number pages to add/commit
	blelr				// if <=0, return

	mtctr	r.0

PageLoop:

	// Attempt to commit pages beyond the limit

	lwzu	r.0,-PAGE_SIZE(r.11)
	bdnz	PageLoop


	 LEAF_EXIT(_RtlCheckStack)
