Building OnePlus Kernels

Dependencies

First, figure out the system info for your build.

adb shell
cat /proc/version # Identify gcc version
getprop ro.build.version.release # Get release version

Set up your working directory and get required dependencies. The NDK version can be found by googling around with the clang version retrieved above. In our case, we have an OnePlus 10 Pro with Android 13.0. The NDK version is r23.

mkdir oneplus && cd oneplus
wget https://dl.google.com/android/repository/android-ndk-r23-linux.zip # Get NDK
unzip android-ndk-* # Decompress
rm android-ndk-*.zip
mkdir prebuilts && cd prebuilts
git clone https://android.googlesource.com/platform/prebuilts/build-tools -b android-13.0.0_r1 --depth 1 # Pick a branch matching your android version 
cd ..
git clone https://android.googlesource.com/kernel/build -b android-13.0.0_r0.130 --depth 1
mkdir external && cd external
git clone https://android.googlesource.com/platform/external/dtc --depth 1
cd dtc
ln -s dtc-parser.tab.h dtc-parser.h
cd ../..

Now we need to grab the kernel sources. The code structure is pretty picky, so we need to match what it expects. Find the corresponding android_kernel_msm and android_kernel_modules repositories for your device on GitHub, and make sure to checkout the correct branch.

git clone https://github.com/OnePlusOSS/android_kernel_modules_and_devicetree_oneplus_sm8450 -b oneplus/sm8450_t_13.0_10pro --depth 1
ln -s android_kernel_modules_and_devicetree_oneplus_sm8450/vendor vendor
ln -s android_kernel_modules_and_devicetree_oneplus_sm8450/kernel_platform kernel_platform
mkdir kernel && cd kernel
git clone https://github.com/OnePlusOSS/android_kernel_msm-5.10_oneplus_sm8450 -b oneplus/sm8450_t_13.0_10pro --depth 1
cd ..
ln -s kernel/android_kernel_msm-5.10_oneplus_sm8450 common

Now that we have the files, our directory structure looks like this:

.
├── android_kernel_modules_and_devicetree_oneplus_sm8450
├── android-ndk-r23
├── build
├── common -> kernel/android_kernel_msm-5.10_oneplus_sm8450/
├── external
│   └── dtc
├── kernel
│   └── android_kernel_msm-5.10_oneplus_sm8450
├── kernel_platform -> android_kernel_modules_and_devicetree_oneplus_sm8450/kernel_platform
├── prebuilts
│   └── build-tools
└── vendor -> android_kernel_modules_and_devicetree_oneplus_sm8450/vendor

And we get to go and link a bunch of files, along with our final dependency

cd kernel/android*
cd arch/arm64/boot/dts
mv qcom qcom_original
ln -s ../../../../../../kernel_platform/qcom/proprietary/devicetree/qcom qcom
ln -s ../../../../../../kernel_platform/qcom/proprietary/devicetree/oplus oplus
rm vendor
ln -s ../../../../../../kernel_platform/qcom/proprietary/devicetree vendor
cd ../../../../include/soc/oplus
ln -s ../../../../../vendor/oplus/kernel/boot/include/ boot
ln -s ../../../../../vendor/oplus/kernel/dft/include/ dft
cd ../../../drivers/soc/oplus
ln -s ../../../../../vendor/oplus/kernel/boot/ boot
ln -s ../../../../../vendor/oplus/kernel/dfr/ dfr
ln -s ../../../../../vendor/oplus/kernel/dft/ dft
ln -s ../../../../../vendor/oplus/kernel/power/ power
ln -s ../../../../../vendor/oplus/kernel/system/ system
cd ../../input/
ln -s ../../../../vendor/oplus/secure/common/bsp/drivers/ oplus_secure_drivers
cd ../../../..

Then, let's set up a script for our environment variables.
We'll set BUILD_CONFIG to the right config for our system, which is named after the SoC. In our device, it's build.config.msm.waipio.

# env.sh
export ARCH=arm64
export TARGET=arm64
export CROSS_COMPILE=aarch64-linux-android31- # set to correct version
export LLVM=1
export PATH=/path/to/oneplus/android-ndk-r23/toolchains/llvm/prebuilt/linux-x86_64/bin/:${PATH}
export CC=clang-12
export HOSTCC=/usr/bin/clang-17 # may be ignored
export AR=llvm-ar
export OBJCOPY=llvm-objcopy
export OUT_DIR=out
export KERNEL_DIR=kernel/android_kernel_msm-5.10_oneplus_sm8450
export BUILD_CONFIG=${KERNEL_DIR}/build.config.msm.waipio

Then we have to patch the kernel a bit apply the following to the kernel, which seems to have some code pulled from a future kernel version somehow.

diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index fc35eb5af..e9b532a61 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -214,11 +214,11 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
 
 	if (mm) {
 		mmap_read_lock(mm);
-		if (!mmget_still_valid(mm)) {
+		//if (!mmget_still_valid(mm)) {
 			if (allocate == 0)
 				goto free_range;
 			goto err_no_vma;
-		}
+		//}
 		vma = alloc->vma;
 	}
 
diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c
index 982ecccec..f52708f31 100644
--- a/drivers/thermal/thermal_sysfs.c
+++ b/drivers/thermal/thermal_sysfs.c
@@ -906,7 +906,7 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
 	var += sizeof(*stats->time_in_state) * states;
 	var += sizeof(*stats->trans_table) * states * states;
 
-	stats = kvzalloc(var, GFP_KERNEL);
+	stats = kzalloc(var, GFP_KERNEL);
 	if (!stats)
 		return;
 
@@ -925,7 +925,7 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
 
 static void cooling_device_stats_destroy(struct thermal_cooling_device *cdev)
 {
-	kvfree(cdev->stats);
+	kfree(cdev->stats);
 	cdev->stats = NULL;
 }
 

If you want to disable LTO (see later), apply this patch to the modules and devicetree repository to add some missing annotations that aren't caught when LTO is enabled.

diff --git a/vendor/oplus/kernel/boot/misc/bootloader_log.c b/vendor/oplus/kernel/boot/misc/bootloader_log.c
index cb41ae7..0c2dc66 100755
--- a/vendor/oplus/kernel/boot/misc/bootloader_log.c
+++ b/vendor/oplus/kernel/boot/misc/bootloader_log.c
@@ -146,7 +146,7 @@ static int persistent_ram_buffer_map(phys_addr_t start, size_t size,
 }
 
 
-static int bootloader_log_probe(struct platform_device *pdev)
+static int __init bootloader_log_probe(struct platform_device *pdev)
 {
 	struct bootloader_log_platform_data *pdata = pdev->dev.platform_data;
 	struct bootloader_log_platform_data of_pdata;
@@ -201,7 +201,7 @@ static const struct of_device_id bootloader_log_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, bootloader_log_of_match);
 
-static struct platform_driver bootloader_log_driver = {
+static struct platform_driver bootloader_log_driver __refdata = {
 	.probe		= bootloader_log_probe,
 	.remove		= __exit_p(bootloader_log_remove),
 	.driver		= {
diff --git a/vendor/oplus/kernel/dfr/qcom/dump_device_info/dump_device_info.c b/vendor/oplus/kernel/dfr/qcom/dump_device_info/dump_device_info.c
index 8ffbc9d..258a154 100755
--- a/vendor/oplus/kernel/dfr/qcom/dump_device_info/dump_device_info.c
+++ b/vendor/oplus/kernel/dfr/qcom/dump_device_info/dump_device_info.c
@@ -286,7 +286,7 @@ static const struct of_device_id dump_device_info_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, dump_device_info_of_match);
 
-static struct platform_driver dump_device_info_driver = {
+static struct platform_driver dump_device_info_driver __refdata = {
 	.probe = dump_device_info_probe,
 	.remove = __exit_p(dump_device_info_remove),
 	.driver = {
 ~/o/android_kernel_modules_and_devicetree_oneplus_sm8450   oneplus/sm8450_t_13.0_10pro *…  less temp.patch 
 ~/o/android_kernel_modules_and_devicetree_oneplus_sm8450   oneplus/sm8450_t_13.0_10pro *…  cat temp.patch
diff --git a/vendor/oplus/kernel/boot/misc/bootloader_log.c b/vendor/oplus/kernel/boot/misc/bootloader_log.c
index cb41ae7..0c2dc66 100755
--- a/vendor/oplus/kernel/boot/misc/bootloader_log.c
+++ b/vendor/oplus/kernel/boot/misc/bootloader_log.c
@@ -146,7 +146,7 @@ static int persistent_ram_buffer_map(phys_addr_t start, size_t size,
 }
 
 
-static int bootloader_log_probe(struct platform_device *pdev)
+static int __init bootloader_log_probe(struct platform_device *pdev)
 {
 	struct bootloader_log_platform_data *pdata = pdev->dev.platform_data;
 	struct bootloader_log_platform_data of_pdata;
@@ -201,7 +201,7 @@ static const struct of_device_id bootloader_log_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, bootloader_log_of_match);
 
-static struct platform_driver bootloader_log_driver = {
+static struct platform_driver bootloader_log_driver __refdata = {
 	.probe		= bootloader_log_probe,
 	.remove		= __exit_p(bootloader_log_remove),
 	.driver		= {
diff --git a/vendor/oplus/kernel/dfr/qcom/dump_device_info/dump_device_info.c b/vendor/oplus/kernel/dfr/qcom/dump_device_info/dump_device_info.c
index 8ffbc9d..258a154 100755
--- a/vendor/oplus/kernel/dfr/qcom/dump_device_info/dump_device_info.c
+++ b/vendor/oplus/kernel/dfr/qcom/dump_device_info/dump_device_info.c
@@ -286,7 +286,7 @@ static const struct of_device_id dump_device_info_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, dump_device_info_of_match);
 
-static struct platform_driver dump_device_info_driver = {
+static struct platform_driver dump_device_info_driver __refdata = {
 	.probe = dump_device_info_probe,
 	.remove = __exit_p(dump_device_info_remove),
 	.driver = {

Apply this patch to to the build repo.

diff --git a/build.sh b/build.sh
index f4ac890..8394141 100755
--- a/build.sh
+++ b/build.sh
@@ -693,7 +693,7 @@ if [ ! -z "${BUILD_BOOT_IMG}" ] ; then
        fi
 
        set -x
-       python "$MKBOOTIMG_PATH" --kernel "${DIST_DIR}/${KERNEL_BINARY}" \
+       python3 "$MKBOOTIMG_PATH" --kernel "${DIST_DIR}/${KERNEL_BINARY}" \
                --header_version "${BOOT_IMAGE_HEADER_VERSION}" \
                "${MKBOOTIMG_ARGS[@]}" -o "${DIST_DIR}/boot.img"
        set +x

Build

Now we can try to compile.

. env.sh
build/build.sh -j8 # If you don't specify any flags here the script will blow up :)

We'll fail at the very end trying to build the vendor release, but that's fine. If you want to build just changes, make sure to tell the build script to not clean:

SKIP_MRPROPER=1 SKIP_DEFCONFIG=1 ./build/build.sh -j8

Fair warning that the LTO step on the full kernel vmlinux takes a lot of RAM. On my laptop which has 16G, I needed to allocate 8G of swap. If you want to disable it, edit arch/arm64/configs/gki_defconfig and delete the lines defining CONFIG_LTO_CLANG_FULL and CONFIG_CFI_CLANG (CFI requires LTO).

Building a boot image

WARNING: Built image has not been tested on actual device. Flash at your own risk.

First we get new dependencies and build a base boot image.

# On device
cat /dev/block/platform/soc/1d84000.ufshc/by-name/boot_a > boot.img 
cat /dev/block/platform/soc/1d84000.ufshc/by-name/vendor_boot_a > vendor_boot.img 
# Pull images to oplus/prebuild/img

# On host
git clone https://android.googlesource.com/platform/system/libufdt -b android-13.0.0_r1 --depth 1
# Compile libufdt
cd libufdt/sydeps
$HOSTCC -shared libufdt_sysdeps_posix.c -Iinclude -fPIC -o libufdt_sysdeps.so
cd ..
$HOSTCC -I./include -I./sysdeps/include tests/src/ufdt_overlay_test_app.c tests/src/util.c *.c -L./sysdeps -lfdt -lufdt_sysdeps -o ufdt_apply_overlay
cd ..
cd build
cp -r ../kernel_platform/oplus/build/ oplus # Need to copy because it uses readlink to find its base; but expects the android build tools in another place
cd ..

git clone https://android.googlesource.com/platform/system/tools/mkbootimg -b android-13.0.0_r1 --depth 1 tools/mkbootimg
export PATH=${PWD}/libufdt/utils/src:${PWD}/libufdt:${PATH}

Apply this diff to fix the build script.

14c14
< ANDROID_KERNEL_OUT=${ANDROID_BUILD_TOP}/device/qcom/${TARGET_PRODUCT}-kernel
---
> ANDROID_KERNEL_OUT=${OUT_DIR}
67c67
< gunzip vendor_ramdisk.gz
---
> unlz4 vendor_ramdisk.gz vendor_ramdisk
108c108
<   MKBOOTIMG_ARGS+=("--dtb" "${ANDROID_KERNEL_OUT}/dtbs/dtb.img")
---
>   MKBOOTIMG_ARGS+=("--dtb" "${DIST_DIR}/dtb.img")

Now we can build.

# Build boot image
SKIP_MRPROPER=1 SKIP_DEFCONFIG=1 BUILD_BOOT_IMG=1 ./build/build.sh -j8

# Build vendor boot
SKIP_MRPROPER=1 SKIP_DEFCONFIG=1 BUILD_BOOT_IMG=1 ./build/oplus/oplus_build_boot.sh 

Built images will be located at /out/dist/boot.img and /out/dist/vendor_boot.img.