blob: 78dbca266b4d2277e449ee2bf1c65374aa02594c [file] [log] [blame]
/* Copyright 2020 The Fuchsia Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "common.h"
#include <factory_boot_kvs.h>
#include <cbor/cbor.h>
#include <cn-cbor/cn-cbor.h>
static cn_cbor* s_cbor_head = NULL;
static uint8_t s_buf[FACTORY_BOOT_KVS_SIZE] __attribute__ ((aligned(16))) = {0};
FbKvsResult FbKvsInit(FbKvsOps* op)
{
cn_cbor_errback err;
if ((op == NULL) || (op->read == NULL)) {
FB_KVS_ERROR("INVALID args.\n");
return kFbKvsResultInvalidArg;
}
if (s_cbor_head != NULL) {
FB_KVS_ERROR("factory boot is already initialized\n");
return kFbKvsResultOk;
}
FbKvsResult ret = op->read(s_buf, FACTORY_BOOT_KVS_SIZE);
if (ret != kFbKvsResultOk) {
FB_KVS_ERROR("failed to read data: %d\n", ret);
return kFbKvsResultInvalid;
}
s_cbor_head = cn_cbor_decode(s_buf, FACTORY_BOOT_KVS_SIZE, &err);
if (s_cbor_head == NULL) {
/* check if the call failed because there is a padding at the end of data.
* Re-run decoding in this case
*/
if ((err.err == CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED) && (err.pos != 0)) {
s_cbor_head = cn_cbor_decode(s_buf, err.pos, &err);
}
if (s_cbor_head == NULL) {
FB_KVS_ERROR("failed to parse data: position %zu, error %d\n", err.pos, err.err);
return kFbKvsResultInvalidData;
}
}
return kFbKvsResultOk;
}
void FbKvsDeinit(void)
{
if (s_cbor_head == NULL) {
cn_cbor_free(s_cbor_head);
s_cbor_head = NULL;
}
}
FbKvsResult FbKvsGetNumberOfPairs(size_t* size)
{
cn_cbor* cp = NULL;
size_t num = 0;
if (s_cbor_head == NULL) {
return kFbKvsResultErrorNotInitialized;
}
if (size == NULL) {
return kFbKvsResultInvalidArg;
}
*size = 0;
/* counts keys and values together. should be even number */
for (cp = s_cbor_head->first_child; cp; cp = cp->next) {
num++;
}
/* fail if there are odd number of items */
if (num & 1) {
return kFbKvsResultInvalid;
}
*size = num/2;
return kFbKvsResultOk;
}
FbKvsResult FbKvsGetKeyByIndex(uint32_t index, char* key, size_t size)
{
if (s_cbor_head == NULL) {
return kFbKvsResultErrorNotInitialized;
}
if ((key == NULL) || (size == 0)) {
return kFbKvsResultInvalidArg;
}
/* get a key info */
cn_cbor* k = cn_cbor_index(s_cbor_head, index*2);
if (k == NULL) {
return kFbKvsResultNotFound;
}
if (k->type != CN_CBOR_TEXT) {
return kFbKvsResultInvalidData;
}
if ((size_t)(k->length + 1) > size) {
return kFbKvsResultBufTooSmall;
}
memcpy(key, k->v.str, k->length);
key[k->length] = '\0';
return kFbKvsResultOk;
}
FbKvsResult FbKvsGetValueSize(const char* key, size_t* size)
{
if (s_cbor_head == NULL) {
return kFbKvsResultErrorNotInitialized;
}
if ((key == NULL) || (size == NULL)) {
return kFbKvsResultInvalidArg;
}
cn_cbor* cb = cn_cbor_mapget_string(s_cbor_head, key);
if (cb == NULL) {
return kFbKvsResultNotFound;
}
switch (cb->type) {
case CN_CBOR_UINT:
*size = sizeof(uint64_t);
break;
case CN_CBOR_INT:
*size = sizeof(int64_t);
break;
case CN_CBOR_BYTES:
*size = cb->length;
break;
case CN_CBOR_TEXT:
*size = cb->length + 1;
break;
default:
return kFbKvsResultInvalidData;
}
return kFbKvsResultOk;
}
FbKvsResult FbKvsGetValueType(const char* key, FbKvsValueType* type)
{
if (s_cbor_head == NULL) {
return kFbKvsResultErrorNotInitialized;
}
if ((key == NULL) || (type == NULL)) {
return kFbKvsResultInvalidArg;
}
cn_cbor* cb = cn_cbor_mapget_string(s_cbor_head, key);
if (cb == NULL) {
return kFbKvsResultNotFound;
}
switch (cb->type) {
case CN_CBOR_UINT:
*type = kFbKvsTypeULong;
break;
case CN_CBOR_INT:
*type = kFbKvsTypeLong;
break;
case CN_CBOR_BYTES:
*type = kFbKvsTypeData;
break;
case CN_CBOR_TEXT:
*type = kFbKvsTypeString;
break;
default:
return kFbKvsResultInvalid;
}
return kFbKvsResultOk;
}
FbKvsResult FbKvsGetString(const char* key, char* value, size_t* size)
{
if (s_cbor_head == NULL) {
return kFbKvsResultErrorNotInitialized;
}
if ((key == NULL) || (value == NULL) || (size == NULL) || (*size == 0)) {
return kFbKvsResultInvalidArg;
}
cn_cbor* cb = cn_cbor_mapget_string(s_cbor_head, key);
if (cb == NULL) {
return kFbKvsResultNotFound;
}
if (cb->type != CN_CBOR_TEXT) {
return kFbKvsResultTypeMismatch;
}
if ((size_t)(cb->length + 1) > *size) {
return kFbKvsResultBufTooSmall;
}
memcpy(value, cb->v.str, cb->length);
value[cb->length] = '\0';
*size = cb->length + 1;
return kFbKvsResultOk;
}
FbKvsResult FbKvsGetLong(const char* key, int64_t* value)
{
if (s_cbor_head == NULL) {
return kFbKvsResultErrorNotInitialized;
}
if ((key == NULL) || (value == NULL)) {
return kFbKvsResultInvalidArg;
}
cn_cbor* cb = cn_cbor_mapget_string(s_cbor_head, key);
if (cb == NULL) {
return kFbKvsResultNotFound;
}
switch (cb->type) {
case CN_CBOR_INT:
*value = cb->v.sint;
break;
default:
return kFbKvsResultTypeMismatch;
}
return kFbKvsResultOk;
}
FbKvsResult FbKvsGetULong(const char* key, uint64_t* value)
{
if (s_cbor_head == NULL) {
return kFbKvsResultErrorNotInitialized;
}
if ((key == NULL) || (value == NULL)) {
return kFbKvsResultInvalidArg;
}
cn_cbor* cb = cn_cbor_mapget_string(s_cbor_head, key);
if (cb == NULL) {
return kFbKvsResultNotFound;
}
switch (cb->type) {
case CN_CBOR_UINT:
*value = cb->v.uint;
break;
default:
return kFbKvsResultTypeMismatch;
}
return kFbKvsResultOk;
}
FbKvsResult FbKvsGetData(const char* key, uint8_t* value, size_t* size)
{
if (s_cbor_head == NULL) {
return kFbKvsResultErrorNotInitialized;
}
if ((key == NULL) || (value == NULL) || (size == NULL) || (*size == 0)) {
return kFbKvsResultInvalidArg;
}
cn_cbor* cb = cn_cbor_mapget_string(s_cbor_head, key);
if (cb == NULL) {
return kFbKvsResultNotFound;
}
if (cb->type != CN_CBOR_BYTES) {
return kFbKvsResultTypeMismatch;
}
if ((size_t)cb->length > *size) {
return kFbKvsResultBufTooSmall;
}
memcpy(value, cb->v.bytes, cb->length);
*size = cb->length;
return kFbKvsResultOk;
}