Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
/*
 * arch/mips/kernel/r4xx0.S
 *
 * Copyright (C) 1994, 1995 Waldorf Electronics
 * Written by Ralf Baechle and Andreas Busse
 *
 * This file contains most of the R4xx0 specific routines.  Due to the
 * similarities this should hopefully also be fine for the R10000.  For
 * now we especially support the R10000 by not invalidating entries out of
 * the TLB before calling the C handlers.
 *
 * This code is evil magic. Read appendix f (coprocessor 0 hazards) of
 * all R4xx0 manuals and think about that MIPS means "Microprocessor without
 * Interlocked Pipeline Stages" before you even think about changing this code!
 */
#include <linux/config.h>

#include <asm/asm.h>
#include <asm/bootinfo.h>
#include <asm/cachectl.h>
#include <asm/mipsconfig.h>
#include <asm/mipsregs.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/mipsregs.h>
#include <asm/segment.h>
#include <asm/stackframe.h>

#ifdef __SMP__
#error "Fix this for SMP"
#else
#define current current_set
#endif

MODE_ALIAS	=	0x0016			# uncachable

		.text
		.set	mips3
		.set	noreorder

		.align	5
		NESTED(handle_tlbl, FR_SIZE, sp)
		.set	noat
		/*
		 * Check whether this is a refill or an invalid exception
		 *
		 * NOTE: Some MIPS manuals say that the R4x00 sets the
		 * BadVAddr only when EXL == 0. This is wrong - BadVAddr
		 * is being set for all Reload, Invalid and Modified
		 * exceptions.
		 */
		mfc0	k0,CP0_BADVADDR
		mfc0	k1,CP0_ENTRYHI
		ori	k0,0x1fff
		xori	k0,0x1fff
		andi	k1,0xff
		or	k0,k1
		mfc0	k1,CP0_ENTRYHI
		mtc0	k0,CP0_ENTRYHI
		nop					# for R4[04]00 pipeline
		nop
		nop
		tlbp
		nop					# for R4[04]00 pipeline
		nop
		mfc0	k0,CP0_INDEX
		bgez	k0,invalid_tlbl			# bad addr in c0_badvaddr
		mtc0	k1,CP0_ENTRYHI			# delay slot
		/*
		 * Damn... The next nop is required on my R4400PC V5.0, but
		 * I don't know why - at least there is no documented
		 * reason as for the others :-(
		 */
		nop

#ifdef CONF_DEBUG_TLB
		/*
		 * OK, this is a double fault. Let's see whether this is
		 * due to an invalid entry in the page_table.
		 */
		dmfc0	k0,CP0_BADVADDR
		srl	k0,12
		sll	k0,2
		lui	k1,%HI(TLBMAP)
		addu	k0,k1
		lw	k1,(k0)
		andi	k1,(_PAGE_PRESENT|_PAGE_ACCESSED)
		bnez	k1,reload_pgd_entries
		nop					# delay slot

		.set	noat
		SAVE_ALL
		.set	at
		PRINT("Double fault caused by invalid entries in pgd:\n")
		dmfc0	a1,CP0_BADVADDR
		PRINT("Double fault address     : %08lx\n")
		dmfc0	a1,CP0_EPC
		PRINT("c0_epc                   : %08lx\n")
		jal	show_regs
		move	a0,sp
		jal	dump_tlb_all
		nop
		dmfc0	a0,CP0_BADVADDR
		jal	dump_list_current
		nop
		.set	noat
		STI
		.set	at
		PANIC("Corrupted pagedir")
		.set	noat

reload_pgd_entries:
#endif /* CONF_DEBUG_TLB */

		/*
		 * Load missing pair of entries from the pgd and return.
		 */
		dmfc0	k1,CP0_CONTEXT
		dsra	k1,1
		lwu	k0,(k1)			# Never causes nested exception
		lwu	k1,4(k1)
		dsrl	k0,6			# Convert to EntryLo format
		dsrl	k1,6			# Convert to EntryLo format
		dmtc0	k0,CP0_ENTRYLO0
		dmtc0	k1,CP0_ENTRYLO1
		nop				# for R4[04]00 pipeline
		tlbwr
		nop				# for R4[04]00 pipeline
		nop
		nop
		/*
		 * We don't know whether the original access was read or
		 * write, so return and see what happens...
		 */
		eret

		/*
		 * Handle invalid exception
		 *
		 * There are two possible causes for an invalid (tlbl)
		 * exception:
		 * 1) pages with present bit set but the valid bit clear
		 * 2) nonexistent pages
		 * Case one needs fast handling, therefore don't save
		 * registers yet.
		 *
		 * k0 contains c0_index.
		 */
invalid_tlbl:
#ifdef CONFIG_TLB_SHUTDOWN
		/*
		 * Remove entry so we don't need to care later
		 * For sake of the R4000 V2.2 pipeline the tlbwi insn
		 * has been moved down. Moving it around is juggling with
		 * explosives...
		 */
		lui	k1,0x0008
		or	k0,k1
		dsll	k0,13
		dmtc0	k0,CP0_ENTRYHI
		dmtc0	zero,CP0_ENTRYLO0
		dmtc0	zero,CP0_ENTRYLO1
#endif
		/*
		 * Test present bit in entry
		 */
		dmfc0	k0,CP0_BADVADDR
		srl	k0,12
		sll	k0,2
#ifdef CONFIG_TLB_SHUTDOWN
		tlbwi						# do not move!
#endif
		lui	k1,%HI(TLBMAP)
		addu	k0,k1
		lw	k1,(k0)
		andi	k1,(_PAGE_PRESENT|_PAGE_READ)
		xori	k1,(_PAGE_PRESENT|_PAGE_READ)
		bnez	k1,nopage_tlbl
		/*
		 * Present and read bits are set -> set valid and accessed bits
		 */
		lw	k1,(k0)					# delay slot
		ori	k1,(_PAGE_VALID|_PAGE_ACCESSED)
		sw	k1,(k0)
		eret

		/*
		 * Page doesn't exist. Lots of work which is less important
		 * for speed needs to be done, so hand it all over to the
		 * kernel memory management routines.
		 */
nopage_tlbl:	SAVE_ALL
		dmfc0	a2,CP0_BADVADDR
		STI
		.set	at
		/*
		 * a0 (struct pt_regs *) regs
		 * a1 (unsigned long)    0 for read access
		 * a2 (unsigned long)    faulting virtual address
		 */
		move	a0,sp
		jal	do_page_fault
		li	a1,0				# delay slot
		j	ret_from_sys_call
		nop					# delay slot
		END(handle_tlbl)

		.text
		.align	5
		NESTED(handle_tlbs, FR_SIZE, sp)
		.set	noat
		/*
		 * It is impossible that is a nested reload exception.
		 * Therefore this must be a invalid exception.
		 * Two possible cases:
		 * 1) Page exists but not dirty.
		 * 2) Page doesn't exist yet. Hand over to the kernel.
		 *
		 * Test whether present bit in entry is set
		 */
		dmfc0	k0,CP0_BADVADDR
		srl	k0,12
		sll	k0,2
		lui	k1,%HI(TLBMAP)
		addu	k0,k1
		lw	k1,(k0)
		tlbp					# find faulting entry
		andi	k1,(_PAGE_PRESENT|_PAGE_WRITE)
		xori	k1,(_PAGE_PRESENT|_PAGE_WRITE)
		bnez	k1,nopage_tlbs
		/*
		 * Present and writable bits set: set accessed and dirty bits.
		 */
		lw	k1,(k0)				# delay slot
		ori	k1,k1,(_PAGE_ACCESSED|_PAGE_MODIFIED| \
			       _PAGE_VALID|_PAGE_DIRTY)
		sw	k1,(k0)
		/*
		 * Now reload the entry into the TLB
		 */
		ori	k0,0x0004
		xori	k0,0x0004
		lw	k1,4(k0)
		lw	k0,(k0)
		srl	k1,6
		srl	k0,6
		dmtc0	k1,CP0_ENTRYLO1
		dmtc0	k0,CP0_ENTRYLO0
		nop				# for R4[04]00 pipeline
		tlbwi
		nop				# for R4[04]00 pipeline
		nop
		nop
		eret

		/*
		 * Page doesn't exist. Lots of work which is less important
		 * for speed needs to be done, so hand it all over to the
		 * kernel memory management routines.
		 */
nopage_tlbs:
nowrite_mod:
#ifdef CONFIG_TLB_SHUTDOWN
		/*
		 * Remove entry so we don't need to care later
		 */
		mfc0	k0,CP0_INDEX
#ifdef CONF_DEBUG_TLB
		bgez	k0,2f
		nop
		/*
		 * We got a tlbs exception but found no matching entry in
		 * the tlb.  This should never happen.  Paranoia makes us
		 * check it, though.
		 */
		SAVE_ALL
		jal	show_regs
		move	a0,sp
		.set	at
		mfc0	a1,CP0_BADVADDR
		PRINT("c0_badvaddr == %08lx\n")
		mfc0	a1,CP0_INDEX
		PRINT("c0_index    == %08x\n")
		mfc0	a1,CP0_ENTRYHI
		PRINT("c0_entryhi  == %08x\n")
		.set	noat
		STI
		.set	at
		PANIC("Tlbs or tlbm exception with no matching entry in tlb")
1:		j	1b
		nop
2:
#endif /* CONF_DEBUG_TLB */
		lui	k1,0x0008
		or	k0,k1
		dsll	k0,13
		dmtc0	k0,CP0_ENTRYHI
		dmtc0	zero,CP0_ENTRYLO0
		dmtc0	zero,CP0_ENTRYLO1
		nop				# for R4[04]00 pipeline
		nop				# R4000 V2.2 requires 4 NOPs
		nop
		nop
		tlbwi
#endif
		.set	noat
		SAVE_ALL
		dmfc0	a2,CP0_BADVADDR
		STI
		.set	at
		/*
		 * a0 (struct pt_regs *) regs
		 * a1 (unsigned long)    1 for write access
		 * a2 (unsigned long)    faulting virtual address
		 */
		move	a0,sp
		jal	do_page_fault
		li	a1,1				# delay slot
		j	ret_from_sys_call
		nop					# delay slot
		END(handle_tlbs)

		.align	5
		NESTED(handle_mod, FR_SIZE, sp)
		.set	noat
		/*
		 * Two possible cases:
		 * 1) Page is writable but not dirty -> set dirty and return
		 * 2) Page is not writable -> call C handler
		 */
		dmfc0	k0,CP0_BADVADDR
		srl	k0,12
		sll	k0,2
		lui	k1,%HI(TLBMAP)
		addu	k0,k1
		lw	k1,(k0)
		tlbp					# find faulting entry
		andi	k1,_PAGE_WRITE
		beqz	k1,nowrite_mod
		/*
		 * Present and writable bits set: set accessed and dirty bits.
		 */
		lw	k1,(k0)				# delay slot
		ori	k1,(_PAGE_ACCESSED|_PAGE_DIRTY)
		sw	k1,(k0)
		/*
		 * Now reload the entry into the tlb
		 */
		ori	k0,0x0004
		xori	k0,0x0004
		lw	k1,4(k0)
		lw	k0,(k0)
		srl	k1,6
		srl	k0,6
		dmtc0	k1,CP0_ENTRYLO1
		dmtc0	k0,CP0_ENTRYLO0
		nop				# for R4[04]00 pipeline
		nop
		nop
		tlbwi
		nop				# for R4[04]00 pipeline
		nop
		nop
		eret
		END(handle_mod)
		.set	at

/*
 * Until SAVE_ALL/RESTORE_ALL handle registers 64-bit wide we have to
 * disable interrupts here.
 */
		.set	noreorder
		LEAF(tlbflush)
		mfc0	t3,CP0_STATUS
		ori	t4,t3,1
		xori	t4,1
		mtc0	t4,CP0_STATUS
		li	t0,PM_4K
		mtc0	t0,CP0_PAGEMASK
		la	t0,boot_info
		lw	t0,OFFSET_BOOTINFO_TLB_ENTRIES(t0)
		dmtc0	zero,CP0_ENTRYLO0
		dmtc0	zero,CP0_ENTRYLO1
		mfc0	t2,CP0_WIRED
1:		subu	t0,1
		mtc0	t0,CP0_INDEX
		lui	t1,0x0008
		or	t1,t0,t1
		dsll	t1,13
		dmtc0	t1,CP0_ENTRYHI
		bne	t2,t0,1b
		tlbwi					# delay slot
		jr	ra
		mtc0	t3,CP0_STATUS			# delay slot
		END(tlbflush)

		/*
		 * Code necessary to switch tasks on an Linux/MIPS machine.
		 */
		.align	5
		LEAF(resume)
		/*
		 * Current task's task_struct
		 */
		lui	t5,%hi(current)
		lw	t0,%lo(current)(t5)

		/*
		 * Save status register
		 */
		mfc0	t1,CP0_STATUS
		addu	t0,a1				# Add tss offset
		sw	t1,TOFF_CP0_STATUS(t0)

		/*
		 * Disable interrupts
		 */
		ori	t2,t1,0x1f
		xori	t2,0x1e
		mtc0	t2,CP0_STATUS

		/*
		 * Save non-scratch registers
		 * All other registers have been saved on the kernel stack
		 */
		sw	s0,TOFF_REG16(t0)
		sw	s1,TOFF_REG17(t0)
		sw	s2,TOFF_REG18(t0)
		sw	s3,TOFF_REG19(t0)
		sw	s4,TOFF_REG20(t0)
		sw	s5,TOFF_REG21(t0)
		sw	s6,TOFF_REG22(t0)
		sw	s7,TOFF_REG23(t0)
		sw	gp,TOFF_REG28(t0)
		sw	sp,TOFF_REG29(t0)
		sw	fp,TOFF_REG30(t0)

		/*
		 * Save floating point state
		 */
		sll	t2,t1,2
		bgez	t2,2f
		sw	ra,TOFF_REG31(t0)		# delay slot
		sll	t2,t1,5
		bgez	t2,1f
		sdc1	$f0,(TOFF_FPU+0)(t0)		# delay slot
		/*
		 * Store the 16 odd double precision registers
		 */
		sdc1	$f1,(TOFF_FPU+8)(t0)
		sdc1	$f3,(TOFF_FPU+24)(t0)
		sdc1	$f5,(TOFF_FPU+40)(t0)
		sdc1	$f7,(TOFF_FPU+56)(t0)
		sdc1	$f9,(TOFF_FPU+72)(t0)
		sdc1	$f11,(TOFF_FPU+88)(t0)
		sdc1	$f13,(TOFF_FPU+104)(t0)
		sdc1	$f15,(TOFF_FPU+120)(t0)
		sdc1	$f17,(TOFF_FPU+136)(t0)
		sdc1	$f19,(TOFF_FPU+152)(t0)
		sdc1	$f21,(TOFF_FPU+168)(t0)
		sdc1	$f23,(TOFF_FPU+184)(t0)
		sdc1	$f25,(TOFF_FPU+200)(t0)
		sdc1	$f27,(TOFF_FPU+216)(t0)
		sdc1	$f29,(TOFF_FPU+232)(t0)
		sdc1	$f31,(TOFF_FPU+248)(t0)

		/*
		 * Store the 16 even double precision registers
		 */
1:		cfc1	t1,fcr31
		sdc1	$f2,(TOFF_FPU+16)(t0)
		sdc1	$f4,(TOFF_FPU+32)(t0)
		sdc1	$f6,(TOFF_FPU+48)(t0)
		sdc1	$f8,(TOFF_FPU+64)(t0)
		sdc1	$f10,(TOFF_FPU+80)(t0)
		sdc1	$f12,(TOFF_FPU+96)(t0)
		sdc1	$f14,(TOFF_FPU+112)(t0)
		sdc1	$f16,(TOFF_FPU+128)(t0)
		sdc1	$f18,(TOFF_FPU+144)(t0)
		sdc1	$f20,(TOFF_FPU+160)(t0)
		sdc1	$f22,(TOFF_FPU+176)(t0)
		sdc1	$f24,(TOFF_FPU+192)(t0)
		sdc1	$f26,(TOFF_FPU+208)(t0)
		sdc1	$f28,(TOFF_FPU+224)(t0)
		sdc1	$f30,(TOFF_FPU+240)(t0)
		sw	t1,(TOFF_FPU+256)(t0)

		/*
		 * Switch current task
		 */
2:		sw	a0,%lo(current)(t5)
		addu	a0,a1			# Add tss offset

		/*
		 * Switch address space
		 */

		/*
		 * (Choose new ASID for process)
		 * This isn't really required, but would speed up
		 * context switching.
		 */

		/*
		 * Switch the root pointer
		 */
		lw	t0,TOFF_PG_DIR(a0)
		li	t1,TLB_ROOT
		mtc0	t1,CP0_ENTRYHI
		mtc0	zero,CP0_INDEX
		srl	t0,6
		ori	t0,MODE_ALIAS
		mtc0	t0,CP0_ENTRYLO0
		mtc0	zero,CP0_ENTRYLO1
		lw	a2,TOFF_CP0_STATUS(a0)

		/*
		 * Flush tlb
		 * (probably not needed, doesn't clobber a0-a3)
		 */
		jal	tlbflush
		tlbwi 					# delay slot

		/*
		 * Restore fpu state:
		 *  - cp0 status register bits
		 *  - fp gp registers
		 *  - cp1 status/control register
		 */
		ori	t1,a2,1				# pipeline magic
		xori	t1,1
		mtc0	t1,CP0_STATUS
		sll	t0,a2,2
		bgez	t0,2f
		sll	t0,a2,5				# delay slot
		bgez	t0,1f
		ldc1	$f0,(TOFF_FPU+0)(a0)		# delay slot
		/*
		 * Restore the 16 odd double precision registers only
		 * when enabled in the cp0 status register.
		 */
		ldc1	$f1,(TOFF_FPU+8)(a0)
		ldc1	$f3,(TOFF_FPU+24)(a0)
		ldc1	$f5,(TOFF_FPU+40)(a0)
		ldc1	$f7,(TOFF_FPU+56)(a0)
		ldc1	$f9,(TOFF_FPU+72)(a0)
		ldc1	$f11,(TOFF_FPU+88)(a0)
		ldc1	$f13,(TOFF_FPU+104)(a0)
		ldc1	$f15,(TOFF_FPU+120)(a0)
		ldc1	$f17,(TOFF_FPU+136)(a0)
		ldc1	$f19,(TOFF_FPU+152)(a0)
		ldc1	$f21,(TOFF_FPU+168)(a0)
		ldc1	$f23,(TOFF_FPU+184)(a0)
		ldc1	$f25,(TOFF_FPU+200)(a0)
		ldc1	$f27,(TOFF_FPU+216)(a0)
		ldc1	$f29,(TOFF_FPU+232)(a0)
		ldc1	$f31,(TOFF_FPU+248)(a0)

		/*
		 * Restore the 16 even double precision registers
		 * when cp1 was enabled in the cp0 status register.
		 */
1:		lw	t0,(TOFF_FPU+256)(a0)
		ldc1	$f2,(TOFF_FPU+16)(a0)
		ldc1	$f4,(TOFF_FPU+32)(a0)
		ldc1	$f6,(TOFF_FPU+48)(a0)
		ldc1	$f8,(TOFF_FPU+64)(a0)
		ldc1	$f10,(TOFF_FPU+80)(a0)
		ldc1	$f12,(TOFF_FPU+96)(a0)
		ldc1	$f14,(TOFF_FPU+112)(a0)
		ldc1	$f16,(TOFF_FPU+128)(a0)
		ldc1	$f18,(TOFF_FPU+144)(a0)
		ldc1	$f20,(TOFF_FPU+160)(a0)
		ldc1	$f22,(TOFF_FPU+176)(a0)
		ldc1	$f24,(TOFF_FPU+192)(a0)
		ldc1	$f26,(TOFF_FPU+208)(a0)
		ldc1	$f28,(TOFF_FPU+224)(a0)
		ldc1	$f30,(TOFF_FPU+240)(a0)
		ctc1	t0,fcr31

		/*
		 * Restore non-scratch registers
		 */
2:		lw	s0,TOFF_REG16(a0)
		lw	s1,TOFF_REG17(a0)
		lw	s2,TOFF_REG18(a0)
		lw	s3,TOFF_REG19(a0)
		lw	s4,TOFF_REG20(a0)
		lw	s5,TOFF_REG21(a0)
		lw	s6,TOFF_REG22(a0)
		lw	s7,TOFF_REG23(a0)
		lw	gp,TOFF_REG28(a0)
		lw	sp,TOFF_REG29(a0)
		lw	fp,TOFF_REG30(a0)
		lw	ra,TOFF_REG31(a0)

		/*
		 * Restore status register
		 */
		lw	t0,TOFF_KSP(a0)
		sw	t0,kernelsp

		jr	ra
		mtc0	a2,CP0_STATUS			# delay slot
		END(resume)

		/*
		 * Load a new root pointer into the tlb
		 */
		.set	noreorder
		LEAF(load_pgd)
		/*
		 * Switch the root pointer
		 */
		mfc0	t0,CP0_STATUS
		ori	t1,t0,1
		xori	t1,1
		mtc0	t1,CP0_STATUS
		srl	a0,6
		ori	a0,MODE_ALIAS
		li	t1,TLB_ROOT
		mtc0	t1,CP0_ENTRYHI
		mtc0	zero,CP0_INDEX
		mtc0	a0,CP0_ENTRYLO0
		mtc0	zero,CP0_ENTRYLO1
		mtc0	t0,CP0_STATUS
		j	tlbflush
		tlbwi 					# delay slot
		END(load_pgd)

/*
 * Some bits in the config register
 */
#define CONFIG_DB       (1<<4)
#define CONFIG_IB       (1<<5)

/*
 * Flush instruction/data caches
 *
 * Parameters: a0 - starting address to flush
 *             a1 - size of area to be flushed
 *             a2 - which caches to be flushed
 *
 * FIXME:      - ignores parameters in a0/a1
 *             - doesn't know about second level caches
 */
		.set	noreorder
		LEAF(sys_cacheflush)
		andi	t1,a2,DCACHE
		beqz	t1,do_icache
		li	t0,KSEG0			# delay slot

		/*
		 * Writeback data cache, even lines
		 */
		li	t1,CACHELINES-1
1:		cache	Index_Writeback_Inv_D,0(t0)
		cache	Index_Writeback_Inv_D,32(t0)
		cache	Index_Writeback_Inv_D,64(t0)
		cache	Index_Writeback_Inv_D,96(t0)
		cache	Index_Writeback_Inv_D,128(t0)
		cache	Index_Writeback_Inv_D,160(t0)
		cache	Index_Writeback_Inv_D,192(t0)
		cache	Index_Writeback_Inv_D,224(t0)
		cache	Index_Writeback_Inv_D,256(t0)
		cache	Index_Writeback_Inv_D,288(t0)
		cache	Index_Writeback_Inv_D,320(t0)
		cache	Index_Writeback_Inv_D,352(t0)
		cache	Index_Writeback_Inv_D,384(t0)
		cache	Index_Writeback_Inv_D,416(t0)
		cache	Index_Writeback_Inv_D,448(t0)
		cache	Index_Writeback_Inv_D,480(t0)
		addiu	t0,512
		bnez	t1,1b
		subu	t1,1

		/*
		 * Writeback data cache, odd lines
		 * Only needed for 16 byte line size
		 */
		mfc0	t1,CP0_CONFIG
		andi	t1,CONFIG_DB
		bnez	t1,do_icache
		li	t1,CACHELINES-1
1:		cache	Index_Writeback_Inv_D,16(t0)
		cache	Index_Writeback_Inv_D,48(t0)
		cache	Index_Writeback_Inv_D,80(t0)
		cache	Index_Writeback_Inv_D,112(t0)
		cache	Index_Writeback_Inv_D,144(t0)
		cache	Index_Writeback_Inv_D,176(t0)
		cache	Index_Writeback_Inv_D,208(t0)
		cache	Index_Writeback_Inv_D,240(t0)
		cache	Index_Writeback_Inv_D,272(t0)
		cache	Index_Writeback_Inv_D,304(t0)
		cache	Index_Writeback_Inv_D,336(t0)
		cache	Index_Writeback_Inv_D,368(t0)
		cache	Index_Writeback_Inv_D,400(t0)
		cache	Index_Writeback_Inv_D,432(t0)
		cache	Index_Writeback_Inv_D,464(t0)
		cache	Index_Writeback_Inv_D,496(t0)
		addiu	t0,512
		bnez	t1,1b
		subu	t1,1

do_icache:	andi	t1,a2,ICACHE
		beqz	t1,done

		/*
		 * Flush instruction cache, even lines
		 */
		lui	t0,0x8000
		li	t1,CACHELINES-1
1: 		cache	Index_Invalidate_I,0(t0)	
		cache	Index_Invalidate_I,32(t0)
		cache	Index_Invalidate_I,64(t0)
		cache	Index_Invalidate_I,96(t0)
		cache	Index_Invalidate_I,128(t0)
		cache	Index_Invalidate_I,160(t0)
		cache	Index_Invalidate_I,192(t0)
		cache	Index_Invalidate_I,224(t0)
		cache	Index_Invalidate_I,256(t0)
		cache	Index_Invalidate_I,288(t0)
		cache	Index_Invalidate_I,320(t0)
		cache	Index_Invalidate_I,352(t0)
		cache	Index_Invalidate_I,384(t0)
		cache	Index_Invalidate_I,416(t0)
		cache	Index_Invalidate_I,448(t0)
		cache	Index_Invalidate_I,480(t0)
		addiu	t0,512
		bnez	t1,1b
		subu	t1,1

		/*
		 * Flush instruction cache, even lines
		 * Only needed for 16 byte line size
		 */
		mfc0	t1,CP0_CONFIG
		andi	t1,CONFIG_IB
		bnez	t1,done
		li	t1,CACHELINES-1
1:		cache	Index_Invalidate_I,16(t0)
		cache	Index_Invalidate_I,48(t0)
		cache	Index_Invalidate_I,80(t0)
		cache	Index_Invalidate_I,112(t0)
		cache	Index_Invalidate_I,144(t0)
		cache	Index_Invalidate_I,176(t0)
		cache	Index_Invalidate_I,208(t0)
		cache	Index_Invalidate_I,240(t0)
		cache	Index_Invalidate_I,272(t0)
		cache	Index_Invalidate_I,304(t0)
		cache	Index_Invalidate_I,336(t0)
		cache	Index_Invalidate_I,368(t0)
		cache	Index_Invalidate_I,400(t0)
		cache	Index_Invalidate_I,432(t0)
		cache	Index_Invalidate_I,464(t0)
		cache	Index_Invalidate_I,496(t0)
		addiu	t0,512
		bnez	t1,1b
		subu	t1,1

done:		j	ra
		nop
		END(sys_cacheflush)

/*
 * Update the TLB - or how instruction scheduling makes code unreadable ...
 *
 * MIPS doesn't need any external MMU info: the kernel page tables contain
 * all the necessary information.  We use this hook though to load the
 * TLB as early as possible with uptodate information avoiding unnecessary
 * exceptions.
 *
 * Parameters: a0 - struct vm_area_struct *vma	(ignored)
 *             a1 - unsigned long address
 *             a2 - pte_t pte
 */
		.set	noreorder
		LEAF(update_mmu_cache)
		/*
		 * Step 1: Wipe out old TLB information.  Not sure if
		 * we really need that step; call it paranoia ...
		 * In order to do that we need to disable interrupts.
		 */
		mfc0	t0,CP0_STATUS		# interrupts off
		ori	t1,t0,1
		xori	t1,1
		mtc0	t1,CP0_STATUS
		li	t3,TLBMAP		# then wait 3 cycles
		ori	t1,a1,0xfff		# mask off low 12 bits
		xori	t1,0xfff
		mfc0	t2,CP0_ENTRYHI		# copy ASID into address
		andi	t2,0xff
		or	t2,t1
		mtc0	t2,CP0_ENTRYHI
		srl	t4,a1,12		# wait again three cycles
		sll	t4,t4,PTRLOG
		dmtc0	zero,CP0_ENTRYLO0
		tlbp				# now query the TLB
		addu	t3,t4			# wait another three cycles
		ori	t3,0xffff
		xori	t3,0xffff
		mfc0	t1,CP0_INDEX
		bltz	t1,1f			# No old entry?
		dmtc0	zero,CP0_ENTRYLO1
		or	t3,t1			# wait one cycle
		tlbwi
		/*
		 * But there still might be a entry for the pgd ...
		 */
1:		mtc0	t3,CP0_ENTRYHI
		nop				# wait 3 cycles
		nop
		nop
		tlbp				# TLB lookup
		nop
		nop
		mfc0	t1,CP0_INDEX		# wait 3 cycles
		bltz	t1,1f			# No old entry?
		nop		
		tlbwi				# gotcha ...
		/*
		 * Step 2: Reload the TLB with new information.  We can skip
		 * this but this should speed the mess a bit by avoiding
		 * tlbl/tlbs exceptions. (To be done)
		 */
1:		jr	ra
		mtc0	t0,CP0_STATUS		# delay slot
		END(update_mmu_cache)