diff options
Diffstat (limited to 'futility/cmd_validate_rec_mrc.c')
-rw-r--r-- | futility/cmd_validate_rec_mrc.c | 95 |
1 files changed, 76 insertions, 19 deletions
diff --git a/futility/cmd_validate_rec_mrc.c b/futility/cmd_validate_rec_mrc.c index 3013891a..1fdd47c1 100644 --- a/futility/cmd_validate_rec_mrc.c +++ b/futility/cmd_validate_rec_mrc.c @@ -35,19 +35,22 @@ static void print_help(int argc, char *argv[]) printf("\n"); } -struct mrc_cache { +struct mrc_metadata { uint32_t signature; - uint32_t size; - uint32_t checksum; + uint32_t data_size; + uint16_t data_checksum; + uint16_t header_checksum; uint32_t version; - uint8_t data[0]; } __attribute__((packed)); #define MRC_DATA_SIGNATURE (('M'<<0)|('R'<<8)|('C'<<16)|('D'<<24)) +#define REGF_BLOCK_SHIFT 4 +#define REGF_UNALLOCATED_BLOCK 0xffff -unsigned long compute_ip_checksum(void *addr, unsigned long length) + +unsigned long compute_ip_checksum(const void *addr, unsigned long length) { - uint8_t *ptr; + const uint8_t *ptr; volatile union { uint8_t byte[2]; uint16_t word; @@ -77,29 +80,71 @@ unsigned long compute_ip_checksum(void *addr, unsigned long length) return (~value.word) & 0xFFFF; } -static int verify_checksum(struct mrc_cache *cache, unsigned long cache_len) +static int verify_mrc_slot(struct mrc_metadata *md, unsigned long slot_len) { - uint32_t checksum; - if (cache->signature != MRC_DATA_SIGNATURE) { + uint32_t header_checksum; + if (md->signature != MRC_DATA_SIGNATURE) { fprintf(stderr, "MRC signature mismatch\n"); return 1; } fprintf(stderr, "MRC signature match.. successful\n"); - if (cache->size > cache_len) { + if (md->data_size > slot_len) { fprintf(stderr, "MRC cache size overflow\n"); return 1; } - checksum = compute_ip_checksum((void *)&cache->data[0], cache->size); + header_checksum = md->header_checksum; + md->header_checksum = 0; + + if (header_checksum != compute_ip_checksum(md, sizeof(*md))) { + fprintf(stderr, "MRC metadata header checksum mismatch\n"); + return 1; + } + + md->header_checksum = header_checksum; + + fprintf(stderr, "MRC metadata header checksum.. verified!\n"); - if (checksum != cache->checksum) { - fprintf(stderr, "MRC checksum mismatch\n"); + if (md->data_checksum != compute_ip_checksum(&md[1], md->data_size)) { + fprintf(stderr, "MRC data checksum mismatch\n"); + return 1; + } + + fprintf(stderr, "MRC data checksum.. verified!\n"); + return 0; +} + +static int get_mrc_data_slot(uint16_t *mb, uint32_t *data_offset, + uint32_t *data_size) +{ + /* + * First block offset in metadata block tells the total number of + * metadata blocks. + * Currently, we expect only 1 metadata block to be present. + */ + if (*mb != 1) { + fprintf(stderr, "Only 1 metadata block is expected. " + "Actual %x\n", *mb); + return 1; + } + + /* + * RECOVERY_MRC_CACHE is expected to contain only one slot. Thus, there + * should be only one block offset present, indicating size of the MRC + * cache slot. + */ + mb++; + *data_offset = (1 << REGF_BLOCK_SHIFT); + *data_size = (*mb - 1) << REGF_BLOCK_SHIFT; + + mb++; + if (*mb != REGF_UNALLOCATED_BLOCK) { + fprintf(stderr, "More than 1 slot in recovery mrc cache.\n"); return 1; } - fprintf(stderr, "MRC checksum.. verified!\n"); return 0; } @@ -111,6 +156,8 @@ static int do_validate_rec_mrc(int argc, char *argv[]) uint32_t file_size; uint8_t *buff; uint32_t offset = 0; + uint32_t data_offset; + uint32_t data_size; char *e; while (((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) && @@ -162,12 +209,22 @@ static int do_validate_rec_mrc(int argc, char *argv[]) return 1; } - if (file_size > offset) - ret = verify_checksum((struct mrc_cache *)(buff + offset), - file_size - offset); + if (get_mrc_data_slot((uint16_t *)(buff + offset), &data_offset, + &data_size)) { + fprintf(stderr, "Metadata block error\n"); + futil_unmap_file(fd, MAP_RO, buff, file_size); + close(fd); + return 1; + } + offset += data_offset; + + if ((file_size > offset) && ((file_size - offset) >= data_size)) + ret = verify_mrc_slot((struct mrc_metadata *)(buff + offset), + data_size); else - fprintf(stderr, "Offset greater than file size: offset=0x%x, " - "file size=0x%x\n", offset, file_size); + fprintf(stderr, "Offset or data size greater than file size: " + "offset=0x%x, file size=0x%x, data_size=0x%x\n", + offset, file_size, data_size); if (futil_unmap_file(fd, MAP_RO, buff, file_size) != FILE_ERR_NONE) { fprintf(stderr, "Failed to unmap file %s\n", infile); |