/* SPDX-License-Identifier: GPL-2.0-only */
#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_BUILDSYSTEM	0x640c8007 /* The buildsystem version barebox was built with */
#define IMD_TYPE_CRC32		0x640c1007 /* the checksum of the barebox images */
#define IMD_TYPE_END		0x640c7fff
#define IMD_TYPE_INVALID	0xffffffff

#define IMD_CRC32_FLAG_TAG_VALID	(1 << 0)

/*
 * 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;
}

/*
 * A IMD int.
 */
struct imd_entry_crc32 {
	struct imd_header header;
	uint32_t data;
	uint32_t flags;
};

static inline int imd_is_crc32(uint32_t type)
{
	return type == IMD_TYPE_CRC32;
}

static inline int imd_crc32_is_valid(uint32_t flags)
{
	return (flags & IMD_CRC32_FLAG_TAG_VALID) ? 1 : 0;
}

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

const struct imd_header *imd_next(const 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(const void *_ptr)
{
	const uint8_t *ptr = _ptr;

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

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

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

static inline uint32_t imd_read_flags(const struct imd_entry_crc32 *imd)
{
	return imd_read_le32(&imd->flags);
}

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

const struct imd_header *imd_get(const void *buf, int size);
const char *imd_string_data(const struct imd_header *imd, int index);
const uint32_t *imd_uint32_data(const struct imd_header *imd);
uint32_t *imd_uint32_flags(const struct imd_header *imd);
const char *imd_type_to_name(uint32_t type);
char *imd_concat_strings(const struct imd_header *imd);
const char *imd_get_param(const struct imd_header *imd, const char *name);

extern int imd_command_verbose;
int imd_command_setenv(const char *variable_name, const char *value);
int imd_command(int argc, char *argv[]);
int imd_verify_crc32(void *buf, size_t size);

#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,							\
	}

#define BAREBOX_IMD_CRC(_name, _crc, _keep_if_unused)					\
	const struct imd_entry_crc32 __barebox_imd_##__name				\
	__BAREBOX_IMD_SECTION(.barebox_imd_ ## _keep_if_unused ## _ ## _name) = {	\
		.header.type = cpu_to_le32(IMD_TYPE_CRC32),				\
		.header.datalength = cpu_to_le32(sizeof(uint32_t) * 2),			\
		.data = _crc,								\
	}

#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)
#define IMD_USED_OF(_name) ({ \
	extern char __barebox_imd_OF_ ## _name[]; \
	imd_used(&__barebox_imd_OF_ ## _name); \
	})

#endif /* __BAREBOX__ */

#endif /* __INCLUDE_IMAGE_METADTA_H */
