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