xref: /OK3568_Linux_fs/kernel/drivers/platform/mellanox/mlxbf-bootctl.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Mellanox boot control driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * This driver provides a sysfs interface for systems management
6*4882a593Smuzhiyun  * software to manage reset-time actions.
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * Copyright (C) 2019 Mellanox Technologies
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include <linux/acpi.h>
12*4882a593Smuzhiyun #include <linux/arm-smccc.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/platform_device.h>
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include "mlxbf-bootctl.h"
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #define MLXBF_BOOTCTL_SB_SECURE_MASK		0x03
19*4882a593Smuzhiyun #define MLXBF_BOOTCTL_SB_TEST_MASK		0x0c
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #define MLXBF_SB_KEY_NUM			4
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun /* UUID used to probe ATF service. */
24*4882a593Smuzhiyun static const char *mlxbf_bootctl_svc_uuid_str =
25*4882a593Smuzhiyun 	"89c036b4-e7d7-11e6-8797-001aca00bfc4";
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun struct mlxbf_bootctl_name {
28*4882a593Smuzhiyun 	u32 value;
29*4882a593Smuzhiyun 	const char *name;
30*4882a593Smuzhiyun };
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun static struct mlxbf_bootctl_name boot_names[] = {
33*4882a593Smuzhiyun 	{ MLXBF_BOOTCTL_EXTERNAL, "external" },
34*4882a593Smuzhiyun 	{ MLXBF_BOOTCTL_EMMC, "emmc" },
35*4882a593Smuzhiyun 	{ MLNX_BOOTCTL_SWAP_EMMC, "swap_emmc" },
36*4882a593Smuzhiyun 	{ MLXBF_BOOTCTL_EMMC_LEGACY, "emmc_legacy" },
37*4882a593Smuzhiyun 	{ MLXBF_BOOTCTL_NONE, "none" },
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun static const char * const mlxbf_bootctl_lifecycle_states[] = {
41*4882a593Smuzhiyun 	[0] = "Production",
42*4882a593Smuzhiyun 	[1] = "GA Secured",
43*4882a593Smuzhiyun 	[2] = "GA Non-Secured",
44*4882a593Smuzhiyun 	[3] = "RMA",
45*4882a593Smuzhiyun };
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun /* ARM SMC call which is atomic and no need for lock. */
mlxbf_bootctl_smc(unsigned int smc_op,int smc_arg)48*4882a593Smuzhiyun static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun 	struct arm_smccc_res res;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res);
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun 	return res.a0;
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun /* Return the action in integer or an error code. */
mlxbf_bootctl_reset_action_to_val(const char * action)58*4882a593Smuzhiyun static int mlxbf_bootctl_reset_action_to_val(const char *action)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun 	int i;
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
63*4882a593Smuzhiyun 		if (sysfs_streq(boot_names[i].name, action))
64*4882a593Smuzhiyun 			return boot_names[i].value;
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	return -EINVAL;
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun /* Return the action in string. */
mlxbf_bootctl_action_to_string(int action)70*4882a593Smuzhiyun static const char *mlxbf_bootctl_action_to_string(int action)
71*4882a593Smuzhiyun {
72*4882a593Smuzhiyun 	int i;
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
75*4882a593Smuzhiyun 		if (boot_names[i].value == action)
76*4882a593Smuzhiyun 			return boot_names[i].name;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	return "invalid action";
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun 
post_reset_wdog_show(struct device * dev,struct device_attribute * attr,char * buf)81*4882a593Smuzhiyun static ssize_t post_reset_wdog_show(struct device *dev,
82*4882a593Smuzhiyun 				    struct device_attribute *attr, char *buf)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun 	int ret;
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_POST_RESET_WDOG, 0);
87*4882a593Smuzhiyun 	if (ret < 0)
88*4882a593Smuzhiyun 		return ret;
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	return sprintf(buf, "%d\n", ret);
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun 
post_reset_wdog_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)93*4882a593Smuzhiyun static ssize_t post_reset_wdog_store(struct device *dev,
94*4882a593Smuzhiyun 				     struct device_attribute *attr,
95*4882a593Smuzhiyun 				     const char *buf, size_t count)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun 	unsigned long value;
98*4882a593Smuzhiyun 	int ret;
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	ret = kstrtoul(buf, 10, &value);
101*4882a593Smuzhiyun 	if (ret)
102*4882a593Smuzhiyun 		return ret;
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_POST_RESET_WDOG, value);
105*4882a593Smuzhiyun 	if (ret < 0)
106*4882a593Smuzhiyun 		return ret;
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	return count;
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun 
mlxbf_bootctl_show(int smc_op,char * buf)111*4882a593Smuzhiyun static ssize_t mlxbf_bootctl_show(int smc_op, char *buf)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun 	int action;
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	action = mlxbf_bootctl_smc(smc_op, 0);
116*4882a593Smuzhiyun 	if (action < 0)
117*4882a593Smuzhiyun 		return action;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action));
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun 
mlxbf_bootctl_store(int smc_op,const char * buf,size_t count)122*4882a593Smuzhiyun static int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun 	int ret, action;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	action = mlxbf_bootctl_reset_action_to_val(buf);
127*4882a593Smuzhiyun 	if (action < 0)
128*4882a593Smuzhiyun 		return action;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	ret = mlxbf_bootctl_smc(smc_op, action);
131*4882a593Smuzhiyun 	if (ret < 0)
132*4882a593Smuzhiyun 		return ret;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	return count;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun 
reset_action_show(struct device * dev,struct device_attribute * attr,char * buf)137*4882a593Smuzhiyun static ssize_t reset_action_show(struct device *dev,
138*4882a593Smuzhiyun 				 struct device_attribute *attr, char *buf)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun 	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_RESET_ACTION, buf);
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun 
reset_action_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)143*4882a593Smuzhiyun static ssize_t reset_action_store(struct device *dev,
144*4882a593Smuzhiyun 				  struct device_attribute *attr,
145*4882a593Smuzhiyun 				  const char *buf, size_t count)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun 	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_RESET_ACTION, buf, count);
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun 
second_reset_action_show(struct device * dev,struct device_attribute * attr,char * buf)150*4882a593Smuzhiyun static ssize_t second_reset_action_show(struct device *dev,
151*4882a593Smuzhiyun 					struct device_attribute *attr,
152*4882a593Smuzhiyun 					char *buf)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun 	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION, buf);
155*4882a593Smuzhiyun }
156*4882a593Smuzhiyun 
second_reset_action_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)157*4882a593Smuzhiyun static ssize_t second_reset_action_store(struct device *dev,
158*4882a593Smuzhiyun 					 struct device_attribute *attr,
159*4882a593Smuzhiyun 					 const char *buf, size_t count)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun 	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION, buf,
162*4882a593Smuzhiyun 				   count);
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun 
lifecycle_state_show(struct device * dev,struct device_attribute * attr,char * buf)165*4882a593Smuzhiyun static ssize_t lifecycle_state_show(struct device *dev,
166*4882a593Smuzhiyun 				    struct device_attribute *attr, char *buf)
167*4882a593Smuzhiyun {
168*4882a593Smuzhiyun 	int lc_state;
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	lc_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
171*4882a593Smuzhiyun 				     MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
172*4882a593Smuzhiyun 	if (lc_state < 0)
173*4882a593Smuzhiyun 		return lc_state;
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	lc_state &=
176*4882a593Smuzhiyun 		MLXBF_BOOTCTL_SB_TEST_MASK | MLXBF_BOOTCTL_SB_SECURE_MASK;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	/*
179*4882a593Smuzhiyun 	 * If the test bits are set, we specify that the current state may be
180*4882a593Smuzhiyun 	 * due to using the test bits.
181*4882a593Smuzhiyun 	 */
182*4882a593Smuzhiyun 	if (lc_state & MLXBF_BOOTCTL_SB_TEST_MASK) {
183*4882a593Smuzhiyun 		lc_state &= MLXBF_BOOTCTL_SB_SECURE_MASK;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 		return sprintf(buf, "%s(test)\n",
186*4882a593Smuzhiyun 			       mlxbf_bootctl_lifecycle_states[lc_state]);
187*4882a593Smuzhiyun 	}
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun 
secure_boot_fuse_state_show(struct device * dev,struct device_attribute * attr,char * buf)192*4882a593Smuzhiyun static ssize_t secure_boot_fuse_state_show(struct device *dev,
193*4882a593Smuzhiyun 					   struct device_attribute *attr,
194*4882a593Smuzhiyun 					   char *buf)
195*4882a593Smuzhiyun {
196*4882a593Smuzhiyun 	int burnt, valid, key, key_state, buf_len = 0, upper_key_used = 0;
197*4882a593Smuzhiyun 	const char *status;
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	key_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
200*4882a593Smuzhiyun 				      MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
201*4882a593Smuzhiyun 	if (key_state < 0)
202*4882a593Smuzhiyun 		return key_state;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	/*
205*4882a593Smuzhiyun 	 * key_state contains the bits for 4 Key versions, loaded from eFuses
206*4882a593Smuzhiyun 	 * after a hard reset. Lower 4 bits are a thermometer code indicating
207*4882a593Smuzhiyun 	 * key programming has started for key n (0000 = none, 0001 = version 0,
208*4882a593Smuzhiyun 	 * 0011 = version 1, 0111 = version 2, 1111 = version 3). Upper 4 bits
209*4882a593Smuzhiyun 	 * are a thermometer code indicating key programming has completed for
210*4882a593Smuzhiyun 	 * key n (same encodings as the start bits). This allows for detection
211*4882a593Smuzhiyun 	 * of an interruption in the progamming process which has left the key
212*4882a593Smuzhiyun 	 * partially programmed (and thus invalid). The process is to burn the
213*4882a593Smuzhiyun 	 * eFuse for the new key start bit, burn the key eFuses, then burn the
214*4882a593Smuzhiyun 	 * eFuse for the new key complete bit.
215*4882a593Smuzhiyun 	 *
216*4882a593Smuzhiyun 	 * For example 0000_0000: no key valid, 0001_0001: key version 0 valid,
217*4882a593Smuzhiyun 	 * 0011_0011: key 1 version valid, 0011_0111: key version 2 started
218*4882a593Smuzhiyun 	 * programming but did not complete, etc. The most recent key for which
219*4882a593Smuzhiyun 	 * both start and complete bit is set is loaded. On soft reset, this
220*4882a593Smuzhiyun 	 * register is not modified.
221*4882a593Smuzhiyun 	 */
222*4882a593Smuzhiyun 	for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
223*4882a593Smuzhiyun 		burnt = key_state & BIT(key);
224*4882a593Smuzhiyun 		valid = key_state & BIT(key + MLXBF_SB_KEY_NUM);
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 		if (burnt && valid)
227*4882a593Smuzhiyun 			upper_key_used = 1;
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 		if (upper_key_used) {
230*4882a593Smuzhiyun 			if (burnt)
231*4882a593Smuzhiyun 				status = valid ? "Used" : "Wasted";
232*4882a593Smuzhiyun 			else
233*4882a593Smuzhiyun 				status = valid ? "Invalid" : "Skipped";
234*4882a593Smuzhiyun 		} else {
235*4882a593Smuzhiyun 			if (burnt)
236*4882a593Smuzhiyun 				status = valid ? "InUse" : "Incomplete";
237*4882a593Smuzhiyun 			else
238*4882a593Smuzhiyun 				status = valid ? "Invalid" : "Free";
239*4882a593Smuzhiyun 		}
240*4882a593Smuzhiyun 		buf_len += sprintf(buf + buf_len, "%d:%s ", key, status);
241*4882a593Smuzhiyun 	}
242*4882a593Smuzhiyun 	buf_len += sprintf(buf + buf_len, "\n");
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	return buf_len;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun static DEVICE_ATTR_RW(post_reset_wdog);
248*4882a593Smuzhiyun static DEVICE_ATTR_RW(reset_action);
249*4882a593Smuzhiyun static DEVICE_ATTR_RW(second_reset_action);
250*4882a593Smuzhiyun static DEVICE_ATTR_RO(lifecycle_state);
251*4882a593Smuzhiyun static DEVICE_ATTR_RO(secure_boot_fuse_state);
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun static struct attribute *mlxbf_bootctl_attrs[] = {
254*4882a593Smuzhiyun 	&dev_attr_post_reset_wdog.attr,
255*4882a593Smuzhiyun 	&dev_attr_reset_action.attr,
256*4882a593Smuzhiyun 	&dev_attr_second_reset_action.attr,
257*4882a593Smuzhiyun 	&dev_attr_lifecycle_state.attr,
258*4882a593Smuzhiyun 	&dev_attr_secure_boot_fuse_state.attr,
259*4882a593Smuzhiyun 	NULL
260*4882a593Smuzhiyun };
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun ATTRIBUTE_GROUPS(mlxbf_bootctl);
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
265*4882a593Smuzhiyun 	{"MLNXBF04", 0},
266*4882a593Smuzhiyun 	{}
267*4882a593Smuzhiyun };
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
270*4882a593Smuzhiyun 
mlxbf_bootctl_guid_match(const guid_t * guid,const struct arm_smccc_res * res)271*4882a593Smuzhiyun static bool mlxbf_bootctl_guid_match(const guid_t *guid,
272*4882a593Smuzhiyun 				     const struct arm_smccc_res *res)
273*4882a593Smuzhiyun {
274*4882a593Smuzhiyun 	guid_t id = GUID_INIT(res->a0, res->a1, res->a1 >> 16,
275*4882a593Smuzhiyun 			      res->a2, res->a2 >> 8, res->a2 >> 16,
276*4882a593Smuzhiyun 			      res->a2 >> 24, res->a3, res->a3 >> 8,
277*4882a593Smuzhiyun 			      res->a3 >> 16, res->a3 >> 24);
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	return guid_equal(guid, &id);
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun 
mlxbf_bootctl_probe(struct platform_device * pdev)282*4882a593Smuzhiyun static int mlxbf_bootctl_probe(struct platform_device *pdev)
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun 	struct arm_smccc_res res = { 0 };
285*4882a593Smuzhiyun 	guid_t guid;
286*4882a593Smuzhiyun 	int ret;
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	/* Ensure we have the UUID we expect for this service. */
289*4882a593Smuzhiyun 	arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res);
290*4882a593Smuzhiyun 	guid_parse(mlxbf_bootctl_svc_uuid_str, &guid);
291*4882a593Smuzhiyun 	if (!mlxbf_bootctl_guid_match(&guid, &res))
292*4882a593Smuzhiyun 		return -ENODEV;
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	/*
295*4882a593Smuzhiyun 	 * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_SWAP_EMMC
296*4882a593Smuzhiyun 	 * in case of boot failures. However it doesn't clear the state if there
297*4882a593Smuzhiyun 	 * is no failure. Restore the default boot mode here to avoid any
298*4882a593Smuzhiyun 	 * unnecessary boot partition swapping.
299*4882a593Smuzhiyun 	 */
300*4882a593Smuzhiyun 	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_RESET_ACTION,
301*4882a593Smuzhiyun 				MLXBF_BOOTCTL_EMMC);
302*4882a593Smuzhiyun 	if (ret < 0)
303*4882a593Smuzhiyun 		dev_warn(&pdev->dev, "Unable to reset the EMMC boot mode\n");
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	return 0;
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun static struct platform_driver mlxbf_bootctl_driver = {
309*4882a593Smuzhiyun 	.probe = mlxbf_bootctl_probe,
310*4882a593Smuzhiyun 	.driver = {
311*4882a593Smuzhiyun 		.name = "mlxbf-bootctl",
312*4882a593Smuzhiyun 		.dev_groups = mlxbf_bootctl_groups,
313*4882a593Smuzhiyun 		.acpi_match_table = mlxbf_bootctl_acpi_ids,
314*4882a593Smuzhiyun 	}
315*4882a593Smuzhiyun };
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun module_platform_driver(mlxbf_bootctl_driver);
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun MODULE_DESCRIPTION("Mellanox boot control driver");
320*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
321*4882a593Smuzhiyun MODULE_AUTHOR("Mellanox Technologies");
322