Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ODP Embedded Controller

This book documents the public reference and demonstration firmware for embedded controllers built with Open Device Partnership components.

The repository contains shared platform support and development targets for NXP i.MXRT685S, NXP MCXA266, Nuvoton NPCX498M, and the QEMU RISC-V ec machine. See the repository README for build instructions and platform details.

The book is maintained with the source code in accordance with ODP RFC 0044.

Secure EC Overview

The Open Device Partnership (ODP) Secure Embedded Controller (Secure EC) project strives to build an open-source, microcontroller-agnostic embedded controller platform for the PC ecosystem. It combines secure firmware, standardized host interfaces, and reusable Rust components so device makers can reduce effort by building on a standardized platform that is secure by design while focusing on their unique value added.

Goals

  • Security by default leveraging memory-safe Rust
  • Standardize communication between the host operating system and the EC across hardware architectures and operating systems.
  • Provide reusable and customizable firmware services for battery, thermal, power, USB-C, HID, time alarms, and other common EC functions.
  • Support collaboration among device makers, silicon vendors, operating-system vendors, and the open-source community.
  • Reduce platform bring-up effort through shared abstractions, reference implementations, virtual platforms, and test tooling.

Architecture

Secure EC separates portable service logic from platform-specific hardware support:

  1. MCU HALs and board support provide access to clocks, GPIO, I2C, eSPI, I3C, and other peripherals.
  2. Subsystem interfaces decouple portable service logic from hardware implementations for battery, thermal, power, USB-C, HID, and other common EC subsystems.
  3. Embedded services implement reference application logic for EC subsystems.
  4. Host transports and protocols expose those services to the host through standardized interfaces

Security Direction

The project is working toward an EC that is an attestable platform component rather than an implicitly trusted boundary:

  • Secure boot verifies that firmware is authorized before execution.
  • DICE derives device identity from hardware secrets and firmware measurements.
  • Attestation allows host OS to assess the identity and state of the EC.
  • Encrypted communication provides a standardized mechanism for secure data exchange between the EC and the host OS.

Current Work

Current areas of investment include:

  • Expanding portable Rust services and drivers.
  • Supporting reference MCU platforms from multiple silicon vendors.
  • Developing a standardized HID communication protocol over eSPI and I3C transports with OS support.
  • Starting with secure boot and working toward device identity + attestation.
  • Using QEMU-based virtualized platforms for development, integration testing, and ecosystem onboarding.
  • Exploring how Rust-based Secure EC components and APIs can interoperate with Zephyr-based systems.

Key Repositories

RepositoryPurpose
odp-embedded-controllerReference EC firmware including hardware platforms and virtualized platforms. Refer to its README to get started. Secure EC top-level documentation is also included.
odp-platform-qemu-arm-virtEnd-to-end Secure EC test platform connecting a virtualized RISC-V EC to an Arm64 QEMU host running Windows Validation OS.
embedded-servicesReusable EC service implementations and interfaces, including battery, thermal, HID, time and alarms, and UART services.

The broader set of projects and community activity is available in the Open Device Partnership GitHub organization.

Development

Project development instructions are maintained in the repository README.

The following guides cover processes that require more detail than the main README:

Working with cargo vet

Introduction

cargo vet is a tool to help ensure that third-party Rust dependencies have been audited by a trusted entity. It matches all dependencies against a set of audits conducted by the authors of the project or entities they trust.
To learn more, visit mozilla/cargo-vet

This repository contains platform crates (platform/dev-imxrt, platform/dev-mcxa, platform/dev-npcx, platform/dev-qemu), each with its own Cargo.lock and its own supply-chain/ store. Run all cargo vet commands from the relevant platform directory.


Adding a new dependency

When updating or adding a new dependency, we need to ensure it’s audited before being merged into main.
For our repositories, we have designated experts who are responsible for vetting any new dependencies being added to their repository.
It is the shared responsibility of the developer creating the PR and the auditors to conduct a successful audit.
Follow the process below to ensure compliance:

For Developers

  1. Respond to cargo vet failures:
  • If your PR fails the cargo vet step, the cargo-vet workflow will add a comment to the PR with a template questionnaire
  • Copy the questionnaire, fill it out and paste it as a new comment on the PR. This greatly helps the auditors get some context of the changes requiring the new dependencies
  1. Engage with auditors:
  • Respond to any questions that the auditors might have regarding the need of any new dependencies
  1. Rebase and verify:
  • At their discretion, auditors will check in their audits into either rust-crate-audits or into the same repository
  • Once the new audits have been merged, rebase your branch on main and verify it passes cargo vet
    git fetch upstream
    git rebase upstream/main
    cargo vet
    
  1. Update PR:
  • If the audits were checked into rust-crate-audits, they will show up in imports.lock on running cargo vet. In this case add the updated imports.lock to your PR
  • If the audits were checked into the same repository, they will be present in audits.toml after rebase and you can simply force push to your PR after rebase
    git push -f
    
  1. Check PR status:
  • The existing PR comment from the previous failure will be updated with a success message once the check passes

For Auditors

  1. Review the questionnaire:
  • Check the filled questionnaire on the PR once the developer responds to the cargo vet failure
  • Respond to the developer comment in case more information is needed
  1. Audit new dependencies:
  1. Follow cargo vet recommendations:
  • Follow the recommendations of the cargo vet command output, either cargo vet diff for version update or cargo vet inspect for new dependencies
  1. Record audits:
  • Use cargo vet certify to add new audits to audits.toml
  • Verify all dependencies pass using cargo vet
  1. Decide audit location:
  • Shared audits: New audits should ideally be shared across ODP repositories to reduce the overhead of multiple audits for the same dependencies. To facilitate this, it’s recommended to cut and paste the new audits and submit as a separate PR to the audits.toml in rust-crate-audits
  • If due to business reasons, the audits are not to be shared across repositories, copy the updated audits.toml to a new branch off main in the same repository and submit the PR to update the audits
  1. Communicate successful audit:
  • Communicate to the PR developer via a PR comment so they can update the PR and get cargo vet to pass

Audit criteria

cargo vet comes pre-equipped with two built-in criteria but supports adding new criteria to suit our needs.
As defined here, the default criteria are:

  • safe-to-run
    This crate can be compiled, run, and tested on a local workstation or in controlled automation without surprising consequences, such as:

    • Reading or writing data from sensitive or unrelated parts of the filesystem.
    • Installing software or reconfiguring the device.
    • Connecting to untrusted network endpoints.
    • Misuse of system resources (e.g. cryptocurrency mining).
  • safe-to-deploy
    This crate will not introduce a serious security vulnerability to production software exposed to untrusted input.

    Auditors are not required to perform a full logic review of the entire crate. Rather, they must review enough to fully reason about the behavior of all unsafe blocks and usage of powerful imports. For any reasonable usage of the crate in real-world software, an attacker must not be able to manipulate the runtime behavior of these sections in an exploitable or surprising way.

    Ideally, all unsafe code is fully sound, and ambient capabilities (e.g. filesystem access) are hardened against manipulation and consistent with the advertised behavior of the crate. However, some discretion is permitted. In such cases, the nature of the discretion should be recorded in the notes field of the audit record.

    For crates which generate deployed code (e.g. build dependencies or procedural macros), reasonable usage of the crate should output code which meets the above criteria.

    Note: safe-to-deploy implies safe-to-run


Conducting an audit

When performing an audit for a new or updated dependency, auditors may consider the following criteria to ensure the safety, reliability, and suitability of the crate for use in our projects:

  • Security:

    • Review the crate for known vulnerabilities or security advisories.
    • Check for unsafe code usage and ensure it is justified and well-documented.
    • Evaluate the crate’s history of security issues and responsiveness to reported problems.
  • Maintenance and Activity:

    • Assess the frequency of updates and the responsiveness of maintainers to issues and pull requests.
    • Prefer crates that are actively maintained and have a healthy contributor base.
  • License Compliance:

    • Verify that the crate’s license is compatible with our project’s licensing requirements.
  • Community Trust and Adoption:

    • Consider the crate’s adoption in the wider Rust ecosystem.
    • Prefer crates that are widely used and trusted by the community.
  • Functionality and Suitability:

    • Confirm that the crate provides the required functionality without unnecessary features or bloat.
    • Evaluate whether the crate’s API is stable and unlikely to introduce breaking changes unexpectedly.
  • Audit Trail:

    • Record the audit decision, including any concerns, mitigations, or recommendations for future updates.
    • If exemptions are granted, document the justification and any follow-up actions required.

Tips for using cargo vet:

  • Update imports.lock:

    • Import trusted third party audits to reduce the number of new audits to be performed. Running cargo vet without --locked fetches new imports and updates imports.lock with any audits that are helpful for our project.
  • Add exemptions:

    • If an audit cannot be performed for some dependency due to time sensitivity or business justified reasons, use cargo vet add-exemption <PACKAGE> <VERSION> to add the dependency to exemptions in config.toml
    • To add all remaining audits to exemptions at once, use cargo vet regenerate exemptions
  • Prune unnecessary entries:

    • Remove unnecessary exemptions and imports using cargo vet prune

ODP Specification Documents

Adherence to ODP specifications enables component portability and auditing.

Embedded Controller Interface Specification

The Embedded Controller (EC) Interface Specification describes a base set of requirements for interfacing with core Windows features.

It covers the following areas:

  • Firmware Management
  • Battery
  • Time and Alarm
  • UCSI
  • Thermal
  • Power
  • Input Devices
  • Customization

Protocols and Stacks

There are 3 ways to communicate with the EC:

  • ACPI specification for Legacy EC controller (eSPI) based interface on x86 devices, directly accessed through OperationRegion in ACPI
  • ACPI through FFA driver and in secure world an SV or OEM customer driver to communicate with the EC on ARM devices
  • Directly through HID class driver for bus based devices, I2C, I3C and eSPI

This specification is organized accordingly and you can find the details for each protocol in the sub folder.

Legacy EC Interface

FFA EC Interface

HID EC Interface

Transports

While other transports can be wired up to work with OEM or SV drivers we are focused on supporting I2C, I3C and eSPI natively both through direct ACPI access and the HID stacks.

Today there is partial support for I2C and eSPI in ACPI and support for I2C and I3C on HID. To support eSPI natively work is being done to better standardize eSPI based on PCC specification.

The following is a visual of the driver stacks planned to interface with the EC from the OS.

CPU-to-Embedded-Controller Architecture

flowchart TB
    subgraph CPU["CPU / PC"]
        direction LR

        subgraph NSW["Non-Secure World"]
            direction TB

            subgraph STACK["Top-Level Stack"]
                direction LR
                INPUT["Input"]
                BATTERY["Battery"]
                TAD["TAD"]
                MPTF["MPTF"]
                OEM["OEM"]
            end

            subgraph HID_DRIVERS["HID Class Drivers"]
                direction LR
                INPUT_HID["Input HID Driver"]
                BATTERY_HID["Battery HID Driver"]
                TAD_HID["TAD HID Driver"]
                MPTF_HID["MPTF HID Driver"]
                OEM_HID["OEM HID Driver"]
            end

            INPUT --> INPUT_HID
            BATTERY --> BATTERY_HID
            TAD --> TAD_HID
            MPTF --> MPTF_HID
            OEM --> OEM_HID

            subgraph OS_DRIVERS["OS Driver Layer"]
                direction LR
                HIDCLASS["HIDClass Driver"]
                ACPI["ACPI Driver"]
            end

            INPUT_HID --> HIDCLASS
            BATTERY_HID --> HIDCLASS
            TAD_HID --> HIDCLASS
            MPTF_HID --> HIDCLASS
            OEM_HID --> HIDCLASS

            subgraph HID_SUPPORT["HID Transport Support"]
                direction LR
                HIDSPBCX["HIDSpbCx"]
                HIDI3C["HIDI3C"]
                HIDESPI["HIDeSPI"]
            end

            HIDCLASS --> HIDSPBCX
            HIDCLASS --> HIDI3C
            HIDCLASS --> HIDESPI

            I2C["I2C Transport"]
            I3C["I3C Transport"]
            ESPI["eSPI Transport"]

            HIDSPBCX --> I2C
            HIDI3C --> I3C
            HIDESPI --> ESPI

            FFA["FF-A"]
            I2CI3C["I2C/I3C"]
            ESPIA["ESPI"]
            ACPI --> FFA
            ACPI --> I2CI3C
            ACPI --> ESPIA
        end

        subgraph SW["Secure World"]
            direction TB
            HAFNIUM["Hafnium"]
            ECSP["EC SP"]
            CUSTOM["Custom Transport Driver"]

            HAFNIUM --> ECSP
            ECSP --> CUSTOM
        end

        FFA --> HAFNIUM
    end

    subgraph EC["Embedded Controller"]
        direction TB
        EC_IF["EC Transport Interfaces"]
        EC_FW["EC Firmware / Services"]
        EC_IF --> EC_FW
    end

    I2C -->|I2C| EC_IF
    I3C -->|I3C| EC_IF
    ESPI -->|eSPI| EC_IF
    ESPIA -->|eSPI| EC_IF
    I2CI3C --> |ACPI| EC_IF
    CUSTOM -->|Custom secure transport| EC_IF

    classDef nonsecure fill:#dbeafe,stroke:#2563eb,color:#111827;
    classDef secure fill:#fef3c7,stroke:#d97706,color:#111827;
    classDef controller fill:#dcfce7,stroke:#16a34a,color:#111827;
    class INPUT,BATTERY,TAD,MPTF,OEM,INPUT_HID,BATTERY_HID,TAD_HID,MPTF_HID,OEM_HID,HIDCLASS,ACPI,HIDSPBCX,HIDI3C,HIDESPI,I2C,I3C,ESPI,FFA nonsecure;
    class HAFNIUM,ECSP,CUSTOM secure;
    class EC_IF,EC_FW controller;

EC SOC Interface

EC Physical Interface

The interface by which the EC is physically wired to the SOC may vary depending on what interfaces are supported by the Silicon Vendor, EC manufacturer and OEM. It is recommended that a simple and low latency protocol is chosen such as eSPI, I3C, UART, memory.

EC Software Interface

There are several existing OS interfaces that exist today via ACPI and HID to manage thermal, battery, keyboard, touch etc. These existing structures need to keep working and any new interface must be created in such a way that it does not break existing interfaces. This document covers details on how to implement EC services in secure world and keep compatibility with non-secure EC OperationRegions. It is important to work towards a more robust solution that will handle routing, larger packets and security in a common way across OS’s and across SV architectures.

See the CPU-to-Embedded-Controller Architecture for the supported software stacks and transport paths.

Legacy EC Interface

ACPI specification has a definition for an embedded controller, however this implementation is tied very closely to the eSPI bus and x86 architecture.

The following is an example of legacy EC interface definition from ACPI

11.7. Thermal Zone Examples — ACPI Specification 6.4 documentation

Scope(\\_SB.PCI0.ISA0) {
  Device(EC0) {
    Name(_HID, EISAID("PNP0C09")) // ID for this EC

    // current resource description for this EC
    Name(_CRS, ResourceTemplate() {
      IO(Decode16,0x62,0x62,0,1)
      IO(Decode16,0x66,0x66,0,1)
    })

    Name(_GPE, 0) // GPE index for this EC
    
    // create EC's region and field for thermal support
    OperationRegion(EC0, EmbeddedControl, 0, 0xFF)
    Field(EC0, ByteAcc, Lock, Preserve) {
      MODE, 1, // thermal policy (quiet/perform)
      FAN, 1, // fan power (on/off)
      , 6, // reserved
      TMP, 16, // current temp
      AC0, 16, // active cooling temp (fan high)
      , 16, // reserved
      PSV, 16, // passive cooling temp
      HOT 16, // critical S4 temp
      CRT, 16 // critical temp
    }

    // following is a method that OSPM will schedule after
    // it receives an SCI and queries the EC to receive value 7
    Method(_Q07) {
      Notify (\\_SB.PCI0.ISA0.EC0.TZ0, 0x80)
    } // end of Notify method

    // fan cooling on/off - engaged at AC0 temp
    PowerResource(PFAN, 0, 0) {
      Method(_STA) { Return (\\_SB.PCI0.ISA0.EC0.FAN) } // check power state
      Method(_ON) { Store (One, \\\\_SB.PCI0.ISA0.EC0.FAN) } // turn on fan
      Method(_OFF) { Store ( Zero, \\\\_SB.PCI0.ISA0.EC0.FAN) }// turn off
fan
    }

    // Create FAN device object
    Device (FAN) {
    // Device ID for the FAN
    Name(_HID, EISAID("PNP0C0B"))
    // list power resource for the fan
    Name(_PR0, Package(){PFAN})
    }

    // create a thermal zone
    ThermalZone (TZ0) {
      Method(_TMP) { Return (\\_SB.PCI0.ISA0.EC0.TMP )} // get current temp
      Method(_AC0) { Return (\\_SB.PCI0.ISA0.EC0.AC0) } // fan high temp
      Name(_AL0, Package(){\\_SB.PCI0.ISA0.EC0.FAN}) // fan is act cool dev
      Method(_PSV) { Return (\\_SB.PCI0.ISA0.EC0.PSV) } // passive cooling
temp
      Name(_PSL, Package (){\\_SB.CPU0}) // passive cooling devices
      Method(_HOT) { Return (\\_SB.PCI0.ISA0.EC0.HOT) } // get critical S4
temp
      Method(_CRT) { Return (\\_SB.PCI0.ISA0.EC0.CRT) } // get critical temp
      Method(_SCP, 1) { Store (Arg1, \\\\_SB.PCI0.ISA0.EC0.MODE) } // set
cooling mode

      Name(_TSP, 150) // passive sampling = 15 sec
      Name(_TZP, 0) // polling not required
      Name (_STR, Unicode ("System thermal zone"))
    } // end of TZ0
  } // end of ECO
} // end of \\\\_SB.PCI0.ISA0 scope-

On platforms that do not support IO port access there is an option to define MMIO regions to simulate the IO port transactions.

In the above example you can see that the operation region directly maps to features on the EC and you can change the EC behavior by writing to a byte in the region or reading the latest data from the EC.

For a system with the EC connected via eSPI and that needs a simple non-secure interface to the EC the above mapping works very well and keeps the code simple. The eSPI protocol itself has details on port accesses and uses the peripheral channel to easily read/write memory mapped regions.

As the EC features evolve there are several requirements that do no work well with this interface:

  • Different buses such as I3C, SPI, UART target a packet request/response rather than a memory mapped interface

  • Protected or restricted access and validation of request/response

  • Firmware update, large data driven requests that require larger data response the 256-byte region is limited

  • Discoverability of features available and OEM customizations

  • Out of order completion of requests, concurrency, routing and priority handling

As we try to address these limitations and move to a more packet based protocol described in this document. The following section covers details on how to adopt existing operation region to new ACPI functionality.

Adopting EC Operation Region

The new OS frameworks such as MPTF still use ACPI methods as primary interface. Instead of defining devices such as FAN or ThermalZone in the EC region you can simply define the EC region itself and then map all the other ACPI functions to operate on this region. This will allow you to maintain backwards compatibility with existing EC definitions.

Device(EC0) {
  Name(_HID, EISAID("PNP0C09")) // ID for this EC
  // current resource description for this EC
  Name(_CRS, ResourceTemplate() {
    IO(Decode16,0x62,0x62,0,1)
    IO(Decode16,0x66,0x66,0,1)
  })

  // create EC's region and field for thermal support
  OperationRegion(EC0, EmbeddedControl, 0, 0xFF)
  Field(EC0, ByteAcc, Lock, Preserve) {
    MODE, 1, // thermal policy (quiet/perform)
    FAN, 1, // fan power (on/off)
    , 6, // reserved
    TMP, 16, // current temp
    AC0, 16, // active cooling temp (fan high)
    , 16, // reserved
    PSV, 16, // passive cooling temp
    HOT 16, // critical S4 temp
    CRT, 16 // critical temp
  }
}

Device(SKIN) {
  Name(_HID, "MSFT000A") // New MPTF HID Temperature Device
  Method(_TMP, 0x0, Serialized) {
      Return( \\_SB.PCI0.ISA0.EC0.TMP)
  }
}

For more complicated functions that take a package some of the data may be constructed within ACPI and some of the data pulled from the OperationRegion. For example BIX for battery information may have a combination of static and dynamic data like this:

Method (_BIX) {
  Name (BAT0, Package (0x12)
  {
    0x01, // Revision
    0x02, // Power Unit
    0x03, // Design Capacity
    \\_SB.PCI0.ISA0.EC0.BFCC, // Last Full Charge Capacity
    0x05, // Battery Technology
    0x06, // Design Voltage
    0x07, // Design capacity of Warning
    0x08, // Design Capacity of Low
    \\_SB.PCI0.ISA0.EC0.BCYL, // Cycle Count
    0x0A, // Measurement Accuracy
    0x0B, // Max Sampling Time
    0x0C, // Min Sampling Time
    0x0D, // Max Averaging Interval
    0x0E, // Min Averaging Interval
    0x0F, // Battery Capacity Granularity 1
    0x10, // Battery Capacity Granularity 2
    "Model123", // Model Number
    "Serial456", // Serial Number
    "Li-Ion", // Battery Type
    "OEMName" // OEM Information
  })
  Return(BAT0)
}

Limitations for using Legacy EC

Before using the Legacy EC definition OEM’s should be aware of several use cases that may limit you ability to use it.

ACPI support for eSPI master

In the case of Legacy EC the communication to the EC is accomplished directly by the ACPI driver using PORT IO and eSPI Peripheral Bus commands. On ARM platforms there is no PORT IO and these must be substituted with MMIO regions. The ACPI driver needs changes to support MMIO which is being evaluated and support is not yet available. Some Silicon Vendors also do not implement the full eSPI specification and as such the ACPI driver cannot handle all the communication needs. On these platforms using Legacy EC interface is not an option.

Security of eSPI bus

When non-secure world is given access to the eSPI bus it can send commands to device on that bus. Some HW designs have the TPM or SPINOR on the same physical bus as the EC. On these designs allowing non-secure world to directly sends commands to EC can break the security requirements of other devices on the bus. In these cases the eSPI communication must be done in the secure world over FF-A as covered in this document and not use the Legacy EC channel. Since non-secure world has complete access to the EC operation region there is no chance for encryption of data. All data in the operation region is considered non-secure.

Functional limitations of Legacy EC

The peripheral region that is mapped in the Legacy EC in ACPI is limited to 256 bytes and notification events to the ones that are defined and handled in ACPI driver. To create custom solutions, send large packets or support encryption of data the Legacy EC interface has limitations in this area.

ACPI to FFA Services Overview

In this section we review a system design where the EC communication is in the secure world running in a dedicated SP.

The following GUID’s have been designed to represent each service operating in the secure partition for EC.

EC Service NameService GUIDDescription
EC_SVC_MANAGEMENT330c1273-fde5-4757-9819-5b6539037502Used to query EC functionality, Board info, version, security state, FW update
EC_SVC_POWER7157addf-2fbe-4c63-ae95-efac16e3b01cHandles general power related requests and OS Sx state transition state notification
EC_SVC_BATTERY25cb5207-ac36-427d-aaef-3aa78877d27eHandles battery info, status, charging
EC_SVC_THERMAL31f56da7-593c-4d72-a4b3-8fc7171ac073Handles thermal requests for skin and other thermal events
EC_SVC_UCSI65467f50-827f-4e4f-8770-dbf4c3f77f45Handles PD notifications and calls to UCSI interface
EC_SVC_INPUTe3168a99-4a57-4a2b-8c5e-11bcfec73406Handles wake events, power key, lid, input devices (HID separate instance)
EC_SVC_TIME_ALARM23ea63ed-b593-46ea-b027-8924df88e92fHandles RTC and wake timers.
EC_SVC_DEBUG0bd66c7c-a288-48a6-afc8-e2200c03eb62Used for telemetry, debug control, recovery modes, logs, etc
EC_SVC_TEST6c44c879-d0bc-41d3-bef6-60432182dfe6Used to send commands for manufacturing/factory test
EC_SVC_OEM19a8a1e88-a880-447c-830d-6d764e9172bbSample OEM custom service and example piping of events

Note: EC_SVC_UCSI (65467f50-827f-4e4f-8770-dbf4c3f77f45) is the FF-A routing UUID that selects the UCSI service in secure world. It is distinct from the UCSI _DSM selector 6f8398c2-7ca4-11e4-ad36-631042b5008f, which is the OS-standard argument the _DSM method matches on. The two must not be confused.

FFA Overview

This section covers the components involved in sending a command to EC through the FFA flow in windows. This path is specific to ARM devices for x86 devices should use HID or direct access. Those will continue through the non-secure OperationRegion.

ARM has a standard for calling into the secure world through SMC’s and targeting a particular service running in secure world via a UUID. The full specification and details can be found here: Firmware Framework for A-Profile

The windows kernel provides native ability for ACPI to directly send and receive FFA commands. It also provides a driver ffadrv.sys to expose a DDI that allows other drivers to directly send/receive FFA commands without needing to go through ACPI.

Hyper-V forwards the SMC’s through to EL3 to Hafnium which then uses the UUID to route the request to the correct SP and service. From the corresponding EC service it then calls into the eSPI or underlying transport layer to send and receive the request to the physical EC.

FFA Device Definition

The FFA device is loaded from ACPI during boot and as such requires a Device entry in ACPI

  Name(_HID, "ARML0002")

  OperationRegion(AFFH, FFixedHw, 2, 144) 
  Field(AFFH, BufferAcc, NoLock, Preserve) { AccessAs(BufferAcc, 0x1), FFAC, 1152 }     
    

  Name(_DSD, Package() {
      ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), //Device Prop UUID
      Package() {
        Package(2) {
          "arm-arml0002-ffa-ntf-bind",
          Package() {
              1, // Revision
              2, // Count of following packages
              Package () {
                     ToUUID("330c1273-fde5-4757-9819-5b6539037502"), // Service1 UUID
                     Package () {
                          0x01,     //Cookie1 (UINT32)
                          0x07,     //Cookie2
                      }
              },
              Package () {
                     ToUUID("b510b3a3-59f6-4054-ba7a-ff2eb1eac765"), // Service2 UUID
                     Package () {
                          0x01,     //Cookie1
                          0x03,     //Cookie2
                      }
             }
         }
      }
    }
  }) // _DSD()

  Method(_DSM, 0x4, NotSerialized)
  {
    // Arg0 - UUID
    // Arg1 - Revision
    // Arg2: Function Index
    //         0 - Query
    //         1 - Notify
    //         2 - binding failure
    //         3 - infra failure    
    // Arg3 - Data
  
    //
    // Device specific method used to query
    // configuration data. See ACPI 5.0 specification
    // for further details.
    //
    If(LEqual(Arg0, Buffer(0x10) {
        //
        // UUID: {7681541E-8827-4239-8D9D-36BE7FE12542}
        //
        0x1e, 0x54, 0x81, 0x76, 0x27, 0x88, 0x39, 0x42, 0x8d, 0x9d, 0x36, 0xbe, 0x7f, 0xe1, 0x25, 0x42
      }))
    {
      // Query Function
      If(LEqual(Arg2, Zero)) 
      {
        Return(Buffer(One) { 0x03 }) // Bitmask Query + Notify
      }
      
      // Notify Function
      If(LEqual(Arg2, One))
      {
        // Arg3 - Package {UUID, Cookie}
        Store(DeRefOf(Index(Arg3,1)), \_SB.ECT0.NEVT )
        Return(Zero) 
      }
    } Else {
      Return(Buffer(One) { 0x00 })
    }
  }

  Method(AVAL,0x0, Serialized)
  {
    Return(One)
  }
}

HID definition

The _HID “ARML0002” is reserved for FFA devices. Defining this HID for your device will cause the FFA interface for the OS to be loaded on this device.

Operation Region Definition

The operation region is marked as FFixedHw type 4 which lets the ACPI interpreter know that any read/write to this region requires special handling. The length is 144 bytes because this region operates on registers X0-X17 each of which are 8 bytes 18*8 = 144 bytes. This is mapped to FFAC is 1152 bits (144*8) and this field is where we act upon.

OperationRegion(AFFH, FFixedHw, 2, 144)
Field(AFFH, BufferAcc, NoLock, Preserve) { AccessAs(BufferAcc, 0x1),FFAC, 1152 }

When reading and writing from this operation region the FFA driver does some underlying mapping for X0-X3

X0 = 0xc400008d // FFA_DIRECT_REQ2
X1 = (Receiver Endpoint ID) | (Sender Endpoint ID \<\< 16)
X2/X3 = UUID

The following is the format of the request and response packets that are sent via ACPI

FFA_REQ_PACKET
{
  uint64 status; // Output status should be zero on input
  uint64 recvid; // Lower 16-bits is receiver ID, leave 0 for OS to populate
  uint128 UUID;
  uint8 reqdata[];
}

FFA_RSP_PACKET
{
  uint64 status;      // Output status from framework, zero on success
  uint64 sendrecvid;  // Sender and receiver ID's
  uint128 UUID;
  uint8 rspdata[];
}

CreateField(BUFF,0,64,STAT) // Out – Status for req/rsp
CreateField(BUFF,64,64,RECV) // In/Out – 16-bits for receiver ID
CreateField(BUFF,128,128,UUID) // In/Out - UUID of service

Inter Partition Setup Protocol

During FFA driver initialization it calls into secure world to get a list of all available services for each secure partition. When parsing the _DSD, for each service UUID a notification registration is sent for each cookie defined. The FFA driver will assign globally unique notification ID with each cookie that the corresponding service must use to trigger given notification going forward.

  Name(_DSD, Package() {
      ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), //Device Prop UUID
      Package() {
        Package(2) {
          "arm-arml0002-ffa-ntf-bind",
          Package() {
              1, // Revision
              1, // Count of following packages
              Package () {
                     ToUUID("330c1273-fde5-4757-9819-5b6539037502"), // Service1 UUID
                     Package () {
                          0x01,     //Cookie1 (UINT32)
                          0x07,     //Cookie2
                      }
              },
         }
      }
    }
  }) // _DSD()

In the above example we indicate that the OS will handle 2 different notification events for UUID 330c1273-fde5-4757-9819-5b6539037502 which is our EC management UUID. FFA knows which secure partition this maps to based on the list of services for each SP it has retrieved. Rather than having to keep track of all the physical bits in the bitmask that are used the FFA driver keeps track of this and allows each service to create a list of virtual ID’s they need to handle. The FFA driver then maps this to one of the available bits in the hardware bitmask and passes this mapping down to the notification service running in a given SP.

Please refer to ARM documentation for full details on Inter-partition protocol DEN0077A_Firmware_Framework_Arm_A-profile_1.3

Input

Parameter Register Value 
FFA_MSG_SEND_DIRECT_REQ2X00xC400008D
Sender/Receiver IdX1Bits[31:16]: Sender endpoint ID
Bits[15:0]: Receiver endpoint ID 
Protocol UUID lowX2Bytes[0..7] of Inter-partition setup protocol UUID 
Protocol UUID highX3Bytes[8..15] of Inter-partition setup protocol UUID 
Reserved SBZX40x0
Sender UUID lowX5Bytes[0..7] of service UUID 
Sender UUID highX6Bytes[8..15] of service UUID 
Receiver UUID lowX7Bytes[0..7] of service UUID 
Receiver UUID highX8Bytes[8..15] of service UUID 
Message InformationX9Bits[63:9]: Reserved MBZ
Bit[8]: Message Direction
- b’0 Request Message
Bits[7:3]: Reserved MBZ
Bits[2:0]: Message ID
- b’010: Notification registration for a service
Cookie InformationX10Bits[63:9]: Bits[63:9]: Reserved MBZ
Bits[8:0]: Count of (cookie,notification ID) tuples
- 1 <= Count <= 7
Tuple MappingX11-xX17Bits[63:32]: Cookie value
Bits[31:23]: Notification ID associated with cookie
Bits[22:1]: Reserved MBZ
Bit[0]: Per-vcpu notification flag
- b’0: Notification is a global notification
- b’1: Notification is per-vcpu notification

Output

Parameter Register Value 
FFA_MSG_SEND_DIRECT_RESP2X00xC400008E
Sender/Receiver IdX1Bits[31:16]: Sender endpoint ID
Bits[15:0]: Receiver endpoint ID 
Protocol UUID lowX2Bytes[0..7] of Inter-partition setup protocol UUID 
Protocol UUID highX3Bytes[8..15] of Inter-partition setup protocol UUID 
Reserved SBZX40x0
Sender UUID lowX5Bytes[0..7] of service UUID 
Sender UUID highX6Bytes[8..15] of service UUID 
Receiver UUID lowX7Bytes[0..7] of service UUID 
Receiver UUID highX8Bytes[8..15] of service UUID 
Message InformationX9Bits[63:9]: Reserved MBZ
Bit[8]: Message Direction
- b’1 Response Message
Bits[7:3]: Reserved MBZ
Bits[2:0]: Message ID
- b’010: Notification registration for a service
Cookie InformationX10Bits[63:9]: Bits[63:9]: Reserved MBZ
Bits[8:0]: Count of (cookie,notification ID) tuples
- 1 <= Count <= 7
Tuple MappingX11-xX17Bits[63:32]: Cookie value
Bits[31:23]: Notification ID associated with cookie
Bits[22:1]: Reserved MBZ
Bit[0]: Per-vcpu notification flag
- b’0: Notification is a global notification
- b’1: Notification is per-vcpu notification

 

Note this NOTIFICATION_REGISTER request is sent to the Inter-Partition Service UUID in the SP. The UUID of the service that the notifications are for are stored in X5/X6 registers shown above.

The UUID for notification service is {e474d87e-5731-4044-a727-cb3e8cf3c8df} which is stored in X2/X3.

Notification Events

All notification events sent from all secure partitions are passed back through the FFA driver. The notification calls the _DSM method. Function 0 is always a bitmap of all the other functions supported. We must support at least Query and Notify. The UUID is stored in Arg0 and the notification cookie is stored in Arg3 when Arg2 is 11.

  Method(_DSM, 0x4, NotSerialized)
  {
    // Arg0 - UUID  0194daab-ab08-7d5e-aea3-854bc457606a
    // Arg1 - Revision
    // Arg2: Function Index
    //         0 - Query
    //         1 - Notify
    //         2 - binding failure
    //         3 - infra failure    
    // Arg3 - Data
  
    //
    // Device specific method used to query
    // configuration data. See ACPI 5.0 specification
    // for further details.
    //
    If(LEqual(Arg0, Buffer(0x10) {
        //
        // UUID: {0194daab-ab08-7d5e-aea3-854bc457606a}
        //
        0x01, 0x94, 0xda, 0xab, 0xab, 0x08, 0x7d, 0x5e, 0xae, 0xa3, 0x85, 0x4b, 0xc4, 0x57, 0x60, 0x6a
      }))
    {
      // Query Function
      If(LEqual(Arg2, 0x0)) 
      {
        Return(Buffer(One) { 0x0f }) // Bitmask Query + Notify + binding failure + infra failure
      }
      
      // Notify Function
      If(LEqual(Arg2, 0x1))
      {
        // Arg3 - Package {UUID, Cookie}
        Store(DeRefOf(Index(Arg3,1)), \_SB.ECT0.NEVT )
      }

      // Binding Failure
      If(LEqual(Arg2, 0x2))
      {
        // Arg3 Binding failure details
      }

      // Infra Failure
      If(LEqual(Arg2, 0x3))
      {
        // Arg3 Infra failure details
      }
    }
    Return(Buffer(One) { 0x00 })
  }

The following is the call flow showing a secure interrupt arriving to the EC service which results in a notification back to ACPI. The notification payload can optionally be written to a shared buffer or ACPI can make another call back into EC service to retrieve the notification details.

In the _DSM, Arg2=1, Arg3 only contains the ID of the notification and no other payload, so both ACPI and the EC service must be designed either with shared memory buffer or a further notify data packet.

Runtime Requests

During runtime the non-secure side uses FFA_MSG_SEND_DIRECT_REQ2 requests to send requests to a given service within an SP. Any request that is expected to take longer than 1 ms should yield control back to the OS by calling FFA_YIELD within the service. When FFA_YIELD is called it will return control back to the OS to continue executing but the corresponding ACPI thread will be blocked until the original FFA request completes with DIRECT_RSP2. Note this creates a polling type interface where the OS will resume the SP thread after the timeout specified. The following is sample call sequence.

FFA Example Data Flow

For an example let’s take the battery status request _BST and follow data through.

FFA_REQ_PACKET req = {
  0x0, // Initialize to no error
  0x0, // Let the OS populate the sender/receiver ID
  {0x25,0xcb,0x52,0x07,0xac,0x36,0x42,0x7d,0xaa,0xef,0x3a,0xa7,0x88,0x77,0xd2,0x7e},
  0x2 // EC_BAT_GET_BST
}

The equivalent to write this data into a BUFF in ACPI is as follows

CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
CreateField(BUFF,64,64,RECV) // In/Out – Sender/Receiver ID
CreateField(BUFF,128,128,UUID) // UUID of service
CreateField(BUFF,256,8,CMDD) // In – First byte of command
CreateField(BUFF,256,128,BSTD) // Out – Raw data response 4 DWords
Store(0x2, CMDD)
Store(ToUUID ("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)
Store(Store(BUFF, \\_SB_.FFA0.FFAC), BUFF)

The ACPI interpreter when walking through this code creates a buffer and populates the data into buffer. The last line indicates to send this buffer over FFA interface.

ACPI calls into the FFA interface to send the data over to the secure world EC Service

typedef struct _FFA_INTERFACE {
    ULONG Version;
    PFFA_MSG_SEND_DIRECT_REQ2 SendDirectReq2;
} FFA_INTERFACE, PFFA_INTERFACE;

FFA Parsing

FFA is in charge of sending the SMC over to the secure world and routing to the correct service based on UUID.

X0 = SEND_DIRECT_REQ2 SMC command ID
X1 = Source ID and Destination ID
X2 = UUID Low
X3 = UUID High
X4-X17 = rawdata

Note: The status and length are not passed through to the secure world they are consumed only be ACPI.

HyperV and Monitor have a chance to filter or deny the request, but in general just pass the SMC request through to Hafnium

Hafnium extracts the data from the registers into an sp_msg structure which is directly mapping contents from x0-x17 into these fields.

pub struct FfaParams {
    pub x0: u64,
    pub x1: u64,
    pub x2: u64,
    pub x3: u64,
    pub x4: u64,
    pub x5: u64,
    pub x6: u64,
    pub x7: u64,
    pub x8: u64,
    pub x9: u64,
    pub x10: u64,
    pub x11: u64,
    pub x12: u64,
    pub x13: u64,
    pub x14: u64,
    pub x15: u64,
    pub x16: u64,
    pub x17: u64,
}

The EC service receives all direct messages through the odp-ffa crate in DirectMessage. You will find this conversion into the RegisterPayload here.

    fn try_from(value: SmcParams) -> Result<Self, Self::Error> {
        let source_id = (value.x1 & 0xFFFF) as u16;
        let destination_id = (value.x1 >> 16) as u16;

        let uuid_high = u64::from_be(value.x2);
        let uuid_low = u64::from_be(value.x3);
        let uuid = Uuid::from_u64_pair(uuid_high, uuid_low);

        // x4-x17 are for payload (14 registers)
        let payload_regs = [
            value.x4, value.x5, value.x6, value.x7, value.x8, value.x9, value.x10, value.x11, value.x12, value.x13,
            value.x14, value.x15, value.x16, value.x17,
        ];
        let payload_bytes_iter = payload_regs.iter().flat_map(|&reg| u64::to_le_bytes(reg).into_iter());

        let payload = RegisterPayload::from_iter(payload_bytes_iter);

        Ok(DirectMessage {
            source_id,
            destination_id,
            uuid,
            payload,
        })
    }

The destination_id is used to route the message to the correct SP, this is based on the ID field in the DTS description file. Eg: id = <0x8001>;

Embassy and Scheduling

The Secure Partition uses embassy as the scheduler for secure partition. This allows us to use await and do useful work while waiting for events even when we only are single threaded.

Embassy depends on timers and interrupts for signalling events. When we don’t have any work to do we still in the poll loop today. Optimizations can be made to yield control back to non-secure world in these situations.

EC Service Parsing

Within the EC partition there are several services that register their UUID to receive messages. You will find the main message loop and registration of each service in the embassy_main entry.

    service_list![
        ec_service_lib::services::Thermal::new(),
        ec_service_lib::services::Battery::new(),
        ec_service_lib::services::FwMgmt::new(),
        ec_service_lib::services::Notify::new()
    ]
    .run_message_loop()
    .await
    .expect("Error in run_message_loop");

Each service must implement the following 3 functions to register and allow it to recieve direct messages. The following is example implementation of the notification service entry.

const UUID: Uuid = uuid!("e474d87e-5731-4044-a727-cb3e8cf3c8df");

impl Service for Notify {
    fn service_name(&self) -> &'static str {
        "Notify"
    }

    fn service_uuid(&self) -> Uuid {
        UUID
    }

    async fn ffa_msg_send_direct_req2(&mut self, msg: MsgSendDirectReq2) -> Result<MsgSendDirectResp2> {
        let req: NotifyReq = msg.clone().into();
        debug!("Received notify command: {:?}", req.msg_info.message_id());

        let payload = match req.msg_info.message_id() {
            MessageID::Setup => RegisterPayload::from(self.nfy_setup(req)),
            MessageID::Destroy => RegisterPayload::from(self.nfy_destroy(req)),
            _ => {
                error!("Unknown Notify Command: {:?}", req.msg_info.message_id());
                return Err(odp_ffa::Error::Other("Unknown Notify Command"));
            }
        };

        Ok(MsgSendDirectResp2::from_req_with_payload(&msg, payload))
    }
}

Large Data Transfers

When making an FFA_MSG_SEND_DIRECT_REQ2 call the data is stored in registers X0-X17. X0-X3 are reserved to store the Function Id, Source Id, Destination Id and UUID. This leaves X4-X17 or 112 bytes. For larger messages they either need to be broken into multiple pieces or make use of a shared buffer between the OS and Secure Partition.

Shared Buffer Definitions

To create a shared buffer you need to modify the dts file for the secure partition to include mapping to your buffer.

ns_comm_buffer {
  description = "ns-comm";
  base-address = <0x00000100 0x60000000>;
  pages-count = <0x8>;
  attributes = <NON_SECURE_RW>;
};

During UEFI Platform initialization you will need to do the following steps, see the FFA specification for more details on these commands

  • FFA_MAP_RXTX_BUFFER
  • FFA_MEM_SHARE
  • FFA_MSG_SEND_DIRECT_REQ2 (EC_CAP_MEM_SHARE)
  • FFA_UNMAP_RXTX_BUFFER

The RXTX buffer is used during larger packet transfers but can be overridden and updated by the framework. The MEM_SHARE command uses the RXTX buffer so we first map that buffer then populate our memory descriptor requests to the TX_BUFFER and send to Hafnium. After sending the MEM_SHARE request we need to instruct our SP to retrieve this memory mapping request. This is done through our customer EC_CAP_MEM_SHARE request where we describe the shared memory region that UEFI has donated. From there we call FFA_MEM_RETRIEVE_REQ to map the shared memory that was described to Hafnium. After we are done with the RXTX buffers we must unmap them as the OS will re-map new RXTX buffers. From this point on both Non-secure and Secure side will have access to this shared memory buffer that was allocated.

Async Transfers

All services are single threaded by default. Even when doing FFA_YIELD it does not allow any new content to be executed within the service. If you need your service to be truly asynchronous you must have commands with delayed responses.

There is no packet identifier by default and tracking of requests and completion by FFA, so the sample solution given here is based on shared buffers defined in previous section and existing ACPI and FFA functionality.

Inside of our FFA functions rather than copying our data payload into the direct registers we define a queue in shared memory and populate the actual data into this queue entry. In the FFA_MSG_SEND_DIRECT_REQ2 we populate an ASYNC command ID (0x0) along with the seq #. The seq # is then used by the service to locate the request in the TX queue. We define a separate queue for RX and TX so we don’t need to synchronize between OS and secure partition.

ACPI Structures and Methods for Asynchronous

The SMTX is shared memory TX region definition

// Shared memory regions and ASYNC implementation
OperationRegion (SMTX, SystemMemory, 0x10060000000, 0x1000)

// Store our actual request to shared memory TX buffer
Field (SMTX, AnyAcc, NoLock, Preserve)
{
  TVER, 16,
  TCNT, 16,
  TRS0, 32,
  TB0, 64,
  TB1, 64,
  TB2, 64,
  TB3, 64,
  TB4, 64,
  TB5, 64,
  TB6, 64,
  TB7, 64,
  Offset(0x100), // First Entry starts at 256 byte offset each entry is 256 bytes
  TE0, 2048,
  TE1, 2048,
  TE2, 2048,
  TE3, 2048,
  TE4, 2048,
  TE5, 2048,
  TE6, 2048,
  TE7, 2048,
}

The QTXB method copies data into first available entry in the TX queue and returns sequence number used.

// Arg0 is buffer pointer
// Arg1 is length of Data
// Return Seq \#
Method(QTXB, 0x2, Serialized) {
  Name(TBX, 0x0)
  Store(Add(ShiftLeft(1,32),Add(ShiftLeft(Arg1,16),SEQN)),TBX)
  Increment(SEQN)
  // Loop until we find a free entry to populate
  While(One) {
    If(LEqual(And(TB0,0xFFFF),0x0)) {
      Store(TBX,TB0); Store(Arg0,TE0); Return( And(TBX,0xFFFF) )
    }

    If(LEqual(And(TB1,0xFFFF),0x0)) {
      Store(TBX,TB1); Store(Arg0,TE1); Return( And(TBX,0xFFFF) )
    }

    If(LEqual(And(TB2,0xFFFF),0x0)) {
      Store(TBX,TB2); Store(Arg0,TE2); Return( And(TBX,0xFFFF) )
    }

    If(LEqual(And(TB3,0xFFFF),0x0)) {
      Store(TBX,TB3); Store(Arg0,TE3); Return( And(TBX,0xFFFF) )
    }

    If(LEqual(And(TB4,0xFFFF),0x0)) {
      Store(TBX,TB4); Store(Arg0,TE4); Return( And(TBX,0xFFFF) )
    }

    If(LEqual(And(TB5,0xFFFF),0x0)) {
      Store(TBX,TB5); Store(Arg0,TE5); Return( And(TBX,0xFFFF) )
    }

    If(LEqual(And(TB6,0xFFFF),0x0)) {
      Store(TBX,TB6); Store(Arg0,TE6); Return( And(TBX,0xFFFF) )
    }

    If(LEqual(And(TB7,0xFFFF),0x0)) {
      Store(TBX,TB7); Store(Arg0,TE7); Return( And(TBX,0xFFFF) )
    }

    Sleep(5)
  }
}

The SMRX is shared memory region for RX queues

// Shared memory region
OperationRegion (SMRX, SystemMemory, 0x10060001000, 0x1000)

// Store our actual request to shared memory TX buffer
Field (SMRX, AnyAcc, NoLock, Preserve)
{
  RVER, 16,
  RCNT, 16,
  RRS0, 32,
  RB0, 64,
  RB1, 64,
  RB2, 64,
  RB3, 64,
  RB4, 64,
  RB5, 64,
  RB6, 64,
  RB7, 64,
  Offset(0x100), // First Entry starts at 256 byte offset each entry is 256 bytes
  RE0, 2048,
  RE1, 2048,
  RE2, 2048,
  RE3, 2048,
  RE4, 2048,
  RE5, 2048,
  RE6, 2048,
  RE7, 2048,
}

The RXDB function takes sequence number as input and will keep looping through all the entries until we see packet has completed. Sleeps for 5ms between each iteration to allow the OS to do other things and other ACPI threads can run.

// Allow multiple threads to wait for their SEQ packet at once
// If supporting packet \> 256 bytes need to modify to stitch together packet
Method(RXDB, 0x1, Serialized) {
  Name(BUFF, Buffer(256){})
  // Loop forever until we find our seq
  While (One) {
    If(LEqual(And(RB0,0xFFFF),Arg0)) {
      CreateField(BUFF, 0, Multiply(And(ShiftRight(RB0,16),0xFFFF),8), XB0)
      Store(RE0,BUFF); Store(0,RB0); Return( XB0 )
    }

    If(LEqual(And(RB1,0xFFFF),Arg0)) {
      CreateField(BUFF, 0, Multiply(And(ShiftRight(RB1,16),0xFFFF),8), XB1)
      Store(RE1,BUFF); Store(0,RB1); Return( XB1 )
    }

    If(LEqual(And(RB2,0xFFFF),Arg0)) {
      CreateField(BUFF, 0, Multiply(And(ShiftRight(RB2,16),0xFFFF),8), XB2)
      Store(RE2,BUFF); Store(0,RB2); Return( XB2 )
    }

    If(LEqual(And(RB3,0xFFFF),Arg0)) {
      CreateField(BUFF, 0, Multiply(And(ShiftRight(RB3,16),0xFFFF),8), XB3)
      Store(RE3,BUFF); Store(0,RB3); Return( XB3 )
    }

    If(LEqual(And(RB4,0xFFFF),Arg0)) {
      CreateField(BUFF, 0, Multiply(And(ShiftRight(RB4,16),0xFFFF),8), XB4)
      Store(RE4,BUFF); Store(0,RB4); Return( XB4 )
    }

    If(LEqual(And(RB5,0xFFFF),Arg0)) {
      CreateField(BUFF, 0, Multiply(And(ShiftRight(RB5,16),0xFFFF),8), XB5)
      Store(RE5,BUFF); Store(0,RB5); Return( XB5 )
    }

    If(LEqual(And(RB6,0xFFFF),Arg0)) {
      CreateField(BUFF, 0, Multiply(And(ShiftRight(RB6,16),0xFFFF),8), XB6)
      Store(RE6,BUFF); Store(0,RB6); Return( XB6 )
    }

    If(LEqual(And(RB7,0xFFFF),Arg0)) {
      CreateField(BUFF, 0, Multiply(And(ShiftRight(RB7,16),0xFFFF),8), XB7)
      Store(RE7,BUFF); Store(0,RB7); Return( XB7 )
    }

    Sleep(5)
  }

  // If we get here didn't find a matching sequence number
  Return (Ones)
}

The following is sample code to transmit a ASYNC request and wait for the data in the RX buffer.

Method(ASYC, 0x0, Serialized) {
  If(LEqual(\\_SB.FFA0.AVAL,One)) {
  CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
  CreateField(BUFF,128,128,UUID) // UUID of service
  CreateByteField(BUFF,32,CMDD) // Command register
  CreateWordField(BUFF,33,BSQN) // Sequence Number

  Store(0x0, CMDD) // EC_ASYNC command
  Local0 = QTXB(BUFF,20) // Copy data to our queue entry and get back SEQN
  Store(Local0,BSQN) // Sequence packet to read from shared memory
  Store(ToUUID("330c1273-fde5-4757-9819-5b6539037502"), UUID)
  Store(Store(BUFF, \\_SB_.FFA0.FFAC), BUFF)

  If(LEqual(STAT,0x0) ) // Check FF-A successful?
  {
    Return (RXDB(Local0)) // Loop through our RX queue till packet completes
  }
}

Recovery and Errors

The eSPI or bus driver is expected to detect if the EC is not responding and retry. The FFA driver will report back in the status byte if it cannot successfully talk to the secure world. If there are other failures generally they should be returned back up through ACPI with a value of (Ones) to indicate failure condition. This may cause some features to work incorrectly.

It is also expected that the EC has a watchdog if something on the EC is hung it should reset and reload on its own. The EC is also responsible for monitoring that the system is running within safe parameters. The thermal requests and queries are meant to be advisory in nature and EC should be able to run independently and safely without any intervention from the OS.

EC Firmware Management

This service is to provide details about the security state, supported features, debug, firmware version and firmware update functionality.

NIST SP 800-193 compliance requires failsafe update of primary and backup EC FW images. EC should run from primary partition while writing backup partitions and then change flag to indicate backup becomes primary and primary becomes backup.

Capability CommandDescription
EC_CAP_GET_FW_STATE = 0x1Return details of FW in EC, DICE, Secure Boot, Version, etc
EC_CAP_GET_SVC_LIST = 0x2Get list of services/features that this EC supports
EC_CAP_GET_BID = 0x3Read Board ID that is used customized behavior
EC_CAP_TEST_NFY = 0x4Create test notification event

Get Firmware State

Returns start of the overall EC if DICE and secure boot was enabled, currently running firmware version, EC status like boot failures.

Secure Boot and DICE

DICE is a specification from the Trusted Computing Group that allows the MCU to verify the signature of the code that it is executing, thereby establishing trust in the code. To do this, it has a primary bootloader program that reads the firmware on flash and using a key that is only accessible by the ROM bootloader, can verify the authenticity of the firmware. 

Trusted Platform Architecture - Device Identity Composition Engine (trustedcomputinggroup.org) 

Input Parameters

None

Output Parameters

Field Bits Description
FWVersion 16 Version of FW running on EC
SecureState 8

Bit mask representing the secure state of the device

0 – DICE is enabled

1 – Firmware is signed

BootStatus 8

Boot status and error codes

0 = SUCCESS

FFA ACPI Example

Method (TFWS) {
  // Check to make sure FFA is available and not unloaded
  If(LEqual(\\_SB.FFA0.AVAL,One)) {
    CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
    CreateField(BUFF,128,128,UUID) // UUID of service
    CreateByteField(BUFF,32, CMDD) // In – First byte of command
    CreateDwordField(BUFF,32,FWSD) // Out – Raw data response (overlaps with CMDD)

    Store(ToUUID("330c1273-fde5-4757-9819-5b6539037502"), UUID) // Management
    Store(0x1, CMDD) // EC_CAP_GET_FW_STATE
    Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

    If(LEqual(STAT,0x0) ) // Check FF-A successful?
    {
      Return (FWSD)
    } 
  }
  Return(Zero)
}

Get Features Supported

Get a list of services/features supported by this EC. Several features like HID devices are optional and may not be present. OEM services may also be added to this list as additional features supported.

Input Parameters

None

Output Parameters

FieldBitsDescription
DebugMask160 - Supports reset reason
1 - Supports debug tracing
BatteryMask80 - Battery 0 present
1 - Battery 1 present
…
FanMask80 - Fan 0 present
1 - Fan 1 present
…
ThermalMask80 - Skin TZ present
HIDMask80 - HID0 present
1 - HID1 present
…
KeyMask160 - Power key present
1 - LID switch present
2 - VolUp key present
3 - VolDown key present
4 - Camera key present

FFA ACPI Example

Method(TFET, 0x0, Serialized) {
  If(LEqual(\\_SB.FFA0.AVAL,One)) {
    CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
    CreateField(BUFF,128,128,UUID) // UUID of service
    CreateByteField(BUFF,32, CMDD) // In – First byte of command
    CreateWordField(BUFF,32,FET0) // DebugMask
    CreateByteField(BUFF,34,FET1) // BatteryMask
    CreateByteField(BUFF,35,FET2) // FanMask
    CreateByteField(BUFF,36,FET3) // ThermalMask
    CreateByteField(BUFF,37,FET4) // HIDMask
    CreateWordField(BUFF,38,FET5) // KeyMask

    Store(0x2, CMDD) // EC_CAP_GET_SVC_LIST
    Store(ToUUID("330c1273-fde5-4757-9819-5b6539037502"), UUID)
    Store(Store(BUFF, \\_SB_.FFA0.FFAC), BUFF)

    If(LEqual(STAT,0x0) ) {
      Return (package () {FET0,FET1,FET2,FET3,FET4,FET5})
    }
  }
  Return(package () {0,0,0,0,0,0,0})
}

Get Board ID

EC is often used to read pins or details to determine the HW configuration based on GPIO’s or ADC values. This ID allows SW to change behavior depending on this HW version information.

Input Parameters

None

Output Parameters

FieldBitsDescription
BoardID64Vendor defined

FFA ACPI Example

Method(TBID, 0x0, Serialized) {
  If(LEqual(\\_SB.FFA0.AVAL,One)) {
    CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
    CreateField(BUFF,128,128,UUID) // UUID of service
    CreateByteField(BUFF,32, CMDD) // In – First byte of command
    CreateDwordField(BUFF,32,BIDD) // Output Data

    Store(0x3, CMDD) // EC_CAP_GET_BID
    Store(ToUUID("330c1273-fde5-4757-9819-5b6539037502"), UUID)
    Store(Store(BUFF, \\_SB_.FFA0.FFAC), BUFF)

    If(LEqual(STAT,0x0) ) {
      Return (BIDD)
    } else {
  }
  Return(Zero)
}

Firmware Update

This should initiate update of a particular firmware in the backup partition to provide NIST SP 800-193 failsafe compliance. EC firmware update is planned to be handled through CFU. Further details are available in CFU specification.

EC Power Service

System Power State

OS calls in to notify EC or a change in system power state.

Perform appropriate power sequencing for the SoC from low power states (S3, S4, S5) to S0, and from S0 to low power states

Battery Service

Battery control is monitored through the Modern Power Thermal Framework (MPTF). See this specification for further details on implementing firmware for these features. This section outlines the interface required in ACPI for this framework to function.

Note: There is an issue with ACPI and embedded packages return Package() {BST0,BST1,BST2,BST3} returns “BST0”,“BST1”,“BST2”,“BST3” rather than the values pointed to by these variables. As such we need to create a global Name for BSTD and initialize default values and update these fields like the following.

  Name (BSTD, Package (4) {
    0x2,
    0x500,
    0x10000,
    0x3C28
  })
...
  BSTD[0] = BST0
  BSTD[1] = BST1
  BSTD[2] = BST2
  BSTD[3] = BST3
  Return(BSTD)
CommandDescription
EC_BAT_GET_BIX = 0x1Returns information about battery, model, serial number voltage. Note this is a superset of BIF. (MPTF)
EC_BAT_GET_BST = 0x2Get Battery Status, must also have notify event on state change. (MPTF)
EC_BAT_GET_PSR = 0x3Returns whether this power source device is currently online. (MPTF)
EC_BAT_GET_PIF = 0x4Returns static information about a power source. (MPTF)
EC_BAT_GET_BPS = 0x5Power delivery capabilities of battery at present time. (MPTF)
EC_BAT_SET_BTP = 0x6Set battery trip point to generate SCI event (MPTF)
EC_BAT_SET_BPT = 0x7Set Battery Power Threshold (MPTF)
EC_BAT_GET_BPC = 0x8Returns static variables that are associated with system power characteristics on the battery path and power threshold support settings. (MPTF)
EC_BAT_SET_BMC= 0x9Battery Maintenance Control
EC_BAT_GET_BMD = 0xAReturns battery information regarding charging and calibration
EC_BAT_GET_BCT = 0xBReturns battery charge time.
EC_BAT_GET_BTM = 0xCGet estimated runtime of battery while discharging
EC_BAT_SET_BMS = 0xDSets battery capacity sampling time in ms
EC_BAT_SET_BMA = 0xEBattery Measurement Average Interval
EC_BAT_GET_STA = 0xFGet battery availability

EC_BAT_GET_BIX

Returns information about battery, model, serial number voltage etc

Input Parameters

None

Output Parameters

Should return structure as defined by ACPI specification

10. Power Source and Power Meter Devices — ACPI Specification 6.4 documentation

FFA ACPI Example

  Name (BIXD, Package(21) {
    0,
    0,
    0x15F90,
    0x15F90,
    1,
    0x3C28,
    0x8F,
    0xE10,
    1,
    0x17318,
    0x03E8,
    0x03E8,
    0x03E8,
    0x03E8,
    0x380,
    0xE1,
    "        ",
    "        ",
    "        ",
    "        ",
    0
  })

  Method (_BIX, 0, Serialized) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) //  In – First byte of command
      CreateDwordField(BUFF,32,BIX0)  // Out – Revision
      CreateDwordField(BUFF,36,BIX1)  // Out – Power Unit
      CreateDwordField(BUFF,40,BIX2)  // Out – Design Capacity
      CreateDwordField(BUFF,44,BIX3)  // Out – Last Full Charge Capacity
      CreateDwordField(BUFF,48,BIX4)  // Out – Battery Technology
      CreateDwordField(BUFF,52,BIX5)  // Out – Design Voltage
      CreateDwordField(BUFF,56,BIX6)  // Out – Design Capacity of Warning
      CreateDwordField(BUFF,60,BIX7)  // Out – Design Capacity of Low
      CreateDwordField(BUFF,64,BIX8)  // Out – Cycle Count
      CreateDwordField(BUFF,68,BIX9)  // Out – Measurement Accuracy
      CreateDwordField(BUFF,72,BI10)  // Out – Max Sampling Time
      CreateDwordField(BUFF,76,BI11)  // Out – Min Sampling Time
      CreateDwordField(BUFF,80,BI12)  // Out – Max Averaging Internal
      CreateDwordField(BUFF,84,BI13)  // Out – Min Averaging Interval
      CreateDwordField(BUFF,88,BI14)  // Out – Battery Capacity Granularity 1
      CreateDwordField(BUFF,92,BI15)  // Out – Battery Capacity Granularity 2
      CreateField(BUFF,768,64,BI16)  // Out – Model Number
      CreateField(BUFF,832,64,BI17)  // Out – Serial number
      CreateField(BUFF,896,64,BI18)  // Out – Battery Type
      CreateField(BUFF,960,64,BI19)  // Out – OEM Information
      CreateDwordField(BUFF,128,BI20)  // Out – OEM Information

      Store(0x1, CMDD) //EC_BAT_GET_BIX
      Store(ToUUID("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)

      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        BIXD[0] = BIX0
        BIXD[1] = BIX1
        BIXD[2] = BIX2
        BIXD[3] = BIX3
        BIXD[4] = BIX4
        BIXD[5] = BIX5
        BIXD[6] = BIX6
        BIXD[7] = BIX7
        BIXD[8] = BIX8
        BIXD[9] = BIX9
        BIXD[10] = BI10
        BIXD[11] = BI11
        BIXD[12] = BI12
        BIXD[13] = BI13
        BIXD[14] = BI14
        BIXD[15] = BI15
        BIXD[16] = BI16
        BIXD[17] = BI17
        BIXD[18] = BI18
        BIXD[19] = BI19
        BIXD[20] = BI20
      }
    }
    Return(BIXD)
  }

EC_BAT_GET_BST

This object returns the present battery status. Whenever the Battery State value changes, the system will generate an SCI to notify the OS.

Input Parameters

None

Output Parameters

Should return structure as defined by ACPI specification

10. Power Source and Power Meter Devices — ACPI Specification 6.4 documentation

FFA ACPI Example

  Name (BSTD, Package (4) {
    0x2,
    0x500,
    0x10000,
    0x3C28
  })

  Method (_BST, 0, Serialized) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) //  In – First byte of command
      CreateDwordField(BUFF,32,BST0)  // Out – Battery State DWord
      CreateDwordField(BUFF,36,BST1)  // Out – Battery Rate DWord
      CreateDwordField(BUFF,40,BST2)  // Out – Battery Reamining Capacity DWord
      CreateDwordField(BUFF,44,BST3)  // Out – Battery Voltage DWord

      Store(0x2, CMDD) //EC_BAT_GET_BST
      Store(ToUUID("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)

      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        BSTD[0] = BST0
        BSTD[1] = BST1
        BSTD[2] = BST2
        BSTD[3] = BST3
      }
    }
    Return(BSTD)
  }

EC_BAT_GET_PSR

Returns whether the power source device is currently in use. This can be used to determine if system is running off this power supply or adapter. On mobile systes this will report that the system is not running on the AC adapter if any of the batteries in the system is being forced to discharge. In systems that contains multiple power sources, this object reports the power source’s online or offline status.

Input Parameters

None

Output Parameters

Should return structure as defined by ACPI specification

10. Power Source and Power Meter Devices — ACPI Specification 6.4 documentation

FFA ACPI Example

  Method (_PSR, 0, Serialized) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) //  In – First byte of command
      CreateDwordField(BUFF,32,PSR0)  // Out – Power Source

      Store(0x3, CMDD) //EC_BAT_GET_PSR
      Store(ToUUID("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)

      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        return(PSR0)
      }
    }

    Return(0)
  }

EC_BAT_GET_PIF

This object returns information about the Power Source, which remains constant until the Power Source is changed. When the power source changes, the platform issues a Notify(0x0) (Bus Check) to the Power Source device to indicate that OSPM must re-evaluate the _PIF object.

Input Parameters

None

Output Parameters

Should return structure as defined by ACPI specification

10. Power Source and Power Meter Devices — ACPI Specification 6.4 documentation

FFA ACPI Example

  Name( PIFD, Package(6) {
    0,          // Out – Power Source State
    0,          // Out – Maximum Output Power
    0,          // Out – Maximum Input Power
    "        ", // Out – Model Number
    "        ", // Out – Serial Number
    "        "  // Out – OEM Information
  })

  Method (_PIF, 0, Serialized) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) //  In – First byte of command
      CreateDwordField(BUFF,32,PIF0)  // Out – Power Source State
      CreateDwordField(BUFF,36,PIF1)  // Out – Maximum Output Power
      CreateDwordField(BUFF,40,PIF2)  // Out – Maximum Input Power
      CreateField(BUFF,352,64,PIF3)  // Out – Model Number
      CreateField(BUFF,416,64,PIF4)  // Out – Serial Number
      CreateField(BUFF,480,64,PIF5)  // Out – OEM Information

      Store(0x4, CMDD) //EC_BAT_GET_PIF
      Store(ToUUID("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)

      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        PIFD[0] = PIF0
        PIFD[1] = PIF1
        PIFD[2] = PIF2
        PIFD[3] = PIF3
        PIFD[4] = PIF4
        PIFD[5] = PIF5

      }
    }

    Return(PIFD)
  }

EC_BAT_GET_BPS

This optional object returns the power delivery capabilities of the battery at the present time. If multiple batteries are present within the system, the sum of peak power levels from each battery can be used to determine the total available power.

Input Parameters

None

Output Parameters

Should return structure as defined by ACPI specification

FFA ACPI Example

  Name( BPSD, Package(5) {
    0,  // Out – Revision
    0,  // Out – Instantaneous Peak Power Level
    0,  // Out – Instantaneous Peak Power Period
    0,  // Out – Sustainable Peak Power Level
    0  // Out – Sustainable Peak Power Period
  })

  Method (_BPS, 0, Serialized) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) //  In – First byte of command
      CreateDwordField(BUFF,32,BPS0)  // Out – Revision
      CreateDwordField(BUFF,36,BPS1)  // Out – Instantaneous Peak Power Level
      CreateDwordField(BUFF,40,BPS2)  // Out – Instantaneous Peak Power Period
      CreateDwordField(BUFF,44,BPS3)  // Out – Sustainable Peak Power Level
      CreateDwordField(BUFF,48,BPS4)  // Out – Sustainable Peak Power Period

      Store(0x5, CMDD) //EC_BAT_GET_BPS
      Store(ToUUID("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)

      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        BPSD[0] = BPS0
        BPSD[1] = BPS1
        BPSD[2] = BPS2
        BPSD[3] = BPS3
        BPSD[4] = BPS4
      }
    }
    Return(BPSD)
  }

EC_BAT_SET_BTP

This object is used to set a trip point to generate an SCI whenever the Battery Remaining Capacity reaches or crosses the value specified in the _BTP object. Required on systems supporting Modern Standby

Platform design for modern standby | Microsoft Learn

Input Parameters

See ACPI documentation for details

10. Power Source and Power Meter Devices — ACPI Specification 6.4 documentation

Output Parameters

None

FFA ACPI Example

  Method (_BTP, 1, Serialized) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) //  In – First byte of command
      CreateDwordField(BUFF,36,BTP0)  // In - Trip point value

      Store(0x6, CMDD) //EC_BAT_SET_BTP
      Store(Arg0, BTP0)
      Store(ToUUID("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)

      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        return(Zero )
      }
    }
    Return(Zero)
  }

EC_BAT_GET_BPC

This optional object returns static values that are used to configure power threshold support in the platform firmware. OSPM can use the information to determine the capabilities of power delivery and threshold support for each battery in the system.

Input Parameters

None

Output Parameters

Should return structure as defined by ACPI specification

10. Power Source and Power Meter Devices — ACPI Specification 6.4 documentation

FFA ACPI Example

  Name( BPCD, Package(4) {
    1,  // Out - Revision
    0,  // Out - Threshold support
    8000,  // Out - Max Inst peak power
    2000  // Out - Max Sust peak power
  })

  Method (_BPC, 0, Serialized) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) //  In – First byte of command
      CreateDwordField(BUFF,32,BPC0)  // Out - Revision
      CreateDwordField(BUFF,36,BPC1)  // Out - Threshold support
      CreateDwordField(BUFF,40,BPC2)  // Out - Max Inst peak power
      CreateDwordField(BUFF,44,BPC3)  // Out - Max Sust peak power

      Store(0x8, CMDD) //EC_BAT_GET_BPC
      Store(ToUUID("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)

      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        BPCD[0] = BPC0
        BPCD[1] = BPC1
        BPCD[2] = BPC2
        BPCD[3] = BPC3
      }
    }
    Return(BPCD)
  }

EC_BAT_SET_BPT

his optional object may be present under a battery device. OSPM must read _BPC first to determine the power delivery capability threshold support in the platform firmware and invoke this Method in order to program the threshold accordingly. If the platform does not support battery peak power thresholds, this Method should not be included in the namespace.

Input Parameters

See ACPI specification for input parameters

10. Power Source and Power Meter Devices — ACPI Specification 6.4 documentation

Output Parameters

Should return structure as defined by ACPI specification

10. Power Source and Power Meter Devices — ACPI Specification 6.4 documentation

FFA ACPI Example

  Method (_BPT, 3, Serialized) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) //  In – First byte of command
      CreateDwordField(BUFF,36,BPT0)  // In - Revision
      CreateDwordField(BUFF,40,BPT1)  // In - Threshold ID
      CreateDwordField(BUFF,44,BPT2)  // In - Threshold value
      CreateDwordField(BUFF,32,BPTS)  // Out - Trip point value

      Store(0x7, CMDD) //EC_BAT_SET_BPT
      Store(Arg0, BPT0)
      Store(Arg1, BPT1)
      Store(Arg2, BPT2)
      Store(ToUUID("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)

      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        return(BPTS)
      }
    }
    Return(Zero)
  }

EC_BAT_SET_BMC

This object is used to initiate calibration cycles or to control the charger and whether or not a battery is powering the system. This object is only present under a battery device if the _BMD Capabilities Flags field has bit 0, 1, 2, or 5 set.

Input Parameters

See ACPI specification for input parameter definition

10. Power Source and Power Meter Devices — ACPI Specification 6.4 documentation

Output Parameters

None

FFA ACPI Example

  Method (_BMC, 1, Serialized) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) //  In – First byte of command
      CreateDwordField(BUFF,36,BMC0)  // In - Feature control flags

      Store(0x9, CMDD) //EC_BAT_SET_BMC
      Store(Arg0, BMC0)
      Store(ToUUID("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)

      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
    }
    Return(Zero)
  }

EC_BAT_GET_BMD

This optional object returns information about the battery’s capabilities and current state in relation to battery calibration and charger control features. If the _BMC object (defined below) is present under a battery device, this object must also be present. Whenever the Status Flags value changes, AML code will issue a Notify(battery_device, 0x82). In addition, AML will issue a Notify(battery_device, 0x82) if evaluating _BMC did not result in causing the Status Flags to be set as indicated in that argument to _BMC. AML is not required to issue Notify(battery_device, 0x82) if the Status Flags change while evaluating _BMC unless the change does not correspond to the argument passed to _BMC.

10. Power Source and Power Meter Devices — ACPI Specification 6.4 documentation

Input Parameters

None

Output Parameters

Should return structure as defined by ACPI specification

FFA ACPI Example

  Name( BMDD, Package(5) {
    0,  // Out - Status
    0,  // Out - Capability Flags
    0,  // Out - Recalibrate count
    0,  // Out - Quick recal time
    0 // Out - Slow recal time
  })
  
  Method (_BMD, 0, Serialized) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) //  In – First byte of command
      CreateDwordField(BUFF,32,BMD0)  // Out - Status
      CreateDwordField(BUFF,36,BMD1)  // Out - Capability Flags
      CreateDwordField(BUFF,40,BMD2)  // Out - Recalibrate count
      CreateDwordField(BUFF,44,BMD3)  // Out - Quick recal time
      CreateDwordField(BUFF,48,BMD4)  // Out - Slow recal time

      Store(0xa, CMDD) //EC_BAT_GET_BMD
      Store(ToUUID("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)

      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        BMDD[0] = BMD0
        BMDD[1] = BMD1
        BMDD[2] = BMD2
        BMDD[3] = BMD3
        BMDD[4] = BMD4
      }
    }
    Return(BMDD)
  }

EC_BAT_GET_BCT

When the battery is charging, this optional object returns the estimated time from present to when it is charged to a given percentage of Last Full Charge Capacity.

10. Power Source and Power Meter Devices — ACPI Specification 6.4 documentation

Input Parameters

Input parameters as described in ACPI specification.

Output Parameters

Should return structure as defined by ACPI specification

FFA ACPI Example

  Method (_BCT, 1, Serialized) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) //  In – First byte of command
      CreateDwordField(BUFF,36,BCT0)  // In - ChargeLevel
      CreateDwordField(BUFF,32,BCTD)  // Out - Result

      Store(0xb, CMDD) //EC_BAT_GET_BCT
      Store(Arg0, BCT0)
      Store(ToUUID("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)

      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        return(BCTD)
      }
    }
    Return(Zero)
  }

EC_BAT_GET_BTM

This optional object returns the estimated runtime of the battery while it is discharging.

10. Power Source and Power Meter Devices — ACPI Specification 6.4 documentation

Input Parameters

Input parameters as described in ACPI specification.

Output Parameters

Should return structure as defined by ACPI specification

EC_BAT_SET_BMS

This object is used to set the sampling time of the battery capacity measurement, in milliseconds.

The Sampling Time is the duration between two consecutive measurements of the battery’s capacities specified in _BST, such as present rate and remaining capacity. If the OSPM makes two succeeding readings through _BST beyond the duration, two different results will be returned.

The OSPM may read the Max Sampling Time and Min Sampling Time with _BIX during boot time, and set a specific sampling time within the range with _BMS.

10. Power Source and Power Meter Devices — ACPI Specification 6.4 documentation

Input Parameters

Input parameters as described in ACPI specification.

Output Parameters

Should return structure as defined by ACPI specification

FFA ACPI Example

  Method (_BMS, 1, Serialized) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) //  In – First byte of command
      CreateDwordField(BUFF,36,BMS0)  // In - Sampling Time
      CreateDwordField(BUFF,32,BMSD)  // Out - Result code

      Store(0xd, CMDD) //EC_BAT_SET_BMS
      Store(Arg0, BMS0)
      Store(ToUUID("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)

      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        return(BMSD)
      }
    }
    Return(Zero)
  }

EC_BAT_SET_BMA

This object is used to set the averaging interval of the battery capacity measurement, in milliseconds. The Battery Measurement Averaging Interval is the length of time within which the battery averages the capacity measurements specified in _BST, such as remaining capacity and present rate.

The OSPM may read the Max Average Interval and Min Average Interval with _BIX during boot time, and set a specific average interval within the range with _BMA.

10. Power Source and Power Meter Devices — ACPI Specification 6.4 documentation

Input Parameters

Input parameters as described in ACPI specification.

Output Parameters

Should return structure as defined by ACPI specification

FFA ACPI Example

#![allow(unused)]
fn main() {
  Method (_BMA, 1, Serialized) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) //  In – First byte of command
      CreateDwordField(BUFF,36,BMA0)  // In - Averaging Interval
      CreateDwordField(BUFF,32,BMAD)  // Out - Result code

      Store(0xe, CMDD) //EC_BAT_SET_BMA
      Store(Arg0, BMA0)
      Store(ToUUID("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)

      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        return(BMAD)
      }
    }
    Return(Zero)
  }
}

EC_BAT_GET_STA

Returns battery status to the OS along with any error conditions as defined by ACPI specification.

Input Parameters

None

Output Parameters

Should return structure as defined by ACPI specification

10. Power Source and Power Meter Devices — ACPI Specification 6.4 documentation

FFA ACPI Example

#![allow(unused)]
fn main() {
  Method (BSTA, 0, Serialized) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) //  In – First byte of command
      CreateDwordField(BUFF,32,STAD)  // Out - Battery supported info

      Store(0xf, CMDD) //EC_BAT_GET_STA
      Store(ToUUID("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)

      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        return(STAD)
      }
    }
    Return(Zero)
  }
}

Thermal Zone Service

Battery temperature and other temperatures are read through a modified thermal interface called Microsoft Temperature Sensor that implements the _TMP and _DSM functionality. There is also still a generic thermal zone interface which has a few more entries for system outside of MPTF.

CommandDescription
EC_THM_GET_TMP = 0x1Returns the thermal zone’s current temperature in tenths of degrees.
EC_THM_SET_THRS = 0x2Sets the thresholds for high, low and timeout.
EC_THM_GET_THRS = 0x3Get thresholds for low and high points
EC_THM_SET_SCP = 0x4Set cooling Policy for thermal zone
EC_THM_GET_VAR = 0x5Read DWORD variable related to thermal
EC_THM_SET_VAR = 0x6Write DWORD variable related to thermal

EC_THM_GET_TMP

The Microsoft Thermal Sensor is a simplified ACPI Thermal Zone object, it only keeps the temperature input part of the thermal zone. It is used as the interface to send temperatures from the hardware to the OS. Like the thermal zone, Thermal Sensor also supports getting temperatures through _TMP method.

Input Parameters

Arg0 – Byte Thermal Zone Identifier

Output Parameters

An Integer containing the current temperature of the thermal zone (in tenths of degrees Kelvin)

The return value is the current temperature of the thermal zone in tenths of degrees Kelvin. For example, 300.0K is represented by the integer 3000.

FFA ACPI Example

Method (_TMP) {
  // Check to make sure FFA is available and not unloaded
  If(LEqual(\_SB.FFA0.AVAL,One)) {
    CreateDwordField(BUFF, 0, STAT) // Out – Status
    CreateField(BUFF, 128, 128, UUID) // UUID of service
    CreateByteField(BUFF, 32, CMDD) // Command register
    CreateByteField(BUFF, 33, TMP1) // In – Thermal Zone Identifier
    CreateDwordField(BUFF, 34, TMPD) // Out – temperature for TZ

    Store(0x1, CMDD) // EC_THM_GET_TMP
    Store(1,TMP1)
    Store(ToUUID("31f56da7-593c-4d72-a4b3-8fc7171ac073"), UUID) // Thermal
    Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

    If(LEqual(STAT,0x0) ) // Check FF-A successful?
    {
      Return (TMPD)
    } else {
      Return(Zero)
    }
  } else {
    Return(Zero)
  }
}

EC_THM_SET_THRS

Update thresholds for thermal zone

The platform should inform the OSPM to read _TMP method through Notify(device, 0x80) when any of below conditions is met: 

  • The Timeout has been met. 
  • The current temperature crosses the zone specified by LowTemperature or HighTemperature. 

Input Parameters

Arg0 – Byte Thermal Zone Identifier

Arg1 – Timeout // Integer (DWORD) in mS

Arg2 – LowTemperature // Integer (DWORD) in tenth deg Kelvin

Arg3 - HighTemperature // Integer (DWORD) in tenth deg Kelvin

Output Parameters

Integer with status

  • 0x00000000: Succeed 

  • 0x00000001: Failure, invalid parameter 

  • 0x00000002: Failure, unsupported revision 

  • 0x00000003: Failure, hardware error 

  • Others: Reserved 

FFA ACPI Example

Method(_DSM,4,Serialized,0,UnknownObj, {BuffObj, IntObj,IntObj,PkgObj}) {
  // Compare passed in UUID to Supported UUID
  If(LEqual(Arg0,ToUUID(“1f0849fc-a845-4fcf-865c-4101bf8e8d79 ”)))
  {

  // Implement function 1 which is update threshold
  If(LEqual(Arg2,One)) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF, 0, STAT) // Out – Status
      CreateField(BUFF, 128, 128, UUID) // UUID of service
      CreateByteField(BUFF, 32, CMDD) // Command register
      CreateByteField(BUFF, 33, TID1) // In – Thermal Zone Identifier
      CreateDwordField(BUFF, 34, THS1) // In – Timeout in ms
      CreateDwordField(BUFF, 38, THS2) // In – Low threshold tenth Kelvin
      CreateDwordField(BUFF, 42, THS3) // In – High threshold tenth Kelvin
      CreateDwordField(BUFF, 46, THSD) // Out – Status from EC

      Store(0x2, CMDD) // EC_THM_SET_THRS
      Store(1,TID1)
      Store(Arg0,THS1)
      Store(Arg1,THS2)
      Store(Arg2,THS3)
      Store(ToUUID("31f56da7-593c-4d72-a4b3-8fc7171ac073"), UUID) // Thermal
      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        Return (THSD)
      }
    }
    Return(Zero)
  }
}

EC_THM_GET_THRS

Read back thresholds that have been set or default thresholds that exist on the EC.

Input Parameters

Arg0 - Thermal ID – Identifier to determine which TZ to read the thresholds for

Output Parameters

Arg0 – Status // 0 on success or negative error code

Arg1 – Timeout // Integer (DWORD) in mS

Arg2 – LowTemperature // Integer (DWORD) in tenth deg Kelvin

Arg3 - HighTemperature // Integer (DWORD) in tenth deg Kelvin

FFA ACPI Example

Method(_DSM,4,Serialized,0,UnknownObj, {BuffObj, IntObj,IntObj,PkgObj}) {
  // Compare passed in UUID to Supported UUID
  If(LEqual(Arg0,ToUUID(“1f0849fc-a845-4fcf-865c-4101bf8e8d79 ”)))
  {
    // Implement function 2 which is update threshold
    If(LEqual(Arg2,Two)) {
      // Check to make sure FFA is available and not unloaded
      If(LEqual(\_SB.FFA0.AVAL,One)) {
        CreateDwordField(BUFF, 0, STAT) // Out – Status
        CreateField(BUFF, 128, 128, UUID) // UUID of service
        CreateByteField(BUFF, 32, CMDD) // Command register
        CreateByteField(BUFF, 33, TID1) // In – Thermal Zone Identifier
        CreateDwordField(BUFF, 34, THS1) // Out – Timeout in ms
        CreateDwordField(BUFF, 38, THS2) // Out – Low threshold tenth Kelvin
        CreateDwordField(BUFF, 42, THS3) // Out – High threshold tenth Kelvin

        Store(0x3, CMDD) // EC_THM_GET_THRS
        Store(1,TID1)
        Store(ToUUID("31f56da7-593c-4d72-a4b3-8fc7171ac073"), UUID) // Thermal
        Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

        If(LEqual(STAT,0x0) ) // Check FF-A successful?
        {
          Return (Package () {THS1, THS2, THS3})
        } 
    }
    Return(Zero)
  }
}

EC_THM_SET_SCP

This optional object is a control method that OSPM invokes to set the platform’s cooling mode policy setting. 

Input Parameters

Arg0 - Identifier to determine which TZ to read the thresholds for

Arg1 - Mode An Integer containing the cooling mode policy code

Arg2 - AcousticLimit An Integer containing the acoustic limit

Arg3 - PowerLimit An Integer containing the power limit

Output Parameters

Arg0 – Status from EC

  • 0x00000000: Succeed 

  • 0x00000001: Failure, invalid parameter 

  • 0x00000002: Failure, unsupported revision 

  • 0x00000003: Failure, hardware error 

  • Others: Reserved 

FFA ACPI Example

Method (_SCP) {
  // Check to make sure FFA is available and not unloaded
  If(LEqual(\_SB.FFA0.AVAL,One)) {
    CreateDwordField(BUFF, 0, STAT) // Out – Status
    CreateField(BUFF, 128, 128, UUID) // UUID of service
    CreateByteField(BUFF, 32, CMDD) // Command register
    CreateByteField(BUFF, 33, TID1) // In – Thermal Zone Identifier
    CreateDwordField(BUFF, 34, SCP1) // In – Timeout in ms
    CreateDwordField(BUFF, 38, SCP2) // In – Low threshold tenth Kelvin
    CreateDwordField(BUFF, 42, SCP3) // In – High threshold tenth Kelvin
    CreateDwordField(BUFF, 46, SCPD) // Out – Status from EC

    Store(0x4, CMDD) // EC_THM_SET_SCP
    Store(1,TID1)
    Store(Arg0,SCP1)
    Store(Arg1,SCP2)
    Store(Arg2,SCP3)
    Store(ToUUID("31f56da7-593c-4d72-a4b3-8fc7171ac073"), UUID) // Thermal
    Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

    If(LEqual(STAT,0x0) ) // Check FF-A successful?
    {
      Return (SCPD)
    }
  }
  Return(Zero)
}

EC_THM_GET_VAR

This API is to read a variable from the EC related to thermal. Variables are defined as GUID’s and include length of variable to read. In the case of default MPTF interface it is expecting a 32-bit variable.

Input Parameters

Arg0 – 128-bit UUID the defines the variable

Arg1 – 16-bit Length field specifies the length of variable in bytes

Output Parameters

Arg0 – 32-bit status field

  • 0x00000000: Succeed 

  • 0x00000001: Failure, invalid parameter 

  • 0x00000002: Failure, unsupported revision 

  • 0x00000003: Failure, hardware error 

  • Others: Reserved 

Var – Variable length data must match requested length otherwise should return error code

FFA ACPI Example

Method(GVAR,2,Serialized) {
  If(LEqual(\_SB.FFA0.AVAL,One)) {
    CreateDwordField(BUFF, 0, 64, STAT) // Out – Status
    CreateField(BUFF, 128, 128, UUID) // UUID of service
    CreateByteField(BUFF, 32, CMDD) // Command register
    CreateByteField(BUFF, 33, INST) // In – Instance ID
    CreateWordField(BUFF, 34, VLEN) // In – Variable Length in bytes
    CreateField(BUFF, 288, 128, VUID) // In – Variable UUID
    CreateQWordField(BUFF, 52, RVAL) // Out – Variable value

    Store(ToUUID("31f56da7-593c-4d72-a4b3-8fc7171ac073"), UUID)
    Store(0x5, CMDD) // EC_THM_GET_VAR
    Store(Arg0,INST) // Save instance ID
    Store(4,VLEN) // Variable is always DWORD here
    Store(Arg1, VUID)
    Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
  
    If(LEqual(STAT,0x0) ) // Check FF-A successful?
    {
     Return (RVAL)
    }
  }
  Return (Ones)
}

EC_THM_SET_VAR

This API is to write a variable to the EC related to thermal. Variables are defined as GUID’s and include length of variable to write. In the case of default MPTF interface it is expecting a 32-bit variable.

Input Parameters

Arg0 – 128-bit UUID the defines the variable

Arg1 – 16-bit Length field specifies the length of variable in bytes

Var - Variable length field of variable data

Output Parameters

Arg0 – 32-bit status field

  • 0x00000000: Succeed 

  • 0x00000001: Failure, invalid parameter 

  • 0x00000002: Failure, unsupported revision 

  • 0x00000003: Failure, hardware error 

  • Others: Reserved 

FFA ACPI Example

Method(SVAR,3,Serialized) {
  If(LEqual(\_SB.FFA0.AVAL,One)) {
    CreateQwordField(BUFF, 0, STAT) // Out – Status
    CreateField(BUFF, 128, 128, UUID) // UUID of service
    CreateByteField(BUFF, 32, CMDD) // Command register
    CreateByteField(BUFF, 33, INST) // In – Instance ID
    CreateWordField(BUFF, 34, VLEN) // In – Variable Length in bytes
    CreateField(BUFF, 288, 128, VUID) // In – Variable UUID
    CreateQwordField(BUFF, 52, DVAL) // In – Variable UUID
    CreateQwordField(BUFF, 60, RVAL) // Out – status

    Store(ToUUID("31f56da7-593c-4d72-a4b3-8fc7171ac073"), UUID)
    Store(0x6, CMDD) // EC_THM_SET_VAR
    Store(Arg0,INST) // Save instance ID
    Store(4,VLEN) // Variable is always DWORD here
    Store(Arg1, VUID)
    Store(Arg2,DVAL)
    Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

    If(LEqual(STAT,0x0) ) // Check FF-A successful?
    {
      Return (RVAL)
    }
  }
  Return (Ones)
}

Fan Service

The new MBTF framework depends on reading and writing variables on the EC to allow the EC to make the best decisions on cooling. The recommendations from the OS are aggregated on the EC side and decisions are made on setting FAN speed based on these.

All the control of fan and thermal parameters is done through variable interface using EC_THM_GET_VAR and EC_THM_SET_VAR.

Fan and Thermal variables

It is optional to implement Dba and Sones.

VariableGUIDDescription
OnTempba17b567-c368-48d5-bc6f-a312a41583c1Lowest temperature at which the fan is turned on.
RampTemp3a62688c-d95b-4d2d-bacc-90d7a5816bcdTemperature at which the fan starts ramping from min speed.
MaxTempdcb758b1-f0fd-4ec7-b2c0-ef1e2a547b76Temperature at top of fan ramp where fan is at maximum speed.
CrtTemp218246e7-baf6-45f1-aa13-07e4845256b8Critical temperature at which we need to shut down the system.
ProcHotTemp22dc52d2-fd0b-47ab-95b8-26552f9831a5Temperature at which the EC will assert the PROCHOT notification.
MinRpmdb261c77-934b-45e2-9742-256c62badb7aMinimum RPM FAN speed
MinDba (Optional)0457a722-58f4-41ca-b053-c7088fcfb89dMinimum Dba from FAN
MinSones (Optional)311668e2-09aa-416e-a7ce-7b978e7f88beMinimum Sones from FAN
MaxRpm5cf839df-8be7-42b9-9ac5-3403ca2c8a6aMaximum RPM for FAN
MaxDba (Optional)372ae76b-eb64-466d-ae6b-1228397cf374Maximum DBA for FAN
MaxSones (Optional)6deb7eb1-839a-4482-8757-502ac31b20b7Maximum Sones for FAN
ProfileType23b4a025-cdfd-4af9-a411-37a24c574615Set profile for EC, gaming, quiet, lap, etc
CurrentRpmadf95492-0776-4ffc-84f3-b6c8b5269683The current RPM of FAN
CurrentDba (Optional)4bb2ccd9-c7d7-4629-9fd6-1bc46300ee77The current Dba from FAN
CurrentSones (Optional)7719d686-02af-48a5-8283-20ba6ca2e940The current Sones from FAN

ACPI example of Input/Output _DSM

// Arg0 GUID
// 07ff6382-e29a-47c9-ac87-e79dad71dd82 - Input
// d9b9b7f3-2a3e-4064-8841-cb13d317669e - Output
// Arg1 Revision
// Arg2 Function Index
// Arg3 Function dependent

Method(_DSM, 0x4, Serialized) {
  // Input Variable
  If(LEqual(ToUuid("07ff6382-e29a-47c9-ac87-e79dad71dd82"),Arg0)) {
    Switch(Arg2) {
      Case(0) {
        // We support function 0-3
        Return(0xf)
      }
      Case(1) {
        Return(GVAR(1,ToUuid("ba17b567-c368-48d5-bc6f-a312a41583c1"))) // OnTemp
      }
      Case(2) {
        Return(GVAR(1,ToUuid("3a62688c-d95b-4d2d-bacc-90d7a5816bcd"))) // RampTemp
      }
      Case(3) {
        Return(GVAR(1,ToUuid("dcb758b1-f0fd-4ec7-b2c0-ef1e2a547b76"))) // MaxTemp
      }
    }
    Return(Ones)
  }

  // Output Variable
  If(LEqual(ToUuid("d9b9b7f3-2a3e-4064-8841-cb13d317669e"),Arg0)) {
    Switch(Arg2) {
      Case(0) {
        // We support function 0-3
        Return(0xf)
      }
      Case(1) {
        Return(SVAR(1,ToUuid("ba17b567-c368-48d5-bc6f-a312a41583c1"),Arg3)) // OnTemp
      }

      Case(2) {
        Return(SVAR(1,ToUuid("3a62688c-d95b-4d2d-bacc-90d7a5816bcd"),Arg3)) // RampTemp
      }

      Case(3) {
        Return(SVAR(1,ToUuid("dcb758b1-f0fd-4ec7-b2c0-ef1e2a547b76"),Arg3)) // MaxTemp
      }
    }
    Return(Ones)
  }
  Return (Ones)
}

UCSI Interface

EC must have the ability to interface with a discrete PD controller to negotiate power contracts/alt-modes with port partner

See the UCSI specification for commands that are required in all UCSI implementations.

USB-C Connector System Software Interface (UCSI) Driver - Windows drivers | Microsoft Learn

In addition to the commands marked as Required, Windows requires these commands:

  • GET_ALTERNATE_MODES

  • GET_CAM_SUPPORTED

  • GET_PDOS

  • SET_NOTIFICATION_ENABLE: The system or controller must support the following notifications within SET_NOTIFICATION_ENABLE:

    • Supported Provider Capabilities Change

    • Negotiated Power Level Change

  • GET_CONNECTOR_STATUS: The system or controller must support these connector status changes within GET_CONNECTOR_STATUS:

    • Supported Provider Capabilities Change

    • Negotiated Power Level Change

UCSI ACPI Interface

Shared Mailbox Interface

The following table is the reserved memory structure that must be reserved and shared with the EC for communication. When using FF-A this memory region must be statically carved out and 4K aligned and directly accessible by secure world.

Offset (Bytes)MnemonicDescriptionDirectionSize (bits)
0VERSIONUCSI Version NumberPPM->OPM16
2RESERVEDReservedN/A16
4CCIUSB Type-C Command Status and Connector Change IndicationPPM->OPM32
8CONTROLUSB Type-C ControlOPM->PPM64
16MESSAGE INUSB Type-C Message InPPM->OPM128
32MESSAGE OUTUSB Type-C Message OutOPM->PPM128

ACPI Definitions

Device(USBC) {
  Name(_HID,EISAID(“USBC000”))
  Name(_CID,EISAID(“PNP0CA0”))
  Name(_UID,1)
  Name(_DDN, “USB Type-C”)
  Name(_ADR,0x0)

  OperationRegion(USBC, SystemMemory, 0xFFFF0000, 0x30)
  Field(USBC,AnyAcc,Lock,Preserve)
  {
    // USB C Mailbox Interface
    VERS,16, // PPM-\>OPM Version
    RES, 16, // Reserved
    CCI, 32, // PPM-\>OPM CCI Indicator
    CTRL,64, // OPM-\>PPM Control Messages
    MSGI,128, // OPM-\>PPM Message In
    MSGO,128, // PPM-\>OPM Message Out
  }

  Method(_DSM,4,Serialized,0,UnknownObj, {BuffObj, IntObj,IntObj,PkgObj})
  {
    // Compare passed in UUID to Supported UUID
    If(LEqual(Arg0,ToUUID(“6f8398c2-7ca4-11e4-ad36-631042b5008f”)))
    {
      // Use FFA to send Notification event down to copy data to EC
      If(LEqual(\\_SB.FFA0.AVAL,One)) {
        CreateQwordField(BUFF,0,STAT) // Out – Status
        CreateField(BUFF,128,128,UUID) // UUID of service
        CreateByteField(BUFF,32, CMDD) // In – First byte of command
        CreateField(BUFF,288,384,FIFD) // Out – Msg data

        // Create UCSI Doorbell Event
        Store(0x0, CMDD) // UCSI set doorbell
        Store(ToUUID("65467f50-827f-4e4f-8770-dbf4c3f77f45"), UUID) // EC_SVC_UCSI
        Store(USBC, FIFD) // Copy output data
        Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

        If(LEqual(STAT,0x0) ) // Check FF-A successful?
        {
          Return (FIFD)
        }
      } // End AVAL
      Return(Zero)
    } // End UUID
  } // End DSM
}

The UCSI service is routed over FF-A using the EC_SVC_UCSI service UUID 65467f50-827f-4e4f-8770-dbf4c3f77f45. This is distinct from the UCSI _DSM selector 6f8398c2-7ca4-11e4-ad36-631042b5008f, which is the OS-standard argument the _DSM method matches on.

QEMU Test-Stub FF-A Transport

The normative interface above uses a statically carved-out, 4K-aligned shared-memory mailbox reachable from secure world. For deterministic integration testing under QEMU there is a simplified FF-A transport that carries the mailbox inline in the FF-A DIRECT_REQ2 register payload instead of a shared-memory region. This test transport is for the QEMU test stub only and does not replace the normative shared-memory ACPI interface described above.

Framing (routed to EC_SVC_UCSI, 65467f50-827f-4e4f-8770-dbf4c3f77f45):

  • Request payload:
    • byte 0 = doorbell/command tag 0x00 (UCSI set doorbell).
    • byte offsets 1 through 48 inclusive = the full 48-byte mailbox (VERSION..MESSAGE OUT) copied inline.
    • remaining bytes = zero padding.
  • Response payload:
    • byte offsets 0 through 47 inclusive = the updated 48-byte mailbox returned by the stub (VERSION, CCI, unchanged CONTROL, and populated MESSAGE IN).

Because the FF-A register payload comfortably holds the 49-byte request, no shared-memory carve-out or memory-retrieve mapping is required for the test stub.

EC Input Management

An EC may have several input devices including LID, Power key, touch and keyboard. HID based devices requiring low latency input, are recommended to be connected directly through a non-secure BUS interface such as I2C or I3C for performance reasons.

LID State

Monitor sensors that indicate lid state. If lid is opened, potentially boot the system. If lid is closed, potentially shut down or hibernate the system.

ACPIDescription
_LIDGet state of LID device for clamshell designs

ACPI Example for LID notification

Assuming that LID is managed by the EC during registration we register for Input Management service for a Virtual ID = 1

 Name(_DSD, Package() {
      ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), //Device Prop UUID
      Package() {
        Package(2) {
          "arm-arml0002-ffa-ntf-bind",
          Package() {
              1, // Revision
              1, // Count of following packages
              Package () {
                     ToUUID("e3168a99-4a57-4a2b-8c5e-11bcfec73406"), // Input Management UUID
                     Package () {
                          0x02,     // Cookie for LID
                      }
              },
         }
      }
    }
  }) // _DSD()

  Method(_DSM, 0x4, NotSerialized)
  {
    // Arg0 - UUID
    // Arg1 - Revision
    // Arg2: Function Index
    //         0 - Query
    //         1 - Notify
    //         2 - binding failure
    //         3 - infra failure    
    // Arg3 - Data
  
    //
    // Device specific method used to query
    // configuration data. See ACPI 5.0 specification
    // for further details.
    //
    If(LEqual(Arg0, Buffer(0x10) {
        //
        // UUID: {7681541E-8827-4239-8D9D-36BE7FE12542}
        //
        0x1e, 0x54, 0x81, 0x76, 0x27, 0x88, 0x39, 0x42, 0x8d, 0x9d, 0x36, 0xbe, 0x7f, 0xe1, 0x25, 0x42
      }))
    {
      // Query Function
      If(LEqual(Arg2, Zero)) 
      {
        Return(Buffer(One) { 0x03 }) // Bitmask Query + Notify
      }
      
      // Notify Function
      If(LEqual(Arg2, One))
      {
        // Arg3 - Package {UUID, Cookie}
        Local0 = DeRefOf(Index(Arg3,1))
        Switch(Local0) {
          Case(2) {
            Notify(\_SB._LID, 0x80)
          }
        }
      }
    }
    Return(Buffer(One) { 0x00 })
  }

System Wake Event

Ability to wake the system from various external events. This is for more complicated events that aren’t a simple GPIO for LID/Power button that require EC monitoring.

HID descriptor Interface

Communication with EC must have packet sent/received in HID format so the OS HIDClass driver can properly understand requests. At this time HID packets will go over HIDI2C but in future these HID packets could be included over a single interface.

HID IOCTLDescription
IOCTL_HID_GET_DEVICE_DESCRIPTORRetrieves the device’s HID descriptor
IOCTL_HID_GET_DEVICE_ATTRIBUTESRetrieves a device’s attributes in a HID_DEVICE_ATTRIBUTES structure
IOCTL_HID_GET_REPORT_DESCRIPTORObtains the report descriptor for the HID device
IOCTL_HID_READ_REPORTReturns a report from the device into a class driver-supplied buffer
IOCTL_HID_WRITE_REPORTTransmits a class driver-supplied report to the device
IOCTL_HID_GET_FEATUREGet capabilities of a feature from the device
IOCTL_HID_SET_FEATURESet/Enable a specific feature on device
IOCTL_HID_GET_INPUT_REPORTGet input report from HID device if input device
IOCTL_HID_SET_OUTPUT_REPORTSend output HID report to device
IOCTL_HID_GET_STRINGGet a specific string from device
IOCTL_HID_GET_INDEXED_STRINGGet a string from device based on index
IOCTL_HID_SEND_IDLE_NOTIFICATIONNotification to idle device into idle/sleep state

EC Time Alarm Service

The following sections define the operation and definition of the optional control method-based Time and Alarm device, which provides a hardware independent abstraction and a more robust alternative to the Real Time Clock (RTC)

ACPI specification details are in version 6.5 Chapter 9.

9. ACPI-Defined Devices and Device-Specific Objects — ACPI Specification 6.5 documentation (uefi.org)

CommandDescription
EC_TAS_GET_GCP = 0x1Get the capabilities of the time and alarm device
EC_TAS_GET_GRT = 0x2Get the Real Time
EC_TAS_SET_SRT = 0x3Set the Real Time
EC_TAS_GET_GWS = 0x4Get Wake Status
EC_TAS_SET_CWS = 0x5Clear Wake Status
EC_TAS_SET_STV = 0x6Set Timer value for given timer
EC_TAS_GET_TIV = 0x7Get Timer value remaining for given timer
EC_TAS_SET_STP = 0x8Set expired timer policy for given timer
EC_TAS_GET_TIP = 0x9Get expired timer policy for given timer

Relay-backed FF-A command layout

The service UUID is 23ea63ed-b593-46ea-b027-8924df88e92f. In an FF-A Direct Request v2 payload, byte 0 is the command and arguments start at byte 1. Response data starts at payload byte 0. These correspond to ACPI BUFF offsets 32, 33, and 32 respectively; they are not an EC peripheral-memory map. All multibyte argument and response fields are little-endian.

The secure-world handler forwards commands through the existing ODP relay (service ID 0x0B, MCTP message type 0x7D). The ODP header is big-endian and carries the command separately from the body. The implemented command and response IDs are defined in time-alarm-service-relay.

CommandArgument bytesEC success response ID / body
1 / _GCPNone1 / u32 capabilities
2 / _GRTNone2 / 16-byte ACPI timestamp
3 / _SRT16-byte ACPI timestamp6 / empty
4 / _GWSu32 timer ID3 / u32 wake status
5 / _CWSu32 timer ID6 / empty
6 / _STVu32 timer ID, u32 seconds6 / empty
7 / _TIVu32 timer ID5 / u32 seconds
8 / _STPu32 timer ID, u32 policy seconds6 / empty
9 / _TIPu32 timer ID4 / u32 policy seconds

Timer ID 0 selects AC; 1 selects DC. The expired-timer policy controls the delay after returning to the correct power source when a timer expired on the wrong source: 0 means immediately, and 0xFFFFFFFF means never. Other u32 values specify the delay in seconds. _CWS clears only the selected timer’s status, not its policy or the other timer’s status.

For setters (3, 5, 6, 8), the SP converts an empty EC success response into a u32 status at FF-A response offset 0: 0 for success, the EC error discriminant for a remote failure, or 0xFFFFFFFF for a local, transport, or protocol failure. The current EC service error is 1 (unspecified failure). FF-A framework status (STAT in the examples) is separate: successful FF-A delivery alone does not establish command success. The ACPI methods must translate any nonzero SP status or FF-A failure to their specified failure value: 0xFFFFFFFF for _SRT, and 1 for _CWS, _STV, and _STP. These method results are not raw transport error codes. At the FF-A service-payload level, scalar getter failures return 0xFFFFFFFF; _GRT failures return an all-zero invalid timestamp. These are SP responses delivered by a successful FF-A call, not ACPI getter fallback values when FF-A is unavailable or STAT is nonzero. For that separate case, the _GCP, _GWS, and _TIV examples below return Zero. A policy value of 0xFFFFFFFF is also valid, so an isolated _TIP read cannot distinguish that policy from a failure.

The timestamp body contains year (u16, offset 0), month/day/hour/minute/second (bytes 2..6), padding/valid (byte 7), milliseconds (u16, offset 8), timezone (i16, offset 10), daylight (byte 12), and three reserved zero bytes (13..15). For _SRT, byte 7 is padding (conventionally 0); for successful _GRT, it is the valid byte, 1. The current shared relay serializer also emits 1 for _SRT; the EC decoder accepts either. The SP forwards the input bytes unchanged rather than normalizing that compatibility difference.

The shared decoder accepts milliseconds 0..999 and daylight values 0, 1, and 3. ACPI 6.6 still lists milliseconds 1..1000 and does not explicitly reserve daylight value 2. These decoder restrictions are implementation compatibility choices, not corrections to the published ACPI specification.

These synchronous commands do not establish physical wake or asynchronous notification delivery. Power-source/wake integration and notification routing remain separate work; an EC triggered-wake status bit is bookkeeping, not proof that the host resumed.

EC_TAS_GET_GCP

This object is required and provides the OSPM with a bit mask of the device capabilities.

9. ACPI-Defined Devices and Device-Specific Objects — ACPI Specification 6.5 documentation

Input Parameters

Input parameters as described in ACPI specification.

Output Parameters

Should return structure as defined by ACPI specification

FFA ACPI

Method (_GCP) {
  // Check to make sure FFA is available and not unloaded
  If(LEqual(\\_SB.FFA0.AVAL,One)) {
    CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
    CreateField(BUFF,128,128,UUID) // UUID of service
    CreateByteField(BUFF,32, CMDD) // In – First byte of command
    CreateDwordField(BUFF,32,GCPD) // Out – 32-bit integer described above
  
    Store(0x1, CMDD) // EC_TAS_GET_GCP
    Store(ToUUID("23ea63ed-b593-46ea-b027-8924df88e92f"), UUID) // RTC
    Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

    If(LEqual(STAT,0x0) ) // Check FF-A successful?
    {
      Return (GCPD)
    }
  }
  Return(Zero)
}

EC_TAS_GET_GRT

This object is required if the capabilities bit 2 is set to 1. The OSPM can use this object to get time. The return value is a buffer containing the time information as described below.

9. ACPI-Defined Devices and Device-Specific Objects — ACPI Specification 6.5 documentation

Input Parameters

Input parameters as described in ACPI specification.

Output Parameters

Should return structure as defined by ACPI specification

FFA ACPI Example

Method (_GRT) {
  Name(RBUF, Buffer(16){})
  // Check to make sure FFA is available and not unloaded
  If(LEqual(\\_SB.FFA0.AVAL,One)) {
    CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
    CreateField(BUFF,128,128,UUID) // UUID of service
    CreateByteField(BUFF,32, CMDD) // In – First byte of command
    CreateField(BUFF,256,128,GRTD) // Out – 16-byte timestamp
    Store(0x2, CMDD) // EC_TAS_GET_GRT
    Store(ToUUID("23ea63ed-b593-46ea-b027-8924df88e92f"), UUID) // RTC
    Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

    If(LEqual(STAT,0x0) ) // Check FF-A successful?
    {
      Store(GRTD, RBUF)
    }
  }
  Return(RBUF)
}

EC_TAS_SET_SRT

This object is required if the capabilities bit 2 is set to 1. The OSPM can use this object to set the time.

9. ACPI-Defined Devices and Device-Specific Objects — ACPI Specification 6.5 documentation

Input Parameters

Input parameters as described in ACPI specification.

Output Parameters

Should return structure as defined by ACPI specification

FFA ACPI Example

Method (_SRT, 1) {
  // Check to make sure FFA is available and not unloaded
  If(LEqual(\\_SB.FFA0.AVAL,One)) {
    CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
    CreateField(BUFF,128,128,UUID) // UUID of service
    CreateByteField(BUFF,32, CMDD) // In – First byte of command
    CreateField(BUFF,264,128,SRTD)  // 16 bytes of data
    CreateDwordField(BUFF,32,SRTS) // Out – Command status

    Store(0x3, CMDD) // EC_TAS_SET_SRT
    Store(ToUUID("23ea63ed-b593-46ea-b027-8924df88e92f"), UUID) // RTC
    Store(Arg0, SRTD) // Copy over the RTC data
    Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

    If(LAnd(LEqual(STAT,0), LEqual(SRTS,0)))
    {
      Return (Zero)
    }
  }
  Return(0xFFFFFFFF)
}

EC_TAS_GET_GWS

This object is required if the capabilities bit 0 is set to 1. It enables the OSPM to read the status of wake alarms

9. ACPI-Defined Devices and Device-Specific Objects — ACPI Specification 6.5 documentation

Input Parameters

Input parameters as described in ACPI specification.

Output Parameters

Should return structure as defined by ACPI specification

FFA ACPI Example

Method (_GWS, 1) {
  // Check to make sure FFA is available and not unloaded
  If(LEqual(\\_SB.FFA0.AVAL,One)) {
    CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
    CreateField(BUFF,128,128,UUID) // UUID of service
    CreateByteField(BUFF,32, CMDD) // In – First byte of command
    CreateDwordField(BUFF,33,GWS1) // In – Dword for timer type AC/DC
    CreateDwordField(BUFF,32,GWSD) // Out – Dword timer state

    Store(20, LENG)
    Store(0x4, CMDD) // EC_TAS_GET_GWS
    Store(Arg0, GWS1)
    Store(ToUUID("23ea63ed-b593-46ea-b027-8924df88e92f"), UUID) // RTC
    Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

    If(LEqual(STAT,0x0) ) // Check FF-A successful?
    {
      Return (GWSD)
    } 
  } 
  Return(Zero)
}

EC_TAS_SET_CWS

This object is required if the capabilities bit 0 is set to 1. It enables the OSPM to clear the status of wake alarms

9. ACPI-Defined Devices and Device-Specific Objects — ACPI Specification 6.5 documentation

Input Parameters

Input parameters as described in ACPI specification.

Output Parameters

Should return structure as defined by ACPI specification

FFA ACPI Example

Method (_CWS, 1) {
  // Check to make sure FFA is available and not unloaded
  If(LEqual(\\_SB.FFA0.AVAL,One)) {
    CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
    CreateField(BUFF,128,128,UUID) // UUID of service
    CreateByteField(BUFF,32, CMDD) // In – First byte of command
    CreateDwordField(BUFF,33, CWS1) // In – Dword for timer type AC/DC
    CreateDwordField(BUFF,32,CWSD) // Out – Command status
 
    Store(20, LENG)
    Store(0x5, CMDD) // EC_TAS_SET_CWS
    Store(Arg0,CWS1)
    Store(ToUUID("23ea63ed-b593-46ea-b027-8924df88e92f"), UUID) // RTC
    Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

    If(LAnd(LEqual(STAT,0), LEqual(CWSD,0)))
    {
      Return (Zero)
    }
  } 
  Return(One)
}

EC_TAS_SET_STV

This object is required if the capabilities bit 0 is set to 1. It sets the timer to the specified value. 

9. ACPI-Defined Devices and Device-Specific Objects — ACPI Specification 6.5 documentation

Input Parameters

Input parameters as described in ACPI specification.

Output Parameters

Should return structure as defined by ACPI specification

FFA ACPI Example

Method (_STV, 2) {
  // Check to make sure FFA is available and not unloaded
  If(LEqual(\\_SB.FFA0.AVAL,One)) {
    CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
    CreateField(BUFF,128,128,UUID) // UUID of service
    CreateByteField(BUFF,32, CMDD) // In – First byte of command
    CreateDwordField(BUFF,33, STV1) // In – Dword for timer type AC/DC
    CreateDwordField(BUFF,37, STV2) // In – Dword Timer Value
    CreateDwordField(BUFF,32,STVD) // Out – Command status

    Store(0x6, CMDD) // EC_TAS_SET_STV
    Store(Arg0,STV1)
    Store(Arg1,STV2)
    Store(ToUUID("23ea63ed-b593-46ea-b027-8924df88e92f"), UUID) // RTC
    Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
  
    If(LAnd(LEqual(STAT,0), LEqual(STVD,0)))
    {
      Return (Zero)
    }
  }
  Return(One)
}

EC_TAS_GET_TIV

This object is required if the capabilities bit 0 is set to 1. It returns the remaining time of the specified timer before that expires.

9. ACPI-Defined Devices and Device-Specific Objects — ACPI Specification 6.5 documentation

Input Parameters

Input parameters as described in ACPI specification.

Output Parameters

Should return structure as defined by ACPI specification

FFA ACPI Example

Method (_TIV, 1) {
  // Check to make sure FFA is available and not unloaded
  If(LEqual(\\_SB.FFA0.AVAL,One)) {
    CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
    CreateField(BUFF,128,128,UUID) // UUID of service
    CreateByteField(BUFF,32, CMDD) // In – First byte of command
    CreateDwordField(BUFF,33, TIV1) // In – Dword for timer type AC/DC
    CreateDwordField(BUFF,32,TIVD) // Out – Dword timer state

    Store(0x7, CMDD) // EC_TAS_GET_TIV
    Store(Arg0,TIV1)
    Store(ToUUID("23ea63ed-b593-46ea-b027-8924df88e92f"), UUID) // RTC
    Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

    If(LEqual(STAT,0x0) ) // Check FF-A successful?
    {
      Return (TIVD)
    }
  }
  Return(Zero)
}

EC Debug Service

The debug service is used for telemetry, debug logs, system reset information etc.

Recovery Mode

Put EC into recovery mode for development flashing and debugging.

Dump Debug State

EC should be able to support typical engineering requests, such as getting detailed subsystem information, setting/getting GPIOs, etc, for design verification and benchtop testing.

Telemetry

Ability to communicate with the HLOS event logging system, and record EC critical events for later analysis.

System Boot State

In many designs, OEMs will desire indication that the system is responding to a power on request. This could be a logo display on the screen or a bezel LED. EC should be able to control these devices during the boot sequence.

During first boot sequence EC may also be initialized and setup its services. Needs to know when OS is up to send notification for events that are only used by OS.

Memory Mapped Transactions

There are two cases where you may want to use the memory mapped transactions. The first is if you have a large buffer you need to transfer data between EC and HLOS like a debug buffer. The second use case is if you want to emulate an eSPI memory mapped interface for compatibility with legacy devices.

For this mode to work you will need memory carved out which is dedicated and shared between HLOS and secure world. In your UEFI memory map this memory should be marked as EfiMemoryReservedType so that the OS will not use or allocate the memory. In your SP manifest file you will also need to add access to this physical memory range. It needs to be aligned on a 4K boundary and a multiple of 4K. This memory region is carved out and must never be used for any other purpose. Since the memory is shared with HLOS there is also no security surrounding accesses to the memory.

Example Memory Mapped Interface

// Map 4K memory region shared
OperationRegion(ABCD, SystemMemory, 0xFFFF0000, 0x1000)

// DSM Method to send sync event
Method(_DSM,4,Serialized,0,UnknownObj, {BuffObj, IntObj,IntObj,PkgObj})
{
  // Compare passed in UUID to Supported UUID
  If(LEqual(Arg0,ToUUID(“6f8398c2-7ca4-11e4-ad36-631042b5008f”)))
  {
    // Use FFA to send Notification event down to copy data to EC
    If(LEqual(\\_SB.FFA0.AVAL,One)) {

      CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32, CMDD) // In – First byte of command

      // Create Doorbell Event to read shared memory
      Store(0x0, CMDD) // 
      Store(ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), UUID) // Debug Service UUID
      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

    } // End AVAL
  } // End UUID
} // End DSM

Any updates from the EC come back through a notification event registered in the FFA for this particular service.

EC Manufacturing Service

This service should contain all the functionality that is need to perform self test, validation of the EC and special manufacturing modes. This service should be disabled on retail devices or at least protected to prevent unwanted modes.

Self Test

EC should perform self test and return results/details of test validation

Set Calibration Data

Have ability to store factory calibrations and setup information into EC non-volatile memory. For instance keyboard language information, or thermistor calibration values.

EC OEM Service

Any OEM special custom features should be put in their own service sandbox to support OEM specific features. This will prevent definitions from colliding with other services.

Sample System Implementation

ACPI Interface Definition

FFA Device Definition

Device(\_SB_.FFA0) {
  Name(_HID, "MSFT000C")
  OperationRegion(AFFH, FFixedHw, 4, 144)
  Field(AFFH, BufferAcc, NoLock, Preserve) { AccessAs(BufferAcc, 0x1), FFAC, 1152 }

  // Other components check this to make sure FFA is available
  Method(AVAL, 0, Serialized) {
    Return(One)
  }

  // Register notification events from FFA
  Method(_RNY, 0, Serialized) {
    Return( Package() {
      Package(0x2) { // Events for Management Service
        ToUUID("330c1273-fde5-4757-9819-5b6539037502"),
        Buffer() {0x1,0x0} // Register event 0x1
      },
      Package(0x2) { // Events for Thermal service
        ToUUID("31f56da7-593c-4d72-a4b3-8fc7171ac073"),
        Buffer() {0x1,0x0,0x2,0x0,0x3,0x0} // Register events 0x1, 0x2, 0x3
      },
      Package(0x2) { // Events for input device
        ToUUID("e3168a99-4a57-4a2b-8c5e-11bcfec73406"),
        Buffer() {0x1,0x0} // Register event 0x1 for LID
      }
    } )
  }

  Method(_NFY, 2, Serialized) {
    // Arg0 == UUID
    // Arg1 == Notify ID
    // Management Service Events

    If(LEqual(ToUUID("330c1273-fde5-4757-9819-5b6539037502"),Arg0)) {
      Switch(Arg1) {
        Case(1) { // Test Notification Event
          Notify(\_SB.ECT0,0x20)
        }
      }
    }

    // Thermal service events
    If(LEqual(ToUUID("31f56da7-593c-4d72-a4b3-8fc7171ac073"),Arg0)) {
      Switch(Arg1) {
        Case(1) { // Temp crossed low threshold
          Notify(\_SB.SKIN,0x80)
        }
        Case(2) { // Temp crossed high threshold
          Notify(\_SB.SKIN,0x81)
        }
        Case(3) { // Critical temperature event
          Notify(\_SB.SKIN,0x82)
        }
      }
    }

    // Input Device Events
    If(LEqual(ToUUID("e3168a99-4a57-4a2b-8c5e-11bcfec73406"),Arg0)) {
      Switch(Arg1) {
        Case(1) { // LID event
          Notify(\_SB._LID,0x80)
        }
      }
    }
  }
}

Memory Mapped Interface via FFA for UCSI

Note for this implementation of memory mapped interface to work the memory must be marked as reserved by UEFI and not used by the OS and direct access also given to the corresponding service in secure world.

Device(USBC) {
  Name(_HID,EISAID(“USBC000”))
  Name(_CID,EISAID(“PNP0CA0”))
  Name(_UID,1)
  Name(_DDN, “USB Type-C”)
  Name(_ADR,0x0)

  Name(BUFF, Buffer(144){}) // Create buffer for FFA data

  OperationRegion(USBC, SystemMemory, UCSI_PHYS_MEM, 0x30)
  Field(USBC,AnyAcc,Lock,Preserve)
  {
    // USB C Mailbox Interface
    VERS,16, // PPM-\>OPM Version
    RES, 16, // Reserved
    CCI, 32, // PPM-\>OPM CCI Indicator
    CTRL,64, // OPM-\>PPM Control Messages
    MSGI,128, // OPM-\>PPM Message In
    MSGO,128, // PPM-\>OPM Message Out
  }

  Method(_DSM,4,Serialized,0,UnknownObj, {BuffObj, IntObj,IntObj,PkgObj})
  {

    // Compare passed in UUID to Supported UUID
    If(LEqual(Arg0,ToUUID(“6f8398c2-7ca4-11e4-ad36-631042b5008f”)))
    {
      // Use FFA to send Notification event down to copy data to EC
      If(LEqual(\_SB.FFA0.AVAL,One)) {
        CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
        CreateField(BUFF,128,128,UUID) // UUID of service
        CreateByteField(BUFF,32, CMDD) // In – First byte of command
        CreateField(BUFF,288,384,FIFD) // Out – Msg data

        // Create Doorbell Event
        Store(0x0, CMDD) // UCSI set doorbell
        Store(ToUUID("65467f50-827f-4e4f-8770-dbf4c3f77f45"), UUID) // EC_SVC_UCSI
        Store(USBC,FIFD)
        Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
      } // End AVAL
    } // End UUID
  } // End DSM
}

Thermal ACPI Interface for FFA

This sample code shows one Microsoft Thermal zone for SKIN and then a thermal device THRM for implementing customized IO.

// Sample Definition of FAN ACPI
Device(SKIN) {
  Name(_HID, "MSFT000A")

  Method(_TMP, 0x0, Serialized) {
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) // Command register
      CreateByteField(BUFF,33,TZID) // Temp Sensor ID
      CreateDWordField(BUFF,32,RTMP) // Output Data

      Store(0x1, CMDD) // EC_THM_GET_TMP
      Store(0x2, TZID) // Temp zone ID for SKIIN
      Store(ToUUID("31f56da7-593c-4d72-a4b3-8fc7171ac073"), UUID)
      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        Return (RTMP)
      }
    }
    Return (Ones)
  }

  // Arg0 Temp sensor ID
  // Arg1 Package with Low and High set points
  Method(THRS,0x2, Serialized) {
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) // Command register
      CreateByteField(BUFF,33,TZID) // Temp Sensor ID
      CreateDwordField(BUFF,34,VTIM) // Timeout
      CreateDwordField(BUFF,38,VLO) // Low Threshold
      CreateDwordField(BUFF,42,VHI) // High Threshold
      CreateDWordField(BUFF,46,TSTS) // Output Data

      Store(ToUUID("31f56da7-593c-4d72-a4b3-8fc7171ac073"), UUID)
      Store(0x2, CMDD) // EC_THM_SET_THRS
      Store(Arg0, TZID)
      Store(DeRefOf(Index(Arg1,0)),VTIM)
      Store(DeRefOf(Index(Arg1,1)),VLO)
      Store(DeRefOf(Index(Arg1,2)),VHI)
      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        Return (TSTS)
      }
    }
    Return (0x3) // Hardware failure
  }

  // Arg0 GUID 1f0849fc-a845-4fcf-865c-4101bf8e8d79
  // Arg1 Revision
  // Arg2 Function Index
  // Arg3 Function dependent
  Method(_DSM, 0x4, Serialized) {
    If(LEqual(ToUuid("1f0849fc-a845-4fcf-865c-4101bf8e8d79"),Arg0)) {
      Switch(Arg2) {
        Case (0) {
          Return(0x3) // Support Function 0 and Function 1
        }
        Case (1) {
          Return( THRS(0x2, Arg3) ) // Call to function to set threshold
        }
      }
    }
    Return(0x3)
  }
}

Device(THRM) {
  Name(_HID, "MSFT000B")

  // Arg0 Instance ID
  // Arg1 UUID of variable
  // Return (Status,Value)
  Method(GVAR,2,Serialized) {
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) // Command register
      CreateByteField(BUFF,33,INST) // Instance ID
      CreateWordField(BUFF,34,VLEN) // 16-bit variable length
      CreateField(BUFF,288,128,VUID) // UUID of variable to read
      CreateQwordField(BUFF,52,64,RVAL) // Output Data

      Store(ToUUID("31f56da7-593c-4d72-a4b3-8fc7171ac073"), UUID)
      Store(0x5, CMDD) // EC_THM_GET_VAR
      Store(Arg0,INST) // Save instance ID
      Store(4,VLEN) // Variable is always DWORD here
      Store(Arg1, VUID)
      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        Return (RVAL)
      }
    }
    Return (0x3)
  }

  // Arg0 Instance ID
  // Arg1 UUID of variable
  // Return (Status,Value)
  Method(SVAR,3,Serialized) {
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateQwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) // Command register
      CreateByteField(BUFF,33,INST) // Instance ID
      CreateWordField(BUFF,34,VLEN) // 16-bit variable length
      CreateField(BUFF,288,128,VUID) // UUID of variable to Write
      CreateQwordField(BUFF,52,64,RVAL) // Output Data
      CreateDwordField(BUFF,60,DVAL) // Data value

      Store(ToUUID("31f56da7-593c-4d72-a4b3-8fc7171ac073"), UUID)
      Store(0x6, CMDD) // EC_THM_SET_VAR
      Store(Arg0,INST) // Save instance ID
      Store(4,VLEN) // Variable is always DWORD here
      Store(Arg1, VUID)
      Store(Arg2,DVAL)
      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)

      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
      Return (RVAL)
      }
    }
    Return (0x3)
  }

  // Arg0 GUID
  // 07ff6382-e29a-47c9-ac87-e79dad71dd82 - Input
  // d9b9b7f3-2a3e-4064-8841-cb13d317669e - Output
  // Arg1 Revision
  // Arg2 Function Index
  // Arg3 Function dependent
  Method(_DSM, 0x4, Serialized) {
    // Input Variable
    If(LEqual(ToUuid("07ff6382-e29a-47c9-ac87-e79dad71dd82"),Arg0)) {
      Switch(Arg2) {
        Case(0) {
          // We support function 0-3
          Return(0xf)
        }
        Case(1) {
          Return(GVAR(1,ToUuid("ba17b567-c368-48d5-bc6f-a312a41583c1"))) // OnTemp
        }
        Case(2) {
          Return(GVAR(1,ToUuid("3a62688c-d95b-4d2d-bacc-90d7a5816bcd"))) // RampTemp
        }
        Case(3) {
          Return(GVAR(1,ToUuid("dcb758b1-f0fd-4ec7-b2c0-ef1e2a547b76"))) // MaxTemp
        }
      }
      Return(0x1)
    }

    // Output Variable
    If(LEqual(ToUuid("d9b9b7f3-2a3e-4064-8841-cb13d317669e"),Arg0)) {
      Switch(Arg2) {
        Case(0) {
          // We support function 0-3
          Return(0xf)
        }
        Case(1) {
          Return(SVAR(1,ToUuid("ba17b567-c368-48d5-bc6f-a312a41583c1"),Arg3)) // OnTemp
        }
        Case(2) {
          Return(SVAR(1,ToUuid("3a62688c-d95b-4d2d-bacc-90d7a5816bcd"),Arg3)) // RampTemp
        }
        Case(3) {
          Return(SVAR(1,ToUuid("dcb758b1-f0fd-4ec7-b2c0-ef1e2a547b76"),Arg3)) // MaxTemp
        }
      }
    }
    Return (0x1)
  }
}

Call Flows for secure and non-secure Implementation

Depending on system requirements the ACPI calls may go directly to the EC or through secure world then through to EC.

When using non-secure interface the ACPI functions must define protocol level which is the Embedded controller for eSPI. For I2C/I3C or SPI interfaces the corresponding ACPI device must define the bus dependency and build the packet directly that is sent to the EC.

For secure communication all data is sent to the secure world via FF-A commands described in this document and the actual bus protocol and data sent to the EC is defined in the secure world in Hafnium. All support for FF-A is inboxed in the OS by default so EC communication will always work in any environment. However, FF-A is not supported in x86/x64 platforms so direct EC communication must be used on these platforms.

Non-Secure eSPI Access

This call flow assumes using Embedded controller definition with independent ACPI functions for MPTF support

Non-Secure eSPI READ

Device(EC0) {
  Name(_HID, EISAID("PNP0C09")) // ID for this EC

  // current resource description for this EC
  Name(_CRS, ResourceTemplate() {
    Memory32Fixed (ReadWrite, 0x100000, 0x10) // Used for simulated port access
    Memory32Fixed (ReadWrite, 0x100010, 0x10)
    // Interrupt defined for eSPI event signalling
    GpioInt(Edge, ActiveHigh, ExclusiveAndWake,PullUp 0,"\_SB.GPI2"){43} 
  })

  Name(_GPE, 0) // GPE index for this EC

  // create EC's region and field for thermal support
  OperationRegion(EC0, EmbeddedControl, 0, 0xFF)
  Field(EC0, ByteAcc, Lock, Preserve) {
    MODE, 1, // thermal policy (quiet/perform)
    FAN, 1, // fan power (on/off)
    , 6, // reserved
    TMP, 16, // current temp
    AC0, 16, // active cooling temp (fan high)
    , 16, // reserved
    PSV, 16, // passive cooling temp
    HOT 16, // critical S4 temp
    CRT, 16 // critical temp
    BST1, 32, // Battery State
    BST2, 32, // Battery Present Rate
    BST3, 32, // Battery Remaining capacity
    BST4, 32, // Battery Present Voltage
  }

  Method (_BST) {
    Name (BSTD, Package (0x4)
    {
      \_SB.PCI0.ISA0.EC0.BST1, // Battery State
      \_SB.PCI0.ISA0.EC0.BST2, // Battery Present Rate
      \_SB.PCI0.ISA0.EC0.BST3, // Battery Remaining Capacity
      \_SB.PCI0.ISA0.EC0.BST4, // Battery Present Voltage
    })
    Return(BSTD)
  }
}

Non-Secure eSPI Notifications

All interrupts are handled by the ACPI driver. When EC needs to send a notification event the GPIO is asserted and traps into IRQ. ACPI driver reads the EC_SC status register to determine if an SCI is pending. DPC callback calls and reads the EC_DATA port to determine the _Qxx event that is pending. Based on the event that is determined by ACPI the corresponding _Qxx event function is called.

Method (_Q07) {
  // Take action for event 7
  Notify(\_SB._LID, 0x80)
}

Secure eSPI Access

The following flow assumes ARM platform using FF-A for secure calls. Note if you want to use the same EC firmware on both platforms with secure and non-secure access the EC_BAT_GET_BST in this case should be convert to a peripheral access with the same IO port and offset as non-secure definition.

Secure eSPI READ

  Method (_BST, 0, Serialized) {
    // Check to make sure FFA is available and not unloaded
    If(LEqual(\_SB.FFA0.AVAL,One)) {
      CreateDwordField(BUFF,0,STAT) // Out – Status for req/rsp
      CreateField(BUFF,128,128,UUID) // UUID of service
      CreateByteField(BUFF,32,CMDD) //  In – First byte of command
      CreateDwordField(BUFF,32,BST0)  // Out – Battery State DWord
      CreateDwordField(BUFF,36,BST1)  // Out – Battery Rate DWord
      CreateDwordField(BUFF,40,BST2)  // Out – Battery Reamining Capacity DWord
      CreateDwordField(BUFF,44,BST3)  // Out – Battery Voltage DWord

      Store(0x2, CMDD) //EC_BAT_GET_BST
      Store(ToUUID("25cb5207-ac36-427d-aaef-3aa78877d27e"), UUID)

      Store(Store(BUFF, \_SB_.FFA0.FFAC), BUFF)
      If(LEqual(STAT,0x0) ) // Check FF-A successful?
      {
        return(Package() {BST0, BST1, BST2, BST3} )
      }
    }
    Return(Package() {0,0,0,0})
  }

Secure eSPI Notification

When EC communication is done through Secure world we assert FIQ which is handled as eSPI interrupt. eSPI driver reads EC_SC and EC_DATA to retrieve the notification event details. On Non-secure implementation ACPI converts this to Qxx callback. On secure platform this is converted to a virtual ID and sent back to the OS via _NFY callback and a virtual ID.

  Method(_DSM, 0x4, NotSerialized)
  {
    // Arg0 - UUID
    // Arg1 - Revision
    // Arg2: Function Index
    //         0 - Query
    //         1 - Notify
    //         2 - binding failure
    //         3 - infra failure    
    // Arg3 - Data
  
    //
    // Device specific method used to query
    // configuration data. See ACPI 5.0 specification
    // for further details.
    //
    If(LEqual(Arg0, Buffer(0x10) {
        //
        // UUID: {7681541E-8827-4239-8D9D-36BE7FE12542}
        //
        0x1e, 0x54, 0x81, 0x76, 0x27, 0x88, 0x39, 0x42, 0x8d, 0x9d, 0x36, 0xbe, 0x7f, 0xe1, 0x25, 0x42
      }))
    {
      // Query Function
      If(LEqual(Arg2, Zero)) 
      {
        Return(Buffer(One) { 0x03 }) // Bitmask Query + Notify
      }
      
      // Notify Function
      If(LEqual(Arg2, One))
      {
        // Arg3 - Package {UUID, Cookie}
        Store(DeRefOf(Index(Arg3,1)), \_SB.ECT0.NEVT )
        If(LEqual(0x2,\_SB.ECT0.NEVT)) {
          Notify(\_SB._LID, 0x80)
        }
      }
    }
    Return(Buffer(One) { 0x00 })
  }

HID EC Interface

This section is a collection of the HID definitions for each HID class driver in the OS that is loaded on a Top Level Collection.

Today HIDClass, HIDI2C and HIDI3C are all inboxed drivers in the OS and support input devices. Here we outline the specs for the remaining missing pieces for all the HID class drivers and HIDeSPI.

HID Transport Drivers

HID supports I3C inbox in the OS without requiring any vendor or OEM drivers to be installed if the HID device is HCI compliant. For I2C and SPI drivers the stack is also present, but requires a Silicon Vendor driver for the underlying I2C interface as I2C controllers are not fully standardized.

For eSPI we first need to standardize and have a more efficient eSPI interface. As part of that we are working on updating the EC specification based on PCC interface defined in ACPI. You can find the current draft spec here:

ESPI PCC Specification

HID Class Drivers

For each top level collection there is a corresponding HID Class driver that gets loaded for that TLC. The HID Class driver implements the miniport driver interface from the OS based on the HID specification for that class.

Below are links to HUTRR documents for HID Class drivers as they are created as draft specifications to cover all the EC services:

HID Time and Alarm

Summary:

Add new Usages to support configuring a system’s onboard RTC (Real-Time-Clock) and System Wake Timer.

Scenario:

CPU timers tend to drift and lack time-keeping capability during sleep/shutdown. An RTC (Real-Time-Clock, distinct from the CPU-Clock) is a dedicated, battery-powered device (typically integrated into motherboards/chipsets) to maintain a ‘wall-clock’ time+date with high precision. Commonly used to provide current time+date to the user, data timestamping, task-scheduling and a basis for authentication.

The pairing with a battery/super-capacitor, ensures accurate time+date is kept while the CPU is not running. While this device is very-accurate and typically factory-initialized, it still requires regular updates from the system to account for drift (e.g. using NTP), timezone changes and addition of leap-seconds. The system typically retrieves the current time from the device on returning to its working-state.

The system wake-timer device provides a CPU-external mechanism to signal a ‘wake’. It is commonly implemented as an ‘always on’ low-power hardware block in the chipset/SoC or directly into the platform’s power-management/wake controller.

When configured, the device will wake the system at the specified future time after the timer expires. This enables the system to save power by sleeping during idle periods and then resume to perform scheduled tasks.

Proposal:

Add to Table 9.1 – Generic Device Controls Page

Usage IdUsage NameUsage Types
0x46-4FReserved
0x50Vendor Current StateDV
0x51Current StateNAry
0x52ClearedSel
0x53FailedSel
0x54RunningSel
0x55ExpiredSel
0x56SignaledSel

Add a new section to Generic Device Controls with the following details.

Reviewer Note: These Usages are reminiscent of ‘23.3 Sensor State Event Usages’. New Usages have been declared since Sensor Events are expected to be used with Modifiers. Modifiers are not applicable to non-sensor scenarios, so new, dedicated Usages shall be created.

9.5 Device State

Some devices can fail requested operations (though the Report is successfully sent/received) because of underlying hardware faults, loss of backing resources, invalid configuration, or other device-specific conditions that are not visible through transport-level success alone. These usages allow the system to determine whether a requested operation succeeded, failed, or requires recovery.

Usage Name Usage Types Usage Description
Current State NAry The most-recent state
Cleared Sel State is uninitialized or recently reset/cleared.
Failed Sel

Failed to perform the most recent operation. (Note: HID-transport functionality is still clearly working)

Common for when the device is actually backed by another device that can experience failures

Expired Sel Expired (e.g. timer has completed)
Signaled Sel Signaled (e.g. external interrupt raised)
Running Sel Running (e.g. everything behaving normally)
Usage Name Usage Types Usage Description
Vendor Current State DV

Opaque state-value meaningful only to the device vendor.

Typically used in conjunction with ‘Current State’

Create new page ‘Time and Date’ (0x13).

Reviewer Note: These Usages are reminiscent of ‘23.15 Time Sensor Field Usages’. New Usages have been declared since Sensor Data Fields are expected to be used with Modifiers. Modifiers are not applicable to non-sensor scenarios, so new, dedicated Usages shall be created.

22 - Time and Date (0x13)

Common Usages to describe times and dates.

Usage IdUsage NameUsage Types
0x00Undefined
0x01YearDV
0x02MonthDV
0x03DayDV
0x04HourDV
0x05MinuteDV
0x06SecondDV
0x07MillisecondDV
0x08-FReserved
0x10Time Zone Offset From UTCDV
0x11Daylight Savings Time ObservedDF
0x12Daylight Savings Time ActiveDF

22.1 Date

Conventional UTC (proleptic Gregorian) calendar divisions.

Usage Name Usage Types Usage Description
Year DV

Standard UTC Gregorian calendar year.

Positive integers for AD and negative for BC.

0 is undefined in the Gregorian calendar.

Month DV Standard UTC Gregorian calendar month
Day DV Standard UTC Gregorian calendar day of the month

22.2 Time

22.2.1 Conventional UTC time (Coordinated Universal Time) divisions.

Usage NameUsage TypesUsage Description
HourDVStandard UTC hour
MinuteDVStandard UTC minute
SecondDVStandard UTC second
MillisecondDVStandard UTC millisecond

22.2.2 Time zone and Daylight Savings Time (DST).

Usage Name Usage Types Usage Description
Time Zone Offset From UTC DV

Minutes local time is behind (-) or ahead (+) of UTC+00:00

Offset = (localTime – UTC)
e.g. AEST timezone of UTC+10:00 is ahead +600 minutes.

e.g. EST timezone of UTC-05:00 is behind -300 minutes.

Daylight Savings Time Observed DF Indicates if DST is observed in current time zone
Daylight Savings Time Active DF

Indicates if time has been adjusted by system to apply DST.

Useful when the device is shared to coordinate applying/removing DST.

Add to Table 4.1 – Generic Desktop Page

Usage IdUsage NameUsage Types
0x14System Wake TimerCA
0x15System Real Time ClockCA
0xF0Timer Expiration: External PowerDV
0xF1Timer Expiration: Internal PowerDV
0xF2Power Source Change Minimum ExpirationDV
0xF3Lowest System Wakeable Power StateNAry
0xF4S1Sel
0xF5S2Sel
0xF6S3Sel
0xF7S4Sel
0xF8S5Sel
0xF9-FFFFReserved

Add 4.17 System Wake Timer

The system wake-timer device provides a CPU-external mechanism to signal a ‘wake’. It is commonly implemented as an ‘always on’ low-power hardware block in the chipset/SoC or directly into the platform’s power-management/wake controller.

When configured, the device will wake the system (via the above out-of-band means) at the specified future time after the timer expires. This enables the system to save power by sleeping during idle periods and then resume to perform scheduled tasks.

Separate wake configurations may be made for whether the system is running on External (i.e. AC/wall) power or Internal (i.e. DC/battery) power. It is up to the device to determine the power source and choose the right configuration, switching as necessary.

There is typically a single system wake timer available to the system.

Usage Name Usage Types Usage Description
System Wake Timer CA A configurable timer, capable of waking the system when in a low-power state.
Timer Expiration: External Power DV Timer expiration when device is connected to external (i.e. AC/wall) power.
Timer Expiration: Internal Power DV Timer expiration when device is connected to internal (i.e. DC/battery) power.
Power Source Change Minimum Expiration DV

Additional time the wake-timer must wait for, when the power-source changes (e.g. from external to internal) while the system is in Sx and the timer has already expired.

(e.g. System enters Sx while on external-power with internal-power timer expired. While in Sx, power-source changes to internal, must wait for additional period before signaling wake.)

4.17.1 Lowest System Wakeable Power State

By ACPI convention, S0 is the working-state, and S1-5 (i.e. Sx) is a low-power state, (where the larger the digit, the lower the power).

Usage Name Usage Types Usage Description
Lowest System Wakeable Power State NAry Indicates the lowest power state the system can enter and still be signaled awake.
S1 Sel Standard ACPI S1 sleep state
S2 Sel Standard ACPI S2 sleep state
S3 Sel Standard ACPI S3 sleep state
S4 Sel

Standard ACPI S4 (Hibernate) sleep state.

System context saved, hardware mostly off

S5 Sel

Standard ACPI S5 (soft off) sleep state.

System context lost, requires full reboot.

4.17.2 Timer Expiration Rules

  1. Upon configuration, the wake timer immediately begins counting-down (i.e. while in S0).

  2. A wake shall be signalled, ONLY when the system is in Sx (i.e. a non-working state). The lowest such state is defined by ‘Lowest System Wakeable Power State’

  3. If the system is in Sx, and the wake timer expires, the system is signalled to wake.

  4. If the system is in S0, and the wake timer expires, no wake is signalled.

  5. If the system enters Sx with an expired wake timer, the system is immediately signalled to wake.

  6. Once expired, the wake timer remains expired until it is cleared or re-configured. (i.e. entering S0 or signalling wake does NOT clear the timer)

  7. If the system changes power-source while in Sx, and the associated timer has already expired, ‘Power Source Change Minimum Expiration’ must additionally be waited before signalling to wake.

Add 4.18 System Real Time Clock (RTC)

CPU timers tend to drift and lack time-keeping capability during sleep/shutdown. An RTC (Real-Time-Clock, distinct from the CPU-Clock) is a dedicated, battery-powered device (typically integrated into motherboards/chipsets) to maintain a ‘wall-clock’ time+date with high precision. Commonly used to provide current time+date to the user, data timestamping, task-scheduling and a basis for authentication.

The pairing with a battery/super-capacitor, ensures accurate time+date is kept while the CPU is not running. While this device is typically factory-initialized, it requires regular system updates to account for drift (e.g. using NTP), timezone changes and addition of leap-seconds. The system typically retrieves the current time from the device on returning to its working-state.

There is typically a single RTC available to the system.

Usage NameUsage TypesUsage Description
System Real Time ClockCAThe system accessible RTC.

Sample Descriptor .wara:

The below sample describes a System Wake Timer and a System Real Time Clock.

System Wake Timer Reports:-

Report Type Report Id Description
Feature 1 Describes the lowest system power state this wake timer can wake the system.
Output 1

Wait timer configuration.

Setting a null-value clears the configuration, stopping any active countdown.

Must request corresponding Input report to validate configuration was applied successfully, as underlying hardware may fail

Input 1

Current wake-timer configuration.

Must be requested explicitly by system. Will NOT be sent as Interrupt.

Examine ‘Current State’ to determine if configuration was successfully applied.

  • Cleared (wake-timer is NOT running)

  • Failed (could NOT apply requested configuration, timer is NOT running)

  • Running (successfully applied configuration, timer is counting-down)

  • Expired (wake-timer has expired but NOT signaled a wake)

  • Signaled (expired wake has been signaled)

System Real Time Clock Reports:-

Output 3

Sets the RTC.

This device supports a resolution of 1 millisecond.

Must request corresponding Input report to validate configuration was applied successfully, as underlying hardware may fail.

Input 3

Retrieves the current RTC time.

Examine ‘Current State’ to determine if previously set time was successfully applied.

  • Failed (could NOT apply requested time, current time is invalid)

  • Running (successfully applied time)

Summary

Add a Control Method Battery application usage to the Battery System Usage Page (0x85). The new top-level collection (TLC) exposes one independently managed battery using a transport-neutral HID protocol modeled on the ACPI 6.5 Control Method Battery interface.

The TLC reuses existing Power Page (0x84) and Battery System Page (0x85) usages where their semantics match. New usages are introduced for ACPI fields and operations that have no equivalent HID usage, including extended static information, portable UTF-8 identity strings, measurement configuration, maintenance controls, charge/runtime estimates, peak-power capabilities, thresholds, thermal throttling, capability negotiation, notifications, and a stable battery tag.

This draft intentionally defines the complete ACPI 6.5 battery surface as part of the HIDBattery contract rather than defining only the subset currently consumed by one operating system.

Background

ACPI Control Method Batteries provide operating systems with a common model for static battery information, dynamic status, capacity-change notification, and optional battery management functions. That model is independent of the physical fuel-gauge or charger implementation, but ACPI normally exposes it through AML control methods.

HID already defines Power (0x84) and Battery System (0x85) pages. Those pages contain useful electrical and Smart Battery usages, but they do not define an application collection whose report contract is equivalent to an ACPI Control Method Battery. Implementations consequently need vendor-defined collections and host-specific knowledge even when the underlying information is standard.

Windows battery miniclass drivers are expected to provide battery identity, static information, current status, status-change notifications, and selected controls to the battery class driver. A HID miniclass can implement those callbacks directly from this TLC without requiring vendor-specific report formats.

Goals

  1. Define one standard battery TLC that is independent of USB, HID-over-I2C, HID-over-SPI, and other HID transports.
  2. Preserve the meaning, units, special values, and event behavior of the ACPI 6.5 Control Method Battery interface.
  3. Reuse existing HID usages when their meaning is equivalent.
  4. Support a persistent controller with a removable or replaceable battery pack.
  5. Support multiple batteries by exposing one TLC for each independently managed battery.
  6. Carry battery identity strings in reports rather than relying on transport-specific string descriptor indexes.
  7. Provide the information and controls needed by the Windows battery miniclass interface without making Windows structures the wire format.

Non-goals

  1. This proposal does not define charger, AC adapter, UPS, or system aggregate battery TLCs.
  2. This proposal does not require report IDs to have fixed numeric values. Hosts discover reports and fields by usages.
  3. This proposal does not expose AML, ACPI namespace paths, General Purpose Events, or System Control Interrupts on the wire.
  4. This proposal does not replace the existing Smart Battery collections.

Proposal

Add the Control Method Battery application usage and supporting usages to the Battery System Page (0x85). Change the final reserved range from 0xF4-0xFFFF to:

0x00F4-0x00FF  Reserved
0x0100-0x0186  Defined by this proposal
0x0187-0xFFFF  Reserved

For this TLC only, add Dynamic Flag (DF) as an allowed alternate usage type for the existing Charging (0x44) and Discharging (0x45) usages. Their existing Selector (Sel) type and existing behavior are unchanged.

Control Method Battery application usage

Usage ID Usage Name Type


0x0100 Control Method Battery CA

Control Method Battery (0x0100)

Defines an Application Collection for one independently managed battery. A device containing multiple independently managed batteries shall expose one Control Method Battery TLC per battery. A persistent controller may keep the TLC enumerated while Battery Present is zero. Battery Tag shall change when the pack represented by the TLC is removed, replaced, or materially changes identity.

All reports defined by this proposal are required. A field may contain an unknown value only when this proposal or the referenced ACPI definition explicitly defines an unknown value.

Collection usages

Usage ID Usage Name Type


0x0101 Control Method Battery Information CL 0x0102 Control Method Battery Identity CL 0x0103 Control Method Battery Status CL 0x0104 Control Method Battery Notification CL 0x0105 Battery Measurement Configuration CL 0x0106 Battery Maintenance CL 0x0107 Battery Charge Time CL 0x0108 Battery Runtime CL 0x0109 Battery Power Characteristics CL 0x010A Battery Power State CL 0x010B Battery Power Threshold CL 0x010C Battery Capacity Trip Point CL 0x010D Battery Thermal Control CL 0x010E Battery Capability Negotiation CL 0x010F Power Consumer List CL

Each collection usage groups the fields for the correspondingly named report. The grouping does not change the independent meaning of any data usage.

Protocol and static-information usages

Usage ID Usage Name Type Description


0x0120 Protocol Revision SV Revision of this HIDBattery report contract. This draft defines revision 1. 0x0121 Battery Tag DV Nonzero identity value for the currently installed pack. It changes after removal, replacement, or identity change. Zero means that no battery is present. 0x0122 Battery Information Revision SV Revision of the static information layout. ACPI _BIX revision 1 is encoded as 1. 0x0123 Battery Power Unit CL Selects the unit for all capacity, rate, and peak-power fields in this TLC. 0x0124 Capacity in Milliwatt Hours Sel Capacity is in mWh and rate/power is in mW. 0x0125 Capacity in Milliampere Hours Sel Capacity is in mAh and rate/current is in mA. 0x0126 Battery Technology CL Selects primary or secondary battery technology. 0x0127 Primary Battery Technology Sel Non-rechargeable battery. 0x0128 Secondary Battery Technology Sel Rechargeable battery. 0x0129 Design Capacity Low SV OEM low-capacity threshold in the selected capacity unit. 0x012A Measurement Accuracy SV Measurement accuracy in thousandths of one percent; 80000 represents 80 percent. 0x012B Maximum Sampling Time SV Maximum supported sampling interval in milliseconds. All ones means unavailable. 0x012C Minimum Sampling Time SV Minimum supported sampling interval in milliseconds. All ones means unavailable. 0x012D Maximum Averaging Interval SV Maximum supported averaging interval in milliseconds. 0x012E Minimum Averaging Interval SV Minimum supported averaging interval in milliseconds. 0x012F Battery Swapping Capability CL Selects the replacement behavior of the battery. 0x0130 Battery Not Swappable Sel Battery is not user replaceable. 0x0131 Battery Cold Swappable Sel System must be shut down before replacement while on battery power. 0x0132 Battery Hot Swappable Sel Battery may be replaced without shutting down the system.

The following existing usages are reused in Control Method Battery Information:

Page ID Usage Name Meaning in this TLC


0x85 0x83 Design Capacity ACPI _BIX Design Capacity. 0x85 0x67 Full Charge Capacity ACPI _BIX Last Full Charge Capacity. 0x84 0x40 Config Voltage ACPI _BIX Design Voltage, in mV. 0x85 0x8C Warning Capacity Limit ACPI _BIX Design Capacity of Warning. 0x85 0x6B Cycle Count ACPI _BIX Cycle Count. 0x85 0x8D Capacity Granularity 1 ACPI _BIX Battery Capacity Granularity 1. 0x85 0x8E Capacity Granularity 2 ACPI _BIX Battery Capacity Granularity 2. 0x85 0x85 Manufacture Date Packed Smart Battery manufacture date.

Design Capacity, Full Charge Capacity, and Config Voltage use all ones to mean unknown. Cycle Count uses all ones to mean unknown. Capacity values use the selected Battery Power Unit. Voltage is in millivolts.

Identity usages and string encoding

Usage ID Usage Name Type


0x0133 Battery Model Number BufferedBytes 0x0134 Battery Serial Number String BufferedBytes 0x0135 Battery Type String BufferedBytes 0x0136 Battery OEM Information BufferedBytes 0x0137 Battery Manufacturer Name BufferedBytes 0x0138 Battery Unique Identifier BufferedBytes 0x0139 Power Consumer Identifiers BufferedBytes

Each identity field is a fixed-size byte buffer declared by the report descriptor. It contains a UTF-8 string terminated by 0x00. The first 0x00 and all remaining bytes shall be zero. A value that does not fit shall be truncated at a UTF-8 code-point boundary and terminated. An unsupported or empty value is encoded as an all-zero buffer.

Battery Model Number, Battery Serial Number String, Battery Type String, and Battery OEM Information correspond to the ACPI _BIX strings. Battery Manufacturer Name and Battery Unique Identifier provide the additional identity needed by battery class APIs. Battery Unique Identifier shall be stable for the life of a physical pack and unique among packs that can be attached to the system.

Power Consumer Identifiers is a sequence of NUL-terminated UTF-8 stable device identifiers followed by an additional NUL. It represents the relationships reported by ACPI _PCL. An empty list contains two zero bytes. Each identifier shall be either an ACPI namespace path beginning with “acpi:” or an RFC 8141 UUID URN beginning with “urn:uuid:”. A platform that uses UUID URNs shall expose the same UUID as a device-enumeration property on the referenced consumer so the host can resolve the relationship.

The complete encoded list, including the final extra NUL, shall fit in the declared report field. It shall not be silently truncated. A conforming device therefore selects a field size large enough for its complete consumer list.

Dynamic-status and event usages

Usage ID Usage Name Type Description


0x0140 Battery Critical DF Battery is in the critical energy state. 0x0141 Battery Charge Limiting Active DF Battery charge limiting is active. 0x0142 Battery Present Rate DV Power accepted or supplied at the battery terminals, in mW or mA according to Battery Power Unit. Direction is given by Charging and Discharging. All ones means unknown. 0x0143 Battery Status Sequence DV Monotonically increasing status generation. Wrap from 0xFFFFFFFF to zero is permitted. 0x0144 Battery Information Changed DF Static information or identity changed and shall be reread. 0x0145 Battery Maintenance Changed DF Maintenance status changed and shall be reread. 0x0146 Battery Power State Changed DF Peak-power capability changed and shall be reread. 0x0147 Battery Power Threshold Crossed DF A configured peak-power threshold was crossed. 0x0186 System On Line DF The system is drawing operating power from an external source.

The following existing usages are reused in Control Method Battery Status:

Page ID Usage Name Meaning in this TLC


0x85 0x44 Charging ACPI _BST charging state; used as a DF. 0x85 0x45 Discharging ACPI _BST discharging state; used as a DF. 0x85 0xD0 AC Present External power is physically connected. 0x85 0xD1 Battery Present A battery pack is installed in this TLC. 0x85 0x66 Remaining Capacity ACPI _BST Battery Remaining Capacity. 0x84 0x30 Voltage ACPI _BST Battery Present Voltage, in mV. 0x84 0x36 Temperature Battery temperature, in 0.1 kelvin.

Charging and Discharging are mutually exclusive. A rechargeable battery shall report a valid Present Rate, Remaining Capacity, and Voltage. A primary battery may report all ones where ACPI permits an unknown value.

AC Present reports physical source presence. System On Line reports the effective source supplying the system. System On Line shall be zero while the battery is intentionally discharging with AC present, including Discharge on AC Enabled operation. Hosts shall use System On Line, not AC Present, for operating-system “on line power” state.

The device shall send the status Input report when any status value changes, when Battery Tag changes, or when one of the event flags is asserted. Event flags describe the reason for the current report and are cleared after the report is successfully delivered. Battery Status Sequence increments before each changed report is made available. The host shall tolerate lost or coalesced Input reports and may read all Feature reports at any time.

Battery Information Changed corresponds to ACPI Notify 0x81. Battery Maintenance Changed corresponds to Notify 0x82. Battery Power State Changed and Battery Power Threshold Crossed correspond to Notify 0x83. A remaining-capacity trip-point crossing causes a status report and corresponds to Notify 0x80.

Windows notification support usages

Usage ID Usage Name Type Description


0x0182 Notification Power State Mask DV Power-state changes that trigger a status Input report. 0x0183 Notification Low Capacity DV Inclusive lower capacity bound in the selected capacity unit. 0x0184 Notification High Capacity DV Inclusive upper capacity bound in the selected capacity unit. 0x0185 Critical Bias DV Capacity reserved for the critical transition, in the selected capacity unit.

The host writes the Control Method Battery Notification Feature report to arm notifications. The device shall generate a status Input report when the power state differs from the requested mask or Remaining Capacity leaves the inclusive low/high band. Writing a power-state mask of zero and both capacity bounds as zero disables this notification condition. ACPI Battery Capacity Trip Point remains independently active.

Measurement configuration usages

Usage ID Usage Name Type Description


0x0148 Sampling Time DV Time in milliseconds between measurements used for status values. 0x0149 Averaging Interval DV Time in milliseconds over which status measurements are averaged. 0x014A Operation Status DV Result of the most recent operation in the containing report.

Sampling Time maps to ACPI _BMS and shall be within Minimum Sampling Time and Maximum Sampling Time. Averaging Interval maps to ACPI _BMA and shall be within Minimum Averaging Interval and Maximum Averaging Interval. Values are 1 through 0xFFFFFFFF milliseconds.

The host sets both values in one Set Feature operation. The device applies the values atomically. Operation Status is read-only:

0  Success
1  One or both values are outside the supported range
2-0xFFFFFFFF  Reserved

The host may Get Feature after Set Feature to verify the applied values and Operation Status.

Maintenance usages

Usage ID Usage Name Type


0x0150 Calibration Active DF 0x0151 Charging Disabled DF 0x0152 Discharge on AC Enabled DF 0x0153 Recalibration Requested DF 0x0154 Standby Recommended for Calibration DF 0x0155 Charge Limit Suspension Blocked by Thermal Condition DF 0x0156 Charge Limit Suspension Blocked by Battery Protection DF 0x0157 Calibration Supported SF 0x0158 Charging Control Supported SF 0x0159 Discharge on AC Supported SF 0x015A Individual Battery Control Supported SF 0x015B Charge Before Calibration Required SF 0x015C Charge Limit Suspension Supported SF 0x015D Recalibrate Count SV 0x015E Quick Recalibrate Time DV 0x015F Slow Recalibrate Time DV 0x0160 Start Calibration OOC 0x0161 Disable Charging OOC 0x0162 Enable Discharge on AC OOC 0x0163 Suspend Charge Limiting OOC

The state, capability, count, and time usages map in order to ACPI _BMD. Start Calibration, Disable Charging, Enable Discharge on AC, and Suspend Charge Limiting map to ACPI _BMC argument bits 0 through 3.

Start Calibration is mutually exclusive with the other three controls and takes precedence if set. Suspend Charge Limiting shall be set only when Charge Limit Suspension Supported is one. A device that cannot apply a control shall leave the corresponding state unchanged and assert Battery Maintenance Changed.

If Individual Battery Control Supported is zero, writing any maintenance control in one TLC applies to every affected battery in the system. The device shall update the maintenance state and send Battery Maintenance Changed status reports for every affected TLC. Hosts shall not assume that an operation is local to the TLC that received the write.

Recalibrate Count is zero when recalibration is requested only by Recalibration Requested. Quick and Slow Recalibrate Time are in seconds:

0           Meaning defined by the corresponding ACPI _BMD field
1-0xFFFFFFFE Estimated or remaining time
0xFFFFFFFF  Unknown

Charge-time and runtime usages

Usage ID Usage Name Type Description


0x0168 Target Charge Level DV Requested percentage of Full Charge Capacity, from 1 through 100. 0x0169 Estimated Charge Time DV ACPI _BCT result in seconds. 0x016A Requested Discharge Rate DV Expected discharge rate in mW or mA; zero selects the current average rate. 0x016B Estimated Runtime DV ACPI _BTM result in seconds.

For Battery Charge Time, the host writes Target Charge Level and then reads the same Feature report. Estimated Charge Time is read-only:

0             Target is below current capacity or above 100 percent
1-0xFFFFFFFE  Estimated charging time in seconds
0xFFFFFFFF    Unknown

For Battery Runtime, the host writes Requested Discharge Rate and then reads the same Feature report. Estimated Runtime is read-only:

0             Requested rate is unsustainable, or battery is critical when
              Requested Discharge Rate is zero
1-0xFFFFFFFE  Estimated runtime in seconds
0xFFFFFFFF    Unknown

Set Feature completion means that the result is available to Get Feature. A device shall not expose partially updated request/result pairs.

Peak-power usages

Usage ID Usage Name Type


0x0170 Power Characteristics Revision SV 0x0171 Instantaneous Peak Power Threshold Supported SF 0x0172 Sustainable Peak Power Threshold Supported SF 0x0173 Maximum Instantaneous Peak Power Threshold SV 0x0174 Maximum Sustainable Peak Power Threshold SV 0x0175 Power State Revision DV 0x0176 Instantaneous Peak Power Level DV 0x0177 Instantaneous Peak Power Period DV 0x0178 Sustainable Peak Power Level DV 0x0179 Sustainable Peak Power Period DV 0x017A Power Threshold Revision DV 0x017B Power Threshold Identifier DV 0x017C Power Threshold Value DV

Power Characteristics usages map to ACPI _BPC revision 1. Maximum threshold values use mW or mA according to Battery Power Unit.

Power State usages map to ACPI _BPS revision 1. Peak Power Levels use mW or mA. Peak Power Periods use milliseconds. A level and period of zero means that the corresponding capability is unsupported. The device sends the Battery Power State Input report when either capability changes by the configured relative threshold.

Power Threshold maps to ACPI _BPT revision 1:

Power Threshold Identifier 0  Clear all thresholds
Power Threshold Identifier 1  Instantaneous peak power
Power Threshold Identifier 2  Sustainable peak power

Power Threshold Value is the relative change threshold in mW or mA. Zero disables the selected threshold. Operation Status in this report is:

0  Success
1  Invalid threshold value
2  Hardware timeout
3  Unknown hardware error
4  Unsupported threshold type
5  Unsupported revision
6-0xFFFFFFFF  Reserved

Capacity-trip, thermal, and capability usages

Usage ID Usage Name Type Description


0x017D Capacity Trip Point DV Remaining-capacity crossing point in the selected capacity unit. Zero clears it. 0x017E Thermal Throttle Limit DV Maximum permitted charging level in percent, from 0 through 100. 0x0180 Host Capability Flags DV Host capabilities corresponding to ACPI battery _OSC revision 1 DWORD 2. 0x0181 Device Capability Flags DV Device acknowledgement and supported capability bits.

Capacity Trip Point maps to ACPI _BTP. The device sends a status Input report when Remaining Capacity crosses the programmed value in either direction.

Thermal Throttle Limit maps to ACPI _BTH. At 100 percent the battery may charge at maximum current. The device is responsible for applying the limit.

Capability negotiation uses the ACPI Control Method Battery UUID F18FC78B-0F15-4978-B793-53F833A1D35B and revision 1 semantics. Defined flags are:

Bit 0  Revised battery granularity definition
Bit 1  Wake on low-battery user preference
Bit 2  Battery power-delivery threshold notifications
Bits 3-31  Reserved

The host writes Host Capability Flags. The device returns supported and accepted bits in Device Capability Flags. Reserved bits shall be zero.

Report contract

The Control Method Battery TLC contains the following reports. Report IDs are assigned by the descriptor and are not standardized.

Report Kind Access


Control Method Battery Information Feature Device to host Control Method Battery Identity Feature Device to host Control Method Battery Status Input Device to host, on change Control Method Battery Notification Feature Host configuration/readback Battery Measurement Configuration Feature Host configuration/readback Battery Maintenance Feature Bidirectional control/status Battery Charge Time Feature Host request/device result Battery Runtime Feature Host request/device result Battery Power Characteristics Feature Device to host Battery Power State Input Device to host, on change Battery Power Threshold Feature Host configuration/readback Battery Capacity Trip Point Feature Host configuration/readback Battery Thermal Control Feature Host configuration/readback Battery Capability Negotiation Feature Host configuration/readback Power Consumer List Feature Device to host

Feature reports that contain read-only fields shall ignore host-written values for those fields. Host-writable fields are applied atomically at successful Set Feature completion. Get Feature returns the last accepted configuration and the result derived from that configuration.

Common representation rules

  1. Fields are packed as declared by the HID report descriptor. Multi-byte fields use HID little-endian bit ordering.
  2. ACPI Integer (DWORD) fields use 32 report bits unless a smaller range is explicitly defined by this proposal.
  3. ACPI DWORD values are interpreted as unsigned. The all-ones bit pattern is 0xFFFFFFFF even if a descriptor tool displays it as signed -1.
  4. Reserved bits are zero when sent and ignored when received.
  5. Capacity, rate, and peak-power values use Battery Power Unit. Voltage uses millivolts. Time fields state milliseconds or seconds in their definitions.
  6. Charging and Discharging shall not both be one.
  7. Static information shall describe the battery identified by Battery Tag. The host shall discard cached information when Battery Tag changes.
  8. If Battery Present is zero, Battery Tag is zero and all other dynamic fields are ignored.
  9. While Battery Tag is zero, every identity buffer is all zero. Static fields are zero or use their defined unknown sentinel. A host shall not cache identity or static information read while Battery Tag is zero.

Host operation sequences

Enumeration

  1. Parse each Control Method Battery TLC.
  2. Read Control Method Battery Information and Identity.
  3. Read configuration/status Feature reports.
  4. Arm Control Method Battery Notification.
  5. Consume status and power-state Input reports.

Battery replacement

  1. Device sends a status Input report with Battery Present zero and Battery Tag zero.
  2. When a pack is inserted, the device assigns a new nonzero Battery Tag.
  3. Device sends a status Input report with Battery Present one, the new tag, and Battery Information Changed one.
  4. Host invalidates all cached data and rereads static, identity, maintenance, and power-characteristic reports.

Measurement configuration

  1. Host reads the supported ranges from Control Method Battery Information.
  2. Host sets Sampling Time and Averaging Interval.
  3. Host gets Battery Measurement Configuration and verifies Operation Status.

Charge-time or runtime query

  1. Host sets the request field in the corresponding Feature report.
  2. Device computes and stores the result before completing Set Feature.
  3. Host gets the same Feature report and reads the result.

ACPI 6.5 mapping

ACPI object HIDBattery representation


_STA Battery Present; HID transport presence remains independent. _BIX Control Method Battery Information and Identity. _BIF Represented by the _BIX superset; no separate legacy report. _BST Control Method Battery Status Input report. _BTP Capacity Trip Point and status Input report on crossing. _BMS Sampling Time and Operation Status. _BMA Averaging Interval and Operation Status. _BMD Battery Maintenance status/capability/count/time fields. _BMC Battery Maintenance control fields. _BCT Battery Charge Time request/result Feature report. _BTM Battery Runtime request/result Feature report. _BPC Battery Power Characteristics Feature report. _BPS Battery Power State Input report. _BPT Battery Power Threshold Feature report and threshold event. _BTH Thermal Throttle Limit Feature report. _OSC Battery Capability Negotiation Feature report. _PCL Power Consumer List Feature report.

Windows battery miniclass mapping

Battery miniclass operation HIDBattery source


BatteryMiniQueryTag Battery Present and Battery Tag. BatteryMiniQueryStatus Status flags, Remaining Capacity, Voltage, and Present Rate. BatteryMiniQueryInformation Static information, Identity, Temperature, Manufacture Date, Cycle Count, and Estimated Runtime. BatteryMiniSetInformation Critical Bias and applicable Battery Maintenance controls. BatteryMiniSetStatusNotify Notification Power State Mask and low/high capacity bounds. BatteryMiniDisableStatusNotify Clear the Control Method Battery Notification report.

BATTERY_INFORMATION mapping:

Windows field HIDBattery source


Capabilities Battery Present, Secondary Battery Technology, and driver policy. Technology Primary or Secondary Battery Technology. Chemistry First four ASCII bytes of Battery Type String, padded with spaces. DesignedCapacity Design Capacity. FullChargedCapacity Full Charge Capacity. DefaultAlert1 Design Capacity Low. DefaultAlert2 Warning Capacity Limit. CriticalBias Critical Bias. CycleCount Cycle Count.

BATTERY_STATUS mapping:

Windows field HIDBattery source


PowerState System On Line, Charging, Discharging, and Battery Critical. Capacity Remaining Capacity. Voltage Voltage. Rate Present Rate, positive while charging and negative while discharging.

When Battery Power Unit selects milliampere-hours/milliamperes, a Windows miniclass converts every Windows-facing capacity or rate quantity using Voltage when the Windows API requires power units. This includes DesignedCapacity, FullChargedCapacity, DefaultAlert1, DefaultAlert2, CriticalBias, status Capacity and Rate, and notification low/high bounds. If a reliable conversion is not possible, the driver uses the Windows relative-capacity capability consistently for capacities, alert thresholds, critical bias, and notification bounds while preserving their ordering and threshold semantics.

Sample descriptor

The accompanying HIDBattery.wara file is a non-normative Waratah 1.9 source descriptor for this proposal. It:

  • extends the built-in Battery System page with the proposed usages;
  • creates one Control Method Battery TLC;
  • reuses the existing Power and Battery System usages listed above;
  • contains the complete report surface described by this proposal; and
  • compiles with WaratahCmd 1.9 against HID Usage Tables 1.7.

Waratah 1.9 cannot declare the complete unsigned 32-bit logical range required by ACPI while also generating the all-ones sentinel. The sample therefore uses a signed logical range for raw ACPI DWORD fields, and some non-sentinel fields are optimized to fewer than 32 bits. The sample validates report organization and usage compatibility, but it is not a normative firmware descriptor for values from 0x80000000 through 0xFFFFFFFE. A production descriptor shall follow the Common representation rules above and encode the complete 32-bit field.

References

  1. USB-IF, HID Usage Tables 1.7, Power Page (0x84) and Battery System Page (0x85): https://usb.org/sites/default/files/hut1_7.pdf
  2. UEFI Forum, ACPI Specification 6.5, section 10.2.2, Battery Control Methods: https://uefi.org/specs/ACPI/6.5/10_Power_Source_and_Power_Meter_Devices.html#battery-control-methods
  3. Microsoft, Writing Battery Miniclass Drivers: https://learn.microsoft.com/windows-hardware/drivers/battery/writing-battery-miniclass-drivers
  4. Microsoft, hidtools / Waratah: https://github.com/microsoft/hidtools
  5. USB-IF, HUTRR submission template and process: https://www.usb.org/sites/default/files/hutrr_form.txt

Response

eSPI ACPI Interface Update

Purpose

The purpose of this document is to propose an update for the eSPI hardware interface that allows a more efficient and common interface to various eSPI hardware on x86 and ARM designs.

Current ACPI EC eSPI Interface

The current ACPI design is implemented with 3 I/O ports and two interrupt signals as defined below.

12. ACPI Embedded Controller Interface Specification — ACPI Specification 6.5 documentation

I/O Ports

RegisterAccessPurpose
EC_SCReadStatus Register
EC_SCWriteCommand Register
EC_DATARead/WriteData Register

Status Register

BitNameMeaning
0OBFOutput Buffer Full
1IBFInput Buffer Full
2Reserved
3CMDData register contains command
4BURSTEC in burst mode
5SCI_EVTSCI query pending
6SMI_EVTSMI query pending
7Reserved

Command Register

CommandValue
RD_EC0x80
WR_EC0x81
BE_EC0x82
BD_EC0x83
QR_EC0x84

EC Operations

EC Write Flow

  1. Wait until ready: Confirm EC_SC[IBF] = 0.
  2. Issue write command: Write 0x81 (WR_EC) to EC_SC.
  3. Wait for command acceptance: Confirm EC_SC[IBF] = 0.
  4. Provide address: Write the EC address to EC_DATA.
  5. Wait for address acceptance: Confirm EC_SC[IBF] = 0.
  6. Provide data: Write the data byte to EC_DATA.
  7. Complete transaction: Wait until EC_SC[IBF] = 0.

EC Read Flow

  1. Wait until ready: Confirm EC_SC[IBF] = 0.
  2. Issue read command: Write 0x80 (RD_EC) to EC_SC.
  3. Wait for command acceptance: Confirm EC_SC[IBF] = 0.
  4. Provide address: Write the EC address to EC_DATA.
  5. Wait for response: Wait until EC_SC[OBF] = 1.
  6. Read data: Read the data byte from EC_DATA.

EC Event Flow

  1. Receive notification: OSPM invokes the EC GPE handler.
  2. Confirm pending SCI: Verify EC_SC[SCI_EVT] = 1.
  3. Wait until ready: Confirm EC_SC[IBF] = 0.
  4. Issue query command: Write 0x84 (QR_EC) to EC_SC.
  5. Wait for command acceptance: Confirm EC_SC[IBF] = 0.
  6. Read event code: Read EVT = XX from EC_DATA.
  7. Dispatch handler: Invoke the corresponding _QXX control method.

Note: The OEM defines the meaning of each event code and its corresponding _QXX method.

eSPI Features to Address

The following is a list of issues with the current ACPI eSPI EC definition that we are seeking to address:

  1. Current ACPI definition is based on I/O port definition only works on architectures that support I/O ports
  2. Only works for flat memory mapped layout, does not work well with packet-based transactions
  3. Very inefficient throughput for larger transfers of data, it doesn’t take advantage of the actual eSPI protocol ability to send larger packets
  4. Limited VWire support for SMI and SCI, want IRQ and GPIO expansion ability
  5. I/O port model does not allow for easy expansion to multiple channels and protection/securing of channels

eSPI Proposal over PCC

Existing ACPI specifications are available for defining channel-based communication through PCC.

14. Platform Communications Channel (PCC) — ACPI Specification 6.5 documentation

Recommendation is to define a hardware interface that is compatible with PCC Type 3 and 4 that allows us to create inbox ACPI based or driver-based handling of communication with the channel.

Keep PCC Type 0 for a bi-directional flat mapped memory region as existing today and support backward compatible EC’s that are not packet based.

Hardware Resources

Each eSPI controller consists of channel independent registers, VWire, Peripheral, OOB and Flash Channels. Each of these sections is optional and only present if defined via a resource entry.

Channel Independent Config

An optional Memory32Fixed region can be defined in the ESPI _CRS that identifies non-channel independent register configuration as defined in the eSPI specification.

If no resources are defined, then it is presumed that the BIOS or firmware will configure the channels as necessary to operate with the PCC regions defined.

Start (Hex)End (Hex)Register Name
000003Reserved
004007Device Identification
00800BGeneral Capabilities and Configurations
00C00FReserved
010013Channel 0 Capabilities and Configurations
01401FReserved
020023Channel 1 Capabilities and Configurations
02402FReserved
030033Channel 2 Capabilities and Configurations
03403FReserved
040043Channel 3 Capabilities and Configurations
044047Channel 3 Capabilities and Configurations 2
04804BChannel 3 Capabilities and Configurations 3
04C04FChannel 3 Capabilities and Configurations 4
0507FFReserved
800FFFPlatform Specific registers

Status Registers

The eSPI status register is processed directly by the controller hardware itself and as such isn’t directly exposed. However, the PCC specification requires a doorbell and command complete register for each PCC channel that allows flow control.

Doorbell – Requires a single bit in a register or I/O port to indicate when the host has finished writing the data and the device can read the data from the peripheral memory.

Command Complete – Requires a single bit in a register or I/O port to indicate that the device has finished processing the data and the host can overwrite it with new data.

Error Status – Optional, reports errors to the OS if error recovery isn’t implemented fully in the controller itself.

On existing platforms recommendation is to use an I/O port for each of these. Each bit within the I/O port represents the state for the corresponding channel allowing 8-channels. The mask and values can be programmed via PCC so this isn’t required but a suggested implementation to allow existing hardware to function.

Global Reset Register

Optional, an MMIO or I/O port register and mask that can be written to that traps into FW to initiate an in-band reset and reconfigures the eSPI config space back to defaults. Any pending transactions are lost, and the controller is in a fresh state.

VWire Channel

The eSPI controller should decode VWires and allow a configuration that maps at least 16 IRQ’s from index 0 to hard coded SIRQ’s or vectors. These may either be directed to ACPI or the OS may choose to directly register an ISR for these interrupts. The meaning behind these interrupts and what to do in response to them is implementation defined.

How many IRQ’s are supported and their mapping should be defined in the _CRS of the eSPI device.

System events with indexes 2-7 should be supported via legacy ACPI interface with SCI and SMI support.

Optional support for GPIO expansion via index 128-255 can be defined as well. The details of how those are reported and defining structures need to be decided via MMIO or I/O definitions in future revision.

Peripheral Channel

There can be multiple sub-channels exposed via a single peripheral channel. Each sub-channel must have a corresponding PCC table defining the channel details and the address of the MMIO region. For packet-based communication there must be two PCC tables defined:

Type 3 – Initiator table to describe all the communication from the host to the device.

Type 4 – Responder table to describe all the communication from the device to the host.

For legacy flat mapped memory space which just uses direct memory updates and is not packet based, this region can be directly accessed as MMIO or SystemMemory. Type 0 PCC region may also be used to describe the memory if a doorbell mechanism or more signaling is required.

The doorbell register when written will trigger a peripheral channel transfer of the data length specified by the Length field in the PCC Shared Memory Region.

On systems where MMIO accesses automatically generate peripheral transactions, the doorbell can be set to 0. The command complete register will still need to point to an MMIO or I/O space location to indicate the state of data transfer.

Sample ACPI Definition for the eSPI PCC Device

The sample separates the namespace device definition from the PCCT subspace records. PCC is exposed as independent subspaces, and the subspace ID is the index of each structure in the PCCT array. The addresses below are illustrative and must be replaced by the platform memory map.

Illustrative Address Map

ResourceBase AddressLengthPurpose
Channel-independent configuration0xFEDC00000x1000Read-only eSPI identification and channel capability/configuration registers.
Global Doorbell Register0xFEDC10000x4Doorbell status, allows 32-channels, bit 0-31 represents doorbell status for each channel.
Global Command Complete Register0xFEDC10040x4Command complete status for 32-channels, one bit per channel.
PCC Type 3 shared memory0xFEDC20000x4001024-byte extended master subspace for OS-initiated bidirectional peripheral traffic.
PCC Type 4 shared memory0xFEDC24000x4001024-byte extended slave subspace for platform-initiated bidirectional peripheral traffic.

Sample SSDT Namespace Definition

DefinitionBlock (“”, “SSDT”, 2, “OEMID”, “ESPIPCC”, 0x00000001)
{
  Scope (\SB)
  {
    Device (ESPI)
    {
      Name (_HID, “OEM0001”)
      Name (_UID, Zero)
      Name (_DDN, “eSPI PCC Controller”)
      Name (_STA, 0x0F)

      Name (_CRS, ResourceTemplate ()
      {
        Memory32Fixed (ReadOnly, 0xFEDC0000, 0x00001000, CFG0)
        Memory32Fixed (ReadWrite, 0xFEDC1000, 0x00000004, STS0)
        Memory32Fixed (ReadWrite, 0xFEDC1004, 0x00000004, RST0)
        Memory32Fixed (ReadWrite, 0xFEDC1008, 0x00000008, DBR0)
        Memory32Fixed (ReadWrite, 0xFEDC1010, 0x00000004, VWF0)
      })

      OperationRegion (ECFG, SystemMemory, 0xFEDC0000, 0x1000)
      OperationRegion (GCSR, SystemMemory, 0xFEDC1000, 0x0014)
      Field (GCSR, DWordAcc, NoLock, Preserve)
      {
        GSTA, 32,
        GRST, 1, Reserved, 31,
        DB00, 1, Reserved, 31,
        AK00, 1, Reserved, 31,
        VWFR, 32  // VWire FIFO, offset 0x10
      }

      // Extended PCC shared-memory header: 16 bytes, followed by 1008-byte payload.
      OperationRegion (PCC3, PCC, 0, 0x400)  // Host -> EC, Type 3
      Field (PCC3, DWordAcc, NoLock, Preserve)
      {
        C3SG, 32,  // Signature, offset 0x00
        C3FL, 32,  // Flags, offset 0x04
        C3LN, 32,  // Length, offset 0x08
        C3CM, 32,  // Command, offset 0x0C
        C3DT, 8064  // Payload, offset 0x10, 1008 bytes
      }

      OperationRegion (PCC4, PCC, 1, 0x400)  // EC -> Host, Type 4
      Field (PCC4, DWordAcc, NoLock, Preserve)
      {
        C4SG, 32,  // Signature, offset 0x00
        C4FL, 32,  // Flags, offset 0x04
        C4LN, 32,  // Length, offset 0x08
        C4CM, 32,  // Command, offset 0x0C
        C4DT, 8064  // Payload, offset 0x10, 1008 bytes
      }

      Method (ERST, 0, Serialized)
      {
        Store (One, GRST)
      }
    }
  }
}

Namespace notes: PCC3 references PCCT subspace ID 0 and PCC4 references subspace ID 1. Each OperationRegion remains exactly 0x400 bytes. The first 16 bytes follow the Extended PCC shared-memory header layout: a 32-bit Signature at offset 0x00, 32-bit Flags at offset 0x04, 32-bit Length at offset 0x08, and 32-bit Command at offset 0x0C. The remaining 1008 bytes, starting at offset 0x10, are available for the eSPI packet payload.

Sample PCCT Subspace Definitions

The PCCT contains an ordered array of PCC subspace structures, with the array index becoming the PCC subspace ID. The following pseudo-definition shows the fields that platform firmware would emit for the two channels; exact C structure names depend on the firmware ACPI table library.

PCCT.Header.Signature = ‘PCCT’
PCCT.Header.Revision = 2
PCCT.Flags = 1  // Platform interrupt supported

// Subspace ID 0: Extended PCC Master (Type 3)
Type = 3
Length = sizeof (TYPE3_SUBSPACE)
PlatformInterrupt = <GSI used by the eSPI controller>
InterruptFlags = <edge/level and polarity for that GSI>
BaseAddress = 0x00000000FEDC2000
AddressLength = 0x0000000000000400  // 1024 bytes
DoorbellRegister = GAS(SystemMemory, 32, 0, DWord, 0xFEDC1008)
DoorbellPreserve = 0xFFFFFFFE
DoorbellWrite = 0x00000001
CommandCompleteCheck = GAS(SystemMemory, 32, 0, DWord, 0xFEDC1000)
CommandCompleteMask = 0x00000001
CommandCompleteValue = 0x00000001
ErrorStatusRegister = GAS(SystemMemory, 32, 0, DWord, 0xFEDC1000)
ErrorStatusMask = 0x00000002

// Subspace ID 1: Extended PCC Slave (Type 4)
Type = 4
Length = sizeof (TYPE4_SUBSPACE)
PlatformInterrupt = <GSI used by the eSPI controller>
InterruptFlags = <edge/level and polarity for that GSI>
BaseAddress = 0x00000000FEDC2400
AddressLength = 0x0000000000000400  // 1024 bytes
DoorbellRegister = GAS(SystemMemory, 32, 0, DWord, 0xFEDC1008)
DoorbellPreserve = 0xFFFFFFFE
DoorbellWrite = 0x00000001
CommandCompleteCheck = GAS(SystemMemory, 32, 0, DWord, 0xFEDC1000)
CommandCompleteMask = 0x00000004
CommandCompleteValue = 0x00000004
PlatformAckRegister = GAS(SystemMemory, 32, 0, DWord, 0xFEDC100C)
PlatformAckPreserve = 0xFFFFFFFE
PlatformAckWrite = 0x00000001

Sample PCC Type 3 and Type 4 Call Flows

PCC communication uses independent subspaces, shared memory, a doorbell protocol, platform notification, and completion/status registers. The following sample maps those mechanisms to the document’s Type 3 PCC3 initiator channel and Type 4 PCC4 platform-notification channel.

CCC = Command Complete Check register

Initiator/Responder Call Flow: Host 🡪 eSPI Device

sequenceDiagram
    participant OSPM
    participant EC as eSPI Controller / EC

    OSPM->>EC: Wait PCC3 complete
    OSPM->>EC: Clear PCC3 complete
    OSPM->>EC: Write header + request
    OSPM->>EC: Memory barrier
    OSPM->>EC: Trigger Doorbell

    Note right of EC: Read PCC3 request

    EC-->>OSPM: Set PCC3 complete
    EC-->>OSPM: Wait PCC4 complete
    EC-->>OSPM: Clear PCC4 complete
    EC-->>OSPM: Write Header + Response
    EC-->>OSPM: Trigger VWire/Interrupt

    Note left of OSPM: Notification PCC4
    Note left of OSPM: Process PCC4 response

    OSPM->>EC: Set PCC4 complete