drm/i915/bios: abstract finding the panel sequence block
authorJani Nikula <jani.nikula@intel.com>
Tue, 5 Jan 2016 13:50:51 +0000 (15:50 +0200)
committerJani Nikula <jani.nikula@intel.com>
Tue, 5 Jan 2016 14:37:50 +0000 (16:37 +0200)
Make the whole thing easier to read. While at it, make the parsing more
robust, and ensure we don't read past buffer being parsed.

v2: improve commit message (Daniel)

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Jani Nikula <jani.nikula@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1452001851-8967-1-git-send-email-jani.nikula@intel.com
drivers/gpu/drm/i915/intel_bios.c

index 961ae7f6d075469f0726516579530328bf9f3c06..422ba76700e3de02a36c9a7669e81a881b52258e 100644 (file)
@@ -697,7 +697,7 @@ parse_psr(struct drm_i915_private *dev_priv, const struct bdb_header *bdb)
        dev_priv->vbt.psr.tp2_tp3_wakeup_time = psr_table->tp2_tp3_wakeup_time;
 }
 
-static u8 *goto_next_sequence(u8 *data, int *size)
+static u8 *goto_next_sequence(u8 *data, u16 *size)
 {
        u16 len;
        int tmp = *size;
@@ -818,15 +818,52 @@ parse_mipi_config(struct drm_i915_private *dev_priv,
        dev_priv->vbt.dsi.panel_id = MIPI_DSI_GENERIC_PANEL_ID;
 }
 
+/* Find the sequence block and size for the given panel. */
+static const u8 *
+find_panel_sequence_block(const struct bdb_mipi_sequence *sequence,
+                         u16 panel_id, u16 *seq_size)
+{
+       u32 total = get_blocksize(sequence);
+       const u8 *data = &sequence->data[0];
+       u8 current_id;
+       u16 current_size;
+       int index = 0;
+       int i;
+
+       for (i = 0; i < MAX_MIPI_CONFIGURATIONS && index + 3 < total; i++) {
+               current_id = *(data + index);
+               index++;
+
+               current_size = *((const u16 *)(data + index));
+               index += 2;
+
+               if (index + current_size > total) {
+                       DRM_ERROR("Invalid sequence block\n");
+                       return NULL;
+               }
+
+               if (current_id == panel_id) {
+                       *seq_size = current_size;
+                       return data + index;
+               }
+
+               index += current_size;
+       }
+
+       DRM_ERROR("Sequence block detected but no valid configuration\n");
+
+       return NULL;
+}
+
 static void
 parse_mipi_sequence(struct drm_i915_private *dev_priv,
                    const struct bdb_header *bdb)
 {
        const struct bdb_mipi_sequence *sequence;
        const u8 *seq_data;
+       u16 seq_size;
        u8 *data;
        u16 block_size;
-       int i, panel_id, seq_size;
 
        /* Only our generic panel driver uses the sequence block. */
        if (dev_priv->vbt.dsi.panel_id != MIPI_DSI_GENERIC_PANEL_ID)
@@ -853,40 +890,11 @@ parse_mipi_sequence(struct drm_i915_private *dev_priv,
         */
        dev_priv->vbt.dsi.seq_version = sequence->version;
 
-       seq_data = &sequence->data[0];
-
-       /*
-        * sequence block is variable length and hence we need to parse and
-        * get the sequence data for specific panel id
-        */
-       for (i = 0; i < MAX_MIPI_CONFIGURATIONS; i++) {
-               panel_id = *seq_data;
-               seq_size = *((u16 *) (seq_data + 1));
-               if (panel_id == panel_type)
-                       break;
-
-               /* skip the sequence including seq header of 3 bytes */
-               seq_data = seq_data + 3 + seq_size;
-               if ((seq_data - &sequence->data[0]) > block_size) {
-                       DRM_ERROR("Sequence start is beyond sequence block size, corrupted sequence block\n");
-                       return;
-               }
-       }
-
-       if (i == MAX_MIPI_CONFIGURATIONS) {
-               DRM_ERROR("Sequence block detected but no valid configuration\n");
+       seq_data = find_panel_sequence_block(sequence, panel_type, &seq_size);
+       if (!seq_data)
                return;
-       }
-
-       /* check if found sequence is completely within the sequence block
-        * just being paranoid */
-       if (seq_size > block_size) {
-               DRM_ERROR("Corrupted sequence/size, bailing out\n");
-               return;
-       }
 
-       /* skip the panel id(1 byte) and seq size(2 bytes) */
-       dev_priv->vbt.dsi.data = kmemdup(seq_data + 3, seq_size, GFP_KERNEL);
+       dev_priv->vbt.dsi.data = kmemdup(seq_data, seq_size, GFP_KERNEL);
        if (!dev_priv->vbt.dsi.data)
                return;