1226586Sdim/*-
2226586Sdim * Copyright (c) 2001 Benno Rice <benno@FreeBSD.org>
3353358Sdim * All rights reserved.
4353358Sdim *
5353358Sdim * Redistribution and use in source and binary forms, with or without
6226586Sdim * modification, are permitted provided that the following conditions
7226586Sdim * are met:
8226586Sdim * 1. Redistributions of source code must retain the above copyright
9226586Sdim *    notice, this list of conditions and the following disclaimer.
10226586Sdim * 2. Redistributions in binary form must reproduce the above copyright
11226586Sdim *    notice, this list of conditions and the following disclaimer in the
12226586Sdim *    documentation and/or other materials provided with the distribution.
13249423Sdim *
14314564Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15226586Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16226586Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17226586Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18226586Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19226586Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20226586Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21226586Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22341825Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23327952Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24327952Sdim * SUCH DAMAGE.
25327952Sdim */
26327952Sdim
27327952Sdim#define __ELF_WORD_SIZE 64
28327952Sdim
29327952Sdim#include <sys/param.h>
30327952Sdim#include <sys/endian.h>
31327952Sdim#include <sys/linker.h>
32327952Sdim
33327952Sdim#include <machine/metadata.h>
34327952Sdim#include <machine/elf.h>
35327952Sdim
36327952Sdim#include <stand.h>
37327952Sdim
38327952Sdim#include "bootstrap.h"
39327952Sdim#include "syscall_nr.h"
40226586Sdim#include "host_syscall.h"
41226586Sdim#include "modinfo.h"
42226586Sdim#include "kboot.h"
43226586Sdim
44226586Sdimextern char		end[];
45226586Sdimextern void		*kerneltramp;
46296417Sdimextern size_t		szkerneltramp;
47226586Sdim
48226586Sdimstruct trampoline_data {
49226586Sdim	uint32_t	kernel_entry;
50226586Sdim	uint32_t	dtb;
51296417Sdim	uint32_t	phys_mem_offset;
52288943Sdim	uint32_t	of_entry;
53226586Sdim	uint32_t	mdp;
54226586Sdim	uint32_t	mdp_size;
55296417Sdim};
56234353Sdim
57234353Sdimint
58234353Sdimppc64_elf_loadfile(char *filename, uint64_t dest,
59234353Sdim    struct preloaded_file **result)
60296417Sdim{
61226586Sdim	int	r;
62296417Sdim
63226586Sdim	r = __elfN(loadfile)(filename, dest, result);
64226586Sdim	if (r != 0)
65226586Sdim		return (r);
66234353Sdim
67243830Sdim	return (0);
68276479Sdim}
69276479Sdim
70226586Sdimint
71226586Sdimppc64_elf_exec(struct preloaded_file *fp)
72226586Sdim{
73239462Sdim	struct file_metadata	*fmp;
74234353Sdim	vm_offset_t		mdp, dtb;
75234353Sdim	Elf_Ehdr		*e;
76226586Sdim	int			error;
77226586Sdim	uint32_t		*trampoline;
78296417Sdim	uint64_t		entry;
79226586Sdim	uint64_t		trampolinebase;
80243830Sdim	struct trampoline_data	*trampoline_data;
81239462Sdim	int			nseg;
82239462Sdim	void			*kseg;
83239462Sdim
84239462Sdim	if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) {
85243830Sdim		return(EFTYPE);
86327952Sdim	}
87327952Sdim	e = (Elf_Ehdr *)&fmp->md_data;
88327952Sdim
89327952Sdim	/*
90239462Sdim	 * Figure out where to put it.
91239462Sdim	 *
92276479Sdim	 * Linux does not allow to do kexec_load into
93276479Sdim	 * any part of memory. Ask arch_loadaddr to
94276479Sdim	 * resolve the first available chunk of physical
95276479Sdim	 * memory where loading is possible (load_addr).
96276479Sdim	 *
97276479Sdim	 * Memory organization is shown below.
98226586Sdim	 * It is assumed, that text segment offset of
99226586Sdim	 * kernel ELF (KERNPHYSADDR) is non-zero,
100296417Sdim	 * which is true for PPC/PPC64 architectures,
101327952Sdim	 * where default is 0x100000.
102327952Sdim	 *
103353358Sdim	 * load_addr:                 trampoline code
104353358Sdim	 * load_addr + KERNPHYSADDR:  kernel text segment
105360784Sdim	 */
106360784Sdim	trampolinebase = archsw.arch_loadaddr(LOAD_RAW, NULL, 0);
107296417Sdim	printf("Load address at %#jx\n", (uintmax_t)trampolinebase);
108226586Sdim	printf("Relocation offset is %#jx\n", (uintmax_t)elf64_relocation_offset);
109234353Sdim
110226586Sdim	/* Set up loader trampoline */
111226586Sdim	trampoline = malloc(szkerneltramp);
112296417Sdim	memcpy(trampoline, &kerneltramp, szkerneltramp);
113226586Sdim
114296417Sdim	/* Parse function descriptor for ELFv1 kernels */
115226586Sdim	if ((e->e_flags & 3) == 2)
116226586Sdim		entry = e->e_entry;
117226586Sdim	else {
118226586Sdim		archsw.arch_copyout(e->e_entry + elf64_relocation_offset,
119226586Sdim		    &entry, 8);
120226586Sdim		entry = be64toh(entry);
121226586Sdim	}
122226586Sdim
123226586Sdim	/*
124226586Sdim	 * Placeholder for trampoline data is at trampolinebase + 0x08
125226586Sdim	 * CAUTION: all data must be Big Endian
126226586Sdim	 */
127226586Sdim	trampoline_data = (void*)&trampoline[2];
128226586Sdim	trampoline_data->kernel_entry = htobe32(entry + elf64_relocation_offset);
129296417Sdim	trampoline_data->phys_mem_offset = htobe32(0);
130226586Sdim	trampoline_data->of_entry = htobe32(0);
131226586Sdim
132226586Sdim	if ((error = md_load64(fp->f_args, &mdp, &dtb)) != 0)
133226586Sdim		return (error);
134234353Sdim
135296417Sdim	trampoline_data->dtb = htobe32(dtb);
136226586Sdim	trampoline_data->mdp = htobe32(mdp);
137226586Sdim	trampoline_data->mdp_size = htobe32(0xfb5d104d);
138226586Sdim
139226586Sdim	printf("Kernel entry at %#jx (%#x) ...\n",
140234353Sdim	    entry, be32toh(trampoline_data->kernel_entry));
141234353Sdim	printf("DTB at %#x, mdp at %#x\n",
142296417Sdim	    be32toh(trampoline_data->dtb), be32toh(trampoline_data->mdp));
143226586Sdim
144226586Sdim	dev_cleanup();
145226586Sdim
146226586Sdim	archsw.arch_copyin(trampoline, trampolinebase, szkerneltramp);
147296417Sdim	free(trampoline);
148226586Sdim
149226586Sdim	kboot_kseg_get(&nseg, &kseg);
150226586Sdim
151296417Sdim	error = host_kexec_load(trampolinebase, nseg, kseg, HOST_KEXEC_ARCH_PPC64);
152226586Sdim	if (error != 0)
153296417Sdim		panic("kexec_load returned error: %d", error);
154226586Sdim
155226586Sdim	error = host_reboot(HOST_REBOOT_MAGIC1, HOST_REBOOT_MAGIC2, HOST_REBOOT_CMD_KEXEC,
156296417Sdim	    (uintptr_t)NULL);
157226586Sdim	if (error != 0)
158226586Sdim		panic("reboot returned error: %d", error);
159226586Sdim
160296417Sdim	while (1) {}
161226586Sdim}
162226586Sdim
163296417Sdimstruct file_format	ppc_elf64 =
164226586Sdim{
165296417Sdim	ppc64_elf_loadfile,
166234353Sdim	ppc64_elf_exec
167226586Sdim};
168226586Sdim
169226586Sdim/*
170276479Sdim * Sort formats so that those that can detect based on arguments rather than
171243830Sdim * reading the file first.
172226586Sdim */
173226586Sdim
174226586Sdimstruct file_format *file_formats[] = {
175226586Sdim    &ppc_elf64,
176226586Sdim    NULL
177226586Sdim};
178226586Sdim