#ifndef __INCLUDE_IMAGE_METADTA_H
#define __INCLUDE_IMAGE_METADTA_H

/*
 * barebox Image MetaData (IMD)
 *
 * IMD is a mechanism to store metadata in barebox images. With IMD
 * it's possible to extract the release, the build timestamp and the
 * board type from a barebox image.
 *
 * Since there is no fixed place in the image suitable for all SoC image
 * types the metadata can be stored anywhere in the image and is found
 * by iterating over the image. The metadata starts with a start header
 * and ends with an end header. All tags in between carry the payload.
 *
 * Make sure source files containing IMD data are compiled with lwl-y so
 * that the tags end up in the PBL if one exists and in the regular image
 * without PBL.
 *
 * The following types exist:
 */
#define IMD_TYPE_START		0x640c0001
#define IMD_TYPE_RELEASE	0x640c8002 /* The barebox release aka UTS_RELEASE */
#define IMD_TYPE_BUILD		0x640c8003 /* build number and timestamp (UTS_VERSION) */
#define IMD_TYPE_MODEL		0x640c8004 /* The board name this image is for */
#define IMD_TYPE_OF_COMPATIBLE	0x640c8005 /* the device tree compatible string */
#define IMD_TYPE_PARAMETER	0x640c8006 /* A generic parameter. Use key=value as data */
#define IMD_TYPE_END		0x640c7fff
#define IMD_TYPE_INVALID	0xffffffff

/*
 * The IMD header. All data is stored in little endian format in the image.
 * The next header starts at the next 4 byte boundary after the data.
 */
struct imd_header {
	uint32_t type;		/* One of IMD_TYPE_* above */
	uint32_t datalength;	/* Length of the data (exluding the header) */
};

/*
 * A IMD string. Set bit 15 of the IMD_TYPE to indicate the data is printable
 * as string.
 */
struct imd_entry_string {
	struct imd_header header;
	char data[];
};

static inline int imd_is_string(uint32_t type)
{
	return (type & 0x8000) ? 1 : 0;
}

static inline int imd_type_valid(uint32_t type)
{
	return (type & 0xffff0000) == 0x640c0000;
}

struct imd_header *imd_next(struct imd_header *imd);

#define imd_for_each(start, imd) \
	for (imd = imd_next(start); imd_read_type(imd) != IMD_TYPE_END; imd = imd_next(imd))

static inline uint32_t imd_read_le32(void *_ptr)
{
	uint8_t *ptr = _ptr;

	return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
}

static inline uint32_t imd_read_type(struct imd_header *imd)
{
	return imd_read_le32(&imd->type);
}

static inline uint32_t imd_read_length(struct imd_header *imd)
{
	return imd_read_le32(&imd->datalength);
}

struct imd_header *imd_find_type(struct imd_header *imd, uint32_t type);

extern int imd_command_verbose;
int imd_command_setenv(const char *variable_name, const char *value);
int imd_command(int argc, char *argv[]);

#ifdef __BAREBOX__

#include <linux/stringify.h>

#define __BAREBOX_IMD_SECTION(_section) \
	__attribute__ ((unused,section (__stringify(_section)))) \
	__attribute__((aligned(4)))

#define BAREBOX_IMD_TAG_STRING(_name, _type, _string, _keep_if_unused)			\
	const struct imd_entry_string __barebox_imd_##_name				\
	__BAREBOX_IMD_SECTION(.barebox_imd_ ## _keep_if_unused ## _ ## _name) = {	\
		.header.type = cpu_to_le32(_type),					\
		.header.datalength = cpu_to_le32(sizeof(_string)),			\
		.data = _string,							\
	}


#ifdef CONFIG_IMD
void imd_used(const void *);
#else
static inline void imd_used(const void *unused)
{
}
#endif

#define IMD_USED(_name) \
	imd_used(&__barebox_imd_##_name)

#endif /* __BAREBOX__ */

#endif /* __INCLUDE_IMAGE_METADTA_H */
