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.
330 lines
8.3 KiB
330 lines
8.3 KiB
/*[
|
|
|
|
iret.c
|
|
|
|
LOCAL CHAR SccsID[]="@(#)iret.c 1.13 1/19/95";
|
|
|
|
IRET CPU Functions.
|
|
-------------------
|
|
|
|
]*/
|
|
|
|
|
|
#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 <iret.h>
|
|
#include <c_xfer.h>
|
|
#include <c_tsksw.h>
|
|
#include <c_page.h>
|
|
#include <fault.h>
|
|
|
|
|
|
|
|
/*
|
|
=====================================================================
|
|
INTERNAL ROUTINES STARTS HERE.
|
|
=====================================================================
|
|
*/
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* Intelligent support for writing (E)FLAGS. */
|
|
/*--------------------------------------------------------------------*/
|
|
LOCAL VOID
|
|
set_current_FLAGS
|
|
|
|
IFN1(
|
|
IU32, flags
|
|
)
|
|
|
|
|
|
{
|
|
if ( GET_OPERAND_SIZE() == USE16 )
|
|
setFLAGS(flags);
|
|
else /* USE32 */
|
|
c_setEFLAGS(flags);
|
|
}
|
|
|
|
|
|
/*
|
|
=====================================================================
|
|
EXTERNAL ROUTINES STARTS HERE.
|
|
=====================================================================
|
|
*/
|
|
|
|
|
|
GLOBAL VOID
|
|
IRET()
|
|
{
|
|
IU16 new_cs; /* The return destination */
|
|
IU32 new_ip;
|
|
|
|
IU32 new_flags; /* The new flags */
|
|
|
|
IU16 back_link; /* Task Return variables */
|
|
IU32 tss_descr_addr;
|
|
|
|
ISM32 dest_type; /* category for destination */
|
|
ISM32 privilege; /* return privilege level */
|
|
|
|
IU32 cs_descr_addr; /* code segment descriptor address */
|
|
CPU_DESCR cs_entry; /* code segment descriptor entry */
|
|
|
|
IU16 new_ss; /* The new stack */
|
|
IU32 new_sp;
|
|
|
|
IU16 new_data_selector; /* ES,DS,FS,GS selector */
|
|
|
|
IU32 ss_descr_addr; /* stack segment descriptor address */
|
|
CPU_DESCR ss_entry; /* stack segment descriptor entry */
|
|
|
|
if ( GET_PE() == 0 || GET_VM() == 1 )
|
|
{
|
|
/* Real Mode or V86 Mode */
|
|
|
|
/* must have (E)IP:CS:(E)FLAGS on stack */
|
|
validate_stack_exists(USE_SP, (ISM32)NR_ITEMS_3);
|
|
|
|
/* retrieve return destination and flags from stack */
|
|
new_ip = tpop(STACK_ITEM_1, NULL_BYTE_OFFSET);
|
|
new_cs = tpop(STACK_ITEM_2, NULL_BYTE_OFFSET);
|
|
new_flags = tpop(STACK_ITEM_3, NULL_BYTE_OFFSET);
|
|
|
|
#ifdef TAKE_REAL_MODE_LIMIT_FAULT
|
|
|
|
/* do ip limit check */
|
|
if ( new_ip > GET_CS_LIMIT() )
|
|
GP((IU16)0, FAULT_IRET_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);
|
|
|
|
change_SP((IS32)NR_ITEMS_3);
|
|
|
|
set_current_FLAGS(new_flags);
|
|
|
|
return;
|
|
}
|
|
|
|
/* PROTECTED MODE */
|
|
|
|
/* look for nested return, ie return to another task */
|
|
if ( GET_NT() == 1 )
|
|
{
|
|
/* NESTED RETURN - get old TSS */
|
|
back_link = spr_read_word(GET_TR_BASE());
|
|
(VOID)validate_TSS(back_link, &tss_descr_addr, TRUE);
|
|
switch_tasks(RETURNING, NOT_NESTING, back_link, tss_descr_addr,
|
|
GET_EIP());
|
|
|
|
/* limit check new IP (now in new task) */
|
|
if ( GET_EIP() > GET_CS_LIMIT() )
|
|
GP((IU16)0, FAULT_IRET_PM_TASK_CS_LIMIT);
|
|
|
|
return;
|
|
}
|
|
|
|
/* SAME TASK RETURN */
|
|
|
|
/* must have (E)IP:CS:(E)FLAGS on stack */
|
|
validate_stack_exists(USE_SP, (ISM32)NR_ITEMS_3);
|
|
|
|
/* retrieve return destination from stack */
|
|
new_ip = tpop(STACK_ITEM_1, NULL_BYTE_OFFSET);
|
|
new_cs = tpop(STACK_ITEM_2, NULL_BYTE_OFFSET);
|
|
new_flags = tpop(STACK_ITEM_3, NULL_BYTE_OFFSET);
|
|
|
|
if ( GET_CPL() != 0 )
|
|
new_flags = new_flags & ~BIT17_MASK; /* Clear new VM */
|
|
|
|
if ( new_flags & BIT17_MASK ) /* VM bit set? */
|
|
{
|
|
/*
|
|
Return to V86 Mode. Stack holds:-
|
|
|
|
===========
|
|
| EIP |
|
|
| | CS |
|
|
| EFLAGS |
|
|
| ESP |
|
|
| | SS |
|
|
| | ES |
|
|
| | DS |
|
|
| | FS |
|
|
| | GS |
|
|
===========
|
|
*/
|
|
|
|
validate_stack_exists(USE_SP, (ISM32)NR_ITEMS_9);
|
|
|
|
/* Check Instruction Pointer valid. */
|
|
if ( new_ip > (IU32)0xffff )
|
|
GP((IU16)0, FAULT_IRET_VM_CS_LIMIT);
|
|
|
|
/* ALL SYSTEMS GO */
|
|
c_setEFLAGS(new_flags); /* ensure VM set before segment loads */
|
|
|
|
SET_CPL(3); /* V86 privilege level */
|
|
load_CS_cache(new_cs, (IU32)0, (CPU_DESCR *)0);
|
|
SET_CS_LIMIT(0xffff);
|
|
|
|
SET_EIP(new_ip);
|
|
|
|
/* Retrieve new stack ESP:SS */
|
|
new_sp = tpop(STACK_ITEM_4, NULL_BYTE_OFFSET);
|
|
new_ss = tpop(STACK_ITEM_5, NULL_BYTE_OFFSET);
|
|
|
|
/* Retrieve and set up new data selectors */
|
|
new_data_selector = tpop(STACK_ITEM_6, NULL_BYTE_OFFSET);
|
|
load_data_seg(ES_REG, new_data_selector);
|
|
|
|
new_data_selector = tpop(STACK_ITEM_7, NULL_BYTE_OFFSET);
|
|
load_data_seg(DS_REG, new_data_selector);
|
|
|
|
new_data_selector = tpop(STACK_ITEM_8, NULL_BYTE_OFFSET);
|
|
load_data_seg(FS_REG, new_data_selector);
|
|
|
|
new_data_selector = tpop(STACK_ITEM_9, NULL_BYTE_OFFSET);
|
|
load_data_seg(GS_REG, new_data_selector);
|
|
|
|
/* Set up new stack */
|
|
load_stack_seg(new_ss);
|
|
set_current_SP(new_sp);
|
|
|
|
/* Set up pseudo descriptors */
|
|
load_pseudo_descr(SS_REG);
|
|
load_pseudo_descr(DS_REG);
|
|
load_pseudo_descr(ES_REG);
|
|
load_pseudo_descr(FS_REG);
|
|
load_pseudo_descr(GS_REG);
|
|
|
|
return;
|
|
}
|
|
|
|
/* decode action and further check stack */
|
|
privilege = GET_SELECTOR_RPL(new_cs);
|
|
if ( privilege < GET_CPL() )
|
|
{
|
|
GP(new_cs, FAULT_IRET_CS_ACCESS_1); /* you can't get to higher privilege */
|
|
}
|
|
else if ( privilege == GET_CPL() )
|
|
{
|
|
dest_type = SAME_LEVEL;
|
|
}
|
|
else
|
|
{
|
|
/* going to lower privilege */
|
|
/* must have (E)IP:CS, (E)FLAGS, (E)SP:SS on stack */
|
|
validate_stack_exists(USE_SP, (ISM32)NR_ITEMS_5);
|
|
dest_type = LOWER_PRIVILEGE;
|
|
}
|
|
|
|
if ( selector_outside_GDT_LDT(new_cs, &cs_descr_addr) )
|
|
GP(new_cs, FAULT_IRET_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 */
|
|
/* note that this even true when changing to outer rings - despite
|
|
what it says in the 80286 & i486 PRMs - this has been verified on
|
|
a real 80386 & i486 - Wayne 18th May 1994 */
|
|
if ( GET_AR_DPL(cs_entry.AR) > privilege )
|
|
GP(new_cs, FAULT_IRET_ACCESS_2);
|
|
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_IRET_ACCESS_3);
|
|
break;
|
|
|
|
default:
|
|
GP(new_cs, FAULT_IRET_BAD_SEG_TYPE);
|
|
}
|
|
|
|
if ( GET_AR_P(cs_entry.AR) == NOT_PRESENT )
|
|
NP(new_cs, FAULT_IRET_NP_CS);
|
|
|
|
/* action the target */
|
|
switch ( dest_type )
|
|
{
|
|
case SAME_LEVEL:
|
|
/* do ip limit checking */
|
|
if ( new_ip > cs_entry.limit )
|
|
GP((IU16)0, FAULT_IRET_PM_CS_LIMIT_1);
|
|
|
|
/* ALL SYSTEMS GO */
|
|
|
|
load_CS_cache(new_cs, cs_descr_addr, &cs_entry);
|
|
SET_EIP(new_ip);
|
|
|
|
change_SP((IS32)NR_ITEMS_3);
|
|
|
|
set_current_FLAGS(new_flags);
|
|
break;
|
|
|
|
case LOWER_PRIVILEGE:
|
|
/* check new stack */
|
|
new_ss = tpop(STACK_ITEM_5, NULL_BYTE_OFFSET);
|
|
check_SS(new_ss, privilege, &ss_descr_addr, &ss_entry);
|
|
|
|
/* do ip limit checking */
|
|
if ( new_ip > cs_entry.limit )
|
|
GP((IU16)0, FAULT_IRET_PM_CS_LIMIT_2);
|
|
|
|
/* ALL SYSTEMS GO */
|
|
|
|
load_CS_cache(new_cs, cs_descr_addr, &cs_entry);
|
|
SET_EIP(new_ip);
|
|
|
|
set_current_FLAGS(new_flags);
|
|
|
|
new_sp = tpop(STACK_ITEM_4, NULL_BYTE_OFFSET);
|
|
load_SS_cache(new_ss, ss_descr_addr, &ss_entry);
|
|
if ( GET_OPERAND_SIZE() == USE16 )
|
|
SET_SP (new_sp);
|
|
else
|
|
SET_ESP (new_sp);
|
|
|
|
SET_CPL(privilege);
|
|
|
|
/* 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;
|
|
}
|
|
}
|