Module Name:    src
Committed By:   jmcneill
Date:           Sun Oct 10 13:03:10 UTC 2021

Modified Files:
        src/distrib/sets/lists/comp: mi
        src/etc: MAKEDEV.tmpl
        src/etc/etc.evbarm: MAKEDEV.conf
        src/sys/arch/arm/arm: efi_runtime.c efi_runtime.h
        src/sys/arch/arm/fdt: arm_fdt.c
        src/sys/arch/evbarm/conf: GENERIC64
        src/sys/conf: majors
        src/sys/dev: files.dev
        src/sys/sys: Makefile
Added Files:
        src/sys/dev: efi.c efivar.h
        src/sys/sys: efiio.h

Log Message:
efi: Add /dev/efi character device

Introduce a /dev/efi character device that provides a means for accessing
UEFI RT variable services from userland. Compatible with the FreeBSD ioctl
interface for ease of porting their libefivar and associated tools.

The ioctl interface is defined in sys/efiio.h.

To enable support for this on an arch, the kernel needs `pseudo-device efi`
and the MD EFI implementation needs to register its backend by calling
efi_ops_register(). This commit includes an implementation for Arm.


To generate a diff of this commit:
cvs rdiff -u -r1.2395 -r1.2396 src/distrib/sets/lists/comp/mi
cvs rdiff -u -r1.224 -r1.225 src/etc/MAKEDEV.tmpl
cvs rdiff -u -r1.21 -r1.22 src/etc/etc.evbarm/MAKEDEV.conf
cvs rdiff -u -r1.5 -r1.6 src/sys/arch/arm/arm/efi_runtime.c
cvs rdiff -u -r1.3 -r1.4 src/sys/arch/arm/arm/efi_runtime.h
cvs rdiff -u -r1.19 -r1.20 src/sys/arch/arm/fdt/arm_fdt.c
cvs rdiff -u -r1.186 -r1.187 src/sys/arch/evbarm/conf/GENERIC64
cvs rdiff -u -r1.98 -r1.99 src/sys/conf/majors
cvs rdiff -u -r0 -r1.1 src/sys/dev/efi.c src/sys/dev/efivar.h
cvs rdiff -u -r1.7 -r1.8 src/sys/dev/files.dev
cvs rdiff -u -r1.179 -r1.180 src/sys/sys/Makefile
cvs rdiff -u -r0 -r1.1 src/sys/sys/efiio.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/comp/mi
diff -u src/distrib/sets/lists/comp/mi:1.2395 src/distrib/sets/lists/comp/mi:1.2396
--- src/distrib/sets/lists/comp/mi:1.2395	Thu Sep 30 02:00:19 2021
+++ src/distrib/sets/lists/comp/mi	Sun Oct 10 13:03:09 2021
@@ -1,4 +1,4 @@
-#	$NetBSD: mi,v 1.2395 2021/09/30 02:00:19 yamaguchi Exp $
+#	$NetBSD: mi,v 1.2396 2021/10/10 13:03:09 jmcneill Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 ./etc/mtree/set.comp				comp-sys-root
@@ -3273,6 +3273,7 @@
 ./usr/include/sys/domain.h			comp-c-include
 ./usr/include/sys/drvctlio.h			comp-c-include
 ./usr/include/sys/dvdio.h			comp-c-include
+./usr/include/sys/efiio.h			comp-c-include
 ./usr/include/sys/elfdefinitions.h		comp-c-include
 ./usr/include/sys/endian.h			comp-c-include
 ./usr/include/sys/envsys.h			comp-c-include

Index: src/etc/MAKEDEV.tmpl
diff -u src/etc/MAKEDEV.tmpl:1.224 src/etc/MAKEDEV.tmpl:1.225
--- src/etc/MAKEDEV.tmpl:1.224	Sat Jul 24 11:39:18 2021
+++ src/etc/MAKEDEV.tmpl	Sun Oct 10 13:03:08 2021
@@ -1,5 +1,5 @@
 #!/bin/sh -
-#	$NetBSD: MAKEDEV.tmpl,v 1.224 2021/07/24 11:39:18 jmcneill Exp $
+#	$NetBSD: MAKEDEV.tmpl,v 1.225 2021/10/10 13:03:08 jmcneill Exp $
 #
 # Copyright (c) 2003,2007,2008 The NetBSD Foundation, Inc.
 # All rights reserved.
@@ -2244,6 +2244,10 @@ smbios)
 	mkdev smbios c %smbios_chr% 0
 	;;
 
+efi)
+	mkdev efi c %efi_chr% 0 660
+	;;
+
 midevend)
 %MI_DEVICES_END%
 local)

Index: src/etc/etc.evbarm/MAKEDEV.conf
diff -u src/etc/etc.evbarm/MAKEDEV.conf:1.21 src/etc/etc.evbarm/MAKEDEV.conf:1.22
--- src/etc/etc.evbarm/MAKEDEV.conf:1.21	Tue Nov 10 08:52:36 2020
+++ src/etc/etc.evbarm/MAKEDEV.conf	Sun Oct 10 13:03:09 2021
@@ -1,4 +1,4 @@
-# $NetBSD: MAKEDEV.conf,v 1.21 2020/11/10 08:52:36 rin Exp $
+# $NetBSD: MAKEDEV.conf,v 1.22 2021/10/10 13:03:09 jmcneill Exp $
 
 all_md)
 	makedev wscons fd0 fd1 wd0 wd1 wd2 wd3 sd0 sd1 sd2 sd3
@@ -27,6 +27,7 @@ all_md)
 	makedev spiflash0
 	makedev bpf
 	makedev openfirm
+	makedev acpi smbios efi
 	;;
 
 ramdisk|floppy)

Index: src/sys/arch/arm/arm/efi_runtime.c
diff -u src/sys/arch/arm/arm/efi_runtime.c:1.5 src/sys/arch/arm/arm/efi_runtime.c:1.6
--- src/sys/arch/arm/arm/efi_runtime.c:1.5	Fri Dec 18 07:40:27 2020
+++ src/sys/arch/arm/arm/efi_runtime.c	Sun Oct 10 13:03:09 2021
@@ -1,4 +1,4 @@
-/* $NetBSD: efi_runtime.c,v 1.5 2020/12/18 07:40:27 skrll Exp $ */
+/* $NetBSD: efi_runtime.c,v 1.6 2021/10/10 13:03:09 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2018 The NetBSD Foundation, Inc.
@@ -29,8 +29,10 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include "efi.h"
+
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: efi_runtime.c,v 1.5 2020/12/18 07:40:27 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: efi_runtime.c,v 1.6 2021/10/10 13:03:09 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/mutex.h>
@@ -38,12 +40,33 @@ __KERNEL_RCSID(0, "$NetBSD: efi_runtime.
 
 #include <uvm/uvm_extern.h>
 
+#include <dev/efivar.h>
+
 #include <arm/arm/efi_runtime.h>
 
+#ifdef _LP64
+#define	EFIERR(x)	(0x8000000000000000 | x)
+#else
+#define	EFIERR(x)	(0x80000000 | x)
+#endif
+
+#define	EFI_UNSUPPORTED		EFIERR(3)
+#define	EFI_DEVICE_ERROR	EFIERR(7)
+
 static kmutex_t efi_lock;
 
 static struct efi_rt *RT = NULL;
 
+#if NEFI > 0 && BYTE_ORDER == LITTLE_ENDIAN
+static const struct efi_ops arm_efi_ops = {
+	.efi_gettime	= arm_efirt_gettime,
+	.efi_settime	= arm_efirt_settime,
+	.efi_getvar	= arm_efirt_getvar,
+	.efi_setvar	= arm_efirt_setvar,
+	.efi_nextvar	= arm_efirt_nextvar,
+};
+#endif
+
 int
 arm_efirt_init(paddr_t efi_system_table)
 {
@@ -75,6 +98,8 @@ arm_efirt_init(paddr_t efi_system_table)
 	RT = ST->st_rt;
 	mutex_init(&efi_lock, MUTEX_DEFAULT, IPL_HIGH);
 
+	efi_register_ops(&arm_efi_ops);
+
 	return 0;
 #else
 	/* EFI runtime not supported in big endian mode */
@@ -82,42 +107,101 @@ arm_efirt_init(paddr_t efi_system_table)
 #endif
 }
 
-int
-arm_efirt_gettime(struct efi_tm *tm)
+efi_status
+arm_efirt_gettime(struct efi_tm *tm, struct efi_tmcap *tmcap)
 {
-	int error;
+	efi_status status = EFI_DEVICE_ERROR;
 
-	if (RT == NULL || RT->rt_gettime == NULL)
-		return ENXIO;
+	if (RT == NULL || RT->rt_gettime == NULL) {
+		return EFI_UNSUPPORTED;
+	}
 
 	mutex_enter(&efi_lock);
-	if ((error = arm_efirt_md_enter()) == 0) {
-		if (RT->rt_gettime(tm, NULL) != 0)
-			error = EIO;
+	if (arm_efirt_md_enter() == 0) {
+		status = RT->rt_gettime(tm, tmcap);
 	}
 	arm_efirt_md_exit();
 	mutex_exit(&efi_lock);
 
-	return error;
+	return status;
 }
 
-int
+efi_status
 arm_efirt_settime(struct efi_tm *tm)
 {
-	int error;
+	efi_status status = EFI_DEVICE_ERROR;
 
-	if (RT == NULL || RT->rt_settime == NULL)
-		return ENXIO;
+	if (RT == NULL || RT->rt_settime == NULL) {
+		return EFI_UNSUPPORTED;
+	}
 
 	mutex_enter(&efi_lock);
-	if ((error = arm_efirt_md_enter()) == 0) {
-		if (RT->rt_settime(tm) != 0)
-			error = EIO;
+	if (arm_efirt_md_enter() == 0) {
+		status = RT->rt_settime(tm);
 	}
 	arm_efirt_md_exit();
 	mutex_exit(&efi_lock);
 
-	return error;
+	return status;
+}
+
+efi_status
+arm_efirt_getvar(uint16_t *name, struct uuid *vendor, uint32_t *attrib,
+    u_long *datasize, void *data)
+{
+	efi_status status = EFI_DEVICE_ERROR;
+
+	if (RT == NULL || RT->rt_getvar == NULL) {
+		return EFI_UNSUPPORTED;
+	}
+
+	mutex_enter(&efi_lock);
+	if (arm_efirt_md_enter() == 0) {
+		status = RT->rt_getvar(name, vendor, attrib, datasize, data);
+	}
+	arm_efirt_md_exit();
+	mutex_exit(&efi_lock);
+
+	return status;
+}
+
+efi_status
+arm_efirt_nextvar(u_long *namesize, efi_char *name, struct uuid *vendor)
+{
+	efi_status status = EFI_DEVICE_ERROR;
+
+	if (RT == NULL || RT->rt_scanvar == NULL) {
+		return EFI_UNSUPPORTED;
+	}
+
+	mutex_enter(&efi_lock);
+	if (arm_efirt_md_enter() == 0) {
+		status = RT->rt_scanvar(namesize, name, vendor);
+	}
+	arm_efirt_md_exit();
+	mutex_exit(&efi_lock);
+
+	return status;
+}
+
+efi_status
+arm_efirt_setvar(uint16_t *name, struct uuid *vendor, uint32_t attrib,
+    u_long datasize, void *data)
+{
+	efi_status status = EFI_DEVICE_ERROR;
+
+	if (RT == NULL || RT->rt_setvar == NULL) {
+		return EFI_UNSUPPORTED;
+	}
+
+	mutex_enter(&efi_lock);
+	if (arm_efirt_md_enter() == 0) {
+		status = RT->rt_setvar(name, vendor, attrib, datasize, data);
+	}
+	arm_efirt_md_exit();
+	mutex_exit(&efi_lock);
+
+	return status;
 }
 
 int
@@ -133,8 +217,9 @@ arm_efirt_reset(enum efi_reset type)
 	if (reset_called == false) {
 		reset_called = true;
 		if ((error = arm_efirt_md_enter()) == 0) {
-			if (RT->rt_reset(type, 0, 0, NULL) != 0)
+			if (RT->rt_reset(type, 0, 0, NULL) != 0) {
 				error = EIO;
+			}
 		}
 		arm_efirt_md_exit();
 	} else {

Index: src/sys/arch/arm/arm/efi_runtime.h
diff -u src/sys/arch/arm/arm/efi_runtime.h:1.3 src/sys/arch/arm/arm/efi_runtime.h:1.4
--- src/sys/arch/arm/arm/efi_runtime.h:1.3	Mon Dec 16 00:03:50 2019
+++ src/sys/arch/arm/arm/efi_runtime.h	Sun Oct 10 13:03:09 2021
@@ -1,4 +1,4 @@
-/* $NetBSD: efi_runtime.h,v 1.3 2019/12/16 00:03:50 jmcneill Exp $ */
+/* $NetBSD: efi_runtime.h,v 1.4 2021/10/10 13:03:09 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2018 The NetBSD Foundation, Inc.
@@ -35,8 +35,13 @@
 #include <arm/efi.h>
 
 int		arm_efirt_init(paddr_t);
-int		arm_efirt_gettime(struct efi_tm *);
-int		arm_efirt_settime(struct efi_tm *);
+efi_status	arm_efirt_gettime(struct efi_tm *, struct efi_tmcap *);
+efi_status	arm_efirt_settime(struct efi_tm *);
+efi_status	arm_efirt_getvar(uint16_t *, struct uuid *, uint32_t *,
+				 u_long *, void *);
+efi_status	arm_efirt_nextvar(u_long *, uint16_t *, struct uuid *);
+efi_status	arm_efirt_setvar(uint16_t *, struct uuid *, uint32_t,
+				 u_long, void *);
 int		arm_efirt_reset(enum efi_reset);
 
 enum arm_efirt_mem_type {

Index: src/sys/arch/arm/fdt/arm_fdt.c
diff -u src/sys/arch/arm/fdt/arm_fdt.c:1.19 src/sys/arch/arm/fdt/arm_fdt.c:1.20
--- src/sys/arch/arm/fdt/arm_fdt.c:1.19	Sun Sep  5 13:20:34 2021
+++ src/sys/arch/arm/fdt/arm_fdt.c	Sun Oct 10 13:03:09 2021
@@ -1,4 +1,4 @@
-/* $NetBSD: arm_fdt.c,v 1.19 2021/09/05 13:20:34 jmcneill Exp $ */
+/* $NetBSD: arm_fdt.c,v 1.20 2021/10/10 13:03:09 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2017 Jared D. McNeill <jmcne...@invisible.ca>
@@ -31,7 +31,7 @@
 #include "opt_modular.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: arm_fdt.c,v 1.19 2021/09/05 13:20:34 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: arm_fdt.c,v 1.20 2021/10/10 13:03:09 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -308,7 +308,7 @@ arm_fdt_efi_init(device_t dev)
 
 	aprint_debug_dev(dev, "EFI system table at %#" PRIx64 "\n", efi_system_table);
 
-	if (arm_efirt_gettime(&tm) == 0) {
+	if (arm_efirt_gettime(&tm, NULL) == 0) {
 		aprint_normal_dev(dev, "using EFI runtime services for RTC\n");
 		efi_todr.cookie = NULL;
 		efi_todr.todr_gettime_ymdhms = arm_fdt_efi_rtc_gettime;
@@ -321,11 +321,11 @@ static int
 arm_fdt_efi_rtc_gettime(todr_chip_handle_t tch, struct clock_ymdhms *dt)
 {
 	struct efi_tm tm;
-	int error;
+	efi_status status;
 
-	error = arm_efirt_gettime(&tm);
-	if (error)
-		return error;
+	status = arm_efirt_gettime(&tm, NULL);
+	if (status != 0)
+		return EIO;
 
 	dt->dt_year = tm.tm_year;
 	dt->dt_mon = tm.tm_mon;
@@ -342,6 +342,7 @@ static int
 arm_fdt_efi_rtc_settime(todr_chip_handle_t tch, struct clock_ymdhms *dt)
 {
 	struct efi_tm tm;
+	efi_status status;
 
 	memset(&tm, 0, sizeof(tm));
 	tm.tm_year = dt->dt_year;
@@ -351,6 +352,10 @@ arm_fdt_efi_rtc_settime(todr_chip_handle
 	tm.tm_min = dt->dt_min;
 	tm.tm_sec = dt->dt_sec;
 
-	return arm_efirt_settime(&tm);
+	status = arm_efirt_settime(&tm);
+	if (status != 0)
+		return EIO;
+
+	return 0;
 }
 #endif

Index: src/sys/arch/evbarm/conf/GENERIC64
diff -u src/sys/arch/evbarm/conf/GENERIC64:1.186 src/sys/arch/evbarm/conf/GENERIC64:1.187
--- src/sys/arch/evbarm/conf/GENERIC64:1.186	Thu Sep 23 06:56:27 2021
+++ src/sys/arch/evbarm/conf/GENERIC64	Sun Oct 10 13:03:09 2021
@@ -1,5 +1,5 @@
 #
-#	$NetBSD: GENERIC64,v 1.186 2021/09/23 06:56:27 ryo Exp $
+#	$NetBSD: GENERIC64,v 1.187 2021/10/10 13:03:09 jmcneill Exp $
 #
 #	GENERIC ARM (aarch64) kernel
 #
@@ -95,6 +95,7 @@ options 	EXEC_ELF32
 
 # EFI runtime support
 options 	EFI_RUNTIME
+pseudo-device	efi		# /dev/efi
 
 # Device tree support
 armfdt0 	at root

Index: src/sys/conf/majors
diff -u src/sys/conf/majors:1.98 src/sys/conf/majors:1.99
--- src/sys/conf/majors:1.98	Sat Jul 24 11:39:19 2021
+++ src/sys/conf/majors	Sun Oct 10 13:03:09 2021
@@ -1,4 +1,4 @@
-# $NetBSD: majors,v 1.98 2021/07/24 11:39:19 jmcneill Exp $
+# $NetBSD: majors,v 1.99 2021/10/10 13:03:09 jmcneill Exp $
 #
 # Device majors for Machine-Independent drivers.
 #
@@ -92,3 +92,4 @@ device-major fault     char 357		   faul
 device-major wwanc     char 358	           wwanc
 device-major acpi      char 359            acpi
 device-major smbios    char 360            smbios
+device-major efi       char 361            efi

Index: src/sys/dev/files.dev
diff -u src/sys/dev/files.dev:1.7 src/sys/dev/files.dev:1.8
--- src/sys/dev/files.dev:1.7	Wed Jul 21 23:16:09 2021
+++ src/sys/dev/files.dev	Sun Oct 10 13:03:09 2021
@@ -1,4 +1,4 @@
-#	$NetBSD: files.dev,v 1.7 2021/07/21 23:16:09 jmcneill Exp $
+#	$NetBSD: files.dev,v 1.8 2021/10/10 13:03:09 jmcneill Exp $
 
 file	dev/bio.c			bio			needs-flag
 file	dev/ccd.c			ccd
@@ -27,3 +27,6 @@ file	dev/video.c			video			needs-flag
 file	dev/vnd.c			vnd
 file	dev/ipmi.c			ipmi			needs-flag
 file	dev/smbios.c			smbios
+
+defpseudo efi
+file	dev/efi.c			efi			needs-flag

Index: src/sys/sys/Makefile
diff -u src/sys/sys/Makefile:1.179 src/sys/sys/Makefile:1.180
--- src/sys/sys/Makefile:1.179	Thu Sep 30 01:26:07 2021
+++ src/sys/sys/Makefile	Sun Oct 10 13:03:10 2021
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.179 2021/09/30 01:26:07 yamaguchi Exp $
+#	$NetBSD: Makefile,v 1.180 2021/10/10 13:03:10 jmcneill Exp $
 
 .include <bsd.own.mk>
 
@@ -18,8 +18,9 @@ INCS=	acct.h acl.h agpio.h aio.h ansi.h 
 	dir.h dirent.h \
 	disk.h disklabel.h disklabel_acorn.h disklabel_gpt.h disklabel_rdb.h \
 	dkbad.h dkio.h dkstat.h domain.h drvctlio.h dvdio.h \
-	endian.h envsys.h errno.h evcnt.h event.h eventfd.h exec.h exec_aout.h \
-	exec_coff.h exec_ecoff.h exec_elf.h exec_script.h extattr.h extent.h \
+	efiio.h endian.h envsys.h errno.h evcnt.h event.h eventfd.h exec.h \
+	exec_aout.h exec_coff.h exec_ecoff.h exec_elf.h exec_script.h \
+	extattr.h extent.h \
 	fault.h \
 	fcntl.h fd_set.h fdio.h featuretest.h file.h filedesc.h filio.h \
 	flashio.h float_ieee754.h fstypes.h futex.h gcq.h gmon.h gpio.h hash.h \

Added files:

Index: src/sys/dev/efi.c
diff -u /dev/null src/sys/dev/efi.c:1.1
--- /dev/null	Sun Oct 10 13:03:10 2021
+++ src/sys/dev/efi.c	Sun Oct 10 13:03:09 2021
@@ -0,0 +1,306 @@
+/* $NetBSD: efi.c,v 1.1 2021/10/10 13:03:09 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2021 Jared McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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.
+ */
+
+/*
+ * This pseudo-driver implements a /dev/efi character device that provides
+ * ioctls for using UEFI runtime time and variable services.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: efi.c,v 1.1 2021/10/10 13:03:09 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/atomic.h>
+#include <sys/efiio.h>
+
+#include <dev/efivar.h>
+
+#ifdef _LP64
+#define	EFIERR(x)		(0x8000000000000000 | x)
+#else
+#define	EFIERR(x)		(0x80000000 | x)
+#endif
+
+#define	EFI_SUCCESS		0
+#define	EFI_INVALID_PARAMETER	EFIERR(2)
+#define	EFI_UNSUPPORTED		EFIERR(3)
+#define	EFI_BUFFER_TOO_SMALL	EFIERR(5)
+#define	EFI_DEVICE_ERROR	EFIERR(7)
+#define	EFI_WRITE_PROTECTED	EFIERR(8)
+#define	EFI_OUT_OF_RESOURCES	EFIERR(9)
+#define	EFI_NOT_FOUND		EFIERR(14)
+#define	EFI_SECURITY_VIOLATION	EFIERR(26)
+
+#include "ioconf.h"
+
+/* 
+ * Maximum length of an EFI variable name. The UEFI spec doesn't specify a
+ * constraint, but we want to limit the size to act as a guard rail against
+ * allocating too much kernel memory.
+ */
+#define	EFI_VARNAME_MAXLENGTH		EFI_PAGE_SIZE
+
+/*
+ * Pointer to arch specific EFI backend.
+ */
+static const struct efi_ops *efi_ops = NULL;
+
+/*
+ * Only allow one user of /dev/efi at a time. Even though the MD EFI backends
+ * should serialize individual UEFI RT calls, the UEFI specification says
+ * that a SetVariable() call between calls to GetNextVariableName() may
+ * produce unpredictable results, and we want to avoid this.
+ */
+static u_int efi_isopen = 0;
+
+static dev_type_open(efi_open);
+static dev_type_close(efi_close);
+static dev_type_ioctl(efi_ioctl);
+
+const struct cdevsw efi_cdevsw = {
+	.d_open =	efi_open,
+	.d_close =	efi_close,
+	.d_ioctl =	efi_ioctl,
+	.d_read =	noread,
+	.d_write =	nowrite,
+	.d_stop =	nostop,
+	.d_tty =	notty,
+	.d_poll =	nopoll,
+	.d_mmap =	nommap,
+	.d_kqfilter =	nokqfilter,
+	.d_discard =	nodiscard,
+	.d_flag =	D_OTHER | D_MPSAFE,
+};
+
+static int
+efi_open(dev_t dev, int flags, int type, struct lwp *l)
+{
+	if (efi_ops == NULL) {
+		return ENXIO;
+	}
+	if (atomic_cas_uint(&efi_isopen, 0, 1) == 1) {
+		return EBUSY;
+	}
+	return 0;
+}
+
+static int
+efi_close(dev_t dev, int flags, int type, struct lwp *l)
+{
+	KASSERT(efi_isopen);
+	atomic_swap_uint(&efi_isopen, 0);
+	return 0;
+}
+
+static int
+efi_status_to_error(efi_status status)
+{
+	switch (status) {
+	case EFI_SUCCESS:
+		return 0;
+	case EFI_INVALID_PARAMETER:
+		return EINVAL;
+	case EFI_UNSUPPORTED:
+		return EOPNOTSUPP;
+	case EFI_BUFFER_TOO_SMALL:
+		return ERANGE;
+	case EFI_DEVICE_ERROR:
+		return EIO;
+	case EFI_WRITE_PROTECTED:
+		return EROFS;
+	case EFI_OUT_OF_RESOURCES:
+		return ENOMEM;
+	case EFI_NOT_FOUND:
+		return ENOENT;
+	case EFI_SECURITY_VIOLATION:
+		return EACCES;
+	default:
+		return EIO;
+	}
+}
+
+static int
+efi_ioctl_var_get(struct efi_var_ioc *var)
+{
+	uint16_t *namebuf;
+	void *databuf = NULL;
+	efi_status status;
+	int error;
+
+	if (var->name == NULL || var->namesize == 0 ||
+	    (var->data != NULL && var->datasize == 0)) {
+		return EINVAL;
+	}
+	if (var->namesize > EFI_VARNAME_MAXLENGTH) {
+		return ENOMEM;
+	}
+
+	namebuf = kmem_alloc(var->namesize, KM_SLEEP);
+	error = copyin(var->name, namebuf, var->namesize);
+	if (error != 0) {
+		goto done;
+	}
+	if (namebuf[var->namesize / 2 - 1] != '\0') {
+		error = EINVAL;
+		goto done;
+	}
+	if (var->datasize != 0) {
+		databuf = kmem_alloc(var->datasize, KM_SLEEP);
+		error = copyin(var->data, databuf, var->datasize);
+		if (error != 0) {
+			goto done;
+		}
+	}
+
+	status = efi_ops->efi_getvar(namebuf, &var->vendor, &var->attrib,
+	    &var->datasize, databuf);
+	if (status != EFI_SUCCESS && status != EFI_BUFFER_TOO_SMALL) {
+		error = efi_status_to_error(status);
+		goto done;
+	}
+	if (status == EFI_SUCCESS && databuf != NULL) {
+		error = copyout(databuf, var->data, var->datasize);
+	} else {
+		var->data = NULL;
+	}
+
+done:
+	kmem_free(namebuf, var->namesize);
+	if (databuf != NULL) {
+		kmem_free(databuf, var->datasize);
+	}
+	return error;
+}
+
+static int
+efi_ioctl_var_next(struct efi_var_ioc *var)
+{
+	efi_status status;
+	uint16_t *namebuf;
+	int error;
+
+	if (var->name == NULL || var->namesize == 0) {
+		return EINVAL;
+	}
+	if (var->namesize > EFI_VARNAME_MAXLENGTH) {
+		return ENOMEM;
+	}
+
+	namebuf = kmem_alloc(var->namesize, KM_SLEEP);
+	error = copyin(var->name, namebuf, var->namesize);
+	if (error != 0) {
+		goto done;
+	}
+
+	status = efi_ops->efi_nextvar(&var->namesize, namebuf, &var->vendor);
+	if (status != EFI_SUCCESS && status != EFI_BUFFER_TOO_SMALL) {
+		error = efi_status_to_error(status);
+		goto done;
+	}
+	if (status == EFI_SUCCESS) {
+		error = copyout(namebuf, var->name, var->namesize);
+	} else {
+		var->name = NULL;
+	}
+
+done:
+	kmem_free(namebuf, var->namesize);
+	return error;
+}
+
+static int
+efi_ioctl_var_set(struct efi_var_ioc *var)
+{
+	efi_status status;
+	uint16_t *namebuf;
+	uint16_t *databuf = NULL;
+	int error;
+
+	if (var->name == NULL || var->namesize == 0) {
+		return EINVAL;
+	}
+
+	namebuf = kmem_alloc(var->namesize, KM_SLEEP);
+	error = copyin(var->name, namebuf, var->namesize);
+	if (error != 0) {
+		goto done;
+	}
+	if (namebuf[var->namesize / 2 - 1] != '\0') {
+		error = EINVAL;
+		goto done;
+	}
+	if (var->datasize != 0) {
+		databuf = kmem_alloc(var->datasize, KM_SLEEP);
+		error = copyin(var->data, databuf, var->datasize);
+		if (error != 0) {
+			goto done;
+		}
+	}
+
+	status = efi_ops->efi_setvar(namebuf, &var->vendor, var->attrib,
+	    var->datasize, databuf);
+	error = efi_status_to_error(status);
+
+done:
+	kmem_free(namebuf, var->namesize);
+	if (databuf != NULL) {
+		kmem_free(databuf, var->datasize);
+	}
+	return error;
+}
+
+static int
+efi_ioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
+{
+	KASSERT(efi_ops != NULL);
+
+	switch (cmd) {
+	case EFIIOC_VAR_GET:
+		return efi_ioctl_var_get(data);
+	case EFIIOC_VAR_NEXT:
+		return efi_ioctl_var_next(data);
+	case EFIIOC_VAR_SET:
+		return efi_ioctl_var_set(data);
+	}
+
+	return ENOTTY;
+}
+
+void
+efi_register_ops(const struct efi_ops *ops)
+{
+	KASSERT(efi_ops == NULL);
+	efi_ops = ops;
+}
+
+void
+efiattach(int count)
+{
+}
Index: src/sys/dev/efivar.h
diff -u /dev/null src/sys/dev/efivar.h:1.1
--- /dev/null	Sun Oct 10 13:03:10 2021
+++ src/sys/dev/efivar.h	Sun Oct 10 13:03:09 2021
@@ -0,0 +1,46 @@
+/* $NetBSD: efivar.h,v 1.1 2021/10/10 13:03:09 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2021 Jared McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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.
+ */
+
+#ifndef _DEV_EFIVAR_H
+#define _DEV_EFIVAR_H
+
+#include <machine/efi.h>
+
+struct efi_ops {
+	efi_status	(*efi_gettime)(struct efi_tm *, struct efi_tmcap *);
+	efi_status	(*efi_settime)(struct efi_tm *);
+	efi_status	(*efi_getvar)(uint16_t *, struct uuid *, uint32_t *,
+				      u_long *, void *);
+	efi_status	(*efi_setvar)(uint16_t *, struct uuid *, uint32_t,
+				      u_long, void *);
+	efi_status	(*efi_nextvar)(u_long *, uint16_t *, struct uuid *);
+};
+
+void	efi_register_ops(const struct efi_ops *);
+
+#endif /* !_DEV_EFIVAR_H */

Index: src/sys/sys/efiio.h
diff -u /dev/null src/sys/sys/efiio.h:1.1
--- /dev/null	Sun Oct 10 13:03:10 2021
+++ src/sys/sys/efiio.h	Sun Oct 10 13:03:10 2021
@@ -0,0 +1,64 @@
+/* $NetBSD: efiio.h,v 1.1 2021/10/10 13:03:10 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2021 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jared McNeill <jmcne...@invisible.ca>.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _SYS_EFIIO_H
+#define _SYS_EFIIO_H
+
+#include <sys/types.h>
+#include <sys/ioccom.h>
+#include <sys/uuid.h>
+
+/*
+ * Variable attributes
+ */
+#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
+#define	EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS		0x00000080
+
+struct efi_var_ioc {
+	uint16_t *	name;		/* vendor's variable name */
+	size_t		namesize;	/* size in bytes of the name buffer */
+	struct uuid	vendor;		/* unique identifier for vendor */
+	uint32_t	attrib;		/* variable attribute bitmask */
+	void *		data;		/* buffer containing variable data */
+	size_t		datasize;	/* size in bytes of the data buffer */
+};
+
+#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', 7, struct efi_var_ioc)
+
+#endif /* _SYS_EFIIO_H */

Reply via email to