blob: e7905d1f1c3f58fa0401496132cd93baa0305140 [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 <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include "mosys/log.h"
#include "mosys/kv_pair.h"
#include "mosys/platform.h"
#include "lib/string.h"
#include "lib/vpd.h"
#include "lib/vpd_tables.h"
static int vpd_find_string_cmd(struct platform_intf *intf,
struct platform_cmd *cmd, int argc, char **argv)
{
uint8_t type, num;
char *str;
int rc = 0;
/* this is legacy tool format */
if (argc < 2) {
platform_cmd_usage(cmd);
errno = EINVAL;
return -1;
}
type = (uint8_t) strtoul(argv[0], NULL, 0);
num = (uint8_t) strtoul(argv[1], NULL, 0);
lprintf(LOG_DEBUG, "vpd get %d %d\n", type, num);
/* table offset starts at 0, string offset starts at 1 */
if (num == 0)
return 0; /* legacy tool always returns success */
num--;
/* locate and display specified string */
str = vpd_find_string(intf, type, num, vpd_rom_base, vpd_rom_size);
if (str) {
struct kv_pair *kv = kv_pair_new();
kv_pair_fmt(kv, "string", str);
rc = kv_pair_print(kv);
kv_pair_free(kv);
free(str);
}
return rc; /* the legacy tool always returns success */
}
static int vpd_find_blob_cmd(struct platform_intf *intf,
struct platform_cmd *cmd, int argc, char **argv)
{
errno = ENOSYS;
return -1;
}
static int vpd_print_firmware_cmd(struct platform_intf *intf,
struct platform_cmd *cmd, int argc, char **argv)
{
struct vpd_table table;
struct kv_pair *kv;
int rc;
if (vpd_find_table(intf, VPD_TYPE_FIRMWARE, 0, &table,
vpd_rom_base, vpd_rom_size) < 0)
return 0;
kv = kv_pair_new();
kv_pair_fmt(kv, "table_type", "%d", table.header.type);
kv_pair_add(kv, "vendor", table.string[table.data.firmware.vendor]);
kv_pair_add(kv, "version", table.string[table.data.firmware.version]);
kv_pair_add(kv, "release_date",
table.string[table.data.firmware.release_date]);
kv_pair_fmt(kv, "size", "%u KB",
(table.data.firmware.rom_size_64k_blocks + 1) * 64);
rc = kv_pair_print(kv);
kv_pair_free(kv);
return rc;
}
static int vpd_print_system_cmd(struct platform_intf *intf,
struct platform_cmd *cmd,
int argc, char **argv)
{
struct vpd_table table;
struct kv_pair *kv;
int rc;
if (vpd_find_table(intf, VPD_TYPE_SYSTEM, 0, &table,
vpd_rom_base, vpd_rom_size) < 0)
return 0;
kv = kv_pair_new();
kv_pair_fmt(kv, "table_type", "%d", table.header.type);
kv_pair_add(kv, "manufacturer",
table.string[table.data.system.manufacturer]);
kv_pair_add(kv, "name", table.string[table.data.system.name]);
kv_pair_add(kv, "version", table.string[table.data.system.version]);
kv_pair_add(kv, "serial_number",
table.string[table.data.system.serial_number]);
kv_pair_add(kv, "sku",
table.string[table.data.system.sku_number]);
kv_pair_add(kv, "family",
table.string[table.data.system.family]);
rc = kv_pair_print(kv);
kv_pair_free(kv);
return rc;
}
static int vpd_print_blobs_cmd(struct platform_intf *intf,
struct platform_cmd *cmd,
int argc, char **argv)
{
struct vpd_table table;
struct kv_pair *kv, *blob_kv;
int i, rc;
for (i = 0; i < 0xffff; i++) {
if (vpd_find_table(intf, VPD_TYPE_BINARY_BLOB_POINTER,
i, &table, vpd_rom_base, vpd_rom_size) < 0) {
lprintf(LOG_DEBUG, "cannot find binary blob pointer\n");
break;
}
kv = kv_pair_new();
blob_kv = kv_pair_new();
kv_pair_fmt(kv, "table_type", "%d", table.header.type);
kv_pair_fmt(kv, "handle", "%u", table.header.handle);
kv_pair_add(kv, "vendor",
table.string[table.data.blob.vendor]);
kv_pair_add(kv, "description",
table.string[table.data.blob.description]);
kv_pair_fmt(kv, "offset", "0x%08x", table.data.blob.offset);
kv_pair_fmt(kv, "size", "%d", table.data.blob.size);
vpd_print_blob(intf, blob_kv, &table);
rc = kv_pair_print(kv);
kv_pair_free(kv);
if (rc)
break;
rc = kv_pair_print(blob_kv);
kv_pair_free(blob_kv);
if (rc)
break;
}
return rc;
}
static int vpd_print_all_cmd(struct platform_intf *intf,
struct platform_cmd *cmd,
int argc, char **argv)
{
int rc = 0;
rc |= vpd_print_firmware_cmd(intf, cmd, argc, argv);
rc |= vpd_print_system_cmd(intf, cmd, argc, argv);
rc |= vpd_print_blobs_cmd(intf, cmd, argc, argv);
return rc;
}
static int vpd_dump_blobs_cmd(struct platform_intf *intf,
struct platform_cmd *cmd,
int argc, char **argv)
{
int handle, type, start, end;
if ((argc < 1)) {
platform_cmd_usage(cmd);
errno = EINVAL;
return -1;
}
type = strtol(argv[0], NULL, 0);
if (type != VPD_TYPE_BINARY_BLOB_POINTER) {
errno = EINVAL;
return -1;
}
if (argc == 2) {
handle = strtol(argv[1], NULL, 0);
if ((handle < 0) || (handle > 0xffff) || (errno == ERANGE)) {
lprintf(LOG_ERR, "bad handle: %s\n", argv[1]);
errno = EINVAL;
return -1;
}
start = handle;
end = start + 1;
} else {
start = 0;
end = 0xffff;
}
/* find the VPD structure table entry */
for (handle = start; handle < end; handle++) {
int size, fd = -1;
uint8_t *buf;
struct vpd_table table;
char filename[128]; /* FIXME: use string_builder here */
if (vpd_find_table(intf, type, handle, &table,
vpd_rom_base, vpd_rom_size) < 0) {
lprintf(LOG_DEBUG, "cannot find binary blob pointer\n");
break;
}
size = vpd_get_blob(intf, &table.data.blob, &buf);
sprintf(filename, "%s%d_%d.bin", "type", type, handle);
if ((fd = open(filename,
O_CREAT | O_WRONLY | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP)) < 0) {
int errsv = errno;
lperror(LOG_ERR, "Could not create file \"%s\": %s",
filename, strerror(errno));
free(buf);
errno = errsv;
return -1;
}
if (write(fd, buf, size) != size) {
int errsv = errno;
lperror(LOG_ERR, "Could not write file \"%s\": %s",
filename, strerror(errno));
free(buf);
close(fd);
errno = errsv;
return -1;
}
lprintf(LOG_NOTICE, "Wrote %s\n", filename);
close(fd);
free(buf);
}
return 0;
}
struct platform_cmd vpd_find_cmds[] = {
{
.name = "string",
.desc = "Retrieve a Specified String",
.usage = "<table type> <string number>",
.type = ARG_TYPE_GETTER,
.arg = { .func = vpd_find_string_cmd }
},
{
.name = "blob",
.desc = "Decode Specified Binary Blob",
.usage = "<vendor> <description>",
.type = ARG_TYPE_GETTER,
.arg = { .func = vpd_find_blob_cmd }
},
{ NULL },
};
struct platform_cmd vpd_print_cmds[] = {
{
.name = "all",
.desc = "Print All Tables",
.type = ARG_TYPE_GETTER,
.arg = { .func = vpd_print_all_cmd }
},
{
.name = "firmware",
.desc = "Firmware Information Table",
.type = ARG_TYPE_GETTER,
.arg = { .func = vpd_print_firmware_cmd }
},
{
.name = "system",
.desc = "System Information Table",
.type = ARG_TYPE_GETTER,
.arg = { .func = vpd_print_system_cmd }
},
{
.name = "blobs",
.desc = "Binary Blobs Pointer Tables",
.type = ARG_TYPE_GETTER,
.arg = { .func = vpd_print_blobs_cmd }
},
{ NULL }
};
struct platform_cmd vpd_dump_cmds[] = {
#if 0
{
.name = "all",
.desc = "Print All Tables",
.type = ARG_TYPE_GETTER,
.arg = { .func = vpd_dump_all_cmd }
},
{
.name = "firmware",
.desc = "Firmware Information Table",
.type = ARG_TYPE_GETTER,
.arg = { .func = vpd_dump_firmware_cmd }
},
{
.name = "system",
.desc = "System Information Table",
.type = ARG_TYPE_GETTER,
.arg = { .func = vpd_dump_system_cmd }
},
#endif
{
.name = "blobs",
.desc = "Binary Blobs",
.type = ARG_TYPE_GETTER,
.arg = { .func = vpd_dump_blobs_cmd }
},
{ NULL }
};
struct platform_cmd vpd_cmds[] = {
{
.name = "print",
.desc = "Print Known VPD Tables",
.type = ARG_TYPE_SUB,
.arg = { .sub = vpd_print_cmds }
},
{
.name = "dump",
.desc = "Dump VPD Structure to File",
.usage = "<type>",
.type = ARG_TYPE_SUB,
.arg = { .sub = vpd_dump_cmds }
},
{
.name = "find",
.desc = "Find Specific Information",
.type = ARG_TYPE_SUB,
.arg = { .sub = vpd_find_cmds }
},
{ NULL }
};
struct platform_cmd cmd_vpd = {
.type = ARG_TYPE_SUB,
.name = "vpd",
.desc = "Vital Product Data",
.arg = { .sub = vpd_cmds },
};