-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathbuild-kernel
More file actions
executable file
·463 lines (447 loc) · 23.7 KB
/
Copy pathbuild-kernel
File metadata and controls
executable file
·463 lines (447 loc) · 23.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
#!/bin/sh
# This file is part of KASLD - https://github.com/bcoles/kasld
#
# tests/vm/build-kernel — build a bootable kernel from kernel.org source for the
# gap architectures Alpine does not ship (the tests/vm `flavor=local` set: mips,
# mipsel, riscv32, ppc32, armeb). Output is staged to
# tests/vm/.cache/<cell>-vmlinuz (cell = <arch>-mainline-<version>), which
# tests/vm/run then boots with the same init.c initramfs as the Alpine flavors.
# Alpine stays the default for every arch it ports; this only fills gaps.
#
# Reproducible: a pinned kernel.org source tarball + a stock upstream defconfig +
# a fixed overlay (endianness where the target byte order differs from the base
# defconfig — e.g. mips forces BE over malta's LE default — plus devtmpfs so the
# initramfs init gets a console). No config content is maintained here. Slow;
# run manually.
#
# Prereqs: the arch's cross toolchain on PATH (the same set `make cross` uses,
# located via build/<prefix>-*), a host C toolchain + the kernel build deps
# (bison flex bc libelf/elfutils openssl), and curl + xz.
#
# Usage: tests/vm/build-kernel [arch...] (default: the bootable gap arches)
# Env: LINUX_VERSION pinned source version (default below)
# LINUX_MIRROR kernel.org mirror base
# BUILD_DIR kasld build root (to find the cross toolchain triple)
# JOBS parallel make jobs
# PRUNE_TREE=1 reduce each build tree to its configuration once the
# kernel, modules and release are staged (see prune_tree)
# ---
# <bcoles@gmail.com>
set -u
HERE=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)
ROOT=$(CDPATH='' cd -- "$HERE/../.." && pwd)
CACHE=$HERE/.cache
BUILD_DIR=${BUILD_DIR:-$ROOT/build}
# Pinned to current mainline (7.0). Builds clean with a C23-default compiler
# (gcc 15); the pre-6.13 MIPS vdso true/false/bool-keyword breakage that blocked
# 6.12 LTS is long resolved. riscv32 has no standalone rv32_defconfig here (it
# was removed after 6.15) — build_one configures it from `defconfig` plus the
# `32-bit.config` fragment instead.
LINUX_VERSION=${LINUX_VERSION:-7.0}
LINUX_MIRROR=${LINUX_MIRROR:-https://cdn.kernel.org/pub/linux/kernel}
JOBS=${JOBS:-$(nproc 2>/dev/null || echo 1)}
# Cells are named <arch>-<distro>-<kernel maj.minor>; build-kernel only ever
# builds the `mainline` (kernel.org) cells. Default build set: the gap arches
# with no Alpine port, at the current 7.0 pin. Everything else is built only on
# request (pass the cell name):
# armeb-* — builds and boots. The toolchain emits BE32 unless given
# -mbe8, and BE32 cannot execute on the BE8 userspace an arm
# kernel runs from ARMv6 on; `make cross` and the harness's
# init build both pass it.
# <arch>-mainline-<ver> — mainline is the primary matrix: every arch spans a
# set of kernel lines (currently 5.15 LTS, 6.6 LTS, 7.0), so
# each has one cell per version. Pass the matching
# LINUX_VERSION (e.g. LINUX_VERSION=6.6.144 for a `-6.6` cell):
# the version in the name is a label; the caller sets the
# source version. Alpine cells are a bonus, not built here.
ALL_ARCHES="mips-mainline-7.0 mipsel-mainline-7.0 mips64el-mainline-7.0
riscv32-mainline-7.0 ppc32-mainline-7.0 powerpc64-mainline-7.0"
# arch -> "tc-prefix kernel-ARCH defconfig image endian"
# tc-prefix : build/<prefix>-* dir whose triple gives CROSS_COMPILE
# image : make target; staged from <O>/vmlinux or <O>/arch/<ARCH>/boot/<img>
# endian : - (use defconfig as-is), le, or be (one-line overlay)
spec_for() {
# The build recipe depends only on the arch, so key on the leading
# dash-component of the <arch>-<distro>-<version> cell name. (Alpine cells never
# reach here — only kernel.org 'local' cells are built.) ppc64le has no
# ppc64le_defconfig on this tree, so it builds ppc64_defconfig with the `le`
# overlay (CPU_LITTLE_ENDIAN, selecting the ELF v2 ABI); its toolchain prefix is
# powerpc64le, distinct from the BE powerpc64 gap arch.
set -- "${1%%-*}"
case "$1" in
mips) echo "mips mips malta_defconfig vmlinux be" ;;
mipsel) echo "mipsel mips malta_defconfig vmlinux le" ;;
mips64el) echo "mips64el mips malta_defconfig vmlinux le" ;;
riscv32) echo "riscv32 riscv defconfig Image -" ;;
ppc32) echo "powerpc powerpc mpc85xx_defconfig vmlinux -" ;;
powerpc64) echo "powerpc64 powerpc ppc64_defconfig vmlinux -" ;;
armeb) echo "armeb arm multi_v7_defconfig zImage be" ;;
x86_64) echo "x86_64 x86 x86_64_defconfig bzImage -" ;;
aarch64) echo "aarch64 arm64 defconfig Image -" ;;
armv7) echo "armv7 arm multi_v7_defconfig zImage -" ;;
i686) echo "i686 x86 i386_defconfig bzImage -" ;;
s390x) echo "s390x s390 defconfig bzImage -" ;;
riscv64) echo "riscv64 riscv defconfig Image -" ;;
ppc64le) echo "powerpc64le powerpc ppc64_defconfig vmlinux le" ;;
loongarch64) echo "loongarch64 loongarch defconfig vmlinuz.efi -" ;;
*) echo "" ;;
esac
}
# tc-prefix -> "triple-" for CROSS_COMPILE (the make-cross triple whose gcc is on
# PATH); empty if none. Naming-agnostic, like tests/run and tests/test-cross.
cross_for() {
for d in "$BUILD_DIR/$1"-*; do
[ -d "$d" ] || continue
t=$(basename "$d")
command -v "${t}-gcc" >/dev/null 2>&1 && { echo "${t}-"; return; }
done
}
src=$CACHE/linux-$LINUX_VERSION
fetch_src() {
[ -d "$src" ] && return 0
mkdir -p "$CACHE"
maj=${LINUX_VERSION%%.*}
url="$LINUX_MIRROR/v${maj}.x/linux-$LINUX_VERSION.tar.xz"
echo "[*] fetching $url"
curl -fSL "$url" | tar -xJ -C "$CACHE" || { echo "!! source fetch failed"; return 1; }
}
# Build and stage whichever candidate drivers this cell's .config marks =m.
#
# NAMED kbuild targets, never `make modules`: a mainline defconfig marks
# hundreds of drivers =m (i915, sunrpc, 9p), and building all of them costs
# several times the kernel itself for two files nothing else needs.
#
# Both where both are available, so /proc/modules holds a RANGE -- the module
# band's floor and ceiling are then both observed rather than one address
# standing in for both.
# Reduce a built tree to what identifies it. A tree is ~1.7 GiB and a batch of
# them does not fit alongside the rest of the cache; nothing downstream reads one
# — tests/vm/run boots the staged kernel and loads the staged modules. .config
# and include/config/kernel.release stay, so the cell's configuration remains on
# record and a rebuild reproduces the same kernel.
#
# Keeps the tree unless all three artefacts are staged. A tree pruned after a
# partial build is indistinguishable afterwards from one that was never built,
# and a full rebuild is the only way back — so the ambiguous case keeps the tree
# and says which artefact was missing.
#
# A pruned tree can no longer build modules: Module.symvers goes with it, and
# without it modpost resolves every kernel symbol as undefined. stage-modules
# checks for that file and reports the tree as needing a rebuild.
prune_tree() { # cell build-dir
_pc=$1 _pb=$2
for _need in "$CACHE/$_pc-vmlinuz:kernel" "$CACHE/$_pc-kernel.release:release"; do
[ -s "${_need%:*}" ] && continue
echo " [*] keeping tree: no staged ${_need##*:}"
return 0
done
if [ -z "$(find "$CACHE/$_pc-modules" -name '*.ko' 2>/dev/null | head -1)" ]; then
echo " [*] keeping tree: no staged modules"
return 0
fi
_pt=$(mktemp -d) || return 0
cp "$_pb/.config" "$_pt/" 2>/dev/null
cp "$_pb/include/config/kernel.release" "$_pt/kernel.release" 2>/dev/null
rm -rf "$_pb"
mkdir -p "$_pb/include/config"
cp "$_pt/.config" "$_pb/" 2>/dev/null
cp "$_pt/kernel.release" "$_pb/include/config/" 2>/dev/null
rm -rf "$_pt"
echo " [*] pruned build tree (kept .config and kernel.release)"
}
stage_modules() { # cell kernel-ARCH cross-prefix build-dir
_cell=$1 _ka=$2 _cc=$3 _bdir=$4
_targets=''
_names=''
for _m in dummy veth; do
_sym=$(echo "$_m" | tr '[:lower:]' '[:upper:]')
grep -q "^CONFIG_$_sym=m$" "$_bdir/.config" || continue
_targets="$_targets drivers/net/$_m.ko"
_names="$_names $_m"
done
[ -n "$_targets" ] || { echo " !! no candidate driver is modular here"; return 1; }
# shellcheck disable=SC2086 # intentional word-split of the target list
make -C "$src" O="$_bdir" ARCH="$_ka" CROSS_COMPILE="$_cc" -j"$JOBS" \
$_targets >/dev/null || { echo " !! module build failed"; return 1; }
rm -rf "$CACHE/$_cell-modules"
mkdir -p "$CACHE/$_cell-modules"
for _m in $_names; do
_f=$(find "$_bdir/drivers/net" -name "$_m.ko" | head -1)
[ -n "$_f" ] && cp "$_f" "$CACHE/$_cell-modules/"
done
_nko=$(find "$CACHE/$_cell-modules" -name '*.ko' | wc -l)
[ "$_nko" -gt 0 ] || { echo " !! no modules staged"; return 1; }
echo " [*] staged $_nko module(s):$_names -> $CACHE/$_cell-modules"
}
build_one() {
arch=$1
base=${arch%%-*} # <arch>-<distro>-<version> -> <arch>, for arch-specific tweaks
spec=$(spec_for "$arch")
[ -n "$spec" ] || { echo "SKIP $arch (no build spec)"; return 0; }
# shellcheck disable=SC2086 # spec fields are space-separated by construction
set -- $spec
tcp=$1 ka=$2 defc=$3 image=$4 endian=$5
cc=$(cross_for "$tcp")
[ -n "$cc" ] || { echo "SKIP $arch (no $tcp toolchain on PATH — run make cross)"; return 0; }
bdir=$CACHE/kbuild-$arch
echo "=== $arch: ARCH=$ka $defc CROSS_COMPILE=$cc (endian=$endian) ==="
make -C "$src" O="$bdir" ARCH="$ka" CROSS_COMPILE="$cc" "$defc" >/dev/null ||
{ echo " !! $defc failed"; return 1; }
# Config overlay. (1) Endianness, where the target order differs from the base
# defconfig. (2) devtmpfs + auto-mount, so the kernel creates /dev/console
# before exec'ing init — some defconfigs (e.g. pmac32) omit devtmpfs, leaving
# the initramfs init with no console and no captured output. Harmless where on.
case "$endian" in
le) "$src/scripts/config" --file "$bdir/.config" -d CPU_BIG_ENDIAN -e CPU_LITTLE_ENDIAN ;;
be) "$src/scripts/config" --file "$bdir/.config" -d CPU_LITTLE_ENDIAN -e CPU_BIG_ENDIAN ;;
esac
# 64-bit MIPS on the malta board: the stock malta_defconfig is 32-bit, so
# promote its CPU to MIPS64 (selects 64BIT) for the mips64* targets.
case "$base" in
mips64 | mips64el)
"$src/scripts/config" --file "$bdir/.config" \
-d CPU_MIPS32_R2 -e CPU_MIPS64_R2 -e 64BIT -d 32BIT ;;
esac
# riscv32: use the standalone rv32_defconfig where the tree still ships it
# (<= 6.15); on newer trees it was removed, so merge the `32-bit.config`
# fragment onto the plain `defconfig` (already applied above) instead.
case "$base" in
riscv32)
if [ -f "$src/arch/riscv/configs/rv32_defconfig" ]; then
make -C "$src" O="$bdir" ARCH="$ka" CROSS_COMPILE="$cc" rv32_defconfig >/dev/null ||
{ echo " !! rv32_defconfig failed"; return 1; }
else
make -C "$src" O="$bdir" ARCH="$ka" CROSS_COMPILE="$cc" 32-bit.config >/dev/null ||
{ echo " !! 32-bit.config merge failed"; return 1; }
fi ;;
esac
# ppc64le: the little-endian powerpc musl toolchain is pure 64-bit, so the
# 32-bit compat VDSO (built under CONFIG_COMPAT) fails with "-m32 not
# supported". Disable 32-bit userspace compat — the minimal 64-bit init needs
# no 32-bit ABI. (The big-endian powerpc64 gap arch uses a biarch toolchain and
# is unaffected.)
case "$base" in
ppc64le)
"$src/scripts/config" --file "$bdir/.config" -d COMPAT ;;
esac
# ppc32 (Freescale e500v2 via qemu's generic ppce500 board): enable that board
# (PPC_QEMU_E500, required to boot -M ppce500) and text KASLR (RELOCATABLE +
# RANDOMIZE_BASE — the latter is available only on PPC_85xx + FLATMEM, both of
# which mpc85xx_defconfig provides; e500v2 stays FLATMEM, so the option holds).
# The 8250 console and initrd support come from the base defconfig, and e500
# KASLR self-seeds from the timebase, so no device-tree seed injection is needed
# (unlike riscv64). pmac/g3beige is not used: its book3s32 kernel has no text
# KASLR, so it could only ever report the disabled-base pin.
case "$base" in
ppc32)
# mpc85xx_defconfig ships CONFIG_DEBUG_INFO=y, which bloats vmlinux to ~270 MB;
# since run packages the kernel image into the initramfs, that overflows the
# e500's RAM at unpack time ("initrd: incomplete write -28"). Turn debug info
# off to keep the image small, like the other gap arches: pre-5.18 it is a
# plain bool (-d DEBUG_INFO); 5.18+ it is a choice, so the selected DWARF
# member must be disabled and NONE selected. CRYPTO_DEV_TALITOS (the Freescale
# SEC accel, unused here) fails to build on 6.6 with the current toolchain, so
# drop it.
"$src/scripts/config" --file "$bdir/.config" \
-e PPC_QEMU_E500 -e RELOCATABLE -e RANDOMIZE_BASE -d CRYPTO_DEV_TALITOS \
-d DEBUG_INFO -d DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT -e DEBUG_INFO_NONE ;;
esac
# riscv64: the stock defconfig leaves RANDOMIZE_BASE off, so text KASLR is
# compiled out and the kernel always boots at its link address. Enable it (and
# its RELOCATABLE prerequisite) so the mainline riscv64 cells exercise the
# randomized layout, matching the KASLR-on posture of the other 64-bit arches.
# riscv KASLR needs a boot-supplied seed (DT /chosen/kaslr-seed or the Zkr CSR),
# which tests/vm/run injects. Harmless on trees predating riscv KASLR (5.18-):
# the symbols do not exist there, so olddefconfig simply drops the request.
case "$base" in
riscv64)
"$src/scripts/config" --file "$bdir/.config" -e RELOCATABLE -e RANDOMIZE_BASE ;;
esac
# mips (malta): enable text KASLR so the cells exercise the randomized layout
# rather than a fixed base. MIPS text KASLR self-seeds from random_get_entropy()
# (the CP0 count register qemu emulates) xored with the banner, so no
# device-tree seed is needed (like ppc32, unlike riscv64).
#
# EXCEPT MIPS on 6.6: there the relocation is broken. Once the kernel actually
# moves, a kernel paging request in a syscall's user-copy path faults on a bad
# address and Oopses — deterministically on 32-bit (init dies in mount before
# kasld runs), flakily on 64-bit (an Oops in poll/copy on some boots). The
# trigger is the relocation act (a nokaslr boot of the same kernel is fine), in
# the MIPS exception path; fixed by 7.0 and absent in 5.15 — an upstream
# regression. All three pinned versions were booted on all widths, so this is an
# exact exclusion of the version that breaks, not a guess at a range. A static
# mips cell is still validated soundly (kasld cannot read RANDOMIZE_BASE
# unprivileged, so it reports the conservative KASLR-possible window, which
# contains the fixed base).
case "$base" in
mips | mipsel | mips64 | mips64el)
case "$LINUX_VERSION" in
6.6*)
echo " [*] mips KASLR left off: MIPS $LINUX_VERSION relocation is broken (fixed by 7.0)" ;;
*)
"$src/scripts/config" --file "$bdir/.config" -e RELOCATABLE -e RANDOMIZE_BASE ;;
esac ;;
esac
# devtmpfs (console at init-exec) + initramfs support. BLK_DEV_INITRD/RD_GZIP
# are absent from some older defconfigs (mips/ppc32 malta at 5.15), so the
# kernel cannot unpack the gzipped initramfs and panics "unable to mount root
# fs". Forced on so every version boots the same -initrd; harmless where on.
"$src/scripts/config" --file "$bdir/.config" \
-e DEVTMPFS -e DEVTMPFS_MOUNT -e BLK_DEV_INITRD -e RD_GZIP
# Unprivileged BPF off by default, matching the modern upstream/distro posture
# (every distro cell here ships CONFIG_BPF_UNPRIV_DEFAULT_OFF). A kernel built
# straight from an older defconfig otherwise boots unprivileged bpf() permitted,
# which is not a realistic default: the `default` profile reflects what an
# unprivileged attacker faces (bpf() blocked), and `bpf-open` is the profile
# that enables it. No dependency where BPF_SYSCALL is off -- then it is inert.
"$src/scripts/config" --file "$bdir/.config" -e BPF_UNPRIV_DEFAULT_OFF
# At least one real in-tree driver as a MODULE, so the cell has a populated
# module region. dummy (drivers/net/dummy.c) is a genuine driver rather than a
# stub, exists on every architecture, and pulls in no dependencies. veth joins
# it so /proc/modules holds a RANGE rather than a single address -- the module
# band's two edges are then both observed.
# BOTH must be modular, not either: a defconfig that ships one of them =m
# would otherwise satisfy the check and stage a single module, leaving
# /proc/modules holding one address where the point is to hold two. The arm64
# defconfig is exactly that case (VETH=m, DUMMY not).
#
# Requesting a symbol that is already =m is a no-op, so this costs nothing
# where the defconfig already provides both; where it does not, .config
# changes and with it vermagic, which obliges rebuilding and re-staging the
# kernel or the module is rejected at load.
if ! grep -qE '^CONFIG_DUMMY=m$' "$bdir/.config" ||
! grep -qE '^CONFIG_VETH=m$' "$bdir/.config"; then
"$src/scripts/config" --file "$bdir/.config" -e MODULES -m DUMMY -m VETH
fi
# Cell VARIANTS, keyed on the full cell name rather than $base: the linear-map
# base is a build-time choice with no runtime override, so a moved-VMSPLIT
# kernel cannot be a boot mode of an existing cell -- it is a separate kernel
# and therefore a separate cell. x86 and arm spell the symbols the same way.
case "$arch" in
*-vmsplit2g)
"$src/scripts/config" --file "$bdir/.config" \
-d VMSPLIT_3G -d VMSPLIT_3G_OPT -d VMSPLIT_1G -e VMSPLIT_2G ;;
# arm64 derives PAGE_OFFSET from the VA width: -(1 << VA_BITS). The width is
# a Kconfig choice, not a boot parameter, so a second width is a second cell
# (unlike riscv64, where no4lvl/no5lvl pick the paging mode at boot). 39 bits
# is what the great majority of shipped arm64 devices run, and the narrowest
# kernel address space the architecture offers. The whole choice is cleared
# before selecting, because scripts/config sets symbols one at a time and
# leaving the previous member set makes the outcome depend on which of them
# olddefconfig resolves first. 4K pages is the dependency of the 39-bit
# member.
*-va36 | *-va39 | *-va42 | *-va47)
# The VA width fixes the page size: 39 needs 4K, 36 and 47 need 16K, 42
# needs 64K (arch/arm64/Kconfig). 36-bit is additionally EXPERT-gated. Clear
# the whole choice before selecting, because scripts/config sets symbols one
# at a time and leaving a previous member set makes the outcome depend on
# which one olddefconfig resolves first.
_w=${arch##*-va}
case "$_w" in
39) _pg=ARM64_4K_PAGES ;;
36 | 47) _pg=ARM64_16K_PAGES ;;
42) _pg=ARM64_64K_PAGES ;;
esac
"$src/scripts/config" --file "$bdir/.config" \
-d ARM64_4K_PAGES -d ARM64_16K_PAGES -d ARM64_64K_PAGES -e "$_pg" \
-d ARM64_VA_BITS_36 -d ARM64_VA_BITS_39 -d ARM64_VA_BITS_42 \
-d ARM64_VA_BITS_47 -d ARM64_VA_BITS_48 -d ARM64_VA_BITS_52
[ "$_w" != 36 ] || "$src/scripts/config" --file "$bdir/.config" -e EXPERT
"$src/scripts/config" --file "$bdir/.config" -e "ARM64_VA_BITS_$_w" ;;
esac
make -C "$src" O="$bdir" ARCH="$ka" CROSS_COMPILE="$cc" olddefconfig >/dev/null
# olddefconfig DROPS a request whose symbol the tree does not have (see the
# riscv KASLR note above), which for a split variant would silently produce a
# default-split kernel wearing the variant's name -- a cell that passes while
# testing nothing. Assert the split actually moved.
case "$arch" in
*-vmsplit2g)
if ! grep -q '^CONFIG_PAGE_OFFSET=0x80000000$' "$bdir/.config"; then
echo " !! $arch: VMSPLIT_2G did not take (CONFIG_PAGE_OFFSET is" \
"$(sed -n 's/^CONFIG_PAGE_OFFSET=//p' "$bdir/.config"))"
return 1
fi
echo " [*] split moved: CONFIG_PAGE_OFFSET=0x80000000" ;;
*-va36 | *-va39 | *-va42 | *-va47)
_w=${arch##*-va}
if ! grep -q "^CONFIG_ARM64_VA_BITS=$_w\$" "$bdir/.config"; then
echo " !! $arch: ARM64_VA_BITS_$_w did not take (CONFIG_ARM64_VA_BITS is" \
"$(sed -n 's/^CONFIG_ARM64_VA_BITS=//p' "$bdir/.config"))"
return 1
fi
echo " [*] VA width moved: CONFIG_ARM64_VA_BITS=$_w" \
"(PAGE_OFFSET = -(1 << $_w))" ;;
esac
make -C "$src" O="$bdir" ARCH="$ka" CROSS_COMPILE="$cc" -j"$JOBS" "$image" ||
{ echo " !! build failed"; return 1; }
# Stage the modules flat next to the kernel. Flat because init loads them with
# finit_module() by path -- there is no depmod, no module tree and no shell in
# the initramfs.
stage_modules "$arch" "$ka" "$cc" "$bdir" || return 1
case "$image" in
vmlinux) img="$bdir/vmlinux" ;;
*) img="$bdir/arch/$ka/boot/$image" ;;
esac
[ -f "$img" ] || { echo " !! no image at $img"; return 1; }
cp "$img" "$CACHE/$arch-vmlinuz"
# Stage the symbol map beside the image, for the same reason the release is
# recorded beside it: it says what this kernel IS, and nothing else does.
#
# Not every kernel publishes _text through kallsyms -- arm and mips export only
# _stext, a head gap above the address the analysis solves for. That gap is a
# per-build fact (an alignment on arm, a config-gated fill on mips), so the
# running kernel cannot be asked for it and a constant cannot stand in. Without
# this file a boot on those arches has no ground truth, and a verdict with no
# truth is not a verdict.
#
# Text, ~1.5 MB, in the same "addr type name" form as /proc/kallsyms. It
# survives prune_tree() by being staged out rather than left in the tree;
# vmlinux carries the same two addresses at ten times the size.
if [ -s "$bdir/System.map" ]; then
cp "$bdir/System.map" "$CACHE/$arch-System.map"
else
echo " !! no System.map in the tree; this cell will have no ground truth" >&2
fi
# Record which kernel this image IS, beside it.
#
# The version in a cell name is a label; the source version comes from the
# caller and nothing used to survive the build to say which was used. A
# kbuild tree reconfigured later against different sources then reports one
# release while the staged image still holds another, and a module built from
# that tree carries the wrong vermagic, which finit_module() rejects: init
# logs it FAILED and continues, so the module silently fails to load rather
# than giving a readable error. Several cells reached exactly
# that state. Writing the release here makes the mismatch checkable instead of
# latent; stage-modules refuses when the two disagree.
# Module.symvers is what modpost needs to resolve kernel symbols. Without it a
# tree can build no further modules and only a full kernel rebuild brings it
# back, so it is staged for the same reason as the map: it is cheap to keep and
# expensive to recreate. modules.builtin says which drivers were built in
# rather than modular, which is what decides whether a module-backed source
# exists on this cell at all.
for _keep in Module.symvers modules.builtin; do
[ -s "$bdir/$_keep" ] && cp "$bdir/$_keep" "$CACHE/$arch-$_keep"
done
if [ -f "$bdir/include/config/kernel.release" ]; then
cp "$bdir/include/config/kernel.release" "$CACHE/$arch-kernel.release"
fi
echo " [ok] staged $CACHE/$arch-vmlinuz (boot it: tests/vm/run $arch)"
if [ "${PRUNE_TREE:-0}" = 1 ]; then
prune_tree "$arch" "$bdir"
fi
}
# Sourcing with BUILD_KERNEL_LIB=1 exposes spec_for()/stage_modules() and the
# cache layout without building anything, so a caller can stage modules for an
# already-built cell without keeping a second copy of the arch mapping.
# shellcheck disable=SC2317 # reached only when this file is sourced
if [ -n "${BUILD_KERNEL_LIB:-}" ]; then
return 0 2>/dev/null || exit 0
fi
fetch_src || exit 1
if [ "$#" -eq 0 ]; then
# shellcheck disable=SC2086 # intentional word-split of the default set
set -- $ALL_ARCHES
fi
rc=0
for a in "$@"; do build_one "$a" || rc=1; done
exit $rc