Changeset View
Standalone View
sys/boot/efi/loader/main.c
Show All 33 Lines | |||||
#include <efi.h> | #include <efi.h> | ||||
#include <efilib.h> | #include <efilib.h> | ||||
#include <bootstrap.h> | #include <bootstrap.h> | ||||
#include <smbios.h> | #include <smbios.h> | ||||
#include "loader_efi.h" | #include "loader_efi.h" | ||||
#include "libzfs.h" | |||||
extern char bootprog_name[]; | extern char bootprog_name[]; | ||||
extern char bootprog_rev[]; | extern char bootprog_rev[]; | ||||
extern char bootprog_date[]; | extern char bootprog_date[]; | ||||
extern char bootprog_maker[]; | extern char bootprog_maker[]; | ||||
struct devdesc currdev; /* our current device */ | /* our current device */ | ||||
struct arch_switch archsw; /* MI/MD interface boundary */ | /* MI/MD interface boundary */ | ||||
struct arch_switch archsw; | |||||
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; | ||||
static void efi_zfs_probe(void); | |||||
static void | |||||
delphij: Perhaps comment here that we didn't use printf("%s") because there are potential \0's, and we… | |||||
print_str16(const CHAR16* const str) | |||||
{ | |||||
for(int i; str[i]; i++) | |||||
{ | |||||
printf("%c", str[i]); | |||||
delphijUnsubmitted Not Done Inline ActionsI'd explicitly truncate str[i] to char here but consider this optional. delphij: I'd explicitly truncate str[i] to char here but consider this optional. | |||||
} | |||||
} | |||||
Not Done Inline ActionsBlank line before this, space before (. delphij: Blank line before this, space before (. | |||||
/* | |||||
Not Done Inline Actions{ } should not be used here per style(9). delphij: { } should not be used here per style(9). | |||||
static int | |||||
str16cmp(const CHAR16 const *a, | |||||
const char* const b) | |||||
{ | |||||
for(int i = 0; a[i] || b[i]; i++) | |||||
{ | |||||
const CHAR16 achr = a[i]; | |||||
const CHAR16 bchr = b[i]; | |||||
if (achr < bchr) | |||||
{ | |||||
return -1; | |||||
} else if (achr > bchr) | |||||
{ | |||||
return 1; | |||||
} | |||||
} | |||||
return 0; | |||||
} | |||||
// Split an arg of the form "argname=argval", replacing the '=' with a \0 | |||||
static CHAR16* | |||||
split_arg(CHAR16 *const str) | |||||
{ | |||||
for (int i = 0; str[i]; i++) | |||||
{ | |||||
if ('=' == str[i]) | |||||
{ | |||||
str[i] = 0; | |||||
return str + i + 1; | |||||
} | |||||
} | |||||
return NULL; | |||||
} | |||||
static void | |||||
handle_arg(CHAR16 *const arg) | |||||
{ | |||||
const CHAR16* const argval = split_arg(arg); | |||||
const CHAR16* const argname = arg; | |||||
if (NULL != argval) | |||||
{ | |||||
printf("Unrecognized argument \""); | |||||
print_arg(argname); | |||||
printf("\n"); | |||||
} else { | |||||
printf("Unrecognized argument \""); | |||||
print_arg(argname); | |||||
printf("\n"); | |||||
} | |||||
} | |||||
*/ | |||||
EFI_STATUS | EFI_STATUS | ||||
main(int argc, CHAR16 *argv[]) | main(int argc, CHAR16 *argv[]) | ||||
{ | { | ||||
char vendor[128]; | char vendor[128]; | ||||
EFI_LOADED_IMAGE *img; | EFI_LOADED_IMAGE *img; | ||||
EFI_GUID *guid; | EFI_GUID *guid; | ||||
int i; | int i; | ||||
archsw.arch_autoload = efi_autoload; | |||||
archsw.arch_getdev = efi_getdev; | |||||
archsw.arch_copyin = efi_copyin; | |||||
archsw.arch_copyout = efi_copyout; | |||||
archsw.arch_readin = efi_readin; | |||||
// Note this needs to be set before ZFS init | |||||
archsw.arch_zfs_probe = efi_zfs_probe; | |||||
/* | /* | ||||
* XXX Chicken-and-egg problem; we want to have console output | * XXX Chicken-and-egg problem; we want to have console output | ||||
* early, but some console attributes may depend on reading from | * early, but some console attributes may depend on reading from | ||||
* eg. the boot device, which we can't do yet. We can use | * eg. the boot device, which we can't do yet. We can use | ||||
* printf() etc. once this is done. | * printf() etc. once this is done. | ||||
*/ | */ | ||||
cons_probe(); | cons_probe(); | ||||
if (efi_copy_init()) { | if (efi_copy_init()) { | ||||
Not Done Inline ActionsThese should be at the top of the function andrew: These should be at the top of the function | |||||
printf("failed to allocate staging area\n"); | printf("failed to allocate staging area\n"); | ||||
return (EFI_BUFFER_TOO_SMALL); | return (EFI_BUFFER_TOO_SMALL); | ||||
} | } | ||||
Not Done Inline Actions{ should not be used here. delphij: { should not be used here. | |||||
/* | /* | ||||
Not Done Inline ActionsBlank line. delphij: Blank line. | |||||
* March through the device switch probing for things. | * March through the device switch probing for things. | ||||
*/ | */ | ||||
for (i = 0; devsw[i] != NULL; i++) | for (i = 0; devsw[i] != NULL; i++) { | ||||
if (devsw[i]->dv_init != NULL) | if (devsw[i]->dv_init != NULL) { | ||||
printf("Initializing %s\n", devsw[i]->dv_name); | |||||
(devsw[i]->dv_init)(); | (devsw[i]->dv_init)(); | ||||
} | |||||
} | |||||
/* Get our loaded image protocol interface structure. */ | /* Get our loaded image protocol interface structure. */ | ||||
BS->HandleProtocol(IH, &imgid, (VOID**)&img); | BS->HandleProtocol(IH, &imgid, (VOID**)&img); | ||||
Not Done Inline ActionsDon't use } here. delphij: Don't use } here. | |||||
Not Done Inline ActionsNo { here. Indent break;. delphij: No { here. Indent break;. | |||||
printf("Command line arguments:"); | |||||
for(i = 0; i < argc; i++) { | |||||
printf(" "); | |||||
print_str16(argv[i]); | |||||
} | |||||
printf("\n"); | |||||
printf("Image base: 0x%lx\n", (u_long)img->ImageBase); | printf("Image base: 0x%lx\n", (u_long)img->ImageBase); | ||||
printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16, | printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16, | ||||
ST->Hdr.Revision & 0xffff); | ST->Hdr.Revision & 0xffff); | ||||
Not Done Inline ActionsNo } here, indent break. delphij: No } here, indent break. | |||||
printf("EFI Firmware: "); | printf("EFI Firmware: "); | ||||
/* printf doesn't understand EFI Unicode */ | /* printf doesn't understand EFI Unicode */ | ||||
ST->ConOut->OutputString(ST->ConOut, ST->FirmwareVendor); | ST->ConOut->OutputString(ST->ConOut, ST->FirmwareVendor); | ||||
printf(" (rev %d.%02d)\n", ST->FirmwareRevision >> 16, | printf(" (rev %d.%02d)\n", ST->FirmwareRevision >> 16, | ||||
ST->FirmwareRevision & 0xffff); | ST->FirmwareRevision & 0xffff); | ||||
printf("\n"); | printf("\n"); | ||||
printf("%s, Revision %s\n", bootprog_name, bootprog_rev); | printf("%s, Revision %s\n", bootprog_name, bootprog_rev); | ||||
printf("(%s, %s)\n", bootprog_maker, bootprog_date); | printf("(%s, %s)\n", bootprog_maker, bootprog_date); | ||||
efi_handle_lookup(img->DeviceHandle, &currdev.d_dev, &currdev.d_unit); | // Handle command-line arguments | ||||
currdev.d_type = currdev.d_dev->dv_type; | /* | ||||
for(i = 1; i < argc; i++) | |||||
{ | |||||
handle_arg(argv[i]); | |||||
} | |||||
*/ | |||||
/* | /* | ||||
* Disable the watchdog timer. By default the boot manager sets | * Disable the watchdog timer. By default the boot manager sets | ||||
* the timer to 5 minutes before invoking a boot option. If we | * the timer to 5 minutes before invoking a boot option. If we | ||||
* want to return to the boot manager, we have to disable the | * want to return to the boot manager, we have to disable the | ||||
* watchdog timer and since we're an interactive program, we don't | * watchdog timer and since we're an interactive program, we don't | ||||
* want to wait until the user types "quit". The timer may have | * 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 | * fired by then. We don't care if this fails. It does not prevent | ||||
* normal functioning in any way... | * normal functioning in any way... | ||||
*/ | */ | ||||
BS->SetWatchdogTimer(0, 0, 0, NULL); | BS->SetWatchdogTimer(0, 0, 0, NULL); | ||||
struct devsw *dev; | |||||
int unit; | |||||
uint64_t pool_guid; | |||||
efi_handle_lookup(img->DeviceHandle, &dev, &unit, &pool_guid); | |||||
switch (dev->dv_type) { | |||||
case DEVT_ZFS: { | |||||
struct zfs_devdesc currdev; | |||||
currdev.d_dev = dev; | |||||
currdev.d_unit = unit; | |||||
currdev.d_type = currdev.d_dev->dv_type; | |||||
currdev.d_opendata = NULL; | |||||
currdev.pool_guid = pool_guid; | |||||
currdev.root_guid = 0; | |||||
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; | |||||
default: { | |||||
struct devdesc currdev; | |||||
currdev.d_dev = dev; | |||||
currdev.d_unit = unit; | |||||
currdev.d_opendata = NULL; | |||||
currdev.d_type = currdev.d_dev->dv_type; | |||||
env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), | |||||
efi_setcurrdev, env_nounset); | |||||
env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, | |||||
env_nounset); | |||||
} break; | |||||
} | |||||
setenv("LINES", "24", 1); /* optional */ | setenv("LINES", "24", 1); /* optional */ | ||||
archsw.arch_autoload = efi_autoload; | |||||
archsw.arch_getdev = efi_getdev; | |||||
archsw.arch_copyin = efi_copyin; | |||||
archsw.arch_copyout = efi_copyout; | |||||
archsw.arch_readin = efi_readin; | |||||
for (i = 0; i < ST->NumberOfTableEntries; i++) { | for (i = 0; i < ST->NumberOfTableEntries; i++) { | ||||
guid = &ST->ConfigurationTable[i].VendorGuid; | guid = &ST->ConfigurationTable[i].VendorGuid; | ||||
if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { | if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { | ||||
smbios_detect(ST->ConfigurationTable[i].VendorTable); | smbios_detect(ST->ConfigurationTable[i].VendorTable); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 202 Lines • ▼ Show 20 Lines | command_mode(int argc, char *argv[]) | ||||
} | } | ||||
if (i != 0) | if (i != 0) | ||||
printf("Select a mode with the command \"mode <number>\"\n"); | printf("Select a mode with the command \"mode <number>\"\n"); | ||||
return (CMD_OK); | return (CMD_OK); | ||||
} | } | ||||
COMMAND_SET(nvram, "nvram", "get or set NVRAM variables", command_nvram); | COMMAND_SET(nvram, "nvram", "get or set NVRAM variables", command_nvram); | ||||
static int | static int | ||||
command_nvram(int argc, char *argv[]) | command_nvram(int argc, char *argv[]) | ||||
{ | { | ||||
CHAR16 var[128]; | CHAR16 var[128]; | ||||
CHAR16 *data; | CHAR16 *data; | ||||
EFI_STATUS status; | EFI_STATUS status; | ||||
Show All 28 Lines | else { | ||||
for (i = 0; i < datasz; i++) { | for (i = 0; i < datasz; i++) { | ||||
if (isalnum(data[i]) || isspace(data[i])) | if (isalnum(data[i]) || isspace(data[i])) | ||||
printf("%c", data[i]); | printf("%c", data[i]); | ||||
else | else | ||||
printf("\\x%02x", data[i]); | printf("\\x%02x", data[i]); | ||||
} | } | ||||
} | } | ||||
/* XXX */ | /* XXX */ | ||||
pager_output("\n"); | pager_output("\n"); | ||||
free(data); | free(data); | ||||
Not Done Inline ActionsThere is a style bug here, the int i declaration should be in the group above. There is a second subtle issue. You are redefining h to be an int. efi_find_handle returns an EFI_HANDLE, aka a void *. On a 64-bit machine this will truncate the pointer. When used later it will be sign extended. This is an issue if the handle has bit 31 set (as is the case on my laptop) as the handle will have the top 32 bits set incorrectly. It would also be an issue if any of the top bits were used in the handle. The fix for this is the same as the style issue. andrew: There is a style bug here, the `int i` declaration should be in the group above.
There is a… | |||||
} | } | ||||
return (CMD_OK); | return (CMD_OK); | ||||
Not Done Inline Actionsif (zfs_probe_dev(..) == 0) { andrew: `if (zfs_probe_dev(..) == 0) {` | |||||
} | } | ||||
COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset", | |||||
command_lszfs); | |||||
static int | |||||
command_lszfs(int argc, char *argv[]) | |||||
{ | |||||
int err; | |||||
if (argc != 2) { | |||||
command_errmsg = "wrong number of arguments"; | |||||
return (CMD_ERROR); | |||||
} | |||||
err = zfs_list(argv[1]); | |||||
if (err != 0) { | |||||
command_errmsg = strerror(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 | ||||
* calls the proper fdt handling routine. | * calls the proper fdt handling routine. | ||||
*/ | */ | ||||
static int | static int | ||||
command_fdt(int argc, char *argv[]) | command_fdt(int argc, char *argv[]) | ||||
{ | { | ||||
return (command_fdt_internal(argc, argv)); | return (command_fdt_internal(argc, argv)); | ||||
} | } | ||||
COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); | COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); | ||||
#endif | #endif | ||||
static void | |||||
efi_zfs_probe(void) | |||||
{ | |||||
EFI_BLOCK_IO *blkio; | |||||
EFI_HANDLE h; | |||||
EFI_STATUS status; | |||||
u_int unit = 0; | |||||
char devname[32]; | |||||
uint64_t pool_guid; | |||||
for (int i = 0, h = efi_find_handle(&efipart_dev, 0); | |||||
h != NULL; h = efi_find_handle(&efipart_dev, ++i)) { | |||||
snprintf(devname, sizeof devname, "%s%d:", | |||||
efipart_dev.dv_name, i); | |||||
if(0 == zfs_probe_dev(devname, &pool_guid)) { | |||||
efi_handle_update_dev(h, &zfs_dev, unit++, pool_guid); | |||||
} | |||||
} | |||||
} |
Perhaps comment here that we didn't use printf("%s") because there are potential \0's, and we don't support real wide character either?