efi-pstore.c (10928B)
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 9#define DUMP_NAME_LEN 66 10 11#define EFIVARS_DATA_SIZE_MAX 1024 12 13static bool efivars_pstore_disable = 14 IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE); 15 16module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644); 17 18#define PSTORE_EFI_ATTRIBUTES \ 19 (EFI_VARIABLE_NON_VOLATILE | \ 20 EFI_VARIABLE_BOOTSERVICE_ACCESS | \ 21 EFI_VARIABLE_RUNTIME_ACCESS) 22 23static LIST_HEAD(efi_pstore_list); 24static DECLARE_WORK(efivar_work, NULL); 25 26static int efi_pstore_open(struct pstore_info *psi) 27{ 28 psi->data = NULL; 29 return 0; 30} 31 32static int efi_pstore_close(struct pstore_info *psi) 33{ 34 psi->data = NULL; 35 return 0; 36} 37 38static inline u64 generic_id(u64 timestamp, unsigned int part, int count) 39{ 40 return (timestamp * 100 + part) * 1000 + count; 41} 42 43static int efi_pstore_read_func(struct efivar_entry *entry, 44 struct pstore_record *record) 45{ 46 efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 47 char name[DUMP_NAME_LEN], data_type; 48 int i; 49 int cnt; 50 unsigned int part; 51 unsigned long size; 52 u64 time; 53 54 if (efi_guidcmp(entry->var.VendorGuid, vendor)) 55 return 0; 56 57 for (i = 0; i < DUMP_NAME_LEN; i++) 58 name[i] = entry->var.VariableName[i]; 59 60 if (sscanf(name, "dump-type%u-%u-%d-%llu-%c", 61 &record->type, &part, &cnt, &time, &data_type) == 5) { 62 record->id = generic_id(time, part, cnt); 63 record->part = part; 64 record->count = cnt; 65 record->time.tv_sec = time; 66 record->time.tv_nsec = 0; 67 if (data_type == 'C') 68 record->compressed = true; 69 else 70 record->compressed = false; 71 record->ecc_notice_size = 0; 72 } else if (sscanf(name, "dump-type%u-%u-%d-%llu", 73 &record->type, &part, &cnt, &time) == 4) { 74 record->id = generic_id(time, part, cnt); 75 record->part = part; 76 record->count = cnt; 77 record->time.tv_sec = time; 78 record->time.tv_nsec = 0; 79 record->compressed = false; 80 record->ecc_notice_size = 0; 81 } else if (sscanf(name, "dump-type%u-%u-%llu", 82 &record->type, &part, &time) == 3) { 83 /* 84 * Check if an old format, 85 * which doesn't support holding 86 * multiple logs, remains. 87 */ 88 record->id = generic_id(time, part, 0); 89 record->part = part; 90 record->count = 0; 91 record->time.tv_sec = time; 92 record->time.tv_nsec = 0; 93 record->compressed = false; 94 record->ecc_notice_size = 0; 95 } else 96 return 0; 97 98 entry->var.DataSize = 1024; 99 __efivar_entry_get(entry, &entry->var.Attributes, 100 &entry->var.DataSize, entry->var.Data); 101 size = entry->var.DataSize; 102 memcpy(record->buf, entry->var.Data, 103 (size_t)min_t(unsigned long, EFIVARS_DATA_SIZE_MAX, size)); 104 105 return size; 106} 107 108/** 109 * efi_pstore_scan_sysfs_enter 110 * @pos: scanning entry 111 * @next: next entry 112 * @head: list head 113 */ 114static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos, 115 struct efivar_entry *next, 116 struct list_head *head) 117{ 118 pos->scanning = true; 119 if (&next->list != head) 120 next->scanning = true; 121} 122 123/** 124 * __efi_pstore_scan_sysfs_exit 125 * @entry: deleting entry 126 * @turn_off_scanning: Check if a scanning flag should be turned off 127 */ 128static inline int __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry, 129 bool turn_off_scanning) 130{ 131 if (entry->deleting) { 132 list_del(&entry->list); 133 efivar_entry_iter_end(); 134 kfree(entry); 135 if (efivar_entry_iter_begin()) 136 return -EINTR; 137 } else if (turn_off_scanning) 138 entry->scanning = false; 139 140 return 0; 141} 142 143/** 144 * efi_pstore_scan_sysfs_exit 145 * @pos: scanning entry 146 * @next: next entry 147 * @head: list head 148 * @stop: a flag checking if scanning will stop 149 */ 150static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos, 151 struct efivar_entry *next, 152 struct list_head *head, bool stop) 153{ 154 int ret = __efi_pstore_scan_sysfs_exit(pos, true); 155 156 if (ret) 157 return ret; 158 159 if (stop) 160 ret = __efi_pstore_scan_sysfs_exit(next, &next->list != head); 161 return ret; 162} 163 164/** 165 * efi_pstore_sysfs_entry_iter 166 * 167 * @record: pstore record to pass to callback 168 * 169 * You MUST call efivar_entry_iter_begin() before this function, and 170 * efivar_entry_iter_end() afterwards. 171 * 172 */ 173static int efi_pstore_sysfs_entry_iter(struct pstore_record *record) 174{ 175 struct efivar_entry **pos = (struct efivar_entry **)&record->psi->data; 176 struct efivar_entry *entry, *n; 177 struct list_head *head = &efi_pstore_list; 178 int size = 0; 179 int ret; 180 181 if (!*pos) { 182 list_for_each_entry_safe(entry, n, head, list) { 183 efi_pstore_scan_sysfs_enter(entry, n, head); 184 185 size = efi_pstore_read_func(entry, record); 186 ret = efi_pstore_scan_sysfs_exit(entry, n, head, 187 size < 0); 188 if (ret) 189 return ret; 190 if (size) 191 break; 192 } 193 *pos = n; 194 return size; 195 } 196 197 list_for_each_entry_safe_from((*pos), n, head, list) { 198 efi_pstore_scan_sysfs_enter((*pos), n, head); 199 200 size = efi_pstore_read_func((*pos), record); 201 ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0); 202 if (ret) 203 return ret; 204 if (size) 205 break; 206 } 207 *pos = n; 208 return size; 209} 210 211/** 212 * efi_pstore_read 213 * 214 * This function returns a size of NVRAM entry logged via efi_pstore_write(). 215 * The meaning and behavior of efi_pstore/pstore are as below. 216 * 217 * size > 0: Got data of an entry logged via efi_pstore_write() successfully, 218 * and pstore filesystem will continue reading subsequent entries. 219 * size == 0: Entry was not logged via efi_pstore_write(), 220 * and efi_pstore driver will continue reading subsequent entries. 221 * size < 0: Failed to get data of entry logging via efi_pstore_write(), 222 * and pstore will stop reading entry. 223 */ 224static ssize_t efi_pstore_read(struct pstore_record *record) 225{ 226 ssize_t size; 227 228 record->buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL); 229 if (!record->buf) 230 return -ENOMEM; 231 232 if (efivar_entry_iter_begin()) { 233 size = -EINTR; 234 goto out; 235 } 236 size = efi_pstore_sysfs_entry_iter(record); 237 efivar_entry_iter_end(); 238 239out: 240 if (size <= 0) { 241 kfree(record->buf); 242 record->buf = NULL; 243 } 244 return size; 245} 246 247static int efi_pstore_write(struct pstore_record *record) 248{ 249 char name[DUMP_NAME_LEN]; 250 efi_char16_t efi_name[DUMP_NAME_LEN]; 251 efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 252 int i, ret = 0; 253 254 record->id = generic_id(record->time.tv_sec, record->part, 255 record->count); 256 257 /* Since we copy the entire length of name, make sure it is wiped. */ 258 memset(name, 0, sizeof(name)); 259 260 snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lld-%c", 261 record->type, record->part, record->count, 262 (long long)record->time.tv_sec, 263 record->compressed ? 'C' : 'D'); 264 265 for (i = 0; i < DUMP_NAME_LEN; i++) 266 efi_name[i] = name[i]; 267 268 ret = efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES, 269 false, record->size, record->psi->buf); 270 271 if (record->reason == KMSG_DUMP_OOPS && try_module_get(THIS_MODULE)) 272 if (!schedule_work(&efivar_work)) 273 module_put(THIS_MODULE); 274 275 return ret; 276}; 277 278/* 279 * Clean up an entry with the same name 280 */ 281static int efi_pstore_erase_func(struct efivar_entry *entry, void *data) 282{ 283 efi_char16_t *efi_name = data; 284 efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 285 unsigned long ucs2_len = ucs2_strlen(efi_name); 286 287 if (efi_guidcmp(entry->var.VendorGuid, vendor)) 288 return 0; 289 290 if (ucs2_strncmp(entry->var.VariableName, efi_name, (size_t)ucs2_len)) 291 return 0; 292 293 if (entry->scanning) { 294 /* 295 * Skip deletion because this entry will be deleted 296 * after scanning is completed. 297 */ 298 entry->deleting = true; 299 } else 300 list_del(&entry->list); 301 302 /* found */ 303 __efivar_entry_delete(entry); 304 305 return 1; 306} 307 308static int efi_pstore_erase_name(const char *name) 309{ 310 struct efivar_entry *entry = NULL; 311 efi_char16_t efi_name[DUMP_NAME_LEN]; 312 int found, i; 313 314 for (i = 0; i < DUMP_NAME_LEN; i++) { 315 efi_name[i] = name[i]; 316 if (name[i] == '\0') 317 break; 318 } 319 320 if (efivar_entry_iter_begin()) 321 return -EINTR; 322 323 found = __efivar_entry_iter(efi_pstore_erase_func, &efi_pstore_list, 324 efi_name, &entry); 325 efivar_entry_iter_end(); 326 327 if (found && !entry->scanning) 328 kfree(entry); 329 330 return found ? 0 : -ENOENT; 331} 332 333static int efi_pstore_erase(struct pstore_record *record) 334{ 335 char name[DUMP_NAME_LEN]; 336 int ret; 337 338 snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lld", 339 record->type, record->part, record->count, 340 (long long)record->time.tv_sec); 341 ret = efi_pstore_erase_name(name); 342 if (ret != -ENOENT) 343 return ret; 344 345 snprintf(name, sizeof(name), "dump-type%u-%u-%lld", 346 record->type, record->part, (long long)record->time.tv_sec); 347 ret = efi_pstore_erase_name(name); 348 349 return ret; 350} 351 352static struct pstore_info efi_pstore_info = { 353 .owner = THIS_MODULE, 354 .name = "efi", 355 .flags = PSTORE_FLAGS_DMESG, 356 .open = efi_pstore_open, 357 .close = efi_pstore_close, 358 .read = efi_pstore_read, 359 .write = efi_pstore_write, 360 .erase = efi_pstore_erase, 361}; 362 363static int efi_pstore_callback(efi_char16_t *name, efi_guid_t vendor, 364 unsigned long name_size, void *data) 365{ 366 struct efivar_entry *entry; 367 int ret; 368 369 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 370 if (!entry) 371 return -ENOMEM; 372 373 memcpy(entry->var.VariableName, name, name_size); 374 entry->var.VendorGuid = vendor; 375 376 ret = efivar_entry_add(entry, &efi_pstore_list); 377 if (ret) 378 kfree(entry); 379 380 return ret; 381} 382 383static int efi_pstore_update_entry(efi_char16_t *name, efi_guid_t vendor, 384 unsigned long name_size, void *data) 385{ 386 struct efivar_entry *entry = data; 387 388 if (efivar_entry_find(name, vendor, &efi_pstore_list, false)) 389 return 0; 390 391 memcpy(entry->var.VariableName, name, name_size); 392 memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t)); 393 394 return 1; 395} 396 397static void efi_pstore_update_entries(struct work_struct *work) 398{ 399 struct efivar_entry *entry; 400 int err; 401 402 /* Add new sysfs entries */ 403 while (1) { 404 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 405 if (!entry) 406 return; 407 408 err = efivar_init(efi_pstore_update_entry, entry, 409 false, &efi_pstore_list); 410 if (!err) 411 break; 412 413 efivar_entry_add(entry, &efi_pstore_list); 414 } 415 416 kfree(entry); 417 module_put(THIS_MODULE); 418} 419 420static __init int efivars_pstore_init(void) 421{ 422 int ret; 423 424 if (!efivars_kobject() || !efivar_supports_writes()) 425 return 0; 426 427 if (efivars_pstore_disable) 428 return 0; 429 430 ret = efivar_init(efi_pstore_callback, NULL, true, &efi_pstore_list); 431 if (ret) 432 return ret; 433 434 efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); 435 if (!efi_pstore_info.buf) 436 return -ENOMEM; 437 438 efi_pstore_info.bufsize = 1024; 439 440 if (pstore_register(&efi_pstore_info)) { 441 kfree(efi_pstore_info.buf); 442 efi_pstore_info.buf = NULL; 443 efi_pstore_info.bufsize = 0; 444 } 445 446 INIT_WORK(&efivar_work, efi_pstore_update_entries); 447 448 return 0; 449} 450 451static __exit void efivars_pstore_exit(void) 452{ 453 if (!efi_pstore_info.bufsize) 454 return; 455 456 pstore_unregister(&efi_pstore_info); 457 kfree(efi_pstore_info.buf); 458 efi_pstore_info.buf = NULL; 459 efi_pstore_info.bufsize = 0; 460} 461 462module_init(efivars_pstore_init); 463module_exit(efivars_pstore_exit); 464 465MODULE_DESCRIPTION("EFI variable backend for pstore"); 466MODULE_LICENSE("GPL"); 467MODULE_ALIAS("platform:efivars");