[U-Boot] [PATCH v0 21/20] efi_loader: hack for archs that cannot do unaligned accesses

Mark Kettenis mark.kettenis at xs4all.nl
Mon Aug 7 21:14:58 UTC 2017


> From: Alexander Graf <agraf at suse.de>
> Date: Mon, 7 Aug 2017 21:19:37 +0100
> 
> On 05.08.17 21:31, Rob Clark wrote:
> > On Sat, Aug 5, 2017 at 4:05 PM, Heinrich Schuchardt <xypron.glpk at gmx.de> wrote:
> >> On 08/05/2017 08:43 PM, Rob Clark wrote:
> >>> On Sat, Aug 5, 2017 at 1:06 PM, Rob Clark <robdclark at gmail.com> wrote:
> >>>> On Sat, Aug 5, 2017 at 12:52 PM, Heinrich Schuchardt <xypron.glpk at gmx.de> wrote:
> >>>>> On 08/05/2017 06:16 PM, Rob Clark wrote:
> >>>>>> On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt <xypron.glpk at gmx.de> wrote:
> >>>>>>> On 08/05/2017 05:58 PM, Rob Clark wrote:
> >>>>>>>> Some arch's have trouble with unaligned accesses.  Technically
> >>>>>>>> EFI device-path structs should be byte aligned, and the next node
> >>>>>>>> in the path starts immediately after the previous.  Meaning that
> >>>>>>>> a pointer to an 'struct efi_device_path' is not necessarily word
> >>>>>>>> aligned.  See section 10.3.1 in v2.7 of UEFI spec.
> >>>>>>>>
> >>>>>>>> This causes problems not just for u-boot, but also most/all EFI
> >>>>>>>> payloads loaded by u-boot on these archs.  Fortunately the common
> >>>>>>>> practice for traversing a device path is to rely on the length
> >>>>>>>> field in the header, rather than the specified length of the
> >>>>>>>> particular device path type+subtype.  So the EFI_DP_PAD() macro
> >>>>>>>> will add the specified number of bytes to the tail of device path
> >>>>>>>> structs to pad them to word alignment.
> >>>>>>>>
> >>>>>>>> Technically this is non-compliant, BROKEN_UNALIGNED should *only*
> >>>>>>>> be defined on archs that cannot do unaligned accesses.
> >>>>>>>>
> >>>>>>>> Signed-off-by: Rob Clark <robdclark at gmail.com>
> >>>>>>>> ---
> >>>>>>>> I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED
> >>>>>>>>
> >>>>>>>> Mark, this is untested but I think it should solve your crash on the
> >>>>>>>> Banana Pi.  Could you give it a try when you get a chance?
> >>>>>>>>
> >>>>>>>>   arch/arm/config.mk               |  2 +-
> >>>>>>>>   include/efi_api.h                | 30 ++++++++++++++++++++++++++++++
> >>>>>>>>   lib/efi_loader/efi_device_path.c |  3 +++
> >>>>>>>>   3 files changed, 34 insertions(+), 1 deletion(-)
> >>>>>>>>
> >>>>>>>> diff --git a/arch/arm/config.mk b/arch/arm/config.mk
> >>>>>>>> index 1a77779db4..067dc93a9d 100644
> >>>>>>>> --- a/arch/arm/config.mk
> >>>>>>>> +++ b/arch/arm/config.mk
> >>>>>>>> @@ -28,7 +28,7 @@ LLVMS_RELFLAGS              := $(call cc-option,-mllvm,) \
> >>>>>>>>                        $(call cc-option,-arm-use-movt=0,)
> >>>>>>>>   PLATFORM_RELFLAGS    += $(LLVM_RELFLAGS)
> >>>>>>>>
> >>>>>>>> -PLATFORM_CPPFLAGS += -D__ARM__
> >>>>>>>> +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED
> >>>>>>>
> >>>>>>> NAK
> >>>>>>>
> >>>>>>> We have more then ARM. And other architectures also create exceptions
> >>>>>>> for unaligned access.
> >>>>>>>
> >>>>>>> I hate platform specific code. It should not be used outside /arch.
> >>>>>>>
> >>>>>>> To play it save you should not use _packed at all!
> >>>>>>> Use memcpy to transfer between aligned and unaligned memory.
> >>>>>>
> >>>>>> except for reasons I explained in the thread on the patch that added
> >>>>>> the __packed in the first place.  Sorry, this is ugly but we have to
> >>>>>> do it.
> >>>>>>
> >>>>>> BR,
> >>>>>> -R
> >>>>>
> >>>>> According to the UEFI standard the nodes in paths are not to be assumed
> >>>>> to be aligned.
> >>>>>
> >>>>> So even if you use padding bytes in paths that you pass to the EFI
> >>>>> application you should not expect that the EFI application does the
> >>>>> same. Expect the EFI application either to remove them or send new nodes
> >>>>> without padding.
> >>>>>
> >>>>> To the idea of padding bytes and __packed does not make sense.
> >>>>
> >>>> Ok, to be fair, you are right about device-paths passed too u-boot.
> >>>> On BROKEN_UNALIGNED archs, we should sanitise with a copy to an
> >>>> aligned device-path in *addition* to what I proposed.  I can make a
> >>>> patch to add a helper to do this a bit later.
> >>>
> >>> so thinking about this a bit, I have two options in mind:
> >>>
> >>>   + efi_dp_sanitize() + efi_dp_free() helpers that are no-ops on
> >>>     archs that can do unaligned access, but efi_dp_sanitize() always
> >>>     allocates and copies on BROKEN_UNALIGNED archs and efi_dp_free()
> >>>     always free's on BROKEN_UNALIGNED archs, even if the dp passed
> >>>     from efi payload doesn't require it.
> >>>
> >>>   + efi_dp_sanitize() that is no-op on archs that can do unaligned
> >>>     access but only allocates/copies when passed a device path that
> >>>     would result in unaligned access, plus hook some mechanism to
> >>>     auto-free on EFI_EXIT().. which is slightly tricky w/ nesting of
> >>>     efi calls, but not impossible and at least avoids the problem
> >>>     of missing calls to free the dup'd device-path.. which is the
> >>>     sort of leak I might miss since I'm not using EFI_LOADER on any
> >>>     BROKEN_UNALIGNED arch
> >>>
> >>> anyone who cares about armv7 + efi have a preference between always
> >>> doing an extra alloc+copy+free vs EFI_EXIT() magic?
> >>>
> >>
> >> Please, go for the simplest code.
> > 
> > well, the source of my question was there are two definitions of
> > "simplest" here, ie "simplest to use" and "simplest helpers".. I'm
> > mostly worried about future patches that use the helpers introducing
> > leaks.  But actually I think a reasonable middle ground is "simplest
> > helpers plus always make the helpers not no-ops for DEBUG builds"..
> > 
> >> I cannot imagine that copying takes more than 10ms for starting grub on
> >> any architecture. So the user will not notice it anyway.
> >> Assume every architecture requiring alignment for which you do not have
> >> proof of the contrary.
> >>
> >> Even on ARM64 there are op codes that fail without alignment.
> > 
> > iirc the restrictions where mostly about device memory, and atomics.
> > Which should not apply here.  (And we have aarch64 servers in
> > production with grub, which doesn't take any protections about
> > unaligned device-path nodes, using non u-boot EFI implementations.  So
> > maybe that counts as proof to the contrary ;-))
> 
> The UEFI spec mandates that unaligned access are fixed up properly 
> (usually by hardware). We violate the spec on 32bit ARM, but do fulfill 
> it on AArch64.

Actually for AArch32 the spec says:

  Unaligned access should be enabled if supported; Alignment faults
  are enabled otherwise.

So UEFI payload that wants to support pre-ARMv7 hardware should make
sure it doesn't do any unaligned access.

> The reason things worked before really was just that both U-Boot and 
> grub were compiled with -mno-unaligned-access, so they never issued 
> unaligned accesses.

That in itself is not enough to prevent unaligned access.  If you
explicitly cast an unaligned pointer to say (uint32_t *) and then
dereference it, you'll create an unaligned access.

> Which system broke for Mark? Banana pi mostly has 32bit SBCs, so I 
> assume it's one of those? In that case, we would need to either
> 
>    1) hack up the openbsd loader to also get compiled with 
> -mno-unaligned-access
> 
> or
> 
>    2) rework the 32bit arm mmu code to enable the MMU plus hardware 
> unaligned fixups on all systems. (that's a *lot* of work)

The system that broke was Cortex-A7 based, so ARMv7.

*However*, the OpenBSD bootloader does not perform any unaligned
access.  *All* cases of unaligned access that I encountered while
testing/debugging Rob's code were in U-Boot itself.  This was mostly a
consequence from a diff in the series that added __packed to the
device path node structs and then passed members of those structs to
functions that accessed the contents of tose (now byte aligned
structs) through (uint16_t *) or (uint32_t *) pointers.

There was initially some confusion because I used the pc reported by
U-Boot to look up the faulting instruction whereas I should have been
using the reloc_pc.

I think Rob is worried about grub/shim/fallback.efi doing unaligned
access of device path nodes on armv7.  But I'm not sure he has any
evidence beyond looking at code.

> To me personally, UEFI in 32bit ARM land is a nice to have standardized 
> platform, but not something where we really need to be fully standards 
> compliant in every aspect. I think "making things work" is good enough 
> there.

Right.  For us UEFI us just a convenient way to re-use the U-Boot
device driver code to load a kernel from a UFS/FFS filesystem.

> For AArch64 things are different. There we should strive for full UEFI 
> compliance as it's "the future" :).

Even there just making things work would be good enough for me ;).
Our AArach64 bootloader is almost identical to the AArch32 one and
works on a real UEFI implementation as well (SoftIron Overdrive 1000).

Cheers,

Mark


More information about the U-Boot mailing list