diff options
Diffstat (limited to 'futility/cmd_validate_rec_mrc.c')
-rw-r--r-- | futility/cmd_validate_rec_mrc.c | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/futility/cmd_validate_rec_mrc.c b/futility/cmd_validate_rec_mrc.c new file mode 100644 index 00000000..3013891a --- /dev/null +++ b/futility/cmd_validate_rec_mrc.c @@ -0,0 +1,182 @@ +/* + * Copyright 2016 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "futility.h" + +enum { + OPT_HELP = 1000, + OPT_OFFSET, +}; + +static const struct option long_opts[] = { + {"help", 0, 0, OPT_HELP}, + {"offset", 1, 0, OPT_OFFSET}, + {NULL, 0, NULL, 0}, +}; + +static void print_help(int argc, char *argv[]) +{ + printf("\nUsage: " MYNAME " %s FILE [OPTIONS]\n", argv[0]); + printf("\nOptions:\n"); + printf(" --offset <offset> Offset of cache within FILE\n"); + printf("\n"); +} + +struct mrc_cache { + uint32_t signature; + uint32_t size; + uint32_t checksum; + uint32_t version; + uint8_t data[0]; +} __attribute__((packed)); + +#define MRC_DATA_SIGNATURE (('M'<<0)|('R'<<8)|('C'<<16)|('D'<<24)) + +unsigned long compute_ip_checksum(void *addr, unsigned long length) +{ + uint8_t *ptr; + volatile union { + uint8_t byte[2]; + uint16_t word; + } value; + unsigned long sum; + unsigned long i; + /* In the most straight forward way possible, + * compute an ip style checksum. + */ + sum = 0; + ptr = addr; + for(i = 0; i < length; i++) { + unsigned long v; + v = ptr[i]; + if (i & 1) { + v <<= 8; + } + /* Add the new value */ + sum += v; + /* Wrap around the carry */ + if (sum > 0xFFFF) { + sum = (sum + (sum >> 16)) & 0xFFFF; + } + } + value.byte[0] = sum & 0xff; + value.byte[1] = (sum >> 8) & 0xff; + return (~value.word) & 0xFFFF; +} + +static int verify_checksum(struct mrc_cache *cache, unsigned long cache_len) +{ + uint32_t checksum; + if (cache->signature != MRC_DATA_SIGNATURE) { + fprintf(stderr, "MRC signature mismatch\n"); + return 1; + } + + fprintf(stderr, "MRC signature match.. successful\n"); + + if (cache->size > cache_len) { + fprintf(stderr, "MRC cache size overflow\n"); + return 1; + } + + checksum = compute_ip_checksum((void *)&cache->data[0], cache->size); + + if (checksum != cache->checksum) { + fprintf(stderr, "MRC checksum mismatch\n"); + return 1; + } + + fprintf(stderr, "MRC checksum.. verified!\n"); + return 0; +} + +static int do_validate_rec_mrc(int argc, char *argv[]) +{ + char *infile = NULL; + int parse_error = 0; + int fd, i, ret = 1; + uint32_t file_size; + uint8_t *buff; + uint32_t offset = 0; + char *e; + + while (((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) && + !parse_error) { + switch (i) { + case OPT_HELP: + print_help(argc, argv); + return 0; + case OPT_OFFSET: + offset = strtoul(optarg, &e, 0); + if (!*optarg || (e && *e)) { + fprintf(stderr, "Invalid --offset\n"); + parse_error = 1; + } + break; + default: + case '?': + parse_error = 1; + break; + } + } + + if (parse_error) { + print_help(argc, argv); + return 1; + } + + if ((argc - optind) < 1) { + fprintf(stderr, "You must specify an input FILE!\n"); + print_help(argc, argv); + return 1; + } else if ((argc - optind) != 1) { + fprintf(stderr, "Unexpected arguments!\n"); + print_help(argc, argv); + return 1; + } + + infile = argv[optind++]; + + fd = open(infile, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Cannot open %s:%s\n", infile, strerror(errno)); + return 1; + } + + if (futil_map_file(fd, MAP_RO, &buff, &file_size) != FILE_ERR_NONE) { + fprintf(stderr, "Cannot map file %s\n", infile); + close(fd); + return 1; + } + + if (file_size > offset) + ret = verify_checksum((struct mrc_cache *)(buff + offset), + file_size - offset); + else + fprintf(stderr, "Offset greater than file size: offset=0x%x, " + "file size=0x%x\n", offset, file_size); + + if (futil_unmap_file(fd, MAP_RO, buff, file_size) != FILE_ERR_NONE) { + fprintf(stderr, "Failed to unmap file %s\n", infile); + ret = 1; + } + + close(fd); + return ret; +} + +DECLARE_FUTIL_COMMAND(validate_rec_mrc, do_validate_rec_mrc, VBOOT_VERSION_ALL, + "Validates content of Recovery MRC cache"); |