-
Notifications
You must be signed in to change notification settings - Fork 102
/
entry.S
2319 lines (1903 loc) · 53.4 KB
/
entry.S
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
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Linux/PA-RISC Project (http://www.parisc-linux.org/)
*
* kernel entry points (interruptions, system call wrappers)
* Copyright (C) 1999,2000 Philipp Rumpf
* Copyright (C) 1999 SuSE GmbH Nuernberg
* Copyright (C) 2000 Hewlett-Packard (John Marvin)
* Copyright (C) 1999 Hewlett-Packard (Frank Rowand)
*/
#include <asm/asm-offsets.h>
/* we have the following possibilities to act on an interruption:
* - handle in assembly and use shadowed registers only
* - save registers to kernel stack and handle in assembly or C */
#include <asm/psw.h>
#include <asm/cache.h> /* for L1_CACHE_SHIFT */
#include <asm/assembly.h> /* for LDREG/STREG defines */
#include <asm/signal.h>
#include <asm/unistd.h>
#include <asm/ldcw.h>
#include <asm/traps.h>
#include <asm/thread_info.h>
#include <asm/alternative.h>
#include <asm/spinlock_types.h>
#include <linux/linkage.h>
#include <linux/pgtable.h>
#ifdef CONFIG_64BIT
.level 2.0w
#else
.level 2.0
#endif
/* Get aligned page_table_lock address for this mm from cr28/tr4 */
.macro get_ptl reg
mfctl %cr28,\reg
.endm
/* space_to_prot macro creates a prot id from a space id */
#if (SPACEID_SHIFT) == 0
.macro space_to_prot spc prot
depd,z \spc,62,31,\prot
.endm
#else
.macro space_to_prot spc prot
extrd,u \spc,(64 - (SPACEID_SHIFT)),32,\prot
.endm
#endif
/*
* The "get_stack" macros are responsible for determining the
* kernel stack value.
*
* If sr7 == 0
* Already using a kernel stack, so call the
* get_stack_use_r30 macro to push a pt_regs structure
* on the stack, and store registers there.
* else
* Need to set up a kernel stack, so call the
* get_stack_use_cr30 macro to set up a pointer
* to the pt_regs structure contained within the
* task pointer pointed to by cr30. Load the stack
* pointer from the task structure.
*
* Note that we use shadowed registers for temps until
* we can save %r26 and %r29. %r26 is used to preserve
* %r8 (a shadowed register) which temporarily contained
* either the fault type ("code") or the eirr. We need
* to use a non-shadowed register to carry the value over
* the rfir in virt_map. We use %r26 since this value winds
* up being passed as the argument to either do_cpu_irq_mask
* or handle_interruption. %r29 is used to hold a pointer
* the register save area, and once again, it needs to
* be a non-shadowed register so that it survives the rfir.
*/
.macro get_stack_use_cr30
/* we save the registers in the task struct */
copy %r30, %r17
mfctl %cr30, %r1
tophys %r1,%r9 /* task_struct */
LDREG TASK_STACK(%r9),%r30
ldo PT_SZ_ALGN(%r30),%r30
mtsp %r0,%sr7 /* clear sr7 after kernel stack was set! */
mtsp %r16,%sr3
ldo TASK_REGS(%r9),%r9
STREG %r17,PT_GR30(%r9)
STREG %r29,PT_GR29(%r9)
STREG %r26,PT_GR26(%r9)
STREG %r16,PT_SR7(%r9)
copy %r9,%r29
.endm
.macro get_stack_use_r30
/* we put a struct pt_regs on the stack and save the registers there */
tophys %r30,%r9
copy %r30,%r1
ldo PT_SZ_ALGN(%r30),%r30
STREG %r1,PT_GR30(%r9)
STREG %r29,PT_GR29(%r9)
STREG %r26,PT_GR26(%r9)
STREG %r16,PT_SR7(%r9)
copy %r9,%r29
.endm
.macro rest_stack
LDREG PT_GR1(%r29), %r1
LDREG PT_GR30(%r29),%r30
LDREG PT_GR29(%r29),%r29
.endm
/* default interruption handler
* (calls traps.c:handle_interruption) */
.macro def code
b intr_save
ldi \code, %r8
.align 32
.endm
/* Interrupt interruption handler
* (calls irq.c:do_cpu_irq_mask) */
.macro extint code
b intr_extint
mfsp %sr7,%r16
.align 32
.endm
.import os_hpmc, code
/* HPMC handler */
.macro hpmc code
nop /* must be a NOP, will be patched later */
load32 PA(os_hpmc), %r3
bv,n 0(%r3)
nop
.word 0 /* checksum (will be patched) */
.word 0 /* address of handler */
.word 0 /* length of handler */
.endm
/*
* Performance Note: Instructions will be moved up into
* this part of the code later on, once we are sure
* that the tlb miss handlers are close to final form.
*/
/* Register definitions for tlb miss handler macros */
va = r8 /* virtual address for which the trap occurred */
spc = r24 /* space for which the trap occurred */
#ifndef CONFIG_64BIT
/*
* itlb miss interruption handler (parisc 1.1 - 32 bit)
*/
.macro itlb_11 code
mfctl %pcsq, spc
b itlb_miss_11
mfctl %pcoq, va
.align 32
.endm
#endif
/*
* itlb miss interruption handler (parisc 2.0)
*/
.macro itlb_20 code
mfctl %pcsq, spc
#ifdef CONFIG_64BIT
b itlb_miss_20w
#else
b itlb_miss_20
#endif
mfctl %pcoq, va
.align 32
.endm
#ifndef CONFIG_64BIT
/*
* naitlb miss interruption handler (parisc 1.1 - 32 bit)
*/
.macro naitlb_11 code
mfctl %isr,spc
b naitlb_miss_11
mfctl %ior,va
.align 32
.endm
#endif
/*
* naitlb miss interruption handler (parisc 2.0)
*/
.macro naitlb_20 code
mfctl %isr,spc
#ifdef CONFIG_64BIT
b naitlb_miss_20w
#else
b naitlb_miss_20
#endif
mfctl %ior,va
.align 32
.endm
#ifndef CONFIG_64BIT
/*
* dtlb miss interruption handler (parisc 1.1 - 32 bit)
*/
.macro dtlb_11 code
mfctl %isr, spc
b dtlb_miss_11
mfctl %ior, va
.align 32
.endm
#endif
/*
* dtlb miss interruption handler (parisc 2.0)
*/
.macro dtlb_20 code
mfctl %isr, spc
#ifdef CONFIG_64BIT
b dtlb_miss_20w
#else
b dtlb_miss_20
#endif
mfctl %ior, va
.align 32
.endm
#ifndef CONFIG_64BIT
/* nadtlb miss interruption handler (parisc 1.1 - 32 bit) */
.macro nadtlb_11 code
mfctl %isr,spc
b nadtlb_miss_11
mfctl %ior,va
.align 32
.endm
#endif
/* nadtlb miss interruption handler (parisc 2.0) */
.macro nadtlb_20 code
mfctl %isr,spc
#ifdef CONFIG_64BIT
b nadtlb_miss_20w
#else
b nadtlb_miss_20
#endif
mfctl %ior,va
.align 32
.endm
#ifndef CONFIG_64BIT
/*
* dirty bit trap interruption handler (parisc 1.1 - 32 bit)
*/
.macro dbit_11 code
mfctl %isr,spc
b dbit_trap_11
mfctl %ior,va
.align 32
.endm
#endif
/*
* dirty bit trap interruption handler (parisc 2.0)
*/
.macro dbit_20 code
mfctl %isr,spc
#ifdef CONFIG_64BIT
b dbit_trap_20w
#else
b dbit_trap_20
#endif
mfctl %ior,va
.align 32
.endm
/* In LP64, the space contains part of the upper 32 bits of the
* fault. We have to extract this and place it in the va,
* zeroing the corresponding bits in the space register */
.macro space_adjust spc,va,tmp
#ifdef CONFIG_64BIT
extrd,u \spc,63,SPACEID_SHIFT,\tmp
depd %r0,63,SPACEID_SHIFT,\spc
depd \tmp,31,SPACEID_SHIFT,\va
#endif
.endm
.import swapper_pg_dir,code
/* Get the pgd. For faults on space zero (kernel space), this
* is simply swapper_pg_dir. For user space faults, the
* pgd is stored in %cr25 */
.macro get_pgd spc,reg
ldil L%PA(swapper_pg_dir),\reg
ldo R%PA(swapper_pg_dir)(\reg),\reg
or,COND(=) %r0,\spc,%r0
mfctl %cr25,\reg
.endm
/*
space_check(spc,tmp,fault)
spc - The space we saw the fault with.
tmp - The place to store the current space.
fault - Function to call on failure.
Only allow faults on different spaces from the
currently active one if we're the kernel
*/
.macro space_check spc,tmp,fault
mfsp %sr7,\tmp
/* check against %r0 which is same value as LINUX_GATEWAY_SPACE */
or,COND(<>) %r0,\spc,%r0 /* user may execute gateway page
* as kernel, so defeat the space
* check if it is */
copy \spc,\tmp
or,COND(=) %r0,\tmp,%r0 /* nullify if executing as kernel */
cmpb,COND(<>),n \tmp,\spc,\fault
.endm
/* Look up a PTE in a 2-Level scheme (faulting at each
* level if the entry isn't present
*
* NOTE: we use ldw even for LP64, since the short pointers
* can address up to 1TB
*/
.macro L2_ptep pmd,pte,index,va,fault
#if CONFIG_PGTABLE_LEVELS == 3
extru_safe \va,31-ASM_PMD_SHIFT,ASM_BITS_PER_PMD,\index
#else
extru_safe \va,31-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index
#endif
dep %r0,31,PAGE_SHIFT,\pmd /* clear offset */
#if CONFIG_PGTABLE_LEVELS < 3
copy %r0,\pte
#endif
ldw,s \index(\pmd),\pmd
bb,>=,n \pmd,_PxD_PRESENT_BIT,\fault
dep %r0,31,PxD_FLAG_SHIFT,\pmd /* clear flags */
SHLREG \pmd,PxD_VALUE_SHIFT,\pmd
extru_safe \va,31-PAGE_SHIFT,ASM_BITS_PER_PTE,\index
dep %r0,31,PAGE_SHIFT,\pmd /* clear offset */
shladd \index,BITS_PER_PTE_ENTRY,\pmd,\pmd /* pmd is now pte */
.endm
/* Look up PTE in a 3-Level scheme. */
.macro L3_ptep pgd,pte,index,va,fault
#if CONFIG_PGTABLE_LEVELS == 3
copy %r0,\pte
extrd,u \va,63-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index
ldw,s \index(\pgd),\pgd
bb,>=,n \pgd,_PxD_PRESENT_BIT,\fault
shld \pgd,PxD_VALUE_SHIFT,\pgd
#endif
L2_ptep \pgd,\pte,\index,\va,\fault
.endm
/* Acquire page_table_lock and check page is present. */
.macro ptl_lock spc,ptp,pte,tmp,tmp1,fault
#ifdef CONFIG_TLB_PTLOCK
98: cmpib,COND(=),n 0,\spc,2f
get_ptl \tmp
1: LDCW 0(\tmp),\tmp1
cmpib,COND(=) 0,\tmp1,1b
nop
LDREG 0(\ptp),\pte
bb,<,n \pte,_PAGE_PRESENT_BIT,3f
b \fault
stw \tmp1,0(\tmp)
99: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP)
#endif
2: LDREG 0(\ptp),\pte
bb,>=,n \pte,_PAGE_PRESENT_BIT,\fault
3:
.endm
/* Release page_table_lock without reloading lock address.
We use an ordered store to ensure all prior accesses are
performed prior to releasing the lock. */
.macro ptl_unlock0 spc,tmp,tmp2
#ifdef CONFIG_TLB_PTLOCK
98: ldi __ARCH_SPIN_LOCK_UNLOCKED_VAL, \tmp2
or,COND(=) %r0,\spc,%r0
stw,ma \tmp2,0(\tmp)
99: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP)
#endif
.endm
/* Release page_table_lock. */
.macro ptl_unlock1 spc,tmp,tmp2
#ifdef CONFIG_TLB_PTLOCK
98: get_ptl \tmp
ptl_unlock0 \spc,\tmp,\tmp2
99: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP)
#endif
.endm
/* Set the _PAGE_ACCESSED bit of the PTE. Be clever and
* don't needlessly dirty the cache line if it was already set */
.macro update_accessed ptp,pte,tmp,tmp1
ldi _PAGE_ACCESSED,\tmp1
or \tmp1,\pte,\tmp
and,COND(<>) \tmp1,\pte,%r0
STREG \tmp,0(\ptp)
.endm
/* Set the dirty bit (and accessed bit). No need to be
* clever, this is only used from the dirty fault */
.macro update_dirty ptp,pte,tmp
ldi _PAGE_ACCESSED|_PAGE_DIRTY,\tmp
or \tmp,\pte,\pte
STREG \pte,0(\ptp)
.endm
/* We have (depending on the page size):
* - 38 to 52-bit Physical Page Number
* - 12 to 26-bit page offset
*/
/* bitshift difference between a PFN (based on kernel's PAGE_SIZE)
* to a CPU TLB 4k PFN (4k => 12 bits to shift) */
#define PAGE_ADD_SHIFT (PAGE_SHIFT-12)
#define PAGE_ADD_HUGE_SHIFT (REAL_HPAGE_SHIFT-12)
/* Drop prot bits and convert to page addr for iitlbt and idtlbt */
.macro convert_for_tlb_insert20 pte,tmp
#ifdef CONFIG_HUGETLB_PAGE
copy \pte,\tmp
extrd,u \tmp,(63-ASM_PFN_PTE_SHIFT)+(63-58)+PAGE_ADD_SHIFT,\
64-PAGE_SHIFT-PAGE_ADD_SHIFT,\pte
depdi _PAGE_SIZE_ENCODING_DEFAULT,63,\
(63-58)+PAGE_ADD_SHIFT,\pte
extrd,u,*= \tmp,_PAGE_HPAGE_BIT+32,1,%r0
depdi _HUGE_PAGE_SIZE_ENCODING_DEFAULT,63,\
(63-58)+PAGE_ADD_HUGE_SHIFT,\pte
#else /* Huge pages disabled */
extrd,u \pte,(63-ASM_PFN_PTE_SHIFT)+(63-58)+PAGE_ADD_SHIFT,\
64-PAGE_SHIFT-PAGE_ADD_SHIFT,\pte
depdi _PAGE_SIZE_ENCODING_DEFAULT,63,\
(63-58)+PAGE_ADD_SHIFT,\pte
#endif
.endm
/* Convert the pte and prot to tlb insertion values. How
* this happens is quite subtle, read below */
.macro make_insert_tlb spc,pte,prot,tmp
space_to_prot \spc \prot /* create prot id from space */
/* The following is the real subtlety. This is depositing
* T <-> _PAGE_REFTRAP
* D <-> _PAGE_DIRTY
* B <-> _PAGE_DMB (memory break)
*
* Then incredible subtlety: The access rights are
* _PAGE_GATEWAY, _PAGE_EXEC and _PAGE_WRITE
* See 3-14 of the parisc 2.0 manual
*
* Finally, _PAGE_READ goes in the top bit of PL1 (so we
* trigger an access rights trap in user space if the user
* tries to read an unreadable page */
#if _PAGE_SPECIAL_BIT == _PAGE_DMB_BIT
/* need to drop DMB bit, as it's used as SPECIAL flag */
depi 0,_PAGE_SPECIAL_BIT,1,\pte
#endif
depd \pte,8,7,\prot
/* PAGE_USER indicates the page can be read with user privileges,
* so deposit X1|11 to PL1|PL2 (remember the upper bit of PL1
* contains _PAGE_READ) */
extrd,u,*= \pte,_PAGE_USER_BIT+32,1,%r0
depdi 7,11,3,\prot
/* If we're a gateway page, drop PL2 back to zero for promotion
* to kernel privilege (so we can execute the page as kernel).
* Any privilege promotion page always denys read and write */
extrd,u,*= \pte,_PAGE_GATEWAY_BIT+32,1,%r0
depd %r0,11,2,\prot /* If Gateway, Set PL2 to 0 */
/* Enforce uncacheable pages.
* This should ONLY be use for MMIO on PA 2.0 machines.
* Memory/DMA is cache coherent on all PA2.0 machines we support
* (that means T-class is NOT supported) and the memory controllers
* on most of those machines only handles cache transactions.
*/
extrd,u,*= \pte,_PAGE_NO_CACHE_BIT+32,1,%r0
depdi 1,12,1,\prot
/* Drop prot bits and convert to page addr for iitlbt and idtlbt */
convert_for_tlb_insert20 \pte \tmp
.endm
/* Identical macro to make_insert_tlb above, except it
* makes the tlb entry for the differently formatted pa11
* insertion instructions */
.macro make_insert_tlb_11 spc,pte,prot
#if _PAGE_SPECIAL_BIT == _PAGE_DMB_BIT
/* need to drop DMB bit, as it's used as SPECIAL flag */
depi 0,_PAGE_SPECIAL_BIT,1,\pte
#endif
zdep \spc,30,15,\prot
dep \pte,8,7,\prot
extru,= \pte,_PAGE_NO_CACHE_BIT,1,%r0
depi 1,12,1,\prot
extru,= \pte,_PAGE_USER_BIT,1,%r0
depi 7,11,3,\prot /* Set for user space (1 rsvd for read) */
extru,= \pte,_PAGE_GATEWAY_BIT,1,%r0
depi 0,11,2,\prot /* If Gateway, Set PL2 to 0 */
/* Get rid of prot bits and convert to page addr for iitlba */
depi 0,31,ASM_PFN_PTE_SHIFT,\pte
SHRREG \pte,(ASM_PFN_PTE_SHIFT-(31-26)),\pte
.endm
/* This is for ILP32 PA2.0 only. The TLB insertion needs
* to extend into I/O space if the address is 0xfXXXXXXX
* so we extend the f's into the top word of the pte in
* this case */
.macro f_extend pte,tmp
extrd,s \pte,42,4,\tmp
addi,<> 1,\tmp,%r0
extrd,s \pte,63,25,\pte
.endm
/* The alias region is comprised of a pair of 4 MB regions
* aligned to 8 MB. It is used to clear/copy/flush user pages
* using kernel virtual addresses congruent with the user
* virtual address.
*
* To use the alias page, you set %r26 up with the to TLB
* entry (identifying the physical page) and %r23 up with
* the from tlb entry (or nothing if only a to entry---for
* clear_user_page_asm) */
.macro do_alias spc,tmp,tmp1,va,pte,prot,fault,patype
cmpib,COND(<>),n 0,\spc,\fault
ldil L%(TMPALIAS_MAP_START),\tmp
copy \va,\tmp1
depi_safe 0,31,TMPALIAS_SIZE_BITS+1,\tmp1
cmpb,COND(<>),n \tmp,\tmp1,\fault
mfctl %cr19,\tmp /* iir */
/* get the opcode (first six bits) into \tmp */
extrw,u \tmp,5,6,\tmp
/*
* Only setting the T bit prevents data cache movein
* Setting access rights to zero prevents instruction cache movein
*
* Note subtlety here: _PAGE_GATEWAY, _PAGE_EXEC and _PAGE_WRITE go
* to type field and _PAGE_READ goes to top bit of PL1
*/
ldi (_PAGE_REFTRAP|_PAGE_READ|_PAGE_WRITE),\prot
/*
* so if the opcode is one (i.e. this is a memory management
* instruction) nullify the next load so \prot is only T.
* Otherwise this is a normal data operation
*/
cmpiclr,= 0x01,\tmp,%r0
ldi (_PAGE_DIRTY|_PAGE_READ|_PAGE_WRITE),\prot
.ifc \patype,20
depd,z \prot,8,7,\prot
.else
.ifc \patype,11
depw,z \prot,8,7,\prot
.else
.error "undefined PA type to do_alias"
.endif
.endif
/*
* OK, it is in the temp alias region, check whether "from" or "to".
* Check "subtle" note in pacache.S re: r23/r26.
*/
extrw,u,= \va,31-TMPALIAS_SIZE_BITS,1,%r0
or,COND(tr) %r23,%r0,\pte
or %r26,%r0,\pte
/* convert phys addr in \pte (from r23 or r26) to tlb insert format */
SHRREG \pte,PAGE_SHIFT+PAGE_ADD_SHIFT-5, \pte
depi_safe _PAGE_SIZE_ENCODING_DEFAULT, 31,5, \pte
.endm
/*
* Fault_vectors are architecturally required to be aligned on a 2K
* boundary
*/
.section .text.hot
.align 2048
ENTRY(fault_vector_20)
/* First vector is invalid (0) */
.ascii "cows can fly"
.byte 0
.align 32
hpmc 1
def 2
def 3
extint 4
def 5
itlb_20 PARISC_ITLB_TRAP
def 7
def 8
def 9
def 10
def 11
def 12
def 13
def 14
dtlb_20 15
naitlb_20 16
nadtlb_20 17
def 18
def 19
dbit_20 20
def 21
def 22
def 23
def 24
def 25
def 26
def 27
def 28
def 29
def 30
def 31
END(fault_vector_20)
#ifndef CONFIG_64BIT
.align 2048
ENTRY(fault_vector_11)
/* First vector is invalid (0) */
.ascii "cows can fly"
.byte 0
.align 32
hpmc 1
def 2
def 3
extint 4
def 5
itlb_11 PARISC_ITLB_TRAP
def 7
def 8
def 9
def 10
def 11
def 12
def 13
def 14
dtlb_11 15
naitlb_11 16
nadtlb_11 17
def 18
def 19
dbit_11 20
def 21
def 22
def 23
def 24
def 25
def 26
def 27
def 28
def 29
def 30
def 31
END(fault_vector_11)
#endif
/* Fault vector is separately protected and *must* be on its own page */
.align PAGE_SIZE
.import handle_interruption,code
.import do_cpu_irq_mask,code
/*
* Child Returns here
*
* copy_thread moved args into task save area.
*/
ENTRY(ret_from_kernel_thread)
/* Call schedule_tail first though */
BL schedule_tail, %r2
nop
mfctl %cr30,%r1 /* task_struct */
LDREG TASK_PT_GR25(%r1), %r26
#ifdef CONFIG_64BIT
LDREG TASK_PT_GR27(%r1), %r27
#endif
LDREG TASK_PT_GR26(%r1), %r1
ble 0(%sr7, %r1)
copy %r31, %r2
b finish_child_return
nop
END(ret_from_kernel_thread)
/*
* struct task_struct *_switch_to(struct task_struct *prev,
* struct task_struct *next)
*
* switch kernel stacks and return prev */
ENTRY_CFI(_switch_to)
STREG %r2, -RP_OFFSET(%r30)
callee_save_float
callee_save
load32 _switch_to_ret, %r2
STREG %r2, TASK_PT_KPC(%r26)
LDREG TASK_PT_KPC(%r25), %r2
STREG %r30, TASK_PT_KSP(%r26)
LDREG TASK_PT_KSP(%r25), %r30
bv %r0(%r2)
mtctl %r25,%cr30
ENTRY(_switch_to_ret)
mtctl %r0, %cr0 /* Needed for single stepping */
callee_rest
callee_rest_float
LDREG -RP_OFFSET(%r30), %r2
bv %r0(%r2)
copy %r26, %r28
ENDPROC_CFI(_switch_to)
/*
* Common rfi return path for interruptions, kernel execve, and
* sys_rt_sigreturn (sometimes). The sys_rt_sigreturn syscall will
* return via this path if the signal was received when the process
* was running; if the process was blocked on a syscall then the
* normal syscall_exit path is used. All syscalls for traced
* proceses exit via intr_restore.
*
* XXX If any syscalls that change a processes space id ever exit
* this way, then we will need to copy %sr3 in to PT_SR[3..7], and
* adjust IASQ[0..1].
*
*/
.align PAGE_SIZE
ENTRY_CFI(syscall_exit_rfi)
mfctl %cr30,%r16 /* task_struct */
ldo TASK_REGS(%r16),%r16
/* Force iaoq to userspace, as the user has had access to our current
* context via sigcontext. Also Filter the PSW for the same reason.
*/
LDREG PT_IAOQ0(%r16),%r19
depi PRIV_USER,31,2,%r19
STREG %r19,PT_IAOQ0(%r16)
LDREG PT_IAOQ1(%r16),%r19
depi PRIV_USER,31,2,%r19
STREG %r19,PT_IAOQ1(%r16)
LDREG PT_PSW(%r16),%r19
load32 USER_PSW_MASK,%r1
#ifdef CONFIG_64BIT
load32 USER_PSW_HI_MASK,%r20
depd %r20,31,32,%r1
#endif
and %r19,%r1,%r19 /* Mask out bits that user shouldn't play with */
load32 USER_PSW,%r1
or %r19,%r1,%r19 /* Make sure default USER_PSW bits are set */
STREG %r19,PT_PSW(%r16)
/*
* If we aren't being traced, we never saved space registers
* (we don't store them in the sigcontext), so set them
* to "proper" values now (otherwise we'll wind up restoring
* whatever was last stored in the task structure, which might
* be inconsistent if an interrupt occurred while on the gateway
* page). Note that we may be "trashing" values the user put in
* them, but we don't support the user changing them.
*/
STREG %r0,PT_SR2(%r16)
mfsp %sr3,%r19
STREG %r19,PT_SR0(%r16)
STREG %r19,PT_SR1(%r16)
STREG %r19,PT_SR3(%r16)
STREG %r19,PT_SR4(%r16)
STREG %r19,PT_SR5(%r16)
STREG %r19,PT_SR6(%r16)
STREG %r19,PT_SR7(%r16)
ENTRY(intr_return)
/* check for reschedule */
mfctl %cr30,%r1
LDREG TASK_TI_FLAGS(%r1),%r19 /* sched.h: TIF_NEED_RESCHED */
bb,<,n %r19,31-TIF_NEED_RESCHED,intr_do_resched /* forward */
.import do_notify_resume,code
intr_check_sig:
/* As above */
mfctl %cr30,%r1
LDREG TASK_TI_FLAGS(%r1),%r19
ldi (_TIF_USER_WORK_MASK & ~_TIF_NEED_RESCHED), %r20
and,COND(<>) %r19, %r20, %r0
b,n intr_restore /* skip past if we've nothing to do */
/* This check is critical to having LWS
* working. The IASQ is zero on the gateway
* page and we cannot deliver any signals until
* we get off the gateway page.
*
* Only do signals if we are returning to user space
*/
LDREG PT_IASQ0(%r16), %r20
cmpib,COND(=),n LINUX_GATEWAY_SPACE, %r20, intr_restore /* forward */
LDREG PT_IASQ1(%r16), %r20
cmpib,COND(=),n LINUX_GATEWAY_SPACE, %r20, intr_restore /* forward */
copy %r0, %r25 /* long in_syscall = 0 */
#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
#endif
/* NOTE: We need to enable interrupts if we have to deliver
* signals. We used to do this earlier but it caused kernel
* stack overflows. */
ssm PSW_SM_I, %r0
BL do_notify_resume,%r2
copy %r16, %r26 /* struct pt_regs *regs */
b,n intr_check_sig
intr_restore:
copy %r16,%r29
ldo PT_FR31(%r29),%r1
rest_fp %r1
rest_general %r29
/* inverse of virt_map */
pcxt_ssm_bug
rsm PSW_SM_QUIET,%r0 /* prepare for rfi */
tophys_r1 %r29
/* Restore space id's and special cr's from PT_REGS
* structure pointed to by r29
*/
rest_specials %r29
/* IMPORTANT: rest_stack restores r29 last (we are using it)!
* It also restores r1 and r30.
*/
rest_stack
rfi
nop
#ifndef CONFIG_PREEMPTION
# define intr_do_preempt intr_restore
#endif /* !CONFIG_PREEMPTION */
.import schedule,code
intr_do_resched:
/* Only call schedule on return to userspace. If we're returning
* to kernel space, we may schedule if CONFIG_PREEMPTION, otherwise
* we jump back to intr_restore.
*/
LDREG PT_IASQ0(%r16), %r20
cmpib,COND(=) 0, %r20, intr_do_preempt
nop
LDREG PT_IASQ1(%r16), %r20
cmpib,COND(=) 0, %r20, intr_do_preempt
nop
/* NOTE: We need to enable interrupts if we schedule. We used
* to do this earlier but it caused kernel stack overflows. */
ssm PSW_SM_I, %r0
#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
#endif
ldil L%intr_check_sig, %r2
#ifndef CONFIG_64BIT
b schedule
#else
load32 schedule, %r20
bv %r0(%r20)
#endif
ldo R%intr_check_sig(%r2), %r2
/* preempt the current task on returning to kernel
* mode from an interrupt, iff need_resched is set,
* and preempt_count is 0. otherwise, we continue on
* our merry way back to the current running task.
*/
#ifdef CONFIG_PREEMPTION
.import preempt_schedule_irq,code
intr_do_preempt:
rsm PSW_SM_I, %r0 /* disable interrupts */
/* current_thread_info()->preempt_count */
mfctl %cr30, %r1
ldw TI_PRE_COUNT(%r1), %r19
cmpib,<> 0, %r19, intr_restore /* if preempt_count > 0 */
nop /* prev insn branched backwards */
/* check if we interrupted a critical path */
LDREG PT_PSW(%r16), %r20
bb,<,n %r20, 31 - PSW_SM_I, intr_restore
nop
/* ssm PSW_SM_I done later in intr_restore */
#ifdef CONFIG_MLONGCALLS
ldil L%intr_restore, %r2
load32 preempt_schedule_irq, %r1
bv %r0(%r1)
ldo R%intr_restore(%r2), %r2
#else
ldil L%intr_restore, %r1
BL preempt_schedule_irq, %r2
ldo R%intr_restore(%r1), %r2
#endif
#endif /* CONFIG_PREEMPTION */
/*
* External interrupts.
*/
intr_extint:
cmpib,COND(=),n 0,%r16,1f
get_stack_use_cr30
b,n 2f
1:
get_stack_use_r30
2:
save_specials %r29
virt_map
save_general %r29
ldo PT_FR0(%r29), %r24
save_fp %r24
loadgp
copy %r29, %r26 /* arg0 is pt_regs */
copy %r29, %r16 /* save pt_regs */
ldil L%intr_return, %r2
#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
#endif
b do_cpu_irq_mask
ldo R%intr_return(%r2), %r2 /* return to intr_return, not here */
ENDPROC_CFI(syscall_exit_rfi)