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 | |||||
typedef CHAR16 efi_char; | typedef CHAR16 efi_char; | ||||
#include <efichar.h> | #include <efichar.h> | ||||
#include "boot_module.h" | #include <bootstrap.h> | ||||
#include "efi_drivers.h" | |||||
#include "efizfs.h" | |||||
#include "paths.h" | #include "paths.h" | ||||
static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3); | static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3); | ||||
#ifdef EFI_DEBUG | |||||
#define DPRINTF(fmt, args...) printf(fmt, ##args) | |||||
#define DSTALL(d) BS->Stall(d) | |||||
#else | |||||
#define DPRINTF(fmt, ...) {} | |||||
#define DSTALL(d) {} | |||||
#endif | |||||
static const boot_module_t *boot_modules[] = | 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; | ||||
allanjude: 'bs' appears to have been renamed 'BS', but this macro was not updated. | |||||
#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, | |||||
&gzipfs_fsops, | |||||
&bzipfs_fsops, | |||||
NULL | |||||
}; | }; | ||||
#define NUM_BOOT_MODULES nitems(boot_modules) | struct devsw *devsw[] = { | ||||
&efipart_hddev, | |||||
&efipart_fddev, | |||||
&efipart_cddev, | |||||
#ifdef EFI_ZFS_BOOT | |||||
&zfs_dev, | |||||
#endif | |||||
NULL | |||||
}; | |||||
Done Inline ActionsWhy no more ifdef EFI_UFS_BOOT ? manu: Why no more ifdef EFI_UFS_BOOT ? | |||||
struct console *consoles[] = { | |||||
&efi_console, | |||||
NULL | |||||
}; | |||||
static EFI_LOADED_IMAGE *boot_image; | |||||
static EFI_DEVICE_PATH *imgpath; | |||||
static EFI_DEVICE_PATH *imgprefix; | |||||
/* Definitions we don't actually need for boot, but we need to define | |||||
* to make the linker happy. | |||||
*/ | |||||
struct file_format *file_formats[] = { NULL }; | |||||
struct netif_driver *netif_drivers[] = { NULL }; | |||||
static int | |||||
efi_autoload(void) | |||||
{ | |||||
printf("******** Boot block should not call autoload\n"); | |||||
return (-1); | |||||
} | |||||
static ssize_t | |||||
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); | |||||
} | |||||
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); | |||||
} | |||||
static ssize_t | |||||
efi_readin(int fd __unused, vm_offset_t dest __unused, | |||||
const size_t len __unused) | |||||
{ | |||||
printf("******** Boot block should not call readin\n"); | |||||
return (-1); | |||||
} | |||||
/* The initial number of handles used to query EFI for partitions. */ | /* The initial number of handles used to query EFI for partitions. */ | ||||
#define NUM_HANDLES_INIT 24 | #define NUM_HANDLES_INIT 24 | ||||
static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; | |||||
static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; | static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; | ||||
static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; | static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; | ||||
static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; | |||||
static EFI_GUID FreeBSDBootVarGUID = FREEBSD_BOOT_VAR_GUID; | |||||
/* | static EFI_STATUS | ||||
* Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures | do_load(const char *filepath, void **bufp, size_t *bufsize) | ||||
* 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; | struct stat st; | ||||
void *buf = NULL; | |||||
int fd, err; | |||||
size_t fsize, remaining; | |||||
ssize_t readsize; | |||||
if (BS->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS) | if ((fd = open(filepath, O_RDONLY)) < 0) { | ||||
return (out); | return (ENOTSUP); | ||||
} | |||||
return (NULL); | if ((err = fstat(fd, &st)) != 0) { | ||||
goto close_file; | |||||
} | } | ||||
void | fsize = st.st_size; | ||||
Free(void *buf, const char *file __unused, int line __unused) | |||||
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) | |||||
{ | { | ||||
if (buf != NULL) | int fd; | ||||
(void)BS->FreePool(buf); | |||||
if ((fd = open(filepath, O_RDONLY)) < 0) { | |||||
return (ENOTSUP); | |||||
} | } | ||||
static EFI_STATUS | close(fd); | ||||
efi_setenv_freebsd_wcs(const char *varname, CHAR16 *valstr) | |||||
return (0); | |||||
} | |||||
static int | |||||
probe_dev(struct devsw *dev, int unit, const char *filepath) | |||||
{ | { | ||||
CHAR16 *var = NULL; | struct devdesc currdev; | ||||
size_t len; | char *devname; | ||||
EFI_STATUS rv; | int err; | ||||
utf8_to_ucs2(varname, &var, &len); | currdev.d_dev = dev; | ||||
if (var == NULL) | currdev.d_type = currdev.d_dev->dv_type; | ||||
return (EFI_OUT_OF_RESOURCES); | currdev.d_unit = unit; | ||||
rv = RS->SetVariable(var, &FreeBSDBootVarGUID, | currdev.d_opendata = NULL; | ||||
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, | devname = efi_fmtdev(&currdev); | ||||
(ucs2len(valstr) + 1) * sizeof(efi_char), valstr); | |||||
free(var); | env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, | ||||
return (rv); | env_nounset); | ||||
err = probe_fs(filepath); | |||||
return (err); | |||||
} | } | ||||
/* | static bool | ||||
* nodes_match returns TRUE if the imgpath isn't NULL and the nodes match, | check_preferred(EFI_HANDLE *h) | ||||
* FALSE otherwise. | |||||
*/ | |||||
static BOOLEAN | |||||
nodes_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath) | |||||
{ | { | ||||
size_t len; | EFI_DEVICE_PATH *path = efi_lookup_devpath(h); | ||||
bool out; | |||||
if (imgpath == NULL || imgpath->Type != devpath->Type || | if ((path = efi_lookup_devpath(h)) == NULL) | ||||
imgpath->SubType != devpath->SubType) | return (false); | ||||
return (FALSE); | |||||
len = DevicePathNodeLength(imgpath); | out = efi_devpath_is_prefix(imgpath, path) || | ||||
if (len != DevicePathNodeLength(devpath)) | efi_devpath_is_prefix(imgprefix, path); | ||||
return (FALSE); | |||||
return (memcmp(imgpath, devpath, (size_t)len) == 0); | return (out); | ||||
} | } | ||||
/* | bool | ||||
* device_paths_match returns TRUE if the imgpath isn't NULL and all nodes | efi_zfs_is_preferred(EFI_HANDLE *h) | ||||
* in imgpath and devpath match up to their respective occurrences of a | |||||
* media node, FALSE otherwise. | |||||
*/ | |||||
static BOOLEAN | |||||
device_paths_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath) | |||||
{ | { | ||||
return (check_preferred(h)); | |||||
} | |||||
if (imgpath == NULL) | static int | ||||
return (FALSE); | 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; | |||||
char *devname; | |||||
while (!IsDevicePathEnd(imgpath) && !IsDevicePathEnd(devpath)) { | #ifdef EFI_ZFS_BOOT | ||||
if (IsDevicePathType(imgpath, MEDIA_DEVICE_PATH) && | /* Did efi_zfs_probe() detect the boot pool? */ | ||||
IsDevicePathType(devpath, MEDIA_DEVICE_PATH)) | if (pool_guid != 0) { | ||||
return (TRUE); | struct zfs_devdesc currdev; | ||||
if (!nodes_match(imgpath, devpath)) | currdev.d_dev = &zfs_dev; | ||||
return (FALSE); | 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); | |||||
imgpath = NextDevicePathNode(imgpath); | env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, | ||||
devpath = NextDevicePathNode(devpath); | env_nounset); | ||||
if (probe_fs(filepath) == 0 && | |||||
do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { | |||||
*handlep = img->DeviceHandle; | |||||
return (0); | |||||
} | } | ||||
} | |||||
#endif /* EFI_ZFS_BOOT */ | |||||
return (FALSE); | /* 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 (check_preferred(dp->pd_handle) && | |||||
probe_fs(filepath) == 0 && | |||||
do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { | |||||
*handlep = img->DeviceHandle; | |||||
return (0); | |||||
} | } | ||||
/* | /* Assuming GPT partitioning. */ | ||||
* devpath_last returns the last non-path end node in devpath. | STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { | ||||
*/ | if (check_preferred(pp->pd_handle)) { | ||||
static EFI_DEVICE_PATH * | currdev.d_slice = pp->pd_unit; | ||||
devpath_last(EFI_DEVICE_PATH *devpath) | currdev.d_partition = 255; | ||||
{ | devname = efi_fmtdev(&currdev); | ||||
while (!IsDevicePathEnd(NextDevicePathNode(devpath))) | env_setenv("currdev", EV_VOLATILE, devname, | ||||
devpath = NextDevicePathNode(devpath); | efi_setcurrdev, env_nounset); | ||||
return (devpath); | if (probe_fs(filepath) == 0 && | ||||
do_load(filepath, bufp, bufsize) == | |||||
EFI_SUCCESS) { | |||||
*handlep = img->DeviceHandle; | |||||
return (0); | |||||
} | } | ||||
} | |||||
} | |||||
} | |||||
/* | pdi_list = efiblk_get_pdinfo_list(&efipart_cddev); | ||||
* load_loader attempts to load the loader image data. | STAILQ_FOREACH(dp, pdi_list, pd_link) { | ||||
* | if ((dp->pd_handle == img->DeviceHandle || | ||||
* It tries each module and its respective devices, identified by mod->probe, | dp->pd_alias == img->DeviceHandle || | ||||
* in order until a successful load occurs at which point it returns EFI_SUCCESS | check_preferred(dp->pd_handle)) && | ||||
* and EFI_NOT_FOUND otherwise. | probe_dev(&efipart_cddev, dp->pd_unit, filepath) == 0 && | ||||
* | do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { | ||||
* Only devices which have preferred matching the preferred parameter are tried. | *handlep = img->DeviceHandle; | ||||
*/ | return (0); | ||||
static EFI_STATUS | } | ||||
load_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp, | } | ||||
size_t *bufsize, BOOLEAN preferred) | |||||
pdi_list = efiblk_get_pdinfo_list(&efipart_fddev); | |||||
STAILQ_FOREACH(dp, pdi_list, pd_link) { | |||||
if ((dp->pd_handle == img->DeviceHandle || | |||||
check_preferred(dp->pd_handle)) && | |||||
probe_dev(&efipart_cddev, dp->pd_unit, filepath) == 0 && | |||||
do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { | |||||
*handlep = img->DeviceHandle; | |||||
return (0); | |||||
} | |||||
} | |||||
return (ENOENT); | |||||
} | |||||
static int | |||||
load_all(const char *filepath, void **bufp, size_t *bufsize, | |||||
EFI_HANDLE *handlep) | |||||
{ | { | ||||
UINTN i; | pdinfo_list_t *pdi_list; | ||||
dev_info_t *dev; | pdinfo_t *dp, *pp; | ||||
const boot_module_t *mod; | zfsinfo_list_t *zfsi_list; | ||||
zfsinfo_t *zi; | |||||
char *devname; | |||||
for (i = 0; i < NUM_BOOT_MODULES; i++) { | #ifdef EFI_ZFS_BOOT | ||||
mod = boot_modules[i]; | zfsi_list = efizfs_get_zfsinfo_list(); | ||||
for (dev = mod->devices(); dev != NULL; dev = dev->next) { | STAILQ_FOREACH(zi, zfsi_list, zi_link) { | ||||
if (dev->preferred != preferred) | struct zfs_devdesc currdev; | ||||
continue; | |||||
if (mod->load(PATH_LOADER_EFI, dev, bufp, bufsize) == | currdev.d_dev = &zfs_dev; | ||||
EFI_SUCCESS) { | currdev.d_unit = 0; | ||||
*devinfop = dev; | currdev.d_type = currdev.d_dev->dv_type; | ||||
*modp = mod; | currdev.d_opendata = NULL; | ||||
return (EFI_SUCCESS); | 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; | |||||
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; | |||||
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… | |||||
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. | |||||
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) | |||||
{ | |||||
/* 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; | ||||
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… | |||||
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, IH, devpath_last(dev->devpath), | if ((status = BS->LoadImage(TRUE, IH, efi_devpath_last_node(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); | ||||
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… | |||||
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); | ||||
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… | |||||
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_DEBUG | #ifdef EFI_ZFS_BOOT | ||||
{ | /* Note this needs to be set before ZFS init. */ | ||||
CHAR16 *text = efi_devpath_name(devpath); | archsw.arch_zfs_probe = efi_zfs_probe; | ||||
DPRINTF("probing: %S\n", text); | |||||
efi_free_devpath_name(text); | |||||
} | |||||
#endif | #endif | ||||
status = BS->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio); | |||||
if (status == EFI_UNSUPPORTED) | |||||
return (status); | |||||
if (status != EFI_SUCCESS) { | /* Init the time source */ | ||||
DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n", | efi_time_init(); | ||||
EFI_ERROR_CODE(status)); | cons_probe(); | ||||
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++) { | |||||
devinfo = malloc(sizeof(*devinfo)); | |||||
if (devinfo == NULL) { | |||||
DPRINTF("\nFailed to allocate devinfo\n"); | |||||
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); | |||||
free(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; | |||||
CHAR16 *text; | |||||
/* Basic initialization*/ | |||||
ST = Xsystab; | |||||
IH = Ximage; | |||||
BS = ST->BootServices; | |||||
RS = ST->RuntimeServices; | |||||
/* 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 = ST->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); | ||||
/* Print this here, so people know it's at least starting. */ | |||||
printf("\n>> FreeBSD EFI boot block\n"); | printf("\n>> FreeBSD EFI boot block\n"); | ||||
printf(" Loader path: %s\n\n", PATH_LOADER_EFI); | printf(" Loader path: %s\n\n", PATH_LOADER_EFI); | ||||
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'); | |||||
/* Determine the devpath of our image so we can prefer it. */ | /* Get the image path and trim it to get the disk on which we | ||||
status = BS->HandleProtocol(IH, &LoadedImageGUID, (VOID**)&img); | * found this loader. | ||||
imgpath = NULL; | */ | ||||
if (status == EFI_SUCCESS) { | if ((status = BS->OpenProtocol(IH, &LoadedImageGUID, | ||||
text = efi_devpath_name(img->FilePath); | (VOID**)&boot_image, IH, NULL, | ||||
printf(" Load Path: %S\n", text); | EFI_OPEN_PROTOCOL_GET_PROTOCOL)) != EFI_SUCCESS) { | ||||
efi_setenv_freebsd_wcs("Boot1Path", text); | panic("Failed to query LoadedImage (%lu)\n", | ||||
efi_free_devpath_name(text); | |||||
Done Inline ActionsThis shouldn't be deleted. imp: This shouldn't be deleted. | |||||
status = BS->HandleProtocol(img->DeviceHandle, &DevicePathGUID, | |||||
(void **)&imgpath); | |||||
if (status != EFI_SUCCESS) { | |||||
DPRINTF("Failed to get image DevicePath (%lu)\n", | |||||
EFI_ERROR_CODE(status)); | EFI_ERROR_CODE(status)); | ||||
} else { | |||||
text = efi_devpath_name(imgpath); | |||||
printf(" Load Device: %S\n", text); | |||||
efi_setenv_freebsd_wcs("Boot1Dev", text); | |||||
efi_free_devpath_name(text); | |||||
} | } | ||||
Done Inline ActionsThis shouldn't be deleted either. imp: This shouldn't be deleted either. | |||||
if ((imgpath = efi_lookup_image_devpath(IH)) == NULL) { | |||||
panic("Couldn't obtain image device path!"); | |||||
} | } | ||||
/* Get all the device handles */ | /* The loaded image device path ends with a partition, then a | ||||
hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE); | * file path. Trim them both to get the actual disk. | ||||
handles = malloc(hsize); | */ | ||||
if (handles == NULL) { | if ((imgprefix = efi_devpath_trim(imgpath)) == NULL || | ||||
printf("Failed to allocate %d handles\n", NUM_HANDLES_INIT); | (imgprefix = efi_devpath_trim(imgprefix)) == NULL) { | ||||
panic("Couldn't trim device path"); | |||||
} | } | ||||
status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL, | /* | ||||
&hsize, handles); | * Initialize the block cache. Set the upper limit. | ||||
switch (status) { | */ | ||||
case EFI_SUCCESS: | bcache_init(32768, 512); | ||||
break; | |||||
case EFI_BUFFER_TOO_SMALL: | |||||
free(handles); | |||||
handles = malloc(hsize); | |||||
if (handles == NULL) | |||||
efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n", | |||||
NUM_HANDLES_INIT); | |||||
status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, | |||||
NULL, &hsize, handles); | |||||
if (status != EFI_SUCCESS) | |||||
efi_panic(status, "Failed to get device handles\n"); | |||||
break; | |||||
default: | |||||
efi_panic(status, "Failed to get device handles\n"); | |||||
break; | |||||
} | |||||
/* Scan all partitions, probing with all modules. */ | printf("\n Initializing modules:"); | ||||
nhandles = hsize / sizeof(*handles); | |||||
printf(" Probing %zu block devices...", nhandles); | |||||
DPRINTF("\n"); | |||||
for (i = 0; i < nhandles; i++) | for (i = 0; efi_drivers[i] != NULL; i++) { | ||||
probe_handle_status(handles[i], imgpath); | printf(" %s", efi_drivers[i]->name); | ||||
printf(" done\n"); | if (efi_drivers[i]->init != NULL) | ||||
efi_drivers[i]->init(); | |||||
} | |||||
/* Status summary. */ | for (i = 0; devsw[i] != NULL; i++) { | ||||
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. | |||||
for (i = 0; i < NUM_BOOT_MODULES; i++) { | if (devsw[i]->dv_init != NULL) { | ||||
printf(" "); | printf(" %s", devsw[i]->dv_name); | ||||
boot_modules[i]->status(); | (devsw[i]->dv_init)(); | ||||
} | } | ||||
} | |||||
putchar('\n'); | |||||
try_boot(); | try_boot(); | ||||
/* If we get here, we're out of luck... */ | /* If we get here, we're out of luck... */ | ||||
efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!"); | efi_panic(EFI_LOAD_ERROR, "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; | |||||
} | |||||
/* | |||||
* OK. We totally give up. Exit back to EFI with a sensible status so | * OK. We totally give up. Exit back to EFI with a sensible status so | ||||
* it can try the next option on the list. | * it can try the next option on the list. | ||||
*/ | */ | ||||
static void | static void | ||||
efi_panic(EFI_STATUS s, const char *fmt, ...) | efi_panic(EFI_STATUS s, const char *fmt, ...) | ||||
{ | { | ||||
va_list ap; | va_list ap; | ||||
printf("panic: "); | printf("panic: "); | ||||
va_start(ap, fmt); | va_start(ap, fmt); | ||||
vprintf(fmt, ap); | vprintf(fmt, ap); | ||||
va_end(ap); | va_end(ap); | ||||
printf("\n"); | printf("\n"); | ||||
BS->Exit(IH, s, 0, NULL); | BS->Exit(IH, s, 0, NULL); | ||||
} | |||||
void | |||||
putchar(int c) | |||||
{ | |||||
CHAR16 buf[2]; | |||||
if (c == '\n') { | |||||
buf[0] = '\r'; | |||||
buf[1] = 0; | |||||
ST->ConOut->OutputString(ST->ConOut, buf); | |||||
} | |||||
buf[0] = c; | |||||
buf[1] = 0; | |||||
ST->ConOut->OutputString(ST->ConOut, buf); | |||||
} | } |
'bs' appears to have been renamed 'BS', but this macro was not updated.