[U-Boot] [PATCH 4/4] arm: factorize relocate_code routine

Benoît Thébaudeau
2013-05-11T13:40:50Z

Hi Albert,

On Saturday, May 11, 2013 9:40:02 AM, Albert ARIBAUD wrote:
> Hi Beno?t,
>
> On Sat, 11 May 2013 04:04:09 +0200 (CEST), Beno?t Th?baudeau
> <benoit.thebaudeau@advansee.com> wrote:
>
> > Hi Albert,
> >
> > On Friday, May 10, 2013 11:56:52 PM, Albert ARIBAUD wrote:
> > > Signed-off-by: Albert ARIBAUD <albert.u.boot@aribaud.net>

[...]

> > > + mov r6, r0 /* save addr of destination */
> > > +
> > > + ldr r0, =_start
> >
> > This is wrong. Here, r0 will get _start link-time address, while with 'adr'
> > in
> > the original code, r0 was getting _start runtime address, which is not the
> > same
> > for all boards. relocate_code() is and must stay position-independent code,
> > at
> > least for SPL.
> >
> > E.g., for mx31pdk or tx25, the SPL is first loaded by the boot ROM
> in the NAND
> > Flash controller buffer, then executed from there. The SPL then has to use
> > relocate_code() to relocate itself to CONFIG_SPL_TEXT_BASE in order to free
> > the
> > NFC buffer to load U-Boot from NAND Flash. This means that 'ldr r0,
> > =_start'
> > would set r0 to CONFIG_SPL_TEXT_BASE, while 'adr r0, _start' sets r0 to the
> > address of the NFC buffer. Since the SPL calls relocate_code() with
> > CONFIG_SPL_TEXT_BASE, the 'ldr' choice would just result in a branch to
> > relocate_done below, which would abort the relocation and break the boot on
> > those boards.
> >
> > The issue is that 'adr' requires that _start be defined in the same file
> > and
> > section. That's why my patch 31/31 was using a macro to define
> > relocate_code()
> > in the start.S files. Perhaps some other constructs like
> > 'sub r0, pc, . - _start' would work, but I doubt it if _start is not in the
> > same
> > file and section since this is exactly how 'adr' is translated.
>
> Alright, so -- formally, there is no assumption that code running before
> relocation must be position-independent, as far as I remember. The
> assumption is that it is designed to run at the link-time location, and
> that it is responsible for making sure that the rest of the code is
> properly relocated and thus will run at whatever location it was moved
> to.
>
> In the case you mention, SPL does not relocate itself: it only *moves*
> itself, skipping the actual relocation (reference fixing part) -- it
> could havdoe so with a short ad hoc loop instead of relocate_code, and
> thus not impose a specific requirement on relocate_code that was not
> initially there. I'd be happier with a preprocessor conditional at the
> crt0.S stage to choose between relocate_code and a new, short,
> move_code routine depending on whether we are building SPL or not.
>
> That being said, I am fine with trying to make the code before
> relocation as position-independent as possible anyway; let me see how
> this can be done.

OK, I let you try something. Otherwise, I agree with what you say above. The
original code for the non-SPL part below is not position-independent, so it is
safe to assume that only the beginning of relocate_code() (before
'#ifndef CONFIG_SPL_BUILD') should be position-independent, and only for SPL. It
is also probably true that the initial runtime address of all SPLs is known at
build time, so a new CONFIG_SPL_ could be created for that purpose and used in a
dedicated small SPL copy routine, which would also make the need for SPL move
testable at build time, and solve the 'adr' vs. 'ldr' issue.

> > > + subs r9, r6, r0 /* r9 <- relocation offset */
> > > + beq relocate_done /* skip relocation */
> > > + mov r1, r6 /* r1 <- scratch for copy_loop */
> > > + ldr r2, =__image_copy_end
> > > +
> > > +copy_loop:
> > > + ldmia r0!, {r10-r11} /* copy from source address [r0] */
> > > + stmia r1!, {r10-r11} /* copy to target address [r1] */
> > > + cmp r0, r2 /* until source end address [r2] */
> > > + blo copy_loop
> > > +
> > > +#ifndef CONFIG_SPL_BUILD
> > > + /*
> > > + * fix .rel.dyn relocations
> > > + */
> > > + ldr r0, =_TEXT_BASE /* r0 <- Text base */
> >
> > The value you load above into r0 then gets overwritten prior to having been
> > used, so you can drop this line, all the more it is wrong, loading the
> > address
> > of _TEXT_BASE instead of its value.
>
> Correct. Will fix in V2.
>
> > > + ldr r10, =__dynsym_start /* r10 <- sym table ofs */
> > > + ldr r2, =__rel_dyn_start /* r2 <- rel dyn start ofs */
> > > + ldr r3, =__rel_dyn_end /* r3 <- rel dyn end ofs */
> >
> > 'ofs' should be replaced with 'in FLASH' in the comments above.
> >
> > Contrary to above, this construct works here because this can't be SPL
> > code.
>
> Actually, this would not work either if used by non-SPL code as SPL code
> expects to use it, e.g. by loading U-Boot at another address -- the
> issue is in the use scenario, not in the requirements for SPL vs.
> U-Boot.

Right, but this new code behaves exactly in the same way as the original code,
so it is safe to assume that there is no such use scenario. This new code just
can't introduce a regression.

> > > +relocate_done:
> > > +
> > > + bx lr
> >
> > This instruction is not supported on ARMv4, even if GCC does not complain
> > about
> > it (it looks like this is not even fixed by the linker, except if the
> > instruction was issued by GCC itself), but it is required for more recent
> > ARM
> > versions for Thumb inter-working, which is enabled by some boards in
> > U-Boot.
> > Hence, shouldn't the line above be replaced with:
> >
> > #ifdef __ARM_ARCH_4__
> > mov pc, lr
> > #else
> > bx lr
> > #endif
> >
> > Or using CONFIG_SYS_THUMB_BUILD?
>
> I would have preferred adding --fix-v4bx to the linker flags for ARMv4
> targets, but it seems to have no effect at least with some toolchains.

Indeed.

> I'll fix the code above with the ARMv4 conditional, as it is always
> correct regardless of U-boot configuration options semantics.
>
> > "ENDPROC(relocate_code)" should be added here if you go for ENTRY() at the
> > beginning.
>
> Ok.
>
> > Best regards,
> > Beno?t
>
> Thanks for your review!

You're welcome.

Best regards,
Beno?t