Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
286 lines (247 loc) · 13.4 KB
/
Copy pathMakefile
File metadata and controls
286 lines (247 loc) · 13.4 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
# kvmhost -- a stripped-down Linux kernel for KVM hypervisor hosts.
#
# Everything runs in a container: the kernel tree cannot live on a
# case-insensitive filesystem, and a reproducible fleet kernel should not
# depend on whatever toolchain the developer happens to have.
include configs/kernel.pin
IMAGE := kvmhost-build
SRC_VOLUME := kvmhost-src
OUT := $(CURDIR)/out
# Deliberately NOT the host's core count: the build runs inside a VM that
# usually has fewer CPUs and much less RAM than the host, and over-subscribing
# -j there gets cc1 OOM-killed. build.sh defaults to the container's nproc;
# set JOBS=N here only to override that.
JOBS ?=
PROFILE ?= hypervisor
# Target architecture: x86_64 (default) or arm64 (Graviton/Ampere/Grace).
KARCH ?= x86_64
# Destination-track artifacts get a -dst suffix so the two tracks coexist in
# out/ -- learned after a 7.2 build silently overwrote the v1 bzImage.
TRACK_TAG := $(if $(filter $(DESTINATION_VERSION),$(KERNEL_VERSION)),dst,)
KVMHOST_EXTRA ?=
# Which NIC families to build in. Empty = all of them (portable image).
KVMHOST_NICS ?=
DOCKER_RUN = docker run --rm \
-v $(SRC_VOLUME):/build \
-v $(CURDIR):/repo:ro \
-v $(OUT):/out \
-e KERNEL_VERSION=$(KERNEL_VERSION) \
-e SRC=/build/linux-$(KERNEL_VERSION) \
$(if $(JOBS),-e JOBS=$(JOBS)) \
-e KVMHOST_EXTRA="$(KVMHOST_EXTRA)" \
-e KVMHOST_NICS="$(KVMHOST_NICS)" \
-e KVMHOST_ACCEL="$(ACCEL)" \
-e KVMHOST_MITIGATIONS="$(MITIGATIONS)" \
-e KVMHOST_GPU="$(GPU)" \
-e KVMHOST_CPU="$(CPU)" \
-e KVMHOST_PLATFORM="$(PLATFORM)" \
-e KVMHOST_ARCH="$(KARCH)" \
-e KVMHOST_TRACK="$(TRACK_TAG)" \
-e PROFILE="$(PROFILE)" \
-e MSV="$(MSV)" \
-e LUO_FLOOR="$(LUO_FLOOR)" \
$(IMAGE)
.PHONY: help image check-msv fetch config build validate validate-all msv audit audit-list hardening pki artifact repro signed-kexec secureboot luo hw fc-real ch-real tpm viommu diag perf unaudited unused menuconfig config-diff initramfs smoke smoke-fc shell clean tree-clean distclean reclaim promote-dev promote-staging promote-canary promote-prod test secret-scan
help:
@echo "kvmhost -- fleet kernels. v1 track: linux-$(KERNEL_VERSION) (LTS);"
@echo "destination track: linux-$(DESTINATION_VERSION) (KHO+LUO live update)."
@echo
@echo "Profiles (PROFILE=<name>, default $(PROFILE)):"
@for p in profiles/*.profile; do \
n=$$(basename $$p .profile); \
d=$$(sed -n 's/^DESC="\(.*\)"/\1/p' $$p); \
printf " %-14s %s\n" "$$n" "$$d"; \
done
@echo
@echo " make image build the container toolchain"
@echo " make fetch download + unpack the kernel source"
@echo " make config resolve fragments -> .config, verify, stop"
@echo " make build config + compile bzImage into out/"
@echo " make validate config-only check against the pinned version"
@echo " make validate-all resolve + verify the shippable matrix on both tracks"
@echo " make msv recompute the minimum supported kernel version"
@echo " make unused fail if any fragment is unreachable"
@echo " make audit fail if any default-on feature is un-accounted"
@echo " make hardening third-party KSPP/CLIP/grsec score of PROFILE"
@echo " make menuconfig explore interactively on top of the resolved config"
@echo " make config-diff show what menuconfig changed, as fragment lines"
@echo " make smoke boot the built kernel under QEMU and assert on it"
@echo " make shell drop into the build container"
@echo
@echo " KERNEL_VERSION=$(DESTINATION_VERSION) make build build the destination track (>= MSV $(MSV))"
@echo " KVMHOST_EXTRA=opt-windows make build add optional fragments (opt-rt, opt-lowmem, ...)"
@echo " KVMHOST_NICS=mellanox make build build only your fleet's NICs"
@echo " ACCEL=intel-dsa make build add an accelerator (DSA/IAA, QAT)"
@echo " GPU=nvidia|amd make build add GPU support (see docs/PROVIDERS.md)"
@echo " CPU=intel|amd make build single-vendor fleet (default: both)"
@echo " PLATFORM=vm make build this kernel runs inside a VM, not on metal"
@echo " KARCH=arm64 make build Graviton/Ampere-class target"
image:
docker build -t $(IMAGE) -f docker/Dockerfile docker
$(OUT):
@mkdir -p $(OUT)
# Enforced here rather than only in build.sh so that an out-of-range version
# fails before downloading 150MB of source.
check-msv:
@kv="$(KERNEL_VERSION)"; msv="$(MSV)"; \
kvn=$$(printf '%d%03d' $${kv%%.*} $$(echo $$kv | cut -d. -f2)); \
msvn=$$(printf '%d%03d' $${msv%%.*} $$(echo $$msv | cut -d. -f2)); \
if [ "$$kvn" -lt "$$msvn" ]; then \
echo "kernel $$kv is below the minimum supported version $$msv" >&2; \
echo "(v1 feature floor -- iommufd/cdev, KVM TDX, PREEMPT_LAZY. docs/MSV.md)" >&2; \
echo "See docs/MSV.md; regenerate the floor with 'make msv'." >&2; \
exit 1; \
fi
fetch: check-msv
@docker volume create $(SRC_VOLUME) >/dev/null
docker run --rm -v $(SRC_VOLUME):/build -v $(CURDIR):/repo:ro \
-e KERNEL_VERSION=$(KERNEL_VERSION) $(IMAGE) /repo/scripts/fetch.sh
config: fetch | $(OUT)
$(DOCKER_RUN) sh -c 'CONFIG_ONLY=1 /repo/scripts/build.sh'
build: fetch pki | $(OUT)
$(DOCKER_RUN) /repo/scripts/build.sh
validate: config
msv:
./scripts/feature-floor.sh
# Fail if a fragment exists that no profile or knob can select.
unused:
./scripts/unused-fragments.sh
# Independent third-party hardening score (KSPP/CLIP/grsec) of the current
# PROFILE. Fetches the checker into a cache on first use.
hardening: config
./scripts/hardening-check.sh $(PROFILE)
# The scoping gate: every default-on feature must be requested, implied, or in
# configs/accept-defaults.config. Runs against the current PROFILE/KARCH.
audit: config
docker run --rm -v $(SRC_VOLUME):/build -v $(CURDIR):/repo:ro $(IMAGE) sh -c \
'python3 /repo/scripts/unaudited.py --strict \
--accept /repo/configs/accept-defaults.config \
/build/linux-$(KERNEL_VERSION) /build/linux-$(KERNEL_VERSION)/.config \
$$(cat /build/linux-$(KERNEL_VERSION)/.kvmhost-fragments)'
# Print default-on features not yet in the ledger (to extend it).
audit-list: config
docker run --rm -v $(SRC_VOLUME):/build -v $(CURDIR):/repo:ro $(IMAGE) sh -c \
'python3 /repo/scripts/unaudited.py --accept /repo/configs/accept-defaults.config \
/build/linux-$(KERNEL_VERSION) /build/linux-$(KERNEL_VERSION)/.config \
$$(cat /build/linux-$(KERNEL_VERSION)/.kvmhost-fragments)'"'"
# Classify every enabled symbol: requested by a fragment, implied by a select,
# or arrived from a Kconfig default with nobody looking.
unaudited: config
docker run --rm -v $(SRC_VOLUME):/build -v $(CURDIR):/repo:ro $(IMAGE) sh -c \
'python3 /repo/scripts/unaudited.py /build/linux-$(KERNEL_VERSION) \
/build/linux-$(KERNEL_VERSION)/.config $$(cat /build/linux-$(KERNEL_VERSION)/.kvmhost-fragments)'
# Interactive exploration only. menuconfig is not how this kernel is
# configured -- an interactive session is not reviewable, not reproducible in
# CI, and records no reason for any choice. Use it to find a symbol or check
# a dependency, then run `make config-diff` and fold the result into a
# fragment with a comment saying why.
menuconfig: config
docker run --rm -it -v $(SRC_VOLUME):/build -v $(CURDIR):/repo:ro \
-v $(OUT):/out -e KERNEL_VERSION=$(KERNEL_VERSION) $(IMAGE) \
sh -c 'cd /build/linux-$(KERNEL_VERSION) && make ARCH=x86_64 menuconfig'
@$(MAKE) --no-print-directory config-diff
config-diff:
docker run --rm -v $(SRC_VOLUME):/build -v $(CURDIR):/repo:ro -v $(OUT):/out \
-e SRC=/build/linux-$(KERNEL_VERSION) $(IMAGE) /repo/scripts/config-diff.sh
validate-all: | $(OUT)
VALIDATE_VERSIONS="$(KERNEL_VERSION) $(DESTINATION_VERSION)" ./scripts/validate-matrix.sh
# The initramfs embeds that arch's host kernel as the kexec-probe target --
# a real unsigned image is the only thing that reaches the KEXEC_SIG gate.
PROBE_KERNEL = $(if $(filter arm64,$(KARCH)),Image-hypervisor-arm64,bzImage-hypervisor)
initramfs: | $(OUT)
docker run --rm -v $(SRC_VOLUME):/build -v $(CURDIR):/repo:ro -v $(OUT):/out \
-e KVMHOST_ARCH=$(KARCH) -e PROBE_KERNEL=$(PROBE_KERNEL) \
$(IMAGE) /repo/scripts/mkinitramfs.sh
# Guest profiles assert a guest-shaped kernel (no KVM, no modules); fc-guest
# boots on QEMU's microvm machine, the faithful stand-in for Firecracker's
# virtio-mmio world. scripts/fc-smoke.sh runs the REAL VMM on a Linux+KVM box.
SMOKE_MACHINE = $(if $(filter fc-guest,$(PROFILE)),microvm,q35)
SMOKE_EXPECT = $(if $(filter ch-guest ch-guest-k8s fc-guest,$(PROFILE)),guest,host)
SMOKE_SUFFIX = $(PROFILE)$(if $(TRACK_TAG),-$(TRACK_TAG))$(if $(filter arm64,$(KARCH)),-arm64)
SMOKE_KERNEL = $(if $(filter arm64,$(KARCH)),$(OUT)/Image-$(SMOKE_SUFFIX),$(OUT)/bzImage-$(SMOKE_SUFFIX))
# kexec policy differs per SKU: hosts must be sig-gated (eperm), fc-guest has
# no kexec syscall at all (enosys), ch-guest keeps plain kdump -- root in a
# guest owns the guest kernel anyway (enoexec = parsed, no gate).
# hosts: unsigned image refused at the gate (eperm). ch-guest: no gate by
# design (root owns the guest kernel) -- the unsigned image loads. fc-guest:
# no syscall at all.
SMOKE_KEXEC = $(if $(filter fc-guest,$(PROFILE)),enosys,$(if $(filter ch-guest ch-guest-k8s,$(PROFILE)),loaded,eperm))
smoke: initramfs
KARCH=$(KARCH) MACHINE=$(SMOKE_MACHINE) EXPECT=$(SMOKE_EXPECT) \
KVER=$(KERNEL_VERSION) LUO_FLOOR=$(LUO_FLOOR) KEXEC_WANT=$(SMOKE_KEXEC) \
./scripts/qemu-smoke.sh $(SMOKE_KERNEL) $(OUT)/initramfs-$(KARCH).cpio.gz
smoke-fc:
./scripts/fc-smoke.sh $(OUT)/vmlinux-fc-guest $(OUT)/initramfs.cpio.gz
# Boot/update signature chain: dev PKI -> verity-sealed root -> signed UKI.
# Proves the mechanism end to end (sbverify + veritysetup verify); production
# swaps the dev CA for the fleet CA in an HSM.
pki:
docker run --rm -v $(CURDIR)/out:/out -v $(CURDIR):/repo:ro $(IMAGE) \
sh -c 'OUT=/out /repo/scripts/pki-init.sh'
artifact: pki initramfs
docker run --rm -v $(CURDIR)/out:/out -v $(CURDIR):/repo:ro $(IMAGE) \
sh -c 'OUT=/out REPO=/repo /repo/scripts/mk-rootfs.sh'
docker run --rm -v $(CURDIR)/out:/out -v $(CURDIR):/repo:ro $(IMAGE) \
sh -c 'OUT=/out /repo/scripts/mk-uki.sh $(PROFILE) /out/bzImage-$(PROFILE)'
# Prove the same source + fragments -> byte-identical bzImage.
repro:
./scripts/repro-check.sh $(PROFILE)
# Prove a SIGNED kexec target is accepted (the converse of the unsigned=EPERM
# that `make smoke` already asserts).
signed-kexec:
./scripts/signed-kexec-smoke.sh
# Prove UEFI Secure Boot ENFORCEMENT under OVMF: firmware launches our signed
# UKI and refuses a tampered one. All in-container (Debian OVMF + qemu TCG).
luo:
./scripts/luo-smoke.sh
# Exercise NVMe / VT-d IOMMU / NUMA / Intel-NIC driver paths against emulated
# hardware (needs KVMHOST_NICS=intel so igb has a driver).
# --- Emulation-based tests (your map): exercise real driver/RAS/security
# paths against device models and in-kernel frameworks, no silicon needed.
fc-real: # real Firecracker on nested KVM (arm64 guest)
./scripts/fc-real.sh
ch-real: # real Cloud Hypervisor on nested KVM (arm64 guest)
./scripts/ch-real.sh
hw: initramfs # NVMe / VT-d / NUMA / Intel NIC driver paths
./scripts/hw-smoke.sh $(OUT)/bzImage-hypervisor $(OUT)/initramfs-x86_64.cpio.gz
tpm: # swtpm-backed TPM 2.0 measured-boot plumbing
./scripts/tpm-smoke.sh
viommu: # virtio-iommu (paravirt IOMMU) translating for a guest
./scripts/emu-smoke.sh bzImage-ch-guest "kvmhost.viommu=1 kvmhost.expect=guest" -device virtio-iommu-pci -device virtio-net-pci,netdev=n0 -netdev user,id=n0
diag: # netdevsim SR-IOV VFs + MCE/fault injection interfaces
./scripts/emu-smoke.sh bzImage-hypervisor kvmhost.diag=1
perf: # deterministic (instruction-proportional) boot-cost metric
./scripts/perf-icount.sh
secureboot:
docker run --rm -v $(CURDIR)/out:/out -v $(CURDIR):/repo:ro $(IMAGE) \
sh -c 'OUT=/out /repo/scripts/secureboot-smoke.sh /out/bzImage-hypervisor'
shell:
docker run --rm -it -v $(SRC_VOLUME):/build -v $(CURDIR):/repo:ro \
-v $(OUT):/out -e KERNEL_VERSION=$(KERNEL_VERSION) $(IMAGE) bash
clean:
rm -rf $(OUT)
# Scrub the shared volume's kernel object tree (keeps the source). Needed
# after an interrupted build: a SIGKILL mid-write leaves half-written *.cmd
# files that later detonate as "unterminated variable reference" in an
# unrelated subsystem. Cheaper than distclean (which re-downloads source).
tree-clean:
docker run --rm -v $(SRC_VOLUME):/build $(IMAGE) sh -c \
'cd /build/linux-$(KERNEL_VERSION) && make -s ARCH=$(KARCH) clean'
distclean: clean
-docker volume rm $(SRC_VOLUME)
reclaim: # between-tenant sanitize: crypto-erase + RAM scrub + attest (QEMU nvme+swtpm)
$(MAKE) build PROFILE=reclaim
./scripts/reclaim-smoke.sh
PROMOTE_ARTIFACT ?= bzImage-hypervisor
promote-dev: # stamp the dev manifest (after matrix + smokes pass)
./scripts/promote.sh dev $(PROMOTE_ARTIFACT)
promote-staging: # requires the identical artifact to have cleared dev
./scripts/promote.sh staging $(PROMOTE_ARTIFACT)
promote-canary: # requires staging
./scripts/promote.sh canary $(PROMOTE_ARTIFACT)
promote-prod: # requires canary -- no skipping, no swapped binary
./scripts/promote.sh prod $(PROMOTE_ARTIFACT)
test: # fast unit tests (promotion gate). Integration = smoke / validate-all
./scripts/test-promote.sh
secret-scan: # fail if a key/token/credential is tracked (run before sharing)
./scripts/secret-scan.sh