Coverage Report

Created: 2026-08-15 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/plugins/acpi-phat/fu-acpi-phat-health-record.c
Line
Count
Source
1
/*
2
 * Copyright 2021 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#include "config.h"
8
9
#include "fu-acpi-phat-health-record.h"
10
#include "fu-acpi-phat-struct.h"
11
#include "fu-acpi-phat.h"
12
13
struct _FuAcpiPhatHealthRecord {
14
  FuFirmware parent_instance;
15
  guint8 am_healthy;
16
  gchar *guid;
17
  gchar *device_path;
18
};
19
20
5.13k
G_DEFINE_TYPE(FuAcpiPhatHealthRecord, fu_acpi_phat_health_record, FU_TYPE_FIRMWARE)
21
5.13k
22
5.13k
static void
23
5.13k
fu_acpi_phat_health_record_export(FuFirmware *firmware,
24
5.13k
          FuFirmwareExportFlags flags,
25
5.13k
          XbBuilderNode *bn)
26
5.13k
{
27
0
  FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(firmware);
28
0
  if (self->guid != NULL)
29
0
    fu_xmlb_builder_insert_kv(bn, "guid", self->guid);
30
0
  if (self->device_path != NULL)
31
0
    fu_xmlb_builder_insert_kv(bn, "device_path", self->device_path);
32
0
  if (self->am_healthy != 0)
33
0
    fu_xmlb_builder_insert_kx(bn, "am_healthy", self->am_healthy);
34
0
}
35
36
static gboolean
37
fu_acpi_phat_health_record_parse(FuFirmware *firmware,
38
         FuInputStream *stream,
39
         FuFirmwareParseFlags flags,
40
         GError **error)
41
1.36k
{
42
1.36k
  FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(firmware);
43
1.36k
  gsize streamsz = 0;
44
1.36k
  guint16 rcdlen;
45
1.36k
  guint32 dataoff;
46
1.36k
  g_autoptr(FuStructAcpiPhatHealthRecord) st = NULL;
47
48
  /* sanity check record length */
49
1.36k
  st = fu_struct_acpi_phat_health_record_parse_stream(stream, 0x0, error);
50
1.36k
  if (st == NULL)
51
21
    return FALSE;
52
1.34k
  rcdlen = fu_struct_acpi_phat_health_record_get_rcdlen(st);
53
1.34k
  if (!fu_input_stream_size(stream, &streamsz, error))
54
0
    return FALSE;
55
1.34k
  if (rcdlen != streamsz) {
56
0
    g_set_error(error,
57
0
          FWUPD_ERROR,
58
0
          FWUPD_ERROR_INVALID_DATA,
59
0
          "record length not valid: %" G_GUINT16_FORMAT,
60
0
          rcdlen);
61
0
    return FALSE;
62
0
  }
63
1.34k
  self->am_healthy = fu_struct_acpi_phat_health_record_get_flags(st);
64
1.34k
  self->guid =
65
1.34k
      fwupd_guid_to_string(fu_struct_acpi_phat_health_record_get_device_signature(st),
66
1.34k
         FWUPD_GUID_FLAG_MIXED_ENDIAN);
67
68
  /* device path */
69
1.34k
  dataoff = fu_struct_acpi_phat_health_record_get_device_specific_data(st);
70
1.34k
  if (streamsz > 28) {
71
932
    gsize ubufsz; /* bytes */
72
932
    g_autoptr(GBytes) ubuf = NULL;
73
74
    /* header -> devicepath -> data */
75
932
    if (dataoff == 0x0) {
76
94
      ubufsz = streamsz - 28;
77
838
    } else if (dataoff < 28) {
78
3
      g_set_error(error,
79
3
            FWUPD_ERROR,
80
3
            FWUPD_ERROR_INVALID_DATA,
81
3
            "device specific data offset not valid: 0x%x",
82
3
            dataoff);
83
3
      return FALSE;
84
835
    } else {
85
835
      ubufsz = dataoff - 28;
86
835
    }
87
929
    if (ubufsz == 0) {
88
1
      g_set_error(error,
89
1
            FWUPD_ERROR,
90
1
            FWUPD_ERROR_INVALID_DATA,
91
1
            "device path not valid: %zu",
92
1
            ubufsz);
93
1
      return FALSE;
94
1
    }
95
96
    /* align and convert */
97
928
    ubuf = fu_input_stream_read_bytes(stream, 28, ubufsz, NULL, error);
98
928
    if (ubuf == NULL)
99
0
      return FALSE;
100
928
    self->device_path = fu_utf16_to_utf8_bytes(ubuf, G_LITTLE_ENDIAN, error);
101
928
    if (self->device_path == NULL)
102
145
      return FALSE;
103
928
  }
104
105
  /* success */
106
1.19k
  return TRUE;
107
1.34k
}
108
109
static GByteArray *
110
fu_acpi_phat_health_record_write(FuFirmware *firmware, GError **error)
111
1.02k
{
112
1.02k
  FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(firmware);
113
1.02k
  g_autoptr(FuStructAcpiPhatHealthRecord) st = fu_struct_acpi_phat_health_record_new();
114
115
  /* convert device path ahead of time */
116
1.02k
  if (self->device_path != NULL) {
117
704
    g_autoptr(GByteArray) buf = fu_utf8_to_utf16_byte_array(self->device_path,
118
704
                  G_LITTLE_ENDIAN,
119
704
                  FU_UTF_CONVERT_FLAG_NONE,
120
704
                  error);
121
704
    if (buf == NULL)
122
0
      return NULL;
123
704
    g_byte_array_append(st->buf, buf->data, buf->len);
124
704
  }
125
126
  /* data record */
127
1.02k
  if (self->guid != NULL) {
128
1.02k
    fwupd_guid_t guid = {0x0};
129
1.02k
    if (!fwupd_guid_from_string(self->guid, &guid, FWUPD_GUID_FLAG_MIXED_ENDIAN, error))
130
0
      return NULL;
131
1.02k
    fu_struct_acpi_phat_health_record_set_device_signature(st, &guid);
132
1.02k
  }
133
1.02k
  fu_struct_acpi_phat_health_record_set_rcdlen(st, st->buf->len);
134
1.02k
  fu_struct_acpi_phat_health_record_set_version(st, fu_firmware_get_version_raw(firmware));
135
1.02k
  fu_struct_acpi_phat_health_record_set_flags(st, self->am_healthy);
136
137
  /* success */
138
1.02k
  return g_steal_pointer(&st->buf);
139
1.02k
}
140
141
static void
142
fu_acpi_phat_health_record_set_guid(FuAcpiPhatHealthRecord *self, const gchar *guid)
143
0
{
144
0
  g_free(self->guid);
145
0
  self->guid = g_strdup(guid);
146
0
}
147
148
static void
149
fu_acpi_phat_health_record_set_device_path(FuAcpiPhatHealthRecord *self, const gchar *device_path)
150
0
{
151
0
  g_free(self->device_path);
152
0
  self->device_path = g_strdup(device_path);
153
0
}
154
155
static gboolean
156
fu_acpi_phat_health_record_build(FuFirmware *firmware, XbNode *n, GError **error)
157
0
{
158
0
  FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(firmware);
159
0
  const gchar *tmp;
160
0
  guint64 tmp64;
161
162
  /* optional properties */
163
0
  tmp = xb_node_query_text(n, "device_path", NULL);
164
0
  if (tmp != NULL)
165
0
    fu_acpi_phat_health_record_set_device_path(self, tmp);
166
0
  tmp = xb_node_query_text(n, "guid", NULL);
167
0
  if (tmp != NULL)
168
0
    fu_acpi_phat_health_record_set_guid(self, tmp);
169
0
  tmp64 = xb_node_query_text_as_uint(n, "am_healthy", NULL);
170
0
  if (tmp64 != G_MAXUINT64) {
171
0
    if (tmp64 > G_MAXUINT8) {
172
0
      g_set_error(error,
173
0
            FWUPD_ERROR,
174
0
            FWUPD_ERROR_NOT_SUPPORTED,
175
0
            "am_healthy value invalid, got 0x%x",
176
0
            (guint)tmp64);
177
0
      return FALSE;
178
0
    }
179
0
    self->am_healthy = (guint8)tmp64;
180
0
  }
181
182
  /* success */
183
0
  return TRUE;
184
0
}
185
186
static void
187
fu_acpi_phat_health_record_init(FuAcpiPhatHealthRecord *self)
188
1.37k
{
189
1.37k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION);
190
1.37k
}
191
192
static void
193
fu_acpi_phat_health_record_finalize(GObject *object)
194
1.37k
{
195
1.37k
  FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(object);
196
1.37k
  g_free(self->guid);
197
1.37k
  g_free(self->device_path);
198
1.37k
  G_OBJECT_CLASS(fu_acpi_phat_health_record_parent_class)->finalize(object);
199
1.37k
}
200
201
static void
202
fu_acpi_phat_health_record_class_init(FuAcpiPhatHealthRecordClass *klass)
203
1
{
204
1
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
205
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
206
1
  object_class->finalize = fu_acpi_phat_health_record_finalize;
207
1
  firmware_class->parse = fu_acpi_phat_health_record_parse;
208
1
  firmware_class->write = fu_acpi_phat_health_record_write;
209
1
  firmware_class->export = fu_acpi_phat_health_record_export;
210
1
  firmware_class->build = fu_acpi_phat_health_record_build;
211
1
  fu_firmware_set_size_max(firmware_class, 1 * FU_MB);
212
1
  fu_firmware_set_images_max(firmware_class, 2000);
213
1
}
214
215
FuFirmware *
216
fu_acpi_phat_health_record_new(void)
217
1.37k
{
218
1.37k
  return FU_FIRMWARE(g_object_new(FU_TYPE_ACPI_PHAT_HEALTH_RECORD, NULL));
219
1.37k
}