blob: b8a35d1238bfd586620f25f6066e21a54427c027 [file] [log] [blame]
/*
* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lib_vpd.h"
/* Encodes the len into multiple bytes with the following format.
*
* 7 6 ............ 0
* +----+------------------+
* |More| Length | ...
* +----+------------------+
*
* Returns fail if the buffer is not long enough.
*/
vpd_err_t encodeLen(
const int32_t len,
uint8_t *encode_buf,
const int32_t max_len,
int32_t *encoded_len) {
unsigned int shifting;
unsigned int reversed_7bits = 0;
int out_index = 0;
assert(encoded_len);
if (len < 0) return VPD_ERR_INVALID;
shifting = len;
/* reverse the len for every 7-bit. The little endian. */
for (*encoded_len = 0; shifting; (*encoded_len)++) {
reversed_7bits = (reversed_7bits << 7) | (shifting & 0x7f);
shifting >>= 7;
}
if (*encoded_len > max_len) return VPD_ERR_OVERFLOW;
if (!*encoded_len) *encoded_len = 1; /* output at least 1 byte */
/* Output in reverse order, now big endian. */
while (out_index < *encoded_len) {
/* always set MORE flag */
encode_buf[out_index++] = 0x80 | (reversed_7bits & 0x7f);
reversed_7bits >>= 7;
}
encode_buf[out_index - 1] &= 0x7f; /* clear the MORE flag in last byte */
return VPD_OK;
}
/* Encodes the terminator.
*/
vpd_err_t encodeVpdTerminator(
const int max_buffer_len,
uint8_t *output_buf,
int *generated_len) {
assert(generated_len);
if (*generated_len >= max_buffer_len) return VPD_FAIL;
output_buf += *generated_len; /* move cursor to end of string */
*(output_buf++) = VPD_TYPE_TERMINATOR;
(*generated_len)++;
return VPD_OK;
}
/* Encodes a string with padding support. */
vpd_err_t encodeVpdString(
const uint8_t *key,
const uint8_t *value,
const int pad_value_len,
const int max_buffer_len,
uint8_t *output_buf,
int *generated_len) {
int key_len, value_len;
int ret_len;
int pad_len = 0;
vpd_err_t retval;
assert(generated_len);
key_len = strlen((char*)key);
value_len = strlen((char*)value);
output_buf += *generated_len; /* move cursor to end of string */
/* encode type */
if (*generated_len >= max_buffer_len) return VPD_ERR_OVERFLOW;
*(output_buf++) = VPD_TYPE_STRING;
(*generated_len)++;
/* encode key len */
if (VPD_OK != encodeLen(key_len, output_buf,
max_buffer_len - *generated_len, &ret_len))
return VPD_FAIL;
output_buf += ret_len;
*generated_len += ret_len;
/* encode key string */
if (*generated_len + key_len > max_buffer_len) return VPD_ERR_OVERFLOW;
memcpy(output_buf, key, key_len);
output_buf += key_len;
*generated_len += key_len;
/* count padding length */
if (pad_value_len != VPD_AS_LONG_AS) {
if (value_len < pad_value_len) {
pad_len = pad_value_len - value_len;
} else {
value_len = pad_value_len;
}
}
/* encode value len */
retval = encodeLen(value_len + pad_len, output_buf,
max_buffer_len - *generated_len, &ret_len);
if (VPD_OK != retval)
return retval;
output_buf += ret_len;
*generated_len += ret_len;
/* encode value string */
if (*generated_len + value_len > max_buffer_len) return VPD_ERR_OVERFLOW;
memcpy(output_buf, value, value_len);
output_buf += value_len;
*generated_len += value_len;
/* encode padding (if applicable) */
if (*generated_len + pad_len > max_buffer_len) return VPD_ERR_OVERFLOW;
memset(output_buf, 0, pad_len);
output_buf += pad_len;
*generated_len += pad_len;
return VPD_OK;
}