Skip to content
Snippets Groups Projects
rmap.c 16.58 KiB
/**
 * @file   rmap.c
 * @author Armin Luntzer (armin.luntzer@univie.ac.at),
 * @date   2018
 *
 * @copyright GPLv2
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * @brief rmap command/reply helper functions
 *
 * @note the extended address byte is always set to 0x0
 */



#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <rmap.h>





/**
 * @brief valiidates a command code
 *
 * @param cmd the command code
 *
 * @returns 0 on success, error otherwise
 */

static int rmap_validate_cmd_code(uint8_t cmd)
{
	switch (cmd) {
	case RMAP_READ_ADDR_SINGLE:
	case RMAP_READ_ADDR_INC:
	case RMAP_READ_MODIFY_WRITE_ADDR_INC:
	case RMAP_WRITE_ADDR_SINGLE:
	case RMAP_WRITE_ADDR_INC:
	case RMAP_WRITE_ADDR_SINGLE_REPLY:
	case RMAP_WRITE_ADDR_INC_REPLY:
	case RMAP_WRITE_ADDR_SINGLE_VERIFY:
	case RMAP_WRITE_ADDR_INC_VERIFY:
	case RMAP_WRITE_ADDR_SINGLE_VERIFY_REPLY:
	case RMAP_WRITE_ADDR_INC_VERIFY_REPLY:
		return 0;
	default:
		return -1;
	}
}


/**
 * @brief get the minimum header size given the RMAP instruction
 *
 * @param pkt a struct rmap_pkt
 *
 * @returns header size or -1 on error
 */

static int rmap_get_min_hdr_size(struct rmap_pkt *pkt)
{


	switch (pkt->ri.cmd) {
	case RMAP_READ_ADDR_SINGLE:
	case RMAP_READ_ADDR_INC:
	case RMAP_READ_MODIFY_WRITE_ADDR_INC:

		if (pkt->ri.cmd_resp)
			return RMAP_HDR_MIN_SIZE_READ_CMD;

		return RMAP_HDR_MIN_SIZE_READ_REP;

	case RMAP_WRITE_ADDR_SINGLE:
	case RMAP_WRITE_ADDR_INC:
	case RMAP_WRITE_ADDR_SINGLE_REPLY:
	case RMAP_WRITE_ADDR_INC_REPLY:
	case RMAP_WRITE_ADDR_SINGLE_VERIFY:
	case RMAP_WRITE_ADDR_INC_VERIFY:
	case RMAP_WRITE_ADDR_SINGLE_VERIFY_REPLY:
	case RMAP_WRITE_ADDR_INC_VERIFY_REPLY:

		if (pkt->ri.cmd_resp)
			return RMAP_HDR_MIN_SIZE_WRITE_CMD;

		return RMAP_HDR_MIN_SIZE_WRITE_REP;

	default:
		return -1;
	}
}

/**
 * @brief calculate the CRC8 of a given buffer
 *
 * @param buf the buffer containing the data
 * @param len the length of the buffer
 *
 * @returns the CRC8
 */

uint8_t rmap_crc8(const uint8_t *buf, const size_t len)
{
	size_t i;

	uint8_t crc8 = 0;

	/* crc8 lookup table from ECSS‐E‐ST‐50‐52C A.3 */
	const uint8_t crc8_lt[256] = {
		0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
		0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
		0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
		0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
		0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
		0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
		0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
		0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
		0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
		0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
		0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
		0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
		0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
		0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
		0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
		0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
		0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
		0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
		0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
		0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
		0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
		0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
		0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
		0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
		0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
		0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
		0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
		0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
		0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
		0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
		0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
		0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf,
	};



	if (!buf)
		return 0;


	for (i = 0; i < len; i++)
		crc8 = crc8_lt[crc8 ^ buf[i]];

	return crc8;
}


/**
 * @brief create an RMAP packet and set defaults
 *
 *
 * @note initialises protocol id to 1 and all others to 0
 *
 * @returns a struct rmap_pkt or NULL on error
 */

struct rmap_pkt *rmap_create_packet(void)
{
	struct rmap_pkt *pkt;


	pkt = (struct rmap_pkt *) calloc(sizeof(struct rmap_pkt), 1);
	if (pkt)
		pkt->proto_id = RMAP_PROTOCOL_ID;

	return pkt;
}


/**
 * @brief destroys an RMAP packet
 *
 * @param pkt a struct rmap_pkt
 *
 * @note this will NOT deallocate and pointer references assigned by the user
 */

void rmap_destroy_packet(struct rmap_pkt *pkt)
{
	free(pkt);
}


/**
 * @brief completely destroys an RMAP packet
 *
 * @param pkt a struct rmap_pkt
 *
 * @note this will attempt to deallocate any pointer references assigned by the
 * 	 user
 * @warning use with care
 */

void rmap_erase_packet(struct rmap_pkt *pkt)
{
	free(pkt->path);
	free(pkt->rpath);
	free(pkt->data);
	free(pkt);
}

/**
 * @brief set the destination (target) logical address
 *
 * @param pkt	a struct rmap_pkt
 * @param addr	the destination logical address
 */

void rmap_set_dst(struct rmap_pkt *pkt, uint8_t addr)
{
	if (pkt)
		pkt->dst = addr;
}


/**
 * @brief set the source (initiator) logical address
 *
 * @param pkt	a struct rmap_pkt
 * @param addr	the source logical address
 */

void rmap_set_src(struct rmap_pkt *pkt, uint8_t addr)
{
	if (pkt)
		pkt->src = addr;
}


/**
 * @brief set the command authorisation key
 *
 * @param pkt	a struct rmap_pkt
 * @param key	the authorisation key
 */

void rmap_set_key(struct rmap_pkt *pkt, uint8_t key)
{
	if (pkt)
		pkt->key = key;
}


/**
 * @brief set the reply address path
 *
 * @param pkt	a struct rmap_pkt
 * @param rpath the reply path
 * @param len   the number of elements in the reply path (multiple of 4)
 *
 * @note see ECSS‐E‐ST‐50‐52C 5.1.6 for return path rules
 *
 * @returns 0 on success, -1 on error
 */

int rmap_set_reply_path(struct rmap_pkt *pkt, const uint8_t *rpath, uint8_t len)
{
	if (!pkt)
		return -1;

	if (!rpath && len)
		return -1;

	if (len > RMAP_MAX_REPLY_PATH_LEN)
		return -1;

	if (len & 0x3)
		return -1;

	pkt->rpath_len = len;

	pkt->rpath = (uint8_t *) malloc(pkt->rpath_len);
	if (!pkt->rpath)
		return -1;

	memcpy(pkt->rpath, rpath, pkt->rpath_len);

	/* number of 32 bit words needed to contain the path */
	pkt->ri.reply_addr_len = len >> 2;

	return 0;
}


/**
 * @brief set the destination address path
 *
 * @param pkt	a struct rmap_pkt
 * @param path the destination path
 * @param len   the number of elements in the destination path
 *
 * @note see ECSS‐E‐ST‐50‐52C 5.1.6 for return path rules
 *
 * @returns 0 on success, -1 on error
 */

int rmap_set_dest_path(struct rmap_pkt *pkt, const uint8_t *path, uint8_t len)
{
	if (!pkt)
		return -1;

	if (!path && len)
		return -1;

	if (len > RMAP_MAX_PATH_LEN)
		return -1;

	pkt->path_len = len;

	pkt->path = (uint8_t *) malloc(pkt->path_len);
	if (!pkt->path)
		return -1;

	memcpy(pkt->path, path, pkt->path_len);

	return 0;
}


/**
 * @brief set an RMAP command
 *
 * @param pkt	a struct rmap_pkt
 * @param cmd	the selected command
 *
 * @returns -1 on error
 */

int rmap_set_cmd(struct rmap_pkt *pkt, uint8_t cmd)
{
	if (!pkt)
		return -1;

	if (rmap_validate_cmd_code(cmd))
		return -1;


	pkt->ri.cmd      = cmd & 0xF;
	pkt->ri.cmd_resp = 1;

	return 0;
}


/**
 * @brief set an RMAP transaction identifier
 *
 * @param pkt	a struct rmap_pkt
 * @param id	the transaction identifier
 */

void rmap_set_tr_id(struct rmap_pkt *pkt, uint16_t id)
{
	if (!pkt)
		return;

	pkt->tr_id = id;
}


/**
 * @brief set a data address
 *
 * @param pkt	a struct rmap_pkt
 * @param addr	the address
 */

void rmap_set_data_addr(struct rmap_pkt *pkt, uint32_t addr)
{
	if (!pkt)
		return;

	pkt->addr = addr;
}

/**
 * @brief set an RMAP command
 *
 * @param pkt	a struct rmap_pkt
 * @param len	the data length (in bytes)
 *
 * @returns -1 on error
 *
 * @note the length is at most 2^24-1 bytes
 * @note if the RMAP command is of 'SINGLE' type, only multiples of 4
 *	 will result in successfull execution of the command (at least
 *	 with the GRSPW2 core)
 */

int rmap_set_data_len(struct rmap_pkt *pkt, uint32_t len)
{
	if (!pkt)
		return -1;

	if (len > RMAP_MAX_DATA_LEN)
		return -1;

	pkt->data_len = len;

	return 0;
}


/**
 * @brief build an rmap header
 *
 * @param pkt	a struct rmap_pkt
 * @param hdr	the header buffer; if NULL, the function returns the needed size
 *
 * @returns -1 on error, size of header otherwise
 */

int rmap_build_hdr(struct rmap_pkt *pkt, uint8_t *hdr)
{
	int i;
	int n = 0;


	if (!pkt)
		return -1;

	if (!hdr) {
		n = rmap_get_min_hdr_size(pkt);
		n += pkt->path_len;
		n += pkt->rpath_len;
		return n;
	}


	for (i = 0; i < pkt->path_len; i++)
		hdr[n++] = pkt->path[i];	/* routing path to target */

	hdr[n++] = pkt->dst;			/* target logical address */
	hdr[n++] = pkt->proto_id;		/* protocol id */
	hdr[n++] = pkt->instruction;		/* instruction */
	hdr[n++] = pkt->key;			/* key/status */

	for (i = 0; i < pkt->rpath_len; i++)
		hdr[n++] = pkt->rpath[i];	/* return path to source */

	hdr[n++] = pkt->src;			/* source logical address */
	hdr[n++] = (uint8_t) (pkt->tr_id >> 8);	/* MSB of transaction id */
	hdr[n++] = (uint8_t)  pkt->tr_id;	/* LSB of transaction id */


	/* commands have a data address */
	if (pkt->ri.cmd_resp) {
		hdr[n++] = 0x0;	/* extended address field (unused) */
		hdr[n++] = (uint8_t) (pkt->addr >> 24); /* data addr MSB */
		hdr[n++] = (uint8_t) (pkt->addr >> 16);
		hdr[n++] = (uint8_t) (pkt->addr >>  8);
		hdr[n++] = (uint8_t)  pkt->addr;	/* data addr LSB */
	} else if (!pkt->ri.cmd_resp && pkt->ri.cmd & RMAP_CMD_BIT_WRITE) {
		/* all headers have data length unless they are a write reply */
		return n;
	} else {
		hdr[n++] = 0x0;	/* on other replies, this is a reserved field */
	}

	hdr[n++] = (uint8_t) (pkt->data_len >> 16); /* data len MSB */
	hdr[n++] = (uint8_t) (pkt->data_len >>  8);
	hdr[n++] = (uint8_t)  pkt->data_len;	    /* data len LSB */

	return n;
}


/**
 * @brief create an rmap packet from a buffer
 *
 * @param buf the buffer, with the target path stripped away, i.e.
 *	  starting with <logical address>, <protocol id>, ...
 * @param len the data length of the buffer (in bytes)
 *
 * @returns an rmap packet, containing the decoded buffer including any data,
 *	    NULL on error
 */

struct rmap_pkt *rmap_pkt_from_buffer(uint8_t *buf, uint32_t len)
{
	size_t n = 0;
	size_t i;
	int min_hdr_size;

	struct rmap_pkt *pkt = NULL;


	if (!buf)
		goto error;

	if (len < RMAP_HDR_MIN_SIZE_WRITE_REP) {
		printf("buffer len is smaller than the smallest RMAP packet\n");
		goto error;
	}

	if (buf[RMAP_PROTOCOL_ID] != RMAP_PROTOCOL_ID) {
		printf("Not an RMAP packet, got %x but expected %x\n",
		       buf[RMAP_PROTOCOL_ID], RMAP_PROTOCOL_ID);
		goto error;
	}

	pkt = rmap_create_packet();
	if (!pkt) {
		printf("Error creating packet\n");
		goto error;
	}

	pkt->dst         = buf[RMAP_DEST_ADDRESS];
	pkt->proto_id    = buf[RMAP_PROTOCOL_ID];
	pkt->instruction = buf[RMAP_INSTRUCTION];
	pkt->key         = buf[RMAP_CMD_DESTKEY];

	min_hdr_size = rmap_get_min_hdr_size(pkt);
	if (min_hdr_size < 0)
		goto error;

	if (len < (uint32_t)min_hdr_size) {
		printf("buffer len is smaller than the contained RMAP packet\n");
		goto error;
	}


	if (pkt->ri.cmd_resp) {
		pkt->rpath_len = pkt->ri.reply_addr_len << 2;
		if (len < (uint32_t)min_hdr_size + pkt->rpath_len) {
			printf("buffer is smaller than the contained RMAP packet\n");
			goto error;
		}

		pkt->rpath = (uint8_t *) malloc(pkt->rpath_len);
		if (!pkt->rpath)
			goto error;

		for (i = 0; i < pkt->rpath_len; i++)
			pkt->rpath[i] = buf[RMAP_REPLY_ADDR_START + i];

		n = pkt->rpath_len; /* rpath skip */
	}

	pkt->src   = buf[RMAP_SRC_ADDR + n];
	pkt->tr_id = ((uint16_t) buf[RMAP_TRANS_ID_BYTE0 + n] << 8) |
	              (uint16_t) buf[RMAP_TRANS_ID_BYTE1 + n];

	/* commands have a data address */
	if (pkt->ri.cmd_resp) {
		pkt->addr = ((uint32_t) buf[RMAP_ADDR_BYTE0 + n] << 24) |
			    ((uint32_t) buf[RMAP_ADDR_BYTE1 + n] << 16) |
			    ((uint32_t) buf[RMAP_ADDR_BYTE2 + n] <<  8) |
			     (uint32_t) buf[RMAP_ADDR_BYTE3 + n];
		n += 4; /* addr skip, extended byte is incorporated in define */
	}

	/* all headers have data length unless they are a write reply */
	if (!(!pkt->ri.cmd_resp && (pkt->ri.cmd & (RMAP_CMD_BIT_WRITE)))) {

		pkt->data_len = ((uint32_t) buf[RMAP_DATALEN_BYTE0 + n] << 16) |
				((uint32_t) buf[RMAP_DATALEN_BYTE1 + n] <<  8) |
			         (uint32_t) buf[RMAP_DATALEN_BYTE2 + n];
	}

	pkt->hdr_crc  = buf[RMAP_HEADER_CRC];

	if (pkt->data_len) {
		if (len < RMAP_DATA_START + n + pkt->data_len + 1) {  /* +1 for data CRC */
			printf("buffer len is smaller than the contained RMAP packet; buf len: %u bytes vs RMAP: %lu bytes needed\n",
				len , RMAP_DATA_START + n + pkt->data_len);
			goto error;
		}
		if (len > RMAP_DATA_START + n + pkt->data_len + 1)  /* +1 for data CRC */
			printf("warning: the buffer is larger than the included RMAP packet\n");

		pkt->data = (uint8_t *) malloc(pkt->data_len);
		if (!pkt->data)
			goto error;

		for (i = 0; i < pkt->data_len; i++)
			pkt->data[i] = buf[RMAP_DATA_START + n + i];

		/* final byte is data crc */
		pkt->data_crc = buf[RMAP_DATA_START + n + i];
	}


	return pkt;

error:
	if (pkt) {
		free(pkt->data);
		free(pkt->rpath);
		free(pkt);
	}

	return NULL;
}



/**** UNFINISHED INFO STUFF BELOW ******/

__extension__
static int rmap_check_status(uint8_t status)
{


	printf("\tStatus: ");

	switch (status) {
	case RMAP_STATUS_SUCCESS:
		printf("Command executed successfully");
		break;
	case RMAP_STATUS_GENERAL_ERROR:
		printf("General error code");
		break;
	case RMAP_STATUS_UNUSED_TYPE_OR_CODE:
		printf("Unused RMAP Packet Type or Command Code");
		break;
	case RMAP_STATUS_INVALID_KEY:
		printf("Invalid key");
		break;
	case RMAP_STATUS_INVALID_DATA_CRC:
		printf("Invalid Data CRC");
		break;
	case RMAP_STATUS_EARLY_EOP:
		printf("Early EOP");
		break;
	case RMAP_STATUS_TOO_MUCH_DATA:
		printf("Too much data");
		break;
	case RMAP_STATUS_EEP:
		printf("EEP");
		break;
	case RMAP_STATUS_RESERVED:
		printf("Reserved");
		break;
	case RMAP_STATUS_VERIFY_BUFFER_OVERRRUN:
		printf("Verify buffer overrrun");
		break;
	case RMAP_STATUS_CMD_NOT_IMPL_OR_AUTH:
		printf("RMAP Command not implemented or not authorised");
		break;
	case RMAP_STATUS_RMW_DATA_LEN_ERROR:
		printf("RMW Data Length error");
		break;
	case RMAP_STATUS_INVALID_TARGET_LOGICAL_ADDR:
		printf("Invalid Target Logical Address");
		break;
	default:
		printf("Reserved unused error code %d", status);
		break;
	}

	printf("\n");


	return status;
}



static void rmap_process_read_reply(uint8_t *pkt)
{
	uint32_t i;

	uint32_t len = 0;


	len |= ((uint32_t) pkt[RMAP_DATALEN_BYTE0]) << 16;
	len |= ((uint32_t) pkt[RMAP_DATALEN_BYTE1]) <<  8;
	len |= ((uint32_t) pkt[RMAP_DATALEN_BYTE2]) <<  0;

#if (__sparc__)
	printf("\tData length is %lu bytes:\n\t", len);
#else
	printf("\tData length is %u bytes:\n\t", len);
#endif


	for (i = 0; i < len; i++)
		printf("%02x:", pkt[RMAP_DATA_START + i]);

	printf("\b \n");
}




static void rmap_parse_cmd_pkt(uint8_t *pkt)
{
	(void) pkt;
	printf("\trmap_parse_cmd_pkt() not implemented\n");
}


static void rmap_parse_reply_pkt(uint8_t *pkt)
{
	struct rmap_instruction *ri;


	ri = (struct rmap_instruction *) &pkt[RMAP_INSTRUCTION];

	printf("\tInstruction: ");

	switch (ri->cmd) {

	case RMAP_READ_ADDR_SINGLE:
		printf("Read single address\n");
		rmap_process_read_reply(pkt);
		break;
	case RMAP_READ_ADDR_INC:
		printf("Read incrementing address\n");
		rmap_process_read_reply(pkt);
		break;
	case RMAP_READ_MODIFY_WRITE_ADDR_INC:
		printf("RMW incrementing address verify reply\n");
		break;
	case RMAP_WRITE_ADDR_INC_VERIFY_REPLY:
		printf("Write incrementing address verify reply\n");
		break;
	case RMAP_WRITE_ADDR_INC_REPLY:
		printf("Write incrementing address reply\n");
		break;
	default:
		printf("decoding of instruction 0x%02X not implemented\n",
		       ri->cmd);
		break;
	}
}


/**
 * parse an RMAP packet:
 *
 * expected format: <logical address> <protocol id> ...
 */

void rmap_parse_pkt(uint8_t *pkt)
{
	struct rmap_instruction *ri;

	if (pkt[RMAP_PROTOCOL_ID] != RMAP_PROTOCOL_ID) {
		printf("\nNot an RMAP packet, got %x but expected %x\n",
		       pkt[RMAP_PROTOCOL_ID], RMAP_PROTOCOL_ID);
		return;
	}


	ri = (struct rmap_instruction *) &pkt[RMAP_INSTRUCTION];

	if (ri->cmd_resp) {
		printf("This is a command packet\n");
		if (!rmap_check_status(pkt[RMAP_REPLY_STATUS]))
			rmap_parse_cmd_pkt(pkt);
	} else {
		printf("This is a reply packet\n");
		if (!rmap_check_status(pkt[RMAP_REPLY_STATUS]))
			rmap_parse_reply_pkt(pkt);
	}
}