0001
0002
0003 #include <linux/efi.h>
0004 #include <linux/module.h>
0005 #include <linux/pstore.h>
0006 #include <linux/slab.h>
0007 #include <linux/ucs2_string.h>
0008
0009 MODULE_IMPORT_NS(EFIVAR);
0010
0011 #define DUMP_NAME_LEN 66
0012
0013 #define EFIVARS_DATA_SIZE_MAX 1024
0014
0015 static bool efivars_pstore_disable =
0016 IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE);
0017
0018 module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644);
0019
0020 #define PSTORE_EFI_ATTRIBUTES \
0021 (EFI_VARIABLE_NON_VOLATILE | \
0022 EFI_VARIABLE_BOOTSERVICE_ACCESS | \
0023 EFI_VARIABLE_RUNTIME_ACCESS)
0024
0025 static int efi_pstore_open(struct pstore_info *psi)
0026 {
0027 int err;
0028
0029 err = efivar_lock();
0030 if (err)
0031 return err;
0032
0033 psi->data = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL);
0034 if (!psi->data)
0035 return -ENOMEM;
0036
0037 return 0;
0038 }
0039
0040 static int efi_pstore_close(struct pstore_info *psi)
0041 {
0042 efivar_unlock();
0043 kfree(psi->data);
0044 return 0;
0045 }
0046
0047 static inline u64 generic_id(u64 timestamp, unsigned int part, int count)
0048 {
0049 return (timestamp * 100 + part) * 1000 + count;
0050 }
0051
0052 static int efi_pstore_read_func(struct pstore_record *record,
0053 efi_char16_t *varname)
0054 {
0055 unsigned long wlen, size = EFIVARS_DATA_SIZE_MAX;
0056 char name[DUMP_NAME_LEN], data_type;
0057 efi_status_t status;
0058 int cnt;
0059 unsigned int part;
0060 u64 time;
0061
0062 ucs2_as_utf8(name, varname, DUMP_NAME_LEN);
0063
0064 if (sscanf(name, "dump-type%u-%u-%d-%llu-%c",
0065 &record->type, &part, &cnt, &time, &data_type) == 5) {
0066 record->id = generic_id(time, part, cnt);
0067 record->part = part;
0068 record->count = cnt;
0069 record->time.tv_sec = time;
0070 record->time.tv_nsec = 0;
0071 if (data_type == 'C')
0072 record->compressed = true;
0073 else
0074 record->compressed = false;
0075 record->ecc_notice_size = 0;
0076 } else if (sscanf(name, "dump-type%u-%u-%d-%llu",
0077 &record->type, &part, &cnt, &time) == 4) {
0078 record->id = generic_id(time, part, cnt);
0079 record->part = part;
0080 record->count = cnt;
0081 record->time.tv_sec = time;
0082 record->time.tv_nsec = 0;
0083 record->compressed = false;
0084 record->ecc_notice_size = 0;
0085 } else if (sscanf(name, "dump-type%u-%u-%llu",
0086 &record->type, &part, &time) == 3) {
0087
0088
0089
0090
0091
0092 record->id = generic_id(time, part, 0);
0093 record->part = part;
0094 record->count = 0;
0095 record->time.tv_sec = time;
0096 record->time.tv_nsec = 0;
0097 record->compressed = false;
0098 record->ecc_notice_size = 0;
0099 } else
0100 return 0;
0101
0102 record->buf = kmalloc(size, GFP_KERNEL);
0103 if (!record->buf)
0104 return -ENOMEM;
0105
0106 status = efivar_get_variable(varname, &LINUX_EFI_CRASH_GUID, NULL,
0107 &size, record->buf);
0108 if (status != EFI_SUCCESS) {
0109 kfree(record->buf);
0110 return -EIO;
0111 }
0112
0113
0114
0115
0116
0117
0118 wlen = (ucs2_strnlen(varname, DUMP_NAME_LEN) + 1) * sizeof(efi_char16_t);
0119 record->priv = kmemdup(varname, wlen, GFP_KERNEL);
0120 if (!record->priv) {
0121 kfree(record->buf);
0122 return -ENOMEM;
0123 }
0124
0125 return size;
0126 }
0127
0128 static ssize_t efi_pstore_read(struct pstore_record *record)
0129 {
0130 efi_char16_t *varname = record->psi->data;
0131 efi_guid_t guid = LINUX_EFI_CRASH_GUID;
0132 unsigned long varname_size;
0133 efi_status_t status;
0134
0135 for (;;) {
0136 varname_size = EFIVARS_DATA_SIZE_MAX;
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150 status = efivar_get_next_variable(&varname_size, varname, &guid);
0151 if (status == EFI_NOT_FOUND)
0152 return 0;
0153
0154 if (status != EFI_SUCCESS)
0155 return -EIO;
0156
0157
0158 if (efi_guidcmp(guid, LINUX_EFI_CRASH_GUID))
0159 continue;
0160
0161 return efi_pstore_read_func(record, varname);
0162 }
0163 }
0164
0165 static int efi_pstore_write(struct pstore_record *record)
0166 {
0167 char name[DUMP_NAME_LEN];
0168 efi_char16_t efi_name[DUMP_NAME_LEN];
0169 efi_status_t status;
0170 int i;
0171
0172 record->id = generic_id(record->time.tv_sec, record->part,
0173 record->count);
0174
0175
0176 memset(name, 0, sizeof(name));
0177
0178 snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lld-%c",
0179 record->type, record->part, record->count,
0180 (long long)record->time.tv_sec,
0181 record->compressed ? 'C' : 'D');
0182
0183 for (i = 0; i < DUMP_NAME_LEN; i++)
0184 efi_name[i] = name[i];
0185
0186 if (efivar_trylock())
0187 return -EBUSY;
0188 status = efivar_set_variable_locked(efi_name, &LINUX_EFI_CRASH_GUID,
0189 PSTORE_EFI_ATTRIBUTES,
0190 record->size, record->psi->buf,
0191 true);
0192 efivar_unlock();
0193 return status == EFI_SUCCESS ? 0 : -EIO;
0194 };
0195
0196 static int efi_pstore_erase(struct pstore_record *record)
0197 {
0198 efi_status_t status;
0199
0200 status = efivar_set_variable(record->priv, &LINUX_EFI_CRASH_GUID,
0201 PSTORE_EFI_ATTRIBUTES, 0, NULL);
0202
0203 if (status != EFI_SUCCESS && status != EFI_NOT_FOUND)
0204 return -EIO;
0205 return 0;
0206 }
0207
0208 static struct pstore_info efi_pstore_info = {
0209 .owner = THIS_MODULE,
0210 .name = "efi",
0211 .flags = PSTORE_FLAGS_DMESG,
0212 .open = efi_pstore_open,
0213 .close = efi_pstore_close,
0214 .read = efi_pstore_read,
0215 .write = efi_pstore_write,
0216 .erase = efi_pstore_erase,
0217 };
0218
0219 static __init int efivars_pstore_init(void)
0220 {
0221 if (!efivar_supports_writes())
0222 return 0;
0223
0224 if (efivars_pstore_disable)
0225 return 0;
0226
0227 efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
0228 if (!efi_pstore_info.buf)
0229 return -ENOMEM;
0230
0231 efi_pstore_info.bufsize = 1024;
0232
0233 if (pstore_register(&efi_pstore_info)) {
0234 kfree(efi_pstore_info.buf);
0235 efi_pstore_info.buf = NULL;
0236 efi_pstore_info.bufsize = 0;
0237 }
0238
0239 return 0;
0240 }
0241
0242 static __exit void efivars_pstore_exit(void)
0243 {
0244 if (!efi_pstore_info.bufsize)
0245 return;
0246
0247 pstore_unregister(&efi_pstore_info);
0248 kfree(efi_pstore_info.buf);
0249 efi_pstore_info.buf = NULL;
0250 efi_pstore_info.bufsize = 0;
0251 }
0252
0253 module_init(efivars_pstore_init);
0254 module_exit(efivars_pstore_exit);
0255
0256 MODULE_DESCRIPTION("EFI variable backend for pstore");
0257 MODULE_LICENSE("GPL");
0258 MODULE_ALIAS("platform:efivars");