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.
 
 
 
 
 
 

589 lines
18 KiB

/*[
c_tsksw.c
LOCAL CHAR SccsID[]="@(#)c_tsksw.c 1.11 03/03/95";
Task Switch Support.
--------------------
]*/
#include <stdio.h>
#include <insignia.h>
#include <host_def.h>
#include <xt.h>
#include CpuH
#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_tsksw.h>
#include <c_page.h>
#include <mov.h>
#include <fault.h>
/*[
The 286 TSS is laid out as follows:-
=============================
| Back Link to TSS Selector | +00 =
| SP for CPL 0 | +02 *
| SS for CPL 0 | +04 *
| SP for CPL 1 | +06 * Initial Stacks (STATIC)
| SS for CPL 1 | +08 *
| SP for CPL 2 | +0a *
| SS for CPL 2 | +0c *
| IP | +0e =
| FLAG Register | +10 =
| AX | +12 =
| CX | +14 =
| DX | +16 =
| BX | +18 =
| SP | +1a = Current State (DYNAMIC)
| BP | +1c =
| SI | +1e =
| DI | +20 =
| ES | +22 =
| CS | +24 =
| SS | +26 =
| DS | +28 =
| Task LDT Selector | +2a *
=============================
The 386 TSS is laid out as follows:-
===========================================
| 0 | Back Link | +00 =
| ESP for CPL 0 | +04 *
| 0 | SS for CPL 0 | +08 *
| ESP for CPL 1 | +0c *
| 0 | SS for CPL 1 | +10 *
| ESP for CPL 2 | +14 *
| 0 | SS for CPL 2 | +18 *
| CR3 | +1c *
| EIP | +20 =
| EFLAG | +24 =
| EAX | +28 =
| ECX | +2c =
| EDX | +30 =
| EBX | +34 =
| ESP | +38 =
| EBP | +3c =
| ESI | +40 =
| EDI | +44 =
| 0 | ES | +48 =
| 0 | CS | +4c =
| 0 | SS | +50 =
| 0 | DS | +54 =
| 0 | FS | +58 =
| 0 | GS | +5c =
| 0 | LDT Selector | +60 *
| I/O Map Base Addr. | 0 |T| +64 *
|-----------------------------------------|
| ... |
|-----------------------------------------|
| I/O Permission Bit Map | +I/O Map Base Addr.
| |
|11111111| |
===========================================
]*/
/*
Prototype our internal functions.
*/
LOCAL VOID load_LDT_in_task_switch
IPT1(
IU16, tss_selector
);
LOCAL VOID load_data_seg_new_task
IPT2(
ISM32, indx,
IU16, selector
);
#define IP_OFFSET_IN_286_TSS 0x0e
#define IP_OFFSET_IN_386_TSS 0x20
#define CR3_OFFSET_IN_386_TSS 0x1c
#define LOCAL_BRK_ENABLE 0x155 /* LE,L3,L2,L1 and L0 bits of DCR */
/*
=====================================================================
INTERNAL FUNCTIONS STARTS HERE.
=====================================================================
*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Load LDT selector during a task switch. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
LOCAL VOID
load_LDT_in_task_switch
IFN1(
IU16, tss_selector
)
{
IU16 selector;
IU32 descr_addr;
CPU_DESCR entry;
/* The selector is already loaded into LDTR */
selector = GET_LDT_SELECTOR();
/* A null selector can be left alone */
if ( !selector_is_null(selector) )
{
/* must be in GDT */
if ( selector_outside_GDT(selector, &descr_addr) )
{
SET_LDT_SELECTOR(0); /* invalidate selector */
TS(tss_selector, FAULT_LOADLDT_SELECTOR);
}
read_descriptor_linear(descr_addr, &entry);
/* is it really a LDT segment */
if ( descriptor_super_type(entry.AR) != LDT_SEGMENT )
{
SET_LDT_SELECTOR(0); /* invalidate selector */
TS(tss_selector, FAULT_LOADLDT_NOT_AN_LDT);
}
/* must be present */
if ( GET_AR_P(entry.AR) == NOT_PRESENT )
{
SET_LDT_SELECTOR(0); /* invalidate selector */
TS(tss_selector, FAULT_LOADLDT_NOTPRESENT);
}
/* ok, good selector, load register */
SET_LDT_BASE(entry.base);
SET_LDT_LIMIT(entry.limit);
}
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Load a Data Segment Register (DS, ES, FS, GS) during */
/* a Task Switch . */
/* Take #GP(selector) if segment not valid */
/* Take #NP(selector) if segment not present */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
LOCAL VOID
load_data_seg_new_task
IFN2(
ISM32, indx, /* Segment Register identifier */
IU16, selector /* value to be loaded */
)
{
load_data_seg(indx, selector);
/* Reload pseudo descriptors if V86 Mode */
if ( GET_VM() == 1 )
load_pseudo_descr(indx);
}
/*
=====================================================================
EXTERNAL ROUTINES STARTS HERE.
=====================================================================
*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Switch tasks */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
switch_tasks
IFN5(
BOOL, returning, /* (I) if true doing return from task */
BOOL, nesting, /* (I) if true switch with nesting */
IU16, TSS_selector, /* (I) selector for new task */
IU32, descr, /* (I) memory address of new task descriptor */
IU32, return_ip /* (I) offset to restart old task at */
)
{
IU16 old_tss; /* components of old descriptor */
IU8 old_AR;
IU32 old_descr;
CPU_DESCR new_tss; /* components of new descriptor */
IU32 tss_addr; /* variables used to put/get TSS state */
IU32 next_addr;
IU32 flags;
ISM32 save_cpl;
IU8 T_byte; /* Byte holding the T bit */
IU32 ss_descr; /* variables defining new SS and CS values */
CPU_DESCR ss_entry;
IU16 new_cs;
IU32 cs_descr;
CPU_DESCR cs_entry;
IU32 pdbr; /* New value for PDBR */
if ( GET_TR_SELECTOR() == 0 )
TS(TSS_selector, FAULT_SWTASK_NULL_TR_SEL);
/* get new TSS info. */
read_descriptor_linear(descr, &new_tss);
/* calc address of descriptor related to old TSS */
old_tss = GET_TR_SELECTOR();
old_descr = GET_GDT_BASE() + GET_SELECTOR_INDEX_TIMES8(old_tss);
old_AR = spr_read_byte(old_descr+5);
/* SAVE OUTGOING STATE */
if ( GET_TR_AR_SUPER() == XTND_BUSY_TSS )
{
/* check outgoing TSS is large enough to save current state */
if ( GET_TR_LIMIT() < 0x67 )
{
TS(TSS_selector, FAULT_SWTASK_BAD_TSS_SIZE_1);
}
tss_addr = GET_TR_BASE();
next_addr = tss_addr + CR3_OFFSET_IN_386_TSS;
spr_write_dword(next_addr, GET_CR(3));
next_addr += 4;
spr_write_dword(next_addr, return_ip);
next_addr += 4;
flags = c_getEFLAGS();
if ( returning )
flags = flags & ~BIT14_MASK; /* clear NT */
spr_write_dword(next_addr, (IU32)flags);
#ifdef PIG
/* Note the possibility of unknown flags "pushed" */
record_flags_addr(next_addr);
#endif /* PIG */
next_addr += 4;
spr_write_dword(next_addr, GET_EAX());
next_addr += 4;
spr_write_dword(next_addr, GET_ECX());
next_addr += 4;
spr_write_dword(next_addr, GET_EDX());
next_addr += 4;
spr_write_dword(next_addr, GET_EBX());
next_addr += 4;
spr_write_dword(next_addr, GET_ESP());
next_addr += 4;
spr_write_dword(next_addr, GET_EBP());
next_addr += 4;
spr_write_dword(next_addr, GET_ESI());
next_addr += 4;
spr_write_dword(next_addr, GET_EDI());
next_addr += 4;
spr_write_word(next_addr, GET_ES_SELECTOR());
next_addr += 4;
spr_write_word(next_addr, GET_CS_SELECTOR());
next_addr += 4;
spr_write_word(next_addr, GET_SS_SELECTOR());
next_addr += 4;
spr_write_word(next_addr, GET_DS_SELECTOR());
next_addr += 4;
spr_write_word(next_addr, GET_FS_SELECTOR());
next_addr += 4;
spr_write_word(next_addr, GET_GS_SELECTOR());
}
else /* 286 TSS */
{
/* check outgoing TSS is large enough to save current state */
if ( GET_TR_LIMIT() < 0x29 )
{
TS(TSS_selector, FAULT_SWTASK_BAD_TSS_SIZE_2);
}
tss_addr = GET_TR_BASE();
next_addr = tss_addr + IP_OFFSET_IN_286_TSS;
spr_write_word(next_addr, (IU16)return_ip);
next_addr += 2;
flags = getFLAGS();
if ( returning )
flags = flags & ~BIT14_MASK; /* clear NT */
spr_write_word(next_addr, (IU16)flags);
#ifdef PIG
/* Note the possibility of unknown flags "pushed" */
record_flags_addr(next_addr);
#endif /* PIG */
next_addr += 2;
spr_write_word(next_addr, GET_AX());
next_addr += 2;
spr_write_word(next_addr, GET_CX());
next_addr += 2;
spr_write_word(next_addr, GET_DX());
next_addr += 2;
spr_write_word(next_addr, GET_BX());
next_addr += 2;
spr_write_word(next_addr, GET_SP());
next_addr += 2;
spr_write_word(next_addr, GET_BP());
next_addr += 2;
spr_write_word(next_addr, GET_SI());
next_addr += 2;
spr_write_word(next_addr, GET_DI());
next_addr += 2;
spr_write_word(next_addr, GET_ES_SELECTOR());
next_addr += 2;
spr_write_word(next_addr, GET_CS_SELECTOR());
next_addr += 2;
spr_write_word(next_addr, GET_SS_SELECTOR());
next_addr += 2;
spr_write_word(next_addr, GET_DS_SELECTOR());
}
/* LOAD TASK REGISTER */
/* mark incoming TSS as busy */
new_tss.AR |= BIT1_MASK;
spr_write_byte(descr+5, (IU8)new_tss.AR);
/* update task register */
SET_TR_SELECTOR(TSS_selector);
SET_TR_BASE(new_tss.base);
SET_TR_LIMIT(new_tss.limit);
SET_TR_AR_SUPER(descriptor_super_type(new_tss.AR));
tss_addr = GET_TR_BASE();
/* save back link if nesting, else make outgoing TSS available */
if ( nesting )
{
spr_write_word(tss_addr, old_tss);
}
else
{
/* mark old TSS as available */
old_AR = old_AR & ~BIT1_MASK;
spr_write_byte(old_descr+5, old_AR);
}
/* Note: Exceptions now happen in the incoming task */
/* EXTRACT NEW STATE */
if ( GET_TR_AR_SUPER() == XTND_BUSY_TSS )
{
/* check new TSS is large enough to extract new state from */
if ( GET_TR_LIMIT() < 0x67 )
TS(TSS_selector, FAULT_SWTASK_BAD_TSS_SIZE_3);
next_addr = tss_addr + CR3_OFFSET_IN_386_TSS;
pdbr = (IU32)spr_read_dword(next_addr);
if ( pdbr != GET_CR(CR_PDBR) )
{
/* Only reload PDBR if diferent */
MOV_CR(CR_PDBR, pdbr);
}
next_addr = tss_addr + IP_OFFSET_IN_386_TSS;
SET_EIP(spr_read_dword(next_addr)); next_addr += 4;
flags = (IU32)spr_read_dword(next_addr); next_addr += 4;
save_cpl = GET_CPL();
SET_CPL(0); /* act like highest privilege to set all flags */
c_setEFLAGS(flags);
SET_CPL(save_cpl);
if ( flags & BIT17_MASK )
fprintf(stderr, "(Task Switch)Entering V86 Mode.\n");
SET_EAX(spr_read_dword(next_addr)); next_addr += 4;
SET_ECX(spr_read_dword(next_addr)); next_addr += 4;
SET_EDX(spr_read_dword(next_addr)); next_addr += 4;
SET_EBX(spr_read_dword(next_addr)); next_addr += 4;
SET_ESP(spr_read_dword(next_addr)); next_addr += 4;
SET_EBP(spr_read_dword(next_addr)); next_addr += 4;
SET_ESI(spr_read_dword(next_addr)); next_addr += 4;
SET_EDI(spr_read_dword(next_addr)); next_addr += 4;
SET_ES_SELECTOR(spr_read_word(next_addr)); next_addr += 4;
SET_CS_SELECTOR(spr_read_word(next_addr)); next_addr += 4;
SET_SS_SELECTOR(spr_read_word(next_addr)); next_addr += 4;
SET_DS_SELECTOR(spr_read_word(next_addr)); next_addr += 4;
SET_FS_SELECTOR(spr_read_word(next_addr)); next_addr += 4;
SET_GS_SELECTOR(spr_read_word(next_addr)); next_addr += 4;
SET_LDT_SELECTOR(spr_read_word(next_addr)); next_addr += 4;
T_byte = spr_read_byte(next_addr);
}
else /* 286 TSS */
{
/* check new TSS is large enough to extract new state from */
if ( GET_TR_LIMIT() < 0x2b )
TS(TSS_selector, FAULT_SWTASK_BAD_TSS_SIZE_4);
next_addr = tss_addr + IP_OFFSET_IN_286_TSS;
SET_EIP(spr_read_word(next_addr)); next_addr += 2;
flags = (IU32)spr_read_word(next_addr); next_addr += 2;
save_cpl = GET_CPL();
SET_CPL(0); /* act like highest privilege to set all flags */
setFLAGS(flags);
SET_VM(0);
SET_CPL(save_cpl);
SET_AX(spr_read_word(next_addr)); next_addr += 2;
SET_CX(spr_read_word(next_addr)); next_addr += 2;
SET_DX(spr_read_word(next_addr)); next_addr += 2;
SET_BX(spr_read_word(next_addr)); next_addr += 2;
SET_SP(spr_read_word(next_addr)); next_addr += 2;
SET_BP(spr_read_word(next_addr)); next_addr += 2;
SET_SI(spr_read_word(next_addr)); next_addr += 2;
SET_DI(spr_read_word(next_addr)); next_addr += 2;
SET_ES_SELECTOR(spr_read_word(next_addr)); next_addr += 2;
SET_CS_SELECTOR(spr_read_word(next_addr)); next_addr += 2;
SET_SS_SELECTOR(spr_read_word(next_addr)); next_addr += 2;
SET_DS_SELECTOR(spr_read_word(next_addr)); next_addr += 2;
SET_FS_SELECTOR(0);
SET_GS_SELECTOR(0);
SET_LDT_SELECTOR(spr_read_word(next_addr));
T_byte = 0;
}
/* invalidate cache entries for segment registers */
SET_CS_AR_R(0); SET_CS_AR_W(0);
SET_DS_AR_R(0); SET_DS_AR_W(0);
SET_ES_AR_R(0); SET_ES_AR_W(0);
SET_SS_AR_R(0); SET_SS_AR_W(0);
SET_FS_AR_R(0); SET_FS_AR_W(0);
SET_GS_AR_R(0); SET_GS_AR_W(0);
/* update NT bit */
if ( nesting )
SET_NT(1);
else
if ( !returning )
SET_NT(0);
/* update TS */
SET_CR(CR_STAT, GET_CR(CR_STAT) | BIT3_MASK);
/* kill local breakpoints */
SET_DR(DR_DCR, GET_DR(DR_DCR) & ~LOCAL_BRK_ENABLE);
/* set up trap on T-bit */
if ( T_byte & BIT0_MASK )
{
SET_DR(DR_DSR, GET_DR(DR_DSR) | DSR_BT_MASK);
}
/* ERROR CHECKING */
/* check new LDT and load hidden cache if ok */
load_LDT_in_task_switch(TSS_selector);
if ( GET_VM() == 1 )
{
SET_CPL(3); /* set V86 privilege level */
/* CS selector requires no checks */
}
else
{
/* change CPL to that of incoming code segment */
SET_CPL(GET_SELECTOR_RPL(GET_CS_SELECTOR()));
/* check new code selector... */
new_cs = GET_CS_SELECTOR();
if ( selector_outside_GDT_LDT(new_cs, &cs_descr) )
TS(new_cs, FAULT_SWTASK_BAD_CS_SELECTOR);
read_descriptor_linear(cs_descr, &cs_entry);
/* check type and privilege of new cs selector */
switch ( descriptor_super_type(cs_entry.AR) )
{
case CONFORM_NOREAD_CODE:
case CONFORM_READABLE_CODE:
/* check code is present */
if ( GET_AR_P(cs_entry.AR) == NOT_PRESENT )
NP(new_cs, FAULT_SWTASK_CONFORM_CS_NP);
/* privilege check requires DPL <= CPL */
if ( GET_AR_DPL(cs_entry.AR) > GET_CPL() )
TS(new_cs, FAULT_SWTASK_ACCESS_1);
break;
case NONCONFORM_NOREAD_CODE:
case NONCONFORM_READABLE_CODE:
/* check code is present */
if ( GET_AR_P(cs_entry.AR) == NOT_PRESENT )
NP(new_cs, FAULT_SWTASK_NOCONFORM_CS_NP);
/* privilege check requires DPL == CPL */
if ( GET_AR_DPL(cs_entry.AR) != GET_CPL() )
TS(new_cs, FAULT_SWTASK_ACCESS_2);
break;
default:
TS(new_cs, FAULT_SWTASK_BAD_SEG_TYPE);
}
}
/* code ok, load hidden cache */
load_CS_cache(new_cs, cs_descr, &cs_entry);
#if 0
/* retain operand size from gate until first instruction fetch */
if ( GET_CS_AR_X() == USE16 )
SET_OPERAND_SIZE(USE16);
else /* USE32 */
SET_OPERAND_SIZE(USE32);
#endif
/* check new SS and load if ok */
if ( GET_VM() == 1 )
{
/* SS selector requires no checks */
load_stack_seg(GET_SS_SELECTOR());
load_pseudo_descr(SS_REG);
}
else
{
validate_SS_on_stack_change(GET_CPL(), GET_SS_SELECTOR(),
&ss_descr, &ss_entry);
load_SS_cache(GET_SS_SELECTOR(), ss_descr, &ss_entry);
}
/* finally check new DS, ES, FS and GS */
load_data_seg_new_task(DS_REG, GET_DS_SELECTOR());
load_data_seg_new_task(ES_REG, GET_ES_SELECTOR());
load_data_seg_new_task(FS_REG, GET_FS_SELECTOR());
load_data_seg_new_task(GS_REG, GET_GS_SELECTOR());
}