diff options
-rw-r--r-- | fastboot/device/flashing.cpp | 18 | ||||
-rw-r--r-- | fs_mgr/fs_mgr_fstab.cpp | 17 | ||||
-rw-r--r-- | fs_mgr/libsnapshot/cow_snapuserd_test.cpp | 74 | ||||
-rw-r--r-- | fs_mgr/libsnapshot/include/libsnapshot/snapshot.h | 1 | ||||
-rw-r--r-- | fs_mgr/libsnapshot/snapshot.cpp | 40 | ||||
-rw-r--r-- | fs_mgr/libsnapshot/snapshot_test.cpp | 76 | ||||
-rw-r--r-- | fs_mgr/libsnapshot/snapuserd_worker.cpp | 28 | ||||
-rw-r--r-- | init/init.cpp | 15 | ||||
-rw-r--r-- | libprocessgroup/profiles/task_profiles_28.json | 13 | ||||
-rw-r--r-- | libprocessgroup/profiles/task_profiles_29.json | 13 | ||||
-rw-r--r-- | libprocessgroup/profiles/task_profiles_30.json | 13 | ||||
-rw-r--r-- | libutils/Threads.cpp | 6 | ||||
-rw-r--r-- | rootdir/init.rc | 16 | ||||
-rw-r--r-- | trusty/storage/interface/include/trusty/interface/storage.h | 40 | ||||
-rw-r--r-- | trusty/storage/proxy/Android.bp | 3 | ||||
-rw-r--r-- | trusty/storage/proxy/checkpoint_handling.cpp | 77 | ||||
-rw-r--r-- | trusty/storage/proxy/checkpoint_handling.h | 37 | ||||
-rw-r--r-- | trusty/storage/proxy/proxy.c | 16 | ||||
-rw-r--r-- | trusty/storage/proxy/rpmb.c | 263 |
19 files changed, 692 insertions, 74 deletions
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp index ee0aa582b..9b5d2cd25 100644 --- a/fastboot/device/flashing.cpp +++ b/fastboot/device/flashing.cpp @@ -187,11 +187,17 @@ bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wip ", build may be missing broken or missing boot_devices"); } + std::string slot_suffix = device->GetCurrentSlot(); + uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix); + + std::string other_slot_suffix; + if (!slot_suffix.empty()) { + other_slot_suffix = (slot_suffix == "_a") ? "_b" : "_a"; + } + // If we are unable to read the existing metadata, then the super partition // is corrupt. In this case we reflash the whole thing using the provided // image. - std::string slot_suffix = device->GetCurrentSlot(); - uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix); std::unique_ptr<LpMetadata> old_metadata = ReadMetadata(super_name, slot_number); if (wipe || !old_metadata) { if (!FlashPartitionTable(super_name, *new_metadata.get())) { @@ -203,11 +209,15 @@ bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wip } std::set<std::string> partitions_to_keep; + bool virtual_ab = android::base::GetBoolProperty("ro.virtual_ab.enabled", false); for (const auto& partition : old_metadata->partitions) { // Preserve partitions in the other slot, but not the current slot. std::string partition_name = GetPartitionName(partition); - if (!slot_suffix.empty() && GetPartitionSlotSuffix(partition_name) == slot_suffix) { - continue; + if (!slot_suffix.empty()) { + auto part_suffix = GetPartitionSlotSuffix(partition_name); + if (part_suffix == slot_suffix || (part_suffix == other_slot_suffix && virtual_ab)) { + continue; + } } std::string group_name = GetPartitionGroupName(old_metadata->groups[partition.group_index]); // Skip partitions in the COW group diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp index 9a2332445..7fc73bdc5 100644 --- a/fs_mgr/fs_mgr_fstab.cpp +++ b/fs_mgr/fs_mgr_fstab.cpp @@ -414,17 +414,24 @@ std::string ReadFstabFromDt() { return fstab_result; } -// Identify path to fstab file. Lookup is based on pattern -// fstab.<fstab_suffix>, fstab.<hardware>, fstab.<hardware.platform> in -// folders /odm/etc, vendor/etc, or /. +// Return the path to the fstab file. There may be multiple fstab files; the +// one that is returned will be the first that exists of fstab.<fstab_suffix>, +// fstab.<hardware>, and fstab.<hardware.platform>. The fstab is searched for +// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in +// the first stage ramdisk during early boot. Previously, the first stage +// ramdisk's copy of the fstab had to be located in the root directory, but now +// the system/etc directory is supported too and is the preferred location. std::string GetFstabPath() { for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) { std::string suffix; if (!fs_mgr_get_boot_config(prop, &suffix)) continue; - for (const char* prefix : - {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab.", "/first_stage_ramdisk/fstab."}) { + for (const char* prefix : {// late-boot/post-boot locations + "/odm/etc/fstab.", "/vendor/etc/fstab.", + // early boot locations + "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.", + "/fstab.", "/first_stage_ramdisk/fstab."}) { std::string fstab_path = prefix + suffix; if (access(fstab_path.c_str(), F_OK) == 0) { return fstab_path; diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp index d09c6e9c9..bd432bbdf 100644 --- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp +++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp @@ -108,6 +108,7 @@ class CowSnapuserdTest final { void MergeInterruptFixed(int duration); void MergeInterruptRandomly(int max_duration); void ReadDmUserBlockWithoutDaemon(); + void ReadLastBlock(); std::string snapshot_dev() const { return snapshot_dev_->path(); } @@ -256,6 +257,73 @@ void CowSnapuserdTest::StartSnapuserdDaemon() { } } +void CowSnapuserdTest::ReadLastBlock() { + unique_fd rnd_fd; + total_base_size_ = BLOCK_SZ * 2; + + base_fd_ = CreateTempFile("base_device", total_base_size_); + ASSERT_GE(base_fd_, 0); + + rnd_fd.reset(open("/dev/random", O_RDONLY)); + ASSERT_TRUE(rnd_fd > 0); + + std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ); + + for (size_t j = 0; j < ((total_base_size_) / BLOCK_SZ); j++) { + ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), BLOCK_SZ, 0), true); + ASSERT_EQ(android::base::WriteFully(base_fd_, random_buffer.get(), BLOCK_SZ), true); + } + + ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0); + + base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s); + ASSERT_TRUE(base_loop_->valid()); + + std::string path = android::base::GetExecutableDirectory(); + cow_system_ = std::make_unique<TemporaryFile>(path); + + std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(total_base_size_); + loff_t offset = 0; + + // Fill random data + for (size_t j = 0; j < (total_base_size_ / BLOCK_SZ); j++) { + ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, BLOCK_SZ, 0), + true); + + offset += BLOCK_SZ; + } + + CowOptions options; + options.compression = "gz"; + CowWriter writer(options); + + ASSERT_TRUE(writer.Initialize(cow_system_->fd)); + + ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1_.get(), BLOCK_SZ)); + ASSERT_TRUE(writer.AddRawBlocks(1, (char*)random_buffer_1_.get() + BLOCK_SZ, BLOCK_SZ)); + + ASSERT_TRUE(writer.Finalize()); + + SetDeviceControlName(); + + StartSnapuserdDaemon(); + InitCowDevice(); + + CreateDmUserDevice(); + InitDaemon(); + + CreateSnapshotDevice(); + + unique_fd snapshot_fd(open(snapshot_dev_->path().c_str(), O_RDONLY)); + ASSERT_TRUE(snapshot_fd > 0); + + std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ); + + offset = 7680; + ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), 512, offset), true); + ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)random_buffer_1_.get() + offset, 512), 0); +} + void CowSnapuserdTest::CreateBaseDevice() { unique_fd rnd_fd; @@ -1068,6 +1136,12 @@ TEST(Snapuserd_Test, Snapshot_IO_TEST) { harness.Shutdown(); } +TEST(Snapuserd_Test, Snapshot_END_IO_TEST) { + CowSnapuserdTest harness; + harness.ReadLastBlock(); + harness.Shutdown(); +} + TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST_1) { CowSnapuserdTest harness; ASSERT_TRUE(harness.SetupCopyOverlap_1()); diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h index 15882b382..9bf5db18e 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h @@ -399,6 +399,7 @@ class SnapshotManager final : public ISnapshotManager { FRIEND_TEST(SnapshotTest, MergeFailureCode); FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot); FRIEND_TEST(SnapshotTest, UpdateBootControlHal); + FRIEND_TEST(SnapshotUpdateTest, AddPartition); FRIEND_TEST(SnapshotUpdateTest, DaemonTransition); FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback); FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery); diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp index 0e36da151..4c94da28f 100644 --- a/fs_mgr/libsnapshot/snapshot.cpp +++ b/fs_mgr/libsnapshot/snapshot.cpp @@ -518,6 +518,13 @@ bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name, break; } + if (mode == SnapshotStorageMode::Persistent && status.state() == SnapshotState::MERGING) { + LOG(ERROR) << "Snapshot: " << name + << " has snapshot status Merging but mode set to Persistent." + << " Changing mode to Snapshot-Merge."; + mode = SnapshotStorageMode::Merge; + } + DmTable table; table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_device, mode, kSnapshotChunkSize); @@ -886,6 +893,10 @@ bool SnapshotManager::QuerySnapshotStatus(const std::string& dm_name, std::strin if (target_type) { *target_type = DeviceMapper::GetTargetType(target.spec); } + if (!status->error.empty()) { + LOG(ERROR) << "Snapshot: " << dm_name << " returned error code: " << status->error; + return false; + } return true; } @@ -1456,7 +1467,7 @@ bool SnapshotManager::PerformInitTransition(InitTransition transition, std::vector<std::string>* snapuserd_argv) { LOG(INFO) << "Performing transition for snapuserd."; - // Don't use EnsuerSnapuserdConnected() because this is called from init, + // Don't use EnsureSnapuserdConnected() because this is called from init, // and attempting to do so will deadlock. if (!snapuserd_client_ && transition != InitTransition::SELINUX_DETACH) { snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s); @@ -1513,8 +1524,15 @@ bool SnapshotManager::PerformInitTransition(InitTransition transition, continue; } + std::string source_device_name; + if (snapshot_status.old_partition_size() > 0) { + source_device_name = GetSourceDeviceName(snapshot); + } else { + source_device_name = GetBaseDeviceName(snapshot); + } + std::string source_device; - if (!dm.GetDmDevicePathByName(GetSourceDeviceName(snapshot), &source_device)) { + if (!dm.GetDmDevicePathByName(source_device_name, &source_device)) { LOG(ERROR) << "Could not get device path for " << GetSourceDeviceName(snapshot); continue; } @@ -2091,14 +2109,18 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock, if (live_snapshot_status->compression_enabled()) { // Get the source device (eg the view of the partition from before it was resized). std::string source_device_path; - if (!MapSourceDevice(lock, params.GetPartitionName(), remaining_time, - &source_device_path)) { - LOG(ERROR) << "Could not map source device for: " << cow_name; - return false; - } + if (live_snapshot_status->old_partition_size() > 0) { + if (!MapSourceDevice(lock, params.GetPartitionName(), remaining_time, + &source_device_path)) { + LOG(ERROR) << "Could not map source device for: " << cow_name; + return false; + } - auto source_device = GetSourceDeviceName(params.GetPartitionName()); - created_devices.EmplaceBack<AutoUnmapDevice>(&dm, source_device); + auto source_device = GetSourceDeviceName(params.GetPartitionName()); + created_devices.EmplaceBack<AutoUnmapDevice>(&dm, source_device); + } else { + source_device_path = base_path; + } if (!WaitForDevice(source_device_path, remaining_time)) { return false; diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp index 60186434a..7630efe3f 100644 --- a/fs_mgr/libsnapshot/snapshot_test.cpp +++ b/fs_mgr/libsnapshot/snapshot_test.cpp @@ -963,7 +963,7 @@ class SnapshotUpdateTest : public SnapshotTest { } AssertionResult UnmapAll() { - for (const auto& name : {"sys", "vnd", "prd"}) { + for (const auto& name : {"sys", "vnd", "prd", "dlkm"}) { if (!dm_.DeleteDeviceIfExists(name + "_a"s)) { return AssertionFailure() << "Cannot unmap " << name << "_a"; } @@ -2026,6 +2026,80 @@ TEST_F(SnapshotUpdateTest, LowSpace) { ASSERT_LT(res.required_size(), 40_MiB); } +TEST_F(SnapshotUpdateTest, AddPartition) { + // OTA client blindly unmaps all partitions that are possibly mapped. + for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { + ASSERT_TRUE(sm->UnmapUpdateSnapshot(name)); + } + + group_->add_partition_names("dlkm"); + + auto dlkm = manifest_.add_partitions(); + dlkm->set_partition_name("dlkm"); + dlkm->set_estimate_cow_size(2_MiB); + SetSize(dlkm, 3_MiB); + + // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs + // fit in super, but not |prd|. + constexpr uint64_t partition_size = 3788_KiB; + SetSize(sys_, partition_size); + SetSize(vnd_, partition_size); + SetSize(prd_, partition_size); + SetSize(dlkm, partition_size); + + AddOperationForPartitions({sys_, vnd_, prd_, dlkm}); + + // Execute the update. + ASSERT_TRUE(sm->BeginUpdate()); + ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_)); + + // Write some data to target partitions. + for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) { + ASSERT_TRUE(WriteSnapshotAndHash(name)); + } + + // Assert that source partitions aren't affected. + for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) { + ASSERT_TRUE(IsPartitionUnchanged(name)); + } + + ASSERT_TRUE(sm->FinishedSnapshotWrites(false)); + + // Simulate shutting down the device. + ASSERT_TRUE(UnmapAll()); + + // After reboot, init does first stage mount. + auto init = NewManagerForFirstStageMount("_b"); + ASSERT_NE(init, nullptr); + + ASSERT_TRUE(init->EnsureSnapuserdConnected()); + init->set_use_first_stage_snapuserd(true); + + ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); + ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); + + // Check that the target partitions have the same content. + std::vector<std::string> partitions = {"sys_b", "vnd_b", "prd_b", "dlkm_b"}; + for (const auto& name : partitions) { + ASSERT_TRUE(IsPartitionUnchanged(name)); + } + + ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE)); + for (const auto& name : partitions) { + ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete(name + "-user-cow-init")); + } + + // Initiate the merge and wait for it to be completed. + ASSERT_TRUE(init->InitiateMerge()); + ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState()); + + // Check that the target partitions have the same content after the merge. + for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) { + ASSERT_TRUE(IsPartitionUnchanged(name)) + << "Content of " << name << " changes after the merge"; + } +} + class AutoKill final { public: explicit AutoKill(pid_t pid) : pid_(pid) {} diff --git a/fs_mgr/libsnapshot/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd_worker.cpp index 682f9da58..defb5bb76 100644 --- a/fs_mgr/libsnapshot/snapuserd_worker.cpp +++ b/fs_mgr/libsnapshot/snapuserd_worker.cpp @@ -287,16 +287,36 @@ int WorkerThread::ReadData(sector_t sector, size_t size) { it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr), Snapuserd::compare); - if (!(it != chunk_vec.end())) { - SNAP_LOG(ERROR) << "ReadData: Sector " << sector << " not found in chunk_vec"; - return -1; + bool read_end_of_device = false; + if (it == chunk_vec.end()) { + // |-------|-------|-------| + // 0 1 2 3 + // + // Block 0 - op 1 + // Block 1 - op 2 + // Block 2 - op 3 + // + // chunk_vec will have block 0, 1, 2 which maps to relavant COW ops. + // + // Each block is 4k bytes. Thus, the last block will span 8 sectors + // ranging till block 3 (However, block 3 won't be in chunk_vec as + // it doesn't have any mapping to COW ops. Now, if we get an I/O request for a sector + // spanning between block 2 and block 3, we need to step back + // and get hold of the last element. + // + // Additionally, dm-snapshot makes sure that I/O request beyond block 3 + // will not be routed to the daemon. Hence, it is safe to assume that + // if a sector is not available in the chunk_vec, the I/O falls in the + // end of region. + it = std::prev(chunk_vec.end()); + read_end_of_device = true; } // We didn't find the required sector; hence find the previous sector // as lower_bound will gives us the value greater than // the requested sector if (it->first != sector) { - if (it != chunk_vec.begin()) { + if (it != chunk_vec.begin() && !read_end_of_device) { --it; } diff --git a/init/init.cpp b/init/init.cpp index 28e100486..6dcf4a30b 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -27,6 +27,7 @@ #include <sys/mount.h> #include <sys/signalfd.h> #include <sys/types.h> +#include <sys/utsname.h> #include <unistd.h> #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ @@ -569,6 +570,19 @@ static void SetUsbController() { } } +/// Set ro.kernel.version property to contain the major.minor pair as returned +/// by uname(2). +static void SetKernelVersion() { + struct utsname uts; + unsigned int major, minor; + + if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) { + LOG(ERROR) << "Could not parse the kernel version from uname"; + return; + } + SetProperty("ro.kernel.version", android::base::StringPrintf("%u.%u", major, minor)); +} + static void HandleSigtermSignal(const signalfd_siginfo& siginfo) { if (siginfo.ssi_pid != 0) { // Drop any userspace SIGTERM requests. @@ -854,6 +868,7 @@ int SecondStageMain(int argc, char** argv) { export_oem_lock_status(); MountHandler mount_handler(&epoll); SetUsbController(); + SetKernelVersion(); const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap(); Action::set_function_map(&function_map); diff --git a/libprocessgroup/profiles/task_profiles_28.json b/libprocessgroup/profiles/task_profiles_28.json index 9f8378590..56053e05a 100644 --- a/libprocessgroup/profiles/task_profiles_28.json +++ b/libprocessgroup/profiles/task_profiles_28.json @@ -40,6 +40,19 @@ ] }, { + "Name": "ServicePerformance", + "Actions": [ + { + "Name": "JoinCgroup", + "Params": + { + "Controller": "schedtune", + "Path": "background" + } + } + ] + }, + { "Name": "HighPerformance", "Actions": [ { diff --git a/libprocessgroup/profiles/task_profiles_29.json b/libprocessgroup/profiles/task_profiles_29.json index 9f8378590..52279b872 100644 --- a/libprocessgroup/profiles/task_profiles_29.json +++ b/libprocessgroup/profiles/task_profiles_29.json @@ -53,6 +53,19 @@ ] }, { + "Name": "ServicePerformance", + "Actions": [ + { + "Name": "JoinCgroup", + "Params": + { + "Controller": "schedtune", + "Path": "background" + } + } + ] + }, + { "Name": "MaxPerformance", "Actions": [ { diff --git a/libprocessgroup/profiles/task_profiles_30.json b/libprocessgroup/profiles/task_profiles_30.json index 9f8378590..56053e05a 100644 --- a/libprocessgroup/profiles/task_profiles_30.json +++ b/libprocessgroup/profiles/task_profiles_30.json @@ -40,6 +40,19 @@ ] }, { + "Name": "ServicePerformance", + "Actions": [ + { + "Name": "JoinCgroup", + "Params": + { + "Controller": "schedtune", + "Path": "background" + } + } + ] + }, + { "Name": "HighPerformance", "Actions": [ { diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp index 540dcf49d..6e293c741 100644 --- a/libutils/Threads.cpp +++ b/libutils/Threads.cpp @@ -86,8 +86,10 @@ struct thread_data_t { // A new thread will be in its parent's sched group by default, // so we just need to handle the background case. + // currently set to system_background group which is different + // from background group for app. if (prio >= ANDROID_PRIORITY_BACKGROUND) { - SetTaskProfiles(0, {"SCHED_SP_BACKGROUND"}, true); + SetTaskProfiles(0, {"SCHED_SP_SYSTEM"}, true); } if (name) { @@ -313,7 +315,7 @@ int androidSetThreadPriority(pid_t tid, int pri) } if (pri >= ANDROID_PRIORITY_BACKGROUND) { - rc = SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1; + rc = SetTaskProfiles(tid, {"SCHED_SP_SYSTEM"}, true) ? 0 : -1; } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) { SchedPolicy policy = SP_FOREGROUND; // Change to the sched policy group of the process. diff --git a/rootdir/init.rc b/rootdir/init.rc index 637cab938..8272654c7 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -607,9 +607,23 @@ on late-fs # Load trusted keys from dm-verity protected partitions exec -- /system/bin/fsverity_init --load-verified-keys +# Only enable the bootreceiver tracing instance for kernels 5.10 and above. +on late-fs && property:ro.kernel.version=4.9 + setprop bootreceiver.enable 0 +on late-fs && property:ro.kernel.version=4.14 + setprop bootreceiver.enable 0 +on late-fs && property:ro.kernel.version=4.19 + setprop bootreceiver.enable 0 +on late-fs && property:ro.kernel.version=5.4 + setprop bootreceiver.enable 0 +on late-fs + # Bootreceiver tracing instance is enabled by default. + setprop bootreceiver.enable ${bootreceiver.enable:-1} + +on property:ro.product.cpu.abilist64=* && property:bootreceiver.enable=1 # Set up a tracing instance for system_server to monitor error_report_end events. # These are sent by kernel tools like KASAN and KFENCE when a memory corruption - # is detected. + # is detected. This is only needed for 64-bit systems. mkdir /sys/kernel/tracing/instances/bootreceiver 0700 system system restorecon_recursive /sys/kernel/tracing/instances/bootreceiver write /sys/kernel/tracing/instances/bootreceiver/buffer_size_kb 1 diff --git a/trusty/storage/interface/include/trusty/interface/storage.h b/trusty/storage/interface/include/trusty/interface/storage.h index b196d88b3..3f1dcb8c6 100644 --- a/trusty/storage/interface/include/trusty/interface/storage.h +++ b/trusty/storage/interface/include/trusty/interface/storage.h @@ -112,26 +112,30 @@ enum storage_file_open_flag { /** * enum storage_msg_flag - protocol-level flags in struct storage_msg - * @STORAGE_MSG_FLAG_BATCH: if set, command belongs to a batch transaction. - * No response will be sent by the server until - * it receives a command with this flag unset, at - * which point a cummulative result for all messages - * sent with STORAGE_MSG_FLAG_BATCH will be sent. - * This is only supported by the non-secure disk proxy - * server. - * @STORAGE_MSG_FLAG_PRE_COMMIT: if set, indicates that server need to commit - * pending changes before processing this message. - * @STORAGE_MSG_FLAG_POST_COMMIT: if set, indicates that server need to commit - * pending changes after processing this message. - * @STORAGE_MSG_FLAG_TRANSACT_COMPLETE: if set, indicates that server need to commit - * current transaction after processing this message. - * It is an alias for STORAGE_MSG_FLAG_POST_COMMIT. + * @STORAGE_MSG_FLAG_BATCH: if set, command belongs to a batch transaction. + * No response will be sent by the server until + * it receives a command with this flag unset, at + * which point a cumulative result for all messages + * sent with STORAGE_MSG_FLAG_BATCH will be sent. + * This is only supported by the non-secure disk proxy + * server. + * @STORAGE_MSG_FLAG_PRE_COMMIT: if set, indicates that server need to commit + * pending changes before processing this message. + * @STORAGE_MSG_FLAG_POST_COMMIT: if set, indicates that server need to commit + * pending changes after processing this message. + * @STORAGE_MSG_FLAG_TRANSACT_COMPLETE: if set, indicates that server need to commit + * current transaction after processing this message. + * It is an alias for STORAGE_MSG_FLAG_POST_COMMIT. + * @STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT: if set, indicates that server needs to ensure + * that there is not a pending checkpoint for + * userdata before processing this message. */ enum storage_msg_flag { - STORAGE_MSG_FLAG_BATCH = 0x1, - STORAGE_MSG_FLAG_PRE_COMMIT = 0x2, - STORAGE_MSG_FLAG_POST_COMMIT = 0x4, - STORAGE_MSG_FLAG_TRANSACT_COMPLETE = STORAGE_MSG_FLAG_POST_COMMIT, + STORAGE_MSG_FLAG_BATCH = 0x1, + STORAGE_MSG_FLAG_PRE_COMMIT = 0x2, + STORAGE_MSG_FLAG_POST_COMMIT = 0x4, + STORAGE_MSG_FLAG_TRANSACT_COMPLETE = STORAGE_MSG_FLAG_POST_COMMIT, + STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT = 0x8, }; /* diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp index d67089fb2..38d868508 100644 --- a/trusty/storage/proxy/Android.bp +++ b/trusty/storage/proxy/Android.bp @@ -23,6 +23,7 @@ cc_binary { vendor: true, srcs: [ + "checkpoint_handling.cpp", "ipc.c", "rpmb.c", "storage.c", @@ -30,12 +31,14 @@ cc_binary { ], shared_libs: [ + "libbase", "liblog", "libhardware_legacy", ], header_libs: ["libcutils_headers"], static_libs: [ + "libfstab", "libtrustystorageinterface", "libtrusty", ], diff --git a/trusty/storage/proxy/checkpoint_handling.cpp b/trusty/storage/proxy/checkpoint_handling.cpp new file mode 100644 index 000000000..6c2fd363e --- /dev/null +++ b/trusty/storage/proxy/checkpoint_handling.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2021 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 "checkpoint_handling.h" +#include "log.h" + +#include <fstab/fstab.h> +#include <cstring> +#include <string> + +namespace { + +bool checkpointingDoneForever = false; + +} // namespace + +int is_data_checkpoint_active(bool* active) { + if (!active) { + ALOGE("active out parameter is null"); + return 0; + } + + *active = false; + + if (checkpointingDoneForever) { + return 0; + } + + android::fs_mgr::Fstab procMounts; + bool success = android::fs_mgr::ReadFstabFromFile("/proc/mounts", &procMounts); + if (!success) { + ALOGE("Could not parse /proc/mounts\n"); + /* Really bad. Tell the caller to abort the write. */ + return -1; + } + + android::fs_mgr::FstabEntry* dataEntry = + android::fs_mgr::GetEntryForMountPoint(&procMounts, "/data"); + if (dataEntry == NULL) { + ALOGE("/data is not mounted yet\n"); + return 0; + } + + /* We can't handle e.g., ext4. Nothing we can do about it for now. */ + if (dataEntry->fs_type != "f2fs") { + ALOGW("Checkpoint status not supported for filesystem %s\n", dataEntry->fs_type.c_str()); + checkpointingDoneForever = true; + return 0; + } + + /* + * The data entry looks like "... blah,checkpoint=disable:0,blah ...". + * checkpoint=disable means checkpointing is on (yes, arguably reversed). + */ + size_t checkpointPos = dataEntry->fs_options.find("checkpoint=disable"); + if (checkpointPos == std::string::npos) { + /* Assumption is that once checkpointing turns off, it stays off */ + checkpointingDoneForever = true; + } else { + *active = true; + } + + return 0; +} diff --git a/trusty/storage/proxy/checkpoint_handling.h b/trusty/storage/proxy/checkpoint_handling.h new file mode 100644 index 000000000..f1bf27c8d --- /dev/null +++ b/trusty/storage/proxy/checkpoint_handling.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 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. + */ + +#pragma once + +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * is_data_checkpoint_active() - Check for an active, uncommitted checkpoint of + * /data. If a checkpoint is active, storage should not commit any + * rollback-protected writes to /data. + * @active: Out parameter that will be set to the result of the check. + * + * Return: 0 if active was set and is valid, non-zero otherwise. + */ +int is_data_checkpoint_active(bool* active); + +#ifdef __cplusplus +} +#endif diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c index e23094183..c690a2876 100644 --- a/trusty/storage/proxy/proxy.c +++ b/trusty/storage/proxy/proxy.c @@ -26,6 +26,7 @@ #include <cutils/android_filesystem_config.h> +#include "checkpoint_handling.h" #include "ipc.h" #include "log.h" #include "rpmb.h" @@ -130,6 +131,21 @@ static int handle_req(struct storage_msg* msg, const void* req, size_t req_len) } } + if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT) { + bool is_checkpoint_active = false; + + rc = is_data_checkpoint_active(&is_checkpoint_active); + if (rc != 0) { + ALOGE("is_data_checkpoint_active failed in an unexpected way. Aborting.\n"); + msg->result = STORAGE_ERR_GENERIC; + return ipc_respond(msg, NULL, 0); + } else if (is_checkpoint_active) { + ALOGE("Checkpoint in progress, dropping write ...\n"); + msg->result = STORAGE_ERR_GENERIC; + return ipc_respond(msg, NULL, 0); + } + } + switch (msg->cmd) { case STORAGE_FILE_DELETE: rc = storage_file_delete(msg, req, req_len); diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c index b59fb67f6..f059935bf 100644 --- a/trusty/storage/proxy/rpmb.c +++ b/trusty/storage/proxy/rpmb.c @@ -16,7 +16,10 @@ #include <errno.h> #include <fcntl.h> +#include <scsi/scsi.h> +#include <scsi/scsi_proto.h> #include <scsi/sg.h> +#include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -55,6 +58,17 @@ #define MMC_BLOCK_SIZE 512 /* + * Number of retry attempts when an RPMB authenticated write triggers a UNIT + * ATTENTION + */ +#define UFS_RPMB_WRITE_RETRY_COUNT 1 +/* + * Number of retry attempts when an RPMB read operation triggers a UNIT + * ATTENTION + */ +#define UFS_RPMB_READ_RETRY_COUNT 3 + +/* * There should be no timeout for security protocol ioctl call, so we choose a * large number for timeout. * 20000 millisecs == 20 seconds @@ -104,21 +118,62 @@ static enum dev_type dev_type = UNKNOWN_RPMB; static const char* UFS_WAKE_LOCK_NAME = "ufs_seq_wakelock"; -#ifdef RPMB_DEBUG - -static void print_buf(const char* prefix, const uint8_t* buf, size_t size) { +/** + * log_buf - Log a byte buffer to the android log. + * @priority: One of ANDROID_LOG_* priority levels from android_LogPriority in + * android/log.h + * @prefix: A null-terminated string that identifies this buffer. Must be less + * than 128 bytes. + * @buf: Buffer to dump. + * @size: Length of @buf in bytes. + */ +#define LOG_BUF_SIZE 256 +static int log_buf(int priority, const char* prefix, const uint8_t* buf, size_t size) { + int rc; size_t i; + char line[LOG_BUF_SIZE] = {0}; + char* cur = line; - printf("%s @%p [%zu]", prefix, buf, size); + rc = snprintf(line, LOG_BUF_SIZE, "%s @%p [%zu]", prefix, buf, size); + if (rc < 0 || rc >= LOG_BUF_SIZE) { + goto err; + } + cur += rc; for (i = 0; i < size; i++) { - if (i && i % 32 == 0) printf("\n%*s", (int)strlen(prefix), ""); - printf(" %02x", buf[i]); + if (i % 32 == 0) { + /* + * Flush the line out to the log after we have printed 32 bytes + * (also flushes the header line on the first iteration and sets up + * for printing the buffer itself) + */ + LOG_PRI(priority, LOG_TAG, "%s", line); + memset(line, 0, LOG_BUF_SIZE); + cur = line; + /* Shift output over by the length of the prefix */ + rc = snprintf(line, LOG_BUF_SIZE, "%*s", (int)strlen(prefix), ""); + if (rc < 0 || rc >= LOG_BUF_SIZE) { + goto err; + } + cur += rc; + } + rc = snprintf(cur, LOG_BUF_SIZE - (cur - line), "%02x ", buf[i]); + if (rc < 0 || rc >= LOG_BUF_SIZE - (cur - line)) { + goto err; + } + cur += rc; } - printf("\n"); - fflush(stdout); -} + LOG_PRI(priority, LOG_TAG, "%s", line); -#endif + return 0; + +err: + if (rc < 0) { + return rc; + } else { + ALOGE("log_buf prefix was too long"); + return -1; + } +} static void set_sg_io_hdr(sg_io_hdr_t* io_hdrp, int dxfer_direction, unsigned char cmd_len, unsigned char mx_sb_len, unsigned int dxfer_len, void* dxferp, @@ -135,6 +190,137 @@ static void set_sg_io_hdr(sg_io_hdr_t* io_hdrp, int dxfer_direction, unsigned ch io_hdrp->timeout = TIMEOUT; } +/** + * enum scsi_result - Results of checking the SCSI status and sense buffer + * + * @SCSI_RES_OK: SCSI status and sense are good + * @SCSI_RES_ERR: SCSI status or sense contain an unhandled error + * @SCSI_RES_RETRY: SCSI sense buffer contains a status that indicates that the + * command should be retried + */ +enum scsi_result { + SCSI_RES_OK = 0, + SCSI_RES_ERR, + SCSI_RES_RETRY, +}; + +static enum scsi_result check_scsi_sense(const uint8_t* sense_buf, size_t len) { + uint8_t response_code = 0; + uint8_t sense_key = 0; + uint8_t additional_sense_code = 0; + uint8_t additional_sense_code_qualifier = 0; + uint8_t additional_length = 0; + + if (!sense_buf || len == 0) { + ALOGE("Invalid SCSI sense buffer, length: %zu\n", len); + return SCSI_RES_ERR; + } + + response_code = 0x7f & sense_buf[0]; + + if (response_code < 0x70 || response_code > 0x73) { + ALOGE("Invalid SCSI sense response code: %hhu\n", response_code); + return SCSI_RES_ERR; + } + + if (response_code >= 0x72) { + /* descriptor format, SPC-6 4.4.2 */ + if (len > 1) { + sense_key = 0xf & sense_buf[1]; + } + if (len > 2) { + additional_sense_code = sense_buf[2]; + } + if (len > 3) { + additional_sense_code_qualifier = sense_buf[3]; + } + if (len > 7) { + additional_length = sense_buf[7]; + } + } else { + /* fixed format, SPC-6 4.4.3 */ + if (len > 2) { + sense_key = 0xf & sense_buf[2]; + } + if (len > 7) { + additional_length = sense_buf[7]; + } + if (len > 12) { + additional_sense_code = sense_buf[12]; + } + if (len > 13) { + additional_sense_code_qualifier = sense_buf[13]; + } + } + + switch (sense_key) { + case NO_SENSE: + case 0x0f: /* COMPLETED, not present in kernel headers */ + ALOGD("SCSI success with sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key, + additional_sense_code, additional_sense_code_qualifier); + return SCSI_RES_OK; + case UNIT_ATTENTION: + ALOGD("UNIT ATTENTION with sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key, + additional_sense_code, additional_sense_code_qualifier); + if (additional_sense_code == 0x29) { + /* POWER ON or RESET condition */ + return SCSI_RES_RETRY; + } + + /* treat this UNIT ATTENTION as an error if we don't recognize it */ + break; + } + + ALOGE("Unexpected SCSI sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key, + additional_sense_code, additional_sense_code_qualifier); + log_buf(ANDROID_LOG_ERROR, "sense buffer: ", sense_buf, len); + return SCSI_RES_ERR; +} + +static enum scsi_result check_sg_io_hdr(const sg_io_hdr_t* io_hdrp) { + if (io_hdrp->status == 0 && io_hdrp->host_status == 0 && io_hdrp->driver_status == 0) { + return SCSI_RES_OK; + } + + if (io_hdrp->status & 0x01) { + ALOGE("SG_IO received unknown status, LSB is set: %hhu", io_hdrp->status); + } + + if (io_hdrp->masked_status != GOOD && io_hdrp->sb_len_wr > 0) { + enum scsi_result scsi_res = check_scsi_sense(io_hdrp->sbp, io_hdrp->sb_len_wr); + if (scsi_res == SCSI_RES_RETRY) { + return SCSI_RES_RETRY; + } else if (scsi_res != SCSI_RES_OK) { + ALOGE("Unexpected SCSI sense. masked_status: %hhu, host_status: %hu, driver_status: " + "%hu\n", + io_hdrp->masked_status, io_hdrp->host_status, io_hdrp->driver_status); + return scsi_res; + } + } + + switch (io_hdrp->masked_status) { + case GOOD: + break; + case CHECK_CONDITION: + /* handled by check_sg_sense above */ + break; + default: + ALOGE("SG_IO failed with masked_status: %hhu, host_status: %hu, driver_status: %hu\n", + io_hdrp->masked_status, io_hdrp->host_status, io_hdrp->driver_status); + return SCSI_RES_ERR; + } + + if (io_hdrp->host_status != 0) { + ALOGE("SG_IO failed with host_status: %hu, driver_status: %hu\n", io_hdrp->host_status, + io_hdrp->driver_status); + } + + if (io_hdrp->resid != 0) { + ALOGE("SG_IO resid was non-zero: %d\n", io_hdrp->resid); + } + return SCSI_RES_ERR; +} + static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req) { struct { struct mmc_ioc_multi_cmd multi; @@ -153,7 +339,7 @@ static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req mmc_ioc_cmd_set_data((*cmd), write_buf); #ifdef RPMB_DEBUG ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag); - print_buf("request: ", write_buf, req->reliable_write_size); + log_buf(ANDROID_LOG_INFO, "request: ", write_buf, req->reliable_write_size); #endif write_buf += req->reliable_write_size; mmc.multi.num_of_cmds++; @@ -169,7 +355,7 @@ static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req mmc_ioc_cmd_set_data((*cmd), write_buf); #ifdef RPMB_DEBUG ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag); - print_buf("request: ", write_buf, req->write_size); + log_buf(ANDROID_LOG_INFO, "request: ", write_buf, req->write_size); #endif write_buf += req->write_size; mmc.multi.num_of_cmds++; @@ -207,6 +393,8 @@ static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req) struct sec_proto_cdb out_cdb = {0xB5, 0xEC, 0x00, 0x01, 0x00, 0x00, 0, 0x00, 0x00}; unsigned char sense_buffer[32]; + bool is_request_write = req->reliable_write_size > 0; + wl_rc = acquire_wake_lock(PARTIAL_WAKE_LOCK, UFS_WAKE_LOCK_NAME); if (wl_rc < 0) { ALOGE("%s: failed to acquire wakelock: %d, %s\n", __func__, wl_rc, strerror(errno)); @@ -215,30 +403,44 @@ static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req) if (req->reliable_write_size) { /* Prepare SECURITY PROTOCOL OUT command. */ - out_cdb.length = __builtin_bswap32(req->reliable_write_size); sg_io_hdr_t io_hdr; - set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer), - req->reliable_write_size, (void*)write_buf, (unsigned char*)&out_cdb, - sense_buffer); - rc = ioctl(sg_fd, SG_IO, &io_hdr); - if (rc < 0) { - ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno)); - goto err_op; - } + int retry_count = UFS_RPMB_WRITE_RETRY_COUNT; + do { + out_cdb.length = __builtin_bswap32(req->reliable_write_size); + set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer), + req->reliable_write_size, (void*)write_buf, (unsigned char*)&out_cdb, + sense_buffer); + rc = ioctl(sg_fd, SG_IO, &io_hdr); + if (rc < 0) { + ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno)); + goto err_op; + } + } while (check_sg_io_hdr(&io_hdr) == SCSI_RES_RETRY && retry_count-- > 0); write_buf += req->reliable_write_size; } if (req->write_size) { /* Prepare SECURITY PROTOCOL OUT command. */ - out_cdb.length = __builtin_bswap32(req->write_size); sg_io_hdr_t io_hdr; - set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer), - req->write_size, (void*)write_buf, (unsigned char*)&out_cdb, sense_buffer); - rc = ioctl(sg_fd, SG_IO, &io_hdr); - if (rc < 0) { - ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno)); - goto err_op; - } + /* + * We don't retry write response request messages (is_request_write == + * true) because a unit attention condition between the write and + * requesting a response means that the device was reset and we can't + * get a response to our original write. We can only retry this SG_IO + * call when it is the first call in our sequence. + */ + int retry_count = is_request_write ? 0 : UFS_RPMB_READ_RETRY_COUNT; + do { + out_cdb.length = __builtin_bswap32(req->write_size); + set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer), + req->write_size, (void*)write_buf, (unsigned char*)&out_cdb, + sense_buffer); + rc = ioctl(sg_fd, SG_IO, &io_hdr); + if (rc < 0) { + ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno)); + goto err_op; + } + } while (check_sg_io_hdr(&io_hdr) == SCSI_RES_RETRY && retry_count-- > 0); write_buf += req->write_size; } @@ -252,6 +454,7 @@ static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req) if (rc < 0) { ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno)); } + check_sg_io_hdr(&io_hdr); } err_op: @@ -353,7 +556,7 @@ int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len) { goto err_response; } #ifdef RPMB_DEBUG - if (req->read_size) print_buf("response: ", read_buf, req->read_size); + if (req->read_size) log_buf(ANDROID_LOG_INFO, "response: ", read_buf, req->read_size); #endif if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) { |