xref: /openbsd/sys/arch/arm64/stand/efiboot/efiboot.c (revision 111d6387)
1*111d6387Skettenis /*	$OpenBSD: efiboot.c,v 1.11 2017/08/07 19:34:53 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>
22f24071e5Spatrick #include <dev/cons.h>
23f24071e5Spatrick #include <sys/disklabel.h>
24f24071e5Spatrick 
25f24071e5Spatrick #include <efi.h>
26f24071e5Spatrick #include <efiapi.h>
27f24071e5Spatrick #include <efiprot.h>
28f24071e5Spatrick #include <eficonsctl.h>
29f24071e5Spatrick 
30f24071e5Spatrick #include <lib/libkern/libkern.h>
31f24071e5Spatrick #include <stand/boot/cmd.h>
32f24071e5Spatrick 
33f24071e5Spatrick #include "disk.h"
34f24071e5Spatrick #include "eficall.h"
35f24071e5Spatrick #include "fdt.h"
36f24071e5Spatrick #include "libsa.h"
37f24071e5Spatrick 
38f24071e5Spatrick EFI_SYSTEM_TABLE	*ST;
39f24071e5Spatrick EFI_BOOT_SERVICES	*BS;
40f24071e5Spatrick EFI_RUNTIME_SERVICES	*RS;
41fa854029Spatrick EFI_HANDLE		 IH, efi_bootdp = NULL;
42f24071e5Spatrick 
43fa854029Spatrick EFI_PHYSICAL_ADDRESS	 heap;
44fa854029Spatrick UINTN			 heapsiz = 1 * 1024 * 1024;
45fa854029Spatrick EFI_MEMORY_DESCRIPTOR	*mmap = NULL;
46fa854029Spatrick UINTN			 mmap_key;
47fa854029Spatrick UINTN			 mmap_ndesc;
48fa854029Spatrick UINTN			 mmap_descsiz;
49f4dd1aebSkettenis UINT32			 mmap_version;
50f24071e5Spatrick 
51f24071e5Spatrick static EFI_GUID		 imgp_guid = LOADED_IMAGE_PROTOCOL;
52f24071e5Spatrick static EFI_GUID		 blkio_guid = BLOCK_IO_PROTOCOL;
53f24071e5Spatrick static EFI_GUID		 devp_guid = DEVICE_PATH_PROTOCOL;
54f24071e5Spatrick 
55c6d7d0adSpatrick static int efi_device_path_depth(EFI_DEVICE_PATH *dp, int);
56c6d7d0adSpatrick static int efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int);
57fa854029Spatrick static void	 efi_heap_init(void);
58fa854029Spatrick static void	 efi_memprobe_internal(void);
59f24071e5Spatrick static void	 efi_timer_init(void);
60f24071e5Spatrick static void	 efi_timer_cleanup(void);
61f24071e5Spatrick static EFI_STATUS efi_memprobe_find(UINTN, UINTN, EFI_PHYSICAL_ADDRESS *);
62f24071e5Spatrick 
63f24071e5Spatrick EFI_STATUS
64f24071e5Spatrick efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
65f24071e5Spatrick {
66f24071e5Spatrick 	extern char		*progname;
67f24071e5Spatrick 	EFI_LOADED_IMAGE	*imgp;
68f24071e5Spatrick 	EFI_DEVICE_PATH		*dp = NULL;
69f24071e5Spatrick 	EFI_STATUS		 status;
70f24071e5Spatrick 
71f24071e5Spatrick 	ST = systab;
72f24071e5Spatrick 	BS = ST->BootServices;
73f24071e5Spatrick 	IH = image;
74f24071e5Spatrick 
75f24071e5Spatrick 	status = EFI_CALL(BS->HandleProtocol, image, &imgp_guid,
76f24071e5Spatrick 	    (void **)&imgp);
77f24071e5Spatrick 	if (status == EFI_SUCCESS)
78f24071e5Spatrick 		status = EFI_CALL(BS->HandleProtocol, imgp->DeviceHandle,
79f24071e5Spatrick 		    &devp_guid, (void **)&dp);
80f24071e5Spatrick 	if (status == EFI_SUCCESS)
81f24071e5Spatrick 		efi_bootdp = dp;
82f24071e5Spatrick 
83f24071e5Spatrick 	progname = "BOOTAA64";
84f24071e5Spatrick 
85f24071e5Spatrick 	boot(0);
86f24071e5Spatrick 
87f24071e5Spatrick 	return (EFI_SUCCESS);
88f24071e5Spatrick }
89f24071e5Spatrick 
90f24071e5Spatrick static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
91f24071e5Spatrick static SIMPLE_INPUT_INTERFACE *conin;
92f24071e5Spatrick 
93f24071e5Spatrick void
94f24071e5Spatrick efi_cons_probe(struct consdev *cn)
95f24071e5Spatrick {
96f24071e5Spatrick 	cn->cn_pri = CN_MIDPRI;
97f24071e5Spatrick 	cn->cn_dev = makedev(12, 0);
98f24071e5Spatrick }
99f24071e5Spatrick 
100f24071e5Spatrick void
101f24071e5Spatrick efi_cons_init(struct consdev *cp)
102f24071e5Spatrick {
103f24071e5Spatrick 	conin = ST->ConIn;
104f24071e5Spatrick 	conout = ST->ConOut;
105f24071e5Spatrick }
106f24071e5Spatrick 
107f24071e5Spatrick int
108f24071e5Spatrick efi_cons_getc(dev_t dev)
109f24071e5Spatrick {
110f24071e5Spatrick 	EFI_INPUT_KEY	 key;
111f24071e5Spatrick 	EFI_STATUS	 status;
112f24071e5Spatrick #if 0
113f24071e5Spatrick 	UINTN		 dummy;
114f24071e5Spatrick #endif
115f24071e5Spatrick 	static int	 lastchar = 0;
116f24071e5Spatrick 
117f24071e5Spatrick 	if (lastchar) {
118f24071e5Spatrick 		int r = lastchar;
119f24071e5Spatrick 		if ((dev & 0x80) == 0)
120f24071e5Spatrick 			lastchar = 0;
121f24071e5Spatrick 		return (r);
122f24071e5Spatrick 	}
123f24071e5Spatrick 
124f24071e5Spatrick 	status = conin->ReadKeyStroke(conin, &key);
125f24071e5Spatrick 	while (status == EFI_NOT_READY) {
126f24071e5Spatrick 		if (dev & 0x80)
127f24071e5Spatrick 			return (0);
128f24071e5Spatrick 		/*
129f24071e5Spatrick 		 * XXX The implementation of WaitForEvent() in U-boot
130f24071e5Spatrick 		 * is broken and neverreturns.
131f24071e5Spatrick 		 */
132f24071e5Spatrick #if 0
133f24071e5Spatrick 		BS->WaitForEvent(1, &conin->WaitForKey, &dummy);
134f24071e5Spatrick #endif
135f24071e5Spatrick 		status = conin->ReadKeyStroke(conin, &key);
136f24071e5Spatrick 	}
137f24071e5Spatrick 
138f24071e5Spatrick 	if (dev & 0x80)
139f24071e5Spatrick 		lastchar = key.UnicodeChar;
140f24071e5Spatrick 
141f24071e5Spatrick 	return (key.UnicodeChar);
142f24071e5Spatrick }
143f24071e5Spatrick 
144f24071e5Spatrick void
145f24071e5Spatrick efi_cons_putc(dev_t dev, int c)
146f24071e5Spatrick {
147f24071e5Spatrick 	CHAR16	buf[2];
148f24071e5Spatrick 
149f24071e5Spatrick 	if (c == '\n')
150f24071e5Spatrick 		efi_cons_putc(dev, '\r');
151f24071e5Spatrick 
152f24071e5Spatrick 	buf[0] = c;
153f24071e5Spatrick 	buf[1] = 0;
154f24071e5Spatrick 
155f24071e5Spatrick 	conout->OutputString(conout, buf);
156f24071e5Spatrick }
157f24071e5Spatrick 
158f24071e5Spatrick static void
159f24071e5Spatrick efi_heap_init(void)
160f24071e5Spatrick {
161f24071e5Spatrick 	EFI_STATUS	 status;
162f24071e5Spatrick 
163f24071e5Spatrick 	status = EFI_CALL(BS->AllocatePages, AllocateAnyPages, EfiLoaderData,
164f24071e5Spatrick 	    EFI_SIZE_TO_PAGES(heapsiz), &heap);
165f24071e5Spatrick 	if (status != EFI_SUCCESS)
166f24071e5Spatrick 		panic("BS->AllocatePages()");
167f24071e5Spatrick }
168f24071e5Spatrick 
169f24071e5Spatrick EFI_BLOCK_IO	*disk;
170f24071e5Spatrick 
171f24071e5Spatrick void
172f24071e5Spatrick efi_diskprobe(void)
173f24071e5Spatrick {
174c6d7d0adSpatrick 	int			 i, depth = -1;
175f24071e5Spatrick 	UINTN			 sz;
176f24071e5Spatrick 	EFI_STATUS		 status;
177f24071e5Spatrick 	EFI_HANDLE		*handles = NULL;
178f24071e5Spatrick 	EFI_BLOCK_IO		*blkio;
179f24071e5Spatrick 	EFI_BLOCK_IO_MEDIA	*media;
180c6d7d0adSpatrick 	EFI_DEVICE_PATH		*dp;
181f24071e5Spatrick 
182f24071e5Spatrick 	sz = 0;
183f24071e5Spatrick 	status = EFI_CALL(BS->LocateHandle, ByProtocol, &blkio_guid, 0, &sz, 0);
184f24071e5Spatrick 	if (status == EFI_BUFFER_TOO_SMALL) {
185f24071e5Spatrick 		handles = alloc(sz);
186f24071e5Spatrick 		status = EFI_CALL(BS->LocateHandle, ByProtocol, &blkio_guid,
187f24071e5Spatrick 		    0, &sz, handles);
188f24071e5Spatrick 	}
189f24071e5Spatrick 	if (handles == NULL || EFI_ERROR(status))
190f24071e5Spatrick 		panic("BS->LocateHandle() returns %d", status);
191f24071e5Spatrick 
192c6d7d0adSpatrick 	if (efi_bootdp != NULL)
193c6d7d0adSpatrick 		depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
194c6d7d0adSpatrick 
195c94e7257Skettenis 	/*
196c94e7257Skettenis 	 * U-Boot incorrectly represents devices with a single
197c94e7257Skettenis 	 * MEDIA_DEVICE_PATH component.  In that case include that
198c94e7257Skettenis 	 * component into the matching, otherwise we'll blindly select
199c94e7257Skettenis 	 * the first device.
200c94e7257Skettenis 	 */
201c94e7257Skettenis 	if (depth == 0)
202c94e7257Skettenis 		depth = 1;
203c94e7257Skettenis 
204f24071e5Spatrick 	for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) {
205f24071e5Spatrick 		status = EFI_CALL(BS->HandleProtocol, handles[i], &blkio_guid,
206f24071e5Spatrick 		    (void **)&blkio);
207f24071e5Spatrick 		if (EFI_ERROR(status))
208f24071e5Spatrick 			panic("BS->HandleProtocol() returns %d", status);
209f24071e5Spatrick 
210f24071e5Spatrick 		media = blkio->Media;
211f24071e5Spatrick 		if (media->LogicalPartition || !media->MediaPresent)
212f24071e5Spatrick 			continue;
213f24071e5Spatrick 
214c6d7d0adSpatrick 		if (efi_bootdp == NULL || depth == -1)
215c6d7d0adSpatrick 			continue;
216f24071e5Spatrick 		status = EFI_CALL(BS->HandleProtocol, handles[i], &devp_guid,
217f24071e5Spatrick 		    (void **)&dp);
218f24071e5Spatrick 		if (EFI_ERROR(status))
219c6d7d0adSpatrick 			continue;
220c6d7d0adSpatrick 		if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) {
221f24071e5Spatrick 			disk = blkio;
222f24071e5Spatrick 			break;
223f24071e5Spatrick 		}
224f24071e5Spatrick 	}
225f24071e5Spatrick 
226f24071e5Spatrick 	free(handles, sz);
227f24071e5Spatrick }
228f24071e5Spatrick 
229c94e7257Skettenis /*
230c94e7257Skettenis  * Determine the number of nodes up to, but not including, the first
231c94e7257Skettenis  * node of the specified type.
232c94e7257Skettenis  */
233c6d7d0adSpatrick static int
234c6d7d0adSpatrick efi_device_path_depth(EFI_DEVICE_PATH *dp, int dptype)
235c6d7d0adSpatrick {
236c6d7d0adSpatrick 	int	i;
237c6d7d0adSpatrick 
238c6d7d0adSpatrick 	for (i = 0; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp), i++) {
239c6d7d0adSpatrick 		if (DevicePathType(dp) == dptype)
240c94e7257Skettenis 			return (i);
241c6d7d0adSpatrick 	}
242c6d7d0adSpatrick 
243c6d7d0adSpatrick 	return (-1);
244c6d7d0adSpatrick }
245c6d7d0adSpatrick 
246c6d7d0adSpatrick static int
247c6d7d0adSpatrick efi_device_path_ncmp(EFI_DEVICE_PATH *dpa, EFI_DEVICE_PATH *dpb, int deptn)
248c6d7d0adSpatrick {
249c6d7d0adSpatrick 	int	 i, cmp;
250c6d7d0adSpatrick 
251c6d7d0adSpatrick 	for (i = 0; i < deptn; i++) {
252c6d7d0adSpatrick 		if (IsDevicePathEnd(dpa) || IsDevicePathEnd(dpb))
253c6d7d0adSpatrick 			return ((IsDevicePathEnd(dpa) && IsDevicePathEnd(dpb))
254c6d7d0adSpatrick 			    ? 0 : (IsDevicePathEnd(dpa))? -1 : 1);
255c6d7d0adSpatrick 		cmp = DevicePathNodeLength(dpa) - DevicePathNodeLength(dpb);
256c6d7d0adSpatrick 		if (cmp)
257c6d7d0adSpatrick 			return (cmp);
258c6d7d0adSpatrick 		cmp = memcmp(dpa, dpb, DevicePathNodeLength(dpa));
259c6d7d0adSpatrick 		if (cmp)
260c6d7d0adSpatrick 			return (cmp);
261c6d7d0adSpatrick 		dpa = NextDevicePathNode(dpa);
262c6d7d0adSpatrick 		dpb = NextDevicePathNode(dpb);
263c6d7d0adSpatrick 	}
264c6d7d0adSpatrick 
265c6d7d0adSpatrick 	return (0);
266c6d7d0adSpatrick }
267c6d7d0adSpatrick 
268f24071e5Spatrick static EFI_GUID fdt_guid = FDT_TABLE_GUID;
269f24071e5Spatrick 
270f24071e5Spatrick #define	efi_guidcmp(_a, _b)	memcmp((_a), (_b), sizeof(EFI_GUID))
271f24071e5Spatrick 
272f24071e5Spatrick void *
273f24071e5Spatrick efi_makebootargs(char *bootargs)
274f24071e5Spatrick {
275f24071e5Spatrick 	void *fdt = NULL;
276f4dd1aebSkettenis 	u_char bootduid[8];
277f4dd1aebSkettenis 	u_char zero[8] = { 0 };
278f4dd1aebSkettenis 	uint64_t uefi_system_table = htobe64((uintptr_t)ST);
279f24071e5Spatrick 	void *node;
280f24071e5Spatrick 	size_t len;
281f24071e5Spatrick 	int i;
282f24071e5Spatrick 
283f24071e5Spatrick 	for (i = 0; i < ST->NumberOfTableEntries; i++) {
284f24071e5Spatrick 		if (efi_guidcmp(&fdt_guid,
285f24071e5Spatrick 		    &ST->ConfigurationTable[i].VendorGuid) == 0)
286f24071e5Spatrick 			fdt = ST->ConfigurationTable[i].VendorTable;
287f24071e5Spatrick 	}
288f24071e5Spatrick 
289f24071e5Spatrick 	if (!fdt_init(fdt))
290f24071e5Spatrick 		return NULL;
291f24071e5Spatrick 
292f24071e5Spatrick 	node = fdt_find_node("/chosen");
293f24071e5Spatrick 	if (!node)
294f24071e5Spatrick 		return NULL;
295f24071e5Spatrick 
296f24071e5Spatrick 	len = strlen(bootargs) + 1;
297f24071e5Spatrick 	fdt_node_add_property(node, "bootargs", bootargs, len);
298f24071e5Spatrick 
299f24071e5Spatrick 	/* Pass DUID of the boot disk. */
300f24071e5Spatrick 	memcpy(&bootduid, diskinfo.disklabel.d_uid, sizeof(bootduid));
301f24071e5Spatrick 	if (memcmp(bootduid, zero, sizeof(bootduid)) != 0) {
302f24071e5Spatrick 		fdt_node_add_property(node, "openbsd,bootduid", bootduid,
303f24071e5Spatrick 		    sizeof(bootduid));
304f24071e5Spatrick 	}
305f24071e5Spatrick 
306f4dd1aebSkettenis 	/* Pass EFI system table. */
307f4dd1aebSkettenis 	fdt_node_add_property(node, "openbsd,uefi-system-table",
308f4dd1aebSkettenis 	    &uefi_system_table, sizeof(uefi_system_table));
309f4dd1aebSkettenis 
310f4dd1aebSkettenis 	/* Placeholders for EFI memory map. */
311f4dd1aebSkettenis 	fdt_node_add_property(node, "openbsd,uefi-mmap-start", zero, 8);
312f4dd1aebSkettenis 	fdt_node_add_property(node, "openbsd,uefi-mmap-size", zero, 4);
313f4dd1aebSkettenis 	fdt_node_add_property(node, "openbsd,uefi-mmap-desc-size", zero, 4);
314f4dd1aebSkettenis 	fdt_node_add_property(node, "openbsd,uefi-mmap-desc-ver", zero, 4);
315f4dd1aebSkettenis 
316f24071e5Spatrick 	fdt_finalize();
317f24071e5Spatrick 
318f24071e5Spatrick 	return fdt;
319f24071e5Spatrick }
320f24071e5Spatrick 
321f4dd1aebSkettenis void
322f4dd1aebSkettenis efi_updatefdt(void)
323f4dd1aebSkettenis {
324f4dd1aebSkettenis 	uint64_t uefi_mmap_start = htobe64((uintptr_t)mmap);
325f4dd1aebSkettenis 	uint32_t uefi_mmap_size = htobe32(mmap_ndesc * mmap_descsiz);
326f4dd1aebSkettenis 	uint32_t uefi_mmap_desc_size = htobe32(mmap_descsiz);
327f4dd1aebSkettenis 	uint32_t uefi_mmap_desc_ver = htobe32(mmap_version);
328f4dd1aebSkettenis 	void *node;
329f4dd1aebSkettenis 
330f4dd1aebSkettenis 	node = fdt_find_node("/chosen");
331f4dd1aebSkettenis 	if (!node)
332f4dd1aebSkettenis 		return;
333f4dd1aebSkettenis 
334f4dd1aebSkettenis 	/* Pass EFI memory map. */
335f4dd1aebSkettenis 	fdt_node_set_property(node, "openbsd,uefi-mmap-start",
336f4dd1aebSkettenis 	    &uefi_mmap_start, sizeof(uefi_mmap_start));
337f4dd1aebSkettenis 	fdt_node_set_property(node, "openbsd,uefi-mmap-size",
338f4dd1aebSkettenis 	    &uefi_mmap_size, sizeof(uefi_mmap_size));
339f4dd1aebSkettenis 	fdt_node_set_property(node, "openbsd,uefi-mmap-desc-size",
340f4dd1aebSkettenis 	    &uefi_mmap_desc_size, sizeof(uefi_mmap_desc_size));
341f4dd1aebSkettenis 	fdt_node_set_property(node, "openbsd,uefi-mmap-desc-ver",
342f4dd1aebSkettenis 	    &uefi_mmap_desc_ver, sizeof(uefi_mmap_desc_ver));
343f4dd1aebSkettenis 
344f4dd1aebSkettenis 	fdt_finalize();
345f4dd1aebSkettenis }
346f4dd1aebSkettenis 
347f24071e5Spatrick u_long efi_loadaddr;
348f24071e5Spatrick 
349f24071e5Spatrick void
350f24071e5Spatrick machdep(void)
351f24071e5Spatrick {
352f24071e5Spatrick 	EFI_PHYSICAL_ADDRESS addr;
353f24071e5Spatrick 
354f24071e5Spatrick 	cninit();
355f24071e5Spatrick 	efi_heap_init();
356f24071e5Spatrick 
357f24071e5Spatrick 	/*
3589a118d7eSpatrick 	 * The kernel expects to be loaded into a block of memory aligned
3599a118d7eSpatrick 	 * on a 2MB boundary.  We allocate a block of 64MB of memory, which
3609a118d7eSpatrick 	 * gives us plenty of room for growth.
361f24071e5Spatrick 	 */
3629a118d7eSpatrick 	if (efi_memprobe_find(EFI_SIZE_TO_PAGES(64 * 1024 * 1024),
3639a118d7eSpatrick 	    0x200000, &addr) != EFI_SUCCESS)
364f24071e5Spatrick 		printf("Can't allocate memory\n");
365f24071e5Spatrick 	efi_loadaddr = addr;
366f24071e5Spatrick 
367f24071e5Spatrick 	efi_timer_init();
368f24071e5Spatrick 	efi_diskprobe();
369f24071e5Spatrick }
370f24071e5Spatrick 
371f24071e5Spatrick void
372f24071e5Spatrick efi_cleanup(void)
373f24071e5Spatrick {
374fa854029Spatrick 	int		 retry;
375fa854029Spatrick 	EFI_STATUS	 status;
376fa854029Spatrick 
377f24071e5Spatrick 	efi_timer_cleanup();
378f24071e5Spatrick 
379fa854029Spatrick 	/* retry once in case of failure */
380fa854029Spatrick 	for (retry = 1; retry >= 0; retry--) {
381fa854029Spatrick 		efi_memprobe_internal();	/* sync the current map */
382f4dd1aebSkettenis 		efi_updatefdt();
383fa854029Spatrick 		status = EFI_CALL(BS->ExitBootServices, IH, mmap_key);
384fa854029Spatrick 		if (status == EFI_SUCCESS)
385fa854029Spatrick 			break;
386fa854029Spatrick 		if (retry == 0)
387fa854029Spatrick 			panic("ExitBootServices failed (%d)", status);
388fa854029Spatrick 	}
389f24071e5Spatrick }
390f24071e5Spatrick 
391f24071e5Spatrick void
392f24071e5Spatrick _rtt(void)
393f24071e5Spatrick {
394f24071e5Spatrick #ifdef EFI_DEBUG
395f24071e5Spatrick 	printf("Hit any key to reboot\n");
396f24071e5Spatrick 	efi_cons_getc(0);
397f24071e5Spatrick #endif
398f24071e5Spatrick 	RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
399*111d6387Skettenis 	for (;;)
400*111d6387Skettenis 		continue;
401f24071e5Spatrick }
402f24071e5Spatrick 
403f24071e5Spatrick /*
404f24071e5Spatrick  * U-Boot only implements the GetTime() Runtime Service if it has been
405f24071e5Spatrick  * configured with CONFIG_DM_RTC.  Most board configurations don't
406f24071e5Spatrick  * include that option, so we can't use it to implement our boot
407f24071e5Spatrick  * prompt timeout.  Instead we use timer events to simulate a clock
408f24071e5Spatrick  * that ticks ever second.
409f24071e5Spatrick  */
410f24071e5Spatrick 
411f24071e5Spatrick EFI_EVENT timer;
412f24071e5Spatrick int ticks;
413f24071e5Spatrick 
414f24071e5Spatrick static VOID
415f24071e5Spatrick efi_timer(EFI_EVENT event, VOID *context)
416f24071e5Spatrick {
417f24071e5Spatrick 	ticks++;
418f24071e5Spatrick }
419f24071e5Spatrick 
420f24071e5Spatrick static void
421f24071e5Spatrick efi_timer_init(void)
422f24071e5Spatrick {
423f24071e5Spatrick 	EFI_STATUS status;
424f24071e5Spatrick 
425a4d88df4Sjsg 	status = BS->CreateEvent(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
426f24071e5Spatrick 	    efi_timer, NULL, &timer);
427f24071e5Spatrick 	if (status == EFI_SUCCESS)
428f24071e5Spatrick 		status = BS->SetTimer(timer, TimerPeriodic, 10000000);
429f24071e5Spatrick 	if (EFI_ERROR(status))
430f24071e5Spatrick 		printf("Can't create timer\n");
431f24071e5Spatrick }
432f24071e5Spatrick 
433f24071e5Spatrick static void
434f24071e5Spatrick efi_timer_cleanup(void)
435f24071e5Spatrick {
436f24071e5Spatrick 	BS->CloseEvent(timer);
437f24071e5Spatrick }
438f24071e5Spatrick 
439f24071e5Spatrick time_t
440f24071e5Spatrick getsecs(void)
441f24071e5Spatrick {
442f24071e5Spatrick 	return ticks;
443f24071e5Spatrick }
444f24071e5Spatrick 
445f24071e5Spatrick /*
446f24071e5Spatrick  * Various device-related bits.
447f24071e5Spatrick  */
448f24071e5Spatrick 
449f24071e5Spatrick void
450f24071e5Spatrick devboot(dev_t dev, char *p)
451f24071e5Spatrick {
452f24071e5Spatrick 	strlcpy(p, "sd0a", 5);
453f24071e5Spatrick }
454f24071e5Spatrick 
455f24071e5Spatrick int
456f24071e5Spatrick cnspeed(dev_t dev, int sp)
457f24071e5Spatrick {
458f24071e5Spatrick 	return 115200;
459f24071e5Spatrick }
460f24071e5Spatrick 
461f24071e5Spatrick char *
462f24071e5Spatrick ttyname(int fd)
463f24071e5Spatrick {
464f24071e5Spatrick 	return "com0";
465f24071e5Spatrick }
466f24071e5Spatrick 
467f24071e5Spatrick dev_t
468f24071e5Spatrick ttydev(char *name)
469f24071e5Spatrick {
470f24071e5Spatrick 	return NODEV;
471f24071e5Spatrick }
472f24071e5Spatrick 
473f24071e5Spatrick #define MAXDEVNAME	16
474f24071e5Spatrick 
475f24071e5Spatrick /*
476f24071e5Spatrick  * Parse a device spec.
477f24071e5Spatrick  *
478f24071e5Spatrick  * [A-Za-z]*[0-9]*[A-Za-z]:file
479f24071e5Spatrick  *    dev   uint    part
480f24071e5Spatrick  */
481f24071e5Spatrick int
482f24071e5Spatrick devparse(const char *fname, int *dev, int *unit, int *part, const char **file)
483f24071e5Spatrick {
484f24071e5Spatrick 	const char *s;
485f24071e5Spatrick 
486f24071e5Spatrick 	*unit = 0;	/* default to wd0a */
487f24071e5Spatrick 	*part = 0;
488f24071e5Spatrick 	*dev  = 0;
489f24071e5Spatrick 
490f24071e5Spatrick 	s = strchr(fname, ':');
491f24071e5Spatrick 	if (s != NULL) {
492f24071e5Spatrick 		int devlen;
493f24071e5Spatrick 		int i, u, p = 0;
494f24071e5Spatrick 		struct devsw *dp;
495f24071e5Spatrick 		char devname[MAXDEVNAME];
496f24071e5Spatrick 
497f24071e5Spatrick 		devlen = s - fname;
498f24071e5Spatrick 		if (devlen > MAXDEVNAME)
499f24071e5Spatrick 			return (EINVAL);
500f24071e5Spatrick 
501f24071e5Spatrick 		/* extract device name */
502f24071e5Spatrick 		for (i = 0; isalpha(fname[i]) && (i < devlen); i++)
503f24071e5Spatrick 			devname[i] = fname[i];
504f24071e5Spatrick 		devname[i] = 0;
505f24071e5Spatrick 
506f24071e5Spatrick 		if (!isdigit(fname[i]))
507f24071e5Spatrick 			return (EUNIT);
508f24071e5Spatrick 
509f24071e5Spatrick 		/* device number */
510f24071e5Spatrick 		for (u = 0; isdigit(fname[i]) && (i < devlen); i++)
511f24071e5Spatrick 			u = u * 10 + (fname[i] - '0');
512f24071e5Spatrick 
513f24071e5Spatrick 		if (!isalpha(fname[i]))
514f24071e5Spatrick 			return (EPART);
515f24071e5Spatrick 
516f24071e5Spatrick 		/* partition number */
517f24071e5Spatrick 		if (i < devlen)
518f24071e5Spatrick 			p = fname[i++] - 'a';
519f24071e5Spatrick 
520f24071e5Spatrick 		if (i != devlen)
521f24071e5Spatrick 			return (ENXIO);
522f24071e5Spatrick 
523f24071e5Spatrick 		/* check device name */
524f24071e5Spatrick 		for (dp = devsw, i = 0; i < ndevs; dp++, i++) {
525f24071e5Spatrick 			if (dp->dv_name && !strcmp(devname, dp->dv_name))
526f24071e5Spatrick 				break;
527f24071e5Spatrick 		}
528f24071e5Spatrick 
529f24071e5Spatrick 		if (i >= ndevs)
530f24071e5Spatrick 			return (ENXIO);
531f24071e5Spatrick 
532f24071e5Spatrick 		*unit = u;
533f24071e5Spatrick 		*part = p;
534f24071e5Spatrick 		*dev  = i;
535f24071e5Spatrick 		fname = ++s;
536f24071e5Spatrick 	}
537f24071e5Spatrick 
538f24071e5Spatrick 	*file = fname;
539f24071e5Spatrick 
540f24071e5Spatrick 	return (0);
541f24071e5Spatrick }
542f24071e5Spatrick 
543f24071e5Spatrick int
544f24071e5Spatrick devopen(struct open_file *f, const char *fname, char **file)
545f24071e5Spatrick {
546f24071e5Spatrick 	struct devsw *dp;
547f24071e5Spatrick 	int dev, unit, part, error;
548f24071e5Spatrick 
549f24071e5Spatrick 	error = devparse(fname, &dev, &unit, &part, (const char **)file);
550f24071e5Spatrick 	if (error)
551f24071e5Spatrick 		return (error);
552f24071e5Spatrick 
553f24071e5Spatrick 	dp = &devsw[0];
554f24071e5Spatrick 	f->f_dev = dp;
555f24071e5Spatrick 
556f24071e5Spatrick 	return (*dp->dv_open)(f, unit, part);
557f24071e5Spatrick }
558f24071e5Spatrick 
559fa854029Spatrick static void
560fa854029Spatrick efi_memprobe_internal(void)
561fa854029Spatrick {
562fa854029Spatrick 	EFI_STATUS		 status;
563fa854029Spatrick 	UINTN			 mapkey, mmsiz, siz;
564fa854029Spatrick 	UINT32			 mmver;
565fa854029Spatrick 	EFI_MEMORY_DESCRIPTOR	*mm;
566fa854029Spatrick 	int			 n;
567fa854029Spatrick 
568fa854029Spatrick 	free(mmap, mmap_ndesc * mmap_descsiz);
569fa854029Spatrick 
570fa854029Spatrick 	siz = 0;
571fa854029Spatrick 	status = EFI_CALL(BS->GetMemoryMap, &siz, NULL, &mapkey, &mmsiz,
572fa854029Spatrick 	    &mmver);
573fa854029Spatrick 	if (status != EFI_BUFFER_TOO_SMALL)
574fa854029Spatrick 		panic("cannot get the size of memory map");
575fa854029Spatrick 	mm = alloc(siz);
576fa854029Spatrick 	status = EFI_CALL(BS->GetMemoryMap, &siz, mm, &mapkey, &mmsiz, &mmver);
577fa854029Spatrick 	if (status != EFI_SUCCESS)
578fa854029Spatrick 		panic("cannot get the memory map");
579fa854029Spatrick 	n = siz / mmsiz;
580fa854029Spatrick 	mmap = mm;
581fa854029Spatrick 	mmap_key = mapkey;
582fa854029Spatrick 	mmap_ndesc = n;
583fa854029Spatrick 	mmap_descsiz = mmsiz;
584f4dd1aebSkettenis 	mmap_version = mmver;
585fa854029Spatrick }
586fa854029Spatrick 
587f24071e5Spatrick /*
588f24071e5Spatrick  * 64-bit ARMs can have a much wider memory mapping, as in somewhere
589f24071e5Spatrick  * after the 32-bit region.  To cope with our alignment requirement,
590f24071e5Spatrick  * use the memory table to find a place where we can fit.
591f24071e5Spatrick  */
592f24071e5Spatrick static EFI_STATUS
593f24071e5Spatrick efi_memprobe_find(UINTN pages, UINTN align, EFI_PHYSICAL_ADDRESS *addr)
594f24071e5Spatrick {
595fa854029Spatrick 	EFI_MEMORY_DESCRIPTOR	*mm;
596fa854029Spatrick 	int			 i, j;
597f24071e5Spatrick 
598f24071e5Spatrick 	if (align < EFI_PAGE_SIZE)
599f24071e5Spatrick 		return EFI_INVALID_PARAMETER;
600f24071e5Spatrick 
601fa854029Spatrick 	efi_memprobe_internal();	/* sync the current map */
602f24071e5Spatrick 
603fa854029Spatrick 	for (i = 0, mm = mmap; i < mmap_ndesc;
604fa854029Spatrick 	    i++, mm = NextMemoryDescriptor(mm, mmap_descsiz)) {
605f24071e5Spatrick 		if (mm->Type != EfiConventionalMemory)
606f24071e5Spatrick 			continue;
607f24071e5Spatrick 
608f24071e5Spatrick 		if (mm->NumberOfPages < pages)
609f24071e5Spatrick 			continue;
610f24071e5Spatrick 
611f2c0e408Skettenis 		for (j = 0; j < mm->NumberOfPages; j++) {
612f24071e5Spatrick 			EFI_PHYSICAL_ADDRESS paddr;
613f24071e5Spatrick 
614f24071e5Spatrick 			if (mm->NumberOfPages - j < pages)
615f24071e5Spatrick 				break;
616f24071e5Spatrick 
617f24071e5Spatrick 			paddr = mm->PhysicalStart + (j * EFI_PAGE_SIZE);
61873dc1409Spatrick 			if (paddr & (align - 1))
61973dc1409Spatrick 				continue;
62073dc1409Spatrick 
62173dc1409Spatrick 			if (EFI_CALL(BS->AllocatePages, AllocateAddress,
62273dc1409Spatrick 			    EfiLoaderData, pages, &paddr) == EFI_SUCCESS) {
623f24071e5Spatrick 				*addr = paddr;
624f24071e5Spatrick 				return EFI_SUCCESS;
625f24071e5Spatrick 			}
626f24071e5Spatrick 		}
627f24071e5Spatrick 	}
628f24071e5Spatrick 	return EFI_OUT_OF_RESOURCES;
629f24071e5Spatrick }
630*111d6387Skettenis 
631*111d6387Skettenis /*
632*111d6387Skettenis  * Commands
633*111d6387Skettenis  */
634*111d6387Skettenis 
635*111d6387Skettenis int Xexit_efi(void);
636*111d6387Skettenis int Xpoweroff_efi(void);
637*111d6387Skettenis 
638*111d6387Skettenis const struct cmd_table cmd_machine[] = {
639*111d6387Skettenis 	{ "exit",	CMDT_CMD, Xexit_efi },
640*111d6387Skettenis 	{ "poweroff",	CMDT_CMD, Xpoweroff_efi },
641*111d6387Skettenis 	{ NULL, 0 }
642*111d6387Skettenis };
643*111d6387Skettenis 
644*111d6387Skettenis int
645*111d6387Skettenis Xexit_efi(void)
646*111d6387Skettenis {
647*111d6387Skettenis 	EFI_CALL(BS->Exit, IH, 0, 0, NULL);
648*111d6387Skettenis 	for (;;)
649*111d6387Skettenis 		continue;
650*111d6387Skettenis 	return (0);
651*111d6387Skettenis }
652*111d6387Skettenis 
653*111d6387Skettenis int
654*111d6387Skettenis Xpoweroff_efi(void)
655*111d6387Skettenis {
656*111d6387Skettenis 	EFI_CALL(RS->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
657*111d6387Skettenis 	return (0);
658*111d6387Skettenis }
659