Merge "libadf: delete libadf & libadfhwc"
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 38c6bf8..36e1169 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -34,7 +34,19 @@
     MERGE_COMPLETED = 3;
 }
 
-// Next: 10
+// Next: 3
+enum MergePhase {
+    // No merge is in progress.
+    NO_MERGE = 0;
+
+    // Shrunk partitions can merge.
+    FIRST_PHASE = 1;
+
+    // Grown partitions can merge.
+    SECOND_PHASE = 2;
+}
+
+// Next: 11
 message SnapshotStatus {
     // Name of the snapshot. This is usually the name of the snapshotted
     // logical partition; for example, "system_b".
@@ -87,6 +99,9 @@
 
     // True if compression is enabled, false otherwise.
     bool compression_enabled = 9;
+
+    // The old partition size (if none existed, this will be zero).
+    uint64 old_partition_size = 10;
 }
 
 // Next: 8
@@ -118,7 +133,7 @@
     Cancelled = 7;
 };
 
-// Next: 6
+// Next: 7
 message SnapshotUpdateStatus {
     UpdateState state = 1;
 
@@ -136,6 +151,9 @@
 
     // Whether compression/dm-user was used for any snapshots.
     bool compression_enabled = 5;
+
+    // Merge phase (if state == MERGING).
+    MergePhase merge_phase = 6;
 }
 
 // Next: 4
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 397ff2e..0a8567f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -463,6 +463,10 @@
                       const std::string& base_device, const std::chrono::milliseconds& timeout_ms,
                       std::string* path);
 
+    // Map the source device used for dm-user.
+    bool MapSourceDevice(LockedFile* lock, const std::string& name,
+                         const std::chrono::milliseconds& timeout_ms, std::string* path);
+
     // Map a COW image that was previous created with CreateCowImage.
     std::optional<std::string> MapCowImage(const std::string& name,
                                            const std::chrono::milliseconds& timeout_ms);
@@ -521,11 +525,13 @@
     std::string GetMergeStateFilePath() const;
 
     // Helpers for merging.
+    bool MergeSecondPhaseSnapshots(LockedFile* lock);
     bool SwitchSnapshotToMerge(LockedFile* lock, const std::string& name);
     bool RewriteSnapshotDeviceTable(const std::string& dm_name);
     bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name);
     void AcknowledgeMergeSuccess(LockedFile* lock);
     void AcknowledgeMergeFailure();
+    MergePhase DecideMergePhase(const SnapshotStatus& status);
     std::unique_ptr<LpMetadata> ReadCurrentMetadata();
 
     enum class MetadataPartitionState {
@@ -558,7 +564,8 @@
     //   UpdateState::MergeNeedsReboot
     UpdateState CheckMergeState(const std::function<bool()>& before_cancel);
     UpdateState CheckMergeState(LockedFile* lock, const std::function<bool()>& before_cancel);
-    UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name);
+    UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name,
+                                      const SnapshotUpdateStatus& update_status);
 
     // Interact with status files under /metadata/ota/snapshots.
     bool WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus& status);
@@ -568,12 +575,9 @@
     std::string GetSnapshotBootIndicatorPath();
     std::string GetRollbackIndicatorPath();
     std::string GetForwardMergeIndicatorPath();
+    std::string GetOldPartitionMetadataPath();
 
-    // Return the name of the device holding the "snapshot" or "snapshot-merge"
-    // target. This may not be the final device presented via MapSnapshot(), if
-    // for example there is a linear segment.
-    std::string GetSnapshotDeviceName(const std::string& snapshot_name,
-                                      const SnapshotStatus& status);
+    const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);
 
     bool MapAllPartitions(LockedFile* lock, const std::string& super_device, uint32_t slot,
                           const std::chrono::milliseconds& timeout_ms);
@@ -673,8 +677,8 @@
 
     // Helper for RemoveAllSnapshots.
     // Check whether |name| should be deleted as a snapshot name.
-    bool ShouldDeleteSnapshot(LockedFile* lock, const std::map<std::string, bool>& flashing_status,
-                              Slot current_slot, const std::string& name);
+    bool ShouldDeleteSnapshot(const std::map<std::string, bool>& flashing_status, Slot current_slot,
+                              const std::string& name);
 
     // Create or delete forward merge indicator given |wipe|. Iff wipe is scheduled,
     // allow forward merge on FDR.
@@ -722,6 +726,7 @@
     bool in_factory_data_reset_ = false;
     std::function<bool(const std::string&)> uevent_regen_callback_;
     std::unique_ptr<SnapuserdClient> snapuserd_client_;
+    std::unique_ptr<LpMetadata> old_partition_metadata_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 21b720e..ebda430 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -132,8 +132,8 @@
     return partition_name + "-base";
 }
 
-static std::string GetSnapshotExtraDeviceName(const std::string& snapshot_name) {
-    return snapshot_name + "-inner";
+static std::string GetSourceDeviceName(const std::string& partition_name) {
+    return partition_name + "-src";
 }
 
 bool SnapshotManager::BeginUpdate() {
@@ -157,6 +157,9 @@
         images_->RemoveAllImages();
     }
 
+    // Clear any cached metadata (this allows re-using one manager across tests).
+    old_partition_metadata_ = nullptr;
+
     auto state = ReadUpdateState(file.get());
     if (state != UpdateState::None) {
         LOG(ERROR) << "An update is already in progress, cannot begin a new update";
@@ -255,6 +258,7 @@
             GetSnapshotBootIndicatorPath(),
             GetRollbackIndicatorPath(),
             GetForwardMergeIndicatorPath(),
+            GetOldPartitionMetadataPath(),
     };
     for (const auto& file : files) {
         RemoveFileIfExists(file);
@@ -465,8 +469,13 @@
         LOG(ERROR) << "Invalid snapshot size for " << base_device << ": " << status.snapshot_size();
         return false;
     }
+    if (status.device_size() != status.snapshot_size()) {
+        LOG(ERROR) << "Device size and snapshot size must be the same (device size = "
+                   << status.device_size() << ", snapshot size = " << status.snapshot_size();
+        return false;
+    }
+
     uint64_t snapshot_sectors = status.snapshot_size() / kSectorSize;
-    uint64_t linear_sectors = (status.device_size() - status.snapshot_size()) / kSectorSize;
 
     auto& dm = DeviceMapper::Instance();
 
@@ -474,7 +483,8 @@
     // have completed merging, but the start of the merge process is considered
     // atomic.
     SnapshotStorageMode mode;
-    switch (ReadUpdateState(lock)) {
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+    switch (update_status.state()) {
         case UpdateState::MergeCompleted:
         case UpdateState::MergeNeedsReboot:
             LOG(ERROR) << "Should not create a snapshot device for " << name
@@ -484,52 +494,24 @@
         case UpdateState::MergeFailed:
             // Note: MergeFailed indicates that a merge is in progress, but
             // is possibly stalled. We still have to honor the merge.
-            mode = SnapshotStorageMode::Merge;
+            if (DecideMergePhase(status) == update_status.merge_phase()) {
+                mode = SnapshotStorageMode::Merge;
+            } else {
+                mode = SnapshotStorageMode::Persistent;
+            }
             break;
         default:
             mode = SnapshotStorageMode::Persistent;
             break;
     }
 
-    // The kernel (tested on 4.19) crashes horribly if a device has both a snapshot
-    // and a linear target in the same table. Instead, we stack them, and give the
-    // snapshot device a different name. It is not exposed to the caller in this
-    // case.
-    auto snap_name = (linear_sectors > 0) ? GetSnapshotExtraDeviceName(name) : name;
-
     DmTable table;
     table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_device, mode,
                                     kSnapshotChunkSize);
-    if (!dm.CreateDevice(snap_name, table, dev_path, timeout_ms)) {
-        LOG(ERROR) << "Could not create snapshot device: " << snap_name;
+    if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
+        LOG(ERROR) << "Could not create snapshot device: " << name;
         return false;
     }
-
-    if (linear_sectors) {
-        std::string snap_dev;
-        if (!dm.GetDeviceString(snap_name, &snap_dev)) {
-            LOG(ERROR) << "Cannot determine major/minor for: " << snap_name;
-            return false;
-        }
-
-        // Our stacking will looks like this:
-        //     [linear, linear] ; to snapshot, and non-snapshot region of base device
-        //     [snapshot-inner]
-        //     [base device]   [cow]
-        DmTable table;
-        table.Emplace<DmTargetLinear>(0, snapshot_sectors, snap_dev, 0);
-        table.Emplace<DmTargetLinear>(snapshot_sectors, linear_sectors, base_device,
-                                      snapshot_sectors);
-        if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
-            LOG(ERROR) << "Could not create outer snapshot device: " << name;
-            dm.DeleteDevice(snap_name);
-            return false;
-        }
-    }
-
-    // :TODO: when merging is implemented, we need to add an argument to the
-    // status indicating how much progress is left to merge. (device-mapper
-    // does not retain the initial values, so we can't derive them.)
     return true;
 }
 
@@ -557,6 +539,36 @@
     return std::nullopt;
 }
 
+bool SnapshotManager::MapSourceDevice(LockedFile* lock, const std::string& name,
+                                      const std::chrono::milliseconds& timeout_ms,
+                                      std::string* path) {
+    CHECK(lock);
+
+    auto metadata = ReadOldPartitionMetadata(lock);
+    if (!metadata) {
+        LOG(ERROR) << "Could not map source device due to missing or corrupt metadata";
+        return false;
+    }
+
+    auto old_name = GetOtherPartitionName(name);
+    auto slot_suffix = device_->GetSlotSuffix();
+    auto slot = SlotNumberForSlotSuffix(slot_suffix);
+
+    CreateLogicalPartitionParams params = {
+            .block_device = device_->GetSuperDevice(slot),
+            .metadata = metadata,
+            .partition_name = old_name,
+            .timeout_ms = timeout_ms,
+            .device_name = GetSourceDeviceName(name),
+            .partition_opener = &device_->GetPartitionOpener(),
+    };
+    if (!CreateLogicalPartition(std::move(params), path)) {
+        LOG(ERROR) << "Could not create source device for snapshot " << name;
+        return false;
+    }
+    return true;
+}
+
 bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
     CHECK(lock);
 
@@ -565,13 +577,6 @@
         LOG(ERROR) << "Could not delete snapshot device: " << name;
         return false;
     }
-
-    auto snapshot_extra_device = GetSnapshotExtraDeviceName(name);
-    if (!dm.DeleteDeviceIfExists(snapshot_extra_device)) {
-        LOG(ERROR) << "Could not delete snapshot inner device: " << snapshot_extra_device;
-        return false;
-    }
-
     return true;
 }
 
@@ -678,6 +683,8 @@
 
     bool compression_enabled = false;
 
+    std::vector<std::string> first_merge_group;
+
     uint64_t total_cow_file_size = 0;
     DmTargetSnapshot::Status initial_target_values = {};
     for (const auto& snapshot : snapshots) {
@@ -696,6 +703,9 @@
         total_cow_file_size += snapshot_status.cow_file_size();
 
         compression_enabled |= snapshot_status.compression_enabled();
+        if (DecideMergePhase(snapshot_status) == MergePhase::FIRST_PHASE) {
+            first_merge_group.emplace_back(snapshot);
+        }
     }
 
     if (cow_file_size) {
@@ -709,14 +719,26 @@
     initial_status.set_metadata_sectors(initial_target_values.metadata_sectors);
     initial_status.set_compression_enabled(compression_enabled);
 
+    // If any partitions shrunk, we need to merge them before we merge any other
+    // partitions (see b/177935716). Otherwise, a merge from another partition
+    // may overwrite the source block of a copy operation.
+    const std::vector<std::string>* merge_group;
+    if (first_merge_group.empty()) {
+        merge_group = &snapshots;
+        initial_status.set_merge_phase(MergePhase::SECOND_PHASE);
+    } else {
+        merge_group = &first_merge_group;
+        initial_status.set_merge_phase(MergePhase::FIRST_PHASE);
+    }
+
     // Point of no return - mark that we're starting a merge. From now on every
-    // snapshot must be a merge target.
+    // eligible snapshot must be a merge target.
     if (!WriteSnapshotUpdateStatus(lock.get(), initial_status)) {
         return false;
     }
 
     bool rewrote_all = true;
-    for (const auto& snapshot : snapshots) {
+    for (const auto& snapshot : *merge_group) {
         // If this fails, we have no choice but to continue. Everything must
         // be merged. This is not an ideal state to be in, but it is safe,
         // because we the next boot will try again.
@@ -749,16 +771,15 @@
 
     // After this, we return true because we technically did switch to a merge
     // target. Everything else we do here is just informational.
-    auto dm_name = GetSnapshotDeviceName(name, status);
-    if (!RewriteSnapshotDeviceTable(dm_name)) {
+    if (!RewriteSnapshotDeviceTable(name)) {
         return false;
     }
 
     status.set_state(SnapshotState::MERGING);
 
     DmTargetSnapshot::Status dm_status;
-    if (!QuerySnapshotStatus(dm_name, nullptr, &dm_status)) {
-        LOG(ERROR) << "Could not query merge status for snapshot: " << dm_name;
+    if (!QuerySnapshotStatus(name, nullptr, &dm_status)) {
+        LOG(ERROR) << "Could not query merge status for snapshot: " << name;
     }
     status.set_sectors_allocated(dm_status.sectors_allocated);
     status.set_metadata_sectors(dm_status.metadata_sectors);
@@ -768,33 +789,33 @@
     return true;
 }
 
-bool SnapshotManager::RewriteSnapshotDeviceTable(const std::string& dm_name) {
+bool SnapshotManager::RewriteSnapshotDeviceTable(const std::string& name) {
     auto& dm = DeviceMapper::Instance();
 
     std::vector<DeviceMapper::TargetInfo> old_targets;
-    if (!dm.GetTableInfo(dm_name, &old_targets)) {
-        LOG(ERROR) << "Could not read snapshot device table: " << dm_name;
+    if (!dm.GetTableInfo(name, &old_targets)) {
+        LOG(ERROR) << "Could not read snapshot device table: " << name;
         return false;
     }
     if (old_targets.size() != 1 || DeviceMapper::GetTargetType(old_targets[0].spec) != "snapshot") {
-        LOG(ERROR) << "Unexpected device-mapper table for snapshot: " << dm_name;
+        LOG(ERROR) << "Unexpected device-mapper table for snapshot: " << name;
         return false;
     }
 
     std::string base_device, cow_device;
     if (!DmTargetSnapshot::GetDevicesFromParams(old_targets[0].data, &base_device, &cow_device)) {
-        LOG(ERROR) << "Could not derive underlying devices for snapshot: " << dm_name;
+        LOG(ERROR) << "Could not derive underlying devices for snapshot: " << name;
         return false;
     }
 
     DmTable table;
     table.Emplace<DmTargetSnapshot>(0, old_targets[0].spec.length, base_device, cow_device,
                                     SnapshotStorageMode::Merge, kSnapshotChunkSize);
-    if (!dm.LoadTableAndActivate(dm_name, table)) {
-        LOG(ERROR) << "Could not swap device-mapper tables on snapshot device " << dm_name;
+    if (!dm.LoadTableAndActivate(name, table)) {
+        LOG(ERROR) << "Could not swap device-mapper tables on snapshot device " << name;
         return false;
     }
-    LOG(INFO) << "Successfully switched snapshot device to a merge target: " << dm_name;
+    LOG(INFO) << "Successfully switched snapshot device to a merge target: " << name;
     return true;
 }
 
@@ -908,13 +929,13 @@
 
 UpdateState SnapshotManager::CheckMergeState(LockedFile* lock,
                                              const std::function<bool()>& before_cancel) {
-    UpdateState state = ReadUpdateState(lock);
-    switch (state) {
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+    switch (update_status.state()) {
         case UpdateState::None:
         case UpdateState::MergeCompleted:
             // Harmless races are allowed between two callers of WaitForMerge,
             // so in both of these cases we just propagate the state.
-            return state;
+            return update_status.state();
 
         case UpdateState::Merging:
         case UpdateState::MergeNeedsReboot:
@@ -931,10 +952,10 @@
             if (HandleCancelledUpdate(lock, before_cancel)) {
                 return UpdateState::Cancelled;
             }
-            return state;
+            return update_status.state();
 
         default:
-            return state;
+            return update_status.state();
     }
 
     std::vector<std::string> snapshots;
@@ -946,8 +967,9 @@
     bool failed = false;
     bool merging = false;
     bool needs_reboot = false;
+    bool wrong_phase = false;
     for (const auto& snapshot : snapshots) {
-        UpdateState snapshot_state = CheckTargetMergeState(lock, snapshot);
+        UpdateState snapshot_state = CheckTargetMergeState(lock, snapshot, update_status);
         switch (snapshot_state) {
             case UpdateState::MergeFailed:
                 failed = true;
@@ -963,6 +985,9 @@
             case UpdateState::Cancelled:
                 cancelled = true;
                 break;
+            case UpdateState::None:
+                wrong_phase = true;
+                break;
             default:
                 LOG(ERROR) << "Unknown merge status for \"" << snapshot << "\": "
                            << "\"" << snapshot_state << "\"";
@@ -982,6 +1007,14 @@
         // it in WaitForMerge rather than here and elsewhere.
         return UpdateState::MergeFailed;
     }
+    if (wrong_phase) {
+        // If we got here, no other partitions are being merged, and nothing
+        // failed to merge. It's safe to move to the next merge phase.
+        if (!MergeSecondPhaseSnapshots(lock)) {
+            return UpdateState::MergeFailed;
+        }
+        return UpdateState::Merging;
+    }
     if (needs_reboot) {
         WriteUpdateState(lock, UpdateState::MergeNeedsReboot);
         return UpdateState::MergeNeedsReboot;
@@ -997,17 +1030,16 @@
     return UpdateState::MergeCompleted;
 }
 
-UpdateState SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string& name) {
+UpdateState SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string& name,
+                                                   const SnapshotUpdateStatus& update_status) {
     SnapshotStatus snapshot_status;
     if (!ReadSnapshotStatus(lock, name, &snapshot_status)) {
         return UpdateState::MergeFailed;
     }
 
-    std::string dm_name = GetSnapshotDeviceName(name, snapshot_status);
-
     std::unique_ptr<LpMetadata> current_metadata;
 
-    if (!IsSnapshotDevice(dm_name)) {
+    if (!IsSnapshotDevice(name)) {
         if (!current_metadata) {
             current_metadata = ReadCurrentMetadata();
         }
@@ -1021,7 +1053,7 @@
         // During a check, we decided the merge was complete, but we were unable to
         // collapse the device-mapper stack and perform COW cleanup. If we haven't
         // rebooted after this check, the device will still be a snapshot-merge
-        // target. If the have rebooted, the device will now be a linear target,
+        // target. If we have rebooted, the device will now be a linear target,
         // and we can try cleanup again.
         if (snapshot_status.state() == SnapshotState::MERGE_COMPLETED) {
             // NB: It's okay if this fails now, we gave cleanup our best effort.
@@ -1029,7 +1061,7 @@
             return UpdateState::MergeCompleted;
         }
 
-        LOG(ERROR) << "Expected snapshot or snapshot-merge for device: " << dm_name;
+        LOG(ERROR) << "Expected snapshot or snapshot-merge for device: " << name;
         return UpdateState::MergeFailed;
     }
 
@@ -1039,9 +1071,15 @@
 
     std::string target_type;
     DmTargetSnapshot::Status status;
-    if (!QuerySnapshotStatus(dm_name, &target_type, &status)) {
+    if (!QuerySnapshotStatus(name, &target_type, &status)) {
         return UpdateState::MergeFailed;
     }
+    if (target_type == "snapshot" &&
+        DecideMergePhase(snapshot_status) == MergePhase::SECOND_PHASE &&
+        update_status.merge_phase() == MergePhase::FIRST_PHASE) {
+        // The snapshot is not being merged because it's in the wrong phase.
+        return UpdateState::None;
+    }
     if (target_type != "snapshot-merge") {
         // We can get here if we failed to rewrite the target type in
         // InitiateMerge(). If we failed to create the target in first-stage
@@ -1077,6 +1115,38 @@
     return UpdateState::MergeCompleted;
 }
 
+bool SnapshotManager::MergeSecondPhaseSnapshots(LockedFile* lock) {
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(lock, &snapshots)) {
+        return UpdateState::MergeFailed;
+    }
+
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+    CHECK(update_status.state() == UpdateState::Merging);
+    CHECK(update_status.merge_phase() == MergePhase::FIRST_PHASE);
+
+    update_status.set_merge_phase(MergePhase::SECOND_PHASE);
+    if (!WriteSnapshotUpdateStatus(lock, update_status)) {
+        return false;
+    }
+
+    bool rewrote_all = true;
+    for (const auto& snapshot : snapshots) {
+        SnapshotStatus snapshot_status;
+        if (!ReadSnapshotStatus(lock, snapshot, &snapshot_status)) {
+            return UpdateState::MergeFailed;
+        }
+        if (DecideMergePhase(snapshot_status) != MergePhase::SECOND_PHASE) {
+            continue;
+        }
+        if (!SwitchSnapshotToMerge(lock, snapshot)) {
+            LOG(ERROR) << "Failed to switch snapshot to a second-phase merge target: " << snapshot;
+            rewrote_all = false;
+        }
+    }
+    return rewrote_all;
+}
+
 std::string SnapshotManager::GetSnapshotBootIndicatorPath() {
     return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath);
 }
@@ -1089,6 +1159,10 @@
     return metadata_dir_ + "/allow-forward-merge";
 }
 
+std::string SnapshotManager::GetOldPartitionMetadataPath() {
+    return metadata_dir_ + "/old-partition-metadata";
+}
+
 void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
     // It's not possible to remove update state in recovery, so write an
     // indicator that cleanup is needed on reboot. If a factory data reset
@@ -1124,21 +1198,20 @@
 
 bool SnapshotManager::OnSnapshotMergeComplete(LockedFile* lock, const std::string& name,
                                               const SnapshotStatus& status) {
-    auto dm_name = GetSnapshotDeviceName(name, status);
-    if (IsSnapshotDevice(dm_name)) {
+    if (IsSnapshotDevice(name)) {
         // We are extra-cautious here, to avoid deleting the wrong table.
         std::string target_type;
         DmTargetSnapshot::Status dm_status;
-        if (!QuerySnapshotStatus(dm_name, &target_type, &dm_status)) {
+        if (!QuerySnapshotStatus(name, &target_type, &dm_status)) {
             return false;
         }
         if (target_type != "snapshot-merge") {
             LOG(ERROR) << "Unexpected target type " << target_type
-                       << " for snapshot device: " << dm_name;
+                       << " for snapshot device: " << name;
             return false;
         }
         if (dm_status.sectors_allocated != dm_status.metadata_sectors) {
-            LOG(ERROR) << "Merge is unexpectedly incomplete for device " << dm_name;
+            LOG(ERROR) << "Merge is unexpectedly incomplete for device " << name;
             return false;
         }
         if (!CollapseSnapshotDevice(name, status)) {
@@ -1159,23 +1232,21 @@
 bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
                                              const SnapshotStatus& status) {
     auto& dm = DeviceMapper::Instance();
-    auto dm_name = GetSnapshotDeviceName(name, status);
 
     // Verify we have a snapshot-merge device.
     DeviceMapper::TargetInfo target;
-    if (!GetSingleTarget(dm_name, TableQuery::Table, &target)) {
+    if (!GetSingleTarget(name, TableQuery::Table, &target)) {
         return false;
     }
     if (DeviceMapper::GetTargetType(target.spec) != "snapshot-merge") {
         // This should be impossible, it was checked earlier.
-        LOG(ERROR) << "Snapshot device has invalid target type: " << dm_name;
+        LOG(ERROR) << "Snapshot device has invalid target type: " << name;
         return false;
     }
 
     std::string base_device, cow_device;
     if (!DmTargetSnapshot::GetDevicesFromParams(target.data, &base_device, &cow_device)) {
-        LOG(ERROR) << "Could not parse snapshot device " << dm_name
-                   << " parameters: " << target.data;
+        LOG(ERROR) << "Could not parse snapshot device " << name << " parameters: " << target.data;
         return false;
     }
 
@@ -1186,42 +1257,6 @@
         return false;
     }
 
-    if (dm_name != name) {
-        // We've derived the base device, but we actually need to replace the
-        // table of the outermost device. Do a quick verification that this
-        // device looks like we expect it to.
-        std::vector<DeviceMapper::TargetInfo> outer_table;
-        if (!dm.GetTableInfo(name, &outer_table)) {
-            LOG(ERROR) << "Could not validate outer snapshot table: " << name;
-            return false;
-        }
-        if (outer_table.size() != 2) {
-            LOG(ERROR) << "Expected 2 dm-linear targets for table " << name
-                       << ", got: " << outer_table.size();
-            return false;
-        }
-        for (const auto& target : outer_table) {
-            auto target_type = DeviceMapper::GetTargetType(target.spec);
-            if (target_type != "linear") {
-                LOG(ERROR) << "Outer snapshot table may only contain linear targets, but " << name
-                           << " has target: " << target_type;
-                return false;
-            }
-        }
-        if (outer_table[0].spec.length != snapshot_sectors) {
-            LOG(ERROR) << "dm-snapshot " << name << " should have " << snapshot_sectors
-                       << " sectors, got: " << outer_table[0].spec.length;
-            return false;
-        }
-        uint64_t expected_device_sectors = status.device_size() / kSectorSize;
-        uint64_t actual_device_sectors = outer_table[0].spec.length + outer_table[1].spec.length;
-        if (expected_device_sectors != actual_device_sectors) {
-            LOG(ERROR) << "Outer device " << name << " should have " << expected_device_sectors
-                       << " sectors, got: " << actual_device_sectors;
-            return false;
-        }
-    }
-
     uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
     // Create a DmTable that is identical to the base device.
     CreateLogicalPartitionParams base_device_params{
@@ -1236,7 +1271,6 @@
         return false;
     }
 
-    // Note: we are replacing the *outer* table here, so we do not use dm_name.
     if (!dm.LoadTableAndActivate(name, table)) {
         return false;
     }
@@ -1246,22 +1280,17 @@
     // flushed remaining I/O. We could in theory replace with dm-zero (or
     // re-use the table above), but for now it's better to know why this
     // would fail.
-    if (dm_name != name && !dm.DeleteDeviceIfExists(dm_name)) {
-        LOG(ERROR) << "Unable to delete snapshot device " << dm_name << ", COW cannot be "
-                   << "reclaimed until after reboot.";
-        return false;
-    }
-
     if (status.compression_enabled()) {
         UnmapDmUserDevice(name);
     }
-
-    // Cleanup the base device as well, since it is no longer used. This does
-    // not block cleanup.
     auto base_name = GetBaseDeviceName(name);
     if (!dm.DeleteDeviceIfExists(base_name)) {
         LOG(ERROR) << "Unable to delete base device for snapshot: " << base_name;
     }
+    auto source_name = GetSourceDeviceName(name);
+    if (!dm.DeleteDeviceIfExists(source_name)) {
+        LOG(ERROR) << "Unable to delete source device for snapshot: " << source_name;
+    }
     return true;
 }
 
@@ -1364,9 +1393,9 @@
             continue;
         }
 
-        std::string backing_device;
-        if (!dm.GetDmDevicePathByName(GetBaseDeviceName(snapshot), &backing_device)) {
-            LOG(ERROR) << "Could not get device path for " << GetBaseDeviceName(snapshot);
+        std::string source_device;
+        if (!dm.GetDmDevicePathByName(GetSourceDeviceName(snapshot), &source_device)) {
+            LOG(ERROR) << "Could not get device path for " << GetSourceDeviceName(snapshot);
             continue;
         }
 
@@ -1392,7 +1421,7 @@
         }
 
         if (transition == InitTransition::SELINUX_DETACH) {
-            auto message = misc_name + "," + cow_image_device + "," + backing_device;
+            auto message = misc_name + "," + cow_image_device + "," + source_device;
             snapuserd_argv->emplace_back(std::move(message));
 
             // Do not attempt to connect to the new snapuserd yet, it hasn't
@@ -1403,7 +1432,7 @@
         }
 
         uint64_t base_sectors =
-                snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, backing_device);
+                snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, source_device);
         if (base_sectors == 0) {
             // Unrecoverable as metadata reads from cow device failed
             LOG(FATAL) << "Failed to retrieve base_sectors from Snapuserd";
@@ -1548,7 +1577,7 @@
         //    Otherwise (UPDATED flag), only delete snapshots if they are not mapped
         //    as dm-snapshot (for example, after merge completes).
         bool should_unmap = current_slot != Slot::Target;
-        bool should_delete = ShouldDeleteSnapshot(lock, flashing_status, current_slot, name);
+        bool should_delete = ShouldDeleteSnapshot(flashing_status, current_slot, name);
 
         bool partition_ok = true;
         if (should_unmap && !UnmapPartitionWithSnapshot(lock, name)) {
@@ -1582,8 +1611,7 @@
 }
 
 // See comments in RemoveAllSnapshots().
-bool SnapshotManager::ShouldDeleteSnapshot(LockedFile* lock,
-                                           const std::map<std::string, bool>& flashing_status,
+bool SnapshotManager::ShouldDeleteSnapshot(const std::map<std::string, bool>& flashing_status,
                                            Slot current_slot, const std::string& name) {
     if (current_slot != Slot::Target) {
         return true;
@@ -1597,16 +1625,7 @@
         // partition flashed, okay to delete obsolete snapshots
         return true;
     }
-    // partition updated, only delete if not dm-snapshot
-    SnapshotStatus status;
-    if (!ReadSnapshotStatus(lock, name, &status)) {
-        LOG(WARNING) << "Unable to read snapshot status for " << name
-                     << ", guessing snapshot device name";
-        auto extra_name = GetSnapshotExtraDeviceName(name);
-        return !IsSnapshotDevice(name) && !IsSnapshotDevice(extra_name);
-    }
-    auto dm_name = GetSnapshotDeviceName(name, status);
-    return !IsSnapshotDevice(dm_name);
+    return !IsSnapshotDevice(name);
 }
 
 UpdateState SnapshotManager::GetUpdateState(double* progress) {
@@ -1922,24 +1941,35 @@
     }
 
     if (live_snapshot_status->compression_enabled()) {
-        auto name = GetDmUserCowName(params.GetPartitionName());
+        // 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;
+        }
+
+        auto source_device = GetSourceDeviceName(params.GetPartitionName());
+        created_devices.EmplaceBack<AutoUnmapDevice>(&dm, source_device);
+
+        if (!WaitForDevice(source_device_path, remaining_time)) {
+            return false;
+        }
 
         std::string cow_path;
         if (!GetMappedImageDevicePath(cow_name, &cow_path)) {
             LOG(ERROR) << "Could not determine path for: " << cow_name;
             return false;
         }
-
-        // Ensure both |base_path| and |cow_path| are created, for snapuserd.
-        if (!WaitForDevice(base_path, remaining_time)) {
-            return false;
-        }
         if (!WaitForDevice(cow_path, remaining_time)) {
             return false;
         }
 
+        auto name = GetDmUserCowName(params.GetPartitionName());
+
         std::string new_cow_device;
-        if (!MapDmUserCow(lock, name, cow_path, base_path, remaining_time, &new_cow_device)) {
+        if (!MapDmUserCow(lock, name, cow_path, source_device_path, remaining_time,
+                          &new_cow_device)) {
             LOG(ERROR) << "Could not map dm-user device for partition "
                        << params.GetPartitionName();
             return false;
@@ -1983,12 +2013,18 @@
     }
 
     auto& dm = DeviceMapper::Instance();
-    std::string base_name = GetBaseDeviceName(target_partition_name);
+    auto base_name = GetBaseDeviceName(target_partition_name);
     if (!dm.DeleteDeviceIfExists(base_name)) {
         LOG(ERROR) << "Cannot delete base device: " << base_name;
         return false;
     }
 
+    auto source_name = GetSourceDeviceName(target_partition_name);
+    if (!dm.DeleteDeviceIfExists(source_name)) {
+        LOG(ERROR) << "Cannot delete source device: " << source_name;
+        return false;
+    }
+
     LOG(INFO) << "Successfully unmapped snapshot " << target_partition_name;
 
     return true;
@@ -2409,14 +2445,6 @@
     return true;
 }
 
-std::string SnapshotManager::GetSnapshotDeviceName(const std::string& snapshot_name,
-                                                   const SnapshotStatus& status) {
-    if (status.device_size() != status.snapshot_size()) {
-        return GetSnapshotExtraDeviceName(snapshot_name);
-    }
-    return snapshot_name;
-}
-
 bool SnapshotManager::EnsureImageManager() {
     if (images_) return true;
 
@@ -2588,6 +2616,24 @@
         return Return::Error();
     }
 
+    // If compression is enabled, we need to retain a copy of the old metadata
+    // so we can access original blocks in case they are moved around. We do
+    // not want to rely on the old super metadata slot because we don't
+    // guarantee its validity after the slot switch is successful.
+    if (cow_creator.compression_enabled) {
+        auto metadata = current_metadata->Export();
+        if (!metadata) {
+            LOG(ERROR) << "Could not export current metadata";
+            return Return::Error();
+        }
+
+        auto path = GetOldPartitionMetadataPath();
+        if (!android::fs_mgr::WriteToImageFile(path, *metadata.get())) {
+            LOG(ERROR) << "Cannot write old metadata to " << path;
+            return Return::Error();
+        }
+    }
+
     SnapshotUpdateStatus status = {};
     status.set_state(update_state);
     status.set_compression_enabled(cow_creator.compression_enabled);
@@ -2688,6 +2734,15 @@
             continue;
         }
 
+        // Find the original partition size.
+        auto name = target_partition->name();
+        auto old_partition_name =
+                name.substr(0, name.size() - target_suffix.size()) + cow_creator->current_suffix;
+        auto old_partition = cow_creator->current_metadata->FindPartition(old_partition_name);
+        if (old_partition) {
+            cow_creator_ret->snapshot_status.set_old_partition_size(old_partition->size());
+        }
+
         // Store these device sizes to snapshot status file.
         if (!CreateSnapshot(lock, &cow_creator_ret->snapshot_status)) {
             return Return::Error();
@@ -3375,5 +3430,26 @@
     return PerformInitTransition(InitTransition::SECOND_STAGE);
 }
 
+const LpMetadata* SnapshotManager::ReadOldPartitionMetadata(LockedFile* lock) {
+    CHECK(lock);
+
+    if (!old_partition_metadata_) {
+        auto path = GetOldPartitionMetadataPath();
+        old_partition_metadata_ = android::fs_mgr::ReadFromImageFile(path);
+        if (!old_partition_metadata_) {
+            LOG(ERROR) << "Could not read old partition metadata from " << path;
+            return nullptr;
+        }
+    }
+    return old_partition_metadata_.get();
+}
+
+MergePhase SnapshotManager::DecideMergePhase(const SnapshotStatus& status) {
+    if (status.compression_enabled() && status.device_size() < status.old_partition_size()) {
+        return MergePhase::FIRST_PHASE;
+    }
+    return MergePhase::SECOND_PHASE;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 80a6074..4c209ec 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -234,7 +234,8 @@
                 .partition_opener = &opener,
         };
 
-        auto result = sm->OpenSnapshotWriter(params, {});
+        auto old_partition = "/dev/block/mapper/" + GetOtherPartitionName(name);
+        auto result = sm->OpenSnapshotWriter(params, {old_partition});
         if (!result) {
             return AssertionFailure() << "Cannot open snapshot for writing: " << name;
         }
@@ -467,31 +468,6 @@
     ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
 }
 
-TEST_F(SnapshotTest, MapPartialSnapshot) {
-    ASSERT_TRUE(AcquireLock());
-
-    static const uint64_t kSnapshotSize = 1024 * 1024;
-    static const uint64_t kDeviceSize = 1024 * 1024 * 2;
-    SnapshotStatus status;
-    status.set_name("test-snapshot");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kSnapshotSize);
-    status.set_cow_file_size(kSnapshotSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
-    ASSERT_TRUE(CreateCowImage("test-snapshot"));
-
-    std::string base_device;
-    ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
-
-    std::string cow_device;
-    ASSERT_TRUE(MapCowImage("test-snapshot", 10s, &cow_device));
-
-    std::string snap_device;
-    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
-                                &snap_device));
-    ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
-}
-
 TEST_F(SnapshotTest, NoMergeBeforeReboot) {
     ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
@@ -590,8 +566,7 @@
     ASSERT_EQ(status.state(), SnapshotState::CREATED);
 
     DeviceMapper::TargetInfo target;
-    auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
-    ASSERT_TRUE(init->IsSnapshotDevice(dm_name, &target));
+    ASSERT_TRUE(init->IsSnapshotDevice("test_partition_b", &target));
     ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
 }
 
@@ -618,8 +593,7 @@
 
     // We should not get a snapshot device now.
     DeviceMapper::TargetInfo target;
-    auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
-    ASSERT_FALSE(init->IsSnapshotDevice(dm_name, &target));
+    ASSERT_FALSE(init->IsSnapshotDevice("test_partition_b", &target));
 
     // We should see a cancelled update as well.
     lock_ = nullptr;
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 7342fd4..4a2af1c 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -187,5 +187,13 @@
     return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
 }
 
+std::string GetOtherPartitionName(const std::string& name) {
+    auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
+    CHECK(suffix == "_a" || suffix == "_b");
+
+    auto other_suffix = (suffix == "_a") ? "_b" : "_a";
+    return name.substr(0, name.size() - suffix.size()) + other_suffix;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 3e6873b..671de9d 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -131,5 +131,8 @@
 
 bool IsCompressionEnabled();
 
+// Swap the suffix of a partition name.
+std::string GetOtherPartitionName(const std::string& name);
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index f5bbe35..2433833 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -740,7 +740,7 @@
   grep -v \
     -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\) " \
     -e "^\(binfmt_misc\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
-    -e "^\(ramdumpfs\|binder\|/sys/kernel/debug\) " \
+    -e "^\(ramdumpfs\|binder\|/sys/kernel/debug\|securityfs\) " \
     -e " functionfs " \
     -e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
     -e "^rootfs / rootfs rw," \
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index 5690031..23e0433 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -37,6 +37,12 @@
     if (versionRsp.error != KM_ERROR_OK) {
         ALOGW("TA appears not to support GetVersion2, falling back (err = %d)", versionRsp.error);
 
+        err = trusty_keymaster_connect();
+        if (err) {
+            ALOGE("Failed to connect to trusty keymaster %d", err);
+            return err;
+        }
+
         GetVersionRequest versionReq;
         GetVersionResponse versionRsp;
         GetVersion(versionReq, &versionRsp);
diff --git a/trusty/secure_dpu/Android.bp b/trusty/secure_dpu/Android.bp
new file mode 100644
index 0000000..0d57cea
--- /dev/null
+++ b/trusty/secure_dpu/Android.bp
@@ -0,0 +1,20 @@
+// 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.
+
+cc_library_headers {
+    name: "secure_dpu_headers",
+    vendor: true,
+
+    export_include_dirs: ["include"],
+}
diff --git a/trusty/secure_dpu/include/trusty/secure_dpu/SecureDpu.h b/trusty/secure_dpu/include/trusty/secure_dpu/SecureDpu.h
new file mode 100644
index 0000000..b939d44
--- /dev/null
+++ b/trusty/secure_dpu/include/trusty/secure_dpu/SecureDpu.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020, 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 <stdint.h>
+
+/**
+ * DOC: Secure DPU
+ *
+ * The Secure DPU works as the persistent channel between the non-secure and the
+ * secure world. The channel is established during the boot up stage of the
+ * non-secure world system. In general, the established channel allows the
+ * secure world applications initiate requests or notifications to the non-secure
+ * world.
+ *
+ * For particular devices, the secure world can only perform operations on the
+ * display when in the TUI session if device-specific setup is done by the
+ * non-secure world. Besides, the non-secure world could allocate framebuffer
+ * for the secure world application if the memory is limited in the secure world
+ * on specific devices.
+ *
+ * Currently, supported requests are to start / stop the secure display mode and
+ * to allocate framebuffer.
+ *
+ * This header file needs to be synced on both the Trusty and the Android
+ * codebase.
+ */
+
+#define SECURE_DPU_PORT_NAME "com.android.trusty.secure_dpu"
+#define SECURE_DPU_MAX_MSG_SIZE 64
+
+/**
+ * enum secure_dpu_cmd - command identifiers for secure_fb interface
+ * @SECURE_DPU_CMD_RESP_BIT:
+ *      Message is a response.
+ * @SECURE_DPU_CMD_REQ_SHIFT:
+ *      Number of bits used by @SECURE_DPU_CMD_RESP_BIT.
+ * @SECURE_DPU_CMD_START_SECURE_DISPLAY:
+ *      Notify the system to start secure display mode
+ * @SECURE_DPU_CMD_STOP_SECURE_DISPLAY:
+ *      Notify the system to stop secure display mode
+ * @SECURE_DPU_CMD_ALLOCATE_BUFFER:
+ *      Request non-secure world to allocate the buffer
+ */
+enum secure_dpu_cmd {
+    SECURE_DPU_CMD_RESP_BIT = 1,
+    SECURE_DPU_CMD_REQ_SHIFT = 1,
+    SECURE_DPU_CMD_START_SECURE_DISPLAY = (1 << SECURE_DPU_CMD_REQ_SHIFT),
+    SECURE_DPU_CMD_STOP_SECURE_DISPLAY = (2 << SECURE_DPU_CMD_REQ_SHIFT),
+    SECURE_DPU_CMD_ALLOCATE_BUFFER = (3 << SECURE_DPU_CMD_REQ_SHIFT),
+};
+
+/**
+ * struct secure_dpu_allocate_buffer_req - payload for
+ *                                         %SECURE_DPU_CMD_ALLOCATE_BUFFER
+ *                                         request
+ * @buffer_len: Requested length
+ */
+struct secure_dpu_allocate_buffer_req {
+    uint64_t buffer_len;
+};
+
+/**
+ * struct secure_dpu_allocate_buffer_resp - payload for
+ *                                          %SECURE_DPU_CMD_ALLOCATE_BUFFER
+ *                                          response
+ * @buffer_len: Allocated length
+ */
+struct secure_dpu_allocate_buffer_resp {
+    uint64_t buffer_len;
+};
+
+/**
+ * struct secure_fb_req - common structure for secure_fb requests.
+ * @cmd: Command identifier - one of &enum secure_dpu_cmd.
+ */
+struct secure_dpu_req {
+    uint32_t cmd;
+};
+
+/**
+ * struct secure_dpu_resp - common structure for secure_dpu responses.
+ * @cmd:    Command identifier - %SECURE_DPU_CMD_RESP_BIT or'ed with the
+ *                               command identifier of the corresponding
+ *                               request.
+ * @status: Status of requested operation. One of &enum secure_dpu_error.
+ */
+struct secure_dpu_resp {
+    uint32_t cmd;
+    int32_t status;
+};
+
+enum secure_dpu_error {
+    SECURE_DPU_ERROR_OK = 0,
+    SECURE_DPU_ERROR_FAIL = -1,
+    SECURE_DPU_ERROR_UNINITIALIZED = -2,
+    SECURE_DPU_ERROR_PARAMETERS = -3,
+    SECURE_DPU_ERROR_NO_MEMORY = -4,
+};