On Sat, 6 Jun 2026, Chad Jablonski wrote:
Implement registers used for loading and reading microcode for the CCE
engine. Loading the microcode is the first step for any driver
implementing CCE. Reading, while not used by drivers, is very helpful for
any reverse engineering and testing work. The microcode is currently
stored but not used. This lays the groundwork for future RE work on the
microcode.
There's some quirky behavior around microcode reads that isn't
documented elsewhere. There appear to be two internal pointers, one for
reading and one for writing that can get out of sync. Comments in the
code expand on this.
Tested and validated against a Rage 128 Pro Ultra (PCI 1002:5446) and
Radeon QY (RV100) (PCI 1002:5159).
Signed-off-by: Chad Jablonski <[email protected]>
---
hw/display/ati.c | 61 ++++++++++++++++++++++++++++++++++++++++++++
hw/display/ati_int.h | 11 ++++++++
2 files changed, 72 insertions(+)
diff --git a/hw/display/ati.c b/hw/display/ati.c
index 826c856b1c..73fd5e7d23 100644
--- a/hw/display/ati.c
+++ b/hw/display/ati.c
@@ -579,6 +579,36 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr,
unsigned int size)
qemu_log_mask(LOG_GUEST_ERROR,
"Read from write-only register 0x%x\n", (unsigned)addr);
break;
+ /* r100: CP_ME_RAM_ADDR */
+ case PM4_MICROCODE_ADDR:
+ val = s->cce.microcode.addr;
+ break;
+ /* r100: CP_ME_RAM_RADDR */
+ case PM4_MICROCODE_RADDR:
+ /* Always returns 0. Tested on hardware. */
+ val = 0;
+ break;
+ /* r100: CP_ME_RAM_DATAH */
+ case PM4_MICROCODE_DATAH:
+ val = (s->cce.microcode.microcode[s->cce.microcode.raddr] >> 32) &
+ 0xffffffff;
I'm not sure we need to mask here as you shift out the low part and the
high part will be 0 so unless the compiler warns I'd just write >> 32 but
I'm not sure we need all of this anyway as I'm not sure any driver would
do more than just write the microcode and then never read it. So I'd only
bother to implement it if something is known to need it.
Regards,
BALATON Zoltan