Changeset View
Changeset View
Standalone View
Standalone View
head/sys/boot/efi/boot1/boot1.c
/*- | /*- | ||||
* Copyright (c) 1998 Robert Nordier | * Copyright (c) 1998 Robert Nordier | ||||
* All rights reserved. | * All rights reserved. | ||||
* Copyright (c) 2001 Robert Drehmel | * Copyright (c) 2001 Robert Drehmel | ||||
* All rights reserved. | * All rights reserved. | ||||
* Copyright (c) 2014 Nathan Whitehorn | * Copyright (c) 2014 Nathan Whitehorn | ||||
* All rights reserved. | * All rights reserved. | ||||
* Copyright (c) 2015 Eric McCorkle | |||||
* All rights reserved. | |||||
* | * | ||||
* Redistribution and use in source and binary forms are freely | * Redistribution and use in source and binary forms are freely | ||||
* permitted provided that the above copyright notice and this | * permitted provided that the above copyright notice and this | ||||
* paragraph and the following disclaimer are duplicated in all | * paragraph and the following disclaimer are duplicated in all | ||||
* such forms. | * such forms. | ||||
* | * | ||||
* This software is provided "AS IS" and without any express or | * This software is provided "AS IS" and without any express or | ||||
* implied warranties, including, without limitation, the implied | * implied warranties, including, without limitation, the implied | ||||
* 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/dirent.h> | |||||
#include <machine/elf.h> | #include <machine/elf.h> | ||||
#include <machine/stdarg.h> | #include <machine/stdarg.h> | ||||
#include <stand.h> | #include <stand.h> | ||||
#include <efi.h> | #include <efi.h> | ||||
#include <eficonsctl.h> | #include <eficonsctl.h> | ||||
#include "boot_module.h" | |||||
#define _PATH_LOADER "/boot/loader.efi" | #define _PATH_LOADER "/boot/loader.efi" | ||||
#define _PATH_KERNEL "/boot/kernel/kernel" | |||||
#define BSIZEMAX 16384 | static const boot_module_t *boot_modules[] = | ||||
{ | |||||
#ifdef EFI_UFS_BOOT | |||||
&ufs_module | |||||
#endif | |||||
}; | |||||
void panic(const char *fmt, ...) __dead2; | #define NUM_BOOT_MODULES (sizeof(boot_modules) / sizeof(boot_module_t*)) | ||||
/* The initial number of handles used to query EFI for partitions. */ | |||||
#define NUM_HANDLES_INIT 24 | |||||
void putchar(int c); | void putchar(int c); | ||||
EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab); | EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab); | ||||
static int domount(EFI_DEVICE_PATH *device, EFI_BLOCK_IO *blkio, int quiet); | static void try_load(const boot_module_t* mod); | ||||
static void load(const char *fname); | static EFI_STATUS probe_handle(EFI_HANDLE h); | ||||
static EFI_SYSTEM_TABLE *systab; | EFI_SYSTEM_TABLE *systab; | ||||
EFI_BOOT_SERVICES *bs; | |||||
static EFI_HANDLE *image; | static EFI_HANDLE *image; | ||||
static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; | 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 ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; | ||||
static EFI_BLOCK_IO *bootdev; | /* | ||||
static EFI_DEVICE_PATH *bootdevpath; | * Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures | ||||
static EFI_HANDLE *bootdevhandle; | * 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; | |||||
EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab) | if (bs->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS) | ||||
return (out); | |||||
return (NULL); | |||||
} | |||||
void | |||||
Free(void *buf, const char *file __unused, int line __unused) | |||||
{ | { | ||||
EFI_HANDLE handles[128]; | (void)bs->FreePool(buf); | ||||
EFI_BLOCK_IO *blkio; | } | ||||
UINTN i, nparts = sizeof(handles), cols, rows, max_dim, best_mode; | |||||
/* | |||||
* This function only returns if it fails to load the kernel. If it | |||||
* succeeds, it simply boots the kernel. | |||||
*/ | |||||
void | |||||
try_load(const boot_module_t *mod) | |||||
{ | |||||
size_t bufsize; | |||||
void *buf; | |||||
dev_info_t *dev; | |||||
EFI_HANDLE loaderhandle; | |||||
EFI_LOADED_IMAGE *loaded_image; | |||||
EFI_STATUS status; | EFI_STATUS status; | ||||
EFI_DEVICE_PATH *devpath; | |||||
EFI_BOOT_SERVICES *BS; | status = mod->load(_PATH_LOADER, &dev, &buf, &bufsize); | ||||
if (status == EFI_NOT_FOUND) | |||||
return; | |||||
if (status != EFI_SUCCESS) { | |||||
printf("%s failed to load %s (%lu)\n", mod->name, _PATH_LOADER, | |||||
EFI_ERROR_CODE(status)); | |||||
return; | |||||
} | |||||
if ((status = bs->LoadImage(TRUE, image, dev->devpath, buf, bufsize, | |||||
&loaderhandle)) != EFI_SUCCESS) { | |||||
printf("Failed to load image provided by %s, size: %zu, (%lu)\n", | |||||
mod->name, bufsize, EFI_ERROR_CODE(status)); | |||||
return; | |||||
} | |||||
if ((status = bs->HandleProtocol(loaderhandle, &LoadedImageGUID, | |||||
(VOID**)&loaded_image)) != EFI_SUCCESS) { | |||||
printf("Failed to query LoadedImage provided by %s (%lu)\n", | |||||
mod->name, EFI_ERROR_CODE(status)); | |||||
return; | |||||
} | |||||
loaded_image->DeviceHandle = dev->devhandle; | |||||
if ((status = bs->StartImage(loaderhandle, NULL, NULL)) != | |||||
EFI_SUCCESS) { | |||||
printf("Failed start image provided by %s (%lu)\n", mod->name, | |||||
EFI_ERROR_CODE(status)); | |||||
return; | |||||
} | |||||
} | |||||
EFI_STATUS | |||||
efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab) | |||||
{ | |||||
EFI_HANDLE *handles; | |||||
EFI_STATUS status; | |||||
EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; | EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; | ||||
SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; | SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; | ||||
const char *path = _PATH_LOADER; | UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles; | ||||
/* Basic initialization*/ | |||||
systab = Xsystab; | systab = Xsystab; | ||||
image = Ximage; | image = Ximage; | ||||
bs = Xsystab->BootServices; | |||||
BS = systab->BootServices; | /* Set up the console, so printf works. */ | ||||
status = BS->LocateProtocol(&ConsoleControlGUID, NULL, | status = bs->LocateProtocol(&ConsoleControlGUID, NULL, | ||||
(VOID **)&ConsoleControl); | (VOID **)&ConsoleControl); | ||||
if (status == EFI_SUCCESS) | if (status == EFI_SUCCESS) | ||||
(void)ConsoleControl->SetMode(ConsoleControl, | (void)ConsoleControl->SetMode(ConsoleControl, | ||||
EfiConsoleControlScreenText); | EfiConsoleControlScreenText); | ||||
/* | /* | ||||
* Reset the console and find the best text mode. | * Reset the console and find the best text mode. | ||||
*/ | */ | ||||
conout = systab->ConOut; | conout = systab->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); | ||||
printf("\n" | printf("\n>> FreeBSD EFI boot block\n"); | ||||
">> FreeBSD EFI boot block\n"); | printf(" Loader path: %s\n\n", _PATH_LOADER); | ||||
printf(" Loader path: %s\n", path); | printf(" Initializing modules:"); | ||||
for (i = 0; i < NUM_BOOT_MODULES; i++) { | |||||
status = systab->BootServices->LocateHandle(ByProtocol, | if (boot_modules[i] == NULL) | ||||
&BlockIoProtocolGUID, NULL, &nparts, handles); | |||||
nparts /= sizeof(handles[0]); | |||||
for (i = 0; i < nparts; i++) { | |||||
status = systab->BootServices->HandleProtocol(handles[i], | |||||
&DevicePathGUID, (void **)&devpath); | |||||
if (EFI_ERROR(status)) | |||||
continue; | continue; | ||||
while (!IsDevicePathEnd(NextDevicePathNode(devpath))) | printf(" %s", boot_modules[i]->name); | ||||
devpath = NextDevicePathNode(devpath); | if (boot_modules[i]->init != NULL) | ||||
boot_modules[i]->init(); | |||||
} | |||||
putchar('\n'); | |||||
status = systab->BootServices->HandleProtocol(handles[i], | /* Get all the device handles */ | ||||
&BlockIoProtocolGUID, (void **)&blkio); | hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE); | ||||
if (EFI_ERROR(status)) | if ((status = bs->AllocatePool(EfiLoaderData, hsize, (void **)&handles)) | ||||
continue; | != EFI_SUCCESS) | ||||
panic("Failed to allocate %d handles (%lu)", NUM_HANDLES_INIT, | |||||
EFI_ERROR_CODE(status)); | |||||
if (!blkio->Media->LogicalPartition) | status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL, | ||||
continue; | &hsize, handles); | ||||
switch (status) { | |||||
case EFI_SUCCESS: | |||||
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)); | |||||
} | |||||
if (domount(devpath, blkio, 1) >= 0) | /* Scan all partitions, probing with all modules. */ | ||||
nhandles = hsize / sizeof(*handles); | |||||
printf(" Probing %zu block devices...", nhandles); | |||||
for (i = 0; i < nhandles; i++) { | |||||
status = probe_handle(handles[i]); | |||||
switch (status) { | |||||
case EFI_UNSUPPORTED: | |||||
printf("."); | |||||
break; | break; | ||||
case EFI_SUCCESS: | |||||
printf("+"); | |||||
break; | |||||
default: | |||||
printf("x"); | |||||
break; | |||||
} | } | ||||
} | |||||
printf(" done\n"); | |||||
if (i == nparts) | /* Status summary. */ | ||||
panic("No bootable partition found"); | for (i = 0; i < NUM_BOOT_MODULES; i++) { | ||||
if (boot_modules[i] != NULL) { | |||||
printf(" "); | |||||
boot_modules[i]->status(); | |||||
} | |||||
} | |||||
bootdevhandle = handles[i]; | /* Select a partition to boot by trying each module in order. */ | ||||
load(path); | for (i = 0; i < NUM_BOOT_MODULES; i++) | ||||
if (boot_modules[i] != NULL) | |||||
try_load(boot_modules[i]); | |||||
panic("Load failed"); | /* If we get here, we're out of luck... */ | ||||
panic("No bootable partitions found!"); | |||||
return EFI_SUCCESS; | |||||
} | } | ||||
static int | static EFI_STATUS | ||||
dskread(void *buf, u_int64_t lba, int nblk) | probe_handle(EFI_HANDLE h) | ||||
{ | { | ||||
dev_info_t *devinfo; | |||||
EFI_BLOCK_IO *blkio; | |||||
EFI_DEVICE_PATH *devpath; | |||||
EFI_STATUS status; | EFI_STATUS status; | ||||
int size; | UINTN i; | ||||
lba = lba / (bootdev->Media->BlockSize / DEV_BSIZE); | /* Figure out if we're dealing with an actual partition. */ | ||||
size = nblk * DEV_BSIZE; | status = bs->HandleProtocol(h, &DevicePathGUID, (void **)&devpath); | ||||
status = bootdev->ReadBlocks(bootdev, bootdev->Media->MediaId, lba, | if (status == EFI_UNSUPPORTED) | ||||
size, buf); | return (status); | ||||
if (EFI_ERROR(status)) | if (status != EFI_SUCCESS) { | ||||
return (-1); | DPRINTF("\nFailed to query DevicePath (%lu)\n", | ||||
EFI_ERROR_CODE(status)); | |||||
return (0); | return (status); | ||||
} | } | ||||
#include "ufsread.c" | while (!IsDevicePathEnd(NextDevicePathNode(devpath))) | ||||
devpath = NextDevicePathNode(devpath); | |||||
static ssize_t | status = bs->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio); | ||||
fsstat(ufs_ino_t inode) | if (status == EFI_UNSUPPORTED) | ||||
{ | return (status); | ||||
#ifndef UFS2_ONLY | |||||
static struct ufs1_dinode dp1; | |||||
#endif | |||||
#ifndef UFS1_ONLY | |||||
static struct ufs2_dinode dp2; | |||||
#endif | |||||
static struct fs fs; | |||||
static ufs_ino_t inomap; | |||||
char *blkbuf; | |||||
void *indbuf; | |||||
size_t n, size; | |||||
static ufs2_daddr_t blkmap, indmap; | |||||
blkbuf = dmadat->blkbuf; | if (status != EFI_SUCCESS) { | ||||
indbuf = dmadat->indbuf; | DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n", | ||||
if (!dsk_meta) { | EFI_ERROR_CODE(status)); | ||||
inomap = 0; | return (status); | ||||
for (n = 0; sblock_try[n] != -1; n++) { | |||||
if (dskread(dmadat->sbbuf, sblock_try[n] / DEV_BSIZE, | |||||
SBLOCKSIZE / DEV_BSIZE)) | |||||
return -1; | |||||
memcpy(&fs, dmadat->sbbuf, sizeof(struct fs)); | |||||
if (( | |||||
#if defined(UFS1_ONLY) | |||||
fs.fs_magic == FS_UFS1_MAGIC | |||||
#elif defined(UFS2_ONLY) | |||||
(fs.fs_magic == FS_UFS2_MAGIC && | |||||
fs.fs_sblockloc == sblock_try[n]) | |||||
#else | |||||
fs.fs_magic == FS_UFS1_MAGIC || | |||||
(fs.fs_magic == FS_UFS2_MAGIC && | |||||
fs.fs_sblockloc == sblock_try[n]) | |||||
#endif | |||||
) && | |||||
fs.fs_bsize <= MAXBSIZE && | |||||
fs.fs_bsize >= (int32_t)sizeof(struct fs)) | |||||
break; | |||||
} | } | ||||
if (sblock_try[n] == -1) { | |||||
return -1; | |||||
} | |||||
dsk_meta++; | |||||
} else | |||||
memcpy(&fs, dmadat->sbbuf, sizeof(struct fs)); | |||||
if (!inode) | |||||
return 0; | |||||
if (inomap != inode) { | |||||
n = IPERVBLK(&fs); | |||||
if (dskread(blkbuf, INO_TO_VBA(&fs, n, inode), DBPERVBLK)) | |||||
return -1; | |||||
n = INO_TO_VBO(n, inode); | |||||
#if defined(UFS1_ONLY) | |||||
memcpy(&dp1, (struct ufs1_dinode *)blkbuf + n, | |||||
sizeof(struct ufs1_dinode)); | |||||
#elif defined(UFS2_ONLY) | |||||
memcpy(&dp2, (struct ufs2_dinode *)blkbuf + n, | |||||
sizeof(struct ufs2_dinode)); | |||||
#else | |||||
if (fs.fs_magic == FS_UFS1_MAGIC) | |||||
memcpy(&dp1, (struct ufs1_dinode *)(void *)blkbuf + n, | |||||
sizeof(struct ufs1_dinode)); | |||||
else | |||||
memcpy(&dp2, (struct ufs2_dinode *)(void *)blkbuf + n, | |||||
sizeof(struct ufs2_dinode)); | |||||
#endif | |||||
inomap = inode; | |||||
fs_off = 0; | |||||
blkmap = indmap = 0; | |||||
} | |||||
size = DIP(di_size); | |||||
n = size - fs_off; | |||||
return (n); | |||||
} | |||||
static struct dmadat __dmadat; | if (!blkio->Media->LogicalPartition) | ||||
return (EFI_UNSUPPORTED); | |||||
static int | /* Run through each module, see if it can load this partition */ | ||||
domount(EFI_DEVICE_PATH *device, EFI_BLOCK_IO *blkio, int quiet) | for (i = 0; i < NUM_BOOT_MODULES; i++) { | ||||
{ | if (boot_modules[i] == NULL) | ||||
continue; | |||||
dmadat = &__dmadat; | if ((status = bs->AllocatePool(EfiLoaderData, | ||||
bootdev = blkio; | sizeof(*devinfo), (void **)&devinfo)) != | ||||
bootdevpath = device; | EFI_SUCCESS) { | ||||
if (fsread(0, NULL, 0)) { | DPRINTF("\nFailed to allocate devinfo (%lu)\n", | ||||
if (!quiet) | EFI_ERROR_CODE(status)); | ||||
printf("domount: can't read superblock\n"); | continue; | ||||
return (-1); | |||||
} | } | ||||
if (!quiet) | devinfo->dev = blkio; | ||||
printf("Succesfully mounted UFS filesystem\n"); | devinfo->devpath = devpath; | ||||
return (0); | devinfo->devhandle = h; | ||||
devinfo->devdata = NULL; | |||||
devinfo->next = NULL; | |||||
status = boot_modules[i]->probe(devinfo); | |||||
if (status == EFI_SUCCESS) | |||||
return (EFI_SUCCESS); | |||||
(void)bs->FreePool(devinfo); | |||||
} | } | ||||
static void | return (EFI_UNSUPPORTED); | ||||
load(const char *fname) | } | ||||
void | |||||
add_device(dev_info_t **devinfop, dev_info_t *devinfo) | |||||
{ | { | ||||
ufs_ino_t ino; | dev_info_t *dev; | ||||
EFI_STATUS status; | |||||
EFI_HANDLE loaderhandle; | |||||
EFI_LOADED_IMAGE *loaded_image; | |||||
void *buffer; | |||||
size_t bufsize; | |||||
if ((ino = lookup(fname)) == 0) { | if (*devinfop == NULL) { | ||||
printf("File %s not found\n", fname); | *devinfop = devinfo; | ||||
return; | return; | ||||
} | } | ||||
bufsize = fsstat(ino); | for (dev = *devinfop; dev->next != NULL; dev = dev->next) | ||||
status = systab->BootServices->AllocatePool(EfiLoaderData, | ; | ||||
bufsize, &buffer); | |||||
fsread(ino, buffer, bufsize); | |||||
/* XXX: For secure boot, we need our own loader here */ | dev->next = devinfo; | ||||
status = systab->BootServices->LoadImage(TRUE, image, bootdevpath, | |||||
buffer, bufsize, &loaderhandle); | |||||
if (EFI_ERROR(status)) | |||||
printf("LoadImage failed with error %lu\n", | |||||
EFI_ERROR_CODE(status)); | |||||
status = systab->BootServices->HandleProtocol(loaderhandle, | |||||
&LoadedImageGUID, (VOID**)&loaded_image); | |||||
if (EFI_ERROR(status)) | |||||
printf("HandleProtocol failed with error %lu\n", | |||||
EFI_ERROR_CODE(status)); | |||||
loaded_image->DeviceHandle = bootdevhandle; | |||||
status = systab->BootServices->StartImage(loaderhandle, NULL, NULL); | |||||
if (EFI_ERROR(status)) | |||||
printf("StartImage failed with error %lu\n", | |||||
EFI_ERROR_CODE(status)); | |||||
} | } | ||||
void | void | ||||
panic(const char *fmt, ...) | panic(const char *fmt, ...) | ||||
{ | { | ||||
va_list ap; | va_list ap; | ||||
printf("panic: "); | printf("panic: "); | ||||
Show All 22 Lines |