1f43f8ee6SDavid du Colombier /*
2f43f8ee6SDavid du Colombier * traps, exceptions, faults and interrupts on ar7161
3f43f8ee6SDavid du Colombier */
4f43f8ee6SDavid du Colombier #include "u.h"
5f43f8ee6SDavid du Colombier #include "../port/lib.h"
6f43f8ee6SDavid du Colombier #include "mem.h"
7f43f8ee6SDavid du Colombier #include "dat.h"
8f43f8ee6SDavid du Colombier #include "fns.h"
9f43f8ee6SDavid du Colombier #include "ureg.h"
10f43f8ee6SDavid du Colombier #include "io.h"
11f43f8ee6SDavid du Colombier #include <tos.h>
12f43f8ee6SDavid du Colombier #include "../port/error.h"
13f43f8ee6SDavid du Colombier
14f43f8ee6SDavid du Colombier #define setstatus(v) /* experiment: delete this to enable recursive traps */
15f43f8ee6SDavid du Colombier
16f43f8ee6SDavid du Colombier typedef struct Handler Handler;
17f43f8ee6SDavid du Colombier
18f43f8ee6SDavid du Colombier struct Handler {
19f43f8ee6SDavid du Colombier void (*handler)(void *);
20f43f8ee6SDavid du Colombier void *arg;
21f43f8ee6SDavid du Colombier Handler *next; /* at this interrupt level */
22f43f8ee6SDavid du Colombier ulong intrs;
23f43f8ee6SDavid du Colombier };
24f43f8ee6SDavid du Colombier
25f43f8ee6SDavid du Colombier ulong offintrs;
26f43f8ee6SDavid du Colombier ulong intrcauses[ILmax+1];
27f43f8ee6SDavid du Colombier
28f43f8ee6SDavid du Colombier int intr(Ureg*);
29f43f8ee6SDavid du Colombier void kernfault(Ureg*, int);
30f43f8ee6SDavid du Colombier void noted(Ureg*, Ureg**, ulong);
31f43f8ee6SDavid du Colombier void rfnote(Ureg**);
32f43f8ee6SDavid du Colombier
33f43f8ee6SDavid du Colombier char *excname[] =
34f43f8ee6SDavid du Colombier {
35f43f8ee6SDavid du Colombier "trap: external interrupt",
36f43f8ee6SDavid du Colombier "trap: TLB modification (store to unwritable)",
37f43f8ee6SDavid du Colombier "trap: TLB miss (load or fetch)",
38f43f8ee6SDavid du Colombier "trap: TLB miss (store)",
39f43f8ee6SDavid du Colombier "trap: address error (load or fetch)",
40f43f8ee6SDavid du Colombier "trap: address error (store)",
41f43f8ee6SDavid du Colombier "trap: bus error (fetch)",
42f43f8ee6SDavid du Colombier "trap: bus error (data load or store)",
43f43f8ee6SDavid du Colombier "trap: system call",
44f43f8ee6SDavid du Colombier "breakpoint",
45f43f8ee6SDavid du Colombier "trap: reserved instruction",
46f43f8ee6SDavid du Colombier "trap: coprocessor unusable",
47f43f8ee6SDavid du Colombier "trap: arithmetic overflow",
48f43f8ee6SDavid du Colombier "trap: TRAP exception",
49f43f8ee6SDavid du Colombier "trap: VCE (instruction)",
50f43f8ee6SDavid du Colombier "trap: floating-point exception",
51f43f8ee6SDavid du Colombier "trap: coprocessor 2 implementation-specific", /* used as sys call for debugger */
52f43f8ee6SDavid du Colombier "trap: corextend unusable",
53f43f8ee6SDavid du Colombier "trap: precise coprocessor 2 exception",
54f43f8ee6SDavid du Colombier "trap: TLB read-inhibit",
55f43f8ee6SDavid du Colombier "trap: TLB execute-inhibit",
56f43f8ee6SDavid du Colombier "trap: undefined 21",
57f43f8ee6SDavid du Colombier "trap: undefined 22",
58f43f8ee6SDavid du Colombier "trap: WATCH exception",
59f43f8ee6SDavid du Colombier "trap: machine checkcore",
60f43f8ee6SDavid du Colombier "trap: undefined 25",
61f43f8ee6SDavid du Colombier "trap: undefined 26",
62f43f8ee6SDavid du Colombier "trap: undefined 27",
63f43f8ee6SDavid du Colombier "trap: undefined 28",
64f43f8ee6SDavid du Colombier "trap: undefined 29",
65f43f8ee6SDavid du Colombier "trap: cache error",
66f43f8ee6SDavid du Colombier "trap: VCE (data)",
67f43f8ee6SDavid du Colombier };
68f43f8ee6SDavid du Colombier
69f43f8ee6SDavid du Colombier char *fpcause[] =
70f43f8ee6SDavid du Colombier {
71f43f8ee6SDavid du Colombier "inexact operation",
72f43f8ee6SDavid du Colombier "underflow",
73f43f8ee6SDavid du Colombier "overflow",
74f43f8ee6SDavid du Colombier "division by zero",
75f43f8ee6SDavid du Colombier "invalid operation",
76f43f8ee6SDavid du Colombier };
77f43f8ee6SDavid du Colombier char *fpexcname(Ureg*, ulong, char*, uint);
78f43f8ee6SDavid du Colombier #define FPEXPMASK (0x3f<<12) /* Floating exception bits in fcr31 */
79f43f8ee6SDavid du Colombier
80f43f8ee6SDavid du Colombier struct {
81f43f8ee6SDavid du Colombier char *name;
82f43f8ee6SDavid du Colombier uint off;
83f43f8ee6SDavid du Colombier } regname[] = {
84f43f8ee6SDavid du Colombier "STATUS", Ureg_status,
85f43f8ee6SDavid du Colombier "PC", Ureg_pc,
86f43f8ee6SDavid du Colombier "SP", Ureg_sp,
87f43f8ee6SDavid du Colombier "CAUSE",Ureg_cause,
88f43f8ee6SDavid du Colombier "BADADDR", Ureg_badvaddr,
89f43f8ee6SDavid du Colombier "TLBVIRT", Ureg_tlbvirt,
90f43f8ee6SDavid du Colombier "HI", Ureg_hi,
91f43f8ee6SDavid du Colombier "LO", Ureg_lo,
92f43f8ee6SDavid du Colombier "R31", Ureg_r31,
93f43f8ee6SDavid du Colombier "R30", Ureg_r30,
94f43f8ee6SDavid du Colombier "R28", Ureg_r28,
95f43f8ee6SDavid du Colombier "R27", Ureg_r27,
96f43f8ee6SDavid du Colombier "R26", Ureg_r26,
97f43f8ee6SDavid du Colombier "R25", Ureg_r25,
98f43f8ee6SDavid du Colombier "R24", Ureg_r24,
99f43f8ee6SDavid du Colombier "R23", Ureg_r23,
100f43f8ee6SDavid du Colombier "R22", Ureg_r22,
101f43f8ee6SDavid du Colombier "R21", Ureg_r21,
102f43f8ee6SDavid du Colombier "R20", Ureg_r20,
103f43f8ee6SDavid du Colombier "R19", Ureg_r19,
104f43f8ee6SDavid du Colombier "R18", Ureg_r18,
105f43f8ee6SDavid du Colombier "R17", Ureg_r17,
106f43f8ee6SDavid du Colombier "R16", Ureg_r16,
107f43f8ee6SDavid du Colombier "R15", Ureg_r15,
108f43f8ee6SDavid du Colombier "R14", Ureg_r14,
109f43f8ee6SDavid du Colombier "R13", Ureg_r13,
110f43f8ee6SDavid du Colombier "R12", Ureg_r12,
111f43f8ee6SDavid du Colombier "R11", Ureg_r11,
112f43f8ee6SDavid du Colombier "R10", Ureg_r10,
113f43f8ee6SDavid du Colombier "R9", Ureg_r9,
114f43f8ee6SDavid du Colombier "R8", Ureg_r8,
115f43f8ee6SDavid du Colombier "R7", Ureg_r7,
116f43f8ee6SDavid du Colombier "R6", Ureg_r6,
117f43f8ee6SDavid du Colombier "R5", Ureg_r5,
118f43f8ee6SDavid du Colombier "R4", Ureg_r4,
119f43f8ee6SDavid du Colombier "R3", Ureg_r3,
120f43f8ee6SDavid du Colombier "R2", Ureg_r2,
121f43f8ee6SDavid du Colombier "R1", Ureg_r1,
122f43f8ee6SDavid du Colombier };
123f43f8ee6SDavid du Colombier
124f43f8ee6SDavid du Colombier static Lock intrlock;
125f43f8ee6SDavid du Colombier static Handler handlers[ILmax+1];
126f43f8ee6SDavid du Colombier
127f43f8ee6SDavid du Colombier static char *
ptlb(ulong phys)128f43f8ee6SDavid du Colombier ptlb(ulong phys)
129f43f8ee6SDavid du Colombier {
130f43f8ee6SDavid du Colombier static char buf[4][32];
131f43f8ee6SDavid du Colombier static int k;
132f43f8ee6SDavid du Colombier char *p;
133f43f8ee6SDavid du Colombier
134f43f8ee6SDavid du Colombier k = (k+1)&3;
135f43f8ee6SDavid du Colombier p = buf[k];
136f43f8ee6SDavid du Colombier p += snprint(p, sizeof buf[k] - (p - buf[k]), "(%#lux %lud ",
137f43f8ee6SDavid du Colombier (phys<<6) & ~(BY2PG-1), (phys>>3)&7);
138f43f8ee6SDavid du Colombier if(phys & 4)
139f43f8ee6SDavid du Colombier *p++ = 'd';
140f43f8ee6SDavid du Colombier if(phys & 2)
141f43f8ee6SDavid du Colombier *p++ = 'v';
142f43f8ee6SDavid du Colombier if(phys & 1)
143f43f8ee6SDavid du Colombier *p++ = 'g';
144f43f8ee6SDavid du Colombier *p++ = ')';
145f43f8ee6SDavid du Colombier *p = 0;
146f43f8ee6SDavid du Colombier return buf[k];
147f43f8ee6SDavid du Colombier }
148f43f8ee6SDavid du Colombier
149f43f8ee6SDavid du Colombier static void
kpteprint(Ureg * ur)150f43f8ee6SDavid du Colombier kpteprint(Ureg *ur)
151f43f8ee6SDavid du Colombier {
152f43f8ee6SDavid du Colombier ulong i, tlbstuff[3];
153f43f8ee6SDavid du Colombier KMap *k;
154f43f8ee6SDavid du Colombier
155f43f8ee6SDavid du Colombier i = (ur->badvaddr & ~(2*BY2PG-1)) | TLBPID(tlbvirt());
156f43f8ee6SDavid du Colombier print("tlbvirt=%#lux\n", i);
157f43f8ee6SDavid du Colombier i = gettlbp(i, tlbstuff);
158f43f8ee6SDavid du Colombier print("i=%lud v=%#lux p0=%s p1=%s\n",
159f43f8ee6SDavid du Colombier i, tlbstuff[0], ptlb(tlbstuff[1]), ptlb(tlbstuff[2]));
160f43f8ee6SDavid du Colombier
161f43f8ee6SDavid du Colombier i = (ur->badvaddr & ~KMAPADDR)>>15;
162f43f8ee6SDavid du Colombier if(i > KPTESIZE){
163f43f8ee6SDavid du Colombier print("kpte index = %lud ?\n", i);
164f43f8ee6SDavid du Colombier return;
165f43f8ee6SDavid du Colombier }
166f43f8ee6SDavid du Colombier k = &kpte[i];
167f43f8ee6SDavid du Colombier print("i=%lud, &k=%#p, k={v=%#lux, p0=%s, p1=%s, pg=%#p}\n",
168f43f8ee6SDavid du Colombier i, k, k->virt, ptlb(k->phys0), ptlb(k->phys1), k->pg);
169f43f8ee6SDavid du Colombier print("pg={pa=%#lux, va=%#lux}\n", k->pg->pa, k->pg->va);
170f43f8ee6SDavid du Colombier }
171f43f8ee6SDavid du Colombier
172f43f8ee6SDavid du Colombier void
kvce(Ureg * ur,int ecode)173f43f8ee6SDavid du Colombier kvce(Ureg *ur, int ecode)
174f43f8ee6SDavid du Colombier {
175f43f8ee6SDavid du Colombier char c;
176f43f8ee6SDavid du Colombier Pte **p;
177f43f8ee6SDavid du Colombier Page **pg;
178f43f8ee6SDavid du Colombier Segment *s;
179f43f8ee6SDavid du Colombier ulong addr, soff;
180f43f8ee6SDavid du Colombier
181f43f8ee6SDavid du Colombier c = 'D';
182f43f8ee6SDavid du Colombier if(ecode == CVCEI)
183f43f8ee6SDavid du Colombier c = 'I';
184f43f8ee6SDavid du Colombier print("Trap: VCE%c: addr=%#lux\n", c, ur->badvaddr);
185f43f8ee6SDavid du Colombier if((ur->badvaddr & KSEGM) == KSEG3) {
186f43f8ee6SDavid du Colombier kpteprint(ur);
187f43f8ee6SDavid du Colombier return;
188f43f8ee6SDavid du Colombier }
189f43f8ee6SDavid du Colombier if(up && !(ur->badvaddr & KSEGM)) {
190f43f8ee6SDavid du Colombier addr = ur->badvaddr;
191f43f8ee6SDavid du Colombier s = seg(up, addr, 0);
192f43f8ee6SDavid du Colombier if(s == 0){
193f43f8ee6SDavid du Colombier print("kvce: no seg for %#lux\n", addr);
194f43f8ee6SDavid du Colombier for(;;)
195f43f8ee6SDavid du Colombier ;
196f43f8ee6SDavid du Colombier }
197f43f8ee6SDavid du Colombier addr &= ~(BY2PG-1);
198f43f8ee6SDavid du Colombier soff = addr - s->base;
199f43f8ee6SDavid du Colombier p = &s->map[soff/PTEMAPMEM];
200f43f8ee6SDavid du Colombier if(*p){
201f43f8ee6SDavid du Colombier pg = &(*p)->pages[(soff&(PTEMAPMEM-1))/BY2PG];
202f43f8ee6SDavid du Colombier if(*pg)
203f43f8ee6SDavid du Colombier print("kvce: pa=%#lux, va=%#lux\n",
204f43f8ee6SDavid du Colombier (*pg)->pa, (*pg)->va);
205f43f8ee6SDavid du Colombier else
206f43f8ee6SDavid du Colombier print("kvce: no *pg\n");
207f43f8ee6SDavid du Colombier }else
208f43f8ee6SDavid du Colombier print("kvce: no *p\n");
209f43f8ee6SDavid du Colombier }
210f43f8ee6SDavid du Colombier }
211f43f8ee6SDavid du Colombier
212f43f8ee6SDavid du Colombier /* prepare to go to user space */
213f43f8ee6SDavid du Colombier void
kexit(Ureg *)214f43f8ee6SDavid du Colombier kexit(Ureg*)
215f43f8ee6SDavid du Colombier {
216f43f8ee6SDavid du Colombier Tos *tos;
217f43f8ee6SDavid du Colombier
218f43f8ee6SDavid du Colombier /* precise time accounting, kernel exit */
219f43f8ee6SDavid du Colombier tos = (Tos*)(USTKTOP-sizeof(Tos));
220f43f8ee6SDavid du Colombier tos->kcycles += fastticks(&tos->cyclefreq) - up->kentry;
221f43f8ee6SDavid du Colombier tos->pcycles = up->pcycles;
222f43f8ee6SDavid du Colombier tos->pid = up->pid;
223f43f8ee6SDavid du Colombier }
224f43f8ee6SDavid du Colombier
225f43f8ee6SDavid du Colombier void
trap(Ureg * ur)226f43f8ee6SDavid du Colombier trap(Ureg *ur)
227f43f8ee6SDavid du Colombier {
228f43f8ee6SDavid du Colombier int ecode, clockintr, user, cop, x, fpchk;
229f43f8ee6SDavid du Colombier ulong fpfcr31;
230f43f8ee6SDavid du Colombier char buf[2*ERRMAX], buf1[ERRMAX], *fpexcep;
231f43f8ee6SDavid du Colombier static int dumps;
232f43f8ee6SDavid du Colombier
233f43f8ee6SDavid du Colombier ecode = (ur->cause>>2)&EXCMASK;
234f43f8ee6SDavid du Colombier user = ur->status&KUSER;
235f43f8ee6SDavid du Colombier if (ur->cause & TS)
236f43f8ee6SDavid du Colombier panic("trap: tlb shutdown");
237f43f8ee6SDavid du Colombier
238f43f8ee6SDavid du Colombier fpchk = 0;
239f43f8ee6SDavid du Colombier if(user){
240f43f8ee6SDavid du Colombier up->dbgreg = ur;
241f43f8ee6SDavid du Colombier cycles(&up->kentry);
242f43f8ee6SDavid du Colombier /* no fpu, so no fp state to save */
243f43f8ee6SDavid du Colombier }
244f43f8ee6SDavid du Colombier
245f43f8ee6SDavid du Colombier if (up && (char *)(ur) - up->kstack < 1024 && dumps++ == 0) {
246f43f8ee6SDavid du Colombier iprint("trap: proc %ld kernel stack getting full\n", up->pid);
247f43f8ee6SDavid du Colombier dumpregs(ur);
248f43f8ee6SDavid du Colombier dumpstack();
249f43f8ee6SDavid du Colombier }
250f43f8ee6SDavid du Colombier if (up == nil &&
251f43f8ee6SDavid du Colombier (char *)(ur) - (char *)m->stack < 1024 && dumps++ == 0) {
252f43f8ee6SDavid du Colombier iprint("trap: cpu%d kernel stack getting full\n", m->machno);
253f43f8ee6SDavid du Colombier dumpregs(ur);
254f43f8ee6SDavid du Colombier dumpstack();
255f43f8ee6SDavid du Colombier }
256f43f8ee6SDavid du Colombier
257f43f8ee6SDavid du Colombier // splhi(); /* for the experiment: make it explicit */
258f43f8ee6SDavid du Colombier /* clear EXL in status */
259f43f8ee6SDavid du Colombier setstatus(getstatus() & ~EXL);
260f43f8ee6SDavid du Colombier
261f43f8ee6SDavid du Colombier clockintr = 0;
262f43f8ee6SDavid du Colombier switch(ecode){
263f43f8ee6SDavid du Colombier case CINT:
264f43f8ee6SDavid du Colombier clockintr = intr(ur);
265f43f8ee6SDavid du Colombier break;
266f43f8ee6SDavid du Colombier
267f43f8ee6SDavid du Colombier case CFPE:
268f43f8ee6SDavid du Colombier panic("FP exception but no FPU");
269f43f8ee6SDavid du Colombier #ifdef OLD_MIPS_EXAMPLE
270f43f8ee6SDavid du Colombier fptrap(ur);
271f43f8ee6SDavid du Colombier clrfpintr();
272f43f8ee6SDavid du Colombier fpchk = 1;
273f43f8ee6SDavid du Colombier #endif
274f43f8ee6SDavid du Colombier break;
275f43f8ee6SDavid du Colombier
276f43f8ee6SDavid du Colombier case CTLBM:
277f43f8ee6SDavid du Colombier case CTLBL:
278f43f8ee6SDavid du Colombier case CTLBS:
279f43f8ee6SDavid du Colombier /* user tlb entries assumed not overwritten during startup */
280f43f8ee6SDavid du Colombier if(up == 0)
281f43f8ee6SDavid du Colombier kernfault(ur, ecode);
282f43f8ee6SDavid du Colombier
283f43f8ee6SDavid du Colombier if(!user && (ur->badvaddr & KSEGM) == KSEG3) {
284f43f8ee6SDavid du Colombier kfault(ur);
285f43f8ee6SDavid du Colombier break;
286f43f8ee6SDavid du Colombier }
287f43f8ee6SDavid du Colombier x = up->insyscall;
288f43f8ee6SDavid du Colombier up->insyscall = 1;
289f43f8ee6SDavid du Colombier spllo();
290f43f8ee6SDavid du Colombier faultmips(ur, user, ecode);
291f43f8ee6SDavid du Colombier up->insyscall = x;
292f43f8ee6SDavid du Colombier break;
293f43f8ee6SDavid du Colombier
294f43f8ee6SDavid du Colombier case CVCEI:
295f43f8ee6SDavid du Colombier case CVCED:
296f43f8ee6SDavid du Colombier kvce(ur, ecode);
297f43f8ee6SDavid du Colombier goto Default;
298f43f8ee6SDavid du Colombier
299f43f8ee6SDavid du Colombier case CWATCH:
300f43f8ee6SDavid du Colombier if(!user)
301f43f8ee6SDavid du Colombier panic("watchpoint trap from kernel mode pc=%#p",
302f43f8ee6SDavid du Colombier ur->pc);
303f43f8ee6SDavid du Colombier fpwatch(ur);
304f43f8ee6SDavid du Colombier break;
305f43f8ee6SDavid du Colombier
306f43f8ee6SDavid du Colombier case CCPU:
307f43f8ee6SDavid du Colombier cop = (ur->cause>>28)&3;
308f43f8ee6SDavid du Colombier if(user && up && cop == 1) {
309f43f8ee6SDavid du Colombier if(up->fpstate & FPillegal) {
310f43f8ee6SDavid du Colombier /* someone used floating point in a note handler */
311f43f8ee6SDavid du Colombier postnote(up, 1,
312f43f8ee6SDavid du Colombier "sys: floating point in note handler",
313f43f8ee6SDavid du Colombier NDebug);
314f43f8ee6SDavid du Colombier break;
315f43f8ee6SDavid du Colombier }
316f43f8ee6SDavid du Colombier /* no fpu, so we can only emulate fp ins'ns */
317f43f8ee6SDavid du Colombier if (fpuemu(ur) < 0)
318f43f8ee6SDavid du Colombier postnote(up, 1,
319f43f8ee6SDavid du Colombier "sys: fp instruction not emulated",
320f43f8ee6SDavid du Colombier NDebug);
321f43f8ee6SDavid du Colombier else
322f43f8ee6SDavid du Colombier fpchk = 1;
323f43f8ee6SDavid du Colombier break;
324f43f8ee6SDavid du Colombier }
325f43f8ee6SDavid du Colombier /* Fallthrough */
326f43f8ee6SDavid du Colombier
327f43f8ee6SDavid du Colombier Default:
328f43f8ee6SDavid du Colombier default:
329f43f8ee6SDavid du Colombier if(user) {
330f43f8ee6SDavid du Colombier spllo();
331f43f8ee6SDavid du Colombier snprint(buf, sizeof buf, "sys: %s", excname[ecode]);
332f43f8ee6SDavid du Colombier postnote(up, 1, buf, NDebug);
333f43f8ee6SDavid du Colombier break;
334f43f8ee6SDavid du Colombier }
335f43f8ee6SDavid du Colombier if (ecode == CADREL || ecode == CADRES)
336f43f8ee6SDavid du Colombier iprint("kernel addr exception for va %#p pid %#ld %s\n",
337f43f8ee6SDavid du Colombier ur->badvaddr, (up? up->pid: 0),
338f43f8ee6SDavid du Colombier (up? up->text: ""));
339f43f8ee6SDavid du Colombier print("cpu%d: kernel %s pc=%#lux\n",
340f43f8ee6SDavid du Colombier m->machno, excname[ecode], ur->pc);
341f43f8ee6SDavid du Colombier dumpregs(ur);
342f43f8ee6SDavid du Colombier dumpstack();
343f43f8ee6SDavid du Colombier if(m->machno == 0)
344f43f8ee6SDavid du Colombier spllo();
345f43f8ee6SDavid du Colombier exit(1);
346f43f8ee6SDavid du Colombier }
347f43f8ee6SDavid du Colombier
348f43f8ee6SDavid du Colombier if(fpchk) {
349f43f8ee6SDavid du Colombier fpfcr31 = up->fpsave.fpstatus;
350f43f8ee6SDavid du Colombier if((fpfcr31>>12) & ((fpfcr31>>7)|0x20) & 0x3f) {
351f43f8ee6SDavid du Colombier spllo();
352f43f8ee6SDavid du Colombier fpexcep = fpexcname(ur, fpfcr31, buf1, sizeof buf1);
353f43f8ee6SDavid du Colombier snprint(buf, sizeof buf, "sys: fp: %s", fpexcep);
354f43f8ee6SDavid du Colombier postnote(up, 1, buf, NDebug);
355f43f8ee6SDavid du Colombier }
356f43f8ee6SDavid du Colombier }
357f43f8ee6SDavid du Colombier
358f43f8ee6SDavid du Colombier splhi();
359f43f8ee6SDavid du Colombier
360f43f8ee6SDavid du Colombier /* delaysched set because we held a lock or because our quantum ended */
361f43f8ee6SDavid du Colombier if(up && up->delaysched && clockintr){
362f43f8ee6SDavid du Colombier sched();
363f43f8ee6SDavid du Colombier splhi();
364f43f8ee6SDavid du Colombier }
365f43f8ee6SDavid du Colombier
366f43f8ee6SDavid du Colombier if(user){
367f43f8ee6SDavid du Colombier notify(ur);
368f43f8ee6SDavid du Colombier /* no fpu, so no fp state to restore */
369f43f8ee6SDavid du Colombier kexit(ur);
370f43f8ee6SDavid du Colombier }
371f43f8ee6SDavid du Colombier
372f43f8ee6SDavid du Colombier /* restore EXL in status */
373f43f8ee6SDavid du Colombier setstatus(getstatus() | EXL);
374f43f8ee6SDavid du Colombier }
375f43f8ee6SDavid du Colombier
376f43f8ee6SDavid du Colombier /* periodically zero all the interrupt counts */
377f43f8ee6SDavid du Colombier static void
resetcounts(void)378f43f8ee6SDavid du Colombier resetcounts(void)
379f43f8ee6SDavid du Colombier {
380f43f8ee6SDavid du Colombier int i;
381f43f8ee6SDavid du Colombier Handler *hp;
382f43f8ee6SDavid du Colombier
383f43f8ee6SDavid du Colombier ilock(&intrlock);
384f43f8ee6SDavid du Colombier for (i = 0; i < nelem(handlers); i++)
385f43f8ee6SDavid du Colombier for (hp = &handlers[i]; hp != nil; hp = hp->next)
386f43f8ee6SDavid du Colombier hp->intrs = 0;
387f43f8ee6SDavid du Colombier iunlock(&intrlock);
388f43f8ee6SDavid du Colombier }
389f43f8ee6SDavid du Colombier
390f43f8ee6SDavid du Colombier /*
391f43f8ee6SDavid du Colombier * set handlers
392f43f8ee6SDavid du Colombier */
393f43f8ee6SDavid du Colombier void
intrenable(int irq,void (* h)(void *),void * arg)394f43f8ee6SDavid du Colombier intrenable(int irq, void (*h)(void *), void *arg)
395f43f8ee6SDavid du Colombier {
396f43f8ee6SDavid du Colombier Handler *hp;
397f43f8ee6SDavid du Colombier static int resetclock;
398f43f8ee6SDavid du Colombier
399f43f8ee6SDavid du Colombier if (h == nil)
400f43f8ee6SDavid du Colombier panic("intrenable: nil handler intr %d", irq);
401f43f8ee6SDavid du Colombier if(irq < ILmin || irq >= nelem(handlers))
402f43f8ee6SDavid du Colombier panic("intrenable: bad handler intr %d %#p", irq, h);
403f43f8ee6SDavid du Colombier
404f43f8ee6SDavid du Colombier hp = &handlers[irq];
405f43f8ee6SDavid du Colombier ilock(&intrlock);
406f43f8ee6SDavid du Colombier if (hp->handler != nil) { /* occupied? */
407f43f8ee6SDavid du Colombier /* add a new one at the end of the chain */
408f43f8ee6SDavid du Colombier for (; hp->next != nil; hp = hp->next)
409f43f8ee6SDavid du Colombier ;
410f43f8ee6SDavid du Colombier hp->next = smalloc(sizeof *hp);
411f43f8ee6SDavid du Colombier hp = hp->next;
412f43f8ee6SDavid du Colombier hp->next = nil;
413f43f8ee6SDavid du Colombier }
414f43f8ee6SDavid du Colombier hp->handler = h;
415f43f8ee6SDavid du Colombier hp->arg = arg;
416f43f8ee6SDavid du Colombier iunlock(&intrlock);
417f43f8ee6SDavid du Colombier
418f43f8ee6SDavid du Colombier if (irq == ILduart0) { /* special apb sub-interrupt */
419f43f8ee6SDavid du Colombier *Apbintrsts = 0;
420f43f8ee6SDavid du Colombier *Apbintrmask = 1 << Apbintruart; /* enable, actually */
421f43f8ee6SDavid du Colombier coherence();
422f43f8ee6SDavid du Colombier }
423f43f8ee6SDavid du Colombier intron(1 << (ILshift + irq));
424f43f8ee6SDavid du Colombier if (!resetclock) {
425f43f8ee6SDavid du Colombier resetclock = 1;
426f43f8ee6SDavid du Colombier addclock0link(resetcounts, 100);
427f43f8ee6SDavid du Colombier }
428f43f8ee6SDavid du Colombier }
429f43f8ee6SDavid du Colombier
430f43f8ee6SDavid du Colombier void
intrshutdown(void)431f43f8ee6SDavid du Colombier intrshutdown(void)
432f43f8ee6SDavid du Colombier {
433f43f8ee6SDavid du Colombier introff(INTMASK);
434f43f8ee6SDavid du Colombier }
435f43f8ee6SDavid du Colombier
436f43f8ee6SDavid du Colombier static void
jabberoff(Ureg * ur,int irq,ulong bit)437f43f8ee6SDavid du Colombier jabberoff(Ureg *ur, int irq, ulong bit)
438f43f8ee6SDavid du Colombier {
439f43f8ee6SDavid du Colombier introff(bit); /* interrupt off now ... */
440f43f8ee6SDavid du Colombier if (ur)
441f43f8ee6SDavid du Colombier ur->status &= ~bit; /* ... and upon return */
442f43f8ee6SDavid du Colombier offintrs |= bit;
443f43f8ee6SDavid du Colombier iprint("irq %d jabbering; shutting it down\n", irq);
444f43f8ee6SDavid du Colombier }
445f43f8ee6SDavid du Colombier
446f43f8ee6SDavid du Colombier ulong
pollall(Ureg * ur,ulong cause)447f43f8ee6SDavid du Colombier pollall(Ureg *ur, ulong cause) /* must be called splhi */
448f43f8ee6SDavid du Colombier {
449f43f8ee6SDavid du Colombier int i, intrs, sts;
450f43f8ee6SDavid du Colombier ulong bit;
451f43f8ee6SDavid du Colombier Handler *hp;
452f43f8ee6SDavid du Colombier
453f43f8ee6SDavid du Colombier /* exclude clock and sw intrs */
454f43f8ee6SDavid du Colombier intrs = cause & (INTR6|INTR5|INTR4|INTR3|INTR2) & getstatus();
455f43f8ee6SDavid du Colombier if(intrs == 0)
456f43f8ee6SDavid du Colombier return cause;
457f43f8ee6SDavid du Colombier
458f43f8ee6SDavid du Colombier ilock(&intrlock);
459f43f8ee6SDavid du Colombier for (i = ILmax; i >= ILmin; i--) {
460f43f8ee6SDavid du Colombier bit = 1 << (ILshift + i);
461f43f8ee6SDavid du Colombier if (!(intrs & bit))
462f43f8ee6SDavid du Colombier continue;
463f43f8ee6SDavid du Colombier intrcauses[i]++;
464f43f8ee6SDavid du Colombier for (hp = &handlers[i]; hp != nil; hp = hp->next)
465f43f8ee6SDavid du Colombier if (hp->handler) {
466f43f8ee6SDavid du Colombier if (i == ILduart0) {
467f43f8ee6SDavid du Colombier sts = *Apbintrsts;
468f43f8ee6SDavid du Colombier if((sts & (1 << Apbintruart)) == 0)
469f43f8ee6SDavid du Colombier continue;
470f43f8ee6SDavid du Colombier /* don't need to ack apb sub-intr */
471f43f8ee6SDavid du Colombier // *Apbintrsts &= ~(1 << Apbintruart);
472f43f8ee6SDavid du Colombier }
473f43f8ee6SDavid du Colombier (*hp->handler)(hp->arg);
474f43f8ee6SDavid du Colombier splhi();
475f43f8ee6SDavid du Colombier if (++hp->intrs > 25000) {
476f43f8ee6SDavid du Colombier jabberoff(ur, i, bit);
477f43f8ee6SDavid du Colombier intrs &= ~bit;
478f43f8ee6SDavid du Colombier hp->intrs = 0;
479f43f8ee6SDavid du Colombier }
480f43f8ee6SDavid du Colombier } else if (ur)
481f43f8ee6SDavid du Colombier iprint("no handler for interrupt %d\n", i);
482f43f8ee6SDavid du Colombier cause &= ~bit;
483f43f8ee6SDavid du Colombier }
484f43f8ee6SDavid du Colombier iunlock(&intrlock);
485f43f8ee6SDavid du Colombier return cause;
486f43f8ee6SDavid du Colombier }
487f43f8ee6SDavid du Colombier
488f43f8ee6SDavid du Colombier int
intr(Ureg * ur)489f43f8ee6SDavid du Colombier intr(Ureg *ur)
490f43f8ee6SDavid du Colombier {
491f43f8ee6SDavid du Colombier int clockintr;
492f43f8ee6SDavid du Colombier ulong cause;
493f43f8ee6SDavid du Colombier
494f43f8ee6SDavid du Colombier m->intr++;
495f43f8ee6SDavid du Colombier clockintr = 0;
496f43f8ee6SDavid du Colombier /*
497f43f8ee6SDavid du Colombier * ignore interrupts that we have disabled, even if their cause bits
498f43f8ee6SDavid du Colombier * are set.
499f43f8ee6SDavid du Colombier */
500f43f8ee6SDavid du Colombier cause = ur->cause & ur->status & INTMASK;
501f43f8ee6SDavid du Colombier cause &= ~(INTR1|INTR0); /* ignore sw interrupts */
502f43f8ee6SDavid du Colombier if (cause == 0)
503f43f8ee6SDavid du Colombier print("spurious interrupt\n");
504f43f8ee6SDavid du Colombier if(cause & INTR7){
505f43f8ee6SDavid du Colombier clock(ur);
506f43f8ee6SDavid du Colombier intrcauses[ILclock]++;
507f43f8ee6SDavid du Colombier cause &= ~(1 << (ILclock + ILshift));
508f43f8ee6SDavid du Colombier clockintr = 1;
509f43f8ee6SDavid du Colombier }
510f43f8ee6SDavid du Colombier cause = pollall(ur, cause);
511f43f8ee6SDavid du Colombier if(cause){
512f43f8ee6SDavid du Colombier print("intr: cause %#lux not handled\n", cause);
513f43f8ee6SDavid du Colombier exit(1);
514f43f8ee6SDavid du Colombier }
515f43f8ee6SDavid du Colombier
516f43f8ee6SDavid du Colombier /* preemptive scheduling */
517f43f8ee6SDavid du Colombier if(up && !clockintr)
518f43f8ee6SDavid du Colombier preempted();
519f43f8ee6SDavid du Colombier /* if it was a clockintr, sched will be called at end of trap() */
520f43f8ee6SDavid du Colombier return clockintr;
521f43f8ee6SDavid du Colombier }
522f43f8ee6SDavid du Colombier
523f43f8ee6SDavid du Colombier char*
fpexcname(Ureg * ur,ulong fcr31,char * buf,uint size)524f43f8ee6SDavid du Colombier fpexcname(Ureg *ur, ulong fcr31, char *buf, uint size)
525f43f8ee6SDavid du Colombier {
526f43f8ee6SDavid du Colombier int i;
527f43f8ee6SDavid du Colombier char *s;
528f43f8ee6SDavid du Colombier ulong fppc;
529f43f8ee6SDavid du Colombier
530f43f8ee6SDavid du Colombier fppc = ur->pc;
531f43f8ee6SDavid du Colombier if(ur->cause & BD) /* branch delay */
532f43f8ee6SDavid du Colombier fppc += 4;
533f43f8ee6SDavid du Colombier s = 0;
534f43f8ee6SDavid du Colombier if(fcr31 & (1<<17))
535f43f8ee6SDavid du Colombier s = "unimplemented operation";
536f43f8ee6SDavid du Colombier else{
537f43f8ee6SDavid du Colombier fcr31 >>= 7; /* trap enable bits */
538f43f8ee6SDavid du Colombier fcr31 &= (fcr31>>5); /* anded with exceptions */
539f43f8ee6SDavid du Colombier for(i=0; i<5; i++)
540f43f8ee6SDavid du Colombier if(fcr31 & (1<<i))
541f43f8ee6SDavid du Colombier s = fpcause[i];
542f43f8ee6SDavid du Colombier }
543f43f8ee6SDavid du Colombier
544f43f8ee6SDavid du Colombier if(s == 0)
545f43f8ee6SDavid du Colombier return "no floating point exception";
546f43f8ee6SDavid du Colombier
547f43f8ee6SDavid du Colombier snprint(buf, size, "%s fppc=%#lux", s, fppc);
548f43f8ee6SDavid du Colombier return buf;
549f43f8ee6SDavid du Colombier }
550f43f8ee6SDavid du Colombier
551*dc100ed4SDavid du Colombier // #define KERNPC(x) (KTZERO <= (ulong)(x) && (ulong)(x) < (ulong)&etext)
552f43f8ee6SDavid du Colombier
553f43f8ee6SDavid du Colombier void
kernfault(Ureg * ur,int code)554f43f8ee6SDavid du Colombier kernfault(Ureg *ur, int code)
555f43f8ee6SDavid du Colombier {
556f43f8ee6SDavid du Colombier print("panic: kfault %s badvaddr=%#lux", excname[code], ur->badvaddr);
557f43f8ee6SDavid du Colombier kpteprint(ur);
558f43f8ee6SDavid du Colombier print("u=%#p status=%#lux pc=%#lux sp=%#lux\n",
559f43f8ee6SDavid du Colombier up, ur->status, ur->pc, ur->sp);
560f43f8ee6SDavid du Colombier delay(500);
561f43f8ee6SDavid du Colombier panic("kfault");
562f43f8ee6SDavid du Colombier }
563f43f8ee6SDavid du Colombier
564f43f8ee6SDavid du Colombier static void
getpcsp(ulong * pc,ulong * sp)565f43f8ee6SDavid du Colombier getpcsp(ulong *pc, ulong *sp)
566f43f8ee6SDavid du Colombier {
567f43f8ee6SDavid du Colombier *pc = getcallerpc(&pc);
568f43f8ee6SDavid du Colombier *sp = (ulong)&pc-4;
569f43f8ee6SDavid du Colombier }
570f43f8ee6SDavid du Colombier
571f43f8ee6SDavid du Colombier void
callwithureg(void (* fn)(Ureg *))572f43f8ee6SDavid du Colombier callwithureg(void (*fn)(Ureg*))
573f43f8ee6SDavid du Colombier {
574f43f8ee6SDavid du Colombier Ureg ureg;
575f43f8ee6SDavid du Colombier
576f43f8ee6SDavid du Colombier memset(&ureg, 0, sizeof ureg);
577f43f8ee6SDavid du Colombier getpcsp((ulong*)&ureg.pc, (ulong*)&ureg.sp);
578f43f8ee6SDavid du Colombier ureg.r31 = getcallerpc(&fn);
579f43f8ee6SDavid du Colombier fn(&ureg);
580f43f8ee6SDavid du Colombier }
581f43f8ee6SDavid du Colombier
582f43f8ee6SDavid du Colombier static void
_dumpstack(Ureg * ureg)583f43f8ee6SDavid du Colombier _dumpstack(Ureg *ureg)
584f43f8ee6SDavid du Colombier {
585f43f8ee6SDavid du Colombier ulong l, v, top, i;
586f43f8ee6SDavid du Colombier extern ulong etext;
587f43f8ee6SDavid du Colombier
588f43f8ee6SDavid du Colombier if(up == 0)
589f43f8ee6SDavid du Colombier return;
590f43f8ee6SDavid du Colombier
591f43f8ee6SDavid du Colombier print("ktrace /kernel/path %.8lux %.8lux %.8lux\n",
592f43f8ee6SDavid du Colombier ureg->pc, ureg->sp, ureg->r31);
593f43f8ee6SDavid du Colombier top = (ulong)up->kstack + KSTACK;
594f43f8ee6SDavid du Colombier i = 0;
595f43f8ee6SDavid du Colombier for(l=ureg->sp; l < top; l += BY2WD) {
596f43f8ee6SDavid du Colombier v = *(ulong*)l;
597f43f8ee6SDavid du Colombier if(KTZERO < v && v < (ulong)&etext) {
598f43f8ee6SDavid du Colombier print("%.8lux=%.8lux ", l, v);
599f43f8ee6SDavid du Colombier if((++i%4) == 0){
600f43f8ee6SDavid du Colombier print("\n");
601f43f8ee6SDavid du Colombier delay(200);
602f43f8ee6SDavid du Colombier }
603f43f8ee6SDavid du Colombier }
604f43f8ee6SDavid du Colombier }
605f43f8ee6SDavid du Colombier print("\n");
606f43f8ee6SDavid du Colombier }
607f43f8ee6SDavid du Colombier
608f43f8ee6SDavid du Colombier void
dumpstack(void)609f43f8ee6SDavid du Colombier dumpstack(void)
610f43f8ee6SDavid du Colombier {
611f43f8ee6SDavid du Colombier callwithureg(_dumpstack);
612f43f8ee6SDavid du Colombier }
613f43f8ee6SDavid du Colombier
614f43f8ee6SDavid du Colombier static ulong
R(Ureg * ur,int i)615f43f8ee6SDavid du Colombier R(Ureg *ur, int i)
616f43f8ee6SDavid du Colombier {
617f43f8ee6SDavid du Colombier uchar *s;
618f43f8ee6SDavid du Colombier
619f43f8ee6SDavid du Colombier s = (uchar*)ur;
620f43f8ee6SDavid du Colombier return *(ulong*)(s + regname[i].off - Uoffset);
621f43f8ee6SDavid du Colombier }
622f43f8ee6SDavid du Colombier
623f43f8ee6SDavid du Colombier void
dumpregs(Ureg * ur)624f43f8ee6SDavid du Colombier dumpregs(Ureg *ur)
625f43f8ee6SDavid du Colombier {
626f43f8ee6SDavid du Colombier int i;
627f43f8ee6SDavid du Colombier
628f43f8ee6SDavid du Colombier if(up)
629f43f8ee6SDavid du Colombier print("registers for %s %lud\n", up->text, up->pid);
630f43f8ee6SDavid du Colombier else
631f43f8ee6SDavid du Colombier print("registers for kernel\n");
632f43f8ee6SDavid du Colombier
633f43f8ee6SDavid du Colombier for(i = 0; i < nelem(regname); i += 2)
634f43f8ee6SDavid du Colombier print("%s\t%#.8lux\t%s\t%#.8lux\n",
635f43f8ee6SDavid du Colombier regname[i].name, R(ur, i),
636f43f8ee6SDavid du Colombier regname[i+1].name, R(ur, i+1));
637f43f8ee6SDavid du Colombier }
638f43f8ee6SDavid du Colombier
639f43f8ee6SDavid du Colombier int
notify(Ureg * ur)640f43f8ee6SDavid du Colombier notify(Ureg *ur)
641f43f8ee6SDavid du Colombier {
642f43f8ee6SDavid du Colombier int l, s;
643f43f8ee6SDavid du Colombier ulong sp;
644f43f8ee6SDavid du Colombier Note *n;
645f43f8ee6SDavid du Colombier
646f43f8ee6SDavid du Colombier if(up->procctl)
647f43f8ee6SDavid du Colombier procctl(up);
648f43f8ee6SDavid du Colombier if(up->nnote == 0)
649f43f8ee6SDavid du Colombier return 0;
650f43f8ee6SDavid du Colombier
651f43f8ee6SDavid du Colombier s = spllo();
652f43f8ee6SDavid du Colombier qlock(&up->debug);
653f43f8ee6SDavid du Colombier up->fpstate |= FPillegal;
654f43f8ee6SDavid du Colombier up->notepending = 0;
655f43f8ee6SDavid du Colombier n = &up->note[0];
656f43f8ee6SDavid du Colombier if(strncmp(n->msg, "sys:", 4) == 0) {
657f43f8ee6SDavid du Colombier l = strlen(n->msg);
658f43f8ee6SDavid du Colombier if(l > ERRMAX-15) /* " pc=0x12345678\0" */
659f43f8ee6SDavid du Colombier l = ERRMAX-15;
660f43f8ee6SDavid du Colombier
661f43f8ee6SDavid du Colombier seprint(n->msg+l, &n->msg[sizeof n->msg], " pc=%#lux", ur->pc);
662f43f8ee6SDavid du Colombier }
663f43f8ee6SDavid du Colombier
664f43f8ee6SDavid du Colombier if(n->flag != NUser && (up->notified || up->notify==0)) {
665f43f8ee6SDavid du Colombier if(n->flag == NDebug)
666f43f8ee6SDavid du Colombier pprint("suicide: %s\n", n->msg);
667f43f8ee6SDavid du Colombier
668f43f8ee6SDavid du Colombier qunlock(&up->debug);
669f43f8ee6SDavid du Colombier pexit(n->msg, n->flag!=NDebug);
670f43f8ee6SDavid du Colombier }
671f43f8ee6SDavid du Colombier
672f43f8ee6SDavid du Colombier if(up->notified) {
673f43f8ee6SDavid du Colombier qunlock(&up->debug);
674f43f8ee6SDavid du Colombier splx(s);
675f43f8ee6SDavid du Colombier return 0;
676f43f8ee6SDavid du Colombier }
677f43f8ee6SDavid du Colombier
678f43f8ee6SDavid du Colombier if(!up->notify) {
679f43f8ee6SDavid du Colombier qunlock(&up->debug);
680f43f8ee6SDavid du Colombier pexit(n->msg, n->flag!=NDebug);
681f43f8ee6SDavid du Colombier }
682f43f8ee6SDavid du Colombier sp = ur->usp & ~(BY2V-1);
683f43f8ee6SDavid du Colombier sp -= sizeof(Ureg);
684f43f8ee6SDavid du Colombier
685f43f8ee6SDavid du Colombier if(!okaddr((ulong)up->notify, BY2WD, 0) ||
686f43f8ee6SDavid du Colombier !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)) {
687f43f8ee6SDavid du Colombier pprint("suicide: bad address or sp in notify\n");
688f43f8ee6SDavid du Colombier qunlock(&up->debug);
689f43f8ee6SDavid du Colombier pexit("Suicide", 0);
690f43f8ee6SDavid du Colombier }
691f43f8ee6SDavid du Colombier
692f43f8ee6SDavid du Colombier memmove((Ureg*)sp, ur, sizeof(Ureg)); /* push user regs */
693f43f8ee6SDavid du Colombier *(Ureg**)(sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */
694f43f8ee6SDavid du Colombier up->ureg = (void*)sp;
695f43f8ee6SDavid du Colombier
696f43f8ee6SDavid du Colombier sp -= BY2WD+ERRMAX;
697f43f8ee6SDavid du Colombier memmove((char*)sp, up->note[0].msg, ERRMAX); /* push err string */
698f43f8ee6SDavid du Colombier
699f43f8ee6SDavid du Colombier sp -= 3*BY2WD;
700f43f8ee6SDavid du Colombier *(ulong*)(sp+2*BY2WD) = sp+3*BY2WD; /* arg 2 is string */
701f43f8ee6SDavid du Colombier ur->r1 = (long)up->ureg; /* arg 1 is ureg* */
702f43f8ee6SDavid du Colombier ((ulong*)sp)[1] = (ulong)up->ureg; /* arg 1 0(FP) is ureg* */
703f43f8ee6SDavid du Colombier ((ulong*)sp)[0] = 0; /* arg 0 is pc */
704f43f8ee6SDavid du Colombier ur->usp = sp;
705f43f8ee6SDavid du Colombier /*
706f43f8ee6SDavid du Colombier * arrange to resume at user's handler as if handler(ureg, errstr)
707f43f8ee6SDavid du Colombier * were being called.
708f43f8ee6SDavid du Colombier */
709f43f8ee6SDavid du Colombier ur->pc = (ulong)up->notify;
710f43f8ee6SDavid du Colombier
711f43f8ee6SDavid du Colombier up->notified = 1;
712f43f8ee6SDavid du Colombier up->nnote--;
713f43f8ee6SDavid du Colombier memmove(&up->lastnote, &up->note[0], sizeof(Note));
714f43f8ee6SDavid du Colombier memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note));
715f43f8ee6SDavid du Colombier
716f43f8ee6SDavid du Colombier qunlock(&up->debug);
717f43f8ee6SDavid du Colombier splx(s);
718f43f8ee6SDavid du Colombier return 1;
719f43f8ee6SDavid du Colombier }
720f43f8ee6SDavid du Colombier
721f43f8ee6SDavid du Colombier /*
722f43f8ee6SDavid du Colombier * Check that status is OK to return from note.
723f43f8ee6SDavid du Colombier */
724f43f8ee6SDavid du Colombier int
validstatus(ulong kstatus,ulong ustatus)725f43f8ee6SDavid du Colombier validstatus(ulong kstatus, ulong ustatus)
726f43f8ee6SDavid du Colombier {
727f43f8ee6SDavid du Colombier // if((kstatus & (INTMASK|KX|SX|UX)) != (ustatus & (INTMASK|KX|SX|UX)))
728f43f8ee6SDavid du Colombier if((kstatus & INTMASK) != (ustatus & INTMASK))
729f43f8ee6SDavid du Colombier return 0;
730f43f8ee6SDavid du Colombier if((ustatus&(KSU|ERL|EXL|IE)) != (KUSER|EXL|IE))
731f43f8ee6SDavid du Colombier return 0;
732f43f8ee6SDavid du Colombier if(ustatus & (0xFFFF0000&~CU1)) /* no CU3, CU2, CU0, RP, FR, RE, DS */
733f43f8ee6SDavid du Colombier return 0;
734f43f8ee6SDavid du Colombier return 1;
735f43f8ee6SDavid du Colombier }
736f43f8ee6SDavid du Colombier
737f43f8ee6SDavid du Colombier /*
738f43f8ee6SDavid du Colombier * Return user to state before notify(); called from user's handler.
739f43f8ee6SDavid du Colombier */
740f43f8ee6SDavid du Colombier void
noted(Ureg * kur,Ureg ** urp,ulong arg0)741f43f8ee6SDavid du Colombier noted(Ureg *kur, Ureg **urp, ulong arg0)
742f43f8ee6SDavid du Colombier {
743f43f8ee6SDavid du Colombier Ureg *nur;
744f43f8ee6SDavid du Colombier ulong oureg, sp;
745f43f8ee6SDavid du Colombier
746f43f8ee6SDavid du Colombier qlock(&up->debug);
747f43f8ee6SDavid du Colombier if(arg0!=NRSTR && !up->notified) {
748f43f8ee6SDavid du Colombier qunlock(&up->debug);
749f43f8ee6SDavid du Colombier pprint("call to noted() when not notified\n");
750f43f8ee6SDavid du Colombier pexit("Suicide", 0);
751f43f8ee6SDavid du Colombier }
752f43f8ee6SDavid du Colombier up->notified = 0;
753f43f8ee6SDavid du Colombier
754f43f8ee6SDavid du Colombier up->fpstate &= ~FPillegal;
755f43f8ee6SDavid du Colombier
756f43f8ee6SDavid du Colombier nur = up->ureg;
757f43f8ee6SDavid du Colombier
758f43f8ee6SDavid du Colombier oureg = (ulong)nur;
759f43f8ee6SDavid du Colombier if((oureg & (BY2WD-1))
760f43f8ee6SDavid du Colombier || !okaddr((ulong)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){
761f43f8ee6SDavid du Colombier pprint("bad up->ureg in noted or call to noted() when not notified\n");
762f43f8ee6SDavid du Colombier qunlock(&up->debug);
763f43f8ee6SDavid du Colombier pexit("Suicide", 0);
764f43f8ee6SDavid du Colombier }
765f43f8ee6SDavid du Colombier
766f43f8ee6SDavid du Colombier if(!validstatus(kur->status, nur->status)) {
767f43f8ee6SDavid du Colombier qunlock(&up->debug);
768f43f8ee6SDavid du Colombier pprint("bad noted ureg status %#lux\n", nur->status);
769f43f8ee6SDavid du Colombier pexit("Suicide", 0);
770f43f8ee6SDavid du Colombier }
771f43f8ee6SDavid du Colombier
772f43f8ee6SDavid du Colombier memmove(*urp, up->ureg, sizeof(Ureg));
773f43f8ee6SDavid du Colombier switch(arg0) {
774f43f8ee6SDavid du Colombier case NCONT:
775f43f8ee6SDavid du Colombier case NRSTR: /* only used by APE */
776f43f8ee6SDavid du Colombier if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->usp, BY2WD, 0)){
777f43f8ee6SDavid du Colombier pprint("suicide: trap in noted\n");
778f43f8ee6SDavid du Colombier qunlock(&up->debug);
779f43f8ee6SDavid du Colombier pexit("Suicide", 0);
780f43f8ee6SDavid du Colombier }
781f43f8ee6SDavid du Colombier up->ureg = (Ureg*)(*(ulong*)(oureg-BY2WD));
782f43f8ee6SDavid du Colombier qunlock(&up->debug);
783f43f8ee6SDavid du Colombier splhi();
784f43f8ee6SDavid du Colombier /*
785f43f8ee6SDavid du Colombier * the old challenge and carrera ports called rfnote here,
786f43f8ee6SDavid du Colombier * but newer ports do not, and notes seem to work only
787f43f8ee6SDavid du Colombier * without this call.
788f43f8ee6SDavid du Colombier */
789f43f8ee6SDavid du Colombier // rfnote(urp); /* return from note with SP=urp */
790f43f8ee6SDavid du Colombier break;
791f43f8ee6SDavid du Colombier
792f43f8ee6SDavid du Colombier case NSAVE: /* only used by APE */
793f43f8ee6SDavid du Colombier if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->usp, BY2WD, 0)){
794f43f8ee6SDavid du Colombier pprint("suicide: trap in noted\n");
795f43f8ee6SDavid du Colombier qunlock(&up->debug);
796f43f8ee6SDavid du Colombier pexit("Suicide", 0);
797f43f8ee6SDavid du Colombier }
798f43f8ee6SDavid du Colombier qunlock(&up->debug);
799f43f8ee6SDavid du Colombier sp = oureg-4*BY2WD-ERRMAX;
800f43f8ee6SDavid du Colombier
801f43f8ee6SDavid du Colombier splhi();
802f43f8ee6SDavid du Colombier (*urp)->sp = sp;
803f43f8ee6SDavid du Colombier ((ulong*)sp)[1] = oureg; /* arg 1 0(FP) is ureg* */
804f43f8ee6SDavid du Colombier ((ulong*)sp)[0] = 0; /* arg 0 is pc */
805f43f8ee6SDavid du Colombier (*urp)->r1 = oureg; /* arg 1 is ureg* */
806f43f8ee6SDavid du Colombier
807f43f8ee6SDavid du Colombier // rfnote(urp); /* return from note with SP=urp */
808f43f8ee6SDavid du Colombier break;
809f43f8ee6SDavid du Colombier
810f43f8ee6SDavid du Colombier default:
811f43f8ee6SDavid du Colombier pprint("unknown noted arg %#lux\n", arg0);
812f43f8ee6SDavid du Colombier up->lastnote.flag = NDebug;
813f43f8ee6SDavid du Colombier /* fall through */
814f43f8ee6SDavid du Colombier
815f43f8ee6SDavid du Colombier case NDFLT:
816f43f8ee6SDavid du Colombier if(up->lastnote.flag == NDebug)
817f43f8ee6SDavid du Colombier pprint("suicide: %s\n", up->lastnote.msg);
818f43f8ee6SDavid du Colombier qunlock(&up->debug);
819f43f8ee6SDavid du Colombier pexit(up->lastnote.msg, up->lastnote.flag!=NDebug);
820f43f8ee6SDavid du Colombier }
821f43f8ee6SDavid du Colombier }
822f43f8ee6SDavid du Colombier
823f43f8ee6SDavid du Colombier #include "../port/systab.h"
824f43f8ee6SDavid du Colombier
825f43f8ee6SDavid du Colombier static Ref goodsyscall;
826f43f8ee6SDavid du Colombier static Ref totalsyscall;
827f43f8ee6SDavid du Colombier
828f43f8ee6SDavid du Colombier static void
sctracesetup(ulong scallnr,ulong sp,uintptr pc,vlong * startnsp)829f43f8ee6SDavid du Colombier sctracesetup(ulong scallnr, ulong sp, uintptr pc, vlong *startnsp)
830f43f8ee6SDavid du Colombier {
831f43f8ee6SDavid du Colombier if(up->procctl == Proc_tracesyscall){
832f43f8ee6SDavid du Colombier /*
833f43f8ee6SDavid du Colombier * Redundant validaddr. Do we care?
834f43f8ee6SDavid du Colombier * Tracing syscalls is not exactly a fast path...
835f43f8ee6SDavid du Colombier * Beware, validaddr currently does a pexit rather
836f43f8ee6SDavid du Colombier * than an error if there's a problem; that might
837f43f8ee6SDavid du Colombier * change in the future.
838f43f8ee6SDavid du Colombier */
839f43f8ee6SDavid du Colombier if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD))
840f43f8ee6SDavid du Colombier validaddr(sp, sizeof(Sargs)+BY2WD, 0);
841f43f8ee6SDavid du Colombier
842f43f8ee6SDavid du Colombier syscallfmt(scallnr, pc, (va_list)(sp+BY2WD));
843f43f8ee6SDavid du Colombier up->procctl = Proc_stopme;
844f43f8ee6SDavid du Colombier procctl(up);
845f43f8ee6SDavid du Colombier if(up->syscalltrace)
846f43f8ee6SDavid du Colombier free(up->syscalltrace);
847f43f8ee6SDavid du Colombier up->syscalltrace = nil;
848f43f8ee6SDavid du Colombier *startnsp = todget(nil);
849f43f8ee6SDavid du Colombier }
850f43f8ee6SDavid du Colombier }
851f43f8ee6SDavid du Colombier
852f43f8ee6SDavid du Colombier static void
sctracefinish(ulong scallnr,ulong sp,int ret,vlong startns)853f43f8ee6SDavid du Colombier sctracefinish(ulong scallnr, ulong sp, int ret, vlong startns)
854f43f8ee6SDavid du Colombier {
855f43f8ee6SDavid du Colombier int s;
856f43f8ee6SDavid du Colombier
857f43f8ee6SDavid du Colombier if(up->procctl == Proc_tracesyscall){
858f43f8ee6SDavid du Colombier up->procctl = Proc_stopme;
859f43f8ee6SDavid du Colombier sysretfmt(scallnr, (va_list)(sp+BY2WD), ret,
860f43f8ee6SDavid du Colombier startns, todget(nil));
861f43f8ee6SDavid du Colombier s = splhi();
862f43f8ee6SDavid du Colombier procctl(up);
863f43f8ee6SDavid du Colombier splx(s);
864f43f8ee6SDavid du Colombier if(up->syscalltrace)
865f43f8ee6SDavid du Colombier free(up->syscalltrace);
866f43f8ee6SDavid du Colombier up->syscalltrace = nil;
867f43f8ee6SDavid du Colombier }
868f43f8ee6SDavid du Colombier }
869f43f8ee6SDavid du Colombier
870f43f8ee6SDavid du Colombier /*
871f43f8ee6SDavid du Colombier * called directly from assembler, not via trap()
872f43f8ee6SDavid du Colombier */
873f43f8ee6SDavid du Colombier long
syscall(Ureg * aur)874f43f8ee6SDavid du Colombier syscall(Ureg *aur)
875f43f8ee6SDavid du Colombier {
876f43f8ee6SDavid du Colombier int i;
877f43f8ee6SDavid du Colombier volatile long ret;
878f43f8ee6SDavid du Colombier ulong sp, scallnr;
879f43f8ee6SDavid du Colombier vlong startns;
880f43f8ee6SDavid du Colombier char *e;
881f43f8ee6SDavid du Colombier Ureg *ur;
882f43f8ee6SDavid du Colombier
883f43f8ee6SDavid du Colombier cycles(&up->kentry);
884f43f8ee6SDavid du Colombier
885f43f8ee6SDavid du Colombier incref(&totalsyscall);
886f43f8ee6SDavid du Colombier m->syscall++;
887f43f8ee6SDavid du Colombier up->insyscall = 1;
888f43f8ee6SDavid du Colombier ur = aur;
889f43f8ee6SDavid du Colombier up->pc = ur->pc;
890f43f8ee6SDavid du Colombier up->dbgreg = aur;
891f43f8ee6SDavid du Colombier ur->cause = 16<<2; /* for debugging: system call is undef 16 */
892f43f8ee6SDavid du Colombier
893f43f8ee6SDavid du Colombier scallnr = ur->r1;
894f43f8ee6SDavid du Colombier up->scallnr = ur->r1;
895f43f8ee6SDavid du Colombier sp = ur->sp;
896f43f8ee6SDavid du Colombier sctracesetup(scallnr, sp, ur->pc, &startns);
897f43f8ee6SDavid du Colombier
898f43f8ee6SDavid du Colombier /* clear EXL in status */
899f43f8ee6SDavid du Colombier setstatus(getstatus() & ~EXL);
900f43f8ee6SDavid du Colombier
901f43f8ee6SDavid du Colombier /* no fpu, so no fp state to save */
902f43f8ee6SDavid du Colombier spllo();
903f43f8ee6SDavid du Colombier
904f43f8ee6SDavid du Colombier up->nerrlab = 0;
905f43f8ee6SDavid du Colombier ret = -1;
906f43f8ee6SDavid du Colombier if(!waserror()) {
907f43f8ee6SDavid du Colombier if(scallnr >= nsyscall || systab[scallnr] == 0){
908f43f8ee6SDavid du Colombier pprint("bad sys call number %ld pc %#lux\n",
909f43f8ee6SDavid du Colombier scallnr, ur->pc);
910f43f8ee6SDavid du Colombier postnote(up, 1, "sys: bad sys call", NDebug);
911f43f8ee6SDavid du Colombier error(Ebadarg);
912f43f8ee6SDavid du Colombier }
913f43f8ee6SDavid du Colombier
914f43f8ee6SDavid du Colombier if(sp & (BY2WD-1)){
915f43f8ee6SDavid du Colombier pprint("odd sp in sys call pc %#lux sp %#lux\n",
916f43f8ee6SDavid du Colombier ur->pc, ur->sp);
917f43f8ee6SDavid du Colombier postnote(up, 1, "sys: odd stack", NDebug);
918f43f8ee6SDavid du Colombier error(Ebadarg);
919f43f8ee6SDavid du Colombier }
920f43f8ee6SDavid du Colombier
921f43f8ee6SDavid du Colombier if(sp<(USTKTOP-BY2PG) || sp>(USTKTOP-sizeof(Sargs)-BY2WD))
922f43f8ee6SDavid du Colombier validaddr(sp, sizeof(Sargs)+BY2WD, 0);
923f43f8ee6SDavid du Colombier
924f43f8ee6SDavid du Colombier up->s = *((Sargs*)(sp+BY2WD));
925f43f8ee6SDavid du Colombier up->psstate = sysctab[scallnr];
926f43f8ee6SDavid du Colombier
927f43f8ee6SDavid du Colombier ret = systab[scallnr](up->s.args);
928f43f8ee6SDavid du Colombier poperror();
929f43f8ee6SDavid du Colombier }else{
930f43f8ee6SDavid du Colombier /* failure: save the error buffer for errstr */
931f43f8ee6SDavid du Colombier e = up->syserrstr;
932f43f8ee6SDavid du Colombier up->syserrstr = up->errstr;
933f43f8ee6SDavid du Colombier up->errstr = e;
934f43f8ee6SDavid du Colombier if(0 && up->pid == 1)
935f43f8ee6SDavid du Colombier print("[%lud %s] syscall %lud: %s\n",
936f43f8ee6SDavid du Colombier up->pid, up->text, scallnr, up->errstr);
937f43f8ee6SDavid du Colombier }
938f43f8ee6SDavid du Colombier if(up->nerrlab){
939f43f8ee6SDavid du Colombier print("bad errstack [%lud]: %d extra\n", scallnr, up->nerrlab);
940f43f8ee6SDavid du Colombier for(i = 0; i < NERR; i++)
941f43f8ee6SDavid du Colombier print("sp=%#lux pc=%#lux\n",
942f43f8ee6SDavid du Colombier up->errlab[i].sp, up->errlab[i].pc);
943f43f8ee6SDavid du Colombier panic("error stack");
944f43f8ee6SDavid du Colombier }
945f43f8ee6SDavid du Colombier sctracefinish(scallnr, sp, ret, startns);
946f43f8ee6SDavid du Colombier
947f43f8ee6SDavid du Colombier ur->pc += 4;
948f43f8ee6SDavid du Colombier ur->r1 = ret;
949f43f8ee6SDavid du Colombier
950f43f8ee6SDavid du Colombier up->psstate = 0;
951f43f8ee6SDavid du Colombier up->insyscall = 0;
952f43f8ee6SDavid du Colombier
953f43f8ee6SDavid du Colombier if(scallnr == NOTED) { /* ugly hack */
954f43f8ee6SDavid du Colombier noted(ur, &aur, *(ulong*)(sp+BY2WD)); /* may return */
955f43f8ee6SDavid du Colombier ret = ur->r1;
956f43f8ee6SDavid du Colombier }
957f43f8ee6SDavid du Colombier incref(&goodsyscall);
958f43f8ee6SDavid du Colombier splhi();
959f43f8ee6SDavid du Colombier if(scallnr!=RFORK && (up->procctl || up->nnote)){
960f43f8ee6SDavid du Colombier ur->r1 = ret; /* load up for noted() above */
961f43f8ee6SDavid du Colombier notify(ur);
962f43f8ee6SDavid du Colombier }
963f43f8ee6SDavid du Colombier /* if we delayed sched because we held a lock, sched now */
964f43f8ee6SDavid du Colombier if(up->delaysched)
965f43f8ee6SDavid du Colombier sched();
966f43f8ee6SDavid du Colombier kexit(ur);
967f43f8ee6SDavid du Colombier
968f43f8ee6SDavid du Colombier /* restore EXL in status */
969f43f8ee6SDavid du Colombier setstatus(getstatus() | EXL);
970f43f8ee6SDavid du Colombier
971f43f8ee6SDavid du Colombier return ret;
972f43f8ee6SDavid du Colombier }
973f43f8ee6SDavid du Colombier
974f43f8ee6SDavid du Colombier void
forkchild(Proc * p,Ureg * ur)975f43f8ee6SDavid du Colombier forkchild(Proc *p, Ureg *ur)
976f43f8ee6SDavid du Colombier {
977f43f8ee6SDavid du Colombier Ureg *cur;
978f43f8ee6SDavid du Colombier
979f43f8ee6SDavid du Colombier p->sched.sp = (ulong)p->kstack+KSTACK-UREGSIZE;
980f43f8ee6SDavid du Colombier p->sched.pc = (ulong)forkret;
981f43f8ee6SDavid du Colombier
982f43f8ee6SDavid du Colombier cur = (Ureg*)(p->sched.sp+2*BY2WD);
983f43f8ee6SDavid du Colombier memmove(cur, ur, sizeof(Ureg));
984f43f8ee6SDavid du Colombier
985f43f8ee6SDavid du Colombier cur->pc += 4;
986f43f8ee6SDavid du Colombier
987f43f8ee6SDavid du Colombier /* Things from bottom of syscall we never got to execute */
988f43f8ee6SDavid du Colombier p->psstate = 0;
989f43f8ee6SDavid du Colombier p->insyscall = 0;
990f43f8ee6SDavid du Colombier }
991f43f8ee6SDavid du Colombier
992f43f8ee6SDavid du Colombier static
993f43f8ee6SDavid du Colombier void
linkproc(void)994f43f8ee6SDavid du Colombier linkproc(void)
995f43f8ee6SDavid du Colombier {
996f43f8ee6SDavid du Colombier spllo();
997f43f8ee6SDavid du Colombier up->kpfun(up->kparg);
998f43f8ee6SDavid du Colombier pexit("kproc exiting", 0);
999f43f8ee6SDavid du Colombier }
1000f43f8ee6SDavid du Colombier
1001f43f8ee6SDavid du Colombier void
kprocchild(Proc * p,void (* func)(void *),void * arg)1002f43f8ee6SDavid du Colombier kprocchild(Proc *p, void (*func)(void*), void *arg)
1003f43f8ee6SDavid du Colombier {
1004f43f8ee6SDavid du Colombier p->sched.pc = (ulong)linkproc;
1005f43f8ee6SDavid du Colombier p->sched.sp = (ulong)p->kstack+KSTACK;
1006f43f8ee6SDavid du Colombier
1007f43f8ee6SDavid du Colombier p->kpfun = func;
1008f43f8ee6SDavid du Colombier p->kparg = arg;
1009f43f8ee6SDavid du Colombier }
1010f43f8ee6SDavid du Colombier
1011f43f8ee6SDavid du Colombier /* set up user registers before return from exec() */
1012f43f8ee6SDavid du Colombier long
execregs(ulong entry,ulong ssize,ulong nargs)1013f43f8ee6SDavid du Colombier execregs(ulong entry, ulong ssize, ulong nargs)
1014f43f8ee6SDavid du Colombier {
1015f43f8ee6SDavid du Colombier Ureg *ur;
1016f43f8ee6SDavid du Colombier ulong *sp;
1017f43f8ee6SDavid du Colombier
1018f43f8ee6SDavid du Colombier sp = (ulong*)(USTKTOP - ssize);
1019f43f8ee6SDavid du Colombier *--sp = nargs;
1020f43f8ee6SDavid du Colombier
1021f43f8ee6SDavid du Colombier ur = (Ureg*)up->dbgreg;
1022f43f8ee6SDavid du Colombier ur->usp = (ulong)sp;
1023f43f8ee6SDavid du Colombier ur->pc = entry - 4; /* syscall advances it */
1024f43f8ee6SDavid du Colombier up->fpsave.fpstatus = initfp.fpstatus;
1025f43f8ee6SDavid du Colombier return USTKTOP-sizeof(Tos); /* address of kernel/user shared data */
1026f43f8ee6SDavid du Colombier }
1027f43f8ee6SDavid du Colombier
1028f43f8ee6SDavid du Colombier ulong
userpc(void)1029f43f8ee6SDavid du Colombier userpc(void)
1030f43f8ee6SDavid du Colombier {
1031f43f8ee6SDavid du Colombier Ureg *ur;
1032f43f8ee6SDavid du Colombier
1033f43f8ee6SDavid du Colombier ur = (Ureg*)up->dbgreg;
1034f43f8ee6SDavid du Colombier return ur->pc;
1035f43f8ee6SDavid du Colombier }
1036f43f8ee6SDavid du Colombier
1037f43f8ee6SDavid du Colombier /*
1038f43f8ee6SDavid du Colombier * This routine must save the values of registers the user is not
1039f43f8ee6SDavid du Colombier * permitted to write from devproc and then restore the saved values
1040f43f8ee6SDavid du Colombier * before returning
1041f43f8ee6SDavid du Colombier */
1042f43f8ee6SDavid du Colombier void
setregisters(Ureg * xp,char * pureg,char * uva,int n)1043f43f8ee6SDavid du Colombier setregisters(Ureg *xp, char *pureg, char *uva, int n)
1044f43f8ee6SDavid du Colombier {
1045f43f8ee6SDavid du Colombier ulong status;
1046f43f8ee6SDavid du Colombier
1047f43f8ee6SDavid du Colombier status = xp->status;
1048f43f8ee6SDavid du Colombier memmove(pureg, uva, n);
1049f43f8ee6SDavid du Colombier xp->status = status;
1050f43f8ee6SDavid du Colombier }
1051f43f8ee6SDavid du Colombier
1052f43f8ee6SDavid du Colombier /*
1053f43f8ee6SDavid du Colombier * Give enough context in the ureg to produce a kernel stack for
1054f43f8ee6SDavid du Colombier * a sleeping process
1055f43f8ee6SDavid du Colombier */
1056f43f8ee6SDavid du Colombier void
setkernur(Ureg * xp,Proc * p)1057f43f8ee6SDavid du Colombier setkernur(Ureg *xp, Proc *p)
1058f43f8ee6SDavid du Colombier {
1059f43f8ee6SDavid du Colombier xp->pc = p->sched.pc;
1060f43f8ee6SDavid du Colombier xp->sp = p->sched.sp;
1061f43f8ee6SDavid du Colombier xp->r24 = (ulong)p; /* up */
1062f43f8ee6SDavid du Colombier xp->r31 = (ulong)sched;
1063f43f8ee6SDavid du Colombier }
1064f43f8ee6SDavid du Colombier
1065f43f8ee6SDavid du Colombier ulong
dbgpc(Proc * p)1066f43f8ee6SDavid du Colombier dbgpc(Proc *p)
1067f43f8ee6SDavid du Colombier {
1068f43f8ee6SDavid du Colombier Ureg *ur;
1069f43f8ee6SDavid du Colombier
1070f43f8ee6SDavid du Colombier ur = p->dbgreg;
1071f43f8ee6SDavid du Colombier if(ur == 0)
1072f43f8ee6SDavid du Colombier return 0;
1073f43f8ee6SDavid du Colombier
1074f43f8ee6SDavid du Colombier return ur->pc;
1075f43f8ee6SDavid du Colombier }
1076