blob: be579000b5493a447b5fd026596473fb2218147c [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "bub_image_util.h"
#define ab_init(magic, \
a_priority, a_tries_remaining, a_successful_boot, \
b_priority, b_tries_remaining, b_successful_boot) \
do { \
BubAbData init; \
ops_.write_ab_metadata(&init, (uint8_t[4])magic, \
a_priority, a_tries_remaining, a_successful_boot, \
b_priority, b_tries_remaining, b_successful_boot); \
GenerateMiscImage(&init); \
} while(0)
#define test_ab_flow(a_priority, a_tries_remaining, a_successful_boot, \
b_priority, b_tries_remaining, b_successful_boot, \
expected_result, expected_suffix) \
do { \
char suffix[BUB_SUFFIX_SIZE] = {0}; \
BubAbData ab_result; \
ops_.write_ab_metadata(&ab_result, (uint8_t[4])BUB_BOOT_CTRL_MAGIC, \
a_priority, a_tries_remaining, a_successful_boot, \
b_priority, b_tries_remaining, b_successful_boot); \
EXPECT_EQ(expected_result, \
bub_ab_flow((BubOps*)ops_.bub_ops(), suffix, BUB_SUFFIX_SIZE)); \
EXPECT_EQ(0, bub_memcmp(expected_suffix, suffix, BUB_SUFFIX_SIZE)); \
EXPECT_EQ(0, CompareMiscImage(ab_result)); \
} while (0)
static int converted_utf8_ucs2(const char* data,
const char* raw_bytes,
size_t utf8_num_bytes) {
int ret;
size_t ucs2_num_bytes = sizeof(uint16_t) * utf8_num_bytes;
uint16_t* test_str = (uint16_t*)bub_calloc(ucs2_num_bytes);
if (test_str == NULL) {
fprintf(stderr, "Bad bub_calloc.\n");
return 1;
}
if (utf8_to_ucs2(reinterpret_cast<const uint8_t*>(data),
utf8_num_bytes, test_str, ucs2_num_bytes)) {
bub_free(test_str);
return 1;
}
ret = bub_memcmp(reinterpret_cast<const uint16_t*>(raw_bytes),
test_str, bub_strlen(raw_bytes) + 1);
bub_free(test_str);
return ret;
}
TEST(UtilTest, Utf8toUcs2) {
// UTF-8 3 bytes encoding case.
EXPECT_EQ(0, converted_utf8_ucs2("æ", "\xE6\x00", 3));
// UTF-8 4 bytes encoding case.
EXPECT_EQ(0, converted_utf8_ucs2("€", "\xAC\x20", 4));
// UTF-8 multiple character case.
EXPECT_EQ(0, converted_utf8_ucs2("AB", "\x41\x00\x42\x00", 6));
// These should fail.
// UTF-8 5 bytes encoding case.
EXPECT_NE(0, converted_utf8_ucs2("👦", "\x66\xF4", 5));
}
TEST_F(AbTest, NoValidSlots) {
ab_init(BUB_BOOT_CTRL_MAGIC, 0, 0, 0, 0, 0, 0);
test_ab_flow(
0, 0, 0, 0, 0, 0, // Expected A/B state.
BUB_AB_FLOW_ERROR_NO_VALID_SLOTS, // Expected A/B result.
"\0\0"); // Expected A/B suffix.
}
TEST_F(AbTest, InvalidMetadataMagicInvalidSlots) {
char suffix[BUB_SUFFIX_SIZE] = {0};
BubAbData ab_init;
BubAbData ab_result;
ops_.write_ab_metadata(&ab_init, (uint8_t[4]){'N','O','P','E'},
0, 0, 0, 0, 0, 0);
ops_.write_ab_metadata(&ab_result, (uint8_t[4])BUB_BOOT_CTRL_MAGIC,
15, 7, 0, 15, 7, 0);
GenerateMiscImage(&ab_init);
// Invalid metadata should be found by ab flow at this point. It should reset
// each slot to an 'updating' state on disk so that we may reattempt boot.
EXPECT_EQ(BUB_AB_FLOW_ERROR_INVALID_METADATA,
bub_ab_flow((BubOps*)ops_.bub_ops(), suffix, BUB_SUFFIX_SIZE));
EXPECT_EQ(0, bub_memcmp((char[BUB_SUFFIX_SIZE]){0,0,0},
suffix,
BUB_SUFFIX_SIZE));
EXPECT_EQ(0, CompareMiscImage(ab_result));
// Run again to check slot A is selected with one less try remaining.
test_ab_flow(
15, 6, 0, 15, 7, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a");
}
TEST_F(AbTest, InvalidMetadataMagicValidSlots) {
char suffix[BUB_SUFFIX_SIZE] = {0};
BubAbData ab_init;
BubAbData ab_result;
ops_.write_ab_metadata(&ab_init, (uint8_t[4]){'N','O','P','E'},
15, 0, 1, 14, 0, 1);
ops_.write_ab_metadata(&ab_result, (uint8_t[4])BUB_BOOT_CTRL_MAGIC,
15, 7, 0, 15, 7, 0);
GenerateMiscImage(&ab_init);
// Invalid metadata should be found by ab flow at this point. It should reset
// each slot to an 'updating' state on disk so that we may reattempt boot.
EXPECT_EQ(BUB_AB_FLOW_ERROR_INVALID_METADATA,
bub_ab_flow((BubOps*)ops_.bub_ops(), suffix, BUB_SUFFIX_SIZE));
EXPECT_EQ(0, bub_memcmp((char[BUB_SUFFIX_SIZE]){0,0,0},
suffix,
BUB_SUFFIX_SIZE));
EXPECT_EQ(0, CompareMiscImage(ab_result));
// Run again to check slot A is selected with one less try remaining.
test_ab_flow(
15, 6, 0, 15, 7, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
}
TEST_F(AbTest, SingleSuccessfulSlot) {
ab_init(BUB_BOOT_CTRL_MAGIC, 14, 0, 1, 0, 0, 0);
test_ab_flow(
14, 0, 1, 0, 0, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
}
TEST_F(AbTest, SingleTryingSlot) {
ab_init(BUB_BOOT_CTRL_MAGIC, 14, 3, 0, 0, 0, 0);
test_ab_flow(
14, 2, 0, 0, 0, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
}
TEST_F(AbTest, TwoValidSlotsA) {
ab_init(BUB_BOOT_CTRL_MAGIC, 15, 0, 1, 14, 0, 1);
test_ab_flow(
15, 0, 1, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
}
TEST_F(AbTest, TwoValidSlotsB) {
ab_init(BUB_BOOT_CTRL_MAGIC, 14, 0, 1, 15, 0, 1);
test_ab_flow(
14, 0, 1, 15, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_b"); // Expected A/B suffix.
}
TEST_F(AbTest, TryingFallback) {
ab_init(BUB_BOOT_CTRL_MAGIC, 15, 7, 0, 14, 0, 1);
// Decremented our expected tries_remaining for slot a as we run ab_flow
test_ab_flow(
15, 6, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 5, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 4, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 3, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 2, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 1, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 0, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
// Should revert to slot b.
test_ab_flow(
0, 0, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_b"); // Expected A/B suffix.
}
TEST_F(AbTest, TryingNoFallbackRecovery) {
ab_init(BUB_BOOT_CTRL_MAGIC, 15, 7, 0, 0, 0, 0);
// Decremented our expected tries_remaining for slot a as we run ab_flow
test_ab_flow(
15, 6, 0, 0, 0, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 5, 0, 0, 0, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 4, 0, 0, 0, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 3, 0, 0, 0, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 2, 0, 0, 0, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 1, 0, 0, 0, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 0, 0, 0, 0, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
0, 0, 0, 0, 0, 0, // Expected A/B state.
BUB_AB_FLOW_ERROR_NO_VALID_SLOTS, // Expected A/B result.
"\0\0"); // Expected A/B suffix.
}
TEST_F(AbTest, SingleTryingSuccess) {
ab_init(BUB_BOOT_CTRL_MAGIC, 15, 7, 0, 14, 0, 1);
// Decremented our expected tries_remaining for slot a. Boot was a success
// on our 6th try and we reboot 2 more times to make sure we stick to the
// same slot.
test_ab_flow(
15, 6, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 5, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 4, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 3, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 2, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 1, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 0, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
// Boot Control HAL should do this.
ab_init(BUB_BOOT_CTRL_MAGIC, 15, 0, 1, 14, 0, 1);
test_ab_flow(
15, 0, 1, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
// Should not have changed still.
test_ab_flow(
15, 0, 1, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
}
TEST_F(AbTest, TwoTryingRecovery) {
ab_init(BUB_BOOT_CTRL_MAGIC, 15, 7, 0, 14, 7, 0);
// Decremented our expected tries_remaining for slot a.
test_ab_flow(
15, 6, 0, 14, 7, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 5, 0, 14, 7, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 4, 0, 14, 7, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 3, 0, 14, 7, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 2, 0, 14, 7, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 1, 0, 14, 7, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
test_ab_flow(
15, 0, 0, 14, 7, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
// At this point a should have run out of tries, so we expect the other
// updating slot to be chosen.
// Decremented our expected tries_remaining for slot b.
test_ab_flow(
0, 0, 0, 14, 6, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_b"); // Expected A/B suffix.
test_ab_flow(
0, 0, 0, 14, 5, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_b"); // Expected A/B suffix.
test_ab_flow(
0, 0, 0, 14, 4, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_b"); // Expected A/B suffix.
test_ab_flow(
0, 0, 0, 14, 3, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_b"); // Expected A/B suffix.
test_ab_flow(
0, 0, 0, 14, 2, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_b"); // Expected A/B suffix.
test_ab_flow(
0, 0, 0, 14, 1, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_b"); // Expected A/B suffix.
test_ab_flow(
0, 0, 0, 14, 0, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_b"); // Expected A/B suffix.
test_ab_flow(
0, 0, 0, 0, 0, 0, // Expected A/B state.
BUB_AB_FLOW_ERROR_NO_VALID_SLOTS, // Expected A/B result.
"\0\0"); // Expected A/B suffix.
}
TEST_F(AbTest, MarkedInvalidFallback) {
ab_init(BUB_BOOT_CTRL_MAGIC, 15, 0, 1, 14, 0, 1);
// Initially selects slot a.
test_ab_flow(
15, 0, 1, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
// Invalidate slot a. We expect this slot to be all zero values with slot b
// unchanged.
EXPECT_TRUE(bub_ab_mark_as_invalid((BubOps*)ops_.bub_ops(), "_a"));
// Should select slot b now.
test_ab_flow(
0, 0, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_b"); // Expected A/B suffix.
}
TEST_F(AbTest, ValidAndInvalidHigherPriority) {
ab_init(BUB_BOOT_CTRL_MAGIC, 14, 0, 1, 15, 0, 0);
// Normalizes and selects slot a.
test_ab_flow(
14, 0, 1, 0, 0, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_a"); // Expected A/B suffix.
}
TEST_F(AbTest, ValidAndUpdatingBadSuccessfulBoot) {
ab_init(BUB_BOOT_CTRL_MAGIC, 14, 0, 1, 15, 7, 1);
// Normalizes and selects slot b.
test_ab_flow(
14, 0, 1, 15, 6, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_b"); // Expected A/B suffix.
}
TEST_F(AbTest, InvalidBadTriesRemainingAndValid) {
ab_init(BUB_BOOT_CTRL_MAGIC, 0, 7, 0, 14, 0, 1);
// Normalizes and selects slot b.
test_ab_flow(
0, 0, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_b"); // Expected A/B suffix.
}
TEST_F(AbTest, InvalidBadSuccessfulBootandValid) {
ab_init(BUB_BOOT_CTRL_MAGIC, 0, 0, 1, 14, 0, 1);
// Normalizes and selects slot b.
test_ab_flow(
0, 0, 0, 14, 0, 1, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_b"); // Expected A/B suffix.
}
TEST_F(AbTest, InvalidTriesBootAndUpdatingBadSuccessfulBoot) {
ab_init(BUB_BOOT_CTRL_MAGIC, 0, 7, 1, 15, 7, 1);
// Normalizes and selects slot b.
test_ab_flow(
0, 0, 0, 15, 6, 0, // Expected A/B state.
BUB_AB_FLOW_RESULT_OK, // Expected A/B result.
"_b"); // Expected A/B suffix.
}