This makes for more questions that answers, but lets brainstorm a bit to see if it produces a new idea:
1) In the 1.9.3 image, what exactly is where the pointer points (I could get this myself by looking with a hex editor the ROM, but I’m a brick when it comes to understanding binary or code. As an absolute I NEED INSTANT GRATIFICATION procastinator guy, I get easily frustrated with anything related to programming)?
Basically, on the original image it should be the beginning of the Microcode, but since you’re relocating it somewhere else with the pointer staying the same, it should probabily be a random part of the GOP code, which for the Microcode loader should be garbage data. I’m surprised that loading that works at all, which carries me to the next question…
2) What happens if the -apparently optional- Microcode in the ROM is random, garbage data?
Based on the 1.9.3 image results, I would think that the Microcode loader should validate it or something, and may decide to skip loading it, instead of outright crashing. I suppose that there should be either a hardcoded Microcode in the GPU, or maybe it works with no Microcode at all. It should be too much coincidence if the garbage data where the old pointer points is somehow compatible enough to get the GPU running.
3) Is possible to ask the GPU which Microcode version is running?
As far that I know based on CPUs, Microcodes are versioned, and some low level Software like the BIOS can tell you which Microcode version a CPU is running. Linux and Windows also got specialized kernel-level modules to check the current version and load newer Microcode if available during boot, since it was deemed to be easier for this to be done by the OS rather that needing a BIOS update. However, when it comes to GPUs, I don’t have idea about how much flexibility you have to deal with the Microcode.
4) If the answer to point 3 is a bold No, what alternative methods are available to know if a Microcode has been loaded at all? Is possible to dump the running Microcode to compare it to what is on the ROM (Or an embedded hardcoded Microcode)?
Also based on my understanding on CPUs (That comes from horror histories like this one), during the 386 era, which was before a formal way to ask the Processor what it was existed (The 486 CPUID instruction), developers of Processor identification applications relied on trying to reproduce know bugs of each Processor to figure out by trial-and-error what the Processor could be, as a certain bug or combination of them was usually exclusive to a particular Processor vendor/model/revision. Since in the same way that there are no changelog between different AMD GOP versions, I doubt that we know specific Microcode differences, I don’t think that we have the required level of detail available for such alternative. At least it would require to know how a Juniper with no Microcode/default hardcoded Microcode behaves vs loading the one provided by the ROM one. As is hard to believe that the GPU actually works with garbage Microcode, I would think that it doesn’t get loaded at all in the 1.9.3 ROM, but does in the Image3. It could be worth to check that.
5) Is the Microcode in the ROM loaded by the GPU when it gets initialized during POST? Can the Drivers also update it when the OS boots?
All these could further complicate testing. Even further complicated because every generation or specific GPU could behave differently to the others…
My belief at this point is that since the two Images that worked are those where the Microcode pointer is intact (Regardless if the Microcode is valid or garbage), then either it is mean to be a static value, or at least on the Junipers is not a simple pointer, and may be validated or something by another value. I suppose that some viable tests would be checking with two sets of ROMs, one set where all the Images have the intact Microcode pointer and point to either the correct Microcode or garbage data, and another where the microcode pointer changes to match it. I suppose that the first set will always work, and the second will never do so. This way we will know if that pointer is actually a magic number of sorts, or whatever else that shouldn’t be modified at all. If you want to mythbust what happens with Junipers, I think is worth to identify if that pointer is the magic culprit before any further testing.
The thing is that with no changelog or detailed info about what changes are in the AMD GOP, or what bugfixes the Microcode has (Or if the Drivers update it later during boot, so it means that having the Microcode available at POST may be rather redundant. In that case, having the latest GOP would be certainly better), is a bit too hard to figure out what one of those two choices is better. Its summarized pretty much as "choose between option 1 that works, or option 2 that also works". With no pros or cons, I become indecise, then my brain BSODs. As such, I propose a built-in RNG based solution to workaround my edge case…
Talking seriously, I can feel the pain of having loads of different AMD GOP versions and not knowing what the functional differences are due to lack of manufacturer information to know which one is better in different scenarios…
QEMU users should probabily be the easiest to deal with of all your tool users. Since you aren’t limited by the ROM size in this scenario, the straightforward way would be if you merely append the GOP at the end of the file, change the pointers required to reference the VBIOS tables, and call it a day. You don’t need to be creative with tricks like Microcode relocation, rearrange existing code, nor touch anything else from the original ROM to make room for the GOP. I’m nearly absolutely confident and extremely optimistic that this should work in a near universal way, with the massive advantage of no risk of breaking what is already there. Where I’m not sure is how OVMF behaves, or if it has quirks that you should be aware of.
Based on what I know about ROM images (I researched that topic a bit out of curiosity when I begun exploring if modding the VBIOS for adding the GOP was possible. Not much later I discovered your tool :D), inside a ROM you can have multiple images that are separated by an header. There is the legacy header, which is called "Standard PCI Expansion ROM Header", and another introduced by the UEFI spec which is the "EFI PCI Expansion ROM Header". As the vanilla OVMF is a pure EFI Firmware, it should only look for the second one and ignore everything else. What I don’t know is how a Firmware scans the ROM to figure out where the headers are, if they do a full linear scan, or if they look at specific boundaries (Say, if there are partitions with a specific size like 32/64/128 KiB or so, and it only looks for headers at those points). I can ask in OVMF Mailing List if you need any specific info, but chances are that it either behaves strictly to spec, or is relaxed enough to be compatible with mainstream Motherboard Firmwares.
As always, with a few more QEMU testers this would be much more fun. Your tool is close to perfection, it extended the life of entire generation of cards.