// SPDX-License-Identifier: MIT OR Apache-2.0

//! UEFI update capsules.
//!
//! Capsules are used to pass information to the firmware, for example to
//! trigger a firmware update.

use crate::{Guid, PhysicalAddress};
use bitflags::bitflags;

/// Descriptor that defines a scatter-gather list for passing a set of capsules
/// to the firmware.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct CapsuleBlockDescriptor {
    /// Size in bytes of the data block. If zero, the block is treated as a
    /// continuation pointer.
    pub length: u64,

    /// Either a data block pointer or a continuation pointer.
    ///
    /// * If `length` is non-zero, this is the physical address of the data
    ///   block.
    /// * If `length` is zero:
    ///   * If `addr` is non-zero, this is the physical address of another block
    ///     of `CapsuleBlockDescriptor`.
    ///   * If `addr` is zero, this entry represents the end of the list.
    pub address: PhysicalAddress,
}

bitflags! {
    /// Capsule update flags.
    ///
    /// The meaning of bits `0..=15` are defined by the capsule GUID.
    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
    #[repr(transparent)]
    pub struct CapsuleFlags: u32 {
        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_0 = 1 << 0;

        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_1 = 1 << 1;

        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_2 = 1 << 2;

        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_3 = 1 << 3;

        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_4 = 1 << 4;

        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_5 = 1 << 5;

        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_6 = 1 << 6;

        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_7 = 1 << 7;

        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_8 = 1 << 8;

        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_9 = 1 << 9;

        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_10 = 1 << 10;

        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_11 = 1 << 11;

        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_12 = 1 << 12;

        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_13 = 1 << 13;

        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_14 = 1 << 14;

        /// The meaning of this bit depends on the capsule GUID.
        const TYPE_SPECIFIC_BIT_15 = 1 << 15;

        /// Indicates the firmware should process the capsule after system reset.
        const PERSIST_ACROSS_RESET = 1 << 16;

        /// Causes the contents of the capsule to be coalesced from the
        /// scatter-gather list into a contiguous buffer, and then a pointer to
        /// that buffer will be placed in the configuration table after system
        /// reset.
        ///
        /// If this flag is set, [`PERSIST_ACROSS_RESET`] must be set as well.
        ///
        /// [`PERSIST_ACROSS_RESET`]: Self::PERSIST_ACROSS_RESET
        const POPULATE_SYSTEM_TABLE = 1 << 17;

        /// Trigger a system reset after passing the capsule to the firmware.
        ///
        /// If this flag is set, [`PERSIST_ACROSS_RESET`] must be set as well.
        ///
        /// [`PERSIST_ACROSS_RESET`]: Self::PERSIST_ACROSS_RESET
        const INITIATE_RESET = 1 << 18;
    }
}

/// Common header at the start of a capsule.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct CapsuleHeader {
    /// GUID that defines the type of data in the capsule.
    pub capsule_guid: Guid,

    /// Size in bytes of the capsule header. This may be larger than the size of
    /// `CapsuleHeader` since the specific capsule type defined by
    /// [`capsule_guid`] may add additional header fields.
    ///
    /// [`capsule_guid`]: Self::capsule_guid
    pub header_size: u32,

    /// Capsule update flags.
    pub flags: CapsuleFlags,

    /// Size in bytes of the entire capsule, including the header.
    pub capsule_image_size: u32,
}
