From 246dd4e9fa31de32278dcd1e79ff681a336abb32 Mon Sep 17 00:00:00 2001
From: Sascha Wildner <saw@online.de>
Date: Sun, 13 Nov 2016 22:10:02 +0100
Subject: [PATCH 10/10] WIP efidev, libefivar, efivar, uefisign

---
 lib/Makefile                     |   1 +
 lib/libefivar/Makefile           |  51 ++++
 lib/libefivar/efivar.3           | 100 +++++++
 lib/libefivar/efivar.c           | 380 ++++++++++++++++++++++++
 lib/libefivar/efivar.h           | 121 ++++++++
 lib/libefivar/libefivar.c        | 188 ++++++++++++
 lib/libefivar/libefivar_int.h    |  35 +++
 share/mk/bsd.libnames.mk         |   1 +
 sys/dev/misc/efidev/Makefile     |   9 +
 sys/dev/misc/efidev/efidev.c     | 208 +++++++++++++
 sys/platform/pc64/include/efi.h  |  59 ++++
 sys/platform/pc64/include/pmap.h |  34 +++
 sys/platform/pc64/x86_64/efirt.c | 611 +++++++++++++++++++++++++++++++++++++++
 sys/platform/pc64/x86_64/pmap.c  |  11 -
 sys/sys/efiio.h                  |  59 ++++
 usr.sbin/Makefile                |   2 +
 usr.sbin/efivar/Makefile         |   9 +
 usr.sbin/efivar/efivar.8         | 164 +++++++++++
 usr.sbin/efivar/efivar.c         | 351 ++++++++++++++++++++++
 usr.sbin/uefisign/Makefile       |  11 +
 usr.sbin/uefisign/child.c        | 267 +++++++++++++++++
 usr.sbin/uefisign/magic.h        |  66 +++++
 usr.sbin/uefisign/pe.c           | 564 ++++++++++++++++++++++++++++++++++++
 usr.sbin/uefisign/uefisign.8     |  93 ++++++
 usr.sbin/uefisign/uefisign.c     | 425 +++++++++++++++++++++++++++
 usr.sbin/uefisign/uefisign.h     |  91 ++++++
 26 files changed, 3900 insertions(+), 11 deletions(-)
 create mode 100644 lib/libefivar/Makefile
 create mode 100644 lib/libefivar/efivar.3
 create mode 100644 lib/libefivar/efivar.c
 create mode 100644 lib/libefivar/efivar.h
 create mode 100644 lib/libefivar/libefivar.c
 create mode 100644 lib/libefivar/libefivar_int.h
 create mode 100644 sys/dev/misc/efidev/Makefile
 create mode 100644 sys/dev/misc/efidev/efidev.c
 create mode 100644 sys/platform/pc64/include/efi.h
 create mode 100644 sys/platform/pc64/x86_64/efirt.c
 create mode 100644 sys/sys/efiio.h
 create mode 100644 usr.sbin/efivar/Makefile
 create mode 100644 usr.sbin/efivar/efivar.8
 create mode 100644 usr.sbin/efivar/efivar.c
 create mode 100644 usr.sbin/uefisign/Makefile
 create mode 100644 usr.sbin/uefisign/child.c
 create mode 100644 usr.sbin/uefisign/magic.h
 create mode 100644 usr.sbin/uefisign/pe.c
 create mode 100644 usr.sbin/uefisign/uefisign.8
 create mode 100644 usr.sbin/uefisign/uefisign.c
 create mode 100644 usr.sbin/uefisign/uefisign.h

diff --git a/lib/Makefile b/lib/Makefile
index 1b09eed..3976ebd 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -67,6 +67,7 @@ SUBDIR=	${SUBDIR_ORDERED} \
 	libdl \
 	libdm \
 	${_libdmsg} \
+	libefivar \
 	libevtr \
 	libexpat \
 	libfsid \
diff --git a/lib/libefivar/Makefile b/lib/libefivar/Makefile
new file mode 100644
index 0000000..840ccdf
--- /dev/null
+++ b/lib/libefivar/Makefile
@@ -0,0 +1,51 @@
+# Copyright 1998 Juniper Networks, Inc.
+# 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: head/lib/libefivar/Makefile 307071 2016-10-11 22:30:41Z imp $
+
+PACKAGE=lib${LIB}
+LIB=		efivar
+SRCS=		efivar.c libefivar.c
+INCS=		efivar.h
+SHLIB_MAJOR=	1
+MAN=		efivar.3
+
+MLINKS+=efivar.3 efi_set_variables_supported.3 \
+	efivar.3 efi_del_variable.3 \
+	efivar.3 efi_get_variable.3 \
+	efivar.3 efi_get_variable_attributes.3 \
+	efivar.3 efi_get_variable_size.3 \
+	efivar.3 efi_append_variable.3 \
+	efivar.3 efi_set_variable.3 \
+	efivar.3 efi_get_next_variable_name.3 \
+	efivar.3 efi_str_to_guid.3 \
+	efivar.3 efi_guid_to_str.3 \
+	efivar.3 efi_name_to_guid.3 \
+	efivar.3 efi_guid_to_name.3 \
+	efivar.3 efi_guid_to_symbol.3 \
+	efivar.3 libefivar.3
+
+WARNS?=		6
+
+.include <bsd.lib.mk>
diff --git a/lib/libefivar/efivar.3 b/lib/libefivar/efivar.3
new file mode 100644
index 0000000..2bcb7fb
--- /dev/null
+++ b/lib/libefivar/efivar.3
@@ -0,0 +1,100 @@
+.\" Copyright 2016 Netflix, Inc.
+.\" 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: head/lib/libefivar/efivar.3 307071 2016-10-11 22:30:41Z imp $
+.\"
+.Dd September 14, 2016
+.Dt LIBEFIVAR 3
+.Os
+.Sh NAME
+.Nm libefivar
+.Nd EFI Non Volatile Variable Support
+.Sh LIBRARY
+.Lb libefivar
+.Sh SYNOPSIS
+.In efivar.h
+.Ft int
+.Fn efi_append_variable "efi_guid_t guid" "const char *name" "void *data" "size_t data_size" "uint32_t attributes"
+.Ft int
+.Fn efi_del_variable "efi_guid_t guid" "const char *name"
+.Ft int
+.Fn efi_get_variable "efi_guid_t guid" "const char *name" "void **data" "ssize_t *data_size" "uint32_t *attributes"
+.Ft int
+.Fn efi_get_variable_attributes "efi_guid_t guid" "const char *name" "uint32_t *attributes"
+.Ft int
+.Fn efi_get_variable_size "efi_guid_t guid" "const char *name" "size_t *size"
+.Ft int
+.Fn efi_get_next_variable_name "efi_guid_t **guid" "char **name"
+.Ft int
+.Fn efi_guid_to_name "efi_guid_t *guid" "char **name"
+.Ft int
+.Fn efi_guid_to_symbol "efi_guid_t *guid" "char **symbol"
+.Ft int
+.Fn efi_guid_to_str "const efi_guid_t *guid" "char **sp"
+.Ft int
+.Fn efi_name_to_guid "const char *name" "efi_guid_t *guid"
+.Ft int
+.Fn efi_set_variable "efi_guid_t guid" "const char *name" "void *data" "size_t data_size" "uint32_t attributes"
+.Ft int
+.Fn efi_str_to_guid "const char *s" "efi_guid_t *guid";
+.Ft int
+.Fn efi_variables_supported "void";
+.Sh DESCRIPTION
+The
+.Nm
+library implements access to EFI Variables via the EFI Runtime
+Serivces.
+All char * strings are converted to 16-bit UTF strings before passing
+them to EFI.
+.Pp
+.Fn efi_variables_supported
+returns non-zero if the current machine supports setting of EFI firmware
+variables and the kernel support for doing so is present.
+Otherwise zero is returned.
+.Pp
+.Fn efi_del_variable
+deletes the EFI variable selected by
+.Dv guid
+and
+.Dv name .
+.Pp
+.Fn efi_get_variable
+.Fn efi_get_variable_attributes
+.Fn efi_get_variable_size
+.Fn efi_append_variable
+.Fn efi_set_variable
+.Fn efi_get_next_variable_name
+.Fn efi_str_to_guid
+.Fn efi_guid_to_str
+.Fn efi_name_to_guid
+.Fn efi_guid_to_name
+.Fn efi_guid_to_symbol
+This function is not actually implemented.
+.Sh BUGS
+No facilities exist to process the strings as native UTF.
+This is a limitation in the Linux libefivar library interface.
+.Sh AUTHORS
+.An -nosplit
+This software was originally written by
+.An Warner Losh .
diff --git a/lib/libefivar/efivar.c b/lib/libefivar/efivar.c
new file mode 100644
index 0000000..cf9240d
--- /dev/null
+++ b/lib/libefivar/efivar.c
@@ -0,0 +1,380 @@
+/*-
+ * Copyright (c) 2016 Netflix, Inc.
+ * 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: head/lib/libefivar/efivar.c 307189 2016-10-13 06:56:23Z imp $");
+
+#include <efivar.h>
+#include <sys/efiio.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libefivar_int.h"
+
+static int efi_fd = -2;
+
+#define Z { 0, 0, 0, 0, 0, { 0 } }
+
+const efi_guid_t efi_guid_empty = Z;
+
+static struct uuid_table
+{
+	const char *uuid_str;
+	const char *name;
+	efi_guid_t guid;
+} guid_tbl [] =
+{
+	{ "00000000-0000-0000-0000-000000000000", "zero", Z },
+	{ "093e0fae-a6c4-4f50-9f1b-d41e2b89c19a", "sha512", Z },
+	{ "0abba7dc-e516-4167-bbf5-4d9d1c739416", "redhat", Z },
+	{ "0b6e5233-a65c-44c9-9407-d9ab83bfc8bd", "sha224", Z },
+	{ "126a762d-5758-4fca-8531-201a7f57f850", "lenovo_boot_menu", Z },
+	{ "3bd2a492-96c0-4079-b420-fcf98ef103ed", "x509_sha256", Z },
+	{ "3c5766e8-269c-4e34-aa14-ed776e85b3b6", "rsa2048", Z },
+	{ "3CC24E96-22C7-41D8-8863-8E39DCDCC2CF", "lenovo", Z },
+	{ "3f7e615b-0d45-4f80-88dc-26b234958560", "lenovo_diag", Z },
+	{ "446dbf63-2502-4cda-bcfa-2465d2b0fe9d", "x509_sha512", Z },
+	{ "4aafd29d-68df-49ee-8aa9-347d375665a7", "pkcs7_cert", Z },
+	{ "605dab50-e046-4300-abb6-3dd810dd8b23", "shim", Z },
+	{ "665d3f60-ad3e-4cad-8e26-db46eee9f1b5", "lenovo_rescue", Z },
+	{ "67f8444f-8743-48f1-a328-1eaab8736080", "rsa2048_sha1", Z },
+	{ "7076876e-80c2-4ee6-aad2-28b349a6865b", "x509_sha384", Z },
+	{ "721c8b66-426c-4e86-8e99-3457c46ab0b9", "lenovo_setup", Z },
+	{ "77fa9abd-0359-4d32-bd60-28f4e78f784b", "microsoft", Z },
+	{ "7FACC7B6-127F-4E9C-9C5D-080F98994345", "lenovo_2", Z },
+	{ "826ca512-cf10-4ac9-b187-be01496631bd", "sha1", Z },
+	{ "82988420-7467-4490-9059-feb448dd1963", "lenovo_me_config", Z },
+	{ "8be4df61-93ca-11d2-aa0d-00e098032b8c", "global", Z },
+	{ "a5c059a1-94e4-4aa7-87b5-ab155c2bf072", "x509_cert", Z },
+	{ "a7717414-c616-4977-9420-844712a735bf", "rsa2048_sha256_cert", Z },
+	{ "a7d8d9a6-6ab0-4aeb-ad9d-163e59a7a380", "lenovo_diag_splash", Z },
+	{ "ade9e48f-9cb8-98e6-31af-b4e6009e2fe3", "redhat_2", Z },
+	{ "bc7838d2-0f82-4d60-8316-c068ee79d25b", "lenovo_msg", Z },
+	{ "c1c41626-504c-4092-aca9-41f936934328", "sha256", Z },
+	{ "c57ad6b7-0515-40a8-9d21-551652854e37", "shell", Z },
+	{ "d719b2cb-3d3a-4596-a3bc-dad00e67656f", "security", Z },
+	{ "e2b36190-879b-4a3d-ad8d-f2e7bba32784", "rsa2048_sha256", Z },
+	{ "ff3e5307-9fd0-48c9-85f1-8ad56c701e01", "sha384", Z },
+	{ "f46ee6f4-4785-43a3-923d-7f786c3c8479", "lenovo_startup_interrupt", Z },
+	{ "ffffffff-ffff-ffff-ffff-ffffffffffff", "zzignore-this-guid", Z },
+};
+#undef Z
+
+static void
+efi_guid_tbl_compile(void)
+{
+	size_t i;
+	uint32_t status;
+
+	for (i = 0; i < nitems(guid_tbl); i++) {
+		uuid_from_string(guid_tbl[i].uuid_str, &guid_tbl[i].guid,
+		    &status);
+		/* all f's is a bad version, so ignore that error */
+		if (status != uuid_s_ok && status != uuid_s_bad_version)
+			fprintf(stderr, "Can't convert %s to a uuid for %s: %d\n",
+			    guid_tbl[i].uuid_str, guid_tbl[i].name, (int)status);
+	}
+}
+
+static int
+efi_open_dev(void)
+{
+
+	if (efi_fd == -2)
+		efi_fd = open("/dev/efi", O_RDWR);
+	if (efi_fd < 0)
+		efi_fd = -1;
+	else
+		efi_guid_tbl_compile();
+	return (efi_fd);
+}
+
+static void
+efi_var_reset(struct efi_var_ioc *var)
+{
+	var->name = NULL;
+	var->namesize = 0;
+	memset(&var->vendor, 0, sizeof(var->vendor));
+	var->attrib = 0;
+	var->data = NULL;
+	var->datasize = 0;
+}
+
+static int
+rv_to_linux_rv(int rv)
+{
+	if (rv == 0)
+		rv = 1;
+	else if (errno == ENOENT) {
+		rv = 0;
+		errno = 0;
+	} else
+		rv = -errno;
+	return (rv);
+}
+
+int
+efi_append_variable(efi_guid_t guid, const char *name,
+    uint8_t *data, size_t data_size, uint32_t attributes)
+{
+
+	return efi_set_variable(guid, name, data, data_size,
+	    attributes | EFI_VARIABLE_APPEND_WRITE, 0);
+}
+
+int
+efi_del_variable(efi_guid_t guid, const char *name)
+{
+
+	/* data_size of 0 deletes the variable */
+	return efi_set_variable(guid, name, NULL, 0, 0, 0);
+}
+
+int
+efi_get_variable(efi_guid_t guid, const char *name,
+    uint8_t **data, size_t *data_size, uint32_t *attributes)
+{
+	struct efi_var_ioc var;
+	int rv;
+	static uint8_t buf[1024*32];
+
+	if (efi_open_dev() == -1)
+		return -1;
+
+	efi_var_reset(&var);
+	rv = libefi_utf8_to_ucs2(name, &var.name, &var.namesize);
+	if (rv != 0)
+		goto errout;
+	var.vendor = guid;
+	var.data = buf;
+	var.datasize = sizeof(buf);
+	rv = ioctl(efi_fd, EFIIOC_VAR_GET, &var);
+	if (data_size != NULL)
+		*data_size = var.datasize;
+	if (data != NULL)
+		*data = buf;
+	if (attributes != NULL)
+		*attributes = var.attrib;
+errout:
+	free(var.name);
+
+	return rv_to_linux_rv(rv);
+}
+
+int
+efi_get_variable_attributes(efi_guid_t guid, const char *name,
+    uint32_t *attributes)
+{
+	/* Make sure this construct works -- I think it will fail */
+
+	return efi_get_variable(guid, name, NULL, NULL, attributes);
+}
+
+int
+efi_get_variable_size(efi_guid_t guid, const char *name,
+    size_t *size)
+{
+
+	/* XXX check to make sure this matches the linux value */
+
+	*size = 0;
+	return efi_get_variable(guid, name, NULL, size, NULL);
+}
+
+int
+efi_get_next_variable_name(efi_guid_t **guid, char **name)
+{
+	struct efi_var_ioc var;
+	int rv;
+	static efi_char *buf;
+	static size_t buflen = 256 * sizeof(efi_char);
+	static efi_guid_t retguid;
+	size_t size;
+
+	if (efi_open_dev() == -1)
+		return -1;
+
+	if (buf == NULL)
+		buf = malloc(buflen);
+
+again:
+	efi_var_reset(&var);
+	var.name = buf;
+	var.namesize = buflen;
+	if (*name == NULL) {
+		*buf = 0;
+		/* GUID zeroed in var_reset */
+	} else {
+		rv = libefi_utf8_to_ucs2(*name, &var.name, &size);
+		if (rv != 0)
+			goto errout;
+		var.vendor = **guid;
+	}
+	rv = ioctl(efi_fd, EFIIOC_VAR_NEXT, &var);
+	if (rv == 0 && var.name == NULL) {
+		/*
+		 * oops, too little space. Try again.
+		 */
+		void *new = realloc(buf, buflen);
+		buflen = var.namesize;
+		if (new == NULL) {
+			rv = -1;
+			errno = ENOMEM;
+			goto done;
+		}
+		buf = new;
+		goto again;
+	}
+
+	if (rv == 0) {
+		*name = NULL; /* XXX */
+		var.name[var.namesize / sizeof(efi_char)] = 0;	/* EFI doesn't NUL terminate */
+		rv = libefi_ucs2_to_utf8(var.name, name);
+		if (rv != 0)
+			goto errout;
+		retguid = var.vendor;
+		*guid = &retguid;
+	}
+errout:
+
+	/* XXX The linux interface expects name to be a static buffer -- fix or leak memory? */
+done:
+	return (rv_to_linux_rv(rv));
+}
+
+int
+efi_guid_cmp(const efi_guid_t *guid1, const efi_guid_t *guid2)
+{
+	uint32_t status;
+
+	return uuid_compare(guid1, guid2, &status);
+}
+
+int
+efi_guid_is_zero(const efi_guid_t *guid)
+{
+	uint32_t status;
+
+	return uuid_is_nil(guid, &status);
+}
+
+int
+efi_guid_to_name(efi_guid_t *guid, char **name)
+{
+	size_t i;
+	uint32_t status;
+
+	for (i = 0; i < nitems(guid_tbl); i++) {
+		if (uuid_equal(guid, &guid_tbl[i].guid, &status)) {
+			*name = strdup(guid_tbl[i].name);
+			return (0);
+		}
+	}
+	return (efi_guid_to_str(guid, name));
+}
+
+int
+efi_guid_to_symbol(efi_guid_t *guid __unused, char **symbol __unused)
+{
+
+	/*
+	 * Unsure what this is used for, efibootmgr doesn't use it.
+	 * Leave unimplemented for now.
+	 */
+	return -1;
+}
+
+int
+efi_guid_to_str(const efi_guid_t *guid, char **sp)
+{
+	uint32_t status;
+
+	/* knows efi_guid_t is a typedef of uuid_t */
+	uuid_to_string(guid, sp, &status);
+
+	return (status == uuid_s_ok ? 0 : -1);
+}
+
+int
+efi_name_to_guid(const char *name, efi_guid_t *guid)
+{
+	size_t i;
+
+	for (i = 0; i < nitems(guid_tbl); i++) {
+		if (strcmp(name, guid_tbl[i].name) == 0) {
+			*guid = guid_tbl[i].guid;
+			return (0);
+		}
+	}
+	return (efi_str_to_guid(name, guid));
+}
+
+int
+efi_set_variable(efi_guid_t guid, const char *name,
+    uint8_t *data, size_t data_size, uint32_t attributes, mode_t mode __unused)
+{
+	struct efi_var_ioc var;
+	int rv;
+
+	if (efi_open_dev() == -1)
+		return -1;
+
+	efi_var_reset(&var);
+	rv = libefi_utf8_to_ucs2(name, &var.name, &var.namesize);
+	if (rv != 0)
+		goto errout;
+	var.vendor = guid;
+	var.data = data;
+	var.datasize = data_size;
+	var.attrib = attributes;
+	rv = ioctl(efi_fd, EFIIOC_VAR_SET, &var);
+errout:
+	free(var.name);
+
+	return rv;
+}
+
+int
+efi_str_to_guid(const char *s, efi_guid_t *guid)
+{
+	uint32_t status;
+
+	/* knows efi_guid_t is a typedef of uuid_t */
+	uuid_from_string(s, guid, &status);
+
+	return (status == uuid_s_ok ? 0 : -1);
+}
+
+int
+efi_variables_supported(void)
+{
+
+	return efi_open_dev() != -1;
+}
diff --git a/lib/libefivar/efivar.h b/lib/libefivar/efivar.h
new file mode 100644
index 0000000..822cd3c
--- /dev/null
+++ b/lib/libefivar/efivar.h
@@ -0,0 +1,121 @@
+/*-
+ * Copyright (c) 2016 Netflix, Inc.
+ * 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: head/lib/libefivar/efivar.h 307071 2016-10-11 22:30:41Z imp $
+ */
+
+#ifndef	_EFIVAR_H_
+#define	_EFIVAR_H_
+
+#include <uuid.h>
+#include <sys/efi.h>
+#include <sys/endian.h>
+#include <stdint.h>
+
+/* Shoud these be elsewhere ? */
+#define	EFI_VARIABLE_NON_VOLATILE		0x00000001
+#define	EFI_VARIABLE_BOOTSERVICE_ACCESS		0x00000002
+#define	EFI_VARIABLE_RUNTIME_ACCESS		0x00000004
+#define	EFI_VARIABLE_HARDWARE_ERROR_RECORD	0x00000008
+#define	EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS	0x00000010
+#define	EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS \
+						0x00000020
+#define	EFI_VARIABLE_APPEND_WRITE		0x00000040
+#if 0 /* todo */
+#define	EFI_VARIABLE_HAS_AUTH_HEADER
+#define EFI_VARIABLE_HAS_SIGNATURE
+#endif
+
+
+typedef uuid_t efi_guid_t;
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define	EFI_GUID(a, b, c, d, e0, e1, e2, e3, e4, e5)			\
+	((efi_guid_t) {(a), (b), (c), (d) >> 8, (d) & 0xff,		\
+	{ (e0), (e1), (e2), (e3), (e4), (e5) }})
+#else
+#define	EFI_GUID(a, b, c, d, e0, e1, e2, e3, e4, e5)			\
+	((efi_guid_t) {(a), (b), (c), (d) & 0xff, (d) >> 8,		\
+	{ (e0), (e1), (e2), (e3), (e4), (e5) }})
+#endif
+
+#define EFI_GLOBAL_GUID EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa0d, \
+    0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
+
+int efi_append_variable(efi_guid_t guid, const char *name,
+    uint8_t *data, size_t data_size, uint32_t attributes);
+int efi_del_variable(efi_guid_t guid, const char *name);
+int efi_get_variable(efi_guid_t guid, const char *name,
+    uint8_t **data, size_t *data_size, uint32_t *attributes);
+int efi_get_variable_attributes(efi_guid_t guid, const char *name,
+    uint32_t *attributes);
+int efi_get_variable_size(efi_guid_t guid, const char *name, size_t *size);
+int efi_get_next_variable_name(efi_guid_t **guid, char **name);
+int efi_guid_cmp(const efi_guid_t *guid1, const efi_guid_t *guid2);
+int efi_guid_is_zero(const efi_guid_t *guid1);
+int efi_guid_to_name(efi_guid_t *guid, char **name);
+int efi_guid_to_symbol(efi_guid_t *guid, char **symbol);
+int efi_guid_to_str(const efi_guid_t *guid, char **sp);
+int efi_name_to_guid(const char *name, efi_guid_t *guid);
+int efi_set_variable(efi_guid_t guid, const char *name,
+    uint8_t *data, size_t data_size, uint32_t attributes, mode_t mode);
+int efi_str_to_guid(const char *s, efi_guid_t *guid);
+int efi_variables_supported(void);
+
+extern const efi_guid_t efi_guid_empty;
+
+/* Stubs that are expected, but aren't really used */
+static inline int
+efi_error_get(unsigned int n __unused, char ** const fn __unused,
+    char ** const func __unused, int *line __unused,
+    char ** const msg __unused, int *err __unused)
+{
+	return 0;
+}
+
+static inline int
+efi_error_set(const char *fn __unused, const char *func __unused,
+    int line __unused, int err __unused, const char *fmt __unused, ...)
+{
+	return 0;
+}
+
+static inline void
+efi_error_clear(void)
+{
+}
+
+static inline int
+efi_error(const char *fmt __unused, ...)
+{
+	return 0;
+}
+
+static inline int
+efi_error_val(int val __unused, const char *fmt __unused, ...)
+{
+	return 0;
+}
+
+#endif /* _EFIVAR_H_ */
diff --git a/lib/libefivar/libefivar.c b/lib/libefivar/libefivar.c
new file mode 100644
index 0000000..e8d167b
--- /dev/null
+++ b/lib/libefivar/libefivar.c
@@ -0,0 +1,188 @@
+/*-
+ * Copyright (c) 2010 Marcel Moolenaar
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/lib/libefivar/libefivar.c 307071 2016-10-11 22:30:41Z imp $");
+
+#include <sys/types.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/efi.h>
+#include <machine/efi.h>
+
+#include "libefivar_int.h"
+
+#include <stdio.h>
+
+/*
+ * If nm were converted to utf8, what what would strlen
+ * return on the resulting string?
+ */
+static size_t
+utf8_len_of_ucs2(const efi_char *nm)
+{
+	size_t len;
+	efi_char c;
+
+	len = 0;
+	while (*nm) {
+		c = *nm++;
+		if (c > 0x7ff)
+			len += 3;
+		else if (c > 0x7f)
+			len += 2;
+		else
+			len++;
+	}
+
+	return (len);
+}
+
+int
+libefi_ucs2_to_utf8(const efi_char *nm, char **name)
+{
+	size_t len, sz;
+	efi_char c;
+	char *cp;
+	int freeit = *name == NULL;
+
+	sz = utf8_len_of_ucs2(nm) + 1;
+	len = 0;
+	if (*name != NULL)
+		cp = *name;
+	else
+		cp = *name = malloc(sz);
+	if (*name == NULL)
+		return (ENOMEM);
+
+	while (*nm) {
+		c = *nm++;
+		if (c > 0x7ff) {
+			if (len++ < sz)
+				*cp++ = (char)(0xE0 | (c >> 12));
+			if (len++ < sz)
+				*cp++ = (char)(0x80 | ((c >> 6) & 0x3f));
+			if (len++ < sz)
+				*cp++ = (char)(0x80 | (c & 0x3f));
+		} else if (c > 0x7f) {
+			if (len++ < sz)
+				*cp++ = (char)(0xC0 | ((c >> 6) & 0x1f));
+			if (len++ < sz)
+				*cp++ = (char)(0x80 | (c & 0x3f));
+		} else {
+			if (len++ < sz)
+				*cp++ = (char)(c & 0x7f);
+		}
+	}
+
+	if (len >= sz) {
+		/* Absent bugs, we'll never return EOVERFLOW */
+		if (freeit)
+			free(*name);
+		return (EOVERFLOW);
+	}
+	*cp++ = '\0';
+
+	return (0);
+}
+
+int
+libefi_utf8_to_ucs2(const char *name, efi_char **nmp, size_t *len)
+{
+	efi_char *nm;
+	size_t sz;
+	uint32_t ucs4;
+	int c, bytes;
+	int freeit = *nmp == NULL;
+
+	sz = strlen(name) * 2 + 2;
+	if (*nmp == NULL)
+		*nmp = malloc(sz);
+	nm = *nmp;
+	*len = sz;
+
+	ucs4 = 0;
+	bytes = 0;
+	while (sz > 1 && *name != '\0') {
+		c = *name++;
+		/*
+		 * Conditionalize on the two major character types:
+		 * initial and followup characters.
+		 */
+		if ((c & 0xc0) != 0x80) {
+			/* Initial characters. */
+			if (bytes != 0) {
+				if (freeit)
+					free(nm);
+				return (EILSEQ);
+			}
+			if ((c & 0xf8) == 0xf0) {
+				ucs4 = c & 0x07;
+				bytes = 3;
+			} else if ((c & 0xf0) == 0xe0) {
+				ucs4 = c & 0x0f;
+				bytes = 2;
+			} else if ((c & 0xe0) == 0xc0) {
+				ucs4 = c & 0x1f;
+				bytes = 1;
+			} else {
+				ucs4 = c & 0x7f;
+				bytes = 0;
+			}
+		} else {
+			/* Followup characters. */
+			if (bytes > 0) {
+				ucs4 = (ucs4 << 6) + (c & 0x3f);
+				bytes--;
+			} else if (bytes == 0) {
+				if (freeit)
+					free(nm);
+				return (EILSEQ);
+			}
+		}
+		if (bytes == 0) {
+			if (ucs4 > 0xffff) {
+				if (freeit)
+					free(nm);
+				return (EILSEQ);
+			}
+			*nm++ = (efi_char)ucs4;
+			sz -= 2;
+		}
+	}
+	if (sz < 2) {
+		if (freeit)
+			free(nm);
+		return (EDOOFUS);
+	}
+	sz -= 2;
+	*nm = 0;
+	*len -= sz;
+	return (0);
+}
diff --git a/lib/libefivar/libefivar_int.h b/lib/libefivar/libefivar_int.h
new file mode 100644
index 0000000..2bebd41
--- /dev/null
+++ b/lib/libefivar/libefivar_int.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2010 Marcel Moolenaar
+ * 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: head/lib/libefivar/libefivar_int.h 307071 2016-10-11 22:30:41Z imp $
+ */
+
+#ifndef _LIBEFI_INT_H_
+#define	_LIBEFI_INT_H_
+
+int libefi_ucs2_to_utf8(const efi_char *, char **);
+int libefi_utf8_to_ucs2(const char *, efi_char **, size_t *);
+
+#endif /* _LIBEFI_INT_H_ */
diff --git a/share/mk/bsd.libnames.mk b/share/mk/bsd.libnames.mk
index 8e7267d..f536c19 100644
--- a/share/mk/bsd.libnames.mk
+++ b/share/mk/bsd.libnames.mk
@@ -30,6 +30,7 @@ LIBDIALOG?=	${DESTDIR}${LIBDIR}/libdialog.a
 LIBDM?=		${DESTDIR}${LIBDIR}/libdm.a
 #LIBDMSG?=	${DESTDIR}${LIBDIR}/libdmsg.a
 LIBEDIT?=	${DESTDIR}${LIBDIR}/priv/libprivate_edit.a
+LIBEFIVAR?=	${DESTDIR}${LIBDIR}/libefivar.a
 LIBEVTR?=	${DESTDIR}${LIBDIR}/libevtr.a
 LIBEXECINFO?=	${DESTDIR}${LIBDIR}/libexecinfo.a
 LIBFETCH?=	${DESTDIR}${LIBDIR}/libfetch.a
diff --git a/sys/dev/misc/efidev/Makefile b/sys/dev/misc/efidev/Makefile
new file mode 100644
index 0000000..32de94f
--- /dev/null
+++ b/sys/dev/misc/efidev/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD: head/sys/modules/efirt/Makefile 307070 2016-10-11 22:24:30Z imp $
+
+.PATH: ${.CURDIR}/../../../platform/${MACHINE_PLATFORM}/${MACHINE_ARCH}
+
+KMOD=	efirt
+SRCS=	efirt.c efidev.c
+SRCS+=  device_if.h bus_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/misc/efidev/efidev.c b/sys/dev/misc/efidev/efidev.c
new file mode 100644
index 0000000..d63a36d
--- /dev/null
+++ b/sys/dev/misc/efidev/efidev.c
@@ -0,0 +1,208 @@
+/*-
+ * Copyright (c) 2016 Netflix, Inc.
+ * 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: head/sys/dev/efidev/efidev.c 307391 2016-10-16 06:07:43Z kib $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/device.h>
+
+#include <machine/efi.h>
+#include <sys/efiio.h>
+
+static d_ioctl_t efidev_ioctl;
+
+static struct dev_ops efi_ops = {
+	{ "efi", 0, 0 },
+	.d_ioctl = efidev_ioctl,
+};
+	
+static int
+efidev_ioctl(struct dev_ioctl_args *ap)
+{
+	u_long cmd = ap->a_cmd;
+	caddr_t addr = ap->a_data;
+	int error;
+
+	switch (cmd) {
+	case EFIIOC_GET_TABLE:
+	{
+		struct efi_get_table_ioc *egtioc =
+		    (struct efi_get_table_ioc *)addr;
+
+		error = efi_get_table(&egtioc->uuid, &egtioc->ptr);
+		break;
+	}
+	case EFIIOC_GET_TIME:
+	{
+		struct efi_tm *tm = (struct efi_tm *)addr;
+
+		error = efi_get_time(tm);
+		break;
+	}
+	case EFIIOC_SET_TIME:
+	{
+		struct efi_tm *tm = (struct efi_tm *)addr;
+
+		error = efi_set_time(tm);
+		break;
+	}
+	case EFIIOC_VAR_GET:
+	{
+		struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
+		void *data;
+		efi_char *name;
+
+		data = kmalloc(ev->datasize, M_TEMP, M_WAITOK);
+		name = kmalloc(ev->namesize, M_TEMP, M_WAITOK);
+		error = copyin(ev->name, name, ev->namesize);
+		if (error)
+			goto vg_out;
+		if (name[ev->namesize / sizeof(efi_char) - 1] != 0) {
+			error = EINVAL;
+			goto vg_out;
+		}
+
+		error = efi_var_get(name, &ev->vendor, &ev->attrib,
+		    &ev->datasize, data);
+
+		if (error == 0) {
+			error = copyout(data, ev->data, ev->datasize);
+		} else if (error == EOVERFLOW) {
+			/*
+			 * Pass back the size we really need, but
+			 * convert the error to 0 so the copyout
+			 * happens. datasize was updated in the
+			 * efi_var_get call.
+			 */
+			ev->data = NULL;
+			error = 0;
+		}
+vg_out:
+		kfree(data, M_TEMP);
+		kfree(name, M_TEMP);
+		break;
+	}
+	case EFIIOC_VAR_NEXT:
+	{
+		struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
+		efi_char *name;
+
+		name = kmalloc(ev->namesize, M_TEMP, M_WAITOK);
+		error = copyin(ev->name, name, ev->namesize);
+		if (error)
+			goto vn_out;
+		/* Note: namesize is the buffer size, not the string lenght */
+
+		error = efi_var_nextname(&ev->namesize, name, &ev->vendor);
+		if (error == 0) {
+			error = copyout(name, ev->name, ev->namesize);
+		} else if (error == EOVERFLOW) {
+			ev->name = NULL;
+			error = 0;
+		}
+	vn_out:
+		kfree(name, M_TEMP);
+		break;
+	}
+	case EFIIOC_VAR_SET:
+	{
+		struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
+		void *data = NULL;
+		efi_char *name;
+
+		/* datasize == 0 -> delete (more or less) */
+		if (ev->datasize > 0)
+			data = kmalloc(ev->datasize, M_TEMP, M_WAITOK);
+		name = kmalloc(ev->namesize, M_TEMP, M_WAITOK);
+		if (ev->datasize) {
+			error = copyin(ev->data, data, ev->datasize);
+			if (error)
+				goto vs_out;
+		}
+		error = copyin(ev->name, name, ev->namesize);
+		if (error)
+			goto vs_out;
+		if (name[ev->namesize / sizeof(efi_char) - 1] != 0) {
+			error = EINVAL;
+			goto vs_out;
+		}
+
+		error = efi_var_set(name, &ev->vendor, ev->attrib, ev->datasize,
+		    data);
+vs_out:
+		kfree(data, M_TEMP);
+		kfree(name, M_TEMP);
+		break;
+	}
+	default:
+		error = ENOTTY;
+		break;
+	}
+
+	return (error);
+}
+
+static struct cdev *efidev;
+
+static int
+efidev_modevents(module_t m, int event, void *arg __unused)
+{
+	switch (event) {
+	case MOD_LOAD:
+		efidev = make_dev(&efi_ops, 0, UID_ROOT, GID_WHEEL, 0700,
+		    "efi");
+		return (0);
+
+	case MOD_UNLOAD:
+		if (efidev != NULL)
+			destroy_dev(efidev);
+		efidev = NULL;
+		return (0);
+
+	case MOD_SHUTDOWN:
+		return (0);
+
+	default:
+		return (EOPNOTSUPP);
+	}
+}
+
+static moduledata_t efidev_moddata = {
+	.name = "efidev",
+	.evhand = efidev_modevents,
+	.priv = NULL,
+};
+
+DECLARE_MODULE(efidev, efidev_moddata, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY);
+MODULE_VERSION(efidev, 1);
+MODULE_DEPEND(efidev, efirt, 1, 1, 1);
diff --git a/sys/platform/pc64/include/efi.h b/sys/platform/pc64/include/efi.h
new file mode 100644
index 0000000..3c47500
--- /dev/null
+++ b/sys/platform/pc64/include/efi.h
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 2016 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * 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: head/sys/amd64/include/efi.h 306209 2016-09-22 19:04:51Z imp $
+ */
+
+#ifndef __AMD64_INCLUDE_EFI_H_
+#define __AMD64_INCLUDE_EFI_H_
+
+/*
+ * XXX: from gcc 6.2 manual:
+ * Note, the ms_abi attribute for Microsoft Windows 64-bit targets
+ * currently requires the -maccumulate-outgoing-args option.
+ */
+#define	EFIABI_ATTR	__attribute__((ms_abi))
+
+#ifdef _KERNEL
+struct uuid;
+struct efi_tm;
+
+int efi_get_table(struct uuid *uuid, void **ptr);
+int efi_get_time(struct efi_tm *tm);
+int efi_get_time_locked(struct efi_tm *tm);
+int efi_reset_system(void);
+int efi_set_time(struct efi_tm *tm);
+int efi_set_time_locked(struct efi_tm *tm);
+int efi_var_get(uint16_t *name, struct uuid *vendor, uint32_t *attrib,
+    size_t *datasize, void *data);
+int efi_var_nextname(size_t *namesize, uint16_t *name, struct uuid *vendor);
+int efi_var_set(uint16_t *name, struct uuid *vendor, uint32_t attrib,
+    size_t datasize, void *data);
+#endif
+
+#endif /* __AMD64_INCLUDE_EFI_H_ */
diff --git a/sys/platform/pc64/include/pmap.h b/sys/platform/pc64/include/pmap.h
index 54ca8b3..eb87044 100644
--- a/sys/platform/pc64/include/pmap.h
+++ b/sys/platform/pc64/include/pmap.h
@@ -377,6 +377,40 @@ pmap_emulate_ad_bits(pmap_t pmap) {
 	return pmap->pm_flags & PMAP_EMULATE_AD_BITS;
 }
 
+/* Return various clipped indexes for a given VA */
+
+/*
+ * Returns the index of a pte in a page table, representing a terminal
+ * page.
+ */
+static __inline vm_pindex_t
+pmap_pte_index(vm_offset_t va)
+{
+
+	return ((va >> PAGE_SHIFT) & ((1ul << NPTEPGSHIFT) - 1));
+}
+
+static __inline vm_pindex_t
+pmap_pde_index(vm_offset_t va)
+{
+
+	return ((va >> PDRSHIFT) & ((1ul << NPDEPGSHIFT) - 1));
+}
+
+static __inline vm_pindex_t
+pmap_pdpe_index(vm_offset_t va)
+{
+
+	return ((va >> PDPSHIFT) & ((1ul << NPDPEPGSHIFT) - 1));
+}
+
+static __inline vm_pindex_t
+pmap_pml4e_index(vm_offset_t va)
+{
+
+	return ((va >> PML4SHIFT) & ((1ul << NPML4EPGSHIFT) - 1));
+}
+
 #endif /* _KERNEL */
 
 #endif /* !LOCORE */
diff --git a/sys/platform/pc64/x86_64/efirt.c b/sys/platform/pc64/x86_64/efirt.c
new file mode 100644
index 0000000..efea167
--- /dev/null
+++ b/sys/platform/pc64/x86_64/efirt.c
@@ -0,0 +1,611 @@
+/*-
+ * Copyright (c) 2004 Marcel Moolenaar
+ * Copyright (c) 2001 Doug Rabson
+ * Copyright (c) 2016 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Konstantin Belousov
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * 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: head/sys/amd64/amd64/efirt.c 307391 2016-10-16 06:07:43Z kib $
+ */
+
+#include <sys/param.h>
+#include <sys/efi.h>
+#include <sys/kernel.h>
+#include <sys/linker.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <machine/efi.h>
+#include <machine/metadata.h>
+#include <machine/md_var.h>
+#include <machine/smp.h>
+#include <machine/vmparam.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_map.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
+
+static struct efi_systbl *efi_systbl;
+static struct efi_cfgtbl *efi_cfgtbl;
+static struct efi_rt *efi_runtime;
+
+static int efi_status2err[25] = {
+	0,		/* EFI_SUCCESS */
+	ENOEXEC,	/* EFI_LOAD_ERROR */
+	EINVAL,		/* EFI_INVALID_PARAMETER */
+	ENOSYS,		/* EFI_UNSUPPORTED */
+	EMSGSIZE, 	/* EFI_BAD_BUFFER_SIZE */
+	EOVERFLOW,	/* EFI_BUFFER_TOO_SMALL */
+	EBUSY,		/* EFI_NOT_READY */
+	EIO,		/* EFI_DEVICE_ERROR */
+	EROFS,		/* EFI_WRITE_PROTECTED */
+	EAGAIN,		/* EFI_OUT_OF_RESOURCES */
+	EIO,		/* EFI_VOLUME_CORRUPTED */
+	ENOSPC,		/* EFI_VOLUME_FULL */
+	ENXIO,		/* EFI_NO_MEDIA */
+	ESTALE,		/* EFI_MEDIA_CHANGED */
+	ENOENT,		/* EFI_NOT_FOUND */
+	EACCES,		/* EFI_ACCESS_DENIED */
+	ETIMEDOUT,	/* EFI_NO_RESPONSE */
+	EADDRNOTAVAIL,	/* EFI_NO_MAPPING */
+	ETIMEDOUT,	/* EFI_TIMEOUT */
+	EDOOFUS,	/* EFI_NOT_STARTED */
+	EALREADY,	/* EFI_ALREADY_STARTED */
+	ECANCELED,	/* EFI_ABORTED */
+	EPROTO,		/* EFI_ICMP_ERROR */
+	EPROTO,		/* EFI_TFTP_ERROR */
+	EPROTO		/* EFI_PROTOCOL_ERROR */
+};
+
+static int
+efi_status_to_errno(efi_status status)
+{
+	u_long code;
+
+	code = status & 0x3ffffffffffffffful;
+	return (code < nitems(efi_status2err) ? efi_status2err[code] : EDOOFUS);
+}
+
+static struct lock efi_lock;
+static pml4_entry_t *efi_pml4;
+static vm_object_t obj_1t1_pt;
+static vm_page_t efi_pml4_page;
+
+static void
+efi_destroy_1t1_map(void)
+{
+	vm_page_t m;
+
+	if (obj_1t1_pt != NULL) {
+		VM_OBJECT_RLOCK(obj_1t1_pt);
+		TAILQ_FOREACH(m, &obj_1t1_pt->memq, listq)
+			m->wire_count = 0;
+		atomic_subtract_int(&vm_cnt.v_wire_count,
+		    obj_1t1_pt->resident_page_count);
+		VM_OBJECT_RUNLOCK(obj_1t1_pt);
+		vm_object_deallocate(obj_1t1_pt);
+	}
+
+	obj_1t1_pt = NULL;
+	efi_pml4 = NULL;
+	efi_pml4_page = NULL;
+}
+
+static vm_page_t
+efi_1t1_page(vm_pindex_t idx)
+{
+
+	return (vm_page_grab(obj_1t1_pt, idx, VM_ALLOC_NOBUSY |
+	    VM_ALLOC_WIRED | VM_ALLOC_ZERO));
+}
+
+static pt_entry_t *
+efi_1t1_pte(vm_offset_t va)
+{
+	pml4_entry_t *pml4e;
+	pdp_entry_t *pdpe;
+	pd_entry_t *pde;
+	pt_entry_t *pte;
+	vm_page_t m;
+	vm_pindex_t pml4_idx, pdp_idx, pd_idx;
+	vm_paddr_t mphys;
+
+	pml4_idx = pmap_pml4e_index(va);
+	pml4e = &efi_pml4[pml4_idx];
+	if (*pml4e == 0) {
+		m = efi_1t1_page(1 + pml4_idx);
+		mphys =  VM_PAGE_TO_PHYS(m);
+		*pml4e = mphys | X86_PG_RW | X86_PG_V;
+	} else {
+		mphys = *pml4e & ~PAGE_MASK;
+	}
+
+	pdpe = (pdp_entry_t *)PHYS_TO_DMAP(mphys);
+	pdp_idx = pmap_pdpe_index(va);
+	pdpe += pdp_idx;
+	if (*pdpe == 0) {
+		m = efi_1t1_page(1 + NPML4EPG + (pml4_idx + 1) * (pdp_idx + 1));
+		mphys =  VM_PAGE_TO_PHYS(m);
+		*pdpe = mphys | X86_PG_RW | X86_PG_V;
+	} else {
+		mphys = *pdpe & ~PAGE_MASK;
+	}
+
+	pde = (pd_entry_t *)PHYS_TO_DMAP(mphys);
+	pd_idx = pmap_pde_index(va);
+	pde += pd_idx;
+	if (*pde == 0) {
+		m = efi_1t1_page(1 + NPML4EPG + NPML4EPG * NPDPEPG +
+		    (pml4_idx + 1) * (pdp_idx + 1) * (pd_idx + 1));
+		mphys = VM_PAGE_TO_PHYS(m);
+		*pde = mphys | X86_PG_RW | X86_PG_V;
+	} else {
+		mphys = *pde & ~PAGE_MASK;
+	}
+
+	pte = (pt_entry_t *)PHYS_TO_DMAP(mphys);
+	pte += pmap_pte_index(va);
+	KASSERT(*pte == 0, ("va %#jx *pt %#jx", va, *pte));
+
+	return (pte);
+}
+
+static bool
+efi_create_1t1_map(struct efi_md *map, int ndesc, int descsz)
+{
+	struct efi_md *p;
+	pt_entry_t *pte;
+	vm_offset_t va;
+	uint64_t idx;
+	int bits, i, mode;
+
+	obj_1t1_pt = phys_pager_alloc(NULL, 1 + NPML4EPG +
+	    NPML4EPG * NPDPEPG + NPML4EPG * NPDPEPG * NPDEPG,
+	    VM_PROT_ALL, 0);
+	VM_OBJECT_WLOCK(obj_1t1_pt);
+	efi_pml4_page = efi_1t1_page(0);
+	VM_OBJECT_WUNLOCK(obj_1t1_pt);
+	efi_pml4 = (pml4_entry_t *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(efi_pml4_page));
+	pmap_pinit_pml4(efi_pml4_page);
+
+	for (i = 0, p = map; i < ndesc; i++, p = efi_next_descriptor(p,
+	    descsz)) {
+		if ((p->md_attr & EFI_MD_ATTR_RT) == 0)
+			continue;
+		if (p->md_virt != NULL) {
+			if (bootverbose)
+				kprintf("EFI Runtime entry %d is mapped\n", i);
+			goto fail;
+		}
+		if ((p->md_phys & EFI_PAGE_MASK) != 0) {
+			if (bootverbose)
+				kprintf("EFI Runtime entry %d is not aligned\n",
+				    i);
+			goto fail;
+		}
+		if (p->md_phys + p->md_pages * EFI_PAGE_SIZE < p->md_phys ||
+		    p->md_phys + p->md_pages * EFI_PAGE_SIZE >=
+		    VM_MAXUSER_ADDRESS) {
+			kprintf("EFI Runtime entry %d is not in mappable for RT:"
+			    "base %#016jx %#jx pages\n",
+			    i, (uintmax_t)p->md_phys,
+			    (uintmax_t)p->md_pages);
+			goto fail;
+		}
+		if ((p->md_attr & EFI_MD_ATTR_WB) != 0)
+			mode = VM_MEMATTR_WRITE_BACK;
+		else if ((p->md_attr & EFI_MD_ATTR_WT) != 0)
+			mode = VM_MEMATTR_WRITE_THROUGH;
+		else if ((p->md_attr & EFI_MD_ATTR_WC) != 0)
+			mode = VM_MEMATTR_WRITE_COMBINING;
+		else if ((p->md_attr & EFI_MD_ATTR_WP) != 0)
+			mode = VM_MEMATTR_WRITE_PROTECTED;
+		else if ((p->md_attr & EFI_MD_ATTR_UC) != 0)
+			mode = VM_MEMATTR_UNCACHEABLE;
+		else {
+			if (bootverbose)
+				kprintf("EFI Runtime entry %d mapping "
+				    "attributes unsupported\n", i);
+			mode = VM_MEMATTR_UNCACHEABLE;
+		}
+		bits = pmap_cache_bits(kernel_pmap, mode, FALSE) | X86_PG_RW |
+		    X86_PG_V;
+		VM_OBJECT_WLOCK(obj_1t1_pt);
+		for (va = p->md_phys, idx = 0; idx < p->md_pages; idx++,
+		    va += PAGE_SIZE) {
+			pte = efi_1t1_pte(va);
+			pte_store(pte, va | bits);
+		}
+		VM_OBJECT_WUNLOCK(obj_1t1_pt);
+	}
+
+	return (true);
+
+fail:
+	efi_destroy_1t1_map();
+	return (false);
+}
+
+/*
+ * Create an environment for the EFI runtime code call.  The most
+ * important part is creating the required 1:1 physical->virtual
+ * mappings for the runtime segments.  To do that, we manually create
+ * page table which unmap userspace but gives correct kernel mapping.
+ * The 1:1 mappings for runtime segments usually occupy low 4G of the
+ * physical address map.
+ *
+ * The 1:1 mappings were chosen over the SetVirtualAddressMap() EFI RT
+ * service, because there are some BIOSes which fail to correctly
+ * relocate itself on the call, requiring both 1:1 and virtual
+ * mapping.  As result, we must provide 1:1 mapping anyway, so no
+ * reason to bother with the virtual map, and no need to add a
+ * complexity into loader.
+ *
+ * The fpu_kern_enter() call allows firmware to use FPU, as mandated
+ * by the specification.  In particular, CR0.TS bit is cleared.  Also
+ * it enters critical section, giving us neccessary protection against
+ * context switch.
+ *
+ * There is no need to disable interrupts around the change of %cr3,
+ * the kernel mappings are correct, while we only grabbed the
+ * userspace portion of VA.  Interrupts handlers must not access
+ * userspace.  Having interrupts enabled fixes the issue with
+ * firmware/SMM long operation, which would negatively affect IPIs,
+ * esp. TLB shootdown requests.
+ */
+static int
+efi_enter(void)
+{
+	pmap_t curpmap;
+	int error;
+
+	if (efi_runtime == NULL)
+		return (ENXIO);
+	curpmap = PCPU_GET(curpmap);
+	PMAP_LOCK(curpmap);
+	lockmgr(&efi_lock, LK_EXCLUSIVE);
+	error = fpu_kern_enter(curthread, NULL, FPU_KERN_NOCTX);
+	if (error != 0) {
+		PMAP_UNLOCK(curpmap);
+		return (error);
+	}
+
+#if __DragonFly__ /* XXX */
+	load_cr3(VM_PAGE_TO_PHYS(efi_pml4_page));
+#else
+	/*
+	 * IPI TLB shootdown handler invltlb_pcid_handler() reloads
+	 * %cr3 from the curpmap->pm_cr3, which would disable runtime
+	 * segments mappings.  Block the handler's action by setting
+	 * curpmap to impossible value.  See also comment in
+	 * pmap.c:pmap_activate_sw().
+	 */
+	if (pmap_pcid_enabled && !invpcid_works)
+		PCPU_SET(curpmap, NULL);
+
+	load_cr3(VM_PAGE_TO_PHYS(efi_pml4_page) | (pmap_pcid_enabled ?
+	    curpmap->pm_pcids[PCPU_GET(cpuid)].pm_pcid : 0));
+	/*
+	 * If PCID is enabled, the clear CR3_PCID_SAVE bit in the loaded %cr3
+	 * causes TLB invalidation.
+	 */
+	if (!pmap_pcid_enabled)
+#endif
+		cpu_invltlb();
+	return (0);
+}
+
+static void
+efi_leave(void)
+{
+	pmap_t curpmap;
+
+	curpmap = &curproc->p_vmspace->vm_pmap;
+#if __DragonFly__ /* XXX */
+	load_cr3(curpmap->pm_cr3);
+#else
+	if (pmap_pcid_enabled && !invpcid_works)
+		PCPU_SET(curpmap, curpmap);
+	load_cr3(curpmap->pm_cr3 | (pmap_pcid_enabled ?
+	    curpmap->pm_pcids[PCPU_GET(cpuid)].pm_pcid : 0));
+	if (!pmap_pcid_enabled)
+#endif
+		cpu_invltlb();
+
+	fpu_kern_leave(curthread, NULL);
+	lockmgr(&efi_lock, LK_RELEASE);
+	PMAP_UNLOCK(curpmap);
+}
+
+static int
+efi_init(void)
+{
+	struct efi_map_header *efihdr;
+	struct efi_md *map;
+	caddr_t kmdp;
+	size_t efisz;
+
+	lockinit(&efi_lock, "efi", 0, LK_CANRECURSE);
+
+	if (efi_systbl_phys == 0) {
+		if (bootverbose)
+			kprintf("EFI systbl not available\n");
+		return (ENXIO);
+	}
+	efi_systbl = (struct efi_systbl *)PHYS_TO_DMAP(efi_systbl_phys);
+	if (efi_systbl->st_hdr.th_sig != EFI_SYSTBL_SIG) {
+		efi_systbl = NULL;
+		if (bootverbose)
+			kprintf("EFI systbl signature invalid\n");
+		return (ENXIO);
+	}
+	efi_cfgtbl = (efi_systbl->st_cfgtbl == 0) ? NULL :
+	    (struct efi_cfgtbl *)efi_systbl->st_cfgtbl;
+	if (efi_cfgtbl == NULL) {
+		if (bootverbose)
+			kprintf("EFI config table is not present\n");
+	}
+
+	kmdp = preload_search_by_type("elf kernel");
+	if (kmdp == NULL)
+		kmdp = preload_search_by_type("elf64 kernel");
+	efihdr = (struct efi_map_header *)preload_search_info(kmdp,
+	    MODINFO_METADATA | MODINFOMD_EFI_MAP);
+	if (efihdr == NULL) {
+		if (bootverbose)
+			kprintf("EFI map is not present\n");
+		return (ENXIO);
+	}
+	efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf;
+	map = (struct efi_md *)((uint8_t *)efihdr + efisz);
+	if (efihdr->descriptor_size == 0)
+		return (ENOMEM);
+
+	if (!efi_create_1t1_map(map, efihdr->memory_size /
+	    efihdr->descriptor_size, efihdr->descriptor_size)) {
+		if (bootverbose)
+			kprintf("EFI cannot create runtime map\n");
+		return (ENOMEM);
+	}
+
+	efi_runtime = (efi_systbl->st_rt == 0) ? NULL :
+	    (struct efi_rt *)efi_systbl->st_rt;
+	if (efi_runtime == NULL) {
+		if (bootverbose)
+			kprintf("EFI runtime services table is not present\n");
+		efi_destroy_1t1_map();
+		return (ENXIO);
+	}
+
+	return (0);
+}
+
+static void
+efi_uninit(void)
+{
+
+	efi_destroy_1t1_map();
+
+	efi_systbl = NULL;
+	efi_cfgtbl = NULL;
+	efi_runtime = NULL;
+
+	lockuninit(&efi_lock);
+}
+
+int
+efi_get_table(struct uuid *uuid, void **ptr)
+{
+	struct efi_cfgtbl *ct;
+	u_long count;
+
+	if (efi_cfgtbl == NULL)
+		return (ENXIO);
+	count = efi_systbl->st_entries;
+	ct = efi_cfgtbl;
+	while (count--) {
+		if (!bcmp(&ct->ct_uuid, uuid, sizeof(*uuid))) {
+			*ptr = (void *)PHYS_TO_DMAP(ct->ct_data);
+			return (0);
+		}
+		ct++;
+	}
+	return (ENOENT);
+}
+
+int
+efi_get_time_locked(struct efi_tm *tm)
+{
+	efi_status status;
+	int error;
+
+	KKASSERT(lockowned(&resettodr_lock) != 0);
+	error = efi_enter();
+	if (error != 0)
+		return (error);
+	status = efi_runtime->rt_gettime(tm, NULL);
+	efi_leave();
+	error = efi_status_to_errno(status);
+	return (error);
+}
+
+int
+efi_get_time(struct efi_tm *tm)
+{
+	int error;
+
+	if (efi_runtime == NULL)
+		return (ENXIO);
+	lockmgr(&resettodr_lock, LK_EXCLUSIVE);
+	error = efi_get_time_locked(tm);
+	lockmgr(&resettodr_lock, LK_RELEASE);
+	return (error);
+}
+
+int
+efi_reset_system(void)
+{
+	int error;
+
+	error = efi_enter();
+	if (error != 0)
+		return (error);
+	efi_runtime->rt_reset(EFI_RESET_WARM, 0, 0, NULL);
+	efi_leave();
+	return (EIO);
+}
+
+int
+efi_set_time_locked(struct efi_tm *tm)
+{
+	efi_status status;
+	int error;
+
+	KKASSERT(lockowned(&resettodr_lock) != 0);
+	error = efi_enter();
+	if (error != 0)
+		return (error);
+	status = efi_runtime->rt_settime(tm);
+	efi_leave();
+	error = efi_status_to_errno(status);
+	return (error);
+}
+
+int
+efi_set_time(struct efi_tm *tm)
+{
+	int error;
+
+	if (efi_runtime == NULL)
+		return (ENXIO);
+	lockmgr(&resettodr_lock, LK_EXCLUSIVE);
+	error = efi_set_time_locked(tm);
+	lockmgr(&resettodr_lock, LK_RELEASE);
+	return (error);
+}
+
+int
+efi_var_get(efi_char *name, struct uuid *vendor, uint32_t *attrib,
+    size_t *datasize, void *data)
+{
+	efi_status status;
+	int error;
+
+	error = efi_enter();
+	if (error != 0)
+		return (error);
+	status = efi_runtime->rt_getvar(name, vendor, attrib, datasize, data);
+	efi_leave();
+	error = efi_status_to_errno(status);
+	return (error);
+}
+
+int
+efi_var_nextname(size_t *namesize, efi_char *name, struct uuid *vendor)
+{
+	efi_status status;
+	int error;
+
+	error = efi_enter();
+	if (error != 0)
+		return (error);
+	status = efi_runtime->rt_scanvar(namesize, name, vendor);
+	efi_leave();
+	error = efi_status_to_errno(status);
+	return (error);
+}
+
+int
+efi_var_set(efi_char *name, struct uuid *vendor, uint32_t attrib,
+    size_t datasize, void *data)
+{
+	efi_status status;
+	int error;
+
+	error = efi_enter();
+	if (error != 0)
+		return (error);
+	status = efi_runtime->rt_setvar(name, vendor, attrib, datasize, data);
+	efi_leave();
+	error = efi_status_to_errno(status);
+	return (error);
+}
+
+static int
+efirt_modevents(module_t m, int event, void *arg __unused)
+{
+
+	switch (event) {
+	case MOD_LOAD:
+		return (efi_init());
+
+	case MOD_UNLOAD:
+		efi_uninit();
+		return (0);
+
+	case MOD_SHUTDOWN:
+		return (0);
+
+	default:
+		return (EOPNOTSUPP);
+	}
+}
+
+static moduledata_t efirt_moddata = {
+	.name = "efirt",
+	.evhand = efirt_modevents,
+	.priv = NULL,
+};
+DECLARE_MODULE(efirt, efirt_moddata, SI_SUB_VM_CONF, SI_ORDER_ANY);
+MODULE_VERSION(efirt, 1);
+
+/* XXX debug stuff */
+static int
+efi_time_sysctl_handler(SYSCTL_HANDLER_ARGS)
+{
+	struct efi_tm tm;
+	int error, val;
+
+	val = 0;
+	error = sysctl_handle_int(oidp, &val, 0, req);
+	if (error != 0 || req->newptr == NULL)
+		return (error);
+	error = efi_get_time(&tm);
+	if (error == 0) {
+		uprintf("EFI reports: Year %d Month %d Day %d Hour %d Min %d "
+		    "Sec %d\n", tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
+		    tm.tm_min, tm.tm_sec);
+	}
+	return (error);
+}
+
+SYSCTL_PROC(_debug, OID_AUTO, efi_time, CTLTYPE_INT | CTLFLAG_RW, NULL, 0,
+    efi_time_sysctl_handler, "I", "");
diff --git a/sys/platform/pc64/x86_64/pmap.c b/sys/platform/pc64/x86_64/pmap.c
index 7764abc..67c5429 100644
--- a/sys/platform/pc64/x86_64/pmap.c
+++ b/sys/platform/pc64/x86_64/pmap.c
@@ -437,17 +437,6 @@ pmap_pml4_pindex(void)
 /*
  * Return various clipped indexes for a given VA
  *
- * Returns the index of a pte in a page table, representing a terminal
- * page.
- */
-static __inline
-vm_pindex_t
-pmap_pte_index(vm_offset_t va)
-{
-	return ((va >> PAGE_SHIFT) & ((1ul << NPTEPGSHIFT) - 1));
-}
-
-/*
  * Returns the index of a pt in a page directory, representing a page
  * table.
  */
diff --git a/sys/sys/efiio.h b/sys/sys/efiio.h
new file mode 100644
index 0000000..e4db866
--- /dev/null
+++ b/sys/sys/efiio.h
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 2016 Netflix, Inc.
+ * 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: head/sys/sys/efiio.h 307070 2016-10-11 22:24:30Z imp $
+ */
+
+#ifndef	_SYS_EFIIO_H_
+#define	_SYS_EFIIO_H_
+
+#include <sys/ioccom.h>
+#include <sys/uuid.h>
+#include <sys/efi.h>
+
+struct efi_get_table_ioc
+{
+	struct uuid uuid;	/* UUID to look up */
+	void *ptr;		/* Pointer to table in KVA space */
+};
+
+struct efi_var_ioc
+{
+	efi_char *name;		/* User pointer to name, in wide chars */
+	size_t namesize;	/* Number of wide characters in name */
+	struct uuid vendor;	/* Vendor's UUID for variable */
+	uint32_t attrib;	/* Attributes */
+	void *data;		/* User pointer to the data */
+	size_t datasize;	/* Number of *bytes* in the data */
+};
+
+#define EFIIOC_GET_TABLE	_IOWR('E',  1, struct efi_get_table_ioc)
+#define EFIIOC_GET_TIME		_IOR('E',   2, struct efi_tm)
+#define EFIIOC_SET_TIME		_IOW('E',   3, struct efi_tm)
+#define EFIIOC_VAR_GET		_IOWR('E',  4, struct efi_var_ioc)
+#define EFIIOC_VAR_NEXT		_IOWR('E',  5, struct efi_var_ioc)
+#define EFIIOC_VAR_SET		_IOWR('E',  6, struct efi_var_ioc)
+
+#endif /* _SYS_EFIIO_H_ */
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index 6dc01b9..8169e1e 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -32,6 +32,7 @@ SUBDIR= 802_11 \
 	dev_mkdb \
 	dntpd \
 	edquota \
+	efivar \
 	faithd \
 	fdcontrol \
 	fdformat \
@@ -124,6 +125,7 @@ SUBDIR= 802_11 \
 	traceroute6 \
 	trpt \
 	tzsetup \
+	uefisign \
 	usbconfig \
 	usbdump \
 	vidcontrol \
diff --git a/usr.sbin/efivar/Makefile b/usr.sbin/efivar/Makefile
new file mode 100644
index 0000000..1c6fe20
--- /dev/null
+++ b/usr.sbin/efivar/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD: head/usr.sbin/efivar/Makefile 307072 2016-10-11 22:31:45Z imp $
+
+PROG=	efivar
+MAN=	efivar.8
+
+LDADD=	-lefivar
+DPADD=	${LIBEFIVAR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/efivar/efivar.8 b/usr.sbin/efivar/efivar.8
new file mode 100644
index 0000000..341ef33
--- /dev/null
+++ b/usr.sbin/efivar/efivar.8
@@ -0,0 +1,164 @@
+.\" Copyright (c) 2003 Netflix, Inc
+.\" 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: head/usr.sbin/efivar/efivar.8 307224 2016-10-13 17:03:54Z imp $
+.\"
+.Dd September 29, 2016
+.Dt EFIVAR 8
+.Os
+.Sh NAME
+.Nm efivar
+.Nd UEFI environemnt variable interaction
+.Sh SYNOPSIS
+.Nm
+.Op Fl abdDHlLNpRtw
+.Op Fl n Ar name
+.Op Fl f Ar file
+.Op Fl -append
+.Op Fl -ascii
+.Op Fl -attributes
+.Op Fl -binary
+.Op Fl -delete
+.Op Fl -fromfile Ar file
+.Op Fl -hex
+.Op Fl -list-guids
+.Op Fl -list
+.Op Fl -name Ar name
+.Op Fl -no-name
+.Op Fl -print
+.Op Fl -print-decimal
+.Op Fl -raw-guid
+.Op Fl -write
+.Ar name Ns Op = Ns Ar value
+.Sh DESCRIPTION
+This program manages
+.Dq Unified Extensible Firmware Interface
+.Pq UEFI
+environment variables.
+UEFI variables have three part: A namespace, a name and a value.
+The namespace is a GUID that's self assigned by the group defining the
+variables.
+The name is a Unicode name for the variable.
+The value is binary data.
+All Unicode data is presented to the user as UTF-8.
+.Pp
+The following options are available:
+.Bl -tag -width 20m
+.It Fl n Ar name Fl -name Ar name
+Specify the name of the variable to operate on.
+The
+.Ar name
+argument is the GUID of variable, followed by a dash, followed by the
+UEFI variable name.
+The GUID may be in numeric format, or may be one of the well known
+symbolic name (see
+.Fl -list-guids
+for a complete list).
+.It Fl f Ar file Fl -fromfile Ar file
+When writing or appending to a variable, take the data for the
+variable's value from
+.Ar file
+instead of from the command line.
+This flag implies
+.Fl -write
+unless the
+.Fl -append
+flag is given.
+This is not well understood and currently unimplemented.
+.It Fl a Fl -append
+Append the specified value to the UEFI variable rather than replacing
+it.p
+.It Fl t Ar attr Fl -attributes Ar attr
+Specify, in user hostile hexidecimal, the attributes for this
+variable.
+See section 7.2 (GetVariable subsection, Related Definitions) of the
+UEFI Specification for hex values to use.
+.It Fl A Fl -ascii
+Display the variable data as modified ascii: All printable characters
+are printed, while unprintable characters are rendered as a two-digit
+hexadecimal number preceeded by a % character.
+.It Fl b Fl -binary
+Display the variable data as binary data.
+Usually will be used with the
+.Fl N
+or
+.Fl -no-name
+flag.
+Useful in scripts.
+.It Fl D Fl -delete
+Delete the specified variable.
+May not be used with either the
+.Fl -write
+or the
+.Fl -append
+flags.
+No
+.Ar value
+may be specified.
+.It Fl H Fl -hex
+List variable data as a hex dump.
+.It Fl L Fl -list-guids
+Lists the well known GUIDs.
+The names listed here may be used in place of the numeric GUID values.
+These names will replace the numeric GUID values unless
+.Fl -raw-guid
+flag is specified.
+.It Fl l Fl -list
+List all the variables.
+If the
+.Fl -print
+flag is also listed, their values will be displayed.
+.It Fl N Fl -no-name
+Do not display the variable name.
+.It Fl p Fl -print
+Print the value of the variable.
+.It Fl d Fl -print-decimal
+Treat the value of the variable as a number and print it as a
+decimal.
+This is currently unimplemented.
+.It Fl R Fl -raw-guid
+Do not substitute well known names for GUID numeric values in output.
+.It Fl w Fl -write
+Write (replace) the variable specified with the value specified.
+.It Ar name
+Display the
+.Ar name
+environment variable.
+.It Ar name Ns = Ns Ar value
+Set the specified
+.Ar name
+to
+.Ar value .
+This is not yet implemented.
+.Sh COMPATIBILITY
+The
+.Nm
+program is intended to be compatible (strict superset) with a progam
+of the same name included in the Red Hat libefivar package.
+.Sh SEE ALSO
+Appendix A of the UEFI specification has the format for GUIDs.
+All GUIDs
+.Dq Globally Unique Identifiers
+have the format described in RFC 4122.
+.El
diff --git a/usr.sbin/efivar/efivar.c b/usr.sbin/efivar/efivar.c
new file mode 100644
index 0000000..5c62564
--- /dev/null
+++ b/usr.sbin/efivar/efivar.c
@@ -0,0 +1,351 @@
+/*-
+ * Copyright (c) 2016 Netflix, Inc.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.sbin/efivar/efivar.c 307390 2016-10-16 05:53:18Z imp $");
+
+#include <ctype.h>
+#include <efivar.h>
+#include <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* options descriptor */
+static struct option longopts[] = {
+	{ "append",		no_argument,		NULL,	'a' },
+	{ "ascii",		no_argument,		NULL,	'A' },
+	{ "attributes",		required_argument,	NULL,	't' },
+	{ "binary",		no_argument,		NULL,	'b' },
+	{ "delete",		no_argument,		NULL,   'D' },
+	{ "fromfile",		required_argument,	NULL,	'f' },
+	{ "hex",		no_argument,		NULL,	'H' },
+	{ "list-guids",		no_argument,		NULL,	'L' },
+	{ "list",		no_argument,		NULL,	'l' },
+	{ "name",		required_argument,	NULL,	'n' },
+	{ "no-name",		no_argument,		NULL,	'N' },
+	{ "print",		no_argument,		NULL,	'p' },
+	{ "print-decimal",	no_argument,		NULL,	'd' },
+	{ "raw-guid",		no_argument,		NULL,   'R' },
+	{ "write",		no_argument,		NULL,	'w' },
+	{ NULL,			0,			NULL,	0 }
+};
+
+
+static int aflag, Aflag, bflag, dflag, Dflag, Hflag, Nflag,
+	lflag, Lflag, Rflag, wflag, pflag;
+static char *varname;
+static u_long attrib = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
+
+static void
+usage(void)
+{
+
+	errx(1, "efivar [-abdDHlLNpRtw] [-n name] [-f file] [--append] [--ascii]\n"
+	    "\t[--attributes] [--binary] [--delete] [--fromfile file] [--hex]\n"
+	    "\t[--list-guids] [--list] [--name name] [--no-name] [--print]\n"
+	    "\t[--print-decimal] [--raw-guid] [--write] name[=value]");
+}
+
+static void
+breakdown_name(char *name, efi_guid_t *guid, char **vname)
+{
+	char *cp;
+
+	cp = strrchr(name, '-');
+	if (cp == NULL)
+		errx(1, "Invalid name: %s", name);
+	*vname = cp + 1;
+	*cp = '\0';
+	if (efi_str_to_guid(name, guid) < 0)
+		errx(1, "Invalid guid %s", name);
+}
+
+static uint8_t *
+get_value(char *val, size_t *datalen)
+{
+	static char buffer[16*1024];
+
+	if (val != NULL) {
+		*datalen = strlen(val);
+		return ((uint8_t *)val);
+	}
+	/* Read from stdin */
+	*datalen = sizeof(buffer);
+	*datalen = read(0, buffer, *datalen);
+	return ((uint8_t *)buffer);
+}
+
+static void
+append_variable(char *name, char *val)
+{
+	char *vname;
+	efi_guid_t guid;
+	size_t datalen;
+	uint8_t *data;
+
+	breakdown_name(name, &guid, &vname);
+	data = get_value(val, &datalen);
+	if (efi_append_variable(guid, vname, data, datalen, attrib) < 0)
+		err(1, "efi_append_variable");
+}
+
+static void
+delete_variable(char *name)
+{
+	char *vname;
+	efi_guid_t guid;
+
+	breakdown_name(name, &guid, &vname);
+	if (efi_del_variable(guid, vname) < 0)
+		err(1, "efi_del_variable");
+}
+
+static void
+write_variable(char *name, char *val)
+{
+	char *vname;
+	efi_guid_t guid;
+	size_t datalen;
+	uint8_t *data;
+
+	breakdown_name(name, &guid, &vname);
+	data = get_value(val, &datalen);
+	if (efi_set_variable(guid, vname, data, datalen, attrib, 0) < 0)
+		err(1, "efi_set_variable");
+}
+
+static void
+asciidump(uint8_t *data, size_t datalen)
+{
+	size_t i;
+	int len;
+
+	len = 0;
+	if (!Nflag)
+		printf("\n");
+	for (i = 0; i < datalen; i++) {
+		if (isprint(data[i])) {
+			len++;
+			if (len > 80) {
+				len = 0;
+				printf("\n");
+			}
+			printf("%c", data[i]);
+		} else {
+			len +=3;
+			if (len > 80) {
+				len = 0;
+				printf("\n");
+			}
+			printf("%%%02x", data[i]);
+		}
+	}
+	printf("\n");
+}
+
+static void
+hexdump(uint8_t *data, size_t datalen)
+{
+	size_t i;
+
+	if (!Nflag)
+		printf("\n");
+	for (i = 0; i < datalen; i++) {
+		if (i % 16 == 0) {
+			if (i != 0)
+				printf("\n");
+			printf("%04x: ", (int)i);
+		}
+		printf("%02x ", data[i]);
+	}
+	printf("\n");
+}
+
+static void
+bindump(uint8_t *data, size_t datalen)
+{
+	write(1, data, datalen);
+}
+
+static void
+print_var(efi_guid_t *guid, char *name)
+{
+	uint32_t att;
+	uint8_t *data;
+	size_t datalen;
+	char *gname;
+	int rv;
+
+	efi_guid_to_str(guid, &gname);
+	if (!Nflag)
+		printf("%s-%s", gname, name);
+	if (pflag) {
+		rv = efi_get_variable(*guid, name, &data, &datalen, &att);
+
+		if (rv < 0)
+			printf("\n --- Error getting value --- %d", errno);
+		else {
+			if (Aflag)
+				asciidump(data, datalen);
+			else if (bflag)
+				bindump(data, datalen);
+			else
+				hexdump(data, datalen);
+		}
+	}
+	free(gname);
+	if (!Nflag)
+		printf("\n");
+}
+
+static void
+print_variable(char *name)
+{
+	char *vname;
+	efi_guid_t guid;
+
+	breakdown_name(name, &guid, &vname);
+	print_var(&guid, vname);
+}
+
+static void
+print_variables(void)
+{
+	int rv;
+	char *name = NULL;
+	efi_guid_t *guid = NULL;
+
+	while ((rv = efi_get_next_variable_name(&guid, &name)) > 0)
+		print_var(guid, name);
+
+	if (rv < 0)
+		err(1, "Error listing names");
+}
+
+static void
+parse_args(int argc, char **argv)
+{
+	int ch, i;
+
+	while ((ch = getopt_long(argc, argv, "aAbdDf:HlLNn:pRt:w",
+		    longopts, NULL)) != -1) {
+		switch (ch) {
+		case 'a':
+			aflag++;
+			break;
+		case 'A':
+			Aflag++;
+			break;
+		case 'b':
+			bflag++;
+			break;
+		case 'd':
+			dflag++;
+			break;
+		case 'D':
+			Dflag++;
+			break;
+		case 'H':
+			Hflag++;
+			break;
+		case 'l':
+			lflag++;
+			break;
+		case 'L':
+			Lflag++;
+			break;
+		case 'n':
+			varname = optarg;
+			break;
+		case 'N':
+			Nflag++;
+			break;
+		case 'p':
+			pflag++;
+			break;
+		case 'R':
+			Rflag++;
+			break;
+		case 't':
+			attrib = strtoul(optarg, NULL, 16);
+			break;
+		case 'w':
+			wflag++;
+			break;
+		case 'f':
+		case 0:
+			errx(1, "unknown or unimplemented option\n");
+			break;
+		default:
+			usage();
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	if (argc == 1)
+		varname = argv[0];
+
+	if (aflag + Dflag + wflag > 1) {
+		warnx("Can only use one of -a (--append), "
+		    "-D (--delete) and -w (--write)");
+		usage();
+	}
+
+	if (aflag + Dflag + wflag > 0 && varname == NULL) {
+		warnx("Must specify a variable for -a (--append), "
+		    "-D (--delete) or -w (--write)");
+		usage();
+	}
+
+	if (aflag)
+		append_variable(varname, NULL);
+	else if (Dflag)
+		delete_variable(varname);
+	else if (wflag)
+		write_variable(varname, NULL);
+	else if (varname) {
+		pflag++;
+		print_variable(varname);
+	} else if (argc > 0) {
+		pflag++;
+		for (i = 0; i < argc; i++)
+			print_variable(argv[i]);
+	} else
+		print_variables();
+}
+
+int
+main(int argc, char **argv)
+{
+
+	parse_args(argc, argv);
+}
diff --git a/usr.sbin/uefisign/Makefile b/usr.sbin/uefisign/Makefile
new file mode 100644
index 0000000..f36eb7f
--- /dev/null
+++ b/usr.sbin/uefisign/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD: head/usr.sbin/uefisign/Makefile 279323 2015-02-26 15:48:20Z trasz $
+
+PROG=	uefisign
+SRCS=	uefisign.c child.c pe.c
+MAN=	uefisign.8
+
+LDADD=	-lprivate_crypto
+DPADD=	${LIBRECRYPTO}
+LDFLAGS+= -rpath /lib/priv -L ${_SHLIBDIRPREFIX}/usr/lib/priv
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/uefisign/child.c b/usr.sbin/uefisign/child.c
new file mode 100644
index 0000000..ad6c6ce
--- /dev/null
+++ b/usr.sbin/uefisign/child.c
@@ -0,0 +1,267 @@
+/*-
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * 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: head/usr.sbin/uefisign/child.c 305980 2016-09-19 16:07:32Z emaste $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+
+#include "uefisign.h"
+
+static void
+load(struct executable *x)
+{
+	int error, fd;
+	struct stat sb;
+	char *buf;
+	size_t nread, len;
+
+	fd = fileno(x->x_fp);
+
+	error = fstat(fd, &sb);
+	if (error != 0)
+		err(1, "%s: fstat", x->x_path);
+
+	len = sb.st_size;
+	if (len <= 0)
+		errx(1, "%s: file is empty", x->x_path);
+
+	buf = malloc(len);
+	if (buf == NULL)
+		err(1, "%s: cannot malloc %zd bytes", x->x_path, len);
+
+	nread = fread(buf, len, 1, x->x_fp);
+	if (nread != 1)
+		err(1, "%s: fread", x->x_path);
+
+	x->x_buf = buf;
+	x->x_len = len;
+}
+
+static void
+digest_range(struct executable *x, EVP_MD_CTX *mdctx, off_t off, size_t len)
+{
+	int ok;
+
+	range_check(x, off, len, "chunk");
+
+	ok = EVP_DigestUpdate(mdctx, x->x_buf + off, len);
+	if (ok == 0) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "EVP_DigestUpdate(3) failed");
+	}
+}
+
+static void
+digest(struct executable *x)
+{
+	EVP_MD_CTX *mdctx;
+	const EVP_MD *md;
+	size_t sum_of_bytes_hashed;
+	int i, ok;
+
+	/*
+	 * Windows Authenticode Portable Executable Signature Format
+	 * spec version 1.0 specifies MD5 and SHA1.  However, pesign
+	 * and sbsign both use SHA256, so do the same.
+	 */
+	md = EVP_get_digestbyname(DIGEST);
+	if (md == NULL) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "EVP_get_digestbyname(\"%s\") failed", DIGEST);
+	}
+
+	mdctx = EVP_MD_CTX_create();
+	if (mdctx == NULL) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "EVP_MD_CTX_create(3) failed");
+	}
+
+	ok = EVP_DigestInit_ex(mdctx, md, NULL);
+	if (ok == 0) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "EVP_DigestInit_ex(3) failed");
+	}
+
+	/*
+	 * According to the Authenticode spec, we need to compute
+	 * the digest in a rather... specific manner; see "Calculating
+	 * the PE Image Hash" part of the spec for details.
+	 *
+	 * First, everything from 0 to before the PE checksum.
+	 */
+	digest_range(x, mdctx, 0, x->x_checksum_off);
+
+	/*
+	 * Second, from after the PE checksum to before the Certificate
+	 * entry in Data Directory.
+	 */
+	digest_range(x, mdctx, x->x_checksum_off + x->x_checksum_len,
+	    x->x_certificate_entry_off -
+	    (x->x_checksum_off + x->x_checksum_len));
+
+	/*
+	 * Then, from after the Certificate entry to the end of headers.
+	 */
+	digest_range(x, mdctx,
+	    x->x_certificate_entry_off + x->x_certificate_entry_len,
+	    x->x_headers_len -
+	    (x->x_certificate_entry_off + x->x_certificate_entry_len));
+
+	/*
+	 * Then, each section in turn, as specified in the PE Section Table.
+	 *
+	 * XXX: Sorting.
+	 */
+	sum_of_bytes_hashed = x->x_headers_len;
+	for (i = 0; i < x->x_nsections; i++) {
+		digest_range(x, mdctx,
+		    x->x_section_off[i], x->x_section_len[i]);
+		sum_of_bytes_hashed += x->x_section_len[i];
+	}
+
+	/*
+	 * I believe this can happen with overlapping sections.
+	 */
+	if (sum_of_bytes_hashed > x->x_len)
+		errx(1, "number of bytes hashed is larger than file size");
+
+	/*
+	 * I can't really explain this one; just do what the spec says.
+	 */
+	if (sum_of_bytes_hashed < x->x_len) {
+		digest_range(x, mdctx, sum_of_bytes_hashed,
+		    x->x_len - (signature_size(x) + sum_of_bytes_hashed));
+	}
+
+	ok = EVP_DigestFinal_ex(mdctx, x->x_digest, &x->x_digest_len);
+	if (ok == 0) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "EVP_DigestFinal_ex(3) failed");
+	}
+
+	EVP_MD_CTX_destroy(mdctx);
+}
+
+static void
+show_digest(const struct executable *x)
+{
+	int i;
+
+	printf("computed %s digest ", DIGEST);
+	for (i = 0; i < (int)x->x_digest_len; i++)
+		printf("%02x", (unsigned char)x->x_digest[i]);
+	printf("; digest len %u\n", x->x_digest_len);
+}
+
+static void
+send_digest(const struct executable *x, int pipefd)
+{
+
+	send_chunk(x->x_digest, x->x_digest_len, pipefd);
+}
+
+static void
+receive_signature(struct executable *x, int pipefd)
+{
+
+	receive_chunk(&x->x_signature, &x->x_signature_len, pipefd);
+}
+
+static void
+save(struct executable *x, FILE *fp, const char *path)
+{
+	size_t nwritten;
+
+	assert(fp != NULL);
+	assert(path != NULL);
+
+	nwritten = fwrite(x->x_buf, x->x_len, 1, fp);
+	if (nwritten != 1)
+		err(1, "%s: fwrite", path);
+}
+
+int
+child(const char *inpath, const char *outpath, int pipefd,
+    bool Vflag, bool vflag)
+{
+	FILE *outfp = NULL, *infp = NULL;
+	struct executable *x;
+
+	infp = checked_fopen(inpath, "r");
+	if (outpath != NULL)
+		outfp = checked_fopen(outpath, "w");
+
+	x = calloc(1, sizeof(*x));
+	if (x == NULL)
+		err(1, "calloc");
+	x->x_path = inpath;
+	x->x_fp = infp;
+
+	load(x);
+	parse(x);
+	if (Vflag) {
+		if (signature_size(x) == 0)
+			errx(1, "file not signed");
+
+		printf("file contains signature\n");
+		if (vflag) {
+			digest(x);
+			show_digest(x);
+			show_certificate(x);
+		}
+	} else {
+		if (signature_size(x) != 0)
+			errx(1, "file already signed");
+
+		digest(x);
+		if (vflag)
+			show_digest(x);
+		send_digest(x, pipefd);
+		receive_signature(x, pipefd);
+		update(x);
+		save(x, outfp, outpath);
+	}
+
+	return (0);
+}
diff --git a/usr.sbin/uefisign/magic.h b/usr.sbin/uefisign/magic.h
new file mode 100644
index 0000000..c4c4e4d
--- /dev/null
+++ b/usr.sbin/uefisign/magic.h
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * 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: head/usr.sbin/uefisign/magic.h 289677 2015-10-21 05:37:09Z eadler $
+ *
+ */
+
+/*
+ * This file contains Authenticode-specific ASN.1 "configuration", used,
+ * after being processed by asprintf(3), as an input to ASN1_generate_nconf(3).
+ */
+static const char *magic_fmt =
+"asn1 = SEQUENCE:SpcIndirectDataContent\n"
+"\n"
+"[SpcIndirectDataContent]\n"
+"a = SEQUENCE:SpcAttributeTypeAndOptionalValue\n"
+"b = SEQUENCE:DigestInfo\n"
+"\n"
+"[SpcAttributeTypeAndOptionalValue]\n"
+"# SPC_PE_IMAGE_DATAOBJ\n"
+"a = OID:1.3.6.1.4.1.311.2.1.15\n"
+"b = SEQUENCE:SpcPeImageData\n"
+"\n"
+"[SpcPeImageData]\n"
+"a = FORMAT:HEX,BITSTRING:00\n"
+/*
+ * Well, there should be some other struct here, "SPCLink", but it doesn't
+ * appear to be necessary for UEFI, and I have no idea how to synthesize it,
+ * as it uses the CHOICE type.
+ */
+"\n"
+"[DigestInfo]\n"
+"a = SEQUENCE:AlgorithmIdentifier\n"
+/*
+ * Here goes the digest computed from PE headers and sections.
+ */
+"b = FORMAT:HEX,OCTETSTRING:%s\n"
+"\n"
+"[AlgorithmIdentifier]\n"
+"a = OBJECT:sha256\n"
+"b = NULL\n";
diff --git a/usr.sbin/uefisign/pe.c b/usr.sbin/uefisign/pe.c
new file mode 100644
index 0000000..7a1606b
--- /dev/null
+++ b/usr.sbin/uefisign/pe.c
@@ -0,0 +1,564 @@
+/*-
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * PE format reference:
+ * http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.sbin/uefisign/pe.c 289677 2015-10-21 05:37:09Z eadler $");
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "uefisign.h"
+
+#ifndef CTASSERT
+#define CTASSERT(x)		_CTASSERT(x, __LINE__)
+#define _CTASSERT(x, y)		__CTASSERT(x, y)
+#define __CTASSERT(x, y)	typedef char __assert_ ## y [(x) ? 1 : -1]
+#endif
+
+struct mz_header {
+	uint8_t			mz_signature[2];
+	uint8_t			mz_dont_care[58];
+	uint16_t		mz_lfanew;
+} __attribute__((packed));
+
+struct coff_header {
+	uint8_t			coff_dont_care[2];
+	uint16_t		coff_number_of_sections;
+	uint8_t			coff_dont_care_either[16];
+} __attribute__((packed));
+
+#define	PE_SIGNATURE		0x00004550
+
+struct pe_header {
+	uint32_t		pe_signature;
+	struct coff_header	pe_coff;
+} __attribute__((packed));
+
+#define	PE_OPTIONAL_MAGIC_32		0x010B
+#define	PE_OPTIONAL_MAGIC_32_PLUS	0x020B
+
+#define	PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION	10
+#define	PE_OPTIONAL_SUBSYSTEM_EFI_BOOT		11
+#define	PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME	12
+
+struct pe_optional_header_32 {
+	uint16_t		po_magic;
+	uint8_t			po_dont_care[58];
+	uint32_t		po_size_of_headers;
+	uint32_t		po_checksum;
+	uint16_t		po_subsystem;
+	uint8_t			po_dont_care_either[22];
+	uint32_t		po_number_of_rva_and_sizes;
+} __attribute__((packed));
+
+CTASSERT(offsetof(struct pe_optional_header_32, po_size_of_headers) == 60);
+CTASSERT(offsetof(struct pe_optional_header_32, po_checksum) == 64);
+CTASSERT(offsetof(struct pe_optional_header_32, po_subsystem) == 68);
+CTASSERT(offsetof(struct pe_optional_header_32, po_number_of_rva_and_sizes) == 92);
+
+struct pe_optional_header_32_plus {
+	uint16_t		po_magic;
+	uint8_t			po_dont_care[58];
+	uint32_t		po_size_of_headers;
+	uint32_t		po_checksum;
+	uint16_t		po_subsystem;
+	uint8_t			po_dont_care_either[38];
+	uint32_t		po_number_of_rva_and_sizes;
+} __attribute__((packed));
+
+CTASSERT(offsetof(struct pe_optional_header_32_plus, po_size_of_headers) == 60);
+CTASSERT(offsetof(struct pe_optional_header_32_plus, po_checksum) == 64);
+CTASSERT(offsetof(struct pe_optional_header_32_plus, po_subsystem) == 68);
+CTASSERT(offsetof(struct pe_optional_header_32_plus, po_number_of_rva_and_sizes) == 108);
+
+#define	PE_DIRECTORY_ENTRY_CERTIFICATE	4
+
+struct pe_directory_entry {
+	uint32_t	pde_rva;
+	uint32_t	pde_size;
+} __attribute__((packed));
+
+struct pe_section_header {
+	uint8_t			psh_dont_care[16];
+	uint32_t		psh_size_of_raw_data;
+	uint32_t		psh_pointer_to_raw_data;
+	uint8_t			psh_dont_care_either[16];
+} __attribute__((packed));
+
+CTASSERT(offsetof(struct pe_section_header, psh_size_of_raw_data) == 16);
+CTASSERT(offsetof(struct pe_section_header, psh_pointer_to_raw_data) == 20);
+
+#define	PE_CERTIFICATE_REVISION		0x0200
+#define	PE_CERTIFICATE_TYPE		0x0002
+
+struct pe_certificate {
+	uint32_t	pc_len;
+	uint16_t	pc_revision;
+	uint16_t	pc_type;
+	char		pc_signature[0];
+} __attribute__((packed));
+
+void
+range_check(const struct executable *x, off_t off, size_t len,
+    const char *name)
+{
+
+	if (off < 0) {
+		errx(1, "%s starts at negative offset %jd",
+		    name, (intmax_t)off);
+	}
+	if (off >= (off_t)x->x_len) {
+		errx(1, "%s starts at %jd, past the end of executable at %zd",
+		    name, (intmax_t)off, x->x_len);
+	}
+	if (len >= x->x_len) {
+		errx(1, "%s size %zd is larger than the executable size %zd",
+		    name, len, x->x_len);
+	}
+	if (off + len > x->x_len) {
+		errx(1, "%s extends to %jd, past the end of executable at %zd",
+		    name, (intmax_t)(off + len), x->x_len);
+	}
+}
+
+size_t
+signature_size(const struct executable *x)
+{
+	const struct pe_directory_entry *pde;
+
+	range_check(x, x->x_certificate_entry_off,
+	    x->x_certificate_entry_len, "Certificate Directory");
+
+	pde = (struct pe_directory_entry *)
+	    (x->x_buf + x->x_certificate_entry_off);
+
+	if (pde->pde_rva != 0 && pde->pde_size == 0)
+		warnx("signature size is 0, but its RVA is %d", pde->pde_rva);
+	if (pde->pde_rva == 0 && pde->pde_size != 0)
+		warnx("signature RVA is 0, but its size is %d", pde->pde_size);
+
+	return (pde->pde_size);
+}
+
+void
+show_certificate(const struct executable *x)
+{
+	struct pe_certificate *pc;
+	const struct pe_directory_entry *pde;
+
+	range_check(x, x->x_certificate_entry_off,
+	    x->x_certificate_entry_len, "Certificate Directory");
+
+	pde = (struct pe_directory_entry *)
+	    (x->x_buf + x->x_certificate_entry_off);
+
+	if (signature_size(x) == 0) {
+		printf("file not signed\n");
+		return;
+	}
+
+#if 0
+	printf("certificate chunk at offset %zd, size %zd\n",
+	    pde->pde_rva, pde->pde_size);
+#endif
+
+	range_check(x, pde->pde_rva, pde->pde_size, "Certificate chunk");
+
+	pc = (struct pe_certificate *)(x->x_buf + pde->pde_rva);
+	if (pc->pc_revision != PE_CERTIFICATE_REVISION) {
+		errx(1, "wrong certificate chunk revision, is %d, should be %d",
+		    pc->pc_revision, PE_CERTIFICATE_REVISION);
+	}
+	if (pc->pc_type != PE_CERTIFICATE_TYPE) {
+		errx(1, "wrong certificate chunk type, is %d, should be %d",
+		    pc->pc_type, PE_CERTIFICATE_TYPE);
+	}
+	printf("to dump PKCS7:\n    "
+	    "dd if='%s' bs=1 skip=%zd | openssl pkcs7 -inform DER -print\n",
+	    x->x_path, pde->pde_rva + offsetof(struct pe_certificate, pc_signature));
+	printf("to dump raw ASN.1:\n    "
+	    "openssl asn1parse -i -inform DER -offset %zd -in '%s'\n",
+	    pde->pde_rva + offsetof(struct pe_certificate, pc_signature), x->x_path);
+}
+
+static void
+parse_section_table(struct executable *x, off_t off, int number_of_sections)
+{
+	const struct pe_section_header *psh;
+	int i;
+
+	range_check(x, off, sizeof(*psh) * number_of_sections,
+	    "section table");
+
+	if (x->x_headers_len <= off + sizeof(*psh) * number_of_sections)
+		errx(1, "section table outside of headers");
+
+	psh = (const struct pe_section_header *)(x->x_buf + off);
+
+	if (number_of_sections >= MAX_SECTIONS) {
+		errx(1, "too many sections: got %d, should be %d",
+		    number_of_sections, MAX_SECTIONS);
+	}
+	x->x_nsections = number_of_sections;
+
+	for (i = 0; i < number_of_sections; i++) {
+		if (psh->psh_pointer_to_raw_data < x->x_headers_len)
+			errx(1, "section points inside the headers");
+
+		range_check(x, psh->psh_pointer_to_raw_data,
+		    psh->psh_size_of_raw_data, "section");
+#if 0
+		printf("section %d: start %d, size %d\n",
+		    i, psh->psh_pointer_to_raw_data, psh->psh_size_of_raw_data);
+#endif
+		x->x_section_off[i] = psh->psh_pointer_to_raw_data;
+		x->x_section_len[i] = psh->psh_size_of_raw_data;
+		psh++;
+	}
+}
+
+static void
+parse_directory(struct executable *x, off_t off,
+    int number_of_rva_and_sizes, int number_of_sections)
+{
+	//int i;
+	const struct pe_directory_entry *pde;
+
+	//printf("Data Directory at offset %zd\n", off);
+
+	if (number_of_rva_and_sizes <= PE_DIRECTORY_ENTRY_CERTIFICATE) {
+		errx(1, "wrong NumberOfRvaAndSizes %d; should be at least %d",
+		    number_of_rva_and_sizes, PE_DIRECTORY_ENTRY_CERTIFICATE);
+	}
+
+	range_check(x, off, sizeof(*pde) * number_of_rva_and_sizes,
+	    "PE Data Directory");
+	if (x->x_headers_len <= off + sizeof(*pde) * number_of_rva_and_sizes)
+		errx(1, "PE Data Directory outside of headers");
+
+	x->x_certificate_entry_off =
+	    off + sizeof(*pde) * PE_DIRECTORY_ENTRY_CERTIFICATE;
+	x->x_certificate_entry_len = sizeof(*pde);
+#if 0
+	printf("certificate directory entry at offset %zd, len %zd\n",
+	    x->x_certificate_entry_off, x->x_certificate_entry_len);
+
+	pde = (struct pe_directory_entry *)(x->x_buf + off);
+	for (i = 0; i < number_of_rva_and_sizes; i++) {
+		printf("rva %zd, size %zd\n", pde->pde_rva, pde->pde_size);
+		pde++;
+	}
+#endif
+
+	return (parse_section_table(x,
+	    off + sizeof(*pde) * number_of_rva_and_sizes, number_of_sections));
+}
+
+/*
+ * The PE checksum algorithm is undocumented; this code is mostly based on
+ * http://forum.sysinternals.com/optional-header-checksum-calculation_topic24214.html
+ *
+ * "Sum the entire image file, excluding the CheckSum field in the optional
+ * header, as an array of USHORTs, allowing any carry above 16 bits to be added
+ * back onto the low 16 bits. Then add the file size to get a 32-bit value."
+ *
+ * Note that most software does not care about the checksum at all; perhaps
+ * we could just set it to 0 instead.
+ *
+ * XXX: Endianness?
+ */
+static uint32_t
+compute_checksum(const struct executable *x)
+{
+	uint32_t cksum = 0;
+	uint16_t tmp;
+	int i;
+
+	range_check(x, x->x_checksum_off, x->x_checksum_len, "PE checksum");
+
+	assert(x->x_checksum_off % 2 == 0);
+
+	for (i = 0; i + sizeof(tmp) < x->x_len; i += 2) {
+		/*
+		 * Don't checksum the checksum.  The +2 is because the checksum
+		 * is 4 bytes, and here we're iterating over 2 byte chunks.
+		 */
+		if (i == x->x_checksum_off || i == x->x_checksum_off + 2) {
+			tmp = 0;
+		} else {
+			assert(i + sizeof(tmp) <= x->x_len);
+			memcpy(&tmp, x->x_buf + i, sizeof(tmp));
+		}
+
+		cksum += tmp;
+		cksum += cksum >> 16;
+		cksum &= 0xffff;
+	}
+
+	cksum += cksum >> 16;
+	cksum &= 0xffff;
+
+	cksum += x->x_len;
+
+	return (cksum);
+}
+
+static void
+parse_optional_32_plus(struct executable *x, off_t off,
+    int number_of_sections)
+{
+#if 0
+	uint32_t computed_checksum;
+#endif
+	const struct pe_optional_header_32_plus	*po;
+
+	range_check(x, off, sizeof(*po), "PE Optional Header");
+
+	po = (struct pe_optional_header_32_plus *)(x->x_buf + off);
+	switch (po->po_subsystem) {
+	case PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION:
+	case PE_OPTIONAL_SUBSYSTEM_EFI_BOOT:
+	case PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME:
+		break;
+	default:
+		errx(1, "wrong PE Optional Header subsystem 0x%x",
+		    po->po_subsystem);
+	}
+
+#if 0
+	printf("subsystem %d, checksum 0x%x, %d data directories\n",
+	    po->po_subsystem, po->po_checksum, po->po_number_of_rva_and_sizes);
+#endif
+
+	x->x_checksum_off = off +
+	    offsetof(struct pe_optional_header_32_plus, po_checksum);
+	x->x_checksum_len = sizeof(po->po_checksum);
+#if 0
+	printf("checksum 0x%x at offset %zd, len %zd\n",
+	    po->po_checksum, x->x_checksum_off, x->x_checksum_len);
+
+	computed_checksum = compute_checksum(x);
+	if (computed_checksum != po->po_checksum) {
+		warnx("invalid PE+ checksum; is 0x%x, should be 0x%x",
+		    po->po_checksum, computed_checksum);
+	}
+#endif
+
+	if (x->x_len < x->x_headers_len)
+		errx(1, "invalid SizeOfHeaders %d", po->po_size_of_headers);
+	x->x_headers_len = po->po_size_of_headers;
+	//printf("Size of Headers: %d\n", po->po_size_of_headers);
+
+	return (parse_directory(x, off + sizeof(*po),
+	    po->po_number_of_rva_and_sizes, number_of_sections));
+}
+
+static void
+parse_optional_32(struct executable *x, off_t off, int number_of_sections)
+{
+#if 0
+	uint32_t computed_checksum;
+#endif
+	const struct pe_optional_header_32 *po;
+
+	range_check(x, off, sizeof(*po), "PE Optional Header");
+
+	po = (struct pe_optional_header_32 *)(x->x_buf + off);
+	switch (po->po_subsystem) {
+	case PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION:
+	case PE_OPTIONAL_SUBSYSTEM_EFI_BOOT:
+	case PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME:
+		break;
+	default:
+		errx(1, "wrong PE Optional Header subsystem 0x%x",
+		    po->po_subsystem);
+	}
+
+#if 0
+	printf("subsystem %d, checksum 0x%x, %d data directories\n",
+	    po->po_subsystem, po->po_checksum, po->po_number_of_rva_and_sizes);
+#endif
+
+	x->x_checksum_off = off +
+	    offsetof(struct pe_optional_header_32, po_checksum);
+	x->x_checksum_len = sizeof(po->po_checksum);
+#if 0
+	printf("checksum at offset %zd, len %zd\n",
+	    x->x_checksum_off, x->x_checksum_len);
+
+	computed_checksum = compute_checksum(x);
+	if (computed_checksum != po->po_checksum) {
+		warnx("invalid PE checksum; is 0x%x, should be 0x%x",
+		    po->po_checksum, computed_checksum);
+	}
+#endif
+
+	if (x->x_len < x->x_headers_len)
+		errx(1, "invalid SizeOfHeaders %d", po->po_size_of_headers);
+	x->x_headers_len = po->po_size_of_headers;
+	//printf("Size of Headers: %d\n", po->po_size_of_headers);
+
+	return (parse_directory(x, off + sizeof(*po),
+	    po->po_number_of_rva_and_sizes, number_of_sections));
+}
+
+static void
+parse_optional(struct executable *x, off_t off, int number_of_sections)
+{
+	const struct pe_optional_header_32 *po;
+
+	//printf("Optional header offset %zd\n", off);
+
+	range_check(x, off, sizeof(*po), "PE Optional Header");
+
+	po = (struct pe_optional_header_32 *)(x->x_buf + off);
+
+	switch (po->po_magic) {
+	case PE_OPTIONAL_MAGIC_32:
+		return (parse_optional_32(x, off, number_of_sections));
+	case PE_OPTIONAL_MAGIC_32_PLUS:
+		return (parse_optional_32_plus(x, off, number_of_sections));
+	default:
+		errx(1, "wrong PE Optional Header magic 0x%x", po->po_magic);
+	}
+}
+
+static void
+parse_pe(struct executable *x, off_t off)
+{
+	const struct pe_header *pe;
+
+	//printf("PE offset %zd, PE size %zd\n", off, sizeof(*pe));
+
+	range_check(x, off, sizeof(*pe), "PE header");
+
+	pe = (struct pe_header *)(x->x_buf + off);
+	if (pe->pe_signature != PE_SIGNATURE)
+		errx(1, "wrong PE signature 0x%x", pe->pe_signature);
+
+	//printf("Number of sections: %d\n", pe->pe_coff.coff_number_of_sections);
+
+	parse_optional(x, off + sizeof(*pe),
+	    pe->pe_coff.coff_number_of_sections);
+}
+
+void
+parse(struct executable *x)
+{
+	const struct mz_header *mz;
+
+	range_check(x, 0, sizeof(*mz), "MZ header");
+
+	mz = (struct mz_header *)x->x_buf;
+	if (mz->mz_signature[0] != 'M' || mz->mz_signature[1] != 'Z')
+		errx(1, "MZ header not found");
+
+	return (parse_pe(x, mz->mz_lfanew));
+}
+
+static off_t
+append(struct executable *x, void *ptr, size_t len)
+{
+	off_t off;
+
+	/*
+	 * XXX: Alignment.
+	 */
+	off = x->x_len;
+	x->x_buf = realloc(x->x_buf, x->x_len + len);
+	if (x->x_buf == NULL)
+		err(1, "realloc");
+	memcpy(x->x_buf + x->x_len, ptr, len);
+	x->x_len += len;
+
+	return (off);
+}
+
+void
+update(struct executable *x)
+{
+	uint32_t checksum;
+	struct pe_certificate *pc;
+	struct pe_directory_entry pde;
+	size_t pc_len;
+	off_t pc_off;
+
+	pc_len = sizeof(*pc) + x->x_signature_len;
+	pc = calloc(1, pc_len);
+	if (pc == NULL)
+		err(1, "calloc");
+
+#if 0
+	/*
+	 * Note that pc_len is the length of pc_certificate,
+	 * not the whole structure.
+	 *
+	 * XXX: That's what the spec says - but it breaks at least
+	 *      sbverify and "pesign -S", so the spec is probably wrong.
+	 */
+	pc->pc_len = x->x_signature_len;
+#else
+	pc->pc_len = pc_len;
+#endif
+	pc->pc_revision = PE_CERTIFICATE_REVISION;
+	pc->pc_type = PE_CERTIFICATE_TYPE;
+	memcpy(&pc->pc_signature, x->x_signature, x->x_signature_len);
+
+	pc_off = append(x, pc, pc_len);
+#if 0
+	printf("added signature chunk at offset %zd, len %zd\n",
+	    pc_off, pc_len);
+#endif
+
+	free(pc);
+
+	pde.pde_rva = pc_off;
+	pde.pde_size = pc_len;
+	memcpy(x->x_buf + x->x_certificate_entry_off, &pde, sizeof(pde));
+
+	checksum = compute_checksum(x);
+	assert(sizeof(checksum) == x->x_checksum_len);
+	memcpy(x->x_buf + x->x_checksum_off, &checksum, sizeof(checksum));
+#if 0
+	printf("new checksum 0x%x\n", checksum);
+#endif
+}
diff --git a/usr.sbin/uefisign/uefisign.8 b/usr.sbin/uefisign/uefisign.8
new file mode 100644
index 0000000..3c9a9e9
--- /dev/null
+++ b/usr.sbin/uefisign/uefisign.8
@@ -0,0 +1,93 @@
+.\" Copyright (c) 2014 The FreeBSD Foundation
+.\" All rights reserved.
+.\"
+.\" This software was developed by Edward Tomasz Napierala under sponsorship
+.\" from the FreeBSD Foundation.
+.\"
+.\" 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 AUTHORS 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 AUTHORS 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: head/usr.sbin/uefisign/uefisign.8 285671 2015-07-18 12:03:17Z trasz $
+.\"
+.Dd July 11, 2015
+.Dt UEFISIGN 8
+.Os
+.Sh NAME
+.Nm uefisign
+.Nd UEFI Secure Boot signing utility
+.Sh SYNOPSIS
+.Nm
+.Fl k Ar key
+.Fl c Ar certificate
+.Fl o Ar output
+.Op Fl v
+.Ar file
+.Nm
+.Fl V
+.Op Fl v
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility signs PE binary files using Authenticode scheme, as required by
+UEFI Secure Boot specification.
+Alternatively, it can be used to view and verify existing signatures.
+These options are available:
+.Bl -tag -width ".Fl l"
+.It Fl V
+Determine whether the file is signed.
+Note that this does not verify the correctness of the signature;
+only that the file contains a signature.
+.It Fl k
+Name of file containing the private key used to sign the binary.
+.It Fl c
+Name of file containing the certificate used to sign the binary.
+.It Fl o
+Name of file to write the signed binary to.
+.It Fl v
+Be verbose.
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh EXAMPLES
+Generate self-signed certificate and use it to sign a binary:
+.Dl /usr/share/examples/uefisign/uefikeys testcert
+.Dl uefisign -c testcert.pem -k testcert.key -o signed-binary binary
+.Pp
+View signature:
+.Dl uefisign -Vv binary
+.Sh SEE ALSO
+.Xr openssl 1 ,
+.Xr loader 8 ,
+.Xr uefi 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 10.2 .
+.Sh AUTHORS
+The
+.Nm
+utility was developed by
+.An Edward Tomasz Napierala Aq Mt trasz@FreeBSD.org
+under sponsorship from the FreeBSD Foundation.
diff --git a/usr.sbin/uefisign/uefisign.c b/usr.sbin/uefisign/uefisign.c
new file mode 100644
index 0000000..390eab5
--- /dev/null
+++ b/usr.sbin/uefisign/uefisign.c
@@ -0,0 +1,425 @@
+/*-
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * 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: head/usr.sbin/uefisign/uefisign.c 279315 2015-02-26 09:15:24Z trasz $");
+
+#include <sys/wait.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/conf.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs7.h>
+
+#include "uefisign.h"
+#include "magic.h"
+
+static void
+usage(void)
+{
+
+	fprintf(stderr, "usage: uefisign -c cert -k key -o outfile [-v] file\n"
+			"       uefisign -V [-c cert] [-v] file\n");
+	exit(1);
+}
+
+static char *
+checked_strdup(const char *s)
+{
+	char *c;
+
+	c = strdup(s);
+	if (c == NULL)
+		err(1, "strdup");
+	return (c);
+}
+
+FILE *
+checked_fopen(const char *path, const char *mode)
+{
+	FILE *fp;
+
+	assert(path != NULL);
+
+	fp = fopen(path, mode);
+	if (fp == NULL)
+		err(1, "%s", path);
+	return (fp);
+}
+
+void
+send_chunk(const void *buf, size_t len, int pipefd)
+{
+	ssize_t ret;
+
+	ret = write(pipefd, &len, sizeof(len));
+	if (ret != sizeof(len))
+		err(1, "write");
+	ret = write(pipefd, buf, len);
+	if (ret != (ssize_t)len)
+		err(1, "write");
+}
+
+void
+receive_chunk(void **bufp, size_t *lenp, int pipefd)
+{
+	ssize_t ret;
+	size_t len;
+	void *buf;
+
+	ret = read(pipefd, &len, sizeof(len));
+	if (ret != sizeof(len))
+		err(1, "read");
+
+	buf = calloc(1, len);
+	if (buf == NULL)
+		err(1, "calloc");
+
+	ret = read(pipefd, buf, len);
+	if (ret != (ssize_t)len)
+		err(1, "read");
+
+	*bufp = buf;
+	*lenp = len;
+}
+
+static char *
+bin2hex(const char *bin, size_t bin_len)
+{
+	unsigned char *hex, *tmp, ch;
+	size_t hex_len;
+	size_t i;
+
+	hex_len = bin_len * 2 + 1; /* +1 for '\0'. */
+	hex = malloc(hex_len);
+	if (hex == NULL)
+		err(1, "malloc");
+
+	tmp = hex;
+	for (i = 0; i < bin_len; i++) {
+		ch = bin[i];
+		tmp += sprintf(tmp, "%02x", ch);
+	}
+
+	return (hex);
+}
+
+/*
+ * We need to replace a standard chunk of PKCS7 signature with one mandated
+ * by Authenticode.  Problem is, replacing it just like that and then calling
+ * PKCS7_final() would make OpenSSL segfault somewhere in PKCS7_dataFinal().
+ * So, instead, we call PKCS7_dataInit(), then put our Authenticode-specific
+ * data into BIO it returned, then call PKCS7_dataFinal() - which now somehow
+ * does not panic - and _then_ we replace it in the signature.  This technique
+ * was used in sbsigntool by Jeremy Kerr, and might have originated in
+ * osslsigncode.
+ */
+static void
+magic(PKCS7 *pkcs7, const char *digest, size_t digest_len)
+{
+	BIO *bio, *t_bio;
+	ASN1_TYPE *t;
+	ASN1_STRING *s;
+	CONF *cnf;
+	unsigned char *buf, *tmp;
+	char *digest_hex, *magic_conf, *str;
+	int len, nid, ok;
+
+	digest_hex = bin2hex(digest, digest_len);
+
+	/*
+	 * Construct the SpcIndirectDataContent chunk.
+	 */
+	nid = OBJ_create("1.3.6.1.4.1.311.2.1.4", NULL, NULL);
+
+	asprintf(&magic_conf, magic_fmt, digest_hex);
+	if (magic_conf == NULL)
+		err(1, "asprintf");
+
+	bio = BIO_new_mem_buf((void *)magic_conf, -1);
+	if (bio == NULL) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "BIO_new_mem_buf(3) failed");
+	}
+
+	cnf = NCONF_new(NULL);
+	if (cnf == NULL) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "NCONF_new(3) failed");
+	}
+
+	ok = NCONF_load_bio(cnf, bio, NULL);
+	if (ok == 0) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "NCONF_load_bio(3) failed");
+	}
+
+	str = NCONF_get_string(cnf, "default", "asn1");
+	if (str == NULL) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "NCONF_get_string(3) failed");
+	}
+
+	t = ASN1_generate_nconf(str, cnf);
+	if (t == NULL) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "ASN1_generate_nconf(3) failed");
+	}
+
+	/*
+	 * We now have our proprietary piece of ASN.1.  Let's do
+	 * the actual signing.
+	 */
+	len = i2d_ASN1_TYPE(t, NULL);
+	tmp = buf = calloc(1, len);
+	if (tmp == NULL)
+		err(1, "calloc");
+	i2d_ASN1_TYPE(t, &tmp);
+
+	/*
+	 * We now have contents of 't' stuffed into memory buffer 'buf'.
+	 */
+	tmp = NULL;
+	t = NULL;
+
+	t_bio = PKCS7_dataInit(pkcs7, NULL);
+	if (t_bio == NULL) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "PKCS7_dataInit(3) failed");
+	}
+
+	BIO_write(t_bio, buf + 2, len - 2);
+
+	ok = PKCS7_dataFinal(pkcs7, t_bio);
+	if (ok == 0) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "PKCS7_dataFinal(3) failed");
+	}
+
+	t = ASN1_TYPE_new();
+	s = ASN1_STRING_new();
+	ASN1_STRING_set(s, buf, len);
+	ASN1_TYPE_set(t, V_ASN1_SEQUENCE, s);
+
+	PKCS7_set0_type_other(pkcs7->d.sign->contents, nid, t);
+}
+
+static void
+sign(X509 *cert, EVP_PKEY *key, int pipefd)
+{
+	PKCS7 *pkcs7;
+	BIO *bio, *out;
+	const EVP_MD *md;
+	PKCS7_SIGNER_INFO *info;
+	void *digest, *signature;
+	size_t digest_len, signature_len;
+	int ok;
+
+	assert(cert != NULL);
+	assert(key != NULL);
+
+	receive_chunk(&digest, &digest_len, pipefd);
+
+	bio = BIO_new_mem_buf(digest, digest_len);
+	if (bio == NULL) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "BIO_new_mem_buf(3) failed");
+	}
+
+	pkcs7 = PKCS7_sign(NULL, NULL, NULL, bio, PKCS7_BINARY | PKCS7_PARTIAL);
+	if (pkcs7 == NULL) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "PKCS7_sign(3) failed");
+	}
+
+	md = EVP_get_digestbyname(DIGEST);
+	if (md == NULL) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "EVP_get_digestbyname(\"%s\") failed", DIGEST);
+	}
+
+	info = PKCS7_sign_add_signer(pkcs7, cert, key, md, 0);
+	if (info == NULL) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "PKCS7_sign_add_signer(3) failed");
+	}
+
+	/*
+	 * XXX: All the signed binaries seem to have this, but where is it
+	 *      described in the spec?
+	 */
+	PKCS7_add_signed_attribute(info, NID_pkcs9_contentType,
+	    V_ASN1_OBJECT, OBJ_txt2obj("1.3.6.1.4.1.311.2.1.4", 1));
+
+	magic(pkcs7, digest, digest_len);
+
+#if 0
+	out = BIO_new(BIO_s_file());
+	BIO_set_fp(out, stdout, BIO_NOCLOSE);
+	PKCS7_print_ctx(out, pkcs7, 0, NULL);
+
+	i2d_PKCS7_bio(out, pkcs7);
+#endif
+
+	out = BIO_new(BIO_s_mem());
+	if (out == NULL) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "BIO_new(3) failed");
+	}
+
+	ok = i2d_PKCS7_bio(out, pkcs7);
+	if (ok == 0) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "i2d_PKCS7_bio(3) failed");
+	}
+
+	signature_len = BIO_get_mem_data(out, &signature);
+	if (signature_len <= 0) {
+		ERR_print_errors_fp(stderr);
+		errx(1, "BIO_get_mem_data(3) failed");
+	}
+
+	(void)BIO_set_close(out, BIO_NOCLOSE);
+	BIO_free(out);
+
+	send_chunk(signature, signature_len, pipefd);
+}
+
+static int
+wait_for_child(pid_t pid)
+{
+	int status;
+
+	pid = waitpid(pid, &status, 0);
+	if (pid == -1)
+		err(1, "waitpid");
+
+	return (WEXITSTATUS(status));
+}
+
+int
+main(int argc, char **argv)
+{
+	int ch, error;
+	bool Vflag = false, vflag = false;
+	const char *certpath = NULL, *keypath = NULL, *outpath = NULL, *inpath = NULL;
+	FILE *certfp = NULL, *keyfp = NULL;
+	X509 *cert = NULL;
+	EVP_PKEY *key = NULL;
+	pid_t pid;
+	int pipefds[2];
+
+	while ((ch = getopt(argc, argv, "Vc:k:o:v")) != -1) {
+		switch (ch) {
+		case 'V':
+			Vflag = true;
+			break;
+		case 'c':
+			certpath = checked_strdup(optarg);
+			break;
+		case 'k':
+			keypath = checked_strdup(optarg);
+			break;
+		case 'o':
+			outpath = checked_strdup(optarg);
+			break;
+		case 'v':
+			vflag = true;
+			break;
+		default:
+			usage();
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	if (argc != 1)
+		usage();
+
+	if (Vflag) {
+		if (certpath != NULL)
+			errx(1, "-V and -c are mutually exclusive");
+		if (keypath != NULL)
+			errx(1, "-V and -k are mutually exclusive");
+		if (outpath != NULL)
+			errx(1, "-V and -o are mutually exclusive");
+	} else {
+		if (certpath == NULL)
+			errx(1, "-c option is mandatory");
+		if (keypath == NULL)
+			errx(1, "-k option is mandatory");
+		if (outpath == NULL)
+			errx(1, "-o option is mandatory");
+	}
+
+	inpath = argv[0];
+
+	OPENSSL_config(NULL);
+	ERR_load_crypto_strings();
+	OpenSSL_add_all_algorithms();
+
+	error = pipe(pipefds);
+	if (error != 0)
+		err(1, "pipe");
+
+	pid = fork();
+	if (pid < 0)
+		err(1, "fork");
+
+	if (pid == 0)
+		return (child(inpath, outpath, pipefds[1], Vflag, vflag));
+
+	if (!Vflag) {
+		certfp = checked_fopen(certpath, "r");
+		cert = PEM_read_X509(certfp, NULL, NULL, NULL);
+		if (cert == NULL) {
+			ERR_print_errors_fp(stderr);
+			errx(1, "failed to load certificate from %s", certpath);
+		}
+
+		keyfp = checked_fopen(keypath, "r");
+		key = PEM_read_PrivateKey(keyfp, NULL, NULL, NULL);
+		if (key == NULL) {
+			ERR_print_errors_fp(stderr);
+			errx(1, "failed to load private key from %s", keypath);
+		}
+
+		sign(cert, key, pipefds[0]);
+	}
+
+	return (wait_for_child(pid));
+}
diff --git a/usr.sbin/uefisign/uefisign.h b/usr.sbin/uefisign/uefisign.h
new file mode 100644
index 0000000..ac0bdf0
--- /dev/null
+++ b/usr.sbin/uefisign/uefisign.h
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * 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: head/usr.sbin/uefisign/uefisign.h 279315 2015-02-26 09:15:24Z trasz $
+ */
+
+#ifndef EFISIGN_H
+#define	EFISIGN_H
+
+#include <stdbool.h>
+#include <openssl/evp.h>
+
+#define	DIGEST		"SHA256"
+#define	MAX_SECTIONS	128
+
+struct executable {
+	const char	*x_path;
+	FILE		*x_fp;
+
+	char		*x_buf;
+	size_t		x_len;
+
+	/*
+	 * Set by pe_parse(), used by digest().
+	 */
+	size_t		x_headers_len;
+
+	off_t		x_checksum_off;
+	size_t		x_checksum_len;
+
+	off_t		x_certificate_entry_off;
+	size_t		x_certificate_entry_len;
+
+	int		x_nsections;
+	off_t		x_section_off[MAX_SECTIONS];
+	size_t		x_section_len[MAX_SECTIONS];
+
+	/*
+	 * Computed by digest().
+	 */
+	unsigned char	x_digest[EVP_MAX_MD_SIZE];
+	unsigned int	x_digest_len;
+
+	/*
+	 * Received from the parent process, which computes it in sign().
+	 */
+	void		*x_signature;
+	size_t		x_signature_len;
+};
+
+
+FILE	*checked_fopen(const char *path, const char *mode);
+void	send_chunk(const void *buf, size_t len, int pipefd);
+void	receive_chunk(void **bufp, size_t *lenp, int pipefd);
+
+int	child(const char *inpath, const char *outpath, int pipefd,
+	    bool Vflag, bool vflag);
+
+void	parse(struct executable *x);
+void	update(struct executable *x);
+size_t	signature_size(const struct executable *x);
+void	show_certificate(const struct executable *x);
+void	range_check(const struct executable *x,
+	    off_t off, size_t len, const char *name);
+
+#endif /* !EFISIGN_H */
-- 
2.9.2

