Makefile: Add rules for building kselftests

Linux kselftests are userspace programs (with optional kernel modules)
built from the Linux codebase. Add rules for building these.

Building these is a bit cumbersome because:
  * cross-compiling with Clang is not supported, and
  * building out-of-tree still clobbers the source directory.

Because of these, we use rsync to make a copy of the source directory
and build the tests in-tree. Tests are rebuilt only if some files were
overwritten by rsync (and a stamp file touched).

When the kernel (and modules) are built with Clang, the userspace
programs are built with GCC. The config file for Clang is copied over
and updated for GCC with `make olddefconfig`. This should preserve the
same configuration options.

Bug: 193407077
Test: make kselftest
Change-Id: I8e7fb0b39139f806c06b3847ce581389b2f1266c
diff --git a/Makefile b/Makefile
index dbab5af..db7c74a 100644
--- a/Makefile
+++ b/Makefile
@@ -84,12 +84,14 @@
 default: kvm-unit-tests linux
 
 .PHONY: clean
-clean: buildroot_clean kvm-unit-tests_clean linux_clean
+clean: buildroot_clean kvm-unit-tests_clean linux_clean kselftest_clean
 
 .PHONY: distclean
 distclean:
 	- rm -rf $(OUT_DIR) $(DIST_DIR)
 
+FORCE:
+
 .PHONY: all
 all: buildroot kvm-unit-tests linux
 
@@ -100,14 +102,16 @@
 	@echo '   all                  - builds all generic targets'
 	@echo '   buildroot            - the buildroot rootfs image as well as the host qemu'
 	@echo '   kvm-unit-tests       - the kvm-unit-tests'
-	@echo '   linux                - the linux kernel'
+	@echo '   linux                - the Linux kernel'
+	@echo '   kselftest            - the Linux kernel selftests'
 	@echo ''
 	@echo 'Clean Targets:'
 	@echo '   clean                - cleans all, but keeps the prebuilts'
 	@echo '   distclean            - distclean for all targets'
 	@echo '   buildroot_clean      - the buildroot rootfs image as well as the host qemu'
 	@echo '   kvm-unit-tests_clean - the kvm-unit-tests'
-	@echo '   linux_clean          - the linux kernel'
+	@echo '   linux_clean          - the Linux kernel'
+	@echo '   kselftest_clean      - the Linux kernel selftests'
 	@echo ''
 	@echo 'Test Targets:'
 	@echo '   test                 - runs all tests in qemu-aarch64 simulated environment'
@@ -265,6 +269,58 @@
 
 
 ##
+## Linux kselftest
+##
+KSFT_VERBOSE ?= $(VERBOSE)
+KSFT_SRC ?= $(LINUX_SRC)
+KSFT_SRC_STAMP ?= $(KSFT_OUT)/ksefltest_src.stamp
+KSFT_OUT ?= $(OUT_DIR)/kselftest
+KSFT_OUT_CONFIG ?= $(KSFT_OUT)/.config
+KSFT_INSTALL_DIR ?= $(KSFT_OUT)/tools/testing/selftests/kselftest_install
+KSFT_STAMP ?= $(KSFT_INSTALL_DIR)/kselftest-list.txt
+
+KSFT_TESTS := \
+	lib:printf.sh \
+	kvm:kvm_create_max_vcpus \
+	kvm:set_memory_region_test \
+	kvm:steal_time
+
+ifneq ($(TOOLCHAIN_IS_LLVM),1)
+KSFT_TOOLCHAIN ?= $(TOOLCHAIN)
+else
+KSFT_TOOLCHAIN ?= gcc-9.2
+endif
+$(eval $(call define_toolchain,$(KSFT_TOOLCHAIN),KSFT_))
+
+KSFT_MAKE := \
+	PATH=$(KSFT_TOOLCHAIN_BIN):$(PATH) \
+	ARCH=$(ARCH) \
+	CROSS_COMPILE="$(KSFT_TARGET)-" \
+	$(MAKE) \
+	-C $(KSFT_OUT) \
+	V=$(KSFT_VERBOSE)
+
+KSFT_MAKE_TARGETS := $(sort $(foreach t,$(KSFT_TESTS),$(firstword $(subst :, ,$(t)))))
+
+# Copy the source to another folder because 'kselftest-install' clobbers it.
+# Stamp is touched if any files (not directories) were synced.
+$(KSFT_SRC_STAMP): FORCE
+	@ mkdir -p $(KSFT_OUT) $(@D)
+	@ LIST="$$(rsync -a --info=name $(KSFT_SRC)/ $(KSFT_OUT) | grep -vE '/$$')"; \
+	  [ -f $@ -a -z "$$LIST" ] || touch $@
+
+.PHONY: kselftest
+kselftest $(KSFT_STAMP): $(KSFT_SRC_STAMP) $(LINUX_CONFIG)
+	@ cp $(LINUX_CONFIG) $(KSFT_OUT_CONFIG)
+	+ $(KSFT_MAKE) olddefconfig prepare
+	+ $(KSFT_MAKE) TARGETS="$(KSFT_MAKE_TARGETS)" kselftest-install
+
+.PHONY: kselftest_clean
+kselftest_clean:
+	@ rm -rf $(KSFT_OUT)
+
+
+##
 ## Generating/Updating prebuilts
 ##