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:
- MCU HALs and board support provide access to clocks, GPIO, I2C, eSPI, I3C, and other peripherals.
- Subsystem interfaces decouple portable service logic from hardware implementations for battery, thermal, power, USB-C, HID, and other common EC subsystems.
- Embedded services implement reference application logic for EC subsystems.
- 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
| Repository | Purpose |
|---|---|
odp-embedded-controller | Reference 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-virt | End-to-end Secure EC test platform connecting a virtualized RISC-V EC to an Arm64 QEMU host running Windows Validation OS. |
embedded-services | Reusable 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
- Respond to
cargo vetfailures:
- If your PR fails the
cargo vetstep, 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
- Engage with auditors:
- Respond to any questions that the auditors might have regarding the need of any new dependencies
- 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 vetgit fetch upstream git rebase upstream/main cargo vet
- 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
- Check PR status:
- The existing PR comment from the previous failure will be updated with a success message once the check passes
For Auditors
- Review the questionnaire:
- Check the filled questionnaire on the PR once the developer responds to the
cargo vetfailure - Respond to the developer comment in case more information is needed
- Audit new dependencies:
- Inspect the
cargo vetfailures using your preferred method- Use gh pr checkout to checkout the PR and run
cargo vet --locked - Use GitHub Pull Requests for Visual Studio Code to checkout the PR and run
cargo vet --locked - For more suggestions: Checking out pull requests locally
- Use gh pr checkout to checkout the PR and run
- Follow
cargo vetrecommendations:
- Follow the recommendations of the
cargo vetcommand output, eithercargo vet difffor version update orcargo vet inspectfor new dependencies
- Record audits:
- Use
cargo vet certifyto add new audits to audits.toml - Verify all dependencies pass using
cargo vet
- 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
- Communicate successful audit:
- Communicate to the PR developer via a PR comment so they can update the PR and get
cargo vetto 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
notesfield 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-deployimpliessafe-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 vetwithout--lockedfetches new imports and updates imports.lock with any audits that are helpful for our project.
- Import trusted third party audits to reduce the number of new audits to be performed. Running
-
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
- If an audit cannot be performed for some dependency due to time sensitivity or business justified reasons, use
-
Prune unnecessary entries:
- Remove unnecessary exemptions and imports using
cargo vet prune
- Remove unnecessary exemptions and imports using
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.
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 Name | Service GUID | Description |
|---|---|---|
| EC_SVC_MANAGEMENT | 330c1273-fde5-4757-9819-5b6539037502 | Used to query EC functionality, Board info, version, security state, FW update |
| EC_SVC_POWER | 7157addf-2fbe-4c63-ae95-efac16e3b01c | Handles general power related requests and OS Sx state transition state notification |
| EC_SVC_BATTERY | 25cb5207-ac36-427d-aaef-3aa78877d27e | Handles battery info, status, charging |
| EC_SVC_THERMAL | 31f56da7-593c-4d72-a4b3-8fc7171ac073 | Handles thermal requests for skin and other thermal events |
| EC_SVC_UCSI | 65467f50-827f-4e4f-8770-dbf4c3f77f45 | Handles PD notifications and calls to UCSI interface |
| EC_SVC_INPUT | e3168a99-4a57-4a2b-8c5e-11bcfec73406 | Handles wake events, power key, lid, input devices (HID separate instance) |
| EC_SVC_TIME_ALARM | 23ea63ed-b593-46ea-b027-8924df88e92f | Handles RTC and wake timers. |
| EC_SVC_DEBUG | 0bd66c7c-a288-48a6-afc8-e2200c03eb62 | Used for telemetry, debug control, recovery modes, logs, etc |
| EC_SVC_TEST | 6c44c879-d0bc-41d3-bef6-60432182dfe6 | Used to send commands for manufacturing/factory test |
| EC_SVC_OEM1 | 9a8a1e88-a880-447c-830d-6d764e9172bb | Sample 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_DSMselector6f8398c2-7ca4-11e4-ad36-631042b5008f, which is the OS-standard argument the_DSMmethod 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_REQ2 | X0 | 0xC400008D |
| Sender/Receiver Id | X1 | Bits[31:16]: Sender endpoint ID Bits[15:0]: Receiver endpoint ID |
| Protocol UUID low | X2 | Bytes[0..7] of Inter-partition setup protocol UUID |
| Protocol UUID high | X3 | Bytes[8..15] of Inter-partition setup protocol UUID |
| Reserved SBZ | X4 | 0x0 |
| Sender UUID low | X5 | Bytes[0..7] of service UUID |
| Sender UUID high | X6 | Bytes[8..15] of service UUID |
| Receiver UUID low | X7 | Bytes[0..7] of service UUID |
| Receiver UUID high | X8 | Bytes[8..15] of service UUID |
| Message Information | X9 | Bits[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 Information | X10 | Bits[63:9]: Bits[63:9]: Reserved MBZ Bits[8:0]: Count of (cookie,notification ID) tuples - 1 <= Count <= 7 |
| Tuple Mapping | X11-xX17 | Bits[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_RESP2 | X0 | 0xC400008E |
| Sender/Receiver Id | X1 | Bits[31:16]: Sender endpoint ID Bits[15:0]: Receiver endpoint ID |
| Protocol UUID low | X2 | Bytes[0..7] of Inter-partition setup protocol UUID |
| Protocol UUID high | X3 | Bytes[8..15] of Inter-partition setup protocol UUID |
| Reserved SBZ | X4 | 0x0 |
| Sender UUID low | X5 | Bytes[0..7] of service UUID |
| Sender UUID high | X6 | Bytes[8..15] of service UUID |
| Receiver UUID low | X7 | Bytes[0..7] of service UUID |
| Receiver UUID high | X8 | Bytes[8..15] of service UUID |
| Message Information | X9 | Bits[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 Information | X10 | Bits[63:9]: Bits[63:9]: Reserved MBZ Bits[8:0]: Count of (cookie,notification ID) tuples - 1 <= Count <= 7 |
| Tuple Mapping | X11-xX17 | Bits[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(|®| 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 Command | Description |
|---|---|
| EC_CAP_GET_FW_STATE = 0x1 | Return details of FW in EC, DICE, Secure Boot, Version, etc |
| EC_CAP_GET_SVC_LIST = 0x2 | Get list of services/features that this EC supports |
| EC_CAP_GET_BID = 0x3 | Read Board ID that is used customized behavior |
| EC_CAP_TEST_NFY = 0x4 | Create 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
| Field | Bits | Description |
|---|---|---|
| DebugMask | 16 | 0 - Supports reset reason 1 - Supports debug tracing |
| BatteryMask | 8 | 0 - Battery 0 present 1 - Battery 1 present … |
| FanMask | 8 | 0 - Fan 0 present 1 - Fan 1 present … |
| ThermalMask | 8 | 0 - Skin TZ present |
| HIDMask | 8 | 0 - HID0 present 1 - HID1 present … |
| KeyMask | 16 | 0 - 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
| Field | Bits | Description |
|---|---|---|
| BoardID | 64 | Vendor 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)
| Command | Description |
|---|---|
| EC_BAT_GET_BIX = 0x1 | Returns information about battery, model, serial number voltage. Note this is a superset of BIF. (MPTF) |
| EC_BAT_GET_BST = 0x2 | Get Battery Status, must also have notify event on state change. (MPTF) |
| EC_BAT_GET_PSR = 0x3 | Returns whether this power source device is currently online. (MPTF) |
| EC_BAT_GET_PIF = 0x4 | Returns static information about a power source. (MPTF) |
| EC_BAT_GET_BPS = 0x5 | Power delivery capabilities of battery at present time. (MPTF) |
| EC_BAT_SET_BTP = 0x6 | Set battery trip point to generate SCI event (MPTF) |
| EC_BAT_SET_BPT = 0x7 | Set Battery Power Threshold (MPTF) |
| EC_BAT_GET_BPC = 0x8 | Returns static variables that are associated with system power characteristics on the battery path and power threshold support settings. (MPTF) |
| EC_BAT_SET_BMC= 0x9 | Battery Maintenance Control |
| EC_BAT_GET_BMD = 0xA | Returns battery information regarding charging and calibration |
| EC_BAT_GET_BCT = 0xB | Returns battery charge time. |
| EC_BAT_GET_BTM = 0xC | Get estimated runtime of battery while discharging |
| EC_BAT_SET_BMS = 0xD | Sets battery capacity sampling time in ms |
| EC_BAT_SET_BMA = 0xE | Battery Measurement Average Interval |
| EC_BAT_GET_STA = 0xF | Get 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.
| Command | Description |
|---|---|
| EC_THM_GET_TMP = 0x1 | Returns the thermal zone’s current temperature in tenths of degrees. |
| EC_THM_SET_THRS = 0x2 | Sets the thresholds for high, low and timeout. |
| EC_THM_GET_THRS = 0x3 | Get thresholds for low and high points |
| EC_THM_SET_SCP = 0x4 | Set cooling Policy for thermal zone |
| EC_THM_GET_VAR = 0x5 | Read DWORD variable related to thermal |
| EC_THM_SET_VAR = 0x6 | Write 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.
| Variable | GUID | Description |
|---|---|---|
| OnTemp | ba17b567-c368-48d5-bc6f-a312a41583c1 | Lowest temperature at which the fan is turned on. |
| RampTemp | 3a62688c-d95b-4d2d-bacc-90d7a5816bcd | Temperature at which the fan starts ramping from min speed. |
| MaxTemp | dcb758b1-f0fd-4ec7-b2c0-ef1e2a547b76 | Temperature at top of fan ramp where fan is at maximum speed. |
| CrtTemp | 218246e7-baf6-45f1-aa13-07e4845256b8 | Critical temperature at which we need to shut down the system. |
| ProcHotTemp | 22dc52d2-fd0b-47ab-95b8-26552f9831a5 | Temperature at which the EC will assert the PROCHOT notification. |
| MinRpm | db261c77-934b-45e2-9742-256c62badb7a | Minimum RPM FAN speed |
| MinDba (Optional) | 0457a722-58f4-41ca-b053-c7088fcfb89d | Minimum Dba from FAN |
| MinSones (Optional) | 311668e2-09aa-416e-a7ce-7b978e7f88be | Minimum Sones from FAN |
| MaxRpm | 5cf839df-8be7-42b9-9ac5-3403ca2c8a6a | Maximum RPM for FAN |
| MaxDba (Optional) | 372ae76b-eb64-466d-ae6b-1228397cf374 | Maximum DBA for FAN |
| MaxSones (Optional) | 6deb7eb1-839a-4482-8757-502ac31b20b7 | Maximum Sones for FAN |
| ProfileType | 23b4a025-cdfd-4af9-a411-37a24c574615 | Set profile for EC, gaming, quiet, lap, etc |
| CurrentRpm | adf95492-0776-4ffc-84f3-b6c8b5269683 | The current RPM of FAN |
| CurrentDba (Optional) | 4bb2ccd9-c7d7-4629-9fd6-1bc46300ee77 | The current Dba from FAN |
| CurrentSones (Optional) | 7719d686-02af-48a5-8283-20ba6ca2e940 | The 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) | Mnemonic | Description | Direction | Size (bits) |
|---|---|---|---|---|
| 0 | VERSION | UCSI Version Number | PPM->OPM | 16 |
| 2 | RESERVED | Reserved | N/A | 16 |
| 4 | CCI | USB Type-C Command Status and Connector Change Indication | PPM->OPM | 32 |
| 8 | CONTROL | USB Type-C Control | OPM->PPM | 64 |
| 16 | MESSAGE IN | USB Type-C Message In | PPM->OPM | 128 |
| 32 | MESSAGE OUT | USB Type-C Message Out | OPM->PPM | 128 |
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.
- byte 0 = doorbell/command tag
- 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.
| ACPI | Description |
|---|---|
| _LID | Get 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 IOCTL | Description |
|---|---|
| IOCTL_HID_GET_DEVICE_DESCRIPTOR | Retrieves the device’s HID descriptor |
| IOCTL_HID_GET_DEVICE_ATTRIBUTES | Retrieves a device’s attributes in a HID_DEVICE_ATTRIBUTES structure |
| IOCTL_HID_GET_REPORT_DESCRIPTOR | Obtains the report descriptor for the HID device |
| IOCTL_HID_READ_REPORT | Returns a report from the device into a class driver-supplied buffer |
| IOCTL_HID_WRITE_REPORT | Transmits a class driver-supplied report to the device |
| IOCTL_HID_GET_FEATURE | Get capabilities of a feature from the device |
| IOCTL_HID_SET_FEATURE | Set/Enable a specific feature on device |
| IOCTL_HID_GET_INPUT_REPORT | Get input report from HID device if input device |
| IOCTL_HID_SET_OUTPUT_REPORT | Send output HID report to device |
| IOCTL_HID_GET_STRING | Get a specific string from device |
| IOCTL_HID_GET_INDEXED_STRING | Get a string from device based on index |
| IOCTL_HID_SEND_IDLE_NOTIFICATION | Notification 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.
| Command | Description |
|---|---|
| EC_TAS_GET_GCP = 0x1 | Get the capabilities of the time and alarm device |
| EC_TAS_GET_GRT = 0x2 | Get the Real Time |
| EC_TAS_SET_SRT = 0x3 | Set the Real Time |
| EC_TAS_GET_GWS = 0x4 | Get Wake Status |
| EC_TAS_SET_CWS = 0x5 | Clear Wake Status |
| EC_TAS_SET_STV = 0x6 | Set Timer value for given timer |
| EC_TAS_GET_TIV = 0x7 | Get Timer value remaining for given timer |
| EC_TAS_SET_STP = 0x8 | Set expired timer policy for given timer |
| EC_TAS_GET_TIP = 0x9 | Get 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.
| Command | Argument bytes | EC success response ID / body |
|---|---|---|
| 1 / _GCP | None | 1 / u32 capabilities |
| 2 / _GRT | None | 2 / 16-byte ACPI timestamp |
| 3 / _SRT | 16-byte ACPI timestamp | 6 / empty |
| 4 / _GWS | u32 timer ID | 3 / u32 wake status |
| 5 / _CWS | u32 timer ID | 6 / empty |
| 6 / _STV | u32 timer ID, u32 seconds | 6 / empty |
| 7 / _TIV | u32 timer ID | 5 / u32 seconds |
| 8 / _STP | u32 timer ID, u32 policy seconds | 6 / empty |
| 9 / _TIP | u32 timer ID | 4 / 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:
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 Id | Usage Name | Usage Types |
|---|---|---|
| 0x46-4F | Reserved | |
| 0x50 | Vendor Current State | DV |
| 0x51 | Current State | NAry |
| 0x52 | Cleared | Sel |
| 0x53 | Failed | Sel |
| 0x54 | Running | Sel |
| 0x55 | Expired | Sel |
| 0x56 | Signaled | Sel |
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 Id | Usage Name | Usage Types |
|---|---|---|
| 0x00 | Undefined | |
| 0x01 | Year | DV |
| 0x02 | Month | DV |
| 0x03 | Day | DV |
| 0x04 | Hour | DV |
| 0x05 | Minute | DV |
| 0x06 | Second | DV |
| 0x07 | Millisecond | DV |
| 0x08-F | Reserved | |
| 0x10 | Time Zone Offset From UTC | DV |
| 0x11 | Daylight Savings Time Observed | DF |
| 0x12 | Daylight Savings Time Active | DF |
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 Name | Usage Types | Usage Description |
|---|---|---|
| Hour | DV | Standard UTC hour |
| Minute | DV | Standard UTC minute |
| Second | DV | Standard UTC second |
| Millisecond | DV | Standard 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. 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 Id | Usage Name | Usage Types |
|---|---|---|
| 0x14 | System Wake Timer | CA |
| 0x15 | System Real Time Clock | CA |
| 0xF0 | Timer Expiration: External Power | DV |
| 0xF1 | Timer Expiration: Internal Power | DV |
| 0xF2 | Power Source Change Minimum Expiration | DV |
| 0xF3 | Lowest System Wakeable Power State | NAry |
| 0xF4 | S1 | Sel |
| 0xF5 | S2 | Sel |
| 0xF6 | S3 | Sel |
| 0xF7 | S4 | Sel |
| 0xF8 | S5 | Sel |
| 0xF9-FFFF | Reserved |
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
-
Upon configuration, the wake timer immediately begins counting-down (i.e. while in S0).
-
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’
-
If the system is in Sx, and the wake timer expires, the system is signalled to wake.
-
If the system is in S0, and the wake timer expires, no wake is signalled.
-
If the system enters Sx with an expired wake timer, the system is immediately signalled to wake.
-
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)
-
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 Name | Usage Types | Usage Description |
|---|---|---|
| System Real Time Clock | CA | The 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.
|
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.
|
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
- Define one standard battery TLC that is independent of USB, HID-over-I2C, HID-over-SPI, and other HID transports.
- Preserve the meaning, units, special values, and event behavior of the ACPI 6.5 Control Method Battery interface.
- Reuse existing HID usages when their meaning is equivalent.
- Support a persistent controller with a removable or replaceable battery pack.
- Support multiple batteries by exposing one TLC for each independently managed battery.
- Carry battery identity strings in reports rather than relying on transport-specific string descriptor indexes.
- Provide the information and controls needed by the Windows battery miniclass interface without making Windows structures the wire format.
Non-goals
- This proposal does not define charger, AC adapter, UPS, or system aggregate battery TLCs.
- This proposal does not require report IDs to have fixed numeric values. Hosts discover reports and fields by usages.
- This proposal does not expose AML, ACPI namespace paths, General Purpose Events, or System Control Interrupts on the wire.
- 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
- Fields are packed as declared by the HID report descriptor. Multi-byte fields use HID little-endian bit ordering.
- ACPI Integer (DWORD) fields use 32 report bits unless a smaller range is explicitly defined by this proposal.
- ACPI DWORD values are interpreted as unsigned. The all-ones bit pattern is 0xFFFFFFFF even if a descriptor tool displays it as signed -1.
- Reserved bits are zero when sent and ignored when received.
- Capacity, rate, and peak-power values use Battery Power Unit. Voltage uses millivolts. Time fields state milliseconds or seconds in their definitions.
- Charging and Discharging shall not both be one.
- Static information shall describe the battery identified by Battery Tag. The host shall discard cached information when Battery Tag changes.
- If Battery Present is zero, Battery Tag is zero and all other dynamic fields are ignored.
- 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
- Parse each Control Method Battery TLC.
- Read Control Method Battery Information and Identity.
- Read configuration/status Feature reports.
- Arm Control Method Battery Notification.
- Consume status and power-state Input reports.
Battery replacement
- Device sends a status Input report with Battery Present zero and Battery Tag zero.
- When a pack is inserted, the device assigns a new nonzero Battery Tag.
- Device sends a status Input report with Battery Present one, the new tag, and Battery Information Changed one.
- Host invalidates all cached data and rereads static, identity, maintenance, and power-characteristic reports.
Measurement configuration
- Host reads the supported ranges from Control Method Battery Information.
- Host sets Sampling Time and Averaging Interval.
- Host gets Battery Measurement Configuration and verifies Operation Status.
Charge-time or runtime query
- Host sets the request field in the corresponding Feature report.
- Device computes and stores the result before completing Set Feature.
- 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
- USB-IF, HID Usage Tables 1.7, Power Page (0x84) and Battery System Page (0x85): https://usb.org/sites/default/files/hut1_7.pdf
- 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
- Microsoft, Writing Battery Miniclass Drivers: https://learn.microsoft.com/windows-hardware/drivers/battery/writing-battery-miniclass-drivers
- Microsoft, hidtools / Waratah: https://github.com/microsoft/hidtools
- 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
| Register | Access | Purpose |
|---|---|---|
| EC_SC | Read | Status Register |
| EC_SC | Write | Command Register |
| EC_DATA | Read/Write | Data Register |
Status Register
| Bit | Name | Meaning |
|---|---|---|
| 0 | OBF | Output Buffer Full |
| 1 | IBF | Input Buffer Full |
| 2 | Reserved | |
| 3 | CMD | Data register contains command |
| 4 | BURST | EC in burst mode |
| 5 | SCI_EVT | SCI query pending |
| 6 | SMI_EVT | SMI query pending |
| 7 | Reserved |
Command Register
| Command | Value |
|---|---|
| RD_EC | 0x80 |
| WR_EC | 0x81 |
| BE_EC | 0x82 |
| BD_EC | 0x83 |
| QR_EC | 0x84 |
EC Operations
EC Write Flow
- Wait until ready: Confirm EC_SC[IBF] = 0.
- Issue write command: Write 0x81 (WR_EC) to EC_SC.
- Wait for command acceptance: Confirm EC_SC[IBF] = 0.
- Provide address: Write the EC address to EC_DATA.
- Wait for address acceptance: Confirm EC_SC[IBF] = 0.
- Provide data: Write the data byte to EC_DATA.
- Complete transaction: Wait until EC_SC[IBF] = 0.
EC Read Flow
- Wait until ready: Confirm EC_SC[IBF] = 0.
- Issue read command: Write 0x80 (RD_EC) to EC_SC.
- Wait for command acceptance: Confirm EC_SC[IBF] = 0.
- Provide address: Write the EC address to EC_DATA.
- Wait for response: Wait until EC_SC[OBF] = 1.
- Read data: Read the data byte from EC_DATA.
EC Event Flow
- Receive notification: OSPM invokes the EC GPE handler.
- Confirm pending SCI: Verify EC_SC[SCI_EVT] = 1.
- Wait until ready: Confirm EC_SC[IBF] = 0.
- Issue query command: Write 0x84 (QR_EC) to EC_SC.
- Wait for command acceptance: Confirm EC_SC[IBF] = 0.
- Read event code: Read EVT = XX from EC_DATA.
- 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:
- Current ACPI definition is based on I/O port definition only works on architectures that support I/O ports
- Only works for flat memory mapped layout, does not work well with packet-based transactions
- Very inefficient throughput for larger transfers of data, it doesn’t take advantage of the actual eSPI protocol ability to send larger packets
- Limited VWire support for SMI and SCI, want IRQ and GPIO expansion ability
- 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 |
|---|---|---|
| 000 | 003 | Reserved |
| 004 | 007 | Device Identification |
| 008 | 00B | General Capabilities and Configurations |
| 00C | 00F | Reserved |
| 010 | 013 | Channel 0 Capabilities and Configurations |
| 014 | 01F | Reserved |
| 020 | 023 | Channel 1 Capabilities and Configurations |
| 024 | 02F | Reserved |
| 030 | 033 | Channel 2 Capabilities and Configurations |
| 034 | 03F | Reserved |
| 040 | 043 | Channel 3 Capabilities and Configurations |
| 044 | 047 | Channel 3 Capabilities and Configurations 2 |
| 048 | 04B | Channel 3 Capabilities and Configurations 3 |
| 04C | 04F | Channel 3 Capabilities and Configurations 4 |
| 050 | 7FF | Reserved |
| 800 | FFF | Platform 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
| Resource | Base Address | Length | Purpose |
|---|---|---|---|
| Channel-independent configuration | 0xFEDC0000 | 0x1000 | Read-only eSPI identification and channel capability/configuration registers. |
| Global Doorbell Register | 0xFEDC1000 | 0x4 | Doorbell status, allows 32-channels, bit 0-31 represents doorbell status for each channel. |
| Global Command Complete Register | 0xFEDC1004 | 0x4 | Command complete status for 32-channels, one bit per channel. |
| PCC Type 3 shared memory | 0xFEDC2000 | 0x400 | 1024-byte extended master subspace for OS-initiated bidirectional peripheral traffic. |
| PCC Type 4 shared memory | 0xFEDC2400 | 0x400 | 1024-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