blob: 0caa0da74f819c8ef4cdb6be9a6bf93e84c41bb7 [file] [log] [blame]
/*
* This file is part of the flashrom project.
*
* Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
* Copyright (C) 2009 Carl-Daniel Hailfinger
* Copyright (C) 2011-2013 Stefan Tauner
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that 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.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#if HAVE_UTSNAME == 1
#include <sys/utsname.h>
#endif
#if IS_WINDOWS
#include <windows.h>
#undef min
#undef max
#endif
#include "flash.h"
#include "programmer.h"
static const char *test_state_to_text(enum test_state test_state)
{
switch (test_state) {
case OK: return "OK";
case BAD: return "Not working";
case NA: return "N/A";
case DEP: return "Config-dependent";
case NT:
default: return "Untested";
}
}
static int print_supported_chips(void)
{
const char *delim = "/";
const int mintoklen = 5;
const int border = 2;
int i, chipcount = 0;
int maxvendorlen = strlen("Vendor") + 1;
int maxchiplen = strlen("Device") + 1;
int maxtypelen = strlen("Type") + 1;
const struct flashchip *chip;
char *s;
char *ven, *dev;
char *tmpven, *tmpdev, *tmpven_save, *tmpdev_save;
int tmpvenlen, tmpdevlen, curvenlen, curdevlen;
/* calculate maximum column widths and by iterating over all chips */
for (chip = flashchips; chip->name != NULL; chip++) {
/* Ignore generic entries. */
if (!strncmp(chip->vendor, "Unknown", 7) ||
!strncmp(chip->vendor, "Programmer", 10) ||
!strncmp(chip->name, "unknown", 7))
continue;
chipcount++;
/* Find maximum vendor length (respecting line splitting). */
tmpven = (char *)chip->vendor;
do {
/* and take minimum token lengths into account */
tmpvenlen = 0;
do {
tmpvenlen += strcspn(tmpven, delim);
/* skip to the address after the first token */
tmpven += tmpvenlen;
if (tmpven[0] == '\0')
break;
tmpven++;
} while (tmpvenlen < mintoklen);
maxvendorlen = max(maxvendorlen, tmpvenlen);
if (tmpven[0] == '\0')
break;
} while (1);
/* same for device name */
tmpdev = (char *)chip->name;
do {
tmpdevlen = 0;
do {
tmpdevlen += strcspn(tmpdev, delim);
tmpdev += tmpdevlen;
if (tmpdev[0] == '\0')
break;
tmpdev++;
} while (tmpdevlen < mintoklen);
maxchiplen = max(maxchiplen, tmpdevlen);
if (tmpdev[0] == '\0')
break;
} while (1);
s = flashbuses_to_text(chip->bustype);
if (s == NULL) {
msg_gerr("Out of memory!\n");
return 1;
}
maxtypelen = max(maxtypelen, strlen(s));
free(s);
}
maxvendorlen += border;
maxchiplen += border;
maxtypelen += border;
msg_ginfo("Supported flash chips (total: %d):\n\n", chipcount);
msg_ginfo("Vendor");
for (i = strlen("Vendor"); i < maxvendorlen; i++)
msg_ginfo(" ");
msg_ginfo("Device");
for (i = strlen("Device"); i < maxchiplen; i++)
msg_ginfo(" ");
msg_ginfo("Test");
for (i = 0; i < border; i++)
msg_ginfo(" ");
msg_ginfo("Known");
for (i = 0; i < border; i++)
msg_ginfo(" ");
msg_ginfo(" Size ");
for (i = 0; i < border; i++)
msg_ginfo(" ");
msg_ginfo("Type");
for (i = strlen("Type"); i < maxtypelen; i++)
msg_ginfo(" ");
msg_gdbg("Voltage");
msg_ginfo("\n");
for (i = 0; i < maxvendorlen + maxchiplen; i++)
msg_ginfo(" ");
msg_ginfo("OK ");
for (i = 0; i < border; i++)
msg_ginfo(" ");
msg_ginfo("Broken");
for (i = 0; i < border; i++)
msg_ginfo(" ");
msg_ginfo("[kB] ");
for (i = 0; i < border + maxtypelen; i++)
msg_ginfo(" ");
msg_gdbg("range [V]");
msg_ginfo("\n\n");
msg_ginfo("(P = PROBE, R = READ, E = ERASE, W = WRITE, - = N/A)\n\n");
for (chip = flashchips; chip->name != NULL; chip++) {
/* Don't print generic entries. */
if (!strncmp(chip->vendor, "Unknown", 7) ||
!strncmp(chip->vendor, "Programmer", 10) ||
!strncmp(chip->name, "unknown", 7))
continue;
/* support for multiline vendor names:
* - make a copy of the original vendor name
* - use strok to put the first token in tmpven
* - keep track of the length of all tokens on the current line
* for ' '-padding in curvenlen
* - check if additional tokens should be printed on the current
* line
* - after all other values are printed print the surplus tokens
* on fresh lines
*/
ven = malloc(strlen(chip->vendor) + 1);
if (ven == NULL) {
msg_gerr("Out of memory!\n");
return 1;
}
strcpy(ven, chip->vendor);
tmpven = strtok_r(ven, delim, &tmpven_save);
msg_ginfo("%s", tmpven);
curvenlen = strlen(tmpven);
while ((tmpven = strtok_r(NULL, delim, &tmpven_save)) != NULL) {
msg_ginfo("%s", delim);
curvenlen++;
tmpvenlen = strlen(tmpven);
if (tmpvenlen >= mintoklen)
break; /* big enough to be on its own line */
msg_ginfo("%s", tmpven);
curvenlen += tmpvenlen;
}
for (i = curvenlen; i < maxvendorlen; i++)
msg_ginfo(" ");
/* support for multiline device names as above */
dev = malloc(strlen(chip->name) + 1);
if (dev == NULL) {
msg_gerr("Out of memory!\n");
free(ven);
return 1;
}
strcpy(dev, chip->name);
tmpdev = strtok_r(dev, delim, &tmpdev_save);
msg_ginfo("%s", tmpdev);
curdevlen = strlen(tmpdev);
while ((tmpdev = strtok_r(NULL, delim, &tmpdev_save)) != NULL) {
msg_ginfo("%s", delim);
curdevlen++;
tmpdevlen = strlen(tmpdev);
if (tmpdevlen >= mintoklen)
break; /* big enough to be on its own line */
msg_ginfo("%s", tmpdev);
curdevlen += tmpdevlen;
}
for (i = curdevlen; i < maxchiplen; i++)
msg_ginfo(" ");
if (chip->tested.probe == OK)
msg_ginfo("P");
else if (chip->tested.probe == NA)
msg_ginfo("-");
else
msg_ginfo(" ");
if (chip->tested.read == OK)
msg_ginfo("R");
else if (chip->tested.read == NA)
msg_ginfo("-");
else
msg_ginfo(" ");
if (chip->tested.erase == OK)
msg_ginfo("E");
else if (chip->tested.erase == NA)
msg_ginfo("-");
else
msg_ginfo(" ");
if (chip->tested.write == OK)
msg_ginfo("W");
else if (chip->tested.write == NA)
msg_ginfo("-");
else
msg_ginfo(" ");
for (i = 0; i < border; i++)
msg_ginfo(" ");
if (chip->tested.probe == BAD)
msg_ginfo("P");
else
msg_ginfo(" ");
if (chip->tested.read == BAD)
msg_ginfo("R");
else
msg_ginfo(" ");
if (chip->tested.erase == BAD)
msg_ginfo("E");
else
msg_ginfo(" ");
if (chip->tested.write == BAD)
msg_ginfo("W");
else
msg_ginfo(" ");
for (i = 0; i < border + 1; i++)
msg_ginfo(" ");
msg_ginfo("%6d", chip->total_size);
for (i = 0; i < border; i++)
msg_ginfo(" ");
s = flashbuses_to_text(chip->bustype);
if (s == NULL) {
msg_gerr("Out of memory!\n");
free(ven);
free(dev);
return 1;
}
msg_ginfo("%s", s);
for (i = strlen(s); i < maxtypelen; i++)
msg_ginfo(" ");
free(s);
if (chip->voltage.min == 0 && chip->voltage.max == 0)
msg_gdbg("no info");
else
msg_gdbg("%0.02f;%0.02f",
chip->voltage.min/(double)1000,
chip->voltage.max/(double)1000);
/* print surplus vendor and device name tokens */
while (tmpven != NULL || tmpdev != NULL) {
msg_ginfo("\n");
if (tmpven != NULL){
msg_ginfo("%s", tmpven);
curvenlen = strlen(tmpven);
while ((tmpven = strtok_r(NULL, delim, &tmpven_save)) != NULL) {
msg_ginfo("%s", delim);
curvenlen++;
tmpvenlen = strlen(tmpven);
/* big enough to be on its own line */
if (tmpvenlen >= mintoklen)
break;
msg_ginfo("%s", tmpven);
curvenlen += tmpvenlen;
}
} else
curvenlen = 0;
for (i = curvenlen; i < maxvendorlen; i++)
msg_ginfo(" ");
if (tmpdev != NULL){
msg_ginfo("%s", tmpdev);
curdevlen = strlen(tmpdev);
while ((tmpdev = strtok_r(NULL, delim, &tmpdev_save)) != NULL) {
msg_ginfo("%s", delim);
curdevlen++;
tmpdevlen = strlen(tmpdev);
/* big enough to be on its own line */
if (tmpdevlen >= mintoklen)
break;
msg_ginfo("%s", tmpdev);
curdevlen += tmpdevlen;
}
}
}
msg_ginfo("\n");
free(ven);
free(dev);
}
return 0;
}
#if CONFIG_INTERNAL == 1
static void print_supported_chipsets(void)
{
unsigned int i, chipsetcount = 0;
const struct penable *c = chipset_enables;
size_t maxvendorlen = strlen("Vendor") + 1;
size_t maxchipsetlen = strlen("Chipset") + 1;
for (c = chipset_enables; c->vendor_name != NULL; c++) {
chipsetcount++;
maxvendorlen = MAX(maxvendorlen, strlen(c->vendor_name));
maxchipsetlen = MAX(maxchipsetlen, strlen(c->device_name));
}
maxvendorlen++;
maxchipsetlen++;
msg_ginfo("Supported chipsets (total: %u):\n\n", chipsetcount);
msg_ginfo("Vendor");
for (i = strlen("Vendor"); i < maxvendorlen; i++)
msg_ginfo(" ");
msg_ginfo("Chipset");
for (i = strlen("Chipset"); i < maxchipsetlen; i++)
msg_ginfo(" ");
msg_ginfo("PCI IDs Status\n\n");
for (c = chipset_enables; c->vendor_name != NULL; c++) {
msg_ginfo("%s", c->vendor_name);
for (i = 0; i < maxvendorlen - strlen(c->vendor_name); i++)
msg_ginfo(" ");
msg_ginfo("%s", c->device_name);
for (i = 0; i < maxchipsetlen - strlen(c->device_name); i++)
msg_ginfo(" ");
msg_ginfo("%04x:%04x %s\n", c->vendor_id, c->device_id,
test_state_to_text(c->status));
}
}
static void print_supported_boards_helper(const struct board_info *boards,
const char *devicetype)
{
unsigned int i;
unsigned int boardcount_good = 0, boardcount_bad = 0, boardcount_nt = 0;
const struct board_match *e = board_matches;
const struct board_info *b = boards;
size_t maxvendorlen = strlen("Vendor") + 1;
size_t maxboardlen = strlen("Board") + 1;
for (b = boards; b->vendor != NULL; b++) {
maxvendorlen = max(maxvendorlen, strlen(b->vendor));
maxboardlen = max(maxboardlen, strlen(b->name));
if (b->working == OK)
boardcount_good++;
else if (b->working == NT)
boardcount_nt++;
else
boardcount_bad++;
}
maxvendorlen++;
maxboardlen++;
msg_ginfo("%d known %s (good: %d, untested: %d, bad: %d):\n\n",
boardcount_good + boardcount_nt + boardcount_bad,
devicetype, boardcount_good, boardcount_nt, boardcount_bad);
msg_ginfo("Vendor");
for (i = strlen("Vendor"); i < maxvendorlen; i++)
msg_ginfo(" ");
msg_ginfo("Board");
for (i = strlen("Board"); i < maxboardlen; i++)
msg_ginfo(" ");
msg_ginfo("Status Required value for\n");
for (i = 0; i < maxvendorlen + maxboardlen + strlen("Status "); i++)
msg_ginfo(" ");
msg_ginfo("-p internal:mainboard=\n");
for (b = boards; b->vendor != NULL; b++) {
msg_ginfo("%s", b->vendor);
for (i = 0; i < maxvendorlen - strlen(b->vendor); i++)
msg_ginfo(" ");
msg_ginfo("%s", b->name);
for (i = 0; i < maxboardlen - strlen(b->name); i++)
msg_ginfo(" ");
switch (b->working) {
case OK: msg_ginfo("OK "); break;
case NT: msg_ginfo("NT "); break;
case DEP: msg_ginfo("DEP "); break;
case NA: msg_ginfo("N/A "); break;
case BAD:
default: msg_ginfo("BAD "); break;
}
for (e = board_matches; e->vendor_name != NULL; e++) {
if (strcmp(e->vendor_name, b->vendor)
|| strcmp(e->board_name, b->name))
continue;
if (e->lb_vendor == NULL)
msg_ginfo("(autodetected)");
else
msg_ginfo("%s:%s", e->lb_vendor,
e->lb_part);
}
msg_ginfo("\n");
}
}
#endif
static void print_supported_devs(const struct programmer_entry *const prog, const char *const type)
{
const struct dev_entry *const devs = prog->devs.dev;
msg_ginfo("\nSupported %s devices for the %s programmer:\n", type, prog->name);
unsigned int maxvendorlen = strlen("Vendor") + 1;
unsigned int maxdevlen = strlen("Device") + 1;
unsigned int i;
for (i = 0; devs[i].vendor_name != NULL; i++) {
maxvendorlen = max(maxvendorlen, strlen(devs[i].vendor_name));
maxdevlen = max(maxdevlen, strlen(devs[i].device_name));
}
maxvendorlen++;
maxdevlen++;
msg_ginfo("Vendor");
for (i = strlen("Vendor"); i < maxvendorlen; i++)
msg_ginfo(" ");
msg_ginfo("Device");
for (i = strlen("Device"); i < maxdevlen; i++)
msg_ginfo(" ");
msg_ginfo(" %s IDs Status\n", type);
for (i = 0; devs[i].vendor_name != NULL; i++) {
msg_ginfo("%s", devs[i].vendor_name);
unsigned int j;
for (j = strlen(devs[i].vendor_name); j < maxvendorlen; j++)
msg_ginfo(" ");
msg_ginfo("%s", devs[i].device_name);
for (j = strlen(devs[i].device_name); j < maxdevlen; j++)
msg_ginfo(" ");
msg_pinfo(" %04x:%04x %s\n", devs[i].vendor_id, devs[i].device_id,
test_state_to_text(devs[i].status));
}
}
int print_supported(void)
{
unsigned int i;
if (print_supported_chips())
return 1;
msg_ginfo("\nSupported programmers:\n");
list_programmers_linebreak(0, 80, 0);
msg_ginfo("\n");
#if CONFIG_INTERNAL == 1
msg_ginfo("\nSupported devices for the internal programmer:\n\n");
print_supported_chipsets();
msg_ginfo("\n");
print_supported_boards_helper(boards_known, "mainboards");
msg_ginfo("\n");
print_supported_boards_helper(laptops_known, "mobile devices");
#endif
for (i = 0; i < programmer_table_size; i++) {
const struct programmer_entry *const prog = programmer_table[i];
switch (prog->type) {
case USB:
print_supported_devs(prog, "USB");
break;
case PCI:
print_supported_devs(prog, "PCI");
break;
case OTHER:
if (prog->devs.note != NULL) {
msg_ginfo("\nSupported devices for the %s programmer:\n", prog->name);
msg_ginfo("%s", prog->devs.note);
}
break;
default:
msg_gerr("\n%s: %s: Uninitialized programmer type! Please report a bug at "
"flashrom@flashrom.org\n", __func__, prog->name);
break;
}
}
return 0;
}
static void print_sysinfo(void)
{
#if IS_WINDOWS
SYSTEM_INFO si = { 0 };
OSVERSIONINFOEX osvi = { 0 };
msg_ginfo(" on Windows");
/* Tell Windows which version of the structure we want. */
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (GetVersionEx((OSVERSIONINFO*) &osvi))
msg_ginfo(" %lu.%lu", (unsigned long)osvi.dwMajorVersion,
(unsigned long)osvi.dwMinorVersion);
else
msg_ginfo(" unknown version");
GetSystemInfo(&si);
switch (si.wProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_AMD64:
msg_ginfo(" (x86_64)");
break;
case PROCESSOR_ARCHITECTURE_INTEL:
msg_ginfo(" (x86)");
break;
default:
msg_ginfo(" (unknown arch)");
break;
}
#elif HAVE_UTSNAME == 1
struct utsname osinfo;
uname(&osinfo);
msg_ginfo(" on %s %s (%s)", osinfo.sysname, osinfo.release,
osinfo.machine);
#else
msg_ginfo(" on unknown machine");
#endif
}
void print_buildinfo(void)
{
msg_gdbg("flashrom was built with");
#ifdef __clang__
msg_gdbg(" LLVM Clang");
#ifdef __clang_version__
msg_gdbg(" %s,", __clang_version__);
#else
msg_gdbg(" unknown version (before r102686),");
#endif
#elif defined(__GNUC__)
msg_gdbg(" GCC");
#ifdef __VERSION__
msg_gdbg(" %s,", __VERSION__);
#else
msg_gdbg(" unknown version,");
#endif
#else
msg_gdbg(" unknown compiler,");
#endif
#if defined (__FLASHROM_LITTLE_ENDIAN__)
msg_gdbg(" little endian");
#elif defined (__FLASHROM_BIG_ENDIAN__)
msg_gdbg(" big endian");
#else
#error Endianness could not be determined
#endif
msg_gdbg("\n");
}
void print_version(void)
{
msg_ginfo("flashrom %s", flashrom_version_info());
print_sysinfo();
msg_ginfo("\n");
}
void print_banner(void)
{
msg_ginfo("flashrom is free software, get the source code at "
"https://flashrom.org\n");
msg_ginfo("\n");
}