305 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3#include <linux/efi.h>4#include <linux/module.h>5#include <linux/pstore.h>6#include <linux/slab.h>7#include <linux/ucs2_string.h>8 9MODULE_IMPORT_NS(EFIVAR);10 11#define DUMP_NAME_LEN 6612 13static unsigned int record_size = 1024;14module_param(record_size, uint, 0444);15MODULE_PARM_DESC(record_size, "size of each pstore UEFI var (in bytes, min/default=1024)");16 17#define PSTORE_EFI_ATTRIBUTES \18 (EFI_VARIABLE_NON_VOLATILE | \19 EFI_VARIABLE_BOOTSERVICE_ACCESS | \20 EFI_VARIABLE_RUNTIME_ACCESS)21 22static bool pstore_disable = IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE);23 24static int efivars_pstore_init(void);25static void efivars_pstore_exit(void);26 27static int efi_pstore_disable_set(const char *val, const struct kernel_param *kp)28{29 int err;30 bool old_pstore_disable = pstore_disable;31 32 err = param_set_bool(val, kp);33 if (err)34 return err;35 36 if (old_pstore_disable != pstore_disable) {37 if (pstore_disable)38 efivars_pstore_exit();39 else40 efivars_pstore_init();41 }42 43 return 0;44}45 46static const struct kernel_param_ops pstore_disable_ops = {47 .set = efi_pstore_disable_set,48 .get = param_get_bool,49};50 51module_param_cb(pstore_disable, &pstore_disable_ops, &pstore_disable, 0644);52__MODULE_PARM_TYPE(pstore_disable, "bool");53 54static int efi_pstore_open(struct pstore_info *psi)55{56 int err;57 58 err = efivar_lock();59 if (err)60 return err;61 62 psi->data = kzalloc(record_size, GFP_KERNEL);63 if (!psi->data)64 return -ENOMEM;65 66 return 0;67}68 69static int efi_pstore_close(struct pstore_info *psi)70{71 efivar_unlock();72 kfree(psi->data);73 return 0;74}75 76static inline u64 generic_id(u64 timestamp, unsigned int part, int count)77{78 return (timestamp * 100 + part) * 1000 + count;79}80 81static int efi_pstore_read_func(struct pstore_record *record,82 efi_char16_t *varname)83{84 unsigned long wlen, size = record_size;85 char name[DUMP_NAME_LEN], data_type;86 efi_status_t status;87 int cnt;88 unsigned int part;89 u64 time;90 91 ucs2_as_utf8(name, varname, DUMP_NAME_LEN);92 93 if (sscanf(name, "dump-type%u-%u-%d-%llu-%c",94 &record->type, &part, &cnt, &time, &data_type) == 5) {95 record->id = generic_id(time, part, cnt);96 record->part = part;97 record->count = cnt;98 record->time.tv_sec = time;99 record->time.tv_nsec = 0;100 if (data_type == 'C')101 record->compressed = true;102 else103 record->compressed = false;104 record->ecc_notice_size = 0;105 } else if (sscanf(name, "dump-type%u-%u-%d-%llu",106 &record->type, &part, &cnt, &time) == 4) {107 record->id = generic_id(time, part, cnt);108 record->part = part;109 record->count = cnt;110 record->time.tv_sec = time;111 record->time.tv_nsec = 0;112 record->compressed = false;113 record->ecc_notice_size = 0;114 } else if (sscanf(name, "dump-type%u-%u-%llu",115 &record->type, &part, &time) == 3) {116 /*117 * Check if an old format,118 * which doesn't support holding119 * multiple logs, remains.120 */121 record->id = generic_id(time, part, 0);122 record->part = part;123 record->count = 0;124 record->time.tv_sec = time;125 record->time.tv_nsec = 0;126 record->compressed = false;127 record->ecc_notice_size = 0;128 } else129 return 0;130 131 record->buf = kmalloc(size, GFP_KERNEL);132 if (!record->buf)133 return -ENOMEM;134 135 status = efivar_get_variable(varname, &LINUX_EFI_CRASH_GUID, NULL,136 &size, record->buf);137 if (status != EFI_SUCCESS) {138 kfree(record->buf);139 return efi_status_to_err(status);140 }141 142 /*143 * Store the name of the variable in the pstore_record priv field, so144 * we can reuse it later if we need to delete the EFI variable from the145 * variable store.146 */147 wlen = (ucs2_strnlen(varname, DUMP_NAME_LEN) + 1) * sizeof(efi_char16_t);148 record->priv = kmemdup(varname, wlen, GFP_KERNEL);149 if (!record->priv) {150 kfree(record->buf);151 return -ENOMEM;152 }153 154 return size;155}156 157static ssize_t efi_pstore_read(struct pstore_record *record)158{159 efi_char16_t *varname = record->psi->data;160 efi_guid_t guid = LINUX_EFI_CRASH_GUID;161 unsigned long varname_size;162 efi_status_t status;163 164 for (;;) {165 /*166 * A small set of old UEFI implementations reject sizes167 * above a certain threshold, the lowest seen in the wild168 * is 512.169 *170 * TODO: Commonize with the iteration implementation in171 * fs/efivarfs to keep all the quirks in one place.172 */173 varname_size = 512;174 175 /*176 * If this is the first read() call in the pstore enumeration,177 * varname will be the empty string, and the GetNextVariable()178 * runtime service call will return the first EFI variable in179 * its own enumeration order, ignoring the guid argument.180 *181 * Subsequent calls to GetNextVariable() must pass the name and182 * guid values returned by the previous call, which is why we183 * store varname in record->psi->data. Given that we only184 * enumerate variables with the efi-pstore GUID, there is no185 * need to record the guid return value.186 */187 status = efivar_get_next_variable(&varname_size, varname, &guid);188 if (status == EFI_NOT_FOUND)189 return 0;190 191 if (status != EFI_SUCCESS)192 return efi_status_to_err(status);193 194 /* skip variables that don't concern us */195 if (efi_guidcmp(guid, LINUX_EFI_CRASH_GUID))196 continue;197 198 return efi_pstore_read_func(record, varname);199 }200}201 202static int efi_pstore_write(struct pstore_record *record)203{204 char name[DUMP_NAME_LEN];205 efi_char16_t efi_name[DUMP_NAME_LEN];206 efi_status_t status;207 int i;208 209 record->id = generic_id(record->time.tv_sec, record->part,210 record->count);211 212 /* Since we copy the entire length of name, make sure it is wiped. */213 memset(name, 0, sizeof(name));214 215 snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lld-%c",216 record->type, record->part, record->count,217 (long long)record->time.tv_sec,218 record->compressed ? 'C' : 'D');219 220 for (i = 0; i < DUMP_NAME_LEN; i++)221 efi_name[i] = name[i];222 223 if (efivar_trylock())224 return -EBUSY;225 status = efivar_set_variable_locked(efi_name, &LINUX_EFI_CRASH_GUID,226 PSTORE_EFI_ATTRIBUTES,227 record->size, record->psi->buf,228 true);229 efivar_unlock();230 return efi_status_to_err(status);231};232 233static int efi_pstore_erase(struct pstore_record *record)234{235 efi_status_t status;236 237 status = efivar_set_variable(record->priv, &LINUX_EFI_CRASH_GUID,238 PSTORE_EFI_ATTRIBUTES, 0, NULL);239 240 if (status != EFI_SUCCESS && status != EFI_NOT_FOUND)241 return efi_status_to_err(status);242 return 0;243}244 245static struct pstore_info efi_pstore_info = {246 .owner = THIS_MODULE,247 .name = KBUILD_MODNAME,248 .flags = PSTORE_FLAGS_DMESG,249 .open = efi_pstore_open,250 .close = efi_pstore_close,251 .read = efi_pstore_read,252 .write = efi_pstore_write,253 .erase = efi_pstore_erase,254};255 256static int efivars_pstore_init(void)257{258 if (!efivar_supports_writes())259 return 0;260 261 if (pstore_disable)262 return 0;263 264 /*265 * Notice that 1024 is the minimum here to prevent issues with266 * decompression algorithms that were spotted during tests;267 * even in the case of not using compression, smaller values would268 * just pollute more the pstore FS with many small collected files.269 */270 if (record_size < 1024)271 record_size = 1024;272 273 efi_pstore_info.buf = kmalloc(record_size, GFP_KERNEL);274 if (!efi_pstore_info.buf)275 return -ENOMEM;276 277 efi_pstore_info.bufsize = record_size;278 279 if (pstore_register(&efi_pstore_info)) {280 kfree(efi_pstore_info.buf);281 efi_pstore_info.buf = NULL;282 efi_pstore_info.bufsize = 0;283 }284 285 return 0;286}287 288static void efivars_pstore_exit(void)289{290 if (!efi_pstore_info.bufsize)291 return;292 293 pstore_unregister(&efi_pstore_info);294 kfree(efi_pstore_info.buf);295 efi_pstore_info.buf = NULL;296 efi_pstore_info.bufsize = 0;297}298 299module_init(efivars_pstore_init);300module_exit(efivars_pstore_exit);301 302MODULE_DESCRIPTION("EFI variable backend for pstore");303MODULE_LICENSE("GPL");304MODULE_ALIAS("platform:efivars");305