Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 8 Jun 2019 19:02:17 +0000 (UTC)
From:      Warner Losh <imp@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r348812 - in head/stand/efi: . gptboot
Message-ID:  <201906081902.x58J2HDp088164@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: imp
Date: Sat Jun  8 19:02:17 2019
New Revision: 348812
URL: https://svnweb.freebsd.org/changeset/base/348812

Log:
  Create gptboot.efi
  
  This is a primary boot loader that is intended to implement the
  gptboot partition selection algorithm just like we did for BIOS
  booting. While the preferred method for UEFI is to use the UEFI Boot
  Manager protocol, there are situations where that can't be done: some
  BIOS makers interfere with the protocol in unhelpful ways, there's a
  new standard for a zero variable write from the client OS, and finally
  for USB drives that might be mobile between systems with multiple
  partitions there needs to be a media stable way to select.
  
  Reviewed by: tsoome, bcran
  Differential Revision: https://reviews.freebsd.org/D20547

Added:
  head/stand/efi/gptboot/
  head/stand/efi/gptboot/Makefile   (contents, props changed)
  head/stand/efi/gptboot/drv.h   (contents, props changed)
  head/stand/efi/gptboot/proto.c   (contents, props changed)
Modified:
  head/stand/efi/Makefile

Modified: head/stand/efi/Makefile
==============================================================================
--- head/stand/efi/Makefile	Sat Jun  8 18:59:50 2019	(r348811)
+++ head/stand/efi/Makefile	Sat Jun  8 19:02:17 2019	(r348812)
@@ -9,7 +9,7 @@ NO_OBJ=t
 .if ${COMPILER_TYPE} != "gcc" || ${COMPILER_VERSION} >= 40500
 
 SUBDIR.${MK_FDT}+=	fdt
-SUBDIR.yes+=	libefi boot1
+SUBDIR.yes+=	libefi boot1 gptboot
 SUBDIR.${MK_FORTH}+= loader_4th
 SUBDIR.${MK_LOADER_LUA}+= loader_lua
 SUBDIR.yes+=	loader_simp

Added: head/stand/efi/gptboot/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/stand/efi/gptboot/Makefile	Sat Jun  8 19:02:17 2019	(r348812)
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+# ZFS is not supported, we want debugging until this is vetted and
+# we don't want the gptboot.efifat thing created.
+MK_LOADER_ZFS=no
+EFI_DEBUG=yes
+NOFAT=yes
+
+BOOT1?=		gptboot
+.PATH:		${SRCTOP}/stand/efi/boot1 ${SRCTOP}/stand/libsa
+CFLAGS+=	-I${SRCTOP}/stand/efi/boot1
+CFLAGS+=	-I${.CURDIR}
+CFLAGS+=	-DBOOTPROG=\"gptboot.efi\"
+SRCS+=		gpt.c
+CWARNFLAGS.gpt.c+=	-Wno-sign-compare -Wno-cast-align
+WARNS=6
+.include "${.CURDIR}/../boot1/Makefile"

Added: head/stand/efi/gptboot/drv.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/stand/efi/gptboot/drv.h	Sat Jun  8 19:02:17 2019	(r348812)
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2019 Netflix, Inc
+ *
+ * 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$
+ */
+
+#ifndef _DRV_H_
+#define _DRV_H_
+
+struct dsk {
+	int part;
+	daddr_t start;
+	void *devinfo;		/* Really a dev_into_t *, but that's not in scope */
+};
+
+int drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk);
+int drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk);
+uint64_t drvsize(struct dsk *dskp);
+
+#endif	/* !_DRV_H_ */

Added: head/stand/efi/gptboot/proto.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/stand/efi/gptboot/proto.c	Sat Jun  8 19:02:17 2019	(r348812)
@@ -0,0 +1,279 @@
+/*-
+ * Copyright (c) 2019 Netflix, Inc
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <machine/elf.h>
+#include <machine/stdarg.h>
+#include <stand.h>
+
+#include <efi.h>
+#include <eficonsctl.h>
+#include <efichar.h>
+
+#include "boot_module.h"
+#include "paths.h"
+#include "proto.h"
+
+#include "gpt.h"
+#include <sys/gpt.h>
+static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS;
+static char secbuf[4096];
+static struct dsk dsk;
+static dev_info_t *devices = NULL;
+static dev_info_t *raw_device = NULL;
+
+static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
+static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
+
+/*
+ * Shim routine for the gpt code to read in the gpt table. The
+ * devinfo is always going to be for the raw device.
+ */
+int
+drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk)
+{
+	int size;
+	EFI_STATUS status;
+	dev_info_t *devinfo = (dev_info_t *)dskp->devinfo;
+	EFI_BLOCK_IO *dev = devinfo->dev;
+
+	lba = lba / (dev->Media->BlockSize / DEV_BSIZE);
+	size = nblk * DEV_BSIZE;
+
+	status = dev->ReadBlocks(dev, dev->Media->MediaId, lba, size, buf);
+	if (status != EFI_SUCCESS) {
+		DPRINTF("dskread: failed dev: %p, id: %u, lba: %ju, size: %d, "
+		    "status: %lu\n", devinfo->dev,
+		    dev->Media->MediaId, (uintmax_t)lba, size,
+		    EFI_ERROR_CODE(status));
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*
+ * Shim routine for the gpt code to write in the gpt table. The
+ * devinfo is always going to be for the raw device.
+ */
+int
+drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk)
+{
+	int size;
+	EFI_STATUS status;
+	dev_info_t *devinfo = (dev_info_t *)dskp->devinfo;
+	EFI_BLOCK_IO *dev = devinfo->dev;
+
+	if (dev->Media->ReadOnly)
+		return -1;
+
+	lba = lba / (dev->Media->BlockSize / DEV_BSIZE);
+	size = nblk * DEV_BSIZE;
+
+	status = dev->WriteBlocks(dev, dev->Media->MediaId, lba, size, buf);
+	if (status != EFI_SUCCESS) {
+		DPRINTF("dskread: failed dev: %p, id: %u, lba: %ju, size: %d, "
+		    "status: %lu\n", devinfo->dev,
+		    dev->Media->MediaId, (uintmax_t)lba, size,
+		    EFI_ERROR_CODE(status));
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*
+ * Return the number of LBAs the drive has.
+ */
+uint64_t
+drvsize(struct dsk *dskp)
+{
+	dev_info_t *devinfo = (dev_info_t *)dskp->devinfo;
+	EFI_BLOCK_IO *dev = devinfo->dev;
+
+	return (dev->Media->LastBlock + 1);
+}
+
+static int
+partition_number(EFI_DEVICE_PATH *devpath)
+{
+	EFI_DEVICE_PATH *md;
+	HARDDRIVE_DEVICE_PATH *hd;
+
+	md = efi_devpath_last_node(devpath);
+	if (md == NULL)
+		return (-1);
+	if (DevicePathSubType(md) != MEDIA_HARDDRIVE_DP)
+		return (-1);
+	hd = (HARDDRIVE_DEVICE_PATH *)md;
+	return (hd->PartitionNumber);
+}
+
+/*
+ * Find the raw partition for the imgpath and save it
+ */
+static void
+probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath)
+{
+	dev_info_t *devinfo;
+	EFI_BLOCK_IO *blkio;
+	EFI_DEVICE_PATH *devpath, *trimmed = NULL;
+	EFI_STATUS status;
+
+	/* Figure out if we're dealing with an actual partition. */
+	status = BS->HandleProtocol(h, &DevicePathGUID, (void **)&devpath);
+	if (status != EFI_SUCCESS)
+		return;
+#ifdef EFI_DEBUG
+	{
+		CHAR16 *text = efi_devpath_name(devpath);
+		DPRINTF("probing: %S ", text);
+		efi_free_devpath_name(text);
+	}
+#endif
+	/*
+	 * The RAW device is the same as the imgpath with the last
+	 * MEDIA_DEVICE bit trimmed off. imgpath will end with the
+	 * MEDIA_DEVICE for the ESP we booted off of.
+	 */
+	if (!efi_devpath_same_disk(imgpath, devpath)) {
+		trimmed = efi_devpath_trim(imgpath);
+		if (!efi_devpath_match(trimmed, devpath)) {
+			free(trimmed);
+			DPRINTF("Not the same disk\n");
+			return;
+		}
+	}
+	status = BS->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio);
+	if (status != EFI_SUCCESS) {
+		DPRINTF("Can't get the block I/O protocol block\n");
+		return;
+	}
+	devinfo = malloc(sizeof(*devinfo));
+	if (devinfo == NULL) {
+		DPRINTF("Failed to allocate devinfo\n");
+		return;
+	}
+	devinfo->dev = blkio;
+	devinfo->devpath = devpath;
+	devinfo->devhandle = h;
+	devinfo->preferred = 1;
+	devinfo->next = NULL;
+	devinfo->devdata = NULL;
+	if (trimmed == NULL) {
+		DPRINTF("Found partition %d\n", partition_number(devpath));
+		add_device(&devices, devinfo);
+	} else {
+		free(trimmed);
+		DPRINTF("Found raw device\n");
+		if (raw_device) {
+			printf(BOOTPROG": Found two raw devices, inconceivable?\n");
+			return;
+		}
+		raw_device = devinfo;
+	}
+}
+
+static void
+probe_handles(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath)
+{
+	UINTN i;
+
+	for (i = 0; i < nhandles; i++)
+		probe_handle(handles[i], imgpath);
+}
+
+static dev_info_t *
+find_partition(int part)
+{
+	dev_info_t *dev;
+
+	if (part == 0)
+		return (NULL);
+	for (dev = devices; dev != NULL; dev = dev->next)
+		if (partition_number(dev->devpath) == part)
+			break;
+	return (dev);
+}
+
+void
+choice_protocol(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath)
+{
+	const boot_module_t *mod = &ufs_module;
+	dev_info_t *bootdev;
+	void *loaderbuf;
+	size_t loadersize;
+	int parts;
+	const char *fn = PATH_LOADER_EFI;
+
+	/*
+	 * Probe the provided handles to find the partitions that
+	 * are on the same drive.
+	 */
+	probe_handles(handles, nhandles, imgpath);
+	dsk.devinfo = raw_device;
+	if (dsk.devinfo == NULL) {
+		printf(BOOTPROG": unable to find raw disk to read gpt\n");
+		return;
+	}
+
+	/*
+	 * Read in the GPT table, and then find the right partition.
+	 * gptread, gptfind and gptfaileboot are shared with the
+	 * BIOS version of the gptboot program.
+	 */
+	if (gptread(&dsk, secbuf) == -1) {
+		printf(BOOTPROG ": unable to load GPT\n");
+		return;
+	}
+	// XXX:
+	// real gptboot can parse a command line before trying this loop.
+	// But since we don't parse anything at all, hard wire the partition
+	// to be -1 (meaning look for the next one).
+	parts = 0;
+	while (gptfind(&freebsd_ufs_uuid, &dsk, -1) != -1) {
+		parts++;
+		bootdev = find_partition(dsk.part);
+		if (bootdev == NULL) {
+			printf(BOOTPROG": Can't find partition %d\n",
+			    dsk.part);
+			goto next;
+		}
+		if (mod->load(fn, bootdev, &loaderbuf, &loadersize) !=
+		    EFI_SUCCESS) {
+			printf(BOOTPROG": Can't load %s from partition %d\n",
+			    fn, dsk.part);
+			goto next;
+		}
+		try_boot(mod, bootdev, loaderbuf, loadersize);
+next:
+		gptbootfailed(&dsk);
+	}
+	if (parts == 0)
+		printf("%s: no UFS partition was found\n", BOOTPROG);
+}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201906081902.x58J2HDp088164>