cros_ec_python.devices.win_fw_ec
1from typing import Final 2import warnings 3import os 4import ctypes 5from ctypes import wintypes 6 7# Workaround for pdoc failing on Linux 8if os.name == 'nt': 9 from ctypes import windll 10 11__all__ = ["WinFrameworkEc"] 12 13from ..baseclass import CrosEcClass 14from ..constants.COMMON import * 15from ..constants.MEMMAP import EC_MEMMAP_SIZE 16from ..exceptions import ECError 17 18FILE_GENERIC_READ: Final = 0x80000000 19FILE_GENERIC_WRITE: Final = 0x40000000 20FILE_SHARE_READ: Final = 0x00000001 21FILE_SHARE_WRITE: Final = 0x00000002 22OPEN_EXISTING: Final = 3 23 24FILE_READ_DATA: Final = 0x0001 25FILE_WRITE_DATA: Final = 0x0002 26FILE_READ_ACCESS: Final = 0x0001 27METHOD_BUFFERED: Final = 0 28 29INVALID_HANDLE_VALUE: Final = -1 30 31 32def CTL_CODE(device_type, function, method, access) -> int: 33 return (device_type << 16) | (access << 14) | (function << 2) | method 34 35 36CROSEC_CMD_MAX_REQUEST = 0x100 37 38FILE_DEVICE_CROS_EMBEDDED_CONTROLLER = 0x80EC 39 40IOCTL_CROSEC_XCMD = CTL_CODE( 41 FILE_DEVICE_CROS_EMBEDDED_CONTROLLER, 42 0x801, 43 METHOD_BUFFERED, 44 FILE_READ_DATA | FILE_WRITE_DATA, 45) 46IOCTL_CROSEC_RDMEM = CTL_CODE( 47 FILE_DEVICE_CROS_EMBEDDED_CONTROLLER, 48 0x802, 49 METHOD_BUFFERED, 50 FILE_READ_ACCESS, 51) 52 53 54def CreateFileW( 55 filename: str, access: int, mode: int, creation: int, flags: int 56) -> wintypes.HANDLE: 57 knl32_CreateFileW = windll.kernel32.CreateFileW 58 knl32_CreateFileW.argtypes = [ 59 wintypes.LPCWSTR, # lpFileName 60 wintypes.DWORD, # dwDesiredAccess 61 wintypes.DWORD, # dwShareMode 62 wintypes.LPVOID, # lpSecurityAttributes 63 wintypes.DWORD, # dwCreationDisposition 64 wintypes.DWORD, # dwFlagsAndAttributes 65 wintypes.HANDLE, # hTemplateFile 66 ] 67 knl32_CreateFileW.restype = wintypes.HANDLE 68 69 return wintypes.HANDLE( 70 knl32_CreateFileW(filename, access, mode, 0, creation, flags, 0) 71 ) 72 73 74def DeviceIoControl( 75 handle: wintypes.HANDLE, 76 ioctl: int, 77 inbuf: ctypes.pointer, 78 insize: int, 79 outbuf: ctypes.pointer, 80 outsize: int, 81) -> bool: 82 knl32_DeviceIoControl = windll.kernel32.DeviceIoControl 83 knl32_DeviceIoControl.argtypes = [ 84 wintypes.HANDLE, # hDevice 85 wintypes.DWORD, # dwIoControlCode 86 wintypes.LPVOID, # lpInBuffer 87 wintypes.DWORD, # nInBufferSize 88 wintypes.LPVOID, # lpOutBuffer 89 wintypes.DWORD, # nOutBufferSize 90 wintypes.LPDWORD, # lpBytesReturned 91 wintypes.LPVOID, # lpOverlapped 92 ] 93 knl32_DeviceIoControl.restype = wintypes.BOOL 94 95 status = knl32_DeviceIoControl( 96 handle, ioctl, inbuf, insize, outbuf, outsize, None, None 97 ) 98 99 return bool(status) 100 101 102class WinFrameworkEc(CrosEcClass): 103 """ 104 Class to interact with the EC using the Framework EC Windows Driver. 105 """ 106 107 def __init__(self, handle: wintypes.HANDLE | None = None): 108 """ 109 Initialise communication with the Framework EC driver. 110 :param handle: Use a custom device handle, opens one by default. 111 """ 112 if handle is None: 113 handle = CreateFileW( 114 r"\\.\GLOBALROOT\Device\CrosEC", 115 FILE_GENERIC_READ | FILE_GENERIC_WRITE, 116 FILE_SHARE_READ | FILE_SHARE_WRITE, 117 OPEN_EXISTING, 118 0, 119 ) 120 121 if handle.value == wintypes.HANDLE(INVALID_HANDLE_VALUE).value: 122 errno = windll.kernel32.GetLastError() 123 raise OSError(f"{errno}: {ctypes.FormatError(errno)}") 124 125 self.handle: wintypes.HANDLE = handle 126 r"""The handle for \\.\GLOBALROOT\Device\CrosEC.""" 127 128 def __del__(self): 129 self.ec_exit() 130 131 @staticmethod 132 def detect() -> bool: 133 r""" 134 Checks for `\\.\GLOBALROOT\Device\CrosEC` and returns True if it exists. 135 """ 136 return os.path.exists(r"\\.\GLOBALROOT\Device\CrosEC") 137 138 def ec_init(self) -> None: 139 pass 140 141 def ec_exit(self) -> None: 142 """ 143 Close the file on exit. 144 """ 145 if hasattr(self, "handle"): 146 windll.kernel32.CloseHandle(self.handle) 147 148 def command( 149 self, 150 version: Int32, 151 command: Int32, 152 outsize: Int32, 153 insize: Int32, 154 data: bytes = None, 155 warn: bool = True, 156 ) -> bytes: 157 """ 158 Send a command to the EC and return the response. 159 :param version: Command version number (often 0). 160 :param command: Command to send (EC_CMD_...). 161 :param outsize: Outgoing length in bytes. 162 :param insize: Max number of bytes to accept from the EC. 163 :param data: Outgoing data to EC. 164 :param warn: Whether to warn if the response size is not as expected. Default is True. 165 :return: Incoming data from EC. 166 """ 167 if data is None: 168 data = bytes(outsize) 169 170 class CrosEcCommand(ctypes.Structure): 171 _fields_ = [ 172 ("version", ctypes.c_uint32), 173 ("command", ctypes.c_uint32), 174 ("outsize", ctypes.c_uint32), 175 ("insize", ctypes.c_uint32), 176 ("result", ctypes.c_uint32), 177 ("buffer", ctypes.c_ubyte * (CROSEC_CMD_MAX_REQUEST - (4 * 5))), 178 ] 179 180 cmd = CrosEcCommand() 181 cmd.version = version 182 cmd.command = command 183 cmd.outsize = outsize 184 cmd.insize = insize 185 cmd.result = 0xFF 186 ctypes.memmove(ctypes.addressof(cmd.buffer), data, len(data)) 187 p_cmd = ctypes.pointer(cmd) 188 189 status = DeviceIoControl( 190 self.handle, 191 IOCTL_CROSEC_XCMD, 192 p_cmd, 193 ctypes.sizeof(CrosEcCommand), 194 p_cmd, 195 ctypes.sizeof(CrosEcCommand), 196 ) 197 if not status: 198 errno = windll.kernel32.GetLastError() 199 raise IOError( 200 f"ioctl failed with error {errno}: {ctypes.FormatError(errno)}" 201 ) 202 203 if cmd.insize != insize: 204 warnings.warn( 205 f"Expected {insize} bytes, got {cmd.insize} back from EC", 206 RuntimeWarning, 207 ) 208 209 if cmd.result != 0: 210 raise ECError(cmd.result) 211 212 return bytes(cmd.buffer[:insize]) 213 214 def memmap(self, offset: Int32, num_bytes: Int32) -> bytes: 215 """ 216 Read memory from the EC. 217 :param offset: Offset to read from. 218 :param num_bytes: Number of bytes to read. 219 :return: Bytes read from the EC. 220 """ 221 222 class CrosEcReadMem(ctypes.Structure): 223 _fields_ = [ 224 ("offset", ctypes.c_uint32), 225 ("bytes", ctypes.c_uint32), 226 ("buffer", ctypes.c_ubyte * EC_MEMMAP_SIZE), 227 ] 228 229 data = CrosEcReadMem() 230 data.offset = offset 231 data.bytes = num_bytes 232 p_data = ctypes.pointer(data) 233 234 status = DeviceIoControl( 235 self.handle, 236 IOCTL_CROSEC_RDMEM, 237 p_data, 238 ctypes.sizeof(CrosEcReadMem), 239 p_data, 240 ctypes.sizeof(CrosEcReadMem), 241 ) 242 243 if not status: 244 errno = windll.kernel32.GetLastError() 245 raise IOError( 246 f"ioctl failed with error {errno}: {ctypes.FormatError(errno)}" 247 ) 248 249 if data.bytes != num_bytes: 250 warnings.warn( 251 f"Expected {num_bytes} bytes, got {data.bytes} back from EC", 252 RuntimeWarning, 253 ) 254 255 return bytes(data.buffer[:num_bytes])
103class WinFrameworkEc(CrosEcClass): 104 """ 105 Class to interact with the EC using the Framework EC Windows Driver. 106 """ 107 108 def __init__(self, handle: wintypes.HANDLE | None = None): 109 """ 110 Initialise communication with the Framework EC driver. 111 :param handle: Use a custom device handle, opens one by default. 112 """ 113 if handle is None: 114 handle = CreateFileW( 115 r"\\.\GLOBALROOT\Device\CrosEC", 116 FILE_GENERIC_READ | FILE_GENERIC_WRITE, 117 FILE_SHARE_READ | FILE_SHARE_WRITE, 118 OPEN_EXISTING, 119 0, 120 ) 121 122 if handle.value == wintypes.HANDLE(INVALID_HANDLE_VALUE).value: 123 errno = windll.kernel32.GetLastError() 124 raise OSError(f"{errno}: {ctypes.FormatError(errno)}") 125 126 self.handle: wintypes.HANDLE = handle 127 r"""The handle for \\.\GLOBALROOT\Device\CrosEC.""" 128 129 def __del__(self): 130 self.ec_exit() 131 132 @staticmethod 133 def detect() -> bool: 134 r""" 135 Checks for `\\.\GLOBALROOT\Device\CrosEC` and returns True if it exists. 136 """ 137 return os.path.exists(r"\\.\GLOBALROOT\Device\CrosEC") 138 139 def ec_init(self) -> None: 140 pass 141 142 def ec_exit(self) -> None: 143 """ 144 Close the file on exit. 145 """ 146 if hasattr(self, "handle"): 147 windll.kernel32.CloseHandle(self.handle) 148 149 def command( 150 self, 151 version: Int32, 152 command: Int32, 153 outsize: Int32, 154 insize: Int32, 155 data: bytes = None, 156 warn: bool = True, 157 ) -> bytes: 158 """ 159 Send a command to the EC and return the response. 160 :param version: Command version number (often 0). 161 :param command: Command to send (EC_CMD_...). 162 :param outsize: Outgoing length in bytes. 163 :param insize: Max number of bytes to accept from the EC. 164 :param data: Outgoing data to EC. 165 :param warn: Whether to warn if the response size is not as expected. Default is True. 166 :return: Incoming data from EC. 167 """ 168 if data is None: 169 data = bytes(outsize) 170 171 class CrosEcCommand(ctypes.Structure): 172 _fields_ = [ 173 ("version", ctypes.c_uint32), 174 ("command", ctypes.c_uint32), 175 ("outsize", ctypes.c_uint32), 176 ("insize", ctypes.c_uint32), 177 ("result", ctypes.c_uint32), 178 ("buffer", ctypes.c_ubyte * (CROSEC_CMD_MAX_REQUEST - (4 * 5))), 179 ] 180 181 cmd = CrosEcCommand() 182 cmd.version = version 183 cmd.command = command 184 cmd.outsize = outsize 185 cmd.insize = insize 186 cmd.result = 0xFF 187 ctypes.memmove(ctypes.addressof(cmd.buffer), data, len(data)) 188 p_cmd = ctypes.pointer(cmd) 189 190 status = DeviceIoControl( 191 self.handle, 192 IOCTL_CROSEC_XCMD, 193 p_cmd, 194 ctypes.sizeof(CrosEcCommand), 195 p_cmd, 196 ctypes.sizeof(CrosEcCommand), 197 ) 198 if not status: 199 errno = windll.kernel32.GetLastError() 200 raise IOError( 201 f"ioctl failed with error {errno}: {ctypes.FormatError(errno)}" 202 ) 203 204 if cmd.insize != insize: 205 warnings.warn( 206 f"Expected {insize} bytes, got {cmd.insize} back from EC", 207 RuntimeWarning, 208 ) 209 210 if cmd.result != 0: 211 raise ECError(cmd.result) 212 213 return bytes(cmd.buffer[:insize]) 214 215 def memmap(self, offset: Int32, num_bytes: Int32) -> bytes: 216 """ 217 Read memory from the EC. 218 :param offset: Offset to read from. 219 :param num_bytes: Number of bytes to read. 220 :return: Bytes read from the EC. 221 """ 222 223 class CrosEcReadMem(ctypes.Structure): 224 _fields_ = [ 225 ("offset", ctypes.c_uint32), 226 ("bytes", ctypes.c_uint32), 227 ("buffer", ctypes.c_ubyte * EC_MEMMAP_SIZE), 228 ] 229 230 data = CrosEcReadMem() 231 data.offset = offset 232 data.bytes = num_bytes 233 p_data = ctypes.pointer(data) 234 235 status = DeviceIoControl( 236 self.handle, 237 IOCTL_CROSEC_RDMEM, 238 p_data, 239 ctypes.sizeof(CrosEcReadMem), 240 p_data, 241 ctypes.sizeof(CrosEcReadMem), 242 ) 243 244 if not status: 245 errno = windll.kernel32.GetLastError() 246 raise IOError( 247 f"ioctl failed with error {errno}: {ctypes.FormatError(errno)}" 248 ) 249 250 if data.bytes != num_bytes: 251 warnings.warn( 252 f"Expected {num_bytes} bytes, got {data.bytes} back from EC", 253 RuntimeWarning, 254 ) 255 256 return bytes(data.buffer[:num_bytes])
Class to interact with the EC using the Framework EC Windows Driver.
WinFrameworkEc(handle: ctypes.c_void_p | None = None)
108 def __init__(self, handle: wintypes.HANDLE | None = None): 109 """ 110 Initialise communication with the Framework EC driver. 111 :param handle: Use a custom device handle, opens one by default. 112 """ 113 if handle is None: 114 handle = CreateFileW( 115 r"\\.\GLOBALROOT\Device\CrosEC", 116 FILE_GENERIC_READ | FILE_GENERIC_WRITE, 117 FILE_SHARE_READ | FILE_SHARE_WRITE, 118 OPEN_EXISTING, 119 0, 120 ) 121 122 if handle.value == wintypes.HANDLE(INVALID_HANDLE_VALUE).value: 123 errno = windll.kernel32.GetLastError() 124 raise OSError(f"{errno}: {ctypes.FormatError(errno)}") 125 126 self.handle: wintypes.HANDLE = handle 127 r"""The handle for \\.\GLOBALROOT\Device\CrosEC."""
Initialise communication with the Framework EC driver.
Parameters
- handle: Use a custom device handle, opens one by default.
@staticmethod
def
detect() -> bool:
132 @staticmethod 133 def detect() -> bool: 134 r""" 135 Checks for `\\.\GLOBALROOT\Device\CrosEC` and returns True if it exists. 136 """ 137 return os.path.exists(r"\\.\GLOBALROOT\Device\CrosEC")
Checks for \\.\GLOBALROOT\Device\CrosEC and returns True if it exists.
def
ec_init(self) -> None:
Initialise the EC, this shouldn't need to be called unless the keyword arg init was set to false.
def
ec_exit(self) -> None:
142 def ec_exit(self) -> None: 143 """ 144 Close the file on exit. 145 """ 146 if hasattr(self, "handle"): 147 windll.kernel32.CloseHandle(self.handle)
Close the file on exit.
def
command( self, version: int, command: int, outsize: int, insize: int, data: bytes = None, warn: bool = True) -> bytes:
149 def command( 150 self, 151 version: Int32, 152 command: Int32, 153 outsize: Int32, 154 insize: Int32, 155 data: bytes = None, 156 warn: bool = True, 157 ) -> bytes: 158 """ 159 Send a command to the EC and return the response. 160 :param version: Command version number (often 0). 161 :param command: Command to send (EC_CMD_...). 162 :param outsize: Outgoing length in bytes. 163 :param insize: Max number of bytes to accept from the EC. 164 :param data: Outgoing data to EC. 165 :param warn: Whether to warn if the response size is not as expected. Default is True. 166 :return: Incoming data from EC. 167 """ 168 if data is None: 169 data = bytes(outsize) 170 171 class CrosEcCommand(ctypes.Structure): 172 _fields_ = [ 173 ("version", ctypes.c_uint32), 174 ("command", ctypes.c_uint32), 175 ("outsize", ctypes.c_uint32), 176 ("insize", ctypes.c_uint32), 177 ("result", ctypes.c_uint32), 178 ("buffer", ctypes.c_ubyte * (CROSEC_CMD_MAX_REQUEST - (4 * 5))), 179 ] 180 181 cmd = CrosEcCommand() 182 cmd.version = version 183 cmd.command = command 184 cmd.outsize = outsize 185 cmd.insize = insize 186 cmd.result = 0xFF 187 ctypes.memmove(ctypes.addressof(cmd.buffer), data, len(data)) 188 p_cmd = ctypes.pointer(cmd) 189 190 status = DeviceIoControl( 191 self.handle, 192 IOCTL_CROSEC_XCMD, 193 p_cmd, 194 ctypes.sizeof(CrosEcCommand), 195 p_cmd, 196 ctypes.sizeof(CrosEcCommand), 197 ) 198 if not status: 199 errno = windll.kernel32.GetLastError() 200 raise IOError( 201 f"ioctl failed with error {errno}: {ctypes.FormatError(errno)}" 202 ) 203 204 if cmd.insize != insize: 205 warnings.warn( 206 f"Expected {insize} bytes, got {cmd.insize} back from EC", 207 RuntimeWarning, 208 ) 209 210 if cmd.result != 0: 211 raise ECError(cmd.result) 212 213 return bytes(cmd.buffer[:insize])
Send a command to the EC and return the response.
Parameters
- version: Command version number (often 0).
- command: Command to send (EC_CMD_...).
- outsize: Outgoing length in bytes.
- insize: Max number of bytes to accept from the EC.
- data: Outgoing data to EC.
- warn: Whether to warn if the response size is not as expected. Default is True.
Returns
Incoming data from EC.
def
memmap(self, offset: int, num_bytes: int) -> bytes:
215 def memmap(self, offset: Int32, num_bytes: Int32) -> bytes: 216 """ 217 Read memory from the EC. 218 :param offset: Offset to read from. 219 :param num_bytes: Number of bytes to read. 220 :return: Bytes read from the EC. 221 """ 222 223 class CrosEcReadMem(ctypes.Structure): 224 _fields_ = [ 225 ("offset", ctypes.c_uint32), 226 ("bytes", ctypes.c_uint32), 227 ("buffer", ctypes.c_ubyte * EC_MEMMAP_SIZE), 228 ] 229 230 data = CrosEcReadMem() 231 data.offset = offset 232 data.bytes = num_bytes 233 p_data = ctypes.pointer(data) 234 235 status = DeviceIoControl( 236 self.handle, 237 IOCTL_CROSEC_RDMEM, 238 p_data, 239 ctypes.sizeof(CrosEcReadMem), 240 p_data, 241 ctypes.sizeof(CrosEcReadMem), 242 ) 243 244 if not status: 245 errno = windll.kernel32.GetLastError() 246 raise IOError( 247 f"ioctl failed with error {errno}: {ctypes.FormatError(errno)}" 248 ) 249 250 if data.bytes != num_bytes: 251 warnings.warn( 252 f"Expected {num_bytes} bytes, got {data.bytes} back from EC", 253 RuntimeWarning, 254 ) 255 256 return bytes(data.buffer[:num_bytes])
Read memory from the EC.
Parameters
- offset: Offset to read from.
- num_bytes: Number of bytes to read.
Returns
Bytes read from the EC.