e286869b24
- Why I did it 1. Update Mellanox HW-MGMT package to newer version V.7.0030.1011 2. Replace the SONiC PMON Thermal control algorithm with the one inside the HW-MGMT package on all Nvidia platforms 3. Support Spectrum-4 systems - How I did it 1. Update the HW-MGMT package version number and submodule pointer 2. Remove the thermal control algorithm implementation from Mellanox platform API 3. Revise the patch to HW-MGMT package which will disable HW-MGMT from running on SIMX 4. Update the downstream kernel patch list Signed-off-by: Kebo Liu <kebol@nvidia.com>
1720 lines
50 KiB
Diff
1720 lines
50 KiB
Diff
From 988b360c98ef157e37818f5f7db322c129d3dfb9 Mon Sep 17 00:00:00 2001
|
|
From: Shravan Kumar Ramani <shravankr@nvidia.com>
|
|
Date: Wed, 6 Jul 2022 07:37:22 -0400
|
|
Subject: [PATCH backport 5.10 12/63] UBUNTU: SAUCE: platform/mellanox: Updates
|
|
to mlxbf-bootctl
|
|
|
|
BugLink: https://launchpad.net/bugs/1980832
|
|
|
|
The driver supports the VPD fields in the EEPROM and exposes
|
|
sysfs files for configuring and reading the same.
|
|
Also address buffer overflow and exclusion issues.
|
|
|
|
Signed-off-by: Shravan Kumar Ramani <shravankr@nvidia.com>
|
|
Signed-off-by: Ike Panhc <ike.pan@canonical.com>
|
|
---
|
|
drivers/platform/mellanox/mlxbf-bootctl.c | 1410 ++++++++++++++++++---
|
|
drivers/platform/mellanox/mlxbf-bootctl.h | 88 +-
|
|
2 files changed, 1268 insertions(+), 230 deletions(-)
|
|
|
|
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c
|
|
index 1c7a288b5..2302e1e09 100644
|
|
--- a/drivers/platform/mellanox/mlxbf-bootctl.c
|
|
+++ b/drivers/platform/mellanox/mlxbf-bootctl.c
|
|
@@ -1,51 +1,129 @@
|
|
-// SPDX-License-Identifier: GPL-2.0+
|
|
+// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause
|
|
/*
|
|
- * Mellanox boot control driver
|
|
+ * Mellanox boot control driver
|
|
+ * This driver provides a sysfs interface for systems management
|
|
+ * software to manage reset-time actions.
|
|
*
|
|
- * This driver provides a sysfs interface for systems management
|
|
- * software to manage reset-time actions.
|
|
+ * Copyright (C) 2020 Mellanox Technologies. All rights reserved.
|
|
*
|
|
- * Copyright (C) 2019 Mellanox Technologies
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License v2.0 as published by
|
|
+ * the Free Software Foundation.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/acpi.h>
|
|
#include <linux/arm-smccc.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/if_ether.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
-
|
|
#include "mlxbf-bootctl.h"
|
|
|
|
-#define MLXBF_BOOTCTL_SB_SECURE_MASK 0x03
|
|
-#define MLXBF_BOOTCTL_SB_TEST_MASK 0x0c
|
|
+#define DRIVER_NAME "mlxbf-bootctl"
|
|
+#define DRIVER_VERSION "1.5"
|
|
+#define DRIVER_DESCRIPTION "Mellanox boot control driver"
|
|
+
|
|
+#define SB_MODE_SECURE_MASK 0x03
|
|
+#define SB_MODE_TEST_MASK 0x0c
|
|
+#define SB_MODE_DEV_MASK 0x10
|
|
|
|
-#define MLXBF_SB_KEY_NUM 4
|
|
+#define SB_KEY_NUM 4
|
|
+
|
|
+struct boot_name {
|
|
+ int value;
|
|
+ const char name[12];
|
|
+};
|
|
|
|
-/* UUID used to probe ATF service. */
|
|
-static const char *mlxbf_bootctl_svc_uuid_str =
|
|
- "89c036b4-e7d7-11e6-8797-001aca00bfc4";
|
|
+static struct boot_name boot_names[] = {
|
|
+ { MLNX_BOOT_EXTERNAL, "external" },
|
|
+ { MLNX_BOOT_EMMC, "emmc" },
|
|
+ { MLNX_BOOT_SWAP_EMMC, "swap_emmc" },
|
|
+ { MLNX_BOOT_EMMC_LEGACY, "emmc_legacy" },
|
|
+ { MLNX_BOOT_NONE, "none" },
|
|
+ { -1, "" }
|
|
+};
|
|
|
|
-struct mlxbf_bootctl_name {
|
|
- u32 value;
|
|
- const char *name;
|
|
+enum {
|
|
+ SB_LIFECYCLE_PRODUCTION = 0,
|
|
+ SB_LIFECYCLE_GA_SECURE = 1,
|
|
+ SB_LIFECYCLE_GA_NON_SECURE = 2,
|
|
+ SB_LIFECYCLE_RMA = 3
|
|
};
|
|
|
|
-static struct mlxbf_bootctl_name boot_names[] = {
|
|
- { MLXBF_BOOTCTL_EXTERNAL, "external" },
|
|
- { MLXBF_BOOTCTL_EMMC, "emmc" },
|
|
- { MLNX_BOOTCTL_SWAP_EMMC, "swap_emmc" },
|
|
- { MLXBF_BOOTCTL_EMMC_LEGACY, "emmc_legacy" },
|
|
- { MLXBF_BOOTCTL_NONE, "none" },
|
|
+static char lifecycle_states[][16] = {
|
|
+ [SB_LIFECYCLE_PRODUCTION] = "Production",
|
|
+ [SB_LIFECYCLE_GA_SECURE] = "GA Secured",
|
|
+ [SB_LIFECYCLE_GA_NON_SECURE] = "GA Non-Secured",
|
|
+ [SB_LIFECYCLE_RMA] = "RMA",
|
|
};
|
|
|
|
-static const char * const mlxbf_bootctl_lifecycle_states[] = {
|
|
- [0] = "Production",
|
|
- [1] = "GA Secured",
|
|
- [2] = "GA Non-Secured",
|
|
- [3] = "RMA",
|
|
+/* ctl/data register within the resource. */
|
|
+#define RSH_SCRATCH_BUF_CTL_OFF 0
|
|
+#define RSH_SCRATCH_BUF_DATA_OFF 0x10
|
|
+
|
|
+static void __iomem *rsh_boot_data;
|
|
+static void __iomem *rsh_boot_cnt;
|
|
+static void __iomem *rsh_semaphore;
|
|
+static void __iomem *rsh_scratch_buf_ctl;
|
|
+static void __iomem *rsh_scratch_buf_data;
|
|
+
|
|
+static int rsh_log_clear_on_read;
|
|
+module_param(rsh_log_clear_on_read, int, 0644);
|
|
+MODULE_PARM_DESC(rsh_log_clear_on_read, "Clear rshim logging buffer after read.");
|
|
+
|
|
+/*
|
|
+ * Objects are stored within the MFG partition per type. Type 0 is not
|
|
+ * supported.
|
|
+ */
|
|
+enum {
|
|
+ MLNX_MFG_TYPE_OOB_MAC = 1,
|
|
+ MLNX_MFG_TYPE_OPN_0,
|
|
+ MLNX_MFG_TYPE_OPN_1,
|
|
+ MLNX_MFG_TYPE_OPN_2,
|
|
+ MLNX_MFG_TYPE_SKU_0,
|
|
+ MLNX_MFG_TYPE_SKU_1,
|
|
+ MLNX_MFG_TYPE_SKU_2,
|
|
+ MLNX_MFG_TYPE_MODL_0,
|
|
+ MLNX_MFG_TYPE_MODL_1,
|
|
+ MLNX_MFG_TYPE_MODL_2,
|
|
+ MLNX_MFG_TYPE_SN_0,
|
|
+ MLNX_MFG_TYPE_SN_1,
|
|
+ MLNX_MFG_TYPE_SN_2,
|
|
+ MLNX_MFG_TYPE_UUID_0,
|
|
+ MLNX_MFG_TYPE_UUID_1,
|
|
+ MLNX_MFG_TYPE_UUID_2,
|
|
+ MLNX_MFG_TYPE_UUID_3,
|
|
+ MLNX_MFG_TYPE_UUID_4,
|
|
+ MLNX_MFG_TYPE_REV,
|
|
};
|
|
|
|
-/* ARM SMC call which is atomic and no need for lock. */
|
|
-static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg)
|
|
+/* This mutex is used to serialize MFG write and lock operations. */
|
|
+static DEFINE_MUTEX(mfg_ops_lock);
|
|
+
|
|
+#define MLNX_MFG_OOB_MAC_LEN ETH_ALEN
|
|
+#define MLNX_MFG_OPN_VAL_LEN 24
|
|
+#define MLNX_MFG_SKU_VAL_LEN 24
|
|
+#define MLNX_MFG_MODL_VAL_LEN 24
|
|
+#define MLNX_MFG_SN_VAL_LEN 24
|
|
+#define MLNX_MFG_UUID_VAL_LEN 40
|
|
+#define MLNX_MFG_REV_VAL_LEN 8
|
|
+#define MLNX_MFG_VAL_QWORD_CNT(type) \
|
|
+ (MLNX_MFG_##type##_VAL_LEN / sizeof(u64))
|
|
+
|
|
+/*
|
|
+ * The MAC address consists of 6 bytes (2 digits each) separated by ':'.
|
|
+ * The expected format is: "XX:XX:XX:XX:XX:XX"
|
|
+ */
|
|
+#define MLNX_MFG_OOB_MAC_FORMAT_LEN \
|
|
+ ((MLNX_MFG_OOB_MAC_LEN * 2) + (MLNX_MFG_OOB_MAC_LEN - 1))
|
|
+
|
|
+/* The SMC calls in question are atomic, so we don't have to lock here. */
|
|
+static int smc_call1(unsigned int smc_op, int smc_arg)
|
|
{
|
|
struct arm_smccc_res res;
|
|
|
|
@@ -54,268 +132,1212 @@ static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg)
|
|
return res.a0;
|
|
}
|
|
|
|
-/* Return the action in integer or an error code. */
|
|
-static int mlxbf_bootctl_reset_action_to_val(const char *action)
|
|
+/* Syntactic sugar to avoid having to specify an unused argument. */
|
|
+#define smc_call0(smc_op) smc_call1(smc_op, 0)
|
|
+
|
|
+static int reset_action_to_val(const char *action, size_t len)
|
|
{
|
|
- int i;
|
|
+ struct boot_name *bn;
|
|
+
|
|
+ /* Accept string either with or without a newline terminator */
|
|
+ if (action[len-1] == '\n')
|
|
+ --len;
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(boot_names); i++)
|
|
- if (sysfs_streq(boot_names[i].name, action))
|
|
- return boot_names[i].value;
|
|
+ for (bn = boot_names; bn->value >= 0; ++bn)
|
|
+ if (strncmp(bn->name, action, len) == 0)
|
|
+ break;
|
|
|
|
- return -EINVAL;
|
|
+ return bn->value;
|
|
}
|
|
|
|
-/* Return the action in string. */
|
|
-static const char *mlxbf_bootctl_action_to_string(int action)
|
|
+static const char *reset_action_to_string(int action)
|
|
{
|
|
- int i;
|
|
+ struct boot_name *bn;
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(boot_names); i++)
|
|
- if (boot_names[i].value == action)
|
|
- return boot_names[i].name;
|
|
+ for (bn = boot_names; bn->value >= 0; ++bn)
|
|
+ if (bn->value == action)
|
|
+ break;
|
|
|
|
- return "invalid action";
|
|
+ return bn->name;
|
|
}
|
|
|
|
-static ssize_t post_reset_wdog_show(struct device *dev,
|
|
- struct device_attribute *attr, char *buf)
|
|
+static ssize_t post_reset_wdog_show(struct device_driver *drv,
|
|
+ char *buf)
|
|
{
|
|
- int ret;
|
|
+ return snprintf(buf, PAGE_SIZE, "%d\n",
|
|
+ smc_call0(MLNX_GET_POST_RESET_WDOG));
|
|
+}
|
|
|
|
- ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_POST_RESET_WDOG, 0);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
+static ssize_t post_reset_wdog_store(struct device_driver *drv,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ int err;
|
|
+ unsigned long watchdog;
|
|
+
|
|
+ err = kstrtoul(buf, 10, &watchdog);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (smc_call1(MLNX_SET_POST_RESET_WDOG, watchdog) < 0)
|
|
+ return -EINVAL;
|
|
|
|
- return sprintf(buf, "%d\n", ret);
|
|
+ return count;
|
|
}
|
|
|
|
-static ssize_t post_reset_wdog_store(struct device *dev,
|
|
- struct device_attribute *attr,
|
|
- const char *buf, size_t count)
|
|
+static ssize_t reset_action_show(struct device_driver *drv,
|
|
+ char *buf)
|
|
{
|
|
- unsigned long value;
|
|
- int ret;
|
|
+ return snprintf(buf, PAGE_SIZE, "%s\n", reset_action_to_string(
|
|
+ smc_call0(MLNX_GET_RESET_ACTION)));
|
|
+}
|
|
|
|
- ret = kstrtoul(buf, 10, &value);
|
|
- if (ret)
|
|
- return ret;
|
|
+static ssize_t reset_action_store(struct device_driver *drv,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ int action = reset_action_to_val(buf, count);
|
|
|
|
- ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_POST_RESET_WDOG, value);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
+ if (action < 0 || action == MLNX_BOOT_NONE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (smc_call1(MLNX_SET_RESET_ACTION, action) < 0)
|
|
+ return -EINVAL;
|
|
|
|
return count;
|
|
}
|
|
|
|
-static ssize_t mlxbf_bootctl_show(int smc_op, char *buf)
|
|
+static ssize_t second_reset_action_show(struct device_driver *drv,
|
|
+ char *buf)
|
|
{
|
|
- int action;
|
|
+ return snprintf(buf, PAGE_SIZE, "%s\n", reset_action_to_string(
|
|
+ smc_call0(MLNX_GET_SECOND_RESET_ACTION)));
|
|
+}
|
|
+
|
|
+static ssize_t second_reset_action_store(struct device_driver *drv,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ int action = reset_action_to_val(buf, count);
|
|
|
|
- action = mlxbf_bootctl_smc(smc_op, 0);
|
|
if (action < 0)
|
|
- return action;
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (smc_call1(MLNX_SET_SECOND_RESET_ACTION, action) < 0)
|
|
+ return -EINVAL;
|
|
|
|
- return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action));
|
|
+ return count;
|
|
}
|
|
|
|
-static int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count)
|
|
+static ssize_t lifecycle_state_show(struct device_driver *drv,
|
|
+ char *buf)
|
|
{
|
|
- int ret, action;
|
|
+ int lc_state = smc_call1(MLNX_GET_TBB_FUSE_STATUS,
|
|
+ MLNX_FUSE_STATUS_LIFECYCLE);
|
|
|
|
- action = mlxbf_bootctl_reset_action_to_val(buf);
|
|
- if (action < 0)
|
|
- return action;
|
|
+ if (lc_state < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ lc_state &= (SB_MODE_TEST_MASK |
|
|
+ SB_MODE_SECURE_MASK |
|
|
+ SB_MODE_DEV_MASK);
|
|
|
|
- ret = mlxbf_bootctl_smc(smc_op, action);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
+ /*
|
|
+ * If the test bits are set, we specify that the current state may be
|
|
+ * due to using the test bits.
|
|
+ */
|
|
+ if ((lc_state & SB_MODE_TEST_MASK) != 0) {
|
|
+
|
|
+ lc_state &= SB_MODE_SECURE_MASK;
|
|
+
|
|
+ return snprintf(buf, PAGE_SIZE, "%s(test)\n",
|
|
+ lifecycle_states[lc_state]);
|
|
+ } else if ((lc_state & SB_MODE_SECURE_MASK) == SB_LIFECYCLE_GA_SECURE
|
|
+ && (lc_state & SB_MODE_DEV_MASK)) {
|
|
+ return snprintf(buf, PAGE_SIZE, "Secured (development)\n");
|
|
+ }
|
|
+
|
|
+ return snprintf(buf, PAGE_SIZE, "%s\n", lifecycle_states[lc_state]);
|
|
+}
|
|
+
|
|
+static ssize_t secure_boot_fuse_state_show(struct device_driver *drv,
|
|
+ char *buf)
|
|
+{
|
|
+ int key;
|
|
+ int buf_len = 0;
|
|
+ int upper_key_used = 0;
|
|
+ int sb_key_state = smc_call1(MLNX_GET_TBB_FUSE_STATUS,
|
|
+ MLNX_FUSE_STATUS_KEYS);
|
|
+
|
|
+ if (sb_key_state < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ for (key = SB_KEY_NUM - 1; key >= 0; key--) {
|
|
+ int burnt = ((sb_key_state & (1 << key)) != 0);
|
|
+ int valid = ((sb_key_state & (1 << (key + SB_KEY_NUM))) != 0);
|
|
+
|
|
+ buf_len += sprintf(buf + buf_len, "Ver%d:", key);
|
|
+ if (upper_key_used) {
|
|
+ if (burnt) {
|
|
+ if (valid)
|
|
+ buf_len += sprintf(buf + buf_len,
|
|
+ "Used");
|
|
+ else
|
|
+ buf_len += sprintf(buf + buf_len,
|
|
+ "Wasted");
|
|
+ } else {
|
|
+ if (valid)
|
|
+ buf_len += sprintf(buf + buf_len,
|
|
+ "Invalid");
|
|
+ else
|
|
+ buf_len += sprintf(buf + buf_len,
|
|
+ "Skipped");
|
|
+ }
|
|
+ } else {
|
|
+ if (burnt) {
|
|
+ if (valid) {
|
|
+ upper_key_used = 1;
|
|
+ buf_len += sprintf(buf + buf_len,
|
|
+ "In use");
|
|
+ } else
|
|
+ buf_len += sprintf(buf + buf_len,
|
|
+ "Burn incomplete");
|
|
+ } else {
|
|
+ if (valid)
|
|
+ buf_len += sprintf(buf + buf_len,
|
|
+ "Invalid");
|
|
+ else
|
|
+ buf_len += sprintf(buf + buf_len,
|
|
+ "Free");
|
|
+ }
|
|
+ }
|
|
+ buf_len += sprintf(buf + buf_len, "\n");
|
|
+ }
|
|
+
|
|
+ return buf_len;
|
|
+}
|
|
+
|
|
+static ssize_t fw_reset_store(struct device_driver *drv,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ int err;
|
|
+ unsigned long key;
|
|
+
|
|
+ err = kstrtoul(buf, 16, &key);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (smc_call1(MLNX_HANDLE_FW_RESET, key) < 0)
|
|
+ return -EINVAL;
|
|
|
|
return count;
|
|
}
|
|
|
|
-static ssize_t reset_action_show(struct device *dev,
|
|
- struct device_attribute *attr, char *buf)
|
|
+static ssize_t oob_mac_show(struct device_driver *drv, char *buf)
|
|
{
|
|
- return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_RESET_ACTION, buf);
|
|
+ char mac_str[MLNX_MFG_OOB_MAC_FORMAT_LEN + 1] = { 0 };
|
|
+ struct arm_smccc_res res;
|
|
+ u8 *mac_byte_ptr;
|
|
+
|
|
+ mutex_lock(&mfg_ops_lock);
|
|
+ arm_smccc_smc(MLNX_HANDLE_GET_MFG_INFO, MLNX_MFG_TYPE_OOB_MAC, 0, 0, 0,
|
|
+ 0, 0, 0, &res);
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ if (res.a0)
|
|
+ return -EPERM;
|
|
+
|
|
+ mac_byte_ptr = (u8 *)&res.a1;
|
|
+
|
|
+ sprintf(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X",
|
|
+ mac_byte_ptr[0], mac_byte_ptr[1], mac_byte_ptr[2],
|
|
+ mac_byte_ptr[3], mac_byte_ptr[4], mac_byte_ptr[5]);
|
|
+
|
|
+ return snprintf(buf, PAGE_SIZE, "%s", mac_str);
|
|
}
|
|
|
|
-static ssize_t reset_action_store(struct device *dev,
|
|
- struct device_attribute *attr,
|
|
- const char *buf, size_t count)
|
|
+static ssize_t oob_mac_store(struct device_driver *drv, const char *buf,
|
|
+ size_t count)
|
|
+{
|
|
+ int byte[MLNX_MFG_OOB_MAC_FORMAT_LEN] = { 0 };
|
|
+ struct arm_smccc_res res;
|
|
+ u64 mac_addr = 0;
|
|
+ u8 *mac_byte_ptr;
|
|
+ int byte_idx, len;
|
|
+
|
|
+ if ((count - 1) != MLNX_MFG_OOB_MAC_FORMAT_LEN)
|
|
+ return -EINVAL;
|
|
+
|
|
+ len = sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
|
|
+ &byte[0], &byte[1], &byte[2],
|
|
+ &byte[3], &byte[4], &byte[5]);
|
|
+ if (len != MLNX_MFG_OOB_MAC_LEN)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mac_byte_ptr = (u8 *)&mac_addr;
|
|
+
|
|
+ for (byte_idx = 0; byte_idx < MLNX_MFG_OOB_MAC_LEN; byte_idx++)
|
|
+ mac_byte_ptr[byte_idx] = (u8) byte[byte_idx];
|
|
+
|
|
+ mutex_lock(&mfg_ops_lock);
|
|
+ arm_smccc_smc(MLNX_HANDLE_SET_MFG_INFO, MLNX_MFG_TYPE_OOB_MAC,
|
|
+ MLNX_MFG_OOB_MAC_LEN, mac_addr, 0, 0, 0, 0, &res);
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+
|
|
+ return res.a0 ? -EPERM : count;
|
|
+}
|
|
+
|
|
+static ssize_t opn_show(struct device_driver *drv, char *buf)
|
|
{
|
|
- return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_RESET_ACTION, buf, count);
|
|
+ u64 opn_data[MLNX_MFG_VAL_QWORD_CNT(OPN)] = { 0 };
|
|
+ char opn[MLNX_MFG_OPN_VAL_LEN + 1] = { 0 };
|
|
+ struct arm_smccc_res res;
|
|
+ int word;
|
|
+
|
|
+ mutex_lock(&mfg_ops_lock);
|
|
+ for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(OPN); word++) {
|
|
+ arm_smccc_smc(MLNX_HANDLE_GET_MFG_INFO,
|
|
+ MLNX_MFG_TYPE_OPN_0 + word,
|
|
+ 0, 0, 0, 0, 0, 0, &res);
|
|
+ if (res.a0) {
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ return -EPERM;
|
|
+ }
|
|
+ opn_data[word] = res.a1;
|
|
+ }
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ memcpy(opn, opn_data, MLNX_MFG_OPN_VAL_LEN);
|
|
+
|
|
+ return snprintf(buf, PAGE_SIZE, "%s", opn);
|
|
}
|
|
|
|
-static ssize_t second_reset_action_show(struct device *dev,
|
|
- struct device_attribute *attr,
|
|
- char *buf)
|
|
+static ssize_t opn_store(struct device_driver *drv, const char *buf,
|
|
+ size_t count)
|
|
{
|
|
- return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION, buf);
|
|
+ u64 opn[MLNX_MFG_VAL_QWORD_CNT(OPN)] = { 0 };
|
|
+ struct arm_smccc_res res;
|
|
+ int word;
|
|
+
|
|
+ if (count > MLNX_MFG_OPN_VAL_LEN)
|
|
+ return -EINVAL;
|
|
+
|
|
+ memcpy(opn, buf, count);
|
|
+
|
|
+ mutex_lock(&mfg_ops_lock);
|
|
+ for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(OPN); word++) {
|
|
+ arm_smccc_smc(MLNX_HANDLE_SET_MFG_INFO,
|
|
+ MLNX_MFG_TYPE_OPN_0 + word,
|
|
+ sizeof(u64), opn[word], 0, 0, 0, 0, &res);
|
|
+ if (res.a0) {
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ return -EPERM;
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+
|
|
+ return count;
|
|
}
|
|
|
|
-static ssize_t second_reset_action_store(struct device *dev,
|
|
- struct device_attribute *attr,
|
|
- const char *buf, size_t count)
|
|
+static ssize_t sku_show(struct device_driver *drv, char *buf)
|
|
{
|
|
- return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION, buf,
|
|
- count);
|
|
+ u64 sku_data[MLNX_MFG_VAL_QWORD_CNT(SKU)] = { 0 };
|
|
+ char sku[MLNX_MFG_SKU_VAL_LEN + 1] = { 0 };
|
|
+ struct arm_smccc_res res;
|
|
+ int word;
|
|
+
|
|
+ mutex_lock(&mfg_ops_lock);
|
|
+ for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SKU); word++) {
|
|
+ arm_smccc_smc(MLNX_HANDLE_GET_MFG_INFO,
|
|
+ MLNX_MFG_TYPE_SKU_0 + word,
|
|
+ 0, 0, 0, 0, 0, 0, &res);
|
|
+ if (res.a0) {
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ return -EPERM;
|
|
+ }
|
|
+ sku_data[word] = res.a1;
|
|
+ }
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ memcpy(sku, sku_data, MLNX_MFG_SKU_VAL_LEN);
|
|
+
|
|
+ return snprintf(buf, PAGE_SIZE, "%s", sku);
|
|
}
|
|
|
|
-static ssize_t lifecycle_state_show(struct device *dev,
|
|
- struct device_attribute *attr, char *buf)
|
|
+static ssize_t sku_store(struct device_driver *drv, const char *buf,
|
|
+ size_t count)
|
|
{
|
|
- int lc_state;
|
|
+ u64 sku[MLNX_MFG_VAL_QWORD_CNT(SKU)] = { 0 };
|
|
+ struct arm_smccc_res res;
|
|
+ int word;
|
|
|
|
- lc_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
|
|
- MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
|
|
- if (lc_state < 0)
|
|
- return lc_state;
|
|
+ if (count > MLNX_MFG_SKU_VAL_LEN)
|
|
+ return -EINVAL;
|
|
|
|
- lc_state &=
|
|
- MLXBF_BOOTCTL_SB_TEST_MASK | MLXBF_BOOTCTL_SB_SECURE_MASK;
|
|
+ memcpy(sku, buf, count);
|
|
|
|
- /*
|
|
- * If the test bits are set, we specify that the current state may be
|
|
- * due to using the test bits.
|
|
- */
|
|
- if (lc_state & MLXBF_BOOTCTL_SB_TEST_MASK) {
|
|
- lc_state &= MLXBF_BOOTCTL_SB_SECURE_MASK;
|
|
+ mutex_lock(&mfg_ops_lock);
|
|
+ for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SKU); word++) {
|
|
+ arm_smccc_smc(MLNX_HANDLE_SET_MFG_INFO,
|
|
+ MLNX_MFG_TYPE_SKU_0 + word,
|
|
+ sizeof(u64), sku[word], 0, 0, 0, 0, &res);
|
|
+ if (res.a0) {
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ return -EPERM;
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
|
|
- return sprintf(buf, "%s(test)\n",
|
|
- mlxbf_bootctl_lifecycle_states[lc_state]);
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t modl_show(struct device_driver *drv, char *buf)
|
|
+{
|
|
+ u64 modl_data[MLNX_MFG_VAL_QWORD_CNT(MODL)] = { 0 };
|
|
+ char modl[MLNX_MFG_MODL_VAL_LEN + 1] = { 0 };
|
|
+ struct arm_smccc_res res;
|
|
+ int word;
|
|
+
|
|
+ mutex_lock(&mfg_ops_lock);
|
|
+ for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(MODL); word++) {
|
|
+ arm_smccc_smc(MLNX_HANDLE_GET_MFG_INFO,
|
|
+ MLNX_MFG_TYPE_MODL_0 + word,
|
|
+ 0, 0, 0, 0, 0, 0, &res);
|
|
+ if (res.a0) {
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ return -EPERM;
|
|
+ }
|
|
+ modl_data[word] = res.a1;
|
|
}
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ memcpy(modl, modl_data, MLNX_MFG_MODL_VAL_LEN);
|
|
|
|
- return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
|
|
+ return snprintf(buf, PAGE_SIZE, "%s", modl);
|
|
}
|
|
|
|
-static ssize_t secure_boot_fuse_state_show(struct device *dev,
|
|
- struct device_attribute *attr,
|
|
- char *buf)
|
|
+static ssize_t modl_store(struct device_driver *drv, const char *buf,
|
|
+ size_t count)
|
|
{
|
|
- int burnt, valid, key, key_state, buf_len = 0, upper_key_used = 0;
|
|
- const char *status;
|
|
+ u64 modl[MLNX_MFG_VAL_QWORD_CNT(MODL)] = { 0 };
|
|
+ struct arm_smccc_res res;
|
|
+ int word;
|
|
+
|
|
+ if (count > MLNX_MFG_MODL_VAL_LEN)
|
|
+ return -EINVAL;
|
|
+
|
|
+ memcpy(modl, buf, count);
|
|
+
|
|
+ mutex_lock(&mfg_ops_lock);
|
|
+ for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(MODL); word++) {
|
|
+ arm_smccc_smc(MLNX_HANDLE_SET_MFG_INFO,
|
|
+ MLNX_MFG_TYPE_MODL_0 + word,
|
|
+ sizeof(u64), modl[word], 0, 0, 0, 0, &res);
|
|
+ if (res.a0) {
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ return -EPERM;
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t sn_show(struct device_driver *drv, char *buf)
|
|
+{
|
|
+ u64 sn_data[MLNX_MFG_VAL_QWORD_CNT(SN)] = { 0 };
|
|
+ char sn[MLNX_MFG_SN_VAL_LEN + 1] = { 0 };
|
|
+ struct arm_smccc_res res;
|
|
+ int word;
|
|
+
|
|
+ mutex_lock(&mfg_ops_lock);
|
|
+ for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SN); word++) {
|
|
+ arm_smccc_smc(MLNX_HANDLE_GET_MFG_INFO,
|
|
+ MLNX_MFG_TYPE_SN_0 + word,
|
|
+ 0, 0, 0, 0, 0, 0, &res);
|
|
+ if (res.a0) {
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ return -EPERM;
|
|
+ }
|
|
+ sn_data[word] = res.a1;
|
|
+ }
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ memcpy(sn, sn_data, MLNX_MFG_SN_VAL_LEN);
|
|
+
|
|
+ return snprintf(buf, PAGE_SIZE, "%s", sn);
|
|
+}
|
|
+
|
|
+static ssize_t sn_store(struct device_driver *drv, const char *buf,
|
|
+ size_t count)
|
|
+{
|
|
+ u64 sn[MLNX_MFG_VAL_QWORD_CNT(SN)] = { 0 };
|
|
+ struct arm_smccc_res res;
|
|
+ int word;
|
|
+
|
|
+ if (count > MLNX_MFG_SN_VAL_LEN)
|
|
+ return -EINVAL;
|
|
+
|
|
+ memcpy(sn, buf, count);
|
|
+
|
|
+ mutex_lock(&mfg_ops_lock);
|
|
+ for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SN); word++) {
|
|
+ arm_smccc_smc(MLNX_HANDLE_SET_MFG_INFO,
|
|
+ MLNX_MFG_TYPE_SN_0 + word,
|
|
+ sizeof(u64), sn[word], 0, 0, 0, 0, &res);
|
|
+ if (res.a0) {
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ return -EPERM;
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t uuid_show(struct device_driver *drv, char *buf)
|
|
+{
|
|
+ u64 uuid_data[MLNX_MFG_VAL_QWORD_CNT(UUID)] = { 0 };
|
|
+ char uuid[MLNX_MFG_UUID_VAL_LEN + 1] = { 0 };
|
|
+ struct arm_smccc_res res;
|
|
+ int word;
|
|
+
|
|
+ mutex_lock(&mfg_ops_lock);
|
|
+ for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(UUID); word++) {
|
|
+ arm_smccc_smc(MLNX_HANDLE_GET_MFG_INFO,
|
|
+ MLNX_MFG_TYPE_UUID_0 + word,
|
|
+ 0, 0, 0, 0, 0, 0, &res);
|
|
+ if (res.a0) {
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ return -EPERM;
|
|
+ }
|
|
+ uuid_data[word] = res.a1;
|
|
+ }
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ memcpy(uuid, uuid_data, MLNX_MFG_UUID_VAL_LEN);
|
|
+
|
|
+ return snprintf(buf, PAGE_SIZE, "%s", uuid);
|
|
+}
|
|
+
|
|
+static ssize_t uuid_store(struct device_driver *drv, const char *buf,
|
|
+ size_t count)
|
|
+{
|
|
+ u64 uuid[MLNX_MFG_VAL_QWORD_CNT(UUID)] = { 0 };
|
|
+ struct arm_smccc_res res;
|
|
+ int word;
|
|
|
|
- key_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
|
|
- MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
|
|
- if (key_state < 0)
|
|
- return key_state;
|
|
+ if (count > MLNX_MFG_UUID_VAL_LEN)
|
|
+ return -EINVAL;
|
|
+
|
|
+ memcpy(uuid, buf, count);
|
|
+
|
|
+ mutex_lock(&mfg_ops_lock);
|
|
+ for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(UUID); word++) {
|
|
+ arm_smccc_smc(MLNX_HANDLE_SET_MFG_INFO,
|
|
+ MLNX_MFG_TYPE_UUID_0 + word,
|
|
+ sizeof(u64), uuid[word], 0, 0, 0, 0, &res);
|
|
+ if (res.a0) {
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ return -EPERM;
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t rev_show(struct device_driver *drv, char *buf)
|
|
+{
|
|
+ u64 rev_data[MLNX_MFG_VAL_QWORD_CNT(REV)] = { 0 };
|
|
+ char rev[MLNX_MFG_REV_VAL_LEN + 1] = { 0 };
|
|
+ struct arm_smccc_res res;
|
|
+ int word;
|
|
+
|
|
+ mutex_lock(&mfg_ops_lock);
|
|
+ for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(REV); word++) {
|
|
+ arm_smccc_smc(MLNX_HANDLE_GET_MFG_INFO,
|
|
+ MLNX_MFG_TYPE_REV + word,
|
|
+ 0, 0, 0, 0, 0, 0, &res);
|
|
+ if (res.a0) {
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ return -EPERM;
|
|
+ }
|
|
+ rev_data[word] = res.a1;
|
|
+ }
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ memcpy(rev, rev_data, MLNX_MFG_REV_VAL_LEN);
|
|
+
|
|
+ return snprintf(buf, PAGE_SIZE, "%s", rev);
|
|
+}
|
|
+
|
|
+static ssize_t rev_store(struct device_driver *drv, const char *buf,
|
|
+ size_t count)
|
|
+{
|
|
+ u64 rev[MLNX_MFG_VAL_QWORD_CNT(REV)] = { 0 };
|
|
+ struct arm_smccc_res res;
|
|
+ int word;
|
|
+
|
|
+ if (count > MLNX_MFG_REV_VAL_LEN)
|
|
+ return -EINVAL;
|
|
+
|
|
+ memcpy(rev, buf, count);
|
|
+
|
|
+ mutex_lock(&mfg_ops_lock);
|
|
+ for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(REV); word++) {
|
|
+ arm_smccc_smc(MLNX_HANDLE_SET_MFG_INFO,
|
|
+ MLNX_MFG_TYPE_REV + word,
|
|
+ sizeof(u64), rev[word], 0, 0, 0, 0, &res);
|
|
+ if (res.a0) {
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+ return -EPERM;
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t mfg_lock_store(struct device_driver *drv, const char *buf,
|
|
+ size_t count)
|
|
+{
|
|
+ unsigned long val;
|
|
+ int err;
|
|
+
|
|
+ err = kstrtoul(buf, 10, &val);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (val != 1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&mfg_ops_lock);
|
|
+ smc_call0(MLNX_HANDLE_LOCK_MFG_INFO);
|
|
+ mutex_unlock(&mfg_ops_lock);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+/* Log header format. */
|
|
+#define RSH_LOG_TYPE_SHIFT 56
|
|
+#define RSH_LOG_LEN_SHIFT 48
|
|
+#define RSH_LOG_LEVEL_SHIFT 0
|
|
+
|
|
+/* Module ID and type used here. */
|
|
+#define BF_RSH_LOG_TYPE_UNKNOWN 0x00ULL
|
|
+#define BF_RSH_LOG_TYPE_PANIC 0x01ULL
|
|
+#define BF_RSH_LOG_TYPE_EXCEPTION 0x02ULL
|
|
+#define BF_RSH_LOG_TYPE_UNUSED 0x03ULL
|
|
+#define BF_RSH_LOG_TYPE_MSG 0x04ULL
|
|
+
|
|
+/* Utility macro. */
|
|
+#define BF_RSH_LOG_MOD_MASK 0x0FULL
|
|
+#define BF_RSH_LOG_MOD_SHIFT 60
|
|
+#define BF_RSH_LOG_TYPE_MASK 0x0FULL
|
|
+#define BF_RSH_LOG_TYPE_SHIFT 56
|
|
+#define BF_RSH_LOG_LEN_MASK 0x7FULL
|
|
+#define BF_RSH_LOG_LEN_SHIFT 48
|
|
+#define BF_RSH_LOG_ARG_MASK 0xFFFFFFFFULL
|
|
+#define BF_RSH_LOG_ARG_SHIFT 16
|
|
+#define BF_RSH_LOG_HAS_ARG_MASK 0xFFULL
|
|
+#define BF_RSH_LOG_HAS_ARG_SHIFT 8
|
|
+#define BF_RSH_LOG_LEVEL_MASK 0xFFULL
|
|
+#define BF_RSH_LOG_LEVEL_SHIFT 0
|
|
+#define BF_RSH_LOG_PC_MASK 0xFFFFFFFFULL
|
|
+#define BF_RSH_LOG_PC_SHIFT 0
|
|
+#define BF_RSH_LOG_SYNDROME_MASK 0xFFFFFFFFULL
|
|
+#define BF_RSH_LOG_SYNDROME_SHIFT 0
|
|
+
|
|
+#define BF_RSH_LOG_HEADER_GET(f, h) \
|
|
+ (((h) >> BF_RSH_LOG_##f##_SHIFT) & BF_RSH_LOG_##f##_MASK)
|
|
+
|
|
+/* Log message level. */
|
|
+enum {
|
|
+ RSH_LOG_INFO,
|
|
+ RSH_LOG_WARN,
|
|
+ RSH_LOG_ERR
|
|
+};
|
|
+
|
|
+/* Log module */
|
|
+const char * const rsh_log_mod[] = {
|
|
+ "MISC", "BL1", "BL2", "BL2R", "BL31", "UEFI"
|
|
+};
|
|
+
|
|
+const char *rsh_log_level[] = {"INFO", "WARN", "ERR", "ASSERT"};
|
|
+
|
|
+#define AARCH64_MRS_REG_SHIFT 5
|
|
+#define AARCH64_MRS_REG_MASK 0xffff
|
|
+#define AARCH64_ESR_ELX_EXCEPTION_CLASS_SHIFT 26
|
|
+
|
|
+struct rsh_log_reg {
|
|
+ char *name;
|
|
+ u32 opcode;
|
|
+} rsh_log_reg;
|
|
+
|
|
+static struct rsh_log_reg rsh_log_regs[] = {
|
|
+ {"actlr_el1", 0b1100000010000001},
|
|
+ {"actlr_el2", 0b1110000010000001},
|
|
+ {"actlr_el3", 0b1111000010000001},
|
|
+ {"afsr0_el1", 0b1100001010001000},
|
|
+ {"afsr0_el2", 0b1110001010001000},
|
|
+ {"afsr0_el3", 0b1111001010001000},
|
|
+ {"afsr1_el1", 0b1100001010001001},
|
|
+ {"afsr1_el2", 0b1110001010001001},
|
|
+ {"afsr1_el3", 0b1111001010001001},
|
|
+ {"amair_el1", 0b1100010100011000},
|
|
+ {"amair_el2", 0b1110010100011000},
|
|
+ {"amair_el3", 0b1111010100011000},
|
|
+ {"ccsidr_el1", 0b1100100000000000},
|
|
+ {"clidr_el1", 0b1100100000000001},
|
|
+ {"cntkctl_el1", 0b1100011100001000},
|
|
+ {"cntp_ctl_el0", 0b1101111100010001},
|
|
+ {"cntp_cval_el0", 0b1101111100010010},
|
|
+ {"cntv_ctl_el0", 0b1101111100011001},
|
|
+ {"cntv_cval_el0", 0b1101111100011010},
|
|
+ {"contextidr_el1", 0b1100011010000001},
|
|
+ {"cpacr_el1", 0b1100000010000010},
|
|
+ {"cptr_el2", 0b1110000010001010},
|
|
+ {"cptr_el3", 0b1111000010001010},
|
|
+ {"vtcr_el2", 0b1110000100001010},
|
|
+ {"ctr_el0", 0b1101100000000001},
|
|
+ {"currentel", 0b1100001000010010},
|
|
+ {"dacr32_el2", 0b1110000110000000},
|
|
+ {"daif", 0b1101101000010001},
|
|
+ {"dczid_el0", 0b1101100000000111},
|
|
+ {"dlr_el0", 0b1101101000101001},
|
|
+ {"dspsr_el0", 0b1101101000101000},
|
|
+ {"elr_el1", 0b1100001000000001},
|
|
+ {"elr_el2", 0b1110001000000001},
|
|
+ {"elr_el3", 0b1111001000000001},
|
|
+ {"esr_el1", 0b1100001010010000},
|
|
+ {"esr_el2", 0b1110001010010000},
|
|
+ {"esr_el3", 0b1111001010010000},
|
|
+ {"esselr_el1", 0b1101000000000000},
|
|
+ {"far_el1", 0b1100001100000000},
|
|
+ {"far_el2", 0b1110001100000000},
|
|
+ {"far_el3", 0b1111001100000000},
|
|
+ {"fpcr", 0b1101101000100000},
|
|
+ {"fpexc32_el2", 0b1110001010011000},
|
|
+ {"fpsr", 0b1101101000100001},
|
|
+ {"hacr_el2", 0b1110000010001111},
|
|
+ {"har_el2", 0b1110000010001000},
|
|
+ {"hpfar_el2", 0b1110001100000100},
|
|
+ {"hstr_el2", 0b1110000010001011},
|
|
+ {"far_el1", 0b1100001100000000},
|
|
+ {"far_el2", 0b1110001100000000},
|
|
+ {"far_el3", 0b1111001100000000},
|
|
+ {"hcr_el2", 0b1110000010001000},
|
|
+ {"hpfar_el2", 0b1110001100000100},
|
|
+ {"id_aa64afr0_el1", 0b1100000000101100},
|
|
+ {"id_aa64afr1_el1", 0b1100000000101101},
|
|
+ {"id_aa64dfr0_el1", 0b1100000000101100},
|
|
+ {"id_aa64isar0_el1", 0b1100000000110000},
|
|
+ {"id_aa64isar1_el1", 0b1100000000110001},
|
|
+ {"id_aa64mmfr0_el1", 0b1100000000111000},
|
|
+ {"id_aa64mmfr1_el1", 0b1100000000111001},
|
|
+ {"id_aa64pfr0_el1", 0b1100000000100000},
|
|
+ {"id_aa64pfr1_el1", 0b1100000000100001},
|
|
+ {"ifsr32_el2", 0b1110001010000001},
|
|
+ {"isr_el1", 0b1100011000001000},
|
|
+ {"mair_el1", 0b1100010100010000},
|
|
+ {"mair_el2", 0b1110010100010000},
|
|
+ {"mair_el3", 0b1111010100010000},
|
|
+ {"midr_el1", 0b1100000000000000},
|
|
+ {"mpidr_el1", 0b1100000000000101},
|
|
+ {"nzcv", 0b1101101000010000},
|
|
+ {"revidr_el1", 0b1100000000000110},
|
|
+ {"rmr_el3", 0b1111011000000010},
|
|
+ {"par_el1", 0b1100001110100000},
|
|
+ {"rvbar_el3", 0b1111011000000001},
|
|
+ {"scr_el3", 0b1111000010001000},
|
|
+ {"sctlr_el1", 0b1100000010000000},
|
|
+ {"sctlr_el2", 0b1110000010000000},
|
|
+ {"sctlr_el3", 0b1111000010000000},
|
|
+ {"sp_el0", 0b1100001000001000},
|
|
+ {"sp_el1", 0b1110001000001000},
|
|
+ {"spsel", 0b1100001000010000},
|
|
+ {"spsr_abt", 0b1110001000011001},
|
|
+ {"spsr_el1", 0b1100001000000000},
|
|
+ {"spsr_el2", 0b1110001000000000},
|
|
+ {"spsr_el3", 0b1111001000000000},
|
|
+ {"spsr_fiq", 0b1110001000011011},
|
|
+ {"spsr_irq", 0b1110001000011000},
|
|
+ {"spsr_und", 0b1110001000011010},
|
|
+ {"tcr_el1", 0b1100000100000010},
|
|
+ {"tcr_el2", 0b1110000100000010},
|
|
+ {"tcr_el3", 0b1111000100000010},
|
|
+ {"tpidr_el0", 0b1101111010000010},
|
|
+ {"tpidr_el1", 0b1100011010000100},
|
|
+ {"tpidr_el2", 0b1110011010000010},
|
|
+ {"tpidr_el3", 0b1111011010000010},
|
|
+ {"tpidpro_el0", 0b1101111010000011},
|
|
+ {"vbar_el1", 0b1100011000000000},
|
|
+ {"vbar_el2", 0b1110011000000000},
|
|
+ {"vbar_el3", 0b1111011000000000},
|
|
+ {"vmpidr_el2", 0b1110000000000101},
|
|
+ {"vpidr_el2", 0b1110000000000000},
|
|
+ {"ttbr0_el1", 0b1100000100000000},
|
|
+ {"ttbr0_el2", 0b1110000100000000},
|
|
+ {"ttbr0_el3", 0b1111000100000000},
|
|
+ {"ttbr1_el1", 0b1100000100000001},
|
|
+ {"vtcr_el2", 0b1110000100001010},
|
|
+ {"vttbr_el2", 0b1110000100001000},
|
|
+ {NULL, 0b0000000000000000},
|
|
+};
|
|
+
|
|
+/* Size(8-byte words) of the log buffer. */
|
|
+#define RSH_SCRATCH_BUF_CTL_IDX_MASK 0x7f
|
|
+
|
|
+static int rsh_log_sem_lock(void)
|
|
+{
|
|
+ unsigned long timeout;
|
|
+
|
|
+ /* Take the semaphore. */
|
|
+ timeout = jiffies + msecs_to_jiffies(100);
|
|
+ while (readq(rsh_semaphore)) {
|
|
+ if (time_after(jiffies, timeout))
|
|
+ return -ETIMEDOUT;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void rsh_log_sem_unlock(void)
|
|
+{
|
|
+ writeq(0, rsh_semaphore);
|
|
+}
|
|
+
|
|
+static ssize_t rsh_log_store(struct device_driver *drv, const char *buf,
|
|
+ size_t count)
|
|
+{
|
|
+ int idx, num, len, size = (int)count, level = RSH_LOG_INFO, rc;
|
|
+ u64 data;
|
|
+
|
|
+ if (!size)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (!rsh_semaphore || !rsh_scratch_buf_ctl)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ /* Ignore line break at the end. */
|
|
+ if (buf[size-1] == 0xa)
|
|
+ size--;
|
|
+
|
|
+ /* Check the message prefix. */
|
|
+ for (idx = 0; idx < ARRAY_SIZE(rsh_log_level); idx++) {
|
|
+ len = strlen(rsh_log_level[idx]);
|
|
+ if (len + 1 < size && !strncmp(buf, rsh_log_level[idx], len)) {
|
|
+ buf += len + 1;
|
|
+ size -= len + 1;
|
|
+ level = idx;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Ignore leading spaces. */
|
|
+ while (size > 0 && buf[0] == ' ') {
|
|
+ size--;
|
|
+ buf++;
|
|
+ }
|
|
+
|
|
+ /* Take the semaphore. */
|
|
+ rc = rsh_log_sem_lock();
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ /* Calculate how many words are available. */
|
|
+ num = (size + sizeof(u64) - 1) / sizeof(u64);
|
|
+ idx = readq(rsh_scratch_buf_ctl);
|
|
+ if (idx + num + 1 >= RSH_SCRATCH_BUF_CTL_IDX_MASK)
|
|
+ num = RSH_SCRATCH_BUF_CTL_IDX_MASK - idx - 1;
|
|
+ if (num <= 0)
|
|
+ goto done;
|
|
+
|
|
+ /* Write Header. */
|
|
+ data = (BF_RSH_LOG_TYPE_MSG << RSH_LOG_TYPE_SHIFT) |
|
|
+ ((u64)num << RSH_LOG_LEN_SHIFT) |
|
|
+ ((u64)level << RSH_LOG_LEVEL_SHIFT);
|
|
+ writeq(data, rsh_scratch_buf_data);
|
|
+
|
|
+ /* Write message. */
|
|
+ for (idx = 0, len = size; idx < num && len > 0; idx++) {
|
|
+ if (len <= sizeof(u64)) {
|
|
+ data = 0;
|
|
+ memcpy(&data, buf, len);
|
|
+ len = 0;
|
|
+ } else {
|
|
+ memcpy(&data, buf, sizeof(u64));
|
|
+ len -= sizeof(u64);
|
|
+ buf += sizeof(u64);
|
|
+ }
|
|
+ writeq(data, rsh_scratch_buf_data);
|
|
+ }
|
|
+
|
|
+done:
|
|
+ /* Release the semaphore. */
|
|
+ rsh_log_sem_unlock();
|
|
+
|
|
+ /* Ignore the rest if no more space. */
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static char *rsh_log_get_reg_name(u64 opcode)
|
|
+{
|
|
+ struct rsh_log_reg *reg = rsh_log_regs;
|
|
+
|
|
+ while (reg->name) {
|
|
+ if (reg->opcode == opcode)
|
|
+ return reg->name;
|
|
+ reg++;
|
|
+ }
|
|
+
|
|
+ return "unknown";
|
|
+}
|
|
+
|
|
+static int rsh_log_show_crash(u64 hdr, char *buf, int size)
|
|
+{
|
|
+ int i, module, type, len, n = 0;
|
|
+ u32 pc, syndrome, ec;
|
|
+ u64 opcode, data;
|
|
+ char *p = buf;
|
|
+
|
|
+ module = BF_RSH_LOG_HEADER_GET(MOD, hdr);
|
|
+ if (module >= ARRAY_SIZE(rsh_log_mod))
|
|
+ module = 0;
|
|
+ type = BF_RSH_LOG_HEADER_GET(TYPE, hdr);
|
|
+ len = BF_RSH_LOG_HEADER_GET(LEN, hdr);
|
|
+
|
|
+ if (type == BF_RSH_LOG_TYPE_EXCEPTION) {
|
|
+ syndrome = BF_RSH_LOG_HEADER_GET(SYNDROME, hdr);
|
|
+ ec = syndrome >> AARCH64_ESR_ELX_EXCEPTION_CLASS_SHIFT;
|
|
+ n = snprintf(p, size, " Exception(%s): syndrome = 0x%x%s\n",
|
|
+ rsh_log_mod[module], syndrome,
|
|
+ (ec == 0x24 || ec == 0x25) ? "(Data Abort)" :
|
|
+ (ec == 0x2f) ? "(SError)" : "");
|
|
+ } else if (type == BF_RSH_LOG_TYPE_PANIC) {
|
|
+ pc = BF_RSH_LOG_HEADER_GET(PC, hdr);
|
|
+ n = snprintf(p, size,
|
|
+ " PANIC(%s): PC = 0x%x\n", rsh_log_mod[module],
|
|
+ pc);
|
|
+ }
|
|
+ if (n > 0) {
|
|
+ p += n;
|
|
+ size -= n;
|
|
+ }
|
|
|
|
/*
|
|
- * key_state contains the bits for 4 Key versions, loaded from eFuses
|
|
- * after a hard reset. Lower 4 bits are a thermometer code indicating
|
|
- * key programming has started for key n (0000 = none, 0001 = version 0,
|
|
- * 0011 = version 1, 0111 = version 2, 1111 = version 3). Upper 4 bits
|
|
- * are a thermometer code indicating key programming has completed for
|
|
- * key n (same encodings as the start bits). This allows for detection
|
|
- * of an interruption in the programming process which has left the key
|
|
- * partially programmed (and thus invalid). The process is to burn the
|
|
- * eFuse for the new key start bit, burn the key eFuses, then burn the
|
|
- * eFuse for the new key complete bit.
|
|
- *
|
|
- * For example 0000_0000: no key valid, 0001_0001: key version 0 valid,
|
|
- * 0011_0011: key 1 version valid, 0011_0111: key version 2 started
|
|
- * programming but did not complete, etc. The most recent key for which
|
|
- * both start and complete bit is set is loaded. On soft reset, this
|
|
- * register is not modified.
|
|
+ * Read the registers in a loop. 'len' is the total number of words in
|
|
+ * 8-bytes. Two words are read in each loop.
|
|
*/
|
|
- for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
|
|
- burnt = key_state & BIT(key);
|
|
- valid = key_state & BIT(key + MLXBF_SB_KEY_NUM);
|
|
+ for (i = 0; i < len/2; i++) {
|
|
+ opcode = readq(rsh_scratch_buf_data);
|
|
+ data = readq(rsh_scratch_buf_data);
|
|
+
|
|
+ opcode = (opcode >> AARCH64_MRS_REG_SHIFT) &
|
|
+ AARCH64_MRS_REG_MASK;
|
|
+ n = snprintf(p, size,
|
|
+ " %-16s0x%llx\n", rsh_log_get_reg_name(opcode),
|
|
+ (unsigned long long)data);
|
|
+ if (n > 0) {
|
|
+ p += n;
|
|
+ size -= n;
|
|
+ }
|
|
+ }
|
|
|
|
- if (burnt && valid)
|
|
- upper_key_used = 1;
|
|
+ return p - buf;
|
|
+}
|
|
|
|
- if (upper_key_used) {
|
|
- if (burnt)
|
|
- status = valid ? "Used" : "Wasted";
|
|
- else
|
|
- status = valid ? "Invalid" : "Skipped";
|
|
- } else {
|
|
- if (burnt)
|
|
- status = valid ? "InUse" : "Incomplete";
|
|
- else
|
|
- status = valid ? "Invalid" : "Free";
|
|
+static int rsh_log_format_msg(char *buf, int size, const char *msg, ...)
|
|
+{
|
|
+ va_list args;
|
|
+ int len;
|
|
+
|
|
+ va_start(args, msg);
|
|
+ len = vsnprintf(buf, size, msg, args);
|
|
+ va_end(args);
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int rsh_log_show_msg(u64 hdr, char *buf, int size)
|
|
+{
|
|
+ int has_arg = BF_RSH_LOG_HEADER_GET(HAS_ARG, hdr);
|
|
+ int level = BF_RSH_LOG_HEADER_GET(LEVEL, hdr);
|
|
+ int module = BF_RSH_LOG_HEADER_GET(MOD, hdr);
|
|
+ int len = BF_RSH_LOG_HEADER_GET(LEN, hdr);
|
|
+ u32 arg = BF_RSH_LOG_HEADER_GET(ARG, hdr);
|
|
+ char *msg, *p;
|
|
+ u64 data;
|
|
+
|
|
+ if (len <= 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (module >= ARRAY_SIZE(rsh_log_mod))
|
|
+ module = 0;
|
|
+
|
|
+ if (level >= ARRAY_SIZE(rsh_log_level))
|
|
+ level = 0;
|
|
+
|
|
+ msg = kmalloc(len * sizeof(u64) + 1, GFP_KERNEL);
|
|
+ if (!msg)
|
|
+ return 0;
|
|
+ p = msg;
|
|
+
|
|
+ while (len--) {
|
|
+ data = readq(rsh_scratch_buf_data);
|
|
+ memcpy(p, &data, sizeof(data));
|
|
+ p += sizeof(data);
|
|
+ }
|
|
+ *p = '\0';
|
|
+ if (!has_arg) {
|
|
+ len = snprintf(buf, size, " %s[%s]: %s\n", rsh_log_level[level],
|
|
+ rsh_log_mod[module], msg);
|
|
+ } else {
|
|
+ len = snprintf(buf, size, " %s[%s]: ", rsh_log_level[level],
|
|
+ rsh_log_mod[module]);
|
|
+ len += rsh_log_format_msg(buf + len, size - len, msg, arg);
|
|
+ len += snprintf(buf + len, size - len, "\n");
|
|
+ }
|
|
+
|
|
+ kfree(msg);
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static ssize_t rsh_log_show(struct device_driver *drv, char *buf)
|
|
+{
|
|
+ u64 hdr;
|
|
+ char *p = buf;
|
|
+ int i, n, rc, idx, type, len, size = PAGE_SIZE;
|
|
+
|
|
+ if (!rsh_semaphore || !rsh_scratch_buf_ctl)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ /* Take the semaphore. */
|
|
+ rc = rsh_log_sem_lock();
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ /* Save the current index and read from 0. */
|
|
+ idx = readq(rsh_scratch_buf_ctl) & RSH_SCRATCH_BUF_CTL_IDX_MASK;
|
|
+ if (!idx)
|
|
+ goto done;
|
|
+ writeq(0, rsh_scratch_buf_ctl);
|
|
+
|
|
+ i = 0;
|
|
+ while (i < idx) {
|
|
+ hdr = readq(rsh_scratch_buf_data);
|
|
+ type = BF_RSH_LOG_HEADER_GET(TYPE, hdr);
|
|
+ len = BF_RSH_LOG_HEADER_GET(LEN, hdr);
|
|
+ i += 1 + len;
|
|
+ if (i > idx)
|
|
+ break;
|
|
+
|
|
+ switch (type) {
|
|
+ case BF_RSH_LOG_TYPE_PANIC:
|
|
+ case BF_RSH_LOG_TYPE_EXCEPTION:
|
|
+ n = rsh_log_show_crash(hdr, p, size);
|
|
+ p += n;
|
|
+ size -= n;
|
|
+ break;
|
|
+ case BF_RSH_LOG_TYPE_MSG:
|
|
+ n = rsh_log_show_msg(hdr, p, size);
|
|
+ p += n;
|
|
+ size -= n;
|
|
+ break;
|
|
+ default:
|
|
+ /* Drain this message. */
|
|
+ while (len--)
|
|
+ (void) readq(rsh_scratch_buf_data);
|
|
+ break;
|
|
}
|
|
- buf_len += sprintf(buf + buf_len, "%d:%s ", key, status);
|
|
}
|
|
- buf_len += sprintf(buf + buf_len, "\n");
|
|
|
|
- return buf_len;
|
|
+ if (rsh_log_clear_on_read)
|
|
+ writeq(0, rsh_scratch_buf_ctl);
|
|
+ else
|
|
+ writeq(idx, rsh_scratch_buf_ctl);
|
|
+
|
|
+done:
|
|
+ /* Release the semaphore. */
|
|
+ rsh_log_sem_unlock();
|
|
+
|
|
+ return p - buf;
|
|
}
|
|
|
|
-static DEVICE_ATTR_RW(post_reset_wdog);
|
|
-static DEVICE_ATTR_RW(reset_action);
|
|
-static DEVICE_ATTR_RW(second_reset_action);
|
|
-static DEVICE_ATTR_RO(lifecycle_state);
|
|
-static DEVICE_ATTR_RO(secure_boot_fuse_state);
|
|
+static DRIVER_ATTR_RW(post_reset_wdog);
|
|
+static DRIVER_ATTR_RW(reset_action);
|
|
+static DRIVER_ATTR_RW(second_reset_action);
|
|
+static DRIVER_ATTR_RO(lifecycle_state);
|
|
+static DRIVER_ATTR_RO(secure_boot_fuse_state);
|
|
+static DRIVER_ATTR_WO(fw_reset);
|
|
+static DRIVER_ATTR_RW(oob_mac);
|
|
+static DRIVER_ATTR_RW(opn);
|
|
+static DRIVER_ATTR_RW(sku);
|
|
+static DRIVER_ATTR_RW(modl);
|
|
+static DRIVER_ATTR_RW(sn);
|
|
+static DRIVER_ATTR_RW(uuid);
|
|
+static DRIVER_ATTR_RW(rev);
|
|
+static DRIVER_ATTR_WO(mfg_lock);
|
|
+static DRIVER_ATTR_RW(rsh_log);
|
|
|
|
-static struct attribute *mlxbf_bootctl_attrs[] = {
|
|
- &dev_attr_post_reset_wdog.attr,
|
|
- &dev_attr_reset_action.attr,
|
|
- &dev_attr_second_reset_action.attr,
|
|
- &dev_attr_lifecycle_state.attr,
|
|
- &dev_attr_secure_boot_fuse_state.attr,
|
|
+static struct attribute *mbc_dev_attrs[] = {
|
|
+ &driver_attr_post_reset_wdog.attr,
|
|
+ &driver_attr_reset_action.attr,
|
|
+ &driver_attr_second_reset_action.attr,
|
|
+ &driver_attr_lifecycle_state.attr,
|
|
+ &driver_attr_secure_boot_fuse_state.attr,
|
|
+ &driver_attr_fw_reset.attr,
|
|
+ &driver_attr_oob_mac.attr,
|
|
+ &driver_attr_opn.attr,
|
|
+ &driver_attr_sku.attr,
|
|
+ &driver_attr_modl.attr,
|
|
+ &driver_attr_sn.attr,
|
|
+ &driver_attr_uuid.attr,
|
|
+ &driver_attr_rev.attr,
|
|
+ &driver_attr_mfg_lock.attr,
|
|
+ &driver_attr_rsh_log.attr,
|
|
NULL
|
|
};
|
|
|
|
-ATTRIBUTE_GROUPS(mlxbf_bootctl);
|
|
+static struct attribute_group mbc_attr_group = {
|
|
+ .attrs = mbc_dev_attrs
|
|
+};
|
|
|
|
-static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
|
|
+static const struct attribute_group *mbc_attr_groups[] = {
|
|
+ &mbc_attr_group,
|
|
+ NULL
|
|
+};
|
|
+
|
|
+static const struct of_device_id mbc_dt_ids[] = {
|
|
+ {.compatible = "mellanox,bootctl"},
|
|
+ {},
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(of, mbc_dt_ids);
|
|
+
|
|
+static const struct acpi_device_id mbc_acpi_ids[] = {
|
|
{"MLNXBF04", 0},
|
|
- {}
|
|
+ {},
|
|
};
|
|
|
|
-MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
|
|
+MODULE_DEVICE_TABLE(acpi, mbc_acpi_ids);
|
|
|
|
-static bool mlxbf_bootctl_guid_match(const guid_t *guid,
|
|
- const struct arm_smccc_res *res)
|
|
+static ssize_t mbc_bootfifo_read_raw(struct file *filp, struct kobject *kobj,
|
|
+ struct bin_attribute *bin_attr,
|
|
+ char *buf, loff_t pos, size_t count)
|
|
{
|
|
- guid_t id = GUID_INIT(res->a0, res->a1, res->a1 >> 16,
|
|
- res->a2, res->a2 >> 8, res->a2 >> 16,
|
|
- res->a2 >> 24, res->a3, res->a3 >> 8,
|
|
- res->a3 >> 16, res->a3 >> 24);
|
|
+ unsigned long timeout = jiffies + HZ / 2;
|
|
+ char *p = buf;
|
|
+ int cnt = 0;
|
|
+ u64 data;
|
|
|
|
- return guid_equal(guid, &id);
|
|
+ /* Give up reading if no more data within 500ms. */
|
|
+ while (count >= sizeof(data)) {
|
|
+ if (!cnt) {
|
|
+ cnt = readq(rsh_boot_cnt);
|
|
+ if (!cnt) {
|
|
+ if (time_after(jiffies, timeout))
|
|
+ break;
|
|
+ udelay(10);
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ data = readq(rsh_boot_data);
|
|
+ memcpy(p, &data, sizeof(data));
|
|
+ count -= sizeof(data);
|
|
+ p += sizeof(data);
|
|
+ cnt--;
|
|
+ timeout = jiffies + HZ / 2;
|
|
+ }
|
|
+
|
|
+ return p - buf;
|
|
}
|
|
|
|
-static int mlxbf_bootctl_probe(struct platform_device *pdev)
|
|
+static struct bin_attribute mbc_bootfifo_sysfs_attr = {
|
|
+ .attr = { .name = "bootfifo", .mode = 0400 },
|
|
+ .read = mbc_bootfifo_read_raw,
|
|
+};
|
|
+
|
|
+static int mbc_probe(struct platform_device *pdev)
|
|
{
|
|
- struct arm_smccc_res res = { 0 };
|
|
- guid_t guid;
|
|
- int ret;
|
|
+ struct resource *resource;
|
|
+ struct arm_smccc_res res;
|
|
+ void __iomem *data;
|
|
+ int err;
|
|
|
|
- /* Ensure we have the UUID we expect for this service. */
|
|
- arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res);
|
|
- guid_parse(mlxbf_bootctl_svc_uuid_str, &guid);
|
|
- if (!mlxbf_bootctl_guid_match(&guid, &res))
|
|
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
+ if (!resource)
|
|
return -ENODEV;
|
|
+ rsh_boot_data = devm_ioremap_resource(&pdev->dev, resource);
|
|
+ if (IS_ERR(rsh_boot_data))
|
|
+ return PTR_ERR(rsh_boot_data);
|
|
+
|
|
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
+ if (!resource)
|
|
+ return -ENODEV;
|
|
+ rsh_boot_cnt = devm_ioremap_resource(&pdev->dev, resource);
|
|
+ if (IS_ERR(rsh_boot_cnt))
|
|
+ return PTR_ERR(rsh_boot_cnt);
|
|
+
|
|
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 2);
|
|
+ if (resource) {
|
|
+ data = devm_ioremap_resource(&pdev->dev, resource);
|
|
+ if (!IS_ERR(data))
|
|
+ rsh_semaphore = data;
|
|
+ }
|
|
+
|
|
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 3);
|
|
+ if (resource) {
|
|
+ data = devm_ioremap_resource(&pdev->dev, resource);
|
|
+ if (!IS_ERR(data)) {
|
|
+ rsh_scratch_buf_ctl = data + RSH_SCRATCH_BUF_CTL_OFF;
|
|
+ rsh_scratch_buf_data = data + RSH_SCRATCH_BUF_DATA_OFF;
|
|
+ }
|
|
+ }
|
|
|
|
/*
|
|
- * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_SWAP_EMMC
|
|
+ * Ensure we have the UUID we expect for this service.
|
|
+ * Note that the functionality we want is present in the first
|
|
+ * released version of this service, so we don't check the version.
|
|
+ */
|
|
+ arm_smccc_smc(MLNX_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res);
|
|
+ if (res.a0 != 0x89c036b4 || res.a1 != 0x11e6e7d7 ||
|
|
+ res.a2 != 0x1a009787 || res.a3 != 0xc4bf00ca)
|
|
+ return -ENODEV;
|
|
+
|
|
+ /*
|
|
+ * When watchdog is used, it sets the boot mode to MLNX_BOOT_SWAP_EMMC
|
|
* in case of boot failures. However it doesn't clear the state if there
|
|
* is no failure. Restore the default boot mode here to avoid any
|
|
* unnecessary boot partition swapping.
|
|
*/
|
|
- ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_RESET_ACTION,
|
|
- MLXBF_BOOTCTL_EMMC);
|
|
- if (ret < 0)
|
|
- dev_warn(&pdev->dev, "Unable to reset the EMMC boot mode\n");
|
|
+ if (smc_call1(MLNX_SET_RESET_ACTION, MLNX_BOOT_EMMC) < 0)
|
|
+ pr_err("Unable to reset the EMMC boot mode\n");
|
|
+
|
|
+ err = sysfs_create_bin_file(&pdev->dev.kobj, &mbc_bootfifo_sysfs_attr);
|
|
+ if (err) {
|
|
+ pr_err("Unable to create bootfifo sysfs file, error %d\n", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ pr_info("%s (version %s)\n", DRIVER_DESCRIPTION, DRIVER_VERSION);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mbc_remove(struct platform_device *pdev)
|
|
+{
|
|
+ sysfs_remove_bin_file(&pdev->dev.kobj, &mbc_bootfifo_sysfs_attr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
-static struct platform_driver mlxbf_bootctl_driver = {
|
|
- .probe = mlxbf_bootctl_probe,
|
|
+static struct platform_driver mbc_driver = {
|
|
+ .probe = mbc_probe,
|
|
+ .remove = mbc_remove,
|
|
.driver = {
|
|
- .name = "mlxbf-bootctl",
|
|
- .dev_groups = mlxbf_bootctl_groups,
|
|
- .acpi_match_table = mlxbf_bootctl_acpi_ids,
|
|
+ .name = DRIVER_NAME,
|
|
+ .groups = mbc_attr_groups,
|
|
+ .of_match_table = mbc_dt_ids,
|
|
+ .acpi_match_table = ACPI_PTR(mbc_acpi_ids),
|
|
}
|
|
};
|
|
|
|
-module_platform_driver(mlxbf_bootctl_driver);
|
|
+module_platform_driver(mbc_driver);
|
|
|
|
-MODULE_DESCRIPTION("Mellanox boot control driver");
|
|
-MODULE_LICENSE("GPL v2");
|
|
-MODULE_AUTHOR("Mellanox Technologies");
|
|
+MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
|
|
+MODULE_VERSION(DRIVER_VERSION);
|
|
+MODULE_AUTHOR("Shravan Kumar Ramani <shravankr@nvidia.com>");
|
|
+MODULE_LICENSE("Dual BSD/GPL");
|
|
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.h b/drivers/platform/mellanox/mlxbf-bootctl.h
|
|
index 148fdb43b..3e9dda829 100644
|
|
--- a/drivers/platform/mellanox/mlxbf-bootctl.h
|
|
+++ b/drivers/platform/mellanox/mlxbf-bootctl.h
|
|
@@ -1,11 +1,22 @@
|
|
-/* SPDX-License-Identifier: GPL-2.0 */
|
|
+// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause
|
|
/*
|
|
- * Copyright (c) 2019, Mellanox Technologies. All rights reserved.
|
|
+ * Copyright (C) 2020 Mellanox Technologies. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License v2.0 as published by
|
|
+ * the Free Software Foundation.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
*/
|
|
|
|
#ifndef __MLXBF_BOOTCTL_H__
|
|
#define __MLXBF_BOOTCTL_H__
|
|
|
|
+/* BlueField-specific SMC function IDs */
|
|
+
|
|
/*
|
|
* Request that the on-chip watchdog be enabled, or disabled, after
|
|
* the next chip soft reset. This call does not affect the current
|
|
@@ -14,14 +25,14 @@
|
|
* will not be enabled after the next soft reset. Non-zero errors are
|
|
* returned as documented below.
|
|
*/
|
|
-#define MLXBF_BOOTCTL_SET_POST_RESET_WDOG 0x82000000
|
|
+#define MLNX_SET_POST_RESET_WDOG 0x82000000
|
|
|
|
/*
|
|
* Query the status which has been requested for the on-chip watchdog
|
|
* after the next chip soft reset. Returns the interval as set by
|
|
- * MLXBF_BOOTCTL_SET_POST_RESET_WDOG.
|
|
+ * MLNX_SET_POST_RESET_WDOG.
|
|
*/
|
|
-#define MLXBF_BOOTCTL_GET_POST_RESET_WDOG 0x82000001
|
|
+#define MLNX_GET_POST_RESET_WDOG 0x82000001
|
|
|
|
/*
|
|
* Request that a specific boot action be taken at the next soft
|
|
@@ -32,72 +43,77 @@
|
|
* invoked. See below for the available MLNX_BOOT_xxx parameter
|
|
* values. Non-zero errors are returned as documented below.
|
|
*/
|
|
-#define MLXBF_BOOTCTL_SET_RESET_ACTION 0x82000002
|
|
+#define MLNX_SET_RESET_ACTION 0x82000002
|
|
|
|
/*
|
|
* Return the specific boot action which will be taken at the next
|
|
* soft reset. Returns the reset action (see below for the parameter
|
|
- * values for MLXBF_BOOTCTL_SET_RESET_ACTION).
|
|
+ * values for MLNX_SET_RESET_ACTION).
|
|
*/
|
|
-#define MLXBF_BOOTCTL_GET_RESET_ACTION 0x82000003
|
|
+#define MLNX_GET_RESET_ACTION 0x82000003
|
|
|
|
/*
|
|
* Request that a specific boot action be taken at the soft reset
|
|
* after the next soft reset. For a specified valid boot mode, the
|
|
* effect of this call is identical to that of invoking
|
|
- * MLXBF_BOOTCTL_SET_RESET_ACTION after the next chip soft reset; in
|
|
+ * MLNX_SET_RESET_ACTION after the next chip soft reset; in
|
|
* particular, after that reset, the action for the now next reset can
|
|
- * be queried with MLXBF_BOOTCTL_GET_RESET_ACTION and modified with
|
|
- * MLXBF_BOOTCTL_SET_RESET_ACTION. You may also specify the parameter as
|
|
+ * be queried with MLNX_GET_RESET_ACTION and modified with
|
|
+ * MLNX_SET_RESET_ACTION. You may also specify the parameter as
|
|
* MLNX_BOOT_NONE, which is equivalent to specifying that no call to
|
|
- * MLXBF_BOOTCTL_SET_RESET_ACTION be taken after the next chip soft reset.
|
|
+ * MLNX_SET_RESET_ACTION be taken after the next chip soft reset.
|
|
* This call does not affect the action to be taken at the next soft
|
|
* reset. Non-zero errors are returned as documented below.
|
|
*/
|
|
-#define MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION 0x82000004
|
|
+#define MLNX_SET_SECOND_RESET_ACTION 0x82000004
|
|
|
|
/*
|
|
* Return the specific boot action which will be taken at the soft
|
|
* reset after the next soft reset; this will be one of the valid
|
|
- * actions for MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION.
|
|
+ * actions for MLNX_SET_SECOND_RESET_ACTION.
|
|
*/
|
|
-#define MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION 0x82000005
|
|
+#define MLNX_GET_SECOND_RESET_ACTION 0x82000005
|
|
|
|
/*
|
|
* Return the fuse status of the current chip. The caller should specify
|
|
* with the second argument if the state of the lifecycle fuses or the
|
|
* version of secure boot fuse keys left should be returned.
|
|
*/
|
|
-#define MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS 0x82000006
|
|
+#define MLNX_GET_TBB_FUSE_STATUS 0x82000006
|
|
|
|
-/* Reset eMMC by programming the RST_N register. */
|
|
-#define MLXBF_BOOTCTL_SET_EMMC_RST_N 0x82000007
|
|
+/*
|
|
+ * Initiate Firmware Reset via TYU. This might be invoked during the reset
|
|
+ * flow in isolation mode.
|
|
+ */
|
|
+#define MLNX_HANDLE_FW_RESET 0x8200000D
|
|
|
|
-#define MLXBF_BOOTCTL_GET_DIMM_INFO 0x82000008
|
|
+/*
|
|
+ * SMC function IDs to set, get and reset the manufacturing information
|
|
+ * stored within the eeprom.
|
|
+ */
|
|
+#define MLNX_HANDLE_SET_MFG_INFO 0x8200000E
|
|
+#define MLNX_HANDLE_GET_MFG_INFO 0x8200000F
|
|
+#define MLNX_HANDLE_LOCK_MFG_INFO 0x82000011
|
|
|
|
/* SMC function IDs for SiP Service queries */
|
|
-#define MLXBF_BOOTCTL_SIP_SVC_CALL_COUNT 0x8200ff00
|
|
-#define MLXBF_BOOTCTL_SIP_SVC_UID 0x8200ff01
|
|
-#define MLXBF_BOOTCTL_SIP_SVC_VERSION 0x8200ff03
|
|
-
|
|
-/* ARM Standard Service Calls version numbers */
|
|
-#define MLXBF_BOOTCTL_SVC_VERSION_MAJOR 0x0
|
|
-#define MLXBF_BOOTCTL_SVC_VERSION_MINOR 0x2
|
|
+#define MLNX_SIP_SVC_CALL_COUNT 0x8200ff00
|
|
+#define MLNX_SIP_SVC_UID 0x8200ff01
|
|
+#define MLNX_SIP_SVC_VERSION 0x8200ff03
|
|
|
|
/* Number of svc calls defined. */
|
|
-#define MLXBF_BOOTCTL_NUM_SVC_CALLS 12
|
|
+#define MLNX_NUM_SVC_CALLS 16
|
|
|
|
-/* Valid reset actions for MLXBF_BOOTCTL_SET_RESET_ACTION. */
|
|
-#define MLXBF_BOOTCTL_EXTERNAL 0 /* Not boot from eMMC */
|
|
-#define MLXBF_BOOTCTL_EMMC 1 /* From primary eMMC boot partition */
|
|
-#define MLNX_BOOTCTL_SWAP_EMMC 2 /* Swap eMMC boot partitions and reboot */
|
|
-#define MLXBF_BOOTCTL_EMMC_LEGACY 3 /* From primary eMMC in legacy mode */
|
|
+/* Valid reset actions for MLNX_SET_RESET_ACTION. */
|
|
+#define MLNX_BOOT_EXTERNAL 0 /* Do not boot from eMMC */
|
|
+#define MLNX_BOOT_EMMC 1 /* Boot from primary eMMC boot partition */
|
|
+#define MLNX_BOOT_SWAP_EMMC 2 /* Swap eMMC boot partitions and reboot */
|
|
+#define MLNX_BOOT_EMMC_LEGACY 3 /* Boot from primary eMMC in legacy mode */
|
|
|
|
/* Valid arguments for requesting the fuse status. */
|
|
-#define MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE 0 /* Return lifecycle status. */
|
|
-#define MLXBF_BOOTCTL_FUSE_STATUS_KEYS 1 /* Return secure boot key status */
|
|
+#define MLNX_FUSE_STATUS_LIFECYCLE 0 /* Return the lifecycle status. */
|
|
+#define MLNX_FUSE_STATUS_KEYS 1 /* Return secure boot key status */
|
|
|
|
-/* Additional value to disable the MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION. */
|
|
-#define MLXBF_BOOTCTL_NONE 0x7fffffff /* Don't change next boot action */
|
|
+/* Additional parameter value to disable the MLNX_SET_SECOND_RESET_ACTION. */
|
|
+#define MLNX_BOOT_NONE 0x7fffffff /* Don't change next boot action */
|
|
|
|
#endif /* __MLXBF_BOOTCTL_H__ */
|
|
--
|
|
2.20.1
|
|
|