Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

270 lines
6.5 KiB

/*[
ret.c
LOCAL CHAR SccsID[]="@(#)ret.c 1.9 02/27/95";
RET CPU Functions.
------------------
]*/
#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 <ret.h>
#include <c_xfer.h>
#include <fault.h>
/*
=====================================================================
EXTERNAL ROUTINES STARTS HERE.
=====================================================================
*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Process far RET. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
RETF
IFN1(
IU32, op1 /* Number of bytes to pop from stack.
NB OS2 Rel 2 implies (contrary to Intel doc.) that
imm16(op1) is always a byte quantity! */
)
{
IU16 new_cs; /* The return destination */
IU32 new_ip;
IU32 cs_descr_addr; /* code segment descriptor address */
CPU_DESCR cs_entry; /* code segment descriptor entry */
ISM32 dest_type; /* category for destination */
ISM32 privilege; /* return privilege level */
IU16 new_ss; /* The new stack */
IU32 new_sp;
IU32 ss_descr_addr; /* stack segment descriptor address */
CPU_DESCR ss_entry; /* stack segment descriptor entry */
IS32 stk_inc; /* Stack increment for basic instruction */
ISM32 stk_item; /* Number of items of immediate data */
/* must have CS:(E)IP on the stack */
validate_stack_exists(USE_SP, (ISM32)NR_ITEMS_2);
/* retrieve return destination from stack */
new_ip = tpop(STACK_ITEM_1, NULL_BYTE_OFFSET);
new_cs = tpop(STACK_ITEM_2, NULL_BYTE_OFFSET);
/* force immediate offset to be an item count */
if ( GET_OPERAND_SIZE() == USE16 )
stk_item = op1 / 2;
else /* USE32 */
stk_item = op1 / 4;
if ( GET_PE() == 0 || GET_VM() == 1 )
{
/* Real Mode or V86 Mode */
#ifdef TAKE_REAL_MODE_LIMIT_FAULT
/* do ip limit check */
if ( new_ip > GET_CS_LIMIT() )
GP((IU16)0, FAULT_RETF_RM_CS_LIMIT);
#else /* TAKE_REAL_MODE_LIMIT_FAULT */
/* The Soft486 EDL CPU does not take Real Mode limit failures.
* Since the Ccpu486 is used as a "reference" cpu we wish it
* to behave a C version of the EDL Cpu rather than as a C
* version of a i486.
*/
#endif /* TAKE_REAL_MODE_LIMIT_FAULT */
/* all systems go */
load_CS_cache(new_cs, (IU32)0, (CPU_DESCR *)0);
SET_EIP(new_ip);
stk_inc = NR_ITEMS_2; /* allow for CS:(E)IP */
}
else
{
/* Protected Mode */
/* decode final action and complete stack check */
privilege = GET_SELECTOR_RPL(new_cs);
if ( privilege < GET_CPL() )
{
GP(new_cs, FAULT_RETF_PM_ACCESS); /* you can't get to higher privilege */
}
else if ( privilege == GET_CPL() )
{
dest_type = SAME_LEVEL;
}
else
{
/* going to lower privilege */
/* must have CS:(E)IP, immed bytes, SS:(E)SP on stack */
validate_stack_exists(USE_SP, (ISM32)(NR_ITEMS_4 + stk_item));
dest_type = LOWER_PRIVILEGE;
}
if ( selector_outside_GDT_LDT(new_cs, &cs_descr_addr) )
GP(new_cs, FAULT_RETF_SELECTOR);
/* check type, access and presence of return addr */
/* load descriptor */
read_descriptor_linear(cs_descr_addr, &cs_entry);
/* must be a code segment */
switch ( descriptor_super_type(cs_entry.AR) )
{
case CONFORM_NOREAD_CODE:
case CONFORM_READABLE_CODE:
/* access check requires DPL <= return RPL */
if ( GET_AR_DPL(cs_entry.AR) > privilege )
GP(new_cs, FAULT_RETF_ACCESS_1);
break;
case NONCONFORM_NOREAD_CODE:
case NONCONFORM_READABLE_CODE:
/* access check requires DPL == return RPL */
if ( GET_AR_DPL(cs_entry.AR) != privilege )
GP(new_cs, FAULT_RETF_ACCESS_2);
break;
default:
GP(new_cs, FAULT_RETF_BAD_SEG_TYPE);
}
if ( GET_AR_P(cs_entry.AR) == NOT_PRESENT )
NP(new_cs, FAULT_RETF_CS_NOTPRESENT);
/* action the target */
switch ( dest_type )
{
case SAME_LEVEL:
/* do ip limit checking */
if ( new_ip > cs_entry.limit )
GP((IU16)0, FAULT_RETF_PM_CS_LIMIT_1);
/* ALL SYSTEMS GO */
load_CS_cache(new_cs, cs_descr_addr, &cs_entry);
SET_EIP(new_ip);
stk_inc = NR_ITEMS_2; /* allow for CS:(E)IP */
break;
case LOWER_PRIVILEGE:
/*
==========
SS:SP -> | old IP |
| old CS |
| parm 1 |
| ... |
| parm n |
| old SP |
| old SS |
==========
*/
/* check new stack */
new_ss = tpop(STACK_ITEM_4, (ISM32)op1);
check_SS(new_ss, privilege, &ss_descr_addr, &ss_entry);
/* do ip limit checking */
if ( new_ip > cs_entry.limit )
GP((IU16)0, FAULT_RETF_PM_CS_LIMIT_2);
/* ALL SYSTEMS GO */
SET_CPL(privilege);
load_CS_cache(new_cs, cs_descr_addr, &cs_entry);
SET_EIP(new_ip);
new_sp = tpop(STACK_ITEM_3, (ISM32)op1);
load_SS_cache(new_ss, ss_descr_addr, &ss_entry);
if ( GET_OPERAND_SIZE() == USE16 )
SET_SP(new_sp);
else
SET_ESP(new_sp);
stk_inc = 0;
/* finally re-validate DS and ES segments */
load_data_seg_new_privilege(DS_REG);
load_data_seg_new_privilege(ES_REG);
load_data_seg_new_privilege(FS_REG);
load_data_seg_new_privilege(GS_REG);
break;
}
}
/* finally increment stack pointer */
change_SP(stk_inc);
byte_change_SP((IS32)op1);
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* near return */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
RETN
IFN1(
IU32, op1
)
{
IU32 new_ip;
/* must have ip on stack */
validate_stack_exists(USE_SP, (ISM32)NR_ITEMS_1);
new_ip = tpop(STACK_ITEM_1, NULL_BYTE_OFFSET); /* get ip */
/* do ip limit check */
#ifndef TAKE_REAL_MODE_LIMIT_FAULT
/* The Soft486 EDL CPU does not take Real Mode limit failures.
* Since the Ccpu486 is used as a "reference" cpu we wish it
* to behave a C version of the EDL Cpu rather than as a C
* version of a i486.
*/
if ( GET_PE() == 1 && GET_VM() == 0 )
#endif /* nTAKE_REAL_MODE_LIMIT_FAULT */
{
if ( new_ip > GET_CS_LIMIT() )
GP((IU16)0, FAULT_RETN_CS_LIMIT);
}
/* all systems go */
SET_EIP(new_ip);
change_SP((IS32)NR_ITEMS_1);
if ( op1 )
{
byte_change_SP((IS32)op1);
}
}