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.
 
 
 
 
 
 

515 lines
12 KiB

/*[
c_intr.c
LOCAL CHAR SccsID[]="@(#)c_intr.c 1.21 03/07/95";
Interrupt 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_intr.h>
#include <c_xfer.h>
#include <c_tsksw.h>
#include <c_page.h>
#include <c_mem.h>
#include <ccpusas4.h>
#include <ccpupig.h>
#include <fault.h>
#ifdef PIG
#include <gdpvar.h>
#endif
/*
Prototype our internal functions.
*/
LOCAL ISM32 validate_int_dest
IPT6(
IU16, vector,
BOOL, do_priv,
IU16 *, cs,
IU32 *, ip,
IU32 *, descr_addr,
ISM32 *, dest_type
);
/*
=====================================================================
INTERNAL FUNCTIONS STARTS HERE.
=====================================================================
*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Validate int destination. Essentially decode int instruction. */
/* Take #GP_INT(vector) if invalid. */
/* Take #NP_INT(vector) if not present. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
LOCAL ISM32
validate_int_dest
IFN6(
IU16, vector, /* (I) vector to be checked */
BOOL, do_priv, /* (I) if true do privilege check */
IU16 *, cs, /* (O) segment of target address */
IU32 *, ip, /* (O) offset of target address */
IU32 *, descr_addr, /* (O) related descriptor memory address */
ISM32 *, dest_type /* (O) destination type */
)
{
IU16 offset;
IU8 AR;
ISM32 super;
/* calc address within IDT */
offset = vector * 8;
/* check within IDT */
if ( offset + 7 > GET_IDT_LIMIT() )
GP_INT(vector, FAULT_INT_DEST_NOT_IN_IDT);
*descr_addr = GET_IDT_BASE() + offset;
AR = spr_read_byte((*descr_addr)+5);
/* check type */
switch ( super = descriptor_super_type((IU16)AR) )
{
case INTERRUPT_GATE:
case TRAP_GATE:
SET_OPERAND_SIZE(USE16);
break;
case XTND_INTERRUPT_GATE:
case XTND_TRAP_GATE:
SET_OPERAND_SIZE(USE32);
break;
case TASK_GATE:
break; /* ok */
default:
GP_INT(vector, FAULT_INT_DEST_BAD_SEG_TYPE);
}
/* access check requires CPL <= DPL */
if ( do_priv && (GET_CPL() > GET_AR_DPL(AR)) )
GP_INT(vector, FAULT_INT_DEST_ACCESS);
/* gate must be present */
if ( GET_AR_P(AR) == NOT_PRESENT )
NP_INT(vector, FAULT_INT_DEST_NOTPRESENT);
/* ok, get real destination from gate */
*cs = spr_read_word((*descr_addr)+2);
/* action gate type */
if ( super == TASK_GATE )
{
/* Need to set operand size here so that any
* error code is push with correct size.
*/
switch (validate_task_dest(*cs, descr_addr))
{
case BUSY_TSS:
case AVAILABLE_TSS:
SET_OPERAND_SIZE(USE16);
break;
case XTND_BUSY_TSS:
case XTND_AVAILABLE_TSS:
SET_OPERAND_SIZE(USE32);
break;
}
*dest_type = NEW_TASK;
}
else
{
/* INTERRUPT or TRAP GATE */
*ip = (IU32)spr_read_word(*descr_addr);
if ( super == XTND_INTERRUPT_GATE || super == XTND_TRAP_GATE )
*ip = (IU32)spr_read_word((*descr_addr)+6) << 16 | *ip;
validate_gate_dest(INT_ID, *cs, descr_addr, dest_type);
}
return super;
}
/*
=====================================================================
EXTERNAL ROUTINES STARTS HERE.
=====================================================================
*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Process interrupt. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
do_intrupt
IFN4(
IU16, vector, /* (I) interrupt vector to call */
BOOL, priv_check, /* (I) if true access check is needed */
BOOL, has_error_code, /* (I) if true needs error code pushing
on stack */
IU16, error_code /* (I) error code to be pushed */
)
{
/* GLOBALS USED */
/* doing_contributory cleared on success of interrupt */
/* doing_page_fault cleared on success of interrupt */
/* doing_double_fault cleared on success of interrupt */
/* doing_fault indicates RF should be set in pushed
flags, cleared on success */
IU32 flags; /* temp store for FLAGS register */
IU32 ivt_addr; /* address of ivt entry */
IU16 new_cs; /* The 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 super; /* super type of destination */
IU32 dpl; /* new privilege level (if used) */
ISM32 stk_sz; /* space (in bytes) reqd on stack */
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 */
IU32 old_ss; /* Variables used while making stack */
IU32 old_sp;
if ( GET_PE() == 0 )
{
/* Real Mode */
/* must be able to push FLAGS:CS:IP */
validate_stack_space(USE_SP, (ISM32)NR_ITEMS_3);
/* get new destination */
ivt_addr = (IU32)vector * 4;
new_ip = (IU32)phy_read_word(ivt_addr);
new_cs = phy_read_word(ivt_addr+2);
#ifdef TAKE_REAL_MODE_LIMIT_FAULT
/*
* In real mode, there is still an IP limit check. The new IP is
* compared against the last CS limit from when the program was last
* in protected mode (or 64K if it never was). For us, this is stored
* in the CS limit field. (cf i486PRM page 22-4)
*/
if ( new_ip > GET_CS_LIMIT() )
GP((IU16)0, FAULT_INTR_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 */
flags = c_getEFLAGS();
if ( doing_fault )
{
#ifdef PIG
if (GLOBAL_RF_OnXcptnWanted)
flags |= BIT16_MASK; /* SET RF bit */
#else
flags |= BIT16_MASK; /* SET RF bit */
#endif
}
#ifdef PIG
if (vector < 31 && (((1 << vector) & NO_FLAGS_EXCEPTION_MASK) != 0))
spush_flags(flags);
else
#endif /* PIG */
spush(flags);
spush16((IU32)GET_CS_SELECTOR());
spush((IU32)GET_EIP());
load_CS_cache(new_cs, (IU32)0, (CPU_DESCR *)0);
SET_EIP(new_ip);
SET_IF(0);
SET_TF(0);
}
else
{
/* Protected Mode */
super = validate_int_dest(vector, priv_check, &new_cs, &new_ip,
&cs_descr_addr, &dest_type);
/* check type of indirect target */
switch ( dest_type )
{
case NEW_TASK:
switch_tasks(NOT_RETURNING, NESTING, new_cs, cs_descr_addr, GET_EIP());
/* save error code on new stack */
if ( has_error_code )
{
validate_stack_space(USE_SP, (ISM32)NR_ITEMS_1);
spush((IU32)error_code);
}
/* limit check new IP (now in new task) */
if ( GET_EIP() > GET_CS_LIMIT() )
GP((IU16)0, FAULT_INTR_TASK_CS_LIMIT);
break;
case SAME_LEVEL:
read_descriptor_linear(cs_descr_addr, &cs_entry);
/* stamp new selector with CPL */
SET_SELECTOR_RPL(new_cs, GET_CPL());
/* check room for return address CS:(E)IP:(E)FLAGS:(Error) */
if ( has_error_code )
stk_sz = NR_ITEMS_4;
else
stk_sz = NR_ITEMS_3;
validate_stack_space(USE_SP, stk_sz);
if ( new_ip > cs_entry.limit )
GP((IU16)0, FAULT_INTR_PM_CS_LIMIT_1);
/* ALL SYSTEMS GO */
/* push flags */
flags = c_getEFLAGS();
if ( doing_fault )
{
#ifdef PIG
if (GLOBAL_RF_OnXcptnWanted)
flags |= BIT16_MASK; /* SET RF bit */
#else
flags |= BIT16_MASK; /* SET RF bit */
#endif
}
#ifdef PIG
if (vector < 31 && (((1 << vector) & NO_FLAGS_EXCEPTION_MASK) != 0))
spush_flags(flags);
else
#endif /* PIG */
spush(flags);
/* push return address */
spush16((IU32)GET_CS_SELECTOR());
spush((IU32)GET_EIP());
/* finally push error code if required */
if ( has_error_code )
{
spush((IU32)error_code);
}
load_CS_cache(new_cs, cs_descr_addr, &cs_entry);
SET_EIP(new_ip);
/* finally action IF, TF and NT flags */
if ((super == INTERRUPT_GATE) || (super == XTND_INTERRUPT_GATE))
SET_IF(0);
SET_TF(0);
SET_NT(0);
break;
default: /* MORE PRIVILEGE(0|1|2) */
read_descriptor_linear(cs_descr_addr, &cs_entry);
dpl = dest_type;
/* stamp new selector with new CPL */
SET_SELECTOR_RPL(new_cs, dpl);
/* find out about new stack */
get_stack_selector_from_TSS(dpl, &new_ss, &new_sp);
/* check new stack selector */
validate_SS_on_stack_change(dpl, new_ss,
&ss_descr_addr, &ss_entry);
/* check room for (GS:FS:DS:ES)
SS:(E)SP
(E)FLAGS
CS:(E)IP
(ERROR) */
if ( GET_VM() == 1 )
stk_sz = NR_ITEMS_9;
else
stk_sz = NR_ITEMS_5;
if ( has_error_code )
stk_sz = stk_sz + NR_ITEMS_1;
validate_new_stack_space(stk_sz, new_sp, &ss_entry, new_ss);
if ( new_ip > cs_entry.limit )
GP((IU16)0, FAULT_INTR_PM_CS_LIMIT_2);
/* ALL SYSTEMS GO */
flags = c_getEFLAGS();
if ( doing_fault )
{
#ifdef PIG
if (GLOBAL_RF_OnXcptnWanted)
flags |= BIT16_MASK; /* SET RF bit */
#else
flags |= BIT16_MASK; /* SET RF bit */
#endif
}
SET_CPL(dpl);
SET_VM(0);
/* update stack segment */
old_ss = (IU32)GET_SS_SELECTOR();
old_sp = GET_ESP();
load_SS_cache(new_ss, ss_descr_addr, &ss_entry);
set_current_SP(new_sp);
/*
FORM NEW STACK, VIZ
==============
new SS:IP -> | error code |
| old IP |
| old CS |
| FLAGS |
| old SP |
| old SS |
==============
| old ES |
| old DS |
| old FS |
| old GS |
==============
*/
if ( stk_sz >= NR_ITEMS_9 )
{
/* interrupt from V86 mode */
spush16((IU32)GET_GS_SELECTOR());
spush16((IU32)GET_FS_SELECTOR());
spush16((IU32)GET_DS_SELECTOR());
spush16((IU32)GET_ES_SELECTOR());
/* invalidate data segments */
load_data_seg(GS_REG, (IU16)0);
load_data_seg(FS_REG, (IU16)0);
load_data_seg(DS_REG, (IU16)0);
load_data_seg(ES_REG, (IU16)0);
}
/* push old stack values */
spush16(old_ss);
spush(old_sp);
/* push old flags */
#ifdef PIG
if (vector < 31 && (((1 << vector) & NO_FLAGS_EXCEPTION_MASK) != 0))
spush_flags(flags);
else
#endif /* PIG */
spush(flags);
/* push return address */
spush16((IU32)GET_CS_SELECTOR());
spush((IU32)GET_EIP());
/* finally push error code if required */
if ( has_error_code )
{
spush((IU32)error_code);
}
/* update code segment */
load_CS_cache(new_cs, cs_descr_addr, &cs_entry);
SET_EIP(new_ip);
/* finally action IF, TF and NT flags */
if ((super == INTERRUPT_GATE) || (super == XTND_INTERRUPT_GATE))
SET_IF(0);
SET_TF(0);
SET_NT(0);
break;
}
}
EXT = INTERNAL;
#ifdef PIG
save_last_inst_details("do_intr");
pig_cpu_action = CHECK_ALL;
/* If the destination is going to page fault, or need
* accessing, then the EDL CPU will do so before issuing
* the pig synch. We use the dasm386 decode to prefetch
* a single instruction which mimics the EDL Cpu's behaviour
* when close to a page boundary.
*/
prefetch_1_instruction(); /* Will PF if destination not present */
ccpu_synch_count++;
#else /* !PIG */
#ifdef SYNCH_TIMERS
if (doing_fault)
{
extern void SynchTick IPT0();
SynchTick();
}
#endif /* SYNCH_TIMERS */
#endif /* PIG */
/* mark successful end to interrupt */
doing_fault = FALSE;
doing_contributory = FALSE;
doing_page_fault = FALSE;
doing_double_fault = FALSE;
#ifdef PIG
c_cpu_unsimulate();
#endif /* PIG */
}