diff options
| author | Jordan Crouse <jcrouse@codeaurora.org> | 2017-02-13 10:14:20 -0700 |
|---|---|---|
| committer | Jordan Crouse <jcrouse@codeaurora.org> | 2017-02-22 09:52:14 -0700 |
| commit | ef3370e0ca6fa8d4becc7e61c13cc004cd1c6fc8 (patch) | |
| tree | b92f0dde82cca81822ca6a72d598fc2a671757e9 | |
| parent | 0a7274232bb86ef80e4402e5eaffd59edbcda85d (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.c | 90 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/adreno/a5xx_gpu.h | 6 |
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; |
