1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Based on arch/arm/mm/extable.c
4 */
5
6#include <linux/bitfield.h>
7#include <linux/extable.h>
8#include <linux/uaccess.h>
9
10#include <asm/asm-extable.h>
11#include <asm/esr.h>
12#include <asm/ptrace.h>
13
14static bool cpy_faulted_on_uaccess(const struct exception_table_entry *ex,
15 unsigned long esr)
16{
17 bool uaccess_is_write = FIELD_GET(EX_DATA_UACCESS_WRITE, ex->data);
18 bool fault_on_write = esr & ESR_ELx_WNR;
19
20 return uaccess_is_write == fault_on_write;
21}
22
23bool insn_may_access_user(unsigned long addr, unsigned long esr)
24{
25 const struct exception_table_entry *ex = search_exception_tables(add: addr);
26
27 if (!ex)
28 return false;
29
30 switch (ex->type) {
31 case EX_TYPE_UACCESS_CPY:
32 return cpy_faulted_on_uaccess(ex, esr);
33 default:
34 return true;
35 }
36}
37
38static inline unsigned long
39get_ex_fixup(const struct exception_table_entry *ex)
40{
41 return ((unsigned long)&ex->fixup + ex->fixup);
42}
43
44static bool ex_handler_uaccess_err_zero(const struct exception_table_entry *ex,
45 struct pt_regs *regs)
46{
47 int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data);
48 int reg_zero = FIELD_GET(EX_DATA_REG_ZERO, ex->data);
49
50 pt_regs_write_reg(regs, reg_err, -EFAULT);
51 pt_regs_write_reg(regs, reg_zero, 0);
52
53 regs->pc = get_ex_fixup(ex);
54 return true;
55}
56
57static bool ex_handler_uaccess_cpy(const struct exception_table_entry *ex,
58 struct pt_regs *regs, unsigned long esr)
59{
60 /* Do not fix up faults on kernel memory accesses */
61 if (!cpy_faulted_on_uaccess(ex, esr))
62 return false;
63
64 regs->pc = get_ex_fixup(ex);
65 return true;
66}
67
68static bool
69ex_handler_load_unaligned_zeropad(const struct exception_table_entry *ex,
70 struct pt_regs *regs)
71{
72 int reg_data = FIELD_GET(EX_DATA_REG_DATA, ex->data);
73 int reg_addr = FIELD_GET(EX_DATA_REG_ADDR, ex->data);
74 unsigned long data, addr, offset;
75
76 addr = pt_regs_read_reg(regs, reg_addr);
77
78 offset = addr & 0x7UL;
79 addr &= ~0x7UL;
80
81 data = *(unsigned long*)addr;
82
83#ifndef __AARCH64EB__
84 data >>= 8 * offset;
85#else
86 data <<= 8 * offset;
87#endif
88
89 pt_regs_write_reg(regs, reg_data, data);
90
91 regs->pc = get_ex_fixup(ex);
92 return true;
93}
94
95bool fixup_exception(struct pt_regs *regs, unsigned long esr)
96{
97 const struct exception_table_entry *ex;
98
99 ex = search_exception_tables(add: instruction_pointer(regs));
100 if (!ex)
101 return false;
102
103 switch (ex->type) {
104 case EX_TYPE_BPF:
105 return ex_handler_bpf(x: ex, regs);
106 case EX_TYPE_UACCESS_ERR_ZERO:
107 case EX_TYPE_KACCESS_ERR_ZERO:
108 return ex_handler_uaccess_err_zero(ex, regs);
109 case EX_TYPE_UACCESS_CPY:
110 return ex_handler_uaccess_cpy(ex, regs, esr);
111 case EX_TYPE_LOAD_UNALIGNED_ZEROPAD:
112 return ex_handler_load_unaligned_zeropad(ex, regs);
113 }
114
115 BUG();
116}
117

source code of linux/arch/arm64/mm/extable.c