blob: 6c86af6f23f9279051046022f2be4ab14f612f78 [file] [log] [blame]
/*
** Copyright 2012-2013 Intel Corporation
**
** 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.
*/
#include <stdbool.h> /* defines bool type */
#include <stddef.h> /* defines size_t */
#include <stdint.h> /* defines integer types with specified widths */
#include <stdio.h> /* defines FILE */
#include <string.h> /* defines memcpy and memset */
#include "libtbd.h" /* our own header file */
/*!
* \brief Debug messages.
*/
#ifdef __ANDROID__
#define LOG_TAG "libtbd"
#include <utils/Log.h>
#define MSG_LOG(...) LOGD(__VA_ARGS__)
#define MSG_ERR(...) LOGE(__VA_ARGS__)
#else
#include <stdio.h>
#define MSG_LOG(...) fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n");
#define MSG_ERR(...) fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n");
#endif
/*
* Checks the validity of the pointer
* param[in] a_ptr Pointer to be examined
* return True if pointer ok
*/
bool is_valid_pointer(void* a_ptr)
{
if ((!a_ptr) || ((unsigned long)(a_ptr) % sizeof(uint32_t))) {
return false;
} else {
return true;
}
}
/*
* Calculates checksum for a data block.
* param[in] a_data_ptr Data from where to calculate the checksum
* param[in] a_data_size Size of the data
* return The checksum
*/
uint32_t get_checksum(void *a_data_ptr, size_t a_data_size)
{
uint32_t *ptr32 = a_data_ptr;
int size32 = a_data_size / sizeof(uint32_t);
/* Simple checksum algorithm: summing up the data content
* as 32-bit numbers */
uint32_t checksum32 = 0;
if (size32) {
if (size32 & 0x01) {
checksum32 += *ptr32++;
size32 -= 1;
}
if (size32 & 0x02) {
checksum32 += *ptr32++;
checksum32 += *ptr32++;
size32 -= 2;
}
for (; size32 > 0; size32-=4) {
checksum32 += *ptr32++;
checksum32 += *ptr32++;
checksum32 += *ptr32++;
checksum32 += *ptr32++;
}
}
return checksum32;
}
/*
* Common subroutine to validate Tagged Binary Data container, without
* paying attention to checksum or data tagging. This function assumes
* that the data resides in "legal" memory area as there is no size
* given together with input pointer.
* param[in] a_data_ptr Pointer to container
* return Return code indicating possible errors
*/
tbd_error_t validate_anysize(void *a_data_ptr)
{
uint8_t *byte_ptr, *eof_ptr;
tbd_record_header_t *record_ptr;
uint32_t record_size;
/* Container should begin with a header */
tbd_header_t *header_ptr = a_data_ptr;
/* Check against illegal pointers */
if (!is_valid_pointer(header_ptr)) {
MSG_ERR("LIBTBD ERROR: Cannot access data!");
return tbd_err_data;
}
/* Check that the indicated data size makes sense,
* and is not too much or too little */
if (header_ptr->size % sizeof(uint32_t)) {
MSG_ERR("LIBTBD ERROR: Size in header should be multiple of 4 bytes!");
return tbd_err_data;
}
if (header_ptr->size < sizeof(tbd_header_t)) {
MSG_ERR("LIBTBD ERROR: Invalid data header!");
return tbd_err_data;
}
/* First record is just after header, a byte pointer is needed
* to do math with sizes and pointers */
byte_ptr = (uint8_t *)(header_ptr + 1);
eof_ptr = (uint8_t *)(a_data_ptr) + header_ptr->size;
/* Loop until there are no more records to go */
while (byte_ptr < eof_ptr) {
/* At least one more record is expected */
/* Record header must be within the given data size */
if (byte_ptr + sizeof(tbd_record_header_t) > eof_ptr) {
MSG_ERR("LIBTBD ERROR: Invalid data header!");
return tbd_err_data;
}
record_ptr = (tbd_record_header_t *)(byte_ptr);
record_size = record_ptr->size;
/* Check that the indicated record size makes sense,
* and is not too much or too little */
if (record_size % sizeof(uint32_t)) {
MSG_ERR("LIBTBD ERROR: Size in record should be multiple of 4 bytes!");
return tbd_err_data;
}
if (record_size < sizeof(tbd_record_header_t)) {
MSG_ERR("LIBTBD ERROR: Invalid record header!");
return tbd_err_data;
}
if (byte_ptr + record_size > eof_ptr) {
MSG_ERR("LIBTBD ERROR: Invalid record header!");
return tbd_err_data;
}
/* This record ok, continue the while loop... */
byte_ptr += record_size;
}
/* Seems that we have a valid data with no more headers */
return tbd_err_none;
}
/*
* Common subroutine to validate Tagged Binary Data, without paying
* attention to checksum or data tagging. Also, this function does
* check that the data fits in the given buffer size.
* param[in] a_data_ptr Pointer to data buffer
* param[in] a_data_size Size of the data buffer
* return Return code indicating possible errors
*/
tbd_error_t validate(void *a_data_ptr, size_t a_data_size)
{
/* Container should begin with a header */
tbd_header_t *header_ptr = a_data_ptr;
/* Check against illegal pointers */
if (!is_valid_pointer(header_ptr)) {
MSG_ERR("LIBTBD ERROR: Cannot access data!");
return tbd_err_data;
}
/* Check that the TBD header fits into given data */
if (sizeof(tbd_header_t) > a_data_size) {
MSG_ERR("TBD ERROR: #1 Too small data buffer given!");
return tbd_err_data;
}
/* Check that the indicated data fits in the buffer */
if (header_ptr->size > a_data_size) {
MSG_ERR("TBD ERROR: #2 Too small data buffer given!");
return tbd_err_data;
}
/* Check the the content is ok */
return validate_anysize(a_data_ptr);
}
/*
* Creates a new, empty Tagged Binary Data container with the tag
* that was given. Also updates the checksum and size accordingly.
* Note that the buffer size must be large enough for the header
* to fit in, the exact amount being 24 bytes (for tbd_header_t).
* param[in] a_data_ptr Pointer to modifiable container buffer
* param[in] a_data_size Size of the container buffer
* param[in] a_tag Tag the container shall have
* param[out] a_new_size Updated container size
* return Return code indicating possible errors
*/
tbd_error_t tbd_create(void *a_data_ptr, size_t a_data_size
, tbd_tag_t a_tag, size_t *a_new_size)
{
tbd_header_t *header_ptr;
/* Check that the TBD header fits into given data */
if (sizeof(tbd_header_t) > a_data_size) {
MSG_ERR("LIBTBD ERROR: Not enough data given!");
return tbd_err_argument;
}
/* Nullify everything */
memset(a_data_ptr, 0, sizeof(tbd_header_t));
/* The header is what we need */
header_ptr = a_data_ptr;
header_ptr->tag = a_tag;
header_ptr->size = sizeof(tbd_header_t);
header_ptr->version = IA_TBD_VERSION;
header_ptr->revision = IA_TBD_REVISION;
header_ptr->config_bits = 0;
header_ptr->checksum = get_checksum(header_ptr, sizeof(tbd_header_t));
*a_new_size = sizeof(tbd_header_t);
return tbd_err_none;
}
/*
* Performs number of checks to given Tagged Binary Data container,
* including the verification of the checksum. The function does not
* care about the tag type of the container.
* param[in] a_data_ptr Pointer to container buffer
* param[in] a_data_size Size of the container buffer
* return Return code indicating possible errors
*/
tbd_error_t tbd_validate_anytag(void *a_data_ptr, size_t a_data_size)
{
tbd_header_t *header_ptr;
/* Check the the content is ok */
int r;
if ((r = validate(a_data_ptr, a_data_size))) {
return r;
}
/* Container should begin with a header */
header_ptr = a_data_ptr;
/* Check that the checksum is correct */
/* When calculating the checksum for the original data, the checksum
* field has been filled with zero value - so after inserting the
* checksum in its place, the new calculated checksum is actually
* two times the original */
if (get_checksum(header_ptr, header_ptr->size) - header_ptr->checksum != header_ptr->checksum) {
MSG_ERR("LIBTBD ERROR: Checksum doesn't match!");
return tbd_err_data;
}
/* Seems that we have valid data */
return tbd_err_none;
}
/*
* Performs number of checks to given Tagged Binary Data container,
* including the verification of the checksum. Also, the data must have
* been tagged properly. The tag is further used to check endianness,
* and if it seems wrong, a specific debug message is printed out.
* param[in] a_data_ptr Pointer to container buffer
* param[in] a_data_size Size of the container buffer
* param[in] a_tag Tag the data must have
* return Return code indicating possible errors
*/
tbd_error_t tbd_validate(void *a_data_ptr, size_t a_data_size
, tbd_tag_t a_tag)
{
tbd_header_t *header_ptr;
/* Check the the content is ok */
int r;
if ((r = validate(a_data_ptr, a_data_size))) {
return r;
}
/* Container should begin with a header */
header_ptr = a_data_ptr;
/* Check that the tag is correct */
if (header_ptr->tag != a_tag) {
/* See if we have wrong endianness or incorrect tag */
uint32_t reverse_tag = ( (((a_tag) >> 24) & 0x000000FF)
| (((a_tag) >> 8) & 0x0000FF00)
| (((a_tag) << 8) & 0x00FF0000)
| (((a_tag) << 24) & 0xFF000000) );
if (reverse_tag == header_ptr->tag) {
MSG_ERR("LIBTBD ERROR: Wrong endianness of data!");
} else {
MSG_ERR("LIBTBD ERROR: Data is not tagged properly!");
}
return tbd_err_data;
}
/* Check that the checksum is correct */
/* When calculating the checksum for the original data, the checksum
* field has been filled with zero value - so after inserting the
* checksum in its place, the new calculated checksum is actually
* two times the original */
if (get_checksum(header_ptr, header_ptr->size) - header_ptr->checksum != header_ptr->checksum) {
MSG_ERR("LIBTBD ERROR: Checksum doesn't match!");
return tbd_err_data;
}
/* Seems that we have valid data */
return tbd_err_none;
}
/*
* Checks if a given kind of record exists in the Tagged Binary Data,
* and if yes, tells the location of such record as well as its size.
* If there are multiple records that match the query, the indicated
* record is the first one.
* param[in] a_data_ptr Pointer to container buffer
* param[in] a_record_class Class the record must have
* param[in] a_record_format Format the record must have
* param[out] a_record_data Record data (or NULL if not found)
* param[out] a_record_size Record size (or 0 if not found)
* return Return code indicating possible errors
*/
tbd_error_t tbd_get_record(void *a_data_ptr
, tbd_class_t a_record_class, tbd_format_t a_record_format
, void **a_record_data, size_t *a_record_size)
{
tbd_header_t *header_ptr;
uint8_t *byte_ptr, *eof_ptr;
/* Check the the content is ok */
int r;
if ((r = validate_anysize(a_data_ptr))) {
return r;
}
/* Container should begin with a header */
header_ptr = a_data_ptr;
/* First record is just after header */
byte_ptr = (uint8_t *)(header_ptr + 1);
eof_ptr = (uint8_t *)(a_data_ptr) + header_ptr->size;
/* Loop until there are no more records to go */
while (byte_ptr < eof_ptr) {
/* At least one more record is expected */
tbd_record_header_t *record_ptr = (tbd_record_header_t *)(byte_ptr);
uint16_t record_class = record_ptr->class_id;
uint8_t record_format = record_ptr->format_id;
uint32_t record_size = record_ptr->size;
if (((a_record_class == tbd_class_any) || (a_record_class == record_class))
&& ((a_record_format == tbd_format_any) || (a_record_format == record_format))) {
/* Match found */
*a_record_data = record_ptr + 1;
*a_record_size = record_size - sizeof(tbd_record_header_t);
return tbd_err_none;
}
/* Match not found yet, continue the while loop... */
byte_ptr += record_size;
}
MSG_LOG("libtbd: Record not found!");
*a_record_data = NULL;
*a_record_size = 0;
return tbd_err_none;
}
/*
* The given record is inserted into the Tagged Binary Data container
* that must exist already. New records are always added to the end,
* regardless if a record with the same class and format field already
* exists in the data. Also updates the checksum and size accordingly.
* Note that the buffer size must be large enough for the inserted
* record to fit in, the exact amount being the size of original
* Tagged Binary Data container plus the size of record data to be
* inserted plus 8 bytes (for tbd_record_header_t).
* param[in] a_data_ptr Pointer to modifiable container buffer
* param[in] a_data_size Size of buffer (surplus included)
* param[in] a_record_class Class the record shall have
* param[in] a_record_format Format the record shall have
* param[in] a_record_data Record data
* param[in] a_record_size Record size
* param[out] a_new_size Updated container size
* return Return code indicating possible errors
*/
tbd_error_t tbd_insert_record(void *a_data_ptr, size_t a_data_size
, tbd_class_t a_record_class, tbd_format_t a_record_format
, void *a_record_data, size_t a_record_size
, size_t *a_new_size)
{
tbd_header_t *header_ptr;
size_t new_size;
tbd_record_header_t *record_ptr;
int r;
/* Check the the content is ok */
if ((r = validate(a_data_ptr, a_data_size))) {
return r;
}
/* Container should begin with a header */
header_ptr = a_data_ptr;
/* Check that the new record fits into given data */
new_size = header_ptr->size + sizeof(tbd_record_header_t) + a_record_size;
if (new_size > a_data_size) {
MSG_ERR("LIBTBD ERROR: #3 Too small data buffer given!");
return tbd_err_argument;
}
/* Check against illegal pointers */
if (!is_valid_pointer(a_record_data)) {
MSG_ERR("LIBTBD ERROR: Cannot access data!");
return tbd_err_data;
}
/* Check that the indicated data size makes sense */
if (a_record_size % sizeof(uint32_t)) {
MSG_ERR("LIBTBD ERROR: Size in record should be multiple of 4 bytes!");
return tbd_err_data;
}
/* Where our record should go */
record_ptr = (tbd_record_header_t *)((char *)(a_data_ptr) + header_ptr->size);
/* Create record header and store the record itself */
record_ptr->size = sizeof(tbd_record_header_t) + a_record_size;
record_ptr->format_id = a_record_format;
record_ptr->packing_key = 0;
record_ptr->class_id = a_record_class;
record_ptr++;
memcpy(record_ptr, a_record_data, a_record_size);
/* Update the header */
header_ptr->size = new_size;
header_ptr->checksum = 0;
header_ptr->checksum = get_checksum(header_ptr, new_size);
*a_new_size = new_size;
return tbd_err_none;
}
/*
* The indicated record is removed from the Tagged Binary Data, after
* which the checksum and size are updated accordingly. If there are
* multiple records that match the class and format, only the first
* instance is removed. If no record is found, nothing will be done.
* Note that the resulting Tagged Binary Data container will
* be smaller than the original, but it does not harm to store the
* resulting container in its original length, either.
* param[in] a_data_ptr Pointer to modifiable container buffer
* param[in] a_record_class Class the record should have
* param[in] a_record_format Format the record should have
* param[out] a_new_size Updated container size
* return Return code indicating possible errors
*/
tbd_error_t tbd_remove_record(void *a_data_ptr
, tbd_class_t a_record_class, tbd_format_t a_record_format
, size_t *a_new_size)
{
tbd_header_t *header_ptr;
uint8_t *byte_ptr, *eof_ptr;
size_t new_size;
/* Check the the content is ok */
int r;
if ((r = validate_anysize(a_data_ptr))) {
return r;
}
/* Container should begin with a header */
header_ptr = a_data_ptr;
/* First record is just after header */
byte_ptr = (uint8_t *)(header_ptr + 1);
eof_ptr = (uint8_t *)(a_data_ptr) + header_ptr->size;
/* Loop until there are no more records to go */
while (byte_ptr < eof_ptr) {
/* At least one more record is expected */
tbd_record_header_t *record_ptr = (tbd_record_header_t *)(byte_ptr);
uint16_t record_class = record_ptr->class_id;
uint8_t record_format = record_ptr->format_id;
uint32_t record_size = record_ptr->size;
if (((a_record_class == tbd_class_any) || (a_record_class == record_class))
&& ((a_record_format == tbd_format_any) || (a_record_format == record_format))) {
/* Match found, remove the record */
memcpy(byte_ptr, byte_ptr + record_size, eof_ptr - (byte_ptr + record_size));
/* Update the header */
new_size = header_ptr->size - record_size;
header_ptr->size = new_size;
header_ptr->checksum = 0;
header_ptr->checksum = get_checksum(header_ptr, new_size);
*a_new_size = new_size;
return tbd_err_none;
}
/* Match not found yet, continue the while loop... */
byte_ptr += record_size;
}
MSG_LOG("libtbd: Record not found!");
*a_new_size = header_ptr->size;
return tbd_err_none;
}
/*
* Validates the Tagged Binary data container and generates a human
* readable detailed report on the content, including information about
* the records contained.
* param[in] a_data_ptr Pointer to container buffer
* param[in] a_data_size Size of the container buffer
* param[in] a_outfile Pointer to open file (may be stdout)
* return Return code indicating possible errors
*/
tbd_error_t tbd_infoprint(void *a_data_ptr, size_t a_data_size
, FILE *a_outfile)
{
tbd_header_t *header_ptr;
uint8_t *byte_ptr, *eof_ptr, record_format, record_packing;
int num_of_records = 0, total_data = 0;
uint16_t record_class;
uint32_t record_size;
/* Check the the content is ok */
int r;
if ((r = validate(a_data_ptr, a_data_size))) {
return r;
}
/* Container should begin with a header */
header_ptr = a_data_ptr;
fprintf(a_outfile, "Data tag: 0x%08x (\'%c\' \'%c\' \'%c\' \'%c\')\n", header_ptr->tag, ((char *)(&header_ptr->tag))[0], ((char *)(&header_ptr->tag))[1], ((char *)(&header_ptr->tag))[2], ((char *)(&header_ptr->tag))[3]);
fprintf(a_outfile, "Data size: %d (0x%x), buffer size %d (0x%x)\n", header_ptr->size, header_ptr->size, a_data_size, a_data_size);
fprintf(a_outfile, "Data version: 0x%08x\n", header_ptr->version);
fprintf(a_outfile, "Data revision: 0x%08x\n", header_ptr->revision);
fprintf(a_outfile, "Data config: 0x%08x\n", header_ptr->config_bits);
fprintf(a_outfile, "Data checksum: 0x%08x\n", header_ptr->checksum);
fprintf(a_outfile, "\n");
/* First record is just after header */
byte_ptr = (uint8_t *)(header_ptr + 1);
eof_ptr = (uint8_t *)(a_data_ptr) + header_ptr->size;
/* Loop until there are no more records to go */
while (byte_ptr < eof_ptr) {
/* At least one more record is expected */
tbd_record_header_t *record_ptr = (tbd_record_header_t *)(byte_ptr);
num_of_records++;
record_class = record_ptr->class_id;
record_format = record_ptr->format_id;
record_packing = record_ptr->packing_key;
record_size = record_ptr->size;
total_data += record_size - sizeof(tbd_record_header_t);
fprintf(a_outfile, "Record size: %d (0x%x)\n", record_size, record_size);
fprintf(a_outfile, "Size w/o header: %d (0x%x)\n", record_size - sizeof(tbd_record_header_t), record_size - sizeof(tbd_record_header_t));
fprintf(a_outfile, "Record class: %d", record_class);
switch (record_class) {
case tbd_class_any:
fprintf(a_outfile, " \"tbd_class_any\"\n");
break;
case tbd_class_aiq:
fprintf(a_outfile, " \"tbd_class_aiq\"\n");
break;
case tbd_class_drv:
fprintf(a_outfile, " \"tbd_class_drv\"\n");
break;
case tbd_class_hal:
fprintf(a_outfile, " \"tbd_class_hal\"\n");
break;
default:
fprintf(a_outfile, " (unknown class)\n");
break;
}
fprintf(a_outfile, "Record format: %d", record_format);
switch (record_format) {
case tbd_format_any:
fprintf(a_outfile, " \"tbd_format_any\"\n");
break;
case tbd_format_custom:
fprintf(a_outfile, " \"tbd_format_custom\"\n");
break;
case tbd_format_container:
fprintf(a_outfile, " \"tbd_format_container\"\n");
break;
default:
fprintf(a_outfile, " (unknown format)\n");
break;
}
fprintf(a_outfile, "Packing: %d", record_packing);
if (record_packing == 0) {
fprintf(a_outfile, " (no packing)\n");
} else {
fprintf(a_outfile, "\n");
}
fprintf(a_outfile, "\n");
/* Continue the while loop... */
byte_ptr += record_size;
}
fprintf(a_outfile, "Number of records found: %d\n", num_of_records);
fprintf(a_outfile, "Total data in records: %d bytes (without headers)\n", total_data);
fprintf(a_outfile, "\n");
return tbd_err_none;
}