build/debian: Build and use our custom Debian kernel

In this CL, we introduce a new flag `-k` to allow building our own Debian kernel (as opposed to using the default cloud kernel) in our FAI workflow.

- Add a new `avf` kernel flavour based on the existing `cloud` flavour
- Build `avf-amd64` (or `avf-arm64`) headers and image, and generate `.deb` packages
- Copy `.deb` packages to FAI's `localdebs` location and add package names to `package_config/AVF` for FAI to pick up during installation

Bug: 380176863
Change-Id: Ic3737746ff8b8949173fea79a6b6d0173fda058c
Test: ./build_in_container.sh -k
Test: ./build_in_container.sh -a aarch64 -k
diff --git a/build/debian/build.sh b/build/debian/build.sh
index 9104adc..e438f0f 100755
--- a/build/debian/build.sh
+++ b/build/debian/build.sh
@@ -11,6 +11,7 @@
 	echo "Options:"
 	echo "-h         Print usage and this help message and exit."
 	echo "-a ARCH    Architecture of the image [default is host arch: $(uname -m)]"
+	echo "-k         Build and use our custom kernel [default is cloud kernel]"
 	echo "-r         Release mode build"
 	echo "-w         Save temp work directory [for debugging]"
 }
@@ -22,7 +23,7 @@
 }
 
 parse_options() {
-	while getopts "a:hrw" option; do
+	while getopts "a:hkrw" option; do
 		case ${option} in
 			h)
 				show_help ; exit
@@ -30,6 +31,9 @@
 			a)
 				arch="$OPTARG"
 				;;
+			k)
+				use_custom_kernel=1
+				;;
 			r)
 				mode=release
 				;;
@@ -117,6 +121,33 @@
 			linux-image-generic
 		)
 	fi
+
+	if [[ "$use_custom_kernel" -eq 1 ]]; then
+		packages+=(
+			bc
+			bison
+			debhelper
+			dh-exec
+			flex
+			gcc-12
+			kernel-wedge
+			libelf-dev
+			libpci-dev
+			lz4
+			pahole
+			python3-jinja2
+			python3-docutils
+			quilt
+			rsync
+		)
+		if [[ "$arch" == "aarch64" ]]; then
+			packages+=(
+				gcc-arm-linux-gnueabihf
+				gcc-12-aarch64-linux-gnu
+			)
+		fi
+	fi
+
 	DEBIAN_FRONTEND=noninteractive \
 	apt install --no-install-recommends --assume-yes "${packages[@]}"
 
@@ -194,6 +225,74 @@
 	build_rust_binary_and_copy ip_addr_reporter
 }
 
+package_custom_kernel() {
+	if [[ "$use_custom_kernel" != 1 ]]; then
+		echo "linux-headers-generic" >> "${config_space}/package_config/AVF"
+		return
+	fi
+
+	# NOTE: 6.1 is the latest LTS kernel for which Debian's kernel build scripts
+	#       work on Python 3.10, the default version on our Ubuntu 22.04 builders.
+	local debian_kver="6.1.119-1"
+	local custom_flavour="avf"
+	local ksrc_base_url="https://deb.debian.org/debian/pool/main/l/linux"
+
+	local debian_ksrc_url="${ksrc_base_url}/linux_${debian_kver}.debian.tar.xz"
+	local orig_ksrc_url="${ksrc_base_url}/linux_${debian_kver%-*}.orig.tar.xz"
+
+	# 1. Grab original kernel source, merge debian patches etc.
+	mkdir -p "${workdir}/kernel/avf-${debian_arch}"
+	pushd "${workdir}/kernel" > /dev/null
+	wget "$orig_ksrc_url"
+	pushd "avf-${debian_arch}" > /dev/null
+	wget "${debian_ksrc_url}" -O - | tar xJ
+	# TODO: Copy our own kernel patches to debian/patches
+	#       and add patch file names in the desired order to debian/patches/series
+	./debian/rules orig
+
+	local abi_kver="$(sed -nE 's;Package: linux-support-(.*);\1;p' debian/control)"
+	local debarch_flavour="${custom_flavour}-${debian_arch}"
+	local abi_flavour="${abi_kver}-${debarch_flavour}"
+
+	# 2. Define our custom flavour and regenerate control file
+	# NOTE: Our flavour extends Debian's `cloud` config on the `none` featureset.
+	cat > debian/config/${debian_arch}/config.${debarch_flavour} <<EOF
+# TODO: Add our custom kernel config to this file
+EOF
+
+	sed -z "s;\[base\]\nflavours:;[base]\nflavours:\n ${debarch_flavour};" \
+	    -i debian/config/${debian_arch}/none/defines
+	cat >> debian/config/${debian_arch}/none/defines <<EOF
+[${debarch_flavour}_image]
+configs:
+ config.cloud
+ ${debian_arch}/config.${debarch_flavour}
+EOF
+	cat >> debian/config/${debian_arch}/defines <<EOF
+[${debarch_flavour}_description]
+hardware: ${arch} AVF
+hardware-long: ${arch} Android Virtualization Framework
+EOF
+	./debian/rules debian/control || true
+
+	# 3. Build the kernel and generate Debian packages
+	./debian/rules source
+	[[ "$arch" == "$(uname -m)" ]] || export $(dpkg-architecture -a $debian_arch)
+	make -j$(nproc) -f debian/rules.gen \
+	     "binary-arch_${debian_arch}_none_${debarch_flavour}"
+
+	# 4. Copy the packages to localdebs and add their names to package_config/AVF
+	popd > /dev/null
+	cp "linux-headers-${abi_flavour}_${debian_kver}_${debian_arch}.deb" \
+	   "linux-image-${abi_flavour}-unsigned_${debian_kver}_${debian_arch}.deb" \
+	   "${debian_cloud_image}/localdebs/"
+	popd > /dev/null
+	cat >> "${config_space}/package_config/AVF" <<EOF
+linux-headers-${abi_flavour}
+linux-image-${abi_flavour}-unsigned
+EOF
+}
+
 run_fai() {
 	local out="${built_image}"
 	make -C "${debian_cloud_image}" "image_bookworm_nocloud_${debian_arch}"
@@ -237,12 +336,14 @@
 arch="$(uname -m)"
 mode=debug
 save_workdir=0
+use_custom_kernel=0
 
 parse_options "$@"
 check_sudo
 install_prerequisites
 download_debian_cloud_image
 copy_android_config
+package_custom_kernel
 run_fai
 fdisk -l "${built_image}"
 images=()