mirror of https://github.com/lianthony/NT4.0
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.
287 lines
8.0 KiB
287 lines
8.0 KiB
/*[
|
|
|
|
c_prot.c
|
|
|
|
LOCAL CHAR SccsID[]="@(#)c_prot.c 1.7 01/19/95";
|
|
|
|
Protected Mode Support (Misc).
|
|
------------------------------
|
|
|
|
]*/
|
|
|
|
|
|
#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 <fault.h>
|
|
|
|
|
|
/*
|
|
=====================================================================
|
|
EXTERNAL ROUTINES STARTS HERE.
|
|
=====================================================================
|
|
*/
|
|
|
|
|
|
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
|
|
/* Check selector is valid for load into SS register. */
|
|
/* Only invoked in protected mode. */
|
|
/* Take GP if segment not valid. */
|
|
/* Take SF if segment not present. */
|
|
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
|
|
GLOBAL VOID
|
|
check_SS
|
|
|
|
IFN4(
|
|
IU16, selector, /* (I) 16-bit selector to stack segment */
|
|
ISM32, privilege, /* (I) privilege level to check against */
|
|
IU32 *, descr_addr, /* (O) address of stack segment descriptor */
|
|
CPU_DESCR *, entry /* (O) the decoded descriptor */
|
|
)
|
|
|
|
|
|
{
|
|
/* must be within GDT or LDT */
|
|
if ( selector_outside_GDT_LDT(selector, descr_addr) )
|
|
GP(selector, FAULT_CHECKSS_SELECTOR);
|
|
|
|
read_descriptor_linear(*descr_addr, entry);
|
|
|
|
/* must be writable data */
|
|
switch ( descriptor_super_type(entry->AR) )
|
|
{
|
|
case EXPANDUP_WRITEABLE_DATA:
|
|
case EXPANDDOWN_WRITEABLE_DATA:
|
|
break; /* good type */
|
|
|
|
default:
|
|
GP(selector, FAULT_CHECKSS_BAD_SEG_TYPE); /* bad type */
|
|
}
|
|
|
|
/* access check requires RPL == DPL == privilege */
|
|
if ( GET_SELECTOR_RPL(selector) != privilege ||
|
|
GET_AR_DPL(entry->AR) != privilege )
|
|
GP(selector, FAULT_CHECKSS_ACCESS);
|
|
|
|
/* finally it must be present */
|
|
if ( GET_AR_P(entry->AR) == NOT_PRESENT )
|
|
SF(selector, FAULT_CHECKSS_NOTPRESENT);
|
|
|
|
}
|
|
|
|
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
|
|
/* Get SS:(E)SP for a given privilege from the TSS */
|
|
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
|
|
GLOBAL VOID
|
|
get_stack_selector_from_TSS
|
|
|
|
IFN3(
|
|
IU32, priv, /* (I) priv level for which stack is reqd */
|
|
IU16 *, new_ss, /* (O) SS as retrieved from TSS */
|
|
IU32 *, new_sp /* (O) (E)SP as retrieved from TSS */
|
|
)
|
|
|
|
|
|
{
|
|
IU32 address;
|
|
|
|
if ( GET_TR_AR_SUPER() == BUSY_TSS )
|
|
{
|
|
/* 286 TSS */
|
|
switch ( priv )
|
|
{
|
|
case 0: address = 0x02; break;
|
|
case 1: address = 0x06; break;
|
|
case 2: address = 0x0a; break;
|
|
}
|
|
|
|
address += GET_TR_BASE();
|
|
|
|
*new_sp = (IU32)spr_read_word(address);
|
|
*new_ss = spr_read_word(address+2);
|
|
}
|
|
else
|
|
{
|
|
/* 386 TSS */
|
|
switch ( priv )
|
|
{
|
|
case 0: address = 0x04; break;
|
|
case 1: address = 0x0c; break;
|
|
case 2: address = 0x14; break;
|
|
}
|
|
|
|
address += GET_TR_BASE();
|
|
|
|
*new_sp = spr_read_dword(address);
|
|
*new_ss = spr_read_word(address+4);
|
|
}
|
|
}
|
|
|
|
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
|
|
/* Check a Data Segment Register (DS, ES, FS, GS) during */
|
|
/* a Privilege Change. */
|
|
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
|
|
GLOBAL VOID
|
|
load_data_seg_new_privilege
|
|
|
|
IFN1(
|
|
ISM32, indx /* Segment Register identifier */
|
|
)
|
|
|
|
|
|
{
|
|
IU16 selector; /* selector to be examined */
|
|
IU32 descr; /* ... its associated decriptor location */
|
|
ISM32 dpl; /* ... its associated DPL */
|
|
BOOL valid; /* selector validity */
|
|
|
|
selector = GET_SR_SELECTOR(indx); /* take local copy */
|
|
|
|
if ( !selector_outside_GDT_LDT(selector, &descr) )
|
|
{
|
|
valid = TRUE; /* at least its in table */
|
|
|
|
/* Type must be ok, else it would not have been loaded. */
|
|
|
|
/* for data and non-conforming code the access check applies */
|
|
if ( GET_SR_AR_C(indx) == 0 )
|
|
{
|
|
/* The access check is:- DPL >= CPL and DPL >= RPL */
|
|
dpl = GET_SR_AR_DPL(indx);
|
|
if ( dpl >= GET_CPL() && dpl >= GET_SELECTOR_RPL(selector) )
|
|
; /* ok */
|
|
else
|
|
valid = FALSE; /* fails privilege check */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
valid = FALSE; /* not in table */
|
|
}
|
|
|
|
if ( !valid )
|
|
{
|
|
/* segment can't be seen at new privilege */
|
|
SET_SR_SELECTOR(indx, 0);
|
|
SET_SR_AR_W(indx, 0); /* deny write */
|
|
SET_SR_AR_R(indx, 0); /* deny read */
|
|
|
|
/* the following lines were added to make the C-CPU act like the Soft-486
|
|
* in this respect... an investigation is under way to see how the real
|
|
* i486 behaves - this code may need to be changed in the future
|
|
*/
|
|
SET_SR_BASE(indx, 0);
|
|
SET_SR_LIMIT(indx, 0);
|
|
}
|
|
}
|
|
|
|
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
|
|
/* Validate a stack segment selector, during a stack change */
|
|
/* Take #TS(selector) if not valid stack selector */
|
|
/* Take #SF(selector) if segment not present */
|
|
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
|
|
GLOBAL VOID
|
|
validate_SS_on_stack_change
|
|
|
|
IFN4(
|
|
IU32, priv, /* (I) privilege level to check against */
|
|
IU16, selector, /* (I) selector to be checked */
|
|
IU32 *, descr, /* (O) address of related descriptor */
|
|
CPU_DESCR *, entry /* (O) the decoded descriptor */
|
|
)
|
|
|
|
|
|
{
|
|
if ( selector_outside_GDT_LDT(selector, descr) )
|
|
TS(selector, FAULT_VALSS_CHG_SELECTOR);
|
|
|
|
read_descriptor_linear(*descr, entry);
|
|
|
|
/* do access check */
|
|
if ( GET_SELECTOR_RPL(selector) != priv ||
|
|
GET_AR_DPL(entry->AR) != priv )
|
|
TS(selector, FAULT_VALSS_CHG_ACCESS);
|
|
|
|
/* do type check */
|
|
switch ( descriptor_super_type(entry->AR) )
|
|
{
|
|
case EXPANDUP_WRITEABLE_DATA:
|
|
case EXPANDDOWN_WRITEABLE_DATA:
|
|
break; /* ok */
|
|
|
|
default:
|
|
TS(selector, FAULT_VALSS_CHG_BAD_SEG_TYPE); /* wrong type */
|
|
}
|
|
|
|
/* finally check it is present */
|
|
if ( GET_AR_P(entry->AR) == NOT_PRESENT )
|
|
SF(selector, FAULT_VALSS_CHG_NOTPRESENT);
|
|
}
|
|
|
|
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
|
|
/* Validate TSS selector. */
|
|
/* Take #GP(selector) or #TS(selector) if not valid TSS. */
|
|
/* Take #NP(selector) if TSS not present */
|
|
/* Return super type of TSS decscriptor. */
|
|
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
|
|
GLOBAL ISM32
|
|
validate_TSS
|
|
|
|
IFN3(
|
|
IU16, selector, /* (I) selector to be checked */
|
|
IU32 *, descr_addr, /* (O) address of related descriptor */
|
|
BOOL, is_switch /* (I) if true we are in task switch */
|
|
)
|
|
|
|
|
|
{
|
|
BOOL is_ok = TRUE;
|
|
IU8 AR;
|
|
ISM32 super;
|
|
|
|
/* must be in GDT */
|
|
if ( selector_outside_GDT(selector, descr_addr) )
|
|
{
|
|
is_ok = FALSE;
|
|
}
|
|
else
|
|
{
|
|
/* is it really an available TSS segment (is_switch false) or
|
|
is it really a busy TSS segment (is_switch true) */
|
|
AR = spr_read_byte((*descr_addr)+5);
|
|
super = descriptor_super_type((IU16)AR);
|
|
if ( ( !is_switch &&
|
|
(super == AVAILABLE_TSS || super == XTND_AVAILABLE_TSS) )
|
|
||
|
|
( is_switch &&
|
|
(super == BUSY_TSS || super == XTND_BUSY_TSS) ) )
|
|
; /* ok */
|
|
else
|
|
is_ok = FALSE;
|
|
}
|
|
|
|
/* handle invalid TSS */
|
|
if ( !is_ok )
|
|
{
|
|
if ( is_switch )
|
|
TS(selector, FAULT_VALTSS_SELECTOR);
|
|
else
|
|
GP(selector, FAULT_VALTSS_SELECTOR);
|
|
}
|
|
|
|
/* must be present */
|
|
if ( GET_AR_P(AR) == NOT_PRESENT )
|
|
NP(selector, FAULT_VALTSS_NP);
|
|
|
|
return super;
|
|
}
|