xref: /openbsd/sys/arch/armv7/stand/efiboot/efiboot.c (revision 9b7c3dbb)
1 /*	$OpenBSD: efiboot.c,v 1.11 2016/07/01 09:34:39 patrick 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 <dev/cons.h>
23 #include <sys/disklabel.h>
24 
25 #include <efi.h>
26 #include <efiapi.h>
27 #include <efiprot.h>
28 #include <eficonsctl.h>
29 
30 #include <lib/libkern/libkern.h>
31 #include <stand/boot/cmd.h>
32 
33 #include "disk.h"
34 #include "eficall.h"
35 #include "fdt.h"
36 #include "libsa.h"
37 
38 EFI_SYSTEM_TABLE	*ST;
39 EFI_BOOT_SERVICES	*BS;
40 EFI_RUNTIME_SERVICES	*RS;
41 EFI_HANDLE		 IH;
42 
43 EFI_HANDLE		 efi_bootdp;
44 
45 static EFI_GUID		 imgp_guid = LOADED_IMAGE_PROTOCOL;
46 static EFI_GUID		 blkio_guid = BLOCK_IO_PROTOCOL;
47 static EFI_GUID		 devp_guid = DEVICE_PATH_PROTOCOL;
48 
49 static void efi_timer_init(void);
50 static void efi_timer_cleanup(void);
51 
52 EFI_STATUS
53 efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
54 {
55 	extern char		*progname;
56 	EFI_LOADED_IMAGE	*imgp;
57 	EFI_DEVICE_PATH		*dp = NULL;
58 	EFI_STATUS		 status;
59 
60 	ST = systab;
61 	BS = ST->BootServices;
62 	IH = image;
63 
64 	status = EFI_CALL(BS->HandleProtocol, image, &imgp_guid,
65 	    (void **)&imgp);
66 	if (status == EFI_SUCCESS)
67 		status = EFI_CALL(BS->HandleProtocol, imgp->DeviceHandle,
68 		    &devp_guid, (void **)&dp);
69 	if (status == EFI_SUCCESS)
70 		efi_bootdp = dp;
71 
72 	progname = "BOOTARM";
73 
74 	boot(0);
75 
76 	return (EFI_SUCCESS);
77 }
78 
79 static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
80 static SIMPLE_INPUT_INTERFACE *conin;
81 
82 void
83 efi_cons_probe(struct consdev *cn)
84 {
85 	cn->cn_pri = CN_MIDPRI;
86 	cn->cn_dev = makedev(12, 0);
87 }
88 
89 void
90 efi_cons_init(struct consdev *cp)
91 {
92 	conin = ST->ConIn;
93 	conout = ST->ConOut;
94 }
95 
96 int
97 efi_cons_getc(dev_t dev)
98 {
99 	EFI_INPUT_KEY	 key;
100 	EFI_STATUS	 status;
101 #if 0
102 	UINTN		 dummy;
103 #endif
104 	static int	 lastchar = 0;
105 
106 	if (lastchar) {
107 		int r = lastchar;
108 		if ((dev & 0x80) == 0)
109 			lastchar = 0;
110 		return (r);
111 	}
112 
113 	status = conin->ReadKeyStroke(conin, &key);
114 	while (status == EFI_NOT_READY) {
115 		if (dev & 0x80)
116 			return (0);
117 		/*
118 		 * XXX The implementation of WaitForEvent() in U-boot
119 		 * is broken and neverreturns.
120 		 */
121 #if 0
122 		BS->WaitForEvent(1, &conin->WaitForKey, &dummy);
123 #endif
124 		status = conin->ReadKeyStroke(conin, &key);
125 	}
126 
127 	if (dev & 0x80)
128 		lastchar = key.UnicodeChar;
129 
130 	return (key.UnicodeChar);
131 }
132 
133 void
134 efi_cons_putc(dev_t dev, int c)
135 {
136 	CHAR16	buf[2];
137 
138 	if (c == '\n')
139 		efi_cons_putc(dev, '\r');
140 
141 	buf[0] = c;
142 	buf[1] = 0;
143 
144 	conout->OutputString(conout, buf);
145 }
146 
147 EFI_PHYSICAL_ADDRESS	 heap;
148 UINTN			 heapsiz = 1 * 1024 * 1024;
149 
150 static void
151 efi_heap_init(void)
152 {
153 	EFI_STATUS	 status;
154 
155 	status = EFI_CALL(BS->AllocatePages, AllocateAnyPages, EfiLoaderData,
156 	    EFI_SIZE_TO_PAGES(heapsiz), &heap);
157 	if (status != EFI_SUCCESS)
158 		panic("BS->AllocatePages()");
159 }
160 
161 EFI_BLOCK_IO	*disk;
162 
163 void
164 efi_diskprobe(void)
165 {
166 	int			 i, bootdev;
167 	UINTN			 sz;
168 	EFI_STATUS		 status;
169 	EFI_HANDLE		*handles = NULL;
170 	EFI_BLOCK_IO		*blkio;
171 	EFI_BLOCK_IO_MEDIA	*media;
172 	EFI_DEVICE_PATH		*dp, *bp;
173 
174 	sz = 0;
175 	status = EFI_CALL(BS->LocateHandle, ByProtocol, &blkio_guid, 0, &sz, 0);
176 	if (status == EFI_BUFFER_TOO_SMALL) {
177 		handles = alloc(sz);
178 		status = EFI_CALL(BS->LocateHandle, ByProtocol, &blkio_guid,
179 		    0, &sz, handles);
180 	}
181 	if (handles == NULL || EFI_ERROR(status))
182 		panic("BS->LocateHandle() returns %d", status);
183 
184 	for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) {
185 		bootdev = 0;
186 		status = EFI_CALL(BS->HandleProtocol, handles[i], &blkio_guid,
187 		    (void **)&blkio);
188 		if (EFI_ERROR(status))
189 			panic("BS->HandleProtocol() returns %d", status);
190 
191 		media = blkio->Media;
192 		if (media->LogicalPartition || !media->MediaPresent)
193 			continue;
194 
195 		if (efi_bootdp == NULL)
196 			goto next;
197 		status = EFI_CALL(BS->HandleProtocol, handles[i], &devp_guid,
198 		    (void **)&dp);
199 		if (EFI_ERROR(status))
200 			goto next;
201 		bp = efi_bootdp;
202 		while (1) {
203 			if (IsDevicePathEnd(dp)) {
204 				bootdev = 1;
205 				break;
206 			}
207 			if (memcmp(dp, bp, sizeof(EFI_DEVICE_PATH)) != 0 ||
208 			    memcmp(dp, bp, DevicePathNodeLength(dp)) != 0)
209 				break;
210 			dp = NextDevicePathNode(dp);
211 			bp = NextDevicePathNode(bp);
212 		}
213 next:
214 		if (bootdev) {
215 			disk = blkio;
216 			break;
217 		}
218 	}
219 
220 	free(handles, sz);
221 }
222 
223 struct board_id {
224 	const char *name;
225 	uint32_t board_id;
226 };
227 
228 struct board_id board_id_table[] = {
229 	{ "allwinner,sun4i-a10",		4104 },
230 	{ "allwinner,sun7i-a20",		4283 },
231 	{ "arm,vexpress",			2272 },
232 	{ "boundary,imx6q-nitrogen6_max",	3769 },
233 	{ "boundary,imx6q-nitrogen6x",		3769 },
234 	{ "compulab,cm-fx6",			4273 },
235 	{ "fsl,imx6q-sabrelite",		3769 },
236 	{ "fsl,imx6q-sabresd",			3980 },
237 	{ "google,snow",			3774 },
238 	{ "google,spring",			3774 },
239 	{ "kosagi,imx6q-novena",		4269 },
240 	{ "samsung,universal_c210",		2838 },
241 	{ "solidrun,cubox-i/dl",		4821 },
242 	{ "solidrun,cubox-i/q",			4821 },
243 	{ "solidrun,hummingboard/dl",		4773 },
244 	{ "solidrun,hummingboard/q",		4773 },
245 	{ "ti,am335x-bone",			3589 },
246 	{ "ti,omap3-beagle",			1546 },
247 	{ "ti,omap3-beagle-xm",			1546 },
248 	{ "ti,omap4-panda",			2791 },
249 	{ "udoo,imx6q-udoo",			4800 },
250 	{ "wand,imx6q-wandboard",		4412 },
251 };
252 
253 static EFI_GUID fdt_guid = FDT_TABLE_GUID;
254 
255 #define	efi_guidcmp(_a, _b)	memcmp((_a), (_b), sizeof(EFI_GUID))
256 
257 void *
258 efi_makebootargs(char *bootargs, uint32_t *board_id)
259 {
260 	void *fdt = NULL;
261 	u_char bootduid[8];
262 	u_char zero[8];
263 	void *node;
264 	size_t len;
265 	int i;
266 
267 	for (i = 0; i < ST->NumberOfTableEntries; i++) {
268 		if (efi_guidcmp(&fdt_guid,
269 		    &ST->ConfigurationTable[i].VendorGuid) == 0)
270 			fdt = ST->ConfigurationTable[i].VendorTable;
271 	}
272 
273 	if (!fdt_init(fdt))
274 		return NULL;
275 
276 	node = fdt_find_node("/chosen");
277 	if (!node)
278 		return NULL;
279 
280 	len = strlen(bootargs) + 1;
281 	fdt_node_add_property(node, "bootargs", bootargs, len);
282 
283 	/* Pass DUID of the boot disk. */
284 	memset(&zero, 0, sizeof(zero));
285 	memcpy(&bootduid, diskinfo.disklabel.d_uid, sizeof(bootduid));
286 	if (memcmp(bootduid, zero, sizeof(bootduid)) != 0) {
287 		fdt_node_add_property(node, "openbsd,bootduid", bootduid,
288 		    sizeof(bootduid));
289 	}
290 
291 	fdt_finalize();
292 
293 	node = fdt_find_node("/");
294 	for (i = 0; i < nitems(board_id_table); i++) {
295 		if (fdt_node_is_compatible(node, board_id_table[i].name)) {
296 			*board_id = board_id_table[i].board_id;
297 			break;
298 		}
299 	}
300 
301 	return fdt;
302 }
303 
304 u_long efi_loadaddr;
305 
306 void
307 machdep(void)
308 {
309 	EFI_PHYSICAL_ADDRESS addr;
310 	EFI_STATUS status;
311 
312 	cninit();
313 
314 	/*
315 	 * The kernel expects to be loaded at offset 0x00300000 into a
316 	 * block of memory aligned on a 256MB boundary.  We allocate a
317 	 * block of 32MB of memory, which gives us plenty of room for
318 	 * growth.
319 	 */
320 	for (addr = 0x10000000; addr <= 0xf0000000; addr += 0x10000000) {
321 		status = BS->AllocatePages(AllocateAddress, EfiLoaderData,
322 		    EFI_SIZE_TO_PAGES(32 * 1024 * 1024), &addr);
323 		if (status == EFI_SUCCESS) {
324 			efi_loadaddr = addr;
325 			break;
326 		}
327 	}
328 	if (efi_loadaddr == 0)
329 		printf("Can't allocate memory\n");
330 
331 	efi_heap_init();
332 	efi_timer_init();
333 	efi_diskprobe();
334 }
335 
336 void
337 efi_cleanup(void)
338 {
339 	efi_timer_cleanup();
340 
341 	BS->ExitBootServices(NULL, 0);
342 }
343 
344 void
345 _rtt(void)
346 {
347 #ifdef EFI_DEBUG
348 	printf("Hit any key to reboot\n");
349 	efi_cons_getc(0);
350 #endif
351 	/*
352 	 * XXX ResetSystem doesn't seem to work on U-Boot 2016.05 on
353 	 * the CuBox-i.  So trigger an unimplemented instruction trap
354 	 * instead.
355 	 */
356 #if 1
357 	asm volatile(".word 0xa000f7f0\n");
358 #else
359 	RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
360 #endif
361 	while (1) { }
362 }
363 
364 /*
365  * U-Boot only implements the GetTime() Runtime Service if it has been
366  * configured with CONFIG_DM_RTC.  Most board configurations don't
367  * include that option, so we can't use it to implement our boot
368  * prompt timeout.  Instead we use timer events to simulate a clock
369  * that ticks ever second.
370  */
371 
372 EFI_EVENT timer;
373 int ticks;
374 
375 static VOID
376 efi_timer(EFI_EVENT event, VOID *context)
377 {
378 	ticks++;
379 }
380 
381 static void
382 efi_timer_init(void)
383 {
384 	EFI_STATUS status;
385 
386 	status = BS->CreateEvent(EVT_TIMER, TPL_CALLBACK,
387 	    efi_timer, NULL, &timer);
388 	if (status == EFI_SUCCESS)
389 		status = BS->SetTimer(timer, TimerPeriodic, 10000000);
390 	if (EFI_ERROR(status))
391 		printf("Can't create timer\n");
392 }
393 
394 static void
395 efi_timer_cleanup(void)
396 {
397 	BS->CloseEvent(timer);
398 }
399 
400 time_t
401 getsecs(void)
402 {
403 	return ticks;
404 }
405 
406 /*
407  * Various device-related bits.
408  */
409 
410 void
411 devboot(dev_t dev, char *p)
412 {
413 	strlcpy(p, "sd0a", 5);
414 }
415 
416 int
417 cnspeed(dev_t dev, int sp)
418 {
419 	return 115200;
420 }
421 
422 char *
423 ttyname(int fd)
424 {
425 	return "com0";
426 }
427 
428 dev_t
429 ttydev(char *name)
430 {
431 	return NODEV;
432 }
433 
434 #define MAXDEVNAME	16
435 
436 /*
437  * Parse a device spec.
438  *
439  * [A-Za-z]*[0-9]*[A-Za-z]:file
440  *    dev   uint    part
441  */
442 int
443 devparse(const char *fname, int *dev, int *unit, int *part, const char **file)
444 {
445 	const char *s;
446 
447 	*unit = 0;	/* default to wd0a */
448 	*part = 0;
449 	*dev  = 0;
450 
451 	s = strchr(fname, ':');
452 	if (s != NULL) {
453 		int devlen;
454 		int i, u, p = 0;
455 		struct devsw *dp;
456 		char devname[MAXDEVNAME];
457 
458 		devlen = s - fname;
459 		if (devlen > MAXDEVNAME)
460 			return (EINVAL);
461 
462 		/* extract device name */
463 		for (i = 0; isalpha(fname[i]) && (i < devlen); i++)
464 			devname[i] = fname[i];
465 		devname[i] = 0;
466 
467 		if (!isdigit(fname[i]))
468 			return (EUNIT);
469 
470 		/* device number */
471 		for (u = 0; isdigit(fname[i]) && (i < devlen); i++)
472 			u = u * 10 + (fname[i] - '0');
473 
474 		if (!isalpha(fname[i]))
475 			return (EPART);
476 
477 		/* partition number */
478 		if (i < devlen)
479 			p = fname[i++] - 'a';
480 
481 		if (i != devlen)
482 			return (ENXIO);
483 
484 		/* check device name */
485 		for (dp = devsw, i = 0; i < ndevs; dp++, i++) {
486 			if (dp->dv_name && !strcmp(devname, dp->dv_name))
487 				break;
488 		}
489 
490 		if (i >= ndevs)
491 			return (ENXIO);
492 
493 		*unit = u;
494 		*part = p;
495 		*dev  = i;
496 		fname = ++s;
497 	}
498 
499 	*file = fname;
500 
501 	return (0);
502 }
503 
504 int
505 devopen(struct open_file *f, const char *fname, char **file)
506 {
507 	struct devsw *dp;
508 	int dev, unit, part, error;
509 
510 	error = devparse(fname, &dev, &unit, &part, (const char **)file);
511 	if (error)
512 		return (error);
513 
514 	dp = &devsw[0];
515 	f->f_dev = dp;
516 
517 	return (*dp->dv_open)(f, unit, part);
518 }
519