1*f2e05ce9SSean Rhodes // SPDX-License-Identifier: GPL-2.0-only
2*f2e05ce9SSean Rhodes /*
3*f2e05ce9SSean Rhodes * coreboot CFR firmware attributes driver.
4*f2e05ce9SSean Rhodes *
5*f2e05ce9SSean Rhodes * Parses LB_TAG_CFR_ROOT records from the coreboot table and exposes
6*f2e05ce9SSean Rhodes * runtime EFI variable-backed options through the firmware-attributes class.
7*f2e05ce9SSean Rhodes */
8*f2e05ce9SSean Rhodes
9*f2e05ce9SSean Rhodes #include <linux/array_size.h>
10*f2e05ce9SSean Rhodes #include <linux/bitops.h>
11*f2e05ce9SSean Rhodes #include <linux/cleanup.h>
12*f2e05ce9SSean Rhodes #include <linux/compiler_attributes.h>
13*f2e05ce9SSean Rhodes #include <linux/container_of.h>
14*f2e05ce9SSean Rhodes #include <linux/crc32.h>
15*f2e05ce9SSean Rhodes #include <linux/ctype.h>
16*f2e05ce9SSean Rhodes #include <linux/device.h>
17*f2e05ce9SSean Rhodes #include <linux/device-id/coreboot.h>
18*f2e05ce9SSean Rhodes #include <linux/efi.h>
19*f2e05ce9SSean Rhodes #include <linux/err.h>
20*f2e05ce9SSean Rhodes #include <linux/firmware_attributes.h>
21*f2e05ce9SSean Rhodes #include <linux/io.h>
22*f2e05ce9SSean Rhodes #include <linux/kdev_t.h>
23*f2e05ce9SSean Rhodes #include <linux/kobject.h>
24*f2e05ce9SSean Rhodes #include <linux/kstrtox.h>
25*f2e05ce9SSean Rhodes #include <linux/limits.h>
26*f2e05ce9SSean Rhodes #include <linux/list.h>
27*f2e05ce9SSean Rhodes #include <linux/module.h>
28*f2e05ce9SSean Rhodes #include <linux/mutex.h>
29*f2e05ce9SSean Rhodes #include <linux/slab.h>
30*f2e05ce9SSean Rhodes #include <linux/string.h>
31*f2e05ce9SSean Rhodes #include <linux/sysfs.h>
32*f2e05ce9SSean Rhodes #include <linux/types.h>
33*f2e05ce9SSean Rhodes
34*f2e05ce9SSean Rhodes #include "coreboot_table.h"
35*f2e05ce9SSean Rhodes
36*f2e05ce9SSean Rhodes #define DRIVER_NAME "coreboot-cfr"
37*f2e05ce9SSean Rhodes
38*f2e05ce9SSean Rhodes #define LB_TAG_CFR_ROOT 0x47
39*f2e05ce9SSean Rhodes #define CFR_VERSION 0
40*f2e05ce9SSean Rhodes
41*f2e05ce9SSean Rhodes enum cfr_tags {
42*f2e05ce9SSean Rhodes CFR_TAG_OPTION_FORM = 1,
43*f2e05ce9SSean Rhodes CFR_TAG_ENUM_VALUE = 2,
44*f2e05ce9SSean Rhodes CFR_TAG_OPTION_ENUM = 3,
45*f2e05ce9SSean Rhodes CFR_TAG_OPTION_NUMBER = 4,
46*f2e05ce9SSean Rhodes CFR_TAG_OPTION_BOOL = 5,
47*f2e05ce9SSean Rhodes CFR_TAG_VARCHAR_OPT_NAME = 7,
48*f2e05ce9SSean Rhodes CFR_TAG_VARCHAR_UI_NAME = 8,
49*f2e05ce9SSean Rhodes CFR_TAG_RUNTIME_APPLY = 13,
50*f2e05ce9SSean Rhodes };
51*f2e05ce9SSean Rhodes
52*f2e05ce9SSean Rhodes enum cfr_option_flags {
53*f2e05ce9SSean Rhodes CFR_OPTFLAG_READONLY = BIT(0),
54*f2e05ce9SSean Rhodes CFR_OPTFLAG_INACTIVE = BIT(1),
55*f2e05ce9SSean Rhodes CFR_OPTFLAG_SUPPRESS = BIT(2),
56*f2e05ce9SSean Rhodes CFR_OPTFLAG_VOLATILE = BIT(3),
57*f2e05ce9SSean Rhodes CFR_OPTFLAG_RUNTIME = BIT(4),
58*f2e05ce9SSean Rhodes };
59*f2e05ce9SSean Rhodes
60*f2e05ce9SSean Rhodes enum cfr_runtime_apply_method {
61*f2e05ce9SSean Rhodes CFR_RUNTIME_APPLY_NONE = 0,
62*f2e05ce9SSean Rhodes CFR_RUNTIME_APPLY_APM_CNT = 1,
63*f2e05ce9SSean Rhodes };
64*f2e05ce9SSean Rhodes
65*f2e05ce9SSean Rhodes struct lb_cfr {
66*f2e05ce9SSean Rhodes u32 tag;
67*f2e05ce9SSean Rhodes u32 size;
68*f2e05ce9SSean Rhodes u32 version;
69*f2e05ce9SSean Rhodes u32 checksum;
70*f2e05ce9SSean Rhodes } __packed;
71*f2e05ce9SSean Rhodes
72*f2e05ce9SSean Rhodes struct lb_cfr_varbinary {
73*f2e05ce9SSean Rhodes u32 tag;
74*f2e05ce9SSean Rhodes u32 size;
75*f2e05ce9SSean Rhodes u32 data_length;
76*f2e05ce9SSean Rhodes } __packed;
77*f2e05ce9SSean Rhodes
78*f2e05ce9SSean Rhodes struct lb_cfr_enum_value {
79*f2e05ce9SSean Rhodes u32 tag;
80*f2e05ce9SSean Rhodes u32 size;
81*f2e05ce9SSean Rhodes u32 value;
82*f2e05ce9SSean Rhodes } __packed;
83*f2e05ce9SSean Rhodes
84*f2e05ce9SSean Rhodes struct lb_cfr_runtime_apply {
85*f2e05ce9SSean Rhodes u32 tag;
86*f2e05ce9SSean Rhodes u32 size;
87*f2e05ce9SSean Rhodes u32 method;
88*f2e05ce9SSean Rhodes u32 id;
89*f2e05ce9SSean Rhodes } __packed;
90*f2e05ce9SSean Rhodes
91*f2e05ce9SSean Rhodes struct lb_cfr_numeric_option {
92*f2e05ce9SSean Rhodes u32 tag;
93*f2e05ce9SSean Rhodes u32 size;
94*f2e05ce9SSean Rhodes cb_u64 object_id;
95*f2e05ce9SSean Rhodes cb_u64 dependency_id;
96*f2e05ce9SSean Rhodes u32 flags;
97*f2e05ce9SSean Rhodes u32 default_value;
98*f2e05ce9SSean Rhodes u32 min;
99*f2e05ce9SSean Rhodes u32 max;
100*f2e05ce9SSean Rhodes u32 step;
101*f2e05ce9SSean Rhodes u32 display_flags;
102*f2e05ce9SSean Rhodes } __packed;
103*f2e05ce9SSean Rhodes
104*f2e05ce9SSean Rhodes struct lb_cfr_option_form {
105*f2e05ce9SSean Rhodes u32 tag;
106*f2e05ce9SSean Rhodes u32 size;
107*f2e05ce9SSean Rhodes cb_u64 object_id;
108*f2e05ce9SSean Rhodes cb_u64 dependency_id;
109*f2e05ce9SSean Rhodes u32 flags;
110*f2e05ce9SSean Rhodes } __packed;
111*f2e05ce9SSean Rhodes
112*f2e05ce9SSean Rhodes #define COREBOOT_CFR_OPT_SKIP_FLAGS \
113*f2e05ce9SSean Rhodes (CFR_OPTFLAG_SUPPRESS | CFR_OPTFLAG_VOLATILE)
114*f2e05ce9SSean Rhodes
115*f2e05ce9SSean Rhodes #define COREBOOT_CFR_OPT_READ_ONLY_FLAGS \
116*f2e05ce9SSean Rhodes (CFR_OPTFLAG_READONLY | CFR_OPTFLAG_INACTIVE)
117*f2e05ce9SSean Rhodes
118*f2e05ce9SSean Rhodes #define COREBOOT_CFR_EFI_ATTRS \
119*f2e05ce9SSean Rhodes (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | \
120*f2e05ce9SSean Rhodes EFI_VARIABLE_RUNTIME_ACCESS)
121*f2e05ce9SSean Rhodes
122*f2e05ce9SSean Rhodes #define COREBOOT_CFR_APM_CNT_PORT 0xb2
123*f2e05ce9SSean Rhodes #define COREBOOT_CFR_APM_STS_PORT 0xb3
124*f2e05ce9SSean Rhodes #define COREBOOT_CFR_APM_APPLY_CMD 0xe3
125*f2e05ce9SSean Rhodes #define COREBOOT_CFR_MAX_FORM_DEPTH 16
126*f2e05ce9SSean Rhodes
127*f2e05ce9SSean Rhodes static efi_guid_t coreboot_cfr_guid = EFI_GUID(0xceae4c1d, 0x335b, 0x4685,
128*f2e05ce9SSean Rhodes 0xa4, 0xa0, 0xfc, 0x4a,
129*f2e05ce9SSean Rhodes 0x94, 0xee, 0xa0, 0x85);
130*f2e05ce9SSean Rhodes
131*f2e05ce9SSean Rhodes enum coreboot_cfr_setting_type {
132*f2e05ce9SSean Rhodes COREBOOT_CFR_SETTING_ENUM,
133*f2e05ce9SSean Rhodes COREBOOT_CFR_SETTING_NUMBER,
134*f2e05ce9SSean Rhodes COREBOOT_CFR_SETTING_BOOL,
135*f2e05ce9SSean Rhodes };
136*f2e05ce9SSean Rhodes
137*f2e05ce9SSean Rhodes struct coreboot_cfr_enum {
138*f2e05ce9SSean Rhodes char *label;
139*f2e05ce9SSean Rhodes u32 value;
140*f2e05ce9SSean Rhodes };
141*f2e05ce9SSean Rhodes
142*f2e05ce9SSean Rhodes struct coreboot_cfr_setting {
143*f2e05ce9SSean Rhodes struct kobject kobj;
144*f2e05ce9SSean Rhodes struct list_head node;
145*f2e05ce9SSean Rhodes struct coreboot_cfr_drvdata *drvdata;
146*f2e05ce9SSean Rhodes enum coreboot_cfr_setting_type type;
147*f2e05ce9SSean Rhodes char *name;
148*f2e05ce9SSean Rhodes char *display_name;
149*f2e05ce9SSean Rhodes struct coreboot_cfr_enum *values;
150*f2e05ce9SSean Rhodes unsigned int n_values;
151*f2e05ce9SSean Rhodes u32 default_value;
152*f2e05ce9SSean Rhodes u32 min;
153*f2e05ce9SSean Rhodes u32 max;
154*f2e05ce9SSean Rhodes u32 step;
155*f2e05ce9SSean Rhodes u32 runtime_apply_method;
156*f2e05ce9SSean Rhodes u32 runtime_apply_id;
157*f2e05ce9SSean Rhodes bool read_only;
158*f2e05ce9SSean Rhodes };
159*f2e05ce9SSean Rhodes
160*f2e05ce9SSean Rhodes struct coreboot_cfr_drvdata {
161*f2e05ce9SSean Rhodes struct device *class_dev;
162*f2e05ce9SSean Rhodes struct kset *attrs_kset;
163*f2e05ce9SSean Rhodes struct list_head settings;
164*f2e05ce9SSean Rhodes /* Serializes EFI variable writes and the matching runtime apply hook. */
165*f2e05ce9SSean Rhodes struct mutex lock;
166*f2e05ce9SSean Rhodes bool efi_writes_supported;
167*f2e05ce9SSean Rhodes bool pending_reboot;
168*f2e05ce9SSean Rhodes };
169*f2e05ce9SSean Rhodes
170*f2e05ce9SSean Rhodes struct coreboot_cfr_iterator {
171*f2e05ce9SSean Rhodes const u8 *cursor;
172*f2e05ce9SSean Rhodes size_t remaining;
173*f2e05ce9SSean Rhodes };
174*f2e05ce9SSean Rhodes
to_coreboot_cfr_setting(struct kobject * kobj)175*f2e05ce9SSean Rhodes static struct coreboot_cfr_setting *to_coreboot_cfr_setting(struct kobject *kobj)
176*f2e05ce9SSean Rhodes {
177*f2e05ce9SSean Rhodes return container_of(kobj, struct coreboot_cfr_setting, kobj);
178*f2e05ce9SSean Rhodes }
179*f2e05ce9SSean Rhodes
coreboot_cfr_string_is_valid_name(const char * name)180*f2e05ce9SSean Rhodes static bool coreboot_cfr_string_is_valid_name(const char *name)
181*f2e05ce9SSean Rhodes {
182*f2e05ce9SSean Rhodes return name && name[0] && !strchr(name, '/') &&
183*f2e05ce9SSean Rhodes strcmp(name, "pending_reboot") != 0;
184*f2e05ce9SSean Rhodes }
185*f2e05ce9SSean Rhodes
coreboot_cfr_string_is_valid_label(const char * label)186*f2e05ce9SSean Rhodes static bool coreboot_cfr_string_is_valid_label(const char *label)
187*f2e05ce9SSean Rhodes {
188*f2e05ce9SSean Rhodes return label && !strchr(label, ';') && !strchr(label, '\n');
189*f2e05ce9SSean Rhodes }
190*f2e05ce9SSean Rhodes
coreboot_cfr_string_dup(const struct lb_cfr_varbinary * str)191*f2e05ce9SSean Rhodes static char *coreboot_cfr_string_dup(const struct lb_cfr_varbinary *str)
192*f2e05ce9SSean Rhodes {
193*f2e05ce9SSean Rhodes const char *data = (const char *)(str + 1);
194*f2e05ce9SSean Rhodes size_t len = str->data_length;
195*f2e05ce9SSean Rhodes
196*f2e05ce9SSean Rhodes if (len && !data[len - 1])
197*f2e05ce9SSean Rhodes len--;
198*f2e05ce9SSean Rhodes
199*f2e05ce9SSean Rhodes return kmemdup_nul(data, len, GFP_KERNEL);
200*f2e05ce9SSean Rhodes }
201*f2e05ce9SSean Rhodes
202*f2e05ce9SSean Rhodes static const struct coreboot_table_entry *
coreboot_cfr_next_entry(struct coreboot_cfr_iterator * iterator)203*f2e05ce9SSean Rhodes coreboot_cfr_next_entry(struct coreboot_cfr_iterator *iterator)
204*f2e05ce9SSean Rhodes {
205*f2e05ce9SSean Rhodes const struct coreboot_table_entry *entry;
206*f2e05ce9SSean Rhodes
207*f2e05ce9SSean Rhodes if (!iterator->remaining)
208*f2e05ce9SSean Rhodes return NULL;
209*f2e05ce9SSean Rhodes
210*f2e05ce9SSean Rhodes if (iterator->remaining < sizeof(*entry))
211*f2e05ce9SSean Rhodes return ERR_PTR(-EINVAL);
212*f2e05ce9SSean Rhodes
213*f2e05ce9SSean Rhodes entry = (const struct coreboot_table_entry *)iterator->cursor;
214*f2e05ce9SSean Rhodes if (entry->size < sizeof(*entry) || entry->size > iterator->remaining)
215*f2e05ce9SSean Rhodes return ERR_PTR(-EINVAL);
216*f2e05ce9SSean Rhodes
217*f2e05ce9SSean Rhodes iterator->cursor += entry->size;
218*f2e05ce9SSean Rhodes iterator->remaining -= entry->size;
219*f2e05ce9SSean Rhodes
220*f2e05ce9SSean Rhodes return entry;
221*f2e05ce9SSean Rhodes }
222*f2e05ce9SSean Rhodes
223*f2e05ce9SSean Rhodes static const struct coreboot_table_entry *
coreboot_cfr_child_entry(const void * base,size_t len,u32 tag)224*f2e05ce9SSean Rhodes coreboot_cfr_child_entry(const void *base, size_t len, u32 tag)
225*f2e05ce9SSean Rhodes {
226*f2e05ce9SSean Rhodes struct coreboot_cfr_iterator iterator = {
227*f2e05ce9SSean Rhodes .cursor = base,
228*f2e05ce9SSean Rhodes .remaining = len,
229*f2e05ce9SSean Rhodes };
230*f2e05ce9SSean Rhodes const struct coreboot_table_entry *entry;
231*f2e05ce9SSean Rhodes
232*f2e05ce9SSean Rhodes for (;;) {
233*f2e05ce9SSean Rhodes entry = coreboot_cfr_next_entry(&iterator);
234*f2e05ce9SSean Rhodes if (IS_ERR_OR_NULL(entry))
235*f2e05ce9SSean Rhodes return entry;
236*f2e05ce9SSean Rhodes
237*f2e05ce9SSean Rhodes if (entry->tag == tag)
238*f2e05ce9SSean Rhodes return entry;
239*f2e05ce9SSean Rhodes }
240*f2e05ce9SSean Rhodes }
241*f2e05ce9SSean Rhodes
242*f2e05ce9SSean Rhodes static const struct lb_cfr_varbinary *
coreboot_cfr_child_string(const void * base,size_t len,u32 tag)243*f2e05ce9SSean Rhodes coreboot_cfr_child_string(const void *base, size_t len, u32 tag)
244*f2e05ce9SSean Rhodes {
245*f2e05ce9SSean Rhodes const struct lb_cfr_varbinary *str;
246*f2e05ce9SSean Rhodes const struct coreboot_table_entry *entry;
247*f2e05ce9SSean Rhodes
248*f2e05ce9SSean Rhodes entry = coreboot_cfr_child_entry(base, len, tag);
249*f2e05ce9SSean Rhodes if (IS_ERR(entry))
250*f2e05ce9SSean Rhodes return ERR_CAST(entry);
251*f2e05ce9SSean Rhodes if (!entry)
252*f2e05ce9SSean Rhodes return NULL;
253*f2e05ce9SSean Rhodes
254*f2e05ce9SSean Rhodes if (entry->size < sizeof(*str))
255*f2e05ce9SSean Rhodes return ERR_PTR(-EINVAL);
256*f2e05ce9SSean Rhodes
257*f2e05ce9SSean Rhodes str = (const struct lb_cfr_varbinary *)entry;
258*f2e05ce9SSean Rhodes if (str->data_length > entry->size - sizeof(*str))
259*f2e05ce9SSean Rhodes return ERR_PTR(-EINVAL);
260*f2e05ce9SSean Rhodes
261*f2e05ce9SSean Rhodes return str;
262*f2e05ce9SSean Rhodes }
263*f2e05ce9SSean Rhodes
264*f2e05ce9SSean Rhodes static const struct lb_cfr_runtime_apply *
coreboot_cfr_child_runtime_apply(const void * base,size_t len)265*f2e05ce9SSean Rhodes coreboot_cfr_child_runtime_apply(const void *base, size_t len)
266*f2e05ce9SSean Rhodes {
267*f2e05ce9SSean Rhodes const struct lb_cfr_runtime_apply *runtime_apply;
268*f2e05ce9SSean Rhodes const struct coreboot_table_entry *entry;
269*f2e05ce9SSean Rhodes
270*f2e05ce9SSean Rhodes entry = coreboot_cfr_child_entry(base, len, CFR_TAG_RUNTIME_APPLY);
271*f2e05ce9SSean Rhodes if (IS_ERR(entry))
272*f2e05ce9SSean Rhodes return ERR_CAST(entry);
273*f2e05ce9SSean Rhodes if (!entry)
274*f2e05ce9SSean Rhodes return NULL;
275*f2e05ce9SSean Rhodes
276*f2e05ce9SSean Rhodes if (entry->size < sizeof(*runtime_apply))
277*f2e05ce9SSean Rhodes return ERR_PTR(-EINVAL);
278*f2e05ce9SSean Rhodes
279*f2e05ce9SSean Rhodes runtime_apply = (const struct lb_cfr_runtime_apply *)entry;
280*f2e05ce9SSean Rhodes if (runtime_apply->method == CFR_RUNTIME_APPLY_APM_CNT &&
281*f2e05ce9SSean Rhodes runtime_apply->id > U8_MAX)
282*f2e05ce9SSean Rhodes return ERR_PTR(-EINVAL);
283*f2e05ce9SSean Rhodes
284*f2e05ce9SSean Rhodes return runtime_apply;
285*f2e05ce9SSean Rhodes }
286*f2e05ce9SSean Rhodes
coreboot_cfr_efi_name(const char * name)287*f2e05ce9SSean Rhodes static efi_char16_t *coreboot_cfr_efi_name(const char *name)
288*f2e05ce9SSean Rhodes {
289*f2e05ce9SSean Rhodes size_t len, i;
290*f2e05ce9SSean Rhodes
291*f2e05ce9SSean Rhodes len = strlen(name);
292*f2e05ce9SSean Rhodes if (len >= EFI_VAR_NAME_LEN)
293*f2e05ce9SSean Rhodes return ERR_PTR(-ENAMETOOLONG);
294*f2e05ce9SSean Rhodes
295*f2e05ce9SSean Rhodes efi_char16_t *efi_name __free(kfree) =
296*f2e05ce9SSean Rhodes kcalloc(len + 1, sizeof(*efi_name), GFP_KERNEL);
297*f2e05ce9SSean Rhodes if (!efi_name)
298*f2e05ce9SSean Rhodes return ERR_PTR(-ENOMEM);
299*f2e05ce9SSean Rhodes
300*f2e05ce9SSean Rhodes for (i = 0; i < len; i++) {
301*f2e05ce9SSean Rhodes if (!isascii(name[i]))
302*f2e05ce9SSean Rhodes return ERR_PTR(-EINVAL);
303*f2e05ce9SSean Rhodes efi_name[i] = name[i];
304*f2e05ce9SSean Rhodes }
305*f2e05ce9SSean Rhodes
306*f2e05ce9SSean Rhodes return no_free_ptr(efi_name);
307*f2e05ce9SSean Rhodes }
308*f2e05ce9SSean Rhodes
309*f2e05ce9SSean Rhodes /* Caller must hold the efivar lock. */
coreboot_cfr_read_efi_value(efi_char16_t * efi_name,u32 * value,u32 * attrs)310*f2e05ce9SSean Rhodes static int coreboot_cfr_read_efi_value(efi_char16_t *efi_name, u32 *value,
311*f2e05ce9SSean Rhodes u32 *attrs)
312*f2e05ce9SSean Rhodes {
313*f2e05ce9SSean Rhodes unsigned long size = sizeof(__le32);
314*f2e05ce9SSean Rhodes efi_status_t status;
315*f2e05ce9SSean Rhodes __le32 data;
316*f2e05ce9SSean Rhodes u32 attr;
317*f2e05ce9SSean Rhodes
318*f2e05ce9SSean Rhodes status = efivar_get_variable(efi_name, &coreboot_cfr_guid, &attr,
319*f2e05ce9SSean Rhodes &size, &data);
320*f2e05ce9SSean Rhodes if (status != EFI_SUCCESS)
321*f2e05ce9SSean Rhodes return efi_status_to_err(status);
322*f2e05ce9SSean Rhodes
323*f2e05ce9SSean Rhodes if (size != sizeof(data))
324*f2e05ce9SSean Rhodes return -EINVAL;
325*f2e05ce9SSean Rhodes
326*f2e05ce9SSean Rhodes if (!(attr & EFI_VARIABLE_RUNTIME_ACCESS))
327*f2e05ce9SSean Rhodes return -EOPNOTSUPP;
328*f2e05ce9SSean Rhodes
329*f2e05ce9SSean Rhodes *value = le32_to_cpu(data);
330*f2e05ce9SSean Rhodes if (attrs)
331*f2e05ce9SSean Rhodes *attrs = attr;
332*f2e05ce9SSean Rhodes
333*f2e05ce9SSean Rhodes return 0;
334*f2e05ce9SSean Rhodes }
335*f2e05ce9SSean Rhodes
coreboot_cfr_read_value(const struct coreboot_cfr_setting * setting,u32 * value,u32 * attrs)336*f2e05ce9SSean Rhodes static int coreboot_cfr_read_value(const struct coreboot_cfr_setting *setting,
337*f2e05ce9SSean Rhodes u32 *value, u32 *attrs)
338*f2e05ce9SSean Rhodes {
339*f2e05ce9SSean Rhodes efi_char16_t *efi_name __free(kfree) =
340*f2e05ce9SSean Rhodes coreboot_cfr_efi_name(setting->name);
341*f2e05ce9SSean Rhodes int ret;
342*f2e05ce9SSean Rhodes
343*f2e05ce9SSean Rhodes if (IS_ERR(efi_name))
344*f2e05ce9SSean Rhodes return PTR_ERR(no_free_ptr(efi_name));
345*f2e05ce9SSean Rhodes
346*f2e05ce9SSean Rhodes ret = efivar_lock();
347*f2e05ce9SSean Rhodes if (ret)
348*f2e05ce9SSean Rhodes return ret;
349*f2e05ce9SSean Rhodes
350*f2e05ce9SSean Rhodes ret = coreboot_cfr_read_efi_value(efi_name, value, attrs);
351*f2e05ce9SSean Rhodes efivar_unlock();
352*f2e05ce9SSean Rhodes
353*f2e05ce9SSean Rhodes return ret;
354*f2e05ce9SSean Rhodes }
355*f2e05ce9SSean Rhodes
356*f2e05ce9SSean Rhodes /* Caller must hold the efivar lock. */
coreboot_cfr_write_efi_value(efi_char16_t * efi_name,u32 value,u32 attrs)357*f2e05ce9SSean Rhodes static int coreboot_cfr_write_efi_value(efi_char16_t *efi_name, u32 value,
358*f2e05ce9SSean Rhodes u32 attrs)
359*f2e05ce9SSean Rhodes {
360*f2e05ce9SSean Rhodes efi_status_t status;
361*f2e05ce9SSean Rhodes __le32 data;
362*f2e05ce9SSean Rhodes
363*f2e05ce9SSean Rhodes if (!efivar_supports_writes())
364*f2e05ce9SSean Rhodes return -EROFS;
365*f2e05ce9SSean Rhodes
366*f2e05ce9SSean Rhodes data = cpu_to_le32(value);
367*f2e05ce9SSean Rhodes status = efivar_set_variable_locked(efi_name, &coreboot_cfr_guid, attrs,
368*f2e05ce9SSean Rhodes sizeof(data), &data, false);
369*f2e05ce9SSean Rhodes if (status != EFI_SUCCESS)
370*f2e05ce9SSean Rhodes return efi_status_to_err(status);
371*f2e05ce9SSean Rhodes
372*f2e05ce9SSean Rhodes return 0;
373*f2e05ce9SSean Rhodes }
374*f2e05ce9SSean Rhodes
coreboot_cfr_apply_runtime(struct coreboot_cfr_setting * setting)375*f2e05ce9SSean Rhodes static int coreboot_cfr_apply_runtime(struct coreboot_cfr_setting *setting)
376*f2e05ce9SSean Rhodes {
377*f2e05ce9SSean Rhodes u8 status;
378*f2e05ce9SSean Rhodes
379*f2e05ce9SSean Rhodes if (setting->runtime_apply_method != CFR_RUNTIME_APPLY_APM_CNT)
380*f2e05ce9SSean Rhodes return -EOPNOTSUPP;
381*f2e05ce9SSean Rhodes
382*f2e05ce9SSean Rhodes outb((u8)setting->runtime_apply_id, COREBOOT_CFR_APM_STS_PORT);
383*f2e05ce9SSean Rhodes outb(COREBOOT_CFR_APM_APPLY_CMD, COREBOOT_CFR_APM_CNT_PORT);
384*f2e05ce9SSean Rhodes status = inb(COREBOOT_CFR_APM_STS_PORT);
385*f2e05ce9SSean Rhodes if (status)
386*f2e05ce9SSean Rhodes return -EIO;
387*f2e05ce9SSean Rhodes
388*f2e05ce9SSean Rhodes return 0;
389*f2e05ce9SSean Rhodes }
390*f2e05ce9SSean Rhodes
coreboot_cfr_write_value(struct coreboot_cfr_setting * setting,u32 value)391*f2e05ce9SSean Rhodes static int coreboot_cfr_write_value(struct coreboot_cfr_setting *setting,
392*f2e05ce9SSean Rhodes u32 value)
393*f2e05ce9SSean Rhodes {
394*f2e05ce9SSean Rhodes efi_char16_t *efi_name;
395*f2e05ce9SSean Rhodes u32 attrs;
396*f2e05ce9SSean Rhodes u32 old;
397*f2e05ce9SSean Rhodes int restore_ret;
398*f2e05ce9SSean Rhodes int ret;
399*f2e05ce9SSean Rhodes
400*f2e05ce9SSean Rhodes if (setting->read_only)
401*f2e05ce9SSean Rhodes return -EACCES;
402*f2e05ce9SSean Rhodes
403*f2e05ce9SSean Rhodes efi_name = coreboot_cfr_efi_name(setting->name);
404*f2e05ce9SSean Rhodes if (IS_ERR(efi_name))
405*f2e05ce9SSean Rhodes return PTR_ERR(efi_name);
406*f2e05ce9SSean Rhodes
407*f2e05ce9SSean Rhodes mutex_lock(&setting->drvdata->lock);
408*f2e05ce9SSean Rhodes
409*f2e05ce9SSean Rhodes ret = efivar_lock();
410*f2e05ce9SSean Rhodes if (ret)
411*f2e05ce9SSean Rhodes goto out_unlock_mutex;
412*f2e05ce9SSean Rhodes
413*f2e05ce9SSean Rhodes ret = coreboot_cfr_read_efi_value(efi_name, &old, &attrs);
414*f2e05ce9SSean Rhodes if (ret)
415*f2e05ce9SSean Rhodes goto out_unlock_efi;
416*f2e05ce9SSean Rhodes
417*f2e05ce9SSean Rhodes if ((attrs & COREBOOT_CFR_EFI_ATTRS) != COREBOOT_CFR_EFI_ATTRS) {
418*f2e05ce9SSean Rhodes ret = -EOPNOTSUPP;
419*f2e05ce9SSean Rhodes goto out_unlock_efi;
420*f2e05ce9SSean Rhodes }
421*f2e05ce9SSean Rhodes
422*f2e05ce9SSean Rhodes if (old == value)
423*f2e05ce9SSean Rhodes goto out_unlock_efi;
424*f2e05ce9SSean Rhodes
425*f2e05ce9SSean Rhodes ret = coreboot_cfr_write_efi_value(efi_name, value, attrs);
426*f2e05ce9SSean Rhodes if (ret)
427*f2e05ce9SSean Rhodes goto out_unlock_efi;
428*f2e05ce9SSean Rhodes
429*f2e05ce9SSean Rhodes ret = coreboot_cfr_apply_runtime(setting);
430*f2e05ce9SSean Rhodes if (ret == -EOPNOTSUPP) {
431*f2e05ce9SSean Rhodes /* EFI changed; firmware will consume it after reboot. */
432*f2e05ce9SSean Rhodes setting->drvdata->pending_reboot = true;
433*f2e05ce9SSean Rhodes ret = 0;
434*f2e05ce9SSean Rhodes } else if (ret) {
435*f2e05ce9SSean Rhodes restore_ret = coreboot_cfr_write_efi_value(efi_name, old, attrs);
436*f2e05ce9SSean Rhodes if (restore_ret) {
437*f2e05ce9SSean Rhodes setting->drvdata->pending_reboot = true;
438*f2e05ce9SSean Rhodes ret = restore_ret;
439*f2e05ce9SSean Rhodes } else {
440*f2e05ce9SSean Rhodes goto out_unlock_efi;
441*f2e05ce9SSean Rhodes }
442*f2e05ce9SSean Rhodes }
443*f2e05ce9SSean Rhodes
444*f2e05ce9SSean Rhodes efivar_unlock();
445*f2e05ce9SSean Rhodes kobject_uevent(&setting->drvdata->class_dev->kobj, KOBJ_CHANGE);
446*f2e05ce9SSean Rhodes mutex_unlock(&setting->drvdata->lock);
447*f2e05ce9SSean Rhodes kfree(efi_name);
448*f2e05ce9SSean Rhodes return ret;
449*f2e05ce9SSean Rhodes
450*f2e05ce9SSean Rhodes out_unlock_efi:
451*f2e05ce9SSean Rhodes efivar_unlock();
452*f2e05ce9SSean Rhodes out_unlock_mutex:
453*f2e05ce9SSean Rhodes mutex_unlock(&setting->drvdata->lock);
454*f2e05ce9SSean Rhodes kfree(efi_name);
455*f2e05ce9SSean Rhodes return ret;
456*f2e05ce9SSean Rhodes }
457*f2e05ce9SSean Rhodes
458*f2e05ce9SSean Rhodes static const char *
coreboot_cfr_label_from_value(const struct coreboot_cfr_setting * setting,u32 value)459*f2e05ce9SSean Rhodes coreboot_cfr_label_from_value(const struct coreboot_cfr_setting *setting,
460*f2e05ce9SSean Rhodes u32 value)
461*f2e05ce9SSean Rhodes {
462*f2e05ce9SSean Rhodes unsigned int i;
463*f2e05ce9SSean Rhodes
464*f2e05ce9SSean Rhodes for (i = 0; i < setting->n_values; i++) {
465*f2e05ce9SSean Rhodes if (setting->values[i].value == value)
466*f2e05ce9SSean Rhodes return setting->values[i].label;
467*f2e05ce9SSean Rhodes }
468*f2e05ce9SSean Rhodes
469*f2e05ce9SSean Rhodes return NULL;
470*f2e05ce9SSean Rhodes }
471*f2e05ce9SSean Rhodes
coreboot_cfr_parse_value(struct coreboot_cfr_setting * setting,const char * label,u32 * value_out)472*f2e05ce9SSean Rhodes static int coreboot_cfr_parse_value(struct coreboot_cfr_setting *setting,
473*f2e05ce9SSean Rhodes const char *label, u32 *value_out)
474*f2e05ce9SSean Rhodes {
475*f2e05ce9SSean Rhodes unsigned int i;
476*f2e05ce9SSean Rhodes
477*f2e05ce9SSean Rhodes for (i = 0; i < setting->n_values; i++) {
478*f2e05ce9SSean Rhodes if (!sysfs_streq(label, setting->values[i].label))
479*f2e05ce9SSean Rhodes continue;
480*f2e05ce9SSean Rhodes
481*f2e05ce9SSean Rhodes *value_out = setting->values[i].value;
482*f2e05ce9SSean Rhodes return 0;
483*f2e05ce9SSean Rhodes }
484*f2e05ce9SSean Rhodes
485*f2e05ce9SSean Rhodes return kstrtou32(label, 0, value_out);
486*f2e05ce9SSean Rhodes }
487*f2e05ce9SSean Rhodes
coreboot_cfr_value_is_valid(struct coreboot_cfr_setting * setting,u32 value)488*f2e05ce9SSean Rhodes static bool coreboot_cfr_value_is_valid(struct coreboot_cfr_setting *setting,
489*f2e05ce9SSean Rhodes u32 value)
490*f2e05ce9SSean Rhodes {
491*f2e05ce9SSean Rhodes u32 delta;
492*f2e05ce9SSean Rhodes
493*f2e05ce9SSean Rhodes if (setting->type != COREBOOT_CFR_SETTING_NUMBER) {
494*f2e05ce9SSean Rhodes /* Enum and bool values are valid only if they have a label. */
495*f2e05ce9SSean Rhodes return coreboot_cfr_label_from_value(setting, value);
496*f2e05ce9SSean Rhodes }
497*f2e05ce9SSean Rhodes
498*f2e05ce9SSean Rhodes if (value < setting->min || value > setting->max)
499*f2e05ce9SSean Rhodes return false;
500*f2e05ce9SSean Rhodes
501*f2e05ce9SSean Rhodes if (!setting->step)
502*f2e05ce9SSean Rhodes return true;
503*f2e05ce9SSean Rhodes
504*f2e05ce9SSean Rhodes delta = value - setting->min;
505*f2e05ce9SSean Rhodes return delta % setting->step == 0;
506*f2e05ce9SSean Rhodes }
507*f2e05ce9SSean Rhodes
type_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)508*f2e05ce9SSean Rhodes static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
509*f2e05ce9SSean Rhodes char *buf)
510*f2e05ce9SSean Rhodes {
511*f2e05ce9SSean Rhodes struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj);
512*f2e05ce9SSean Rhodes
513*f2e05ce9SSean Rhodes if (setting->type == COREBOOT_CFR_SETTING_NUMBER)
514*f2e05ce9SSean Rhodes return sysfs_emit(buf, "integer\n");
515*f2e05ce9SSean Rhodes
516*f2e05ce9SSean Rhodes return sysfs_emit(buf, "enumeration\n");
517*f2e05ce9SSean Rhodes }
518*f2e05ce9SSean Rhodes
display_name_language_code_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)519*f2e05ce9SSean Rhodes static ssize_t display_name_language_code_show(struct kobject *kobj,
520*f2e05ce9SSean Rhodes struct kobj_attribute *attr,
521*f2e05ce9SSean Rhodes char *buf)
522*f2e05ce9SSean Rhodes {
523*f2e05ce9SSean Rhodes return sysfs_emit(buf, "en_US.UTF-8\n");
524*f2e05ce9SSean Rhodes }
525*f2e05ce9SSean Rhodes
display_name_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)526*f2e05ce9SSean Rhodes static ssize_t display_name_show(struct kobject *kobj,
527*f2e05ce9SSean Rhodes struct kobj_attribute *attr, char *buf)
528*f2e05ce9SSean Rhodes {
529*f2e05ce9SSean Rhodes struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj);
530*f2e05ce9SSean Rhodes
531*f2e05ce9SSean Rhodes return sysfs_emit(buf, "%s\n", setting->display_name);
532*f2e05ce9SSean Rhodes }
533*f2e05ce9SSean Rhodes
possible_values_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)534*f2e05ce9SSean Rhodes static ssize_t possible_values_show(struct kobject *kobj,
535*f2e05ce9SSean Rhodes struct kobj_attribute *attr, char *buf)
536*f2e05ce9SSean Rhodes {
537*f2e05ce9SSean Rhodes struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj);
538*f2e05ce9SSean Rhodes ssize_t len = 0;
539*f2e05ce9SSean Rhodes unsigned int i;
540*f2e05ce9SSean Rhodes
541*f2e05ce9SSean Rhodes for (i = 0; i < setting->n_values; i++) {
542*f2e05ce9SSean Rhodes len += sysfs_emit_at(buf, len, "%s%s", i ? ";" : "",
543*f2e05ce9SSean Rhodes setting->values[i].label);
544*f2e05ce9SSean Rhodes }
545*f2e05ce9SSean Rhodes
546*f2e05ce9SSean Rhodes len += sysfs_emit_at(buf, len, "\n");
547*f2e05ce9SSean Rhodes return len;
548*f2e05ce9SSean Rhodes }
549*f2e05ce9SSean Rhodes
min_value_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)550*f2e05ce9SSean Rhodes static ssize_t min_value_show(struct kobject *kobj, struct kobj_attribute *attr,
551*f2e05ce9SSean Rhodes char *buf)
552*f2e05ce9SSean Rhodes {
553*f2e05ce9SSean Rhodes struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj);
554*f2e05ce9SSean Rhodes
555*f2e05ce9SSean Rhodes return sysfs_emit(buf, "%u\n", setting->min);
556*f2e05ce9SSean Rhodes }
557*f2e05ce9SSean Rhodes
max_value_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)558*f2e05ce9SSean Rhodes static ssize_t max_value_show(struct kobject *kobj, struct kobj_attribute *attr,
559*f2e05ce9SSean Rhodes char *buf)
560*f2e05ce9SSean Rhodes {
561*f2e05ce9SSean Rhodes struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj);
562*f2e05ce9SSean Rhodes
563*f2e05ce9SSean Rhodes return sysfs_emit(buf, "%u\n", setting->max);
564*f2e05ce9SSean Rhodes }
565*f2e05ce9SSean Rhodes
scalar_increment_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)566*f2e05ce9SSean Rhodes static ssize_t scalar_increment_show(struct kobject *kobj,
567*f2e05ce9SSean Rhodes struct kobj_attribute *attr, char *buf)
568*f2e05ce9SSean Rhodes {
569*f2e05ce9SSean Rhodes struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj);
570*f2e05ce9SSean Rhodes
571*f2e05ce9SSean Rhodes return sysfs_emit(buf, "%u\n", setting->step);
572*f2e05ce9SSean Rhodes }
573*f2e05ce9SSean Rhodes
default_value_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)574*f2e05ce9SSean Rhodes static ssize_t default_value_show(struct kobject *kobj,
575*f2e05ce9SSean Rhodes struct kobj_attribute *attr, char *buf)
576*f2e05ce9SSean Rhodes {
577*f2e05ce9SSean Rhodes struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj);
578*f2e05ce9SSean Rhodes const char *label;
579*f2e05ce9SSean Rhodes
580*f2e05ce9SSean Rhodes if (setting->type == COREBOOT_CFR_SETTING_NUMBER)
581*f2e05ce9SSean Rhodes return sysfs_emit(buf, "%u\n", setting->default_value);
582*f2e05ce9SSean Rhodes
583*f2e05ce9SSean Rhodes label = coreboot_cfr_label_from_value(setting, setting->default_value);
584*f2e05ce9SSean Rhodes if (!label)
585*f2e05ce9SSean Rhodes return sysfs_emit(buf, "%u\n", setting->default_value);
586*f2e05ce9SSean Rhodes
587*f2e05ce9SSean Rhodes return sysfs_emit(buf, "%s\n", label);
588*f2e05ce9SSean Rhodes }
589*f2e05ce9SSean Rhodes
current_value_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)590*f2e05ce9SSean Rhodes static ssize_t current_value_show(struct kobject *kobj,
591*f2e05ce9SSean Rhodes struct kobj_attribute *attr, char *buf)
592*f2e05ce9SSean Rhodes {
593*f2e05ce9SSean Rhodes struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj);
594*f2e05ce9SSean Rhodes const char *label;
595*f2e05ce9SSean Rhodes u32 value;
596*f2e05ce9SSean Rhodes int ret;
597*f2e05ce9SSean Rhodes
598*f2e05ce9SSean Rhodes ret = coreboot_cfr_read_value(setting, &value, NULL);
599*f2e05ce9SSean Rhodes if (ret)
600*f2e05ce9SSean Rhodes return ret;
601*f2e05ce9SSean Rhodes
602*f2e05ce9SSean Rhodes if (setting->type == COREBOOT_CFR_SETTING_NUMBER)
603*f2e05ce9SSean Rhodes return sysfs_emit(buf, "%u\n", value);
604*f2e05ce9SSean Rhodes
605*f2e05ce9SSean Rhodes label = coreboot_cfr_label_from_value(setting, value);
606*f2e05ce9SSean Rhodes if (!label)
607*f2e05ce9SSean Rhodes return -EINVAL;
608*f2e05ce9SSean Rhodes
609*f2e05ce9SSean Rhodes return sysfs_emit(buf, "%s\n", label);
610*f2e05ce9SSean Rhodes }
611*f2e05ce9SSean Rhodes
current_value_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)612*f2e05ce9SSean Rhodes static ssize_t current_value_store(struct kobject *kobj,
613*f2e05ce9SSean Rhodes struct kobj_attribute *attr,
614*f2e05ce9SSean Rhodes const char *buf, size_t count)
615*f2e05ce9SSean Rhodes {
616*f2e05ce9SSean Rhodes struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj);
617*f2e05ce9SSean Rhodes u32 value;
618*f2e05ce9SSean Rhodes int ret;
619*f2e05ce9SSean Rhodes
620*f2e05ce9SSean Rhodes ret = coreboot_cfr_parse_value(setting, buf, &value);
621*f2e05ce9SSean Rhodes if (ret)
622*f2e05ce9SSean Rhodes return ret;
623*f2e05ce9SSean Rhodes
624*f2e05ce9SSean Rhodes if (!coreboot_cfr_value_is_valid(setting, value))
625*f2e05ce9SSean Rhodes return -EINVAL;
626*f2e05ce9SSean Rhodes
627*f2e05ce9SSean Rhodes ret = coreboot_cfr_write_value(setting, value);
628*f2e05ce9SSean Rhodes if (ret)
629*f2e05ce9SSean Rhodes return ret;
630*f2e05ce9SSean Rhodes
631*f2e05ce9SSean Rhodes return count;
632*f2e05ce9SSean Rhodes }
633*f2e05ce9SSean Rhodes
634*f2e05ce9SSean Rhodes static struct kobj_attribute type_attr = __ATTR_RO(type);
635*f2e05ce9SSean Rhodes static struct kobj_attribute display_name_language_code_attr =
636*f2e05ce9SSean Rhodes __ATTR_RO(display_name_language_code);
637*f2e05ce9SSean Rhodes static struct kobj_attribute display_name_attr = __ATTR_RO(display_name);
638*f2e05ce9SSean Rhodes static struct kobj_attribute possible_values_attr = __ATTR_RO(possible_values);
639*f2e05ce9SSean Rhodes static struct kobj_attribute min_value_attr = __ATTR_RO(min_value);
640*f2e05ce9SSean Rhodes static struct kobj_attribute max_value_attr = __ATTR_RO(max_value);
641*f2e05ce9SSean Rhodes static struct kobj_attribute scalar_increment_attr = __ATTR_RO(scalar_increment);
642*f2e05ce9SSean Rhodes static struct kobj_attribute default_value_attr = __ATTR_RO(default_value);
643*f2e05ce9SSean Rhodes static struct kobj_attribute current_value_attr = __ATTR_RW(current_value);
644*f2e05ce9SSean Rhodes
645*f2e05ce9SSean Rhodes static struct attribute *coreboot_cfr_setting_attrs[] = {
646*f2e05ce9SSean Rhodes &type_attr.attr,
647*f2e05ce9SSean Rhodes &display_name_language_code_attr.attr,
648*f2e05ce9SSean Rhodes &display_name_attr.attr,
649*f2e05ce9SSean Rhodes &possible_values_attr.attr,
650*f2e05ce9SSean Rhodes &min_value_attr.attr,
651*f2e05ce9SSean Rhodes &max_value_attr.attr,
652*f2e05ce9SSean Rhodes &scalar_increment_attr.attr,
653*f2e05ce9SSean Rhodes &default_value_attr.attr,
654*f2e05ce9SSean Rhodes ¤t_value_attr.attr,
655*f2e05ce9SSean Rhodes NULL,
656*f2e05ce9SSean Rhodes };
657*f2e05ce9SSean Rhodes
coreboot_cfr_attr_is_visible(struct kobject * kobj,struct attribute * attr,int n)658*f2e05ce9SSean Rhodes static umode_t coreboot_cfr_attr_is_visible(struct kobject *kobj,
659*f2e05ce9SSean Rhodes struct attribute *attr, int n)
660*f2e05ce9SSean Rhodes {
661*f2e05ce9SSean Rhodes struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj);
662*f2e05ce9SSean Rhodes
663*f2e05ce9SSean Rhodes if (setting->type == COREBOOT_CFR_SETTING_NUMBER &&
664*f2e05ce9SSean Rhodes attr == &possible_values_attr.attr)
665*f2e05ce9SSean Rhodes return 0;
666*f2e05ce9SSean Rhodes
667*f2e05ce9SSean Rhodes if (setting->type != COREBOOT_CFR_SETTING_NUMBER &&
668*f2e05ce9SSean Rhodes (attr == &min_value_attr.attr || attr == &max_value_attr.attr ||
669*f2e05ce9SSean Rhodes attr == &scalar_increment_attr.attr))
670*f2e05ce9SSean Rhodes return 0;
671*f2e05ce9SSean Rhodes
672*f2e05ce9SSean Rhodes if (setting->read_only && attr == ¤t_value_attr.attr)
673*f2e05ce9SSean Rhodes return 0444;
674*f2e05ce9SSean Rhodes
675*f2e05ce9SSean Rhodes return attr->mode;
676*f2e05ce9SSean Rhodes }
677*f2e05ce9SSean Rhodes
678*f2e05ce9SSean Rhodes static const struct attribute_group coreboot_cfr_setting_group = {
679*f2e05ce9SSean Rhodes .attrs = coreboot_cfr_setting_attrs,
680*f2e05ce9SSean Rhodes .is_visible = coreboot_cfr_attr_is_visible,
681*f2e05ce9SSean Rhodes };
682*f2e05ce9SSean Rhodes
coreboot_cfr_free_setting(struct coreboot_cfr_setting * setting)683*f2e05ce9SSean Rhodes static void coreboot_cfr_free_setting(struct coreboot_cfr_setting *setting)
684*f2e05ce9SSean Rhodes {
685*f2e05ce9SSean Rhodes unsigned int i;
686*f2e05ce9SSean Rhodes
687*f2e05ce9SSean Rhodes for (i = 0; i < setting->n_values; i++)
688*f2e05ce9SSean Rhodes kfree(setting->values[i].label);
689*f2e05ce9SSean Rhodes
690*f2e05ce9SSean Rhodes kfree(setting->values);
691*f2e05ce9SSean Rhodes kfree(setting->display_name);
692*f2e05ce9SSean Rhodes kfree(setting->name);
693*f2e05ce9SSean Rhodes kfree(setting);
694*f2e05ce9SSean Rhodes }
695*f2e05ce9SSean Rhodes
coreboot_cfr_setting_release(struct kobject * kobj)696*f2e05ce9SSean Rhodes static void coreboot_cfr_setting_release(struct kobject *kobj)
697*f2e05ce9SSean Rhodes {
698*f2e05ce9SSean Rhodes coreboot_cfr_free_setting(to_coreboot_cfr_setting(kobj));
699*f2e05ce9SSean Rhodes }
700*f2e05ce9SSean Rhodes
701*f2e05ce9SSean Rhodes static const struct kobj_type coreboot_cfr_setting_ktype = {
702*f2e05ce9SSean Rhodes .release = coreboot_cfr_setting_release,
703*f2e05ce9SSean Rhodes .sysfs_ops = &kobj_sysfs_ops,
704*f2e05ce9SSean Rhodes };
705*f2e05ce9SSean Rhodes
pending_reboot_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)706*f2e05ce9SSean Rhodes static ssize_t pending_reboot_show(struct kobject *kobj,
707*f2e05ce9SSean Rhodes struct kobj_attribute *attr, char *buf)
708*f2e05ce9SSean Rhodes {
709*f2e05ce9SSean Rhodes struct device *dev = kobj_to_dev(kobj->parent);
710*f2e05ce9SSean Rhodes struct coreboot_cfr_drvdata *data;
711*f2e05ce9SSean Rhodes
712*f2e05ce9SSean Rhodes data = dev_get_drvdata(dev);
713*f2e05ce9SSean Rhodes
714*f2e05ce9SSean Rhodes return sysfs_emit(buf, "%d\n", data->pending_reboot);
715*f2e05ce9SSean Rhodes }
716*f2e05ce9SSean Rhodes
717*f2e05ce9SSean Rhodes static struct kobj_attribute pending_reboot_attr = __ATTR_RO(pending_reboot);
718*f2e05ce9SSean Rhodes
coreboot_cfr_copy_bool_values(struct coreboot_cfr_setting * setting)719*f2e05ce9SSean Rhodes static int coreboot_cfr_copy_bool_values(struct coreboot_cfr_setting *setting)
720*f2e05ce9SSean Rhodes {
721*f2e05ce9SSean Rhodes static const struct coreboot_cfr_enum bool_values[] = {
722*f2e05ce9SSean Rhodes { .label = "Disabled", .value = 0 },
723*f2e05ce9SSean Rhodes { .label = "Enabled", .value = 1 },
724*f2e05ce9SSean Rhodes };
725*f2e05ce9SSean Rhodes unsigned int i;
726*f2e05ce9SSean Rhodes
727*f2e05ce9SSean Rhodes setting->values = kcalloc(ARRAY_SIZE(bool_values), sizeof(*setting->values),
728*f2e05ce9SSean Rhodes GFP_KERNEL);
729*f2e05ce9SSean Rhodes if (!setting->values)
730*f2e05ce9SSean Rhodes return -ENOMEM;
731*f2e05ce9SSean Rhodes
732*f2e05ce9SSean Rhodes for (i = 0; i < ARRAY_SIZE(bool_values); i++) {
733*f2e05ce9SSean Rhodes setting->values[i].label = kstrdup(bool_values[i].label,
734*f2e05ce9SSean Rhodes GFP_KERNEL);
735*f2e05ce9SSean Rhodes if (!setting->values[i].label)
736*f2e05ce9SSean Rhodes return -ENOMEM;
737*f2e05ce9SSean Rhodes setting->values[i].value = bool_values[i].value;
738*f2e05ce9SSean Rhodes setting->n_values++;
739*f2e05ce9SSean Rhodes }
740*f2e05ce9SSean Rhodes
741*f2e05ce9SSean Rhodes return 0;
742*f2e05ce9SSean Rhodes }
743*f2e05ce9SSean Rhodes
coreboot_cfr_count_enum_values(const void * base,size_t len)744*f2e05ce9SSean Rhodes static int coreboot_cfr_count_enum_values(const void *base, size_t len)
745*f2e05ce9SSean Rhodes {
746*f2e05ce9SSean Rhodes struct coreboot_cfr_iterator iterator = {
747*f2e05ce9SSean Rhodes .cursor = base,
748*f2e05ce9SSean Rhodes .remaining = len,
749*f2e05ce9SSean Rhodes };
750*f2e05ce9SSean Rhodes const struct coreboot_table_entry *entry;
751*f2e05ce9SSean Rhodes int count = 0;
752*f2e05ce9SSean Rhodes
753*f2e05ce9SSean Rhodes for (;;) {
754*f2e05ce9SSean Rhodes entry = coreboot_cfr_next_entry(&iterator);
755*f2e05ce9SSean Rhodes if (IS_ERR(entry))
756*f2e05ce9SSean Rhodes return PTR_ERR(entry);
757*f2e05ce9SSean Rhodes if (!entry)
758*f2e05ce9SSean Rhodes return count;
759*f2e05ce9SSean Rhodes
760*f2e05ce9SSean Rhodes if (entry->tag == CFR_TAG_ENUM_VALUE)
761*f2e05ce9SSean Rhodes count++;
762*f2e05ce9SSean Rhodes }
763*f2e05ce9SSean Rhodes }
764*f2e05ce9SSean Rhodes
coreboot_cfr_copy_enum_values(struct coreboot_cfr_setting * setting,const void * base,size_t len)765*f2e05ce9SSean Rhodes static int coreboot_cfr_copy_enum_values(struct coreboot_cfr_setting *setting,
766*f2e05ce9SSean Rhodes const void *base, size_t len)
767*f2e05ce9SSean Rhodes {
768*f2e05ce9SSean Rhodes struct coreboot_cfr_iterator iterator = {
769*f2e05ce9SSean Rhodes .cursor = base,
770*f2e05ce9SSean Rhodes .remaining = len,
771*f2e05ce9SSean Rhodes };
772*f2e05ce9SSean Rhodes const struct lb_cfr_enum_value *enum_value;
773*f2e05ce9SSean Rhodes const struct lb_cfr_varbinary *label;
774*f2e05ce9SSean Rhodes const struct coreboot_table_entry *entry;
775*f2e05ce9SSean Rhodes struct coreboot_cfr_enum *value;
776*f2e05ce9SSean Rhodes int count;
777*f2e05ce9SSean Rhodes
778*f2e05ce9SSean Rhodes count = coreboot_cfr_count_enum_values(base, len);
779*f2e05ce9SSean Rhodes if (count <= 0)
780*f2e05ce9SSean Rhodes return count ?: -EINVAL;
781*f2e05ce9SSean Rhodes
782*f2e05ce9SSean Rhodes setting->values = kcalloc(count, sizeof(*setting->values), GFP_KERNEL);
783*f2e05ce9SSean Rhodes if (!setting->values)
784*f2e05ce9SSean Rhodes return -ENOMEM;
785*f2e05ce9SSean Rhodes
786*f2e05ce9SSean Rhodes for (;;) {
787*f2e05ce9SSean Rhodes entry = coreboot_cfr_next_entry(&iterator);
788*f2e05ce9SSean Rhodes if (IS_ERR(entry))
789*f2e05ce9SSean Rhodes return PTR_ERR(entry);
790*f2e05ce9SSean Rhodes if (!entry)
791*f2e05ce9SSean Rhodes return 0;
792*f2e05ce9SSean Rhodes
793*f2e05ce9SSean Rhodes if (entry->tag != CFR_TAG_ENUM_VALUE)
794*f2e05ce9SSean Rhodes continue;
795*f2e05ce9SSean Rhodes
796*f2e05ce9SSean Rhodes if (entry->size < sizeof(*enum_value))
797*f2e05ce9SSean Rhodes return -EINVAL;
798*f2e05ce9SSean Rhodes
799*f2e05ce9SSean Rhodes enum_value = (const struct lb_cfr_enum_value *)entry;
800*f2e05ce9SSean Rhodes label = coreboot_cfr_child_string(enum_value + 1,
801*f2e05ce9SSean Rhodes enum_value->size - sizeof(*enum_value),
802*f2e05ce9SSean Rhodes CFR_TAG_VARCHAR_UI_NAME);
803*f2e05ce9SSean Rhodes if (IS_ERR(label))
804*f2e05ce9SSean Rhodes return PTR_ERR(label);
805*f2e05ce9SSean Rhodes if (!label)
806*f2e05ce9SSean Rhodes return -EINVAL;
807*f2e05ce9SSean Rhodes
808*f2e05ce9SSean Rhodes value = &setting->values[setting->n_values];
809*f2e05ce9SSean Rhodes value->label = coreboot_cfr_string_dup(label);
810*f2e05ce9SSean Rhodes if (!value->label)
811*f2e05ce9SSean Rhodes return -ENOMEM;
812*f2e05ce9SSean Rhodes if (!coreboot_cfr_string_is_valid_label(value->label)) {
813*f2e05ce9SSean Rhodes kfree(value->label);
814*f2e05ce9SSean Rhodes value->label = NULL;
815*f2e05ce9SSean Rhodes return -EINVAL;
816*f2e05ce9SSean Rhodes }
817*f2e05ce9SSean Rhodes
818*f2e05ce9SSean Rhodes value->value = enum_value->value;
819*f2e05ce9SSean Rhodes setting->n_values++;
820*f2e05ce9SSean Rhodes }
821*f2e05ce9SSean Rhodes }
822*f2e05ce9SSean Rhodes
823*f2e05ce9SSean Rhodes static bool
coreboot_cfr_possible_values_fit(const struct coreboot_cfr_setting * setting)824*f2e05ce9SSean Rhodes coreboot_cfr_possible_values_fit(const struct coreboot_cfr_setting *setting)
825*f2e05ce9SSean Rhodes {
826*f2e05ce9SSean Rhodes size_t len = 1; /* Trailing newline. */
827*f2e05ce9SSean Rhodes size_t label_len;
828*f2e05ce9SSean Rhodes unsigned int i;
829*f2e05ce9SSean Rhodes
830*f2e05ce9SSean Rhodes for (i = 0; i < setting->n_values; i++) {
831*f2e05ce9SSean Rhodes if (len >= PAGE_SIZE)
832*f2e05ce9SSean Rhodes return false;
833*f2e05ce9SSean Rhodes
834*f2e05ce9SSean Rhodes if (i)
835*f2e05ce9SSean Rhodes len++;
836*f2e05ce9SSean Rhodes
837*f2e05ce9SSean Rhodes label_len = strlen(setting->values[i].label);
838*f2e05ce9SSean Rhodes if (label_len >= PAGE_SIZE - len)
839*f2e05ce9SSean Rhodes return false;
840*f2e05ce9SSean Rhodes
841*f2e05ce9SSean Rhodes len += label_len;
842*f2e05ce9SSean Rhodes }
843*f2e05ce9SSean Rhodes
844*f2e05ce9SSean Rhodes return true;
845*f2e05ce9SSean Rhodes }
846*f2e05ce9SSean Rhodes
coreboot_cfr_setting_is_usable(struct coreboot_cfr_setting * setting)847*f2e05ce9SSean Rhodes static int coreboot_cfr_setting_is_usable(struct coreboot_cfr_setting *setting)
848*f2e05ce9SSean Rhodes {
849*f2e05ce9SSean Rhodes u32 value;
850*f2e05ce9SSean Rhodes int ret;
851*f2e05ce9SSean Rhodes
852*f2e05ce9SSean Rhodes ret = coreboot_cfr_read_value(setting, &value, NULL);
853*f2e05ce9SSean Rhodes if (ret)
854*f2e05ce9SSean Rhodes return ret;
855*f2e05ce9SSean Rhodes
856*f2e05ce9SSean Rhodes if (!coreboot_cfr_value_is_valid(setting, value))
857*f2e05ce9SSean Rhodes return -EINVAL;
858*f2e05ce9SSean Rhodes
859*f2e05ce9SSean Rhodes return 0;
860*f2e05ce9SSean Rhodes }
861*f2e05ce9SSean Rhodes
coreboot_cfr_register_setting(struct coreboot_cfr_drvdata * data,struct coreboot_cfr_setting * setting)862*f2e05ce9SSean Rhodes static int coreboot_cfr_register_setting(struct coreboot_cfr_drvdata *data,
863*f2e05ce9SSean Rhodes struct coreboot_cfr_setting *setting)
864*f2e05ce9SSean Rhodes {
865*f2e05ce9SSean Rhodes int ret;
866*f2e05ce9SSean Rhodes
867*f2e05ce9SSean Rhodes ret = kobject_init_and_add(&setting->kobj, &coreboot_cfr_setting_ktype,
868*f2e05ce9SSean Rhodes &data->attrs_kset->kobj, "%s", setting->name);
869*f2e05ce9SSean Rhodes if (ret)
870*f2e05ce9SSean Rhodes goto err_put_kobj;
871*f2e05ce9SSean Rhodes
872*f2e05ce9SSean Rhodes ret = sysfs_create_group(&setting->kobj, &coreboot_cfr_setting_group);
873*f2e05ce9SSean Rhodes if (ret)
874*f2e05ce9SSean Rhodes goto err_put_kobj;
875*f2e05ce9SSean Rhodes
876*f2e05ce9SSean Rhodes list_add_tail(&setting->node, &data->settings);
877*f2e05ce9SSean Rhodes return 0;
878*f2e05ce9SSean Rhodes
879*f2e05ce9SSean Rhodes err_put_kobj:
880*f2e05ce9SSean Rhodes kobject_put(&setting->kobj);
881*f2e05ce9SSean Rhodes return ret;
882*f2e05ce9SSean Rhodes }
883*f2e05ce9SSean Rhodes
coreboot_cfr_add_numeric_option(struct coreboot_cfr_drvdata * data,const struct lb_cfr_numeric_option * option,bool parent_read_only)884*f2e05ce9SSean Rhodes static int coreboot_cfr_add_numeric_option(struct coreboot_cfr_drvdata *data,
885*f2e05ce9SSean Rhodes const struct lb_cfr_numeric_option *option,
886*f2e05ce9SSean Rhodes bool parent_read_only)
887*f2e05ce9SSean Rhodes {
888*f2e05ce9SSean Rhodes const struct lb_cfr_varbinary *name;
889*f2e05ce9SSean Rhodes const struct lb_cfr_varbinary *display_name;
890*f2e05ce9SSean Rhodes const struct lb_cfr_runtime_apply *runtime_apply;
891*f2e05ce9SSean Rhodes const void *child_base = option + 1;
892*f2e05ce9SSean Rhodes struct coreboot_cfr_setting *setting;
893*f2e05ce9SSean Rhodes size_t child_len = option->size - sizeof(*option);
894*f2e05ce9SSean Rhodes int ret;
895*f2e05ce9SSean Rhodes
896*f2e05ce9SSean Rhodes if (!(option->flags & CFR_OPTFLAG_RUNTIME))
897*f2e05ce9SSean Rhodes return 0;
898*f2e05ce9SSean Rhodes
899*f2e05ce9SSean Rhodes if (option->flags & COREBOOT_CFR_OPT_SKIP_FLAGS)
900*f2e05ce9SSean Rhodes return 0;
901*f2e05ce9SSean Rhodes
902*f2e05ce9SSean Rhodes if (option->dependency_id)
903*f2e05ce9SSean Rhodes return 0;
904*f2e05ce9SSean Rhodes
905*f2e05ce9SSean Rhodes setting = kzalloc_obj(*setting, GFP_KERNEL);
906*f2e05ce9SSean Rhodes if (!setting)
907*f2e05ce9SSean Rhodes return -ENOMEM;
908*f2e05ce9SSean Rhodes
909*f2e05ce9SSean Rhodes INIT_LIST_HEAD(&setting->node);
910*f2e05ce9SSean Rhodes setting->drvdata = data;
911*f2e05ce9SSean Rhodes setting->default_value = option->default_value;
912*f2e05ce9SSean Rhodes setting->min = option->min;
913*f2e05ce9SSean Rhodes setting->max = option->max;
914*f2e05ce9SSean Rhodes setting->step = option->step ?: 1;
915*f2e05ce9SSean Rhodes setting->read_only =
916*f2e05ce9SSean Rhodes (option->flags & COREBOOT_CFR_OPT_READ_ONLY_FLAGS) ||
917*f2e05ce9SSean Rhodes !data->efi_writes_supported || parent_read_only;
918*f2e05ce9SSean Rhodes
919*f2e05ce9SSean Rhodes runtime_apply = coreboot_cfr_child_runtime_apply(child_base, child_len);
920*f2e05ce9SSean Rhodes if (IS_ERR(runtime_apply)) {
921*f2e05ce9SSean Rhodes ret = PTR_ERR(runtime_apply);
922*f2e05ce9SSean Rhodes goto err_put_setting;
923*f2e05ce9SSean Rhodes }
924*f2e05ce9SSean Rhodes
925*f2e05ce9SSean Rhodes if (runtime_apply && runtime_apply->method == CFR_RUNTIME_APPLY_APM_CNT) {
926*f2e05ce9SSean Rhodes setting->runtime_apply_method = runtime_apply->method;
927*f2e05ce9SSean Rhodes setting->runtime_apply_id = runtime_apply->id;
928*f2e05ce9SSean Rhodes }
929*f2e05ce9SSean Rhodes
930*f2e05ce9SSean Rhodes name = coreboot_cfr_child_string(child_base, child_len,
931*f2e05ce9SSean Rhodes CFR_TAG_VARCHAR_OPT_NAME);
932*f2e05ce9SSean Rhodes if (IS_ERR(name)) {
933*f2e05ce9SSean Rhodes ret = PTR_ERR(name);
934*f2e05ce9SSean Rhodes goto err_put_setting;
935*f2e05ce9SSean Rhodes }
936*f2e05ce9SSean Rhodes if (!name) {
937*f2e05ce9SSean Rhodes ret = -EINVAL;
938*f2e05ce9SSean Rhodes goto err_put_setting;
939*f2e05ce9SSean Rhodes }
940*f2e05ce9SSean Rhodes
941*f2e05ce9SSean Rhodes setting->name = coreboot_cfr_string_dup(name);
942*f2e05ce9SSean Rhodes if (!setting->name) {
943*f2e05ce9SSean Rhodes ret = -ENOMEM;
944*f2e05ce9SSean Rhodes goto err_put_setting;
945*f2e05ce9SSean Rhodes }
946*f2e05ce9SSean Rhodes
947*f2e05ce9SSean Rhodes if (!coreboot_cfr_string_is_valid_name(setting->name)) {
948*f2e05ce9SSean Rhodes ret = -EINVAL;
949*f2e05ce9SSean Rhodes goto err_put_setting;
950*f2e05ce9SSean Rhodes }
951*f2e05ce9SSean Rhodes
952*f2e05ce9SSean Rhodes display_name = coreboot_cfr_child_string(child_base, child_len,
953*f2e05ce9SSean Rhodes CFR_TAG_VARCHAR_UI_NAME);
954*f2e05ce9SSean Rhodes if (IS_ERR(display_name)) {
955*f2e05ce9SSean Rhodes ret = PTR_ERR(display_name);
956*f2e05ce9SSean Rhodes goto err_put_setting;
957*f2e05ce9SSean Rhodes }
958*f2e05ce9SSean Rhodes if (display_name)
959*f2e05ce9SSean Rhodes setting->display_name = coreboot_cfr_string_dup(display_name);
960*f2e05ce9SSean Rhodes else
961*f2e05ce9SSean Rhodes setting->display_name = kstrdup(setting->name, GFP_KERNEL);
962*f2e05ce9SSean Rhodes if (!setting->display_name) {
963*f2e05ce9SSean Rhodes ret = -ENOMEM;
964*f2e05ce9SSean Rhodes goto err_put_setting;
965*f2e05ce9SSean Rhodes }
966*f2e05ce9SSean Rhodes
967*f2e05ce9SSean Rhodes switch (option->tag) {
968*f2e05ce9SSean Rhodes case CFR_TAG_OPTION_BOOL:
969*f2e05ce9SSean Rhodes setting->type = COREBOOT_CFR_SETTING_BOOL;
970*f2e05ce9SSean Rhodes setting->min = 0;
971*f2e05ce9SSean Rhodes setting->max = 1;
972*f2e05ce9SSean Rhodes setting->step = 1;
973*f2e05ce9SSean Rhodes ret = coreboot_cfr_copy_bool_values(setting);
974*f2e05ce9SSean Rhodes break;
975*f2e05ce9SSean Rhodes case CFR_TAG_OPTION_ENUM:
976*f2e05ce9SSean Rhodes setting->type = COREBOOT_CFR_SETTING_ENUM;
977*f2e05ce9SSean Rhodes ret = coreboot_cfr_copy_enum_values(setting, child_base,
978*f2e05ce9SSean Rhodes child_len);
979*f2e05ce9SSean Rhodes break;
980*f2e05ce9SSean Rhodes case CFR_TAG_OPTION_NUMBER:
981*f2e05ce9SSean Rhodes setting->type = COREBOOT_CFR_SETTING_NUMBER;
982*f2e05ce9SSean Rhodes if (setting->max < setting->min)
983*f2e05ce9SSean Rhodes ret = -EINVAL;
984*f2e05ce9SSean Rhodes else
985*f2e05ce9SSean Rhodes ret = 0;
986*f2e05ce9SSean Rhodes break;
987*f2e05ce9SSean Rhodes default:
988*f2e05ce9SSean Rhodes ret = -EINVAL;
989*f2e05ce9SSean Rhodes break;
990*f2e05ce9SSean Rhodes }
991*f2e05ce9SSean Rhodes
992*f2e05ce9SSean Rhodes if (ret)
993*f2e05ce9SSean Rhodes goto err_put_setting;
994*f2e05ce9SSean Rhodes /* possible_values must be returned completely in one sysfs read. */
995*f2e05ce9SSean Rhodes if (setting->type != COREBOOT_CFR_SETTING_NUMBER &&
996*f2e05ce9SSean Rhodes !coreboot_cfr_possible_values_fit(setting)) {
997*f2e05ce9SSean Rhodes ret = 0;
998*f2e05ce9SSean Rhodes goto err_put_setting;
999*f2e05ce9SSean Rhodes }
1000*f2e05ce9SSean Rhodes
1001*f2e05ce9SSean Rhodes ret = coreboot_cfr_setting_is_usable(setting);
1002*f2e05ce9SSean Rhodes if (ret) {
1003*f2e05ce9SSean Rhodes /*
1004*f2e05ce9SSean Rhodes * CFR may describe options without a matching, readable runtime
1005*f2e05ce9SSean Rhodes * EFI variable. Skip those, but propagate transient EFI failures.
1006*f2e05ce9SSean Rhodes */
1007*f2e05ce9SSean Rhodes if (ret == -ENOENT || ret == -EINVAL || ret == -EOPNOTSUPP ||
1008*f2e05ce9SSean Rhodes ret == -ENAMETOOLONG)
1009*f2e05ce9SSean Rhodes ret = 0;
1010*f2e05ce9SSean Rhodes goto err_put_setting;
1011*f2e05ce9SSean Rhodes }
1012*f2e05ce9SSean Rhodes
1013*f2e05ce9SSean Rhodes /* The kobject release callback owns setting after this call. */
1014*f2e05ce9SSean Rhodes return coreboot_cfr_register_setting(data, setting);
1015*f2e05ce9SSean Rhodes
1016*f2e05ce9SSean Rhodes err_put_setting:
1017*f2e05ce9SSean Rhodes coreboot_cfr_free_setting(setting);
1018*f2e05ce9SSean Rhodes return ret;
1019*f2e05ce9SSean Rhodes }
1020*f2e05ce9SSean Rhodes
coreboot_cfr_parse_records(struct coreboot_cfr_drvdata * data,const void * base,size_t len,unsigned int depth,bool parent_read_only)1021*f2e05ce9SSean Rhodes static int coreboot_cfr_parse_records(struct coreboot_cfr_drvdata *data,
1022*f2e05ce9SSean Rhodes const void *base, size_t len,
1023*f2e05ce9SSean Rhodes unsigned int depth,
1024*f2e05ce9SSean Rhodes bool parent_read_only)
1025*f2e05ce9SSean Rhodes {
1026*f2e05ce9SSean Rhodes struct coreboot_cfr_iterator iterator = {
1027*f2e05ce9SSean Rhodes .cursor = base,
1028*f2e05ce9SSean Rhodes .remaining = len,
1029*f2e05ce9SSean Rhodes };
1030*f2e05ce9SSean Rhodes const struct lb_cfr_option_form *form;
1031*f2e05ce9SSean Rhodes const struct lb_cfr_numeric_option *option;
1032*f2e05ce9SSean Rhodes const struct coreboot_table_entry *entry;
1033*f2e05ce9SSean Rhodes const void *child_base;
1034*f2e05ce9SSean Rhodes size_t child_len;
1035*f2e05ce9SSean Rhodes int ret;
1036*f2e05ce9SSean Rhodes
1037*f2e05ce9SSean Rhodes for (;;) {
1038*f2e05ce9SSean Rhodes entry = coreboot_cfr_next_entry(&iterator);
1039*f2e05ce9SSean Rhodes if (IS_ERR(entry))
1040*f2e05ce9SSean Rhodes return PTR_ERR(entry);
1041*f2e05ce9SSean Rhodes if (!entry)
1042*f2e05ce9SSean Rhodes return 0;
1043*f2e05ce9SSean Rhodes
1044*f2e05ce9SSean Rhodes switch (entry->tag) {
1045*f2e05ce9SSean Rhodes case CFR_TAG_OPTION_FORM:
1046*f2e05ce9SSean Rhodes if (entry->size < sizeof(struct lb_cfr_option_form))
1047*f2e05ce9SSean Rhodes return -EINVAL;
1048*f2e05ce9SSean Rhodes
1049*f2e05ce9SSean Rhodes form = (const struct lb_cfr_option_form *)entry;
1050*f2e05ce9SSean Rhodes if (form->dependency_id ||
1051*f2e05ce9SSean Rhodes form->flags & CFR_OPTFLAG_SUPPRESS)
1052*f2e05ce9SSean Rhodes break;
1053*f2e05ce9SSean Rhodes
1054*f2e05ce9SSean Rhodes if (depth >= COREBOOT_CFR_MAX_FORM_DEPTH)
1055*f2e05ce9SSean Rhodes return -E2BIG;
1056*f2e05ce9SSean Rhodes
1057*f2e05ce9SSean Rhodes child_base = form + 1;
1058*f2e05ce9SSean Rhodes child_len = entry->size - sizeof(struct lb_cfr_option_form);
1059*f2e05ce9SSean Rhodes ret = coreboot_cfr_parse_records(data, child_base,
1060*f2e05ce9SSean Rhodes child_len, depth + 1,
1061*f2e05ce9SSean Rhodes parent_read_only ||
1062*f2e05ce9SSean Rhodes (form->flags &
1063*f2e05ce9SSean Rhodes COREBOOT_CFR_OPT_READ_ONLY_FLAGS));
1064*f2e05ce9SSean Rhodes if (ret)
1065*f2e05ce9SSean Rhodes return ret;
1066*f2e05ce9SSean Rhodes break;
1067*f2e05ce9SSean Rhodes case CFR_TAG_OPTION_ENUM:
1068*f2e05ce9SSean Rhodes case CFR_TAG_OPTION_NUMBER:
1069*f2e05ce9SSean Rhodes case CFR_TAG_OPTION_BOOL:
1070*f2e05ce9SSean Rhodes option = (const struct lb_cfr_numeric_option *)entry;
1071*f2e05ce9SSean Rhodes if (entry->size < sizeof(*option))
1072*f2e05ce9SSean Rhodes return -EINVAL;
1073*f2e05ce9SSean Rhodes ret = coreboot_cfr_add_numeric_option(data, option,
1074*f2e05ce9SSean Rhodes parent_read_only);
1075*f2e05ce9SSean Rhodes if (ret)
1076*f2e05ce9SSean Rhodes return ret;
1077*f2e05ce9SSean Rhodes break;
1078*f2e05ce9SSean Rhodes default:
1079*f2e05ce9SSean Rhodes /* Ignore unsupported, child-only and future record types. */
1080*f2e05ce9SSean Rhodes break;
1081*f2e05ce9SSean Rhodes }
1082*f2e05ce9SSean Rhodes }
1083*f2e05ce9SSean Rhodes }
1084*f2e05ce9SSean Rhodes
coreboot_cfr_unregister_settings(struct coreboot_cfr_drvdata * data)1085*f2e05ce9SSean Rhodes static void coreboot_cfr_unregister_settings(struct coreboot_cfr_drvdata *data)
1086*f2e05ce9SSean Rhodes {
1087*f2e05ce9SSean Rhodes struct coreboot_cfr_setting *setting, *tmp;
1088*f2e05ce9SSean Rhodes
1089*f2e05ce9SSean Rhodes list_for_each_entry_safe(setting, tmp, &data->settings, node) {
1090*f2e05ce9SSean Rhodes sysfs_remove_group(&setting->kobj, &coreboot_cfr_setting_group);
1091*f2e05ce9SSean Rhodes list_del(&setting->node);
1092*f2e05ce9SSean Rhodes kobject_put(&setting->kobj);
1093*f2e05ce9SSean Rhodes }
1094*f2e05ce9SSean Rhodes }
1095*f2e05ce9SSean Rhodes
coreboot_cfr_probe(struct coreboot_device * dev)1096*f2e05ce9SSean Rhodes static int coreboot_cfr_probe(struct coreboot_device *dev)
1097*f2e05ce9SSean Rhodes {
1098*f2e05ce9SSean Rhodes const struct lb_cfr *root = (const struct lb_cfr *)dev->raw;
1099*f2e05ce9SSean Rhodes struct coreboot_cfr_drvdata *data;
1100*f2e05ce9SSean Rhodes size_t payload_len;
1101*f2e05ce9SSean Rhodes int ret;
1102*f2e05ce9SSean Rhodes
1103*f2e05ce9SSean Rhodes if (dev->entry.size < sizeof(*root))
1104*f2e05ce9SSean Rhodes return -EINVAL;
1105*f2e05ce9SSean Rhodes
1106*f2e05ce9SSean Rhodes if (root->tag != LB_TAG_CFR_ROOT || root->version != CFR_VERSION)
1107*f2e05ce9SSean Rhodes return -EINVAL;
1108*f2e05ce9SSean Rhodes
1109*f2e05ce9SSean Rhodes if (root->size < sizeof(*root) || root->size > dev->entry.size)
1110*f2e05ce9SSean Rhodes return -EINVAL;
1111*f2e05ce9SSean Rhodes
1112*f2e05ce9SSean Rhodes payload_len = root->size - sizeof(*root);
1113*f2e05ce9SSean Rhodes if (crc32_be(0, root + 1, payload_len) != root->checksum)
1114*f2e05ce9SSean Rhodes return -EBADMSG;
1115*f2e05ce9SSean Rhodes
1116*f2e05ce9SSean Rhodes if (!efivar_is_available())
1117*f2e05ce9SSean Rhodes return -EPROBE_DEFER;
1118*f2e05ce9SSean Rhodes
1119*f2e05ce9SSean Rhodes data = devm_kzalloc(&dev->dev, sizeof(*data), GFP_KERNEL);
1120*f2e05ce9SSean Rhodes if (!data)
1121*f2e05ce9SSean Rhodes return -ENOMEM;
1122*f2e05ce9SSean Rhodes
1123*f2e05ce9SSean Rhodes ret = efivar_lock();
1124*f2e05ce9SSean Rhodes if (ret)
1125*f2e05ce9SSean Rhodes return ret;
1126*f2e05ce9SSean Rhodes data->efi_writes_supported = efivar_supports_writes();
1127*f2e05ce9SSean Rhodes efivar_unlock();
1128*f2e05ce9SSean Rhodes
1129*f2e05ce9SSean Rhodes INIT_LIST_HEAD(&data->settings);
1130*f2e05ce9SSean Rhodes ret = devm_mutex_init(&dev->dev, &data->lock);
1131*f2e05ce9SSean Rhodes if (ret)
1132*f2e05ce9SSean Rhodes return ret;
1133*f2e05ce9SSean Rhodes
1134*f2e05ce9SSean Rhodes dev_set_drvdata(&dev->dev, data);
1135*f2e05ce9SSean Rhodes
1136*f2e05ce9SSean Rhodes data->class_dev = device_create(&firmware_attributes_class, NULL,
1137*f2e05ce9SSean Rhodes MKDEV(0, 0), NULL, DRIVER_NAME);
1138*f2e05ce9SSean Rhodes if (IS_ERR(data->class_dev))
1139*f2e05ce9SSean Rhodes return PTR_ERR(data->class_dev);
1140*f2e05ce9SSean Rhodes dev_set_drvdata(data->class_dev, data);
1141*f2e05ce9SSean Rhodes
1142*f2e05ce9SSean Rhodes data->attrs_kset = kset_create_and_add("attributes", NULL,
1143*f2e05ce9SSean Rhodes &data->class_dev->kobj);
1144*f2e05ce9SSean Rhodes if (!data->attrs_kset) {
1145*f2e05ce9SSean Rhodes ret = -ENOMEM;
1146*f2e05ce9SSean Rhodes goto err_unregister_dev;
1147*f2e05ce9SSean Rhodes }
1148*f2e05ce9SSean Rhodes
1149*f2e05ce9SSean Rhodes ret = sysfs_create_file(&data->attrs_kset->kobj,
1150*f2e05ce9SSean Rhodes &pending_reboot_attr.attr);
1151*f2e05ce9SSean Rhodes if (ret)
1152*f2e05ce9SSean Rhodes goto err_unregister_attrs;
1153*f2e05ce9SSean Rhodes
1154*f2e05ce9SSean Rhodes ret = coreboot_cfr_parse_records(data, root + 1, payload_len, 0, false);
1155*f2e05ce9SSean Rhodes if (ret)
1156*f2e05ce9SSean Rhodes goto err_unregister_settings;
1157*f2e05ce9SSean Rhodes
1158*f2e05ce9SSean Rhodes if (list_empty(&data->settings)) {
1159*f2e05ce9SSean Rhodes ret = -ENODEV;
1160*f2e05ce9SSean Rhodes goto err_unregister_settings;
1161*f2e05ce9SSean Rhodes }
1162*f2e05ce9SSean Rhodes
1163*f2e05ce9SSean Rhodes return 0;
1164*f2e05ce9SSean Rhodes
1165*f2e05ce9SSean Rhodes err_unregister_settings:
1166*f2e05ce9SSean Rhodes coreboot_cfr_unregister_settings(data);
1167*f2e05ce9SSean Rhodes sysfs_remove_file(&data->attrs_kset->kobj, &pending_reboot_attr.attr);
1168*f2e05ce9SSean Rhodes err_unregister_attrs:
1169*f2e05ce9SSean Rhodes kset_unregister(data->attrs_kset);
1170*f2e05ce9SSean Rhodes err_unregister_dev:
1171*f2e05ce9SSean Rhodes device_unregister(data->class_dev);
1172*f2e05ce9SSean Rhodes return ret;
1173*f2e05ce9SSean Rhodes }
1174*f2e05ce9SSean Rhodes
coreboot_cfr_remove(struct coreboot_device * dev)1175*f2e05ce9SSean Rhodes static void coreboot_cfr_remove(struct coreboot_device *dev)
1176*f2e05ce9SSean Rhodes {
1177*f2e05ce9SSean Rhodes struct coreboot_cfr_drvdata *data = dev_get_drvdata(&dev->dev);
1178*f2e05ce9SSean Rhodes
1179*f2e05ce9SSean Rhodes coreboot_cfr_unregister_settings(data);
1180*f2e05ce9SSean Rhodes sysfs_remove_file(&data->attrs_kset->kobj, &pending_reboot_attr.attr);
1181*f2e05ce9SSean Rhodes kset_unregister(data->attrs_kset);
1182*f2e05ce9SSean Rhodes device_unregister(data->class_dev);
1183*f2e05ce9SSean Rhodes }
1184*f2e05ce9SSean Rhodes
1185*f2e05ce9SSean Rhodes static const struct coreboot_device_id coreboot_cfr_ids[] = {
1186*f2e05ce9SSean Rhodes { .tag = LB_TAG_CFR_ROOT },
1187*f2e05ce9SSean Rhodes { }
1188*f2e05ce9SSean Rhodes };
1189*f2e05ce9SSean Rhodes MODULE_DEVICE_TABLE(coreboot, coreboot_cfr_ids);
1190*f2e05ce9SSean Rhodes
1191*f2e05ce9SSean Rhodes static struct coreboot_driver coreboot_cfr_driver = {
1192*f2e05ce9SSean Rhodes .probe = coreboot_cfr_probe,
1193*f2e05ce9SSean Rhodes .remove = coreboot_cfr_remove,
1194*f2e05ce9SSean Rhodes .drv = {
1195*f2e05ce9SSean Rhodes .name = DRIVER_NAME,
1196*f2e05ce9SSean Rhodes },
1197*f2e05ce9SSean Rhodes .id_table = coreboot_cfr_ids,
1198*f2e05ce9SSean Rhodes };
1199*f2e05ce9SSean Rhodes module_coreboot_driver(coreboot_cfr_driver);
1200*f2e05ce9SSean Rhodes
1201*f2e05ce9SSean Rhodes MODULE_AUTHOR("Sean Rhodes <sean@starlabs.systems>");
1202*f2e05ce9SSean Rhodes MODULE_DESCRIPTION("coreboot CFR firmware attributes driver");
1203*f2e05ce9SSean Rhodes MODULE_LICENSE("GPL");
1204*f2e05ce9SSean Rhodes MODULE_IMPORT_NS("EFIVAR");
1205