Эх сурвалжийг харах

firmware: qcom: scm: Expose download-mode control

In order to aid post-mortem debugging the Qualcomm platforms provide a
"memory download mode", where the boot loader will provide an interface
for custom tools to "download" the content of RAM to a host machine.

The mode is triggered by writing a magic value somewhere in RAM, that is
read in the boot code path after a warm-restart. Two mechanism for
setting this magic value are supported in modern platforms; a direct SCM
call to enable the mode or through a secure io write of a magic value.

In order for a normal reboot not to trigger "download mode" the magic
must be cleared during a clean reboot.

Download mode has to be enabled by including qcom_scm.download_mode=1 on
the command line.

Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Signed-off-by: Andy Gross <andy.gross@linaro.org>
Bjorn Andersson 8 жил өмнө
parent
commit
8c1b7dc9ba

+ 2 - 0
Documentation/devicetree/bindings/firmware/qcom,scm.txt

@@ -18,6 +18,8 @@ Required properties:
  * Core, iface, and bus clocks required for "qcom,scm"
  * Core, iface, and bus clocks required for "qcom,scm"
 - clock-names: Must contain "core" for the core clock, "iface" for the interface
 - clock-names: Must contain "core" for the core clock, "iface" for the interface
   clock and "bus" for the bus clock per the requirements of the compatible.
   clock and "bus" for the bus clock per the requirements of the compatible.
+- qcom,dload-mode: phandle to the TCSR hardware block and offset of the
+		   download mode control register (optional)
 
 
 Example for MSM8916:
 Example for MSM8916:
 
 

+ 11 - 0
drivers/firmware/Kconfig

@@ -215,6 +215,17 @@ config QCOM_SCM_64
 	def_bool y
 	def_bool y
 	depends on QCOM_SCM && ARM64
 	depends on QCOM_SCM && ARM64
 
 
+config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
+	bool "Qualcomm download mode enabled by default"
+	depends on QCOM_SCM
+	help
+	  A device with "download mode" enabled will upon an unexpected
+	  warm-restart enter a special debug mode that allows the user to
+	  "download" memory content over USB for offline postmortem analysis.
+	  The feature can be enabled/disabled on the kernel command line.
+
+	  Say Y here to enable "download mode" by default.
+
 config TI_SCI_PROTOCOL
 config TI_SCI_PROTOCOL
 	tristate "TI System Control Interface (TISCI) Message Protocol"
 	tristate "TI System Control Interface (TISCI) Message Protocol"
 	depends on TI_MESSAGE_MANAGER
 	depends on TI_MESSAGE_MANAGER

+ 6 - 0
drivers/firmware/qcom_scm-32.c

@@ -561,6 +561,12 @@ int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
 	return ret ? : le32_to_cpu(out);
 	return ret ? : le32_to_cpu(out);
 }
 }
 
 
+int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
+{
+	return qcom_scm_call_atomic2(QCOM_SCM_SVC_BOOT, QCOM_SCM_SET_DLOAD_MODE,
+				     enable ? QCOM_SCM_SET_DLOAD_MODE : 0, 0);
+}
+
 int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id)
 int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id)
 {
 {
 	struct {
 	struct {

+ 13 - 0
drivers/firmware/qcom_scm-64.c

@@ -440,6 +440,19 @@ int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size,
 	return ret;
 	return ret;
 }
 }
 
 
+int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
+{
+	struct qcom_scm_desc desc = {0};
+	struct arm_smccc_res res;
+
+	desc.args[0] = QCOM_SCM_SET_DLOAD_MODE;
+	desc.args[1] = enable ? QCOM_SCM_SET_DLOAD_MODE : 0;
+	desc.arginfo = QCOM_SCM_ARGS(2);
+
+	return qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_SET_DLOAD_MODE,
+			     &desc, &res);
+}
+
 int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr,
 int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr,
 			unsigned int *val)
 			unsigned int *val)
 {
 {

+ 75 - 0
drivers/firmware/qcom_scm.c

@@ -19,15 +19,20 @@
 #include <linux/cpumask.h>
 #include <linux/cpumask.h>
 #include <linux/export.h>
 #include <linux/export.h>
 #include <linux/dma-mapping.h>
 #include <linux/dma-mapping.h>
+#include <linux/module.h>
 #include <linux/types.h>
 #include <linux/types.h>
 #include <linux/qcom_scm.h>
 #include <linux/qcom_scm.h>
 #include <linux/of.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/of_platform.h>
 #include <linux/of_platform.h>
 #include <linux/clk.h>
 #include <linux/clk.h>
 #include <linux/reset-controller.h>
 #include <linux/reset-controller.h>
 
 
 #include "qcom_scm.h"
 #include "qcom_scm.h"
 
 
+static bool download_mode = IS_ENABLED(CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT);
+module_param(download_mode, bool, 0);
+
 #define SCM_HAS_CORE_CLK	BIT(0)
 #define SCM_HAS_CORE_CLK	BIT(0)
 #define SCM_HAS_IFACE_CLK	BIT(1)
 #define SCM_HAS_IFACE_CLK	BIT(1)
 #define SCM_HAS_BUS_CLK		BIT(2)
 #define SCM_HAS_BUS_CLK		BIT(2)
@@ -38,6 +43,8 @@ struct qcom_scm {
 	struct clk *iface_clk;
 	struct clk *iface_clk;
 	struct clk *bus_clk;
 	struct clk *bus_clk;
 	struct reset_controller_dev reset;
 	struct reset_controller_dev reset;
+
+	u64 dload_mode_addr;
 };
 };
 
 
 static struct qcom_scm *__scm;
 static struct qcom_scm *__scm;
@@ -345,6 +352,54 @@ int qcom_scm_io_writel(phys_addr_t addr, unsigned int val)
 }
 }
 EXPORT_SYMBOL(qcom_scm_io_writel);
 EXPORT_SYMBOL(qcom_scm_io_writel);
 
 
+static void qcom_scm_set_download_mode(bool enable)
+{
+	bool avail;
+	int ret = 0;
+
+	avail = __qcom_scm_is_call_available(__scm->dev,
+					     QCOM_SCM_SVC_BOOT,
+					     QCOM_SCM_SET_DLOAD_MODE);
+	if (avail) {
+		ret = __qcom_scm_set_dload_mode(__scm->dev, enable);
+	} else if (__scm->dload_mode_addr) {
+		ret = __qcom_scm_io_writel(__scm->dev, __scm->dload_mode_addr,
+					   enable ? QCOM_SCM_SET_DLOAD_MODE : 0);
+	} else {
+		dev_err(__scm->dev,
+			"No available mechanism for setting download mode\n");
+	}
+
+	if (ret)
+		dev_err(__scm->dev, "failed to set download mode: %d\n", ret);
+}
+
+static int qcom_scm_find_dload_address(struct device *dev, u64 *addr)
+{
+	struct device_node *tcsr;
+	struct device_node *np = dev->of_node;
+	struct resource res;
+	u32 offset;
+	int ret;
+
+	tcsr = of_parse_phandle(np, "qcom,dload-mode", 0);
+	if (!tcsr)
+		return 0;
+
+	ret = of_address_to_resource(tcsr, 0, &res);
+	of_node_put(tcsr);
+	if (ret)
+		return ret;
+
+	ret = of_property_read_u32_index(np, "qcom,dload-mode", 1, &offset);
+	if (ret < 0)
+		return ret;
+
+	*addr = res.start + offset;
+
+	return 0;
+}
+
 /**
 /**
  * qcom_scm_is_available() - Checks if SCM is available
  * qcom_scm_is_available() - Checks if SCM is available
  */
  */
@@ -370,6 +425,10 @@ static int qcom_scm_probe(struct platform_device *pdev)
 	if (!scm)
 	if (!scm)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
+	ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr);
+	if (ret < 0)
+		return ret;
+
 	clks = (unsigned long)of_device_get_match_data(&pdev->dev);
 	clks = (unsigned long)of_device_get_match_data(&pdev->dev);
 	if (clks & SCM_HAS_CORE_CLK) {
 	if (clks & SCM_HAS_CORE_CLK) {
 		scm->core_clk = devm_clk_get(&pdev->dev, "core");
 		scm->core_clk = devm_clk_get(&pdev->dev, "core");
@@ -418,9 +477,24 @@ static int qcom_scm_probe(struct platform_device *pdev)
 
 
 	__qcom_scm_init();
 	__qcom_scm_init();
 
 
+	/*
+	 * If requested enable "download mode", from this point on warmboot
+	 * will cause the the boot stages to enter download mode, unless
+	 * disabled below by a clean shutdown/reboot.
+	 */
+	if (download_mode)
+		qcom_scm_set_download_mode(true);
+
 	return 0;
 	return 0;
 }
 }
 
 
+static void qcom_scm_shutdown(struct platform_device *pdev)
+{
+	/* Clean shutdown, disable download mode to allow normal restart */
+	if (download_mode)
+		qcom_scm_set_download_mode(false);
+}
+
 static const struct of_device_id qcom_scm_dt_match[] = {
 static const struct of_device_id qcom_scm_dt_match[] = {
 	{ .compatible = "qcom,scm-apq8064",
 	{ .compatible = "qcom,scm-apq8064",
 	  /* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */
 	  /* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */
@@ -448,6 +522,7 @@ static struct platform_driver qcom_scm_driver = {
 		.of_match_table = qcom_scm_dt_match,
 		.of_match_table = qcom_scm_dt_match,
 	},
 	},
 	.probe = qcom_scm_probe,
 	.probe = qcom_scm_probe,
+	.shutdown = qcom_scm_shutdown,
 };
 };
 
 
 static int __init qcom_scm_init(void)
 static int __init qcom_scm_init(void)

+ 2 - 0
drivers/firmware/qcom_scm.h

@@ -14,9 +14,11 @@
 
 
 #define QCOM_SCM_SVC_BOOT		0x1
 #define QCOM_SCM_SVC_BOOT		0x1
 #define QCOM_SCM_BOOT_ADDR		0x1
 #define QCOM_SCM_BOOT_ADDR		0x1
+#define QCOM_SCM_SET_DLOAD_MODE		0x10
 #define QCOM_SCM_BOOT_ADDR_MC		0x11
 #define QCOM_SCM_BOOT_ADDR_MC		0x11
 #define QCOM_SCM_SET_REMOTE_STATE	0xa
 #define QCOM_SCM_SET_REMOTE_STATE	0xa
 extern int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id);
 extern int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id);
+extern int __qcom_scm_set_dload_mode(struct device *dev, bool enable);
 
 
 #define QCOM_SCM_FLAG_HLOS		0x01
 #define QCOM_SCM_FLAG_HLOS		0x01
 #define QCOM_SCM_FLAG_COLDBOOT_MC	0x02
 #define QCOM_SCM_FLAG_COLDBOOT_MC	0x02