Home | History | Annotate | Line # | Download | only in aarch64
      1 /*	$NetBSD: fault.c,v 1.29 2026/05/21 10:07:02 skrll Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2017 Ryo Shimizu
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
     20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
     25  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: fault.c,v 1.29 2026/05/21 10:07:02 skrll Exp $");
     31 
     32 #include "opt_compat_netbsd32.h"
     33 #include "opt_cpuoptions.h"
     34 #include "opt_ddb.h"
     35 #include "opt_uvmhist.h"
     36 
     37 #include <sys/param.h>
     38 #include <sys/kauth.h>
     39 #include <sys/proc.h>
     40 #include <sys/siginfo.h>
     41 
     42 #include <uvm/uvm.h>
     43 
     44 #include <aarch64/frame.h>
     45 #include <aarch64/machdep.h>
     46 #include <aarch64/armreg.h>
     47 #include <aarch64/db_machdep.h>
     48 #if defined(PMAP_DEBUG)
     49 #include <aarch64/pmap.h>
     50 #endif
     51 
     52 #include <arm/cpufunc.h>
     53 
     54 UVMHIST_DECL(pmaphist);
     55 
     56 const char * const fault_status_code[] = {
     57 	[ESR_ISS_FSC_ADDRESS_SIZE_FAULT_0] = "Address Size Fault L0",
     58 	[ESR_ISS_FSC_ADDRESS_SIZE_FAULT_1] = "Address Size Fault L1",
     59 	[ESR_ISS_FSC_ADDRESS_SIZE_FAULT_2] = "Address Size Fault L2",
     60 	[ESR_ISS_FSC_ADDRESS_SIZE_FAULT_3] = "Address Size Fault L3",
     61 	[ESR_ISS_FSC_TRANSLATION_FAULT_0] = "Translation Fault L0",
     62 	[ESR_ISS_FSC_TRANSLATION_FAULT_1] = "Translation Fault L1",
     63 	[ESR_ISS_FSC_TRANSLATION_FAULT_2] = "Translation Fault L2",
     64 	[ESR_ISS_FSC_TRANSLATION_FAULT_3] = "Translation Fault L3",
     65 	[ESR_ISS_FSC_ACCESS_FAULT_0] = "Access Flag Fault L0",
     66 	[ESR_ISS_FSC_ACCESS_FAULT_1] = "Access Flag Fault L1",
     67 	[ESR_ISS_FSC_ACCESS_FAULT_2] = "Access Flag Fault L2",
     68 	[ESR_ISS_FSC_ACCESS_FAULT_3] = "Access Flag Fault L3",
     69 	[ESR_ISS_FSC_PERM_FAULT_0] = "Permission Fault L0",
     70 	[ESR_ISS_FSC_PERM_FAULT_1] = "Permission Fault L1",
     71 	[ESR_ISS_FSC_PERM_FAULT_2] = "Permission Fault L2",
     72 	[ESR_ISS_FSC_PERM_FAULT_3] = "Permission Fault L3",
     73 	[ESR_ISS_FSC_SYNC_EXTERNAL_ABORT]  =
     74 	    "Synchronous External Abort",
     75 	[ESR_ISS_FSC_SYNC_EXTERNAL_ABORT_TTWALK_0]  =
     76 	    "Synchronous External Abort on translation table walk L0",
     77 	[ESR_ISS_FSC_SYNC_EXTERNAL_ABORT_TTWALK_1]  =
     78 	    "Synchronous External Abort on translation table walk L1",
     79 	[ESR_ISS_FSC_SYNC_EXTERNAL_ABORT_TTWALK_2]  =
     80 	    "Synchronous External Abort on translation table walk L2",
     81 	[ESR_ISS_FSC_SYNC_EXTERNAL_ABORT_TTWALK_3]  =
     82 	    "Synchronous External Abort on translation table walk L3",
     83 	[ESR_ISS_FSC_SYNC_PARITY_ERROR] =
     84 	    "Synchronous Parity error",
     85 	[ESR_ISS_FSC_SYNC_PARITY_ERROR_ON_TTWALK_0] =
     86 	    "Synchronous Parity error on translation table walk L0",
     87 	[ESR_ISS_FSC_SYNC_PARITY_ERROR_ON_TTWALK_1] =
     88 	    "Synchronous Parity error on translation table walk L1",
     89 	[ESR_ISS_FSC_SYNC_PARITY_ERROR_ON_TTWALK_2] =
     90 	    "Synchronous Parity error on translation table walk L2",
     91 	[ESR_ISS_FSC_SYNC_PARITY_ERROR_ON_TTWALK_3] =
     92 	    "Synchronous Parity error on translation table walk L3",
     93 	[ESR_ISS_FSC_ALIGNMENT_FAULT] = "Alignment Fault",
     94 	[ESR_ISS_FSC_TLB_CONFLICT_FAULT] = "TLB Conflict Fault",
     95 	[ESR_ISS_FSC_LOCKDOWN_ABORT] = "Lockdown Abort",
     96 	[ESR_ISS_FSC_UNSUPPORTED_EXCLUSIVE] = "Unsupported exclusive",
     97 	[ESR_ISS_FSC_FIRST_LEVEL_DOMAIN_FAULT] =
     98 	    "First Level Domain Fault",
     99 	[ESR_ISS_FSC_SECOND_LEVEL_DOMAIN_FAULT] =
    100 	    "Second Level Domain Fault"
    101 };
    102 
    103 static bool
    104 is_fatal_abort(uint32_t esr)
    105 {
    106 	uint32_t fsc;
    107 
    108 	fsc = __SHIFTOUT(esr, ESR_ISS_DATAABORT_DFSC);
    109 
    110 	switch (fsc) {
    111 	case ESR_ISS_FSC_TRANSLATION_FAULT_0:
    112 	case ESR_ISS_FSC_TRANSLATION_FAULT_1:
    113 	case ESR_ISS_FSC_TRANSLATION_FAULT_2:
    114 	case ESR_ISS_FSC_TRANSLATION_FAULT_3:
    115 	case ESR_ISS_FSC_ACCESS_FAULT_0:
    116 	case ESR_ISS_FSC_ACCESS_FAULT_1:
    117 	case ESR_ISS_FSC_ACCESS_FAULT_2:
    118 	case ESR_ISS_FSC_ACCESS_FAULT_3:
    119 	case ESR_ISS_FSC_PERM_FAULT_0:
    120 	case ESR_ISS_FSC_PERM_FAULT_1:
    121 	case ESR_ISS_FSC_PERM_FAULT_2:
    122 	case ESR_ISS_FSC_PERM_FAULT_3:
    123 		return false;
    124 	}
    125 	return true;
    126 }
    127 
    128 /* SPSR_M is SPSR_M_EL0T or SPSR_M_USR32 ? */
    129 #define IS_SPSR_USER(spsr)	\
    130 	(((spsr) & (SPSR_M & ~SPSR_A32)) == 0)
    131 
    132 void
    133 data_abort_handler(struct trapframe *tf, uint32_t eclass)
    134 {
    135 	struct proc *p;
    136 	struct lwp *l;
    137 	struct vm_map *map;
    138 	struct faultbuf *fb;
    139 	vaddr_t va;
    140 	uint32_t esr, fsc, rw;
    141 	vm_prot_t ftype;
    142 	int error = EFAULT, len;
    143 	const bool user = IS_SPSR_USER(tf->tf_spsr) ? true : false;
    144 	bool is_pan_trap = false;
    145 
    146 	bool fatalabort;
    147 	const char *faultstr;
    148 	static char panicinfo[256];
    149 
    150 	UVMHIST_FUNC(__func__);
    151 	UVMHIST_CALLED(pmaphist);
    152 
    153 	__asm __volatile ("clrex");
    154 
    155 	l = curlwp;
    156 
    157 	esr = tf->tf_esr;
    158 	rw = __SHIFTOUT(esr, ESR_ISS_DATAABORT_WnR); /* 0 if IFSC */
    159 
    160 	fatalabort = is_fatal_abort(esr);
    161 	if (fatalabort)
    162 		goto do_fault;
    163 
    164 	p = l->l_proc;
    165 	va = trunc_page((vaddr_t)tf->tf_far);
    166 
    167 	/* eliminate address tag if ECR_EL1.TBI[01] is enabled */
    168 	va = aarch64_untag_address(va);
    169 
    170 	if (VM_MIN_KERNEL_ADDRESS <= va && va < VM_MAX_KERNEL_ADDRESS) {
    171 		map = kernel_map;
    172 		UVMHIST_LOG(pmaphist, "use kernel_map %p", map, 0, 0, 0);
    173 	} else if (VM_MIN_ADDRESS <= va && va <= VM_MAX_ADDRESS) {
    174 		map = &p->p_vmspace->vm_map;
    175 		UVMHIST_LOG(pmaphist, "use user vm_map %p (kernel_map=%p)",
    176 		   map, kernel_map, 0, 0);
    177 	} else
    178 		goto do_fault;
    179 
    180 	if (eclass == ESR_EC_INSN_ABT_EL0 || eclass == ESR_EC_INSN_ABT_EL1)
    181 		ftype = VM_PROT_EXECUTE;
    182 	else if (__SHIFTOUT(esr, ESR_ISS_DATAABORT_CM))
    183 		ftype = VM_PROT_READ;
    184 	else
    185 		ftype = (rw == 0) ? VM_PROT_READ : VM_PROT_WRITE;
    186 
    187 	if (ftype & VM_PROT_EXECUTE) {
    188 		UVMHIST_LOG(pmaphist, "pagefault %016jx %016jx user=%jd EXEC",
    189 		    tf->tf_far, va, user, 0);
    190 	} else {
    191 		UVMHIST_LOG(pmaphist, "pagefault %016lx %016lx user=%jd "
    192 		    "write=%jd", tf->tf_far, va, user, rw);
    193 	}
    194 
    195 	if (__predict_false(!user && map != kernel_map &&
    196 	    (tf->tf_spsr & SPSR_PAN))) {
    197 		/*
    198 		 * We were in kernel mode, faulted on a user address,
    199 		 * and had PAN enabled. This is a fatal fault.
    200 		 */
    201 		is_pan_trap = true;
    202 		goto handle_fault;
    203 	}
    204 
    205 	/* reference/modified emulation */
    206 #ifdef ARMV81_HAFDBS
    207 	if (aarch64_hafdbs_enabled == ID_AA64MMFR1_EL1_HAFDBS_NONE ||
    208 	    (aarch64_hafdbs_enabled == ID_AA64MMFR1_EL1_HAFDBS_A &&
    209 	    ftype == VM_PROT_WRITE))
    210 #endif
    211 	{
    212 		if (pmap_fault_fixup(map->pmap, va, ftype, user)) {
    213 			UVMHIST_LOG(pmaphist, "fixed: va=%016llx", tf->tf_far, 0, 0, 0);
    214 			return;
    215 		}
    216 	}
    217 
    218 	fb = cpu_disable_onfault();
    219 	error = uvm_fault(map, va, ftype);
    220 	cpu_enable_onfault(fb);
    221 	if (__predict_true(error == 0)) {
    222 		if (user)
    223 			uvm_grow(p, va);
    224 
    225 		UVMHIST_LOG(pmaphist, "uvm_fault success: far=%016lx, va=%016llx",
    226 		    tf->tf_far, va, 0, 0);
    227 		return;
    228 	}
    229 
    230  do_fault:
    231 	/* faultbail path? */
    232 	kpreempt_disable();
    233 	const bool intrdepthzero = (curcpu()->ci_intr_depth == 0);
    234 	kpreempt_enable();
    235 	if (intrdepthzero) {
    236 		fb = cpu_disable_onfault();
    237 		if (fb != NULL) {
    238 			cpu_jump_onfault(tf, fb, error);
    239 			return;
    240 		}
    241 	}
    242 
    243  handle_fault:
    244 	fsc = __SHIFTOUT(esr, ESR_ISS_DATAABORT_DFSC); /* also IFSC */
    245 	if (user) {
    246 		if (!fatalabort) {
    247 			switch (error) {
    248 			case ENOMEM:
    249 				printf("UVM: pid %d (%s), uid %d killed: "
    250 				    "out of swap\n",
    251 				    l->l_proc->p_pid, l->l_proc->p_comm,
    252 				    l->l_cred ?
    253 				    kauth_cred_geteuid(l->l_cred) : -1);
    254 				do_trapsignal(l, SIGKILL, 0,
    255 				    (void *)tf->tf_far, esr);
    256 				break;
    257 			case EACCES:
    258 				do_trapsignal(l, SIGSEGV, SEGV_ACCERR,
    259 				    (void *)tf->tf_far, esr);
    260 				break;
    261 			case EINVAL:
    262 				do_trapsignal(l, SIGBUS, BUS_ADRERR,
    263 				    (void *)tf->tf_far, esr);
    264 				break;
    265 			default:
    266 				do_trapsignal(l, SIGSEGV, SEGV_MAPERR,
    267 				    (void *)tf->tf_far, esr);
    268 				break;
    269 			}
    270 		} else {
    271 			/*
    272 			 * fatal abort in usermode
    273 			 */
    274 			switch (fsc) {
    275 			case ESR_ISS_FSC_TLB_CONFLICT_FAULT:
    276 			case ESR_ISS_FSC_LOCKDOWN_ABORT:
    277 			case ESR_ISS_FSC_UNSUPPORTED_EXCLUSIVE:
    278 			case ESR_ISS_FSC_FIRST_LEVEL_DOMAIN_FAULT:
    279 			case ESR_ISS_FSC_SECOND_LEVEL_DOMAIN_FAULT:
    280 			default:
    281 				do_trapsignal(l, SIGSEGV, SEGV_MAPERR,
    282 				    (void *)tf->tf_far, esr);
    283 				break;
    284 			case ESR_ISS_FSC_ADDRESS_SIZE_FAULT_0:
    285 			case ESR_ISS_FSC_ADDRESS_SIZE_FAULT_1:
    286 			case ESR_ISS_FSC_ADDRESS_SIZE_FAULT_2:
    287 			case ESR_ISS_FSC_ADDRESS_SIZE_FAULT_3:
    288 				do_trapsignal(l, SIGBUS, BUS_ADRERR,
    289 				    (void *)tf->tf_far, esr);
    290 				break;
    291 			case ESR_ISS_FSC_SYNC_EXTERNAL_ABORT:
    292 			case ESR_ISS_FSC_SYNC_EXTERNAL_ABORT_TTWALK_0:
    293 			case ESR_ISS_FSC_SYNC_EXTERNAL_ABORT_TTWALK_1:
    294 			case ESR_ISS_FSC_SYNC_EXTERNAL_ABORT_TTWALK_2:
    295 			case ESR_ISS_FSC_SYNC_EXTERNAL_ABORT_TTWALK_3:
    296 			case ESR_ISS_FSC_SYNC_PARITY_ERROR:
    297 			case ESR_ISS_FSC_SYNC_PARITY_ERROR_ON_TTWALK_0:
    298 			case ESR_ISS_FSC_SYNC_PARITY_ERROR_ON_TTWALK_1:
    299 			case ESR_ISS_FSC_SYNC_PARITY_ERROR_ON_TTWALK_2:
    300 			case ESR_ISS_FSC_SYNC_PARITY_ERROR_ON_TTWALK_3:
    301 				do_trapsignal(l, SIGBUS, BUS_OBJERR,
    302 				    (void *)tf->tf_far, esr);
    303 				break;
    304 			case ESR_ISS_FSC_ALIGNMENT_FAULT:
    305 				do_trapsignal(l, SIGBUS, BUS_ADRALN,
    306 				    (void *)tf->tf_far, esr);
    307 				break;
    308 			}
    309 		}
    310 
    311 #undef DEBUG_DUMP_ON_USERFAULT		/* DEBUG */
    312 #undef DEBUG_DDB_ON_USERFAULT		/* DEBUG */
    313 
    314 #if defined(DEBUG_DUMP_ON_USERFAULT) || \
    315     (defined(DDB) && defined(DEBUG_DDB_ON_USERFAULT))
    316 		__nothing;
    317 #else
    318 		return;
    319 #endif
    320 	}
    321 
    322 	/*
    323 	 * fatal abort. analyze fault status code to show by panic()
    324 	 */
    325 	len = snprintf(panicinfo, sizeof(panicinfo), "Trap: %s:",
    326 	    eclass_trapname(eclass));
    327 
    328 	if (fsc >= __arraycount(fault_status_code) ||
    329 	    (faultstr = fault_status_code[fsc]) == NULL)
    330 		len += snprintf(panicinfo + len, sizeof(panicinfo) - len,
    331 		    " unknown fault status 0x%x ", fsc);
    332 	else
    333 		len += snprintf(panicinfo + len, sizeof(panicinfo) - len,
    334 		    " %s", faultstr);
    335 
    336 	if (__SHIFTOUT(esr, ESR_EC) == ESR_EC_DATA_ABT_EL1 ||
    337 	    __SHIFTOUT(esr, ESR_EC) == ESR_EC_DATA_ABT_EL0)
    338 		len += snprintf(panicinfo + len, sizeof(panicinfo) - len,
    339 		    " with %s access", (rw == 0) ? "read" : "write");
    340 
    341 	len += snprintf(panicinfo + len, sizeof(panicinfo) - len,
    342 	    " for %016"PRIxREGISTER, tf->tf_far);
    343 
    344 	if (__SHIFTOUT(esr, ESR_ISS_DATAABORT_EA) != 0)
    345 		len += snprintf(panicinfo + len, sizeof(panicinfo) - len,
    346 		    ", External abort");
    347 
    348 	if (__SHIFTOUT(esr, ESR_ISS_DATAABORT_S1PTW) != 0)
    349 		len += snprintf(panicinfo + len, sizeof(panicinfo) - len,
    350 		    ", State 2 Fault");
    351 
    352 	if (is_pan_trap)
    353 		len += snprintf(panicinfo + len, sizeof(panicinfo) - len,
    354 		    ", PAN Set");
    355 
    356 	len += snprintf(panicinfo + len, sizeof(panicinfo) - len,
    357 	    ": pc %016"PRIxREGISTER, tf->tf_pc);
    358 
    359 	if (tf->tf_pc == tf->tf_far) {	/* XXX? */
    360 		/* fault address is pc. the causal instruction cannot be read */
    361 		len += snprintf(panicinfo + len, sizeof(panicinfo) - len,
    362 		    ": opcode unknown");
    363 	}
    364 #ifdef DDB
    365 	else {
    366 		/* ...and disassemble the instruction */
    367 		len += snprintf(panicinfo + len, sizeof(panicinfo) - len,
    368 		    ": %s", strdisasm(tf->tf_pc, tf->tf_spsr));
    369 	}
    370 #endif
    371 
    372 	if (user) {
    373 #if defined(DEBUG_DDB_ON_USERFAULT) && defined(DDB)
    374 		printf("%s\n", panicinfo);
    375 		Debugger();
    376 #elif defined(DEBUG_DUMP_ON_USERFAULT)
    377 		printf("%s\n", panicinfo);
    378 		dump_trapframe(tf, printf);
    379 #endif
    380 	}
    381 
    382 	if (!user)
    383 		panic("%s\n", panicinfo);
    384 }
    385