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