/src/fwupd/plugins/acpi-phat/fu-acpi-phat-health-record.c
Line | Count | Source (jump to first uncovered line) |
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 | | G_DEFINE_TYPE(FuAcpiPhatHealthRecord, fu_acpi_phat_health_record, FU_TYPE_FIRMWARE) |
21 | | |
22 | | static void |
23 | | fu_acpi_phat_health_record_export(FuFirmware *firmware, |
24 | | FuFirmwareExportFlags flags, |
25 | | XbBuilderNode *bn) |
26 | 0 | { |
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 | | GInputStream *stream, |
39 | | FuFirmwareParseFlags flags, |
40 | | GError **error) |
41 | 2.06k | { |
42 | 2.06k | FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(firmware); |
43 | 2.06k | gsize streamsz = 0; |
44 | 2.06k | guint16 rcdlen; |
45 | 2.06k | guint32 dataoff; |
46 | 2.06k | g_autoptr(GByteArray) st = NULL; |
47 | | |
48 | | /* sanity check record length */ |
49 | 2.06k | st = fu_struct_acpi_phat_health_record_parse_stream(stream, 0x0, error); |
50 | 2.06k | if (st == NULL) |
51 | 29 | return FALSE; |
52 | 2.03k | rcdlen = fu_struct_acpi_phat_health_record_get_rcdlen(st); |
53 | 2.03k | if (!fu_input_stream_size(stream, &streamsz, error)) |
54 | 0 | return FALSE; |
55 | 2.03k | 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 | 2.03k | self->am_healthy = fu_struct_acpi_phat_health_record_get_flags(st); |
64 | 2.03k | self->guid = |
65 | 2.03k | fwupd_guid_to_string(fu_struct_acpi_phat_health_record_get_device_signature(st), |
66 | 2.03k | FWUPD_GUID_FLAG_MIXED_ENDIAN); |
67 | | |
68 | | /* device path */ |
69 | 2.03k | dataoff = fu_struct_acpi_phat_health_record_get_device_specific_data(st); |
70 | 2.03k | if (streamsz > 28) { |
71 | 1.52k | gsize ubufsz; /* bytes */ |
72 | 1.52k | g_autoptr(GBytes) ubuf = NULL; |
73 | | |
74 | | /* header -> devicepath -> data */ |
75 | 1.52k | if (dataoff == 0x0) { |
76 | 299 | ubufsz = streamsz - 28; |
77 | 1.22k | } else { |
78 | 1.22k | ubufsz = dataoff - 28; |
79 | 1.22k | } |
80 | 1.52k | if (ubufsz == 0) { |
81 | 3 | g_set_error(error, |
82 | 3 | FWUPD_ERROR, |
83 | 3 | FWUPD_ERROR_INVALID_DATA, |
84 | 3 | "device path not valid: %" G_GSIZE_FORMAT, |
85 | 3 | ubufsz); |
86 | 3 | return FALSE; |
87 | 3 | } |
88 | | |
89 | | /* align and convert */ |
90 | 1.52k | ubuf = fu_input_stream_read_bytes(stream, 28, ubufsz, NULL, error); |
91 | 1.52k | if (ubuf == NULL) |
92 | 0 | return FALSE; |
93 | 1.52k | self->device_path = fu_utf16_to_utf8_bytes(ubuf, G_LITTLE_ENDIAN, error); |
94 | 1.52k | if (self->device_path == NULL) |
95 | 171 | return FALSE; |
96 | 1.52k | } |
97 | | |
98 | | /* success */ |
99 | 1.86k | return TRUE; |
100 | 2.03k | } |
101 | | |
102 | | static GByteArray * |
103 | | fu_acpi_phat_health_record_write(FuFirmware *firmware, GError **error) |
104 | 735 | { |
105 | 735 | FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(firmware); |
106 | 735 | g_autoptr(GByteArray) st = fu_struct_acpi_phat_health_record_new(); |
107 | | |
108 | | /* convert device path ahead of time */ |
109 | 735 | if (self->device_path != NULL) { |
110 | 529 | g_autoptr(GByteArray) buf = fu_utf8_to_utf16_byte_array(self->device_path, |
111 | 529 | G_LITTLE_ENDIAN, |
112 | 529 | FU_UTF_CONVERT_FLAG_NONE, |
113 | 529 | error); |
114 | 529 | if (buf == NULL) |
115 | 0 | return NULL; |
116 | 529 | g_byte_array_append(st, buf->data, buf->len); |
117 | 529 | } |
118 | | |
119 | | /* data record */ |
120 | 735 | if (self->guid != NULL) { |
121 | 735 | fwupd_guid_t guid = {0x0}; |
122 | 735 | if (!fwupd_guid_from_string(self->guid, &guid, FWUPD_GUID_FLAG_MIXED_ENDIAN, error)) |
123 | 0 | return NULL; |
124 | 735 | fu_struct_acpi_phat_health_record_set_device_signature(st, &guid); |
125 | 735 | } |
126 | 735 | fu_struct_acpi_phat_health_record_set_rcdlen(st, st->len); |
127 | 735 | fu_struct_acpi_phat_health_record_set_version(st, fu_firmware_get_version_raw(firmware)); |
128 | 735 | fu_struct_acpi_phat_health_record_set_flags(st, self->am_healthy); |
129 | | |
130 | | /* success */ |
131 | 735 | return g_steal_pointer(&st); |
132 | 735 | } |
133 | | |
134 | | static void |
135 | | fu_acpi_phat_health_record_set_guid(FuAcpiPhatHealthRecord *self, const gchar *guid) |
136 | 0 | { |
137 | 0 | g_free(self->guid); |
138 | 0 | self->guid = g_strdup(guid); |
139 | 0 | } |
140 | | |
141 | | static void |
142 | | fu_acpi_phat_health_record_set_device_path(FuAcpiPhatHealthRecord *self, const gchar *device_path) |
143 | 0 | { |
144 | 0 | g_free(self->device_path); |
145 | 0 | self->device_path = g_strdup(device_path); |
146 | 0 | } |
147 | | |
148 | | static gboolean |
149 | | fu_acpi_phat_health_record_build(FuFirmware *firmware, XbNode *n, GError **error) |
150 | 0 | { |
151 | 0 | FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(firmware); |
152 | 0 | const gchar *tmp; |
153 | 0 | guint64 tmp64; |
154 | | |
155 | | /* optional properties */ |
156 | 0 | tmp = xb_node_query_text(n, "device_path", NULL); |
157 | 0 | if (tmp != NULL) |
158 | 0 | fu_acpi_phat_health_record_set_device_path(self, tmp); |
159 | 0 | tmp = xb_node_query_text(n, "guid", NULL); |
160 | 0 | if (tmp != NULL) |
161 | 0 | fu_acpi_phat_health_record_set_guid(self, tmp); |
162 | 0 | tmp64 = xb_node_query_text_as_uint(n, "am_healthy", NULL); |
163 | 0 | if (tmp64 != G_MAXUINT64) { |
164 | 0 | if (tmp64 > G_MAXUINT8) { |
165 | 0 | g_set_error(error, |
166 | 0 | FWUPD_ERROR, |
167 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
168 | 0 | "am_healthy value invalid, got 0x%x", |
169 | 0 | (guint)tmp64); |
170 | 0 | return FALSE; |
171 | 0 | } |
172 | 0 | self->am_healthy = (guint8)tmp64; |
173 | 0 | } |
174 | | |
175 | | /* success */ |
176 | 0 | return TRUE; |
177 | 0 | } |
178 | | |
179 | | static void |
180 | | fu_acpi_phat_health_record_init(FuAcpiPhatHealthRecord *self) |
181 | 2.08k | { |
182 | 2.08k | fu_firmware_set_images_max(FU_FIRMWARE(self), 2000); |
183 | 2.08k | fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION); |
184 | 2.08k | } |
185 | | |
186 | | static void |
187 | | fu_acpi_phat_health_record_finalize(GObject *object) |
188 | 2.08k | { |
189 | 2.08k | FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(object); |
190 | 2.08k | g_free(self->guid); |
191 | 2.08k | g_free(self->device_path); |
192 | 2.08k | G_OBJECT_CLASS(fu_acpi_phat_health_record_parent_class)->finalize(object); |
193 | 2.08k | } |
194 | | |
195 | | static void |
196 | | fu_acpi_phat_health_record_class_init(FuAcpiPhatHealthRecordClass *klass) |
197 | 1 | { |
198 | 1 | GObjectClass *object_class = G_OBJECT_CLASS(klass); |
199 | 1 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
200 | 1 | object_class->finalize = fu_acpi_phat_health_record_finalize; |
201 | 1 | firmware_class->parse = fu_acpi_phat_health_record_parse; |
202 | 1 | firmware_class->write = fu_acpi_phat_health_record_write; |
203 | 1 | firmware_class->export = fu_acpi_phat_health_record_export; |
204 | 1 | firmware_class->build = fu_acpi_phat_health_record_build; |
205 | 1 | } |
206 | | |
207 | | FuFirmware * |
208 | | fu_acpi_phat_health_record_new(void) |
209 | 2.08k | { |
210 | 2.08k | return FU_FIRMWARE(g_object_new(FU_TYPE_ACPI_PHAT_HEALTH_RECORD, NULL)); |
211 | 2.08k | } |