-
-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathMakefile
459 lines (388 loc) · 16.2 KB
/
Makefile
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
ifneq (,$(wildcard ./.env))
include .env
endif
ROOT_DIR := $(CURDIR)
OUT_DIR := $(ROOT_DIR)/out
SCRIPT_DIR := $(ROOT_DIR)/scripts
CONFIG_DIR := $(ROOT_DIR)/config
CLANG_DIR ?= $(ROOT_DIR)/toolchain/clang
CLANG_URL := https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-18.1.3-x86_64.tar.xz
SHARED_DIR := $(ROOT_DIR)/shared
VERBOSE ?= 0
ARCH ?= x86_64
GREEN := $(shell tput setaf 2)
YELLOW := $(shell tput setaf 3)
NC := $(shell tput sgr0)
ifeq ($(ARCH),aarch64)
$(warning $(YELLOW)Incorrect architecture 'aarch64', using 'arm64' instead $(NC))
ARCH := arm64
else ifeq ($(ARCH),x86)
$(warning $(YELLOW)Incorrect architecture 'x86', using 'x86_64' instead $(NC))
ARCH := x86_64
else ifeq ($(filter x86_64 arm64 i386,$(ARCH)),)
$(error Invalid architecture $(ARCH))
endif
.PHONY: default
default: linux linux_modules tools-vm
.PHONY: clean
clean: linux_clean tools-vm_clean
.PHONY: help
help:
@echo '$(GREEN)General Environment Variables:$(NC)'
@echo ' ARCH - Specify one of the supported architectures: x86_64, i386, arm64 (default: x86_64)'
@echo ' ACK - Set to 1 to build ACK instead of the Linux kernel. Does not need to be set for `ack` and `run-ack` targets (default: 0)'
@echo ' VERBOSE - Set to 1 to enable verbose output (default: 0)'
@echo ''
@echo '$(GREEN)Build/Config:$(NC)'
@echo ' Targets:'
@echo ' linux (default) - Build the Linux kernel'
@echo ' linux_defconfig - Run `make defconfig`'
@echo ' linux_menuconfig - Run `make menuconfig`'
@echo ' linux_modules - Build the Linux kernel modules'
@echo ' linux_debpkg - Creates a Debian package for the kernel'
@echo ' ack - Build the Android Common Kernel'
@echo ' tools-vm - Build linux/tools/vm'
@echo ''
@echo ' Environment Variables:'
@echo ' LINUX_DEFCONFIG - The defconfig to use when building the kernel (default: defconfig, ACK default: gki_defconfig)'
@echo ' LINUX_SRC - The path to the kernel source directory (default: linux, ACK default: ack/common)'
@echo ' LINUX_OUT - The path where the kernel build output should be stored (default: out/linux/$$ARCH, ACK default: out/ack/common/$$ARCH)'
@echo ' LINUX_CONFIG_FRAGMENT - A kernel config fragment to merge with the defconfig (default: config/config.fragment)'
@echo ''
@echo '$(GREEN)Clean:$(NC)'
@echo ' Targets:'
@echo ' clean - Clean output from default build targets'
@echo ' <target>_clean - Clean output for <target>, where <target> is one of: ack, linux, tools-vm, rootfs'
@echo ''
@echo '$(GREEN)Run/Debug:$(NC)'
@echo ' Targets:'
@echo ' run - Run QEMU with the built kernel and rootfs image'
@echo ' run-ack - Same as `run` but runs ACK instead'
@echo ''
@echo ' Environment Variables:'
@echo ' GDB - Set to 1 to start a gdbserver and wait for GDB when running QEMU (default: 0)'
@echo ' CPU - The number of CPUs to use when running QEMU (default: 4)'
@echo ' MEM - The memory size in MB to use when running QEMU (default: 1024)'
@echo ' QEMU_EXTRA_ARGS - Additional arguments to pass to QEMU (default: "")'
@echo ' QEMU_EXTRA_KERNEL_CMDLINE - Additional arguments to pass to the kernel (default: "")'
@echo ' QEMU_KERNEL_IMAGE - The path to the kernel image to run (x86_64/i386 default: $$LINUX_OUT/arch/$$ARCH/boot/bzImage, arm64 default: $$LINUX_OUT/arch/$$ARCH/boot/Image)'
@echo ' ROOTFS - The path to the rootfs image file (default: rootfs/alpine-$$ARCH.img)'
@echo ' ROOTFS_FORMAT - The format of the rootfs image file: raw, qcow2 (default: qcow2)'
@echo ' INITRD - Set to 1 to use the $$CPIO_FILE initramfs instead of the $$ROOTFS image as the rootfs, or specify the path to an alternative CPIO file (default: "")'
@echo ' RDINIT - The value of the `rdinit` kernel command line parameter (default: "", default if INITRD is set: /sbin/init)'
@echo ' ECHR - The value of the QEMU `-echr` flag (default: 1)'
@echo ' ROOT - The value of the `root` kernel command line parameter (default: /dev/vda)'
@echo ' RW - Whether to mount the rootfs as read-write or read-only: ro, rw (default: rw)'
@echo ' KASLR - Set to 1 to enable KASLR (default: 0)'
@echo ''
@echo '$(GREEN)rootfs:$(NC)'
@echo ' Targets:'
@echo ' rootfs-init - Extract the Alpine Linux rootfs to $$ROOTFS_DIR and then run the `rootfs-overlay` target'
@echo ' rootfs-overlay - Apply arch-specific changes to $$ROOTFS_DIR and run the `rootfs` target'
@echo ' rootfs - Run the `ext4` and `cpio` targets'
@echo ' ext4 - Build a $$ROOTFS $$ROOTFS_FORMAT (default qcow2) image with an ext4 filesystem from $$ROOTFS_DIR'
@echo ' cpio - Build a $$CPIO_FILE gzipped initramfs CPIO file from $$ROOTFS_DIR'
@echo ' initramfs - An alias for the `cpio` target'
@echo ' uncpio - Extract $$CPIO_FILE to $$ROOTFS_DIR'
@echo ' rootfs-mount - Mount $$ROOTFS image at /tmp/rootfs'
@echo ' rootfs-unmount - Unmount rootfs image from /tmp/rootfs'
@echo ' chroot - chroot into $$ROOTFS_DIR'
@echo ''
@echo ' Environment Variables:'
@echo ' EXT4_SIZE - The disk size of the rootfs image to build'
@echo ' ROOTFS_DIR - The directory to create the ext4 rootfs image and initramfs CPIO from (default: rootfs/alpine-$$ARCH)'
@echo ' ROOTFS - The path to the rootfs image file (default: rootfs/alpine-$$ARCH.img)'
@echo ' ROOTFS_FORMAT - The format of the rootfs image file: raw, qcow2 (default: qcow2)'
@echo ' CPIO_FILE - The path to the CPIO file to create (default: rootfs/alpine-$$ARCH.cpio.gz)'
@echo ''
@echo '$(GREEN)Miscellaneous:$(NC)'
@echo ' Targets:'
@echo ' linux_download - Downloads an archive of the Linux kernel source for the version specified in $$VERSION'
@echo ' linux_checkout - Checks out the version specified by $$VERSION of the linux kernel in $$LINUX_SRC'
@echo ''
@echo ' Environment Variables:'
@echo " VERSION - The version to download or checkout. For checkout only, if the third number in the version string is a 'y', the latest version of the kernel with that major and minor version is used. Examples: 5.10, 5.10.107, v5.10, 5.10.y, linux-5.10.y"
$(SHARED_DIR):
mkdir -p $(SHARED_DIR)
##
## Linux Kernel and Android Common Kernel
##
ACK_TARGETS := ack ack_clean run-ack
ACK ?= 0
ACK_SRC := $(ROOT_DIR)/ack/common
ACK_OUT := $(OUT_DIR)/ack/common/$(ARCH)
ifneq ($(filter $(ACK_TARGETS),$(MAKECMDGOALS)),)
ACK := 1
endif
# If we're building ACK instead of Linux, set the appropriate variables
ifeq ($(ACK),1)
LINUX_SRC ?= $(ACK_SRC)
LINUX_OUT ?= $(ACK_OUT)
LINUX_DEFCONFIG ?= gki_defconfig
else
LINUX_SRC ?= $(ROOT_DIR)/linux
LINUX_OUT ?= $(OUT_DIR)/linux/$(ARCH)
LINUX_DEFCONFIG ?= defconfig
endif
LINUX_CONFIG_FRAGMENT ?= $(CONFIG_DIR)/config.fragment
LINUX_OUT_MODULES_DEP := $(LINUX_OUT)/modules_install.stamp
LINUX_MODULES_INSTALL_PATH := $(LINUX_OUT)/modules_install
LINUX_CONFIG := $(LINUX_OUT)/.config
ifeq ($(ARCH),x86_64)
TARGET := x86_64-pc-linux-gnu
KERNEL_IMAGE := $(LINUX_OUT)/arch/$(ARCH)/boot/bzImage
else ifeq ($(ARCH),i386)
TARGET := i386-pc-linux-gnu
KERNEL_IMAGE := $(LINUX_OUT)/arch/$(ARCH)/boot/bzImage
else ifeq ($(ARCH),arm64)
TARGET := aarch64-linux-gnu
KERNEL_IMAGE := $(LINUX_OUT)/arch/$(ARCH)/boot/Image
endif
LINUX_MAKE := \
PATH=$(CLANG_DIR)/bin:$(PATH) \
$(MAKE) \
-C $(LINUX_SRC) \
ARCH=$(ARCH) \
CROSS_COMPILE=$(TARGET)- \
LLVM=1 LLVM_IAS=1 \
V=$(VERBOSE) \
O=$(LINUX_OUT) \
-j `nproc`
$(CLANG_DIR):
$(warning $(YELLOW)Clang directory $(CLANG_DIR) does not exist, downloading prebuilt binaries $(NC))
wget --no-verbose --show-progress --progress=bar:force $(CLANG_URL) -O clang.tar.xz
mkdir -p $(CLANG_DIR)
tar -xf clang.tar.xz -C $(CLANG_DIR) --strip-components=1
.PHONY: linux_defconfig
linux_defconfig $(LINUX_CONFIG): $(LINUX_CONFIG_FRAGMENT) | $(CLANG_DIR)
+ $(LINUX_MAKE) $(LINUX_DEFCONFIG)
KCONFIG_CONFIG=$(LINUX_CONFIG) \
$(LINUX_SRC)/scripts/kconfig/merge_config.sh \
-m \
$(LINUX_CONFIG) \
$(LINUX_CONFIG_FRAGMENT)
+ $(LINUX_MAKE) olddefconfig
$(SCRIPT_DIR)/check_merged_config.sh $(LINUX_CONFIG) $(LINUX_CONFIG_FRAGMENT)
.PHONY: linux_menuconfig
linux_menuconfig:
+ $(LINUX_MAKE) menuconfig
$(SCRIPT_DIR)/check_merged_config.sh $(LINUX_CONFIG) $(LINUX_CONFIG_FRAGMENT)
.PHONY: linux
linux $(KERNEL_IMAGE): $(LINUX_CONFIG) | $(CLANG_DIR)
+ $(LINUX_MAKE)
# Older versions of Linux don't have this script
ifneq (,$(wildcard $(LINUX_SRC)/scripts/clang-tools/gen_compile_commands.py))
cd $(LINUX_SRC) && ./scripts/clang-tools/gen_compile_commands.py -d $(LINUX_OUT)
endif
.PHONY: linux_modules
linux_modules $(LINUX_OUT_MODULES_DEP): $(KERNEL_IMAGE)
+ $(LINUX_MAKE) modules
+ $(LINUX_MAKE) INSTALL_MOD_PATH=$(LINUX_MODULES_INSTALL_PATH) modules_install
@find $(LINUX_MODULES_INSTALL_PATH) -type f | sort | \
xargs sha1sum > $(LINUX_OUT_MODULES_DEP).tmp
@cmp $(LINUX_OUT_MODULES_DEP).tmp $(LINUX_OUT_MODULES_DEP) || \
mv $(LINUX_OUT_MODULES_DEP).tmp $(LINUX_OUT_MODULES_DEP)
.PHONY: linux_bindebpkg
linux_bindebpkg: $(KERNEL_IMAGE)
+ $(LINUX_MAKE) bindeb-pkg
.PHONY: linux_clean
linux_clean:
+ $(LINUX_MAKE) mrproper
.PHONY: linux_download
linux_download:
ifndef VERSION
$(error VERSION environment variable is not defined)
endif
$(SCRIPT_DIR)/download_linux.sh $(VERSION) $(ACK)
.PHONY: linux_checkout
linux_checkout:
ifndef VERSION
$(error VERSION environment variable is not defined)
endif
$(SCRIPT_DIR)/checkout_linux.sh $(VERSION) $(LINUX_SRC)
# Use xz compression instead of zstd, as the latter is not supported by some
# versions of dpkg.
.PHONY: linux_debpkg
linux_debpkg: $(KERNEL_IMAGE)
+ $(LINUX_MAKE) KDEB_COMPRESS=xz bindeb-pkg
# These targets do the same thing as the `linux` targets, but because they're in
# `ACK_TARGETS`, the `ACK` variable is set to 1 and the ACK source and output
# directories are used instead of the Linux directories. The same thing can be
# achieved by setting the `ACK=1` environment variable, so these are just
# provided for convenience.
.PHONY: ack
ack: linux
.PHONY: ack_clean
ack_clean: linux_clean
# Because of how `tools/vm/Makefile` is designed, we can't use `$(LINUX_MAKE)`
# here, as specifying the `O` variable causes compilation errors, and `clang`
# won't properly cross-compile the binary so we need to use GCC
TOOLS_MAKE := $(MAKE) \
-C $(LINUX_SRC)/tools \
DESTDIR=$(SHARED_DIR) \
sbindir=/tools/$(ARCH)
# Assuming we're building on an x86 system, we should only set `CROSS_COMPILE`
# if we're building arm64.
ifeq ($(ARCH),arm64)
TOOLS_MAKE += CROSS_COMPILE=$(TARGET)-
endif
.PHONY: tools-vm
tools-vm: | $(SHARED_DIR)
+ $(TOOLS_MAKE) vm_install
.PHONY: tools-vm_clean
tools-vm_clean:
+ $(TOOLS_MAKE) vm_clean
rm -f $(SHARED_DIR)/tools/$(ARCH)/{page_owner_sort,page-types,slabinfo}
##
## Generate rootfs images
##
ROOTFS_PARENT_DIR := $(ROOT_DIR)/rootfs
ROOTFS ?= $(ROOTFS_PARENT_DIR)/$$(basename $(ROOTFS_DIR)).img
ROOTFS_FORMAT ?= qcow2
ROOTFS_DIR ?= $(ROOTFS_PARENT_DIR)/alpine-$(ARCH)
CPIO_FILE ?= $(ROOTFS_PARENT_DIR)/$$(basename $(ROOTFS_DIR)).cpio.gz
EXT4_SIZE ?= 1G
ifeq ($(SUDO),1)
SUDO := sudo
endif
# The user might set a relative path for `CPIO_FILE`, so we need to get the
# absolute path, as we need to reference it after we've changed directories
CPIO_FILE := $(shell realpath $(CPIO_FILE))
$(ROOTFS_DIR):
mkdir -p $(ROOTFS_DIR)
.PHONY: rootfs-init
rootfs-init: | $(ROOTFS_DIR)
tar -xf $(ROOTFS_PARENT_DIR)/alpine-minirootfs-*-$(ARCH).tar.gz -C $(ROOTFS_DIR)
$(MAKE) rootfs-overlay
.PHONY: rootfs-overlay
rootfs-overlay: | $(ROOTFS_DIR)
$(SUDO) cp -f -r $(CONFIG_DIR)/rootfs-overlay/. $(ROOTFS_DIR)
$(SUDO) $(SCRIPT_DIR)/arch_specific.sh $(ARCH) $(ROOTFS_DIR)
$(MAKE) rootfs
.PHONY: ext4
ext4: | $(ROOTFS_DIR)
dd if=/dev/zero of=$(ROOTFS) bs=1 count=0 seek=$(EXT4_SIZE)
$(SUDO) mkfs.ext4 -b 4096 -d $(ROOTFS_DIR) -F $(ROOTFS)
qemu-img convert -O $(ROOTFS_FORMAT) $(ROOTFS) $(ROOTFS).tmp
mv $(ROOTFS).tmp $(ROOTFS)
.PHONY: cpio initramfs
initramfs cpio: | $(ROOTFS_DIR)
(cd $(ROOTFS_DIR) && $(SUDO) find . -print0 \
| $(SUDO) cpio --null --create --verbose --format=newc) \
| gzip --best > $(CPIO_FILE)
.PHONY: uncpio
uncpio: | $(ROOTFS_DIR)
cd $(ROOTFS_DIR) && zcat $(CPIO_FILE) | $(SUDO) cpio --extract --make-directories --format=newc --no-absolute-filenames
.PHONY: rootfs
rootfs: ext4 cpio
.PHONY: rootfs-mount
rootfs-mount:
sudo modprobe nbd
sudo qemu-nbd -c /dev/nbd0 -f $(ROOTFS_FORMAT) $(ROOTFS)
mkdir -p /tmp/rootfs
sudo mount /dev/nbd0 /tmp/rootfs
.PHONY: rootfs-unmount
rootfs-unmount:
sudo umount /dev/nbd0
sudo qemu-nbd -d /dev/nbd0
.PHONY: chroot
chroot:
sudo chroot $(ROOTFS_DIR) /bin/sh
.PHONY: rootfs_clean
rootfs_clean:
$(SUDO) rm -rf $(ROOTFS_DIR)
$(SUDO) rm -f $(CPIO_FILE)
$(SUDO) rm -f $(ROOTFS)
##
## Run QEMU
##
CPU ?= 4
MEM ?= 1024
QEMU_EXTRA_ARGS ?=
QEMU_EXTRA_KERNEL_CMDLINE ?=
GDB ?= 0
INITRD ?=
RDINIT ?= /sbin/init
QEMU_KERNEL_IMAGE ?= $(KERNEL_IMAGE)
ECHR ?= 1
ROOT ?= /dev/vda
RW ?= rw
KASLR ?= 0
QEMU_KERNEL_CMDLINE := selinux=0
QEMU_ARGS := \
-m $(MEM) \
-smp $(CPU) \
-nographic \
-no-reboot \
-kernel $(QEMU_KERNEL_IMAGE) \
-netdev user,id=eth0,hostfwd=tcp::7777-:7777,hostfwd=tcp::2222-:22,hostfwd=tcp::2223-:23 -device virtio-net-pci,netdev=eth0 \
-virtfs local,security_model=mapped-xattr,path=$(SHARED_DIR),mount_tag=shared \
-virtfs local,security_model=mapped-xattr,path=$(LINUX_MODULES_INSTALL_PATH)/lib/modules,mount_tag=modules \
-echr $(ECHR) \
$(QEMU_EXTRA_ARGS)
ifneq ($(INITRD),)
ifeq ($(INITRD),1)
INITRD := $(CPIO_FILE)
endif
QEMU_KERNEL_CMDLINE += rdinit=$(RDINIT)
QEMU_ARGS += -initrd $(INITRD)
else
QEMU_ARGS += -drive file=$(ROOTFS),if=virtio,format=$(ROOTFS_FORMAT)
QEMU_KERNEL_CMDLINE += root=$(ROOT) $(RW)
endif
ifeq ($(GDB),1)
QEMU_ARGS += -s -S
endif
ifeq ($(ARCH),x86_64)
QEMU_BIN := qemu-system-x86_64
# 8250.nr_uarts=1 is needed because some Android kernels set
# `CONFIG_SERIAL_8250_RUNTIME_UARTS` to zero
QEMU_KERNEL_CMDLINE += console=ttyS0 kpti no5lvl 8250.nr_uarts=1
QEMU_ARGS += -cpu kvm64,+smep,+smap
# We can't use KVM with GitHub Actions CI
ifneq ($(CI),true)
QEMU_ARGS += -accel kvm
endif
else ifeq ($(ARCH),i386)
QEMU_BIN := qemu-system-i386
QEMU_KERNEL_CMDLINE += console=ttyS0
else
QEMU_BIN := qemu-system-aarch64
QEMU_KERNEL_CMDLINE += console=ttyAMA0
QEMU_ARGS += \
-M virt \
-cpu cortex-a53 \
-semihosting-config enable=on,target=native
endif
ifeq ($(KASLR),0)
QEMU_KERNEL_CMDLINE += nokaslr
endif
# This is not an actual kernel parameter, but it allows us to check from within
# QEMU if this is an ACK kernel or not.
ifeq ($(ACK),1)
QEMU_KERNEL_CMDLINE += lede.ack=1
endif
QEMU_ARGS += -append "$(QEMU_KERNEL_CMDLINE) $(QEMU_EXTRA_KERNEL_CMDLINE)"
RUN_DEPS := $(QEMU_KERNEL_IMAGE)
# Make sure the modules directory exists, even if it's empty. Otherwise mount
# will fail.
$(LINUX_MODULES_INSTALL_PATH)/lib/modules:
mkdir -p $@
.PHONY: run
run: $(RUN_DEPS) | $(SHARED_DIR) $(LINUX_MODULES_INSTALL_PATH)/lib/modules
@echo "$(GREEN)Running QEMU, press 'ctrl-a x' to quit $(NC)"
ifeq ($(GDB),1)
@echo "$(ARCH) $(ACK)" > $(OUT_DIR)/.gdb
@echo "$(GREEN)Waiting for GDB, attach with \`scripts/gdb.sh\` $(NC)"
ifdef TERMINAL_CMD
$(TERMINAL_CMD) $(SCRIPT_DIR)/gdb.sh
endif
endif
@echo ''
$(QEMU_BIN) $(QEMU_ARGS)
.PHONY: run-ack
run-ack: run
EXTERNAL_DIR := $(ROOT_DIR)/external
ifneq (,$(wildcard $(EXTERNAL_DIR)/external.mk))
include $(EXTERNAL_DIR)/external.mk
endif