1/*	$OpenBSD: efiboot.c,v 1.50 2024/02/23 21:52:12 kettenis Exp $	*/
2
3/*
4 * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
5 * Copyright (c) 2016 Mark Kettenis
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/param.h>
21#include <sys/queue.h>
22#include <sys/stat.h>
23#include <dev/cons.h>
24#include <sys/disklabel.h>
25
26#include <efi.h>
27#include <efiapi.h>
28#include <efiprot.h>
29#include <eficonsctl.h>
30
31#include <dev/biovar.h>
32#include <dev/softraidvar.h>
33
34#include <lib/libkern/libkern.h>
35#include <lib/libsa/softraid.h>
36#include <stand/boot/cmd.h>
37
38#include "libsa.h"
39#include "disk.h"
40#include "softraid_arm64.h"
41
42#include "efidev.h"
43#include "efiboot.h"
44#include "fdt.h"
45
46EFI_SYSTEM_TABLE	*ST;
47EFI_BOOT_SERVICES	*BS;
48EFI_RUNTIME_SERVICES	*RS;
49EFI_HANDLE		 IH, efi_bootdp;
50void			*fdt_sys = NULL;
51void			*fdt_override = NULL;
52size_t			 fdt_override_size;
53void			*smbios = NULL;
54
55EFI_PHYSICAL_ADDRESS	 heap;
56UINTN			 heapsiz = 1 * 1024 * 1024;
57EFI_MEMORY_DESCRIPTOR	*mmap;
58UINTN			 mmap_key;
59UINTN			 mmap_ndesc;
60UINTN			 mmap_descsiz;
61UINT32			 mmap_version;
62
63static EFI_GUID		 imgp_guid = LOADED_IMAGE_PROTOCOL;
64static EFI_GUID		 blkio_guid = BLOCK_IO_PROTOCOL;
65static EFI_GUID		 devp_guid = DEVICE_PATH_PROTOCOL;
66static EFI_GUID		 gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
67static EFI_GUID		 fdt_guid = FDT_TABLE_GUID;
68static EFI_GUID		 smbios_guid = SMBIOS_TABLE_GUID;
69static EFI_GUID		 smbios3_guid = SMBIOS3_TABLE_GUID;
70
71#define efi_guidcmp(_a, _b)	memcmp((_a), (_b), sizeof(EFI_GUID))
72
73int efi_device_path_depth(EFI_DEVICE_PATH *dp, int);
74int efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int);
75static void efi_heap_init(void);
76static void efi_memprobe_internal(void);
77static void efi_timer_init(void);
78static void efi_timer_cleanup(void);
79static EFI_STATUS efi_memprobe_find(UINTN, UINTN, EFI_MEMORY_TYPE,
80    EFI_PHYSICAL_ADDRESS *);
81void *efi_fdt(void);
82int fdt_load_override(char *);
83extern void smbios_init(void *);
84
85EFI_STATUS
86efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
87{
88	extern char		*progname;
89	EFI_LOADED_IMAGE	*imgp;
90	EFI_DEVICE_PATH		*dp = NULL;
91	EFI_STATUS		 status;
92	int			 i;
93
94	ST = systab;
95	BS = ST->BootServices;
96	RS = ST->RuntimeServices;
97	IH = image;
98
99	/* disable reset by watchdog after 5 minutes */
100	BS->SetWatchdogTimer(0, 0, 0, NULL);
101
102	status = BS->HandleProtocol(image, &imgp_guid,
103	    (void **)&imgp);
104	if (status == EFI_SUCCESS)
105		status = BS->HandleProtocol(imgp->DeviceHandle, &devp_guid,
106		    (void **)&dp);
107	if (status == EFI_SUCCESS)
108		efi_bootdp = dp;
109
110	for (i = 0; i < ST->NumberOfTableEntries; i++) {
111		if (efi_guidcmp(&fdt_guid,
112		    &ST->ConfigurationTable[i].VendorGuid) == 0)
113			fdt_sys = ST->ConfigurationTable[i].VendorTable;
114		if (efi_guidcmp(&smbios_guid,
115		    &ST->ConfigurationTable[i].VendorGuid) == 0)
116			smbios = ST->ConfigurationTable[i].VendorTable;
117		if (efi_guidcmp(&smbios3_guid,
118		    &ST->ConfigurationTable[i].VendorGuid) == 0)
119			smbios = ST->ConfigurationTable[i].VendorTable;
120	}
121	fdt_init(fdt_sys);
122
123	progname = "BOOTAA64";
124
125	boot(0);
126
127	return (EFI_SUCCESS);
128}
129
130static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
131static SIMPLE_INPUT_INTERFACE *conin;
132
133/*
134 * The device majors for these don't match the ones used by the
135 * kernel.  That's fine.  They're just used as an index into the cdevs
136 * array and never passed on to the kernel.
137 */
138static dev_t serial = makedev(1, 0);
139static dev_t framebuffer = makedev(2, 0);
140
141static char framebuffer_path[128];
142
143void
144efi_cons_probe(struct consdev *cn)
145{
146	cn->cn_pri = CN_MIDPRI;
147	cn->cn_dev = makedev(0, 0);
148}
149
150void
151efi_cons_init(struct consdev *cp)
152{
153	conin = ST->ConIn;
154	conout = ST->ConOut;
155}
156
157int
158efi_cons_getc(dev_t dev)
159{
160	EFI_INPUT_KEY	 key;
161	EFI_STATUS	 status;
162#if 0
163	UINTN		 dummy;
164#endif
165	static int	 lastchar = 0;
166
167	if (lastchar) {
168		int r = lastchar;
169		if ((dev & 0x80) == 0)
170			lastchar = 0;
171		return (r);
172	}
173
174	status = conin->ReadKeyStroke(conin, &key);
175	while (status == EFI_NOT_READY || key.UnicodeChar == 0) {
176		if (dev & 0x80)
177			return (0);
178		/*
179		 * XXX The implementation of WaitForEvent() in U-boot
180		 * is broken and neverreturns.
181		 */
182#if 0
183		BS->WaitForEvent(1, &conin->WaitForKey, &dummy);
184#endif
185		status = conin->ReadKeyStroke(conin, &key);
186	}
187
188	if (dev & 0x80)
189		lastchar = key.UnicodeChar;
190
191	return (key.UnicodeChar);
192}
193
194void
195efi_cons_putc(dev_t dev, int c)
196{
197	CHAR16	buf[2];
198
199	if (c == '\n')
200		efi_cons_putc(dev, '\r');
201
202	buf[0] = c;
203	buf[1] = 0;
204
205	conout->OutputString(conout, buf);
206}
207
208void
209efi_com_probe(struct consdev *cn)
210{
211	cn->cn_pri = CN_LOWPRI;
212	cn->cn_dev = serial;
213}
214
215void
216efi_com_init(struct consdev *cn)
217{
218	conin = ST->ConIn;
219	conout = ST->ConOut;
220}
221
222int
223efi_com_getc(dev_t dev)
224{
225	return efi_cons_getc(dev);
226}
227
228void
229efi_com_putc(dev_t dev, int c)
230{
231	efi_cons_putc(dev, c);
232}
233
234void
235efi_fb_probe(struct consdev *cn)
236{
237	cn->cn_pri = CN_LOWPRI;
238	cn->cn_dev = framebuffer;
239}
240
241void
242efi_fb_init(struct consdev *cn)
243{
244	conin = ST->ConIn;
245	conout = ST->ConOut;
246}
247
248int
249efi_fb_getc(dev_t dev)
250{
251	return efi_cons_getc(dev);
252}
253
254void
255efi_fb_putc(dev_t dev, int c)
256{
257	efi_cons_putc(dev, c);
258}
259
260static void
261efi_heap_init(void)
262{
263	EFI_STATUS	 status;
264
265	status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
266	    EFI_SIZE_TO_PAGES(heapsiz), &heap);
267	if (status != EFI_SUCCESS)
268		panic("BS->AllocatePages()");
269}
270
271struct disklist_lh disklist;
272struct diskinfo *bootdev_dip;
273
274void
275efi_diskprobe(void)
276{
277	int			 i, bootdev = 0, depth = -1;
278	UINTN			 sz;
279	EFI_STATUS		 status;
280	EFI_HANDLE		*handles = NULL;
281	EFI_BLOCK_IO		*blkio;
282	EFI_BLOCK_IO_MEDIA	*media;
283	struct diskinfo		*di;
284	EFI_DEVICE_PATH		*dp;
285
286	TAILQ_INIT(&disklist);
287
288	sz = 0;
289	status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, 0);
290	if (status == EFI_BUFFER_TOO_SMALL) {
291		handles = alloc(sz);
292		status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
293		    handles);
294	}
295	if (handles == NULL || EFI_ERROR(status))
296		return;
297
298	if (efi_bootdp != NULL)
299		depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
300
301	/*
302	 * U-Boot incorrectly represents devices with a single
303	 * MEDIA_DEVICE_PATH component.  In that case include that
304	 * component into the matching, otherwise we'll blindly select
305	 * the first device.
306	 */
307	if (depth == 0)
308		depth = 1;
309
310	for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) {
311		status = BS->HandleProtocol(handles[i], &blkio_guid,
312		    (void **)&blkio);
313		if (EFI_ERROR(status))
314			panic("BS->HandleProtocol() returns %d", status);
315
316		media = blkio->Media;
317		if (media->LogicalPartition || !media->MediaPresent)
318			continue;
319		di = alloc(sizeof(struct diskinfo));
320		efid_init(di, blkio);
321
322		if (efi_bootdp == NULL || depth == -1 || bootdev != 0)
323			goto next;
324		status = BS->HandleProtocol(handles[i], &devp_guid,
325		    (void **)&dp);
326		if (EFI_ERROR(status))
327			goto next;
328		if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) {
329			TAILQ_INSERT_HEAD(&disklist, di, list);
330			bootdev_dip = di;
331			bootdev = 1;
332			continue;
333		}
334next:
335		TAILQ_INSERT_TAIL(&disklist, di, list);
336	}
337
338	free(handles, sz);
339
340	/* Print available disks and probe for softraid. */
341	i = 0;
342	printf("disks:");
343	TAILQ_FOREACH(di, &disklist, list) {
344		printf(" sd%d%s", i, di == bootdev_dip ? "*" : "");
345		i++;
346	}
347	srprobe();
348	printf("\n");
349}
350
351/*
352 * Determine the number of nodes up to, but not including, the first
353 * node of the specified type.
354 */
355int
356efi_device_path_depth(EFI_DEVICE_PATH *dp, int dptype)
357{
358	int	i;
359
360	for (i = 0; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp), i++) {
361		if (DevicePathType(dp) == dptype)
362			return (i);
363	}
364
365	return (i);
366}
367
368int
369efi_device_path_ncmp(EFI_DEVICE_PATH *dpa, EFI_DEVICE_PATH *dpb, int deptn)
370{
371	int	 i, cmp;
372
373	for (i = 0; i < deptn; i++) {
374		if (IsDevicePathEnd(dpa) || IsDevicePathEnd(dpb))
375			return ((IsDevicePathEnd(dpa) && IsDevicePathEnd(dpb))
376			    ? 0 : (IsDevicePathEnd(dpa))? -1 : 1);
377		cmp = DevicePathNodeLength(dpa) - DevicePathNodeLength(dpb);
378		if (cmp)
379			return (cmp);
380		cmp = memcmp(dpa, dpb, DevicePathNodeLength(dpa));
381		if (cmp)
382			return (cmp);
383		dpa = NextDevicePathNode(dpa);
384		dpb = NextDevicePathNode(dpb);
385	}
386
387	return (0);
388}
389
390void
391efi_framebuffer(void)
392{
393	EFI_GRAPHICS_OUTPUT *gop;
394	EFI_STATUS status;
395	void *node, *child;
396	uint32_t acells, scells;
397	uint64_t base, size;
398	uint32_t reg[4];
399	uint32_t width, height, stride;
400	char *format;
401	char *prop;
402
403	/*
404	 * Don't create a "simple-framebuffer" node if we already have
405	 * one.  Besides "/chosen", we also check under "/" since that
406	 * is where the Raspberry Pi firmware puts it.
407	 */
408	node = fdt_find_node("/chosen");
409	for (child = fdt_child_node(node); child;
410	     child = fdt_next_node(child)) {
411		if (!fdt_node_is_compatible(child, "simple-framebuffer"))
412			continue;
413		if (!fdt_node_property(child, "status", &prop) ||
414		    strcmp(prop, "okay") == 0) {
415			strlcpy(framebuffer_path, "/chosen/",
416			    sizeof(framebuffer_path));
417			strlcat(framebuffer_path, fdt_node_name(child),
418			    sizeof(framebuffer_path));
419			return;
420		}
421	}
422	node = fdt_find_node("/");
423	for (child = fdt_child_node(node); child;
424	     child = fdt_next_node(child)) {
425		if (!fdt_node_is_compatible(child, "simple-framebuffer"))
426			continue;
427		if (!fdt_node_property(child, "status", &prop) ||
428		    strcmp(prop, "okay") == 0) {
429			strlcpy(framebuffer_path, "/",
430			    sizeof(framebuffer_path));
431			strlcat(framebuffer_path, fdt_node_name(child),
432			    sizeof(framebuffer_path));
433			return;
434		}
435	}
436
437	status = BS->LocateProtocol(&gop_guid, NULL, (void **)&gop);
438	if (status != EFI_SUCCESS)
439		return;
440
441	/* Paranoia! */
442	if (gop == NULL || gop->Mode == NULL || gop->Mode->Info == NULL)
443		return;
444
445	/* We only support 32-bit pixel modes for now. */
446	switch (gop->Mode->Info->PixelFormat) {
447	case PixelRedGreenBlueReserved8BitPerColor:
448		format = "x8b8g8r8";
449		break;
450	case PixelBlueGreenRedReserved8BitPerColor:
451		format = "x8r8g8b8";
452		break;
453	default:
454		return;
455	}
456
457	base = gop->Mode->FrameBufferBase;
458	size = gop->Mode->FrameBufferSize;
459	width = htobe32(gop->Mode->Info->HorizontalResolution);
460	height = htobe32(gop->Mode->Info->VerticalResolution);
461	stride = htobe32(gop->Mode->Info->PixelsPerScanLine * 4);
462
463	node = fdt_find_node("/");
464	if (fdt_node_property_int(node, "#address-cells", &acells) != 1)
465		acells = 1;
466	if (fdt_node_property_int(node, "#size-cells", &scells) != 1)
467		scells = 1;
468	if (acells > 2 || scells > 2)
469		return;
470	if (acells >= 1)
471		reg[0] = htobe32(base);
472	if (acells == 2) {
473		reg[1] = reg[0];
474		reg[0] = htobe32(base >> 32);
475	}
476	if (scells >= 1)
477		reg[acells] = htobe32(size);
478	if (scells == 2) {
479		reg[acells + 1] = reg[acells];
480		reg[acells] = htobe32(size >> 32);
481	}
482
483	node = fdt_find_node("/chosen");
484	fdt_node_add_node(node, "framebuffer", &child);
485	fdt_node_add_property(child, "status", "okay", strlen("okay") + 1);
486	fdt_node_add_property(child, "format", format, strlen(format) + 1);
487	fdt_node_add_property(child, "stride", &stride, 4);
488	fdt_node_add_property(child, "height", &height, 4);
489	fdt_node_add_property(child, "width", &width, 4);
490	fdt_node_add_property(child, "reg", reg, (acells + scells) * 4);
491	fdt_node_add_property(child, "compatible",
492	    "simple-framebuffer", strlen("simple-framebuffer") + 1);
493
494	strlcpy(framebuffer_path, "/chosen/framebuffer",
495	    sizeof(framebuffer_path));
496}
497
498void
499efi_console(void)
500{
501	void *node;
502
503	if (major(cn_tab->cn_dev) == major(serial)) {
504		char *serial_path;
505		char alias[16];
506		int len;
507
508		/* Construct alias and resolve it. */
509		snprintf(alias, sizeof(alias), "serial%d",
510		    minor(cn_tab->cn_dev));
511		node = fdt_find_node("/aliases");
512		len = fdt_node_property(node, alias, &serial_path);
513		if (len <= 0)
514			return;
515
516		/* Point stdout-path at the serial node. */
517		node = fdt_find_node("/chosen");
518		fdt_node_add_property(node, "stdout-path",
519		    serial_path, strlen(serial_path) + 1);
520	} else if (major(cn_tab->cn_dev) == major(framebuffer)) {
521		if (strlen(framebuffer_path) == 0)
522			return;
523
524		/* Point stdout-path at the framebuffer node. */
525		node = fdt_find_node("/chosen");
526		fdt_node_add_property(node, "stdout-path",
527		    framebuffer_path, strlen(framebuffer_path) + 1);
528	}
529}
530
531uint64_t dma_constraint[2] = { 0, -1 };
532
533void
534efi_dma_constraint(void)
535{
536	void *node;
537	char *prop;
538	uint32_t *propint;
539	uint64_t base, size;
540	uint32_t pacells, pscells;
541	uint32_t acells, scells;
542	int len;
543
544	node = fdt_find_node("/");
545	if (fdt_node_property_int(node, "#address-cells", &pacells) != 1)
546		pacells = 1;
547	if (fdt_node_property_int(node, "#size-cells", &pscells) != 1)
548		pscells = 1;
549	if (pacells > 2 || pscells > 2)
550		return;
551
552	node = fdt_find_node("/soc");
553	if (node != NULL) {
554		if (fdt_node_property_int(node, "#address-cells", &acells) != 1)
555			acells = pacells;
556		if (fdt_node_property_int(node, "#size-cells", &scells) != 1)
557			scells = pscells;
558		if (acells > 2 || scells > 2)
559			return;
560
561		len = fdt_node_property(node, "dma-ranges", &prop);
562		propint = (uint32_t *)prop;
563		if (len == (acells + pacells + scells) * sizeof(uint32_t)) {
564			base = betoh32(propint[acells]);
565			if (pacells == 2)
566				base = (base << 32) |
567				    betoh32(propint[acells + 1]);
568			size = betoh32(propint[acells + pacells]);
569			if (scells == 2)
570				size = (size << 32) |
571				    betoh32(propint[acells + pacells + 1]);
572
573			dma_constraint[0] = htobe64(base);
574			dma_constraint[1] = htobe64(base + size - 1);
575		}
576	}
577
578	/*
579	 * Some SoC's have DMA constraints that aren't explicitly
580	 * advertised.
581	 */
582	node = fdt_find_node("/");
583	if (fdt_node_is_compatible(node, "brcm,bcm2711"))
584		dma_constraint[1] = htobe64(0x3bffffff);
585	if (fdt_node_is_compatible(node, "rockchip,rk3566") ||
586	    fdt_node_is_compatible(node, "rockchip,rk3568") ||
587	    fdt_node_is_compatible(node, "rockchip,rk3588") ||
588	    fdt_node_is_compatible(node, "rockchip,rk3588s"))
589		dma_constraint[1] = htobe64(0xffffffff);
590	if (fdt_node_is_compatible(node, "lenovo,thinkpad-x13s"))
591		dma_constraint[1] = htobe64(0xffffffff);
592
593	/* Pass DMA constraint. */
594	node = fdt_find_node("/chosen");
595	fdt_node_add_property(node, "openbsd,dma-constraint",
596	    dma_constraint, sizeof(dma_constraint));
597}
598
599int acpi = 0;
600char *bootmac = NULL;
601
602void *
603efi_makebootargs(char *bootargs, int howto)
604{
605	struct sr_boot_volume *bv;
606	u_char bootduid[8];
607	u_char zero[8] = { 0 };
608	uint64_t uefi_system_table = htobe64((uintptr_t)ST);
609	uint32_t boothowto = htobe32(howto);
610	EFI_PHYSICAL_ADDRESS addr;
611	void *node, *fdt;
612	size_t len;
613
614	fdt = efi_fdt();
615	if (fdt == NULL || acpi)
616		fdt = efi_acpi();
617
618	if (!fdt_get_size(fdt))
619		return NULL;
620
621	len = roundup(fdt_get_size(fdt) + PAGE_SIZE, PAGE_SIZE);
622	if (BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
623	    EFI_SIZE_TO_PAGES(len), &addr) == EFI_SUCCESS) {
624		memcpy((void *)addr, fdt, fdt_get_size(fdt));
625		((struct fdt_head *)addr)->fh_size = htobe32(len);
626		fdt = (void *)addr;
627	}
628
629	if (!fdt_init(fdt))
630		return NULL;
631
632	/* Create common nodes which might not exist when using mach dtb */
633	node = fdt_find_node("/aliases");
634	if (node == NULL)
635		fdt_node_add_node(fdt_find_node("/"), "aliases", &node);
636	node = fdt_find_node("/chosen");
637	if (node == NULL)
638		fdt_node_add_node(fdt_find_node("/"), "chosen", &node);
639
640	node = fdt_find_node("/chosen");
641	len = strlen(bootargs) + 1;
642	fdt_node_add_property(node, "bootargs", bootargs, len);
643	fdt_node_add_property(node, "openbsd,boothowto",
644	    &boothowto, sizeof(boothowto));
645
646	/* Pass DUID of the boot disk. */
647	if (bootdev_dip) {
648		memcpy(&bootduid, bootdev_dip->disklabel.d_uid,
649		    sizeof(bootduid));
650		if (memcmp(bootduid, zero, sizeof(bootduid)) != 0) {
651			fdt_node_add_property(node, "openbsd,bootduid",
652			    bootduid, sizeof(bootduid));
653		}
654
655		if (bootdev_dip->sr_vol != NULL) {
656			bv = bootdev_dip->sr_vol;
657			fdt_node_add_property(node, "openbsd,sr-bootuuid",
658			    &bv->sbv_uuid, sizeof(bv->sbv_uuid));
659			if (bv->sbv_maskkey != NULL)
660				fdt_node_add_property(node,
661				    "openbsd,sr-bootkey", bv->sbv_maskkey,
662				    SR_CRYPTO_MAXKEYBYTES);
663		}
664	}
665
666	sr_clear_keys();
667
668	/* Pass netboot interface address. */
669	if (bootmac)
670		fdt_node_add_property(node, "openbsd,bootmac", bootmac, 6);
671
672	/* Pass EFI system table. */
673	fdt_node_add_property(node, "openbsd,uefi-system-table",
674	    &uefi_system_table, sizeof(uefi_system_table));
675
676	/* Placeholders for EFI memory map. */
677	fdt_node_add_property(node, "openbsd,uefi-mmap-start", zero, 8);
678	fdt_node_add_property(node, "openbsd,uefi-mmap-size", zero, 4);
679	fdt_node_add_property(node, "openbsd,uefi-mmap-desc-size", zero, 4);
680	fdt_node_add_property(node, "openbsd,uefi-mmap-desc-ver", zero, 4);
681
682	efi_framebuffer();
683	efi_console();
684	efi_dma_constraint();
685
686	fdt_finalize();
687
688	return fdt;
689}
690
691void
692efi_updatefdt(void)
693{
694	uint64_t uefi_mmap_start = htobe64((uintptr_t)mmap);
695	uint32_t uefi_mmap_size = htobe32(mmap_ndesc * mmap_descsiz);
696	uint32_t uefi_mmap_desc_size = htobe32(mmap_descsiz);
697	uint32_t uefi_mmap_desc_ver = htobe32(mmap_version);
698	void *node;
699
700	node = fdt_find_node("/chosen");
701	if (!node)
702		return;
703
704	/* Pass EFI memory map. */
705	fdt_node_set_property(node, "openbsd,uefi-mmap-start",
706	    &uefi_mmap_start, sizeof(uefi_mmap_start));
707	fdt_node_set_property(node, "openbsd,uefi-mmap-size",
708	    &uefi_mmap_size, sizeof(uefi_mmap_size));
709	fdt_node_set_property(node, "openbsd,uefi-mmap-desc-size",
710	    &uefi_mmap_desc_size, sizeof(uefi_mmap_desc_size));
711	fdt_node_set_property(node, "openbsd,uefi-mmap-desc-ver",
712	    &uefi_mmap_desc_ver, sizeof(uefi_mmap_desc_ver));
713
714	fdt_finalize();
715}
716
717u_long efi_loadaddr;
718
719void
720machdep(void)
721{
722	EFI_PHYSICAL_ADDRESS addr;
723
724	cninit();
725	efi_heap_init();
726	smbios_init(smbios);
727
728	/*
729	 * The kernel expects to be loaded into a block of memory aligned
730	 * on a 2MB boundary.  We allocate a block of 64MB of memory, which
731	 * gives us plenty of room for growth.
732	 */
733	if (efi_memprobe_find(EFI_SIZE_TO_PAGES(64 * 1024 * 1024),
734	    0x200000, EfiLoaderCode, &addr) != EFI_SUCCESS)
735		printf("Can't allocate memory\n");
736	efi_loadaddr = addr;
737
738	efi_timer_init();
739	efi_diskprobe();
740	efi_pxeprobe();
741}
742
743void
744efi_cleanup(void)
745{
746	int		 retry;
747	EFI_STATUS	 status;
748
749	efi_timer_cleanup();
750
751	/* retry once in case of failure */
752	for (retry = 1; retry >= 0; retry--) {
753		efi_memprobe_internal();	/* sync the current map */
754		efi_updatefdt();
755		status = BS->ExitBootServices(IH, mmap_key);
756		if (status == EFI_SUCCESS)
757			break;
758		if (retry == 0)
759			panic("ExitBootServices failed (%d)", status);
760	}
761}
762
763void
764_rtt(void)
765{
766#ifdef EFI_DEBUG
767	printf("Hit any key to reboot\n");
768	efi_cons_getc(0);
769#endif
770	RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
771	for (;;)
772		continue;
773}
774
775/*
776 * U-Boot only implements the GetTime() Runtime Service if it has been
777 * configured with CONFIG_DM_RTC.  Most board configurations don't
778 * include that option, so we can't use it to implement our boot
779 * prompt timeout.  Instead we use timer events to simulate a clock
780 * that ticks ever second.
781 */
782
783EFI_EVENT timer;
784int ticks;
785
786static VOID
787efi_timer(EFI_EVENT event, VOID *context)
788{
789	ticks++;
790}
791
792static void
793efi_timer_init(void)
794{
795	EFI_STATUS status;
796
797	status = BS->CreateEvent(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
798	    efi_timer, NULL, &timer);
799	if (status == EFI_SUCCESS)
800		status = BS->SetTimer(timer, TimerPeriodic, 10000000);
801	if (EFI_ERROR(status))
802		printf("Can't create timer\n");
803}
804
805static void
806efi_timer_cleanup(void)
807{
808	BS->CloseEvent(timer);
809}
810
811time_t
812getsecs(void)
813{
814	return ticks;
815}
816
817/*
818 * Various device-related bits.
819 */
820
821void
822devboot(dev_t dev, char *p)
823{
824	struct sr_boot_volume *bv;
825	struct sr_boot_chunk *bc;
826	struct diskinfo *dip;
827	int sd_boot_vol = 0;
828	int sr_boot_vol = -1;
829	int part_type = FS_UNUSED;
830
831	if (bootdev_dip == NULL) {
832		strlcpy(p, "tftp0a", 7);
833		return;
834	}
835
836	/*
837	 * If there is no BSD disklabel on the boot device, boot from
838	 * the ESP instead.
839	 */
840	if ((bootdev_dip->flags & DISKINFO_FLAG_GOODLABEL) == 0) {
841		strlcpy(p, "esp0a", 6);
842		return;
843	}
844
845	TAILQ_FOREACH(dip, &disklist, list) {
846		if (bootdev_dip == dip)
847			break;
848		sd_boot_vol++;
849	}
850
851	/*
852	 * Determine the partition type for the 'a' partition of the
853	 * boot device.
854	 */
855	part_type = bootdev_dip->disklabel.d_partitions[0].p_fstype;
856
857	/*
858	 * See if we booted from a disk that is a member of a bootable
859	 * softraid volume.
860	 */
861	SLIST_FOREACH(bv, &sr_volumes, sbv_link) {
862		SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link)
863			if (bc->sbc_diskinfo == bootdev_dip)
864				sr_boot_vol = bv->sbv_unit;
865		if (sr_boot_vol != -1)
866			break;
867	}
868
869	if (sr_boot_vol != -1 && part_type != FS_BSDFFS) {
870		strlcpy(p, "sr0a", 5);
871		p[2] = '0' + sr_boot_vol;
872		return;
873	}
874
875	strlcpy(p, "sd0a", 5);
876	p[2] = '0' + sd_boot_vol;
877}
878
879const char cdevs[][4] = { "cons", "com", "fb" };
880const int ncdevs = nitems(cdevs);
881
882int
883cnspeed(dev_t dev, int sp)
884{
885	return 115200;
886}
887
888char ttyname_buf[8];
889
890char *
891ttyname(int fd)
892{
893	snprintf(ttyname_buf, sizeof ttyname_buf, "%s%d",
894	    cdevs[major(cn_tab->cn_dev)], minor(cn_tab->cn_dev));
895
896	return ttyname_buf;
897}
898
899dev_t
900ttydev(char *name)
901{
902	int i, unit = -1;
903	char *no = name + strlen(name) - 1;
904
905	while (no >= name && *no >= '0' && *no <= '9')
906		unit = (unit < 0 ? 0 : (unit * 10)) + *no-- - '0';
907	if (no < name || unit < 0)
908		return NODEV;
909	for (i = 0; i < ncdevs; i++)
910		if (strncmp(name, cdevs[i], no - name + 1) == 0)
911			return makedev(i, unit);
912	return NODEV;
913}
914
915#define MAXDEVNAME	16
916
917/*
918 * Parse a device spec.
919 *
920 * [A-Za-z]*[0-9]*[A-Za-z]:file
921 *    dev   uint    part
922 */
923int
924devparse(const char *fname, int *dev, int *unit, int *part, const char **file)
925{
926	const char *s;
927
928	*unit = 0;	/* default to wd0a */
929	*part = 0;
930	*dev  = 0;
931
932	s = strchr(fname, ':');
933	if (s != NULL) {
934		int devlen;
935		int i, u, p = 0;
936		struct devsw *dp;
937		char devname[MAXDEVNAME];
938
939		devlen = s - fname;
940		if (devlen > MAXDEVNAME)
941			return (EINVAL);
942
943		/* extract device name */
944		for (i = 0; isalpha(fname[i]) && (i < devlen); i++)
945			devname[i] = fname[i];
946		devname[i] = 0;
947
948		if (!isdigit(fname[i]))
949			return (EUNIT);
950
951		/* device number */
952		for (u = 0; isdigit(fname[i]) && (i < devlen); i++)
953			u = u * 10 + (fname[i] - '0');
954
955		if (!isalpha(fname[i]))
956			return (EPART);
957
958		/* partition number */
959		if (i < devlen)
960			p = fname[i++] - 'a';
961
962		if (i != devlen)
963			return (ENXIO);
964
965		/* check device name */
966		for (dp = devsw, i = 0; i < ndevs; dp++, i++) {
967			if (dp->dv_name && !strcmp(devname, dp->dv_name))
968				break;
969		}
970
971		if (i >= ndevs)
972			return (ENXIO);
973
974		*unit = u;
975		*part = p;
976		*dev  = i;
977		fname = ++s;
978	}
979
980	*file = fname;
981
982	return (0);
983}
984
985int
986devopen(struct open_file *f, const char *fname, char **file)
987{
988	struct devsw *dp;
989	int dev, unit, part, error;
990
991	error = devparse(fname, &dev, &unit, &part, (const char **)file);
992	if (error)
993		return (error);
994
995	dp = &devsw[dev];
996	f->f_dev = dp;
997
998	if (strcmp("tftp", dp->dv_name) != 0) {
999		/*
1000		 * Clear bootmac, to signal that we loaded this file from a
1001		 * non-network device.
1002		 */
1003		bootmac = NULL;
1004	}
1005
1006	return (*dp->dv_open)(f, unit, part);
1007}
1008
1009static void
1010efi_memprobe_internal(void)
1011{
1012	EFI_STATUS		 status;
1013	UINTN			 mapkey, mmsiz, siz;
1014	UINT32			 mmver;
1015	EFI_MEMORY_DESCRIPTOR	*mm;
1016	int			 n;
1017
1018	free(mmap, mmap_ndesc * mmap_descsiz);
1019
1020	siz = 0;
1021	status = BS->GetMemoryMap(&siz, NULL, &mapkey, &mmsiz, &mmver);
1022	if (status != EFI_BUFFER_TOO_SMALL)
1023		panic("cannot get the size of memory map");
1024	mm = alloc(siz);
1025	status = BS->GetMemoryMap(&siz, mm, &mapkey, &mmsiz, &mmver);
1026	if (status != EFI_SUCCESS)
1027		panic("cannot get the memory map");
1028	n = siz / mmsiz;
1029	mmap = mm;
1030	mmap_key = mapkey;
1031	mmap_ndesc = n;
1032	mmap_descsiz = mmsiz;
1033	mmap_version = mmver;
1034}
1035
1036/*
1037 * 64-bit ARMs can have a much wider memory mapping, as in somewhere
1038 * after the 32-bit region.  To cope with our alignment requirement,
1039 * use the memory table to find a place where we can fit.
1040 */
1041static EFI_STATUS
1042efi_memprobe_find(UINTN pages, UINTN align, EFI_MEMORY_TYPE type,
1043    EFI_PHYSICAL_ADDRESS *addr)
1044{
1045	EFI_MEMORY_DESCRIPTOR	*mm;
1046	int			 i, j;
1047
1048	if (align < EFI_PAGE_SIZE)
1049		return EFI_INVALID_PARAMETER;
1050
1051	efi_memprobe_internal();	/* sync the current map */
1052
1053	for (i = 0, mm = mmap; i < mmap_ndesc;
1054	    i++, mm = NextMemoryDescriptor(mm, mmap_descsiz)) {
1055		if (mm->Type != EfiConventionalMemory)
1056			continue;
1057
1058		if (mm->NumberOfPages < pages)
1059			continue;
1060
1061		for (j = 0; j < mm->NumberOfPages; j++) {
1062			EFI_PHYSICAL_ADDRESS paddr;
1063
1064			if (mm->NumberOfPages - j < pages)
1065				break;
1066
1067			paddr = mm->PhysicalStart + (j * EFI_PAGE_SIZE);
1068			if (paddr & (align - 1))
1069				continue;
1070
1071			if (BS->AllocatePages(AllocateAddress, type,
1072			    pages, &paddr) == EFI_SUCCESS) {
1073				*addr = paddr;
1074				return EFI_SUCCESS;
1075			}
1076		}
1077	}
1078	return EFI_OUT_OF_RESOURCES;
1079}
1080
1081int
1082mdrandom(char *buf, size_t buflen)
1083{
1084	char *random;
1085	void *node;
1086	int i, len, ret = -1;
1087
1088	node = fdt_find_node("/chosen");
1089	if (!node)
1090		return -1;
1091
1092	len = fdt_node_property(node, "rng-seed", &random);
1093	if (len > 0) {
1094		for (i = 0; i < buflen; i++)
1095			buf[i] ^= random[i % len];
1096		ret = 0;
1097	}
1098
1099	len = fdt_node_property(node, "kaslr-seed", &random);
1100	if (len > 0) {
1101		for (i = 0; i < buflen; i++)
1102			buf[i] ^= random[i % len];
1103		ret = 0;
1104	}
1105
1106	return ret;
1107}
1108
1109#define FW_PATH "/etc/firmware/dtb/"
1110
1111void *
1112efi_fdt(void)
1113{
1114	extern char *hw_vendor, *hw_prod;
1115
1116	/* 'mach dtb' has precedence */
1117	if (fdt_override != NULL)
1118		return fdt_override;
1119
1120	/* Return system provided one */
1121	if (hw_vendor == NULL || hw_prod == NULL)
1122		return fdt_sys;
1123
1124	if (strcmp(hw_vendor, "LENOVO") == 0) {
1125		if (strncmp(hw_prod, "21BX", 4) == 0 ||
1126		    strncmp(hw_prod, "21BY", 4) == 0) {
1127			fdt_load_override(FW_PATH
1128			    "qcom/sc8280xp-lenovo-thinkpad-x13s.dtb");
1129			/* TODO: find a better mechanism */
1130			cnset(ttydev("fb0"));
1131		}
1132	}
1133
1134	return fdt_override ? fdt_override : fdt_sys;
1135}
1136
1137int
1138fdt_load_override(char *file)
1139{
1140	EFI_PHYSICAL_ADDRESS addr;
1141	char path[MAXPATHLEN];
1142	struct stat sb;
1143	int fd;
1144
1145	if (file == NULL && fdt_override) {
1146		BS->FreePages((uint64_t)fdt_override,
1147		    EFI_SIZE_TO_PAGES(fdt_override_size));
1148		fdt_override = NULL;
1149		fdt_init(fdt_sys);
1150		return 0;
1151	}
1152
1153	snprintf(path, sizeof(path), "%s:%s", cmd.bootdev, file);
1154
1155	fd = open(path, O_RDONLY);
1156	if (fd < 0 || fstat(fd, &sb) == -1) {
1157		printf("cannot open %s\n", path);
1158		return 0;
1159	}
1160	if (efi_memprobe_find(EFI_SIZE_TO_PAGES(sb.st_size),
1161	    PAGE_SIZE, EfiLoaderData, &addr) != EFI_SUCCESS) {
1162		printf("cannot allocate memory for %s\n", path);
1163		return 0;
1164	}
1165	if (read(fd, (void *)addr, sb.st_size) != sb.st_size) {
1166		printf("cannot read from %s\n", path);
1167		return 0;
1168	}
1169
1170	if (!fdt_init((void *)addr)) {
1171		printf("invalid device tree\n");
1172		BS->FreePages(addr, EFI_SIZE_TO_PAGES(sb.st_size));
1173		return 0;
1174	}
1175
1176	if (fdt_override) {
1177		BS->FreePages((uint64_t)fdt_override,
1178		    EFI_SIZE_TO_PAGES(fdt_override_size));
1179		fdt_override = NULL;
1180	}
1181
1182	fdt_override = (void *)addr;
1183	fdt_override_size = sb.st_size;
1184	return 0;
1185}
1186
1187/*
1188 * Commands
1189 */
1190
1191int Xacpi_efi(void);
1192int Xdtb_efi(void);
1193int Xexit_efi(void);
1194int Xpoweroff_efi(void);
1195
1196const struct cmd_table cmd_machine[] = {
1197	{ "acpi",	CMDT_CMD, Xacpi_efi },
1198	{ "dtb",	CMDT_CMD, Xdtb_efi },
1199	{ "exit",	CMDT_CMD, Xexit_efi },
1200	{ "poweroff",	CMDT_CMD, Xpoweroff_efi },
1201	{ NULL, 0 }
1202};
1203
1204int
1205Xacpi_efi(void)
1206{
1207	acpi = 1;
1208	return (0);
1209}
1210
1211int
1212Xdtb_efi(void)
1213{
1214	if (cmd.argc == 1) {
1215		fdt_load_override(NULL);
1216		return (0);
1217	}
1218
1219	if (cmd.argc != 2) {
1220		printf("dtb file\n");
1221		return (0);
1222	}
1223
1224	return fdt_load_override(cmd.argv[1]);
1225}
1226
1227int
1228Xexit_efi(void)
1229{
1230	BS->Exit(IH, 0, 0, NULL);
1231	for (;;)
1232		continue;
1233	return (0);
1234}
1235
1236int
1237Xpoweroff_efi(void)
1238{
1239	RS->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
1240	return (0);
1241}
1242