1246 lines
29 KiB
C
1246 lines
29 KiB
C
/* $OpenBSD: efiboot.c,v 1.42 2024/04/25 18:31:49 kn Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/queue.h>
|
|
#include <dev/cons.h>
|
|
#include <dev/isa/isareg.h>
|
|
#include <dev/ic/comreg.h>
|
|
#include <sys/disklabel.h>
|
|
#include <cmd.h>
|
|
#include <stand/boot/bootarg.h>
|
|
#include <machine/pio.h>
|
|
|
|
#include "libsa.h"
|
|
#include "disk.h"
|
|
|
|
#include <efi.h>
|
|
#include <efiapi.h>
|
|
#include <efiprot.h>
|
|
#include <eficonsctl.h>
|
|
|
|
#include "efidev.h"
|
|
#include "efiboot.h"
|
|
#include "run_i386.h"
|
|
|
|
#define KERN_LOADSPACE_SIZE (64 * 1024 * 1024)
|
|
|
|
EFI_SYSTEM_TABLE *ST;
|
|
EFI_BOOT_SERVICES *BS;
|
|
EFI_RUNTIME_SERVICES *RS;
|
|
EFI_HANDLE IH;
|
|
EFI_DEVICE_PATH *efi_bootdp = NULL;
|
|
EFI_PHYSICAL_ADDRESS heap;
|
|
EFI_LOADED_IMAGE *loadedImage;
|
|
UINTN heapsiz = 1 * 1024 * 1024;
|
|
UINTN mmap_key;
|
|
static EFI_GUID imgp_guid = LOADED_IMAGE_PROTOCOL;
|
|
static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
|
|
static EFI_GUID devp_guid = DEVICE_PATH_PROTOCOL;
|
|
u_long efi_loadaddr;
|
|
|
|
int efi_device_path_depth(EFI_DEVICE_PATH *dp, int);
|
|
int efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int);
|
|
static void efi_heap_init(void);
|
|
static int efi_memprobe_internal(void);
|
|
static void efi_video_init(void);
|
|
static void efi_video_reset(void);
|
|
static EFI_STATUS
|
|
efi_gop_setmode(int mode);
|
|
EFI_STATUS efi_main(EFI_HANDLE, EFI_SYSTEM_TABLE *);
|
|
|
|
void (*run_i386)(u_long, u_long, int, int, int, int, int, int, int, int)
|
|
__attribute__((noreturn));
|
|
|
|
extern int bios_bootdev;
|
|
|
|
EFI_STATUS
|
|
efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
|
|
{
|
|
extern char *progname;
|
|
EFI_LOADED_IMAGE *imgp;
|
|
EFI_DEVICE_PATH *dp0 = NULL, *dp;
|
|
EFI_STATUS status;
|
|
EFI_PHYSICAL_ADDRESS stack;
|
|
|
|
ST = systab;
|
|
BS = ST->BootServices;
|
|
RS = ST->RuntimeServices;
|
|
IH = image;
|
|
|
|
/* disable reset by watchdog after 5 minutes */
|
|
BS->SetWatchdogTimer(0, 0, 0, NULL);
|
|
|
|
efi_video_init();
|
|
efi_heap_init();
|
|
|
|
status = BS->HandleProtocol(image, &imgp_guid, (void **)&imgp);
|
|
if (status == EFI_SUCCESS)
|
|
status = BS->HandleProtocol(imgp->DeviceHandle, &devp_guid,
|
|
(void **)&dp0);
|
|
if (status == EFI_SUCCESS) {
|
|
for (dp = dp0; !IsDevicePathEnd(dp);
|
|
dp = NextDevicePathNode(dp)) {
|
|
if (DevicePathType(dp) == MEDIA_DEVICE_PATH &&
|
|
(DevicePathSubType(dp) == MEDIA_HARDDRIVE_DP ||
|
|
DevicePathSubType(dp) == MEDIA_CDROM_DP)) {
|
|
bios_bootdev =
|
|
(DevicePathSubType(dp) == MEDIA_CDROM_DP)
|
|
? 0x1e0 : 0x80;
|
|
efi_bootdp = dp0;
|
|
break;
|
|
} else if (DevicePathType(dp) == MESSAGING_DEVICE_PATH&&
|
|
DevicePathSubType(dp) == MSG_MAC_ADDR_DP) {
|
|
bios_bootdev = 0x0;
|
|
efi_bootdp = dp0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef __amd64__
|
|
/* allocate run_i386_start() on heap */
|
|
if ((run_i386 = alloc(run_i386_size)) == NULL)
|
|
panic("alloc() failed");
|
|
memcpy(run_i386, run_i386_start, run_i386_size);
|
|
#endif
|
|
|
|
/* can't use sa_cleanup since printf is used after sa_cleanup() */
|
|
/* sa_cleanup = efi_cleanup; */
|
|
|
|
#ifdef __amd64__
|
|
progname = "BOOTX64";
|
|
#else
|
|
progname = "BOOTIA32";
|
|
#endif
|
|
|
|
/*
|
|
* Move the stack before calling boot(). UEFI on some machines
|
|
* locate the stack on our kernel load address.
|
|
*/
|
|
stack = heap + heapsiz;
|
|
#if defined(__amd64__)
|
|
asm("movq %0, %%rsp;"
|
|
"mov %1, %%edi;"
|
|
"call boot;"
|
|
:: "r"(stack - 32), "r"(bios_bootdev));
|
|
#else
|
|
asm("movl %0, %%esp;"
|
|
"movl %1, (%%esp);"
|
|
"call boot;"
|
|
:: "r"(stack - 32), "r"(bios_bootdev));
|
|
#endif
|
|
/* must not reach here */
|
|
return (EFI_SUCCESS);
|
|
}
|
|
|
|
void
|
|
efi_cleanup(void)
|
|
{
|
|
int retry;
|
|
EFI_STATUS status;
|
|
|
|
/* retry once in case of failure */
|
|
for (retry = 1; retry >= 0; retry--) {
|
|
efi_memprobe_internal(); /* sync the current map */
|
|
status = BS->ExitBootServices(IH, mmap_key);
|
|
if (status == EFI_SUCCESS)
|
|
break;
|
|
if (retry == 0)
|
|
panic("ExitBootServices failed (%d)", status);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
* Disk
|
|
***********************************************************************/
|
|
struct disklist_lh efi_disklist;
|
|
|
|
void
|
|
efi_diskprobe(void)
|
|
{
|
|
int i, bootdev = 0, depth = -1;
|
|
UINTN sz;
|
|
EFI_STATUS status;
|
|
EFI_HANDLE *handles = NULL;
|
|
EFI_BLOCK_IO *blkio;
|
|
EFI_BLOCK_IO_MEDIA *media;
|
|
struct diskinfo *di;
|
|
EFI_DEVICE_PATH *dp;
|
|
|
|
TAILQ_INIT(&efi_disklist);
|
|
|
|
sz = 0;
|
|
status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, 0);
|
|
if (status == EFI_BUFFER_TOO_SMALL) {
|
|
handles = alloc(sz);
|
|
status = BS->LocateHandle(ByProtocol, &blkio_guid,
|
|
0, &sz, handles);
|
|
}
|
|
if (handles == NULL || EFI_ERROR(status))
|
|
panic("BS->LocateHandle() returns %d", status);
|
|
|
|
if (efi_bootdp != NULL)
|
|
depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
|
|
|
|
/*
|
|
* U-Boot incorrectly represents devices with a single
|
|
* MEDIA_DEVICE_PATH component. In that case include that
|
|
* component into the matching, otherwise we'll blindly select
|
|
* the first device.
|
|
*/
|
|
if (depth == 0)
|
|
depth = 1;
|
|
|
|
for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) {
|
|
status = BS->HandleProtocol(handles[i], &blkio_guid,
|
|
(void **)&blkio);
|
|
if (EFI_ERROR(status))
|
|
panic("BS->HandleProtocol() returns %d", status);
|
|
|
|
media = blkio->Media;
|
|
if (media->LogicalPartition || !media->MediaPresent)
|
|
continue;
|
|
di = alloc(sizeof(struct diskinfo));
|
|
efid_init(di, blkio);
|
|
|
|
if (efi_bootdp == NULL || depth == -1 || bootdev != 0)
|
|
goto next;
|
|
status = BS->HandleProtocol(handles[i], &devp_guid,
|
|
(void **)&dp);
|
|
if (EFI_ERROR(status))
|
|
goto next;
|
|
if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) {
|
|
TAILQ_INSERT_HEAD(&efi_disklist, di, list);
|
|
bootdev = 1;
|
|
continue;
|
|
}
|
|
next:
|
|
TAILQ_INSERT_TAIL(&efi_disklist, di, list);
|
|
}
|
|
|
|
free(handles, sz);
|
|
}
|
|
|
|
/*
|
|
* Determine the number of nodes up to, but not including, the first
|
|
* node of the specified type.
|
|
*/
|
|
int
|
|
efi_device_path_depth(EFI_DEVICE_PATH *dp, int dptype)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp), i++) {
|
|
if (DevicePathType(dp) == dptype)
|
|
return (i);
|
|
}
|
|
|
|
return (i);
|
|
}
|
|
|
|
int
|
|
efi_device_path_ncmp(EFI_DEVICE_PATH *dpa, EFI_DEVICE_PATH *dpb, int deptn)
|
|
{
|
|
int i, cmp;
|
|
|
|
for (i = 0; i < deptn; i++) {
|
|
if (IsDevicePathEnd(dpa) || IsDevicePathEnd(dpb))
|
|
return ((IsDevicePathEnd(dpa) && IsDevicePathEnd(dpb))
|
|
? 0 : (IsDevicePathEnd(dpa))? -1 : 1);
|
|
cmp = DevicePathNodeLength(dpa) - DevicePathNodeLength(dpb);
|
|
if (cmp)
|
|
return (cmp);
|
|
cmp = memcmp(dpa, dpb, DevicePathNodeLength(dpa));
|
|
if (cmp)
|
|
return (cmp);
|
|
dpa = NextDevicePathNode(dpa);
|
|
dpb = NextDevicePathNode(dpb);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* Memory
|
|
***********************************************************************/
|
|
bios_memmap_t bios_memmap[128];
|
|
bios_efiinfo_t bios_efiinfo;
|
|
|
|
static void
|
|
efi_heap_init(void)
|
|
{
|
|
EFI_STATUS status;
|
|
|
|
heap = HEAP_LIMIT;
|
|
status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData,
|
|
EFI_SIZE_TO_PAGES(heapsiz), &heap);
|
|
if (status != EFI_SUCCESS)
|
|
panic("BS->AllocatePages()");
|
|
}
|
|
|
|
void
|
|
efi_memprobe(void)
|
|
{
|
|
u_int n = 0;
|
|
bios_memmap_t *bm;
|
|
EFI_STATUS status;
|
|
EFI_PHYSICAL_ADDRESS
|
|
addr = 0x10000000ULL; /* Below 256MB */
|
|
int error;
|
|
|
|
status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData,
|
|
EFI_SIZE_TO_PAGES(KERN_LOADSPACE_SIZE), &addr);
|
|
if (status != EFI_SUCCESS)
|
|
panic("BS->AllocatePages()");
|
|
efi_loadaddr = addr;
|
|
|
|
printf(" mem[");
|
|
error = efi_memprobe_internal();
|
|
for (bm = bios_memmap; bm->type != BIOS_MAP_END; bm++) {
|
|
if (bm->type == BIOS_MAP_FREE && bm->size > 12 * 1024) {
|
|
if (n++ != 0)
|
|
printf(" ");
|
|
if (bm->size > 1024 * 1024)
|
|
printf("%uM", bm->size / 1024 / 1024);
|
|
else
|
|
printf("%uK", bm->size / 1024);
|
|
}
|
|
}
|
|
if (error == E2BIG)
|
|
printf(" overflow");
|
|
printf("]");
|
|
}
|
|
|
|
static int
|
|
efi_memprobe_internal(void)
|
|
{
|
|
EFI_STATUS status;
|
|
UINTN mapkey, mmsiz, siz;
|
|
UINT32 mmver;
|
|
EFI_MEMORY_DESCRIPTOR *mm0, *mm;
|
|
int i, n;
|
|
bios_memmap_t *bm, bm0;
|
|
int error = 0;
|
|
|
|
cnvmem = extmem = 0;
|
|
bios_memmap[0].type = BIOS_MAP_END;
|
|
|
|
if (bios_efiinfo.mmap_start != 0)
|
|
free((void *)bios_efiinfo.mmap_start, bios_efiinfo.mmap_size);
|
|
|
|
siz = 0;
|
|
status = BS->GetMemoryMap(&siz, NULL, &mapkey, &mmsiz, &mmver);
|
|
if (status != EFI_BUFFER_TOO_SMALL)
|
|
panic("cannot get the size of memory map");
|
|
mm0 = alloc(siz);
|
|
status = BS->GetMemoryMap(&siz, mm0, &mapkey, &mmsiz, &mmver);
|
|
if (status != EFI_SUCCESS)
|
|
panic("cannot get the memory map");
|
|
n = siz / mmsiz;
|
|
mmap_key = mapkey;
|
|
|
|
for (i = 0, mm = mm0; i < n; i++, mm = NextMemoryDescriptor(mm, mmsiz)){
|
|
bm0.type = BIOS_MAP_END;
|
|
bm0.addr = mm->PhysicalStart;
|
|
bm0.size = mm->NumberOfPages * EFI_PAGE_SIZE;
|
|
if (mm->Type == EfiReservedMemoryType ||
|
|
mm->Type == EfiUnusableMemory ||
|
|
mm->Type == EfiRuntimeServicesCode ||
|
|
mm->Type == EfiRuntimeServicesData)
|
|
bm0.type = BIOS_MAP_RES;
|
|
else if (mm->Type == EfiLoaderCode ||
|
|
mm->Type == EfiLoaderData ||
|
|
mm->Type == EfiBootServicesCode ||
|
|
mm->Type == EfiBootServicesData ||
|
|
mm->Type == EfiConventionalMemory)
|
|
bm0.type = BIOS_MAP_FREE;
|
|
else if (mm->Type == EfiACPIReclaimMemory)
|
|
bm0.type = BIOS_MAP_ACPI;
|
|
else if (mm->Type == EfiACPIMemoryNVS)
|
|
bm0.type = BIOS_MAP_NVS;
|
|
else
|
|
/*
|
|
* XXX Is there anything to do for EfiMemoryMappedIO
|
|
* XXX EfiMemoryMappedIOPortSpace EfiPalCode?
|
|
*/
|
|
bm0.type = BIOS_MAP_RES;
|
|
|
|
for (bm = bios_memmap; bm->type != BIOS_MAP_END; bm++) {
|
|
if (bm->type != bm0.type)
|
|
continue;
|
|
if (bm->addr <= bm0.addr &&
|
|
bm0.addr <= bm->addr + bm->size) {
|
|
bm->size = bm0.addr + bm0.size - bm->addr;
|
|
break;
|
|
} else if (bm0.addr <= bm->addr &&
|
|
bm->addr <= bm0.addr + bm0.size) {
|
|
bm->size = bm->addr + bm->size - bm0.addr;
|
|
bm->addr = bm0.addr;
|
|
break;
|
|
}
|
|
}
|
|
if (bm->type == BIOS_MAP_END) {
|
|
if (bm == &bios_memmap[nitems(bios_memmap) - 1]) {
|
|
error = E2BIG;
|
|
break;
|
|
}
|
|
*bm = bm0;
|
|
(++bm)->type = BIOS_MAP_END;
|
|
}
|
|
}
|
|
for (bm = bios_memmap; bm->type != BIOS_MAP_END; bm++) {
|
|
if (bm->addr < IOM_BEGIN) /* Below memory hole */
|
|
cnvmem =
|
|
max(cnvmem, (bm->addr + bm->size) / 1024);
|
|
if (bm->addr >= IOM_END /* Above the memory hole */ &&
|
|
bm->addr / 1024 == extmem + 1024)
|
|
extmem += bm->size / 1024;
|
|
}
|
|
|
|
bios_efiinfo.mmap_desc_ver = mmver;
|
|
bios_efiinfo.mmap_desc_size = mmsiz;
|
|
bios_efiinfo.mmap_size = siz;
|
|
bios_efiinfo.mmap_start = (uintptr_t)mm0;
|
|
|
|
return error;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* Console
|
|
***********************************************************************/
|
|
static SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
|
|
static SIMPLE_INPUT_INTERFACE *conin;
|
|
static EFI_GRAPHICS_OUTPUT *gop = NULL;
|
|
static EFI_GUID con_guid
|
|
= EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
|
|
static EFI_GUID gop_guid
|
|
= EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
|
|
static EFI_GUID serio_guid
|
|
= SERIAL_IO_PROTOCOL;
|
|
struct efi_video {
|
|
int cols;
|
|
int rows;
|
|
} efi_video[32];
|
|
|
|
static void
|
|
efi_video_init(void)
|
|
{
|
|
EFI_CONSOLE_CONTROL_PROTOCOL *conctrl = NULL;
|
|
int i, mode80x25, mode100x31;
|
|
UINTN cols, rows;
|
|
EFI_STATUS status;
|
|
EFI_HANDLE *handles;
|
|
UINTN nhandles;
|
|
EFI_GRAPHICS_OUTPUT *first_gop = NULL;
|
|
EFI_DEVICE_PATH *devp_test = NULL;
|
|
|
|
status = BS->LocateHandleBuffer(ByProtocol, &gop_guid, NULL, &nhandles,
|
|
&handles);
|
|
if (!EFI_ERROR(status)) {
|
|
for (i = 0; i < nhandles; i++) {
|
|
status = BS->HandleProtocol(handles[i], &gop_guid,
|
|
(void **)&gop);
|
|
if (first_gop == NULL)
|
|
first_gop = gop;
|
|
status = BS->HandleProtocol(handles[i], &devp_guid,
|
|
(void **)&devp_test);
|
|
if (status == EFI_SUCCESS)
|
|
break;
|
|
}
|
|
if (status != EFI_SUCCESS)
|
|
gop = first_gop;
|
|
BS->FreePool(handles);
|
|
}
|
|
|
|
conout = ST->ConOut;
|
|
status = BS->LocateProtocol(&con_guid, NULL, (void **)&conctrl);
|
|
if (status == EFI_SUCCESS)
|
|
conctrl->SetMode(conctrl, EfiConsoleControlScreenText);
|
|
mode80x25 = -1;
|
|
mode100x31 = -1;
|
|
for (i = 0; i < conout->Mode->MaxMode; i++) {
|
|
status = conout->QueryMode(conout, i, &cols, &rows);
|
|
if (EFI_ERROR(status))
|
|
continue;
|
|
if (mode80x25 < 0 && cols == 80 && rows == 25)
|
|
mode80x25 = i;
|
|
if (mode100x31 < 0 && cols == 100 && rows == 31)
|
|
mode100x31 = i;
|
|
if (i < nitems(efi_video)) {
|
|
efi_video[i].cols = cols;
|
|
efi_video[i].rows = rows;
|
|
}
|
|
}
|
|
if (mode100x31 >= 0)
|
|
conout->SetMode(conout, mode100x31);
|
|
else if (mode80x25 >= 0)
|
|
conout->SetMode(conout, mode80x25);
|
|
conin = ST->ConIn;
|
|
efi_video_reset();
|
|
}
|
|
|
|
static void
|
|
efi_video_reset(void)
|
|
{
|
|
conout->EnableCursor(conout, TRUE);
|
|
conout->SetAttribute(conout, EFI_TEXT_ATTR(EFI_LIGHTGRAY, EFI_BLACK));
|
|
conout->ClearScreen(conout);
|
|
}
|
|
|
|
void
|
|
efi_cons_probe(struct consdev *cn)
|
|
{
|
|
cn->cn_pri = CN_MIDPRI;
|
|
cn->cn_dev = makedev(12, 0);
|
|
printf(" pc%d", minor(cn->cn_dev));
|
|
}
|
|
|
|
void
|
|
efi_cons_init(struct consdev *cp)
|
|
{
|
|
}
|
|
|
|
int
|
|
efi_cons_getc(dev_t dev)
|
|
{
|
|
EFI_INPUT_KEY key;
|
|
EFI_STATUS status;
|
|
UINTN dummy;
|
|
static int lastchar = 0;
|
|
|
|
if (lastchar) {
|
|
int r = lastchar;
|
|
if ((dev & 0x80) == 0)
|
|
lastchar = 0;
|
|
return (r);
|
|
}
|
|
|
|
status = conin->ReadKeyStroke(conin, &key);
|
|
while (status == EFI_NOT_READY || key.UnicodeChar == 0) {
|
|
if (dev & 0x80)
|
|
return (0);
|
|
BS->WaitForEvent(1, &conin->WaitForKey, &dummy);
|
|
status = conin->ReadKeyStroke(conin, &key);
|
|
}
|
|
|
|
if (dev & 0x80)
|
|
lastchar = key.UnicodeChar;
|
|
|
|
return (key.UnicodeChar);
|
|
}
|
|
|
|
void
|
|
efi_cons_putc(dev_t dev, int c)
|
|
{
|
|
CHAR16 buf[2];
|
|
|
|
if (c == '\n')
|
|
efi_cons_putc(dev, '\r');
|
|
|
|
buf[0] = c;
|
|
buf[1] = 0;
|
|
|
|
conout->OutputString(conout, buf);
|
|
}
|
|
|
|
int
|
|
efi_cons_getshifts(dev_t dev)
|
|
{
|
|
/* XXX */
|
|
return (0);
|
|
}
|
|
|
|
int com_addr = -1;
|
|
int com_speed = -1;
|
|
|
|
static SERIAL_IO_INTERFACE *serios[4];
|
|
const int comports[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
|
|
|
|
/* call with sp == 0 to query the current speed */
|
|
int
|
|
pio_comspeed(dev_t dev, int sp)
|
|
{
|
|
int port = (com_addr == -1) ? comports[minor(dev)] : com_addr;
|
|
int i, newsp;
|
|
int err;
|
|
|
|
if (sp <= 0)
|
|
return com_speed;
|
|
/* valid baud rate? */
|
|
if (115200 < sp || sp < 75)
|
|
return -1;
|
|
|
|
/*
|
|
* Accepted speeds:
|
|
* 75 150 300 600 1200 2400 4800 9600 19200 38400 76800 and
|
|
* 14400 28800 57600 115200
|
|
*/
|
|
for (i = sp; i != 75 && i != 14400; i >>= 1)
|
|
if (i & 1)
|
|
return -1;
|
|
|
|
/* ripped screaming from dev/ic/com.c */
|
|
#define divrnd(n, q) (((n)*2/(q)+1)/2) /* divide and round off */
|
|
newsp = divrnd((COM_FREQ / 16), sp);
|
|
if (newsp <= 0)
|
|
return -1;
|
|
err = divrnd((COM_FREQ / 16) * 1000, sp * newsp) - 1000;
|
|
if (err < 0)
|
|
err = -err;
|
|
if (err > COM_TOLERANCE)
|
|
return -1;
|
|
#undef divrnd
|
|
|
|
if (com_speed != -1 && cn_tab && cn_tab->cn_dev == dev &&
|
|
com_speed != sp) {
|
|
printf("com%d: changing speed to %d baud in 5 seconds, "
|
|
"change your terminal to match!\n\a",
|
|
minor(dev), sp);
|
|
sleep(5);
|
|
}
|
|
|
|
outb(port + com_cfcr, LCR_DLAB);
|
|
outb(port + com_dlbl, newsp);
|
|
outb(port + com_dlbh, newsp>>8);
|
|
outb(port + com_cfcr, LCR_8BITS);
|
|
if (com_speed != -1)
|
|
printf("\ncom%d: %d baud\n", minor(dev), sp);
|
|
|
|
newsp = com_speed;
|
|
com_speed = sp;
|
|
return newsp;
|
|
}
|
|
|
|
int
|
|
pio_com_getc(dev_t dev)
|
|
{
|
|
int port = (com_addr == -1) ? comports[minor(dev & 0x7f)] : com_addr;
|
|
|
|
if (dev & 0x80)
|
|
return (inb(port + com_lsr) & LSR_RXRDY);
|
|
|
|
while ((inb(port + com_lsr) & LSR_RXRDY) == 0)
|
|
;
|
|
|
|
return (inb(port + com_data) & 0xff);
|
|
}
|
|
|
|
void
|
|
pio_com_putc(dev_t dev, int c)
|
|
{
|
|
int port = (com_addr == -1) ? comports[minor(dev)] : com_addr;
|
|
|
|
while ((inb(port + com_lsr) & LSR_TXRDY) == 0)
|
|
;
|
|
|
|
outb(port + com_data, c);
|
|
}
|
|
|
|
void
|
|
efi_com_probe(struct consdev *cn)
|
|
{
|
|
EFI_HANDLE *handles = NULL;
|
|
SERIAL_IO_INTERFACE *serio;
|
|
EFI_STATUS status;
|
|
EFI_DEVICE_PATH *dp, *dp0;
|
|
EFI_DEV_PATH_PTR dpp;
|
|
UINTN sz;
|
|
int i, uid = -1;
|
|
|
|
cn->cn_pri = CN_LOWPRI;
|
|
cn->cn_dev = makedev(8, 0);
|
|
|
|
sz = 0;
|
|
status = BS->LocateHandle(ByProtocol, &serio_guid, 0, &sz, 0);
|
|
if (status == EFI_BUFFER_TOO_SMALL) {
|
|
handles = alloc(sz);
|
|
status = BS->LocateHandle(ByProtocol, &serio_guid,
|
|
0, &sz, handles);
|
|
}
|
|
if (handles == NULL || EFI_ERROR(status)) {
|
|
free(handles, sz);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) {
|
|
/*
|
|
* Identify port number of the handle. This assumes ACPI
|
|
* UID 0-3 map to legacy COM[1-4] and they use the legacy
|
|
* port address.
|
|
*/
|
|
status = BS->HandleProtocol(handles[i], &devp_guid,
|
|
(void **)&dp0);
|
|
if (EFI_ERROR(status))
|
|
continue;
|
|
uid = -1;
|
|
for (dp = dp0; !IsDevicePathEnd(dp);
|
|
dp = NextDevicePathNode(dp)) {
|
|
dpp = (EFI_DEV_PATH_PTR)dp;
|
|
if (DevicePathType(dp) == ACPI_DEVICE_PATH &&
|
|
DevicePathSubType(dp) == ACPI_DP)
|
|
if (dpp.Acpi->HID == EFI_PNP_ID(0x0501)) {
|
|
uid = dpp.Acpi->UID;
|
|
break;
|
|
}
|
|
}
|
|
if (uid < 0 || nitems(serios) <= uid)
|
|
continue;
|
|
|
|
/* Prepare SERIAL_IO_INTERFACE */
|
|
status = BS->HandleProtocol(handles[i], &serio_guid,
|
|
(void **)&serio);
|
|
if (EFI_ERROR(status))
|
|
continue;
|
|
serios[uid] = serio;
|
|
}
|
|
free(handles, sz);
|
|
|
|
for (i = 0; i < nitems(serios); i++) {
|
|
if (serios[i] != NULL)
|
|
printf(" com%d", i);
|
|
}
|
|
}
|
|
|
|
int
|
|
efi_valid_com(dev_t dev)
|
|
{
|
|
return (minor(dev) < nitems(serios) && serios[minor(dev)] != NULL);
|
|
}
|
|
|
|
int
|
|
comspeed(dev_t dev, int sp)
|
|
{
|
|
EFI_STATUS status;
|
|
SERIAL_IO_INTERFACE *serio = serios[minor(dev)];
|
|
int newsp;
|
|
|
|
if (sp <= 0)
|
|
return com_speed;
|
|
|
|
if (!efi_valid_com(dev))
|
|
return pio_comspeed(dev, sp);
|
|
|
|
if (serio->Mode->BaudRate != sp) {
|
|
status = serio->SetAttributes(serio, sp,
|
|
serio->Mode->ReceiveFifoDepth,
|
|
serio->Mode->Timeout, serio->Mode->Parity,
|
|
serio->Mode->DataBits, serio->Mode->StopBits);
|
|
if (EFI_ERROR(status)) {
|
|
printf("com%d: SetAttribute() failed with status=%d\n",
|
|
minor(dev), status);
|
|
return (-1);
|
|
}
|
|
if (com_speed != -1)
|
|
printf("\ncom%d: %d baud\n", minor(dev), sp);
|
|
}
|
|
|
|
/* same as comspeed() in libsa/bioscons.c */
|
|
newsp = com_speed;
|
|
com_speed = sp;
|
|
|
|
return (newsp);
|
|
}
|
|
|
|
void
|
|
efi_com_init(struct consdev *cn)
|
|
{
|
|
if (!efi_valid_com(cn->cn_dev))
|
|
/* This actually happens if the machine has another serial. */
|
|
return;
|
|
|
|
if (com_speed == -1)
|
|
comspeed(cn->cn_dev, 9600); /* default speed is 9600 baud */
|
|
}
|
|
|
|
int
|
|
efi_com_getc(dev_t dev)
|
|
{
|
|
EFI_STATUS status;
|
|
SERIAL_IO_INTERFACE *serio;
|
|
UINTN sz;
|
|
u_char buf;
|
|
static u_char lastchar = 0;
|
|
|
|
if (!efi_valid_com(dev & 0x7f))
|
|
return pio_com_getc(dev);
|
|
serio = serios[minor(dev & 0x7f)];
|
|
|
|
if (lastchar != 0) {
|
|
int r = lastchar;
|
|
if ((dev & 0x80) == 0)
|
|
lastchar = 0;
|
|
return (r);
|
|
}
|
|
|
|
for (;;) {
|
|
sz = 1;
|
|
status = serio->Read(serio, &sz, &buf);
|
|
if (status == EFI_SUCCESS && sz > 0)
|
|
break;
|
|
if (status != EFI_TIMEOUT && EFI_ERROR(status))
|
|
panic("Error reading from serial status=%d", status);
|
|
if (dev & 0x80)
|
|
return (0);
|
|
}
|
|
|
|
if (dev & 0x80)
|
|
lastchar = buf;
|
|
|
|
return (buf);
|
|
}
|
|
|
|
void
|
|
efi_com_putc(dev_t dev, int c)
|
|
{
|
|
SERIAL_IO_INTERFACE *serio;
|
|
UINTN sz = 1;
|
|
u_char buf;
|
|
|
|
if (!efi_valid_com(dev)) {
|
|
pio_com_putc(dev, c);
|
|
return;
|
|
}
|
|
serio = serios[minor(dev)];
|
|
buf = c;
|
|
serio->Write(serio, &sz, &buf);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* Miscellaneous
|
|
***********************************************************************/
|
|
/*
|
|
* ACPI GUID is confusing in UEFI spec.
|
|
* {EFI_,}_ACPI_20_TABLE_GUID or EFI_ACPI_TABLE_GUID means
|
|
* ACPI 2.0 or above.
|
|
*/
|
|
static EFI_GUID acpi_guid = ACPI_20_TABLE_GUID;
|
|
static EFI_GUID smbios_guid = SMBIOS_TABLE_GUID;
|
|
static EFI_GUID esrt_guid = EFI_SYSTEM_RESOURCE_TABLE_GUID;
|
|
static int gopmode = -1;
|
|
|
|
#define efi_guidcmp(_a, _b) memcmp((_a), (_b), sizeof(EFI_GUID))
|
|
|
|
static EFI_STATUS
|
|
efi_gop_setmode(int mode)
|
|
{
|
|
EFI_STATUS status;
|
|
|
|
status = gop->SetMode(gop, mode);
|
|
if (EFI_ERROR(status) || gop->Mode->Mode != mode)
|
|
printf("GOP SetMode() failed (%d)\n", status);
|
|
|
|
return (status);
|
|
}
|
|
|
|
void
|
|
efi_makebootargs(void)
|
|
{
|
|
int i;
|
|
EFI_STATUS status;
|
|
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
|
|
*gopi;
|
|
bios_efiinfo_t *ei = &bios_efiinfo;
|
|
int curmode;
|
|
UINTN sz, gopsiz, bestsiz = 0;
|
|
|
|
/*
|
|
* ACPI, BIOS configuration table
|
|
*/
|
|
for (i = 0; i < ST->NumberOfTableEntries; i++) {
|
|
if (efi_guidcmp(&acpi_guid,
|
|
&ST->ConfigurationTable[i].VendorGuid) == 0)
|
|
ei->config_acpi = (uintptr_t)
|
|
ST->ConfigurationTable[i].VendorTable;
|
|
else if (efi_guidcmp(&smbios_guid,
|
|
&ST->ConfigurationTable[i].VendorGuid) == 0)
|
|
ei->config_smbios = (uintptr_t)
|
|
ST->ConfigurationTable[i].VendorTable;
|
|
else if (efi_guidcmp(&esrt_guid,
|
|
&ST->ConfigurationTable[i].VendorGuid) == 0)
|
|
ei->config_esrt = (uintptr_t)
|
|
ST->ConfigurationTable[i].VendorTable;
|
|
}
|
|
|
|
/*
|
|
* Need to copy ESRT because call to ExitBootServices() frees memory of
|
|
* type EfiBootServicesData in which ESRT resides.
|
|
*/
|
|
if (ei->config_esrt != 0) {
|
|
EFI_SYSTEM_RESOURCE_TABLE *esrt =
|
|
(EFI_SYSTEM_RESOURCE_TABLE *)ei->config_esrt;
|
|
size_t esrt_size = sizeof(*esrt) +
|
|
esrt->FwResourceCount * sizeof(EFI_SYSTEM_RESOURCE_ENTRY);
|
|
void *esrt_copy;
|
|
|
|
/*
|
|
* Using EfiRuntimeServicesData as it maps to BIOS_MAP_RES,
|
|
* while EfiLoaderData becomes BIOS_MAP_FREE.
|
|
*/
|
|
status = BS->AllocatePool(EfiRuntimeServicesData,
|
|
esrt_size, &esrt_copy);
|
|
if (status == EFI_SUCCESS) {
|
|
memcpy(esrt_copy, esrt, esrt_size);
|
|
ei->config_esrt = (uintptr_t)esrt_copy;
|
|
ei->flags |= BEI_ESRT;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Frame buffer
|
|
*/
|
|
if (gop != NULL) {
|
|
if (gopmode < 0) {
|
|
for (i = 0; i < gop->Mode->MaxMode; i++) {
|
|
status = gop->QueryMode(gop, i, &sz, &gopi);
|
|
if (EFI_ERROR(status))
|
|
continue;
|
|
gopsiz = gopi->HorizontalResolution *
|
|
gopi->VerticalResolution;
|
|
if (gopsiz > bestsiz) {
|
|
gopmode = i;
|
|
bestsiz = gopsiz;
|
|
}
|
|
}
|
|
}
|
|
if (gopmode >= 0 && gopmode != gop->Mode->Mode) {
|
|
curmode = gop->Mode->Mode;
|
|
if (efi_gop_setmode(gopmode) != EFI_SUCCESS)
|
|
(void)efi_gop_setmode(curmode);
|
|
}
|
|
|
|
gopi = gop->Mode->Info;
|
|
switch (gopi->PixelFormat) {
|
|
case PixelBlueGreenRedReserved8BitPerColor:
|
|
ei->fb_red_mask = 0x00ff0000;
|
|
ei->fb_green_mask = 0x0000ff00;
|
|
ei->fb_blue_mask = 0x000000ff;
|
|
ei->fb_reserved_mask = 0xff000000;
|
|
break;
|
|
case PixelRedGreenBlueReserved8BitPerColor:
|
|
ei->fb_red_mask = 0x000000ff;
|
|
ei->fb_green_mask = 0x0000ff00;
|
|
ei->fb_blue_mask = 0x00ff0000;
|
|
ei->fb_reserved_mask = 0xff000000;
|
|
break;
|
|
case PixelBitMask:
|
|
ei->fb_red_mask = gopi->PixelInformation.RedMask;
|
|
ei->fb_green_mask = gopi->PixelInformation.GreenMask;
|
|
ei->fb_blue_mask = gopi->PixelInformation.BlueMask;
|
|
ei->fb_reserved_mask =
|
|
gopi->PixelInformation.ReservedMask;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ei->fb_addr = gop->Mode->FrameBufferBase;
|
|
ei->fb_size = gop->Mode->FrameBufferSize;
|
|
ei->fb_height = gopi->VerticalResolution;
|
|
ei->fb_width = gopi->HorizontalResolution;
|
|
ei->fb_pixpsl = gopi->PixelsPerScanLine;
|
|
}
|
|
|
|
/*
|
|
* EFI system table
|
|
*/
|
|
ei->system_table = (uintptr_t)ST;
|
|
|
|
#ifdef __amd64__
|
|
ei->flags |= BEI_64BIT;
|
|
#endif
|
|
|
|
addbootarg(BOOTARG_EFIINFO, sizeof(bios_efiinfo), &bios_efiinfo);
|
|
}
|
|
|
|
/* Vendor device path used to indicate the mmio UART on AMD SoCs. */
|
|
#define AMDSOC_DEVPATH \
|
|
{ 0xe76fd4e9, 0x0a30, 0x4ca9, \
|
|
{ 0x95, 0x40, 0xd7, 0x99, 0x53, 0x4c, 0xc4, 0xff } }
|
|
|
|
void
|
|
efi_setconsdev(void)
|
|
{
|
|
bios_consdev_t cd;
|
|
EFI_STATUS status;
|
|
UINT8 data[128];
|
|
UINTN size = sizeof(data);
|
|
EFI_DEVICE_PATH *dp = (void *)data;
|
|
VENDOR_DEVICE_PATH *vdp;
|
|
UART_DEVICE_PATH *udp;
|
|
EFI_GUID global = EFI_GLOBAL_VARIABLE;
|
|
EFI_GUID amdsoc = AMDSOC_DEVPATH;
|
|
|
|
memset(&cd, 0, sizeof(cd));
|
|
cd.consdev = cn_tab->cn_dev;
|
|
cd.conspeed = com_speed;
|
|
cd.consaddr = com_addr;
|
|
|
|
/*
|
|
* If the ConOut variable indicates we're using a serial
|
|
* console, use it to determine the baud rate.
|
|
*/
|
|
status = RS->GetVariable(L"ConOut", &global, NULL, &size, &data);
|
|
if (status == EFI_SUCCESS) {
|
|
for (dp = (void *)data; !IsDevicePathEnd(dp);
|
|
dp = NextDevicePathNode(dp)) {
|
|
/*
|
|
* AMD Ryzen Embedded V1000 SoCs integrate a
|
|
* Synopsys DesignWare UART that is not
|
|
* compatible with the traditional 8250 UART
|
|
* found on the IBM PC. Pass the magic
|
|
* parameters to the kernel to make this UART
|
|
* work.
|
|
*/
|
|
if (DevicePathType(dp) == HARDWARE_DEVICE_PATH &&
|
|
DevicePathSubType(dp) == HW_VENDOR_DP) {
|
|
vdp = (VENDOR_DEVICE_PATH *)dp;
|
|
if (efi_guidcmp(&vdp->Guid, &amdsoc) == 0) {
|
|
cd.consdev = makedev(8, 4);
|
|
cd.consaddr = *(uint64_t *)(vdp + 1);
|
|
cd.consfreq = 48000000;
|
|
cd.flags = BCD_MMIO;
|
|
cd.reg_width = 4;
|
|
cd.reg_shift = 2;
|
|
}
|
|
}
|
|
|
|
if (DevicePathType(dp) == MESSAGING_DEVICE_PATH &&
|
|
DevicePathSubType(dp) == MSG_UART_DP) {
|
|
udp = (UART_DEVICE_PATH *)dp;
|
|
if (cd.conspeed == -1)
|
|
cd.conspeed = udp->BaudRate;
|
|
}
|
|
}
|
|
}
|
|
|
|
addbootarg(BOOTARG_CONSDEV, sizeof(cd), &cd);
|
|
}
|
|
|
|
void
|
|
_rtt(void)
|
|
{
|
|
#ifdef EFI_DEBUG
|
|
printf("Hit any key to reboot\n");
|
|
efi_cons_getc(0);
|
|
#endif
|
|
RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
|
|
for (;;)
|
|
continue;
|
|
}
|
|
|
|
time_t
|
|
getsecs(void)
|
|
{
|
|
EFI_TIME t;
|
|
time_t r = 0;
|
|
int y = 0;
|
|
const int daytab[][14] = {
|
|
{ 0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 },
|
|
{ 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
|
|
};
|
|
#define isleap(_y) (((_y) % 4) == 0 && (((_y) % 100) != 0 || ((_y) % 400) == 0))
|
|
|
|
ST->RuntimeServices->GetTime(&t, NULL);
|
|
|
|
/* Calc days from UNIX epoch */
|
|
r = (t.Year - 1970) * 365;
|
|
for (y = 1970; y < t.Year; y++) {
|
|
if (isleap(y))
|
|
r++;
|
|
}
|
|
r += daytab[isleap(t.Year)? 1 : 0][t.Month] + t.Day;
|
|
|
|
/* Calc secs */
|
|
r *= 60 * 60 * 24;
|
|
r += ((t.Hour * 60) + t.Minute) * 60 + t.Second;
|
|
if (-24 * 60 < t.TimeZone && t.TimeZone < 24 * 60)
|
|
r += t.TimeZone * 60;
|
|
|
|
return (r);
|
|
}
|
|
|
|
u_int
|
|
sleep(u_int i)
|
|
{
|
|
time_t t;
|
|
u_int intr = 0;
|
|
|
|
/*
|
|
* Loop for the requested number of seconds, polling,
|
|
* so that it may handle interrupts.
|
|
*/
|
|
for (t = getsecs() + i; intr == 0 && getsecs() < t; intr = cnischar())
|
|
;
|
|
|
|
return intr;
|
|
}
|
|
|
|
#ifdef IDLE_POWEROFF
|
|
CHAR16 *idle_name = L"IdlePoweroff";
|
|
EFI_STATUS idle_status;
|
|
/* randomly generated f948e8a9-0570-4338-ad10-29f4cf12849d */
|
|
EFI_GUID openbsd_guid = { 0xf948e8a9, 0x0570, 0x4338,
|
|
{ 0xad, 0x10, 0x29, 0xf4, 0xcf, 0x12, 0x84, 0x9d } };
|
|
/* Non-Volatile, Boot Service Access, Runtime Service Access */
|
|
UINT32 idle_attrs = 0x1 | 0x2 | 0x4;
|
|
UINT16 idle_secs;
|
|
UINTN idle_sz = sizeof(idle_secs);
|
|
|
|
int
|
|
get_idle_timeout(void)
|
|
{
|
|
idle_status = RS->GetVariable(idle_name, &openbsd_guid, NULL,
|
|
&idle_sz, &idle_secs);
|
|
if (idle_status != EFI_SUCCESS) {
|
|
if (idle_status != EFI_NOT_FOUND) {
|
|
printf("%s: %d\n", __func__, idle_status);
|
|
return 1;
|
|
}
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
set_idle_timeout(int secs)
|
|
{
|
|
idle_secs = secs;
|
|
idle_sz = idle_secs > 0 ? sizeof(idle_secs) : 0;
|
|
idle_status = RS->SetVariable(idle_name, &openbsd_guid, idle_attrs,
|
|
idle_sz, &idle_secs);
|
|
if (idle_status != EFI_SUCCESS) {
|
|
printf("%s: %d\n", __func__, idle_status);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* see lib/libsa/softraid.c sr_crypto_passphrase_decrypt() */
|
|
void
|
|
idle_poweroff(void)
|
|
{
|
|
if (get_idle_timeout() == 0 && sleep(idle_secs) == 0) {
|
|
printf("\nno input after %us, powering off...\n", idle_secs);
|
|
Xpoweroff_efi();
|
|
}
|
|
}
|
|
#endif /* IDLE_POWEROFF */
|
|
|
|
/***********************************************************************
|
|
* Commands
|
|
***********************************************************************/
|
|
int
|
|
Xexit_efi(void)
|
|
{
|
|
BS->Exit(IH, 0, 0, NULL);
|
|
for (;;)
|
|
continue;
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
Xvideo_efi(void)
|
|
{
|
|
int i, mode = -1;
|
|
|
|
if (cmd.argc >= 2) {
|
|
mode = strtol(cmd.argv[1], NULL, 10);
|
|
if (0 <= mode && mode < nitems(efi_video) &&
|
|
efi_video[mode].cols > 0) {
|
|
conout->SetMode(conout, mode);
|
|
efi_video_reset();
|
|
}
|
|
} else {
|
|
for (i = 0; i < nitems(efi_video) &&
|
|
i < conout->Mode->MaxMode; i++) {
|
|
if (efi_video[i].cols > 0)
|
|
printf("Mode %d: %d x %d\n", i,
|
|
efi_video[i].cols,
|
|
efi_video[i].rows);
|
|
}
|
|
printf("\n");
|
|
}
|
|
printf("Current Mode = %d\n", conout->Mode->Mode);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
Xpoweroff_efi(void)
|
|
{
|
|
RS->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
Xgop_efi(void)
|
|
{
|
|
EFI_STATUS status;
|
|
int i, mode = -1;
|
|
UINTN sz;
|
|
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
|
|
*gopi;
|
|
|
|
if (gop == NULL) {
|
|
printf("No GOP found\n");
|
|
return (0);
|
|
}
|
|
if (cmd.argc >= 2) {
|
|
mode = strtol(cmd.argv[1], NULL, 10);
|
|
if (0 <= mode && mode < gop->Mode->MaxMode) {
|
|
status = gop->QueryMode(gop, mode, &sz, &gopi);
|
|
if (!EFI_ERROR(status)) {
|
|
if (efi_gop_setmode(mode) == EFI_SUCCESS)
|
|
gopmode = mode;
|
|
}
|
|
}
|
|
} else {
|
|
for (i = 0; i < gop->Mode->MaxMode; i++) {
|
|
status = gop->QueryMode(gop, i, &sz, &gopi);
|
|
if (EFI_ERROR(status))
|
|
continue;
|
|
printf("Mode %d: %d x %d (stride = %d)\n", i,
|
|
gopi->HorizontalResolution,
|
|
gopi->VerticalResolution,
|
|
gopi->PixelsPerScanLine);
|
|
}
|
|
printf("\n");
|
|
}
|
|
printf("Current Mode = %d\n", gop->Mode->Mode);
|
|
|
|
return (0);
|
|
}
|
|
|
|
#ifdef IDLE_POWEROFF
|
|
int
|
|
Xidle_efi(void)
|
|
{
|
|
if (cmd.argc >= 2) {
|
|
int secs;
|
|
|
|
secs = strtol(cmd.argv[1], NULL, 10);
|
|
if (0 <= secs && secs < UINT16_MAX)
|
|
set_idle_timeout(secs);
|
|
} else {
|
|
if (get_idle_timeout() == 0)
|
|
printf("Timeout = %us\n", idle_secs);
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* IDLE_POWEROFF */
|