Page MenuHomeFreeBSD

D20780 format-patch

Authored By
val_packett.cool
Jan 28 2022, 11:59 AM
Size
99 KB
Referenced Files
None
Subscribers
None

D20780 format-patch

From eb92ab48517ba4c6a793e8c9cecf6d6c027cd664 Mon Sep 17 00:00:00 2001
From: Greg V <greg@unrelenting.technology>
Date: Thu, 27 Jun 2019 17:39:22 +0300
Subject: [PATCH] loader: Add support for getting early entropy from the UEFI
RNG protocol
This is most useful on architectures like aarch64 that don't yet have
a HW RNG instruction that's as ubiquitous as AES-NI is on amd64.
Differential Revision: https://reviews.freebsd.org/D20780
---
stand/defaults/loader.conf | 4 ++-
stand/efi/include/efirng.h | 50 ++++++++++++++++++++++++++++++++
stand/efi/loader/main.c | 42 +++++++++++++++++++++++++++
stand/lua/core.lua | 10 +++++++
sys/dev/random/random_harvestq.c | 8 +++++
sys/sys/random.h | 1 +
6 files changed, 114 insertions(+), 1 deletion(-)
create mode 100644 stand/efi/include/efirng.h
diff --git a/stand/defaults/loader.conf b/stand/defaults/loader.conf
index 6feb909d708..24a47e75c39 100644
--- a/stand/defaults/loader.conf
+++ b/stand/defaults/loader.conf
@@ -1,177 +1,179 @@
# This is loader.conf - a file full of useful variables that you can
# set to change the default load behavior of your system. You should
# not edit this file! Put any overrides into one of the
# loader_conf_files instead and you will be able to update these
# defaults later without spamming your local configuration information.
#
# All arguments must be in double quotes.
#
# $FreeBSD$
### Basic configuration options ############################
exec="echo Loading /boot/defaults/loader.conf"
kernel="kernel" # /boot sub-directory containing kernel and modules
bootfile="kernel" # Kernel name (possibly absolute path)
kernel_options="" # Flags to be passed to the kernel
loader_conf_files="/boot/device.hints /boot/loader.conf /boot/loader.conf.local"
loader_conf_dirs="/boot/loader.conf.d"
nextboot_conf="/boot/nextboot.conf"
verbose_loading="NO" # Set to YES for verbose loader output
### Splash screen configuration ############################
splash_bmp_load="NO" # Set this to YES for bmp splash screen!
splash_pcx_load="NO" # Set this to YES for pcx splash screen!
splash_txt_load="NO" # Set this to YES for TheDraw splash screen!
vesa_load="NO" # Set this to YES to load the vesa module
bitmap_load="NO" # Set this to YES if you want splash screen!
bitmap_name="splash.bmp" # Set this to the name of the file
bitmap_type="splash_image_data" # and place it on the module_path
### Screen saver modules ###################################
# This is best done in rc.conf
screensave_load="NO" # Set to YES to load a screensaver module
screensave_name="green_saver" # Set to the name of the screensaver module
### Early hostid configuration ############################
hostuuid_load="YES"
hostuuid_name="/etc/hostid"
hostuuid_type="hostuuid"
### Random number generator configuration ##################
# See rc.conf(5). The entropy_boot_file config variable must agree with the
# settings below.
entropy_cache_load="YES" # Set this to NO to disable loading
- # entropy at boot time
+ # cached entropy at boot time
entropy_cache_name="/boot/entropy" # Set this to the name of the file
entropy_cache_type="boot_entropy_cache" # Required for the kernel to find
# the boot-time entropy cache. This
# must not change value even if the
# _name above does change!
+entropy_efi_seed="YES" # Set this to NO to disable loading
+ # entropy from the UEFI hardware random number generator API
### RAM Blacklist configuration ############################
ram_blacklist_load="NO" # Set this to YES to load a file
# containing a list of addresses to
# exclude from the running system.
ram_blacklist_name="/boot/blacklist.txt" # Set this to the name of the file
ram_blacklist_type="ram_blacklist" # Required for the kernel to find
# the blacklist module
### Microcode loading configuration ########################
cpu_microcode_load="NO" # Set this to YES to load and apply a
# microcode update file during boot.
cpu_microcode_name="/boot/firmware/ucode.bin" # Set this to the microcode
# update file path.
cpu_microcode_type="cpu_microcode" # Required for the kernel to find
# the microcode update file.
### ACPI settings ##########################################
acpi_dsdt_load="NO" # DSDT Overriding
acpi_dsdt_type="acpi_dsdt" # Don't change this
acpi_dsdt_name="/boot/acpi_dsdt.aml"
# Override DSDT in BIOS by this file
acpi_video_load="NO" # Load the ACPI video extension driver
### Audit settings #########################################
audit_event_load="NO" # Preload audit_event config
audit_event_name="/etc/security/audit_event"
audit_event_type="etc_security_audit_event"
### Initial memory disk settings ###########################
#mdroot_load="YES" # The "mdroot" prefix is arbitrary.
#mdroot_type="md_image" # Create md(4) disk at boot.
#mdroot_name="/boot/root.img" # Path to a file containing the image.
#rootdev="ufs:/dev/md0" # Set the root filesystem to md(4) device.
### Loader settings ########################################
#loader_delay="3" # Delay in seconds before loading anything.
# Default is unset and disabled (no delay).
#autoboot_delay="10" # Delay in seconds before autobooting,
# -1 for no user interrupts, NO to disable
#password="" # Prevent changes to boot options
#bootlock_password="" # Prevent booting (see check-password.4th(8))
#geom_eli_passphrase_prompt="NO" # Prompt for geli(8) passphrase to mount root
bootenv_autolist="YES" # Auto populate the list of ZFS Boot Environments
#beastie_disable="NO" # Turn the beastie boot menu on and off
efi_max_resolution="1x1" # Set the max resolution for EFI loader to use:
# 480p, 720p, 1080p, 2160p/4k, 5k, or specify
# WidthxHeight (e.g. 1920x1080)
#kernels="kernel kernel.old" # Kernels to display in the boot menu
kernels_autodetect="YES" # Auto-detect kernel directories in /boot
#loader_logo="orbbw" # Desired logo: orbbw, orb, fbsdbw, beastiebw, beastie, none
#comconsole_speed="9600" # Set the current serial console speed
#console="vidconsole" # A comma separated list of console(s)
#currdev="disk1s1a" # Set the current device
module_path="/boot/modules;/boot/dtb;/boot/dtb/overlays" # Set the module search path
module_blacklist="drm drm2 radeonkms i915kms amdgpu" # Loader module blacklist
#prompt="\\${interpret}" # Set the command prompt
#root_disk_unit="0" # Force the root disk unit number
#rootdev="disk1s1a" # Set the root filesystem
#dumpdev="disk1s1b" # Set a dump device early in the boot process
#tftp.blksize="1428" # Set the RFC 2348 TFTP block size.
# If the TFTP server does not support RFC 2348,
# the block size is set to 512. Valid: (8,9007)
#twiddle_divisor="16" # >16 slows down the progress indicator;
# <16 speeds up the progress indicator.
### Kernel settings ########################################
# The following boot_ variables are enabled by setting them to any value.
# Their presence in the kernel environment (see kenv(1)) has the same
# effect as setting the given boot flag (see boot(8)).
#boot_askname="" # -a: Prompt the user for the name of the root device
#boot_cdrom="" # -C: Attempt to mount root file system from CD-ROM
#boot_ddb="" # -d: Instructs the kernel to start in the DDB debugger
#boot_dfltroot="" # -r: Use the statically configured root file system
#boot_gdb="" # -g: Selects gdb-remote mode for the kernel debugger
#boot_multicons="" # -D: Use multiple consoles
#boot_mute="" # -m: Mute the console
#boot_pause="" # -p: Pause after each line during device probing
#boot_serial="" # -h: Use serial console
#boot_single="" # -s: Start system in single-user mode
#boot_verbose="" # -v: Causes extra debugging information to be printed
#init_path="/sbin/init:/sbin/oinit:/sbin/init.bak:/rescue/init"
# Sets the list of init candidates
#init_shell="/bin/sh" # The shell binary used by init(8).
#init_script="" # Initial script to run by init(8) before chrooting.
#init_chroot="" # Directory for init(8) to chroot into.
### Kernel tunables ########################################
#hw.physmem="1G" # Limit physical memory. See loader(8)
#kern.dfldsiz="" # Set the initial data size limit
#kern.dflssiz="" # Set the initial stack size limit
#kern.hz="100" # Set the kernel interval timer rate
#kern.maxbcache="" # Set the max buffer cache KVA storage
#kern.maxdsiz="" # Set the max data size
#kern.maxfiles="" # Set the sys. wide open files limit
#kern.maxproc="" # Set the maximum # of processes
#kern.maxssiz="" # Set the max stack size
#kern.maxswzone="" # Set the max swmeta KVA storage
#kern.maxtsiz="" # Set the max text size
#kern.maxusers="32" # Set size of various static tables
#kern.msgbufsize="65536" # Set size of kernel message buffer
#kern.nbuf="" # Set the number of buffer headers
#kern.ncallout="" # Set the maximum # of timer events
#kern.ngroups="1023" # Set the maximum # of supplemental groups
#kern.sgrowsiz="" # Set the amount to grow stack
#kern.cam.boot_delay="10000" # Delay (in ms) of root mount for CAM bus
# registration, useful for USB sticks as root
#kern.cam.scsi_delay="2000" # Delay (in ms) before probing SCSI
#kern.ipc.maxsockets="" # Set the maximum number of sockets available
#kern.ipc.nmbclusters="" # Set the number of mbuf clusters
#kern.ipc.nsfbufs="" # Set the number of sendfile(2) bufs
#net.inet.tcp.tcbhashsize="" # Set the value of TCBHASHSIZE
#vfs.root.mountfrom="" # Specify root partition
#vm.kmem_size="" # Sets the size of kernel memory (bytes)
#debug.kdb.break_to_debugger="0" # Allow console to break into debugger.
#debug.ktr.cpumask="0xf" # Bitmask of CPUs to enable KTR on
#debug.ktr.mask="0x1200" # Bitmask of KTR events to enable
#debug.ktr.verbose="1" # Enable console dump of KTR events
### Module loading syntax example ##########################
#module_load="YES" # loads module "module"
#module_name="realname" # uses "realname" instead of "module"
#module_type="type" # passes "-t type" to load
#module_flags="flags" # passes "flags" to the module
#module_before="cmd" # executes "cmd" before loading the module
#module_after="cmd" # executes "cmd" after loading the module
#module_error="cmd" # executes "cmd" if load fails
diff --git a/stand/efi/include/efirng.h b/stand/efi/include/efirng.h
new file mode 100644
index 00000000000..3d6c7767798
--- /dev/null
+++ b/stand/efi/include/efirng.h
@@ -0,0 +1,50 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2006 - 2016 Unified EFI, Inc.<BR>
+ * Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>
+ * This program and the accompanying materials
+ * are licensed and made available under the terms and conditions of the BSD License
+ * which accompanies this distribution. The full text of the license may be found at
+ * http://opensource.org/licenses/bsd-license.php
+ *
+ * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+ *
+ */
+
+#ifndef _EFIRNG_H
+#define _EFIRNG_H
+
+#define EFI_RNG_PROTOCOL_GUID \
+ { 0x3152bca5, 0xeade, 0x433d, {0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44} }
+
+INTERFACE_DECL(_EFI_RNG_PROTOCOL);
+
+typedef EFI_GUID EFI_RNG_ALGORITHM;
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_RNG_GET_INFO) (
+ IN struct _EFI_RNG_PROTOCOL *This,
+ IN OUT UINTN *RNGAlgorithmListSize,
+ OUT EFI_RNG_ALGORITHM *RNGAlgorithmList
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_RNG_GET_RNG) (
+ IN struct _EFI_RNG_PROTOCOL *This,
+ IN EFI_RNG_ALGORITHM *RNGAlgorithm, OPTIONAL
+ IN UINTN RNGValueLength,
+ OUT UINT8 *RNGValue
+ );
+
+typedef struct _EFI_RNG_PROTOCOL {
+ EFI_RNG_GET_INFO GetInfo;
+ EFI_RNG_GET_RNG GetRNG;
+} EFI_RNG_PROTOCOL;
+
+static EFI_GUID rng_guid = EFI_RNG_PROTOCOL_GUID;
+
+#endif /* _EFIRNG_H */
diff --git a/stand/efi/loader/main.c b/stand/efi/loader/main.c
index 7b78f94eb36..eb143989190 100644
--- a/stand/efi/loader/main.c
+++ b/stand/efi/loader/main.c
@@ -1,1682 +1,1724 @@
/*-
* Copyright (c) 2008-2010 Rui Paulo
* Copyright (c) 2006 Marcel Moolenaar
* All rights reserved.
*
* Copyright (c) 2016-2019 Netflix, Inc. written by M. Warner Losh
*
* 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 AUTHOR ``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 AUTHOR 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <stand.h>
#include <sys/disk.h>
#include <sys/param.h>
#include <sys/reboot.h>
#include <sys/boot.h>
#ifdef EFI_ZFS_BOOT
#include <sys/zfs_bootenv.h>
#endif
#include <paths.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <stdint.h>
#include <string.h>
#include <setjmp.h>
#include <disk.h>
#include <dev_net.h>
#include <net.h>
#include <efi.h>
#include <efilib.h>
#include <efichar.h>
+#include <efirng.h>
#include <uuid.h>
#include <bootstrap.h>
#include <smbios.h>
#include "efizfs.h"
#include "loader_efi.h"
struct arch_switch archsw; /* MI/MD interface boundary */
EFI_GUID acpi = ACPI_TABLE_GUID;
EFI_GUID acpi20 = ACPI_20_TABLE_GUID;
EFI_GUID devid = DEVICE_PATH_PROTOCOL;
EFI_GUID imgid = LOADED_IMAGE_PROTOCOL;
EFI_GUID mps = MPS_TABLE_GUID;
EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL;
EFI_GUID smbios = SMBIOS_TABLE_GUID;
EFI_GUID smbios3 = SMBIOS3_TABLE_GUID;
EFI_GUID dxe = DXE_SERVICES_TABLE_GUID;
EFI_GUID hoblist = HOB_LIST_TABLE_GUID;
EFI_GUID lzmadecomp = LZMA_DECOMPRESSION_GUID;
EFI_GUID mpcore = ARM_MP_CORE_INFO_TABLE_GUID;
EFI_GUID esrt = ESRT_TABLE_GUID;
EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID;
EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID;
EFI_GUID fdtdtb = FDT_TABLE_GUID;
EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL;
/*
* Number of seconds to wait for a keystroke before exiting with failure
* in the event no currdev is found. -2 means always break, -1 means
* never break, 0 means poll once and then reboot, > 0 means wait for
* that many seconds. "fail_timeout" can be set in the environment as
* well.
*/
static int fail_timeout = 5;
/*
* Current boot variable
*/
UINT16 boot_current;
/*
* Image that we booted from.
*/
EFI_LOADED_IMAGE *boot_img;
static bool
has_keyboard(void)
{
EFI_STATUS status;
EFI_DEVICE_PATH *path;
EFI_HANDLE *hin, *hin_end, *walker;
UINTN sz;
bool retval = false;
/*
* Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and
* do the typical dance to get the right sized buffer.
*/
sz = 0;
hin = NULL;
status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0);
if (status == EFI_BUFFER_TOO_SMALL) {
hin = (EFI_HANDLE *)malloc(sz);
status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz,
hin);
if (EFI_ERROR(status))
free(hin);
}
if (EFI_ERROR(status))
return retval;
/*
* Look at each of the handles. If it supports the device path protocol,
* use it to get the device path for this handle. Then see if that
* device path matches either the USB device path for keyboards or the
* legacy device path for keyboards.
*/
hin_end = &hin[sz / sizeof(*hin)];
for (walker = hin; walker < hin_end; walker++) {
status = OpenProtocolByHandle(*walker, &devid, (void **)&path);
if (EFI_ERROR(status))
continue;
while (!IsDevicePathEnd(path)) {
/*
* Check for the ACPI keyboard node. All PNP3xx nodes
* are keyboards of different flavors. Note: It is
* unclear of there's always a keyboard node when
* there's a keyboard controller, or if there's only one
* when a keyboard is detected at boot.
*/
if (DevicePathType(path) == ACPI_DEVICE_PATH &&
(DevicePathSubType(path) == ACPI_DP ||
DevicePathSubType(path) == ACPI_EXTENDED_DP)) {
ACPI_HID_DEVICE_PATH *acpi;
acpi = (ACPI_HID_DEVICE_PATH *)(void *)path;
if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 &&
(acpi->HID & 0xffff) == PNP_EISA_ID_CONST) {
retval = true;
goto out;
}
/*
* Check for USB keyboard node, if present. Unlike a
* PS/2 keyboard, these definitely only appear when
* connected to the system.
*/
} else if (DevicePathType(path) == MESSAGING_DEVICE_PATH &&
DevicePathSubType(path) == MSG_USB_CLASS_DP) {
USB_CLASS_DEVICE_PATH *usb;
usb = (USB_CLASS_DEVICE_PATH *)(void *)path;
if (usb->DeviceClass == 3 && /* HID */
usb->DeviceSubClass == 1 && /* Boot devices */
usb->DeviceProtocol == 1) { /* Boot keyboards */
retval = true;
goto out;
}
}
path = NextDevicePathNode(path);
}
}
out:
free(hin);
return retval;
}
static void
set_currdev(const char *devname)
{
env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev,
env_nounset);
/*
* Don't execute hook here; the loaddev hook makes it immutable
* once we've determined what the proper currdev is.
*/
env_setenv("loaddev", EV_VOLATILE | EV_NOHOOK, devname, env_noset,
env_nounset);
}
static void
set_currdev_devdesc(struct devdesc *currdev)
{
const char *devname;
devname = efi_fmtdev(currdev);
printf("Setting currdev to %s\n", devname);
set_currdev(devname);
}
static void
set_currdev_devsw(struct devsw *dev, int unit)
{
struct devdesc currdev;
currdev.d_dev = dev;
currdev.d_unit = unit;
set_currdev_devdesc(&currdev);
}
static void
set_currdev_pdinfo(pdinfo_t *dp)
{
/*
* Disks are special: they have partitions. if the parent
* pointer is non-null, we're a partition not a full disk
* and we need to adjust currdev appropriately.
*/
if (dp->pd_devsw->dv_type == DEVT_DISK) {
struct disk_devdesc currdev;
currdev.dd.d_dev = dp->pd_devsw;
if (dp->pd_parent == NULL) {
currdev.dd.d_unit = dp->pd_unit;
currdev.d_slice = D_SLICENONE;
currdev.d_partition = D_PARTNONE;
} else {
currdev.dd.d_unit = dp->pd_parent->pd_unit;
currdev.d_slice = dp->pd_unit;
currdev.d_partition = D_PARTISGPT; /* XXX Assumes GPT */
}
set_currdev_devdesc((struct devdesc *)&currdev);
} else {
set_currdev_devsw(dp->pd_devsw, dp->pd_unit);
}
}
static bool
sanity_check_currdev(void)
{
struct stat st;
return (stat(PATH_DEFAULTS_LOADER_CONF, &st) == 0 ||
#ifdef PATH_BOOTABLE_TOKEN
stat(PATH_BOOTABLE_TOKEN, &st) == 0 || /* non-standard layout */
#endif
stat(PATH_KERNEL, &st) == 0);
}
#ifdef EFI_ZFS_BOOT
static bool
probe_zfs_currdev(uint64_t guid)
{
char *devname;
struct zfs_devdesc currdev;
char *buf = NULL;
bool rv;
currdev.dd.d_dev = &zfs_dev;
currdev.dd.d_unit = 0;
currdev.pool_guid = guid;
currdev.root_guid = 0;
set_currdev_devdesc((struct devdesc *)&currdev);
devname = efi_fmtdev(&currdev);
init_zfs_boot_options(devname);
rv = sanity_check_currdev();
if (rv) {
buf = malloc(VDEV_PAD_SIZE);
if (buf != NULL) {
if (zfs_get_bootonce(&currdev, OS_BOOTONCE, buf,
VDEV_PAD_SIZE) == 0) {
printf("zfs bootonce: %s\n", buf);
set_currdev(buf);
setenv("zfs-bootonce", buf, 1);
}
free(buf);
(void) zfs_attach_nvstore(&currdev);
}
}
return (rv);
}
#endif
#ifdef MD_IMAGE_SIZE
static bool
probe_md_currdev(void)
{
extern struct devsw md_dev;
bool rv;
set_currdev_devsw(&md_dev, 0);
rv = sanity_check_currdev();
if (!rv)
printf("MD not present\n");
return (rv);
}
#endif
static bool
try_as_currdev(pdinfo_t *hd, pdinfo_t *pp)
{
uint64_t guid;
#ifdef EFI_ZFS_BOOT
/*
* If there's a zpool on this device, try it as a ZFS
* filesystem, which has somewhat different setup than all
* other types of fs due to imperfect loader integration.
* This all stems from ZFS being both a device (zpool) and
* a filesystem, plus the boot env feature.
*/
if (efizfs_get_guid_by_handle(pp->pd_handle, &guid))
return (probe_zfs_currdev(guid));
#endif
/*
* All other filesystems just need the pdinfo
* initialized in the standard way.
*/
set_currdev_pdinfo(pp);
return (sanity_check_currdev());
}
/*
* Sometimes we get filenames that are all upper case
* and/or have backslashes in them. Filter all this out
* if it looks like we need to do so.
*/
static void
fix_dosisms(char *p)
{
while (*p) {
if (isupper(*p))
*p = tolower(*p);
else if (*p == '\\')
*p = '/';
p++;
}
}
#define SIZE(dp, edp) (size_t)((intptr_t)(void *)edp - (intptr_t)(void *)dp)
enum { BOOT_INFO_OK = 0, BAD_CHOICE = 1, NOT_SPECIFIC = 2 };
static int
match_boot_info(char *boot_info, size_t bisz)
{
uint32_t attr;
uint16_t fplen;
size_t len;
char *walker, *ep;
EFI_DEVICE_PATH *dp, *edp, *first_dp, *last_dp;
pdinfo_t *pp;
CHAR16 *descr;
char *kernel = NULL;
FILEPATH_DEVICE_PATH *fp;
struct stat st;
CHAR16 *text;
/*
* FreeBSD encodes its boot loading path into the boot loader
* BootXXXX variable. We look for the last one in the path
* and use that to load the kernel. However, if we only find
* one DEVICE_PATH, then there's nothing specific and we should
* fall back.
*
* In an ideal world, we'd look at the image handle we were
* passed, match up with the loader we are and then return the
* next one in the path. This would be most flexible and cover
* many chain booting scenarios where you need to use this
* boot loader to get to the next boot loader. However, that
* doesn't work. We rarely have the path to the image booted
* (just the device) so we can't count on that. So, we do the
* next best thing: we look through the device path(s) passed
* in the BootXXXX variable. If there's only one, we return
* NOT_SPECIFIC. Otherwise, we look at the last one and try to
* load that. If we can, we return BOOT_INFO_OK. Otherwise we
* return BAD_CHOICE for the caller to sort out.
*/
if (bisz < sizeof(attr) + sizeof(fplen) + sizeof(CHAR16))
return NOT_SPECIFIC;
walker = boot_info;
ep = walker + bisz;
memcpy(&attr, walker, sizeof(attr));
walker += sizeof(attr);
memcpy(&fplen, walker, sizeof(fplen));
walker += sizeof(fplen);
descr = (CHAR16 *)(intptr_t)walker;
len = ucs2len(descr);
walker += (len + 1) * sizeof(CHAR16);
last_dp = first_dp = dp = (EFI_DEVICE_PATH *)walker;
edp = (EFI_DEVICE_PATH *)(walker + fplen);
if ((char *)edp > ep)
return NOT_SPECIFIC;
while (dp < edp && SIZE(dp, edp) > sizeof(EFI_DEVICE_PATH)) {
text = efi_devpath_name(dp);
if (text != NULL) {
printf(" BootInfo Path: %S\n", text);
efi_free_devpath_name(text);
}
last_dp = dp;
dp = (EFI_DEVICE_PATH *)((char *)dp + efi_devpath_length(dp));
}
/*
* If there's only one item in the list, then nothing was
* specified. Or if the last path doesn't have a media
* path in it. Those show up as various VenHw() nodes
* which are basically opaque to us. Don't count those
* as something specifc.
*/
if (last_dp == first_dp) {
printf("Ignoring Boot%04x: Only one DP found\n", boot_current);
return NOT_SPECIFIC;
}
if (efi_devpath_to_media_path(last_dp) == NULL) {
printf("Ignoring Boot%04x: No Media Path\n", boot_current);
return NOT_SPECIFIC;
}
/*
* OK. At this point we either have a good path or a bad one.
* Let's check.
*/
pp = efiblk_get_pdinfo_by_device_path(last_dp);
if (pp == NULL) {
printf("Ignoring Boot%04x: Device Path not found\n", boot_current);
return BAD_CHOICE;
}
set_currdev_pdinfo(pp);
if (!sanity_check_currdev()) {
printf("Ignoring Boot%04x: sanity check failed\n", boot_current);
return BAD_CHOICE;
}
/*
* OK. We've found a device that matches, next we need to check the last
* component of the path. If it's a file, then we set the default kernel
* to that. Otherwise, just use this as the default root.
*
* Reminder: we're running very early, before we've parsed the defaults
* file, so we may need to have a hack override.
*/
dp = efi_devpath_last_node(last_dp);
if (DevicePathType(dp) != MEDIA_DEVICE_PATH ||
DevicePathSubType(dp) != MEDIA_FILEPATH_DP) {
printf("Using Boot%04x for root partition\n", boot_current);
return (BOOT_INFO_OK); /* use currdir, default kernel */
}
fp = (FILEPATH_DEVICE_PATH *)dp;
ucs2_to_utf8(fp->PathName, &kernel);
if (kernel == NULL) {
printf("Not using Boot%04x: can't decode kernel\n", boot_current);
return (BAD_CHOICE);
}
if (*kernel == '\\' || isupper(*kernel))
fix_dosisms(kernel);
if (stat(kernel, &st) != 0) {
free(kernel);
printf("Not using Boot%04x: can't find %s\n", boot_current,
kernel);
return (BAD_CHOICE);
}
setenv("kernel", kernel, 1);
free(kernel);
text = efi_devpath_name(last_dp);
if (text) {
printf("Using Boot%04x %S + %s\n", boot_current, text,
kernel);
efi_free_devpath_name(text);
}
return (BOOT_INFO_OK);
}
/*
* Look at the passed-in boot_info, if any. If we find it then we need
* to see if we can find ourselves in the boot chain. If we can, and
* there's another specified thing to boot next, assume that the file
* is loaded from / and use that for the root filesystem. If can't
* find the specified thing, we must fail the boot. If we're last on
* the list, then we fallback to looking for the first available /
* candidate (ZFS, if there's a bootable zpool, otherwise a UFS
* partition that has either /boot/defaults/loader.conf on it or
* /boot/kernel/kernel (the default kernel) that we can use.
*
* We always fail if we can't find the right thing. However, as
* a concession to buggy UEFI implementations, like u-boot, if
* we have determined that the host is violating the UEFI boot
* manager protocol, we'll signal the rest of the program that
* a drop to the OK boot loader prompt is possible.
*/
static int
find_currdev(bool do_bootmgr, bool is_last,
char *boot_info, size_t boot_info_sz)
{
pdinfo_t *dp, *pp;
EFI_DEVICE_PATH *devpath, *copy;
EFI_HANDLE h;
CHAR16 *text;
struct devsw *dev;
int unit;
uint64_t extra;
int rv;
char *rootdev;
/*
* First choice: if rootdev is already set, use that, even if
* it's wrong.
*/
rootdev = getenv("rootdev");
if (rootdev != NULL) {
printf(" Setting currdev to configured rootdev %s\n",
rootdev);
set_currdev(rootdev);
return (0);
}
/*
* Second choice: If uefi_rootdev is set, translate that UEFI device
* path to the loader's internal name and use that.
*/
do {
rootdev = getenv("uefi_rootdev");
if (rootdev == NULL)
break;
devpath = efi_name_to_devpath(rootdev);
if (devpath == NULL)
break;
dp = efiblk_get_pdinfo_by_device_path(devpath);
efi_devpath_free(devpath);
if (dp == NULL)
break;
printf(" Setting currdev to UEFI path %s\n",
rootdev);
set_currdev_pdinfo(dp);
return (0);
} while (0);
/*
* Third choice: If we can find out image boot_info, and there's
* a follow-on boot image in that boot_info, use that. In this
* case root will be the partition specified in that image and
* we'll load the kernel specified by the file path. Should there
* not be a filepath, we use the default. This filepath overrides
* loader.conf.
*/
if (do_bootmgr) {
rv = match_boot_info(boot_info, boot_info_sz);
switch (rv) {
case BOOT_INFO_OK: /* We found it */
return (0);
case BAD_CHOICE: /* specified file not found -> error */
/* XXX do we want to have an escape hatch for last in boot order? */
return (ENOENT);
} /* Nothing specified, try normal match */
}
#ifdef EFI_ZFS_BOOT
/*
* Did efi_zfs_probe() detect the boot pool? If so, use the zpool
* it found, if it's sane. ZFS is the only thing that looks for
* disks and pools to boot. This may change in the future, however,
* if we allow specifying which pool to boot from via UEFI variables
* rather than the bootenv stuff that FreeBSD uses today.
*/
if (pool_guid != 0) {
printf("Trying ZFS pool\n");
if (probe_zfs_currdev(pool_guid))
return (0);
}
#endif /* EFI_ZFS_BOOT */
#ifdef MD_IMAGE_SIZE
/*
* If there is an embedded MD, try to use that.
*/
printf("Trying MD\n");
if (probe_md_currdev())
return (0);
#endif /* MD_IMAGE_SIZE */
/*
* Try to find the block device by its handle based on the
* image we're booting. If we can't find a sane partition,
* search all the other partitions of the disk. We do not
* search other disks because it's a violation of the UEFI
* boot protocol to do so. We fail and let UEFI go on to
* the next candidate.
*/
dp = efiblk_get_pdinfo_by_handle(boot_img->DeviceHandle);
if (dp != NULL) {
text = efi_devpath_name(dp->pd_devpath);
if (text != NULL) {
printf("Trying ESP: %S\n", text);
efi_free_devpath_name(text);
}
set_currdev_pdinfo(dp);
if (sanity_check_currdev())
return (0);
if (dp->pd_parent != NULL) {
pdinfo_t *espdp = dp;
dp = dp->pd_parent;
STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
/* Already tried the ESP */
if (espdp == pp)
continue;
/*
* Roll up the ZFS special case
* for those partitions that have
* zpools on them.
*/
text = efi_devpath_name(pp->pd_devpath);
if (text != NULL) {
printf("Trying: %S\n", text);
efi_free_devpath_name(text);
}
if (try_as_currdev(dp, pp))
return (0);
}
}
}
/*
* Try the device handle from our loaded image first. If that
* 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. Currently, this handle list is only for netboot.
*/
if (efi_handle_lookup(boot_img->DeviceHandle, &dev, &unit, &extra) == 0) {
set_currdev_devsw(dev, unit);
if (sanity_check_currdev())
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) {
set_currdev_devsw(dev, unit);
if (sanity_check_currdev())
return (0);
}
devpath = efi_lookup_devpath(h);
if (devpath != NULL) {
copy = efi_devpath_trim(devpath);
devpath = copy;
}
}
free(copy);
return (ENOENT);
}
static bool
interactive_interrupt(const char *msg)
{
time_t now, then, last;
last = 0;
now = then = getsecs();
printf("%s\n", msg);
if (fail_timeout == -2) /* Always break to OK */
return (true);
if (fail_timeout == -1) /* Never break to OK */
return (false);
do {
if (last != now) {
printf("press any key to interrupt reboot in %d seconds\r",
fail_timeout - (int)(now - then));
last = now;
}
/* XXX no pause or timeout wait for char */
if (ischar())
return (true);
now = getsecs();
} while (now - then < fail_timeout);
return (false);
}
static int
parse_args(int argc, CHAR16 *argv[])
{
int i, j, howto;
bool vargood;
char var[128];
/*
* Parse the args to set the console settings, etc
* boot1.efi passes these in, if it can read /boot.config or /boot/config
* or iPXE may be setup to pass these in. Or the optional argument in the
* boot environment was used to pass these arguments in (in which case
* neither /boot.config nor /boot/config are consulted).
*
* Loop through the args, and for each one that contains an '=' that is
* not the first character, add it to the environment. This allows
* loader and kernel env vars to be passed on the command line. Convert
* args from UCS-2 to ASCII (16 to 8 bit) as they are copied (though this
* method is flawed for non-ASCII characters).
*/
howto = 0;
for (i = 1; i < argc; i++) {
cpy16to8(argv[i], var, sizeof(var));
howto |= boot_parse_arg(var);
}
return (howto);
}
static void
setenv_int(const char *key, int val)
{
char buf[20];
snprintf(buf, sizeof(buf), "%d", val);
setenv(key, buf, 1);
}
/*
* Parse ConOut (the list of consoles active) and see if we can find a
* serial port and/or a video port. It would be nice to also walk the
* ACPI name space to map the UID for the serial port to a port. The
* latter is especially hard.
*/
int
parse_uefi_con_out(void)
{
int how, rv;
int vid_seen = 0, com_seen = 0, seen = 0;
size_t sz;
char buf[4096], *ep;
EFI_DEVICE_PATH *node;
ACPI_HID_DEVICE_PATH *acpi;
UART_DEVICE_PATH *uart;
bool pci_pending;
how = 0;
sz = sizeof(buf);
rv = efi_global_getenv("ConOut", buf, &sz);
if (rv != EFI_SUCCESS)
rv = efi_global_getenv("ConOutDev", buf, &sz);
if (rv != EFI_SUCCESS) {
/* If we don't have any ConOut default to serial */
how = RB_SERIAL;
goto out;
}
ep = buf + sz;
node = (EFI_DEVICE_PATH *)buf;
while ((char *)node < ep) {
if (IsDevicePathEndType(node)) {
if (pci_pending && vid_seen == 0)
vid_seen = ++seen;
}
pci_pending = false;
if (DevicePathType(node) == ACPI_DEVICE_PATH &&
(DevicePathSubType(node) == ACPI_DP ||
DevicePathSubType(node) == ACPI_EXTENDED_DP)) {
/* Check for Serial node */
acpi = (void *)node;
if (EISA_ID_TO_NUM(acpi->HID) == 0x501) {
setenv_int("efi_8250_uid", acpi->UID);
com_seen = ++seen;
}
} else if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
DevicePathSubType(node) == MSG_UART_DP) {
com_seen = ++seen;
uart = (void *)node;
setenv_int("efi_com_speed", uart->BaudRate);
} else if (DevicePathType(node) == ACPI_DEVICE_PATH &&
DevicePathSubType(node) == ACPI_ADR_DP) {
/* Check for AcpiAdr() Node for video */
vid_seen = ++seen;
} else if (DevicePathType(node) == HARDWARE_DEVICE_PATH &&
DevicePathSubType(node) == HW_PCI_DP) {
/*
* Note, vmware fusion has a funky console device
* PciRoot(0x0)/Pci(0xf,0x0)
* which we can only detect at the end since we also
* have to cope with:
* PciRoot(0x0)/Pci(0x1f,0x0)/Serial(0x1)
* so only match it if it's last.
*/
pci_pending = true;
}
node = NextDevicePathNode(node);
}
/*
* Truth table for RB_MULTIPLE | RB_SERIAL
* Value Result
* 0 Use only video console
* RB_SERIAL Use only serial console
* RB_MULTIPLE Use both video and serial console
* (but video is primary so gets rc messages)
* both Use both video and serial console
* (but serial is primary so gets rc messages)
*
* Try to honor this as best we can. If only one of serial / video
* found, then use that. Otherwise, use the first one we found.
* This also implies if we found nothing, default to video.
*/
how = 0;
if (vid_seen && com_seen) {
how |= RB_MULTIPLE;
if (com_seen < vid_seen)
how |= RB_SERIAL;
} else if (com_seen)
how |= RB_SERIAL;
out:
return (how);
}
void
parse_loader_efi_config(EFI_HANDLE h, const char *env_fn)
{
pdinfo_t *dp;
struct stat st;
int fd = -1;
char *env = NULL;
dp = efiblk_get_pdinfo_by_handle(h);
if (dp == NULL)
return;
set_currdev_pdinfo(dp);
if (stat(env_fn, &st) != 0)
return;
fd = open(env_fn, O_RDONLY);
if (fd == -1)
return;
env = malloc(st.st_size + 1);
if (env == NULL)
goto out;
if (read(fd, env, st.st_size) != st.st_size)
goto out;
env[st.st_size] = '\0';
boot_parse_cmdline(env);
out:
free(env);
close(fd);
}
static void
read_loader_env(const char *name, char *def_fn, bool once)
{
UINTN len;
char *fn, *freeme = NULL;
len = 0;
fn = def_fn;
if (efi_freebsd_getenv(name, NULL, &len) == EFI_BUFFER_TOO_SMALL) {
freeme = fn = malloc(len + 1);
if (fn != NULL) {
if (efi_freebsd_getenv(name, fn, &len) != EFI_SUCCESS) {
free(fn);
fn = NULL;
printf(
"Can't fetch FreeBSD::%s we know is there\n", name);
} else {
/*
* if tagged as 'once' delete the env variable so we
* only use it once.
*/
if (once)
efi_freebsd_delenv(name);
/*
* We malloced 1 more than len above, then redid the call.
* so now we have room at the end of the string to NUL terminate
* it here, even if the typical idium would have '- 1' here to
* not overflow. len should be the same on return both times.
*/
fn[len] = '\0';
}
} else {
printf(
"Can't allocate %d bytes to fetch FreeBSD::%s env var\n",
len, name);
}
}
if (fn) {
printf(" Reading loader env vars from %s\n", fn);
parse_loader_efi_config(boot_img->DeviceHandle, fn);
}
}
caddr_t
ptov(uintptr_t x)
{
return ((caddr_t)x);
}
EFI_STATUS
main(int argc, CHAR16 *argv[])
{
EFI_GUID *guid;
int howto, i, uhowto;
UINTN k;
bool has_kbd, is_last;
char *s;
EFI_DEVICE_PATH *imgpath;
CHAR16 *text;
EFI_STATUS rv;
size_t sz, bosz = 0, bisz = 0;
UINT16 boot_order[100];
char boot_info[4096];
char buf[32];
bool uefi_boot_mgr;
archsw.arch_autoload = efi_autoload;
archsw.arch_getdev = efi_getdev;
archsw.arch_copyin = efi_copyin;
archsw.arch_copyout = efi_copyout;
#ifdef __amd64__
archsw.arch_hypervisor = x86_hypervisor;
#endif
archsw.arch_readin = efi_readin;
archsw.arch_zfs_probe = efi_zfs_probe;
/* Get our loaded image protocol interface structure. */
(void) OpenProtocolByHandle(IH, &imgid, (void **)&boot_img);
/*
* Chicken-and-egg problem; we want to have console output early, but
* some console attributes may depend on reading from eg. the boot
* device, which we can't do yet. We can use printf() etc. once this is
* done. So, we set it to the efi console, then call console init. This
* gets us printf early, but also primes the pump for all future console
* changes to take effect, regardless of where they come from.
*/
setenv("console", "efi", 1);
uhowto = parse_uefi_con_out();
#if defined(__riscv)
if ((uhowto & RB_SERIAL) != 0)
setenv("console", "comconsole", 1);
#endif
cons_probe();
/* Set up currdev variable to have hooks in place. */
env_setenv("currdev", EV_VOLATILE, "", efi_setcurrdev, env_nounset);
/* Init the time source */
efi_time_init();
/*
* Initialise the block cache. Set the upper limit.
*/
bcache_init(32768, 512);
/*
* Scan the BLOCK IO MEDIA handles then
* march through the device switch probing for things.
*/
i = efipart_inithandles();
if (i != 0 && i != ENOENT) {
printf("efipart_inithandles failed with ERRNO %d, expect "
"failures\n", i);
}
for (i = 0; devsw[i] != NULL; i++)
if (devsw[i]->dv_init != NULL)
(devsw[i]->dv_init)();
/*
* Detect console settings two different ways: one via the command
* args (eg -h) or via the UEFI ConOut variable.
*/
has_kbd = has_keyboard();
howto = parse_args(argc, argv);
if (!has_kbd && (howto & RB_PROBE))
howto |= RB_SERIAL | RB_MULTIPLE;
howto &= ~RB_PROBE;
/*
* Read additional environment variables from the boot device's
* "LoaderEnv" file. Any boot loader environment variable may be set
* there, which are subtly different than loader.conf variables. Only
* the 'simple' ones may be set so things like foo_load="YES" won't work
* for two reasons. First, the parser is simplistic and doesn't grok
* quotes. Second, because the variables that cause an action to happen
* are parsed by the lua, 4th or whatever code that's not yet
* loaded. This is relative to the root directory when loader.efi is
* loaded off the UFS root drive (when chain booted), or from the ESP
* when directly loaded by the BIOS.
*
* We also read in NextLoaderEnv if it was specified. This allows next boot
* functionality to be implemented and to override anything in LoaderEnv.
*/
read_loader_env("LoaderEnv", "/efi/freebsd/loader.env", false);
read_loader_env("NextLoaderEnv", NULL, true);
/*
* We now have two notions of console. howto should be viewed as
* overrides. If console is already set, don't set it again.
*/
#define VIDEO_ONLY 0
#define SERIAL_ONLY RB_SERIAL
#define VID_SER_BOTH RB_MULTIPLE
#define SER_VID_BOTH (RB_SERIAL | RB_MULTIPLE)
#define CON_MASK (RB_SERIAL | RB_MULTIPLE)
if (strcmp(getenv("console"), "efi") == 0) {
if ((howto & CON_MASK) == 0) {
/* No override, uhowto is controlling and efi cons is perfect */
howto = howto | (uhowto & CON_MASK);
} else if ((howto & CON_MASK) == (uhowto & CON_MASK)) {
/* override matches what UEFI told us, efi console is perfect */
} else if ((uhowto & (CON_MASK)) != 0) {
/*
* We detected a serial console on ConOut. All possible
* overrides include serial. We can't really override what efi
* gives us, so we use it knowing it's the best choice.
*/
/* Do nothing */
} else {
/*
* We detected some kind of serial in the override, but ConOut
* has no serial, so we have to sort out which case it really is.
*/
switch (howto & CON_MASK) {
case SERIAL_ONLY:
setenv("console", "comconsole", 1);
break;
case VID_SER_BOTH:
setenv("console", "efi comconsole", 1);
break;
case SER_VID_BOTH:
setenv("console", "comconsole efi", 1);
break;
/* case VIDEO_ONLY can't happen -- it's the first if above */
}
}
}
/*
* howto is set now how we want to export the flags to the kernel, so
* set the env based on it.
*/
boot_howto_to_env(howto);
if (efi_copy_init()) {
printf("failed to allocate staging area\n");
return (EFI_BUFFER_TOO_SMALL);
}
if ((s = getenv("fail_timeout")) != NULL)
fail_timeout = strtol(s, NULL, 10);
printf("%s\n", bootprog_info);
printf(" Command line arguments:");
for (i = 0; i < argc; i++)
printf(" %S", argv[i]);
printf("\n");
printf(" Image base: 0x%lx\n", (unsigned long)boot_img->ImageBase);
printf(" EFI version: %d.%02d\n", ST->Hdr.Revision >> 16,
ST->Hdr.Revision & 0xffff);
printf(" EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor,
ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
printf(" Console: %s (%#x)\n", getenv("console"), howto);
/* Determine the devpath of our image so we can prefer it. */
text = efi_devpath_name(boot_img->FilePath);
if (text != NULL) {
printf(" Load Path: %S\n", text);
efi_setenv_freebsd_wcs("LoaderPath", text);
efi_free_devpath_name(text);
}
rv = OpenProtocolByHandle(boot_img->DeviceHandle, &devid,
(void **)&imgpath);
if (rv == EFI_SUCCESS) {
text = efi_devpath_name(imgpath);
if (text != NULL) {
printf(" Load Device: %S\n", text);
efi_setenv_freebsd_wcs("LoaderDev", text);
efi_free_devpath_name(text);
}
}
if (getenv("uefi_ignore_boot_mgr") != NULL) {
printf(" Ignoring UEFI boot manager\n");
uefi_boot_mgr = false;
} else {
uefi_boot_mgr = true;
boot_current = 0;
sz = sizeof(boot_current);
rv = efi_global_getenv("BootCurrent", &boot_current, &sz);
if (rv == EFI_SUCCESS)
printf(" BootCurrent: %04x\n", boot_current);
else {
boot_current = 0xffff;
uefi_boot_mgr = false;
}
sz = sizeof(boot_order);
rv = efi_global_getenv("BootOrder", &boot_order, &sz);
if (rv == EFI_SUCCESS) {
printf(" BootOrder:");
for (i = 0; i < sz / sizeof(boot_order[0]); i++)
printf(" %04x%s", boot_order[i],
boot_order[i] == boot_current ? "[*]" : "");
printf("\n");
is_last = boot_order[(sz / sizeof(boot_order[0])) - 1] == boot_current;
bosz = sz;
} else if (uefi_boot_mgr) {
/*
* u-boot doesn't set BootOrder, but otherwise participates in the
* boot manager protocol. So we fake it here and don't consider it
* a failure.
*/
bosz = sizeof(boot_order[0]);
boot_order[0] = boot_current;
is_last = true;
}
}
/*
* Next, find the boot info structure the UEFI boot manager is
* supposed to setup. We need this so we can walk through it to
* find where we are in the booting process and what to try to
* boot next.
*/
if (uefi_boot_mgr) {
snprintf(buf, sizeof(buf), "Boot%04X", boot_current);
sz = sizeof(boot_info);
rv = efi_global_getenv(buf, &boot_info, &sz);
if (rv == EFI_SUCCESS)
bisz = sz;
else
uefi_boot_mgr = false;
}
/*
* Disable the watchdog timer. By default the boot manager sets
* the timer to 5 minutes before invoking a boot option. If we
* want to return to the boot manager, we have to disable the
* watchdog timer and since we're an interactive program, we don't
* 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
* normal functioning in any way...
*/
BS->SetWatchdogTimer(0, 0, 0, NULL);
/*
* Initialize the trusted/forbidden certificates from UEFI.
* They will be later used to verify the manifest(s),
* which should contain hashes of verified files.
* This needs to be initialized before any configuration files
* are loaded.
*/
#ifdef EFI_SECUREBOOT
ve_efi_init();
#endif
/*
* Try and find a good currdev based on the image that was booted.
* It might be desirable here to have a short pause to allow falling
* through to the boot loader instead of returning instantly to follow
* the boot protocol and also allow an escape hatch for users wishing
* to try something different.
*/
if (find_currdev(uefi_boot_mgr, is_last, boot_info, bisz) != 0)
if (uefi_boot_mgr &&
!interactive_interrupt("Failed to find bootable partition"))
return (EFI_NOT_FOUND);
autoload_font(false); /* Set up the font list for console. */
efi_init_environment();
#if !defined(__arm__)
for (k = 0; k < ST->NumberOfTableEntries; k++) {
guid = &ST->ConfigurationTable[k].VendorGuid;
if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) {
char buf[40];
snprintf(buf, sizeof(buf), "%p",
ST->ConfigurationTable[k].VendorTable);
setenv("hint.smbios.0.mem", buf, 1);
smbios_detect(ST->ConfigurationTable[k].VendorTable);
break;
}
}
#endif
interact(); /* doesn't return */
return (EFI_SUCCESS); /* keep compiler happy */
}
+COMMAND_SET(efi_seed_entropy, "efi-seed-entropy", "try to get entropy from the EFI RNG", command_seed_entropy);
+
+static int
+command_seed_entropy(int argc, char *argv[])
+{
+ EFI_STATUS status;
+ EFI_RNG_PROTOCOL *rng;
+ unsigned int size = 2048;
+ void *buf;
+
+ if (argc > 1) {
+ size = strtol(argv[1], NULL, 0);
+ }
+
+ status = BS->LocateProtocol(&rng_guid, NULL, (VOID **)&rng);
+ if (status != EFI_SUCCESS) {
+ command_errmsg = "RNG protocol not found";
+ return (CMD_ERROR);
+ }
+
+ if ((buf = malloc(size)) == NULL) {
+ command_errmsg = "out of memory";
+ return (CMD_ERROR);
+ }
+
+ status = rng->GetRNG(rng, NULL, size, (UINT8 *)buf);
+ if (status != EFI_SUCCESS) {
+ free(buf);
+ command_errmsg = "GetRNG failed";
+ return (CMD_ERROR);
+ }
+
+ if (file_addbuf("efi_rng_seed", "boot_entropy_platform", size, buf) != 0) {
+ free(buf);
+ return (CMD_ERROR);
+ }
+
+ free(buf);
+ return (CMD_OK);
+}
+
COMMAND_SET(poweroff, "poweroff", "power off the system", command_poweroff);
static int
command_poweroff(int argc __unused, char *argv[] __unused)
{
int i;
for (i = 0; devsw[i] != NULL; ++i)
if (devsw[i]->dv_cleanup != NULL)
(devsw[i]->dv_cleanup)();
RS->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
/* NOTREACHED */
return (CMD_ERROR);
}
COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
static int
command_reboot(int argc, char *argv[])
{
int i;
for (i = 0; devsw[i] != NULL; ++i)
if (devsw[i]->dv_cleanup != NULL)
(devsw[i]->dv_cleanup)();
RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
/* NOTREACHED */
return (CMD_ERROR);
}
COMMAND_SET(quit, "quit", "exit the loader", command_quit);
static int
command_quit(int argc, char *argv[])
{
exit(0);
return (CMD_OK);
}
COMMAND_SET(memmap, "memmap", "print memory map", command_memmap);
static int
command_memmap(int argc __unused, char *argv[] __unused)
{
UINTN sz;
EFI_MEMORY_DESCRIPTOR *map, *p;
UINTN key, dsz;
UINT32 dver;
EFI_STATUS status;
int i, ndesc;
char line[80];
sz = 0;
status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver);
if (status != EFI_BUFFER_TOO_SMALL) {
printf("Can't determine memory map size\n");
return (CMD_ERROR);
}
map = malloc(sz);
status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver);
if (EFI_ERROR(status)) {
printf("Can't read memory map\n");
return (CMD_ERROR);
}
ndesc = sz / dsz;
snprintf(line, sizeof(line), "%23s %12s %12s %8s %4s\n",
"Type", "Physical", "Virtual", "#Pages", "Attr");
pager_open();
if (pager_output(line)) {
pager_close();
return (CMD_OK);
}
for (i = 0, p = map; i < ndesc;
i++, p = NextMemoryDescriptor(p, dsz)) {
snprintf(line, sizeof(line), "%23s %012jx %012jx %08jx ",
efi_memory_type(p->Type), (uintmax_t)p->PhysicalStart,
(uintmax_t)p->VirtualStart, (uintmax_t)p->NumberOfPages);
if (pager_output(line))
break;
if (p->Attribute & EFI_MEMORY_UC)
printf("UC ");
if (p->Attribute & EFI_MEMORY_WC)
printf("WC ");
if (p->Attribute & EFI_MEMORY_WT)
printf("WT ");
if (p->Attribute & EFI_MEMORY_WB)
printf("WB ");
if (p->Attribute & EFI_MEMORY_UCE)
printf("UCE ");
if (p->Attribute & EFI_MEMORY_WP)
printf("WP ");
if (p->Attribute & EFI_MEMORY_RP)
printf("RP ");
if (p->Attribute & EFI_MEMORY_XP)
printf("XP ");
if (p->Attribute & EFI_MEMORY_NV)
printf("NV ");
if (p->Attribute & EFI_MEMORY_MORE_RELIABLE)
printf("MR ");
if (p->Attribute & EFI_MEMORY_RO)
printf("RO ");
if (pager_output("\n"))
break;
}
pager_close();
return (CMD_OK);
}
COMMAND_SET(configuration, "configuration", "print configuration tables",
command_configuration);
static int
command_configuration(int argc, char *argv[])
{
UINTN i;
char *name;
printf("NumberOfTableEntries=%lu\n",
(unsigned long)ST->NumberOfTableEntries);
for (i = 0; i < ST->NumberOfTableEntries; i++) {
EFI_GUID *guid;
printf(" ");
guid = &ST->ConfigurationTable[i].VendorGuid;
if (efi_guid_to_name(guid, &name) == true) {
printf(name);
free(name);
} else {
printf("Error while translating UUID to name");
}
printf(" at %p\n", ST->ConfigurationTable[i].VendorTable);
}
return (CMD_OK);
}
COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode);
static int
command_mode(int argc, char *argv[])
{
UINTN cols, rows;
unsigned int mode;
int i;
char *cp;
EFI_STATUS status;
SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
conout = ST->ConOut;
if (argc > 1) {
mode = strtol(argv[1], &cp, 0);
if (cp[0] != '\0') {
printf("Invalid mode\n");
return (CMD_ERROR);
}
status = conout->QueryMode(conout, mode, &cols, &rows);
if (EFI_ERROR(status)) {
printf("invalid mode %d\n", mode);
return (CMD_ERROR);
}
status = conout->SetMode(conout, mode);
if (EFI_ERROR(status)) {
printf("couldn't set mode %d\n", mode);
return (CMD_ERROR);
}
(void) cons_update_mode(true);
return (CMD_OK);
}
printf("Current mode: %d\n", conout->Mode->Mode);
for (i = 0; i <= conout->Mode->MaxMode; i++) {
status = conout->QueryMode(conout, i, &cols, &rows);
if (EFI_ERROR(status))
continue;
printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols,
(unsigned)rows);
}
if (i != 0)
printf("Select a mode with the command \"mode <number>\"\n");
return (CMD_OK);
}
COMMAND_SET(lsefi, "lsefi", "list EFI handles", command_lsefi);
static void
lsefi_print_handle_info(EFI_HANDLE handle)
{
EFI_DEVICE_PATH *devpath;
EFI_DEVICE_PATH *imagepath;
CHAR16 *dp_name;
imagepath = efi_lookup_image_devpath(handle);
if (imagepath != NULL) {
dp_name = efi_devpath_name(imagepath);
printf("Handle for image %S", dp_name);
efi_free_devpath_name(dp_name);
return;
}
devpath = efi_lookup_devpath(handle);
if (devpath != NULL) {
dp_name = efi_devpath_name(devpath);
printf("Handle for device %S", dp_name);
efi_free_devpath_name(dp_name);
return;
}
printf("Handle %p", handle);
}
static int
command_lsefi(int argc __unused, char *argv[] __unused)
{
char *name;
EFI_HANDLE *buffer = NULL;
EFI_HANDLE handle;
UINTN bufsz = 0, i, j;
EFI_STATUS status;
int ret = 0;
status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer);
if (status != EFI_BUFFER_TOO_SMALL) {
snprintf(command_errbuf, sizeof (command_errbuf),
"unexpected error: %lld", (long long)status);
return (CMD_ERROR);
}
if ((buffer = malloc(bufsz)) == NULL) {
sprintf(command_errbuf, "out of memory");
return (CMD_ERROR);
}
status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer);
if (EFI_ERROR(status)) {
free(buffer);
snprintf(command_errbuf, sizeof (command_errbuf),
"LocateHandle() error: %lld", (long long)status);
return (CMD_ERROR);
}
pager_open();
for (i = 0; i < (bufsz / sizeof (EFI_HANDLE)); i++) {
UINTN nproto = 0;
EFI_GUID **protocols = NULL;
handle = buffer[i];
lsefi_print_handle_info(handle);
if (pager_output("\n"))
break;
/* device path */
status = BS->ProtocolsPerHandle(handle, &protocols, &nproto);
if (EFI_ERROR(status)) {
snprintf(command_errbuf, sizeof (command_errbuf),
"ProtocolsPerHandle() error: %lld",
(long long)status);
continue;
}
for (j = 0; j < nproto; j++) {
if (efi_guid_to_name(protocols[j], &name) == true) {
printf(" %s", name);
free(name);
} else {
printf("Error while translating UUID to name");
}
if ((ret = pager_output("\n")) != 0)
break;
}
BS->FreePool(protocols);
if (ret != 0)
break;
}
pager_close();
free(buffer);
return (CMD_OK);
}
#ifdef LOADER_FDT_SUPPORT
extern int command_fdt_internal(int argc, char *argv[]);
/*
* 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
* (which uses static pointer), we're defining wrapper function, which
* calls the proper fdt handling routine.
*/
static int
command_fdt(int argc, char *argv[])
{
return (command_fdt_internal(argc, argv));
}
COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
#endif
/*
* Chain load another efi loader.
*/
static int
command_chain(int argc, char *argv[])
{
EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
EFI_HANDLE loaderhandle;
EFI_LOADED_IMAGE *loaded_image;
EFI_STATUS status;
struct stat st;
struct devdesc *dev;
char *name, *path;
void *buf;
int fd;
if (argc < 2) {
command_errmsg = "wrong number of arguments";
return (CMD_ERROR);
}
name = argv[1];
if ((fd = open(name, O_RDONLY)) < 0) {
command_errmsg = "no such file";
return (CMD_ERROR);
}
#ifdef LOADER_VERIEXEC
if (verify_file(fd, name, 0, VE_MUST, __func__) < 0) {
sprintf(command_errbuf, "can't verify: %s", name);
close(fd);
return (CMD_ERROR);
}
#endif
if (fstat(fd, &st) < -1) {
command_errmsg = "stat failed";
close(fd);
return (CMD_ERROR);
}
status = BS->AllocatePool(EfiLoaderCode, (UINTN)st.st_size, &buf);
if (status != EFI_SUCCESS) {
command_errmsg = "failed to allocate buffer";
close(fd);
return (CMD_ERROR);
}
if (read(fd, buf, st.st_size) != st.st_size) {
command_errmsg = "error while reading the file";
(void)BS->FreePool(buf);
close(fd);
return (CMD_ERROR);
}
close(fd);
status = BS->LoadImage(FALSE, IH, NULL, buf, st.st_size, &loaderhandle);
(void)BS->FreePool(buf);
if (status != EFI_SUCCESS) {
command_errmsg = "LoadImage failed";
return (CMD_ERROR);
}
status = OpenProtocolByHandle(loaderhandle, &LoadedImageGUID,
(void **)&loaded_image);
if (argc > 2) {
int i, len = 0;
CHAR16 *argp;
for (i = 2; i < argc; i++)
len += strlen(argv[i]) + 1;
len *= sizeof (*argp);
loaded_image->LoadOptions = argp = malloc (len);
loaded_image->LoadOptionsSize = len;
for (i = 2; i < argc; i++) {
char *ptr = argv[i];
while (*ptr)
*(argp++) = *(ptr++);
*(argp++) = ' ';
}
*(--argv) = 0;
}
if (efi_getdev((void **)&dev, name, (const char **)&path) == 0) {
#ifdef EFI_ZFS_BOOT
struct zfs_devdesc *z_dev;
#endif
struct disk_devdesc *d_dev;
pdinfo_t *hd, *pd;
switch (dev->d_dev->dv_type) {
#ifdef EFI_ZFS_BOOT
case DEVT_ZFS:
z_dev = (struct zfs_devdesc *)dev;
loaded_image->DeviceHandle =
efizfs_get_handle_by_guid(z_dev->pool_guid);
break;
#endif
case DEVT_NET:
loaded_image->DeviceHandle =
efi_find_handle(dev->d_dev, dev->d_unit);
break;
default:
hd = efiblk_get_pdinfo(dev);
if (STAILQ_EMPTY(&hd->pd_part)) {
loaded_image->DeviceHandle = hd->pd_handle;
break;
}
d_dev = (struct disk_devdesc *)dev;
STAILQ_FOREACH(pd, &hd->pd_part, pd_link) {
/*
* d_partition should be 255
*/
if (pd->pd_unit == (uint32_t)d_dev->d_slice) {
loaded_image->DeviceHandle =
pd->pd_handle;
break;
}
}
break;
}
}
dev_cleanup();
status = BS->StartImage(loaderhandle, NULL, NULL);
if (status != EFI_SUCCESS) {
command_errmsg = "StartImage failed";
free(loaded_image->LoadOptions);
loaded_image->LoadOptions = NULL;
status = BS->UnloadImage(loaded_image);
return (CMD_ERROR);
}
return (CMD_ERROR); /* not reached */
}
COMMAND_SET(chain, "chain", "chain load file", command_chain);
extern struct in_addr servip;
static int
command_netserver(int argc, char *argv[])
{
char *proto;
n_long rootaddr;
if (argc > 2) {
command_errmsg = "wrong number of arguments";
return (CMD_ERROR);
}
if (argc < 2) {
proto = netproto == NET_TFTP ? "tftp://" : "nfs://";
printf("Netserver URI: %s%s%s\n", proto, intoa(rootip.s_addr),
rootpath);
return (CMD_OK);
}
if (argc == 2) {
strncpy(rootpath, argv[1], sizeof(rootpath));
rootpath[sizeof(rootpath) -1] = '\0';
if ((rootaddr = net_parse_rootpath()) != INADDR_NONE)
servip.s_addr = rootip.s_addr = rootaddr;
return (CMD_OK);
}
return (CMD_ERROR); /* not reached */
}
COMMAND_SET(netserver, "netserver", "change or display netserver URI",
command_netserver);
diff --git a/stand/lua/core.lua b/stand/lua/core.lua
index 67d51b99285..27e3c14de9f 100644
--- a/stand/lua/core.lua
+++ b/stand/lua/core.lua
@@ -1,517 +1,527 @@
--
-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD
--
-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
-- Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
-- 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 AUTHOR 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 AUTHOR 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.
--
-- $FreeBSD$
--
local config = require("config")
local hook = require("hook")
local core = {}
local default_safe_mode = false
local default_single_user = false
local default_verbose = false
local bootenv_list = "bootenvs"
local function composeLoaderCmd(cmd_name, argstr)
if argstr ~= nil then
cmd_name = cmd_name .. " " .. argstr
end
return cmd_name
end
local function recordDefaults()
-- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386,
-- it will generally be set upon execution of the kernel. Because of
-- this, we can't (or don't really want to) detect/disable ACPI on !i386
-- reliably. Just set it enabled if we detect it and leave well enough
-- alone if we don't.
local boot_acpi = core.isSystem386() and core.getACPIPresent(false)
local boot_single = loader.getenv("boot_single") or "no"
local boot_verbose = loader.getenv("boot_verbose") or "no"
default_single_user = boot_single:lower() ~= "no"
default_verbose = boot_verbose:lower() ~= "no"
if boot_acpi then
core.setACPI(true)
end
core.setSingleUser(default_single_user)
core.setVerbose(default_verbose)
end
-- Globals
-- try_include will return the loaded module on success, or false and the error
-- message on failure.
function try_include(module)
if module:sub(1, 1) ~= "/" then
local lua_path = loader.lua_path
-- XXX Temporary compat shim; this should be removed once the
-- loader.lua_path export has sufficiently spread.
if lua_path == nil then
lua_path = "/boot/lua"
end
module = lua_path .. "/" .. module
-- We only attempt to append an extension if an absolute path
-- wasn't specified. This assumes that the caller either wants
-- to treat this like it would require() and specify just the
-- base filename, or they know what they're doing as they've
-- specified an absolute path and we shouldn't impede.
if module:match(".lua$") == nil then
module = module .. ".lua"
end
end
if lfs.attributes(module, "mode") ~= "file" then
return
end
return dofile(module)
end
-- Module exports
-- Commonly appearing constants
core.KEY_BACKSPACE = 8
core.KEY_ENTER = 13
core.KEY_DELETE = 127
-- Note that this is a decimal representation, despite the leading 0 that in
-- other contexts (outside of Lua) may mean 'octal'
core.KEYSTR_ESCAPE = "\027"
core.KEYSTR_CSI = core.KEYSTR_ESCAPE .. "["
core.KEYSTR_RESET = core.KEYSTR_ESCAPE .. "c"
core.MENU_RETURN = "return"
core.MENU_ENTRY = "entry"
core.MENU_SEPARATOR = "separator"
core.MENU_SUBMENU = "submenu"
core.MENU_CAROUSEL_ENTRY = "carousel_entry"
function core.setVerbose(verbose)
if verbose == nil then
verbose = not core.verbose
end
if verbose then
loader.setenv("boot_verbose", "YES")
else
loader.unsetenv("boot_verbose")
end
core.verbose = verbose
end
function core.setSingleUser(single_user)
if single_user == nil then
single_user = not core.su
end
if single_user then
loader.setenv("boot_single", "YES")
else
loader.unsetenv("boot_single")
end
core.su = single_user
end
function core.getACPIPresent(checking_system_defaults)
local c = loader.getenv("hint.acpi.0.rsdp")
if c ~= nil then
if checking_system_defaults then
return true
end
-- Otherwise, respect disabled if it's set
c = loader.getenv("hint.acpi.0.disabled")
return c == nil or tonumber(c) ~= 1
end
return false
end
function core.setACPI(acpi)
if acpi == nil then
acpi = not core.acpi
end
if acpi then
loader.setenv("acpi_load", "YES")
loader.setenv("hint.acpi.0.disabled", "0")
loader.unsetenv("loader.acpi_disabled_by_user")
else
loader.unsetenv("acpi_load")
loader.setenv("hint.acpi.0.disabled", "1")
loader.setenv("loader.acpi_disabled_by_user", "1")
end
core.acpi = acpi
end
function core.setSafeMode(safe_mode)
if safe_mode == nil then
safe_mode = not core.sm
end
if safe_mode then
loader.setenv("kern.smp.disabled", "1")
loader.setenv("hw.ata.ata_dma", "0")
loader.setenv("hw.ata.atapi_dma", "0")
loader.setenv("hw.ata.wc", "0")
loader.setenv("hw.eisa_slots", "0")
loader.setenv("kern.eventtimer.periodic", "1")
loader.setenv("kern.geom.part.check_integrity", "0")
else
loader.unsetenv("kern.smp.disabled")
loader.unsetenv("hw.ata.ata_dma")
loader.unsetenv("hw.ata.atapi_dma")
loader.unsetenv("hw.ata.wc")
loader.unsetenv("hw.eisa_slots")
loader.unsetenv("kern.eventtimer.periodic")
loader.unsetenv("kern.geom.part.check_integrity")
end
core.sm = safe_mode
end
function core.clearCachedKernels()
-- Clear the kernel cache on config changes, autodetect might have
-- changed or if we've switched boot environments then we could have
-- a new kernel set.
core.cached_kernels = nil
end
function core.kernelList()
if core.cached_kernels ~= nil then
return core.cached_kernels
end
local k = loader.getenv("kernel")
local v = loader.getenv("kernels")
local autodetect = loader.getenv("kernels_autodetect") or ""
local kernels = {}
local unique = {}
local i = 0
if k ~= nil then
i = i + 1
kernels[i] = k
unique[k] = true
end
if v ~= nil then
for n in v:gmatch("([^;, ]+)[;, ]?") do
if unique[n] == nil then
i = i + 1
kernels[i] = n
unique[n] = true
end
end
end
-- Do not attempt to autodetect if underlying filesystem
-- do not support directory listing (e.g. tftp, http)
if not lfs.attributes("/boot", "mode") then
autodetect = "no"
loader.setenv("kernels_autodetect", "NO")
end
-- Base whether we autodetect kernels or not on a loader.conf(5)
-- setting, kernels_autodetect. If it's set to 'yes', we'll add
-- any kernels we detect based on the criteria described.
if autodetect:lower() ~= "yes" then
core.cached_kernels = kernels
return core.cached_kernels
end
-- Automatically detect other bootable kernel directories using a
-- heuristic. Any directory in /boot that contains an ordinary file
-- named "kernel" is considered eligible.
for file, ftype in lfs.dir("/boot") do
local fname = "/boot/" .. file
if file == "." or file == ".." then
goto continue
end
if ftype then
if ftype ~= lfs.DT_DIR then
goto continue
end
elseif lfs.attributes(fname, "mode") ~= "directory" then
goto continue
end
if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then
goto continue
end
if unique[file] == nil then
i = i + 1
kernels[i] = file
unique[file] = true
end
::continue::
end
core.cached_kernels = kernels
return core.cached_kernels
end
function core.bootenvDefault()
return loader.getenv("zfs_be_active")
end
function core.bootenvList()
local bootenv_count = tonumber(loader.getenv(bootenv_list .. "_count"))
local bootenvs = {}
local curenv
local envcount = 0
local unique = {}
if bootenv_count == nil or bootenv_count <= 0 then
return bootenvs
end
-- Currently selected bootenv is always first/default
-- On the rewinded list the bootenv may not exists
if core.isRewinded() then
curenv = core.bootenvDefaultRewinded()
else
curenv = core.bootenvDefault()
end
if curenv ~= nil then
envcount = envcount + 1
bootenvs[envcount] = curenv
unique[curenv] = true
end
for curenv_idx = 0, bootenv_count - 1 do
curenv = loader.getenv(bootenv_list .. "[" .. curenv_idx .. "]")
if curenv ~= nil and unique[curenv] == nil then
envcount = envcount + 1
bootenvs[envcount] = curenv
unique[curenv] = true
end
end
return bootenvs
end
function core.isCheckpointed()
return loader.getenv("zpool_checkpoint") ~= nil
end
function core.bootenvDefaultRewinded()
local defname = "zfs:!" .. string.sub(core.bootenvDefault(), 5)
local bootenv_count = tonumber("bootenvs_check_count")
if bootenv_count == nil or bootenv_count <= 0 then
return defname
end
for curenv_idx = 0, bootenv_count - 1 do
local curenv = loader.getenv("bootenvs_check[" .. curenv_idx .. "]")
if curenv == defname then
return defname
end
end
return loader.getenv("bootenvs_check[0]")
end
function core.isRewinded()
return bootenv_list == "bootenvs_check"
end
function core.changeRewindCheckpoint()
if core.isRewinded() then
bootenv_list = "bootenvs"
else
bootenv_list = "bootenvs_check"
end
end
+function core.loadEntropy()
+ if core.isUEFIBoot() then
+ if (loader.getenv("entropy_efi_seed") or "no"):lower() == "yes" then
+ loader.perform("efi-seed-entropy")
+ end
+ end
+end
+
function core.setDefaults()
core.setACPI(core.getACPIPresent(true))
core.setSafeMode(default_safe_mode)
core.setSingleUser(default_single_user)
core.setVerbose(default_verbose)
end
function core.autoboot(argstr)
-- loadelf() only if we've not already loaded a kernel
if loader.getenv("kernelname") == nil then
config.loadelf()
end
+ core.loadEntropy()
loader.perform(composeLoaderCmd("autoboot", argstr))
end
function core.boot(argstr)
-- loadelf() only if we've not already loaded a kernel
if loader.getenv("kernelname") == nil then
config.loadelf()
end
+ core.loadEntropy()
loader.perform(composeLoaderCmd("boot", argstr))
end
function core.isSingleUserBoot()
local single_user = loader.getenv("boot_single")
return single_user ~= nil and single_user:lower() == "yes"
end
function core.isUEFIBoot()
local efiver = loader.getenv("efi-version")
return efiver ~= nil
end
function core.isZFSBoot()
local c = loader.getenv("currdev")
if c ~= nil then
return c:match("^zfs:") ~= nil
end
return false
end
function core.isFramebufferConsole()
local c = loader.getenv("console")
if c ~= nil then
if c:find("efi") == nil and c:find("vidconsole") == nil then
return false
end
if loader.getenv("screen.depth") ~= nil then
return true
end
end
return false
end
function core.isSerialConsole()
local c = loader.getenv("console")
if c ~= nil then
-- serial console is comconsole, but also userboot.
-- userboot is there, because we have no way to know
-- if the user terminal can draw unicode box chars or not.
if c:find("comconsole") ~= nil or c:find("userboot") ~= nil then
return true
end
end
return false
end
function core.isSerialBoot()
local s = loader.getenv("boot_serial")
if s ~= nil then
return true
end
local m = loader.getenv("boot_multicons")
if m ~= nil then
return true
end
return false
end
function core.isSystem386()
return loader.machine_arch == "i386"
end
-- Is the menu skipped in the environment in which we've booted?
function core.isMenuSkipped()
return string.lower(loader.getenv("beastie_disable") or "") == "yes"
end
-- This may be a better candidate for a 'utility' module.
function core.deepCopyTable(tbl)
local new_tbl = {}
for k, v in pairs(tbl) do
if type(v) == "table" then
new_tbl[k] = core.deepCopyTable(v)
else
new_tbl[k] = v
end
end
return new_tbl
end
-- XXX This should go away if we get the table lib into shape for importing.
-- As of now, it requires some 'os' functions, so we'll implement this in lua
-- for our uses
function core.popFrontTable(tbl)
-- Shouldn't reasonably happen
if #tbl == 0 then
return nil, nil
elseif #tbl == 1 then
return tbl[1], {}
end
local first_value = tbl[1]
local new_tbl = {}
-- This is not a cheap operation
for k, v in ipairs(tbl) do
if k > 1 then
new_tbl[k - 1] = v
end
end
return first_value, new_tbl
end
function core.getConsoleName()
if loader.getenv("boot_multicons") ~= nil then
if loader.getenv("boot_serial") ~= nil then
return "Dual (Serial primary)"
else
return "Dual (Video primary)"
end
else
if loader.getenv("boot_serial") ~= nil then
return "Serial"
else
return "Video"
end
end
end
function core.nextConsoleChoice()
if loader.getenv("boot_multicons") ~= nil then
if loader.getenv("boot_serial") ~= nil then
loader.unsetenv("boot_serial")
else
loader.unsetenv("boot_multicons")
loader.setenv("boot_serial", "YES")
end
else
if loader.getenv("boot_serial") ~= nil then
loader.unsetenv("boot_serial")
else
loader.setenv("boot_multicons", "YES")
loader.setenv("boot_serial", "YES")
end
end
end
recordDefaults()
hook.register("config.reloaded", core.clearCachedKernels)
return core
diff --git a/sys/dev/random/random_harvestq.c b/sys/dev/random/random_harvestq.c
index 46c06912694..e868c1fe3c5 100644
--- a/sys/dev/random/random_harvestq.c
+++ b/sys/dev/random/random_harvestq.c
@@ -1,669 +1,677 @@
/*-
* Copyright (c) 2017 Oliver Pinter
* Copyright (c) 2017 W. Dean Freeman
* Copyright (c) 2000-2015 Mark R V Murray
* Copyright (c) 2013 Arthur Mesh
* Copyright (c) 2004 Robert N. M. Watson
* 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
* in this position and unchanged.
* 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 AUTHOR ``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 AUTHOR 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ck.h>
#include <sys/conf.h>
#include <sys/epoch.h>
#include <sys/eventhandler.h>
#include <sys/hash.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/linker.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/random.h>
#include <sys/sbuf.h>
#include <sys/sysctl.h>
#include <sys/unistd.h>
#include <machine/atomic.h>
#include <machine/cpu.h>
#include <crypto/rijndael/rijndael-api-fst.h>
#include <crypto/sha2/sha256.h>
#include <dev/random/hash.h>
#include <dev/random/randomdev.h>
#include <dev/random/random_harvestq.h>
#if defined(RANDOM_ENABLE_ETHER)
#define _RANDOM_HARVEST_ETHER_OFF 0
#else
#define _RANDOM_HARVEST_ETHER_OFF (1u << RANDOM_NET_ETHER)
#endif
#if defined(RANDOM_ENABLE_UMA)
#define _RANDOM_HARVEST_UMA_OFF 0
#else
#define _RANDOM_HARVEST_UMA_OFF (1u << RANDOM_UMA)
#endif
/*
* Note that random_sources_feed() will also use this to try and split up
* entropy into a subset of pools per iteration with the goal of feeding
* HARVESTSIZE into every pool at least once per second.
*/
#define RANDOM_KTHREAD_HZ 10
static void random_kthread(void);
static void random_sources_feed(void);
/*
* Random must initialize much earlier than epoch, but we can initialize the
* epoch code before SMP starts. Prior to SMP, we can safely bypass
* concurrency primitives.
*/
static __read_mostly bool epoch_inited;
static __read_mostly epoch_t rs_epoch;
/*
* How many events to queue up. We create this many items in
* an 'empty' queue, then transfer them to the 'harvest' queue with
* supplied junk. When used, they are transferred back to the
* 'empty' queue.
*/
#define RANDOM_RING_MAX 1024
#define RANDOM_ACCUM_MAX 8
/* 1 to let the kernel thread run, 0 to terminate, -1 to mark completion */
volatile int random_kthread_control;
/* Allow the sysadmin to select the broad category of
* entropy types to harvest.
*/
__read_frequently u_int hc_source_mask;
struct random_sources {
CK_LIST_ENTRY(random_sources) rrs_entries;
struct random_source *rrs_source;
};
static CK_LIST_HEAD(sources_head, random_sources) source_list =
CK_LIST_HEAD_INITIALIZER(source_list);
SYSCTL_NODE(_kern_random, OID_AUTO, harvest, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"Entropy Device Parameters");
/*
* Put all the harvest queue context stuff in one place.
* this make is a bit easier to lock and protect.
*/
static struct harvest_context {
/* The harvest mutex protects all of harvest_context and
* the related data.
*/
struct mtx hc_mtx;
/* Round-robin destination cache. */
u_int hc_destination[ENTROPYSOURCE];
/* The context of the kernel thread processing harvested entropy */
struct proc *hc_kthread_proc;
/*
* Lockless ring buffer holding entropy events
* If ring.in == ring.out,
* the buffer is empty.
* If ring.in != ring.out,
* the buffer contains harvested entropy.
* If (ring.in + 1) == ring.out (mod RANDOM_RING_MAX),
* the buffer is full.
*
* NOTE: ring.in points to the last added element,
* and ring.out points to the last consumed element.
*
* The ring.in variable needs locking as there are multiple
* sources to the ring. Only the sources may change ring.in,
* but the consumer may examine it.
*
* The ring.out variable does not need locking as there is
* only one consumer. Only the consumer may change ring.out,
* but the sources may examine it.
*/
struct entropy_ring {
struct harvest_event ring[RANDOM_RING_MAX];
volatile u_int in;
volatile u_int out;
} hc_entropy_ring;
struct fast_entropy_accumulator {
volatile u_int pos;
uint32_t buf[RANDOM_ACCUM_MAX];
} hc_entropy_fast_accumulator;
} harvest_context;
static struct kproc_desc random_proc_kp = {
"rand_harvestq",
random_kthread,
&harvest_context.hc_kthread_proc,
};
/* Pass the given event straight through to Fortuna/Whatever. */
static __inline void
random_harvestq_fast_process_event(struct harvest_event *event)
{
p_random_alg_context->ra_event_processor(event);
explicit_bzero(event, sizeof(*event));
}
static void
random_kthread(void)
{
u_int maxloop, ring_out, i;
/*
* Locking is not needed as this is the only place we modify ring.out, and
* we only examine ring.in without changing it. Both of these are volatile,
* and this is a unique thread.
*/
for (random_kthread_control = 1; random_kthread_control;) {
/* Deal with events, if any. Restrict the number we do in one go. */
maxloop = RANDOM_RING_MAX;
while (harvest_context.hc_entropy_ring.out != harvest_context.hc_entropy_ring.in) {
ring_out = (harvest_context.hc_entropy_ring.out + 1)%RANDOM_RING_MAX;
random_harvestq_fast_process_event(harvest_context.hc_entropy_ring.ring + ring_out);
harvest_context.hc_entropy_ring.out = ring_out;
if (!--maxloop)
break;
}
random_sources_feed();
/* XXX: FIX!! Increase the high-performance data rate? Need some measurements first. */
for (i = 0; i < RANDOM_ACCUM_MAX; i++) {
if (harvest_context.hc_entropy_fast_accumulator.buf[i]) {
random_harvest_direct(harvest_context.hc_entropy_fast_accumulator.buf + i, sizeof(harvest_context.hc_entropy_fast_accumulator.buf[0]), RANDOM_UMA);
harvest_context.hc_entropy_fast_accumulator.buf[i] = 0;
}
}
/* XXX: FIX!! This is a *great* place to pass hardware/live entropy to random(9) */
tsleep_sbt(&harvest_context.hc_kthread_proc, 0, "-",
SBT_1S/RANDOM_KTHREAD_HZ, 0, C_PREL(1));
}
random_kthread_control = -1;
wakeup(&harvest_context.hc_kthread_proc);
kproc_exit(0);
/* NOTREACHED */
}
/* This happens well after SI_SUB_RANDOM */
SYSINIT(random_device_h_proc, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, kproc_start,
&random_proc_kp);
static void
rs_epoch_init(void *dummy __unused)
{
rs_epoch = epoch_alloc("Random Sources", EPOCH_PREEMPT);
epoch_inited = true;
}
SYSINIT(rs_epoch_init, SI_SUB_EPOCH, SI_ORDER_ANY, rs_epoch_init, NULL);
/*
* Run through all fast sources reading entropy for the given
* number of rounds, which should be a multiple of the number
* of entropy accumulation pools in use; it is 32 for Fortuna.
*/
static void
random_sources_feed(void)
{
uint32_t entropy[HARVESTSIZE];
struct epoch_tracker et;
struct random_sources *rrs;
u_int i, n, npools;
bool rse_warm;
rse_warm = epoch_inited;
/*
* Evenly-ish distribute pool population across the second based on how
* frequently random_kthread iterates.
*
* For Fortuna, the math currently works out as such:
*
* 64 bits * 4 pools = 256 bits per iteration
* 256 bits * 10 Hz = 2560 bits per second, 320 B/s
*
*/
npools = howmany(p_random_alg_context->ra_poolcount, RANDOM_KTHREAD_HZ);
/*
* Step over all of live entropy sources, and feed their output
* to the system-wide RNG.
*/
if (rse_warm)
epoch_enter_preempt(rs_epoch, &et);
CK_LIST_FOREACH(rrs, &source_list, rrs_entries) {
for (i = 0; i < npools; i++) {
n = rrs->rrs_source->rs_read(entropy, sizeof(entropy));
KASSERT((n <= sizeof(entropy)), ("%s: rs_read returned too much data (%u > %zu)", __func__, n, sizeof(entropy)));
/*
* Sometimes the HW entropy source doesn't have anything
* ready for us. This isn't necessarily untrustworthy.
* We don't perform any other verification of an entropy
* source (i.e., length is allowed to be anywhere from 1
* to sizeof(entropy), quality is unchecked, etc), so
* don't balk verbosely at slow random sources either.
* There are reports that RDSEED on x86 metal falls
* behind the rate at which we query it, for example.
* But it's still a better entropy source than RDRAND.
*/
if (n == 0)
continue;
random_harvest_direct(entropy, n, rrs->rrs_source->rs_source);
}
}
if (rse_warm)
epoch_exit_preempt(rs_epoch, &et);
explicit_bzero(entropy, sizeof(entropy));
}
/* ARGSUSED */
static int
random_check_uint_harvestmask(SYSCTL_HANDLER_ARGS)
{
static const u_int user_immutable_mask =
(((1 << ENTROPYSOURCE) - 1) & (-1UL << RANDOM_PURE_START)) |
_RANDOM_HARVEST_ETHER_OFF | _RANDOM_HARVEST_UMA_OFF;
int error;
u_int value, orig_value;
orig_value = value = hc_source_mask;
error = sysctl_handle_int(oidp, &value, 0, req);
if (error != 0 || req->newptr == NULL)
return (error);
if (flsl(value) > ENTROPYSOURCE)
return (EINVAL);
/*
* Disallow userspace modification of pure entropy sources.
*/
hc_source_mask = (value & ~user_immutable_mask) |
(orig_value & user_immutable_mask);
return (0);
}
SYSCTL_PROC(_kern_random_harvest, OID_AUTO, mask,
CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, NULL, 0,
random_check_uint_harvestmask, "IU",
"Entropy harvesting mask");
/* ARGSUSED */
static int
random_print_harvestmask(SYSCTL_HANDLER_ARGS)
{
struct sbuf sbuf;
int error, i;
error = sysctl_wire_old_buffer(req, 0);
if (error == 0) {
sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
for (i = ENTROPYSOURCE - 1; i >= 0; i--)
sbuf_cat(&sbuf, (hc_source_mask & (1 << i)) ? "1" : "0");
error = sbuf_finish(&sbuf);
sbuf_delete(&sbuf);
}
return (error);
}
SYSCTL_PROC(_kern_random_harvest, OID_AUTO, mask_bin,
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
random_print_harvestmask, "A",
"Entropy harvesting mask (printable)");
static const char *random_source_descr[ENTROPYSOURCE] = {
[RANDOM_CACHED] = "CACHED",
[RANDOM_ATTACH] = "ATTACH",
[RANDOM_KEYBOARD] = "KEYBOARD",
[RANDOM_MOUSE] = "MOUSE",
[RANDOM_NET_TUN] = "NET_TUN",
[RANDOM_NET_ETHER] = "NET_ETHER",
[RANDOM_NET_NG] = "NET_NG",
[RANDOM_INTERRUPT] = "INTERRUPT",
[RANDOM_SWI] = "SWI",
[RANDOM_FS_ATIME] = "FS_ATIME",
[RANDOM_UMA] = "UMA", /* ENVIRONMENTAL_END */
[RANDOM_PURE_OCTEON] = "PURE_OCTEON", /* PURE_START */
[RANDOM_PURE_SAFE] = "PURE_SAFE",
[RANDOM_PURE_GLXSB] = "PURE_GLXSB",
[RANDOM_PURE_HIFN] = "PURE_HIFN",
[RANDOM_PURE_RDRAND] = "PURE_RDRAND",
[RANDOM_PURE_NEHEMIAH] = "PURE_NEHEMIAH",
[RANDOM_PURE_RNDTEST] = "PURE_RNDTEST",
[RANDOM_PURE_VIRTIO] = "PURE_VIRTIO",
[RANDOM_PURE_BROADCOM] = "PURE_BROADCOM",
[RANDOM_PURE_CCP] = "PURE_CCP",
[RANDOM_PURE_DARN] = "PURE_DARN",
[RANDOM_PURE_TPM] = "PURE_TPM",
[RANDOM_PURE_VMGENID] = "VMGENID",
/* "ENTROPYSOURCE" */
};
/* ARGSUSED */
static int
random_print_harvestmask_symbolic(SYSCTL_HANDLER_ARGS)
{
struct sbuf sbuf;
int error, i;
bool first;
first = true;
error = sysctl_wire_old_buffer(req, 0);
if (error == 0) {
sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
for (i = ENTROPYSOURCE - 1; i >= 0; i--) {
if (i >= RANDOM_PURE_START &&
(hc_source_mask & (1 << i)) == 0)
continue;
if (!first)
sbuf_cat(&sbuf, ",");
sbuf_cat(&sbuf, !(hc_source_mask & (1 << i)) ? "[" : "");
sbuf_cat(&sbuf, random_source_descr[i]);
sbuf_cat(&sbuf, !(hc_source_mask & (1 << i)) ? "]" : "");
first = false;
}
error = sbuf_finish(&sbuf);
sbuf_delete(&sbuf);
}
return (error);
}
SYSCTL_PROC(_kern_random_harvest, OID_AUTO, mask_symbolic,
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
random_print_harvestmask_symbolic, "A",
"Entropy harvesting mask (symbolic)");
/* ARGSUSED */
static void
random_harvestq_init(void *unused __unused)
{
static const u_int almost_everything_mask =
(((1 << (RANDOM_ENVIRONMENTAL_END + 1)) - 1) &
~_RANDOM_HARVEST_ETHER_OFF & ~_RANDOM_HARVEST_UMA_OFF);
hc_source_mask = almost_everything_mask;
RANDOM_HARVEST_INIT_LOCK();
harvest_context.hc_entropy_ring.in = harvest_context.hc_entropy_ring.out = 0;
}
SYSINIT(random_device_h_init, SI_SUB_RANDOM, SI_ORDER_THIRD, random_harvestq_init, NULL);
/*
* Subroutine to slice up a contiguous chunk of 'entropy' and feed it into the
* underlying algorithm. Returns number of bytes actually fed into underlying
* algorithm.
*/
static size_t
random_early_prime(char *entropy, size_t len)
{
struct harvest_event event;
size_t i;
len = rounddown(len, sizeof(event.he_entropy));
if (len == 0)
return (0);
for (i = 0; i < len; i += sizeof(event.he_entropy)) {
event.he_somecounter = (uint32_t)get_cyclecount();
event.he_size = sizeof(event.he_entropy);
event.he_source = RANDOM_CACHED;
event.he_destination =
harvest_context.hc_destination[RANDOM_CACHED]++;
memcpy(event.he_entropy, entropy + i, sizeof(event.he_entropy));
random_harvestq_fast_process_event(&event);
}
explicit_bzero(entropy, len);
return (len);
}
/*
* Subroutine to search for known loader-loaded files in memory and feed them
* into the underlying algorithm early in boot. Returns the number of bytes
* loaded (zero if none were loaded).
*/
static size_t
random_prime_loader_file(const char *type)
{
uint8_t *keyfile, *data;
size_t size;
keyfile = preload_search_by_type(type);
if (keyfile == NULL)
return (0);
data = preload_fetch_addr(keyfile);
size = preload_fetch_size(keyfile);
if (data == NULL)
return (0);
return (random_early_prime(data, size));
}
/*
* This is used to prime the RNG by grabbing any early random stuff
* known to the kernel, and inserting it directly into the hashing
* module, currently Fortuna.
*/
/* ARGSUSED */
static void
random_harvestq_prime(void *unused __unused)
{
size_t size;
/*
* Get entropy that may have been preloaded by loader(8)
* and use it to pre-charge the entropy harvest queue.
*/
size = random_prime_loader_file(RANDOM_CACHED_BOOT_ENTROPY_MODULE);
if (bootverbose) {
if (size > 0)
printf("random: read %zu bytes from preloaded cache\n",
size);
else
printf("random: no preloaded entropy cache\n");
}
+ size = random_prime_loader_file(RANDOM_PLATFORM_BOOT_ENTROPY_MODULE);
+ if (bootverbose) {
+ if (size > 0)
+ printf("random: read %zu bytes from platform bootloader\n",
+ size);
+ else
+ printf("random: no platform bootloader entropy\n");
+ }
}
SYSINIT(random_device_prime, SI_SUB_RANDOM, SI_ORDER_MIDDLE, random_harvestq_prime, NULL);
/* ARGSUSED */
static void
random_harvestq_deinit(void *unused __unused)
{
/* Command the hash/reseed thread to end and wait for it to finish */
random_kthread_control = 0;
while (random_kthread_control >= 0)
tsleep(&harvest_context.hc_kthread_proc, 0, "harvqterm", hz/5);
}
SYSUNINIT(random_device_h_init, SI_SUB_RANDOM, SI_ORDER_THIRD, random_harvestq_deinit, NULL);
/*-
* Entropy harvesting queue routine.
*
* This is supposed to be fast; do not do anything slow in here!
* It is also illegal (and morally reprehensible) to insert any
* high-rate data here. "High-rate" is defined as a data source
* that will usually cause lots of failures of the "Lockless read"
* check a few lines below. This includes the "always-on" sources
* like the Intel "rdrand" or the VIA Nehamiah "xstore" sources.
*/
/* XXXRW: get_cyclecount() is cheap on most modern hardware, where cycle
* counters are built in, but on older hardware it will do a real time clock
* read which can be quite expensive.
*/
void
random_harvest_queue_(const void *entropy, u_int size, enum random_entropy_source origin)
{
struct harvest_event *event;
u_int ring_in;
KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE, ("%s: origin %d invalid\n", __func__, origin));
RANDOM_HARVEST_LOCK();
ring_in = (harvest_context.hc_entropy_ring.in + 1)%RANDOM_RING_MAX;
if (ring_in != harvest_context.hc_entropy_ring.out) {
/* The ring is not full */
event = harvest_context.hc_entropy_ring.ring + ring_in;
event->he_somecounter = (uint32_t)get_cyclecount();
event->he_source = origin;
event->he_destination = harvest_context.hc_destination[origin]++;
if (size <= sizeof(event->he_entropy)) {
event->he_size = size;
memcpy(event->he_entropy, entropy, size);
}
else {
/* Big event, so squash it */
event->he_size = sizeof(event->he_entropy[0]);
event->he_entropy[0] = jenkins_hash(entropy, size, (uint32_t)(uintptr_t)event);
}
harvest_context.hc_entropy_ring.in = ring_in;
}
RANDOM_HARVEST_UNLOCK();
}
/*-
* Entropy harvesting fast routine.
*
* This is supposed to be very fast; do not do anything slow in here!
* This is the right place for high-rate harvested data.
*/
void
random_harvest_fast_(const void *entropy, u_int size)
{
u_int pos;
pos = harvest_context.hc_entropy_fast_accumulator.pos;
harvest_context.hc_entropy_fast_accumulator.buf[pos] ^= jenkins_hash(entropy, size, (uint32_t)get_cyclecount());
harvest_context.hc_entropy_fast_accumulator.pos = (pos + 1)%RANDOM_ACCUM_MAX;
}
/*-
* Entropy harvesting direct routine.
*
* This is not supposed to be fast, but will only be used during
* (e.g.) booting when initial entropy is being gathered.
*/
void
random_harvest_direct_(const void *entropy, u_int size, enum random_entropy_source origin)
{
struct harvest_event event;
KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE, ("%s: origin %d invalid\n", __func__, origin));
size = MIN(size, sizeof(event.he_entropy));
event.he_somecounter = (uint32_t)get_cyclecount();
event.he_size = size;
event.he_source = origin;
event.he_destination = harvest_context.hc_destination[origin]++;
memcpy(event.he_entropy, entropy, size);
random_harvestq_fast_process_event(&event);
}
void
random_harvest_register_source(enum random_entropy_source source)
{
hc_source_mask |= (1 << source);
}
void
random_harvest_deregister_source(enum random_entropy_source source)
{
hc_source_mask &= ~(1 << source);
}
void
random_source_register(struct random_source *rsource)
{
struct random_sources *rrs;
KASSERT(rsource != NULL, ("invalid input to %s", __func__));
rrs = malloc(sizeof(*rrs), M_ENTROPY, M_WAITOK);
rrs->rrs_source = rsource;
random_harvest_register_source(rsource->rs_source);
printf("random: registering fast source %s\n", rsource->rs_ident);
RANDOM_HARVEST_LOCK();
CK_LIST_INSERT_HEAD(&source_list, rrs, rrs_entries);
RANDOM_HARVEST_UNLOCK();
}
void
random_source_deregister(struct random_source *rsource)
{
struct random_sources *rrs = NULL;
KASSERT(rsource != NULL, ("invalid input to %s", __func__));
random_harvest_deregister_source(rsource->rs_source);
RANDOM_HARVEST_LOCK();
CK_LIST_FOREACH(rrs, &source_list, rrs_entries)
if (rrs->rrs_source == rsource) {
CK_LIST_REMOVE(rrs, rrs_entries);
break;
}
RANDOM_HARVEST_UNLOCK();
if (rrs != NULL && epoch_inited)
epoch_wait_preempt(rs_epoch);
free(rrs, M_ENTROPY);
}
static int
random_source_handler(SYSCTL_HANDLER_ARGS)
{
struct epoch_tracker et;
struct random_sources *rrs;
struct sbuf sbuf;
int error, count;
error = sysctl_wire_old_buffer(req, 0);
if (error != 0)
return (error);
sbuf_new_for_sysctl(&sbuf, NULL, 64, req);
count = 0;
epoch_enter_preempt(rs_epoch, &et);
CK_LIST_FOREACH(rrs, &source_list, rrs_entries) {
sbuf_cat(&sbuf, (count++ ? ",'" : "'"));
sbuf_cat(&sbuf, rrs->rrs_source->rs_ident);
sbuf_cat(&sbuf, "'");
}
epoch_exit_preempt(rs_epoch, &et);
error = sbuf_finish(&sbuf);
sbuf_delete(&sbuf);
return (error);
}
SYSCTL_PROC(_kern_random, OID_AUTO, random_sources, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
NULL, 0, random_source_handler, "A",
"List of active fast entropy sources.");
MODULE_VERSION(random_harvestq, 1);
diff --git a/sys/sys/random.h b/sys/sys/random.h
index cfcf4b30e69..02cb409cd99 100644
--- a/sys/sys/random.h
+++ b/sys/sys/random.h
@@ -1,167 +1,168 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2000-2015, 2017 Mark R. V. Murray
* 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
* in this position and unchanged.
* 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 AUTHOR ``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 AUTHOR 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.
*
* $FreeBSD$
*/
#ifndef _SYS_RANDOM_H_
#define _SYS_RANDOM_H_
#include <sys/types.h>
#ifdef _KERNEL
struct uio;
/*
* In the loadable random world, there are set of dangling pointers left in the
* core kernel:
* * read_random, read_random_uio, is_random_seeded are function pointers,
* rather than functions.
* * p_random_alg_context is a true pointer in loadable random kernels.
*
* These are initialized at SI_SUB_RANDOM:SI_ORDER_SECOND during boot. The
* read-type pointers are initialized by random_alg_context_init() in
* randomdev.c and p_random_alg_context in the algorithm, e.g., fortuna.c's
* random_fortuna_init_alg(). The nice thing about function pointers is they
* have a similar calling convention to ordinary functions.
*
* (In !loadable, the read_random, etc, routines are just plain functions;
* p_random_alg_context is a macro for the public visibility
* &random_alg_context.)
*/
#if defined(RANDOM_LOADABLE)
extern void (*_read_random)(void *, u_int);
extern int (*_read_random_uio)(struct uio *, bool);
extern bool (*_is_random_seeded)(void);
#define read_random(a, b) (*_read_random)(a, b)
#define read_random_uio(a, b) (*_read_random_uio)(a, b)
#define is_random_seeded() (*_is_random_seeded)()
#else
void read_random(void *, u_int);
int read_random_uio(struct uio *, bool);
bool is_random_seeded(void);
#endif
/*
* Note: if you add or remove members of random_entropy_source, remember to
* also update the strings in the static array random_source_descr[] in
* random_harvestq.c.
*/
enum random_entropy_source {
RANDOM_START = 0,
RANDOM_CACHED = 0,
/* Environmental sources */
RANDOM_ATTACH,
RANDOM_KEYBOARD,
RANDOM_MOUSE,
RANDOM_NET_TUN,
RANDOM_NET_ETHER,
RANDOM_NET_NG,
RANDOM_INTERRUPT,
RANDOM_SWI,
RANDOM_FS_ATIME,
RANDOM_UMA, /* Special!! UMA/SLAB Allocator */
RANDOM_ENVIRONMENTAL_END = RANDOM_UMA,
/* Fast hardware random-number sources from here on. */
RANDOM_PURE_START,
RANDOM_PURE_OCTEON = RANDOM_PURE_START,
RANDOM_PURE_SAFE,
RANDOM_PURE_GLXSB,
RANDOM_PURE_HIFN,
RANDOM_PURE_RDRAND,
RANDOM_PURE_NEHEMIAH,
RANDOM_PURE_RNDTEST,
RANDOM_PURE_VIRTIO,
RANDOM_PURE_BROADCOM,
RANDOM_PURE_CCP,
RANDOM_PURE_DARN,
RANDOM_PURE_TPM,
RANDOM_PURE_VMGENID,
RANDOM_PURE_QUALCOMM,
ENTROPYSOURCE
};
_Static_assert(ENTROPYSOURCE <= 32,
"hardcoded assumption that values fit in a typical word-sized bitset");
#define RANDOM_CACHED_BOOT_ENTROPY_MODULE "boot_entropy_cache"
+#define RANDOM_PLATFORM_BOOT_ENTROPY_MODULE "boot_entropy_platform"
extern u_int hc_source_mask;
void random_harvest_queue_(const void *, u_int, enum random_entropy_source);
void random_harvest_fast_(const void *, u_int);
void random_harvest_direct_(const void *, u_int, enum random_entropy_source);
static __inline void
random_harvest_queue(const void *entropy, u_int size, enum random_entropy_source origin)
{
if (hc_source_mask & (1 << origin))
random_harvest_queue_(entropy, size, origin);
}
static __inline void
random_harvest_fast(const void *entropy, u_int size, enum random_entropy_source origin)
{
if (hc_source_mask & (1 << origin))
random_harvest_fast_(entropy, size);
}
static __inline void
random_harvest_direct(const void *entropy, u_int size, enum random_entropy_source origin)
{
if (hc_source_mask & (1 << origin))
random_harvest_direct_(entropy, size, origin);
}
void random_harvest_register_source(enum random_entropy_source);
void random_harvest_deregister_source(enum random_entropy_source);
#if defined(RANDOM_ENABLE_UMA)
#define random_harvest_fast_uma(a, b, c) random_harvest_fast(a, b, c)
#else /* !defined(RANDOM_ENABLE_UMA) */
#define random_harvest_fast_uma(a, b, c) do {} while (0)
#endif /* defined(RANDOM_ENABLE_UMA) */
#if defined(RANDOM_ENABLE_ETHER)
#define random_harvest_queue_ether(a, b) random_harvest_queue(a, b, RANDOM_NET_ETHER)
#else /* !defined(RANDOM_ENABLE_ETHER) */
#define random_harvest_queue_ether(a, b) do {} while (0)
#endif /* defined(RANDOM_ENABLE_ETHER) */
#endif /* _KERNEL */
#define GRND_NONBLOCK 0x1
#define GRND_RANDOM 0x2
#define GRND_INSECURE 0x4
__BEGIN_DECLS
ssize_t getrandom(void *buf, size_t buflen, unsigned int flags);
__END_DECLS
#endif /* _SYS_RANDOM_H_ */
--
2.30.0

File Metadata

Mime Type
text/plain; charset=utf-8
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4462757
Default Alt Text
D20780 format-patch (99 KB)

Event Timeline