sonic-buildimage/platform/mellanox/non-upstream-patches/patches/0211-UBUNTU-SAUCE-platform-mellanox-Updates-to-mlxbf-boot.patch
Kebo Liu e286869b24
[Mellanox] Update HW-MGMT package to new version V.7.0030.1011 (#16239)
- 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>
2023-09-06 11:32:08 +03:00

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