1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * CoProcessor (SPU/AFU) mm fault handler
5 * (C) Copyright IBM Deutschland Entwicklung GmbH 2007
7 * Author: Arnd Bergmann <arndb@de.ibm.com>
8 * Author: Jeremy Kerr <jk@ozlabs.org>
10 #include <linux/sched.h>
12 #include <linux/export.h>
14 #include <asm/copro.h>
16 #include <misc/cxl-base.h>
19 * This ought to be kept in sync with the powerpc specific do_page_fault
20 * function. Currently, there are a few corner cases that we haven't had
21 * to handle fortunately.
23 int copro_handle_mm_fault(struct mm_struct
*mm
, unsigned long ea
,
24 unsigned long dsisr
, vm_fault_t
*flt
)
26 struct vm_area_struct
*vma
;
27 unsigned long is_write
;
36 down_read(&mm
->mmap_sem
);
38 vma
= find_vma(mm
, ea
);
42 if (ea
< vma
->vm_start
) {
43 if (!(vma
->vm_flags
& VM_GROWSDOWN
))
45 if (expand_stack(vma
, ea
))
49 is_write
= dsisr
& DSISR_ISSTORE
;
51 if (!(vma
->vm_flags
& VM_WRITE
))
54 if (!(vma
->vm_flags
& (VM_READ
| VM_EXEC
)))
57 * PROT_NONE is covered by the VMA check above.
58 * and hash should get a NOHPTE fault instead of
59 * a PROTFAULT in case fixup is needed for things
63 WARN_ON_ONCE(dsisr
& DSISR_PROTFAULT
);
67 *flt
= handle_mm_fault(vma
, ea
, is_write
? FAULT_FLAG_WRITE
: 0);
68 if (unlikely(*flt
& VM_FAULT_ERROR
)) {
69 if (*flt
& VM_FAULT_OOM
) {
72 } else if (*flt
& (VM_FAULT_SIGBUS
| VM_FAULT_SIGSEGV
)) {
79 if (*flt
& VM_FAULT_MAJOR
)
85 up_read(&mm
->mmap_sem
);
88 EXPORT_SYMBOL_GPL(copro_handle_mm_fault
);
90 int copro_calculate_slb(struct mm_struct
*mm
, u64 ea
, struct copro_slb
*slb
)
95 switch (get_region_id(ea
)) {
97 pr_devel("%s: 0x%llx -- USER_REGION_ID\n", __func__
, ea
);
100 psize
= get_slice_psize(mm
, ea
);
101 ssize
= user_segment_size(ea
);
102 vsid
= get_user_vsid(&mm
->context
, ea
, ssize
);
103 vsidkey
= SLB_VSID_USER
;
105 case VMALLOC_REGION_ID
:
106 pr_devel("%s: 0x%llx -- VMALLOC_REGION_ID\n", __func__
, ea
);
107 psize
= mmu_vmalloc_psize
;
108 ssize
= mmu_kernel_ssize
;
109 vsid
= get_kernel_vsid(ea
, mmu_kernel_ssize
);
110 vsidkey
= SLB_VSID_KERNEL
;
113 pr_devel("%s: 0x%llx -- IO_REGION_ID\n", __func__
, ea
);
114 psize
= mmu_io_psize
;
115 ssize
= mmu_kernel_ssize
;
116 vsid
= get_kernel_vsid(ea
, mmu_kernel_ssize
);
117 vsidkey
= SLB_VSID_KERNEL
;
119 case LINEAR_MAP_REGION_ID
:
120 pr_devel("%s: 0x%llx -- LINEAR_MAP_REGION_ID\n", __func__
, ea
);
121 psize
= mmu_linear_psize
;
122 ssize
= mmu_kernel_ssize
;
123 vsid
= get_kernel_vsid(ea
, mmu_kernel_ssize
);
124 vsidkey
= SLB_VSID_KERNEL
;
127 pr_debug("%s: invalid region access at %016llx\n", __func__
, ea
);
134 vsid
= (vsid
<< slb_vsid_shift(ssize
)) | vsidkey
;
136 vsid
|= mmu_psize_defs
[psize
].sllp
|
137 ((ssize
== MMU_SEGSIZE_1T
) ? SLB_VSID_B_1T
: 0);
139 slb
->esid
= (ea
& (ssize
== MMU_SEGSIZE_1T
? ESID_MASK_1T
: ESID_MASK
)) | SLB_ESID_V
;
144 EXPORT_SYMBOL_GPL(copro_calculate_slb
);
146 void copro_flush_all_slbs(struct mm_struct
*mm
)
148 #ifdef CONFIG_SPU_BASE
149 spu_flush_all_slbs(mm
);
153 EXPORT_SYMBOL_GPL(copro_flush_all_slbs
);