1/*-
2 * Copyright (c) 2001 Benno Rice <benno@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#define __ELF_WORD_SIZE 64
31
32#include <sys/param.h>
33#include <sys/endian.h>
34#include <sys/linker.h>
35
36#include <machine/metadata.h>
37#include <machine/elf.h>
38
39#include <stand.h>
40
41#include "bootstrap.h"
42#include "host_syscall.h"
43
44extern char		end[];
45extern void		*kerneltramp;
46extern size_t		szkerneltramp;
47
48struct trampoline_data {
49	uint32_t	kernel_entry;
50	uint32_t	dtb;
51	uint32_t	phys_mem_offset;
52	uint32_t	of_entry;
53	uint32_t	mdp;
54	uint32_t	mdp_size;
55};
56
57vm_offset_t md_load64(char *args, vm_offset_t *modulep, vm_offset_t *dtb);
58
59int
60ppc64_elf_loadfile(char *filename, uint64_t dest,
61    struct preloaded_file **result)
62{
63	int	r;
64
65	r = __elfN(loadfile)(filename, dest, result);
66	if (r != 0)
67		return (r);
68
69	return (0);
70}
71
72int
73ppc64_elf_exec(struct preloaded_file *fp)
74{
75	struct file_metadata	*fmp;
76	vm_offset_t		mdp, dtb;
77	Elf_Ehdr		*e;
78	int			error;
79	uint32_t		*trampoline;
80	uint64_t		entry;
81	uint64_t		trampolinebase;
82	struct trampoline_data	*trampoline_data;
83	int			nseg;
84	void			*kseg;
85
86	if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) {
87		return(EFTYPE);
88	}
89	e = (Elf_Ehdr *)&fmp->md_data;
90
91	/*
92	 * Figure out where to put it.
93	 *
94	 * Linux does not allow to do kexec_load into
95	 * any part of memory. Ask arch_loadaddr to
96	 * resolve the first available chunk of physical
97	 * memory where loading is possible (load_addr).
98	 *
99	 * Memory organization is shown below.
100	 * It is assumed, that text segment offset of
101	 * kernel ELF (KERNPHYSADDR) is non-zero,
102	 * which is true for PPC/PPC64 architectures,
103	 * where default is 0x100000.
104	 *
105	 * load_addr:                 trampoline code
106	 * load_addr + KERNPHYSADDR:  kernel text segment
107	 */
108	trampolinebase = archsw.arch_loadaddr(LOAD_RAW, NULL, 0);
109	printf("Load address at %#jx\n", (uintmax_t)trampolinebase);
110	printf("Relocation offset is %#jx\n", (uintmax_t)elf64_relocation_offset);
111
112	/* Set up loader trampoline */
113	trampoline = malloc(szkerneltramp);
114	memcpy(trampoline, &kerneltramp, szkerneltramp);
115
116	/* Parse function descriptor for ELFv1 kernels */
117	if ((e->e_flags & 3) == 2)
118		entry = e->e_entry;
119	else {
120		archsw.arch_copyout(e->e_entry + elf64_relocation_offset,
121		    &entry, 8);
122		entry = be64toh(entry);
123	}
124
125	/*
126	 * Placeholder for trampoline data is at trampolinebase + 0x08
127	 * CAUTION: all data must be Big Endian
128	 */
129	trampoline_data = (void*)&trampoline[2];
130	trampoline_data->kernel_entry = htobe32(entry + elf64_relocation_offset);
131	trampoline_data->phys_mem_offset = htobe32(0);
132	trampoline_data->of_entry = htobe32(0);
133
134	if ((error = md_load64(fp->f_args, &mdp, &dtb)) != 0)
135		return (error);
136
137	trampoline_data->dtb = htobe32(dtb);
138	trampoline_data->mdp = htobe32(mdp);
139	trampoline_data->mdp_size = htobe32(0xfb5d104d);
140
141	printf("Kernel entry at %#jx (%#x) ...\n",
142	    entry, be32toh(trampoline_data->kernel_entry));
143	printf("DTB at %#x, mdp at %#x\n",
144	    be32toh(trampoline_data->dtb), be32toh(trampoline_data->mdp));
145
146	dev_cleanup();
147
148	archsw.arch_copyin(trampoline, trampolinebase, szkerneltramp);
149	free(trampoline);
150
151	if (archsw.arch_kexec_kseg_get == NULL)
152		panic("architecture did not provide kexec segment mapping");
153	archsw.arch_kexec_kseg_get(&nseg, &kseg);
154
155	error = kexec_load(trampolinebase, nseg, (uintptr_t)kseg);
156	if (error != 0)
157		panic("kexec_load returned error: %d", error);
158
159	error = host_reboot(0xfee1dead, 672274793,
160	    0x45584543 /* LINUX_REBOOT_CMD_KEXEC */, (uintptr_t)NULL);
161	if (error != 0)
162		panic("reboot returned error: %d", error);
163
164	while (1) {}
165}
166
167struct file_format	ppc_elf64 =
168{
169	ppc64_elf_loadfile,
170	ppc64_elf_exec
171};
172