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.
 
 
 
 
 
 

416 lines
11 KiB

/*[
c_xfer.c
LOCAL CHAR SccsID[]="@(#)c_xfer.c 1.14 02/17/95";
Transfer of Control 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_xfer.h>
#include <c_page.h>
#include <fault.h>
/*
Prototype our internal functions.
*/
LOCAL VOID read_call_gate
IPT5(
IU32, descr_addr,
ISM32, super,
IU16 *, selector,
IU32 *, offset,
IU8 *, count
);
IMPORT IBOOL took_relative_jump;
/*
=====================================================================
INTERNAL FUNCTIONS STARTS HERE.
=====================================================================
*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Read call gate descriptor. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
LOCAL VOID
read_call_gate
IFN5(
IU32, descr_addr, /* (I) memory address of call gate descriptor */
ISM32, super, /* (I) descriptor type
(CALL_GATE|XTND_CALL_GATE) */
IU16 *, selector, /* (O) selector retrieved from descriptor */
IU32 *, offset, /* (O) offset retrieved from descriptor */
IU8 *, count /* (O) count retrieved from descriptor */
)
{
/*
The format of a gate descriptor is:-
===========================
+1 | LIMIT 15-0 | +0
===========================
+3 | SELECTOR | +2
===========================
+5 | AR | COUNT | +4
===========================
+7 | LIMIT 31-16 | +6
===========================
*/
IU32 first_dword;
IU32 second_dword;
/* read in descriptor with minimum interaction with memory */
first_dword = spr_read_dword(descr_addr);
second_dword = spr_read_dword(descr_addr+4);
/* unpack selector */
*selector = first_dword >> 16;
/* unpack lower bits of offset */
*offset = first_dword & WORD_MASK;
/* unpack count */
*count = second_dword & BYTE_MASK;
if ( super == XTND_CALL_GATE )
{
/* unpack higer bits of offset */
*offset = second_dword & ~WORD_MASK | *offset;
*count &= 0x0f; /* 4-bit double word count */
SET_OPERAND_SIZE(USE32); /* Gate Overrides all else. */
}
else
{
*count &= 0x1f; /* 5-bit word count */
SET_OPERAND_SIZE(USE16); /* Gate Overrides all else. */
}
}
/*
=====================================================================
EXTERNAL ROUTINES STARTS HERE.
=====================================================================
*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Update IP with relative offset. Check new IP is valid. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
update_relative_ip
IFN1(
IU32, rel_offset /* sign extended relative offset */
)
{
IU32 new_dest;
new_dest = GET_EIP() + rel_offset;
if ( GET_OPERAND_SIZE() == USE16 )
new_dest &= WORD_MASK;
#ifdef TAKE_REAL_MODE_LIMIT_FAULT
if ( new_dest > GET_CS_LIMIT() )
GP((IU16)0, FAULT_RM_REL_IP_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.
*/
#ifdef TAKE_PROT_MODE_LIMIT_FAILURE
/* The Soft486 EDL CPU does not take Protected Mode limit failues
* for the instructions with relative offsets, Jxx, LOOPxx, JCXZ,
* JMP rel and CALL rel, or instructions with near offsets,
* JMP near and CALL near.
* 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 )
{
if ( new_dest > GET_CS_LIMIT() )
GP((IU16)0, FAULT_PM_REL_IP_CS_LIMIT);
}
#endif /* TAKE_PROT_MODE_LIMIT_FAILURE */
#endif /* TAKE_REAL_MODE_LIMIT_FAULT */
SET_EIP(new_dest);
took_relative_jump = TRUE;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Validate far call or far jump destination */
/* Take #GP if invalid or access check fail. */
/* Take #NP if not present. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
validate_far_dest
IFN6(
IU16 *, cs, /* (I/O) segment of target address */
IU32 *, ip, /* (I/O) offset of target address */
IU32 *, descr_addr, /* (O) related descriptor memory address */
IU8 *, count, /* (O) call gate count(valid if CALL_GATE) */
ISM32 *, dest_type, /* (O) destination type */
ISM32, caller_id /* (I) bit mapped caller identifier */
)
{
IU16 new_cs;
IU32 new_ip;
IU32 cs_descr_addr;
IU8 AR;
ISM32 super;
new_cs = *cs; /* take local copies */
new_ip = *ip;
*dest_type = SAME_LEVEL; /* default to commonest type */
if ( selector_outside_GDT_LDT(new_cs, &cs_descr_addr) )
GP(new_cs, FAULT_FAR_DEST_SELECTOR);
/* load access rights */
AR = spr_read_byte(cs_descr_addr+5);
/* validate possible types of target */
switch ( super = descriptor_super_type((IU16)AR) )
{
case CONFORM_NOREAD_CODE:
case CONFORM_READABLE_CODE:
/* access check requires DPL <= CPL */
if ( GET_AR_DPL(AR) > GET_CPL() )
GP(new_cs, FAULT_FAR_DEST_ACCESS_1);
/* it must be present */
if ( GET_AR_P(AR) == NOT_PRESENT )
NP(new_cs, FAULT_FAR_DEST_NP_CONFORM);
break;
case NONCONFORM_NOREAD_CODE:
case NONCONFORM_READABLE_CODE:
/* access check requires RPL <= CPL and DPL == CPL */
if ( GET_SELECTOR_RPL(new_cs) > GET_CPL() ||
GET_AR_DPL(AR) != GET_CPL() )
GP(new_cs, FAULT_FAR_DEST_ACCESS_2);
/* it must be present */
if ( GET_AR_P(AR) == NOT_PRESENT )
NP(new_cs, FAULT_FAR_DEST_NP_NONCONFORM);
break;
case CALL_GATE:
case XTND_CALL_GATE:
/* Check gate present and access allowed */
/* access check requires DPL >= RPL and DPL >= CPL */
if ( GET_SELECTOR_RPL(new_cs) > GET_AR_DPL(AR) ||
GET_CPL() > GET_AR_DPL(AR) )
GP(new_cs, FAULT_FAR_DEST_ACCESS_3);
if ( GET_AR_P(AR) == NOT_PRESENT )
NP(new_cs, FAULT_FAR_DEST_NP_CALLG);
/* OK, get real destination from gate */
read_call_gate(cs_descr_addr, super, &new_cs, &new_ip, count);
validate_gate_dest(caller_id, new_cs, &cs_descr_addr, dest_type);
break;
case TASK_GATE:
/* Check gate present and access allowed */
/* access check requires DPL >= RPL and DPL >= CPL */
if ( GET_SELECTOR_RPL(new_cs) > GET_AR_DPL(AR) ||
GET_CPL() > GET_AR_DPL(AR) )
GP(new_cs, FAULT_FAR_DEST_ACCESS_4);
if ( GET_AR_P(AR) == NOT_PRESENT )
NP(new_cs, FAULT_FAR_DEST_NP_TASKG);
/* OK, get real destination from gate */
new_cs = spr_read_word(cs_descr_addr+2);
/* Check out new destination */
(void)validate_task_dest(new_cs, &cs_descr_addr);
*dest_type = NEW_TASK;
break;
case AVAILABLE_TSS:
case XTND_AVAILABLE_TSS:
/* TSS must be in GDT */
if ( GET_SELECTOR_TI(new_cs) == 1 )
GP(new_cs, FAULT_FAR_DEST_TSS_IN_LDT);
/* access check requires DPL >= RPL and DPL >= CPL */
if ( GET_SELECTOR_RPL(new_cs) > GET_AR_DPL(AR) ||
GET_CPL() > GET_AR_DPL(AR) )
GP(new_cs, FAULT_FAR_DEST_ACCESS_5);
/* it must be present */
if ( GET_AR_P(AR) == NOT_PRESENT )
NP(new_cs, FAULT_FAR_DEST_NP_TSS);
*dest_type = NEW_TASK;
break;
default:
GP(new_cs, FAULT_FAR_DEST_BAD_SEG_TYPE); /* bad type for far destination */
}
*cs = new_cs; /* Return final values */
*ip = new_ip;
*descr_addr = cs_descr_addr;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Validate transfer of control to a call gate destination. */
/* Take #GP if invalid or access check fail. */
/* Take #NP if not present. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
validate_gate_dest
IFN4(
ISM32, caller_id, /* (I) bit mapped caller identifier */
IU16, new_cs, /* (I) segment of target address */
IU32 *, descr_addr, /* (O) related descriptor memory address */
ISM32 *, dest_type /* (O) destination type */
)
{
IU8 AR;
*dest_type = SAME_LEVEL; /* default */
/* Check out new destination */
if ( selector_outside_GDT_LDT(new_cs, descr_addr) )
GP(new_cs, FAULT_GATE_DEST_SELECTOR);
/* load access rights */
AR = spr_read_byte((*descr_addr)+5);
/* must be a code segment */
switch ( descriptor_super_type((IU16)AR) )
{
case CONFORM_NOREAD_CODE:
case CONFORM_READABLE_CODE:
/* access check requires DPL <= CPL */
if ( GET_AR_DPL(AR) > GET_CPL() )
GP(new_cs, FAULT_GATE_DEST_ACCESS_1);
break;
case NONCONFORM_NOREAD_CODE:
case NONCONFORM_READABLE_CODE:
/* access check requires DPL <= CPL */
if ( GET_AR_DPL(AR) > GET_CPL() )
GP(new_cs, FAULT_GATE_DEST_ACCESS_2);
/* but jumps must have DPL == CPL */
if ( (caller_id & JMP_ID) && (GET_AR_DPL(AR) != GET_CPL()) )
GP(new_cs, FAULT_GATE_DEST_ACCESS_3);
/* set MORE_PRIVILEGE(0|1|2) */
if ( GET_AR_DPL(AR) < GET_CPL() )
*dest_type = GET_AR_DPL(AR);
break;
default:
GP(new_cs, FAULT_GATE_DEST_BAD_SEG_TYPE);
}
if ( GET_VM() == 1 )
{
/*
We must be called by ISM32, so ensure we go to CPL 0 via
a 32-bit gate.
*/
if ( *dest_type != MORE_PRIVILEGE0 || GET_OPERAND_SIZE() != USE32 )
GP(new_cs, FAULT_GATE_DEST_GATE_SIZE);
}
/* it must be present */
if ( GET_AR_P(AR) == NOT_PRESENT )
NP(new_cs, FAULT_GATE_DEST_NP);
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Validate transfer of control to a task gate destination. */
/* Take #GP if invalid or access check fail. */
/* Take #NP if not present. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL IMPORT ISM32
validate_task_dest
IFN2(
IU16, selector, /* (I) segment of target address */
IU32 *, descr_addr /* (O) related descriptor memory address */
)
{
IU8 AR;
ISM32 super;
/* must be in GDT */
if ( selector_outside_GDT(selector, descr_addr) )
GP(selector, FAULT_TASK_DEST_SELECTOR);
/* load access rights */
AR = spr_read_byte((*descr_addr)+5);
/* is it really an available TSS segment */
super = descriptor_super_type((IU16)AR);
if ( super == AVAILABLE_TSS || super == XTND_AVAILABLE_TSS )
; /* ok */
else
GP(selector, FAULT_TASK_DEST_NOT_TSS);
/* it must be present */
if ( GET_AR_P(AR) == NOT_PRESENT )
NP(selector, FAULT_TASK_DEST_NP);
return super;
}