loongson-community / discussions

Cross-community issue tracker & discussions / 跨社区工单追踪 & 讨论场所
7 stars 0 forks source link

systemd-boot UEFI code isn't guaranteed to be soft-float on LoongArch #19

Open xen0n opened 7 months ago

xen0n commented 7 months ago

With GCC 14 snapshot and -march=la464 or -march=la664 in CFLAGS, UKIs produced by systemd's ukify tool fail to print anything when brought up by sd-boot.

Likely due to LSX/LASX being emitted by GCC for block moves, but because the SIMD unit is disabled at the UEFI stage, the execution simply halts with an SXD or ASXD exception.

xen0n commented 7 months ago

Confirmed this is the root cause. By re-building systemd with -mno-lsx -mno-lasx, I successfully produced working UKIs.

xen0n commented 7 months ago

We need to teach sd-boot's build system to just use the LP64S ABI for the UEFI bits.

xen0n commented 7 months ago

@xry111: Do you think something like -mgeneral-regs-only useful for LoongArch too? I fear -msoft-float alone may not be able to cancel other fancy additions from future -march additions, while filtering -march is arguably not appropriate either.

-mabi=lp64s isn't directly usable, because the sd-boot UEFI code didn't make the effort to avoid inclusion of libc headers:

In file included from /usr/include/features.h:527,
                 from /usr/include/bits/libc-header-start.h:33,
                 from /usr/include/limits.h:26,
                 from /usr/lib/gcc/loongarch64-unknown-linux-gnu/14/include/limits.h:210,
                 from /usr/lib/gcc/loongarch64-unknown-linux-gnu/14/include/syslimits.h:7,
                 from /usr/lib/gcc/loongarch64-unknown-linux-gnu/14/include/limits.h:34,
                 from ../systemd-stable-254.6/src/fundamental/macro-fundamental.h:8,
                 from ../systemd-stable-254.6/src/boot/efi/efi.h:9,
                 from ../systemd-stable-254.6/src/fundamental/string-util-fundamental.h:5,
                 from ../systemd-stable-254.6/src/fundamental/bootspec-fundamental.h:4,
                 from ../systemd-stable-254.6/src/fundamental/bootspec-fundamental.c:3:
/usr/include/gnu/stubs.h:8:11: fatal error: gnu/stubs-lp64s.h: No such file or directory
    8 | # include <gnu/stubs-lp64s.h>
      |           ^~~~~~~~~~~~~~~~~~~
compilation terminated.

EDIT: apparently -msoft-float also fails in exactly the same way. I really don't want to pretend compiling for LP64D while doing -mno-lsx -mno-lasx, because we can't disable FP the same way, hence it's only papering over the problem,

xen0n commented 7 months ago

According to the UEFI spec, it's okay for UEFI apps to self-enable FPU with CSR.EUEN if they're willing to self-manage the context afterwards. But I don't think it's a good idea for every single UEFI app to do so either.

xry111 commented 7 months ago

Hmm, what's the expected difference between -mgeneral-regs-only and -msoft-float or -mabi=lp64s?

xen0n commented 7 months ago

Hmm, what's the expected difference between -mgeneral-regs-only and -msoft-float or -mabi=lp64s?

See:

It seems it doesn't change the ABI, but it's likely because aarch64 and x86_64 don't have multiple commonly used ABIs.

xen0n commented 7 months ago

Maybe fixing the libc header dep of the UEFI code is the most elegant way to go... The UEFI code cannot and shouldn't get into contact with the Linux libc after all.

xry111 commented 7 months ago

Maybe fixing the libc header dep of the UEFI code is the most elegant way to go... The UEFI code cannot and shouldn't get into contact with the Linux libc after all.

I'm wondering if -ffreestanding should prevent the inclusion of Glibc limits.h (like stdint.h).

xen0n commented 7 months ago

Maybe fixing the libc header dep of the UEFI code is the most elegant way to go... The UEFI code cannot and shouldn't get into contact with the Linux libc after all.

I'm wondering if -ffreestanding should prevent the inclusion of Glibc limits.h (like stdint.h).

Is that possible or appropriate to do? IIUC -f* flags are meant for the optimizer, but to implement what you suggested we seem to need to poke into the include search path. Otherwise all header files look identical and there's no magical mark/pragma that distinguishes libc headers from ordinary user-supplied headers.

xry111 commented 7 months ago

Maybe fixing the libc header dep of the UEFI code is the most elegant way to go... The UEFI code cannot and shouldn't get into contact with the Linux libc after all.

I'm wondering if -ffreestanding should prevent the inclusion of Glibc limits.h (like stdint.h).

Is that possible or appropriate to do? IIUC -f* flags are meant for the optimizer, but to implement what you suggested we seem to need to poke into the include search path. Otherwise all header files look identical and there's no magical mark/pragma that distinguishes libc headers from ordinary user-supplied headers.

-ffreestanding already prevents the inclusion of libc stdint.h as at now. When we include stdint.h in a freestanding C program, GCC will use its own minimized version of stdint.h.

       -ffreestanding
           Assert that compilation targets a freestanding  environment.   This
           implies  -fno-builtin.   A freestanding environment is one in which
           the standard library may not exist, and  program  startup  may  not
           necessarily  be  at  "main".   The  most  obvious  example is an OS
           kernel.  This is equivalent to -fno-hosted.

It seems reasonable for a bootloader.

xen0n commented 7 months ago

Ah, that's nice, but the problem is that -ffreestanding is already present...

xry111 commented 7 months ago

Ah, that's nice, but the problem is that -ffreestanding is already present...

Yes, I mean I'm wondering if GCC limits.h should be self-contained if !__STDC_HOSTED__, like stdint.h.

GCC stdint.h now reads:

#ifndef _GCC_WRAP_STDINT_H
#if __STDC_HOSTED__
# if defined __cplusplus && __cplusplus >= 201103L
#  undef __STDC_LIMIT_MACROS
#  define __STDC_LIMIT_MACROS
#  undef __STDC_CONSTANT_MACROS
#  define __STDC_CONSTANT_MACROS
# endif
# include_next <stdint.h>
#else
# include "stdint-gcc.h"
#endif
#define _GCC_WRAP_STDINT_H
#endif

So it does not refer to Glibc stdint.h if !__STDC_HOSTED__. Maybe it should be done for limits.h too.

xry111 commented 7 months ago

i.e. in GCC limits.h now we have (line 32):

#ifndef _LIBC_LIMITS_H_
/* Use "..." so that we find syslimits.h only in this same directory.  */
#include "syslimits.h"
#endif

Maybe it should be

#ifndef _LIBC_LIMITS_H_ && __STDC_HOSTED__
/* Use "..." so that we find syslimits.h only in this same directory.  */
#include "syslimits.h"
#endif

?

xen0n commented 7 months ago

Ah, so GCC's -ffreestanding adaptation of <stdint.h> is some local trick... Then extending that to <limits.h> seems reasonable to me!

xry111 commented 7 months ago

https://gcc.gnu.org/PR112699

xen0n commented 6 months ago

I also found out that the libgcc.a contains vectorized __popcountdi2 so even if I disabled SIMD codegen for systemd, this code still found its way into the UKI stub...

000000000000001c <__popcountdi2>:
  1c:   73e18000        vldi            $vr0, 3072
  20:   72ebf080        vinsgr2vr.d     $vr0, $a0, 0x0
  24:   729c2c00        vpcnt.d         $vr0, $vr0
  28:   72f3f004        vpickve2gr.du   $a0, $vr0, 0x0
xry111 commented 6 months ago

Phew. Now to me systemd-boot just requires a multilib...

xry111 commented 6 months ago

Anyway to me depending on libgcc.a is wrong even on x86_64: what will happen if libgcc.a uses a SSE2 instruction?

xry111 commented 6 months ago

I'm having a debate with Segher Boessenkool on the topic.

hanyuwei70 commented 4 months ago

got hit by this. using sd-boot on loongarchlinux & AOSC OS. Now I can't boot to any of them even using EFI Shell command to boot.