diff --git a/stand/efi/libefi/devpath.c b/stand/efi/libefi/devpath.c index 85a5459c7d6b..db152f8eb9ad 100644 --- a/stand/efi/libefi/devpath.c +++ b/stand/efi/libefi/devpath.c @@ -1,766 +1,766 @@ /*- * Copyright (c) 2016 John Baldwin * * 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 AND CONTRIBUTORS ``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 AUTHOR 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 static EFI_GUID ImageDevicePathGUID = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID; static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; static EFI_GUID DevicePathToTextGUID = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID; static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *toTextProtocol; static EFI_GUID DevicePathFromTextGUID = EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID; static EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *fromTextProtocol; EFI_DEVICE_PATH * efi_lookup_image_devpath(EFI_HANDLE handle) { EFI_DEVICE_PATH *devpath; EFI_STATUS status; status = OpenProtocolByHandle(handle, &ImageDevicePathGUID, (void **)&devpath); if (EFI_ERROR(status)) devpath = NULL; return (devpath); } EFI_DEVICE_PATH * efi_lookup_devpath(EFI_HANDLE handle) { EFI_DEVICE_PATH *devpath; EFI_STATUS status; status = OpenProtocolByHandle(handle, &DevicePathGUID, (void **)&devpath); if (EFI_ERROR(status)) devpath = NULL; return (devpath); } void efi_close_devpath(EFI_HANDLE handle) { EFI_STATUS status; status = BS->CloseProtocol(handle, &DevicePathGUID, IH, NULL); if (EFI_ERROR(status)) printf("CloseProtocol error: %lu\n", EFI_ERROR_CODE(status)); } static char * efi_make_tail(char *suffix) { char *tail; tail = NULL; if (suffix != NULL) (void)asprintf(&tail, "/%s", suffix); else tail = strdup(""); return (tail); } typedef struct { EFI_DEVICE_PATH Header; EFI_GUID Guid; UINT8 VendorDefinedData[1]; } __packed VENDOR_DEVICE_PATH_WITH_DATA; static char * efi_vendor_path(const char *type, VENDOR_DEVICE_PATH *node, char *suffix) { uint32_t size = DevicePathNodeLength(&node->Header) - sizeof(*node); VENDOR_DEVICE_PATH_WITH_DATA *dp = (VENDOR_DEVICE_PATH_WITH_DATA *)node; char *name, *tail, *head; char *uuid; int rv; uuid_to_string((const uuid_t *)(void *)&node->Guid, &uuid, &rv); if (rv != uuid_s_ok) return (NULL); tail = efi_make_tail(suffix); rv = asprintf(&head, "%sVendor(%s)[%x:", type, uuid, size); free(uuid); if (rv < 0) return (NULL); if (DevicePathNodeLength(&node->Header) > sizeof(*node)) { for (uint32_t i = 0; i < size; i++) { rv = asprintf(&name, "%s%02x", head, dp->VendorDefinedData[i]); if (rv < 0) { free(tail); free(head); return (NULL); } free(head); head = name; } } if (asprintf(&name, "%s]%s", head, tail) < 0) name = NULL; free(head); free(tail); return (name); } static char * efi_hw_dev_path(EFI_DEVICE_PATH *node, char *suffix) { uint8_t subtype = DevicePathSubType(node); char *name, *tail; tail = efi_make_tail(suffix); switch (subtype) { case HW_PCI_DP: if (asprintf(&name, "Pci(%x,%x)%s", ((PCI_DEVICE_PATH *)node)->Device, ((PCI_DEVICE_PATH *)node)->Function, tail) < 0) name = NULL; break; case HW_PCCARD_DP: if (asprintf(&name, "PCCARD(%x)%s", ((PCCARD_DEVICE_PATH *)node)->FunctionNumber, tail) < 0) name = NULL; break; case HW_MEMMAP_DP: if (asprintf(&name, "MMap(%x,%" PRIx64 ",%" PRIx64 ")%s", ((MEMMAP_DEVICE_PATH *)node)->MemoryType, ((MEMMAP_DEVICE_PATH *)node)->StartingAddress, ((MEMMAP_DEVICE_PATH *)node)->EndingAddress, tail) < 0) name = NULL; break; case HW_VENDOR_DP: name = efi_vendor_path("Hardware", (VENDOR_DEVICE_PATH *)node, tail); break; case HW_CONTROLLER_DP: if (asprintf(&name, "Ctrl(%x)%s", ((CONTROLLER_DEVICE_PATH *)node)->Controller, tail) < 0) name = NULL; break; default: if (asprintf(&name, "UnknownHW(%x)%s", subtype, tail) < 0) name = NULL; break; } free(tail); return (name); } static char * efi_acpi_dev_path(EFI_DEVICE_PATH *node, char *suffix) { uint8_t subtype = DevicePathSubType(node); ACPI_HID_DEVICE_PATH *acpi = (ACPI_HID_DEVICE_PATH *)node; char *name, *tail; tail = efi_make_tail(suffix); switch (subtype) { case ACPI_DP: if ((acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST) { switch (EISA_ID_TO_NUM (acpi->HID)) { case 0x0a03: if (asprintf(&name, "PciRoot(%x)%s", acpi->UID, tail) < 0) name = NULL; break; case 0x0a08: if (asprintf(&name, "PcieRoot(%x)%s", acpi->UID, tail) < 0) name = NULL; break; case 0x0604: if (asprintf(&name, "Floppy(%x)%s", acpi->UID, tail) < 0) name = NULL; break; case 0x0301: if (asprintf(&name, "Keyboard(%x)%s", acpi->UID, tail) < 0) name = NULL; break; case 0x0501: if (asprintf(&name, "Serial(%x)%s", acpi->UID, tail) < 0) name = NULL; break; case 0x0401: if (asprintf(&name, "ParallelPort(%x)%s", acpi->UID, tail) < 0) name = NULL; break; default: if (asprintf(&name, "Acpi(PNP%04x,%x)%s", EISA_ID_TO_NUM(acpi->HID), acpi->UID, tail) < 0) name = NULL; break; } } else { if (asprintf(&name, "Acpi(%08x,%x)%s", acpi->HID, acpi->UID, tail) < 0) name = NULL; } break; case ACPI_EXTENDED_DP: default: if (asprintf(&name, "UnknownACPI(%x)%s", subtype, tail) < 0) name = NULL; break; } free(tail); return (name); } static char * efi_messaging_dev_path(EFI_DEVICE_PATH *node, char *suffix) { uint8_t subtype = DevicePathSubType(node); char *name; char *tail; tail = efi_make_tail(suffix); switch (subtype) { case MSG_ATAPI_DP: if (asprintf(&name, "ATA(%s,%s,%x)%s", ((ATAPI_DEVICE_PATH *)node)->PrimarySecondary == 1 ? "Secondary" : "Primary", ((ATAPI_DEVICE_PATH *)node)->SlaveMaster == 1 ? "Slave" : "Master", ((ATAPI_DEVICE_PATH *)node)->Lun, tail) < 0) name = NULL; break; case MSG_SCSI_DP: if (asprintf(&name, "SCSI(%x,%x)%s", ((SCSI_DEVICE_PATH *)node)->Pun, ((SCSI_DEVICE_PATH *)node)->Lun, tail) < 0) name = NULL; break; case MSG_FIBRECHANNEL_DP: if (asprintf(&name, "Fibre(%" PRIx64 ",%" PRIx64 ")%s", ((FIBRECHANNEL_DEVICE_PATH *)node)->WWN, ((FIBRECHANNEL_DEVICE_PATH *)node)->Lun, tail) < 0) name = NULL; break; case MSG_1394_DP: if (asprintf(&name, "I1394(%016" PRIx64 ")%s", ((F1394_DEVICE_PATH *)node)->Guid, tail) < 0) name = NULL; break; case MSG_USB_DP: if (asprintf(&name, "USB(%x,%x)%s", ((USB_DEVICE_PATH *)node)->ParentPortNumber, ((USB_DEVICE_PATH *)node)->InterfaceNumber, tail) < 0) name = NULL; break; case MSG_USB_CLASS_DP: if (asprintf(&name, "UsbClass(%x,%x,%x,%x,%x)%s", ((USB_CLASS_DEVICE_PATH *)node)->VendorId, ((USB_CLASS_DEVICE_PATH *)node)->ProductId, ((USB_CLASS_DEVICE_PATH *)node)->DeviceClass, ((USB_CLASS_DEVICE_PATH *)node)->DeviceSubClass, ((USB_CLASS_DEVICE_PATH *)node)->DeviceProtocol, tail) < 0) name = NULL; break; case MSG_MAC_ADDR_DP: if (asprintf(&name, "MAC(%02x:%02x:%02x:%02x:%02x:%02x,%x)%s", ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[0], ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[1], ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[2], ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[3], ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[4], ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[5], ((MAC_ADDR_DEVICE_PATH *)node)->IfType, tail) < 0) name = NULL; break; case MSG_VENDOR_DP: name = efi_vendor_path("Messaging", (VENDOR_DEVICE_PATH *)node, tail); break; case MSG_UART_DP: if (asprintf(&name, "UART(%" PRIu64 ",%u,%x,%x)%s", ((UART_DEVICE_PATH *)node)->BaudRate, ((UART_DEVICE_PATH *)node)->DataBits, ((UART_DEVICE_PATH *)node)->Parity, ((UART_DEVICE_PATH *)node)->StopBits, tail) < 0) name = NULL; break; case MSG_SATA_DP: if (asprintf(&name, "Sata(%x,%x,%x)%s", ((SATA_DEVICE_PATH *)node)->HBAPortNumber, ((SATA_DEVICE_PATH *)node)->PortMultiplierPortNumber, ((SATA_DEVICE_PATH *)node)->Lun, tail) < 0) name = NULL; break; default: if (asprintf(&name, "UnknownMessaging(%x)%s", subtype, tail) < 0) name = NULL; break; } free(tail); return (name); } static char * efi_media_dev_path(EFI_DEVICE_PATH *node, char *suffix) { uint8_t subtype = DevicePathSubType(node); HARDDRIVE_DEVICE_PATH *hd; char *name; char *str; char *tail; int rv; tail = efi_make_tail(suffix); name = NULL; switch (subtype) { case MEDIA_HARDDRIVE_DP: hd = (HARDDRIVE_DEVICE_PATH *)node; switch (hd->SignatureType) { case SIGNATURE_TYPE_MBR: if (asprintf(&name, "HD(%d,MBR,%08x,%" PRIx64 ",%" PRIx64 ")%s", hd->PartitionNumber, *((uint32_t *)(uintptr_t)&hd->Signature[0]), hd->PartitionStart, hd->PartitionSize, tail) < 0) name = NULL; break; case SIGNATURE_TYPE_GUID: name = NULL; uuid_to_string((const uuid_t *)(void *) &hd->Signature[0], &str, &rv); if (rv != uuid_s_ok) break; rv = asprintf(&name, "HD(%d,GPT,%s,%" PRIx64 ",%" PRIx64 ")%s", hd->PartitionNumber, str, hd->PartitionStart, hd->PartitionSize, tail); free(str); break; default: if (asprintf(&name, "HD(%d,%d,0)%s", hd->PartitionNumber, hd->SignatureType, tail) < 0) { name = NULL; } break; } break; case MEDIA_CDROM_DP: if (asprintf(&name, "CD(%x,%" PRIx64 ",%" PRIx64 ")%s", ((CDROM_DEVICE_PATH *)node)->BootEntry, ((CDROM_DEVICE_PATH *)node)->PartitionStart, ((CDROM_DEVICE_PATH *)node)->PartitionSize, tail) < 0) { name = NULL; } break; case MEDIA_VENDOR_DP: name = efi_vendor_path("Media", (VENDOR_DEVICE_PATH *)node, tail); break; case MEDIA_FILEPATH_DP: name = NULL; str = NULL; if (ucs2_to_utf8(((FILEPATH_DEVICE_PATH *)node)->PathName, &str) == 0) { (void)asprintf(&name, "%s%s", str, tail); free(str); } break; case MEDIA_PROTOCOL_DP: name = NULL; uuid_to_string((const uuid_t *)(void *) &((MEDIA_PROTOCOL_DEVICE_PATH *)node)->Protocol, &str, &rv); if (rv != uuid_s_ok) break; rv = asprintf(&name, "Protocol(%s)%s", str, tail); free(str); break; default: if (asprintf(&name, "UnknownMedia(%x)%s", subtype, tail) < 0) name = NULL; } free(tail); return (name); } static char * efi_translate_devpath(EFI_DEVICE_PATH *devpath) { EFI_DEVICE_PATH *dp = NextDevicePathNode(devpath); char *name, *ptr; uint8_t type; if (!IsDevicePathEnd(devpath)) name = efi_translate_devpath(dp); else return (NULL); ptr = NULL; type = DevicePathType(devpath); switch (type) { case HARDWARE_DEVICE_PATH: ptr = efi_hw_dev_path(devpath, name); break; case ACPI_DEVICE_PATH: ptr = efi_acpi_dev_path(devpath, name); break; case MESSAGING_DEVICE_PATH: ptr = efi_messaging_dev_path(devpath, name); break; case MEDIA_DEVICE_PATH: ptr = efi_media_dev_path(devpath, name); break; case BBS_DEVICE_PATH: default: if (asprintf(&ptr, "UnknownPath(%x)%s", type, name? name : "") < 0) ptr = NULL; break; } if (ptr != NULL) { free(name); name = ptr; } return (name); } static CHAR16 * efi_devpath_to_name(EFI_DEVICE_PATH *devpath) { char *name = NULL; CHAR16 *ptr = NULL; size_t len; int rv; name = efi_translate_devpath(devpath); if (name == NULL) return (NULL); /* * We need to return memory from AllocatePool, so it can be freed * with FreePool() in efi_free_devpath_name(). */ rv = utf8_to_ucs2(name, &ptr, &len); free(name); if (rv == 0) { CHAR16 *out = NULL; EFI_STATUS status; status = BS->AllocatePool(EfiLoaderData, len, (void **)&out); if (EFI_ERROR(status)) { free(ptr); return (out); } memcpy(out, ptr, len); free(ptr); ptr = out; } - + return (ptr); } CHAR16 * efi_devpath_name(EFI_DEVICE_PATH *devpath) { EFI_STATUS status; if (devpath == NULL) return (NULL); if (toTextProtocol == NULL) { status = BS->LocateProtocol(&DevicePathToTextGUID, NULL, (VOID **)&toTextProtocol); if (EFI_ERROR(status)) toTextProtocol = NULL; } if (toTextProtocol == NULL) return (efi_devpath_to_name(devpath)); return (toTextProtocol->ConvertDevicePathToText(devpath, TRUE, TRUE)); } void efi_free_devpath_name(CHAR16 *text) { if (text != NULL) BS->FreePool(text); } EFI_DEVICE_PATH * efi_name_to_devpath(const char *path) { EFI_DEVICE_PATH *devpath; CHAR16 *uv; size_t ul; uv = NULL; if (utf8_to_ucs2(path, &uv, &ul) != 0) return (NULL); devpath = efi_name_to_devpath16(uv); free(uv); return (devpath); } EFI_DEVICE_PATH * efi_name_to_devpath16(CHAR16 *path) { EFI_STATUS status; if (path == NULL) return (NULL); if (fromTextProtocol == NULL) { status = BS->LocateProtocol(&DevicePathFromTextGUID, NULL, (VOID **)&fromTextProtocol); if (EFI_ERROR(status)) fromTextProtocol = NULL; } if (fromTextProtocol == NULL) return (NULL); return (fromTextProtocol->ConvertTextToDevicePath(path)); } void efi_devpath_free(EFI_DEVICE_PATH *devpath) { BS->FreePool(devpath); } EFI_DEVICE_PATH * efi_devpath_last_node(EFI_DEVICE_PATH *devpath) { if (IsDevicePathEnd(devpath)) return (NULL); while (!IsDevicePathEnd(NextDevicePathNode(devpath))) devpath = NextDevicePathNode(devpath); return (devpath); } /* * Walk device path nodes, return next instance or end node. */ EFI_DEVICE_PATH * efi_devpath_next_instance(EFI_DEVICE_PATH *devpath) { while (!IsDevicePathEnd(devpath)) { devpath = NextDevicePathNode(devpath); if (IsDevicePathEndType(devpath) && devpath->SubType == END_INSTANCE_DEVICE_PATH_SUBTYPE) { devpath = NextDevicePathNode(devpath); break; } } return (devpath); } EFI_DEVICE_PATH * efi_devpath_trim(EFI_DEVICE_PATH *devpath) { EFI_DEVICE_PATH *node, *copy; size_t prefix, len; if ((node = efi_devpath_last_node(devpath)) == NULL) return (NULL); prefix = (UINT8 *)node - (UINT8 *)devpath; if (prefix == 0) return (NULL); len = prefix + DevicePathNodeLength(NextDevicePathNode(node)); copy = malloc(len); if (copy != NULL) { memcpy(copy, devpath, prefix); node = (EFI_DEVICE_PATH *)((UINT8 *)copy + prefix); SetDevicePathEndNode(node); } return (copy); } EFI_HANDLE efi_devpath_handle(EFI_DEVICE_PATH *devpath) { EFI_STATUS status; EFI_HANDLE h; /* * There isn't a standard way to locate a handle for a given * device path. However, querying the EFI_DEVICE_PATH protocol * for a given device path should give us a handle for the * closest node in the path to the end that is valid. */ status = BS->LocateDevicePath(&DevicePathGUID, &devpath, &h); if (EFI_ERROR(status)) return (NULL); return (h); } bool efi_devpath_match_node(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) { size_t len; if (devpath1 == NULL || devpath2 == NULL) return (false); if (DevicePathType(devpath1) != DevicePathType(devpath2) || DevicePathSubType(devpath1) != DevicePathSubType(devpath2)) return (false); len = DevicePathNodeLength(devpath1); if (len != DevicePathNodeLength(devpath2)) return (false); if (memcmp(devpath1, devpath2, len) != 0) return (false); return (true); } static bool _efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2, bool ignore_media) { if (devpath1 == NULL || devpath2 == NULL) return (false); while (true) { if (ignore_media && IsDevicePathType(devpath1, MEDIA_DEVICE_PATH) && IsDevicePathType(devpath2, MEDIA_DEVICE_PATH)) return (true); if (!efi_devpath_match_node(devpath1, devpath2)) return false; if (IsDevicePathEnd(devpath1)) break; devpath1 = NextDevicePathNode(devpath1); devpath2 = NextDevicePathNode(devpath2); } return (true); } /* * Are two devpaths identical? */ bool efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) { return _efi_devpath_match(devpath1, devpath2, false); } /* * Like efi_devpath_match, but stops at when we hit the media device * path node that specifies the partition information. If we match * up to that point, then we're on the same disk. */ bool efi_devpath_same_disk(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) { return _efi_devpath_match(devpath1, devpath2, true); } bool efi_devpath_is_prefix(EFI_DEVICE_PATH *prefix, EFI_DEVICE_PATH *path) { size_t len; if (prefix == NULL || path == NULL) return (false); while (1) { if (IsDevicePathEnd(prefix)) break; if (DevicePathType(prefix) != DevicePathType(path) || DevicePathSubType(prefix) != DevicePathSubType(path)) return (false); len = DevicePathNodeLength(prefix); if (len != DevicePathNodeLength(path)) return (false); if (memcmp(prefix, path, len) != 0) return (false); prefix = NextDevicePathNode(prefix); path = NextDevicePathNode(path); } return (true); } /* * Skip over the 'prefix' part of path and return the part of the path * that starts with the first node that's a MEDIA_DEVICE_PATH. */ EFI_DEVICE_PATH * efi_devpath_to_media_path(EFI_DEVICE_PATH *path) { while (!IsDevicePathEnd(path)) { if (DevicePathType(path) == MEDIA_DEVICE_PATH) return (path); path = NextDevicePathNode(path); } return (NULL); } UINTN efi_devpath_length(EFI_DEVICE_PATH *path) { EFI_DEVICE_PATH *start = path; while (!IsDevicePathEnd(path)) path = NextDevicePathNode(path); return ((UINTN)path - (UINTN)start) + DevicePathNodeLength(path); } EFI_HANDLE efi_devpath_to_handle(EFI_DEVICE_PATH *path, EFI_HANDLE *handles, unsigned nhandles) { unsigned i; EFI_DEVICE_PATH *media, *devpath; EFI_HANDLE h; media = efi_devpath_to_media_path(path); if (media == NULL) return (NULL); for (i = 0; i < nhandles; i++) { h = handles[i]; devpath = efi_lookup_devpath(h); if (devpath == NULL) continue; if (!efi_devpath_match_node(media, efi_devpath_to_media_path(devpath))) continue; return (h); } return (NULL); } diff --git a/stand/efi/libefi/efi_console.c b/stand/efi/libefi/efi_console.c index 5813e2d20aef..cbb4dd01d1fb 100644 --- a/stand/efi/libefi/efi_console.c +++ b/stand/efi/libefi/efi_console.c @@ -1,1427 +1,1427 @@ /*- * Copyright (c) 2000 Doug Rabson * 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 AND CONTRIBUTORS ``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 AUTHOR 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 #include #include #include #include "bootstrap.h" extern EFI_GUID gop_guid; bool boot_services_active = true; /* boot services active first thing in main */ static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; static SIMPLE_INPUT_INTERFACE *conin; static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex; static bool efi_started; static int mode; /* Does ConOut have serial console? */ static uint32_t utf8_left; static uint32_t utf8_partial; #ifdef TERM_EMU #define DEFAULT_FGCOLOR EFI_LIGHTGRAY #define DEFAULT_BGCOLOR EFI_BLACK #define MAXARGS 8 static int args[MAXARGS], argc; static int fg_c, bg_c, curx, cury; static int esc; void get_pos(int *x, int *y); void curs_move(int *_x, int *_y, int x, int y); static void CL(int); void HO(void); void end_term(void); #endif #define TEXT_ROWS 25 #define TEXT_COLS 80 static tf_bell_t efi_cons_bell; static tf_cursor_t efi_text_cursor; static tf_putchar_t efi_text_putchar; static tf_fill_t efi_text_fill; static tf_copy_t efi_text_copy; static tf_param_t efi_text_param; static tf_respond_t efi_cons_respond; static teken_funcs_t tf = { .tf_bell = efi_cons_bell, .tf_cursor = efi_text_cursor, .tf_putchar = efi_text_putchar, .tf_fill = efi_text_fill, .tf_copy = efi_text_copy, .tf_param = efi_text_param, .tf_respond = efi_cons_respond, }; static teken_funcs_t tfx = { .tf_bell = efi_cons_bell, .tf_cursor = gfx_fb_cursor, .tf_putchar = gfx_fb_putchar, .tf_fill = gfx_fb_fill, .tf_copy = gfx_fb_copy, .tf_param = gfx_fb_param, .tf_respond = efi_cons_respond, }; #define KEYBUFSZ 10 static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */ static int key_pending; static const unsigned char teken_color_to_efi_color[16] = { EFI_BLACK, EFI_RED, EFI_GREEN, EFI_BROWN, EFI_BLUE, EFI_MAGENTA, EFI_CYAN, EFI_LIGHTGRAY, EFI_DARKGRAY, EFI_LIGHTRED, EFI_LIGHTGREEN, EFI_YELLOW, EFI_LIGHTBLUE, EFI_LIGHTMAGENTA, EFI_LIGHTCYAN, EFI_WHITE }; static void efi_cons_probe(struct console *); static int efi_cons_init(int); void efi_cons_putchar(int); int efi_cons_getchar(void); void efi_cons_efiputchar(int); int efi_cons_poll(void); static void cons_draw_frame(teken_attr_t *); struct console efi_console = { .c_name = "efi", .c_desc = "EFI console", .c_flags = C_WIDEOUT, .c_probe = efi_cons_probe, .c_init = efi_cons_init, .c_out = efi_cons_putchar, .c_in = efi_cons_getchar, .c_ready = efi_cons_poll }; /* * This function is used to mark a rectangular image area so the scrolling * will know we need to copy the data from there. */ void term_image_display(teken_gfx_t *state, const teken_rect_t *r) { teken_pos_t p; int idx; if (screen_buffer == NULL) return; for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row; p.tp_row++) { for (p.tp_col = r->tr_begin.tp_col; p.tp_col < r->tr_end.tp_col; p.tp_col++) { idx = p.tp_col + p.tp_row * state->tg_tp.tp_col; if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row) return; screen_buffer[idx].a.ta_format |= TF_IMAGE; } } } /* * Not implemented. */ static void efi_cons_bell(void *s __unused) { } static void efi_text_cursor(void *arg, const teken_pos_t *p) { teken_gfx_t *state = arg; UINTN col, row; if (!boot_services_active) return; row = p->tp_row; if (p->tp_row >= state->tg_tp.tp_row) row = state->tg_tp.tp_row - 1; col = p->tp_col; if (p->tp_col >= state->tg_tp.tp_col) col = state->tg_tp.tp_col - 1; conout->SetCursorPosition(conout, col, row); } static void efi_text_printchar(teken_gfx_t *state, const teken_pos_t *p, bool autoscroll) { UINTN a, attr; struct text_pixel *px; teken_color_t fg, bg, tmp; px = screen_buffer + p->tp_col + p->tp_row * state->tg_tp.tp_col; a = conout->Mode->Attribute; fg = teken_256to16(px->a.ta_fgcolor); bg = teken_256to16(px->a.ta_bgcolor); if (px->a.ta_format & TF_BOLD) fg |= TC_LIGHT; if (px->a.ta_format & TF_BLINK) bg |= TC_LIGHT; if (px->a.ta_format & TF_REVERSE) { tmp = fg; fg = bg; bg = tmp; } attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg], teken_color_to_efi_color[bg] & 0x7); conout->SetCursorPosition(conout, p->tp_col, p->tp_row); /* to prevent autoscroll, skip print of lower right char */ if (!autoscroll && p->tp_row == state->tg_tp.tp_row - 1 && p->tp_col == state->tg_tp.tp_col - 1) return; (void) conout->SetAttribute(conout, attr); efi_cons_efiputchar(px->c); (void) conout->SetAttribute(conout, a); } static void efi_text_putchar(void *s, const teken_pos_t *p, teken_char_t c, const teken_attr_t *a) { teken_gfx_t *state = s; EFI_STATUS status; int idx; if (!boot_services_active) return; idx = p->tp_col + p->tp_row * state->tg_tp.tp_col; if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row) return; screen_buffer[idx].c = c; screen_buffer[idx].a = *a; efi_text_printchar(s, p, false); } static void efi_text_fill(void *arg, const teken_rect_t *r, teken_char_t c, const teken_attr_t *a) { teken_gfx_t *state = arg; teken_pos_t p; if (!boot_services_active) return; if (state->tg_cursor_visible) conout->EnableCursor(conout, FALSE); for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row; p.tp_row++) for (p.tp_col = r->tr_begin.tp_col; p.tp_col < r->tr_end.tp_col; p.tp_col++) efi_text_putchar(state, &p, c, a); if (state->tg_cursor_visible) conout->EnableCursor(conout, TRUE); } static void efi_text_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, teken_pos_t *d, bool scroll) { unsigned soffset, doffset; teken_pos_t sp, dp; int x; soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col; doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col; sp = *s; dp = *d; for (x = 0; x < ncol; x++) { sp.tp_col = s->tp_col + x; dp.tp_col = d->tp_col + x; if (!is_same_pixel(&screen_buffer[soffset + x], &screen_buffer[doffset + x])) { screen_buffer[doffset + x] = screen_buffer[soffset + x]; if (!scroll) efi_text_printchar(state, &dp, false); } else if (scroll) { /* Draw last char and trigger scroll. */ if (dp.tp_col + 1 == state->tg_tp.tp_col && dp.tp_row + 1 == state->tg_tp.tp_row) { efi_text_printchar(state, &dp, true); } } } } static void efi_text_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p) { teken_gfx_t *state = arg; unsigned doffset, soffset; teken_pos_t d, s; int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */ bool scroll = false; if (!boot_services_active) return; /* * Copying is a little tricky. We must make sure we do it in * correct order, to make sure we don't overwrite our own data. */ nrow = r->tr_end.tp_row - r->tr_begin.tp_row; ncol = r->tr_end.tp_col - r->tr_begin.tp_col; /* * Check if we do copy whole screen. */ if (p->tp_row == 0 && p->tp_col == 0 && nrow == state->tg_tp.tp_row - 2 && ncol == state->tg_tp.tp_col - 2) scroll = true; soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col; doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col; /* remove the cursor */ if (state->tg_cursor_visible) conout->EnableCursor(conout, FALSE); /* * Copy line by line. */ if (doffset <= soffset) { s = r->tr_begin; d = *p; for (y = 0; y < nrow; y++) { s.tp_row = r->tr_begin.tp_row + y; d.tp_row = p->tp_row + y; efi_text_copy_line(state, ncol, &s, &d, scroll); } } else { for (y = nrow - 1; y >= 0; y--) { s.tp_row = r->tr_begin.tp_row + y; d.tp_row = p->tp_row + y; efi_text_copy_line(state, ncol, &s, &d, false); } } /* display the cursor */ if (state->tg_cursor_visible) conout->EnableCursor(conout, TRUE); } static void efi_text_param(void *arg, int cmd, unsigned int value) { teken_gfx_t *state = arg; if (!boot_services_active) return; switch (cmd) { case TP_SETLOCALCURSOR: /* * 0 means normal (usually block), 1 means hidden, and * 2 means blinking (always block) for compatibility with * syscons. We don't support any changes except hiding, * so must map 2 to 0. */ value = (value == 1) ? 0 : 1; /* FALLTHROUGH */ case TP_SHOWCURSOR: if (value != 0) { conout->EnableCursor(conout, TRUE); state->tg_cursor_visible = true; } else { conout->EnableCursor(conout, FALSE); state->tg_cursor_visible = false; } break; default: /* Not yet implemented */ break; } } /* * Not implemented. */ static void efi_cons_respond(void *s __unused, const void *buf __unused, size_t len __unused) { } /* * Set up conin/conout/coninex to make sure we have input ready. */ static void efi_cons_probe(struct console *cp) { EFI_STATUS status; conout = ST->ConOut; conin = ST->ConIn; /* * Call SetMode to work around buggy firmware. */ status = conout->SetMode(conout, conout->Mode->Mode); if (coninex == NULL) { status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid, (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (status != EFI_SUCCESS) coninex = NULL; } cp->c_flags |= C_PRESENTIN | C_PRESENTOUT; } static bool color_name_to_teken(const char *name, int *val) { int light = 0; if (strncasecmp(name, "light", 5) == 0) { name += 5; light = TC_LIGHT; } else if (strncasecmp(name, "bright", 6) == 0) { name += 6; light = TC_LIGHT; } if (strcasecmp(name, "black") == 0) { *val = TC_BLACK | light; return (true); } if (strcasecmp(name, "red") == 0) { *val = TC_RED | light; return (true); } if (strcasecmp(name, "green") == 0) { *val = TC_GREEN | light; return (true); } if (strcasecmp(name, "yellow") == 0 || strcasecmp(name, "brown") == 0) { *val = TC_YELLOW | light; return (true); } if (strcasecmp(name, "blue") == 0) { *val = TC_BLUE | light; return (true); } if (strcasecmp(name, "magenta") == 0) { *val = TC_MAGENTA | light; return (true); } if (strcasecmp(name, "cyan") == 0) { *val = TC_CYAN | light; return (true); } if (strcasecmp(name, "white") == 0) { *val = TC_WHITE | light; return (true); } return (false); } static int efi_set_colors(struct env_var *ev, int flags, const void *value) { int val = 0; char buf[3]; const void *evalue; const teken_attr_t *ap; teken_attr_t a; if (value == NULL) return (CMD_OK); if (color_name_to_teken(value, &val)) { snprintf(buf, sizeof (buf), "%d", val); evalue = buf; } else { char *end; long lval; errno = 0; lval = strtol(value, &end, 0); if (errno != 0 || *end != '\0' || lval < 0 || lval > 15) { printf("Allowed values are either ansi color name or " "number from range [0-15].\n"); return (CMD_OK); } val = (int)lval; evalue = value; } ap = teken_get_defattr(&gfx_state.tg_teken); a = *ap; if (strcmp(ev->ev_name, "teken.fg_color") == 0) { /* is it already set? */ if (ap->ta_fgcolor == val) return (CMD_OK); a.ta_fgcolor = val; } if (strcmp(ev->ev_name, "teken.bg_color") == 0) { /* is it already set? */ if (ap->ta_bgcolor == val) return (CMD_OK); a.ta_bgcolor = val; } /* Improve visibility */ if (a.ta_bgcolor == TC_WHITE) a.ta_bgcolor |= TC_LIGHT; teken_set_defattr(&gfx_state.tg_teken, &a); cons_draw_frame(&a); env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL); teken_input(&gfx_state.tg_teken, "\e[2J", 4); return (CMD_OK); } #ifdef TERM_EMU /* Get cursor position. */ void get_pos(int *x, int *y) { *x = conout->Mode->CursorColumn; *y = conout->Mode->CursorRow; } /* Move cursor to x rows and y cols (0-based). */ void curs_move(int *_x, int *_y, int x, int y) { conout->SetCursorPosition(conout, x, y); if (_x != NULL) *_x = conout->Mode->CursorColumn; if (_y != NULL) *_y = conout->Mode->CursorRow; } - + /* Clear internal state of the terminal emulation code. */ void end_term(void) { esc = 0; argc = -1; } #endif static void efi_cons_rawputchar(int c) { int i; UINTN x, y; conout->QueryMode(conout, conout->Mode->Mode, &x, &y); if (c == '\t') { int n; n = 8 - ((conout->Mode->CursorColumn + 8) % 8); for (i = 0; i < n; i++) efi_cons_rawputchar(' '); } else { #ifndef TERM_EMU if (c == '\n') efi_cons_efiputchar('\r'); efi_cons_efiputchar(c); #else switch (c) { case '\r': curx = 0; efi_cons_efiputchar('\r'); return; case '\n': efi_cons_efiputchar('\n'); efi_cons_efiputchar('\r'); cury++; if (cury >= y) cury--; curx = 0; return; case '\b': if (curx > 0) { efi_cons_efiputchar('\b'); curx--; } return; default: efi_cons_efiputchar(c); curx++; if (curx > x-1) { curx = 0; cury++; } if (cury > y-1) { curx = 0; cury--; } } #endif } conout->EnableCursor(conout, TRUE); } #ifdef TERM_EMU /* Gracefully exit ESC-sequence processing in case of misunderstanding. */ static void bail_out(int c) { char buf[16], *ch; int i; if (esc) { efi_cons_rawputchar('\033'); if (esc != '\033') efi_cons_rawputchar(esc); for (i = 0; i <= argc; ++i) { sprintf(buf, "%d", args[i]); ch = buf; while (*ch) efi_cons_rawputchar(*ch++); } } efi_cons_rawputchar(c); end_term(); } /* Clear display from current position to end of screen. */ static void CD(void) { int i; UINTN x, y; get_pos(&curx, &cury); if (curx == 0 && cury == 0) { conout->ClearScreen(conout); end_term(); return; } conout->QueryMode(conout, conout->Mode->Mode, &x, &y); CL(0); /* clear current line from cursor to end */ for (i = cury + 1; i < y-1; i++) { curs_move(NULL, NULL, 0, i); CL(0); } curs_move(NULL, NULL, curx, cury); end_term(); } /* * Absolute cursor move to args[0] rows and args[1] columns * (the coordinates are 1-based). */ static void CM(void) { if (args[0] > 0) args[0]--; if (args[1] > 0) args[1]--; curs_move(&curx, &cury, args[1], args[0]); end_term(); } /* Home cursor (left top corner), also called from mode command. */ void HO(void) { argc = 1; args[0] = args[1] = 1; CM(); } - + /* Clear line from current position to end of line */ static void CL(int direction) { int i, len; UINTN x, y; CHAR16 *line; conout->QueryMode(conout, conout->Mode->Mode, &x, &y); switch (direction) { case 0: /* from cursor to end */ len = x - curx + 1; break; case 1: /* from beginning to cursor */ len = curx; break; case 2: /* entire line */ len = x; break; default: /* NOTREACHED */ __unreachable(); } - + if (cury == y - 1) len--; line = malloc(len * sizeof (CHAR16)); if (line == NULL) { printf("out of memory\n"); return; } for (i = 0; i < len; i++) line[i] = ' '; line[len-1] = 0; if (direction != 0) curs_move(NULL, NULL, 0, cury); - + conout->OutputString(conout, line); /* restore cursor position */ curs_move(NULL, NULL, curx, cury); free(line); end_term(); } static void get_arg(int c) { if (argc < 0) argc = 0; args[argc] *= 10; args[argc] += c - '0'; } #endif - + /* Emulate basic capabilities of cons25 terminal */ static void efi_term_emu(int c) { if (!boot_services_active) return; #ifdef TERM_EMU static int ansi_col[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; int t, i; EFI_STATUS status; - + switch (esc) { case 0: switch (c) { case '\033': esc = c; break; default: efi_cons_rawputchar(c); break; } break; case '\033': switch (c) { case '[': esc = c; args[0] = 0; argc = -1; break; default: bail_out(c); break; } break; case '[': switch (c) { case ';': if (argc < 0) argc = 0; else if (argc + 1 >= MAXARGS) bail_out(c); else args[++argc] = 0; break; case 'H': /* ho = \E[H */ if (argc < 0) HO(); else if (argc == 1) CM(); else bail_out(c); break; case 'J': /* cd = \E[J */ if (argc < 0) CD(); else bail_out(c); break; case 'm': if (argc < 0) { fg_c = DEFAULT_FGCOLOR; bg_c = DEFAULT_BGCOLOR; } for (i = 0; i <= argc; ++i) { switch (args[i]) { case 0: /* back to normal */ fg_c = DEFAULT_FGCOLOR; bg_c = DEFAULT_BGCOLOR; break; case 1: /* bold */ fg_c |= 0x8; break; case 4: /* underline */ case 5: /* blink */ bg_c |= 0x8; break; case 7: /* reverse */ t = fg_c; fg_c = bg_c; bg_c = t; break; case 22: /* normal intensity */ fg_c &= ~0x8; break; case 24: /* not underline */ case 25: /* not blinking */ bg_c &= ~0x8; break; case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: fg_c = ansi_col[args[i] - 30]; break; case 39: /* normal */ fg_c = DEFAULT_FGCOLOR; break; case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: bg_c = ansi_col[args[i] - 40]; break; case 49: /* normal */ bg_c = DEFAULT_BGCOLOR; break; } } conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c)); end_term(); break; default: if (isdigit(c)) get_arg(c); else bail_out(c); break; } break; default: bail_out(c); break; } #else efi_cons_rawputchar(c); #endif } static int env_screen_nounset(struct env_var *ev __unused) { if (gfx_state.tg_fb_type == FB_TEXT) return (0); return (EPERM); } static void cons_draw_frame(teken_attr_t *a) { teken_attr_t attr = *a; teken_color_t fg = a->ta_fgcolor; attr.ta_fgcolor = attr.ta_bgcolor; teken_set_defattr(&gfx_state.tg_teken, &attr); gfx_fb_drawrect(0, 0, gfx_state.tg_fb.fb_width, gfx_state.tg_origin.tp_row, 1); gfx_fb_drawrect(0, gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1); gfx_fb_drawrect(0, gfx_state.tg_origin.tp_row, gfx_state.tg_origin.tp_col, gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 1); gfx_fb_drawrect( gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col - 1, gfx_state.tg_origin.tp_row, gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1); attr.ta_fgcolor = fg; teken_set_defattr(&gfx_state.tg_teken, &attr); } bool cons_update_mode(bool use_gfx_mode) { UINTN cols, rows; const teken_attr_t *a; teken_attr_t attr; EFI_STATUS status; char env[10], *ptr; if (!efi_started) return (false); /* * Despite the use_gfx_mode, we want to make sure we call * efi_find_framebuffer(). This will populate the fb data, * which will be passed to kernel. */ if (efi_find_framebuffer(&gfx_state) == 0 && use_gfx_mode) { int roff, goff, boff; roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1; goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1; boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1; (void) generate_cons_palette(cmap, COLOR_FORMAT_RGB, gfx_state.tg_fb.fb_mask_red >> roff, roff, gfx_state.tg_fb.fb_mask_green >> goff, goff, gfx_state.tg_fb.fb_mask_blue >> boff, boff); } else { /* * Either text mode was asked by user or we failed to * find frame buffer. */ gfx_state.tg_fb_type = FB_TEXT; } status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows); if (EFI_ERROR(status) || cols * rows == 0) { cols = TEXT_COLS; rows = TEXT_ROWS; } /* * When we have serial port listed in ConOut, use pre-teken emulator, * if built with. * The problem is, we can not output text on efi and comconsole when * efi also has comconsole bound. But then again, we need to have * terminal emulator for efi text mode to support the menu. * While teken is too expensive to be used on serial console, the * pre-teken emulator is light enough to be used on serial console. * * When doing multiple consoles (both serial and video), * also just use the old emulator. RB_MULTIPLE also implies * we're using a serial console. */ mode = parse_uefi_con_out(); if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) { conout->EnableCursor(conout, FALSE); gfx_state.tg_cursor_visible = false; if (gfx_state.tg_fb_type == FB_TEXT) { gfx_state.tg_functions = &tf; /* ensure the following are not set for text mode */ unsetenv("screen.height"); unsetenv("screen.width"); unsetenv("screen.depth"); } else { uint32_t fb_height, fb_width; fb_height = gfx_state.tg_fb.fb_height; fb_width = gfx_state.tg_fb.fb_width; /* * setup_font() can adjust terminal size. * We can see two kind of bad happening. * We either can get too small console font - requested * terminal size is large, display resolution is * large, and we get very small font. * Or, we can get too large font - requested * terminal size is small and this will cause large * font to be selected. * Now, the setup_font() is updated to consider * display density and this should give us mostly * acceptable font. However, the catch is, not all * display devices will give us display density. * Still, we do hope, external monitors do - this is * where the display size will matter the most. * And for laptop screens, we should still get good * results by requesting 80x25 terminal. */ gfx_state.tg_tp.tp_row = 25; gfx_state.tg_tp.tp_col = 80; setup_font(&gfx_state, fb_height, fb_width); rows = gfx_state.tg_tp.tp_row; cols = gfx_state.tg_tp.tp_col; /* Point of origin in pixels. */ gfx_state.tg_origin.tp_row = (fb_height - (rows * gfx_state.tg_font.vf_height)) / 2; gfx_state.tg_origin.tp_col = (fb_width - (cols * gfx_state.tg_font.vf_width)) / 2; /* UEFI gop has depth 32. */ gfx_state.tg_glyph_size = gfx_state.tg_font.vf_height * gfx_state.tg_font.vf_width * 4; free(gfx_state.tg_glyph); gfx_state.tg_glyph = malloc(gfx_state.tg_glyph_size); if (gfx_state.tg_glyph == NULL) return (false); gfx_state.tg_functions = &tfx; snprintf(env, sizeof (env), "%d", fb_height); env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK, env, env_noset, env_screen_nounset); snprintf(env, sizeof (env), "%d", fb_width); env_setenv("screen.width", EV_VOLATILE | EV_NOHOOK, env, env_noset, env_screen_nounset); snprintf(env, sizeof (env), "%d", gfx_state.tg_fb.fb_bpp); env_setenv("screen.depth", EV_VOLATILE | EV_NOHOOK, env, env_noset, env_screen_nounset); } /* Record our terminal screen size. */ gfx_state.tg_tp.tp_row = rows; gfx_state.tg_tp.tp_col = cols; teken_init(&gfx_state.tg_teken, gfx_state.tg_functions, &gfx_state); free(screen_buffer); screen_buffer = malloc(rows * cols * sizeof(*screen_buffer)); if (screen_buffer != NULL) { teken_set_winsize(&gfx_state.tg_teken, &gfx_state.tg_tp); a = teken_get_defattr(&gfx_state.tg_teken); attr = *a; /* * On first run, we set up the efi_set_colors() * callback. If the env is already set, we * pick up fg and bg color values from the environment. */ ptr = getenv("teken.fg_color"); if (ptr != NULL) { attr.ta_fgcolor = strtol(ptr, NULL, 10); ptr = getenv("teken.bg_color"); attr.ta_bgcolor = strtol(ptr, NULL, 10); teken_set_defattr(&gfx_state.tg_teken, &attr); } else { snprintf(env, sizeof(env), "%d", attr.ta_fgcolor); env_setenv("teken.fg_color", EV_VOLATILE, env, efi_set_colors, env_nounset); snprintf(env, sizeof(env), "%d", attr.ta_bgcolor); env_setenv("teken.bg_color", EV_VOLATILE, env, efi_set_colors, env_nounset); } } } if (screen_buffer == NULL) { conout->EnableCursor(conout, TRUE); #ifdef TERM_EMU conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR, DEFAULT_BGCOLOR)); end_term(); get_pos(&curx, &cury); curs_move(&curx, &cury, curx, cury); fg_c = DEFAULT_FGCOLOR; bg_c = DEFAULT_BGCOLOR; #endif } else { /* Improve visibility */ if (attr.ta_bgcolor == TC_WHITE) attr.ta_bgcolor |= TC_LIGHT; teken_set_defattr(&gfx_state.tg_teken, &attr); /* Draw frame around terminal area. */ cons_draw_frame(&attr); /* * Erase display, this will also fill our screen * buffer. */ teken_input(&gfx_state.tg_teken, "\e[2J", 4); gfx_state.tg_functions->tf_param(&gfx_state, TP_SHOWCURSOR, 1); } snprintf(env, sizeof (env), "%u", (unsigned)rows); setenv("LINES", env, 1); snprintf(env, sizeof (env), "%u", (unsigned)cols); setenv("COLUMNS", env, 1); return (true); } static int efi_cons_init(int arg) { EFI_STATUS status; if (efi_started) return (0); efi_started = true; gfx_framework_init(); if (cons_update_mode(gfx_state.tg_fb_type != FB_TEXT)) return (0); return (1); } static void input_partial(void) { unsigned i; uint32_t c; if (utf8_left == 0) return; for (i = 0; i < sizeof(utf8_partial); i++) { c = (utf8_partial >> (24 - (i << 3))) & 0xff; if (c != 0) efi_term_emu(c); } utf8_left = 0; utf8_partial = 0; } static void input_byte(uint8_t c) { if ((c & 0x80) == 0x00) { /* One-byte sequence. */ input_partial(); efi_term_emu(c); return; } if ((c & 0xe0) == 0xc0) { /* Two-byte sequence. */ input_partial(); utf8_left = 1; utf8_partial = c; return; } if ((c & 0xf0) == 0xe0) { /* Three-byte sequence. */ input_partial(); utf8_left = 2; utf8_partial = c; return; } if ((c & 0xf8) == 0xf0) { /* Four-byte sequence. */ input_partial(); utf8_left = 3; utf8_partial = c; return; } if ((c & 0xc0) == 0x80) { /* Invalid state? */ if (utf8_left == 0) { efi_term_emu(c); return; } utf8_left--; utf8_partial = (utf8_partial << 8) | c; if (utf8_left == 0) { uint32_t v, u; uint8_t b; v = 0; u = utf8_partial; b = (u >> 24) & 0xff; if (b != 0) { /* Four-byte sequence */ v = b & 0x07; b = (u >> 16) & 0xff; v = (v << 6) | (b & 0x3f); b = (u >> 8) & 0xff; v = (v << 6) | (b & 0x3f); b = u & 0xff; v = (v << 6) | (b & 0x3f); } else if ((b = (u >> 16) & 0xff) != 0) { v = b & 0x0f; /* Three-byte sequence */ b = (u >> 8) & 0xff; v = (v << 6) | (b & 0x3f); b = u & 0xff; v = (v << 6) | (b & 0x3f); } else if ((b = (u >> 8) & 0xff) != 0) { v = b & 0x1f; /* Two-byte sequence */ b = u & 0xff; v = (v << 6) | (b & 0x3f); } /* Send unicode char directly to console. */ efi_cons_efiputchar(v); utf8_partial = 0; } return; } /* Anything left is illegal in UTF-8 sequence. */ input_partial(); efi_term_emu(c); } void efi_cons_putchar(int c) { unsigned char ch = c; /* * Don't use Teken when we're doing pure serial, or a multiple console * with video "primary" because that's also serial. */ if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || screen_buffer == NULL) { input_byte(ch); return; } teken_input(&gfx_state.tg_teken, &ch, sizeof (ch)); } static int keybuf_getchar(void) { int i, c = 0; for (i = 0; i < KEYBUFSZ; i++) { if (keybuf[i] != 0) { c = keybuf[i]; keybuf[i] = 0; break; } } return (c); } static bool keybuf_ischar(void) { int i; for (i = 0; i < KEYBUFSZ; i++) { if (keybuf[i] != 0) return (true); } return (false); } /* * We are not reading input before keybuf is empty, so we are safe * just to fill keybuf from the beginning. */ static void keybuf_inschar(EFI_INPUT_KEY *key) { switch (key->ScanCode) { case SCAN_UP: /* UP */ keybuf[0] = 0x1b; /* esc */ keybuf[1] = '['; keybuf[2] = 'A'; break; case SCAN_DOWN: /* DOWN */ keybuf[0] = 0x1b; /* esc */ keybuf[1] = '['; keybuf[2] = 'B'; break; case SCAN_RIGHT: /* RIGHT */ keybuf[0] = 0x1b; /* esc */ keybuf[1] = '['; keybuf[2] = 'C'; break; case SCAN_LEFT: /* LEFT */ keybuf[0] = 0x1b; /* esc */ keybuf[1] = '['; keybuf[2] = 'D'; break; case SCAN_DELETE: keybuf[0] = CHAR_BACKSPACE; break; case SCAN_ESC: keybuf[0] = 0x1b; /* esc */ break; default: keybuf[0] = key->UnicodeChar; break; } } static bool efi_readkey(void) { EFI_STATUS status; EFI_INPUT_KEY key; status = conin->ReadKeyStroke(conin, &key); if (status == EFI_SUCCESS) { keybuf_inschar(&key); return (true); } return (false); } static bool efi_readkey_ex(void) { EFI_STATUS status; EFI_INPUT_KEY *kp; EFI_KEY_DATA key_data; uint32_t kss; status = coninex->ReadKeyStrokeEx(coninex, &key_data); if (status == EFI_SUCCESS) { kss = key_data.KeyState.KeyShiftState; kp = &key_data.Key; if (kss & EFI_SHIFT_STATE_VALID) { /* * quick mapping to control chars, replace with * map lookup later. */ if (kss & EFI_RIGHT_CONTROL_PRESSED || kss & EFI_LEFT_CONTROL_PRESSED) { if (kp->UnicodeChar >= 'a' && kp->UnicodeChar <= 'z') { kp->UnicodeChar -= 'a'; kp->UnicodeChar++; } } } /* * The shift state and/or toggle state may not be valid, * but we still can have ScanCode or UnicodeChar. */ if (kp->ScanCode == 0 && kp->UnicodeChar == 0) return (false); keybuf_inschar(kp); return (true); } return (false); } int efi_cons_getchar(void) { int c; if ((c = keybuf_getchar()) != 0) return (c); if (!boot_services_active) return (-1); key_pending = 0; if (coninex == NULL) { if (efi_readkey()) return (keybuf_getchar()); } else { if (efi_readkey_ex()) return (keybuf_getchar()); } return (-1); } int efi_cons_poll(void) { EFI_STATUS status; if (keybuf_ischar() || key_pending) return (1); if (!boot_services_active) return (0); /* * Some EFI implementation (u-boot for example) do not support * WaitForKey(). * CheckEvent() can clear the signaled state. */ if (coninex != NULL) { if (coninex->WaitForKeyEx == NULL) { key_pending = efi_readkey_ex(); } else { status = BS->CheckEvent(coninex->WaitForKeyEx); key_pending = status == EFI_SUCCESS; } } else { if (conin->WaitForKey == NULL) { key_pending = efi_readkey(); } else { status = BS->CheckEvent(conin->WaitForKey); key_pending = status == EFI_SUCCESS; } } return (key_pending); } /* Plain direct access to EFI OutputString(). */ void efi_cons_efiputchar(int c) { CHAR16 buf[2]; EFI_STATUS status; buf[0] = c; buf[1] = 0; /* terminate string */ status = conout->TestString(conout, buf); if (EFI_ERROR(status)) buf[0] = '?'; conout->OutputString(conout, buf); } diff --git a/stand/efi/libefi/eficom.c b/stand/efi/libefi/eficom.c index f1ce14eb50f8..2aa592da47f4 100644 --- a/stand/efi/libefi/eficom.c +++ b/stand/efi/libefi/eficom.c @@ -1,601 +1,601 @@ /*- * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) * * 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 AND CONTRIBUTORS ``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 AUTHOR 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 #include static EFI_GUID serial = SERIAL_IO_PROTOCOL; #define COMC_TXWAIT 0x40000 /* transmit timeout */ #define PNP0501 0x501 /* 16550A-compatible COM port */ struct serial { uint64_t newbaudrate; uint64_t baudrate; uint32_t timeout; uint32_t receivefifodepth; uint32_t databits; EFI_PARITY_TYPE parity; EFI_STOP_BITS_TYPE stopbits; int ioaddr; /* index in handles array */ EFI_HANDLE currdev; /* current serial device */ EFI_HANDLE condev; /* EFI Console device */ SERIAL_IO_INTERFACE *sio; }; static void comc_probe(struct console *); static int comc_init(int); static void comc_putchar(int); static int comc_getchar(void); static int comc_ischar(void); static bool comc_setup(void); static int comc_parse_intval(const char *, unsigned *); static int comc_port_set(struct env_var *, int, const void *); static int comc_speed_set(struct env_var *, int, const void *); static struct serial *comc_port; extern struct console efi_console; struct console eficom = { .c_name = "eficom", .c_desc = "serial port", .c_flags = 0, .c_probe = comc_probe, .c_init = comc_init, .c_out = comc_putchar, .c_in = comc_getchar, .c_ready = comc_ischar, }; #if defined(__aarch64__) && __FreeBSD_version < 1500000 static void comc_probe_compat(struct console *); struct console comconsole = { .c_name = "comconsole", .c_desc = "serial port", .c_flags = 0, .c_probe = comc_probe_compat, .c_init = comc_init, .c_out = comc_putchar, .c_in = comc_getchar, .c_ready = comc_ischar, }; #endif static EFI_STATUS efi_serial_init(EFI_HANDLE **handlep, int *nhandles) { UINTN bufsz = 0; EFI_STATUS status; EFI_HANDLE *handles; /* * get buffer size */ *nhandles = 0; handles = NULL; status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles); if (status != EFI_BUFFER_TOO_SMALL) return (status); if ((handles = malloc(bufsz)) == NULL) return (ENOMEM); *nhandles = (int)(bufsz / sizeof (EFI_HANDLE)); /* * get handle array */ status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles); if (EFI_ERROR(status)) { free(handles); *nhandles = 0; } else *handlep = handles; return (status); } /* * Find serial device number from device path. * Return -1 if not found. */ static int efi_serial_get_index(EFI_DEVICE_PATH *devpath, int idx) { ACPI_HID_DEVICE_PATH *acpi; CHAR16 *text; while (!IsDevicePathEnd(devpath)) { if (DevicePathType(devpath) == MESSAGING_DEVICE_PATH && DevicePathSubType(devpath) == MSG_UART_DP) return (idx); if (DevicePathType(devpath) == ACPI_DEVICE_PATH && (DevicePathSubType(devpath) == ACPI_DP || DevicePathSubType(devpath) == ACPI_EXTENDED_DP)) { acpi = (ACPI_HID_DEVICE_PATH *)devpath; if (acpi->HID == EISA_PNP_ID(PNP0501)) { return (acpi->UID); } } devpath = NextDevicePathNode(devpath); } return (-1); } /* * The order of handles from LocateHandle() is not known, we need to * iterate handles, pick device path for handle, and check the device * number. */ static EFI_HANDLE efi_serial_get_handle(int port, EFI_HANDLE condev) { EFI_STATUS status; EFI_HANDLE *handles, handle; EFI_DEVICE_PATH *devpath; int index, nhandles; if (port == -1) return (NULL); handles = NULL; nhandles = 0; status = efi_serial_init(&handles, &nhandles); if (EFI_ERROR(status)) return (NULL); /* * We have console handle, set ioaddr for it. */ if (condev != NULL) { for (index = 0; index < nhandles; index++) { if (condev == handles[index]) { devpath = efi_lookup_devpath(condev); comc_port->ioaddr = efi_serial_get_index(devpath, index); efi_close_devpath(condev); free(handles); return (condev); } } } handle = NULL; for (index = 0; handle == NULL && index < nhandles; index++) { devpath = efi_lookup_devpath(handles[index]); if (port == efi_serial_get_index(devpath, index)) handle = (handles[index]); efi_close_devpath(handles[index]); } /* * In case we did fail to identify the device by path, use port as * array index. Note, we did check port == -1 above. */ if (port < nhandles && handle == NULL) handle = handles[port]; free(handles); return (handle); } static EFI_HANDLE comc_get_con_serial_handle(const char *name) { EFI_HANDLE handle; EFI_DEVICE_PATH *node; EFI_STATUS status; char *buf, *ep; size_t sz; buf = NULL; sz = 0; status = efi_global_getenv(name, buf, &sz); if (status == EFI_BUFFER_TOO_SMALL) { buf = malloc(sz); if (buf == NULL) return (NULL); status = efi_global_getenv(name, buf, &sz); } if (status != EFI_SUCCESS) { free(buf); return (NULL); } ep = buf + sz; node = (EFI_DEVICE_PATH *)buf; while ((char *)node < ep) { status = BS->LocateDevicePath(&serial, &node, &handle); if (status == EFI_SUCCESS) { free(buf); return (handle); } /* Sanity check the node before moving to the next node. */ if (DevicePathNodeLength(node) < sizeof(*node)) break; /* Start of next device path in list. */ node = NextDevicePathNode(node); } free(buf); return (NULL); } /* * Called from cons_probe() to see if this device is available. * Return immediately on x86, except for hyperv, since it interferes with * common configurations otherwise (yes, this is just firewalling the bug). */ static void comc_probe(struct console *sc) { EFI_STATUS status; EFI_HANDLE handle; char name[20]; char value[20]; unsigned val; char *env, *buf, *ep; size_t sz; #ifdef __amd64__ /* * This driver tickles issues on a number of different firmware loads. * It is only required for HyperV, and is only known to work on HyperV, * so only allow it on HyperV. */ env = getenv("smbios.bios.version"); if (env == NULL || strncmp(env, "Hyper-V", 7) != 0) { return; } #endif if (comc_port == NULL) { comc_port = calloc(1, sizeof (struct serial)); if (comc_port == NULL) return; } /* Use defaults from firmware */ comc_port->databits = 8; comc_port->parity = DefaultParity; comc_port->stopbits = DefaultStopBits; handle = NULL; env = getenv("efi_com_port"); if (comc_parse_intval(env, &val) == CMD_OK) { comc_port->ioaddr = val; } else { /* * efi_com_port is not set, we need to select default. * First, we consult ConOut variable to see if * we have serial port redirection. If not, we just * pick first device. */ handle = comc_get_con_serial_handle("ConOut"); comc_port->condev = handle; } handle = efi_serial_get_handle(comc_port->ioaddr, handle); if (handle != NULL) { comc_port->currdev = handle; status = BS->OpenProtocol(handle, &serial, (void**)&comc_port->sio, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(status)) { comc_port->sio = NULL; } else { comc_port->newbaudrate = comc_port->baudrate = comc_port->sio->Mode->BaudRate; comc_port->timeout = comc_port->sio->Mode->Timeout; comc_port->receivefifodepth = comc_port->sio->Mode->ReceiveFifoDepth; comc_port->databits = comc_port->sio->Mode->DataBits; comc_port->parity = comc_port->sio->Mode->Parity; comc_port->stopbits = comc_port->sio->Mode->StopBits; } } /* * If there's no sio, then the device isn't there, so just return since * the present flags aren't yet set. */ if (comc_port->sio == NULL) { free(comc_port); comc_port = NULL; return; } - if (env != NULL) + if (env != NULL) unsetenv("efi_com_port"); snprintf(value, sizeof (value), "%u", comc_port->ioaddr); env_setenv("efi_com_port", EV_VOLATILE, value, comc_port_set, env_nounset); env = getenv("efi_com_speed"); if (env == NULL) /* fallback to comconsole setting */ env = getenv("comconsole_speed"); if (comc_parse_intval(env, &val) == CMD_OK) comc_port->newbaudrate = val; if (env != NULL) unsetenv("efi_com_speed"); snprintf(value, sizeof (value), "%ju", (uintmax_t)comc_port->baudrate); env_setenv("efi_com_speed", EV_VOLATILE, value, comc_speed_set, env_nounset); if (comc_setup()) { sc->c_flags = C_PRESENTIN | C_PRESENTOUT; } else { sc->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); free(comc_port); comc_port = NULL; } } #if defined(__aarch64__) && __FreeBSD_version < 1500000 static void comc_probe_compat(struct console *sc) { comc_probe(&eficom); if (eficom.c_flags & (C_PRESENTIN | C_PRESENTOUT)) { printf("comconsole: comconsole device name is deprecated, switch to eficom\n"); } /* * Note: We leave the present bits unset in sc to avoid ghosting. */ } #endif /* * Called when the console is selected in cons_change. If we didn't detect the * device, comc_port will be NULL, and comc_setup will fail. It may be called * even when the device isn't present as a 'fallback' console or when listed * specifically in console env, so we have to reset the c_flags in those case to * say it's not present. */ static int comc_init(int arg __unused) { if (comc_setup()) return (0); eficom.c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT); return (1); } static void comc_putchar(int c) { int wait; EFI_STATUS status; UINTN bufsz = 1; char cb = c; if (comc_port->sio == NULL) return; for (wait = COMC_TXWAIT; wait > 0; wait--) { status = comc_port->sio->Write(comc_port->sio, &bufsz, &cb); if (status != EFI_TIMEOUT) break; } } static int comc_getchar(void) { EFI_STATUS status; UINTN bufsz = 1; char c; /* * if this device is also used as ConIn, some firmwares * fail to return all input via SIO protocol. */ if (comc_port->currdev == comc_port->condev) { if ((efi_console.c_flags & C_ACTIVEIN) == 0) return (efi_console.c_in()); return (-1); } if (comc_port->sio == NULL) return (-1); status = comc_port->sio->Read(comc_port->sio, &bufsz, &c); if (EFI_ERROR(status) || bufsz == 0) return (-1); return (c); } static int comc_ischar(void) { EFI_STATUS status; uint32_t control; /* * if this device is also used as ConIn, some firmwares * fail to return all input via SIO protocol. */ if (comc_port->currdev == comc_port->condev) { if ((efi_console.c_flags & C_ACTIVEIN) == 0) return (efi_console.c_ready()); return (0); } if (comc_port->sio == NULL) return (0); status = comc_port->sio->GetControl(comc_port->sio, &control); if (EFI_ERROR(status)) return (0); return (!(control & EFI_SERIAL_INPUT_BUFFER_EMPTY)); } static int comc_parse_intval(const char *value, unsigned *valp) { unsigned n; char *ep; if (value == NULL || *value == '\0') return (CMD_ERROR); errno = 0; n = strtoul(value, &ep, 10); if (errno != 0 || *ep != '\0') return (CMD_ERROR); *valp = n; return (CMD_OK); } static int comc_port_set(struct env_var *ev, int flags, const void *value) { unsigned port; SERIAL_IO_INTERFACE *sio; EFI_HANDLE handle; EFI_STATUS status; if (value == NULL || comc_port == NULL) return (CMD_ERROR); - if (comc_parse_intval(value, &port) != CMD_OK) + if (comc_parse_intval(value, &port) != CMD_OK) return (CMD_ERROR); handle = efi_serial_get_handle(port, NULL); if (handle == NULL) { printf("no handle\n"); return (CMD_ERROR); } status = BS->OpenProtocol(handle, &serial, (void**)&sio, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(status)) { printf("OpenProtocol: %lu\n", EFI_ERROR_CODE(status)); return (CMD_ERROR); } comc_port->currdev = handle; comc_port->ioaddr = port; comc_port->sio = sio; (void) comc_setup(); env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); return (CMD_OK); } static int comc_speed_set(struct env_var *ev, int flags, const void *value) { unsigned speed; if (value == NULL || comc_port == NULL) return (CMD_ERROR); - if (comc_parse_intval(value, &speed) != CMD_OK) + if (comc_parse_intval(value, &speed) != CMD_OK) return (CMD_ERROR); comc_port->newbaudrate = speed; if (comc_setup()) env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); return (CMD_OK); } /* * In case of error, we also reset ACTIVE flags, so the console * framefork will try alternate consoles. */ static bool comc_setup(void) { EFI_STATUS status; char *ev; /* * If the device isn't active, or there's no port present. */ if ((eficom.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0 || comc_port == NULL) return (false); if (comc_port->sio->Reset != NULL) { status = comc_port->sio->Reset(comc_port->sio); if (EFI_ERROR(status)) return (false); } /* * Avoid setting the baud rate on Hyper-V. Also, only set the baud rate * if the baud rate has changed from the default. And pass in '0' or * DefaultFoo when we're not changing those values. Some EFI * implementations get cranky when you set things to the values reported * back even when they are unchanged. */ if (comc_port->sio->SetAttributes != NULL && comc_port->newbaudrate != comc_port->baudrate) { ev = getenv("smbios.bios.version"); if (ev != NULL && strncmp(ev, "Hyper-V", 7) != 0) { status = comc_port->sio->SetAttributes(comc_port->sio, comc_port->newbaudrate, 0, 0, DefaultParity, 0, DefaultStopBits); if (EFI_ERROR(status)) return (false); comc_port->baudrate = comc_port->newbaudrate; } } #ifdef EFI_FORCE_RTS if (comc_port->sio->GetControl != NULL && comc_port->sio->SetControl != NULL) { UINT32 control; status = comc_port->sio->GetControl(comc_port->sio, &control); if (EFI_ERROR(status)) return (false); control |= EFI_SERIAL_REQUEST_TO_SEND; (void) comc_port->sio->SetControl(comc_port->sio, control); } #endif /* Mark this port usable. */ eficom.c_flags |= (C_PRESENTIN | C_PRESENTOUT); return (true); } diff --git a/stand/efi/libefi/efinet.c b/stand/efi/libefi/efinet.c index 97890c9d9b43..186d816cd323 100644 --- a/stand/efi/libefi/efinet.c +++ b/stand/efi/libefi/efinet.c @@ -1,464 +1,464 @@ /*- * Copyright (c) 2001 Doug Rabson * Copyright (c) 2002, 2006 Marcel Moolenaar * 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 AND CONTRIBUTORS ``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 AUTHOR 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 #include #include #include #include #include "dev_net.h" static EFI_GUID sn_guid = EFI_SIMPLE_NETWORK_PROTOCOL; static void efinet_end(struct netif *); static ssize_t efinet_get(struct iodesc *, void **, time_t); static void efinet_init(struct iodesc *, void *); static int efinet_match(struct netif *, void *); static int efinet_probe(struct netif *, void *); static ssize_t efinet_put(struct iodesc *, void *, size_t); -struct netif_driver efinetif = { +struct netif_driver efinetif = { .netif_bname = "efinet", .netif_match = efinet_match, .netif_probe = efinet_probe, .netif_init = efinet_init, .netif_get = efinet_get, .netif_put = efinet_put, .netif_end = efinet_end, .netif_ifs = NULL, .netif_nifs = 0 }; #ifdef EFINET_DEBUG static void dump_mode(EFI_SIMPLE_NETWORK_MODE *mode) { int i; printf("State = %x\n", mode->State); printf("HwAddressSize = %u\n", mode->HwAddressSize); printf("MediaHeaderSize = %u\n", mode->MediaHeaderSize); printf("MaxPacketSize = %u\n", mode->MaxPacketSize); printf("NvRamSize = %u\n", mode->NvRamSize); printf("NvRamAccessSize = %u\n", mode->NvRamAccessSize); printf("ReceiveFilterMask = %x\n", mode->ReceiveFilterMask); printf("ReceiveFilterSetting = %u\n", mode->ReceiveFilterSetting); printf("MaxMCastFilterCount = %u\n", mode->MaxMCastFilterCount); printf("MCastFilterCount = %u\n", mode->MCastFilterCount); printf("MCastFilter = {"); for (i = 0; i < mode->MCastFilterCount; i++) printf(" %s", ether_sprintf(mode->MCastFilter[i].Addr)); printf(" }\n"); printf("CurrentAddress = %s\n", ether_sprintf(mode->CurrentAddress.Addr)); printf("BroadcastAddress = %s\n", ether_sprintf(mode->BroadcastAddress.Addr)); printf("PermanentAddress = %s\n", ether_sprintf(mode->PermanentAddress.Addr)); printf("IfType = %u\n", mode->IfType); printf("MacAddressChangeable = %d\n", mode->MacAddressChangeable); printf("MultipleTxSupported = %d\n", mode->MultipleTxSupported); printf("MediaPresentSupported = %d\n", mode->MediaPresentSupported); printf("MediaPresent = %d\n", mode->MediaPresent); } #endif static int efinet_match(struct netif *nif, void *machdep_hint) { struct devdesc *dev = machdep_hint; if (dev->d_unit == nif->nif_unit) return (1); return(0); } static int efinet_probe(struct netif *nif, void *machdep_hint) { EFI_SIMPLE_NETWORK *net; EFI_HANDLE h; EFI_STATUS status; h = nif->nif_driver->netif_ifs[nif->nif_unit].dif_private; /* * Open the network device in exclusive mode. Without this * we will be racing with the UEFI network stack. It will * pull packets off the network leading to lost packets. */ status = BS->OpenProtocol(h, &sn_guid, (void **)&net, IH, NULL, EFI_OPEN_PROTOCOL_EXCLUSIVE); if (status != EFI_SUCCESS) { printf("Unable to open network interface %d for " "exclusive access: %lu\n", nif->nif_unit, EFI_ERROR_CODE(status)); return (efi_status_to_errno(status)); } return (0); } static ssize_t efinet_put(struct iodesc *desc, void *pkt, size_t len) { struct netif *nif = desc->io_netif; EFI_SIMPLE_NETWORK *net; EFI_STATUS status; void *buf; net = nif->nif_devdata; if (net == NULL) return (-1); status = net->Transmit(net, 0, len, pkt, NULL, NULL, NULL); if (status != EFI_SUCCESS) return (-1); /* Wait for the buffer to be transmitted */ do { buf = NULL; /* XXX Is this needed? */ status = net->GetStatus(net, NULL, &buf); /* - * XXX EFI1.1 and the E1000 card returns a different + * XXX EFI1.1 and the E1000 card returns a different * address than we gave. Sigh. */ } while (status == EFI_SUCCESS && buf == NULL); /* XXX How do we deal with status != EFI_SUCCESS now? */ return ((status == EFI_SUCCESS) ? len : -1); } static ssize_t efinet_get(struct iodesc *desc, void **pkt, time_t timeout) { struct netif *nif = desc->io_netif; EFI_SIMPLE_NETWORK *net; EFI_STATUS status; UINTN bufsz; time_t t; char *buf, *ptr; ssize_t ret = -1; net = nif->nif_devdata; if (net == NULL) return (ret); bufsz = net->Mode->MaxPacketSize + ETHER_HDR_LEN + ETHER_CRC_LEN; buf = malloc(bufsz + ETHER_ALIGN); if (buf == NULL) return (ret); ptr = buf + ETHER_ALIGN; t = getsecs(); while ((getsecs() - t) < timeout) { status = net->Receive(net, NULL, &bufsz, ptr, NULL, NULL, NULL); if (status == EFI_SUCCESS) { *pkt = buf; ret = (ssize_t)bufsz; break; } if (status != EFI_NOT_READY) break; } if (ret == -1) free(buf); return (ret); } /* * Loader uses BOOTP/DHCP and also uses RARP as a fallback to populate * network parameters and problems with DHCP servers can cause the loader * to fail to populate them. Allow the device to ask about the basic * network parameters and if present use them. */ static void efi_env_net_params(struct iodesc *desc) { char *envstr; in_addr_t ipaddr, mask, gwaddr, serveraddr; n_long rootaddr; if ((envstr = getenv("rootpath")) != NULL) strlcpy(rootpath, envstr, sizeof(rootpath)); /* * Get network parameters. */ envstr = getenv("ipaddr"); ipaddr = (envstr != NULL) ? inet_addr(envstr) : 0; envstr = getenv("netmask"); mask = (envstr != NULL) ? inet_addr(envstr) : 0; envstr = getenv("gatewayip"); gwaddr = (envstr != NULL) ? inet_addr(envstr) : 0; envstr = getenv("serverip"); serveraddr = (envstr != NULL) ? inet_addr(envstr) : 0; /* No network params. */ if (ipaddr == 0 && mask == 0 && gwaddr == 0 && serveraddr == 0) return; /* Partial network params. */ if (ipaddr == 0 || mask == 0 || gwaddr == 0 || serveraddr == 0) { printf("Incomplete network settings from U-Boot\n"); return; } /* * Set network parameters. */ myip.s_addr = ipaddr; netmask = mask; gateip.s_addr = gwaddr; servip.s_addr = serveraddr; /* * There must be a rootpath. It may be ip:/path or it may be just the * path in which case the ip needs to be serverip. */ rootaddr = net_parse_rootpath(); if (rootaddr == INADDR_NONE) rootaddr = serveraddr; rootip.s_addr = rootaddr; #ifdef EFINET_DEBUG printf("%s: ip=%s\n", __func__, inet_ntoa(myip)); printf("%s: mask=%s\n", __func__, intoa(netmask)); printf("%s: gateway=%s\n", __func__, inet_ntoa(gateip)); printf("%s: server=%s\n", __func__, inet_ntoa(servip)); #endif desc->myip = myip; } static void efinet_init(struct iodesc *desc, void *machdep_hint) { struct netif *nif = desc->io_netif; EFI_SIMPLE_NETWORK *net; EFI_HANDLE h; EFI_STATUS status; UINT32 mask; /* Attempt to get netboot params from env */ efi_env_net_params(desc); if (nif->nif_driver->netif_ifs[nif->nif_unit].dif_unit < 0) { printf("Invalid network interface %d\n", nif->nif_unit); return; } h = nif->nif_driver->netif_ifs[nif->nif_unit].dif_private; status = OpenProtocolByHandle(h, &sn_guid, (void **)&nif->nif_devdata); if (status != EFI_SUCCESS) { printf("net%d: cannot fetch interface data (status=%lu)\n", nif->nif_unit, EFI_ERROR_CODE(status)); return; } net = nif->nif_devdata; if (net->Mode->State == EfiSimpleNetworkStopped) { status = net->Start(net); if (status != EFI_SUCCESS) { printf("net%d: cannot start interface (status=%lu)\n", nif->nif_unit, EFI_ERROR_CODE(status)); return; } } if (net->Mode->State != EfiSimpleNetworkInitialized) { status = net->Initialize(net, 0, 0); if (status != EFI_SUCCESS) { printf("net%d: cannot init. interface (status=%lu)\n", nif->nif_unit, EFI_ERROR_CODE(status)); return; } } mask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST; status = net->ReceiveFilters(net, mask, 0, FALSE, 0, NULL); if (status != EFI_SUCCESS) printf("net%d: cannot set rx. filters (status=%lu)\n", nif->nif_unit, EFI_ERROR_CODE(status)); #ifdef EFINET_DEBUG dump_mode(net->Mode); #endif bcopy(net->Mode->CurrentAddress.Addr, desc->myea, 6); desc->xid = 1; } static void efinet_end(struct netif *nif) { - EFI_SIMPLE_NETWORK *net = nif->nif_devdata; + EFI_SIMPLE_NETWORK *net = nif->nif_devdata; if (net == NULL) return; net->Shutdown(net); } static int efinet_dev_init(void); static int efinet_dev_print(int); struct devsw efinet_dev = { .dv_name = "net", .dv_type = DEVT_NET, .dv_init = efinet_dev_init, .dv_strategy = NULL, /* Will be set in efinet_dev_init */ .dv_open = NULL, /* Will be set in efinet_dev_init */ .dv_close = NULL, /* Will be set in efinet_dev_init */ .dv_ioctl = noioctl, .dv_print = efinet_dev_print, .dv_cleanup = nullsys, }; static int efinet_dev_init(void) { struct netif_dif *dif; struct netif_stats *stats; EFI_DEVICE_PATH *devpath, *node; EFI_HANDLE *handles, *handles2; EFI_STATUS status; UINTN sz; int err, i, nifs; extern struct devsw netdev; sz = 0; handles = NULL; status = BS->LocateHandle(ByProtocol, &sn_guid, NULL, &sz, NULL); if (status == EFI_BUFFER_TOO_SMALL) { handles = (EFI_HANDLE *)malloc(sz); if (handles == NULL) return (ENOMEM); status = BS->LocateHandle(ByProtocol, &sn_guid, NULL, &sz, handles); if (EFI_ERROR(status)) free(handles); } if (EFI_ERROR(status)) return (efi_status_to_errno(status)); handles2 = (EFI_HANDLE *)malloc(sz); if (handles2 == NULL) { free(handles); return (ENOMEM); } nifs = 0; for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) { devpath = efi_lookup_devpath(handles[i]); if (devpath == NULL) continue; if ((node = efi_devpath_last_node(devpath)) == NULL) continue; if (DevicePathType(node) != MESSAGING_DEVICE_PATH || DevicePathSubType(node) != MSG_MAC_ADDR_DP) continue; handles2[nifs] = handles[i]; nifs++; } free(handles); if (nifs == 0) { err = ENOENT; goto done; } err = efi_register_handles(&efinet_dev, handles2, NULL, nifs); if (err != 0) goto done; efinetif.netif_ifs = calloc(nifs, sizeof(struct netif_dif)); stats = calloc(nifs, sizeof(struct netif_stats)); if (efinetif.netif_ifs == NULL || stats == NULL) { free(efinetif.netif_ifs); free(stats); efinetif.netif_ifs = NULL; err = ENOMEM; goto done; } efinetif.netif_nifs = nifs; for (i = 0; i < nifs; i++) { dif = &efinetif.netif_ifs[i]; dif->dif_unit = i; dif->dif_nsel = 1; dif->dif_stats = &stats[i]; dif->dif_private = handles2[i]; } efinet_dev.dv_open = netdev.dv_open; efinet_dev.dv_close = netdev.dv_close; efinet_dev.dv_strategy = netdev.dv_strategy; done: free(handles2); return (err); } static int efinet_dev_print(int verbose) { CHAR16 *text; EFI_HANDLE h; int unit, ret = 0; printf("%s devices:", efinet_dev.dv_name); if ((ret = pager_output("\n")) != 0) return (ret); for (unit = 0, h = efi_find_handle(&efinet_dev, 0); h != NULL; h = efi_find_handle(&efinet_dev, ++unit)) { printf(" %s%d:", efinet_dev.dv_name, unit); if (verbose) { text = efi_devpath_name(efi_lookup_devpath(h)); if (text != NULL) { printf(" %S", text); efi_free_devpath_name(text); } } if ((ret = pager_output("\n")) != 0) break; } return (ret); } diff --git a/stand/efi/libefi/time.c b/stand/efi/libefi/time.c index 99d86e30ee96..aa8508879b20 100644 --- a/stand/efi/libefi/time.c +++ b/stand/efi/libefi/time.c @@ -1,280 +1,280 @@ /*- * Copyright (c) 1999, 2000 * Intel Corporation. * 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. * * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * * This product includes software developed by Intel Corporation and * its contributors. * * 4. Neither the name of Intel Corporation or its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION AND CONTRIBUTORS ``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 INTEL CORPORATION 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 /* * Accurate only for the past couple of centuries; * that will probably do. * * (#defines From FreeBSD 3.2 lib/libc/stdtime/tzfile.h) */ #define isleap(y) (((y) % 4) == 0 && \ (((y) % 100) != 0 || ((y) % 400) == 0)) #define SECSPERHOUR (60*60) #define SECSPERDAY (24 * SECSPERHOUR) /* * These arrays give the cumulative number of days up to the first of the * month number used as the index (1 -> 12) for regular and leap years. * The value at index 13 is for the whole year. */ static const time_t CumulativeDays[2][14] = { {0, 0, 31, 31 + 28, 31 + 28 + 31, 31 + 28 + 31 + 30, 31 + 28 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 }, {0, 0, 31, 31 + 29, 31 + 29 + 31, 31 + 29 + 31 + 30, 31 + 29 + 31 + 30 + 31, 31 + 29 + 31 + 30 + 31 + 30, 31 + 29 + 31 + 30 + 31 + 30 + 31, 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31, 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30, 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 }}; void efi_time_init(void) { } void efi_time_fini(void) { } void to_efi_time(EFI_TIME *efi_time, time_t time) { int lyear, month; time_t seconds; if (time >= 0) { efi_time->Year = 1970; lyear = isleap(efi_time->Year); month = 13; seconds = CumulativeDays[lyear][month] * SECSPERDAY; while (time > seconds) { time -= seconds; efi_time->Year++; lyear = isleap(efi_time->Year); seconds = CumulativeDays[lyear][month] * SECSPERDAY; } efi_time->Month = 0; while (time > CumulativeDays[lyear][month] * SECSPERDAY) { efi_time->Month++; } month = efi_time->Month - 1; time -= CumulativeDays[lyear][month] * SECSPERDAY; for (efi_time->Day = 0; time > SECSPERDAY; efi_time->Day++) time -= SECSPERDAY; for (efi_time->Hour = 0; time > SECSPERHOUR; efi_time->Hour++) time -= SECSPERHOUR; for (efi_time->Minute = 0; time > 60; efi_time->Minute++) time -= 60; efi_time->Second = time; efi_time->Nanosecond = 0; efi_time->TimeZone = 0; efi_time->Daylight = 0; } else { memset(efi_time, 0, sizeof(EFI_TIME)); } } time_t from_efi_time(EFI_TIME *ETime) { time_t UTime; int Year; /* * Do a santity check */ if (ETime->Year < 1998 || ETime->Year > 2099 || ETime->Month == 0 || ETime->Month > 12 || ETime->Day == 0 || ETime->Month > 31 || ETime->Hour > 23 || ETime->Minute > 59 || ETime->Second > 59 || ETime->TimeZone < -1440 || (ETime->TimeZone > 1440 && ETime->TimeZone != 2047)) { return (0); } /* * Years */ UTime = 0; for (Year = 1970; Year != ETime->Year; ++Year) { UTime += (CumulativeDays[isleap(Year)][13] * SECSPERDAY); } /* * UTime should now be set to 00:00:00 on Jan 1 of the file's year. * - * Months + * Months */ UTime += (CumulativeDays[isleap(ETime->Year)][ETime->Month] * SECSPERDAY); /* * UTime should now be set to 00:00:00 on the first of the file's * month and year. * * Days -- Don't count the file's day */ UTime += (((ETime->Day > 0) ? ETime->Day-1:0) * SECSPERDAY); /* * Hours */ UTime += (ETime->Hour * SECSPERHOUR); /* * Minutes */ UTime += (ETime->Minute * 60); /* * Seconds */ UTime += ETime->Second; /* * EFI time is repored in local time. Adjust for any time zone * offset to get true UT */ if (ETime->TimeZone != EFI_UNSPECIFIED_TIMEZONE) { /* * TimeZone is kept in minues... */ UTime += (ETime->TimeZone * 60); } return (UTime); } static int EFI_GetTimeOfDay(OUT struct timeval *tp, OUT struct timezone *tzp) { EFI_TIME EfiTime; EFI_TIME_CAPABILITIES Capabilities; EFI_STATUS Status; /* * Get time from EFI */ Status = RS->GetTime(&EfiTime, &Capabilities); if (EFI_ERROR(Status)) return (-1); /* * Convert to UNIX time (ie seconds since the epoch */ tp->tv_sec = from_efi_time(&EfiTime); tp->tv_usec = 0; /* EfiTime.Nanosecond * 1000; */ /* * Do something with the timezone if needed */ if (tzp != NULL) { if (EfiTime.TimeZone == EFI_UNSPECIFIED_TIMEZONE) tzp->tz_minuteswest = 0; else tzp->tz_minuteswest = EfiTime.TimeZone; /* * This isn't quit right since it doesn't deal with * EFI_TIME_IN_DAYLIGHT */ tzp->tz_dsttime = EfiTime.Daylight & EFI_TIME_ADJUST_DAYLIGHT ? 1 : 0; } return (0); } time_t time(time_t *tloc) { struct timeval tv; memset(&tv, 0, sizeof(tv)); EFI_GetTimeOfDay(&tv, NULL); if (tloc) *tloc = tv.tv_sec; return (tv.tv_sec); } time_t getsecs(void) { return (time(NULL)); }