xref: /openbsd/sys/arch/amd64/stand/efiboot/efiboot.c (revision c2111d31)
1 /*	$OpenBSD: efiboot.c,v 1.42 2024/04/25 18:31:49 kn 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
efi_main(EFI_HANDLE image,EFI_SYSTEM_TABLE * systab)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
efi_cleanup(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
efi_diskprobe(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
efi_device_path_depth(EFI_DEVICE_PATH * dp,int dptype)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
efi_device_path_ncmp(EFI_DEVICE_PATH * dpa,EFI_DEVICE_PATH * dpb,int deptn)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
efi_heap_init(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
efi_memprobe(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
efi_memprobe_internal(void)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_GRAPHICS_OUTPUT		*gop = NULL;
430 static EFI_GUID				 con_guid
431 					    = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
432 static EFI_GUID				 gop_guid
433 					    = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
434 static EFI_GUID				 serio_guid
435 					    = SERIAL_IO_PROTOCOL;
436 struct efi_video {
437 	int	cols;
438 	int	rows;
439 } efi_video[32];
440 
441 static void
efi_video_init(void)442 efi_video_init(void)
443 {
444 	EFI_CONSOLE_CONTROL_PROTOCOL	*conctrl = NULL;
445 	int				 i, mode80x25, mode100x31;
446 	UINTN				 cols, rows;
447 	EFI_STATUS			 status;
448 	EFI_HANDLE			*handles;
449 	UINTN				 nhandles;
450 	EFI_GRAPHICS_OUTPUT		*first_gop = NULL;
451 	EFI_DEVICE_PATH			*devp_test = NULL;
452 
453 	status = BS->LocateHandleBuffer(ByProtocol, &gop_guid, NULL, &nhandles,
454 		&handles);
455 	if (!EFI_ERROR(status)) {
456 		for (i = 0; i < nhandles; i++) {
457 			status = BS->HandleProtocol(handles[i], &gop_guid,
458 			    (void **)&gop);
459 			if (first_gop == NULL)
460 				first_gop = gop;
461 			status = BS->HandleProtocol(handles[i], &devp_guid,
462 			    (void **)&devp_test);
463 			if (status == EFI_SUCCESS)
464 				break;
465 		}
466 		if (status != EFI_SUCCESS)
467 			gop = first_gop;
468 		BS->FreePool(handles);
469 	}
470 
471 	conout = ST->ConOut;
472 	status = BS->LocateProtocol(&con_guid, NULL, (void **)&conctrl);
473 	if (status == EFI_SUCCESS)
474 		conctrl->SetMode(conctrl, EfiConsoleControlScreenText);
475 	mode80x25 = -1;
476 	mode100x31 = -1;
477 	for (i = 0; i < conout->Mode->MaxMode; i++) {
478 		status = conout->QueryMode(conout, i, &cols, &rows);
479 		if (EFI_ERROR(status))
480 			continue;
481 		if (mode80x25 < 0 && cols == 80 && rows == 25)
482 			mode80x25 = i;
483 		if (mode100x31 < 0 && cols == 100 && rows == 31)
484 			mode100x31 = i;
485 		if (i < nitems(efi_video)) {
486 			efi_video[i].cols = cols;
487 			efi_video[i].rows = rows;
488 		}
489 	}
490 	if (mode100x31 >= 0)
491 		conout->SetMode(conout, mode100x31);
492 	else if (mode80x25 >= 0)
493 		conout->SetMode(conout, mode80x25);
494 	conin = ST->ConIn;
495 	efi_video_reset();
496 }
497 
498 static void
efi_video_reset(void)499 efi_video_reset(void)
500 {
501 	conout->EnableCursor(conout, TRUE);
502 	conout->SetAttribute(conout, EFI_TEXT_ATTR(EFI_LIGHTGRAY, EFI_BLACK));
503 	conout->ClearScreen(conout);
504 }
505 
506 void
efi_cons_probe(struct consdev * cn)507 efi_cons_probe(struct consdev *cn)
508 {
509 	cn->cn_pri = CN_MIDPRI;
510 	cn->cn_dev = makedev(12, 0);
511 	printf(" pc%d", minor(cn->cn_dev));
512 }
513 
514 void
efi_cons_init(struct consdev * cp)515 efi_cons_init(struct consdev *cp)
516 {
517 }
518 
519 int
efi_cons_getc(dev_t dev)520 efi_cons_getc(dev_t dev)
521 {
522 	EFI_INPUT_KEY	 key;
523 	EFI_STATUS	 status;
524 	UINTN		 dummy;
525 	static int	 lastchar = 0;
526 
527 	if (lastchar) {
528 		int r = lastchar;
529 		if ((dev & 0x80) == 0)
530 			lastchar = 0;
531 		return (r);
532 	}
533 
534 	status = conin->ReadKeyStroke(conin, &key);
535 	while (status == EFI_NOT_READY || key.UnicodeChar == 0) {
536 		if (dev & 0x80)
537 			return (0);
538 		BS->WaitForEvent(1, &conin->WaitForKey, &dummy);
539 		status = conin->ReadKeyStroke(conin, &key);
540 	}
541 
542 	if (dev & 0x80)
543 		lastchar = key.UnicodeChar;
544 
545 	return (key.UnicodeChar);
546 }
547 
548 void
efi_cons_putc(dev_t dev,int c)549 efi_cons_putc(dev_t dev, int c)
550 {
551 	CHAR16	buf[2];
552 
553 	if (c == '\n')
554 		efi_cons_putc(dev, '\r');
555 
556 	buf[0] = c;
557 	buf[1] = 0;
558 
559 	conout->OutputString(conout, buf);
560 }
561 
562 int
efi_cons_getshifts(dev_t dev)563 efi_cons_getshifts(dev_t dev)
564 {
565 	/* XXX */
566 	return (0);
567 }
568 
569 int com_addr = -1;
570 int com_speed = -1;
571 
572 static SERIAL_IO_INTERFACE	*serios[4];
573 const int comports[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
574 
575 /* call with sp == 0 to query the current speed */
576 int
pio_comspeed(dev_t dev,int sp)577 pio_comspeed(dev_t dev, int sp)
578 {
579 	int port = (com_addr == -1) ? comports[minor(dev)] : com_addr;
580 	int i, newsp;
581 	int err;
582 
583 	if (sp <= 0)
584 		return com_speed;
585 	/* valid baud rate? */
586 	if (115200 < sp || sp < 75)
587 		return -1;
588 
589 	/*
590 	 * Accepted speeds:
591 	 *   75 150 300 600 1200 2400 4800 9600 19200 38400 76800 and
592 	 *   14400 28800 57600 115200
593 	 */
594 	for (i = sp; i != 75 && i != 14400; i >>= 1)
595 		if (i & 1)
596 			return -1;
597 
598 /* ripped screaming from dev/ic/com.c */
599 #define divrnd(n, q)    (((n)*2/(q)+1)/2)       /* divide and round off */
600 	newsp = divrnd((COM_FREQ / 16), sp);
601 	if (newsp <= 0)
602 		return -1;
603 	err = divrnd((COM_FREQ / 16) * 1000, sp * newsp) - 1000;
604 	if (err < 0)
605 		err = -err;
606 	if (err > COM_TOLERANCE)
607 		return -1;
608 #undef  divrnd
609 
610 	if (com_speed != -1 && cn_tab && cn_tab->cn_dev == dev &&
611 	    com_speed != sp) {
612 		printf("com%d: changing speed to %d baud in 5 seconds, "
613 		    "change your terminal to match!\n\a",
614 		    minor(dev), sp);
615 		sleep(5);
616 	}
617 
618 	outb(port + com_cfcr, LCR_DLAB);
619 	outb(port + com_dlbl, newsp);
620 	outb(port + com_dlbh, newsp>>8);
621 	outb(port + com_cfcr, LCR_8BITS);
622 	if (com_speed != -1)
623 		printf("\ncom%d: %d baud\n", minor(dev), sp);
624 
625 	newsp = com_speed;
626 	com_speed = sp;
627 	return newsp;
628 }
629 
630 int
pio_com_getc(dev_t dev)631 pio_com_getc(dev_t dev)
632 {
633 	int port = (com_addr == -1) ? comports[minor(dev & 0x7f)] : com_addr;
634 
635 	if (dev & 0x80)
636 		return (inb(port + com_lsr) & LSR_RXRDY);
637 
638 	while ((inb(port + com_lsr) & LSR_RXRDY) == 0)
639 		;
640 
641 	return (inb(port + com_data) & 0xff);
642 }
643 
644 void
pio_com_putc(dev_t dev,int c)645 pio_com_putc(dev_t dev, int c)
646 {
647 	int port = (com_addr == -1) ? comports[minor(dev)] : com_addr;
648 
649 	while ((inb(port + com_lsr) & LSR_TXRDY) == 0)
650 		;
651 
652 	outb(port + com_data, c);
653 }
654 
655 void
efi_com_probe(struct consdev * cn)656 efi_com_probe(struct consdev *cn)
657 {
658 	EFI_HANDLE		*handles = NULL;
659 	SERIAL_IO_INTERFACE	*serio;
660 	EFI_STATUS		 status;
661 	EFI_DEVICE_PATH		*dp, *dp0;
662 	EFI_DEV_PATH_PTR	 dpp;
663 	UINTN			 sz;
664 	int			 i, uid = -1;
665 
666 	cn->cn_pri = CN_LOWPRI;
667 	cn->cn_dev = makedev(8, 0);
668 
669 	sz = 0;
670 	status = BS->LocateHandle(ByProtocol, &serio_guid, 0, &sz, 0);
671 	if (status == EFI_BUFFER_TOO_SMALL) {
672 		handles = alloc(sz);
673 		status = BS->LocateHandle(ByProtocol, &serio_guid,
674 		    0, &sz, handles);
675 	}
676 	if (handles == NULL || EFI_ERROR(status)) {
677 		free(handles, sz);
678 		return;
679 	}
680 
681 	for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) {
682 		/*
683 		 * Identify port number of the handle.  This assumes ACPI
684 		 * UID 0-3 map to legacy COM[1-4] and they use the legacy
685 		 * port address.
686 		 */
687 		status = BS->HandleProtocol(handles[i], &devp_guid,
688 		    (void **)&dp0);
689 		if (EFI_ERROR(status))
690 			continue;
691 		uid = -1;
692 		for (dp = dp0; !IsDevicePathEnd(dp);
693 		    dp = NextDevicePathNode(dp)) {
694 			dpp = (EFI_DEV_PATH_PTR)dp;
695 			if (DevicePathType(dp) == ACPI_DEVICE_PATH &&
696 			    DevicePathSubType(dp) == ACPI_DP)
697 				if (dpp.Acpi->HID == EFI_PNP_ID(0x0501)) {
698 					uid = dpp.Acpi->UID;
699 					break;
700 				}
701 		}
702 		if (uid < 0 || nitems(serios) <= uid)
703 			continue;
704 
705 		/* Prepare SERIAL_IO_INTERFACE */
706 		status = BS->HandleProtocol(handles[i], &serio_guid,
707 		    (void **)&serio);
708 		if (EFI_ERROR(status))
709 			continue;
710 		serios[uid] = serio;
711 	}
712 	free(handles, sz);
713 
714 	for (i = 0; i < nitems(serios); i++) {
715 		if (serios[i] != NULL)
716 			printf(" com%d", i);
717 	}
718 }
719 
720 int
efi_valid_com(dev_t dev)721 efi_valid_com(dev_t dev)
722 {
723 	return (minor(dev) < nitems(serios) && serios[minor(dev)] != NULL);
724 }
725 
726 int
comspeed(dev_t dev,int sp)727 comspeed(dev_t dev, int sp)
728 {
729 	EFI_STATUS		 status;
730 	SERIAL_IO_INTERFACE	*serio = serios[minor(dev)];
731 	int			 newsp;
732 
733 	if (sp <= 0)
734 		return com_speed;
735 
736 	if (!efi_valid_com(dev))
737 		return pio_comspeed(dev, sp);
738 
739 	if (serio->Mode->BaudRate != sp) {
740 		status = serio->SetAttributes(serio, sp,
741 		    serio->Mode->ReceiveFifoDepth,
742 		    serio->Mode->Timeout, serio->Mode->Parity,
743 		    serio->Mode->DataBits, serio->Mode->StopBits);
744 		if (EFI_ERROR(status)) {
745 			printf("com%d: SetAttribute() failed with status=%d\n",
746 			    minor(dev), status);
747 			return (-1);
748 		}
749 		if (com_speed != -1)
750 			printf("\ncom%d: %d baud\n", minor(dev), sp);
751 	}
752 
753 	/* same as comspeed() in libsa/bioscons.c */
754 	newsp = com_speed;
755 	com_speed = sp;
756 
757 	return (newsp);
758 }
759 
760 void
efi_com_init(struct consdev * cn)761 efi_com_init(struct consdev *cn)
762 {
763 	if (!efi_valid_com(cn->cn_dev))
764 		/* This actually happens if the machine has another serial.  */
765 		return;
766 
767 	if (com_speed == -1)
768 		comspeed(cn->cn_dev, 9600); /* default speed is 9600 baud */
769 }
770 
771 int
efi_com_getc(dev_t dev)772 efi_com_getc(dev_t dev)
773 {
774 	EFI_STATUS		 status;
775 	SERIAL_IO_INTERFACE	*serio;
776 	UINTN			 sz;
777 	u_char			 buf;
778 	static u_char		 lastchar = 0;
779 
780 	if (!efi_valid_com(dev & 0x7f))
781 		return pio_com_getc(dev);
782 	serio = serios[minor(dev & 0x7f)];
783 
784 	if (lastchar != 0) {
785 		int r = lastchar;
786 		if ((dev & 0x80) == 0)
787 			lastchar = 0;
788 		return (r);
789 	}
790 
791 	for (;;) {
792 		sz = 1;
793 		status = serio->Read(serio, &sz, &buf);
794 		if (status == EFI_SUCCESS && sz > 0)
795 			break;
796 		if (status != EFI_TIMEOUT && EFI_ERROR(status))
797 			panic("Error reading from serial status=%d", status);
798 		if (dev & 0x80)
799 			return (0);
800 	}
801 
802 	if (dev & 0x80)
803 		lastchar = buf;
804 
805 	return (buf);
806 }
807 
808 void
efi_com_putc(dev_t dev,int c)809 efi_com_putc(dev_t dev, int c)
810 {
811 	SERIAL_IO_INTERFACE	*serio;
812 	UINTN			 sz = 1;
813 	u_char			 buf;
814 
815 	if (!efi_valid_com(dev)) {
816 		pio_com_putc(dev, c);
817 		return;
818 	}
819 	serio = serios[minor(dev)];
820 	buf = c;
821 	serio->Write(serio, &sz, &buf);
822 }
823 
824 /***********************************************************************
825  * Miscellaneous
826  ***********************************************************************/
827 /*
828  * ACPI GUID is confusing in UEFI spec.
829  * {EFI_,}_ACPI_20_TABLE_GUID or EFI_ACPI_TABLE_GUID means
830  * ACPI 2.0 or above.
831  */
832 static EFI_GUID			 acpi_guid = ACPI_20_TABLE_GUID;
833 static EFI_GUID			 smbios_guid = SMBIOS_TABLE_GUID;
834 static EFI_GUID			 esrt_guid = EFI_SYSTEM_RESOURCE_TABLE_GUID;
835 static int			 gopmode = -1;
836 
837 #define	efi_guidcmp(_a, _b)	memcmp((_a), (_b), sizeof(EFI_GUID))
838 
839 static EFI_STATUS
efi_gop_setmode(int mode)840 efi_gop_setmode(int mode)
841 {
842 	EFI_STATUS	status;
843 
844 	status = gop->SetMode(gop, mode);
845 	if (EFI_ERROR(status) || gop->Mode->Mode != mode)
846 		printf("GOP SetMode() failed (%d)\n", status);
847 
848 	return (status);
849 }
850 
851 void
efi_makebootargs(void)852 efi_makebootargs(void)
853 {
854 	int			 i;
855 	EFI_STATUS		 status;
856 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
857 				*gopi;
858 	bios_efiinfo_t		*ei = &bios_efiinfo;
859 	int			 curmode;
860 	UINTN			 sz, gopsiz, bestsiz = 0;
861 
862 	/*
863 	 * ACPI, BIOS configuration table
864 	 */
865 	for (i = 0; i < ST->NumberOfTableEntries; i++) {
866 		if (efi_guidcmp(&acpi_guid,
867 		    &ST->ConfigurationTable[i].VendorGuid) == 0)
868 			ei->config_acpi = (uintptr_t)
869 			    ST->ConfigurationTable[i].VendorTable;
870 		else if (efi_guidcmp(&smbios_guid,
871 		    &ST->ConfigurationTable[i].VendorGuid) == 0)
872 			ei->config_smbios = (uintptr_t)
873 			    ST->ConfigurationTable[i].VendorTable;
874 		else if (efi_guidcmp(&esrt_guid,
875 		    &ST->ConfigurationTable[i].VendorGuid) == 0)
876 			ei->config_esrt = (uintptr_t)
877 			    ST->ConfigurationTable[i].VendorTable;
878 	}
879 
880 	/*
881 	 * Need to copy ESRT because call to ExitBootServices() frees memory of
882 	 * type EfiBootServicesData in which ESRT resides.
883 	 */
884 	if (ei->config_esrt != 0) {
885 		EFI_SYSTEM_RESOURCE_TABLE *esrt =
886 		    (EFI_SYSTEM_RESOURCE_TABLE *)ei->config_esrt;
887 		size_t esrt_size = sizeof(*esrt) +
888 		    esrt->FwResourceCount * sizeof(EFI_SYSTEM_RESOURCE_ENTRY);
889 		void *esrt_copy;
890 
891 		/*
892 		 * Using EfiRuntimeServicesData as it maps to BIOS_MAP_RES,
893 		 * while EfiLoaderData becomes BIOS_MAP_FREE.
894 		 */
895 		status = BS->AllocatePool(EfiRuntimeServicesData,
896 		    esrt_size, &esrt_copy);
897 		if (status == EFI_SUCCESS) {
898 			memcpy(esrt_copy, esrt, esrt_size);
899 			ei->config_esrt = (uintptr_t)esrt_copy;
900 			ei->flags |= BEI_ESRT;
901 		}
902 	}
903 
904 	/*
905 	 * Frame buffer
906 	 */
907 	if (gop != NULL) {
908 		if (gopmode < 0) {
909 			for (i = 0; i < gop->Mode->MaxMode; i++) {
910 				status = gop->QueryMode(gop, i, &sz, &gopi);
911 				if (EFI_ERROR(status))
912 					continue;
913 				gopsiz = gopi->HorizontalResolution *
914 				    gopi->VerticalResolution;
915 				if (gopsiz > bestsiz) {
916 					gopmode = i;
917 					bestsiz = gopsiz;
918 				}
919 			}
920 		}
921 		if (gopmode >= 0 && gopmode != gop->Mode->Mode) {
922 			curmode = gop->Mode->Mode;
923 			if (efi_gop_setmode(gopmode) != EFI_SUCCESS)
924 				(void)efi_gop_setmode(curmode);
925 		}
926 
927 		gopi = gop->Mode->Info;
928 		switch (gopi->PixelFormat) {
929 		case PixelBlueGreenRedReserved8BitPerColor:
930 			ei->fb_red_mask      = 0x00ff0000;
931 			ei->fb_green_mask    = 0x0000ff00;
932 			ei->fb_blue_mask     = 0x000000ff;
933 			ei->fb_reserved_mask = 0xff000000;
934 			break;
935 		case PixelRedGreenBlueReserved8BitPerColor:
936 			ei->fb_red_mask      = 0x000000ff;
937 			ei->fb_green_mask    = 0x0000ff00;
938 			ei->fb_blue_mask     = 0x00ff0000;
939 			ei->fb_reserved_mask = 0xff000000;
940 			break;
941 		case PixelBitMask:
942 			ei->fb_red_mask = gopi->PixelInformation.RedMask;
943 			ei->fb_green_mask = gopi->PixelInformation.GreenMask;
944 			ei->fb_blue_mask = gopi->PixelInformation.BlueMask;
945 			ei->fb_reserved_mask =
946 			    gopi->PixelInformation.ReservedMask;
947 			break;
948 		default:
949 			break;
950 		}
951 		ei->fb_addr = gop->Mode->FrameBufferBase;
952 		ei->fb_size = gop->Mode->FrameBufferSize;
953 		ei->fb_height = gopi->VerticalResolution;
954 		ei->fb_width = gopi->HorizontalResolution;
955 		ei->fb_pixpsl = gopi->PixelsPerScanLine;
956 	}
957 
958 	/*
959 	 * EFI system table
960 	 */
961 	ei->system_table = (uintptr_t)ST;
962 
963 #ifdef __amd64__
964 	ei->flags |= BEI_64BIT;
965 #endif
966 
967 	addbootarg(BOOTARG_EFIINFO, sizeof(bios_efiinfo), &bios_efiinfo);
968 }
969 
970 /* Vendor device path used to indicate the mmio UART on AMD SoCs. */
971 #define AMDSOC_DEVPATH \
972 	{ 0xe76fd4e9, 0x0a30, 0x4ca9, \
973 	    { 0x95, 0x40, 0xd7, 0x99, 0x53, 0x4c, 0xc4, 0xff } }
974 
975 void
efi_setconsdev(void)976 efi_setconsdev(void)
977 {
978 	bios_consdev_t cd;
979 	EFI_STATUS status;
980 	UINT8 data[128];
981 	UINTN size = sizeof(data);
982 	EFI_DEVICE_PATH *dp = (void *)data;
983 	VENDOR_DEVICE_PATH *vdp;
984 	UART_DEVICE_PATH *udp;
985 	EFI_GUID global = EFI_GLOBAL_VARIABLE;
986 	EFI_GUID amdsoc = AMDSOC_DEVPATH;
987 
988 	memset(&cd, 0, sizeof(cd));
989 	cd.consdev = cn_tab->cn_dev;
990 	cd.conspeed = com_speed;
991 	cd.consaddr = com_addr;
992 
993 	/*
994 	 * If the ConOut variable indicates we're using a serial
995 	 * console, use it to determine the baud rate.
996 	 */
997 	status = RS->GetVariable(L"ConOut", &global, NULL, &size, &data);
998 	if (status == EFI_SUCCESS) {
999 		for (dp = (void *)data; !IsDevicePathEnd(dp);
1000 		     dp = NextDevicePathNode(dp)) {
1001 			/*
1002 			 * AMD Ryzen Embedded V1000 SoCs integrate a
1003 			 * Synopsys DesignWare UART that is not
1004 			 * compatible with the traditional 8250 UART
1005 			 * found on the IBM PC.  Pass the magic
1006 			 * parameters to the kernel to make this UART
1007 			 * work.
1008 			 */
1009 			if (DevicePathType(dp) == HARDWARE_DEVICE_PATH &&
1010 			    DevicePathSubType(dp) == HW_VENDOR_DP) {
1011 				vdp = (VENDOR_DEVICE_PATH *)dp;
1012 				if (efi_guidcmp(&vdp->Guid, &amdsoc) == 0) {
1013 					cd.consdev = makedev(8, 4);
1014 					cd.consaddr = *(uint64_t *)(vdp + 1);
1015 					cd.consfreq = 48000000;
1016 					cd.flags = BCD_MMIO;
1017 					cd.reg_width = 4;
1018 					cd.reg_shift = 2;
1019 				}
1020 			}
1021 
1022 			if (DevicePathType(dp) == MESSAGING_DEVICE_PATH &&
1023 			    DevicePathSubType(dp) == MSG_UART_DP) {
1024 				udp = (UART_DEVICE_PATH *)dp;
1025 				if (cd.conspeed == -1)
1026 					cd.conspeed = udp->BaudRate;
1027 			}
1028 		}
1029 	}
1030 
1031 	addbootarg(BOOTARG_CONSDEV, sizeof(cd), &cd);
1032 }
1033 
1034 void
_rtt(void)1035 _rtt(void)
1036 {
1037 #ifdef EFI_DEBUG
1038 	printf("Hit any key to reboot\n");
1039 	efi_cons_getc(0);
1040 #endif
1041 	RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
1042 	for (;;)
1043 		continue;
1044 }
1045 
1046 time_t
getsecs(void)1047 getsecs(void)
1048 {
1049 	EFI_TIME	t;
1050 	time_t		r = 0;
1051 	int		y = 0;
1052 	const int	daytab[][14] = {
1053 	    { 0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 },
1054 	    { 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
1055 	};
1056 #define isleap(_y) (((_y) % 4) == 0 && (((_y) % 100) != 0 || ((_y) % 400) == 0))
1057 
1058 	ST->RuntimeServices->GetTime(&t, NULL);
1059 
1060 	/* Calc days from UNIX epoch */
1061 	r = (t.Year - 1970) * 365;
1062 	for (y = 1970; y < t.Year; y++) {
1063 		if (isleap(y))
1064 			r++;
1065 	}
1066 	r += daytab[isleap(t.Year)? 1 : 0][t.Month] + t.Day;
1067 
1068 	/* Calc secs */
1069 	r *= 60 * 60 * 24;
1070 	r += ((t.Hour * 60) + t.Minute) * 60 + t.Second;
1071 	if (-24 * 60 < t.TimeZone && t.TimeZone < 24 * 60)
1072 		r += t.TimeZone * 60;
1073 
1074 	return (r);
1075 }
1076 
1077 u_int
sleep(u_int i)1078 sleep(u_int i)
1079 {
1080 	time_t t;
1081 	u_int intr = 0;
1082 
1083 	/*
1084 	 * Loop for the requested number of seconds, polling,
1085 	 * so that it may handle interrupts.
1086 	 */
1087 	for (t = getsecs() + i; intr == 0 && getsecs() < t; intr = cnischar())
1088 		;
1089 
1090 	return intr;
1091 }
1092 
1093 #ifdef IDLE_POWEROFF
1094 CHAR16		*idle_name = L"IdlePoweroff";
1095 EFI_STATUS	 idle_status;
1096 /* randomly generated f948e8a9-0570-4338-ad10-29f4cf12849d */
1097 EFI_GUID	 openbsd_guid = { 0xf948e8a9, 0x0570, 0x4338,
1098     { 0xad, 0x10, 0x29, 0xf4, 0xcf, 0x12, 0x84, 0x9d } };
1099 /* Non-Volatile, Boot Service Access, Runtime Service Access */
1100 UINT32		 idle_attrs = 0x1 | 0x2 | 0x4;
1101 UINT16		 idle_secs;
1102 UINTN		 idle_sz = sizeof(idle_secs);
1103 
1104 int
get_idle_timeout(void)1105 get_idle_timeout(void)
1106 {
1107 	idle_status = RS->GetVariable(idle_name, &openbsd_guid, NULL,
1108 	    &idle_sz, &idle_secs);
1109 	if (idle_status != EFI_SUCCESS) {
1110 		if (idle_status != EFI_NOT_FOUND) {
1111 			printf("%s: %d\n", __func__, idle_status);
1112 			return 1;
1113 		}
1114 		return -1;
1115 	}
1116 	return 0;
1117 }
1118 
1119 int
set_idle_timeout(int secs)1120 set_idle_timeout(int secs)
1121 {
1122 	idle_secs = secs;
1123 	idle_sz = idle_secs > 0 ? sizeof(idle_secs) : 0;
1124 	idle_status = RS->SetVariable(idle_name, &openbsd_guid, idle_attrs,
1125 	    idle_sz, &idle_secs);
1126 	if (idle_status != EFI_SUCCESS) {
1127 		printf("%s: %d\n", __func__, idle_status);
1128 		return -1;
1129 	}
1130 	return 0;
1131 }
1132 
1133 /* see lib/libsa/softraid.c sr_crypto_passphrase_decrypt() */
1134 void
idle_poweroff(void)1135 idle_poweroff(void)
1136 {
1137 	if (get_idle_timeout() == 0 && sleep(idle_secs) == 0) {
1138 		printf("\nno input after %us, powering off...\n", idle_secs);
1139 		Xpoweroff_efi();
1140 	}
1141 }
1142 #endif /* IDLE_POWEROFF */
1143 
1144 /***********************************************************************
1145  * Commands
1146  ***********************************************************************/
1147 int
Xexit_efi(void)1148 Xexit_efi(void)
1149 {
1150 	BS->Exit(IH, 0, 0, NULL);
1151 	for (;;)
1152 		continue;
1153 	return (0);
1154 }
1155 
1156 int
Xvideo_efi(void)1157 Xvideo_efi(void)
1158 {
1159 	int	 i, mode = -1;
1160 
1161 	if (cmd.argc >= 2) {
1162 		mode = strtol(cmd.argv[1], NULL, 10);
1163 		if (0 <= mode && mode < nitems(efi_video) &&
1164 		    efi_video[mode].cols > 0) {
1165 			conout->SetMode(conout, mode);
1166 			efi_video_reset();
1167 		}
1168 	} else {
1169 		for (i = 0; i < nitems(efi_video) &&
1170 		    i < conout->Mode->MaxMode; i++) {
1171 			if (efi_video[i].cols > 0)
1172 				printf("Mode %d: %d x %d\n", i,
1173 				    efi_video[i].cols,
1174 				    efi_video[i].rows);
1175 		}
1176 		printf("\n");
1177 	}
1178 	printf("Current Mode = %d\n", conout->Mode->Mode);
1179 
1180 	return (0);
1181 }
1182 
1183 int
Xpoweroff_efi(void)1184 Xpoweroff_efi(void)
1185 {
1186 	RS->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
1187 	return (0);
1188 }
1189 
1190 int
Xgop_efi(void)1191 Xgop_efi(void)
1192 {
1193 	EFI_STATUS	 status;
1194 	int		 i, mode = -1;
1195 	UINTN		 sz;
1196 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
1197 			*gopi;
1198 
1199 	if (gop == NULL) {
1200 		printf("No GOP found\n");
1201 		return (0);
1202 	}
1203 	if (cmd.argc >= 2) {
1204 		mode = strtol(cmd.argv[1], NULL, 10);
1205 		if (0 <= mode && mode < gop->Mode->MaxMode) {
1206 			status = gop->QueryMode(gop, mode, &sz, &gopi);
1207 			if (!EFI_ERROR(status)) {
1208 				if (efi_gop_setmode(mode) == EFI_SUCCESS)
1209 					gopmode = mode;
1210 			}
1211 		}
1212 	} else {
1213 		for (i = 0; i < gop->Mode->MaxMode; i++) {
1214 			status = gop->QueryMode(gop, i, &sz, &gopi);
1215 			if (EFI_ERROR(status))
1216 				continue;
1217 			printf("Mode %d: %d x %d (stride = %d)\n", i,
1218 			    gopi->HorizontalResolution,
1219 			    gopi->VerticalResolution,
1220 			    gopi->PixelsPerScanLine);
1221 		}
1222 		printf("\n");
1223 	}
1224 	printf("Current Mode = %d\n", gop->Mode->Mode);
1225 
1226 	return (0);
1227 }
1228 
1229 #ifdef IDLE_POWEROFF
1230 int
Xidle_efi(void)1231 Xidle_efi(void)
1232 {
1233 	if (cmd.argc >= 2) {
1234 		int secs;
1235 
1236 		secs = strtol(cmd.argv[1], NULL, 10);
1237 		if (0 <= secs && secs < UINT16_MAX)
1238 			set_idle_timeout(secs);
1239 	} else {
1240 		if (get_idle_timeout() == 0)
1241 			printf("Timeout = %us\n", idle_secs);
1242 	}
1243 	return 0;
1244 }
1245 #endif /* IDLE_POWEROFF */
1246