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