Changeset View
Changeset View
Standalone View
Standalone View
sys/boot/efi/loader/main.c
Show First 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | |||||
#ifdef EFI_ZFS_BOOT | #ifdef EFI_ZFS_BOOT | ||||
#include <libzfs.h> | #include <libzfs.h> | ||||
#endif | #endif | ||||
#include "loader_efi.h" | #include "loader_efi.h" | ||||
extern char bootprog_info[]; | extern char bootprog_info[]; | ||||
#ifdef BOOT_FORTH | |||||
/* | |||||
* Normally, efi.o from libefi.a would be brought in due to a function we call | |||||
* there that's defined there. However, none of its functions are callable from | |||||
* here since it just adds words to the FORTH environment or implement those | |||||
* words. So, add a reference to a symbol in efi.o to force it to be be brought | |||||
* in so the init function there gets added to the "compile" linker set happens | |||||
* correctly. | |||||
* | |||||
* This assumes there's no global analysys that notices dummy1 isn't used | |||||
* anywhere and tries to eliminate it. | |||||
*/ | |||||
extern int efi_variable_support; | |||||
int *dummy1 = &efi_variable_support; | |||||
#endif | |||||
struct arch_switch archsw; /* MI/MD interface boundary */ | struct arch_switch archsw; /* MI/MD interface boundary */ | ||||
EFI_GUID acpi = ACPI_TABLE_GUID; | EFI_GUID acpi = ACPI_TABLE_GUID; | ||||
EFI_GUID acpi20 = ACPI_20_TABLE_GUID; | EFI_GUID acpi20 = ACPI_20_TABLE_GUID; | ||||
EFI_GUID devid = DEVICE_PATH_PROTOCOL; | EFI_GUID devid = DEVICE_PATH_PROTOCOL; | ||||
EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; | EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; | ||||
EFI_GUID mps = MPS_TABLE_GUID; | EFI_GUID mps = MPS_TABLE_GUID; | ||||
EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL; | EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL; | ||||
EFI_GUID smbios = SMBIOS_TABLE_GUID; | EFI_GUID smbios = SMBIOS_TABLE_GUID; | ||||
EFI_GUID dxe = DXE_SERVICES_TABLE_GUID; | EFI_GUID dxe = DXE_SERVICES_TABLE_GUID; | ||||
EFI_GUID hoblist = HOB_LIST_TABLE_GUID; | EFI_GUID hoblist = HOB_LIST_TABLE_GUID; | ||||
EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID; | EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID; | ||||
EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID; | EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID; | ||||
EFI_GUID fdtdtb = FDT_TABLE_GUID; | EFI_GUID fdtdtb = FDT_TABLE_GUID; | ||||
EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL; | EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL; | ||||
#ifdef EFI_ZFS_BOOT | #ifdef EFI_ZFS_BOOT | ||||
static void efi_zfs_probe(void); | static void efi_zfs_probe(void); | ||||
#endif | #endif | ||||
/* | |||||
* cpy8to16 copies a traditional C string into a CHAR16 string and | |||||
* 0 terminates it. len is the size of *dst in bytes. | |||||
*/ | |||||
static void | |||||
cpy8to16(const char *src, CHAR16 *dst, size_t len) | |||||
{ | |||||
len <<= 1; /* Assume CHAR16 is 2 bytes */ | |||||
while (len > 0 && *src) { | |||||
*dst++ = *src++; | |||||
len--; | |||||
} | |||||
*dst++ = (CHAR16)0; | |||||
} | |||||
static void | |||||
cpy16to8(const CHAR16 *src, char *dst, size_t len) | |||||
{ | |||||
size_t i; | |||||
for (i = 0; i < len && src[i]; i++) | |||||
dst[i] = (char)src[i]; | |||||
if (i < len) | |||||
dst[i] = '\0'; | |||||
} | |||||
static int | static int | ||||
has_keyboard(void) | has_keyboard(void) | ||||
{ | { | ||||
EFI_STATUS status; | EFI_STATUS status; | ||||
EFI_DEVICE_PATH *path; | EFI_DEVICE_PATH *path; | ||||
EFI_HANDLE *hin, *hin_end, *walker; | EFI_HANDLE *hin, *hin_end, *walker; | ||||
UINTN sz; | UINTN sz; | ||||
int retval = 0; | int retval = 0; | ||||
▲ Show 20 Lines • Show All 325 Lines • ▼ Show 20 Lines | default: { | ||||
env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), | env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), | ||||
efi_setcurrdev, env_nounset); | efi_setcurrdev, env_nounset); | ||||
env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, | env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, | ||||
env_nounset); | env_nounset); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
snprintf(var, sizeof(var), "%d.%02d", ST->Hdr.Revision >> 16, | efi_init_environment(); | ||||
ST->Hdr.Revision & 0xffff); | |||||
env_setenv("efi-version", EV_VOLATILE, var, env_noset, env_nounset); | |||||
setenv("LINES", "24", 1); /* optional */ | setenv("LINES", "24", 1); /* optional */ | ||||
for (k = 0; k < ST->NumberOfTableEntries; k++) { | for (k = 0; k < ST->NumberOfTableEntries; k++) { | ||||
guid = &ST->ConfigurationTable[k].VendorGuid; | guid = &ST->ConfigurationTable[k].VendorGuid; | ||||
if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { | if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { | ||||
snprintf(buf, sizeof(buf), "%p", | snprintf(buf, sizeof(buf), "%p", | ||||
ST->ConfigurationTable[k].VendorTable); | ST->ConfigurationTable[k].VendorTable); | ||||
setenv("hint.smbios.0.mem", buf, 1); | setenv("hint.smbios.0.mem", buf, 1); | ||||
smbios_detect(ST->ConfigurationTable[k].VendorTable); | smbios_detect(ST->ConfigurationTable[k].VendorTable); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
interact(NULL); /* doesn't return */ | interact(NULL); /* doesn't return */ | ||||
return (EFI_SUCCESS); /* keep compiler happy */ | return (EFI_SUCCESS); /* keep compiler happy */ | ||||
} | } | ||||
/* XXX move to lib stand ? */ | |||||
static int | |||||
wcscmp(CHAR16 *a, CHAR16 *b) | |||||
{ | |||||
while (*a && *b && *a == *b) { | |||||
a++; | |||||
b++; | |||||
} | |||||
return *a - *b; | |||||
} | |||||
COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); | COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); | ||||
static int | static int | ||||
command_reboot(int argc, char *argv[]) | command_reboot(int argc, char *argv[]) | ||||
{ | { | ||||
int i; | int i; | ||||
for (i = 0; devsw[i] != NULL; ++i) | for (i = 0; devsw[i] != NULL; ++i) | ||||
▲ Show 20 Lines • Show All 264 Lines • ▼ Show 20 Lines | command_reloadbe(int argc, char *argv[]) | ||||
if (err != 0) { | if (err != 0) { | ||||
command_errmsg = strerror(err); | command_errmsg = strerror(err); | ||||
return (CMD_ERROR); | return (CMD_ERROR); | ||||
} | } | ||||
return (CMD_OK); | return (CMD_OK); | ||||
} | } | ||||
#endif | #endif | ||||
COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show); | |||||
static int | |||||
efi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag) | |||||
{ | |||||
UINTN datasz, i; | |||||
EFI_STATUS status; | |||||
UINT32 attr; | |||||
CHAR16 *data; | |||||
char *str; | |||||
uint32_t uuid_status; | |||||
int is_ascii; | |||||
datasz = 0; | |||||
status = RS->GetVariable(varnamearg, matchguid, &attr, | |||||
&datasz, NULL); | |||||
if (status != EFI_BUFFER_TOO_SMALL) { | |||||
printf("Can't get the variable: error %#lx\n", status); | |||||
return (CMD_ERROR); | |||||
} | |||||
data = malloc(datasz); | |||||
status = RS->GetVariable(varnamearg, matchguid, &attr, | |||||
&datasz, data); | |||||
if (status != EFI_SUCCESS) { | |||||
printf("Can't get the variable: error %#lx\n", status); | |||||
return (CMD_ERROR); | |||||
} | |||||
uuid_to_string((uuid_t *)matchguid, &str, &uuid_status); | |||||
if (lflag) { | |||||
printf("%s 0x%x %S", str, attr, varnamearg); | |||||
} else { | |||||
printf("%s 0x%x %S=", str, attr, varnamearg); | |||||
is_ascii = 1; | |||||
free(str); | |||||
str = (char *)data; | |||||
for (i = 0; i < datasz - 1; i++) { | |||||
/* Quick hack to see if this ascii-ish string printable range plus tab, cr and lf */ | |||||
if ((str[i] < 32 || str[i] > 126) && str[i] != 9 && str[i] != 10 && str[i] != 13) { | |||||
is_ascii = 0; | |||||
break; | |||||
} | |||||
} | |||||
if (str[datasz - 1] != '\0') | |||||
is_ascii = 0; | |||||
if (is_ascii) | |||||
printf("%s", str); | |||||
else { | |||||
for (i = 0; i < datasz / 2; i++) { | |||||
if (isalnum(data[i]) || isspace(data[i])) | |||||
printf("%c", data[i]); | |||||
else | |||||
printf("\\x%02x", data[i]); | |||||
} | |||||
} | |||||
} | |||||
free(data); | |||||
if (pager_output("\n")) | |||||
return (CMD_WARN); | |||||
return (CMD_OK); | |||||
} | |||||
static int | |||||
command_efi_show(int argc, char *argv[]) | |||||
{ | |||||
/* | |||||
* efi-show [-a] | |||||
* print all the env | |||||
* efi-show -u UUID | |||||
* print all the env vars tagged with UUID | |||||
* efi-show -v var | |||||
* search all the env vars and print the ones matching var | |||||
* eif-show -u UUID -v var | |||||
* eif-show UUID var | |||||
* print all the env vars that match UUID and var | |||||
*/ | |||||
/* NB: We assume EFI_GUID is the same as uuid_t */ | |||||
int aflag = 0, gflag = 0, lflag = 0, vflag = 0; | |||||
int ch, rv; | |||||
unsigned i; | |||||
EFI_STATUS status; | |||||
EFI_GUID varguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; | |||||
EFI_GUID matchguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; | |||||
uint32_t uuid_status; | |||||
CHAR16 *varname; | |||||
CHAR16 *newnm; | |||||
CHAR16 varnamearg[128]; | |||||
UINTN varalloc; | |||||
UINTN varsz; | |||||
while ((ch = getopt(argc, argv, "ag:lv:")) != -1) { | |||||
switch (ch) { | |||||
case 'a': | |||||
aflag = 1; | |||||
break; | |||||
case 'g': | |||||
gflag = 1; | |||||
uuid_from_string(optarg, (uuid_t *)&matchguid, | |||||
&uuid_status); | |||||
if (uuid_status != uuid_s_ok) { | |||||
printf("uid %s could not be parsed\n", optarg); | |||||
return (CMD_ERROR); | |||||
} | |||||
break; | |||||
case 'l': | |||||
lflag = 1; | |||||
break; | |||||
case 'v': | |||||
vflag = 1; | |||||
if (strlen(optarg) >= nitems(varnamearg)) { | |||||
printf("Variable %s is longer than %zd characters\n", | |||||
optarg, nitems(varnamearg)); | |||||
return (CMD_ERROR); | |||||
} | |||||
for (i = 0; i < strlen(optarg); i++) | |||||
varnamearg[i] = optarg[i]; | |||||
varnamearg[i] = 0; | |||||
break; | |||||
default: | |||||
printf("Invalid argument %c\n", ch); | |||||
return (CMD_ERROR); | |||||
} | |||||
} | |||||
if (aflag && (gflag || vflag)) { | |||||
printf("-a isn't compatible with -v or -u\n"); | |||||
return (CMD_ERROR); | |||||
} | |||||
if (aflag && optind < argc) { | |||||
printf("-a doesn't take any args"); | |||||
return (CMD_ERROR); | |||||
} | |||||
if (optind == argc) | |||||
aflag = 1; | |||||
argc -= optind; | |||||
argv += optind; | |||||
pager_open(); | |||||
if (vflag && gflag) { | |||||
rv = efi_print_var(varnamearg, &matchguid, lflag); | |||||
pager_close(); | |||||
return (rv); | |||||
} | |||||
if (argc == 2) { | |||||
optarg = argv[0]; | |||||
if (strlen(optarg) >= nitems(varnamearg)) { | |||||
printf("Variable %s is longer than %zd characters\n", | |||||
optarg, nitems(varnamearg)); | |||||
pager_close(); | |||||
return (CMD_ERROR); | |||||
} | |||||
for (i = 0; i < strlen(optarg); i++) | |||||
varnamearg[i] = optarg[i]; | |||||
varnamearg[i] = 0; | |||||
optarg = argv[1]; | |||||
uuid_from_string(optarg, (uuid_t *)&matchguid, | |||||
&uuid_status); | |||||
if (uuid_status != uuid_s_ok) { | |||||
printf("uid %s could not be parsed\n", optarg); | |||||
pager_close(); | |||||
return (CMD_ERROR); | |||||
} | |||||
rv = efi_print_var(varnamearg, &matchguid, lflag); | |||||
pager_close(); | |||||
return (rv); | |||||
} | |||||
if (argc > 0) { | |||||
printf("Too many args %d\n", argc); | |||||
pager_close(); | |||||
return (CMD_ERROR); | |||||
} | |||||
/* | |||||
* Initiate the search -- note the standard takes pain | |||||
* to specify the initial call must be a poiner to a NULL | |||||
* character. | |||||
*/ | |||||
varalloc = 1024; | |||||
varname = malloc(varalloc); | |||||
if (varname == NULL) { | |||||
printf("Can't allocate memory to get variables\n"); | |||||
pager_close(); | |||||
return (CMD_ERROR); | |||||
} | |||||
varname[0] = 0; | |||||
while (1) { | |||||
varsz = varalloc; | |||||
status = RS->GetNextVariableName(&varsz, varname, &varguid); | |||||
if (status == EFI_BUFFER_TOO_SMALL) { | |||||
varalloc = varsz; | |||||
newnm = malloc(varalloc); | |||||
if (newnm == NULL) { | |||||
printf("Can't allocate memory to get variables\n"); | |||||
free(varname); | |||||
pager_close(); | |||||
return (CMD_ERROR); | |||||
} | |||||
memcpy(newnm, varname, varsz); | |||||
free(varname); | |||||
varname = newnm; | |||||
continue; /* Try again with bigger buffer */ | |||||
} | |||||
if (status != EFI_SUCCESS) | |||||
break; | |||||
if (aflag) { | |||||
if (efi_print_var(varname, &varguid, lflag) != CMD_OK) | |||||
break; | |||||
continue; | |||||
} | |||||
if (vflag) { | |||||
if (wcscmp(varnamearg, varname) == 0) { | |||||
if (efi_print_var(varname, &varguid, lflag) != CMD_OK) | |||||
break; | |||||
continue; | |||||
} | |||||
} | |||||
if (gflag) { | |||||
if (memcmp(&varguid, &matchguid, sizeof(varguid)) == 0) { | |||||
if (efi_print_var(varname, &varguid, lflag) != CMD_OK) | |||||
break; | |||||
continue; | |||||
} | |||||
} | |||||
} | |||||
free(varname); | |||||
pager_close(); | |||||
return (CMD_OK); | |||||
} | |||||
COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set); | |||||
static int | |||||
command_efi_set(int argc, char *argv[]) | |||||
{ | |||||
char *uuid, *var, *val; | |||||
CHAR16 wvar[128]; | |||||
EFI_GUID guid; | |||||
uint32_t status; | |||||
EFI_STATUS err; | |||||
if (argc != 4) { | |||||
printf("efi-set uuid var new-value\n"); | |||||
return (CMD_ERROR); | |||||
} | |||||
uuid = argv[1]; | |||||
var = argv[2]; | |||||
val = argv[3]; | |||||
uuid_from_string(uuid, (uuid_t *)&guid, &status); | |||||
if (status != uuid_s_ok) { | |||||
printf("Invalid uuid %s %d\n", uuid, status); | |||||
return (CMD_ERROR); | |||||
} | |||||
cpy8to16(var, wvar, sizeof(wvar)); | |||||
err = RS->SetVariable(wvar, &guid, | |||||
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, | |||||
strlen(val) + 1, val); | |||||
if (EFI_ERROR(err)) { | |||||
printf("Failed to set variable: error %lu\n", EFI_ERROR_CODE(err)); | |||||
return (CMD_ERROR); | |||||
} | |||||
return (CMD_OK); | |||||
} | |||||
COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset); | |||||
static int | |||||
command_efi_unset(int argc, char *argv[]) | |||||
{ | |||||
char *uuid, *var; | |||||
CHAR16 wvar[128]; | |||||
EFI_GUID guid; | |||||
uint32_t status; | |||||
EFI_STATUS err; | |||||
if (argc != 3) { | |||||
printf("efi-unset uuid var\n"); | |||||
return (CMD_ERROR); | |||||
} | |||||
uuid = argv[1]; | |||||
var = argv[2]; | |||||
uuid_from_string(uuid, (uuid_t *)&guid, &status); | |||||
if (status != uuid_s_ok) { | |||||
printf("Invalid uuid %s\n", uuid); | |||||
return (CMD_ERROR); | |||||
} | |||||
cpy8to16(var, wvar, sizeof(wvar)); | |||||
err = RS->SetVariable(wvar, &guid, 0, 0, NULL); | |||||
if (EFI_ERROR(err)) { | |||||
printf("Failed to unset variable: error %lu\n", EFI_ERROR_CODE(err)); | |||||
return (CMD_ERROR); | |||||
} | |||||
return (CMD_OK); | |||||
} | |||||
#ifdef LOADER_FDT_SUPPORT | #ifdef LOADER_FDT_SUPPORT | ||||
extern int command_fdt_internal(int argc, char *argv[]); | extern int command_fdt_internal(int argc, char *argv[]); | ||||
/* | /* | ||||
* Since proper fdt command handling function is defined in fdt_loader_cmd.c, | * 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 | * and declaring it as extern is in contradiction with COMMAND_SET() macro | ||||
* (which uses static pointer), we're defining wrapper function, which | * (which uses static pointer), we're defining wrapper function, which | ||||
Show All 31 Lines |