1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Mellanox boot control driver |
4 | * |
5 | * This driver provides a sysfs interface for systems management |
6 | * software to manage reset-time actions. |
7 | * |
8 | * Copyright (C) 2019 Mellanox Technologies |
9 | */ |
10 | |
11 | #include <linux/acpi.h> |
12 | #include <linux/arm-smccc.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/if_ether.h> |
15 | #include <linux/iopoll.h> |
16 | #include <linux/module.h> |
17 | #include <linux/platform_device.h> |
18 | |
19 | #include "mlxbf-bootctl.h" |
20 | |
21 | #define MLXBF_BOOTCTL_SB_SECURE_MASK 0x03 |
22 | #define MLXBF_BOOTCTL_SB_TEST_MASK 0x0c |
23 | #define MLXBF_BOOTCTL_SB_DEV_MASK BIT(4) |
24 | |
25 | #define MLXBF_SB_KEY_NUM 4 |
26 | |
27 | /* UUID used to probe ATF service. */ |
28 | static const char *mlxbf_bootctl_svc_uuid_str = |
29 | "89c036b4-e7d7-11e6-8797-001aca00bfc4" ; |
30 | |
31 | struct mlxbf_bootctl_name { |
32 | u32 value; |
33 | const char *name; |
34 | }; |
35 | |
36 | static struct mlxbf_bootctl_name boot_names[] = { |
37 | { MLXBF_BOOTCTL_EXTERNAL, "external" }, |
38 | { MLXBF_BOOTCTL_EMMC, "emmc" }, |
39 | { MLNX_BOOTCTL_SWAP_EMMC, "swap_emmc" }, |
40 | { MLXBF_BOOTCTL_EMMC_LEGACY, "emmc_legacy" }, |
41 | { MLXBF_BOOTCTL_NONE, "none" }, |
42 | }; |
43 | |
44 | enum { |
45 | MLXBF_BOOTCTL_SB_LIFECYCLE_PRODUCTION = 0, |
46 | MLXBF_BOOTCTL_SB_LIFECYCLE_GA_SECURE = 1, |
47 | MLXBF_BOOTCTL_SB_LIFECYCLE_GA_NON_SECURE = 2, |
48 | MLXBF_BOOTCTL_SB_LIFECYCLE_RMA = 3 |
49 | }; |
50 | |
51 | static const char * const mlxbf_bootctl_lifecycle_states[] = { |
52 | [MLXBF_BOOTCTL_SB_LIFECYCLE_PRODUCTION] = "Production" , |
53 | [MLXBF_BOOTCTL_SB_LIFECYCLE_GA_SECURE] = "GA Secured" , |
54 | [MLXBF_BOOTCTL_SB_LIFECYCLE_GA_NON_SECURE] = "GA Non-Secured" , |
55 | [MLXBF_BOOTCTL_SB_LIFECYCLE_RMA] = "RMA" , |
56 | }; |
57 | |
58 | /* Log header format. */ |
59 | #define MLXBF_RSH_LOG_TYPE_MASK GENMASK_ULL(59, 56) |
60 | #define MLXBF_RSH_LOG_LEN_MASK GENMASK_ULL(54, 48) |
61 | #define MLXBF_RSH_LOG_LEVEL_MASK GENMASK_ULL(7, 0) |
62 | |
63 | /* Log module ID and type (only MSG type in Linux driver for now). */ |
64 | #define MLXBF_RSH_LOG_TYPE_MSG 0x04ULL |
65 | |
66 | /* Log ctl/data register offset. */ |
67 | #define MLXBF_RSH_SCRATCH_BUF_CTL_OFF 0 |
68 | #define MLXBF_RSH_SCRATCH_BUF_DATA_OFF 0x10 |
69 | |
70 | /* Log message levels. */ |
71 | enum { |
72 | MLXBF_RSH_LOG_INFO, |
73 | MLXBF_RSH_LOG_WARN, |
74 | MLXBF_RSH_LOG_ERR, |
75 | MLXBF_RSH_LOG_ASSERT |
76 | }; |
77 | |
78 | /* Mapped pointer for RSH_BOOT_FIFO_DATA and RSH_BOOT_FIFO_COUNT register. */ |
79 | static void __iomem *mlxbf_rsh_boot_data; |
80 | static void __iomem *mlxbf_rsh_boot_cnt; |
81 | |
82 | /* Mapped pointer for rsh log semaphore/ctrl/data register. */ |
83 | static void __iomem *mlxbf_rsh_semaphore; |
84 | static void __iomem *mlxbf_rsh_scratch_buf_ctl; |
85 | static void __iomem *mlxbf_rsh_scratch_buf_data; |
86 | |
87 | /* Rsh log levels. */ |
88 | static const char * const mlxbf_rsh_log_level[] = { |
89 | "INFO" , "WARN" , "ERR" , "ASSERT" }; |
90 | |
91 | static DEFINE_MUTEX(icm_ops_lock); |
92 | static DEFINE_MUTEX(os_up_lock); |
93 | static DEFINE_MUTEX(mfg_ops_lock); |
94 | |
95 | /* |
96 | * Objects are stored within the MFG partition per type. |
97 | * Type 0 is not supported. |
98 | */ |
99 | enum { |
100 | MLNX_MFG_TYPE_OOB_MAC = 1, |
101 | MLNX_MFG_TYPE_OPN_0, |
102 | MLNX_MFG_TYPE_OPN_1, |
103 | MLNX_MFG_TYPE_OPN_2, |
104 | MLNX_MFG_TYPE_SKU_0, |
105 | MLNX_MFG_TYPE_SKU_1, |
106 | MLNX_MFG_TYPE_SKU_2, |
107 | MLNX_MFG_TYPE_MODL_0, |
108 | MLNX_MFG_TYPE_MODL_1, |
109 | MLNX_MFG_TYPE_MODL_2, |
110 | MLNX_MFG_TYPE_SN_0, |
111 | MLNX_MFG_TYPE_SN_1, |
112 | MLNX_MFG_TYPE_SN_2, |
113 | MLNX_MFG_TYPE_UUID_0, |
114 | MLNX_MFG_TYPE_UUID_1, |
115 | MLNX_MFG_TYPE_UUID_2, |
116 | MLNX_MFG_TYPE_UUID_3, |
117 | MLNX_MFG_TYPE_UUID_4, |
118 | MLNX_MFG_TYPE_REV, |
119 | }; |
120 | |
121 | #define MLNX_MFG_OPN_VAL_LEN 24 |
122 | #define MLNX_MFG_SKU_VAL_LEN 24 |
123 | #define MLNX_MFG_MODL_VAL_LEN 24 |
124 | #define MLNX_MFG_SN_VAL_LEN 24 |
125 | #define MLNX_MFG_UUID_VAL_LEN 40 |
126 | #define MLNX_MFG_REV_VAL_LEN 8 |
127 | #define MLNX_MFG_VAL_QWORD_CNT(type) \ |
128 | (MLNX_MFG_##type##_VAL_LEN / sizeof(u64)) |
129 | |
130 | /* |
131 | * The MAC address consists of 6 bytes (2 digits each) separated by ':'. |
132 | * The expected format is: "XX:XX:XX:XX:XX:XX" |
133 | */ |
134 | #define MLNX_MFG_OOB_MAC_FORMAT_LEN \ |
135 | ((ETH_ALEN * 2) + (ETH_ALEN - 1)) |
136 | |
137 | /* ARM SMC call which is atomic and no need for lock. */ |
138 | static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg) |
139 | { |
140 | struct arm_smccc_res res; |
141 | |
142 | arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res); |
143 | |
144 | return res.a0; |
145 | } |
146 | |
147 | /* Return the action in integer or an error code. */ |
148 | static int mlxbf_bootctl_reset_action_to_val(const char *action) |
149 | { |
150 | int i; |
151 | |
152 | for (i = 0; i < ARRAY_SIZE(boot_names); i++) |
153 | if (sysfs_streq(s1: boot_names[i].name, s2: action)) |
154 | return boot_names[i].value; |
155 | |
156 | return -EINVAL; |
157 | } |
158 | |
159 | /* Return the action in string. */ |
160 | static const char *mlxbf_bootctl_action_to_string(int action) |
161 | { |
162 | int i; |
163 | |
164 | for (i = 0; i < ARRAY_SIZE(boot_names); i++) |
165 | if (boot_names[i].value == action) |
166 | return boot_names[i].name; |
167 | |
168 | return "invalid action" ; |
169 | } |
170 | |
171 | static ssize_t post_reset_wdog_show(struct device *dev, |
172 | struct device_attribute *attr, char *buf) |
173 | { |
174 | int ret; |
175 | |
176 | ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_POST_RESET_WDOG, smc_arg: 0); |
177 | if (ret < 0) |
178 | return ret; |
179 | |
180 | return sprintf(buf, fmt: "%d\n" , ret); |
181 | } |
182 | |
183 | static ssize_t post_reset_wdog_store(struct device *dev, |
184 | struct device_attribute *attr, |
185 | const char *buf, size_t count) |
186 | { |
187 | unsigned long value; |
188 | int ret; |
189 | |
190 | ret = kstrtoul(s: buf, base: 10, res: &value); |
191 | if (ret) |
192 | return ret; |
193 | |
194 | ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_POST_RESET_WDOG, smc_arg: value); |
195 | if (ret < 0) |
196 | return ret; |
197 | |
198 | return count; |
199 | } |
200 | |
201 | static ssize_t mlxbf_bootctl_show(int smc_op, char *buf) |
202 | { |
203 | int action; |
204 | |
205 | action = mlxbf_bootctl_smc(smc_op, smc_arg: 0); |
206 | if (action < 0) |
207 | return action; |
208 | |
209 | return sprintf(buf, fmt: "%s\n" , mlxbf_bootctl_action_to_string(action)); |
210 | } |
211 | |
212 | static int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count) |
213 | { |
214 | int ret, action; |
215 | |
216 | action = mlxbf_bootctl_reset_action_to_val(action: buf); |
217 | if (action < 0) |
218 | return action; |
219 | |
220 | ret = mlxbf_bootctl_smc(smc_op, smc_arg: action); |
221 | if (ret < 0) |
222 | return ret; |
223 | |
224 | return count; |
225 | } |
226 | |
227 | static ssize_t reset_action_show(struct device *dev, |
228 | struct device_attribute *attr, char *buf) |
229 | { |
230 | return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_RESET_ACTION, buf); |
231 | } |
232 | |
233 | static ssize_t reset_action_store(struct device *dev, |
234 | struct device_attribute *attr, |
235 | const char *buf, size_t count) |
236 | { |
237 | return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_RESET_ACTION, buf, count); |
238 | } |
239 | |
240 | static ssize_t second_reset_action_show(struct device *dev, |
241 | struct device_attribute *attr, |
242 | char *buf) |
243 | { |
244 | return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION, buf); |
245 | } |
246 | |
247 | static ssize_t second_reset_action_store(struct device *dev, |
248 | struct device_attribute *attr, |
249 | const char *buf, size_t count) |
250 | { |
251 | return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION, buf, |
252 | count); |
253 | } |
254 | |
255 | static ssize_t lifecycle_state_show(struct device *dev, |
256 | struct device_attribute *attr, char *buf) |
257 | { |
258 | int status_bits; |
259 | int use_dev_key; |
260 | int test_state; |
261 | int lc_state; |
262 | |
263 | status_bits = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS, |
264 | MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE); |
265 | if (status_bits < 0) |
266 | return status_bits; |
267 | |
268 | use_dev_key = status_bits & MLXBF_BOOTCTL_SB_DEV_MASK; |
269 | test_state = status_bits & MLXBF_BOOTCTL_SB_TEST_MASK; |
270 | lc_state = status_bits & MLXBF_BOOTCTL_SB_SECURE_MASK; |
271 | |
272 | /* |
273 | * If the test bits are set, we specify that the current state may be |
274 | * due to using the test bits. |
275 | */ |
276 | if (test_state) { |
277 | return sprintf(buf, fmt: "%s(test)\n" , |
278 | mlxbf_bootctl_lifecycle_states[lc_state]); |
279 | } else if (use_dev_key && |
280 | (lc_state == MLXBF_BOOTCTL_SB_LIFECYCLE_GA_SECURE)) { |
281 | return sprintf(buf, fmt: "Secured (development)\n" ); |
282 | } |
283 | |
284 | return sprintf(buf, fmt: "%s\n" , mlxbf_bootctl_lifecycle_states[lc_state]); |
285 | } |
286 | |
287 | static ssize_t secure_boot_fuse_state_show(struct device *dev, |
288 | struct device_attribute *attr, |
289 | char *buf) |
290 | { |
291 | int burnt, valid, key, key_state, buf_len = 0, upper_key_used = 0; |
292 | const char *status; |
293 | |
294 | key_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS, |
295 | MLXBF_BOOTCTL_FUSE_STATUS_KEYS); |
296 | if (key_state < 0) |
297 | return key_state; |
298 | |
299 | /* |
300 | * key_state contains the bits for 4 Key versions, loaded from eFuses |
301 | * after a hard reset. Lower 4 bits are a thermometer code indicating |
302 | * key programming has started for key n (0000 = none, 0001 = version 0, |
303 | * 0011 = version 1, 0111 = version 2, 1111 = version 3). Upper 4 bits |
304 | * are a thermometer code indicating key programming has completed for |
305 | * key n (same encodings as the start bits). This allows for detection |
306 | * of an interruption in the programming process which has left the key |
307 | * partially programmed (and thus invalid). The process is to burn the |
308 | * eFuse for the new key start bit, burn the key eFuses, then burn the |
309 | * eFuse for the new key complete bit. |
310 | * |
311 | * For example 0000_0000: no key valid, 0001_0001: key version 0 valid, |
312 | * 0011_0011: key 1 version valid, 0011_0111: key version 2 started |
313 | * programming but did not complete, etc. The most recent key for which |
314 | * both start and complete bit is set is loaded. On soft reset, this |
315 | * register is not modified. |
316 | */ |
317 | for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) { |
318 | burnt = key_state & BIT(key); |
319 | valid = key_state & BIT(key + MLXBF_SB_KEY_NUM); |
320 | |
321 | if (burnt && valid) |
322 | upper_key_used = 1; |
323 | |
324 | if (upper_key_used) { |
325 | if (burnt) |
326 | status = valid ? "Used" : "Wasted" ; |
327 | else |
328 | status = valid ? "Invalid" : "Skipped" ; |
329 | } else { |
330 | if (burnt) |
331 | status = valid ? "InUse" : "Incomplete" ; |
332 | else |
333 | status = valid ? "Invalid" : "Free" ; |
334 | } |
335 | buf_len += sprintf(buf: buf + buf_len, fmt: "%d:%s " , key, status); |
336 | } |
337 | buf_len += sprintf(buf: buf + buf_len, fmt: "\n" ); |
338 | |
339 | return buf_len; |
340 | } |
341 | |
342 | static ssize_t fw_reset_store(struct device *dev, |
343 | struct device_attribute *attr, |
344 | const char *buf, size_t count) |
345 | { |
346 | unsigned long key; |
347 | int err; |
348 | |
349 | err = kstrtoul(s: buf, base: 16, res: &key); |
350 | if (err) |
351 | return err; |
352 | |
353 | if (mlxbf_bootctl_smc(MLXBF_BOOTCTL_FW_RESET, smc_arg: key) < 0) |
354 | return -EINVAL; |
355 | |
356 | return count; |
357 | } |
358 | |
359 | /* Size(8-byte words) of the log buffer. */ |
360 | #define RSH_SCRATCH_BUF_CTL_IDX_MASK 0x7f |
361 | |
362 | /* 100ms timeout */ |
363 | #define RSH_SCRATCH_BUF_POLL_TIMEOUT 100000 |
364 | |
365 | static int mlxbf_rsh_log_sem_lock(void) |
366 | { |
367 | unsigned long reg; |
368 | |
369 | return readq_poll_timeout(mlxbf_rsh_semaphore, reg, !reg, 0, |
370 | RSH_SCRATCH_BUF_POLL_TIMEOUT); |
371 | } |
372 | |
373 | static void mlxbf_rsh_log_sem_unlock(void) |
374 | { |
375 | writeq(val: 0, addr: mlxbf_rsh_semaphore); |
376 | } |
377 | |
378 | static ssize_t rsh_log_store(struct device *dev, |
379 | struct device_attribute *attr, |
380 | const char *buf, size_t count) |
381 | { |
382 | int rc, idx, num, len, level = MLXBF_RSH_LOG_INFO; |
383 | size_t size = count; |
384 | u64 data; |
385 | |
386 | if (!size) |
387 | return -EINVAL; |
388 | |
389 | if (!mlxbf_rsh_semaphore || !mlxbf_rsh_scratch_buf_ctl) |
390 | return -EOPNOTSUPP; |
391 | |
392 | /* Ignore line break at the end. */ |
393 | if (buf[size - 1] == '\n') |
394 | size--; |
395 | |
396 | /* Check the message prefix. */ |
397 | for (idx = 0; idx < ARRAY_SIZE(mlxbf_rsh_log_level); idx++) { |
398 | len = strlen(mlxbf_rsh_log_level[idx]); |
399 | if (len + 1 < size && |
400 | !strncmp(buf, mlxbf_rsh_log_level[idx], len)) { |
401 | buf += len; |
402 | size -= len; |
403 | level = idx; |
404 | break; |
405 | } |
406 | } |
407 | |
408 | /* Ignore leading spaces. */ |
409 | while (size > 0 && buf[0] == ' ') { |
410 | size--; |
411 | buf++; |
412 | } |
413 | |
414 | /* Take the semaphore. */ |
415 | rc = mlxbf_rsh_log_sem_lock(); |
416 | if (rc) |
417 | return rc; |
418 | |
419 | /* Calculate how many words are available. */ |
420 | idx = readq(addr: mlxbf_rsh_scratch_buf_ctl); |
421 | num = min((int)DIV_ROUND_UP(size, sizeof(u64)), |
422 | RSH_SCRATCH_BUF_CTL_IDX_MASK - idx - 1); |
423 | if (num <= 0) |
424 | goto done; |
425 | |
426 | /* Write Header. */ |
427 | data = FIELD_PREP(MLXBF_RSH_LOG_TYPE_MASK, MLXBF_RSH_LOG_TYPE_MSG); |
428 | data |= FIELD_PREP(MLXBF_RSH_LOG_LEN_MASK, num); |
429 | data |= FIELD_PREP(MLXBF_RSH_LOG_LEVEL_MASK, level); |
430 | writeq(val: data, addr: mlxbf_rsh_scratch_buf_data); |
431 | |
432 | /* Write message. */ |
433 | for (idx = 0; idx < num && size > 0; idx++) { |
434 | if (size < sizeof(u64)) { |
435 | data = 0; |
436 | memcpy(&data, buf, size); |
437 | size = 0; |
438 | } else { |
439 | memcpy(&data, buf, sizeof(u64)); |
440 | size -= sizeof(u64); |
441 | buf += sizeof(u64); |
442 | } |
443 | writeq(val: data, addr: mlxbf_rsh_scratch_buf_data); |
444 | } |
445 | |
446 | done: |
447 | /* Release the semaphore. */ |
448 | mlxbf_rsh_log_sem_unlock(); |
449 | |
450 | /* Ignore the rest if no more space. */ |
451 | return count; |
452 | } |
453 | |
454 | static ssize_t large_icm_show(struct device *dev, |
455 | struct device_attribute *attr, char *buf) |
456 | { |
457 | struct arm_smccc_res res; |
458 | |
459 | mutex_lock(&icm_ops_lock); |
460 | arm_smccc_smc(MLNX_HANDLE_GET_ICM_INFO, 0, 0, 0, 0, |
461 | 0, 0, 0, &res); |
462 | mutex_unlock(lock: &icm_ops_lock); |
463 | if (res.a0) |
464 | return -EPERM; |
465 | |
466 | return sysfs_emit(buf, fmt: "0x%lx" , res.a1); |
467 | } |
468 | |
469 | static ssize_t large_icm_store(struct device *dev, |
470 | struct device_attribute *attr, |
471 | const char *buf, size_t count) |
472 | { |
473 | struct arm_smccc_res res; |
474 | unsigned long icm_data; |
475 | int err; |
476 | |
477 | err = kstrtoul(s: buf, MLXBF_LARGE_ICMC_MAX_STRING_SIZE, res: &icm_data); |
478 | if (err) |
479 | return err; |
480 | |
481 | if ((icm_data != 0 && icm_data < MLXBF_LARGE_ICMC_SIZE_MIN) || |
482 | icm_data > MLXBF_LARGE_ICMC_SIZE_MAX || icm_data % MLXBF_LARGE_ICMC_GRANULARITY) |
483 | return -EPERM; |
484 | |
485 | mutex_lock(&icm_ops_lock); |
486 | arm_smccc_smc(MLNX_HANDLE_SET_ICM_INFO, icm_data, 0, 0, 0, 0, 0, 0, &res); |
487 | mutex_unlock(lock: &icm_ops_lock); |
488 | |
489 | return res.a0 ? -EPERM : count; |
490 | } |
491 | |
492 | static ssize_t os_up_store(struct device *dev, |
493 | struct device_attribute *attr, |
494 | const char *buf, size_t count) |
495 | { |
496 | struct arm_smccc_res res; |
497 | unsigned long val; |
498 | int err; |
499 | |
500 | err = kstrtoul(s: buf, base: 10, res: &val); |
501 | if (err) |
502 | return err; |
503 | |
504 | if (val != 1) |
505 | return -EINVAL; |
506 | |
507 | mutex_lock(&os_up_lock); |
508 | arm_smccc_smc(MLNX_HANDLE_OS_UP, 0, 0, 0, 0, 0, 0, 0, &res); |
509 | mutex_unlock(lock: &os_up_lock); |
510 | |
511 | return count; |
512 | } |
513 | |
514 | static ssize_t oob_mac_show(struct device *dev, |
515 | struct device_attribute *attr, char *buf) |
516 | { |
517 | struct arm_smccc_res res; |
518 | u8 *mac_byte_ptr; |
519 | |
520 | mutex_lock(&mfg_ops_lock); |
521 | arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, MLNX_MFG_TYPE_OOB_MAC, 0, 0, 0, |
522 | 0, 0, 0, &res); |
523 | mutex_unlock(lock: &mfg_ops_lock); |
524 | if (res.a0) |
525 | return -EPERM; |
526 | |
527 | mac_byte_ptr = (u8 *)&res.a1; |
528 | |
529 | return sysfs_format_mac(buf, addr: mac_byte_ptr, ETH_ALEN); |
530 | } |
531 | |
532 | static ssize_t oob_mac_store(struct device *dev, |
533 | struct device_attribute *attr, |
534 | const char *buf, size_t count) |
535 | { |
536 | unsigned int byte[MLNX_MFG_OOB_MAC_FORMAT_LEN] = { 0 }; |
537 | struct arm_smccc_res res; |
538 | int byte_idx, len; |
539 | u64 mac_addr = 0; |
540 | u8 *mac_byte_ptr; |
541 | |
542 | if ((count - 1) != MLNX_MFG_OOB_MAC_FORMAT_LEN) |
543 | return -EINVAL; |
544 | |
545 | len = sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x" , |
546 | &byte[0], &byte[1], &byte[2], |
547 | &byte[3], &byte[4], &byte[5]); |
548 | if (len != ETH_ALEN) |
549 | return -EINVAL; |
550 | |
551 | mac_byte_ptr = (u8 *)&mac_addr; |
552 | |
553 | for (byte_idx = 0; byte_idx < ETH_ALEN; byte_idx++) |
554 | mac_byte_ptr[byte_idx] = (u8)byte[byte_idx]; |
555 | |
556 | mutex_lock(&mfg_ops_lock); |
557 | arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, MLNX_MFG_TYPE_OOB_MAC, |
558 | ETH_ALEN, mac_addr, 0, 0, 0, 0, &res); |
559 | mutex_unlock(lock: &mfg_ops_lock); |
560 | |
561 | return res.a0 ? -EPERM : count; |
562 | } |
563 | |
564 | static ssize_t opn_show(struct device *dev, |
565 | struct device_attribute *attr, char *buf) |
566 | { |
567 | u64 opn_data[MLNX_MFG_VAL_QWORD_CNT(OPN) + 1] = { 0 }; |
568 | struct arm_smccc_res res; |
569 | int word; |
570 | |
571 | mutex_lock(&mfg_ops_lock); |
572 | for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(OPN); word++) { |
573 | arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, |
574 | MLNX_MFG_TYPE_OPN_0 + word, |
575 | 0, 0, 0, 0, 0, 0, &res); |
576 | if (res.a0) { |
577 | mutex_unlock(lock: &mfg_ops_lock); |
578 | return -EPERM; |
579 | } |
580 | opn_data[word] = res.a1; |
581 | } |
582 | mutex_unlock(lock: &mfg_ops_lock); |
583 | |
584 | return sysfs_emit(buf, fmt: "%s" , (char *)opn_data); |
585 | } |
586 | |
587 | static ssize_t opn_store(struct device *dev, |
588 | struct device_attribute *attr, |
589 | const char *buf, size_t count) |
590 | { |
591 | u64 opn[MLNX_MFG_VAL_QWORD_CNT(OPN)] = { 0 }; |
592 | struct arm_smccc_res res; |
593 | int word; |
594 | |
595 | if (count > MLNX_MFG_OPN_VAL_LEN) |
596 | return -EINVAL; |
597 | |
598 | memcpy(opn, buf, count); |
599 | |
600 | mutex_lock(&mfg_ops_lock); |
601 | for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(OPN); word++) { |
602 | arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, |
603 | MLNX_MFG_TYPE_OPN_0 + word, |
604 | sizeof(u64), opn[word], 0, 0, 0, 0, &res); |
605 | if (res.a0) { |
606 | mutex_unlock(lock: &mfg_ops_lock); |
607 | return -EPERM; |
608 | } |
609 | } |
610 | mutex_unlock(lock: &mfg_ops_lock); |
611 | |
612 | return count; |
613 | } |
614 | |
615 | static ssize_t sku_show(struct device *dev, |
616 | struct device_attribute *attr, char *buf) |
617 | { |
618 | u64 sku_data[MLNX_MFG_VAL_QWORD_CNT(SKU) + 1] = { 0 }; |
619 | struct arm_smccc_res res; |
620 | int word; |
621 | |
622 | mutex_lock(&mfg_ops_lock); |
623 | for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SKU); word++) { |
624 | arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, |
625 | MLNX_MFG_TYPE_SKU_0 + word, |
626 | 0, 0, 0, 0, 0, 0, &res); |
627 | if (res.a0) { |
628 | mutex_unlock(lock: &mfg_ops_lock); |
629 | return -EPERM; |
630 | } |
631 | sku_data[word] = res.a1; |
632 | } |
633 | mutex_unlock(lock: &mfg_ops_lock); |
634 | |
635 | return sysfs_emit(buf, fmt: "%s" , (char *)sku_data); |
636 | } |
637 | |
638 | static ssize_t sku_store(struct device *dev, |
639 | struct device_attribute *attr, |
640 | const char *buf, size_t count) |
641 | { |
642 | u64 sku[MLNX_MFG_VAL_QWORD_CNT(SKU)] = { 0 }; |
643 | struct arm_smccc_res res; |
644 | int word; |
645 | |
646 | if (count > MLNX_MFG_SKU_VAL_LEN) |
647 | return -EINVAL; |
648 | |
649 | memcpy(sku, buf, count); |
650 | |
651 | mutex_lock(&mfg_ops_lock); |
652 | for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SKU); word++) { |
653 | arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, |
654 | MLNX_MFG_TYPE_SKU_0 + word, |
655 | sizeof(u64), sku[word], 0, 0, 0, 0, &res); |
656 | if (res.a0) { |
657 | mutex_unlock(lock: &mfg_ops_lock); |
658 | return -EPERM; |
659 | } |
660 | } |
661 | mutex_unlock(lock: &mfg_ops_lock); |
662 | |
663 | return count; |
664 | } |
665 | |
666 | static ssize_t modl_show(struct device *dev, |
667 | struct device_attribute *attr, char *buf) |
668 | { |
669 | u64 modl_data[MLNX_MFG_VAL_QWORD_CNT(MODL) + 1] = { 0 }; |
670 | struct arm_smccc_res res; |
671 | int word; |
672 | |
673 | mutex_lock(&mfg_ops_lock); |
674 | for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(MODL); word++) { |
675 | arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, |
676 | MLNX_MFG_TYPE_MODL_0 + word, |
677 | 0, 0, 0, 0, 0, 0, &res); |
678 | if (res.a0) { |
679 | mutex_unlock(lock: &mfg_ops_lock); |
680 | return -EPERM; |
681 | } |
682 | modl_data[word] = res.a1; |
683 | } |
684 | mutex_unlock(lock: &mfg_ops_lock); |
685 | |
686 | return sysfs_emit(buf, fmt: "%s" , (char *)modl_data); |
687 | } |
688 | |
689 | static ssize_t modl_store(struct device *dev, |
690 | struct device_attribute *attr, |
691 | const char *buf, size_t count) |
692 | { |
693 | u64 modl[MLNX_MFG_VAL_QWORD_CNT(MODL)] = { 0 }; |
694 | struct arm_smccc_res res; |
695 | int word; |
696 | |
697 | if (count > MLNX_MFG_MODL_VAL_LEN) |
698 | return -EINVAL; |
699 | |
700 | memcpy(modl, buf, count); |
701 | |
702 | mutex_lock(&mfg_ops_lock); |
703 | for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(MODL); word++) { |
704 | arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, |
705 | MLNX_MFG_TYPE_MODL_0 + word, |
706 | sizeof(u64), modl[word], 0, 0, 0, 0, &res); |
707 | if (res.a0) { |
708 | mutex_unlock(lock: &mfg_ops_lock); |
709 | return -EPERM; |
710 | } |
711 | } |
712 | mutex_unlock(lock: &mfg_ops_lock); |
713 | |
714 | return count; |
715 | } |
716 | |
717 | static ssize_t sn_show(struct device *dev, |
718 | struct device_attribute *attr, char *buf) |
719 | { |
720 | u64 sn_data[MLNX_MFG_VAL_QWORD_CNT(SN) + 1] = { 0 }; |
721 | struct arm_smccc_res res; |
722 | int word; |
723 | |
724 | mutex_lock(&mfg_ops_lock); |
725 | for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SN); word++) { |
726 | arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, |
727 | MLNX_MFG_TYPE_SN_0 + word, |
728 | 0, 0, 0, 0, 0, 0, &res); |
729 | if (res.a0) { |
730 | mutex_unlock(lock: &mfg_ops_lock); |
731 | return -EPERM; |
732 | } |
733 | sn_data[word] = res.a1; |
734 | } |
735 | mutex_unlock(lock: &mfg_ops_lock); |
736 | |
737 | return sysfs_emit(buf, fmt: "%s" , (char *)sn_data); |
738 | } |
739 | |
740 | static ssize_t sn_store(struct device *dev, |
741 | struct device_attribute *attr, |
742 | const char *buf, size_t count) |
743 | { |
744 | u64 sn[MLNX_MFG_VAL_QWORD_CNT(SN)] = { 0 }; |
745 | struct arm_smccc_res res; |
746 | int word; |
747 | |
748 | if (count > MLNX_MFG_SN_VAL_LEN) |
749 | return -EINVAL; |
750 | |
751 | memcpy(sn, buf, count); |
752 | |
753 | mutex_lock(&mfg_ops_lock); |
754 | for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SN); word++) { |
755 | arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, |
756 | MLNX_MFG_TYPE_SN_0 + word, |
757 | sizeof(u64), sn[word], 0, 0, 0, 0, &res); |
758 | if (res.a0) { |
759 | mutex_unlock(lock: &mfg_ops_lock); |
760 | return -EPERM; |
761 | } |
762 | } |
763 | mutex_unlock(lock: &mfg_ops_lock); |
764 | |
765 | return count; |
766 | } |
767 | |
768 | static ssize_t uuid_show(struct device *dev, |
769 | struct device_attribute *attr, char *buf) |
770 | { |
771 | u64 uuid_data[MLNX_MFG_VAL_QWORD_CNT(UUID) + 1] = { 0 }; |
772 | struct arm_smccc_res res; |
773 | int word; |
774 | |
775 | mutex_lock(&mfg_ops_lock); |
776 | for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(UUID); word++) { |
777 | arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, |
778 | MLNX_MFG_TYPE_UUID_0 + word, |
779 | 0, 0, 0, 0, 0, 0, &res); |
780 | if (res.a0) { |
781 | mutex_unlock(lock: &mfg_ops_lock); |
782 | return -EPERM; |
783 | } |
784 | uuid_data[word] = res.a1; |
785 | } |
786 | mutex_unlock(lock: &mfg_ops_lock); |
787 | |
788 | return sysfs_emit(buf, fmt: "%s" , (char *)uuid_data); |
789 | } |
790 | |
791 | static ssize_t uuid_store(struct device *dev, |
792 | struct device_attribute *attr, |
793 | const char *buf, size_t count) |
794 | { |
795 | u64 uuid[MLNX_MFG_VAL_QWORD_CNT(UUID)] = { 0 }; |
796 | struct arm_smccc_res res; |
797 | int word; |
798 | |
799 | if (count > MLNX_MFG_UUID_VAL_LEN) |
800 | return -EINVAL; |
801 | |
802 | memcpy(uuid, buf, count); |
803 | |
804 | mutex_lock(&mfg_ops_lock); |
805 | for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(UUID); word++) { |
806 | arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, |
807 | MLNX_MFG_TYPE_UUID_0 + word, |
808 | sizeof(u64), uuid[word], 0, 0, 0, 0, &res); |
809 | if (res.a0) { |
810 | mutex_unlock(lock: &mfg_ops_lock); |
811 | return -EPERM; |
812 | } |
813 | } |
814 | mutex_unlock(lock: &mfg_ops_lock); |
815 | |
816 | return count; |
817 | } |
818 | |
819 | static ssize_t rev_show(struct device *dev, |
820 | struct device_attribute *attr, char *buf) |
821 | { |
822 | u64 rev_data[MLNX_MFG_VAL_QWORD_CNT(REV) + 1] = { 0 }; |
823 | struct arm_smccc_res res; |
824 | int word; |
825 | |
826 | mutex_lock(&mfg_ops_lock); |
827 | for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(REV); word++) { |
828 | arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, |
829 | MLNX_MFG_TYPE_REV + word, |
830 | 0, 0, 0, 0, 0, 0, &res); |
831 | if (res.a0) { |
832 | mutex_unlock(lock: &mfg_ops_lock); |
833 | return -EPERM; |
834 | } |
835 | rev_data[word] = res.a1; |
836 | } |
837 | mutex_unlock(lock: &mfg_ops_lock); |
838 | |
839 | return sysfs_emit(buf, fmt: "%s" , (char *)rev_data); |
840 | } |
841 | |
842 | static ssize_t rev_store(struct device *dev, |
843 | struct device_attribute *attr, |
844 | const char *buf, size_t count) |
845 | { |
846 | u64 rev[MLNX_MFG_VAL_QWORD_CNT(REV)] = { 0 }; |
847 | struct arm_smccc_res res; |
848 | int word; |
849 | |
850 | if (count > MLNX_MFG_REV_VAL_LEN) |
851 | return -EINVAL; |
852 | |
853 | memcpy(rev, buf, count); |
854 | |
855 | mutex_lock(&mfg_ops_lock); |
856 | for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(REV); word++) { |
857 | arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, |
858 | MLNX_MFG_TYPE_REV + word, |
859 | sizeof(u64), rev[word], 0, 0, 0, 0, &res); |
860 | if (res.a0) { |
861 | mutex_unlock(lock: &mfg_ops_lock); |
862 | return -EPERM; |
863 | } |
864 | } |
865 | mutex_unlock(lock: &mfg_ops_lock); |
866 | |
867 | return count; |
868 | } |
869 | |
870 | static ssize_t mfg_lock_store(struct device *dev, |
871 | struct device_attribute *attr, |
872 | const char *buf, size_t count) |
873 | { |
874 | struct arm_smccc_res res; |
875 | unsigned long val; |
876 | int err; |
877 | |
878 | err = kstrtoul(s: buf, base: 10, res: &val); |
879 | if (err) |
880 | return err; |
881 | |
882 | if (val != 1) |
883 | return -EINVAL; |
884 | |
885 | mutex_lock(&mfg_ops_lock); |
886 | arm_smccc_smc(MLXBF_BOOTCTL_LOCK_MFG_INFO, 0, 0, 0, 0, 0, 0, 0, &res); |
887 | mutex_unlock(lock: &mfg_ops_lock); |
888 | |
889 | return count; |
890 | } |
891 | |
892 | static DEVICE_ATTR_RW(post_reset_wdog); |
893 | static DEVICE_ATTR_RW(reset_action); |
894 | static DEVICE_ATTR_RW(second_reset_action); |
895 | static DEVICE_ATTR_RO(lifecycle_state); |
896 | static DEVICE_ATTR_RO(secure_boot_fuse_state); |
897 | static DEVICE_ATTR_WO(fw_reset); |
898 | static DEVICE_ATTR_WO(rsh_log); |
899 | static DEVICE_ATTR_RW(large_icm); |
900 | static DEVICE_ATTR_WO(os_up); |
901 | static DEVICE_ATTR_RW(oob_mac); |
902 | static DEVICE_ATTR_RW(opn); |
903 | static DEVICE_ATTR_RW(sku); |
904 | static DEVICE_ATTR_RW(modl); |
905 | static DEVICE_ATTR_RW(sn); |
906 | static DEVICE_ATTR_RW(uuid); |
907 | static DEVICE_ATTR_RW(rev); |
908 | static DEVICE_ATTR_WO(mfg_lock); |
909 | |
910 | static struct attribute *mlxbf_bootctl_attrs[] = { |
911 | &dev_attr_post_reset_wdog.attr, |
912 | &dev_attr_reset_action.attr, |
913 | &dev_attr_second_reset_action.attr, |
914 | &dev_attr_lifecycle_state.attr, |
915 | &dev_attr_secure_boot_fuse_state.attr, |
916 | &dev_attr_fw_reset.attr, |
917 | &dev_attr_rsh_log.attr, |
918 | &dev_attr_large_icm.attr, |
919 | &dev_attr_os_up.attr, |
920 | &dev_attr_oob_mac.attr, |
921 | &dev_attr_opn.attr, |
922 | &dev_attr_sku.attr, |
923 | &dev_attr_modl.attr, |
924 | &dev_attr_sn.attr, |
925 | &dev_attr_uuid.attr, |
926 | &dev_attr_rev.attr, |
927 | &dev_attr_mfg_lock.attr, |
928 | NULL |
929 | }; |
930 | |
931 | ATTRIBUTE_GROUPS(mlxbf_bootctl); |
932 | |
933 | static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = { |
934 | {"MLNXBF04" , 0}, |
935 | {} |
936 | }; |
937 | |
938 | MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids); |
939 | |
940 | static ssize_t mlxbf_bootctl_bootfifo_read(struct file *filp, |
941 | struct kobject *kobj, |
942 | struct bin_attribute *bin_attr, |
943 | char *buf, loff_t pos, |
944 | size_t count) |
945 | { |
946 | unsigned long timeout = msecs_to_jiffies(m: 500); |
947 | unsigned long expire = jiffies + timeout; |
948 | u64 data, cnt = 0; |
949 | char *p = buf; |
950 | |
951 | while (count >= sizeof(data)) { |
952 | /* Give up reading if no more data within 500ms. */ |
953 | if (!cnt) { |
954 | cnt = readq(addr: mlxbf_rsh_boot_cnt); |
955 | if (!cnt) { |
956 | if (time_after(jiffies, expire)) |
957 | break; |
958 | usleep_range(min: 10, max: 50); |
959 | continue; |
960 | } |
961 | } |
962 | |
963 | data = readq(addr: mlxbf_rsh_boot_data); |
964 | memcpy(p, &data, sizeof(data)); |
965 | count -= sizeof(data); |
966 | p += sizeof(data); |
967 | cnt--; |
968 | expire = jiffies + timeout; |
969 | } |
970 | |
971 | return p - buf; |
972 | } |
973 | |
974 | static struct bin_attribute mlxbf_bootctl_bootfifo_sysfs_attr = { |
975 | .attr = { .name = "bootfifo" , .mode = 0400 }, |
976 | .read = mlxbf_bootctl_bootfifo_read, |
977 | }; |
978 | |
979 | static bool mlxbf_bootctl_guid_match(const guid_t *guid, |
980 | const struct arm_smccc_res *res) |
981 | { |
982 | guid_t id = GUID_INIT(res->a0, res->a1, res->a1 >> 16, |
983 | res->a2, res->a2 >> 8, res->a2 >> 16, |
984 | res->a2 >> 24, res->a3, res->a3 >> 8, |
985 | res->a3 >> 16, res->a3 >> 24); |
986 | |
987 | return guid_equal(u1: guid, u2: &id); |
988 | } |
989 | |
990 | static int mlxbf_bootctl_probe(struct platform_device *pdev) |
991 | { |
992 | struct arm_smccc_res res = { 0 }; |
993 | void __iomem *reg; |
994 | guid_t guid; |
995 | int ret; |
996 | |
997 | /* Map the resource of the bootfifo data register. */ |
998 | mlxbf_rsh_boot_data = devm_platform_ioremap_resource(pdev, index: 0); |
999 | if (IS_ERR(ptr: mlxbf_rsh_boot_data)) |
1000 | return PTR_ERR(ptr: mlxbf_rsh_boot_data); |
1001 | |
1002 | /* Map the resource of the bootfifo counter register. */ |
1003 | mlxbf_rsh_boot_cnt = devm_platform_ioremap_resource(pdev, index: 1); |
1004 | if (IS_ERR(ptr: mlxbf_rsh_boot_cnt)) |
1005 | return PTR_ERR(ptr: mlxbf_rsh_boot_cnt); |
1006 | |
1007 | /* Map the resource of the rshim semaphore register. */ |
1008 | mlxbf_rsh_semaphore = devm_platform_ioremap_resource(pdev, index: 2); |
1009 | if (IS_ERR(ptr: mlxbf_rsh_semaphore)) |
1010 | return PTR_ERR(ptr: mlxbf_rsh_semaphore); |
1011 | |
1012 | /* Map the resource of the scratch buffer (log) registers. */ |
1013 | reg = devm_platform_ioremap_resource(pdev, index: 3); |
1014 | if (IS_ERR(ptr: reg)) |
1015 | return PTR_ERR(ptr: reg); |
1016 | mlxbf_rsh_scratch_buf_ctl = reg + MLXBF_RSH_SCRATCH_BUF_CTL_OFF; |
1017 | mlxbf_rsh_scratch_buf_data = reg + MLXBF_RSH_SCRATCH_BUF_DATA_OFF; |
1018 | |
1019 | /* Ensure we have the UUID we expect for this service. */ |
1020 | arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res); |
1021 | guid_parse(uuid: mlxbf_bootctl_svc_uuid_str, u: &guid); |
1022 | if (!mlxbf_bootctl_guid_match(guid: &guid, res: &res)) |
1023 | return -ENODEV; |
1024 | |
1025 | /* |
1026 | * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_SWAP_EMMC |
1027 | * in case of boot failures. However it doesn't clear the state if there |
1028 | * is no failure. Restore the default boot mode here to avoid any |
1029 | * unnecessary boot partition swapping. |
1030 | */ |
1031 | ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_RESET_ACTION, |
1032 | MLXBF_BOOTCTL_EMMC); |
1033 | if (ret < 0) |
1034 | dev_warn(&pdev->dev, "Unable to reset the EMMC boot mode\n" ); |
1035 | |
1036 | ret = sysfs_create_bin_file(kobj: &pdev->dev.kobj, |
1037 | attr: &mlxbf_bootctl_bootfifo_sysfs_attr); |
1038 | if (ret) |
1039 | pr_err("Unable to create bootfifo sysfs file, error %d\n" , ret); |
1040 | |
1041 | return ret; |
1042 | } |
1043 | |
1044 | static void mlxbf_bootctl_remove(struct platform_device *pdev) |
1045 | { |
1046 | sysfs_remove_bin_file(kobj: &pdev->dev.kobj, |
1047 | attr: &mlxbf_bootctl_bootfifo_sysfs_attr); |
1048 | } |
1049 | |
1050 | static struct platform_driver mlxbf_bootctl_driver = { |
1051 | .probe = mlxbf_bootctl_probe, |
1052 | .remove_new = mlxbf_bootctl_remove, |
1053 | .driver = { |
1054 | .name = "mlxbf-bootctl" , |
1055 | .dev_groups = mlxbf_bootctl_groups, |
1056 | .acpi_match_table = mlxbf_bootctl_acpi_ids, |
1057 | } |
1058 | }; |
1059 | |
1060 | module_platform_driver(mlxbf_bootctl_driver); |
1061 | |
1062 | MODULE_DESCRIPTION("Mellanox boot control driver" ); |
1063 | MODULE_LICENSE("GPL v2" ); |
1064 | MODULE_AUTHOR("Mellanox Technologies" ); |
1065 | |