From 742913ebcb10cf819a54699497359535047874f7 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 28 Sep 2025 01:10:11 -0700 Subject: [PATCH] Support installing Magisk on vendor_boot Close #9238, fix #8835 --- .../java/com/topjohnwu/magisk/core/Info.kt | 3 + .../magisk/core/tasks/MagiskInstaller.kt | 10 +-- native/src/boot/bootimg.cpp | 67 ++++++++++--------- native/src/boot/bootimg.hpp | 14 ++-- native/src/boot/sign.rs | 4 +- scripts/app_functions.sh | 2 + scripts/boot_patch.sh | 41 ++++++++---- scripts/util_functions.sh | 17 ++++- 8 files changed, 97 insertions(+), 61 deletions(-) diff --git a/app/core/src/main/java/com/topjohnwu/magisk/core/Info.kt b/app/core/src/main/java/com/topjohnwu/magisk/core/Info.kt index 4def8ce52..8aa8df540 100644 --- a/app/core/src/main/java/com/topjohnwu/magisk/core/Info.kt +++ b/app/core/src/main/java/com/topjohnwu/magisk/core/Info.kt @@ -47,6 +47,8 @@ object Info { private set var slot = "" private set + var isVendorBoot = false + private set @JvmField val isZygiskEnabled = System.getenv("ZYGISK_ENABLED") == "1" @JvmStatic val isFDE get() = crypto == "block" @JvmStatic var ramdisk = false @@ -113,6 +115,7 @@ object Info { crypto = getVar("CRYPTOTYPE") slot = getVar("SLOT") legacySAR = getBool("LEGACYSAR") + isVendorBoot = getBool("VENDORBOOT") // Default presets Config.recovery = getBool("RECOVERYMODE") diff --git a/app/core/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt b/app/core/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt index 507fb4185..4468d3a9c 100644 --- a/app/core/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt +++ b/app/core/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt @@ -81,10 +81,12 @@ abstract class MagiskInstallImpl protected constructor( } private fun findImage(slot: String): Boolean { - val bootPath = ( - "(RECOVERYMODE=${Config.recovery} " + - "SLOT=$slot find_boot_image; " + - "echo \$BOOTIMAGE)").fsh() + val cmd = + "RECOVERYMODE=${Config.recovery} " + + "VENDORBOOT=${Info.isVendorBoot} " + + "SLOT=$slot " + + "find_boot_image; echo \$BOOTIMAGE" + val bootPath = ("($cmd)").fsh() if (bootPath.isEmpty()) { console.add("! Unable to detect target image") return false diff --git a/native/src/boot/bootimg.cpp b/native/src/boot/bootimg.cpp index 519cb0104..2eadf807f 100644 --- a/native/src/boot/bootimg.cpp +++ b/native/src/boot/bootimg.cpp @@ -15,6 +15,11 @@ using namespace std; #define SHA256_DIGEST_SIZE 32 #define SHA_DIGEST_SIZE 20 +#define RETURN_OK 0 +#define RETURN_ERROR 1 +#define RETURN_CHROMEOS 2 +#define RETURN_VENDOR 3 + static void decompress(FileFormat type, int fd, const void *in, size_t size) { decompress_bytes(type, byte_view { in, size }, fd); } @@ -217,7 +222,7 @@ map(image), k_fmt(FileFormat::UNKNOWN), r_fmt(FileFormat::UNKNOWN), e_fmt(FileFo break; } } - exit(1); + exit(RETURN_ERROR); } boot_img::~boot_img() { @@ -264,7 +269,6 @@ struct [[gnu::packed]] fdt_header { fdt32_t size_dt_struct; /* size of the structure block */ }; - static int find_dtb_offset(const uint8_t *buf, unsigned sz) { const uint8_t * const end = buf + sz; @@ -403,6 +407,23 @@ static const char *vendor_ramdisk_type(int type) { } } +std::span boot_img::vendor_ramdisk_tbl() const { + if (hdr->vendor_ramdisk_table_size() == 0) { + return {}; + } + + // v4 vendor boot contains multiple ramdisks + using table_entry = const vendor_ramdisk_table_entry_v4; + if (hdr->vendor_ramdisk_table_entry_size() != sizeof(table_entry)) { + fprintf(stderr, + "! Invalid vendor image: vendor_ramdisk_table_entry_size != %zu\n", + sizeof(table_entry)); + exit(RETURN_ERROR); + } + return span(reinterpret_cast(vendor_ramdisk_table), hdr->vendor_ramdisk_table_entry_num()); +} + + #define assert_off() \ if ((base_addr + off) > (map.data() + map_end)) { \ fprintf(stderr, "Corrupted boot image!\n"); \ @@ -421,6 +442,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) { fprintf(stderr, "Invalid boot image header!\n"); return false; } + this->hdr = hdr; if (const char *id = hdr->id()) { for (int i = SHA_DIGEST_SIZE + 4; i < SHA256_DIGEST_SIZE; ++i) { @@ -468,9 +490,9 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) { k_fmt = check_fmt_lg(kernel, hdr->kernel_size()); } if (k_fmt == FileFormat::ZIMAGE) { - z_hdr = reinterpret_cast(kernel); + z_info.hdr = reinterpret_cast(kernel); - const uint8_t* found_pos = 0; + const uint8_t* found_pos = nullptr; for (const uint8_t* search_pos = kernel + 0x28; search_pos < kernel + hdr->kernel_size(); search_pos++) { // ^^^^^^ +0x28 to search after zimage header and magic @@ -480,12 +502,12 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) { } } - if (found_pos != 0) { + if (found_pos != nullptr) { fprintf(stderr, "ZIMAGE_KERNEL\n"); - z_info.hdr_sz = (const uint8_t *) found_pos - kernel; + z_info.hdr_sz = found_pos - kernel; // Find end of piggy - uint32_t zImage_size = z_hdr->end - z_hdr->start; + uint32_t zImage_size = z_info.hdr->end - z_info.hdr->start; uint32_t piggy_end = zImage_size; uint32_t offsets[16]; memcpy(offsets, kernel + zImage_size - sizeof(offsets), sizeof(offsets)); @@ -513,19 +535,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) { } if (auto size = hdr->ramdisk_size()) { if (hdr->vendor_ramdisk_table_size()) { - // v4 vendor boot contains multiple ramdisks - using table_entry = const vendor_ramdisk_table_entry_v4; - if (hdr->vendor_ramdisk_table_entry_size() != sizeof(table_entry)) { - fprintf(stderr, - "! Invalid vendor image: vendor_ramdisk_table_entry_size != %zu\n", - sizeof(table_entry)); - exit(1); - } - - span table( - reinterpret_cast(vendor_ramdisk_table), - hdr->vendor_ramdisk_table_entry_num()); - for (auto &it : table) { + for (auto &it : vendor_ramdisk_tbl()) { FileFormat fmt = check_fmt_lg(ramdisk + it.ramdisk_offset, it.ramdisk_size); fprintf(stderr, "%-*s name=[%s] type=[%s] size=[%u] fmt=[%s]\n", PADDING, "VND_RAMDISK", @@ -579,7 +589,6 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) { } } - this->hdr = hdr; return true; } @@ -680,7 +689,9 @@ int unpack(Utf8CStr image, bool skip_decomp, bool hdr) { // Dump bootconfig dump(boot.bootconfig, boot.hdr->bootconfig_size(), BOOTCONFIG_FILE); - return boot.flags[CHROMEOS_FLAG] ? 2 : 0; + if (boot.flags[CHROMEOS_FLAG]) return RETURN_CHROMEOS; + if (boot.hdr->is_vendor()) return RETURN_VENDOR; + return RETURN_OK; } #define file_align_with(page_size) \ @@ -744,7 +755,7 @@ void repack(Utf8CStr src_img, Utf8CStr out_img, bool skip_comp) { } if (boot.flags[ZIMAGE_KERNEL]) { // Copy zImage headers - xwrite(fd, boot.z_hdr, boot.z_info.hdr_sz); + xwrite(fd, boot.z_info.hdr, boot.z_info.hdr_sz); } if (access(KERNEL_FILE, R_OK) == 0) { mmap_data m(KERNEL_FILE); @@ -794,15 +805,11 @@ void repack(Utf8CStr src_img, Utf8CStr out_img, bool skip_comp) { xwrite(fd, boot.r_hdr, sizeof(mtk_hdr)); } - using table_entry = vendor_ramdisk_table_entry_v4; - vector ramdisk_table; + vector ramdisk_table; if (boot.hdr->vendor_ramdisk_table_size()) { // Create a copy so we can modify it - auto entry_start = reinterpret_cast(boot.vendor_ramdisk_table); - ramdisk_table.insert( - ramdisk_table.begin(), - entry_start, entry_start + boot.hdr->vendor_ramdisk_table_entry_num()); + ramdisk_table.assign_range(boot.vendor_ramdisk_tbl()); owned_fd dirfd = xopen(VND_RAMDISK_DIR, O_RDONLY | O_CLOEXEC); uint32_t ramdisk_offset = 0; @@ -885,7 +892,7 @@ void repack(Utf8CStr src_img, Utf8CStr out_img, bool skip_comp) { // vendor ramdisk table if (!ramdisk_table.empty()) { - xwrite(fd, ramdisk_table.data(), sizeof(table_entry) * ramdisk_table.size()); + xwrite(fd, ramdisk_table.data(), sizeof(*ramdisk_table.data()) * ramdisk_table.size()); file_align(); } diff --git a/native/src/boot/bootimg.hpp b/native/src/boot/bootimg.hpp index ead64b486..8cde3b48e 100644 --- a/native/src/boot/bootimg.hpp +++ b/native/src/boot/bootimg.hpp @@ -437,7 +437,7 @@ private: #define __impl_cls(name, hdr) \ protected: name() = default; \ public: \ -name(const void *ptr) { \ +explicit name(const void *ptr) { \ raw = malloc(sizeof(hdr)); \ memcpy(raw, ptr, sizeof(hdr)); \ } \ @@ -651,10 +651,10 @@ struct boot_img { // +---------------+ // | z_info.tail | z_info.tail.sz() // +---------------+ - const zimage_hdr *z_hdr = nullptr; struct { - uint32_t hdr_sz; - byte_view tail; + const zimage_hdr *hdr = nullptr; + uint32_t hdr_sz = 0; + byte_view tail{}; } z_info; // AVB structs @@ -675,14 +675,12 @@ struct boot_img { // dtb embedded in kernel byte_view kernel_dtb; - // Blocks defined in header but we do not care - byte_view ignore; - - boot_img(const char *); + explicit boot_img(const char *); ~boot_img(); bool parse_image(const uint8_t *addr, FileFormat type); std::pair create_hdr(const uint8_t *addr, FileFormat type); + std::span vendor_ramdisk_tbl() const; // Rust FFI static std::unique_ptr create(Utf8CStr name) { return std::make_unique(name.c_str()); } diff --git a/native/src/boot/sign.rs b/native/src/boot/sign.rs index 4dd1c03ab..7de26ec0f 100644 --- a/native/src/boot/sign.rs +++ b/native/src/boot/sign.rs @@ -25,7 +25,7 @@ use x509_cert::der::Any; use x509_cert::der::asn1::{OctetString, PrintableString}; use x509_cert::spki::AlgorithmIdentifier; -use base::{LoggedResult, MappedFile, ResultExt, Utf8CStr, cstr, log_err}; +use base::{LoggedResult, MappedFile, ResultExt, SilentLogExt, Utf8CStr, cstr, log_err}; use crate::ffi::BootImage; @@ -273,7 +273,7 @@ impl BootImage { // Don't use BootSignature::from_der because tail might have trailing zeros let mut reader = SliceReader::new(tail)?; - let mut sig = BootSignature::decode(&mut reader)?; + let mut sig = BootSignature::decode(&mut reader).silent()?; if let Some(s) = cert { let pem = MappedFile::open(s)?; sig.certificate = Certificate::from_pem(pem)?; diff --git a/scripts/app_functions.sh b/scripts/app_functions.sh index 26dc0d66f..b2e242e8f 100644 --- a/scripts/app_functions.sh +++ b/scripts/app_functions.sh @@ -214,6 +214,7 @@ get_flags() { PATCHVBMETAFLAG=true fi [ -z $RECOVERYMODE ] && RECOVERYMODE=false + [ -z $VENDORBOOT ] && VENDORBOOT=false } run_migrations() { return; } @@ -243,6 +244,7 @@ app_init() { printvar RECOVERYMODE printvar KEEPVERITY printvar KEEPFORCEENCRYPT + printvar VENDORBOOT } export BOOTMODE=true diff --git a/scripts/boot_patch.sh b/scripts/boot_patch.sh index 64f3d8e07..db5696f93 100644 --- a/scripts/boot_patch.sh +++ b/scripts/boot_patch.sh @@ -86,54 +86,66 @@ chmod -R 755 . ######### CHROMEOS=false +VENDORBOOT=false ui_print "- Unpacking boot image" ./magiskboot unpack "$BOOTIMAGE" case $? in 0 ) ;; - 1 ) - abort "! Unsupported/Unknown image format" - ;; 2 ) ui_print "- ChromeOS boot image detected" CHROMEOS=true ;; + 3 ) + ui_print "- Vendor boot image detected" + VENDORBOOT=true + ;; * ) abort "! Unable to unpack boot image" ;; esac -################### -# Ramdisk Restores -################### +################# +# Ramdisk Checks +################# + +unset RAMDISK +for path in ramdisk.cpio vendor_ramdisk/init_boot.cpio vendor_ramdisk/ramdisk.cpio; do + if [ -e $path ]; then + RAMDISK=$path + break + fi +done -# Test patch status and do restore ui_print "- Checking ramdisk status" -if [ -e ramdisk.cpio ]; then - ./magiskboot cpio ramdisk.cpio test +if [ -n "$RAMDISK" ]; then + ./magiskboot cpio $RAMDISK test STATUS=$? SKIP_BACKUP="" else - # Stock A only legacy SAR, or some Android 13 GKIs + # No ramdisk found, create one from scratch + RAMDISK=ramdisk.cpio + # Could be stock A only legacy SAR, or some Android 13 GKIs STATUS=0 SKIP_BACKUP="#" fi + case $STATUS in 0 ) # Stock boot ui_print "- Stock boot image detected" SHA1=$(./magiskboot sha1 "$BOOTIMAGE" 2>/dev/null) cat $BOOTIMAGE > stock_boot.img - cp -af ramdisk.cpio ramdisk.cpio.orig 2>/dev/null + cp -af $RAMDISK ramdisk.cpio.orig 2>/dev/null ;; 1 ) # Magisk patched ui_print "- Magisk patched boot image detected" - ./magiskboot cpio ramdisk.cpio \ + ./magiskboot cpio $RAMDISK \ "extract .backup/.magisk config.orig" \ "restore" - cp -af ramdisk.cpio ramdisk.cpio.orig + cp -af $RAMDISK ramdisk.cpio.orig rm -f stock_boot.img ;; 2 ) @@ -170,13 +182,14 @@ $BOOTMODE && [ -z "$PREINITDEVICE" ] && PREINITDEVICE=$(./magisk --preinit-devic echo "KEEPVERITY=$KEEPVERITY" > config echo "KEEPFORCEENCRYPT=$KEEPFORCEENCRYPT" >> config echo "RECOVERYMODE=$RECOVERYMODE" >> config +echo "VENDORBOOT=$VENDORBOOT" >> config if [ -n "$PREINITDEVICE" ]; then ui_print "- Pre-init storage partition: $PREINITDEVICE" echo "PREINITDEVICE=$PREINITDEVICE" >> config fi [ -n "$SHA1" ] && echo "SHA1=$SHA1" >> config -./magiskboot cpio ramdisk.cpio \ +./magiskboot cpio $RAMDISK \ "add 0750 init magiskinit" \ "mkdir 0750 overlay.d" \ "mkdir 0750 overlay.d/sbin" \ diff --git a/scripts/util_functions.sh b/scripts/util_functions.sh index 1a80a1a64..dd91cd177 100644 --- a/scripts/util_functions.sh +++ b/scripts/util_functions.sh @@ -325,7 +325,7 @@ mount_partitions() { # After calling this method, the following variables will be set: # ISENCRYPTED, PATCHVBMETAFLAG, -# KEEPVERITY, KEEPFORCEENCRYPT, RECOVERYMODE +# KEEPVERITY, KEEPFORCEENCRYPT, RECOVERYMODE, VENDORBOOT get_flags() { if grep ' /data ' /proc/mounts | grep -q 'dm-'; then ISENCRYPTED=true @@ -348,6 +348,7 @@ get_flags() { getvar KEEPVERITY getvar KEEPFORCEENCRYPT getvar RECOVERYMODE + getvar VENDORBOOT if [ -z $KEEPVERITY ]; then if $SYSTEM_AS_ROOT; then KEEPVERITY=true @@ -365,13 +366,23 @@ get_flags() { fi fi [ -z $RECOVERYMODE ] && RECOVERYMODE=false + [ -z $VENDORBOOT ] && VENDORBOOT=false } +# Returns whether the device is GKI 13+ +is_gt_gki_13() { + [ "$(uname -r | cut -d. -f1)" -ge 5 ] && uname -r | grep -Evq "android12-|^5\.4" +} + +# Require RECOVERYMODE, VENDORBOOT, SLOT to be set. +# After calling this method, BOOTIMAGE will be set. find_boot_image() { BOOTIMAGE= - if $RECOVERYMODE; then + if $VENDORBOOT; then + BOOTIMAGE="/dev/block/by-name/vendor_boot$SLOT" + elif $RECOVERYMODE; then BOOTIMAGE=$(find_block "recovery$SLOT" "sos") - elif [ -e "/dev/block/by-name/init_boot$SLOT" ] && [ "$(uname -r | cut -d. -f1)" -ge 5 ] && uname -r | grep -Evq "android12-|^5\.4"; then + elif [ -e "/dev/block/by-name/init_boot$SLOT" ] && is_gt_gki_13; then # init_boot is only used with GKI 13+. It is possible that some devices with init_boot # partition still uses Android 12 GKI or previous kernels, so we need to explicitly detect that scenario. BOOTIMAGE="/dev/block/by-name/init_boot$SLOT"