From 202de5ccfd00bc1ff1a39ae317df4f5e0eef69fd Mon Sep 17 00:00:00 2001 From: paravoid Date: Sat, 2 Jul 2022 02:36:58 +0300 Subject: [PATCH] screenshot: Initial files --- efigop/screenshot/cmd_i386.c | 162 ++++++ efigop/screenshot/efiboot.c | 1061 ++++++++++++++++++++++++++++++++++ efigop/screenshot/efiboot.h | 41 ++ 3 files changed, 1264 insertions(+) create mode 100644 efigop/screenshot/cmd_i386.c create mode 100644 efigop/screenshot/efiboot.c create mode 100644 efigop/screenshot/efiboot.h diff --git a/efigop/screenshot/cmd_i386.c b/efigop/screenshot/cmd_i386.c new file mode 100644 index 0000000..f79e2b1 --- /dev/null +++ b/efigop/screenshot/cmd_i386.c @@ -0,0 +1,162 @@ +/* $OpenBSD: cmd_i386.c,v 1.1 2019/05/10 21:20:42 mlarkin Exp $ */ + +/* + * Copyright (c) 1997-1999 Michael Shalayeff + * Copyright (c) 1997 Tobias Weingartner + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include "disk.h" +#include "biosdev.h" +#include "libsa.h" +#include + +#include "efiboot.h" +#include "efidev.h" + +extern const char version[]; + +int Xboot(void); +int Xcomaddr(void); +int Xdiskinfo(void); +int Xmemory(void); +int Xregs(void); + +/* From gidt.S */ +int bootbuf(void *, int); + +const struct cmd_table cmd_machine[] = { + { "comaddr", CMDT_CMD, Xcomaddr }, + { "diskinfo", CMDT_CMD, Xdiskinfo }, + { "memory", CMDT_CMD, Xmemory }, + { "video", CMDT_CMD, Xvideo_efi }, + { "gop", CMDT_CMD, Xgop_efi }, + { "exit", CMDT_CMD, Xexit_efi }, + { "poweroff", CMDT_CMD, Xpoweroff_efi }, +#ifdef DEBUG + { "regs", CMDT_CMD, Xregs }, +#endif + { NULL, 0 } +}; + +int +Xdiskinfo(void) +{ + efi_dump_diskinfo(); + return 0; +} + +#ifdef DEBUG +int +Xregs(void) +{ + DUMP_REGS; + return 0; +} +#endif + +int +Xmemory(void) +{ + if (cmd.argc >= 2) { + int i; + /* parse the memory specs */ + + for (i = 1; i < cmd.argc; i++) { + char *p; + long long addr, size; + + p = cmd.argv[i]; + + size = strtoll(p + 1, &p, 0); + /* Size the size */ + switch (*p) { + case 'G': + case 'g': + size *= 1024; + case 'M': + case 'm': + size *= 1024; + case 'K': + case 'k': + size *= 1024; + p++; + } + + /* Handle (possibly non-existent) address part */ + switch (*p) { + case '@': + addr = strtoll(p + 1, NULL, 0); + break; + + /* Adjust address if we don't need it */ + default: + if (cmd.argv[i][0] == '=') + addr = -1; + else + addr = 0; + } + + if (addr == 0 || size == 0) { + printf("bad language\n"); + return 0; + } else { + switch (cmd.argv[i][0]) { + case '-': + mem_delete(addr, addr + size); + break; + case '+': + mem_add(addr, addr + size); + break; + case '=': + mem_limit(size); + break; + default : + printf("bad OP\n"); + return 0; + } + } + } + } + + dump_biosmem(NULL); + + return 0; +} + +int +Xcomaddr(void) +{ + extern int com_addr; + + if (cmd.argc >= 2) + com_addr = (int)strtol(cmd.argv[1], NULL, 0); + + return 0; +} diff --git a/efigop/screenshot/efiboot.c b/efigop/screenshot/efiboot.c new file mode 100644 index 0000000..a8d8c76 --- /dev/null +++ b/efigop/screenshot/efiboot.c @@ -0,0 +1,1061 @@ +/* $OpenBSD: efiboot.c,v 1.38 2021/06/07 00:04:20 krw Exp $ */ + +/* + * Copyright (c) 2015 YASUOKA Masahiko + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libsa.h" +#include "disk.h" + +#include +#include +#include +#include + +#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_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; + + 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_GRAPHICS_OUTPUT *gop; +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; + } + + /* + * Frame buffer + */ + status = BS->LocateProtocol(&gop_guid, NULL, (void **)&gop); + if (!EFI_ERROR(status)) { + 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); +} + +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; + + /* + * Loop for the requested number of seconds, polling, + * so that it may handle interrupts. + */ + for (t = getsecs() + i; getsecs() < t; cnischar()) + ; + + return 0; +} + +/*********************************************************************** + * 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; + + status = BS->LocateProtocol(&gop_guid, NULL, (void **)&gop); + if (EFI_ERROR(status)) + 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); +} diff --git a/efigop/screenshot/efiboot.h b/efigop/screenshot/efiboot.h new file mode 100644 index 0000000..5f50525 --- /dev/null +++ b/efigop/screenshot/efiboot.h @@ -0,0 +1,41 @@ +/* $OpenBSD: efiboot.h,v 1.4 2017/11/25 19:02:07 patrick Exp $ */ + +/* + * Copyright (c) 2015 YASUOKA Masahiko + * + * 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. + */ + +void efi_cleanup(void); +void efi_cons_probe(struct consdev *); +void efi_memprobe(void); +void efi_hardprobe(void); +void efi_diskprobe(void); +void efi_pxeprobe(void); +void efi_cons_init(struct consdev *); +int efi_cons_getc(dev_t); +void efi_cons_putc(dev_t, int); +int efi_cons_getshifts(dev_t dev); +void efi_com_probe(struct consdev *); +void efi_com_init(struct consdev *); +int efi_com_getc(dev_t); +void efi_com_putc(dev_t, int); +int Xvideo_efi(void); +int Xgop_efi(void); +int Xexit_efi(void); +void efi_makebootargs(void); + +int Xpoweroff_efi(void); + +extern void (*run_i386)(u_long, u_long, int, int, int, int, int, int, int, int) + __attribute__ ((noreturn)); -- 2.45.2