xref: /plan9-contrib/sys/src/9/rb/l.s (revision dc100ed4690b2e24ee94b6fe674abc915c477a6d)
1/*
2 * mips 24k machine assist for routerboard rb450g
3 */
4#include "mem.h"
5#include "mips.s"
6
7#define SANITY 0x12345678
8
9	NOSCHED
10
11/*
12 * Boot only processor
13 */
14TEXT	start(SB), $-4
15	/* save parameters passed from routerboot */
16	MOVW	R4, R19			/* argc */
17	MOVW	R5, R20			/* argv */
18	MOVW	R6, R21			/* envp */
19	MOVW	R7, R23			/* memory size */
20
21	MOVW	$setR30(SB), R30
22
23PUTC('9', R1, R2)
24	DI(0)
25
26	MOVW	sanity(SB), R1
27	CONST(SANITY, R2)
28	SUBU	R1, R2, R2
29	BNE	R2, insane
30	  NOP
31
32	MOVW	R0, M(COMPARE)
33	EHB
34
35	/* don't enable any interrupts nor FP, but leave BEV on. */
36	MOVW	$BEV,R1
37	MOVW	R1, M(STATUS)
38	UBARRIERS(7, R7, stshb)		/* returns to kseg1 space */
39	MOVW	R0, M(CAUSE)
40	EHB
41
42	/* silence the atheros watchdog */
43	MOVW	$(KSEG1|0x18060008), R1
44	MOVW	R0, (R1)			/* set no action */
45	SYNC
46
47	MOVW	$PE, R1
48	MOVW	R1, M(CACHEECC)		/* aka ErrCtl */
49	EHB
50	JAL	cleancache(SB)
51	  NOP
52
53	MOVW	$TLBROFF, R1
54	MOVW	R1, M(WIRED)
55
56	MOVW	R0, M(CONTEXT)
57	EHB
58
59	/* set KSEG0 cachability before trying LL/SC in lock code */
60	MOVW	M(CONFIG), R1
61	AND	$~CFG_K0, R1
62	/* make kseg0 cachable, enable write-through merging */
63	OR	$((PTECACHABILITY>>3)|CFG_MM), R1
64	MOVW	R1, M(CONFIG)
65	BARRIERS(7, R7, cfghb)			/* back to kseg0 space */
66
67	MOVW	$setR30(SB), R30		/* again */
68
69	/* initialize Mach, including stack */
70	MOVW	$MACHADDR, R(MACH)
71	ADDU	$(MACHSIZE-BY2V), R(MACH), SP
72	MOVW	R(MACH), R1
73clrmach:
74	MOVW	R0, (R1)
75	ADDU	$BY2WD, R1
76	BNE	R1, SP, clrmach
77	  NOP
78	MOVW	R0, 0(R(MACH))			/* m->machno = 0 */
79	MOVW	R0, R(USER)			/* up = nil */
80
81	/* zero bss */
82	MOVW	$edata(SB), R1
83	MOVW	$end(SB), R2
84clrbss:
85	MOVW	R0, (R1)
86	ADDU	$BY2WD, R1
87	BNE	R1, R2, clrbss
88	  NOP
89
90	MOVW	R0, HI
91	MOVW	R0, LO
92
93PUTC('\r', R1, R2)
94PUTC('\n', R1, R2)
95
96	/*
97	 * restore parameters passed from routerboot and
98	 * pass them as arguments to main().
99	 */
100	SUB	$(5*4), SP	/* reserve space for args to main() + link */
101	MOVW	R19, R1			/* argc */
102	MOVW	R20, 8(SP)		/* argv */
103	MOVW	R21, 12(SP)		/* envp */
104	MOVW	R23, 16(SP)		/* memory size */
105
106	MOVW	$0x16, R16
107	MOVW	$0x17, R17
108	MOVW	$0x18, R18
109	MOVW	$0x19, R19
110	MOVW	$0x20, R20
111	MOVW	$0x21, R21
112	MOVW	$0x22, R22
113	MOVW	$0x23, R23
114
115	JAL	main(SB)
116	  NOP
117
118	/* shouldn't get here */
119	CONST(ROM, R1)
120	JMP	(R1)			/* back to the rom */
121	  NOP
122
123#define PUT(c) PUTC(c, R1, R2)
124#define DELAY(lab) \
125	CONST(34000000, R3); \
126lab:	SUBU	$1, R3; \
127	BNE	R3, lab; \
128	  NOP
129
130insane:
131	/*
132	 * data segment is misaligned; kernel needs vl -R4096 or -R16384,
133	 * as appropriate, for reboot.
134	 */
135	PUT('?'); PUT('d'); PUT('a'); PUT('t'); PUT('a'); PUT(' '); DELAY(dl1)
136	PUT('s'); PUT('e'); PUT('g'); PUT('m'); PUT('e'); PUT('n'); DELAY(dl2)
137	PUT('t'); PUT(' '); PUT('m'); PUT('i'); PUT('s'); PUT('a'); DELAY(dl3)
138	PUT('l'); PUT('i'); PUT('g'); PUT('n'); PUT('e'); PUT('d'); DELAY(dl4)
139	PUT('\r'); PUT('\n'); DELAY(dl5)
140	CONST(ROM, R1)
141	JMP	(R1)			/* back to the rom */
142	  NOP
143
144/* target for JALRHB in BARRIERS */
145TEXT ret(SB), $-4
146	JMP	(R22)
147	  NOP
148
149/* print R1 in hex; clobbers R3—8 */
150TEXT printhex(SB), $-4
151	MOVW	$32, R5
152	MOVW	$9, R7
153prtop:
154	SUB	$4, R5
155	MOVW	R1, R6
156	SRL	R5, R6
157	AND	$0xf, R6
158	SGTU	R6, R7, R8
159	BEQ	R8, prdec		/* branch if R6 <= 9 */
160	  NOP
161	ADD	$('a'-10), R6
162	JMP	prchar
163	  NOP
164prdec:
165	ADD	$'0', R6
166prchar:
167	PUTC(R6, R3, R4)
168	BNE	R5, prtop
169	  NOP
170	RETURN
171
172/*
173 * Take first processor into user mode
174 * 	- argument is stack pointer to user
175 */
176TEXT	touser(SB), $-4
177	DI(0)			/* ensure intrs see consistent M(STATUS) */
178	MOVW	R1, SP
179	MOVW	$(UTZERO+32), R2	/* header appears in text */
180	MOVW	R2, M(EPC)
181	EHB
182	MOVW	M(STATUS), R4
183	AND	$(~KMODEMASK), R4
184	OR	$(KUSER|IE|EXL), R4	/* switch to user mode, intrs on, exc */
185	MOVW	R4, M(STATUS)		/* " */
186	ERET				/* clears EXL */
187
188/*
189 * manipulate interrupts
190 */
191
192/* enable an interrupt; bit is in R1 */
193TEXT	intron(SB), $0
194	MOVW	M(STATUS), R2
195	OR	R1, R2
196	MOVW	R2, M(STATUS)
197	EHB
198	RETURN
199
200/* disable an interrupt; bit is in R1 */
201TEXT	introff(SB), $0
202	MOVW	M(STATUS), R2
203	XOR	$-1, R1
204	AND	R1, R2
205	MOVW	R2, M(STATUS)
206	EHB
207	RETURN
208
209/* on our 24k, wait instructions are not interruptible, alas. */
210TEXT	idle(SB), $-4
211	EI(1)				/* old M(STATUS) into R1 */
212	EHB
213	/* fall through */
214
215TEXT	wait(SB), $-4
216	WAIT
217	  NOP
218
219	MOVW	R1, M(STATUS)		/* interrupts restored */
220	EHB
221	RETURN
222
223TEXT	splhi(SB), $0
224	EHB
225	MOVW	R31, 12(R(MACH))	/* save PC in m->splpc */
226	DI(1)				/* old M(STATUS) into R1 */
227	EHB
228	RETURN
229
230TEXT	splx(SB), $0
231	EHB
232	MOVW	R31, 12(R(MACH))	/* save PC in m->splpc */
233	MOVW	M(STATUS), R2
234	AND	$IE, R1
235	AND	$~IE, R2
236	OR	R2, R1
237	MOVW	R1, M(STATUS)
238	EHB
239	RETURN
240
241TEXT	spllo(SB), $0
242	EHB
243	EI(1)				/* old M(STATUS) into R1 */
244	EHB
245	RETURN
246
247TEXT	spldone(SB), $0
248	RETURN
249
250TEXT	islo(SB), $0
251	MOVW	M(STATUS), R1
252	AND	$IE, R1
253	RETURN
254
255TEXT	coherence(SB), $-4
256	BARRIERS(7, R7, cohhb)
257	SYNC
258	EHB
259	RETURN
260
261/*
262 * bit twiddling
263 */
264
265TEXT	clz(SB), $-4		/* dest = clz(src): count leading zeroes */
266	CLZ(1, 1)
267	RETURN
268
269/*
270 * process switching
271 */
272
273TEXT	setlabel(SB), $-4
274	MOVW	R29, 0(R1)
275	MOVW	R31, 4(R1)
276	MOVW	R0, R1
277	RETURN
278
279TEXT	gotolabel(SB), $-4
280	MOVW	0(R1), R29
281	MOVW	4(R1), R31
282	MOVW	$1, R1
283	RETURN
284
285/*
286 * the tlb routines need to be called at splhi.
287 */
288
289TEXT	puttlb(SB), $0			/* puttlb(virt, phys0, phys1) */
290	EHB
291	MOVW	R1, M(TLBVIRT)
292	EHB
293	MOVW	4(FP), R2		/* phys0 */
294	MOVW	8(FP), R3		/* phys1 */
295	MOVW	R2, M(TLBPHYS0)
296	EHB
297	MOVW	$PGSZ, R1
298	MOVW	R3, M(TLBPHYS1)
299	EHB
300	MOVW	R1, M(PAGEMASK)
301	OR	R2, R3, R4		/* MTC0 delay slot */
302	AND	$PTEVALID, R4		/* MTC0 delay slot */
303	EHB
304	TLBP				/* tlb probe */
305	EHB
306	MOVW	M(INDEX), R1
307	BGEZ	R1, index		/* if tlb entry found, use it */
308	  NOP
309	BEQ	R4, dont		/* not valid? cf. kunmap */
310	  NOP
311	MOVW	M(RANDOM), R1		/* write random tlb entry */
312	MOVW	R1, M(INDEX)
313index:
314	EHB
315	TLBWI				/* write indexed tlb entry */
316	JRHB(31)			/* return and clear all hazards */
317dont:
318	RETURN
319
320TEXT	getwired(SB),$0
321	MOVW	M(WIRED), R1
322	RETURN
323
324TEXT	setwired(SB),$0
325	MOVW	R1, M(WIRED)
326	EHB
327	RETURN
328
329TEXT	getrandom(SB),$0
330	MOVW	M(RANDOM), R1
331	RETURN
332
333TEXT	getpagemask(SB),$0
334	MOVW	M(PAGEMASK), R1
335	RETURN
336
337TEXT	setpagemask(SB),$0
338	EHB
339	MOVW	R1, M(PAGEMASK)
340	EHB
341	MOVW	R0, R1			/* prevent accidents */
342	RETURN
343
344TEXT	puttlbx(SB), $0	/* puttlbx(index, virt, phys0, phys1, pagemask) */
345	MOVW	4(FP), R2
346	MOVW	8(FP), R3
347	MOVW	12(FP), R4
348	MOVW	16(FP), R5
349	EHB
350	MOVW	R2, M(TLBVIRT)
351	EHB
352	MOVW	R3, M(TLBPHYS0)
353	MOVW	R4, M(TLBPHYS1)
354	MOVW	R5, M(PAGEMASK)
355	EHB
356	MOVW	R1, M(INDEX)
357	EHB
358	TLBWI				/* write indexed tlb entry */
359	JRHB(31)			/* return and clear all hazards */
360
361TEXT	tlbvirt(SB), $0
362	EHB
363	MOVW	M(TLBVIRT), R1
364	EHB
365	RETURN
366
367TEXT	gettlbx(SB), $0			/* gettlbx(index, &entry) */
368	MOVW	4(FP), R5
369	MOVW	M(TLBVIRT), R10		/* save our asid */
370	EHB
371	MOVW	R1, M(INDEX)
372	EHB
373	TLBR				/* read indexed tlb entry */
374	EHB
375	MOVW	M(TLBVIRT), R2
376	MOVW	M(TLBPHYS0), R3
377	MOVW	M(TLBPHYS1), R4
378	MOVW	R2, 0(R5)
379	MOVW	R3, 4(R5)
380	MIPS24KNOP
381	MOVW	R4, 8(R5)
382	EHB
383	MOVW	R10, M(TLBVIRT)		/* restore our asid */
384	EHB
385	RETURN
386
387TEXT	gettlbp(SB), $0			/* gettlbp(tlbvirt, &entry) */
388	MOVW	4(FP), R5
389	MOVW	M(TLBVIRT), R10		/* save our asid */
390	EHB
391	MOVW	R1, M(TLBVIRT)
392	EHB
393	TLBP				/* probe tlb */
394	EHB
395	MOVW	M(INDEX), R1
396	BLTZ	R1, gettlbp1		/* if no tlb entry found, return */
397	  NOP
398	EHB
399	TLBR				/* read indexed tlb entry */
400	EHB
401	MOVW	M(TLBVIRT), R2
402	MOVW	M(TLBPHYS0), R3
403	MOVW	M(TLBPHYS1), R4
404	MOVW	M(PAGEMASK), R6
405	MOVW	R2, 0(R5)
406	MOVW	R3, 4(R5)
407	MIPS24KNOP
408	MOVW	R4, 8(R5)
409	MOVW	R6, 12(R5)
410gettlbp1:
411	EHB
412	MOVW	R10, M(TLBVIRT)		/* restore our asid */
413	EHB
414	RETURN
415
416TEXT	gettlbvirt(SB), $0		/* gettlbvirt(index) */
417	MOVW	M(TLBVIRT), R10		/* save our asid */
418	EHB
419	MOVW	R1, M(INDEX)
420	EHB
421	TLBR				/* read indexed tlb entry */
422	EHB
423	MOVW	M(TLBVIRT), R1
424	EHB
425	MOVW	R10, M(TLBVIRT)		/* restore our asid */
426	EHB
427	RETURN
428
429/*
430 * exceptions.
431 *
432 * mips promises that there will be no current hazards upon entry
433 * to exception handlers.
434 */
435
436	/* will be copied into low memory vectors; must fit in 32 words */
437TEXT	vector0(SB), $-4
438	MOVW	$utlbmiss(SB), R26
439	JMP	(R26)
440	  NOP
441
442/*
443 * compute stlb hash index.
444 * must match index calculation in mmu.c/putstlb()
445 *
446 * M(TLBVIRT) [page & asid] in arg, result in arg.
447 * stir in swizzled asid; we get best results with asid in both high & low bits.
448 *
449 * page = tlbvirt >> (PGSHIFT+1);	// ignoring even/odd bit
450 * arg = (tlbvirt<<(STLBLOG-8) ^ (uchar)tlbvirt ^ page ^
451 *	((page & (MASK(HIPFNBITS) << STLBLOG)) >> HIPFNBITS)) & (STLBSIZE-1);
452 */
453#define STLBHASH(arg, tmp, tmp2) \
454	MOVW	arg, tmp2; \
455	SRL	$(PGSHIFT+1), arg;	/* move low page # bits to low bits */ \
456	CONST	((MASK(HIPFNBITS) << STLBLOG), tmp); \
457	AND	arg, tmp;		/* extract high page # bits */ \
458	SRL	$HIPFNBITS, tmp;	/* position them */ \
459	XOR	tmp, arg;		/* include them */ \
460	MOVW	tmp2, tmp;		/* asid in low byte */ \
461	SLL	$(STLBLOG-8), tmp;	/* move asid to high bits */ \
462	XOR	tmp, arg;		/* include asid in high bits too */ \
463	MOVBU	tmp2, tmp;		/* asid in low byte of tlbvirt */ \
464	XOR	tmp, arg;		/* include asid in low bits */ \
465	CONST	(STLBSIZE-1, tmp); \
466	AND	tmp, arg		/* chop to fit */
467/*
468 * vc -S generated essentially this, which uses 4 registers and
469 * R28 for big constants:
470	MOVW	R1, R4
471	SRL	$(PGSHIFT+1), R1, R3	// page = tlbvirt>>(PGSHIFT+1)
472	SLL	$(STLBLOG-8), R1, R1	// tlbvirt<<(STLBLOG-8)
473	MOVBU	R4, R5			// (uchar)tlbvirt
474	XOR	R5, R1
475	XOR	R3, R1
476	AND	$(MASK(HIPFNBITS) << STLBLOG), R3
477	SRL	$HIPFNBITS, R3
478	XOR	R3, R1
479	AND	$(STLBSIZE-1), R1	// chop to fit
480 */
481
482TEXT	utlbmiss(SB), $-4
483	/*
484	 * don't use R28 by using constants that span both word halves,
485	 * it's unsaved so far.  avoid R24 (up in kernel) and R25 (m in kernel).
486	 */
487	/* update statistics */
488	CONST	(MACHADDR+16, R26)	/* R26 = &m->tlbfault */
489	MOVW	(R26), R27
490	ADDU	$1, R27
491	MOVW	R27, (R26)		/* m->tlbfault++ */
492
493	MOVW	R23, M(DESAVE)		/* save R23 */
494
495#ifdef KUTLBSTATS
496	ADDU	$4, R26			/* &m->ktlbfault */
497
498	MOVW	M(STATUS), R23
499	AND	$KUSER, R23
500	BEQ	R23, kmiss
501	  NOP
502	ADDU	$4, R26			/* m->utlbfault */
503kmiss:
504	MOVW	(R26), R27
505	ADDU	$1, R27
506	MOVW	R27, (R26)		/* m->[ku]tlbfault++ */
507#endif
508
509	/* compute stlb index */
510	EHB
511	MOVW	M(TLBVIRT), R27		/* asid in low byte */
512	STLBHASH(R27, R26, R23)
513	MOVW	M(DESAVE), R23		/* restore R23 */
514
515	/* scale to a byte index (multiply by 12 [3 ulongs]) */
516	SLL	$1, R27, R26		/* × 2 */
517	ADDU	R26, R27		/* × 3 */
518	SLL	$2, R27			/* × 12 */
519
520	CONST	(MACHADDR, R26)		/* R26 = m-> */
521	MOVW	4(R26), R26		/* R26 = m->stb */
522	ADDU	R26, R27		/* R27 = &m->stb[hash] */
523
524	MOVW	M(BADVADDR), R26
525	AND	$BY2PG, R26
526	BNE	R26, utlbodd		/* odd page? */
527	  NOP
528
529utlbeven:
530	MOVW	4(R27), R26		/* R26 = m->stb[hash].phys0 */
531	BEQ	R26, stlbm		/* nothing cached? do it the hard way */
532	  NOP
533	MOVW	R26, M(TLBPHYS0)
534	EHB
535	MOVW	8(R27), R26		/* R26 = m->stb[hash].phys1 */
536	JMP	utlbcom
537	  MOVW	R26, M(TLBPHYS1)	/* branch delay slot */
538
539utlbodd:
540	MOVW	8(R27), R26		/* R26 = m->stb[hash].phys1 */
541	BEQ	R26, stlbm		/* nothing cached? do it the hard way */
542	  NOP
543	MOVW	R26, M(TLBPHYS1)
544	EHB
545	MOVW	4(R27), R26		/* R26 = m->stb[hash].phys0 */
546	MOVW	R26, M(TLBPHYS0)
547
548utlbcom:
549	EHB				/* MTC0/MFC0 hazard */
550	MOVW	M(TLBVIRT), R26
551	MOVW	(R27), R27		/* R27 = m->stb[hash].virt */
552	BEQ	R27, stlbm		/* nothing cached? do it the hard way */
553	  NOP
554	/* is the stlb entry for the right virtual address? */
555	BNE	R26, R27, stlbm		/* M(TLBVIRT) != m->stb[hash].virt? */
556	  NOP
557
558	/* if an entry exists, overwrite it, else write a random one */
559	CONST	(PGSZ, R27)
560	MOVW	R27, M(PAGEMASK)	/* select page size */
561	EHB
562	TLBP				/* probe tlb */
563	EHB
564	MOVW	M(INDEX), R26
565	BGEZ	R26, utlindex		/* if tlb entry found, rewrite it */
566	  EHB				/* delay slot */
567	TLBWR				/* else write random tlb entry */
568	ERET
569utlindex:
570	TLBWI				/* write indexed tlb entry */
571	ERET
572
573/*
574 * will be copied into low memory vectors; must fit in 32 words
575 * and avoid relative branches.
576 */
577TEXT	vector100(SB), $-4		/* cache trap */
578TEXT	vector180(SB), $-4		/* most exceptions */
579stlbm:			/* not in the stlb either; make trap.c figure it out */
580	MOVW	$exception(SB), R26
581	JMP	(R26)
582	  NOP
583
584TEXT	stlbhash(SB), $-4
585	STLBHASH(R1, R2, R3)
586	RETURN
587
588/*
589 * exceptions other than tlb miss come here directly.
590 */
591TEXT	exception(SB), $-4
592	MOVW	M(STATUS), R26
593	AND	$KUSER, R26, R27
594	BEQ	R27, waskernel
595	  MOVW	SP, R27			/* delay slot */
596
597wasuser:
598	CONST	(MACHADDR, SP)		/*  m-> */
599	MOVW	8(SP), SP		/*  m->proc */
600	MOVW	8(SP), SP		/*  m->proc->kstack */
601	MOVW	M(STATUS), R26		/* redundant load */
602	ADDU	$(KSTACK-UREGSIZE), SP
603	MOVW	R31, Ureg_r31(SP)
604
605	JAL	savereg1(SB)
606	  NOP
607
608	MOVW	R30, Ureg_r30(SP)
609	MOVW	R(MACH), Ureg_r25(SP)
610	MIPS24KNOP
611	MOVW	R(USER), Ureg_r24(SP)
612
613	MOVW	$setR30(SB), R30
614	CONST	(MACHADDR, R(MACH))		/* R(MACH) = m-> */
615	MOVW	8(R(MACH)), R(USER)		/* up = m->proc */
616
617	AND	$(EXCMASK<<2), R26, R1
618	SUBU	$(CSYS<<2), R1
619	BNE	R1, notsys
620	  NOP
621
622	/* the carrera does this: */
623//	ADDU	$8, SP, R1			/* first arg for syscall */
624
625	MOVW	SP, R1				/* first arg for syscall */
626	JAL	syscall(SB)			/* to C */
627	  SUBU	$Notuoffset, SP			/* delay slot */
628sysrestore:
629	JAL	restreg1(SB)
630	  ADDU	$Notuoffset, SP			/* delay slot */
631
632	MOVW	Ureg_r31(SP), R31
633	MOVW	Ureg_status(SP), R26
634	MOVW	Ureg_r30(SP), R30
635	MOVW	R26, M(STATUS)
636	EHB
637	MOVW	Ureg_pc(SP), R26		/* old pc */
638erettor26:
639	MOVW	Ureg_sp(SP), SP
640	MOVW	R26, M(EPC)
641	ERET
642
643notsys:
644	JAL	savereg2(SB)
645	  NOP
646
647	/* the carrera does this: */
648//	ADDU	$8, SP, R1			/* first arg for trap */
649
650	MOVW	SP, R1				/* first arg for trap */
651	JAL	trap(SB)			/* to C for user trap */
652	  SUBU	$Notuoffset, SP			/* delay slot */
653
654	ADDU	$Notuoffset, SP
655
656restore:
657	JAL	restreg1(SB)
658	  NOP
659	JAL	restreg2(SB)		/* restores R28, among others */
660	  NOP
661
662	MOVW	Ureg_r30(SP), R30
663	MOVW	Ureg_r31(SP), R31
664	MOVW	Ureg_r25(SP), R(MACH)
665	MOVW	Ureg_r24(SP), R(USER)
666	JMP	erettor26
667	  NOP
668
669waskernel:
670	SUBU	$UREGSIZE, SP
671	OR	$7, SP				/* conservative rounding */
672	XOR	$7, SP
673	MOVW	R31, Ureg_r31(SP)
674
675	JAL	savereg1(SB)
676	  NOP
677	JAL	savereg2(SB)
678	  NOP
679
680	/* the carrera does this: */
681//	ADDU	$8, SP, R1			/* first arg for trap */
682
683	MOVW	SP, R1				/* first arg for trap */
684	JAL	trap(SB)			/* to C for kernel trap */
685	  SUBU	$Notuoffset, SP			/* delay slot */
686
687	ADDU	$Notuoffset, SP
688
689	JAL	restreg1(SB)
690	  NOP
691
692	/*
693	 * if about to return to `wait', interrupt arrived just before
694	 * executing wait, so move saved pc past it.
695	 */
696	MOVW	Ureg_pc(SP), R26
697	MOVW	R26, R31
698	MOVW	$wait(SB), R1
699	SUBU	R1, R31
700	BNE	R31, notwait
701	  NOP
702	ADD	$BY2WD, R26		/* advance saved pc */
703	MOVW	R26, Ureg_pc(SP)
704notwait:
705	JAL	restreg2(SB)		/* restores R28, among others */
706	  NOP
707
708	MOVW	Ureg_r31(SP), R31
709	JMP	erettor26
710	  NOP
711
712TEXT	forkret(SB), $0
713	JMP	sysrestore
714	  MOVW	R0, R1			/* delay slot; child returns 0 */
715
716/*
717 * save mandatory registers.
718 * called with old M(STATUS) in R26.
719 * called with old SP in R27
720 * returns with M(CAUSE) in R26
721 */
722TEXT	savereg1(SB), $-4
723	MOVW	R1, Ureg_r1(SP)
724
725	MOVW	$(~KMODEMASK),R1	/* don't use R28, it's unsaved so far */
726	AND	R26, R1
727	MOVW	R1, M(STATUS)
728	EHB
729
730	MOVW	R26, Ureg_status(SP)	/* status */
731	MOVW	R27, Ureg_sp(SP)	/* user SP */
732
733	MOVW	M(EPC), R1
734	MOVW	M(CAUSE), R26
735
736	MOVW	R23, Ureg_r23(SP)
737	MOVW	R22, Ureg_r22(SP)
738	MIPS24KNOP
739	MOVW	R21, Ureg_r21(SP)
740	MOVW	R20, Ureg_r20(SP)
741	MIPS24KNOP
742	MOVW	R19, Ureg_r19(SP)
743	MOVW	R1, Ureg_pc(SP)
744	RETURN
745
746/*
747 * all other registers.
748 * called with M(CAUSE) in R26
749 */
750TEXT	savereg2(SB), $-4
751	MOVW	R2, Ureg_r2(SP)
752
753	MOVW	M(BADVADDR), R2
754	MOVW	R26, Ureg_cause(SP)
755	MOVW	M(TLBVIRT), R1
756	MOVW	R2, Ureg_badvaddr(SP)
757	MOVW	R1, Ureg_tlbvirt(SP)
758	MOVW	HI, R1
759	MOVW	LO, R2
760	MOVW	R1, Ureg_hi(SP)
761	MOVW	R2, Ureg_lo(SP)
762	MIPS24KNOP
763					/* LINK,SB,SP missing */
764	MOVW	R28, Ureg_r28(SP)
765					/* R27, R26 not saved */
766					/* R25, R24 missing */
767					/* R23- R19 saved in save1 */
768	MOVW	R18, Ureg_r18(SP)
769	MIPS24KNOP
770	MOVW	R17, Ureg_r17(SP)
771	MOVW	R16, Ureg_r16(SP)
772	MIPS24KNOP
773	MOVW	R15, Ureg_r15(SP)
774	MOVW	R14, Ureg_r14(SP)
775	MIPS24KNOP
776	MOVW	R13, Ureg_r13(SP)
777	MOVW	R12, Ureg_r12(SP)
778	MIPS24KNOP
779	MOVW	R11, Ureg_r11(SP)
780	MOVW	R10, Ureg_r10(SP)
781	MIPS24KNOP
782	MOVW	R9, Ureg_r9(SP)
783	MOVW	R8, Ureg_r8(SP)
784	MIPS24KNOP
785	MOVW	R7, Ureg_r7(SP)
786	MOVW	R6, Ureg_r6(SP)
787	MIPS24KNOP
788	MOVW	R5, Ureg_r5(SP)
789	MOVW	R4, Ureg_r4(SP)
790	MIPS24KNOP
791	MOVW	R3, Ureg_r3(SP)
792	RETURN
793
794TEXT	restreg1(SB), $-4
795	MOVW	Ureg_r23(SP), R23
796	MOVW	Ureg_r22(SP), R22
797	MOVW	Ureg_r21(SP), R21
798	MOVW	Ureg_r20(SP), R20
799	MOVW	Ureg_r19(SP), R19
800	RETURN
801
802TEXT	restreg2(SB), $-4
803					/* LINK,SB,SP missing */
804	MOVW	Ureg_r28(SP), R28
805					/* R27, R26 not saved */
806					/* R25, R24 missing */
807					/* R19- R23 restored in rest1 */
808	MOVW	Ureg_r18(SP), R18
809	MOVW	Ureg_r17(SP), R17
810	MOVW	Ureg_r16(SP), R16
811	MOVW	Ureg_r15(SP), R15
812	MOVW	Ureg_r14(SP), R14
813	MOVW	Ureg_r13(SP), R13
814	MOVW	Ureg_r12(SP), R12
815	MOVW	Ureg_r11(SP), R11
816	MOVW	Ureg_r10(SP), R10
817	MOVW	Ureg_r9(SP), R9
818	MOVW	Ureg_r8(SP), R8
819	MOVW	Ureg_r7(SP), R7
820	MOVW	Ureg_r6(SP), R6
821	MOVW	Ureg_r5(SP), R5
822	MOVW	Ureg_r4(SP), R4
823	MOVW	Ureg_r3(SP), R3
824	MOVW	Ureg_lo(SP), R2
825	MOVW	Ureg_hi(SP), R1
826	MOVW	R2, LO
827	MOVW	R1, HI
828
829	MOVW	Ureg_status(SP), R1
830	MOVW	Ureg_r2(SP), R2
831	MOVW	R1, M(STATUS)		/* could change interruptibility */
832	EHB
833	MOVW	Ureg_r1(SP), R1	/* BOTCH */
834	MOVW	Ureg_pc(SP), R26
835	RETURN
836
837/*
838 * degenerate floating-point stuff
839 */
840
841TEXT	clrfpintr(SB), $0
842	RETURN
843
844TEXT	savefpregs(SB), $0
845	RETURN
846
847TEXT	restfpregs(SB), $0
848	RETURN
849
850TEXT	fcr31(SB), $0			/* fp csr */
851	MOVW	R0, R1
852	RETURN
853
854/*
855 * Emulate 68020 test and set: load linked / store conditional
856 */
857
858TEXT	tas(SB), $0
859	MOVW	R1, R2		/* address of key */
860tas1:
861	MOVW	$1, R3
862	LL(2, 1)
863	NOP
864	SC(2, 3)
865	NOP
866	BEQ	R3, tas1
867	  NOP
868	RETURN
869
870/* used by the semaphore implementation */
871TEXT cmpswap(SB), $0
872	MOVW	R1, R2		/* address of key */
873	MOVW	old+4(FP), R3	/* old value */
874	MOVW	new+8(FP), R4	/* new value */
875	LL(2, 1)		/* R1 = (R2) */
876	NOP
877	BNE	R1, R3, fail
878	  NOP
879	MOVW	R4, R1
880	SC(2, 1)	/* (R2) = R1 if (R2) hasn't changed; R1 = success */
881	NOP
882	RETURN
883fail:
884	MOVW	R0, R1
885	RETURN
886
887/*
888 *  cache manipulation
889 */
890
891/*
892 *  we avoided using R4, R5, R6, and R7 so gotopc can call us without saving
893 *  them, but gotopc is now gone.
894 */
895TEXT	icflush(SB), $-4			/* icflush(virtaddr, count) */
896	MOVW	4(FP), R9
897	DI(10)				/* intrs off, old status -> R10 */
898	UBARRIERS(7, R7, ichb);		/* return to kseg1 (uncached) */
899	ADDU	R1, R9			/* R9 = last address */
900	MOVW	$(~(CACHELINESZ-1)), R8
901	AND	R1, R8			/* R8 = first address, rounded down */
902	ADDU	$(CACHELINESZ-1), R9
903	AND	$(~(CACHELINESZ-1)), R9	/* round last address up */
904	SUBU	R8, R9			/* R9 = revised count */
905icflush1:
906//	CACHE	PD+HWB, (R8)		/* flush D to ram */
907	CACHE	PI+HINV, (R8)		/* invalidate in I */
908	SUBU	$CACHELINESZ, R9
909	BGTZ	R9, icflush1
910	  ADDU	$CACHELINESZ, R8	/* delay slot */
911
912	BARRIERS(7, R7, ic2hb);		/* return to kseg0 (cached) */
913	MOVW	R10, M(STATUS)
914	JRHB(31)			/* return and clear all hazards */
915
916TEXT	dcflush(SB), $-4			/* dcflush(virtaddr, count) */
917	MOVW	4(FP), R9
918	DI(10)				/* intrs off, old status -> R10 */
919	SYNC
920	EHB
921	ADDU	R1, R9			/* R9 = last address */
922	MOVW	$(~(CACHELINESZ-1)), R8
923	AND	R1, R8			/* R8 = first address, rounded down */
924	ADDU	$(CACHELINESZ-1), R9
925	AND	$(~(CACHELINESZ-1)), R9	/* round last address up */
926	SUBU	R8, R9			/* R9 = revised count */
927dcflush1:
928//	CACHE	PI+HINV, (R8)		/* invalidate in I */
929	CACHE	PD+HWBI, (R8)		/* flush & invalidate in D */
930	SUBU	$CACHELINESZ, R9
931	BGTZ	R9, dcflush1
932	  ADDU	$CACHELINESZ, R8	/* delay slot */
933	SYNC
934	EHB
935	MOVW	R10, M(STATUS)
936	JRHB(31)			/* return and clear all hazards */
937
938/* the i and d caches may be different sizes, so clean them separately */
939TEXT	cleancache(SB), $-4
940	DI(10)				/* intrs off, old status -> R10 */
941
942	UBARRIERS(7, R7, cchb);		/* return to kseg1 (uncached) */
943	MOVW	R0, R1			/* index, not address */
944	MOVW	$ICACHESIZE, R9
945iccache:
946	CACHE	PI+IWBI, (R1)		/* flush & invalidate I by index */
947	SUBU	$CACHELINESZ, R9
948	BGTZ	R9, iccache
949	  ADDU	$CACHELINESZ, R1	/* delay slot */
950
951	BARRIERS(7, R7, cc2hb);		/* return to kseg0 (cached) */
952
953	MOVW	R0, R1			/* index, not address */
954	MOVW	$DCACHESIZE, R9
955dccache:
956	CACHE	PD+IWBI, (R1)		/* flush & invalidate D by index */
957	SUBU	$CACHELINESZ, R9
958	BGTZ	R9, dccache
959	  ADDU	$CACHELINESZ, R1	/* delay slot */
960
961	SYNC
962	MOVW	R10, M(STATUS)
963	JRHB(31)			/* return and clear all hazards */
964
965/*
966 * access to CP0 registers
967 */
968
969TEXT	prid(SB), $0
970	MOVW	M(PRID), R1
971	RETURN
972
973TEXT	rdcount(SB), $0
974	MOVW	M(COUNT), R1
975	RETURN
976
977TEXT	wrcount(SB), $0
978	MOVW	R1, M(COUNT)
979	EHB
980	RETURN
981
982TEXT	wrcompare(SB), $0
983	MOVW	R1, M(COMPARE)
984	EHB
985	RETURN
986
987TEXT	rdcompare(SB), $0
988	MOVW	M(COMPARE), R1
989	RETURN
990
991TEXT	getconfig(SB), $-4
992	MOVW	M(CONFIG), R1
993	RETURN
994
995TEXT	getconfig1(SB), $-4
996	MFC0(CONFIG, 1, 1)
997	RETURN
998
999TEXT	getconfig2(SB), $-4
1000	MFC0(CONFIG, 2, 1)
1001	RETURN
1002
1003TEXT	getconfig3(SB), $-4
1004	MFC0(CONFIG, 3, 1)
1005	RETURN
1006
1007TEXT	getconfig4(SB), $-4
1008	MFC0(CONFIG, 4, 1)
1009	RETURN
1010
1011TEXT	getconfig7(SB), $-4
1012	MFC0(CONFIG, 7, 1)
1013	RETURN
1014
1015TEXT	gethwreg3(SB), $-4
1016	RDHWR(3, 1)
1017	RETURN
1018
1019TEXT	getcause(SB), $-4
1020	MOVW	M(CAUSE), R1
1021	RETURN
1022
1023TEXT	C_fcr0(SB), $-4		/* fp implementation */
1024	MOVW	$0x500, R1	/* claim to be an r4k, thus have ll/sc */
1025	RETURN
1026
1027TEXT	getstatus(SB), $0
1028	MOVW	M(STATUS), R1
1029	RETURN
1030
1031TEXT	setstatus(SB), $0
1032	MOVW	R1, M(STATUS)
1033	EHB
1034	RETURN
1035
1036TEXT	setwatchhi0(SB), $0
1037	MOVW	R1, M(WATCHHI)
1038	EHB
1039	RETURN
1040
1041/*
1042 * beware that the register takes a double-word address, so it's not
1043 * precise to the individual instruction.
1044 */
1045TEXT	setwatchlo0(SB), $0
1046	MOVW	R1, M(WATCHLO)
1047	EHB
1048	RETURN
1049
1050TEXT	setsp(SB), $-4
1051	MOVW	R1, SP
1052	RETURN
1053
1054TEXT	getintctl(SB), $-4
1055	MFC0(STATUS, 1, 1)
1056	RETURN
1057
1058TEXT	getsrsctl(SB), $-4
1059	MFC0(STATUS, 2, 1)
1060	RETURN
1061
1062TEXT	getsrsmap(SB), $-4
1063	MFC0(STATUS, 3, 1)
1064	RETURN
1065
1066TEXT	getperfctl0(SB), $-4
1067	MFC0(PERFCOUNT, 0, 1)
1068	RETURN
1069
1070TEXT	getperfctl1(SB), $-4
1071	MFC0(PERFCOUNT, 2, 1)
1072	RETURN
1073
1074	GLOBL	sanity(SB), $4
1075	DATA	sanity(SB)/4, $SANITY
1076
1077	SCHED
1078