Refactor dtb in rust

This commit is contained in:
LoveSy 2023-09-19 00:12:49 -07:00 committed by John Wu
parent 8e1a91509c
commit 8d7c7c3dfb
14 changed files with 403 additions and 626 deletions

3
.gitmodules vendored
View File

@ -4,9 +4,6 @@
[submodule "busybox"]
path = native/src/external/busybox
url = https://github.com/topjohnwu/ndk-busybox.git
[submodule "dtc"]
path = native/src/external/dtc
url = https://github.com/dgibson/dtc.git
[submodule "lz4"]
path = native/src/external/lz4
url = https://github.com/lz4/lz4.git

View File

@ -99,7 +99,6 @@ LOCAL_STATIC_LIBRARIES := \
liblzma \
liblz4 \
libbz2 \
libfdt \
libz \
libzopfli \
libboot-rs
@ -109,7 +108,6 @@ LOCAL_SRC_FILES := \
boot/bootimg.cpp \
boot/compress.cpp \
boot/format.cpp \
boot/dtb.cpp \
boot/boot-rs.cpp
include $(BUILD_EXECUTABLE)

39
native/src/Cargo.lock generated
View File

@ -4,9 +4,9 @@ version = 3
[[package]]
name = "aho-corasick"
version = "1.0.5"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783"
checksum = "0f2135563fb5c609d2b2b87c1e8ce7bc41b0b45430fa9661f457981503dd5bf0"
dependencies = [
"memchr",
]
@ -324,6 +324,12 @@ dependencies = [
"termcolor",
]
[[package]]
name = "fdt"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67"
[[package]]
name = "ff"
version = "0.13.0"
@ -336,9 +342,9 @@ dependencies = [
[[package]]
name = "flagset"
version = "0.4.3"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda653ca797810c02f7ca4b804b40b8b95ae046eb989d356bce17919a8c25499"
checksum = "d52a7e408202050813e6f1d9addadcaafef3dca7530c7ddfb005d4081cce6779"
[[package]]
name = "generic-array"
@ -420,9 +426,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.147"
version = "0.2.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
[[package]]
name = "libm"
@ -461,6 +467,7 @@ dependencies = [
"cxx-gen",
"der",
"digest",
"fdt",
"p256",
"p384",
"pb-rs",
@ -654,9 +661,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.66"
version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
dependencies = [
"unicode-ident",
]
@ -883,9 +890,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "2.0.31"
version = "2.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398"
checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
dependencies = [
"proc-macro2",
"quote",
@ -894,9 +901,9 @@ dependencies = [
[[package]]
name = "termcolor"
version = "1.2.0"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64"
dependencies = [
"winapi-util",
]
@ -932,15 +939,15 @@ dependencies = [
[[package]]
name = "typenum"
version = "1.16.0"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.11"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-width"

View File

@ -22,6 +22,7 @@ rsa = "0.9"
x509-cert = "0.2"
der = "0.7"
bytemuck = "1.14"
fdt = "0.1"
[workspace.dependencies.argh]
git = "https://github.com/topjohnwu/argh.git"

View File

@ -26,3 +26,4 @@ p384 = { workspace = true }
rsa = { workspace = true, features = ["sha2"] }
x509-cert = { workspace = true }
der = { workspace = true, features = ["derive"] }
fdt = { workspace = true }

View File

@ -1,7 +1,7 @@
#include <bit>
#include <functional>
#include <memory>
#include <libfdt.h>
#include <base.hpp>
#include "boot-rs.hpp"
@ -183,29 +183,70 @@ boot_img::~boot_img() {
delete hdr;
}
struct [[gnu::packed]] fdt_header {
struct fdt32_t {
uint32_t byte0: 8;
uint32_t byte1: 8;
uint32_t byte2: 8;
uint32_t byte3: 8;
constexpr operator uint32_t() const {
return bit_cast<uint32_t>(fdt32_t {
.byte0 = byte3,
.byte1 = byte2,
.byte2 = byte1,
.byte3 = byte0
});
}
};
struct node_header {
fdt32_t tag;
char name[0];
};
fdt32_t magic; /* magic word FDT_MAGIC */
fdt32_t totalsize; /* total size of DT block */
fdt32_t off_dt_struct; /* offset to structure */
fdt32_t off_dt_strings; /* offset to strings */
fdt32_t off_mem_rsvmap; /* offset to memory reserve map */
fdt32_t version; /* format version */
fdt32_t last_comp_version; /* last compatible version */
/* version 2 fields below */
fdt32_t boot_cpuid_phys; /* Which physical CPU id we're
booting on */
/* version 3 fields below */
fdt32_t size_dt_strings; /* size of the strings block */
/* version 17 fields below */
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;
for (auto curr = buf; curr < end; curr += sizeof(fdt_header)) {
curr = static_cast<uint8_t*>(memmem(curr, end - curr, DTB_MAGIC, sizeof(fdt32_t)));
curr = static_cast<uint8_t*>(memmem(curr, end - curr, DTB_MAGIC, sizeof(fdt_header::fdt32_t)));
if (curr == nullptr)
return -1;
auto fdt_hdr = reinterpret_cast<const fdt_header *>(curr);
// Check that fdt_header.totalsize does not overflow kernel image size
uint32_t totalsize = fdt32_to_cpu(fdt_hdr->totalsize);
uint32_t totalsize = fdt_hdr->totalsize;
if (totalsize > end - curr)
continue;
// Check that fdt_header.off_dt_struct does not overflow kernel image size
uint32_t off_dt_struct = fdt32_to_cpu(fdt_hdr->off_dt_struct);
uint32_t off_dt_struct = fdt_hdr->off_dt_struct;
if (off_dt_struct > end - curr)
continue;
// Check that fdt_node_header.tag of first node is FDT_BEGIN_NODE
auto fdt_node_hdr = reinterpret_cast<const fdt_node_header *>(curr + off_dt_struct);
if (fdt32_to_cpu(fdt_node_hdr->tag) != FDT_BEGIN_NODE)
auto fdt_node_hdr = reinterpret_cast<const fdt_header::node_header *>(curr + off_dt_struct);
if (fdt_node_hdr->tag != 0x1u)
continue;
return curr - buf;

View File

@ -1,452 +0,0 @@
#include <bitset>
#include <vector>
#include <map>
#include <libfdt.h>
#include <base.hpp>
#include "magiskboot.hpp"
#include "dtb.hpp"
#include "format.hpp"
#include "boot-rs.hpp"
using namespace std;
constexpr int MAX_DEPTH = 32;
static bitset<MAX_DEPTH> depth_set;
static void pretty_node(int depth) {
if (depth == 0)
return;
for (int i = 0; i < depth - 1; ++i)
printf(depth_set[i] ? "" : " ");
printf(depth_set[depth - 1] ? "├── " : "└── ");
}
static void pretty_prop(int depth) {
for (int i = 0; i < depth; ++i)
printf(depth_set[i] ? "" : " ");
printf(depth_set[depth] ? "" : " ");
}
static void print_node(const void *fdt, int node = 0, int depth = 0) {
// Print node itself
pretty_node(depth);
printf("#%d: %s\n", node, fdt_get_name(fdt, node, nullptr));
// Print properties
depth_set[depth] = fdt_first_subnode(fdt, node) >= 0;
int prop;
fdt_for_each_property_offset(prop, fdt, node) {
pretty_prop(depth);
int size;
const char *name;
auto value = static_cast<const char *>(fdt_getprop_by_offset(fdt, prop, &name, &size));
bool is_str = !(size > 1 && value[0] == 0);
if (is_str) {
// Scan through value to see if printable
for (int i = 0; i < size; ++i) {
char c = value[i];
if (i == size - 1) {
// Make sure null terminate
is_str = c == '\0';
} else if ((c > 0 && c < 32) || c >= 127) {
is_str = false;
break;
}
}
}
if (is_str) {
printf("[%s]: [%s]\n", name, value);
} else {
printf("[%s]: <bytes>(%d)\n", name, size);
}
}
// Recursive
if (depth_set[depth]) {
int child;
int prev = -1;
fdt_for_each_subnode(child, fdt, node) {
if (prev >= 0)
print_node(fdt, prev, depth + 1);
prev = child;
}
depth_set[depth] = false;
print_node(fdt, prev, depth + 1);
}
}
static int find_fstab(const void *fdt, int node = 0) {
if (auto name = fdt_get_name(fdt, node, nullptr); name && name == "fstab"sv)
return node;
int child;
fdt_for_each_subnode(child, fdt, node) {
int fstab = find_fstab(fdt, child);
if (fstab >= 0)
return fstab;
}
return -1;
}
template<typename Func>
static void for_each_fdt(const char *file, bool rw, Func fn) {
mmap_data m(file, rw);
uint8_t *end = m.buf() + m.sz();
for (uint8_t *fdt = m.buf(); fdt < end;) {
fdt = static_cast<uint8_t*>(memmem(fdt, end - fdt, DTB_MAGIC, sizeof(fdt32_t)));
if (fdt == nullptr)
break;
fn(fdt);
fdt += fdt_totalsize(fdt);
}
}
static void dtb_print(const char *file, bool fstab) {
fprintf(stderr, "Loading dtbs from [%s]\n", file);
int dtb_num = 0;
for_each_fdt(file, false, [&](uint8_t *fdt) {
if (fstab) {
if (int node = find_fstab(fdt); node >= 0) {
fprintf(stderr, "Found fstab in dtb.%04d\n", dtb_num);
print_node(fdt, node);
}
} else {
fprintf(stderr, "Printing dtb.%04d\n", dtb_num);
print_node(fdt);
}
++dtb_num;
});
fprintf(stderr, "\n");
}
static bool dtb_patch(const char *file) {
fprintf(stderr, "Loading dtbs from [%s]\n", file);
bool keep_verity = check_env("KEEPVERITY");
bool patched = false;
for_each_fdt(file, true, [&](uint8_t *fdt) {
int node;
// Patch the chosen node for bootargs
fdt_for_each_subnode(node, fdt, 0) {
if (auto name = fdt_get_name(fdt, node, nullptr); !name || name != "chosen"sv)
continue;
int len;
if (auto value = fdt_getprop(fdt, node, "bootargs", &len)) {
if (void *skip = memmem(value, len, "skip_initramfs", 14)) {
fprintf(stderr, "Patch [skip_initramfs] -> [want_initramfs]\n");
memcpy(skip, "want", 4);
patched = true;
}
}
break;
}
if (!keep_verity) {
if (int fstab = find_fstab(fdt); fstab >= 0) {
fdt_for_each_subnode(node, fdt, fstab) {
int len;
char *value = (char *) fdt_getprop(fdt, node, "fsmgr_flags", &len);
byte_data data(value, len);
patched |= (patch_verity(data) != len);
}
}
}
});
return patched;
}
[[noreturn]]
static void dtb_test(const char *file) {
for_each_fdt(file, false, [&](uint8_t *fdt) {
// Find the system node in fstab
if (int fstab = find_fstab(fdt); fstab >= 0) {
int node;
fdt_for_each_subnode(node, fdt, fstab) {
if (auto name = fdt_get_name(fdt, node, nullptr); !name || name != "system"sv)
continue;
int len;
if (auto value = fdt_getprop(fdt, node, "mnt_point", &len)) {
// If mnt_point is set to /system_root, abort!
if (strncmp(static_cast<const char *>(value), "/system_root", len) == 0) {
exit(1);
}
}
}
}
});
exit(0);
}
int dtb_commands(int argc, char *argv[]) {
char *dtb = argv[0];
++argv;
--argc;
if (argv[0] == "print"sv) {
dtb_print(dtb, argc > 1 && argv[1] == "-f"sv);
return 0;
} else if (argv[0] == "patch"sv) {
if (!dtb_patch(dtb))
exit(1);
return 0;
} else if (argv[0] == "test"sv) {
dtb_test(dtb);
} else {
return 1;
}
}
// The following code is unused, left here for historical purpose. Since the code is
// extremely complicated, I won't want to rewrite this whole thing if somehow we need
// to use it in the future...
namespace {
struct fdt_blob {
void *fdt;
uint32_t offset;
uint32_t len;
};
static bool fdt_patch(void *fdt) {
int fstab = find_fstab(fdt);
if (fstab < 0)
return false;
bool modified = false;
int node;
fdt_for_each_subnode(node, fdt, fstab) {
const char *name = fdt_get_name(fdt, node, nullptr);
// Force remove AVB for 2SI since it may bootloop some devices
int len;
const void *value = fdt_getprop(fdt, node, "fsmgr_flags", &len);
heap_data copy = byte_view(value, len).clone();
auto patched_sz = patch_verity(copy);
if (patched_sz != len) {
modified = true;
fdt_setprop(fdt, node, "fsmgr_flags", copy.buf(), patched_sz);
}
if (name == "system"sv) {
fprintf(stderr, "Setting [mnt_point] to [/system_root]\n");
fdt_setprop_string(fdt, node, "mnt_point", "/system_root");
modified = true;
}
}
return modified;
}
#define MAX_FDT_GROWTH 256
template <class Table, class Header>
static bool dt_table_patch(const Header *hdr, const char *out) {
map<uint32_t, fdt_blob> dtb_map;
auto buf = reinterpret_cast<const uint8_t *>(hdr);
auto tables = reinterpret_cast<const Table *>(buf + sizeof(Header));
constexpr bool is_aosp = std::is_same_v<Header, dt_table_header>;
// AOSP DTB store ints in big endian
using endian_conv = uint32_t (*)(uint32_t);
endian_conv be_to_le;
endian_conv le_to_be;
if constexpr (is_aosp) {
be_to_le = fdt32_to_cpu;
le_to_be = cpu_to_fdt32;
} else {
be_to_le = le_to_be = [](uint32_t x) { return x; };
}
// Collect all dtbs
auto num_dtb = be_to_le(hdr->num_dtbs);
for (int i = 0; i < num_dtb; ++i) {
auto offset = be_to_le(tables[i].offset);
if (dtb_map.count(offset) == 0) {
auto blob = buf + offset;
uint32_t size = fdt_totalsize(blob);
auto fdt = malloc(size + MAX_FDT_GROWTH);
memcpy(fdt, blob, size);
fdt_open_into(fdt, fdt, size + MAX_FDT_GROWTH);
dtb_map[offset] = { fdt, offset };
}
}
if (dtb_map.empty())
return false;
// Patch fdt
bool modified = false;
for (auto &[_, blob] : dtb_map)
modified |= fdt_patch(blob.fdt);
if (!modified)
return false;
unlink(out);
int fd = xopen(out, O_RDWR | O_CREAT | O_CLOEXEC, 0644);
// This value is only used if AOSP DTB
uint32_t total_size = 0;
// Copy headers and tables
total_size += xwrite(fd, buf, dtb_map.begin()->first);
// mmap rw to patch table values retroactively
mmap_data m(fd, lseek(fd, 0, SEEK_CUR), true);
// Guess alignment using gcd
uint32_t align = 1;
if constexpr (!is_aosp) {
auto it = dtb_map.begin();
align = (it++)->first;
for (; it != dtb_map.end(); ++it)
align = binary_gcd(align, it->first);
}
// Write dtbs
for (auto &val : dtb_map) {
val.second.offset = lseek(fd, 0, SEEK_CUR);
auto fdt = val.second.fdt;
fdt_pack(fdt);
auto size = fdt_totalsize(fdt);
total_size += xwrite(fd, fdt, size);
if constexpr (!is_aosp) {
val.second.len = align_to(size, align);
write_zero(fd, align_padding(lseek(fd, 0, SEEK_CUR), align));
}
free(fdt);
}
// Patch headers
if constexpr (is_aosp) {
auto hdr_rw = reinterpret_cast<Header *>(m.buf());
hdr_rw->total_size = le_to_be(total_size);
}
auto tables_rw = reinterpret_cast<Table *>(m.buf() + sizeof(Header));
for (int i = 0; i < num_dtb; ++i) {
auto &blob = dtb_map[be_to_le(tables_rw[i].offset)];
tables_rw[i].offset = le_to_be(blob.offset);
tables_rw[i].len = le_to_be(blob.len);
}
close(fd);
return true;
}
static bool blob_patch(uint8_t *dtb, size_t dtb_sz, const char *out) {
vector<uint8_t *> fdt_list;
vector<uint32_t> padding_list;
uint8_t * const end = dtb + dtb_sz;
for (uint8_t *curr = dtb; curr < end;) {
curr = static_cast<uint8_t*>(memmem(curr, end - curr, DTB_MAGIC, sizeof(fdt32_t)));
if (curr == nullptr)
break;
auto len = fdt_totalsize(curr);
auto fdt = static_cast<uint8_t *>(malloc(len + MAX_FDT_GROWTH));
memcpy(fdt, curr, len);
fdt_pack(fdt);
uint32_t padding = len - fdt_totalsize(fdt);
padding_list.push_back(padding);
fdt_open_into(fdt, fdt, len + MAX_FDT_GROWTH);
fdt_list.push_back(fdt);
curr += len;
}
bool modified = false;
for (auto fdt : fdt_list)
modified |= fdt_patch(fdt);
if (!modified)
return false;
unlink(out);
int fd = xopen(out, O_WRONLY | O_CREAT | O_CLOEXEC, 0644);
for (int i = 0; i < fdt_list.size(); ++i) {
auto fdt = fdt_list[i];
fdt_pack(fdt);
// Only add padding back if it is anything meaningful
if (padding_list[i] > 4) {
auto len = fdt_totalsize(fdt);
fdt_set_totalsize(fdt, len + padding_list[i]);
}
xwrite(fd, fdt, fdt_totalsize(fdt));
free(fdt);
}
close(fd);
return true;
}
#define DTB_MATCH(s) BUFFER_MATCH(dtb, s)
[[maybe_unused]]
static bool dtb_patch_rebuild(uint8_t *dtb, size_t dtb_sz, const char *file) {
if (DTB_MATCH(QCDT_MAGIC)) {
auto hdr = reinterpret_cast<qcdt_hdr*>(dtb);
switch (hdr->version) {
case 1:
fprintf(stderr, "QCDT v1\n");
return dt_table_patch<qctable_v1>(hdr, file);
case 2:
fprintf(stderr, "QCDT v2\n");
return dt_table_patch<qctable_v2>(hdr, file);
case 3:
fprintf(stderr, "QCDT v3\n");
return dt_table_patch<qctable_v3>(hdr, file);
default:
return false;
}
} else if (DTB_MATCH(DTBH_MAGIC)) {
auto hdr = reinterpret_cast<dtbh_hdr *>(dtb);
switch (hdr->version) {
case 2:
fprintf(stderr, "DTBH v2\n");
return dt_table_patch<bhtable_v2>(hdr, file);
default:
return false;
}
} else if (DTB_MATCH(PXADT_MAGIC)) {
auto hdr = reinterpret_cast<pxadt_hdr *>(dtb);
switch (hdr->version) {
case 1:
fprintf(stderr, "PXA-DT v1\n");
return dt_table_patch<pxatable_v1>(hdr, file);
default:
return false;
}
} else if (DTB_MATCH(PXA19xx_MAGIC)) {
auto hdr = reinterpret_cast<pxa19xx_hdr *>(dtb);
switch (hdr->version) {
case 1:
fprintf(stderr, "PXA-19xx v1\n");
return dt_table_patch<pxatable_v1>(hdr, file);
default:
return false;
}
} else if (DTB_MATCH(SPRD_MAGIC)) {
auto hdr = reinterpret_cast<sprd_hdr *>(dtb);
switch (hdr->version) {
case 1:
fprintf(stderr, "SPRD v1\n");
return dt_table_patch<sprdtable_v1>(hdr, file);
default:
return false;
}
} else if (DTB_MATCH(DT_TABLE_MAGIC)) {
auto hdr = reinterpret_cast<dt_table_header *>(dtb);
switch (hdr->version) {
case 0:
fprintf(stderr, "DT_TABLE v0\n");
return dt_table_patch<dt_table_entry>(hdr, file);
default:
return false;
}
} else {
return blob_patch(dtb, dtb_sz, file);
}
}
} // namespace

View File

@ -1,103 +0,0 @@
#pragma once
#include <stdint.h>
#define DT_TABLE_MAGIC "\xd7\xb7\xab\x1e"
#define QCDT_MAGIC "QCDT"
#define DTBH_MAGIC "DTBH"
#define PXADT_MAGIC "PXA-DT"
#define PXA19xx_MAGIC "PXA-19xx"
#define SPRD_MAGIC "SPRD"
struct qcdt_hdr {
char magic[4]; /* "QCDT" */
uint32_t version; /* QCDT version */
uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed));
struct qctable_v1 {
uint32_t cpu_info[3]; /* Some CPU info */
uint32_t offset; /* DTB offset in QCDT */
uint32_t len; /* DTB size */
} __attribute__((packed));
struct qctable_v2 {
uint32_t cpu_info[4]; /* Some CPU info */
uint32_t offset; /* DTB offset in QCDT */
uint32_t len; /* DTB size */
} __attribute__((packed));
struct qctable_v3 {
uint32_t cpu_info[8]; /* Some CPU info */
uint32_t offset; /* DTB offset in QCDT */
uint32_t len; /* DTB size */
} __attribute__((packed));
struct dtbh_hdr {
char magic[4]; /* "DTBH" */
uint32_t version; /* DTBH version */
uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed));
struct bhtable_v2 {
uint32_t cpu_info[5]; /* Some CPU info */
uint32_t offset; /* DTB offset in DTBH */
uint32_t len; /* DTB size */
uint32_t space; /* 0x00000020 */
} __attribute__((packed));
struct pxadt_hdr {
char magic[6]; /* "PXA-DT" */
uint32_t version; /* PXA-* version */
uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed));
struct pxa19xx_hdr {
char magic[8]; /* "PXA-19xx" */
uint32_t version; /* PXA-* version */
uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed));
struct pxatable_v1 {
uint32_t cpu_info[2]; /* Some CPU info */
uint32_t offset; /* DTB offset in PXA-* */
uint32_t len; /* DTB size */
} __attribute__((packed));
struct sprd_hdr {
char magic[4]; /* "SPRD" */
uint32_t version; /* SPRD version */
uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed));
struct sprdtable_v1 {
uint32_t cpu_info[3]; /* Some CPU info */
uint32_t offset; /* DTB offset in SPRD */
uint32_t len; /* DTB size */
} __attribute__((packed));
/* AOSP DTB/DTBO partition layout */
struct dt_table_header {
uint32_t magic; /* DT_TABLE_MAGIC */
uint32_t total_size; /* includes dt_table_header + all dt_table_entry */
uint32_t header_size; /* sizeof(dt_table_header) */
uint32_t dt_entry_size; /* sizeof(dt_table_entry) */
uint32_t num_dtbs; /* number of dt_table_entry */
uint32_t dt_entries_offset; /* offset to the first dt_table_entry */
uint32_t page_size; /* flash page size we assume */
uint32_t version; /* DTBO image version */
} __attribute__((packed));
struct dt_table_entry {
uint32_t len; /* DTB size */
uint32_t offset;
uint32_t id;
uint32_t rev;
uint32_t flags;
uint32_t custom[3];
} __attribute__((packed));

316
native/src/boot/dtb.rs Normal file
View File

@ -0,0 +1,316 @@
use crate::{check_env, patch::patch_verity};
use argh::FromArgs;
use base::{
libc::c_char, log_err, map_args, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr,
};
use fdt::{
node::{FdtNode, NodeProperty},
Fdt,
};
use std::{cell::UnsafeCell, fmt::Write, process::exit};
#[derive(FromArgs)]
struct DtbCli {
#[argh(positional)]
file: String,
#[argh(subcommand)]
action: DtbAction,
}
#[derive(FromArgs)]
#[argh(subcommand)]
enum DtbAction {
Print(Print),
Patch(Patch),
Test(Test),
}
#[derive(FromArgs)]
#[argh(subcommand, name = "print")]
struct Print {
#[argh(switch, short = 'f')]
fstab: bool,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "patch")]
struct Patch {}
#[derive(FromArgs)]
#[argh(subcommand, name = "test")]
struct Test {}
fn print_dtb_usage() {
eprintln!(
r#"Usage: magiskboot dtb <file> <action> [args...]
Do dtb related actions to <file>.
Supported actions:
print [-f] Print all contents of dtb for debugging
Specify [-f] to only print fstab nodes
patch
Search for fstab and remove verity/avb
Modifications are done directly to the file in-place
Configure with env variables: KEEPVERITY
test
Test the fstab's status
Return values:
0:valid 1:error"#
);
}
const MAX_PRINT_LEN: usize = 32;
fn print_node(node: &FdtNode) {
fn pretty_node(depth_set: &Vec<bool>) {
let mut depth_set = depth_set.iter().peekable();
while let Some(depth) = depth_set.next() {
let last = depth_set.peek().is_none();
if *depth {
if last {
print!("├── ");
} else {
print!("");
}
} else {
if last {
print!("└── ");
} else {
print!(" ");
}
}
}
}
fn pretty_prop(depth_set: &Vec<bool>) {
let mut depth_set = depth_set.iter().peekable();
while let Some(depth) = depth_set.next() {
let last = depth_set.peek().is_none();
if *depth {
if last {
print!("");
} else {
print!("");
}
} else {
if last {
print!("└─ ");
} else {
print!(" ");
}
}
}
}
fn do_print_node(node: &FdtNode, depth_set: &mut Vec<bool>) {
pretty_node(depth_set);
let depth = depth_set.len();
depth_set.push(true);
println!("{}", node.name);
let mut properties = node.properties().peekable();
let mut children = node.children().peekable();
while let Some(NodeProperty { name, value }) = properties.next() {
let size = value.len();
let is_str = !(size > 1 && value[0] == 0)
&& matches!(value.last(), Some(0u8) | None)
&& value.iter().all(|c| *c == 0 || (*c >= 32 && *c < 127));
if depth_set[depth] && properties.peek().is_none() && children.peek().is_none() {
depth_set[depth] = false;
}
pretty_prop(depth_set);
if is_str {
println!(
"[{}]: [\"{}\"]",
name,
if value.is_empty() {
""
} else {
unsafe { Utf8CStr::from_bytes_unchecked(value) }
}
);
} else {
if size > MAX_PRINT_LEN {
println!("[{}]: <bytes>({})", name, size);
} else {
println!("[{}]: {:02x?}", name, value);
}
}
}
while let Some(child) = children.next() {
if depth_set[depth] && children.peek().is_none() {
depth_set[depth] = false;
}
do_print_node(&child, depth_set);
}
depth_set.pop();
}
do_print_node(node, &mut vec![]);
}
fn for_each_fdt<F: FnMut(usize, Fdt) -> LoggedResult<()>>(
file: &Utf8CStr,
rw: bool,
mut f: F,
) -> LoggedResult<()> {
eprintln!("Loading dtbs from [{}]", file);
let file = if rw {
MappedFile::open_rw(file)?
} else {
MappedFile::open(file)?
};
let mut buf = Some(file.as_ref());
let mut dtb_num = 0usize;
while let Some(slice) = buf {
let slice = if let Some(pos) = slice.windows(4).position(|w| w == b"\xd0\x0d\xfe\xed") {
&slice[pos..]
} else {
break;
};
if slice.len() < 40 {
break;
}
let fdt = Fdt::new(slice)?;
let size = fdt.total_size();
if size > slice.len() {
eprintln!("dtb.{:04} is truncated", dtb_num);
break;
}
f(dtb_num, fdt)?;
dtb_num += 1;
buf = Some(&slice[size..]);
}
Ok(())
}
fn find_fstab<'b, 'a: 'b>(fdt: &'b Fdt<'a>) -> Option<FdtNode<'b, 'a>> {
for node in fdt.all_nodes() {
if node.name == "fstab" {
return Some(node);
}
}
None
}
fn dtb_print(file: &Utf8CStr, fstab: bool) -> LoggedResult<()> {
for_each_fdt(file, false, |n, fdt| {
if fstab {
if let Some(fstab) = find_fstab(&fdt) {
eprintln!("Found fstab in dtb.{:04}", n);
print_node(&fstab);
}
} else {
if let Some(mut root) = fdt.find_node("/") {
eprintln!("Printing dtb.{:04}", n);
if root.name.is_empty() {
root.name = "/";
}
print_node(&root);
}
}
Ok(())
})
}
fn dtb_test(file: &Utf8CStr) -> LoggedResult<bool> {
let mut ret = true;
for_each_fdt(file, false, |_, fdt| {
if let Some(fstab) = find_fstab(&fdt) {
for child in fstab.children() {
if child.name != "system" {
continue;
}
if let Some(mount_point) = child.property("mnt_point") {
if mount_point.value == b"/system_root\0" {
ret = false;
break;
}
}
}
}
Ok(())
})?;
Ok(ret)
}
fn dtb_patch(file: &Utf8CStr) -> LoggedResult<bool> {
let keep_verity = check_env("KEEPVERITY");
let mut patched = false;
for_each_fdt(file, true, |n, fdt| {
for node in fdt.all_nodes() {
if node.name != "chosen" {
continue;
}
if let Some(boot_args) = node.property("bootargs") {
boot_args.value.windows(14).for_each(|w| {
if w == b"skip_initramfs" {
let w = unsafe {
&mut *std::mem::transmute::<&[u8], &UnsafeCell<[u8]>>(w).get()
};
w[..=4].copy_from_slice(b"want");
eprintln!("Patch [skip_initramfs] -> [want_initramfs] in dtb.{:04}", n);
patched = true;
}
});
}
}
if keep_verity {
return Ok(());
}
if let Some(fstab) = find_fstab(&fdt) {
for child in fstab.children() {
if let Some(flags) = child.property("fsmgr_flags") {
let flags = unsafe {
&mut *std::mem::transmute::<&[u8], &UnsafeCell<[u8]>>(flags.value).get()
};
if patch_verity(flags) != flags.len() {
patched = true;
}
}
}
}
Ok(())
})?;
Ok(patched)
}
pub fn dtb_commands(argc: i32, argv: *const *const c_char) -> bool {
fn inner(argc: i32, argv: *const *const c_char) -> LoggedResult<()> {
if argc < 1 {
return Err(log_err!("No arguments"));
}
let cmds = map_args(argc, argv)?;
let mut cli =
DtbCli::from_args(&["magiskboot", "dtb"], &cmds).on_early_exit(print_dtb_usage);
let file = Utf8CStr::from_string(&mut cli.file);
match cli.action {
DtbAction::Print(Print { fstab }) => {
dtb_print(file, fstab)?;
}
DtbAction::Test(_) => {
if !dtb_test(file)? {
exit(1);
}
}
DtbAction::Patch(_) => {
if !dtb_patch(file)? {
exit(1);
}
}
}
Ok(())
}
inner(argc, argv)
.log_with_msg(|w| w.write_str("Failed to process dtb"))
.is_ok()
}

View File

@ -3,11 +3,14 @@
pub use base;
use cpio::cpio_commands;
use patch::{hexpatch, patch_encryption, patch_verity};
use dtb::dtb_commands;
use patch::hexpatch;
use payload::extract_boot_from_payload;
use sign::{get_sha, sha1_hash, sha256_hash, sign_boot_image, verify_boot_image, SHA};
use std::env;
mod cpio;
mod dtb;
mod patch;
mod payload;
// Suppress warnings in generated code
@ -41,8 +44,6 @@ pub mod ffi {
fn sha256_hash(data: &[u8], out: &mut [u8]);
fn hexpatch(file: &[u8], from: &[u8], to: &[u8]) -> bool;
fn patch_encryption(buf: &mut [u8]) -> usize;
fn patch_verity(buf: &mut [u8]) -> usize;
}
#[namespace = "rust"]
@ -52,7 +53,6 @@ pub mod ffi {
in_path: *const c_char,
out_path: *const c_char,
) -> bool;
unsafe fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool;
unsafe fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool;
unsafe fn sign_boot_image(
@ -61,5 +61,11 @@ pub mod ffi {
cert: *const c_char,
key: *const c_char,
) -> Vec<u8>;
unsafe fn dtb_commands(argc: i32, argv: *const *const c_char) -> bool;
}
}
#[inline(always)]
pub(crate) fn check_env(env: &str) -> bool {
env::var(env).map_or(false, |var| var == "true")
}

View File

@ -76,19 +76,8 @@ Supported actions:
See "cpio --help" for supported commands.
dtb <file> <action> [args...]
Do dtb related actions to <file>
Supported actions:
print [-f]
Print all contents of dtb for debugging
Specify [-f] to only print fstab nodes
patch
Search for fstab and remove verity/avb
Modifications are done directly to the file in-place
Configure with env variables: KEEPVERITY
test
Test the fstab's status
Return values:
0:valid 1:error
Do dtb related actions to <file>.
See "dtb --help" for supported actions.
split <file>
Split image.*-dtb into kernel + kernel_dtb
@ -202,9 +191,8 @@ int main(int argc, char *argv[]) {
return hexpatch(byte_view(argv[2]), byte_view(argv[3]), byte_view(argv[4])) ? 0 : 1;
} else if (argc > 2 && action == "cpio") {
return rust::cpio_commands(argc - 2, argv + 2) ? 0 : 1;
} else if (argc > 3 && action == "dtb") {
if (dtb_commands(argc - 2, argv + 2))
usage(argv[0]);
} else if (argc > 2 && action == "dtb") {
return rust::dtb_commands(argc - 2, argv + 2) ? 0 : 1;
} else if (argc > 2 && action == "extract") {
return rust::extract_boot_from_payload(
argv[2],

View File

@ -1,11 +1,11 @@
use std::cmp::Ordering;
use std::collections::HashMap;
use std::env;
use std::str::from_utf8;
use base::libc::{S_IFDIR, S_IFMT, S_IFREG};
use base::{LoggedResult, Utf8CStr};
use crate::check_env;
use crate::cpio::{Cpio, CpioEntry};
use crate::patch::{patch_encryption, patch_verity};
@ -20,11 +20,6 @@ const MAGISK_PATCHED: i32 = 1 << 0;
const UNSUPPORTED_CPIO: i32 = 1 << 1;
const SONY_INIT: i32 = 1 << 2;
#[inline(always)]
fn check_env(env: &str) -> bool {
env::var(env).map_or(false, |var| var == "true")
}
impl MagiskCpio for Cpio {
fn patch(&mut self) {
let keep_verity = check_env("KEEPVERITY");

View File

@ -17,23 +17,6 @@ LOCAL_SRC_FILES := \
xz-embedded/xz_dec_stream.c
include $(BUILD_STATIC_LIBRARY)
# libfdt.a
include $(CLEAR_VARS)
LOCAL_MODULE:= libfdt
LOCAL_C_INCLUDES := $(LOCAL_PATH)/dtc/libfdt
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_SRC_FILES := \
dtc/libfdt/fdt.c \
dtc/libfdt/fdt_addresses.c \
dtc/libfdt/fdt_empty_tree.c \
dtc/libfdt/fdt_overlay.c \
dtc/libfdt/fdt_ro.c \
dtc/libfdt/fdt_rw.c \
dtc/libfdt/fdt_strerror.c \
dtc/libfdt/fdt_sw.c \
dtc/libfdt/fdt_wip.c
include $(BUILD_STATIC_LIBRARY)
# liblz4.a
include $(CLEAR_VARS)
LOCAL_MODULE := liblz4

@ -1 +0,0 @@
Subproject commit c0c2e115f82ed3bc5f9d3f9e5380f0f7e81a1c21