Changeset View
Changeset View
Standalone View
Standalone View
stand/efi/loader/main.c
Show First 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | |||||
#include <smbios.h> | #include <smbios.h> | ||||
#ifdef EFI_ZFS_BOOT | #ifdef EFI_ZFS_BOOT | ||||
#include <libzfs.h> | #include <libzfs.h> | ||||
#include "efizfs.h" | #include "efizfs.h" | ||||
#endif | #endif | ||||
#include "efi_drivers.h" | |||||
#include "loader_efi.h" | #include "loader_efi.h" | ||||
extern char bootprog_info[]; | extern char bootprog_info[]; | ||||
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; | ||||
static EFI_LOADED_IMAGE *img; | static EFI_LOADED_IMAGE *img; | ||||
static EFI_DEVICE_PATH *imgpath; | |||||
static EFI_DEVICE_PATH *imgprefix; | |||||
#ifdef EFI_ZFS_BOOT | |||||
bool | |||||
efi_zfs_is_preferred(EFI_HANDLE *h) | |||||
{ | |||||
return (h == img->DeviceHandle); | |||||
} | |||||
#endif | |||||
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 66 Lines • ▼ Show 20 Lines | while (!IsDevicePathEnd(path)) { | ||||
path = NextDevicePathNode(path); | path = NextDevicePathNode(path); | ||||
} | } | ||||
} | } | ||||
out: | out: | ||||
free(hin); | free(hin); | ||||
return retval; | return retval; | ||||
} | } | ||||
/* Check if this is a preferred device */ | |||||
#ifndef LOADER_CHECK_ALL_DEVS | |||||
static bool | |||||
check_preferred(EFI_HANDLE *h) | |||||
{ | |||||
EFI_DEVICE_PATH *path = efi_lookup_devpath(h); | |||||
bool out; | |||||
if ((path = efi_lookup_devpath(h)) == NULL) | |||||
return (false); | |||||
out = efi_devpath_is_prefix(imgpath, path) || | |||||
efi_devpath_is_prefix(imgprefix, path); | |||||
return (out); | |||||
} | |||||
static bool | |||||
zfs_check_preferred(uint64_t check_guid) | |||||
{ | |||||
return (pool_guid != 0 && check_guid == pool_guid); | |||||
} | |||||
#else | |||||
static bool | |||||
check_preferred(EFI_HANDLE *h) | |||||
{ | |||||
return (true); | |||||
} | |||||
static bool | |||||
zfs_check_preferred(uint64_t check_guid) | |||||
{ | |||||
return (true); | |||||
} | |||||
#endif | |||||
static void | static void | ||||
set_vars(struct devdesc *currdev) | |||||
{ | |||||
char *devname; | |||||
devname = efi_fmtdev(currdev); | |||||
env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, | |||||
env_nounset); | |||||
env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset); | |||||
} | |||||
static void | |||||
set_devdesc_currdev(struct devsw *dev, int unit) | set_devdesc_currdev(struct devsw *dev, int unit) | ||||
{ | { | ||||
struct devdesc currdev; | struct devdesc currdev; | ||||
char *devname; | |||||
currdev.d_dev = dev; | currdev.d_dev = dev; | ||||
currdev.d_type = currdev.d_dev->dv_type; | currdev.d_type = currdev.d_dev->dv_type; | ||||
currdev.d_unit = unit; | currdev.d_unit = unit; | ||||
currdev.d_opendata = NULL; | currdev.d_opendata = NULL; | ||||
devname = efi_fmtdev(&currdev); | set_vars(&currdev); | ||||
} | |||||
env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, | /* Directly check a devdesc for an installed system */ | ||||
env_nounset); | static int | ||||
env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset); | check_devdesc(struct devdesc *currdev) { | ||||
/* Files that indicate the presence of an installed system */ | |||||
static const char* paths[] = { | |||||
"/boot/loader.conf", | |||||
"/boot/kernel", | |||||
NULL | |||||
}; | |||||
struct stat sb; | |||||
int i, err; | |||||
/* Check for the presence of any of the files */ | |||||
for (i = 0; paths[i] != NULL; i++) { | |||||
err = stat(paths[i], &sb); | |||||
if (errno != ENOENT) { | |||||
return (err); | |||||
} | } | ||||
} | |||||
return (err); | |||||
} | |||||
/* Set up a dev and then check it for an installed system */ | |||||
static int | static int | ||||
find_currdev(EFI_LOADED_IMAGE *img) | check_dev(struct devsw *dev, int unit) { | ||||
struct devdesc currdev; | |||||
currdev.d_dev = dev; | |||||
currdev.d_type = currdev.d_dev->dv_type; | |||||
currdev.d_unit = unit; | |||||
currdev.d_opendata = NULL; | |||||
set_vars(&currdev); | |||||
return (check_devdesc(&currdev)); | |||||
} | |||||
static int | |||||
find_currdev(void) | |||||
{ | { | ||||
pdinfo_list_t *pdi_list; | pdinfo_list_t *pdi_list; | ||||
pdinfo_t *dp, *pp; | pdinfo_t *dp, *pp; | ||||
EFI_DEVICE_PATH *devpath, *copy; | zfsinfo_list_t *zfsi_list; | ||||
EFI_HANDLE h; | zfsinfo_t *zi; | ||||
char *devname; | char *devname; | ||||
struct devsw *dev; | |||||
int unit; | |||||
uint64_t extra; | |||||
#ifdef EFI_ZFS_BOOT | #ifdef EFI_ZFS_BOOT | ||||
/* Did efi_zfs_probe() detect the boot pool? */ | zfsi_list = efizfs_get_zfsinfo_list(); | ||||
if (pool_guid != 0) { | STAILQ_FOREACH(zi, zfsi_list, zi_link) { | ||||
if (zfs_check_preferred(zi->zi_pool_guid)) { | |||||
struct zfs_devdesc currdev; | struct zfs_devdesc currdev; | ||||
currdev.d_dev = &zfs_dev; | currdev.d_dev = &zfs_dev; | ||||
currdev.d_unit = 0; | currdev.d_unit = 0; | ||||
currdev.d_type = currdev.d_dev->dv_type; | currdev.d_type = currdev.d_dev->dv_type; | ||||
currdev.d_opendata = NULL; | currdev.d_opendata = NULL; | ||||
currdev.pool_guid = pool_guid; | currdev.pool_guid = zi->zi_pool_guid; | ||||
currdev.root_guid = 0; | currdev.root_guid = 0; | ||||
devname = efi_fmtdev(&currdev); | devname = efi_fmtdev(&currdev); | ||||
set_vars((struct devdesc*)(&currdev)); | |||||
env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, | |||||
env_nounset); | |||||
env_setenv("loaddev", EV_VOLATILE, devname, env_noset, | |||||
env_nounset); | |||||
init_zfs_bootenv(devname); | init_zfs_bootenv(devname); | ||||
if (check_devdesc((struct devdesc*)(&currdev)) == 0) | |||||
return (0); | return (0); | ||||
} | } | ||||
} | |||||
#endif /* EFI_ZFS_BOOT */ | #endif /* EFI_ZFS_BOOT */ | ||||
/* We have device lists for hd, cd, fd, walk them all. */ | /* We have device lists for hd, cd, fd, walk them all. */ | ||||
pdi_list = efiblk_get_pdinfo_list(&efipart_hddev); | pdi_list = efiblk_get_pdinfo_list(&efipart_hddev); | ||||
STAILQ_FOREACH(dp, pdi_list, pd_link) { | STAILQ_FOREACH(dp, pdi_list, pd_link) { | ||||
struct disk_devdesc currdev; | struct disk_devdesc currdev; | ||||
currdev.d_dev = &efipart_hddev; | currdev.d_dev = &efipart_hddev; | ||||
currdev.d_type = currdev.d_dev->dv_type; | currdev.d_type = currdev.d_dev->dv_type; | ||||
currdev.d_unit = dp->pd_unit; | currdev.d_unit = dp->pd_unit; | ||||
currdev.d_opendata = NULL; | currdev.d_opendata = NULL; | ||||
currdev.d_slice = -1; | currdev.d_slice = -1; | ||||
currdev.d_partition = -1; | currdev.d_partition = -1; | ||||
set_vars((struct devdesc*)(&currdev)); | |||||
if (dp->pd_handle == img->DeviceHandle) { | if (check_preferred(dp->pd_handle) && | ||||
devname = efi_fmtdev(&currdev); | check_devdesc((struct devdesc*)(&currdev)) == 0) | ||||
env_setenv("currdev", EV_VOLATILE, devname, | |||||
efi_setcurrdev, env_nounset); | |||||
env_setenv("loaddev", EV_VOLATILE, devname, | |||||
env_noset, env_nounset); | |||||
return (0); | return (0); | ||||
} | |||||
/* Assuming GPT partitioning. */ | /* Assuming GPT partitioning. */ | ||||
STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { | STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { | ||||
if (pp->pd_handle == img->DeviceHandle) { | if (check_preferred(pp->pd_handle)) { | ||||
currdev.d_slice = pp->pd_unit; | currdev.d_slice = pp->pd_unit; | ||||
currdev.d_partition = 255; | currdev.d_partition = 255; | ||||
devname = efi_fmtdev(&currdev); | set_vars((struct devdesc*)(&currdev)); | ||||
env_setenv("currdev", EV_VOLATILE, devname, | if (check_devdesc((struct devdesc*) | ||||
efi_setcurrdev, env_nounset); | (&currdev)) == 0) | ||||
env_setenv("loaddev", EV_VOLATILE, devname, | |||||
env_noset, env_nounset); | |||||
return (0); | return (0); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
#ifdef LOADER_CHECK_ALL_DEVS | |||||
pdi_list = efiblk_get_pdinfo_list(&efipart_cddev); | pdi_list = efiblk_get_pdinfo_list(&efipart_cddev); | ||||
STAILQ_FOREACH(dp, pdi_list, pd_link) { | STAILQ_FOREACH(dp, pdi_list, pd_link) { | ||||
if (dp->pd_handle == img->DeviceHandle || | if (check_preferred(dp->pd_handle) && | ||||
dp->pd_alias == img->DeviceHandle) { | check_dev(&efipart_cddev, dp->pd_unit) == 0) | ||||
set_devdesc_currdev(&efipart_cddev, dp->pd_unit); | |||||
return (0); | return (0); | ||||
} | } | ||||
} | |||||
pdi_list = efiblk_get_pdinfo_list(&efipart_fddev); | pdi_list = efiblk_get_pdinfo_list(&efipart_fddev); | ||||
STAILQ_FOREACH(dp, pdi_list, pd_link) { | STAILQ_FOREACH(dp, pdi_list, pd_link) { | ||||
if (dp->pd_handle == img->DeviceHandle) { | if (check_preferred(dp->pd_handle) && | ||||
set_devdesc_currdev(&efipart_fddev, dp->pd_unit); | check_dev(&efipart_fddev, dp->pd_unit) == 0) | ||||
return (0); | return (0); | ||||
} | } | ||||
#endif | |||||
return (ENOENT); | |||||
} | } | ||||
/* | #ifdef EFI_ZFS_BOOT | ||||
* Try the device handle from our loaded image first. If that | bool | ||||
* fails, use the device path from the loaded image and see if | efi_zfs_is_preferred(EFI_HANDLE *h) | ||||
* any of the nodes in that path match one of the enumerated | { | ||||
* handles. | return (h == img->DeviceHandle || check_preferred(h)); | ||||
*/ | |||||
if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &extra) == 0) { | |||||
set_devdesc_currdev(dev, unit); | |||||
return (0); | |||||
} | } | ||||
#endif | |||||
copy = NULL; | |||||
devpath = efi_lookup_image_devpath(IH); | |||||
while (devpath != NULL) { | |||||
h = efi_devpath_handle(devpath); | |||||
if (h == NULL) | |||||
break; | |||||
free(copy); | |||||
copy = NULL; | |||||
if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) { | |||||
set_devdesc_currdev(dev, unit); | |||||
return (0); | |||||
} | |||||
devpath = efi_lookup_devpath(h); | |||||
if (devpath != NULL) { | |||||
copy = efi_devpath_trim(devpath); | |||||
devpath = copy; | |||||
} | |||||
} | |||||
free(copy); | |||||
return (ENOENT); | |||||
} | |||||
EFI_STATUS | EFI_STATUS | ||||
main(int argc, CHAR16 *argv[]) | main(int argc, CHAR16 *argv[]) | ||||
{ | { | ||||
char var[128]; | char var[128]; | ||||
EFI_GUID *guid; | EFI_GUID *guid; | ||||
EFI_STATUS status; | |||||
int i, j, vargood, howto; | int i, j, vargood, howto; | ||||
UINTN k; | UINTN k; | ||||
int has_kbd; | int has_kbd; | ||||
#if !defined(__arm__) | #if !defined(__arm__) | ||||
char buf[40]; | char buf[40]; | ||||
#endif | #endif | ||||
archsw.arch_autoload = efi_autoload; | archsw.arch_autoload = efi_autoload; | ||||
Show All 22 Lines | #endif | ||||
*/ | */ | ||||
cons_probe(); | cons_probe(); | ||||
/* | /* | ||||
* Initialise the block cache. Set the upper limit. | * Initialise the block cache. Set the upper limit. | ||||
*/ | */ | ||||
bcache_init(32768, 512); | bcache_init(32768, 512); | ||||
for (i = 0; efi_drivers[i] != NULL; i++) { | |||||
if (efi_drivers[i]->init != NULL) | |||||
efi_drivers[i]->init(); | |||||
} | |||||
if ((status = BS->HandleProtocol(img->DeviceHandle, &devid, | |||||
(VOID**)&imgpath)) != | |||||
EFI_SUCCESS) { | |||||
panic("Failed to query LoadedImage (%lu)\n", | |||||
EFI_ERROR_CODE(status)); | |||||
} | |||||
/* The loaded image device path ends with a partition, then a | |||||
* file path. Trim them both to get the actual disk. | |||||
*/ | |||||
if ((imgprefix = efi_devpath_trim(imgpath)) == NULL) { | |||||
panic("Couldn't trim device path"); | |||||
} | |||||
/* | /* | ||||
* Parse the args to set the console settings, etc | * Parse the args to set the console settings, etc boot1.efi | ||||
* boot1.efi passes these in, if it can read /boot.config or /boot/config | * passes these in, if it can read /boot.config or | ||||
* or iPXE may be setup to pass these in. | * /boot/config or iPXE may be setup to pass these in. | ||||
* | * | ||||
* Loop through the args, and for each one that contains an '=' that is | * Loop through the args, and for each one that contains an '=' that is | ||||
* not the first character, add it to the environment. This allows | * not the first character, add it to the environment. This allows | ||||
* loader and kernel env vars to be passed on the command line. Convert | * 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. | * args from UCS-2 to ASCII (16 to 8 bit) as they are copied. | ||||
*/ | */ | ||||
howto = 0; | howto = 0; | ||||
for (i = 1; i < argc; i++) { | for (i = 1; i < argc; i++) { | ||||
▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | #endif | ||||
* 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); | ||||
if (find_currdev(img) != 0) | if (find_currdev() != 0) | ||||
return (EFI_NOT_FOUND); | return (EFI_NOT_FOUND); | ||||
efi_init_environment(); | efi_init_environment(); | ||||
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 !defined(__arm__) | #if !defined(__arm__) | ||||
▲ Show 20 Lines • Show All 443 Lines • Show Last 20 Lines |