simos_interface.c
17.7 KB
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
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
/*
* Copyright (C) 1996-1998 by the Board of Trustees
* of Leland Stanford Junior University.
*
* This file is part of the SimOS distribution.
* See LICENSE file for terms of the license.
*
*/
/*****************************************************************
* simos_interface.c
*
* This is the interface from simos into the detailed cpu simulator.
* Simos puts all of the state into a structure that can be directly
* loaded into the cpus data structures. When this level of simulation
* has completed, the new state will be restored and control will be
* passed back to simos. Mipsy will run until it has executed the
* number of instructions passed to MipsyEnter() or until MipsyExit()
* is invoked.
*
* $Author: blythe $
* $Date: 2002/05/29 01:09:10 $
*****************************************************************/
#include <stdio.h>
#include <bstring.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/prctl.h>
#include <sys/time.h>
#include <limits.h>
#include <sys/mman.h>
#include "simtypes.h"
#include "simutil.h"
#include "simmisc.h"
#include "firewall.h"
#include "simmagic.h"
#include "cpu_interface.h"
#include "gdb_interface.h"
#include "simstats.h"
#include "embra.h"
#include "driver.h"
#include "cp0.h"
#include "cache.h"
#include "clock.h"
#include "qc.h"
#include "mem_control.h"
#include "main_run.h"
#include "stats.h"
#include "debug.h"
#include "directory.h"
#include "translator.h"
#include "registry.h"
#include "embra_interface.h"
#include "hw_events.h"
#include "tc.h"
#include "params.h"
#include "embra.h"
#include "../../memsystems/memsys.h"
/* Local Functions */
static void Embra_FaultInject(int cpuNum, faultTypeEnum faultType);
static void DeadCpuCycleInc(int cpuNum, EventCallbackHdr *E, void* arg);
static void EmbraRemapInit(void);
int Embra_ResetCPU(int cpuNum);
int EmbraCurrentCpuNum(void);
VA EmbraCurrentPC(int cpuNum);
extern CPUState *SBase;
extern SimTime embraLastCount[SIM_MAXCPUS];
void EmbraTclInit(Tcl_Interp *interp)
{
ParamRegister("PARAM(CPU.Embra.MPinUP)", (char *)&embra.MPinUP, PARAM_BOOLEAN);
ParamRegister("PARAM(CPU.EmbraPage.UseETLB)", (char *)&embra.useETLB, PARAM_BOOLEAN);
ParamRegister("PARAM(CPU.EmbraCache.UseVQC)", (char *)&embra.useVQC, PARAM_BOOLEAN);
ParamRegister("PARAM(CPU.Embra.DisableStat)", (char *)&embra.stats, PARAM_BOOLEAN);
ParamRegister("PARAM(CPU.Embra.InlineQC)", (char *) &embra.inlineQC, PARAM_BOOLEAN);
ParamRegister("PARAM(CPU.Embra.TimeQuantum)", (char *)&embra.timeQuantum, PARAM_INT);
ParamRegister("PARAM(CPU.Embra.PeriodAnnInterval)",
(char *)&embra.periodicAnnInterval, PARAM_INT);
ParamRegister("PARAM(CPU.Embra.StatInterval)",
(char *)&embra.statInterval, PARAM_INT);
ParamRegister("PARAM(CPU.Embra.MiscCheckInterval)",
(char *)&embra.miscCheckInterval, PARAM_INT);
ParamRegister("PARAM(CPU.Embra.SeparateMMUs)",
(char *)&embra.separateMMUs,PARAM_BOOLEAN);
}
/*****************************************************************
* EmbraEnter
*
* This is the main interface into setting up Embra to run. It
* should be called from simos to set up registers, tlb, cache,
* memory, etc. The second parameter is the number of instructions
* to simulate.
*****************************************************************/
void
EmbraEnter(int swtch)
{
int i;
EMP = SBase;
/* If we are uniprocessor, don't do MPinUP */
if (TOTAL_CPUS == 1) {
embra.MPinUP = 0;
embra.sequential = 1;
embra.parallel = 0;
} else if (embra.MPinUP) {
embra.sequential = 1;
embra.parallel = 0;
} else {
ASSERT(0);
embra.sequential = 0;
embra.parallel = 1;
}
embra.emode = sim_misc.myCPUType;
ASSERT( embra.emode ==EMBRA_PAGE || embra.emode==EMBRA_CACHE);
if (embra.emode==EMBRA_CACHE) {
if (SCACHE_ASSOC!=1) {
CPUError("EMBRA_CACHE requires a direct-mapped cache. set PARAM(CACHE.2Level.L2Assoc) 1 \n");
}
}
#ifdef EMBRA_USE_QC64
embra.inlineQC = FALSE; /* Doesn't work with 64bit mode */
#endif
/* Load these on each entry */
EmbraCPUVectorInit();
EmbraDebugInit();
/* enable reinstating callbacks */
InstallTimers(); /* tell simmagic to start timers */
InstallPoller(); /* add poll callback to eventqueue */
EmbraRemapInit(); /* initialize remap array */
simosCPUType = embra.emode;
Embra_Init(0, swtch);
Clear_Translation_State( TCFLUSH_ALL);
if (!swtch) {
/* this is not a switch from another simulator */
for (i=0;i<TOTAL_CPUS;i++) {
curEmp = &EMP[i];
embraLastCount[i] = 0ll;
AnnExec(AnnFind("simos", "enter"));
}
}
/* if switching from MIPSY, check for not_booted/faulted cpus */
if (swtch) {
for (i = 0; i < TOTAL_CPUS; i++) {
if (SBase[i].cpuStatus == cpu_not_booted) {
Embra_ResetCPU(i);
}
}
}
EmbraTimeDiff();
HWEventsLateInit();
Embra_Run(0, 1);
/* Will we return from here? */
ASSERT(0);
}
/*****************************************************************
* EmbraExit
*
*****************************************************************/
void
EmbraExit(CPUType new_cpu)
{
extern struct timeval run_start; /* driver.c */
extern struct timeval run_end; /* driver.c */
if (new_cpu != NO_CPU) {
CPUWarning("Exit embra into %s\n", simName[new_cpu]);
}
sim_misc.enterThisCPU = new_cpu;
if (embra.sequential) {
int cpu;
int annType = EmbraAnnType();
EmbraClosePeriodicCallbacks();
/*
* XXX this is key. If we have a post-pc annotation,
* XXX we better advance the pc.
*/
if (annType==ANNFM_PC_TYPE) {
CPUWarning("Embra: switching on PC annotation. Advancing PC \n");
EMP[CPUVec.CurrentCpuNum()].PC += INST_SIZE;
}
/* Switching in delay slot doesn't work. */
ASSERT ((new_cpu == NO_CPU) || !IN_BD(EMP[CPUVec.CurrentCpuNum()].PC));
/*
* sync the clocks or mipsy will get confused.
*/
EmbraFixCycleCounts();
EventProcess(-1,EMP[0].cycleCount);
} else {
ASSERT (0);
}
Em_Tlb_Clear(0);
AnnExec(AnnFind("simos","periodic"));
if ((new_cpu == NO_CPU) || (new_cpu == BASE)) {
AnnExec(AnnFind("simos", "exit"));
}
Print_Recent_Stats(0);
gettimeofday( &run_end );
CPUPrint("EmbraRunTime %u sec for %10lld cycles\n",
run_end.tv_sec - run_start.tv_sec ,
(uint64)EmbraCpuCycleCount(0));
SimulatorSwitch(embra.emode, new_cpu);
}
/*****************************************************************
* This is called from simhd DMA engine
*****************************************************************/
void
EmbraDMAInval(int machine, PA* k0list)
{
int cpu;
SIM_DEBUG(('d', "DMA Clear "));
while( *k0list ) {
switch( embra.emode ) {
case EMBRA_CACHE: {
/* Invalidate caches */
PA pa;
for( pa = K0_TO_PHYS(*k0list); pa < K0_TO_PHYS(*k0list) + DEFAULT_PAGESZ;
pa += SCACHE_LINE_SIZE ) {
if( embra.sequential ) {
/* Invalidate from everybodies cache */
unsigned dir_entry;
if (NUM_CPUS(machine)>1) {
dir_entry = directory[machine][ADDR2SLINE(pa)];
} else {
dir_entry = 1<<FIRST_CPU(machine);
}
Cache_Clobber( machine, TOTAL_CPUS, pa, dir_entry, MEM_D_EXCLUSIVE );
} else {
Dir_Entry new_dir_entry =
Directory_Lock(CURR_CPU, pa, 0/*VA*/, MEM_D_EXCLUSIVE);
/* Invalidate from everybodies cache */
Cache_Clobber( machine, TOTAL_CPUS, pa, (~((Dir_Entry)0)) >> (32 - TOTAL_CPUS),
MEM_D_EXCLUSIVE );
Directory_Free(CURR_CPU, pa, new_dir_entry);
}
}
/* Note that this is being called via the backdoor, so on the */
/* stack is mem_translate. Since that routine returns via */
/* ReenterTC..., it is acceptable to call icache_co... without */
/* checkint the return code and doing a ReenterTC... */
for (cpu = 0; cpu < TOTAL_CPUS; cpu++)
EmbraTCCoherenceCheck(cpu, 0,(PA)*k0list, (PA)*k0list+DEFAULT_PAGESZ);
k0list++;
break;
}
case EMBRA_PAGE:
SIM_DEBUG(('d', "0x%08x ", *k0list));
/* Note that this is being called via the backdoor, so on the */
/* stack is mem_translate. Since that routine returns via */
/* ReenterTC..., it is acceptable to call icache_co... without */
/* checkint the return code and doing a ReenterTC... */
/*xxx This MUST be called in embra page mode to invalidate any
* stale translations which might still linger in the TC (the real
* Icache will also be flushed).
*xxx Also note that ALL CPU caches must be invalidated!
*/
for (cpu = 0; cpu < TOTAL_CPUS; cpu++)
EmbraTCCoherenceCheck(cpu, 0,(PA)*k0list, (PA)*k0list+DEFAULT_PAGESZ);
k0list++;
break;
}
}
SIM_DEBUG(('d', "\n"));
}
/*****************************************************************
* remap region support
*
*****************************************************************/
static void
EmbraRemapInit(void)
{
int i;
if (!remapVec->NodeAddrInitialized) {
for (i=0; i<TOTAL_CPUS; i++) {
int m = M_FROM_CPU(i);
#ifdef TORNADO
remapVec->NodeAddr[i] = (i*NUM_MEMORIES(m)/NUM_CPUS(m)) *
(MEM_SIZE(m) / NUM_CPUS(m));
CPUWarning("embra: nodeaddr for %d is %lx\n",
i, (unsigned long)(remapVec->NodeAddr[i]));
#elif defined(SIM_ORIGIN)
remapVec->NodeAddr[i] =
MEMADDR_TO_PHYS(m, SIM_MEM_ADDR(m) +
(MCPU_FROM_CPU(i)/2) * 2 * (MEM_SIZE(m) / NUM_CPUS(m)));
#else
remapVec->NodeAddr[i] =
MEMADDR_TO_PHYS(m, SIM_MEM_ADDR(m) +
MCPU_FROM_CPU(i) * (MEM_SIZE(m) / NUM_CPUS(m)));
#endif
}
remapVec->NodeAddrInitialized = 1;
}
}
static void
em_setremap(int cpunum, PA mask)
{
unsigned int cnum = (unsigned int) cpunum;
if (cnum < SIM_MAXCPUS) {
remapVec->RemapMask[cnum] = mask;
/* changing the mask might increase or decrease the remap zone.
* The simplest way to reflect this change in the mmu is to
* flush the mmu and renew it.
*/
qc_renew(cnum);
} else {
CPUWarning("cpunum out of range in em_setremap");
}
}
static void
em_controlremap(int cpunum, int isEnabled)
{
unsigned int cnum = (unsigned int) cpunum;
if (cnum < SIM_MAXCPUS) {
remapVec->RemapEnable[cnum] = isEnabled;
/* the simplest way to update the mmu is to flush and renew it. */
qc_renew(cnum);
} else {
CPUWarning("cpunum out of range in em_controlremap");
}
}
static PA
em_getnodeaddr(int cpunum)
{
return remapVec->NodeAddr[cpunum];
}
uint NearestLog2(int n)
{
uint pwr = 0x80000000;
while( !(pwr & n) ) {
pwr >>= 1;
}
return pwr;
}
/*****************************************************************
* EmbraCPUVectorInit
*
* Assign the proper function pointers to the shared CPUVector
*****************************************************************/
void EmbraCPUVectorInit(void)
{
CPUVec.Handle_Debug_Signal = Embra_Handle_Debug_Signal;
CPUVec.InstallMemAnnotation = EmbraInstallMemAnnotation;
CPUVec.GetRegister = Embra_GetRegister;
CPUVec.PutRegister = Embra_PutRegister;
CPUVec.GetMemory = Embra_GetMemory;
CPUVec.PutMemory = Embra_PutMemory;
CPUVec.TranslateVirtualNoSE = Embra_TranslateVirtualNoSE;
CPUVec.CycleCount = EmbraCpuCycleCount;
CPUVec.InstrCount = EmbraCpuInstrCount;
CPUVec.Send_Interrupt = Embra_Send_Interrupt;
CPUVec.Deliver_SIPS = Embra_Deliver_SIPS;
CPUVec.FaultInject = Embra_FaultInject;
CPUVec.ResetCPU = Embra_ResetCPU;
CPUVec.CurrentCpuNum = EmbraCurrentCpuNum;
CPUVec.CurrentPC = EmbraCurrentPC;
memsysVec.type = NO_MEMSYS;
memsysVec.MemsysCmd = 0;
memsysVec.MemsysSetRemap = em_setremap;
memsysVec.MemsysGetNodeAddress = em_getnodeaddr;
memsysVec.MemsysControlRemap = em_controlremap;
CPUVec.DMAInval = EmbraDMAInval;
if( embra.sequential ) {
CPUVec.singleEventQueue = 1;
} else {
CPUVec.singleEventQueue = 0;
}
CPUVec.SelectorChange = Dump_QC_Counters;
CPUVec.DoCheckpoint = EmbraCheckpoint;
CPUVec.ProcMakeProcExit = EmbraMakeProcExit;
CPUVec.ExitSimulator = EmbraExit;
CPUVec.IntrBitsChanged = EmIntrBitsChanged;
CPUVec.FirewallChange = EmFirewallChange;
CPUVec.MigRepStart = MigRepStart;
CPUVec.MigRepEnd = MigRepEnd;
CPUVec.useMemRef = FALSE;
}
/*****************************************************************
* Procexit Support
*****************************************************************/
/******************************************************************
* Write a call to the system call exit on the user's stack. Then
* jump to it
*****************************************************************/
/* Macros to cons up instructions ( ConsInstruction<instruction_type>) */
#define CIi(opCode, rt, rs, immed) ((int)\
((opCode) << 26 | (rs) << 21 | (rt) << 16 | ((immed) & 0xffff ) ) )
#define CIs(funct, rd, rs, rt) ((int)\
(spec_op << 26 | (rs) << 21 | (rt) << 16 | (rd) << 11 | funct) )
int EmbraMakeProcessExit(int cpuNum)
{
EmbraState *P = &EMP[cpuNum];
uint *pPtr;
VA vSP;
if (CURRENT_MODE(P) != USER_MODE ){
CPUWarning("%d %llu Tyring to make pid exit\n",
cpuNum, EmbraCpuCycleCount(cpuNum));
return 0;
}
/* Insure that we are not writting over a page boundary by using */
/* the beginning of the page*/
vSP = FORM_ADDR( PAGE_NUMBER(P->R[REG_SP]), 0 );
pPtr = (uint*) non_excepting_tv( cpuNum, vSP );
if( !pPtr ) {
CPUWarning("Failure to translate stack address in MakeProcExit\n");
return 0;
}
CPUWarning("PROCexit: CPU %d smashing address %#x\n", P->myNum, vSP);
*pPtr++ = CIi( addiu_op, A0, G0, 0 ); /* li a0, 0 */
*pPtr++ = CIi( addiu_op, V0, G0, 1001 ); /* li v0, 1001 */
/* This trick won't work in base mode both because of this opcode */
/* because we are not flushing the data cache*/
*pPtr++ = CIs( syscall_op, 0, 0, 0); /* syscall */
P->PC = vSP;
return 1;
}
/*****************************************************************
* fault support
*
*/
static EventCallbackHdr DeadCpuCycleMaintenance[SIM_MAXCPUS];
static int lowest_live_cpu = 0;
static uint deadcpus = 0;
#define DEADCPUCYCLEINTERVAL 10000
int
EmbraCurrentCpuNum(void) {
int cpuNum = curEmp - &EMP[0];
ASSERT( cpuNum == curEmp->myNum);
ASSERT( cpuNum >= 0 && cpuNum < TOTAL_CPUS);
return cpuNum;
}
VA
EmbraCurrentPC(int cpuNum)
{
return EMP[cpuNum].PC;
}
static void
Embra_FaultInject(int cpuno, faultTypeEnum faultType)
{
switch (faultType) {
case SIMFAULT_HALT:
/* do something lower-level (not the right thing, eventually) */
ProcRemove( cpuno );
/* the problem with ProcRemove is that the cycle counter of
* the failed cpu stops incrementing, so events driven off of
* that (notably the timer interrupt) cause an infinite loop
* when the time on that cpu gets sufficiently far behind the
* time on the rest of the system. We need to set up a periodic
* callback on some other cpu that increments the cycle count
* on the failed cpu.
*
* We use the lowest_live_cpu to do this. If it is the lowest
* live cpu itself that has failed, need to move any previous
* cycle maintenance callbacks to the new lowest live cpu.
*/
if (cpuno == lowest_live_cpu) {
int i;
lowest_live_cpu = -1;
for (i=0; i<TOTAL_CPUS; i++) {
if (i == cpuno) continue;
if (deadcpus & (1<<i)) continue;
lowest_live_cpu = i;
break;
}
ASSERT(lowest_live_cpu != -1); /* should be at least 1 live one*/
for (i=0; i<TOTAL_CPUS; i++) {
if (deadcpus & (1<<i)) {
SimTime delta = DeadCpuCycleMaintenance[i].when
- EmbraCpuCycleCount(cpuno);
EventCallbackRemove(&DeadCpuCycleMaintenance[i]);
EventDoCallback(lowest_live_cpu, DeadCpuCycleInc,
&DeadCpuCycleMaintenance[i],
(void*) i, delta);
}
}
}
deadcpus |= (1 << cpuno);
EventDoCallback(lowest_live_cpu, DeadCpuCycleInc,
&DeadCpuCycleMaintenance[cpuno], (void*) cpuno,
DEADCPUCYCLEINTERVAL);
break;
default:
CPUWarning("Unknown fault (%d) ignored\n", faultType);
break;
}
}
static void
DeadCpuCycleInc(int cpuNum, EventCallbackHdr *E, void* arg)
{
int deadcpu = (int) arg;
EMP[deadcpu].cycleCount += DEADCPUCYCLEINTERVAL;
EventDoCallback(lowest_live_cpu, DeadCpuCycleInc,
&DeadCpuCycleMaintenance[deadcpu], (void*) deadcpu,
DEADCPUCYCLEINTERVAL);
}
int Embra_ResetCPU(int cpuNum)
{
/* TBD: clear cached state about interrupts, etc */
/* by calling putregister for the pc and RA, we trigger normal side-
* effect detection which prevents us from returning to bad code
* in the TC
*/
Embra_PutRegister(cpuNum, PC_REGNUM, (uint)continue_run_without_chaining);
Embra_PutRegister(cpuNum, 31, (uint)continue_run_without_chaining);
EMP[cpuNum].jumpPC = (uint)continue_run_without_chaining;
return 0;
}