linux/drivers/firmware/efi/efi-pstore.c
<<
>>
Prefs
   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                              preemptible(), 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");
 468