1 #include "pattern.hpp"
2
3 #include "erase.hpp"
4
5 #include <unistd.h>
6
7 #include <phosphor-logging/lg2.hpp>
8 #include <stdplus/fd/create.hpp>
9 #include <stdplus/fd/managed.hpp>
10 #include <xyz/openbmc_project/Common/error.hpp>
11
12 #include <algorithm>
13 #include <array>
14 #include <chrono>
15 #include <iostream>
16 #include <random>
17 #include <span>
18 #include <string>
19 #include <thread>
20
21 namespace estoraged
22 {
23
24 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
25 using stdplus::fd::Fd;
26
writePattern(const uint64_t driveSize,Fd & fd)27 void Pattern::writePattern(const uint64_t driveSize, Fd& fd)
28 {
29 // static seed defines a fixed prng sequence so it can be verified later,
30 // and validated for entropy
31 uint64_t currentIndex = 0;
32
33 // random number generator seeded with a constant value will
34 // generate a predictable sequence of values NOLINTNEXTLINE
35 std::minstd_rand0 generator(seed);
36 std::array<std::byte, blockSize> randArr{};
37
38 while (currentIndex < driveSize)
39 {
40 // generate a 4k block of prng
41 std::array<uint32_t, blockSizeUsing32>* randArrFill =
42 reinterpret_cast<std::array<uint32_t, blockSizeUsing32>*>(&randArr);
43 for (uint32_t i = 0; i < blockSizeUsing32; i++)
44 {
45 (*randArrFill)[i] = generator();
46 }
47 // if we can write all 4k bytes do that, else write the remainder
48 size_t writeSize = currentIndex + blockSize < driveSize
49 ? blockSize
50 : driveSize - currentIndex;
51 size_t written = 0;
52 size_t retry = 0;
53 while (written < writeSize)
54 {
55 written += fd.write(std::span{randArr}.subspan(written,
56 writeSize - written))
57 .size();
58 if (written == writeSize)
59 {
60 break;
61 }
62 if (written > writeSize)
63 {
64 throw InternalFailure();
65 }
66 retry++;
67 if (retry > maxRetry)
68 {
69 lg2::error("Unable to do full write", "REDFISH_MESSAGE_ID",
70 std::string("eStorageD.1.0.EraseFailure"));
71 throw InternalFailure();
72 }
73 std::this_thread::sleep_for(delay);
74 }
75 currentIndex = currentIndex + writeSize;
76 }
77 }
78
verifyPattern(const uint64_t driveSize,Fd & fd)79 void Pattern::verifyPattern(const uint64_t driveSize, Fd& fd)
80 {
81 uint64_t currentIndex = 0;
82 // random number generator seeded with a constant value will
83 // generate a predictable sequence of values NOLINTNEXTLINE
84 std::minstd_rand0 generator(seed);
85 std::array<std::byte, blockSize> randArr{};
86 std::array<std::byte, blockSize> readArr{};
87
88 while (currentIndex < driveSize)
89 {
90 size_t readSize = currentIndex + blockSize < driveSize
91 ? blockSize
92 : driveSize - currentIndex;
93 try
94 {
95 std::array<uint32_t, blockSizeUsing32>* randArrFill =
96 reinterpret_cast<std::array<uint32_t, blockSizeUsing32>*>(
97 &randArr);
98 for (uint32_t i = 0; i < blockSizeUsing32; i++)
99 {
100 (*randArrFill)[i] = generator();
101 }
102 size_t read = 0;
103 size_t retry = 0;
104 while (read < readSize)
105 {
106 read +=
107 fd.read(std::span{readArr}.subspan(read, readSize - read))
108 .size();
109 if (read == readSize)
110 {
111 break;
112 }
113 if (read > readSize)
114 {
115 throw InternalFailure();
116 }
117 retry++;
118 if (retry > maxRetry)
119 {
120 lg2::error("Unable to do full read", "REDFISH_MESSAGE_ID",
121 std::string("eStorageD.1.0.EraseFailure"));
122 throw InternalFailure();
123 }
124 std::this_thread::sleep_for(delay);
125 }
126 }
127 catch (...)
128 {
129 lg2::error("Estoraged erase pattern unable to read",
130 "REDFISH_MESSAGE_ID",
131 std::string("eStorageD.1.0.EraseFailure"));
132 throw InternalFailure();
133 }
134
135 if (!std::ranges::equal(std::span{randArr}.subspan(0, readSize),
136 std::span{readArr}.subspan(0, readSize)))
137 {
138 lg2::error("Estoraged erase pattern does not match",
139 "REDFISH_MESSAGE_ID",
140 std::string("eStorageD.1.0.EraseFailure"));
141 throw InternalFailure();
142 }
143 currentIndex = currentIndex + readSize;
144 }
145 }
146
147 } // namespace estoraged
148