From 7ae01698424cc3adf635c324961b1405594f5156 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 16 May 2019 14:42:42 -0700 Subject: Add misc_writer. bootloader_message.h currently divides /misc into four segments. The space between 2K and 16K is reserved for vendor use (e.g. bootloader persists flags). This CL adds a vendor tool "misc_writer", to allow writing data to the vendor space in /misc, before getting a dedicated HAL for accessing /misc partition (b/131775112). Targets need to explicitly include the module, then invoke the executable to write data. For example, the following command will write 3-byte data ("0xABCDEF") to offset 4 in vendor space (i.e. 2048 + 4 in /misc). $ /vendor/bin/misc_writer --vendor-space-offset 4 --hex-string 0xABCDEF Bug: 132906936 Test: Run recovery_unit_test on crosshatch. Test: Call the command via init.hardware.rc on crosshatch. Check that the call finishes successfully. Then check the contents written to /misc (`dd bs=1 skip=2048 if=/dev/block/sda2 count=32 | xxd`). Change-Id: I79548fc63fc79b705a0320868690569c3106949f --- bootloader_message/Android.bp | 34 ++++--- bootloader_message/bootloader_message.cpp | 42 ++++++++ .../bootloader_message/bootloader_message.h | 13 ++- misc_writer/Android.bp | 40 ++++++++ misc_writer/misc_writer.cpp | 106 +++++++++++++++++++++ tests/unit/bootloader_message_test.cpp | 38 ++++++++ 6 files changed, 259 insertions(+), 14 deletions(-) create mode 100644 misc_writer/Android.bp create mode 100644 misc_writer/misc_writer.cpp diff --git a/bootloader_message/Android.bp b/bootloader_message/Android.bp index e76305fd7..8d72a1163 100644 --- a/bootloader_message/Android.bp +++ b/bootloader_message/Android.bp @@ -14,10 +14,8 @@ // limitations under the License. // -cc_library { - name: "libbootloader_message", - recovery_available: true, - host_supported: true, +cc_defaults { + name: "libbootloader_message_defaults", srcs: ["bootloader_message.cpp"], cflags: [ "-Wall", @@ -26,24 +24,36 @@ cc_library { shared_libs: [ "libbase", ], + static_libs: [ + "libfstab", + ], export_include_dirs: ["include"], +} + +cc_library { + name: "libbootloader_message", + defaults: [ + "libbootloader_message_defaults", + ], + recovery_available: true, + host_supported: true, target: { - android: { - shared_libs: [ - "libfs_mgr", - ], - }, host: { shared_libs: [ "libcutils", // for strlcpy ], - static_libs: [ - "libfstab", - ], }, darwin: { enabled: false, }, } } + +cc_library_static { + name: "libbootloader_message_vendor", + defaults: [ + "libbootloader_message_defaults", + ], + vendor: true, +} diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp index 331a42b2a..e684abbad 100644 --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -36,7 +37,17 @@ using android::fs_mgr::Fstab; using android::fs_mgr::ReadDefaultFstab; +static std::string g_misc_device_for_test; + +// Exposed for test purpose. +void SetMiscBlockDeviceForTest(std::string_view misc_device) { + g_misc_device_for_test = misc_device; +} + static std::string get_misc_blk_device(std::string* err) { + if (!g_misc_device_for_test.empty()) { + return g_misc_device_for_test; + } Fstab fstab; if (!ReadDefaultFstab(&fstab)) { *err = "failed to read default fstab"; @@ -242,6 +253,37 @@ bool write_wipe_package(const std::string& package_data, std::string* err) { WIPE_PACKAGE_OFFSET_IN_MISC, err); } +static bool OffsetAndSizeInVendorSpace(size_t offset, size_t size) { + auto total_size = WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC; + return size <= total_size && offset <= total_size - size; +} + +bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::string* err) { + if (!OffsetAndSizeInVendorSpace(offset, size)) { + *err = android::base::StringPrintf("Out of bound read (offset %zu size %zu)", offset, size); + return false; + } + auto misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + return read_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset, + err); +} + +bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err) { + if (!OffsetAndSizeInVendorSpace(offset, size)) { + *err = android::base::StringPrintf("Out of bound write (offset %zu size %zu)", offset, size); + return false; + } + auto misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + return write_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset, + err); +} + extern "C" bool write_reboot_bootloader(void) { std::string err; return write_reboot_bootloader(&err); diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index 2207d4cb3..5c0a450fc 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -28,8 +28,9 @@ // 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices // Note that these offsets are admitted by bootloader,recovery and uncrypt, so they // are not configurable without changing all of them. -static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0; -static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024; +constexpr size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0; +constexpr size_t VENDOR_SPACE_OFFSET_IN_MISC = 2 * 1024; +constexpr size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024; /* Bootloader Message (2-KiB) * @@ -233,6 +234,14 @@ bool read_wipe_package(std::string* package_data, size_t size, std::string* err) // Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC). bool write_wipe_package(const std::string& package_data, std::string* err); +// Reads data from the vendor space in /misc partition, with the given offset and size. Note that +// offset is in relative to the start of vendor space. +bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::string* err); + +// Writes the given data to the vendor space in /misc partition, at the given offset. Note that +// offset is in relative to the start of the vendor space. +bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err); + #else #include diff --git a/misc_writer/Android.bp b/misc_writer/Android.bp new file mode 100644 index 000000000..567143c79 --- /dev/null +++ b/misc_writer/Android.bp @@ -0,0 +1,40 @@ +// +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_binary { + name: "misc_writer", + vendor: true, + + srcs: [ + "misc_writer.cpp", + ], + + cpp_std: "experimental", + + cflags: [ + "-Wall", + "-Werror", + ], + + shared_libs: [ + "libbase", + ], + + static_libs: [ + "libbootloader_message_vendor", + "libfstab", + ], +} diff --git a/misc_writer/misc_writer.cpp b/misc_writer/misc_writer.cpp new file mode 100644 index 000000000..1d9702ebf --- /dev/null +++ b/misc_writer/misc_writer.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +using namespace std::string_literals; + +static std::vector ParseHexString(std::string_view hex_string) { + auto length = hex_string.size(); + if (length % 2 != 0 || length == 0) { + return {}; + } + + std::vector result(length / 2); + for (size_t i = 0; i < length / 2; i++) { + auto sub = "0x" + std::string(hex_string.substr(i * 2, 2)); + if (!android::base::ParseUint(sub, &result[i])) { + return {}; + } + } + return result; +} + +static int Usage(std::string_view name) { + std::cerr << name << " usage:\n"; + std::cerr << name << " [--vendor-space-offset ] --hex-string 0xABCDEF\n"; + std::cerr << "Writes the given hex string to the specified offset in vendor space in /misc " + "partition. Offset defaults to 0 if unspecified.\n"; + return EXIT_FAILURE; +} + +// misc_writer is a vendor tool that writes data to the vendor space in /misc. +int main(int argc, char** argv) { + constexpr struct option OPTIONS[] = { + { "vendor-space-offset", required_argument, nullptr, 0 }, + { "hex-string", required_argument, nullptr, 0 }, + { nullptr, 0, nullptr, 0 }, + }; + + // Offset defaults to 0 if unspecified. + size_t offset = 0; + std::string_view hex_string; + + int arg; + int option_index; + while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) { + if (arg != 0) { + LOG(ERROR) << "Invalid command argument"; + return Usage(argv[0]); + } + auto option_name = OPTIONS[option_index].name; + if (option_name == "vendor-space-offset"s) { + if (!android::base::ParseUint(optarg, &offset)) { + LOG(ERROR) << "Failed to parse the offset: " << optarg; + return Usage(argv[0]); + } + } else if (option_name == "hex-string"s) { + hex_string = optarg; + } + } + + if (hex_string.starts_with("0x") || hex_string.starts_with("0X")) { + hex_string = hex_string.substr(2); + } + if (hex_string.empty()) { + LOG(ERROR) << "Invalid input hex string: " << hex_string; + return Usage(argv[0]); + } + + auto data = ParseHexString(hex_string); + if (data.empty()) { + LOG(ERROR) << "Failed to parse the input hex string: " << hex_string; + return EXIT_FAILURE; + } + if (std::string err; !WriteMiscPartitionVendorSpace(data.data(), data.size(), offset, &err)) { + LOG(ERROR) << "Failed to write to misc partition: " << err; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/tests/unit/bootloader_message_test.cpp b/tests/unit/bootloader_message_test.cpp index b005d199c..95d875e69 100644 --- a/tests/unit/bootloader_message_test.cpp +++ b/tests/unit/bootloader_message_test.cpp @@ -15,6 +15,7 @@ */ #include +#include #include #include @@ -22,6 +23,10 @@ #include #include +using namespace std::string_literals; + +extern void SetMiscBlockDeviceForTest(std::string_view misc_device); + TEST(BootloaderMessageTest, read_and_write_bootloader_message) { TemporaryFile temp_misc; @@ -114,3 +119,36 @@ TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_long) { std::string(boot.reserved, sizeof(boot.reserved))); } +TEST(BootloaderMessageTest, WriteMiscPartitionVendorSpace) { + TemporaryFile temp_misc; + ASSERT_TRUE(android::base::WriteStringToFile(std::string(4096, '\x00'), temp_misc.path)); + SetMiscBlockDeviceForTest(temp_misc.path); + + constexpr std::string_view kTestMessage = "kTestMessage"; + std::string err; + ASSERT_TRUE(WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), 0, &err)); + + std::string message; + message.resize(kTestMessage.size()); + ASSERT_TRUE(ReadMiscPartitionVendorSpace(message.data(), message.size(), 0, &err)); + ASSERT_EQ(kTestMessage, message); + + // Write with an offset. + ASSERT_TRUE(WriteMiscPartitionVendorSpace("\x00\x00", 2, 5, &err)); + ASSERT_TRUE(ReadMiscPartitionVendorSpace(message.data(), message.size(), 0, &err)); + ASSERT_EQ("kTest\x00\x00ssage"s, message); + + // Write with the right size. + auto start_offset = + WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC - kTestMessage.size(); + ASSERT_TRUE( + WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), start_offset, &err)); + + // Out-of-bound write. + ASSERT_FALSE(WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), + start_offset + 1, &err)); + + // Message won't fit. + std::string long_message(WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC + 1, 'a'); + ASSERT_FALSE(WriteMiscPartitionVendorSpace(long_message.data(), long_message.size(), 0, &err)); +} -- cgit v1.2.3