[U-Boot] [PATCH v0 21/20] efi_loader: hack for archs that cannot do unaligned accesses
Alexander Graf
agraf at suse.de
Tue Aug 8 06:52:14 UTC 2017
> Am 07.08.2017 um 23:18 schrieb Rob Clark <robdclark at gmail.com>:
>
> On Mon, Aug 7, 2017 at 5:14 PM, Mark Kettenis <mark.kettenis at xs4all.nl> wrote:
>>> 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.
>
> It is a bit strange to me that alignment faults are disabled by
> default, but presumably is disabled out of reset.. I don't totally
> understand the connection to having mmu disabled, unless all memory is
> treated like device memory without mmu enabled. (Which, also,
> wouldn't that be super-slow?) But anyway, I guess that is a bit
> beside the point.
It is treated as device memory with mmu disabled, yes.
>
>> 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.
>
> This is problematic around file nodes in the device path. Adding the
> padding bytes to the end of each device-path struct would "solve"
> that, and if pre-aarch64 we are aiming at "good enough to work", I
> kinda think that this the approach we should go for. Other than
> file-path nodes, the rest of the issues in u-boot should be solved by
> addition of missing __packed on 'struct efi_device_path' (which I've
> added locally and will be in the next revision of the patchset).
Let's ask Leif and Ard if that is actually correct. If I remember correctly, edk2 some times leverages automatic padding from the compiler on structs.
>
>>> 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.
>
> Yeah.. the padding byte approach I suggested would solve this. Not
> *really* compliant, but should work for all the payload code I've come
> across. We definitely shouldn't enable the padding bytes on aarch64,
> but I think padding bytes would be a pragmatic solution for armv7 and
> earlier. (And hopefully someday someone enables mmu on armv7 so we
> can turn off the hack there too.)
The problem really is inconsistency. Some 32bit arm platforms enable the mmu, some don't.
>
>> 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.
>
> It will definitely be an issue (if grub/shim/fallback were not
> compiled with -mno-unaligned-access) in some of the debug code that
> prints out device-paths. I haven't checked the compiler flags used w/
> shim/fallback.
>
> Parsing MEDIA_DEVICE:HARD_DRIVE nodes would be an issue regardless
> without -mno-unaligned-access, although we might be getting lucky by
> just not hitting debug paths. Some of the other issues would be
> hidden by padding bytes at the end of the DP structs which would keep
> the file-path nodes word aligned.
>
>>> 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).
>>
>
> I think we should make the aarch64 implementation fully compliant (ie.
> addition of missing __packed's and no extra padding bytes). I won't
> loose sleep if some efi payloads don't work on pre-aarch64 (we should
> be able to keep things that were working before working). If adding
> missing __packed breaks things on platforms that can't do unaligned
> access, the solution is not to remove __packed, but to conditionally
> add padding bytes at the end of the struct. That way we keep things
> working as before on the old platforms, but make things work correctly
> on aarch64.
>
> I'll add back my patch for EFI_DP_PAD() to this patchset, since this
> seems like the sensible way forward.
Let's make sure we get an ack from Leif/Ard for that approach :)
Alex
More information about the U-Boot
mailing list