Add power failure during update tests
Adds tests that simulate a power failure at different points during
the update flow. The tests are intended to check that the device is
never left in an un-bootable state during the update process when
unexpected power-cycles occur. Note that metadata atomic write
robustness is tested separately under the banked_fw_store tests.
Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: I3326ab787509d208a3ff51d8381819d39929edf2
diff --git a/components/service/fwu/test/ref_scenarios/component.cmake b/components/service/fwu/test/ref_scenarios/component.cmake
index d94fa08..12362ba 100644
--- a/components/service/fwu/test/ref_scenarios/component.cmake
+++ b/components/service/fwu/test/ref_scenarios/component.cmake
@@ -12,4 +12,5 @@
"${CMAKE_CURRENT_LIST_DIR}/image_directory_tests.cpp"
"${CMAKE_CURRENT_LIST_DIR}/invalid_behaviour_tests.cpp"
"${CMAKE_CURRENT_LIST_DIR}/update_scenario_tests.cpp"
+ "${CMAKE_CURRENT_LIST_DIR}/power_failure_tests.cpp"
)
diff --git a/components/service/fwu/test/ref_scenarios/power_failure_tests.cpp b/components/service/fwu/test/ref_scenarios/power_failure_tests.cpp
new file mode 100644
index 0000000..892951c
--- /dev/null
+++ b/components/service/fwu/test/ref_scenarios/power_failure_tests.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <vector>
+#include <protocols/service/fwu/packed-c/status.h>
+#include <CppUTest/TestHarness.h>
+#include <service/fwu/test/fwu_dut_factory/fwu_dut_factory.h>
+#include <service/fwu/test/fwu_dut/fwu_dut.h>
+/*
+ * Tests to check that FWU metadata is never left in an invalid state
+ * during the update process when unexpected power failures occur. Power
+ * failures are simulated by shutting down the DUT after each write to
+ * metadata and re-boot to check all conditions for a successful boot
+ * are met.
+ */
+TEST_GROUP(FwuPowerFailureTests)
+{
+ void setup()
+ {
+ m_dut = NULL;
+ m_fwu_client = NULL;
+ m_metadata_checker = NULL;
+ }
+
+ void teardown()
+ {
+ delete m_metadata_checker;
+ m_metadata_checker = NULL;
+
+ delete m_fwu_client;
+ m_fwu_client = NULL;
+
+ delete m_dut;
+ m_dut = NULL;
+ }
+
+ fwu_dut *m_dut;
+ metadata_checker *m_metadata_checker;
+ fwu_client *m_fwu_client;
+};
+
+TEST(FwuPowerFailureTests, powerFailureDuringStaging)
+{
+ int status = 0;
+ struct uuid_octets uuid;
+ uint32_t stream_handle = 0;
+ std::vector<uint8_t> image_data;
+
+ m_dut = fwu_dut_factory::create(3, true);
+ m_fwu_client = m_dut->create_fwu_client();
+ m_metadata_checker = m_dut->create_metadata_checker();
+
+ m_dut->boot();
+
+ /* Check assumptions about the first boot. */
+ struct boot_info boot_info = m_dut->get_boot_info();
+ UNSIGNED_LONGS_EQUAL(boot_info.boot_index, boot_info.active_index);
+ m_metadata_checker->check_regular(boot_info.boot_index);
+
+ /* Begin staging */
+ status = m_fwu_client->begin_staging();
+ LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
+ m_metadata_checker->check_ready_for_staging(boot_info.boot_index);
+
+ /* Power cycle */
+ m_dut->shutdown();
+ m_dut->boot();
+
+ /* Expect to reboot into the regular state without errors */
+ boot_info = m_dut->get_boot_info();
+ m_metadata_checker->check_regular(boot_info.boot_index);
+
+ /* Begin staging again */
+ status = m_fwu_client->begin_staging();
+ LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
+ m_metadata_checker->check_ready_for_staging(boot_info.boot_index);
+
+ /* Start installing an image but don't commit it */
+ m_dut->whole_volume_image_type_uuid(0, &uuid);
+ status = m_fwu_client->open(&uuid, &stream_handle);
+ LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
+
+ m_dut->generate_image_data(&image_data);
+
+ status = m_fwu_client->write_stream(stream_handle,
+ reinterpret_cast<const uint8_t *>(image_data.data()), image_data.size());
+ LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
+
+ /* Power cycle */
+ m_dut->shutdown();
+ m_dut->boot();
+
+ /* Expect to reboot into the regular state without errors */
+ boot_info = m_dut->get_boot_info();
+ m_metadata_checker->check_regular(boot_info.boot_index);
+
+ /* Begin staging again */
+ status = m_fwu_client->begin_staging();
+ LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
+ m_metadata_checker->check_ready_for_staging(boot_info.boot_index);
+
+ /* Start installing an image but this time commit it without ending staging */
+ m_dut->whole_volume_image_type_uuid(1, &uuid);
+ status = m_fwu_client->open(&uuid, &stream_handle);
+ LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
+
+ m_dut->generate_image_data(&image_data);
+
+ status = m_fwu_client->write_stream(stream_handle,
+ reinterpret_cast<const uint8_t *>(image_data.data()), image_data.size());
+ LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
+
+ status = m_fwu_client->commit(stream_handle, false);
+ LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
+
+ /* Power cycle */
+ m_dut->shutdown();
+ m_dut->boot();
+
+ /* Expect to reboot into the regular state without errors */
+ boot_info = m_dut->get_boot_info();
+ m_metadata_checker->check_regular(boot_info.boot_index);
+}
+
+TEST(FwuPowerFailureTests, powerFailureDuringTrial)
+{
+ int status = 0;
+ struct uuid_octets uuid;
+ uint32_t stream_handle = 0;
+ std::vector<uint8_t> image_data;
+
+ m_dut = fwu_dut_factory::create(3, true);
+ m_fwu_client = m_dut->create_fwu_client();
+ m_metadata_checker = m_dut->create_metadata_checker();
+
+ m_dut->boot();
+
+ /* Check assumptions about the first boot. */
+ struct boot_info boot_info = m_dut->get_boot_info();
+ UNSIGNED_LONGS_EQUAL(boot_info.boot_index, boot_info.active_index);
+ m_metadata_checker->check_regular(boot_info.boot_index);
+
+ /* Begin staging */
+ status = m_fwu_client->begin_staging();
+ LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
+ m_metadata_checker->check_ready_for_staging(boot_info.boot_index);
+
+ /* Install a partial update */
+ m_dut->whole_volume_image_type_uuid(2, &uuid);
+ status = m_fwu_client->open(&uuid, &stream_handle);
+ LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
+
+ m_dut->generate_image_data(&image_data);
+
+ status = m_fwu_client->write_stream(stream_handle,
+ reinterpret_cast<const uint8_t *>(image_data.data()), image_data.size());
+ LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
+
+ status = m_fwu_client->commit(stream_handle, false);
+ LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
+
+ /* Transition to the trial state */
+ status = m_fwu_client->end_staging();
+ LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
+ m_metadata_checker->check_ready_to_activate(boot_info.boot_index);
+
+ /* Power cycle */
+ m_dut->shutdown();
+ m_dut->boot();
+
+ /* Check the metadata after the reboot to activate the update */
+ boot_info = m_dut->get_boot_info();
+ m_metadata_checker->check_trial(boot_info.boot_index);
+
+ /* Power cycle again */
+ m_dut->shutdown();
+ m_dut->boot();
+
+ /* Check trial is still active */
+ boot_info = m_dut->get_boot_info();
+ m_metadata_checker->check_trial(boot_info.boot_index);
+
+ /* Power cycle again */
+ m_dut->shutdown();
+ m_dut->boot();
+
+ /* Check trial is still active */
+ boot_info = m_dut->get_boot_info();
+ m_metadata_checker->check_trial(boot_info.boot_index);
+
+ /* Only image 2 should need accepting as it was the only image updated */
+ m_dut->whole_volume_image_type_uuid(2, &uuid);
+ status = m_fwu_client->accept(&uuid);
+ LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
+
+ /* Power cycle again */
+ m_dut->shutdown();
+ m_dut->boot();
+
+ /* Check transition to regular */
+ boot_info = m_dut->get_boot_info();
+ m_metadata_checker->check_regular(boot_info.boot_index);
+}