xref: /freebsd-src/sys/dev/efidev/efirt.c (revision b72ae900d4348118829fe04abdc11b620930c30f)
10e73a619SAndrew Turner /*-
20e73a619SAndrew Turner  * Copyright (c) 2004 Marcel Moolenaar
30e73a619SAndrew Turner  * Copyright (c) 2001 Doug Rabson
450cd0be7SKonstantin Belousov  * Copyright (c) 2016, 2018 The FreeBSD Foundation
50e73a619SAndrew Turner  * All rights reserved.
60e73a619SAndrew Turner  *
70e73a619SAndrew Turner  * Portions of this software were developed by Konstantin Belousov
80e73a619SAndrew Turner  * under sponsorship from the FreeBSD Foundation.
90e73a619SAndrew Turner  *
100e73a619SAndrew Turner  * Redistribution and use in source and binary forms, with or without
110e73a619SAndrew Turner  * modification, are permitted provided that the following conditions
120e73a619SAndrew Turner  * are met:
130e73a619SAndrew Turner  * 1. Redistributions of source code must retain the above copyright
140e73a619SAndrew Turner  *    notice, this list of conditions and the following disclaimer.
150e73a619SAndrew Turner  * 2. Redistributions in binary form must reproduce the above copyright
160e73a619SAndrew Turner  *    notice, this list of conditions and the following disclaimer in the
170e73a619SAndrew Turner  *    documentation and/or other materials provided with the distribution.
180e73a619SAndrew Turner  *
190e73a619SAndrew Turner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
200e73a619SAndrew Turner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
210e73a619SAndrew Turner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
220e73a619SAndrew Turner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
230e73a619SAndrew Turner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
240e73a619SAndrew Turner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
250e73a619SAndrew Turner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
260e73a619SAndrew Turner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
270e73a619SAndrew Turner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
280e73a619SAndrew Turner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
290e73a619SAndrew Turner  * SUCH DAMAGE.
300e73a619SAndrew Turner  */
310e73a619SAndrew Turner 
320e73a619SAndrew Turner #include <sys/cdefs.h>
333f3ad565SJohannes Totz #include "opt_acpi.h"
343f3ad565SJohannes Totz 
350e73a619SAndrew Turner #include <sys/param.h>
360e73a619SAndrew Turner #include <sys/efi.h>
3726649bb5SConrad Meyer #include <sys/eventhandler.h>
380e73a619SAndrew Turner #include <sys/kernel.h>
390e73a619SAndrew Turner #include <sys/linker.h>
400e73a619SAndrew Turner #include <sys/lock.h>
41d12d651fSPavel Balaev #include <sys/malloc.h>
420e73a619SAndrew Turner #include <sys/module.h>
43eec892a8SMark Johnston #include <sys/msan.h>
440e73a619SAndrew Turner #include <sys/mutex.h>
450e73a619SAndrew Turner #include <sys/clock.h>
460e73a619SAndrew Turner #include <sys/proc.h>
4726649bb5SConrad Meyer #include <sys/reboot.h>
480e73a619SAndrew Turner #include <sys/rwlock.h>
490e73a619SAndrew Turner #include <sys/sched.h>
500e73a619SAndrew Turner #include <sys/sysctl.h>
510e73a619SAndrew Turner #include <sys/systm.h>
52d12d651fSPavel Balaev #include <sys/uio.h>
530e73a619SAndrew Turner #include <sys/vmmeter.h>
540e73a619SAndrew Turner 
550e73a619SAndrew Turner #include <machine/fpu.h>
560e73a619SAndrew Turner #include <machine/efi.h>
570e73a619SAndrew Turner #include <machine/metadata.h>
580e73a619SAndrew Turner #include <machine/vmparam.h>
590e73a619SAndrew Turner 
600e73a619SAndrew Turner #include <vm/vm.h>
610e73a619SAndrew Turner #include <vm/pmap.h>
620e73a619SAndrew Turner #include <vm/vm_map.h>
630e73a619SAndrew Turner 
643f3ad565SJohannes Totz #ifdef DEV_ACPI
653f3ad565SJohannes Totz #include <contrib/dev/acpica/include/acpi.h>
663f3ad565SJohannes Totz #endif
673f3ad565SJohannes Totz 
68d12d651fSPavel Balaev #define EFI_TABLE_ALLOC_MAX 0x800000
69d12d651fSPavel Balaev 
700e73a619SAndrew Turner static struct efi_systbl *efi_systbl;
7126649bb5SConrad Meyer static eventhandler_tag efi_shutdown_tag;
7250da29d2SKyle Evans /*
7350da29d2SKyle Evans  * The following pointers point to tables in the EFI runtime service data pages.
7450da29d2SKyle Evans  * Care should be taken to make sure that we've properly entered the EFI runtime
7550da29d2SKyle Evans  * environment (efi_enter()) before dereferencing them.
7650da29d2SKyle Evans  */
770e73a619SAndrew Turner static struct efi_cfgtbl *efi_cfgtbl;
780e73a619SAndrew Turner static struct efi_rt *efi_runtime;
790e73a619SAndrew Turner 
800e73a619SAndrew Turner static int efi_status2err[25] = {
810e73a619SAndrew Turner 	0,		/* EFI_SUCCESS */
820e73a619SAndrew Turner 	ENOEXEC,	/* EFI_LOAD_ERROR */
830e73a619SAndrew Turner 	EINVAL,		/* EFI_INVALID_PARAMETER */
840e73a619SAndrew Turner 	ENOSYS,		/* EFI_UNSUPPORTED */
850e73a619SAndrew Turner 	EMSGSIZE, 	/* EFI_BAD_BUFFER_SIZE */
860e73a619SAndrew Turner 	EOVERFLOW,	/* EFI_BUFFER_TOO_SMALL */
870e73a619SAndrew Turner 	EBUSY,		/* EFI_NOT_READY */
880e73a619SAndrew Turner 	EIO,		/* EFI_DEVICE_ERROR */
890e73a619SAndrew Turner 	EROFS,		/* EFI_WRITE_PROTECTED */
900e73a619SAndrew Turner 	EAGAIN,		/* EFI_OUT_OF_RESOURCES */
910e73a619SAndrew Turner 	EIO,		/* EFI_VOLUME_CORRUPTED */
920e73a619SAndrew Turner 	ENOSPC,		/* EFI_VOLUME_FULL */
930e73a619SAndrew Turner 	ENXIO,		/* EFI_NO_MEDIA */
940e73a619SAndrew Turner 	ESTALE,		/* EFI_MEDIA_CHANGED */
950e73a619SAndrew Turner 	ENOENT,		/* EFI_NOT_FOUND */
960e73a619SAndrew Turner 	EACCES,		/* EFI_ACCESS_DENIED */
970e73a619SAndrew Turner 	ETIMEDOUT,	/* EFI_NO_RESPONSE */
980e73a619SAndrew Turner 	EADDRNOTAVAIL,	/* EFI_NO_MAPPING */
990e73a619SAndrew Turner 	ETIMEDOUT,	/* EFI_TIMEOUT */
1000e73a619SAndrew Turner 	EDOOFUS,	/* EFI_NOT_STARTED */
1010e73a619SAndrew Turner 	EALREADY,	/* EFI_ALREADY_STARTED */
1020e73a619SAndrew Turner 	ECANCELED,	/* EFI_ABORTED */
1030e73a619SAndrew Turner 	EPROTO,		/* EFI_ICMP_ERROR */
1040e73a619SAndrew Turner 	EPROTO,		/* EFI_TFTP_ERROR */
1050e73a619SAndrew Turner 	EPROTO		/* EFI_PROTOCOL_ERROR */
1060e73a619SAndrew Turner };
1070e73a619SAndrew Turner 
108d12d651fSPavel Balaev enum efi_table_type {
109d12d651fSPavel Balaev 	TYPE_ESRT = 0,
110d12d651fSPavel Balaev 	TYPE_PROP
111d12d651fSPavel Balaev };
112d12d651fSPavel Balaev 
113112b88e3SAndrew Turner static int efi_enter(void);
114112b88e3SAndrew Turner static void efi_leave(void);
115112b88e3SAndrew Turner 
11627d39026SRoger Pau Monné int
1170e73a619SAndrew Turner efi_status_to_errno(efi_status status)
1180e73a619SAndrew Turner {
1190e73a619SAndrew Turner 	u_long code;
1200e73a619SAndrew Turner 
1210e73a619SAndrew Turner 	code = status & 0x3ffffffffffffffful;
1220e73a619SAndrew Turner 	return (code < nitems(efi_status2err) ? efi_status2err[code] : EDOOFUS);
1230e73a619SAndrew Turner }
1240e73a619SAndrew Turner 
1250e73a619SAndrew Turner static struct mtx efi_lock;
1267029da5cSPawel Biernacki static SYSCTL_NODE(_hw, OID_AUTO, efi, CTLFLAG_RWTUN | CTLFLAG_MPSAFE, NULL,
1277029da5cSPawel Biernacki     "EFI");
12826649bb5SConrad Meyer static bool efi_poweroff = true;
12926649bb5SConrad Meyer SYSCTL_BOOL(_hw_efi, OID_AUTO, poweroff, CTLFLAG_RWTUN, &efi_poweroff, 0,
13026649bb5SConrad Meyer     "If true, use EFI runtime services to power off in preference to ACPI");
1310e73a619SAndrew Turner 
132ed4c884fSAndrew Turner static bool
133ed4c884fSAndrew Turner efi_is_in_map(struct efi_md *map, int ndesc, int descsz, vm_offset_t addr)
134ed4c884fSAndrew Turner {
135ed4c884fSAndrew Turner 	struct efi_md *p;
136ed4c884fSAndrew Turner 	int i;
137ed4c884fSAndrew Turner 
138ed4c884fSAndrew Turner 	for (i = 0, p = map; i < ndesc; i++, p = efi_next_descriptor(p,
139ed4c884fSAndrew Turner 	    descsz)) {
140ed4c884fSAndrew Turner 		if ((p->md_attr & EFI_MD_ATTR_RT) == 0)
141ed4c884fSAndrew Turner 			continue;
142ed4c884fSAndrew Turner 
143c78ad207SAndrew Turner 		if (addr >= p->md_virt &&
144643e14d0SAndrew Turner 		    addr < p->md_virt + p->md_pages * EFI_PAGE_SIZE)
145ed4c884fSAndrew Turner 			return (true);
146ed4c884fSAndrew Turner 	}
147ed4c884fSAndrew Turner 
148ed4c884fSAndrew Turner 	return (false);
149ed4c884fSAndrew Turner }
150ed4c884fSAndrew Turner 
15126649bb5SConrad Meyer static void
15226649bb5SConrad Meyer efi_shutdown_final(void *dummy __unused, int howto)
15326649bb5SConrad Meyer {
15426649bb5SConrad Meyer 
15526649bb5SConrad Meyer 	/*
15626649bb5SConrad Meyer 	 * On some systems, ACPI S5 is missing or does not function properly.
15726649bb5SConrad Meyer 	 * When present, shutdown via EFI Runtime Services instead, unless
15826649bb5SConrad Meyer 	 * disabled.
15926649bb5SConrad Meyer 	 */
16026649bb5SConrad Meyer 	if ((howto & RB_POWEROFF) != 0 && efi_poweroff)
16126649bb5SConrad Meyer 		(void)efi_reset_system(EFI_RESET_SHUTDOWN);
16226649bb5SConrad Meyer }
16326649bb5SConrad Meyer 
1640e73a619SAndrew Turner static int
1650e73a619SAndrew Turner efi_init(void)
1660e73a619SAndrew Turner {
1670e73a619SAndrew Turner 	struct efi_map_header *efihdr;
1680e73a619SAndrew Turner 	struct efi_md *map;
1693395e43aSKyle Evans 	struct efi_rt *rtdm;
1700e73a619SAndrew Turner 	size_t efisz;
1713395e43aSKyle Evans 	int ndesc, rt_disabled;
1720e73a619SAndrew Turner 
17321307740SKyle Evans 	rt_disabled = 0;
1741ddc8a8eSKyle Evans 	TUNABLE_INT_FETCH("efi.rt.disabled", &rt_disabled);
17521307740SKyle Evans 	if (rt_disabled == 1)
17621307740SKyle Evans 		return (0);
1770e73a619SAndrew Turner 	mtx_init(&efi_lock, "efi", NULL, MTX_DEF);
1780e73a619SAndrew Turner 
1790e73a619SAndrew Turner 	if (efi_systbl_phys == 0) {
1800e73a619SAndrew Turner 		if (bootverbose)
1810e73a619SAndrew Turner 			printf("EFI systbl not available\n");
1820e73a619SAndrew Turner 		return (0);
1830e73a619SAndrew Turner 	}
1843395e43aSKyle Evans 
1853395e43aSKyle Evans 	efi_systbl = (struct efi_systbl *)efi_phys_to_kva(efi_systbl_phys);
1863395e43aSKyle Evans 	if (efi_systbl == NULL || efi_systbl->st_hdr.th_sig != EFI_SYSTBL_SIG) {
1870e73a619SAndrew Turner 		efi_systbl = NULL;
1880e73a619SAndrew Turner 		if (bootverbose)
1890e73a619SAndrew Turner 			printf("EFI systbl signature invalid\n");
1900e73a619SAndrew Turner 		return (0);
1910e73a619SAndrew Turner 	}
1920e73a619SAndrew Turner 	efi_cfgtbl = (efi_systbl->st_cfgtbl == 0) ? NULL :
1930e73a619SAndrew Turner 	    (struct efi_cfgtbl *)efi_systbl->st_cfgtbl;
1940e73a619SAndrew Turner 	if (efi_cfgtbl == NULL) {
1950e73a619SAndrew Turner 		if (bootverbose)
1960e73a619SAndrew Turner 			printf("EFI config table is not present\n");
1970e73a619SAndrew Turner 	}
1980e73a619SAndrew Turner 
199*b72ae900SAhmad Khalifa 	efihdr = (struct efi_map_header *)preload_search_info(preload_kmdp,
2000e73a619SAndrew Turner 	    MODINFO_METADATA | MODINFOMD_EFI_MAP);
2010e73a619SAndrew Turner 	if (efihdr == NULL) {
2020e73a619SAndrew Turner 		if (bootverbose)
2030e73a619SAndrew Turner 			printf("EFI map is not present\n");
2040e73a619SAndrew Turner 		return (0);
2050e73a619SAndrew Turner 	}
2060e73a619SAndrew Turner 	efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf;
2070e73a619SAndrew Turner 	map = (struct efi_md *)((uint8_t *)efihdr + efisz);
2080e73a619SAndrew Turner 	if (efihdr->descriptor_size == 0)
2090e73a619SAndrew Turner 		return (ENOMEM);
2100e73a619SAndrew Turner 
2113395e43aSKyle Evans 	ndesc = efihdr->memory_size / efihdr->descriptor_size;
2123395e43aSKyle Evans 	if (!efi_create_1t1_map(map, ndesc, efihdr->descriptor_size)) {
2130e73a619SAndrew Turner 		if (bootverbose)
2140e73a619SAndrew Turner 			printf("EFI cannot create runtime map\n");
2150e73a619SAndrew Turner 		return (ENOMEM);
2160e73a619SAndrew Turner 	}
2170e73a619SAndrew Turner 
2180e73a619SAndrew Turner 	efi_runtime = (efi_systbl->st_rt == 0) ? NULL :
2190e73a619SAndrew Turner 	    (struct efi_rt *)efi_systbl->st_rt;
2200e73a619SAndrew Turner 	if (efi_runtime == NULL) {
2210e73a619SAndrew Turner 		if (bootverbose)
2220e73a619SAndrew Turner 			printf("EFI runtime services table is not present\n");
2230e73a619SAndrew Turner 		efi_destroy_1t1_map();
2240e73a619SAndrew Turner 		return (ENXIO);
2250e73a619SAndrew Turner 	}
2260e73a619SAndrew Turner 
2273395e43aSKyle Evans #if defined(__aarch64__) || defined(__amd64__)
228ed4c884fSAndrew Turner 	/*
229ed4c884fSAndrew Turner 	 * Some UEFI implementations have multiple implementations of the
230ed4c884fSAndrew Turner 	 * RS->GetTime function. They switch from one we can only use early
231ed4c884fSAndrew Turner 	 * in the boot process to one valid as a RunTime service only when we
232ed4c884fSAndrew Turner 	 * call RS->SetVirtualAddressMap. As this is not always the case, e.g.
233ed4c884fSAndrew Turner 	 * with an old loader.efi, check if the RS->GetTime function is within
234ed4c884fSAndrew Turner 	 * the EFI map, and fail to attach if not.
235ed4c884fSAndrew Turner 	 */
2363395e43aSKyle Evans 	rtdm = (struct efi_rt *)efi_phys_to_kva((uintptr_t)efi_runtime);
2373395e43aSKyle Evans 	if (rtdm == NULL || !efi_is_in_map(map, ndesc, efihdr->descriptor_size,
2383395e43aSKyle Evans 	    (vm_offset_t)rtdm->rt_gettime)) {
239ed4c884fSAndrew Turner 		if (bootverbose)
240ed4c884fSAndrew Turner 			printf(
241ed4c884fSAndrew Turner 			 "EFI runtime services table has an invalid pointer\n");
242ed4c884fSAndrew Turner 		efi_runtime = NULL;
243ed4c884fSAndrew Turner 		efi_destroy_1t1_map();
244ed4c884fSAndrew Turner 		return (ENXIO);
245ed4c884fSAndrew Turner 	}
2463395e43aSKyle Evans #endif
247ed4c884fSAndrew Turner 
24826649bb5SConrad Meyer 	/*
24926649bb5SConrad Meyer 	 * We use SHUTDOWN_PRI_LAST - 1 to trigger after IPMI, but before ACPI.
25026649bb5SConrad Meyer 	 */
25126649bb5SConrad Meyer 	efi_shutdown_tag = EVENTHANDLER_REGISTER(shutdown_final,
25226649bb5SConrad Meyer 	    efi_shutdown_final, NULL, SHUTDOWN_PRI_LAST - 1);
25326649bb5SConrad Meyer 
2540e73a619SAndrew Turner 	return (0);
2550e73a619SAndrew Turner }
2560e73a619SAndrew Turner 
2570e73a619SAndrew Turner static void
2580e73a619SAndrew Turner efi_uninit(void)
2590e73a619SAndrew Turner {
2600e73a619SAndrew Turner 
26121307740SKyle Evans 	/* Most likely disabled by tunable */
26221307740SKyle Evans 	if (efi_runtime == NULL)
26321307740SKyle Evans 		return;
26426649bb5SConrad Meyer 	if (efi_shutdown_tag != NULL)
26526649bb5SConrad Meyer 		EVENTHANDLER_DEREGISTER(shutdown_final, efi_shutdown_tag);
2660e73a619SAndrew Turner 	efi_destroy_1t1_map();
2670e73a619SAndrew Turner 
2680e73a619SAndrew Turner 	efi_systbl = NULL;
2690e73a619SAndrew Turner 	efi_cfgtbl = NULL;
2700e73a619SAndrew Turner 	efi_runtime = NULL;
2710e73a619SAndrew Turner 
2720e73a619SAndrew Turner 	mtx_destroy(&efi_lock);
2730e73a619SAndrew Turner }
2740e73a619SAndrew Turner 
27527d39026SRoger Pau Monné static int
27627d39026SRoger Pau Monné rt_ok(void)
2770e73a619SAndrew Turner {
2780e73a619SAndrew Turner 
2790e73a619SAndrew Turner 	if (efi_runtime == NULL)
2800e73a619SAndrew Turner 		return (ENXIO);
2810e73a619SAndrew Turner 	return (0);
2820e73a619SAndrew Turner }
2830e73a619SAndrew Turner 
2848173fa60SJohn Baldwin /*
2858173fa60SJohn Baldwin  * The fpu_kern_enter() call in allows firmware to use FPU, as
2868173fa60SJohn Baldwin  * mandated by the specification.  It also enters a critical section,
2878173fa60SJohn Baldwin  * giving us neccessary protection against context switches.
2888173fa60SJohn Baldwin  */
2890e73a619SAndrew Turner static int
2900e73a619SAndrew Turner efi_enter(void)
2910e73a619SAndrew Turner {
2920e73a619SAndrew Turner 	struct thread *td;
2930e73a619SAndrew Turner 	pmap_t curpmap;
294b5c45a3eSKonstantin Belousov 	int error;
2950e73a619SAndrew Turner 
2960e73a619SAndrew Turner 	if (efi_runtime == NULL)
2970e73a619SAndrew Turner 		return (ENXIO);
2980e73a619SAndrew Turner 	td = curthread;
2990e73a619SAndrew Turner 	curpmap = &td->td_proc->p_vmspace->vm_pmap;
3000e73a619SAndrew Turner 	PMAP_LOCK(curpmap);
3010e73a619SAndrew Turner 	mtx_lock(&efi_lock);
302849ce31aSConrad Meyer 	fpu_kern_enter(td, NULL, FPU_KERN_NOCTX);
303b5c45a3eSKonstantin Belousov 	error = efi_arch_enter();
304b5c45a3eSKonstantin Belousov 	if (error != 0) {
305b5c45a3eSKonstantin Belousov 		fpu_kern_leave(td, NULL);
306b5c45a3eSKonstantin Belousov 		mtx_unlock(&efi_lock);
307b5c45a3eSKonstantin Belousov 		PMAP_UNLOCK(curpmap);
308a03957a7SKonstantin Belousov 	} else {
309a03957a7SKonstantin Belousov 		MPASS((td->td_pflags & TDP_EFIRT) == 0);
310a03957a7SKonstantin Belousov 		td->td_pflags |= TDP_EFIRT;
311b5c45a3eSKonstantin Belousov 	}
312b5c45a3eSKonstantin Belousov 	return (error);
3130e73a619SAndrew Turner }
3140e73a619SAndrew Turner 
3150e73a619SAndrew Turner static void
3160e73a619SAndrew Turner efi_leave(void)
3170e73a619SAndrew Turner {
3180e73a619SAndrew Turner 	struct thread *td;
3190e73a619SAndrew Turner 	pmap_t curpmap;
3200e73a619SAndrew Turner 
321a03957a7SKonstantin Belousov 	td = curthread;
32207593d13SMark Johnston 	MPASS((td->td_pflags & TDP_EFIRT) != 0);
323a03957a7SKonstantin Belousov 	td->td_pflags &= ~TDP_EFIRT;
324a03957a7SKonstantin Belousov 
3250e73a619SAndrew Turner 	efi_arch_leave();
3260e73a619SAndrew Turner 
3270e73a619SAndrew Turner 	curpmap = &curproc->p_vmspace->vm_pmap;
3280e73a619SAndrew Turner 	fpu_kern_leave(td, NULL);
3290e73a619SAndrew Turner 	mtx_unlock(&efi_lock);
3300e73a619SAndrew Turner 	PMAP_UNLOCK(curpmap);
3310e73a619SAndrew Turner }
3320e73a619SAndrew Turner 
33327d39026SRoger Pau Monné static int
33427d39026SRoger Pau Monné get_table(struct uuid *uuid, void **ptr)
3350e73a619SAndrew Turner {
3360e73a619SAndrew Turner 	struct efi_cfgtbl *ct;
3370e73a619SAndrew Turner 	u_long count;
3380861c7d3SKyle Evans 	int error;
3390e73a619SAndrew Turner 
3400e73a619SAndrew Turner 	if (efi_cfgtbl == NULL || efi_systbl == NULL)
3410e73a619SAndrew Turner 		return (ENXIO);
3420861c7d3SKyle Evans 	error = efi_enter();
3430861c7d3SKyle Evans 	if (error != 0)
3440861c7d3SKyle Evans 		return (error);
3450e73a619SAndrew Turner 	count = efi_systbl->st_entries;
3460e73a619SAndrew Turner 	ct = efi_cfgtbl;
3470e73a619SAndrew Turner 	while (count--) {
3480e73a619SAndrew Turner 		if (!bcmp(&ct->ct_uuid, uuid, sizeof(*uuid))) {
349ee938b20SKyle Evans 			*ptr = ct->ct_data;
3500861c7d3SKyle Evans 			efi_leave();
3510e73a619SAndrew Turner 			return (0);
3520e73a619SAndrew Turner 		}
3530e73a619SAndrew Turner 		ct++;
3540e73a619SAndrew Turner 	}
3550861c7d3SKyle Evans 
3560861c7d3SKyle Evans 	efi_leave();
3570e73a619SAndrew Turner 	return (ENOENT);
3580e73a619SAndrew Turner }
3590e73a619SAndrew Turner 
360d12d651fSPavel Balaev static int
361d12d651fSPavel Balaev get_table_length(enum efi_table_type type, size_t *table_len, void **taddr)
362d12d651fSPavel Balaev {
363d12d651fSPavel Balaev 	switch (type) {
364d12d651fSPavel Balaev 	case TYPE_ESRT:
365d12d651fSPavel Balaev 	{
366d12d651fSPavel Balaev 		struct efi_esrt_table *esrt = NULL;
367d12d651fSPavel Balaev 		struct uuid uuid = EFI_TABLE_ESRT;
368d12d651fSPavel Balaev 		uint32_t fw_resource_count = 0;
369d12d651fSPavel Balaev 		size_t len = sizeof(*esrt);
370d12d651fSPavel Balaev 		int error;
371d12d651fSPavel Balaev 		void *buf;
372d12d651fSPavel Balaev 
373d12d651fSPavel Balaev 		error = efi_get_table(&uuid, (void **)&esrt);
374d12d651fSPavel Balaev 		if (error != 0)
375d12d651fSPavel Balaev 			return (error);
376d12d651fSPavel Balaev 
377d12d651fSPavel Balaev 		buf = malloc(len, M_TEMP, M_WAITOK);
378d12d651fSPavel Balaev 		error = physcopyout((vm_paddr_t)esrt, buf, len);
379d12d651fSPavel Balaev 		if (error != 0) {
380d12d651fSPavel Balaev 			free(buf, M_TEMP);
381d12d651fSPavel Balaev 			return (error);
382d12d651fSPavel Balaev 		}
383d12d651fSPavel Balaev 
384d12d651fSPavel Balaev 		/* Check ESRT version */
385d12d651fSPavel Balaev 		if (((struct efi_esrt_table *)buf)->fw_resource_version !=
386d12d651fSPavel Balaev 		    ESRT_FIRMWARE_RESOURCE_VERSION) {
387d12d651fSPavel Balaev 			free(buf, M_TEMP);
388d12d651fSPavel Balaev 			return (ENODEV);
389d12d651fSPavel Balaev 		}
390d12d651fSPavel Balaev 
391d12d651fSPavel Balaev 		fw_resource_count = ((struct efi_esrt_table *)buf)->
392d12d651fSPavel Balaev 		    fw_resource_count;
393d12d651fSPavel Balaev 		if (fw_resource_count > EFI_TABLE_ALLOC_MAX /
394d12d651fSPavel Balaev 		    sizeof(struct efi_esrt_entry_v1)) {
395d12d651fSPavel Balaev 			free(buf, M_TEMP);
396d12d651fSPavel Balaev 			return (ENOMEM);
397d12d651fSPavel Balaev 		}
398d12d651fSPavel Balaev 
399d12d651fSPavel Balaev 		len += fw_resource_count * sizeof(struct efi_esrt_entry_v1);
400d12d651fSPavel Balaev 		*table_len = len;
401d12d651fSPavel Balaev 
402d12d651fSPavel Balaev 		if (taddr != NULL)
403d12d651fSPavel Balaev 			*taddr = esrt;
404d12d651fSPavel Balaev 		free(buf, M_TEMP);
405d12d651fSPavel Balaev 		return (0);
406d12d651fSPavel Balaev 	}
407d12d651fSPavel Balaev 	case TYPE_PROP:
408d12d651fSPavel Balaev 	{
409d12d651fSPavel Balaev 		struct uuid uuid = EFI_PROPERTIES_TABLE;
410d12d651fSPavel Balaev 		struct efi_prop_table *prop;
411d12d651fSPavel Balaev 		size_t len = sizeof(*prop);
412d12d651fSPavel Balaev 		uint32_t prop_len;
413d12d651fSPavel Balaev 		int error;
414d12d651fSPavel Balaev 		void *buf;
415d12d651fSPavel Balaev 
416d12d651fSPavel Balaev 		error = efi_get_table(&uuid, (void **)&prop);
417d12d651fSPavel Balaev 		if (error != 0)
418d12d651fSPavel Balaev 			return (error);
419d12d651fSPavel Balaev 
420d12d651fSPavel Balaev 		buf = malloc(len, M_TEMP, M_WAITOK);
421d12d651fSPavel Balaev 		error = physcopyout((vm_paddr_t)prop, buf, len);
422d12d651fSPavel Balaev 		if (error != 0) {
423d12d651fSPavel Balaev 			free(buf, M_TEMP);
424d12d651fSPavel Balaev 			return (error);
425d12d651fSPavel Balaev 		}
426d12d651fSPavel Balaev 
427d12d651fSPavel Balaev 		prop_len = ((struct efi_prop_table *)buf)->length;
428d12d651fSPavel Balaev 		if (prop_len > EFI_TABLE_ALLOC_MAX) {
429d12d651fSPavel Balaev 			free(buf, M_TEMP);
430d12d651fSPavel Balaev 			return (ENOMEM);
431d12d651fSPavel Balaev 		}
432d12d651fSPavel Balaev 		*table_len = prop_len;
433d12d651fSPavel Balaev 
434d12d651fSPavel Balaev 		if (taddr != NULL)
435d12d651fSPavel Balaev 			*taddr = prop;
436d12d651fSPavel Balaev 		free(buf, M_TEMP);
437d12d651fSPavel Balaev 		return (0);
438d12d651fSPavel Balaev 	}
439d12d651fSPavel Balaev 	}
440d12d651fSPavel Balaev 	return (ENOENT);
441d12d651fSPavel Balaev }
442d12d651fSPavel Balaev 
443d12d651fSPavel Balaev static int
444d12d651fSPavel Balaev copy_table(struct uuid *uuid, void **buf, size_t buf_len, size_t *table_len)
445d12d651fSPavel Balaev {
446d12d651fSPavel Balaev 	static const struct known_table {
447d12d651fSPavel Balaev 		struct uuid uuid;
448d12d651fSPavel Balaev 		enum efi_table_type type;
449d12d651fSPavel Balaev 	} tables[] = {
450d12d651fSPavel Balaev 		{ EFI_TABLE_ESRT,       TYPE_ESRT },
451d12d651fSPavel Balaev 		{ EFI_PROPERTIES_TABLE, TYPE_PROP }
452d12d651fSPavel Balaev 	};
453d12d651fSPavel Balaev 	size_t table_idx;
454d12d651fSPavel Balaev 	void *taddr;
455d12d651fSPavel Balaev 	int rc;
456d12d651fSPavel Balaev 
457d12d651fSPavel Balaev 	for (table_idx = 0; table_idx < nitems(tables); table_idx++) {
458d12d651fSPavel Balaev 		if (!bcmp(&tables[table_idx].uuid, uuid, sizeof(*uuid)))
459d12d651fSPavel Balaev 			break;
460d12d651fSPavel Balaev 	}
461d12d651fSPavel Balaev 
462d12d651fSPavel Balaev 	if (table_idx == nitems(tables))
463d12d651fSPavel Balaev 		return (EINVAL);
464d12d651fSPavel Balaev 
465d12d651fSPavel Balaev 	rc = get_table_length(tables[table_idx].type, table_len, &taddr);
466d12d651fSPavel Balaev 	if (rc != 0)
467d12d651fSPavel Balaev 		return rc;
468d12d651fSPavel Balaev 
469d12d651fSPavel Balaev 	/* return table length to userspace */
470d12d651fSPavel Balaev 	if (buf == NULL)
471d12d651fSPavel Balaev 		return (0);
472d12d651fSPavel Balaev 
473d12d651fSPavel Balaev 	*buf = malloc(*table_len, M_TEMP, M_WAITOK);
474d12d651fSPavel Balaev 	rc = physcopyout((vm_paddr_t)taddr, *buf, *table_len);
475d12d651fSPavel Balaev 	return (rc);
476d12d651fSPavel Balaev }
477d12d651fSPavel Balaev 
47850cd0be7SKonstantin Belousov static int efi_rt_handle_faults = EFI_RT_HANDLE_FAULTS_DEFAULT;
47950cd0be7SKonstantin Belousov SYSCTL_INT(_machdep, OID_AUTO, efi_rt_handle_faults, CTLFLAG_RWTUN,
48050cd0be7SKonstantin Belousov     &efi_rt_handle_faults, 0,
48150cd0be7SKonstantin Belousov     "Call EFI RT methods with fault handler wrapper around");
48250cd0be7SKonstantin Belousov 
4830e73a619SAndrew Turner static int
48450cd0be7SKonstantin Belousov efi_rt_arch_call_nofault(struct efirt_callinfo *ec)
4850e73a619SAndrew Turner {
48650cd0be7SKonstantin Belousov 
48750cd0be7SKonstantin Belousov 	switch (ec->ec_argcnt) {
48850cd0be7SKonstantin Belousov 	case 0:
4893e8f4a30SAhmad Khalifa 		ec->ec_efi_status = ((register_t EFIABI_ATTR (*)(void))
4903e8f4a30SAhmad Khalifa 		    ec->ec_fptr)();
49150cd0be7SKonstantin Belousov 		break;
49250cd0be7SKonstantin Belousov 	case 1:
4933e8f4a30SAhmad Khalifa 		ec->ec_efi_status = ((register_t EFIABI_ATTR (*)(register_t))
4943e8f4a30SAhmad Khalifa 		    ec->ec_fptr)(ec->ec_arg1);
49550cd0be7SKonstantin Belousov 		break;
49650cd0be7SKonstantin Belousov 	case 2:
4973e8f4a30SAhmad Khalifa 		ec->ec_efi_status = ((register_t EFIABI_ATTR (*)(register_t,
4983e8f4a30SAhmad Khalifa 		    register_t))ec->ec_fptr)(ec->ec_arg1, ec->ec_arg2);
49950cd0be7SKonstantin Belousov 		break;
50050cd0be7SKonstantin Belousov 	case 3:
5013e8f4a30SAhmad Khalifa 		ec->ec_efi_status = ((register_t EFIABI_ATTR (*)(register_t,
5023e8f4a30SAhmad Khalifa 		    register_t, register_t))ec->ec_fptr)(ec->ec_arg1,
5033e8f4a30SAhmad Khalifa 		    ec->ec_arg2, ec->ec_arg3);
50450cd0be7SKonstantin Belousov 		break;
50550cd0be7SKonstantin Belousov 	case 4:
5063e8f4a30SAhmad Khalifa 		ec->ec_efi_status = ((register_t EFIABI_ATTR (*)(register_t,
5073e8f4a30SAhmad Khalifa 		    register_t, register_t, register_t))ec->ec_fptr)(
5083e8f4a30SAhmad Khalifa 		    ec->ec_arg1, ec->ec_arg2, ec->ec_arg3, ec->ec_arg4);
50950cd0be7SKonstantin Belousov 		break;
51050cd0be7SKonstantin Belousov 	case 5:
5113e8f4a30SAhmad Khalifa 		ec->ec_efi_status = ((register_t EFIABI_ATTR (*)(register_t,
5123e8f4a30SAhmad Khalifa 		    register_t, register_t, register_t, register_t))
5133e8f4a30SAhmad Khalifa 		    ec->ec_fptr)(ec->ec_arg1, ec->ec_arg2, ec->ec_arg3,
5143e8f4a30SAhmad Khalifa 		    ec->ec_arg4, ec->ec_arg5);
51550cd0be7SKonstantin Belousov 		break;
51650cd0be7SKonstantin Belousov 	default:
51750cd0be7SKonstantin Belousov 		panic("efi_rt_arch_call: %d args", (int)ec->ec_argcnt);
51850cd0be7SKonstantin Belousov 	}
51950cd0be7SKonstantin Belousov 
52050cd0be7SKonstantin Belousov 	return (0);
52150cd0be7SKonstantin Belousov }
52250cd0be7SKonstantin Belousov 
52350cd0be7SKonstantin Belousov static int
52450cd0be7SKonstantin Belousov efi_call(struct efirt_callinfo *ecp)
52550cd0be7SKonstantin Belousov {
5260e73a619SAndrew Turner 	int error;
5270e73a619SAndrew Turner 
5280e73a619SAndrew Turner 	error = efi_enter();
5290e73a619SAndrew Turner 	if (error != 0)
5300e73a619SAndrew Turner 		return (error);
53150cd0be7SKonstantin Belousov 	error = efi_rt_handle_faults ? efi_rt_arch_call(ecp) :
53250cd0be7SKonstantin Belousov 	    efi_rt_arch_call_nofault(ecp);
5330e73a619SAndrew Turner 	efi_leave();
53450cd0be7SKonstantin Belousov 	if (error == 0)
53550cd0be7SKonstantin Belousov 		error = efi_status_to_errno(ecp->ec_efi_status);
53650cd0be7SKonstantin Belousov 	else if (bootverbose)
53750cd0be7SKonstantin Belousov 		printf("EFI %s call faulted, error %d\n", ecp->ec_name, error);
5380e73a619SAndrew Turner 	return (error);
5390e73a619SAndrew Turner }
5400e73a619SAndrew Turner 
54150cd0be7SKonstantin Belousov #define	EFI_RT_METHOD_PA(method)				\
54250cd0be7SKonstantin Belousov     ((uintptr_t)((struct efi_rt *)efi_phys_to_kva((uintptr_t)	\
54350cd0be7SKonstantin Belousov     efi_runtime))->method)
54450cd0be7SKonstantin Belousov 
54550cd0be7SKonstantin Belousov static int
54650cd0be7SKonstantin Belousov efi_get_time_locked(struct efi_tm *tm, struct efi_tmcap *tmcap)
54750cd0be7SKonstantin Belousov {
54850cd0be7SKonstantin Belousov 	struct efirt_callinfo ec;
549eec892a8SMark Johnston 	int error;
55050cd0be7SKonstantin Belousov 
55150cd0be7SKonstantin Belousov 	EFI_TIME_OWNED();
55250cd0be7SKonstantin Belousov 	if (efi_runtime == NULL)
55350cd0be7SKonstantin Belousov 		return (ENXIO);
55450cd0be7SKonstantin Belousov 	bzero(&ec, sizeof(ec));
55550cd0be7SKonstantin Belousov 	ec.ec_name = "rt_gettime";
55650cd0be7SKonstantin Belousov 	ec.ec_argcnt = 2;
55750cd0be7SKonstantin Belousov 	ec.ec_arg1 = (uintptr_t)tm;
55850cd0be7SKonstantin Belousov 	ec.ec_arg2 = (uintptr_t)tmcap;
55950cd0be7SKonstantin Belousov 	ec.ec_fptr = EFI_RT_METHOD_PA(rt_gettime);
560eec892a8SMark Johnston 	error = efi_call(&ec);
561eec892a8SMark Johnston 	if (error == 0)
562eec892a8SMark Johnston 		kmsan_mark(tm, sizeof(*tm), KMSAN_STATE_INITED);
563eec892a8SMark Johnston 	return (error);
56450cd0be7SKonstantin Belousov }
56550cd0be7SKonstantin Belousov 
56627d39026SRoger Pau Monné static int
56727d39026SRoger Pau Monné get_time(struct efi_tm *tm)
5680e73a619SAndrew Turner {
5698521b4a9SKyle Evans 	struct efi_tmcap dummy;
5700e73a619SAndrew Turner 	int error;
5710e73a619SAndrew Turner 
5720e73a619SAndrew Turner 	if (efi_runtime == NULL)
5730e73a619SAndrew Turner 		return (ENXIO);
574d4be3789SKonstantin Belousov 	EFI_TIME_LOCK();
5758521b4a9SKyle Evans 	/*
5768521b4a9SKyle Evans 	 * UEFI spec states that the Capabilities argument to GetTime is
5778521b4a9SKyle Evans 	 * optional, but some UEFI implementations choke when passed a NULL
57892f1731bSKyle Evans 	 * pointer. Pass a dummy efi_tmcap, even though we won't use it,
5798521b4a9SKyle Evans 	 * to workaround such implementations.
5808521b4a9SKyle Evans 	 */
5818521b4a9SKyle Evans 	error = efi_get_time_locked(tm, &dummy);
582d4be3789SKonstantin Belousov 	EFI_TIME_UNLOCK();
58335e313cfSIan Lepore 	return (error);
58435e313cfSIan Lepore }
58535e313cfSIan Lepore 
58627d39026SRoger Pau Monné static int
5873f3ad565SJohannes Totz get_waketime(uint8_t *enabled, uint8_t *pending, struct efi_tm *tm)
5883f3ad565SJohannes Totz {
5893f3ad565SJohannes Totz 	struct efirt_callinfo ec;
5903f3ad565SJohannes Totz 	int error;
5913f3ad565SJohannes Totz #ifdef DEV_ACPI
5923f3ad565SJohannes Totz 	UINT32 acpiRtcEnabled;
5933f3ad565SJohannes Totz #endif
5943f3ad565SJohannes Totz 
5953f3ad565SJohannes Totz 	if (efi_runtime == NULL)
5963f3ad565SJohannes Totz 		return (ENXIO);
5973f3ad565SJohannes Totz 
5983f3ad565SJohannes Totz 	EFI_TIME_LOCK();
5993f3ad565SJohannes Totz 	bzero(&ec, sizeof(ec));
6003f3ad565SJohannes Totz 	ec.ec_name = "rt_getwaketime";
6013f3ad565SJohannes Totz 	ec.ec_argcnt = 3;
6023f3ad565SJohannes Totz 	ec.ec_arg1 = (uintptr_t)enabled;
6033f3ad565SJohannes Totz 	ec.ec_arg2 = (uintptr_t)pending;
6043f3ad565SJohannes Totz 	ec.ec_arg3 = (uintptr_t)tm;
6053f3ad565SJohannes Totz 	ec.ec_fptr = EFI_RT_METHOD_PA(rt_getwaketime);
6063f3ad565SJohannes Totz 	error = efi_call(&ec);
6073f3ad565SJohannes Totz 	EFI_TIME_UNLOCK();
6083f3ad565SJohannes Totz 
6093f3ad565SJohannes Totz #ifdef DEV_ACPI
6103f3ad565SJohannes Totz 	if (error == 0) {
6113f3ad565SJohannes Totz 		error = AcpiReadBitRegister(ACPI_BITREG_RT_CLOCK_ENABLE,
6123f3ad565SJohannes Totz 		    &acpiRtcEnabled);
6133f3ad565SJohannes Totz 		if (ACPI_SUCCESS(error)) {
6143f3ad565SJohannes Totz 			*enabled = *enabled && acpiRtcEnabled;
6153f3ad565SJohannes Totz 		} else
6163f3ad565SJohannes Totz 			error = EIO;
6173f3ad565SJohannes Totz 	}
6183f3ad565SJohannes Totz #endif
6193f3ad565SJohannes Totz 
6203f3ad565SJohannes Totz 	return (error);
6213f3ad565SJohannes Totz }
6223f3ad565SJohannes Totz 
6233f3ad565SJohannes Totz static int
6243f3ad565SJohannes Totz set_waketime(uint8_t enable, struct efi_tm *tm)
6253f3ad565SJohannes Totz {
6263f3ad565SJohannes Totz 	struct efirt_callinfo ec;
6273f3ad565SJohannes Totz 	int error;
6283f3ad565SJohannes Totz 
6293f3ad565SJohannes Totz 	if (efi_runtime == NULL)
6303f3ad565SJohannes Totz 		return (ENXIO);
6313f3ad565SJohannes Totz 
6323f3ad565SJohannes Totz 	EFI_TIME_LOCK();
6333f3ad565SJohannes Totz 	bzero(&ec, sizeof(ec));
6343f3ad565SJohannes Totz 	ec.ec_name = "rt_setwaketime";
6353f3ad565SJohannes Totz 	ec.ec_argcnt = 2;
6363f3ad565SJohannes Totz 	ec.ec_arg1 = (uintptr_t)enable;
6373f3ad565SJohannes Totz 	ec.ec_arg2 = (uintptr_t)tm;
6383f3ad565SJohannes Totz 	ec.ec_fptr = EFI_RT_METHOD_PA(rt_setwaketime);
6393f3ad565SJohannes Totz 	error = efi_call(&ec);
6403f3ad565SJohannes Totz 	EFI_TIME_UNLOCK();
6413f3ad565SJohannes Totz 
6423f3ad565SJohannes Totz #ifdef DEV_ACPI
6433f3ad565SJohannes Totz 	if (error == 0) {
6443f3ad565SJohannes Totz 		error = AcpiWriteBitRegister(ACPI_BITREG_RT_CLOCK_ENABLE,
6453f3ad565SJohannes Totz 		    (enable != 0) ? 1 : 0);
6463f3ad565SJohannes Totz 		if (ACPI_FAILURE(error))
6473f3ad565SJohannes Totz 			error = EIO;
6483f3ad565SJohannes Totz 	}
6493f3ad565SJohannes Totz #endif
6503f3ad565SJohannes Totz 
6513f3ad565SJohannes Totz 	return (error);
6523f3ad565SJohannes Totz }
6533f3ad565SJohannes Totz 
6543f3ad565SJohannes Totz static int
65527d39026SRoger Pau Monné get_time_capabilities(struct efi_tmcap *tmcap)
65635e313cfSIan Lepore {
65735e313cfSIan Lepore 	struct efi_tm dummy;
65835e313cfSIan Lepore 	int error;
65935e313cfSIan Lepore 
66035e313cfSIan Lepore 	if (efi_runtime == NULL)
66135e313cfSIan Lepore 		return (ENXIO);
662d4be3789SKonstantin Belousov 	EFI_TIME_LOCK();
66335e313cfSIan Lepore 	error = efi_get_time_locked(&dummy, tmcap);
664d4be3789SKonstantin Belousov 	EFI_TIME_UNLOCK();
6650e73a619SAndrew Turner 	return (error);
6660e73a619SAndrew Turner }
6670e73a619SAndrew Turner 
66827d39026SRoger Pau Monné static int
66927d39026SRoger Pau Monné reset_system(enum efi_reset type)
6700e73a619SAndrew Turner {
67150cd0be7SKonstantin Belousov 	struct efirt_callinfo ec;
6720e73a619SAndrew Turner 
67326649bb5SConrad Meyer 	switch (type) {
67426649bb5SConrad Meyer 	case EFI_RESET_COLD:
67526649bb5SConrad Meyer 	case EFI_RESET_WARM:
67626649bb5SConrad Meyer 	case EFI_RESET_SHUTDOWN:
67726649bb5SConrad Meyer 		break;
67826649bb5SConrad Meyer 	default:
67926649bb5SConrad Meyer 		return (EINVAL);
68026649bb5SConrad Meyer 	}
68150cd0be7SKonstantin Belousov 	if (efi_runtime == NULL)
68250cd0be7SKonstantin Belousov 		return (ENXIO);
68350cd0be7SKonstantin Belousov 	bzero(&ec, sizeof(ec));
68450cd0be7SKonstantin Belousov 	ec.ec_name = "rt_reset";
68550cd0be7SKonstantin Belousov 	ec.ec_argcnt = 4;
68626649bb5SConrad Meyer 	ec.ec_arg1 = (uintptr_t)type;
6878e6e1ba8SKonstantin Belousov 	ec.ec_arg2 = (uintptr_t)0;
6888e6e1ba8SKonstantin Belousov 	ec.ec_arg3 = (uintptr_t)0;
6898e6e1ba8SKonstantin Belousov 	ec.ec_arg4 = (uintptr_t)NULL;
69050cd0be7SKonstantin Belousov 	ec.ec_fptr = EFI_RT_METHOD_PA(rt_reset);
69150cd0be7SKonstantin Belousov 	return (efi_call(&ec));
6920e73a619SAndrew Turner }
6930e73a619SAndrew Turner 
6940e73a619SAndrew Turner static int
6950e73a619SAndrew Turner efi_set_time_locked(struct efi_tm *tm)
6960e73a619SAndrew Turner {
69750cd0be7SKonstantin Belousov 	struct efirt_callinfo ec;
6980e73a619SAndrew Turner 
6990e73a619SAndrew Turner 	EFI_TIME_OWNED();
70050cd0be7SKonstantin Belousov 	if (efi_runtime == NULL)
70150cd0be7SKonstantin Belousov 		return (ENXIO);
70250cd0be7SKonstantin Belousov 	bzero(&ec, sizeof(ec));
70350cd0be7SKonstantin Belousov 	ec.ec_name = "rt_settime";
70450cd0be7SKonstantin Belousov 	ec.ec_argcnt = 1;
70550cd0be7SKonstantin Belousov 	ec.ec_arg1 = (uintptr_t)tm;
70650cd0be7SKonstantin Belousov 	ec.ec_fptr = EFI_RT_METHOD_PA(rt_settime);
70750cd0be7SKonstantin Belousov 	return (efi_call(&ec));
7080e73a619SAndrew Turner }
7090e73a619SAndrew Turner 
71027d39026SRoger Pau Monné static int
71127d39026SRoger Pau Monné set_time(struct efi_tm *tm)
7120e73a619SAndrew Turner {
7130e73a619SAndrew Turner 	int error;
7140e73a619SAndrew Turner 
7150e73a619SAndrew Turner 	if (efi_runtime == NULL)
7160e73a619SAndrew Turner 		return (ENXIO);
717d4be3789SKonstantin Belousov 	EFI_TIME_LOCK();
7180e73a619SAndrew Turner 	error = efi_set_time_locked(tm);
719d4be3789SKonstantin Belousov 	EFI_TIME_UNLOCK();
7200e73a619SAndrew Turner 	return (error);
7210e73a619SAndrew Turner }
7220e73a619SAndrew Turner 
72327d39026SRoger Pau Monné static int
72427d39026SRoger Pau Monné var_get(efi_char *name, struct uuid *vendor, uint32_t *attrib,
7250e73a619SAndrew Turner     size_t *datasize, void *data)
7260e73a619SAndrew Turner {
72750cd0be7SKonstantin Belousov 	struct efirt_callinfo ec;
728eec892a8SMark Johnston 	int error;
7290e73a619SAndrew Turner 
73050cd0be7SKonstantin Belousov 	if (efi_runtime == NULL)
73150cd0be7SKonstantin Belousov 		return (ENXIO);
73250cd0be7SKonstantin Belousov 	bzero(&ec, sizeof(ec));
73350cd0be7SKonstantin Belousov 	ec.ec_argcnt = 5;
73450cd0be7SKonstantin Belousov 	ec.ec_name = "rt_getvar";
73550cd0be7SKonstantin Belousov 	ec.ec_arg1 = (uintptr_t)name;
73650cd0be7SKonstantin Belousov 	ec.ec_arg2 = (uintptr_t)vendor;
73750cd0be7SKonstantin Belousov 	ec.ec_arg3 = (uintptr_t)attrib;
73850cd0be7SKonstantin Belousov 	ec.ec_arg4 = (uintptr_t)datasize;
73950cd0be7SKonstantin Belousov 	ec.ec_arg5 = (uintptr_t)data;
74050cd0be7SKonstantin Belousov 	ec.ec_fptr = EFI_RT_METHOD_PA(rt_getvar);
741eec892a8SMark Johnston 	error = efi_call(&ec);
742eec892a8SMark Johnston 	if (error == 0)
743eec892a8SMark Johnston 		kmsan_mark(data, *datasize, KMSAN_STATE_INITED);
744eec892a8SMark Johnston 	return (error);
7450e73a619SAndrew Turner }
7460e73a619SAndrew Turner 
74727d39026SRoger Pau Monné static int
74827d39026SRoger Pau Monné var_nextname(size_t *namesize, efi_char *name, struct uuid *vendor)
7490e73a619SAndrew Turner {
75050cd0be7SKonstantin Belousov 	struct efirt_callinfo ec;
751eec892a8SMark Johnston 	int error;
7520e73a619SAndrew Turner 
75350cd0be7SKonstantin Belousov 	if (efi_runtime == NULL)
75450cd0be7SKonstantin Belousov 		return (ENXIO);
75550cd0be7SKonstantin Belousov 	bzero(&ec, sizeof(ec));
75650cd0be7SKonstantin Belousov 	ec.ec_argcnt = 3;
75750cd0be7SKonstantin Belousov 	ec.ec_name = "rt_scanvar";
75850cd0be7SKonstantin Belousov 	ec.ec_arg1 = (uintptr_t)namesize;
75950cd0be7SKonstantin Belousov 	ec.ec_arg2 = (uintptr_t)name;
76050cd0be7SKonstantin Belousov 	ec.ec_arg3 = (uintptr_t)vendor;
76150cd0be7SKonstantin Belousov 	ec.ec_fptr = EFI_RT_METHOD_PA(rt_scanvar);
762eec892a8SMark Johnston 	error = efi_call(&ec);
763eec892a8SMark Johnston 	if (error == 0)
764eec892a8SMark Johnston 		kmsan_mark(name, *namesize, KMSAN_STATE_INITED);
765eec892a8SMark Johnston 	return (error);
7660e73a619SAndrew Turner }
7670e73a619SAndrew Turner 
76827d39026SRoger Pau Monné static int
76927d39026SRoger Pau Monné var_set(efi_char *name, struct uuid *vendor, uint32_t attrib,
7700e73a619SAndrew Turner     size_t datasize, void *data)
7710e73a619SAndrew Turner {
77250cd0be7SKonstantin Belousov 	struct efirt_callinfo ec;
7730e73a619SAndrew Turner 
77450cd0be7SKonstantin Belousov 	if (efi_runtime == NULL)
77550cd0be7SKonstantin Belousov 		return (ENXIO);
77650cd0be7SKonstantin Belousov 	bzero(&ec, sizeof(ec));
77750cd0be7SKonstantin Belousov 	ec.ec_argcnt = 5;
77850cd0be7SKonstantin Belousov 	ec.ec_name = "rt_setvar";
77950cd0be7SKonstantin Belousov 	ec.ec_arg1 = (uintptr_t)name;
78050cd0be7SKonstantin Belousov 	ec.ec_arg2 = (uintptr_t)vendor;
78150cd0be7SKonstantin Belousov 	ec.ec_arg3 = (uintptr_t)attrib;
78250cd0be7SKonstantin Belousov 	ec.ec_arg4 = (uintptr_t)datasize;
78350cd0be7SKonstantin Belousov 	ec.ec_arg5 = (uintptr_t)data;
78450cd0be7SKonstantin Belousov 	ec.ec_fptr = EFI_RT_METHOD_PA(rt_setvar);
78550cd0be7SKonstantin Belousov 	return (efi_call(&ec));
7860e73a619SAndrew Turner }
7870e73a619SAndrew Turner 
78827d39026SRoger Pau Monné const static struct efi_ops efi_ops = {
78927d39026SRoger Pau Monné 	.rt_ok = rt_ok,
79027d39026SRoger Pau Monné 	.get_table = get_table,
791d12d651fSPavel Balaev 	.copy_table = copy_table,
79227d39026SRoger Pau Monné 	.get_time = get_time,
79327d39026SRoger Pau Monné 	.get_time_capabilities = get_time_capabilities,
79427d39026SRoger Pau Monné 	.reset_system = reset_system,
79527d39026SRoger Pau Monné 	.set_time = set_time,
7963f3ad565SJohannes Totz 	.get_waketime = get_waketime,
7973f3ad565SJohannes Totz 	.set_waketime = set_waketime,
79827d39026SRoger Pau Monné 	.var_get = var_get,
79927d39026SRoger Pau Monné 	.var_nextname = var_nextname,
80027d39026SRoger Pau Monné 	.var_set = var_set,
80127d39026SRoger Pau Monné };
80227d39026SRoger Pau Monné const struct efi_ops *active_efi_ops = &efi_ops;
80327d39026SRoger Pau Monné 
8040e73a619SAndrew Turner static int
8050e73a619SAndrew Turner efirt_modevents(module_t m, int event, void *arg __unused)
8060e73a619SAndrew Turner {
8070e73a619SAndrew Turner 
8080e73a619SAndrew Turner 	switch (event) {
8090e73a619SAndrew Turner 	case MOD_LOAD:
8100e73a619SAndrew Turner 		return (efi_init());
8110e73a619SAndrew Turner 
8120e73a619SAndrew Turner 	case MOD_UNLOAD:
8130e73a619SAndrew Turner 		efi_uninit();
8140e73a619SAndrew Turner 		return (0);
8150e73a619SAndrew Turner 
8160e73a619SAndrew Turner 	case MOD_SHUTDOWN:
8170e73a619SAndrew Turner 		return (0);
8180e73a619SAndrew Turner 
8190e73a619SAndrew Turner 	default:
8200e73a619SAndrew Turner 		return (EOPNOTSUPP);
8210e73a619SAndrew Turner 	}
8220e73a619SAndrew Turner }
8230e73a619SAndrew Turner 
8240e73a619SAndrew Turner static moduledata_t efirt_moddata = {
8250e73a619SAndrew Turner 	.name = "efirt",
8260e73a619SAndrew Turner 	.evhand = efirt_modevents,
8270e73a619SAndrew Turner 	.priv = NULL,
8280e73a619SAndrew Turner };
829ad456dd9SKyle Evans /* After fpuinitstate, before efidev */
830ad456dd9SKyle Evans DECLARE_MODULE(efirt, efirt_moddata, SI_SUB_DRIVERS, SI_ORDER_SECOND);
8310e73a619SAndrew Turner MODULE_VERSION(efirt, 1);
832