xref: /openbsd/sys/arch/arm64/stand/efiboot/efiboot.c (revision 6f83097e)
1*6f83097eSpatrick /*	$OpenBSD: efiboot.c,v 1.19 2018/08/19 14:09:41 patrick 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>
22*6f83097eSpatrick #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 
31f24071e5Spatrick #include <lib/libkern/libkern.h>
32f24071e5Spatrick #include <stand/boot/cmd.h>
33f24071e5Spatrick 
34f24071e5Spatrick #include "disk.h"
3533e5575aSpatrick #include "efiboot.h"
36f24071e5Spatrick #include "eficall.h"
37f24071e5Spatrick #include "fdt.h"
38f24071e5Spatrick #include "libsa.h"
39f24071e5Spatrick 
40f24071e5Spatrick EFI_SYSTEM_TABLE	*ST;
41f24071e5Spatrick EFI_BOOT_SERVICES	*BS;
42f24071e5Spatrick EFI_RUNTIME_SERVICES	*RS;
43f673f4ddSkettenis EFI_HANDLE		 IH, efi_bootdp;
44f24071e5Spatrick 
45fa854029Spatrick EFI_PHYSICAL_ADDRESS	 heap;
46fa854029Spatrick UINTN			 heapsiz = 1 * 1024 * 1024;
47f673f4ddSkettenis EFI_MEMORY_DESCRIPTOR	*mmap;
48fa854029Spatrick UINTN			 mmap_key;
49fa854029Spatrick UINTN			 mmap_ndesc;
50fa854029Spatrick UINTN			 mmap_descsiz;
51f4dd1aebSkettenis UINT32			 mmap_version;
52f24071e5Spatrick 
53f24071e5Spatrick static EFI_GUID		 imgp_guid = LOADED_IMAGE_PROTOCOL;
54f24071e5Spatrick static EFI_GUID		 blkio_guid = BLOCK_IO_PROTOCOL;
55f24071e5Spatrick static EFI_GUID		 devp_guid = DEVICE_PATH_PROTOCOL;
561e44dc38Skettenis static EFI_GUID		 gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
57f24071e5Spatrick 
5833e5575aSpatrick int efi_device_path_depth(EFI_DEVICE_PATH *dp, int);
5933e5575aSpatrick int efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int);
60fa854029Spatrick static void efi_heap_init(void);
61fa854029Spatrick static void efi_memprobe_internal(void);
62f24071e5Spatrick static void efi_timer_init(void);
63f24071e5Spatrick static void efi_timer_cleanup(void);
64f24071e5Spatrick static EFI_STATUS efi_memprobe_find(UINTN, UINTN, EFI_PHYSICAL_ADDRESS *);
65f24071e5Spatrick 
66f24071e5Spatrick EFI_STATUS
67f24071e5Spatrick efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
68f24071e5Spatrick {
69f24071e5Spatrick 	extern char		*progname;
70f24071e5Spatrick 	EFI_LOADED_IMAGE	*imgp;
71f24071e5Spatrick 	EFI_DEVICE_PATH		*dp = NULL;
72f24071e5Spatrick 	EFI_STATUS		 status;
73f24071e5Spatrick 
74f24071e5Spatrick 	ST = systab;
75f24071e5Spatrick 	BS = ST->BootServices;
76f24071e5Spatrick 	IH = image;
77f24071e5Spatrick 
78f0da534cSjsg 	/* disable reset by watchdog after 5 minutes */
79f0da534cSjsg 	EFI_CALL(BS->SetWatchdogTimer, 0, 0, 0, NULL);
80f0da534cSjsg 
81f24071e5Spatrick 	status = EFI_CALL(BS->HandleProtocol, image, &imgp_guid,
82f24071e5Spatrick 	    (void **)&imgp);
83f24071e5Spatrick 	if (status == EFI_SUCCESS)
84f24071e5Spatrick 		status = EFI_CALL(BS->HandleProtocol, imgp->DeviceHandle,
85f24071e5Spatrick 		    &devp_guid, (void **)&dp);
86f24071e5Spatrick 	if (status == EFI_SUCCESS)
87f24071e5Spatrick 		efi_bootdp = dp;
88f24071e5Spatrick 
89f24071e5Spatrick 	progname = "BOOTAA64";
90f24071e5Spatrick 
91f24071e5Spatrick 	boot(0);
92f24071e5Spatrick 
93f24071e5Spatrick 	return (EFI_SUCCESS);
94f24071e5Spatrick }
95f24071e5Spatrick 
96f24071e5Spatrick static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
97f24071e5Spatrick static SIMPLE_INPUT_INTERFACE *conin;
98f24071e5Spatrick 
99f24071e5Spatrick void
100f24071e5Spatrick efi_cons_probe(struct consdev *cn)
101f24071e5Spatrick {
102f24071e5Spatrick 	cn->cn_pri = CN_MIDPRI;
103f24071e5Spatrick 	cn->cn_dev = makedev(12, 0);
104f24071e5Spatrick }
105f24071e5Spatrick 
106f24071e5Spatrick void
107f24071e5Spatrick efi_cons_init(struct consdev *cp)
108f24071e5Spatrick {
109f24071e5Spatrick 	conin = ST->ConIn;
110f24071e5Spatrick 	conout = ST->ConOut;
111f24071e5Spatrick }
112f24071e5Spatrick 
113f24071e5Spatrick int
114f24071e5Spatrick efi_cons_getc(dev_t dev)
115f24071e5Spatrick {
116f24071e5Spatrick 	EFI_INPUT_KEY	 key;
117f24071e5Spatrick 	EFI_STATUS	 status;
118f24071e5Spatrick #if 0
119f24071e5Spatrick 	UINTN		 dummy;
120f24071e5Spatrick #endif
121f24071e5Spatrick 	static int	 lastchar = 0;
122f24071e5Spatrick 
123f24071e5Spatrick 	if (lastchar) {
124f24071e5Spatrick 		int r = lastchar;
125f24071e5Spatrick 		if ((dev & 0x80) == 0)
126f24071e5Spatrick 			lastchar = 0;
127f24071e5Spatrick 		return (r);
128f24071e5Spatrick 	}
129f24071e5Spatrick 
130f24071e5Spatrick 	status = conin->ReadKeyStroke(conin, &key);
131f24071e5Spatrick 	while (status == EFI_NOT_READY) {
132f24071e5Spatrick 		if (dev & 0x80)
133f24071e5Spatrick 			return (0);
134f24071e5Spatrick 		/*
135f24071e5Spatrick 		 * XXX The implementation of WaitForEvent() in U-boot
136f24071e5Spatrick 		 * is broken and neverreturns.
137f24071e5Spatrick 		 */
138f24071e5Spatrick #if 0
139f24071e5Spatrick 		BS->WaitForEvent(1, &conin->WaitForKey, &dummy);
140f24071e5Spatrick #endif
141f24071e5Spatrick 		status = conin->ReadKeyStroke(conin, &key);
142f24071e5Spatrick 	}
143f24071e5Spatrick 
144f24071e5Spatrick 	if (dev & 0x80)
145f24071e5Spatrick 		lastchar = key.UnicodeChar;
146f24071e5Spatrick 
147f24071e5Spatrick 	return (key.UnicodeChar);
148f24071e5Spatrick }
149f24071e5Spatrick 
150f24071e5Spatrick void
151f24071e5Spatrick efi_cons_putc(dev_t dev, int c)
152f24071e5Spatrick {
153f24071e5Spatrick 	CHAR16	buf[2];
154f24071e5Spatrick 
155f24071e5Spatrick 	if (c == '\n')
156f24071e5Spatrick 		efi_cons_putc(dev, '\r');
157f24071e5Spatrick 
158f24071e5Spatrick 	buf[0] = c;
159f24071e5Spatrick 	buf[1] = 0;
160f24071e5Spatrick 
161f24071e5Spatrick 	conout->OutputString(conout, buf);
162f24071e5Spatrick }
163f24071e5Spatrick 
164f24071e5Spatrick static void
165f24071e5Spatrick efi_heap_init(void)
166f24071e5Spatrick {
167f24071e5Spatrick 	EFI_STATUS	 status;
168f24071e5Spatrick 
169f24071e5Spatrick 	status = EFI_CALL(BS->AllocatePages, AllocateAnyPages, EfiLoaderData,
170f24071e5Spatrick 	    EFI_SIZE_TO_PAGES(heapsiz), &heap);
171f24071e5Spatrick 	if (status != EFI_SUCCESS)
172f24071e5Spatrick 		panic("BS->AllocatePages()");
173f24071e5Spatrick }
174f24071e5Spatrick 
175f24071e5Spatrick EFI_BLOCK_IO	*disk;
176f24071e5Spatrick 
177f24071e5Spatrick void
178f24071e5Spatrick efi_diskprobe(void)
179f24071e5Spatrick {
180c6d7d0adSpatrick 	int			 i, depth = -1;
181f24071e5Spatrick 	UINTN			 sz;
182f24071e5Spatrick 	EFI_STATUS		 status;
183f24071e5Spatrick 	EFI_HANDLE		*handles = NULL;
184f24071e5Spatrick 	EFI_BLOCK_IO		*blkio;
185f24071e5Spatrick 	EFI_BLOCK_IO_MEDIA	*media;
186c6d7d0adSpatrick 	EFI_DEVICE_PATH		*dp;
187f24071e5Spatrick 
188f24071e5Spatrick 	sz = 0;
189f24071e5Spatrick 	status = EFI_CALL(BS->LocateHandle, ByProtocol, &blkio_guid, 0, &sz, 0);
190f24071e5Spatrick 	if (status == EFI_BUFFER_TOO_SMALL) {
191f24071e5Spatrick 		handles = alloc(sz);
192f24071e5Spatrick 		status = EFI_CALL(BS->LocateHandle, ByProtocol, &blkio_guid,
193f24071e5Spatrick 		    0, &sz, handles);
194f24071e5Spatrick 	}
195f24071e5Spatrick 	if (handles == NULL || EFI_ERROR(status))
19633e5575aSpatrick 		return;
197f24071e5Spatrick 
198c6d7d0adSpatrick 	if (efi_bootdp != NULL)
199c6d7d0adSpatrick 		depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
200c6d7d0adSpatrick 
201c94e7257Skettenis 	/*
202c94e7257Skettenis 	 * U-Boot incorrectly represents devices with a single
203c94e7257Skettenis 	 * MEDIA_DEVICE_PATH component.  In that case include that
204c94e7257Skettenis 	 * component into the matching, otherwise we'll blindly select
205c94e7257Skettenis 	 * the first device.
206c94e7257Skettenis 	 */
207c94e7257Skettenis 	if (depth == 0)
208c94e7257Skettenis 		depth = 1;
209c94e7257Skettenis 
210f24071e5Spatrick 	for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) {
211f24071e5Spatrick 		status = EFI_CALL(BS->HandleProtocol, handles[i], &blkio_guid,
212f24071e5Spatrick 		    (void **)&blkio);
213f24071e5Spatrick 		if (EFI_ERROR(status))
214f24071e5Spatrick 			panic("BS->HandleProtocol() returns %d", status);
215f24071e5Spatrick 
216f24071e5Spatrick 		media = blkio->Media;
217f24071e5Spatrick 		if (media->LogicalPartition || !media->MediaPresent)
218f24071e5Spatrick 			continue;
219f24071e5Spatrick 
220c6d7d0adSpatrick 		if (efi_bootdp == NULL || depth == -1)
221c6d7d0adSpatrick 			continue;
222f24071e5Spatrick 		status = EFI_CALL(BS->HandleProtocol, handles[i], &devp_guid,
223f24071e5Spatrick 		    (void **)&dp);
224f24071e5Spatrick 		if (EFI_ERROR(status))
225c6d7d0adSpatrick 			continue;
226c6d7d0adSpatrick 		if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) {
227f24071e5Spatrick 			disk = blkio;
228f24071e5Spatrick 			break;
229f24071e5Spatrick 		}
230f24071e5Spatrick 	}
231f24071e5Spatrick 
232f24071e5Spatrick 	free(handles, sz);
233f24071e5Spatrick }
234f24071e5Spatrick 
235c94e7257Skettenis /*
236c94e7257Skettenis  * Determine the number of nodes up to, but not including, the first
237c94e7257Skettenis  * node of the specified type.
238c94e7257Skettenis  */
23933e5575aSpatrick int
240c6d7d0adSpatrick efi_device_path_depth(EFI_DEVICE_PATH *dp, int dptype)
241c6d7d0adSpatrick {
242c6d7d0adSpatrick 	int	i;
243c6d7d0adSpatrick 
244c6d7d0adSpatrick 	for (i = 0; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp), i++) {
245c6d7d0adSpatrick 		if (DevicePathType(dp) == dptype)
246c94e7257Skettenis 			return (i);
247c6d7d0adSpatrick 	}
248c6d7d0adSpatrick 
249c6d7d0adSpatrick 	return (-1);
250c6d7d0adSpatrick }
251c6d7d0adSpatrick 
25233e5575aSpatrick int
253c6d7d0adSpatrick efi_device_path_ncmp(EFI_DEVICE_PATH *dpa, EFI_DEVICE_PATH *dpb, int deptn)
254c6d7d0adSpatrick {
255c6d7d0adSpatrick 	int	 i, cmp;
256c6d7d0adSpatrick 
257c6d7d0adSpatrick 	for (i = 0; i < deptn; i++) {
258c6d7d0adSpatrick 		if (IsDevicePathEnd(dpa) || IsDevicePathEnd(dpb))
259c6d7d0adSpatrick 			return ((IsDevicePathEnd(dpa) && IsDevicePathEnd(dpb))
260c6d7d0adSpatrick 			    ? 0 : (IsDevicePathEnd(dpa))? -1 : 1);
261c6d7d0adSpatrick 		cmp = DevicePathNodeLength(dpa) - DevicePathNodeLength(dpb);
262c6d7d0adSpatrick 		if (cmp)
263c6d7d0adSpatrick 			return (cmp);
264c6d7d0adSpatrick 		cmp = memcmp(dpa, dpb, DevicePathNodeLength(dpa));
265c6d7d0adSpatrick 		if (cmp)
266c6d7d0adSpatrick 			return (cmp);
267c6d7d0adSpatrick 		dpa = NextDevicePathNode(dpa);
268c6d7d0adSpatrick 		dpb = NextDevicePathNode(dpb);
269c6d7d0adSpatrick 	}
270c6d7d0adSpatrick 
271c6d7d0adSpatrick 	return (0);
272c6d7d0adSpatrick }
273c6d7d0adSpatrick 
2741e44dc38Skettenis void
2751e44dc38Skettenis efi_framebuffer(void)
2761e44dc38Skettenis {
2771e44dc38Skettenis 	EFI_GRAPHICS_OUTPUT *gop;
2781e44dc38Skettenis 	EFI_STATUS status;
2791e44dc38Skettenis 	void *node, *child;
2801e44dc38Skettenis 	uint32_t acells, scells;
2811e44dc38Skettenis 	uint64_t base, size;
2821e44dc38Skettenis 	uint32_t reg[4];
2831e44dc38Skettenis 	uint32_t width, height, stride;
2841e44dc38Skettenis 	char *format;
2851e44dc38Skettenis 
2861e44dc38Skettenis 	/*
2871e44dc38Skettenis 	 * Don't create a "simple-framebuffer" node if we already have
2881e44dc38Skettenis 	 * one.  Besides "/chosen", we also check under "/" since that
2891e44dc38Skettenis 	 * is where the Raspberry Pi firmware puts it.
2901e44dc38Skettenis 	 */
2911e44dc38Skettenis 	node = fdt_find_node("/chosen");
2921e44dc38Skettenis 	for (child = fdt_child_node(node); child;
2931e44dc38Skettenis 	     child = fdt_next_node(child)) {
2941e44dc38Skettenis 		if (fdt_node_is_compatible(child, "simple-framebuffer"))
2951e44dc38Skettenis 			return;
2961e44dc38Skettenis 	}
2971e44dc38Skettenis 	node = fdt_find_node("/");
2981e44dc38Skettenis 	for (child = fdt_child_node(node); child;
2991e44dc38Skettenis 	     child = fdt_next_node(child)) {
3001e44dc38Skettenis 		if (fdt_node_is_compatible(child, "simple-framebuffer"))
3011e44dc38Skettenis 			return;
3021e44dc38Skettenis 	}
3031e44dc38Skettenis 
3041e44dc38Skettenis 	status = EFI_CALL(BS->LocateProtocol, &gop_guid, NULL, (void **)&gop);
3051e44dc38Skettenis 	if (status != EFI_SUCCESS)
3061e44dc38Skettenis 		return;
3071e44dc38Skettenis 
3081e44dc38Skettenis 	/* Paranoia! */
3091e44dc38Skettenis 	if (gop == NULL || gop->Mode == NULL || gop->Mode->Info == NULL)
3101e44dc38Skettenis 		return;
3111e44dc38Skettenis 
3121e44dc38Skettenis 	/* We only support 32-bit pixel modes for now. */
3131e44dc38Skettenis 	switch (gop->Mode->Info->PixelFormat) {
3141e44dc38Skettenis 	case PixelRedGreenBlueReserved8BitPerColor:
3151e44dc38Skettenis 		format = "a8r8g8b8";
3161e44dc38Skettenis 		break;
3171e44dc38Skettenis 	case PixelBlueGreenRedReserved8BitPerColor:
3181e44dc38Skettenis 		format = "a8b8g8r8";
3191e44dc38Skettenis 		break;
3201e44dc38Skettenis 	default:
3211e44dc38Skettenis 		return;
3221e44dc38Skettenis 	}
3231e44dc38Skettenis 
3241e44dc38Skettenis 	base = gop->Mode->FrameBufferBase;
3251e44dc38Skettenis 	size = gop->Mode->FrameBufferSize;
3261e44dc38Skettenis 	width = htobe32(gop->Mode->Info->HorizontalResolution);
3271e44dc38Skettenis 	height = htobe32(gop->Mode->Info->VerticalResolution);
3281e44dc38Skettenis 	stride = htobe32(gop->Mode->Info->PixelsPerScanLine * 4);
3291e44dc38Skettenis 
3301e44dc38Skettenis 	node = fdt_find_node("/");
3311e44dc38Skettenis 	if (fdt_node_property_int(node, "#address-cells", &acells) != 1)
3321e44dc38Skettenis 		acells = 1;
3331e44dc38Skettenis 	if (fdt_node_property_int(node, "#size-cells", &scells) != 1)
3341e44dc38Skettenis 		scells = 1;
3351e44dc38Skettenis 	if (acells > 2 || scells > 2)
3361e44dc38Skettenis 		return;
3371e44dc38Skettenis 	if (acells >= 1)
3381e44dc38Skettenis 		reg[0] = htobe32(base);
3391e44dc38Skettenis 	if (acells == 2) {
3401e44dc38Skettenis 		reg[1] = reg[0];
3411e44dc38Skettenis 		reg[0] = htobe32(base >> 32);
3421e44dc38Skettenis 	}
3431e44dc38Skettenis 	if (scells >= 1)
3441e44dc38Skettenis 		reg[acells] = htobe32(size);
3451e44dc38Skettenis 	if (scells == 2) {
3461e44dc38Skettenis 		reg[acells + 1] = reg[acells];
3471e44dc38Skettenis 		reg[acells] = htobe32(size >> 32);
3481e44dc38Skettenis 	}
3491e44dc38Skettenis 
3501e44dc38Skettenis 	node = fdt_find_node("/chosen");
3511e44dc38Skettenis 	fdt_node_add_node(node, "framebuffer", &child);
3521e44dc38Skettenis 	fdt_node_add_property(child, "status", "okay", strlen("okay") + 1);
3531e44dc38Skettenis 	fdt_node_add_property(child, "format", format, strlen(format) + 1);
3541e44dc38Skettenis 	fdt_node_add_property(child, "stride", &stride, 4);
3551e44dc38Skettenis 	fdt_node_add_property(child, "height", &height, 4);
3561e44dc38Skettenis 	fdt_node_add_property(child, "width", &width, 4);
3571e44dc38Skettenis 	fdt_node_add_property(child, "reg", reg, (acells + scells) * 4);
3581e44dc38Skettenis 	fdt_node_add_property(child, "compatible",
3591e44dc38Skettenis 	    "simple-framebuffer", strlen("simple-framebuffer") + 1);
3601e44dc38Skettenis }
3611e44dc38Skettenis 
362be07ee62Skettenis int acpi = 0;
363*6f83097eSpatrick void *fdt = NULL;
3649ccb13abSnaddy char *bootmac = NULL;
365f24071e5Spatrick static EFI_GUID fdt_guid = FDT_TABLE_GUID;
366f24071e5Spatrick 
367f24071e5Spatrick #define	efi_guidcmp(_a, _b)	memcmp((_a), (_b), sizeof(EFI_GUID))
368f24071e5Spatrick 
369f24071e5Spatrick void *
370f24071e5Spatrick efi_makebootargs(char *bootargs)
371f24071e5Spatrick {
372f4dd1aebSkettenis 	u_char bootduid[8];
373f4dd1aebSkettenis 	u_char zero[8] = { 0 };
374f4dd1aebSkettenis 	uint64_t uefi_system_table = htobe64((uintptr_t)ST);
375f24071e5Spatrick 	void *node;
376f24071e5Spatrick 	size_t len;
377f24071e5Spatrick 	int i;
378f24071e5Spatrick 
379*6f83097eSpatrick 	if (fdt == NULL) {
380f24071e5Spatrick 		for (i = 0; i < ST->NumberOfTableEntries; i++) {
381f24071e5Spatrick 			if (efi_guidcmp(&fdt_guid,
382f24071e5Spatrick 			    &ST->ConfigurationTable[i].VendorGuid) == 0)
383f24071e5Spatrick 				fdt = ST->ConfigurationTable[i].VendorTable;
384f24071e5Spatrick 		}
385*6f83097eSpatrick 	}
386f24071e5Spatrick 
387be07ee62Skettenis 	if (fdt == NULL || acpi)
3889a1480b1Skettenis 		fdt = efi_acpi();
3899a1480b1Skettenis 
390f24071e5Spatrick 	if (!fdt_init(fdt))
391f24071e5Spatrick 		return NULL;
392f24071e5Spatrick 
393f24071e5Spatrick 	node = fdt_find_node("/chosen");
394f24071e5Spatrick 	if (!node)
395f24071e5Spatrick 		return NULL;
396f24071e5Spatrick 
397f24071e5Spatrick 	len = strlen(bootargs) + 1;
398f24071e5Spatrick 	fdt_node_add_property(node, "bootargs", bootargs, len);
399f24071e5Spatrick 
400f24071e5Spatrick 	/* Pass DUID of the boot disk. */
401f24071e5Spatrick 	memcpy(&bootduid, diskinfo.disklabel.d_uid, sizeof(bootduid));
402f24071e5Spatrick 	if (memcmp(bootduid, zero, sizeof(bootduid)) != 0) {
403f24071e5Spatrick 		fdt_node_add_property(node, "openbsd,bootduid", bootduid,
404f24071e5Spatrick 		    sizeof(bootduid));
405f24071e5Spatrick 	}
406f24071e5Spatrick 
4079ccb13abSnaddy 	/* Pass netboot interface address. */
4089ccb13abSnaddy 	if (bootmac)
4099ccb13abSnaddy 		fdt_node_add_property(node, "openbsd,bootmac", bootmac, 6);
4109ccb13abSnaddy 
411f4dd1aebSkettenis 	/* Pass EFI system table. */
412f4dd1aebSkettenis 	fdt_node_add_property(node, "openbsd,uefi-system-table",
413f4dd1aebSkettenis 	    &uefi_system_table, sizeof(uefi_system_table));
414f4dd1aebSkettenis 
415f4dd1aebSkettenis 	/* Placeholders for EFI memory map. */
416f4dd1aebSkettenis 	fdt_node_add_property(node, "openbsd,uefi-mmap-start", zero, 8);
417f4dd1aebSkettenis 	fdt_node_add_property(node, "openbsd,uefi-mmap-size", zero, 4);
418f4dd1aebSkettenis 	fdt_node_add_property(node, "openbsd,uefi-mmap-desc-size", zero, 4);
419f4dd1aebSkettenis 	fdt_node_add_property(node, "openbsd,uefi-mmap-desc-ver", zero, 4);
420f4dd1aebSkettenis 
4211e44dc38Skettenis 	efi_framebuffer();
4221e44dc38Skettenis 
423f24071e5Spatrick 	fdt_finalize();
424f24071e5Spatrick 
425f24071e5Spatrick 	return fdt;
426f24071e5Spatrick }
427f24071e5Spatrick 
428f4dd1aebSkettenis void
429f4dd1aebSkettenis efi_updatefdt(void)
430f4dd1aebSkettenis {
431f4dd1aebSkettenis 	uint64_t uefi_mmap_start = htobe64((uintptr_t)mmap);
432f4dd1aebSkettenis 	uint32_t uefi_mmap_size = htobe32(mmap_ndesc * mmap_descsiz);
433f4dd1aebSkettenis 	uint32_t uefi_mmap_desc_size = htobe32(mmap_descsiz);
434f4dd1aebSkettenis 	uint32_t uefi_mmap_desc_ver = htobe32(mmap_version);
435f4dd1aebSkettenis 	void *node;
436f4dd1aebSkettenis 
437f4dd1aebSkettenis 	node = fdt_find_node("/chosen");
438f4dd1aebSkettenis 	if (!node)
439f4dd1aebSkettenis 		return;
440f4dd1aebSkettenis 
441f4dd1aebSkettenis 	/* Pass EFI memory map. */
442f4dd1aebSkettenis 	fdt_node_set_property(node, "openbsd,uefi-mmap-start",
443f4dd1aebSkettenis 	    &uefi_mmap_start, sizeof(uefi_mmap_start));
444f4dd1aebSkettenis 	fdt_node_set_property(node, "openbsd,uefi-mmap-size",
445f4dd1aebSkettenis 	    &uefi_mmap_size, sizeof(uefi_mmap_size));
446f4dd1aebSkettenis 	fdt_node_set_property(node, "openbsd,uefi-mmap-desc-size",
447f4dd1aebSkettenis 	    &uefi_mmap_desc_size, sizeof(uefi_mmap_desc_size));
448f4dd1aebSkettenis 	fdt_node_set_property(node, "openbsd,uefi-mmap-desc-ver",
449f4dd1aebSkettenis 	    &uefi_mmap_desc_ver, sizeof(uefi_mmap_desc_ver));
450f4dd1aebSkettenis 
451f4dd1aebSkettenis 	fdt_finalize();
452f4dd1aebSkettenis }
453f4dd1aebSkettenis 
454f24071e5Spatrick u_long efi_loadaddr;
455f24071e5Spatrick 
456f24071e5Spatrick void
457f24071e5Spatrick machdep(void)
458f24071e5Spatrick {
459f24071e5Spatrick 	EFI_PHYSICAL_ADDRESS addr;
460f24071e5Spatrick 
461f24071e5Spatrick 	cninit();
462f24071e5Spatrick 	efi_heap_init();
463f24071e5Spatrick 
464f24071e5Spatrick 	/*
4659a118d7eSpatrick 	 * The kernel expects to be loaded into a block of memory aligned
4669a118d7eSpatrick 	 * on a 2MB boundary.  We allocate a block of 64MB of memory, which
4679a118d7eSpatrick 	 * gives us plenty of room for growth.
468f24071e5Spatrick 	 */
4699a118d7eSpatrick 	if (efi_memprobe_find(EFI_SIZE_TO_PAGES(64 * 1024 * 1024),
4709a118d7eSpatrick 	    0x200000, &addr) != EFI_SUCCESS)
471f24071e5Spatrick 		printf("Can't allocate memory\n");
472f24071e5Spatrick 	efi_loadaddr = addr;
473f24071e5Spatrick 
474f24071e5Spatrick 	efi_timer_init();
475f24071e5Spatrick 	efi_diskprobe();
47633e5575aSpatrick 	efi_pxeprobe();
477f24071e5Spatrick }
478f24071e5Spatrick 
479f24071e5Spatrick void
480f24071e5Spatrick efi_cleanup(void)
481f24071e5Spatrick {
482fa854029Spatrick 	int		 retry;
483fa854029Spatrick 	EFI_STATUS	 status;
484fa854029Spatrick 
485f24071e5Spatrick 	efi_timer_cleanup();
486f24071e5Spatrick 
487fa854029Spatrick 	/* retry once in case of failure */
488fa854029Spatrick 	for (retry = 1; retry >= 0; retry--) {
489fa854029Spatrick 		efi_memprobe_internal();	/* sync the current map */
490f4dd1aebSkettenis 		efi_updatefdt();
491fa854029Spatrick 		status = EFI_CALL(BS->ExitBootServices, IH, mmap_key);
492fa854029Spatrick 		if (status == EFI_SUCCESS)
493fa854029Spatrick 			break;
494fa854029Spatrick 		if (retry == 0)
495fa854029Spatrick 			panic("ExitBootServices failed (%d)", status);
496fa854029Spatrick 	}
497f24071e5Spatrick }
498f24071e5Spatrick 
499f24071e5Spatrick void
500f24071e5Spatrick _rtt(void)
501f24071e5Spatrick {
502f24071e5Spatrick #ifdef EFI_DEBUG
503f24071e5Spatrick 	printf("Hit any key to reboot\n");
504f24071e5Spatrick 	efi_cons_getc(0);
505f24071e5Spatrick #endif
506f24071e5Spatrick 	RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
507111d6387Skettenis 	for (;;)
508111d6387Skettenis 		continue;
509f24071e5Spatrick }
510f24071e5Spatrick 
511f24071e5Spatrick /*
512f24071e5Spatrick  * U-Boot only implements the GetTime() Runtime Service if it has been
513f24071e5Spatrick  * configured with CONFIG_DM_RTC.  Most board configurations don't
514f24071e5Spatrick  * include that option, so we can't use it to implement our boot
515f24071e5Spatrick  * prompt timeout.  Instead we use timer events to simulate a clock
516f24071e5Spatrick  * that ticks ever second.
517f24071e5Spatrick  */
518f24071e5Spatrick 
519f24071e5Spatrick EFI_EVENT timer;
520f24071e5Spatrick int ticks;
521f24071e5Spatrick 
522f24071e5Spatrick static VOID
523f24071e5Spatrick efi_timer(EFI_EVENT event, VOID *context)
524f24071e5Spatrick {
525f24071e5Spatrick 	ticks++;
526f24071e5Spatrick }
527f24071e5Spatrick 
528f24071e5Spatrick static void
529f24071e5Spatrick efi_timer_init(void)
530f24071e5Spatrick {
531f24071e5Spatrick 	EFI_STATUS status;
532f24071e5Spatrick 
533a4d88df4Sjsg 	status = BS->CreateEvent(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
534f24071e5Spatrick 	    efi_timer, NULL, &timer);
535f24071e5Spatrick 	if (status == EFI_SUCCESS)
536f24071e5Spatrick 		status = BS->SetTimer(timer, TimerPeriodic, 10000000);
537f24071e5Spatrick 	if (EFI_ERROR(status))
538f24071e5Spatrick 		printf("Can't create timer\n");
539f24071e5Spatrick }
540f24071e5Spatrick 
541f24071e5Spatrick static void
542f24071e5Spatrick efi_timer_cleanup(void)
543f24071e5Spatrick {
544f24071e5Spatrick 	BS->CloseEvent(timer);
545f24071e5Spatrick }
546f24071e5Spatrick 
547f24071e5Spatrick time_t
548f24071e5Spatrick getsecs(void)
549f24071e5Spatrick {
550f24071e5Spatrick 	return ticks;
551f24071e5Spatrick }
552f24071e5Spatrick 
553f24071e5Spatrick /*
554f24071e5Spatrick  * Various device-related bits.
555f24071e5Spatrick  */
556f24071e5Spatrick 
557f24071e5Spatrick void
558f24071e5Spatrick devboot(dev_t dev, char *p)
559f24071e5Spatrick {
56033e5575aSpatrick 	if (disk)
561f24071e5Spatrick 		strlcpy(p, "sd0a", 5);
56233e5575aSpatrick 	else
56333e5575aSpatrick 		strlcpy(p, "tftp0a", 7);
564f24071e5Spatrick }
565f24071e5Spatrick 
566f24071e5Spatrick int
567f24071e5Spatrick cnspeed(dev_t dev, int sp)
568f24071e5Spatrick {
569f24071e5Spatrick 	return 115200;
570f24071e5Spatrick }
571f24071e5Spatrick 
572f24071e5Spatrick char *
573f24071e5Spatrick ttyname(int fd)
574f24071e5Spatrick {
575f24071e5Spatrick 	return "com0";
576f24071e5Spatrick }
577f24071e5Spatrick 
578f24071e5Spatrick dev_t
579f24071e5Spatrick ttydev(char *name)
580f24071e5Spatrick {
581f24071e5Spatrick 	return NODEV;
582f24071e5Spatrick }
583f24071e5Spatrick 
584f24071e5Spatrick #define MAXDEVNAME	16
585f24071e5Spatrick 
586f24071e5Spatrick /*
587f24071e5Spatrick  * Parse a device spec.
588f24071e5Spatrick  *
589f24071e5Spatrick  * [A-Za-z]*[0-9]*[A-Za-z]:file
590f24071e5Spatrick  *    dev   uint    part
591f24071e5Spatrick  */
592f24071e5Spatrick int
593f24071e5Spatrick devparse(const char *fname, int *dev, int *unit, int *part, const char **file)
594f24071e5Spatrick {
595f24071e5Spatrick 	const char *s;
596f24071e5Spatrick 
597f24071e5Spatrick 	*unit = 0;	/* default to wd0a */
598f24071e5Spatrick 	*part = 0;
599f24071e5Spatrick 	*dev  = 0;
600f24071e5Spatrick 
601f24071e5Spatrick 	s = strchr(fname, ':');
602f24071e5Spatrick 	if (s != NULL) {
603f24071e5Spatrick 		int devlen;
604f24071e5Spatrick 		int i, u, p = 0;
605f24071e5Spatrick 		struct devsw *dp;
606f24071e5Spatrick 		char devname[MAXDEVNAME];
607f24071e5Spatrick 
608f24071e5Spatrick 		devlen = s - fname;
609f24071e5Spatrick 		if (devlen > MAXDEVNAME)
610f24071e5Spatrick 			return (EINVAL);
611f24071e5Spatrick 
612f24071e5Spatrick 		/* extract device name */
613f24071e5Spatrick 		for (i = 0; isalpha(fname[i]) && (i < devlen); i++)
614f24071e5Spatrick 			devname[i] = fname[i];
615f24071e5Spatrick 		devname[i] = 0;
616f24071e5Spatrick 
617f24071e5Spatrick 		if (!isdigit(fname[i]))
618f24071e5Spatrick 			return (EUNIT);
619f24071e5Spatrick 
620f24071e5Spatrick 		/* device number */
621f24071e5Spatrick 		for (u = 0; isdigit(fname[i]) && (i < devlen); i++)
622f24071e5Spatrick 			u = u * 10 + (fname[i] - '0');
623f24071e5Spatrick 
624f24071e5Spatrick 		if (!isalpha(fname[i]))
625f24071e5Spatrick 			return (EPART);
626f24071e5Spatrick 
627f24071e5Spatrick 		/* partition number */
628f24071e5Spatrick 		if (i < devlen)
629f24071e5Spatrick 			p = fname[i++] - 'a';
630f24071e5Spatrick 
631f24071e5Spatrick 		if (i != devlen)
632f24071e5Spatrick 			return (ENXIO);
633f24071e5Spatrick 
634f24071e5Spatrick 		/* check device name */
635f24071e5Spatrick 		for (dp = devsw, i = 0; i < ndevs; dp++, i++) {
636f24071e5Spatrick 			if (dp->dv_name && !strcmp(devname, dp->dv_name))
637f24071e5Spatrick 				break;
638f24071e5Spatrick 		}
639f24071e5Spatrick 
640f24071e5Spatrick 		if (i >= ndevs)
641f24071e5Spatrick 			return (ENXIO);
642f24071e5Spatrick 
643f24071e5Spatrick 		*unit = u;
644f24071e5Spatrick 		*part = p;
645f24071e5Spatrick 		*dev  = i;
646f24071e5Spatrick 		fname = ++s;
647f24071e5Spatrick 	}
648f24071e5Spatrick 
649f24071e5Spatrick 	*file = fname;
650f24071e5Spatrick 
651f24071e5Spatrick 	return (0);
652f24071e5Spatrick }
653f24071e5Spatrick 
654f24071e5Spatrick int
655f24071e5Spatrick devopen(struct open_file *f, const char *fname, char **file)
656f24071e5Spatrick {
657f24071e5Spatrick 	struct devsw *dp;
658f24071e5Spatrick 	int dev, unit, part, error;
659f24071e5Spatrick 
660f24071e5Spatrick 	error = devparse(fname, &dev, &unit, &part, (const char **)file);
661f24071e5Spatrick 	if (error)
662f24071e5Spatrick 		return (error);
663f24071e5Spatrick 
66433e5575aSpatrick 	dp = &devsw[dev];
665f24071e5Spatrick 	f->f_dev = dp;
666f24071e5Spatrick 
667f24071e5Spatrick 	return (*dp->dv_open)(f, unit, part);
668f24071e5Spatrick }
669f24071e5Spatrick 
670fa854029Spatrick static void
671fa854029Spatrick efi_memprobe_internal(void)
672fa854029Spatrick {
673fa854029Spatrick 	EFI_STATUS		 status;
674fa854029Spatrick 	UINTN			 mapkey, mmsiz, siz;
675fa854029Spatrick 	UINT32			 mmver;
676fa854029Spatrick 	EFI_MEMORY_DESCRIPTOR	*mm;
677fa854029Spatrick 	int			 n;
678fa854029Spatrick 
679fa854029Spatrick 	free(mmap, mmap_ndesc * mmap_descsiz);
680fa854029Spatrick 
681fa854029Spatrick 	siz = 0;
682fa854029Spatrick 	status = EFI_CALL(BS->GetMemoryMap, &siz, NULL, &mapkey, &mmsiz,
683fa854029Spatrick 	    &mmver);
684fa854029Spatrick 	if (status != EFI_BUFFER_TOO_SMALL)
685fa854029Spatrick 		panic("cannot get the size of memory map");
686fa854029Spatrick 	mm = alloc(siz);
687fa854029Spatrick 	status = EFI_CALL(BS->GetMemoryMap, &siz, mm, &mapkey, &mmsiz, &mmver);
688fa854029Spatrick 	if (status != EFI_SUCCESS)
689fa854029Spatrick 		panic("cannot get the memory map");
690fa854029Spatrick 	n = siz / mmsiz;
691fa854029Spatrick 	mmap = mm;
692fa854029Spatrick 	mmap_key = mapkey;
693fa854029Spatrick 	mmap_ndesc = n;
694fa854029Spatrick 	mmap_descsiz = mmsiz;
695f4dd1aebSkettenis 	mmap_version = mmver;
696fa854029Spatrick }
697fa854029Spatrick 
698f24071e5Spatrick /*
699f24071e5Spatrick  * 64-bit ARMs can have a much wider memory mapping, as in somewhere
700f24071e5Spatrick  * after the 32-bit region.  To cope with our alignment requirement,
701f24071e5Spatrick  * use the memory table to find a place where we can fit.
702f24071e5Spatrick  */
703f24071e5Spatrick static EFI_STATUS
704f24071e5Spatrick efi_memprobe_find(UINTN pages, UINTN align, EFI_PHYSICAL_ADDRESS *addr)
705f24071e5Spatrick {
706fa854029Spatrick 	EFI_MEMORY_DESCRIPTOR	*mm;
707fa854029Spatrick 	int			 i, j;
708f24071e5Spatrick 
709f24071e5Spatrick 	if (align < EFI_PAGE_SIZE)
710f24071e5Spatrick 		return EFI_INVALID_PARAMETER;
711f24071e5Spatrick 
712fa854029Spatrick 	efi_memprobe_internal();	/* sync the current map */
713f24071e5Spatrick 
714fa854029Spatrick 	for (i = 0, mm = mmap; i < mmap_ndesc;
715fa854029Spatrick 	    i++, mm = NextMemoryDescriptor(mm, mmap_descsiz)) {
716f24071e5Spatrick 		if (mm->Type != EfiConventionalMemory)
717f24071e5Spatrick 			continue;
718f24071e5Spatrick 
719f24071e5Spatrick 		if (mm->NumberOfPages < pages)
720f24071e5Spatrick 			continue;
721f24071e5Spatrick 
722f2c0e408Skettenis 		for (j = 0; j < mm->NumberOfPages; j++) {
723f24071e5Spatrick 			EFI_PHYSICAL_ADDRESS paddr;
724f24071e5Spatrick 
725f24071e5Spatrick 			if (mm->NumberOfPages - j < pages)
726f24071e5Spatrick 				break;
727f24071e5Spatrick 
728f24071e5Spatrick 			paddr = mm->PhysicalStart + (j * EFI_PAGE_SIZE);
72973dc1409Spatrick 			if (paddr & (align - 1))
73073dc1409Spatrick 				continue;
73173dc1409Spatrick 
73273dc1409Spatrick 			if (EFI_CALL(BS->AllocatePages, AllocateAddress,
73373dc1409Spatrick 			    EfiLoaderData, pages, &paddr) == EFI_SUCCESS) {
734f24071e5Spatrick 				*addr = paddr;
735f24071e5Spatrick 				return EFI_SUCCESS;
736f24071e5Spatrick 			}
737f24071e5Spatrick 		}
738f24071e5Spatrick 	}
739f24071e5Spatrick 	return EFI_OUT_OF_RESOURCES;
740f24071e5Spatrick }
741111d6387Skettenis 
742111d6387Skettenis /*
743111d6387Skettenis  * Commands
744111d6387Skettenis  */
745111d6387Skettenis 
746be07ee62Skettenis int Xacpi_efi(void);
747*6f83097eSpatrick int Xdtb_efi(void);
748111d6387Skettenis int Xexit_efi(void);
749111d6387Skettenis int Xpoweroff_efi(void);
750111d6387Skettenis 
751111d6387Skettenis const struct cmd_table cmd_machine[] = {
752be07ee62Skettenis 	{ "acpi",	CMDT_CMD, Xacpi_efi },
753*6f83097eSpatrick 	{ "dtb",	CMDT_CMD, Xdtb_efi },
754111d6387Skettenis 	{ "exit",	CMDT_CMD, Xexit_efi },
755111d6387Skettenis 	{ "poweroff",	CMDT_CMD, Xpoweroff_efi },
756111d6387Skettenis 	{ NULL, 0 }
757111d6387Skettenis };
758111d6387Skettenis 
759111d6387Skettenis int
760be07ee62Skettenis Xacpi_efi(void)
761be07ee62Skettenis {
762be07ee62Skettenis 	acpi = 1;
763be07ee62Skettenis 	return (0);
764be07ee62Skettenis }
765be07ee62Skettenis 
766be07ee62Skettenis int
767*6f83097eSpatrick Xdtb_efi(void)
768*6f83097eSpatrick {
769*6f83097eSpatrick 	EFI_PHYSICAL_ADDRESS addr;
770*6f83097eSpatrick 	char path[MAXPATHLEN];
771*6f83097eSpatrick 	struct stat sb;
772*6f83097eSpatrick 	int fd;
773*6f83097eSpatrick 
774*6f83097eSpatrick #define O_RDONLY	0
775*6f83097eSpatrick 
776*6f83097eSpatrick 	if (cmd.argc != 2)
777*6f83097eSpatrick 		return (1);
778*6f83097eSpatrick 
779*6f83097eSpatrick 	snprintf(path, sizeof(path), "%s:%s", cmd.bootdev, cmd.argv[1]);
780*6f83097eSpatrick 
781*6f83097eSpatrick 	fd = open(path, O_RDONLY);
782*6f83097eSpatrick 	if (fd < 0 || fstat(fd, &sb) == -1) {
783*6f83097eSpatrick 		printf("cannot open %s\n", path);
784*6f83097eSpatrick 		return (1);
785*6f83097eSpatrick 	}
786*6f83097eSpatrick 	if (efi_memprobe_find(EFI_SIZE_TO_PAGES(sb.st_size),
787*6f83097eSpatrick 	    0x1000, &addr) != EFI_SUCCESS) {
788*6f83097eSpatrick 		printf("cannot allocate memory for %s\n", path);
789*6f83097eSpatrick 		return (1);
790*6f83097eSpatrick 	}
791*6f83097eSpatrick 	if (read(fd, (void *)addr, sb.st_size) != sb.st_size) {
792*6f83097eSpatrick 		printf("cannot read from %s\n", path);
793*6f83097eSpatrick 		return (1);
794*6f83097eSpatrick 	}
795*6f83097eSpatrick 
796*6f83097eSpatrick 	fdt = (void *)addr;
797*6f83097eSpatrick 	return (0);
798*6f83097eSpatrick }
799*6f83097eSpatrick 
800*6f83097eSpatrick int
801111d6387Skettenis Xexit_efi(void)
802111d6387Skettenis {
803111d6387Skettenis 	EFI_CALL(BS->Exit, IH, 0, 0, NULL);
804111d6387Skettenis 	for (;;)
805111d6387Skettenis 		continue;
806111d6387Skettenis 	return (0);
807111d6387Skettenis }
808111d6387Skettenis 
809111d6387Skettenis int
810111d6387Skettenis Xpoweroff_efi(void)
811111d6387Skettenis {
812111d6387Skettenis 	EFI_CALL(RS->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
813111d6387Skettenis 	return (0);
814111d6387Skettenis }
815