DF-4601 / construct_order.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | /* * DF-4601 demonstrator: dce112_compressor_construct() initialization-order * defect, mirrored statement-for-statement from * sys/dev/drm/amd/display/dc/dce112/dce112_compressor.c:787-829 (construct) * and :653-724 (program_lpt_control switch). * * Proves, without AMD hardware: * (1) the `memory_bus_width == 64` single-DRAM-channel guard at :802 is * evaluated while the field is still 0 (kzalloc) โ it is assigned from * asic_id.vram_width only afterwards, at :810 โ so LPT_SUPPORT is never * cleared on 64-bit-bus ASICs (dead guard); * (2) `lpt_channels_num = 1` at :797 is unconditionally overwritten to 0 at * :818, so the only valid `case 1:` of the * dce112_compressor_program_lpt_control() switch (:675-691) can never * execute โ LOW_POWER_TILING_MODE is never programmed by this driver. * * Build & run on the host (no GPU required): cc -O2 -o construct_order construct_order.c && ./construct_order * Exit 0 = defect confirmed (both proofs hold). */ #include <stdint.h> #include <stdio.h> #include <string.h> /* --- mirror of struct compressor fields used by construct() --- */ struct options { struct { uint32_t FBC_SUPPORT:1, LPT_SUPPORT:1, DUMMY_BACKEND:1, CLK_GATING_DISABLED:1; } bits; uint32_t raw; }; struct compressor { struct options options; uint32_t memory_bus_width, banks_num, raw_size, channel_interleave_size, dram_channels_num, lpt_channels_num; }; struct dc_ctx { uint32_t vram_width; }; /* exact mirror of dce112_compressor.c:787-829 */ static void dce112_compressor_construct(struct compressor *compressor, struct dc_ctx *ctx) { /* kzalloc'd by dce112_compressor_create(): all fields start 0 */ compressor->options.raw = 0; compressor->options.bits.FBC_SUPPORT = 1; compressor->options.bits.LPT_SUPPORT = 1; /* For DCE 11 always use one DRAM channel for LPT */ compressor->lpt_channels_num = 1; /* :797 */ compressor->options.bits.DUMMY_BACKEND = 0; /* :802 โ BUG: memory_bus_width is still 0 here */ if (compressor->memory_bus_width == 64) compressor->options.bits.LPT_SUPPORT = 0; /* DEAD */ compressor->options.bits.CLK_GATING_DISABLED = 0; compressor->memory_bus_width = ctx->vram_width; /* :810 */ compressor->banks_num = 0; compressor->raw_size = 0; compressor->channel_interleave_size = 0; compressor->dram_channels_num = 0; compressor->lpt_channels_num = 0; /* :818 wipes :797 */ } /* exact mirror of the switch at dce112_compressor.c:675-691 */ static int lpt_mode_programmed(struct compressor *compressor) { switch (compressor->lpt_channels_num) { case 1: return 1; /* LOW_POWER_TILING_MODE would be set to 0 */ default: return 0; /* warning; mode keeps stale HW value */ } } int main(void) { struct dc_ctx ctx64 = { .vram_width = 64 }; /* 1-channel system */ struct compressor cp; int fail = 0; /* dce112_compressor_create() uses kzalloc: zeroed object */ memset(&cp, 0, sizeof(cp)); dce112_compressor_construct(&cp, &ctx64); /* Proof 1: the 64-bit-bus guard never fired */ if (cp.memory_bus_width == 64 && cp.options.bits.LPT_SUPPORT != 0) { printf("PROOF-1 OK: memory_bus_width==%u (64-bit bus), " "but LPT_SUPPORT still %u โ guard at :802 is dead " "(field assigned at :810, after the check)\n", cp.memory_bus_width, cp.options.bits.LPT_SUPPORT); } else { printf("PROOF-1 FAILED (guard fired?)\n"); fail = 1; } /* Proof 2: lpt_channels_num wiped to 0 -> switch case 1 dead */ if (cp.lpt_channels_num == 0 && !lpt_mode_programmed(&cp)) { printf("PROOF-2 OK: lpt_channels_num==%u at use โ " "case 1 at :678 unreachable, LOW_POWER_TILING_MODE " "never programmed (falls to default at :686)\n", cp.lpt_channels_num); } else { printf("PROOF-2 FAILED\n"); fail = 1; } /* Knock-on: with banks_num/raw_size/dram_channels_num all 0 (never * set anywhere in the tree), lpt_size_alignment() == 0, so * rows_per_channel stays 0 (:708) and the low_part alignment at * :500-505 is skipped โ enable_lpt() arms LPT with an all-default * config whenever FBC+LPT come up on eDP. */ printf("KNOCK-ON: lpt_size_alignment = %u * %u * %u = 0 -> " "ROWS_PER_CHAN=0, addr alignment skipped, yet " "LOW_POWER_TILING_ENABLE=1 at :645-650 when LPT comes up\n", cp.raw_size, cp.banks_num, cp.dram_channels_num); printf(fail ? "RESULT: NOT REPRODUCED\n" : "RESULT: DEFECT CONFIRMED\n"); return (fail); } |