Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 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 "init_first_stage.h" |
| 18 | |
| 19 | #include <stdlib.h> |
| 20 | #include <unistd.h> |
| 21 | |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 22 | #include <chrono> |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 23 | #include <memory> |
| 24 | #include <set> |
| 25 | #include <string> |
| 26 | #include <vector> |
| 27 | |
Tom Cherry | ede0d53 | 2017-07-06 14:20:11 -0700 | [diff] [blame] | 28 | #include <android-base/chrono_utils.h> |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 29 | #include <android-base/file.h> |
| 30 | #include <android-base/logging.h> |
| 31 | #include <android-base/strings.h> |
| 32 | |
| 33 | #include "devices.h" |
| 34 | #include "fs_mgr.h" |
| 35 | #include "fs_mgr_avb.h" |
Tom Cherry | ed506f7 | 2017-05-25 15:58:59 -0700 | [diff] [blame] | 36 | #include "uevent.h" |
| 37 | #include "uevent_listener.h" |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 38 | #include "util.h" |
| 39 | |
Tom Cherry | ede0d53 | 2017-07-06 14:20:11 -0700 | [diff] [blame] | 40 | using android::base::Timer; |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 41 | |
Tom Cherry | 81f5d3e | 2017-06-22 12:53:17 -0700 | [diff] [blame] | 42 | namespace android { |
| 43 | namespace init { |
| 44 | |
Bowgo Tsai | fd18a45 | 2017-04-24 18:36:25 +0800 | [diff] [blame] | 45 | // Class Declarations |
| 46 | // ------------------ |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 47 | class FirstStageMount { |
| 48 | public: |
| 49 | FirstStageMount(); |
| 50 | virtual ~FirstStageMount() = default; |
| 51 | |
| 52 | // The factory method to create either FirstStageMountVBootV1 or FirstStageMountVBootV2 |
| 53 | // based on device tree configurations. |
| 54 | static std::unique_ptr<FirstStageMount> Create(); |
| 55 | bool DoFirstStageMount(); // Mounts fstab entries read from device tree. |
Bowgo Tsai | fd18a45 | 2017-04-24 18:36:25 +0800 | [diff] [blame] | 56 | bool InitDevices(); |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 57 | |
| 58 | protected: |
Greg Hartman | 8864749 | 2018-02-05 13:48:11 -0800 | [diff] [blame] | 59 | ListenerAction HandleBlockDevice(const std::string& name, const Uevent&); |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 60 | bool InitRequiredDevices(); |
| 61 | bool InitVerityDevice(const std::string& verity_device); |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 62 | bool MountPartitions(); |
| 63 | |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 64 | virtual ListenerAction UeventCallback(const Uevent& uevent); |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 65 | |
| 66 | // Pure virtual functions. |
| 67 | virtual bool GetRequiredDevices() = 0; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 68 | virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0; |
| 69 | |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 70 | bool need_dm_verity_; |
Bowgo Tsai | 06ed613 | 2017-06-08 10:43:41 +0800 | [diff] [blame] | 71 | |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 72 | std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 73 | std::vector<fstab_rec*> mount_fstab_recs_; |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 74 | std::set<std::string> required_devices_partition_names_; |
Bowgo Tsai | 5ee7dae | 2018-05-16 18:33:44 +0800 | [diff] [blame] | 75 | std::unique_ptr<DeviceHandler> device_handler_; |
Tom Cherry | ed506f7 | 2017-05-25 15:58:59 -0700 | [diff] [blame] | 76 | UeventListener uevent_listener_; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 77 | }; |
| 78 | |
| 79 | class FirstStageMountVBootV1 : public FirstStageMount { |
| 80 | public: |
| 81 | FirstStageMountVBootV1() = default; |
| 82 | ~FirstStageMountVBootV1() override = default; |
| 83 | |
| 84 | protected: |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 85 | bool GetRequiredDevices() override; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 86 | bool SetUpDmVerity(fstab_rec* fstab_rec) override; |
| 87 | }; |
| 88 | |
| 89 | class FirstStageMountVBootV2 : public FirstStageMount { |
| 90 | public: |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 91 | friend void SetInitAvbVersionInRecovery(); |
| 92 | |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 93 | FirstStageMountVBootV2(); |
| 94 | ~FirstStageMountVBootV2() override = default; |
| 95 | |
| 96 | protected: |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 97 | ListenerAction UeventCallback(const Uevent& uevent) override; |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 98 | bool GetRequiredDevices() override; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 99 | bool SetUpDmVerity(fstab_rec* fstab_rec) override; |
| 100 | bool InitAvbHandle(); |
| 101 | |
| 102 | std::string device_tree_vbmeta_parts_; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 103 | FsManagerAvbUniquePtr avb_handle_; |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 104 | ByNameSymlinkMap by_name_symlink_map_; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 105 | }; |
| 106 | |
Bowgo Tsai | fd18a45 | 2017-04-24 18:36:25 +0800 | [diff] [blame] | 107 | // Static Functions |
| 108 | // ---------------- |
| 109 | static inline bool IsDtVbmetaCompatible() { |
| 110 | return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta"); |
| 111 | } |
| 112 | |
| 113 | static bool inline IsRecoveryMode() { |
| 114 | return access("/sbin/recovery", F_OK) == 0; |
| 115 | } |
| 116 | |
| 117 | // Class Definitions |
| 118 | // ----------------- |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 119 | FirstStageMount::FirstStageMount() |
| 120 | : need_dm_verity_(false), device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) { |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 121 | if (!device_tree_fstab_) { |
Bowgo Tsai | fe92dd0 | 2018-04-30 16:13:42 +0800 | [diff] [blame] | 122 | LOG(INFO) << "Failed to read fstab from device tree"; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 123 | return; |
| 124 | } |
Bowgo Tsai | 06ed613 | 2017-06-08 10:43:41 +0800 | [diff] [blame] | 125 | // Stores device_tree_fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>) |
| 126 | // for easier manipulation later, e.g., range-base for loop. |
| 127 | for (int i = 0; i < device_tree_fstab_->num_entries; i++) { |
| 128 | mount_fstab_recs_.push_back(&device_tree_fstab_->recs[i]); |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 129 | } |
Bowgo Tsai | 5ee7dae | 2018-05-16 18:33:44 +0800 | [diff] [blame] | 130 | |
| 131 | auto boot_devices = fs_mgr_get_boot_devices(); |
| 132 | device_handler_ = |
| 133 | std::make_unique<DeviceHandler>(std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, |
| 134 | std::vector<Subsystem>{}, std::move(boot_devices), false); |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 135 | } |
| 136 | |
| 137 | std::unique_ptr<FirstStageMount> FirstStageMount::Create() { |
Bowgo Tsai | fd18a45 | 2017-04-24 18:36:25 +0800 | [diff] [blame] | 138 | if (IsDtVbmetaCompatible()) { |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 139 | return std::make_unique<FirstStageMountVBootV2>(); |
| 140 | } else { |
| 141 | return std::make_unique<FirstStageMountVBootV1>(); |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | bool FirstStageMount::DoFirstStageMount() { |
| 146 | // Nothing to mount. |
| 147 | if (mount_fstab_recs_.empty()) return true; |
| 148 | |
Bowgo Tsai | fd18a45 | 2017-04-24 18:36:25 +0800 | [diff] [blame] | 149 | if (!InitDevices()) return false; |
| 150 | |
| 151 | if (!MountPartitions()) return false; |
| 152 | |
| 153 | return true; |
| 154 | } |
| 155 | |
| 156 | bool FirstStageMount::InitDevices() { |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 157 | return GetRequiredDevices() && InitRequiredDevices(); |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 158 | } |
| 159 | |
| 160 | // Creates devices with uevent->partition_name matching one in the member variable |
| 161 | // required_devices_partition_names_. Found partitions will then be removed from it |
| 162 | // for the subsequent member function to check which devices are NOT created. |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 163 | bool FirstStageMount::InitRequiredDevices() { |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 164 | if (required_devices_partition_names_.empty()) { |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 165 | return true; |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 166 | } |
| 167 | |
| 168 | if (need_dm_verity_) { |
Bowgo Tsai | ea5fca4 | 2017-05-02 18:30:33 +0800 | [diff] [blame] | 169 | const std::string dm_path = "/devices/virtual/misc/device-mapper"; |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 170 | bool found = false; |
| 171 | auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) { |
| 172 | if (uevent.path == dm_path) { |
Bowgo Tsai | 5ee7dae | 2018-05-16 18:33:44 +0800 | [diff] [blame] | 173 | device_handler_->HandleDeviceEvent(uevent); |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 174 | found = true; |
| 175 | return ListenerAction::kStop; |
| 176 | } |
| 177 | return ListenerAction::kContinue; |
| 178 | }; |
| 179 | uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback); |
| 180 | if (!found) { |
Tom Cherry | 322e176 | 2017-07-05 13:57:42 -0700 | [diff] [blame] | 181 | LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent"; |
| 182 | Timer t; |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 183 | uevent_listener_.Poll(dm_callback, 10s); |
Tom Cherry | 322e176 | 2017-07-05 13:57:42 -0700 | [diff] [blame] | 184 | LOG(INFO) << "Wait for device-mapper returned after " << t; |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 185 | } |
| 186 | if (!found) { |
Tom Cherry | 322e176 | 2017-07-05 13:57:42 -0700 | [diff] [blame] | 187 | LOG(ERROR) << "device-mapper device not found after polling timeout"; |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 188 | return false; |
| 189 | } |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 190 | } |
| 191 | |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 192 | auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); }; |
| 193 | uevent_listener_.RegenerateUevents(uevent_callback); |
| 194 | |
| 195 | // UeventCallback() will remove found partitions from required_devices_partition_names_. |
| 196 | // So if it isn't empty here, it means some partitions are not found. |
| 197 | if (!required_devices_partition_names_.empty()) { |
Tom Cherry | 322e176 | 2017-07-05 13:57:42 -0700 | [diff] [blame] | 198 | LOG(INFO) << __PRETTY_FUNCTION__ |
| 199 | << ": partition(s) not found in /sys, waiting for their uevent(s): " |
| 200 | << android::base::Join(required_devices_partition_names_, ", "); |
| 201 | Timer t; |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 202 | uevent_listener_.Poll(uevent_callback, 10s); |
Tom Cherry | 322e176 | 2017-07-05 13:57:42 -0700 | [diff] [blame] | 203 | LOG(INFO) << "Wait for partitions returned after " << t; |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 204 | } |
| 205 | |
| 206 | if (!required_devices_partition_names_.empty()) { |
Tom Cherry | 322e176 | 2017-07-05 13:57:42 -0700 | [diff] [blame] | 207 | LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: " |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 208 | << android::base::Join(required_devices_partition_names_, ", "); |
| 209 | return false; |
| 210 | } |
| 211 | |
| 212 | return true; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 213 | } |
| 214 | |
Greg Hartman | 8864749 | 2018-02-05 13:48:11 -0800 | [diff] [blame] | 215 | ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) { |
| 216 | // Matches partition name to create device nodes. |
| 217 | // Both required_devices_partition_names_ and uevent->partition_name have A/B |
| 218 | // suffix when A/B is used. |
| 219 | auto iter = required_devices_partition_names_.find(name); |
| 220 | if (iter != required_devices_partition_names_.end()) { |
| 221 | LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter; |
| 222 | required_devices_partition_names_.erase(iter); |
Bowgo Tsai | 5ee7dae | 2018-05-16 18:33:44 +0800 | [diff] [blame] | 223 | device_handler_->HandleDeviceEvent(uevent); |
Greg Hartman | 8864749 | 2018-02-05 13:48:11 -0800 | [diff] [blame] | 224 | if (required_devices_partition_names_.empty()) { |
| 225 | return ListenerAction::kStop; |
| 226 | } else { |
| 227 | return ListenerAction::kContinue; |
| 228 | } |
| 229 | } |
| 230 | return ListenerAction::kContinue; |
| 231 | } |
| 232 | |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 233 | ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent) { |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 234 | // Ignores everything that is not a block device. |
Tom Cherry | ed506f7 | 2017-05-25 15:58:59 -0700 | [diff] [blame] | 235 | if (uevent.subsystem != "block") { |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 236 | return ListenerAction::kContinue; |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 237 | } |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 238 | |
Tom Cherry | ed506f7 | 2017-05-25 15:58:59 -0700 | [diff] [blame] | 239 | if (!uevent.partition_name.empty()) { |
Greg Hartman | 8864749 | 2018-02-05 13:48:11 -0800 | [diff] [blame] | 240 | return HandleBlockDevice(uevent.partition_name, uevent); |
| 241 | } else { |
| 242 | size_t base_idx = uevent.path.rfind('/'); |
| 243 | if (base_idx != std::string::npos) { |
| 244 | return HandleBlockDevice(uevent.path.substr(base_idx + 1), uevent); |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 245 | } |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 246 | } |
| 247 | // Not found a partition or find an unneeded partition, continue to find others. |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 248 | return ListenerAction::kContinue; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 249 | } |
| 250 | |
| 251 | // Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX. |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 252 | bool FirstStageMount::InitVerityDevice(const std::string& verity_device) { |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 253 | const std::string device_name(basename(verity_device.c_str())); |
| 254 | const std::string syspath = "/sys/block/" + device_name; |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 255 | bool found = false; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 256 | |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 257 | auto verity_callback = [&device_name, &verity_device, this, &found](const Uevent& uevent) { |
| 258 | if (uevent.device_name == device_name) { |
| 259 | LOG(VERBOSE) << "Creating dm-verity device : " << verity_device; |
Bowgo Tsai | 5ee7dae | 2018-05-16 18:33:44 +0800 | [diff] [blame] | 260 | device_handler_->HandleDeviceEvent(uevent); |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 261 | found = true; |
| 262 | return ListenerAction::kStop; |
| 263 | } |
| 264 | return ListenerAction::kContinue; |
| 265 | }; |
| 266 | |
| 267 | uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback); |
| 268 | if (!found) { |
Tom Cherry | 322e176 | 2017-07-05 13:57:42 -0700 | [diff] [blame] | 269 | LOG(INFO) << "dm-verity device not found in /sys, waiting for its uevent"; |
| 270 | Timer t; |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 271 | uevent_listener_.Poll(verity_callback, 10s); |
Tom Cherry | 322e176 | 2017-07-05 13:57:42 -0700 | [diff] [blame] | 272 | LOG(INFO) << "wait for dm-verity device returned after " << t; |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 273 | } |
| 274 | if (!found) { |
Tom Cherry | 322e176 | 2017-07-05 13:57:42 -0700 | [diff] [blame] | 275 | LOG(ERROR) << "dm-verity device not found after polling timeout"; |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 276 | return false; |
| 277 | } |
| 278 | |
| 279 | return true; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 280 | } |
| 281 | |
| 282 | bool FirstStageMount::MountPartitions() { |
| 283 | for (auto fstab_rec : mount_fstab_recs_) { |
| 284 | if (!SetUpDmVerity(fstab_rec)) { |
| 285 | PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'"; |
| 286 | return false; |
| 287 | } |
| 288 | if (fs_mgr_do_mount_one(fstab_rec)) { |
| 289 | PLOG(ERROR) << "Failed to mount '" << fstab_rec->mount_point << "'"; |
| 290 | return false; |
| 291 | } |
| 292 | } |
| 293 | return true; |
| 294 | } |
| 295 | |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 296 | bool FirstStageMountVBootV1::GetRequiredDevices() { |
Bowgo Tsai | 71881ff | 2017-04-25 13:09:47 +0800 | [diff] [blame] | 297 | std::string verity_loc_device; |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 298 | need_dm_verity_ = false; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 299 | |
| 300 | for (auto fstab_rec : mount_fstab_recs_) { |
| 301 | // Don't allow verifyatboot in the first stage. |
| 302 | if (fs_mgr_is_verifyatboot(fstab_rec)) { |
| 303 | LOG(ERROR) << "Partitions can't be verified at boot"; |
| 304 | return false; |
| 305 | } |
| 306 | // Checks for verified partitions. |
| 307 | if (fs_mgr_is_verified(fstab_rec)) { |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 308 | need_dm_verity_ = true; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 309 | } |
Bowgo Tsai | 71881ff | 2017-04-25 13:09:47 +0800 | [diff] [blame] | 310 | // Checks if verity metadata is on a separate partition. Note that it is |
| 311 | // not partition specific, so there must be only one additional partition |
| 312 | // that carries verity state. |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 313 | if (fstab_rec->verity_loc) { |
Bowgo Tsai | 71881ff | 2017-04-25 13:09:47 +0800 | [diff] [blame] | 314 | if (verity_loc_device.empty()) { |
| 315 | verity_loc_device = fstab_rec->verity_loc; |
| 316 | } else if (verity_loc_device != fstab_rec->verity_loc) { |
| 317 | LOG(ERROR) << "More than one verity_loc found: " << verity_loc_device << ", " |
| 318 | << fstab_rec->verity_loc; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 319 | return false; |
| 320 | } |
| 321 | } |
| 322 | } |
| 323 | |
Bowgo Tsai | 71881ff | 2017-04-25 13:09:47 +0800 | [diff] [blame] | 324 | // Includes the partition names of fstab records and verity_loc_device (if any). |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 325 | // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used. |
| 326 | for (auto fstab_rec : mount_fstab_recs_) { |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 327 | required_devices_partition_names_.emplace(basename(fstab_rec->blk_device)); |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 328 | } |
| 329 | |
Bowgo Tsai | 71881ff | 2017-04-25 13:09:47 +0800 | [diff] [blame] | 330 | if (!verity_loc_device.empty()) { |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 331 | required_devices_partition_names_.emplace(basename(verity_loc_device.c_str())); |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 332 | } |
| 333 | |
| 334 | return true; |
| 335 | } |
| 336 | |
| 337 | bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) { |
| 338 | if (fs_mgr_is_verified(fstab_rec)) { |
| 339 | int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */); |
Sandeep Patil | 95366e9 | 2017-06-21 12:58:31 -0700 | [diff] [blame] | 340 | switch (ret) { |
Bowgo Tsai | 60f19a0 | 2017-06-22 22:23:08 +0800 | [diff] [blame] | 341 | case FS_MGR_SETUP_VERITY_SKIPPED: |
| 342 | case FS_MGR_SETUP_VERITY_DISABLED: |
| 343 | LOG(INFO) << "Verity disabled/skipped for '" << fstab_rec->mount_point << "'"; |
| 344 | return true; |
| 345 | case FS_MGR_SETUP_VERITY_SUCCESS: |
| 346 | // The exact block device name (fstab_rec->blk_device) is changed to |
| 347 | // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init |
| 348 | // first stage. |
| 349 | return InitVerityDevice(fstab_rec->blk_device); |
| 350 | default: |
| 351 | return false; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 352 | } |
| 353 | } |
| 354 | return true; // Returns true to mount the partition. |
| 355 | } |
| 356 | |
| 357 | // FirstStageMountVBootV2 constructor. |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 358 | // Gets the vbmeta partitions from device tree. |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 359 | // /{ |
| 360 | // firmware { |
| 361 | // android { |
| 362 | // vbmeta { |
| 363 | // compatible = "android,vbmeta"; |
| 364 | // parts = "vbmeta,boot,system,vendor" |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 365 | // }; |
| 366 | // }; |
| 367 | // }; |
| 368 | // } |
| 369 | FirstStageMountVBootV2::FirstStageMountVBootV2() : avb_handle_(nullptr) { |
| 370 | if (!read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts_)) { |
| 371 | PLOG(ERROR) << "Failed to read vbmeta/parts from device tree"; |
| 372 | return; |
| 373 | } |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 374 | } |
| 375 | |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 376 | bool FirstStageMountVBootV2::GetRequiredDevices() { |
| 377 | need_dm_verity_ = false; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 378 | |
| 379 | // fstab_rec->blk_device has A/B suffix. |
| 380 | for (auto fstab_rec : mount_fstab_recs_) { |
| 381 | if (fs_mgr_is_avb(fstab_rec)) { |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 382 | need_dm_verity_ = true; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 383 | } |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 384 | required_devices_partition_names_.emplace(basename(fstab_rec->blk_device)); |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 385 | } |
| 386 | |
| 387 | // libavb verifies AVB metadata on all verified partitions at once. |
| 388 | // e.g., The device_tree_vbmeta_parts_ will be "vbmeta,boot,system,vendor" |
| 389 | // for libavb to verify metadata, even if there is only /vendor in the |
| 390 | // above mount_fstab_recs_. |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 391 | if (need_dm_verity_) { |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 392 | if (device_tree_vbmeta_parts_.empty()) { |
| 393 | LOG(ERROR) << "Missing vbmeta parts in device tree"; |
| 394 | return false; |
| 395 | } |
| 396 | std::vector<std::string> partitions = android::base::Split(device_tree_vbmeta_parts_, ","); |
| 397 | std::string ab_suffix = fs_mgr_get_slot_suffix(); |
| 398 | for (const auto& partition : partitions) { |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 399 | // required_devices_partition_names_ is of type std::set so it's not an issue |
| 400 | // to emplace a partition twice. e.g., /vendor might be in both places: |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 401 | // - device_tree_vbmeta_parts_ = "vbmeta,boot,system,vendor" |
| 402 | // - mount_fstab_recs_: /vendor_a |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 403 | required_devices_partition_names_.emplace(partition + ab_suffix); |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 404 | } |
| 405 | } |
| 406 | return true; |
| 407 | } |
| 408 | |
Sandeep Patil | 4cbedee | 2017-06-21 13:02:57 -0700 | [diff] [blame] | 409 | ListenerAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) { |
Tom Cherry | ed506f7 | 2017-05-25 15:58:59 -0700 | [diff] [blame] | 410 | // Check if this uevent corresponds to one of the required partitions and store its symlinks if |
| 411 | // so, in order to create FsManagerAvbHandle later. |
| 412 | // Note that the parent callback removes partitions from the list of required partitions |
| 413 | // as it finds them, so this must happen first. |
| 414 | if (!uevent.partition_name.empty() && |
| 415 | required_devices_partition_names_.find(uevent.partition_name) != |
| 416 | required_devices_partition_names_.end()) { |
| 417 | // GetBlockDeviceSymlinks() will return three symlinks at most, depending on |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 418 | // the content of uevent. by-name symlink will be at [0] if uevent->partition_name |
| 419 | // is not empty. e.g., |
| 420 | // - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 421 | // - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1 |
Bowgo Tsai | 5ee7dae | 2018-05-16 18:33:44 +0800 | [diff] [blame] | 422 | std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent); |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 423 | if (!links.empty()) { |
Tom Cherry | ed506f7 | 2017-05-25 15:58:59 -0700 | [diff] [blame] | 424 | auto[it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]); |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 425 | if (!inserted) { |
Tom Cherry | ed506f7 | 2017-05-25 15:58:59 -0700 | [diff] [blame] | 426 | LOG(ERROR) << "Partition '" << uevent.partition_name |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 427 | << "' already existed in the by-name symlink map with a value of '" |
| 428 | << it->second << "', new value '" << links[0] << "' will be ignored."; |
| 429 | } |
| 430 | } |
| 431 | } |
| 432 | |
Tom Cherry | ed506f7 | 2017-05-25 15:58:59 -0700 | [diff] [blame] | 433 | return FirstStageMount::UeventCallback(uevent); |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 434 | } |
| 435 | |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 436 | bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) { |
| 437 | if (fs_mgr_is_avb(fstab_rec)) { |
| 438 | if (!InitAvbHandle()) return false; |
Bowgo Tsai | 60f19a0 | 2017-06-22 22:23:08 +0800 | [diff] [blame] | 439 | SetUpAvbHashtreeResult hashtree_result = |
| 440 | avb_handle_->SetUpAvbHashtree(fstab_rec, false /* wait_for_verity_dev */); |
| 441 | switch (hashtree_result) { |
| 442 | case SetUpAvbHashtreeResult::kDisabled: |
| 443 | return true; // Returns true to mount the partition. |
| 444 | case SetUpAvbHashtreeResult::kSuccess: |
| 445 | // The exact block device name (fstab_rec->blk_device) is changed to |
| 446 | // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init |
| 447 | // first stage. |
| 448 | return InitVerityDevice(fstab_rec->blk_device); |
| 449 | default: |
| 450 | return false; |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 451 | } |
| 452 | } |
| 453 | return true; // Returns true to mount the partition. |
| 454 | } |
| 455 | |
| 456 | bool FirstStageMountVBootV2::InitAvbHandle() { |
| 457 | if (avb_handle_) return true; // Returns true if the handle is already initialized. |
| 458 | |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 459 | if (by_name_symlink_map_.empty()) { |
| 460 | LOG(ERROR) << "by_name_symlink_map_ is empty"; |
| 461 | return false; |
| 462 | } |
| 463 | |
| 464 | avb_handle_ = FsManagerAvbHandle::Open(std::move(by_name_symlink_map_)); |
| 465 | by_name_symlink_map_.clear(); // Removes all elements after the above std::move(). |
| 466 | |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 467 | if (!avb_handle_) { |
| 468 | PLOG(ERROR) << "Failed to open FsManagerAvbHandle"; |
| 469 | return false; |
| 470 | } |
| 471 | // Sets INIT_AVB_VERSION here for init to set ro.boot.avb_version in the second stage. |
| 472 | setenv("INIT_AVB_VERSION", avb_handle_->avb_version().c_str(), 1); |
| 473 | return true; |
| 474 | } |
| 475 | |
| 476 | // Public functions |
| 477 | // ---------------- |
Bowgo Tsai | 06ed613 | 2017-06-08 10:43:41 +0800 | [diff] [blame] | 478 | // Mounts partitions specified by fstab in device tree. |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 479 | bool DoFirstStageMount() { |
| 480 | // Skips first stage mount if we're in recovery mode. |
Bowgo Tsai | fd18a45 | 2017-04-24 18:36:25 +0800 | [diff] [blame] | 481 | if (IsRecoveryMode()) { |
Bowgo Tsai | d262017 | 2017-04-17 22:17:09 +0800 | [diff] [blame] | 482 | LOG(INFO) << "First stage mount skipped (recovery mode)"; |
| 483 | return true; |
| 484 | } |
| 485 | |
| 486 | // Firstly checks if device tree fstab entries are compatible. |
| 487 | if (!is_android_dt_value_expected("fstab/compatible", "android,fstab")) { |
| 488 | LOG(INFO) << "First stage mount skipped (missing/incompatible fstab in device tree)"; |
| 489 | return true; |
| 490 | } |
| 491 | |
| 492 | std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create(); |
| 493 | if (!handle) { |
| 494 | LOG(ERROR) << "Failed to create FirstStageMount"; |
| 495 | return false; |
| 496 | } |
| 497 | return handle->DoFirstStageMount(); |
| 498 | } |
Bowgo Tsai | fd18a45 | 2017-04-24 18:36:25 +0800 | [diff] [blame] | 499 | |
| 500 | void SetInitAvbVersionInRecovery() { |
| 501 | if (!IsRecoveryMode()) { |
| 502 | LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not in recovery mode)"; |
| 503 | return; |
| 504 | } |
| 505 | |
| 506 | if (!IsDtVbmetaCompatible()) { |
| 507 | LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not vbmeta compatible)"; |
| 508 | return; |
| 509 | } |
| 510 | |
| 511 | // Initializes required devices for the subsequent FsManagerAvbHandle::Open() |
| 512 | // to verify AVB metadata on all partitions in the verified chain. |
| 513 | // We only set INIT_AVB_VERSION when the AVB verification succeeds, i.e., the |
| 514 | // Open() function returns a valid handle. |
| 515 | // We don't need to mount partitions here in recovery mode. |
| 516 | FirstStageMountVBootV2 avb_first_mount; |
| 517 | if (!avb_first_mount.InitDevices()) { |
| 518 | LOG(ERROR) << "Failed to init devices for INIT_AVB_VERSION"; |
| 519 | return; |
| 520 | } |
| 521 | |
Bowgo Tsai | 20651f6 | 2017-05-08 20:45:50 +0800 | [diff] [blame] | 522 | FsManagerAvbUniquePtr avb_handle = |
| 523 | FsManagerAvbHandle::Open(std::move(avb_first_mount.by_name_symlink_map_)); |
Bowgo Tsai | fd18a45 | 2017-04-24 18:36:25 +0800 | [diff] [blame] | 524 | if (!avb_handle) { |
| 525 | PLOG(ERROR) << "Failed to open FsManagerAvbHandle for INIT_AVB_VERSION"; |
| 526 | return; |
| 527 | } |
| 528 | setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1); |
| 529 | } |
Tom Cherry | 81f5d3e | 2017-06-22 12:53:17 -0700 | [diff] [blame] | 530 | |
| 531 | } // namespace init |
| 532 | } // namespace android |