// SPDX-License-Identifier: GPL-2.0 /* * AVM simple efivar API wrappers * * Copyright 2019 AVM GmbH * */ #include #include #include #include #define AVM_EFIVAR_NAME_LEN sizeof_field(struct _TFFS_Name_Table, Name) struct avm_efivar_resource { struct efivar_entry entry; }; static int ascii_to_efistr(const char *str, efi_char16_t *efistr, size_t efisize) { if (!efisize) return -EINVAL; while (--efisize && *str) *efistr++ = *str++; *efistr = '\0'; if (*str) return -ERANGE; return 0; } struct avm_efivar_resource *avm_efivar_resource_create(void) { struct avm_efivar_resource *res; res = kzalloc(sizeof(*res), GFP_KERNEL); if (!res) return ERR_PTR(-ENOMEM); res->entry.var.VendorGuid = EFI_AVM_VARIABLE_GUID; return res; } EXPORT_SYMBOL(avm_efivar_resource_create); void avm_efivar_resource_destroy(struct avm_efivar_resource *res) { kfree(res); } EXPORT_SYMBOL(avm_efivar_resource_destroy); int avm_efivar_resource_attach_var(struct avm_efivar_resource *res, const char *varname) { struct efi_variable *efivar = &res->entry.var; int ret; if (varname) { ret = ascii_to_efistr(varname, efivar->VariableName, ARRAY_SIZE(efivar->VariableName)); if (ret) return ret; } if (!efivar->VariableName[0]) return -EINVAL; return 0; } EXPORT_SYMBOL(avm_efivar_resource_attach_var); int _avm_efivar_set(struct avm_efivar_resource *res, const char *varname, void *value, size_t valsize, bool mayblock) { struct efi_variable *efivar = &res->entry.var; int ret; ret = avm_efivar_resource_attach_var(res, varname); if (ret) return ret; ret = efivar_entry_set_safe(efivar->VariableName, EFI_AVM_VARIABLE_GUID, (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS), mayblock, valsize, value); return ret; } EXPORT_SYMBOL(_avm_efivar_set); int _avm_efivar_set_one(const char *varname, void *val, size_t valsize, bool mayblock) { efi_char16_t efi_varname[AVM_EFIVAR_NAME_LEN]; int ret; if (strlen(varname) >= sizeof(efi_varname)) return -EINVAL; ascii_to_efistr(varname, efi_varname, ARRAY_SIZE(efi_varname)); ret = efivar_entry_set_safe(efi_varname, EFI_AVM_VARIABLE_GUID, (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS), mayblock, valsize, val); return ret; } EXPORT_SYMBOL(_avm_efivar_set_one); void *avm_efivar_get(struct avm_efivar_resource *res, const char *varname, size_t *valsize) { struct efivar_entry *efivar_entry = &res->entry; struct efi_variable *efivar = &efivar_entry->var; int ret; ret = avm_efivar_resource_attach_var(res, varname); if (ret) return ERR_PTR(ret); ret = efivar_entry_size(efivar_entry, &efivar->DataSize); if (ret) return ERR_PTR(ret); if (efivar->DataSize > sizeof(efivar->Data)) return ERR_PTR(-ERANGE); efivar->DataSize = sizeof(efivar->Data); ret = efivar_entry_get(efivar_entry, NULL, &efivar->DataSize, efivar->Data); if (ret) return ERR_PTR(ret); if (valsize) *valsize = efivar->DataSize; return efivar->Data; } EXPORT_SYMBOL(avm_efivar_get); ssize_t avm_efivar_get_one(const char *varname, void *val, size_t valsize) { struct avm_efivar_resource *res; const void *efival; size_t efival_size; int err = 0; res = avm_efivar_resource_create(); if (IS_ERR(res)) return PTR_ERR(res); efival = avm_efivar_get(res, varname, &efival_size); if (IS_ERR(efival)) { err = PTR_ERR(efival); goto out; } /* EFI variables probably don't exceed 2GB, but then, pigs can fly */ if ((ssize_t)efival_size < 0) { err = -ERANGE; goto out; } if (efival_size < valsize) valsize = efival_size; memcpy(val, efival, valsize); out: avm_efivar_resource_destroy(res); return err ?: efival_size; } EXPORT_SYMBOL(avm_efivar_get_one); int efivar_read_one_fixed_size_verbose(const char *varname, void *val, size_t siz) { ssize_t ret; ret = avm_efivar_get_one(varname, val, siz); if (ret < 0) { pr_err("Error reading EFI variable %s: %d\n", varname, ret); return ret; } if (ret != siz) { pr_err("Error reading EFI variable %s: read %d bytes, expected %d\n", varname, ret, siz); return -EILSEQ; } return 0; }