xref: /freebsd/stand/kboot/kboot/arch/aarch64/exec.c (revision 783d3ff6)
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
60 static EFI_GUID acpi_guid = ACPI_TABLE_GUID;
61 static EFI_GUID acpi20_guid = ACPI_20_TABLE_GUID;
62 #endif
63 
64 static int elf64_exec(struct preloaded_file *amp);
65 static int elf64_obj_exec(struct preloaded_file *amp);
66 
67 bool do_mem_map = false;
68 
69 extern uint32_t efi_map_size;
70 extern vm_paddr_t efi_map_phys_src;	/* From DTB */
71 extern vm_paddr_t efi_map_phys_dst;	/* From our memory map metadata module */
72 
73 int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp,
74     bool exit_bs);
75 
76 static struct file_format arm64_elf = {
77 	elf64_loadfile,
78 	elf64_exec
79 };
80 
81 struct file_format *file_formats[] = {
82 	&arm64_elf,
83 	NULL
84 };
85 
86 #ifndef EFI
87 extern uintptr_t tramp;
88 extern uint32_t tramp_size;
89 extern uint32_t tramp_data_offset;
90 
91 struct 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 
100 static int
101 elf64_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 
283 static int
284 elf64_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