blob: a55213c01904f8629fc09fcfa3d3cec86d608f5f [file]
# Copyright 2025 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Module for EC host command definitions."""
from enum import IntEnum
from command import HostCommand
class EcCommandResult(IntEnum):
"""EC command result codes."""
SUCCESS = 0
INVALID_COMMAND = 1
ERROR = 2
INVALID_PARAM = 3
ACCESS_DENIED = 4
INVALID_RESPONSE = 5
INVALID_VERSION = 6
INVALID_CHECKSUM = 7
IN_PROGRESS = 8
UNAVAILABLE = 9
TIMEOUT = 10
OVERFLOW = 11
INVALID_HEADER = 12
REQUEST_TRUNCATED = 13
RESPONSE_TOO_BIG = 14
BUS_ERROR = 15
BUSY = 16
INVALID_HEADER_VERSION = 17
INVALID_HEADER_CRC = 18
INVALID_DATA_CRC = 19
DUP_UNAVAILABLE = 20
UNKNOWN = -1
class ECCommandsIds(IntEnum):
"""IDs of supported EC commands"""
GET_VERSION = 0x0002
GET_VERSIONS = 0x0008
PROTOCOL_INFO = 0x000B
FLASH_INFO = 0x0010
FLASH_READ = 0x0011
FLASH_WRITE = 0x0012
FLASH_ERASE = 0x0013
FLASH_REGION_INFO = 0x0016
GET_NEXT_EVENT = 0x0067
REBOOT_EC = 0x00D2
ENTER_BOOTLOADER = 0x00E2
RWSIG_ACTION = 0x011D
FP_MODE = 0x0402
FP_INFO = 0x0403
FP_FRAME = 0x0404
FP_VENDOR = 0x040B
FP_ASCP_CLAIM = 0x0420
FP_ASCP_ESTABLISH = 0x0421
class ImageType(IntEnum):
"""EC Image type"""
RO = 1
RW = 2
@staticmethod
def get_image_name(image: int) -> str:
"""Returns the name of the image."""
if image == ImageType.RO:
return "RO"
if image == ImageType.RW:
return "RW"
return "Unknown"
class ECRebootCmd(IntEnum):
"""EC Reboot commands."""
CANCEL = 0
JUMP_RO = 1
JUMP_RW = 2
COLD = 4
DISABLE_JUMP = 5
HIBERNATE = 6
HIBERNATE_CLEAR_AP_OFF = 7
COLD_AP_OFF = 8
NO_OP = 9
def __str__(self) -> str:
return self.name
@staticmethod
def from_string(s) -> "ECRebootCmd":
"""Returns reboot command from a string."""
return ECRebootCmd[s]
class FlashRegion(IntEnum):
"""Flash regions."""
RO = 0
ACTIVE = 1
WP_RO = 2
UPDATE = 3
def __str__(self) -> str:
"""Returns the name of the region."""
return self.name
@staticmethod
def from_string(s) -> "FlashRegion":
"""Returns the region from a string."""
return FlashRegion[s]
class RwSigAction(IntEnum):
"""RwSig actions."""
ABORT = 0
CONTINUE = 1
def __str__(self) -> str:
"""Returns the name of the RwSig action."""
return self.name
@staticmethod
def from_string(s) -> "RwSigAction":
"""Returns the RwSigAction from a string."""
return RwSigAction[s]
class ECCommand(HostCommand):
"""Base class for EC Commands."""
def run(self, comm) -> EcCommandResult:
"""Runs the EC command."""
ret = super().run(comm)
try:
return EcCommandResult(ret)
except ValueError:
print(f"Unknown error code {ret} returned by EC.")
return EcCommandResult.UNKNOWN
class GetVersionCmd1(ECCommand):
"""Gets the version of the EC."""
def __init__(self):
# 32 bytes of RO string version
# 32 bytes of RW string version
# 32 bytes of fwid_ro
# 4 bytes of current image
# 32 bytes of fwid_rw
response_msg = [
("ro_ver", "32s"),
("rw_ver", "32s"),
("fwid_ro", "32s"),
("curr_image", "I"),
("fwid_rw", "32s"),
]
super().__init__(
ECCommandsIds.GET_VERSION, 1, response_msg=response_msg
)
class GetVersionsCmd1(ECCommand):
"""Gets the supported versions of a command."""
def __init__(self, command):
# 4 bytes of version mask
response_msg = [("version_mask", "I")]
# 2 bytes of cmd
request_msg = [(command, "H")]
super().__init__(
ECCommandsIds.GET_VERSIONS,
1,
response_msg=response_msg,
request_msg=request_msg,
)
class FlashInfoCmd2(ECCommand):
"""Gets flash information."""
def __init__(self, num_banks_desc: int):
# 4 bytes of flash_size
# 4 bytes of flags
# 4 bytes of write_ideal_size
# 2 bytes of num_banks_total
# 2 bytes of num_banks_desc
# num_banks_desc of ec_flash_bank struct
response_msg = [
("flash_size", "I"),
("flags", "I"),
("write_ideal_size", "I"),
("num_banks_total", "H"),
("num_banks_desc", "H"),
("ec_flash_banks", ""),
]
# 2 bytes of num_banks
# 2 bytes reserved
request_msg = [(num_banks_desc, "H"), (0, "H")]
# 2 bytes of count
# 1 byte of size_exp
# 1 byte of write_size_exp
# 1 byte of erase_size_exp
# 1 byte of protect_size_exp
# 2 bytes of reserved
ec_flash_bank = [
("count", "H"),
("size_exp", "B"),
("write_size_exp", "B"),
("erase_size_exp", "B"),
("protect_size_exp", "B"),
("reserved", "H"),
]
super().__init__(
ECCommandsIds.FLASH_INFO,
2,
response_msg=response_msg,
request_msg=request_msg,
variable_payload_msg=ec_flash_bank,
)
class ProtocolInfoCmd0(ECCommand):
"""Gets protocol information."""
def __init__(self):
# 4 bytes of protocol version
# 2 bytes of max_request_packet_size
# 2 bytes of max_response_packet_size
# 4 bytes of flags
response_msg = [
("protocol_versions", "I"),
("max_request_packet_size", "H"),
("max_response_packet_size", "H"),
("flags", "I"),
]
super().__init__(
ECCommandsIds.PROTOCOL_INFO, 0, response_msg=response_msg
)
class FpModeCmd0(ECCommand):
"""Sets the FP mode."""
def __init__(self, mode: int):
# 4 bytes of mode
response_msg = [("mode", "I")]
# 4 bytes of param1
request_msg = [(mode, "I")]
super().__init__(
0x0402, 0, response_msg=response_msg, request_msg=request_msg
)
class FpInfoCmd1(ECCommand):
"""Gets FP information (version 1)."""
def __init__(self):
# 4 bytes of vendor id
# 4 bytes of product id
# 4 bytes of model id
# 4 bytes of version
# 4 bytes of frame size
# 4 bytes of pixel format
# 2 bytes of width
# 2 bytes of height
# 2 bytes of bpp
# 2 bytes of errors
# 4 bytes of template size
# 2 bytes of template max
# 2 bytes of template valid
# 4 bytes of template dirty
# 4 bytes of template version
response_msg = [
("vendor_id", "I"),
("product_id", "I"),
("model_id", "I"),
("version", "I"),
("frame_size", "I"),
("pixel_format", "I"),
("width", "H"),
("height", "H"),
("bpp", "H"),
("errors", "H"),
("template_size", "I"),
("template_max", "H"),
("template_valid", "H"),
("template_dirty", "I"),
("template_version", "I"),
]
super().__init__(ECCommandsIds.FP_INFO, 1, response_msg=response_msg)
class FpInfoCmd2(ECCommand):
"""Gets FP information (version 2)."""
def __init__(self):
# 4 bytes of vendor id
# 4 bytes of product id
# 4 bytes of model id
# 4 bytes of version
# 2 bytes of num of capture types
# 2 bytes of errors
# 4 bytes of template size
# 2 bytes of template max
# 2 bytes of template valid
# 4 bytes of template dirty
# 4 bytes of template version
# unknown number of image_frame
response_msg = [
("vendor_id", "I"),
("product_id", "I"),
("model_id", "I"),
("version", "I"),
("num_capture_types", "H"),
("errors", "H"),
("template_size", "I"),
("template_max", "H"),
("template_valid", "H"),
("template_dirty", "I"),
("template_version", "I"),
("image_frame_params", ""),
]
# fp_image_frame_params
# 4 bytes of frame_size;
# 4 bytes of pixel_format;
# 2 bytes of width;
# 2 bytes of height;
# 2 bytes of bpp;
# 1 byte of fp_capture_type;
# 1 byte of reserved;
image_frame = [
("frame_size", "I"),
("pixel_format", "I"),
("width", "H"),
("height", "H"),
("bpp", "H"),
("fp_capture_type", "B"),
("reserved", "B"),
]
super().__init__(
ECCommandsIds.FP_INFO,
2,
response_msg=response_msg,
variable_payload_msg=image_frame,
)
class FpInfoCmd3(ECCommand):
"""Gets FP information (version 3)."""
def __init__(self):
# 4 bytes of vendor id
# 4 bytes of product id
# 4 bytes of model id
# 4 bytes of version
# 2 bytes of num of capture types
# 2 bytes of errors
# 4 bytes of template size
# 2 bytes of template max
# 2 bytes of template valid
# 4 bytes of template dirty
# 4 bytes of template version
# unknown number of image_frame
response_msg = [
("vendor_id", "I"),
("product_id", "I"),
("model_id", "I"),
("version", "I"),
("num_capture_types", "H"),
("errors", "H"),
("template_size", "I"),
("template_max", "H"),
("template_valid", "H"),
("template_dirty", "I"),
("template_version", "I"),
("image_frame_params", ""),
]
# fp_image_frame_params
# 4 bytes of frame_size;
# 4 bytes of image offset;
# 4 bytes of pixel_format;
# 2 bytes of width;
# 2 bytes of height;
# 2 bytes of bpp;
# 1 byte of fp_capture_type;
# 1 byte of reserved;
image_frame = [
("frame_size", "I"),
("image_data_offset_bytes", "I"),
("pixel_format", "I"),
("width", "H"),
("height", "H"),
("bpp", "H"),
("fp_capture_type", "B"),
("reserved", "B"),
]
super().__init__(
ECCommandsIds.FP_INFO,
3,
response_msg=response_msg,
variable_payload_msg=image_frame,
)
class FpFrameCmd0(ECCommand):
"""Gets FP frame (version 0)."""
def __init__(self, idx: int, offset: int, size: int):
# Variable number of bytes in response
response_msg = [("data", "")]
# 4 bytes of offset with index
# 4 bytes of size
idx_off = ((idx << 28) | (offset & 0x0FFFFFFF)) & 0xFFFFFFFF
request_msg = [
(idx_off, "I"),
(size, "I"),
]
super().__init__(
ECCommandsIds.FP_FRAME,
0,
response_msg=response_msg,
request_msg=request_msg,
)
class FpFrameCmd1(ECCommand):
"""Gets FP frame (version 1)."""
def __init__(self, cmd: int, idx: int, offset: int, size: int):
# Variable number of bytes in response
response_msg = [("data", "")]
# 4 bytes of offset with index
# 4 bytes of size
request_msg = [
(cmd, "B"),
(0, "B"),
(idx, "H"),
(offset, "I"),
(size, "I"),
]
super().__init__(
ECCommandsIds.FP_FRAME,
1,
response_msg=response_msg,
request_msg=request_msg,
)
class FpVendorCmd0(ECCommand):
"""FP vendor command."""
def __init__(self, param1: int):
# Variable number of bytes in response
response_msg = [("payload", "")]
# 4 bytes of param1
request_msg = [(param1, "I")]
super().__init__(
ECCommandsIds.FP_VENDOR,
0,
response_msg=response_msg,
request_msg=request_msg,
)
class EnterBootloaderCmd0(ECCommand):
"""Enters the bootloader."""
def __init__(self):
super().__init__(ECCommandsIds.ENTER_BOOTLOADER, 0)
class FlashReadCmd0(ECCommand):
"""Reads from flash."""
def __init__(self, offset: int, size: int):
# "size" bytes of data
response_msg = [("data", f"{size}s")]
# 4 bytes of offset
# 4 bytes of size
request_msg = [(offset, "I"), (size, "I")]
super().__init__(
ECCommandsIds.FLASH_READ,
0,
response_msg=response_msg,
request_msg=request_msg,
)
class FlashWriteCmd0(ECCommand):
"""Writes to flash."""
def __init__(self, offset: int, data: bytes):
size = len(data)
# 4 bytes of offset
# 4 bytes of size
request_msg = [(offset, "I"), (size, "I"), (data, f"{size}s")]
super().__init__(ECCommandsIds.FLASH_WRITE, 0, request_msg=request_msg)
class FlashEraseCmd0(ECCommand):
"""Erases flash."""
def __init__(self, offset: int, size: int):
# 4 bytes of offset
# 4 bytes of size
request_msg = [(offset, "I"), (size, "I")]
super().__init__(ECCommandsIds.FLASH_ERASE, 0, request_msg=request_msg)
class FlashRegionInfoCmd1(ECCommand):
"""Gets flash region information."""
def __init__(self, region: int):
# 4 bytes of offset
# 4 bytes of size
response_msg = [("offset", "I"), ("size", "I")]
# 4 bytes of region
request_msg = [(region, "I")]
super().__init__(
ECCommandsIds.FLASH_REGION_INFO,
1,
response_msg=response_msg,
request_msg=request_msg,
)
class GetNextEventCmd2(ECCommand):
"""Gets next MKBP event."""
def __init__(self):
# 1 byte of event_type
# 16 bytes of event_data
response_msg = [("event_type", "B"), ("event_data", "16s")]
super().__init__(
ECCommandsIds.GET_NEXT_EVENT, 2, response_msg=response_msg
)
class GetNextEventCmd3(ECCommand):
"""Gets next MKBP event."""
def __init__(self):
# 1 byte of event_type
# Up to 18 bytes of event_data
response_msg = [("event_type", "B"), ("event_data", "")]
super().__init__(
ECCommandsIds.GET_NEXT_EVENT, 3, response_msg=response_msg
)
class RebootECCmd0(ECCommand):
"""Reboots the EC."""
def __init__(self, cmd: int, flags: int = 0):
# 1 byte of command
# 1 byte of flags
request_msg = [(cmd, "B"), (flags, "B")]
super().__init__(ECCommandsIds.REBOOT_EC, 0, request_msg=request_msg)
class RwSigActionCmd0(ECCommand):
"""RWSIG action."""
def __init__(self, action: int):
# 4 bytes of action
request_msg = [(action, "I")]
super().__init__(ECCommandsIds.RWSIG_ACTION, 0, request_msg=request_msg)
class FpAscpClaimCmd0(ECCommand):
"""Gets ASCP claim."""
def __init__(self):
response_msg = [
("pk_m", "65s"),
("s_goog", "64s"),
("pk_d", "65s"),
("s_m", "64s"),
("pk_f", "65s"),
("h_f", "32s"),
("s_d", "64s"),
]
super().__init__(
ECCommandsIds.FP_ASCP_CLAIM, 0, response_msg=response_msg
)
class FpAscpEstablishCmd0(ECCommand):
"""Establishes ASCP session."""
def __init__(self, pk_g: bytearray):
request_msg = [(pk_g, "65s")]
super().__init__(
ECCommandsIds.FP_ASCP_ESTABLISH, 0, request_msg=request_msg
)
VERSIONED_COMMANDS = {
ECCommandsIds.GET_VERSION: {1: GetVersionCmd1},
ECCommandsIds.GET_VERSIONS: {1: GetVersionsCmd1},
ECCommandsIds.PROTOCOL_INFO: {0: ProtocolInfoCmd0},
ECCommandsIds.FLASH_INFO: {2: FlashInfoCmd2},
ECCommandsIds.FLASH_READ: {0: FlashReadCmd0},
ECCommandsIds.FLASH_WRITE: {0: FlashWriteCmd0},
ECCommandsIds.FLASH_ERASE: {0: FlashEraseCmd0},
ECCommandsIds.FLASH_REGION_INFO: {1: FlashRegionInfoCmd1},
ECCommandsIds.GET_NEXT_EVENT: {2: GetNextEventCmd2, 3: GetNextEventCmd3},
ECCommandsIds.REBOOT_EC: {0: RebootECCmd0},
ECCommandsIds.ENTER_BOOTLOADER: {0: EnterBootloaderCmd0},
ECCommandsIds.FP_MODE: {0: FpModeCmd0},
ECCommandsIds.FP_INFO: {1: FpInfoCmd1, 2: FpInfoCmd2, 3: FpInfoCmd3},
ECCommandsIds.FP_FRAME: {0: FpFrameCmd0, 1: FpFrameCmd1},
ECCommandsIds.FP_VENDOR: {0: FpVendorCmd0},
ECCommandsIds.RWSIG_ACTION: {0: RwSigActionCmd0},
ECCommandsIds.FP_ASCP_CLAIM: {0: FpAscpClaimCmd0},
ECCommandsIds.FP_ASCP_ESTABLISH: {0: FpAscpEstablishCmd0},
}
def get_versions(command_id, comm) -> list[int]:
"""Returns a list of supported versions for a given command."""
get_ver = GetVersionsCmd1(command_id)
ret = get_ver.run(comm)
if ret != EcCommandResult.SUCCESS:
return []
versions = []
version_mask = get_ver.response.version_mask
i = 0
while version_mask > 0:
if version_mask & 1:
versions.append(i)
version_mask >>= 1
i += 1
return versions
def get_cmd(command_id: ECCommandsIds, comm) -> HostCommand:
"""Returns the highest supported command by both the device and the library."""
versions = get_versions(command_id, comm)
if len(versions) > 0:
for version in sorted(versions, reverse=True):
if version in VERSIONED_COMMANDS[command_id]:
return VERSIONED_COMMANDS[command_id][version]
return None