1/*-
2 * Copyright (c) 2006 Marcel Moolenaar
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 *
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 <stand.h>
28#include <string.h>
29
30#include <sys/param.h>
31#include <sys/linker.h>
32#include <machine/elf.h>
33
34#ifdef EFI
35#include <efi.h>
36#include <efilib.h>
37#include "loader_efi.h"
38#else
39#include "host_syscall.h"
40#endif
41#include <machine/metadata.h>
42
43#include "bootstrap.h"
44#include "kboot.h"
45#include "bootstrap.h"
46
47#include "platform/acfreebsd.h"
48#include "acconfig.h"
49#define ACPI_SYSTEM_XFACE
50#include "actypes.h"
51#include "actbl.h"
52
53#include "cache.h"
54
55#ifndef EFI
56#define LOADER_PAGE_SIZE PAGE_SIZE
57#endif
58
59#ifdef EFI
60static EFI_GUID acpi_guid = ACPI_TABLE_GUID;
61static EFI_GUID acpi20_guid = ACPI_20_TABLE_GUID;
62#endif
63
64static int elf64_exec(struct preloaded_file *amp);
65static int elf64_obj_exec(struct preloaded_file *amp);
66
67bool do_mem_map = false;
68
69extern uint32_t efi_map_size;
70extern vm_paddr_t efi_map_phys_src;	/* From DTB */
71extern vm_paddr_t efi_map_phys_dst;	/* From our memory map metadata module */
72
73int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp,
74    bool exit_bs);
75
76static struct file_format arm64_elf = {
77	elf64_loadfile,
78	elf64_exec
79};
80
81struct file_format *file_formats[] = {
82	&arm64_elf,
83	NULL
84};
85
86#ifndef EFI
87extern uintptr_t tramp;
88extern uint32_t tramp_size;
89extern uint32_t tramp_data_offset;
90
91struct trampoline_data {
92	uint64_t	entry;			//  0 (PA where kernel loaded)
93	uint64_t	modulep;		//  8 module metadata
94	uint64_t	memmap_src;		// 16 Linux-provided memory map PA
95	uint64_t	memmap_dst;		// 24 Module data copy PA
96	uint64_t	memmap_len;		// 32 Length to copy
97};
98#endif
99
100static int
101elf64_exec(struct preloaded_file *fp)
102{
103	vm_offset_t modulep, kernendp;
104#ifdef EFI
105	vm_offset_t		clean_addr;
106	size_t			clean_size;
107	void (*entry)(vm_offset_t);
108#else
109	vm_offset_t		trampolinebase;
110	vm_offset_t		staging;
111	void			*trampcode;
112	uint64_t		*trampoline;
113	struct trampoline_data	*trampoline_data;
114	int			nseg;
115	void			*kseg;
116#endif
117	struct file_metadata	*md;
118	Elf_Ehdr		*ehdr;
119	int			error;
120#ifdef EFI
121	ACPI_TABLE_RSDP *rsdp;
122	char buf[24];
123	int revision;
124
125	/*
126	 * Report the RSDP to the kernel. The old code used the 'hints' method
127	 * to communite this to the kernel. However, while convenient, the
128	 * 'hints' method is fragile and does not work when static hints are
129	 * compiled into the kernel. Instead, move to setting different tunables
130	 * that start with acpi. The old 'hints' can be removed before we branch
131	 * for FreeBSD 15.
132	 */
133	rsdp = efi_get_table(&acpi20_guid);
134	if (rsdp == NULL) {
135		rsdp = efi_get_table(&acpi_guid);
136	}
137	if (rsdp != NULL) {
138		sprintf(buf, "0x%016llx", (unsigned long long)rsdp);
139		setenv("hint.acpi.0.rsdp", buf, 1);
140		setenv("acpi.rsdp", buf, 1);
141		revision = rsdp->Revision;
142		if (revision == 0)
143			revision = 1;
144		sprintf(buf, "%d", revision);
145		setenv("hint.acpi.0.revision", buf, 1);
146		setenv("acpi.revision", buf, 1);
147		strncpy(buf, rsdp->OemId, sizeof(rsdp->OemId));
148		buf[sizeof(rsdp->OemId)] = '\0';
149		setenv("hint.acpi.0.oem", buf, 1);
150		setenv("acpi.oem", buf, 1);
151		sprintf(buf, "0x%016x", rsdp->RsdtPhysicalAddress);
152		setenv("hint.acpi.0.rsdt", buf, 1);
153		setenv("acpi.rsdt", buf, 1);
154		if (revision >= 2) {
155			/* XXX extended checksum? */
156			sprintf(buf, "0x%016llx",
157			    (unsigned long long)rsdp->XsdtPhysicalAddress);
158			setenv("hint.acpi.0.xsdt", buf, 1);
159			setenv("acpi.xsdt", buf, 1);
160			sprintf(buf, "%d", rsdp->Length);
161			setenv("hint.acpi.0.xsdt_length", buf, 1);
162			setenv("acpi.xsdt_length", buf, 1);
163		}
164	}
165#else
166	vm_offset_t rsdp;
167	rsdp = acpi_rsdp();
168	if (rsdp != 0) {
169		char buf[24];
170
171		printf("Found ACPI 2.0 at %#016lx\n", rsdp);
172		sprintf(buf, "0x%016llx", (unsigned long long)rsdp);
173		setenv("hint.acpi.0.rsdp", buf, 1); /* For 13.1R bootability */
174		setenv("acpi.rsdp", buf, 1);
175		/* Nobody uses the rest of that stuff */
176	}
177
178
179	// XXX Question: why not just use malloc?
180	trampcode = host_getmem(LOADER_PAGE_SIZE);
181	if (trampcode == NULL) {
182		printf("Unable to allocate trampoline\n");
183		return (ENOMEM);
184	}
185	bzero((void *)trampcode, LOADER_PAGE_SIZE);
186	bcopy((void *)&tramp, (void *)trampcode, tramp_size);
187	trampoline = (void *)trampcode;
188
189	/*
190	 * Figure out where to put it.
191	 *
192	 * Linux does not allow us to kexec_load into any part of memory. Ask
193	 * arch_loadaddr to resolve the first available chunk of physical memory
194	 * where loading is possible (load_addr).
195	 *
196	 * The kernel is loaded at the 'base' address in continguous physical
197	 * memory. We use the 2MB in front of the kernel as a place to put our
198	 * trampoline, but that's really overkill since we only need ~100 bytes.
199	 * The arm64 kernel's entry requirements are only 'load the kernel at a
200	 * 2MB alignment' and it figures out the rest, creates the right page
201	 * tables, etc.
202	 */
203	staging = kboot_get_phys_load_segment();
204	printf("Load address at %#jx\n", (uintmax_t)staging);
205	printf("Relocation offset is %#jx\n", (uintmax_t)elf64_relocation_offset);
206#endif
207
208	if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
209        	return(EFTYPE);
210
211	ehdr = (Elf_Ehdr *)&(md->md_data);
212#ifdef EFI
213	entry = efi_translate(ehdr->e_entry);
214
215	efi_time_fini();
216#endif
217	error = bi_load(fp->f_args, &modulep, &kernendp, true);
218	if (error != 0) {
219#ifdef EFI
220		efi_time_init();
221#endif
222		return (error);
223	}
224
225	dev_cleanup();
226
227#ifdef EFI
228	/* Clean D-cache under kernel area and invalidate whole I-cache */
229	clean_addr = (vm_offset_t)efi_translate(fp->f_addr);
230	clean_size = (vm_offset_t)efi_translate(kernendp) - clean_addr;
231
232	cpu_flush_dcache((void *)clean_addr, clean_size);
233	cpu_inval_icache();
234
235	(*entry)(modulep);
236
237#else
238	/* Linux will flush the caches, just pass this data into our trampoline and go */
239	trampoline_data = (void *)trampoline + tramp_data_offset;
240	memset(trampoline_data, 0, sizeof(*trampoline_data));
241	trampoline_data->entry = ehdr->e_entry - fp->f_addr + staging;
242	trampoline_data->modulep = modulep;
243	printf("Modulep = %jx\n", (uintmax_t)modulep);
244	if (efi_map_phys_src != 0) {
245		md = file_findmetadata(fp, MODINFOMD_EFI_MAP);
246		if (md == NULL || md->md_addr == 0) {
247			printf("Need to copy EFI MAP, but EFI MAP not found. %p\n", md);
248		} else {
249			printf("Metadata EFI map loaded at VA %lx\n", md->md_addr);
250			efi_map_phys_dst = md->md_addr + staging +
251			    roundup2(sizeof(struct efi_map_header), 16) - fp->f_addr;
252			trampoline_data->memmap_src = efi_map_phys_src;
253			trampoline_data->memmap_dst = efi_map_phys_dst;
254			trampoline_data->memmap_len = efi_map_size - roundup2(sizeof(struct efi_map_header), 16);
255			printf("Copying UEFI Memory Map data from %#lx to %#lx %ld bytes\n",
256			    efi_map_phys_src,
257			    trampoline_data->memmap_dst,
258			    trampoline_data->memmap_len);
259		}
260	}
261	/*
262	 * Copy the trampoline to the ksegs. Since we're just bouncing off of
263	 * this into the kernel, no need to preserve the pages. On arm64, the
264	 * kernel sets up the initial page table, so we don't have to preserve
265	 * the memory used for the trampoline past when it calls the kernel.
266	 */
267	printf("kernendp = %#llx\n", (long long)kernendp);
268	trampolinebase = staging + (kernendp - fp->f_addr);
269	printf("trampolinebase = %#llx\n", (long long)trampolinebase);
270	archsw.arch_copyin((void *)trampcode, kernendp, tramp_size);
271	printf("Trampoline bouncing to %#llx\n", (long long)trampoline_data->entry);
272
273	kboot_kseg_get(&nseg, &kseg);
274	error = host_kexec_load(trampolinebase, nseg, kseg, HOST_KEXEC_ARCH_AARCH64);
275	if (error != 0)
276		panic("kexec_load returned error: %d", error);
277	host_reboot(HOST_REBOOT_MAGIC1, HOST_REBOOT_MAGIC2, HOST_REBOOT_CMD_KEXEC, 0);
278#endif
279
280	panic("exec returned");
281}
282
283static int
284elf64_obj_exec(struct preloaded_file *fp)
285{
286
287	printf("%s called for preloaded file %p (=%s):\n", __func__, fp,
288	    fp->f_name);
289	return (ENOSYS);
290}
291
292