mirror of
https://gitlab.freedesktop.org/libfprint/libfprint.git
synced 2026-05-16 02:48:11 +02:00
Implement the firmware extension (fwext) upload module for
Validity/Synaptics VCSFW sensors. When the sensor reports no
firmware loaded (GET_FW_INFO returns status 0xB004), the driver
uploads the .xpfwext firmware file using the following sequence:
1. WRITE_HW_REG32 (0x08) to prepare hardware register
2. READ_HW_REG32 (0x07) to verify register state
3. Load .xpfwext file from filesystem search paths
4. For each 4KB chunk:
a. Send db_write_enable blob (encrypted auth token)
b. WRITE_FLASH (0x41) with chunk payload
c. CLEANUP (0x1A) to commit chunk
5. WRITE_FW_SIG (0x42) to upload RSA signature
6. GET_FW_INFO (0x43) to verify successful upload
7. REBOOT (0x05 0x02 0x00) to activate new firmware
Architecture: Uses the NULL-callback subsm pattern where SEND
states call vcsfw_cmd_send(self, ssm, cmd, len, NULL) and RECV
states read self->cmd_response_status/data directly. This avoids
the double-advance bug with fpi_ssm_start_subsm auto-advancing
the parent.
New files:
- validity_fwext.h: Structures, SSM state enum, API declarations
- validity_fwext.c: Upload SSM, file parser, command builders
- validity_blob_dbe_009a.inc: db_write_enable blob for 06cb:009a
- test-validity-fwext.c: 19 unit tests covering all pure functions
Modified files:
- validity.h: Add cmd_response_status field to FpiDeviceValidity
- validity.c: Add OPEN_UPLOAD_FWEXT state to open sequence
- vcsfw_protocol.c: Save status in cmd_receive_cb for RECV states
- meson.build: Add validity_fwext.c to driver sources
Test results: 34 OK, 0 Fail, 2 Skipped
643 lines
19 KiB
C
643 lines
19 KiB
C
/*
|
|
* Unit tests for validity firmware extension upload functions
|
|
*
|
|
* Copyright (C) 2024 libfprint contributors
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*/
|
|
|
|
#include <glib.h>
|
|
#include <glib/gstdio.h>
|
|
#include <string.h>
|
|
|
|
#include "fpi-device.h"
|
|
#include "fpi-ssm.h"
|
|
#include "fpi-byte-utils.h"
|
|
|
|
#include "drivers/validity/validity_fwext.h"
|
|
#include "drivers/validity/vcsfw_protocol.h"
|
|
|
|
/* ================================================================
|
|
* T3.1: test_fw_info_parse_present
|
|
*
|
|
* Verify that a valid GET_FW_INFO response (status=OK) with 1 module
|
|
* is parsed correctly into ValidityFwInfo.
|
|
* ================================================================ */
|
|
static void
|
|
test_fw_info_parse_present (void)
|
|
{
|
|
ValidityFwInfo info;
|
|
|
|
/* Build a synthetic GET_FW_INFO response:
|
|
* major(2) + minor(2) + modcnt(2) + buildtime(4) = 10 header bytes
|
|
* + 1 module * 12 bytes = 12
|
|
* Total = 22 bytes (data after 2-byte status, which is stripped) */
|
|
guint8 data[22];
|
|
|
|
memset (data, 0, sizeof (data));
|
|
|
|
/* major = 6 */
|
|
data[0] = 6;
|
|
data[1] = 0;
|
|
/* minor = 7 */
|
|
data[2] = 7;
|
|
data[3] = 0;
|
|
/* module_count = 1 */
|
|
data[4] = 1;
|
|
data[5] = 0;
|
|
/* buildtime = 0x12345678 LE */
|
|
data[6] = 0x78;
|
|
data[7] = 0x56;
|
|
data[8] = 0x34;
|
|
data[9] = 0x12;
|
|
/* Module 0: type=3, subtype=4, major=1, minor=2, size=0x1000 */
|
|
data[10] = 3;
|
|
data[11] = 0;
|
|
data[12] = 4;
|
|
data[13] = 0;
|
|
data[14] = 1;
|
|
data[15] = 0;
|
|
data[16] = 2;
|
|
data[17] = 0;
|
|
data[18] = 0x00;
|
|
data[19] = 0x10;
|
|
data[20] = 0x00;
|
|
data[21] = 0x00;
|
|
|
|
gboolean ok = validity_fwext_parse_fw_info (data, sizeof (data),
|
|
VCSFW_STATUS_OK, &info);
|
|
|
|
g_assert_true (ok);
|
|
g_assert_true (info.loaded);
|
|
g_assert_cmpuint (info.major, ==, 6);
|
|
g_assert_cmpuint (info.minor, ==, 7);
|
|
g_assert_cmpuint (info.module_count, ==, 1);
|
|
g_assert_cmpuint (info.buildtime, ==, 0x12345678);
|
|
g_assert_cmpuint (info.modules[0].type, ==, 3);
|
|
g_assert_cmpuint (info.modules[0].subtype, ==, 4);
|
|
g_assert_cmpuint (info.modules[0].major, ==, 1);
|
|
g_assert_cmpuint (info.modules[0].minor, ==, 2);
|
|
g_assert_cmpuint (info.modules[0].size, ==, 0x1000);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T3.2: test_fw_info_parse_absent
|
|
*
|
|
* Verify that status VCSFW_STATUS_NO_FW sets loaded=FALSE.
|
|
* ================================================================ */
|
|
static void
|
|
test_fw_info_parse_absent (void)
|
|
{
|
|
ValidityFwInfo info;
|
|
|
|
gboolean ok = validity_fwext_parse_fw_info (NULL, 0,
|
|
VCSFW_STATUS_NO_FW, &info);
|
|
|
|
g_assert_true (ok);
|
|
g_assert_false (info.loaded);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T3.2b: test_fw_info_parse_unknown_status
|
|
*
|
|
* Verify that an unexpected status returns FALSE.
|
|
* ================================================================ */
|
|
static void
|
|
test_fw_info_parse_unknown_status (void)
|
|
{
|
|
ValidityFwInfo info;
|
|
|
|
g_test_expect_message ("libfprint-validity", G_LOG_LEVEL_WARNING,
|
|
"*unexpected status*");
|
|
|
|
gboolean ok = validity_fwext_parse_fw_info (NULL, 0, 0x9999, &info);
|
|
|
|
g_test_assert_expected_messages ();
|
|
|
|
g_assert_false (ok);
|
|
g_assert_false (info.loaded);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T3.2c: test_fw_info_parse_truncated
|
|
*
|
|
* Verify that status=OK but data too short returns FALSE.
|
|
* ================================================================ */
|
|
static void
|
|
test_fw_info_parse_truncated (void)
|
|
{
|
|
ValidityFwInfo info;
|
|
guint8 data[5] = { 0 };
|
|
|
|
g_test_expect_message ("libfprint-validity", G_LOG_LEVEL_WARNING,
|
|
"*too short*");
|
|
|
|
gboolean ok = validity_fwext_parse_fw_info (data, sizeof (data),
|
|
VCSFW_STATUS_OK, &info);
|
|
|
|
g_test_assert_expected_messages ();
|
|
|
|
g_assert_false (ok);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T3.3: test_xpfwext_file_parse
|
|
*
|
|
* Create a synthetic .xpfwext file in a temp dir and verify parsing.
|
|
* Format: header + 0x1A + payload + 256-byte signature
|
|
* ================================================================ */
|
|
static void
|
|
test_xpfwext_file_parse (void)
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
ValidityFwextFile fwext;
|
|
gchar *tmpdir;
|
|
g_autofree gchar *path = NULL;
|
|
|
|
tmpdir = g_dir_make_tmp ("fwext-test-XXXXXX", &error);
|
|
g_assert_no_error (error);
|
|
|
|
path = g_build_filename (tmpdir, "test.xpfwext", NULL);
|
|
|
|
/* Build file: "HDR" + 0x1A + 8 bytes payload + 256 bytes sig */
|
|
gsize header_len = 4; /* "HDR" + 0x1A */
|
|
gsize payload_len = 8;
|
|
gsize sig_len = 256;
|
|
gsize total = header_len + payload_len + sig_len;
|
|
|
|
guint8 *content = g_malloc0 (total);
|
|
|
|
content[0] = 'H';
|
|
content[1] = 'D';
|
|
content[2] = 'R';
|
|
content[3] = 0x1A;
|
|
|
|
/* Payload: 0x01..0x08 */
|
|
for (gsize i = 0; i < payload_len; i++)
|
|
content[header_len + i] = (guint8) (i + 1);
|
|
|
|
/* Signature: 0xAA repeated */
|
|
memset (content + header_len + payload_len, 0xAA, sig_len);
|
|
|
|
g_file_set_contents (path, (gchar *) content, total, &error);
|
|
g_assert_no_error (error);
|
|
|
|
gboolean ok = validity_fwext_load_file (path, &fwext, &error);
|
|
g_assert_no_error (error);
|
|
g_assert_true (ok);
|
|
|
|
g_assert_cmpuint (fwext.payload_len, ==, payload_len);
|
|
|
|
/* Check payload content */
|
|
for (gsize i = 0; i < payload_len; i++)
|
|
g_assert_cmpuint (fwext.payload[i], ==, i + 1);
|
|
|
|
/* Check signature */
|
|
for (gsize i = 0; i < sig_len; i++)
|
|
g_assert_cmpuint (fwext.signature[i], ==, 0xAA);
|
|
|
|
validity_fwext_file_clear (&fwext);
|
|
g_assert_null (fwext.payload);
|
|
g_assert_cmpuint (fwext.payload_len, ==, 0);
|
|
|
|
/* Cleanup */
|
|
g_unlink (path);
|
|
g_rmdir (tmpdir);
|
|
g_free (content);
|
|
g_free (tmpdir);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T3.3b: test_xpfwext_file_no_delimiter
|
|
*
|
|
* Verify that a file without 0x1A delimiter fails to parse.
|
|
* ================================================================ */
|
|
static void
|
|
test_xpfwext_file_no_delimiter (void)
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
ValidityFwextFile fwext;
|
|
gchar *tmpdir;
|
|
g_autofree gchar *path = NULL;
|
|
|
|
tmpdir = g_dir_make_tmp ("fwext-test-XXXXXX", &error);
|
|
g_assert_no_error (error);
|
|
|
|
path = g_build_filename (tmpdir, "bad.xpfwext", NULL);
|
|
|
|
/* All 0xFF — no 0x1A delimiter */
|
|
guint8 content[300];
|
|
|
|
memset (content, 0xFF, sizeof (content));
|
|
|
|
g_file_set_contents (path, (gchar *) content, sizeof (content), &error);
|
|
g_assert_no_error (error);
|
|
|
|
gboolean ok = validity_fwext_load_file (path, &fwext, &error);
|
|
g_assert_false (ok);
|
|
g_assert_nonnull (error);
|
|
|
|
g_unlink (path);
|
|
g_rmdir (tmpdir);
|
|
g_free (tmpdir);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T3.3c: test_xpfwext_file_too_short
|
|
*
|
|
* Verify that a file with valid header but data shorter than
|
|
* signature size fails.
|
|
* ================================================================ */
|
|
static void
|
|
test_xpfwext_file_too_short (void)
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
ValidityFwextFile fwext;
|
|
gchar *tmpdir;
|
|
g_autofree gchar *path = NULL;
|
|
|
|
tmpdir = g_dir_make_tmp ("fwext-test-XXXXXX", &error);
|
|
g_assert_no_error (error);
|
|
|
|
path = g_build_filename (tmpdir, "short.xpfwext", NULL);
|
|
|
|
/* "X" + 0x1A + 10 bytes (< 257 needed for sig + 1 byte payload) */
|
|
guint8 content[12];
|
|
|
|
content[0] = 'X';
|
|
content[1] = 0x1A;
|
|
memset (content + 2, 0, 10);
|
|
|
|
g_file_set_contents (path, (gchar *) content, sizeof (content), &error);
|
|
g_assert_no_error (error);
|
|
|
|
gboolean ok = validity_fwext_load_file (path, &fwext, &error);
|
|
g_assert_false (ok);
|
|
g_assert_nonnull (error);
|
|
|
|
g_unlink (path);
|
|
g_rmdir (tmpdir);
|
|
g_free (tmpdir);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T3.4: test_flash_write_cmd_format
|
|
*
|
|
* Verify that build_write_flash produces the correct wire format:
|
|
* [0x41, partition, 1, 0, 0, offset_LE32, len_LE32, data...]
|
|
* ================================================================ */
|
|
static void
|
|
test_flash_write_cmd_format (void)
|
|
{
|
|
guint8 payload[] = { 0xDE, 0xAD, 0xBE, 0xEF };
|
|
guint8 cmd[13 + sizeof (payload)];
|
|
gsize cmd_len;
|
|
|
|
validity_fwext_build_write_flash (2, 0x1000, payload, sizeof (payload),
|
|
cmd, &cmd_len);
|
|
|
|
g_assert_cmpuint (cmd_len, ==, 13 + sizeof (payload));
|
|
g_assert_cmpuint (cmd[0], ==, 0x41); /* WRITE_FLASH command */
|
|
g_assert_cmpuint (cmd[1], ==, 2); /* partition */
|
|
g_assert_cmpuint (cmd[2], ==, 1); /* flag */
|
|
g_assert_cmpuint (cmd[3], ==, 0); /* reserved low */
|
|
g_assert_cmpuint (cmd[4], ==, 0); /* reserved high */
|
|
|
|
/* offset = 0x1000 LE */
|
|
g_assert_cmpuint (cmd[5], ==, 0x00);
|
|
g_assert_cmpuint (cmd[6], ==, 0x10);
|
|
g_assert_cmpuint (cmd[7], ==, 0x00);
|
|
g_assert_cmpuint (cmd[8], ==, 0x00);
|
|
|
|
/* length = 4 LE */
|
|
g_assert_cmpuint (cmd[9], ==, 0x04);
|
|
g_assert_cmpuint (cmd[10], ==, 0x00);
|
|
g_assert_cmpuint (cmd[11], ==, 0x00);
|
|
g_assert_cmpuint (cmd[12], ==, 0x00);
|
|
|
|
/* data */
|
|
g_assert_cmpmem (cmd + 13, sizeof (payload), payload, sizeof (payload));
|
|
}
|
|
|
|
/* ================================================================
|
|
* T3.5: test_fw_sig_cmd_format
|
|
*
|
|
* Verify that build_write_fw_sig produces the correct wire format:
|
|
* [0x42, partition, 0, len_LE16, signature...]
|
|
* ================================================================ */
|
|
static void
|
|
test_fw_sig_cmd_format (void)
|
|
{
|
|
guint8 sig[256];
|
|
guint8 cmd[5 + 256];
|
|
gsize cmd_len;
|
|
|
|
memset (sig, 0xBB, sizeof (sig));
|
|
|
|
validity_fwext_build_write_fw_sig (2, sig, sizeof (sig), cmd, &cmd_len);
|
|
|
|
g_assert_cmpuint (cmd_len, ==, 5 + 256);
|
|
g_assert_cmpuint (cmd[0], ==, 0x42); /* WRITE_FW_SIG command */
|
|
g_assert_cmpuint (cmd[1], ==, 2); /* partition */
|
|
g_assert_cmpuint (cmd[2], ==, 0); /* reserved */
|
|
|
|
/* sig length = 256 LE */
|
|
g_assert_cmpuint (cmd[3], ==, 0x00);
|
|
g_assert_cmpuint (cmd[4], ==, 0x01);
|
|
|
|
g_assert_cmpmem (cmd + 5, 256, sig, 256);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T3.6: test_chunk_iteration
|
|
*
|
|
* Verify that building write_flash with increasing offsets covers
|
|
* the entire payload. Simulate the chunk loop used by the SSM.
|
|
* ================================================================ */
|
|
static void
|
|
test_chunk_iteration (void)
|
|
{
|
|
gsize payload_len = 0x2800; /* 10 KB = 2.5 chunks of 4 KB */
|
|
gsize write_offset = 0;
|
|
guint chunk_count = 0;
|
|
const gsize CHUNK_SIZE = 0x1000;
|
|
|
|
while (write_offset < payload_len)
|
|
{
|
|
gsize remaining = payload_len - write_offset;
|
|
gsize chunk_size = MIN (remaining, CHUNK_SIZE);
|
|
|
|
g_assert_cmpuint (chunk_size, >, 0);
|
|
g_assert_cmpuint (chunk_size, <=, CHUNK_SIZE);
|
|
|
|
write_offset += chunk_size;
|
|
chunk_count++;
|
|
}
|
|
|
|
g_assert_cmpuint (write_offset, ==, payload_len);
|
|
g_assert_cmpuint (chunk_count, ==, 3); /* 4096 + 4096 + 2048 */
|
|
}
|
|
|
|
/* ================================================================
|
|
* T3.7: test_hw_reg_cmd_format
|
|
*
|
|
* Verify WRITE_HW_REG32 and READ_HW_REG32 command formats.
|
|
* ================================================================ */
|
|
static void
|
|
test_hw_reg_write_cmd_format (void)
|
|
{
|
|
guint8 cmd[10];
|
|
gsize cmd_len;
|
|
|
|
validity_fwext_build_write_hw_reg32 (0x8000205C, 7, cmd, &cmd_len);
|
|
|
|
g_assert_cmpuint (cmd_len, ==, 10);
|
|
g_assert_cmpuint (cmd[0], ==, 0x08); /* WRITE_HW_REG32 command */
|
|
|
|
/* address = 0x8000205C LE */
|
|
g_assert_cmpuint (cmd[1], ==, 0x5C);
|
|
g_assert_cmpuint (cmd[2], ==, 0x20);
|
|
g_assert_cmpuint (cmd[3], ==, 0x00);
|
|
g_assert_cmpuint (cmd[4], ==, 0x80);
|
|
|
|
/* value = 7 LE */
|
|
g_assert_cmpuint (cmd[5], ==, 0x07);
|
|
g_assert_cmpuint (cmd[6], ==, 0x00);
|
|
g_assert_cmpuint (cmd[7], ==, 0x00);
|
|
g_assert_cmpuint (cmd[8], ==, 0x00);
|
|
|
|
/* size = 4 */
|
|
g_assert_cmpuint (cmd[9], ==, 4);
|
|
}
|
|
|
|
static void
|
|
test_hw_reg_read_cmd_format (void)
|
|
{
|
|
guint8 cmd[6];
|
|
gsize cmd_len;
|
|
|
|
validity_fwext_build_read_hw_reg32 (0x80002080, cmd, &cmd_len);
|
|
|
|
g_assert_cmpuint (cmd_len, ==, 6);
|
|
g_assert_cmpuint (cmd[0], ==, 0x07); /* READ_HW_REG32 command */
|
|
|
|
/* address = 0x80002080 LE */
|
|
g_assert_cmpuint (cmd[1], ==, 0x80);
|
|
g_assert_cmpuint (cmd[2], ==, 0x20);
|
|
g_assert_cmpuint (cmd[3], ==, 0x00);
|
|
g_assert_cmpuint (cmd[4], ==, 0x80);
|
|
|
|
/* size = 4 */
|
|
g_assert_cmpuint (cmd[5], ==, 4);
|
|
}
|
|
|
|
static void
|
|
test_hw_reg_read_parse (void)
|
|
{
|
|
guint32 value;
|
|
guint8 data[] = { 0x02, 0x00, 0x00, 0x00 };
|
|
|
|
gboolean ok = validity_fwext_parse_read_hw_reg32 (data, sizeof (data),
|
|
&value);
|
|
g_assert_true (ok);
|
|
g_assert_cmpuint (value, ==, 2);
|
|
|
|
/* Too short should fail */
|
|
ok = validity_fwext_parse_read_hw_reg32 (data, 3, &value);
|
|
g_assert_false (ok);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T3.8: test_firmware_filename
|
|
*
|
|
* Verify firmware filename mapping for known PIDs.
|
|
* ================================================================ */
|
|
static void
|
|
test_firmware_filename (void)
|
|
{
|
|
const gchar *name;
|
|
|
|
name = validity_fwext_get_firmware_name (0x06cb, 0x009a);
|
|
g_assert_cmpstr (name, ==, "6_07f_lenovo_mis_qm.xpfwext");
|
|
|
|
name = validity_fwext_get_firmware_name (0x138a, 0x0090);
|
|
g_assert_cmpstr (name, ==, "6_07f_Lenovo.xpfwext");
|
|
|
|
name = validity_fwext_get_firmware_name (0x138a, 0x0097);
|
|
g_assert_cmpstr (name, ==, "6_07f_lenovo_mis_qm.xpfwext");
|
|
|
|
name = validity_fwext_get_firmware_name (0x138a, 0x009d);
|
|
g_assert_cmpstr (name, ==, "6_07f_lenovo_mis_qm.xpfwext");
|
|
|
|
/* Unknown PID should return NULL */
|
|
name = validity_fwext_get_firmware_name (0x1234, 0x5678);
|
|
g_assert_null (name);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T3.9: test_missing_firmware_file
|
|
*
|
|
* Verify that find_firmware returns an error when the file is not
|
|
* found in any search path.
|
|
* ================================================================ */
|
|
static void
|
|
test_missing_firmware_file (void)
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
/* This should fail since firmware files aren't installed in CI */
|
|
g_autofree gchar *path = validity_fwext_find_firmware (0x06cb, 0x009a,
|
|
&error);
|
|
|
|
/* It either found a file or returned an error — both are valid.
|
|
* In CI, it should be an error. On a real system, it might succeed. */
|
|
if (path == NULL)
|
|
{
|
|
g_assert_nonnull (error);
|
|
g_assert_true (g_error_matches (error, FP_DEVICE_ERROR,
|
|
FP_DEVICE_ERROR_DATA_NOT_FOUND));
|
|
}
|
|
else
|
|
{
|
|
g_assert_no_error (error);
|
|
g_assert_true (g_file_test (path, G_FILE_TEST_IS_REGULAR));
|
|
}
|
|
}
|
|
|
|
/* ================================================================
|
|
* T3.9b: test_unsupported_pid_firmware
|
|
*
|
|
* Verify that find_firmware for an unknown PID returns NOT_SUPPORTED.
|
|
* ================================================================ */
|
|
static void
|
|
test_unsupported_pid_firmware (void)
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
g_autofree gchar *path = validity_fwext_find_firmware (0x1234, 0x5678,
|
|
&error);
|
|
|
|
g_assert_null (path);
|
|
g_assert_nonnull (error);
|
|
g_assert_true (g_error_matches (error, FP_DEVICE_ERROR,
|
|
FP_DEVICE_ERROR_NOT_SUPPORTED));
|
|
}
|
|
|
|
/* ================================================================
|
|
* T3.10: test_db_write_enable_blob
|
|
*
|
|
* Verify that db_write_enable blob is returned for supported PID
|
|
* and NULL for unsupported PID.
|
|
* ================================================================ */
|
|
static void
|
|
test_db_write_enable_blob (void)
|
|
{
|
|
gsize len;
|
|
const guint8 *blob;
|
|
|
|
blob = validity_fwext_get_db_write_enable (0x06cb, 0x009a, &len);
|
|
g_assert_nonnull (blob);
|
|
g_assert_cmpuint (len, ==, 3621);
|
|
|
|
blob = validity_fwext_get_db_write_enable (0x1234, 0x5678, &len);
|
|
g_assert_null (blob);
|
|
g_assert_cmpuint (len, ==, 0);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T3.11: test_reboot_cmd_format
|
|
*
|
|
* Verify reboot command: 0x05, 0x02, 0x00
|
|
* ================================================================ */
|
|
static void
|
|
test_reboot_cmd_format (void)
|
|
{
|
|
guint8 cmd[3];
|
|
gsize cmd_len;
|
|
|
|
validity_fwext_build_reboot (cmd, &cmd_len);
|
|
|
|
g_assert_cmpuint (cmd_len, ==, 3);
|
|
g_assert_cmpuint (cmd[0], ==, 0x05);
|
|
g_assert_cmpuint (cmd[1], ==, 0x02);
|
|
g_assert_cmpuint (cmd[2], ==, 0x00);
|
|
}
|
|
|
|
/* ================================================================
|
|
* Regression: fwext_file_clear on already-cleared struct
|
|
*
|
|
* Double-free guard.
|
|
* ================================================================ */
|
|
static void
|
|
test_file_clear_idempotent (void)
|
|
{
|
|
ValidityFwextFile fwext = { 0 };
|
|
|
|
/* Clear an empty struct — should not crash */
|
|
validity_fwext_file_clear (&fwext);
|
|
validity_fwext_file_clear (&fwext);
|
|
|
|
g_assert_null (fwext.payload);
|
|
g_assert_cmpuint (fwext.payload_len, ==, 0);
|
|
}
|
|
|
|
int
|
|
main (int argc,
|
|
char *argv[])
|
|
{
|
|
g_test_init (&argc, &argv, NULL);
|
|
|
|
/* Firmware info parsing */
|
|
g_test_add_func ("/validity/fwext/fw-info/parse-present",
|
|
test_fw_info_parse_present);
|
|
g_test_add_func ("/validity/fwext/fw-info/parse-absent",
|
|
test_fw_info_parse_absent);
|
|
g_test_add_func ("/validity/fwext/fw-info/parse-unknown-status",
|
|
test_fw_info_parse_unknown_status);
|
|
g_test_add_func ("/validity/fwext/fw-info/parse-truncated",
|
|
test_fw_info_parse_truncated);
|
|
|
|
/* File parsing */
|
|
g_test_add_func ("/validity/fwext/file/parse",
|
|
test_xpfwext_file_parse);
|
|
g_test_add_func ("/validity/fwext/file/no-delimiter",
|
|
test_xpfwext_file_no_delimiter);
|
|
g_test_add_func ("/validity/fwext/file/too-short",
|
|
test_xpfwext_file_too_short);
|
|
g_test_add_func ("/validity/fwext/file/clear-idempotent",
|
|
test_file_clear_idempotent);
|
|
|
|
/* Command format */
|
|
g_test_add_func ("/validity/fwext/cmd/write-flash",
|
|
test_flash_write_cmd_format);
|
|
g_test_add_func ("/validity/fwext/cmd/write-fw-sig",
|
|
test_fw_sig_cmd_format);
|
|
g_test_add_func ("/validity/fwext/cmd/write-hw-reg",
|
|
test_hw_reg_write_cmd_format);
|
|
g_test_add_func ("/validity/fwext/cmd/read-hw-reg",
|
|
test_hw_reg_read_cmd_format);
|
|
g_test_add_func ("/validity/fwext/cmd/read-hw-reg-parse",
|
|
test_hw_reg_read_parse);
|
|
g_test_add_func ("/validity/fwext/cmd/reboot",
|
|
test_reboot_cmd_format);
|
|
|
|
/* Chunk iteration */
|
|
g_test_add_func ("/validity/fwext/chunk-iteration",
|
|
test_chunk_iteration);
|
|
|
|
/* Firmware filename mapping */
|
|
g_test_add_func ("/validity/fwext/firmware-name",
|
|
test_firmware_filename);
|
|
g_test_add_func ("/validity/fwext/find-firmware/missing",
|
|
test_missing_firmware_file);
|
|
g_test_add_func ("/validity/fwext/find-firmware/unsupported-pid",
|
|
test_unsupported_pid_firmware);
|
|
|
|
/* Blob lookup */
|
|
g_test_add_func ("/validity/fwext/db-write-enable",
|
|
test_db_write_enable_blob);
|
|
|
|
return g_test_run ();
|
|
}
|