]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm/amd/display: Add additional checks for PSP footer size
authorOvidiu Bunea <ovidiu.bunea@amd.com>
Tue, 11 Nov 2025 20:44:08 +0000 (15:44 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 8 Dec 2025 18:56:37 +0000 (13:56 -0500)
[WHY & HOW]
Newer ASICs have different PSP footer sizes which lead to driver
failing to locate the DMCUB FW meta info, which in turn causes
improper DMCUB FW loading and causes DMCUB to crash.

Add support for custom PSP footer sizes and check 512B by default
as well.

Reviewed-by: Charlene Liu <charlene.liu@amd.com>
Signed-off-by: Ovidiu Bunea <ovidiu.bunea@amd.com>
Signed-off-by: Alex Hung <alex.hung@amd.com>
Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
drivers/gpu/drm/amd/display/dc/dc.h
drivers/gpu/drm/amd/display/dmub/dmub_srv.h
drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c

index 740711ac1037ce14eda589324a26f312f8560da6..219c537977524580e47bdb9daa98ddf59d218228 100644 (file)
@@ -152,12 +152,6 @@ MODULE_FIRMWARE(FIRMWARE_DCN_36_DMUB);
 #define FIRMWARE_DCN_401_DMUB "amdgpu/dcn_4_0_1_dmcub.bin"
 MODULE_FIRMWARE(FIRMWARE_DCN_401_DMUB);
 
-/* Number of bytes in PSP header for firmware. */
-#define PSP_HEADER_BYTES 0x100
-
-/* Number of bytes in PSP footer for firmware. */
-#define PSP_FOOTER_BYTES 0x100
-
 /**
  * DOC: overview
  *
@@ -1298,15 +1292,14 @@ static int dm_dmub_hw_init(struct amdgpu_device *adev)
 
        fw_inst_const = dmub_fw->data +
                        le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
-                       PSP_HEADER_BYTES;
+                       PSP_HEADER_BYTES_256;
 
        fw_bss_data = dmub_fw->data +
                      le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
                      le32_to_cpu(hdr->inst_const_bytes);
 
        /* Copy firmware and bios info into FB memory. */
-       fw_inst_const_size = le32_to_cpu(hdr->inst_const_bytes) -
-                            PSP_HEADER_BYTES - PSP_FOOTER_BYTES;
+       fw_inst_const_size = adev->dm.fw_inst_size;
 
        fw_bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
 
@@ -2437,9 +2430,11 @@ static void amdgpu_dm_dmub_reg_write(void *ctx, uint32_t address,
 static int dm_dmub_sw_init(struct amdgpu_device *adev)
 {
        struct dmub_srv_create_params create_params;
+       struct dmub_srv_fw_meta_info_params fw_meta_info_params;
        struct dmub_srv_region_params region_params;
        struct dmub_srv_region_info region_info;
        struct dmub_srv_memory_params memory_params;
+       struct dmub_fw_meta_info fw_info;
        struct dmub_srv_fb_info *fb_info;
        struct dmub_srv *dmub_srv;
        const struct dmcub_firmware_header_v1_0 *hdr;
@@ -2547,22 +2542,37 @@ static int dm_dmub_sw_init(struct amdgpu_device *adev)
                return -EINVAL;
        }
 
+       /* Extract the FW meta info. */
+       memset(&fw_meta_info_params, 0, sizeof(fw_meta_info_params));
+
+       fw_meta_info_params.inst_const_size = le32_to_cpu(hdr->inst_const_bytes) -
+                                             PSP_HEADER_BYTES_256;
+       fw_meta_info_params.bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
+       fw_meta_info_params.fw_inst_const = adev->dm.dmub_fw->data +
+                                           le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
+                                           PSP_HEADER_BYTES_256;
+       fw_meta_info_params.fw_bss_data = region_params.bss_data_size ? adev->dm.dmub_fw->data +
+                                         le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
+                                         le32_to_cpu(hdr->inst_const_bytes) : NULL;
+       fw_meta_info_params.custom_psp_footer_size = 0;
+
+       status = dmub_srv_get_fw_meta_info_from_raw_fw(&fw_meta_info_params, &fw_info);
+       if (status != DMUB_STATUS_OK) {
+               /* Skip returning early, just log the error. */
+               drm_err(adev_to_drm(adev), "Error getting DMUB FW meta info: %d\n", status);
+               // return -EINVAL;
+       }
+
        /* Calculate the size of all the regions for the DMUB service. */
        memset(&region_params, 0, sizeof(region_params));
 
-       region_params.inst_const_size = le32_to_cpu(hdr->inst_const_bytes) -
-                                       PSP_HEADER_BYTES - PSP_FOOTER_BYTES;
-       region_params.bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
+       region_params.inst_const_size = fw_meta_info_params.inst_const_size;
+       region_params.bss_data_size = fw_meta_info_params.bss_data_size;
        region_params.vbios_size = adev->bios_size;
-       region_params.fw_bss_data = region_params.bss_data_size ?
-               adev->dm.dmub_fw->data +
-               le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
-               le32_to_cpu(hdr->inst_const_bytes) : NULL;
-       region_params.fw_inst_const =
-               adev->dm.dmub_fw->data +
-               le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
-               PSP_HEADER_BYTES;
+       region_params.fw_bss_data = fw_meta_info_params.fw_bss_data;
+       region_params.fw_inst_const = fw_meta_info_params.fw_inst_const;
        region_params.window_memory_type = window_memory_type;
+       region_params.fw_info = (status == DMUB_STATUS_OK) ? &fw_info : NULL;
 
        status = dmub_srv_calc_region_info(dmub_srv, &region_params,
                                           &region_info);
@@ -2609,6 +2619,7 @@ static int dm_dmub_sw_init(struct amdgpu_device *adev)
        }
 
        adev->dm.bb_from_dmub = dm_dmub_get_vbios_bounding_box(adev);
+       adev->dm.fw_inst_size = fw_meta_info_params.inst_const_size;
 
        return 0;
 }
index bd0403005f370e11e65274a5cb114c0543c702a0..ab363f2f6d479e219b70a05ae62c13a82be2db2c 100644 (file)
@@ -413,6 +413,13 @@ struct amdgpu_display_manager {
         */
        uint32_t dmcub_fw_version;
 
+       /**
+        * @fw_inst_size:
+        *
+        * Size of the firmware instruction buffer.
+        */
+       uint32_t fw_inst_size;
+
        /**
         * @cgs_device:
         *
index 37d301e82d39c67eb717deb9c0ac72db5c394a0a..57df6dc81041b54eafd022a4a15dd6e32329cabd 100644 (file)
@@ -1188,6 +1188,7 @@ struct dc_debug_options {
        short auxless_alpm_lfps_t1t2_offset_us;
        bool disable_stutter_for_wm_program;
        bool enable_block_sequence_programming;
+       uint32_t custom_psp_footer_size;
 };
 
 
index 9d0168986fe7a5ac9863a3c20faa5a2ac84bd62d..12c1f9f7115a5e51107ddb53a786b9a55767a2b8 100644 (file)
@@ -72,6 +72,9 @@
 /* Default tracebuffer size if meta is absent. */
 #define DMUB_TRACE_BUFFER_SIZE (64 * 1024)
 
+#define PSP_HEADER_BYTES_256 0x100 // 256 bytes
+#define PSP_FOOTER_BYTES_256 0x100 // 256 bytes
+
 /* Forward declarations */
 struct dmub_srv;
 struct dmub_srv_common_regs;
@@ -227,6 +230,23 @@ struct dmub_srv_region_params {
        const uint8_t *fw_inst_const;
        const uint8_t *fw_bss_data;
        const enum dmub_window_memory_type *window_memory_type;
+       const struct dmub_fw_meta_info *fw_info;
+};
+
+/**
+ * struct dmub_srv_fw_meta_info_params - params used for fetching fw meta info from fw_image
+ * @inst_const_size: size of the fw inst const section
+ * @bss_data_size: size of the fw bss data section
+ * @fw_inst_const: raw firmware inst const section
+ * @fw_bss_data: raw firmware bss data section
+ * @custom_psp_footer_size: custom psp footer size to use when indexing for fw meta info
+ */
+struct dmub_srv_fw_meta_info_params {
+       uint32_t inst_const_size;
+       uint32_t bss_data_size;
+       const uint8_t *fw_inst_const;
+       const uint8_t *fw_bss_data;
+       uint32_t custom_psp_footer_size;
 };
 
 /**
@@ -249,6 +269,7 @@ struct dmub_srv_region_info {
        uint32_t gart_size;
        uint8_t num_regions;
        struct dmub_region regions[DMUB_WINDOW_TOTAL];
+       uint32_t verified_psp_footer_size;
 };
 
 /**
@@ -1098,4 +1119,16 @@ enum dmub_status dmub_srv_update_inbox_status(struct dmub_srv *dmub);
  */
 bool dmub_srv_get_preos_info(struct dmub_srv *dmub);
 
+/**
+ * dmub_srv_get_fw_meta_info_from_raw_fw() - Fetch firmware metadata info from raw firmware image
+ * @params: parameters for fetching firmware metadata info
+ * @fw_info_out: output buffer for firmware metadata info
+ *
+ * Return:
+ *   DMUB_STATUS_OK - success
+ *   DMUB_STATUS_INVALID - no FW meta info found
+ */
+enum dmub_status dmub_srv_get_fw_meta_info_from_raw_fw(struct dmub_srv_fw_meta_info_params *params,
+                                                      struct dmub_fw_meta_info *fw_info_out);
+
 #endif /* _DMUB_SRV_H_ */
index a6ae1d2e9685bf0e4b48b5ec154b6027b45ffdd0..be893531ae7da83a0c29a480c824b37cc8371156 100644 (file)
@@ -134,7 +134,7 @@ dmub_get_fw_meta_info_from_blob(const uint8_t *blob, uint32_t blob_size, uint32_
 }
 
 static const struct dmub_fw_meta_info *
-dmub_get_fw_meta_info(const struct dmub_srv_region_params *params)
+dmub_get_fw_meta_info(const struct dmub_srv_fw_meta_info_params *params)
 {
        const struct dmub_fw_meta_info *info = NULL;
 
@@ -159,6 +159,46 @@ dmub_get_fw_meta_info(const struct dmub_srv_region_params *params)
        return info;
 }
 
+enum dmub_status
+dmub_srv_get_fw_meta_info_from_raw_fw(struct dmub_srv_fw_meta_info_params *params,
+                                     struct dmub_fw_meta_info *fw_info_out)
+{
+       const struct dmub_fw_meta_info *fw_info = NULL;
+       uint32_t inst_const_size_temp = params->inst_const_size;
+
+       /* First try custom psp footer size, if present */
+       if (params->custom_psp_footer_size) {
+               params->inst_const_size -= params->custom_psp_footer_size;
+               fw_info = dmub_get_fw_meta_info(params);
+               if (fw_info) {
+                       memcpy(fw_info_out, fw_info, sizeof(*fw_info));
+                       return DMUB_STATUS_OK;
+               }
+               params->inst_const_size = inst_const_size_temp;
+       }
+
+       /* Try 256-byte psp footer size */
+       params->inst_const_size -= PSP_FOOTER_BYTES_256;
+       fw_info = dmub_get_fw_meta_info(params);
+       if (fw_info) {
+               memcpy(fw_info_out, fw_info, sizeof(*fw_info));
+               return DMUB_STATUS_OK;
+       }
+
+       /* Try 512-byte psp footer size - final attempt */
+       params->inst_const_size -= PSP_FOOTER_BYTES_256; // 256 bytes already subtracted, subtract 256 again
+       fw_info = dmub_get_fw_meta_info(params);
+       if (fw_info) {
+               memcpy(fw_info_out, fw_info, sizeof(*fw_info));
+               return DMUB_STATUS_OK;
+       }
+
+       /* Restore original inst_const_size and subtract default PSP footer size - default behaviour */
+       params->inst_const_size = inst_const_size_temp - PSP_FOOTER_BYTES_256;
+
+       return DMUB_STATUS_INVALID;
+}
+
 static bool dmub_srv_hw_setup(struct dmub_srv *dmub, enum dmub_asic asic)
 {
        struct dmub_srv_hw_funcs *funcs = &dmub->hw_funcs;
@@ -524,7 +564,6 @@ enum dmub_status
                const struct dmub_srv_region_params *params,
                struct dmub_srv_region_info *out)
 {
-       const struct dmub_fw_meta_info *fw_info;
        uint32_t fw_state_size = DMUB_FW_STATE_SIZE;
        uint32_t trace_buffer_size = DMUB_TRACE_BUFFER_SIZE;
        uint32_t shared_state_size = DMUB_FW_HEADER_SHARED_STATE_SIZE;
@@ -538,14 +577,12 @@ enum dmub_status
 
        out->num_regions = DMUB_NUM_WINDOWS;
 
-       fw_info = dmub_get_fw_meta_info(params);
-
-       if (fw_info) {
-               memcpy(&dmub->meta_info, fw_info, sizeof(*fw_info));
+       if (params->fw_info) {
+               memcpy(&dmub->meta_info, params->fw_info, sizeof(*params->fw_info));
 
-               fw_state_size = fw_info->fw_region_size;
-               trace_buffer_size = fw_info->trace_buffer_size;
-               shared_state_size = fw_info->shared_state_size;
+               fw_state_size = params->fw_info->fw_region_size;
+               trace_buffer_size = params->fw_info->trace_buffer_size;
+               shared_state_size = params->fw_info->shared_state_size;
 
                /**
                 * If DM didn't fill in a version, then fill it in based on
@@ -555,7 +592,7 @@ enum dmub_status
                 * pass during creation.
                 */
                if (dmub->fw_version == 0)
-                       dmub->fw_version = fw_info->fw_version;
+                       dmub->fw_version = params->fw_info->fw_version;
        }
 
        window_sizes[DMUB_WINDOW_0_INST_CONST] = params->inst_const_size;