162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Based on arch/arm/mm/extable.c
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/bitfield.h>
762306a36Sopenharmony_ci#include <linux/extable.h>
862306a36Sopenharmony_ci#include <linux/uaccess.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <asm/asm-extable.h>
1162306a36Sopenharmony_ci#include <asm/ptrace.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic inline unsigned long
1462306a36Sopenharmony_ciget_ex_fixup(const struct exception_table_entry *ex)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	return ((unsigned long)&ex->fixup + ex->fixup);
1762306a36Sopenharmony_ci}
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic bool ex_handler_uaccess_err_zero(const struct exception_table_entry *ex,
2062306a36Sopenharmony_ci					struct pt_regs *regs)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data);
2362306a36Sopenharmony_ci	int reg_zero = FIELD_GET(EX_DATA_REG_ZERO, ex->data);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	pt_regs_write_reg(regs, reg_err, -EFAULT);
2662306a36Sopenharmony_ci	pt_regs_write_reg(regs, reg_zero, 0);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	regs->pc = get_ex_fixup(ex);
2962306a36Sopenharmony_ci	return true;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic bool
3362306a36Sopenharmony_ciex_handler_load_unaligned_zeropad(const struct exception_table_entry *ex,
3462306a36Sopenharmony_ci				  struct pt_regs *regs)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	int reg_data = FIELD_GET(EX_DATA_REG_DATA, ex->data);
3762306a36Sopenharmony_ci	int reg_addr = FIELD_GET(EX_DATA_REG_ADDR, ex->data);
3862306a36Sopenharmony_ci	unsigned long data, addr, offset;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	addr = pt_regs_read_reg(regs, reg_addr);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	offset = addr & 0x7UL;
4362306a36Sopenharmony_ci	addr &= ~0x7UL;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	data = *(unsigned long*)addr;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#ifndef __AARCH64EB__
4862306a36Sopenharmony_ci	data >>= 8 * offset;
4962306a36Sopenharmony_ci#else
5062306a36Sopenharmony_ci	data <<= 8 * offset;
5162306a36Sopenharmony_ci#endif
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	pt_regs_write_reg(regs, reg_data, data);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	regs->pc = get_ex_fixup(ex);
5662306a36Sopenharmony_ci	return true;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cibool fixup_exception(struct pt_regs *regs)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	const struct exception_table_entry *ex;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	ex = search_exception_tables(instruction_pointer(regs));
6462306a36Sopenharmony_ci	if (!ex)
6562306a36Sopenharmony_ci		return false;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	switch (ex->type) {
6862306a36Sopenharmony_ci	case EX_TYPE_BPF:
6962306a36Sopenharmony_ci		return ex_handler_bpf(ex, regs);
7062306a36Sopenharmony_ci	case EX_TYPE_UACCESS_ERR_ZERO:
7162306a36Sopenharmony_ci	case EX_TYPE_KACCESS_ERR_ZERO:
7262306a36Sopenharmony_ci		return ex_handler_uaccess_err_zero(ex, regs);
7362306a36Sopenharmony_ci	case EX_TYPE_LOAD_UNALIGNED_ZEROPAD:
7462306a36Sopenharmony_ci		return ex_handler_load_unaligned_zeropad(ex, regs);
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	BUG();
7862306a36Sopenharmony_ci}
79