Question

I am trying to understand part of code which says its relocation of U-boot in RAM,below is the code

   #ifndef  CONFIG_SKIP_RELOCATE_UBOOT
   relocate:
     adr r0,_start      /*r0 <--- Current posistion of code8*/
     ldr r1,_TEXT_BASE  /* test if we run from flash or Ram /*
     cmp r0,r1
     beq stack_setup
     ldr r2,_armboot_start   
     ldr r3,_bss_start
     sub r2,r3,r2
     add r2,r0,r2
   copy_loop:
     ldmia r0!,{r3-r10}
     stmia r1!,{r3-r10}
     cmp   r0,r2
     ble   cop_loop
     #endif /*CONFIG_SKIP_RELOCATE_UBOOT/*

Now canbody let me know how it is happening here??How we are testing if our u-boot is running from RAM or Flash??

I am on ARM platform.

Was it helpful?

Solution

Here is a very simple example,

.globl _start
_start:

    adr r0,_start
    ldr r1,_TEXT_BASE

...

_TEXT_BASE: .word _start

when assembled, linked and then disassembled:

00008000 <_start>:
    8000:   e24f0008    sub r0, pc, #8
    8004:   e59f101c    ldr r1, [pc, #28]   ; 8028 <_TEXT_BASE>
...
00008028 <_TEXT_BASE>:
    8028:   00008000    andeq   r8, r0, r0

And there is your answer. the adr instruction is based on the assumption that your pc contains 0x8008 at the time that you execute it. The ldr is going to pull in a link time value which is the same no matter where you are.

If for example this code is actually located at address 0x20000000 then when that first instruction (the adr is a pseudo instruction, in the disassembly it is a sub of 8), the adr, is executed now you get a 0x20000008-8 = 0x20000000 and you compare that with 0x8000 they dont match. If you are running the code at 0x8000 then 0x8008-8 = 0x8000 and the two match.

Just read the code and look up the adr instruction (or do what I did and just try it and examine the output of the compiler/tools, and/or run it on hardware if that doesnt show the answer).

EDIT:

Using gnu tools which have various prefixes, but this code is simple enough to pretty much not care. arm-none-eabi- or arm-none-linux-gnueabi-.

assuming the assembly filename is foo.s

arm-none-eabi-as foo.s -o foo.o
arm-none-eabi-ld -T memmap foo.o -o foo.elf
arm-none-eabi-objdump -D foo.elf

what I called memmap is the linker script, you could use a command line -Ttext=0x8000 in this case. And I like to use the .elf extension for my cross compile binaries, not everyone does that.

00008000 <_start>:
    8000:   e24f0008    sub r0, pc, #8
    8004:   e59f101c    ldr r1, [pc, #28]   ; 8028 <_TEXT_BASE>
...
00008028 <_TEXT_BASE>:
    8028:   00008000    andeq   r8, r0, r0

_start is a gnu tools thing the linker needs/wants this label to know where the entry point of the code is. So it is not really a uboot thing, although uboot may care as well, but it is definitely a gnu linker thing.

0x8000 is not an uncommon address for ARM based linux programs as an entry point, and you will see linux as a kernel started at an address like that, but it is really arbitrary you can setup your system and binaries for whatever.

There is nothing really ARM specific or magic about what is going on here. Same idea on whatever platform you just have to come up with the right instructions.

The program counter in arm is two instructions ahead, this is a 32 bit arm instruction so the program counter when executing the instruction is assumed to be the address of this instruction plus 8. Since this adr thing is the first instruction at _start that means this instruction is at address 0x8000 as linked/compiled and as a result to get from 0x8008 to 0x8000 the instruction is encoded as r0 = pc-8; The other information is the linker supplies the address for _start after the label _TEXT_BASE. So the other step is to load that value into r1.

This only works if you operate on the assumption and fact that the code actually lives in a flash such that the processor sees that instruction in flash at address 0x8000. The comparison of 0x8000 and 0x8000 are equal, so then the program makes a copy of itself in ram, then it will jump to the beginning of where this copy is, this time when it passes through the copy of the code one register contains some address other than 0x8000 so the compare fails, this is just a detector if you are running the original version from flash or running a copy from ram. The purpose is to make a copy and run the ram based copy. Other precautions are required to insure that the code can run at both addresses (position independent code).

If you happened to know that the flash address was 0x00008000 and happened to know for example that the ram address was say 0x20000000 you could have instead compared the pc with say 0x10000 or could have anded the value of the pc with 0xFF000000

and r0,pc,#0xFF000000
beq stack_setup

But I assume this is more generic code so the extra instructions and the exact comparison are used.

Again this type of trick is fairly common, detect the flash or ram copy, etc. the exact instructions used and how to get the linker to fill in things for you is specific to the target.

In this case perhaps the flash is at some non-zero address and 0x8000 is the ram copy. I dont know.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top