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
;
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, NULL
);
68 if (unlikely(*flt
& VM_FAULT_ERROR
)) {
69 if (*flt
& VM_FAULT_OOM
) {
72 } else if (*flt
& (VM_FAULT_SIGBUS
| VM_FAULT_SIGSEGV
)) {
83 EXPORT_SYMBOL_GPL(copro_handle_mm_fault
);
85 int copro_calculate_slb(struct mm_struct
*mm
, u64 ea
, struct copro_slb
*slb
)
90 switch (get_region_id(ea
)) {
92 pr_devel("%s: 0x%llx -- USER_REGION_ID\n", __func__
, ea
);
95 psize
= get_slice_psize(mm
, ea
);
96 ssize
= user_segment_size(ea
);
97 vsid
= get_user_vsid(&mm
->context
, ea
, ssize
);
98 vsidkey
= SLB_VSID_USER
;
100 case VMALLOC_REGION_ID
:
101 pr_devel("%s: 0x%llx -- VMALLOC_REGION_ID\n", __func__
, ea
);
102 psize
= mmu_vmalloc_psize
;
103 ssize
= mmu_kernel_ssize
;
104 vsid
= get_kernel_vsid(ea
, mmu_kernel_ssize
);
105 vsidkey
= SLB_VSID_KERNEL
;
108 pr_devel("%s: 0x%llx -- IO_REGION_ID\n", __func__
, ea
);
109 psize
= mmu_io_psize
;
110 ssize
= mmu_kernel_ssize
;
111 vsid
= get_kernel_vsid(ea
, mmu_kernel_ssize
);
112 vsidkey
= SLB_VSID_KERNEL
;
114 case LINEAR_MAP_REGION_ID
:
115 pr_devel("%s: 0x%llx -- LINEAR_MAP_REGION_ID\n", __func__
, ea
);
116 psize
= mmu_linear_psize
;
117 ssize
= mmu_kernel_ssize
;
118 vsid
= get_kernel_vsid(ea
, mmu_kernel_ssize
);
119 vsidkey
= SLB_VSID_KERNEL
;
122 pr_debug("%s: invalid region access at %016llx\n", __func__
, ea
);
129 vsid
= (vsid
<< slb_vsid_shift(ssize
)) | vsidkey
;
131 vsid
|= mmu_psize_defs
[psize
].sllp
|
132 ((ssize
== MMU_SEGSIZE_1T
) ? SLB_VSID_B_1T
: 0);
134 slb
->esid
= (ea
& (ssize
== MMU_SEGSIZE_1T
? ESID_MASK_1T
: ESID_MASK
)) | SLB_ESID_V
;
139 EXPORT_SYMBOL_GPL(copro_calculate_slb
);
141 void copro_flush_all_slbs(struct mm_struct
*mm
)
143 #ifdef CONFIG_SPU_BASE
144 spu_flush_all_slbs(mm
);
148 EXPORT_SYMBOL_GPL(copro_flush_all_slbs
);