x86 exception priorities during loading and/or execution?

Emulation of old PCs, PC hardware, or PC peripherals.

x86 exception priorities during loading and/or execution?

Postby superfury » 2018-7-31 @ 13:15

The 80386 programmer's reference manual describes various exceptions that occur because of varying reasons, but nothin is said about priorities between those?

What happens during simultaneous faults? In what order are the faults checked for when loading or using(during pointer dereferencing in any way) the descriptors(and Page faults)? Do Page faults have lowest priority(only when segmentations checks succeed)? What about during segment loading into segment register descriptor cache? Nothing is said about checking order as far as I can see?
superfury
l33t
 
Posts: 3278
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: x86 exception priorities during loading and/or execution?

Postby peterferrie » 2018-8-01 @ 18:15

What would be a simultaneous fault in your example? It seems more like the operations follow an order for which exceptions can happen only in that strict order.
peterferrie
Oldbie
 
Posts: 638
Joined: 2008-5-08 @ 21:54

Re: x86 exception priorities during loading and/or execution?

Postby BloodyCactus » 2018-8-01 @ 19:44

doesnt a simultaenous fault lead to a triple fault and double fault regardless of order.
--/\-[ Stu : Bloody Cactus :: http://kråketær.com :: http://mega-tokyo.com ]-/\--
User avatar
BloodyCactus
Oldbie
 
Posts: 925
Joined: 2016-2-03 @ 13:34
Location: Lexington VA

Re: x86 exception priorities during loading and/or execution?

Postby superfury » 2018-8-02 @ 05:08

What I mean in this case is, what happens when, let's say, a data fetch triggers both a Page Fault(unmapped) and General Protection Fault(Offset invalid or anything else not allowed)? Does the #GP fault or #PF get triggered?

The same about the order of checks described within the segment descriptor loading process. I'd assume Page faults have priority over General Protection/Stack faults(due to invalid bits unloaded)?

In what order are the protections during the descriptor loading process handled? Does it matter what order they're handled in, to software? I'd assume the Present bit has priority over all, but for the other protection bits I don't know?

Nothing is mentioned in the documentation about any order with fault handling? I'd assume segmentation first, then paging last?

Edit: Now that I think about it: can general protection faults occur while loading a segment descriptor into SS? Or are they stack faults instead?

Current segment descriptor handling:
Code: Select all
/*

getsegment_seg: Gets a segment, if allowed.
parameters:
   whatsegment: What segment is used?
   segment: The segment to get.
   isJMPorCALL: 0 for normal segment setting. 1 for JMP, 2 for CALL, 3 for IRET.
result:
   The segment when available, NULL on error or disallow.

*/

SEGMENT_DESCRIPTOR *getsegment_seg(int segment, SEGMENT_DESCRIPTOR *dest, word *segmentval, byte isJMPorCALL, byte *isdifferentCPL) //Get this corresponding segment descriptor (or LDT. For LDT, specify LDT register as segment) for loading into the segment descriptor cache!
{
   //byte newCPL = getCPL(); //New CPL after switching! Default: no change!
   SEGDESCRIPTOR_TYPE LOADEDDESCRIPTOR, GATEDESCRIPTOR; //The descriptor holder/converter!
   word originalval=*segmentval; //Back-up of the original segment value!
   byte allowNP; //Allow #NP to be used?
   sbyte loadresult;

   if ((*segmentval&4) && (GENERALSEGMENT_P(CPU[activeCPU].SEG_DESCRIPTOR[CPU_SEGMENT_LDTR])==0) && (segment!=CPU_SEGMENT_LDTR)) //Invalid LDT segment and LDT is addressed?
   {
      THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw error!
      return NULL; //We're an invalid TSS to execute!
   }

   if ((loadresult = LOADDESCRIPTOR(segment,*segmentval,&LOADEDDESCRIPTOR,isJMPorCALL))<=0) //Error loading current descriptor?
   {
      if (loadresult == 0) //Not already faulted?
      {
         THROWDESCGP(*segmentval, 1, (*segmentval & 4) ? EXCEPTION_TABLE_LDT : EXCEPTION_TABLE_GDT); //Throw #GP error!
      }
      return NULL; //Error, by specified reason!
   }
   allowNP = ((segment==CPU_SEGMENT_DS) || (segment==CPU_SEGMENT_ES) || (segment==CPU_SEGMENT_FS) || (segment==CPU_SEGMENT_GS)); //Allow segment to be marked non-present(exception: values 0-3 with data segments)?
   byte equalprivilege = 0; //Special gate stuff requirement: DPL must equal CPL? 1 for enable, 0 for normal handling.
   byte privilegedone = 0; //Privilege already calculated?
   byte is_gated = 0;
   byte is_TSS = 0; //Are we a TSS?
   byte callgatetype = 0; //Default: no call gate!

   if (((*segmentval&~3)==0)) //NULL GDT segment when not allowed?
   {
      if (segment==CPU_SEGMENT_LDTR) //in LDTR? We're valid!
      {
         memset(&LOADEDDESCRIPTOR,0,sizeof(LOADEDDESCRIPTOR)); //Allow!
         goto validLDTR; //Skip all checks, and check out as valid! We're allowed on the LDTR only!
      }
      else //Skip checks: we're invalid to check any further!
      {
         if ((segment==CPU_SEGMENT_CS) || (segment==CPU_SEGMENT_TR) || (segment==CPU_SEGMENT_SS)) //Not allowed?
         {
            THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw #GP error!
            return NULL; //Error, by specified reason!
         }
         else if (allowNP)
         {
            memset(&LOADEDDESCRIPTOR,0,sizeof(LOADEDDESCRIPTOR)); //Allow!
            goto validLDTR; //Load NULL descriptor!
         }
      }
   }

   if (GENERALSEGMENT_P(LOADEDDESCRIPTOR.desc)==0) //Not present loaded into non-data segment register?
   {
      if (segment==CPU_SEGMENT_SS) //Stack fault?
      {
         THROWDESCSP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Stack fault!
      }
      else
      {
         THROWDESCNP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw error!
      }
      return NULL; //We're an invalid TSS to execute!
   }

   if (isGateDescriptor(&LOADEDDESCRIPTOR)==0) //Invalid descriptor?
   {
      THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw #GP error!
      return NULL; //We're an invalid descriptor to use!
   }

   if ((isGateDescriptor(&LOADEDDESCRIPTOR)==1) && (segment == CPU_SEGMENT_CS) && isJMPorCALL) //Handling of gate descriptors?
   {
      is_gated = 1; //We're gated!
      memcpy(&GATEDESCRIPTOR, &LOADEDDESCRIPTOR, sizeof(GATEDESCRIPTOR)); //Copy the loaded descriptor to the GATE!
      //Check for invalid loads!
      switch (GENERALSEGMENT_TYPE(GATEDESCRIPTOR.desc))
      {
      default: //Unknown type?
      case AVL_SYSTEM_INTERRUPTGATE16BIT:
      case AVL_SYSTEM_TRAPGATE16BIT:
      case AVL_SYSTEM_INTERRUPTGATE32BIT:
      case AVL_SYSTEM_TRAPGATE32BIT:
         //We're an invalid gate!
         THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw #GP error!      
         return NULL; //Not present: invalid descriptor type loaded!
         break;
      case AVL_SYSTEM_TASKGATE: //Task gate?
      case AVL_SYSTEM_CALLGATE16BIT:
      case AVL_SYSTEM_CALLGATE32BIT:
         //Valid gate! Allow!
         break;
      }
      if ((MAX(getCPL(), getRPL(*segmentval)) > GENERALSEGMENT_DPL(GATEDESCRIPTOR.desc)) && (isJMPorCALL!=3)) //Gate has too high a privilege level? Only when not an IRET(always allowed)!
      {
         THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw error!
         return NULL; //We are a lower privilege level, so don't load!            
      }
      *segmentval = (GATEDESCRIPTOR.desc.selector & ~3) | (*segmentval & 3); //We're loading this segment now, with requesting privilege!
      if ((loadresult = LOADDESCRIPTOR(segment, *segmentval, &LOADEDDESCRIPTOR,isJMPorCALL))<=0) //Error loading current descriptor?
      {
         if (loadresult == 0) //Not faulted already?
         {
            THROWDESCGP(*segmentval, 1, (*segmentval & 4) ? EXCEPTION_TABLE_LDT : EXCEPTION_TABLE_GDT); //Throw error!
         }
         return NULL; //Error, by specified reason!
      }
      if (isGateDescriptor(&LOADEDDESCRIPTOR)==0) //Invalid descriptor?
      {
         THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw #GP error!
         return NULL; //We're an invalid descriptor to use!
      }
      privilegedone = 1; //Privilege has been precalculated!
      if (GENERALSEGMENT_TYPE(GATEDESCRIPTOR.desc) == AVL_SYSTEM_TASKGATE) //Task gate?
      {
         if (segment != CPU_SEGMENT_CS) //Not code? We're not a task switch! We're trying to load the task segment into a data register. This is illegal! TR doesn't support Task Gates directly(hardware only)!
         {
            THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw error!
            return NULL; //Don't load!
         }
      }
      else //Normal descriptor?
      {
         if ((isJMPorCALL == 1) && !EXECSEGMENT_C(LOADEDDESCRIPTOR.desc)) //JMP to a nonconforming segment?
         {
            if (GENERALSEGMENT_DPL(LOADEDDESCRIPTOR.desc) != getCPL()) //Different CPL?
            {
               THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw error!
               return NULL; //We are a different privilege level, so don't load!                  
            }
         }
         else if (isJMPorCALL) //Call instruction (or JMP instruction to a conforming segment)
         {
            if (GENERALSEGMENT_DPL(LOADEDDESCRIPTOR.desc) > getCPL()) //We have a lower CPL?
            {
               THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw error!
               return NULL; //We are a different privilege level, so don't load!
            }
         }
      }
   }
   else if ((isGateDescriptor(&LOADEDDESCRIPTOR)==-1) && (segment==CPU_SEGMENT_CS) && isJMPorCALL) //JMP/CALL to non-gate descriptor(and not a system segment)?
   {
      equalprivilege = 1; //Enforce equal privilege!
   }

   //Final descriptor safety check!
   if (isGateDescriptor(&LOADEDDESCRIPTOR)==0) //Invalid descriptor?
   {
      THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw #GP error!
      return NULL; //We're an invalid descriptor to use!
   }

   if (
      (
      (segment==CPU_SEGMENT_SS) ||
      (segment==CPU_SEGMENT_DS) ||
      (segment==CPU_SEGMENT_ES) ||
      (segment==CPU_SEGMENT_FS) ||
      (segment==CPU_SEGMENT_GS) //SS,DS,ES,FS,GS are ...
      ) &&
      (
      (getLoadedTYPE(&LOADEDDESCRIPTOR)==2) || //A System segment? OR ...
      ((getLoadedTYPE(&LOADEDDESCRIPTOR)==1) && (EXECSEGMENT_R(LOADEDDESCRIPTOR.desc)==0)) //An execute-only code segment?
      )
      )
   {
      THROWDESCGP(originalval,1,(originalval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw error!
      return NULL; //Not present: limit exceeded!   
   }
   
   switch (GENERALSEGMENT_TYPE(LOADEDDESCRIPTOR.desc)) //We're a TSS? We're to perform a task switch!
   {
   case AVL_SYSTEM_BUSY_TSS16BIT:
   case AVL_SYSTEM_TSS16BIT: //TSS?
   case AVL_SYSTEM_BUSY_TSS32BIT:
   case AVL_SYSTEM_TSS32BIT: //TSS?
      is_TSS = (getLoadedTYPE(&LOADEDDESCRIPTOR)==2); //We're a TSS when a system segment!
      break;
   default:
      is_TSS = 0; //We're no TSS!
      break;
   }

   if (is_TSS && (segment==CPU_SEGMENT_CS) && (isJMPorCALL==3)) //IRET allowed regardless of privilege?
   {
      privilegedone = 1; //Allow us always!
   }

   //Now check for CPL,DPL&RPL! (chapter 6.3.2)
   if (
      (
      (!privilegedone && !equalprivilege && (MAX(getCPL(),getRPL(*segmentval))>GENERALSEGMENT_DPL(LOADEDDESCRIPTOR.desc)) && ((!(EXECSEGMENT_ISEXEC(LOADEDDESCRIPTOR.desc) && EXECSEGMENT_C(LOADEDDESCRIPTOR.desc) && getLoadedTYPE(&LOADEDDESCRIPTOR)==1)))) || //We are a lower privilege level with either non-conforming or a data/system segment descriptor?
      ((!privilegedone && equalprivilege && MAX(getCPL(),getRPL(*segmentval))!=GENERALSEGMENT_DPL(LOADEDDESCRIPTOR.desc)) && //We must be at the same privilege level?
         !(EXECSEGMENT_C(LOADEDDESCRIPTOR.desc)) //Not conforming checking further ahead makes sure that we don't double check things?
         )
      )
      && (!((isJMPorCALL==3) && is_TSS)) //No privilege checking is done on IRET through TSS!
      )
   {
      THROWDESCGP(originalval,1,(originalval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw error!
      return NULL; //We are a lower privilege level, so don't load!
   }

   if (is_TSS && (segment==CPU_SEGMENT_TR)) //We're a TSS loading into TR? We're to perform a task switch!
   {
      if (*segmentval & 2) //LDT lookup set?
      {
         THROWDESCGP(originalval,1,(originalval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw error!
         return NULL; //We're an invalid TSS to call!
      }
      //Handle the task switch normally! We're allowed to use the TSS!
   }

   if ((GENERALSEGMENT_P(LOADEDDESCRIPTOR.desc)==0) && ((segment==CPU_SEGMENT_CS) || (segment==CPU_SEGMENT_SS) || (segment==CPU_SEGMENT_TR) || (*segmentval&~3))) //Not present loaded into non-data register?
   {
      if (segment==CPU_SEGMENT_SS) //Stack fault?
      {
         THROWDESCSP(originalval,1,(originalval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw error!
      }
      else
      {
         THROWDESCNP(originalval,1,(originalval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw error!
      }
      return NULL; //We're an invalid TSS to execute!
   }

   if ((segment==CPU_SEGMENT_CS) && is_TSS) //Special stuff on CS, CPL, Task switch.
   {
      //Execute a normal task switch!
      if (CPU_executionphase_starttaskswitch(segment,&LOADEDDESCRIPTOR,segmentval,*segmentval,isJMPorCALL,is_gated,-1)) //Switching to a certain task?
      {
         return NULL; //Error changing priviledges or anything else!
      }

      //We've properly switched to the destination task! Continue execution normally!
      return NULL; //Don't actually load CS with the descriptor: we've caused a task switch after all!
   }

   if ((segment == CPU_SEGMENT_CS) && (is_gated==0)) //Special stuff on normal CS register (conforming?), CPL.
   {
      if (EXECSEGMENT_C(LOADEDDESCRIPTOR.desc)) //Conforming segment?
      {
         if ((!privilegedone) && (GENERALSEGMENT_DPL(LOADEDDESCRIPTOR.desc)<MAX(getCPL(),getRPL(*segmentval)))) //Target DPL must be less-or-equal to the CPL.
         {
            THROWDESCGP(originalval,1,(originalval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw error!
            return NULL; //We are a lower privilege level, so don't load!            
         }
      }
      else //Non-conforming segment?
      {
         if ((!privilegedone) && (GENERALSEGMENT_DPL(LOADEDDESCRIPTOR.desc)!=MAX(getCPL(),getRPL(*segmentval)))) //Check for equal only when using Gate Descriptors?
         {
            THROWDESCGP(originalval,1,(originalval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw error!
            return NULL; //We are a lower privilege level, so don't load!            
         }
      }
   }

   if ((segment == CPU_SEGMENT_CS) && (isGateDescriptor(&GATEDESCRIPTOR) == 1) && (is_gated)) //Gated CS?
   {
      switch (GENERALSEGMENT_TYPE(GATEDESCRIPTOR.desc)) //What type of gate are we using?
      {
      case AVL_SYSTEM_CALLGATE16BIT: //16-bit call gate?
         callgatetype = 1; //16-bit call gate!
         break;
      case AVL_SYSTEM_CALLGATE32BIT: //32-bit call gate?
         callgatetype = 2; //32-bit call gate!
         break;
      default:
         callgatetype = 0; //No call gate!
         break;
      }
      if (callgatetype) //To process a call gate's parameters and offsets?
      {
         destEIP = GATEDESCRIPTOR.desc.callgate_base_low; //16-bit EIP!
         if (callgatetype == 2) //32-bit destination?
         {
            destEIP |= (GATEDESCRIPTOR.desc.callgate_base_mid<<16); //Mid EIP!
            destEIP |= (GATEDESCRIPTOR.desc.callgate_base_high<<24); //High EIP!
         }
         uint_32 argument; //Current argument to copy to the destination stack!
         word arguments;
         CPU[activeCPU].CallGateParamCount = 0; //Clear our stack to transfer!
         CPU[activeCPU].CallGateSize = (callgatetype==2)?1:0; //32-bit vs 16-bit call gate!

         if ((GENERALSEGMENT_DPL(LOADEDDESCRIPTOR.desc)!=getCPL()) && (isJMPorCALL==2)) //Stack switch required (with CALL only)?
         {
            //Backup the old stack data!
            /*
            CPU[activeCPU].have_oldESP = 1;
            CPU[activeCPU].have_oldSS = 1;
            CPU[activeCPU].oldESP = REG_ESP; //Backup!
            CPU[activeCPU].oldSS = REG_SS; //Backup!
            */ //Dont automatically at the start of the instruction!
            //Now, copy the stack arguments!

            *isdifferentCPL = 1; //We're a different level!
            arguments = CALLGATE_NUMARGUMENTS =  (GATEDESCRIPTOR.desc.ParamCnt&0x1F); //Amount of parameters!
            CPU[activeCPU].CallGateParamCount = 0; //Initialize the amount of arguments that we're storing!
            if (checkStackAccess(arguments,0,(callgatetype==2)?1:0)) return NULL; //Abort on stack fault!
            for (;arguments--;) //Copy as many arguments as needed!
            {
               if (callgatetype==2) //32-bit source?
               {
                  argument = MMU_rdw(CPU_SEGMENT_SS, CPU[activeCPU].registers->SS, CPU[activeCPU].registers->ESP&getstackaddrsizelimiter(), 0,!STACK_SEGMENT_DESCRIPTOR_B_BIT()); //POP 32-bit argument!
                  if (STACK_SEGMENT_DESCRIPTOR_B_BIT()) //32-bits?
                  {
                     CPU[activeCPU].registers->ESP += 4; //Increase!
                  }
                  else //16-bits?
                  {
                     CPU[activeCPU].registers->SP += 4; //Increase!
                  }
               }
               else //16-bit source?
               {
                  argument = MMU_rw(CPU_SEGMENT_SS, CPU[activeCPU].registers->SS, (CPU[activeCPU].registers->ESP&getstackaddrsizelimiter()), 0,!STACK_SEGMENT_DESCRIPTOR_B_BIT()); //POP 16-bit argument!
                  if (STACK_SEGMENT_DESCRIPTOR_B_BIT()) //32-bits?
                  {
                     CPU[activeCPU].registers->ESP += 2; //Increase!
                  }
                  else //16-bits?
                  {
                     CPU[activeCPU].registers->SP += 2; //Increase!
                  }
               }
               CPU[activeCPU].CallGateStack[CPU[activeCPU].CallGateParamCount++] = argument; //Add the argument to the call gate buffer to transfer to the new stack! Implement us as a LIFO for transfers!
            }

            CPU[activeCPU].CPL = GENERALSEGMENT_DPL(LOADEDDESCRIPTOR.desc); //Changing privilege to this!
         }
         else
         {
            *isdifferentCPL = 2; //Indicate call gate determines operand size!
         }
      }
   }

   //Handle invalid types now!
   if ((segment==CPU_SEGMENT_CS) &&
      (getLoadedTYPE(&LOADEDDESCRIPTOR)!=1) //Data or System in CS (non-exec)?
      )
   {
      THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw #GP error!      
      return NULL; //Not present: invalid descriptor type loaded!
   }
   else if ((getLoadedTYPE(&LOADEDDESCRIPTOR)==1) && (segment!=CPU_SEGMENT_CS) && (EXECSEGMENT_R(LOADEDDESCRIPTOR.desc)==0)) //Executable non-readable in non-executable segment?
   {
      THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw #GP error!      
      return NULL; //Not present: invalid descriptor type loaded!
   }
   else if ((getLoadedTYPE(&LOADEDDESCRIPTOR)==1) && ((segment==CPU_SEGMENT_LDTR) || (segment==CPU_SEGMENT_TR))) //Executable segment loaded invalid?
   {
      THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw #GP error!      
      return NULL; //Not present: invalid descriptor type loaded!
   }
   else if (getLoadedTYPE(&LOADEDDESCRIPTOR)==0) //Data descriptor loaded?
   {
      if (((segment!=CPU_SEGMENT_DS) && (segment!=CPU_SEGMENT_ES) && (segment!=CPU_SEGMENT_FS) && (segment!=CPU_SEGMENT_GS) && (segment!=CPU_SEGMENT_SS))) //Data descriptor in invalid type?
      {
         THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw #GP error!      
         return NULL; //Not present: invalid descriptor type loaded!
      }
      if ((DATASEGMENT_W(LOADEDDESCRIPTOR.desc)==0) && (segment==CPU_SEGMENT_SS)) //Non-writable SS segment?
      {
         THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw #GP error!      
         return NULL; //Not present: invalid descriptor type loaded!
      }
   }
   else if (getLoadedTYPE(&LOADEDDESCRIPTOR)==2) //System descriptor loaded?
   {
      if ((segment==CPU_SEGMENT_CS) || (segment==CPU_SEGMENT_DS) || (segment==CPU_SEGMENT_ES) || (segment==CPU_SEGMENT_FS) || (segment==CPU_SEGMENT_GS) || (segment==CPU_SEGMENT_SS)) //System descriptor in invalid register?
      {
         THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw #GP error!      
         return NULL; //Not present: invalid descriptor type loaded!
      }
      if ((segment==CPU_SEGMENT_LDTR) && (GENERALSEGMENT_TYPE(LOADEDDESCRIPTOR.desc)!=AVL_SYSTEM_LDT)) //Invalid LDT load?
      {
         THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw #GP error!      
         return NULL; //Not present: invalid descriptor type loaded!
      }
      if ((segment==CPU_SEGMENT_TR) && (is_TSS==0)) //Non-TSS into task register?
      {
         THROWDESCGP(*segmentval,1,(*segmentval&4)?EXCEPTION_TABLE_LDT:EXCEPTION_TABLE_GDT); //Throw #GP error!      
         return NULL; //Not present: invalid descriptor type loaded!
      }
   }

   if (segment==CPU_SEGMENT_CS) //We need to reload a new CPL?
   {
      //Non-gates doesn't change RPL(CPL) of CS! (IRET?&)RETF does change CPL here(already done beforehand)!
      if ((is_gated==0) && (isJMPorCALL==4)) //RETF changes privilege level?
      {
         if (getRPL(*segmentval)>getCPL()) //Lowered?
         {
            CPU[activeCPU].CPL = getRPL(*segmentval); //New CPL, lowered!
            setRPL(*segmentval,CPU[activeCPU].CPL); //Only gated loads(CALL gates) can change RPL(active lowest CPL in CS). Otherwise, it keeps the old RPL.
            setRPL(originalval,CPU[activeCPU].CPL); //Only gated loads(CALL gates) can change RPL(active lowest CPL in CS). Otherwise, it keeps the old RPL.
         }
      }
   }

   validLDTR:
   memcpy(dest,&LOADEDDESCRIPTOR,sizeof(LOADEDDESCRIPTOR)); //Give the loaded descriptor!

   return dest; //Give the segment descriptor read from memory!
}
superfury
l33t
 
Posts: 3278
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: x86 exception priorities during loading and/or execution?

Postby peterferrie » 2018-8-03 @ 18:03

I think that the #GPF would occur before the #PF especially when the access style is illegal, and no memory is actually accessed in that case.
i.e. issue a fault as soon as possible.
peterferrie
Oldbie
 
Posts: 638
Joined: 2008-5-08 @ 21:54

Re: x86 exception priorities during loading and/or execution?

Postby superfury » 2018-8-03 @ 20:22

And Page faults during loading direct or gated descriptors? Do they occur immediately at the start of loading a segment descriptor, or after the loading?

So what happens when:
- Non-present descriptor at unpaged memory. #GP/#SS fault?
- Present gate descriptor pointing to unpaged descriptor in memory. #PF fault?
- Present gate descriptor pointing to invalid descriptor in memory. #GP fault?
- Order of various privilege checks on the descriptor to load into the cache(various privilege right checks documented don't have any order of checking mentioned). #GP fault always?

The last step is currently handled after the first/third step(depending on descriptor being a gate or not), although nothing is documented on priority between those checks(does it matter???).

What about #GP vs #NP fault? Which does the Present bit being zero trigger?
superfury
l33t
 
Posts: 3278
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: x86 exception priorities during loading and/or execution?

Postby peterferrie » 2018-8-04 @ 23:43

I think that #NP would be favoured over #PF since if it's not present then there's no reason to fetch anything.
As for the others, your order is probably correct.
peterferrie
Oldbie
 
Posts: 638
Joined: 2008-5-08 @ 21:54

Re: x86 exception priorities during loading and/or execution?

Postby superfury » 2019-2-28 @ 22:25

Just improved the remaining cases of #GP/#TS vs #NP/#SS for non-present descriptors(#NP/#SS(notpresent) having the lowest priority of all) for the remainder of the cases. The only thing that now still has lower priority than #NP/SS(notpresent) is the gated stack transfer(using a call gate to a higher privilege level with a CALL instruction).
superfury
l33t
 
Posts: 3278
Joined: 2014-3-08 @ 11:25
Location: Netherlands


Return to PC Emulation

Who is online

Users browsing this forum: No registered users and 0 guests