1187418Sgonzo/*-
2187418Sgonzo * Copyright (c) 2005 Olivier Houchard.  All rights reserved.
3187418Sgonzo *
4187418Sgonzo * Redistribution and use in source and binary forms, with or without
5187418Sgonzo * modification, are permitted provided that the following conditions
6187418Sgonzo * are met:
7187418Sgonzo * 1. Redistributions of source code must retain the above copyright
8187418Sgonzo *    notice, this list of conditions and the following disclaimer.
9187418Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
10187418Sgonzo *    notice, this list of conditions and the following disclaimer in the
11187418Sgonzo *    documentation and/or other materials provided with the distribution.
12187418Sgonzo *
13187418Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14187418Sgonzo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15187418Sgonzo * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16187418Sgonzo * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17187418Sgonzo * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18187418Sgonzo * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19187418Sgonzo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20187418Sgonzo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21187418Sgonzo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22187418Sgonzo * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23187418Sgonzo */
24187418Sgonzo
25187418Sgonzo#include <sys/cdefs.h>
26187418Sgonzo__FBSDID("$FreeBSD$");
27187418Sgonzo#include <machine/asm.h>
28187418Sgonzo#include <sys/param.h>
29197013Simp
30224105Sjchandra#if ELFSIZE == 64
31197014Simp#include <sys/elf64.h>
32197014Simp#else
33187418Sgonzo#include <sys/elf32.h>
34197014Simp#endif
35187418Sgonzo
36187418Sgonzo/*
37187418Sgonzo * Since we are compiled outside of the normal kernel build process, we
38187418Sgonzo * need to include opt_global.h manually.
39187418Sgonzo */
40187418Sgonzo#include "opt_global.h"
41187418Sgonzo
42256171Sadrian#include <sys/inflate.h>
43256171Sadrian#include <machine/elf.h>
44256171Sadrian#include <machine/cpufunc.h>
45256171Sadrian#include <machine/stdarg.h>
46256171Sadrian
47216474Sjchandra#ifndef KERNNAME
48216474Sjchandra#error Kernel name not provided
49216474Sjchandra#endif
50216474Sjchandra
51187418Sgonzoextern char kernel_start[];
52187418Sgonzoextern char kernel_end[];
53187418Sgonzo
54187418Sgonzostatic __inline void *
55197013Simpmemcpy(void *dst, const void *src, size_t len)
56187418Sgonzo{
57187418Sgonzo	const char *s = src;
58187418Sgonzo    	char *d = dst;
59187418Sgonzo
60187418Sgonzo	while (len) {
61187418Sgonzo		if (0 && len >= 4 && !((vm_offset_t)d & 3) &&
62187418Sgonzo		    !((vm_offset_t)s & 3)) {
63187418Sgonzo			*(uint32_t *)d = *(uint32_t *)s;
64187418Sgonzo			s += 4;
65187418Sgonzo			d += 4;
66187418Sgonzo			len -= 4;
67187418Sgonzo		} else {
68187418Sgonzo			*d++ = *s++;
69187418Sgonzo			len--;
70187418Sgonzo		}
71187418Sgonzo	}
72187418Sgonzo	return (dst);
73187418Sgonzo}
74187418Sgonzo
75187418Sgonzostatic __inline void
76197013Simpbzero(void *addr, size_t count)
77187418Sgonzo{
78187418Sgonzo	char *tmp = (char *)addr;
79187418Sgonzo
80187418Sgonzo	while (count > 0) {
81187418Sgonzo		if (count >= 4 && !((vm_offset_t)tmp & 3)) {
82187418Sgonzo			*(uint32_t *)tmp = 0;
83187418Sgonzo			tmp += 4;
84187418Sgonzo			count -= 4;
85187418Sgonzo		} else {
86187418Sgonzo			*tmp = 0;
87187418Sgonzo			tmp++;
88187418Sgonzo			count--;
89187418Sgonzo		}
90187418Sgonzo	}
91187418Sgonzo}
92187418Sgonzo
93187418Sgonzo/*
94224105Sjchandra * Convert number to pointer, truncate on 64->32 case, sign extend
95224105Sjchandra * in 32->64 case
96224105Sjchandra */
97224105Sjchandra#define	mkptr(x)	((void *)(intptr_t)(int)(x))
98224105Sjchandra
99224105Sjchandra/*
100187418Sgonzo * Relocate PT_LOAD segements of kernel ELF image to their respective
101187418Sgonzo * virtual addresses and return entry point
102187418Sgonzo */
103187418Sgonzovoid *
104187418Sgonzoload_kernel(void * kstart)
105187418Sgonzo{
106224105Sjchandra#if ELFSIZE == 64
107197014Simp	Elf64_Ehdr *eh;
108197014Simp	Elf64_Phdr phdr[64] /* XXX */;
109204052Simp	Elf64_Shdr shdr[64] /* XXX */;
110197014Simp#else
111187418Sgonzo	Elf32_Ehdr *eh;
112187418Sgonzo	Elf32_Phdr phdr[64] /* XXX */;
113202908Sgonzo	Elf32_Shdr shdr[64] /* XXX */;
114197014Simp#endif
115202908Sgonzo	int i, j;
116187418Sgonzo	void *entry_point;
117224105Sjchandra	vm_offset_t loadend = 0;
118224105Sjchandra	intptr_t lastaddr;
119202908Sgonzo	int symtabindex = -1;
120202908Sgonzo	int symstrindex = -1;
121224105Sjchandra	Elf_Size tmp;
122187418Sgonzo
123224105Sjchandra#if ELFSIZE == 64
124197014Simp	eh = (Elf64_Ehdr *)kstart;
125197014Simp#else
126187418Sgonzo	eh = (Elf32_Ehdr *)kstart;
127197014Simp#endif
128224105Sjchandra	entry_point = mkptr(eh->e_entry);
129224105Sjchandra	memcpy(phdr, (void *)(kstart + eh->e_phoff),
130187418Sgonzo	    eh->e_phnum * sizeof(phdr[0]));
131187418Sgonzo
132202908Sgonzo	memcpy(shdr, (void *)(kstart + eh->e_shoff),
133202908Sgonzo	    sizeof(*shdr) * eh->e_shnum);
134202908Sgonzo
135202908Sgonzo	if (eh->e_shnum * eh->e_shentsize != 0 && eh->e_shoff != 0) {
136202908Sgonzo		for (i = 0; i < eh->e_shnum; i++) {
137202908Sgonzo			if (shdr[i].sh_type == SHT_SYMTAB) {
138202908Sgonzo				/*
139202908Sgonzo				 * XXX: check if .symtab is in PT_LOAD?
140202908Sgonzo				 */
141202908Sgonzo				if (shdr[i].sh_offset != 0 &&
142202908Sgonzo				    shdr[i].sh_size != 0) {
143202908Sgonzo					symtabindex = i;
144202908Sgonzo					symstrindex = shdr[i].sh_link;
145202908Sgonzo				}
146202908Sgonzo			}
147202908Sgonzo		}
148202908Sgonzo	}
149202908Sgonzo
150202908Sgonzo	/*
151202908Sgonzo	 * Copy loadable segments
152202908Sgonzo	 */
153187418Sgonzo	for (i = 0; i < eh->e_phnum; i++) {
154187418Sgonzo		volatile char c;
155187418Sgonzo
156187418Sgonzo		if (phdr[i].p_type != PT_LOAD)
157187418Sgonzo			continue;
158187418Sgonzo
159224105Sjchandra		memcpy(mkptr(phdr[i].p_vaddr),
160187418Sgonzo		    (void*)(kstart + phdr[i].p_offset), phdr[i].p_filesz);
161202908Sgonzo
162187418Sgonzo		/* Clean space from oversized segments, eg: bss. */
163187418Sgonzo		if (phdr[i].p_filesz < phdr[i].p_memsz)
164224105Sjchandra			bzero(mkptr(phdr[i].p_vaddr + phdr[i].p_filesz),
165187418Sgonzo			    phdr[i].p_memsz - phdr[i].p_filesz);
166202908Sgonzo
167224105Sjchandra		if (loadend < phdr[i].p_vaddr + phdr[i].p_memsz)
168224105Sjchandra			loadend = phdr[i].p_vaddr + phdr[i].p_memsz;
169187418Sgonzo	}
170187418Sgonzo
171202908Sgonzo	/* Now grab the symbol tables. */
172224105Sjchandra	lastaddr = (intptr_t)(int)loadend;
173202908Sgonzo	if (symtabindex >= 0 && symstrindex >= 0) {
174224105Sjchandra		tmp = SYMTAB_MAGIC;
175224105Sjchandra		memcpy((void *)lastaddr, &tmp, sizeof(tmp));
176202908Sgonzo		lastaddr += sizeof(Elf_Size);
177224105Sjchandra		tmp = shdr[symtabindex].sh_size +
178202908Sgonzo		    shdr[symstrindex].sh_size + 2*sizeof(Elf_Size);
179224105Sjchandra		memcpy((void *)lastaddr, &tmp, sizeof(tmp));
180202908Sgonzo		lastaddr += sizeof(Elf_Size);
181202908Sgonzo		/* .symtab size */
182224105Sjchandra		tmp = shdr[symtabindex].sh_size;
183224105Sjchandra		memcpy((void *)lastaddr, &tmp, sizeof(tmp));
184202908Sgonzo		lastaddr += sizeof(shdr[symtabindex].sh_size);
185202908Sgonzo		/* .symtab data */
186202908Sgonzo		memcpy((void*)lastaddr,
187202908Sgonzo		    shdr[symtabindex].sh_offset + kstart,
188202908Sgonzo		    shdr[symtabindex].sh_size);
189202908Sgonzo		lastaddr += shdr[symtabindex].sh_size;
190202908Sgonzo
191202908Sgonzo		/* .strtab size */
192224105Sjchandra		tmp = shdr[symstrindex].sh_size;
193224105Sjchandra		memcpy((void *)lastaddr, &tmp, sizeof(tmp));
194202908Sgonzo		lastaddr += sizeof(shdr[symstrindex].sh_size);
195202908Sgonzo
196202908Sgonzo		/* .strtab data */
197202908Sgonzo		memcpy((void*)lastaddr,
198202908Sgonzo		    shdr[symstrindex].sh_offset + kstart,
199202908Sgonzo		    shdr[symstrindex].sh_size);
200224105Sjchandra	} else {
201202908Sgonzo		/* Do not take any chances */
202224105Sjchandra		tmp = 0;
203224105Sjchandra		memcpy((void *)lastaddr, &tmp, sizeof(tmp));
204224105Sjchandra	}
205202908Sgonzo
206187418Sgonzo	return entry_point;
207187418Sgonzo}
208187418Sgonzo
209187418Sgonzovoid
210187418Sgonzo_startC(register_t a0, register_t a1, register_t a2, register_t a3)
211187418Sgonzo{
212187418Sgonzo	unsigned int * code;
213187418Sgonzo	int i;
214187418Sgonzo	void (*entry_point)(register_t, register_t, register_t, register_t);
215187418Sgonzo
216187418Sgonzo	/*
217187418Sgonzo	 * Relocate segment to the predefined memory location
218187418Sgonzo	 * Most likely it will be KSEG0/KSEG1 address
219187418Sgonzo	 */
220187418Sgonzo	entry_point = load_kernel(kernel_start);
221187418Sgonzo
222187418Sgonzo	/* Pass saved registers to original _start */
223187418Sgonzo	entry_point(a0, a1, a2, a3);
224187418Sgonzo}
225