xref: /freebsd-14.2/sys/arm/arm/machdep_boot.c (revision 685dc743)
1 /*-
2  * Copyright (c) 2004 Olivier Houchard
3  * Copyright (c) 1994-1998 Mark Brinicombe.
4  * Copyright (c) 1994 Brini.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include "opt_platform.h"
30 #include "opt_ddb.h"
31 
32 #include <sys/cdefs.h>
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/ctype.h>
36 #include <sys/linker.h>
37 #include <sys/physmem.h>
38 #include <sys/reboot.h>
39 #include <sys/sysctl.h>
40 #if defined(LINUX_BOOT_ABI)
41 #include <sys/boot.h>
42 #endif
43 
44 #include <machine/atags.h>
45 #include <machine/cpu.h>
46 #include <machine/machdep.h>
47 #include <machine/metadata.h>
48 #include <machine/vmparam.h>	/* For KERNVIRTADDR */
49 
50 #ifdef FDT
51 #include <contrib/libfdt/libfdt.h>
52 #include <dev/fdt/fdt_common.h>
53 #endif
54 
55 #ifdef EFI
56 #include <sys/efi.h>
57 #endif
58 
59 #ifdef DDB
60 #include <ddb/ddb.h>
61 #endif
62 
63 #ifdef DEBUG
64 #define	debugf(fmt, args...) printf(fmt, ##args)
65 #else
66 #define	debugf(fmt, args...)
67 #endif
68 
69 #ifdef LINUX_BOOT_ABI
70 static char static_kenv[4096];
71 #endif
72 
73 extern int *end;
74 
75 static uint32_t board_revision;
76 /* hex representation of uint64_t */
77 static char board_serial[32];
78 static char *loader_envp;
79 
80 #if defined(LINUX_BOOT_ABI)
81 #define LBABI_MAX_BANKS	10
82 #define CMDLINE_GUARD "FreeBSD:"
83 static uint32_t board_id;
84 static struct arm_lbabi_tag *atag_list;
85 static char linux_command_line[LBABI_MAX_COMMAND_LINE + 1];
86 static char atags[LBABI_MAX_COMMAND_LINE * 2];
87 #endif /* defined(LINUX_BOOT_ABI) */
88 
89 SYSCTL_NODE(_hw, OID_AUTO, board, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
90     "Board attributes");
91 SYSCTL_UINT(_hw_board, OID_AUTO, revision, CTLFLAG_RD,
92     &board_revision, 0, "Board revision");
93 SYSCTL_STRING(_hw_board, OID_AUTO, serial, CTLFLAG_RD,
94     board_serial, 0, "Board serial");
95 
96 int vfp_exists;
97 SYSCTL_INT(_hw, HW_FLOATINGPT, floatingpoint, CTLFLAG_RD,
98     &vfp_exists, 0, "Floating point support enabled");
99 
100 void
board_set_serial(uint64_t serial)101 board_set_serial(uint64_t serial)
102 {
103 
104 	snprintf(board_serial, sizeof(board_serial)-1,
105 		    "%016jx", serial);
106 }
107 
108 void
board_set_revision(uint32_t revision)109 board_set_revision(uint32_t revision)
110 {
111 
112 	board_revision = revision;
113 }
114 
115 static char *
kenv_next(char * cp)116 kenv_next(char *cp)
117 {
118 
119 	if (cp != NULL) {
120 		while (*cp != 0)
121 			cp++;
122 		cp++;
123 		if (*cp == 0)
124 			cp = NULL;
125 	}
126 	return (cp);
127 }
128 
129 void
arm_print_kenv(void)130 arm_print_kenv(void)
131 {
132 	char *cp;
133 
134 	debugf("loader passed (static) kenv:\n");
135 	if (loader_envp == NULL) {
136 		debugf(" no env, null ptr\n");
137 		return;
138 	}
139 	debugf(" loader_envp = 0x%08x\n", (uint32_t)loader_envp);
140 
141 	for (cp = loader_envp; cp != NULL; cp = kenv_next(cp))
142 		debugf(" %x %s\n", (uint32_t)cp, cp);
143 }
144 
145 #if defined(LINUX_BOOT_ABI)
146 
147 /* Convert the U-Boot command line into FreeBSD kenv and boot options. */
148 static void
cmdline_set_env(char * cmdline,const char * guard)149 cmdline_set_env(char *cmdline, const char *guard)
150 {
151 	size_t guard_len;
152 
153 	/* Skip leading spaces. */
154 	while (isspace(*cmdline))
155 		cmdline++;
156 
157 	/* Test and remove guard. */
158 	if (guard != NULL && guard[0] != '\0') {
159 		guard_len  =  strlen(guard);
160 		if (strncasecmp(cmdline, guard, guard_len) != 0)
161 			return;
162 		cmdline += guard_len;
163 	}
164 
165 	boothowto |= boot_parse_cmdline(cmdline);
166 }
167 
168 /*
169  * Called for armv6 and newer.
170  */
arm_parse_fdt_bootargs(void)171 void arm_parse_fdt_bootargs(void)
172 {
173 
174 #ifdef FDT
175 	if (loader_envp == NULL && fdt_get_chosen_bootargs(linux_command_line,
176 	    LBABI_MAX_COMMAND_LINE) == 0) {
177 		init_static_kenv(static_kenv, sizeof(static_kenv));
178 		cmdline_set_env(linux_command_line, CMDLINE_GUARD);
179 	}
180 #endif
181 }
182 
183 /*
184  * Called for armv[45].
185  */
186 static vm_offset_t
linux_parse_boot_param(struct arm_boot_params * abp)187 linux_parse_boot_param(struct arm_boot_params *abp)
188 {
189 	struct arm_lbabi_tag *walker;
190 	uint32_t revision;
191 	uint64_t serial;
192 	int size;
193 	vm_offset_t lastaddr;
194 #ifdef FDT
195 	struct fdt_header *dtb_ptr;
196 	uint32_t dtb_size;
197 #endif
198 
199 	/*
200 	 * Linux boot ABI: r0 = 0, r1 is the board type (!= 0) and r2
201 	 * is atags or dtb pointer.  If all of these aren't satisfied,
202 	 * then punt. Unfortunately, it looks like DT enabled kernels
203 	 * doesn't uses board type and U-Boot delivers 0 in r1 for them.
204 	 */
205 	if (abp->abp_r0 != 0 || abp->abp_r2 == 0)
206 		return (0);
207 #ifdef FDT
208 	/* Test if r2 point to valid DTB. */
209 	dtb_ptr = (struct fdt_header *)abp->abp_r2;
210 	if (fdt_check_header(dtb_ptr) == 0) {
211 		dtb_size = fdt_totalsize(dtb_ptr);
212 		return (fake_preload_metadata(abp, dtb_ptr, dtb_size));
213 	}
214 #endif
215 
216 	board_id = abp->abp_r1;
217 	walker = (struct arm_lbabi_tag *)abp->abp_r2;
218 
219 	if (ATAG_TAG(walker) != ATAG_CORE)
220 		return 0;
221 
222 	atag_list = walker;
223 	while (ATAG_TAG(walker) != ATAG_NONE) {
224 		switch (ATAG_TAG(walker)) {
225 		case ATAG_CORE:
226 			break;
227 		case ATAG_MEM:
228 			physmem_hardware_region(walker->u.tag_mem.start,
229 			    walker->u.tag_mem.size);
230 			break;
231 		case ATAG_INITRD2:
232 			break;
233 		case ATAG_SERIAL:
234 			serial = walker->u.tag_sn.high;
235 			serial <<= 32;
236 			serial |= walker->u.tag_sn.low;
237 			board_set_serial(serial);
238 			break;
239 		case ATAG_REVISION:
240 			revision = walker->u.tag_rev.rev;
241 			board_set_revision(revision);
242 			break;
243 		case ATAG_CMDLINE:
244 			size = ATAG_SIZE(walker) -
245 			    sizeof(struct arm_lbabi_header);
246 			size = min(size, LBABI_MAX_COMMAND_LINE);
247 			strncpy(linux_command_line, walker->u.tag_cmd.command,
248 			    size);
249 			linux_command_line[size] = '\0';
250 			break;
251 		default:
252 			break;
253 		}
254 		walker = ATAG_NEXT(walker);
255 	}
256 
257 	/* Save a copy for later */
258 	bcopy(atag_list, atags,
259 	    (char *)walker - (char *)atag_list + ATAG_SIZE(walker));
260 
261 	lastaddr = fake_preload_metadata(abp, NULL, 0);
262 	init_static_kenv(static_kenv, sizeof(static_kenv));
263 	cmdline_set_env(linux_command_line, CMDLINE_GUARD);
264 	return lastaddr;
265 }
266 #endif
267 
268 #if defined(FREEBSD_BOOT_LOADER)
269 static vm_offset_t
freebsd_parse_boot_param(struct arm_boot_params * abp)270 freebsd_parse_boot_param(struct arm_boot_params *abp)
271 {
272 	vm_offset_t lastaddr = 0;
273 	void *mdp;
274 	void *kmdp;
275 #ifdef DDB
276 	vm_offset_t ksym_start;
277 	vm_offset_t ksym_end;
278 #endif
279 
280 	/*
281 	 * Mask metadata pointer: it is supposed to be on page boundary. If
282 	 * the first argument (mdp) doesn't point to a valid address the
283 	 * bootloader must have passed us something else than the metadata
284 	 * ptr, so we give up.  Also give up if we cannot find metadta section
285 	 * the loader creates that we get all this data out of.
286 	 */
287 
288 	if ((mdp = (void *)(abp->abp_r0 & ~PAGE_MASK)) == NULL)
289 		return 0;
290 	preload_metadata = mdp;
291 	kmdp = preload_search_by_type("elf kernel");
292 	if (kmdp == NULL)
293 		return 0;
294 
295 	boothowto = MD_FETCH(kmdp, MODINFOMD_HOWTO, int);
296 	loader_envp = MD_FETCH(kmdp, MODINFOMD_ENVP, char *);
297 	init_static_kenv(loader_envp, 0);
298 	lastaddr = MD_FETCH(kmdp, MODINFOMD_KERNEND, vm_offset_t);
299 #ifdef DDB
300 	ksym_start = MD_FETCH(kmdp, MODINFOMD_SSYM, uintptr_t);
301 	ksym_end = MD_FETCH(kmdp, MODINFOMD_ESYM, uintptr_t);
302 	db_fetch_ksymtab(ksym_start, ksym_end, 0);
303 #endif
304 	return lastaddr;
305 }
306 #endif
307 
308 vm_offset_t
default_parse_boot_param(struct arm_boot_params * abp)309 default_parse_boot_param(struct arm_boot_params *abp)
310 {
311 	vm_offset_t lastaddr;
312 
313 #if defined(LINUX_BOOT_ABI)
314 	if ((lastaddr = linux_parse_boot_param(abp)) != 0)
315 		return lastaddr;
316 #endif
317 #if defined(FREEBSD_BOOT_LOADER)
318 	if ((lastaddr = freebsd_parse_boot_param(abp)) != 0)
319 		return lastaddr;
320 #endif
321 	/* Fall back to hardcoded metadata. */
322 	lastaddr = fake_preload_metadata(abp, NULL, 0);
323 
324 	return lastaddr;
325 }
326 
327 /*
328  * Stub version of the boot parameter parsing routine.  We are
329  * called early in initarm, before even VM has been initialized.
330  * This routine needs to preserve any data that the boot loader
331  * has passed in before the kernel starts to grow past the end
332  * of the BSS, traditionally the place boot-loaders put this data.
333  *
334  * Since this is called so early, things that depend on the vm system
335  * being setup (including access to some SoC's serial ports), about
336  * all that can be done in this routine is to copy the arguments.
337  *
338  * This is the default boot parameter parsing routine.  Individual
339  * kernels/boards can override this weak function with one of their
340  * own.  We just fake metadata...
341  */
342 __weak_reference(default_parse_boot_param, parse_boot_param);
343 
344 /*
345  * Fake up a boot descriptor table
346  */
347 vm_offset_t
fake_preload_metadata(struct arm_boot_params * abp __unused,void * dtb_ptr,size_t dtb_size)348 fake_preload_metadata(struct arm_boot_params *abp __unused, void *dtb_ptr,
349     size_t dtb_size)
350 {
351 	vm_offset_t lastaddr;
352 	int i = 0;
353 	static uint32_t fake_preload[35];
354 
355 	lastaddr = (vm_offset_t)&end;
356 
357 	fake_preload[i++] = MODINFO_NAME;
358 	fake_preload[i++] = strlen("kernel") + 1;
359 	strcpy((char*)&fake_preload[i++], "kernel");
360 	i += 1;
361 	fake_preload[i++] = MODINFO_TYPE;
362 	fake_preload[i++] = strlen("elf kernel") + 1;
363 	strcpy((char*)&fake_preload[i++], "elf kernel");
364 	i += 2;
365 	fake_preload[i++] = MODINFO_ADDR;
366 	fake_preload[i++] = sizeof(vm_offset_t);
367 	fake_preload[i++] = KERNVIRTADDR;
368 	fake_preload[i++] = MODINFO_SIZE;
369 	fake_preload[i++] = sizeof(uint32_t);
370 	fake_preload[i++] = (uint32_t)&end - KERNVIRTADDR;
371 	if (dtb_ptr != NULL) {
372 		/* Copy DTB to KVA space and insert it into module chain. */
373 		lastaddr = roundup(lastaddr, sizeof(int));
374 		fake_preload[i++] = MODINFO_METADATA | MODINFOMD_DTBP;
375 		fake_preload[i++] = sizeof(uint32_t);
376 		fake_preload[i++] = (uint32_t)lastaddr;
377 		memmove((void *)lastaddr, dtb_ptr, dtb_size);
378 		lastaddr += dtb_size;
379 		lastaddr = roundup(lastaddr, sizeof(int));
380 	}
381 	fake_preload[i++] = 0;
382 	fake_preload[i] = 0;
383 	preload_metadata = (void *)fake_preload;
384 
385 	init_static_kenv(NULL, 0);
386 
387 	return (lastaddr);
388 }
389 
390 #ifdef EFI
391 void
arm_add_efi_map_entries(struct efi_map_header * efihdr,struct mem_region * mr,int * mrcnt)392 arm_add_efi_map_entries(struct efi_map_header *efihdr, struct mem_region *mr,
393     int *mrcnt)
394 {
395 	struct efi_md *map, *p;
396 	const char *type;
397 	size_t efisz;
398 	int ndesc, i, j;
399 
400 	static const char *types[] = {
401 		"Reserved",
402 		"LoaderCode",
403 		"LoaderData",
404 		"BootServicesCode",
405 		"BootServicesData",
406 		"RuntimeServicesCode",
407 		"RuntimeServicesData",
408 		"ConventionalMemory",
409 		"UnusableMemory",
410 		"ACPIReclaimMemory",
411 		"ACPIMemoryNVS",
412 		"MemoryMappedIO",
413 		"MemoryMappedIOPortSpace",
414 		"PalCode",
415 		"PersistentMemory"
416 	};
417 
418 	*mrcnt = 0;
419 
420 	/*
421 	 * Memory map data provided by UEFI via the GetMemoryMap
422 	 * Boot Services API.
423 	 */
424 	efisz = roundup2(sizeof(struct efi_map_header), 0x10);
425 	map = (struct efi_md *)((uint8_t *)efihdr + efisz);
426 
427 	if (efihdr->descriptor_size == 0)
428 		return;
429 	ndesc = efihdr->memory_size / efihdr->descriptor_size;
430 
431 	if (boothowto & RB_VERBOSE)
432 		printf("%23s %12s %12s %8s %4s\n",
433 		    "Type", "Physical", "Virtual", "#Pages", "Attr");
434 
435 	for (i = 0, j = 0, p = map; i < ndesc; i++,
436 	    p = efi_next_descriptor(p, efihdr->descriptor_size)) {
437 		if (boothowto & RB_VERBOSE) {
438 			if (p->md_type < nitems(types))
439 				type = types[p->md_type];
440 			else
441 				type = "<INVALID>";
442 			printf("%23s %012llx %012llx %08llx ", type, p->md_phys,
443 			    p->md_virt, p->md_pages);
444 			if (p->md_attr & EFI_MD_ATTR_UC)
445 				printf("UC ");
446 			if (p->md_attr & EFI_MD_ATTR_WC)
447 				printf("WC ");
448 			if (p->md_attr & EFI_MD_ATTR_WT)
449 				printf("WT ");
450 			if (p->md_attr & EFI_MD_ATTR_WB)
451 				printf("WB ");
452 			if (p->md_attr & EFI_MD_ATTR_UCE)
453 				printf("UCE ");
454 			if (p->md_attr & EFI_MD_ATTR_WP)
455 				printf("WP ");
456 			if (p->md_attr & EFI_MD_ATTR_RP)
457 				printf("RP ");
458 			if (p->md_attr & EFI_MD_ATTR_XP)
459 				printf("XP ");
460 			if (p->md_attr & EFI_MD_ATTR_NV)
461 				printf("NV ");
462 			if (p->md_attr & EFI_MD_ATTR_MORE_RELIABLE)
463 				printf("MORE_RELIABLE ");
464 			if (p->md_attr & EFI_MD_ATTR_RO)
465 				printf("RO ");
466 			if (p->md_attr & EFI_MD_ATTR_RT)
467 				printf("RUNTIME");
468 			printf("\n");
469 		}
470 
471 		switch (p->md_type) {
472 		case EFI_MD_TYPE_CODE:
473 		case EFI_MD_TYPE_DATA:
474 		case EFI_MD_TYPE_BS_CODE:
475 		case EFI_MD_TYPE_BS_DATA:
476 		case EFI_MD_TYPE_FREE:
477 			/*
478 			 * We're allowed to use any entry with these types.
479 			 */
480 			break;
481 		default:
482 			continue;
483 		}
484 
485 		j++;
486 		if (j >= FDT_MEM_REGIONS)
487 			break;
488 
489 		mr[j].mr_start = p->md_phys;
490 		mr[j].mr_size = p->md_pages * EFI_PAGE_SIZE;
491 	}
492 
493 	*mrcnt = j;
494 }
495 #endif /* EFI */
496