diff options
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/Kconfig | 36 | ||||
-rw-r--r-- | drivers/firmware/Makefile | 1 | ||||
-rw-r--r-- | drivers/firmware/efi/Kconfig | 39 | ||||
-rw-r--r-- | drivers/firmware/efi/Makefile | 4 | ||||
-rw-r--r-- | drivers/firmware/efi/efi-pstore.c | 244 | ||||
-rw-r--r-- | drivers/firmware/efivars.c | 277 |
6 files changed, 301 insertions, 300 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 42c759a4d047..93876302fb2e 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig | |||
@@ -36,41 +36,6 @@ config FIRMWARE_MEMMAP | |||
36 | 36 | ||
37 | See also Documentation/ABI/testing/sysfs-firmware-memmap. | 37 | See also Documentation/ABI/testing/sysfs-firmware-memmap. |
38 | 38 | ||
39 | config EFI_VARS | ||
40 | tristate "EFI Variable Support via sysfs" | ||
41 | depends on EFI | ||
42 | default n | ||
43 | help | ||
44 | If you say Y here, you are able to get EFI (Extensible Firmware | ||
45 | Interface) variable information via sysfs. You may read, | ||
46 | write, create, and destroy EFI variables through this interface. | ||
47 | |||
48 | Note that using this driver in concert with efibootmgr requires | ||
49 | at least test release version 0.5.0-test3 or later, which is | ||
50 | available from Matt Domsch's website located at: | ||
51 | <http://linux.dell.com/efibootmgr/testing/efibootmgr-0.5.0-test3.tar.gz> | ||
52 | |||
53 | Subsequent efibootmgr releases may be found at: | ||
54 | <http://linux.dell.com/efibootmgr> | ||
55 | |||
56 | config EFI_VARS_PSTORE | ||
57 | bool "Register efivars backend for pstore" | ||
58 | depends on EFI_VARS && PSTORE | ||
59 | default y | ||
60 | help | ||
61 | Say Y here to enable use efivars as a backend to pstore. This | ||
62 | will allow writing console messages, crash dumps, or anything | ||
63 | else supported by pstore to EFI variables. | ||
64 | |||
65 | config EFI_VARS_PSTORE_DEFAULT_DISABLE | ||
66 | bool "Disable using efivars as a pstore backend by default" | ||
67 | depends on EFI_VARS_PSTORE | ||
68 | default n | ||
69 | help | ||
70 | Saying Y here will disable the use of efivars as a storage | ||
71 | backend for pstore by default. This setting can be overridden | ||
72 | using the efivars module's pstore_disable parameter. | ||
73 | |||
74 | config EFI_PCDP | 39 | config EFI_PCDP |
75 | bool "Console device selection via EFI PCDP or HCDP table" | 40 | bool "Console device selection via EFI PCDP or HCDP table" |
76 | depends on ACPI && EFI && IA64 | 41 | depends on ACPI && EFI && IA64 |
@@ -164,5 +129,6 @@ config ISCSI_IBFT | |||
164 | Otherwise, say N. | 129 | Otherwise, say N. |
165 | 130 | ||
166 | source "drivers/firmware/google/Kconfig" | 131 | source "drivers/firmware/google/Kconfig" |
132 | source "drivers/firmware/efi/Kconfig" | ||
167 | 133 | ||
168 | endmenu | 134 | endmenu |
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 5a7e27399729..31bf68c93593 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile | |||
@@ -14,3 +14,4 @@ obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o | |||
14 | obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o | 14 | obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o |
15 | 15 | ||
16 | obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ | 16 | obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ |
17 | obj-$(CONFIG_EFI) += efi/ | ||
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig new file mode 100644 index 000000000000..b0fc7c79dfbb --- /dev/null +++ b/drivers/firmware/efi/Kconfig | |||
@@ -0,0 +1,39 @@ | |||
1 | menu "EFI (Extensible Firmware Interface) Support" | ||
2 | depends on EFI | ||
3 | |||
4 | config EFI_VARS | ||
5 | tristate "EFI Variable Support via sysfs" | ||
6 | depends on EFI | ||
7 | default n | ||
8 | help | ||
9 | If you say Y here, you are able to get EFI (Extensible Firmware | ||
10 | Interface) variable information via sysfs. You may read, | ||
11 | write, create, and destroy EFI variables through this interface. | ||
12 | |||
13 | Note that using this driver in concert with efibootmgr requires | ||
14 | at least test release version 0.5.0-test3 or later, which is | ||
15 | available from Matt Domsch's website located at: | ||
16 | <http://linux.dell.com/efibootmgr/testing/efibootmgr-0.5.0-test3.tar.gz> | ||
17 | |||
18 | Subsequent efibootmgr releases may be found at: | ||
19 | <http://linux.dell.com/efibootmgr> | ||
20 | |||
21 | config EFI_VARS_PSTORE | ||
22 | tristate "Register efivars backend for pstore" | ||
23 | depends on EFI_VARS && PSTORE | ||
24 | default y | ||
25 | help | ||
26 | Say Y here to enable use efivars as a backend to pstore. This | ||
27 | will allow writing console messages, crash dumps, or anything | ||
28 | else supported by pstore to EFI variables. | ||
29 | |||
30 | config EFI_VARS_PSTORE_DEFAULT_DISABLE | ||
31 | bool "Disable using efivars as a pstore backend by default" | ||
32 | depends on EFI_VARS_PSTORE | ||
33 | default n | ||
34 | help | ||
35 | Saying Y here will disable the use of efivars as a storage | ||
36 | backend for pstore by default. This setting can be overridden | ||
37 | using the efivars module's pstore_disable parameter. | ||
38 | |||
39 | endmenu | ||
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile new file mode 100644 index 000000000000..e03cd51525c2 --- /dev/null +++ b/drivers/firmware/efi/Makefile | |||
@@ -0,0 +1,4 @@ | |||
1 | # | ||
2 | # Makefile for linux kernel | ||
3 | # | ||
4 | obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o | ||
diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c new file mode 100644 index 000000000000..47ae712c9504 --- /dev/null +++ b/drivers/firmware/efi/efi-pstore.c | |||
@@ -0,0 +1,244 @@ | |||
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"); | ||
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 82fd145ead3b..5e7c3b1acde9 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
@@ -78,7 +78,6 @@ | |||
78 | #include <linux/kobject.h> | 78 | #include <linux/kobject.h> |
79 | #include <linux/device.h> | 79 | #include <linux/device.h> |
80 | #include <linux/slab.h> | 80 | #include <linux/slab.h> |
81 | #include <linux/pstore.h> | ||
82 | #include <linux/ctype.h> | 81 | #include <linux/ctype.h> |
83 | 82 | ||
84 | #include <linux/fs.h> | 83 | #include <linux/fs.h> |
@@ -95,15 +94,9 @@ MODULE_DESCRIPTION("sysfs interface to EFI Variables"); | |||
95 | MODULE_LICENSE("GPL"); | 94 | MODULE_LICENSE("GPL"); |
96 | MODULE_VERSION(EFIVARS_VERSION); | 95 | MODULE_VERSION(EFIVARS_VERSION); |
97 | 96 | ||
98 | #define DUMP_NAME_LEN 52 | ||
99 | |||
100 | static LIST_HEAD(efivarfs_list); | 97 | static LIST_HEAD(efivarfs_list); |
101 | static LIST_HEAD(efivar_sysfs_list); | 98 | LIST_HEAD(efivar_sysfs_list); |
102 | 99 | EXPORT_SYMBOL_GPL(efivar_sysfs_list); | |
103 | static bool efivars_pstore_disable = | ||
104 | IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE); | ||
105 | |||
106 | module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644); | ||
107 | 100 | ||
108 | struct efivar_attribute { | 101 | struct efivar_attribute { |
109 | struct attribute attr; | 102 | struct attribute attr; |
@@ -114,11 +107,6 @@ struct efivar_attribute { | |||
114 | /* Private pointer to registered efivars */ | 107 | /* Private pointer to registered efivars */ |
115 | static struct efivars *__efivars; | 108 | static struct efivars *__efivars; |
116 | 109 | ||
117 | #define PSTORE_EFI_ATTRIBUTES \ | ||
118 | (EFI_VARIABLE_NON_VOLATILE | \ | ||
119 | EFI_VARIABLE_BOOTSERVICE_ACCESS | \ | ||
120 | EFI_VARIABLE_RUNTIME_ACCESS) | ||
121 | |||
122 | static struct kset *efivars_kset; | 110 | static struct kset *efivars_kset; |
123 | 111 | ||
124 | static struct bin_attribute *efivars_new_var; | 112 | static struct bin_attribute *efivars_new_var; |
@@ -148,34 +136,6 @@ static void efivar_update_sysfs_entries(struct work_struct *); | |||
148 | static DECLARE_WORK(efivar_work, efivar_update_sysfs_entries); | 136 | static DECLARE_WORK(efivar_work, efivar_update_sysfs_entries); |
149 | static bool efivar_wq_enabled = true; | 137 | static bool efivar_wq_enabled = true; |
150 | 138 | ||
151 | /* | ||
152 | * Return the number of bytes is the length of this string | ||
153 | * Note: this is NOT the same as the number of unicode characters | ||
154 | */ | ||
155 | static inline unsigned long | ||
156 | utf16_strsize(efi_char16_t *data, unsigned long maxlength) | ||
157 | { | ||
158 | return utf16_strnlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t); | ||
159 | } | ||
160 | |||
161 | static inline int | ||
162 | utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len) | ||
163 | { | ||
164 | while (1) { | ||
165 | if (len == 0) | ||
166 | return 0; | ||
167 | if (*a < *b) | ||
168 | return -1; | ||
169 | if (*a > *b) | ||
170 | return 1; | ||
171 | if (*a == 0) /* implies *b == 0 */ | ||
172 | return 0; | ||
173 | a++; | ||
174 | b++; | ||
175 | len--; | ||
176 | } | ||
177 | } | ||
178 | |||
179 | static bool | 139 | static bool |
180 | validate_device_path(struct efi_variable *var, int match, u8 *buffer, | 140 | validate_device_path(struct efi_variable *var, int match, u8 *buffer, |
181 | unsigned long len) | 141 | unsigned long len) |
@@ -598,12 +558,6 @@ static struct kobj_type efivar_ktype = { | |||
598 | .default_attrs = def_attrs, | 558 | .default_attrs = def_attrs, |
599 | }; | 559 | }; |
600 | 560 | ||
601 | static inline void | ||
602 | efivar_unregister(struct efivar_entry *var) | ||
603 | { | ||
604 | kobject_put(&var->kobj); | ||
605 | } | ||
606 | |||
607 | static int efivarfs_file_open(struct inode *inode, struct file *file) | 561 | static int efivarfs_file_open(struct inode *inode, struct file *file) |
608 | { | 562 | { |
609 | file->private_data = inode->i_private; | 563 | file->private_data = inode->i_private; |
@@ -1130,220 +1084,6 @@ static const struct inode_operations efivarfs_dir_inode_operations = { | |||
1130 | .create = efivarfs_create, | 1084 | .create = efivarfs_create, |
1131 | }; | 1085 | }; |
1132 | 1086 | ||
1133 | #ifdef CONFIG_EFI_VARS_PSTORE | ||
1134 | |||
1135 | static int efi_pstore_open(struct pstore_info *psi) | ||
1136 | { | ||
1137 | efivar_entry_iter_begin(); | ||
1138 | psi->data = NULL; | ||
1139 | return 0; | ||
1140 | } | ||
1141 | |||
1142 | static int efi_pstore_close(struct pstore_info *psi) | ||
1143 | { | ||
1144 | efivar_entry_iter_end(); | ||
1145 | psi->data = NULL; | ||
1146 | return 0; | ||
1147 | } | ||
1148 | |||
1149 | struct pstore_read_data { | ||
1150 | u64 *id; | ||
1151 | enum pstore_type_id *type; | ||
1152 | int *count; | ||
1153 | struct timespec *timespec; | ||
1154 | char **buf; | ||
1155 | }; | ||
1156 | |||
1157 | static int efi_pstore_read_func(struct efivar_entry *entry, void *data) | ||
1158 | { | ||
1159 | efi_guid_t vendor = LINUX_EFI_CRASH_GUID; | ||
1160 | struct pstore_read_data *cb_data = data; | ||
1161 | char name[DUMP_NAME_LEN]; | ||
1162 | int i; | ||
1163 | int cnt; | ||
1164 | unsigned int part; | ||
1165 | unsigned long time, size; | ||
1166 | |||
1167 | if (efi_guidcmp(entry->var.VendorGuid, vendor)) | ||
1168 | return 0; | ||
1169 | |||
1170 | for (i = 0; i < DUMP_NAME_LEN; i++) | ||
1171 | name[i] = entry->var.VariableName[i]; | ||
1172 | |||
1173 | if (sscanf(name, "dump-type%u-%u-%d-%lu", | ||
1174 | cb_data->type, &part, &cnt, &time) == 4) { | ||
1175 | *cb_data->id = part; | ||
1176 | *cb_data->count = cnt; | ||
1177 | cb_data->timespec->tv_sec = time; | ||
1178 | cb_data->timespec->tv_nsec = 0; | ||
1179 | } else if (sscanf(name, "dump-type%u-%u-%lu", | ||
1180 | cb_data->type, &part, &time) == 3) { | ||
1181 | /* | ||
1182 | * Check if an old format, | ||
1183 | * which doesn't support holding | ||
1184 | * multiple logs, remains. | ||
1185 | */ | ||
1186 | *cb_data->id = part; | ||
1187 | *cb_data->count = 0; | ||
1188 | cb_data->timespec->tv_sec = time; | ||
1189 | cb_data->timespec->tv_nsec = 0; | ||
1190 | } else | ||
1191 | return 0; | ||
1192 | |||
1193 | __efivar_entry_size(entry, &size); | ||
1194 | *cb_data->buf = kmalloc(size, GFP_KERNEL); | ||
1195 | if (*cb_data->buf == NULL) | ||
1196 | return -ENOMEM; | ||
1197 | memcpy(*cb_data->buf, entry->var.Data, size); | ||
1198 | return size; | ||
1199 | } | ||
1200 | |||
1201 | static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, | ||
1202 | int *count, struct timespec *timespec, | ||
1203 | char **buf, struct pstore_info *psi) | ||
1204 | { | ||
1205 | struct pstore_read_data data; | ||
1206 | |||
1207 | data.id = id; | ||
1208 | data.type = type; | ||
1209 | data.count = count; | ||
1210 | data.timespec = timespec; | ||
1211 | data.buf = buf; | ||
1212 | |||
1213 | return __efivar_entry_iter(efi_pstore_read_func, &efivar_sysfs_list, &data, | ||
1214 | (struct efivar_entry **)&psi->data); | ||
1215 | } | ||
1216 | |||
1217 | static int efi_pstore_write(enum pstore_type_id type, | ||
1218 | enum kmsg_dump_reason reason, u64 *id, | ||
1219 | unsigned int part, int count, size_t size, | ||
1220 | struct pstore_info *psi) | ||
1221 | { | ||
1222 | char name[DUMP_NAME_LEN]; | ||
1223 | efi_char16_t efi_name[DUMP_NAME_LEN]; | ||
1224 | efi_guid_t vendor = LINUX_EFI_CRASH_GUID; | ||
1225 | int i, ret = 0; | ||
1226 | |||
1227 | sprintf(name, "dump-type%u-%u-%d-%lu", type, part, count, | ||
1228 | get_seconds()); | ||
1229 | |||
1230 | for (i = 0; i < DUMP_NAME_LEN; i++) | ||
1231 | efi_name[i] = name[i]; | ||
1232 | |||
1233 | ret = efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES, | ||
1234 | !pstore_cannot_block_path(reason), | ||
1235 | size, psi->buf); | ||
1236 | |||
1237 | if (reason == KMSG_DUMP_OOPS && efivar_wq_enabled) | ||
1238 | schedule_work(&efivar_work); | ||
1239 | |||
1240 | *id = part; | ||
1241 | return ret; | ||
1242 | }; | ||
1243 | |||
1244 | struct pstore_erase_data { | ||
1245 | u64 id; | ||
1246 | enum pstore_type_id type; | ||
1247 | int count; | ||
1248 | struct timespec time; | ||
1249 | efi_char16_t *name; | ||
1250 | }; | ||
1251 | |||
1252 | /* | ||
1253 | * Clean up an entry with the same name | ||
1254 | */ | ||
1255 | static int efi_pstore_erase_func(struct efivar_entry *entry, void *data) | ||
1256 | { | ||
1257 | struct pstore_erase_data *ed = data; | ||
1258 | efi_guid_t vendor = LINUX_EFI_CRASH_GUID; | ||
1259 | efi_char16_t efi_name_old[DUMP_NAME_LEN]; | ||
1260 | efi_char16_t *efi_name = ed->name; | ||
1261 | unsigned long utf16_len = utf16_strlen(ed->name); | ||
1262 | char name_old[DUMP_NAME_LEN]; | ||
1263 | int i; | ||
1264 | |||
1265 | if (efi_guidcmp(entry->var.VendorGuid, vendor)) | ||
1266 | return 0; | ||
1267 | |||
1268 | if (utf16_strncmp(entry->var.VariableName, | ||
1269 | efi_name, (size_t)utf16_len)) { | ||
1270 | /* | ||
1271 | * Check if an old format, which doesn't support | ||
1272 | * holding multiple logs, remains. | ||
1273 | */ | ||
1274 | sprintf(name_old, "dump-type%u-%u-%lu", ed->type, | ||
1275 | (unsigned int)ed->id, ed->time.tv_sec); | ||
1276 | |||
1277 | for (i = 0; i < DUMP_NAME_LEN; i++) | ||
1278 | efi_name_old[i] = name_old[i]; | ||
1279 | |||
1280 | if (utf16_strncmp(entry->var.VariableName, efi_name_old, | ||
1281 | utf16_strlen(efi_name_old))) | ||
1282 | return 0; | ||
1283 | } | ||
1284 | |||
1285 | /* found */ | ||
1286 | __efivar_entry_delete(entry); | ||
1287 | return 1; | ||
1288 | } | ||
1289 | |||
1290 | static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, | ||
1291 | struct timespec time, struct pstore_info *psi) | ||
1292 | { | ||
1293 | struct pstore_erase_data edata; | ||
1294 | struct efivar_entry *entry; | ||
1295 | char name[DUMP_NAME_LEN]; | ||
1296 | efi_char16_t efi_name[DUMP_NAME_LEN]; | ||
1297 | int found, i; | ||
1298 | |||
1299 | sprintf(name, "dump-type%u-%u-%d-%lu", type, (unsigned int)id, count, | ||
1300 | time.tv_sec); | ||
1301 | |||
1302 | for (i = 0; i < DUMP_NAME_LEN; i++) | ||
1303 | efi_name[i] = name[i]; | ||
1304 | |||
1305 | edata.id = id; | ||
1306 | edata.type = type; | ||
1307 | edata.count = count; | ||
1308 | edata.time = time; | ||
1309 | edata.name = efi_name; | ||
1310 | |||
1311 | efivar_entry_iter_begin(); | ||
1312 | found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry); | ||
1313 | efivar_entry_iter_end(); | ||
1314 | |||
1315 | if (found) | ||
1316 | efivar_unregister(entry); | ||
1317 | |||
1318 | return 0; | ||
1319 | } | ||
1320 | |||
1321 | static struct pstore_info efi_pstore_info = { | ||
1322 | .owner = THIS_MODULE, | ||
1323 | .name = "efi", | ||
1324 | .open = efi_pstore_open, | ||
1325 | .close = efi_pstore_close, | ||
1326 | .read = efi_pstore_read, | ||
1327 | .write = efi_pstore_write, | ||
1328 | .erase = efi_pstore_erase, | ||
1329 | }; | ||
1330 | |||
1331 | static void efivar_pstore_register(void) | ||
1332 | { | ||
1333 | efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); | ||
1334 | if (efi_pstore_info.buf) { | ||
1335 | efi_pstore_info.bufsize = 1024; | ||
1336 | spin_lock_init(&efi_pstore_info.buf_lock); | ||
1337 | pstore_register(&efi_pstore_info); | ||
1338 | } | ||
1339 | } | ||
1340 | #else | ||
1341 | static void efivar_pstore_register(void) | ||
1342 | { | ||
1343 | return; | ||
1344 | } | ||
1345 | #endif | ||
1346 | |||
1347 | static ssize_t efivar_create(struct file *filp, struct kobject *kobj, | 1087 | static ssize_t efivar_create(struct file *filp, struct kobject *kobj, |
1348 | struct bin_attribute *bin_attr, | 1088 | struct bin_attribute *bin_attr, |
1349 | char *buf, loff_t pos, size_t count) | 1089 | char *buf, loff_t pos, size_t count) |
@@ -2397,6 +2137,16 @@ struct kobject *efivars_kobject(void) | |||
2397 | EXPORT_SYMBOL_GPL(efivars_kobject); | 2137 | EXPORT_SYMBOL_GPL(efivars_kobject); |
2398 | 2138 | ||
2399 | /** | 2139 | /** |
2140 | * efivar_run_worker - schedule the efivar worker thread | ||
2141 | */ | ||
2142 | void efivar_run_worker(void) | ||
2143 | { | ||
2144 | if (efivar_wq_enabled) | ||
2145 | schedule_work(&efivar_work); | ||
2146 | } | ||
2147 | EXPORT_SYMBOL_GPL(efivar_run_worker); | ||
2148 | |||
2149 | /** | ||
2400 | * efivars_register - register an efivars | 2150 | * efivars_register - register an efivars |
2401 | * @efivars: efivars to register | 2151 | * @efivars: efivars to register |
2402 | * @ops: efivars operations | 2152 | * @ops: efivars operations |
@@ -2414,9 +2164,6 @@ int efivars_register(struct efivars *efivars, | |||
2414 | 2164 | ||
2415 | __efivars = efivars; | 2165 | __efivars = efivars; |
2416 | 2166 | ||
2417 | if (!efivars_pstore_disable) | ||
2418 | efivar_pstore_register(); | ||
2419 | |||
2420 | register_filesystem(&efivarfs_type); | 2167 | register_filesystem(&efivarfs_type); |
2421 | 2168 | ||
2422 | return 0; | 2169 | return 0; |