162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/err.h>
362306a36Sopenharmony_ci#include <linux/mm.h>
462306a36Sopenharmony_ci#include <asm/current.h>
562306a36Sopenharmony_ci#include <asm/traps.h>
662306a36Sopenharmony_ci#include <asm/vdso.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_cistruct vdso_exception_table_entry {
962306a36Sopenharmony_ci	int insn, fixup;
1062306a36Sopenharmony_ci};
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cibool fixup_vdso_exception(struct pt_regs *regs, int trapnr,
1362306a36Sopenharmony_ci			  unsigned long error_code, unsigned long fault_addr)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	const struct vdso_image *image = current->mm->context.vdso_image;
1662306a36Sopenharmony_ci	const struct vdso_exception_table_entry *extable;
1762306a36Sopenharmony_ci	unsigned int nr_entries, i;
1862306a36Sopenharmony_ci	unsigned long base;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	/*
2162306a36Sopenharmony_ci	 * Do not attempt to fixup #DB or #BP.  It's impossible to identify
2262306a36Sopenharmony_ci	 * whether or not a #DB/#BP originated from within an SGX enclave and
2362306a36Sopenharmony_ci	 * SGX enclaves are currently the only use case for vDSO fixup.
2462306a36Sopenharmony_ci	 */
2562306a36Sopenharmony_ci	if (trapnr == X86_TRAP_DB || trapnr == X86_TRAP_BP)
2662306a36Sopenharmony_ci		return false;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	if (!current->mm->context.vdso)
2962306a36Sopenharmony_ci		return false;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	base =  (unsigned long)current->mm->context.vdso + image->extable_base;
3262306a36Sopenharmony_ci	nr_entries = image->extable_len / (sizeof(*extable));
3362306a36Sopenharmony_ci	extable = image->extable;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	for (i = 0; i < nr_entries; i++) {
3662306a36Sopenharmony_ci		if (regs->ip == base + extable[i].insn) {
3762306a36Sopenharmony_ci			regs->ip = base + extable[i].fixup;
3862306a36Sopenharmony_ci			regs->di = trapnr;
3962306a36Sopenharmony_ci			regs->si = error_code;
4062306a36Sopenharmony_ci			regs->dx = fault_addr;
4162306a36Sopenharmony_ci			return true;
4262306a36Sopenharmony_ci		}
4362306a36Sopenharmony_ci	}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	return false;
4662306a36Sopenharmony_ci}
47