Ashok G

Linux, Observed · Linux 7.3 · Draft

5 — The kernel, from allnoconfig

Requirements: chapter 0’s packages, Linux 7.3 at /lo/build/linux-7.3, and the initramfs from chapter 4. Use your host compiler here. The kernel doesn’t link against libc, so it doesn’t need the musl toolchain.

Step 1: start from nothing

cd /lo/build/linux-7.3
make ARCH=x86_64 allnoconfig
grep -E '^CONFIG_(64BIT|X86_64)=' .config

Always pass ARCH=x86_64. Without it, allnoconfig answers no to the 64-bit question, and you get a 32-bit base (arch/x86/Kconfig).

Expected: both options are =y.

Step 2: write the fragment

Create /lo/out/lo.fragment. Add a one-line reason to your manifest for every group:

cat > /lo/out/lo.fragment <<'E'
CONFIG_64BIT=y
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_SCRIPT=y
CONFIG_TTY=y
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_PROC_FS=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
CONFIG_DEVTMPFS=y
CONFIG_PCI=y
CONFIG_VIRTIO_MENU=y
CONFIG_VIRTIO_PCI=y
CONFIG_BLK_DEV=y
CONFIG_VIRTIO_BLK=y
CONFIG_EXT4_FS=y
CONFIG_EROFS_FS=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_VERITY=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_NET=y
CONFIG_NET_CORE=y
CONFIG_NETDEVICES=y
CONFIG_INET=y
CONFIG_UNIX=y
CONFIG_PACKET=y
CONFIG_VIRTIO_NET=y
CONFIG_VETH=y
CONFIG_CGROUPS=y
CONFIG_MEMCG=y
CONFIG_CGROUP_PIDS=y
CONFIG_NAMESPACES=y
CONFIG_SYSVIPC=y
CONFIG_USER_NS=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_FS=y
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_DEBUG_INFO_BTF=y
CONFIG_FTRACE=y
CONFIG_FUNCTION_TRACER=y
CONFIG_FUNCTION_GRAPH_TRACER=y
CONFIG_FTRACE_SYSCALLS=y
CONFIG_KPROBES=y
CONFIG_KPROBE_EVENTS=y
CONFIG_UPROBE_EVENTS=y
CONFIG_PERF_EVENTS=y
CONFIG_BPF_SYSCALL=y
CONFIG_BPF_JIT=y
CONFIG_CGROUP_BPF=y
E
KCONFIG_ALLCONFIG=/lo/out/lo.fragment make ARCH=x86_64 allnoconfig

Decision: build drivers in, with no modules. The kernel image is then the whole kernel, and the module-signing question never arises.

Step 3: check every option survived

This is the most important step in the chapter. Kconfig silently drops options whose dependencies aren’t met:

grep -E '^CONFIG_' /lo/out/lo.fragment | while read -r line; do
  grep -qx "$line" .config || echo "DROPPED: $line"
done

Expected: no DROPPED lines.

If some appear: run make ARCH=x86_64 menuconfig, press /, search for the symbol, and read its Depends on: line. Add what’s missing to the fragment.

Step 4: build it

make ARCH=x86_64 -j"$(nproc)" 2>&1 | tee /lo/out/kbuild.log
ls -l arch/x86/boot/bzImage vmlinux
cp arch/x86/boot/bzImage /lo/out/bzImage

Expected: both files exist. A very large vmlinux is normal, because of debug info. bzImage is what boots.

Step 5: boot and read the timeline

qemu-system-x86_64 -m 512M -nographic -no-reboot \
  -kernel /lo/out/bzImage -initrd /lo/out/initramfs.cpio.gz \
  -append "console=ttyS0 initcall_debug printk.time=1 log_buf_len=4M panic=-1"

initcall_debug logs every kernel init function and how long it took (kernel parameters). In the guest:

dmesg | grep 'initcall .* after' | tail
mount -t tracefs nodev /sys/kernel/tracing
wc -l /sys/kernel/tracing/available_events

Look for: lines of the form “initcall … returned … after … usecs”. Sort on the usecs field, and find your three slowest. The last number shows how many tracepoints this kernel exposes.

To draw it as a picture, save the dmesg output to a file, and on the host run:

perl scripts/bootgraph.pl < dmesg.txt > boot.svg

Step 6: measure the result

grep -c '=y' .config
ls -l vmlinux arch/x86/boot/bzImage

Now compare with the default config, in a separate copy of the tree:

make ARCH=x86_64 defconfig && grep -c '=y' .config

Write down both counts and sizes. These are your numbers.

If it fails

What you seeCauseFix
Boot prints nothingNo serial consoleCheck SERIAL_8250_CONSOLE=y and console=ttyS0
DEBUG_INFO_BTF dropped, or the link fails on BTFMissing or old paholeInstall it. The kernel docs list 1.26
“No working init found”No /init, or BLK_DEV_INITRD droppedStep 3, and chapter 4’s checks
initramfs ignoredRD_GZIP offConfirm CONFIG_RD_GZIP=y
32-bit buildMissing ARCH=x86_64Use it on every make
“Exec format error” for /initBINFMT_ELF offStep 3

If you’re really stuck, make ARCH=x86_64 kvm_guest.config adds a working QEMU set, though it brings in more than you need.

Principle: every default is a decision someone made for you

Each enabled option is a surface, a boot delay, or a behaviour someone else chose. Building up from nothing doesn’t make the kernel “better”; it makes every enabled thing yours, with a reason next to it. The same is true of every framework’s defaults in any system.