/* $NetBSD: boot.c,v 1.18.4.2 2020/02/12 20:10:09 martin Exp $ */
/*-
* Copyright (c) 2016 Kimihiro Nonaka <nonaka@netbsd.org>
* Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "efiboot.h"
#include "efiblock.h"
#include "efifdt.h"
#include "efiacpi.h"
#include "efienv.h"
#include <sys/bootblock.h>
#include <sys/boot_flag.h>
#include <machine/limits.h>
#include <loadfile.h>
extern const char bootprog_name[], bootprog_rev[], bootprog_kernrev[];
extern char twiddle_toggle;
static const char * const names[] = {
"netbsd", "netbsd.gz",
"onetbsd", "onetbsd.gz",
"netbsd.old", "netbsd.old.gz",
};
#define NUMNAMES __arraycount(names)
static const char *efi_memory_type[] = {
[EfiReservedMemoryType] = "Reserved Memory Type",
[EfiLoaderCode] = "Loader Code",
[EfiLoaderData] = "Loader Data",
[EfiBootServicesCode] = "Boot Services Code",
[EfiBootServicesData] = "Boot Services Data",
[EfiRuntimeServicesCode] = "Runtime Services Code",
[EfiRuntimeServicesData] = "Runtime Services Data",
[EfiConventionalMemory] = "Conventional Memory",
[EfiUnusableMemory] = "Unusable Memory",
[EfiACPIReclaimMemory] = "ACPI Reclaim Memory",
[EfiACPIMemoryNVS] = "ACPI Memory NVS",
[EfiMemoryMappedIO] = "MMIO",
[EfiMemoryMappedIOPortSpace] = "MMIO (Port Space)",
[EfiPalCode] = "Pal Code",
[EfiPersistentMemory] = "Persistent Memory",
};
static char default_device[32];
static char initrd_path[255];
static char dtb_path[255];
static char efibootplist_path[255];
static char netbsd_path[255];
static char netbsd_args[255];
static char rndseed_path[255];
#define DEFTIMEOUT 5
#define DEFFILENAME names[0]
int set_bootfile(const char *);
int set_bootargs(const char *);
void command_boot(char *);
void command_dev(char *);
void command_dtb(char *);
void command_plist(char *);
void command_initrd(char *);
void command_rndseed(char *);
void command_ls(char *);
void command_mem(char *);
void command_printenv(char *);
void command_setenv(char *);
void command_clearenv(char *);
void command_resetenv(char *);
void command_reset(char *);
void command_version(char *);
void command_quit(char *);
const struct boot_command commands[] = {
{ "boot", command_boot, "boot [dev:][filename] [args]\n (ex. \"hd0a:\\netbsd.old -s\"" },
{ "dev", command_dev, "dev" },
{ "dtb", command_dtb, "dtb [dev:][filename]" },
{ "plist", command_plist, "plist [dev:][filename]" },
{ "initrd", command_initrd, "initrd [dev:][filename]" },
{ "rndseed", command_rndseed, "rndseed [dev:][filename]" },
{ "ls", command_ls, "ls [hdNn:/path]" },
{ "mem", command_mem, "mem" },
{ "printenv", command_printenv, "printenv [key]" },
{ "setenv", command_setenv, "setenv <key> <value>" },
{ "clearenv", command_clearenv, "clearenv <key>" },
{ "resetenv", command_resetenv, "resetenv" },
{ "reboot", command_reset, "reboot|reset" },
{ "reset", command_reset, NULL },
{ "version", command_version, "version" },
{ "ver", command_version, NULL },
{ "help", command_help, "help|?" },
{ "?", command_help, NULL },
{ "quit", command_quit, "quit" },
{ NULL, NULL },
};
void
command_help(char *arg)
{
int n;
printf("commands are:\n");
for (n = 0; commands[n].c_name; n++) {
if (commands[n].c_help)
printf("%s\n", commands[n].c_help);
}
}
void
command_boot(char *arg)
{
char *fname = arg;
const char *kernel = *fname ? fname : bootfile;
char *bootargs = gettrailer(arg);
if (!kernel || !*kernel)
kernel = DEFFILENAME;
if (!*bootargs)
bootargs = netbsd_args;
exec_netbsd(kernel, bootargs);
}
void
command_dev(char *arg)
{
if (arg && *arg) {
set_default_device(arg);
} else {
efi_block_show();
efi_net_show();
}
if (strlen(default_device) > 0) {
printf("\n");
printf("default: %s\n", default_device);
}
}
void
command_dtb(char *arg)
{
set_dtb_path(arg);
}
void
command_plist(char *arg)
{
if (set_efibootplist_path(arg) == 0)
load_efibootplist(false);
}
void
command_initrd(char *arg)
{
set_initrd_path(arg);
}
void
command_rndseed(char *arg)
{
set_rndseed_path(arg);
}
void
command_ls(char *arg)
{
ls(arg);
}
void
command_mem(char *arg)
{
EFI_MEMORY_DESCRIPTOR *md, *memmap;
UINTN nentries, mapkey, descsize;
UINT32 descver;
int n;
printf("Type Start End Attributes\n");
printf("---------------------- ---------------- ---------------- ----------------\n");
memmap = LibMemoryMap(&nentries, &mapkey, &descsize, &descver);
for (n = 0, md = memmap; n < nentries; n++, md = NextMemoryDescriptor(md, descsize)) {
const char *mem_type = "<unknown>";
if (md->Type < __arraycount(efi_memory_type))
mem_type = efi_memory_type[md->Type];
printf("%-22s %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
mem_type, md->PhysicalStart, md->PhysicalStart + (md->NumberOfPages * EFI_PAGE_SIZE) - 1,
md->Attribute);
}
}
void
command_printenv(char *arg)
{
char *val;
if (arg && *arg) {
val = efi_env_get(arg);
if (val) {
printf("\"%s\" = \"%s\"\n", arg, val);
FreePool(val);
}
} else {
efi_env_print();
}
}
void
command_setenv(char *arg)
{
char *spc;
spc = strchr(arg, ' ');
if (spc == NULL || spc[1] == '\0') {
command_help("");
return;
}
*spc = '\0';
efi_env_set(arg, spc + 1);
}
void
command_clearenv(char *arg)
{
if (*arg == '\0') {
command_help("");
return;
}
efi_env_clear(arg);
}
void
command_resetenv(char *arg)
{
efi_env_reset();
}
void
command_version(char *arg)
{
char *ufirmware;
int rv;
printf("Version: %s (%s)\n", bootprog_rev, bootprog_kernrev);
printf("EFI: %d.%02d\n",
ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
ufirmware = NULL;
rv = ucs2_to_utf8(ST->FirmwareVendor, &ufirmware);
if (rv == 0) {
printf("Firmware: %s (rev 0x%x)\n", ufirmware,
ST->FirmwareRevision);
FreePool(ufirmware);
}
efi_fdt_show();
efi_acpi_show();
}
void
command_quit(char *arg)
{
efi_exit();
}
void
command_reset(char *arg)
{
efi_reboot();
}
int
set_default_device(const char *arg)
{
if (strlen(arg) + 1 > sizeof(default_device))
return ERANGE;
strcpy(default_device, arg);
return 0;
}
char *
get_default_device(void)
{
return default_device;
}
int
set_initrd_path(const char *arg)
{
if (strlen(arg) + 1 > sizeof(initrd_path))
return ERANGE;
strcpy(initrd_path, arg);
return 0;
}
char *
get_initrd_path(void)
{
return initrd_path;
}
int
set_dtb_path(const char *arg)
{
if (strlen(arg) + 1 > sizeof(dtb_path))
return ERANGE;
strcpy(dtb_path, arg);
return 0;
}
char *
get_dtb_path(void)
{
return dtb_path;
}
int
set_efibootplist_path(const char *arg)
{
if (strlen(arg) + 1 > sizeof(efibootplist_path))
return ERANGE;
strcpy(efibootplist_path, arg);
return 0;
}
char *get_efibootplist_path(void)
{
return efibootplist_path;
}
int
set_rndseed_path(const char *arg)
{
if (strlen(arg) + 1 > sizeof(rndseed_path))
return ERANGE;
strcpy(rndseed_path, arg);
return 0;
}
char *
get_rndseed_path(void)
{
return rndseed_path;
}
int
set_bootfile(const char *arg)
{
if (strlen(arg) + 1 > sizeof(netbsd_path))
return ERANGE;
strcpy(netbsd_path, arg);
return 0;
}
int
set_bootargs(const char *arg)
{
if (strlen(arg) + 1 > sizeof(netbsd_args))
return ERANGE;
strcpy(netbsd_args, arg);
return 0;
}
void
print_banner(void)
{
printf("\n\n"
">> %s, Revision %s\n",
bootprog_name, bootprog_rev);
}
static void
read_env(void)
{
char *s;
s = efi_env_get("efibootplist");
if (s) {
#ifdef EFIBOOT_DEBUG
printf(">> Setting efiboot.plist path to '%s' from environment\n", s);
#endif
set_efibootplist_path(s);
FreePool(s);
}
/*
* Read the efiboot.plist now as it may contain additional
* environment variables.
*/
load_efibootplist(true);
s = efi_env_get("fdtfile");
if (s) {
#ifdef EFIBOOT_DEBUG
printf(">> Setting DTB path to '%s' from environment\n", s);
#endif
set_dtb_path(s);
FreePool(s);
}
s = efi_env_get("initrd");
if (s) {
#ifdef EFIBOOT_DEBUG
printf(">> Setting initrd path to '%s' from environment\n", s);
#endif
set_initrd_path(s);
FreePool(s);
}
s = efi_env_get("bootfile");
if (s) {
#ifdef EFIBOOT_DEBUG
printf(">> Setting bootfile path to '%s' from environment\n", s);
#endif
set_bootfile(s);
FreePool(s);
}
s = efi_env_get("rootdev");
if (s) {
#ifdef EFIBOOT_DEBUG
printf(">> Setting default device to '%s' from environment\n", s);
#endif
set_default_device(s);
FreePool(s);
}
s = efi_env_get("bootargs");
if (s) {
#ifdef EFIBOOT_DEBUG
printf(">> Setting default boot args to '%s' from environment\n", s);
#endif
set_bootargs(s);
FreePool(s);
}
s = efi_env_get("rndseed");
if (s) {
#ifdef EFIBOOT_DEBUG
printf(">> Setting rndseed path to '%s' from environment\n", s);
#endif
set_rndseed_path(s);
FreePool(s);
}
}
void
boot(void)
{
int currname, c;
read_env();
print_banner();
printf("Press return to boot now, any other key for boot prompt\n");
if (netbsd_path[0] != '\0')
currname = -1;
else
currname = 0;
for (; currname < (int)NUMNAMES; currname++) {
if (currname >= 0)
set_bootfile(names[currname]);
printf("booting %s%s%s - starting in ", netbsd_path,
netbsd_args[0] != '\0' ? " " : "", netbsd_args);
c = awaitkey(DEFTIMEOUT, 1);
if (c != '\r' && c != '\n' && c != '\0')
bootprompt(); /* does not return */
exec_netbsd(netbsd_path, netbsd_args);
}
bootprompt(); /* does not return */
}