// SPDX-License-Identifier: GPL-2.0 /* * unaligned.c: Unaligned load/store trap handling with special * cases for the kernel to do them more quickly. * * Copyright (C) 1996,2008 David S. Miller (davem@davemloft.net) * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
/* GCC should never warn that control reaches the end * of this function without returning a value because * die_if_kernel() is marked with attribute 'noreturn'. * Alas, some versions do...
*/
if (!from_kernel && test_thread_flag(TIF_32BIT))
addr &= 0xffffffff;
return addr;
}
/* This is just to make gcc think die_if_kernel does return... */ staticvoid __used unaligned_panic(char *str, struct pt_regs *regs)
{
die_if_kernel(str, regs);
}
externint do_int_load(unsignedlong *dest_reg, int size, unsignedlong *saddr, int is_signed, int asi);
externint __do_int_store(unsignedlong *dst_addr, int size, unsignedlong src_val, int asi);
staticinlineint do_int_store(int reg_num, int size, unsignedlong *dst_addr, struct pt_regs *regs, int asi, int orig_asi)
{ unsignedlong zero = 0; unsignedlong *src_val_p = &zero; unsignedlong src_val;
/* If this is a {get,put}_user() on an unaligned userspace pointer, * just signal a fault and do not log the event.
*/ if (asi == ASI_AIUS) {
kernel_mna_trap_fault(0); return;
}
log_unaligned(regs);
if (!ok_for_kernel(insn) || dir == both) {
printk("Unsupported unaligned load/store trap for kernel " "at <%016lx>.\n", regs->tpc);
unaligned_panic("Kernel does fpu/atomic " "unaligned load/store.", regs);
kernel_mna_trap_fault(0);
} else { unsignedlong addr, *reg_addr; int err;
addr = compute_effective_address(regs, insn,
((insn >> 25) & 0x1f));
perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, addr); switch (asi) { case ASI_NL: case ASI_AIUPL: case ASI_AIUSL: case ASI_PL: case ASI_SL: case ASI_PNFL: case ASI_SNFL:
asi &= ~0x08; break;
} switch (dir) { case load:
reg_addr = fetch_reg_addr(((insn>>25)&0x1f), regs);
err = do_int_load(reg_addr, size,
(unsignedlong *) addr,
decode_signedness(insn), asi); if (likely(!err) && unlikely(asi != orig_asi)) { unsignedlong val_in = *reg_addr; switch (size) { case2:
val_in = swab16(val_in); break; case4:
val_in = swab32(val_in); break; case8:
val_in = swab64(val_in); break; case16: default:
BUG(); break;
}
*reg_addr = val_in;
} break;
if (tstate & TSTATE_PRIV)
die_if_kernel("lddfmna from kernel", regs);
perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, sfar); if (test_thread_flag(TIF_32BIT))
pc = (u32)pc; if (get_user(insn, (u32 __user *) pc) != -EFAULT) { int asi = decode_asi(insn, regs);
u32 first, second; int err;
if ((asi > ASI_SNFL) ||
(asi < ASI_P)) goto daex;
first = second = 0;
err = get_user(first, (u32 __user *)sfar); if (!err)
err = get_user(second, (u32 __user *)(sfar + 4)); if (err) { if (!(asi & 0x2)) goto daex;
first = second = 0;
}
save_and_clear_fpu();
freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20);
value = (((u64)first) << 32) | second; if (asi & 0x8) /* Little */
value = __swab64p(&value);
flag = (freg < 32) ? FPRS_DL : FPRS_DU; if (!(current_thread_info()->fpsaved[0] & FPRS_FEF)) {
current_thread_info()->fpsaved[0] = FPRS_FEF;
current_thread_info()->gsr[0] = 0;
} if (!(current_thread_info()->fpsaved[0] & flag)) { if (freg < 32)
memset(f->regs, 0, 32*sizeof(u32)); else
memset(f->regs+32, 0, 32*sizeof(u32));
}
*(u64 *)(f->regs + freg) = value;
current_thread_info()->fpsaved[0] |= flag;
} else {
daex: if (tlb_type == hypervisor)
sun4v_data_access_exception(regs, sfar, sfsr); else
spitfire_data_access_exception(regs, sfsr, sfar); goto out;
}
advance(regs);
out:
exception_exit(prev_state);
}
if (tstate & TSTATE_PRIV)
die_if_kernel("stdfmna from kernel", regs);
perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, sfar); if (test_thread_flag(TIF_32BIT))
pc = (u32)pc; if (get_user(insn, (u32 __user *) pc) != -EFAULT) { int asi = decode_asi(insn, regs);
freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20);
value = 0;
flag = (freg < 32) ? FPRS_DL : FPRS_DU; if ((asi > ASI_SNFL) ||
(asi < ASI_P)) goto daex;
save_and_clear_fpu(); if (current_thread_info()->fpsaved[0] & flag)
value = *(u64 *)&f->regs[freg]; switch (asi) { case ASI_P: case ASI_S: break; case ASI_PL: case ASI_SL:
value = __swab64p(&value); break; default: goto daex;
} if (put_user (value >> 32, (u32 __user *) sfar) ||
__put_user ((u32)value, (u32 __user *)(sfar + 4))) goto daex;
} else {
daex: if (tlb_type == hypervisor)
sun4v_data_access_exception(regs, sfar, sfsr); else
spitfire_data_access_exception(regs, sfsr, sfar); goto out;
}
advance(regs);
out:
exception_exit(prev_state);
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.28Bemerkung:
(vorverarbeitet am 2026-06-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.