Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

/*-
 * Copyright (c) 2017 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD$
 */

/**
 * @file
 * OCS VPD parser
 */

#if !defined(__OCS_VPD_H__)
#define __OCS_VPD_H__

/**
 * @brief VPD buffer structure
 */

typedef struct {
	uint8_t *buffer;
	uint32_t length;
	uint32_t offset;
	uint8_t checksum;
	} vpdbuf_t;

/**
 * @brief return next VPD byte
 *
 * Returns next VPD byte and updates accumulated checksum
 *
 * @param vpd pointer to vpd buffer
 *
 * @return returns next byte for success, or a negative error code value for failure.
 *
 */

static inline int
vpdnext(vpdbuf_t *vpd)
{
	int rc = -1;
	if (vpd->offset < vpd->length) {
		rc = vpd->buffer[vpd->offset++];
		vpd->checksum += rc;
	}
	return rc;
}

/**
 * @brief return true if no more vpd buffer data
 *
 * return true if the vpd buffer data has been completely consumed
 *
 * @param vpd pointer to vpd buffer
 *
 * @return returns true if no more data
 *
 */
static inline int
vpddone(vpdbuf_t *vpd)
{
	return vpd->offset >= vpd->length;
}
/**
 * @brief return pointer to current VPD data location
 *
 * Returns a pointer to the current location in the VPD data
 *
 * @param vpd pointer to vpd buffer
 *
 * @return pointer to current VPD data location
 */

static inline uint8_t *
vpdref(vpdbuf_t *vpd)
{
	return &vpd->buffer[vpd->offset];
}

#define VPD_LARGE_RESOURCE_TYPE_ID_STRING_TAG	0x82
#define VPD_LARGE_RESOURCE_TYPE_R_TAG		0x90
#define VPD_LARGE_RESOURCE_TYPE_W_TAG		0x91
#define VPD_SMALL_RESOURCE_TYPE_END_TAG		0x78

/**
 * @brief find a VPD entry
 *
 * Finds a VPD entry given the two character code
 *
 * @param vpddata pointer to raw vpd data buffer
 * @param vpddata_length length of vpddata buffer in bytes
 * @param key key to look up

 * @return returns a pointer to the key location or NULL if not found or checksum error
 */

static inline uint8_t *
ocs_find_vpd(uint8_t *vpddata, uint32_t vpddata_length, const char *key)
{
	vpdbuf_t vpdbuf;
	uint8_t *pret = NULL;
	uint8_t c0 = key[0];
	uint8_t c1 = key[1];

	vpdbuf.buffer = (uint8_t*) vpddata;
	vpdbuf.length = vpddata_length;
	vpdbuf.offset = 0;
	vpdbuf.checksum = 0;

	while (!vpddone(&vpdbuf)) {
		int type = vpdnext(&vpdbuf);
		int len_lo;
		int len_hi;
		int len;
		int i;

		if (type == VPD_SMALL_RESOURCE_TYPE_END_TAG) {
			break;
		}

		len_lo = vpdnext(&vpdbuf);
		len_hi = vpdnext(&vpdbuf);
		len = len_lo + (len_hi << 8);

		if ((type == VPD_LARGE_RESOURCE_TYPE_R_TAG) || (type == VPD_LARGE_RESOURCE_TYPE_W_TAG)) {
			while (len > 0) {
				int rc0;
				int rc1;
				int sublen;
				uint8_t *pstart;

				rc0 = vpdnext(&vpdbuf);
				rc1 = vpdnext(&vpdbuf);

				/* Mark this location */
				pstart = vpdref(&vpdbuf);

				sublen = vpdnext(&vpdbuf);

				/* Adjust remaining len */
				len -= (sublen + 3);

				/* check for match with request */
				if ((c0 == rc0) && (c1 == rc1)) {
					pret = pstart;
					for (i = 0; i < sublen; i++) {
						vpdnext(&vpdbuf);
					}
				/* check for "RV" end */
				} else if ('R' == rc0 && 'V' == rc1) {
					/* Read the checksum */
					for (i = 0; i < sublen; i++) {
						vpdnext(&vpdbuf);
					}

					/* The accumulated checksum should be zero here */
					if (vpdbuf.checksum != 0) {
						ocs_log_test(NULL, "checksum error\n");
						return NULL;
					}
				}
				else
					for (i = 0; i < sublen; i++) {
						vpdnext(&vpdbuf);
					}
			}
		}

		for (i = 0; i < len; i++) {
			vpdnext(&vpdbuf);
		}
	}

	return pret;
}
#endif