fwupd/plugins/optionrom/fu-rom.c
Richard Hughes fbd8b5d325 Add fu_device_dump_firmware()
Conceptually we were trying to stuff subtly different actions into one vfunc:

 * Read firmware from the device to update the verification checksums

 * Read a firmware blob from the device for debugging

For the first action we might want to mask out the sections of the flash with
serial numbers (so the verification hashes match the ones published on the LVFS)
and for the second we want just a raw ROM file from the hardware with no
pre-processing that we can compare against an external SPI dumper.

Split out ->dump_firmware to get the raw blob, and allow plugins to also
implement ->read_firmware() if they have to mask out specific offsets or remove
specific images from the FuFirmware container.

In the common case when masking is not required, fall back to using a 'binary'
FuFirmware automatically to make most plugins simpler.
2020-09-24 10:54:27 -05:00

836 lines
21 KiB
C

/*
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupd.h>
#include <glib/gstdio.h>
#include <string.h>
#include "fu-rom.h"
static void fu_rom_finalize (GObject *object);
/* data from http://resources.infosecinstitute.com/pci-expansion-rom/ */
typedef struct {
guint8 *rom_data;
guint32 rom_len;
guint32 rom_offset;
guint32 entry_point;
guint8 reserved[18];
guint16 cpi_ptr;
guint16 vendor_id;
guint16 device_id;
guint16 device_list_ptr;
guint16 data_len;
guint8 data_rev;
guint32 class_code;
guint32 image_len;
guint16 revision_level;
guint8 code_type;
guint8 last_image;
guint32 max_runtime_len;
guint16 config_header_ptr;
guint16 dmtf_clp_ptr;
} FuRomPciHeader;
struct _FuRom {
GObject parent_instance;
FuRomKind kind;
gchar *version;
guint16 vendor_id;
guint16 device_id;
GPtrArray *hdrs; /* of FuRomPciHeader */
};
G_DEFINE_TYPE (FuRom, fu_rom, G_TYPE_OBJECT)
static void
fu_rom_pci_header_free (FuRomPciHeader *hdr)
{
g_free (hdr->rom_data);
g_free (hdr);
}
const gchar *
fu_rom_kind_to_string (FuRomKind kind)
{
if (kind == FU_ROM_KIND_UNKNOWN)
return "unknown";
if (kind == FU_ROM_KIND_ATI)
return "ati";
if (kind == FU_ROM_KIND_NVIDIA)
return "nvidia";
if (kind == FU_ROM_KIND_INTEL)
return "intel";
if (kind == FU_ROM_KIND_PCI)
return "pci";
return NULL;
}
static guint8 *
fu_rom_pci_strstr (FuRomPciHeader *hdr, const gchar *needle)
{
gsize needle_len;
guint8 *haystack;
gsize haystack_len;
if (needle == NULL || needle[0] == '\0')
return NULL;
if (hdr->rom_data == NULL)
return NULL;
if (hdr->data_len > hdr->rom_len)
return NULL;
haystack = &hdr->rom_data[hdr->data_len];
haystack_len = hdr->rom_len - hdr->data_len;
needle_len = strlen (needle);
if (needle_len > haystack_len)
return NULL;
for (guint i = 0; i < haystack_len - needle_len; i++) {
if (memcmp (haystack + i, needle, needle_len) == 0)
return &haystack[i];
}
return NULL;
}
static guint
fu_rom_blank_serial_numbers (guint8 *buffer, guint buffer_sz)
{
guint i;
for (i = 0; i < buffer_sz; i++) {
if (buffer[i] == 0xff ||
buffer[i] == '\0' ||
buffer[i] == '\n' ||
buffer[i] == '\r')
break;
buffer[i] = '\0';
}
return i;
}
static gchar *
fu_rom_get_hex_dump (const guint8 *buffer, guint32 sz)
{
GString *str = g_string_new ("");
for (guint32 i = 0; i < sz; i++)
g_string_append_printf (str, "%02x ", buffer[i]);
g_string_append (str, " ");
for (guint32 i = 0; i < sz; i++) {
gchar tmp = '?';
if (g_ascii_isprint (buffer[i]))
tmp = (gchar) buffer[i];
g_string_append_printf (str, "%c", tmp);
}
return g_string_free (str, FALSE);
}
typedef struct {
guint8 segment_kind;
guint8 *data;
guint16 data_len;
guint16 next_offset;
} FooRomPciCertificateHdr;
static void
fu_rom_pci_print_certificate_data (const guint8 *buffer, gssize sz)
{
guint16 off = 0;
g_autofree gchar *hdr_str = NULL;
/* 27 byte header, unknown purpose */
hdr_str = fu_rom_get_hex_dump (buffer+off, 27);
g_debug (" ISBN header: %s", hdr_str);
buffer += 27;
while (TRUE) {
/* 29 byte header to the segment, then data:
* 0x01 = type. 0x1 = certificate, 0x2 = hashes?
* 0x13,0x14 = offset to next segment */
FooRomPciCertificateHdr h;
g_autofree gchar *segment_str = NULL;
segment_str = fu_rom_get_hex_dump (buffer+off, 29);
g_debug (" ISBN segment @%02x: %s", off, segment_str);
h.segment_kind = buffer[off+1];
h.next_offset = (guint16) (((guint16) buffer[off+14] << 8) + buffer[off+13]);
h.data = (guint8 *) &buffer[off+29];
/* calculate last block length automatically */
if (h.next_offset == 0)
h.data_len = (guint16) (sz - off - 29 - 27);
else
h.data_len = (guint16) (h.next_offset - off - 29);
/* print the certificate */
if (h.segment_kind == 0x01) {
g_autofree gchar *tmp = NULL;
tmp = fu_rom_get_hex_dump (h.data, h.data_len);
g_debug ("%s(%i)", tmp, h.data_len);
} else if (h.segment_kind == 0x02) {
g_autofree gchar *tmp = NULL;
tmp = fu_rom_get_hex_dump (h.data,
h.data_len < 32 ? h.data_len : 32);
g_debug ("%s(%i)", tmp, h.data_len);
} else {
g_warning ("unknown segment kind %i", h.segment_kind);
}
/* last block */
if (h.next_offset == 0x0000)
break;
off = h.next_offset;
}
}
static const gchar *
fu_rom_pci_code_type_to_string (guint8 code_type)
{
if (code_type == 0)
return "Intel86";
if (code_type == 1)
return "OpenFirmware";
if (code_type == 2)
return "PA-RISC";
if (code_type == 3)
return "EFI";
return "reserved";
}
static guint8
fu_rom_pci_header_get_checksum (FuRomPciHeader *hdr)
{
guint8 chksum_check = 0x00;
for (guint i = 0; i < hdr->rom_len; i++)
chksum_check += hdr->rom_data[i];
return chksum_check;
}
static void
fu_rom_pci_print_header (FuRomPciHeader *hdr)
{
guint8 chksum_check;
guint8 *buffer;
g_autofree gchar *data_str = NULL;
g_autofree gchar *reserved_str = NULL;
g_debug ("PCI Header");
g_debug (" RomOffset: 0x%04x", hdr->rom_offset);
g_debug (" RomSize: 0x%04x", hdr->rom_len);
g_debug (" EntryPnt: 0x%06x", hdr->entry_point);
reserved_str = fu_rom_get_hex_dump (hdr->reserved, 18);
g_debug (" Reserved: %s", reserved_str);
g_debug (" CpiPtr: 0x%04x", hdr->cpi_ptr);
/* sanity check */
if (hdr->cpi_ptr > hdr->rom_len) {
g_debug (" PCI DATA: Invalid as cpi_ptr > rom_len");
return;
}
if (hdr->data_len > hdr->rom_len) {
g_debug (" PCI DATA: Invalid as data_len > rom_len");
return;
}
/* print the data */
buffer = &hdr->rom_data[hdr->cpi_ptr];
g_debug (" PCI Data");
g_debug (" VendorID: 0x%04x", hdr->vendor_id);
g_debug (" DeviceID: 0x%04x", hdr->device_id);
g_debug (" DevList: 0x%04x", hdr->device_list_ptr);
g_debug (" DataLen: 0x%04x", hdr->data_len);
g_debug (" DataRev: 0x%04x", hdr->data_rev);
if (hdr->image_len < 0x0f) {
data_str = fu_rom_get_hex_dump (&buffer[hdr->data_len], hdr->image_len);
g_debug (" ImageLen: 0x%04x [%s]", hdr->image_len, data_str);
} else if (hdr->image_len >= 0x0f) {
data_str = fu_rom_get_hex_dump (&buffer[hdr->data_len], 0x0f);
g_debug (" ImageLen: 0x%04x [%s...]", hdr->image_len, data_str);
} else {
g_debug (" ImageLen: 0x%04x", hdr->image_len);
}
g_debug (" RevLevel: 0x%04x", hdr->revision_level);
g_debug (" CodeType: 0x%02x [%s]", hdr->code_type,
fu_rom_pci_code_type_to_string (hdr->code_type));
g_debug (" LastImg: 0x%02x [%s]", hdr->last_image,
hdr->last_image == 0x80 ? "yes" : "no");
g_debug (" MaxRunLen: 0x%04x", hdr->max_runtime_len);
g_debug (" ConfigHdr: 0x%04x", hdr->config_header_ptr);
g_debug (" ClpPtr: 0x%04x", hdr->dmtf_clp_ptr);
/* dump the ISBN */
if (hdr->code_type == 0x70 &&
memcmp (&buffer[hdr->data_len], "ISBN", 4) == 0) {
fu_rom_pci_print_certificate_data (&buffer[hdr->data_len],
hdr->image_len);
}
/* verify the checksum byte */
if (hdr->image_len <= hdr->rom_len && hdr->image_len > 0) {
buffer = hdr->rom_data;
chksum_check = fu_rom_pci_header_get_checksum (hdr);
if (chksum_check == 0x00) {
g_debug (" ChkSum: 0x%02x [valid]",
buffer[hdr->image_len-1]);
} else {
g_debug (" ChkSum: 0x%02x [failed, got 0x%02x]",
buffer[hdr->image_len-1],
chksum_check);
}
} else {
g_debug (" ChkSum: 0x?? [unknown]");
}
}
gboolean
fu_rom_extract_all (FuRom *self, const gchar *path, GError **error)
{
FuRomPciHeader *hdr;
for (guint i = 0; i < self->hdrs->len; i++) {
g_autofree gchar *fn = NULL;
hdr = g_ptr_array_index (self->hdrs, i);
fn = g_strdup_printf ("%s/%02u.bin", path, i);
g_debug ("dumping ROM #%u at 0x%04x [0x%02x] to %s",
i, hdr->rom_offset, hdr->rom_len, fn);
if (hdr->rom_len == 0)
continue;
if (!g_file_set_contents (fn,
(const gchar *) hdr->rom_data,
(gssize) hdr->rom_len, error))
return FALSE;
}
return TRUE;
}
static void
fu_rom_find_and_blank_serial_numbers (FuRom *self)
{
FuRomPciHeader *hdr;
guint8 *tmp;
/* bail if not likely */
if (self->kind == FU_ROM_KIND_PCI ||
self->kind == FU_ROM_KIND_INTEL) {
g_debug ("no serial numbers likely");
return;
}
for (guint i = 0; i < self->hdrs->len; i++) {
hdr = g_ptr_array_index (self->hdrs, i);
g_debug ("looking for PPID at 0x%04x", hdr->rom_offset);
tmp = fu_rom_pci_strstr (hdr, "PPID");
if (tmp != NULL) {
guint len;
guint8 chk;
len = fu_rom_blank_serial_numbers (tmp, hdr->rom_len - hdr->data_len);
g_debug ("cleared %u chars @ 0x%04lx",
len, (gulong) (tmp - &hdr->rom_data[hdr->data_len]));
/* we have to fix the checksum */
chk = fu_rom_pci_header_get_checksum (hdr);
hdr->rom_data[hdr->rom_len - 1] -= chk;
fu_rom_pci_print_header (hdr);
}
}
}
static gboolean
fu_rom_pci_parse_data (FuRomPciHeader *hdr)
{
guint8 *buffer;
/* check valid */
if (hdr->cpi_ptr == 0x0000) {
g_debug ("No PCI DATA @ 0x%04x", hdr->rom_offset);
return FALSE;
}
if (hdr->rom_len > 0 && hdr->cpi_ptr > hdr->rom_len) {
g_debug ("Invalid PCI DATA @ 0x%04x", hdr->rom_offset);
return FALSE;
}
/* gahh, CPI is out of the first chunk */
if (hdr->cpi_ptr > hdr->rom_len) {
g_debug ("No available PCI DATA @ 0x%04x : 0x%04x > 0x%04x",
hdr->rom_offset, hdr->cpi_ptr, hdr->rom_len);
return FALSE;
}
/* check signature */
buffer = &hdr->rom_data[hdr->cpi_ptr];
if (memcmp (buffer, "PCIR", 4) != 0) {
if (memcmp (buffer, "RGIS", 4) == 0 ||
memcmp (buffer, "NPDS", 4) == 0 ||
memcmp (buffer, "NPDE", 4) == 0) {
g_debug ("-- using NVIDIA DATA quirk");
} else {
g_debug ("Not PCI DATA: %02x%02x%02x%02x [%c%c%c%c]",
buffer[0], buffer[1],
buffer[2], buffer[3],
buffer[0], buffer[1],
buffer[2], buffer[3]);
return FALSE;
}
}
/* parse */
hdr->vendor_id = ((guint16) buffer[0x05] << 8) + buffer[0x04];
hdr->device_id = ((guint16) buffer[0x07] << 8) + buffer[0x06];
hdr->device_list_ptr = ((guint16) buffer[0x09] << 8) + buffer[0x08];
hdr->data_len = ((guint16) buffer[0x0b] << 8) + buffer[0x0a];
hdr->data_rev = buffer[0x0c];
hdr->class_code = ((guint16) buffer[0x0f] << 16) +
((guint16) buffer[0x0e] << 8) +
buffer[0x0d];
hdr->image_len = (((guint16) buffer[0x11] << 8) + buffer[0x10]) * 512;
hdr->revision_level = ((guint16) buffer[0x13] << 8) + buffer[0x12];
hdr->code_type = buffer[0x14];
hdr->last_image = buffer[0x15];
hdr->max_runtime_len = (((guint16) buffer[0x17] << 8) +
buffer[0x16]) * 512;
hdr->config_header_ptr = ((guint16) buffer[0x19] << 8) + buffer[0x18];
hdr->dmtf_clp_ptr = ((guint16) buffer[0x1b] << 8) + buffer[0x1a];
return TRUE;
}
static FuRomPciHeader *
fu_rom_pci_get_header (const guint8 *buffer, guint32 sz)
{
FuRomPciHeader *hdr;
/* check signature */
if (memcmp (buffer, "\x55\xaa", 2) != 0) {
if (memcmp (buffer, "\x56\x4e", 2) == 0) {
g_debug ("-- using NVIDIA ROM quirk");
} else {
g_autofree gchar *sig_str = NULL;
sig_str = fu_rom_get_hex_dump (buffer, MIN (16, sz));
g_debug ("Not PCI ROM %s", sig_str);
return NULL;
}
}
/* decode structure */
hdr = g_new0 (FuRomPciHeader, 1);
hdr->rom_len = buffer[0x02] * 512;
/* fix up misreporting */
if (hdr->rom_len == 0) {
g_debug ("fixing up last image size");
hdr->rom_len = sz;
}
/* copy this locally to the header */
hdr->rom_data = g_memdup (buffer, hdr->rom_len);
/* parse out CPI */
hdr->entry_point = ((guint32) buffer[0x05] << 16) +
((guint16) buffer[0x04] << 8) +
buffer[0x03];
memcpy (&hdr->reserved, &buffer[6], 18);
hdr->cpi_ptr = ((guint16) buffer[0x19] << 8) + buffer[0x18];
/* parse the header data */
g_debug ("looking for PCI DATA @ 0x%04x", hdr->cpi_ptr);
fu_rom_pci_parse_data (hdr);
return hdr;
}
static gchar *
fu_rom_find_version_pci (FuRomPciHeader *hdr)
{
gchar *str;
/* ARC storage */
if (memcmp (hdr->reserved, "\0\0ARC", 5) == 0) {
str = (gchar *) fu_rom_pci_strstr (hdr, "BIOS: ");
if (str != NULL)
return g_strdup (str + 6);
}
return NULL;
}
static gchar *
fu_rom_find_version_nvidia (FuRomPciHeader *hdr)
{
gchar *str;
/* static location for some firmware */
if (memcmp (hdr->rom_data + 0x013d, "Version ", 8) == 0)
return g_strdup ((gchar *) &hdr->rom_data[0x013d + 8]);
/* usual search string */
str = (gchar *) fu_rom_pci_strstr (hdr, "Version ");
if (str != NULL)
return g_strdup (str + 8);
/* broken */
str = (gchar *) fu_rom_pci_strstr (hdr, "Vension:");
if (str != NULL)
return g_strdup (str + 8);
str = (gchar *) fu_rom_pci_strstr (hdr, "Version");
if (str != NULL)
return g_strdup (str + 7);
/* fallback to VBIOS */
if (memcmp (hdr->rom_data + 0xfa, "VBIOS Ver", 9) == 0)
return g_strdup ((gchar *) &hdr->rom_data[0xfa + 9]);
return NULL;
}
static gchar *
fu_rom_find_version_intel (FuRomPciHeader *hdr)
{
gchar *str;
/* 2175_RYan PC 14.34 06/06/2013 21:27:53 */
str = (gchar *) fu_rom_pci_strstr (hdr, "Build Number:");
if (str != NULL) {
g_auto(GStrv) split = NULL;
split = g_strsplit (str + 14, " ", -1);
for (guint i = 0; split[i] != NULL; i++) {
if (g_strstr_len (split[i], -1, ".") == NULL)
continue;
return g_strdup (split[i]);
}
}
/* fallback to VBIOS */
str = (gchar *) fu_rom_pci_strstr (hdr, "VBIOS ");
if (str != NULL)
return g_strdup (str + 6);
return NULL;
}
static gchar *
fu_rom_find_version_ati (FuRomPciHeader *hdr)
{
gchar *str;
str = (gchar *) fu_rom_pci_strstr (hdr, " VER0");
if (str != NULL)
return g_strdup (str + 4);
/* broken */
str = (gchar *) fu_rom_pci_strstr (hdr, " VR");
if (str != NULL)
return g_strdup (str + 4);
return NULL;
}
static gchar *
fu_rom_find_version (FuRomKind kind, FuRomPciHeader *hdr)
{
if (kind == FU_ROM_KIND_PCI)
return fu_rom_find_version_pci (hdr);
if (kind == FU_ROM_KIND_NVIDIA)
return fu_rom_find_version_nvidia (hdr);
if (kind == FU_ROM_KIND_INTEL)
return fu_rom_find_version_intel (hdr);
if (kind == FU_ROM_KIND_ATI)
return fu_rom_find_version_ati (hdr);
return NULL;
}
gboolean
fu_rom_load_data (FuRom *self,
GBytes *blob,
FuRomLoadFlags flags,
GCancellable *cancellable,
GError **error)
{
FuRomPciHeader *hdr = NULL;
guint32 jump = 0;
guint32 hdr_sz = 0;
gsize buffer_sz = 0;
const guint8 *buffer = g_bytes_get_data (blob, &buffer_sz);
guint32 sz = buffer_sz;
g_return_val_if_fail (FU_IS_ROM (self), FALSE);
/* detect optional IFR header and skip to option ROM */
if (memcmp (buffer, "NVGI", 4) == 0) {
guint16 ifr_sz_raw;
memcpy (&ifr_sz_raw, &buffer[0x15], 2);
hdr_sz = GUINT16_FROM_BE (ifr_sz_raw);
g_debug ("detected IFR header, skipping %x bytes", hdr_sz);
}
/* read all the ROM headers */
while (sz > hdr_sz + jump) {
guint32 jump_sz;
g_debug ("looking for PCI ROM @ 0x%04x", hdr_sz + jump);
hdr = fu_rom_pci_get_header (&buffer[hdr_sz + jump], sz - hdr_sz - jump);
if (hdr == NULL) {
gboolean found_data = FALSE;
/* check it's not just NUL padding */
for (guint i = jump + hdr_sz; i < buffer_sz; i++) {
if (buffer[i] != 0x00) {
found_data = TRUE;
break;
}
}
if (found_data) {
g_debug ("found junk data, adding fake");
hdr = g_new0 (FuRomPciHeader, 1);
hdr->vendor_id = 0x0000;
hdr->device_id = 0x0000;
hdr->code_type = 0x00;
hdr->last_image = 0x80;
hdr->rom_offset = hdr_sz + jump;
hdr->rom_len = sz - hdr->rom_offset;
hdr->rom_data = g_memdup (&buffer[hdr->rom_offset], hdr->rom_len);
hdr->image_len = hdr->rom_len;
g_ptr_array_add (self->hdrs, hdr);
} else {
g_debug ("ignoring 0x%04x bytes of padding",
(guint) (buffer_sz - (jump + hdr_sz)));
}
break;
}
/* save this so we can fix checksums */
hdr->rom_offset = hdr_sz + jump;
/* we can't break on hdr->last_image as
* NVIDIA uses packed but not merged extended headers */
g_ptr_array_add (self->hdrs, hdr);
/* NVIDIA don't always set a ROM size for extensions */
jump_sz = hdr->rom_len;
if (jump_sz == 0)
jump_sz = hdr->image_len;
if (jump_sz == 0x0)
break;
jump += jump_sz;
}
/* we found nothing */
if (self->hdrs->len == 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"Failed to detect firmware header [%02x%02x]",
buffer[0], buffer[1]);
return FALSE;
}
/* print all headers */
for (guint i = 0; i < self->hdrs->len; i++) {
hdr = g_ptr_array_index (self->hdrs, i);
fu_rom_pci_print_header (hdr);
}
/* find first ROM header */
hdr = g_ptr_array_index (self->hdrs, 0);
self->vendor_id = hdr->vendor_id;
self->device_id = hdr->device_id;
self->kind = FU_ROM_KIND_PCI;
/* detect intel header */
if (memcmp (hdr->reserved, "00000000000", 11) == 0)
hdr_sz = (guint32) (((guint16) buffer[0x1b] << 8) + buffer[0x1a]);
if (hdr_sz > sz) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"firmware corrupt (overflow)");
return FALSE;
}
if (hdr->entry_point == 0x374beb) {
self->kind = FU_ROM_KIND_NVIDIA;
} else if (memcmp (buffer + hdr_sz, "$VBT", 4) == 0) {
self->kind = FU_ROM_KIND_INTEL;
} else if (memcmp(buffer + 0x30, " 761295520", 10) == 0) {
self->kind = FU_ROM_KIND_ATI;
}
/* nothing */
if (self->kind == FU_ROM_KIND_UNKNOWN) {
g_autofree gchar *str = NULL;
str = fu_rom_get_hex_dump (buffer + hdr_sz, 0x32);
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"Failed to detect firmware kind from [%s]",
str);
return FALSE;
}
/* find version string */
self->version = fu_rom_find_version (self->kind, hdr);
if (self->version != NULL) {
g_strstrip (self->version);
g_strdelimit (self->version, "\r\n ", '\0');
}
/* update checksum */
if (flags & FU_ROM_LOAD_FLAG_BLANK_PPID)
fu_rom_find_and_blank_serial_numbers (self);
/* not known */
if (self->version == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Firmware version extractor not known");
return FALSE;
}
return TRUE;
}
GBytes *
fu_rom_dump_firmware (GFile *file, GCancellable *cancellable, GError **error)
{
guint number_reads = 0;
g_autofree gchar *fn = NULL;
g_autoptr(GByteArray) buf = NULL;
g_autoptr(GError) error_local = NULL;
g_autoptr(GInputStream) stream = NULL;
g_return_val_if_fail (G_IS_FILE (file), NULL);
/* open file */
stream = G_INPUT_STREAM (g_file_read (file, cancellable, &error_local));
if (stream == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_AUTH_FAILED,
error_local->message);
return FALSE;
}
/* we have to enable the read for devices */
fn = g_file_get_path (file);
if (g_str_has_prefix (fn, "/sys")) {
g_autoptr(GFileOutputStream) output_stream = NULL;
output_stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE,
cancellable, error);
if (output_stream == NULL)
return FALSE;
if (g_output_stream_write (G_OUTPUT_STREAM (output_stream), "1", 1,
cancellable, error) < 0)
return FALSE;
}
/* ensure we got enough data to fill the buffer */
while (TRUE) {
gssize sz;
guint8 tmp[32 * 1024] = { 0x0 };
sz = g_input_stream_read (stream, tmp, sizeof(tmp), cancellable, error);
if (sz == 0)
break;
g_debug ("ROM returned 0x%04x bytes", (guint) sz);
if (sz < 0)
return FALSE;
g_byte_array_append (buf, tmp, sz);
/* check the firmware isn't serving us small chunks */
if (number_reads++ > 1024) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"firmware not fulfilling requests");
return FALSE;
}
}
if (buf->len < 512) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"firmware too small: %u bytes", buf->len);
return FALSE;
}
return g_byte_array_free_to_bytes (g_steal_pointer (&buf));
}
gboolean
fu_rom_load_file (FuRom *self, GFile *file, FuRomLoadFlags flags,
GCancellable *cancellable, GError **error)
{
g_autoptr(GBytes) blob = NULL;
g_return_val_if_fail (FU_IS_ROM (self), FALSE);
blob = fu_rom_dump_firmware (file, cancellable, error);
if (blob == NULL)
return FALSE;
return fu_rom_load_data (self, blob, flags, cancellable, error);
}
FuRomKind
fu_rom_get_kind (FuRom *self)
{
g_return_val_if_fail (FU_IS_ROM (self), FU_ROM_KIND_UNKNOWN);
return self->kind;
}
const gchar *
fu_rom_get_version (FuRom *self)
{
g_return_val_if_fail (FU_IS_ROM (self), NULL);
return self->version;
}
guint16
fu_rom_get_vendor (FuRom *self)
{
g_return_val_if_fail (FU_IS_ROM (self), 0x0000);
return self->vendor_id;
}
guint16
fu_rom_get_model (FuRom *self)
{
g_return_val_if_fail (FU_IS_ROM (self), 0x0000);
return self->device_id;
}
GBytes *
fu_rom_get_data (FuRom *self)
{
GByteArray *buf = g_byte_array_new ();
for (guint i = 0; i < self->hdrs->len; i++) {
FuRomPciHeader *hdr = g_ptr_array_index (self->hdrs, i);
g_byte_array_append (buf, hdr->rom_data, hdr->rom_len);
}
return g_byte_array_free_to_bytes (buf);
}
static void
fu_rom_class_init (FuRomClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = fu_rom_finalize;
}
static void
fu_rom_init (FuRom *self)
{
self->hdrs = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_rom_pci_header_free);
}
static void
fu_rom_finalize (GObject *object)
{
FuRom *self = FU_ROM (object);
g_free (self->version);
g_ptr_array_unref (self->hdrs);
G_OBJECT_CLASS (fu_rom_parent_class)->finalize (object);
}
FuRom *
fu_rom_new (void)
{
FuRom *self;
self = g_object_new (FU_TYPE_ROM, NULL);
return FU_ROM (self);
}