xref: /f-stack/freebsd/mips/mips/elf_trampoline.c (revision 22ce4aff)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2005 Olivier Houchard.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 #include <machine/asm.h>
30 #include <sys/param.h>
31 
32 #if ELFSIZE == 64
33 #include <sys/elf64.h>
34 #else
35 #include <sys/elf32.h>
36 #endif
37 
38 /*
39  * Since we are compiled outside of the normal kernel build process, we
40  * need to include opt_global.h manually.
41  */
42 #include "opt_global.h"
43 
44 #include <machine/elf.h>
45 #include <machine/cpufunc.h>
46 #include <machine/stdarg.h>
47 
48 #ifndef KERNNAME
49 #error Kernel name not provided
50 #endif
51 
52 extern char kernel_start[];
53 extern char kernel_end[];
54 
55 static __inline void *
memcpy(void * dst,const void * src,size_t len)56 memcpy(void *dst, const void *src, size_t len)
57 {
58 	const char *s = src;
59     	char *d = dst;
60 
61 	while (len) {
62 		if (0 && len >= 4 && !((vm_offset_t)d & 3) &&
63 		    !((vm_offset_t)s & 3)) {
64 			*(uint32_t *)d = *(uint32_t *)s;
65 			s += 4;
66 			d += 4;
67 			len -= 4;
68 		} else {
69 			*d++ = *s++;
70 			len--;
71 		}
72 	}
73 	return (dst);
74 }
75 
76 static __inline void
bzero(void * addr,size_t count)77 bzero(void *addr, size_t count)
78 {
79 	char *tmp = (char *)addr;
80 
81 	while (count > 0) {
82 		if (count >= 4 && !((vm_offset_t)tmp & 3)) {
83 			*(uint32_t *)tmp = 0;
84 			tmp += 4;
85 			count -= 4;
86 		} else {
87 			*tmp = 0;
88 			tmp++;
89 			count--;
90 		}
91 	}
92 }
93 
94 /*
95  * Convert number to pointer, truncate on 64->32 case, sign extend
96  * in 32->64 case
97  */
98 #define	mkptr(x)	((void *)(intptr_t)(int)(x))
99 
100 /*
101  * Relocate PT_LOAD segements of kernel ELF image to their respective
102  * virtual addresses and return entry point
103  */
104 void *
load_kernel(void * kstart)105 load_kernel(void * kstart)
106 {
107 #if ELFSIZE == 64
108 	Elf64_Ehdr *eh;
109 	Elf64_Phdr phdr[64] /* XXX */;
110 	Elf64_Shdr shdr[64] /* XXX */;
111 #else
112 	Elf32_Ehdr *eh;
113 	Elf32_Phdr phdr[64] /* XXX */;
114 	Elf32_Shdr shdr[64] /* XXX */;
115 #endif
116 	int i, j;
117 	void *entry_point;
118 	vm_offset_t loadend = 0;
119 	intptr_t lastaddr;
120 	int symtabindex = -1;
121 	int symstrindex = -1;
122 	Elf_Size tmp;
123 
124 #if ELFSIZE == 64
125 	eh = (Elf64_Ehdr *)kstart;
126 #else
127 	eh = (Elf32_Ehdr *)kstart;
128 #endif
129 	entry_point = mkptr(eh->e_entry);
130 	memcpy(phdr, (void *)(kstart + eh->e_phoff),
131 	    eh->e_phnum * sizeof(phdr[0]));
132 
133 	memcpy(shdr, (void *)(kstart + eh->e_shoff),
134 	    sizeof(*shdr) * eh->e_shnum);
135 
136 	if (eh->e_shnum * eh->e_shentsize != 0 && eh->e_shoff != 0) {
137 		for (i = 0; i < eh->e_shnum; i++) {
138 			if (shdr[i].sh_type == SHT_SYMTAB) {
139 				/*
140 				 * XXX: check if .symtab is in PT_LOAD?
141 				 */
142 				if (shdr[i].sh_offset != 0 &&
143 				    shdr[i].sh_size != 0) {
144 					symtabindex = i;
145 					symstrindex = shdr[i].sh_link;
146 				}
147 			}
148 		}
149 	}
150 
151 	/*
152 	 * Copy loadable segments
153 	 */
154 	for (i = 0; i < eh->e_phnum; i++) {
155 		volatile char c;
156 
157 		if (phdr[i].p_type != PT_LOAD)
158 			continue;
159 
160 		memcpy(mkptr(phdr[i].p_vaddr),
161 		    (void*)(kstart + phdr[i].p_offset), phdr[i].p_filesz);
162 
163 		/* Clean space from oversized segments, eg: bss. */
164 		if (phdr[i].p_filesz < phdr[i].p_memsz)
165 			bzero(mkptr(phdr[i].p_vaddr + phdr[i].p_filesz),
166 			    phdr[i].p_memsz - phdr[i].p_filesz);
167 
168 		if (loadend < phdr[i].p_vaddr + phdr[i].p_memsz)
169 			loadend = phdr[i].p_vaddr + phdr[i].p_memsz;
170 	}
171 
172 	/* Now grab the symbol tables. */
173 	lastaddr = (intptr_t)(int)loadend;
174 	if (symtabindex >= 0 && symstrindex >= 0) {
175 		tmp = SYMTAB_MAGIC;
176 		memcpy((void *)lastaddr, &tmp, sizeof(tmp));
177 		lastaddr += sizeof(Elf_Size);
178 		tmp = shdr[symtabindex].sh_size +
179 		    shdr[symstrindex].sh_size + 2*sizeof(Elf_Size);
180 		memcpy((void *)lastaddr, &tmp, sizeof(tmp));
181 		lastaddr += sizeof(Elf_Size);
182 		/* .symtab size */
183 		tmp = shdr[symtabindex].sh_size;
184 		memcpy((void *)lastaddr, &tmp, sizeof(tmp));
185 		lastaddr += sizeof(shdr[symtabindex].sh_size);
186 		/* .symtab data */
187 		memcpy((void*)lastaddr,
188 		    shdr[symtabindex].sh_offset + kstart,
189 		    shdr[symtabindex].sh_size);
190 		lastaddr += shdr[symtabindex].sh_size;
191 
192 		/* .strtab size */
193 		tmp = shdr[symstrindex].sh_size;
194 		memcpy((void *)lastaddr, &tmp, sizeof(tmp));
195 		lastaddr += sizeof(shdr[symstrindex].sh_size);
196 
197 		/* .strtab data */
198 		memcpy((void*)lastaddr,
199 		    shdr[symstrindex].sh_offset + kstart,
200 		    shdr[symstrindex].sh_size);
201 	} else {
202 		/* Do not take any chances */
203 		tmp = 0;
204 		memcpy((void *)lastaddr, &tmp, sizeof(tmp));
205 	}
206 
207 	return entry_point;
208 }
209 
210 void
_startC(register_t a0,register_t a1,register_t a2,register_t a3)211 _startC(register_t a0, register_t a1, register_t a2, register_t a3)
212 {
213 	unsigned int * code;
214 	int i;
215 	void (*entry_point)(register_t, register_t, register_t, register_t);
216 
217 	/*
218 	 * Relocate segment to the predefined memory location
219 	 * Most likely it will be KSEG0/KSEG1 address
220 	 */
221 	entry_point = load_kernel(kernel_start);
222 
223 	/* Pass saved registers to original _start */
224 	entry_point(a0, a1, a2, a3);
225 }
226