arch/arm/boot/compressed/Makefileをもうちょっと読もう

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm/boot/compressed/Makefile?h=master

Makefileの中身を読み解いていく。興味のないfdtの部分は省略します。

# TEXT領域の設定などなど。

```makefile:arch/arm/boot/compressed/Makefile
# SPDX-License-Identifier: GPL-2.0
#
# linux/arch/arm/boot/compressed/Makefile
#
# create a compressed vmlinuz image from the original vmlinux
#

OBJS		=
AFLAGS_head.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
```

head.oを作るときに、TEXT_OFFSETのオプションを追加しますという意味。

## TEXT_OFFSET
$(TEXT_OFFSET)は、arch/arm/Makefile内で定義。

```c:arch/arm/Makefile
# Text offset. This list is sorted numerically by address in order to
# provide a means to avoid/resolve conflicts in multi-arch kernels.
textofs-y	:= 0x00008000
# We don't want the htc bootloader to corrupt kernel during resume
textofs-$(CONFIG_PM_H1940)      := 0x00108000
# SA1111 DMA bug: we don't want the kernel to live in precious DMA-able memory
ifeq ($(CONFIG_ARCH_SA1100),y)
textofs-$(CONFIG_SA1111) := 0x00208000
endif
textofs-$(CONFIG_ARCH_IPQ40XX) := 0x00208000
textofs-$(CONFIG_ARCH_MSM8X60) := 0x00208000
textofs-$(CONFIG_ARCH_MSM8960) := 0x00208000
textofs-$(CONFIG_ARCH_MESON) := 0x00208000
textofs-$(CONFIG_ARCH_AXXIA) := 0x00308000

（略）

TEXT_OFFSET := $(textofs-y)
```

# kernelとして実処理するコード(head.*/misc.*/decompress.*)

```makefile:arch/arm/boot/compressed/Makefile
HEAD	= head.o
OBJS	+= misc.o decompress.o
```

## head.S 
ここは、圧縮Linux Kernel展開部そのもののロジック。

色々やるが、decompress_kernelを呼び出す。

```c:arch/arm/boot/compressed/head.S
/*
 * The C runtime environment should now be setup sufficiently.
 * Set up some pointers, and start decompressing.
 *   r4  = kernel execution address
 *   r7  = architecture ID
 *   r8  = atags pointer
 */
		mov	r0, r4
		mov	r1, sp			@ malloc space above stack
		add	r2, sp, #0x10000	@ 64k max
		mov	r3, r7
		bl	decompress_kernel
```

## misc.S
decompress_kernel()本体などが記載されている。

実際にはこの中から、do_decompress()が呼ばれる。

```c:arch/arm/boot/compressed/misc.c
void
decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p,
		unsigned long free_mem_ptr_end_p,
		int arch_id)
{
	int ret;

	output_data		= (unsigned char *)output_start;
	free_mem_ptr		= free_mem_ptr_p;
	free_mem_end_ptr	= free_mem_ptr_end_p;
	__machine_arch_type	= arch_id;

	arch_decomp_setup();

	putstr("Uncompressing Linux...");
	ret = do_decompress(input_data, input_data_end - input_data,
			    output_data, error);
	if (ret)
		error("decompressor returned an error");
	else
		putstr(" done, booting the kernel.\n");
}
```

## decompress.S

各圧縮モジュールの__decompress()を呼び出して解凍する部分。

```c:arch/arm/boot/compressed/decompress.c
#ifdef CONFIG_KERNEL_GZIP
#include "../../../../lib/decompress_inflate.c"
#endif

#ifdef CONFIG_KERNEL_LZO
#include "../../../../lib/decompress_unlzo.c"
#endif

#ifdef CONFIG_KERNEL_LZMA
#include "../../../../lib/decompress_unlzma.c"
#endif

#ifdef CONFIG_KERNEL_XZ
#define memmove memmove
#define memcpy memcpy
#include "../../../../lib/decompress_unxz.c"
#endif

#ifdef CONFIG_KERNEL_LZ4
#include "../../../../lib/decompress_unlz4.c"
#endif

int do_decompress(u8 *input, int len, u8 *output, void (*error)(char *x))
{
	return __decompress(input, len, NULL, NULL, output, 0, NULL, error);
}
```

例えば、lz4ならば以下。

```c:lib/decompress_unlz4.c
#ifdef PREBOOT
STATIC int INIT __decompress(unsigned char *buf, long in_len,
                  long (*fill)(void*, unsigned long),
                  long (*flush)(void*, unsigned long),
                  unsigned char *output, long out_len,
                  long *posp,
                  void (*error)(char *x)
    )
{
    return unlz4(buf, in_len - 4, fill, flush, output, posp, error);
}
#endif

```

# debug用の文字出力

```makefile:arch/arm/boot/compressed/Makefile
ifeq ($(CONFIG_DEBUG_UNCOMPRESS),y)
OBJS	+= debug.o
endif
FONTC	= $(srctree)/lib/fonts/font_acorn_8x8.c
```

font_acorn_8x8.c ってacorn以外使ってないんじゃないかなあ……

## DEBUG_UNCOMPRESS

DEBUG_UNCOMPRESSは、KConfig.debugの中で定義。

このオプションでは、multiplatform kernelでdecompressorの出力に関する。
通常、multiplatform kernelsではdecompressorのすることはよくない。
なぜならば、どうやってdecompressorの出力をどこに送ればいいのかわからないから。

このオプションが設定されたら、DEBUG_LL output 手段によって、再利用される。

ということで、デバッグできますと。

```config:arch/arm/Kconfig.debug
config DEBUG_UNCOMPRESS
	bool "Enable decompressor debugging via DEBUG_LL output"
	depends on ARCH_MULTIPLATFORM || PLAT_SAMSUNG || ARM_SINGLE_ARMV7M
	depends on DEBUG_LL && !DEBUG_OMAP2PLUS_UART && \
		     (!DEBUG_TEGRA_UART || !ZBOOT_ROM) && \
		     !DEBUG_BRCMSTB_UART
	help
	  This option influences the normal decompressor output for
	  multiplatform kernels.  Normally, multiplatform kernels disable
	  decompressor output because it is not possible to know where to
	  send the decompressor output.

	  When this option is set, the selected DEBUG_LL output method
	  will be re-used for normal decompressor output on multiplatform
	  kernels.

```

## debug.S

debug.sは、putc()の実装のための使う。CONFIG_DEBUG_SEMIHOSTINGが無効化されている場合には、adduart/waituart/senduart/busyuart で文字をコンソールへ出力する。

```c:arm/boot/compressed/debug.S
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/linkage.h>
#include <asm/assembler.h>

#ifndef CONFIG_DEBUG_SEMIHOSTING

#include CONFIG_DEBUG_LL_INCLUDE

ENTRY(putc)
	addruart r1, r2, r3
	waituart r3, r1
	senduart r0, r1
	busyuart r3, r1
	mov	 pc, lr
ENDPROC(putc)

#else
（略）

```

# string library

string.c は、memcpu/memmove/strlen/strnlenなどのサブセット。
decompressorが動くときに単独で動かせるように組み込まれていると推定。

```makefile:arch/arm/boot/compressed/Makefile
# string library code (-Os is enforced to keep it much smaller)
OBJS		+= string.o
CFLAGS_string.o	:= -Os
```

## string.c

中身は、string.hの中で定義されている関数の簡易版の実装。

```c:arch/arm/boot/compressed/string.c
void *memcpy(void *__dest, __const void *__src, size_t __n)
{
	int i = 0;
	unsigned char *d = (unsigned char *)__dest, *s = (unsigned char *)__src;

	for (i = __n >> 3; i > 0; i--) {
		*d++ = *s++;
		*d++ = *s++;
		*d++ = *s++;
		*d++ = *s++;
		*d++ = *s++;
		*d++ = *s++;
		*d++ = *s++;
		*d++ = *s++;
	}

	if (__n & 1 << 2) {
		*d++ = *s++;
		*d++ = *s++;
		*d++ = *s++;
		*d++ = *s++;
	}

	if (__n & 1 << 1) {
		*d++ = *s++;
		*d++ = *s++;
	}

	if (__n & 1)
		*d++ = *s++;

	return __dest;
}
```


# VIRT_EXT

```makefile:arch/arm/boot/compressed/Makefile
ifeq ($(CONFIG_ARM_VIRT_EXT),y)
OBJS		+= hyp-stub.o
endif
```

## ARM_VIRT_EXT

```config:arch/arm/mm/Kconfig
config ARM_VIRT_EXT
    bool
    default y if CPU_V7
    help
      Enable the kernel to make use of the ARM Virtualization
      Extensions to install hypervisors without run-time firmware
      assistance.

      A compliant bootloader is required in order to make maximum
      use of this feature.  Refer to Documentation/arm/booting.rst for
      details.
```

# GCOV/KCOV

デバッグ用の機能は無効化されている。

```makefile:arch/arm/boot/compressed/Makefile
GCOV_PROFILE		:= n

# Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
KCOV_INSTRUMENT		:= n
```

# 機種依存部

このあたりは機種依存部なので省略。

```makefile:arch/arm/boot/compressed/Makefile
#
# Architecture dependencies
#
ifeq ($(CONFIG_ARCH_ACORN),y)
OBJS		+= ll_char_wr.o font.o
endif

＜略＞
```

# アドレスの指定(ZTEXTADDR/ZBSSADDR)

- decompressor 実装はPIC(アドレス非依存実装）になっている。
- DecompressorをRAM上で動かす場合には、ZTEXTADDRを指定してはいけない。
- DecompressorをROM/Flashで動かす場合には、指定が必要。

```makefile:arch/arm/boot/compressed/Makefile
#
# We now have a PIC decompressor implementation.  Decompressors running
# from RAM should not define ZTEXTADDR.  Decompressors running directly
# from ROM or Flash must define ZTEXTADDR (preferably via the config)
# FIXME: Previous assignment to ztextaddr-y is lost here. See SHARK
ifeq ($(CONFIG_ZBOOT_ROM),y)
ZTEXTADDR	:= $(CONFIG_ZBOOT_ROM_TEXT)
ZBSSADDR	:= $(CONFIG_ZBOOT_ROM_BSS)
else
ZTEXTADDR	:= 0
ZBSSADDR	:= ALIGN(8)
endif

CPPFLAGS_vmlinux.lds := -DTEXT_START="$(ZTEXTADDR)" -DBSS_START="$(ZBSSADDR)"

```

# 圧縮方式の確定

KConfigで指定した圧縮形式の指定。

```makefile:arch/arm/boot/compressed/Makefile

compress-$(CONFIG_KERNEL_GZIP) = gzip
compress-$(CONFIG_KERNEL_LZO)  = lzo
compress-$(CONFIG_KERNEL_LZMA) = lzma
compress-$(CONFIG_KERNEL_XZ)   = xzkern
compress-$(CONFIG_KERNEL_LZ4)  = lz4

```

# fdt部（省略）

```makefile:arch/arm/boot/compressed/Makefile

# Borrowed libfdt files for the ATAG compatibility mode
＜略＞

```

# Make用のtarget/clean-files設定

```makefile:arch/arm/boot/compressed/Makefile
targets       := vmlinux vmlinux.lds piggy_data piggy.o \
		 lib1funcs.o ashldi3.o bswapsdi2.o \
		 head.o $(OBJS)

clean-files += piggy_data lib1funcs.S ashldi3.S bswapsdi2.S \
		$(libfdt) $(libfdt_hdrs) hyp-stub.S

KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING

ifeq ($(CONFIG_FUNCTION_TRACER),y)
ORIG_CFLAGS := $(KBUILD_CFLAGS)
KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
endif
```

# fdt部2（省略）

stack-protector-strongを使うにはまだ早いらしい。

```makefile:arch/arm/boot/compressed/Makefile
# -fstack-protector-strong triggers protection checks in this code,
# but it is being used too early to link to meaningful stack_chk logic.
nossp-flags-$(CONFIG_CC_HAS_STACKPROTECTOR_NONE) := -fno-stack-protector
CFLAGS_atags_to_fdt.o := $(nossp-flags-y)
＜略＞
```

# BSSサイズの指定。
\_\_bss_startと\_\_bss_stopの間を使って計算しているっぽい。それが、Linkerへのオプションとして伝達される。

```makefile:arch/arm/boot/compressed/Makefile
# Supply kernel BSS size to the decompressor via a linker symbol.
KBSS_SZ = $(shell echo $$(($$($(NM) $(obj)/../../../../vmlinux | \
		sed -n -e 's/^\([^ ]*\) [AB] __bss_start$$/-0x\1/p' \
		       -e 's/^\([^ ]*\) [AB] __bss_stop$$/+0x\1/p') )) )
LDFLAGS_vmlinux = --defsym _kernel_bss_size=$(KBSS_SZ)
# Supply ZRELADDR to the decompressor via a linker symbol.
ifneq ($(CONFIG_AUTO_ZRELADDR),y)
LDFLAGS_vmlinux += --defsym zreladdr=$(ZRELADDR)
endif
```

# vmlinuxリンク時の他の設定

- BigEndian対応
- 未解決参照はダメ
- 一時的なローカルシンボルの削除
- -Tオプションはlinker script用だよ、かな？

```makefile:arch/arm/boot/compressed/Makefile
ifeq ($(CONFIG_CPU_ENDIAN_BE8),y)
LDFLAGS_vmlinux += --be8
endif
# Report unresolved symbol references
LDFLAGS_vmlinux += --no-undefined
# Delete all temporary local symbols
LDFLAGS_vmlinux += -X
# Next argument is a linker script
LDFLAGS_vmlinux += -T
```

# 存在しない命令のための対応。

存在しない関数をごにょごにょするために用意されたコードの配備。

```makefile:arch/arm/boot/compressed/Makefile
# For __aeabi_uidivmod
lib1funcs = $(obj)/lib1funcs.o

$(obj)/lib1funcs.S: $(srctree)/arch/$(SRCARCH)/lib/lib1funcs.S
	$(call cmd,shipped)

# For __aeabi_llsl
ashldi3 = $(obj)/ashldi3.o

$(obj)/ashldi3.S: $(srctree)/arch/$(SRCARCH)/lib/ashldi3.S
	$(call cmd,shipped)

# For __bswapsi2, __bswapdi2
bswapsdi2 = $(obj)/bswapsdi2.o

$(obj)/bswapsdi2.S: $(srctree)/arch/$(SRCARCH)/lib/bswapsdi2.S
	$(call cmd,shipped)
```

## lib1funcs.S
除算の最適化ルーチンらしい。

https://gcc.gnu.org/onlinedocs/gccint/Integer-library-routines.html

下記の関数などなどが含まれている。

>Runtime Function: int __divsi3 (int a, int b)
Runtime Function: long __divdi3 (long a, long b)
Runtime Function: long long __divti3 (long long a, long long b)
　　These functions return the quotient of the signed division of a and b.




```c:arch/arm/lib/lib1funcs.S
/*
 * linux/arch/arm/lib/lib1funcs.S: Optimized ARM division routines
 *
 * Author: Nicolas Pitre <nico@fluxnic.net>
 *   - contributed to gcc-3.4 on Sep 30, 2003
 *   - adapted for the Linux kernel on Oct 2, 2003
 */
```

## ashldi3.S/bswapsdi2.S
https://gcc.gnu.org/onlinedocs/gccint/Integer-library-routines.html

ここら辺に何の関数か記載がある。

>Runtime Function: int __ashlsi3 (int a, int b)
Runtime Function: long __ashldi3 (long a, int b)
Runtime Function: long long __ashlti3 (long long a, int b)
　　These functions return the result of shifting a left by b bits.
>
>Runtime Function: int32_t __bswapsi2 (int32_t a)
Runtime Function: int64_t __bswapdi2 (int64_t a)
　　These functions return the a byteswapped.




# シンボルのチェック。

「以下のsymbolは、local/private scopeにあってはいけません」とのこと。

```makefile:arch/arm/boot/compressed/Makefile

# We need to prevent any GOTOFF relocs being used with references
# to symbols in the .bss section since we cannot relocate them
# independently from the rest at run time.  This can be achieved by
# ensuring that no private .bss symbols exist, as global symbols
# always have a GOT entry which is what we need.
# The .data section is already discarded by the linker script so no need
# to bother about it here.
check_for_bad_syms = \
bad_syms=$$($(NM) $@ | sed -n 's/^.\{8\} [bc] \(.*\)/\1/p') && \
[ -z "$$bad_syms" ] || \
  ( echo "following symbols must have non local/private scope:" >&2; \
    echo "$$bad_syms" >&2; false )
```

# ZRELADDR関係のチェック

ZRELADDRが設定されず、AUTO_ZRELADDRが設定もされてなかったら怒られる、かな？

```makefile:arch/arm/boot/compressed/Makefile
check_for_multiple_zreladdr = \
if [ $(words $(ZRELADDR)) -gt 1 -a "$(CONFIG_AUTO_ZRELADDR)" = "" ]; then \
	echo 'multiple zreladdrs: $(ZRELADDR)'; \
	echo 'This needs CONFIG_AUTO_ZRELADDR to be set'; \
	false; \
fi
```

# EFI対応

（興味なしで省略）

```makefile:arch/arm/boot/compressed/Makefile
efi-obj-$(CONFIG_EFI_STUB) := $(objtree)/drivers/firmware/efi/libstub/lib.a
```

# vmlinuxの作り方！！（ここが最終ライン）

```makefile:arch/arm/boot/compressed/Makefile
$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.o \
		$(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) \
		$(bswapsdi2) $(efi-obj-y) FORCE
	@$(check_for_multiple_zreladdr)
	$(call if_changed,ld)
	@$(check_for_bad_syms)
```

# Imageを圧縮する

compress-y は、 `compress-$(CONFIG_KERNEL_XXXX)   = CMD ` の形で指定した圧縮プログラムが入る。これを使って、Imageをpiggy_dataに亜ssy九


```makefile:arch/arm/boot/compressed/Makefile

$(obj)/piggy_data: $(obj)/../Image FORCE
	$(call if_changed,$(compress-y))

$(obj)/piggy.o: $(obj)/piggy_data

```

## piggy.S

piggy_dataをバイナリデータとして取り込んでおしまい。

```c:arch/arm/boot/compressed/piggy.S
/* SPDX-License-Identifier: GPL-2.0 */
	.section .piggydata, "a"
	.globl	input_data
input_data:
	.incbin	"arch/arm/boot/compressed/piggy_data"
	.globl	input_data_end
input_data_end:
```




# あとは各種オプションの指定


```makefile:arch/arm/boot/compressed/Makefile

CFLAGS_font.o := -Dstatic=

$(obj)/font.c: $(FONTC)
	$(call cmd,shipped)

AFLAGS_hyp-stub.o := -Wa,-march=armv7-a

$(obj)/hyp-stub.S: $(srctree)/arch/$(SRCARCH)/kernel/hyp-stub.S
	$(call cmd,shipped)
```