18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/arch/sparc/mm/extable.c
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/extable.h>
88c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_civoid sort_extable(struct exception_table_entry *start,
118c2ecf20Sopenharmony_ci		  struct exception_table_entry *finish)
128c2ecf20Sopenharmony_ci{
138c2ecf20Sopenharmony_ci}
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/* Caller knows they are in a range if ret->fixup == 0 */
168c2ecf20Sopenharmony_ciconst struct exception_table_entry *
178c2ecf20Sopenharmony_cisearch_extable(const struct exception_table_entry *base,
188c2ecf20Sopenharmony_ci	       const size_t num,
198c2ecf20Sopenharmony_ci	       unsigned long value)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	int i;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	/* Single insn entries are encoded as:
248c2ecf20Sopenharmony_ci	 *	word 1:	insn address
258c2ecf20Sopenharmony_ci	 *	word 2:	fixup code address
268c2ecf20Sopenharmony_ci	 *
278c2ecf20Sopenharmony_ci	 * Range entries are encoded as:
288c2ecf20Sopenharmony_ci	 *	word 1: first insn address
298c2ecf20Sopenharmony_ci	 *	word 2: 0
308c2ecf20Sopenharmony_ci	 *	word 3: last insn address + 4 bytes
318c2ecf20Sopenharmony_ci	 *	word 4: fixup code address
328c2ecf20Sopenharmony_ci	 *
338c2ecf20Sopenharmony_ci	 * Deleted entries are encoded as:
348c2ecf20Sopenharmony_ci	 *	word 1: unused
358c2ecf20Sopenharmony_ci	 *	word 2: -1
368c2ecf20Sopenharmony_ci	 *
378c2ecf20Sopenharmony_ci	 * See asm/uaccess.h for more details.
388c2ecf20Sopenharmony_ci	 */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	/* 1. Try to find an exact match. */
418c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++) {
428c2ecf20Sopenharmony_ci		if (base[i].fixup == 0) {
438c2ecf20Sopenharmony_ci			/* A range entry, skip both parts. */
448c2ecf20Sopenharmony_ci			i++;
458c2ecf20Sopenharmony_ci			continue;
468c2ecf20Sopenharmony_ci		}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci		/* A deleted entry; see trim_init_extable */
498c2ecf20Sopenharmony_ci		if (base[i].fixup == -1)
508c2ecf20Sopenharmony_ci			continue;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci		if (base[i].insn == value)
538c2ecf20Sopenharmony_ci			return &base[i];
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	/* 2. Try to find a range match. */
578c2ecf20Sopenharmony_ci	for (i = 0; i < (num - 1); i++) {
588c2ecf20Sopenharmony_ci		if (base[i].fixup)
598c2ecf20Sopenharmony_ci			continue;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci		if (base[i].insn <= value && base[i + 1].insn > value)
628c2ecf20Sopenharmony_ci			return &base[i];
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci		i++;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci        return NULL;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#ifdef CONFIG_MODULES
718c2ecf20Sopenharmony_ci/* We could memmove them around; easier to mark the trimmed ones. */
728c2ecf20Sopenharmony_civoid trim_init_extable(struct module *m)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	unsigned int i;
758c2ecf20Sopenharmony_ci	bool range;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	for (i = 0; i < m->num_exentries; i += range ? 2 : 1) {
788c2ecf20Sopenharmony_ci		range = m->extable[i].fixup == 0;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci		if (within_module_init(m->extable[i].insn, m)) {
818c2ecf20Sopenharmony_ci			m->extable[i].fixup = -1;
828c2ecf20Sopenharmony_ci			if (range)
838c2ecf20Sopenharmony_ci				m->extable[i+1].fixup = -1;
848c2ecf20Sopenharmony_ci		}
858c2ecf20Sopenharmony_ci		if (range)
868c2ecf20Sopenharmony_ci			i++;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci#endif /* CONFIG_MODULES */
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/* Special extable search, which handles ranges.  Returns fixup */
928c2ecf20Sopenharmony_ciunsigned long search_extables_range(unsigned long addr, unsigned long *g2)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	const struct exception_table_entry *entry;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	entry = search_exception_tables(addr);
978c2ecf20Sopenharmony_ci	if (!entry)
988c2ecf20Sopenharmony_ci		return 0;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* Inside range?  Fix g2 and return correct fixup */
1018c2ecf20Sopenharmony_ci	if (!entry->fixup) {
1028c2ecf20Sopenharmony_ci		*g2 = (addr - entry->insn) / 4;
1038c2ecf20Sopenharmony_ci		return (entry + 1)->fixup;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return entry->fixup;
1078c2ecf20Sopenharmony_ci}
108