mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-04 03:36:57 +00:00
Add support for the PHAT table
On Alterlake and newer hardware the Platform Health Assessment Record data can be used by the IHV to debug why a specific capsule update failed. Any custom firmware loaded by the OEM can be identified and used to further debug the root cause.
This commit is contained in:
parent
54d9de9290
commit
769cbbf8af
@ -295,6 +295,7 @@ def _build(bld: Builder) -> None:
|
||||
|
||||
# plugins
|
||||
for fzr in [
|
||||
Fuzzer("acpi-phat", pattern="acpi-phat"),
|
||||
Fuzzer("bcm57xx"),
|
||||
Fuzzer("ccgx-dmc", srcdir="ccgx", globstr="ccgx-dmc*.bin"),
|
||||
Fuzzer("ccgx", globstr="ccgx*.cyacd"),
|
||||
|
@ -401,6 +401,7 @@ done
|
||||
%dir %{_libdir}/fwupd-plugins-3
|
||||
%{_libdir}/fwupd-plugins-3/libfu_plugin_acpi_dmar.so
|
||||
%{_libdir}/fwupd-plugins-3/libfu_plugin_acpi_facp.so
|
||||
%{_libdir}/fwupd-plugins-3/libfu_plugin_acpi_phat.so
|
||||
%{_libdir}/fwupd-plugins-3/libfu_plugin_altos.so
|
||||
%{_libdir}/fwupd-plugins-3/libfu_plugin_amt.so
|
||||
%{_libdir}/fwupd-plugins-3/libfu_plugin_analogix.so
|
||||
|
@ -13,6 +13,7 @@ run_test()
|
||||
|
||||
run_test acpi-dmar-self-test
|
||||
run_test acpi-facp-self-test
|
||||
run_test acpi-phat-self-test
|
||||
run_test ata-self-test
|
||||
run_test nitrokey-self-test
|
||||
run_test linux-swap-self-test
|
||||
|
@ -745,7 +745,7 @@ fwupd_guid_from_string (const gchar *guidstr,
|
||||
g_set_error_literal (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_DATA,
|
||||
"is not valid format");
|
||||
"GUID is not valid format");
|
||||
return FALSE;
|
||||
}
|
||||
split = g_strsplit (guidstr, "-", 5);
|
||||
@ -753,7 +753,7 @@ fwupd_guid_from_string (const gchar *guidstr,
|
||||
g_set_error_literal (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_DATA,
|
||||
"is not valid format, no dashes");
|
||||
"GUID is not valid format, no dashes");
|
||||
return FALSE;
|
||||
}
|
||||
if (strlen (split[0]) != 8 && strlen (split[1]) != 4 &&
|
||||
@ -762,7 +762,7 @@ fwupd_guid_from_string (const gchar *guidstr,
|
||||
g_set_error_literal (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_DATA,
|
||||
"is not valid format, not GUID");
|
||||
"GUID is not valid format, not GUID");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
23
plugins/acpi-phat/README.md
Normal file
23
plugins/acpi-phat/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
Platform Health Assessment Table
|
||||
================================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The PHAT is an ACPI table where a platform can expose health related telemetry
|
||||
that may be useful for software running within the constraints of an OS.
|
||||
|
||||
These elements are typically going to encompass things that are likely otherwise
|
||||
not enumerable during the OS runtime phase of operations, such as version of
|
||||
pre-OS components.
|
||||
|
||||
The daemon includes some of the PHAT data in the report data sent to the LVFS
|
||||
so that we can debug failures with the help of the IHV. This allows us to find
|
||||
the root cause of the problem, and so we know what other OEMs may be affected.
|
||||
|
||||
See https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#platform-health-assessment-table
|
||||
for more information.
|
||||
|
||||
External interface access
|
||||
-------------------------
|
||||
This plugin requires read access to `/sys/firmware/acpi/tables`.
|
205
plugins/acpi-phat/fu-acpi-phat-health-record.c
Normal file
205
plugins/acpi-phat/fu-acpi-phat-health-record.c
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "fu-common.h"
|
||||
#include "fu-firmware-common.h"
|
||||
|
||||
#include "fu-acpi-phat.h"
|
||||
#include "fu-acpi-phat-health-record.h"
|
||||
|
||||
struct _FuAcpiPhatHealthRecord {
|
||||
FuFirmware parent_instance;
|
||||
guint8 am_healthy;
|
||||
gchar *guid;
|
||||
gchar *device_path;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (FuAcpiPhatHealthRecord, fu_acpi_phat_health_record, FU_TYPE_FIRMWARE)
|
||||
|
||||
static void
|
||||
fu_acpi_phat_health_record_export (FuFirmware *firmware,
|
||||
FuFirmwareExportFlags flags,
|
||||
XbBuilderNode *bn)
|
||||
{
|
||||
FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD (firmware);
|
||||
if (self->guid != NULL)
|
||||
fu_xmlb_builder_insert_kv (bn, "guid", self->guid);
|
||||
if (self->device_path != NULL)
|
||||
fu_xmlb_builder_insert_kv (bn, "device_path", self->device_path);
|
||||
if (self->am_healthy != 0)
|
||||
fu_xmlb_builder_insert_kx (bn, "am_healthy", self->am_healthy);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_acpi_phat_health_record_parse (FuFirmware *firmware,
|
||||
GBytes *fw,
|
||||
guint64 addr_start,
|
||||
guint64 addr_end,
|
||||
FwupdInstallFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD (firmware);
|
||||
gsize bufsz = 0;
|
||||
const guint8 *buf = g_bytes_get_data (fw, &bufsz);
|
||||
fwupd_guid_t guid = { 0x0 };
|
||||
|
||||
/* am healthy */
|
||||
if (!fu_common_read_uint8_safe (buf, bufsz, 7, &self->am_healthy, error))
|
||||
return FALSE;
|
||||
|
||||
/* device signature */
|
||||
if (!fu_memcpy_safe ((guint8 *) &guid, sizeof(guid), 0x0, /* dst */
|
||||
buf, bufsz, 8, /* src */
|
||||
sizeof(guid), error))
|
||||
return FALSE;
|
||||
self->guid = fwupd_guid_to_string (&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN);
|
||||
|
||||
/* device path */
|
||||
if (bufsz > 28) {
|
||||
self->device_path = g_utf16_to_utf8 ((const gunichar2 *) (buf + 28),
|
||||
(bufsz - 28) / 2,
|
||||
NULL, NULL,
|
||||
error);
|
||||
if (self->device_path == NULL)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
fu_acpi_phat_health_record_write (FuFirmware *firmware, GError **error)
|
||||
{
|
||||
FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD (firmware);
|
||||
fwupd_guid_t guid = { 0x0 };
|
||||
glong device_path_utf16sz = 0;
|
||||
g_autofree gunichar2 *device_path_utf16 = NULL;
|
||||
g_autoptr(GByteArray) buf = g_byte_array_new ();
|
||||
|
||||
/* convert device path ahead of time to get total record length */
|
||||
if (self->device_path != NULL) {
|
||||
device_path_utf16 = g_utf8_to_utf16 (self->device_path, -1,
|
||||
NULL, &device_path_utf16sz,
|
||||
error);
|
||||
if (device_path_utf16 == NULL)
|
||||
return NULL;
|
||||
device_path_utf16sz *= 2;
|
||||
}
|
||||
|
||||
/* data record */
|
||||
fu_byte_array_append_uint16 (buf,
|
||||
FU_ACPI_PHAT_RECORD_TYPE_HEALTH,
|
||||
G_LITTLE_ENDIAN);
|
||||
fu_byte_array_append_uint16 (buf, 28 + device_path_utf16sz, G_LITTLE_ENDIAN);
|
||||
fu_byte_array_append_uint8 (buf, fu_firmware_get_version_raw (firmware));
|
||||
fu_byte_array_append_uint8 (buf, 0x00);
|
||||
fu_byte_array_append_uint8 (buf, 0x00);
|
||||
fu_byte_array_append_uint8 (buf, self->am_healthy);
|
||||
|
||||
/* device signature */
|
||||
if (self->guid != NULL) {
|
||||
if (!fwupd_guid_from_string (self->guid, &guid,
|
||||
FWUPD_GUID_FLAG_MIXED_ENDIAN,
|
||||
error))
|
||||
return NULL;
|
||||
}
|
||||
g_byte_array_append (buf, guid, sizeof(guid));
|
||||
|
||||
/* device-specific data unsupported */
|
||||
fu_byte_array_append_uint32 (buf, 0x0, G_LITTLE_ENDIAN);
|
||||
|
||||
/* device path */
|
||||
if (self->device_path != NULL) {
|
||||
g_byte_array_append (buf,
|
||||
(const guint8 *) device_path_utf16,
|
||||
device_path_utf16sz);
|
||||
}
|
||||
|
||||
/* success */
|
||||
return g_byte_array_free_to_bytes (g_steal_pointer (&buf));
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_health_record_set_guid (FuAcpiPhatHealthRecord *self,
|
||||
const gchar *guid)
|
||||
{
|
||||
g_free (self->guid);
|
||||
self->guid = g_strdup (guid);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_health_record_set_device_path (FuAcpiPhatHealthRecord *self,
|
||||
const gchar *device_path)
|
||||
{
|
||||
g_free (self->device_path);
|
||||
self->device_path = g_strdup (device_path);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_acpi_phat_health_record_build (FuFirmware *firmware, XbNode *n, GError **error)
|
||||
{
|
||||
FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD (firmware);
|
||||
const gchar *tmp;
|
||||
guint64 tmp64;
|
||||
|
||||
/* optional properties */
|
||||
tmp = xb_node_query_text (n, "device_path", NULL);
|
||||
if (tmp != NULL)
|
||||
fu_acpi_phat_health_record_set_device_path (self, tmp);
|
||||
tmp = xb_node_query_text (n, "guid", NULL);
|
||||
if (tmp != NULL)
|
||||
fu_acpi_phat_health_record_set_guid (self, tmp);
|
||||
tmp64 = xb_node_query_text_as_uint (n, "am_healthy", NULL);
|
||||
if (tmp64 > G_MAXUINT8) {
|
||||
g_set_error (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
"am_healthy value invalid, got 0x%x",
|
||||
(guint) tmp64);
|
||||
return FALSE;
|
||||
}
|
||||
self->am_healthy = (guint8) tmp64;
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_health_record_init (FuAcpiPhatHealthRecord *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_health_record_finalize (GObject *object)
|
||||
{
|
||||
FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD (object);
|
||||
g_free (self->guid);
|
||||
g_free (self->device_path);
|
||||
G_OBJECT_CLASS (fu_acpi_phat_health_record_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_health_record_class_init (FuAcpiPhatHealthRecordClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass);
|
||||
object_class->finalize = fu_acpi_phat_health_record_finalize;
|
||||
klass_firmware->parse = fu_acpi_phat_health_record_parse;
|
||||
klass_firmware->write = fu_acpi_phat_health_record_write;
|
||||
klass_firmware->export = fu_acpi_phat_health_record_export;
|
||||
klass_firmware->build = fu_acpi_phat_health_record_build;
|
||||
}
|
||||
|
||||
FuFirmware *
|
||||
fu_acpi_phat_health_record_new (void)
|
||||
{
|
||||
return FU_FIRMWARE (g_object_new (FU_TYPE_ACPI_PHAT_HEALTH_RECORD, NULL));
|
||||
}
|
14
plugins/acpi-phat/fu-acpi-phat-health-record.h
Normal file
14
plugins/acpi-phat/fu-acpi-phat-health-record.h
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fu-firmware.h"
|
||||
|
||||
#define FU_TYPE_ACPI_PHAT_HEALTH_RECORD (fu_acpi_phat_health_record_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (FuAcpiPhatHealthRecord, fu_acpi_phat_health_record, FU, ACPI_PHAT_HEALTH_RECORD, FuFirmware)
|
||||
|
||||
FuFirmware *fu_acpi_phat_health_record_new (void);
|
180
plugins/acpi-phat/fu-acpi-phat-version-element.c
Normal file
180
plugins/acpi-phat/fu-acpi-phat-version-element.c
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "fu-common.h"
|
||||
#include "fu-firmware-common.h"
|
||||
#include "fu-acpi-phat-version-element.h"
|
||||
|
||||
struct _FuAcpiPhatVersionElement {
|
||||
FuFirmware parent_instance;
|
||||
gchar *guid;
|
||||
gchar *producer_id;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (FuAcpiPhatVersionElement, fu_acpi_phat_version_element, FU_TYPE_FIRMWARE)
|
||||
|
||||
static void
|
||||
fu_acpi_phat_version_element_export (FuFirmware *firmware,
|
||||
FuFirmwareExportFlags flags,
|
||||
XbBuilderNode *bn)
|
||||
{
|
||||
FuAcpiPhatVersionElement *self = FU_ACPI_PHAT_VERSION_ELEMENT (firmware);
|
||||
if (self->guid != NULL)
|
||||
fu_xmlb_builder_insert_kv (bn, "guid", self->guid);
|
||||
if (self->producer_id != NULL)
|
||||
fu_xmlb_builder_insert_kv (bn, "producer_id", self->producer_id);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_acpi_phat_version_element_parse (FuFirmware *firmware,
|
||||
GBytes *fw,
|
||||
guint64 addr_start,
|
||||
guint64 addr_end,
|
||||
FwupdInstallFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
FuAcpiPhatVersionElement *self = FU_ACPI_PHAT_VERSION_ELEMENT (firmware);
|
||||
fwupd_guid_t component_id = { 0x0 };
|
||||
gchar producer_id[4] = { '\0' };
|
||||
gsize bufsz = 0;
|
||||
guint64 version_value = 0;
|
||||
g_autofree gchar *guid_str = NULL;
|
||||
const guint8 *buf = g_bytes_get_data (fw, &bufsz);
|
||||
|
||||
/* hardcoded */
|
||||
fu_firmware_set_size (firmware, 28);
|
||||
|
||||
if (!fu_memcpy_safe ((guint8 *) &component_id, sizeof(component_id), 0x0, /* dst */
|
||||
buf, bufsz, 0, /* src */
|
||||
sizeof(component_id), error))
|
||||
return FALSE;
|
||||
self->guid = fwupd_guid_to_string (&component_id, FWUPD_GUID_FLAG_MIXED_ENDIAN);
|
||||
|
||||
if (!fu_common_read_uint64_safe (buf, bufsz, 16, &version_value,
|
||||
G_LITTLE_ENDIAN, error))
|
||||
return FALSE;
|
||||
fu_firmware_set_version_raw (firmware, version_value);
|
||||
if (!fu_memcpy_safe ((guint8 *) producer_id, sizeof(producer_id), 0x0, /* dst */
|
||||
buf, bufsz, 24, /* src */
|
||||
sizeof(producer_id), error))
|
||||
return FALSE;
|
||||
if (memcmp (producer_id, "\0\0\0\0", 4) == 0) {
|
||||
g_set_error (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
"PHAT version element invalid");
|
||||
return FALSE;
|
||||
}
|
||||
self->producer_id = fu_common_strsafe ((const gchar *) producer_id,
|
||||
sizeof(producer_id));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
fu_acpi_phat_version_element_write (FuFirmware *firmware, GError **error)
|
||||
{
|
||||
FuAcpiPhatVersionElement *self = FU_ACPI_PHAT_VERSION_ELEMENT (firmware);
|
||||
fwupd_guid_t guid = { 0x0 };
|
||||
guint8 producer_id[4] = { '\0' };
|
||||
g_autoptr(GByteArray) buf = g_byte_array_new ();
|
||||
|
||||
/* component ID */
|
||||
if (self->guid != NULL) {
|
||||
if (!fwupd_guid_from_string (self->guid, &guid,
|
||||
FWUPD_GUID_FLAG_MIXED_ENDIAN,
|
||||
error))
|
||||
return NULL;
|
||||
}
|
||||
g_byte_array_append (buf, guid, sizeof(guid));
|
||||
|
||||
/* version value */
|
||||
fu_byte_array_append_uint64 (buf,
|
||||
fu_firmware_get_version_raw (firmware),
|
||||
G_LITTLE_ENDIAN);
|
||||
|
||||
/* producer ID */
|
||||
if (self->producer_id != NULL) {
|
||||
gsize producer_idsz = strlen (self->producer_id);
|
||||
if (!fu_memcpy_safe (producer_id, sizeof(producer_id), 0x0, /* dst */
|
||||
(const guint8 *) self->producer_id, producer_idsz, 0x0, /* src */
|
||||
producer_idsz, error))
|
||||
return NULL;
|
||||
}
|
||||
g_byte_array_append (buf, producer_id, sizeof(producer_id));
|
||||
|
||||
/* success */
|
||||
return g_byte_array_free_to_bytes (g_steal_pointer (&buf));
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_version_element_set_guid (FuAcpiPhatVersionElement *self,
|
||||
const gchar *guid)
|
||||
{
|
||||
g_free (self->guid);
|
||||
self->guid = g_strdup (guid);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_version_element_set_producer_id (FuAcpiPhatVersionElement *self,
|
||||
const gchar *producer_id)
|
||||
{
|
||||
g_free (self->producer_id);
|
||||
self->producer_id = g_strdup (producer_id);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_acpi_phat_version_element_build (FuFirmware *firmware, XbNode *n, GError **error)
|
||||
{
|
||||
FuAcpiPhatVersionElement *self = FU_ACPI_PHAT_VERSION_ELEMENT (firmware);
|
||||
const gchar *tmp;
|
||||
|
||||
/* optional properties */
|
||||
tmp = xb_node_query_text (n, "producer_id", NULL);
|
||||
if (tmp != NULL)
|
||||
fu_acpi_phat_version_element_set_producer_id (self, tmp);
|
||||
tmp = xb_node_query_text (n, "guid", NULL);
|
||||
if (tmp != NULL)
|
||||
fu_acpi_phat_version_element_set_guid (self, tmp);
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_version_element_init (FuAcpiPhatVersionElement *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_version_element_finalize (GObject *object)
|
||||
{
|
||||
FuAcpiPhatVersionElement *self = FU_ACPI_PHAT_VERSION_ELEMENT (object);
|
||||
g_free (self->guid);
|
||||
g_free (self->producer_id);
|
||||
G_OBJECT_CLASS (fu_acpi_phat_version_element_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_version_element_class_init (FuAcpiPhatVersionElementClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass);
|
||||
object_class->finalize = fu_acpi_phat_version_element_finalize;
|
||||
klass_firmware->parse = fu_acpi_phat_version_element_parse;
|
||||
klass_firmware->write = fu_acpi_phat_version_element_write;
|
||||
klass_firmware->export = fu_acpi_phat_version_element_export;
|
||||
klass_firmware->build = fu_acpi_phat_version_element_build;
|
||||
}
|
||||
|
||||
FuFirmware *
|
||||
fu_acpi_phat_version_element_new (void)
|
||||
{
|
||||
return FU_FIRMWARE (g_object_new (FU_TYPE_ACPI_PHAT_VERSION_ELEMENT, NULL));
|
||||
}
|
14
plugins/acpi-phat/fu-acpi-phat-version-element.h
Normal file
14
plugins/acpi-phat/fu-acpi-phat-version-element.h
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fu-firmware.h"
|
||||
|
||||
#define FU_TYPE_ACPI_PHAT_VERSION_ELEMENT (fu_acpi_phat_version_element_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (FuAcpiPhatVersionElement, fu_acpi_phat_version_element, FU, ACPI_PHAT_VERSION_ELEMENT, FuFirmware)
|
||||
|
||||
FuFirmware *fu_acpi_phat_version_element_new (void);
|
103
plugins/acpi-phat/fu-acpi-phat-version-record.c
Normal file
103
plugins/acpi-phat/fu-acpi-phat-version-record.c
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "fu-common.h"
|
||||
#include "fu-firmware-common.h"
|
||||
#include "fu-acpi-phat.h"
|
||||
#include "fu-acpi-phat-version-element.h"
|
||||
#include "fu-acpi-phat-version-record.h"
|
||||
|
||||
struct _FuAcpiPhatVersionRecord {
|
||||
FuFirmware parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (FuAcpiPhatVersionRecord, fu_acpi_phat_version_record, FU_TYPE_FIRMWARE)
|
||||
|
||||
static gboolean
|
||||
fu_acpi_phat_version_record_parse (FuFirmware *firmware,
|
||||
GBytes *fw,
|
||||
guint64 addr_start,
|
||||
guint64 addr_end,
|
||||
FwupdInstallFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
gsize bufsz = 0;
|
||||
gsize offset = 0;
|
||||
guint32 record_count = 0;
|
||||
const guint8 *buf = g_bytes_get_data (fw, &bufsz);
|
||||
|
||||
if (!fu_common_read_uint32_safe (buf, bufsz, offset + 8, &record_count,
|
||||
G_LITTLE_ENDIAN, error))
|
||||
return FALSE;
|
||||
for (guint32 i = 0; i < record_count; i++) {
|
||||
g_autoptr(FuFirmware) firmware_tmp = fu_acpi_phat_version_element_new ();
|
||||
g_autoptr(GBytes) fw_tmp = NULL;
|
||||
fw_tmp = fu_common_bytes_new_offset (fw, offset + 12, 28, error);
|
||||
if (fw_tmp == NULL)
|
||||
return FALSE;
|
||||
fu_firmware_set_offset (firmware_tmp, offset + 12);
|
||||
if (!fu_firmware_parse (firmware_tmp, fw_tmp, flags, error))
|
||||
return FALSE;
|
||||
fu_firmware_add_image (firmware, firmware_tmp);
|
||||
offset += fu_firmware_get_size (firmware_tmp);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
fu_acpi_phat_version_record_write (FuFirmware *firmware, GError **error)
|
||||
{
|
||||
g_autoptr(GByteArray) buf = g_byte_array_new ();
|
||||
g_autoptr(GByteArray) buf2 = g_byte_array_new ();
|
||||
g_autoptr(GPtrArray) images = fu_firmware_get_images (firmware);
|
||||
|
||||
/* write each element so we get the image size */
|
||||
for (guint i = 0; i < images->len; i++) {
|
||||
FuFirmware *img = g_ptr_array_index (images, i);
|
||||
g_autoptr(GBytes) blob = fu_firmware_write (img, error);
|
||||
if (blob == NULL)
|
||||
return NULL;
|
||||
fu_byte_array_append_bytes (buf2, blob);
|
||||
}
|
||||
|
||||
/* data record */
|
||||
fu_byte_array_append_uint16 (buf,
|
||||
FU_ACPI_PHAT_RECORD_TYPE_VERSION,
|
||||
G_LITTLE_ENDIAN);
|
||||
fu_byte_array_append_uint16 (buf, 12 + buf2->len, G_LITTLE_ENDIAN);
|
||||
fu_byte_array_append_uint8 (buf, fu_firmware_get_version_raw (firmware));
|
||||
fu_byte_array_append_uint8 (buf, 0x00);
|
||||
fu_byte_array_append_uint8 (buf, 0x00);
|
||||
fu_byte_array_append_uint8 (buf, 0x00);
|
||||
fu_byte_array_append_uint32 (buf, images->len, G_LITTLE_ENDIAN);
|
||||
|
||||
/* element data */
|
||||
g_byte_array_append (buf, buf2->data, buf2->len);
|
||||
return g_byte_array_free_to_bytes (g_steal_pointer (&buf));
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_version_record_init (FuAcpiPhatVersionRecord *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_version_record_class_init (FuAcpiPhatVersionRecordClass *klass)
|
||||
{
|
||||
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass);
|
||||
klass_firmware->parse = fu_acpi_phat_version_record_parse;
|
||||
klass_firmware->write = fu_acpi_phat_version_record_write;
|
||||
}
|
||||
|
||||
FuFirmware *
|
||||
fu_acpi_phat_version_record_new (void)
|
||||
{
|
||||
return FU_FIRMWARE (g_object_new (FU_TYPE_ACPI_PHAT_VERSION_RECORD, NULL));
|
||||
}
|
14
plugins/acpi-phat/fu-acpi-phat-version-record.h
Normal file
14
plugins/acpi-phat/fu-acpi-phat-version-record.h
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fu-firmware.h"
|
||||
|
||||
#define FU_TYPE_ACPI_PHAT_VERSION_RECORD (fu_acpi_phat_version_record_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (FuAcpiPhatVersionRecord, fu_acpi_phat_version_record, FU, ACPI_PHAT_VERSION_RECORD, FuFirmware)
|
||||
|
||||
FuFirmware *fu_acpi_phat_version_record_new (void);
|
327
plugins/acpi-phat/fu-acpi-phat.c
Normal file
327
plugins/acpi-phat/fu-acpi-phat.c
Normal file
@ -0,0 +1,327 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "fu-common.h"
|
||||
#include "fu-firmware-common.h"
|
||||
#include "fu-acpi-phat.h"
|
||||
#include "fu-acpi-phat-health-record.h"
|
||||
#include "fu-acpi-phat-version-record.h"
|
||||
|
||||
struct _FuAcpiPhat {
|
||||
FuFirmware parent_instance;
|
||||
gchar *oem_id;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (FuAcpiPhat, fu_acpi_phat, FU_TYPE_FIRMWARE)
|
||||
|
||||
static void
|
||||
fu_acpi_phat_export (FuFirmware *firmware,
|
||||
FuFirmwareExportFlags flags,
|
||||
XbBuilderNode *bn)
|
||||
{
|
||||
FuAcpiPhat *self = FU_ACPI_PHAT (firmware);
|
||||
if (self->oem_id != NULL)
|
||||
fu_xmlb_builder_insert_kv (bn, "oem_id", self->oem_id);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_acpi_phat_record_parse (FuFirmware *firmware,
|
||||
GBytes *fw,
|
||||
gsize *offset,
|
||||
FwupdInstallFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
gsize bufsz = 0;
|
||||
guint16 record_length = 0;
|
||||
guint16 record_type = 0;
|
||||
guint8 revision;
|
||||
const guint8 *buf = g_bytes_get_data (fw, &bufsz);
|
||||
g_autoptr(FuFirmware) firmware_rcd = NULL;
|
||||
|
||||
/* common header */
|
||||
if (!fu_common_read_uint16_safe (buf, bufsz, *offset, &record_type,
|
||||
G_LITTLE_ENDIAN, error))
|
||||
return FALSE;
|
||||
if (!fu_common_read_uint16_safe (buf, bufsz, *offset + 2, &record_length,
|
||||
G_LITTLE_ENDIAN, error))
|
||||
return FALSE;
|
||||
if (record_length < 5) {
|
||||
g_set_error (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
"PHAT record length invalid, got 0x%x",
|
||||
record_length);
|
||||
return FALSE;
|
||||
}
|
||||
if (!fu_common_read_uint8_safe (buf, bufsz, *offset + 4, &revision, error))
|
||||
return FALSE;
|
||||
|
||||
/* firmware version data record */
|
||||
if (record_type == FU_ACPI_PHAT_RECORD_TYPE_VERSION) {
|
||||
firmware_rcd = fu_acpi_phat_version_record_new ();
|
||||
} else if (record_type == FU_ACPI_PHAT_RECORD_TYPE_HEALTH) {
|
||||
firmware_rcd = fu_acpi_phat_health_record_new ();
|
||||
}
|
||||
|
||||
/* supported record type */
|
||||
if (firmware_rcd != NULL) {
|
||||
g_autoptr(GBytes) fw_tmp = NULL;
|
||||
fw_tmp = fu_common_bytes_new_offset (fw, *offset, record_length, error);
|
||||
if (fw_tmp == NULL)
|
||||
return FALSE;
|
||||
fu_firmware_set_size (firmware_rcd, record_length);
|
||||
fu_firmware_set_offset (firmware_rcd, *offset);
|
||||
fu_firmware_set_version_raw (firmware_rcd, revision);
|
||||
if (!fu_firmware_parse (firmware_rcd, fw_tmp, flags, error))
|
||||
return FALSE;
|
||||
fu_firmware_add_image (firmware, firmware_rcd);
|
||||
}
|
||||
|
||||
*offset += record_length;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_set_oem_id (FuAcpiPhat *self, const gchar *oem_id)
|
||||
{
|
||||
g_free (self->oem_id);
|
||||
self->oem_id = g_strdup (oem_id);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_acpi_phat_parse (FuFirmware *firmware,
|
||||
GBytes *fw,
|
||||
guint64 addr_start,
|
||||
guint64 addr_end,
|
||||
FwupdInstallFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
FuAcpiPhat *self = FU_ACPI_PHAT (firmware);
|
||||
gchar oem_id[6] = { '\0' };
|
||||
gchar oem_table_id[8] = { '\0' };
|
||||
gchar signature[4] = { '\0' };
|
||||
gsize bufsz = 0;
|
||||
guint32 length = 0;
|
||||
guint32 oem_revision = 0;
|
||||
const guint8 *buf = g_bytes_get_data (fw, &bufsz);
|
||||
g_autofree gchar *oem_id_safe = NULL;
|
||||
g_autofree gchar *oem_table_id_safe = NULL;
|
||||
|
||||
/* parse table */
|
||||
if (!fu_memcpy_safe ((guint8 *) signature, sizeof(signature), 0x0, /* dst */
|
||||
buf, bufsz, 0x00, /* src */
|
||||
sizeof(signature), error))
|
||||
return FALSE;
|
||||
if (memcmp (signature, "PHAT", 4) != 0) {
|
||||
g_set_error (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
"Not a PHAT table, got %s",
|
||||
signature);
|
||||
return FALSE;
|
||||
}
|
||||
if (!fu_common_read_uint32_safe (buf, bufsz, 4, &length, G_LITTLE_ENDIAN, error))
|
||||
return FALSE;
|
||||
if (bufsz < length) {
|
||||
g_set_error (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
"PHAT table invalid size, got 0x%x, expected 0x%x",
|
||||
(guint) bufsz, length);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* spec revision */
|
||||
if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
|
||||
guint8 revision = 0;
|
||||
if (!fu_common_read_uint8_safe (buf, bufsz, 8, &revision, error))
|
||||
return FALSE;
|
||||
if (revision != FU_ACPI_PHAT_REVISION) {
|
||||
g_set_error (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
"PHAT table revision invalid, got 0x%x, expected 0x%x",
|
||||
revision, (guint) FU_ACPI_PHAT_REVISION);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* verify checksum */
|
||||
if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) {
|
||||
guint8 checksum = 0;
|
||||
for (gsize i = 0; i < length; i++)
|
||||
checksum += buf[i];
|
||||
if (checksum != 0x00) {
|
||||
g_set_error (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
"PHAT table checksum invalid, got 0x%x",
|
||||
checksum);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* OEMID */
|
||||
if (!fu_memcpy_safe ((guint8 *) oem_id, sizeof(oem_id), 0x0, /* dst */
|
||||
buf, bufsz, 10, /* src */
|
||||
sizeof(oem_id), error))
|
||||
return FALSE;
|
||||
oem_id_safe = fu_common_strsafe ((const gchar *) oem_id, sizeof(oem_id));
|
||||
fu_acpi_phat_set_oem_id (self, oem_id_safe);
|
||||
|
||||
/* OEM Table ID */
|
||||
if (!fu_memcpy_safe ((guint8 *) oem_table_id, sizeof(oem_table_id), 0x0, /* dst */
|
||||
buf, bufsz, 16, /* src */
|
||||
sizeof(oem_table_id), error))
|
||||
return FALSE;
|
||||
oem_table_id_safe = fu_common_strsafe ((const gchar *) oem_table_id, sizeof(oem_table_id));
|
||||
fu_firmware_set_id (firmware, oem_table_id_safe);
|
||||
if (!fu_common_read_uint32_safe (buf, bufsz, 24, &oem_revision, G_LITTLE_ENDIAN, error))
|
||||
return FALSE;
|
||||
fu_firmware_set_version_raw (firmware, oem_revision);
|
||||
|
||||
/* platform telemetry records */
|
||||
for (gsize offset = 36; offset < length;) {
|
||||
if (!fu_acpi_phat_record_parse (firmware, fw, &offset, flags, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
fu_acpi_phat_write (FuFirmware *firmware, GError **error)
|
||||
{
|
||||
FuAcpiPhat *self = FU_ACPI_PHAT (firmware);
|
||||
const gchar *oem_table_id_str = fu_firmware_get_id (firmware);
|
||||
guint8 checksum = 0;
|
||||
guint8 creator_id[] = { 'F', 'W', 'U', 'P' };
|
||||
guint8 creator_rev[] = { '0', '0', '0', '0' };
|
||||
guint8 oem_id[6] = { '\0' };
|
||||
guint8 oem_table_id[8] = { '\0' };
|
||||
guint8 signature[] = { 'P', 'H', 'A', 'T' };
|
||||
g_autoptr(GByteArray) buf = g_byte_array_new ();
|
||||
g_autoptr(GByteArray) buf2 = g_byte_array_new ();
|
||||
g_autoptr(GPtrArray) images = fu_firmware_get_images (firmware);
|
||||
|
||||
/* write each image so we get the total size */
|
||||
for (guint i = 0; i < images->len; i++) {
|
||||
FuFirmware *img = g_ptr_array_index (images, i);
|
||||
g_autoptr(GBytes) blob = fu_firmware_write (img, error);
|
||||
if (blob == NULL)
|
||||
return NULL;
|
||||
fu_byte_array_append_bytes (buf2, blob);
|
||||
}
|
||||
|
||||
/* header */
|
||||
g_byte_array_append (buf, signature, sizeof(signature));
|
||||
fu_byte_array_append_uint32 (buf, buf2->len + 36, G_LITTLE_ENDIAN);
|
||||
fu_byte_array_append_uint8 (buf, fu_firmware_get_version_raw (firmware));
|
||||
fu_byte_array_append_uint8 (buf, 0xFF); /* will fixup */
|
||||
if (self->oem_id != NULL) {
|
||||
gsize oem_id_strlen = strlen (self->oem_id);
|
||||
if (!fu_memcpy_safe (oem_id, sizeof(oem_id), 0x0, /* dst */
|
||||
(const guint8 *) self->oem_id, oem_id_strlen, 0x0, /* src */
|
||||
oem_id_strlen, error))
|
||||
return NULL;
|
||||
}
|
||||
g_byte_array_append (buf, oem_id, sizeof(oem_id));
|
||||
if (oem_table_id_str != NULL) {
|
||||
gsize oem_table_id_strlen = strlen (oem_table_id_str);
|
||||
if (!fu_memcpy_safe (oem_table_id, sizeof(oem_table_id), 0x0, /* dst */
|
||||
(const guint8 *) oem_table_id_str, oem_table_id_strlen, 0x0, /* src */
|
||||
oem_table_id_strlen, error))
|
||||
return NULL;
|
||||
}
|
||||
g_byte_array_append (buf, oem_table_id, sizeof(oem_table_id));
|
||||
fu_byte_array_append_uint32 (buf, fu_firmware_get_version_raw (firmware), G_LITTLE_ENDIAN);
|
||||
g_byte_array_append (buf, creator_id, sizeof(creator_id));
|
||||
g_byte_array_append (buf, creator_rev, sizeof(creator_rev));
|
||||
g_byte_array_append (buf, buf2->data, buf2->len);
|
||||
|
||||
/* fixup checksum */
|
||||
for (gsize i = 0; i < buf->len; i++)
|
||||
checksum += buf->data[i];
|
||||
buf->data[9] = 0xFF - checksum;
|
||||
|
||||
/* success */
|
||||
return g_byte_array_free_to_bytes (g_steal_pointer (&buf));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_acpi_phat_build (FuFirmware *firmware, XbNode *n, GError **error)
|
||||
{
|
||||
FuAcpiPhat *self = FU_ACPI_PHAT (firmware);
|
||||
const gchar *tmp;
|
||||
|
||||
/* optional properties */
|
||||
tmp = xb_node_query_text (n, "oem_id", NULL);
|
||||
if (tmp != NULL)
|
||||
fu_acpi_phat_set_oem_id (self, tmp);
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_acpi_phat_to_report_string_cb (XbBuilderNode *bn, gpointer user_data)
|
||||
{
|
||||
if (g_strcmp0 (xb_builder_node_get_element (bn), "offset") == 0 ||
|
||||
g_strcmp0 (xb_builder_node_get_element (bn), "flags") == 0 ||
|
||||
g_strcmp0 (xb_builder_node_get_element (bn), "size") == 0)
|
||||
xb_builder_node_add_flag (bn, XB_BUILDER_NODE_FLAG_IGNORE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gchar *
|
||||
fu_acpi_phat_to_report_string (FuAcpiPhat *self)
|
||||
{
|
||||
g_autoptr(XbBuilderNode) bn = xb_builder_node_new ("firmware");
|
||||
fu_firmware_export (FU_FIRMWARE (self), FU_FIRMWARE_EXPORT_FLAG_NONE, bn);
|
||||
xb_builder_node_traverse (bn, G_PRE_ORDER, G_TRAVERSE_ALL, 3,
|
||||
fu_acpi_phat_to_report_string_cb, NULL);
|
||||
return xb_builder_node_export (bn,
|
||||
XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE |
|
||||
XB_NODE_EXPORT_FLAG_FORMAT_INDENT,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_init (FuAcpiPhat *self)
|
||||
{
|
||||
fu_firmware_add_flag (FU_FIRMWARE (self), FU_FIRMWARE_FLAG_HAS_CHECKSUM);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_finalize (GObject *object)
|
||||
{
|
||||
FuAcpiPhat *self = FU_ACPI_PHAT (object);
|
||||
g_free (self->oem_id);
|
||||
G_OBJECT_CLASS (fu_acpi_phat_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_acpi_phat_class_init (FuAcpiPhatClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass);
|
||||
object_class->finalize = fu_acpi_phat_finalize;
|
||||
klass_firmware->parse = fu_acpi_phat_parse;
|
||||
klass_firmware->write = fu_acpi_phat_write;
|
||||
klass_firmware->export = fu_acpi_phat_export;
|
||||
klass_firmware->build = fu_acpi_phat_build;
|
||||
}
|
||||
|
||||
FuFirmware *
|
||||
fu_acpi_phat_new (void)
|
||||
{
|
||||
return FU_FIRMWARE (g_object_new (FU_TYPE_ACPI_PHAT, NULL));
|
||||
}
|
19
plugins/acpi-phat/fu-acpi-phat.h
Normal file
19
plugins/acpi-phat/fu-acpi-phat.h
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fu-firmware.h"
|
||||
|
||||
#define FU_TYPE_ACPI_PHAT (fu_acpi_phat_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (FuAcpiPhat, fu_acpi_phat, FU, ACPI_PHAT, FuFirmware)
|
||||
|
||||
#define FU_ACPI_PHAT_RECORD_TYPE_VERSION 0x0000
|
||||
#define FU_ACPI_PHAT_RECORD_TYPE_HEALTH 0x0001
|
||||
#define FU_ACPI_PHAT_REVISION 0x01
|
||||
|
||||
FuFirmware *fu_acpi_phat_new (void);
|
||||
gchar *fu_acpi_phat_to_report_string (FuAcpiPhat *self);
|
44
plugins/acpi-phat/fu-plugin-acpi-phat.c
Normal file
44
plugins/acpi-phat/fu-plugin-acpi-phat.c
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "fu-acpi-phat.h"
|
||||
#include "fu-acpi-phat-health-record.h"
|
||||
#include "fu-acpi-phat-version-element.h"
|
||||
#include "fu-acpi-phat-version-record.h"
|
||||
#include "fu-plugin-vfuncs.h"
|
||||
|
||||
void
|
||||
fu_plugin_init (FuPlugin *plugin)
|
||||
{
|
||||
fu_plugin_set_build_hash (plugin, FU_BUILD_HASH);
|
||||
fu_plugin_add_firmware_gtype (plugin, NULL, FU_TYPE_ACPI_PHAT);
|
||||
fu_plugin_add_firmware_gtype (plugin, NULL, FU_TYPE_ACPI_PHAT_HEALTH_RECORD);
|
||||
fu_plugin_add_firmware_gtype (plugin, NULL, FU_TYPE_ACPI_PHAT_VERSION_ELEMENT);
|
||||
fu_plugin_add_firmware_gtype (plugin, NULL, FU_TYPE_ACPI_PHAT_VERSION_RECORD);
|
||||
}
|
||||
|
||||
gboolean
|
||||
fu_plugin_coldplug (FuPlugin *plugin, GError **error)
|
||||
{
|
||||
g_autofree gchar *path = NULL;
|
||||
g_autofree gchar *fn = NULL;
|
||||
g_autofree gchar *str = NULL;
|
||||
g_autoptr(FuFirmware) phat = fu_acpi_phat_new ();
|
||||
g_autoptr(GBytes) blob = NULL;
|
||||
|
||||
path = fu_common_get_path (FU_PATH_KIND_ACPI_TABLES);
|
||||
fn = g_build_filename (path, "PHAT", NULL);
|
||||
blob = fu_common_get_contents_bytes (fn, error);
|
||||
if (blob == NULL)
|
||||
return FALSE;
|
||||
if (!fu_firmware_parse (phat, blob, FWUPD_INSTALL_FLAG_NONE, error))
|
||||
return FALSE;
|
||||
str = fu_acpi_phat_to_report_string (FU_ACPI_PHAT (phat));
|
||||
fu_plugin_add_report_metadata (plugin, "PHAT", str);
|
||||
return TRUE;
|
||||
}
|
53
plugins/acpi-phat/fu-self-test.c
Normal file
53
plugins/acpi-phat/fu-self-test.c
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <fwupd.h>
|
||||
|
||||
#include "fu-common.h"
|
||||
|
||||
#include "fu-acpi-phat.h"
|
||||
|
||||
static void
|
||||
fu_acpi_phat_parse_func (void)
|
||||
{
|
||||
gboolean ret;
|
||||
g_autoptr(FuFirmware) phat = fu_acpi_phat_new ();
|
||||
g_autoptr(GError) error = NULL;
|
||||
g_autoptr(GBytes) blob = NULL;
|
||||
g_autofree gchar *str = NULL;
|
||||
g_autofree gchar *fn = NULL;
|
||||
|
||||
fn = g_test_build_filename (G_TEST_DIST, "tests", "PHAT", NULL);
|
||||
if (!g_file_test (fn, G_FILE_TEST_EXISTS)) {
|
||||
g_test_skip ("missing PHAT");
|
||||
return;
|
||||
}
|
||||
blob = fu_common_get_contents_bytes (fn, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_nonnull (blob);
|
||||
ret = fu_firmware_parse (phat, blob, FWUPD_INSTALL_FLAG_FORCE, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_true (ret);
|
||||
str = fu_acpi_phat_to_report_string (FU_ACPI_PHAT (phat));
|
||||
g_print ("%s\n", str);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
/* only critical and error are fatal */
|
||||
g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
|
||||
g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
|
||||
|
||||
/* tests go here */
|
||||
g_test_add_func ("/acpi-phat/parse", fu_acpi_phat_parse_func);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
61
plugins/acpi-phat/meson.build
Normal file
61
plugins/acpi-phat/meson.build
Normal file
@ -0,0 +1,61 @@
|
||||
if host_machine.system() == 'linux'
|
||||
cargs = ['-DG_LOG_DOMAIN="FuPluginAcpiPhat"']
|
||||
|
||||
shared_module('fu_plugin_acpi_phat',
|
||||
fu_hash,
|
||||
sources : [
|
||||
'fu-plugin-acpi-phat.c',
|
||||
'fu-acpi-phat.c', # fuzzing
|
||||
'fu-acpi-phat-health-record.c', # fuzzing
|
||||
'fu-acpi-phat-version-element.c', # fuzzing
|
||||
'fu-acpi-phat-version-record.c', # fuzzing
|
||||
],
|
||||
include_directories : [
|
||||
root_incdir,
|
||||
fwupd_incdir,
|
||||
fwupdplugin_incdir,
|
||||
],
|
||||
install : true,
|
||||
install_dir: plugin_dir,
|
||||
link_with : [
|
||||
fwupd,
|
||||
fwupdplugin,
|
||||
],
|
||||
c_args : cargs,
|
||||
dependencies : [
|
||||
plugin_deps,
|
||||
],
|
||||
)
|
||||
|
||||
if get_option('tests')
|
||||
testdatadirs = environment()
|
||||
testdatadirs.set('G_TEST_SRCDIR', meson.current_source_dir())
|
||||
testdatadirs.set('G_TEST_BUILDDIR', meson.current_build_dir())
|
||||
e = executable(
|
||||
'acpi-phat-self-test',
|
||||
fu_hash,
|
||||
sources : [
|
||||
'fu-self-test.c',
|
||||
'fu-acpi-phat.c',
|
||||
'fu-acpi-phat-health-record.c',
|
||||
'fu-acpi-phat-version-element.c',
|
||||
'fu-acpi-phat-version-record.c',
|
||||
],
|
||||
include_directories : [
|
||||
root_incdir,
|
||||
fwupd_incdir,
|
||||
fwupdplugin_incdir,
|
||||
],
|
||||
dependencies : [
|
||||
plugin_deps,
|
||||
],
|
||||
link_with : [
|
||||
fwupd,
|
||||
fwupdplugin,
|
||||
],
|
||||
install : true,
|
||||
install_dir : installed_test_bindir,
|
||||
)
|
||||
test('acpi-phat-self-test', e, env : testdatadirs) # added to installed-tests
|
||||
endif
|
||||
endif
|
@ -1,5 +1,6 @@
|
||||
subdir('acpi-dmar')
|
||||
subdir('acpi-facp')
|
||||
subdir('acpi-phat')
|
||||
subdir('altos')
|
||||
subdir('amt')
|
||||
subdir('analogix')
|
||||
|
@ -43,6 +43,7 @@ fu_plugin_init (FuPlugin *plugin)
|
||||
fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "tpm_eventlog");
|
||||
fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "dell");
|
||||
fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "linux_lockdown");
|
||||
fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "acpi_phat");
|
||||
fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_CONFLICTS, "uefi"); /* old name */
|
||||
fu_plugin_set_build_hash (plugin, FU_BUILD_HASH);
|
||||
}
|
||||
|
35
src/fuzzing/acpi-phat.builder.xml
Normal file
35
src/fuzzing/acpi-phat.builder.xml
Normal file
@ -0,0 +1,35 @@
|
||||
<firmware gtype="FuAcpiPhat">
|
||||
<id>SUDODAVE</id>
|
||||
<version_raw>0x1</version_raw>
|
||||
<oem_id>HUGHES</oem_id>
|
||||
<firmware gtype="FuAcpiPhatVersionRecord">
|
||||
<version_raw>0x1</version_raw>
|
||||
<firmware gtype="FuAcpiPhatVersionElement">
|
||||
<version_raw>0x123</version_raw>
|
||||
<guid>40338ceb-b966-4eae-adae-9c32edfcc484</guid>
|
||||
<producer_id>HUGH</producer_id>
|
||||
</firmware>
|
||||
<firmware gtype="FuAcpiPhatVersionElement">
|
||||
<version_raw>0x124</version_raw>
|
||||
<guid>2082b5e0-7a64-478a-b1b2-e3404fab6dad</guid>
|
||||
<producer_id>LENO</producer_id>
|
||||
</firmware>
|
||||
</firmware>
|
||||
<firmware gtype="FuAcpiPhatVersionRecord">
|
||||
<version_raw>0x1</version_raw>
|
||||
<firmware gtype="FuAcpiPhatVersionElement">
|
||||
<version_raw>0x125</version_raw>
|
||||
<guid>dfbaaded-754b-5214-a5f2-46aa3331e8ce</guid>
|
||||
<producer_id>DELL</producer_id>
|
||||
</firmware>
|
||||
</firmware>
|
||||
<firmware gtype="FuAcpiPhatVersionRecord">
|
||||
<version_raw>0x1</version_raw>
|
||||
</firmware>
|
||||
<firmware gtype="FuAcpiPhatHealthRecord">
|
||||
<version_raw>0x1</version_raw>
|
||||
<am_healthy>0x1</am_healthy>
|
||||
<guid>dfbaaded-754b-5214-a5f2-46aa3331e8ce</guid>
|
||||
<device_path>/dev/foo</device_path>
|
||||
</firmware>
|
||||
</firmware>
|
BIN
src/fuzzing/firmware/acpi-phat.bin
Normal file
BIN
src/fuzzing/firmware/acpi-phat.bin
Normal file
Binary file not shown.
@ -13,6 +13,7 @@ import subprocess
|
||||
if __name__ == "__main__":
|
||||
|
||||
for fn_src, fn_dst in [
|
||||
("acpi-phat.builder.xml", "acpi-phat.bin"),
|
||||
("bcm57xx.builder.xml", "bcm57xx.bin"),
|
||||
("ccgx.builder.xml", "ccgx.cyacd"),
|
||||
("ccgx-dmc.builder.xml", "ccgx-dmc.bin"),
|
||||
@ -45,6 +46,7 @@ if __name__ == "__main__":
|
||||
print("INFO: converting {} into {}".format(fn_src, fn_dst))
|
||||
try:
|
||||
argv = [
|
||||
"sudo",
|
||||
"../../build/src/fwupdtool",
|
||||
"firmware-build",
|
||||
fn_src,
|
||||
|
Loading…
Reference in New Issue
Block a user