162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/types.h>
362306a36Sopenharmony_ci#include <linux/string.h>
462306a36Sopenharmony_ci#include <limits.h>
562306a36Sopenharmony_ci#include <stdlib.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <internal/lib.h> // page_size
862306a36Sopenharmony_ci#include "../../../util/machine.h"
962306a36Sopenharmony_ci#include "../../../util/map.h"
1062306a36Sopenharmony_ci#include "../../../util/symbol.h"
1162306a36Sopenharmony_ci#include <linux/ctype.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <symbol/kallsyms.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#if defined(__x86_64__)
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct extra_kernel_map_info {
1862306a36Sopenharmony_ci	int cnt;
1962306a36Sopenharmony_ci	int max_cnt;
2062306a36Sopenharmony_ci	struct extra_kernel_map *maps;
2162306a36Sopenharmony_ci	bool get_entry_trampolines;
2262306a36Sopenharmony_ci	u64 entry_trampoline;
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int add_extra_kernel_map(struct extra_kernel_map_info *mi, u64 start,
2662306a36Sopenharmony_ci				u64 end, u64 pgoff, const char *name)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	if (mi->cnt >= mi->max_cnt) {
2962306a36Sopenharmony_ci		void *buf;
3062306a36Sopenharmony_ci		size_t sz;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci		mi->max_cnt = mi->max_cnt ? mi->max_cnt * 2 : 32;
3362306a36Sopenharmony_ci		sz = sizeof(struct extra_kernel_map) * mi->max_cnt;
3462306a36Sopenharmony_ci		buf = realloc(mi->maps, sz);
3562306a36Sopenharmony_ci		if (!buf)
3662306a36Sopenharmony_ci			return -1;
3762306a36Sopenharmony_ci		mi->maps = buf;
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	mi->maps[mi->cnt].start = start;
4162306a36Sopenharmony_ci	mi->maps[mi->cnt].end   = end;
4262306a36Sopenharmony_ci	mi->maps[mi->cnt].pgoff = pgoff;
4362306a36Sopenharmony_ci	strlcpy(mi->maps[mi->cnt].name, name, KMAP_NAME_LEN);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	mi->cnt += 1;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return 0;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int find_extra_kernel_maps(void *arg, const char *name, char type,
5162306a36Sopenharmony_ci				  u64 start)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct extra_kernel_map_info *mi = arg;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (!mi->entry_trampoline && kallsyms2elf_binding(type) == STB_GLOBAL &&
5662306a36Sopenharmony_ci	    !strcmp(name, "_entry_trampoline")) {
5762306a36Sopenharmony_ci		mi->entry_trampoline = start;
5862306a36Sopenharmony_ci		return 0;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (is_entry_trampoline(name)) {
6262306a36Sopenharmony_ci		u64 end = start + page_size;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci		return add_extra_kernel_map(mi, start, end, 0, name);
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return 0;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ciint machine__create_extra_kernel_maps(struct machine *machine,
7162306a36Sopenharmony_ci				      struct dso *kernel)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct extra_kernel_map_info mi = { .cnt = 0, };
7462306a36Sopenharmony_ci	char filename[PATH_MAX];
7562306a36Sopenharmony_ci	int ret;
7662306a36Sopenharmony_ci	int i;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	machine__get_kallsyms_filename(machine, filename, PATH_MAX);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (symbol__restricted_filename(filename, "/proc/kallsyms"))
8162306a36Sopenharmony_ci		return 0;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	ret = kallsyms__parse(filename, &mi, find_extra_kernel_maps);
8462306a36Sopenharmony_ci	if (ret)
8562306a36Sopenharmony_ci		goto out_free;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (!mi.entry_trampoline)
8862306a36Sopenharmony_ci		goto out_free;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	for (i = 0; i < mi.cnt; i++) {
9162306a36Sopenharmony_ci		struct extra_kernel_map *xm = &mi.maps[i];
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		xm->pgoff = mi.entry_trampoline;
9462306a36Sopenharmony_ci		ret = machine__create_extra_kernel_map(machine, kernel, xm);
9562306a36Sopenharmony_ci		if (ret)
9662306a36Sopenharmony_ci			goto out_free;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	machine->trampolines_mapped = mi.cnt;
10062306a36Sopenharmony_ciout_free:
10162306a36Sopenharmony_ci	free(mi.maps);
10262306a36Sopenharmony_ci	return ret;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci#endif
106