diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b7370c..aa10479 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +## [5.0.0] + +**Note:** Backward incompatible API change: + The HECI_DEVICE_KIND_* enums and kind field in struct tee_device_address + are renamed to HECI_HW_TYPE_* and hw_type field to avoid clash with + device kind value provided by OS drivers. + +### Changed + - EFI: Decrease HW register polling timeout + - EFI: Refactor HECI_DEVICE_KIND + - Linux: pull libmei 1.6.4 + +### Fixed + - EFI: Change propertyMap array type to CHAR8* + - EFI: Fixing compilation errors with GCC + - account for old Windows driver + +### Added + - CMake: Windows: add release 64 static preset + - Add cpp wrapper + - TeeGetKind API + ## [4.3.1] ### Fixed diff --git a/CMakePresets.json b/CMakePresets.json index 52d0a1a..7239d7c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -33,7 +33,7 @@ }, { "name": "Debug32AllStatic", - "displayName": "Windows x86 Debug All", + "displayName": "Windows x86 Debug Static All", "description": "Build x86 Debug, VS2019, including test and samples", "inherits": "base32", "binaryDir": "${sourceDir}/Debug", @@ -46,7 +46,7 @@ }, { "name": "Release32AllStatic", - "displayName": "Windows x86 Release All", + "displayName": "Windows x86 Release Static All", "description": "Build x86 Release, VS2019, including test and samples", "inherits": "base32", "binaryDir": "${sourceDir}/Release", @@ -59,7 +59,7 @@ }, { "name": "Release32Static", - "displayName": "Windows x86 Release", + "displayName": "Windows x86 Release Static", "description": "Build x86 Release, VS2019", "inherits": "base32", "binaryDir": "${sourceDir}/Release", @@ -88,6 +88,17 @@ "CMAKE_BUILD_TYPE": "Release" } }, + { + "name": "Release64Static", + "displayName": "Windows x86-64 Release Static", + "description": "Build x86-64 Release, VS2019", + "inherits": "base64", + "binaryDir": "${sourceDir}/Release", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "BUILD_SHARED_LIBS": "NO" + } + }, { "name": "Debug64All", "displayName": "Windows x86-64 Debug All", @@ -127,6 +138,11 @@ "configurePreset": "Release64", "configuration": "Release" }, + { + "name": "Release64Static", + "configurePreset": "Release64Static", + "configuration": "Release" + }, { "name": "Debug64All", "configurePreset": "Debug64All", diff --git a/VERSION b/VERSION index f77856a..0062ac9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.3.1 +5.0.0 diff --git a/conanfile.py b/conanfile.py index 48f483d..09cb002 100644 --- a/conanfile.py +++ b/conanfile.py @@ -46,4 +46,4 @@ def package(self): cmake.install() def package_info(self): - self.cpp_info.libs = ["metee"] + self.cpp_info.components["libmetee"].libs = ["metee"] diff --git a/include/Doxyfile.in b/include/Doxyfile.in index 32bd38f..89a8718 100644 --- a/include/Doxyfile.in +++ b/include/Doxyfile.in @@ -936,7 +936,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = @DOXYGEN_INPUT_DIRECTORY@/include/metee.h +INPUT = @DOXYGEN_INPUT_DIRECTORY@/include/metee.h @DOXYGEN_INPUT_DIRECTORY@/include/meteepp.h # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/include/metee.h b/include/metee.h index 43bea22..49df6ef 100644 --- a/include/metee.h +++ b/include/metee.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: Apache-2.0 */ /* - * Copyright (C) 2014-2024 Intel Corporation + * Copyright (C) 2014-2025 Intel Corporation */ /*! \file metee.h * \brief metee library API @@ -138,12 +138,12 @@ struct tee_device_address { uint32_t device; /** HECI device Device */ uint32_t function; /** HECI device Function */ } value; - /*! Specify HW layout: HECI, FWSTS registers locations */ - enum { - HECI_DEVICE_KIND_PCH, - HECI_DEVICE_KIND_GFX_GSC, - HECI_DEVICE_KIND_GFX_CSC, - } kind; /** HECI device kind */ + /*! Specifies HW layout: HECI, FWSTS registers locations */ + enum HECI_HW_TYPE { + HECI_HW_TYPE_PCH, + HECI_HW_TYPE_GFX_GSC, + HECI_HW_TYPE_GFX_CSC, + } hw_type; /** HECI device HW type */ } bdf; } data; }; @@ -347,6 +347,16 @@ uint32_t TEEAPI TeeGetMaxMsgLen(IN const PTEEHANDLE handle); */ uint8_t TEEAPI TeeGetProtocolVer(IN const PTEEHANDLE handle); + +/*! Obtains kind of the TEE device + * \param handle The handle of the session. + * \param kind Buffer to fill with device kind null terminated string, may be NULL. + * \param kindSize Pointer to kind buffer size in bytes, updated to number of bytes filled in buffer, including null character, on out. + * If buffer is NULL, required size is returned anyway. + * \return 0 if successful, otherwise error code. + */ +TEESTATUS TEEAPI TeeGetKind(IN PTEEHANDLE handle, IN OUT char *kind, IN OUT size_t *kindSize); + #ifdef __cplusplus } #endif diff --git a/include/meteepp.h b/include/meteepp.h new file mode 100644 index 0000000..383ce57 --- /dev/null +++ b/include/meteepp.h @@ -0,0 +1,337 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * Copyright (C) 2021-2025 Intel Corporation + */ +/*! \file meteepp.h + \brief metee C++ library API + */ +#ifndef _METEEPP_H_ +#define _METEEPP_H_ + +#include +#include +#include +#include +#include +#include "metee.h" + +namespace intel { + namespace security { + + /*! \def TEE_ERR_STATE(state) + * Internal macro + */ + + /*! MeTee error category class */ + static class metee_category_t : public std::error_category { + public: + virtual const char* name() const noexcept { return "MeTee"; } + virtual std::string message(int ev) const { +#define TEE_ERR_STATE(state) case TEE_##state: return #state + switch (ev) { + TEE_ERR_STATE(SUCCESS); + TEE_ERR_STATE(INTERNAL_ERROR); + TEE_ERR_STATE(DEVICE_NOT_FOUND); + TEE_ERR_STATE(DEVICE_NOT_READY); + TEE_ERR_STATE(INVALID_PARAMETER); + TEE_ERR_STATE(UNABLE_TO_COMPLETE_OPERATION); + TEE_ERR_STATE(TIMEOUT); + TEE_ERR_STATE(NOTSUPPORTED); + TEE_ERR_STATE(CLIENT_NOT_FOUND); + TEE_ERR_STATE(BUSY); + TEE_ERR_STATE(DISCONNECTED); + TEE_ERR_STATE(INSUFFICIENT_BUFFER); + TEE_ERR_STATE(PERMISSION_DENIED); + default: + return std::to_string(ev); + } +#undef TEE_ERR_STATE + } + } metee_category; + + /*! MeTee error exception class class */ + class metee_exception : public std::system_error + { + public: + /*! Constructor + * \param what error string + * \param err error code + */ + metee_exception(const std::string& what, int err = TEE_INTERNAL_ERROR) : std::system_error(err, metee_category, what) {} + /*! Constructor + * \param err error code + * \param cat error category + */ + metee_exception(int err, const std::error_category& cat) : std::system_error(err, cat) {} + /*! Constructor + * \param err error code + * \param cat error category + * \param what error string + */ + metee_exception(int err, const std::error_category& cat, const std::string& what) + : std::system_error(err, cat, what) {} + /*! Destructor */ + virtual ~metee_exception() noexcept {} + }; + + /*! Dummy client GUID for default constructor */ + DEFINE_GUID(METEE_GUID_ZERO, + 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + + /*! Main interface class + * \brief C++ class to access CSE/CSME/GSC firmware via a mei interface. + */ + class metee + { + public: + /*! Default constructor */ + metee() + { + TEESTATUS status = TeeInit(&_handle, &METEE_GUID_ZERO, NULL); + if (!TEE_IS_SUCCESS(status)) { + throw metee_exception("Init failed", status); + } + } + + /*! Constructor + * \param guid GUID of the FW client that want to start a session + * \param log_level log level to set (from enum tee_log_level) + * \param log_callback pointer to function to run for log write, set NULL to use built-in function + */ + metee(const GUID &guid, + uint32_t log_level = TEE_LOG_LEVEL_VERBOSE, TeeLogCallback log_callback = nullptr) + { + struct tee_device_address device = { tee_device_address::TEE_DEVICE_TYPE_NONE , nullptr }; + TEESTATUS status = TeeInitFull(&_handle, &guid, device, log_level, log_callback); + if (!TEE_IS_SUCCESS(status)) { + throw metee_exception("Init failed", status); + } + } + + /*! Constructor + * \param guid GUID of the FW client that want to start a session + * \param device device address structure + * \param log_level log level to set (from enum tee_log_level) + * \param log_callback pointer to function to run for log write, set NULL to use built-in function + */ + metee(const GUID &guid, const struct tee_device_address &device, + uint32_t log_level = TEE_LOG_LEVEL_VERBOSE, TeeLogCallback log_callback = nullptr) + { + TEESTATUS status = TeeInitFull(&_handle, &guid, device, log_level, log_callback); + if (!TEE_IS_SUCCESS(status)) { + throw metee_exception("Init failed", status); + } + } + + /*! Copy constructor - disabled */ + metee(const metee& other) = delete; + + /*! Move constructor + * \param other Object to move from + */ + metee(metee&& other) noexcept : _handle(other._handle) + { + other._handle.handle = nullptr; + } + + /*! Copy operator - disabled */ + metee& operator=(const metee& other) = delete; + + /*! Move operator + * \param other Object to move from + */ + metee& operator=(metee&& other) noexcept + { + TeeDisconnect(&_handle); + _handle = other._handle; + other._handle.handle = nullptr; + return *this; + } + + /*! Destructor, disconnects, if connected */ + virtual ~metee() + { + TeeDisconnect(&_handle); + } + + /*! Connects to the TEE driver and starts a session */ + void connect() + { + TEESTATUS status; + + status = TeeConnect(&_handle); + if (!TEE_IS_SUCCESS(status)) { + throw metee_exception("Connect failed", status); + } + } + + /*! Read data from the TEE device synchronously. + * \param timeout The timeout to complete read in milliseconds, zero for infinite + * \return vector with data read from the TEE device + */ + std::vector read(uint32_t timeout) + { + TEESTATUS status; + size_t size = 0; + std::vector buffer(max_msg_len()); + + status = TeeRead(&_handle, buffer.data(), buffer.size(), &size, timeout); + if (!TEE_IS_SUCCESS(status)) { + throw metee_exception("Read failed", status); + } + + buffer.resize(size); + return std::move(buffer); + } + + /*! Writes the specified buffer to the TEE device synchronously. + * \param buffer vector containing the data to be written to the TEE device. + * \param timeout The timeout to complete write in milliseconds, zero for infinite + * \return the number of bytes written + */ + size_t write(const std::vector &buffer, uint32_t timeout) + { + TEESTATUS status; + size_t size = 0; + + status = TeeWrite(&_handle, buffer.data(), buffer.size(), &size, timeout); + if (!TEE_IS_SUCCESS(status)) { + throw metee_exception("Write failed", status); + } + + return size; + } + + /*! Retrieves specified FW status register. + * \param fwStatusNum The FW status register number (0-5). + * \return obtained FW status. + */ + uint32_t fw_status(uint32_t fwStatusNum) + { + TEESTATUS status; + uint32_t fwStatus = 0; + + status = TeeFWStatus(&_handle, fwStatusNum, &fwStatus); + if (!TEE_IS_SUCCESS(status)) { + throw metee_exception("FWStatus failed", status); + } + + return fwStatus; + } + + /*! Retrieves TRC register. + * \return TRC value. + */ + uint32_t trc() + { + TEESTATUS status; + uint32_t trc_val = 0; + + status = TeeGetTRC(&_handle, &trc_val); + if (!TEE_IS_SUCCESS(status)) { + throw metee_exception("GetTRC failed", status); + } + + return trc_val; + } + + /*! Retrieves device kind. + * \return kind string value. + */ + std::string kind() + { + TEESTATUS status; + const size_t KIND_SIZE = 32; + char kind[KIND_SIZE]; + size_t kind_size = KIND_SIZE; + + status = TeeGetKind(&_handle, kind, &kind_size); + if (!TEE_IS_SUCCESS(status)) { + throw metee_exception("TeeGetKind failed", status); + } + + return kind; + } + /*! Set log level + * + * \param log_level log level to set + * \return previous log level + */ + uint32_t log_level(uint32_t log_level) + { + return TeeSetLogLevel(&_handle, log_level); + } + + /*! Retrieve current log level + * + * \return current log level + */ + uint32_t log_level() + { + return TeeGetLogLevel(&_handle); + } + + /*! Set log callback + * + * \param log_callback pointer to function to run for log write, set NULL to use built-in function + */ + void log_callback(TeeLogCallback log_callback) + { + TeeSetLogCallback(&_handle, log_callback); + } + + /*! Retrieve client maximum message length (MTU) + * + * \return client maximum message length. + */ + uint32_t max_msg_len() + { + return TeeGetMaxMsgLen(&_handle); + } + + /*! Retrieve client protocol version + * + * \return client protocol version. + */ + uint8_t protocol_ver() + { + return TeeGetProtocolVer(&_handle); + } + + /*! Returns handle of TEE device + * Obtains HECI device handle on Windows and mei device file descriptor on Linux + * \return the handle of the session. + */ + TEE_DEVICE_HANDLE device_handle() + { + return TeeGetDeviceHandle(&_handle); + } + + /*! Obtains version of the TEE device driver + * Not implemented on Linux + * \return Driver version as dotted string. + */ + std::string driver_version() + { + TEESTATUS status; + teeDriverVersion_t driverVersion = { 0 }; + + status = ::GetDriverVersion(&_handle, &driverVersion); + if (!TEE_IS_SUCCESS(status)) { + throw metee_exception("GetDriverVersion failed", status); + } + + std::stringstream ss; + ss << driverVersion.major << "." + << driverVersion.minor << "." + << driverVersion.hotfix << "." + << driverVersion.build; + return ss.str(); + } + + private: + _TEEHANDLE _handle; /*!< Internal device handle */ + }; + } // namespace security +} // namespace intel +#endif // _METEEPP_H_ diff --git a/samples/metee_basic.c b/samples/metee_basic.c index 358051e..851fd19 100644 --- a/samples/metee_basic.c +++ b/samples/metee_basic.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: Apache-2.0 */ /* - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation */ #include #include @@ -66,6 +66,8 @@ int main(int argc, char* argv[]) struct mkhi_fwver_req req; uint8_t *read_buf = NULL; struct mkhi_fwver_rsp* rsp; + char kind[32]; + size_t kind_size = sizeof(kind); status = TeeInitFull(&handle, &MEI_MKHIF, addr, TEE_LOG_LEVEL_VERBOSE, NULL); if (!TEE_IS_SUCCESS(status)) { @@ -73,6 +75,13 @@ int main(int argc, char* argv[]) return 1; } + status = TeeGetKind(&handle, kind, &kind_size); + if (!TEE_IS_SUCCESS(status)) { + fprintf(stderr, "TeeGetKind failed with status = %u\n", status); + } else { + printf("Tee device kind is %s\n", kind); + } + while (retry--) { status = TeeConnect(&handle); if (status != TEE_BUSY && diff --git a/src/Windows/metee_win.c b/src/Windows/metee_win.c index ac9a6c7..b4aeb58 100644 --- a/src/Windows/metee_win.c +++ b/src/Windows/metee_win.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "public.h" #include "helpers.h" @@ -696,3 +698,27 @@ uint8_t TEEAPI TeeGetProtocolVer(IN const PTEEHANDLE handle) } return handle->protcolVer; } + +TEESTATUS TEEAPI TeeGetKind(IN PTEEHANDLE handle, IN OUT char *kind, IN OUT size_t *kindSize) +{ + TEESTATUS status = TEE_INTERNAL_ERROR; + + if (NULL == handle) { + return TEE_INVALID_PARAMETER; + } + + struct METEE_WIN_IMPL* impl_handle = to_int(handle); + + FUNC_ENTRY(handle); + + if (NULL == impl_handle || NULL == kindSize) { + status = TEE_INVALID_PARAMETER; + ERRPRINT(handle, "One of the parameters was illegal"); + goto Cleanup; + } + status = GetDeviceKind(handle, kind, kindSize); + +Cleanup: + FUNC_EXIT(handle, status); + return status; +} diff --git a/src/Windows/metee_win.h b/src/Windows/metee_win.h index 5243e8f..9310613 100644 --- a/src/Windows/metee_win.h +++ b/src/Windows/metee_win.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: Apache-2.0 */ /* - * Copyright (C) 2014-2024 Intel Corporation + * Copyright (C) 2014-2025 Intel Corporation */ #ifndef __TEELIBWIN_H #define __TEELIBWIN_H @@ -54,6 +54,7 @@ TEESTATUS EndOverlapped(IN PTEEHANDLE handle, IN EVENTHANDLE evt, IN DWORD milli OUT OPTIONAL LPDWORD pNumberOfBytesTransferred); TEESTATUS GetDevicePath(IN PTEEHANDLE handle, IN LPCGUID InterfaceGuid, OUT char *path, IN SIZE_T pathSize); +TEESTATUS GetDeviceKind(IN PTEEHANDLE handle, IN OUT OPTIONAL char *kind, IN OUT size_t *kindSize); TEESTATUS SendIOCTL(IN PTEEHANDLE handle, IN EVENTHANDLE evt, IN DWORD ioControlCode, IN LPVOID pInBuffer, IN DWORD inBufferSize, IN LPVOID pOutBuffer, IN DWORD outBufferSize, OUT LPDWORD pBytesRetuned); diff --git a/src/Windows/metee_winhelpers.c b/src/Windows/metee_winhelpers.c index 912f6e3..ad4c478 100644 --- a/src/Windows/metee_winhelpers.c +++ b/src/Windows/metee_winhelpers.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: Apache-2.0 */ /* - * Copyright (C) 2014-2024 Intel Corporation + * Copyright (C) 2014-2025 Intel Corporation */ #include #include @@ -232,6 +232,136 @@ TEESTATUS GetDevicePath(IN PTEEHANDLE handle, IN LPCGUID InterfaceGuid, return status; } +DEFINE_DEVPROPKEY(DEVPKEY_TeedriverKindString, + 0x3279649a, 0x75b8, 0x4663, 0xab, 0x4f, 0x9d, 0xec, 0x58, 0xc5, 0x58, 0xf5, + DEVPROP_TYPE_STRING); +/* +** Get device kind implementation +** +** Parameters: +** handle - metee handle +** kind - pointer to hold device kind, null terminated C string +** kindSize - size in bytes allocated to kind including null terminator +** +** Return: +** TEE_SUCCESS +** TEE_INVALID_PARAMETER +** TEE_INTERNAL_ERROR +*/ +TEESTATUS GetDeviceKind(IN PTEEHANDLE handle, IN OUT OPTIONAL char *kind, IN OUT size_t *kindSize) +{ + CONFIGRET cr; + DEVPROPTYPE prop_type = 0; + ULONG prop_size = 0; + DEVINST devInstHandle; + WCHAR instance_id[MAX_PATH] = { 0 }; + WCHAR* device_path_w = NULL; + WCHAR* kind_w = NULL; + size_t converted_chars; + errno_t err; + + TEESTATUS status = TEE_INTERNAL_ERROR; + + if (NULL == handle) { + return TEE_INVALID_PARAMETER; + } + + struct METEE_WIN_IMPL* impl_handle = to_int(handle); + + FUNC_ENTRY(handle); + + size_t device_path_len = strlen(impl_handle->device_path) + 1; + size_t device_path_size = device_path_len * sizeof(WCHAR); + device_path_w = (WCHAR*)malloc(device_path_size); + if (NULL == device_path_w) { + status = TEE_INTERNAL_ERROR; + ERRPRINT(handle, "Error allocating memory for device path.\n"); + goto Cleanup; + } + converted_chars = 0; + mbstowcs_s(&converted_chars, device_path_w, device_path_len, impl_handle->device_path, _TRUNCATE); + if (converted_chars != device_path_len) { + status = TEE_INTERNAL_ERROR; + ERRPRINT(handle, "Error converting device path to wide.\n"); + goto Cleanup; + } + + prop_size = MAX_PATH; + cr = CM_Get_Device_Interface_PropertyW(device_path_w, &DEVPKEY_Device_InstanceId, &prop_type, (PBYTE)instance_id, &prop_size, 0); + if (cr != CR_SUCCESS) { + ERRPRINT(handle, "CM_Get_Device_Interface_Property: %d\n", cr); + status = TEE_INTERNAL_ERROR; + goto Cleanup; + } + if (DEVPROP_TYPE_STRING != prop_type) + { + ERRPRINT(handle, "Invalid property type %d\n", prop_type); + status = TEE_INTERNAL_ERROR; + goto Cleanup; + } + + cr = CM_Locate_DevNodeW(&devInstHandle, &instance_id[0], CM_LOCATE_DEVNODE_NORMAL); + if (cr != CR_SUCCESS) { + ERRPRINT(handle, "CM_Locate_DevNode: %d\n", cr); + status = TEE_INTERNAL_ERROR; + goto Cleanup; + } + + prop_size = 0; + cr = CM_Get_DevNode_PropertyW(devInstHandle, &DEVPKEY_TeedriverKindString, &prop_type, NULL, &prop_size, 0); + if (cr == CR_NO_SUCH_VALUE) { + DBGPRINT(handle, "CM_Get_DevNode_Property: kind not found\n"); + status = TEE_NOTSUPPORTED; + goto Cleanup; + } + if (cr != CR_BUFFER_SMALL) { + ERRPRINT(handle, "CM_Get_DevNode_Property: %d %d\n", cr, prop_size); + status = TEE_INTERNAL_ERROR; + goto Cleanup; + } + kind_w = (WCHAR*)malloc(prop_size); + if (NULL == kind_w) { + status = TEE_INTERNAL_ERROR; + ERRPRINT(handle, "Error allocating memory for driver kind wide.\n"); + goto Cleanup; + } + cr = CM_Get_DevNode_PropertyW(devInstHandle, &DEVPKEY_TeedriverKindString, &prop_type, (PBYTE)kind_w, &prop_size, 0); + if (cr != CR_SUCCESS) { + ERRPRINT(handle, "CM_Get_DevNode_Property: %d %d\n", cr, prop_size); + status = TEE_INTERNAL_ERROR; + goto Cleanup; + } + if (*kindSize < prop_size) { + ERRPRINT(handle, "Insufficient buffer %d %d\n", *kindSize, prop_size); + *kindSize = prop_size; + status = TEE_INSUFFICIENT_BUFFER; + goto Cleanup; + } + /* + safe implementation of the conversion function + handles NULL input/output buffer values and output buffer overrun + */ + err = wcstombs_s(&converted_chars, kind, *kindSize, kind_w, _TRUNCATE); + if (err != 0) { + ERRPRINT(handle, "convert to multi-byte error %d\n", err); + if (err == STRUNCATE || err == EINVAL) { + *kindSize = prop_size; + status = TEE_INSUFFICIENT_BUFFER; + } else { + status = TEE_INTERNAL_ERROR; + } + goto Cleanup; + } + *kindSize = converted_chars; + status = TEE_SUCCESS; + +Cleanup: + free(kind_w); + free(device_path_w); + FUNC_EXIT(handle, status); + return status; +} + TEESTATUS SendIOCTL(IN PTEEHANDLE handle, IN EVENTHANDLE evt, IN DWORD ioControlCode, IN LPVOID pInBuffer, IN DWORD inBufferSize, IN LPVOID pOutBuffer, IN DWORD outBufferSize, OUT LPDWORD pBytesRetuned) diff --git a/src/linux/libmei.h b/src/linux/libmei.h index 1d2b99c..de9f3a7 100644 --- a/src/linux/libmei.h +++ b/src/linux/libmei.h @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: BSD-3-Clause * - * Copyright(c) 2013 - 2024 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2025 Intel Corporation. All rights reserved. * * Intel Management Engine Interface (Intel MEI) Library */ @@ -256,6 +256,16 @@ int mei_fwstatus(struct mei *me, uint32_t fwsts_num, uint32_t *fwsts); */ int mei_gettrc(struct mei *me, uint32_t *trc_val); +/*! Obtains device kind + * + * \param me The mei handle + * \param kind buffer to fill with device kind null terminated string, may be NULL. + * \param kind_size Pointer to kind buffer size in bytes, updated to number of bytes filled in buffer, including null character, on out. + * If buffer is NULL, required size is returned anyway. + * \return 0 if successful, otherwise error code + */ +int mei_getkind(struct mei *me, char *kind, size_t *kind_size); + /*! Set log level * * \param me The mei handle diff --git a/src/linux/mei.c b/src/linux/mei.c index 2b81db4..70192af 100644 --- a/src/linux/mei.c +++ b/src/linux/mei.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: BSD-3-Clause * - * Copyright(c) 2013 - 2024 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2025 Intel Corporation. All rights reserved. * * Intel Management Engine Interface (Intel MEI) Library */ @@ -337,6 +337,50 @@ static inline int __mei_gettrc(struct mei *me, const char *device, uint32_t *trc #undef CONV_BASE } +static inline int __mei_getkind(struct mei *me, const char *device, char *kind, size_t *kind_size) +{ +#define KIND_FILENAME_LEN 34 +#define KIND_LEN 16 + char path[KIND_FILENAME_LEN]; + char buf[KIND_LEN] = { 0 }; + int fd; + ssize_t len; + + if (snprintf(path, KIND_FILENAME_LEN, + "/sys/class/mei/%s/kind", device) < 0) + return -EINVAL; + path[KIND_FILENAME_LEN - 1] = '\0'; + + errno = 0; + fd = open(path, O_CLOEXEC, O_RDONLY); + if (fd == -1) { + me->last_err = errno; + return -me->last_err; + } + + errno = 0; + len = pread(fd, buf, KIND_LEN, 0); + if (len == -1) { + me->last_err = errno; + close(fd); + return -me->last_err; + } + + close(fd); + if ((size_t)len > *kind_size || !kind) { + me->last_err = ENOSPC; + mei_err(me, "Insufficient buffer %zu %zd\n", *kind_size, len); + *kind_size = (size_t)len; + return -me->last_err; + } + *kind_size = (size_t)len; + memcpy(kind, buf, (size_t)len); + + return 0; +#undef KIND_FILENAME_LEN +#undef KIND_LEN +} + int mei_init_with_log(struct mei *me, const char *device, const uuid_le *guid, unsigned char req_protocol_version, bool verbose, mei_log_callback log_callback) @@ -736,6 +780,35 @@ int mei_gettrc(struct mei *me, uint32_t *trc_val) return 0; } +int mei_getkind(struct mei *me, char *kind, size_t *kind_size) +{ + char *device; + int rc; + + if (!me || !kind_size) + return -EINVAL; + + if (me->device) { + device = strstr(me->device, MEI_DEFAULT_DEVICE_PREFIX); + if (!device) { + mei_err(me, "Device does not start with '%s'\n", + MEI_DEFAULT_DEVICE_PREFIX); + return -EINVAL; + } + device += strlen(MEI_DEFAULT_DEVICE_PREFIX); + } else { + device = MEI_DEFAULT_DEVICE_NAME; + } + rc = __mei_getkind(me, device, kind, kind_size); + if (rc < 0) { + mei_err(me, "Cannot get kind value [%d]:%s\n", + rc, strerror(-rc)); + return rc; + } + + return 0; +} + unsigned int mei_get_api_version(void) { return LIBMEI_API_VERSION; diff --git a/src/linux/metee_linux.c b/src/linux/metee_linux.c index 6ef4a44..0af367b 100644 --- a/src/linux/metee_linux.c +++ b/src/linux/metee_linux.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: Apache-2.0 */ /* - * Copyright (C) 2014-2024 Intel Corporation + * Copyright (C) 2014-2025 Intel Corporation */ #include #include @@ -73,6 +73,7 @@ static inline TEESTATUS errno2status(ssize_t err) case -EACCES: return TEE_PERMISSION_DENIED; case -EOPNOTSUPP: return TEE_NOTSUPPORTED; case -ECANCELED: return TEE_UNABLE_TO_COMPLETE_OPERATION; + case -ENOSPC: return TEE_INSUFFICIENT_BUFFER; default : return TEE_INTERNAL_ERROR; } } @@ -594,4 +595,40 @@ uint8_t TEEAPI TeeGetProtocolVer(IN const PTEEHANDLE handle) return 0; } return handle->protcolVer; +} + +TEESTATUS TEEAPI TeeGetKind(IN PTEEHANDLE handle, IN OUT char *kind, IN OUT size_t *kindSize) +{ + struct mei* me = to_mei(handle); + TEESTATUS status; + int rc; + + if (!handle) { + return TEE_INVALID_PARAMETER; + } + + FUNC_ENTRY(handle); + + if (!me || !kindSize) { + status = TEE_INVALID_PARAMETER; + ERRPRINT(handle, "One of the parameters was illegal\n"); + goto End; + } + + rc = mei_getkind(me, kind, kindSize); + if (rc < 0) { + status = errno2status(rc); + if (status == TEE_INSUFFICIENT_BUFFER) { + DBGPRINT(handle, "Insufficient buffer %zu\n", *kindSize); + } else { + ERRPRINT(handle, "kind get failed with status %d %s\n", rc, strerror(-rc)); + } + goto End; + } + + status = TEE_SUCCESS; + +End: + FUNC_EXIT(handle, status); + return status; } \ No newline at end of file diff --git a/src/uefi/heci_core.c b/src/uefi/heci_core.c index 89dfdc9..37394c7 100644 --- a/src/uefi/heci_core.c +++ b/src/uefi/heci_core.c @@ -99,7 +99,7 @@ GetFilledSlots( #define DelayExecutionFlow gBS->Stall -#define TIMER_STEP 20000 +#define TIMER_STEP 1000 /** Checks if FW is ready for communication over the HECI interface. diff --git a/src/uefi/metee_efi.c b/src/uefi/metee_efi.c index d23b0b8..ed1cc0b 100644 --- a/src/uefi/metee_efi.c +++ b/src/uefi/metee_efi.c @@ -99,20 +99,20 @@ SetHwInfo( { TEESTATUS status = TEE_INVALID_PARAMETER; FUNC_ENTRY(Handle->TeeHandle); - switch (device->data.bdf.kind) + switch (device->data.bdf.hw_type) { - case HECI_DEVICE_KIND_PCH: + case HECI_HW_TYPE_PCH: Handle->Hw = HwInfoPch(device->data.bdf.value.segment, device->data.bdf.value.bus, device->data.bdf.value.device, device->data.bdf.value.function); - DBGPRINT(Handle->TeeHandle, "******** HECI_DEVICE_KIND_PCH\n"); + DBGPRINT(Handle->TeeHandle, "******** HECI_HW_TYPE_PCH\n"); break; - case HECI_DEVICE_KIND_GFX_GSC: + case HECI_HW_TYPE_GFX_GSC: Handle->Hw = HwInfoGfxGsc(device->data.bdf.value.segment, device->data.bdf.value.bus, device->data.bdf.value.device, device->data.bdf.value.function); - DBGPRINT(Handle->TeeHandle, "******** HECI_DEVICE_KIND_GFX_GSC\n"); + DBGPRINT(Handle->TeeHandle, "******** HECI_HW_TYPE_GFX_GSC\n"); break; default: - DBGPRINT(Handle->TeeHandle, "******** Unsupported device kind %d\n", device->data.bdf.kind); + DBGPRINT(Handle->TeeHandle, "******** Unsupported device kind %d\n", device->data.bdf.hw_type); status = TEE_INVALID_PARAMETER; goto End; } @@ -152,6 +152,7 @@ TeeInitFullTypeEfiDevice( efi_impl->TeeHandle = handle; efi_impl->ClientGuid = *guid; efi_impl->State = METEE_CLIENT_STATE_NONE; + efi_impl->HwType = device->data.bdf.hw_type; SetHwInfo(device, efi_impl); *impl_handle = efi_impl; @@ -181,6 +182,7 @@ TeeInitFull( IN OPTIONAL TeeLogCallback log_callback) { TEESTATUS status = TEE_INTERNAL_ERROR; + struct tee_device_address default_device = device; if (NULL == guid) { @@ -219,10 +221,10 @@ TeeInitFull( status = TEE_INVALID_PARAMETER; goto Cleanup; } - struct tee_device_address default_device = device; + // CSME HECI by default #define PCI_DEVICE_NUMBER_PCH_HECI1 22 - default_device.data.bdf.kind = HECI_DEVICE_KIND_PCH; + default_device.data.bdf.hw_type = HECI_HW_TYPE_PCH; default_device.data.bdf.value.segment = 0; default_device.data.bdf.value.bus = 0; default_device.data.bdf.value.device = PCI_DEVICE_NUMBER_PCH_HECI1; @@ -711,4 +713,73 @@ uint8_t TEEAPI TeeGetProtocolVer(IN const PTEEHANDLE handle) return 0; } return handle->protcolVer; +} + +typedef enum TEEDRIVER_PLATFORM_KIND_ +{ + TEEDRIVER_PLATFORM_KIND_MEI = 0, + TEEDRIVER_PLATFORM_KIND_IOE = 1, + TEEDRIVER_PLATFORM_KIND_GSC = 2, + TEEDRIVER_PLATFORM_KIND_MAX = 3, +} TEEDRIVER_PLATFORM_KIND; + +#define HECI1_IOE_SKU_BIT_START 0 +#define HECI1_IOE_SKU_BIT_NUMS 4 +#define HECI1_IOE_SKU_BIT_MASK (((1 << HECI1_IOE_SKU_BIT_NUMS) - 1) << HECI1_IOE_SKU_BIT_START) +#define HECI1_IOE_SKU 0x1 +#define HECI1_FW_STS3 2 + +TEESTATUS TEEAPI TeeGetKind(IN PTEEHANDLE handle, IN OUT char *kind, IN OUT size_t *kindSize) +{ + CHAR8* propertyMap[TEEDRIVER_PLATFORM_KIND_MAX] = { + "mei", + "ioe", + "gscfi", + }; + + TEESTATUS status; + EFI_STATUS efi_status; + UINT32 fw_sts3; + UINT32 kind_ind = 0; + UINTN kind_len; + if (NULL == handle) + { + return TEE_INVALID_PARAMETER; + } + FUNC_ENTRY(handle); + struct METEE_EFI_IMPL *impl_handle = to_int(handle); + + if (NULL == impl_handle || NULL == kindSize) { + status = TEE_INVALID_PARAMETER; + ERRPRINT(handle, "One of the parameters was illegal"); + goto End; + } + + if (impl_handle->HwType == HECI_HW_TYPE_PCH) { + efi_status = HeciFwStatus(impl_handle, HECI1_FW_STS3, &fw_sts3); + if (EFI_ERROR(efi_status)) + { + ERRPRINT(handle, "Failed to retrieve FW Status %d\n", efi_status); + status = TEE_INTERNAL_ERROR; + goto End; + } + kind_ind = ((fw_sts3 & HECI1_IOE_SKU_BIT_MASK) == HECI1_IOE_SKU) ? TEEDRIVER_PLATFORM_KIND_IOE : TEEDRIVER_PLATFORM_KIND_MEI; + } else { + kind_ind = TEEDRIVER_PLATFORM_KIND_GSC; + } + + kind_len = AsciiStrLen(propertyMap[kind_ind]) + 1; + if (kind_len > *kindSize || NULL == kind) { + DBGPRINT(handle, "Insufficient buffer %d %d\n", *kindSize, kind_len); + *kindSize = kind_len; + status = TEE_INSUFFICIENT_BUFFER; + goto End; + } + CopyMem(kind, propertyMap[kind_ind], kind_len); + *kindSize = kind_len; + status = TEE_SUCCESS; + +End: + FUNC_EXIT(handle, status); + return status; } \ No newline at end of file diff --git a/src/uefi/metee_efi.h b/src/uefi/metee_efi.h index cbf2716..2f3d90c 100644 --- a/src/uefi/metee_efi.h +++ b/src/uefi/metee_efi.h @@ -188,11 +188,12 @@ struct METEE_EFI_IMPL enum METEE_CLIENT_STATE State; /** The client state */ HECI_CLIENT_CONNECTION HeciClientConnection; /** HECI EFI Connection information */ struct HECI_HW Hw; /** HECI HW information */ + enum HECI_HW_TYPE HwType; /** HECI HW type */ }; #define HECI_EFI_PRINT_BDF_STR "[HECI %d:%d:%d:%d] " #define HECI_EFI_PRINT_BDF_VAL Handle->Hw.Bdf.Segment, Handle->Hw.Bdf.Bus, Handle->Hw.Bdf.Device, Handle->Hw.Bdf.Function -#define EFIPRINT(Handle, fmt, ...) DBGPRINT(Handle, HECI_EFI_PRINT_BDF_STR fmt, HECI_EFI_PRINT_BDF_VAL, __VA_ARGS__); +#define EFIPRINT(Handle, fmt, ...) DBGPRINT(Handle, HECI_EFI_PRINT_BDF_STR fmt, HECI_EFI_PRINT_BDF_VAL, ##__VA_ARGS__); #endif // METEE_EFI_H_ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 847d926..4344119 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -# Copyright (C) 2014-2024 Intel Corporation +# Copyright (C) 2014-2025 Intel Corporation cmake_minimum_required(VERSION 3.15) project(metee_test) @@ -46,6 +46,7 @@ add_subdirectory( add_executable(${PROJECT_NAME} Main.cpp metee_test.cpp + meteepp_test.cpp $<$:${CMAKE_SOURCE_DIR}/src/Windows/metee_winhelpers.c> ) if(NOT CONSOLE_OUTPUT) diff --git a/tests/metee_test.cpp b/tests/metee_test.cpp index 39a2910..e401fdb 100644 --- a/tests/metee_test.cpp +++ b/tests/metee_test.cpp @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: Apache-2.0 */ /* - * Copyright (C) 2014-2024 Intel Corporation + * Copyright (C) 2014-2025 Intel Corporation */ #include #include @@ -467,6 +467,24 @@ TEST_P(MeTeeTEST, PROD_N_TestLongClientPath) ASSERT_EQ(TEE_CLIENT_NOT_FOUND, ConnectRetry(&handle)); } +TEST_P(MeTeeTEST, PROD_N_TestGetMeiKind) +{ + TEEHANDLE handle = TEEHANDLE_ZERO; + struct MeTeeTESTParams intf = GetParam(); + char kind[64]; + size_t kind_size = sizeof(kind); + TEESTATUS status; + + status = TestTeeInitGUID(&handle, &GUID_NON_EXISTS_CLIENT, intf.device); + ASSERT_EQ(TEE_SUCCESS, status); + + status = TeeGetKind(&handle, kind, &kind_size); + ASSERT_TRUE(status == TEE_SUCCESS || status == TEE_NOTSUPPORTED); + + TeeDisconnect(&handle); + EXPECT_EQ(TEE_INVALID_DEVICE_HANDLE, TeeGetDeviceHandle(&handle)); +} + TEST_P(MeTeeOpenTEST, PROD_N_TestGetDriverVersion) { teeDriverVersion_t ver = {0, 0, 0, 0}; diff --git a/tests/metee_test.h b/tests/metee_test.h index fa8758d..a745418 100644 --- a/tests/metee_test.h +++ b/tests/metee_test.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: Apache-2.0 */ /* - * Copyright (C) 2014-2024 Intel Corporation + * Copyright (C) 2014-2025 Intel Corporation */ #include #include @@ -13,6 +13,7 @@ #include #include "metee.h" +#include "meteepp.h" #include "helpers.h" #ifdef WIN32 #include @@ -93,6 +94,43 @@ class MeTeeTEST : public ::testing::TestWithParam{ GEN_GET_FW_VERSION MkhiRequest; }; +class MeTeePPTEST : public ::testing::TestWithParam { +public: + MeTeePPTEST() { + // initialization code here + } + + void SetUp() { + GEN_GET_FW_VERSION req; +#ifdef _DEBUG + printf("Enter ProdTests SetUp\n"); +#endif + req.Header.Fields.Command = GEN_GET_FW_VERSION_CMD; + req.Header.Fields.GroupId = MKHI_GEN_GROUP_ID; + req.Header.Fields.IsResponse = 0; + auto ptr = reinterpret_cast(&req); + MkhiRequest = std::vector(ptr, ptr + sizeof(GEN_GET_FW_VERSION)); +#ifdef _DEBUG + printf("Exit ProdTests SetUp\n"); +#endif + } + + void TearDown() { +#ifdef _DEBUG + printf("Enter ProdTests TearDown\n"); +#endif + std::this_thread::sleep_for(std::chrono::milliseconds(100)); +#ifdef _DEBUG + printf("Exit ProdTests TearDown\n"); +#endif + } + + ~MeTeePPTEST() { + // cleanup any pending stuff, but no exceptions allowed + } + std::vector MkhiRequest; +}; + class MeTeeFDTEST : public ::testing::TestWithParam { public: MeTeeFDTEST() : deviceHandle(TEE_INVALID_DEVICE_HANDLE) { diff --git a/tests/meteepp_test.cpp b/tests/meteepp_test.cpp new file mode 100644 index 0000000..6cbc30b --- /dev/null +++ b/tests/meteepp_test.cpp @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * Copyright (C) 2021-2025 Intel Corporation + */ +#include "metee_test.h" + +DEFINE_GUID(GUID_NON_EXISTS_CLIENT, + 0x85eb8fa6, 0xbdd, 0x4d01, 0xbe, 0xc4, 0xa5, 0x97, 0x43, 0x4e, 0xd7, 0x62); + +TEST_P(MeTeePPTEST, PROD_MKHI_SimpleGetVersion) +{ + struct MeTeeTESTParams intf = GetParam(); + std::vector MaxResponse; + GEN_GET_FW_VERSION_ACK* pResponseMessage; + + for (int i = 1; i < 2; i++) { + try { + intel::security::metee metee(*intf.client); + + ASSERT_NE(TEE_INVALID_DEVICE_HANDLE, metee.device_handle()); + metee.connect(); + + ASSERT_EQ(MkhiRequest.size(), metee.write(MkhiRequest, 0)); + + MaxResponse = metee.read(0); + ASSERT_LE(sizeof(GEN_GET_FW_VERSION_ACK), MaxResponse.size()); + pResponseMessage = reinterpret_cast(MaxResponse.data()); + + ASSERT_EQ(TEE_SUCCESS, pResponseMessage->Header.Fields.Result); + EXPECT_NE(0, pResponseMessage->Data.FWVersion.CodeMajor); + EXPECT_NE(0, pResponseMessage->Data.FWVersion.CodeBuildNo); + } + catch(const intel::security::metee_exception &ex){ + if (ex.code().value() == TEE_DEVICE_NOT_FOUND) + GTEST_SKIP(); + FAIL() << "Excepton: " << ex.what(); + } + } +} + +TEST_P(MeTeePPTEST, PROD_N_Kind) +{ + struct MeTeeTESTParams intf = GetParam(); + + try { + intel::security::metee metee(*intf.client); + + ASSERT_NE(TEE_INVALID_DEVICE_HANDLE, metee.device_handle()); + + ASSERT_GE(metee.kind().length(), 0); + } + catch (const intel::security::metee_exception& ex) { + if (ex.code().value() == TEE_DEVICE_NOT_FOUND) + GTEST_SKIP(); + if (ex.code().value() != TEE_NOTSUPPORTED) + FAIL() << "Excepton: " << ex.what(); + } +} + +TEST_P(MeTeePPTEST, PROD_MKHI_InitFull) +{ + struct MeTeeTESTParams intf = GetParam(); + std::vector MaxResponse; + GEN_GET_FW_VERSION_ACK* pResponseMessage; + + try { + struct tee_device_address device = {tee_device_address::TEE_DEVICE_TYPE_NONE, { NULL }}; + + intel::security::metee metee(*intf.client, device, TEE_LOG_LEVEL_VERBOSE, NULL); + + ASSERT_NE(TEE_INVALID_DEVICE_HANDLE, metee.device_handle()); + metee.connect(); + } + catch (const intel::security::metee_exception& ex) { + if (ex.code().value() == TEE_DEVICE_NOT_FOUND) + GTEST_SKIP(); + FAIL() << "Excepton: " << ex.what(); + } +} + +TEST_P(MeTeePPTEST, PROD_N_TestConnectToNonExistsUuid) +{ + struct MeTeeTESTParams intf = GetParam(); + + try { + intel::security::metee metee(GUID_NON_EXISTS_CLIENT); + ASSERT_NE(TEE_INVALID_DEVICE_HANDLE, metee.device_handle()); + metee.connect(); + } + catch (const intel::security::metee_exception& ex) { + if (ex.code().value() == TEE_DEVICE_NOT_FOUND) + GTEST_SKIP(); + if (ex.code().value() == TEE_CLIENT_NOT_FOUND) + return; + FAIL() << "Excepton: " << ex.what(); + } + FAIL(); +} + +TEST_P(MeTeePPTEST, PROD_MKHI_MoveSemantics) +{ + struct MeTeeTESTParams intf = GetParam(); + std::vector MaxResponse; + GEN_GET_FW_VERSION_ACK* pResponseMessage; + + intel::security::metee metee2; + + try { + intel::security::metee metee(*intf.client); + ASSERT_NE(TEE_INVALID_DEVICE_HANDLE, metee.device_handle()); + metee.connect(); + + metee2 = std::move(metee); + } + catch (const intel::security::metee_exception& ex) { + if (ex.code().value() == TEE_DEVICE_NOT_FOUND) + GTEST_SKIP(); + FAIL() << "Excepton: " << ex.what(); + } + + ASSERT_EQ(MkhiRequest.size(), metee2.write(MkhiRequest, 0)); + + MaxResponse = metee2.read(0); + ASSERT_LE(sizeof(GEN_GET_FW_VERSION_ACK), MaxResponse.size()); + pResponseMessage = reinterpret_cast(MaxResponse.data()); + + ASSERT_EQ(TEE_SUCCESS, pResponseMessage->Header.Fields.Result); + EXPECT_NE(0, pResponseMessage->Data.FWVersion.CodeMajor); + EXPECT_NE(0, pResponseMessage->Data.FWVersion.CodeBuildNo); +} + +static struct MeTeeTESTParams interfaces[1] = { + {"PCH", NULL, &GUID_DEVINTERFACE_MKHI}}; + +INSTANTIATE_TEST_SUITE_P(MeTeePPTESTInstance, MeTeePPTEST, + testing::ValuesIn(interfaces), + [](const testing::TestParamInfo& info) { + return info.param.name; + }); \ No newline at end of file