Signed-off-by: Egbert Eich <eich@xxxxxxx> --- drivers/gpu/drm/drm_edid.c | 77 ++++++++++++++++++++++++++++++++------- drivers/gpu/drm/drm_edid_load.c | 54 ++++++--------------------- include/drm/drm_edid.h | 1 + 3 files changed, 77 insertions(+), 55 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index d1b9d67..7bdae6e 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -408,6 +408,29 @@ fixup_blockmaps(u8 **blockp, int eblock_cnt) return eblock_cnt; } +static int +fixup_edid(u8 **blockp, int valid_extensions) +{ + u8 *new = NULL; + + if (valid_extensions != (*blockp)[EDID_EXTENSION_FLAG_OFFSET]) { + + if (valid_extensions) + valid_extensions = fixup_blockmaps(blockp, valid_extensions); + + if (valid_extensions >= 0) { + (*blockp)[EDID_CHECKSUM_OFFSET] += (*blockp)[EDID_EXTENSION_FLAG_OFFSET] - valid_extensions; + (*blockp)[EDID_EXTENSION_FLAG_OFFSET] = valid_extensions; + new = krealloc(*blockp, (valid_extensions + 1) * EDID_LENGTH, GFP_KERNEL); + } + if (!new) + kfree(*blockp); + + *blockp = new; + } + return (new ? valid_extensions : -ENOMEM); +} + static u8 * drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) { @@ -474,19 +497,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) } no_more: - if (valid_extensions != block[EDID_EXTENSION_FLAG_OFFSET]) { - if (valid_extensions) - valid_extensions = fixup_blockmaps(&block, valid_extensions); - if (valid_extensions >= 0) { - block[EDID_CHECKSUM_OFFSET] += block[EDID_EXTENSION_FLAG_OFFSET] - valid_extensions; - block[EDID_EXTENSION_FLAG_OFFSET] = valid_extensions; - new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, GFP_KERNEL); - if (!new) - goto out; - } else - goto out; - block = new; - } + fixup_edid(&block, valid_extensions); return block; @@ -503,6 +514,46 @@ out: } /** + * Validate an entire EDID blob. + * \param connector: drm_connector struct of the used connector. + * \param blockp: pointer to address of an raw EDID data block. + * \param len: size if block in bytes. + * + * validate block and return corrected block in \param block. + * \return: number of valid extensions or -errno if unsuccessful. + */ +int +drm_validate_edid_blob(struct drm_connector *connector, u8 **blockp, int len) +{ + int n_blocks = len / EDID_LENGTH; + int valid_extensions = 0, ret = 0; + bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS); + + if (!blockp || !*blockp) + ret = -EINVAL; + else if (!n_blocks || !drm_edid_block_valid(*blockp, 0, print_bad_edid)) { + kfree(*blockp); + *blockp = NULL; + ret = -EINVAL; + } + if (!ret) { + n_blocks--; + if ((*blockp)[EDID_EXTENSION_FLAG_OFFSET] < n_blocks) + n_blocks = (*blockp)[EDID_EXTENSION_FLAG_OFFSET]; + + while (n_blocks--) { + if (drm_edid_block_valid(*blockp + (valid_extensions + 1) * EDID_LENGTH, + valid_extensions + 1, print_bad_edid)) + valid_extensions++; + } + ret = fixup_edid(blockp, valid_extensions); + } + if (ret < 0) + connector->bad_edid_counter++; + return ret; +} + +/** * Probe DDC presence. * * \param adapter : i2c device adaptor diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index 38d3943..6541c1f 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -119,11 +119,10 @@ static u8 *edid_load(struct drm_connector *connector, char *name, { const struct firmware *fw; struct platform_device *pdev; - u8 *fwdata = NULL, *edid, *new_edid; + u8 *fwdata = NULL; + struct edid *edid; int fwsize, expected; int builtin = 0, err = 0; - int i, valid_extensions = 0; - bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS); pdev = platform_device_register_simple(connector_name, -1, NULL, 0); if (IS_ERR(pdev)) { @@ -137,7 +136,7 @@ static u8 *edid_load(struct drm_connector *connector, char *name, platform_device_unregister(pdev); if (err) { - i = 0; + int i = 0; while (i < GENERIC_EDIDS && strcmp(name, generic_edid_name[i])) i++; if (i < GENERIC_EDIDS) { @@ -174,49 +173,20 @@ static u8 *edid_load(struct drm_connector *connector, char *name, } memcpy(edid, fwdata, fwsize); - if (!drm_edid_block_valid(edid, 0, print_bad_edid)) { - connector->bad_edid_counter++; - DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ", - name); - kfree(edid); - err = -EINVAL; - goto relfw_out; - } - - for (i = 1; i <= edid[0x7e]; i++) { - if (i != valid_extensions + 1) - memcpy(edid + (valid_extensions + 1) * EDID_LENGTH, - edid + i * EDID_LENGTH, EDID_LENGTH); - if (drm_edid_block_valid(edid + i * EDID_LENGTH, i, print_bad_edid)) - valid_extensions++; - } - - if (valid_extensions != edid[0x7e]) { - edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions; - DRM_INFO("Found %d valid extensions instead of %d in EDID data " - "\"%s\" for connector \"%s\"\n", valid_extensions, - edid[0x7e], name, connector_name); - edid[0x7e] = valid_extensions; - new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH, - GFP_KERNEL); - if (new_edid == NULL) { - err = -ENOMEM; - kfree(edid); - goto relfw_out; - } - edid = new_edid; - } - - DRM_INFO("Got %s EDID base block and %d extension%s from " - "\"%s\" for connector \"%s\"\n", builtin ? "built-in" : - "external", valid_extensions, valid_extensions == 1 ? "" : "s", - name, connector_name); + err = drm_validate_edid_blob(connector, (u8 **)&edid, fwsize); + if (err < 0) + DRM_ERROR("EDID firmware \"%s\" is invalid ", name); + else + DRM_INFO("Got %s EDID base block and %d extension%s from " + "\"%s\" for connector \"%s\"\n", builtin ? "built-in" : + "external", edid->extensions, edid->extensions == 1 ? "" : "s", + name, connector_name); relfw_out: release_firmware(fw); out: - if (err) + if (err < 0) return ERR_PTR(err); return edid; diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 0cac551..3e8ef06 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -253,5 +253,6 @@ int drm_av_sync_delay(struct drm_connector *connector, struct drm_connector *drm_select_eld(struct drm_encoder *encoder, struct drm_display_mode *mode); int drm_load_edid_firmware(struct drm_connector *connector); +int drm_validate_edid_blob(struct drm_connector *connector, u8 **blockp, int len); #endif /* __DRM_EDID_H__ */ -- 1.7.7 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel