blob: fc4749c426dcc3c04e50b1560f4f1e4d2f9b7863 [file] [log] [blame]
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. 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
* OWNER 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.
*/
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <sys/mman.h>
#include <fmap.h>
#include <valstr.h>
#include "mosys/platform.h"
#include "mosys/alloc.h"
#include "mosys/log.h"
#include "mosys/output.h"
#include "mosys/kv_pair.h"
#include "lib/crypto.h"
#include "lib/eeprom.h"
#include "lib/file.h"
#include "lib/string.h"
#include "lib/string_builder.h"
static int eeprom_enet_info_cmd(struct platform_intf *intf,
struct platform_cmd *cmd, int argc, char **argv)
{
if (!intf->cb->eeprom ||
!intf->cb->eeprom->enet ||
!intf->cb->eeprom->enet->read) {
errno = ENOSYS;
return -1;
}
return intf->cb->eeprom->enet->read(intf, argc, argv);
}
static int eeprom_list_cmd(struct platform_intf *intf,
struct platform_cmd *cmd, int argc, char **argv)
{
struct eeprom *eeprom;
const struct valstr flag_lut[] = {
/* FIXME: this should go in a lib */
{ 1 << EEPROM_RD, "read" },
{ 1 << EEPROM_WR, "write" },
{ 1 << EEPROM_VERBOSE_ONLY, "verbose" },
{ 0, NULL },
};
int i, rc;
if (!intf->cb->eeprom || !intf->cb->eeprom->eeprom_list) {
errno = ENOSYS;
return -1;
}
for (eeprom = intf->cb->eeprom->eeprom_list;
eeprom && eeprom->name;
eeprom++) {
struct kv_pair *kv = kv_pair_new();
unsigned int flags = eeprom->flags;
struct string_builder *str;
kv_pair_add(kv, "name", eeprom->name);
/* FIXME: should this go one level deeper? */
if (eeprom->device) {
int size;
if ((size = eeprom->device->size(intf)) < 0)
return -1;
kv_pair_fmt(kv, "size", "%d", size);
kv_pair_add(kv, "units", "bytes");
}
if (!flags) {
kv_pair_add(kv, "flags", "");
rc = kv_pair_print(kv);
kv_pair_free(kv);
if (rc)
break;
continue;
}
str = new_string_builder();
for (i = 0; i < sizeof(eeprom->flags) * CHAR_BIT; i++) {
if (eeprom->flags & (1 << i)) {
const char *tmp = val2str(1 << i, flag_lut);
string_builder_strncat(str, tmp, strlen(tmp));
flags &= ~(1 << i);
if (flags)
string_builder_add_char(str, ',');
}
}
kv_pair_add(kv, "flags", string_builder_get_string(str));
rc = kv_pair_print(kv);
kv_pair_free(kv);
free_string_builder(str);
if (rc)
break;
}
return rc;
}
/* helper function for printing fmap area information */
static int print_fmap_areas(const char *name, struct fmap *fmap)
{
int i;
for (i = 0; i < fmap->nareas; i++) {
struct kv_pair *kv = kv_pair_new();
const char *str = NULL;
kv_pair_fmt(kv, "name", "%s", name);
kv_pair_fmt(kv, "area_name", "%s", fmap->areas[i].name);
kv_pair_fmt(kv, "area_offset", "0x%08x", fmap->areas[i].offset);
kv_pair_fmt(kv, "area_size", "0x%08x", fmap->areas[i].size);
str = fmap_flags_to_string(fmap->areas[i].flags);
if (str == NULL)
return -1;
kv_pair_fmt(kv, "area_flags", "%s", str);
kv_pair_print(kv);
kv_pair_free(kv);
free((void *)str);
}
return 0;
}
/*
* eeprom_map_cmd_file - helper for printing fmap from ROM image
*
* @intf: platform interface
* @filename: name of file (ROM image)
*
* returns 0 to indicate success
* returns <0 to indicate failure (e.g. file error)
*/
static int eeprom_map_cmd_file(struct platform_intf *intf, char *filename)
{
struct stat s;
uint8_t *blob;
off_t offset;
int rc = -1, errsv = 0, fd;
if ((fd = file_open(filename, FILE_READ)) < 0)
return -1;
if (fstat(fd, &s) < 0) {
errsv = errno;
lprintf(LOG_ERR, "cannot stat \"%s\"\n", filename);
goto eeprom_map_cmd_file_exit_1;
}
blob = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (blob == MAP_FAILED) {
errsv = errno;
lprintf(LOG_ERR, "unable to mmap \"%s\"\n", filename);
goto eeprom_map_cmd_file_exit_1;
}
if ((offset = fmap_find(blob, s.st_size)) < 0) {
lprintf(LOG_ERR, "unable to find fmap\n");
goto eeprom_map_cmd_file_exit_2;
}
rc = print_fmap_areas(filename, (struct fmap *)(blob + offset));
eeprom_map_cmd_file_exit_2:
munmap(blob, s.st_size);
eeprom_map_cmd_file_exit_1:
close(fd);
errno = errsv;
return rc;
}
/*
* eeprom_map_cmd_eeprom - helper for printing fmap of ROMs in the system
*
* @intf: platform interface
* @name: name of ROM/EEPROM for which to print fmap (optional)
*
* returns 0 to indicate success
* returns <0 to indicate failure (e.g. EEPROM not found)
*/
static int eeprom_map_cmd_eeprom(struct platform_intf *intf, char *name)
{
int rc = 0, eeprom_found = 0;
struct eeprom *eeprom;
for (eeprom = intf->cb->eeprom->eeprom_list;
eeprom && eeprom->name;
eeprom++) {
struct fmap *fmap = NULL;
if (name) {
if (!strcmp(eeprom->name, name))
eeprom_found = 1;
else
continue;
}
if (!eeprom->device || !eeprom->device->get_map)
continue;
fmap = eeprom->device->get_map(intf, eeprom);
if (!fmap)
continue;
/* let caller handle errors, allow loop to continue */
rc |= print_fmap_areas(eeprom->name, fmap);
free(fmap);
}
if (name && !eeprom_found) {
errno = ENODEV;
rc = -1;
}
return rc;
}
static int eeprom_map_cmd(struct platform_intf *intf,
struct platform_cmd *cmd, int argc, char **argv)
{
int rc = -1, errsv = 0;
char *name = argv[0];
struct stat s;
/* The simple case: No argument, just print FMAPs from all EEPROMS */
if (!argc) {
rc = eeprom_map_cmd_eeprom(intf, NULL);
goto eeprom_map_cmd_exit;
} else if (argc != 1) {
platform_cmd_usage(cmd);
errsv = EINVAL;
goto eeprom_map_cmd_exit;
}
/*
* The argument can be either a filename or an EEPROM device name.
* Try using it as a filename first since files are generic. If that
* fails, the argument must be a EEPROM present on the machine.
*/
if (stat(name, &s) == 0) {
rc = eeprom_map_cmd_file(intf, name);
errsv = errno;
if (rc < 0) {
lperror(LOG_ERR, "Failed to read flashmap from file "
"\"%s\"", name);
}
goto eeprom_map_cmd_exit;
}
if (intf->cb->eeprom) {
rc = eeprom_map_cmd_eeprom(intf, name);
errsv = errno;
if (rc < 0) {
lperror(LOG_ERR, "Failed to read flashmap from device "
"\"%s\"", name);
}
} else {
errsv = ENOSYS;
}
eeprom_map_cmd_exit:
if (rc < 0)
lprintf(LOG_ERR, "could not read flash map\n");
errno = errsv;
return rc;
}
static int eeprom_csum_cmd(struct platform_intf *intf,
struct platform_cmd *cmd, int argc, char **argv)
{
struct eeprom *eeprom;
struct crypto_algo *crypto = &sha1_algo;
uint8_t *digest = NULL;
char *name;
int fd = 0, digest_len = 0;
int rc = 0;
if (!intf->cb->eeprom || !intf->cb->eeprom->eeprom_list) {
errno = ENOSYS;
return -1;
}
if (argc > 1) {
errno = EINVAL;
return -1;
}
name = argv[0];
if ((fd = file_open(name, FILE_READ)) >= 0) {
struct stat s;
uint8_t *blob;
char *digest_str;
struct kv_pair *kv;
if (fstat(fd, &s) < 0) {
lprintf(LOG_ERR, "cannot stat \"%s\"\n", name);
rc = -1;
goto eeprom_csum_cmd_exit;
}
blob = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (blob == MAP_FAILED) {
lprintf(LOG_ERR, "unable to mmap \"%s\"\n", name);
rc = -1;
close(fd);
goto eeprom_csum_cmd_exit;
}
if ((digest_len = fmap_get_csum(blob, s.st_size, &digest)) < 0){
lprintf(LOG_DEBUG, "fmap_get_csum failed, checksumming "
"entire image\n");
crypto->init(crypto->ctx);
crypto->update(crypto->ctx, blob, s.st_size);
crypto->final(crypto->ctx);
digest = (uint8_t *)crypto->get_digest(crypto);
digest_len = crypto->digest_len;
}
digest_str = buf2str(digest, digest_len);
kv = kv_pair_new();
kv_pair_fmt(kv, "name", name);
kv_pair_fmt(kv, "checksum", digest_str);
kv_pair_print(kv);
kv_pair_free(kv);
free(digest_str);
munmap(blob, s.st_size);
}
for (eeprom = intf->cb->eeprom->eeprom_list;
eeprom && eeprom->name;
eeprom++) {
uint8_t *image = NULL;
int len;
char *digest_str;
struct kv_pair *kv;
if (name && strcmp(eeprom->name, name))
continue;
if (!eeprom->device || !eeprom->device->size ||
!eeprom->device->read)
continue;
if ((len = eeprom->device->size(intf)) < 0) {
lprintf(LOG_DEBUG, "failed to obtain size of %s\n",
eeprom->name);
continue;
}
image = mosys_malloc(len);
if (eeprom->device->read(intf, eeprom, 0, len, image) < 0) {
lprintf(LOG_DEBUG, "failed to read %s\n", eeprom->name);
continue;
}
if ((digest_len = fmap_get_csum(image, len, &digest)) < 0) {
lprintf(LOG_DEBUG, "fmap_get_csum failed, checksumming "
"entire image\n");
crypto->init(crypto->ctx);
crypto->update(crypto->ctx, image, len);
crypto->final(crypto->ctx);
digest = (uint8_t *)crypto->get_digest(crypto);
digest_len = crypto->digest_len;
}
digest_str = buf2str(digest, digest_len);
kv = kv_pair_new();
kv_pair_fmt(kv, "name", eeprom->name);
kv_pair_fmt(kv, "checksum", digest_str);
kv_pair_print(kv);
kv_pair_free(kv);
free(digest_str);
free(image);
}
eeprom_csum_cmd_exit:
return rc;
}
static int eeprom_dump_cmd(struct platform_intf *intf,
struct platform_cmd *cmd, int argc, char **argv)
{
struct eeprom *eeprom;
int rc = 0, fd = -1;
struct stat st;
uint8_t *buf = NULL;
const char *devname, *filename;
int eeprom_size;
if ((argc < 1) || (argc > 2)) {
platform_cmd_usage(cmd);
errno = EINVAL;
return -1;
}
devname = argv[0];
if (argc == 2)
filename = argv[1];
else
filename = NULL;
if (!intf->cb->eeprom || !intf->cb->eeprom->eeprom_list) {
errno = ENOSYS;
return -1;
}
/* find the eeprom to do work on */
for (eeprom = intf->cb->eeprom->eeprom_list;
eeprom && eeprom->name;
eeprom++) {
if (!strcmp(eeprom->name, devname))
break;
}
if (!eeprom->name) {
lprintf(LOG_ERR, "eeprom %s not found\n", devname);
errno = EINVAL;
return -1;
}
if (!eeprom->device->read) {
errno = ENOSYS;
return -1;
}
if (filename != NULL) {
unsigned int filemode = S_IRUSR | S_IWUSR | S_IRGRP;
/* Do not overwrite an existing file */
if (lstat(filename, &st) == 0) {
int errsv = errno;
lprintf(LOG_ERR, "File %s already exists\n", filename);
errno = errsv;
return -1;
}
if ((fd = open(filename, O_CREAT | O_WRONLY, filemode)) < 0) {
int errsv = errno;
lperror(LOG_ERR, "Could not open file %s", filename);
errno = errsv;
return -1;
}
}
/* do the actual work - read from eeprom, print to screen or
* write to file */
if ((eeprom_size = eeprom->device->size(intf)) < 0)
return -1;
buf = mosys_malloc(eeprom_size);
if (eeprom->device->read(intf, eeprom, 0, eeprom_size, buf) < 0) {
lprintf(LOG_ERR, "Unable to read %d bytes from %s\n",
eeprom_size, eeprom->name);
rc = -1;
goto eeprom_dump_done;
}
if (filename != NULL) {
int count;
count = write(fd, buf, eeprom_size);
if (count != eeprom_size) {
lprintf(LOG_ERR, "Unable to write %d bytes to %s\n",
eeprom_size, filename);
rc = -1;
}
} else {
print_buffer(buf, eeprom_size);
}
eeprom_dump_done:
close(fd);
free(buf);
return rc;
}
static int eeprom_write_cmd(struct platform_intf *intf,
struct platform_cmd *cmd, int argc, char **argv)
{
struct eeprom *eeprom;
int rc = 0, fd, errsv = 0;
struct stat st;
uint8_t *buf = NULL;
const char *devname, *filename;
int eeprom_size;
if (argc != 2) {
platform_cmd_usage(cmd);
errno = EINVAL;
return -1;
}
devname = argv[0];
filename = argv[1];
if (!intf->cb->eeprom || !intf->cb->eeprom->eeprom_list) {
errno = ENOSYS;
return -1;
}
/* find the eeprom to do work on */
for (eeprom = intf->cb->eeprom->eeprom_list;
eeprom && eeprom->name;
eeprom++) {
if (!strcmp(eeprom->name, devname))
break;
}
if (!eeprom->name) {
lprintf(LOG_ERR, "eeprom %s not found\n", devname);
return -1;
}
if (!eeprom->device->write) {
errno = ENOSYS;
return -1;
}
/* size sanity checks */
if ((eeprom_size = eeprom->device->size(intf)) < 0)
return -1;
if (lstat(filename, &st) < 0) {
int errsv = errno;
lperror(LOG_ERR, "lstat failure on file %s", filename);
errno = errsv;
return -1;
}
if (st.st_size > eeprom_size) {
lprintf(LOG_ERR, "cannot write %jx bytes to %s eeprom "
"(%u bytes)\n", (intmax_t)st.st_size,
eeprom->name, eeprom_size);
errno = EFBIG;
return -1;
}
if ((fd = open(filename, O_RDONLY)) < 0) {
int errsv = errno;
lperror(LOG_ERR, "Could not open file %s", filename);
errno = errsv;
return -1;
}
/* do the actual work - read from file, write to eeprom */
buf = mosys_malloc(st.st_size);
if (read(fd, buf, st.st_size) != st.st_size) {
errsv = errno;
lperror(LOG_ERR, "Could not read file %s", filename);
rc = -1;
goto eeprom_write_done;
}
if (eeprom->device->write(intf, eeprom, 0,
st.st_size, buf) != st.st_size) {
lprintf(LOG_ERR, "Unable to write %jx bytes to %s\n",
(intmax_t)st.st_size, eeprom->name);
rc = -1;
} else {
mosys_printf("Wrote image to %s\n", eeprom->name);
}
eeprom_write_done:
close(fd);
free(buf);
errno = errsv;
return rc;
}
struct platform_cmd eeprom_enet_cmds[] = {
{
.name = "info",
.desc = "Print information from ethernet controller EEPROM(s)",
.usage = "mosys eeprom enet read [ethN]",
.type = ARG_TYPE_GETTER,
.arg = { .func = eeprom_enet_info_cmd }
},
{ NULL }
};
struct platform_cmd eeprom_cmds[] = {
{
.name = "list",
.desc = "List EEPROMs present in system and some basic info",
.usage = "mosys eeprom list",
.type = ARG_TYPE_GETTER,
.arg = { .func = eeprom_list_cmd }
},
{
.name = "map",
.desc = "Print EEPROM maps if present",
.usage = "mosys eeprom map <eeprom/filename>",
.type = ARG_TYPE_GETTER,
.arg = { .func = eeprom_map_cmd }
},
{
.name = "csum",
.desc = "Print sha1 checksum",
.usage = "mosys eeprom csum <eeprom/filename>",
.type = ARG_TYPE_GETTER,
.arg = { .func = eeprom_csum_cmd }
},
{
.name = "dump",
.desc = "Dump entire contents of EEPROM to file",
.usage = "mosys eeprom dump <device> <file>",
.type = ARG_TYPE_GETTER,
.arg = { .func = eeprom_dump_cmd }
},
{
.name = "write",
.desc = "Write contents of file to EEPROM",
.usage = "mosys eeprom write <device> <file>",
.type = ARG_TYPE_SETTER,
.arg = { .func = eeprom_write_cmd }
},
{
.name = "enet",
.desc = "Ethernet Commands",
.type = ARG_TYPE_SUB,
.arg = { .sub = eeprom_enet_cmds }
},
{ NULL }
};
struct platform_cmd cmd_eeprom = {
.name = "eeprom",
.desc = "EEPROM Information",
.type = ARG_TYPE_SUB,
.arg = { .sub = eeprom_cmds }
};