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