Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3f8fbb94 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drm/msm: add validation for secure transition"

parents 734cd7d0 7444a7db
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -121,13 +121,37 @@ static void msm_fb_output_poll_changed(struct drm_device *dev)
		msm_drm_helper_hotplug_event(dev);
}

/**
 * msm_atomic_helper_check - validate state object
 * @dev: DRM device
 * @state: the driver state object
 *
 * This is a wrapper for the drm_atomic_helper_check to check the modeset
 * and state checking for planes. Additionally it checks if any secure
 * transition(moving CRTC and planes between secure and non-secure states and
 * vice versa) is allowed or not. When going to secure state, planes
 * with fb_mode as dir translated only can be staged on the CRTC, and only one
 * CRTC should be active.
 * Also mixing of secure and non-secure is not allowed.
 *
 * RETURNS
 * Zero for success or -errorno.
 */
int msm_atomic_check(struct drm_device *dev,
			    struct drm_atomic_state *state)
{
	struct msm_drm_private *priv;

	if (msm_is_suspend_blocked(dev)) {
		DRM_DEBUG("rejecting commit during suspend\n");
		return -EBUSY;
	}

	priv = dev->dev_private;
	if (priv && priv->kms && priv->kms->funcs &&
			priv->kms->funcs->atomic_check)
		return priv->kms->funcs->atomic_check(priv->kms, state);

	return drm_atomic_helper_check(dev, state);
}

+3 −0
Original line number Diff line number Diff line
@@ -75,6 +75,9 @@ struct msm_kms_funcs {
			const struct msm_format *msm_fmt,
			const struct drm_mode_fb_cmd2 *cmd,
			struct drm_gem_object **bos);
	/* perform complete atomic check of given atomic state */
	int (*atomic_check)(struct msm_kms *kms,
			struct drm_atomic_state *state);
	/* misc: */
	long (*round_pixclk)(struct msm_kms *kms, unsigned long rate,
			struct drm_encoder *encoder);
+128 −0
Original line number Diff line number Diff line
@@ -2711,6 +2711,130 @@ static int _sde_crtc_excl_dim_layer_check(struct drm_crtc_state *state,
	return rc;
}

static int _sde_crtc_find_plane_fb_modes(struct drm_crtc_state *state,
		uint32_t *fb_ns,
		uint32_t *fb_sec,
		uint32_t *fb_ns_dir,
		uint32_t *fb_sec_dir)
{
	struct drm_plane *plane;
	const struct drm_plane_state *pstate;
	struct sde_plane_state *sde_pstate;
	uint32_t mode = 0;
	int rc;

	if (!state) {
		SDE_ERROR("invalid state\n");
		return -EINVAL;
	}

	*fb_ns = 0;
	*fb_sec = 0;
	*fb_ns_dir = 0;
	*fb_sec_dir = 0;
	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
		if (IS_ERR_OR_NULL(pstate)) {
			rc = PTR_ERR(pstate);
			SDE_ERROR("crtc%d failed to get plane%d state%d\n",
					state->crtc->base.id,
					plane->base.id, rc);
			return rc;
		}
		sde_pstate = to_sde_plane_state(pstate);
		mode = sde_plane_get_property(sde_pstate,
				PLANE_PROP_FB_TRANSLATION_MODE);
		switch (mode) {
		case SDE_DRM_FB_NON_SEC:
			(*fb_ns)++;
			break;
		case SDE_DRM_FB_SEC:
			(*fb_sec)++;
			break;
		case SDE_DRM_FB_NON_SEC_DIR_TRANS:
			(*fb_ns_dir)++;
			break;
		case SDE_DRM_FB_SEC_DIR_TRANS:
			(*fb_sec_dir)++;
			break;
		default:
			SDE_ERROR("Error: Plane[%d], fb_trans_mode:%d",
					plane->base.id,
					mode);
			return -EINVAL;
		}
	}
	return 0;
}

static int _sde_crtc_check_secure_state(struct drm_crtc *crtc,
		struct drm_crtc_state *state)
{
	struct drm_encoder *encoder;
	struct sde_crtc_state *cstate;
	uint32_t secure;
	uint32_t fb_ns = 0, fb_sec = 0, fb_ns_dir = 0, fb_sec_dir = 0;
	int encoder_cnt = 0;
	int rc;

	if (!crtc || !state) {
		SDE_ERROR("invalid arguments\n");
		return -EINVAL;
	}

	cstate = to_sde_crtc_state(state);

	secure = sde_crtc_get_property(cstate,
			CRTC_PROP_SECURITY_LEVEL);

	rc = _sde_crtc_find_plane_fb_modes(state,
			&fb_ns,
			&fb_sec,
			&fb_ns_dir,
			&fb_sec_dir);
	if (rc)
		return rc;

	/**
	 * validate planes
	 * fb_ns_dir is for  secure display use case,
	 * fb_sec_dir is for secure camera preview use case,
	 * fb_sec is for secure video playback,
	 * fb_ns is for normal non secure use cases.
	 */
	if (((secure == SDE_DRM_SEC_ONLY) &&
				(fb_ns || fb_sec || fb_sec_dir)) ||
			(fb_sec || fb_sec_dir)) {
		SDE_ERROR(
			"crtc%d: invalid planes fb_modes Sec:%d, NS:%d, Sec_Dir:%d, NS_Dir%d\n",
				crtc->base.id,
				fb_sec, fb_ns, fb_sec_dir,
				fb_ns_dir);
		return -EINVAL;
	}

	/**
	 * secure_crtc is not allowed in a shared toppolgy
	 * across different encoders.
	 */
	if (fb_ns_dir || fb_sec_dir) {
		drm_for_each_encoder(encoder, crtc->dev)
			if (encoder->crtc ==  crtc)
				encoder_cnt++;

		if (encoder_cnt >
			MAX_ALLOWED_ENCODER_CNT_PER_SECURE_CRTC) {
			SDE_ERROR(
				"crtc%d, invalid virtual encoder crtc%d\n",
				crtc->base.id,
				encoder_cnt);
			return -EINVAL;

		}
	}
	SDE_DEBUG("crtc:%d Secure validation successful\n", crtc->base.id);
	return 0;
}

static int sde_crtc_atomic_check(struct drm_crtc *crtc,
		struct drm_crtc_state *state)
{
@@ -2757,6 +2881,10 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
	_sde_crtc_setup_is_ppsplit(state);
	_sde_crtc_setup_lm_bounds(crtc, state);

	rc = _sde_crtc_check_secure_state(crtc, state);
	if (rc)
		return rc;

	 /* get plane state for all drm planes associated with crtc state */
	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
		if (IS_ERR_OR_NULL(pstate)) {
+17 −0
Original line number Diff line number Diff line
@@ -492,4 +492,21 @@ void sde_crtc_res_put(struct drm_crtc_state *state, u32 type, u64 tag);
void sde_crtc_get_crtc_roi(struct drm_crtc_state *state,
		const struct sde_rect **crtc_roi);

/** sde_crt_get_secure_level - retrieve the secure level from the give state
 *	object, this is used to determine the secure state of the crtc
 * @crtc : Pointer to drm crtc structure
 * @usr: Pointer to drm crtc state
 * return: secure_level
 */
static inline int sde_crtc_get_secure_level(struct drm_crtc *crtc,
		struct drm_crtc_state *state)
{
	if (!crtc || !state)
		return -EINVAL;

	return sde_crtc_get_property(to_sde_crtc_state(state),
			CRTC_PROP_SECURITY_LEVEL);
}


#endif /* _SDE_CRTC_H_ */
+98 −0
Original line number Diff line number Diff line
@@ -1437,6 +1437,103 @@ static void sde_kms_preclose(struct msm_kms *kms, struct drm_file *file)
		sde_crtc_cancel_pending_flip(priv->crtcs[i], file);
}

static int sde_kms_check_secure_transition(struct msm_kms *kms,
		struct drm_atomic_state *state)
{
	struct sde_kms *sde_kms;
	struct drm_device *dev;
	struct drm_crtc *crtc;
	struct drm_crtc *sec_crtc = NULL, *temp_crtc = NULL;
	struct drm_crtc_state *crtc_state;
	int secure_crtc_cnt = 0, active_crtc_cnt = 0;
	int secure_global_crtc_cnt = 0, active_mode_crtc_cnt = 0;
	int i;

	if (!kms || !state) {
		return -EINVAL;
		SDE_ERROR("invalid arguments\n");
	}

	/* iterate state object for active and secure crtc */
	for_each_crtc_in_state(state, crtc, crtc_state, i) {
		if (!crtc_state->active)
			continue;
		active_crtc_cnt++;
		if (sde_crtc_get_secure_level(crtc, crtc_state) ==
				SDE_DRM_SEC_ONLY) {
			sec_crtc = crtc;
			secure_crtc_cnt++;
		}
	}

	/* bail out from further validation if no secure ctrc */
	if (!secure_crtc_cnt)
		return 0;

	if ((secure_crtc_cnt > MAX_ALLOWED_SECURE_CLIENT_CNT) ||
		(secure_crtc_cnt &&
		 (active_crtc_cnt > MAX_ALLOWED_CRTC_CNT_DURING_SECURE))) {
		SDE_ERROR("Secure check failed active:%d, secure:%d\n",
				active_crtc_cnt, secure_crtc_cnt);
		return -EPERM;
	}

	sde_kms = to_sde_kms(kms);
	dev = sde_kms->dev;
	/* iterate global list for active and secure crtc */
	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {

		if (!crtc->state->active)
			continue;

		active_mode_crtc_cnt++;

		if (sde_crtc_get_secure_level(crtc, crtc->state) ==
				SDE_DRM_SEC_ONLY) {
			secure_global_crtc_cnt++;
			temp_crtc = crtc;
		}
	}

	/**
	 * if more than one crtc is active fail
	 * check if the previous and current commit secure
	 * are same
	 */
	if (secure_crtc_cnt && ((active_mode_crtc_cnt > 1) ||
			(secure_global_crtc_cnt && (temp_crtc != sec_crtc))))
		SDE_ERROR("Secure check failed active:%d crtc_id:%d\n",
				active_mode_crtc_cnt, temp_crtc->base.id);

	return 0;
}

static int sde_kms_atomic_check(struct msm_kms *kms,
		struct drm_atomic_state *state)
{
	struct sde_kms *sde_kms;
	struct drm_device *dev;
	int ret;

	if (!kms || !state)
		return -EINVAL;

	sde_kms = to_sde_kms(kms);
	dev = sde_kms->dev;

	ret = drm_atomic_helper_check(dev, state);
	if (ret)
		return ret;
	/*
	 * Check if any secure transition(moving CRTC between secure and
	 * non-secure state and vice-versa) is allowed or not. when moving
	 * to secure state, planes with fb_mode set to dir_translated only can
	 * be staged on the CRTC, and only one CRTC can be active during
	 * Secure state
	 */
	return sde_kms_check_secure_transition(kms, state);
}

static struct msm_gem_address_space*
_sde_kms_get_address_space(struct msm_kms *kms,
		unsigned int domain)
@@ -1479,6 +1576,7 @@ static const struct msm_kms_funcs kms_funcs = {
	.enable_vblank   = sde_kms_enable_vblank,
	.disable_vblank  = sde_kms_disable_vblank,
	.check_modified_format = sde_format_check_modified_format,
	.atomic_check = sde_kms_atomic_check,
	.get_format      = sde_get_msm_format,
	.round_pixclk    = sde_kms_round_pixclk,
	.destroy         = sde_kms_destroy,
Loading