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