// SPDX-License-Identifier: GPL-2.0 /* * efi backend for prom environment * * Copyright 2017, 2019 AVM GmbH */ #define pr_fmt(fmt) "AVM prom-env: " fmt #include #include #include struct prom_env_entry { char *name; char *value; struct list_head list; }; static LIST_HEAD(prom_env); static bool prom_env_finalized; static int __init prom_env_add(const char *var_name, const char *var_value, size_t var_value_size) { struct prom_env_entry *entry; entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; entry->name = kstrdup(var_name, GFP_KERNEL); if (!entry->name) goto oom_free_entry; if (var_value && var_value_size) { entry->value = kstrndup(var_value, var_value_size, GFP_KERNEL); if (!entry->value) goto oom_free_name; } list_add_tail(&entry->list, &prom_env); return 0; oom_free_name: kfree(entry->name); oom_free_entry: kfree(entry); return -ENOMEM; } int __init avm_prom_env_init(void) { static struct mutex prom_initlock __initdata = __MUTEX_INITIALIZER(prom_initlock); const struct _TFFS_Name_Table *ul_env_entry; struct avm_efivar_resource *res; const char *ul_env_varname; const char *efivar_value; size_t efivar_valsiz; int ret = 0; mutex_lock(&prom_initlock); if (!list_empty(&prom_env)) { WARN_ON(1); ret = -EEXIST; goto err_noinit; } pr_info("Initializing urlader environment from EFI vars\n"); res = avm_efivar_resource_create(); if (IS_ERR(res)) { ret = PTR_ERR(res); goto err_noinit; } /* * Query the tffs driver for the list of urlader environment * variables, and populate the prom values from the corresponding EFI * variables. * * If a variable is not found in the EFI, explicitly set it to null, * so that this will be propagated through when the prom is synced to * tffs. */ for (ul_env_entry = avm_urlader_get_nametable(); ul_env_entry && ul_env_entry->id; ++ul_env_entry) { if (!ul_env_entry->Name[0]) continue; if (ul_env_entry->id == FLASH_FS_TABLE_VERSION || ul_env_entry->id == FLASH_FS_NAME_TABLE) continue; ul_env_varname = ul_env_entry->Name; efivar_value = avm_efivar_get(res, ul_env_varname, &efivar_valsiz); ret = PTR_ERR_OR_ZERO(efivar_value); if (ret && ret != -ENOENT) { pr_err("Error reading EFI variable %s: %d\n", ul_env_varname, ret); break; } /* Normalize absent and empty values to NULL */ if (ret == -ENOENT || !efivar_valsiz || !efivar_value[0]) { efivar_value = NULL; efivar_valsiz = 0; } ret = prom_env_add(ul_env_varname, efivar_value, efivar_valsiz); if (ret) break; } avm_efivar_resource_destroy(res); /* Signal prom readers that EFI variables have been copied to prom, * the prom is permanently fixed and they can safely access it without * further locking. */ smp_store_release(&prom_env_finalized, true); err_noinit: if (ret) pr_err("Error initializing urlader environment: %d\n", ret); mutex_unlock(&prom_initlock); return ret; } /* Sync prom env to tffs. Called by the tffs driver when tffs is operational. * Note that any changes to env variables made in tffs later are propagated to * the efi, but not the prom, so the prom is immutable once initialized. */ int avm_prom_tffs_sync(void) { const struct prom_env_entry *entry; char *tffs_env_value; int ret = 0; /* Can't do anything if EFI variables haven't been copied to prom yet */ if (!smp_load_acquire(&prom_env_finalized)) { pr_err("urlader env not ready, cannot sync urlader env to tffs\n"); return -EAGAIN; } pr_info("Syncing urlader env to tffs\n"); list_for_each_entry(entry, &prom_env, list) { tffs_env_value = avm_urlader_env_get_value(entry->name); /* Skip identical entries */ if (!entry->value && !tffs_env_value) continue; if (entry->value && tffs_env_value && !strcmp(entry->value, tffs_env_value)) { kfree(tffs_env_value); continue; } kfree(tffs_env_value); /* Sync differing values from efi to tffs environment */ pr_info("Setting tffs urlader env '%s' to '%s'\n", entry->name, entry->value); ret = avm_urlader_env_set_variable(entry->name, entry->value); if (ret) break; } if (ret) pr_err("Syncing urlader env to tffs failed: %d\n", ret); return ret; } EXPORT_SYMBOL(avm_prom_tffs_sync); char *prom_getenv(char *varname) { const struct prom_env_entry *entry; char *value = NULL; bool found = false; /* Can't do anything if EFI variables haven't been copied to prom yet */ if (!smp_load_acquire(&prom_env_finalized)) { pr_err("getenv: urlader env not ready, cannot get variable: %s\n", varname); return NULL; } list_for_each_entry(entry, &prom_env, list) { if (!strcmp(entry->name, varname)) { value = entry->value; found = true; break; } } if (found) pr_info("getenv: %s=%s\n", varname, value); else pr_err("getenv: Variable not found: %s\n", varname); return value; } EXPORT_SYMBOL(prom_getenv);