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