[U-Boot] Question about relocation

Hello. I'm trying to make at91sam9261ek working again, but relocation overlaps the destinatin address.
In this board, u-boot runs already in RAM. This is exactly like the nhk8815 that I ported a few hours ago, but while there the IPL loaded our code at the beginning of RAM, in this case the IPL loaded us already at end of RAM.
Now, this is the printout of the situation:
U-Boot code: 23F00000 -> 23F36EFC BSS: -> 23F78570 monitor len: 00078570 ramsize: 04000000 TLB table at: 23ff0000 Top of RAM usable for U-Boot at: 23ff0000 Reserving 481k for U-Boot at: 23f64000 Reserving 180k for malloc() at: 23f37000 Reserving 24 Bytes for Board Info at: 23f36fe8 Reserving 144 Bytes for Global Data at: 23f36f58 New Stack Pointer is: 23f36f50 RAM Configuration: Bank #0: 20000000 64 MiB relocation Offset is: 00064000
Since "tor len: 00078570" and "relocation Offset is: 00064000" the system data-aborts during relocation, as code overwrites relocation tables.
What is the suggested solution? Changing the IPL is not acceptable for me, because it is an external tool used by everyone: everybody knows it loads and runs code at 23f0.0000.
Thanks /alessandro

Hi Alessandro,
Le 17/04/2011 03:19, Alessandro Rubini a écrit :
Hello. I'm trying to make at91sam9261ek working again, but relocation overlaps the destinatin address.
In this board, u-boot runs already in RAM. This is exactly like the nhk8815 that I ported a few hours ago, but while there the IPL loaded our code at the beginning of RAM, in this case the IPL loaded us already at end of RAM.
Now, this is the printout of the situation:
U-Boot code: 23F00000 -> 23F36EFC BSS: -> 23F78570 monitor len: 00078570 ramsize: 04000000 TLB table at: 23ff0000 Top of RAM usable for U-Boot at: 23ff0000 Reserving 481k for U-Boot at: 23f64000 Reserving 180k for malloc() at: 23f37000 Reserving 24 Bytes for Board Info at: 23f36fe8 Reserving 144 Bytes for Global Data at: 23f36f58 New Stack Pointer is: 23f36f50 RAM Configuration: Bank #0: 20000000 64 MiB relocation Offset is: 00064000
Since "tor len: 00078570" and "relocation Offset is: 00064000" the system data-aborts during relocation, as code overwrites relocation tables.
What is the suggested solution? Changing the IPL is not acceptable for me, because it is an external tool used by everyone: everybody knows it loads and runs code at 23f0.0000.
Thanks /alessandro
IIUC, the issue is with the moving of the image. One solution I see, and which is not 100% perfect, would be that the copy code perform either a beginning-to-end copy or an end-to-beginning copy depending on the source and destination addresses and size.
So, when copying say 256K from 0x01000000 to 0x03FC0000, the code could do the copy either way, beginning-to end (0x01000000 to 0x0103FFFF) or end-to-beginning (0x0103FFFF to 0x01000000); but when copying from 0x03FB0000 to 0x03FC0000, only the end-to-beginning copy would be possible.
This still leaves holes in that copying U-Boot from RAM to RAM where the source is *really* near the end might actually trash the copy code itself. Maybe the instruction cache will help, but I won't bet on this without heavy testing.
Amicalement,

beginning-to-end copy or an end-to-beginning copy depending on the source and destination addresses and size.
Ori, simply, end-to-beginning when moving up. Or always end-to-beginning since we are expected to always move up (upper than the target address it can't run).
It is safer, but not safe. Also, it's difficult to accomplish bcause copying and fixing addresses is done ina single run, so the relocation tables are read from the old area. Possibly relocatng first and copying then.
Or, easier: if we are already high enough to overlap, don't relocate at all. If it's acceptable, I'll patch for taht.
thanks /alessandro

Le 17/04/2011 19:57, Alessandro Rubini a écrit :
beginning-to-end copy or an end-to-beginning copy depending on the source and destination addresses and size.
Ori, simply, end-to-beginning when moving up. Or always end-to-beginning since we are expected to always move up (upper than the target address it can't run).
Since the 'issue' is caused by the code assuming one direction, I'd prefer it not to assume the other now; I prefer choosing end-to-beginning if target is dest than source, beginning-to-end otherwise.
It is safer, but not safe. Also, it's difficult to accomplish bcause copying and fixing addresses is done ina single run, so the relocation tables are read from the old area. Possibly relocatng first and copying then.
Actually no, copying and fixing is not done in a single run. There is the copying of the text+data+const area, then the fixing which runs through the relocation table area; they are different.
Or, easier: if we are already high enough to overlap, don't relocate at all. If it's acceptable, I'll patch for taht.
But then comes the question of how enough is "high enough". :)
thanks /alessandro
Amicalement,

Ori, simply, end-to-beginning when moving up. Or always end-to-beginning since we are expected to always move up (upper than the target address it can't run).
Since the 'issue' is caused by the code assuming one direction, I'd prefer it not to assume the other now; I prefer choosing end-to-beginning if target is dest than source, beginning-to-end otherwise.
but the calculation is done to move to end of ram, so dest is always higher than source.
Actually no, copying and fixing is not done in a single run. There is the copying of the text+data+const area, then the fixing which runs through the relocation table area; they are different.
Yes, that's what I meant. It's not a memcpy, you also use the data after the copy so any overlap is an issue, indepentend of the order of copying.
Or, easier: if we are already high enough to overlap, don't relocate at all. If it's acceptable, I'll patch for taht.
But then comes the question of how enough is "high enough". :)
If there's no overlap, you can relocate. If the areas overal, you keep the current address which also is "high enough".
If you ack (even offlist) I'll submit a patch tomorrow (monday) /alessandro

Le 17/04/2011 21:30, Alessandro Rubini a écrit :
Ori, simply, end-to-beginning when moving up. Or always end-to-beginning since we are expected to always move up (upper than the target address it can't run).
Since the 'issue' is caused by the code assuming one direction, I'd prefer it not to assume the other now; I prefer choosing end-to-beginning if target is dest than source, beginning-to-end otherwise.
but the calculation is done to move to end of ram, so dest is always higher than source.
I guess I can live with this if you put a big fat comment in the code to explain *why* the copy is done end-to-beginning, so that if one day the assumption stops holding, readers of the code can tell if they should keep or change the direction.
Actually no, copying and fixing is not done in a single run. There is the copying of the text+data+const area, then the fixing which runs through the relocation table area; they are different.
Yes, that's what I meant. It's not a memcpy, you also use the data after the copy so any overlap is an issue, indepentend of the order of copying.
Ok -- you're right, an end-to-beginning copy might trash the relocation info. The only solution, assuming we *know* when we are copying from RAM, would be to actually apply the relocation fixups to the origin location (but still compute them for the destination location!) then copy end-to-beginning. You can happily trash the relocation area now.
The only risk left is if the relocation code is going to trash itself over, and that will only happen if the origin is so high in RAM already that the destination base is below the end of the relocation code --this can be tested programatically. To make things easier, this test should be done in board_init_f() so that relocate_code() always gets a safe destination address. If board_init_f() finds it cannot relocate higher, it should just pass the current base address to relocate_code(), in which case no copy should happen but relocation should be done for the current location.
Or, easier: if we are already high enough to overlap, don't relocate at all. If it's acceptable, I'll patch for taht.
But then comes the question of how enough is "high enough". :)
If there's no overlap, you can relocate. If the areas overal, you keep the current address which also is "high enough".
If you ack (even offlist) I'll submit a patch tomorrow (monday) /alessandro
Can you submit along the lines of what I described here?
Amicalement,

Me:
but the calculation is done to move to end of ram, so dest is always higher than source.
Albert:
I guess I can live with this if you put a big fat comment in the code to explain *why* the copy is done end-to-beginning, so that if one day the assumption stops holding, readers of the code can tell if they should keep or change the direction.
Actually, I was wrong. Since the display buffer is allocated over u-boot, we may already be too high. This never happens for current boards, which lived before relocation was there (I rember having lowered the u-boot address to fit a bigger display buffer above it) but may happen in the future.
So, with the comment the thing may work (like "please for new boards load it low now that it relocates high by itself").
However, the copy is made in 35 assembly files. Only ARM has 14 almost-identical copies of the code (some of them are really identical), so I won't dare touching it.
The only thing I can do is not making the copy by forcing dest = source if we will overlap ourselves, because the assembly will detect this:
adr r0, _start cmp r0, r6 beq clear_bss /* skip relocation */
[I don't understand why here is _start and in C is _TEXT_BASE, which are different by 0x40 bytes, but it unexpectedly works]
For me it is working, but the code is not clean at all. I'm sending the patches anyways. I found the the overwrite was only for the stack, which is the lowest address in the relocated place, so I compared some space below the would-be stack with bss_start.
/alessandro
participants (2)
-
Albert ARIBAUD
-
Alessandro Rubini