Changeset View
Standalone View
sys/boot/efi/boot1/boot1.c
Show All 17 Lines | |||||
* warranties of merchantability and fitness for a particular | * warranties of merchantability and fitness for a particular | ||||
* purpose. | * purpose. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/disk.h> | |||||
#include <machine/elf.h> | #include <machine/elf.h> | ||||
#include <machine/stdarg.h> | #include <machine/stdarg.h> | ||||
#include <stand.h> | #include <stand.h> | ||||
#include <disk.h> | |||||
#include <efi.h> | #include <efi.h> | ||||
#include <efilib.h> | |||||
#include <efiprot.h> | |||||
#include <eficonsctl.h> | #include <eficonsctl.h> | ||||
#ifdef EFI_ZFS_BOOT | |||||
#include <libzfs.h> | |||||
#endif | |||||
#include "boot_module.h" | #include <bootstrap.h> | ||||
#include "efi_drivers.h" | |||||
#include "efizfs.h" | |||||
#include "paths.h" | #include "paths.h" | ||||
static const boot_module_t *boot_modules[] = | #ifdef EFI_DEBUG | ||||
{ | #define DPRINTF(fmt, args...) printf(fmt, ##args) | ||||
#define DSTALL(d) BS->Stall(d) | |||||
allanjude: 'bs' appears to have been renamed 'BS', but this macro was not updated. | |||||
#else | |||||
#define DPRINTF(fmt, ...) {} | |||||
#define DSTALL(d) {} | |||||
#endif | |||||
struct arch_switch archsw; /* MI/MD interface boundary */ | |||||
static const efi_driver_t *efi_drivers[] = { | |||||
NULL | |||||
}; | |||||
extern struct console efi_console; | |||||
#if defined(__amd64__) || defined(__i386__) | |||||
extern struct console comconsole; | |||||
extern struct console nullconsole; | |||||
#endif | |||||
#ifdef EFI_ZFS_BOOT | #ifdef EFI_ZFS_BOOT | ||||
&zfs_module, | uint64_t pool_guid; | ||||
#endif | #endif | ||||
struct fs_ops *file_system[] = { | |||||
#ifdef EFI_ZFS_BOOT | |||||
&zfs_fsops, | |||||
#endif | |||||
&dosfs_fsops, | |||||
#ifdef EFI_UFS_BOOT | #ifdef EFI_UFS_BOOT | ||||
&ufs_module | &ufs_fsops, | ||||
#endif | #endif | ||||
&cd9660_fsops, | |||||
&nfs_fsops, | |||||
Done Inline ActionsWhy no more ifdef EFI_UFS_BOOT ? manu: Why no more ifdef EFI_UFS_BOOT ? | |||||
&gzipfs_fsops, | |||||
&bzipfs_fsops, | |||||
NULL | |||||
}; | }; | ||||
#define NUM_BOOT_MODULES nitems(boot_modules) | struct devsw *devsw[] = { | ||||
/* The initial number of handles used to query EFI for partitions. */ | &efipart_hddev, | ||||
#define NUM_HANDLES_INIT 24 | &efipart_fddev, | ||||
&efipart_cddev, | |||||
#ifdef EFI_ZFS_BOOT | |||||
&zfs_dev, | |||||
#endif | |||||
NULL | |||||
}; | |||||
EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab); | struct console *consoles[] = { | ||||
&efi_console, | |||||
NULL | |||||
}; | |||||
EFI_SYSTEM_TABLE *systab; | /* Definitions we don't actually need for boot, but we need to define | ||||
EFI_BOOT_SERVICES *bs; | * to make the linker happy. | ||||
static EFI_HANDLE *image; | */ | ||||
struct file_format *file_formats[] = { NULL }; | |||||
static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; | struct netif_driver *netif_drivers[] = { NULL }; | ||||
static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; | |||||
static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; | |||||
static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; | |||||
/* | static int | ||||
* Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures | efi_autoload(void) | ||||
* memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from | |||||
* EFI methods. | |||||
*/ | |||||
void * | |||||
Malloc(size_t len, const char *file __unused, int line __unused) | |||||
{ | { | ||||
void *out; | printf("******** Boot block should not call autoload\n"); | ||||
return (-1); | |||||
} | |||||
if (bs->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS) | static ssize_t | ||||
return (out); | efi_copyin(const void *src __unused, vm_offset_t dest __unused, | ||||
const size_t len __unused) | |||||
{ | |||||
printf("******** Boot block should not call copyin\n"); | |||||
return (-1); | |||||
} | |||||
return (NULL); | static ssize_t | ||||
efi_copyout(vm_offset_t src __unused, void *dest __unused, | |||||
const size_t len __unused) | |||||
{ | |||||
printf("******** Boot block should not call copyout\n"); | |||||
return (-1); | |||||
} | } | ||||
void | static ssize_t | ||||
Free(void *buf, const char *file __unused, int line __unused) | efi_readin(int fd __unused, vm_offset_t dest __unused, | ||||
const size_t len __unused) | |||||
{ | { | ||||
if (buf != NULL) | printf("******** Boot block should not call readin\n"); | ||||
(void)bs->FreePool(buf); | return (-1); | ||||
} | } | ||||
/* The initial number of handles used to query EFI for partitions. */ | |||||
#define NUM_HANDLES_INIT 24 | |||||
static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; | |||||
static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; | |||||
/* | /* | ||||
* nodes_match returns TRUE if the imgpath isn't NULL and the nodes match, | * nodes_match returns TRUE if the imgpath isn't NULL and the nodes match, | ||||
* FALSE otherwise. | * FALSE otherwise. | ||||
*/ | */ | ||||
static BOOLEAN | static BOOLEAN | ||||
nodes_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath) | nodes_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath) | ||||
{ | { | ||||
int len; | int len; | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
while (!IsDevicePathEnd(NextDevicePathNode(devpath))) | while (!IsDevicePathEnd(NextDevicePathNode(devpath))) | ||||
devpath = NextDevicePathNode(devpath); | devpath = NextDevicePathNode(devpath); | ||||
return (devpath); | return (devpath); | ||||
} | } | ||||
#ifdef EFI_DEBUG | |||||
Done Inline ActionsDon't bother to change this. I'm about to commit something that will conflict in that it removes all these functions. imp: Don't bother to change this. I'm about to commit something that will conflict in that it… | |||||
/* | /* | ||||
* devpath_node_str is a basic output method for a devpath node which | * devpath_node_str is a basic output method for a devpath node which | ||||
* only understands a subset of the available sub types. | * only understands a subset of the available sub types. | ||||
* | * | ||||
* If we switch to UEFI 2.x then we should update it to use: | * If we switch to UEFI 2.x then we should update it to use: | ||||
* EFI_DEVICE_PATH_TO_TEXT_PROTOCOL. | * EFI_DEVICE_PATH_TO_TEXT_PROTOCOL. | ||||
*/ | */ | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 115 Lines • ▼ Show 20 Lines | devpath_node_str(char *buf, size_t size, EFI_DEVICE_PATH *devpath) | ||||
return snprintf(buf, size, "type(0x%02x, 0x%02x)", devpath->Type, | return snprintf(buf, size, "type(0x%02x, 0x%02x)", devpath->Type, | ||||
devpath->SubType); | devpath->SubType); | ||||
} | } | ||||
/* | /* | ||||
* devpath_strlcat appends a text description of devpath to buf but not more | * devpath_strlcat appends a text description of devpath to buf but not more | ||||
* than size - 1 characters followed by NUL-terminator. | * than size - 1 characters followed by NUL-terminator. | ||||
*/ | */ | ||||
int | static int | ||||
devpath_strlcat(char *buf, size_t size, EFI_DEVICE_PATH *devpath) | devpath_strlcat(char *buf, size_t size, EFI_DEVICE_PATH *devpath) | ||||
{ | { | ||||
size_t len, used; | size_t len, used; | ||||
const char *sep; | const char *sep; | ||||
sep = ""; | sep = ""; | ||||
used = 0; | used = 0; | ||||
while (!IsDevicePathEnd(devpath)) { | while (!IsDevicePathEnd(devpath)) { | ||||
Show All 14 Lines | devpath_strlcat(char *buf, size_t size, EFI_DEVICE_PATH *devpath) | ||||
return (used); | return (used); | ||||
} | } | ||||
/* | /* | ||||
* devpath_str is convenience method which returns the text description of | * devpath_str is convenience method which returns the text description of | ||||
* devpath using a static buffer, so it isn't thread safe! | * devpath using a static buffer, so it isn't thread safe! | ||||
*/ | */ | ||||
char * | static char * | ||||
devpath_str(EFI_DEVICE_PATH *devpath) | devpath_str(EFI_DEVICE_PATH *devpath) | ||||
{ | { | ||||
static char buf[256]; | static char buf[256]; | ||||
devpath_strlcat(buf, sizeof(buf), devpath); | devpath_strlcat(buf, sizeof(buf), devpath); | ||||
return buf; | return (buf); | ||||
} | } | ||||
#endif | |||||
Done Inline Actionsthrough here. None of this needs to be done and will conflict. imp: through here. None of this needs to be done and will conflict. | |||||
/* | |||||
* load_loader attempts to load the loader image data. | |||||
* | |||||
* It tries each module and its respective devices, identified by mod->probe, | |||||
* in order until a successful load occurs at which point it returns EFI_SUCCESS | |||||
* and EFI_NOT_FOUND otherwise. | |||||
* | |||||
* Only devices which have preferred matching the preferred parameter are tried. | |||||
*/ | |||||
static EFI_STATUS | static EFI_STATUS | ||||
load_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp, | do_load(const char *filepath, void **bufp, size_t *bufsize) | ||||
size_t *bufsize, BOOLEAN preferred) | |||||
{ | { | ||||
UINTN i; | struct stat st; | ||||
dev_info_t *dev; | void *buf = NULL; | ||||
const boot_module_t *mod; | int fd, err; | ||||
size_t fsize, remaining; | |||||
ssize_t readsize; | |||||
for (i = 0; i < NUM_BOOT_MODULES; i++) { | if ((fd = open(filepath, O_RDONLY)) < 0) { | ||||
mod = boot_modules[i]; | return (ENOTSUP); | ||||
for (dev = mod->devices(); dev != NULL; dev = dev->next) { | } | ||||
if (dev->preferred != preferred) | |||||
continue; | |||||
if (mod->load(PATH_LOADER_EFI, dev, bufp, bufsize) == | if ((err = fstat(fd, &st)) != 0) { | ||||
goto close_file; | |||||
} | |||||
fsize = st.st_size; | |||||
if ((buf = malloc(fsize)) == NULL) { | |||||
err = ENOMEM; | |||||
goto close_file; | |||||
} | |||||
remaining = fsize; | |||||
do { | |||||
if ((readsize = read(fd, buf, fsize)) < 0) { | |||||
err = (-readsize); | |||||
goto free_buf; | |||||
} | |||||
remaining -= readsize; | |||||
} while(remaining != 0); | |||||
close(fd); | |||||
*bufsize = st.st_size; | |||||
*bufp = buf; | |||||
close_file: | |||||
close(fd); | |||||
return errno_to_efi_status(err); | |||||
free_buf: | |||||
free(buf); | |||||
goto close_file; | |||||
} | |||||
static int | |||||
probe_fs(const char *filepath) | |||||
{ | |||||
int fd; | |||||
if ((fd = open(filepath, O_RDONLY)) < 0) { | |||||
return (ENOTSUP); | |||||
} | |||||
close(fd); | |||||
return (0); | |||||
} | |||||
static int | |||||
probe_dev(struct devsw *dev, int unit, const char *filepath) | |||||
{ | |||||
struct devdesc currdev; | |||||
char *devname; | |||||
int err; | |||||
currdev.d_dev = dev; | |||||
currdev.d_type = currdev.d_dev->dv_type; | |||||
currdev.d_unit = unit; | |||||
currdev.d_opendata = NULL; | |||||
devname = efi_fmtdev(&currdev); | |||||
env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, | |||||
env_nounset); | |||||
err = probe_fs(filepath); | |||||
return (err); | |||||
} | |||||
static int | |||||
load_preferred(EFI_LOADED_IMAGE *img, const char *filepath, void **bufp, | |||||
size_t *bufsize, EFI_HANDLE *handlep) | |||||
{ | |||||
pdinfo_list_t *pdi_list; | |||||
pdinfo_t *dp, *pp; | |||||
EFI_DEVICE_PATH *devpath, *copy; | |||||
EFI_HANDLE h; | |||||
struct devsw *dev; | |||||
int unit; | |||||
uint64_t extra; | |||||
char *devname; | |||||
#ifdef EFI_ZFS_BOOT | |||||
/* Did efi_zfs_probe() detect the boot pool? */ | |||||
if (pool_guid != 0) { | |||||
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 = pool_guid; | |||||
currdev.root_guid = 0; | |||||
devname = efi_fmtdev(&currdev); | |||||
env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, | |||||
env_nounset); | |||||
if (probe_fs(filepath) == 0 && | |||||
do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { | |||||
*handlep = img->DeviceHandle; | |||||
return (0); | |||||
} | |||||
} | |||||
#endif /* EFI_ZFS_BOOT */ | |||||
/* We have device lists for hd, cd, fd, walk them all. */ | |||||
Done Inline ActionsMy comment from an earlier review moved: we still need to actually implement preferential treatment for where boot1.efi was loaded from. imp: My comment from an earlier review moved: we still need to actually implement preferential… | |||||
Done Inline ActionsTake a look at the actual probe statements. This function scans everything looking for the boot partition, which should preserve the old behavior. eric_metricspace.net: Take a look at the actual probe statements. This function scans everything looking for the… | |||||
Done Inline ActionsRight. I see one loop that matches handles (which will never be the same). Then I see a loop for each partition. I see tis code then almost replicated for fd and cd inline. Then I see it do another probe of all the devices. So is that how we get the current behavior? It's quite confusing and very uncommented what it's doing here. I'm still unconvinced that it's actually doing the right thing. And it's making it harder for any scheme that walks through the lists looking for the highest value partition to boot off of (which are mods that we have at work). also, why does it set currdev for the hddev, but not the cddev or fddev cases? That seems wrong to me and deserves at least a comment, but I suspect that the cddev and fddev code isn't actually working and we really need it for all three. imp: Right. I see one loop that matches handles (which will never be the same). Then I see a loop… | |||||
Done Inline Actionsload_preferred filters for devices that are on the same device handle as the loaded image. It first tries ZFS, then all drives (and all) partitions, then attempts to probe the loaded image handle itself, then falls back to the device path. load_all goes through everything looking for something it can boot. In any case, I stole the structure of the code from loader, so it's consistent with the behavior of loader. eric_metricspace.net: load_preferred filters for devices that are on the same device handle as the loaded image. It… | |||||
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; | |||||
devname = efi_fmtdev(&currdev); | |||||
env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, | |||||
env_nounset); | |||||
if (dp->pd_handle == img->DeviceHandle && | |||||
probe_fs(filepath) == 0 && | |||||
do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { | |||||
*handlep = img->DeviceHandle; | |||||
return (0); | |||||
} | |||||
/* Assuming GPT partitioning. */ | |||||
STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { | |||||
if (pp->pd_handle == img->DeviceHandle) { | |||||
currdev.d_slice = pp->pd_unit; | |||||
currdev.d_partition = 255; | |||||
devname = efi_fmtdev(&currdev); | |||||
env_setenv("currdev", EV_VOLATILE, devname, | |||||
efi_setcurrdev, env_nounset); | |||||
if (probe_fs(filepath) == 0 && | |||||
do_load(filepath, bufp, bufsize) == | |||||
EFI_SUCCESS) { | EFI_SUCCESS) { | ||||
*devinfop = dev; | *handlep = img->DeviceHandle; | ||||
*modp = mod; | return (0); | ||||
return (EFI_SUCCESS); | |||||
} | } | ||||
Done Inline ActionsThis structure (find one and boot it) has some problems. It doesn't try to implement the UEFI boot manager protocol, and its structure makes it hard to create a priority list of items to try to load, which the old code made somewhat easy (easy enough we implemented simple boot next protocol to work around issues we were having the the BIOS magically changing which partition it booted off of, but I digress). It also should prefer the device we've been loaded off of to other devices in the system. It doesn't seem to do that, but plow through the list until if finds something, anything, that's bootable. This is a mistake and one that must be corrected before it's committed. With this behavior, you'll get whatever is first in the UEFI list of devices, which could be that thumb drive you plugged in that happens to have a bootable image on it just because it comes before other drives in the list. imp: This structure (find one and boot it) has some problems. It doesn't try to implement the UEFI… | |||||
} | } | ||||
} | } | ||||
} | |||||
pdi_list = efiblk_get_pdinfo_list(&efipart_cddev); | |||||
STAILQ_FOREACH(dp, pdi_list, pd_link) { | |||||
if ((dp->pd_handle == img->DeviceHandle || | |||||
dp->pd_alias == img->DeviceHandle) && | |||||
probe_dev(&efipart_cddev, dp->pd_unit, filepath) == 0 && | |||||
do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { | |||||
*handlep = img->DeviceHandle; | |||||
return (0); | |||||
} | |||||
} | |||||
pdi_list = efiblk_get_pdinfo_list(&efipart_fddev); | |||||
STAILQ_FOREACH(dp, pdi_list, pd_link) { | |||||
if (dp->pd_handle == img->DeviceHandle && | |||||
probe_dev(&efipart_cddev, dp->pd_unit, filepath) == 0 && | |||||
do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { | |||||
*handlep = img->DeviceHandle; | |||||
return (0); | |||||
} | |||||
} | |||||
/* | |||||
* Try the device handle from our loaded image first. If that | |||||
Done Inline ActionsThis comment seems stale since we have all those return (0) above here. This hierarchy that was in the old code is critical and needs to be preserved. imp: This comment seems stale since we have all those return (0) above here. This hierarchy that was… | |||||
* fails, use the device path from the loaded image and see if | |||||
* any of the nodes in that path match one of the enumerated | |||||
* handles. | |||||
*/ | |||||
if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &extra) == 0 && | |||||
probe_dev(dev, dp->pd_unit, filepath) == 0 && | |||||
do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { | |||||
*handlep = img->DeviceHandle; | |||||
return (0); | |||||
} | |||||
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 && | |||||
probe_dev(dev, dp->pd_unit, filepath) == 0 && | |||||
do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { | |||||
*handlep = img->DeviceHandle; | |||||
return (0); | |||||
} | |||||
devpath = efi_lookup_devpath(h); | |||||
if (devpath != NULL) { | |||||
copy = efi_devpath_trim(devpath); | |||||
devpath = copy; | |||||
} | |||||
} | |||||
free(copy); | |||||
return (ENOENT); | |||||
} | |||||
static int | |||||
load_all(const char *filepath, void **bufp, size_t *bufsize, | |||||
EFI_HANDLE *handlep) | |||||
{ | |||||
pdinfo_list_t *pdi_list; | |||||
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) { | |||||
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); | |||||
env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, | |||||
env_nounset); | |||||
if (probe_fs(filepath) == 0 && | |||||
do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { | |||||
*handlep = zi->zi_handle; | |||||
printf("Succeeded\n"); | |||||
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; | |||||
devname = efi_fmtdev(&currdev); | |||||
env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, | |||||
env_nounset); | |||||
if (probe_fs(filepath) == 0 && | |||||
do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { | |||||
*handlep = dp->pd_handle; | |||||
return (0); | |||||
} | |||||
/* Assuming GPT partitioning. */ | |||||
STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { | |||||
currdev.d_slice = pp->pd_unit; | |||||
currdev.d_partition = 255; | |||||
devname = efi_fmtdev(&currdev); | |||||
env_setenv("currdev", EV_VOLATILE, devname, | |||||
efi_setcurrdev, env_nounset); | |||||
if (probe_fs(filepath) == 0 && | |||||
do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { | |||||
*handlep = pp->pd_handle; | |||||
return (0); | |||||
} | |||||
} | |||||
} | |||||
pdi_list = efiblk_get_pdinfo_list(&efipart_cddev); | |||||
STAILQ_FOREACH(dp, pdi_list, pd_link) { | |||||
if (probe_dev(&efipart_cddev, dp->pd_unit, filepath) == 0 && | |||||
do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { | |||||
*handlep = dp->pd_handle; | |||||
return (0); | |||||
} | |||||
} | |||||
pdi_list = efiblk_get_pdinfo_list(&efipart_fddev); | |||||
STAILQ_FOREACH(dp, pdi_list, pd_link) { | |||||
if (probe_dev(&efipart_fddev, dp->pd_unit, filepath) == 0 && | |||||
do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { | |||||
*handlep = dp->pd_handle; | |||||
return (0); | |||||
} | |||||
} | |||||
return (ENOENT); | |||||
} | |||||
static EFI_STATUS | |||||
load_loader(EFI_HANDLE *handlep, void **bufp, size_t *bufsize) | |||||
{ | |||||
EFI_LOADED_IMAGE *boot_image; | |||||
EFI_STATUS status; | |||||
if ((status = BS->OpenProtocol(IH, &LoadedImageGUID, | |||||
(VOID**)&boot_image, IH, NULL, | |||||
EFI_OPEN_PROTOCOL_GET_PROTOCOL)) != EFI_SUCCESS) { | |||||
panic("Failed to query LoadedImage (%lu)\n", | |||||
EFI_ERROR_CODE(status)); | |||||
} | |||||
/* Try the preferred handles first, then all the handles */ | |||||
if (load_preferred(boot_image, PATH_LOADER_EFI, bufp, bufsize, | |||||
handlep) == 0) { | |||||
return (0); | |||||
} | |||||
if (load_all(PATH_LOADER_EFI, bufp, bufsize, handlep) == 0) { | |||||
return (0); | |||||
} | |||||
return (EFI_NOT_FOUND); | return (EFI_NOT_FOUND); | ||||
} | } | ||||
/* | /* | ||||
* try_boot only returns if it fails to load the loader. If it succeeds | * try_boot only returns if it fails to load the loader. If it succeeds | ||||
* it simply boots, otherwise it returns the status of last EFI call. | * it simply boots, otherwise it returns the status of last EFI call. | ||||
*/ | */ | ||||
static EFI_STATUS | static EFI_STATUS | ||||
try_boot(void) | try_boot(void) | ||||
{ | { | ||||
size_t bufsize, loadersize, cmdsize; | size_t bufsize, loadersize, cmdsize; | ||||
void *buf, *loaderbuf; | void *buf, *loaderbuf; | ||||
char *cmd; | char *cmd; | ||||
dev_info_t *dev; | EFI_HANDLE fshandle; | ||||
const boot_module_t *mod; | |||||
EFI_HANDLE loaderhandle; | EFI_HANDLE loaderhandle; | ||||
EFI_LOADED_IMAGE *loaded_image; | EFI_LOADED_IMAGE *loaded_image; | ||||
EFI_STATUS status; | EFI_STATUS status; | ||||
EFI_DEVICE_PATH *fspath; | |||||
status = load_loader(&mod, &dev, &loaderbuf, &loadersize, TRUE); | status = load_loader(&fshandle, &loaderbuf, &loadersize); | ||||
if (status != EFI_SUCCESS) { | if (status != EFI_SUCCESS) { | ||||
status = load_loader(&mod, &dev, &loaderbuf, &loadersize, | |||||
FALSE); | |||||
if (status != EFI_SUCCESS) { | |||||
printf("Failed to load '%s'\n", PATH_LOADER_EFI); | |||||
return (status); | return (status); | ||||
} | } | ||||
fspath = NULL; | |||||
if (status == EFI_SUCCESS) { | |||||
status = BS->OpenProtocol(fshandle, &DevicePathGUID, | |||||
(void **)&fspath, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |||||
if (status != EFI_SUCCESS) { | |||||
DPRINTF("Failed to get image DevicePath (%lu)\n", | |||||
EFI_ERROR_CODE(status)); | |||||
} | } | ||||
DPRINTF("filesystem device path: %s\n", devpath_str(fspath)); | |||||
} | |||||
/* | /* | ||||
* Read in and parse the command line from /boot.config or /boot/config, | * Read in and parse the command line from /boot.config or /boot/config, | ||||
* if present. We'll pass it the next stage via a simple ASCII | * if present. We'll pass it the next stage via a simple ASCII | ||||
* string. loader.efi has a hack for ASCII strings, so we'll use that to | * string. loader.efi has a hack for ASCII strings, so we'll use that to | ||||
* keep the size down here. We only try to read the alternate file if | * keep the size down here. We only try to read the alternate file if | ||||
* we get EFI_NOT_FOUND because all other errors mean that the boot_module | * we get EFI_NOT_FOUND because all other errors mean that the boot_module | ||||
* had troubles with the filesystem. We could return early, but we'll let | * had troubles with the filesystem. We could return early, but we'll let | ||||
* loading the actual kernel sort all that out. Since these files are | * loading the actual kernel sort all that out. Since these files are | ||||
* optional, we don't report errors in trying to read them. | * optional, we don't report errors in trying to read them. | ||||
*/ | */ | ||||
cmd = NULL; | cmd = NULL; | ||||
cmdsize = 0; | cmdsize = 0; | ||||
status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize); | status = do_load(PATH_DOTCONFIG, &buf, &bufsize); | ||||
if (status == EFI_NOT_FOUND) | if (status == EFI_NOT_FOUND) | ||||
status = mod->load(PATH_CONFIG, dev, &buf, &bufsize); | status = do_load(PATH_CONFIG, &buf, &bufsize); | ||||
if (status == EFI_SUCCESS) { | if (status == EFI_SUCCESS) { | ||||
cmdsize = bufsize + 1; | cmdsize = bufsize + 1; | ||||
cmd = malloc(cmdsize); | cmd = malloc(cmdsize); | ||||
if (cmd == NULL) | if (cmd == NULL) | ||||
goto errout; | goto errout; | ||||
memcpy(cmd, buf, bufsize); | memcpy(cmd, buf, bufsize); | ||||
cmd[bufsize] = '\0'; | cmd[bufsize] = '\0'; | ||||
free(buf); | free(buf); | ||||
buf = NULL; | buf = NULL; | ||||
} | } | ||||
if ((status = bs->LoadImage(TRUE, image, devpath_last(dev->devpath), | if ((status = BS->LoadImage(TRUE, IH, devpath_last(fspath), | ||||
loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) { | loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) { | ||||
printf("Failed to load image provided by %s, size: %zu, (%lu)\n", | printf("Failed to load image, size: %zu, (%lu)\n", | ||||
mod->name, loadersize, EFI_ERROR_CODE(status)); | loadersize, EFI_ERROR_CODE(status)); | ||||
goto errout; | goto errout; | ||||
} | } | ||||
if ((status = bs->HandleProtocol(loaderhandle, &LoadedImageGUID, | if ((status = BS->OpenProtocol(loaderhandle, &LoadedImageGUID, | ||||
(VOID**)&loaded_image)) != EFI_SUCCESS) { | (VOID**)&loaded_image, IH, NULL, | ||||
printf("Failed to query LoadedImage provided by %s (%lu)\n", | EFI_OPEN_PROTOCOL_GET_PROTOCOL)) != EFI_SUCCESS) { | ||||
mod->name, EFI_ERROR_CODE(status)); | printf("Failed to query LoadedImage (%lu)\n", | ||||
EFI_ERROR_CODE(status)); | |||||
goto errout; | goto errout; | ||||
} | } | ||||
if (cmd != NULL) | if (cmd != NULL) | ||||
printf(" command args: %s\n", cmd); | printf(" command args: %s\n", cmd); | ||||
loaded_image->DeviceHandle = dev->devhandle; | loaded_image->DeviceHandle = fshandle; | ||||
loaded_image->LoadOptionsSize = cmdsize; | loaded_image->LoadOptionsSize = cmdsize; | ||||
loaded_image->LoadOptions = cmd; | loaded_image->LoadOptions = cmd; | ||||
DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI); | DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI); | ||||
DSTALL(1000000); | DSTALL(1000000); | ||||
DPRINTF("."); | DPRINTF("."); | ||||
DSTALL(1000000); | DSTALL(1000000); | ||||
DPRINTF("."); | DPRINTF("."); | ||||
DSTALL(1000000); | DSTALL(1000000); | ||||
DPRINTF("."); | DPRINTF("."); | ||||
DSTALL(1000000); | DSTALL(1000000); | ||||
DPRINTF("."); | DPRINTF("."); | ||||
DSTALL(1000000); | DSTALL(1000000); | ||||
DPRINTF(".\n"); | DPRINTF(".\n"); | ||||
if ((status = bs->StartImage(loaderhandle, NULL, NULL)) != | if ((status = BS->StartImage(loaderhandle, NULL, NULL)) != | ||||
EFI_SUCCESS) { | EFI_SUCCESS) { | ||||
printf("Failed to start image provided by %s (%lu)\n", | printf("Failed to start image (%lu)\n", | ||||
mod->name, EFI_ERROR_CODE(status)); | EFI_ERROR_CODE(status)); | ||||
loaded_image->LoadOptionsSize = 0; | loaded_image->LoadOptionsSize = 0; | ||||
loaded_image->LoadOptions = NULL; | loaded_image->LoadOptions = NULL; | ||||
} | } | ||||
errout: | errout: | ||||
if (cmd != NULL) | if (cmd != NULL) | ||||
free(cmd); | free(cmd); | ||||
if (buf != NULL) | if (buf != NULL) | ||||
free(buf); | free(buf); | ||||
if (loaderbuf != NULL) | if (loaderbuf != NULL) | ||||
free(loaderbuf); | free(loaderbuf); | ||||
return (status); | return (status); | ||||
} | } | ||||
/* | EFI_STATUS | ||||
* probe_handle determines if the passed handle represents a logical partition | main(int argc __unused, CHAR16 *argv[] __unused) | ||||
* if it does it uses each module in order to probe it and if successful it | |||||
* returns EFI_SUCCESS. | |||||
*/ | |||||
static EFI_STATUS | |||||
probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath, BOOLEAN *preferred) | |||||
{ | { | ||||
dev_info_t *devinfo; | |||||
EFI_BLOCK_IO *blkio; | |||||
EFI_DEVICE_PATH *devpath; | |||||
EFI_STATUS status; | EFI_STATUS status; | ||||
UINTN i; | |||||
/* Figure out if we're dealing with an actual partition. */ | SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; | ||||
status = bs->HandleProtocol(h, &DevicePathGUID, (void **)&devpath); | UINTN i, max_dim, best_mode, cols, rows; | ||||
if (status == EFI_UNSUPPORTED) | |||||
return (status); | |||||
if (status != EFI_SUCCESS) { | archsw.arch_autoload = efi_autoload; | ||||
DPRINTF("\nFailed to query DevicePath (%lu)\n", | archsw.arch_getdev = efi_getdev; | ||||
EFI_ERROR_CODE(status)); | archsw.arch_copyin = efi_copyin; | ||||
return (status); | archsw.arch_copyout = efi_copyout; | ||||
} | archsw.arch_readin = efi_readin; | ||||
#ifdef EFI_ZFS_BOOT | |||||
/* Note this needs to be set before ZFS init. */ | |||||
archsw.arch_zfs_probe = efi_zfs_probe; | |||||
#endif | |||||
DPRINTF("probing: %s\n", devpath_str(devpath)); | /* Init the time source */ | ||||
efi_time_init(); | |||||
status = bs->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio); | cons_probe(); | ||||
if (status == EFI_UNSUPPORTED) | |||||
return (status); | |||||
if (status != EFI_SUCCESS) { | |||||
DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n", | |||||
EFI_ERROR_CODE(status)); | |||||
return (status); | |||||
} | |||||
if (!blkio->Media->LogicalPartition) | |||||
return (EFI_UNSUPPORTED); | |||||
*preferred = device_paths_match(imgpath, devpath); | |||||
/* Run through each module, see if it can load this partition */ | |||||
for (i = 0; i < NUM_BOOT_MODULES; i++) { | |||||
if ((status = bs->AllocatePool(EfiLoaderData, | |||||
sizeof(*devinfo), (void **)&devinfo)) != | |||||
EFI_SUCCESS) { | |||||
DPRINTF("\nFailed to allocate devinfo (%lu)\n", | |||||
EFI_ERROR_CODE(status)); | |||||
continue; | |||||
} | |||||
devinfo->dev = blkio; | |||||
devinfo->devpath = devpath; | |||||
devinfo->devhandle = h; | |||||
devinfo->devdata = NULL; | |||||
devinfo->preferred = *preferred; | |||||
devinfo->next = NULL; | |||||
status = boot_modules[i]->probe(devinfo); | |||||
if (status == EFI_SUCCESS) | |||||
return (EFI_SUCCESS); | |||||
(void)bs->FreePool(devinfo); | |||||
} | |||||
return (EFI_UNSUPPORTED); | |||||
} | |||||
/* | /* | ||||
* probe_handle_status calls probe_handle and outputs the returned status | |||||
* of the call. | |||||
*/ | |||||
static void | |||||
probe_handle_status(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath) | |||||
{ | |||||
EFI_STATUS status; | |||||
BOOLEAN preferred; | |||||
preferred = FALSE; | |||||
status = probe_handle(h, imgpath, &preferred); | |||||
DPRINTF("probe: "); | |||||
switch (status) { | |||||
case EFI_UNSUPPORTED: | |||||
printf("."); | |||||
DPRINTF(" not supported\n"); | |||||
break; | |||||
case EFI_SUCCESS: | |||||
if (preferred) { | |||||
printf("%c", '*'); | |||||
DPRINTF(" supported (preferred)\n"); | |||||
} else { | |||||
printf("%c", '+'); | |||||
DPRINTF(" supported\n"); | |||||
} | |||||
break; | |||||
default: | |||||
printf("x"); | |||||
DPRINTF(" error (%lu)\n", EFI_ERROR_CODE(status)); | |||||
break; | |||||
} | |||||
DSTALL(500000); | |||||
} | |||||
EFI_STATUS | |||||
efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab) | |||||
{ | |||||
EFI_HANDLE *handles; | |||||
EFI_LOADED_IMAGE *img; | |||||
EFI_DEVICE_PATH *imgpath; | |||||
EFI_STATUS status; | |||||
EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; | |||||
SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; | |||||
UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles; | |||||
/* Basic initialization*/ | |||||
systab = Xsystab; | |||||
image = Ximage; | |||||
bs = Xsystab->BootServices; | |||||
/* Set up the console, so printf works. */ | |||||
status = bs->LocateProtocol(&ConsoleControlGUID, NULL, | |||||
(VOID **)&ConsoleControl); | |||||
if (status == EFI_SUCCESS) | |||||
(void)ConsoleControl->SetMode(ConsoleControl, | |||||
EfiConsoleControlScreenText); | |||||
/* | |||||
* Reset the console and find the best text mode. | * Reset the console and find the best text mode. | ||||
*/ | */ | ||||
conout = systab->ConOut; | conout = ST->ConOut; | ||||
conout->Reset(conout, TRUE); | conout->Reset(conout, TRUE); | ||||
max_dim = best_mode = 0; | max_dim = best_mode = 0; | ||||
for (i = 0; ; i++) { | for (i = 0; ; i++) { | ||||
status = conout->QueryMode(conout, i, &cols, &rows); | status = conout->QueryMode(conout, i, &cols, &rows); | ||||
if (EFI_ERROR(status)) | if (EFI_ERROR(status)) | ||||
break; | break; | ||||
if (cols * rows > max_dim) { | if (cols * rows > max_dim) { | ||||
max_dim = cols * rows; | max_dim = cols * rows; | ||||
best_mode = i; | best_mode = i; | ||||
} | } | ||||
} | } | ||||
if (max_dim > 0) | if (max_dim > 0) | ||||
conout->SetMode(conout, best_mode); | conout->SetMode(conout, best_mode); | ||||
conout->EnableCursor(conout, TRUE); | conout->EnableCursor(conout, TRUE); | ||||
conout->ClearScreen(conout); | conout->ClearScreen(conout); | ||||
/* | |||||
* Initialise the block cache. Set the upper limit. | |||||
*/ | |||||
bcache_init(32768, 512); | |||||
printf("\n>> FreeBSD EFI boot block\n"); | printf("\n>> FreeBSD EFI boot block\n"); | ||||
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; | |||||
printf(" Loader path: %s\n\n", PATH_LOADER_EFI); | printf(" Loader path: %s\n\n", PATH_LOADER_EFI); | ||||
printf(" Initializing modules:"); | printf(" Initializing modules:"); | ||||
for (i = 0; i < NUM_BOOT_MODULES; i++) { | |||||
printf(" %s", boot_modules[i]->name); | |||||
if (boot_modules[i]->init != NULL) | |||||
boot_modules[i]->init(); | |||||
} | |||||
putchar('\n'); | |||||
/* Get all the device handles */ | bcache_init(32768, 512); | ||||
Done Inline ActionsThis shouldn't be deleted. imp: This shouldn't be deleted. | |||||
Done Inline ActionsThis shouldn't be deleted either. imp: This shouldn't be deleted either. | |||||
Done Inline ActionsDuplicate bcache_init ? manu: Duplicate bcache_init ? | |||||
Done Inline ActionsNo, this needs to be here. boot1 now uses the same codebase as loader. eric_metricspace.net: No, this needs to be here. boot1 now uses the same codebase as loader. | |||||
Done Inline ActionsYou sure? You ran bcache_init() on line 888, and then again on 901 allanjude: You sure? You ran bcache_init() on line 888, and then again on 901 | |||||
Done Inline ActionsSomething weird is happening with the diffs, because there's definitely only one in my local files. eric_metricspace.net: Something weird is happening with the diffs, because there's definitely only one in my local… | |||||
Done Inline ActionsActually, no, I was on the wrong branch and had apparently removed that in geli. eric_metricspace.net: Actually, no, I was on the wrong branch and had apparently removed that in geli. | |||||
hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE); | |||||
if ((status = bs->AllocatePool(EfiLoaderData, hsize, (void **)&handles)) | |||||
!= EFI_SUCCESS) | |||||
panic("Failed to allocate %d handles (%lu)", NUM_HANDLES_INIT, | |||||
EFI_ERROR_CODE(status)); | |||||
status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL, | for (i = 0; efi_drivers[i] != NULL; i++) { | ||||
&hsize, handles); | printf(" %s", efi_drivers[i]->name); | ||||
switch (status) { | if (efi_drivers[i]->init != NULL) | ||||
case EFI_SUCCESS: | efi_drivers[i]->init(); | ||||
break; | |||||
case EFI_BUFFER_TOO_SMALL: | |||||
(void)bs->FreePool(handles); | |||||
if ((status = bs->AllocatePool(EfiLoaderData, hsize, | |||||
(void **)&handles)) != EFI_SUCCESS) { | |||||
panic("Failed to allocate %zu handles (%lu)", hsize / | |||||
sizeof(*handles), EFI_ERROR_CODE(status)); | |||||
} | } | ||||
status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID, | |||||
NULL, &hsize, handles); | |||||
if (status != EFI_SUCCESS) | |||||
panic("Failed to get device handles (%lu)\n", | |||||
EFI_ERROR_CODE(status)); | |||||
break; | |||||
default: | |||||
panic("Failed to get device handles (%lu)", | |||||
EFI_ERROR_CODE(status)); | |||||
} | |||||
/* Scan all partitions, probing with all modules. */ | for (i = 0; devsw[i] != NULL; i++) { | ||||
nhandles = hsize / sizeof(*handles); | if (devsw[i]->dv_init != NULL) { | ||||
printf(" Probing %zu block devices...", nhandles); | printf(" %s", devsw[i]->dv_name); | ||||
DPRINTF("\n"); | (devsw[i]->dv_init)(); | ||||
/* Determine the devpath of our image so we can prefer it. */ | |||||
status = bs->HandleProtocol(image, &LoadedImageGUID, (VOID**)&img); | |||||
imgpath = NULL; | |||||
if (status == EFI_SUCCESS) { | |||||
status = bs->HandleProtocol(img->DeviceHandle, &DevicePathGUID, | |||||
(void **)&imgpath); | |||||
if (status != EFI_SUCCESS) | |||||
DPRINTF("Failed to get image DevicePath (%lu)\n", | |||||
EFI_ERROR_CODE(status)); | |||||
DPRINTF("boot1 imagepath: %s\n", devpath_str(imgpath)); | |||||
} | } | ||||
for (i = 0; i < nhandles; i++) | |||||
probe_handle_status(handles[i], imgpath); | |||||
printf(" done\n"); | |||||
/* Status summary. */ | |||||
for (i = 0; i < NUM_BOOT_MODULES; i++) { | |||||
printf(" "); | |||||
boot_modules[i]->status(); | |||||
} | } | ||||
putchar('\n'); | |||||
try_boot(); | try_boot(); | ||||
/* If we get here, we're out of luck... */ | /* If we get here, we're out of luck... */ | ||||
panic("No bootable partitions found!"); | panic("No bootable partitions found!"); | ||||
} | |||||
/* | |||||
* add_device adds a device to the passed devinfo list. | |||||
*/ | |||||
void | |||||
add_device(dev_info_t **devinfop, dev_info_t *devinfo) | |||||
{ | |||||
dev_info_t *dev; | |||||
if (*devinfop == NULL) { | |||||
*devinfop = devinfo; | |||||
return; | |||||
} | |||||
for (dev = *devinfop; dev->next != NULL; dev = dev->next) | |||||
; | |||||
dev->next = devinfo; | |||||
} | |||||
void | |||||
panic(const char *fmt, ...) | |||||
{ | |||||
va_list ap; | |||||
printf("panic: "); | |||||
va_start(ap, fmt); | |||||
vprintf(fmt, ap); | |||||
va_end(ap); | |||||
printf("\n"); | |||||
while (1) {} | |||||
} | |||||
void | |||||
putchar(int c) | |||||
{ | |||||
CHAR16 buf[2]; | |||||
if (c == '\n') { | |||||
buf[0] = '\r'; | |||||
buf[1] = 0; | |||||
systab->ConOut->OutputString(systab->ConOut, buf); | |||||
} | |||||
buf[0] = c; | |||||
buf[1] = 0; | |||||
systab->ConOut->OutputString(systab->ConOut, buf); | |||||
} | } |
'bs' appears to have been renamed 'BS', but this macro was not updated.