xref: /openbsd/sys/arch/arm64/stand/efiboot/efiboot.c (revision 2bc3fe80)
1*2bc3fe80Skettenis /*	$OpenBSD: efiboot.c,v 1.47 2023/02/15 14:13:38 kettenis Exp $	*/
2f24071e5Spatrick 
3f24071e5Spatrick /*
4f24071e5Spatrick  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
5f24071e5Spatrick  * Copyright (c) 2016 Mark Kettenis
6f24071e5Spatrick  *
7f24071e5Spatrick  * Permission to use, copy, modify, and distribute this software for any
8f24071e5Spatrick  * purpose with or without fee is hereby granted, provided that the above
9f24071e5Spatrick  * copyright notice and this permission notice appear in all copies.
10f24071e5Spatrick  *
11f24071e5Spatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12f24071e5Spatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13f24071e5Spatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14f24071e5Spatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15f24071e5Spatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16f24071e5Spatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17f24071e5Spatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18f24071e5Spatrick  */
19f24071e5Spatrick 
20f24071e5Spatrick #include <sys/param.h>
21f24071e5Spatrick #include <sys/queue.h>
226f83097eSpatrick #include <sys/stat.h>
23f24071e5Spatrick #include <dev/cons.h>
24f24071e5Spatrick #include <sys/disklabel.h>
25f24071e5Spatrick 
26f24071e5Spatrick #include <efi.h>
27f24071e5Spatrick #include <efiapi.h>
28f24071e5Spatrick #include <efiprot.h>
29f24071e5Spatrick #include <eficonsctl.h>
30f24071e5Spatrick 
315b351376Spatrick #include <dev/biovar.h>
325b351376Spatrick #include <dev/softraidvar.h>
335b351376Spatrick 
34f24071e5Spatrick #include <lib/libkern/libkern.h>
355b351376Spatrick #include <lib/libsa/softraid.h>
36f24071e5Spatrick #include <stand/boot/cmd.h>
37f24071e5Spatrick 
385b351376Spatrick #include "libsa.h"
39f24071e5Spatrick #include "disk.h"
405b351376Spatrick #include "softraid_arm64.h"
415b351376Spatrick 
425b351376Spatrick #include "efidev.h"
4333e5575aSpatrick #include "efiboot.h"
44f24071e5Spatrick #include "fdt.h"
45f24071e5Spatrick 
46f24071e5Spatrick EFI_SYSTEM_TABLE	*ST;
47f24071e5Spatrick EFI_BOOT_SERVICES	*BS;
48f24071e5Spatrick EFI_RUNTIME_SERVICES	*RS;
49f673f4ddSkettenis EFI_HANDLE		 IH, efi_bootdp;
504befd8f0Spatrick void			*fdt_sys = NULL;
514befd8f0Spatrick void			*fdt_override = NULL;
524befd8f0Spatrick size_t			 fdt_override_size;
534befd8f0Spatrick void			*smbios = NULL;
54f24071e5Spatrick 
55fa854029Spatrick EFI_PHYSICAL_ADDRESS	 heap;
56fa854029Spatrick UINTN			 heapsiz = 1 * 1024 * 1024;
57f673f4ddSkettenis EFI_MEMORY_DESCRIPTOR	*mmap;
58fa854029Spatrick UINTN			 mmap_key;
59fa854029Spatrick UINTN			 mmap_ndesc;
60fa854029Spatrick UINTN			 mmap_descsiz;
61f4dd1aebSkettenis UINT32			 mmap_version;
62f24071e5Spatrick 
63f24071e5Spatrick static EFI_GUID		 imgp_guid = LOADED_IMAGE_PROTOCOL;
64f24071e5Spatrick static EFI_GUID		 blkio_guid = BLOCK_IO_PROTOCOL;
65f24071e5Spatrick static EFI_GUID		 devp_guid = DEVICE_PATH_PROTOCOL;
661e44dc38Skettenis static EFI_GUID		 gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
67f3468426Skettenis static EFI_GUID		 fdt_guid = FDT_TABLE_GUID;
684befd8f0Spatrick static EFI_GUID		 smbios_guid = SMBIOS_TABLE_GUID;
694befd8f0Spatrick static EFI_GUID		 smbios3_guid = SMBIOS3_TABLE_GUID;
70f3468426Skettenis 
7149dff005Sderaadt #define efi_guidcmp(_a, _b)	memcmp((_a), (_b), sizeof(EFI_GUID))
72f24071e5Spatrick 
7333e5575aSpatrick int efi_device_path_depth(EFI_DEVICE_PATH *dp, int);
7433e5575aSpatrick int efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int);
75fa854029Spatrick static void efi_heap_init(void);
76fa854029Spatrick static void efi_memprobe_internal(void);
77f24071e5Spatrick static void efi_timer_init(void);
78f24071e5Spatrick static void efi_timer_cleanup(void);
792c32ef33Skettenis static EFI_STATUS efi_memprobe_find(UINTN, UINTN, EFI_MEMORY_TYPE,
802c32ef33Skettenis     EFI_PHYSICAL_ADDRESS *);
814befd8f0Spatrick void *efi_fdt(void);
824befd8f0Spatrick int fdt_load_override(char *);
834befd8f0Spatrick extern void smbios_init(void *);
84f24071e5Spatrick 
85f24071e5Spatrick EFI_STATUS
86f24071e5Spatrick efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
87f24071e5Spatrick {
88f24071e5Spatrick 	extern char		*progname;
89f24071e5Spatrick 	EFI_LOADED_IMAGE	*imgp;
90f24071e5Spatrick 	EFI_DEVICE_PATH		*dp = NULL;
91f24071e5Spatrick 	EFI_STATUS		 status;
92f3468426Skettenis 	int			 i;
93f24071e5Spatrick 
94f24071e5Spatrick 	ST = systab;
95f24071e5Spatrick 	BS = ST->BootServices;
96496ffee0Spatrick 	RS = ST->RuntimeServices;
97f24071e5Spatrick 	IH = image;
98f24071e5Spatrick 
99f0da534cSjsg 	/* disable reset by watchdog after 5 minutes */
1001f462730Skrw 	BS->SetWatchdogTimer(0, 0, 0, NULL);
101f0da534cSjsg 
1021f462730Skrw 	status = BS->HandleProtocol(image, &imgp_guid,
103f24071e5Spatrick 	    (void **)&imgp);
104f24071e5Spatrick 	if (status == EFI_SUCCESS)
1051f462730Skrw 		status = BS->HandleProtocol(imgp->DeviceHandle, &devp_guid,
1061f462730Skrw 		    (void **)&dp);
107f24071e5Spatrick 	if (status == EFI_SUCCESS)
108f24071e5Spatrick 		efi_bootdp = dp;
109f24071e5Spatrick 
110f3468426Skettenis 	for (i = 0; i < ST->NumberOfTableEntries; i++) {
111f3468426Skettenis 		if (efi_guidcmp(&fdt_guid,
112f3468426Skettenis 		    &ST->ConfigurationTable[i].VendorGuid) == 0)
1134befd8f0Spatrick 			fdt_sys = ST->ConfigurationTable[i].VendorTable;
1144befd8f0Spatrick 		if (efi_guidcmp(&smbios_guid,
1154befd8f0Spatrick 		    &ST->ConfigurationTable[i].VendorGuid) == 0)
1164befd8f0Spatrick 			smbios = ST->ConfigurationTable[i].VendorTable;
1174befd8f0Spatrick 		if (efi_guidcmp(&smbios3_guid,
1184befd8f0Spatrick 		    &ST->ConfigurationTable[i].VendorGuid) == 0)
1194befd8f0Spatrick 			smbios = ST->ConfigurationTable[i].VendorTable;
120f3468426Skettenis 	}
1214befd8f0Spatrick 	fdt_init(fdt_sys);
122f3468426Skettenis 
123f24071e5Spatrick 	progname = "BOOTAA64";
124f24071e5Spatrick 
125f24071e5Spatrick 	boot(0);
126f24071e5Spatrick 
127f24071e5Spatrick 	return (EFI_SUCCESS);
128f24071e5Spatrick }
129f24071e5Spatrick 
130f24071e5Spatrick static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
131f24071e5Spatrick static SIMPLE_INPUT_INTERFACE *conin;
132f24071e5Spatrick 
1335cfe0970Skettenis /*
1345cfe0970Skettenis  * The device majors for these don't match the ones used by the
1355cfe0970Skettenis  * kernel.  That's fine.  They're just used as an index into the cdevs
1365cfe0970Skettenis  * array and never passed on to the kernel.
1375cfe0970Skettenis  */
138c7045f51Skettenis static dev_t serial = makedev(1, 0);
139c7045f51Skettenis static dev_t framebuffer = makedev(2, 0);
1405cfe0970Skettenis 
1415f7ab753Skettenis static char framebuffer_path[128];
1425f7ab753Skettenis 
143f24071e5Spatrick void
144f24071e5Spatrick efi_cons_probe(struct consdev *cn)
145f24071e5Spatrick {
146f24071e5Spatrick 	cn->cn_pri = CN_MIDPRI;
147c7045f51Skettenis 	cn->cn_dev = makedev(0, 0);
148f24071e5Spatrick }
149f24071e5Spatrick 
150f24071e5Spatrick void
151f24071e5Spatrick efi_cons_init(struct consdev *cp)
152f24071e5Spatrick {
153f24071e5Spatrick 	conin = ST->ConIn;
154f24071e5Spatrick 	conout = ST->ConOut;
155f24071e5Spatrick }
156f24071e5Spatrick 
157f24071e5Spatrick int
158f24071e5Spatrick efi_cons_getc(dev_t dev)
159f24071e5Spatrick {
160f24071e5Spatrick 	EFI_INPUT_KEY	 key;
161f24071e5Spatrick 	EFI_STATUS	 status;
162f24071e5Spatrick #if 0
163f24071e5Spatrick 	UINTN		 dummy;
164f24071e5Spatrick #endif
165f24071e5Spatrick 	static int	 lastchar = 0;
166f24071e5Spatrick 
167f24071e5Spatrick 	if (lastchar) {
168f24071e5Spatrick 		int r = lastchar;
169f24071e5Spatrick 		if ((dev & 0x80) == 0)
170f24071e5Spatrick 			lastchar = 0;
171f24071e5Spatrick 		return (r);
172f24071e5Spatrick 	}
173f24071e5Spatrick 
174f24071e5Spatrick 	status = conin->ReadKeyStroke(conin, &key);
175678d37b9Syasuoka 	while (status == EFI_NOT_READY || key.UnicodeChar == 0) {
176f24071e5Spatrick 		if (dev & 0x80)
177f24071e5Spatrick 			return (0);
178f24071e5Spatrick 		/*
179f24071e5Spatrick 		 * XXX The implementation of WaitForEvent() in U-boot
180f24071e5Spatrick 		 * is broken and neverreturns.
181f24071e5Spatrick 		 */
182f24071e5Spatrick #if 0
183f24071e5Spatrick 		BS->WaitForEvent(1, &conin->WaitForKey, &dummy);
184f24071e5Spatrick #endif
185f24071e5Spatrick 		status = conin->ReadKeyStroke(conin, &key);
186f24071e5Spatrick 	}
187f24071e5Spatrick 
188f24071e5Spatrick 	if (dev & 0x80)
189f24071e5Spatrick 		lastchar = key.UnicodeChar;
190f24071e5Spatrick 
191f24071e5Spatrick 	return (key.UnicodeChar);
192f24071e5Spatrick }
193f24071e5Spatrick 
194f24071e5Spatrick void
195f24071e5Spatrick efi_cons_putc(dev_t dev, int c)
196f24071e5Spatrick {
197f24071e5Spatrick 	CHAR16	buf[2];
198f24071e5Spatrick 
199f24071e5Spatrick 	if (c == '\n')
200f24071e5Spatrick 		efi_cons_putc(dev, '\r');
201f24071e5Spatrick 
202f24071e5Spatrick 	buf[0] = c;
203f24071e5Spatrick 	buf[1] = 0;
204f24071e5Spatrick 
205f24071e5Spatrick 	conout->OutputString(conout, buf);
206f24071e5Spatrick }
207f24071e5Spatrick 
2085cfe0970Skettenis void
209c7045f51Skettenis efi_com_probe(struct consdev *cn)
210c7045f51Skettenis {
211c7045f51Skettenis 	cn->cn_pri = CN_LOWPRI;
212c7045f51Skettenis 	cn->cn_dev = serial;
213c7045f51Skettenis }
214c7045f51Skettenis 
215c7045f51Skettenis void
216c7045f51Skettenis efi_com_init(struct consdev *cn)
217c7045f51Skettenis {
218c7045f51Skettenis 	conin = ST->ConIn;
219c7045f51Skettenis 	conout = ST->ConOut;
220c7045f51Skettenis }
221c7045f51Skettenis 
222c7045f51Skettenis int
223c7045f51Skettenis efi_com_getc(dev_t dev)
224c7045f51Skettenis {
225c7045f51Skettenis 	return efi_cons_getc(dev);
226c7045f51Skettenis }
227c7045f51Skettenis 
228c7045f51Skettenis void
229c7045f51Skettenis efi_com_putc(dev_t dev, int c)
230c7045f51Skettenis {
231c7045f51Skettenis 	efi_cons_putc(dev, c);
232c7045f51Skettenis }
233c7045f51Skettenis 
234c7045f51Skettenis void
2355cfe0970Skettenis efi_fb_probe(struct consdev *cn)
2365cfe0970Skettenis {
2375cfe0970Skettenis 	cn->cn_pri = CN_LOWPRI;
2385cfe0970Skettenis 	cn->cn_dev = framebuffer;
2395cfe0970Skettenis }
2405cfe0970Skettenis 
2415cfe0970Skettenis void
2425cfe0970Skettenis efi_fb_init(struct consdev *cn)
2435cfe0970Skettenis {
2445cfe0970Skettenis 	conin = ST->ConIn;
2455cfe0970Skettenis 	conout = ST->ConOut;
2465cfe0970Skettenis }
2475cfe0970Skettenis 
2485cfe0970Skettenis int
2495cfe0970Skettenis efi_fb_getc(dev_t dev)
2505cfe0970Skettenis {
2515cfe0970Skettenis 	return efi_cons_getc(dev);
2525cfe0970Skettenis }
2535cfe0970Skettenis 
2545cfe0970Skettenis void
2555cfe0970Skettenis efi_fb_putc(dev_t dev, int c)
2565cfe0970Skettenis {
2575cfe0970Skettenis 	efi_cons_putc(dev, c);
2585cfe0970Skettenis }
2595cfe0970Skettenis 
260f24071e5Spatrick static void
261f24071e5Spatrick efi_heap_init(void)
262f24071e5Spatrick {
263f24071e5Spatrick 	EFI_STATUS	 status;
264f24071e5Spatrick 
2651f462730Skrw 	status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
266f24071e5Spatrick 	    EFI_SIZE_TO_PAGES(heapsiz), &heap);
267f24071e5Spatrick 	if (status != EFI_SUCCESS)
268f24071e5Spatrick 		panic("BS->AllocatePages()");
269f24071e5Spatrick }
270f24071e5Spatrick 
2715b351376Spatrick struct disklist_lh disklist;
2725b351376Spatrick struct diskinfo *bootdev_dip;
273f24071e5Spatrick 
274f24071e5Spatrick void
275f24071e5Spatrick efi_diskprobe(void)
276f24071e5Spatrick {
2775b351376Spatrick 	int			 i, bootdev = 0, depth = -1;
278f24071e5Spatrick 	UINTN			 sz;
279f24071e5Spatrick 	EFI_STATUS		 status;
280f24071e5Spatrick 	EFI_HANDLE		*handles = NULL;
281f24071e5Spatrick 	EFI_BLOCK_IO		*blkio;
282f24071e5Spatrick 	EFI_BLOCK_IO_MEDIA	*media;
2835b351376Spatrick 	struct diskinfo		*di;
284c6d7d0adSpatrick 	EFI_DEVICE_PATH		*dp;
285f24071e5Spatrick 
2865b351376Spatrick 	TAILQ_INIT(&disklist);
2875b351376Spatrick 
288f24071e5Spatrick 	sz = 0;
2891f462730Skrw 	status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, 0);
290f24071e5Spatrick 	if (status == EFI_BUFFER_TOO_SMALL) {
291f24071e5Spatrick 		handles = alloc(sz);
2921f462730Skrw 		status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
2931f462730Skrw 		    handles);
294f24071e5Spatrick 	}
295f24071e5Spatrick 	if (handles == NULL || EFI_ERROR(status))
29633e5575aSpatrick 		return;
297f24071e5Spatrick 
298c6d7d0adSpatrick 	if (efi_bootdp != NULL)
299c6d7d0adSpatrick 		depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
300c6d7d0adSpatrick 
301c94e7257Skettenis 	/*
302c94e7257Skettenis 	 * U-Boot incorrectly represents devices with a single
303c94e7257Skettenis 	 * MEDIA_DEVICE_PATH component.  In that case include that
304c94e7257Skettenis 	 * component into the matching, otherwise we'll blindly select
305c94e7257Skettenis 	 * the first device.
306c94e7257Skettenis 	 */
307c94e7257Skettenis 	if (depth == 0)
308c94e7257Skettenis 		depth = 1;
309c94e7257Skettenis 
310f24071e5Spatrick 	for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) {
3111f462730Skrw 		status = BS->HandleProtocol(handles[i], &blkio_guid,
312f24071e5Spatrick 		    (void **)&blkio);
313f24071e5Spatrick 		if (EFI_ERROR(status))
314f24071e5Spatrick 			panic("BS->HandleProtocol() returns %d", status);
315f24071e5Spatrick 
316f24071e5Spatrick 		media = blkio->Media;
317f24071e5Spatrick 		if (media->LogicalPartition || !media->MediaPresent)
318f24071e5Spatrick 			continue;
3195b351376Spatrick 		di = alloc(sizeof(struct diskinfo));
3205b351376Spatrick 		efid_init(di, blkio);
321f24071e5Spatrick 
3225b351376Spatrick 		if (efi_bootdp == NULL || depth == -1 || bootdev != 0)
3235b351376Spatrick 			goto next;
3241f462730Skrw 		status = BS->HandleProtocol(handles[i], &devp_guid,
325f24071e5Spatrick 		    (void **)&dp);
326f24071e5Spatrick 		if (EFI_ERROR(status))
3275b351376Spatrick 			goto next;
328c6d7d0adSpatrick 		if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) {
3295b351376Spatrick 			TAILQ_INSERT_HEAD(&disklist, di, list);
3305b351376Spatrick 			bootdev_dip = di;
3315b351376Spatrick 			bootdev = 1;
3325b351376Spatrick 			continue;
333f24071e5Spatrick 		}
3345b351376Spatrick next:
3355b351376Spatrick 		TAILQ_INSERT_TAIL(&disklist, di, list);
336f24071e5Spatrick 	}
337f24071e5Spatrick 
338f24071e5Spatrick 	free(handles, sz);
3395b351376Spatrick 
3405b351376Spatrick 	/* Print available disks and probe for softraid. */
3415b351376Spatrick 	i = 0;
3425b351376Spatrick 	printf("disks:");
3435b351376Spatrick 	TAILQ_FOREACH(di, &disklist, list) {
3445b351376Spatrick 		printf(" sd%d%s", i, di == bootdev_dip ? "*" : "");
3455b351376Spatrick 		i++;
3465b351376Spatrick 	}
3475b351376Spatrick 	srprobe();
3485b351376Spatrick 	printf("\n");
349f24071e5Spatrick }
350f24071e5Spatrick 
351c94e7257Skettenis /*
352c94e7257Skettenis  * Determine the number of nodes up to, but not including, the first
353c94e7257Skettenis  * node of the specified type.
354c94e7257Skettenis  */
35533e5575aSpatrick int
356c6d7d0adSpatrick efi_device_path_depth(EFI_DEVICE_PATH *dp, int dptype)
357c6d7d0adSpatrick {
358c6d7d0adSpatrick 	int	i;
359c6d7d0adSpatrick 
360c6d7d0adSpatrick 	for (i = 0; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp), i++) {
361c6d7d0adSpatrick 		if (DevicePathType(dp) == dptype)
362c94e7257Skettenis 			return (i);
363c6d7d0adSpatrick 	}
364c6d7d0adSpatrick 
365d64e7de5Skettenis 	return (i);
366c6d7d0adSpatrick }
367c6d7d0adSpatrick 
36833e5575aSpatrick int
369c6d7d0adSpatrick efi_device_path_ncmp(EFI_DEVICE_PATH *dpa, EFI_DEVICE_PATH *dpb, int deptn)
370c6d7d0adSpatrick {
371c6d7d0adSpatrick 	int	 i, cmp;
372c6d7d0adSpatrick 
373c6d7d0adSpatrick 	for (i = 0; i < deptn; i++) {
374c6d7d0adSpatrick 		if (IsDevicePathEnd(dpa) || IsDevicePathEnd(dpb))
375c6d7d0adSpatrick 			return ((IsDevicePathEnd(dpa) && IsDevicePathEnd(dpb))
376c6d7d0adSpatrick 			    ? 0 : (IsDevicePathEnd(dpa))? -1 : 1);
377c6d7d0adSpatrick 		cmp = DevicePathNodeLength(dpa) - DevicePathNodeLength(dpb);
378c6d7d0adSpatrick 		if (cmp)
379c6d7d0adSpatrick 			return (cmp);
380c6d7d0adSpatrick 		cmp = memcmp(dpa, dpb, DevicePathNodeLength(dpa));
381c6d7d0adSpatrick 		if (cmp)
382c6d7d0adSpatrick 			return (cmp);
383c6d7d0adSpatrick 		dpa = NextDevicePathNode(dpa);
384c6d7d0adSpatrick 		dpb = NextDevicePathNode(dpb);
385c6d7d0adSpatrick 	}
386c6d7d0adSpatrick 
387c6d7d0adSpatrick 	return (0);
388c6d7d0adSpatrick }
389c6d7d0adSpatrick 
3901e44dc38Skettenis void
3911e44dc38Skettenis efi_framebuffer(void)
3921e44dc38Skettenis {
3931e44dc38Skettenis 	EFI_GRAPHICS_OUTPUT *gop;
3941e44dc38Skettenis 	EFI_STATUS status;
3951e44dc38Skettenis 	void *node, *child;
3961e44dc38Skettenis 	uint32_t acells, scells;
3971e44dc38Skettenis 	uint64_t base, size;
3981e44dc38Skettenis 	uint32_t reg[4];
3991e44dc38Skettenis 	uint32_t width, height, stride;
4001e44dc38Skettenis 	char *format;
4015cfe0970Skettenis 	char *prop;
4021e44dc38Skettenis 
4031e44dc38Skettenis 	/*
4041e44dc38Skettenis 	 * Don't create a "simple-framebuffer" node if we already have
4051e44dc38Skettenis 	 * one.  Besides "/chosen", we also check under "/" since that
4061e44dc38Skettenis 	 * is where the Raspberry Pi firmware puts it.
4071e44dc38Skettenis 	 */
4081e44dc38Skettenis 	node = fdt_find_node("/chosen");
4091e44dc38Skettenis 	for (child = fdt_child_node(node); child;
4101e44dc38Skettenis 	     child = fdt_next_node(child)) {
4115cfe0970Skettenis 		if (!fdt_node_is_compatible(child, "simple-framebuffer"))
4125cfe0970Skettenis 			continue;
4135028e024Skettenis 		if (!fdt_node_property(child, "status", &prop) ||
4145f7ab753Skettenis 		    strcmp(prop, "okay") == 0) {
4155f7ab753Skettenis 			strlcpy(framebuffer_path, "/chosen/",
4165f7ab753Skettenis 			    sizeof(framebuffer_path));
4175f7ab753Skettenis 			strlcat(framebuffer_path, fdt_node_name(child),
4185f7ab753Skettenis 			    sizeof(framebuffer_path));
4191e44dc38Skettenis 			return;
4201e44dc38Skettenis 		}
4215f7ab753Skettenis 	}
4221e44dc38Skettenis 	node = fdt_find_node("/");
4231e44dc38Skettenis 	for (child = fdt_child_node(node); child;
4241e44dc38Skettenis 	     child = fdt_next_node(child)) {
4255cfe0970Skettenis 		if (!fdt_node_is_compatible(child, "simple-framebuffer"))
4265cfe0970Skettenis 			continue;
4275028e024Skettenis 		if (!fdt_node_property(child, "status", &prop) ||
4285f7ab753Skettenis 		    strcmp(prop, "okay") == 0) {
4295f7ab753Skettenis 			strlcpy(framebuffer_path, "/",
4305f7ab753Skettenis 			    sizeof(framebuffer_path));
4315f7ab753Skettenis 			strlcat(framebuffer_path, fdt_node_name(child),
4325f7ab753Skettenis 			    sizeof(framebuffer_path));
4331e44dc38Skettenis 			return;
4341e44dc38Skettenis 		}
4355f7ab753Skettenis 	}
4361e44dc38Skettenis 
4371f462730Skrw 	status = BS->LocateProtocol(&gop_guid, NULL, (void **)&gop);
4381e44dc38Skettenis 	if (status != EFI_SUCCESS)
4391e44dc38Skettenis 		return;
4401e44dc38Skettenis 
4411e44dc38Skettenis 	/* Paranoia! */
4421e44dc38Skettenis 	if (gop == NULL || gop->Mode == NULL || gop->Mode->Info == NULL)
4431e44dc38Skettenis 		return;
4441e44dc38Skettenis 
4451e44dc38Skettenis 	/* We only support 32-bit pixel modes for now. */
4461e44dc38Skettenis 	switch (gop->Mode->Info->PixelFormat) {
4471e44dc38Skettenis 	case PixelRedGreenBlueReserved8BitPerColor:
448b870cd26Skettenis 		format = "x8b8g8r8";
4491e44dc38Skettenis 		break;
4501e44dc38Skettenis 	case PixelBlueGreenRedReserved8BitPerColor:
451b870cd26Skettenis 		format = "x8r8g8b8";
4521e44dc38Skettenis 		break;
4531e44dc38Skettenis 	default:
4541e44dc38Skettenis 		return;
4551e44dc38Skettenis 	}
4561e44dc38Skettenis 
4571e44dc38Skettenis 	base = gop->Mode->FrameBufferBase;
4581e44dc38Skettenis 	size = gop->Mode->FrameBufferSize;
4591e44dc38Skettenis 	width = htobe32(gop->Mode->Info->HorizontalResolution);
4601e44dc38Skettenis 	height = htobe32(gop->Mode->Info->VerticalResolution);
4611e44dc38Skettenis 	stride = htobe32(gop->Mode->Info->PixelsPerScanLine * 4);
4621e44dc38Skettenis 
4631e44dc38Skettenis 	node = fdt_find_node("/");
4641e44dc38Skettenis 	if (fdt_node_property_int(node, "#address-cells", &acells) != 1)
4651e44dc38Skettenis 		acells = 1;
4661e44dc38Skettenis 	if (fdt_node_property_int(node, "#size-cells", &scells) != 1)
4671e44dc38Skettenis 		scells = 1;
4681e44dc38Skettenis 	if (acells > 2 || scells > 2)
4691e44dc38Skettenis 		return;
4701e44dc38Skettenis 	if (acells >= 1)
4711e44dc38Skettenis 		reg[0] = htobe32(base);
4721e44dc38Skettenis 	if (acells == 2) {
4731e44dc38Skettenis 		reg[1] = reg[0];
4741e44dc38Skettenis 		reg[0] = htobe32(base >> 32);
4751e44dc38Skettenis 	}
4761e44dc38Skettenis 	if (scells >= 1)
4771e44dc38Skettenis 		reg[acells] = htobe32(size);
4781e44dc38Skettenis 	if (scells == 2) {
4791e44dc38Skettenis 		reg[acells + 1] = reg[acells];
4801e44dc38Skettenis 		reg[acells] = htobe32(size >> 32);
4811e44dc38Skettenis 	}
4821e44dc38Skettenis 
4831e44dc38Skettenis 	node = fdt_find_node("/chosen");
4841e44dc38Skettenis 	fdt_node_add_node(node, "framebuffer", &child);
4851e44dc38Skettenis 	fdt_node_add_property(child, "status", "okay", strlen("okay") + 1);
4861e44dc38Skettenis 	fdt_node_add_property(child, "format", format, strlen(format) + 1);
4871e44dc38Skettenis 	fdt_node_add_property(child, "stride", &stride, 4);
4881e44dc38Skettenis 	fdt_node_add_property(child, "height", &height, 4);
4891e44dc38Skettenis 	fdt_node_add_property(child, "width", &width, 4);
4901e44dc38Skettenis 	fdt_node_add_property(child, "reg", reg, (acells + scells) * 4);
4911e44dc38Skettenis 	fdt_node_add_property(child, "compatible",
4921e44dc38Skettenis 	    "simple-framebuffer", strlen("simple-framebuffer") + 1);
4935f7ab753Skettenis 
4945f7ab753Skettenis 	strlcpy(framebuffer_path, "/chosen/framebuffer",
4955f7ab753Skettenis 	    sizeof(framebuffer_path));
4961e44dc38Skettenis }
4971e44dc38Skettenis 
4985cfe0970Skettenis void
4995cfe0970Skettenis efi_console(void)
5005cfe0970Skettenis {
5015f7ab753Skettenis 	void *node;
5025cfe0970Skettenis 
503c7045f51Skettenis 	if (major(cn_tab->cn_dev) == major(serial)) {
504c7045f51Skettenis 		char *serial_path;
505c7045f51Skettenis 		char alias[16];
506c7045f51Skettenis 		int len;
507c7045f51Skettenis 
508c7045f51Skettenis 		/* Construct alias and resolve it. */
509c7045f51Skettenis 		snprintf(alias, sizeof(alias), "serial%d",
510c7045f51Skettenis 		    minor(cn_tab->cn_dev));
511c7045f51Skettenis 		node = fdt_find_node("/aliases");
512c7045f51Skettenis 		len = fdt_node_property(node, alias, &serial_path);
513c7045f51Skettenis 		if (len <= 0)
5145cfe0970Skettenis 			return;
5155cfe0970Skettenis 
516c7045f51Skettenis 		/* Point stdout-path at the serial node. */
517c7045f51Skettenis 		node = fdt_find_node("/chosen");
518c7045f51Skettenis 		fdt_node_add_property(node, "stdout-path",
519c7045f51Skettenis 		    serial_path, strlen(serial_path) + 1);
520c7045f51Skettenis 	} else if (major(cn_tab->cn_dev) == major(framebuffer)) {
5215f7ab753Skettenis 		if (strlen(framebuffer_path) == 0)
5225cfe0970Skettenis 			return;
5235cfe0970Skettenis 
5245cfe0970Skettenis 		/* Point stdout-path at the framebuffer node. */
5255f7ab753Skettenis 		node = fdt_find_node("/chosen");
5265f7ab753Skettenis 		fdt_node_add_property(node, "stdout-path",
5275f7ab753Skettenis 		    framebuffer_path, strlen(framebuffer_path) + 1);
5285cfe0970Skettenis 	}
529c7045f51Skettenis }
5305cfe0970Skettenis 
531a88d1494Skettenis uint64_t dma_constraint[2] = { 0, -1 };
532a88d1494Skettenis 
533a88d1494Skettenis void
534a88d1494Skettenis efi_dma_constraint(void)
535a88d1494Skettenis {
536a88d1494Skettenis 	void *node;
537cfb73cfeSpatrick 	char *prop;
538cfb73cfeSpatrick 	uint32_t *propint;
539cfb73cfeSpatrick 	uint64_t base, size;
540cfb73cfeSpatrick 	uint32_t pacells, pscells;
541cfb73cfeSpatrick 	uint32_t acells, scells;
542cfb73cfeSpatrick 	int len;
543cfb73cfeSpatrick 
544cfb73cfeSpatrick 	node = fdt_find_node("/");
545cfb73cfeSpatrick 	if (fdt_node_property_int(node, "#address-cells", &pacells) != 1)
546cfb73cfeSpatrick 		pacells = 1;
547cfb73cfeSpatrick 	if (fdt_node_property_int(node, "#size-cells", &pscells) != 1)
548cfb73cfeSpatrick 		pscells = 1;
549cfb73cfeSpatrick 	if (pacells > 2 || pscells > 2)
550cfb73cfeSpatrick 		return;
551cfb73cfeSpatrick 
552cfb73cfeSpatrick 	node = fdt_find_node("/soc");
553cfb73cfeSpatrick 	if (node != NULL) {
554cfb73cfeSpatrick 		if (fdt_node_property_int(node, "#address-cells", &acells) != 1)
555cfb73cfeSpatrick 			acells = pacells;
556cfb73cfeSpatrick 		if (fdt_node_property_int(node, "#size-cells", &scells) != 1)
557cfb73cfeSpatrick 			scells = pscells;
558cfb73cfeSpatrick 		if (acells > 2 || scells > 2)
559cfb73cfeSpatrick 			return;
560cfb73cfeSpatrick 
561cfb73cfeSpatrick 		len = fdt_node_property(node, "dma-ranges", &prop);
562cfb73cfeSpatrick 		propint = (uint32_t *)prop;
563cfb73cfeSpatrick 		if (len == (acells + pacells + scells) * sizeof(uint32_t)) {
564cfb73cfeSpatrick 			base = betoh32(propint[acells]);
565cfb73cfeSpatrick 			if (pacells == 2)
566cfb73cfeSpatrick 				base = (base << 32) |
567cfb73cfeSpatrick 				    betoh32(propint[acells + 1]);
568cfb73cfeSpatrick 			size = betoh32(propint[acells + pacells]);
569cfb73cfeSpatrick 			if (scells == 2)
570cfb73cfeSpatrick 				size = (size << 32) |
571cfb73cfeSpatrick 				    betoh32(propint[acells + pacells + 1]);
572cfb73cfeSpatrick 
573cfb73cfeSpatrick 			dma_constraint[0] = htobe64(base);
574cfb73cfeSpatrick 			dma_constraint[1] = htobe64(base + size - 1);
575cfb73cfeSpatrick 		}
576cfb73cfeSpatrick 	}
577a88d1494Skettenis 
578*2bc3fe80Skettenis 	/*
579*2bc3fe80Skettenis 	 * Some SoC's have DMA constraints that aren't explicitly
580*2bc3fe80Skettenis 	 * advertised.
581*2bc3fe80Skettenis 	 */
582a88d1494Skettenis 	node = fdt_find_node("/");
583a88d1494Skettenis 	if (fdt_node_is_compatible(node, "brcm,bcm2711"))
584a88d1494Skettenis 		dma_constraint[1] = htobe64(0x3bffffff);
585*2bc3fe80Skettenis 	if (fdt_node_is_compatible(node, "rockchip,rk3566") ||
586*2bc3fe80Skettenis 	    fdt_node_is_compatible(node, "rockchip,rk3568") ||
587*2bc3fe80Skettenis 	    fdt_node_is_compatible(node, "rockchip,rk3588") ||
588*2bc3fe80Skettenis 	    fdt_node_is_compatible(node, "rockchip,rk3588s"))
589*2bc3fe80Skettenis 		dma_constraint[1] = htobe64(0xffffffff);
590a88d1494Skettenis 
591a88d1494Skettenis 	/* Pass DMA constraint. */
592a88d1494Skettenis 	node = fdt_find_node("/chosen");
593a88d1494Skettenis 	fdt_node_add_property(node, "openbsd,dma-constraint",
594a88d1494Skettenis 	    dma_constraint, sizeof(dma_constraint));
595a88d1494Skettenis }
596a88d1494Skettenis 
597be07ee62Skettenis int acpi = 0;
5989ccb13abSnaddy char *bootmac = NULL;
599f24071e5Spatrick 
600f24071e5Spatrick void *
6019419d8aaSkettenis efi_makebootargs(char *bootargs, int howto)
602f24071e5Spatrick {
6035b351376Spatrick 	struct sr_boot_volume *bv;
604f4dd1aebSkettenis 	u_char bootduid[8];
605f4dd1aebSkettenis 	u_char zero[8] = { 0 };
606f4dd1aebSkettenis 	uint64_t uefi_system_table = htobe64((uintptr_t)ST);
6079419d8aaSkettenis 	uint32_t boothowto = htobe32(howto);
60858554311Spatrick 	EFI_PHYSICAL_ADDRESS addr;
6094befd8f0Spatrick 	void *node, *fdt;
610f24071e5Spatrick 	size_t len;
611f24071e5Spatrick 
6124befd8f0Spatrick 	fdt = efi_fdt();
613be07ee62Skettenis 	if (fdt == NULL || acpi)
6149a1480b1Skettenis 		fdt = efi_acpi();
6159a1480b1Skettenis 
61658554311Spatrick 	if (!fdt_get_size(fdt))
61758554311Spatrick 		return NULL;
61858554311Spatrick 
61958554311Spatrick 	len = roundup(fdt_get_size(fdt) + PAGE_SIZE, PAGE_SIZE);
62058554311Spatrick 	if (BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
62158554311Spatrick 	    EFI_SIZE_TO_PAGES(len), &addr) == EFI_SUCCESS) {
62258554311Spatrick 		memcpy((void *)addr, fdt, fdt_get_size(fdt));
623259d2111Skettenis 		((struct fdt_head *)addr)->fh_size = htobe32(len);
62458554311Spatrick 		fdt = (void *)addr;
62558554311Spatrick 	}
62658554311Spatrick 
627f24071e5Spatrick 	if (!fdt_init(fdt))
628f24071e5Spatrick 		return NULL;
629f24071e5Spatrick 
630bd928107Spatrick 	/* Create common nodes which might not exist when using mach dtb */
631bd928107Spatrick 	node = fdt_find_node("/aliases");
632bd928107Spatrick 	if (node == NULL)
633bd928107Spatrick 		fdt_node_add_node(fdt_find_node("/"), "aliases", &node);
634f24071e5Spatrick 	node = fdt_find_node("/chosen");
635bd928107Spatrick 	if (node == NULL)
636bd928107Spatrick 		fdt_node_add_node(fdt_find_node("/"), "chosen", &node);
637f24071e5Spatrick 
638bd928107Spatrick 	node = fdt_find_node("/chosen");
639f24071e5Spatrick 	len = strlen(bootargs) + 1;
640f24071e5Spatrick 	fdt_node_add_property(node, "bootargs", bootargs, len);
6419419d8aaSkettenis 	fdt_node_add_property(node, "openbsd,boothowto",
6429419d8aaSkettenis 	    &boothowto, sizeof(boothowto));
643f24071e5Spatrick 
644f24071e5Spatrick 	/* Pass DUID of the boot disk. */
6455b351376Spatrick 	if (bootdev_dip) {
6465b351376Spatrick 		memcpy(&bootduid, bootdev_dip->disklabel.d_uid,
647f24071e5Spatrick 		    sizeof(bootduid));
6485b351376Spatrick 		if (memcmp(bootduid, zero, sizeof(bootduid)) != 0) {
6495b351376Spatrick 			fdt_node_add_property(node, "openbsd,bootduid",
6505b351376Spatrick 			    bootduid, sizeof(bootduid));
651f24071e5Spatrick 		}
652f24071e5Spatrick 
6535b351376Spatrick 		if (bootdev_dip->sr_vol != NULL) {
6545b351376Spatrick 			bv = bootdev_dip->sr_vol;
6555b351376Spatrick 			fdt_node_add_property(node, "openbsd,sr-bootuuid",
6565b351376Spatrick 			    &bv->sbv_uuid, sizeof(bv->sbv_uuid));
6575b351376Spatrick 			if (bv->sbv_maskkey != NULL)
6585b351376Spatrick 				fdt_node_add_property(node,
6595b351376Spatrick 				    "openbsd,sr-bootkey", bv->sbv_maskkey,
6605b351376Spatrick 				    SR_CRYPTO_MAXKEYBYTES);
6615b351376Spatrick 		}
6625b351376Spatrick 	}
6635b351376Spatrick 
6645b351376Spatrick 	sr_clear_keys();
6655b351376Spatrick 
6669ccb13abSnaddy 	/* Pass netboot interface address. */
6679ccb13abSnaddy 	if (bootmac)
6689ccb13abSnaddy 		fdt_node_add_property(node, "openbsd,bootmac", bootmac, 6);
6699ccb13abSnaddy 
670f4dd1aebSkettenis 	/* Pass EFI system table. */
671f4dd1aebSkettenis 	fdt_node_add_property(node, "openbsd,uefi-system-table",
672f4dd1aebSkettenis 	    &uefi_system_table, sizeof(uefi_system_table));
673f4dd1aebSkettenis 
674f4dd1aebSkettenis 	/* Placeholders for EFI memory map. */
675f4dd1aebSkettenis 	fdt_node_add_property(node, "openbsd,uefi-mmap-start", zero, 8);
676f4dd1aebSkettenis 	fdt_node_add_property(node, "openbsd,uefi-mmap-size", zero, 4);
677f4dd1aebSkettenis 	fdt_node_add_property(node, "openbsd,uefi-mmap-desc-size", zero, 4);
678f4dd1aebSkettenis 	fdt_node_add_property(node, "openbsd,uefi-mmap-desc-ver", zero, 4);
679f4dd1aebSkettenis 
6801e44dc38Skettenis 	efi_framebuffer();
6815cfe0970Skettenis 	efi_console();
682a88d1494Skettenis 	efi_dma_constraint();
6831e44dc38Skettenis 
684f24071e5Spatrick 	fdt_finalize();
685f24071e5Spatrick 
686f24071e5Spatrick 	return fdt;
687f24071e5Spatrick }
688f24071e5Spatrick 
689f4dd1aebSkettenis void
690f4dd1aebSkettenis efi_updatefdt(void)
691f4dd1aebSkettenis {
692f4dd1aebSkettenis 	uint64_t uefi_mmap_start = htobe64((uintptr_t)mmap);
693f4dd1aebSkettenis 	uint32_t uefi_mmap_size = htobe32(mmap_ndesc * mmap_descsiz);
694f4dd1aebSkettenis 	uint32_t uefi_mmap_desc_size = htobe32(mmap_descsiz);
695f4dd1aebSkettenis 	uint32_t uefi_mmap_desc_ver = htobe32(mmap_version);
696f4dd1aebSkettenis 	void *node;
697f4dd1aebSkettenis 
698f4dd1aebSkettenis 	node = fdt_find_node("/chosen");
699f4dd1aebSkettenis 	if (!node)
700f4dd1aebSkettenis 		return;
701f4dd1aebSkettenis 
702f4dd1aebSkettenis 	/* Pass EFI memory map. */
703f4dd1aebSkettenis 	fdt_node_set_property(node, "openbsd,uefi-mmap-start",
704f4dd1aebSkettenis 	    &uefi_mmap_start, sizeof(uefi_mmap_start));
705f4dd1aebSkettenis 	fdt_node_set_property(node, "openbsd,uefi-mmap-size",
706f4dd1aebSkettenis 	    &uefi_mmap_size, sizeof(uefi_mmap_size));
707f4dd1aebSkettenis 	fdt_node_set_property(node, "openbsd,uefi-mmap-desc-size",
708f4dd1aebSkettenis 	    &uefi_mmap_desc_size, sizeof(uefi_mmap_desc_size));
709f4dd1aebSkettenis 	fdt_node_set_property(node, "openbsd,uefi-mmap-desc-ver",
710f4dd1aebSkettenis 	    &uefi_mmap_desc_ver, sizeof(uefi_mmap_desc_ver));
711f4dd1aebSkettenis 
712f4dd1aebSkettenis 	fdt_finalize();
713f4dd1aebSkettenis }
714f4dd1aebSkettenis 
715f24071e5Spatrick u_long efi_loadaddr;
716f24071e5Spatrick 
717f24071e5Spatrick void
718f24071e5Spatrick machdep(void)
719f24071e5Spatrick {
720f24071e5Spatrick 	EFI_PHYSICAL_ADDRESS addr;
721f24071e5Spatrick 
722f24071e5Spatrick 	cninit();
723f24071e5Spatrick 	efi_heap_init();
7244befd8f0Spatrick 	smbios_init(smbios);
725f24071e5Spatrick 
726f24071e5Spatrick 	/*
7279a118d7eSpatrick 	 * The kernel expects to be loaded into a block of memory aligned
7289a118d7eSpatrick 	 * on a 2MB boundary.  We allocate a block of 64MB of memory, which
7299a118d7eSpatrick 	 * gives us plenty of room for growth.
730f24071e5Spatrick 	 */
7319a118d7eSpatrick 	if (efi_memprobe_find(EFI_SIZE_TO_PAGES(64 * 1024 * 1024),
7322c32ef33Skettenis 	    0x200000, EfiLoaderCode, &addr) != EFI_SUCCESS)
733f24071e5Spatrick 		printf("Can't allocate memory\n");
734f24071e5Spatrick 	efi_loadaddr = addr;
735f24071e5Spatrick 
736f24071e5Spatrick 	efi_timer_init();
737f24071e5Spatrick 	efi_diskprobe();
73833e5575aSpatrick 	efi_pxeprobe();
739f24071e5Spatrick }
740f24071e5Spatrick 
741f24071e5Spatrick void
742f24071e5Spatrick efi_cleanup(void)
743f24071e5Spatrick {
744fa854029Spatrick 	int		 retry;
745fa854029Spatrick 	EFI_STATUS	 status;
746fa854029Spatrick 
747f24071e5Spatrick 	efi_timer_cleanup();
748f24071e5Spatrick 
749fa854029Spatrick 	/* retry once in case of failure */
750fa854029Spatrick 	for (retry = 1; retry >= 0; retry--) {
751fa854029Spatrick 		efi_memprobe_internal();	/* sync the current map */
752f4dd1aebSkettenis 		efi_updatefdt();
7531f462730Skrw 		status = BS->ExitBootServices(IH, mmap_key);
754fa854029Spatrick 		if (status == EFI_SUCCESS)
755fa854029Spatrick 			break;
756fa854029Spatrick 		if (retry == 0)
757fa854029Spatrick 			panic("ExitBootServices failed (%d)", status);
758fa854029Spatrick 	}
759f24071e5Spatrick }
760f24071e5Spatrick 
761f24071e5Spatrick void
762f24071e5Spatrick _rtt(void)
763f24071e5Spatrick {
764f24071e5Spatrick #ifdef EFI_DEBUG
765f24071e5Spatrick 	printf("Hit any key to reboot\n");
766f24071e5Spatrick 	efi_cons_getc(0);
767f24071e5Spatrick #endif
768f24071e5Spatrick 	RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
769111d6387Skettenis 	for (;;)
770111d6387Skettenis 		continue;
771f24071e5Spatrick }
772f24071e5Spatrick 
773f24071e5Spatrick /*
774f24071e5Spatrick  * U-Boot only implements the GetTime() Runtime Service if it has been
775f24071e5Spatrick  * configured with CONFIG_DM_RTC.  Most board configurations don't
776f24071e5Spatrick  * include that option, so we can't use it to implement our boot
777f24071e5Spatrick  * prompt timeout.  Instead we use timer events to simulate a clock
778f24071e5Spatrick  * that ticks ever second.
779f24071e5Spatrick  */
780f24071e5Spatrick 
781f24071e5Spatrick EFI_EVENT timer;
782f24071e5Spatrick int ticks;
783f24071e5Spatrick 
784f24071e5Spatrick static VOID
785f24071e5Spatrick efi_timer(EFI_EVENT event, VOID *context)
786f24071e5Spatrick {
787f24071e5Spatrick 	ticks++;
788f24071e5Spatrick }
789f24071e5Spatrick 
790f24071e5Spatrick static void
791f24071e5Spatrick efi_timer_init(void)
792f24071e5Spatrick {
793f24071e5Spatrick 	EFI_STATUS status;
794f24071e5Spatrick 
795a4d88df4Sjsg 	status = BS->CreateEvent(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
796f24071e5Spatrick 	    efi_timer, NULL, &timer);
797f24071e5Spatrick 	if (status == EFI_SUCCESS)
798f24071e5Spatrick 		status = BS->SetTimer(timer, TimerPeriodic, 10000000);
799f24071e5Spatrick 	if (EFI_ERROR(status))
800f24071e5Spatrick 		printf("Can't create timer\n");
801f24071e5Spatrick }
802f24071e5Spatrick 
803f24071e5Spatrick static void
804f24071e5Spatrick efi_timer_cleanup(void)
805f24071e5Spatrick {
806f24071e5Spatrick 	BS->CloseEvent(timer);
807f24071e5Spatrick }
808f24071e5Spatrick 
809f24071e5Spatrick time_t
810f24071e5Spatrick getsecs(void)
811f24071e5Spatrick {
812f24071e5Spatrick 	return ticks;
813f24071e5Spatrick }
814f24071e5Spatrick 
815f24071e5Spatrick /*
816f24071e5Spatrick  * Various device-related bits.
817f24071e5Spatrick  */
818f24071e5Spatrick 
819f24071e5Spatrick void
820f24071e5Spatrick devboot(dev_t dev, char *p)
821f24071e5Spatrick {
8225b351376Spatrick 	struct sr_boot_volume *bv;
8235b351376Spatrick 	struct sr_boot_chunk *bc;
8245b351376Spatrick 	struct diskinfo *dip;
8255b351376Spatrick 	int sd_boot_vol = 0;
8265b351376Spatrick 	int sr_boot_vol = -1;
8275b351376Spatrick 	int part_type = FS_UNUSED;
8285b351376Spatrick 
8295b351376Spatrick 	if (bootdev_dip == NULL) {
83033e5575aSpatrick 		strlcpy(p, "tftp0a", 7);
8315b351376Spatrick 		return;
8325b351376Spatrick 	}
8335b351376Spatrick 
8345b351376Spatrick 	TAILQ_FOREACH(dip, &disklist, list) {
8355b351376Spatrick 		if (bootdev_dip == dip)
8365b351376Spatrick 			break;
8375b351376Spatrick 		sd_boot_vol++;
8385b351376Spatrick 	}
8395b351376Spatrick 
8405b351376Spatrick 	/*
8415b351376Spatrick 	 * Determine the partition type for the 'a' partition of the
8425b351376Spatrick 	 * boot device.
8435b351376Spatrick 	 */
8445b351376Spatrick 	if ((bootdev_dip->flags & DISKINFO_FLAG_GOODLABEL) != 0)
8455b351376Spatrick 		part_type = bootdev_dip->disklabel.d_partitions[0].p_fstype;
8465b351376Spatrick 
8475b351376Spatrick 	/*
8485b351376Spatrick 	 * See if we booted from a disk that is a member of a bootable
8495b351376Spatrick 	 * softraid volume.
8505b351376Spatrick 	 */
8515b351376Spatrick 	SLIST_FOREACH(bv, &sr_volumes, sbv_link) {
8525b351376Spatrick 		SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link)
8535b351376Spatrick 			if (bc->sbc_diskinfo == bootdev_dip)
8545b351376Spatrick 				sr_boot_vol = bv->sbv_unit;
8555b351376Spatrick 		if (sr_boot_vol != -1)
8565b351376Spatrick 			break;
8575b351376Spatrick 	}
8585b351376Spatrick 
8595b351376Spatrick 	if (sr_boot_vol != -1 && part_type != FS_BSDFFS) {
8605b351376Spatrick 		strlcpy(p, "sr0a", 5);
8615b351376Spatrick 		p[2] = '0' + sr_boot_vol;
8625b351376Spatrick 		return;
8635b351376Spatrick 	}
8645b351376Spatrick 
8655b351376Spatrick 	strlcpy(p, "sd0a", 5);
8665b351376Spatrick 	p[2] = '0' + sd_boot_vol;
867f24071e5Spatrick }
868f24071e5Spatrick 
869c7045f51Skettenis const char cdevs[][4] = { "cons", "com", "fb" };
8705cfe0970Skettenis const int ncdevs = nitems(cdevs);
8715cfe0970Skettenis 
872f24071e5Spatrick int
873f24071e5Spatrick cnspeed(dev_t dev, int sp)
874f24071e5Spatrick {
875f24071e5Spatrick 	return 115200;
876f24071e5Spatrick }
877f24071e5Spatrick 
8785cfe0970Skettenis char ttyname_buf[8];
8795cfe0970Skettenis 
880f24071e5Spatrick char *
881f24071e5Spatrick ttyname(int fd)
882f24071e5Spatrick {
8835cfe0970Skettenis 	snprintf(ttyname_buf, sizeof ttyname_buf, "%s%d",
8845cfe0970Skettenis 	    cdevs[major(cn_tab->cn_dev)], minor(cn_tab->cn_dev));
8855cfe0970Skettenis 
8865cfe0970Skettenis 	return ttyname_buf;
887f24071e5Spatrick }
888f24071e5Spatrick 
889f24071e5Spatrick dev_t
890f24071e5Spatrick ttydev(char *name)
891f24071e5Spatrick {
8925cfe0970Skettenis 	int i, unit = -1;
8935cfe0970Skettenis 	char *no = name + strlen(name) - 1;
8945cfe0970Skettenis 
8955cfe0970Skettenis 	while (no >= name && *no >= '0' && *no <= '9')
8965cfe0970Skettenis 		unit = (unit < 0 ? 0 : (unit * 10)) + *no-- - '0';
8975cfe0970Skettenis 	if (no < name || unit < 0)
8985cfe0970Skettenis 		return NODEV;
8995cfe0970Skettenis 	for (i = 0; i < ncdevs; i++)
9005cfe0970Skettenis 		if (strncmp(name, cdevs[i], no - name + 1) == 0)
9015cfe0970Skettenis 			return makedev(i, unit);
902f24071e5Spatrick 	return NODEV;
903f24071e5Spatrick }
904f24071e5Spatrick 
905f24071e5Spatrick #define MAXDEVNAME	16
906f24071e5Spatrick 
907f24071e5Spatrick /*
908f24071e5Spatrick  * Parse a device spec.
909f24071e5Spatrick  *
910f24071e5Spatrick  * [A-Za-z]*[0-9]*[A-Za-z]:file
911f24071e5Spatrick  *    dev   uint    part
912f24071e5Spatrick  */
913f24071e5Spatrick int
914f24071e5Spatrick devparse(const char *fname, int *dev, int *unit, int *part, const char **file)
915f24071e5Spatrick {
916f24071e5Spatrick 	const char *s;
917f24071e5Spatrick 
918f24071e5Spatrick 	*unit = 0;	/* default to wd0a */
919f24071e5Spatrick 	*part = 0;
920f24071e5Spatrick 	*dev  = 0;
921f24071e5Spatrick 
922f24071e5Spatrick 	s = strchr(fname, ':');
923f24071e5Spatrick 	if (s != NULL) {
924f24071e5Spatrick 		int devlen;
925f24071e5Spatrick 		int i, u, p = 0;
926f24071e5Spatrick 		struct devsw *dp;
927f24071e5Spatrick 		char devname[MAXDEVNAME];
928f24071e5Spatrick 
929f24071e5Spatrick 		devlen = s - fname;
930f24071e5Spatrick 		if (devlen > MAXDEVNAME)
931f24071e5Spatrick 			return (EINVAL);
932f24071e5Spatrick 
933f24071e5Spatrick 		/* extract device name */
934f24071e5Spatrick 		for (i = 0; isalpha(fname[i]) && (i < devlen); i++)
935f24071e5Spatrick 			devname[i] = fname[i];
936f24071e5Spatrick 		devname[i] = 0;
937f24071e5Spatrick 
938f24071e5Spatrick 		if (!isdigit(fname[i]))
939f24071e5Spatrick 			return (EUNIT);
940f24071e5Spatrick 
941f24071e5Spatrick 		/* device number */
942f24071e5Spatrick 		for (u = 0; isdigit(fname[i]) && (i < devlen); i++)
943f24071e5Spatrick 			u = u * 10 + (fname[i] - '0');
944f24071e5Spatrick 
945f24071e5Spatrick 		if (!isalpha(fname[i]))
946f24071e5Spatrick 			return (EPART);
947f24071e5Spatrick 
948f24071e5Spatrick 		/* partition number */
949f24071e5Spatrick 		if (i < devlen)
950f24071e5Spatrick 			p = fname[i++] - 'a';
951f24071e5Spatrick 
952f24071e5Spatrick 		if (i != devlen)
953f24071e5Spatrick 			return (ENXIO);
954f24071e5Spatrick 
955f24071e5Spatrick 		/* check device name */
956f24071e5Spatrick 		for (dp = devsw, i = 0; i < ndevs; dp++, i++) {
957f24071e5Spatrick 			if (dp->dv_name && !strcmp(devname, dp->dv_name))
958f24071e5Spatrick 				break;
959f24071e5Spatrick 		}
960f24071e5Spatrick 
961f24071e5Spatrick 		if (i >= ndevs)
962f24071e5Spatrick 			return (ENXIO);
963f24071e5Spatrick 
964f24071e5Spatrick 		*unit = u;
965f24071e5Spatrick 		*part = p;
966f24071e5Spatrick 		*dev  = i;
967f24071e5Spatrick 		fname = ++s;
968f24071e5Spatrick 	}
969f24071e5Spatrick 
970f24071e5Spatrick 	*file = fname;
971f24071e5Spatrick 
972f24071e5Spatrick 	return (0);
973f24071e5Spatrick }
974f24071e5Spatrick 
975f24071e5Spatrick int
976f24071e5Spatrick devopen(struct open_file *f, const char *fname, char **file)
977f24071e5Spatrick {
978f24071e5Spatrick 	struct devsw *dp;
979f24071e5Spatrick 	int dev, unit, part, error;
980f24071e5Spatrick 
981f24071e5Spatrick 	error = devparse(fname, &dev, &unit, &part, (const char **)file);
982f24071e5Spatrick 	if (error)
983f24071e5Spatrick 		return (error);
984f24071e5Spatrick 
98533e5575aSpatrick 	dp = &devsw[dev];
986f24071e5Spatrick 	f->f_dev = dp;
987f24071e5Spatrick 
988ad424d98Snaddy 	if (strcmp("tftp", dp->dv_name) != 0) {
989ad424d98Snaddy 		/*
990ad424d98Snaddy 		 * Clear bootmac, to signal that we loaded this file from a
991ad424d98Snaddy 		 * non-network device.
992ad424d98Snaddy 		 */
993ad424d98Snaddy 		bootmac = NULL;
994ad424d98Snaddy 	}
995ad424d98Snaddy 
996f24071e5Spatrick 	return (*dp->dv_open)(f, unit, part);
997f24071e5Spatrick }
998f24071e5Spatrick 
999fa854029Spatrick static void
1000fa854029Spatrick efi_memprobe_internal(void)
1001fa854029Spatrick {
1002fa854029Spatrick 	EFI_STATUS		 status;
1003fa854029Spatrick 	UINTN			 mapkey, mmsiz, siz;
1004fa854029Spatrick 	UINT32			 mmver;
1005fa854029Spatrick 	EFI_MEMORY_DESCRIPTOR	*mm;
1006fa854029Spatrick 	int			 n;
1007fa854029Spatrick 
1008fa854029Spatrick 	free(mmap, mmap_ndesc * mmap_descsiz);
1009fa854029Spatrick 
1010fa854029Spatrick 	siz = 0;
10111f462730Skrw 	status = BS->GetMemoryMap(&siz, NULL, &mapkey, &mmsiz, &mmver);
1012fa854029Spatrick 	if (status != EFI_BUFFER_TOO_SMALL)
1013fa854029Spatrick 		panic("cannot get the size of memory map");
1014fa854029Spatrick 	mm = alloc(siz);
10151f462730Skrw 	status = BS->GetMemoryMap(&siz, mm, &mapkey, &mmsiz, &mmver);
1016fa854029Spatrick 	if (status != EFI_SUCCESS)
1017fa854029Spatrick 		panic("cannot get the memory map");
1018fa854029Spatrick 	n = siz / mmsiz;
1019fa854029Spatrick 	mmap = mm;
1020fa854029Spatrick 	mmap_key = mapkey;
1021fa854029Spatrick 	mmap_ndesc = n;
1022fa854029Spatrick 	mmap_descsiz = mmsiz;
1023f4dd1aebSkettenis 	mmap_version = mmver;
1024fa854029Spatrick }
1025fa854029Spatrick 
1026f24071e5Spatrick /*
1027f24071e5Spatrick  * 64-bit ARMs can have a much wider memory mapping, as in somewhere
1028f24071e5Spatrick  * after the 32-bit region.  To cope with our alignment requirement,
1029f24071e5Spatrick  * use the memory table to find a place where we can fit.
1030f24071e5Spatrick  */
1031f24071e5Spatrick static EFI_STATUS
10322c32ef33Skettenis efi_memprobe_find(UINTN pages, UINTN align, EFI_MEMORY_TYPE type,
10332c32ef33Skettenis     EFI_PHYSICAL_ADDRESS *addr)
1034f24071e5Spatrick {
1035fa854029Spatrick 	EFI_MEMORY_DESCRIPTOR	*mm;
1036fa854029Spatrick 	int			 i, j;
1037f24071e5Spatrick 
1038f24071e5Spatrick 	if (align < EFI_PAGE_SIZE)
1039f24071e5Spatrick 		return EFI_INVALID_PARAMETER;
1040f24071e5Spatrick 
1041fa854029Spatrick 	efi_memprobe_internal();	/* sync the current map */
1042f24071e5Spatrick 
1043fa854029Spatrick 	for (i = 0, mm = mmap; i < mmap_ndesc;
1044fa854029Spatrick 	    i++, mm = NextMemoryDescriptor(mm, mmap_descsiz)) {
1045f24071e5Spatrick 		if (mm->Type != EfiConventionalMemory)
1046f24071e5Spatrick 			continue;
1047f24071e5Spatrick 
1048f24071e5Spatrick 		if (mm->NumberOfPages < pages)
1049f24071e5Spatrick 			continue;
1050f24071e5Spatrick 
1051f2c0e408Skettenis 		for (j = 0; j < mm->NumberOfPages; j++) {
1052f24071e5Spatrick 			EFI_PHYSICAL_ADDRESS paddr;
1053f24071e5Spatrick 
1054f24071e5Spatrick 			if (mm->NumberOfPages - j < pages)
1055f24071e5Spatrick 				break;
1056f24071e5Spatrick 
1057f24071e5Spatrick 			paddr = mm->PhysicalStart + (j * EFI_PAGE_SIZE);
105873dc1409Spatrick 			if (paddr & (align - 1))
105973dc1409Spatrick 				continue;
106073dc1409Spatrick 
10612c32ef33Skettenis 			if (BS->AllocatePages(AllocateAddress, type,
10621f462730Skrw 			    pages, &paddr) == EFI_SUCCESS) {
1063f24071e5Spatrick 				*addr = paddr;
1064f24071e5Spatrick 				return EFI_SUCCESS;
1065f24071e5Spatrick 			}
1066f24071e5Spatrick 		}
1067f24071e5Spatrick 	}
1068f24071e5Spatrick 	return EFI_OUT_OF_RESOURCES;
1069f24071e5Spatrick }
1070111d6387Skettenis 
1071f3468426Skettenis int
1072f3468426Skettenis mdrandom(char *buf, size_t buflen)
1073f3468426Skettenis {
1074f3468426Skettenis 	char *random;
1075f3468426Skettenis 	void *node;
1076f3468426Skettenis 	int i, len, ret = -1;
1077f3468426Skettenis 
1078f3468426Skettenis 	node = fdt_find_node("/chosen");
1079f3468426Skettenis 	if (!node)
1080f3468426Skettenis 		return -1;
1081f3468426Skettenis 
1082f3468426Skettenis 	len = fdt_node_property(node, "rng-seed", &random);
1083f3468426Skettenis 	if (len > 0) {
1084f3468426Skettenis 		for (i = 0; i < buflen; i++)
1085f3468426Skettenis 			buf[i] ^= random[i % len];
1086f3468426Skettenis 		ret = 0;
1087f3468426Skettenis 	}
1088f3468426Skettenis 
1089f3468426Skettenis 	len = fdt_node_property(node, "kaslr-seed", &random);
1090f3468426Skettenis 	if (len > 0) {
1091f3468426Skettenis 		for (i = 0; i < buflen; i++)
1092f3468426Skettenis 			buf[i] ^= random[i % len];
1093f3468426Skettenis 		ret = 0;
1094f3468426Skettenis 	}
1095f3468426Skettenis 
1096f3468426Skettenis 	return ret;
1097f3468426Skettenis }
1098f3468426Skettenis 
10994befd8f0Spatrick #define FW_PATH "/etc/firmware/dtb/"
11004befd8f0Spatrick 
11014befd8f0Spatrick void *
11024befd8f0Spatrick efi_fdt(void)
11034befd8f0Spatrick {
11044befd8f0Spatrick 	extern char *hw_vendor, *hw_prod;
11054befd8f0Spatrick 
11064befd8f0Spatrick 	/* 'mach dtb' has precedence */
11074befd8f0Spatrick 	if (fdt_override != NULL)
11084befd8f0Spatrick 		return fdt_override;
11094befd8f0Spatrick 
11104befd8f0Spatrick 	/* Return system provided one */
11114befd8f0Spatrick 	if (hw_vendor == NULL || hw_prod == NULL)
11124befd8f0Spatrick 		return fdt_sys;
11134befd8f0Spatrick 
11144befd8f0Spatrick 	if (strcmp(hw_vendor, "LENOVO") == 0 &&
1115a36d06b9Spatrick 	    strncmp(hw_prod, "21BX", 4) == 0) {
11164befd8f0Spatrick 		fdt_load_override(FW_PATH
11174befd8f0Spatrick 		    "qcom/sc8280xp-lenovo-thinkpad-x13s.dtb");
1118a36d06b9Spatrick 		/* TODO: find a better mechanism */
1119a36d06b9Spatrick 		cnset(ttydev("fb0"));
1120a36d06b9Spatrick 	}
11214befd8f0Spatrick 
11224befd8f0Spatrick 	return fdt_override ? fdt_override : fdt_sys;
11234befd8f0Spatrick }
11244befd8f0Spatrick 
11254befd8f0Spatrick int
11264befd8f0Spatrick fdt_load_override(char *file)
11274befd8f0Spatrick {
11284befd8f0Spatrick 	EFI_PHYSICAL_ADDRESS addr;
11294befd8f0Spatrick 	char path[MAXPATHLEN];
11304befd8f0Spatrick 	struct stat sb;
11314befd8f0Spatrick 	int fd;
11324befd8f0Spatrick 
11334befd8f0Spatrick 	if (file == NULL && fdt_override) {
11344befd8f0Spatrick 		BS->FreePages((uint64_t)fdt_override,
11354befd8f0Spatrick 		    EFI_SIZE_TO_PAGES(fdt_override_size));
11364befd8f0Spatrick 		fdt_override = NULL;
11374befd8f0Spatrick 		fdt_init(fdt_sys);
11384befd8f0Spatrick 		return 0;
11394befd8f0Spatrick 	}
11404befd8f0Spatrick 
11414befd8f0Spatrick 	snprintf(path, sizeof(path), "%s:%s", cmd.bootdev, file);
11424befd8f0Spatrick 
11434befd8f0Spatrick 	fd = open(path, O_RDONLY);
11444befd8f0Spatrick 	if (fd < 0 || fstat(fd, &sb) == -1) {
11454befd8f0Spatrick 		printf("cannot open %s\n", path);
11464befd8f0Spatrick 		return 0;
11474befd8f0Spatrick 	}
11484befd8f0Spatrick 	if (efi_memprobe_find(EFI_SIZE_TO_PAGES(sb.st_size),
11494befd8f0Spatrick 	    PAGE_SIZE, EfiLoaderData, &addr) != EFI_SUCCESS) {
11504befd8f0Spatrick 		printf("cannot allocate memory for %s\n", path);
11514befd8f0Spatrick 		return 0;
11524befd8f0Spatrick 	}
11534befd8f0Spatrick 	if (read(fd, (void *)addr, sb.st_size) != sb.st_size) {
11544befd8f0Spatrick 		printf("cannot read from %s\n", path);
11554befd8f0Spatrick 		return 0;
11564befd8f0Spatrick 	}
11574befd8f0Spatrick 
11584befd8f0Spatrick 	if (!fdt_init((void *)addr)) {
11594befd8f0Spatrick 		printf("invalid device tree\n");
11604befd8f0Spatrick 		BS->FreePages(addr, EFI_SIZE_TO_PAGES(sb.st_size));
11614befd8f0Spatrick 		return 0;
11624befd8f0Spatrick 	}
11634befd8f0Spatrick 
11644befd8f0Spatrick 	if (fdt_override) {
11654befd8f0Spatrick 		BS->FreePages((uint64_t)fdt_override,
11664befd8f0Spatrick 		    EFI_SIZE_TO_PAGES(fdt_override_size));
11674befd8f0Spatrick 		fdt_override = NULL;
11684befd8f0Spatrick 	}
11694befd8f0Spatrick 
11704befd8f0Spatrick 	fdt_override = (void *)addr;
11714befd8f0Spatrick 	fdt_override_size = sb.st_size;
11724befd8f0Spatrick 	return 0;
11734befd8f0Spatrick }
11744befd8f0Spatrick 
1175111d6387Skettenis /*
1176111d6387Skettenis  * Commands
1177111d6387Skettenis  */
1178111d6387Skettenis 
1179be07ee62Skettenis int Xacpi_efi(void);
11806f83097eSpatrick int Xdtb_efi(void);
1181111d6387Skettenis int Xexit_efi(void);
1182111d6387Skettenis int Xpoweroff_efi(void);
1183111d6387Skettenis 
1184111d6387Skettenis const struct cmd_table cmd_machine[] = {
1185be07ee62Skettenis 	{ "acpi",	CMDT_CMD, Xacpi_efi },
11866f83097eSpatrick 	{ "dtb",	CMDT_CMD, Xdtb_efi },
1187111d6387Skettenis 	{ "exit",	CMDT_CMD, Xexit_efi },
1188111d6387Skettenis 	{ "poweroff",	CMDT_CMD, Xpoweroff_efi },
1189111d6387Skettenis 	{ NULL, 0 }
1190111d6387Skettenis };
1191111d6387Skettenis 
1192111d6387Skettenis int
1193be07ee62Skettenis Xacpi_efi(void)
1194be07ee62Skettenis {
1195be07ee62Skettenis 	acpi = 1;
1196be07ee62Skettenis 	return (0);
1197be07ee62Skettenis }
1198be07ee62Skettenis 
1199be07ee62Skettenis int
12006f83097eSpatrick Xdtb_efi(void)
12016f83097eSpatrick {
12024befd8f0Spatrick 	if (cmd.argc == 1) {
12034befd8f0Spatrick 		fdt_load_override(NULL);
12044befd8f0Spatrick 		return (0);
12054befd8f0Spatrick 	}
12066f83097eSpatrick 
1207fcf3d43cSkn 	if (cmd.argc != 2) {
1208fcf3d43cSkn 		printf("dtb file\n");
1209fcf3d43cSkn 		return (0);
1210fcf3d43cSkn 	}
12116f83097eSpatrick 
12124befd8f0Spatrick 	return fdt_load_override(cmd.argv[1]);
12136f83097eSpatrick }
12146f83097eSpatrick 
12156f83097eSpatrick int
1216111d6387Skettenis Xexit_efi(void)
1217111d6387Skettenis {
12181f462730Skrw 	BS->Exit(IH, 0, 0, NULL);
1219111d6387Skettenis 	for (;;)
1220111d6387Skettenis 		continue;
1221111d6387Skettenis 	return (0);
1222111d6387Skettenis }
1223111d6387Skettenis 
1224111d6387Skettenis int
1225111d6387Skettenis Xpoweroff_efi(void)
1226111d6387Skettenis {
12271f462730Skrw 	RS->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
1228111d6387Skettenis 	return (0);
1229111d6387Skettenis }
1230