/* * Copyright (C) 2022 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #include "config.h" #include #include "fu-intel-me-common.h" #include "fu-intel-me-heci-device.h" G_DEFINE_TYPE(FuIntelMeHeciDevice, fu_intel_me_heci_device, FU_TYPE_MEI_DEVICE) #define FU_INTEL_ME_HECI_DEVICE_TIMEOUT 200 /* ms */ static gboolean fu_intel_me_heci_device_open(FuDevice *device, GError **error) { /* open then create context */ if (!FU_DEVICE_CLASS(fu_intel_me_heci_device_parent_class)->open(device, error)) return FALSE; return fu_mei_device_connect(FU_MEI_DEVICE(device), 0, error); } static GByteArray * fu_intel_me_heci_device_read_file_response(GByteArray *buf_res, guint32 datasz_req, GError **error) { guint32 datasz_res = 0; g_autoptr(GByteArray) buf = g_byte_array_new(); /* verify payload size */ if (!fu_memread_uint32_safe(buf_res->data, buf_res->len, sizeof(FuMkhiHeader), &datasz_res, G_LITTLE_ENDIAN, error)) return NULL; if (datasz_res > datasz_req || datasz_res == 0x0) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "invalid response data size, requested 0x%x and got 0x%x", datasz_req, datasz_res); return NULL; } /* copy out payload */ for (gsize i = 0; i < datasz_res; i++) { guint8 tmp = 0; if (!fu_memread_uint8_safe(buf_res->data, buf_res->len, sizeof(FuMkhiHeader) + sizeof(guint32) + i, &tmp, error)) return NULL; fu_byte_array_append_uint8(buf, tmp); } /* success */ return g_steal_pointer(&buf); } GByteArray * fu_intel_me_heci_device_read_file(FuIntelMeHeciDevice *self, const gchar *filename, GError **error) { FuMkhiHeader hdr_res = {0}; gsize filenamesz = strlen(filename); guint datasz_req = 0x80; g_autoptr(GByteArray) buf_fn = g_byte_array_new(); g_autoptr(GByteArray) buf_req = g_byte_array_new(); g_autoptr(GByteArray) buf_res = g_byte_array_new(); const FuMkhiHeader hdr_req = {.group_id = MKHI_GROUP_ID_MCA, .command = MCA_READ_FILE_EX}; /* filename must be a specific size */ fu_byte_array_set_size(buf_fn, 0x40, 0x0); if (!fu_memcpy_safe(buf_fn->data, buf_fn->len - 1, 0x0, (const guint8 *)filename, filenamesz, 0x0, filenamesz, error)) return NULL; /* request */ g_byte_array_append(buf_req, (const guint8 *)&hdr_req, sizeof(hdr_req)); g_byte_array_append(buf_req, buf_fn->data, buf_fn->len); /* Filename */ fu_byte_array_append_uint32(buf_req, 0x0, G_LITTLE_ENDIAN); /* Offset */ fu_byte_array_append_uint32(buf_req, datasz_req, G_LITTLE_ENDIAN); /* DataSize */ fu_byte_array_append_uint8(buf_req, (1 << 3)); /* Flags?? */ if (!fu_mei_device_write(FU_MEI_DEVICE(self), buf_req->data, buf_req->len, FU_INTEL_ME_HECI_DEVICE_TIMEOUT, error)) return NULL; /* response */ fu_byte_array_set_size(buf_res, sizeof(hdr_res) + sizeof(guint32) + datasz_req, 0x0); if (!fu_mei_device_read(FU_MEI_DEVICE(self), buf_res->data, buf_res->len, NULL, FU_INTEL_ME_HECI_DEVICE_TIMEOUT, error)) return NULL; /* verify header */ if (!fu_memcpy_safe((guint8 *)&hdr_res, sizeof(hdr_res), 0x0, /* dst */ buf_res->data, buf_res->len, 0x0, /* src */ sizeof(hdr_req), error)) return NULL; if (!fu_intel_me_mkhi_verify_header(hdr_req, hdr_res, error)) return NULL; return fu_intel_me_heci_device_read_file_response(buf_res, datasz_req, error); } GByteArray * fu_intel_me_heci_device_read_file_ex(FuIntelMeHeciDevice *self, guint32 file_id, guint32 section, guint32 datasz_req, GError **error) { FuMkhiHeader hdr_res = {0}; g_autoptr(GByteArray) buf_req = g_byte_array_new(); g_autoptr(GByteArray) buf_res = g_byte_array_new(); const FuMkhiHeader hdr_req = {.group_id = MKHI_GROUP_ID_MCA, .command = MCA_READ_FILE_EX_CMD}; /* request */ g_byte_array_append(buf_req, (const guint8 *)&hdr_req, sizeof(hdr_req)); fu_byte_array_append_uint32(buf_req, file_id, G_LITTLE_ENDIAN); /* FileId */ fu_byte_array_append_uint32(buf_req, 0x0, G_LITTLE_ENDIAN); /* Offset */ fu_byte_array_append_uint32(buf_req, datasz_req, G_LITTLE_ENDIAN); /* DataSize */ fu_byte_array_append_uint8(buf_req, section); /* Flags */ if (!fu_mei_device_write(FU_MEI_DEVICE(self), buf_req->data, buf_req->len, FU_INTEL_ME_HECI_DEVICE_TIMEOUT, error)) return NULL; /* response */ fu_byte_array_set_size(buf_res, sizeof(hdr_res) + sizeof(guint32) + datasz_req, 0x0); if (!fu_mei_device_read(FU_MEI_DEVICE(self), buf_res->data, buf_res->len, NULL, FU_INTEL_ME_HECI_DEVICE_TIMEOUT, error)) return NULL; /* verify header */ if (!fu_memcpy_safe((guint8 *)&hdr_res, sizeof(hdr_res), 0x0, /* dst */ buf_res->data, buf_res->len, 0x0, /* src */ sizeof(hdr_req), error)) return NULL; if (!fu_intel_me_mkhi_verify_header(hdr_req, hdr_res, error)) return NULL; return fu_intel_me_heci_device_read_file_response(buf_res, datasz_req, error); } static void fu_intel_me_heci_device_init(FuIntelMeHeciDevice *self) { fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_icon(FU_DEVICE(self), "computer"); } static void fu_intel_me_heci_device_class_init(FuIntelMeHeciDeviceClass *klass) { FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); klass_device->open = fu_intel_me_heci_device_open; }