OSDN Git Service

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