1178172Simp/*-
2178172Simp * Copyright 1996-1998 John D. Polstra.
3178172Simp * All rights reserved.
4178172Simp *
5178172Simp * Redistribution and use in source and binary forms, with or without
6178172Simp * modification, are permitted provided that the following conditions
7178172Simp * are met:
8178172Simp * 1. Redistributions of source code must retain the above copyright
9178172Simp *    notice, this list of conditions and the following disclaimer.
10178172Simp * 2. Redistributions in binary form must reproduce the above copyright
11178172Simp *    notice, this list of conditions and the following disclaimer in the
12178172Simp *    documentation and/or other materials provided with the distribution.
13178172Simp *
14178172Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15178172Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16178172Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17178172Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18178172Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19178172Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20178172Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21178172Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22178172Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23178172Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24178172Simp *
25178172Simp *	from: src/sys/i386/i386/elf_machdep.c,v 1.20 2004/08/11 02:35:05 marcel
26178172Simp */
27178172Simp
28178172Simp#include <sys/cdefs.h>
29178172Simp__FBSDID("$FreeBSD$");
30178172Simp
31178172Simp#include <sys/param.h>
32178172Simp#include <sys/kernel.h>
33178172Simp#include <sys/systm.h>
34178172Simp#include <sys/exec.h>
35178172Simp#include <sys/imgact.h>
36178172Simp#include <sys/linker.h>
37178172Simp#include <sys/sysent.h>
38178172Simp#include <sys/imgact_elf.h>
39208453Skib#include <sys/proc.h>
40178172Simp#include <sys/syscall.h>
41178172Simp#include <sys/signalvar.h>
42178172Simp#include <sys/vnode.h>
43178172Simp
44178172Simp#include <vm/vm.h>
45178172Simp#include <vm/pmap.h>
46178172Simp#include <vm/vm_param.h>
47178172Simp
48178172Simp#include <machine/elf.h>
49178172Simp#include <machine/md_var.h>
50204031Sneel#include <machine/cache.h>
51178172Simp
52202046Simp#ifdef __mips_n64
53202046Simpstruct sysentvec elf64_freebsd_sysvec = {
54202046Simp	.sv_size	= SYS_MAXSYSCALL,
55202046Simp	.sv_table	= sysent,
56202046Simp	.sv_mask	= 0,
57202046Simp	.sv_sigsize	= 0,
58202046Simp	.sv_sigtbl	= NULL,
59202046Simp	.sv_errsize	= 0,
60202046Simp	.sv_errtbl	= NULL,
61202046Simp	.sv_transtrap	= NULL,
62202046Simp	.sv_fixup	= __elfN(freebsd_fixup),
63202046Simp	.sv_sendsig	= sendsig,
64202046Simp	.sv_sigcode	= sigcode,
65202046Simp	.sv_szsigcode	= &szsigcode,
66202046Simp	.sv_prepsyscall	= NULL,
67202046Simp	.sv_name	= "FreeBSD ELF64",
68202046Simp	.sv_coredump	= __elfN(coredump),
69202046Simp	.sv_imgact_try	= NULL,
70202046Simp	.sv_minsigstksz	= MINSIGSTKSZ,
71202046Simp	.sv_pagesize	= PAGE_SIZE,
72202046Simp	.sv_minuser	= VM_MIN_ADDRESS,
73202046Simp	.sv_maxuser	= VM_MAXUSER_ADDRESS,
74202046Simp	.sv_usrstack	= USRSTACK,
75202046Simp	.sv_psstrings	= PS_STRINGS,
76202046Simp	.sv_stackprot	= VM_PROT_ALL,
77202046Simp	.sv_copyout_strings = exec_copyout_strings,
78202046Simp	.sv_setregs	= exec_setregs,
79202046Simp	.sv_fixlimit	= NULL,
80202046Simp	.sv_maxssiz	= NULL,
81208453Skib	.sv_flags	= SV_ABI_FREEBSD | SV_LP64,
82208453Skib	.sv_set_syscall_retval = cpu_set_syscall_retval,
83229374Skib	.sv_fetch_syscall_args = cpu_fetch_syscall_args,
84208453Skib	.sv_syscallnames = syscallnames,
85219405Sdchagin	.sv_schedtail	= NULL,
86202046Simp};
87202046Simp
88202046Simpstatic Elf64_Brandinfo freebsd_brand_info = {
89202046Simp	.brand		= ELFOSABI_FREEBSD,
90202046Simp	.machine	= EM_MIPS,
91202046Simp	.compat_3_brand	= "FreeBSD",
92202046Simp	.emul_path	= NULL,
93202046Simp	.interp_path	= "/libexec/ld-elf.so.1",
94202046Simp	.sysvec		= &elf64_freebsd_sysvec,
95202046Simp	.interp_newpath	= NULL,
96202046Simp	.flags		= 0
97202046Simp};
98202046Simp
99202046SimpSYSINIT(elf64, SI_SUB_EXEC, SI_ORDER_ANY,
100202046Simp    (sysinit_cfunc_t) elf64_insert_brand_entry,
101202046Simp    &freebsd_brand_info);
102202046Simp
103202046Simpvoid
104202046Simpelf64_dump_thread(struct thread *td __unused, void *dst __unused,
105202046Simp    size_t *off __unused)
106202046Simp{
107202046Simp}
108202046Simp#else
109178172Simpstruct sysentvec elf32_freebsd_sysvec = {
110183322Skib	.sv_size	= SYS_MAXSYSCALL,
111183322Skib	.sv_table	= sysent,
112183322Skib	.sv_mask	= 0,
113183322Skib	.sv_sigsize	= 0,
114183322Skib	.sv_sigtbl	= NULL,
115183322Skib	.sv_errsize	= 0,
116183322Skib	.sv_errtbl	= NULL,
117183322Skib	.sv_transtrap	= NULL,
118183322Skib	.sv_fixup	= __elfN(freebsd_fixup),
119183322Skib	.sv_sendsig	= sendsig,
120183322Skib	.sv_sigcode	= sigcode,
121183322Skib	.sv_szsigcode	= &szsigcode,
122183322Skib	.sv_prepsyscall	= NULL,
123183322Skib	.sv_name	= "FreeBSD ELF32",
124183322Skib	.sv_coredump	= __elfN(coredump),
125183322Skib	.sv_imgact_try	= NULL,
126183322Skib	.sv_minsigstksz	= MINSIGSTKSZ,
127183322Skib	.sv_pagesize	= PAGE_SIZE,
128183322Skib	.sv_minuser	= VM_MIN_ADDRESS,
129183322Skib	.sv_maxuser	= VM_MAXUSER_ADDRESS,
130183322Skib	.sv_usrstack	= USRSTACK,
131183322Skib	.sv_psstrings	= PS_STRINGS,
132183322Skib	.sv_stackprot	= VM_PROT_ALL,
133183322Skib	.sv_copyout_strings = exec_copyout_strings,
134183322Skib	.sv_setregs	= exec_setregs,
135183322Skib	.sv_fixlimit	= NULL,
136185169Skib	.sv_maxssiz	= NULL,
137208453Skib	.sv_flags	= SV_ABI_FREEBSD | SV_ILP32,
138208453Skib	.sv_set_syscall_retval = cpu_set_syscall_retval,
139229374Skib	.sv_fetch_syscall_args = cpu_fetch_syscall_args,
140208453Skib	.sv_syscallnames = syscallnames,
141219405Sdchagin	.sv_schedtail	= NULL,
142178172Simp};
143178172Simp
144178172Simpstatic Elf32_Brandinfo freebsd_brand_info = {
145183322Skib	.brand		= ELFOSABI_FREEBSD,
146183322Skib	.machine	= EM_MIPS,
147183322Skib	.compat_3_brand	= "FreeBSD",
148183322Skib	.emul_path	= NULL,
149183322Skib	.interp_path	= "/libexec/ld-elf.so.1",
150183322Skib	.sysvec		= &elf32_freebsd_sysvec,
151183322Skib	.interp_newpath	= NULL,
152202046Simp	.flags		= 0
153183322Skib};
154178172Simp
155197729SbzSYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_FIRST,
156183322Skib    (sysinit_cfunc_t) elf32_insert_brand_entry,
157183322Skib    &freebsd_brand_info);
158178172Simp
159178172Simpvoid
160178172Simpelf32_dump_thread(struct thread *td __unused, void *dst __unused,
161178172Simp    size_t *off __unused)
162178172Simp{
163178172Simp}
164202046Simp#endif
165178172Simp
166178172Simp/* Process one elf relocation with addend. */
167178172Simpstatic int
168178172Simpelf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
169178172Simp    int type, int local, elf_lookup_fn lookup)
170178172Simp{
171208022Simp	Elf_Addr *where = (Elf_Addr *)NULL;
172178172Simp	Elf_Addr addr;
173178172Simp	Elf_Addr addend = (Elf_Addr)0;
174178172Simp	Elf_Word rtype = (Elf_Word)0, symidx;
175178172Simp	const Elf_Rel *rel;
176178172Simp
177204031Sneel	/*
178204031Sneel	 * Stash R_MIPS_HI16 info so we can use it when processing R_MIPS_LO16
179204031Sneel	 */
180204031Sneel	static Elf_Addr ahl;
181204031Sneel	static Elf_Addr *where_hi16;
182204031Sneel
183178172Simp	switch (type) {
184178172Simp	case ELF_RELOC_REL:
185178172Simp		rel = (const Elf_Rel *)data;
186178172Simp		where = (Elf_Addr *) (relocbase + rel->r_offset);
187178172Simp		addend = *where;
188178172Simp		rtype = ELF_R_TYPE(rel->r_info);
189178172Simp		symidx = ELF_R_SYM(rel->r_info);
190178172Simp		break;
191178172Simp	default:
192178172Simp		panic("unknown reloc type %d\n", type);
193178172Simp	}
194178172Simp
195178172Simp	switch (rtype) {
196204031Sneel	case R_MIPS_NONE:	/* none */
197204031Sneel		break;
198178172Simp
199204031Sneel	case R_MIPS_32:		/* S + A */
200204031Sneel		addr = lookup(lf, symidx, 1);
201204031Sneel		if (addr == 0)
202204031Sneel			return (-1);
203204031Sneel		addr += addend;
204204031Sneel		if (*where != addr)
205204031Sneel			*where = addr;
206204031Sneel		break;
207178172Simp
208204031Sneel	case R_MIPS_26:		/* ((A << 2) | (P & 0xf0000000) + S) >> 2 */
209204031Sneel		addr = lookup(lf, symidx, 1);
210204031Sneel		if (addr == 0)
211204031Sneel			return (-1);
212178172Simp
213204031Sneel		addend &= 0x03ffffff;
214204031Sneel		addend <<= 2;
215178172Simp
216204031Sneel		addr += ((Elf_Addr)where & 0xf0000000) | addend;
217204031Sneel		addr >>= 2;
218178172Simp
219204031Sneel		*where &= ~0x03ffffff;
220204031Sneel		*where |= addr & 0x03ffffff;
221204031Sneel		break;
222178172Simp
223204031Sneel	case R_MIPS_HI16:	/* ((AHL + S) - ((short)(AHL + S)) >> 16 */
224204031Sneel		ahl = addend << 16;
225204031Sneel		where_hi16 = where;
226204031Sneel		break;
227178172Simp
228204031Sneel	case R_MIPS_LO16:	/* AHL + S */
229204031Sneel		ahl += (int16_t)addend;
230204031Sneel		addr = lookup(lf, symidx, 1);
231204031Sneel		if (addr == 0)
232204031Sneel			return (-1);
233178172Simp
234204031Sneel		addend &= 0xffff0000;
235204031Sneel		addend |= (uint16_t)(ahl + addr);
236204031Sneel		*where = addend;
237178172Simp
238204031Sneel		addend = *where_hi16;
239204031Sneel		addend &= 0xffff0000;
240204031Sneel		addend |= ((ahl + addr) - (int16_t)(ahl + addr)) >> 16;
241204031Sneel		*where_hi16 = addend;
242204031Sneel		break;
243178172Simp
244204031Sneel	default:
245204031Sneel		printf("kldload: unexpected relocation type %d\n",
246204031Sneel			rtype);
247204031Sneel		return (-1);
248178172Simp	}
249178172Simp	return(0);
250178172Simp}
251178172Simp
252178172Simpint
253178172Simpelf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type,
254178172Simp    elf_lookup_fn lookup)
255178172Simp{
256178172Simp
257178172Simp	return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup));
258178172Simp}
259178172Simp
260178172Simpint
261178172Simpelf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data,
262178172Simp    int type, elf_lookup_fn lookup)
263178172Simp{
264178172Simp
265178172Simp	return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup));
266178172Simp}
267178172Simp
268178172Simpint
269178172Simpelf_cpu_load_file(linker_file_t lf __unused)
270178172Simp{
271178172Simp
272204031Sneel	/*
273204031Sneel	 * Sync the I and D caches to make sure our relocations are visible.
274204031Sneel	 */
275204031Sneel	mips_icache_sync_all();
276204031Sneel
277178172Simp	return (0);
278178172Simp}
279178172Simp
280178172Simpint
281178172Simpelf_cpu_unload_file(linker_file_t lf __unused)
282178172Simp{
283178172Simp
284178172Simp	return (0);
285178172Simp}
286