1129198Scognet/*-
2129198Scognet * Copyright 1996-1998 John D. Polstra.
3129198Scognet * All rights reserved.
4129198Scognet *
5129198Scognet * Redistribution and use in source and binary forms, with or without
6129198Scognet * modification, are permitted provided that the following conditions
7129198Scognet * are met:
8129198Scognet * 1. Redistributions of source code must retain the above copyright
9129198Scognet *    notice, this list of conditions and the following disclaimer.
10129198Scognet * 2. Redistributions in binary form must reproduce the above copyright
11129198Scognet *    notice, this list of conditions and the following disclaimer in the
12129198Scognet *    documentation and/or other materials provided with the distribution.
13129198Scognet *
14129198Scognet * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15129198Scognet * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16129198Scognet * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17129198Scognet * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18129198Scognet * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19129198Scognet * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20129198Scognet * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21129198Scognet * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22129198Scognet * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23129198Scognet * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24129198Scognet */
25129198Scognet
26129198Scognet#include <sys/cdefs.h>
27129198Scognet__FBSDID("$FreeBSD: stable/11/sys/arm/arm/elf_machdep.c 338867 2018-09-21 20:40:37Z markj $");
28129198Scognet
29129198Scognet#include <sys/param.h>
30129198Scognet#include <sys/kernel.h>
31129198Scognet#include <sys/systm.h>
32129198Scognet#include <sys/exec.h>
33129198Scognet#include <sys/imgact.h>
34129198Scognet#include <sys/linker.h>
35129198Scognet#include <sys/sysent.h>
36129198Scognet#include <sys/imgact_elf.h>
37208453Skib#include <sys/proc.h>
38129198Scognet#include <sys/syscall.h>
39129198Scognet#include <sys/signalvar.h>
40129198Scognet#include <sys/vnode.h>
41129198Scognet
42129198Scognet#include <vm/vm.h>
43129198Scognet#include <vm/pmap.h>
44129198Scognet#include <vm/vm_param.h>
45129198Scognet
46129198Scognet#include <machine/elf.h>
47129198Scognet#include <machine/md_var.h>
48325837Sjhb#ifdef VFP
49325837Sjhb#include <machine/vfp.h>
50325837Sjhb#endif
51129198Scognet
52270123Simpstatic boolean_t elf32_arm_abi_supported(struct image_params *);
53270123Simp
54325810Sjhbu_long elf_hwcap;
55325810Sjhbu_long elf_hwcap2;
56325810Sjhb
57129198Scognetstruct sysentvec elf32_freebsd_sysvec = {
58183322Skib	.sv_size	= SYS_MAXSYSCALL,
59183322Skib	.sv_table	= sysent,
60183322Skib	.sv_mask	= 0,
61183322Skib	.sv_errsize	= 0,
62183322Skib	.sv_errtbl	= NULL,
63183322Skib	.sv_transtrap	= NULL,
64183322Skib	.sv_fixup	= __elfN(freebsd_fixup),
65183322Skib	.sv_sendsig	= sendsig,
66183322Skib	.sv_sigcode	= sigcode,
67183322Skib	.sv_szsigcode	= &szsigcode,
68183322Skib	.sv_name	= "FreeBSD ELF32",
69183322Skib	.sv_coredump	= __elfN(coredump),
70183322Skib	.sv_imgact_try	= NULL,
71183322Skib	.sv_minsigstksz	= MINSIGSTKSZ,
72183322Skib	.sv_pagesize	= PAGE_SIZE,
73183322Skib	.sv_minuser	= VM_MIN_ADDRESS,
74183322Skib	.sv_maxuser	= VM_MAXUSER_ADDRESS,
75183322Skib	.sv_usrstack	= USRSTACK,
76183322Skib	.sv_psstrings	= PS_STRINGS,
77183322Skib	.sv_stackprot	= VM_PROT_ALL,
78183322Skib	.sv_copyout_strings = exec_copyout_strings,
79183322Skib	.sv_setregs	= exec_setregs,
80183322Skib	.sv_fixlimit	= NULL,
81185169Skib	.sv_maxssiz	= NULL,
82291937Skib	.sv_flags	=
83291937Skib#if __ARM_ARCH >= 6
84291937Skib			  SV_SHP | SV_TIMEKEEP |
85291937Skib#endif
86325810Sjhb			  SV_ABI_FREEBSD | SV_ILP32 | SV_HWCAP,
87208453Skib	.sv_set_syscall_retval = cpu_set_syscall_retval,
88225973Skib	.sv_fetch_syscall_args = cpu_fetch_syscall_args,
89208453Skib	.sv_syscallnames = syscallnames,
90291937Skib	.sv_shared_page_base = SHAREDPAGE,
91291937Skib	.sv_shared_page_len = PAGE_SIZE,
92219405Sdchagin	.sv_schedtail	= NULL,
93283382Sdchagin	.sv_thread_detach = NULL,
94293613Sdchagin	.sv_trap	= NULL,
95325810Sjhb	.sv_hwcap	= &elf_hwcap,
96325810Sjhb	.sv_hwcap2	= &elf_hwcap2,
97129198Scognet};
98291937SkibINIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec);
99129198Scognet
100129198Scognetstatic Elf32_Brandinfo freebsd_brand_info = {
101183322Skib	.brand		= ELFOSABI_FREEBSD,
102183322Skib	.machine	= EM_ARM,
103183322Skib	.compat_3_brand	= "FreeBSD",
104183322Skib	.emul_path	= NULL,
105183322Skib	.interp_path	= "/libexec/ld-elf.so.1",
106183322Skib	.sysvec		= &elf32_freebsd_sysvec,
107183322Skib	.interp_newpath	= NULL,
108189771Sdchagin	.brand_note	= &elf32_freebsd_brandnote,
109270123Simp	.flags		= BI_CAN_EXEC_DYN | BI_BRAND_NOTE,
110270123Simp	.header_supported= elf32_arm_abi_supported,
111183322Skib};
112129198Scognet
113197729SbzSYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_FIRST,
114129198Scognet	(sysinit_cfunc_t) elf32_insert_brand_entry,
115129198Scognet	&freebsd_brand_info);
116129198Scognet
117270123Simpstatic boolean_t
118270123Simpelf32_arm_abi_supported(struct image_params *imgp)
119270123Simp{
120270123Simp	const Elf_Ehdr *hdr = (const Elf_Ehdr *)imgp->image_header;
121133464Smarcel
122270123Simp	/*
123270123Simp	 * When configured for EABI, FreeBSD supports EABI vesions 4 and 5.
124270123Simp	 */
125270123Simp	if (EF_ARM_EABI_VERSION(hdr->e_flags) < EF_ARM_EABI_FREEBSD_MIN) {
126270123Simp		if (bootverbose)
127270123Simp			uprintf("Attempting to execute non EABI binary (rev %d) image %s",
128270123Simp			    EF_ARM_EABI_VERSION(hdr->e_flags), imgp->args->fname);
129270123Simp		return (FALSE);
130270123Simp	}
131270123Simp	return (TRUE);
132270123Simp}
133270123Simp
134133464Smarcelvoid
135325837Sjhbelf32_dump_thread(struct thread *td, void *dst, size_t *off)
136133464Smarcel{
137325837Sjhb#ifdef VFP
138325837Sjhb	mcontext_vfp_t vfp;
139325837Sjhb
140325837Sjhb	if (dst != NULL) {
141325837Sjhb		get_vfpcontext(td, &vfp);
142325837Sjhb		*off = elf32_populate_note(NT_ARM_VFP, &vfp, dst, sizeof(vfp),
143325837Sjhb		    NULL);
144325837Sjhb	} else
145325837Sjhb		*off = elf32_populate_note(NT_ARM_VFP, NULL, NULL, sizeof(vfp),
146325837Sjhb		    NULL);
147325837Sjhb#endif
148133464Smarcel}
149133464Smarcel
150338867Smarkjbool
151338867Smarkjelf_is_ifunc_reloc(Elf_Size r_info __unused)
152338867Smarkj{
153338867Smarkj
154338867Smarkj	return (false);
155338867Smarkj}
156338867Smarkj
157269767Simp/*
158269767Simp * It is possible for the compiler to emit relocations for unaligned data.
159269767Simp * We handle this situation with these inlines.
160269767Simp */
161269767Simp#define	RELOC_ALIGNED_P(x) \
162269767Simp	(((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
163133464Smarcel
164269767Simpstatic __inline Elf_Addr
165269767Simpload_ptr(Elf_Addr *where)
166269767Simp{
167269767Simp	Elf_Addr res;
168269767Simp
169269767Simp	if (RELOC_ALIGNED_P(where))
170269767Simp		return *where;
171269767Simp	memcpy(&res, where, sizeof(res));
172269767Simp	return (res);
173269767Simp}
174269767Simp
175269767Simpstatic __inline void
176269767Simpstore_ptr(Elf_Addr *where, Elf_Addr val)
177269767Simp{
178269767Simp	if (RELOC_ALIGNED_P(where))
179269767Simp		*where = val;
180269767Simp	else
181269767Simp		memcpy(where, &val, sizeof(val));
182269767Simp}
183269767Simp#undef RELOC_ALIGNED_P
184269767Simp
185269767Simp
186129198Scognet/* Process one elf relocation with addend. */
187129198Scognetstatic int
188129282Speterelf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
189129282Speter    int type, int local, elf_lookup_fn lookup)
190129198Scognet{
191129198Scognet	Elf_Addr *where;
192129198Scognet	Elf_Addr addr;
193129198Scognet	Elf_Addr addend;
194129198Scognet	Elf_Word rtype, symidx;
195129198Scognet	const Elf_Rel *rel;
196129198Scognet	const Elf_Rela *rela;
197288000Skib	int error;
198129198Scognet
199129198Scognet	switch (type) {
200129198Scognet	case ELF_RELOC_REL:
201129198Scognet		rel = (const Elf_Rel *)data;
202129198Scognet		where = (Elf_Addr *) (relocbase + rel->r_offset);
203269767Simp		addend = load_ptr(where);
204129198Scognet		rtype = ELF_R_TYPE(rel->r_info);
205129198Scognet		symidx = ELF_R_SYM(rel->r_info);
206129198Scognet		break;
207129198Scognet	case ELF_RELOC_RELA:
208129198Scognet		rela = (const Elf_Rela *)data;
209129198Scognet		where = (Elf_Addr *) (relocbase + rela->r_offset);
210129198Scognet		addend = rela->r_addend;
211129198Scognet		rtype = ELF_R_TYPE(rela->r_info);
212129198Scognet		symidx = ELF_R_SYM(rela->r_info);
213129198Scognet		break;
214129198Scognet	default:
215129198Scognet		panic("unknown reloc type %d\n", type);
216129198Scognet	}
217129198Scognet
218129198Scognet	if (local) {
219129198Scognet		if (rtype == R_ARM_RELATIVE) {	/* A + B */
220194784Sjeff			addr = elf_relocaddr(lf, relocbase + addend);
221269767Simp			if (load_ptr(where) != addr)
222269767Simp				store_ptr(where, addr);
223129198Scognet		}
224129198Scognet		return (0);
225129198Scognet	}
226129198Scognet
227129198Scognet	switch (rtype) {
228129198Scognet
229129198Scognet		case R_ARM_NONE:	/* none */
230129198Scognet			break;
231129198Scognet
232137211Scognet		case R_ARM_ABS32:
233288000Skib			error = lookup(lf, symidx, 1, &addr);
234288000Skib			if (error != 0)
235129198Scognet				return -1;
236269767Simp			store_ptr(where, addr + load_ptr(where));
237129198Scognet			break;
238129198Scognet
239129198Scognet		case R_ARM_COPY:	/* none */
240129198Scognet			/*
241129198Scognet			 * There shouldn't be copy relocations in kernel
242129198Scognet			 * objects.
243129198Scognet			 */
244129198Scognet			printf("kldload: unexpected R_COPY relocation\n");
245129198Scognet			return -1;
246129198Scognet			break;
247129198Scognet
248137211Scognet		case R_ARM_JUMP_SLOT:
249288000Skib			error = lookup(lf, symidx, 1, &addr);
250288000Skib			if (error == 0) {
251269767Simp				store_ptr(where, addr);
252137211Scognet				return (0);
253137211Scognet			}
254137211Scognet			return (-1);
255129198Scognet		case R_ARM_RELATIVE:
256129198Scognet			break;
257129198Scognet
258129198Scognet		default:
259129198Scognet			printf("kldload: unexpected relocation type %d\n",
260129198Scognet			       rtype);
261129198Scognet			return -1;
262129198Scognet	}
263129198Scognet	return(0);
264129198Scognet}
265129198Scognet
266129198Scognetint
267129282Speterelf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type,
268129282Speter    elf_lookup_fn lookup)
269129198Scognet{
270129198Scognet
271129282Speter	return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup));
272129198Scognet}
273129198Scognet
274129198Scognetint
275129282Speterelf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data,
276129282Speter    int type, elf_lookup_fn lookup)
277129198Scognet{
278129198Scognet
279129282Speter	return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup));
280129198Scognet}
281129198Scognet
282129198Scognetint
283294727Sskraelf_cpu_load_file(linker_file_t lf)
284129198Scognet{
285129198Scognet
286264994Sian	/*
287264994Sian	 * The pmap code does not do an icache sync upon establishing executable
288264994Sian	 * mappings in the kernel pmap.  It's an optimization based on the fact
289264994Sian	 * that kernel memory allocations always have EXECUTABLE protection even
290264994Sian	 * when the memory isn't going to hold executable code.  The only time
291264994Sian	 * kernel memory holding instructions does need a sync is after loading
292294727Sskra	 * a kernel module, and that's when this function gets called.
293294727Sskra	 *
294294727Sskra	 * This syncs data and instruction caches after loading a module.  We
295294727Sskra	 * don't worry about the kernel itself (lf->id is 1) as locore.S did
296294727Sskra	 * that on entry.  Even if data cache maintenance was done by IO code,
297294727Sskra	 * the relocation fixup process creates dirty cache entries that we must
298294727Sskra	 * write back before doing icache sync. The instruction cache sync also
299294727Sskra	 * invalidates the branch predictor cache on platforms that have one.
300264994Sian	 */
301294727Sskra	if (lf->id == 1)
302294727Sskra		return (0);
303294727Sskra#if __ARM_ARCH >= 6
304294727Sskra	dcache_wb_pou((vm_offset_t)lf->address, (vm_size_t)lf->size);
305294727Sskra	icache_inv_all();
306294727Sskra#else
307294727Sskra	cpu_dcache_wb_range((vm_offset_t)lf->address, (vm_size_t)lf->size);
308294727Sskra	cpu_l2cache_wb_range((vm_offset_t)lf->address, (vm_size_t)lf->size);
309295207Smmel	cpu_icache_sync_range((vm_offset_t)lf->address, (vm_size_t)lf->size);
310294727Sskra#endif
311129198Scognet	return (0);
312129198Scognet}
313129198Scognet
314129198Scognetint
315129198Scognetelf_cpu_unload_file(linker_file_t lf __unused)
316129198Scognet{
317129198Scognet
318129198Scognet	return (0);
319129198Scognet}
320