Changeset View
Standalone View
sys/arm64/arm64/trap.c
Show First 20 Lines • Show All 240 Lines • ▼ Show 20 Lines | external_abort(struct thread *td, struct trapframe *frame, uint64_t esr, | ||||
panic("Unhandled EL%d external data abort", lower ? 0: 1); | panic("Unhandled EL%d external data abort", lower ? 0: 1); | ||||
} | } | ||||
static void | static void | ||||
data_abort(struct thread *td, struct trapframe *frame, uint64_t esr, | data_abort(struct thread *td, struct trapframe *frame, uint64_t esr, | ||||
uint64_t far, int lower) | uint64_t far, int lower) | ||||
{ | { | ||||
struct vm_map *map; | struct vm_map *map; | ||||
struct proc *p; | |||||
struct pcb *pcb; | struct pcb *pcb; | ||||
vm_prot_t ftype; | vm_prot_t ftype; | ||||
int error, sig, ucode; | int error, sig, ucode; | ||||
#ifdef KDB | #ifdef KDB | ||||
bool handled; | bool handled; | ||||
#endif | #endif | ||||
/* | /* | ||||
* According to the ARMv8-A rev. A.g, B2.10.5 "Load-Exclusive | * According to the ARMv8-A rev. A.g, B2.10.5 "Load-Exclusive | ||||
* and Store-Exclusive instruction usage restrictions", state | * and Store-Exclusive instruction usage restrictions", state | ||||
* of the exclusive monitors after data abort exception is unknown. | * of the exclusive monitors after data abort exception is unknown. | ||||
*/ | */ | ||||
clrex(); | clrex(); | ||||
#ifdef KDB | #ifdef KDB | ||||
if (kdb_active) { | if (kdb_active) { | ||||
kdb_reenter(); | kdb_reenter(); | ||||
return; | return; | ||||
} | } | ||||
#endif | #endif | ||||
pcb = td->td_pcb; | if (lower) { | ||||
p = td->td_proc; | map = &td->td_proc->p_vmspace->vm_map; | ||||
if (lower) | } else if (!ADDR_IS_CANONICAL(far)) { | ||||
map = &p->p_vmspace->vm_map; | |||||
else { | |||||
intr_enable(); | |||||
/* We received a TBI/PAC/etc. fault from the kernel */ | /* We received a TBI/PAC/etc. fault from the kernel */ | ||||
if (!ADDR_IS_CANONICAL(far)) { | |||||
error = KERN_INVALID_ADDRESS; | error = KERN_INVALID_ADDRESS; | ||||
goto bad_far; | goto bad_far; | ||||
} else if (ADDR_IS_KERNEL(far)) { | |||||
/* | |||||
* Handle a special case: the data abort was caused by accessing | |||||
* a thread structure while its mapping was being promoted or | |||||
* demoted, as a consequence of the break-before-make rule. It | |||||
* is not safe to enable interrupts or dereference "td" before | |||||
* this case is handled. | |||||
* | |||||
* In principle, if pmap_klookup() fails, there is no need to | |||||
* call pmap_fault() below, but avoiding that call is not worth | |||||
* the effort. | |||||
*/ | |||||
if (ESR_ELx_EXCEPTION(esr) == EXCP_DATA_ABORT) { | |||||
switch (esr & ISS_DATA_DFSC_MASK) { | |||||
case ISS_DATA_DFSC_TF_L0: | |||||
case ISS_DATA_DFSC_TF_L1: | |||||
case ISS_DATA_DFSC_TF_L2: | |||||
case ISS_DATA_DFSC_TF_L3: | |||||
if (pmap_klookup(far, NULL)) | |||||
andrew: Is there a reason to not call `pmap_fault` unconditionally here? We could then either add a… | |||||
Done Inline ActionsYes: pmap_fault() may acquire the pmap lock to handle other types of faults, which can't be done while interrupts are disabled. Interrupts have to remain disabled here since handlers might try to dereference curthread. We could skip a second pmap_fault() call here, but if it fails the first time then won't this likely be a fatal data abort anyway? markj: Yes: pmap_fault() may acquire the pmap lock to handle other types of faults, which can't be… | |||||
Done Inline ActionsIn that case I think we just use pmap_klookup directly here as all we need to know is if there is a mapping for the virtual address, even if it's currently in a break-before-make sequence. andrew: In that case I think we just use `pmap_klookup` directly here as all we need to know is if… | |||||
Done Inline Actions
I would suggest adding a sentence relating to this, e.g., "In principle, if pmap_klookup() fails, there is no need to call pmap_fault() below, but avoiding that call is not worth the effort." alc: > We could skip a second pmap_fault() call here, but if it fails the first time then won't this… | |||||
return; | |||||
break; | |||||
} | } | ||||
} | |||||
/* The top bit tells us which range to use */ | intr_enable(); | ||||
if (ADDR_IS_KERNEL(far)) { | |||||
map = kernel_map; | map = kernel_map; | ||||
} else { | } else { | ||||
map = &p->p_vmspace->vm_map; | intr_enable(); | ||||
map = &td->td_proc->p_vmspace->vm_map; | |||||
if (map == NULL) | if (map == NULL) | ||||
map = kernel_map; | map = kernel_map; | ||||
} | } | ||||
} | pcb = td->td_pcb; | ||||
/* | /* | ||||
* Try to handle translation, access flag, and permission faults. | * Try to handle translation, access flag, and permission faults. | ||||
* Translation faults may occur as a result of the required | * Translation faults may occur as a result of the required | ||||
* break-before-make sequence used when promoting or demoting | * break-before-make sequence used when promoting or demoting | ||||
* superpages. Such faults must not occur while holding the pmap lock, | * superpages. Such faults must not occur while holding the pmap lock, | ||||
* or pmap_fault() will recurse on that lock. | * or pmap_fault() will recurse on that lock. | ||||
*/ | */ | ||||
Show All 28 Lines | default: | ||||
else | else | ||||
ftype = VM_PROT_WRITE; | ftype = VM_PROT_WRITE; | ||||
break; | break; | ||||
} | } | ||||
/* Fault in the page. */ | /* Fault in the page. */ | ||||
error = vm_fault_trap(map, far, ftype, VM_FAULT_NORMAL, &sig, &ucode); | error = vm_fault_trap(map, far, ftype, VM_FAULT_NORMAL, &sig, &ucode); | ||||
if (error != KERN_SUCCESS) { | if (error != KERN_SUCCESS) { | ||||
bad_far: | |||||
if (lower) { | if (lower) { | ||||
call_trapsignal(td, sig, ucode, (void *)far, | call_trapsignal(td, sig, ucode, (void *)far, | ||||
ESR_ELx_EXCEPTION(esr)); | ESR_ELx_EXCEPTION(esr)); | ||||
} else { | } else { | ||||
bad_far: | |||||
if (td->td_intr_nesting_level == 0 && | if (td->td_intr_nesting_level == 0 && | ||||
pcb->pcb_onfault != 0) { | pcb->pcb_onfault != 0) { | ||||
frame->tf_x[0] = error; | frame->tf_x[0] = error; | ||||
frame->tf_elr = pcb->pcb_onfault; | frame->tf_elr = pcb->pcb_onfault; | ||||
return; | return; | ||||
} | } | ||||
printf("Fatal data abort:\n"); | printf("Fatal data abort:\n"); | ||||
▲ Show 20 Lines • Show All 374 Lines • Show Last 20 Lines |
Is there a reason to not call pmap_fault unconditionally here? We could then either add a flag to skip the call later or move it up to the relavant condition blocks.