Changeset View
Changeset View
Standalone View
Standalone View
sys/boot/efi/loader/main.c
Show All 22 Lines | |||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | |||||
#include <stand.h> | #include <stand.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <setjmp.h> | #include <setjmp.h> | ||||
#include <efi.h> | #include <efi.h> | ||||
#include <efilib.h> | #include <efilib.h> | ||||
#include <bootstrap.h> | #include <bootstrap.h> | ||||
#include <smbios.h> | #include <smbios.h> | ||||
#include "loader_efi.h" | #include "loader_efi.h" | ||||
#ifdef EFI_ZFS_BOOT | |||||
#include "libzfs.h" | |||||
#endif | |||||
extern char bootprog_name[]; | extern char bootprog_name[]; | ||||
extern char bootprog_rev[]; | extern char bootprog_rev[]; | ||||
extern char bootprog_date[]; | extern char bootprog_date[]; | ||||
extern char bootprog_maker[]; | extern char bootprog_maker[]; | ||||
struct devdesc currdev; /* our current device */ | |||||
struct arch_switch archsw; /* MI/MD interface boundary */ | struct arch_switch archsw; /* MI/MD interface boundary */ | ||||
EFI_GUID acpi = ACPI_TABLE_GUID; | EFI_GUID acpi = ACPI_TABLE_GUID; | ||||
EFI_GUID acpi20 = ACPI_20_TABLE_GUID; | EFI_GUID acpi20 = ACPI_20_TABLE_GUID; | ||||
EFI_GUID devid = DEVICE_PATH_PROTOCOL; | EFI_GUID devid = DEVICE_PATH_PROTOCOL; | ||||
EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; | EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; | ||||
EFI_GUID mps = MPS_TABLE_GUID; | EFI_GUID mps = MPS_TABLE_GUID; | ||||
EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL; | EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL; | ||||
EFI_GUID smbios = SMBIOS_TABLE_GUID; | EFI_GUID smbios = SMBIOS_TABLE_GUID; | ||||
EFI_GUID dxe = DXE_SERVICES_TABLE_GUID; | EFI_GUID dxe = DXE_SERVICES_TABLE_GUID; | ||||
EFI_GUID hoblist = HOB_LIST_TABLE_GUID; | EFI_GUID hoblist = HOB_LIST_TABLE_GUID; | ||||
EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID; | EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID; | ||||
EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID; | EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID; | ||||
EFI_GUID fdtdtb = FDT_TABLE_GUID; | EFI_GUID fdtdtb = FDT_TABLE_GUID; | ||||
#ifdef EFI_ZFS_BOOT | |||||
static void efi_zfs_probe(void); | |||||
#endif | |||||
/* | |||||
* Need this because EFI uses UTF-16 unicode string constants, but we | |||||
* use UTF-8. We can't use printf due to the possiblity of \0 and we | |||||
* don't support support wide characters either. | |||||
*/ | |||||
static void | |||||
print_str16(const CHAR16 *str) | |||||
{ | |||||
int i; | |||||
for (i = 0; str[i]; i++) | |||||
printf("%c", (char)str[i]); | |||||
} | |||||
EFI_STATUS | EFI_STATUS | ||||
main(int argc, CHAR16 *argv[]) | main(int argc, CHAR16 *argv[]) | ||||
{ | { | ||||
char vendor[128]; | char vendor[128]; | ||||
EFI_LOADED_IMAGE *img; | EFI_LOADED_IMAGE *img; | ||||
EFI_GUID *guid; | EFI_GUID *guid; | ||||
int i; | int i, unit; | ||||
struct devsw *dev; | |||||
uint64_t pool_guid; | |||||
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; | |||||
#ifdef EFI_ZFS_BOOT | |||||
/* Note this needs to be set before ZFS init. */ | |||||
archsw.arch_zfs_probe = efi_zfs_probe; | |||||
#endif | |||||
/* | /* | ||||
* XXX Chicken-and-egg problem; we want to have console output | * XXX Chicken-and-egg problem; we want to have console output | ||||
* early, but some console attributes may depend on reading from | * early, but some console attributes may depend on reading from | ||||
* eg. the boot device, which we can't do yet. We can use | * eg. the boot device, which we can't do yet. We can use | ||||
* printf() etc. once this is done. | * printf() etc. once this is done. | ||||
*/ | */ | ||||
cons_probe(); | cons_probe(); | ||||
if (efi_copy_init()) { | if (efi_copy_init()) { | ||||
printf("failed to allocate staging area\n"); | printf("failed to allocate staging area\n"); | ||||
return (EFI_BUFFER_TOO_SMALL); | return (EFI_BUFFER_TOO_SMALL); | ||||
} | } | ||||
/* | /* | ||||
* March through the device switch probing for things. | * March through the device switch probing for things. | ||||
*/ | */ | ||||
for (i = 0; devsw[i] != NULL; i++) | for (i = 0; devsw[i] != NULL; i++) | ||||
if (devsw[i]->dv_init != NULL) | if (devsw[i]->dv_init != NULL) | ||||
(devsw[i]->dv_init)(); | (devsw[i]->dv_init)(); | ||||
/* Get our loaded image protocol interface structure. */ | /* Get our loaded image protocol interface structure. */ | ||||
BS->HandleProtocol(IH, &imgid, (VOID**)&img); | BS->HandleProtocol(IH, &imgid, (VOID**)&img); | ||||
printf("Command line arguments:"); | |||||
for (i = 0; i < argc; i++) { | |||||
printf(" "); | |||||
print_str16(argv[i]); | |||||
} | |||||
printf("\n"); | |||||
printf("Image base: 0x%lx\n", (u_long)img->ImageBase); | printf("Image base: 0x%lx\n", (u_long)img->ImageBase); | ||||
printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16, | printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16, | ||||
ST->Hdr.Revision & 0xffff); | ST->Hdr.Revision & 0xffff); | ||||
printf("EFI Firmware: "); | printf("EFI Firmware: "); | ||||
/* printf doesn't understand EFI Unicode */ | /* printf doesn't understand EFI Unicode */ | ||||
ST->ConOut->OutputString(ST->ConOut, ST->FirmwareVendor); | ST->ConOut->OutputString(ST->ConOut, ST->FirmwareVendor); | ||||
printf(" (rev %d.%02d)\n", ST->FirmwareRevision >> 16, | printf(" (rev %d.%02d)\n", ST->FirmwareRevision >> 16, | ||||
ST->FirmwareRevision & 0xffff); | ST->FirmwareRevision & 0xffff); | ||||
printf("\n"); | printf("\n"); | ||||
printf("%s, Revision %s\n", bootprog_name, bootprog_rev); | printf("%s, Revision %s\n", bootprog_name, bootprog_rev); | ||||
printf("(%s, %s)\n", bootprog_maker, bootprog_date); | printf("(%s, %s)\n", bootprog_maker, bootprog_date); | ||||
efi_handle_lookup(img->DeviceHandle, &currdev.d_dev, &currdev.d_unit); | |||||
currdev.d_type = currdev.d_dev->dv_type; | |||||
/* | /* | ||||
* Disable the watchdog timer. By default the boot manager sets | * Disable the watchdog timer. By default the boot manager sets | ||||
* the timer to 5 minutes before invoking a boot option. If we | * the timer to 5 minutes before invoking a boot option. If we | ||||
* want to return to the boot manager, we have to disable the | * want to return to the boot manager, we have to disable the | ||||
* watchdog timer and since we're an interactive program, we don't | * watchdog timer and since we're an interactive program, we don't | ||||
* want to wait until the user types "quit". The timer may have | * want to wait until the user types "quit". The timer may have | ||||
* fired by then. We don't care if this fails. It does not prevent | * fired by then. We don't care if this fails. It does not prevent | ||||
* normal functioning in any way... | * normal functioning in any way... | ||||
*/ | */ | ||||
BS->SetWatchdogTimer(0, 0, 0, NULL); | BS->SetWatchdogTimer(0, 0, 0, NULL); | ||||
if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &pool_guid) != 0) | |||||
return (EFI_NOT_FOUND); | |||||
switch (dev->dv_type) { | |||||
#ifdef EFI_ZFS_BOOT | |||||
case DEVT_ZFS: { | |||||
struct zfs_devdesc currdev; | |||||
currdev.d_dev = dev; | |||||
currdev.d_unit = unit; | |||||
currdev.d_type = currdev.d_dev->dv_type; | |||||
currdev.d_opendata = NULL; | |||||
currdev.pool_guid = pool_guid; | |||||
currdev.root_guid = 0; | |||||
env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), | env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), | ||||
efi_setcurrdev, env_nounset); | efi_setcurrdev, env_nounset); | ||||
env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, | env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, | ||||
env_nounset); | env_nounset); | ||||
break; | |||||
} | |||||
#endif | |||||
default: { | |||||
struct devdesc currdev; | |||||
currdev.d_dev = dev; | |||||
currdev.d_unit = unit; | |||||
currdev.d_opendata = NULL; | |||||
currdev.d_type = currdev.d_dev->dv_type; | |||||
env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), | |||||
efi_setcurrdev, env_nounset); | |||||
env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, | |||||
env_nounset); | |||||
break; | |||||
} | |||||
} | |||||
setenv("LINES", "24", 1); /* optional */ | setenv("LINES", "24", 1); /* optional */ | ||||
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; | |||||
for (i = 0; i < ST->NumberOfTableEntries; i++) { | for (i = 0; i < ST->NumberOfTableEntries; i++) { | ||||
guid = &ST->ConfigurationTable[i].VendorGuid; | guid = &ST->ConfigurationTable[i].VendorGuid; | ||||
if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { | if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { | ||||
smbios_detect(ST->ConfigurationTable[i].VendorTable); | smbios_detect(ST->ConfigurationTable[i].VendorTable); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | static char *types[] = { | ||||
"MemoryMappedIOPortSpace", | "MemoryMappedIOPortSpace", | ||||
"PalCode" | "PalCode" | ||||
}; | }; | ||||
sz = 0; | sz = 0; | ||||
status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver); | status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver); | ||||
if (status != EFI_BUFFER_TOO_SMALL) { | if (status != EFI_BUFFER_TOO_SMALL) { | ||||
printf("Can't determine memory map size\n"); | printf("Can't determine memory map size\n"); | ||||
return CMD_ERROR; | return (CMD_ERROR); | ||||
} | } | ||||
map = malloc(sz); | map = malloc(sz); | ||||
status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); | status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); | ||||
if (EFI_ERROR(status)) { | if (EFI_ERROR(status)) { | ||||
printf("Can't read memory map\n"); | printf("Can't read memory map\n"); | ||||
return CMD_ERROR; | return (CMD_ERROR); | ||||
} | } | ||||
ndesc = sz / dsz; | ndesc = sz / dsz; | ||||
printf("%23s %12s %12s %8s %4s\n", | printf("%23s %12s %12s %8s %4s\n", | ||||
"Type", "Physical", "Virtual", "#Pages", "Attr"); | "Type", "Physical", "Virtual", "#Pages", "Attr"); | ||||
for (i = 0, p = map; i < ndesc; | for (i = 0, p = map; i < ndesc; | ||||
i++, p = NextMemoryDescriptor(p, dsz)) { | i++, p = NextMemoryDescriptor(p, dsz)) { | ||||
printf("%23s %012lx %012lx %08lx ", | printf("%23s %012lx %012lx %08lx ", types[p->Type], | ||||
types[p->Type], | p->PhysicalStart, p->VirtualStart, p->NumberOfPages); | ||||
p->PhysicalStart, | |||||
p->VirtualStart, | |||||
p->NumberOfPages); | |||||
if (p->Attribute & EFI_MEMORY_UC) | if (p->Attribute & EFI_MEMORY_UC) | ||||
printf("UC "); | printf("UC "); | ||||
if (p->Attribute & EFI_MEMORY_WC) | if (p->Attribute & EFI_MEMORY_WC) | ||||
printf("WC "); | printf("WC "); | ||||
if (p->Attribute & EFI_MEMORY_WT) | if (p->Attribute & EFI_MEMORY_WT) | ||||
printf("WT "); | printf("WT "); | ||||
if (p->Attribute & EFI_MEMORY_WB) | if (p->Attribute & EFI_MEMORY_WB) | ||||
printf("WB "); | printf("WB "); | ||||
if (p->Attribute & EFI_MEMORY_UCE) | if (p->Attribute & EFI_MEMORY_UCE) | ||||
printf("UCE "); | printf("UCE "); | ||||
if (p->Attribute & EFI_MEMORY_WP) | if (p->Attribute & EFI_MEMORY_WP) | ||||
printf("WP "); | printf("WP "); | ||||
if (p->Attribute & EFI_MEMORY_RP) | if (p->Attribute & EFI_MEMORY_RP) | ||||
printf("RP "); | printf("RP "); | ||||
if (p->Attribute & EFI_MEMORY_XP) | if (p->Attribute & EFI_MEMORY_XP) | ||||
printf("XP "); | printf("XP "); | ||||
printf("\n"); | printf("\n"); | ||||
} | } | ||||
return CMD_OK; | return (CMD_OK); | ||||
} | } | ||||
COMMAND_SET(configuration, "configuration", | COMMAND_SET(configuration, "configuration", "print configuration tables", | ||||
"print configuration tables", command_configuration); | command_configuration); | ||||
static const char * | static const char * | ||||
guid_to_string(EFI_GUID *guid) | guid_to_string(EFI_GUID *guid) | ||||
{ | { | ||||
static char buf[40]; | static char buf[40]; | ||||
sprintf(buf, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", | sprintf(buf, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", | ||||
guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], | guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], | ||||
Show All 31 Lines | else if (!memcmp(guid, &debugimg, sizeof(EFI_GUID))) | ||||
printf("Debug Image Info Table"); | printf("Debug Image Info Table"); | ||||
else if (!memcmp(guid, &fdtdtb, sizeof(EFI_GUID))) | else if (!memcmp(guid, &fdtdtb, sizeof(EFI_GUID))) | ||||
printf("FDT Table"); | printf("FDT Table"); | ||||
else | else | ||||
printf("Unknown Table (%s)", guid_to_string(guid)); | printf("Unknown Table (%s)", guid_to_string(guid)); | ||||
printf(" at %p\n", ST->ConfigurationTable[i].VendorTable); | printf(" at %p\n", ST->ConfigurationTable[i].VendorTable); | ||||
} | } | ||||
return CMD_OK; | return (CMD_OK); | ||||
} | } | ||||
COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode); | COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode); | ||||
static int | static int | ||||
command_mode(int argc, char *argv[]) | command_mode(int argc, char *argv[]) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | command_nvram(int argc, char *argv[]) | ||||
int i; | int i; | ||||
conout = ST->ConOut; | conout = ST->ConOut; | ||||
/* Initiate the search */ | /* Initiate the search */ | ||||
status = RS->GetNextVariableName(&varsz, NULL, NULL); | status = RS->GetNextVariableName(&varsz, NULL, NULL); | ||||
for (; status != EFI_NOT_FOUND; ) { | for (; status != EFI_NOT_FOUND; ) { | ||||
status = RS->GetNextVariableName(&varsz, var, | status = RS->GetNextVariableName(&varsz, var, &varguid); | ||||
&varguid); | |||||
//if (EFI_ERROR(status)) | //if (EFI_ERROR(status)) | ||||
//break; | //break; | ||||
conout->OutputString(conout, var); | conout->OutputString(conout, var); | ||||
printf("="); | printf("="); | ||||
datasz = 0; | datasz = 0; | ||||
status = RS->GetVariable(var, &varguid, NULL, &datasz, | status = RS->GetVariable(var, &varguid, NULL, &datasz, NULL); | ||||
NULL); | |||||
/* XXX: check status */ | /* XXX: check status */ | ||||
data = malloc(datasz); | data = malloc(datasz); | ||||
status = RS->GetVariable(var, &varguid, NULL, &datasz, | status = RS->GetVariable(var, &varguid, NULL, &datasz, data); | ||||
data); | |||||
if (EFI_ERROR(status)) | if (EFI_ERROR(status)) | ||||
printf("<error retrieving variable>"); | printf("<error retrieving variable>"); | ||||
else { | else { | ||||
for (i = 0; i < datasz; i++) { | for (i = 0; i < datasz; i++) { | ||||
if (isalnum(data[i]) || isspace(data[i])) | if (isalnum(data[i]) || isspace(data[i])) | ||||
printf("%c", data[i]); | printf("%c", data[i]); | ||||
else | else | ||||
printf("\\x%02x", data[i]); | printf("\\x%02x", data[i]); | ||||
} | } | ||||
} | } | ||||
/* XXX */ | /* XXX */ | ||||
pager_output("\n"); | pager_output("\n"); | ||||
free(data); | free(data); | ||||
} | } | ||||
return (CMD_OK); | return (CMD_OK); | ||||
} | } | ||||
#ifdef EFI_ZFS_BOOT | |||||
COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset", | |||||
command_lszfs); | |||||
static int | |||||
command_lszfs(int argc, char *argv[]) | |||||
{ | |||||
int err; | |||||
if (argc != 2) { | |||||
command_errmsg = "wrong number of arguments"; | |||||
return (CMD_ERROR); | |||||
} | |||||
err = zfs_list(argv[1]); | |||||
if (err != 0) { | |||||
command_errmsg = strerror(err); | |||||
return (CMD_ERROR); | |||||
} | |||||
return (CMD_OK); | |||||
} | |||||
#endif | |||||
#ifdef LOADER_FDT_SUPPORT | #ifdef LOADER_FDT_SUPPORT | ||||
extern int command_fdt_internal(int argc, char *argv[]); | extern int command_fdt_internal(int argc, char *argv[]); | ||||
/* | /* | ||||
* Since proper fdt command handling function is defined in fdt_loader_cmd.c, | * Since proper fdt command handling function is defined in fdt_loader_cmd.c, | ||||
* and declaring it as extern is in contradiction with COMMAND_SET() macro | * and declaring it as extern is in contradiction with COMMAND_SET() macro | ||||
* (which uses static pointer), we're defining wrapper function, which | * (which uses static pointer), we're defining wrapper function, which | ||||
* calls the proper fdt handling routine. | * calls the proper fdt handling routine. | ||||
*/ | */ | ||||
static int | static int | ||||
command_fdt(int argc, char *argv[]) | command_fdt(int argc, char *argv[]) | ||||
{ | { | ||||
return (command_fdt_internal(argc, argv)); | return (command_fdt_internal(argc, argv)); | ||||
} | } | ||||
COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); | COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); | ||||
#endif | |||||
#ifdef EFI_ZFS_BOOT | |||||
static void | |||||
efi_zfs_probe(void) | |||||
{ | |||||
EFI_HANDLE h; | |||||
u_int unit; | |||||
int i; | |||||
char dname[SPECNAMELEN + 1]; | |||||
uint64_t guid; | |||||
unit = 0; | |||||
h = efi_find_handle(&efipart_dev, 0); | |||||
for (i = 0; h != NULL; h = efi_find_handle(&efipart_dev, ++i)) { | |||||
snprintf(dname, sizeof(dname), "%s%d:", efipart_dev.dv_name, i); | |||||
if (zfs_probe_dev(dname, &guid) == 0) | |||||
(void)efi_handle_update_dev(h, &zfs_dev, unit++, guid); | |||||
} | |||||
} | |||||
#endif | #endif |