| From: Mark Kettenis <kettenis@openbsd.org> |
| Date: Sun, 21 Jul 2013 16:44:09 -0400 |
| Subject: drm/radeon: fix combios tables on older cards |
| |
| commit cef1d00cd56f600121ad121875655ad410a001b8 upstream. |
| |
| Noticed that my old Radeon 7500 hung after printing |
| |
| drm: GPU not posted. posting now... |
| |
| when it wasn't selected as the primary card the BIOS. Some digging |
| revealed that it was hanging in combios_parse_mmio_table() while |
| parsing the ASIC INIT 3 table. Looking at the BIOS ROM for the card, |
| it becomes obvious that there is no ASIC INIT 3 table in the BIOS. |
| The code is just processing random garbage. No surprise it hangs! |
| |
| Why do I say that there is no ASIC INIT 3 table is the BIOS? This |
| table is found through the MISC INFO table. The MISC INFO table can |
| be found at offset 0x5e in the COMBIOS header. But the header is |
| smaller than that. The COMBIOS header starts at offset 0x126. The |
| standard PCI Data Structure (the bit that starts with 'PCIR') lives at |
| offset 0x180. That means that the COMBIOS header can not be larger |
| than 0x5a bytes and therefore cannot contain a MISC INFO table. |
| |
| I looked at a dozen or so BIOS images, some my own, some downloaded from: |
| |
| <http://www.techpowerup.com/vgabios/index.php?manufacturer=ATI&page=1> |
| |
| It is fairly obvious that the size of the COMBIOS header can be found |
| at offset 0x6 of the header. Not sure if it is a 16-bit number or |
| just an 8-bit number, but that doesn't really matter since the tables |
| seems to be always smaller than 256 bytes. |
| |
| So I think combios_get_table_offset() should check if the requested |
| table is present. This can be done by checking the offset against the |
| size of the header. See the diff below. The diff is against the WIP |
| OpenBSD codebase that roughly corresponds to Linux 3.8.13 at this |
| point. But I don't think this bit of the code changed much since |
| then. |
| |
| For what it is worth: |
| |
| Signed-off-by: Mark Kettenis <kettenis@openbsd.org> |
| Signed-off-by: Alex Deucher <alexander.deucher@amd.com> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| drivers/gpu/drm/radeon/radeon_combios.c | 145 +++++++++----------------------- |
| 1 file changed, 41 insertions(+), 104 deletions(-) |
| |
| --- a/drivers/gpu/drm/radeon/radeon_combios.c |
| +++ b/drivers/gpu/drm/radeon/radeon_combios.c |
| @@ -147,7 +147,7 @@ static uint16_t combios_get_table_offset |
| enum radeon_combios_table_offset table) |
| { |
| struct radeon_device *rdev = dev->dev_private; |
| - int rev; |
| + int rev, size; |
| uint16_t offset = 0, check_offset; |
| |
| if (!rdev->bios) |
| @@ -156,174 +156,106 @@ static uint16_t combios_get_table_offset |
| switch (table) { |
| /* absolute offset tables */ |
| case COMBIOS_ASIC_INIT_1_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0xc); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0xc; |
| break; |
| case COMBIOS_BIOS_SUPPORT_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x14); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x14; |
| break; |
| case COMBIOS_DAC_PROGRAMMING_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x2a); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x2a; |
| break; |
| case COMBIOS_MAX_COLOR_DEPTH_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x2c); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x2c; |
| break; |
| case COMBIOS_CRTC_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x2e); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x2e; |
| break; |
| case COMBIOS_PLL_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x30); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x30; |
| break; |
| case COMBIOS_TV_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x32); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x32; |
| break; |
| case COMBIOS_DFP_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x34); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x34; |
| break; |
| case COMBIOS_HW_CONFIG_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x36); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x36; |
| break; |
| case COMBIOS_MULTIMEDIA_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x38); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x38; |
| break; |
| case COMBIOS_TV_STD_PATCH_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x3e); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x3e; |
| break; |
| case COMBIOS_LCD_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x40); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x40; |
| break; |
| case COMBIOS_MOBILE_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x42); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x42; |
| break; |
| case COMBIOS_PLL_INIT_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x46); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x46; |
| break; |
| case COMBIOS_MEM_CONFIG_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x48); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x48; |
| break; |
| case COMBIOS_SAVE_MASK_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x4a); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x4a; |
| break; |
| case COMBIOS_HARDCODED_EDID_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x4c); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x4c; |
| break; |
| case COMBIOS_ASIC_INIT_2_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x4e); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x4e; |
| break; |
| case COMBIOS_CONNECTOR_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x50); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x50; |
| break; |
| case COMBIOS_DYN_CLK_1_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x52); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x52; |
| break; |
| case COMBIOS_RESERVED_MEM_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x54); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x54; |
| break; |
| case COMBIOS_EXT_TMDS_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x58); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x58; |
| break; |
| case COMBIOS_MEM_CLK_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x5a); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x5a; |
| break; |
| case COMBIOS_EXT_DAC_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x5c); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x5c; |
| break; |
| case COMBIOS_MISC_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x5e); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x5e; |
| break; |
| case COMBIOS_CRT_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x60); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x60; |
| break; |
| case COMBIOS_INTEGRATED_SYSTEM_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x62); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x62; |
| break; |
| case COMBIOS_COMPONENT_VIDEO_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x64); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x64; |
| break; |
| case COMBIOS_FAN_SPEED_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x66); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x66; |
| break; |
| case COMBIOS_OVERDRIVE_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x68); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x68; |
| break; |
| case COMBIOS_OEM_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x6a); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x6a; |
| break; |
| case COMBIOS_DYN_CLK_2_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x6c); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x6c; |
| break; |
| case COMBIOS_POWER_CONNECTOR_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x6e); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x6e; |
| break; |
| case COMBIOS_I2C_INFO_TABLE: |
| - check_offset = RBIOS16(rdev->bios_header_start + 0x70); |
| - if (check_offset) |
| - offset = check_offset; |
| + check_offset = 0x70; |
| break; |
| /* relative offset tables */ |
| case COMBIOS_ASIC_INIT_3_TABLE: /* offset from misc info */ |
| @@ -439,11 +371,16 @@ static uint16_t combios_get_table_offset |
| } |
| break; |
| default: |
| + check_offset = 0; |
| break; |
| } |
| |
| - return offset; |
| + size = RBIOS8(rdev->bios_header_start + 0x6); |
| + /* check absolute offset tables */ |
| + if (table < COMBIOS_ASIC_INIT_3_TABLE && check_offset && check_offset < size) |
| + offset = RBIOS16(rdev->bios_header_start + check_offset); |
| |
| + return offset; |
| } |
| |
| bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) |