[PATCH V9 21/24] LoongArch: Add zboot (compressed kernel) support

Arnd Bergmann arnd at arndb.de
Sat Apr 30 03:07:27 PDT 2022


On Sat, Apr 30, 2022 at 11:05 AM Huacai Chen <chenhuacai at loongson.cn> wrote:
>
> This patch adds zboot (self-extracting compressed kernel) support, all
> existing in-kernel compressing algorithm and efistub are supported.
>
> Signed-off-by: Huacai Chen <chenhuacai at loongson.cn>

I have no objections to adding a decompressor in principle, and
the implementation seems reasonable. However, I think we should try to
be consistent between architectures. On both arm64 and riscv, the
maintainers decided to not include a decompressor and instead leave
it up to the boot loader to decompress the kernel and enter it from there.

As I understand it, this is not part of the UEFI boot flow though, so it
means that you don't get any compressed kernel images at all when
booting using UEFI (let me know if that is wrong). I assume this is why
you decided to include the decompressor here after all.

I think we should first aim for consistency here, and handle this the
same way across the modern architectures, either leaving the
decompressor code out, or adding it consistently. Maybe it would
even be possible to have the decompressor code as part of the
EFI stub and share it between the three architectures (x86 and
32-bit arm already support loading compressed kernels using EFI).

Adding the arm64, risc-v and uefi maintainers for further discussion here,
see full below.

       Arnd

> ---
>  arch/loongarch/Kbuild                         |   2 +-
>  arch/loongarch/Kconfig                        |  11 ++
>  arch/loongarch/Makefile                       |  26 ++-
>  arch/loongarch/boot/Makefile                  |  55 ++++++
>  arch/loongarch/boot/boot.lds.S                |  64 +++++++
>  arch/loongarch/boot/decompress.c              |  98 +++++++++++
>  arch/loongarch/boot/string.c                  | 166 ++++++++++++++++++
>  arch/loongarch/boot/zheader.S                 | 100 +++++++++++
>  arch/loongarch/boot/zkernel.S                 |  99 +++++++++++
>  arch/loongarch/tools/Makefile                 |  15 ++
>  arch/loongarch/tools/calc_vmlinuz_load_addr.c |  51 ++++++
>  arch/loongarch/tools/elf-entry.c              |  66 +++++++
>  12 files changed, 749 insertions(+), 4 deletions(-)
>  create mode 100644 arch/loongarch/boot/boot.lds.S
>  create mode 100644 arch/loongarch/boot/decompress.c
>  create mode 100644 arch/loongarch/boot/string.c
>  create mode 100644 arch/loongarch/boot/zheader.S
>  create mode 100644 arch/loongarch/boot/zkernel.S
>  create mode 100644 arch/loongarch/tools/Makefile
>  create mode 100644 arch/loongarch/tools/calc_vmlinuz_load_addr.c
>  create mode 100644 arch/loongarch/tools/elf-entry.c
>
> diff --git a/arch/loongarch/Kbuild b/arch/loongarch/Kbuild
> index ab5373d0a24f..d907fdd7ca08 100644
> --- a/arch/loongarch/Kbuild
> +++ b/arch/loongarch/Kbuild
> @@ -3,4 +3,4 @@ obj-y += mm/
>  obj-y += vdso/
>
>  # for cleaning
> -subdir- += boot
> +subdir- += boot tools
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 55225ee5f868..6c1042746b2d 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -107,6 +107,7 @@ config LOONGARCH
>         select PERF_USE_VMALLOC
>         select RTC_LIB
>         select SPARSE_IRQ
> +       select SYS_SUPPORTS_ZBOOT
>         select SYSCTL_EXCEPTION_TRACE
>         select SWIOTLB
>         select TRACE_IRQFLAGS_SUPPORT
> @@ -143,6 +144,16 @@ config LOCKDEP_SUPPORT
>         bool
>         default y
>
> +config SYS_SUPPORTS_ZBOOT
> +       bool
> +       select HAVE_KERNEL_GZIP
> +       select HAVE_KERNEL_BZIP2
> +       select HAVE_KERNEL_LZ4
> +       select HAVE_KERNEL_LZMA
> +       select HAVE_KERNEL_LZO
> +       select HAVE_KERNEL_XZ
> +       select HAVE_KERNEL_ZSTD
> +
>  config MACH_LOONGSON32
>         def_bool 32BIT
>
> diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile
> index d88a792dafbe..1ed5b8466565 100644
> --- a/arch/loongarch/Makefile
> +++ b/arch/loongarch/Makefile
> @@ -5,12 +5,31 @@
>
>  boot   := arch/loongarch/boot
>
> +ifndef CONFIG_SYS_SUPPORTS_ZBOOT
> +
>  ifndef CONFIG_EFI_STUB
>  KBUILD_IMAGE   = $(boot)/vmlinux
>  else
>  KBUILD_IMAGE   = $(boot)/vmlinux.efi
>  endif
>
> +else
> +
> +ifndef CONFIG_EFI_STUB
> +KBUILD_IMAGE   = $(boot)/vmlinuz
> +else
> +KBUILD_IMAGE   = $(boot)/vmlinuz.efi
> +endif
> +
> +endif
> +
> +load-y         = 0x9000000000200000
> +bootvars-y     = VMLINUX_LOAD_ADDRESS=$(load-y)
> +
> +archscripts: scripts_basic
> +       $(Q)$(MAKE) $(build)=arch/loongarch/tools elf-entry
> +       $(Q)$(MAKE) $(build)=arch/loongarch/tools calc_vmlinuz_load_addr
> +
>  #
>  # Select the object file format to substitute into the linker script.
>  #
> @@ -55,9 +74,6 @@ KBUILD_CFLAGS_MODULE          += -fplt -Wa,-mla-global-with-abs,-mla-local-with-abs
>  cflags-y += -ffreestanding
>  cflags-y += $(call as-option,-Wa$(comma)-mno-fix-loongson3-llsc,)
>
> -load-y         = 0x9000000000200000
> -bootvars-y     = VMLINUX_LOAD_ADDRESS=$(load-y)
> -
>  drivers-$(CONFIG_PCI)          += arch/loongarch/pci/
>
>  KBUILD_AFLAGS  += $(cflags-y)
> @@ -99,7 +115,11 @@ $(KBUILD_IMAGE): vmlinux
>         $(Q)$(MAKE) $(build)=$(boot) $(bootvars-y) $@
>
>  install:
> +ifndef CONFIG_SYS_SUPPORTS_ZBOOT
>         $(Q)install -D -m 755 $(KBUILD_IMAGE) $(INSTALL_PATH)/vmlinux-$(KERNELRELEASE)
> +else
> +       $(Q)install -D -m 755 $(KBUILD_IMAGE) $(INSTALL_PATH)/vmlinuz-$(KERNELRELEASE)
> +endif
>         $(Q)install -D -m 644 .config $(INSTALL_PATH)/config-$(KERNELRELEASE)
>         $(Q)install -D -m 644 System.map $(INSTALL_PATH)/System.map-$(KERNELRELEASE)
>
> diff --git a/arch/loongarch/boot/Makefile b/arch/loongarch/boot/Makefile
> index 66f2293c34b2..c26a36004ae2 100644
> --- a/arch/loongarch/boot/Makefile
> +++ b/arch/loongarch/boot/Makefile
> @@ -21,3 +21,58 @@ quiet_cmd_eficopy = OBJCOPY $@
>
>  $(obj)/vmlinux.efi: $(obj)/vmlinux FORCE
>         $(call if_changed,eficopy)
> +
> +# zboot
> +extra-y        += boot.lds
> +$(obj)/boot.lds: $(obj)/vmlinux.bin FORCE
> +CPPFLAGS_boot.lds = $(KBUILD_CPPFLAGS) -DVMLINUZ_LOAD_ADDRESS=$(zload-y)
> +
> +entry-y        = $(shell $(objtree)/arch/loongarch/tools/elf-entry $(obj)/vmlinux)
> +zload-y = $(shell $(objtree)/arch/loongarch/tools/calc_vmlinuz_load_addr \
> +                               $(obj)/vmlinux.bin $(VMLINUX_LOAD_ADDRESS))
> +
> +BOOT_HEAP_SIZE := 0x400000
> +BOOT_STACK_SIZE        := 0x002000
> +
> +KBUILD_AFLAGS := $(KBUILD_AFLAGS) -D__ASSEMBLY__ \
> +       -DBOOT_HEAP_SIZE=$(BOOT_HEAP_SIZE) \
> +       -DBOOT_STACK_SIZE=$(BOOT_STACK_SIZE)
> +
> +KBUILD_CFLAGS := $(KBUILD_CFLAGS) -fpic -D__KERNEL__ \
> +       -DBOOT_HEAP_SIZE=$(BOOT_HEAP_SIZE) \
> +       -DBOOT_STACK_SIZE=$(BOOT_STACK_SIZE)
> +
> +targets += vmlinux.bin
> +OBJCOPYFLAGS_vmlinux.bin := $(OBJCOPYFLAGS) -O binary $(strip-flags)
> +$(obj)/vmlinux.bin: $(obj)/vmlinux FORCE
> +       $(call if_changed,objcopy)
> +
> +tool_$(CONFIG_KERNEL_GZIP)    = gzip
> +tool_$(CONFIG_KERNEL_BZIP2)   = bzip2_with_size
> +tool_$(CONFIG_KERNEL_LZ4)     = lz4_with_size
> +tool_$(CONFIG_KERNEL_LZMA)    = lzma_with_size
> +tool_$(CONFIG_KERNEL_LZO)     = lzo_with_size
> +tool_$(CONFIG_KERNEL_XZ)      = xzkern_with_size
> +tool_$(CONFIG_KERNEL_ZSTD)    = zstd22_with_size
> +
> +targets += vmlinux.bin.z
> +$(obj)/vmlinux.bin.z: $(obj)/vmlinux.bin FORCE
> +       $(call if_changed,$(tool_y))
> +
> +targets += $(notdir $(vmlinuzobjs-y))
> +vmlinuzobjs-y := $(obj)/zkernel.o $(obj)/decompress.o $(obj)/string.o
> +vmlinuzobjs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
> +$(obj)/zkernel.o: $(obj)/vmlinux.bin.z
> +AFLAGS_zkernel.o = $(KBUILD_AFLAGS) -DVMLINUZ_LOAD_ADDRESS=$(zload-y) -DKERNEL_ENTRY=$(entry-y)
> +
> +quiet_cmd_zld = LD      $@
> +      cmd_zld = $(LD) $(KBUILD_LDFLAGS) -T $< $(vmlinuzobjs-y) -o $@
> +
> +targets += vmlinuz
> +$(obj)/vmlinuz: $(src)/boot.lds $(vmlinuzobjs-y) FORCE
> +       $(call if_changed,zld)
> +       $(call if_changed,strip)
> +
> +targets += vmlinuz.efi
> +$(obj)/vmlinuz.efi: $(obj)/vmlinuz FORCE
> +       $(call if_changed,eficopy)
> diff --git a/arch/loongarch/boot/boot.lds.S b/arch/loongarch/boot/boot.lds.S
> new file mode 100644
> index 000000000000..23e698782afd
> --- /dev/null
> +++ b/arch/loongarch/boot/boot.lds.S
> @@ -0,0 +1,64 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * ld.script for compressed kernel support of LoongArch
> + *
> + * Author: Huacai Chen <chenhuacai at loongson.cn>
> + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
> + */
> +
> +#include "../kernel/image-vars.h"
> +
> +/*
> + * Max avaliable Page Size is 64K, so we set SectionAlignment
> + * field of EFI application to 64K.
> + */
> +PECOFF_FILE_ALIGN = 0x200;
> +PECOFF_SEGMENT_ALIGN = 0x10000;
> +
> +OUTPUT_ARCH(loongarch)
> +ENTRY(kernel_entry)
> +PHDRS {
> +       text PT_LOAD FLAGS(7); /* RWX */
> +}
> +SECTIONS
> +{
> +       . = VMLINUZ_LOAD_ADDRESS;
> +
> +       _text = .;
> +       .head.text : {
> +               *(.head.text)
> +       }
> +
> +       .text : {
> +               *(.text)
> +               *(.init.text)
> +               *(.rodata)
> +       }: text
> +
> +       . = ALIGN(PECOFF_SEGMENT_ALIGN);
> +       _data = .;
> +       .data : {
> +               *(.data)
> +               *(.init.data)
> +               /* Put the compressed image here */
> +               __image_begin = .;
> +               *(.image)
> +               __image_end = .;
> +               CONSTRUCTORS
> +               . = ALIGN(PECOFF_FILE_ALIGN);
> +       }
> +       _edata = .;
> +
> +       .bss : {
> +               *(.bss)
> +               *(.init.bss)
> +       }
> +       . = ALIGN(PECOFF_SEGMENT_ALIGN);
> +       _end = .;
> +
> +       /DISCARD/ : {
> +               *(.options)
> +               *(.comment)
> +               *(.note)
> +       }
> +}
> diff --git a/arch/loongarch/boot/decompress.c b/arch/loongarch/boot/decompress.c
> new file mode 100644
> index 000000000000..8f55fcd8f285
> --- /dev/null
> +++ b/arch/loongarch/boot/decompress.c
> @@ -0,0 +1,98 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Author: Huacai Chen <chenhuacai at loongson.cn>
> + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/string.h>
> +#include <linux/libfdt.h>
> +
> +#include <asm/addrspace.h>
> +
> +/*
> + * These two variables specify the free mem region
> + * that can be used for temporary malloc area
> + */
> +unsigned long free_mem_ptr;
> +unsigned long free_mem_end_ptr;
> +
> +/* The linker tells us where the image is. */
> +extern unsigned char __image_begin, __image_end;
> +
> +#define puts(s) do {} while (0)
> +#define puthex(val) do {} while (0)
> +
> +void error(char *x)
> +{
> +       puts("\n\n");
> +       puts(x);
> +       puts("\n\n -- System halted");
> +
> +       while (1)
> +               ;       /* Halt */
> +}
> +
> +/* activate the code for pre-boot environment */
> +#define STATIC static
> +
> +#include "../../../../lib/ashldi3.c"
> +
> +#ifdef CONFIG_KERNEL_GZIP
> +#include "../../../../lib/decompress_inflate.c"
> +#endif
> +
> +#ifdef CONFIG_KERNEL_BZIP2
> +#include "../../../../lib/decompress_bunzip2.c"
> +#endif
> +
> +#ifdef CONFIG_KERNEL_LZ4
> +#include "../../../../lib/decompress_unlz4.c"
> +#endif
> +
> +#ifdef CONFIG_KERNEL_LZMA
> +#include "../../../../lib/decompress_unlzma.c"
> +#endif
> +
> +#ifdef CONFIG_KERNEL_LZO
> +#include "../../../../lib/decompress_unlzo.c"
> +#endif
> +
> +#ifdef CONFIG_KERNEL_XZ
> +#include "../../../../lib/decompress_unxz.c"
> +#endif
> +
> +#ifdef CONFIG_KERNEL_ZSTD
> +#include "../../../../lib/decompress_unzstd.c"
> +#endif
> +
> +void decompress_kernel(unsigned long boot_heap_start)
> +{
> +       unsigned long zimage_start, zimage_size;
> +
> +       zimage_start = (unsigned long)(&__image_begin);
> +       zimage_size = (unsigned long)(&__image_end) -
> +           (unsigned long)(&__image_begin);
> +
> +       puts("zimage at:     ");
> +       puthex(zimage_start);
> +       puts(" ");
> +       puthex(zimage_size + zimage_start);
> +       puts("\n");
> +
> +       /* This area are prepared for mallocing when decompressing */
> +       free_mem_ptr = boot_heap_start;
> +       free_mem_end_ptr = boot_heap_start + BOOT_HEAP_SIZE;
> +
> +       /* Display standard Linux/LoongArch boot prompt */
> +       puts("Uncompressing Linux at load address ");
> +       puthex(VMLINUX_LOAD_ADDRESS);
> +       puts("\n");
> +
> +       /* Decompress the kernel with according algorithm */
> +       __decompress((char *)zimage_start, zimage_size, 0, 0,
> +                  (void *)VMLINUX_LOAD_ADDRESS, 0, 0, error);
> +
> +       puts("Now, booting the kernel...\n");
> +}
> diff --git a/arch/loongarch/boot/string.c b/arch/loongarch/boot/string.c
> new file mode 100644
> index 000000000000..3f746e7c2bb5
> --- /dev/null
> +++ b/arch/loongarch/boot/string.c
> @@ -0,0 +1,166 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * arch/loongarch/boot/string.c
> + *
> + * Very small subset of simple string routines
> + */
> +
> +#include <linux/types.h>
> +
> +void __weak *memset(void *s, int c, size_t n)
> +{
> +       int i;
> +       char *ss = s;
> +
> +       for (i = 0; i < n; i++)
> +               ss[i] = c;
> +       return s;
> +}
> +
> +void __weak *memcpy(void *dest, const void *src, size_t n)
> +{
> +       int i;
> +       const char *s = src;
> +       char *d = dest;
> +
> +       for (i = 0; i < n; i++)
> +               d[i] = s[i];
> +       return dest;
> +}
> +
> +void __weak *memmove(void *dest, const void *src, size_t n)
> +{
> +       int i;
> +       const char *s = src;
> +       char *d = dest;
> +
> +       if (d < s) {
> +               for (i = 0; i < n; i++)
> +                       d[i] = s[i];
> +       } else if (d > s) {
> +               for (i = n - 1; i >= 0; i--)
> +                       d[i] = s[i];
> +       }
> +
> +       return dest;
> +}
> +
> +int __weak memcmp(const void *cs, const void *ct, size_t count)
> +{
> +       int res = 0;
> +       const unsigned char *su1, *su2;
> +
> +       for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) {
> +               res = *su1 - *su2;
> +               if (res != 0)
> +                       break;
> +       }
> +       return res;
> +}
> +
> +int __weak strcmp(const char *str1, const char *str2)
> +{
> +       int delta = 0;
> +       const unsigned char *s1 = (const unsigned char *)str1;
> +       const unsigned char *s2 = (const unsigned char *)str2;
> +
> +       while (*s1 || *s2) {
> +               delta = *s1 - *s2;
> +               if (delta)
> +                       return delta;
> +               s1++;
> +               s2++;
> +       }
> +       return 0;
> +}
> +
> +size_t __weak strlen(const char *s)
> +{
> +       const char *sc;
> +
> +       for (sc = s; *sc != '\0'; ++sc)
> +               /* nothing */;
> +       return sc - s;
> +}
> +
> +size_t __weak strnlen(const char *s, size_t count)
> +{
> +       const char *sc;
> +
> +       for (sc = s; count-- && *sc != '\0'; ++sc)
> +               /* nothing */;
> +       return sc - s;
> +}
> +
> +char * __weak strnstr(const char *s1, const char *s2, size_t len)
> +{
> +       size_t l2;
> +
> +       l2 = strlen(s2);
> +       if (!l2)
> +               return (char *)s1;
> +       while (len >= l2) {
> +               len--;
> +               if (!memcmp(s1, s2, l2))
> +                       return (char *)s1;
> +               s1++;
> +       }
> +       return NULL;
> +}
> +
> +#undef strcat
> +char * __weak strcat(char *dest, const char *src)
> +{
> +       char *tmp = dest;
> +
> +       while (*dest)
> +               dest++;
> +       while ((*dest++ = *src++) != '\0')
> +               ;
> +       return tmp;
> +}
> +
> +char * __weak strncat(char *dest, const char *src, size_t count)
> +{
> +       char *tmp = dest;
> +
> +       if (count) {
> +               while (*dest)
> +                       dest++;
> +               while ((*dest++ = *src++) != 0) {
> +                       if (--count == 0) {
> +                               *dest = '\0';
> +                               break;
> +                       }
> +               }
> +       }
> +       return tmp;
> +}
> +
> +char * __weak strpbrk(const char *cs, const char *ct)
> +{
> +       const char *sc1, *sc2;
> +
> +       for (sc1 = cs; *sc1 != '\0'; ++sc1) {
> +               for (sc2 = ct; *sc2 != '\0'; ++sc2) {
> +                       if (*sc1 == *sc2)
> +                               return (char *)sc1;
> +               }
> +       }
> +       return NULL;
> +}
> +
> +char * __weak strsep(char **s, const char *ct)
> +{
> +       char *sbegin = *s;
> +       char *end;
> +
> +       if (sbegin == NULL)
> +               return NULL;
> +
> +       end = strpbrk(sbegin, ct);
> +       if (end)
> +               *end++ = '\0';
> +       *s = end;
> +       return sbegin;
> +}
> diff --git a/arch/loongarch/boot/zheader.S b/arch/loongarch/boot/zheader.S
> new file mode 100644
> index 000000000000..4bc50d953ec7
> --- /dev/null
> +++ b/arch/loongarch/boot/zheader.S
> @@ -0,0 +1,100 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/pe.h>
> +#include <linux/sizes.h>
> +
> +       .macro  __EFI_PE_HEADER
> +       .long   PE_MAGIC
> +coff_header:
> +       .short  IMAGE_FILE_MACHINE_LOONGARCH            /* Machine */
> +       .short  section_count                           /* NumberOfSections */
> +       .long   0                                       /* TimeDateStamp */
> +       .long   0                                       /* PointerToSymbolTable */
> +       .long   0                                       /* NumberOfSymbols */
> +       .short  section_table - optional_header         /* SizeOfOptionalHeader */
> +       .short  IMAGE_FILE_DEBUG_STRIPPED | \
> +               IMAGE_FILE_EXECUTABLE_IMAGE | \
> +               IMAGE_FILE_LINE_NUMS_STRIPPED           /* Characteristics */
> +
> +optional_header:
> +       .short  PE_OPT_MAGIC_PE32PLUS                   /* PE32+ format */
> +       .byte   0x02                                    /* MajorLinkerVersion */
> +       .byte   0x14                                    /* MinorLinkerVersion */
> +       .long   _data - efi_header_end                  /* SizeOfCode */
> +       .long   _end - _data                            /* SizeOfInitializedData */
> +       .long   0                                       /* SizeOfUninitializedData */
> +       .long   __efistub_efi_pe_entry - _head          /* AddressOfEntryPoint */
> +       .long   efi_header_end - _head                  /* BaseOfCode */
> +
> +extra_header_fields:
> +       .quad   0                                       /* ImageBase */
> +       .long   PECOFF_SEGMENT_ALIGN                    /* SectionAlignment */
> +       .long   PECOFF_FILE_ALIGN                       /* FileAlignment */
> +       .short  0                                       /* MajorOperatingSystemVersion */
> +       .short  0                                       /* MinorOperatingSystemVersion */
> +       .short  0                                       /* MajorImageVersion */
> +       .short  0                                       /* MinorImageVersion */
> +       .short  0                                       /* MajorSubsystemVersion */
> +       .short  0                                       /* MinorSubsystemVersion */
> +       .long   0                                       /* Win32VersionValue */
> +
> +       .long   _end - _head                            /* SizeOfImage */
> +
> +       /* Everything before the kernel image is considered part of the header */
> +       .long   efi_header_end - _head                  /* SizeOfHeaders */
> +       .long   0                                       /* CheckSum */
> +       .short  IMAGE_SUBSYSTEM_EFI_APPLICATION         /* Subsystem */
> +       .short  0                                       /* DllCharacteristics */
> +       .quad   0                                       /* SizeOfStackReserve */
> +       .quad   0                                       /* SizeOfStackCommit */
> +       .quad   0                                       /* SizeOfHeapReserve */
> +       .quad   0                                       /* SizeOfHeapCommit */
> +       .long   0                                       /* LoaderFlags */
> +       .long   (section_table - .) / 8                 /* NumberOfRvaAndSizes */
> +
> +       .quad   0                                       /* ExportTable */
> +       .quad   0                                       /* ImportTable */
> +       .quad   0                                       /* ResourceTable */
> +       .quad   0                                       /* ExceptionTable */
> +       .quad   0                                       /* CertificationTable */
> +       .quad   0                                       /* BaseRelocationTable */
> +
> +       /* Section table */
> +section_table:
> +       .ascii  ".text\0\0\0"
> +       .long   _data - efi_header_end                  /* VirtualSize */
> +       .long   efi_header_end - _head                  /* VirtualAddress */
> +       .long   _data - efi_header_end                  /* SizeOfRawData */
> +       .long   efi_header_end - _head                  /* PointerToRawData */
> +
> +       .long   0                                       /* PointerToRelocations */
> +       .long   0                                       /* PointerToLineNumbers */
> +       .short  0                                       /* NumberOfRelocations */
> +       .short  0                                       /* NumberOfLineNumbers */
> +       .long   IMAGE_SCN_CNT_CODE | \
> +               IMAGE_SCN_MEM_READ | \
> +               IMAGE_SCN_MEM_EXECUTE                   /* Characteristics */
> +
> +       .ascii  ".data\0\0\0"
> +       .long   _end - _data                            /* VirtualSize */
> +       .long   _data - _head                           /* VirtualAddress */
> +       .long   _edata - _data                          /* SizeOfRawData */
> +       .long   _data - _head                           /* PointerToRawData */
> +
> +       .long   0                                       /* PointerToRelocations */
> +       .long   0                                       /* PointerToLineNumbers */
> +       .short  0                                       /* NumberOfRelocations */
> +       .short  0                                       /* NumberOfLineNumbers */
> +       .long   IMAGE_SCN_CNT_INITIALIZED_DATA | \
> +               IMAGE_SCN_MEM_READ | \
> +               IMAGE_SCN_MEM_WRITE                     /* Characteristics */
> +
> +       .org 0x20e
> +       .word kernel_version - 512 -  _head
> +
> +       .set    section_count, (. - section_table) / 40
> +efi_header_end:
> +       .endm
> diff --git a/arch/loongarch/boot/zkernel.S b/arch/loongarch/boot/zkernel.S
> new file mode 100644
> index 000000000000..13a8a14a2328
> --- /dev/null
> +++ b/arch/loongarch/boot/zkernel.S
> @@ -0,0 +1,99 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/init.h>
> +#include <linux/linkage.h>
> +#include <asm/addrspace.h>
> +#include <asm/asm.h>
> +#include <asm/loongarch.h>
> +#include <asm/regdef.h>
> +#include <generated/compile.h>
> +#include <generated/utsrelease.h>
> +
> +#ifdef CONFIG_EFI_STUB
> +
> +#include "zheader.S"
> +
> +       __HEAD
> +
> +_head:
> +       /* "MZ", MS-DOS header */
> +       .word   MZ_MAGIC
> +       .org    0x28
> +       .ascii  "Loongson\0"
> +       .org    0x3c
> +       /* Offset to the PE header */
> +       .long   pe_header - _head
> +
> +pe_header:
> +       __EFI_PE_HEADER
> +
> +kernel_asize:
> +       .long _end - _text
> +
> +kernel_fsize:
> +       .long _edata - _text
> +
> +kernel_vaddr:
> +       .quad VMLINUZ_LOAD_ADDRESS
> +
> +kernel_offset:
> +       .long kernel_offset - _text
> +
> +kernel_version:
> +       .ascii  UTS_RELEASE " (" LINUX_COMPILE_BY "@" LINUX_COMPILE_HOST ") " UTS_VERSION "\0"
> +
> +SYM_L_GLOBAL(kernel_asize)
> +SYM_L_GLOBAL(kernel_fsize)
> +SYM_L_GLOBAL(kernel_vaddr)
> +SYM_L_GLOBAL(kernel_offset)
> +
> +#endif
> +
> +       __INIT
> +
> +SYM_CODE_START(kernel_entry)
> +       /* Save boot rom start args */
> +       move    s0, a0
> +       move    s1, a1
> +       move    s2, a2
> +       move    s3, a3
> +
> +       /* Config Direct Mapping */
> +       li.d    t0, CSR_DMW0_INIT
> +       csrwr   t0, LOONGARCH_CSR_DMWIN0
> +       li.d    t0, CSR_DMW1_INIT
> +       csrwr   t0, LOONGARCH_CSR_DMWIN1
> +
> +       /* Clear BSS */
> +       la.abs  a0, _edata
> +       la.abs  a2, _end
> +1:     st.d    zero, a0, 0
> +       addi.d  a0, a0, 8
> +       bne     a2, a0, 1b
> +
> +       la.abs  a0, .heap          /* heap address */
> +       la.abs  sp, .stack + 8192  /* stack address */
> +
> +       la      ra, 2f
> +       la      t4, decompress_kernel
> +       jirl    zero, t4, 0
> +2:
> +       move    a0, s0
> +       move    a1, s1
> +       move    a2, s2
> +       move    a3, s3
> +       PTR_LI  t4, KERNEL_ENTRY
> +       jirl    zero, t4, 0
> +3:
> +       b       3b
> +SYM_CODE_END(kernel_entry)
> +
> +       .comm .heap, BOOT_HEAP_SIZE, 4
> +       .comm .stack, BOOT_STACK_SIZE, 4
> +
> +       .align 4
> +       .section .image, "a", %progbits
> +       .incbin "arch/loongarch/boot/vmlinux.bin.z"
> diff --git a/arch/loongarch/tools/Makefile b/arch/loongarch/tools/Makefile
> new file mode 100644
> index 000000000000..8a6181c82a91
> --- /dev/null
> +++ b/arch/loongarch/tools/Makefile
> @@ -0,0 +1,15 @@
> +#
> +# arch/loongarch/boot/Makefile
> +#
> +# Copyright (C) 2020-2022 Loongson Technology Corporation Limited
> +#
> +
> +hostprogs := elf-entry
> +PHONY += elf-entry
> +elf-entry: $(obj)/elf-entry
> +       @:
> +
> +hostprogs += calc_vmlinuz_load_addr
> +PHONY += calc_vmlinuz_load_addr
> +calc_vmlinuz_load_addr: $(obj)/calc_vmlinuz_load_addr
> +       @:
> diff --git a/arch/loongarch/tools/calc_vmlinuz_load_addr.c b/arch/loongarch/tools/calc_vmlinuz_load_addr.c
> new file mode 100644
> index 000000000000..5e2ca6b4dff6
> --- /dev/null
> +++ b/arch/loongarch/tools/calc_vmlinuz_load_addr.c
> @@ -0,0 +1,51 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
> + */
> +
> +#include <errno.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/stat.h>
> +
> +int main(int argc, char *argv[])
> +{
> +       unsigned long long vmlinux_size, vmlinux_load_addr, vmlinuz_load_addr;
> +       struct stat sb;
> +
> +       if (argc != 3) {
> +               fprintf(stderr, "Usage: %s <pathname> <vmlinux_load_addr>\n", argv[0]);
> +               return EXIT_FAILURE;
> +       }
> +
> +       if (stat(argv[1], &sb) == -1) {
> +               perror("stat");
> +               return EXIT_FAILURE;
> +       }
> +
> +       /* Convert hex characters to dec number */
> +       errno = 0;
> +       if (sscanf(argv[2], "%llx", &vmlinux_load_addr) != 1) {
> +               if (errno != 0)
> +                       perror("sscanf");
> +               else
> +                       fprintf(stderr, "No matching characters\n");
> +
> +               return EXIT_FAILURE;
> +       }
> +
> +       vmlinux_size = (uint64_t)sb.st_size;
> +       vmlinuz_load_addr = vmlinux_load_addr + vmlinux_size;
> +
> +       /*
> +        * Align with 64KB: KEXEC needs load sections to be aligned to PAGE_SIZE,
> +        * which may be as large as 64KB depending on the kernel configuration.
> +        */
> +
> +       vmlinuz_load_addr += (0x10000 - vmlinux_size % 0x10000);
> +
> +       printf("0x%llx\n", vmlinuz_load_addr);
> +
> +       return EXIT_SUCCESS;
> +}
> diff --git a/arch/loongarch/tools/elf-entry.c b/arch/loongarch/tools/elf-entry.c
> new file mode 100644
> index 000000000000..c80721e0dee1
> --- /dev/null
> +++ b/arch/loongarch/tools/elf-entry.c
> @@ -0,0 +1,66 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <elf.h>
> +#include <inttypes.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +__attribute__((noreturn))
> +static void die(const char *msg)
> +{
> +       fputs(msg, stderr);
> +       exit(EXIT_FAILURE);
> +}
> +
> +int main(int argc, const char *argv[])
> +{
> +       uint64_t entry;
> +       size_t nread;
> +       FILE *file;
> +       union {
> +               Elf32_Ehdr ehdr32;
> +               Elf64_Ehdr ehdr64;
> +       } hdr;
> +
> +       if (argc != 2)
> +               die("Usage: elf-entry <elf-file>\n");
> +
> +       file = fopen(argv[1], "r");
> +       if (!file) {
> +               perror("Unable to open input file");
> +               return EXIT_FAILURE;
> +       }
> +
> +       nread = fread(&hdr, 1, sizeof(hdr), file);
> +       if (nread != sizeof(hdr)) {
> +               fclose(file);
> +               perror("Unable to read input file");
> +               return EXIT_FAILURE;
> +       }
> +
> +       if (memcmp(hdr.ehdr32.e_ident, ELFMAG, SELFMAG)) {
> +               fclose(file);
> +               die("Input is not an ELF\n");
> +       }
> +
> +       switch (hdr.ehdr32.e_ident[EI_CLASS]) {
> +       case ELFCLASS32:
> +               /* Sign extend to form a canonical address */
> +               entry = (int64_t)(int32_t)hdr.ehdr32.e_entry;
> +               break;
> +
> +       case ELFCLASS64:
> +               entry = hdr.ehdr64.e_entry;
> +               break;
> +
> +       default:
> +               fclose(file);
> +               die("Invalid ELF class\n");
> +       }
> +
> +       fclose(file);
> +       printf("0x%016" PRIx64 "\n", entry);
> +
> +       return EXIT_SUCCESS;
> +}
> --
> 2.27.0
>



More information about the linux-riscv mailing list