xref: /openbsd/sys/arch/amd64/stand/efiboot/efiboot.c (revision 771fbea0)
1 /*	$OpenBSD: efiboot.c,v 1.38 2021/06/07 00:04:20 krw Exp $	*/
2 
3 /*
4  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/queue.h>
21 #include <dev/cons.h>
22 #include <dev/isa/isareg.h>
23 #include <dev/ic/comreg.h>
24 #include <sys/disklabel.h>
25 #include <cmd.h>
26 #include <stand/boot/bootarg.h>
27 #include <machine/pio.h>
28 
29 #include "libsa.h"
30 #include "disk.h"
31 
32 #include <efi.h>
33 #include <efiapi.h>
34 #include <efiprot.h>
35 #include <eficonsctl.h>
36 
37 #include "efidev.h"
38 #include "efiboot.h"
39 #include "run_i386.h"
40 
41 #define	KERN_LOADSPACE_SIZE	(64 * 1024 * 1024)
42 
43 EFI_SYSTEM_TABLE	*ST;
44 EFI_BOOT_SERVICES	*BS;
45 EFI_RUNTIME_SERVICES	*RS;
46 EFI_HANDLE		 IH;
47 EFI_DEVICE_PATH		*efi_bootdp = NULL;
48 EFI_PHYSICAL_ADDRESS	 heap;
49 EFI_LOADED_IMAGE	*loadedImage;
50 UINTN			 heapsiz = 1 * 1024 * 1024;
51 UINTN			 mmap_key;
52 static EFI_GUID		 imgp_guid = LOADED_IMAGE_PROTOCOL;
53 static EFI_GUID		 blkio_guid = BLOCK_IO_PROTOCOL;
54 static EFI_GUID		 devp_guid = DEVICE_PATH_PROTOCOL;
55 u_long			 efi_loadaddr;
56 
57 int	 efi_device_path_depth(EFI_DEVICE_PATH *dp, int);
58 int	 efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int);
59 static void	 efi_heap_init(void);
60 static int	 efi_memprobe_internal(void);
61 static void	 efi_video_init(void);
62 static void	 efi_video_reset(void);
63 static EFI_STATUS
64 		 efi_gop_setmode(int mode);
65 EFI_STATUS	 efi_main(EFI_HANDLE, EFI_SYSTEM_TABLE *);
66 
67 void (*run_i386)(u_long, u_long, int, int, int, int, int, int, int, int)
68     __attribute__((noreturn));
69 
70 extern int bios_bootdev;
71 
72 EFI_STATUS
73 efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
74 {
75 	extern char		*progname;
76 	EFI_LOADED_IMAGE	*imgp;
77 	EFI_DEVICE_PATH		*dp0 = NULL, *dp;
78 	EFI_STATUS		 status;
79 	EFI_PHYSICAL_ADDRESS	 stack;
80 
81 	ST = systab;
82 	BS = ST->BootServices;
83 	RS = ST->RuntimeServices;
84 	IH = image;
85 
86 	/* disable reset by watchdog after 5 minutes */
87 	BS->SetWatchdogTimer(0, 0, 0, NULL);
88 
89 	efi_video_init();
90 	efi_heap_init();
91 
92 	status = BS->HandleProtocol(image, &imgp_guid, (void **)&imgp);
93 	if (status == EFI_SUCCESS)
94 		status = BS->HandleProtocol(imgp->DeviceHandle, &devp_guid,
95 		    (void **)&dp0);
96 	if (status == EFI_SUCCESS) {
97 		for (dp = dp0; !IsDevicePathEnd(dp);
98 		    dp = NextDevicePathNode(dp)) {
99 			if (DevicePathType(dp) == MEDIA_DEVICE_PATH &&
100 			    (DevicePathSubType(dp) == MEDIA_HARDDRIVE_DP ||
101  			    DevicePathSubType(dp) == MEDIA_CDROM_DP)) {
102 				bios_bootdev =
103 				    (DevicePathSubType(dp) == MEDIA_CDROM_DP)
104 				    ? 0x1e0 : 0x80;
105 				efi_bootdp = dp0;
106 				break;
107 			} else if (DevicePathType(dp) == MESSAGING_DEVICE_PATH&&
108 			    DevicePathSubType(dp) == MSG_MAC_ADDR_DP) {
109 				bios_bootdev = 0x0;
110 				efi_bootdp = dp0;
111 				break;
112 			}
113 		}
114 	}
115 
116 #ifdef __amd64__
117 	/* allocate run_i386_start() on heap */
118 	if ((run_i386 = alloc(run_i386_size)) == NULL)
119 		panic("alloc() failed");
120 	memcpy(run_i386, run_i386_start, run_i386_size);
121 #endif
122 
123 	/* can't use sa_cleanup since printf is used after sa_cleanup() */
124 	/* sa_cleanup = efi_cleanup; */
125 
126 #ifdef __amd64__
127 	progname = "BOOTX64";
128 #else
129 	progname = "BOOTIA32";
130 #endif
131 
132 	/*
133 	 * Move the stack before calling boot().  UEFI on some machines
134 	 * locate the stack on our kernel load address.
135 	 */
136 	stack = heap + heapsiz;
137 #if defined(__amd64__)
138 	asm("movq	%0, %%rsp;"
139 	    "mov	%1, %%edi;"
140 	    "call	boot;"
141 	    :: "r"(stack - 32), "r"(bios_bootdev));
142 #else
143 	asm("movl	%0, %%esp;"
144 	    "movl	%1, (%%esp);"
145 	    "call	boot;"
146 	    :: "r"(stack - 32), "r"(bios_bootdev));
147 #endif
148 	/* must not reach here */
149 	return (EFI_SUCCESS);
150 }
151 
152 void
153 efi_cleanup(void)
154 {
155 	int		 retry;
156 	EFI_STATUS	 status;
157 
158 	/* retry once in case of failure */
159 	for (retry = 1; retry >= 0; retry--) {
160 		efi_memprobe_internal();	/* sync the current map */
161 		status = BS->ExitBootServices(IH, mmap_key);
162 		if (status == EFI_SUCCESS)
163 			break;
164 		if (retry == 0)
165 			panic("ExitBootServices failed (%d)", status);
166 	}
167 }
168 
169 /***********************************************************************
170  * Disk
171  ***********************************************************************/
172 struct disklist_lh efi_disklist;
173 
174 void
175 efi_diskprobe(void)
176 {
177 	int			 i, bootdev = 0, depth = -1;
178 	UINTN			 sz;
179 	EFI_STATUS		 status;
180 	EFI_HANDLE		*handles = NULL;
181 	EFI_BLOCK_IO		*blkio;
182 	EFI_BLOCK_IO_MEDIA	*media;
183 	struct diskinfo		*di;
184 	EFI_DEVICE_PATH		*dp;
185 
186 	TAILQ_INIT(&efi_disklist);
187 
188 	sz = 0;
189 	status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, 0);
190 	if (status == EFI_BUFFER_TOO_SMALL) {
191 		handles = alloc(sz);
192 		status = BS->LocateHandle(ByProtocol, &blkio_guid,
193 		    0, &sz, handles);
194 	}
195 	if (handles == NULL || EFI_ERROR(status))
196 		panic("BS->LocateHandle() returns %d", status);
197 
198 	if (efi_bootdp != NULL)
199 		depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
200 
201 	/*
202 	 * U-Boot incorrectly represents devices with a single
203 	 * MEDIA_DEVICE_PATH component.  In that case include that
204 	 * component into the matching, otherwise we'll blindly select
205 	 * the first device.
206 	 */
207 	if (depth == 0)
208 		depth = 1;
209 
210 	for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) {
211 		status = BS->HandleProtocol(handles[i], &blkio_guid,
212 		    (void **)&blkio);
213 		if (EFI_ERROR(status))
214 			panic("BS->HandleProtocol() returns %d", status);
215 
216 		media = blkio->Media;
217 		if (media->LogicalPartition || !media->MediaPresent)
218 			continue;
219 		di = alloc(sizeof(struct diskinfo));
220 		efid_init(di, blkio);
221 
222 		if (efi_bootdp == NULL || depth == -1 || bootdev != 0)
223 			goto next;
224 		status = BS->HandleProtocol(handles[i], &devp_guid,
225 		    (void **)&dp);
226 		if (EFI_ERROR(status))
227 			goto next;
228 		if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) {
229 			TAILQ_INSERT_HEAD(&efi_disklist, di, list);
230 			bootdev = 1;
231 			continue;
232 		}
233 next:
234 		TAILQ_INSERT_TAIL(&efi_disklist, di, list);
235 	}
236 
237 	free(handles, sz);
238 }
239 
240 /*
241  * Determine the number of nodes up to, but not including, the first
242  * node of the specified type.
243  */
244 int
245 efi_device_path_depth(EFI_DEVICE_PATH *dp, int dptype)
246 {
247 	int	i;
248 
249 	for (i = 0; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp), i++) {
250 		if (DevicePathType(dp) == dptype)
251 			return (i);
252 	}
253 
254 	return (i);
255 }
256 
257 int
258 efi_device_path_ncmp(EFI_DEVICE_PATH *dpa, EFI_DEVICE_PATH *dpb, int deptn)
259 {
260 	int	 i, cmp;
261 
262 	for (i = 0; i < deptn; i++) {
263 		if (IsDevicePathEnd(dpa) || IsDevicePathEnd(dpb))
264 			return ((IsDevicePathEnd(dpa) && IsDevicePathEnd(dpb))
265 			    ? 0 : (IsDevicePathEnd(dpa))? -1 : 1);
266 		cmp = DevicePathNodeLength(dpa) - DevicePathNodeLength(dpb);
267 		if (cmp)
268 			return (cmp);
269 		cmp = memcmp(dpa, dpb, DevicePathNodeLength(dpa));
270 		if (cmp)
271 			return (cmp);
272 		dpa = NextDevicePathNode(dpa);
273 		dpb = NextDevicePathNode(dpb);
274 	}
275 
276 	return (0);
277 }
278 
279 /***********************************************************************
280  * Memory
281  ***********************************************************************/
282 bios_memmap_t		 bios_memmap[128];
283 bios_efiinfo_t		 bios_efiinfo;
284 
285 static void
286 efi_heap_init(void)
287 {
288 	EFI_STATUS	 status;
289 
290 	heap = HEAP_LIMIT;
291 	status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData,
292 	    EFI_SIZE_TO_PAGES(heapsiz), &heap);
293 	if (status != EFI_SUCCESS)
294 		panic("BS->AllocatePages()");
295 }
296 
297 void
298 efi_memprobe(void)
299 {
300 	u_int		 n = 0;
301 	bios_memmap_t	*bm;
302 	EFI_STATUS	 status;
303 	EFI_PHYSICAL_ADDRESS
304 			 addr = 0x10000000ULL;	/* Below 256MB */
305 	int		 error;
306 
307 	status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData,
308 	    EFI_SIZE_TO_PAGES(KERN_LOADSPACE_SIZE), &addr);
309 	if (status != EFI_SUCCESS)
310 		panic("BS->AllocatePages()");
311 	efi_loadaddr = addr;
312 
313 	printf(" mem[");
314 	error = efi_memprobe_internal();
315 	for (bm = bios_memmap; bm->type != BIOS_MAP_END; bm++) {
316 		if (bm->type == BIOS_MAP_FREE && bm->size > 12 * 1024) {
317 			if (n++ != 0)
318 				printf(" ");
319 			if (bm->size > 1024 * 1024)
320 				printf("%uM", bm->size / 1024 / 1024);
321 			else
322 				printf("%uK", bm->size / 1024);
323 		}
324 	}
325 	if (error == E2BIG)
326 		printf(" overflow");
327 	printf("]");
328 }
329 
330 static int
331 efi_memprobe_internal(void)
332 {
333 	EFI_STATUS		 status;
334 	UINTN			 mapkey, mmsiz, siz;
335 	UINT32			 mmver;
336 	EFI_MEMORY_DESCRIPTOR	*mm0, *mm;
337 	int			 i, n;
338 	bios_memmap_t		*bm, bm0;
339 	int			 error = 0;
340 
341 	cnvmem = extmem = 0;
342 	bios_memmap[0].type = BIOS_MAP_END;
343 
344 	if (bios_efiinfo.mmap_start != 0)
345 		free((void *)bios_efiinfo.mmap_start, bios_efiinfo.mmap_size);
346 
347 	siz = 0;
348 	status = BS->GetMemoryMap(&siz, NULL, &mapkey, &mmsiz, &mmver);
349 	if (status != EFI_BUFFER_TOO_SMALL)
350 		panic("cannot get the size of memory map");
351 	mm0 = alloc(siz);
352 	status = BS->GetMemoryMap(&siz, mm0, &mapkey, &mmsiz, &mmver);
353 	if (status != EFI_SUCCESS)
354 		panic("cannot get the memory map");
355 	n = siz / mmsiz;
356 	mmap_key = mapkey;
357 
358 	for (i = 0, mm = mm0; i < n; i++, mm = NextMemoryDescriptor(mm, mmsiz)){
359 		bm0.type = BIOS_MAP_END;
360 		bm0.addr = mm->PhysicalStart;
361 		bm0.size = mm->NumberOfPages * EFI_PAGE_SIZE;
362 		if (mm->Type == EfiReservedMemoryType ||
363 		    mm->Type == EfiUnusableMemory ||
364 		    mm->Type == EfiRuntimeServicesCode ||
365 		    mm->Type == EfiRuntimeServicesData)
366 			bm0.type = BIOS_MAP_RES;
367 		else if (mm->Type == EfiLoaderCode ||
368 		    mm->Type == EfiLoaderData ||
369 		    mm->Type == EfiBootServicesCode ||
370 		    mm->Type == EfiBootServicesData ||
371 		    mm->Type == EfiConventionalMemory)
372 			bm0.type = BIOS_MAP_FREE;
373 		else if (mm->Type == EfiACPIReclaimMemory)
374 			bm0.type = BIOS_MAP_ACPI;
375 		else if (mm->Type == EfiACPIMemoryNVS)
376 			bm0.type = BIOS_MAP_NVS;
377 		else
378 			/*
379 			 * XXX Is there anything to do for EfiMemoryMappedIO
380 			 * XXX EfiMemoryMappedIOPortSpace EfiPalCode?
381 			 */
382 			bm0.type = BIOS_MAP_RES;
383 
384 		for (bm = bios_memmap; bm->type != BIOS_MAP_END; bm++) {
385 			if (bm->type != bm0.type)
386 				continue;
387 			if (bm->addr <= bm0.addr &&
388 			    bm0.addr <= bm->addr + bm->size) {
389 				bm->size = bm0.addr + bm0.size - bm->addr;
390 				break;
391 			} else if (bm0.addr <= bm->addr &&
392 			    bm->addr <= bm0.addr + bm0.size) {
393 				bm->size = bm->addr + bm->size - bm0.addr;
394 				bm->addr = bm0.addr;
395 				break;
396 			}
397 		}
398 		if (bm->type == BIOS_MAP_END) {
399 			if (bm == &bios_memmap[nitems(bios_memmap) - 1]) {
400 				error = E2BIG;
401 				break;
402 			}
403 			*bm = bm0;
404 			(++bm)->type = BIOS_MAP_END;
405 		}
406 	}
407 	for (bm = bios_memmap; bm->type != BIOS_MAP_END; bm++) {
408 		if (bm->addr < IOM_BEGIN)	/* Below memory hole */
409 			cnvmem =
410 			    max(cnvmem, (bm->addr + bm->size) / 1024);
411 		if (bm->addr >= IOM_END /* Above the memory hole */ &&
412 		    bm->addr / 1024 == extmem + 1024)
413 			extmem += bm->size / 1024;
414 	}
415 
416 	bios_efiinfo.mmap_desc_ver = mmver;
417 	bios_efiinfo.mmap_desc_size = mmsiz;
418 	bios_efiinfo.mmap_size = siz;
419 	bios_efiinfo.mmap_start = (uintptr_t)mm0;
420 
421 	return error;
422 }
423 
424 /***********************************************************************
425  * Console
426  ***********************************************************************/
427 static SIMPLE_TEXT_OUTPUT_INTERFACE     *conout = NULL;
428 static SIMPLE_INPUT_INTERFACE           *conin;
429 static EFI_GUID				 con_guid
430 					    = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
431 static EFI_GUID				 gop_guid
432 					    = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
433 static EFI_GUID				 serio_guid
434 					    = SERIAL_IO_PROTOCOL;
435 struct efi_video {
436 	int	cols;
437 	int	rows;
438 } efi_video[32];
439 
440 static void
441 efi_video_init(void)
442 {
443 	EFI_CONSOLE_CONTROL_PROTOCOL	*conctrl = NULL;
444 	int				 i, mode80x25, mode100x31;
445 	UINTN				 cols, rows;
446 	EFI_STATUS			 status;
447 
448 	conout = ST->ConOut;
449 	status = BS->LocateProtocol(&con_guid, NULL, (void **)&conctrl);
450 	if (status == EFI_SUCCESS)
451 		conctrl->SetMode(conctrl, EfiConsoleControlScreenText);
452 	mode80x25 = -1;
453 	mode100x31 = -1;
454 	for (i = 0; i < conout->Mode->MaxMode; i++) {
455 		status = conout->QueryMode(conout, i, &cols, &rows);
456 		if (EFI_ERROR(status))
457 			continue;
458 		if (mode80x25 < 0 && cols == 80 && rows == 25)
459 			mode80x25 = i;
460 		if (mode100x31 < 0 && cols == 100 && rows == 31)
461 			mode100x31 = i;
462 		if (i < nitems(efi_video)) {
463 			efi_video[i].cols = cols;
464 			efi_video[i].rows = rows;
465 		}
466 	}
467 	if (mode100x31 >= 0)
468 		conout->SetMode(conout, mode100x31);
469 	else if (mode80x25 >= 0)
470 		conout->SetMode(conout, mode80x25);
471 	conin = ST->ConIn;
472 	efi_video_reset();
473 }
474 
475 static void
476 efi_video_reset(void)
477 {
478 	conout->EnableCursor(conout, TRUE);
479 	conout->SetAttribute(conout, EFI_TEXT_ATTR(EFI_LIGHTGRAY, EFI_BLACK));
480 	conout->ClearScreen(conout);
481 }
482 
483 void
484 efi_cons_probe(struct consdev *cn)
485 {
486 	cn->cn_pri = CN_MIDPRI;
487 	cn->cn_dev = makedev(12, 0);
488 	printf(" pc%d", minor(cn->cn_dev));
489 }
490 
491 void
492 efi_cons_init(struct consdev *cp)
493 {
494 }
495 
496 int
497 efi_cons_getc(dev_t dev)
498 {
499 	EFI_INPUT_KEY	 key;
500 	EFI_STATUS	 status;
501 	UINTN		 dummy;
502 	static int	 lastchar = 0;
503 
504 	if (lastchar) {
505 		int r = lastchar;
506 		if ((dev & 0x80) == 0)
507 			lastchar = 0;
508 		return (r);
509 	}
510 
511 	status = conin->ReadKeyStroke(conin, &key);
512 	while (status == EFI_NOT_READY || key.UnicodeChar == 0) {
513 		if (dev & 0x80)
514 			return (0);
515 		BS->WaitForEvent(1, &conin->WaitForKey, &dummy);
516 		status = conin->ReadKeyStroke(conin, &key);
517 	}
518 
519 	if (dev & 0x80)
520 		lastchar = key.UnicodeChar;
521 
522 	return (key.UnicodeChar);
523 }
524 
525 void
526 efi_cons_putc(dev_t dev, int c)
527 {
528 	CHAR16	buf[2];
529 
530 	if (c == '\n')
531 		efi_cons_putc(dev, '\r');
532 
533 	buf[0] = c;
534 	buf[1] = 0;
535 
536 	conout->OutputString(conout, buf);
537 }
538 
539 int
540 efi_cons_getshifts(dev_t dev)
541 {
542 	/* XXX */
543 	return (0);
544 }
545 
546 int com_addr = -1;
547 int com_speed = -1;
548 
549 static SERIAL_IO_INTERFACE	*serios[4];
550 const int comports[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
551 
552 /* call with sp == 0 to query the current speed */
553 int
554 pio_comspeed(dev_t dev, int sp)
555 {
556 	int port = (com_addr == -1) ? comports[minor(dev)] : com_addr;
557 	int i, newsp;
558 	int err;
559 
560 	if (sp <= 0)
561 		return com_speed;
562 	/* valid baud rate? */
563 	if (115200 < sp || sp < 75)
564 		return -1;
565 
566 	/*
567 	 * Accepted speeds:
568 	 *   75 150 300 600 1200 2400 4800 9600 19200 38400 76800 and
569 	 *   14400 28800 57600 115200
570 	 */
571 	for (i = sp; i != 75 && i != 14400; i >>= 1)
572 		if (i & 1)
573 			return -1;
574 
575 /* ripped screaming from dev/ic/com.c */
576 #define divrnd(n, q)    (((n)*2/(q)+1)/2)       /* divide and round off */
577 	newsp = divrnd((COM_FREQ / 16), sp);
578 	if (newsp <= 0)
579 		return -1;
580 	err = divrnd((COM_FREQ / 16) * 1000, sp * newsp) - 1000;
581 	if (err < 0)
582 		err = -err;
583 	if (err > COM_TOLERANCE)
584 		return -1;
585 #undef  divrnd
586 
587 	if (com_speed != -1 && cn_tab && cn_tab->cn_dev == dev &&
588 	    com_speed != sp) {
589 		printf("com%d: changing speed to %d baud in 5 seconds, "
590 		    "change your terminal to match!\n\a",
591 		    minor(dev), sp);
592 		sleep(5);
593 	}
594 
595 	outb(port + com_cfcr, LCR_DLAB);
596 	outb(port + com_dlbl, newsp);
597 	outb(port + com_dlbh, newsp>>8);
598 	outb(port + com_cfcr, LCR_8BITS);
599 	if (com_speed != -1)
600 		printf("\ncom%d: %d baud\n", minor(dev), sp);
601 
602 	newsp = com_speed;
603 	com_speed = sp;
604 	return newsp;
605 }
606 
607 int
608 pio_com_getc(dev_t dev)
609 {
610 	int port = (com_addr == -1) ? comports[minor(dev & 0x7f)] : com_addr;
611 
612 	if (dev & 0x80)
613 		return (inb(port + com_lsr) & LSR_RXRDY);
614 
615 	while ((inb(port + com_lsr) & LSR_RXRDY) == 0)
616 		;
617 
618 	return (inb(port + com_data) & 0xff);
619 }
620 
621 void
622 pio_com_putc(dev_t dev, int c)
623 {
624 	int port = (com_addr == -1) ? comports[minor(dev)] : com_addr;
625 
626 	while ((inb(port + com_lsr) & LSR_TXRDY) == 0)
627 		;
628 
629 	outb(port + com_data, c);
630 }
631 
632 void
633 efi_com_probe(struct consdev *cn)
634 {
635 	EFI_HANDLE		*handles = NULL;
636 	SERIAL_IO_INTERFACE	*serio;
637 	EFI_STATUS		 status;
638 	EFI_DEVICE_PATH		*dp, *dp0;
639 	EFI_DEV_PATH_PTR	 dpp;
640 	UINTN			 sz;
641 	int			 i, uid = -1;
642 
643 	cn->cn_pri = CN_LOWPRI;
644 	cn->cn_dev = makedev(8, 0);
645 
646 	sz = 0;
647 	status = BS->LocateHandle(ByProtocol, &serio_guid, 0, &sz, 0);
648 	if (status == EFI_BUFFER_TOO_SMALL) {
649 		handles = alloc(sz);
650 		status = BS->LocateHandle(ByProtocol, &serio_guid,
651 		    0, &sz, handles);
652 	}
653 	if (handles == NULL || EFI_ERROR(status)) {
654 		free(handles, sz);
655 		return;
656 	}
657 
658 	for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) {
659 		/*
660 		 * Identify port number of the handle.  This assumes ACPI
661 		 * UID 0-3 map to legacy COM[1-4] and they use the legacy
662 		 * port address.
663 		 */
664 		status = BS->HandleProtocol(handles[i], &devp_guid,
665 		    (void **)&dp0);
666 		if (EFI_ERROR(status))
667 			continue;
668 		uid = -1;
669 		for (dp = dp0; !IsDevicePathEnd(dp);
670 		    dp = NextDevicePathNode(dp)) {
671 			dpp = (EFI_DEV_PATH_PTR)dp;
672 			if (DevicePathType(dp) == ACPI_DEVICE_PATH &&
673 			    DevicePathSubType(dp) == ACPI_DP)
674 				if (dpp.Acpi->HID == EFI_PNP_ID(0x0501)) {
675 					uid = dpp.Acpi->UID;
676 					break;
677 				}
678 		}
679 		if (uid < 0 || nitems(serios) <= uid)
680 			continue;
681 
682 		/* Prepare SERIAL_IO_INTERFACE */
683 		status = BS->HandleProtocol(handles[i], &serio_guid,
684 		    (void **)&serio);
685 		if (EFI_ERROR(status))
686 			continue;
687 		serios[uid] = serio;
688 	}
689 	free(handles, sz);
690 
691 	for (i = 0; i < nitems(serios); i++) {
692 		if (serios[i] != NULL)
693 			printf(" com%d", i);
694 	}
695 }
696 
697 int
698 efi_valid_com(dev_t dev)
699 {
700 	return (minor(dev) < nitems(serios) && serios[minor(dev)] != NULL);
701 }
702 
703 int
704 comspeed(dev_t dev, int sp)
705 {
706 	EFI_STATUS		 status;
707 	SERIAL_IO_INTERFACE	*serio = serios[minor(dev)];
708 	int			 newsp;
709 
710 	if (sp <= 0)
711 		return com_speed;
712 
713 	if (!efi_valid_com(dev))
714 		return pio_comspeed(dev, sp);
715 
716 	if (serio->Mode->BaudRate != sp) {
717 		status = serio->SetAttributes(serio, sp,
718 		    serio->Mode->ReceiveFifoDepth,
719 		    serio->Mode->Timeout, serio->Mode->Parity,
720 		    serio->Mode->DataBits, serio->Mode->StopBits);
721 		if (EFI_ERROR(status)) {
722 			printf("com%d: SetAttribute() failed with status=%d\n",
723 			    minor(dev), status);
724 			return (-1);
725 		}
726 		if (com_speed != -1)
727 			printf("\ncom%d: %d baud\n", minor(dev), sp);
728 	}
729 
730 	/* same as comspeed() in libsa/bioscons.c */
731 	newsp = com_speed;
732 	com_speed = sp;
733 
734 	return (newsp);
735 }
736 
737 void
738 efi_com_init(struct consdev *cn)
739 {
740 	if (!efi_valid_com(cn->cn_dev))
741 		/* This actually happens if the machine has another serial.  */
742 		return;
743 
744 	if (com_speed == -1)
745 		comspeed(cn->cn_dev, 9600); /* default speed is 9600 baud */
746 }
747 
748 int
749 efi_com_getc(dev_t dev)
750 {
751 	EFI_STATUS		 status;
752 	SERIAL_IO_INTERFACE	*serio;
753 	UINTN			 sz;
754 	u_char			 buf;
755 	static u_char		 lastchar = 0;
756 
757 	if (!efi_valid_com(dev & 0x7f))
758 		return pio_com_getc(dev);
759 	serio = serios[minor(dev & 0x7f)];
760 
761 	if (lastchar != 0) {
762 		int r = lastchar;
763 		if ((dev & 0x80) == 0)
764 			lastchar = 0;
765 		return (r);
766 	}
767 
768 	for (;;) {
769 		sz = 1;
770 		status = serio->Read(serio, &sz, &buf);
771 		if (status == EFI_SUCCESS && sz > 0)
772 			break;
773 		if (status != EFI_TIMEOUT && EFI_ERROR(status))
774 			panic("Error reading from serial status=%d", status);
775 		if (dev & 0x80)
776 			return (0);
777 	}
778 
779 	if (dev & 0x80)
780 		lastchar = buf;
781 
782 	return (buf);
783 }
784 
785 void
786 efi_com_putc(dev_t dev, int c)
787 {
788 	SERIAL_IO_INTERFACE	*serio;
789 	UINTN			 sz = 1;
790 	u_char			 buf;
791 
792 	if (!efi_valid_com(dev)) {
793 		pio_com_putc(dev, c);
794 		return;
795 	}
796 	serio = serios[minor(dev)];
797 	buf = c;
798 	serio->Write(serio, &sz, &buf);
799 }
800 
801 /***********************************************************************
802  * Miscellaneous
803  ***********************************************************************/
804 /*
805  * ACPI GUID is confusing in UEFI spec.
806  * {EFI_,}_ACPI_20_TABLE_GUID or EFI_ACPI_TABLE_GUID means
807  * ACPI 2.0 or above.
808  */
809 static EFI_GUID			 acpi_guid = ACPI_20_TABLE_GUID;
810 static EFI_GUID			 smbios_guid = SMBIOS_TABLE_GUID;
811 static EFI_GRAPHICS_OUTPUT	*gop;
812 static int			 gopmode = -1;
813 
814 #define	efi_guidcmp(_a, _b)	memcmp((_a), (_b), sizeof(EFI_GUID))
815 
816 static EFI_STATUS
817 efi_gop_setmode(int mode)
818 {
819 	EFI_STATUS	status;
820 
821 	status = gop->SetMode(gop, mode);
822 	if (EFI_ERROR(status) || gop->Mode->Mode != mode)
823 		printf("GOP SetMode() failed (%d)\n", status);
824 
825 	return (status);
826 }
827 
828 void
829 efi_makebootargs(void)
830 {
831 	int			 i;
832 	EFI_STATUS		 status;
833 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
834 				*gopi;
835 	bios_efiinfo_t		*ei = &bios_efiinfo;
836 	int			 curmode;
837 	UINTN			 sz, gopsiz, bestsiz = 0;
838 
839 	/*
840 	 * ACPI, BIOS configuration table
841 	 */
842 	for (i = 0; i < ST->NumberOfTableEntries; i++) {
843 		if (efi_guidcmp(&acpi_guid,
844 		    &ST->ConfigurationTable[i].VendorGuid) == 0)
845 			ei->config_acpi = (uintptr_t)
846 			    ST->ConfigurationTable[i].VendorTable;
847 		else if (efi_guidcmp(&smbios_guid,
848 		    &ST->ConfigurationTable[i].VendorGuid) == 0)
849 			ei->config_smbios = (uintptr_t)
850 			    ST->ConfigurationTable[i].VendorTable;
851 	}
852 
853 	/*
854 	 * Frame buffer
855 	 */
856 	status = BS->LocateProtocol(&gop_guid, NULL, (void **)&gop);
857 	if (!EFI_ERROR(status)) {
858 		if (gopmode < 0) {
859 			for (i = 0; i < gop->Mode->MaxMode; i++) {
860 				status = gop->QueryMode(gop, i, &sz, &gopi);
861 				if (EFI_ERROR(status))
862 					continue;
863 				gopsiz = gopi->HorizontalResolution *
864 				    gopi->VerticalResolution;
865 				if (gopsiz > bestsiz) {
866 					gopmode = i;
867 					bestsiz = gopsiz;
868 				}
869 			}
870 		}
871 		if (gopmode >= 0 && gopmode != gop->Mode->Mode) {
872 			curmode = gop->Mode->Mode;
873 			if (efi_gop_setmode(gopmode) != EFI_SUCCESS)
874 				(void)efi_gop_setmode(curmode);
875 		}
876 
877 		gopi = gop->Mode->Info;
878 		switch (gopi->PixelFormat) {
879 		case PixelBlueGreenRedReserved8BitPerColor:
880 			ei->fb_red_mask      = 0x00ff0000;
881 			ei->fb_green_mask    = 0x0000ff00;
882 			ei->fb_blue_mask     = 0x000000ff;
883 			ei->fb_reserved_mask = 0xff000000;
884 			break;
885 		case PixelRedGreenBlueReserved8BitPerColor:
886 			ei->fb_red_mask      = 0x000000ff;
887 			ei->fb_green_mask    = 0x0000ff00;
888 			ei->fb_blue_mask     = 0x00ff0000;
889 			ei->fb_reserved_mask = 0xff000000;
890 			break;
891 		case PixelBitMask:
892 			ei->fb_red_mask = gopi->PixelInformation.RedMask;
893 			ei->fb_green_mask = gopi->PixelInformation.GreenMask;
894 			ei->fb_blue_mask = gopi->PixelInformation.BlueMask;
895 			ei->fb_reserved_mask =
896 			    gopi->PixelInformation.ReservedMask;
897 			break;
898 		default:
899 			break;
900 		}
901 		ei->fb_addr = gop->Mode->FrameBufferBase;
902 		ei->fb_size = gop->Mode->FrameBufferSize;
903 		ei->fb_height = gopi->VerticalResolution;
904 		ei->fb_width = gopi->HorizontalResolution;
905 		ei->fb_pixpsl = gopi->PixelsPerScanLine;
906 	}
907 
908 	/*
909 	 * EFI system table
910 	 */
911 	ei->system_table = (uintptr_t)ST;
912 
913 #ifdef __amd64__
914 	ei->flags |= BEI_64BIT;
915 #endif
916 
917 	addbootarg(BOOTARG_EFIINFO, sizeof(bios_efiinfo), &bios_efiinfo);
918 }
919 
920 void
921 _rtt(void)
922 {
923 #ifdef EFI_DEBUG
924 	printf("Hit any key to reboot\n");
925 	efi_cons_getc(0);
926 #endif
927 	RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
928 	for (;;)
929 		continue;
930 }
931 
932 time_t
933 getsecs(void)
934 {
935 	EFI_TIME	t;
936 	time_t		r = 0;
937 	int		y = 0;
938 	const int	daytab[][14] = {
939 	    { 0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 },
940 	    { 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
941 	};
942 #define isleap(_y) (((_y) % 4) == 0 && (((_y) % 100) != 0 || ((_y) % 400) == 0))
943 
944 	ST->RuntimeServices->GetTime(&t, NULL);
945 
946 	/* Calc days from UNIX epoch */
947 	r = (t.Year - 1970) * 365;
948 	for (y = 1970; y < t.Year; y++) {
949 		if (isleap(y))
950 			r++;
951 	}
952 	r += daytab[isleap(t.Year)? 1 : 0][t.Month] + t.Day;
953 
954 	/* Calc secs */
955 	r *= 60 * 60 * 24;
956 	r += ((t.Hour * 60) + t.Minute) * 60 + t.Second;
957 	if (-24 * 60 < t.TimeZone && t.TimeZone < 24 * 60)
958 		r += t.TimeZone * 60;
959 
960 	return (r);
961 }
962 
963 u_int
964 sleep(u_int i)
965 {
966 	time_t t;
967 
968 	/*
969 	 * Loop for the requested number of seconds, polling,
970 	 * so that it may handle interrupts.
971 	 */
972 	for (t = getsecs() + i; getsecs() < t; cnischar())
973 		;
974 
975 	return 0;
976 }
977 
978 /***********************************************************************
979  * Commands
980  ***********************************************************************/
981 int
982 Xexit_efi(void)
983 {
984 	BS->Exit(IH, 0, 0, NULL);
985 	for (;;)
986 		continue;
987 	return (0);
988 }
989 
990 int
991 Xvideo_efi(void)
992 {
993 	int	 i, mode = -1;
994 
995 	if (cmd.argc >= 2) {
996 		mode = strtol(cmd.argv[1], NULL, 10);
997 		if (0 <= mode && mode < nitems(efi_video) &&
998 		    efi_video[mode].cols > 0) {
999 			conout->SetMode(conout, mode);
1000 			efi_video_reset();
1001 		}
1002 	} else {
1003 		for (i = 0; i < nitems(efi_video) &&
1004 		    i < conout->Mode->MaxMode; i++) {
1005 			if (efi_video[i].cols > 0)
1006 				printf("Mode %d: %d x %d\n", i,
1007 				    efi_video[i].cols,
1008 				    efi_video[i].rows);
1009 		}
1010 		printf("\n");
1011 	}
1012 	printf("Current Mode = %d\n", conout->Mode->Mode);
1013 
1014 	return (0);
1015 }
1016 
1017 int
1018 Xpoweroff_efi(void)
1019 {
1020 	RS->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
1021 	return (0);
1022 }
1023 
1024 int
1025 Xgop_efi(void)
1026 {
1027 	EFI_STATUS	 status;
1028 	int		 i, mode = -1;
1029 	UINTN		 sz;
1030 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
1031 			*gopi;
1032 
1033 	status = BS->LocateProtocol(&gop_guid, NULL, (void **)&gop);
1034 	if (EFI_ERROR(status))
1035 		return (0);
1036 
1037 	if (cmd.argc >= 2) {
1038 		mode = strtol(cmd.argv[1], NULL, 10);
1039 		if (0 <= mode && mode < gop->Mode->MaxMode) {
1040 			status = gop->QueryMode(gop, mode, &sz, &gopi);
1041 			if (!EFI_ERROR(status)) {
1042 				if (efi_gop_setmode(mode) == EFI_SUCCESS)
1043 					gopmode = mode;
1044 			}
1045 		}
1046 	} else {
1047 		for (i = 0; i < gop->Mode->MaxMode; i++) {
1048 			status = gop->QueryMode(gop, i, &sz, &gopi);
1049 			if (EFI_ERROR(status))
1050 				continue;
1051 			printf("Mode %d: %d x %d (stride = %d)\n", i,
1052 			    gopi->HorizontalResolution,
1053 			    gopi->VerticalResolution,
1054 			    gopi->PixelsPerScanLine);
1055 		}
1056 		printf("\n");
1057 	}
1058 	printf("Current Mode = %d\n", gop->Mode->Mode);
1059 
1060 	return (0);
1061 }
1062