/* * Copyright (c) 2020, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "flash.hpp" #if OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE #include #include #include "common/code_utils.hpp" #include "instance/instance.hpp" namespace ot { const uint32_t ot::Flash::sSwapActive; const uint32_t ot::Flash::sSwapInactive; void Flash::Init(void) { RecordHeader record; otPlatFlashInit(&GetInstance()); mSwapSize = otPlatFlashGetSwapSize(&GetInstance()); for (mSwapIndex = 0;; mSwapIndex++) { uint32_t swapMarker; if (mSwapIndex >= 2) { Wipe(); ExitNow(); } otPlatFlashRead(&GetInstance(), mSwapIndex, 0, &swapMarker, sizeof(swapMarker)); if (swapMarker == sSwapActive) { break; } } for (mSwapUsed = kSwapMarkerSize; mSwapUsed <= mSwapSize - sizeof(record); mSwapUsed += record.GetSize()) { otPlatFlashRead(&GetInstance(), mSwapIndex, mSwapUsed, &record, sizeof(record)); if (!record.IsAddBeginSet()) { break; } if (!record.IsAddCompleteSet()) { break; } } SanitizeFreeSpace(); exit: return; } void Flash::SanitizeFreeSpace(void) { uint32_t temp; bool sanitizeNeeded = false; if (mSwapUsed & 3) { ExitNow(sanitizeNeeded = true); } for (uint32_t offset = mSwapUsed; offset < mSwapSize; offset += sizeof(temp)) { otPlatFlashRead(&GetInstance(), mSwapIndex, offset, &temp, sizeof(temp)); if (temp != ~0U) { ExitNow(sanitizeNeeded = true); } } exit: if (sanitizeNeeded) { Swap(); } } Error Flash::Get(uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) const { Error error = kErrorNotFound; uint16_t valueLength = 0; int index = 0; // This must be initialized to 0. See [Note] in Delete(). uint32_t offset; RecordHeader record; for (offset = kSwapMarkerSize; offset < mSwapUsed; offset += record.GetSize()) { otPlatFlashRead(&GetInstance(), mSwapIndex, offset, &record, sizeof(record)); if ((record.GetKey() != aKey) || !record.IsValid()) { continue; } if (record.IsFirst()) { index = 0; } if (index == aIndex) { if (aValue && aValueLength) { uint16_t readLength = *aValueLength; if (readLength > record.GetLength()) { readLength = record.GetLength(); } otPlatFlashRead(&GetInstance(), mSwapIndex, offset + sizeof(record), aValue, readLength); } valueLength = record.GetLength(); error = kErrorNone; } index++; } if (aValueLength) { *aValueLength = valueLength; } return error; } Error Flash::Set(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) { return Add(aKey, true, aValue, aValueLength); } Error Flash::Add(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) { bool first = (Get(aKey, 0, nullptr, nullptr) == kErrorNotFound); return Add(aKey, first, aValue, aValueLength); } Error Flash::Add(uint16_t aKey, bool aFirst, const uint8_t *aValue, uint16_t aValueLength) { Error error = kErrorNone; Record record; record.Init(aKey, aFirst); record.SetData(aValue, aValueLength); OT_ASSERT((mSwapSize - record.GetSize()) >= kSwapMarkerSize); if ((mSwapSize - record.GetSize()) < mSwapUsed) { Swap(); VerifyOrExit((mSwapSize - record.GetSize()) >= mSwapUsed, error = kErrorNoBufs); } otPlatFlashWrite(&GetInstance(), mSwapIndex, mSwapUsed, &record, record.GetSize()); record.SetAddCompleteFlag(); otPlatFlashWrite(&GetInstance(), mSwapIndex, mSwapUsed, &record, sizeof(RecordHeader)); mSwapUsed += record.GetSize(); exit: return error; } bool Flash::DoesValidRecordExist(uint32_t aOffset, uint16_t aKey) const { RecordHeader record; bool rval = false; for (; aOffset < mSwapUsed; aOffset += record.GetSize()) { otPlatFlashRead(&GetInstance(), mSwapIndex, aOffset, &record, sizeof(record)); if (record.IsValid() && record.IsFirst() && (record.GetKey() == aKey)) { ExitNow(rval = true); } } exit: return rval; } void Flash::Swap(void) { uint8_t dstIndex = !mSwapIndex; uint32_t dstOffset = kSwapMarkerSize; Record record; otPlatFlashErase(&GetInstance(), dstIndex); for (uint32_t srcOffset = kSwapMarkerSize; srcOffset < mSwapUsed; srcOffset += record.GetSize()) { otPlatFlashRead(&GetInstance(), mSwapIndex, srcOffset, &record, sizeof(RecordHeader)); VerifyOrExit(record.IsAddBeginSet()); if (!record.IsValid() || DoesValidRecordExist(srcOffset + record.GetSize(), record.GetKey())) { continue; } otPlatFlashRead(&GetInstance(), mSwapIndex, srcOffset, &record, record.GetSize()); otPlatFlashWrite(&GetInstance(), dstIndex, dstOffset, &record, record.GetSize()); dstOffset += record.GetSize(); } exit: otPlatFlashWrite(&GetInstance(), dstIndex, 0, &sSwapActive, sizeof(sSwapActive)); otPlatFlashWrite(&GetInstance(), mSwapIndex, 0, &sSwapInactive, sizeof(sSwapInactive)); mSwapIndex = dstIndex; mSwapUsed = dstOffset; } Error Flash::Delete(uint16_t aKey, int aIndex) { Error error = kErrorNotFound; int index = 0; // This must be initialized to 0. See [Note] below. RecordHeader record; for (uint32_t offset = kSwapMarkerSize; offset < mSwapUsed; offset += record.GetSize()) { otPlatFlashRead(&GetInstance(), mSwapIndex, offset, &record, sizeof(record)); if ((record.GetKey() != aKey) || !record.IsValid()) { continue; } if (record.IsFirst()) { index = 0; } if ((aIndex == index) || (aIndex == -1)) { record.SetDeleted(); otPlatFlashWrite(&GetInstance(), mSwapIndex, offset, &record, sizeof(record)); error = kErrorNone; } /* [Note] If the operation gets interrupted here and aIndex is 0, the next record (index == 1) will never get * marked as first. However, this is not actually an issue because all the methods that iterate over the * settings area initialize the index to 0, without expecting any record to be effectively marked as first. */ if ((index == 1) && (aIndex == 0)) { record.SetFirst(); otPlatFlashWrite(&GetInstance(), mSwapIndex, offset, &record, sizeof(record)); } index++; } return error; } void Flash::Wipe(void) { otPlatFlashErase(&GetInstance(), 0); otPlatFlashWrite(&GetInstance(), 0, 0, &sSwapActive, sizeof(sSwapActive)); mSwapIndex = 0; mSwapUsed = sizeof(sSwapActive); } } // namespace ot #endif // OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE