summaryrefslogtreecommitdiffstats
path: root/private/mvdm/softpc.new/base/ccpu386/c_xfer.c
blob: 4d5980331142e72b3a2924e1b7cbbc51098c5db5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
/*[

c_xfer.c

LOCAL CHAR SccsID[]="@(#)c_xfer.c	1.14 02/17/95";

Transfer of Control 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_xfer.h>
#include <c_page.h>
#include <fault.h>

/*
   Prototype our internal functions.
 */
LOCAL VOID read_call_gate
                       
IPT5(
	IU32, descr_addr,
	ISM32, super,
	IU16 *, selector,
	IU32 *, offset,
	IU8 *, count

   );

IMPORT IBOOL took_relative_jump;



/*
   =====================================================================
   INTERNAL FUNCTIONS STARTS HERE.
   =====================================================================
 */


/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Read call gate descriptor.                                         */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
LOCAL VOID
read_call_gate
       	    		    	    	    	                              
IFN5(
	IU32, descr_addr,	/* (I) memory address of call gate descriptor */
	ISM32, super,	/* (I) descriptor type
			       (CALL_GATE|XTND_CALL_GATE) */
	IU16 *, selector,	/* (O) selector retrieved from descriptor */
	IU32 *, offset,	/* (O) offset retrieved from descriptor */
	IU8 *, count	/* (O) count retrieved from descriptor */
    )


   {
   /*
      The format of a gate descriptor is:-

	 ===========================
      +1 |        LIMIT 15-0       | +0
	 ===========================
      +3 |        SELECTOR         | +2
	 ===========================
      +5 |     AR     |    COUNT   | +4
	 ===========================
      +7 |       LIMIT 31-16       | +6
	 ===========================
   */

   IU32 first_dword;
   IU32 second_dword;

   /* read in descriptor with minimum interaction with memory */
   first_dword  = spr_read_dword(descr_addr);
   second_dword = spr_read_dword(descr_addr+4);

   /* unpack selector */
   *selector = first_dword >> 16;

   /* unpack lower bits of offset */
   *offset = first_dword & WORD_MASK;

   /* unpack count */
   *count = second_dword & BYTE_MASK;

   if ( super == XTND_CALL_GATE )
      {
      /* unpack higer bits of offset */
      *offset = second_dword & ~WORD_MASK | *offset;

      *count &= 0x0f;   /* 4-bit double word count */
      SET_OPERAND_SIZE(USE32);   /* Gate Overrides all else. */
      }
   else
      {
      *count &= 0x1f;   /* 5-bit word count */
      SET_OPERAND_SIZE(USE16);  /* Gate Overrides all else. */
      }
   }


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


/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Update IP with relative offset. Check new IP is valid.             */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
update_relative_ip
       	          
IFN1(
	IU32, rel_offset	/* sign extended relative offset */
    )


   {
   IU32 new_dest;

   new_dest = GET_EIP() + rel_offset;

   if ( GET_OPERAND_SIZE() == USE16 )
      new_dest &= WORD_MASK;

#ifdef	TAKE_REAL_MODE_LIMIT_FAULT

   if ( new_dest > GET_CS_LIMIT() )
      GP((IU16)0, FAULT_RM_REL_IP_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.
       */

#ifdef TAKE_PROT_MODE_LIMIT_FAILURE

      /* The Soft486 EDL CPU does not take Protected Mode limit failues
       * for the instructions with relative offsets, Jxx, LOOPxx, JCXZ,
       * JMP rel and CALL rel, or instructions with near offsets,
       * JMP near and CALL near.
       * 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.
       */

   if ( GET_PE() == 1 && GET_VM() == 0 )
      {
      if ( new_dest > GET_CS_LIMIT() )
	 GP((IU16)0, FAULT_PM_REL_IP_CS_LIMIT);
      }

#endif /* TAKE_PROT_MODE_LIMIT_FAILURE */

#endif	/* TAKE_REAL_MODE_LIMIT_FAULT */

   SET_EIP(new_dest);
   took_relative_jump = TRUE;
   }

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Validate far call or far jump destination                          */
/* Take #GP if invalid or access check fail.                          */
/* Take #NP if not present.                                           */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
validate_far_dest
       		    		    	    	    	    	                                   
IFN6(
	IU16 *, cs,	/* (I/O) segment of target address */
	IU32 *, ip,	/* (I/O) offset  of target address */
	IU32 *, descr_addr,	/*   (O) related descriptor memory address */
	IU8 *, count,	/*   (O) call gate count(valid if CALL_GATE) */
	ISM32 *, dest_type,	/*   (O) destination type */
	ISM32, caller_id	/* (I)   bit mapped caller identifier */
    )


   {
   IU16 new_cs;
   IU32 new_ip;
   IU32 cs_descr_addr;
   IU8 AR;
   ISM32 super;

   new_cs = *cs;	/* take local copies */
   new_ip = *ip;

   *dest_type = SAME_LEVEL;   /* default to commonest type */

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

   /* load access rights */
   AR = spr_read_byte(cs_descr_addr+5);

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

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

      /* it must be present */
      if ( GET_AR_P(AR) == NOT_PRESENT )
	 NP(new_cs, FAULT_FAR_DEST_NP_NONCONFORM);
      break;
   
   case CALL_GATE:
   case XTND_CALL_GATE:
      /* Check gate present and access allowed */

      /* access check requires DPL >= RPL and DPL >= CPL */
      if (  GET_SELECTOR_RPL(new_cs) > GET_AR_DPL(AR) ||
	    GET_CPL() > GET_AR_DPL(AR) )
	 GP(new_cs, FAULT_FAR_DEST_ACCESS_3);

      if ( GET_AR_P(AR) == NOT_PRESENT )
	 NP(new_cs, FAULT_FAR_DEST_NP_CALLG);

      /* OK, get real destination from gate */
      read_call_gate(cs_descr_addr, super, &new_cs, &new_ip, count);

      validate_gate_dest(caller_id, new_cs, &cs_descr_addr, dest_type);
      break;
   
   case TASK_GATE:
      /* Check gate present and access allowed */

      /* access check requires DPL >= RPL and DPL >= CPL */
      if (  GET_SELECTOR_RPL(new_cs) > GET_AR_DPL(AR) ||
	    GET_CPL() > GET_AR_DPL(AR) )
	 GP(new_cs, FAULT_FAR_DEST_ACCESS_4);

      if ( GET_AR_P(AR) == NOT_PRESENT )
	 NP(new_cs, FAULT_FAR_DEST_NP_TASKG);

      /* OK, get real destination from gate */
      new_cs = spr_read_word(cs_descr_addr+2);

      /* Check out new destination */
      (void)validate_task_dest(new_cs, &cs_descr_addr);

      *dest_type = NEW_TASK;
      break;
   
   case AVAILABLE_TSS:
   case XTND_AVAILABLE_TSS:
      /* TSS must be in GDT */
      if ( GET_SELECTOR_TI(new_cs) == 1 )
	 GP(new_cs, FAULT_FAR_DEST_TSS_IN_LDT);

      /* access check requires DPL >= RPL and DPL >= CPL */
      if (  GET_SELECTOR_RPL(new_cs) > GET_AR_DPL(AR) ||
	    GET_CPL() > GET_AR_DPL(AR) )
	 GP(new_cs, FAULT_FAR_DEST_ACCESS_5);

      /* it must be present */
      if ( GET_AR_P(AR) == NOT_PRESENT )
	 NP(new_cs, FAULT_FAR_DEST_NP_TSS);

      *dest_type = NEW_TASK;
      break;
   
   default:
      GP(new_cs, FAULT_FAR_DEST_BAD_SEG_TYPE);   /* bad type for far destination */
      }

   *cs = new_cs;	/* Return final values */
   *ip = new_ip;
   *descr_addr = cs_descr_addr;
   }

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Validate transfer of control to a call gate destination.           */
/* Take #GP if invalid or access check fail.                          */
/* Take #NP if not present.                                           */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
validate_gate_dest
       	    		    	    	                         
IFN4(
	ISM32, caller_id,	/* (I) bit mapped caller identifier */
	IU16, new_cs,	/* (I) segment of target address */
	IU32 *, descr_addr,	/* (O) related descriptor memory address */
	ISM32 *, dest_type	/* (O) destination type */
    )


   {
   IU8 AR;

   *dest_type = SAME_LEVEL;	/* default */

   /* Check out new destination */
   if ( selector_outside_GDT_LDT(new_cs, descr_addr) )
      GP(new_cs, FAULT_GATE_DEST_SELECTOR);

   /* load access rights */
   AR = spr_read_byte((*descr_addr)+5);

   /* must be a code segment */
   switch ( descriptor_super_type((IU16)AR) )
      {
   case CONFORM_NOREAD_CODE:
   case CONFORM_READABLE_CODE:
      /* access check requires DPL <= CPL */
      if ( GET_AR_DPL(AR) > GET_CPL() )
	 GP(new_cs, FAULT_GATE_DEST_ACCESS_1);
      break;
   
   case NONCONFORM_NOREAD_CODE:
   case NONCONFORM_READABLE_CODE:
      /* access check requires DPL <= CPL */
      if ( GET_AR_DPL(AR) > GET_CPL() )
	 GP(new_cs, FAULT_GATE_DEST_ACCESS_2);

      /* but jumps must have DPL == CPL */
      if ( (caller_id & JMP_ID) && (GET_AR_DPL(AR) != GET_CPL()) )
	 GP(new_cs, FAULT_GATE_DEST_ACCESS_3);

      /* set MORE_PRIVILEGE(0|1|2) */
      if ( GET_AR_DPL(AR) < GET_CPL() )
	 *dest_type = GET_AR_DPL(AR);
      break;
   
   default:
      GP(new_cs, FAULT_GATE_DEST_BAD_SEG_TYPE);
      }

   if ( GET_VM() == 1 )
      {
      /*
	 We must be called by ISM32, so ensure we go to CPL 0 via
	 a 32-bit gate.
       */
      if ( *dest_type != MORE_PRIVILEGE0 || GET_OPERAND_SIZE() != USE32 )
	 GP(new_cs, FAULT_GATE_DEST_GATE_SIZE);
      }

   /* it must be present */
   if ( GET_AR_P(AR) == NOT_PRESENT )
      NP(new_cs, FAULT_GATE_DEST_NP);
   }

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Validate transfer of control to a task gate destination.           */
/* Take #GP if invalid or access check fail.                          */
/* Take #NP if not present.                                           */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL IMPORT ISM32
validate_task_dest
       	    	               
IFN2(
	IU16, selector,	/* (I) segment of target address */
	IU32 *, descr_addr	/* (O) related descriptor memory address */
    )


   {
   IU8 AR;
   ISM32 super;

   /* must be in GDT */
   if ( selector_outside_GDT(selector, descr_addr) )
      GP(selector, FAULT_TASK_DEST_SELECTOR);
   
   /* load access rights */
   AR = spr_read_byte((*descr_addr)+5);

   /* is it really an available TSS segment */
   super = descriptor_super_type((IU16)AR);
   if ( super == AVAILABLE_TSS || super == XTND_AVAILABLE_TSS )
      ; /* ok */
   else
      GP(selector, FAULT_TASK_DEST_NOT_TSS);

   /* it must be present */
   if ( GET_AR_P(AR) == NOT_PRESENT )
      NP(selector, FAULT_TASK_DEST_NP);
   return super;
   }