Commit a0f52c3d authored by Jani Nikula's avatar Jani Nikula
Browse files

drm/i915/opregion: rvda is relative from opregion base in opregion 2.1+



Starting from opregion version 2.1 (roughly corresponding to ICL+) the
RVDA field is relative from the beginning of opregion, not absolute
address.

Fix the error path while at it.

v2: Make relative vs. absolute conditional on the opregion version,
    bumped for the purpose. Turned out there are machines relying on
    absolute RVDA in the wild.

v3: Fix the version checks

Fixes: 04ebaadb ("drm/i915/opregion: handle VBT sizes bigger than 6 KB")
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Reviewed-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: default avatarJani Nikula <jani.nikula@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190208184254.24123-2-jani.nikula@intel.com
parent 98fdaaca
Loading
Loading
Loading
Loading
+21 −3
Original line number Diff line number Diff line
@@ -123,7 +123,8 @@ struct opregion_asle {
	u64 fdss;
	u32 fdsp;
	u32 stat;
	u64 rvda;	/* Physical address of raw vbt data */
	u64 rvda;	/* Physical (2.0) or relative from opregion (2.1+)
			 * address of raw VBT data. */
	u32 rvds;	/* Size of raw vbt data */
	u8 rsvd[58];
} __packed;
@@ -964,9 +965,24 @@ int intel_opregion_setup(struct drm_i915_private *dev_priv)

	if (opregion->header->over.major >= 2 && opregion->asle &&
	    opregion->asle->rvda && opregion->asle->rvds) {
		opregion->rvda = memremap(opregion->asle->rvda,
					  opregion->asle->rvds,
		resource_size_t rvda = opregion->asle->rvda;

		/*
		 * opregion 2.0: rvda is the physical VBT address.
		 *
		 * opregion 2.1+: rvda is unsigned, relative offset from
		 * opregion base, and should never point within opregion.
		 */
		if (opregion->header->over.major > 2 ||
		    opregion->header->over.minor >= 1) {
			WARN_ON(rvda < OPREGION_SIZE);

			rvda += asls;
		}

		opregion->rvda = memremap(rvda, opregion->asle->rvds,
					  MEMREMAP_WB);

		vbt = opregion->rvda;
		vbt_size = opregion->asle->rvds;
		if (intel_bios_is_valid_vbt(vbt, vbt_size)) {
@@ -976,6 +992,8 @@ int intel_opregion_setup(struct drm_i915_private *dev_priv)
			goto out;
		} else {
			DRM_DEBUG_KMS("Invalid VBT in ACPI OpRegion (RVDA)\n");
			memunmap(opregion->rvda);
			opregion->rvda = NULL;
		}
	}