xref: /linux/drivers/firmware/coreboot/coreboot-cfr.c (revision 9e947d96915af63e28896caae85a81b74855621b)
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 	&current_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 == &current_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