1 /*
2 * Copyright (c) 2020, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "flash.hpp"
30
31 #if OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE
32
33 #include <openthread/platform/flash.h>
34
35 #include "instance/instance.hpp"
36
37 namespace ot {
38
39 const uint32_t ot::Flash::sSwapActive;
40 const uint32_t ot::Flash::sSwapInactive;
41
Init(void)42 void Flash::Init(void)
43 {
44 RecordHeader record;
45
46 otPlatFlashInit(&GetInstance());
47
48 mSwapSize = otPlatFlashGetSwapSize(&GetInstance());
49
50 for (mSwapIndex = 0;; mSwapIndex++)
51 {
52 uint32_t swapMarker;
53
54 if (mSwapIndex >= 2)
55 {
56 Wipe();
57 ExitNow();
58 }
59
60 otPlatFlashRead(&GetInstance(), mSwapIndex, 0, &swapMarker, sizeof(swapMarker));
61
62 if (swapMarker == sSwapActive)
63 {
64 break;
65 }
66 }
67
68 for (mSwapUsed = kSwapMarkerSize; mSwapUsed <= mSwapSize - sizeof(record); mSwapUsed += record.GetSize())
69 {
70 otPlatFlashRead(&GetInstance(), mSwapIndex, mSwapUsed, &record, sizeof(record));
71 if (!record.IsAddBeginSet())
72 {
73 break;
74 }
75
76 if (!record.IsAddCompleteSet())
77 {
78 break;
79 }
80 }
81
82 SanitizeFreeSpace();
83
84 exit:
85 return;
86 }
87
SanitizeFreeSpace(void)88 void Flash::SanitizeFreeSpace(void)
89 {
90 uint32_t temp;
91 bool sanitizeNeeded = false;
92
93 if (mSwapUsed & 3)
94 {
95 ExitNow(sanitizeNeeded = true);
96 }
97
98 for (uint32_t offset = mSwapUsed; offset < mSwapSize; offset += sizeof(temp))
99 {
100 otPlatFlashRead(&GetInstance(), mSwapIndex, offset, &temp, sizeof(temp));
101 if (temp != ~0U)
102 {
103 ExitNow(sanitizeNeeded = true);
104 }
105 }
106
107 exit:
108 if (sanitizeNeeded)
109 {
110 Swap();
111 }
112 }
113
Get(uint16_t aKey,int aIndex,uint8_t * aValue,uint16_t * aValueLength) const114 Error Flash::Get(uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) const
115 {
116 Error error = kErrorNotFound;
117 uint16_t valueLength = 0;
118 int index = 0; // This must be initialized to 0. See [Note] in Delete().
119 uint32_t offset;
120 RecordHeader record;
121
122 for (offset = kSwapMarkerSize; offset < mSwapUsed; offset += record.GetSize())
123 {
124 otPlatFlashRead(&GetInstance(), mSwapIndex, offset, &record, sizeof(record));
125
126 if ((record.GetKey() != aKey) || !record.IsValid())
127 {
128 continue;
129 }
130
131 if (record.IsFirst())
132 {
133 index = 0;
134 }
135
136 if (index == aIndex)
137 {
138 if (aValue && aValueLength)
139 {
140 uint16_t readLength = *aValueLength;
141
142 if (readLength > record.GetLength())
143 {
144 readLength = record.GetLength();
145 }
146
147 otPlatFlashRead(&GetInstance(), mSwapIndex, offset + sizeof(record), aValue, readLength);
148 }
149
150 valueLength = record.GetLength();
151 error = kErrorNone;
152 }
153
154 index++;
155 }
156
157 if (aValueLength)
158 {
159 *aValueLength = valueLength;
160 }
161
162 return error;
163 }
164
Set(uint16_t aKey,const uint8_t * aValue,uint16_t aValueLength)165 Error Flash::Set(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
166 {
167 return Add(aKey, true, aValue, aValueLength);
168 }
169
Add(uint16_t aKey,const uint8_t * aValue,uint16_t aValueLength)170 Error Flash::Add(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
171 {
172 bool first = (Get(aKey, 0, nullptr, nullptr) == kErrorNotFound);
173
174 return Add(aKey, first, aValue, aValueLength);
175 }
176
Add(uint16_t aKey,bool aFirst,const uint8_t * aValue,uint16_t aValueLength)177 Error Flash::Add(uint16_t aKey, bool aFirst, const uint8_t *aValue, uint16_t aValueLength)
178 {
179 Error error = kErrorNone;
180 Record record;
181
182 record.Init(aKey, aFirst);
183 record.SetData(aValue, aValueLength);
184
185 OT_ASSERT((mSwapSize - record.GetSize()) >= kSwapMarkerSize);
186
187 if ((mSwapSize - record.GetSize()) < mSwapUsed)
188 {
189 Swap();
190 VerifyOrExit((mSwapSize - record.GetSize()) >= mSwapUsed, error = kErrorNoBufs);
191 }
192
193 otPlatFlashWrite(&GetInstance(), mSwapIndex, mSwapUsed, &record, record.GetSize());
194
195 record.SetAddCompleteFlag();
196 otPlatFlashWrite(&GetInstance(), mSwapIndex, mSwapUsed, &record, sizeof(RecordHeader));
197
198 mSwapUsed += record.GetSize();
199
200 exit:
201 return error;
202 }
203
DoesValidRecordExist(uint32_t aOffset,uint16_t aKey) const204 bool Flash::DoesValidRecordExist(uint32_t aOffset, uint16_t aKey) const
205 {
206 RecordHeader record;
207 bool rval = false;
208
209 for (; aOffset < mSwapUsed; aOffset += record.GetSize())
210 {
211 otPlatFlashRead(&GetInstance(), mSwapIndex, aOffset, &record, sizeof(record));
212
213 if (record.IsValid() && record.IsFirst() && (record.GetKey() == aKey))
214 {
215 ExitNow(rval = true);
216 }
217 }
218
219 exit:
220 return rval;
221 }
222
Swap(void)223 void Flash::Swap(void)
224 {
225 uint8_t dstIndex = !mSwapIndex;
226 uint32_t dstOffset = kSwapMarkerSize;
227 Record record;
228
229 otPlatFlashErase(&GetInstance(), dstIndex);
230
231 for (uint32_t srcOffset = kSwapMarkerSize; srcOffset < mSwapUsed; srcOffset += record.GetSize())
232 {
233 otPlatFlashRead(&GetInstance(), mSwapIndex, srcOffset, &record, sizeof(RecordHeader));
234
235 VerifyOrExit(record.IsAddBeginSet());
236
237 if (!record.IsValid() || DoesValidRecordExist(srcOffset + record.GetSize(), record.GetKey()))
238 {
239 continue;
240 }
241
242 otPlatFlashRead(&GetInstance(), mSwapIndex, srcOffset, &record, record.GetSize());
243 otPlatFlashWrite(&GetInstance(), dstIndex, dstOffset, &record, record.GetSize());
244 dstOffset += record.GetSize();
245 }
246
247 exit:
248 otPlatFlashWrite(&GetInstance(), dstIndex, 0, &sSwapActive, sizeof(sSwapActive));
249 otPlatFlashWrite(&GetInstance(), mSwapIndex, 0, &sSwapInactive, sizeof(sSwapInactive));
250
251 mSwapIndex = dstIndex;
252 mSwapUsed = dstOffset;
253 }
254
Delete(uint16_t aKey,int aIndex)255 Error Flash::Delete(uint16_t aKey, int aIndex)
256 {
257 Error error = kErrorNotFound;
258 int index = 0; // This must be initialized to 0. See [Note] below.
259 RecordHeader record;
260
261 for (uint32_t offset = kSwapMarkerSize; offset < mSwapUsed; offset += record.GetSize())
262 {
263 otPlatFlashRead(&GetInstance(), mSwapIndex, offset, &record, sizeof(record));
264
265 if ((record.GetKey() != aKey) || !record.IsValid())
266 {
267 continue;
268 }
269
270 if (record.IsFirst())
271 {
272 index = 0;
273 }
274
275 if ((aIndex == index) || (aIndex == -1))
276 {
277 record.SetDeleted();
278 otPlatFlashWrite(&GetInstance(), mSwapIndex, offset, &record, sizeof(record));
279 error = kErrorNone;
280 }
281
282 /* [Note] If the operation gets interrupted here and aIndex is 0, the next record (index == 1) will never get
283 * marked as first. However, this is not actually an issue because all the methods that iterate over the
284 * settings area initialize the index to 0, without expecting any record to be effectively marked as first. */
285
286 if ((index == 1) && (aIndex == 0))
287 {
288 record.SetFirst();
289 otPlatFlashWrite(&GetInstance(), mSwapIndex, offset, &record, sizeof(record));
290 }
291
292 index++;
293 }
294
295 return error;
296 }
297
Wipe(void)298 void Flash::Wipe(void)
299 {
300 otPlatFlashErase(&GetInstance(), 0);
301 otPlatFlashWrite(&GetInstance(), 0, 0, &sSwapActive, sizeof(sSwapActive));
302
303 mSwapIndex = 0;
304 mSwapUsed = sizeof(sSwapActive);
305 }
306
307 } // namespace ot
308
309 #endif // OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE
310