| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| library fuchsia.paver; |
| |
| using fuchsia.hardware.block; |
| using fuchsia.hardware.block.volume; |
| using fuchsia.io; |
| using fuchsia.mem; |
| using zx; |
| |
| /// Describes the version of an asset. |
| type Configuration = strict enum { |
| A = 1; |
| B = 2; |
| RECOVERY = 3; |
| }; |
| |
| /// Describes assets which may be updated. Each asset has 3 versions, each tied to a particular |
| /// configuration. |
| type Asset = strict enum { |
| /// Zircon Boot Image (ZBI) containing the kernel image as well as bootfs. |
| KERNEL = 1; |
| /// Metadata used for verified boot purposes. |
| VERIFIED_BOOT_METADATA = 2; |
| }; |
| |
| /// Set of states configuration may be in. |
| type ConfigurationStatus = strict enum { |
| /// Bootable and health checked. |
| HEALTHY = 1; |
| /// Bootable but not yet marked healthy. |
| PENDING = 2; |
| /// Unbootable. |
| UNBOOTABLE = 3; |
| }; |
| |
| type ReadInfo = struct { |
| /// Offset into VMO where read data starts. |
| offset zx.off; |
| /// Size of read data. |
| size uint64; |
| }; |
| |
| type ReadResult = strict union { |
| /// Error encountered while reading data. |
| 1: err zx.status; |
| /// End of file reached. |
| 2: eof bool; |
| /// Information about location of successfully read data within pre-registered VMO. |
| 3: info ReadInfo; |
| }; |
| |
| type WriteFirmwareResult = strict union { |
| /// The result status if a write was attempted. |
| 1: status zx.status; |
| |
| /// True if a write was not attempted due to unsupported firmware. This could |
| /// be either unsupported content type or unsupported A/B configuration. |
| /// |
| /// Callers must not treat this as a fatal error, but instead ignore it and |
| /// continue to update the device. This is important to be able to add new |
| /// items to an update package without breaking updates on older devices. |
| 2: unsupported bool; |
| }; |
| |
| /// Protocol for streaming the FVM payload. |
| protocol PayloadStream { |
| /// Registers a VMO to stream into. |
| /// |
| /// This can be called once per PayloadStream. |
| /// Any subsequent calls will return ZX_ERR_ALREADY_BOUND. |
| RegisterVmo(resource struct { |
| vmo zx.handle:VMO; |
| }) -> (struct { |
| status zx.status; |
| }); |
| |
| /// Reads data into the pre-registered vmo. |
| ReadData() -> (struct { |
| result ReadResult; |
| }); |
| }; |
| |
| @discoverable |
| protocol Paver { |
| /// Attempts to auto-discover the data sink where assets and volumes will get paved to. |
| /// On devices with GPT, the partition must have a valid FVM partition in order for |
| /// auto-discovery to find it. If multiple devices are found suitable, error is returned. |
| /// |
| /// `data_sink` will be closed on error, with an epitaph provided on failure reason. |
| FindDataSink(resource struct { |
| data_sink server_end:DataSink; |
| }); |
| |
| /// Provide a block device to use as a data sink. Assets and volumes will be paved to |
| /// partitions within this block device. |
| /// |
| /// It assumes that channel backing `block_device` also implements `fuchsia.io.Node` for now. |
| /// |
| /// `data_sink` will be closed on error, with an epitaph provided on failure reason. |
| UseBlockDevice(resource struct { |
| block_device client_end:fuchsia.hardware.block.Block; |
| data_sink server_end:DynamicDataSink; |
| }); |
| |
| /// Attempts to auto-discover the boot manager. |
| /// |
| /// `boot_manager` will be closed on error, with an epitaph provided on failure reason. |
| /// ZX_ERR_NOT_SUPPORTED indicates lack of support and configuration A is always booted from. |
| FindBootManager(resource struct { |
| boot_manager server_end:BootManager; |
| }); |
| |
| /// Find Sysconfig service. |
| FindSysconfig(resource struct { |
| sysconfig server_end:Sysconfig; |
| }); |
| }; |
| |
| /// Protocol for reading and writing boot partitions. |
| /// |
| /// A note on DataSink.Flush() (and BootManager.Flush() coming after): |
| /// |
| /// Some platforms may implement the Flush() fidl interface of DataSink/BootManager. For these |
| /// platforms, the update of some system images and A/B configuration is not persisted to storage |
| /// immediately and only buffered internally when the write fidl interfaces return. The data is |
| /// guaranteed to be persisted only after the Flush() interfaces are called. |
| /// |
| /// If not implemented, Flush() is no-op and system images and A/B configuration will be persisted |
| /// to storage immediately after the write fidl interfaces return. |
| /// |
| /// For all platforms, it is guaranteed that if DataSink.Flush() is implemented, BootManager.Flush() |
| /// is implemented as well. Therefore, in the context of system update, both of the following update |
| /// sequences are safe in the sense that, new A/B configuration will not be persisted to storage |
| /// before new system images. |
| /// DataSink.Write[...]() --> DataSink.Flush() --> BootManager.Set[...]() --> BootManager.Flush() |
| /// DataSink.Write[...]() --> BootManager.Set[...]() --> DataSink.Flush() --> BootManager.Flush() |
| protocol DataSink { |
| /// Reads partition corresponding to `configuration` and `asset` into a |
| /// vmo and returns it. |
| ReadAsset(struct { |
| configuration Configuration; |
| asset Asset; |
| }) -> (resource struct { |
| asset fuchsia.mem.Buffer; |
| }) error zx.status; |
| |
| /// Writes partition corresponding to `configuration` and `asset` with data from `payload`. |
| /// `payload` may need to be resized to the partition size, so the provided vmo must have |
| /// been created with `ZX_VMO_RESIZABLE` or must be a child VMO that was created with |
| /// `ZX_VMO_CHILD_RESIZABLE`. Will zero out rest of the partition if `payload` is smaller |
| /// than the size of the partition being written. |
| /// |
| /// |
| /// Returns `ZX_ERR_INVALID_ARGS` if `configuration` specifies active configuration. |
| WriteAsset(resource struct { |
| configuration Configuration; |
| asset Asset; |
| payload fuchsia.mem.Buffer; |
| }) -> (struct { |
| status zx.status; |
| }); |
| |
| /// Writes firmware data from `payload`. |
| /// |
| /// `configuration` represents the A/B/R configuration. For platforms that do not support |
| /// firmware A/B/R, the parameter will be ignored by the underlying device-specific logic . |
| /// |
| /// `type` is a device-specific string identifying the payload contents, |
| /// used to select the proper paving logic. For example, a device with |
| /// multiple bootloader stages might send them as separate calls to |
| /// `WriteFirmware()`, differentiated by `type`. An empty string |
| /// indicates the default type. |
| /// |
| /// `payload` may need to be resized to the partition size, so the provided |
| /// vmo must have been created with `ZX_VMO_RESIZABLE` or must be a child |
| /// VMO that was created with `ZX_VMO_CHILD_RESIZABLE`. |
| WriteFirmware(resource struct { |
| configuration Configuration; |
| type string:256; |
| payload fuchsia.mem.Buffer; |
| }) -> (struct { |
| result WriteFirmwareResult; |
| }); |
| |
| /// Writes FVM with data from streamed via `payload`. This potentially affects all |
| /// configurations. |
| WriteVolumes(resource struct { |
| payload client_end:PayloadStream; |
| }) -> (struct { |
| status zx.status; |
| }); |
| |
| // TODO(fxbug.dev/45606): transition users to `WriteFirmware()` and delete this. |
| /// Writes bootloader partition with data from `payload`. |
| /// |
| /// `payload` may need to be resized to the partition size, so the provided vmo must have |
| /// been created with `ZX_VMO_RESIZABLE` or must be a child VMO that was created with |
| /// `ZX_VMO_CHILD_RESIZABLE`. |
| @deprecated |
| WriteBootloader(resource struct { |
| payload fuchsia.mem.Buffer; |
| }) -> (struct { |
| status zx.status; |
| }); |
| |
| /// Writes /data/`filename` with data from `payload`. Overwrites file if it already exists. |
| WriteDataFile(resource struct { |
| filename string:fuchsia.io.MAX_PATH; |
| payload fuchsia.mem.Buffer; |
| }) -> (struct { |
| status zx.status; |
| }); |
| |
| /// Wipes the FVM partition from the device. Should not be confused with factory reset, which |
| /// is less intrusive. The result is that the default FVM volumes are re-created, but empty. |
| /// |
| /// Notable use cases include recovering from corrupted FVM as well as setting device to a |
| /// "clean" state for automation. |
| /// |
| /// If `block_device` is not provided, the paver will perform a search for the the FVM. |
| /// If multiple block devices have valid GPT, `block_device` can be provided to specify |
| /// which one to target. It assumed that channel backing `block_device` also implements |
| /// `fuchsia.io.Node` for now. |
| /// |
| /// On success, returns a channel to the initialized FVM volume. |
| WipeVolume() -> (resource struct { |
| volume client_end:fuchsia.hardware.block.volume.VolumeManager; |
| }) error zx.status; |
| |
| /// Flush all previously buffered writes to persistent storage. |
| Flush() -> (struct { |
| status zx.status; |
| }); |
| }; |
| |
| /// Specialized DataSink with dynamic partition tables. |
| protocol DynamicDataSink { |
| compose DataSink; |
| |
| /// Initializes partitions on given block device. |
| InitializePartitionTables() -> (struct { |
| status zx.status; |
| }); |
| |
| /// Wipes all entries from the partition table of the specified block device. |
| /// Currently only supported on devices with a GPT. |
| /// |
| /// *WARNING*: This API may destructively remove non-fuchsia maintained partitions from |
| /// the block device. |
| WipePartitionTables() -> (struct { |
| status zx.status; |
| }); |
| }; |
| |
| /// Protocol for managing boot configurations. |
| /// |
| /// All functions will first check the A/B/R metadata and reset it to |
| /// the default state if it's invalid. |
| /// The new configuration is not guaranteed to persist to storage before Flush() is called. |
| protocol BootManager { |
| /// Queries the configuration the system is currently running. |
| /// |
| /// Returns `ZX_ERR_NOT_SUPPORTED` if the `zvb.current_slot` boot argument cannot be read |
| /// or is an unexpected value. |
| QueryCurrentConfiguration() -> (struct { |
| configuration Configuration; |
| }) error zx.status; |
| |
| /// Queries the configuration which will be used as the default boot choice on a normal cold |
| /// boot, which may differ from the currently running configuration. `Configuration::RECOVERY` |
| /// should never be active. |
| /// |
| /// Returns `ZX_ERR_NOT_SUPPORTED` if `Configuration.RECOVERY` is active. |
| QueryActiveConfiguration() -> (struct { |
| configuration Configuration; |
| }) error zx.status; |
| |
| /// Queries the configuration that was last explicitly marked as active by |
| /// SetConfigurationActive(). The result is not affected by the current status of the slot. |
| /// |
| /// A newly updated slot is typically marked as active immediately. Therefore this interface |
| /// can be used as a way to identify the newest slot. |
| /// |
| /// Returns `ZX_ERR_IO` if fail to load abr metadata. Returns `ZX_ERR_INTERNAL` if invalid |
| /// slot index is returned by libabr routine. |
| QueryConfigurationLastSetActive() -> (struct { |
| configuration Configuration; |
| }) error zx.status; |
| |
| /// Queries status of `configuration`. |
| /// |
| /// Returns `ZX_ERR_INVALID_ARGS` if `Configuration.RECOVERY` is passed in via `configuration`. |
| QueryConfigurationStatus(struct { |
| configuration Configuration; |
| }) -> (struct { |
| status ConfigurationStatus; |
| }) error zx.status; |
| |
| /// Updates persistent metadata identifying which configuration should be selected as 'primary' |
| /// for booting purposes. Should only be called after `KERNEL` as well as optional |
| /// `VERIFIED_BOOT_METADATA` assets for specified `configuration` were written successfully. |
| /// |
| /// Returns `ZX_ERR_INVALID_ARGS` if `Configuration.RECOVERY` is passed in via `configuration`. |
| SetConfigurationActive(struct { |
| configuration Configuration; |
| }) -> (struct { |
| status zx.status; |
| }); |
| |
| /// Updates persistent metadata identifying whether `configuration` is bootable. |
| /// Should only be called in the following situations: |
| /// * Before `KERNEL` as well as optional `VERIFIED_BOOT_METADATA` assets for specified |
| /// `configuration` are written. |
| /// * After successfully booting from a new configuration and marking it healthy. This method |
| /// would be then called on the old configuration. |
| /// * After "successfully" booting from a new configuration, but encountering an unrecoverable |
| /// error during health check. This method would be then called on the new configuration. |
| /// |
| /// If the configuration is unbootable, no action is taken. |
| /// |
| /// Returns `ZX_ERR_INVALID_ARGS` if `Configuration.RECOVERY` is passed in via `configuration`. |
| SetConfigurationUnbootable(struct { |
| configuration Configuration; |
| }) -> (struct { |
| status zx.status; |
| }); |
| |
| /// Updates persistent metadata to mark a [`fuchsia.paver/Configuration`] |
| /// as successful. |
| /// |
| /// This function is typically used by the OS update system after having |
| /// confirmed that the configuration works as intended and the "rollback to |
| /// previous slot" logic is not needed anymore. |
| /// |
| /// Compatibility between the newly successful configuration and the other |
| /// configuration is unknown. Even if the other configuration was |
| /// successful at one point, it may no longer be. This function adds a |
| /// success mark to the given configuration but also removes any success |
| /// mark on the other. |
| /// |
| /// If `configuration` is unbootable or is |
| /// [`fuchsia.paver/Configuration.RECOVERY`], `response` will be |
| /// `ZX_ERR_INVALID_ARGS`. |
| /// |
| /// + request `configuration` the `Configuration` to mark as healthy. Must |
| /// not be `RECOVERY`. |
| /// - response `status` a zx_status value indicating success or failure. |
| SetConfigurationHealthy(struct { |
| configuration Configuration; |
| }) -> (struct { |
| status zx.status; |
| }); |
| |
| /// Flush all previously buffered writes to persistent storage. |
| Flush() -> (struct { |
| status zx.status; |
| }); |
| }; |
| |
| /// Protocol that provides access to sysconfig-data sub-partition in sysconfig partition. |
| /// The main user of the protocol are pkg-solver and system update-checker, which need to |
| /// read/write sysconfig-data channel. |
| protocol Sysconfig { |
| /// Read from the sub-partition |
| Read() -> (resource struct { |
| data fuchsia.mem.Buffer; |
| }) error zx.status; |
| |
| /// Writes to the sub-partition |
| Write(resource struct { |
| payload fuchsia.mem.Buffer; |
| }) -> (struct { |
| status zx.status; |
| }); |
| |
| /// Get sub-partition size. |
| GetPartitionSize() -> (struct { |
| size uint64; |
| }) error zx.status; |
| |
| /// Flush all previously buffered data to persistent storage. |
| Flush() -> (struct { |
| status zx.status; |
| }); |
| |
| /// Wipe all data in the sub-partition (write 0 to all bytes). |
| Wipe() -> (struct { |
| status zx.status; |
| }); |
| }; |