embed LKM to ksud (#1472)

This commit is contained in:
weishu
2024-03-18 23:12:46 +08:00
committed by GitHub
parent 4bad691ec8
commit 9759a779cd
6 changed files with 105 additions and 57 deletions

View File

@@ -13,7 +13,11 @@ on:
- '.github/workflows/ksud.yml' - '.github/workflows/ksud.yml'
- 'userspace/ksud/**' - 'userspace/ksud/**'
jobs: jobs:
build-lkm:
uses: ./.github/workflows/build-lkm.yml
secrets: inherit
build: build:
needs: build-lkm
strategy: strategy:
matrix: matrix:
include: include:

View File

@@ -20,6 +20,14 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Download artifacts
uses: actions/download-artifact@v4
- name: Prepare LKM fies
run: |
cp android*-lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/
# cross build failed after Rust 1.68, see https://github.com/cross-rs/cross/issues/1222 # cross build failed after Rust 1.68, see https://github.com/cross-rs/cross/issues/1222
- name: Setup rustup - name: Setup rustup
run: | run: |

1
userspace/ksud/bin/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
**/*.ko

View File

@@ -22,7 +22,8 @@ struct Asset;
pub fn ensure_binaries(ignore_if_exist: bool) -> Result<()> { pub fn ensure_binaries(ignore_if_exist: bool) -> Result<()> {
for file in Asset::iter() { for file in Asset::iter() {
if file == "ksuinit" { if file == "ksuinit" || file.ends_with(".ko") {
// don't extract ksuinit and kernel modules
continue; continue;
} }
let asset = Asset::get(&file).ok_or(anyhow::anyhow!("asset not found: {}", file))?; let asset = Asset::get(&file).ok_or(anyhow::anyhow!("asset not found: {}", file))?;

View File

@@ -47,6 +47,25 @@ pub fn get_kernel_version() -> Result<(i32, i32, i32)> {
} }
} }
#[cfg(target_os = "android")]
fn parse_kmi() -> Result<String> {
use regex::Regex;
let uname = rustix::system::uname();
let version = uname.release().to_string_lossy();
let re = Regex::new(r"(.* )?(\d+\.\d+)(\S+)?(android\d+)(.*)")?;
let cap = re
.captures(&version)
.ok_or_else(|| anyhow::anyhow!("No match found"))?;
let android_version = cap.get(4).map_or("", |m| m.as_str());
let kernel_version = cap.get(2).map_or("", |m| m.as_str());
Ok(format!("{android_version}-{kernel_version}"))
}
#[cfg(not(target_os = "android"))]
fn parse_kmi() -> Result<String> {
bail!("Unknown KMI, try use --kmi to specify it.")
}
fn do_cpio_cmd(magiskboot: &Path, workding_dir: &Path, cmd: &str) -> Result<()> { fn do_cpio_cmd(magiskboot: &Path, workding_dir: &Path, cmd: &str) -> Result<()> {
let status = Command::new(magiskboot) let status = Command::new(magiskboot)
.current_dir(workding_dir) .current_dir(workding_dir)
@@ -98,9 +117,10 @@ pub fn patch(
ota: bool, ota: bool,
flash: bool, flash: bool,
out: Option<PathBuf>, out: Option<PathBuf>,
magiskboot_path: Option<PathBuf>, magiskboot: Option<PathBuf>,
kmi: Option<String>,
) -> Result<()> { ) -> Result<()> {
let result = do_patch(image, kernel, kmod, init, ota, flash, out, magiskboot_path); let result = do_patch(image, kernel, kmod, init, ota, flash, out, magiskboot, kmi);
if let Err(ref e) = result { if let Err(ref e) = result {
println!("- Install Error: {e}"); println!("- Install Error: {e}");
} }
@@ -117,6 +137,7 @@ fn do_patch(
flash: bool, flash: bool,
out: Option<PathBuf>, out: Option<PathBuf>,
magiskboot_path: Option<PathBuf>, magiskboot_path: Option<PathBuf>,
kmi: Option<String>,
) -> Result<()> { ) -> Result<()> {
println!(include_str!("banner")); println!(include_str!("banner"));
@@ -132,8 +153,6 @@ fn do_patch(
init.is_none() && kmod.is_none(), init.is_none() && kmod.is_none(),
"init and module must not be specified." "init and module must not be specified."
); );
} else {
ensure!(kmod.is_some(), "module must be specified");
} }
let workding_dir = let workding_dir =
@@ -207,61 +226,71 @@ fn do_patch(
.with_context(|| "copy kernel from failed".to_string())?; .with_context(|| "copy kernel from failed".to_string())?;
} }
println!("- Preparing assets");
let kmod_file = workding_dir.path().join("kernelsu.ko");
if let Some(kmod) = kmod { if let Some(kmod) = kmod {
println!("- Preparing assets"); std::fs::copy(kmod, kmod_file).with_context(|| "copy kernel module failed".to_string())?;
} else {
std::fs::copy(kmod, workding_dir.path().join("kernelsu.ko")) // If kmod is not specified, extract from assets
.with_context(|| "copy kernel module failed".to_string())?; let kmi = if let Some(kmi) = kmi {
let init_file = workding_dir.path().join("init"); kmi
if let Some(init) = init {
std::fs::copy(init, workding_dir.path().join("init"))
.with_context(|| "copy init failed".to_string())?;
} else { } else {
crate::assets::copy_assets_to_file("ksuinit", init_file) parse_kmi()?
.with_context(|| "copy ksuinit failed")?; };
} println!("- KMI: {kmi}");
let name = format!("{kmi}_kernelsu.ko");
assets::copy_assets_to_file(&name, kmod_file)
.with_context(|| format!("Failed to copy {name}"))?;
};
// magiskboot unpack boot.img let init_file = workding_dir.path().join("init");
// magiskboot cpio ramdisk.cpio 'cp init init.real' if let Some(init) = init {
// magiskboot cpio ramdisk.cpio 'add 0755 ksuinit init' std::fs::copy(init, init_file).with_context(|| "copy init failed".to_string())?;
// magiskboot cpio ramdisk.cpio 'add 0755 <kmod> kernelsu.ko' } else {
assets::copy_assets_to_file("ksuinit", init_file).with_context(|| "copy ksuinit failed")?;
println!("- Unpacking boot image");
let status = Command::new(&magiskboot)
.current_dir(workding_dir.path())
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("unpack")
.arg(bootimage.display().to_string())
.status()?;
ensure!(status.success(), "magiskboot unpack failed");
let no_ramdisk = !workding_dir.path().join("ramdisk.cpio").exists();
let is_magisk_patched = is_magisk_patched(&magiskboot, workding_dir.path())?;
ensure!(
no_ramdisk || !is_magisk_patched,
"Cannot work with Magisk patched image"
);
println!("- Adding KernelSU LKM");
let is_kernelsu_patched =
do_cpio_cmd(&magiskboot, workding_dir.path(), "exists kernelsu.ko").is_ok();
if !is_kernelsu_patched {
// kernelsu.ko is not exist, backup init if necessary
let status = do_cpio_cmd(&magiskboot, workding_dir.path(), "exists init");
if status.is_ok() {
do_cpio_cmd(&magiskboot, workding_dir.path(), "mv init init.real")?;
}
}
do_cpio_cmd(&magiskboot, workding_dir.path(), "add 0755 init init")?;
do_cpio_cmd(
&magiskboot,
workding_dir.path(),
"add 0755 kernelsu.ko kernelsu.ko",
)?;
} }
// magiskboot unpack boot.img
// magiskboot cpio ramdisk.cpio 'cp init init.real'
// magiskboot cpio ramdisk.cpio 'add 0755 ksuinit init'
// magiskboot cpio ramdisk.cpio 'add 0755 <kmod> kernelsu.ko'
println!("- Unpacking boot image");
let status = Command::new(&magiskboot)
.current_dir(workding_dir.path())
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("unpack")
.arg(bootimage.display().to_string())
.status()?;
ensure!(status.success(), "magiskboot unpack failed");
let no_ramdisk = !workding_dir.path().join("ramdisk.cpio").exists();
let is_magisk_patched = is_magisk_patched(&magiskboot, workding_dir.path())?;
ensure!(
no_ramdisk || !is_magisk_patched,
"Cannot work with Magisk patched image"
);
println!("- Adding KernelSU LKM");
let is_kernelsu_patched =
do_cpio_cmd(&magiskboot, workding_dir.path(), "exists kernelsu.ko").is_ok();
if !is_kernelsu_patched {
// kernelsu.ko is not exist, backup init if necessary
let status = do_cpio_cmd(&magiskboot, workding_dir.path(), "exists init");
if status.is_ok() {
do_cpio_cmd(&magiskboot, workding_dir.path(), "mv init init.real")?;
}
}
do_cpio_cmd(&magiskboot, workding_dir.path(), "add 0755 init init")?;
do_cpio_cmd(
&magiskboot,
workding_dir.path(),
"add 0755 kernelsu.ko kernelsu.ko",
)?;
println!("- Repacking boot image"); println!("- Repacking boot image");
// magiskboot repack boot.img // magiskboot repack boot.img
let status = Command::new(&magiskboot) let status = Command::new(&magiskboot)

View File

@@ -59,7 +59,7 @@ enum Commands {
#[arg(short, long)] #[arg(short, long)]
kernel: Option<PathBuf>, kernel: Option<PathBuf>,
/// LKM module path to replace /// LKM module path to replace, if not specified, will use the builtin one
#[arg(short, long)] #[arg(short, long)]
module: Option<PathBuf>, module: Option<PathBuf>,
@@ -82,6 +82,10 @@ enum Commands {
/// magiskboot path, if not specified, will use builtin one /// magiskboot path, if not specified, will use builtin one
#[arg(long, default_value = None)] #[arg(long, default_value = None)]
magiskboot: Option<PathBuf>, magiskboot: Option<PathBuf>,
/// KMI version, if specified, will use the specified KMI
#[arg(long, default_value = None)]
kmi: Option<String>,
}, },
/// For developers /// For developers
Debug { Debug {
@@ -316,7 +320,8 @@ pub fn run() -> Result<()> {
flash, flash,
out, out,
magiskboot, magiskboot,
} => crate::boot_patch::patch(boot, kernel, module, init, ota, flash, out, magiskboot), kmi,
} => crate::boot_patch::patch(boot, kernel, module, init, ota, flash, out, magiskboot, kmi),
}; };
if let Err(e) = &result { if let Err(e) = &result {