diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-11-10 20:02:11 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-11-10 20:02:11 +0000 |
commit | b6a1d78c5b4694eb25d1578eff0b5c9710d115e4 (patch) | |
tree | 1ac1c96b026db1cfe77aa38ba49173a798f180c4 | |
parent | 1d37c4499249b17f143df12cebc5ee9ea3600ce3 (diff) | |
parent | 753d79a099a11621991aa350c1cccdb48adddbd5 (diff) | |
download | core-android-platform-12.0.0_r21.tar.gz |
Snap for 7901677 from 753d79a099a11621991aa350c1cccdb48adddbd5 to sc-platform-releaseandroid-platform-12.0.0_r9android-platform-12.0.0_r8android-platform-12.0.0_r7android-platform-12.0.0_r6android-platform-12.0.0_r5android-platform-12.0.0_r4android-platform-12.0.0_r3android-platform-12.0.0_r27android-platform-12.0.0_r26android-platform-12.0.0_r25android-platform-12.0.0_r24android-platform-12.0.0_r23android-platform-12.0.0_r22android-platform-12.0.0_r21android-platform-12.0.0_r20android-platform-12.0.0_r2android-platform-12.0.0_r19android-platform-12.0.0_r18android-platform-12.0.0_r17android-platform-12.0.0_r16android-platform-12.0.0_r15android-platform-12.0.0_r14android-platform-12.0.0_r13android-platform-12.0.0_r12android-platform-12.0.0_r11android-platform-12.0.0_r10
Change-Id: I38caae22077efe8585b55c99deb51d9b8f7c15c4
-rw-r--r-- | fastboot/device/flashing.cpp | 18 | ||||
-rw-r--r-- | fs_mgr/Android.bp | 1 | ||||
-rw-r--r-- | fs_mgr/blockdev.cpp | 159 | ||||
-rw-r--r-- | fs_mgr/blockdev.h | 21 | ||||
-rw-r--r-- | fs_mgr/fs_mgr.cpp | 17 | ||||
-rw-r--r-- | fs_mgr/fs_mgr_fstab.cpp | 17 | ||||
-rw-r--r-- | fs_mgr/libdm/dm.cpp | 30 | ||||
-rw-r--r-- | fs_mgr/libdm/include/libdm/dm.h | 4 | ||||
-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 | 3 | ||||
-rw-r--r-- | trusty/keymaster/Android.bp | 1 | ||||
-rw-r--r-- | trusty/keymaster/ipc/trusty_keymaster_ipc.cpp | 118 | ||||
-rw-r--r-- | trusty/storage/proxy/rpmb.c | 263 |
22 files changed, 825 insertions, 106 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/Android.bp b/fs_mgr/Android.bp index 5356b006d..3c83aabba 100644 --- a/fs_mgr/Android.bp +++ b/fs_mgr/Android.bp @@ -63,6 +63,7 @@ cc_defaults { "-D_FILE_OFFSET_BITS=64", ], srcs: [ + "blockdev.cpp", "file_wait.cpp", "fs_mgr.cpp", "fs_mgr_format.cpp", diff --git a/fs_mgr/blockdev.cpp b/fs_mgr/blockdev.cpp new file mode 100644 index 000000000..14b217ca8 --- /dev/null +++ b/fs_mgr/blockdev.cpp @@ -0,0 +1,159 @@ +/* + * 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 <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> + +#include <dirent.h> +#include <libdm/dm.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <sys/types.h> +#include "blockdev.h" + +using android::base::Basename; +using android::base::ErrnoError; +using android::base::Error; +using android::base::Result; +using android::base::ResultError; +using android::base::StartsWith; +using android::base::StringPrintf; +using android::base::unique_fd; +using android::dm::DeviceMapper; + +// Return the parent device of a partition. Converts e.g. "sda26" into "sda". +static std::string PartitionParent(const std::string& blockdev) { + if (blockdev.find('/') != std::string::npos) { + LOG(ERROR) << __func__ << ": invalid argument " << blockdev; + return blockdev; + } + auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/sys/class/block"), closedir}; + if (!dir) { + return blockdev; + } + for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) { + if (ent->d_name[0] == '.') { + continue; + } + std::string path = StringPrintf("/sys/class/block/%s/%s", ent->d_name, blockdev.c_str()); + struct stat statbuf; + if (stat(path.c_str(), &statbuf) >= 0) { + return ent->d_name; + } + } + return blockdev; +} + +// Convert a major:minor pair into a block device name. +static std::string BlockdevName(dev_t dev) { + auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/dev/block"), closedir}; + if (!dir) { + return {}; + } + for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) { + if (ent->d_name[0] == '.') { + continue; + } + const std::string path = std::string("/dev/block/") + ent->d_name; + struct stat statbuf; + if (stat(path.c_str(), &statbuf) >= 0 && dev == statbuf.st_rdev) { + return ent->d_name; + } + } + return {}; +} + +// Trim whitespace from the end of a string. +static void rtrim(std::string& s) { + s.erase(s.find_last_not_of('\n') + 1, s.length()); +} + +// For file `file_path`, retrieve the block device backing the filesystem on +// which the file exists and return the queue depth of the block device. +static Result<uint32_t> BlockDeviceQueueDepth(const std::string& file_path) { + struct stat statbuf; + int res = stat(file_path.c_str(), &statbuf); + if (res < 0) { + return ErrnoError() << "stat(" << file_path << ")"; + } + std::string blockdev = "/dev/block/" + BlockdevName(statbuf.st_dev); + LOG(DEBUG) << __func__ << ": " << file_path << " -> " << blockdev; + if (blockdev.empty()) { + const std::string err_msg = + StringPrintf("Failed to convert %u:%u (path %s)", major(statbuf.st_dev), + minor(statbuf.st_dev), file_path.c_str()); + return ResultError(err_msg, 0); + } + auto& dm = DeviceMapper::Instance(); + for (;;) { + std::optional<std::string> child = dm.GetParentBlockDeviceByPath(blockdev); + if (!child) { + break; + } + LOG(DEBUG) << __func__ << ": " << blockdev << " -> " << *child; + blockdev = *child; + } + std::optional<std::string> maybe_blockdev = android::dm::ExtractBlockDeviceName(blockdev); + if (!maybe_blockdev) { + return ResultError("Failed to remove /dev/block/ prefix from " + blockdev, 0); + } + blockdev = PartitionParent(*maybe_blockdev); + LOG(DEBUG) << __func__ << ": " + << "Partition parent: " << blockdev; + const std::string nr_tags_path = + StringPrintf("/sys/class/block/%s/mq/0/nr_tags", blockdev.c_str()); + std::string nr_tags; + if (!android::base::ReadFileToString(nr_tags_path, &nr_tags)) { + return ResultError("Failed to read " + nr_tags_path, 0); + } + rtrim(nr_tags); + LOG(DEBUG) << __func__ << ": " << file_path << " is backed by /dev/" << blockdev + << " and that block device supports queue depth " << nr_tags; + return strtol(nr_tags.c_str(), NULL, 0); +} + +// Set 'nr_requests' of `loop_device_path` to the queue depth of the block +// device backing `file_path`. +Result<void> ConfigureQueueDepth(const std::string& loop_device_path, + const std::string& file_path) { + if (!StartsWith(loop_device_path, "/dev/")) { + return Error() << "Invalid argument " << loop_device_path; + } + + const std::string loop_device_name = Basename(loop_device_path); + + const Result<uint32_t> qd = BlockDeviceQueueDepth(file_path); + if (!qd.ok()) { + LOG(DEBUG) << __func__ << ": " + << "BlockDeviceQueueDepth() returned " << qd.error(); + return ResultError(qd.error()); + } + const std::string nr_requests = StringPrintf("%u", *qd); + const std::string sysfs_path = + StringPrintf("/sys/class/block/%s/queue/nr_requests", loop_device_name.c_str()); + unique_fd sysfs_fd(open(sysfs_path.c_str(), O_RDWR | O_CLOEXEC)); + if (sysfs_fd == -1) { + return ErrnoError() << "Failed to open " << sysfs_path; + } + + const int res = write(sysfs_fd.get(), nr_requests.data(), nr_requests.length()); + if (res < 0) { + return ErrnoError() << "Failed to write to " << sysfs_path; + } + return {}; +} diff --git a/fs_mgr/blockdev.h b/fs_mgr/blockdev.h new file mode 100644 index 000000000..2c0d68a77 --- /dev/null +++ b/fs_mgr/blockdev.h @@ -0,0 +1,21 @@ +/* + * 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 <android-base/result.h> +#include <string> + +android::base::Result<void> ConfigureQueueDepth(const std::string& loop_device_path, + const std::string& file_path); diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp index af71fe6d5..21df8af8c 100644 --- a/fs_mgr/fs_mgr.cpp +++ b/fs_mgr/fs_mgr.cpp @@ -70,6 +70,7 @@ #include <log/log_properties.h> #include <logwrap/logwrap.h> +#include "blockdev.h" #include "fs_mgr_priv.h" #define KEY_LOC_PROP "ro.crypto.keyfile.userdata" @@ -2065,22 +2066,24 @@ static bool PrepareZramBackingDevice(off64_t size) { // Allocate loop device and attach it to file_path. LoopControl loop_control; - std::string device; - if (!loop_control.Attach(target_fd.get(), 5s, &device)) { + std::string loop_device; + if (!loop_control.Attach(target_fd.get(), 5s, &loop_device)) { return false; } + ConfigureQueueDepth(loop_device, "/"); + // set block size & direct IO - unique_fd device_fd(TEMP_FAILURE_RETRY(open(device.c_str(), O_RDWR | O_CLOEXEC))); - if (device_fd.get() == -1) { - PERROR << "Cannot open " << device; + unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loop_device.c_str(), O_RDWR | O_CLOEXEC))); + if (loop_fd.get() == -1) { + PERROR << "Cannot open " << loop_device; return false; } - if (!LoopControl::EnableDirectIo(device_fd.get())) { + if (!LoopControl::EnableDirectIo(loop_fd.get())) { return false; } - return InstallZramDevice(device); + return InstallZramDevice(loop_device); } bool fs_mgr_swapon_all(const Fstab& fstab) { diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp index d0c89b909..f5ab5571f 100644 --- a/fs_mgr/fs_mgr_fstab.cpp +++ b/fs_mgr/fs_mgr_fstab.cpp @@ -413,17 +413,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/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp index c4874b8d1..e43c00b44 100644 --- a/fs_mgr/libdm/dm.cpp +++ b/fs_mgr/libdm/dm.cpp @@ -560,34 +560,30 @@ std::string DeviceMapper::GetTargetType(const struct dm_target_spec& spec) { return std::string{spec.target_type, sizeof(spec.target_type)}; } -static bool ExtractBlockDeviceName(const std::string& path, std::string* name) { +std::optional<std::string> ExtractBlockDeviceName(const std::string& path) { static constexpr std::string_view kDevBlockPrefix("/dev/block/"); if (android::base::StartsWith(path, kDevBlockPrefix)) { - *name = path.substr(kDevBlockPrefix.length()); - return true; + return path.substr(kDevBlockPrefix.length()); } - return false; + return {}; } bool DeviceMapper::IsDmBlockDevice(const std::string& path) { - std::string name; - if (!ExtractBlockDeviceName(path, &name)) { - return false; - } - return android::base::StartsWith(name, "dm-"); + std::optional<std::string> name = ExtractBlockDeviceName(path); + return name && android::base::StartsWith(*name, "dm-"); } std::optional<std::string> DeviceMapper::GetDmDeviceNameByPath(const std::string& path) { - std::string name; - if (!ExtractBlockDeviceName(path, &name)) { + std::optional<std::string> name = ExtractBlockDeviceName(path); + if (!name) { LOG(WARNING) << path << " is not a block device"; return std::nullopt; } - if (!android::base::StartsWith(name, "dm-")) { + if (!android::base::StartsWith(*name, "dm-")) { LOG(WARNING) << path << " is not a dm device"; return std::nullopt; } - std::string dm_name_file = "/sys/block/" + name + "/dm/name"; + std::string dm_name_file = "/sys/block/" + *name + "/dm/name"; std::string dm_name; if (!android::base::ReadFileToString(dm_name_file, &dm_name)) { PLOG(ERROR) << "Failed to read file " << dm_name_file; @@ -598,16 +594,16 @@ std::optional<std::string> DeviceMapper::GetDmDeviceNameByPath(const std::string } std::optional<std::string> DeviceMapper::GetParentBlockDeviceByPath(const std::string& path) { - std::string name; - if (!ExtractBlockDeviceName(path, &name)) { + std::optional<std::string> name = ExtractBlockDeviceName(path); + if (!name) { LOG(WARNING) << path << " is not a block device"; return std::nullopt; } - if (!android::base::StartsWith(name, "dm-")) { + if (!android::base::StartsWith(*name, "dm-")) { // Reached bottom of the device mapper stack. return std::nullopt; } - auto slaves_dir = "/sys/block/" + name + "/slaves"; + auto slaves_dir = "/sys/block/" + *name + "/slaves"; auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(slaves_dir.c_str()), closedir); if (dir == nullptr) { PLOG(ERROR) << "Failed to open: " << slaves_dir; diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h index 70b14fa46..bdbbf9112 100644 --- a/fs_mgr/libdm/include/libdm/dm.h +++ b/fs_mgr/libdm/include/libdm/dm.h @@ -49,6 +49,10 @@ enum class DmDeviceState { INVALID, SUSPENDED, ACTIVE }; static constexpr uint64_t kSectorSize = 512; +// Returns `path` without /dev/block prefix if and only if `path` starts with +// that prefix. +std::optional<std::string> ExtractBlockDeviceName(const std::string& path); + class DeviceMapper final { public: class DmBlockDevice final { 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 a7325cad9..942feb939 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_ @@ -554,6 +555,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. @@ -824,6 +838,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 376a678a8..d10689a01 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -590,9 +590,10 @@ on late-fs # Load trusted keys from dm-verity protected partitions exec -- /system/bin/fsverity_init --load-verified-keys +on late-fs && property:ro.product.cpu.abilist64=* # 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/keymaster/Android.bp b/trusty/keymaster/Android.bp index ff6460de8..cf056f091 100644 --- a/trusty/keymaster/Android.bp +++ b/trusty/keymaster/Android.bp @@ -120,6 +120,7 @@ cc_binary { ], required: [ "android.hardware.hardware_keystore.xml", + "RemoteProvisioner", ], } diff --git a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp index 0956fe6b8..2d4400989 100644 --- a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp +++ b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp @@ -25,6 +25,8 @@ #include <unistd.h> #include <algorithm> +#include <variant> +#include <vector> #include <log/log.h> #include <trusty/tipc.h> @@ -46,8 +48,27 @@ int trusty_keymaster_connect() { return 0; } -int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out, - uint32_t* out_size) { +class VectorEraser { + public: + VectorEraser(std::vector<uint8_t>* v) : _v(v) {} + ~VectorEraser() { + if (_v) { + std::fill(const_cast<volatile uint8_t*>(_v->data()), + const_cast<volatile uint8_t*>(_v->data() + _v->size()), 0); + } + } + void disarm() { _v = nullptr; } + VectorEraser(const VectorEraser&) = delete; + VectorEraser& operator=(const VectorEraser&) = delete; + VectorEraser(VectorEraser&& other) = delete; + VectorEraser& operator=(VectorEraser&&) = delete; + + private: + std::vector<uint8_t>* _v; +}; + +std::variant<int, std::vector<uint8_t>> trusty_keymaster_call_2(uint32_t cmd, void* in, + uint32_t in_size) { if (handle_ < 0) { ALOGE("not connected\n"); return -EINVAL; @@ -70,15 +91,38 @@ int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno)); return -errno; } - size_t out_max_size = *out_size; - *out_size = 0; + + std::vector<uint8_t> out(TRUSTY_KEYMASTER_RECV_BUF_SIZE); + VectorEraser out_eraser(&out); + uint8_t* write_pos = out.data(); + uint8_t* out_end = out.data() + out.size(); + struct iovec iov[2]; struct keymaster_message header; iov[0] = {.iov_base = &header, .iov_len = sizeof(struct keymaster_message)}; while (true) { - iov[1] = {.iov_base = out + *out_size, - .iov_len = std::min<uint32_t>(KEYMASTER_MAX_BUFFER_LENGTH, - out_max_size - *out_size)}; + if (out_end - write_pos < KEYMASTER_MAX_BUFFER_LENGTH) { + // In stead of using std::vector.resize(), allocate a new one to have chance + // at zeroing the old buffer. + std::vector<uint8_t> new_out(out.size() + KEYMASTER_MAX_BUFFER_LENGTH); + // After the swap below this erases the old out buffer. + VectorEraser new_out_eraser(&new_out); + std::copy(out.data(), write_pos, new_out.begin()); + + auto write_offset = write_pos - out.data(); + + std::swap(new_out, out); + + write_pos = out.data() + write_offset; + out_end = out.data() + out.size(); + } + size_t buffer_size = 0; + if (__builtin_sub_overflow(reinterpret_cast<uintptr_t>(out_end), + reinterpret_cast<uintptr_t>(write_pos), &buffer_size)) { + return -EOVERFLOW; + } + iov[1] = {.iov_base = write_pos, .iov_len = buffer_size}; + rc = readv(handle_, iov, 2); if (rc < 0) { ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, @@ -95,13 +139,36 @@ int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out ALOGE("invalid command (%d)", header.cmd); return -EINVAL; } - *out_size += ((size_t)rc - sizeof(struct keymaster_message)); + write_pos += ((size_t)rc - sizeof(struct keymaster_message)); if (header.cmd & KEYMASTER_STOP_BIT) { break; } } - return rc; + out.resize(write_pos - out.data()); + out_eraser.disarm(); + return out; +} + +int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out, + uint32_t* out_size) { + auto result = trusty_keymaster_call_2(cmd, in, in_size); + if (auto out_buffer = std::get_if<std::vector<uint8_t>>(&result)) { + if (out_buffer->size() <= *out_size) { + std::copy(out_buffer->begin(), out_buffer->end(), out); + std::fill(const_cast<volatile uint8_t*>(&*out_buffer->begin()), + const_cast<volatile uint8_t*>(&*out_buffer->end()), 0); + + *out_size = out_buffer->size(); + return 0; + } else { + ALOGE("Message was to large (%zu) for the provided buffer (%u)", out_buffer->size(), + *out_size); + return -EMSGSIZE; + } + } else { + return std::get<int>(result); + } } void trusty_keymaster_disconnect() { @@ -155,28 +222,27 @@ keymaster_error_t trusty_keymaster_send(uint32_t command, const keymaster::Seria req.Serialize(send_buf, send_buf + req_size); // Send it - uint8_t recv_buf[TRUSTY_KEYMASTER_RECV_BUF_SIZE]; - keymaster::Eraser recv_buf_eraser(recv_buf, TRUSTY_KEYMASTER_RECV_BUF_SIZE); - uint32_t rsp_size = TRUSTY_KEYMASTER_RECV_BUF_SIZE; - int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size); - if (rc < 0) { + auto response = trusty_keymaster_call_2(command, send_buf, req_size); + if (auto response_buffer = std::get_if<std::vector<uint8_t>>(&response)) { + keymaster::Eraser response_buffer_erasor(response_buffer->data(), response_buffer->size()); + ALOGV("Received %zu byte response\n", response_buffer->size()); + + const uint8_t* p = response_buffer->data(); + if (!rsp->Deserialize(&p, p + response_buffer->size())) { + ALOGE("Error deserializing response of size %zu\n", response_buffer->size()); + return KM_ERROR_UNKNOWN_ERROR; + } else if (rsp->error != KM_ERROR_OK) { + ALOGE("Response of size %zu contained error code %d\n", response_buffer->size(), + (int)rsp->error); + } + return rsp->error; + } else { + auto rc = std::get<int>(response); // Reset the connection on tipc error trusty_keymaster_disconnect(); trusty_keymaster_connect(); ALOGE("tipc error: %d\n", rc); // TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately. return translate_error(rc); - } else { - ALOGV("Received %d byte response\n", rsp_size); - } - - const uint8_t* p = recv_buf; - if (!rsp->Deserialize(&p, p + rsp_size)) { - ALOGE("Error deserializing response of size %d\n", (int)rsp_size); - return KM_ERROR_UNKNOWN_ERROR; - } else if (rsp->error != KM_ERROR_OK) { - ALOGE("Response of size %d contained error code %d\n", (int)rsp_size, (int)rsp->error); - return rsp->error; } - return rsp->error; } 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) { |