Changeset View
Standalone View
stand/efi/loader/main.c
Show First 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | |||||
EFI_GUID mpcore = ARM_MP_CORE_INFO_TABLE_GUID; | EFI_GUID mpcore = ARM_MP_CORE_INFO_TABLE_GUID; | ||||
EFI_GUID esrt = ESRT_TABLE_GUID; | EFI_GUID esrt = ESRT_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 | #ifdef EFI_ZFS_BOOT | ||||
bool | bool | ||||
efi_zfs_is_preferred(EFI_HANDLE *h) | efi_zfs_is_preferred(EFI_HANDLE *h) | ||||
{ | { | ||||
return (h == img->DeviceHandle); | return (h == img->DeviceHandle); | ||||
} | } | ||||
#endif | #endif | ||||
▲ Show 20 Lines • Show All 75 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 | |||||
imp: space star, not star space, is the general convention. | |||||
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_unit = unit; | currdev.d_unit = unit; | ||||
devname = efi_fmtdev(&currdev); | devname = efi_fmtdev(&currdev); | ||||
currdev.d_opendata = NULL; | |||||
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 */ | |||||
Done Inline ActionsAgain, I hate these checks for an installed system. They aren't needed. imp: Again, I hate these checks for an installed system. They aren't needed. | |||||
static int | static int | ||||
find_currdev(EFI_LOADED_IMAGE *img) | check_dev(struct devsw *dev, int unit) { | ||||
Not Done Inline Actions{ goes on the next line. imp: { goes on the next line. | |||||
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)); | |||||
Done Inline ActionsI don't like searching like this. Load the file or don't, but don't guess at the system might be OK before trying. imp: I don't like searching like this. Load the file or don't, but don't guess at the system might… | |||||
Not Done Inline ActionsThat's not really possible given the architecture of loader. It expects you to set the variables, then run a load procedure. The purpose here is to get a good guess of the boot partition. eric_metricspace.net: That's not really possible given the architecture of loader. It expects you to set the… | |||||
Not Done Inline ActionsThat's possible through UEFI environment variables. Failure to do so and looking too hard violates the UEFI spec. imp: That's possible through UEFI environment variables. Failure to do so and looking too hard… | |||||
} | |||||
static int | |||||
find_currdev(void) | |||||
{ | { | ||||
pdinfo_list_t *pdi_list; | pdinfo_list_t *pdi_list; | ||||
pdinfo_t *dp, *pp; | pdinfo_t *dp, *pp; | ||||
zfsinfo_list_t *zfsi_list; | |||||
zfsinfo_t *zi; | |||||
char *devname; | |||||
#ifdef EFI_ZFS_BOOT | |||||
zfsi_list = efizfs_get_zfsinfo_list(); | |||||
STAILQ_FOREACH(zi, zfsi_list, zi_link) { | |||||
if (zfs_check_preferred(zi->zi_pool_guid)) { | |||||
struct zfs_devdesc currdev; | |||||
currdev.d_dev = &zfs_dev; | |||||
currdev.d_unit = 0; | |||||
currdev.d_type = currdev.d_dev->dv_type; | |||||
currdev.d_opendata = NULL; | |||||
currdev.pool_guid = zi->zi_pool_guid; | |||||
currdev.root_guid = 0; | |||||
devname = efi_fmtdev(&currdev); | |||||
set_vars((struct devdesc*)(&currdev)); | |||||
Done Inline ActionsCan't we just load the kernel as the check? I know that will require that we rearrange things, but I do not think we should probe like this. We should try to load the kernel or have it fail. imp: Can't we just load the kernel as the check? I know that will require that we rearrange things… | |||||
init_zfs_bootenv(devname); | |||||
if (check_devdesc((struct devdesc*)(&currdev)) == 0) | |||||
return (0); | |||||
} | |||||
} | |||||
#endif /* EFI_ZFS_BOOT */ | |||||
/* We have device lists for hd, cd, fd, walk them all. */ | |||||
pdi_list = efiblk_get_pdinfo_list(&efipart_hddev); | |||||
STAILQ_FOREACH(dp, pdi_list, pd_link) { | |||||
struct disk_devdesc currdev; | |||||
currdev.d_dev = &efipart_hddev; | |||||
currdev.d_type = currdev.d_dev->dv_type; | |||||
currdev.d_unit = dp->pd_unit; | |||||
currdev.d_opendata = NULL; | |||||
currdev.d_slice = -1; | |||||
currdev.d_partition = -1; | |||||
set_vars((struct devdesc*)(&currdev)); | |||||
Done Inline ActionsI do not like this searching of all the devices. It actively gets in the way of properly implementing EFIBOOTMGR stuff, which is next on my hit parade. imp: I do not like this searching of all the devices. It actively gets in the way of properly… | |||||
Not Done Inline ActionsThis search will end up being a fallback mechanism. EFI bootmgr should take precedence, if the variables are actually defined. eric_metricspace.net: This search will end up being a fallback mechanism. EFI bootmgr should take precedence, if the… | |||||
if (check_preferred(dp->pd_handle) && | |||||
Not Done Inline Actionsyou can't assume GPT partitioning. imp: you can't assume GPT partitioning. | |||||
check_devdesc((struct devdesc*)(&currdev)) == 0) | |||||
return (0); | |||||
/* Assuming GPT partitioning. */ | |||||
STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { | |||||
if (check_preferred(pp->pd_handle)) { | |||||
currdev.d_slice = pp->pd_unit; | |||||
currdev.d_partition = 255; | |||||
set_vars((struct devdesc*)(&currdev)); | |||||
if (check_devdesc((struct devdesc*) | |||||
(&currdev)) == 0) | |||||
return (0); | |||||
} | |||||
} | |||||
} | |||||
#ifdef LOADER_CHECK_ALL_DEVS | |||||
pdi_list = efiblk_get_pdinfo_list(&efipart_cddev); | |||||
STAILQ_FOREACH(dp, pdi_list, pd_link) { | |||||
if (check_preferred(dp->pd_handle) && | |||||
check_dev(&efipart_cddev, dp->pd_unit) == 0) | |||||
return (0); | |||||
} | |||||
pdi_list = efiblk_get_pdinfo_list(&efipart_fddev); | |||||
STAILQ_FOREACH(dp, pdi_list, pd_link) { | |||||
if (check_preferred(dp->pd_handle) && | |||||
check_dev(&efipart_fddev, dp->pd_unit) == 0) | |||||
return (0); | |||||
} | |||||
#endif | |||||
Done Inline Actionsand this is an even worse version of the above since it's simply a copy that doesn't have the calls to check_preferred. imp: and this is an even worse version of the above since it's simply a copy that doesn't have the… | |||||
return (ENOENT); | |||||
} | |||||
<<<<<<< HEAD | |||||
/* Legacy-mode device search: assume we've been loaded by boot1 */ | |||||
static int | |||||
find_currdev_legacy(void) | |||||
Done Inline ActionsAnd this is a third copy of the same thing. imp: And this is a third copy of the same thing. | |||||
Done Inline ActionsThis is the existing behavior, which is here to avoid breaking anyone's system. I think this needs to stay in place until the next major version. eric_metricspace.net: This is the existing behavior, which is here to avoid breaking anyone's system. I think this… | |||||
Done Inline ActionsNormally I'd be right there with you on compatibility. However, compatibility breaks things, including any possibility of efibootmgr failing over to the next thing. I'm not at all comfortable retaining a feature that's little used, causes actual problems today, is of at best dubious value and demonstrably gets in the way of bringing in standards compliant behavior. imp: Normally I'd be right there with you on compatibility.
However, compatibility breaks things… | |||||
Not Done Inline ActionsAnyone with a legacy setup isn't going to have EFI vars set. If this is done as a fallback, it shouldn't cause problems. eric_metricspace.net: Anyone with a legacy setup isn't going to have EFI vars set. If this is done as a fallback, it… | |||||
{ | |||||
pdinfo_list_t *pdi_list; | |||||
pdinfo_t *dp, *pp; | |||||
EFI_DEVICE_PATH *devpath, *copy; | EFI_DEVICE_PATH *devpath, *copy; | ||||
EFI_HANDLE h; | EFI_HANDLE h; | ||||
char *devname; | char *devname; | ||||
struct devsw *dev; | struct devsw *dev; | ||||
int unit; | int unit; | ||||
uint64_t extra; | uint64_t extra; | ||||
#ifdef EFI_ZFS_BOOT | #ifdef EFI_ZFS_BOOT | ||||
/* Did efi_zfs_probe() detect the boot pool? */ | /* Did efi_zfs_probe() detect the boot pool? */ | ||||
if (pool_guid != 0) { | if (pool_guid != 0) { | ||||
struct zfs_devdesc currdev; | struct zfs_devdesc currdev; | ||||
currdev.dd.d_dev = &zfs_dev; | currdev.dd.d_dev = &zfs_dev; | ||||
currdev.dd.d_unit = 0; | currdev.dd.d_unit = 0; | ||||
currdev.pool_guid = pool_guid; | currdev.pool_guid = 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); | ||||
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.dd.d_dev = &efipart_hddev; | currdev.dd.d_dev = &efipart_hddev; | ||||
currdev.dd.d_unit = dp->pd_unit; | currdev.dd.d_unit = dp->pd_unit; | ||||
currdev.d_slice = -1; | currdev.d_slice = -1; | ||||
currdev.d_partition = -1; | currdev.d_partition = -1; | ||||
if (dp->pd_handle == img->DeviceHandle) { | if (dp->pd_handle == img->DeviceHandle) { | ||||
devname = efi_fmtdev(&currdev); | set_vars((struct devdesc*)(&currdev)); | ||||
Done Inline ActionsI like this cleanup. imp: I like this cleanup. | |||||
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 (pp->pd_handle == img->DeviceHandle) { | ||||
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, | |||||
efi_setcurrdev, env_nounset); | |||||
env_setenv("loaddev", EV_VOLATILE, devname, | |||||
env_noset, env_nounset); | |||||
return (0); | return (0); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
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 (dp->pd_handle == img->DeviceHandle || | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | if (devpath != NULL) { | ||||
devpath = copy; | devpath = copy; | ||||
} | } | ||||
} | } | ||||
free(copy); | free(copy); | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
static int | |||||
Done Inline ActionsAgain, I don't like this structure of searching. At most we should search for other partitions on the disk we booted from, and then stop. imp: Again, I don't like this structure of searching. At most we should search for other partitions… | |||||
Done Inline Actionstsoome will almost certainly complain about that, on behalf of everyone who boots from different disks from their ESP eric_metricspace.net: tsoome will almost certainly complain about that, on behalf of everyone who boots from… | |||||
find_currdev(void) | |||||
{ | |||||
int err; | |||||
if ((err = find_currdev_preferred()) != ENOENT) { | |||||
return (err); | |||||
} | |||||
if ((err = find_currdev_all()) != ENOENT) { | |||||
return (err); | |||||
} | |||||
return find_currdev_legacy(); | |||||
} | |||||
======= | |||||
>>>>>>> 8f711283607... Simplify search code | |||||
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; | ||||
CHAR16 *text; | CHAR16 *text; | ||||
UINT16 boot_current; | UINT16 boot_current; | ||||
size_t sz; | size_t sz; | ||||
UINT16 boot_order[100]; | UINT16 boot_order[100]; | ||||
EFI_DEVICE_PATH *imgpath; | EFI_DEVICE_PATH *imgpath; | ||||
Show All 28 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); | ||||
if ((status = BS->HandleProtocol(img->DeviceHandle, &devid, | |||||
Done Inline ActionsWe can likely lose this. imp: We can likely lose this. | |||||
(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 | ||||
Done Inline Actionshere is where we should look for BootCurrent and then BootXXXX. If we find BootXXXX, we should see if it contains additional paths. If so, we should jump to the additional paths straight away. imp: here is where we should look for BootCurrent and then BootXXXX. If we find BootXXXX, we should… | |||||
Done Inline ActionsI'm not going to do EFI vars handling in this changeset, but I agree, that ought to be the way it works eventually. eric_metricspace.net: I'm not going to do EFI vars handling in this changeset, but I agree, that ought to be the way… | |||||
Done Inline ActionsYea, I'm actually working on making that work now that I have efibootmgr in the tree and generating proper paths. I'll see if I can fast track it, as well as a trimmed down searching that goes with it. That will get you onto the GELI stuff I broke :) imp: Yea, I'm actually working on making that work now that I have efibootmgr in the tree and… | |||||
* 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++) { | ||||
Not Done Inline ActionsAt the end of this loop, we should see if we have an arg that specifies a kernel to boot directly. imp: At the end of this loop, we should see if we have an arg that specifies a kernel to boot… | |||||
if (argv[i][0] == '-') { | if (argv[i][0] == '-') { | ||||
for (j = 1; argv[i][j] != 0; j++) { | for (j = 1; argv[i][j] != 0; j++) { | ||||
int ch; | int ch; | ||||
ch = argv[i][j]; | ch = argv[i][j]; | ||||
switch (ch) { | switch (ch) { | ||||
case 'a': | case 'a': | ||||
howto |= RB_ASKNAME; | howto |= RB_ASKNAME; | ||||
▲ Show 20 Lines • Show All 139 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) | ||||
Done Inline ActionsI think expanding the searching is not a good idea. It will get in the way of properly implementing the efibootmgr protocol where the searching is done in the BIOS not in the loader and if we can't boot this device, we should go to the next one. I know that's not what FreeBSD has done traditionally, but the traditional behavior is (a) not useful and (b) violates the standard. imp: I think expanding the searching is not a good idea. It will get in the way of properly… | |||||
Done Inline ActionsThe plan is this will only run if no previous mechanism (command-line args, efibootmgr) has succeeded. eric_metricspace.net: The plan is this will only run if no previous mechanism (command-line args, efibootmgr) has… | |||||
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 396 Lines • Show Last 20 Lines |
space star, not star space, is the general convention.