diff options
Diffstat (limited to 'drivers/gpu/drm/msm/adreno/a6xx_gpu.c')
| -rw-r--r-- | drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 108 | 
1 files changed, 75 insertions, 33 deletions
| diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index ba8e9d3cf0fe..d553f62f4eeb 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -522,28 +522,73 @@ static int a6xx_cp_init(struct msm_gpu *gpu)  	return a6xx_idle(gpu, ring) ? 0 : -EINVAL;  } -static void a6xx_ucode_check_version(struct a6xx_gpu *a6xx_gpu, +/* + * Check that the microcode version is new enough to include several key + * security fixes. Return true if the ucode is safe. + */ +static bool a6xx_ucode_check_version(struct a6xx_gpu *a6xx_gpu,  		struct drm_gem_object *obj)  { +	struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; +	struct msm_gpu *gpu = &adreno_gpu->base;  	u32 *buf = msm_gem_get_vaddr(obj); +	bool ret = false;  	if (IS_ERR(buf)) -		return; +		return false;  	/* -	 * If the lowest nibble is 0xa that is an indication that this microcode -	 * has been patched. The actual version is in dword [3] but we only care -	 * about the patchlevel which is the lowest nibble of dword [3] -	 * -	 * Otherwise check that the firmware is greater than or equal to 1.90 -	 * which was the first version that had this fix built in +	 * Targets up to a640 (a618, a630 and a640) need to check for a +	 * microcode version that is patched to support the whereami opcode or +	 * one that is new enough to include it by default.  	 */ -	if (((buf[0] & 0xf) == 0xa) && (buf[2] & 0xf) >= 1) -		a6xx_gpu->has_whereami = true; -	else if ((buf[0] & 0xfff) > 0x190) -		a6xx_gpu->has_whereami = true; +	if (adreno_is_a618(adreno_gpu) || adreno_is_a630(adreno_gpu) || +		adreno_is_a640(adreno_gpu)) { +		/* +		 * If the lowest nibble is 0xa that is an indication that this +		 * microcode has been patched. The actual version is in dword +		 * [3] but we only care about the patchlevel which is the lowest +		 * nibble of dword [3] +		 * +		 * Otherwise check that the firmware is greater than or equal +		 * to 1.90 which was the first version that had this fix built +		 * in +		 */ +		if ((((buf[0] & 0xf) == 0xa) && (buf[2] & 0xf) >= 1) || +			(buf[0] & 0xfff) >= 0x190) { +			a6xx_gpu->has_whereami = true; +			ret = true; +			goto out; +		} +		DRM_DEV_ERROR(&gpu->pdev->dev, +			"a630 SQE ucode is too old. Have version %x need at least %x\n", +			buf[0] & 0xfff, 0x190); +	}  else { +		/* +		 * a650 tier targets don't need whereami but still need to be +		 * equal to or newer than 0.95 for other security fixes +		 */ +		if (adreno_is_a650(adreno_gpu)) { +			if ((buf[0] & 0xfff) >= 0x095) { +				ret = true; +				goto out; +			} + +			DRM_DEV_ERROR(&gpu->pdev->dev, +				"a650 SQE ucode is too old. Have version %x need at least %x\n", +				buf[0] & 0xfff, 0x095); +		} + +		/* +		 * When a660 is added those targets should return true here +		 * since those have all the critical security fixes built in +		 * from the start +		 */ +	} +out:  	msm_gem_put_vaddr(obj); +	return ret;  }  static int a6xx_ucode_init(struct msm_gpu *gpu) @@ -566,7 +611,13 @@ static int a6xx_ucode_init(struct msm_gpu *gpu)  		}  		msm_gem_object_set_name(a6xx_gpu->sqe_bo, "sqefw"); -		a6xx_ucode_check_version(a6xx_gpu, a6xx_gpu->sqe_bo); +		if (!a6xx_ucode_check_version(a6xx_gpu, a6xx_gpu->sqe_bo)) { +			msm_gem_unpin_iova(a6xx_gpu->sqe_bo, gpu->aspace); +			drm_gem_object_put(a6xx_gpu->sqe_bo); + +			a6xx_gpu->sqe_bo = NULL; +			return -EPERM; +		}  	}  	gpu_write64(gpu, REG_A6XX_CP_SQE_INSTR_BASE_LO, @@ -1177,8 +1228,8 @@ static int a6xx_get_timestamp(struct msm_gpu *gpu, uint64_t *value)  	/* Force the GPU power on so we can read this register */  	a6xx_gmu_set_oob(&a6xx_gpu->gmu, GMU_OOB_PERFCOUNTER_SET); -	*value = gpu_read64(gpu, REG_A6XX_RBBM_PERFCTR_CP_0_LO, -		REG_A6XX_RBBM_PERFCTR_CP_0_HI); +	*value = gpu_read64(gpu, REG_A6XX_CP_ALWAYS_ON_COUNTER_LO, +		REG_A6XX_CP_ALWAYS_ON_COUNTER_HI);  	a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_PERFCOUNTER_SET);  	mutex_unlock(&perfcounter_oob); @@ -1350,35 +1401,26 @@ static int a6xx_set_supported_hw(struct device *dev, struct a6xx_gpu *a6xx_gpu,  		u32 revn)  {  	struct opp_table *opp_table; -	struct nvmem_cell *cell;  	u32 supp_hw = UINT_MAX; -	void *buf; +	u16 speedbin; +	int ret; -	cell = nvmem_cell_get(dev, "speed_bin"); +	ret = nvmem_cell_read_u16(dev, "speed_bin", &speedbin);  	/*  	 * -ENOENT means that the platform doesn't support speedbin which is  	 * fine  	 */ -	if (PTR_ERR(cell) == -ENOENT) +	if (ret == -ENOENT) {  		return 0; -	else if (IS_ERR(cell)) { -		DRM_DEV_ERROR(dev, -				"failed to read speed-bin. Some OPPs may not be supported by hardware"); -		goto done; -	} - -	buf = nvmem_cell_read(cell, NULL); -	if (IS_ERR(buf)) { -		nvmem_cell_put(cell); +	} else if (ret) {  		DRM_DEV_ERROR(dev, -				"failed to read speed-bin. Some OPPs may not be supported by hardware"); +			      "failed to read speed-bin (%d). Some OPPs may not be supported by hardware", +			      ret);  		goto done;  	} +	speedbin = le16_to_cpu(speedbin); -	supp_hw = fuse_to_supp_hw(dev, revn, *((u32 *) buf)); - -	kfree(buf); -	nvmem_cell_put(cell); +	supp_hw = fuse_to_supp_hw(dev, revn, speedbin);  done:  	opp_table = dev_pm_opp_set_supported_hw(dev, &supp_hw, 1); | 
