/*-
* Copyright (c) 2008-2010 Rui Paulo
* Copyright (c) 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
*
* 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 AUTHOR 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.
*/
__FBSDID("$FreeBSD$");
#include <stand.h>
#include <string.h>
#include <setjmp.h>
#include <efi.h>
#include <efilib.h>
#include <efigpt.h>
#include <bootstrap.h>
#include <smbios.h>
#ifdef EFI_ZFS_BOOT
#include <libzfs.h>
#endif
#include "loader_efi.h"
extern char bootprog_name[];
extern char bootprog_rev[];
extern char bootprog_date[];
extern char bootprog_maker[];
extern void acpi_detect(const caddr_t);
void efi_serial_init(void);
#ifdef EFI_ZFS_BOOT
static void efi_zfs_probe(void);
#endif
/*
* Need this because EFI uses UTF-16 unicode string constants, but we
* use UTF-8. We can't use printf due to the possiblity of \0 and we
* don't support support wide characters either.
*/
static void
{
int i;
for (i = 0; str[i]; i++)
}
static void
{
size_t i;
}
static int
has_keyboard(void)
{
int retval = 0;
/*
* Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and
* do the typical dance to get the right sized buffer.
*/
sz = 0;
if (status == EFI_BUFFER_TOO_SMALL) {
hin);
}
return retval;
/*
* Look at each of the handles. If it supports the device path protocol,
* use it to get the device path for this handle. Then see if that
* device path matches either the USB device path for keyboards or the
* legacy device path for keyboards.
*/
continue;
while (!IsDevicePathEnd(path)) {
/*
* Check for the ACPI keyboard node. All PNP3xx nodes
* are keyboards of different flavors. Note: It is
* unclear of there's always a keyboard node when
* there's a keyboard controller, or if there's only one
* when a keyboard is detected at boot.
*/
retval = 1;
goto out;
}
/*
* Check for USB keyboard node, if present. Unlike a
* PS/2 keyboard, these definitely only appear when
* connected to the system.
*/
retval = 1;
goto out;
}
}
}
}
out:
return retval;
}
{
UINTN k;
int has_kbd;
#ifdef EFI_ZFS_BOOT
/* Note this needs to be set before ZFS init. */
#endif
has_kbd = has_keyboard();
/*
* XXX Chicken-and-egg problem; we want to have console output
* early, but some console attributes may depend on reading from
* eg. the boot device, which we can't do yet. We can use
* printf() etc. once this is done.
*/
cons_probe();
/*
* Initialise the block cache. Set the upper limit.
*/
/*
* Parse the args to set the console settings, etc
* or iPXE may be setup to pass these in.
*
* Loop through the args, and for each one that contains an '=' that is
* not the first character, add it to the environment. This allows
* loader and kernel env vars to be passed on the command line. Convert
* args from UCS-2 to ASCII (16 to 8 bit) as they are copied.
*/
howto = 0;
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
for (j = 1; argv[i][j] != 0; j++) {
int ch;
switch (ch) {
case 'a':
howto |= RB_ASKNAME;
break;
case 'd':
break;
case 'D':
howto |= RB_MULTIPLE;
break;
case 'h':
break;
case 'm':
break;
case 'p':
break;
case 'P':
if (!has_kbd)
break;
case 'r':
howto |= RB_DFLTROOT;
break;
case 's':
break;
case 'S':
if (argv[i][j + 1] == 0) {
if (i + 1 == argc) {
} else {
sizeof(var));
}
i++;
break;
} else {
sizeof(var));
break;
}
case 'v':
howto |= RB_VERBOSE;
break;
}
}
} else {
vargood = 0;
for (j = 0; argv[i][j] != 0; j++) {
if (j == sizeof(var)) {
vargood = 0;
break;
}
if (j > 0 && argv[i][j] == '=')
vargood = 1;
}
if (vargood) {
var[j] = 0;
}
}
}
if (howto & RB_MULTIPLE) {
else
}
if (efi_copy_init()) {
printf("failed to allocate staging area\n");
return (EFI_BUFFER_TOO_SMALL);
}
/*
* March through the device switch probing for things.
*/
/* Get our loaded image protocol interface structure. */
printf("Command line arguments:");
for (i = 0; i < argc; i++) {
printf(" ");
print_str16(argv[i]);
}
printf("\n");
printf("EFI Firmware: ");
/* printf doesn't understand EFI Unicode */
printf("\n");
/*
* Disable the watchdog timer. By default the boot manager sets
* the timer to 5 minutes before invoking a boot option. If we
* want to return to the boot manager, we have to disable the
* watchdog timer and since we're an interactive program, we don't
* want to wait until the user types "quit". The timer may have
* fired by then. We don't care if this fails. It does not prevent
* normal functioning in any way...
*/
return (EFI_NOT_FOUND);
#ifdef EFI_ZFS_BOOT
case DEVT_ZFS: {
#ifdef __FreeBSD__
#endif
break;
}
#endif
default: {
break;
}
}
for (k = 0; k < ST->NumberOfTableEntries; k++) {
continue;
}
}
}
efi_serial_init(); /* detect and set up serial ports */
return (EFI_SUCCESS); /* keep compiler happy */
}
static int
{
int i;
(devsw[i]->dv_cleanup)();
/* NOTREACHED */
return (CMD_ERROR);
}
static int
{
int i, ndesc;
int rv = 0;
static const char *types[] = {
"Reserved",
"LoaderCode",
"LoaderData",
"BootServicesCode",
"BootServicesData",
"RuntimeServicesCode",
"RuntimeServicesData",
"ConventionalMemory",
"UnusableMemory",
"ACPIReclaimMemory",
"ACPIMemoryNVS",
"MemoryMappedIO",
"MemoryMappedIOPortSpace",
"PalCode"
};
sz = 0;
if (status != EFI_BUFFER_TOO_SMALL) {
printf("Can't determine memory map size\n");
return (CMD_ERROR);
}
printf("Can't read memory map\n");
return (CMD_ERROR);
}
"Type", "Physical", "Virtual", "#Pages", "Attr");
pager_open();
if (rv) {
pager_close();
return (CMD_OK);
}
i++, p = NextMemoryDescriptor(p, dsz)) {
p->PhysicalStart,
p->VirtualStart,
p->NumberOfPages);
if (rv)
break;
if (p->Attribute & EFI_MEMORY_UC)
printf("UC ");
if (p->Attribute & EFI_MEMORY_WC)
printf("WC ");
if (p->Attribute & EFI_MEMORY_WT)
printf("WT ");
if (p->Attribute & EFI_MEMORY_WB)
printf("WB ");
if (p->Attribute & EFI_MEMORY_UCE)
printf("UCE ");
if (p->Attribute & EFI_MEMORY_WP)
printf("WP ");
if (p->Attribute & EFI_MEMORY_RP)
printf("RP ");
if (p->Attribute & EFI_MEMORY_XP)
printf("XP ");
if (rv)
break;
}
pager_close();
return (CMD_OK);
}
static const char *
{
return (buf);
}
static int
{
UINTN i;
printf("NumberOfTableEntries=%lu\n",
(unsigned long)ST->NumberOfTableEntries);
for (i = 0; i < ST->NumberOfTableEntries; i++) {
printf(" ");
printf("MPS Table");
printf("ACPI Table");
printf("ACPI 2.0 Table");
printf("SMBIOS Table");
printf("SMBIOS3 Table");
printf("DXE Table");
printf("HOB List Table");
printf("Memory Type Information Table");
printf("Debug Image Info Table");
printf("FDT Table");
else
}
return (CMD_OK);
}
static int
{
unsigned int mode;
int i;
char *cp;
extern void HO(void);
if (argc > 1) {
if (cp[0] != '\0') {
printf("Invalid mode\n");
return (CMD_ERROR);
}
return (CMD_ERROR);
}
return (CMD_ERROR);
}
HO(); /* set cursor */
return (CMD_OK);
}
continue;
(unsigned)rows);
}
if (i != 0)
printf("Select a mode with the command \"mode <number>\"\n");
return (CMD_OK);
}
static int
{
pager_open();
var[0] = 0; /* Initiate the enumeration */
varsz = 128;
status != EFI_NOT_FOUND;
/*
* as term emu is keeping track on cursor, use putchar().
*/
for (i = 0; var[i] != 0; i++)
printf(": Attributes:");
datasz = 0;
break;
printf("<error retrieving variable>");
else {
if (attr & EFI_VARIABLE_NON_VOLATILE)
printf(" NV");
printf(" BS");
if (attr & EFI_VARIABLE_RUNTIME_ACCESS)
printf(" RS");
printf(" HR");
printf(" AW");
if (attr &
printf(" TW");
}
if (pager_output("\n"))
break;
}
pager_close();
return (CMD_OK);
}
struct protocol_name {
const char *name;
} proto_names[] = {
{ DEVICE_PATH_PROTOCOL, "device path" },
{ BLOCK_IO_PROTOCOL, "block io" },
{ DISK_IO_PROTOCOL, "disk io" },
{ EFI_DISK_INFO_PROTOCOL_GUID, "disk info" },
{ SIMPLE_FILE_SYSTEM_PROTOCOL, "simple fs" },
{ LOAD_FILE_PROTOCOL, "load file" },
{ DEVICE_IO_PROTOCOL, "device io" },
{ UNICODE_COLLATION_PROTOCOL, "unicode collation" },
{ EFI_UNICODE_COLLATION2_PROTOCOL_GUID, "unicode collation2" },
{ EFI_SIMPLE_NETWORK_PROTOCOL, "simple network" },
{ SIMPLE_TEXT_OUTPUT_PROTOCOL, "simple text output" },
{ SIMPLE_TEXT_INPUT_PROTOCOL, "simple text input" },
{ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID, "simple text ex input" },
{ EFI_CONSOLE_CONTROL_PROTOCOL_GUID, "console control" },
{ EFI_CONSOLE_IN_DEVICE_GUID, "stdin" },
{ EFI_CONSOLE_OUT_DEVICE_GUID, "stdout" },
{ EFI_STANDARD_ERROR_DEVICE_GUID, "stderr" },
{ EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, "GOP" },
{ EFI_UGA_DRAW_PROTOCOL_GUID, "UGA draw" },
{ EFI_PXE_BASE_CODE_PROTOCOL, "PXE base code" },
{ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL, "PXE base code callback" },
{ SERIAL_IO_PROTOCOL, "serial io" },
{ LOADED_IMAGE_PROTOCOL, "loaded image" },
"loaded image device path" },
{ EFI_ISA_IO_PROTOCOL_GUID, "ISA io" },
{ EFI_IDE_CONTROLLER_INIT_PROTOCOL_GUID, "IDE controller init" },
{ EFI_ISA_ACPI_PROTOCOL_GUID, "ISA ACPI" },
{ EFI_PCI_IO_PROTOCOL_GUID, "PCI" },
{ EFI_PCI_ROOT_IO_GUID, "PCI root" },
{ EFI_PCI_ENUMERATION_COMPLETE_GUID, "PCI enumeration" },
{ EFI_DRIVER_DIAGNOSTICS_PROTOCOL_GUID, "Driver diagnostics" },
{ EFI_DRIVER_DIAGNOSTICS2_PROTOCOL_GUID, "Driver diagnostics2" },
{ EFI_SIMPLE_POINTER_PROTOCOL_GUID, "simple pointer" },
{ EFI_ABSOLUTE_POINTER_PROTOCOL_GUID, "absolute pointer" },
{ EFI_VLAN_CONFIG_PROTOCOL_GUID, "VLAN config" },
{ EFI_ARP_SERVICE_BINDING_PROTOCOL_GUID, "ARP service binding" },
{ EFI_ARP_PROTOCOL_GUID, "ARP" },
{ EFI_IP4_SERVICE_BINDING_PROTOCOL, "IPv4 service binding" },
{ EFI_IP4_PROTOCOL, "IPv4" },
{ EFI_IP4_CONFIG_PROTOCOL_GUID, "IPv4 config" },
{ EFI_IP6_SERVICE_BINDING_PROTOCOL, "IPv6 service binding" },
{ EFI_IP6_PROTOCOL, "IPv6" },
{ EFI_IP6_CONFIG_PROTOCOL_GUID, "IPv6 config" },
{ EFI_UDP4_PROTOCOL, "UDPv4" },
{ EFI_UDP4_SERVICE_BINDING_PROTOCOL, "UDPv4 service binding" },
{ EFI_UDP6_PROTOCOL, "UDPv6" },
{ EFI_UDP6_SERVICE_BINDING_PROTOCOL, "UDPv6 service binding" },
{ EFI_TCP4_PROTOCOL, "TCPv4" },
{ EFI_TCP4_SERVICE_BINDING_PROTOCOL, "TCPv4 service binding" },
{ EFI_TCP6_PROTOCOL, "TCPv6" },
{ EFI_TCP6_SERVICE_BINDING_PROTOCOL, "TCPv6 service binding" },
{ EFI_PART_TYPE_EFI_SYSTEM_PART_GUID, "EFI System partition" },
{ EFI_PART_TYPE_LEGACY_MBR_GUID, "MBR legacy" },
{ EFI_DEVICE_TREE_GUID, "device tree" },
{ EFI_USB_IO_PROTOCOL_GUID, "USB io" },
{ EFI_USB2_HC_PROTOCOL_GUID, "USB2 HC" },
{ EFI_COMPONENT_NAME_PROTOCOL_GUID, "component name" },
{ EFI_COMPONENT_NAME2_PROTOCOL_GUID, "component name2" },
{ EFI_DRIVER_BINDING_PROTOCOL_GUID, "driver binding" },
{ EFI_DRIVER_CONFIGURATION_PROTOCOL_GUID, "driver configuration" },
{ EFI_DRIVER_CONFIGURATION2_PROTOCOL_GUID, "driver configuration2" },
{ EFI_DECOMPRESS_PROTOCOL_GUID, "decompress" },
{ EFI_EBC_INTERPRETER_PROTOCOL_GUID, "ebc interpreter" },
"network interface identifier" },
"network interface identifier_31" },
"managed network service binding" },
{ EFI_MANAGED_NETWORK_PROTOCOL_GUID, "managed network" },
{ EFI_FORM_BROWSER2_PROTOCOL_GUID, "form browser" },
{ EFI_HII_CONFIG_ROUTING_PROTOCOL_GUID, "HII config routing" },
{ EFI_HII_DATABASE_PROTOCOL_GUID, "HII database" },
{ EFI_HII_STRING_PROTOCOL_GUID, "HII string" },
{ EFI_HII_IMAGE_PROTOCOL_GUID, "HII image" },
{ EFI_HII_FONT_PROTOCOL_GUID, "HII font" },
{ EFI_HII_CONFIGURATION_ACCESS_PROTOCOL_GUID, "HII config" },
{ EFI_MTFTP4_SERVICE_BINDING_PROTOCOL_GUID, "MTFTP4 service binding" },
{ EFI_MTFTP4_PROTOCOL_GUID, "MTFTP4" },
{ EFI_MTFTP6_SERVICE_BINDING_PROTOCOL_GUID, "MTFTP6 service binding" },
{ EFI_MTFTP6_PROTOCOL_GUID, "MTFTP6" },
{ EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID, "DHCP4 service binding" },
{ EFI_DHCP4_PROTOCOL_GUID, "DHCP4" },
{ EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID, "DHCP6 service binding" },
{ EFI_DHCP6_PROTOCOL_GUID, "DHCP6" },
{ EFI_SCSI_IO_PROTOCOL_GUID, "SCSI io" },
{ EFI_SCSI_PASS_THRU_PROTOCOL_GUID, "SCSI pass thru" },
{ EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID, "SCSI pass thru ext" },
{ EFI_CAPSULE_ARCH_PROTOCOL_GUID, "Capsule arch" },
{ EFI_MONOTONIC_COUNTER_ARCH_PROTOCOL_GUID, "monotonic counter arch" },
{ EFI_REALTIME_CLOCK_ARCH_PROTOCOL_GUID, "realtime clock arch" },
{ EFI_VARIABLE_ARCH_PROTOCOL_GUID, "variable arch" },
{ EFI_VARIABLE_WRITE_ARCH_PROTOCOL_GUID, "variable write arch" },
{ EFI_WATCHDOG_TIMER_ARCH_PROTOCOL_GUID, "watchdog timer arch" },
{ EFI_MP_SERVICES_PROTOCOL_GUID, "MP services" },
{ EFI_ACPI_SUPPORT_PROTOCOL_GUID, "ACPI support" },
{ EFI_BDS_ARCH_PROTOCOL_GUID, "BDS arch" },
{ EFI_METRONOME_ARCH_PROTOCOL_GUID, "metronome arch" },
{ EFI_TIMER_ARCH_PROTOCOL_GUID, "timer arch" },
{ EFI_DPC_PROTOCOL_GUID, "DPC" },
{ EFI_PRINT2_PROTOCOL_GUID, "print2" },
{ EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID, "device path to text" },
{ EFI_RESET_ARCH_PROTOCOL_GUID, "reset arch" },
{ EFI_CPU_ARCH_PROTOCOL_GUID, "CPU arch" },
{ EFI_CPU_IO2_PROTOCOL_GUID, "CPU IO2" },
{ EFI_LEGACY_8259_PROTOCOL_GUID, "Legacy 8259" },
{ EFI_SECURITY_ARCH_PROTOCOL_GUID, "Security arch" },
{ EFI_SECURITY2_ARCH_PROTOCOL_GUID, "Security2 arch" },
{ EFI_RUNTIME_ARCH_PROTOCOL_GUID, "Runtime arch" },
{ EFI_STATUS_CODE_RUNTIME_PROTOCOL_GUID, "status code runtime" },
{ EFI_DATA_HUB_PROTOCOL_GUID, "data hub" },
{ PCD_PROTOCOL_GUID, "PCD" },
{ EFI_PCD_PROTOCOL_GUID, "EFI PCD" },
{ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL_GUID, "firmware volume block" },
{ EFI_FIRMWARE_VOLUME2_PROTOCOL_GUID, "firmware volume2" },
"firmware volume dispatch" },
{ LZMA_COMPRESS_GUID, "lzma compress" },
{ { 0,0,0,{0,0,0,0,0,0,0,0} }, NULL } /* must be last entry */
};
static int
{
int k, ret;
if (status != EFI_BUFFER_TOO_SMALL) {
"unexpected error: %lld", (long long)status);
return (CMD_ERROR);
}
return (CMD_ERROR);
}
"LocateHandle() error: %lld", (long long)status);
return (CMD_ERROR);
}
pager_open();
for (i = 0; i < (bufsz / sizeof (EFI_HANDLE)); i++) {
if (pager_output("\n"))
break;
/* device path */
"ProtocolsPerHandle() error: %lld",
(long long)status);
continue;
}
for (j = 0; j < nproto; j++) {
sizeof (proto_names[k].guid)) == 0)
break;
else
if (ret)
break;
}
if (ret)
break;
}
pager_close();
return (CMD_OK);
}
#ifdef EFI_ZFS_BOOT
static int
{
int err;
if (argc != 2) {
command_errmsg = "wrong number of arguments";
return (CMD_ERROR);
}
if (err != 0) {
return (CMD_ERROR);
}
return (CMD_OK);
}
#ifdef __FreeBSD__
static int
{
int err;
char *root;
if (argc > 2) {
command_errmsg = "wrong number of arguments";
return (CMD_ERROR);
}
if (argc == 2) {
} else {
return (CMD_OK);
}
}
if (err != 0) {
return (CMD_ERROR);
}
return (CMD_OK);
}
#endif /* __FreeBSD__ */
#endif
void
efi_serial_init(void)
{
int serial = 0;
/*
* get buffer size
*/
if (status != EFI_BUFFER_TOO_SMALL) {
"unexpected error: %lld", (long long)status);
return;
}
return;
}
/*
* get handle array
*/
"LocateHandle() error: %lld", (long long)status);
return;
}
for (i = 0; i < (bufsz / sizeof (EFI_HANDLE)); i++) {
"OpenProtocol() error: %lld", (long long)status);
}
}
}
#ifdef LOADER_FDT_SUPPORT
/*
* Since proper fdt command handling function is defined in fdt_loader_cmd.c,
* and declaring it as extern is in contradiction with COMMAND_SET() macro
* (which uses static pointer), we're defining wrapper function, which
* calls the proper fdt handling routine.
*/
static int
{
}
#endif
#ifdef EFI_ZFS_BOOT
static void
efi_zfs_probe(void)
{
EFI_HANDLE h;
int i;
unit = 0;
h = efi_find_handle(&efipart_dev, 0);
}
}
#endif