diff --git a/plugins/synaptics-rmi/fu-synaptics-rmi-device.h b/plugins/synaptics-rmi/fu-synaptics-rmi-device.h index 1bfc683e5..83dc07c01 100644 --- a/plugins/synaptics-rmi/fu-synaptics-rmi-device.h +++ b/plugins/synaptics-rmi/fu-synaptics-rmi-device.h @@ -60,6 +60,7 @@ typedef struct { guint32 build_id; guint8 bootloader_id[2]; guint8 status_addr; + gboolean has_pubkey; } FuSynapticsRmiFlash; #define RMI_F34_HAS_NEW_REG_MAP (1 << 0) @@ -69,11 +70,13 @@ typedef struct { #define RMI_F34_BLOCK_DATA_V1_OFFSET 1 #define RMI_F34_ENABLE_WAIT_MS 300 /* ms */ -#define RMI_F34_IDLE_WAIT_MS 20 /* ms */ +#define RMI_F34_IDLE_WAIT_MS 500 /* ms */ #define RMI_DEVICE_PAGE_SELECT_REGISTER 0xff #define RMI_DEVICE_BUS_SELECT_REGISTER 0xfe +#define RMI_KEY_SIZE_2K 0x100 + typedef enum { RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE = 0, RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34 = (1 << 0), diff --git a/plugins/synaptics-rmi/fu-synaptics-rmi-hid-device.c b/plugins/synaptics-rmi/fu-synaptics-rmi-hid-device.c index e85f2533c..3c84bd62d 100644 --- a/plugins/synaptics-rmi/fu-synaptics-rmi-hid-device.c +++ b/plugins/synaptics-rmi/fu-synaptics-rmi-hid-device.c @@ -32,17 +32,17 @@ G_DEFINE_TYPE(FuSynapticsRmiHidDevice, fu_synaptics_rmi_hid_device, FU_TYPE_SYNA #define RMI_DEVICE_DEFAULT_TIMEOUT 2000 -#define HID_RMI4_REPORT_ID 0 -#define HID_RMI4_READ_INPUT_COUNT 1 -#define HID_RMI4_READ_INPUT_DATA 2 -#define HID_RMI4_READ_OUTPUT_ADDR 2 -#define HID_RMI4_READ_OUTPUT_COUNT 4 -#define HID_RMI4_WRITE_OUTPUT_COUNT 1 -#define HID_RMI4_WRITE_OUTPUT_ADDR 2 -#define HID_RMI4_WRITE_OUTPUT_DATA 4 -#define HID_RMI4_FEATURE_MODE 1 +#define HID_RMI4_REPORT_ID 0 +#define HID_RMI4_READ_INPUT_COUNT 1 +#define HID_RMI4_READ_INPUT_DATA 2 +#define HID_RMI4_READ_OUTPUT_ADDR 2 +#define HID_RMI4_READ_OUTPUT_COUNT 4 +#define HID_RMI4_WRITE_OUTPUT_COUNT 1 +#define HID_RMI4_WRITE_OUTPUT_ADDR 2 +#define HID_RMI4_WRITE_OUTPUT_DATA 4 +#define HID_RMI4_FEATURE_MODE 1 #define HID_RMI4_ATTN_INTERRUPT_SOURCES 1 -#define HID_RMI4_ATTN_DATA 2 +#define HID_RMI4_ATTN_DATA 2 /* * This bit disables whatever sleep mode may be selected by the sleep_mode @@ -559,10 +559,9 @@ static void fu_synaptics_rmi_hid_device_set_progress(FuDevice *self, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 3, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 88, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 7, "attach"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); } diff --git a/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.c b/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.c index 3a7fb5a69..b734a674f 100644 --- a/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.c +++ b/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.c @@ -39,6 +39,7 @@ typedef enum { RMI_PARTITION_ID_DISPLAY_CONFIG, RMI_PARTITION_ID_EXTERNAL_TOUCH_AFE_CONFIG, RMI_PARTITION_ID_UTILITY_PARAMETER, + RMI_PARTITION_ID_PUBKEY, RMI_PARTITION_ID_FIXED_LOCATION_DATA = 0x0E, } RmiPartitionId; @@ -71,6 +72,8 @@ rmi_firmware_partition_id_to_string(RmiPartitionId partition_id) return "external-touch-afe-config"; if (partition_id == RMI_PARTITION_ID_UTILITY_PARAMETER) return "utility-parameter"; + if (partition_id == RMI_PARTITION_ID_PUBKEY) + return "pubkey"; if (partition_id == RMI_PARTITION_ID_FIXED_LOCATION_DATA) return "fixed-location-data"; return NULL; @@ -120,6 +123,53 @@ fu_synaptics_rmi_v7_device_detach(FuDevice *device, FuProgress *progress, GError return TRUE; } +static gboolean +fu_synaptics_rmi_v7_device_erase_partition(FuSynapticsRmiDevice *self, + guint8 partition_id, + GError **error) +{ + FuSynapticsRmiFunction *f34; + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + g_autoptr(GByteArray) erase_cmd = g_byte_array_new(); + + /* f34 */ + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + fu_byte_array_append_uint8(erase_cmd, partition_id); + fu_byte_array_append_uint32(erase_cmd, 0x0, G_LITTLE_ENDIAN); + fu_byte_array_append_uint8(erase_cmd, RMI_FLASH_CMD_ERASE); + + fu_byte_array_append_uint8(erase_cmd, flash->bootloader_id[0]); + fu_byte_array_append_uint8(erase_cmd, flash->bootloader_id[1]); + + g_usleep(1000 * 1000); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 1, + erase_cmd, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to unlock erasing: "); + return FALSE; + } + g_usleep(1000 * 100); + + /* wait for ATTN */ + if (!fu_synaptics_rmi_device_wait_for_idle(self, + RMI_F34_ERASE_WAIT_MS, + RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to wait for idle: "); + return FALSE; + } + + if (!fu_synaptics_rmi_device_poll_wait(self, error)) { + g_prefix_error(error, "failed to get flash success: "); + return FALSE; + } + return TRUE; +} + static gboolean fu_synaptics_rmi_v7_device_erase_all(FuSynapticsRmiDevice *self, GError **error) { @@ -134,7 +184,7 @@ fu_synaptics_rmi_v7_device_erase_all(FuSynapticsRmiDevice *self, GError **error) fu_byte_array_append_uint8(erase_cmd, RMI_PARTITION_ID_CORE_CODE); fu_byte_array_append_uint32(erase_cmd, 0x0, G_LITTLE_ENDIAN); - if (flash->bootloader_id[1] == 8) { + if (flash->bootloader_id[1] >= 8) { /* For bootloader v8 */ fu_byte_array_append_uint8(erase_cmd, RMI_FLASH_CMD_ERASE_AP); } else { @@ -145,7 +195,7 @@ fu_synaptics_rmi_v7_device_erase_all(FuSynapticsRmiDevice *self, GError **error) fu_byte_array_append_uint8(erase_cmd, flash->bootloader_id[1]); /* for BL8 device, we need hold 1 seconds after querying F34 status to * avoid not get attention by following giving erase command */ - if (flash->bootloader_id[1] == 8) + if (flash->bootloader_id[1] >= 8) g_usleep(1000 * 1000); if (!fu_synaptics_rmi_device_write(self, f34->data_base + 1, @@ -156,7 +206,7 @@ fu_synaptics_rmi_v7_device_erase_all(FuSynapticsRmiDevice *self, GError **error) return FALSE; } g_usleep(1000 * 100); - if (flash->bootloader_id[1] == 8) { + if (flash->bootloader_id[1] >= 8) { /* wait for ATTN */ if (!fu_synaptics_rmi_device_wait_for_idle(self, RMI_F34_ERASE_WAIT_MS, @@ -422,6 +472,133 @@ fu_synaptics_rmi_v7_device_write_partition(FuSynapticsRmiDevice *self, return TRUE; } +GBytes * +fu_synaptics_rmi_v7_device_get_pubkey(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + FuSynapticsRmiFunction *f34; + const gsize key_size = RMI_KEY_SIZE_2K; + g_autoptr(GByteArray) req_addr_zero = g_byte_array_new(); + g_autoptr(GByteArray) req_cmd = g_byte_array_new(); + g_autoptr(GByteArray) req_partition_id = g_byte_array_new(); + g_autoptr(GByteArray) req_transfer_length = g_byte_array_new(); + g_autoptr(GByteArray) res = NULL; + g_autoptr(GByteArray) pubkey = g_byte_array_new(); + + /* f34 */ + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return NULL; + + /* set partition id for bootloader 7 */ + fu_byte_array_append_uint8(req_partition_id, RMI_PARTITION_ID_PUBKEY); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 0x1, + req_partition_id, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write flash partition id: "); + return NULL; + } + fu_byte_array_append_uint16(req_addr_zero, 0x0, G_LITTLE_ENDIAN); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 0x2, + req_addr_zero, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write flash config address: "); + return NULL; + } + + /* set transfer length */ + fu_byte_array_append_uint16(req_transfer_length, + key_size / flash->block_size, + G_LITTLE_ENDIAN); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 0x3, + req_transfer_length, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to set transfer length: "); + return NULL; + } + + /* set command to read */ + fu_byte_array_append_uint8(req_cmd, RMI_FLASH_CMD_READ); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 0x4, + req_cmd, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write command to read: "); + return NULL; + } + if (!fu_synaptics_rmi_device_poll_wait(self, error)) { + g_prefix_error(error, "failed to wait: "); + return NULL; + } + + /* read back entire buffer in blocks */ + res = fu_synaptics_rmi_device_read(self, f34->data_base + 0x5, (guint32)key_size, error); + if (res == NULL) { + g_prefix_error(error, "failed to read: "); + return NULL; + } + + for (guint i = 0; i < res->len; i++) + fu_byte_array_append_uint8(pubkey, res->data[res->len - i - 1]); + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&pubkey)); +} + +gboolean +fu_synaptics_rmi_v7_device_secure_check(FuSynapticsRmiDevice *self, + FuFirmware *firmware, + GError **error) +{ + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + g_autoptr(GBytes) pubkey = NULL; + g_autoptr(GPtrArray) imgs = NULL; + + if (flash->bootloader_id[1] >= 10 || flash->has_pubkey == FALSE) + return TRUE; + + pubkey = fu_synaptics_rmi_v7_device_get_pubkey(self, error); + if (pubkey == NULL) { + g_prefix_error(error, "get pubkey failed: "); + return FALSE; + } + + imgs = fu_firmware_get_images(firmware); + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + const gchar *id = fu_firmware_get_id(img); + g_autoptr(GBytes) byte_payload = NULL; + g_autoptr(GBytes) byte_signature = NULL; + g_autofree gchar *id_signature = NULL; + + if (g_str_has_suffix(id, "-signature")) + continue; + id_signature = g_strdup_printf("%s-signature", id); + byte_signature = fu_firmware_get_image_by_id_bytes(firmware, id_signature, NULL); + if (byte_signature == NULL) + continue; + byte_payload = fu_firmware_get_bytes(img, error); + if (byte_payload == NULL) + return FALSE; + if (!fu_synaptics_verify_sha256_signature(byte_payload, + pubkey, + byte_signature, + error)) { + g_prefix_error(error, "%s secure check failed: ", id); + return FALSE; + } + g_debug("%s signature verified successfully", id); + } + return TRUE; +} + gboolean fu_synaptics_rmi_v7_device_write_firmware(FuDevice *device, FuFirmware *firmware, @@ -441,15 +618,46 @@ fu_synaptics_rmi_v7_device_write_firmware(FuDevice *device, /* progress */ fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "disable-sleep"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 2, "fixed-location-data"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 3, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 0, "flash-config"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 89, "core-code"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 2, "core-config"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 2, "external-touch-afe-config"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 2, "display-config"); + if (flash->bootloader_id[1] > 8) { + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "disable-sleep"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 0, "verify-signature"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "fixed-location-data"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 8, "flash-config"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 9, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 81, "core-code"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "core-config"); + fu_progress_add_step(progress, + FWUPD_STATUS_DEVICE_WRITE, + 0, + "external-touch-afe-config"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 0, "display-config"); + } else if (flash->bootloader_id[1] == 8) { + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "disable-sleep"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 0, "verify-signature"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 0, "fixed-location-data"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 16, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 0, "flash-config"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 81, "core-code"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "core-config"); + fu_progress_add_step(progress, + FWUPD_STATUS_DEVICE_WRITE, + 0, + "external-touch-afe-config"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 0, "display-config"); + } else { + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "disable-sleep"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 2, "verify-signature"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 2, "fixed-location-data"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 3, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 89, "core-code"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 2, "core-config"); + fu_progress_add_step(progress, + FWUPD_STATUS_DEVICE_WRITE, + 2, + "external-touch-afe-config"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 2, "display-config"); + } /* we should be in bootloader mode now, but check anyway */ if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { @@ -472,7 +680,7 @@ fu_synaptics_rmi_v7_device_write_firmware(FuDevice *device, bytes_cfg = fu_firmware_get_image_by_id_bytes(firmware, "config", error); if (bytes_cfg == NULL) return FALSE; - if (flash->bootloader_id[1] == 8) { + if (flash->bootloader_id[1] >= 8) { bytes_flashcfg = fu_firmware_get_image_by_id_bytes(firmware, "flash-config", error); if (bytes_flashcfg == NULL) return FALSE; @@ -486,6 +694,11 @@ fu_synaptics_rmi_v7_device_write_firmware(FuDevice *device, return FALSE; fu_progress_step_done(progress); + /* verify signature */ + if (!fu_synaptics_rmi_v7_device_secure_check(self, firmware, error)) + return FALSE; + fu_progress_step_done(progress); + /* write fld before erase if exists */ if (bytes_fld != NULL) { if (!fu_synaptics_rmi_v7_device_write_partition( @@ -500,15 +713,12 @@ fu_synaptics_rmi_v7_device_write_firmware(FuDevice *device, } fu_progress_step_done(progress); - /* erase all */ - if (!fu_synaptics_rmi_v7_device_erase_all(self, error)) { - g_prefix_error(error, "failed to erase all: "); - return FALSE; - } - fu_progress_step_done(progress); - - /* write flash config for v8 */ - if (bytes_flashcfg != NULL) { + /* write flash config for BL > v8 */ + if (flash->bootloader_id[1] > 8) { + if (!fu_synaptics_rmi_v7_device_erase_partition(self, + RMI_PARTITION_ID_FLASH_CONFIG, + error)) + return FALSE; if (!fu_synaptics_rmi_v7_device_write_partition(self, firmware, "flash-config", @@ -517,9 +727,29 @@ fu_synaptics_rmi_v7_device_write_firmware(FuDevice *device, fu_progress_get_child(progress), error)) return FALSE; + fu_progress_step_done(progress); + } + + /* erase all */ + if (!fu_synaptics_rmi_v7_device_erase_all(self, error)) { + g_prefix_error(error, "failed to erase all: "); + return FALSE; } fu_progress_step_done(progress); + /* write flash config for v8 */ + if (flash->bootloader_id[1] == 8) { + if (!fu_synaptics_rmi_v7_device_write_partition(self, + firmware, + "flash-config", + RMI_PARTITION_ID_FLASH_CONFIG, + bytes_flashcfg, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + } + /* write core code */ if (!fu_synaptics_rmi_v7_device_write_partition(self, firmware, @@ -692,6 +922,10 @@ fu_synaptics_rmi_device_read_flash_config_v7(FuSynapticsRmiDevice *self, GError flash->block_count_fw = tbl.partition_len; continue; } + if (tbl.partition_id == RMI_PARTITION_ID_PUBKEY) { + flash->has_pubkey = TRUE; + continue; + } if (tbl.partition_id == RMI_PARTITION_ID_NONE) break; } diff --git a/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.h b/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.h index 05ed59597..88ddb211c 100644 --- a/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.h +++ b/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.h @@ -20,3 +20,9 @@ gboolean fu_synaptics_rmi_v7_device_setup(FuSynapticsRmiDevice *self, GError **error); gboolean fu_synaptics_rmi_v7_device_query_status(FuSynapticsRmiDevice *self, GError **error); +gboolean +fu_synaptics_rmi_v7_device_secure_check(FuSynapticsRmiDevice *self, + FuFirmware *firmware, + GError **error); +GBytes * +fu_synaptics_rmi_v7_device_get_pubkey(FuSynapticsRmiDevice *self, GError **error);