Home | History | Annotate | Download | only in bootloader_message
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <bootloader_message/bootloader_message.h>
     18 
     19 #include <errno.h>
     20 #include <fcntl.h>
     21 #include <string.h>
     22 
     23 #include <string>
     24 #include <string_view>
     25 #include <vector>
     26 
     27 #include <android-base/file.h>
     28 #include <android-base/properties.h>
     29 #include <android-base/stringprintf.h>
     30 #include <android-base/unique_fd.h>
     31 #include <fstab/fstab.h>
     32 
     33 using android::fs_mgr::Fstab;
     34 using android::fs_mgr::ReadDefaultFstab;
     35 
     36 static std::string g_misc_device_for_test;
     37 
     38 // Exposed for test purpose.
     39 void SetMiscBlockDeviceForTest(std::string_view misc_device) {
     40   g_misc_device_for_test = misc_device;
     41 }
     42 
     43 static std::string get_misc_blk_device(std::string* err) {
     44   if (!g_misc_device_for_test.empty()) {
     45     return g_misc_device_for_test;
     46   }
     47   Fstab fstab;
     48   if (!ReadDefaultFstab(&fstab)) {
     49     *err = "failed to read default fstab";
     50     return "";
     51   }
     52   for (const auto& entry : fstab) {
     53     if (entry.mount_point == "/misc") {
     54       return entry.blk_device;
     55     }
     56   }
     57 
     58   *err = "failed to find /misc partition";
     59   return "";
     60 }
     61 
     62 // In recovery mode, recovery can get started and try to access the misc
     63 // device before the kernel has actually created it.
     64 static bool wait_for_device(const std::string& blk_device, std::string* err) {
     65   int tries = 0;
     66   int ret;
     67   err->clear();
     68   do {
     69     ++tries;
     70     struct stat buf;
     71     ret = stat(blk_device.c_str(), &buf);
     72     if (ret == -1) {
     73       *err += android::base::StringPrintf("failed to stat %s try %d: %s\n",
     74                                           blk_device.c_str(), tries, strerror(errno));
     75       sleep(1);
     76     }
     77   } while (ret && tries < 10);
     78 
     79   if (ret) {
     80     *err += android::base::StringPrintf("failed to stat %s\n", blk_device.c_str());
     81   }
     82   return ret == 0;
     83 }
     84 
     85 static bool read_misc_partition(void* p, size_t size, const std::string& misc_blk_device,
     86                                 size_t offset, std::string* err) {
     87   if (!wait_for_device(misc_blk_device, err)) {
     88     return false;
     89   }
     90   android::base::unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY));
     91   if (fd == -1) {
     92     *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
     93                                        strerror(errno));
     94     return false;
     95   }
     96   if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
     97     *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
     98                                        strerror(errno));
     99     return false;
    100   }
    101   if (!android::base::ReadFully(fd, p, size)) {
    102     *err = android::base::StringPrintf("failed to read %s: %s", misc_blk_device.c_str(),
    103                                        strerror(errno));
    104     return false;
    105   }
    106   return true;
    107 }
    108 
    109 static bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
    110                                  size_t offset, std::string* err) {
    111   android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY));
    112   if (fd == -1) {
    113     *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
    114                                        strerror(errno));
    115     return false;
    116   }
    117   if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
    118     *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
    119                                        strerror(errno));
    120     return false;
    121   }
    122   if (!android::base::WriteFully(fd, p, size)) {
    123     *err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(),
    124                                        strerror(errno));
    125     return false;
    126   }
    127   if (fsync(fd) == -1) {
    128     *err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(),
    129                                        strerror(errno));
    130     return false;
    131   }
    132   return true;
    133 }
    134 
    135 std::string get_bootloader_message_blk_device(std::string* err) {
    136   std::string misc_blk_device = get_misc_blk_device(err);
    137   if (misc_blk_device.empty()) return "";
    138   if (!wait_for_device(misc_blk_device, err)) return "";
    139   return misc_blk_device;
    140 }
    141 
    142 bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
    143                                   std::string* err) {
    144   return read_misc_partition(boot, sizeof(*boot), misc_blk_device,
    145                              BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
    146 }
    147 
    148 bool read_bootloader_message(bootloader_message* boot, std::string* err) {
    149   std::string misc_blk_device = get_misc_blk_device(err);
    150   if (misc_blk_device.empty()) {
    151     return false;
    152   }
    153   return read_bootloader_message_from(boot, misc_blk_device, err);
    154 }
    155 
    156 bool write_bootloader_message_to(const bootloader_message& boot, const std::string& misc_blk_device,
    157                                  std::string* err) {
    158   return write_misc_partition(&boot, sizeof(boot), misc_blk_device,
    159                               BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
    160 }
    161 
    162 bool write_bootloader_message(const bootloader_message& boot, std::string* err) {
    163   std::string misc_blk_device = get_misc_blk_device(err);
    164   if (misc_blk_device.empty()) {
    165     return false;
    166   }
    167   return write_bootloader_message_to(boot, misc_blk_device, err);
    168 }
    169 
    170 bool clear_bootloader_message(std::string* err) {
    171   bootloader_message boot = {};
    172   return write_bootloader_message(boot, err);
    173 }
    174 
    175 bool write_bootloader_message(const std::vector<std::string>& options, std::string* err) {
    176   bootloader_message boot = {};
    177   update_bootloader_message_in_struct(&boot, options);
    178 
    179   return write_bootloader_message(boot, err);
    180 }
    181 
    182 bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
    183   bootloader_message boot;
    184   if (!read_bootloader_message(&boot, err)) {
    185     return false;
    186   }
    187   update_bootloader_message_in_struct(&boot, options);
    188 
    189   return write_bootloader_message(boot, err);
    190 }
    191 
    192 bool update_bootloader_message_in_struct(bootloader_message* boot,
    193                                          const std::vector<std::string>& options) {
    194   if (!boot) return false;
    195   // Replace the command & recovery fields.
    196   memset(boot->command, 0, sizeof(boot->command));
    197   memset(boot->recovery, 0, sizeof(boot->recovery));
    198 
    199   strlcpy(boot->command, "boot-recovery", sizeof(boot->command));
    200   strlcpy(boot->recovery, "recovery\n", sizeof(boot->recovery));
    201   for (const auto& s : options) {
    202     strlcat(boot->recovery, s.c_str(), sizeof(boot->recovery));
    203     if (s.back() != '\n') {
    204       strlcat(boot->recovery, "\n", sizeof(boot->recovery));
    205     }
    206   }
    207   return true;
    208 }
    209 
    210 bool write_reboot_bootloader(std::string* err) {
    211   bootloader_message boot;
    212   if (!read_bootloader_message(&boot, err)) {
    213     return false;
    214   }
    215   if (boot.command[0] != '\0') {
    216     *err = "Bootloader command pending.";
    217     return false;
    218   }
    219   strlcpy(boot.command, "bootonce-bootloader", sizeof(boot.command));
    220   return write_bootloader_message(boot, err);
    221 }
    222 
    223 bool read_wipe_package(std::string* package_data, size_t size, std::string* err) {
    224   std::string misc_blk_device = get_misc_blk_device(err);
    225   if (misc_blk_device.empty()) {
    226     return false;
    227   }
    228   package_data->resize(size);
    229   return read_misc_partition(&(*package_data)[0], size, misc_blk_device,
    230                              WIPE_PACKAGE_OFFSET_IN_MISC, err);
    231 }
    232 
    233 bool write_wipe_package(const std::string& package_data, std::string* err) {
    234   std::string misc_blk_device = get_misc_blk_device(err);
    235   if (misc_blk_device.empty()) {
    236     return false;
    237   }
    238   return write_misc_partition(package_data.data(), package_data.size(), misc_blk_device,
    239                               WIPE_PACKAGE_OFFSET_IN_MISC, err);
    240 }
    241 
    242 static bool OffsetAndSizeInVendorSpace(size_t offset, size_t size) {
    243   auto total_size = WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC;
    244   return size <= total_size && offset <= total_size - size;
    245 }
    246 
    247 bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::string* err) {
    248   if (!OffsetAndSizeInVendorSpace(offset, size)) {
    249     *err = android::base::StringPrintf("Out of bound read (offset %zu size %zu)", offset, size);
    250     return false;
    251   }
    252   auto misc_blk_device = get_misc_blk_device(err);
    253   if (misc_blk_device.empty()) {
    254     return false;
    255   }
    256   return read_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset,
    257                              err);
    258 }
    259 
    260 bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err) {
    261   if (!OffsetAndSizeInVendorSpace(offset, size)) {
    262     *err = android::base::StringPrintf("Out of bound write (offset %zu size %zu)", offset, size);
    263     return false;
    264   }
    265   auto misc_blk_device = get_misc_blk_device(err);
    266   if (misc_blk_device.empty()) {
    267     return false;
    268   }
    269   return write_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset,
    270                               err);
    271 }
    272 
    273 extern "C" bool write_reboot_bootloader(void) {
    274   std::string err;
    275   return write_reboot_bootloader(&err);
    276 }
    277 
    278 extern "C" bool write_bootloader_message(const char* options) {
    279   std::string err;
    280   return write_bootloader_message({options}, &err);
    281 }
    282