Support vendor boot unpack/repack

Fix #6460, close #6620
This commit is contained in:
topjohnwu 2024-07-30 04:00:12 -07:00
parent 3b27de3715
commit 5ac7dc0b37
9 changed files with 217 additions and 67 deletions

2
.gitignore vendored
View File

@ -13,7 +13,7 @@ native/out
# Android Studio / Gradle
*.iml
.gradle
.idea
/local.properties
/.idea
/build
/captures

View File

@ -7,6 +7,7 @@ use cfg_if::cfg_if;
use cxx::private::c_char;
use libc::mode_t;
use crate::files::map_file_at;
pub(crate) use crate::xwrap::*;
use crate::{
clone_attr, cstr, fclone_attr, fd_path, map_fd, map_file, slice_from_ptr, CxxResultExt,
@ -61,6 +62,14 @@ pub(crate) fn map_file_for_cxx(path: &Utf8CStr, rw: bool) -> &'static mut [u8] {
map_file(path, rw).log_cxx().unwrap_or(&mut [])
}
pub(crate) fn map_file_at_for_cxx(fd: RawFd, path: &Utf8CStr, rw: bool) -> &'static mut [u8] {
unsafe {
map_file_at(BorrowedFd::borrow_raw(fd), path, rw)
.log_cxx()
.unwrap_or(&mut [])
}
}
pub(crate) fn map_fd_for_cxx(fd: RawFd, sz: usize, rw: bool) -> &'static mut [u8] {
unsafe {
map_fd(BorrowedFd::borrow_raw(fd), sz, rw)

View File

@ -117,6 +117,14 @@ mmap_data::mmap_data(const char *name, bool rw) {
}
}
mmap_data::mmap_data(int dirfd, const char *name, bool rw) {
auto slice = rust::map_file_at(dirfd, name, rw);
if (!slice.empty()) {
_buf = slice.data();
_sz = slice.size();
}
}
mmap_data::mmap_data(int fd, size_t sz, bool rw) {
auto slice = rust::map_fd(fd, sz, rw);
if (!slice.empty()) {

View File

@ -26,6 +26,7 @@ struct mmap_data : public byte_data {
ALLOW_MOVE_ONLY(mmap_data)
explicit mmap_data(const char *name, bool rw = false);
mmap_data(int dirfd, const char *name, bool rw = false);
mmap_data(int fd, size_t sz, bool rw = false);
~mmap_data();
};

View File

@ -856,6 +856,14 @@ impl MappedFile {
Ok(MappedFile(map_file(path, true)?))
}
pub fn openat<T: AsFd>(dir: &T, path: &Utf8CStr) -> io::Result<MappedFile> {
Ok(MappedFile(map_file_at(dir.as_fd(), path, false)?))
}
pub fn openat_rw<T: AsFd>(dir: &T, path: &Utf8CStr) -> io::Result<MappedFile> {
Ok(MappedFile(map_file_at(dir.as_fd(), path, true)?))
}
pub fn create(fd: BorrowedFd, sz: usize, rw: bool) -> io::Result<MappedFile> {
Ok(MappedFile(map_fd(fd, sz, rw)?))
}
@ -888,6 +896,14 @@ extern "C" {
// We mark the returned slice static because it is valid until explicitly unmapped
pub(crate) fn map_file(path: &Utf8CStr, rw: bool) -> io::Result<&'static mut [u8]> {
unsafe { map_file_at(BorrowedFd::borrow_raw(libc::AT_FDCWD), path, rw) }
}
pub(crate) fn map_file_at(
dirfd: BorrowedFd,
path: &Utf8CStr,
rw: bool,
) -> io::Result<&'static mut [u8]> {
#[cfg(target_pointer_width = "64")]
const BLKGETSIZE64: u32 = 0x80081272;
@ -895,18 +911,22 @@ pub(crate) fn map_file(path: &Utf8CStr, rw: bool) -> io::Result<&'static mut [u8
const BLKGETSIZE64: u32 = 0x80041272;
let flag = if rw { O_RDWR } else { O_RDONLY };
let file = FsPath::from(path).open(flag | O_CLOEXEC)?;
let fd = unsafe {
OwnedFd::from_raw_fd(
libc::openat(dirfd.as_raw_fd(), path.as_ptr(), flag | O_CLOEXEC).check_os_err()?,
)
};
let attr = fd_get_attr(file.as_raw_fd())?;
let attr = fd_get_attr(fd.as_raw_fd())?;
let sz = if attr.is_block_device() {
let mut sz = 0_u64;
unsafe { ioctl(file.as_raw_fd(), BLKGETSIZE64, &mut sz) }.as_os_err()?;
unsafe { ioctl(fd.as_raw_fd(), BLKGETSIZE64, &mut sz) }.as_os_err()?;
sz
} else {
attr.st.st_size as u64
};
map_fd(file.as_fd(), sz as usize, rw)
map_fd(fd.as_fd(), sz as usize, rw)
}
pub(crate) fn map_fd(fd: BorrowedFd, sz: usize, rw: bool) -> io::Result<&'static mut [u8]> {

View File

@ -61,6 +61,8 @@ pub mod ffi {
fn fd_path_for_cxx(fd: i32, buf: &mut [u8]) -> isize;
#[cxx_name = "map_file"]
fn map_file_for_cxx(path: Utf8CStrRef, rw: bool) -> &'static mut [u8];
#[cxx_name = "map_file_at"]
fn map_file_at_for_cxx(fd: i32, path: Utf8CStrRef, rw: bool) -> &'static mut [u8];
#[cxx_name = "map_fd"]
fn map_fd_for_cxx(fd: i32, sz: usize, rw: bool) -> &'static mut [u8];
}

View File

@ -1,6 +1,7 @@
#include <bit>
#include <functional>
#include <memory>
#include <span>
#include <base.hpp>
@ -61,6 +62,8 @@ void dyn_img_hdr::print() const {
fprintf(stderr, "%-*s [%u]\n", PADDING, "RECOV_DTBO_SZ", recovery_dtbo_size());
if (ver == 2 || is_vendor())
fprintf(stderr, "%-*s [%u]\n", PADDING, "DTB_SZ", dtb_size());
if (ver == 4 && is_vendor())
fprintf(stderr, "%-*s [%u]\n", PADDING, "BOOTCONFIG_SZ", bootconfig_size());
if (uint32_t os_ver = os_version()) {
int a,b,c,y,m = 0;
@ -273,8 +276,7 @@ static format_t check_fmt_lg(const uint8_t *buf, unsigned sz) {
#define CMD_MATCH(s) BUFFER_MATCH(h->cmdline, s)
const pair<const uint8_t *, dyn_img_hdr *>
boot_img::create_hdr(const uint8_t *addr, format_t type) {
pair<const uint8_t *, dyn_img_hdr *> boot_img::create_hdr(const uint8_t *addr, format_t type) {
if (type == AOSP_VENDOR) {
fprintf(stderr, "VENDOR_BOOT_HDR\n");
auto h = reinterpret_cast<const boot_img_hdr_vnd_v3*>(addr);
@ -346,10 +348,23 @@ const pair<const uint8_t *, dyn_img_hdr *>
addr += ACCLAIM_PRE_HEADER_SZ;
}
// addr could be adjusted
return make_pair(addr, make_hdr(addr));
}
static const char *vendor_ramdisk_type(int type) {
switch (type) {
case VENDOR_RAMDISK_TYPE_PLATFORM:
return "platform";
case VENDOR_RAMDISK_TYPE_RECOVERY:
return "recovery";
case VENDOR_RAMDISK_TYPE_DLKM:
return "dlkm";
case VENDOR_RAMDISK_TYPE_NONE:
default:
return "none";
}
}
#define assert_off() \
if ((base_addr + off) > (map.buf() + map.sz())) { \
fprintf(stderr, "Corrupted boot image!\n"); \
@ -362,14 +377,6 @@ off += hdr->name##_size(); \
off = align_to(off, hdr->page_size()); \
assert_off();
#define get_ignore(name) \
if (hdr->name##_size()) { \
auto blk_sz = align_to(hdr->name##_size(), hdr->page_size()); \
off += blk_sz; \
} \
assert_off();
bool boot_img::parse_image(const uint8_t *p, format_t type) {
auto [base_addr, hdr] = create_hdr(p, type);
if (hdr == nullptr) {
@ -395,15 +402,12 @@ bool boot_img::parse_image(const uint8_t *p, format_t type) {
get_block(extra);
get_block(recovery_dtbo);
get_block(dtb);
auto ignore_addr = base_addr + off;
get_ignore(signature)
get_ignore(vendor_ramdisk_table)
get_ignore(bootconfig)
get_block(signature);
get_block(vendor_ramdisk_table);
get_block(bootconfig);
payload = byte_view(base_addr, off);
auto tail_addr = base_addr + off;
ignore = byte_view(ignore_addr, tail_addr - ignore_addr);
tail = byte_view(tail_addr, map.buf() + map.sz() - tail_addr);
if (auto size = hdr->kernel_size()) {
@ -458,24 +462,40 @@ bool boot_img::parse_image(const uint8_t *p, format_t type) {
fprintf(stderr, "%-*s [%s]\n", PADDING, "KERNEL_FMT", fmt2name[k_fmt]);
}
if (auto size = hdr->ramdisk_size()) {
if (hdr->is_vendor() && hdr->header_version() >= 4) {
if (vendor_ramdisk_table != nullptr) {
// v4 vendor boot contains multiple ramdisks
// Do not try to mess with it for now
r_fmt = UNKNOWN;
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_entry> table(
reinterpret_cast<table_entry *>(vendor_ramdisk_table),
hdr->vendor_ramdisk_table_entry_num());
for (auto &it : table) {
format_t 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",
it.ramdisk_name, vendor_ramdisk_type(it.ramdisk_type),
it.ramdisk_size, fmt2name[fmt]);
}
} else {
r_fmt = check_fmt_lg(ramdisk, size);
if (r_fmt == MTK) {
fprintf(stderr, "MTK_RAMDISK_HDR\n");
flags[MTK_RAMDISK] = true;
r_hdr = reinterpret_cast<const mtk_hdr *>(ramdisk);
fprintf(stderr, "%-*s [%u]\n", PADDING, "SIZE", r_hdr->size);
fprintf(stderr, "%-*s [%s]\n", PADDING, "NAME", r_hdr->name);
ramdisk += sizeof(mtk_hdr);
hdr->ramdisk_size() -= sizeof(mtk_hdr);
r_fmt = check_fmt_lg(ramdisk, hdr->ramdisk_size());
}
fprintf(stderr, "%-*s [%s]\n", PADDING, "RAMDISK_FMT", fmt2name[r_fmt]);
}
if (r_fmt == MTK) {
fprintf(stderr, "MTK_RAMDISK_HDR\n");
flags[MTK_RAMDISK] = true;
r_hdr = reinterpret_cast<const mtk_hdr *>(ramdisk);
fprintf(stderr, "%-*s [%u]\n", PADDING, "SIZE", r_hdr->size);
fprintf(stderr, "%-*s [%s]\n", PADDING, "NAME", r_hdr->name);
ramdisk += sizeof(mtk_hdr);
hdr->ramdisk_size() -= sizeof(mtk_hdr);
r_fmt = check_fmt_lg(ramdisk, hdr->ramdisk_size());
}
fprintf(stderr, "%-*s [%s]\n", PADDING, "RAMDISK_FMT", fmt2name[r_fmt]);
}
if (auto size = hdr->extra_size()) {
e_fmt = check_fmt_lg(extra, size);
@ -561,7 +581,30 @@ int unpack(const char *image, bool skip_decomp, bool hdr) {
dump(boot.kernel_dtb.buf(), boot.kernel_dtb.sz(), KER_DTB_FILE);
// Dump ramdisk
if (!skip_decomp && COMPRESSED(boot.r_fmt)) {
if (boot.vendor_ramdisk_table != nullptr) {
using table_entry = const vendor_ramdisk_table_entry_v4;
span<table_entry> table(
reinterpret_cast<table_entry *>(boot.vendor_ramdisk_table),
boot.hdr->vendor_ramdisk_table_entry_num());
xmkdir(VND_RAMDISK_DIR, 0755);
owned_fd dirfd = xopen(VND_RAMDISK_DIR, O_RDONLY | O_CLOEXEC);
for (auto &it : table) {
char file_name[40];
if (it.ramdisk_name[0] == '\0') {
strscpy(file_name, RAMDISK_FILE, sizeof(file_name));
} else {
ssprintf(file_name, sizeof(file_name), "%s.cpio", it.ramdisk_name);
}
owned_fd fd = xopenat(dirfd, file_name, O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, 0644);
format_t fmt = check_fmt_lg(boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
if (!skip_decomp && COMPRESSED(fmt)) {
decompress(fmt, fd, boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
} else {
xwrite(fd, boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
}
}
} else if (!skip_decomp && COMPRESSED(boot.r_fmt)) {
if (boot.hdr->ramdisk_size() != 0) {
int fd = creat(RAMDISK_FILE, 0644);
decompress(boot.r_fmt, fd, boot.ramdisk, boot.hdr->ramdisk_size());
@ -591,6 +634,9 @@ int unpack(const char *image, bool skip_decomp, bool hdr) {
// Dump dtb
dump(boot.dtb, boot.hdr->dtb_size(), DTB_FILE);
// Dump bootconfig
dump(boot.bootconfig, boot.hdr->bootconfig_size(), BOOTCONFIG_FILE);
return boot.flags[CHROMEOS_FLAG] ? 2 : 0;
}
@ -620,6 +666,7 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
hdr->ramdisk_size() = 0;
hdr->second_size() = 0;
hdr->dtb_size() = 0;
hdr->bootconfig_size() = 0;
if (access(HEADER_FILE, R_OK) == 0)
hdr->load_hdr_file();
@ -703,7 +750,40 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
// Copy MTK headers
xwrite(fd, boot.r_hdr, sizeof(mtk_hdr));
}
if (access(RAMDISK_FILE, R_OK) == 0) {
using table_entry = vendor_ramdisk_table_entry_v4;
vector<table_entry> ramdisk_table;
if (boot.vendor_ramdisk_table) {
// Create a copy so we can modify it
auto entry_start = reinterpret_cast<const table_entry *>(boot.vendor_ramdisk_table);
ramdisk_table.insert(
ramdisk_table.begin(),
entry_start, entry_start + boot.hdr->vendor_ramdisk_table_entry_num());
owned_fd dirfd = xopen(VND_RAMDISK_DIR, O_RDONLY | O_CLOEXEC);
uint32_t ramdisk_offset = 0;
for (auto &it : ramdisk_table) {
char file_name[64];
if (it.ramdisk_name[0] == '\0') {
strscpy(file_name, RAMDISK_FILE, sizeof(file_name));
} else {
ssprintf(file_name, sizeof(file_name), "%s.cpio", it.ramdisk_name);
}
mmap_data m(dirfd, file_name);
format_t fmt = check_fmt_lg(boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
it.ramdisk_offset = ramdisk_offset;
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(fmt)) {
it.ramdisk_size = compress(fmt, fd, m.buf(), m.sz());
} else {
it.ramdisk_size = xwrite(fd, m.buf(), m.sz());
}
ramdisk_offset += it.ramdisk_size;
}
hdr->ramdisk_size() = ramdisk_offset;
file_align();
} else if (access(RAMDISK_FILE, R_OK) == 0) {
mmap_data m(RAMDISK_FILE);
auto r_fmt = boot.r_fmt;
if (!skip_comp && !hdr->is_vendor() && hdr->header_version() == 4 && r_fmt != LZ4_LEGACY) {
@ -754,10 +834,22 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
file_align();
}
// Directly copy ignored blobs
if (boot.ignore.sz()) {
// ignore.sz() should already be aligned
xwrite(fd, boot.ignore.buf(), boot.ignore.sz());
// Copy boot signature
if (boot.hdr->signature_size()) {
xwrite(fd, boot.signature, boot.hdr->signature_size());
file_align();
}
// vendor ramdisk table
if (!ramdisk_table.empty()) {
xwrite(fd, ramdisk_table.data(), sizeof(table_entry) * ramdisk_table.size());
file_align();
}
// bootconfig
if (access(BOOTCONFIG_FILE, R_OK) == 0) {
hdr->bootconfig_size() = restore(fd, BOOTCONFIG_FILE);
file_align();
}
// Proprietary stuffs

View File

@ -1,6 +1,6 @@
#pragma once
#include <stdint.h>
#include <cstdint>
#include <utility>
#include <bitset>
#include <cxx.h>
@ -111,15 +111,21 @@ struct AvbVBMetaImageHeader {
#define VENDOR_RAMDISK_NAME_SIZE 32
#define VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE 16
/* When the boot image header has a version of 0 - 2, the structure of the boot
#define VENDOR_RAMDISK_TYPE_NONE 0
#define VENDOR_RAMDISK_TYPE_PLATFORM 1
#define VENDOR_RAMDISK_TYPE_RECOVERY 2
#define VENDOR_RAMDISK_TYPE_DLKM 3
/*
* When the boot image header has a version of 0 - 2, the structure of the boot
* image is as follows:
*
* +-----------------+
* | boot header | 1 page
* +-----------------+
* | kernel | n pages
* | kernel | m pages
* +-----------------+
* | ramdisk | m pages
* | ramdisk | n pages
* +-----------------+
* | second stage | o pages
* +-----------------+
@ -130,8 +136,8 @@ struct AvbVBMetaImageHeader {
* | dtb | q pages
* +-----------------+
*
* n = (kernel_size + page_size - 1) / page_size
* m = (ramdisk_size + page_size - 1) / page_size
* m = (kernel_size + page_size - 1) / page_size
* n = (ramdisk_size + page_size - 1) / page_size
* o = (second_size + page_size - 1) / page_size
* p = (recovery_dtbo_size + page_size - 1) / page_size
* q = (dtb_size + page_size - 1) / page_size
@ -211,7 +217,8 @@ struct boot_img_hdr_pxa : public boot_img_hdr_v0_common {
char extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
} __attribute__((packed));
/* When the boot image header has a version of 3 - 4, the structure of the boot
/*
* When the boot image header has a version of 3 - 4, the structure of the boot
* image is as follows:
*
* +---------------------+
@ -329,7 +336,7 @@ struct vendor_ramdisk_table_entry_v4 {
uint32_t ramdisk_size; /* size in bytes for the ramdisk image */
uint32_t ramdisk_offset; /* offset to the ramdisk image in vendor ramdisk section */
uint32_t ramdisk_type; /* type of the ramdisk */
uint8_t ramdisk_name[VENDOR_RAMDISK_NAME_SIZE]; /* asciiz ramdisk name */
char ramdisk_name[VENDOR_RAMDISK_NAME_SIZE]; /* asciiz ramdisk name */
// Hardware identifiers describing the board, soc or platform which this
// ramdisk is intended to be loaded on.
@ -376,8 +383,12 @@ struct dyn_img_hdr {
// v4 specific
decl_val(signature_size, 32)
// v4 vendor specific
decl_val(vendor_ramdisk_table_size, 32)
decl_val(bootconfig_size, 32)
decl_val(vendor_ramdisk_table_entry_num, 32)
decl_val(vendor_ramdisk_table_entry_size, 32)
decl_var(bootconfig_size, 32)
virtual ~dyn_img_hdr() {
free(raw);
@ -554,7 +565,9 @@ struct dyn_img_vnd_v4 : public dyn_img_vnd_v3 {
impl_cls(vnd_v4)
impl_val(vendor_ramdisk_table_size)
impl_val(bootconfig_size)
impl_val(vendor_ramdisk_table_entry_num)
impl_val(vendor_ramdisk_table_entry_size)
impl_var(bootconfig_size)
};
#undef __impl_cls
@ -591,7 +604,7 @@ struct boot_img {
const mmap_data map;
// Android image header
const dyn_img_hdr *hdr;
const dyn_img_hdr *hdr = nullptr;
// Flags to indicate the state of current boot image
std::bitset<BOOT_FLAGS_MAX> flags;
@ -607,9 +620,9 @@ struct boot_img {
// Layout of the memory mapped region
// +---------+
// | head | Vendor specific. Should be empty for standard AOSP boot images.
// | head | Vendor specific. Should not exist for standard AOSP boot images.
// +---------+
// | payload | The actual boot image, including the AOSP boot image header.
// | payload | The actual entire AOSP boot image, including the boot image header.
// +---------+
// | tail | Data after payload. Usually contains signature/AVB information.
// +---------+
@ -618,8 +631,8 @@ struct boot_img {
byte_view tail;
// MTK headers
const mtk_hdr *k_hdr;
const mtk_hdr *r_hdr;
const mtk_hdr *k_hdr = nullptr;
const mtk_hdr *r_hdr = nullptr;
// The pointers/values after parse_image
// +---------------+
@ -629,23 +642,26 @@ struct boot_img {
// +---------------+
// | z_info.tail | z_info.tail.sz()
// +---------------+
const zimage_hdr *z_hdr;
const zimage_hdr *z_hdr = nullptr;
struct {
uint32_t hdr_sz;
byte_view tail;
} z_info;
// AVB structs
const AvbFooter *avb_footer;
const AvbVBMetaImageHeader *vbmeta;
const AvbFooter *avb_footer = nullptr;
const AvbVBMetaImageHeader *vbmeta = nullptr;
// Pointers to blocks defined in header
const uint8_t *kernel;
const uint8_t *ramdisk;
const uint8_t *second;
const uint8_t *extra;
const uint8_t *recovery_dtbo;
const uint8_t *dtb;
const uint8_t *kernel = nullptr;
const uint8_t *ramdisk = nullptr;
const uint8_t *second = nullptr;
const uint8_t *extra = nullptr;
const uint8_t *recovery_dtbo = nullptr;
const uint8_t *dtb = nullptr;
const uint8_t *signature = nullptr;
const uint8_t *vendor_ramdisk_table = nullptr;
const uint8_t *bootconfig = nullptr;
// dtb embedded in kernel
byte_view kernel_dtb;
@ -657,7 +673,7 @@ struct boot_img {
~boot_img();
bool parse_image(const uint8_t *addr, format_t type);
const std::pair<const uint8_t *, dyn_img_hdr *> create_hdr(const uint8_t *addr, format_t type);
std::pair<const uint8_t *, dyn_img_hdr *> create_hdr(const uint8_t *addr, format_t type);
// Rust FFI
rust::Slice<const uint8_t> get_payload() const { return payload; }

View File

@ -7,11 +7,13 @@
#define HEADER_FILE "header"
#define KERNEL_FILE "kernel"
#define RAMDISK_FILE "ramdisk.cpio"
#define VND_RAMDISK_DIR "vendor_ramdisk"
#define SECOND_FILE "second"
#define EXTRA_FILE "extra"
#define KER_DTB_FILE "kernel_dtb"
#define RECV_DTBO_FILE "recovery_dtbo"
#define DTB_FILE "dtb"
#define BOOTCONFIG_FILE "bootconfig"
#define NEW_BOOT "new-boot.img"
int unpack(const char *image, bool skip_decomp = false, bool hdr = false);