1 // Copyright 2021 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 15 #pragma once 16 17 #include <string_view> 18 19 #include "pw_result/result.h" 20 #include "pw_software_update/manifest_accessor.h" 21 #include "pw_software_update/update_bundle_accessor.h" 22 #include "pw_status/status.h" 23 #include "pw_stream/interval_reader.h" 24 #include "pw_stream/stream.h" 25 26 namespace pw::software_update { 27 28 // TODO(b/235273688): update documentation for backend api contract 29 class BundledUpdateBackend { 30 public: 31 virtual ~BundledUpdateBackend() = default; 32 33 // Perform optional, product-specific validations to the specified target 34 // file, using whatever metadata available in manifest. 35 // 36 // This is called for each target file after the standard verification has 37 // passed. VerifyTargetFile(ManifestAccessor manifest,std::string_view target_file_name)38 virtual Status VerifyTargetFile( 39 [[maybe_unused]] ManifestAccessor manifest, 40 [[maybe_unused]] std::string_view target_file_name) { 41 // TODO(backend): Perform any additional product-specific validations. 42 // It is safe to assume the target's payload has passed standard 43 // verification. 44 return OkStatus(); 45 } 46 47 // Perform any product-specific tasks needed before starting update sequence. BeforeUpdateStart()48 virtual Status BeforeUpdateStart() { return OkStatus(); } 49 50 // Attempts to enable the transfer service transfer handler, returning the 51 // transfer_id if successful. This is invoked after BeforeUpdateStart(); 52 virtual Result<uint32_t> EnableBundleTransferHandler( 53 std::string_view bundle_filename) = 0; 54 55 // Disables the transfer service transfer handler. This is invoked after 56 // either BeforeUpdateAbort() or BeforeBundleVerify(). 57 virtual void DisableBundleTransferHandler() = 0; 58 59 // Perform any product-specific abort tasks before marking the update as 60 // aborted in bundled updater. This should set any downstream state to a 61 // default no-update-pending state. 62 // TODO(keir): Revisit invariants; should this instead be "Abort()"? This is 63 // called for all error paths in the service and needs to reset. Furthermore, 64 // should this be async? BeforeUpdateAbort()65 virtual Status BeforeUpdateAbort() { return OkStatus(); } 66 67 // Perform any product-specific tasks needed before starting verification. BeforeBundleVerify()68 virtual Status BeforeBundleVerify() { return OkStatus(); } 69 70 // Perform any product-specific bundle verification tasks (e.g. hw version 71 // match check), done after TUF bundle verification process. VerifyManifest(ManifestAccessor manifest_accessor)72 virtual Status VerifyManifest( 73 [[maybe_unused]] ManifestAccessor manifest_accessor) { 74 return OkStatus(); 75 } 76 77 // Perform product-specific tasks after all bundle verifications are complete. AfterBundleVerified()78 virtual Status AfterBundleVerified() { return OkStatus(); } 79 80 // Perform any product-specific tasks before apply sequence started BeforeApply()81 virtual Status BeforeApply() { return OkStatus(); } 82 83 // Get status information from update backend. This will not be called when 84 // BundledUpdater is in a step where it has entire control with no operation 85 // handed over to update backend. GetStatus()86 virtual int64_t GetStatus() { return 0; } 87 88 // Update the specific target file on the device. 89 virtual Status ApplyTargetFile(std::string_view target_file_name, 90 stream::Reader& target_payload, 91 size_t update_bundle_offset) = 0; 92 93 // Backend to probe the device manifest and prepare a ready-to-go reader 94 // for it. See the comments to `GetCurrentManfestReader()` for more context. BeforeManifestRead()95 virtual Status BeforeManifestRead() { 96 // Todo(backend): 97 // 1. Probe device to see if a well-formed manifest already exists. 98 // 2. If not, return `Status::NotFound()`. Note this will cause 99 // anti-rollback to skip. So please don't always return 100 // `Status::NotFound()`! 101 // 3. If yes, instantiate and activate a reader for the manifest! 102 // 4. Return any unexpected condition as errors but note this will cause 103 // the current software update session to abort. 104 return OkStatus(); 105 } 106 107 // Backend to provide a ready-to-go reader for the on-device manifest blob. 108 // This function is called after a successful `BeforeManifestRead()`, 109 // potentially more than once. 110 // 111 // This manifest blob is a serialized `message Manifest{...}` as defined in 112 // update_bundle.proto. 113 // 114 // This manifest blob is ALWAYS and EXCLUSIVELY persisted by a successful 115 // software update. Thus it may not available before the first software 116 // update, in which case `BeforeManifestRead()` should've returned 117 // `Status::NotFound()`. 118 // 119 // This manifest contains trusted metadata of all software currently running 120 // on the device and used for anti-rollback checks. It MUST NOT be tampered 121 // by factory resets, flashing, or any other means other than software 122 // updates. GetCurrentManifestReader()123 virtual Result<stream::SeekableReader*> GetCurrentManifestReader() { 124 // Todo(backend): 125 // 1. Double check if a ready-to-go reader has been prepared by 126 // `BeforeManifestRead()`. 127 // 2. If yes (expected), return the reader. 128 // 3. If not (unexpected), return `Status::FailedPrecondition()`. 129 return Status::Unimplemented(); 130 } 131 132 // TODO(alizhang): Deprecate GetCurrentManifestReader in favor of 133 // `GetManifestReader()`. GetManifestReader()134 virtual Result<stream::SeekableReader*> GetManifestReader() { 135 return GetCurrentManifestReader(); 136 } 137 138 // Backend to prepare for on-device manifest update, e.g. make necessary 139 // efforts to ready the manifest writer. The manifest writer is used to 140 // persist a new manifest on-device following a successful software update. 141 // Manifest writing is never mixed with reading (i.e. reader and writer are 142 // used sequentially). BeforeManifestWrite()143 virtual Status BeforeManifestWrite() { 144 // Todo(backend): 145 // 1. Instantiate and activate a manifest writer pointing at a persistent 146 // storage that at least could survive a factory data reset (FDR), if not 147 // tamper-resistant. 148 return OkStatus(); 149 } 150 151 // Backend to provide a ready-to-go writer for the on-device manifest blob. 152 // This function is called after a successful `BeforeManifestWrite()`, 153 // potentially more than once. 154 // 155 // This manifest blob is a serialized `message Manifest{...}` as defined in 156 // update_bundle.proto. 157 // 158 // This manifest blob is ALWAYS and EXCLUSIVELY persisted by a successful 159 // software update. 160 // 161 // This manifest contains trusted metadata of all software currently running 162 // on the device and used for anti-rollback checks. It MUST NOT be tampered 163 // by factory resets, flashing, or any other means other than software 164 // updates. GetManifestWriter()165 virtual Result<stream::Writer*> GetManifestWriter() { 166 // Todo(backend): 167 // 1. Double check a writer is ready to go as result of 168 // `BeforeManifestWrite()`. 169 // 2. If yes (expected), simply return the writer. 170 // 3. If not (unexpected), return `Status::FailedPrecondition()`. 171 return Status::Unimplemented(); 172 } 173 174 // Backend to finish up manifest writing. AfterManifestWrite()175 virtual Status AfterManifestWrite() { 176 // Todo(backend): 177 // Protect the newly persisted manifest blob. This is to make manifest 178 // probing / reading easier and more reliable. This could involve taking 179 // a measurement (e.g. checksum) and storing that measurement in a 180 // FDR-safe tag, replicating the manifest in a backup location if the 181 // backing media is unreliable (e.g. raw NAND) etc. 182 // 183 // It is safe to assume the writing has been successful in this function. 184 return OkStatus(); 185 } 186 187 // Do any work needed to finish the apply of the update and do a required 188 // reboot of the device! 189 // 190 // NOTE: If successful this method does not return and reboots the device, it 191 // only returns on failure to finalize. 192 // 193 // NOTE: ApplyReboot shall be configured such to allow pending RPC or logs to 194 // send out the reply before the device reboots. 195 virtual Status ApplyReboot() = 0; 196 197 // Do any work needed to finalize the update including optionally doing a 198 // reboot of the device! The software update state and breadcrumbs are not 199 // cleaned up until this method returns OK. 200 // 201 // This method is called after the reboot done as part of ApplyReboot(). 202 // 203 // If this method does an optional reboot, it will be called again after the 204 // reboot. 205 // 206 // NOTE: PostRebootFinalize shall be configured such to allow pending RPC or 207 // logs to send out the reply before the device reboots. PostRebootFinalize()208 virtual Status PostRebootFinalize() { return OkStatus(); } 209 210 // Get reader of the device's root metadata. 211 // 212 // This method MUST return a valid root metadata once verified OTA is enabled. 213 // An invalid or corrupted root metadata will result in permanent OTA 214 // failures. GetRootMetadataReader()215 virtual Result<stream::SeekableReader*> GetRootMetadataReader() { 216 return Status::Unimplemented(); 217 } 218 219 // Write a given root metadata to persistent storage in a failsafe manner. 220 // 221 // The updating must be atomic/fail-safe. An invalid or corrupted root 222 // metadata will result in permanent OTA failures. SafelyPersistRootMetadata(stream::IntervalReader root_metadata)223 virtual Status SafelyPersistRootMetadata( 224 [[maybe_unused]] stream::IntervalReader root_metadata) { 225 return Status::Unimplemented(); 226 } 227 }; 228 229 } // namespace pw::software_update 230