linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
@ 2019-01-29 20:59 Liming Sun
  2019-01-29 22:03 ` Andy Shevchenko
                   ` (8 more replies)
  0 siblings, 9 replies; 34+ messages in thread
From: Liming Sun @ 2019-01-29 20:59 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods
  Cc: Liming Sun, platform-driver-x86, linux-kernel

This commit adds the bootctl platform driver for Mellanox BlueField
Soc, which controls the eMMC boot partition swapping and related
watchdog configuration.

Reviewed-by: David Woods <dwoods@mellanox.com>
Signed-off-by: Liming Sun <lsun@mellanox.com>
---
 drivers/platform/mellanox/Kconfig         |  15 +-
 drivers/platform/mellanox/Makefile        |   1 +
 drivers/platform/mellanox/mlxbf-bootctl.c | 317 ++++++++++++++++++++++++++++++
 drivers/platform/mellanox/mlxbf-bootctl.h | 108 ++++++++++
 4 files changed, 440 insertions(+), 1 deletion(-)
 create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.c
 create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.h

diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig
index cd8a908..580901f 100644
--- a/drivers/platform/mellanox/Kconfig
+++ b/drivers/platform/mellanox/Kconfig
@@ -5,7 +5,7 @@
 
 menuconfig MELLANOX_PLATFORM
 	bool "Platform support for Mellanox hardware"
-	depends on X86 || ARM || COMPILE_TEST
+	depends on X86 || ARM || ARM64 || COMPILE_TEST
 	---help---
 	  Say Y here to get to see options for platform support for
 	  Mellanox systems. This option alone does not add any kernel code.
@@ -34,4 +34,17 @@ config MLXREG_IO
 	  to system resets operation, system reset causes monitoring and some
 	  kinds of mux selection.
 
+config MLXBF_BOOTCTL
+	tristate "Mellanox BlueField Firmware Boot Control driver"
+	depends on ARM64
+	default m
+	help
+          The Mellanox BlueField firmware implements functionality to
+          request swapping the primary and alternate eMMC boot
+          partition, and to set up a watchdog that can undo that swap
+          if the system does not boot up correctly.  This driver
+          provides sysfs access to the firmware, to be used in
+          conjunction with the eMMC device driver to do any necessary
+          initial swap of the boot partition.
+
 endif # MELLANOX_PLATFORM
diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile
index 57074d9c..76b0370 100644
--- a/drivers/platform/mellanox/Makefile
+++ b/drivers/platform/mellanox/Makefile
@@ -5,3 +5,4 @@
 #
 obj-$(CONFIG_MLXREG_HOTPLUG)	+= mlxreg-hotplug.o
 obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o
+obj-$(CONFIG_MLXBF_BOOTCTL)	+= mlxbf-bootctl.o
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c
new file mode 100644
index 0000000..90a655b
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf-bootctl.c
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Mellanox boot control driver
+ * This driver provides a sysfs interface for systems management
+ * software to manage reset-time actions.
+ *
+ * Copyright (C) 2019 Mellanox Technologies
+ */
+
+#include <linux/acpi.h>
+#include <linux/arm-smccc.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "mlxbf-bootctl.h"
+
+#define MLXBF_BOOTCTL_DRIVER_NAME		"mlxbf-bootctl"
+#define MLXBF_BOOTCTL_DRIVER_VERSION		"1.2"
+#define MLXBF_BOOTCTL_DRIVER_DESCRIPTION	"Mellanox boot control driver"
+
+#define MLXBF_BOOTCTL_SB_MODE_SECURE_MASK	0x03
+#define MLXBF_BOOTCTL_SB_MODE_TEST_MASK		0x0c
+
+#define MLXBF_SB_KEY_NUM			4
+
+struct mlxbf_bootctl_name {
+	int value;
+	const char name[12];
+};
+
+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"		},
+	{ -1,				""		}
+};
+
+static char mlxbf_bootctl_lifecycle_states[][16] = {
+	[0] = "soft_non_secure",
+	[1] = "secure",
+	[2] = "hard_non_secure",
+	[3] = "rma",
+};
+
+/* The SMC calls in question are atomic, so we don't have to lock here. */
+static int mlxbf_bootctl_smc_call1(unsigned int smc_op, int smc_arg)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res);
+
+	return res.a0;
+}
+
+/* Syntactic sugar to avoid having to specify an unused argument. */
+#define mlxbf_bootctl_smc_call0(smc_op) mlxbf_bootctl_smc_call1(smc_op, 0)
+
+static int reset_action_to_val(const char *action, size_t len)
+{
+	struct mlxbf_bootctl_name *bn;
+
+	/* Accept string either with or without a newline terminator */
+	if (action[len-1] == '\n')
+		--len;
+
+	for (bn = boot_names; bn->value >= 0; ++bn)
+		if (strncmp(bn->name, action, len) == 0)
+			break;
+
+	return bn->value;
+}
+
+static const char *reset_action_to_string(int action)
+{
+	struct mlxbf_bootctl_name *bn;
+
+	for (bn = boot_names; bn->value >= 0; ++bn)
+		if (bn->value == action)
+			break;
+
+	return bn->name;
+}
+
+static ssize_t post_reset_wdog_show(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "%d\n",
+		mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_POST_RESET_WDOG));
+}
+
+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 (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_POST_RESET_WDOG,
+				    watchdog) < 0)
+		return -EINVAL;
+
+	return count;
+}
+
+static ssize_t reset_action_show(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "%s\n", reset_action_to_string(
+		mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_RESET_ACTION)));
+}
+
+static ssize_t reset_action_store(struct device_driver *drv,
+				  const char *buf, size_t count)
+{
+	int action = reset_action_to_val(buf, count);
+
+	if (action < 0 || action == MLXBF_BOOTCTL_NONE)
+		return -EINVAL;
+
+	if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION, action) < 0)
+		return -EINVAL;
+
+	return count;
+}
+
+static ssize_t second_reset_action_show(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "%s\n", reset_action_to_string(
+		mlxbf_bootctl_smc_call0(
+			MLXBF_BOOTCTL_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);
+
+	if (action < 0)
+		return -EINVAL;
+
+	if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION,
+				    action) < 0)
+		return -EINVAL;
+
+	return count;
+}
+
+static ssize_t lifecycle_state_show(struct device_driver *drv, char *buf)
+{
+	int lc_state = mlxbf_bootctl_smc_call1(
+				MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
+				MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
+
+	if (lc_state < 0)
+		return -EINVAL;
+
+	lc_state &= (MLXBF_BOOTCTL_SB_MODE_TEST_MASK |
+		     MLXBF_BOOTCTL_SB_MODE_SECURE_MASK);
+	/*
+	 * 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_MODE_TEST_MASK) != 0) {
+
+		lc_state &= MLXBF_BOOTCTL_SB_MODE_SECURE_MASK;
+
+		return sprintf(buf, "%s(test)\n",
+			       mlxbf_bootctl_lifecycle_states[lc_state]);
+	}
+
+	return sprintf(buf, "%s\n", mlxbf_bootctl_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 = mlxbf_bootctl_smc_call1(
+				MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
+				MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
+
+	if (sb_key_state < 0)
+		return -EINVAL;
+
+	for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
+		int burnt = ((sb_key_state & (1 << key)) != 0);
+		int valid = ((sb_key_state &
+			      (1 << (key + MLXBF_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;
+}
+
+#define MLXBF_BOOTCTL_DRV_ATTR(_name) DRIVER_ATTR_RW(_name)
+
+static MLXBF_BOOTCTL_DRV_ATTR(post_reset_wdog);
+static MLXBF_BOOTCTL_DRV_ATTR(reset_action);
+static MLXBF_BOOTCTL_DRV_ATTR(second_reset_action);
+static DRIVER_ATTR_RO(lifecycle_state);
+static DRIVER_ATTR_RO(secure_boot_fuse_state);
+
+static struct attribute *mlxbf_bootctl_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,
+	NULL
+};
+
+static struct attribute_group mlxbf_bootctl_attr_group = {
+	.attrs = mlxbf_bootctl_dev_attrs
+};
+
+static const struct attribute_group *mlxbf_bootctl_attr_groups[] = {
+	&mlxbf_bootctl_attr_group,
+	NULL
+};
+
+static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
+	{"MLNXBF04", 0},
+	{},
+};
+
+MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
+
+static int mlxbf_bootctl_probe(struct platform_device *pdev)
+{
+	struct arm_smccc_res res;
+
+	/*
+	 * 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(MLXBF_BOOTCTL_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 boot mode to MLXBF_BOOTCTL_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.
+	 */
+	if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION,
+				    MLXBF_BOOTCTL_EMMC) < 0)
+		pr_err("Unable to reset the EMMC boot mode\n");
+
+	pr_info("%s (version %s)\n", MLXBF_BOOTCTL_DRIVER_DESCRIPTION,
+		MLXBF_BOOTCTL_DRIVER_VERSION);
+
+	return 0;
+}
+
+static int mlxbf_bootctl_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver mlxbf_bootctl_driver = {
+	.probe = mlxbf_bootctl_probe,
+	.remove = mlxbf_bootctl_remove,
+	.driver = {
+		.name = MLXBF_BOOTCTL_DRIVER_NAME,
+		.groups = mlxbf_bootctl_attr_groups,
+		.acpi_match_table = ACPI_PTR(mlxbf_bootctl_acpi_ids),
+	}
+};
+
+module_platform_driver(mlxbf_bootctl_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
+MODULE_VERSION(MLXBF_BOOTCTL_DRIVER_VERSION);
+MODULE_AUTHOR("Mellanox Technologies");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.h b/drivers/platform/mellanox/mlxbf-bootctl.h
new file mode 100644
index 0000000..0592ce4
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf-bootctl.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019, Mellanox Technologies. All rights reserved.
+ */
+
+#ifndef __MLXBF_BOOTCTL_H__
+#define __MLXBF_BOOTCTL_H__
+
+/*
+ * Request that the on-chip watchdog be enabled, or disabled, after
+ * the next chip soft reset. This call does not affect the current
+ * status of the on-chip watchdog. If non-zero, the argument
+ * specifies the watchdog interval in seconds. If zero, the watchdog
+ * 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
+
+/*
+ * 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.
+ */
+#define MLXBF_BOOTCTL_GET_POST_RESET_WDOG	0x82000001
+
+/*
+ * Request that a specific boot action be taken at the next soft
+ * reset. By default, the boot action is set by external chip pins,
+ * which are sampled on hard reset. Note that the boot action
+ * requested by this call will persist on subsequent resets unless
+ * this service, or the MLNX_SET_SECOND_RESET_ACTION service, is
+ * 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
+
+/*
+ * 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).
+ */
+#define MLXBF_BOOTCTL_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
+ * 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
+ * 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.
+ * 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
+
+/*
+ * 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.
+ */
+#define MLXBF_BOOTCTL_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
+
+/*
+ * Reset eMMC by programming the RST_N register.
+ */
+#define MLXBF_BOOTCTL_SET_EMMC_RST_N		0x82000007
+
+#define MLXBF_BOOTCTL_GET_DIMM_INFO		0x82000008
+
+/* 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
+
+/* Number of svc calls defined. */
+#define MLXBF_BOOTCTL_NUM_SVC_CALLS 12
+
+/* 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 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 */
+
+/* Additional value to disable the MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION. */
+#define MLXBF_BOOTCTL_NONE	0x7fffffff /* Don't change next boot action */
+
+/* Error values (non-zero). */
+#define MLXBF_BOOTCTL_SMCCC_INVALID_PARAMETERS	-2
+
+#endif /* __MLXBF_BOOTCTL_H__ */
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-01-29 20:59 [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc Liming Sun
@ 2019-01-29 22:03 ` Andy Shevchenko
  2019-01-30  6:24   ` Vadim Pasternak
  2019-01-30 20:47   ` Liming Sun
  2019-01-31 16:53 ` [PATCH v2] " Liming Sun
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 34+ messages in thread
From: Andy Shevchenko @ 2019-01-29 22:03 UTC (permalink / raw)
  To: Liming Sun
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	Platform Driver, Linux Kernel Mailing List

On Tue, Jan 29, 2019 at 10:59 PM Liming Sun <lsun@mellanox.com> wrote:
>
> This commit adds the bootctl platform driver for Mellanox BlueField
> Soc, which controls the eMMC boot partition swapping and related
> watchdog configuration.

Thanks for the patch.

My comments below.

First of all, is it a real watchdog with a driver? I think watchdog in
that case should be set up through standard watchdog facilities.

> Reviewed-by: David Woods <dwoods@mellanox.com>

As for Mellanox team I would recommend to take this review as few
basics of kernel programming style and some standard practices.

> Signed-off-by: Liming Sun <lsun@mellanox.com>

> +config MLXBF_BOOTCTL
> +       tristate "Mellanox BlueField Firmware Boot Control driver"
> +       depends on ARM64

> +       default m

Why? What would happen if user chooses n?

> +       help
> +          The Mellanox BlueField firmware implements functionality to
> +          request swapping the primary and alternate eMMC boot
> +          partition, and to set up a watchdog that can undo that swap
> +          if the system does not boot up correctly.  This driver
> +          provides sysfs access to the firmware, to be used in
> +          conjunction with the eMMC device driver to do any necessary
> +          initial swap of the boot partition.

> +struct mlxbf_bootctl_name {
> +       int value;
> +       const char name[12];

Can't we do simple

const char *name;

?

Why do we need the limitation. Why is it hard coded like this?

> +};
> +
> +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"          },

> +       { -1,                           ""              }

-1 is usually a bad idea for terminator. It's not in align with many
other structures which require terminator.

> +};

> +
> +static char mlxbf_bootctl_lifecycle_states[][16] = {

static const char * const ... ?

> +       [0] = "soft_non_secure",
> +       [1] = "secure",
> +       [2] = "hard_non_secure",
> +       [3] = "rma",
> +};

> +/* Syntactic sugar to avoid having to specify an unused argument. */
> +#define mlxbf_bootctl_smc_call0(smc_op) mlxbf_bootctl_smc_call1(smc_op, 0)
> +
> +static int reset_action_to_val(const char *action, size_t len)
> +{
> +       struct mlxbf_bootctl_name *bn;
> +

> +       /* Accept string either with or without a newline terminator */
> +       if (action[len-1] == '\n')
> +               --len;

For a long time we have sysfs_streq() API.

> +
> +       for (bn = boot_names; bn->value >= 0; ++bn)
> +               if (strncmp(bn->name, action, len) == 0)
> +                       break;
> +
> +       return bn->value;
> +}

> +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 (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_POST_RESET_WDOG,
> +                                   watchdog) < 0)
> +               return -EINVAL;

If that call returns an error it shouldn't be shadowed here.

> +
> +       return count;
> +}
> +
> +static ssize_t reset_action_show(struct device_driver *drv, char *buf)
> +{

> +       return sprintf(buf, "%s\n", reset_action_to_string(
> +               mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_RESET_ACTION)));

Wouldn't be easy to parse this as

int action = ...call0();
return sprintf(...);

?

(int is an arbitrary type here, choose one that suits)

> +}
> +
> +static ssize_t reset_action_store(struct device_driver *drv,
> +                                 const char *buf, size_t count)
> +{
> +       int action = reset_action_to_val(buf, count);
> +

> +       if (action < 0 || action == MLXBF_BOOTCTL_NONE)
> +               return -EINVAL;

Don't shadow an error.

> +
> +       if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION, action) < 0)
> +               return -EINVAL;

Same.

> +
> +       return count;
> +}
> +
> +static ssize_t second_reset_action_show(struct device_driver *drv, char *buf)
> +{

> +       return sprintf(buf, "%s\n", reset_action_to_string(
> +               mlxbf_bootctl_smc_call0(
> +                       MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION)));

Use temp variable.

> +}
> +
> +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);
> +
> +       if (action < 0)
> +               return -EINVAL;

Don't shadow an error.

> +
> +       if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION,
> +                                   action) < 0)
> +               return -EINVAL;

Same.

> +
> +       return count;
> +}
> +
> +static ssize_t lifecycle_state_show(struct device_driver *drv, char *buf)
> +{

> +       int lc_state = mlxbf_bootctl_smc_call1(
> +                               MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> +                               MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);

Split it as

int ...;

... = call1();
if (...)

> +
> +       if (lc_state < 0)
> +               return -EINVAL;

Don't shadow an error.

> +
> +       lc_state &= (MLXBF_BOOTCTL_SB_MODE_TEST_MASK |
> +                    MLXBF_BOOTCTL_SB_MODE_SECURE_MASK);

Better to split like

xxx =
 (A | B);

> +       /*
> +        * 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_MODE_TEST_MASK) != 0) {

' != 0' is redundant.

> +
> +               lc_state &= MLXBF_BOOTCTL_SB_MODE_SECURE_MASK;
> +

> +               return sprintf(buf, "%s(test)\n",
> +                              mlxbf_bootctl_lifecycle_states[lc_state]);

One line?

> +       }
> +
> +       return sprintf(buf, "%s\n", mlxbf_bootctl_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 = mlxbf_bootctl_smc_call1(
> +                               MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> +                               MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
> +
> +       if (sb_key_state < 0)
> +               return -EINVAL;
> +

> +       for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {

I'm not sure it's a good idea to put several lines in one sysfs attribute.

> +               int burnt = ((sb_key_state & (1 << key)) != 0);

Redundant  ' != 0', redundant parens.

> +               int valid = ((sb_key_state &
> +                             (1 << (key + MLXBF_SB_KEY_NUM))) != 0);

Same.

> +
> +               buf_len += sprintf(buf + buf_len, "Ver%d:", key);
> +               if (upper_key_used) {
> +                       if (burnt) {
> +                               if (valid)
> +                                       buf_len += sprintf(buf + buf_len,
> +                                                         "Used");

Oh, why not just

const char *status;

if (...) {
...
 status = "1";
...
status = "2";
...
}
len = snprintf(buf + len, ..., status);

?

> +                               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;
> +}
> +

> +#define MLXBF_BOOTCTL_DRV_ATTR(_name) DRIVER_ATTR_RW(_name)

What the point?

> +static struct attribute_group mlxbf_bootctl_attr_group = {
> +       .attrs = mlxbf_bootctl_dev_attrs

+ comma.

> +};

> +static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
> +       {"MLNXBF04", 0},

> +       {},

No comma for terminator line.

> +};


> +static int mlxbf_bootctl_probe(struct platform_device *pdev)
> +{
> +       struct arm_smccc_res res;
> +
> +       /*
> +        * 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(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res);



> +       if (res.a0 != 0x89c036b4 || res.a1 != 0x11e6e7d7 ||
> +           res.a2 != 0x1a009787 || res.a3 != 0xc4bf00ca)

What is this?!

Can you use UUID API?

> +               return -ENODEV;
> +
> +       /*
> +        * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_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.
> +        */
> +       if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION,
> +                                   MLXBF_BOOTCTL_EMMC) < 0)

> +               pr_err("Unable to reset the EMMC boot mode\n");

Why pr_? Shouldn't be dev_?

> +
> +       pr_info("%s (version %s)\n", MLXBF_BOOTCTL_DRIVER_DESCRIPTION,

Ditto.

> +               MLXBF_BOOTCTL_DRIVER_VERSION);

No, the driver version is a git SHA sum of the certain tree state.
Remove this definition completely.


> +
> +       return 0;
> +}
> +

> +static int mlxbf_bootctl_remove(struct platform_device *pdev)
> +{
> +       return 0;
> +}

Waste of lines.

> +/* ARM Standard Service Calls version numbers */
> +#define MLXBF_BOOTCTL_SVC_VERSION_MAJOR                0x0
> +#define MLXBF_BOOTCTL_SVC_VERSION_MINOR                0x2
> +
> +/* Number of svc calls defined. */
> +#define MLXBF_BOOTCTL_NUM_SVC_CALLS 12
> +
> +/* 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 */

Since you have this as a sequential starting from 0 you may redefine
 boot_names[]  as static const char * const and use sysfs_match_string() above.

> +/* Error values (non-zero). */
> +#define MLXBF_BOOTCTL_SMCCC_INVALID_PARAMETERS -2

Is it coming from hardware / firmware ?
Otherwise use standard meaningful kernel error codes.

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 34+ messages in thread

* RE: [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-01-29 22:03 ` Andy Shevchenko
@ 2019-01-30  6:24   ` Vadim Pasternak
  2019-01-30 20:56     ` Liming Sun
  2019-01-30 20:47   ` Liming Sun
  1 sibling, 1 reply; 34+ messages in thread
From: Vadim Pasternak @ 2019-01-30  6:24 UTC (permalink / raw)
  To: Andy Shevchenko, Liming Sun
  Cc: Andy Shevchenko, Darren Hart, David Woods, Platform Driver,
	Linux Kernel Mailing List


[...]

Please, be consistent with naming convention.
All the above should have same prefix as others routines.

> 
> > +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 (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_POST_RESET_WDOG,
> > +                                   watchdog) < 0)
> > +               return -EINVAL;
> 
> If that call returns an error it shouldn't be shadowed here.
> 
> > +
> > +       return count;
> > +}
> > +
> > +static ssize_t reset_action_show(struct device_driver *drv, char
> > +*buf) {
> 
> > +       return sprintf(buf, "%s\n", reset_action_to_string(
> > +
> > + mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_RESET_ACTION)));
> 
> Wouldn't be easy to parse this as
> 
> int action = ...call0();
> return sprintf(...);
> 
> ?
> 
> (int is an arbitrary type here, choose one that suits)
> 
> > +}
> > +
> > +static ssize_t reset_action_store(struct device_driver *drv,
> > +                                 const char *buf, size_t count) {
> > +       int action = reset_action_to_val(buf, count);
> > +
> 
> > +       if (action < 0 || action == MLXBF_BOOTCTL_NONE)
> > +               return -EINVAL;
> 
> Don't shadow an error.
> 
> > +
> > +       if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION,
> action) < 0)
> > +               return -EINVAL;
> 
> Same.
> 
> > +
> > +       return count;
> > +}
> > +
> > +static ssize_t second_reset_action_show(struct device_driver *drv,
> > +char *buf) {
> 
> > +       return sprintf(buf, "%s\n", reset_action_to_string(
> > +               mlxbf_bootctl_smc_call0(
> > +                       MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION)));
> 
> Use temp variable.
> 
> > +}
> > +
> > +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);
> > +
> > +       if (action < 0)
> > +               return -EINVAL;
> 
> Don't shadow an error.
> 
> > +
> > +       if
> (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION,
> > +                                   action) < 0)
> > +               return -EINVAL;
> 
> Same.
> 
> > +
> > +       return count;
> > +}
> > +
> > +static ssize_t lifecycle_state_show(struct device_driver *drv, char
> > +*buf) {
> 
> > +       int lc_state = mlxbf_bootctl_smc_call1(
> > +                               MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> > +                               MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
> 
> Split it as
> 
> int ...;
> 
> ... = call1();
> if (...)
> 
> > +
> > +       if (lc_state < 0)
> > +               return -EINVAL;
> 
> Don't shadow an error.
> 
> > +
> > +       lc_state &= (MLXBF_BOOTCTL_SB_MODE_TEST_MASK |
> > +                    MLXBF_BOOTCTL_SB_MODE_SECURE_MASK);
> 
> Better to split like
> 
> xxx =
>  (A | B);
> 
> > +       /*
> > +        * 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_MODE_TEST_MASK) != 0) {
> 
> ' != 0' is redundant.
> 
> > +
> > +               lc_state &= MLXBF_BOOTCTL_SB_MODE_SECURE_MASK;
> > +
> 
> > +               return sprintf(buf, "%s(test)\n",
> > +
> > + mlxbf_bootctl_lifecycle_states[lc_state]);
> 
> One line?
> 
> > +       }
> > +
> > +       return sprintf(buf, "%s\n",
> > +mlxbf_bootctl_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 = mlxbf_bootctl_smc_call1(
> > +                               MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> > +                               MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
> > +
> > +       if (sb_key_state < 0)
> > +               return -EINVAL;
> > +
> 
> > +       for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
> 
> I'm not sure it's a good idea to put several lines in one sysfs attribute.
> 
> > +               int burnt = ((sb_key_state & (1 << key)) != 0);
> 
> Redundant  ' != 0', redundant parens.
> 
> > +               int valid = ((sb_key_state &
> > +                             (1 << (key + MLXBF_SB_KEY_NUM))) != 0);
> 
> Same.
> 
> > +
> > +               buf_len += sprintf(buf + buf_len, "Ver%d:", key);
> > +               if (upper_key_used) {
> > +                       if (burnt) {
> > +                               if (valid)
> > +                                       buf_len += sprintf(buf + buf_len,
> > +                                                         "Used");
> 
> Oh, why not just
> 
> const char *status;
> 
> if (...) {
> ...
>  status = "1";
> ...
> status = "2";
> ...
> }
> len = snprintf(buf + len, ..., status);
> 
> ?
> 
> > +                               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;
> > +}
> > +
> 
> > +#define MLXBF_BOOTCTL_DRV_ATTR(_name) DRIVER_ATTR_RW(_name)
> 
> What the point?
> 
> > +static struct attribute_group mlxbf_bootctl_attr_group = {
> > +       .attrs = mlxbf_bootctl_dev_attrs
> 
> + comma.
> 
[...]

^ permalink raw reply	[flat|nested] 34+ messages in thread

* RE: [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-01-29 22:03 ` Andy Shevchenko
  2019-01-30  6:24   ` Vadim Pasternak
@ 2019-01-30 20:47   ` Liming Sun
  2019-01-30 21:17     ` Andy Shevchenko
  1 sibling, 1 reply; 34+ messages in thread
From: Liming Sun @ 2019-01-30 20:47 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	Platform Driver, Linux Kernel Mailing List

Thanks Andy for the comments! Please also see my response inline.

I do have a question about the "return value shadow an error" comment. Could you help take a look if I understand it correctly?

Regards,
Liming
 
> -----Original Message-----
> From: Andy Shevchenko <andy.shevchenko@gmail.com>
> Sent: Tuesday, January 29, 2019 5:03 PM
> To: Liming Sun <lsun@mellanox.com>
> Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>; David
> Woods <dwoods@mellanox.com>; Platform Driver <platform-driver-x86@vger.kernel.org>; Linux Kernel Mailing List <linux-
> kernel@vger.kernel.org>
> Subject: Re: [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
> 
> On Tue, Jan 29, 2019 at 10:59 PM Liming Sun <lsun@mellanox.com> wrote:
> >
> > This commit adds the bootctl platform driver for Mellanox BlueField
> > Soc, which controls the eMMC boot partition swapping and related
> > watchdog configuration.
> 
> Thanks for the patch.
> 
> My comments below.
> 
> First of all, is it a real watchdog with a driver? I think watchdog in
> that case should be set up through standard watchdog facilities.

This is not a watchdog driver itself. Instead, it provides interface to 
user-space and use ARM SMC calls to ATF to configure registers and
watchdog. I'll update the commit message in v2 to clarify it.

> 
> > Reviewed-by: David Woods <dwoods@mellanox.com>
> 
> As for Mellanox team I would recommend to take this review as few
> basics of kernel programming style and some standard practices.

Thanks!

> 
> > Signed-off-by: Liming Sun <lsun@mellanox.com>
> 
> > +config MLXBF_BOOTCTL
> > +       tristate "Mellanox BlueField Firmware Boot Control driver"
> > +       depends on ARM64
> 
> > +       default m
> 
> Why? What would happen if user chooses n?

Will remove the " default m" in v2.

> 
> > +       help
> > +          The Mellanox BlueField firmware implements functionality to
> > +          request swapping the primary and alternate eMMC boot
> > +          partition, and to set up a watchdog that can undo that swap
> > +          if the system does not boot up correctly.  This driver
> > +          provides sysfs access to the firmware, to be used in
> > +          conjunction with the eMMC device driver to do any necessary
> > +          initial swap of the boot partition.
> 
> > +struct mlxbf_bootctl_name {
> > +       int value;
> > +       const char name[12];
> 
> Can't we do simple
> 
> const char *name;
> 
> ?
> 
> Why do we need the limitation. Why is it hard coded like this?

I don't see any reason to hardcode it. Will update it in v2 as suggested.

> 
> > +};
> > +
> > +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"          },
> 
> > +       { -1,                           ""              }
> 
> -1 is usually a bad idea for terminator. It's not in align with many
> other structures which require terminator.

Will update in v2 to remove the terminator and use ARRAY_SIZE()
to loop it instead.

> 
> > +};
> 
> > +
> > +static char mlxbf_bootctl_lifecycle_states[][16] = {
> 
> static const char * const ... ?

Will update it in v2.

> 
> > +       [0] = "soft_non_secure",
> > +       [1] = "secure",
> > +       [2] = "hard_non_secure",
> > +       [3] = "rma",
> > +};
> 
> > +/* Syntactic sugar to avoid having to specify an unused argument. */
> > +#define mlxbf_bootctl_smc_call0(smc_op) mlxbf_bootctl_smc_call1(smc_op, 0)
> > +
> > +static int reset_action_to_val(const char *action, size_t len)
> > +{
> > +       struct mlxbf_bootctl_name *bn;
> > +
> 
> > +       /* Accept string either with or without a newline terminator */
> > +       if (action[len-1] == '\n')
> > +               --len;
> 
> For a long time we have sysfs_streq() API.

Thanks, will update it in v2.

> 
> > +
> > +       for (bn = boot_names; bn->value >= 0; ++bn)
> > +               if (strncmp(bn->name, action, len) == 0)
> > +                       break;
> > +
> > +       return bn->value;
> > +}
> 
> > +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 (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_POST_RESET_WDOG,
> > +                                   watchdog) < 0)
> > +               return -EINVAL;
> 
> If that call returns an error it shouldn't be shadowed here.

Not sure I understand this comment correctly or not.
This function is defined by DRIVER_ATTR_RW(), which appears to expect
ssize_t or an error code as return value according to other examples I saw.

> 
> > +
> > +       return count;
> > +}
> > +
> > +static ssize_t reset_action_show(struct device_driver *drv, char *buf)
> > +{
> 
> > +       return sprintf(buf, "%s\n", reset_action_to_string(
> > +               mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_RESET_ACTION)));
> 
> Wouldn't be easy to parse this as
> 
> int action = ...call0();
> return sprintf(...);
> 
> ?
> 
> (int is an arbitrary type here, choose one that suits)

Done. Will update it in v2.

> 
> > +}
> > +
> > +static ssize_t reset_action_store(struct device_driver *drv,
> > +                                 const char *buf, size_t count)
> > +{
> > +       int action = reset_action_to_val(buf, count);
> > +
> 
> > +       if (action < 0 || action == MLXBF_BOOTCTL_NONE)
> > +               return -EINVAL;
> 
> Don't shadow an error.

This one is also the same, which is defined by DRIVER_ATTR_RW() and
expects ssize_t or error code as return value. Please correct me if I am wrong.

> 
> > +
> > +       if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION, action) < 0)
> > +               return -EINVAL;
> 
> Same.

Same.

> 
> > +
> > +       return count;
> > +}
> > +
> > +static ssize_t second_reset_action_show(struct device_driver *drv, char *buf)
> > +{
> 
> > +       return sprintf(buf, "%s\n", reset_action_to_string(
> > +               mlxbf_bootctl_smc_call0(
> > +                       MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION)));
> 
> Use temp variable.

Done. Will update it in v2.

> 
> > +}
> > +
> > +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);
> > +
> > +       if (action < 0)
> > +               return -EINVAL;
> 
> Don't shadow an error.

Same xxx_store() function defined by DRIVER_ATTR_RW()

> 
> > +
> > +       if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION,
> > +                                   action) < 0)
> > +               return -EINVAL;
> 
> Same.

Same

> 
> > +
> > +       return count;
> > +}
> > +
> > +static ssize_t lifecycle_state_show(struct device_driver *drv, char *buf)
> > +{
> 
> > +       int lc_state = mlxbf_bootctl_smc_call1(
> > +                               MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> > +                               MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
> 
> Split it as
> 
> int ...;
> 
> ... = call1();
> if (...)

Done. Will update it in v2.

> 
> > +
> > +       if (lc_state < 0)
> > +               return -EINVAL;
> 
> Don't shadow an error.

Same as above, which seems expected to return error code in such
functions.

> 
> > +
> > +       lc_state &= (MLXBF_BOOTCTL_SB_MODE_TEST_MASK |
> > +                    MLXBF_BOOTCTL_SB_MODE_SECURE_MASK);
> 
> Better to split like
> 
> xxx =
>  (A | B);

It seems hard to do "(A | B);" within 80 characters plus the indents.

> 
> > +       /*
> > +        * 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_MODE_TEST_MASK) != 0) {
> 
> ' != 0' is redundant.

Removed. Will update it in v2.

> 
> > +
> > +               lc_state &= MLXBF_BOOTCTL_SB_MODE_SECURE_MASK;
> > +
> 
> > +               return sprintf(buf, "%s(test)\n",
> > +                              mlxbf_bootctl_lifecycle_states[lc_state]);
> 
> One line?

It seems hard to fit them into 80 characters in one line.

> 
> > +       }
> > +
> > +       return sprintf(buf, "%s\n", mlxbf_bootctl_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 = mlxbf_bootctl_smc_call1(
> > +                               MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> > +                               MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
> > +
> > +       if (sb_key_state < 0)
> > +               return -EINVAL;
> > +
> 
> > +       for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
> 
> I'm not sure it's a good idea to put several lines in one sysfs attribute.

Will update it in v2.

> 
> > +               int burnt = ((sb_key_state & (1 << key)) != 0);
> 
> Redundant  ' != 0', redundant parens.

Will update it in v2.

> 
> > +               int valid = ((sb_key_state &
> > +                             (1 << (key + MLXBF_SB_KEY_NUM))) != 0);
> 
> Same.

Will update it in v2.

> 
> > +
> > +               buf_len += sprintf(buf + buf_len, "Ver%d:", key);
> > +               if (upper_key_used) {
> > +                       if (burnt) {
> > +                               if (valid)
> > +                                       buf_len += sprintf(buf + buf_len,
> > +                                                         "Used");
> 
> Oh, why not just
> 
> const char *status;
> 
> if (...) {
> ...
>  status = "1";
> ...
> status = "2";
> ...
> }
> len = snprintf(buf + len, ..., status);
> 
> ?

Good idea! Will update it in v2.

> 
> > +                               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;
> > +}
> > +
> 
> > +#define MLXBF_BOOTCTL_DRV_ATTR(_name) DRIVER_ATTR_RW(_name)
> 
> What the point?

Seems no point... Will update it in v2.

> 
> > +static struct attribute_group mlxbf_bootctl_attr_group = {
> > +       .attrs = mlxbf_bootctl_dev_attrs
> 
> + comma.
> 
> > +};
> 
> > +static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
> > +       {"MLNXBF04", 0},
> 
> > +       {},
> 
> No comma for terminator line.
> 
> > +};

Done. Will update it in v2.

> 
> 
> > +static int mlxbf_bootctl_probe(struct platform_device *pdev)
> > +{
> > +       struct arm_smccc_res res;
> > +
> > +       /*
> > +        * 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(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res);
> 
> 
> 
> > +       if (res.a0 != 0x89c036b4 || res.a1 != 0x11e6e7d7 ||
> > +           res.a2 != 0x1a009787 || res.a3 != 0xc4bf00ca)
> 
> What is this?!
> 
> Can you use UUID API?

Yes, it is UUID comparison. The SMC call returns four 'unsigned long' from ATF
to represent the UUID. There seems no existing APIs in uapi/linux/uuid.h to
compare such special format. How about replacing it with comment and MACROs
instead of the hardcoded values to make it more readable?

> 
> > +               return -ENODEV;
> > +
> > +       /*
> > +        * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_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.
> > +        */
> > +       if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION,
> > +                                   MLXBF_BOOTCTL_EMMC) < 0)
> 
> > +               pr_err("Unable to reset the EMMC boot mode\n");
> 
> Why pr_? Shouldn't be dev_?

Yes, will update it in v2.

> 
> > +
> > +       pr_info("%s (version %s)\n", MLXBF_BOOTCTL_DRIVER_DESCRIPTION,
> 
> Ditto.

Will update it in v2 (by just removing this line) according to next comment.

> 
> > +               MLXBF_BOOTCTL_DRIVER_VERSION);
> 
> No, the driver version is a git SHA sum of the certain tree state.
> Remove this definition completely.

Will update it in v2.

> 
> 
> > +
> > +       return 0;
> > +}
> > +
> 
> > +static int mlxbf_bootctl_remove(struct platform_device *pdev)
> > +{
> > +       return 0;
> > +}
> 
> Waste of lines.

Removed. Will update it in v2.

> 
> > +/* ARM Standard Service Calls version numbers */
> > +#define MLXBF_BOOTCTL_SVC_VERSION_MAJOR                0x0
> > +#define MLXBF_BOOTCTL_SVC_VERSION_MINOR                0x2
> > +
> > +/* Number of svc calls defined. */
> > +#define MLXBF_BOOTCTL_NUM_SVC_CALLS 12
> > +
> > +/* 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 */
> 
> Since you have this as a sequential starting from 0 you may redefine
>  boot_names[]  as static const char * const and use sysfs_match_string() above.

The MLXBF_BOOTCTL_NONE is not sequential. Considering it needs to
match both the action id and the string name (in mlxbf_bootctl_reset_action_to_val
and mlxbf_bootctl_reset_action_to_string), using a for loop and sysfs_streq()
might be more consistent here. Another reason is that more names and actions
might be added later for new boards, and the actions needs to be the same
as defined in ATF. So keep the current structure could be more flexible for
future expansion.

> 
> > +/* Error values (non-zero). */
> > +#define MLXBF_BOOTCTL_SMCCC_INVALID_PARAMETERS -2
> 
> Is it coming from hardware / firmware ?
> Otherwise use standard meaningful kernel error codes.

Removed this line. Will update it in v2.

> 
> --
> With Best Regards,
> Andy Shevchenko

^ permalink raw reply	[flat|nested] 34+ messages in thread

* RE: [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-01-30  6:24   ` Vadim Pasternak
@ 2019-01-30 20:56     ` Liming Sun
  0 siblings, 0 replies; 34+ messages in thread
From: Liming Sun @ 2019-01-30 20:56 UTC (permalink / raw)
  To: Vadim Pasternak, Andy Shevchenko
  Cc: Andy Shevchenko, Darren Hart, David Woods, Platform Driver,
	Linux Kernel Mailing List

Thanks Vadim. Please see my response and questions below.

Regards,
Liming

> -----Original Message-----
> From: Vadim Pasternak
> Sent: Wednesday, January 30, 2019 1:24 AM
> To: Andy Shevchenko <andy.shevchenko@gmail.com>; Liming Sun <lsun@mellanox.com>
> Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; David Woods <dwoods@mellanox.com>; Platform
> Driver <platform-driver-x86@vger.kernel.org>; Linux Kernel Mailing List <linux-kernel@vger.kernel.org>
> Subject: RE: [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
> 
> 
> [...]
> 
> Please, be consistent with naming convention.
> All the above should have same prefix as others routines.

I fixed reset_action_to_val() and reset_action_to_string() and will update
them in v2.

Other static functions like xxx_show() and xxx_store() are actually defined 
by kernel macro DRIVER_ATTR_RW(name) and DRIVER_ATTR_RO(name), 
where the 'name' are attribute names which will be displayed in sysfs. 
In order to use "mlx_bf_" prefix for the functions, the same prefix will need 
to be  added to the attribute name as well, which seems unusual in sysfs. 
So probably I could leave those xxx_show() and xxx_store() attribute functions
as it is? 

> 
> >
> > > +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 (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_POST_RESET_WDOG,
> > > +                                   watchdog) < 0)
> > > +               return -EINVAL;
> >
> > If that call returns an error it shouldn't be shadowed here.
> >
> > > +
> > > +       return count;
> > > +}
> > > +
> > > +static ssize_t reset_action_show(struct device_driver *drv, char
> > > +*buf) {
> >
> > > +       return sprintf(buf, "%s\n", reset_action_to_string(
> > > +
> > > + mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_RESET_ACTION)));
> >
> > Wouldn't be easy to parse this as
> >
> > int action = ...call0();
> > return sprintf(...);
> >
> > ?
> >
> > (int is an arbitrary type here, choose one that suits)
> >
> > > +}
> > > +
> > > +static ssize_t reset_action_store(struct device_driver *drv,
> > > +                                 const char *buf, size_t count) {
> > > +       int action = reset_action_to_val(buf, count);
> > > +
> >
> > > +       if (action < 0 || action == MLXBF_BOOTCTL_NONE)
> > > +               return -EINVAL;
> >
> > Don't shadow an error.
> >
> > > +
> > > +       if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION,
> > action) < 0)
> > > +               return -EINVAL;
> >
> > Same.
> >
> > > +
> > > +       return count;
> > > +}
> > > +
> > > +static ssize_t second_reset_action_show(struct device_driver *drv,
> > > +char *buf) {
> >
> > > +       return sprintf(buf, "%s\n", reset_action_to_string(
> > > +               mlxbf_bootctl_smc_call0(
> > > +                       MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION)));
> >
> > Use temp variable.
> >
> > > +}
> > > +
> > > +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);
> > > +
> > > +       if (action < 0)
> > > +               return -EINVAL;
> >
> > Don't shadow an error.
> >
> > > +
> > > +       if
> > (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION,
> > > +                                   action) < 0)
> > > +               return -EINVAL;
> >
> > Same.
> >
> > > +
> > > +       return count;
> > > +}
> > > +
> > > +static ssize_t lifecycle_state_show(struct device_driver *drv, char
> > > +*buf) {
> >
> > > +       int lc_state = mlxbf_bootctl_smc_call1(
> > > +                               MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> > > +                               MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
> >
> > Split it as
> >
> > int ...;
> >
> > ... = call1();
> > if (...)
> >
> > > +
> > > +       if (lc_state < 0)
> > > +               return -EINVAL;
> >
> > Don't shadow an error.
> >
> > > +
> > > +       lc_state &= (MLXBF_BOOTCTL_SB_MODE_TEST_MASK |
> > > +                    MLXBF_BOOTCTL_SB_MODE_SECURE_MASK);
> >
> > Better to split like
> >
> > xxx =
> >  (A | B);
> >
> > > +       /*
> > > +        * 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_MODE_TEST_MASK) != 0) {
> >
> > ' != 0' is redundant.
> >
> > > +
> > > +               lc_state &= MLXBF_BOOTCTL_SB_MODE_SECURE_MASK;
> > > +
> >
> > > +               return sprintf(buf, "%s(test)\n",
> > > +
> > > + mlxbf_bootctl_lifecycle_states[lc_state]);
> >
> > One line?
> >
> > > +       }
> > > +
> > > +       return sprintf(buf, "%s\n",
> > > +mlxbf_bootctl_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 = mlxbf_bootctl_smc_call1(
> > > +                               MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> > > +                               MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
> > > +
> > > +       if (sb_key_state < 0)
> > > +               return -EINVAL;
> > > +
> >
> > > +       for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
> >
> > I'm not sure it's a good idea to put several lines in one sysfs attribute.
> >
> > > +               int burnt = ((sb_key_state & (1 << key)) != 0);
> >
> > Redundant  ' != 0', redundant parens.
> >
> > > +               int valid = ((sb_key_state &
> > > +                             (1 << (key + MLXBF_SB_KEY_NUM))) != 0);
> >
> > Same.
> >
> > > +
> > > +               buf_len += sprintf(buf + buf_len, "Ver%d:", key);
> > > +               if (upper_key_used) {
> > > +                       if (burnt) {
> > > +                               if (valid)
> > > +                                       buf_len += sprintf(buf + buf_len,
> > > +                                                         "Used");
> >
> > Oh, why not just
> >
> > const char *status;
> >
> > if (...) {
> > ...
> >  status = "1";
> > ...
> > status = "2";
> > ...
> > }
> > len = snprintf(buf + len, ..., status);
> >
> > ?
> >
> > > +                               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;
> > > +}
> > > +
> >
> > > +#define MLXBF_BOOTCTL_DRV_ATTR(_name) DRIVER_ATTR_RW(_name)
> >
> > What the point?
> >
> > > +static struct attribute_group mlxbf_bootctl_attr_group = {
> > > +       .attrs = mlxbf_bootctl_dev_attrs
> >
> > + comma.
> >
> [...]

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-01-30 20:47   ` Liming Sun
@ 2019-01-30 21:17     ` Andy Shevchenko
  2019-01-31 16:53       ` Liming Sun
  0 siblings, 1 reply; 34+ messages in thread
From: Andy Shevchenko @ 2019-01-30 21:17 UTC (permalink / raw)
  To: Liming Sun
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	Platform Driver, Linux Kernel Mailing List

On Wed, Jan 30, 2019 at 10:47 PM Liming Sun <lsun@mellanox.com> wrote:

> > First of all, is it a real watchdog with a driver? I think watchdog in
> > that case should be set up through standard watchdog facilities.
>
> This is not a watchdog driver itself. Instead, it provides interface to
> user-space and use ARM SMC calls to ATF to configure registers and
> watchdog. I'll update the commit message in v2 to clarify it.

Hmm... For example Intel MID platforms have SCU (system controller
unit) that provides a watchdog facility. In the kernel we have a
watchdog driver for that.
Can't you do similar for your case?

> > > +       if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_POST_RESET_WDOG,
> > > +                                   watchdog) < 0)
> > > +               return -EINVAL;
> >
> > If that call returns an error it shouldn't be shadowed here.
>
> Not sure I understand this comment correctly or not.
> This function is defined by DRIVER_ATTR_RW(), which appears to expect
> ssize_t or an error code as return value according to other examples I saw.

What is returned by mlx...call1() should be propagated to the actual caller.
Same comment for all similar cases.

> > > +       lc_state &= (MLXBF_BOOTCTL_SB_MODE_TEST_MASK |
> > > +                    MLXBF_BOOTCTL_SB_MODE_SECURE_MASK);
> >
> > Better to split like
> >
> > xxx =
> >  (A | B);
>
> It seems hard to do "(A | B);" within 80 characters plus the indents.

Repeating myself, it's still better than your variant for readability.

> > > +       if (res.a0 != 0x89c036b4 || res.a1 != 0x11e6e7d7 ||
> > > +           res.a2 != 0x1a009787 || res.a3 != 0xc4bf00ca)
> >
> > What is this?!
> >
> > Can you use UUID API?
>
> Yes, it is UUID comparison. The SMC call returns four 'unsigned long' from ATF
> to represent the UUID. There seems no existing APIs in uapi/linux/uuid.h to
> compare such special format. How about replacing it with comment and MACROs
> instead of the hardcoded values to make it more readable?

Should be no magic numbers involved inside the function at the end.
Use descriptive definitions and I still recommend to give a look at
UUID API how it can be utilized here.
(hint: Thunderbolt hw is operating with integers, though driver uses
UUID API at the end)

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH v2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-01-29 20:59 [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc Liming Sun
  2019-01-29 22:03 ` Andy Shevchenko
@ 2019-01-31 16:53 ` Liming Sun
  2019-01-31 17:02   ` Andy Shevchenko
  2019-01-31 19:19 ` [PATCH v3] " Liming Sun
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 34+ messages in thread
From: Liming Sun @ 2019-01-31 16:53 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods
  Cc: Liming Sun, platform-driver-x86, linux-kernel

This commit adds the bootctl platform driver for Mellanox BlueField
Soc, which controls the eMMC boot partition swapping and sends SMC
calls to ATF running at exception level EL3 to program some system
register. This register is only accessible in secure code and is
used to enable the watchdog after reboot.

Below are the sequences of a typical use case.

  1. User-space tool upgrades one eMMC boot partition and requests
     the boot partition swapping;

  2. The bootctl driver handles such request and sends SMC call
     to ATF. ATF programs register BREADCRUMB0 which has value
     preserved during software reset. It also programs eMMC to
     swap the boot partition;

  3. After software reset (rebooting), ATF BL1 (BootRom) checks
     register BREADCRUMB0 to enable watchdog if configured;

  4. If booting fails, the watchdog timer will trigger rebooting.
     In such case, ATF BootRom will switch the boot partition
     to the previous one.

Reviewed-by: David Woods <dwoods@mellanox.com>
Signed-off-by: Liming Sun <lsun@mellanox.com>
---
 drivers/platform/mellanox/Kconfig         |  14 +-
 drivers/platform/mellanox/Makefile        |   1 +
 drivers/platform/mellanox/mlxbf-bootctl.c | 306 ++++++++++++++++++++++++++++++
 drivers/platform/mellanox/mlxbf-bootctl.h | 108 +++++++++++
 4 files changed, 428 insertions(+), 1 deletion(-)
 create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.c
 create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.h

diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig
index cd8a908..e3c6a5e 100644
--- a/drivers/platform/mellanox/Kconfig
+++ b/drivers/platform/mellanox/Kconfig
@@ -5,7 +5,7 @@
 
 menuconfig MELLANOX_PLATFORM
 	bool "Platform support for Mellanox hardware"
-	depends on X86 || ARM || COMPILE_TEST
+	depends on X86 || ARM || ARM64 || COMPILE_TEST
 	---help---
 	  Say Y here to get to see options for platform support for
 	  Mellanox systems. This option alone does not add any kernel code.
@@ -34,4 +34,16 @@ config MLXREG_IO
 	  to system resets operation, system reset causes monitoring and some
 	  kinds of mux selection.
 
+config MLXBF_BOOTCTL
+	tristate "Mellanox BlueField Firmware Boot Control driver"
+	depends on ARM64
+	help
+          The Mellanox BlueField firmware implements functionality to
+          request swapping the primary and alternate eMMC boot
+          partition, and to set up a watchdog that can undo that swap
+          if the system does not boot up correctly.  This driver
+          provides sysfs access to the firmware, to be used in
+          conjunction with the eMMC device driver to do any necessary
+          initial swap of the boot partition.
+
 endif # MELLANOX_PLATFORM
diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile
index 57074d9c..76b0370 100644
--- a/drivers/platform/mellanox/Makefile
+++ b/drivers/platform/mellanox/Makefile
@@ -5,3 +5,4 @@
 #
 obj-$(CONFIG_MLXREG_HOTPLUG)	+= mlxreg-hotplug.o
 obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o
+obj-$(CONFIG_MLXBF_BOOTCTL)	+= mlxbf-bootctl.o
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c
new file mode 100644
index 0000000..7efa535
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf-bootctl.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Mellanox boot control driver
+ * This driver provides a sysfs interface for systems management
+ * software to manage reset-time actions.
+ *
+ * Copyright (C) 2019 Mellanox Technologies
+ */
+
+#include <linux/acpi.h>
+#include <linux/arm-smccc.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "mlxbf-bootctl.h"
+
+#define MLXBF_BOOTCTL_DRIVER_NAME		"mlxbf-bootctl"
+#define MLXBF_BOOTCTL_DRIVER_DESCRIPTION	"Mellanox boot control driver"
+
+#define MLXBF_BOOTCTL_SB_SECURE_MASK		0x03
+#define MLXBF_BOOTCTL_SB_TEST_MASK		0x0c
+
+#define MLXBF_SB_KEY_NUM			4
+
+/* UUID used to probe ATF service. */
+static const char * const mlxbf_bootctl_svc_uuid_str =
+	"89c036b4-e7d7-11e6-8797-001aca00bfc4";
+
+/*
+ * UUID structure used to match the returned value from ATF in
+ * four 32-bit words. No need to do endian conversion here.
+ */
+union mlxbf_bootctl_uuid {
+	guid_t guid;
+	u32 words[4];
+};
+
+struct mlxbf_bootctl_name {
+	u32 value;
+	const char *name;
+};
+
+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 const char * const mlxbf_bootctl_lifecycle_states[] = {
+	[0] = "soft_non_secure",
+	[1] = "secure",
+	[2] = "hard_non_secure",
+	[3] = "rma",
+};
+
+/* The SMC calls in question are atomic, so we don't have to lock here. */
+static int mlxbf_bootctl_smc_call1(unsigned int smc_op, int smc_arg)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res);
+
+	return res.a0;
+}
+
+/* Syntactic sugar to avoid having to specify an unused argument. */
+#define mlxbf_bootctl_smc_call0(smc_op) mlxbf_bootctl_smc_call1(smc_op, 0)
+
+static int mlxbf_bootctl_reset_action_to_val(const char *action)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
+		if (sysfs_streq(boot_names[i].name, action))
+			return boot_names[i].value;
+
+	return MLXBF_BOOTCTL_INVALID;
+}
+
+static const char *mlxbf_bootctl_reset_action_to_string(int action)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
+		if (boot_names[i].value == action)
+			return boot_names[i].name;
+
+	return "";
+}
+
+static ssize_t post_reset_wdog_show(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "%d\n",
+		mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_POST_RESET_WDOG));
+}
+
+static ssize_t post_reset_wdog_store(struct device_driver *drv,
+				     const char *buf, size_t count)
+{
+	unsigned long watchdog;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &watchdog);
+	if (ret)
+		return ret;
+
+	ret = mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_POST_RESET_WDOG,
+				      watchdog);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t reset_action_show(struct device_driver *drv, char *buf)
+{
+	int action;
+
+	action = mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_RESET_ACTION);
+
+	return sprintf(buf, "%s\n",
+		       mlxbf_bootctl_reset_action_to_string(action));
+}
+
+static ssize_t reset_action_store(struct device_driver *drv,
+				  const char *buf, size_t count)
+{
+	int ret, action = mlxbf_bootctl_reset_action_to_val(buf);
+
+	if (action == MLXBF_BOOTCTL_INVALID || action == MLXBF_BOOTCTL_NONE)
+		return -EINVAL;
+
+	ret = (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION, action));
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t second_reset_action_show(struct device_driver *drv, char *buf)
+{
+	int action;
+	const char *name;
+
+	action = mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION);
+	name = mlxbf_bootctl_reset_action_to_string(action);
+
+	return sprintf(buf, "%s\n", name);
+}
+
+static ssize_t second_reset_action_store(struct device_driver *drv,
+					 const char *buf, size_t count)
+{
+	int ret, action = mlxbf_bootctl_reset_action_to_val(buf);
+
+	if (action < 0)
+		return action;
+
+	ret = mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION,
+				      action);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t lifecycle_state_show(struct device_driver *drv, char *buf)
+{
+	int lc_state;
+
+	lc_state = mlxbf_bootctl_smc_call1(
+					   MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
+					   MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
+
+	if (lc_state < 0)
+		return lc_state;
+
+	lc_state &=
+		(MLXBF_BOOTCTL_SB_TEST_MASK | MLXBF_BOOTCTL_SB_SECURE_MASK);
+
+	/*
+	 * 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;
+
+		return sprintf(buf, "%s(test)\n",
+			       mlxbf_bootctl_lifecycle_states[lc_state]);
+	}
+
+	return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
+}
+
+static ssize_t secure_boot_fuse_state_show(struct device_driver *drv, char *buf)
+{
+	int sb_key_state = mlxbf_bootctl_smc_call1(
+				MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
+				MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
+	int upper_key_used = 0;
+	int buf_len = 0;
+	const char *status;
+	int key;
+
+	if (sb_key_state < 0)
+		return sb_key_state;
+
+	for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
+		int burnt = sb_key_state & (1 << key);
+		int valid = sb_key_state &
+			      (1 << (key + MLXBF_SB_KEY_NUM));
+
+		buf_len += sprintf(buf + buf_len, "Ver%d:", key);
+		if (upper_key_used) {
+			if (burnt)
+				status = valid ? "Used" : "Wasted";
+			else
+				status = valid ? "Invalid" : "Skipped";
+		} else {
+			if (burnt) {
+				status = valid ? "In use" : "Burn incomplete";
+				if (valid)
+					upper_key_used = 1;
+			} else
+				status = valid ? "Invalid" : "Free";
+		}
+		buf_len += sprintf(buf + buf_len, status);
+		if (key != 0)
+			buf_len += sprintf(buf + buf_len, "; ");
+	}
+
+	return buf_len;
+}
+
+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 struct attribute *mlxbf_bootctl_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,
+	NULL
+};
+
+static struct attribute_group mlxbf_bootctl_attr_group = {
+	.attrs = mlxbf_bootctl_dev_attrs
+};
+
+static const struct attribute_group *mlxbf_bootctl_attr_groups[] = {
+	&mlxbf_bootctl_attr_group,
+	NULL
+};
+
+static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
+	{"MLNXBF04", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
+
+static int mlxbf_bootctl_probe(struct platform_device *pdev)
+{
+	struct arm_smccc_res res;
+	union mlxbf_bootctl_uuid uuid;
+
+	/* 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, &uuid.guid);
+	if (res.a0 != uuid.words[0] || res.a1 != uuid.words[1] ||
+	    res.a2 != uuid.words[2] || res.a3 != uuid.words[3])
+		return -ENODEV;
+
+	/*
+	 * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_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.
+	 */
+	if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION,
+				    MLXBF_BOOTCTL_EMMC) < 0)
+		dev_err(&pdev->dev, "Unable to reset the EMMC boot mode\n");
+
+	return 0;
+}
+
+static struct platform_driver mlxbf_bootctl_driver = {
+	.probe = mlxbf_bootctl_probe,
+	.driver = {
+		.name = MLXBF_BOOTCTL_DRIVER_NAME,
+		.groups = mlxbf_bootctl_attr_groups,
+		.acpi_match_table = ACPI_PTR(mlxbf_bootctl_acpi_ids),
+	}
+};
+
+module_platform_driver(mlxbf_bootctl_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
+MODULE_AUTHOR("Mellanox Technologies");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.h b/drivers/platform/mellanox/mlxbf-bootctl.h
new file mode 100644
index 0000000..5ca19db
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf-bootctl.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019, Mellanox Technologies. All rights reserved.
+ */
+
+#ifndef __MLXBF_BOOTCTL_H__
+#define __MLXBF_BOOTCTL_H__
+
+/*
+ * Request that the on-chip watchdog be enabled, or disabled, after
+ * the next chip soft reset. This call does not affect the current
+ * status of the on-chip watchdog. If non-zero, the argument
+ * specifies the watchdog interval in seconds. If zero, the watchdog
+ * 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
+
+/*
+ * 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.
+ */
+#define MLXBF_BOOTCTL_GET_POST_RESET_WDOG	0x82000001
+
+/*
+ * Request that a specific boot action be taken at the next soft
+ * reset. By default, the boot action is set by external chip pins,
+ * which are sampled on hard reset. Note that the boot action
+ * requested by this call will persist on subsequent resets unless
+ * this service, or the MLNX_SET_SECOND_RESET_ACTION service, is
+ * 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
+
+/*
+ * 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).
+ */
+#define MLXBF_BOOTCTL_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
+ * 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
+ * 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.
+ * 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
+
+/*
+ * 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.
+ */
+#define MLXBF_BOOTCTL_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
+
+/*
+ * Reset eMMC by programming the RST_N register.
+ */
+#define MLXBF_BOOTCTL_SET_EMMC_RST_N		0x82000007
+
+#define MLXBF_BOOTCTL_GET_DIMM_INFO		0x82000008
+
+/* 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
+
+/* Number of svc calls defined. */
+#define MLXBF_BOOTCTL_NUM_SVC_CALLS 12
+
+/* 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 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 */
+
+/* Additional value to disable the MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION. */
+#define MLXBF_BOOTCTL_NONE	0x7fffffff /* Don't change next boot action */
+
+/* Invalid mode */
+#define MLXBF_BOOTCTL_INVALID	0xffffffff
+
+#endif /* __MLXBF_BOOTCTL_H__ */
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* RE: [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-01-30 21:17     ` Andy Shevchenko
@ 2019-01-31 16:53       ` Liming Sun
  0 siblings, 0 replies; 34+ messages in thread
From: Liming Sun @ 2019-01-31 16:53 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	Platform Driver, Linux Kernel Mailing List

Thanks!  v2 has been posted trying to solve all the comments. Please also see response inline.

Regards,
Liming

> -----Original Message-----
> From: Andy Shevchenko <andy.shevchenko@gmail.com>
> Sent: Wednesday, January 30, 2019 4:17 PM
> To: Liming Sun <lsun@mellanox.com>
> Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>; David
> Woods <dwoods@mellanox.com>; Platform Driver <platform-driver-x86@vger.kernel.org>; Linux Kernel Mailing List <linux-
> kernel@vger.kernel.org>
> Subject: Re: [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
> 
> On Wed, Jan 30, 2019 at 10:47 PM Liming Sun <lsun@mellanox.com> wrote:
> 
> > > First of all, is it a real watchdog with a driver? I think watchdog in
> > > that case should be set up through standard watchdog facilities.
> >
> > This is not a watchdog driver itself. Instead, it provides interface to
> > user-space and use ARM SMC calls to ATF to configure registers and
> > watchdog. I'll update the commit message in v2 to clarify it.
> 
> Hmm... For example Intel MID platforms have SCU (system controller
> unit) that provides a watchdog facility. In the kernel we have a
> watchdog driver for that.
> Can't you do similar for your case?

The process might be different than what's done on the BlueField SoC.

After the boot-partition swapping, the ARM watchdog is not started
right away. It is started during next rebooting by the ATF BL1 code 
(which is the Boot ROM code than couldn't change). The Boot-ROM 
checks some register value which is preserved during rebooting, and 
knows that a boot-partition swapping is in process, thus enable the 
watchdog timer if configured. At this time, no UEFI or Linux is running, 
just the boot ROM code at the very beginning.

I'll add the sequence of such use case in the commit message of v2.

> 
> > > > +       if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_POST_RESET_WDOG,
> > > > +                                   watchdog) < 0)
> > > > +               return -EINVAL;
> > >
> > > If that call returns an error it shouldn't be shadowed here.
> >
> > Not sure I understand this comment correctly or not.
> > This function is defined by DRIVER_ATTR_RW(), which appears to expect
> > ssize_t or an error code as return value according to other examples I saw.
> 
> What is returned by mlx...call1() should be propagated to the actual caller.
> Same comment for all similar cases.

Make sense! Thanks for the explanation. Updated it in v2.

> 
> > > > +       lc_state &= (MLXBF_BOOTCTL_SB_MODE_TEST_MASK |
> > > > +                    MLXBF_BOOTCTL_SB_MODE_SECURE_MASK);
> > >
> > > Better to split like
> > >
> > > xxx =
> > >  (A | B);
> >
> > It seems hard to do "(A | B);" within 80 characters plus the indents.
> 
> Repeating myself, it's still better than your variant for readability.

Done. Managed to shorten the variable name a little bit.
Updated it in v2.

> 
> > > > +       if (res.a0 != 0x89c036b4 || res.a1 != 0x11e6e7d7 ||
> > > > +           res.a2 != 0x1a009787 || res.a3 != 0xc4bf00ca)
> > >
> > > What is this?!
> > >
> > > Can you use UUID API?
> >
> > Yes, it is UUID comparison. The SMC call returns four 'unsigned long' from ATF
> > to represent the UUID. There seems no existing APIs in uapi/linux/uuid.h to
> > compare such special format. How about replacing it with comment and MACROs
> > instead of the hardcoded values to make it more readable?
> 
> Should be no magic numbers involved inside the function at the end.
> Use descriptive definitions and I still recommend to give a look at
> UUID API how it can be utilized here.
> (hint: Thunderbolt hw is operating with integers, though driver uses
> UUID API at the end)
> 

Updated in v2. Now it should look better :)

> --
> With Best Regards,
> Andy Shevchenko

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-01-31 16:53 ` [PATCH v2] " Liming Sun
@ 2019-01-31 17:02   ` Andy Shevchenko
  2019-01-31 17:18     ` Liming Sun
  0 siblings, 1 reply; 34+ messages in thread
From: Andy Shevchenko @ 2019-01-31 17:02 UTC (permalink / raw)
  To: Liming Sun
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	Platform Driver, Linux Kernel Mailing List

On Thu, Jan 31, 2019 at 6:53 PM Liming Sun <lsun@mellanox.com> wrote:
>
> This commit adds the bootctl platform driver for Mellanox BlueField
> Soc, which controls the eMMC boot partition swapping and sends SMC
> calls to ATF running at exception level EL3 to program some system
> register. This register is only accessible in secure code and is
> used to enable the watchdog after reboot.
>
> Below are the sequences of a typical use case.
>
>   1. User-space tool upgrades one eMMC boot partition and requests
>      the boot partition swapping;
>
>   2. The bootctl driver handles such request and sends SMC call
>      to ATF. ATF programs register BREADCRUMB0 which has value
>      preserved during software reset. It also programs eMMC to
>      swap the boot partition;
>
>   3. After software reset (rebooting), ATF BL1 (BootRom) checks
>      register BREADCRUMB0 to enable watchdog if configured;
>
>   4. If booting fails, the watchdog timer will trigger rebooting.
>      In such case, ATF BootRom will switch the boot partition
>      to the previous one.

Thanks for an update. My comments below.


> Reviewed-by: David Woods <dwoods@mellanox.com>

I'm not sure I see this guy to review v2. Of course if you consider
all changes minor, you may leave this tag.

> Signed-off-by: Liming Sun <lsun@mellanox.com>
> ---

Here should be a changelog what had been done in new version.
> +/* UUID used to probe ATF service. */

> +static const char * const mlxbf_bootctl_svc_uuid_str =
> +       "89c036b4-e7d7-11e6-8797-001aca00bfc4";

static const char *xxx = ...;

> +/*
> + * UUID structure used to match the returned value from ATF in
> + * four 32-bit words. No need to do endian conversion here.
> + */
> +union mlxbf_bootctl_uuid {
> +       guid_t guid;
> +       u32 words[4];
> +};

I'm not sure it's the best you can do. instead of using union, better
to use conversion from and to corresponding uuid type.

The rest I will comment on v3.

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 34+ messages in thread

* RE: [PATCH v2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-01-31 17:02   ` Andy Shevchenko
@ 2019-01-31 17:18     ` Liming Sun
  2019-01-31 19:20       ` Liming Sun
  0 siblings, 1 reply; 34+ messages in thread
From: Liming Sun @ 2019-01-31 17:18 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	Platform Driver, Linux Kernel Mailing List

Please see my response inline. Will provide v3 once I solve the comments.

Thanks,
Liming

> -----Original Message-----
> From: Andy Shevchenko <andy.shevchenko@gmail.com>
> Sent: Thursday, January 31, 2019 12:02 PM
> To: Liming Sun <lsun@mellanox.com>
> Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>; David
> Woods <dwoods@mellanox.com>; Platform Driver <platform-driver-x86@vger.kernel.org>; Linux Kernel Mailing List <linux-
> kernel@vger.kernel.org>
> Subject: Re: [PATCH v2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
> 
> On Thu, Jan 31, 2019 at 6:53 PM Liming Sun <lsun@mellanox.com> wrote:
> >
> > This commit adds the bootctl platform driver for Mellanox BlueField
> > Soc, which controls the eMMC boot partition swapping and sends SMC
> > calls to ATF running at exception level EL3 to program some system
> > register. This register is only accessible in secure code and is
> > used to enable the watchdog after reboot.
> >
> > Below are the sequences of a typical use case.
> >
> >   1. User-space tool upgrades one eMMC boot partition and requests
> >      the boot partition swapping;
> >
> >   2. The bootctl driver handles such request and sends SMC call
> >      to ATF. ATF programs register BREADCRUMB0 which has value
> >      preserved during software reset. It also programs eMMC to
> >      swap the boot partition;
> >
> >   3. After software reset (rebooting), ATF BL1 (BootRom) checks
> >      register BREADCRUMB0 to enable watchdog if configured;
> >
> >   4. If booting fails, the watchdog timer will trigger rebooting.
> >      In such case, ATF BootRom will switch the boot partition
> >      to the previous one.
> 
> Thanks for an update. My comments below.
> 
> 
> > Reviewed-by: David Woods <dwoods@mellanox.com>
> 
> I'm not sure I see this guy to review v2. Of course if you consider
> all changes minor, you may leave this tag.

He sits besides me for internal review. I'll try to ask him to send
comments to the mailing list.

> 
> > Signed-off-by: Liming Sun <lsun@mellanox.com>
> > ---
> 
> Here should be a changelog what had been done in new version.

Will provide changelog in the v3 email.
Or please correct me if you meant adding changelog in the code or
cover letter.

> > +/* UUID used to probe ATF service. */
> 
> > +static const char * const mlxbf_bootctl_svc_uuid_str =
> > +       "89c036b4-e7d7-11e6-8797-001aca00bfc4";
> 
> static const char *xxx = ...;

Will update it in v3.

> 
> > +/*
> > + * UUID structure used to match the returned value from ATF in
> > + * four 32-bit words. No need to do endian conversion here.
> > + */
> > +union mlxbf_bootctl_uuid {
> > +       guid_t guid;
> > +       u32 words[4];
> > +};
> 
> I'm not sure it's the best you can do. instead of using union, better
> to use conversion from and to corresponding uuid type.

I'll try to figure out another way without the union to compare the
uuid directly using uuid api. Will update it in v3.

> 
> The rest I will comment on v3.
> 
> --
> With Best Regards,
> Andy Shevchenko

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH v3] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-01-29 20:59 [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc Liming Sun
  2019-01-29 22:03 ` Andy Shevchenko
  2019-01-31 16:53 ` [PATCH v2] " Liming Sun
@ 2019-01-31 19:19 ` Liming Sun
  2019-02-01  5:16 ` [PATCH v1 1/1] " kbuild test robot
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 34+ messages in thread
From: Liming Sun @ 2019-01-31 19:19 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods
  Cc: Liming Sun, platform-driver-x86, linux-kernel

This commit adds the bootctl platform driver for Mellanox BlueField
Soc, which controls the eMMC boot partition swapping and sends SMC
calls to ATF running at exception level EL3 to program some system
register. This register is only accessible in secure code and is
used to enable the watchdog after reboot.

Below are the sequences of a typical use case.

  1. User-space tool upgrades one eMMC boot partition and requests
     the boot partition swapping;

  2. The bootctl driver handles such request and sends SMC call
     to ATF. ATF programs register BREADCRUMB0 which has value
     preserved during software reset. It also programs eMMC to
     swap the boot partition;

  3. After software reset (rebooting), ATF BL1 (BootRom) checks
     register BREADCRUMB0 to enable watchdog if configured;

  4. If booting fails, the watchdog timer will trigger rebooting.
     In such case, ATF BootRom will switch the boot partition
     to the previous one.

Signed-off-by: Liming Sun <lsun@mellanox.com>

---

v3: More coding style fixes, revised the uuid matching code.
v2: Fix the Kconfig and coding styles, propagate errors to caller.
v1: Initial version.
---
 drivers/platform/mellanox/Kconfig         |  14 +-
 drivers/platform/mellanox/Makefile        |   1 +
 drivers/platform/mellanox/mlxbf-bootctl.c | 310 ++++++++++++++++++++++++++++++
 drivers/platform/mellanox/mlxbf-bootctl.h | 108 +++++++++++
 4 files changed, 432 insertions(+), 1 deletion(-)
 create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.c
 create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.h

diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig
index cd8a908..e3c6a5e 100644
--- a/drivers/platform/mellanox/Kconfig
+++ b/drivers/platform/mellanox/Kconfig
@@ -5,7 +5,7 @@
 
 menuconfig MELLANOX_PLATFORM
 	bool "Platform support for Mellanox hardware"
-	depends on X86 || ARM || COMPILE_TEST
+	depends on X86 || ARM || ARM64 || COMPILE_TEST
 	---help---
 	  Say Y here to get to see options for platform support for
 	  Mellanox systems. This option alone does not add any kernel code.
@@ -34,4 +34,16 @@ config MLXREG_IO
 	  to system resets operation, system reset causes monitoring and some
 	  kinds of mux selection.
 
+config MLXBF_BOOTCTL
+	tristate "Mellanox BlueField Firmware Boot Control driver"
+	depends on ARM64
+	help
+          The Mellanox BlueField firmware implements functionality to
+          request swapping the primary and alternate eMMC boot
+          partition, and to set up a watchdog that can undo that swap
+          if the system does not boot up correctly.  This driver
+          provides sysfs access to the firmware, to be used in
+          conjunction with the eMMC device driver to do any necessary
+          initial swap of the boot partition.
+
 endif # MELLANOX_PLATFORM
diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile
index 57074d9c..76b0370 100644
--- a/drivers/platform/mellanox/Makefile
+++ b/drivers/platform/mellanox/Makefile
@@ -5,3 +5,4 @@
 #
 obj-$(CONFIG_MLXREG_HOTPLUG)	+= mlxreg-hotplug.o
 obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o
+obj-$(CONFIG_MLXBF_BOOTCTL)	+= mlxbf-bootctl.o
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c
new file mode 100644
index 0000000..4c049a0
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf-bootctl.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Mellanox boot control driver
+ * This driver provides a sysfs interface for systems management
+ * software to manage reset-time actions.
+ *
+ * Copyright (C) 2019 Mellanox Technologies
+ */
+
+#include <linux/acpi.h>
+#include <linux/arm-smccc.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "mlxbf-bootctl.h"
+
+#define MLXBF_BOOTCTL_DRIVER_NAME		"mlxbf-bootctl"
+#define MLXBF_BOOTCTL_DRIVER_DESCRIPTION	"Mellanox boot control driver"
+
+#define MLXBF_BOOTCTL_SB_SECURE_MASK		0x03
+#define MLXBF_BOOTCTL_SB_TEST_MASK		0x0c
+
+#define MLXBF_SB_KEY_NUM			4
+
+/* UUID used to probe ATF service. */
+static const char * const mlxbf_bootctl_svc_uuid_str =
+	"89c036b4-e7d7-11e6-8797-001aca00bfc4";
+
+struct mlxbf_bootctl_name {
+	u32 value;
+	const char *name;
+};
+
+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 const char * const mlxbf_bootctl_lifecycle_states[] = {
+	[0] = "soft_non_secure",
+	[1] = "secure",
+	[2] = "hard_non_secure",
+	[3] = "rma",
+};
+
+/* The SMC calls in question are atomic, so we don't have to lock here. */
+static int mlxbf_bootctl_smc_call1(unsigned int smc_op, int smc_arg)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res);
+
+	return res.a0;
+}
+
+/* Syntactic sugar to avoid having to specify an unused argument. */
+#define mlxbf_bootctl_smc_call0(smc_op) mlxbf_bootctl_smc_call1(smc_op, 0)
+
+static int mlxbf_bootctl_reset_action_to_val(const char *action)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
+		if (sysfs_streq(boot_names[i].name, action))
+			return boot_names[i].value;
+
+	return MLXBF_BOOTCTL_INVALID;
+}
+
+static const char *mlxbf_bootctl_reset_action_to_string(int action)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
+		if (boot_names[i].value == action)
+			return boot_names[i].name;
+
+	return "";
+}
+
+static ssize_t post_reset_wdog_show(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "%d\n",
+		mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_POST_RESET_WDOG));
+}
+
+static ssize_t post_reset_wdog_store(struct device_driver *drv,
+				     const char *buf, size_t count)
+{
+	unsigned long watchdog;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &watchdog);
+	if (ret)
+		return ret;
+
+	ret = mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_POST_RESET_WDOG,
+				      watchdog);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t reset_action_show(struct device_driver *drv, char *buf)
+{
+	int action;
+
+	action = mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_RESET_ACTION);
+
+	return sprintf(buf, "%s\n",
+		       mlxbf_bootctl_reset_action_to_string(action));
+}
+
+static ssize_t reset_action_store(struct device_driver *drv,
+				  const char *buf, size_t count)
+{
+	int ret, action = mlxbf_bootctl_reset_action_to_val(buf);
+
+	if (action == MLXBF_BOOTCTL_INVALID || action == MLXBF_BOOTCTL_NONE)
+		return -EINVAL;
+
+	ret = (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION, action));
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t second_reset_action_show(struct device_driver *drv, char *buf)
+{
+	int action;
+	const char *name;
+
+	action = mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION);
+	name = mlxbf_bootctl_reset_action_to_string(action);
+
+	return sprintf(buf, "%s\n", name);
+}
+
+static ssize_t second_reset_action_store(struct device_driver *drv,
+					 const char *buf, size_t count)
+{
+	int ret, action = mlxbf_bootctl_reset_action_to_val(buf);
+
+	if (action < 0)
+		return action;
+
+	ret = mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION,
+				      action);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t lifecycle_state_show(struct device_driver *drv, char *buf)
+{
+	int lc_state;
+
+	lc_state = mlxbf_bootctl_smc_call1(
+					   MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
+					   MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
+
+	if (lc_state < 0)
+		return lc_state;
+
+	lc_state &=
+		(MLXBF_BOOTCTL_SB_TEST_MASK | MLXBF_BOOTCTL_SB_SECURE_MASK);
+
+	/*
+	 * 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;
+
+		return sprintf(buf, "%s(test)\n",
+			       mlxbf_bootctl_lifecycle_states[lc_state]);
+	}
+
+	return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
+}
+
+static ssize_t secure_boot_fuse_state_show(struct device_driver *drv, char *buf)
+{
+	int sb_key_state = mlxbf_bootctl_smc_call1(
+				MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
+				MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
+	int upper_key_used = 0;
+	int buf_len = 0;
+	const char *status;
+	int key;
+
+	if (sb_key_state < 0)
+		return sb_key_state;
+
+	for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
+		int burnt = sb_key_state & (1 << key);
+		int valid = sb_key_state &
+			      (1 << (key + MLXBF_SB_KEY_NUM));
+
+		buf_len += sprintf(buf + buf_len, "%d:", key);
+		if (upper_key_used) {
+			if (burnt)
+				status = valid ? "Used" : "Wasted";
+			else
+				status = valid ? "Invalid" : "Skipped";
+		} else {
+			if (burnt) {
+				status = valid ? "In use" : "Burn incomplete";
+				if (valid)
+					upper_key_used = 1;
+			} else
+				status = valid ? "Invalid" : "Free";
+		}
+		buf_len += sprintf(buf + buf_len, status);
+		if (key != 0)
+			buf_len += sprintf(buf + buf_len, "; ");
+	}
+	buf_len += sprintf(buf + buf_len, "\n");
+
+	return buf_len;
+}
+
+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 struct attribute *mlxbf_bootctl_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,
+	NULL
+};
+
+static struct attribute_group mlxbf_bootctl_attr_group = {
+	.attrs = mlxbf_bootctl_dev_attrs
+};
+
+static const struct attribute_group *mlxbf_bootctl_attr_groups[] = {
+	&mlxbf_bootctl_attr_group,
+	NULL
+};
+
+static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
+	{"MLNXBF04", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
+
+static bool mlxbf_bootctl_guid_match(const guid_t *guid,
+				     const struct arm_smccc_res *res)
+{
+	guid_t id = GUID_INIT(res->a0, res->a1 & 0xFFFF,
+			      (res->a1 >> 16) & 0xFFFF,
+			      res->a2 & 0xff, (res->a2 >> 8) & 0xFF,
+			      (res->a2 >> 16) & 0xFF, (res->a2 >> 24) & 0xFF,
+			      res->a3 & 0xff, (res->a3 >> 8) & 0xFF,
+			      (res->a3 >> 16) & 0xFF, (res->a3 >> 24) & 0xFF);
+
+	return guid_equal(guid, &id);
+}
+
+static int mlxbf_bootctl_probe(struct platform_device *pdev)
+{
+	struct arm_smccc_res res;
+	guid_t guid;
+
+	/* 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))
+		return -ENODEV;
+
+	/*
+	 * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_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.
+	 */
+	if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION,
+				    MLXBF_BOOTCTL_EMMC) < 0)
+		dev_err(&pdev->dev, "Unable to reset the EMMC boot mode\n");
+
+	return 0;
+}
+
+static struct platform_driver mlxbf_bootctl_driver = {
+	.probe = mlxbf_bootctl_probe,
+	.driver = {
+		.name = MLXBF_BOOTCTL_DRIVER_NAME,
+		.groups = mlxbf_bootctl_attr_groups,
+		.acpi_match_table = ACPI_PTR(mlxbf_bootctl_acpi_ids),
+	}
+};
+
+module_platform_driver(mlxbf_bootctl_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
+MODULE_AUTHOR("Mellanox Technologies");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.h b/drivers/platform/mellanox/mlxbf-bootctl.h
new file mode 100644
index 0000000..5ca19db
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf-bootctl.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019, Mellanox Technologies. All rights reserved.
+ */
+
+#ifndef __MLXBF_BOOTCTL_H__
+#define __MLXBF_BOOTCTL_H__
+
+/*
+ * Request that the on-chip watchdog be enabled, or disabled, after
+ * the next chip soft reset. This call does not affect the current
+ * status of the on-chip watchdog. If non-zero, the argument
+ * specifies the watchdog interval in seconds. If zero, the watchdog
+ * 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
+
+/*
+ * 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.
+ */
+#define MLXBF_BOOTCTL_GET_POST_RESET_WDOG	0x82000001
+
+/*
+ * Request that a specific boot action be taken at the next soft
+ * reset. By default, the boot action is set by external chip pins,
+ * which are sampled on hard reset. Note that the boot action
+ * requested by this call will persist on subsequent resets unless
+ * this service, or the MLNX_SET_SECOND_RESET_ACTION service, is
+ * 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
+
+/*
+ * 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).
+ */
+#define MLXBF_BOOTCTL_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
+ * 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
+ * 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.
+ * 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
+
+/*
+ * 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.
+ */
+#define MLXBF_BOOTCTL_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
+
+/*
+ * Reset eMMC by programming the RST_N register.
+ */
+#define MLXBF_BOOTCTL_SET_EMMC_RST_N		0x82000007
+
+#define MLXBF_BOOTCTL_GET_DIMM_INFO		0x82000008
+
+/* 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
+
+/* Number of svc calls defined. */
+#define MLXBF_BOOTCTL_NUM_SVC_CALLS 12
+
+/* 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 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 */
+
+/* Additional value to disable the MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION. */
+#define MLXBF_BOOTCTL_NONE	0x7fffffff /* Don't change next boot action */
+
+/* Invalid mode */
+#define MLXBF_BOOTCTL_INVALID	0xffffffff
+
+#endif /* __MLXBF_BOOTCTL_H__ */
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* RE: [PATCH v2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-01-31 17:18     ` Liming Sun
@ 2019-01-31 19:20       ` Liming Sun
  0 siblings, 0 replies; 34+ messages in thread
From: Liming Sun @ 2019-01-31 19:20 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	Platform Driver, Linux Kernel Mailing List

v3 has been posted with Changelog added.

Thanks!
Liming

> -----Original Message-----
> From: Liming Sun
> Sent: Thursday, January 31, 2019 12:18 PM
> To: 'Andy Shevchenko' <andy.shevchenko@gmail.com>
> Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>; David
> Woods <dwoods@mellanox.com>; Platform Driver <platform-driver-x86@vger.kernel.org>; Linux Kernel Mailing List <linux-
> kernel@vger.kernel.org>
> Subject: RE: [PATCH v2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
> 
> Please see my response inline. Will provide v3 once I solve the comments.
> 
> Thanks,
> Liming
> 
> > -----Original Message-----
> > From: Andy Shevchenko <andy.shevchenko@gmail.com>
> > Sent: Thursday, January 31, 2019 12:02 PM
> > To: Liming Sun <lsun@mellanox.com>
> > Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>;
> David
> > Woods <dwoods@mellanox.com>; Platform Driver <platform-driver-x86@vger.kernel.org>; Linux Kernel Mailing List <linux-
> > kernel@vger.kernel.org>
> > Subject: Re: [PATCH v2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
> >
> > On Thu, Jan 31, 2019 at 6:53 PM Liming Sun <lsun@mellanox.com> wrote:
> > >
> > > This commit adds the bootctl platform driver for Mellanox BlueField
> > > Soc, which controls the eMMC boot partition swapping and sends SMC
> > > calls to ATF running at exception level EL3 to program some system
> > > register. This register is only accessible in secure code and is
> > > used to enable the watchdog after reboot.
> > >
> > > Below are the sequences of a typical use case.
> > >
> > >   1. User-space tool upgrades one eMMC boot partition and requests
> > >      the boot partition swapping;
> > >
> > >   2. The bootctl driver handles such request and sends SMC call
> > >      to ATF. ATF programs register BREADCRUMB0 which has value
> > >      preserved during software reset. It also programs eMMC to
> > >      swap the boot partition;
> > >
> > >   3. After software reset (rebooting), ATF BL1 (BootRom) checks
> > >      register BREADCRUMB0 to enable watchdog if configured;
> > >
> > >   4. If booting fails, the watchdog timer will trigger rebooting.
> > >      In such case, ATF BootRom will switch the boot partition
> > >      to the previous one.
> >
> > Thanks for an update. My comments below.
> >
> >
> > > Reviewed-by: David Woods <dwoods@mellanox.com>
> >
> > I'm not sure I see this guy to review v2. Of course if you consider
> > all changes minor, you may leave this tag.
> 
> He sits besides me for internal review. I'll try to ask him to send
> comments to the mailing list.
> 
> >
> > > Signed-off-by: Liming Sun <lsun@mellanox.com>
> > > ---
> >
> > Here should be a changelog what had been done in new version.
> 
> Will provide changelog in the v3 email.
> Or please correct me if you meant adding changelog in the code or
> cover letter.
> 
> > > +/* UUID used to probe ATF service. */
> >
> > > +static const char * const mlxbf_bootctl_svc_uuid_str =
> > > +       "89c036b4-e7d7-11e6-8797-001aca00bfc4";
> >
> > static const char *xxx = ...;
> 
> Will update it in v3.
> 
> >
> > > +/*
> > > + * UUID structure used to match the returned value from ATF in
> > > + * four 32-bit words. No need to do endian conversion here.
> > > + */
> > > +union mlxbf_bootctl_uuid {
> > > +       guid_t guid;
> > > +       u32 words[4];
> > > +};
> >
> > I'm not sure it's the best you can do. instead of using union, better
> > to use conversion from and to corresponding uuid type.
> 
> I'll try to figure out another way without the union to compare the
> uuid directly using uuid api. Will update it in v3.
> 
> >
> > The rest I will comment on v3.
> >
> > --
> > With Best Regards,
> > Andy Shevchenko

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-01-29 20:59 [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc Liming Sun
                   ` (2 preceding siblings ...)
  2019-01-31 19:19 ` [PATCH v3] " Liming Sun
@ 2019-02-01  5:16 ` kbuild test robot
  2019-02-01 20:48   ` Liming Sun
  2019-02-01 20:46 ` [PATCH v4] " Liming Sun
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 34+ messages in thread
From: kbuild test robot @ 2019-02-01  5:16 UTC (permalink / raw)
  To: Liming Sun
  Cc: kbuild-all, Andy Shevchenko, Darren Hart, Vadim Pasternak,
	David Woods, Liming Sun, platform-driver-x86, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 4089 bytes --]

Hi Liming,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v5.0-rc4 next-20190131]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Liming-Sun/platform-mellanox-Add-bootctl-driver-for-Mellanox-BlueField-Soc/20190201-104657
config: arm64-allmodconfig (attached as .config)
compiler: aarch64-linux-gnu-gcc (Debian 8.2.0-11) 8.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=8.2.0 make.cross ARCH=arm64 

All errors (new ones prefixed by >>):

   drivers/platform/mellanox/mlxbf-bootctl.c:260:36: error: array type has incomplete element type 'struct acpi_device_id'
    static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
                                       ^~~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/module.h:18,
                    from drivers/platform/mellanox/mlxbf-bootctl.c:12:
>> drivers/platform/mellanox/mlxbf-bootctl.c:314:20: error: expected ',' or ';' before 'DRIVER_DESCRIPTION'
    MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
                       ^~~~~~~~~~~~~~~~~~
   include/linux/moduleparam.h:24:26: note: in definition of macro '__MODULE_INFO'
      = __stringify(tag) "=" info
                             ^~~~
   include/linux/module.h:208:42: note: in expansion of macro 'MODULE_INFO'
    #define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description)
                                             ^~~~~~~~~~~
   drivers/platform/mellanox/mlxbf-bootctl.c:314:1: note: in expansion of macro 'MODULE_DESCRIPTION'
    MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
    ^~~~~~~~~~~~~~~~~~

vim +314 drivers/platform/mellanox/mlxbf-bootctl.c

   259	
 > 260	static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
   261		{"MLNXBF04", 0},
   262		{},
   263	};
   264	
   265	MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
   266	
   267	static int mlxbf_bootctl_probe(struct platform_device *pdev)
   268	{
   269		struct arm_smccc_res res;
   270	
   271		/*
   272		 * Ensure we have the UUID we expect for this service.
   273		 * Note that the functionality we want is present in the first
   274		 * released version of this service, so we don't check the version.
   275		 */
   276		arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res);
   277		if (res.a0 != 0x89c036b4 || res.a1 != 0x11e6e7d7 ||
   278		    res.a2 != 0x1a009787 || res.a3 != 0xc4bf00ca)
   279			return -ENODEV;
   280	
   281		/*
   282		 * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_SWAP_EMMC
   283		 * in case of boot failures. However it doesn't clear the state if there
   284		 * is no failure. Restore the default boot mode here to avoid any
   285		 * unnecessary boot partition swapping.
   286		 */
   287		if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION,
   288					    MLXBF_BOOTCTL_EMMC) < 0)
   289			pr_err("Unable to reset the EMMC boot mode\n");
   290	
   291		pr_info("%s (version %s)\n", MLXBF_BOOTCTL_DRIVER_DESCRIPTION,
   292			MLXBF_BOOTCTL_DRIVER_VERSION);
   293	
   294		return 0;
   295	}
   296	
   297	static int mlxbf_bootctl_remove(struct platform_device *pdev)
   298	{
   299		return 0;
   300	}
   301	
   302	static struct platform_driver mlxbf_bootctl_driver = {
   303		.probe = mlxbf_bootctl_probe,
   304		.remove = mlxbf_bootctl_remove,
   305		.driver = {
   306			.name = MLXBF_BOOTCTL_DRIVER_NAME,
   307			.groups = mlxbf_bootctl_attr_groups,
   308			.acpi_match_table = ACPI_PTR(mlxbf_bootctl_acpi_ids),
   309		}
   310	};
   311	
   312	module_platform_driver(mlxbf_bootctl_driver);
   313	
 > 314	MODULE_DESCRIPTION(DRIVER_DESCRIPTION);

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 63186 bytes --]

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH v4] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-01-29 20:59 [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc Liming Sun
                   ` (3 preceding siblings ...)
  2019-02-01  5:16 ` [PATCH v1 1/1] " kbuild test robot
@ 2019-02-01 20:46 ` Liming Sun
  2019-02-05 17:21   ` Andy Shevchenko
  2019-05-17 17:49 ` [PATCH v5 1/2] " Liming Sun
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 34+ messages in thread
From: Liming Sun @ 2019-02-01 20:46 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods
  Cc: Liming Sun, platform-driver-x86, linux-kernel

This commit adds the bootctl platform driver for Mellanox BlueField
Soc, which controls the eMMC boot partition swapping and sends SMC
calls to ATF running at exception level EL3 to program some system
register. This register is only accessible in secure code and is
used to enable the watchdog after reboot.

Below are the sequences of a typical use case.

  1. User-space tool upgrades one eMMC boot partition and requests
     the boot partition swapping;

  2. The bootctl driver handles such request and sends SMC call
     to ATF. ATF programs register BREADCRUMB0 which has value
     preserved during software reset. It also programs eMMC to
     swap the boot partition;

  3. After software reset (rebooting), ATF BL1 (BootRom) checks
     register BREADCRUMB0 to enable watchdog if configured;

  4. If booting fails, the watchdog timer will trigger rebooting.
     In such case, ATF BootRom will switch the boot partition
     to the previous one.

Signed-off-by: Liming Sun <lsun@mellanox.com>

---

v4: Update Kconfig for the dependency on ACPI.
    Fixed a typo which caused build error for kernel module.
v3: More coding style fixes, revised the uuid matching code.
v2: Fix the Kconfig and coding styles, propagate errors to caller.
v1: Initial version.
---
 drivers/platform/mellanox/Kconfig         |  14 +-
 drivers/platform/mellanox/Makefile        |   1 +
 drivers/platform/mellanox/mlxbf-bootctl.c | 307 ++++++++++++++++++++++++++++++
 drivers/platform/mellanox/mlxbf-bootctl.h | 108 +++++++++++
 4 files changed, 429 insertions(+), 1 deletion(-)
 create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.c
 create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.h

diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig
index cd8a908..92bda11 100644
--- a/drivers/platform/mellanox/Kconfig
+++ b/drivers/platform/mellanox/Kconfig
@@ -5,7 +5,7 @@
 
 menuconfig MELLANOX_PLATFORM
 	bool "Platform support for Mellanox hardware"
-	depends on X86 || ARM || COMPILE_TEST
+	depends on X86 || ARM || ARM64 || COMPILE_TEST
 	---help---
 	  Say Y here to get to see options for platform support for
 	  Mellanox systems. This option alone does not add any kernel code.
@@ -34,4 +34,16 @@ config MLXREG_IO
 	  to system resets operation, system reset causes monitoring and some
 	  kinds of mux selection.
 
+config MLXBF_BOOTCTL
+	tristate "Mellanox BlueField Firmware Boot Control driver"
+	depends on ARM64 && ACPI
+	help
+          The Mellanox BlueField firmware implements functionality to
+          request swapping the primary and alternate eMMC boot
+          partition, and to set up a watchdog that can undo that swap
+          if the system does not boot up correctly.  This driver
+          provides sysfs access to the firmware, to be used in
+          conjunction with the eMMC device driver to do any necessary
+          initial swap of the boot partition.
+
 endif # MELLANOX_PLATFORM
diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile
index 57074d9c..76b0370 100644
--- a/drivers/platform/mellanox/Makefile
+++ b/drivers/platform/mellanox/Makefile
@@ -5,3 +5,4 @@
 #
 obj-$(CONFIG_MLXREG_HOTPLUG)	+= mlxreg-hotplug.o
 obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o
+obj-$(CONFIG_MLXBF_BOOTCTL)	+= mlxbf-bootctl.o
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c
new file mode 100644
index 0000000..01298ce
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf-bootctl.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Mellanox boot control driver
+ * This driver provides a sysfs interface for systems management
+ * software to manage reset-time actions.
+ *
+ * Copyright (C) 2019 Mellanox Technologies
+ */
+
+#include <linux/acpi.h>
+#include <linux/arm-smccc.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 MLXBF_SB_KEY_NUM			4
+
+/* UUID used to probe ATF service. */
+static const char * const mlxbf_bootctl_svc_uuid_str =
+	"89c036b4-e7d7-11e6-8797-001aca00bfc4";
+
+struct mlxbf_bootctl_name {
+	u32 value;
+	const char *name;
+};
+
+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 const char * const mlxbf_bootctl_lifecycle_states[] = {
+	[0] = "soft_non_secure",
+	[1] = "secure",
+	[2] = "hard_non_secure",
+	[3] = "rma",
+};
+
+/* The SMC calls in question are atomic, so we don't have to lock here. */
+static int mlxbf_bootctl_smc_call1(unsigned int smc_op, int smc_arg)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res);
+
+	return res.a0;
+}
+
+/* Syntactic sugar to avoid having to specify an unused argument. */
+#define mlxbf_bootctl_smc_call0(smc_op) mlxbf_bootctl_smc_call1(smc_op, 0)
+
+static int mlxbf_bootctl_reset_action_to_val(const char *action)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
+		if (sysfs_streq(boot_names[i].name, action))
+			return boot_names[i].value;
+
+	return MLXBF_BOOTCTL_INVALID;
+}
+
+static const char *mlxbf_bootctl_reset_action_to_string(int action)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
+		if (boot_names[i].value == action)
+			return boot_names[i].name;
+
+	return "";
+}
+
+static ssize_t post_reset_wdog_show(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "%d\n",
+		mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_POST_RESET_WDOG));
+}
+
+static ssize_t post_reset_wdog_store(struct device_driver *drv,
+				     const char *buf, size_t count)
+{
+	unsigned long watchdog;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &watchdog);
+	if (ret)
+		return ret;
+
+	ret = mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_POST_RESET_WDOG,
+				      watchdog);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t reset_action_show(struct device_driver *drv, char *buf)
+{
+	int action;
+
+	action = mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_RESET_ACTION);
+
+	return sprintf(buf, "%s\n",
+		       mlxbf_bootctl_reset_action_to_string(action));
+}
+
+static ssize_t reset_action_store(struct device_driver *drv,
+				  const char *buf, size_t count)
+{
+	int ret, action = mlxbf_bootctl_reset_action_to_val(buf);
+
+	if (action == MLXBF_BOOTCTL_INVALID || action == MLXBF_BOOTCTL_NONE)
+		return -EINVAL;
+
+	ret = (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION, action));
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t second_reset_action_show(struct device_driver *drv, char *buf)
+{
+	int action;
+	const char *name;
+
+	action = mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION);
+	name = mlxbf_bootctl_reset_action_to_string(action);
+
+	return sprintf(buf, "%s\n", name);
+}
+
+static ssize_t second_reset_action_store(struct device_driver *drv,
+					 const char *buf, size_t count)
+{
+	int ret, action = mlxbf_bootctl_reset_action_to_val(buf);
+
+	if (action < 0)
+		return action;
+
+	ret = mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION,
+				      action);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t lifecycle_state_show(struct device_driver *drv, char *buf)
+{
+	int lc_state;
+
+	lc_state = mlxbf_bootctl_smc_call1(
+					   MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
+					   MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
+
+	if (lc_state < 0)
+		return lc_state;
+
+	lc_state &=
+		(MLXBF_BOOTCTL_SB_TEST_MASK | MLXBF_BOOTCTL_SB_SECURE_MASK);
+
+	/*
+	 * 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;
+
+		return sprintf(buf, "%s(test)\n",
+			       mlxbf_bootctl_lifecycle_states[lc_state]);
+	}
+
+	return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
+}
+
+static ssize_t secure_boot_fuse_state_show(struct device_driver *drv, char *buf)
+{
+	int sb_key_state = mlxbf_bootctl_smc_call1(
+				MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
+				MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
+	int upper_key_used = 0;
+	int buf_len = 0;
+	const char *status;
+	int key;
+
+	if (sb_key_state < 0)
+		return sb_key_state;
+
+	for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
+		int burnt = sb_key_state & (1 << key);
+		int valid = sb_key_state &
+			      (1 << (key + MLXBF_SB_KEY_NUM));
+
+		buf_len += sprintf(buf + buf_len, "%d:", key);
+		if (upper_key_used) {
+			if (burnt)
+				status = valid ? "Used" : "Wasted";
+			else
+				status = valid ? "Invalid" : "Skipped";
+		} else {
+			if (burnt) {
+				status = valid ? "In use" : "Burn incomplete";
+				if (valid)
+					upper_key_used = 1;
+			} else
+				status = valid ? "Invalid" : "Free";
+		}
+		buf_len += sprintf(buf + buf_len, status);
+		if (key != 0)
+			buf_len += sprintf(buf + buf_len, "; ");
+	}
+	buf_len += sprintf(buf + buf_len, "\n");
+
+	return buf_len;
+}
+
+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 struct attribute *mlxbf_bootctl_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,
+	NULL
+};
+
+static struct attribute_group mlxbf_bootctl_attr_group = {
+	.attrs = mlxbf_bootctl_dev_attrs
+};
+
+static const struct attribute_group *mlxbf_bootctl_attr_groups[] = {
+	&mlxbf_bootctl_attr_group,
+	NULL
+};
+
+static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
+	{"MLNXBF04", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
+
+static bool mlxbf_bootctl_guid_match(const guid_t *guid,
+				     const struct arm_smccc_res *res)
+{
+	guid_t id = GUID_INIT(res->a0, res->a1 & 0xFFFF,
+			      (res->a1 >> 16) & 0xFFFF,
+			      res->a2 & 0xff, (res->a2 >> 8) & 0xFF,
+			      (res->a2 >> 16) & 0xFF, (res->a2 >> 24) & 0xFF,
+			      res->a3 & 0xff, (res->a3 >> 8) & 0xFF,
+			      (res->a3 >> 16) & 0xFF, (res->a3 >> 24) & 0xFF);
+
+	return guid_equal(guid, &id);
+}
+
+static int mlxbf_bootctl_probe(struct platform_device *pdev)
+{
+	struct arm_smccc_res res;
+	guid_t guid;
+
+	/* 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))
+		return -ENODEV;
+
+	/*
+	 * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_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.
+	 */
+	if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION,
+				    MLXBF_BOOTCTL_EMMC) < 0)
+		dev_err(&pdev->dev, "Unable to reset the EMMC boot mode\n");
+
+	return 0;
+}
+
+static struct platform_driver mlxbf_bootctl_driver = {
+	.probe = mlxbf_bootctl_probe,
+	.driver = {
+		.name = "mlxbf-bootctl",
+		.groups = mlxbf_bootctl_attr_groups,
+		.acpi_match_table = ACPI_PTR(mlxbf_bootctl_acpi_ids),
+	}
+};
+
+module_platform_driver(mlxbf_bootctl_driver);
+
+MODULE_DESCRIPTION("Mellanox boot control driver");
+MODULE_AUTHOR("Mellanox Technologies");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.h b/drivers/platform/mellanox/mlxbf-bootctl.h
new file mode 100644
index 0000000..5ca19db
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf-bootctl.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019, Mellanox Technologies. All rights reserved.
+ */
+
+#ifndef __MLXBF_BOOTCTL_H__
+#define __MLXBF_BOOTCTL_H__
+
+/*
+ * Request that the on-chip watchdog be enabled, or disabled, after
+ * the next chip soft reset. This call does not affect the current
+ * status of the on-chip watchdog. If non-zero, the argument
+ * specifies the watchdog interval in seconds. If zero, the watchdog
+ * 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
+
+/*
+ * 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.
+ */
+#define MLXBF_BOOTCTL_GET_POST_RESET_WDOG	0x82000001
+
+/*
+ * Request that a specific boot action be taken at the next soft
+ * reset. By default, the boot action is set by external chip pins,
+ * which are sampled on hard reset. Note that the boot action
+ * requested by this call will persist on subsequent resets unless
+ * this service, or the MLNX_SET_SECOND_RESET_ACTION service, is
+ * 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
+
+/*
+ * 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).
+ */
+#define MLXBF_BOOTCTL_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
+ * 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
+ * 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.
+ * 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
+
+/*
+ * 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.
+ */
+#define MLXBF_BOOTCTL_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
+
+/*
+ * Reset eMMC by programming the RST_N register.
+ */
+#define MLXBF_BOOTCTL_SET_EMMC_RST_N		0x82000007
+
+#define MLXBF_BOOTCTL_GET_DIMM_INFO		0x82000008
+
+/* 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
+
+/* Number of svc calls defined. */
+#define MLXBF_BOOTCTL_NUM_SVC_CALLS 12
+
+/* 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 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 */
+
+/* Additional value to disable the MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION. */
+#define MLXBF_BOOTCTL_NONE	0x7fffffff /* Don't change next boot action */
+
+/* Invalid mode */
+#define MLXBF_BOOTCTL_INVALID	0xffffffff
+
+#endif /* __MLXBF_BOOTCTL_H__ */
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* RE: [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-02-01  5:16 ` [PATCH v1 1/1] " kbuild test robot
@ 2019-02-01 20:48   ` Liming Sun
  0 siblings, 0 replies; 34+ messages in thread
From: Liming Sun @ 2019-02-01 20:48 UTC (permalink / raw)
  To: kbuild test robot
  Cc: kbuild-all, Andy Shevchenko, Darren Hart, Vadim Pasternak,
	David Woods, platform-driver-x86, linux-kernel

Thanks for the information! 

Patch v4 1/1 has been posted to solve these issues.

Best regards,
Liming

> -----Original Message-----
> From: kbuild test robot <lkp@intel.com>
> Sent: Friday, February 1, 2019 12:17 AM
> To: Liming Sun <lsun@mellanox.com>
> Cc: kbuild-all@01.org; Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak
> <vadimp@mellanox.com>; David Woods <dwoods@mellanox.com>; Liming Sun <lsun@mellanox.com>; platform-driver-
> x86@vger.kernel.org; linux-kernel@vger.kernel.org
> Subject: Re: [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
> 
> Hi Liming,
> 
> Thank you for the patch! Yet something to improve:
> 
> [auto build test ERROR on linus/master]
> [also build test ERROR on v5.0-rc4 next-20190131]
> [if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
> 
> url:    https://github.com/0day-ci/linux/commits/Liming-Sun/platform-mellanox-Add-bootctl-driver-for-Mellanox-BlueField-
> Soc/20190201-104657
> config: arm64-allmodconfig (attached as .config)
> compiler: aarch64-linux-gnu-gcc (Debian 8.2.0-11) 8.2.0
> reproduce:
>         wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
>         chmod +x ~/bin/make.cross
>         # save the attached .config to linux build tree
>         GCC_VERSION=8.2.0 make.cross ARCH=arm64
> 
> All errors (new ones prefixed by >>):
> 
>    drivers/platform/mellanox/mlxbf-bootctl.c:260:36: error: array type has incomplete element type 'struct acpi_device_id'
>     static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
>                                        ^~~~~~~~~~~~~~~~~~~~~~
>    In file included from include/linux/module.h:18,
>                     from drivers/platform/mellanox/mlxbf-bootctl.c:12:
> >> drivers/platform/mellanox/mlxbf-bootctl.c:314:20: error: expected ',' or ';' before 'DRIVER_DESCRIPTION'
>     MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
>                        ^~~~~~~~~~~~~~~~~~
>    include/linux/moduleparam.h:24:26: note: in definition of macro '__MODULE_INFO'
>       = __stringify(tag) "=" info
>                              ^~~~
>    include/linux/module.h:208:42: note: in expansion of macro 'MODULE_INFO'
>     #define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description)
>                                              ^~~~~~~~~~~
>    drivers/platform/mellanox/mlxbf-bootctl.c:314:1: note: in expansion of macro 'MODULE_DESCRIPTION'
>     MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
>     ^~~~~~~~~~~~~~~~~~
> 
> vim +314 drivers/platform/mellanox/mlxbf-bootctl.c
> 
>    259
>  > 260	static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
>    261		{"MLNXBF04", 0},
>    262		{},
>    263	};
>    264
>    265	MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
>    266
>    267	static int mlxbf_bootctl_probe(struct platform_device *pdev)
>    268	{
>    269		struct arm_smccc_res res;
>    270
>    271		/*
>    272		 * Ensure we have the UUID we expect for this service.
>    273		 * Note that the functionality we want is present in the first
>    274		 * released version of this service, so we don't check the version.
>    275		 */
>    276		arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res);
>    277		if (res.a0 != 0x89c036b4 || res.a1 != 0x11e6e7d7 ||
>    278		    res.a2 != 0x1a009787 || res.a3 != 0xc4bf00ca)
>    279			return -ENODEV;
>    280
>    281		/*
>    282		 * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_SWAP_EMMC
>    283		 * in case of boot failures. However it doesn't clear the state if there
>    284		 * is no failure. Restore the default boot mode here to avoid any
>    285		 * unnecessary boot partition swapping.
>    286		 */
>    287		if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION,
>    288					    MLXBF_BOOTCTL_EMMC) < 0)
>    289			pr_err("Unable to reset the EMMC boot mode\n");
>    290
>    291		pr_info("%s (version %s)\n", MLXBF_BOOTCTL_DRIVER_DESCRIPTION,
>    292			MLXBF_BOOTCTL_DRIVER_VERSION);
>    293
>    294		return 0;
>    295	}
>    296
>    297	static int mlxbf_bootctl_remove(struct platform_device *pdev)
>    298	{
>    299		return 0;
>    300	}
>    301
>    302	static struct platform_driver mlxbf_bootctl_driver = {
>    303		.probe = mlxbf_bootctl_probe,
>    304		.remove = mlxbf_bootctl_remove,
>    305		.driver = {
>    306			.name = MLXBF_BOOTCTL_DRIVER_NAME,
>    307			.groups = mlxbf_bootctl_attr_groups,
>    308			.acpi_match_table = ACPI_PTR(mlxbf_bootctl_acpi_ids),
>    309		}
>    310	};
>    311
>    312	module_platform_driver(mlxbf_bootctl_driver);
>    313
>  > 314	MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
> 
> ---
> 0-DAY kernel test infrastructure                Open Source Technology Center
> https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v4] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-02-01 20:46 ` [PATCH v4] " Liming Sun
@ 2019-02-05 17:21   ` Andy Shevchenko
  2019-05-17 17:49     ` Liming Sun
  0 siblings, 1 reply; 34+ messages in thread
From: Andy Shevchenko @ 2019-02-05 17:21 UTC (permalink / raw)
  To: Liming Sun
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	Platform Driver, Linux Kernel Mailing List

On Fri, Feb 1, 2019 at 10:47 PM Liming Sun <lsun@mellanox.com> wrote:

Thanks for an update, my comments below.

(To Mellanox kernel developer team: guys, perhaps you need to
establish a few rounds of internal review before sending the stuff
outside)

First of all, I didn't find ABI documentation for interface this patch
introduces.
Must be added.

> +/* UUID used to probe ATF service. */
> +static const char * const mlxbf_bootctl_svc_uuid_str =
> +       "89c036b4-e7d7-11e6-8797-001aca00bfc4";

static const char *..._str = ...;

> +/* Syntactic sugar to avoid having to specify an unused argument. */
> +#define mlxbf_bootctl_smc_call0(smc_op) mlxbf_bootctl_smc_call1(smc_op, 0)

No.
Please, do it explicitly.

> +static const char *mlxbf_bootctl_reset_action_to_string(int action)
> +{
> +       int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(boot_names); i++)
> +               if (boot_names[i].value == action)
> +                       return boot_names[i].name;
> +

> +       return "";

Hmm...
Shouldn't be more descriptive?

> +}

> +static ssize_t post_reset_wdog_show(struct device_driver *drv, char *buf)
> +{
> +       return sprintf(buf, "%d\n",
> +               mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_POST_RESET_WDOG));

What if call return negative error?

> +}

> +static ssize_t reset_action_show(struct device_driver *drv, char *buf)
> +{
> +       int action;
> +
> +       action = mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_RESET_ACTION);

What if action goes negative?

> +       return sprintf(buf, "%s\n",
> +                      mlxbf_bootctl_reset_action_to_string(action));

Wouldn't be one line?

> +}

> +static ssize_t reset_action_store(struct device_driver *drv,
> +                                 const char *buf, size_t count)
> +{

> +       int ret, action = mlxbf_bootctl_reset_action_to_val(buf);

This should be like
int action;
int ret;

action = ...;
if (action ...)


> +       if (action == MLXBF_BOOTCTL_INVALID || action == MLXBF_BOOTCTL_NONE)
> +               return -EINVAL;

The mlxbf_bootctl_reset_action_to_val() has to return a proper Linux error code.
After this code should be modified to something like

if (action < 0)
 return action;

> +
> +       ret = (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION, action));

Redundant parens.

> +       if (ret < 0)
> +               return ret;
> +
> +       return count;
> +}
> +
> +static ssize_t second_reset_action_show(struct device_driver *drv, char *buf)
> +{
> +       int action;
> +       const char *name;
> +
> +       action = mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION);

What if action is negative?

> +       name = mlxbf_bootctl_reset_action_to_string(action);
> +
> +       return sprintf(buf, "%s\n", name);

return sprintf(... _to_string(...));
?

> +}
> +
> +static ssize_t second_reset_action_store(struct device_driver *drv,
> +                                        const char *buf, size_t count)
> +{

> +       int ret, action = mlxbf_bootctl_reset_action_to_val(buf);

int action;
int ret;

action = ...
if (action ...)

> +
> +       if (action < 0)
> +               return action;
> +
> +       ret = mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION,
> +                                     action);
> +       if (ret < 0)
> +               return ret;
> +
> +       return count;
> +}
> +
> +static ssize_t lifecycle_state_show(struct device_driver *drv, char *buf)
> +{
> +       int lc_state;
> +
> +       lc_state = mlxbf_bootctl_smc_call1(
> +                                          MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> +                                          MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);

> +

Redundant blank line.

> +       if (lc_state < 0)
> +               return lc_state;
> +
> +       lc_state &=
> +               (MLXBF_BOOTCTL_SB_TEST_MASK | MLXBF_BOOTCTL_SB_SECURE_MASK);

Actually parens are not needed. Sorry, I forgot to point to this earlier.

> +
> +       /*
> +        * 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;
> +
> +               return sprintf(buf, "%s(test)\n",
> +                              mlxbf_bootctl_lifecycle_states[lc_state]);
> +       }
> +
> +       return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
> +}
> +
> +static ssize_t secure_boot_fuse_state_show(struct device_driver *drv, char *buf)
> +{
> +       int sb_key_state = mlxbf_bootctl_smc_call1(
> +                               MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> +                               MLXBF_BOOTCTL_FUSE_STATUS_KEYS);

Split it to declaration and assignment.

> +       int upper_key_used = 0;
> +       int buf_len = 0;
> +       const char *status;
> +       int key;
> +
> +       if (sb_key_state < 0)
> +               return sb_key_state;
> +
> +       for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {

> +               int burnt = sb_key_state & (1 << key);

BIT() ?

> +               int valid = sb_key_state &
> +                             (1 << (key + MLXBF_SB_KEY_NUM));

Ditto. And put to one line?

> +               buf_len += sprintf(buf + buf_len, "%d:", key);

Why this can't be part of the below sprintf() call?

> +               if (upper_key_used) {
> +                       if (burnt)
> +                               status = valid ? "Used" : "Wasted";
> +                       else
> +                               status = valid ? "Invalid" : "Skipped";
> +               } else {
> +                       if (burnt) {
> +                               status = valid ? "In use" : "Burn incomplete";

> +                               if (valid)
> +                                       upper_key_used = 1;

Move this out of this if-else-if block. The rationale is to split two
logical parts:
1) message choice
2) flag flip

> +                       } else
> +                               status = valid ? "Invalid" : "Free";
> +               }
> +               buf_len += sprintf(buf + buf_len, status);

> +               if (key != 0)
> +                       buf_len += sprintf(buf + buf_len, "; ");

Why do you need to check this?

> +       }
> +       buf_len += sprintf(buf + buf_len, "\n");
> +
> +       return buf_len;
> +}

> +static struct attribute_group mlxbf_bootctl_attr_group = {
> +       .attrs = mlxbf_bootctl_dev_attrs

+ comma.

> +};

> +static bool mlxbf_bootctl_guid_match(const guid_t *guid,
> +                                    const struct arm_smccc_res *res)
> +{

> +       guid_t id = GUID_INIT(res->a0, res->a1 & 0xFFFF,
> +                             (res->a1 >> 16) & 0xFFFF,
> +                             res->a2 & 0xff, (res->a2 >> 8) & 0xFF,
> +                             (res->a2 >> 16) & 0xFF, (res->a2 >> 24) & 0xFF,
> +                             res->a3 & 0xff, (res->a3 >> 8) & 0xFF,
> +                             (res->a3 >> 16) & 0xFF, (res->a3 >> 24) & 0xFF);

All & 0xFF* are done inside the macro, no need to duplicate.

> +
> +       return guid_equal(guid, &id);
> +}

> +static int mlxbf_bootctl_probe(struct platform_device *pdev)
> +{
> +       struct arm_smccc_res res;
> +       guid_t guid;
> +
> +       /* 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))
> +               return -ENODEV;
> +
> +       /*
> +        * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_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.
> +        */

> +       if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION,
> +                                   MLXBF_BOOTCTL_EMMC) < 0)

Use temporary variable here.

int ret;> +

...
ret = ..._call1(...);
if (ret < 0)

> +               dev_err(&pdev->dev, "Unable to reset the EMMC boot mode\n");

If it's error, why we return 0?
Otherwise, use warn level here.

> +
> +       return 0;
> +}
> +
> +static struct platform_driver mlxbf_bootctl_driver = {
> +       .probe = mlxbf_bootctl_probe,
> +       .driver = {
> +               .name = "mlxbf-bootctl",
> +               .groups = mlxbf_bootctl_attr_groups,

> +               .acpi_match_table = ACPI_PTR(mlxbf_bootctl_acpi_ids),

ACPI_PTR is redundant for the ACPI dependent driver.

> +       }
> +};

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH v5 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-01-29 20:59 [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc Liming Sun
                   ` (4 preceding siblings ...)
  2019-02-01 20:46 ` [PATCH v4] " Liming Sun
@ 2019-05-17 17:49 ` Liming Sun
  2019-05-20 15:56   ` Greg KH
  2019-05-22 13:34   ` Mark Rutland
  2019-05-17 17:49 ` [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions Liming Sun
                   ` (2 subsequent siblings)
  8 siblings, 2 replies; 34+ messages in thread
From: Liming Sun @ 2019-05-17 17:49 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods
  Cc: Liming Sun, platform-driver-x86, linux-kernel

This commit adds the bootctl platform driver for Mellanox BlueField
Soc, which controls the eMMC boot partition swapping and sends SMC
calls to ATF running at exception level EL3 to program some system
register. This register is persistent during warm reset and is only
accessible in secure code which is used to enable the watchdog after
reboot.

Below are the sequences of typical use case.

  1. User-space tool upgrades one eMMC boot partition and requests
     the boot partition swapping;

  2. The bootctl driver handles this request and sends SMC call
     to ATF. ATF programs register BREADCRUMB0 which has value
     preserved during warm reset. It also programs eMMC to swap
     the boot partition;

  3. After software reset (rebooting), ATF BL1 (BootRom) checks
     register BREADCRUMB0 to enable watchdog if configured;

  4. If booting fails, the watchdog timer will trigger rebooting.
     In such case, ATF Boot ROM will switch the boot partition
     back to the previous one.

Reviewed-by: Vadim Pasternak <vadimp@mellanox.com>
Signed-off-by: Liming Sun <lsun@mellanox.com>
---
v4->v5:
    Fixes for comments from Andy:
    - Added ABI documentation;
    - Remove the extra 'const' in mlxbf_bootctl_svc_uuid_str definition;
    - Remove the mlxbf_bootctl_smc_call0() MACRO to use function
      call directly;
    - Return more descriptive string ('invalid action') in
      mlxbf_bootctl_reset_action_to_string();
    - Check return value of the mlxbf_bootctl_smc() in function
      post_reset_wdog_show() and reset_action_show();
    - Revise the sprintf() line in reset_action_show() into one line;
    - Move the 'action = ' assignment out of the declarations
      in reset_action_store() and check return value of
      mlxbf_bootctl_reset_action_to_val() in this function;
    - Removed Redundant parens in reset_action_store();
    - Check return code of the smc_call in second_reset_action_show(),
      merge the 'name = ' assignment into the sprintf() directly;
    - Move the 'action = ' assignment out of the declaration block
      in second_reset_action_store();
    - Remove redundant blank line and parents in lifecycle_state_show();
    - Split declaration and assignment in secure_boot_fuse_state_show();
    - use BIT() in secure_boot_fuse_state_show() and simplify code in
      this function to split the logic of message choice and flag flip;
      Also removed the 'key !=0 ' check since it's not needed;
    - Added comma in mlxbf_bootctl_attr_group definition.
    - Removed un-needed '& 0xFF' logic in mlxbf_bootctl_guid_match();
    - Use temp variable for the result of the smc call in
      mlxbf_bootctl_probe();
    - Use dev_warn() instead of dev_err() in mlxbf_bootctl_probe();
    - Removed the ACPI_PTR usage in the mlxbf_bootctl_driver definition;
    Fixes for comments from Vadim:
    - Move mlxbf-bootctl.o to the top of the Makefile;
    - Fix to use the 'ret' value instead of another mlxbf_bootctl_smc() call;
    - Fix the 'declaration inside the block' in secure_boot_fuse_state_show();
    - Use ATTRIBUTE_GROUPS() for the attribute definition;
    - Use single line for a comment in mlxbf-bootctl.h
    - Use KernelVersion 5.3 instead of 5.2.2 in the ABI doc;
    - Use common utility function for the show and store of reset_action
      and second_reset_action.
    New changes:
    - Rename mlxbf_bootctl_smc_call1() to mlxbf_bootctl_smc() to
      shorten the name so some statement could be make into one line;
    - Fixed the MODULE_LICENSE();
    - Added more comments in secure_boot_fuse_state_show();
v4: Update Kconfig for the dependency on ACPI.
    Fixed a typo which caused build error for kernel module.
v2->v3:
    Fixes for comments from Andy:
    - More coding style fixes;
    - Revised the uuid matching code.
v2: Fix the Kconfig and coding styles, propagate errors to caller.
v1: Initial version.
---
 drivers/platform/mellanox/Kconfig         |  12 ++
 drivers/platform/mellanox/Makefile        |   1 +
 drivers/platform/mellanox/mlxbf-bootctl.c | 311 ++++++++++++++++++++++++++++++
 drivers/platform/mellanox/mlxbf-bootctl.h | 103 ++++++++++
 4 files changed, 427 insertions(+)
 create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.c
 create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.h

diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig
index 530fe7e..386336d 100644
--- a/drivers/platform/mellanox/Kconfig
+++ b/drivers/platform/mellanox/Kconfig
@@ -44,4 +44,16 @@ config MLXBF_TMFIFO
           platform driver support for the TmFifo which supports console
           and networking based on the virtio framework.
 
+config MLXBF_BOOTCTL
+	tristate "Mellanox BlueField Firmware Boot Control driver"
+	depends on ARM64
+	depends on ACPI
+	help
+          The Mellanox BlueField firmware implements functionality to
+          request swapping the primary and alternate eMMC boot partition,
+          and to set up a watchdog that can undo that swap if the system
+          does not boot up correctly. This driver provides sysfs access
+          to the userspace tools, to be used in conjunction with the eMMC
+          device driver to do necessary initial swap of the boot partition.
+
 endif # MELLANOX_PLATFORM
diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile
index a229bda1..499623c 100644
--- a/drivers/platform/mellanox/Makefile
+++ b/drivers/platform/mellanox/Makefile
@@ -3,6 +3,7 @@
 # Makefile for linux/drivers/platform/mellanox
 # Mellanox Platform-Specific Drivers
 #
+obj-$(CONFIG_MLXBF_BOOTCTL)	+= mlxbf-bootctl.o
 obj-$(CONFIG_MLXBF_TMFIFO)	+= mlxbf-tmfifo.o
 obj-$(CONFIG_MLXREG_HOTPLUG)	+= mlxreg-hotplug.o
 obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c
new file mode 100644
index 0000000..842b991
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf-bootctl.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Mellanox boot control driver
+ *
+ * This driver provides a sysfs interface for systems management
+ * software to manage reset-time actions.
+ *
+ * Copyright (C) 2019 Mellanox Technologies
+ */
+
+#include <linux/acpi.h>
+#include <linux/arm-smccc.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 MLXBF_SB_KEY_NUM			4
+
+/* UUID used to probe ATF service. */
+static const char *mlxbf_bootctl_svc_uuid_str =
+	"89c036b4-e7d7-11e6-8797-001aca00bfc4";
+
+struct mlxbf_bootctl_name {
+	u32 value;
+	const char *name;
+};
+
+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 const char * const mlxbf_bootctl_lifecycle_states[] = {
+	[0] = "Production",
+	[1] = "GA Secured",
+	[2] = "GA Non-Secured",
+	[3] = "RMA",
+};
+
+/* ARM SMC call which is atomic and no need for lock. */
+static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res);
+
+	return res.a0;
+}
+
+/* Return the action in integer or an error code. */
+static int mlxbf_bootctl_reset_action_to_val(const char *action)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
+		if (sysfs_streq(boot_names[i].name, action))
+			return boot_names[i].value;
+
+	return -EINVAL;
+}
+
+/* Return the action in string. */
+static const char *mlxbf_bootctl_action_to_string(int action)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
+		if (boot_names[i].value == action)
+			return boot_names[i].name;
+
+	return "invalid action";
+}
+
+static ssize_t post_reset_wdog_show(struct device_driver *drv, char *buf)
+{
+	int ret;
+
+	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_POST_RESET_WDOG, 0);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t post_reset_wdog_store(struct device_driver *drv,
+				     const char *buf, size_t count)
+{
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &value);
+	if (ret)
+		return ret;
+
+	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_POST_RESET_WDOG, value);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t mlxbf_bootctl_show(int smc_op, char *buf)
+{
+	int action;
+
+	action = mlxbf_bootctl_smc(smc_op, 0);
+	if (action < 0)
+		return action;
+
+	return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action));
+}
+
+static int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count)
+{
+	int ret, action;
+
+	action = mlxbf_bootctl_reset_action_to_val(buf);
+	if (action < 0)
+		return action;
+
+	ret = mlxbf_bootctl_smc(smc_op, action);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t reset_action_show(struct device_driver *drv, char *buf)
+{
+	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_RESET_ACTION, buf);
+}
+
+static ssize_t reset_action_store(struct device_driver *drv,
+				  const char *buf, size_t count)
+{
+	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_RESET_ACTION, buf, count);
+}
+
+static ssize_t second_reset_action_show(struct device_driver *drv, char *buf)
+{
+	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION, buf);
+}
+
+static ssize_t second_reset_action_store(struct device_driver *drv,
+					 const char *buf, size_t count)
+{
+	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION, buf,
+				   count);
+}
+
+static ssize_t lifecycle_state_show(struct device_driver *drv, char *buf)
+{
+	int lc_state;
+
+	lc_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
+				     MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
+	if (lc_state < 0)
+		return lc_state;
+
+	lc_state &=
+		MLXBF_BOOTCTL_SB_TEST_MASK | MLXBF_BOOTCTL_SB_SECURE_MASK;
+
+	/*
+	 * 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;
+
+		return sprintf(buf, "%s(test)\n",
+			       mlxbf_bootctl_lifecycle_states[lc_state]);
+	}
+
+	return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
+}
+
+static ssize_t secure_boot_fuse_state_show(struct device_driver *drv, char *buf)
+{
+	int burnt, valid, key, key_state, buf_len = 0, upper_key_used = 0;
+	const char *status;
+
+	key_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
+				      MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
+	if (key_state < 0)
+		return key_state;
+
+	/*
+	 * 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 progamming 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.
+	 */
+	for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
+		burnt = key_state & BIT(key);
+		valid = key_state & BIT(key + MLXBF_SB_KEY_NUM);
+
+		if (burnt && valid)
+			upper_key_used = 1;
+
+		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";
+		}
+		buf_len += sprintf(buf + buf_len, "%d:%s ", key, status);
+	}
+	buf_len += sprintf(buf + buf_len, "\n");
+
+	return buf_len;
+}
+
+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 struct attribute *mlxbf_bootctl_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,
+	NULL
+};
+
+ATTRIBUTE_GROUPS(mlxbf_bootctl);
+
+static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
+	{"MLNXBF04", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
+
+static bool mlxbf_bootctl_guid_match(const guid_t *guid,
+				     const struct arm_smccc_res *res)
+{
+	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);
+
+	return guid_equal(guid, &id);
+}
+
+static int mlxbf_bootctl_probe(struct platform_device *pdev)
+{
+	struct arm_smccc_res res = { 0 };
+	guid_t guid;
+	int ret;
+
+	/* 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))
+		return -ENODEV;
+
+	/*
+	 * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_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");
+
+	return 0;
+}
+
+static struct platform_driver mlxbf_bootctl_driver = {
+	.probe = mlxbf_bootctl_probe,
+	.driver = {
+		.name = "mlxbf-bootctl",
+		.groups = mlxbf_bootctl_groups,
+		.acpi_match_table = mlxbf_bootctl_acpi_ids,
+	}
+};
+
+module_platform_driver(mlxbf_bootctl_driver);
+
+MODULE_DESCRIPTION("Mellanox boot control driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Mellanox Technologies");
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.h b/drivers/platform/mellanox/mlxbf-bootctl.h
new file mode 100644
index 0000000..148fdb4
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf-bootctl.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019, Mellanox Technologies. All rights reserved.
+ */
+
+#ifndef __MLXBF_BOOTCTL_H__
+#define __MLXBF_BOOTCTL_H__
+
+/*
+ * Request that the on-chip watchdog be enabled, or disabled, after
+ * the next chip soft reset. This call does not affect the current
+ * status of the on-chip watchdog. If non-zero, the argument
+ * specifies the watchdog interval in seconds. If zero, the watchdog
+ * 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
+
+/*
+ * 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.
+ */
+#define MLXBF_BOOTCTL_GET_POST_RESET_WDOG	0x82000001
+
+/*
+ * Request that a specific boot action be taken at the next soft
+ * reset. By default, the boot action is set by external chip pins,
+ * which are sampled on hard reset. Note that the boot action
+ * requested by this call will persist on subsequent resets unless
+ * this service, or the MLNX_SET_SECOND_RESET_ACTION service, is
+ * 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
+
+/*
+ * 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).
+ */
+#define MLXBF_BOOTCTL_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
+ * 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
+ * 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.
+ * 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
+
+/*
+ * 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.
+ */
+#define MLXBF_BOOTCTL_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
+
+/* Reset eMMC by programming the RST_N register. */
+#define MLXBF_BOOTCTL_SET_EMMC_RST_N		0x82000007
+
+#define MLXBF_BOOTCTL_GET_DIMM_INFO		0x82000008
+
+/* 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
+
+/* Number of svc calls defined. */
+#define MLXBF_BOOTCTL_NUM_SVC_CALLS 12
+
+/* 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 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 */
+
+/* Additional value to disable the MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION. */
+#define MLXBF_BOOTCTL_NONE	0x7fffffff /* Don't change next boot action */
+
+#endif /* __MLXBF_BOOTCTL_H__ */
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions
  2019-01-29 20:59 [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc Liming Sun
                   ` (5 preceding siblings ...)
  2019-05-17 17:49 ` [PATCH v5 1/2] " Liming Sun
@ 2019-05-17 17:49 ` Liming Sun
  2019-05-17 17:59   ` Greg Kroah-Hartman
  2019-10-07 15:48 ` [PATCH v6 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc Liming Sun
  2019-10-07 15:48 ` [PATCH v6 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions Liming Sun
  8 siblings, 1 reply; 34+ messages in thread
From: Liming Sun @ 2019-05-17 17:49 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods
  Cc: Liming Sun, platform-driver-x86, linux-kernel, David S. Miller,
	Mauro Carvalho Chehab, Greg Kroah-Hartman, Jonathan Cameron,
	Nicolas Ferre, Paul E. McKenney

This commit adds the ABI definitions exposed to userspace for
the platform/mellanox/mlxbf-bootctl driver.

Reviewed-by: Vadim Pasternak <vadimp@mellanox.com>
Signed-off-by: Liming Sun <lsun@mellanox.com>
---
 .../ABI/testing/sysfs-platform-mellanox-bootctl    | 58 ++++++++++++++++++++++
 MAINTAINERS                                        |  1 +
 2 files changed, 59 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-platform-mellanox-bootctl

diff --git a/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl b/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
new file mode 100644
index 0000000..19a14db
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
@@ -0,0 +1,58 @@
+What:		/sys/bus/platform/drivers/mlxbf-bootctl/lifecycle_state
+Date:		May 2019
+KernelVersion:	5.3
+Contact:	"Liming Sun <lsun@mellanox.com>"
+Description:
+		The Life-cycle state of the SoC, which could be one of the
+		following values.
+		  Production - Production state and can be updated to secure
+		  GA Secured - Secure chip and not able to change state
+		  GA Non-Secured - Non-Secure chip and not able to change state
+		  RMA - Return Merchandise Authorization
+
+What:		/sys/bus/platform/drivers/mlxbf-bootctl/post_reset_wdog
+Date:		May 2019
+KernelVersion:	5.3
+Contact:	"Liming Sun <lsun@mellanox.com>"
+Description:
+		The watchdog setting in seconds for the next booting. It's used
+		to reboot the chip and recover it to the old state if the new
+		boot partition fails.
+
+What:		/sys/bus/platform/drivers/mlxbf-bootctl/reset_action
+Date:		May 2019
+KernelVersion:	5.3
+Contact:	"Liming Sun <lsun@mellanox.com>"
+Description:
+		The source of the boot stream for the next reset. It could be
+		one of the following values.
+		  external - boot from external source (USB or PCIe)
+		  emmc - boot from the onchip eMMC
+		  emmc_legacy - boot from the onchip eMMC in legacy (slow) mode
+
+What:		/sys/bus/platform/drivers/mlxbf-bootctl/second_reset_action
+Date:		May 2019
+KernelVersion:	5.3
+Contact:	"Liming Sun <lsun@mellanox.com>"
+Description:
+		Update the source of the boot stream after next reset. It could
+		be one of the following values and will be applied after next
+		reset.
+		  external - boot from external source (USB or PCIe)
+		  emmc - boot from the onchip eMMC
+		  emmc_legacy - boot from the onchip eMMC in legacy (slow) mode
+		  swap_emmc - swap the primary / secondary boot partition
+		  none - cancel the action
+
+What:		/sys/bus/platform/drivers/mlxbf-bootctl/secure_boot_fuse_state
+Date:		May 2019
+KernelVersion:	5.3
+Contact:	"Liming Sun <lsun@mellanox.com>"
+Description:
+		The state of eFuse versions with the following values.
+		  InUse - burnt, valid and currently in use
+		  Used - burnt and valid
+		  Free - not burnt and free to use
+		  Skipped - not burnt but not free (skipped)
+		  Wasted - burnt and invalid
+		  Invalid - not burnt but marked as valid (error state).
diff --git a/MAINTAINERS b/MAINTAINERS
index fb9f9d7..8a9d9ce 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10018,6 +10018,7 @@ M:	Darren Hart <dvhart@infradead.org>
 M:	Vadim Pasternak <vadimp@mellanox.com>
 L:	platform-driver-x86@vger.kernel.org
 S:	Supported
+F:	Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
 F:	drivers/platform/mellanox/
 F:	include/linux/platform_data/mlxreg.h
 
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* RE: [PATCH v4] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-02-05 17:21   ` Andy Shevchenko
@ 2019-05-17 17:49     ` Liming Sun
  0 siblings, 0 replies; 34+ messages in thread
From: Liming Sun @ 2019-05-17 17:49 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	Platform Driver, Linux Kernel Mailing List

Thanks Andy!

v5 has been posted to address all the comments. You could also see the detailed response below.

Regards,
Liming

> -----Original Message-----
> From: linux-kernel-owner@vger.kernel.org <linux-kernel-owner@vger.kernel.org> On Behalf Of Andy Shevchenko
> Sent: Tuesday, February 5, 2019 12:22 PM
> To: Liming Sun <lsun@mellanox.com>
> Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>; David
> Woods <dwoods@mellanox.com>; Platform Driver <platform-driver-x86@vger.kernel.org>; Linux Kernel Mailing List <linux-
> kernel@vger.kernel.org>
> Subject: Re: [PATCH v4] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
> 
> On Fri, Feb 1, 2019 at 10:47 PM Liming Sun <lsun@mellanox.com> wrote:
> 
> Thanks for an update, my comments below.
> 
> (To Mellanox kernel developer team: guys, perhaps you need to
> establish a few rounds of internal review before sending the stuff
> outside)

Yes, we did internal review on v5 and updated the 'Reviewed-by'.

> 
> First of all, I didn't find ABI documentation for interface this patch
> introduces.
> Must be added.

Added in v5.

> 
> > +/* UUID used to probe ATF service. */
> > +static const char * const mlxbf_bootctl_svc_uuid_str =
> > +       "89c036b4-e7d7-11e6-8797-001aca00bfc4";
> 
> static const char *..._str = ...;

Updated in v5.

> 
> > +/* Syntactic sugar to avoid having to specify an unused argument. */
> > +#define mlxbf_bootctl_smc_call0(smc_op) mlxbf_bootctl_smc_call1(smc_op, 0)
> 
> No.
> Please, do it explicitly.

Updated in v5. Renamed mlxbf_bootctl_smc_call1() to mlxbf_bootctl_smc().

> 
> > +static const char *mlxbf_bootctl_reset_action_to_string(int action)
> > +{
> > +       int i;
> > +
> > +       for (i = 0; i < ARRAY_SIZE(boot_names); i++)
> > +               if (boot_names[i].value == action)
> > +                       return boot_names[i].name;
> > +
> 
> > +       return "";
> 
> Hmm...
> Shouldn't be more descriptive?

Updated in v5 to return "invalid action" in such case.

> 
> > +}
> 
> > +static ssize_t post_reset_wdog_show(struct device_driver *drv, char *buf)
> > +{
> > +       return sprintf(buf, "%d\n",
> > +               mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_POST_RESET_WDOG));
> 
> What if call return negative error?

Fixed in v5 to check this return value.

> 
> > +}
> 
> > +static ssize_t reset_action_show(struct device_driver *drv, char *buf)
> > +{
> > +       int action;
> > +
> > +       action = mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_RESET_ACTION);
> 
> What if action goes negative?

Fixed in v5 to check this return value.

> 
> > +       return sprintf(buf, "%s\n",
> > +                      mlxbf_bootctl_reset_action_to_string(action));
> 
> Wouldn't be one line?

Updated in v5 (one line).

> 
> > +}
> 
> > +static ssize_t reset_action_store(struct device_driver *drv,
> > +                                 const char *buf, size_t count)
> > +{
> 
> > +       int ret, action = mlxbf_bootctl_reset_action_to_val(buf);
> 
> This should be like
> int action;
> int ret;
> 
> action = ...;
> if (action ...)

Updated in v5.

> 
> 
> > +       if (action == MLXBF_BOOTCTL_INVALID || action == MLXBF_BOOTCTL_NONE)
> > +               return -EINVAL;
> 
> The mlxbf_bootctl_reset_action_to_val() has to return a proper Linux error code.
> After this code should be modified to something like
> 
> if (action < 0)
>  return action;

Updated in v5.

> 
> > +
> > +       ret = (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION, action));
> 
> Redundant parens.

Fixed in v5.

> 
> > +       if (ret < 0)
> > +               return ret;
> > +
> > +       return count;
> > +}
> > +
> > +static ssize_t second_reset_action_show(struct device_driver *drv, char *buf)
> > +{
> > +       int action;
> > +       const char *name;
> > +
> > +       action = mlxbf_bootctl_smc_call0(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION);
> 
> What if action is negative?

Updated in v5.

> 
> > +       name = mlxbf_bootctl_reset_action_to_string(action);
> > +
> > +       return sprintf(buf, "%s\n", name);
> 
> return sprintf(... _to_string(...));
> ?

Updated in v5 as suggested.

> 
> > +}
> > +
> > +static ssize_t second_reset_action_store(struct device_driver *drv,
> > +                                        const char *buf, size_t count)
> > +{
> 
> > +       int ret, action = mlxbf_bootctl_reset_action_to_val(buf);
> 
> int action;
> int ret;
> 
> action = ...
> if (action ...)

Updated in v5.

> 
> > +
> > +       if (action < 0)
> > +               return action;
> > +
> > +       ret = mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION,
> > +                                     action);
> > +       if (ret < 0)
> > +               return ret;
> > +
> > +       return count;
> > +}
> > +
> > +static ssize_t lifecycle_state_show(struct device_driver *drv, char *buf)
> > +{
> > +       int lc_state;
> > +
> > +       lc_state = mlxbf_bootctl_smc_call1(
> > +                                          MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> > +                                          MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
> 
> > +
> 
> Redundant blank line.

Updated in v5.

> 
> > +       if (lc_state < 0)
> > +               return lc_state;
> > +
> > +       lc_state &=
> > +               (MLXBF_BOOTCTL_SB_TEST_MASK | MLXBF_BOOTCTL_SB_SECURE_MASK);
> 
> Actually parens are not needed. Sorry, I forgot to point to this earlier.

Updated in v5.

> 
> > +
> > +       /*
> > +        * 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;
> > +
> > +               return sprintf(buf, "%s(test)\n",
> > +                              mlxbf_bootctl_lifecycle_states[lc_state]);
> > +       }
> > +
> > +       return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
> > +}
> > +
> > +static ssize_t secure_boot_fuse_state_show(struct device_driver *drv, char *buf)
> > +{
> > +       int sb_key_state = mlxbf_bootctl_smc_call1(
> > +                               MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> > +                               MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
> 
> Split it to declaration and assignment.

Updated in v5.

> 
> > +       int upper_key_used = 0;
> > +       int buf_len = 0;
> > +       const char *status;
> > +       int key;
> > +
> > +       if (sb_key_state < 0)
> > +               return sb_key_state;
> > +
> > +       for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
> 
> > +               int burnt = sb_key_state & (1 << key);
> 
> BIT() ?

Updated in v5.

> 
> > +               int valid = sb_key_state &
> > +                             (1 << (key + MLXBF_SB_KEY_NUM));
> 
> Ditto. And put to one line?

Updated in v5.

> 
> > +               buf_len += sprintf(buf + buf_len, "%d:", key);
> 
> Why this can't be part of the below sprintf() call?

Updated in v5.

> 
> > +               if (upper_key_used) {
> > +                       if (burnt)
> > +                               status = valid ? "Used" : "Wasted";
> > +                       else
> > +                               status = valid ? "Invalid" : "Skipped";
> > +               } else {
> > +                       if (burnt) {
> > +                               status = valid ? "In use" : "Burn incomplete";
> 
> > +                               if (valid)
> > +                                       upper_key_used = 1;
> 
> Move this out of this if-else-if block. The rationale is to split two
> logical parts:
> 1) message choice
> 2) flag flip

Updated the logic in v5 as suggested.

> 
> > +                       } else
> > +                               status = valid ? "Invalid" : "Free";
> > +               }
> > +               buf_len += sprintf(buf + buf_len, status);
> 
> > +               if (key != 0)
> > +                       buf_len += sprintf(buf + buf_len, "; ");
> 
> Why do you need to check this?

Updated in v5 to remove the check and merge it with the line above.

> 
> > +       }
> > +       buf_len += sprintf(buf + buf_len, "\n");
> > +
> > +       return buf_len;
> > +}
> 
> > +static struct attribute_group mlxbf_bootctl_attr_group = {
> > +       .attrs = mlxbf_bootctl_dev_attrs
> 
> + comma.

Updated in v5.

> 
> > +};
> 
> > +static bool mlxbf_bootctl_guid_match(const guid_t *guid,
> > +                                    const struct arm_smccc_res *res)
> > +{
> 
> > +       guid_t id = GUID_INIT(res->a0, res->a1 & 0xFFFF,
> > +                             (res->a1 >> 16) & 0xFFFF,
> > +                             res->a2 & 0xff, (res->a2 >> 8) & 0xFF,
> > +                             (res->a2 >> 16) & 0xFF, (res->a2 >> 24) & 0xFF,
> > +                             res->a3 & 0xff, (res->a3 >> 8) & 0xFF,
> > +                             (res->a3 >> 16) & 0xFF, (res->a3 >> 24) & 0xFF);
> 
> All & 0xFF* are done inside the macro, no need to duplicate.

Updated in v5.

> 
> > +
> > +       return guid_equal(guid, &id);
> > +}
> 
> > +static int mlxbf_bootctl_probe(struct platform_device *pdev)
> > +{
> > +       struct arm_smccc_res res;
> > +       guid_t guid;
> > +
> > +       /* 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))
> > +               return -ENODEV;
> > +
> > +       /*
> > +        * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_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.
> > +        */
> 
> > +       if (mlxbf_bootctl_smc_call1(MLXBF_BOOTCTL_SET_RESET_ACTION,
> > +                                   MLXBF_BOOTCTL_EMMC) < 0)
> 
> Use temporary variable here.

Updated in v5.

> 
> int ret;> +
> 
> ...
> ret = ..._call1(...);
> if (ret < 0)
> 
> > +               dev_err(&pdev->dev, "Unable to reset the EMMC boot mode\n");
> 
> If it's error, why we return 0?
> Otherwise, use warn level here.

Updated to use dev_warn() to avoid module probe failure since other
functionalities can still be used.

> 
> > +
> > +       return 0;
> > +}
> > +
> > +static struct platform_driver mlxbf_bootctl_driver = {
> > +       .probe = mlxbf_bootctl_probe,
> > +       .driver = {
> > +               .name = "mlxbf-bootctl",
> > +               .groups = mlxbf_bootctl_attr_groups,
> 
> > +               .acpi_match_table = ACPI_PTR(mlxbf_bootctl_acpi_ids),
> 
> ACPI_PTR is redundant for the ACPI dependent driver.

Updated in v5.

> 
> > +       }
> > +};
> 
> --
> With Best Regards,
> Andy Shevchenko

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions
  2019-05-17 17:49 ` [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions Liming Sun
@ 2019-05-17 17:59   ` Greg Kroah-Hartman
  2019-05-17 20:36     ` Liming Sun
  0 siblings, 1 reply; 34+ messages in thread
From: Greg Kroah-Hartman @ 2019-05-17 17:59 UTC (permalink / raw)
  To: Liming Sun
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	platform-driver-x86, linux-kernel, David S. Miller,
	Mauro Carvalho Chehab, Jonathan Cameron, Nicolas Ferre,
	Paul E. McKenney

On Fri, May 17, 2019 at 01:49:05PM -0400, Liming Sun wrote:
> This commit adds the ABI definitions exposed to userspace for
> the platform/mellanox/mlxbf-bootctl driver.
> 
> Reviewed-by: Vadim Pasternak <vadimp@mellanox.com>
> Signed-off-by: Liming Sun <lsun@mellanox.com>
> ---
>  .../ABI/testing/sysfs-platform-mellanox-bootctl    | 58 ++++++++++++++++++++++
>  MAINTAINERS                                        |  1 +
>  2 files changed, 59 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
> 
> diff --git a/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl b/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
> new file mode 100644
> index 0000000..19a14db
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
> @@ -0,0 +1,58 @@
> +What:		/sys/bus/platform/drivers/mlxbf-bootctl/lifecycle_state
> +Date:		May 2019
> +KernelVersion:	5.3
> +Contact:	"Liming Sun <lsun@mellanox.com>"
> +Description:
> +		The Life-cycle state of the SoC, which could be one of the
> +		following values.
> +		  Production - Production state and can be updated to secure
> +		  GA Secured - Secure chip and not able to change state
> +		  GA Non-Secured - Non-Secure chip and not able to change state
> +		  RMA - Return Merchandise Authorization

A "driver" does not have a lifecycle state, a "device" does.

You are putting all of these attributes in the wrong place.  Put them on
your device please, not the driver.  driver-specific attributes are
_VERY_ rare, and only for things that can modify/show for all devices
attached to that driver.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* RE: [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions
  2019-05-17 17:59   ` Greg Kroah-Hartman
@ 2019-05-17 20:36     ` Liming Sun
  2019-05-18  6:35       ` Greg Kroah-Hartman
  0 siblings, 1 reply; 34+ messages in thread
From: Liming Sun @ 2019-05-17 20:36 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	platform-driver-x86, linux-kernel, David S. Miller,
	Mauro Carvalho Chehab, Jonathan Cameron, Nicolas Ferre,
	Paul E. McKenney

Thanks Greg for the comments!  Please see my response inline.

Regards,
- Liming

> -----Original Message-----
> From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Sent: Friday, May 17, 2019 1:59 PM
> To: Liming Sun <lsun@mellanox.com>
> Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>; David
> Woods <dwoods@mellanox.com>; platform-driver-x86@vger.kernel.org; linux-kernel@vger.kernel.org; David S. Miller
> <davem@davemloft.net>; Mauro Carvalho Chehab <mchehab+samsung@kernel.org>; Jonathan Cameron
> <Jonathan.Cameron@huawei.com>; Nicolas Ferre <nicolas.ferre@microchip.com>; Paul E. McKenney <paulmck@linux.ibm.com>
> Subject: Re: [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions
> 
> On Fri, May 17, 2019 at 01:49:05PM -0400, Liming Sun wrote:
> > This commit adds the ABI definitions exposed to userspace for
> > the platform/mellanox/mlxbf-bootctl driver.
> >
> > Reviewed-by: Vadim Pasternak <vadimp@mellanox.com>
> > Signed-off-by: Liming Sun <lsun@mellanox.com>
> > ---
> >  .../ABI/testing/sysfs-platform-mellanox-bootctl    | 58 ++++++++++++++++++++++
> >  MAINTAINERS                                        |  1 +
> >  2 files changed, 59 insertions(+)
> >  create mode 100644 Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
> >
> > diff --git a/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl b/Documentation/ABI/testing/sysfs-platform-mellanox-
> bootctl
> > new file mode 100644
> > index 0000000..19a14db
> > --- /dev/null
> > +++ b/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
> > @@ -0,0 +1,58 @@
> > +What:		/sys/bus/platform/drivers/mlxbf-bootctl/lifecycle_state
> > +Date:		May 2019
> > +KernelVersion:	5.3
> > +Contact:	"Liming Sun <lsun@mellanox.com>"
> > +Description:
> > +		The Life-cycle state of the SoC, which could be one of the
> > +		following values.
> > +		  Production - Production state and can be updated to secure
> > +		  GA Secured - Secure chip and not able to change state
> > +		  GA Non-Secured - Non-Secure chip and not able to change state
> > +		  RMA - Return Merchandise Authorization
> 
> A "driver" does not have a lifecycle state, a "device" does.
> 
> You are putting all of these attributes in the wrong place.  Put them on
> your device please, not the driver.  driver-specific attributes are
> _VERY_ rare, and only for things that can modify/show for all devices
> attached to that driver.

This driver is running on the ARM processor of the SoC. The 'device' is
the SoC itself. That's to say, there is only one device here attached to
the driver and the driver state will also be the device state.

This interface has been used by user-space applications for a couple of
releases. It'll be great if it could stay in such way for compatibility. Please
advise if this is strongly preferred to move them under devices.

> 
> thanks,
> 
> greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions
  2019-05-17 20:36     ` Liming Sun
@ 2019-05-18  6:35       ` Greg Kroah-Hartman
  2019-05-20 15:20         ` Liming Sun
  0 siblings, 1 reply; 34+ messages in thread
From: Greg Kroah-Hartman @ 2019-05-18  6:35 UTC (permalink / raw)
  To: Liming Sun
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	platform-driver-x86, linux-kernel, David S. Miller,
	Mauro Carvalho Chehab, Jonathan Cameron, Nicolas Ferre,
	Paul E. McKenney

On Fri, May 17, 2019 at 08:36:53PM +0000, Liming Sun wrote:
> Thanks Greg for the comments!  Please see my response inline.
> 
> Regards,
> - Liming
> 
> > -----Original Message-----
> > From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> > Sent: Friday, May 17, 2019 1:59 PM
> > To: Liming Sun <lsun@mellanox.com>
> > Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>; David
> > Woods <dwoods@mellanox.com>; platform-driver-x86@vger.kernel.org; linux-kernel@vger.kernel.org; David S. Miller
> > <davem@davemloft.net>; Mauro Carvalho Chehab <mchehab+samsung@kernel.org>; Jonathan Cameron
> > <Jonathan.Cameron@huawei.com>; Nicolas Ferre <nicolas.ferre@microchip.com>; Paul E. McKenney <paulmck@linux.ibm.com>
> > Subject: Re: [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions
> > 
> > On Fri, May 17, 2019 at 01:49:05PM -0400, Liming Sun wrote:
> > > This commit adds the ABI definitions exposed to userspace for
> > > the platform/mellanox/mlxbf-bootctl driver.
> > >
> > > Reviewed-by: Vadim Pasternak <vadimp@mellanox.com>
> > > Signed-off-by: Liming Sun <lsun@mellanox.com>
> > > ---
> > >  .../ABI/testing/sysfs-platform-mellanox-bootctl    | 58 ++++++++++++++++++++++
> > >  MAINTAINERS                                        |  1 +
> > >  2 files changed, 59 insertions(+)
> > >  create mode 100644 Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
> > >
> > > diff --git a/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl b/Documentation/ABI/testing/sysfs-platform-mellanox-
> > bootctl
> > > new file mode 100644
> > > index 0000000..19a14db
> > > --- /dev/null
> > > +++ b/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
> > > @@ -0,0 +1,58 @@
> > > +What:		/sys/bus/platform/drivers/mlxbf-bootctl/lifecycle_state
> > > +Date:		May 2019
> > > +KernelVersion:	5.3
> > > +Contact:	"Liming Sun <lsun@mellanox.com>"
> > > +Description:
> > > +		The Life-cycle state of the SoC, which could be one of the
> > > +		following values.
> > > +		  Production - Production state and can be updated to secure
> > > +		  GA Secured - Secure chip and not able to change state
> > > +		  GA Non-Secured - Non-Secure chip and not able to change state
> > > +		  RMA - Return Merchandise Authorization
> > 
> > A "driver" does not have a lifecycle state, a "device" does.
> > 
> > You are putting all of these attributes in the wrong place.  Put them on
> > your device please, not the driver.  driver-specific attributes are
> > _VERY_ rare, and only for things that can modify/show for all devices
> > attached to that driver.
> 
> This driver is running on the ARM processor of the SoC. The 'device' is
> the SoC itself. That's to say, there is only one device here attached to
> the driver and the driver state will also be the device state.

That might be true today, but maybe not tomorrow :)

Anyway, again, this is device state, not driver state.

> This interface has been used by user-space applications for a couple of
> releases. It'll be great if it could stay in such way for compatibility. Please
> advise if this is strongly preferred to move them under devices.

So this is code that is already in the tree, and is just now being
documented?  What .c file(s) is this referring to?

As for "comptability", sysfs is made such that if a file is not present,
userspace should be able to survive, that is why it is
one-value-per-file.  What tool is using this, and where is the source
for it?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* RE: [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions
  2019-05-18  6:35       ` Greg Kroah-Hartman
@ 2019-05-20 15:20         ` Liming Sun
  2019-05-20 15:53           ` Greg Kroah-Hartman
  0 siblings, 1 reply; 34+ messages in thread
From: Liming Sun @ 2019-05-20 15:20 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	platform-driver-x86, linux-kernel, David S. Miller,
	Mauro Carvalho Chehab, Jonathan Cameron, Nicolas Ferre,
	Paul E. McKenney

Please see response inline.

> -----Original Message-----
> From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Sent: Saturday, May 18, 2019 2:35 AM
> To: Liming Sun <lsun@mellanox.com>
> Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>; David
> Woods <dwoods@mellanox.com>; platform-driver-x86@vger.kernel.org; linux-kernel@vger.kernel.org; David S. Miller
> <davem@davemloft.net>; Mauro Carvalho Chehab <mchehab+samsung@kernel.org>; Jonathan Cameron
> <Jonathan.Cameron@huawei.com>; Nicolas Ferre <nicolas.ferre@microchip.com>; Paul E. McKenney <paulmck@linux.ibm.com>
> Subject: Re: [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions
> 
> On Fri, May 17, 2019 at 08:36:53PM +0000, Liming Sun wrote:
> > Thanks Greg for the comments!  Please see my response inline.
> >
> > Regards,
> > - Liming
> >
> > > -----Original Message-----
> > > From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> > > Sent: Friday, May 17, 2019 1:59 PM
> > > To: Liming Sun <lsun@mellanox.com>
> > > Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>;
> David
> > > Woods <dwoods@mellanox.com>; platform-driver-x86@vger.kernel.org; linux-kernel@vger.kernel.org; David S. Miller
> > > <davem@davemloft.net>; Mauro Carvalho Chehab <mchehab+samsung@kernel.org>; Jonathan Cameron
> > > <Jonathan.Cameron@huawei.com>; Nicolas Ferre <nicolas.ferre@microchip.com>; Paul E. McKenney <paulmck@linux.ibm.com>
> > > Subject: Re: [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions
> > >
> > > On Fri, May 17, 2019 at 01:49:05PM -0400, Liming Sun wrote:
> > > > This commit adds the ABI definitions exposed to userspace for
> > > > the platform/mellanox/mlxbf-bootctl driver.
> > > >
> > > > Reviewed-by: Vadim Pasternak <vadimp@mellanox.com>
> > > > Signed-off-by: Liming Sun <lsun@mellanox.com>
> > > > ---
> > > >  .../ABI/testing/sysfs-platform-mellanox-bootctl    | 58 ++++++++++++++++++++++
> > > >  MAINTAINERS                                        |  1 +
> > > >  2 files changed, 59 insertions(+)
> > > >  create mode 100644 Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
> > > >
> > > > diff --git a/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl b/Documentation/ABI/testing/sysfs-platform-mellanox-
> > > bootctl
> > > > new file mode 100644
> > > > index 0000000..19a14db
> > > > --- /dev/null
> > > > +++ b/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
> > > > @@ -0,0 +1,58 @@
> > > > +What:		/sys/bus/platform/drivers/mlxbf-bootctl/lifecycle_state
> > > > +Date:		May 2019
> > > > +KernelVersion:	5.3
> > > > +Contact:	"Liming Sun <lsun@mellanox.com>"
> > > > +Description:
> > > > +		The Life-cycle state of the SoC, which could be one of the
> > > > +		following values.
> > > > +		  Production - Production state and can be updated to secure
> > > > +		  GA Secured - Secure chip and not able to change state
> > > > +		  GA Non-Secured - Non-Secure chip and not able to change state
> > > > +		  RMA - Return Merchandise Authorization
> > >
> > > A "driver" does not have a lifecycle state, a "device" does.
> > >
> > > You are putting all of these attributes in the wrong place.  Put them on
> > > your device please, not the driver.  driver-specific attributes are
> > > _VERY_ rare, and only for things that can modify/show for all devices
> > > attached to that driver.
> >
> > This driver is running on the ARM processor of the SoC. The 'device' is
> > the SoC itself. That's to say, there is only one device here attached to
> > the driver and the driver state will also be the device state.
> 
> That might be true today, but maybe not tomorrow :)
> 
> Anyway, again, this is device state, not driver state.

Yes, I agree. I discussed with the team and will move these attributes
under device (since they could be all considered as device state).

> 
> > This interface has been used by user-space applications for a couple of
> > releases. It'll be great if it could stay in such way for compatibility. Please
> > advise if this is strongly preferred to move them under devices.
> 
> So this is code that is already in the tree, and is just now being
> documented?  What .c file(s) is this referring to?

This code is not in the kernel tree yet. It has been in Mellanox BlueField
SW packages for a couple of releases and is trying to be up-streamed now.

> 
> As for "comptability", sysfs is made such that if a file is not present,
> userspace should be able to survive, that is why it is
> one-value-per-file.  What tool is using this, and where is the source
> for it?

The latest 2.0 code can be found at link
https://github.com/Mellanox/mlxbf-bootctl/tree/2.0

In file mlxbf-bootctl.c, currently it uses the 'drivers' path as sysfs path.
#define SYS_PATH "/sys/bus/platform/drivers/mlx-bootctl". We could
update it to support both paths.

> 
> thanks,
> 
> greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions
  2019-05-20 15:20         ` Liming Sun
@ 2019-05-20 15:53           ` Greg Kroah-Hartman
  0 siblings, 0 replies; 34+ messages in thread
From: Greg Kroah-Hartman @ 2019-05-20 15:53 UTC (permalink / raw)
  To: Liming Sun
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	platform-driver-x86, linux-kernel, David S. Miller,
	Mauro Carvalho Chehab, Jonathan Cameron, Nicolas Ferre,
	Paul E. McKenney

On Mon, May 20, 2019 at 03:20:02PM +0000, Liming Sun wrote:
> Please see response inline.
> 
> > -----Original Message-----
> > From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> > Sent: Saturday, May 18, 2019 2:35 AM
> > To: Liming Sun <lsun@mellanox.com>
> > Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>; David
> > Woods <dwoods@mellanox.com>; platform-driver-x86@vger.kernel.org; linux-kernel@vger.kernel.org; David S. Miller
> > <davem@davemloft.net>; Mauro Carvalho Chehab <mchehab+samsung@kernel.org>; Jonathan Cameron
> > <Jonathan.Cameron@huawei.com>; Nicolas Ferre <nicolas.ferre@microchip.com>; Paul E. McKenney <paulmck@linux.ibm.com>
> > Subject: Re: [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions
> > 
> > On Fri, May 17, 2019 at 08:36:53PM +0000, Liming Sun wrote:
> > > Thanks Greg for the comments!  Please see my response inline.
> > >
> > > Regards,
> > > - Liming
> > >
> > > > -----Original Message-----
> > > > From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> > > > Sent: Friday, May 17, 2019 1:59 PM
> > > > To: Liming Sun <lsun@mellanox.com>
> > > > Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>;
> > David
> > > > Woods <dwoods@mellanox.com>; platform-driver-x86@vger.kernel.org; linux-kernel@vger.kernel.org; David S. Miller
> > > > <davem@davemloft.net>; Mauro Carvalho Chehab <mchehab+samsung@kernel.org>; Jonathan Cameron
> > > > <Jonathan.Cameron@huawei.com>; Nicolas Ferre <nicolas.ferre@microchip.com>; Paul E. McKenney <paulmck@linux.ibm.com>
> > > > Subject: Re: [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions
> > > >
> > > > On Fri, May 17, 2019 at 01:49:05PM -0400, Liming Sun wrote:
> > > > > This commit adds the ABI definitions exposed to userspace for
> > > > > the platform/mellanox/mlxbf-bootctl driver.
> > > > >
> > > > > Reviewed-by: Vadim Pasternak <vadimp@mellanox.com>
> > > > > Signed-off-by: Liming Sun <lsun@mellanox.com>
> > > > > ---
> > > > >  .../ABI/testing/sysfs-platform-mellanox-bootctl    | 58 ++++++++++++++++++++++
> > > > >  MAINTAINERS                                        |  1 +
> > > > >  2 files changed, 59 insertions(+)
> > > > >  create mode 100644 Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
> > > > >
> > > > > diff --git a/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl b/Documentation/ABI/testing/sysfs-platform-mellanox-
> > > > bootctl
> > > > > new file mode 100644
> > > > > index 0000000..19a14db
> > > > > --- /dev/null
> > > > > +++ b/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
> > > > > @@ -0,0 +1,58 @@
> > > > > +What:		/sys/bus/platform/drivers/mlxbf-bootctl/lifecycle_state
> > > > > +Date:		May 2019
> > > > > +KernelVersion:	5.3
> > > > > +Contact:	"Liming Sun <lsun@mellanox.com>"
> > > > > +Description:
> > > > > +		The Life-cycle state of the SoC, which could be one of the
> > > > > +		following values.
> > > > > +		  Production - Production state and can be updated to secure
> > > > > +		  GA Secured - Secure chip and not able to change state
> > > > > +		  GA Non-Secured - Non-Secure chip and not able to change state
> > > > > +		  RMA - Return Merchandise Authorization
> > > >
> > > > A "driver" does not have a lifecycle state, a "device" does.
> > > >
> > > > You are putting all of these attributes in the wrong place.  Put them on
> > > > your device please, not the driver.  driver-specific attributes are
> > > > _VERY_ rare, and only for things that can modify/show for all devices
> > > > attached to that driver.
> > >
> > > This driver is running on the ARM processor of the SoC. The 'device' is
> > > the SoC itself. That's to say, there is only one device here attached to
> > > the driver and the driver state will also be the device state.
> > 
> > That might be true today, but maybe not tomorrow :)
> > 
> > Anyway, again, this is device state, not driver state.
> 
> Yes, I agree. I discussed with the team and will move these attributes
> under device (since they could be all considered as device state).

Great.

> > > This interface has been used by user-space applications for a couple of
> > > releases. It'll be great if it could stay in such way for compatibility. Please
> > > advise if this is strongly preferred to move them under devices.
> > 
> > So this is code that is already in the tree, and is just now being
> > documented?  What .c file(s) is this referring to?
> 
> This code is not in the kernel tree yet. It has been in Mellanox BlueField
> SW packages for a couple of releases and is trying to be up-streamed now.

Ok, then whatever happened in the past, before anyone in the community
reviewed your code, and merged it into the kernel tree, does not count :)

Where is the first patch in this series?  I'll be glad to review your
actual sysfs implementation if you point me at it.

> > As for "comptability", sysfs is made such that if a file is not present,
> > userspace should be able to survive, that is why it is
> > one-value-per-file.  What tool is using this, and where is the source
> > for it?
> 
> The latest 2.0 code can be found at link
> https://github.com/Mellanox/mlxbf-bootctl/tree/2.0
> 
> In file mlxbf-bootctl.c, currently it uses the 'drivers' path as sysfs path.
> #define SYS_PATH "/sys/bus/platform/drivers/mlx-bootctl". We could
> update it to support both paths.

Please do so.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v5 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-05-17 17:49 ` [PATCH v5 1/2] " Liming Sun
@ 2019-05-20 15:56   ` Greg KH
  2019-05-20 18:07     ` Liming Sun
  2019-05-22 13:34   ` Mark Rutland
  1 sibling, 1 reply; 34+ messages in thread
From: Greg KH @ 2019-05-20 15:56 UTC (permalink / raw)
  To: Liming Sun
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	platform-driver-x86, linux-kernel

On Fri, May 17, 2019 at 01:49:04PM -0400, Liming Sun wrote:
> This commit adds the bootctl platform driver for Mellanox BlueField
> Soc, which controls the eMMC boot partition swapping and sends SMC
> calls to ATF running at exception level EL3 to program some system
> register. This register is persistent during warm reset and is only
> accessible in secure code which is used to enable the watchdog after
> reboot.
> 
> Below are the sequences of typical use case.
> 
>   1. User-space tool upgrades one eMMC boot partition and requests
>      the boot partition swapping;
> 
>   2. The bootctl driver handles this request and sends SMC call
>      to ATF. ATF programs register BREADCRUMB0 which has value
>      preserved during warm reset. It also programs eMMC to swap
>      the boot partition;
> 
>   3. After software reset (rebooting), ATF BL1 (BootRom) checks
>      register BREADCRUMB0 to enable watchdog if configured;
> 
>   4. If booting fails, the watchdog timer will trigger rebooting.
>      In such case, ATF Boot ROM will switch the boot partition
>      back to the previous one.
> 
> Reviewed-by: Vadim Pasternak <vadimp@mellanox.com>
> Signed-off-by: Liming Sun <lsun@mellanox.com>
> ---
> v4->v5:
>     Fixes for comments from Andy:
>     - Added ABI documentation;
>     - Remove the extra 'const' in mlxbf_bootctl_svc_uuid_str definition;
>     - Remove the mlxbf_bootctl_smc_call0() MACRO to use function
>       call directly;
>     - Return more descriptive string ('invalid action') in
>       mlxbf_bootctl_reset_action_to_string();
>     - Check return value of the mlxbf_bootctl_smc() in function
>       post_reset_wdog_show() and reset_action_show();
>     - Revise the sprintf() line in reset_action_show() into one line;
>     - Move the 'action = ' assignment out of the declarations
>       in reset_action_store() and check return value of
>       mlxbf_bootctl_reset_action_to_val() in this function;
>     - Removed Redundant parens in reset_action_store();
>     - Check return code of the smc_call in second_reset_action_show(),
>       merge the 'name = ' assignment into the sprintf() directly;
>     - Move the 'action = ' assignment out of the declaration block
>       in second_reset_action_store();
>     - Remove redundant blank line and parents in lifecycle_state_show();
>     - Split declaration and assignment in secure_boot_fuse_state_show();
>     - use BIT() in secure_boot_fuse_state_show() and simplify code in
>       this function to split the logic of message choice and flag flip;
>       Also removed the 'key !=0 ' check since it's not needed;
>     - Added comma in mlxbf_bootctl_attr_group definition.
>     - Removed un-needed '& 0xFF' logic in mlxbf_bootctl_guid_match();
>     - Use temp variable for the result of the smc call in
>       mlxbf_bootctl_probe();
>     - Use dev_warn() instead of dev_err() in mlxbf_bootctl_probe();
>     - Removed the ACPI_PTR usage in the mlxbf_bootctl_driver definition;
>     Fixes for comments from Vadim:
>     - Move mlxbf-bootctl.o to the top of the Makefile;
>     - Fix to use the 'ret' value instead of another mlxbf_bootctl_smc() call;
>     - Fix the 'declaration inside the block' in secure_boot_fuse_state_show();
>     - Use ATTRIBUTE_GROUPS() for the attribute definition;
>     - Use single line for a comment in mlxbf-bootctl.h
>     - Use KernelVersion 5.3 instead of 5.2.2 in the ABI doc;
>     - Use common utility function for the show and store of reset_action
>       and second_reset_action.
>     New changes:
>     - Rename mlxbf_bootctl_smc_call1() to mlxbf_bootctl_smc() to
>       shorten the name so some statement could be make into one line;
>     - Fixed the MODULE_LICENSE();
>     - Added more comments in secure_boot_fuse_state_show();
> v4: Update Kconfig for the dependency on ACPI.
>     Fixed a typo which caused build error for kernel module.
> v2->v3:
>     Fixes for comments from Andy:
>     - More coding style fixes;
>     - Revised the uuid matching code.
> v2: Fix the Kconfig and coding styles, propagate errors to caller.
> v1: Initial version.
> ---
>  drivers/platform/mellanox/Kconfig         |  12 ++
>  drivers/platform/mellanox/Makefile        |   1 +
>  drivers/platform/mellanox/mlxbf-bootctl.c | 311 ++++++++++++++++++++++++++++++
>  drivers/platform/mellanox/mlxbf-bootctl.h | 103 ++++++++++
>  4 files changed, 427 insertions(+)
>  create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.c
>  create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.h
> 
> diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig
> index 530fe7e..386336d 100644
> --- a/drivers/platform/mellanox/Kconfig
> +++ b/drivers/platform/mellanox/Kconfig
> @@ -44,4 +44,16 @@ config MLXBF_TMFIFO
>            platform driver support for the TmFifo which supports console
>            and networking based on the virtio framework.
>  
> +config MLXBF_BOOTCTL
> +	tristate "Mellanox BlueField Firmware Boot Control driver"
> +	depends on ARM64

CONFIG_TEST so that we can all build this to ensure it does nto break?

> +	depends on ACPI
> +	help
> +          The Mellanox BlueField firmware implements functionality to
> +          request swapping the primary and alternate eMMC boot partition,
> +          and to set up a watchdog that can undo that swap if the system
> +          does not boot up correctly. This driver provides sysfs access
> +          to the userspace tools, to be used in conjunction with the eMMC
> +          device driver to do necessary initial swap of the boot partition.
> +
>  endif # MELLANOX_PLATFORM
> diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile
> index a229bda1..499623c 100644
> --- a/drivers/platform/mellanox/Makefile
> +++ b/drivers/platform/mellanox/Makefile
> @@ -3,6 +3,7 @@
>  # Makefile for linux/drivers/platform/mellanox
>  # Mellanox Platform-Specific Drivers
>  #
> +obj-$(CONFIG_MLXBF_BOOTCTL)	+= mlxbf-bootctl.o
>  obj-$(CONFIG_MLXBF_TMFIFO)	+= mlxbf-tmfifo.o
>  obj-$(CONFIG_MLXREG_HOTPLUG)	+= mlxreg-hotplug.o
>  obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o
> diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c
> new file mode 100644
> index 0000000..842b991
> --- /dev/null
> +++ b/drivers/platform/mellanox/mlxbf-bootctl.c
> @@ -0,0 +1,311 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Mellanox boot control driver
> + *
> + * This driver provides a sysfs interface for systems management
> + * software to manage reset-time actions.
> + *
> + * Copyright (C) 2019 Mellanox Technologies
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/arm-smccc.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 MLXBF_SB_KEY_NUM			4
> +
> +/* UUID used to probe ATF service. */
> +static const char *mlxbf_bootctl_svc_uuid_str =
> +	"89c036b4-e7d7-11e6-8797-001aca00bfc4";
> +
> +struct mlxbf_bootctl_name {
> +	u32 value;
> +	const char *name;
> +};
> +
> +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 const char * const mlxbf_bootctl_lifecycle_states[] = {
> +	[0] = "Production",
> +	[1] = "GA Secured",
> +	[2] = "GA Non-Secured",
> +	[3] = "RMA",
> +};
> +
> +/* ARM SMC call which is atomic and no need for lock. */
> +static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg)
> +{
> +	struct arm_smccc_res res;
> +
> +	arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res);
> +
> +	return res.a0;
> +}
> +
> +/* Return the action in integer or an error code. */
> +static int mlxbf_bootctl_reset_action_to_val(const char *action)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
> +		if (sysfs_streq(boot_names[i].name, action))
> +			return boot_names[i].value;
> +
> +	return -EINVAL;
> +}
> +
> +/* Return the action in string. */
> +static const char *mlxbf_bootctl_action_to_string(int action)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
> +		if (boot_names[i].value == action)
> +			return boot_names[i].name;
> +
> +	return "invalid action";
> +}
> +
> +static ssize_t post_reset_wdog_show(struct device_driver *drv, char *buf)
> +{
> +	int ret;
> +
> +	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_POST_RESET_WDOG, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	return sprintf(buf, "%d\n", ret);
> +}
> +
> +static ssize_t post_reset_wdog_store(struct device_driver *drv,
> +				     const char *buf, size_t count)
> +{
> +	unsigned long value;
> +	int ret;
> +
> +	ret = kstrtoul(buf, 10, &value);
> +	if (ret)
> +		return ret;
> +
> +	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_POST_RESET_WDOG, value);
> +	if (ret < 0)
> +		return ret;
> +
> +	return count;
> +}
> +
> +static ssize_t mlxbf_bootctl_show(int smc_op, char *buf)
> +{
> +	int action;
> +
> +	action = mlxbf_bootctl_smc(smc_op, 0);
> +	if (action < 0)
> +		return action;
> +
> +	return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action));
> +}
> +
> +static int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count)
> +{
> +	int ret, action;
> +
> +	action = mlxbf_bootctl_reset_action_to_val(buf);
> +	if (action < 0)
> +		return action;
> +
> +	ret = mlxbf_bootctl_smc(smc_op, action);
> +	if (ret < 0)
> +		return ret;
> +
> +	return count;
> +}
> +
> +static ssize_t reset_action_show(struct device_driver *drv, char *buf)
> +{
> +	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_RESET_ACTION, buf);
> +}
> +
> +static ssize_t reset_action_store(struct device_driver *drv,
> +				  const char *buf, size_t count)
> +{
> +	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_RESET_ACTION, buf, count);
> +}
> +
> +static ssize_t second_reset_action_show(struct device_driver *drv, char *buf)
> +{
> +	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION, buf);
> +}
> +
> +static ssize_t second_reset_action_store(struct device_driver *drv,
> +					 const char *buf, size_t count)
> +{
> +	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION, buf,
> +				   count);
> +}
> +
> +static ssize_t lifecycle_state_show(struct device_driver *drv, char *buf)
> +{
> +	int lc_state;
> +
> +	lc_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> +				     MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
> +	if (lc_state < 0)
> +		return lc_state;
> +
> +	lc_state &=
> +		MLXBF_BOOTCTL_SB_TEST_MASK | MLXBF_BOOTCTL_SB_SECURE_MASK;
> +
> +	/*
> +	 * 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;
> +
> +		return sprintf(buf, "%s(test)\n",
> +			       mlxbf_bootctl_lifecycle_states[lc_state]);

Ick, why is (test) used here?  Who is going to parse this?  Shouldn't
"test" be a separate sysfs file?

> +	}
> +
> +	return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
> +}
> +
> +static ssize_t secure_boot_fuse_state_show(struct device_driver *drv, char *buf)
> +{
> +	int burnt, valid, key, key_state, buf_len = 0, upper_key_used = 0;
> +	const char *status;
> +
> +	key_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> +				      MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
> +	if (key_state < 0)
> +		return key_state;
> +
> +	/*
> +	 * 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 progamming 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.
> +	 */
> +	for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
> +		burnt = key_state & BIT(key);
> +		valid = key_state & BIT(key + MLXBF_SB_KEY_NUM);
> +
> +		if (burnt && valid)
> +			upper_key_used = 1;
> +
> +		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";
> +		}
> +		buf_len += sprintf(buf + buf_len, "%d:%s ", key, status);
> +	}
> +	buf_len += sprintf(buf + buf_len, "\n");

sysfs files are one-value-per-file, not a whole bunch of lists of
different values.  Please fix this up.


> +
> +	return buf_len;
> +}
> +
> +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 struct attribute *mlxbf_bootctl_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,
> +	NULL
> +};
> +
> +ATTRIBUTE_GROUPS(mlxbf_bootctl);
> +
> +static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
> +	{"MLNXBF04", 0},
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
> +
> +static bool mlxbf_bootctl_guid_match(const guid_t *guid,
> +				     const struct arm_smccc_res *res)
> +{
> +	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);
> +
> +	return guid_equal(guid, &id);
> +}
> +
> +static int mlxbf_bootctl_probe(struct platform_device *pdev)
> +{
> +	struct arm_smccc_res res = { 0 };
> +	guid_t guid;
> +	int ret;
> +
> +	/* 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))
> +		return -ENODEV;
> +
> +	/*
> +	 * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_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");
> +
> +	return 0;
> +}
> +
> +static struct platform_driver mlxbf_bootctl_driver = {
> +	.probe = mlxbf_bootctl_probe,
> +	.driver = {
> +		.name = "mlxbf-bootctl",
> +		.groups = mlxbf_bootctl_groups,
> +		.acpi_match_table = mlxbf_bootctl_acpi_ids,

Why is an acpi driver a platform driver?  Isn't there a "real" acpi
driver interface you should be tieing into instead?

Only use a platform driver as an absolute last resort.  I don't think
that is the case here.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* RE: [PATCH v5 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-05-20 15:56   ` Greg KH
@ 2019-05-20 18:07     ` Liming Sun
  2019-05-20 19:12       ` Greg KH
  0 siblings, 1 reply; 34+ messages in thread
From: Liming Sun @ 2019-05-20 18:07 UTC (permalink / raw)
  To: Greg KH
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	platform-driver-x86, linux-kernel



> -----Original Message-----
> From: Greg KH <greg@kroah.com>
> Sent: Monday, May 20, 2019 11:57 AM
> To: Liming Sun <lsun@mellanox.com>
> Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>; David
> Woods <dwoods@mellanox.com>; platform-driver-x86@vger.kernel.org; linux-kernel@vger.kernel.org
> Subject: Re: [PATCH v5 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
> 
> On Fri, May 17, 2019 at 01:49:04PM -0400, Liming Sun wrote:
> > This commit adds the bootctl platform driver for Mellanox BlueField
> > Soc, which controls the eMMC boot partition swapping and sends SMC
> > calls to ATF running at exception level EL3 to program some system
> > register. This register is persistent during warm reset and is only
> > accessible in secure code which is used to enable the watchdog after
> > reboot.
> >
> > Below are the sequences of typical use case.
> >
> >   1. User-space tool upgrades one eMMC boot partition and requests
> >      the boot partition swapping;
> >
> >   2. The bootctl driver handles this request and sends SMC call
> >      to ATF. ATF programs register BREADCRUMB0 which has value
> >      preserved during warm reset. It also programs eMMC to swap
> >      the boot partition;
> >
> >   3. After software reset (rebooting), ATF BL1 (BootRom) checks
> >      register BREADCRUMB0 to enable watchdog if configured;
> >
> >   4. If booting fails, the watchdog timer will trigger rebooting.
> >      In such case, ATF Boot ROM will switch the boot partition
> >      back to the previous one.
> >
> > Reviewed-by: Vadim Pasternak <vadimp@mellanox.com>
> > Signed-off-by: Liming Sun <lsun@mellanox.com>
> > ---
> > v4->v5:
> >     Fixes for comments from Andy:
> >     - Added ABI documentation;
> >     - Remove the extra 'const' in mlxbf_bootctl_svc_uuid_str definition;
> >     - Remove the mlxbf_bootctl_smc_call0() MACRO to use function
> >       call directly;
> >     - Return more descriptive string ('invalid action') in
> >       mlxbf_bootctl_reset_action_to_string();
> >     - Check return value of the mlxbf_bootctl_smc() in function
> >       post_reset_wdog_show() and reset_action_show();
> >     - Revise the sprintf() line in reset_action_show() into one line;
> >     - Move the 'action = ' assignment out of the declarations
> >       in reset_action_store() and check return value of
> >       mlxbf_bootctl_reset_action_to_val() in this function;
> >     - Removed Redundant parens in reset_action_store();
> >     - Check return code of the smc_call in second_reset_action_show(),
> >       merge the 'name = ' assignment into the sprintf() directly;
> >     - Move the 'action = ' assignment out of the declaration block
> >       in second_reset_action_store();
> >     - Remove redundant blank line and parents in lifecycle_state_show();
> >     - Split declaration and assignment in secure_boot_fuse_state_show();
> >     - use BIT() in secure_boot_fuse_state_show() and simplify code in
> >       this function to split the logic of message choice and flag flip;
> >       Also removed the 'key !=0 ' check since it's not needed;
> >     - Added comma in mlxbf_bootctl_attr_group definition.
> >     - Removed un-needed '& 0xFF' logic in mlxbf_bootctl_guid_match();
> >     - Use temp variable for the result of the smc call in
> >       mlxbf_bootctl_probe();
> >     - Use dev_warn() instead of dev_err() in mlxbf_bootctl_probe();
> >     - Removed the ACPI_PTR usage in the mlxbf_bootctl_driver definition;
> >     Fixes for comments from Vadim:
> >     - Move mlxbf-bootctl.o to the top of the Makefile;
> >     - Fix to use the 'ret' value instead of another mlxbf_bootctl_smc() call;
> >     - Fix the 'declaration inside the block' in secure_boot_fuse_state_show();
> >     - Use ATTRIBUTE_GROUPS() for the attribute definition;
> >     - Use single line for a comment in mlxbf-bootctl.h
> >     - Use KernelVersion 5.3 instead of 5.2.2 in the ABI doc;
> >     - Use common utility function for the show and store of reset_action
> >       and second_reset_action.
> >     New changes:
> >     - Rename mlxbf_bootctl_smc_call1() to mlxbf_bootctl_smc() to
> >       shorten the name so some statement could be make into one line;
> >     - Fixed the MODULE_LICENSE();
> >     - Added more comments in secure_boot_fuse_state_show();
> > v4: Update Kconfig for the dependency on ACPI.
> >     Fixed a typo which caused build error for kernel module.
> > v2->v3:
> >     Fixes for comments from Andy:
> >     - More coding style fixes;
> >     - Revised the uuid matching code.
> > v2: Fix the Kconfig and coding styles, propagate errors to caller.
> > v1: Initial version.
> > ---
> >  drivers/platform/mellanox/Kconfig         |  12 ++
> >  drivers/platform/mellanox/Makefile        |   1 +
> >  drivers/platform/mellanox/mlxbf-bootctl.c | 311 ++++++++++++++++++++++++++++++
> >  drivers/platform/mellanox/mlxbf-bootctl.h | 103 ++++++++++
> >  4 files changed, 427 insertions(+)
> >  create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.c
> >  create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.h
> >
> > diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig
> > index 530fe7e..386336d 100644
> > --- a/drivers/platform/mellanox/Kconfig
> > +++ b/drivers/platform/mellanox/Kconfig
> > @@ -44,4 +44,16 @@ config MLXBF_TMFIFO
> >            platform driver support for the TmFifo which supports console
> >            and networking based on the virtio framework.
> >
> > +config MLXBF_BOOTCTL
> > +	tristate "Mellanox BlueField Firmware Boot Control driver"
> > +	depends on ARM64
> 
> CONFIG_TEST so that we can all build this to ensure it does nto break?

Thanks. Will fix it in v6.

> 
> > +	depends on ACPI
> > +	help
> > +          The Mellanox BlueField firmware implements functionality to
> > +          request swapping the primary and alternate eMMC boot partition,
> > +          and to set up a watchdog that can undo that swap if the system
> > +          does not boot up correctly. This driver provides sysfs access
> > +          to the userspace tools, to be used in conjunction with the eMMC
> > +          device driver to do necessary initial swap of the boot partition.
> > +
> >  endif # MELLANOX_PLATFORM
> > diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile
> > index a229bda1..499623c 100644
> > --- a/drivers/platform/mellanox/Makefile
> > +++ b/drivers/platform/mellanox/Makefile
> > @@ -3,6 +3,7 @@
> >  # Makefile for linux/drivers/platform/mellanox
> >  # Mellanox Platform-Specific Drivers
> >  #
> > +obj-$(CONFIG_MLXBF_BOOTCTL)	+= mlxbf-bootctl.o
> >  obj-$(CONFIG_MLXBF_TMFIFO)	+= mlxbf-tmfifo.o
> >  obj-$(CONFIG_MLXREG_HOTPLUG)	+= mlxreg-hotplug.o
> >  obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o
> > diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c
> > new file mode 100644
> > index 0000000..842b991
> > --- /dev/null
> > +++ b/drivers/platform/mellanox/mlxbf-bootctl.c
> > @@ -0,0 +1,311 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Mellanox boot control driver
> > + *
> > + * This driver provides a sysfs interface for systems management
> > + * software to manage reset-time actions.
> > + *
> > + * Copyright (C) 2019 Mellanox Technologies
> > + */
> > +
> > +#include <linux/acpi.h>
> > +#include <linux/arm-smccc.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 MLXBF_SB_KEY_NUM			4
> > +
> > +/* UUID used to probe ATF service. */
> > +static const char *mlxbf_bootctl_svc_uuid_str =
> > +	"89c036b4-e7d7-11e6-8797-001aca00bfc4";
> > +
> > +struct mlxbf_bootctl_name {
> > +	u32 value;
> > +	const char *name;
> > +};
> > +
> > +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 const char * const mlxbf_bootctl_lifecycle_states[] = {
> > +	[0] = "Production",
> > +	[1] = "GA Secured",
> > +	[2] = "GA Non-Secured",
> > +	[3] = "RMA",
> > +};
> > +
> > +/* ARM SMC call which is atomic and no need for lock. */
> > +static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg)
> > +{
> > +	struct arm_smccc_res res;
> > +
> > +	arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res);
> > +
> > +	return res.a0;
> > +}
> > +
> > +/* Return the action in integer or an error code. */
> > +static int mlxbf_bootctl_reset_action_to_val(const char *action)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
> > +		if (sysfs_streq(boot_names[i].name, action))
> > +			return boot_names[i].value;
> > +
> > +	return -EINVAL;
> > +}
> > +
> > +/* Return the action in string. */
> > +static const char *mlxbf_bootctl_action_to_string(int action)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
> > +		if (boot_names[i].value == action)
> > +			return boot_names[i].name;
> > +
> > +	return "invalid action";
> > +}
> > +
> > +static ssize_t post_reset_wdog_show(struct device_driver *drv, char *buf)
> > +{
> > +	int ret;
> > +
> > +	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_POST_RESET_WDOG, 0);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	return sprintf(buf, "%d\n", ret);
> > +}
> > +
> > +static ssize_t post_reset_wdog_store(struct device_driver *drv,
> > +				     const char *buf, size_t count)
> > +{
> > +	unsigned long value;
> > +	int ret;
> > +
> > +	ret = kstrtoul(buf, 10, &value);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_POST_RESET_WDOG, value);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	return count;
> > +}
> > +
> > +static ssize_t mlxbf_bootctl_show(int smc_op, char *buf)
> > +{
> > +	int action;
> > +
> > +	action = mlxbf_bootctl_smc(smc_op, 0);
> > +	if (action < 0)
> > +		return action;
> > +
> > +	return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action));
> > +}
> > +
> > +static int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count)
> > +{
> > +	int ret, action;
> > +
> > +	action = mlxbf_bootctl_reset_action_to_val(buf);
> > +	if (action < 0)
> > +		return action;
> > +
> > +	ret = mlxbf_bootctl_smc(smc_op, action);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	return count;
> > +}
> > +
> > +static ssize_t reset_action_show(struct device_driver *drv, char *buf)
> > +{
> > +	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_RESET_ACTION, buf);
> > +}
> > +
> > +static ssize_t reset_action_store(struct device_driver *drv,
> > +				  const char *buf, size_t count)
> > +{
> > +	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_RESET_ACTION, buf, count);
> > +}
> > +
> > +static ssize_t second_reset_action_show(struct device_driver *drv, char *buf)
> > +{
> > +	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION, buf);
> > +}
> > +
> > +static ssize_t second_reset_action_store(struct device_driver *drv,
> > +					 const char *buf, size_t count)
> > +{
> > +	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION, buf,
> > +				   count);
> > +}
> > +
> > +static ssize_t lifecycle_state_show(struct device_driver *drv, char *buf)
> > +{
> > +	int lc_state;
> > +
> > +	lc_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> > +				     MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
> > +	if (lc_state < 0)
> > +		return lc_state;
> > +
> > +	lc_state &=
> > +		MLXBF_BOOTCTL_SB_TEST_MASK | MLXBF_BOOTCTL_SB_SECURE_MASK;
> > +
> > +	/*
> > +	 * 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;
> > +
> > +		return sprintf(buf, "%s(test)\n",
> > +			       mlxbf_bootctl_lifecycle_states[lc_state]);
> 
> Ick, why is (test) used here?  Who is going to parse this?  Shouldn't
> "test" be a separate sysfs file?

Adding a separate sysfs file for 'test' seems more clear. Will update it in v6.

> 
> > +	}
> > +
> > +	return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
> > +}
> > +
> > +static ssize_t secure_boot_fuse_state_show(struct device_driver *drv, char *buf)
> > +{
> > +	int burnt, valid, key, key_state, buf_len = 0, upper_key_used = 0;
> > +	const char *status;
> > +
> > +	key_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> > +				      MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
> > +	if (key_state < 0)
> > +		return key_state;
> > +
> > +	/*
> > +	 * 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 progamming 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.
> > +	 */
> > +	for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
> > +		burnt = key_state & BIT(key);
> > +		valid = key_state & BIT(key + MLXBF_SB_KEY_NUM);
> > +
> > +		if (burnt && valid)
> > +			upper_key_used = 1;
> > +
> > +		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";
> > +		}
> > +		buf_len += sprintf(buf + buf_len, "%d:%s ", key, status);
> > +	}
> > +	buf_len += sprintf(buf + buf_len, "\n");
> 
> sysfs files are one-value-per-file, not a whole bunch of lists of
> different values.  Please fix this up.

Yes, will fix it in v6.

> 
> 
> > +
> > +	return buf_len;
> > +}
> > +
> > +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 struct attribute *mlxbf_bootctl_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,
> > +	NULL
> > +};
> > +
> > +ATTRIBUTE_GROUPS(mlxbf_bootctl);
> > +
> > +static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
> > +	{"MLNXBF04", 0},
> > +	{}
> > +};
> > +
> > +MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
> > +
> > +static bool mlxbf_bootctl_guid_match(const guid_t *guid,
> > +				     const struct arm_smccc_res *res)
> > +{
> > +	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);
> > +
> > +	return guid_equal(guid, &id);
> > +}
> > +
> > +static int mlxbf_bootctl_probe(struct platform_device *pdev)
> > +{
> > +	struct arm_smccc_res res = { 0 };
> > +	guid_t guid;
> > +	int ret;
> > +
> > +	/* 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))
> > +		return -ENODEV;
> > +
> > +	/*
> > +	 * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_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");
> > +
> > +	return 0;
> > +}
> > +
> > +static struct platform_driver mlxbf_bootctl_driver = {
> > +	.probe = mlxbf_bootctl_probe,
> > +	.driver = {
> > +		.name = "mlxbf-bootctl",
> > +		.groups = mlxbf_bootctl_groups,
> > +		.acpi_match_table = mlxbf_bootctl_acpi_ids,
> 
> Why is an acpi driver a platform driver?  Isn't there a "real" acpi
> driver interface you should be tieing into instead?
> 
> Only use a platform driver as an absolute last resort.  I don't think
> that is the case here.

The driver is trying to configure boot-swapping and display secure state,
and is defined/initiated in ACPI table in UEFI. It seems a little hard to
categorize this driver to any existing subsystem. Any suggestion
where it might be a better fit (like drivers/misc, drivers/firmware, etc)? 
Please correct me if I misunderstand the comments. Thanks!.

> 
> thanks,
> 
> greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v5 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-05-20 18:07     ` Liming Sun
@ 2019-05-20 19:12       ` Greg KH
  2019-05-20 20:43         ` Liming Sun
  0 siblings, 1 reply; 34+ messages in thread
From: Greg KH @ 2019-05-20 19:12 UTC (permalink / raw)
  To: Liming Sun
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	platform-driver-x86, linux-kernel

On Mon, May 20, 2019 at 06:07:44PM +0000, Liming Sun wrote:
> > > +static struct platform_driver mlxbf_bootctl_driver = {
> > > +	.probe = mlxbf_bootctl_probe,
> > > +	.driver = {
> > > +		.name = "mlxbf-bootctl",
> > > +		.groups = mlxbf_bootctl_groups,
> > > +		.acpi_match_table = mlxbf_bootctl_acpi_ids,
> > 
> > Why is an acpi driver a platform driver?  Isn't there a "real" acpi
> > driver interface you should be tieing into instead?
> > 
> > Only use a platform driver as an absolute last resort.  I don't think
> > that is the case here.
> 
> The driver is trying to configure boot-swapping and display secure state,
> and is defined/initiated in ACPI table in UEFI. It seems a little hard to
> categorize this driver to any existing subsystem. Any suggestion
> where it might be a better fit (like drivers/misc, drivers/firmware, etc)? 
> Please correct me if I misunderstand the comments. Thanks!.

The comment was asking why an acpi driver is a platform driver, but then
I went and looked now at a bunch of acpi drivers, and they all are
platform drivers :(

Anyway, drivers/acpi/ seems like the best place for this file, right?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* RE: [PATCH v5 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-05-20 19:12       ` Greg KH
@ 2019-05-20 20:43         ` Liming Sun
  2019-05-21  7:58           ` Arnd Bergmann
  0 siblings, 1 reply; 34+ messages in thread
From: Liming Sun @ 2019-05-20 20:43 UTC (permalink / raw)
  To: Greg KH
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	platform-driver-x86, linux-kernel



> -----Original Message-----
> From: Greg KH <gregkh@linuxfoundation.org>
> Sent: Monday, May 20, 2019 3:12 PM
> To: Liming Sun <lsun@mellanox.com>
> Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>; David
> Woods <dwoods@mellanox.com>; platform-driver-x86@vger.kernel.org; linux-kernel@vger.kernel.org
> Subject: Re: [PATCH v5 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
> 
> On Mon, May 20, 2019 at 06:07:44PM +0000, Liming Sun wrote:
> > > > +static struct platform_driver mlxbf_bootctl_driver = {
> > > > +	.probe = mlxbf_bootctl_probe,
> > > > +	.driver = {
> > > > +		.name = "mlxbf-bootctl",
> > > > +		.groups = mlxbf_bootctl_groups,
> > > > +		.acpi_match_table = mlxbf_bootctl_acpi_ids,
> > >
> > > Why is an acpi driver a platform driver?  Isn't there a "real" acpi
> > > driver interface you should be tieing into instead?
> > >
> > > Only use a platform driver as an absolute last resort.  I don't think
> > > that is the case here.
> >
> > The driver is trying to configure boot-swapping and display secure state,
> > and is defined/initiated in ACPI table in UEFI. It seems a little hard to
> > categorize this driver to any existing subsystem. Any suggestion
> > where it might be a better fit (like drivers/misc, drivers/firmware, etc)?
> > Please correct me if I misunderstand the comments. Thanks!.
> 
> The comment was asking why an acpi driver is a platform driver, but then
> I went and looked now at a bunch of acpi drivers, and they all are
> platform drivers :(
> 
> Anyway, drivers/acpi/ seems like the best place for this file, right?

My understanding is that the "drivers/acpi" is mainly for the acpi common code.
The vendor or platform specific drivers are spread in other various directories,
most of which are 'platform' drivers. 

For this driver, we didn't find better sub-component for it, thus put it under
'drivers/platform/mellanox' which is vendor specific driver by its name.

> 
> thanks,
> 
> greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v5 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-05-20 20:43         ` Liming Sun
@ 2019-05-21  7:58           ` Arnd Bergmann
  2019-05-22 15:12             ` Liming Sun
  0 siblings, 1 reply; 34+ messages in thread
From: Arnd Bergmann @ 2019-05-21  7:58 UTC (permalink / raw)
  To: Liming Sun
  Cc: Greg KH, Andy Shevchenko, Darren Hart, Vadim Pasternak,
	David Woods, platform-driver-x86, linux-kernel

On Mon, May 20, 2019 at 10:44 PM Liming Sun <lsun@mellanox.com> wrote:
> > -----Original Message-----
> > From: Greg KH <gregkh@linuxfoundation.org>
> > Sent: Monday, May 20, 2019 3:12 PM
> > To: Liming Sun <lsun@mellanox.com>
> > Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>; David
> > Woods <dwoods@mellanox.com>; platform-driver-x86@vger.kernel.org; linux-kernel@vger.kernel.org
> > Subject: Re: [PATCH v5 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
> >
> > On Mon, May 20, 2019 at 06:07:44PM +0000, Liming Sun wrote:
> > > > > +static struct platform_driver mlxbf_bootctl_driver = {
> > > > > +       .probe = mlxbf_bootctl_probe,
> > > > > +       .driver = {
> > > > > +               .name = "mlxbf-bootctl",
> > > > > +               .groups = mlxbf_bootctl_groups,
> > > > > +               .acpi_match_table = mlxbf_bootctl_acpi_ids,
> > > >
> > > > Why is an acpi driver a platform driver?  Isn't there a "real" acpi
> > > > driver interface you should be tieing into instead?
> > > >
> > > > Only use a platform driver as an absolute last resort.  I don't think
> > > > that is the case here.
> > >
> > > The driver is trying to configure boot-swapping and display secure state,
> > > and is defined/initiated in ACPI table in UEFI. It seems a little hard to
> > > categorize this driver to any existing subsystem. Any suggestion
> > > where it might be a better fit (like drivers/misc, drivers/firmware, etc)?
> > > Please correct me if I misunderstand the comments. Thanks!.
> >
> > The comment was asking why an acpi driver is a platform driver, but then
> > I went and looked now at a bunch of acpi drivers, and they all are
> > platform drivers :(
> >
> > Anyway, drivers/acpi/ seems like the best place for this file, right?
>
> My understanding is that the "drivers/acpi" is mainly for the acpi common code.
> The vendor or platform specific drivers are spread in other various directories,
> most of which are 'platform' drivers.

It depends on how closely you are following the acpi specification.
If you just implement access to a standard ACPI feature, or you have
added your interface to the ACPI specification, then the driver
should work on any system that supports this feature.

> For this driver, we didn't find better sub-component for it, thus put it under
> 'drivers/platform/mellanox' which is vendor specific driver by its name.

drivers/platform/mellanox/ would be a good place for drivers running on
a host platform with a bluefield accelerator card as an add-on, but as I
understand, this is a driver that actually just runs in Linux on the bluefield
itself, so it should go in a different place.

We use drivers/soc/ for things that are specific to one SoC, and that
are typically used by other drivers, but that don't have (and should not
have) a generic abstraction, which probably is not the case here either.

What we do have in drivers/power/reset is a couple of drivers that
set the "reboot reason", communicating that to the firmware for the
next boot, using the reboot_mode_register() interface. I don't
know too much about that interface, but maybe you can use that
instead of adding another sysfs interface?

If you have a complex firmware on the system that you can talk
to, there is also drivers/firmware/ as another option to put
abstractions into.

         Arnd

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v5 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-05-17 17:49 ` [PATCH v5 1/2] " Liming Sun
  2019-05-20 15:56   ` Greg KH
@ 2019-05-22 13:34   ` Mark Rutland
  2019-05-22 14:51     ` Liming Sun
  1 sibling, 1 reply; 34+ messages in thread
From: Mark Rutland @ 2019-05-22 13:34 UTC (permalink / raw)
  To: Liming Sun
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	platform-driver-x86, linux-kernel, ard.biesheuvel

Hi Liming,

On Fri, May 17, 2019 at 01:49:04PM -0400, Liming Sun wrote:
> This commit adds the bootctl platform driver for Mellanox BlueField
> Soc, which controls the eMMC boot partition swapping and sends SMC
> calls to ATF running at exception level EL3 to program some system
> register. This register is persistent during warm reset and is only
> accessible in secure code which is used to enable the watchdog after
> reboot.

I'm concerned by this. AFAICT this part must boot using EFI (in order
for the ACPI bits to work), and these interfaces significantly overlap
with or contradict the UEFI specification w.r.t. boot management.

How exactly does this SoC boot?

How do these interfaces interact with the regular UEFI bootflow?

Thanks,
Mark.

> 
> Below are the sequences of typical use case.
> 
>   1. User-space tool upgrades one eMMC boot partition and requests
>      the boot partition swapping;
> 
>   2. The bootctl driver handles this request and sends SMC call
>      to ATF. ATF programs register BREADCRUMB0 which has value
>      preserved during warm reset. It also programs eMMC to swap
>      the boot partition;
> 
>   3. After software reset (rebooting), ATF BL1 (BootRom) checks
>      register BREADCRUMB0 to enable watchdog if configured;
> 
>   4. If booting fails, the watchdog timer will trigger rebooting.
>      In such case, ATF Boot ROM will switch the boot partition
>      back to the previous one.
> 
> Reviewed-by: Vadim Pasternak <vadimp@mellanox.com>
> Signed-off-by: Liming Sun <lsun@mellanox.com>
> ---
> v4->v5:
>     Fixes for comments from Andy:
>     - Added ABI documentation;
>     - Remove the extra 'const' in mlxbf_bootctl_svc_uuid_str definition;
>     - Remove the mlxbf_bootctl_smc_call0() MACRO to use function
>       call directly;
>     - Return more descriptive string ('invalid action') in
>       mlxbf_bootctl_reset_action_to_string();
>     - Check return value of the mlxbf_bootctl_smc() in function
>       post_reset_wdog_show() and reset_action_show();
>     - Revise the sprintf() line in reset_action_show() into one line;
>     - Move the 'action = ' assignment out of the declarations
>       in reset_action_store() and check return value of
>       mlxbf_bootctl_reset_action_to_val() in this function;
>     - Removed Redundant parens in reset_action_store();
>     - Check return code of the smc_call in second_reset_action_show(),
>       merge the 'name = ' assignment into the sprintf() directly;
>     - Move the 'action = ' assignment out of the declaration block
>       in second_reset_action_store();
>     - Remove redundant blank line and parents in lifecycle_state_show();
>     - Split declaration and assignment in secure_boot_fuse_state_show();
>     - use BIT() in secure_boot_fuse_state_show() and simplify code in
>       this function to split the logic of message choice and flag flip;
>       Also removed the 'key !=0 ' check since it's not needed;
>     - Added comma in mlxbf_bootctl_attr_group definition.
>     - Removed un-needed '& 0xFF' logic in mlxbf_bootctl_guid_match();
>     - Use temp variable for the result of the smc call in
>       mlxbf_bootctl_probe();
>     - Use dev_warn() instead of dev_err() in mlxbf_bootctl_probe();
>     - Removed the ACPI_PTR usage in the mlxbf_bootctl_driver definition;
>     Fixes for comments from Vadim:
>     - Move mlxbf-bootctl.o to the top of the Makefile;
>     - Fix to use the 'ret' value instead of another mlxbf_bootctl_smc() call;
>     - Fix the 'declaration inside the block' in secure_boot_fuse_state_show();
>     - Use ATTRIBUTE_GROUPS() for the attribute definition;
>     - Use single line for a comment in mlxbf-bootctl.h
>     - Use KernelVersion 5.3 instead of 5.2.2 in the ABI doc;
>     - Use common utility function for the show and store of reset_action
>       and second_reset_action.
>     New changes:
>     - Rename mlxbf_bootctl_smc_call1() to mlxbf_bootctl_smc() to
>       shorten the name so some statement could be make into one line;
>     - Fixed the MODULE_LICENSE();
>     - Added more comments in secure_boot_fuse_state_show();
> v4: Update Kconfig for the dependency on ACPI.
>     Fixed a typo which caused build error for kernel module.
> v2->v3:
>     Fixes for comments from Andy:
>     - More coding style fixes;
>     - Revised the uuid matching code.
> v2: Fix the Kconfig and coding styles, propagate errors to caller.
> v1: Initial version.
> ---
>  drivers/platform/mellanox/Kconfig         |  12 ++
>  drivers/platform/mellanox/Makefile        |   1 +
>  drivers/platform/mellanox/mlxbf-bootctl.c | 311 ++++++++++++++++++++++++++++++
>  drivers/platform/mellanox/mlxbf-bootctl.h | 103 ++++++++++
>  4 files changed, 427 insertions(+)
>  create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.c
>  create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.h
> 
> diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig
> index 530fe7e..386336d 100644
> --- a/drivers/platform/mellanox/Kconfig
> +++ b/drivers/platform/mellanox/Kconfig
> @@ -44,4 +44,16 @@ config MLXBF_TMFIFO
>            platform driver support for the TmFifo which supports console
>            and networking based on the virtio framework.
>  
> +config MLXBF_BOOTCTL
> +	tristate "Mellanox BlueField Firmware Boot Control driver"
> +	depends on ARM64
> +	depends on ACPI
> +	help
> +          The Mellanox BlueField firmware implements functionality to
> +          request swapping the primary and alternate eMMC boot partition,
> +          and to set up a watchdog that can undo that swap if the system
> +          does not boot up correctly. This driver provides sysfs access
> +          to the userspace tools, to be used in conjunction with the eMMC
> +          device driver to do necessary initial swap of the boot partition.
> +
>  endif # MELLANOX_PLATFORM
> diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile
> index a229bda1..499623c 100644
> --- a/drivers/platform/mellanox/Makefile
> +++ b/drivers/platform/mellanox/Makefile
> @@ -3,6 +3,7 @@
>  # Makefile for linux/drivers/platform/mellanox
>  # Mellanox Platform-Specific Drivers
>  #
> +obj-$(CONFIG_MLXBF_BOOTCTL)	+= mlxbf-bootctl.o
>  obj-$(CONFIG_MLXBF_TMFIFO)	+= mlxbf-tmfifo.o
>  obj-$(CONFIG_MLXREG_HOTPLUG)	+= mlxreg-hotplug.o
>  obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o
> diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c
> new file mode 100644
> index 0000000..842b991
> --- /dev/null
> +++ b/drivers/platform/mellanox/mlxbf-bootctl.c
> @@ -0,0 +1,311 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Mellanox boot control driver
> + *
> + * This driver provides a sysfs interface for systems management
> + * software to manage reset-time actions.
> + *
> + * Copyright (C) 2019 Mellanox Technologies
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/arm-smccc.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 MLXBF_SB_KEY_NUM			4
> +
> +/* UUID used to probe ATF service. */
> +static const char *mlxbf_bootctl_svc_uuid_str =
> +	"89c036b4-e7d7-11e6-8797-001aca00bfc4";
> +
> +struct mlxbf_bootctl_name {
> +	u32 value;
> +	const char *name;
> +};
> +
> +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 const char * const mlxbf_bootctl_lifecycle_states[] = {
> +	[0] = "Production",
> +	[1] = "GA Secured",
> +	[2] = "GA Non-Secured",
> +	[3] = "RMA",
> +};
> +
> +/* ARM SMC call which is atomic and no need for lock. */
> +static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg)
> +{
> +	struct arm_smccc_res res;
> +
> +	arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res);
> +
> +	return res.a0;
> +}
> +
> +/* Return the action in integer or an error code. */
> +static int mlxbf_bootctl_reset_action_to_val(const char *action)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
> +		if (sysfs_streq(boot_names[i].name, action))
> +			return boot_names[i].value;
> +
> +	return -EINVAL;
> +}
> +
> +/* Return the action in string. */
> +static const char *mlxbf_bootctl_action_to_string(int action)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
> +		if (boot_names[i].value == action)
> +			return boot_names[i].name;
> +
> +	return "invalid action";
> +}
> +
> +static ssize_t post_reset_wdog_show(struct device_driver *drv, char *buf)
> +{
> +	int ret;
> +
> +	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_POST_RESET_WDOG, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	return sprintf(buf, "%d\n", ret);
> +}
> +
> +static ssize_t post_reset_wdog_store(struct device_driver *drv,
> +				     const char *buf, size_t count)
> +{
> +	unsigned long value;
> +	int ret;
> +
> +	ret = kstrtoul(buf, 10, &value);
> +	if (ret)
> +		return ret;
> +
> +	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_POST_RESET_WDOG, value);
> +	if (ret < 0)
> +		return ret;
> +
> +	return count;
> +}
> +
> +static ssize_t mlxbf_bootctl_show(int smc_op, char *buf)
> +{
> +	int action;
> +
> +	action = mlxbf_bootctl_smc(smc_op, 0);
> +	if (action < 0)
> +		return action;
> +
> +	return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action));
> +}
> +
> +static int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count)
> +{
> +	int ret, action;
> +
> +	action = mlxbf_bootctl_reset_action_to_val(buf);
> +	if (action < 0)
> +		return action;
> +
> +	ret = mlxbf_bootctl_smc(smc_op, action);
> +	if (ret < 0)
> +		return ret;
> +
> +	return count;
> +}
> +
> +static ssize_t reset_action_show(struct device_driver *drv, char *buf)
> +{
> +	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_RESET_ACTION, buf);
> +}
> +
> +static ssize_t reset_action_store(struct device_driver *drv,
> +				  const char *buf, size_t count)
> +{
> +	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_RESET_ACTION, buf, count);
> +}
> +
> +static ssize_t second_reset_action_show(struct device_driver *drv, char *buf)
> +{
> +	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION, buf);
> +}
> +
> +static ssize_t second_reset_action_store(struct device_driver *drv,
> +					 const char *buf, size_t count)
> +{
> +	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION, buf,
> +				   count);
> +}
> +
> +static ssize_t lifecycle_state_show(struct device_driver *drv, char *buf)
> +{
> +	int lc_state;
> +
> +	lc_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> +				     MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
> +	if (lc_state < 0)
> +		return lc_state;
> +
> +	lc_state &=
> +		MLXBF_BOOTCTL_SB_TEST_MASK | MLXBF_BOOTCTL_SB_SECURE_MASK;
> +
> +	/*
> +	 * 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;
> +
> +		return sprintf(buf, "%s(test)\n",
> +			       mlxbf_bootctl_lifecycle_states[lc_state]);
> +	}
> +
> +	return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
> +}
> +
> +static ssize_t secure_boot_fuse_state_show(struct device_driver *drv, char *buf)
> +{
> +	int burnt, valid, key, key_state, buf_len = 0, upper_key_used = 0;
> +	const char *status;
> +
> +	key_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> +				      MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
> +	if (key_state < 0)
> +		return key_state;
> +
> +	/*
> +	 * 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 progamming 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.
> +	 */
> +	for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
> +		burnt = key_state & BIT(key);
> +		valid = key_state & BIT(key + MLXBF_SB_KEY_NUM);
> +
> +		if (burnt && valid)
> +			upper_key_used = 1;
> +
> +		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";
> +		}
> +		buf_len += sprintf(buf + buf_len, "%d:%s ", key, status);
> +	}
> +	buf_len += sprintf(buf + buf_len, "\n");
> +
> +	return buf_len;
> +}
> +
> +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 struct attribute *mlxbf_bootctl_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,
> +	NULL
> +};
> +
> +ATTRIBUTE_GROUPS(mlxbf_bootctl);
> +
> +static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
> +	{"MLNXBF04", 0},
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
> +
> +static bool mlxbf_bootctl_guid_match(const guid_t *guid,
> +				     const struct arm_smccc_res *res)
> +{
> +	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);
> +
> +	return guid_equal(guid, &id);
> +}
> +
> +static int mlxbf_bootctl_probe(struct platform_device *pdev)
> +{
> +	struct arm_smccc_res res = { 0 };
> +	guid_t guid;
> +	int ret;
> +
> +	/* 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))
> +		return -ENODEV;
> +
> +	/*
> +	 * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_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");
> +
> +	return 0;
> +}
> +
> +static struct platform_driver mlxbf_bootctl_driver = {
> +	.probe = mlxbf_bootctl_probe,
> +	.driver = {
> +		.name = "mlxbf-bootctl",
> +		.groups = mlxbf_bootctl_groups,
> +		.acpi_match_table = mlxbf_bootctl_acpi_ids,
> +	}
> +};
> +
> +module_platform_driver(mlxbf_bootctl_driver);
> +
> +MODULE_DESCRIPTION("Mellanox boot control driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Mellanox Technologies");
> diff --git a/drivers/platform/mellanox/mlxbf-bootctl.h b/drivers/platform/mellanox/mlxbf-bootctl.h
> new file mode 100644
> index 0000000..148fdb4
> --- /dev/null
> +++ b/drivers/platform/mellanox/mlxbf-bootctl.h
> @@ -0,0 +1,103 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019, Mellanox Technologies. All rights reserved.
> + */
> +
> +#ifndef __MLXBF_BOOTCTL_H__
> +#define __MLXBF_BOOTCTL_H__
> +
> +/*
> + * Request that the on-chip watchdog be enabled, or disabled, after
> + * the next chip soft reset. This call does not affect the current
> + * status of the on-chip watchdog. If non-zero, the argument
> + * specifies the watchdog interval in seconds. If zero, the watchdog
> + * 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
> +
> +/*
> + * 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.
> + */
> +#define MLXBF_BOOTCTL_GET_POST_RESET_WDOG	0x82000001
> +
> +/*
> + * Request that a specific boot action be taken at the next soft
> + * reset. By default, the boot action is set by external chip pins,
> + * which are sampled on hard reset. Note that the boot action
> + * requested by this call will persist on subsequent resets unless
> + * this service, or the MLNX_SET_SECOND_RESET_ACTION service, is
> + * 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
> +
> +/*
> + * 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).
> + */
> +#define MLXBF_BOOTCTL_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
> + * 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
> + * 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.
> + * 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
> +
> +/*
> + * 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.
> + */
> +#define MLXBF_BOOTCTL_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
> +
> +/* Reset eMMC by programming the RST_N register. */
> +#define MLXBF_BOOTCTL_SET_EMMC_RST_N		0x82000007
> +
> +#define MLXBF_BOOTCTL_GET_DIMM_INFO		0x82000008
> +
> +/* 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
> +
> +/* Number of svc calls defined. */
> +#define MLXBF_BOOTCTL_NUM_SVC_CALLS 12
> +
> +/* 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 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 */
> +
> +/* Additional value to disable the MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION. */
> +#define MLXBF_BOOTCTL_NONE	0x7fffffff /* Don't change next boot action */
> +
> +#endif /* __MLXBF_BOOTCTL_H__ */
> -- 
> 1.8.3.1
> 

^ permalink raw reply	[flat|nested] 34+ messages in thread

* RE: [PATCH v5 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-05-22 13:34   ` Mark Rutland
@ 2019-05-22 14:51     ` Liming Sun
  0 siblings, 0 replies; 34+ messages in thread
From: Liming Sun @ 2019-05-22 14:51 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	platform-driver-x86, linux-kernel, ard.biesheuvel

Mark,

Thanks for the comments. I'll try to describe the SoC boot flow as below.

a) The SoC has a Boot ROM, which is ATF BL1; It's the entry point of HW reset.

b) The SoC has two eMMC boot partitions, which contains the rest of ATF images
  (BL2/BL31/...) and the UEFI image.  These two boot partitions are NOT for Linux
  or Linux file systems. Anything related to Linux are actually stored in eMMC user
  partitions (including the Linux 'boot' partition).

c) Depends on the eMMC configuration, one of the above boot partitions is
  primary, the other one is secondary.

d) During HW reset, HW will move the boot stream from the primary boot
  partition into some BOOT FIFO. The Boot ROM (ATF BL1) will poll the
  BOOT FIFO to load ATF BL2. Later on ATF BL2 will load ATF BL31 and
  the UEFI image. ATF images are loaded into SRAM and UEFI are loaded
  into DRAM.

e) The rest are all standard behavior once the logic is passed into UEFI.

The purpose of this commit is to control the above step b & c for safe upgrade
and fallback if anything bad happens. It's a lower level operation than UEFI.

The ACPI here is just used to auto-load the driver, nothing else. Once the driver
is loaded, it exposes sysfs to userspace tools. Essentially, all the driver does is
proxying the SMC calls into ATF, since user-space doesn't have the permission
to do SMC call itself.

Thanks,
Liming

> -----Original Message-----
> From: Mark Rutland <mark.rutland@arm.com>
> Sent: Wednesday, May 22, 2019 9:35 AM
> To: Liming Sun <lsun@mellanox.com>
> Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>; David
> Woods <dwoods@mellanox.com>; platform-driver-x86@vger.kernel.org; linux-kernel@vger.kernel.org; ard.biesheuvel@linaro.org
> Subject: Re: [PATCH v5 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
> 
> Hi Liming,
> 
> On Fri, May 17, 2019 at 01:49:04PM -0400, Liming Sun wrote:
> > This commit adds the bootctl platform driver for Mellanox BlueField
> > Soc, which controls the eMMC boot partition swapping and sends SMC
> > calls to ATF running at exception level EL3 to program some system
> > register. This register is persistent during warm reset and is only
> > accessible in secure code which is used to enable the watchdog after
> > reboot.
> 
> I'm concerned by this. AFAICT this part must boot using EFI (in order
> for the ACPI bits to work), and these interfaces significantly overlap
> with or contradict the UEFI specification w.r.t. boot management.
> 
> How exactly does this SoC boot?
> 
> How do these interfaces interact with the regular UEFI bootflow?
> 
> Thanks,
> Mark.
> 
> >
> > Below are the sequences of typical use case.
> >
> >   1. User-space tool upgrades one eMMC boot partition and requests
> >      the boot partition swapping;
> >
> >   2. The bootctl driver handles this request and sends SMC call
> >      to ATF. ATF programs register BREADCRUMB0 which has value
> >      preserved during warm reset. It also programs eMMC to swap
> >      the boot partition;
> >
> >   3. After software reset (rebooting), ATF BL1 (BootRom) checks
> >      register BREADCRUMB0 to enable watchdog if configured;
> >
> >   4. If booting fails, the watchdog timer will trigger rebooting.
> >      In such case, ATF Boot ROM will switch the boot partition
> >      back to the previous one.
> >
> > Reviewed-by: Vadim Pasternak <vadimp@mellanox.com>
> > Signed-off-by: Liming Sun <lsun@mellanox.com>
> > ---
> > v4->v5:
> >     Fixes for comments from Andy:
> >     - Added ABI documentation;
> >     - Remove the extra 'const' in mlxbf_bootctl_svc_uuid_str definition;
> >     - Remove the mlxbf_bootctl_smc_call0() MACRO to use function
> >       call directly;
> >     - Return more descriptive string ('invalid action') in
> >       mlxbf_bootctl_reset_action_to_string();
> >     - Check return value of the mlxbf_bootctl_smc() in function
> >       post_reset_wdog_show() and reset_action_show();
> >     - Revise the sprintf() line in reset_action_show() into one line;
> >     - Move the 'action = ' assignment out of the declarations
> >       in reset_action_store() and check return value of
> >       mlxbf_bootctl_reset_action_to_val() in this function;
> >     - Removed Redundant parens in reset_action_store();
> >     - Check return code of the smc_call in second_reset_action_show(),
> >       merge the 'name = ' assignment into the sprintf() directly;
> >     - Move the 'action = ' assignment out of the declaration block
> >       in second_reset_action_store();
> >     - Remove redundant blank line and parents in lifecycle_state_show();
> >     - Split declaration and assignment in secure_boot_fuse_state_show();
> >     - use BIT() in secure_boot_fuse_state_show() and simplify code in
> >       this function to split the logic of message choice and flag flip;
> >       Also removed the 'key !=0 ' check since it's not needed;
> >     - Added comma in mlxbf_bootctl_attr_group definition.
> >     - Removed un-needed '& 0xFF' logic in mlxbf_bootctl_guid_match();
> >     - Use temp variable for the result of the smc call in
> >       mlxbf_bootctl_probe();
> >     - Use dev_warn() instead of dev_err() in mlxbf_bootctl_probe();
> >     - Removed the ACPI_PTR usage in the mlxbf_bootctl_driver definition;
> >     Fixes for comments from Vadim:
> >     - Move mlxbf-bootctl.o to the top of the Makefile;
> >     - Fix to use the 'ret' value instead of another mlxbf_bootctl_smc() call;
> >     - Fix the 'declaration inside the block' in secure_boot_fuse_state_show();
> >     - Use ATTRIBUTE_GROUPS() for the attribute definition;
> >     - Use single line for a comment in mlxbf-bootctl.h
> >     - Use KernelVersion 5.3 instead of 5.2.2 in the ABI doc;
> >     - Use common utility function for the show and store of reset_action
> >       and second_reset_action.
> >     New changes:
> >     - Rename mlxbf_bootctl_smc_call1() to mlxbf_bootctl_smc() to
> >       shorten the name so some statement could be make into one line;
> >     - Fixed the MODULE_LICENSE();
> >     - Added more comments in secure_boot_fuse_state_show();
> > v4: Update Kconfig for the dependency on ACPI.
> >     Fixed a typo which caused build error for kernel module.
> > v2->v3:
> >     Fixes for comments from Andy:
> >     - More coding style fixes;
> >     - Revised the uuid matching code.
> > v2: Fix the Kconfig and coding styles, propagate errors to caller.
> > v1: Initial version.
> > ---
> >  drivers/platform/mellanox/Kconfig         |  12 ++
> >  drivers/platform/mellanox/Makefile        |   1 +
> >  drivers/platform/mellanox/mlxbf-bootctl.c | 311 ++++++++++++++++++++++++++++++
> >  drivers/platform/mellanox/mlxbf-bootctl.h | 103 ++++++++++
> >  4 files changed, 427 insertions(+)
> >  create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.c
> >  create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.h
> >
> > diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig
> > index 530fe7e..386336d 100644
> > --- a/drivers/platform/mellanox/Kconfig
> > +++ b/drivers/platform/mellanox/Kconfig
> > @@ -44,4 +44,16 @@ config MLXBF_TMFIFO
> >            platform driver support for the TmFifo which supports console
> >            and networking based on the virtio framework.
> >
> > +config MLXBF_BOOTCTL
> > +	tristate "Mellanox BlueField Firmware Boot Control driver"
> > +	depends on ARM64
> > +	depends on ACPI
> > +	help
> > +          The Mellanox BlueField firmware implements functionality to
> > +          request swapping the primary and alternate eMMC boot partition,
> > +          and to set up a watchdog that can undo that swap if the system
> > +          does not boot up correctly. This driver provides sysfs access
> > +          to the userspace tools, to be used in conjunction with the eMMC
> > +          device driver to do necessary initial swap of the boot partition.
> > +
> >  endif # MELLANOX_PLATFORM
> > diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile
> > index a229bda1..499623c 100644
> > --- a/drivers/platform/mellanox/Makefile
> > +++ b/drivers/platform/mellanox/Makefile
> > @@ -3,6 +3,7 @@
> >  # Makefile for linux/drivers/platform/mellanox
> >  # Mellanox Platform-Specific Drivers
> >  #
> > +obj-$(CONFIG_MLXBF_BOOTCTL)	+= mlxbf-bootctl.o
> >  obj-$(CONFIG_MLXBF_TMFIFO)	+= mlxbf-tmfifo.o
> >  obj-$(CONFIG_MLXREG_HOTPLUG)	+= mlxreg-hotplug.o
> >  obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o
> > diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c
> > new file mode 100644
> > index 0000000..842b991
> > --- /dev/null
> > +++ b/drivers/platform/mellanox/mlxbf-bootctl.c
> > @@ -0,0 +1,311 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Mellanox boot control driver
> > + *
> > + * This driver provides a sysfs interface for systems management
> > + * software to manage reset-time actions.
> > + *
> > + * Copyright (C) 2019 Mellanox Technologies
> > + */
> > +
> > +#include <linux/acpi.h>
> > +#include <linux/arm-smccc.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 MLXBF_SB_KEY_NUM			4
> > +
> > +/* UUID used to probe ATF service. */
> > +static const char *mlxbf_bootctl_svc_uuid_str =
> > +	"89c036b4-e7d7-11e6-8797-001aca00bfc4";
> > +
> > +struct mlxbf_bootctl_name {
> > +	u32 value;
> > +	const char *name;
> > +};
> > +
> > +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 const char * const mlxbf_bootctl_lifecycle_states[] = {
> > +	[0] = "Production",
> > +	[1] = "GA Secured",
> > +	[2] = "GA Non-Secured",
> > +	[3] = "RMA",
> > +};
> > +
> > +/* ARM SMC call which is atomic and no need for lock. */
> > +static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg)
> > +{
> > +	struct arm_smccc_res res;
> > +
> > +	arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res);
> > +
> > +	return res.a0;
> > +}
> > +
> > +/* Return the action in integer or an error code. */
> > +static int mlxbf_bootctl_reset_action_to_val(const char *action)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
> > +		if (sysfs_streq(boot_names[i].name, action))
> > +			return boot_names[i].value;
> > +
> > +	return -EINVAL;
> > +}
> > +
> > +/* Return the action in string. */
> > +static const char *mlxbf_bootctl_action_to_string(int action)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
> > +		if (boot_names[i].value == action)
> > +			return boot_names[i].name;
> > +
> > +	return "invalid action";
> > +}
> > +
> > +static ssize_t post_reset_wdog_show(struct device_driver *drv, char *buf)
> > +{
> > +	int ret;
> > +
> > +	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_POST_RESET_WDOG, 0);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	return sprintf(buf, "%d\n", ret);
> > +}
> > +
> > +static ssize_t post_reset_wdog_store(struct device_driver *drv,
> > +				     const char *buf, size_t count)
> > +{
> > +	unsigned long value;
> > +	int ret;
> > +
> > +	ret = kstrtoul(buf, 10, &value);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_POST_RESET_WDOG, value);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	return count;
> > +}
> > +
> > +static ssize_t mlxbf_bootctl_show(int smc_op, char *buf)
> > +{
> > +	int action;
> > +
> > +	action = mlxbf_bootctl_smc(smc_op, 0);
> > +	if (action < 0)
> > +		return action;
> > +
> > +	return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action));
> > +}
> > +
> > +static int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count)
> > +{
> > +	int ret, action;
> > +
> > +	action = mlxbf_bootctl_reset_action_to_val(buf);
> > +	if (action < 0)
> > +		return action;
> > +
> > +	ret = mlxbf_bootctl_smc(smc_op, action);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	return count;
> > +}
> > +
> > +static ssize_t reset_action_show(struct device_driver *drv, char *buf)
> > +{
> > +	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_RESET_ACTION, buf);
> > +}
> > +
> > +static ssize_t reset_action_store(struct device_driver *drv,
> > +				  const char *buf, size_t count)
> > +{
> > +	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_RESET_ACTION, buf, count);
> > +}
> > +
> > +static ssize_t second_reset_action_show(struct device_driver *drv, char *buf)
> > +{
> > +	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION, buf);
> > +}
> > +
> > +static ssize_t second_reset_action_store(struct device_driver *drv,
> > +					 const char *buf, size_t count)
> > +{
> > +	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION, buf,
> > +				   count);
> > +}
> > +
> > +static ssize_t lifecycle_state_show(struct device_driver *drv, char *buf)
> > +{
> > +	int lc_state;
> > +
> > +	lc_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> > +				     MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
> > +	if (lc_state < 0)
> > +		return lc_state;
> > +
> > +	lc_state &=
> > +		MLXBF_BOOTCTL_SB_TEST_MASK | MLXBF_BOOTCTL_SB_SECURE_MASK;
> > +
> > +	/*
> > +	 * 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;
> > +
> > +		return sprintf(buf, "%s(test)\n",
> > +			       mlxbf_bootctl_lifecycle_states[lc_state]);
> > +	}
> > +
> > +	return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
> > +}
> > +
> > +static ssize_t secure_boot_fuse_state_show(struct device_driver *drv, char *buf)
> > +{
> > +	int burnt, valid, key, key_state, buf_len = 0, upper_key_used = 0;
> > +	const char *status;
> > +
> > +	key_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
> > +				      MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
> > +	if (key_state < 0)
> > +		return key_state;
> > +
> > +	/*
> > +	 * 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 progamming 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.
> > +	 */
> > +	for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
> > +		burnt = key_state & BIT(key);
> > +		valid = key_state & BIT(key + MLXBF_SB_KEY_NUM);
> > +
> > +		if (burnt && valid)
> > +			upper_key_used = 1;
> > +
> > +		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";
> > +		}
> > +		buf_len += sprintf(buf + buf_len, "%d:%s ", key, status);
> > +	}
> > +	buf_len += sprintf(buf + buf_len, "\n");
> > +
> > +	return buf_len;
> > +}
> > +
> > +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 struct attribute *mlxbf_bootctl_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,
> > +	NULL
> > +};
> > +
> > +ATTRIBUTE_GROUPS(mlxbf_bootctl);
> > +
> > +static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
> > +	{"MLNXBF04", 0},
> > +	{}
> > +};
> > +
> > +MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
> > +
> > +static bool mlxbf_bootctl_guid_match(const guid_t *guid,
> > +				     const struct arm_smccc_res *res)
> > +{
> > +	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);
> > +
> > +	return guid_equal(guid, &id);
> > +}
> > +
> > +static int mlxbf_bootctl_probe(struct platform_device *pdev)
> > +{
> > +	struct arm_smccc_res res = { 0 };
> > +	guid_t guid;
> > +	int ret;
> > +
> > +	/* 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))
> > +		return -ENODEV;
> > +
> > +	/*
> > +	 * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_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");
> > +
> > +	return 0;
> > +}
> > +
> > +static struct platform_driver mlxbf_bootctl_driver = {
> > +	.probe = mlxbf_bootctl_probe,
> > +	.driver = {
> > +		.name = "mlxbf-bootctl",
> > +		.groups = mlxbf_bootctl_groups,
> > +		.acpi_match_table = mlxbf_bootctl_acpi_ids,
> > +	}
> > +};
> > +
> > +module_platform_driver(mlxbf_bootctl_driver);
> > +
> > +MODULE_DESCRIPTION("Mellanox boot control driver");
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_AUTHOR("Mellanox Technologies");
> > diff --git a/drivers/platform/mellanox/mlxbf-bootctl.h b/drivers/platform/mellanox/mlxbf-bootctl.h
> > new file mode 100644
> > index 0000000..148fdb4
> > --- /dev/null
> > +++ b/drivers/platform/mellanox/mlxbf-bootctl.h
> > @@ -0,0 +1,103 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (c) 2019, Mellanox Technologies. All rights reserved.
> > + */
> > +
> > +#ifndef __MLXBF_BOOTCTL_H__
> > +#define __MLXBF_BOOTCTL_H__
> > +
> > +/*
> > + * Request that the on-chip watchdog be enabled, or disabled, after
> > + * the next chip soft reset. This call does not affect the current
> > + * status of the on-chip watchdog. If non-zero, the argument
> > + * specifies the watchdog interval in seconds. If zero, the watchdog
> > + * 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
> > +
> > +/*
> > + * 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.
> > + */
> > +#define MLXBF_BOOTCTL_GET_POST_RESET_WDOG	0x82000001
> > +
> > +/*
> > + * Request that a specific boot action be taken at the next soft
> > + * reset. By default, the boot action is set by external chip pins,
> > + * which are sampled on hard reset. Note that the boot action
> > + * requested by this call will persist on subsequent resets unless
> > + * this service, or the MLNX_SET_SECOND_RESET_ACTION service, is
> > + * 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
> > +
> > +/*
> > + * 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).
> > + */
> > +#define MLXBF_BOOTCTL_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
> > + * 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
> > + * 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.
> > + * 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
> > +
> > +/*
> > + * 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.
> > + */
> > +#define MLXBF_BOOTCTL_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
> > +
> > +/* Reset eMMC by programming the RST_N register. */
> > +#define MLXBF_BOOTCTL_SET_EMMC_RST_N		0x82000007
> > +
> > +#define MLXBF_BOOTCTL_GET_DIMM_INFO		0x82000008
> > +
> > +/* 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
> > +
> > +/* Number of svc calls defined. */
> > +#define MLXBF_BOOTCTL_NUM_SVC_CALLS 12
> > +
> > +/* 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 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 */
> > +
> > +/* Additional value to disable the MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION. */
> > +#define MLXBF_BOOTCTL_NONE	0x7fffffff /* Don't change next boot action */
> > +
> > +#endif /* __MLXBF_BOOTCTL_H__ */
> > --
> > 1.8.3.1
> >

^ permalink raw reply	[flat|nested] 34+ messages in thread

* RE: [PATCH v5 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-05-21  7:58           ` Arnd Bergmann
@ 2019-05-22 15:12             ` Liming Sun
  0 siblings, 0 replies; 34+ messages in thread
From: Liming Sun @ 2019-05-22 15:12 UTC (permalink / raw)
  To: Arnd Bergmann, Mark Rutland
  Cc: Greg KH, Andy Shevchenko, Darren Hart, Vadim Pasternak,
	David Woods, platform-driver-x86, linux-kernel

Thanks Arnd for the comments! Please also see my response below.

> -----Original Message-----
> From: Arnd Bergmann <arnd@arndb.de>
> Sent: Tuesday, May 21, 2019 3:59 AM
> To: Liming Sun <lsun@mellanox.com>
> Cc: Greg KH <gregkh@linuxfoundation.org>; Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim
> Pasternak <vadimp@mellanox.com>; David Woods <dwoods@mellanox.com>; platform-driver-x86@vger.kernel.org; linux-
> kernel@vger.kernel.org
> Subject: Re: [PATCH v5 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
> 
> On Mon, May 20, 2019 at 10:44 PM Liming Sun <lsun@mellanox.com> wrote:
> > > -----Original Message-----
> > > From: Greg KH <gregkh@linuxfoundation.org>
> > > Sent: Monday, May 20, 2019 3:12 PM
> > > To: Liming Sun <lsun@mellanox.com>
> > > Cc: Andy Shevchenko <andy@infradead.org>; Darren Hart <dvhart@infradead.org>; Vadim Pasternak <vadimp@mellanox.com>;
> David
> > > Woods <dwoods@mellanox.com>; platform-driver-x86@vger.kernel.org; linux-kernel@vger.kernel.org
> > > Subject: Re: [PATCH v5 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
> > >
> > > On Mon, May 20, 2019 at 06:07:44PM +0000, Liming Sun wrote:
> > > > > > +static struct platform_driver mlxbf_bootctl_driver = {
> > > > > > +       .probe = mlxbf_bootctl_probe,
> > > > > > +       .driver = {
> > > > > > +               .name = "mlxbf-bootctl",
> > > > > > +               .groups = mlxbf_bootctl_groups,
> > > > > > +               .acpi_match_table = mlxbf_bootctl_acpi_ids,
> > > > >
> > > > > Why is an acpi driver a platform driver?  Isn't there a "real" acpi
> > > > > driver interface you should be tieing into instead?
> > > > >
> > > > > Only use a platform driver as an absolute last resort.  I don't think
> > > > > that is the case here.
> > > >
> > > > The driver is trying to configure boot-swapping and display secure state,
> > > > and is defined/initiated in ACPI table in UEFI. It seems a little hard to
> > > > categorize this driver to any existing subsystem. Any suggestion
> > > > where it might be a better fit (like drivers/misc, drivers/firmware, etc)?
> > > > Please correct me if I misunderstand the comments. Thanks!.
> > >
> > > The comment was asking why an acpi driver is a platform driver, but then
> > > I went and looked now at a bunch of acpi drivers, and they all are
> > > platform drivers :(
> > >
> > > Anyway, drivers/acpi/ seems like the best place for this file, right?
> >
> > My understanding is that the "drivers/acpi" is mainly for the acpi common code.
> > The vendor or platform specific drivers are spread in other various directories,
> > most of which are 'platform' drivers.
> 
> It depends on how closely you are following the acpi specification.
> If you just implement access to a standard ACPI feature, or you have
> added your interface to the ACPI specification, then the driver
> should work on any system that supports this feature.
> 
> > For this driver, we didn't find better sub-component for it, thus put it under
> > 'drivers/platform/mellanox' which is vendor specific driver by its name.
> 
> drivers/platform/mellanox/ would be a good place for drivers running on
> a host platform with a bluefield accelerator card as an add-on, but as I
> understand, this is a driver that actually just runs in Linux on the bluefield
> itself, so it should go in a different place.

Yes, the driver is actually running on the bluefield itself.
So looks like we'll need to find another location for it.

> 
> We use drivers/soc/ for things that are specific to one SoC, and that
> are typically used by other drivers, but that don't have (and should not
> have) a generic abstraction, which probably is not the case here either.

I did a 'grep' for 'module_platform_driver' and 'DEVICE_ATTR' under
drivers/soc, and found quite some under drivers/soc. This 'bootctl' driver
is SoC specific and tries to control the low-level boot partition (not
Linux related). Please also see my response to Mark's comments about
the SoC boot flow. Probably the 'drivers/soc' would be better fit for
this driver? Please advise.

> 
> What we do have in drivers/power/reset is a couple of drivers that
> set the "reboot reason", communicating that to the firmware for the
> next boot, using the reboot_mode_register() interface. I don't
> know too much about that interface, but maybe you can use that
> instead of adding another sysfs interface?

I checked the history of the 'reboot_mode'. Looks like it tries to
control how Linux boots, which is different than what this commit
does. This commit tries to control how ATF/UEFI boots, not how
Linux boots.

> 
> If you have a complex firmware on the system that you can talk
> to, there is also drivers/firmware/ as another option to put
> abstractions into.

I feel like 'drivers/soc' migh be better fit. We could certainly try
drivers/firmware/ and abstractions if you think 'drivers/soc' is not 
a good option.

> 
>          Arnd

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH v6 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc
  2019-01-29 20:59 [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc Liming Sun
                   ` (6 preceding siblings ...)
  2019-05-17 17:49 ` [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions Liming Sun
@ 2019-10-07 15:48 ` Liming Sun
  2019-10-07 15:48 ` [PATCH v6 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions Liming Sun
  8 siblings, 0 replies; 34+ messages in thread
From: Liming Sun @ 2019-10-07 15:48 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods,
	Mark Rutland, Arnd Bergmann
  Cc: Liming Sun, platform-driver-x86, linux-kernel

This commit adds the bootctl platform driver for Mellanox BlueField
Soc, which queries secure state and controls the eMMC boot partition
swapping by sending SMC calls to ATF running at EL3.

Below are the sequences of typical use case.

  1. User requests boot partition swapping, which could be on-demand or
     during boot-image upgrade via UEFI capsule;

  2. This bootctl driver handles the request and sends SMC call
     to ATF. ATF programs register BREADCRUMB0 which has value
     preserved during warm reset. It also programs eMMC to swap
     the boot partition;

  3. After software reset (rebooting), ATF BL1 (BootRom) checks
     register BREADCRUMB0 and enable watchdog if configured;

  4. If booting fails, the watchdog timer will trigger rebooting.
     In such case, ATF Boot ROM will switch the boot partition
     back to the previous one. This is a robust feature and used
     to prevent failure during boot partition upgrade.

Reviewed-by: Vadim Pasternak <vadimp@mellanox.com>
Signed-off-by: Liming Sun <lsun@mellanox.com>
---
v5->v6:
    Fixes for comments from Mark:
    - Changed to use device attributes (DEVICE_ATTR_xx) instead of
      driver attributes (DRIVER_ATTR_xx) for the exported sysfs
      attributes.
    Comments from Arnd:
    - "drivers/platform/mellanox/ would be a good place for drivers
    running on a host platform with a bluefield accelerator card as
    an add-on, but as I understand, this is a driver that actually
    just runs in Linux on the bluefield itself, so it should go in a
    different place."
    It seems hard to find another better place to move this code. This
    directory name appears like a natual fit (if we don't want to use
    new directory drivers/soc/mellanox instead). Also it is guarded by
    'depends on ARM64' in the Kconfig. So I leave it here for now
    unless we have strong opinion to move it to other places.
v4->v5:
    Fixes for comments from Andy:
    - Added ABI documentation;
    - Remove the extra 'const' in mlxbf_bootctl_svc_uuid_str definition;
    - Remove the mlxbf_bootctl_smc_call0() MACRO to use function
      call directly;
    - Return more descriptive string ('invalid action') in
      mlxbf_bootctl_reset_action_to_string();
    - Check return value of the mlxbf_bootctl_smc() in function
      post_reset_wdog_show() and reset_action_show();
    - Revise the sprintf() line in reset_action_show() into one line;
    - Move the 'action = ' assignment out of the declarations
      in reset_action_store() and check return value of
      mlxbf_bootctl_reset_action_to_val() in this function;
    - Removed Redundant parens in reset_action_store();
    - Check return code of the smc_call in second_reset_action_show(),
      merge the 'name = ' assignment into the sprintf() directly;
    - Move the 'action = ' assignment out of the declaration block
      in second_reset_action_store();
    - Remove redundant blank line and parents in lifecycle_state_show();
    - Split declaration and assignment in secure_boot_fuse_state_show();
    - use BIT() in secure_boot_fuse_state_show() and simplify code in
      this function to split the logic of message choice and flag flip;
      Also removed the 'key !=0 ' check since it's not needed;
    - Added comma in mlxbf_bootctl_attr_group definition.
    - Removed un-needed '& 0xFF' logic in mlxbf_bootctl_guid_match();
    - Use temp variable for the result of the smc call in
      mlxbf_bootctl_probe();
    - Use dev_warn() instead of dev_err() in mlxbf_bootctl_probe();
    - Removed the ACPI_PTR usage in the mlxbf_bootctl_driver definition;
    Fixes for comments from Vadim:
    - Move mlxbf-bootctl.o to the top of the Makefile;
    - Fix to use the 'ret' value instead of another mlxbf_bootctl_smc() call;
    - Fix the 'declaration inside the block' in secure_boot_fuse_state_show();
    - Use ATTRIBUTE_GROUPS() for the attribute definition;
    - Use single line for a comment in mlxbf-bootctl.h
    - Use KernelVersion 5.3 instead of 5.2.2 in the ABI doc;
    - Use common utility function for the show and store of reset_action
      and second_reset_action.
    New changes:
    - Rename mlxbf_bootctl_smc_call1() to mlxbf_bootctl_smc() to
      shorten the name so some statement could be make into one line;
    - Fixed the MODULE_LICENSE();
    - Added more comments in secure_boot_fuse_state_show();
v4: Update Kconfig for the dependency on ACPI.
    Fixed a typo which caused build error for kernel module.
v2->v3:
    Fixes for comments from Andy:
    - More coding style fixes;
    - Revised the uuid matching code.
v2: Fix the Kconfig and coding styles, propagate errors to caller.
v1: Initial version.
---
 drivers/platform/mellanox/Kconfig         |  12 ++
 drivers/platform/mellanox/Makefile        |   1 +
 drivers/platform/mellanox/mlxbf-bootctl.c | 321 ++++++++++++++++++++++++++++++
 drivers/platform/mellanox/mlxbf-bootctl.h | 103 ++++++++++
 4 files changed, 437 insertions(+)
 create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.c
 create mode 100644 drivers/platform/mellanox/mlxbf-bootctl.h

diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig
index 530fe7e..386336d 100644
--- a/drivers/platform/mellanox/Kconfig
+++ b/drivers/platform/mellanox/Kconfig
@@ -44,4 +44,16 @@ config MLXBF_TMFIFO
           platform driver support for the TmFifo which supports console
           and networking based on the virtio framework.
 
+config MLXBF_BOOTCTL
+	tristate "Mellanox BlueField Firmware Boot Control driver"
+	depends on ARM64
+	depends on ACPI
+	help
+          The Mellanox BlueField firmware implements functionality to
+          request swapping the primary and alternate eMMC boot partition,
+          and to set up a watchdog that can undo that swap if the system
+          does not boot up correctly. This driver provides sysfs access
+          to the userspace tools, to be used in conjunction with the eMMC
+          device driver to do necessary initial swap of the boot partition.
+
 endif # MELLANOX_PLATFORM
diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile
index a229bda1..499623c 100644
--- a/drivers/platform/mellanox/Makefile
+++ b/drivers/platform/mellanox/Makefile
@@ -3,6 +3,7 @@
 # Makefile for linux/drivers/platform/mellanox
 # Mellanox Platform-Specific Drivers
 #
+obj-$(CONFIG_MLXBF_BOOTCTL)	+= mlxbf-bootctl.o
 obj-$(CONFIG_MLXBF_TMFIFO)	+= mlxbf-tmfifo.o
 obj-$(CONFIG_MLXREG_HOTPLUG)	+= mlxreg-hotplug.o
 obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c
new file mode 100644
index 0000000..61753b6
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf-bootctl.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Mellanox boot control driver
+ *
+ * This driver provides a sysfs interface for systems management
+ * software to manage reset-time actions.
+ *
+ * Copyright (C) 2019 Mellanox Technologies
+ */
+
+#include <linux/acpi.h>
+#include <linux/arm-smccc.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 MLXBF_SB_KEY_NUM			4
+
+/* UUID used to probe ATF service. */
+static const char *mlxbf_bootctl_svc_uuid_str =
+	"89c036b4-e7d7-11e6-8797-001aca00bfc4";
+
+struct mlxbf_bootctl_name {
+	u32 value;
+	const char *name;
+};
+
+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 const char * const mlxbf_bootctl_lifecycle_states[] = {
+	[0] = "Production",
+	[1] = "GA Secured",
+	[2] = "GA Non-Secured",
+	[3] = "RMA",
+};
+
+/* ARM SMC call which is atomic and no need for lock. */
+static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res);
+
+	return res.a0;
+}
+
+/* Return the action in integer or an error code. */
+static int mlxbf_bootctl_reset_action_to_val(const char *action)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
+		if (sysfs_streq(boot_names[i].name, action))
+			return boot_names[i].value;
+
+	return -EINVAL;
+}
+
+/* Return the action in string. */
+static const char *mlxbf_bootctl_action_to_string(int action)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
+		if (boot_names[i].value == action)
+			return boot_names[i].name;
+
+	return "invalid action";
+}
+
+static ssize_t post_reset_wdog_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_POST_RESET_WDOG, 0);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t post_reset_wdog_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &value);
+	if (ret)
+		return ret;
+
+	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_POST_RESET_WDOG, value);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t mlxbf_bootctl_show(int smc_op, char *buf)
+{
+	int action;
+
+	action = mlxbf_bootctl_smc(smc_op, 0);
+	if (action < 0)
+		return action;
+
+	return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action));
+}
+
+static int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count)
+{
+	int ret, action;
+
+	action = mlxbf_bootctl_reset_action_to_val(buf);
+	if (action < 0)
+		return action;
+
+	ret = mlxbf_bootctl_smc(smc_op, action);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t reset_action_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_RESET_ACTION, buf);
+}
+
+static ssize_t reset_action_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_RESET_ACTION, buf, count);
+}
+
+static ssize_t second_reset_action_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION, buf);
+}
+
+static ssize_t second_reset_action_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION, buf,
+				   count);
+}
+
+static ssize_t lifecycle_state_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	int lc_state;
+
+	lc_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
+				     MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
+	if (lc_state < 0)
+		return lc_state;
+
+	lc_state &=
+		MLXBF_BOOTCTL_SB_TEST_MASK | MLXBF_BOOTCTL_SB_SECURE_MASK;
+
+	/*
+	 * 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;
+
+		return sprintf(buf, "%s(test)\n",
+			       mlxbf_bootctl_lifecycle_states[lc_state]);
+	}
+
+	return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
+}
+
+static ssize_t secure_boot_fuse_state_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	int burnt, valid, key, key_state, buf_len = 0, upper_key_used = 0;
+	const char *status;
+
+	key_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
+				      MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
+	if (key_state < 0)
+		return key_state;
+
+	/*
+	 * 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 progamming 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.
+	 */
+	for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
+		burnt = key_state & BIT(key);
+		valid = key_state & BIT(key + MLXBF_SB_KEY_NUM);
+
+		if (burnt && valid)
+			upper_key_used = 1;
+
+		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";
+		}
+		buf_len += sprintf(buf + buf_len, "%d:%s ", key, status);
+	}
+	buf_len += sprintf(buf + buf_len, "\n");
+
+	return buf_len;
+}
+
+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 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,
+	NULL
+};
+
+ATTRIBUTE_GROUPS(mlxbf_bootctl);
+
+static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
+	{"MLNXBF04", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
+
+static bool mlxbf_bootctl_guid_match(const guid_t *guid,
+				     const struct arm_smccc_res *res)
+{
+	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);
+
+	return guid_equal(guid, &id);
+}
+
+static int mlxbf_bootctl_probe(struct platform_device *pdev)
+{
+	struct arm_smccc_res res = { 0 };
+	guid_t guid;
+	int ret;
+
+	/* 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))
+		return -ENODEV;
+
+	/*
+	 * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_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");
+
+	return 0;
+}
+
+static struct platform_driver mlxbf_bootctl_driver = {
+	.probe = mlxbf_bootctl_probe,
+	.driver = {
+		.name = "mlxbf-bootctl",
+		.groups = mlxbf_bootctl_groups,
+		.acpi_match_table = mlxbf_bootctl_acpi_ids,
+	}
+};
+
+module_platform_driver(mlxbf_bootctl_driver);
+
+MODULE_DESCRIPTION("Mellanox boot control driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Mellanox Technologies");
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.h b/drivers/platform/mellanox/mlxbf-bootctl.h
new file mode 100644
index 0000000..148fdb4
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf-bootctl.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019, Mellanox Technologies. All rights reserved.
+ */
+
+#ifndef __MLXBF_BOOTCTL_H__
+#define __MLXBF_BOOTCTL_H__
+
+/*
+ * Request that the on-chip watchdog be enabled, or disabled, after
+ * the next chip soft reset. This call does not affect the current
+ * status of the on-chip watchdog. If non-zero, the argument
+ * specifies the watchdog interval in seconds. If zero, the watchdog
+ * 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
+
+/*
+ * 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.
+ */
+#define MLXBF_BOOTCTL_GET_POST_RESET_WDOG	0x82000001
+
+/*
+ * Request that a specific boot action be taken at the next soft
+ * reset. By default, the boot action is set by external chip pins,
+ * which are sampled on hard reset. Note that the boot action
+ * requested by this call will persist on subsequent resets unless
+ * this service, or the MLNX_SET_SECOND_RESET_ACTION service, is
+ * 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
+
+/*
+ * 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).
+ */
+#define MLXBF_BOOTCTL_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
+ * 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
+ * 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.
+ * 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
+
+/*
+ * 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.
+ */
+#define MLXBF_BOOTCTL_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
+
+/* Reset eMMC by programming the RST_N register. */
+#define MLXBF_BOOTCTL_SET_EMMC_RST_N		0x82000007
+
+#define MLXBF_BOOTCTL_GET_DIMM_INFO		0x82000008
+
+/* 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
+
+/* Number of svc calls defined. */
+#define MLXBF_BOOTCTL_NUM_SVC_CALLS 12
+
+/* 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 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 */
+
+/* Additional value to disable the MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION. */
+#define MLXBF_BOOTCTL_NONE	0x7fffffff /* Don't change next boot action */
+
+#endif /* __MLXBF_BOOTCTL_H__ */
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v6 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions
  2019-01-29 20:59 [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc Liming Sun
                   ` (7 preceding siblings ...)
  2019-10-07 15:48 ` [PATCH v6 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc Liming Sun
@ 2019-10-07 15:48 ` Liming Sun
  8 siblings, 0 replies; 34+ messages in thread
From: Liming Sun @ 2019-10-07 15:48 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Vadim Pasternak, David Woods, Mark Rutland
  Cc: Liming Sun, platform-driver-x86, linux-kernel, David S. Miller,
	Mauro Carvalho Chehab, Greg Kroah-Hartman, Jonathan Cameron,
	Nicolas Ferre, Paul E. McKenney

This commit adds the ABI definitions exposed to userspace for
the platform/mellanox/mlxbf-bootctl driver.

Reviewed-by: Vadim Pasternak <vadimp@mellanox.com>
Signed-off-by: Liming Sun <lsun@mellanox.com>
---
v5->v6:
    Fixes for comments from Mark:
    - Changed to use device attributes (DEVICE_ATTR_xx) instead of
      driver attributes (DRIVER_ATTR_xx) for the exported sysfs
      attributes.
---
 .../ABI/testing/sysfs-platform-mellanox-bootctl    | 58 ++++++++++++++++++++++
 MAINTAINERS                                        |  1 +
 2 files changed, 59 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-platform-mellanox-bootctl

diff --git a/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl b/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
new file mode 100644
index 0000000..b60a46e
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
@@ -0,0 +1,58 @@
+What:		/sys/bus/platform/devices/MLNXBF04:00/driver/lifecycle_state
+Date:		Oct 2019
+KernelVersion:	5.4
+Contact:	"Liming Sun <lsun@mellanox.com>"
+Description:
+		The Life-cycle state of the SoC, which could be one of the
+		following values.
+		  Production - Production state and can be updated to secure
+		  GA Secured - Secure chip and not able to change state
+		  GA Non-Secured - Non-Secure chip and not able to change state
+		  RMA - Return Merchandise Authorization
+
+What:		/sys/bus/platform/devices/MLNXBF04:00/driver/post_reset_wdog
+Date:		Oct 2019
+KernelVersion:	5.4
+Contact:	"Liming Sun <lsun@mellanox.com>"
+Description:
+		The watchdog setting in seconds for the next booting. It's used
+		to reboot the chip and recover it to the old state if the new
+		boot partition fails.
+
+What:		/sys/bus/platform/devices/MLNXBF04:00/driver/reset_action
+Date:		Oct 2019
+KernelVersion:	5.4
+Contact:	"Liming Sun <lsun@mellanox.com>"
+Description:
+		The source of the boot stream for the next reset. It could be
+		one of the following values.
+		  external - boot from external source (USB or PCIe)
+		  emmc - boot from the onchip eMMC
+		  emmc_legacy - boot from the onchip eMMC in legacy (slow) mode
+
+What:		/sys/bus/platform/devices/MLNXBF04:00/driver/second_reset_action
+Date:		Oct 2019
+KernelVersion:	5.4
+Contact:	"Liming Sun <lsun@mellanox.com>"
+Description:
+		Update the source of the boot stream after next reset. It could
+		be one of the following values and will be applied after next
+		reset.
+		  external - boot from external source (USB or PCIe)
+		  emmc - boot from the onchip eMMC
+		  emmc_legacy - boot from the onchip eMMC in legacy (slow) mode
+		  swap_emmc - swap the primary / secondary boot partition
+		  none - cancel the action
+
+What:		/sys/bus/platform/devices/MLNXBF04:00/driver/secure_boot_fuse_state
+Date:		Oct 2019
+KernelVersion:	5.4
+Contact:	"Liming Sun <lsun@mellanox.com>"
+Description:
+		The state of eFuse versions with the following values.
+		  InUse - burnt, valid and currently in use
+		  Used - burnt and valid
+		  Free - not burnt and free to use
+		  Skipped - not burnt but not free (skipped)
+		  Wasted - burnt and invalid
+		  Invalid - not burnt but marked as valid (error state).
diff --git a/MAINTAINERS b/MAINTAINERS
index 55199ef..c2ff283 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10435,6 +10435,7 @@ M:	Darren Hart <dvhart@infradead.org>
 M:	Vadim Pasternak <vadimp@mellanox.com>
 L:	platform-driver-x86@vger.kernel.org
 S:	Supported
+F:	Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
 F:	drivers/platform/mellanox/
 F:	include/linux/platform_data/mlxreg.h
 
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2019-10-07 15:49 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-29 20:59 [PATCH v1 1/1] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc Liming Sun
2019-01-29 22:03 ` Andy Shevchenko
2019-01-30  6:24   ` Vadim Pasternak
2019-01-30 20:56     ` Liming Sun
2019-01-30 20:47   ` Liming Sun
2019-01-30 21:17     ` Andy Shevchenko
2019-01-31 16:53       ` Liming Sun
2019-01-31 16:53 ` [PATCH v2] " Liming Sun
2019-01-31 17:02   ` Andy Shevchenko
2019-01-31 17:18     ` Liming Sun
2019-01-31 19:20       ` Liming Sun
2019-01-31 19:19 ` [PATCH v3] " Liming Sun
2019-02-01  5:16 ` [PATCH v1 1/1] " kbuild test robot
2019-02-01 20:48   ` Liming Sun
2019-02-01 20:46 ` [PATCH v4] " Liming Sun
2019-02-05 17:21   ` Andy Shevchenko
2019-05-17 17:49     ` Liming Sun
2019-05-17 17:49 ` [PATCH v5 1/2] " Liming Sun
2019-05-20 15:56   ` Greg KH
2019-05-20 18:07     ` Liming Sun
2019-05-20 19:12       ` Greg KH
2019-05-20 20:43         ` Liming Sun
2019-05-21  7:58           ` Arnd Bergmann
2019-05-22 15:12             ` Liming Sun
2019-05-22 13:34   ` Mark Rutland
2019-05-22 14:51     ` Liming Sun
2019-05-17 17:49 ` [PATCH v5 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions Liming Sun
2019-05-17 17:59   ` Greg Kroah-Hartman
2019-05-17 20:36     ` Liming Sun
2019-05-18  6:35       ` Greg Kroah-Hartman
2019-05-20 15:20         ` Liming Sun
2019-05-20 15:53           ` Greg Kroah-Hartman
2019-10-07 15:48 ` [PATCH v6 1/2] platform/mellanox: Add bootctl driver for Mellanox BlueField Soc Liming Sun
2019-10-07 15:48 ` [PATCH v6 2/2] platform/mellanox/mlxbf-bootctl: Add the ABI definitions Liming Sun

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).