/src/fwupd/libfwupdplugin/fu-sbatlevel-section.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2023 Canonical Ltd. |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | | #define G_LOG_DOMAIN "FuFirmware" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include "fu-byte-array.h" |
12 | | #include "fu-common.h" |
13 | | #include "fu-csv-firmware.h" |
14 | | #include "fu-input-stream.h" |
15 | | #include "fu-partial-input-stream.h" |
16 | | #include "fu-sbatlevel-section-struct.h" |
17 | | #include "fu-sbatlevel-section.h" |
18 | | |
19 | 4.15k | G_DEFINE_TYPE(FuSbatlevelSection, fu_sbatlevel_section, FU_TYPE_FIRMWARE); |
20 | 4.15k | |
21 | 4.15k | static gboolean |
22 | 4.15k | fu_sbatlevel_section_add_entry(FuSbatlevelSection *self, |
23 | 4.15k | GInputStream *stream, |
24 | 4.15k | gsize offset, |
25 | 4.15k | const gchar *entry_name, |
26 | 4.15k | guint64 entry_idx, |
27 | 4.15k | FuFirmwareParseFlags flags, |
28 | 4.15k | GError **error) |
29 | 4.15k | { |
30 | 247 | gsize streamsz = 0; |
31 | 247 | g_autoptr(FuFirmware) entry_fw = NULL; |
32 | 247 | g_autoptr(GInputStream) partial_stream = NULL; |
33 | | |
34 | | /* stop at the null terminator */ |
35 | 247 | if (!fu_input_stream_size(stream, &streamsz, error)) |
36 | 0 | return FALSE; |
37 | 247 | if (offset >= streamsz) { |
38 | 49 | g_set_error(error, |
39 | 49 | FWUPD_ERROR, |
40 | 49 | FWUPD_ERROR_INVALID_DATA, |
41 | 49 | "offset 0x%x exceeds stream size 0x%x", |
42 | 49 | (guint)offset, |
43 | 49 | (guint)streamsz); |
44 | 49 | return FALSE; |
45 | 49 | } |
46 | 20.1k | for (guint i = offset; i < streamsz; i++) { |
47 | 20.0k | guint8 value = 0; |
48 | 20.0k | if (!fu_input_stream_read_u8(stream, i, &value, error)) |
49 | 0 | return FALSE; |
50 | 20.0k | if (value == 0x0) { |
51 | 175 | streamsz = i - 1; |
52 | 175 | break; |
53 | 175 | } |
54 | 20.0k | } |
55 | | |
56 | 198 | entry_fw = fu_csv_firmware_new(); |
57 | 198 | fu_csv_firmware_add_column_id(FU_CSV_FIRMWARE(entry_fw), "$id"); |
58 | 198 | fu_csv_firmware_add_column_id(FU_CSV_FIRMWARE(entry_fw), "component_generation"); |
59 | 198 | fu_csv_firmware_add_column_id(FU_CSV_FIRMWARE(entry_fw), "date_stamp"); |
60 | 198 | fu_csv_firmware_set_write_column_ids(FU_CSV_FIRMWARE(entry_fw), FALSE); |
61 | | |
62 | 198 | fu_firmware_set_idx(entry_fw, entry_idx); |
63 | 198 | fu_firmware_set_id(entry_fw, entry_name); |
64 | 198 | fu_firmware_set_offset(entry_fw, offset); |
65 | 198 | partial_stream = fu_partial_input_stream_new(stream, offset, streamsz - offset, error); |
66 | 198 | if (partial_stream == NULL) { |
67 | 0 | g_prefix_error_literal(error, "failed to cut CSV section: "); |
68 | 0 | return FALSE; |
69 | 0 | } |
70 | 198 | if (!fu_firmware_parse_stream(entry_fw, partial_stream, 0, flags, error)) { |
71 | 4 | g_prefix_error(error, "failed to parse %s: ", entry_name); |
72 | 4 | return FALSE; |
73 | 4 | } |
74 | 194 | if (!fu_firmware_add_image(FU_FIRMWARE(self), entry_fw, error)) |
75 | 0 | return FALSE; |
76 | | |
77 | | /* success */ |
78 | 194 | return TRUE; |
79 | 194 | } |
80 | | |
81 | | static gboolean |
82 | | fu_sbatlevel_section_parse(FuFirmware *firmware, |
83 | | GInputStream *stream, |
84 | | FuFirmwareParseFlags flags, |
85 | | GError **error) |
86 | 185 | { |
87 | 185 | FuSbatlevelSection *self = FU_SBATLEVEL_SECTION(firmware); |
88 | 185 | g_autoptr(FuStructSbatLevelSectionHeader) st = NULL; |
89 | | |
90 | 185 | st = fu_struct_sbat_level_section_header_parse_stream(stream, 0x0, error); |
91 | 185 | if (st == NULL) |
92 | 48 | return FALSE; |
93 | 137 | if (!fu_sbatlevel_section_add_entry( |
94 | 137 | self, |
95 | 137 | stream, |
96 | 137 | sizeof(guint32) + fu_struct_sbat_level_section_header_get_previous(st), |
97 | 137 | "previous", |
98 | 137 | 0, |
99 | 137 | flags, |
100 | 137 | error)) |
101 | 27 | return FALSE; |
102 | 110 | if (!fu_sbatlevel_section_add_entry(self, |
103 | 110 | stream, |
104 | 110 | sizeof(guint32) + |
105 | 110 | fu_struct_sbat_level_section_header_get_latest(st), |
106 | 110 | "latest", |
107 | 110 | 1, |
108 | 110 | flags, |
109 | 110 | error)) |
110 | 26 | return FALSE; |
111 | 84 | return TRUE; |
112 | 110 | } |
113 | | |
114 | | static GByteArray * |
115 | | fu_sbatlevel_section_write(FuFirmware *firmware, GError **error) |
116 | 27 | { |
117 | 27 | g_autoptr(FuFirmware) img_ltst = NULL; |
118 | 27 | g_autoptr(FuFirmware) img_prev = NULL; |
119 | 27 | g_autoptr(FuStructSbatLevelSectionHeader) st = fu_struct_sbat_level_section_header_new(); |
120 | 27 | g_autoptr(GBytes) blob_ltst = NULL; |
121 | 27 | g_autoptr(GBytes) blob_prev = NULL; |
122 | | |
123 | | /* previous */ |
124 | 27 | fu_struct_sbat_level_section_header_set_previous(st, sizeof(guint32) * 2); |
125 | 27 | img_prev = fu_firmware_get_image_by_id(firmware, "previous", error); |
126 | 27 | if (img_prev == NULL) |
127 | 2 | return NULL; |
128 | 25 | blob_prev = fu_firmware_write(img_prev, error); |
129 | 25 | if (blob_prev == NULL) |
130 | 0 | return NULL; |
131 | 25 | fu_byte_array_append_bytes(st->buf, blob_prev); |
132 | 25 | fu_byte_array_append_uint8(st->buf, 0x0); |
133 | | |
134 | | /* latest */ |
135 | 25 | fu_struct_sbat_level_section_header_set_latest(st, |
136 | 25 | (sizeof(guint32) * 2) + |
137 | 25 | g_bytes_get_size(blob_prev) + 1); |
138 | 25 | img_ltst = fu_firmware_get_image_by_id(firmware, "latest", error); |
139 | 25 | if (img_ltst == NULL) |
140 | 0 | return NULL; |
141 | 25 | blob_ltst = fu_firmware_write(img_ltst, error); |
142 | 25 | if (blob_ltst == NULL) |
143 | 0 | return NULL; |
144 | 25 | fu_byte_array_append_bytes(st->buf, blob_ltst); |
145 | 25 | fu_byte_array_append_uint8(st->buf, 0x0); |
146 | | |
147 | | /* success */ |
148 | 25 | return g_steal_pointer(&st->buf); |
149 | 25 | } |
150 | | |
151 | | static void |
152 | | fu_sbatlevel_section_init(FuSbatlevelSection *self) |
153 | 222 | { |
154 | 222 | fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_CSV_FIRMWARE); |
155 | 222 | fu_firmware_set_images_max(FU_FIRMWARE(self), 2); |
156 | 222 | fu_firmware_set_size_max(FU_FIRMWARE(self), 10 * FU_KB); |
157 | 222 | } |
158 | | |
159 | | static void |
160 | | fu_sbatlevel_section_class_init(FuSbatlevelSectionClass *klass) |
161 | 1 | { |
162 | 1 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
163 | | |
164 | 1 | firmware_class->parse = fu_sbatlevel_section_parse; |
165 | 1 | firmware_class->write = fu_sbatlevel_section_write; |
166 | 1 | } |
167 | | |
168 | | FuFirmware * |
169 | | fu_sbatlevel_section_new(void) |
170 | 222 | { |
171 | 222 | return FU_FIRMWARE(g_object_new(FU_TYPE_SBATLEVEL_SECTION, NULL)); |
172 | 222 | } |