Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

679 lines
19 KiB

/*[
c_stack.c
LOCAL CHAR SccsID[]="@(#)c_stack.c 1.14 03/03/95";
Stack (and related SP/BP access) Support.
-----------------------------------------
]*/
#include <insignia.h>
#include <host_def.h>
#include <xt.h>
#include <c_main.h>
#include <c_addr.h>
#include <c_bsic.h>
#include <c_prot.h>
#include <c_seg.h>
#include <c_stack.h>
#include <c_xcptn.h>
#include <c_reg.h>
#include <c_page.h>
#include <c_tlb.h>
#include <fault.h>
#include <ccpupig.h>
#if defined(PIG) && !defined(PROD)
/* The Soft486 CPU may (when not constrained SAFE_PUSH) corrupt the unwritten
* parts of, say, an interrupt fram which contains 16-bit items pushed into
* 32-bit allocated slots. This defines makes the Pigger blind to these locations.
*/
#define PIG_DONT_CHECK_OTHER_WORD_ON_STACK
#endif /* PIG && !PROD */
/*
=====================================================================
EXTERNAL ROUTINES STARTS HERE.
=====================================================================
*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Semi-intelligent support for Incrementing/Decrementing the Stack */
/* Pointer(E)SP. */
/* Alters ESP or SP depending on StackAddrSize. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
byte_change_SP
IFN1(
IS32, delta
)
{
if ( GET_SS_AR_X() == USE32 ) /* look at SS 'B' bit */
SET_ESP(GET_ESP() + delta);
else
SET_SP(GET_SP() + delta);
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Intelligent support for Incrementing/Decrementing the Stack */
/* Pointer(E)SP by either words or double words items depending on */
/* OperandSize. */
/* Alters ESP or SP depending on StackAddrSize. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
change_SP
IFN1(
IS32, items
)
{
if ( GET_OPERAND_SIZE() == USE16 )
items = items * 2;
else /* USE32 */
items = items * 4;
if ( GET_SS_AR_X() == USE32 ) /* look at SS 'B' bit */
SET_ESP(GET_ESP() + items);
else
SET_SP(GET_SP() + items);
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Intelligent support for Reading the Frame Pointer(E)BP. */
/* Returns either EBP or BP depending on StackAddrSize. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL IU32
get_current_BP()
{
if ( GET_SS_AR_X() == USE32 ) /* look at SS 'B' bit */
return GET_EBP();
return GET_BP();
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Intelligent support for Reading the Stack Pointer(E)SP. */
/* Returns either ESP or SP depending on StackAddrSize. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL IU32
GetStackPointer()
{
if ( GET_SS_AR_X() == USE32 ) /* look at SS 'B' bit */
return GET_ESP();
return GET_SP();
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Intelligent support for Writing the Frame Pointer (E)BP. */
/* Writes EBP or BP depending on StackAddrSize */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
set_current_BP
IFN1(
IU32, new_bp
)
{
if ( GET_SS_AR_X() == USE32 )
SET_EBP(new_bp);
else /* USE16 */
SET_BP(new_bp);
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Intelligent support for Writing the Stack Pointer (E)SP. */
/* Writes ESP or SP depending on StackAddrSize */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
set_current_SP
IFN1(
IU32, new_sp
)
{
if ( GET_SS_AR_X() == USE32 )
SET_ESP(new_sp);
else /* USE16 */
SET_SP(new_sp);
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Pop word or double word from the stack. */
/* Used by instructions which implicitly reference the stack. */
/* Stack Checking MUST have been completed earlier. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL IU32
spop()
{
IU32 addr; /* stack address */
IU32 val;
/*
Intel describe the algorithm as:-
if ( StackAddrSize == 16 )
if ( OperandSize == 16 )
val <- SS:[SP] // 2-byte
SP = SP + 2
else // OperandSize == 32
val <- SS:[SP] // 4-byte
SP = SP + 4
else // StackAddrSize == 32
if ( OperandSize == 16 )
val <- SS:[ESP] // 2-byte
ESP = ESP + 2
else // OperandSize == 32
val <- SS:[ESP] // 4-byte
ESP = ESP + 4
We achieve the same effect by calling 'intelligent' SP functions
which take account of the StackAddrSize, and concentrate here on
the OperandSize.
*/
addr = GET_SS_BASE() + GetStackPointer();
if ( GET_OPERAND_SIZE() == USE16 )
{
val = (IU32)vir_read_word(addr, NO_PHYSICAL_MAPPING);
byte_change_SP((IS32)2);
}
else /* USE32 */
{
val = (IU32)vir_read_dword(addr, NO_PHYSICAL_MAPPING);
byte_change_SP((IS32)4);
}
return val;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Push word or double word onto the stack. */
/* Used by instructions which implicitly reference the stack. */
/* Stack Checking MUST have been completed earlier. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
spush
IFN1(
IU32, data /* value to be pushed */
)
{
IU32 addr; /* stack address */
/*
Intel describe the algorithm as:-
if ( StackAddrSize == 16 )
if ( OperandSize == 16 )
SP = SP - 2
SS:[SP] <- val // 2-byte
else // OperandSize == 32
SP = SP - 4
SS:[SP] <- val // 4-byte
else // StackAddrSize == 32
if ( OperandSize == 16 )
ESP = ESP - 2
SS:[ESP] <- val // 2-byte
else // OperandSize == 32
ESP = ESP - 4
SS:[ESP] <- val // 4-byte
We achieve the same effect by calling 'intelligent' SP functions
which take account of the StackAddrSize, and concentrate here on
the OperandSize.
*/
if ( GET_OPERAND_SIZE() == USE16 )
{
/* push word */
byte_change_SP((IS32)-2);
addr = GET_SS_BASE() + GetStackPointer();
vir_write_word(addr, NO_PHYSICAL_MAPPING, (IU16)data);
}
else /* USE32 */
{
/* push double word */
byte_change_SP((IS32)-4);
addr = GET_SS_BASE() + GetStackPointer();
vir_write_dword(addr, NO_PHYSICAL_MAPPING, (IU32)data);
}
}
#ifdef PIG
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Push word or double word onto the stack. */
/* Used by instructions which implicitly reference the stack. */
/* Stack Checking MUST have been completed earlier. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
spush_flags
IFN1(
IU32, data /* value to be pushed */
)
{
IU32 addr; /* stack address */
/*
* see comment for spush().
*/
if ( GET_OPERAND_SIZE() == USE16 )
{
/* push word */
byte_change_SP((IS32)-2);
addr = GET_SS_BASE() + GetStackPointer();
vir_write_word(addr, NO_PHYSICAL_MAPPING, (IU16)data);
/*
* record the address at which we may not know the flags value
* -- we will examine PigIgnoreFlags when the EDL CPU runs to check
* if there's a problem.
*/
record_flags_addr(addr);
}
else /* USE32 */
{
/* push double word */
byte_change_SP((IS32)-4);
addr = GET_SS_BASE() + GetStackPointer();
vir_write_dword(addr, NO_PHYSICAL_MAPPING, (IU32)data);
/*
* no need to record word at addr+2 as the flags are always known for this word
*/
record_flags_addr(addr);
}
}
#endif /* PIG */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Push word onto the stack. */
/* Operand size of 32 will still push 16 bits of data, but the stack */
/* pointer is adjusted by 4. */
/* Used by PUSH segment-register */
/* Stack Checking MUST have been completed earlier. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
spush16
IFN1(
IU32, data /* value to be pushed */
)
{
IU32 addr; /* stack address */
if ( GET_OPERAND_SIZE() == USE16 )
{
/* stack item size is word */
byte_change_SP((IS32)-2);
addr = GET_SS_BASE() + GetStackPointer();
vir_write_word(addr, NO_PHYSICAL_MAPPING, (IU16)data);
}
else /* USE32 */
{
/* stack item size is double word */
byte_change_SP((IS32)-4);
addr = GET_SS_BASE() + GetStackPointer();
vir_write_word(addr, NO_PHYSICAL_MAPPING, (IU16)data);
#ifdef PIG_DONT_CHECK_OTHER_WORD_ON_STACK
cannot_vir_write_word(addr+2, NO_PHYSICAL_MAPPING, 0x0000);
#endif /* PIG_DONT_CHECK_OTHER_WORD_ON_STACK */
}
}
#ifdef PIG
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Push word onto the stack. */
/* Operand size of 32 will still push 16 bits of data, but the stack */
/* pointer is adjusted by 4. */
/* Used by PUSH segment-register */
/* Stack Checking MUST have been completed earlier. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
spush16_flags
IFN1(
IU32, data /* value to be pushed */
)
{
IU32 addr; /* stack address */
if ( GET_OPERAND_SIZE() == USE16 )
{
/* stack item size is word */
byte_change_SP((IS32)-2);
addr = GET_SS_BASE() + GetStackPointer();
vir_write_word(addr, NO_PHYSICAL_MAPPING, (IU16)data);
record_flags_addr(addr);
}
else /* USE32 */
{
/* stack item size is double word */
byte_change_SP((IS32)-4);
addr = GET_SS_BASE() + GetStackPointer();
vir_write_word(addr, NO_PHYSICAL_MAPPING, (IU16)data);
record_flags_addr(addr);
#ifdef PIG_DONT_CHECK_OTHER_WORD_ON_STACK
cannot_vir_write_word(addr+2, NO_PHYSICAL_MAPPING, 0x0000);
#endif /* PIG_DONT_CHECK_OTHER_WORD_ON_STACK */
}
}
#endif /* PIG */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Temporary Pop word or double word from the stack. */
/* (E)SP is not changed by this instruction. */
/* Used by instructions which implicitly reference the stack. */
/* Stack Checking MUST have been completed earlier. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL IU32
tpop
IFN2(
LIN_ADDR, item_offset, /* item offset(from stack top) to required item */
LIN_ADDR, byte_offset /* byte offset (additional to item_offset) */
)
{
IU32 addr; /* stack address */
IS32 offset; /* total offset from stack top */
IU32 val;
if ( GET_OPERAND_SIZE() == USE16 )
offset = item_offset * 2 + byte_offset;
else /* USE32 */
offset = item_offset * 4 + byte_offset;
/* calculate offset address in 32/16bit arithmetic */
addr = GetStackPointer() + offset;
if ( GET_SS_AR_X() == 0 ) /* look at SS 'B' bit */
addr &= WORD_MASK;
/* then add segment address */
addr += GET_SS_BASE();
if ( GET_OPERAND_SIZE() == USE16 )
val = (IU32)vir_read_word(addr, NO_PHYSICAL_MAPPING);
else /* USE32 */
val = (IU32)vir_read_dword(addr, NO_PHYSICAL_MAPPING);
return val;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Check new stack has space for a given number of bytes. */
/* Take #SF(0) if insufficient room on stack (as in 80386 manual) */
/* Take #SF(sel) if insufficient room on stack (as in i486 manual) */
/* Take #PF if page fault. */
/* Stack wrapping is not supported by this routine. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
validate_new_stack_space
IFN4(
LIN_ADDR, nr_items, /* (I) number of items which must exist */
LIN_ADDR, stack_top, /* (I) stack pointer */
CPU_DESCR *, entry, /* (I) pntr to descriptor cache entry for
stack */
IU16, stack_sel /* (I) selector of new stack */
)
{
ISM32 bytes;
IU32 upper;
IU32 offset;
ISM32 i;
/* The 80386 & i486 PRMs disagree on this matter... the EDL i486 CPU matches
the i486 manual - which seems to do the more sensible thing - until an
experiment is done to show which is the correct behaviour, we'll do what
the book says...
*/
#ifdef SPC486
#define XX_error_code stack_sel
#else
#define XX_error_code 0
#endif
if ( GET_OPERAND_SIZE() == USE16 )
bytes = nr_items * 2;
else /* USE32 */
bytes = nr_items * 4;
if ( GET_AR_E(entry->AR) == 0 )
{
/* expand up */
if ( stack_top < bytes || (stack_top - 1) > entry->limit )
SF(XX_error_code, FAULT_VALNEWSPC_SS_LIMIT_16); /* limit check fails */
}
else
{
/* expand down */
if ( GET_AR_X(entry->AR) == USE16 )
upper = 0xffff;
else /* USE32 */
upper = 0xffffffff;
if ( stack_top <= (entry->limit + bytes) ||
(stack_top - 1) > upper )
SF(XX_error_code, FAULT_VALNEWSPC_SS_LIMIT_32); /* limit check fails */
}
/* finally do paging unit checks */
offset = stack_top - bytes;
for ( i = 0; i < nr_items; i++ )
{
if ( GET_OPERAND_SIZE() == USE16 )
{
spr_chk_word(entry->base + offset, PG_W);
offset += 2;
}
else
{
spr_chk_dword(entry->base + offset, PG_W);
offset += 4;
}
}
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Check stack holds a given number of operands. */
/* Take #GP(0) or #SF(0) if insufficient data on stack. */
/* Take #PF if page fault. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
validate_stack_exists
IFN2(
BOOL, use_bp, /* (I) if true use (E)BP not (E)SP to address
stack */
LIN_ADDR, nr_items /* (I) number of operands which must exist on
stack */
)
{
IU32 offset;
ISM32 operand_size;
ISM32 i;
offset = use_bp ? get_current_BP() : GetStackPointer();
if ( GET_OPERAND_SIZE() == USE16 )
operand_size = 2; /* word */
else /* USE32 */
operand_size = 4; /* double word */
/* do access check */
if ( GET_SS_AR_R() == 0 )
{
/* raise exception - something wrong with stack access */
if ( GET_PE() == 0 || GET_VM() == 1 )
GP((IU16)0, FAULT_VALSTACKEX_ACCESS);
else
SF((IU16)0, FAULT_VALSTACKEX_ACCESS);
}
/* do limit check */
limit_check(SS_REG, offset, nr_items, operand_size);
/* finally do paging unit checks */
for ( i = 0; i < nr_items; i++ )
{
if ( operand_size == 2 )
{
(VOID)usr_chk_word(GET_SS_BASE() + offset, PG_R);
}
else
{
(VOID)usr_chk_dword(GET_SS_BASE() + offset, PG_R);
}
offset += operand_size;
if ( GET_SS_AR_X() == 0 ) /* look at SS 'B' bit */
offset &= WORD_MASK; /* apply 16-bit arith if reqd */
}
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Check stack has space for a given number of operands. */
/* Take #GP(0) or #SF(0) if insufficient room on stack. */
/* Take #PF if page fault. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
validate_stack_space
IFN2(
BOOL, use_bp, /* (I) if true use (E)BP not (E)SP to address
stack */
LIN_ADDR, nr_items /* (I) number of items which must exist on
stack */
)
{
IU32 offset;
ISM32 operand_size;
IS32 size;
ISM32 i;
if ( GET_OPERAND_SIZE() == USE16 )
operand_size = 2; /* word */
else /* USE32 */
operand_size = 4; /* double word */
/* calculate (-ve) total data size */
size = nr_items * -operand_size;
/* get current stack base */
offset = use_bp ? get_current_BP() : GetStackPointer();
/* hence form lowest memory address of new data to be pushed */
/* in 32/16bit arithmetic */
offset = offset + size;
if ( GET_SS_AR_X() == 0 ) /* look at SS 'B' bit */
offset &= WORD_MASK;
/* do access check */
if ( GET_SS_AR_W() == 0 )
{
/* raise exception - something wrong with stack access */
if ( GET_PE() == 0 || GET_VM() == 1 )
GP((IU16)0, FAULT_VALSTKSPACE_ACCESS);
else
SF((IU16)0, FAULT_VALSTKSPACE_ACCESS);
}
/* do limit check */
limit_check(SS_REG, offset, nr_items, operand_size);
/* finally do paging unit checks */
for ( i = 0; i < nr_items; i++ )
{
if ( operand_size == 2 )
{
(VOID)usr_chk_word(GET_SS_BASE() + offset, PG_W);
}
else
{
(VOID)usr_chk_dword(GET_SS_BASE() + offset, PG_W);
}
offset += operand_size;
if ( GET_SS_AR_X() == 0 ) /* look at SS 'B' bit */
offset &= WORD_MASK; /* apply 16-bit arith if reqd */
}
}
#ifdef PIG
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Record a (physical) ESP value for later use if PigIgnoreFlags is */
/* set by the EDL CPU after the pigger has run. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL void
record_flags_addr IFN1(LIN_ADDR, lin_addr)
{
IU32 phy_addr;
if ( GET_PG() == 1 )
{
/*
* we ask for supervisor access because the access has
* already been checked by the push. We dont know the
* U/S used then but asking for PG_S will always work.
*/
phy_addr = lin2phy(lin_addr, PG_S | PG_W);
}
else
phy_addr = lin_addr;
/* printf("recording stack flags push @ lin: %x, phy %x\n", lin_addr, phy_addr); */
pig_fault_write(phy_addr, (~ARITH_FLAGS_BITS) & 0xff);
/*
* short-cut - if bottom bits not 0xfff then we can just add 1
* to the physical addr for byte 2. Otherwise we have to recalculate
* the whole address.
*/
if (((phy_addr & 0xfff) != 0xfff) || (GET_PG() == 0))
pig_fault_write(phy_addr + 1, ((~ARITH_FLAGS_BITS) >> 8) & 0xff);
else
{
phy_addr = lin2phy(lin_addr + 1, PG_S | PG_W);
pig_fault_write(phy_addr, ((~ARITH_FLAGS_BITS) >> 8) & 0xff);
}
}
#endif