18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Derived from arch/ppc/mm/extable.c and arch/i386/mm/extable.c.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Paul Mackerras, IBM Corp.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/bsearch.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/sort.h>
128c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
138c2ecf20Sopenharmony_ci#include <linux/extable.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#ifndef ARCH_HAS_RELATIVE_EXTABLE
168c2ecf20Sopenharmony_ci#define ex_to_insn(x)	((x)->insn)
178c2ecf20Sopenharmony_ci#else
188c2ecf20Sopenharmony_cistatic inline unsigned long ex_to_insn(const struct exception_table_entry *x)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	return (unsigned long)&x->insn + x->insn;
218c2ecf20Sopenharmony_ci}
228c2ecf20Sopenharmony_ci#endif
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#ifndef ARCH_HAS_SORT_EXTABLE
258c2ecf20Sopenharmony_ci#ifndef ARCH_HAS_RELATIVE_EXTABLE
268c2ecf20Sopenharmony_ci#define swap_ex		NULL
278c2ecf20Sopenharmony_ci#else
288c2ecf20Sopenharmony_cistatic void swap_ex(void *a, void *b, int size)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct exception_table_entry *x = a, *y = b, tmp;
318c2ecf20Sopenharmony_ci	int delta = b - a;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	tmp = *x;
348c2ecf20Sopenharmony_ci	x->insn = y->insn + delta;
358c2ecf20Sopenharmony_ci	y->insn = tmp.insn - delta;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#ifdef swap_ex_entry_fixup
388c2ecf20Sopenharmony_ci	swap_ex_entry_fixup(x, y, tmp, delta);
398c2ecf20Sopenharmony_ci#else
408c2ecf20Sopenharmony_ci	x->fixup = y->fixup + delta;
418c2ecf20Sopenharmony_ci	y->fixup = tmp.fixup - delta;
428c2ecf20Sopenharmony_ci#endif
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci#endif /* ARCH_HAS_RELATIVE_EXTABLE */
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/*
478c2ecf20Sopenharmony_ci * The exception table needs to be sorted so that the binary
488c2ecf20Sopenharmony_ci * search that we use to find entries in it works properly.
498c2ecf20Sopenharmony_ci * This is used both for the kernel exception table and for
508c2ecf20Sopenharmony_ci * the exception tables of modules that get loaded.
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_cistatic int cmp_ex_sort(const void *a, const void *b)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	const struct exception_table_entry *x = a, *y = b;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	/* avoid overflow */
578c2ecf20Sopenharmony_ci	if (ex_to_insn(x) > ex_to_insn(y))
588c2ecf20Sopenharmony_ci		return 1;
598c2ecf20Sopenharmony_ci	if (ex_to_insn(x) < ex_to_insn(y))
608c2ecf20Sopenharmony_ci		return -1;
618c2ecf20Sopenharmony_ci	return 0;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_civoid sort_extable(struct exception_table_entry *start,
658c2ecf20Sopenharmony_ci		  struct exception_table_entry *finish)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	sort(start, finish - start, sizeof(struct exception_table_entry),
688c2ecf20Sopenharmony_ci	     cmp_ex_sort, swap_ex);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#ifdef CONFIG_MODULES
728c2ecf20Sopenharmony_ci/*
738c2ecf20Sopenharmony_ci * If the exception table is sorted, any referring to the module init
748c2ecf20Sopenharmony_ci * will be at the beginning or the end.
758c2ecf20Sopenharmony_ci */
768c2ecf20Sopenharmony_civoid trim_init_extable(struct module *m)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	/*trim the beginning*/
798c2ecf20Sopenharmony_ci	while (m->num_exentries &&
808c2ecf20Sopenharmony_ci	       within_module_init(ex_to_insn(&m->extable[0]), m)) {
818c2ecf20Sopenharmony_ci		m->extable++;
828c2ecf20Sopenharmony_ci		m->num_exentries--;
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci	/*trim the end*/
858c2ecf20Sopenharmony_ci	while (m->num_exentries &&
868c2ecf20Sopenharmony_ci	       within_module_init(ex_to_insn(&m->extable[m->num_exentries - 1]),
878c2ecf20Sopenharmony_ci				  m))
888c2ecf20Sopenharmony_ci		m->num_exentries--;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci#endif /* CONFIG_MODULES */
918c2ecf20Sopenharmony_ci#endif /* !ARCH_HAS_SORT_EXTABLE */
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#ifndef ARCH_HAS_SEARCH_EXTABLE
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic int cmp_ex_search(const void *key, const void *elt)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	const struct exception_table_entry *_elt = elt;
988c2ecf20Sopenharmony_ci	unsigned long _key = *(unsigned long *)key;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* avoid overflow */
1018c2ecf20Sopenharmony_ci	if (_key > ex_to_insn(_elt))
1028c2ecf20Sopenharmony_ci		return 1;
1038c2ecf20Sopenharmony_ci	if (_key < ex_to_insn(_elt))
1048c2ecf20Sopenharmony_ci		return -1;
1058c2ecf20Sopenharmony_ci	return 0;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/*
1098c2ecf20Sopenharmony_ci * Search one exception table for an entry corresponding to the
1108c2ecf20Sopenharmony_ci * given instruction address, and return the address of the entry,
1118c2ecf20Sopenharmony_ci * or NULL if none is found.
1128c2ecf20Sopenharmony_ci * We use a binary search, and thus we assume that the table is
1138c2ecf20Sopenharmony_ci * already sorted.
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_ciconst struct exception_table_entry *
1168c2ecf20Sopenharmony_cisearch_extable(const struct exception_table_entry *base,
1178c2ecf20Sopenharmony_ci	       const size_t num,
1188c2ecf20Sopenharmony_ci	       unsigned long value)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	return bsearch(&value, base, num,
1218c2ecf20Sopenharmony_ci		       sizeof(struct exception_table_entry), cmp_ex_search);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci#endif
124