blob: 88b494d977144881523b41e1a13b586bae864c1e [file] [log] [blame]
/*
*
* Copyright (c) 2021-2022 Project CHIP Authors
* All rights reserved.
*
* 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.
*/
/**
* @file
* Platform-specific key value storage implementation for SILABS
*/
#include "MigrationManager.h"
#include <crypto/CHIPCryptoPAL.h>
#include <platform/CHIPDeviceLayer.h>
#include <platform/KeyValueStoreManager.h>
#include <platform/silabs/SilabsConfig.h>
#include <stdio.h>
#include <string.h>
using namespace ::chip;
using namespace ::chip::Crypto;
using namespace ::chip::DeviceLayer::Internal;
#define CONVERT_KEYMAP_INDEX_TO_NVM3KEY(index) (SilabsConfig::kConfigKey_KvsFirstKeySlot + index)
#define CONVERT_NVM3KEY_TO_KEYMAP_INDEX(nvm3Key) (nvm3Key - SilabsConfig::kConfigKey_KvsFirstKeySlot)
namespace chip {
namespace DeviceLayer {
namespace PersistedStorage {
KeyValueStoreManagerImpl KeyValueStoreManagerImpl::sInstance;
uint16_t mKvsKeyMap[KeyValueStoreManagerImpl::kMaxEntries] = { 0 };
CHIP_ERROR KeyValueStoreManagerImpl::Init(void)
{
CHIP_ERROR err;
err = SilabsConfig::Init();
SuccessOrExit(err);
Silabs::MigrationManager::GetMigrationInstance().applyMigrations();
memset(mKvsKeyMap, 0, sizeof(mKvsKeyMap));
size_t outLen;
err = SilabsConfig::ReadConfigValueBin(SilabsConfig::kConfigKey_KvsStringKeyMap, reinterpret_cast<uint8_t *>(mKvsKeyMap),
sizeof(mKvsKeyMap), outLen);
if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) // Initial boot
{
err = CHIP_NO_ERROR;
}
exit:
return err;
}
bool KeyValueStoreManagerImpl::IsValidKvsNvm3Key(uint32_t nvm3Key) const
{
return ((SilabsConfig::kConfigKey_KvsFirstKeySlot <= nvm3Key) && (nvm3Key <= SilabsConfig::kConfigKey_KvsLastKeySlot));
}
uint16_t KeyValueStoreManagerImpl::hashKvsKeyString(const char * key) const
{
uint8_t hash256[Crypto::kSHA256_Hash_Length] = { 0 };
Crypto::Hash_SHA256(reinterpret_cast<const uint8_t *>(key), strlen(key), hash256);
uint16_t hash16 = 0, i = 0;
while (!hash16 && (i < (Crypto::kSHA256_Hash_Length - 1)))
{
hash16 = (hash256[i] | (hash256[i + 1] << 8));
i++;
}
return hash16;
}
CHIP_ERROR KeyValueStoreManagerImpl::MapKvsKeyToNvm3(const char * key, uint16_t hash, uint32_t & nvm3Key, bool isSlotNeeded) const
{
CHIP_ERROR err;
char * strPrefix = nullptr;
uint8_t firstEmptyKeySlot = kMaxEntries;
for (uint8_t keyIndex = 0; keyIndex < kMaxEntries; keyIndex++)
{
if (mKvsKeyMap[keyIndex] == hash)
{
uint32_t tempNvm3key = CONVERT_KEYMAP_INDEX_TO_NVM3KEY(keyIndex);
VerifyOrDie(IsValidKvsNvm3Key(tempNvm3key) == true);
size_t readCount;
size_t length = strlen(key);
if (strPrefix == nullptr)
{
// Use a calloc to initialize all bits to 0. alloc +1 for a null char
strPrefix = static_cast<char *>(Platform::MemoryCalloc(1, length + 1));
VerifyOrDie(strPrefix != nullptr);
}
// Collision prevention
// Read the data from NVM3 it should be prefixed by the kvsString
// else we will look for another matching hash in the map
SilabsConfig::ReadConfigValueBin(tempNvm3key, reinterpret_cast<uint8_t *>(strPrefix), length, readCount, 0);
if (strcmp(key, strPrefix) == 0)
{
// String matches we have confirmed the hash pointed us the right key data
nvm3Key = tempNvm3key;
Platform::MemoryFree(strPrefix);
return CHIP_NO_ERROR;
}
}
if (isSlotNeeded && (firstEmptyKeySlot == kMaxEntries) && (mKvsKeyMap[keyIndex] == 0))
{
firstEmptyKeySlot = keyIndex;
}
}
Platform::MemoryFree(strPrefix);
if (isSlotNeeded)
{
if (firstEmptyKeySlot != kMaxEntries)
{
nvm3Key = CONVERT_KEYMAP_INDEX_TO_NVM3KEY(firstEmptyKeySlot);
VerifyOrDie(IsValidKvsNvm3Key(nvm3Key) == true);
err = CHIP_NO_ERROR;
}
else
{
err = CHIP_ERROR_PERSISTED_STORAGE_FAILED;
}
}
else
{
err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND;
}
return err;
}
void KeyValueStoreManagerImpl::ForceKeyMapSave()
{
OnScheduledKeyMapSave(nullptr, nullptr);
}
void KeyValueStoreManagerImpl::OnScheduledKeyMapSave(System::Layer * systemLayer, void * appState)
{
SilabsConfig::WriteConfigValueBin(SilabsConfig::kConfigKey_KvsStringKeyMap, reinterpret_cast<const uint8_t *>(mKvsKeyMap),
sizeof(mKvsKeyMap));
}
void KeyValueStoreManagerImpl::ScheduleKeyMapSave(void)
{
/*
During commissioning, the key map will be modified multiples times subsequently.
Commit the key map in nvm once it as stabilized.
*/
SystemLayer().StartTimer(
std::chrono::duration_cast<System::Clock::Timeout>(System::Clock::Seconds32(SILABS_KVS_SAVE_DELAY_SECONDS)),
KeyValueStoreManagerImpl::OnScheduledKeyMapSave, NULL);
}
CHIP_ERROR KeyValueStoreManagerImpl::_Get(const char * key, void * value, size_t value_size, size_t * read_bytes_size,
size_t offset_bytes) const
{
VerifyOrReturnError(key != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
uint32_t nvm3Key;
uint16_t hash = hashKvsKeyString(key);
CHIP_ERROR err = MapKvsKeyToNvm3(key, hash, nvm3Key);
VerifyOrReturnError(err == CHIP_NO_ERROR, err);
size_t outLen;
// The user doesn't need the KeyString prefix, Read data after it
size_t KeyStringLen = strlen(key);
err = SilabsConfig::ReadConfigValueBin(nvm3Key, reinterpret_cast<uint8_t *>(value), value_size, outLen,
(offset_bytes + KeyStringLen));
if (read_bytes_size)
{
*read_bytes_size = outLen;
}
if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND)
{
return CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND;
}
return err;
}
CHIP_ERROR KeyValueStoreManagerImpl::_Put(const char * key, const void * value, size_t value_size)
{
VerifyOrReturnError(key != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
uint32_t nvm3Key;
uint16_t hash = hashKvsKeyString(key);
CHIP_ERROR err = MapKvsKeyToNvm3(key, hash, nvm3Key, /* isSlotNeeded */ true);
VerifyOrReturnError(err == CHIP_NO_ERROR, err);
// add the string Key as prefix to the stored data as a collision prevention mechanism.
size_t keyStringLen = strlen(key);
uint8_t * prefixedData = static_cast<uint8_t *>(Platform::MemoryAlloc(keyStringLen + value_size));
VerifyOrDie(prefixedData != nullptr);
memcpy(prefixedData, key, keyStringLen);
memcpy(prefixedData + keyStringLen, value, value_size);
err = SilabsConfig::WriteConfigValueBin(nvm3Key, prefixedData, keyStringLen + value_size);
if (err == CHIP_NO_ERROR)
{
uint32_t keyIndex = CONVERT_NVM3KEY_TO_KEYMAP_INDEX(nvm3Key);
mKvsKeyMap[keyIndex] = hash;
ScheduleKeyMapSave();
}
Platform::MemoryFree(prefixedData);
return err;
}
CHIP_ERROR KeyValueStoreManagerImpl::_Delete(const char * key)
{
VerifyOrReturnError(key != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
uint32_t nvm3Key;
uint16_t hash = hashKvsKeyString(key);
CHIP_ERROR err = MapKvsKeyToNvm3(key, hash, nvm3Key);
VerifyOrReturnError(err == CHIP_NO_ERROR, err);
err = SilabsConfig::ClearConfigValue(nvm3Key);
if (err == CHIP_NO_ERROR)
{
uint32_t keyIndex = CONVERT_NVM3KEY_TO_KEYMAP_INDEX(nvm3Key);
mKvsKeyMap[keyIndex] = 0;
ScheduleKeyMapSave();
}
return err;
}
void KeyValueStoreManagerImpl::ErasePartition(void)
{
// Iterate over all the Matter Kvs nvm3 records and delete each one...
for (uint32_t nvm3Key = SilabsConfig::kMinConfigKey_MatterKvs; nvm3Key <= SilabsConfig::kMaxConfigKey_MatterKvs; nvm3Key++)
{
SilabsConfig::ClearConfigValue(nvm3Key);
}
memset(mKvsKeyMap, 0, sizeof(mKvsKeyMap));
}
void KeyValueStoreManagerImpl::KvsMapMigration(void)
{
size_t readlen = 0;
constexpr uint8_t oldMaxEntires = 120;
char mKvsStoredKeyString[oldMaxEntires][PersistentStorageDelegate::kKeyLengthMax + 1] = { 0 };
CHIP_ERROR err =
SilabsConfig::ReadConfigValueBin(SilabsConfig::kConfigKey_KvsStringKeyMap, reinterpret_cast<uint8_t *>(mKvsStoredKeyString),
sizeof(mKvsStoredKeyString), readlen);
if (err == CHIP_NO_ERROR)
{
for (uint8_t i = 0; i < oldMaxEntires; i++)
{
if (mKvsStoredKeyString[i][0] != 0)
{
size_t dataLen = 0;
uint32_t nvm3Key = CONVERT_KEYMAP_INDEX_TO_NVM3KEY(i);
if (SilabsConfig::ConfigValueExists(nvm3Key, dataLen))
{
// Read old data and prefix it with the string Key for the collision prevention mechanism.
size_t keyStringLen = strlen(mKvsStoredKeyString[i]);
uint8_t * prefixedData = static_cast<uint8_t *>(Platform::MemoryAlloc(keyStringLen + dataLen));
VerifyOrDie(prefixedData != nullptr);
memcpy(prefixedData, mKvsStoredKeyString[i], keyStringLen);
SilabsConfig::ReadConfigValueBin(nvm3Key, prefixedData + keyStringLen, dataLen, readlen);
SilabsConfig::WriteConfigValueBin(nvm3Key, prefixedData, keyStringLen + dataLen);
mKvsKeyMap[i] = KeyValueStoreMgrImpl().hashKvsKeyString(mKvsStoredKeyString[i]);
Platform::MemoryFree(prefixedData);
}
}
}
ForceKeyMapSave();
}
else if (err != CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND)
{
// Error reading the old String Keymap. Cannot not resolve stored data for migration.
ChipLogError(DeviceLayer, "Migration failed ! Kvs Key map could not be recovered %" CHIP_ERROR_FORMAT, err.Format());
// start with a fresh kvs section.
KeyValueStoreMgrImpl().ErasePartition();
}
}
} // namespace PersistedStorage
} // namespace DeviceLayer
} // namespace chip