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#define __ELF_WORD_SIZE 64
28
29#include <sys/param.h>
30#include <sys/endian.h>
31#include <sys/linker.h>
32
33#include <machine/metadata.h>
34#include <machine/elf.h>
35
36#include <stand.h>
37
38#include "bootstrap.h"
39#include "syscall_nr.h"
40#include "host_syscall.h"
41#include "modinfo.h"
42#include "kboot.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
57int
58ppc64_elf_loadfile(char *filename, uint64_t dest,
59    struct preloaded_file **result)
60{
61	int	r;
62
63	r = __elfN(loadfile)(filename, dest, result);
64	if (r != 0)
65		return (r);
66
67	return (0);
68}
69
70int
71ppc64_elf_exec(struct preloaded_file *fp)
72{
73	struct file_metadata	*fmp;
74	vm_offset_t		mdp, dtb;
75	Elf_Ehdr		*e;
76	int			error;
77	uint32_t		*trampoline;
78	uint64_t		entry;
79	uint64_t		trampolinebase;
80	struct trampoline_data	*trampoline_data;
81	int			nseg;
82	void			*kseg;
83
84	if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) {
85		return(EFTYPE);
86	}
87	e = (Elf_Ehdr *)&fmp->md_data;
88
89	/*
90	 * Figure out where to put it.
91	 *
92	 * Linux does not allow to do kexec_load into
93	 * any part of memory. Ask arch_loadaddr to
94	 * resolve the first available chunk of physical
95	 * memory where loading is possible (load_addr).
96	 *
97	 * Memory organization is shown below.
98	 * It is assumed, that text segment offset of
99	 * kernel ELF (KERNPHYSADDR) is non-zero,
100	 * which is true for PPC/PPC64 architectures,
101	 * where default is 0x100000.
102	 *
103	 * load_addr:                 trampoline code
104	 * load_addr + KERNPHYSADDR:  kernel text segment
105	 */
106	trampolinebase = archsw.arch_loadaddr(LOAD_RAW, NULL, 0);
107	printf("Load address at %#jx\n", (uintmax_t)trampolinebase);
108	printf("Relocation offset is %#jx\n", (uintmax_t)elf64_relocation_offset);
109
110	/* Set up loader trampoline */
111	trampoline = malloc(szkerneltramp);
112	memcpy(trampoline, &kerneltramp, szkerneltramp);
113
114	/* Parse function descriptor for ELFv1 kernels */
115	if ((e->e_flags & 3) == 2)
116		entry = e->e_entry;
117	else {
118		archsw.arch_copyout(e->e_entry + elf64_relocation_offset,
119		    &entry, 8);
120		entry = be64toh(entry);
121	}
122
123	/*
124	 * Placeholder for trampoline data is at trampolinebase + 0x08
125	 * CAUTION: all data must be Big Endian
126	 */
127	trampoline_data = (void*)&trampoline[2];
128	trampoline_data->kernel_entry = htobe32(entry + elf64_relocation_offset);
129	trampoline_data->phys_mem_offset = htobe32(0);
130	trampoline_data->of_entry = htobe32(0);
131
132	if ((error = md_load64(fp->f_args, &mdp, &dtb)) != 0)
133		return (error);
134
135	trampoline_data->dtb = htobe32(dtb);
136	trampoline_data->mdp = htobe32(mdp);
137	trampoline_data->mdp_size = htobe32(0xfb5d104d);
138
139	printf("Kernel entry at %#jx (%#x) ...\n",
140	    entry, be32toh(trampoline_data->kernel_entry));
141	printf("DTB at %#x, mdp at %#x\n",
142	    be32toh(trampoline_data->dtb), be32toh(trampoline_data->mdp));
143
144	dev_cleanup();
145
146	archsw.arch_copyin(trampoline, trampolinebase, szkerneltramp);
147	free(trampoline);
148
149	kboot_kseg_get(&nseg, &kseg);
150
151	error = host_kexec_load(trampolinebase, nseg, kseg, HOST_KEXEC_ARCH_PPC64);
152	if (error != 0)
153		panic("kexec_load returned error: %d", error);
154
155	error = host_reboot(HOST_REBOOT_MAGIC1, HOST_REBOOT_MAGIC2, HOST_REBOOT_CMD_KEXEC,
156	    (uintptr_t)NULL);
157	if (error != 0)
158		panic("reboot returned error: %d", error);
159
160	while (1) {}
161}
162
163struct file_format	ppc_elf64 =
164{
165	ppc64_elf_loadfile,
166	ppc64_elf_exec
167};
168
169/*
170 * Sort formats so that those that can detect based on arguments rather than
171 * reading the file first.
172 */
173
174struct file_format *file_formats[] = {
175    &ppc_elf64,
176    NULL
177};
178