0001
0002
0003
0004
0005
0006 #include <linux/bitfield.h>
0007 #include <linux/extable.h>
0008 #include <linux/uaccess.h>
0009
0010 #include <asm/asm-extable.h>
0011 #include <asm/ptrace.h>
0012
0013 static inline unsigned long
0014 get_ex_fixup(const struct exception_table_entry *ex)
0015 {
0016 return ((unsigned long)&ex->fixup + ex->fixup);
0017 }
0018
0019 static bool ex_handler_uaccess_err_zero(const struct exception_table_entry *ex,
0020 struct pt_regs *regs)
0021 {
0022 int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data);
0023 int reg_zero = FIELD_GET(EX_DATA_REG_ZERO, ex->data);
0024
0025 pt_regs_write_reg(regs, reg_err, -EFAULT);
0026 pt_regs_write_reg(regs, reg_zero, 0);
0027
0028 regs->pc = get_ex_fixup(ex);
0029 return true;
0030 }
0031
0032 static bool
0033 ex_handler_load_unaligned_zeropad(const struct exception_table_entry *ex,
0034 struct pt_regs *regs)
0035 {
0036 int reg_data = FIELD_GET(EX_DATA_REG_DATA, ex->data);
0037 int reg_addr = FIELD_GET(EX_DATA_REG_ADDR, ex->data);
0038 unsigned long data, addr, offset;
0039
0040 addr = pt_regs_read_reg(regs, reg_addr);
0041
0042 offset = addr & 0x7UL;
0043 addr &= ~0x7UL;
0044
0045 data = *(unsigned long*)addr;
0046
0047 #ifndef __AARCH64EB__
0048 data >>= 8 * offset;
0049 #else
0050 data <<= 8 * offset;
0051 #endif
0052
0053 pt_regs_write_reg(regs, reg_data, data);
0054
0055 regs->pc = get_ex_fixup(ex);
0056 return true;
0057 }
0058
0059 bool fixup_exception(struct pt_regs *regs)
0060 {
0061 const struct exception_table_entry *ex;
0062
0063 ex = search_exception_tables(instruction_pointer(regs));
0064 if (!ex)
0065 return false;
0066
0067 switch (ex->type) {
0068 case EX_TYPE_BPF:
0069 return ex_handler_bpf(ex, regs);
0070 case EX_TYPE_UACCESS_ERR_ZERO:
0071 case EX_TYPE_KACCESS_ERR_ZERO:
0072 return ex_handler_uaccess_err_zero(ex, regs);
0073 case EX_TYPE_LOAD_UNALIGNED_ZEROPAD:
0074 return ex_handler_load_unaligned_zeropad(ex, regs);
0075 }
0076
0077 BUG();
0078 }