Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include	"u.h"
2
#include	"tos.h"
3
#include	"../port/lib.h"
4
#include	"mem.h"
5
#include	"dat.h"
6
#include	"fns.h"
7
#include	"io.h"
8
#include	"ureg.h"
9
#include	"../port/error.h"
10
 
11
enum {
12
	Dumpstack = 1,		/* flag: allow stack dump on panic */
13
};
14
 
15
static int trapinited;
16
 
17
void	noted(Ureg*, ulong);
18
 
19
static void debugbpt(Ureg*, void*);
20
static void fault386(Ureg*, void*);
21
static void doublefault(Ureg*, void*);
22
static void unexpected(Ureg*, void*);
23
static void _dumpstack(Ureg*);
24
 
25
static Lock vctllock;
26
static Vctl *vctl[256];
27
 
28
enum
29
{
30
	Ntimevec = 20		/* number of time buckets for each intr */
31
};
32
 
33
void
34
intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
35
{
36
	int vno;
37
	Vctl *v;
38
 
39
	if(f == nil){
40
		print("intrenable: nil handler for %d, tbdf %#uX for %s\n",
41
			irq, tbdf, name);
42
		return;
43
	}
44
 
45
	v = xalloc(sizeof(Vctl));
46
	v->isintr = 1;
47
	v->irq = irq;
48
	v->tbdf = tbdf;
49
	v->f = f;
50
	v->a = a;
51
	strncpy(v->name, name, KNAMELEN-1);
52
	v->name[KNAMELEN-1] = 0;
53
 
54
	ilock(&vctllock);
55
	vno = arch->intrenable(v);
56
	if(vno == -1){
57
		iunlock(&vctllock);
58
		print("intrenable: couldn't enable irq %d, tbdf %#uX for %s\n",
59
			irq, tbdf, v->name);
60
		xfree(v);
61
		return;
62
	}
63
	if(vctl[vno]){
64
		if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi)
65
			panic("intrenable: handler: %s %s %#p %#p %#p %#p",
66
				vctl[vno]->name, v->name,
67
				vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi);
68
		v->next = vctl[vno];
69
	}
70
	vctl[vno] = v;
71
	iunlock(&vctllock);
72
}
73
 
74
int
75
intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
76
{
77
	Vctl **pv, *v;
78
	int vno;
79
 
80
	/*
81
	 * For now, none of this will work with the APIC code,
82
	 * there is no mapping between irq and vector as the IRQ
83
	 * is pretty meaningless.
84
	 */
85
	if(arch->intrvecno == nil)
86
		return -1;
87
	vno = arch->intrvecno(irq);
88
	ilock(&vctllock);
89
	pv = &vctl[vno];
90
	while (*pv &&
91
		  ((*pv)->irq != irq || (*pv)->tbdf != tbdf || (*pv)->f != f || (*pv)->a != a ||
92
		   strcmp((*pv)->name, name)))
93
		pv = &((*pv)->next);
94
	assert(*pv);
95
 
96
	v = *pv;
97
	*pv = (*pv)->next;	/* Link out the entry */
98
 
99
	if(vctl[vno] == nil && arch->intrdisable != nil)
100
		arch->intrdisable(irq);
101
	iunlock(&vctllock);
102
	xfree(v);
103
	return 0;
104
}
105
 
106
static long
107
irqallocread(Chan*, void *vbuf, long n, vlong offset)
108
{
109
	char *buf, *p, str[2*(11+1)+KNAMELEN+1+1];
110
	int m, vno;
111
	long oldn;
112
	Vctl *v;
113
 
114
	if(n < 0 || offset < 0)
115
		error(Ebadarg);
116
 
117
	oldn = n;
118
	buf = vbuf;
119
	for(vno=0; vno<nelem(vctl); vno++){
120
		for(v=vctl[vno]; v; v=v->next){
121
			m = snprint(str, sizeof str, "%11d %11d %.*s\n", vno, v->irq, KNAMELEN, v->name);
122
			if(m <= offset)	/* if do not want this, skip entry */
123
				offset -= m;
124
			else{
125
				/* skip offset bytes */
126
				m -= offset;
127
				p = str+offset;
128
				offset = 0;
129
 
130
				/* write at most max(n,m) bytes */
131
				if(m > n)
132
					m = n;
133
				memmove(buf, p, m);
134
				n -= m;
135
				buf += m;
136
 
137
				if(n == 0)
138
					return oldn;
139
			}
140
		}
141
	}
142
	return oldn - n;
143
}
144
 
145
void
146
trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name)
147
{
148
	Vctl *v;
149
 
150
	if(vno < 0 || vno >= VectorPIC)
151
		panic("trapenable: vno %d", vno);
152
	v = xalloc(sizeof(Vctl));
153
	v->tbdf = BUSUNKNOWN;
154
	v->f = f;
155
	v->a = a;
156
	strncpy(v->name, name, KNAMELEN);
157
	v->name[KNAMELEN-1] = 0;
158
 
159
	ilock(&vctllock);
160
	v->next = vctl[vno];
161
	vctl[vno] = v;
162
	iunlock(&vctllock);
163
}
164
 
165
static void
166
nmienable(void)
167
{
168
	int x;
169
 
170
	/*
171
	 * Hack: should be locked with NVRAM access.
172
	 */
173
	outb(0x70, 0x80);		/* NMI latch clear */
174
	outb(0x70, 0);
175
 
176
	x = inb(0x61) & 0x07;		/* Enable NMI */
177
	outb(0x61, 0x08|x);
178
	outb(0x61, x);
179
}
180
 
181
/*
182
 * Minimal trap setup.  Just enough so that we can panic
183
 * on traps (bugs) during kernel initialization.
184
 * Called very early - malloc is not yet available.
185
 */
186
void
187
trapinit0(void)
188
{
189
	int d1, v;
190
	ulong vaddr;
191
	Segdesc *idt;
192
 
193
	idt = (Segdesc*)IDTADDR;
194
	vaddr = (ulong)vectortable;
195
	for(v = 0; v < 256; v++){
196
		d1 = (vaddr & 0xFFFF0000)|SEGP;
197
		switch(v){
198
 
199
		case VectorBPT:
200
			d1 |= SEGPL(3)|SEGIG;
201
			break;
202
 
203
		case VectorSYSCALL:
204
			d1 |= SEGPL(3)|SEGIG;
205
			break;
206
 
207
		default:
208
			d1 |= SEGPL(0)|SEGIG;
209
			break;
210
		}
211
		idt[v].d0 = (vaddr & 0xFFFF)|(KESEL<<16);
212
		idt[v].d1 = d1;
213
		vaddr += 6;
214
	}
215
}
216
 
217
void
218
trapinit(void)
219
{
220
	/*
221
	 * Special traps.
222
	 * Syscall() is called directly without going through trap().
223
	 */
224
	trapenable(VectorBPT, debugbpt, 0, "debugpt");
225
	trapenable(VectorPF, fault386, 0, "fault386");
226
	trapenable(Vector2F, doublefault, 0, "doublefault");
227
	trapenable(Vector15, unexpected, 0, "unexpected");
228
	nmienable();
229
 
230
	addarchfile("irqalloc", 0444, irqallocread, nil);
231
	trapinited = 1;
232
}
233
 
234
static char* excname[32] = {
235
	"divide error",
236
	"debug exception",
237
	"nonmaskable interrupt",
238
	"breakpoint",
239
	"overflow",
240
	"bounds check",
241
	"invalid opcode",
242
	"coprocessor not available",
243
	"double fault",
244
	"coprocessor segment overrun",
245
	"invalid TSS",
246
	"segment not present",
247
	"stack exception",
248
	"general protection violation",
249
	"page fault",
250
	"15 (reserved)",
251
	"coprocessor error",
252
	"alignment check",
253
	"machine check",
254
	"19 (reserved)",
255
	"20 (reserved)",
256
	"21 (reserved)",
257
	"22 (reserved)",
258
	"23 (reserved)",
259
	"24 (reserved)",
260
	"25 (reserved)",
261
	"26 (reserved)",
262
	"27 (reserved)",
263
	"28 (reserved)",
264
	"29 (reserved)",
265
	"30 (reserved)",
266
	"31 (reserved)",
267
};
268
 
269
/*
270
 *  keep histogram of interrupt service times
271
 */
272
void
273
intrtime(Mach*, int vno)
274
{
275
	ulong diff;
276
	ulong x;
277
 
278
	x = perfticks();
279
	diff = x - m->perf.intrts;
280
	m->perf.intrts = x;
281
 
282
	m->perf.inintr += diff;
283
	if(up == nil && m->perf.inidle > diff)
284
		m->perf.inidle -= diff;
285
	USED(vno);
286
}
287
 
288
/* go to user space */
289
void
290
kexit(Ureg*)
291
{
292
	uvlong t;
293
	Tos *tos;
294
 
295
	/* precise time accounting, kernel exit */
296
	tos = (Tos*)(USTKTOP-sizeof(Tos));
297
	cycles(&t);
298
	tos->kcycles += t - up->kentry;
299
	tos->pcycles = up->pcycles;
300
	tos->pid = up->pid;
301
}
302
 
303
/*
304
 *  All traps come here.  It is slower to have all traps call trap()
305
 *  rather than directly vectoring the handler.  However, this avoids a
306
 *  lot of code duplication and possible bugs.  The only exception is
307
 *  VectorSYSCALL.
308
 *  Trap is called with interrupts disabled via interrupt-gates.
309
 */
310
void
311
trap(Ureg* ureg)
312
{
313
	int clockintr, i, vno, user;
314
	Vctl *ctl, *v;
315
	Mach *mach;
316
 
317
	if(!trapinited){
318
		/* fault386 can give a better error message */
319
		if(ureg->trap == VectorPF)
320
			fault386(ureg, nil);
321
		panic("trap %lud: not ready", ureg->trap);
322
	}
323
 
324
	if (m == 0)
325
		panic("trap: nil m");
326
	m->perf.intrts = perfticks();
327
	user = (ureg->cs & 0xFFFF) == UESEL;
328
 
329
	clockintr = 0;
330
 
331
	vno = ureg->trap;
332
	if(ctl = vctl[vno]){
333
		if(ctl->isintr){
334
			m->intr++;
335
			if(vno >= VectorPIC && vno != VectorSYSCALL)
336
				m->lastintr = ctl->irq;
337
		}
338
 
339
		if(ctl->isr)
340
			ctl->isr(vno);
341
		for(v = ctl; v != nil; v = v->next){
342
			if(v->f)
343
				v->f(ureg, v->a);
344
		}
345
		if(ctl->eoi)
346
			ctl->eoi(vno);
347
 
348
		if(ctl->isintr){
349
			intrtime(m, vno);
350
 
351
			if(ctl->irq == IrqCLOCK || ctl->irq == IrqTIMER)
352
				clockintr = 1;
353
 
354
			if(up && !clockintr)
355
				preempted();
356
		}
357
	}
358
	else if(vno < nelem(excname) && user){
359
		char buf[ERRMAX];
360
 
361
		spllo();
362
		snprint(buf, sizeof buf, "sys: trap: %s", excname[vno]);
363
		postnote(up, 1, buf, NDebug);
364
	}
365
	else if(vno >= VectorPIC && vno != VectorSYSCALL){
366
		/*
367
		 * An unknown interrupt.
368
		 * Check for a default IRQ7. This can happen when
369
		 * the IRQ input goes away before the acknowledge.
370
		 * In this case, a 'default IRQ7' is generated, but
371
		 * the corresponding bit in the ISR isn't set.
372
		 * In fact, just ignore all such interrupts.
373
		 */
374
 
375
		/* call all interrupt routines, just in case */
376
		for(i = VectorPIC; i <= MaxIrqLAPIC; i++){
377
			ctl = vctl[i];
378
			if(ctl == nil)
379
				continue;
380
			if(!ctl->isintr)
381
				continue;
382
			for(v = ctl; v != nil; v = v->next){
383
				if(v->f)
384
					v->f(ureg, v->a);
385
			}
386
			/* should we do this? */
387
			if(ctl->eoi)
388
				ctl->eoi(i);
389
		}
390
 
391
		/* clear the interrupt */
392
		i8259isr(vno);
393
 
394
		if(0)print("cpu%d: spurious interrupt %d, last %d\n",
395
			m->machno, vno, m->lastintr);
396
		if(0)if(conf.nmach > 1){
397
			for(i = 0; i < 32; i++){
398
				if(!(active.machs & (1<<i)))
399
					continue;
400
				mach = MACHP(i);
401
				if(m->machno == mach->machno)
402
					continue;
403
				print(" cpu%d: last %d",
404
					mach->machno, mach->lastintr);
405
			}
406
			print("\n");
407
		}
408
		m->spuriousintr++;
409
		return;
410
	}
411
	else{
412
		if(vno == VectorNMI){
413
			/*
414
			 * Don't re-enable, it confuses the crash dumps.
415
			nmienable();
416
			 */
417
			iprint("cpu%d: NMI PC %#8.8lux\n", m->machno, ureg->pc);
418
			while(m->machno != 0)
419
				;
420
		}
421
		dumpregs(ureg);
422
		if(vno < nelem(excname))
423
			panic("%s", excname[vno]);
424
		panic("unknown trap/intr: %d", vno);
425
	}
426
	splhi();
427
 
428
	/* delaysched set because we held a lock or because our quantum ended */
429
	if(up && up->delaysched && clockintr){
430
		sched();
431
		splhi();
432
	}
433
}
434
 
435
/*
436
 *  dump registers
437
 */
438
void
439
dumpregs2(Ureg* ureg)
440
{
441
	if(up)
442
		iprint("cpu%d: registers for %s %lud\n",
443
			m->machno, up->text, up->pid);
444
	else
445
		iprint("cpu%d: registers for kernel\n", m->machno);
446
	iprint("FLAGS=%luX TRAP=%luX ECODE=%luX PC=%luX",
447
		ureg->flags, ureg->trap, ureg->ecode, ureg->pc);
448
	iprint(" SS=%4.4luX USP=%luX\n", ureg->ss & 0xFFFF, ureg->usp);
449
	iprint("  AX %8.8luX  BX %8.8luX  CX %8.8luX  DX %8.8luX\n",
450
		ureg->ax, ureg->bx, ureg->cx, ureg->dx);
451
	iprint("  SI %8.8luX  DI %8.8luX  BP %8.8luX\n",
452
		ureg->si, ureg->di, ureg->bp);
453
	iprint("  CS %4.4luX  DS %4.4luX  ES %4.4luX  FS %4.4luX  GS %4.4luX\n",
454
		ureg->cs & 0xFFFF, ureg->ds & 0xFFFF, ureg->es & 0xFFFF,
455
		ureg->fs & 0xFFFF, ureg->gs & 0xFFFF);
456
}
457
 
458
void
459
dumpregs(Ureg* ureg)
460
{
461
	vlong mca, mct;
462
 
463
	dumpregs2(ureg);
464
 
465
	/*
466
	 * Processor control registers.
467
	 * If machine check exception, time stamp counter, page size extensions
468
	 * or enhanced virtual 8086 mode extensions are supported, there is a
469
	 * CR4. If there is a CR4 and machine check extensions, read the machine
470
	 * check address and machine check type registers if RDMSR supported.
471
	 */
472
	iprint("  CR0 %8.8lux CR2 %8.8lux CR3 %8.8lux",
473
		getcr0(), getcr2(), getcr3());
474
	if(m->cpuiddx & 0x9A){
475
		iprint(" CR4 %8.8lux", getcr4());
476
		if((m->cpuiddx & 0xA0) == 0xA0){
477
			rdmsr(0x00, &mca);
478
			rdmsr(0x01, &mct);
479
			iprint("\n  MCA %8.8llux MCT %8.8llux", mca, mct);
480
		}
481
	}
482
	iprint("\n  ur %#p up %#p\n", ureg, up);
483
}
484
 
485
 
486
/*
487
 * Fill in enough of Ureg to get a stack trace, and call a function.
488
 * Used by debugging interface rdb.
489
 */
490
void
491
callwithureg(void (*fn)(Ureg*))
492
{
493
	Ureg ureg;
494
	ureg.pc = getcallerpc(&fn);
495
	ureg.sp = (ulong)&fn;
496
	fn(&ureg);
497
}
498
 
499
static void
500
_dumpstack(Ureg *ureg)
501
{
502
	uintptr l, v, i, estack;
503
	extern ulong etext;
504
	int x;
505
	char *s;
506
 
507
	if (!Dumpstack) {
508
		print("no stack dump\n");
509
		return;
510
	}
511
	if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){
512
		iprint("dumpstack disabled\n");
513
		return;
514
	}
515
	iprint("dumpstack\n");
516
 
517
	x = 0;
518
	x += iprint("ktrace /kernel/path %.8lux %.8lux <<EOF\n", ureg->pc, ureg->sp);
519
	i = 0;
520
	if(up
521
	&& (uintptr)&l >= (uintptr)up->kstack
522
	&& (uintptr)&l <= (uintptr)up->kstack+KSTACK)
523
		estack = (uintptr)up->kstack+KSTACK;
524
	else if((uintptr)&l >= (uintptr)m->stack
525
	&& (uintptr)&l <= (uintptr)m+MACHSIZE)
526
		estack = (uintptr)m+MACHSIZE;
527
	else
528
		return;
529
	x += iprint("estackx %p\n", estack);
530
 
531
	for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
532
		v = *(uintptr*)l;
533
		if((KTZERO < v && v < (uintptr)&etext) || estack-l < 32){
534
			/*
535
			 * Could Pick off general CALL (((uchar*)v)[-5] == 0xE8)
536
			 * and CALL indirect through AX
537
			 * (((uchar*)v)[-2] == 0xFF && ((uchar*)v)[-2] == 0xD0),
538
			 * but this is too clever and misses faulting address.
539
			 */
540
			x += iprint("%.8p=%.8p ", l, v);
541
			i++;
542
		}
543
		if(i == 4){
544
			i = 0;
545
			x += iprint("\n");
546
		}
547
	}
548
	if(i)
549
		iprint("\n");
550
	iprint("EOF\n");
551
 
552
	if(ureg->trap != VectorNMI)
553
		return;
554
 
555
	i = 0;
556
	for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
557
		iprint("%.8p ", *(uintptr*)l);
558
		if(++i == 8){
559
			i = 0;
560
			iprint("\n");
561
		}
562
	}
563
	if(i)
564
		iprint("\n");
565
}
566
 
567
void
568
dumpstack(void)
569
{
570
	callwithureg(_dumpstack);
571
}
572
 
573
static void
574
debugbpt(Ureg* ureg, void*)
575
{
576
	char buf[ERRMAX];
577
 
578
	if(up == 0)
579
		panic("kernel bpt");
580
	/* restore pc to instruction that caused the trap */
581
	ureg->pc--;
582
	snprint(buf, sizeof buf, "sys: breakpoint");
583
	postnote(up, 1, buf, NDebug);
584
}
585
 
586
static void
587
doublefault(Ureg*, void*)
588
{
589
	panic("double fault");
590
}
591
 
592
static void
593
unexpected(Ureg* ureg, void*)
594
{
595
	print("unexpected trap %lud; ignoring\n", ureg->trap);
596
}
597
 
598
extern void checkfault(ulong, ulong);
599
static void
600
fault386(Ureg* ureg, void*)
601
{
602
	ulong addr;
603
	int read, user, n, insyscall;
604
 
605
	addr = getcr2();
606
	read = !(ureg->ecode & 2);
607
 
608
	user = (ureg->cs & 0xFFFF) == UESEL;
609
	if(!user){
610
		if(vmapsync(addr))
611
			return;
612
		if(addr >= USTKTOP)
613
			panic("kernel fault: bad address pc=%#.8lux addr=%#.8lux", ureg->pc, addr);
614
		if(up == nil)
615
			panic("kernel fault: no user process pc=%#.8lux addr=%#.8lux", ureg->pc, addr);
616
	} else
617
		panic("fault386: fault from user mode");
618
	if(up == nil)
619
		panic("user fault: up=0 pc=%#.8lux addr=%#.8lux", ureg->pc, addr);
620
 
621
	insyscall = up->insyscall;
622
	up->insyscall = 1;
623
	n = fault(addr, read);
624
	if(n < 0){
625
		dumpregs(ureg);
626
		panic("fault: %#lux", addr);
627
	}
628
	up->insyscall = insyscall;
629
}
630
 
631
/*
632
 *  dregs of system calls
633
 */
634
 
635
/*
636
 *  Syscall is called directly from assembler without going through trap().
637
 */
638
void
639
syscall(Ureg*)
640
{
641
	/* the bootstrap doesn't implement system calls */
642
	panic("syscall");
643
}
644
 
645
long
646
execregs(ulong entry, ulong ssize, ulong nargs)
647
{
648
	ulong *sp;
649
	Ureg *ureg;
650
 
651
	up->fpstate = FPinit;
652
	fpoff();
653
 
654
	sp = (ulong*)(USTKTOP - ssize);
655
	*--sp = nargs;
656
 
657
	ureg = up->dbgreg;
658
	ureg->usp = (ulong)sp;
659
	ureg->pc = entry;
660
	return USTKTOP-sizeof(Tos);		/* address of kernel/user shared data */
661
}
662
 
663
/*
664
 *  return the userpc the last exception happened at
665
 */
666
ulong
667
userpc(void)
668
{
669
	Ureg *ureg;
670
 
671
	ureg = (Ureg*)up->dbgreg;
672
	return ureg->pc;
673
}
674
 
675
/* This routine must save the values of registers the user is not permitted
676
 * to write from devproc and then restore the saved values before returning.
677
 */
678
void
679
setregisters(Ureg* ureg, char* pureg, char* uva, int n)
680
{
681
	ulong cs, ds, es, flags, fs, gs, ss;
682
 
683
	ss = ureg->ss;
684
	flags = ureg->flags;
685
	cs = ureg->cs;
686
	ds = ureg->ds;
687
	es = ureg->es;
688
	fs = ureg->fs;
689
	gs = ureg->gs;
690
	memmove(pureg, uva, n);
691
	ureg->gs = gs;
692
	ureg->fs = fs;
693
	ureg->es = es;
694
	ureg->ds = ds;
695
	ureg->cs = cs;
696
	ureg->flags = (ureg->flags & 0x00FF) | (flags & 0xFF00);
697
	ureg->ss = ss;
698
}
699
 
700
static void
701
linkproc(void)
702
{
703
	spllo();
704
	up->kpfun(up->kparg);
705
	pexit("kproc dying", 0);
706
}
707
 
708
void
709
kprocchild(Proc* p, void (*func)(void*), void* arg)
710
{
711
	/*
712
	 * gotolabel() needs a word on the stack in
713
	 * which to place the return PC used to jump
714
	 * to linkproc().
715
	 */
716
	p->sched.pc = (ulong)linkproc;
717
	p->sched.sp = (ulong)p->kstack+KSTACK-BY2WD;
718
 
719
	p->kpfun = func;
720
	p->kparg = arg;
721
}
722
 
723
void
724
forkchild(Proc *p, Ureg *ureg)
725
{
726
	Ureg *cureg;
727
 
728
	/*
729
	 * Add 2*BY2WD to the stack to account for
730
	 *  - the return PC
731
	 *  - trap's argument (ur)
732
	 */
733
	p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Ureg)+2*BY2WD);
734
	p->sched.pc = (ulong)forkret;
735
 
736
	cureg = (Ureg*)(p->sched.sp+2*BY2WD);
737
	memmove(cureg, ureg, sizeof(Ureg));
738
	/* return value of syscall in child */
739
	cureg->ax = 0;
740
 
741
	/* Things from bottom of syscall which were never executed */
742
	p->psstate = 0;
743
	p->insyscall = 0;
744
}
745
 
746
/* Give enough context in the ureg to produce a kernel stack for
747
 * a sleeping process
748
 */
749
void
750
setkernur(Ureg* ureg, Proc* p)
751
{
752
	ureg->pc = p->sched.pc;
753
	ureg->sp = p->sched.sp+4;
754
}
755
 
756
ulong
757
dbgpc(Proc *p)
758
{
759
	Ureg *ureg;
760
 
761
	ureg = p->dbgreg;
762
	if(ureg == 0)
763
		return 0;
764
 
765
	return ureg->pc;
766
}