Coverage Report

Created: 2025-08-24 07:10

/src/fwupd/libfwupdplugin/fu-fmap-firmware.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2020 Benson Leung <bleung@chromium.org>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#include "config.h"
8
9
#include "fu-byte-array.h"
10
#include "fu-bytes.h"
11
#include "fu-common.h"
12
#include "fu-fmap-firmware.h"
13
#include "fu-fmap-struct.h"
14
#include "fu-input-stream.h"
15
#include "fu-partial-input-stream.h"
16
17
/**
18
 * FuFmapFirmware:
19
 *
20
 * A FMAP firmware image.
21
 *
22
 * See also: [class@FuFirmware]
23
 */
24
25
16.1k
#define FMAP_AREANAME "FMAP"
26
27
G_DEFINE_TYPE(FuFmapFirmware, fu_fmap_firmware, FU_TYPE_FIRMWARE)
28
29
static gboolean
30
fu_fmap_firmware_parse(FuFirmware *firmware,
31
           GInputStream *stream,
32
           FuFirmwareParseFlags flags,
33
           GError **error)
34
1.14k
{
35
1.14k
  gsize offset = 0;
36
1.14k
  gsize streamsz = 0;
37
1.14k
  guint32 nareas;
38
1.14k
  g_autoptr(GByteArray) st_hdr = NULL;
39
40
  /* find the magic token */
41
1.14k
  if (!fu_input_stream_find(stream,
42
1.14k
          (const guint8 *)FU_STRUCT_FMAP_DEFAULT_SIGNATURE,
43
1.14k
          FU_STRUCT_FMAP_SIZE_SIGNATURE,
44
1.14k
          &offset,
45
1.14k
          error))
46
299
    return FALSE;
47
48
  /* parse */
49
841
  st_hdr = fu_struct_fmap_parse_stream(stream, offset, error);
50
841
  if (st_hdr == NULL)
51
79
    return FALSE;
52
762
  fu_firmware_set_addr(firmware, fu_struct_fmap_get_base(st_hdr));
53
54
762
  if (!fu_input_stream_size(stream, &streamsz, error))
55
0
    return FALSE;
56
762
  if (fu_struct_fmap_get_size(st_hdr) != streamsz) {
57
185
    g_set_error(error,
58
185
          FWUPD_ERROR,
59
185
          FWUPD_ERROR_INVALID_DATA,
60
185
          "file size incorrect, expected 0x%04x got 0x%04x",
61
185
          fu_struct_fmap_get_size(st_hdr),
62
185
          (guint)streamsz);
63
185
    return FALSE;
64
185
  }
65
577
  nareas = fu_struct_fmap_get_nareas(st_hdr);
66
577
  if (nareas < 1) {
67
2
    g_set_error_literal(error,
68
2
            FWUPD_ERROR,
69
2
            FWUPD_ERROR_INVALID_DATA,
70
2
            "number of areas invalid");
71
2
    return FALSE;
72
2
  }
73
575
  offset += st_hdr->len;
74
4.03M
  for (gsize i = 0; i < nareas; i++) {
75
4.03M
    guint32 area_offset;
76
4.03M
    guint32 area_size;
77
4.03M
    g_autofree gchar *area_name = NULL;
78
4.03M
    g_autoptr(FuFirmware) img = fu_firmware_new();
79
4.03M
    g_autoptr(GByteArray) st_area = NULL;
80
4.03M
    g_autoptr(GInputStream) img_stream = NULL;
81
82
    /* load area */
83
4.03M
    st_area = fu_struct_fmap_area_parse_stream(stream, offset, error);
84
4.03M
    if (st_area == NULL)
85
190
      return FALSE;
86
4.03M
    area_size = fu_struct_fmap_area_get_size(st_area);
87
4.03M
    if (area_size == 0)
88
4.01M
      continue;
89
16.3k
    area_offset = fu_struct_fmap_area_get_offset(st_area);
90
16.3k
    img_stream = fu_partial_input_stream_new(stream,
91
16.3k
               (gsize)area_offset,
92
16.3k
               (gsize)area_size,
93
16.3k
               error);
94
16.3k
    if (img_stream == NULL) {
95
190
      g_prefix_error_literal(error, "failed to cut FMAP area: ");
96
190
      return FALSE;
97
190
    }
98
16.1k
    if (!fu_firmware_parse_stream(img, img_stream, 0x0, flags, error))
99
0
      return FALSE;
100
16.1k
    area_name = fu_struct_fmap_area_get_name(st_area);
101
16.1k
    fu_firmware_set_id(img, area_name);
102
16.1k
    fu_firmware_set_idx(img, i + 1);
103
16.1k
    fu_firmware_set_addr(img, area_offset);
104
16.1k
    if (!fu_firmware_add_image_full(firmware, img, error))
105
2
      return FALSE;
106
107
16.1k
    if (g_strcmp0(area_name, FMAP_AREANAME) == 0) {
108
2.16k
      g_autofree gchar *version = NULL;
109
2.16k
      version = g_strdup_printf("%d.%d",
110
2.16k
              fu_struct_fmap_get_ver_major(st_hdr),
111
2.16k
              fu_struct_fmap_get_ver_minor(st_hdr));
112
2.16k
      fu_firmware_set_version(img, version);
113
2.16k
    }
114
16.1k
    offset += st_area->len;
115
16.1k
  }
116
117
  /* success */
118
193
  return TRUE;
119
575
}
120
121
static GByteArray *
122
fu_fmap_firmware_write(FuFirmware *firmware, GError **error)
123
193
{
124
193
  gsize total_sz;
125
193
  gsize offset;
126
193
  g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware);
127
193
  g_autoptr(GByteArray) buf = g_byte_array_new();
128
193
  g_autoptr(GByteArray) st_hdr = fu_struct_fmap_new();
129
130
  /* pad to offset */
131
193
  if (fu_firmware_get_offset(firmware) > 0)
132
0
    fu_byte_array_set_size(buf, fu_firmware_get_offset(firmware), 0x00);
133
134
  /* add header */
135
193
  total_sz = offset = st_hdr->len + (FU_STRUCT_FMAP_AREA_SIZE * images->len);
136
193
  for (guint i = 0; i < images->len; i++) {
137
102
    FuFirmware *img = g_ptr_array_index(images, i);
138
102
    g_autoptr(GBytes) fw = fu_firmware_get_bytes_with_patches(img, error);
139
102
    if (fw == NULL)
140
102
      return NULL;
141
0
    total_sz += g_bytes_get_size(fw);
142
0
  }
143
144
  /* header */
145
91
  fu_struct_fmap_set_base(st_hdr, fu_firmware_get_addr(firmware));
146
91
  fu_struct_fmap_set_nareas(st_hdr, images->len);
147
91
  fu_struct_fmap_set_size(st_hdr, fu_firmware_get_offset(firmware) + total_sz);
148
91
  g_byte_array_append(buf, st_hdr->data, st_hdr->len);
149
150
  /* add each area */
151
91
  for (guint i = 0; i < images->len; i++) {
152
0
    FuFirmware *img = g_ptr_array_index(images, i);
153
0
    g_autoptr(GBytes) fw = fu_firmware_get_bytes_with_patches(img, NULL);
154
0
    g_autoptr(GByteArray) st_area = fu_struct_fmap_area_new();
155
0
    fu_struct_fmap_area_set_offset(st_area, fu_firmware_get_offset(firmware) + offset);
156
0
    fu_struct_fmap_area_set_size(st_area, g_bytes_get_size(fw));
157
0
    if (fu_firmware_get_id(img) != NULL) {
158
0
      if (!fu_struct_fmap_area_set_name(st_area, fu_firmware_get_id(img), error))
159
0
        return NULL;
160
0
    }
161
0
    g_byte_array_append(buf, st_area->data, st_area->len);
162
0
    offset += g_bytes_get_size(fw);
163
0
  }
164
165
  /* add the images */
166
91
  for (guint i = 0; i < images->len; i++) {
167
0
    FuFirmware *img = g_ptr_array_index(images, i);
168
0
    g_autoptr(GBytes) fw = fu_firmware_get_bytes_with_patches(img, error);
169
0
    if (fw == NULL)
170
0
      return NULL;
171
0
    fu_byte_array_append_bytes(buf, fw);
172
0
  }
173
174
  /* success */
175
91
  return g_steal_pointer(&buf);
176
91
}
177
178
static void
179
fu_fmap_firmware_init(FuFmapFirmware *self)
180
1.14k
{
181
1.14k
  fu_firmware_set_images_max(FU_FIRMWARE(self), 1024);
182
1.14k
}
183
184
static void
185
fu_fmap_firmware_class_init(FuFmapFirmwareClass *klass)
186
2
{
187
2
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
188
2
  firmware_class->parse = fu_fmap_firmware_parse;
189
2
  firmware_class->write = fu_fmap_firmware_write;
190
2
}
191
192
/**
193
 * fu_fmap_firmware_new
194
 *
195
 * Creates a new #FuFirmware of sub type fmap
196
 *
197
 * Since: 1.5.0
198
 **/
199
FuFirmware *
200
fu_fmap_firmware_new(void)
201
0
{
202
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_FMAP_FIRMWARE, NULL));
203
0
}