summaryrefslogblamecommitdiffstats
path: root/private/mvdm/softpc.new/base/ccpu386/c_seg.c
blob: f98d4336e7c53336c4516ef786a8781384af8fee (plain) (tree)











































































































































































































































































































































































































                                                                             
/*[

c_seg.c

LOCAL CHAR SccsID[]="@(#)c_seg.c	1.10 03/02/95";

Segment Register 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_page.h>
#include <fault.h>


/*
   =====================================================================
   EXTERNAL ROUTINES STARTS HERE.
   =====================================================================
 */


/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Load CS, both selector and hidden cache. Selector must be valid.   */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
load_CS_cache
       	    	    		                    
IFN3(
	IU16, selector,	/* (I) 16-bit selector to code segment */
	IU32, descr_addr,	/* (I) address of code segment descriptor */
	CPU_DESCR *, entry	/* (I) the decoded descriptor */
    )


   {
   if ( GET_PE() == 0 || GET_VM() == 1 )
      {
      /* Real Mode or V86 Mode */
      SET_CS_SELECTOR(selector);
      SET_CS_BASE((IU32)selector << 4);

      /* LIMIT is untouched. (cf 80386 PRM Pg14-4) */
      /*                     (cf  i486 PRM Pg22-4) */

      /* But access rights are updated */
      SET_CS_AR_W(1);     /* allow write access */
      SET_CS_AR_R(1);     /* allow read access */
      SET_CS_AR_E(0);     /* expand up */
      SET_CS_AR_C(0);     /* not conforming */
      SET_CS_AR_X(0);     /* not big (16-bit) */

      if ( GET_VM() == 1 )
	 SET_CS_AR_DPL(3);
      else
	 SET_CS_AR_DPL(0);
      }
   else
      {
      /* Protected Mode */

      /* show segment has been accessed (i486 only writes if changed) */
#ifdef	SPC486
      if ((entry->AR & ACCESSED) == 0)
#endif	/* SPC486 */
	 spr_write_byte(descr_addr+5, (IU8)entry->AR | ACCESSED);
      entry->AR |= ACCESSED;

      /* the visible bit */
      SET_CS_SELECTOR(selector);

      /* load hidden cache */
      SET_CS_BASE(entry->base);
      SET_CS_LIMIT(entry->limit);
			      /* load attributes from descriptor */
      SET_CS_AR_DPL(GET_AR_DPL(entry->AR));
      SET_CS_AR_R(GET_AR_R(entry->AR));
      SET_CS_AR_C(GET_AR_C(entry->AR));
      SET_CS_AR_X(GET_AR_X(entry->AR));

      SET_CS_AR_E(0);   /* expand up */
      SET_CS_AR_W(0);   /* deny write */
      }
   }

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Load SS, both selector and hidden cache. Selector must be valid.   */
/* Only invoked in protected mode.                                    */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
load_SS_cache
       	    	    		                    
IFN3(
	IU16, selector,	/* (I) 16-bit selector to stack segment */
	IU32, descr_addr,	/* (I) address of stack segment descriptor */
	CPU_DESCR *, entry	/* (I) the decoded descriptor */
    )


   {
   /* show segment has been accessed (i486 only writes if changed) */
#ifdef	SPC486
   if ((entry->AR & ACCESSED) == 0)
#endif	/* SPC486 */
      spr_write_byte(descr_addr+5, (IU8)entry->AR | ACCESSED);
   entry->AR |= ACCESSED;

   /* the visible bit */
   SET_SS_SELECTOR(selector);

   /* load hidden cache */
   SET_SS_BASE(entry->base);
   SET_SS_LIMIT(entry->limit);
			   /* load attributes from descriptor */
   SET_SS_AR_DPL(GET_AR_DPL(entry->AR));
   SET_SS_AR_E(GET_AR_E(entry->AR));
   SET_SS_AR_X(GET_AR_X(entry->AR));

   SET_SS_AR_W(1);   /* must be writeable */
   SET_SS_AR_R(1);   /* must be readable */
   SET_SS_AR_C(0);   /* not conforming */
   }

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Load CS selector.                                                  */
/* Take #GP if segment not valid                                      */
/* Take #NP if segment not present                                    */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
load_code_seg
                 
IFN1(
	IU16, new_cs
    )


   {
   IU32 cs_descr_addr;	/* code segment descriptor address */
   CPU_DESCR cs_entry;	/* code segment descriptor entry */

   /*
      Given that the CPU should be started from a valid state, we
      check CS selectors as if a far call to the same privilege
      level was being generated. This is in effect saying yes the
      CS could have been loaded by a valid Intel instruction.
      This logic may have to be revised if strange LOADALL usage is
      found.
    */

   if ( GET_PE() == 0 || GET_VM() == 1 )
      {
      /* Real Mode or V86 Mode */
      load_CS_cache(new_cs, (IU32)0, (CPU_DESCR *)0);
      }
   else
      {
      /* Protected Mode */

      if ( selector_outside_GDT_LDT(new_cs, &cs_descr_addr) )
	 GP(new_cs, FAULT_LOADCS_SELECTOR);

      /* load descriptor */
      read_descriptor_linear(cs_descr_addr, &cs_entry);

      /* validate possible types of target */
      switch ( descriptor_super_type(cs_entry.AR) )
	 {
      case CONFORM_NOREAD_CODE:
      case CONFORM_READABLE_CODE:
	 /* access check requires DPL <= CPL */
	 if ( GET_AR_DPL(cs_entry.AR) > GET_CPL() )
	    GP(new_cs, FAULT_LOADCS_ACCESS_1);

	 /* it must be present */
	 if ( GET_AR_P(cs_entry.AR) == NOT_PRESENT )
	    NP(new_cs, FAULT_LOADCS_NOTPRESENT_1);
	 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(cs_entry.AR) != GET_CPL() )
	    GP(new_cs, FAULT_LOADCS_ACCESS_2);

	 /* it must be present */
	 if ( GET_AR_P(cs_entry.AR) == NOT_PRESENT )
	    NP(new_cs, FAULT_LOADCS_NOTPRESENT_2);
	 break;

      default:
	 GP(new_cs, FAULT_LOADCS_BAD_SEG_TYPE);
	 }

      /* stamp new selector with CPL */
      SET_SELECTOR_RPL(new_cs, GET_CPL());

      load_CS_cache(new_cs, cs_descr_addr, &cs_entry);
      }
   }

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Load A Data Segment Register. (DS, ES, FS, GS)                     */
/* Take #GP if segment not valid                                      */
/* Take #NP if segment not present                                    */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
load_data_seg
                          
IFN2(
	ISM32, indx,
	IU16, selector
    )


   {
   IU32 descr_addr;
   CPU_DESCR entry;
   ISM32 super;
   ISM32 dpl;
   BOOL is_data;

   if ( GET_PE() == 0 || GET_VM() == 1 )
      {
      /* Real Mode or V86 Mode */
      SET_SR_SELECTOR(indx, selector);
      SET_SR_BASE(indx, (IU32)selector << 4);
      }
   else
      {
      /* Protected Mode */
      if ( selector_is_null(selector) )
	 {
	 /* load is allowed - but later access will fail
	  * Since the program can not see the internal changes
	  * performed to achieve this, we make the behaviour
	  * match the easiest implementation in the A4CPU
	  */
	 SET_SR_SELECTOR(indx, selector);

	 /* the following lines were added to make the C-CPU behave like
	  * the Soft 486 CPU - an investigation is being made to see if this
	  * behaviour corresponds with the real i486 - this code may have to
	  * change.
	  */
	 SET_SR_BASE(indx, 0);
	 SET_SR_LIMIT(indx, 0);
         SET_SR_AR_W(indx, 0);
         SET_SR_AR_R(indx, 0);
	 }
      else
	 {
	 if ( selector_outside_GDT_LDT(selector, &descr_addr) )
	    GP(selector, FAULT_LOADDS_SELECTOR);

	 read_descriptor_linear(descr_addr, &entry);

	 /* check type */
	 switch ( super = descriptor_super_type(entry.AR) )
	    {
	 case CONFORM_READABLE_CODE:
	 case NONCONFORM_READABLE_CODE:
	    is_data = FALSE;
	    break;

	 case EXPANDUP_READONLY_DATA:
	 case EXPANDUP_WRITEABLE_DATA:
	 case EXPANDDOWN_READONLY_DATA:
	 case EXPANDDOWN_WRITEABLE_DATA:
	    is_data = TRUE;
	    break;
	 
	 default:
	    GP(selector, FAULT_LOADDS_BAD_SEG_TYPE);	/* bad type */
	    }

	 /* for data and non-conforming code the access check applies */
	 if ( super != CONFORM_READABLE_CODE )
	    {
	    /* access check requires CPL <= DPL and RPL <= DPL */
	    dpl = GET_AR_DPL(entry.AR);
	    if ( GET_CPL() > dpl || GET_SELECTOR_RPL(selector) > dpl )
	       GP(selector, FAULT_LOADDS_ACCESS);
	    }

	 /* must be present */
	 if ( GET_AR_P(entry.AR) == NOT_PRESENT )
	    NP(selector, FAULT_LOADDS_NOTPRESENT);

	 /* show segment has been accessed (i486 only writes if changed) */
#ifdef	SPC486
	 if ((entry.AR & ACCESSED) == 0)
#endif	/* SPC486 */
	    spr_write_byte(descr_addr+5, (IU8)entry.AR | ACCESSED);
	 entry.AR |= ACCESSED;

	 /* OK - load up */

	 /* the visible bit */
	 SET_SR_SELECTOR(indx, selector);

	 /* load hidden cache */
	 SET_SR_BASE(indx, entry.base);
	 SET_SR_LIMIT(indx, entry.limit);
				 /* load attributes from descriptor */
	 SET_SR_AR_DPL(indx, GET_AR_DPL(entry.AR));

	 if ( is_data )
	    {
	    SET_SR_AR_W(indx, GET_AR_W(entry.AR));
	    SET_SR_AR_E(indx, GET_AR_E(entry.AR));
	    SET_SR_AR_C(indx, 0);   /* not conforming */
	    }
	 else
	    {
	    SET_SR_AR_C(indx, GET_AR_C(entry.AR));
	    SET_SR_AR_W(indx, 0);   /* deny write access */
	    SET_SR_AR_E(indx, 0);   /* expand up */
	    }

	 SET_SR_AR_X(indx, GET_AR_X(entry.AR));

	 SET_SR_AR_R(indx, 1);   /* must be readable */
	 }
      }
   }

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Load Pseudo Descriptor Semantics for Real Mode or V86 Mode.        */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
load_pseudo_descr
       	          
IFN1(
	ISM32, index	/* index to segment register */
    )


   {
   SET_SR_LIMIT(index, 0xffff);
   SET_SR_AR_W(index, 1);     /* allow write access */
   SET_SR_AR_R(index, 1);     /* allow read access */
   SET_SR_AR_E(index, 0);     /* expand up */
   SET_SR_AR_C(index, 0);     /* not conforming */
   SET_SR_AR_X(index, 0);     /* not big (16-bit) */

   if ( GET_VM() == 1 )
      SET_SR_AR_DPL(index, 3);
   else
      SET_SR_AR_DPL(index, 0);
   }

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Load Stack Segment Register. (SS)                                  */
/* Take #GP if segment not valid                                      */
/* Take #SF if segment not present                                    */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
load_stack_seg
                 
IFN1(
	IU16, selector
    )


   {
   IU32 descr_addr;
   CPU_DESCR entry;

   if ( GET_PE() == 0 || GET_VM() == 1 )
      {
      /* Real Mode or V86 Mode */
      SET_SS_SELECTOR(selector);
      SET_SS_BASE((IU32)selector << 4);
      }
   else
      {
      /* Protected Mode */
      check_SS(selector, (ISM32)GET_CPL(), &descr_addr, &entry);
      load_SS_cache(selector, descr_addr, &entry);
      }
   }