1*04851772SMatt Fleming #include <linux/efi.h> 2*04851772SMatt Fleming #include <linux/module.h> 3*04851772SMatt Fleming #include <linux/pstore.h> 4*04851772SMatt Fleming 5*04851772SMatt Fleming #define DUMP_NAME_LEN 52 6*04851772SMatt Fleming 7*04851772SMatt Fleming static bool efivars_pstore_disable = 8*04851772SMatt Fleming IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE); 9*04851772SMatt Fleming 10*04851772SMatt Fleming module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644); 11*04851772SMatt Fleming 12*04851772SMatt Fleming #define PSTORE_EFI_ATTRIBUTES \ 13*04851772SMatt Fleming (EFI_VARIABLE_NON_VOLATILE | \ 14*04851772SMatt Fleming EFI_VARIABLE_BOOTSERVICE_ACCESS | \ 15*04851772SMatt Fleming EFI_VARIABLE_RUNTIME_ACCESS) 16*04851772SMatt Fleming 17*04851772SMatt Fleming static int efi_pstore_open(struct pstore_info *psi) 18*04851772SMatt Fleming { 19*04851772SMatt Fleming efivar_entry_iter_begin(); 20*04851772SMatt Fleming psi->data = NULL; 21*04851772SMatt Fleming return 0; 22*04851772SMatt Fleming } 23*04851772SMatt Fleming 24*04851772SMatt Fleming static int efi_pstore_close(struct pstore_info *psi) 25*04851772SMatt Fleming { 26*04851772SMatt Fleming efivar_entry_iter_end(); 27*04851772SMatt Fleming psi->data = NULL; 28*04851772SMatt Fleming return 0; 29*04851772SMatt Fleming } 30*04851772SMatt Fleming 31*04851772SMatt Fleming struct pstore_read_data { 32*04851772SMatt Fleming u64 *id; 33*04851772SMatt Fleming enum pstore_type_id *type; 34*04851772SMatt Fleming int *count; 35*04851772SMatt Fleming struct timespec *timespec; 36*04851772SMatt Fleming char **buf; 37*04851772SMatt Fleming }; 38*04851772SMatt Fleming 39*04851772SMatt Fleming static int efi_pstore_read_func(struct efivar_entry *entry, void *data) 40*04851772SMatt Fleming { 41*04851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 42*04851772SMatt Fleming struct pstore_read_data *cb_data = data; 43*04851772SMatt Fleming char name[DUMP_NAME_LEN]; 44*04851772SMatt Fleming int i; 45*04851772SMatt Fleming int cnt; 46*04851772SMatt Fleming unsigned int part; 47*04851772SMatt Fleming unsigned long time, size; 48*04851772SMatt Fleming 49*04851772SMatt Fleming if (efi_guidcmp(entry->var.VendorGuid, vendor)) 50*04851772SMatt Fleming return 0; 51*04851772SMatt Fleming 52*04851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 53*04851772SMatt Fleming name[i] = entry->var.VariableName[i]; 54*04851772SMatt Fleming 55*04851772SMatt Fleming if (sscanf(name, "dump-type%u-%u-%d-%lu", 56*04851772SMatt Fleming cb_data->type, &part, &cnt, &time) == 4) { 57*04851772SMatt Fleming *cb_data->id = part; 58*04851772SMatt Fleming *cb_data->count = cnt; 59*04851772SMatt Fleming cb_data->timespec->tv_sec = time; 60*04851772SMatt Fleming cb_data->timespec->tv_nsec = 0; 61*04851772SMatt Fleming } else if (sscanf(name, "dump-type%u-%u-%lu", 62*04851772SMatt Fleming cb_data->type, &part, &time) == 3) { 63*04851772SMatt Fleming /* 64*04851772SMatt Fleming * Check if an old format, 65*04851772SMatt Fleming * which doesn't support holding 66*04851772SMatt Fleming * multiple logs, remains. 67*04851772SMatt Fleming */ 68*04851772SMatt Fleming *cb_data->id = part; 69*04851772SMatt Fleming *cb_data->count = 0; 70*04851772SMatt Fleming cb_data->timespec->tv_sec = time; 71*04851772SMatt Fleming cb_data->timespec->tv_nsec = 0; 72*04851772SMatt Fleming } else 73*04851772SMatt Fleming return 0; 74*04851772SMatt Fleming 75*04851772SMatt Fleming __efivar_entry_size(entry, &size); 76*04851772SMatt Fleming *cb_data->buf = kmalloc(size, GFP_KERNEL); 77*04851772SMatt Fleming if (*cb_data->buf == NULL) 78*04851772SMatt Fleming return -ENOMEM; 79*04851772SMatt Fleming memcpy(*cb_data->buf, entry->var.Data, size); 80*04851772SMatt Fleming return size; 81*04851772SMatt Fleming } 82*04851772SMatt Fleming 83*04851772SMatt Fleming static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, 84*04851772SMatt Fleming int *count, struct timespec *timespec, 85*04851772SMatt Fleming char **buf, struct pstore_info *psi) 86*04851772SMatt Fleming { 87*04851772SMatt Fleming struct pstore_read_data data; 88*04851772SMatt Fleming 89*04851772SMatt Fleming data.id = id; 90*04851772SMatt Fleming data.type = type; 91*04851772SMatt Fleming data.count = count; 92*04851772SMatt Fleming data.timespec = timespec; 93*04851772SMatt Fleming data.buf = buf; 94*04851772SMatt Fleming 95*04851772SMatt Fleming return __efivar_entry_iter(efi_pstore_read_func, &efivar_sysfs_list, &data, 96*04851772SMatt Fleming (struct efivar_entry **)&psi->data); 97*04851772SMatt Fleming } 98*04851772SMatt Fleming 99*04851772SMatt Fleming static int efi_pstore_write(enum pstore_type_id type, 100*04851772SMatt Fleming enum kmsg_dump_reason reason, u64 *id, 101*04851772SMatt Fleming unsigned int part, int count, size_t size, 102*04851772SMatt Fleming struct pstore_info *psi) 103*04851772SMatt Fleming { 104*04851772SMatt Fleming char name[DUMP_NAME_LEN]; 105*04851772SMatt Fleming efi_char16_t efi_name[DUMP_NAME_LEN]; 106*04851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 107*04851772SMatt Fleming int i, ret = 0; 108*04851772SMatt Fleming 109*04851772SMatt Fleming sprintf(name, "dump-type%u-%u-%d-%lu", type, part, count, 110*04851772SMatt Fleming get_seconds()); 111*04851772SMatt Fleming 112*04851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 113*04851772SMatt Fleming efi_name[i] = name[i]; 114*04851772SMatt Fleming 115*04851772SMatt Fleming efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES, 116*04851772SMatt Fleming !pstore_cannot_block_path(reason), 117*04851772SMatt Fleming size, psi->buf); 118*04851772SMatt Fleming 119*04851772SMatt Fleming if (reason == KMSG_DUMP_OOPS) 120*04851772SMatt Fleming efivar_run_worker(); 121*04851772SMatt Fleming 122*04851772SMatt Fleming *id = part; 123*04851772SMatt Fleming return ret; 124*04851772SMatt Fleming }; 125*04851772SMatt Fleming 126*04851772SMatt Fleming struct pstore_erase_data { 127*04851772SMatt Fleming u64 id; 128*04851772SMatt Fleming enum pstore_type_id type; 129*04851772SMatt Fleming int count; 130*04851772SMatt Fleming struct timespec time; 131*04851772SMatt Fleming efi_char16_t *name; 132*04851772SMatt Fleming }; 133*04851772SMatt Fleming 134*04851772SMatt Fleming /* 135*04851772SMatt Fleming * Clean up an entry with the same name 136*04851772SMatt Fleming */ 137*04851772SMatt Fleming static int efi_pstore_erase_func(struct efivar_entry *entry, void *data) 138*04851772SMatt Fleming { 139*04851772SMatt Fleming struct pstore_erase_data *ed = data; 140*04851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 141*04851772SMatt Fleming efi_char16_t efi_name_old[DUMP_NAME_LEN]; 142*04851772SMatt Fleming efi_char16_t *efi_name = ed->name; 143*04851772SMatt Fleming unsigned long utf16_len = utf16_strlen(ed->name); 144*04851772SMatt Fleming char name_old[DUMP_NAME_LEN]; 145*04851772SMatt Fleming int i; 146*04851772SMatt Fleming 147*04851772SMatt Fleming if (efi_guidcmp(entry->var.VendorGuid, vendor)) 148*04851772SMatt Fleming return 0; 149*04851772SMatt Fleming 150*04851772SMatt Fleming if (utf16_strncmp(entry->var.VariableName, 151*04851772SMatt Fleming efi_name, (size_t)utf16_len)) { 152*04851772SMatt Fleming /* 153*04851772SMatt Fleming * Check if an old format, which doesn't support 154*04851772SMatt Fleming * holding multiple logs, remains. 155*04851772SMatt Fleming */ 156*04851772SMatt Fleming sprintf(name_old, "dump-type%u-%u-%lu", ed->type, 157*04851772SMatt Fleming (unsigned int)ed->id, ed->time.tv_sec); 158*04851772SMatt Fleming 159*04851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 160*04851772SMatt Fleming efi_name_old[i] = name_old[i]; 161*04851772SMatt Fleming 162*04851772SMatt Fleming if (utf16_strncmp(entry->var.VariableName, efi_name_old, 163*04851772SMatt Fleming utf16_strlen(efi_name_old))) 164*04851772SMatt Fleming return 0; 165*04851772SMatt Fleming } 166*04851772SMatt Fleming 167*04851772SMatt Fleming /* found */ 168*04851772SMatt Fleming __efivar_entry_delete(entry); 169*04851772SMatt Fleming return 1; 170*04851772SMatt Fleming } 171*04851772SMatt Fleming 172*04851772SMatt Fleming static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, 173*04851772SMatt Fleming struct timespec time, struct pstore_info *psi) 174*04851772SMatt Fleming { 175*04851772SMatt Fleming struct pstore_erase_data edata; 176*04851772SMatt Fleming struct efivar_entry *entry; 177*04851772SMatt Fleming char name[DUMP_NAME_LEN]; 178*04851772SMatt Fleming efi_char16_t efi_name[DUMP_NAME_LEN]; 179*04851772SMatt Fleming int found, i; 180*04851772SMatt Fleming 181*04851772SMatt Fleming sprintf(name, "dump-type%u-%u-%d-%lu", type, (unsigned int)id, count, 182*04851772SMatt Fleming time.tv_sec); 183*04851772SMatt Fleming 184*04851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 185*04851772SMatt Fleming efi_name[i] = name[i]; 186*04851772SMatt Fleming 187*04851772SMatt Fleming edata.id = id; 188*04851772SMatt Fleming edata.type = type; 189*04851772SMatt Fleming edata.count = count; 190*04851772SMatt Fleming edata.time = time; 191*04851772SMatt Fleming edata.name = efi_name; 192*04851772SMatt Fleming 193*04851772SMatt Fleming efivar_entry_iter_begin(); 194*04851772SMatt Fleming found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry); 195*04851772SMatt Fleming efivar_entry_iter_end(); 196*04851772SMatt Fleming 197*04851772SMatt Fleming if (found) 198*04851772SMatt Fleming efivar_unregister(entry); 199*04851772SMatt Fleming 200*04851772SMatt Fleming return 0; 201*04851772SMatt Fleming } 202*04851772SMatt Fleming 203*04851772SMatt Fleming static struct pstore_info efi_pstore_info = { 204*04851772SMatt Fleming .owner = THIS_MODULE, 205*04851772SMatt Fleming .name = "efi", 206*04851772SMatt Fleming .open = efi_pstore_open, 207*04851772SMatt Fleming .close = efi_pstore_close, 208*04851772SMatt Fleming .read = efi_pstore_read, 209*04851772SMatt Fleming .write = efi_pstore_write, 210*04851772SMatt Fleming .erase = efi_pstore_erase, 211*04851772SMatt Fleming }; 212*04851772SMatt Fleming 213*04851772SMatt Fleming static __init int efivars_pstore_init(void) 214*04851772SMatt Fleming { 215*04851772SMatt Fleming if (!efi_enabled(EFI_RUNTIME_SERVICES)) 216*04851772SMatt Fleming return 0; 217*04851772SMatt Fleming 218*04851772SMatt Fleming if (!efivars_kobject()) 219*04851772SMatt Fleming return 0; 220*04851772SMatt Fleming 221*04851772SMatt Fleming if (efivars_pstore_disable) 222*04851772SMatt Fleming return 0; 223*04851772SMatt Fleming 224*04851772SMatt Fleming efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); 225*04851772SMatt Fleming if (!efi_pstore_info.buf) 226*04851772SMatt Fleming return -ENOMEM; 227*04851772SMatt Fleming 228*04851772SMatt Fleming efi_pstore_info.bufsize = 1024; 229*04851772SMatt Fleming spin_lock_init(&efi_pstore_info.buf_lock); 230*04851772SMatt Fleming 231*04851772SMatt Fleming pstore_register(&efi_pstore_info); 232*04851772SMatt Fleming 233*04851772SMatt Fleming return 0; 234*04851772SMatt Fleming } 235*04851772SMatt Fleming 236*04851772SMatt Fleming static __exit void efivars_pstore_exit(void) 237*04851772SMatt Fleming { 238*04851772SMatt Fleming } 239*04851772SMatt Fleming 240*04851772SMatt Fleming module_init(efivars_pstore_init); 241*04851772SMatt Fleming module_exit(efivars_pstore_exit); 242*04851772SMatt Fleming 243*04851772SMatt Fleming MODULE_DESCRIPTION("EFI variable backend for pstore"); 244*04851772SMatt Fleming MODULE_LICENSE("GPL"); 245