1022ee6c5SArd Biesheuvel /* 2022ee6c5SArd Biesheuvel * runtime-wrappers.c - Runtime Services function call wrappers 3022ee6c5SArd Biesheuvel * 4022ee6c5SArd Biesheuvel * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org> 5022ee6c5SArd Biesheuvel * 6022ee6c5SArd Biesheuvel * Split off from arch/x86/platform/efi/efi.c 7022ee6c5SArd Biesheuvel * 8022ee6c5SArd Biesheuvel * Copyright (C) 1999 VA Linux Systems 9022ee6c5SArd Biesheuvel * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> 10022ee6c5SArd Biesheuvel * Copyright (C) 1999-2002 Hewlett-Packard Co. 11022ee6c5SArd Biesheuvel * Copyright (C) 2005-2008 Intel Co. 12022ee6c5SArd Biesheuvel * Copyright (C) 2013 SuSE Labs 13022ee6c5SArd Biesheuvel * 14022ee6c5SArd Biesheuvel * This file is released under the GPLv2. 15022ee6c5SArd Biesheuvel */ 16022ee6c5SArd Biesheuvel 17161485e8SArd Biesheuvel #include <linux/bug.h> 18022ee6c5SArd Biesheuvel #include <linux/efi.h> 19*1d04ba17SMark Rutland #include <linux/irqflags.h> 20161485e8SArd Biesheuvel #include <linux/mutex.h> 21161485e8SArd Biesheuvel #include <linux/spinlock.h> 22*1d04ba17SMark Rutland #include <linux/stringify.h> 23022ee6c5SArd Biesheuvel #include <asm/efi.h> 24022ee6c5SArd Biesheuvel 25022ee6c5SArd Biesheuvel /* 26*1d04ba17SMark Rutland * Temporary scaffolding until all users provide ARCH_EFI_IRQ_FLAGS_MASK. 27*1d04ba17SMark Rutland */ 28*1d04ba17SMark Rutland #ifdef ARCH_EFI_IRQ_FLAGS_MASK 29*1d04ba17SMark Rutland static void efi_call_virt_check_flags(unsigned long flags, const char *call) 30*1d04ba17SMark Rutland { 31*1d04ba17SMark Rutland unsigned long cur_flags, mismatch; 32*1d04ba17SMark Rutland 33*1d04ba17SMark Rutland local_save_flags(cur_flags); 34*1d04ba17SMark Rutland 35*1d04ba17SMark Rutland mismatch = flags ^ cur_flags; 36*1d04ba17SMark Rutland if (!WARN_ON_ONCE(mismatch & ARCH_EFI_IRQ_FLAGS_MASK)) 37*1d04ba17SMark Rutland return; 38*1d04ba17SMark Rutland 39*1d04ba17SMark Rutland add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_NOW_UNRELIABLE); 40*1d04ba17SMark Rutland pr_err_ratelimited(FW_BUG "IRQ flags corrupted (0x%08lx=>0x%08lx) by EFI %s\n", 41*1d04ba17SMark Rutland flags, cur_flags, call); 42*1d04ba17SMark Rutland local_irq_restore(flags); 43*1d04ba17SMark Rutland } 44*1d04ba17SMark Rutland #else /* ARCH_EFI_IRQ_FLAGS_MASK */ 45*1d04ba17SMark Rutland static inline void efi_call_virt_check_flags(unsigned long flags, const char *call) {} 46*1d04ba17SMark Rutland #endif /* ARCH_EFI_IRQ_FLAGS_MASK */ 47*1d04ba17SMark Rutland 48*1d04ba17SMark Rutland /* 49f51c35f2SMark Rutland * Arch code can implement the following three template macros, avoiding 50f51c35f2SMark Rutland * reptition for the void/non-void return cases of {__,}efi_call_virt: 51f51c35f2SMark Rutland * 52f51c35f2SMark Rutland * * arch_efi_call_virt_setup 53f51c35f2SMark Rutland * 54f51c35f2SMark Rutland * Sets up the environment for the call (e.g. switching page tables, 55f51c35f2SMark Rutland * allowing kernel-mode use of floating point, if required). 56f51c35f2SMark Rutland * 57f51c35f2SMark Rutland * * arch_efi_call_virt 58f51c35f2SMark Rutland * 59f51c35f2SMark Rutland * Performs the call. The last expression in the macro must be the call 60f51c35f2SMark Rutland * itself, allowing the logic to be shared by the void and non-void 61f51c35f2SMark Rutland * cases. 62f51c35f2SMark Rutland * 63f51c35f2SMark Rutland * * arch_efi_call_virt_teardown 64f51c35f2SMark Rutland * 65f51c35f2SMark Rutland * Restores the usual kernel environment once the call has returned. 66f51c35f2SMark Rutland */ 67f51c35f2SMark Rutland 68f51c35f2SMark Rutland #define efi_call_virt(f, args...) \ 69f51c35f2SMark Rutland ({ \ 70f51c35f2SMark Rutland efi_status_t __s; \ 71*1d04ba17SMark Rutland unsigned long flags; \ 72f51c35f2SMark Rutland arch_efi_call_virt_setup(); \ 73*1d04ba17SMark Rutland local_save_flags(flags); \ 74f51c35f2SMark Rutland __s = arch_efi_call_virt(f, args); \ 75*1d04ba17SMark Rutland efi_call_virt_check_flags(flags, __stringify(f)); \ 76f51c35f2SMark Rutland arch_efi_call_virt_teardown(); \ 77f51c35f2SMark Rutland __s; \ 78f51c35f2SMark Rutland }) 79f51c35f2SMark Rutland 80f51c35f2SMark Rutland #define __efi_call_virt(f, args...) \ 81f51c35f2SMark Rutland ({ \ 82*1d04ba17SMark Rutland unsigned long flags; \ 83f51c35f2SMark Rutland arch_efi_call_virt_setup(); \ 84*1d04ba17SMark Rutland local_save_flags(flags); \ 85f51c35f2SMark Rutland arch_efi_call_virt(f, args); \ 86*1d04ba17SMark Rutland efi_call_virt_check_flags(flags, __stringify(f)); \ 87f51c35f2SMark Rutland arch_efi_call_virt_teardown(); \ 88f51c35f2SMark Rutland }) 89f51c35f2SMark Rutland 90f51c35f2SMark Rutland /* 91161485e8SArd Biesheuvel * According to section 7.1 of the UEFI spec, Runtime Services are not fully 92161485e8SArd Biesheuvel * reentrant, and there are particular combinations of calls that need to be 93161485e8SArd Biesheuvel * serialized. (source: UEFI Specification v2.4A) 94161485e8SArd Biesheuvel * 95161485e8SArd Biesheuvel * Table 31. Rules for Reentry Into Runtime Services 96161485e8SArd Biesheuvel * +------------------------------------+-------------------------------+ 97161485e8SArd Biesheuvel * | If previous call is busy in | Forbidden to call | 98161485e8SArd Biesheuvel * +------------------------------------+-------------------------------+ 99161485e8SArd Biesheuvel * | Any | SetVirtualAddressMap() | 100161485e8SArd Biesheuvel * +------------------------------------+-------------------------------+ 101161485e8SArd Biesheuvel * | ConvertPointer() | ConvertPointer() | 102161485e8SArd Biesheuvel * +------------------------------------+-------------------------------+ 103161485e8SArd Biesheuvel * | SetVariable() | ResetSystem() | 104161485e8SArd Biesheuvel * | UpdateCapsule() | | 105161485e8SArd Biesheuvel * | SetTime() | | 106161485e8SArd Biesheuvel * | SetWakeupTime() | | 107161485e8SArd Biesheuvel * | GetNextHighMonotonicCount() | | 108161485e8SArd Biesheuvel * +------------------------------------+-------------------------------+ 109161485e8SArd Biesheuvel * | GetVariable() | GetVariable() | 110161485e8SArd Biesheuvel * | GetNextVariableName() | GetNextVariableName() | 111161485e8SArd Biesheuvel * | SetVariable() | SetVariable() | 112161485e8SArd Biesheuvel * | QueryVariableInfo() | QueryVariableInfo() | 113161485e8SArd Biesheuvel * | UpdateCapsule() | UpdateCapsule() | 114161485e8SArd Biesheuvel * | QueryCapsuleCapabilities() | QueryCapsuleCapabilities() | 115161485e8SArd Biesheuvel * | GetNextHighMonotonicCount() | GetNextHighMonotonicCount() | 116161485e8SArd Biesheuvel * +------------------------------------+-------------------------------+ 117161485e8SArd Biesheuvel * | GetTime() | GetTime() | 118161485e8SArd Biesheuvel * | SetTime() | SetTime() | 119161485e8SArd Biesheuvel * | GetWakeupTime() | GetWakeupTime() | 120161485e8SArd Biesheuvel * | SetWakeupTime() | SetWakeupTime() | 121161485e8SArd Biesheuvel * +------------------------------------+-------------------------------+ 122161485e8SArd Biesheuvel * 123161485e8SArd Biesheuvel * Due to the fact that the EFI pstore may write to the variable store in 124161485e8SArd Biesheuvel * interrupt context, we need to use a spinlock for at least the groups that 125161485e8SArd Biesheuvel * contain SetVariable() and QueryVariableInfo(). That leaves little else, as 126161485e8SArd Biesheuvel * none of the remaining functions are actually ever called at runtime. 127161485e8SArd Biesheuvel * So let's just use a single spinlock to serialize all Runtime Services calls. 128161485e8SArd Biesheuvel */ 129161485e8SArd Biesheuvel static DEFINE_SPINLOCK(efi_runtime_lock); 130161485e8SArd Biesheuvel 131022ee6c5SArd Biesheuvel static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) 132022ee6c5SArd Biesheuvel { 133022ee6c5SArd Biesheuvel efi_status_t status; 134022ee6c5SArd Biesheuvel 135fe324494SArd Biesheuvel spin_lock(&efi_runtime_lock); 136022ee6c5SArd Biesheuvel status = efi_call_virt(get_time, tm, tc); 137fe324494SArd Biesheuvel spin_unlock(&efi_runtime_lock); 138022ee6c5SArd Biesheuvel return status; 139022ee6c5SArd Biesheuvel } 140022ee6c5SArd Biesheuvel 141022ee6c5SArd Biesheuvel static efi_status_t virt_efi_set_time(efi_time_t *tm) 142022ee6c5SArd Biesheuvel { 143022ee6c5SArd Biesheuvel efi_status_t status; 144022ee6c5SArd Biesheuvel 145fe324494SArd Biesheuvel spin_lock(&efi_runtime_lock); 146022ee6c5SArd Biesheuvel status = efi_call_virt(set_time, tm); 147fe324494SArd Biesheuvel spin_unlock(&efi_runtime_lock); 148022ee6c5SArd Biesheuvel return status; 149022ee6c5SArd Biesheuvel } 150022ee6c5SArd Biesheuvel 151022ee6c5SArd Biesheuvel static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled, 152022ee6c5SArd Biesheuvel efi_bool_t *pending, 153022ee6c5SArd Biesheuvel efi_time_t *tm) 154022ee6c5SArd Biesheuvel { 155022ee6c5SArd Biesheuvel efi_status_t status; 156022ee6c5SArd Biesheuvel 157fe324494SArd Biesheuvel spin_lock(&efi_runtime_lock); 158022ee6c5SArd Biesheuvel status = efi_call_virt(get_wakeup_time, enabled, pending, tm); 159fe324494SArd Biesheuvel spin_unlock(&efi_runtime_lock); 160022ee6c5SArd Biesheuvel return status; 161022ee6c5SArd Biesheuvel } 162022ee6c5SArd Biesheuvel 163022ee6c5SArd Biesheuvel static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) 164022ee6c5SArd Biesheuvel { 165022ee6c5SArd Biesheuvel efi_status_t status; 166022ee6c5SArd Biesheuvel 167fe324494SArd Biesheuvel spin_lock(&efi_runtime_lock); 168022ee6c5SArd Biesheuvel status = efi_call_virt(set_wakeup_time, enabled, tm); 169fe324494SArd Biesheuvel spin_unlock(&efi_runtime_lock); 170022ee6c5SArd Biesheuvel return status; 171022ee6c5SArd Biesheuvel } 172022ee6c5SArd Biesheuvel 173022ee6c5SArd Biesheuvel static efi_status_t virt_efi_get_variable(efi_char16_t *name, 174022ee6c5SArd Biesheuvel efi_guid_t *vendor, 175022ee6c5SArd Biesheuvel u32 *attr, 176022ee6c5SArd Biesheuvel unsigned long *data_size, 177022ee6c5SArd Biesheuvel void *data) 178022ee6c5SArd Biesheuvel { 179161485e8SArd Biesheuvel efi_status_t status; 180161485e8SArd Biesheuvel 181fe324494SArd Biesheuvel spin_lock(&efi_runtime_lock); 182161485e8SArd Biesheuvel status = efi_call_virt(get_variable, name, vendor, attr, data_size, 183161485e8SArd Biesheuvel data); 184fe324494SArd Biesheuvel spin_unlock(&efi_runtime_lock); 185161485e8SArd Biesheuvel return status; 186022ee6c5SArd Biesheuvel } 187022ee6c5SArd Biesheuvel 188022ee6c5SArd Biesheuvel static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, 189022ee6c5SArd Biesheuvel efi_char16_t *name, 190022ee6c5SArd Biesheuvel efi_guid_t *vendor) 191022ee6c5SArd Biesheuvel { 192161485e8SArd Biesheuvel efi_status_t status; 193161485e8SArd Biesheuvel 194fe324494SArd Biesheuvel spin_lock(&efi_runtime_lock); 195161485e8SArd Biesheuvel status = efi_call_virt(get_next_variable, name_size, name, vendor); 196fe324494SArd Biesheuvel spin_unlock(&efi_runtime_lock); 197161485e8SArd Biesheuvel return status; 198022ee6c5SArd Biesheuvel } 199022ee6c5SArd Biesheuvel 200022ee6c5SArd Biesheuvel static efi_status_t virt_efi_set_variable(efi_char16_t *name, 201022ee6c5SArd Biesheuvel efi_guid_t *vendor, 202022ee6c5SArd Biesheuvel u32 attr, 203022ee6c5SArd Biesheuvel unsigned long data_size, 204022ee6c5SArd Biesheuvel void *data) 205022ee6c5SArd Biesheuvel { 206161485e8SArd Biesheuvel efi_status_t status; 207161485e8SArd Biesheuvel 208fe324494SArd Biesheuvel spin_lock(&efi_runtime_lock); 209161485e8SArd Biesheuvel status = efi_call_virt(set_variable, name, vendor, attr, data_size, 210161485e8SArd Biesheuvel data); 211fe324494SArd Biesheuvel spin_unlock(&efi_runtime_lock); 212161485e8SArd Biesheuvel return status; 213022ee6c5SArd Biesheuvel } 214022ee6c5SArd Biesheuvel 2156d80dba1SMatt Fleming static efi_status_t 2166d80dba1SMatt Fleming virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor, 2176d80dba1SMatt Fleming u32 attr, unsigned long data_size, 2186d80dba1SMatt Fleming void *data) 2196d80dba1SMatt Fleming { 2206d80dba1SMatt Fleming efi_status_t status; 2216d80dba1SMatt Fleming 222fe324494SArd Biesheuvel if (!spin_trylock(&efi_runtime_lock)) 2236d80dba1SMatt Fleming return EFI_NOT_READY; 2246d80dba1SMatt Fleming 2256d80dba1SMatt Fleming status = efi_call_virt(set_variable, name, vendor, attr, data_size, 2266d80dba1SMatt Fleming data); 227fe324494SArd Biesheuvel spin_unlock(&efi_runtime_lock); 2286d80dba1SMatt Fleming return status; 2296d80dba1SMatt Fleming } 2306d80dba1SMatt Fleming 2316d80dba1SMatt Fleming 232022ee6c5SArd Biesheuvel static efi_status_t virt_efi_query_variable_info(u32 attr, 233022ee6c5SArd Biesheuvel u64 *storage_space, 234022ee6c5SArd Biesheuvel u64 *remaining_space, 235022ee6c5SArd Biesheuvel u64 *max_variable_size) 236022ee6c5SArd Biesheuvel { 237161485e8SArd Biesheuvel efi_status_t status; 238161485e8SArd Biesheuvel 239022ee6c5SArd Biesheuvel if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) 240022ee6c5SArd Biesheuvel return EFI_UNSUPPORTED; 241022ee6c5SArd Biesheuvel 242fe324494SArd Biesheuvel spin_lock(&efi_runtime_lock); 243161485e8SArd Biesheuvel status = efi_call_virt(query_variable_info, attr, storage_space, 244022ee6c5SArd Biesheuvel remaining_space, max_variable_size); 245fe324494SArd Biesheuvel spin_unlock(&efi_runtime_lock); 246161485e8SArd Biesheuvel return status; 247022ee6c5SArd Biesheuvel } 248022ee6c5SArd Biesheuvel 249d3cac1f8SArd Biesheuvel static efi_status_t 250d3cac1f8SArd Biesheuvel virt_efi_query_variable_info_nonblocking(u32 attr, 251d3cac1f8SArd Biesheuvel u64 *storage_space, 252d3cac1f8SArd Biesheuvel u64 *remaining_space, 253d3cac1f8SArd Biesheuvel u64 *max_variable_size) 254d3cac1f8SArd Biesheuvel { 255d3cac1f8SArd Biesheuvel efi_status_t status; 256d3cac1f8SArd Biesheuvel 257d3cac1f8SArd Biesheuvel if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) 258d3cac1f8SArd Biesheuvel return EFI_UNSUPPORTED; 259d3cac1f8SArd Biesheuvel 260fe324494SArd Biesheuvel if (!spin_trylock(&efi_runtime_lock)) 261d3cac1f8SArd Biesheuvel return EFI_NOT_READY; 262d3cac1f8SArd Biesheuvel 263d3cac1f8SArd Biesheuvel status = efi_call_virt(query_variable_info, attr, storage_space, 264d3cac1f8SArd Biesheuvel remaining_space, max_variable_size); 265fe324494SArd Biesheuvel spin_unlock(&efi_runtime_lock); 266d3cac1f8SArd Biesheuvel return status; 267d3cac1f8SArd Biesheuvel } 268d3cac1f8SArd Biesheuvel 269022ee6c5SArd Biesheuvel static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) 270022ee6c5SArd Biesheuvel { 271161485e8SArd Biesheuvel efi_status_t status; 272161485e8SArd Biesheuvel 273fe324494SArd Biesheuvel spin_lock(&efi_runtime_lock); 274161485e8SArd Biesheuvel status = efi_call_virt(get_next_high_mono_count, count); 275fe324494SArd Biesheuvel spin_unlock(&efi_runtime_lock); 276161485e8SArd Biesheuvel return status; 277022ee6c5SArd Biesheuvel } 278022ee6c5SArd Biesheuvel 279022ee6c5SArd Biesheuvel static void virt_efi_reset_system(int reset_type, 280022ee6c5SArd Biesheuvel efi_status_t status, 281022ee6c5SArd Biesheuvel unsigned long data_size, 282022ee6c5SArd Biesheuvel efi_char16_t *data) 283022ee6c5SArd Biesheuvel { 284fe324494SArd Biesheuvel spin_lock(&efi_runtime_lock); 285022ee6c5SArd Biesheuvel __efi_call_virt(reset_system, reset_type, status, data_size, data); 286fe324494SArd Biesheuvel spin_unlock(&efi_runtime_lock); 287022ee6c5SArd Biesheuvel } 288022ee6c5SArd Biesheuvel 289022ee6c5SArd Biesheuvel static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, 290022ee6c5SArd Biesheuvel unsigned long count, 291022ee6c5SArd Biesheuvel unsigned long sg_list) 292022ee6c5SArd Biesheuvel { 293161485e8SArd Biesheuvel efi_status_t status; 294161485e8SArd Biesheuvel 295022ee6c5SArd Biesheuvel if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) 296022ee6c5SArd Biesheuvel return EFI_UNSUPPORTED; 297022ee6c5SArd Biesheuvel 298fe324494SArd Biesheuvel spin_lock(&efi_runtime_lock); 299161485e8SArd Biesheuvel status = efi_call_virt(update_capsule, capsules, count, sg_list); 300fe324494SArd Biesheuvel spin_unlock(&efi_runtime_lock); 301161485e8SArd Biesheuvel return status; 302022ee6c5SArd Biesheuvel } 303022ee6c5SArd Biesheuvel 304022ee6c5SArd Biesheuvel static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, 305022ee6c5SArd Biesheuvel unsigned long count, 306022ee6c5SArd Biesheuvel u64 *max_size, 307022ee6c5SArd Biesheuvel int *reset_type) 308022ee6c5SArd Biesheuvel { 309161485e8SArd Biesheuvel efi_status_t status; 310161485e8SArd Biesheuvel 311022ee6c5SArd Biesheuvel if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) 312022ee6c5SArd Biesheuvel return EFI_UNSUPPORTED; 313022ee6c5SArd Biesheuvel 314fe324494SArd Biesheuvel spin_lock(&efi_runtime_lock); 315161485e8SArd Biesheuvel status = efi_call_virt(query_capsule_caps, capsules, count, max_size, 316022ee6c5SArd Biesheuvel reset_type); 317fe324494SArd Biesheuvel spin_unlock(&efi_runtime_lock); 318161485e8SArd Biesheuvel return status; 319022ee6c5SArd Biesheuvel } 320022ee6c5SArd Biesheuvel 321022ee6c5SArd Biesheuvel void efi_native_runtime_setup(void) 322022ee6c5SArd Biesheuvel { 323022ee6c5SArd Biesheuvel efi.get_time = virt_efi_get_time; 324022ee6c5SArd Biesheuvel efi.set_time = virt_efi_set_time; 325022ee6c5SArd Biesheuvel efi.get_wakeup_time = virt_efi_get_wakeup_time; 326022ee6c5SArd Biesheuvel efi.set_wakeup_time = virt_efi_set_wakeup_time; 327022ee6c5SArd Biesheuvel efi.get_variable = virt_efi_get_variable; 328022ee6c5SArd Biesheuvel efi.get_next_variable = virt_efi_get_next_variable; 329022ee6c5SArd Biesheuvel efi.set_variable = virt_efi_set_variable; 3306d80dba1SMatt Fleming efi.set_variable_nonblocking = virt_efi_set_variable_nonblocking; 331022ee6c5SArd Biesheuvel efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; 332022ee6c5SArd Biesheuvel efi.reset_system = virt_efi_reset_system; 333022ee6c5SArd Biesheuvel efi.query_variable_info = virt_efi_query_variable_info; 334d3cac1f8SArd Biesheuvel efi.query_variable_info_nonblocking = virt_efi_query_variable_info_nonblocking; 335022ee6c5SArd Biesheuvel efi.update_capsule = virt_efi_update_capsule; 336022ee6c5SArd Biesheuvel efi.query_capsule_caps = virt_efi_query_capsule_caps; 337022ee6c5SArd Biesheuvel } 338