(This is a long e-mail. I apologize, but I feel this is important to cover).
This week I've been working with LinuxBIOSv3 to try to get over the XIP initram problem. For those that tuning in late, here's the recap:
There are some symbols that we use that we want to define once, and use in different segments (such as printk). This is very easy to do for segments that are loaded and run at a particular location in memory, by passing in the symbol list from stage0 to LD through the -R command line option, from which LD figures the right relative offsets.
The problem is, this is only easy if you know where your code is executing, and pass the appropriate fu into LD. For most systems, we need to run at least the initram segment in place on the ROM which are not always located at a known location - there can be multiple initram blocks, and LAR can put them anywhere it wishes. Needless to say, this breaks things badly when we try to call functions in the bootblock with a relative offset that walks into the weeds.
So my quest this week was to try to find a list of solutions that we can consider as a group. The only criteria I had for a solution is that it needed to maintain the independence of the individual segments (i.e. - I couldn't just cheat and link the bootblock and initram together). Any other legitimate solution was on the table, though obviously, the more simple and obvious the better. Here's what I came up with:
----------------------------------------- Solution 1 - fixed locations
the -R trick to LD works for the XIP modules too, assuming that you link it at the right address where it will execute in the ROM. The very simplest way to do this would be to define fixed locations for the initram(s) and feed those in to to LD (through a platform configuration). This would be remotely similar to v2.
PROS: Easy CONS: Completely breaks the LAR model, wastes ROM space, and really breaks the whole v3 concept.
Solution 2 - relink the image
Like above, we use -R to pass in the symbols from stage0.
we link the image at an arbitrary address (say, 0x0), and build it into the LAR. We can then ask LAR where it put the segment, and then relink the segment at that address, and replace it in the LAR. There may be other things we can do with LAR to make this easier - perhaps just pass in the sizes of the segments to LAR and have it return the offsets without actually constructing an archive, perhaps?
PROS: Fits in the v3 model CONS: Complex, and relies on some scripts to do the right thing with LAR. Also, its confusing to relink twice.
Solution 3 - postlinker
Again, we use -R to pass in the symbols from stage0 and link the segment to 0x0. But instead of re-linking when we learn the address, we run a special utility that will read the relocation tables in the ELF and update the addresses in the ELF. This should be somewhat more lightweight then re-linking. The same problems with LAR still exist here - the key is finding out the address we need to link to.
PROS: Fits into the v3 model, less complex then relinking CONS: Finding the address from LAR will be a challenge. Plus its another custom utility to maintain.
Solution 3.5 - postlinker + symbol table
One CON for all of these is that each solution requires us to have the stage0.o ELF file laying around. To me, that sort of defeats the purpose of independence. So the 4th option is similar to the 3rd (and suffers from the same problems), but instead of using -R to pass the symbols to LD during the link stage, we leave the shared symbols undefined. Then, we grab a list of shared symbols from stage0.o (which could easily be a "published" list, and feed that into the custom script from above, which could fill in the undefined shared symbols.
PROS: One could build an initram completely separate from stage0 (which is the goal). CONS: An extra stage of abstraction to get the symbol table. It would be difficult to ensure that the symbol table matches the current bootblock. Finding the address from the LAR remains a challenge. Custom utility. -----------------------------------------
I vote for #3 and possibly #4. I've spent some time writing the very custom app described above, and I used it with a little manual trickery to make the Geode boot through the initram stage (I'll send it out in a separate e-mail). Figuring out the right address from LAR will be the tricky part - for that i really don't have a good idea (I build the lar with the un-modified initram, listed the lar, figured out the address, ran my tool, and re-lared the new initram. I don't recommend that method - too much possibility for failure).
So, why am I telling you this? Because I think the time is ripe for a discussion - we know what needs to be done code wise, and we have a list of options at our disposal, but the most important thing we need is consensus. I don't want this to be some crazy scheme cooked up late at night and snuck into the code base when nobody is looking.
Thoughts? Jordan
-- Jordan Crouse Systems Software Development Engineer Advanced Micro Devices, Inc.
* Jordan Crouse jordan.crouse@amd.com [071006 00:28]:
This week I've been working with LinuxBIOSv3 to try to get over the XIP initram problem. For those that tuning in late, here's the recap:
There are some symbols that we use that we want to define once, and use in different segments (such as printk). This is very easy to do for segments that are loaded and run at a particular location in memory, by passing in the symbol list from stage0 to LD through the -R command line option, from which LD figures the right relative offsets.
The problem is, this is only easy if you know where your code is executing, and pass the appropriate fu into LD. For most systems, we need to run at least the initram segment in place on the ROM which are not always located at a known location - there can be multiple initram blocks, and LAR can put them anywhere it wishes. Needless to say, this breaks things badly when we try to call functions in the bootblock with a relative offset that walks into the weeds.
I tried playing with the same thing and I came up with the following hack (attachment) .. The big issue is it doesnt work like that yet. The idea is to take the decision of the jump address from the compiler+linker because we already know it.
Can something like this work a all?