summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Crouse <jcrouse@codeaurora.org>2017-02-13 10:14:20 -0700
committerJordan Crouse <jcrouse@codeaurora.org>2017-02-22 09:52:14 -0700
commitef3370e0ca6fa8d4becc7e61c13cc004cd1c6fc8 (patch)
treeb92f0dde82cca81822ca6a72d598fc2a671757e9
parent0a7274232bb86ef80e4402e5eaffd59edbcda85d (diff)
drm/msm: gpu: Use the zap shader on 5XX if we can
The A5XX GPU powers on in "secure" mode. In secure mode the GPU can only render to buffers that are marked as secure and inaccessible to the kernel and user through a series of hardware protections. In practice secure mode is used to draw things like a UI on a secure video frame. In order to switch out of secure mode the GPU executes a special shader that clears out the GMEM and other sensitve registers and then writes a register. Because the kernel can't be trusted the shader binary is signed and verified and programmed by the trustzone using the PIL loader to upload the binary and access to the special register is blocked with hardware protection. So in summary, to do secure mode correctly you need 1) a friendly trustzone, 2) PIL loader support and 3) a verified zap shader. For targets without secure support there is an out: if the trustzone doesn't support secure then there are no hardware protections and we can freely write the SECVID_TRUST register from the CPU. Unfortunately we don't have any good way to figure out at runtime if the trustzone is secure or not so we use a cheat. If there is a zap shader specified in the device tree for the target, we assume that the trustzone is secure. No zap shader definition means that the target is not secure. The downside of course is that if you guess wrong you trigger a XPU violation which usually ends up in a system crash but thats a problem that shows up immediately. Change-Id: Ic0dedbad8738ad1fac8a7bb8d76e1988aa49f2c8 Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_gpu.c90
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_gpu.h6
2 files changed, 94 insertions, 2 deletions
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index fd92312529a6..056b6c89d877 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -303,6 +303,67 @@ static int a5xx_ucode_init(struct msm_gpu *gpu)
return 0;
}
+#ifdef CONFIG_MSM_SUBSYSTEM_RESTART
+
+#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/scm.h>
+
+static void a5xx_zap_shader_init(struct msm_gpu *gpu)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
+ const char *name;
+ void *ptr;
+
+ /* If no zap shader was defined, we'll assume that none is needed */
+ if (of_property_read_string(GPU_OF_NODE(gpu), "qcom,zap-shader", &name))
+ return;
+
+ /*
+ * If the zap shader has already been loaded then just ask the SCM to
+ * re-initialize the registers (not needed if CPZ retention is a thing)
+ */
+ if (test_bit(A5XX_ZAP_SHADER_LOADED, &a5xx_gpu->flags)) {
+ int ret;
+ struct scm_desc desc = { 0 };
+
+ if (of_property_read_bool(GPU_OF_NODE(gpu),
+ "qcom,cpz-retention"))
+ return;
+
+ desc.args[0] = 0;
+ desc.args[1] = 13;
+ desc.arginfo = SCM_ARGS(2);
+
+ ret = scm_call2(SCM_SIP_FNID(SCM_SVC_BOOT, 0x0A), &desc);
+ if (ret)
+ DRM_ERROR(
+ "%s: zap-shader resume failed with error %d\n",
+ gpu->name, ret);
+
+ return;
+ }
+
+ ptr = subsystem_get(name);
+
+ if (IS_ERR_OR_NULL(ptr)) {
+ DRM_ERROR("%s: Unable to load the zap shader: %ld\n", gpu->name,
+ IS_ERR(ptr) ? PTR_ERR(ptr) : -ENODEV);
+ } else {
+ set_bit(A5XX_ZAP_SHADER_LOADED, &a5xx_gpu->flags);
+ }
+}
+#else
+static void a5xx_zap_shader_init(struct msm_gpu *gpu)
+{
+ if (of_find_property(GPU_OF_NODE(gpu), "qcom,zap-shader", NULL))
+ return;
+
+ DRM_INFO_ONCE("%s: Zap shader is defined but loader isn't available\n",
+ gpu->name);
+}
+#endif
+
#define A5XX_INT_MASK (A5XX_RBBM_INT_0_MASK_RBBM_AHB_ERROR | \
A5XX_RBBM_INT_0_MASK_RBBM_TRANSFER_TIMEOUT | \
A5XX_RBBM_INT_0_MASK_RBBM_ME_MS_TIMEOUT | \
@@ -320,6 +381,7 @@ static int a5xx_hw_init(struct msm_gpu *gpu)
struct msm_drm_private *priv = gpu->dev->dev_private;
struct platform_device *pdev = priv->gpu_pdev;
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
int ret, bit = 0;
pm_qos_update_request(&gpu->pm_qos_req_dma, 101);
@@ -414,6 +476,9 @@ static int a5xx_hw_init(struct msm_gpu *gpu)
}
}
+ /* Try to load and initialize the zap shader if applicable */
+ a5xx_zap_shader_init(gpu);
+
/* Protect registers from the CP */
gpu_write(gpu, REG_A5XX_CP_PROTECT_CNTL, 0x00000007);
@@ -500,8 +565,29 @@ static int a5xx_hw_init(struct msm_gpu *gpu)
return -EINVAL;
}
- /* Put the GPU into insecure mode */
- gpu_write(gpu, REG_A5XX_RBBM_SECVID_TRUST_CNTL, 0x0);
+ /*
+ * If a zap shader was specified in the device tree, assume that we are
+ * on a secure device that blocks access to the RBBM_SECVID registers
+ * so we need to use the CP to switch out of secure mode. If a zap
+ * shader was NOT specified then we assume we are on an unlocked device.
+ * If we guessed wrong then the access to the register will probably
+ * cause a XPU violation.
+ */
+ if (test_bit(A5XX_ZAP_SHADER_LOADED, &a5xx_gpu->flags)) {
+ struct msm_ringbuffer *ring = gpu->rb;
+
+ OUT_PKT7(ring, CP_SET_SECURE_MODE, 1);
+ OUT_RING(ring, 0x00000000);
+
+ gpu->funcs->flush(gpu);
+ if (!gpu->funcs->idle(gpu))
+ return -EINVAL;
+ } else {
+ /* Print a warning so if we die, we know why */
+ dev_warn_once(gpu->dev->dev,
+ "Zap shader not enabled - using SECVID_TRUST_CNTL instead\n");
+ gpu_write(gpu, REG_A5XX_RBBM_SECVID_TRUST_CNTL, 0x0);
+ }
pm_qos_update_request(&gpu->pm_qos_req_dma, 501);
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
index 1590f845d554..e82f54063877 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
@@ -21,7 +21,13 @@
#include "a5xx.xml.h"
+enum {
+ A5XX_ZAP_SHADER_LOADED = 1,
+};
+
struct a5xx_gpu {
+ unsigned long flags;
+
struct adreno_gpu base;
struct platform_device *pdev;