/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #include "config.h" #include "fu-optionrom-device.h" struct _FuOptionromDevice { FuUdevDevice parent_instance; }; G_DEFINE_TYPE (FuOptionromDevice, fu_optionrom_device, FU_TYPE_UDEV_DEVICE) static gboolean fu_optionrom_device_probe (FuDevice *device, GError **error) { g_autofree gchar *fn = NULL; /* FuUdevDevice->probe */ if (!FU_DEVICE_CLASS (fu_optionrom_device_parent_class)->probe (device, error)) return FALSE; /* does the device even have ROM? */ fn = g_build_filename (fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device)), "rom", NULL); if (!g_file_test (fn, G_FILE_TEST_EXISTS)) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "Unable to read firmware from device"); return FALSE; } /* set the physical ID */ return fu_udev_device_set_physical_id (FU_UDEV_DEVICE (device), "pci", error); } static GBytes * fu_optionrom_device_dump_firmware (FuDevice *device, GError **error) { FuUdevDevice *udev_device = FU_UDEV_DEVICE (device); guint number_reads = 0; g_autofree gchar *fn = NULL; g_autofree gchar *rom_fn = NULL; g_autoptr(GByteArray) buf = g_byte_array_new (); g_autoptr(GError) error_local = NULL; g_autoptr(GFile) file = NULL; g_autoptr(GInputStream) stream = NULL; /* open the file */ rom_fn = g_build_filename (fu_udev_device_get_sysfs_path (udev_device), "rom", NULL); if (rom_fn == NULL) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Unable to read firmware from device"); return NULL; } /* open file */ file = g_file_new_for_path (rom_fn); stream = G_INPUT_STREAM (g_file_read (file, NULL, &error_local)); if (stream == NULL) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_AUTH_FAILED, error_local->message); return NULL; } /* 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, NULL, error); if (output_stream == NULL) return NULL; if (g_output_stream_write (G_OUTPUT_STREAM (output_stream), "1", 1, NULL, error) < 0) return NULL; } /* 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), NULL, error); if (sz == 0) break; g_debug ("ROM returned 0x%04x bytes", (guint) sz); if (sz < 0) return NULL; 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 NULL; } } if (buf->len < 512) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "firmware too small: %u bytes", buf->len); return NULL; } return g_byte_array_free_to_bytes (g_steal_pointer (&buf)); } static void fu_optionrom_device_init (FuOptionromDevice *self) { fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_icon (FU_DEVICE (self), "audio-card"); fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); fu_device_set_logical_id (FU_DEVICE (self), "rom"); fu_udev_device_set_flags (FU_UDEV_DEVICE (self), FU_UDEV_DEVICE_FLAG_OPEN_READ | FU_UDEV_DEVICE_FLAG_VENDOR_FROM_PARENT); } static void fu_optionrom_device_finalize (GObject *object) { G_OBJECT_CLASS (fu_optionrom_device_parent_class)->finalize (object); } static void fu_optionrom_device_class_init (FuOptionromDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); object_class->finalize = fu_optionrom_device_finalize; klass_device->dump_firmware = fu_optionrom_device_dump_firmware; klass_device->probe = fu_optionrom_device_probe; }