blob: 7c1ed74f67dfb51161290bb6bde9b263039cc73c [file] [log] [blame]
/* Copyright 2013 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 <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <stdbool.h>
#include "battery.h"
#include "comm-host.h"
#include "chipset.h"
#include "compile_time_macros.h"
#include "cros_ec_dev.h"
#include "ec_panicinfo.h"
#include "ec_flash.h"
#include "ec_version.h"
#include "ectool.h"
#include "i2c.h"
#include "lightbar.h"
#include "lock/gec_lock.h"
#include "misc_util.h"
#include "panic.h"
#include "usb_pd.h"
/* Maximum flash size (16 MB, conservative) */
#define MAX_FLASH_SIZE 0x1000000
/*
* Calculate the expected response for a hello ec command.
*/
#define HELLO_RESP(in_data) ((in_data) + 0x01020304)
/* Command line options */
enum {
OPT_DEV = 1000,
OPT_INTERFACE,
OPT_NAME,
OPT_ASCII,
OPT_I2C_BUS,
};
static struct option long_opts[] = {
{"dev", 1, 0, OPT_DEV},
{"interface", 1, 0, OPT_INTERFACE},
{"name", 1, 0, OPT_NAME},
{"ascii", 0, 0, OPT_ASCII},
{"i2c_bus", 1, 0, OPT_I2C_BUS},
{NULL, 0, 0, 0}
};
#define GEC_LOCK_TIMEOUT_SECS 30 /* 30 secs */
const char help_str[] =
"Commands:\n"
" adcread <channel>\n"
" Read an ADC channel.\n"
" addentropy [reset]\n"
" Add entropy to device secret\n"
" apreset\n"
" Issue AP reset\n"
" autofanctrl <on>\n"
" Turn on automatic fan speed control.\n"
" backlight <enabled>\n"
" Enable/disable LCD backlight\n"
" battery\n"
" Prints battery info\n"
" batterycutoff [at-shutdown]\n"
" Cut off battery output power\n"
" batteryparam\n"
" Read or write board-specific battery parameter\n"
" boardversion\n"
" Prints the board version\n"
" button [vup|vdown|rec] <Delay-ms>\n"
" Simulates button press.\n"
" cbi\n"
" Get/Set/Remove Cros Board Info\n"
" chargecurrentlimit\n"
" Set the maximum battery charging current\n"
" chargecontrol\n"
" Force the battery to stop charging or discharge\n"
" chargeoverride\n"
" Overrides charge port selection logic\n"
" chargestate\n"
" Handle commands related to charge state v2 (and later)\n"
" chipinfo\n"
" Prints chip info\n"
" cmdversions <cmd>\n"
" Prints supported version mask for a command number\n"
" console\n"
" Prints the last output to the EC debug console\n"
" cec\n"
" Read or write CEC messages and settings\n"
" echash [CMDS]\n"
" Various EC hash commands\n"
" eventclear <mask>\n"
" Clears EC host events flags where mask has bits set\n"
" eventclearb <mask>\n"
" Clears EC host events flags copy B where mask has bits set\n"
" eventget\n"
" Prints raw EC host event flags\n"
" eventgetb\n"
" Prints raw EC host event flags copy B\n"
" eventgetscimask\n"
" Prints SCI mask for EC host events\n"
" eventgetsmimask\n"
" Prints SMI mask for EC host events\n"
" eventgetwakemask\n"
" Prints wake mask for EC host events\n"
" eventsetscimask <mask>\n"
" Sets the SCI mask for EC host events\n"
" eventsetsmimask <mask>\n"
" Sets the SMI mask for EC host events\n"
" eventsetwakemask <mask>\n"
" Sets the wake mask for EC host events\n"
" extpwrlimit\n"
" Set the maximum external power limit\n"
" fanduty <percent>\n"
" Forces the fan PWM to a constant duty cycle\n"
" flasherase <offset> <size>\n"
" Erases EC flash\n"
" flasheraseasync <offset> <size>\n"
" Erases EC flash asynchronously\n"
" flashinfo\n"
" Prints information on the EC flash\n"
" flashspiinfo\n"
" Prints information on EC SPI flash, if present\n"
" flashpd <dev_id> <port> <filename>\n"
" Flash commands over PD\n"
" flashprotect [now] [enable | disable]\n"
" Prints or sets EC flash protection state\n"
" flashread <offset> <size> <outfile>\n"
" Reads from EC flash to a file\n"
" flashwrite <offset> <infile>\n"
" Writes to EC flash from a file\n"
" forcelidopen <enable>\n"
" Forces the lid switch to open position\n"
" fpcontext\n"
" Sets the fingerprint sensor context\n"
" fpencstatus\n"
" Prints status of Fingerprint sensor encryption engine\n"
" fpframe\n"
" Retrieve the finger image as a PGM image\n"
" fpinfo\n"
" Prints information about the Fingerprint sensor\n"
" fpmode [capture|deepsleep|fingerdown|fingerup]\n"
" Configure/Read the fingerprint sensor current mode\n"
" fpseed\n"
" Sets the value of the TPM seed.\n"
" fpstats\n"
" Prints timing statisitcs relating to capture and matching\n"
" fptemplate [<infile>|<index 0..2>]\n"
" Add a template if <infile> is provided, else dump it\n"
" gpioget <GPIO name>\n"
" Get the value of GPIO signal\n"
" gpioset <GPIO name>\n"
" Set the value of GPIO signal\n"
" hangdetect <flags> <event_msec> <reboot_msec> | stop | start\n"
" Configure or start/stop the hang detect timer\n"
" hello\n"
" Checks for basic communication with EC\n"
" hibdelay [sec]\n"
" Set the delay before going into hibernation\n"
" hostsleepstate\n"
" Report host sleep state to the EC\n"
" hostevent\n"
" Get & set host event masks.\n"
" i2cprotect <port> [status]\n"
" Protect EC's I2C bus\n"
" i2cread\n"
" Read I2C bus\n"
" i2cwrite\n"
" Write I2C bus\n"
" i2cxfer <port> <slave_addr> <read_count> [write bytes...]\n"
" Perform I2C transfer on EC's I2C bus\n"
" infopddev <port>\n"
" Get info about USB type-C accessory attached to port\n"
" inventory\n"
" Return the list of supported features\n"
" kbfactorytest\n"
" Scan out keyboard if any pins are shorted\n"
" kbid\n"
" Get keyboard ID of supported keyboards\n"
" kbinfo\n"
" Dump keyboard matrix dimensions\n"
" kbpress\n"
" Simulate key press\n"
" keyscan <beat_us> <filename>\n"
" Test low-level key scanning\n"
" led <name> <query | auto | off | <color> | <color>=<value>...>\n"
" Set the color of an LED or query brightness range\n"
" lightbar [CMDS]\n"
" Various lightbar control commands\n"
" mkbpget <buttons|switches>\n"
" Get MKBP buttons/switches supported mask and current state\n"
" mkbpwakemask <get|set> <event|hostevent> [mask]\n"
" Get or Set the MKBP event wake mask, or host event wake mask\n"
" motionsense [CMDS]\n"
" Various motion sense control commands\n"
" panicinfo\n"
" Prints saved panic info\n"
" pause_in_s5 [on|off]\n"
" Whether or not the AP should pause in S5 on shutdown\n"
" pchg [<port>]\n"
" Get peripheral charge port count and status\n"
" pdcontrol [suspend|resume|reset|disable|on]\n"
" Controls the PD chip\n"
" pdchipinfo <port>\n"
" Get PD chip information\n"
" pdlog\n"
" Prints the PD event log entries\n"
" pdwritelog <type> <port>\n"
" Writes a PD event log of the given <type>\n"
" pdgetmode <port>\n"
" Get All USB-PD alternate SVIDs and modes on <port>\n"
" pdsetmode <port> <svid> <opos>\n"
" Set USB-PD alternate SVID and mode on <port>\n"
" port80flood\n"
" Rapidly write bytes to port 80\n"
" port80read\n"
" Print history of port 80 write\n"
" powerinfo\n"
" Prints power-related information\n"
" protoinfo\n"
" Prints EC host protocol information\n"
" pse\n"
" Get and set PoE PSE port power status\n"
" pstoreinfo\n"
" Prints information on the EC host persistent storage\n"
" pstoreread <offset> <size> <outfile>\n"
" Reads from EC host persistent storage to a file\n"
" pstorewrite <offset> <infile>\n"
" Writes to EC host persistent storage from a file\n"
" pwmgetfanrpm [<index> | all]\n"
" Prints current fan RPM\n"
" pwmgetkblight\n"
" Prints current keyboard backlight percent\n"
" pwmgetnumfans\n"
" Prints the number of fans present\n"
" pwmgetduty\n"
" Prints the current 16 bit duty cycle for given PWM\n"
" pwmsetfanrpm <targetrpm>\n"
" Set target fan RPM\n"
" pwmsetkblight <percent>\n"
" Set keyboard backlight in percent\n"
" pwmsetduty\n"
" Set 16 bit duty cycle of given PWM\n"
" rand <num_bytes>\n"
" generate <num_bytes> of random numbers\n"
" readtest <patternoffset> <size>\n"
" Reads a pattern from the EC via LPC\n"
" reboot_ec <RO|RW|cold|hibernate|hibernate-clear-ap-off|disable-jump|cold-ap-off>"
" [at-shutdown|switch-slot]\n"
" Reboot EC to RO or RW\n"
" reboot_ap_on_g3 [<delay>]\n"
" Requests that the EC will automatically reboot the AP after a\n"
" configurable number of seconds the next time we enter the G3\n"
" power state.\n"
" rollbackinfo\n"
" Print rollback block information\n"
" rtcget\n"
" Print real-time clock\n"
" rtcgetalarm\n"
" Print # of seconds before real-time clock alarm goes off.\n"
" rtcset <time>\n"
" Set real-time clock\n"
" rtcsetalarm <sec>\n"
" Set real-time clock alarm to go off in <sec> seconds\n"
" rwhashpd <dev_id> <HASH[0] ... <HASH[4]>\n"
" Set entry in PD MCU's device rw_hash table.\n"
" rwsig <info|dump|action|status> ...\n"
" info: get all info about rwsig\n"
" dump: show individual rwsig field\n"
" action: Control the behavior of RWSIG task.\n"
" status: Run RW signature verification and get status.\n{"
" rwsigaction (DEPRECATED; use \"rwsig action\")\n"
" Control the behavior of RWSIG task.\n"
" rwsigstatus (DEPRECATED; use \"rwsig status\"\n"
" Run RW signature verification and get status.\n"
" sertest\n"
" Serial output test for COM2\n"
" smartdischarge\n"
" Set/Get smart discharge parameters\n"
" stress [reboot] [help]\n"
" Stress test the ec host command interface.\n"
" sysinfo [flags|reset_flags|firmware_copy]\n"
" Display system info.\n"
" switches\n"
" Prints current EC switch positions\n"
" temps <sensorid>\n"
" Print temperature.\n"
" tempsinfo <sensorid>\n"
" Print temperature sensor info.\n"
" thermalget <platform-specific args>\n"
" Get the threshold temperature values from the thermal engine.\n"
" thermalset <platform-specific args>\n"
" Set the threshold temperature values for the thermal engine.\n"
" tpselftest\n"
" Run touchpad self test.\n"
" tpframeget\n"
" Get touchpad frame data.\n"
" tmp006cal <tmp006_index> [params...]\n"
" Get/set TMP006 calibration\n"
" tmp006raw <tmp006_index>\n"
" Get raw TMP006 data\n"
" typeccontrol <port> <command>\n"
" Control USB PD policy\n"
" typecdiscovery <port> <type>\n"
" Get discovery information for port and type\n"
" typecstatus <port>\n"
" Get status information for port\n"
" uptimeinfo\n"
" Get info about how long the EC has been running and the most\n"
" recent AP resets\n"
" usbchargemode <port> <mode> [<inhibit_charge>]\n"
" Set USB charging mode\n"
" usbmux <mux>\n"
" Set USB mux switch state\n"
" usbpd <port> <auto | "
"[toggle|toggle-off|sink|source] [none|usb|dp|dock] "
"[dr_swap|pr_swap|vconn_swap]>\n"
" Control USB PD/type-C [deprecated]\n"
" usbpdmuxinfo\n"
" Get USB-C SS mux info\n"
" usbpdpower [port]\n"
" Get USB PD power information\n"
" version\n"
" Prints EC version\n"
" waitevent <type> [<timeout>]\n"
" Wait for the MKBP event of type and display it\n"
" wireless <flags> [<mask> [<suspend_flags> <suspend_mask>]]\n"
" Enable/disable WLAN/Bluetooth radio\n"
"";
/* Note: depends on enum ec_image */
static const char * const image_names[] = {"unknown", "RO", "RW"};
/* Note: depends on enum ec_led_colors */
static const char * const led_color_names[] = {
"red", "green", "blue", "yellow", "white", "amber"};
BUILD_ASSERT(ARRAY_SIZE(led_color_names) == EC_LED_COLOR_COUNT);
/* Note: depends on enum ec_led_id */
static const char * const led_names[] = {
"battery", "power", "adapter", "left", "right", "recovery_hwreinit",
"sysrq debug" };
BUILD_ASSERT(ARRAY_SIZE(led_names) == EC_LED_ID_COUNT);
/* ASCII mode for printing, default off */
static int ascii_mode = 0;
/* Check SBS numerical value range */
int is_battery_range(int val)
{
return (val >= 0 && val <= 65535) ? 1 : 0;
}
int parse_bool(const char *s, int *dest)
{
if (!strcasecmp(s, "off") || !strncasecmp(s, "dis", 3) ||
tolower(*s) == 'f' || tolower(*s) == 'n') {
*dest = 0;
return 1;
} else if (!strcasecmp(s, "on") || !strncasecmp(s, "ena", 3) ||
tolower(*s) == 't' || tolower(*s) == 'y') {
*dest = 1;
return 1;
} else {
return 0;
}
}
void print_help(const char *prog, int print_cmds)
{
printf("Usage: %s [--dev=n] [--interface=dev|i2c|lpc] [--i2c_bus=n]",
prog);
printf("[--name=cros_ec|cros_fp|cros_pd|cros_scp|cros_ish] [--ascii] ");
printf("<command> [params]\n\n");
printf(" --i2c_bus=n Specifies the number of an I2C bus to use. For\n"
" example, to use /dev/i2c-7, pass --i2c_bus=7.\n"
" Implies --interface=i2c.\n\n");
if (print_cmds)
puts(help_str);
else
printf("Use '%s help' to print a list of commands.\n", prog);
}
static uint8_t read_mapped_mem8(uint8_t offset)
{
int ret;
uint8_t val;
ret = ec_readmem(offset, sizeof(val), &val);
if (ret <= 0) {
fprintf(stderr, "failure in %s(): %d\n", __func__, ret);
exit(1);
}
return val;
}
static uint16_t read_mapped_mem16(uint8_t offset)
{
int ret;
uint16_t val;
ret = ec_readmem(offset, sizeof(val), &val);
if (ret <= 0) {
fprintf(stderr, "failure in %s(): %d\n", __func__, ret);
exit(1);
}
return val;
}
static uint32_t read_mapped_mem32(uint8_t offset)
{
int ret;
uint32_t val;
ret = ec_readmem(offset, sizeof(val), &val);
if (ret <= 0) {
fprintf(stderr, "failure in %s(): %d\n", __func__, ret);
exit(1);
}
return val;
}
static int read_mapped_string(uint8_t offset, char *buffer, int max_size)
{
int ret;
ret = ec_readmem(offset, max_size, buffer);
if (ret <= 0) {
fprintf(stderr, "failure in %s(): %d\n", __func__, ret);
exit(1);
}
return ret;
}
int cmd_adc_read(int argc, char *argv[])
{
char *e;
struct ec_params_adc_read p;
struct ec_response_adc_read r;
int rv;
if (argc < 2) {
fprintf(stderr, "Usage: %s <adc channel>\n", argv[0]);
return -1;
}
p.adc_channel = (uint8_t)strtoull(argv[1], &e, 0);
if (e && *e) {
fprintf(stderr, "\"%s\": invalid channel!\n", argv[1]);
return -1;
}
rv = ec_command(EC_CMD_ADC_READ, 0, &p, sizeof(p), &r, sizeof(r));
if (rv > 0) {
printf("%s: %d\n", argv[1], r.adc_value);
return 0;
}
return rv;
}
int cmd_add_entropy(int argc, char *argv[])
{
struct ec_params_rollback_add_entropy p;
int rv;
int tries = 100; /* Wait for 10 seconds at most */
if (argc >= 2 && !strcmp(argv[1], "reset"))
p.action = ADD_ENTROPY_RESET_ASYNC;
else
p.action = ADD_ENTROPY_ASYNC;
rv = ec_command(EC_CMD_ADD_ENTROPY, 0, &p, sizeof(p), NULL, 0);
if (rv != EC_RES_SUCCESS)
goto out;
while (tries--) {
usleep(100000);
p.action = ADD_ENTROPY_GET_RESULT;
rv = ec_command(EC_CMD_ADD_ENTROPY, 0, &p, sizeof(p), NULL, 0);
if (rv == EC_RES_SUCCESS) {
printf("Entropy added successfully\n");
return EC_RES_SUCCESS;
}
/* Abort if EC returns an error other than EC_RES_BUSY. */
if (rv <= -EECRESULT && rv != -EECRESULT-EC_RES_BUSY)
goto out;
}
rv = -EECRESULT-EC_RES_TIMEOUT;
out:
fprintf(stderr, "Failed to add entropy: %d\n", rv);
return rv;
}
int cmd_hello(int argc, char *argv[])
{
struct ec_params_hello p;
struct ec_response_hello r;
int rv;
p.in_data = 0xa0b0c0d0;
rv = ec_command(EC_CMD_HELLO, 0, &p, sizeof(p), &r, sizeof(r));
if (rv < 0)
return rv;
if (r.out_data != 0xa1b2c3d4) {
fprintf(stderr, "Expected response 0x%08x, got 0x%08x\n",
0xa1b2c3d4, r.out_data);
return -1;
}
printf("EC says hello!\n");
return 0;
}
int cmd_hibdelay(int argc, char *argv[])
{
struct ec_params_hibernation_delay p;
struct ec_response_hibernation_delay r;
char *e;
int rv;
if (argc < 2) {
p.seconds = 0; /* Just read the current settings. */
} else {
p.seconds = strtoull(argv[1], &e, 0);
if (e && *e) {
fprintf(stderr, "invalid number\n");
return -1;
}
}
rv = ec_command(EC_CMD_HIBERNATION_DELAY, 0, &p, sizeof(p),
&r, sizeof(r));
if (rv < 0) {
fprintf(stderr, "err: rv=%d\n", rv);
return -1;
}
printf("Hibernation delay: %u s\n", r.hibernate_delay);
printf("Time G3: %u s\n", r.time_g3);
printf("Time left: %u s\n", r.time_remaining);
return 0;
}
static void cmd_hostevent_help(char *cmd)
{
fprintf(stderr,
" Usage: %s get <type>\n"
" Usage: %s set <type> <value>\n"
" <type> is one of:\n"
" 1: EC_HOST_EVENT_B\n"
" 2: EC_HOST_EVENT_SCI_MASK\n"
" 3: EC_HOST_EVENT_SMI_MASK\n"
" 4: EC_HOST_EVENT_ALWAYS_REPORT_MASK\n"
" 5: EC_HOST_EVENT_ACTIVE_WAKE_MASK\n"
" 6: EC_HOST_EVENT_LAZY_WAKE_MASK_S0IX\n"
" 7: EC_HOST_EVENT_LAZY_WAKE_MASK_S3\n"
" 8: EC_HOST_EVENT_LAZY_WAKE_MASK_S5\n"
, cmd, cmd);
}
static int cmd_hostevent(int argc, char *argv[])
{
struct ec_params_host_event p;
struct ec_response_host_event r;
char *e;
int rv;
if (argc < 2) {
fprintf(stderr, "Invalid number of params\n");
cmd_hostevent_help(argv[0]);
return -1;
}
if (!strcasecmp(argv[1], "get")) {
if (argc != 3) {
fprintf(stderr, "Invalid number of params\n");
cmd_hostevent_help(argv[0]);
return -1;
}
p.action = EC_HOST_EVENT_GET;
} else if (!strcasecmp(argv[1], "set")) {
if (argc != 4) {
fprintf(stderr, "Invalid number of params\n");
cmd_hostevent_help(argv[0]);
return -1;
}
p.action = EC_HOST_EVENT_SET;
p.value = strtoull(argv[3], &e, 0);
if (e && *e) {
fprintf(stderr, "Bad value\n");
return -1;
}
} else {
fprintf(stderr, "Bad subcommand: %s\n", argv[1]);
return -1;
}
p.mask_type = strtol(argv[2], &e, 0);
if (e && *e) {
fprintf(stderr, "Bad type\n");
return -1;
}
rv = ec_command(EC_CMD_HOST_EVENT, 0, &p, sizeof(p), &r, sizeof(r));
if (rv == -EC_RES_ACCESS_DENIED - EECRESULT) {
fprintf(stderr, "%s isn't permitted for mask %d.\n",
p.action == EC_HOST_EVENT_SET ? "Set" : "Get",
p.mask_type);
return rv;
} else if (rv < 0) {
return rv;
}
if (p.action == EC_HOST_EVENT_GET)
printf("0x%" PRIx64 "\n", r.value);
return 0;
}
static int get_latest_cmd_version(uint8_t cmd, int *version)
{
struct ec_params_get_cmd_versions p;
struct ec_response_get_cmd_versions r;
int rv;
*version = 0;
/* Figure out the latest version of the given command the EC supports */
p.cmd = cmd;
rv = ec_command(EC_CMD_GET_CMD_VERSIONS, 0, &p, sizeof(p),
&r, sizeof(r));
if (rv < 0) {
if (rv == -EC_RES_INVALID_PARAM)
printf("Command 0x%02x not supported by EC.\n",
EC_CMD_GET_CMD_VERSIONS);
return rv;
}
if (r.version_mask)
*version = __fls(r.version_mask);
return rv;
}
int cmd_hostsleepstate(int argc, char *argv[])
{
struct ec_params_host_sleep_event p;
struct ec_params_host_sleep_event_v1 p1;
struct ec_response_host_sleep_event_v1 r;
void *pp = &p;
size_t psize = sizeof(p), rsize = 0;
char *afterscan;
int rv;
int version = 0, max_version = 0;
uint32_t timeout, transitions;
if (argc < 2) {
fprintf(stderr, "Usage: %s "
"[suspend|wsuspend|resume|freeze|thaw] [timeout]\n",
argv[0]);
return -1;
}
rv = get_latest_cmd_version(EC_CMD_HOST_SLEEP_EVENT, &max_version);
if (rv < 0)
return rv;
if (!strcmp(argv[1], "suspend"))
p.sleep_event = HOST_SLEEP_EVENT_S3_SUSPEND;
else if (!strcmp(argv[1], "wsuspend"))
p.sleep_event = HOST_SLEEP_EVENT_S3_WAKEABLE_SUSPEND;
else if (!strcmp(argv[1], "resume"))
p.sleep_event = HOST_SLEEP_EVENT_S3_RESUME;
else if (!strcmp(argv[1], "freeze")) {
p.sleep_event = HOST_SLEEP_EVENT_S0IX_SUSPEND;
if (max_version >= 1) {
p1.sleep_event = p.sleep_event;
p1.reserved = 0;
p1.suspend_params.sleep_timeout_ms =
EC_HOST_SLEEP_TIMEOUT_DEFAULT;
if (argc > 2) {
p1.suspend_params.sleep_timeout_ms =
strtoull(argv[2], &afterscan, 0);
if ((*afterscan != '\0') ||
(afterscan == argv[2])) {
fprintf(stderr,
"Invalid value: %s\n",
argv[2]);
return -1;
}
}
pp = &p1;
psize = sizeof(p1);
version = 1;
}
} else if (!strcmp(argv[1], "thaw")) {
p.sleep_event = HOST_SLEEP_EVENT_S0IX_RESUME;
if (max_version >= 1) {
version = 1;
rsize = sizeof(r);
}
} else {
fprintf(stderr, "Unknown command: %s\n", argv[1]);
return -1;
}
rv = ec_command(EC_CMD_HOST_SLEEP_EVENT, version, pp, psize, &r, rsize);
if (rv < 0) {
fprintf(stderr, "EC host sleep command failed: %d\n", rv);
return rv;
}
if (rsize) {
timeout = r.resume_response.sleep_transitions &
EC_HOST_RESUME_SLEEP_TIMEOUT;
transitions = r.resume_response.sleep_transitions &
EC_HOST_RESUME_SLEEP_TRANSITIONS_MASK;
printf("%s%d sleep line transitions.\n",
timeout ? "Timeout: " : "",
transitions);
}
return 0;
}
int cmd_test(int argc, char *argv[])
{
struct ec_params_test_protocol p = {
.buf = "0123456789abcdef0123456789ABCDEF"
};
struct ec_response_test_protocol r;
int rv, version = 0;
char *e;
if (argc < 3) {
fprintf(stderr, "Usage: %s result length [version]\n",
argv[0]);
return -1;
}
p.ec_result = strtol(argv[1], &e, 0);
if (e && *e) {
fprintf(stderr, "invalid param (result)\n");
return -1;
}
p.ret_len = strtol(argv[2], &e, 0);
if (e && *e) {
fprintf(stderr, "invalid param (length)\n");
return -1;
}
if (argc > 3) {
version = strtol(argv[3], &e, 0);
if (e && *e) {
fprintf(stderr, "invalid param (version)\n");
return -1;
}
}
rv = ec_command(EC_CMD_TEST_PROTOCOL, version,
&p, sizeof(p), &r, sizeof(r));
printf("rv = %d\n", rv);
return rv;
}
int cmd_s5(int argc, char *argv[])
{
struct ec_params_get_set_value p;
struct ec_params_get_set_value r;
int rv, param;
p.flags = 0;
if (argc > 1) {
p.flags |= EC_GSV_SET;
if (!parse_bool(argv[1], &param)) {
fprintf(stderr, "invalid arg \"%s\"\n", argv[1]);
return -1;
}
p.value = param;
}
rv = ec_command(EC_CMD_GSV_PAUSE_IN_S5, 0,
&p, sizeof(p), &r, sizeof(r));
if (rv > 0)
printf("%s\n", r.value ? "on" : "off");
return rv < 0;
}
static const char * const ec_feature_names[] = {
[EC_FEATURE_LIMITED] = "Limited image, load RW for more",
[EC_FEATURE_FLASH] = "Flash",
[EC_FEATURE_PWM_FAN] = "Direct Fan power management",
[EC_FEATURE_PWM_KEYB] = "Keyboard backlight",
[EC_FEATURE_LIGHTBAR] = "Lightbar",
[EC_FEATURE_LED] = "LED",
[EC_FEATURE_MOTION_SENSE] = "Motion Sensors",
[EC_FEATURE_KEYB] = "Keyboard",
[EC_FEATURE_PSTORE] = "Host Permanent Storage",
[EC_FEATURE_PORT80] = "BIOS Port 80h access",
[EC_FEATURE_THERMAL] = "Thermal management",
[EC_FEATURE_BKLIGHT_SWITCH] = "Switch backlight on/off",
[EC_FEATURE_WIFI_SWITCH] = "Switch wifi on/off",
[EC_FEATURE_HOST_EVENTS] = "Host event",
[EC_FEATURE_GPIO] = "GPIO",
[EC_FEATURE_I2C] = "I2C master",
[EC_FEATURE_CHARGER] = "Charger",
[EC_FEATURE_BATTERY] = "Simple Battery",
[EC_FEATURE_SMART_BATTERY] = "Smart Battery",
[EC_FEATURE_HANG_DETECT] = "Host hang detection",
[EC_FEATURE_PMU] = "Power Management",
[EC_FEATURE_SUB_MCU] = "Control downstream MCU",
[EC_FEATURE_USB_PD] = "USB Cros Power Delivery",
[EC_FEATURE_USB_MUX] = "USB Multiplexer",
[EC_FEATURE_MOTION_SENSE_FIFO] = "FIFO for Motion Sensors events",
[EC_FEATURE_VSTORE] = "Temporary secure vstore",
[EC_FEATURE_USBC_SS_MUX_VIRTUAL] = "Host-controlled USB-C SS mux",
[EC_FEATURE_RTC] = "Real-time clock",
[EC_FEATURE_FINGERPRINT] = "Fingerprint",
[EC_FEATURE_TOUCHPAD] = "Touchpad",
[EC_FEATURE_RWSIG] = "RWSIG task",
[EC_FEATURE_DEVICE_EVENT] = "Device events reporting",
[EC_FEATURE_UNIFIED_WAKE_MASKS] = "Unified wake masks for LPC/eSPI",
[EC_FEATURE_HOST_EVENT64] = "64-bit host events",
[EC_FEATURE_EXEC_IN_RAM] = "Execute code in RAM",
[EC_FEATURE_CEC] = "Consumer Electronics Control",
[EC_FEATURE_MOTION_SENSE_TIGHT_TIMESTAMPS] =
"Tight timestamp for sensors events",
[EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS] =
"Refined tablet mode hysteresis",
[EC_FEATURE_EFS2] = "Early Firmware Selection v2",
[EC_FEATURE_ISH] = "Intel Integrated Sensor Hub",
[EC_FEATURE_TYPEC_CMD] = "TCPMv2 Type-C commands",
[EC_FEATURE_TYPEC_REQUIRE_AP_MODE_ENTRY] =
"Host-controlled Type-C mode entry",
};
int cmd_inventory(int argc, char *argv[])
{
struct ec_response_get_features r;
int rv, i, j, idx;
rv = ec_command(EC_CMD_GET_FEATURES, 0, NULL, 0, &r, sizeof(r));
if (rv < 0)
return rv;
printf("EC supported features:\n");
for (i = 0, idx = 0; i < 2; i++) {
for (j = 0; j < 32; j++, idx++) {
if (r.flags[i] & BIT(j)) {
if (idx >= ARRAY_SIZE(ec_feature_names) ||
!ec_feature_names[idx] ||
strlen(ec_feature_names[idx]) == 0)
printf("%-4d: Unknown feature\n", idx);
else
printf("%-4d: %s support\n",
idx, ec_feature_names[idx]);
}
}
}
return 0;
}
int cmd_cmdversions(int argc, char *argv[])
{
struct ec_params_get_cmd_versions p;
struct ec_response_get_cmd_versions r;
char *e;
int cmd;
int rv;
if (argc < 2) {
fprintf(stderr, "Usage: %s <cmd>\n", argv[0]);
return -1;
}
cmd = strtol(argv[1], &e, 0);
if ((e && *e) || cmd < 0 || cmd > 0xff) {
fprintf(stderr, "Bad command number.\n");
return -1;
}
p.cmd = cmd;
rv = ec_command(EC_CMD_GET_CMD_VERSIONS, 0, &p, sizeof(p),
&r, sizeof(r));
if (rv < 0) {
if (rv == -EC_RES_INVALID_PARAM)
printf("Command 0x%02x not supported by EC.\n", cmd);
return rv;
}
printf("Command 0x%02x supports version mask 0x%08x\n",
cmd, r.version_mask);
return 0;
}
/*
* Convert a reset cause ID to human-readable string, providing total coverage
* of the 'cause' space. The returned string points to static storage and must
* not be free()ed.
*/
static const char *reset_cause_to_str(uint16_t cause)
{
static const char * const reset_causes[] = {
"(reset unknown)",
"reset: board custom",
"reset: ap hang detected",
"reset: console command",
"reset: host command",
"reset: keyboard sysreset",
"reset: keyboard warm reboot",
"reset: debug warm reboot",
"reset: at AP's request",
"reset: during EC initialization",
"reset: AP watchdog",
};
BUILD_ASSERT(ARRAY_SIZE(reset_causes) == CHIPSET_RESET_COUNT);
static const char * const shutdown_causes[] = {
"shutdown: power failure",
"shutdown: during EC initialization",
"shutdown: board custom",
"shutdown: battery voltage startup inhibit",
"shutdown: power wait asserted",
"shutdown: critical battery",
"shutdown: by console command",
"shutdown: entering G3",
"shutdown: thermal",
"shutdown: power button",
};
BUILD_ASSERT(ARRAY_SIZE(shutdown_causes) ==
CHIPSET_SHUTDOWN_COUNT - CHIPSET_SHUTDOWN_BEGIN);
if (cause < CHIPSET_RESET_COUNT)
return reset_causes[cause];
if (cause < CHIPSET_SHUTDOWN_BEGIN)
return "(reset unknown)";
if (cause < CHIPSET_SHUTDOWN_COUNT)
return shutdown_causes[cause - CHIPSET_SHUTDOWN_BEGIN];
return "(shutdown unknown)";
}
int cmd_uptimeinfo(int argc, char *argv[])
{
struct ec_response_uptime_info r;
int rv;
int i;
int flag_count;
uint32_t flag;
static const char * const reset_flag_descs[] = {
#include "reset_flag_desc.inc"
};
if (argc != 1) {
fprintf(stderr, "uptimeinfo takes no arguments");
return -1;
}
rv = ec_command(EC_CMD_GET_UPTIME_INFO, 0, NULL, 0, &r, sizeof(r));
if (rv < 0) {
fprintf(stderr, "ERROR: EC_CMD_GET_UPTIME_INFO failed; %d\n",
rv);
return rv;
}
printf("EC uptime: %d.%03d seconds\n",
r.time_since_ec_boot_ms / 1000,
r.time_since_ec_boot_ms % 1000);
printf("AP resets since EC boot: %d\n", r.ap_resets_since_ec_boot);
printf("Most recent AP reset causes:\n");
for (i = 0; i != ARRAY_SIZE(r.recent_ap_reset); ++i) {
if (r.recent_ap_reset[i].reset_time_ms == 0)
continue;
printf("\t%d.%03d: %s\n",
r.recent_ap_reset[i].reset_time_ms / 1000,
r.recent_ap_reset[i].reset_time_ms % 1000,
reset_cause_to_str(r.recent_ap_reset[i].reset_cause));
}
printf("EC reset flags at last EC boot: ");
if (!r.ec_reset_flags) {
printf("unknown\n");
return 0;
}
flag_count = 0;
for (flag = 0; flag < ARRAY_SIZE(reset_flag_descs); ++flag) {
if ((r.ec_reset_flags & BIT(flag)) != 0) {
if (flag_count)
printf(" | ");
printf(reset_flag_descs[flag]);
flag_count++;
}
}
if (r.ec_reset_flags >= BIT(flag)) {
if (flag_count)
printf(" | ");
printf("no-desc");
}
printf("\n");
return 0;
}
int cmd_version(int argc, char *argv[])
{
struct ec_response_get_version r;
char *build_string = (char *)ec_inbuf;
int rv;
rv = ec_command(EC_CMD_GET_VERSION, 0, NULL, 0, &r, sizeof(r));
if (rv < 0) {
fprintf(stderr, "ERROR: EC_CMD_GET_VERSION failed: %d\n", rv);
goto exit;
}
rv = ec_command(EC_CMD_GET_BUILD_INFO, 0,
NULL, 0, ec_inbuf, ec_max_insize);
if (rv < 0) {
fprintf(stderr, "ERROR: EC_CMD_GET_BUILD_INFO failed: %d\n",
rv);
goto exit;
}
rv = 0;
/* Ensure versions are null-terminated before we print them */
r.version_string_ro[sizeof(r.version_string_ro) - 1] = '\0';
r.version_string_rw[sizeof(r.version_string_rw) - 1] = '\0';
build_string[ec_max_insize - 1] = '\0';
/* Print versions */
printf("RO version: %s\n", r.version_string_ro);
printf("RW version: %s\n", r.version_string_rw);
printf("Firmware copy: %s\n",
(r.current_image < ARRAY_SIZE(image_names) ?
image_names[r.current_image] : "?"));
printf("Build info: %s\n", build_string);
exit:
printf("Tool version: %s %s %s\n", CROS_ECTOOL_VERSION, DATE, BUILDER);
return rv;
}
int cmd_read_test(int argc, char *argv[])
{
struct ec_params_read_test p;
struct ec_response_read_test r;
int offset, size;
int errors = 0;
int rv;
int i;
char *e;
char *buf;
uint32_t *b;
if (argc < 3) {
fprintf(stderr, "Usage: %s <pattern_offset> <size>\n", argv[0]);
return -1;
}
offset = strtol(argv[1], &e, 0);
size = strtol(argv[2], &e, 0);
if ((e && *e) || size <= 0 || size > MAX_FLASH_SIZE) {
fprintf(stderr, "Bad size.\n");
return -1;
}
printf("Reading %d bytes with pattern offset 0x%x...\n", size, offset);
buf = (char *)malloc(size);
if (!buf) {
fprintf(stderr, "Unable to allocate buffer.\n");
return -1;
}
/* Read data in chunks */
for (i = 0; i < size; i += sizeof(r.data)) {
p.offset = offset + i / sizeof(uint32_t);
p.size = MIN(size - i, sizeof(r.data));
rv = ec_command(EC_CMD_READ_TEST, 0, &p, sizeof(p),
&r, sizeof(r));
if (rv < 0) {
fprintf(stderr, "Read error at offset %d\n", i);
free(buf);
return rv;
}
memcpy(buf + i, r.data, p.size);
}
/* Check data */
for (i = 0, b = (uint32_t *)buf; i < size / 4; i++, b++) {
if (*b != i + offset) {
printf("Mismatch at byte offset 0x%x: "
"expected 0x%08x, got 0x%08x\n",
(int)(i * sizeof(uint32_t)), i + offset, *b);
errors++;
}
}
free(buf);
if (errors) {
printf("Found %d errors\n", errors);
return -1;
}
printf("done.\n");
return 0;
}
int cmd_reboot_ec(int argc, char *argv[])
{
struct ec_params_reboot_ec p;
int rv, i;
if (argc < 2) {
/*
* No params specified so tell the EC to reboot immediately.
* That reboots the AP as well, so unlikely we'll be around
* to see a return code from this...
*/
rv = ec_command(EC_CMD_REBOOT, 0, NULL, 0, NULL, 0);
return (rv < 0 ? rv : 0);
}
/* Parse command */
if (!strcmp(argv[1], "cancel"))
p.cmd = EC_REBOOT_CANCEL;
else if (!strcmp(argv[1], "RO"))
p.cmd = EC_REBOOT_JUMP_RO;
else if (!strcmp(argv[1], "RW"))
p.cmd = EC_REBOOT_JUMP_RW;
else if (!strcmp(argv[1], "cold"))
p.cmd = EC_REBOOT_COLD;
else if (!strcmp(argv[1], "disable-jump"))
p.cmd = EC_REBOOT_DISABLE_JUMP;
else if (!strcmp(argv[1], "hibernate"))
p.cmd = EC_REBOOT_HIBERNATE;
else if (!strcmp(argv[1], "hibernate-clear-ap-off"))
p.cmd = EC_REBOOT_HIBERNATE_CLEAR_AP_OFF;
else if (!strcmp(argv[1], "cold-ap-off"))
p.cmd = EC_REBOOT_COLD_AP_OFF;
else {
fprintf(stderr, "Unknown command: %s\n", argv[1]);
return -1;
}
/* Parse flags, if any */
p.flags = 0;
for (i = 2; i < argc; i++) {
if (!strcmp(argv[i], "at-shutdown")) {
p.flags |= EC_REBOOT_FLAG_ON_AP_SHUTDOWN;
} else if (!strcmp(argv[i], "switch-slot")) {
p.flags |= EC_REBOOT_FLAG_SWITCH_RW_SLOT;
} else {
fprintf(stderr, "Unknown flag: %s\n", argv[i]);
return -1;
}
}
rv = ec_command(EC_CMD_REBOOT_EC, 0, &p, sizeof(p), NULL, 0);
return (rv < 0 ? rv : 0);
}
int cmd_reboot_ap_on_g3(int argc, char *argv[])
{
struct ec_params_reboot_ap_on_g3_v1 p;
int rv;
char *e;
int cmdver;
if (argc < 2) {
p.reboot_ap_at_g3_delay = 0;
} else {
p.reboot_ap_at_g3_delay = strtol(argv[1], &e, 0);
if (e && *e) {
fprintf(stderr, "invalid number\n");
return -1;
}
}
if (ec_cmd_version_supported(EC_CMD_REBOOT_AP_ON_G3, 1))
cmdver = 1;
else
cmdver = 0;
rv = ec_command(EC_CMD_REBOOT_AP_ON_G3, cmdver, &p, sizeof(p), NULL, 0);
return (rv < 0 ? rv : 0);
}
int cmd_button(int argc, char *argv[])
{
struct ec_params_button p;
char *e;
int argv_idx;
int button = KEYBOARD_BUTTON_COUNT;
int rv;
if (argc < 2) {
fprintf(stderr, "Invalid num param %d.\n", argc);
return -1;
}
p.press_ms = 50;
p.btn_mask = 0;
for (argv_idx = 1; argv_idx < argc; argv_idx++) {
if (!strcasecmp(argv[argv_idx], "vup"))
button = KEYBOARD_BUTTON_VOLUME_UP;
else if (!strcasecmp(argv[argv_idx], "vdown"))
button = KEYBOARD_BUTTON_VOLUME_DOWN;
else if (!strcasecmp(argv[argv_idx], "rec"))
button = KEYBOARD_BUTTON_RECOVERY;
else {
/* If last parameter check if it is an integer. */
if (argv_idx == argc - 1) {
p.press_ms = strtol(argv[argv_idx], &e, 0);
/* If integer, break out of the loop. */
if (!*e)
break;
}
button = KEYBOARD_BUTTON_COUNT;
}
if (button == KEYBOARD_BUTTON_COUNT) {
fprintf(stderr, "Invalid button input.\n");
return -1;
}
p.btn_mask |= (1 << button);
}
if (!p.btn_mask)
return 0;
rv = ec_command(EC_CMD_BUTTON, 0, &p, sizeof(p), NULL, 0);
if (rv < 0)
return rv;
printf("Button(s) %d set to %d ms\n", p.btn_mask, p.press_ms);
return 0;
}
int cmd_flash_info(int argc, char *argv[])
{
struct ec_response_flash_info_1 r;
int cmdver = 1;
int rsize = sizeof(r);
int rv;
memset(&r, 0, sizeof(r));
if (!ec_cmd_version_supported(EC_CMD_FLASH_INFO, cmdver)) {
/* Fall back to version 0 command */
cmdver = 0;
rsize = sizeof(struct ec_response_flash_info);
}
rv = ec_command(EC_CMD_FLASH_INFO, cmdver, NULL, 0, &r, rsize);
if (rv < 0)
return rv;
printf("FlashSize %d\nWriteSize %d\nEraseSize %d\nProtectSize %d\n",
r.flash_size, r.write_block_size, r.erase_block_size,
r.protect_block_size);
if (cmdver >= 1) {
/* Fields added in ver.1 available */
printf("WriteIdealSize %d\nFlags 0x%x\n",
r.write_ideal_size, r.flags);
}
return 0;
}
int cmd_rand(int argc, char *argv[])
{
struct ec_params_rand_num p;
struct ec_response_rand_num *r;
size_t r_size;
int64_t num_bytes;
int64_t i;
char *e;
int rv = 0;
if (argc < 2) {
fprintf(stderr, "Usage: %s <num_bytes>\n", argv[0]);
return -1;
}
num_bytes = strtol(argv[1], &e, 0);
if ((e && *e) || (errno == ERANGE)) {
fprintf(stderr, "Invalid num_bytes argument\n");
return -1;
}
r = ec_inbuf;
for (i = 0; i < num_bytes; i += ec_max_insize) {
p.num_rand_bytes = ec_max_insize;
if (num_bytes - i < p.num_rand_bytes)
p.num_rand_bytes = num_bytes - i;
r_size = p.num_rand_bytes;
rv = ec_command(EC_CMD_RAND_NUM, EC_VER_RAND_NUM, &p, sizeof(p),
r, r_size);
if (rv < 0) {
fprintf(stderr, "Random number command failed\n");
return -1;
}
rv = write(STDOUT_FILENO, r->rand, r_size);
if (rv != r_size) {
fprintf(stderr, "Failed to write stdout\n");
return -1;
}
}
return 0;
}
int cmd_flash_spi_info(int argc, char *argv[])
{
struct ec_response_flash_spi_info r;
int rv;
memset(&r, 0, sizeof(r));
/* Print SPI flash info if available */
if (!ec_cmd_version_supported(EC_CMD_FLASH_SPI_INFO, 0)) {
printf("EC has no info (does not use SPI flash?)\n");
return -1;
}
rv = ec_command(EC_CMD_FLASH_SPI_INFO, 0, NULL, 0, &r, sizeof(r));
if (rv < 0)
return rv;
printf("JEDECManufacturerID 0x%02x\n", r.jedec[0]);
printf("JEDECDeviceID 0x%02x 0x%02x\n", r.jedec[1], r.jedec[2]);
printf("JEDECCapacity %d\n", 1 << r.jedec[2]);
printf("ManufacturerID 0x%02x\n", r.mfr_dev_id[0]);
printf("DeviceID 0x%02x\n", r.mfr_dev_id[1]);
printf("StatusRegister1 0x%02x\n", r.sr1);
printf("StatusRegister2 0x%02x\n", r.sr2);
return 0;
}
int cmd_flash_read(int argc, char *argv[])
{
int offset, size;
int rv;
char *e;
char *buf;
if (argc < 4) {
fprintf(stderr,
"Usage: %s <offset> <size> <filename>\n", argv[0]);
return -1;
}
offset = strtol(argv[1], &e, 0);
if ((e && *e) || offset < 0 || offset > MAX_FLASH_SIZE) {
fprintf(stderr, "Bad offset.\n");
return -1;
}
size = strtol(argv[2], &e, 0);
if ((e && *e) || size <= 0 || size > MAX_FLASH_SIZE) {
fprintf(stderr, "Bad size.\n");
return -1;
}
printf("Reading %d bytes at offset %d...\n", size, offset);
buf = (char *)malloc(size);
if (!buf) {
fprintf(stderr, "Unable to allocate buffer.\n");
return -1;
}
/* Read data in chunks */
rv = ec_flash_read(buf, offset, size);
if (rv < 0) {
free(buf);
return rv;
}
rv = write_file(argv[3], buf, size);
free(buf);
if (rv)
return rv;
printf("done.\n");
return 0;
}
int cmd_flash_write(int argc, char *argv[])
{
int offset, size;
int rv;
char *e;
char *buf;
if (argc < 3) {
fprintf(stderr, "Usage: %s <offset> <filename>\n", argv[0]);
return -1;
}
offset = strtol(argv[1], &e, 0);
if ((e && *e) || offset < 0 || offset > MAX_FLASH_SIZE) {
fprintf(stderr, "Bad offset.\n");
return -1;
}
/* Read the input file */
buf = read_file(argv[2], &size);
if (!buf)
return -1;
printf("Writing to offset %d...\n", offset);
/* Write data in chunks */
rv = ec_flash_write(buf, offset, size);
free(buf);
if (rv < 0)
return rv;
printf("done.\n");
return 0;
}
int cmd_flash_erase(int argc, char *argv[])
{
int offset, size;
char *e;
int rv;
bool async = false;
if (argc < 3) {
fprintf(stderr, "Usage: %s <offset> <size>\n", argv[0]);
return -1;
}
if (strcmp(argv[0], "flasheraseasync") == 0)
async = true;
offset = strtol(argv[1], &e, 0);
if ((e && *e) || offset < 0 || offset > MAX_FLASH_SIZE) {
fprintf(stderr, "Bad offset.\n");
return -1;
}
size = strtol(argv[2], &e, 0);
if ((e && *e) || size <= 0 || size > MAX_FLASH_SIZE) {
fprintf(stderr, "Bad size.\n");
return -1;
}
printf("Erasing %d bytes at offset %d...\n", size, offset);
if (async)
rv = ec_flash_erase_async(offset, size);
else
rv = ec_flash_erase(offset, size);
if (rv < 0)
return rv;
printf("done.\n");
return 0;
}
static void print_flash_protect_flags(const char *desc, uint32_t flags)
{
printf("%s 0x%08x", desc, flags);
if (flags & EC_FLASH_PROTECT_GPIO_ASSERTED)
printf(" wp_gpio_asserted");
if (flags & EC_FLASH_PROTECT_RO_AT_BOOT)
printf(" ro_at_boot");
if (flags & EC_FLASH_PROTECT_RW_AT_BOOT)
printf(" rw_at_boot");
if (flags & EC_FLASH_PROTECT_ROLLBACK_AT_BOOT)
printf(" rollback_at_boot");
if (flags & EC_FLASH_PROTECT_ALL_AT_BOOT)
printf(" all_at_boot");
if (flags & EC_FLASH_PROTECT_RO_NOW)
printf(" ro_now");
if (flags & EC_FLASH_PROTECT_RW_NOW)
printf(" rw_now");
if (flags & EC_FLASH_PROTECT_ROLLBACK_NOW)
printf(" rollback_now");
if (flags & EC_FLASH_PROTECT_ALL_NOW)
printf(" all_now");
if (flags & EC_FLASH_PROTECT_ERROR_STUCK)
printf(" STUCK");
if (flags & EC_FLASH_PROTECT_ERROR_INCONSISTENT)
printf(" INCONSISTENT");
printf("\n");
}
int cmd_flash_protect(int argc, char *argv[])
{
struct ec_params_flash_protect p;
struct ec_response_flash_protect r;
int rv, i;
/*
* Set up requested flags. If no flags were specified, p.mask will
* be 0 and nothing will change.
*/
p.mask = p.flags = 0;
for (i = 1; i < argc; i++) {
if (!strcasecmp(argv[i], "now")) {
p.mask |= EC_FLASH_PROTECT_ALL_NOW;
p.flags |= EC_FLASH_PROTECT_ALL_NOW;
} else if (!strcasecmp(argv[i], "enable")) {
p.mask |= EC_FLASH_PROTECT_RO_AT_BOOT;
p.flags |= EC_FLASH_PROTECT_RO_AT_BOOT;
} else if (!strcasecmp(argv[i], "disable"))
p.mask |= EC_FLASH_PROTECT_RO_AT_BOOT;
}
rv = ec_command(EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT,
&p, sizeof(p), &r, sizeof(r));
if (rv < 0)
return rv;
if (rv < sizeof(r)) {
fprintf(stderr, "Too little data returned.\n");
return -1;
}
/* Print returned flags */
print_flash_protect_flags("Flash protect flags:", r.flags);
print_flash_protect_flags("Valid flags: ", r.valid_flags);
print_flash_protect_flags("Writable flags: ", r.writable_flags);
/* Check if we got all the flags we asked for */
if ((r.flags & p.mask) != (p.flags & p.mask)) {
fprintf(stderr, "Unable to set requested flags "
"(wanted mask 0x%08x flags 0x%08x)\n",
p.mask, p.flags);
if (p.mask & ~r.writable_flags)
fprintf(stderr, "Which is expected, because writable "
"mask is 0x%08x.\n", r.writable_flags);
return -1;
}
return 0;
}
int cmd_rw_hash_pd(int argc, char *argv[])
{
struct ec_params_usb_pd_rw_hash_entry *p =
(struct ec_params_usb_pd_rw_hash_entry *)ec_outbuf;
int i, rv;
char *e;
uint32_t val;
uint8_t *rwp;
if (argc < 7) {
fprintf(stderr, "Usage: %s <dev_id> <HASH[0]> ... <HASH[4]>\n",
argv[0]);
return -1;
}
p->dev_id = strtol(argv[1], &e, 0);
if (e && *e) {
fprintf(stderr, "Bad device ID\n");
return -1;
}
rwp = p->dev_rw_hash;
for (i = 2; i < 7; i++) {
val = strtol(argv[i], &e, 0);
if (e && *e) {
fprintf(stderr, "Bad RW hash\n");
return -1;
}
rwp[0] = (uint8_t) (val >> 0) & 0xff;
rwp[1] = (uint8_t) (val >> 8) & 0xff;
rwp[2] = (uint8_t) (val >> 16) & 0xff;
rwp[3] = (uint8_t) (val >> 24) & 0xff;
rwp += 4;
}
rv = ec_command(EC_CMD_USB_PD_RW_HASH_ENTRY, 0, p, sizeof(*p), NULL, 0);
return rv;
}
int cmd_rwsig_status(int argc, char *argv[])
{
int rv;
struct ec_response_rwsig_check_status resp;
rv = ec_command(EC_CMD_RWSIG_CHECK_STATUS, 0, NULL, 0,
&resp, sizeof(resp));
if (rv < 0)
return rv;
printf("RW signature check: %s\n", resp.status ? "OK" : "FAILED");
return 0;
}
static int rwsig_action(const char *command)
{
struct ec_params_rwsig_action req;
if (!strcasecmp(command, "abort"))
req.action = RWSIG_ACTION_ABORT;
else if (!strcasecmp(command, "continue"))
req.action = RWSIG_ACTION_CONTINUE;
else
return -1;
return ec_command(EC_CMD_RWSIG_ACTION, 0, &req, sizeof(req), NULL, 0);
}
int cmd_rwsig_action_legacy(int argc, char *argv[])
{
if (argc < 2) {
fprintf(stderr, "Usage: %s [abort | continue]\n", argv[0]);
return -1;
}
return rwsig_action(argv[1]);
}
int cmd_rwsig_action(int argc, char *argv[])
{
if (argc < 2) {
fprintf(stderr, "Usage: ectool rwsig action [abort | "
"continue]\n");
return -1;
}
return rwsig_action(argv[1]);
}
enum rwsig_info_fields {
RWSIG_INFO_FIELD_SIG_ALG = BIT(0),
RWSIG_INFO_FIELD_KEY_VERSION = BIT(1),
RWSIG_INFO_FIELD_HASH_ALG = BIT(2),
RWSIG_INFO_FIELD_KEY_IS_VALID = BIT(3),
RWSIG_INFO_FIELD_KEY_ID = BIT(4),
RWSIG_INFO_FIELD_ALL = RWSIG_INFO_FIELD_SIG_ALG |
RWSIG_INFO_FIELD_KEY_VERSION | RWSIG_INFO_FIELD_HASH_ALG |
RWSIG_INFO_FIELD_KEY_IS_VALID | RWSIG_INFO_FIELD_KEY_ID
};
static int rwsig_info(enum rwsig_info_fields fields)
{
int i;
int rv;
struct ec_response_rwsig_info r;
bool print_prefix = false;
rv = ec_command(EC_CMD_RWSIG_INFO, EC_VER_RWSIG_INFO, NULL, 0, &r,
sizeof(r));
if (rv < 0) {
fprintf(stderr, "rwsig info command failed\n");
return -1;
}
if ((fields & RWSIG_INFO_FIELD_ALL) == RWSIG_INFO_FIELD_ALL)
print_prefix = true;
if (fields & RWSIG_INFO_FIELD_SIG_ALG) {
if (print_prefix)
printf("sig_alg: ");
printf("%d\n", r.sig_alg);
}
if (fields & RWSIG_INFO_FIELD_KEY_VERSION) {
if (print_prefix)
printf("key_version: ");
printf("%d\n", r.key_version);
}
if (fields & RWSIG_INFO_FIELD_HASH_ALG) {
if (print_prefix)
printf("hash_alg: ");
printf("%d\n", r.hash_alg);
}
if (fields & RWSIG_INFO_FIELD_KEY_IS_VALID) {
if (print_prefix)
printf("key_is_valid: ");
printf("%d\n", r.key_is_valid);
}
if (fields & RWSIG_INFO_FIELD_KEY_ID) {
if (print_prefix)
printf("key_id: ");
for (i = 0; i < sizeof(r.key_id); i++)
printf("%02x", r.key_id[i]);
printf("\n");
}
return 0;
}
static int cmd_rwsig_info(int argc, char *argv[])
{
int i;
struct rwsig_dump_cmds {
const char *cmd;
enum rwsig_info_fields field;
};
struct rwsig_dump_cmds cmd_map[] = {
{ "sig_alg", RWSIG_INFO_FIELD_SIG_ALG },
{ "key_version", RWSIG_INFO_FIELD_KEY_VERSION },
{ "hash_alg", RWSIG_INFO_FIELD_HASH_ALG },
{ "key_valid", RWSIG_INFO_FIELD_KEY_IS_VALID },
{ "key_id", RWSIG_INFO_FIELD_KEY_ID },
};
if (argc == 0)
return -1;
if (strcmp(argv[0], "info") == 0)
return rwsig_info(RWSIG_INFO_FIELD_ALL);
if (strcmp(argv[0], "dump") == 0) {
if (argc != 2) {
fprintf(stderr,
"Usage: rwsig dump "
"[sig_alg|key_version|hash_alg|key_valid|key_id]\n");
return -1;
}
for (i = 0; i < ARRAY_SIZE(cmd_map); i++)
if (strcmp(argv[1], cmd_map[i].cmd) == 0)
return rwsig_info(cmd_map[i].field);
return -1;
}
return -1;
}
int cmd_rwsig(int argc, char **argv)
{
struct rwsig_subcommand {
const char *subcommand;
int (*handler)(int argc, char *argv[]);
};
const struct rwsig_subcommand rwsig_subcommands[] = {
{ "info", cmd_rwsig_info },
{ "dump", cmd_rwsig_info },
{ "action", cmd_rwsig_action },
{ "status", cmd_rwsig_status }
};
int i;
if (argc < 2) {
fprintf(stderr, "Usage: %s <info|dump|action|status>\n",
argv[0]);
return -1;
}
for (i = 0; i < ARRAY_SIZE(rwsig_subcommands); i++)
if (strcmp(argv[1], rwsig_subcommands[i].subcommand) == 0)
return rwsig_subcommands[i].handler(--argc, &argv[1]);
return -1;
}
enum sysinfo_fields {
SYSINFO_FIELD_RESET_FLAGS = BIT(0),
SYSINFO_FIELD_CURRENT_IMAGE = BIT(1),
SYSINFO_FIELD_FLAGS = BIT(2),
SYSINFO_INFO_FIELD_ALL = SYSINFO_FIELD_RESET_FLAGS |
SYSINFO_FIELD_CURRENT_IMAGE |
SYSINFO_FIELD_FLAGS
};
static int sysinfo(struct ec_response_sysinfo *info)
{
struct ec_response_sysinfo r;
int rv;
rv = ec_command(EC_CMD_SYSINFO, 0, NULL, 0, &r, sizeof(r));
if (rv < 0) {
fprintf(stderr, "ERROR: EC_CMD_SYSINFO failed: %d\n", rv);
return rv;
}
return 0;
}
int cmd_sysinfo(int argc, char **argv)
{
struct ec_response_sysinfo r;
enum sysinfo_fields fields = 0;
bool print_prefix = false;
if (argc != 1 && argc != 2)
goto sysinfo_error_usage;
if (argc == 1) {
fields = SYSINFO_INFO_FIELD_ALL;
print_prefix = true;
} else if (argc == 2) {
if (strcmp(argv[1], "flags") == 0)
fields = SYSINFO_FIELD_FLAGS;
else if (strcmp(argv[1], "reset_flags") == 0)
fields = SYSINFO_FIELD_RESET_FLAGS;
else if (strcmp(argv[1], "firmware_copy") == 0)
fields = SYSINFO_FIELD_CURRENT_IMAGE;
else
goto sysinfo_error_usage;
}
if (sysinfo(&r) != 0)
return -1;
if (fields & SYSINFO_FIELD_RESET_FLAGS) {
if (print_prefix)
printf("Reset flags: ");
printf("0x%08x\n", r.reset_flags);
}
if (fields & SYSINFO_FIELD_FLAGS) {
if (print_prefix)
printf("Flags: ");
printf("0x%08x\n", r.flags);
}
if (fields & SYSINFO_FIELD_CURRENT_IMAGE) {
if (print_prefix)
printf("Firmware copy: ");
printf("%d\n", r.current_image);
}
return 0;
sysinfo_error_usage:
fprintf(stderr, "Usage: %s "
"[flags|reset_flags|firmware_copy]\n",
argv[0]);
return -1;
}
int cmd_rollback_info(int argc, char *argv[])
{
struct ec_response_rollback_info r;
int rv;
rv = ec_command(EC_CMD_ROLLBACK_INFO, 0, NULL, 0, &r, sizeof(r));
if (rv < 0) {
fprintf(stderr, "ERROR: EC_CMD_ROLLBACK_INFO failed: %d\n", rv);
return rv;
}
/* Print versions */
printf("Rollback block id: %d\n", r.id);
printf("Rollback min version: %d\n", r.rollback_min_version);
printf("RW rollback version: %d\n", r.rw_rollback_version);
return 0;
}
int cmd_apreset(int argc, char *argv[])
{
return ec_command(EC_CMD_AP_RESET, 0, NULL, 0, NULL, 0);
}
#define FP_FRAME_INDEX_SIMPLE_IMAGE -1
/*
* Download a frame buffer from the FPMCU.
*
* Might be either the finger image or a finger template depending on 'index'.
*
* @param info a pointer to store the struct ec_response_fp_info retrieved by
* this command.
* @param index the specific frame to retrieve, might be:
* -1 (aka FP_FRAME_INDEX_SIMPLE_IMAGE) for the a single grayscale image.
* 0 (aka FP_FRAME_INDEX_RAW_IMAGE) for the full vendor raw finger image.
* 1..n for a finger template.
*
* @returns a pointer to the buffer allocated to contain the frame or NULL
* if case of error. The caller must call free() once it no longer needs the
* buffer.
*/
static void *fp_download_frame(struct ec_response_fp_info *info, int index)
{
struct ec_params_fp_frame p;
int rv = 0;
size_t stride, size;
void *buffer;
uint8_t *ptr;
int cmdver = ec_cmd_version_supported(EC_CMD_FP_INFO, 1) ? 1 : 0;
int rsize = cmdver == 1 ? sizeof(*info)
: sizeof(struct ec_response_fp_info_v0);
const int max_attempts = 3;
int num_attempts;
/* templates not supported in command v0 */
if (index > 0 && cmdver == 0)
return NULL;
rv = ec_command(EC_CMD_FP_INFO, cmdver, NULL, 0, info, rsize);
if (rv < 0)
return NULL;
if (index == FP_FRAME_INDEX_SIMPLE_IMAGE) {
size = (size_t)info->width * info->bpp/8 * info->height;
index = FP_FRAME_INDEX_RAW_IMAGE;
} else if (index == FP_FRAME_INDEX_RAW_IMAGE) {
size = info->frame_size;
} else {
size = info->template_size;
}
buffer = malloc(size);
if (!buffer) {
fprintf(stderr, "Cannot allocate memory for the image\n");
return NULL;
}
ptr = buffer;
p.offset = index << FP_FRAME_INDEX_SHIFT;
while (size) {
stride = MIN(ec_max_insize, size);
p.size = stride;
num_attempts = 0;
while (num_attempts < max_attempts) {
num_attempts++;
rv = ec_command(EC_CMD_FP_FRAME, 0, &p, sizeof(p),
ptr, stride);
if (rv >= 0)
break;
if (rv == -EECRESULT - EC_RES_ACCESS_DENIED)
break;
usleep(100000);
}
if (rv < 0) {
free(buffer);
return NULL;
}
p.offset += stride;
size -= stride;
ptr += stride;
}
return buffer;
}
int cmd_fp_mode(int argc, char *argv[])
{
struct ec_params_fp_mode p;
struct ec_response_fp_mode r;
uint32_t mode = 0;
uint32_t capture_type = FP_CAPTURE_SIMPLE_IMAGE;
int i, rv;
if (argc == 1)
mode = FP_MODE_DONT_CHANGE;
for (i = 1; i < argc; i++) {
/* modes */
if (!strncmp(argv[i], "deepsleep", 9))
mode |= FP_MODE_DEEPSLEEP;
else if (!strncmp(argv[i], "fingerdown", 10))
mode |= FP_MODE_FINGER_DOWN;
else if (!strncmp(argv[i], "fingerup", 8))
mode |= FP_MODE_FINGER_UP;
else if (!strncmp(argv[i], "enroll", 6))
mode |= FP_MODE_ENROLL_IMAGE | FP_MODE_ENROLL_SESSION;
else if (!strncmp(argv[i], "match", 5))
mode |= FP_MODE_MATCH;
else if (!strncmp(argv[i], "reset_sensor", 12))
mode = FP_MODE_RESET_SENSOR;
else if (!strncmp(argv[i], "reset", 5))
mode = 0;
else if (!strncmp(argv[i], "capture", 7))
mode |= FP_MODE_CAPTURE;
/* capture types */
else if (!strncmp(argv[i], "vendor", 6))
capture_type = FP_CAPTURE_VENDOR_FORMAT;
else if (!strncmp(argv[i], "pattern0", 8))
capture_type = FP_CAPTURE_PATTERN0;
else if (!strncmp(argv[i], "pattern1", 8))
capture_type = FP_CAPTURE_PATTERN1;
else if (!strncmp(argv[i], "qual", 4))
capture_type = FP_CAPTURE_QUALITY_TEST;
else if (!strncmp(argv[i], "test_reset", 10))
capture_type = FP_CAPTURE_RESET_TEST;
}
if (mode & FP_MODE_CAPTURE)
mode |= capture_type << FP_MODE_CAPTURE_TYPE_SHIFT;
p.mode = mode;
rv = ec_command(EC_CMD_FP_MODE, 0, &p, sizeof(p), &r, sizeof(r));
if (rv < 0)
return rv;
printf("FP mode: (0x%x) ", r.mode);
if (r.mode & FP_MODE_DEEPSLEEP)
printf("deepsleep ");
if (r.mode & FP_MODE_FINGER_DOWN)
printf("finger-down ");
if (r.mode & FP_MODE_FINGER_UP)
printf("finger-up ");
if (r.mode & FP_MODE_ENROLL_SESSION)
printf("enroll%s ",
r.mode & FP_MODE_ENROLL_IMAGE ? "+image" : "");
if (r.mode & FP_MODE_MATCH)
printf("match ");
if (r.mode & FP_MODE_CAPTURE)
printf("capture ");
printf("\n");
return 0;
}
int cmd_fp_seed(int argc, char *argv[])
{
struct ec_params_fp_seed p;
char *seed;
if (argc != 2) {
fprintf(stderr, "Usage: %s <seed>\n", argv[0]);
return 1;
}
seed = argv[1];
if (strlen(seed) != FP_CONTEXT_TPM_BYTES) {
printf("Invalid seed '%s' is %zd bytes long instead of %d.\n",
seed, strlen(seed), FP_CONTEXT_TPM_BYTES);
return 1;
}
printf("Setting seed '%s'\n", seed);
p.struct_version = FP_TEMPLATE_FORMAT_VERSION;
memcpy(p.seed, seed, FP_CONTEXT_TPM_BYTES);
return ec_command(EC_CMD_FP_SEED, 0, &p, sizeof(p), NULL, 0);
}
int cmd_fp_stats(int argc, char *argv[])
{
struct ec_response_fp_stats r;
int rv;
unsigned long long ts;
rv = ec_command(EC_CMD_FP_STATS, 0, NULL, 0, &r, sizeof(r));
if (rv < 0)
return rv;
ts = (uint64_t)r.overall_t0.hi << 32 | r.overall_t0.lo;
printf("FP stats (t0=%llu us):\n", ts);
printf("Last capture time: ");
if (r.timestamps_invalid & FPSTATS_CAPTURE_INV)
printf("Invalid\n");
else
printf("%d us\n", r.capture_time_us);
printf("Last matching time: ");
if (r.timestamps_invalid & FPSTATS_MATCHING_INV)
printf("Invalid\n");
else
printf("%d us (finger: %d)\n", r.matching_time_us,
r.template_matched);
printf("Last overall time: ");
if (r.timestamps_invalid)
printf("Invalid\n");
else
printf("%d us\n", r.overall_time_us);
return 0;
}
int cmd_fp_info(int argc, char *argv[])
{
struct ec_response_fp_info r;
int rv;
int cmdver = ec_cmd_version_supported(EC_CMD_FP_INFO, 1) ? 1 : 0;
int rsize = cmdver == 1 ? sizeof(r)
: sizeof(struct ec_response_fp_info_v0);
uint16_t dead;
rv = ec_command(EC_CMD_FP_INFO, cmdver, NULL, 0, &r, rsize);
if (rv < 0)
return rv;
printf("Fingerprint sensor: vendor %x product %x model %x version %x\n",
r.vendor_id, r.product_id, r.model_id, r.version);
printf("Image: size %dx%d %d bpp\n", r.width, r.height, r.bpp);
printf("Error flags: %s%s%s%s\n",
r.errors & FP_ERROR_NO_IRQ ? "NO_IRQ " : "",
r.errors & FP_ERROR_SPI_COMM ? "SPI_COMM " : "",
r.errors & FP_ERROR_BAD_HWID ? "BAD_HWID " : "",
r.errors & FP_ERROR_INIT_FAIL ? "INIT_FAIL " : "");
dead = FP_ERROR_DEAD_PIXELS(r.errors);
if (dead == FP_ERROR_DEAD_PIXELS_UNKNOWN) {
printf("Dead pixels: UNKNOWN\n");
} else {
printf("Dead pixels: %u\n", dead);
}
if (cmdver == 1) {
printf("Templates: version %d size %d count %d/%d"
" dirty bitmap %x\n",
r.template_version, r.template_size, r.template_valid,
r.template_max, r.template_dirty);
}
return 0;
}
static void print_fp_enc_flags(const char *desc, uint32_t flags)
{
printf("%s 0x%08x", desc, flags);
if (flags & FP_ENC_STATUS_SEED_SET)
printf(" FPTPM_seed_set");
printf("\n");
}
static int cmd_fp_context(int argc, char *argv[])
{
struct ec_params_fp_context_v1 p;
int rv;
int tries = 20; /* Wait at most two seconds */
if (argc < 2) {
fprintf(stderr, "Usage: %s <context>\n", argv[0]);
return -1;
}
/*
* Note that we treat the resulting "userid" as raw byte array, so we
* don't want to copy the NUL from the end of the string.
*/
if (strlen(argv[1]) != sizeof(p.userid)) {
fprintf(stderr, "Context must be exactly %zu bytes\n",
sizeof(p.userid));
return -1;
}
p.action = FP_CONTEXT_ASYNC;
memcpy(p.userid, argv[1], sizeof(p.userid));
rv = ec_command(EC_CMD_FP_CONTEXT, 1, &p, sizeof(p), NULL, 0);
if (rv != EC_RES_SUCCESS)
goto out;
while (tries--) {
usleep(100000);
p.action = FP_CONTEXT_GET_RESULT;
rv = ec_command(EC_CMD_FP_CONTEXT, 1, &p, sizeof(p), NULL, 0);
if (rv == EC_RES_SUCCESS) {
printf("Set context successfully\n");
return EC_RES_SUCCESS;
}
/* Abort if EC returns an error other than EC_RES_BUSY. */
if (rv <= -EECRESULT && rv != -EECRESULT - EC_RES_BUSY)
goto out;
}
rv = -EECRESULT - EC_RES_TIMEOUT;
out:
fprintf(stderr, "Failed to reset context: %d\n", rv);
return rv;
}
int cmd_fp_enc_status(int argc, char *argv[])
{
int rv;
struct ec_response_fp_encryption_status resp = { 0 };
rv = ec_command(EC_CMD_FP_ENC_STATUS, 0, NULL, 0, &resp, sizeof(resp));
if (rv < 0) {
printf("Get FP sensor encryption status failed.\n");
} else {
print_fp_enc_flags("FPMCU encryption status:", resp.status);
print_fp_enc_flags("Valid flags: ",
resp.valid_flags);
rv = 0;
}
return rv;
}
int cmd_fp_frame(int argc, char *argv[])
{
struct ec_response_fp_info r;
int idx = (argc == 2 && !strcasecmp(argv[1], "raw")) ?
FP_FRAME_INDEX_RAW_IMAGE : FP_FRAME_INDEX_SIMPLE_IMAGE;
void *buffer = fp_download_frame(&r, idx);
uint8_t *ptr = buffer;
int x, y;
if (!buffer) {
fprintf(stderr, "Failed to get FP sensor frame\n");
return -1;
}
if (idx == FP_FRAME_INDEX_RAW_IMAGE) {
fwrite(buffer, r.frame_size, 1, stdout);
goto frame_done;
}
/* Print 8-bpp PGM ASCII header */
printf("P2\n%d %d\n%d\n", r.width, r.height, (1 << r.bpp) - 1);
for (y = 0; y < r.height; y++) {
for (x = 0; x < r.width; x++, ptr++)
printf("%d ", *ptr);
printf("\n");
}
printf("# END OF FILE\n");
frame_done:
free(buffer);
return 0;
}
int cmd_fp_template(int argc, char *argv[])
{
struct ec_response_fp_info r;
struct ec_params_fp_template *p = ec_outbuf;
/* TODO(b/78544921): removing 32 bits is a workaround for the MCU bug */
int max_chunk = ec_max_outsize
- offsetof(struct ec_params_fp_template, data) - 4;
int idx = -1;
char *e;
int size;
void *buffer = NULL;
uint32_t offset = 0;
int rv = 0;
if (argc < 2) {
fprintf(stderr, "Usage: %s [<infile>|<index>]\n", argv[0]);
return -1;
}
idx = strtol(argv[1], &e, 0);
if (!(e && *e)) {
buffer = fp_download_frame(&r, idx + 1);
if (!buffer) {
fprintf(stderr, "Failed to get FP template %d\n", idx);
return -1;
}
fwrite(buffer, r.template_size, 1, stdout);
free(buffer);
return 0;
}
/* not an index, is it a filename ? */
buffer = read_file(argv[1], &size);
if (!buffer) {
fprintf(stderr, "Invalid parameter: %s\n", argv[1]);
return -1;
}
printf("sending template from: %s (%d bytes)\n", argv[1], size);
while (size) {
uint32_t tlen = MIN(max_chunk, size);
p->offset = offset;
p->size = tlen;
size -= tlen;
if (!size)
p->size |= FP_TEMPLATE_COMMIT;
memcpy(p->data, buffer + offset, tlen);
rv = ec_command(EC_CMD_FP_TEMPLATE, 0, p, tlen +
offsetof(struct ec_params_fp_template, data),
NULL, 0);
if (rv < 0)
break;
offset += tlen;
}
if (rv < 0)
fprintf(stderr, "Failed with %d\n", rv);
else
rv = 0;
free(buffer);
return rv;
}
/**
* determine if in GFU mode or not.
*
* NOTE, Sends HOST commands that modify ec_outbuf contents.
*
* @opos return value of GFU mode object position or zero if not found
* @port port number to query
* @return 1 if in GFU mode, 0 if not, -1 if error
*/
static int in_gfu_mode(int *opos, int port)
{
int i;
struct ec_params_usb_pd_get_mode_request *p =
(struct ec_params_usb_pd_get_mode_request *)ec_outbuf;
struct ec_params_usb_pd_get_mode_response *r =
(struct ec_params_usb_pd_get_mode_response *)ec_inbuf;
p->port = port;
p->svid_idx = 0;
do {
ec_command(EC_CMD_USB_PD_GET_AMODE, 0, p, sizeof(*p),
ec_inbuf, ec_max_insize);
if (!r->svid || (r->svid == USB_VID_GOOGLE))
break;
p->svid_idx++;
} while (p->svid_idx < SVID_DISCOVERY_MAX);
if (r->svid != USB_VID_GOOGLE) {
fprintf(stderr, "Google VID not returned\n");
return -1;
}
*opos = 0; /* invalid ... must be 1 thru 6 */
for (i = 0; i < PDO_MODES; i++) {
if (r->vdo[i] == MODE_GOOGLE_FU) {
*opos = i + 1;
break;
}
}
return r->opos == *opos;
}
/**
* Enter GFU mode.
*
* NOTE, Sends HOST commands that modify ec_outbuf contents.
*
* @port port number to enter GFU on.
* @return 1 if entered GFU mode, 0 if not, -1 if error
*/
static int enter_gfu_mode(int port)
{
int opos;
struct ec_params_usb_pd_set_mode_request *p =
(struct ec_params_usb_pd_set_mode_request *)ec_outbuf;
int gfu_mode = in_gfu_mode(&opos, port);
if (gfu_mode < 0) {
fprintf(stderr, "Failed to query GFU mode support\n");
return 0;
} else if (!gfu_mode) {
if (!opos) {
fprintf(stderr, "Invalid object position %d\n", opos);
return 0;
}
p->port = port;
p->svid = USB_VID_GOOGLE;
p->opos = opos;
p->cmd = PD_ENTER_MODE;
ec_command(EC_CMD_USB_PD_SET_AMODE, 0, p, sizeof(*p),
NULL, 0);
usleep(500000); /* sleep to allow time for set mode */
gfu_mode = in_gfu_mode(&opos, port);
}
return gfu_mode;
}
int cmd_pd_device_info(int argc, char *argv[])
{
int i, rv, port;
char *e;
struct ec_params_usb_pd_info_request *p =
(struct ec_params_usb_pd_info_request *)ec_outbuf;
struct ec_params_usb_pd_rw_hash_entry *r0 =
(struct ec_params_usb_pd_rw_hash_entry *)ec_inbuf;
struct ec_params_usb_pd_discovery_entry *r1;
if (argc < 2) {
fprintf(stderr, "Usage: %s <port>\n", argv[0]);
return -1;
}
port = strtol(argv[1], &e, 0);
if (e && *e) {
fprintf(stderr, "Bad port\n");
return -1;
}
p->port = port;
r1 = (struct ec_params_usb_pd_discovery_entry *)ec_inbuf;
rv = ec_command(EC_CMD_USB_PD_DISCOVERY, 0, p, sizeof(*p),
ec_inbuf, ec_max_insize);
if (rv < 0)
return rv;
if (!r1->vid)
printf("Port:%d has no discovered device\n", port);
else {
printf("Port:%d ptype:%d vid:0x%04x pid:0x%04x\n", port,
r1->ptype, r1->vid, r1->pid);
}
if (enter_gfu_mode(port) != 1) {
fprintf(stderr, "Failed to enter GFU mode\n");
return -1;
}
p->port = port;
rv = ec_command(EC_CMD_USB_PD_DEV_INFO, 0, p, sizeof(*p),
ec_inbuf, ec_max_insize);
if (rv < 0)
return rv;
if (!r0->dev_id)
printf("Port:%d has no valid device\n", port);
else {
uint8_t *rwp = r0->dev_rw_hash;
printf("Port:%d DevId:%d.%d Hash:", port,
HW_DEV_ID_MAJ(r0->dev_id), HW_DEV_ID_MIN(r0->dev_id));
for (i = 0; i < 5; i++) {
printf(" 0x%02x%02x%02x%02x", rwp[3], rwp[2], rwp[1],
rwp[0]);
rwp += 4;
}
printf(" CurImg:%s\n", image_names[r0->current_image]);
}
return rv;
}
int cmd_flash_pd(int argc, char *argv[])
{
struct ec_params_usb_pd_fw_update *p =
(struct ec_params_usb_pd_fw_update *)ec_outbuf;
int i, dev_id, port;
int rv, fsize, step = 96;
char *e;
char *buf;
char *data = (char *)p + sizeof(*p);
if (argc < 4) {
fprintf(stderr, "Usage: %s <dev_id> <port> <filename>\n",
argv[0]);
return -1;
}
dev_id = strtol(argv[1], &e, 0);
if (e && *e) {
fprintf(stderr, "Bad device ID\n");
return -1;
}
port = strtol(argv[2], &e, 0);
if (e && *e) {
fprintf(stderr, "Bad port\n");
return -1;
}
if (enter_gfu_mode(port) != 1) {
fprintf(stderr, "Failed to enter GFU mode\n");
return -1;
}
/* Read the input file */
buf = read_file(argv[3], &fsize);
if (!buf)
return -1;
/* Erase the current RW RSA signature */
fprintf(stderr, "Erasing expected RW hash\n");
p->dev_id = dev_id;
p->port = port;
p->cmd = USB_PD_FW_ERASE_SIG;
p->size = 0;
rv = ec_command(EC_CMD_USB_PD_FW_UPDATE, 0,
p, p->size + sizeof(*p), NULL, 0);
if (rv < 0)
goto pd_flash_error;
/* Reboot */
fprintf(stderr, "Rebooting\n");
p->dev_id = dev_id;
p->port = port;
p->cmd = USB_PD_FW_REBOOT;
p->size = 0;
rv = ec_command(EC_CMD_USB_PD_FW_UPDATE, 0,
p, p->size + sizeof(*p), NULL, 0);
if (rv < 0)
goto pd_flash_error;
usleep(3000000); /* 3sec to reboot and get CC line idle */
/* re-enter GFU after reboot */
if (enter_gfu_mode(port) != 1) {
fprintf(stderr, "Failed to enter GFU mode\n");
goto pd_flash_error;
}
/* Erase RW flash */
fprintf(stderr, "Erasing RW flash\n");
p->dev_id = dev_id;
p->port = port;
p->cmd = USB_PD_FW_FLASH_ERASE;
p->size = 0;
rv = ec_command(EC_CMD_USB_PD_FW_UPDATE, 0,
p, p->size + sizeof(*p), NULL, 0);
/* 3 secs should allow ample time for 2KB page erases at 40ms */
usleep(3000000);
if (rv < 0)
goto pd_flash_error;
/* Write RW flash */
fprintf(stderr, "Writing RW flash\n");
p->dev_id = dev_id;
p->port = port;
p->cmd = USB_PD_FW_FLASH_WRITE;
p->size = step;
for (i = 0; i < fsize; i += step) {
p->size = MIN(fsize - i, step);
memcpy(data, buf + i, p->size);
rv = ec_command(EC_CMD_USB_PD_FW_UPDATE, 0,
p, p->size + sizeof(*p), NULL, 0);
if (rv < 0)
goto pd_flash_error;
/*
* TODO(crosbug.com/p/33905) throttle so EC doesn't watchdog on
* other tasks. Remove once issue resolved.
*/
usleep(10000);
}
/* 100msec to guarantee writes finish */
usleep(100000);
/* Reboot into new RW */
fprintf(stderr, "Rebooting PD into new RW\n");
p->cmd = USB_PD_FW_REBOOT;
p->size = 0;
rv = ec_command(EC_CMD_USB_PD_FW_UPDATE, 0,
p, p->size + sizeof(*p), NULL, 0);
if (rv < 0)
goto pd_flash_error;
free(buf);
fprintf(stderr, "Complete\n");
return 0;
pd_flash_error:
free(buf);
fprintf(stderr, "PD flash error\n");
return -1;
}
int cmd_pd_set_amode(int argc, char *argv[])
{
char *e;
struct ec_params_usb_pd_set_mode_request *p =
(struct ec_params_usb_pd_set_mode_request *)ec_outbuf;
if (argc < 5) {
fprintf(stderr, "Usage: %s <port> <svid> <opos> <cmd>\n",
argv[0]);
return -1;
}
p->port = strtol(argv[1], &e, 0);
if (e && *e) {
fprintf(stderr, "Bad port\n");
return -1;
}
p->svid = strtol(argv[2], &e, 0);
if ((e && *e) || !p->svid) {
fprintf(stderr, "Bad svid\n");
return -1;
}
p->opos = strtol(argv[3], &e, 0);
if ((e && *e) || !p->opos) {
fprintf(stderr, "Bad opos\n");
return -1;
}
p->cmd = strtol(argv[4], &e, 0);
if ((e && *e) || (p->cmd >= PD_MODE_CMD_COUNT)) {
fprintf(stderr, "Bad cmd\n");
return -1;
}
return ec_command(EC_CMD_USB_PD_SET_AMODE, 0, p, sizeof(*p), NULL, 0);
}
int cmd_pd_get_amode(int argc, char *argv[])
{
int i;
char *e;
struct ec_params_usb_pd_get_mode_request *p =
(struct ec_params_usb_pd_get_mode_request *)ec_outbuf;
struct ec_params_usb_pd_get_mode_response *r =
(struct ec_params_usb_pd_get_mode_response *)ec_inbuf;
if (argc < 2) {
fprintf(stderr, "Usage: %s <port>\n", argv[0]);
return -1;
}
p->port = strtol(argv[1], &e, 0);
if (e && *e) {
fprintf(stderr, "Bad port\n");
return -1;
}
p->svid_idx = 0;
do {
ec_command(EC_CMD_USB_PD_GET_AMODE, 0, p, sizeof(*p),
ec_inbuf, ec_max_insize);
if (!r->svid)
break;
printf("%cSVID:0x%04x ", (r->opos) ? '*' : ' ',
r->svid);
for (i = 0; i < PDO_MODES; i++) {
printf("%c0x%08x ", (r->opos && (r->opos == i + 1)) ?
'*' : ' ', r->vdo[i]);
}
printf("\n");
p->svid_idx++;
} while (p->svid_idx < SVID_DISCOVERY_MAX);
return -1;
}
/* The I/O asm funcs exist only on x86. */
#if defined(__i386__) || defined(__x86_64__)
#include <sys/io.h>
int cmd_serial_test(int argc, char *argv[])
{
const char *c = "COM2 sample serial output from host!\r\n";
printf("Writing sample serial output to COM2\n");
while (*c) {
/* Wait for space in transmit FIFO */
while (!(inb(0x2fd) & 0x20))
;
/* Put the next character */
outb(*c++, 0x2f8);
}
printf("done.\n");
return 0;
}
int cmd_port_80_flood(int argc, char *argv[])
{
int i;
for (i = 0; i < 256; i++)
outb(i, 0x80);
return 0;
}
#else
int cmd_serial_test(int argc, char *argv[])
{
printf("x86 specific command\n");
return -1;
}
int cmd_port_80_flood(int argc, char *argv[])
{
printf("x86 specific command\n");
return -1;
}
#endif
static void cmd_smart_discharge_usage(const char *command)
{
printf("Usage: %s [hours_to_zero [hibern] [cutoff]]\n", command);
printf("\n");
printf("Set/Get smart discharge parameters\n");
printf("hours_to_zero: Desired hours for state of charge to zero\n");
printf("hibern: Discharge rate in hibernation (uA)\n");
printf("cutoff: Discharge rate in battery cutoff (uA)\n");
}
int cmd_smart_discharge(int argc, char *argv[])
{
struct ec_params_smart_discharge *p = ec_outbuf;
struct ec_response_smart_discharge *r = ec_inbuf;
uint32_t cap;
char *e;
int rv;
if (argc > 1) {
if (strcmp(argv[1], "help") == 0) {
cmd_smart_discharge_usage(argv[0]);
return 0;
}
p->flags = EC_SMART_DISCHARGE_FLAGS_SET;
p->hours_to_zero = strtol(argv[1], &e, 0);
if (p->hours_to_zero < 0 || (e && *e)) {
perror("Bad value for [hours_to_zero]");
return -1;
}
if (argc == 4) {
p->drate.hibern = strtol(argv[2], &e, 0);
if (p->drate.hibern < 0 || (e && *e)) {
perror("Bad value for [hibern]");
return -1;
}
p->drate.cutoff = strtol(argv[3], &e, 0);
if (p->drate.cutoff < 0 || (e && *e)) {
perror("Bad value for [cutoff]");
return -1;
}
} else if (argc != 2) {
/* If argc != 4, it has to be 2. */
perror("Invalid number of parameters");
return -1;
}
}
rv = ec_command(EC_CMD_SMART_DISCHARGE, 0, p, sizeof(*p),
r, ec_max_insize);
if (rv < 0) {
perror("ERROR: EC_CMD_SMART_DISCHARGE failed");
return rv;
}
cap = read_mapped_mem32(EC_MEMMAP_BATT_LFCC);
if (!is_battery_range(cap)) {
perror("WARN: Failed to read battery capacity");
cap = 0;
}
printf("%-27s %5d h\n", "Hours to zero capacity:", r->hours_to_zero);
printf("%-27s %5d mAh (%d %%)\n", "Stay-up threshold:",
r->dzone.stayup, cap > 0 ? r->dzone.stayup * 100 / cap : -1);
printf("%-27s %5d mAh (%d %%)\n", "Cutoff threshold:",
r->dzone.cutoff, cap > 0 ? r->dzone.cutoff * 100 / cap : -1);
printf("%-27s %5d uA\n", "Hibernate discharge rate:", r->drate.hibern);
printf("%-27s %5d uA\n", "Cutoff discharge rate:", r->drate.cutoff);
return 0;
}
/*
* This boolean variable and handler are used for
* catching signals that translate into a quit/shutdown
* of a runtime loop.
* This is used in cmd_stress_test.
*/
static bool sig_quit;
static void sig_quit_handler(int sig)
{
sig_quit = true;
}
int cmd_stress_test(int argc, char *argv[])
{
int i;
bool reboot = false;
time_t now;
time_t start_time, last_update_time;
unsigned int rand_seed = 0;
uint64_t round = 1, attempt = 1;
uint64_t failures = 0;
const int max_sleep_usec = 1000; /* 1ms */
const int loop_update_interval = 10000;
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "help") == 0) {
printf("Usage: %s [reboot] [help]\n", argv[0]);
printf("Stress tests the host command interface by"
" repeatedly issuing common host commands.\n");
printf("The intent is to expose errors in kernel<->mcu"
" communication, such as exceeding timeouts.\n");
printf("\n");
printf("reboot - Reboots the target before"
" starting the stress test.\n");
printf(" This may force restart the host,"
" if the main ec is the target.\n");
return 0;
} else if (strcmp(argv[i], "reboot") == 0) {
reboot = true;
} else {
fprintf(stderr, "Error - Unknown argument '%s'\n",
argv[i]);
return 1;
}
}
printf("Stress test tool version: %s %s %s\n",
CROS_ECTOOL_VERSION, DATE, BUILDER);
start_time = time(NULL);
last_update_time = start_time;
printf("Start time: %s\n", ctime(&start_time));
if (reboot) {
printf("Issuing ec reboot. Expect a few early failed"
" ioctl messages.\n");
ec_command(EC_CMD_REBOOT, 0, NULL, 0, NULL, 0);
sleep(2);
}
sig_quit = false;
signal(SIGINT, sig_quit_handler);
while (!sig_quit) {
int rv;
struct ec_response_get_version ver_r;
char *build_string = (char *)ec_inbuf;
struct ec_params_flash_protect flash_p;
struct ec_response_flash_protect flash_r;
struct ec_params_hello hello_p;
struct ec_response_hello hello_r;
/* Request EC Version Strings */
rv = ec_command(EC_CMD_GET_VERSION, 0,
NULL, 0, &ver_r, sizeof(ver_r));
if (rv < 0) {
failures++;
perror("ERROR: EC_CMD_GET_VERSION failed");
}
ver_r.version_string_ro[sizeof(ver_r.version_string_ro) - 1]
= '\0';
ver_r.version_string_rw[sizeof(ver_r.version_string_rw) - 1]
= '\0';
if (strlen(ver_r.version_string_ro) == 0) {
failures++;
fprintf(stderr, "RO version string is empty\n");
}
if (strlen(ver_r.version_string_rw) == 0) {
failures++;
fprintf(stderr, "RW version string is empty\n");
}
usleep(rand_r(&rand_seed) % max_sleep_usec);
/* Request EC Build String */
rv = ec_command(EC_CMD_GET_BUILD_INFO, 0,
NULL, 0, ec_inbuf, ec_max_insize);
if (rv < 0) {
failures++;
perror("ERROR: EC_CMD_GET_BUILD_INFO failed");
}
build_string[ec_max_insize - 1] = '\0';
if (strlen(build_string) == 0) {
failures++;
fprintf(stderr, "Build string is empty\n");
}
usleep(rand_r(&rand_seed) % max_sleep_usec);
/* Request Flash Protect Status */
rv = ec_command(EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT,
&flash_p, sizeof(flash_p), &flash_r,
sizeof(flash_r));
if (rv < 0) {
failures++;
perror("ERROR: EC_CMD_FLASH_PROTECT failed");
}
usleep(rand_r(&rand_seed) % max_sleep_usec);
/* Request Hello */
hello_p.in_data = 0xa0b0c0d0;
rv = ec_command(EC_CMD_HELLO, 0, &hello_p, sizeof(hello_p),
&hello_r, sizeof(hello_r));
if (rv < 0) {
failures++;
perror("ERROR: EC_CMD_HELLO failed");
}
if (hello_r.out_data != HELLO_RESP(hello_p.in_data)) {
failures++;
fprintf(stderr, "Hello response was invalid.\n");
}
usleep(rand_r(&rand_seed) % max_sleep_usec);
if ((attempt % loop_update_interval) == 0) {
now = time(NULL);
printf("Update: attempt %" PRIu64 " round %" PRIu64
" | took %.f seconds\n",
attempt, round,
difftime(now, last_update_time));
last_update_time = now;
}
if (attempt++ == UINT64_MAX)
round++;
}
printf("\n");
now = time(NULL);
printf("End time: %s\n", ctime(&now));
printf("Total runtime: %.f seconds\n",
difftime(time(NULL), start_time));
printf("Total failures: %" PRIu64 "\n", failures);
return 0;
}
int read_mapped_temperature(int id)
{
int rv;
if (!read_mapped_mem8(EC_MEMMAP_THERMAL_VERSION))