wear_leveling_2byte.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // Copyright 2022 Nick Brassel (@tzarc)
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <numeric>
  4. #include "gtest/gtest.h"
  5. #include "gmock/gmock.h"
  6. #include "backing_mocks.hpp"
  7. class WearLeveling2Byte : public ::testing::Test {
  8. protected:
  9. void SetUp() override {
  10. MockBackingStore::Instance().reset_instance();
  11. wear_leveling_init();
  12. }
  13. };
  14. static std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> verify_data;
  15. static wear_leveling_status_t test_write(const uint32_t address, const void* value, size_t length) {
  16. memcpy(&verify_data[address], value, length);
  17. return wear_leveling_write(address, value, length);
  18. }
  19. /**
  20. * This test verifies that the first write after initialisation occurs after the FNV1a_64 hash location.
  21. */
  22. TEST_F(WearLeveling2Byte, FirstWriteOccursAfterHash) {
  23. auto& inst = MockBackingStore::Instance();
  24. uint8_t test_value = 0x15;
  25. test_write(0x02, &test_value, sizeof(test_value));
  26. EXPECT_EQ(inst.log_begin()->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << "Invalid first write address.";
  27. }
  28. /**
  29. * This test verifies that the first write after initialisation occurs after the FNV1a_64 hash location, after an erase has occurred.
  30. */
  31. TEST_F(WearLeveling2Byte, FirstWriteOccursAfterHash_AfterErase) {
  32. auto& inst = MockBackingStore::Instance();
  33. uint8_t test_value = 0x15;
  34. wear_leveling_erase();
  35. test_write(0x02, &test_value, sizeof(test_value));
  36. EXPECT_EQ((inst.log_begin() + 1)->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << "Invalid first write address.";
  37. }
  38. /**
  39. * This test forces consolidation by writing enough to the write log that it overflows, consolidating the data into the
  40. * base logical area.
  41. */
  42. TEST_F(WearLeveling2Byte, ConsolidationOverflow) {
  43. auto& inst = MockBackingStore::Instance();
  44. // Generate a test block of data which forces OPTIMIZED_64 writes
  45. std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> testvalue;
  46. // Write the data
  47. std::iota(testvalue.begin(), testvalue.end(), 0x20);
  48. EXPECT_EQ(test_write(0, testvalue.data(), testvalue.size()), WEAR_LEVELING_CONSOLIDATED) << "Write returned incorrect status";
  49. uint8_t dummy = 0x40;
  50. EXPECT_EQ(test_write(0x04, &dummy, sizeof(dummy)), WEAR_LEVELING_SUCCESS) << "Write returned incorrect status";
  51. // All writes are at address<64, so each logical byte written will generate 1 write log entry, thus 1 backing store write.
  52. // Expected log:
  53. // [0..11]: optimised64, backing address 0x18, logical address 0x00
  54. // [12]: erase
  55. // [13..20]: consolidated data, backing address 0x00, logical address 0x00
  56. // [21..24]: FNV1a_64 result, backing address 0x10
  57. // [25]: optimised64, backing address 0x18, logical address 0x04
  58. EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), 26);
  59. // Verify the backing store writes for the write log
  60. std::size_t index;
  61. write_log_entry_t e;
  62. for (index = 0; index < 12; ++index) {
  63. auto write_iter = inst.log_begin() + index;
  64. EXPECT_EQ(write_iter->address, WEAR_LEVELING_LOGICAL_SIZE + 8 + (index * BACKING_STORE_WRITE_SIZE)) << "Invalid write log address";
  65. e.raw16[0] = write_iter->value;
  66. EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_OPTIMIZED_64) << "Invalid write log entry type";
  67. }
  68. // Verify the backing store erase
  69. {
  70. index = 12;
  71. auto write_iter = inst.log_begin() + index;
  72. e.raw16[0] = write_iter->value;
  73. EXPECT_TRUE(write_iter->erased) << "Backing store erase did not occur as required";
  74. }
  75. // Verify the backing store writes for consolidation
  76. for (index = 13; index < 21; ++index) {
  77. auto write_iter = inst.log_begin() + index;
  78. EXPECT_EQ(write_iter->address, (index - 13) * BACKING_STORE_WRITE_SIZE) << "Invalid write log entry address";
  79. }
  80. // Verify the FNV1a_64 write
  81. {
  82. EXPECT_EQ((inst.log_begin() + 21)->address, WEAR_LEVELING_LOGICAL_SIZE) << "Invalid write log address";
  83. e.raw16[0] = (inst.log_begin() + 21)->value;
  84. e.raw16[1] = (inst.log_begin() + 22)->value;
  85. e.raw16[2] = (inst.log_begin() + 23)->value;
  86. e.raw16[3] = (inst.log_begin() + 24)->value;
  87. EXPECT_EQ(e.raw64, fnv_64a_buf(testvalue.data(), testvalue.size(), FNV1A_64_INIT)) << "Invalid checksum"; // Note that checksum is based on testvalue, as we overwrote one byte and need to consult the consolidated data, not the current
  88. }
  89. // Verify the final write
  90. EXPECT_EQ((inst.log_begin() + 25)->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << "Invalid write log address";
  91. // Verify the data is what we expected
  92. std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> readback;
  93. EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data";
  94. EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback did not match";
  95. // Re-init and re-read, verifying the reload capability
  96. EXPECT_NE(wear_leveling_init(), WEAR_LEVELING_FAILED) << "Re-initialisation failed";
  97. EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data";
  98. EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback did not match";
  99. }
  100. /**
  101. * This test verifies multibyte readback gets canceled with an out-of-bounds address.
  102. */
  103. TEST_F(WearLeveling2Byte, PlaybackReadbackMultibyte_OOB) {
  104. auto& inst = MockBackingStore::Instance();
  105. auto logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t));
  106. // Invalid FNV1a_64 hash
  107. (logstart + 0)->set(0);
  108. (logstart + 1)->set(0);
  109. (logstart + 2)->set(0);
  110. (logstart + 3)->set(0);
  111. // Set up a 2-byte logical write of [0x11,0x12] at logical offset 0x01
  112. auto entry0 = LOG_ENTRY_MAKE_MULTIBYTE(0x01, 2);
  113. entry0.raw8[3] = 0x11;
  114. entry0.raw8[4] = 0x12;
  115. (logstart + 4)->set(~entry0.raw16[0]);
  116. (logstart + 5)->set(~entry0.raw16[1]);
  117. (logstart + 6)->set(~entry0.raw16[2]);
  118. // Set up a 2-byte logical write of [0x13,0x14] at logical offset 0x1000 (out of bounds)
  119. auto entry1 = LOG_ENTRY_MAKE_MULTIBYTE(0x1000, 2);
  120. entry1.raw8[3] = 0x13;
  121. entry1.raw8[4] = 0x14;
  122. (logstart + 7)->set(~entry1.raw16[0]);
  123. (logstart + 8)->set(~entry1.raw16[1]);
  124. (logstart + 9)->set(~entry1.raw16[2]);
  125. // Set up a 2-byte logical write of [0x15,0x16] at logical offset 0x01
  126. auto entry2 = LOG_ENTRY_MAKE_MULTIBYTE(0x01, 2);
  127. entry2.raw8[3] = 0x15;
  128. entry2.raw8[4] = 0x16;
  129. (logstart + 10)->set(~entry2.raw16[0]);
  130. (logstart + 11)->set(~entry2.raw16[1]);
  131. (logstart + 12)->set(~entry2.raw16[2]);
  132. EXPECT_EQ(inst.erasure_count(), 0) << "Invalid initial erase count";
  133. EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_CONSOLIDATED) << "Readback should have failed and triggered consolidation";
  134. EXPECT_EQ(inst.erasure_count(), 1) << "Invalid final erase count";
  135. uint8_t buf[2];
  136. wear_leveling_read(0x01, buf, sizeof(buf));
  137. EXPECT_EQ(buf[0], 0x11) << "Readback should have maintained the previous pre-failure value from the write log";
  138. EXPECT_EQ(buf[1], 0x12) << "Readback should have maintained the previous pre-failure value from the write log";
  139. }
  140. /**
  141. * This test verifies optimized 64 readback gets canceled with an out-of-bounds address.
  142. */
  143. TEST_F(WearLeveling2Byte, PlaybackReadbackOptimized64_OOB) {
  144. auto& inst = MockBackingStore::Instance();
  145. auto logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t));
  146. // Invalid FNV1a_64 hash
  147. (logstart + 0)->set(0);
  148. (logstart + 1)->set(0);
  149. (logstart + 2)->set(0);
  150. (logstart + 3)->set(0);
  151. // Set up a 1-byte logical write of 0x11 at logical offset 0x01
  152. auto entry0 = LOG_ENTRY_MAKE_OPTIMIZED_64(0x01, 0x11);
  153. (logstart + 4)->set(~entry0.raw16[0]);
  154. // Set up a 1-byte logical write of 0x11 at logical offset 0x30 (out of bounds)
  155. auto entry1 = LOG_ENTRY_MAKE_OPTIMIZED_64(0x30, 0x11);
  156. (logstart + 5)->set(~entry1.raw16[0]);
  157. // Set up a 1-byte logical write of 0x12 at logical offset 0x01
  158. auto entry2 = LOG_ENTRY_MAKE_OPTIMIZED_64(0x01, 0x12);
  159. (logstart + 6)->set(~entry2.raw16[0]);
  160. EXPECT_EQ(inst.erasure_count(), 0) << "Invalid initial erase count";
  161. EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_CONSOLIDATED) << "Readback should have failed and triggered consolidation";
  162. EXPECT_EQ(inst.erasure_count(), 1) << "Invalid final erase count";
  163. uint8_t tmp;
  164. wear_leveling_read(0x01, &tmp, sizeof(tmp));
  165. EXPECT_EQ(tmp, 0x11) << "Readback should have maintained the previous pre-failure value from the write log";
  166. }
  167. /**
  168. * This test verifies word 0/1 readback gets canceled with an out-of-bounds address.
  169. */
  170. TEST_F(WearLeveling2Byte, PlaybackReadbackWord01_OOB) {
  171. auto& inst = MockBackingStore::Instance();
  172. auto logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t));
  173. // Invalid FNV1a_64 hash
  174. (logstart + 0)->set(0);
  175. (logstart + 1)->set(0);
  176. (logstart + 2)->set(0);
  177. (logstart + 3)->set(0);
  178. // Set up a 1-byte logical write of 1 at logical offset 0x02
  179. auto entry0 = LOG_ENTRY_MAKE_WORD_01(0x02, 1);
  180. (logstart + 4)->set(~entry0.raw16[0]);
  181. // Set up a 1-byte logical write of 1 at logical offset 0x1000 (out of bounds)
  182. auto entry1 = LOG_ENTRY_MAKE_WORD_01(0x1000, 1);
  183. (logstart + 5)->set(~entry1.raw16[0]);
  184. // Set up a 1-byte logical write of 0 at logical offset 0x02
  185. auto entry2 = LOG_ENTRY_MAKE_WORD_01(0x02, 0);
  186. (logstart + 6)->set(~entry2.raw16[0]);
  187. EXPECT_EQ(inst.erasure_count(), 0) << "Invalid initial erase count";
  188. EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_CONSOLIDATED) << "Readback should have failed and triggered consolidation";
  189. EXPECT_EQ(inst.erasure_count(), 1) << "Invalid final erase count";
  190. uint8_t tmp;
  191. wear_leveling_read(0x02, &tmp, sizeof(tmp));
  192. EXPECT_EQ(tmp, 1) << "Readback should have maintained the previous pre-failure value from the write log";
  193. }