| // 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.device; |
| 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; |
| }; |
| |
| // The maximum string length for the `firmware type` parameter. |
| const MAX_FIRMWARE_TYPE_LENGTH uint32 = 256; |
| |
| /// Protocol for streaming the FVM payload. |
| closed protocol PayloadStream { |
| /// Registers a VMO to stream into. |
| /// |
| /// This can be called once per PayloadStream. |
| /// Any subsequent calls will return ZX_ERR_ALREADY_BOUND. |
| strict RegisterVmo(resource struct { |
| vmo zx.Handle:VMO; |
| }) -> (struct { |
| status zx.Status; |
| }); |
| |
| /// Reads data into the pre-registered vmo. |
| strict ReadData() -> (struct { |
| result ReadResult; |
| }); |
| }; |
| |
| @discoverable |
| closed 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. |
| strict 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. |
| strict UseBlockDevice(resource struct { |
| block_device client_end:fuchsia.hardware.block.Block; |
| block_controller client_end:fuchsia.device.Controller; |
| 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. |
| strict FindBootManager(resource struct { |
| boot_manager server_end:BootManager; |
| }); |
| |
| /// Find Sysconfig service. |
| strict 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() |
| closed protocol DataSink { |
| /// Reads the partition corresponding to `configuration` and `asset` into a vmo and returns it. |
| /// The size field of the returned `Buffer` will be the size of just the asset, if it can be |
| /// determined. Otherwise, it will be the size of the entire partition. |
| /// The size and stream size of the vmo in the returned `Buffer` will always be the size of the |
| /// entire partition. |
| strict 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. |
| strict 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`. |
| strict WriteFirmware(resource struct { |
| configuration Configuration; |
| type string:MAX_FIRMWARE_TYPE_LENGTH; |
| payload fuchsia.mem.Buffer; |
| }) -> (struct { |
| result WriteFirmwareResult; |
| }); |
| |
| /// Read firmware corresponding to `configuration` and `type`. |
| /// |
| /// Parameter `configuration` and `type` are the same as WriteFirmware. |
| /// |
| /// If ReadFirmware returns error, caller should assume that firmware image does not exist |
| /// or is in a bad state, or firmware read is not defined for the product. |
| strict ReadFirmware(resource struct { |
| configuration Configuration; |
| type string:MAX_FIRMWARE_TYPE_LENGTH; |
| }) -> (resource struct { |
| firmware fuchsia.mem.Buffer; |
| }) error zx.Status; |
| |
| /// Writes FVM with data from streamed via `payload`. This potentially affects all |
| /// configurations. |
| strict WriteVolumes(resource struct { |
| payload client_end:PayloadStream; |
| }) -> (struct { |
| status zx.Status; |
| }); |
| |
| /// Write a raw volume image to the device. The image will be passed as it is to the device |
| /// partitioner backend to write. Therefore the format and write logic for the image is up to |
| /// the product to define. It differs from WriteVolume(), which is specifically for writing the |
| /// FVM sparse image, in that the paver will not perform any FVM related parsing or other |
| /// operation of the image. Thus it is not dependent on the volume driver version and less |
| /// susceptible to an outdated paver. |
| /// |
| /// Returns ZX_ERR_NOT_SUPPORTED if the backend does not support opaque volume blobs. |
| strict WriteOpaqueVolume(resource struct { |
| payload fuchsia.mem.Buffer; |
| }) -> () error zx.Status; |
| |
| /// Writes an image in the Android Sparse format. Identical in behaviour to |
| /// `WriteOpaqueVolume`, except the contents of `payload` are parsed as a sparse image and |
| /// unpacked before being written to disk. |
| strict WriteSparseVolume(resource struct { |
| payload fuchsia.mem.Buffer; |
| }) -> () error zx.Status; |
| |
| /// Flush all previously buffered writes to persistent storage. |
| strict Flush() -> (struct { |
| status zx.Status; |
| }); |
| }; |
| |
| /// Specialized DataSink with dynamic partition tables. |
| closed protocol DynamicDataSink { |
| compose DataSink; |
| |
| /// Initializes partitions on given block device. |
| strict 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. |
| strict 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. |
| closed 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. |
| strict 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. |
| strict 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. |
| strict QueryConfigurationLastSetActive() -> (struct { |
| configuration Configuration; |
| }) error zx.Status; |
| |
| /// Queries status of `configuration`. |
| /// |
| /// Returns `ZX_ERR_INVALID_ARGS` if `Configuration.RECOVERY` is passed in via `configuration`. |
| strict 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`. |
| strict 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`. |
| strict 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. |
| strict SetConfigurationHealthy(struct { |
| configuration Configuration; |
| }) -> (struct { |
| status zx.Status; |
| }); |
| |
| /// Force device to boot to recovery in the next reboot/power cycle. This will only be |
| /// triggered once and will be reset after the reboot. State of A/B configuration slot will not |
| /// be affected. |
| strict SetOneShotRecovery() -> () error zx.Status; |
| |
| /// Flush all previously buffered writes to persistent storage. |
| strict 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. |
| closed protocol Sysconfig { |
| /// Read from the sub-partition |
| strict Read() -> (resource struct { |
| data fuchsia.mem.Buffer; |
| }) error zx.Status; |
| |
| /// Writes to the sub-partition |
| strict Write(resource struct { |
| payload fuchsia.mem.Buffer; |
| }) -> (struct { |
| status zx.Status; |
| }); |
| |
| /// Get sub-partition size. |
| strict GetPartitionSize() -> (struct { |
| size uint64; |
| }) error zx.Status; |
| |
| /// Flush all previously buffered data to persistent storage. |
| strict Flush() -> (struct { |
| status zx.Status; |
| }); |
| |
| /// Wipe all data in the sub-partition (write 0 to all bytes). |
| strict Wipe() -> (struct { |
| status zx.Status; |
| }); |
| }; |