blob: 28daec76a984e5bb1c7229eecf64a8f51ee8d4ce [file] [log] [blame]
/*
* (C) Copyright 2015 Google, Inc.
* All rights reserved.
*
*/
#include "sata.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include "../common/io.h"
#include "../common/util.h"
#include "common.h"
#define SATA_TX_TEST_MFTP 0
#define SATA_TX_TEST_LBP 1
#define SATA_TX_TEST_LFTP 2
#define SATA_TX_TEST_HFTP 3
#define SATA_TX_TEST_SSOP 4
#define SATA_QUERY_INFO "smartctl -a /dev/sda"
#define SATA_QUERY_HEADER "Smartctl"
#define SATA_FAILED "failed"
#define SATA_CRC "CRC"
#define SATA_CRC_COLUMN_NUM 10
#define SATA_CRC_PASS_VAL "0"
char *sataTxTestStr[] = {"Mid Frequency Test Pattern", "Lone Bit Pattern",
"Low Frequency Test Pattern",
"High Frequency Test Pattern",
"Simultaneous Switch Output Pattern"};
int displaySataConfigTerse(uint64_t base) {
int rc;
uint32_t data, len, i, tmp;
static const uint32_t kStep = 0x20, kWordSize = 4;
uint64_t addr = base;
printf("SATA config:\n");
for (len = 0; len <= (SATA_CONFIG_LAST_REG_OFFSET - kStep + kWordSize);
len += kStep) {
addr = base + len;
tmp = addr & UINT_MASK;
printf("0x%08x:", tmp);
for (i = 0; i < kStep; i += kWordSize) {
if ((rc = read_physical_addr(addr, &data)) < 0) {
tmp = (uint32_t)(addr & UINT_MASK);
printf("Read SATA config addr 0x%x failed\n", tmp);
return -1;
}
printf(" 0x%08x", data);
addr += kWordSize;
}
printf("\n");
}
printf("SATA Port 0 config:\n");
for (len = SATA_PORT0_FIRST_REG_OFFSET;
len <= (SATA_PORT0_LAST_REG_OFFSET - kStep + kWordSize); len += kStep) {
addr = base + len;
tmp = addr & UINT_MASK;
printf("0x%08x:", tmp);
for (i = 0; i < kStep; i += kWordSize) {
if ((rc = read_physical_addr(addr, &data)) < 0) {
tmp = (uint32_t)(addr & UINT_MASK);
printf("Read SATA port 0 config addr 0x%x failed\n", tmp);
return -1;
}
printf(" 0x%08x", data);
addr += kWordSize;
}
printf("\n");
}
printf("SATA Port 1 config:\n");
for (len = SATA_PORT1_FIRST_REG_OFFSET;
len <= (SATA_PORT1_LAST_REG_OFFSET - kStep + kWordSize); len += kStep) {
addr = base + len;
tmp = addr & UINT_MASK;
printf("0x%08x:", tmp);
for (i = 0; i < kStep; i += kWordSize) {
if ((rc = read_physical_addr(addr, &data)) < 0) {
tmp = (uint32_t)(addr & UINT_MASK);
printf("Read SATA port 1 config addr 0x%x failed\n", tmp);
return -1;
}
printf(" 0x%08x", data);
addr += kWordSize;
}
printf("\n");
}
return 0;
}
int displaySataConfigVerbose(uint64_t base) {
int rc;
uint32_t data, len, i, tmp;
static const uint32_t kStep = 0x20, kWordSize = 4;
uint64_t addr = base;
printf("SATA config:\n");
read_physical_addr(base + SATA_CAP_REG_OFFSET, &data);
printf(" HBA Capabilities: 0x%08x\n", data);
read_physical_addr(base + SATA_GHC_REG_OFFSET, &data);
printf(" Global HBA Control: 0x%08x\n", data);
read_physical_addr(base + SATA_IS_REG_OFFSET, &data);
printf(" Interrupt Status: 0x%08x\n", data);
read_physical_addr(base + SATA_PI_REG_OFFSET, &data);
printf(" Ports Implemented: 0x%08x\n", data);
read_physical_addr(base + SATA_VS_REG_OFFSET, &data);
printf(" AHCI Version Register: 0x%08x\n", data);
read_physical_addr(base + SATA_CCC_CTL_REG_OFFSET, &data);
printf(" Command Completion Coalescing Control: 0x%08x\n", data);
read_physical_addr(base + SATA_CCC_PORTS_REG_OFFSET, &data);
printf(" Command Completion Coalescing Ports: 0x%08x\n", data);
read_physical_addr(base + SATA_CAP2_REG_OFFSET, &data);
printf(" HBA Capabilities Extended: 0x%08x\n", data);
read_physical_addr(base + SATA_BISTAFR_REG_OFFSET, &data);
printf(" BIST Activate FIS: 0x%08x\n", data);
read_physical_addr(base + SATA_BISTCR_REG_OFFSET, &data);
printf(" BIST Control: 0x%08x\n", data);
read_physical_addr(base + SATA_BISTFCTR_REG_OFFSET, &data);
printf(" BIST FIS Count: 0x%08x\n", data);
read_physical_addr(base + SATA_BISTSR_REG_OFFSET, &data);
printf(" BIST Status: 0x%08x\n", data);
read_physical_addr(base + SATA_BISTDECR_REG_OFFSET, &data);
printf(" BIST DWORD Error Count: 0x%08x\n", data);
read_physical_addr(base + SATA_OOBR_REG_OFFSET, &data);
printf(" OOB: 0x%08x\n", data);
read_physical_addr(base + SATA_GPCR_REG_OFFSET, &data);
printf(" General Purpose Control: 0x%08x\n", data);
read_physical_addr(base + SATA_GPSR_REG_OFFSET, &data);
printf(" General Purpose Status: 0x%08x\n", data);
read_physical_addr(base + SATA_TIMER1MS_REG_OFFSET, &data);
printf(" TImer 1-ms: 0x%08x\n", data);
read_physical_addr(base + SATA_GPARAM1R_REG_OFFSET, &data);
printf(" Global Parameter 1: 0x%08x\n", data);
read_physical_addr(base + SATA_GPARAM2R_REG_OFFSET, &data);
printf(" Global Parameter 2: 0x%08x\n", data);
read_physical_addr(base + SATA_PPARAMR_REG_OFFSET, &data);
printf(" Port Parameter: 0x%08x\n", data);
read_physical_addr(base + SATA_TESTR_REG_OFFSET, &data);
printf(" Test: 0x%08x\n", data);
read_physical_addr(base + SATA_VERSIONR_REG_OFFSET, &data);
printf(" Version: 0x%08x\n", data);
read_physical_addr(base + SATA_IDR_REG_OFFSET, &data);
printf(" ID: 0x%08x\n", data);
printf("SATA Port 0 config:\n");
for (len = SATA_PORT0_FIRST_REG_OFFSET;
len <= (SATA_PORT0_LAST_REG_OFFSET - kStep + kWordSize); len += kStep) {
addr = base + len;
tmp = addr & UINT_MASK;
printf("0x%08x:", tmp);
for (i = 0; i < kStep; i += kWordSize) {
if ((rc = read_physical_addr(addr, &data)) < 0) {
tmp = addr & UINT_MASK;
printf("Read SATA port 0 config addr 0x%x failed\n", tmp);
return -1;
}
printf(" 0x%08x", data);
addr += kWordSize;
}
printf("\n");
}
printf("SATA Port 1 config:\n");
for (len = SATA_PORT1_FIRST_REG_OFFSET;
len <= (SATA_PORT1_LAST_REG_OFFSET - kStep + kWordSize); len += kStep) {
addr = base + len;
tmp = addr & UINT_MASK;
printf("0x%08x:", tmp);
for (i = 0; i < kStep; i += kWordSize) {
if ((rc = read_physical_addr(addr, &data)) < 0) {
tmp = addr & UINT_MASK;
printf("Read SATA port 1 config addr 0x%x failed\n", tmp);
return -1;
}
printf(" 0x%08x", data);
addr += kWordSize;
}
printf("\n");
}
return 0;
}
static void satacfgdump_usage() {
printf("satacfgdump\n");
printf("Example:\n");
printf("satacfgdump\n");
printf("Dump SATA config\n");
}
int satacfgdump(int argc, char *argv[]) {
int rc = 0;
bool verbose = false;
if (argc > 2) {
satacfgdump_usage();
return -1;
} else if (argc == 2) {
if (strcmp(argv[1], "-v") != 0) {
satacfgdump_usage();
return -1;
}
verbose = true;
}
if (verbose) {
if ((rc = displaySataConfigVerbose(SATA_CONFIG_BASE_ADDR)) < 0)
printf("Display full SATA config error %d\n", rc);
} else if ((rc = displaySataConfigTerse(SATA_CONFIG_BASE_ADDR)) < 0) {
printf("Display SATA config error %d\n", rc);
return rc;
}
return rc;
}
int sataSetTX(int option) {
int rc;
rc = write_physical_addr(SATA_CONFIG_BASE_ADDR + SATA_TESTR_REG_OFFSET,
0x00010000);
rc = write_physical_addr(SATA_CONFIG_BASE_ADDR + SATA_TEST_TX_SET1_REG_OFFSET,
0x00000012);
rc = write_physical_addr(SATA_CONFIG_BASE_ADDR + SATA_TEST_TX_SET2_REG_OFFSET,
0x00000001);
rc = write_physical_addr(SATA_CONFIG_BASE_ADDR + SATA_TEST_TX_SET1_REG_OFFSET,
0x00000013);
switch (option) {
case SATA_TX_TEST_MFTP:
rc = write_physical_addr(SATA_CONFIG_BASE_ADDR + SATA_BISTCR_REG_OFFSET,
0x00040706);
break;
case SATA_TX_TEST_LBP:
break;
rc = write_physical_addr(SATA_CONFIG_BASE_ADDR + SATA_BISTCR_REG_OFFSET,
0x00040705);
case SATA_TX_TEST_LFTP:
rc = write_physical_addr(SATA_CONFIG_BASE_ADDR + SATA_BISTCR_REG_OFFSET,
0x00040708);
break;
case SATA_TX_TEST_HFTP:
rc = write_physical_addr(SATA_CONFIG_BASE_ADDR + SATA_BISTCR_REG_OFFSET,
0x00040707);
break;
case SATA_TX_TEST_SSOP:
rc = write_physical_addr(SATA_CONFIG_BASE_ADDR + SATA_BISTCR_REG_OFFSET,
0x00040700);
break;
default:
rc = write_physical_addr(SATA_CONFIG_BASE_ADDR + SATA_BISTCR_REG_OFFSET,
0x00040706);
printf("Invalid SATA TX test option %d\n", option);
rc = -1;
break;
}
return rc;
}
static void satatxtest_usage() {
printf("satatxtest <mftp|lbp|lftp|hftp|ssop>\n");
printf("Example:\n");
printf("satatxtest MFTP\n");
printf("generate SATA TX test traffic MFTP\n");
printf("mftp = Mid Frequency Test Pattern\n");
printf("lbp = Lone Bit Pattern\n");
printf("lftp = Low Frequency Test Pattern\n");
printf("hftp = High Frequency Test Pattern\n");
printf("ssop = Simultaneous Switch Output Pattern\n");
}
int satatxtest(int argc, char *argv[]) {
int rc = 0;
int option = -1;
if (argc != 2) {
satatxtest_usage();
return -1;
}
if (strcmp(argv[1], "mftp") == 0) {
option = SATA_TX_TEST_MFTP;
} else if (strcmp(argv[1], "lbp") == 0) {
option = SATA_TX_TEST_LBP;
} else if (strcmp(argv[1], "lftp") == 0) {
option = SATA_TX_TEST_LFTP;
} else if (strcmp(argv[1], "hftp") == 0) {
option = SATA_TX_TEST_HFTP;
} else if (strcmp(argv[1], "ssop") == 0) {
option = SATA_TX_TEST_SSOP;
} else {
satatxtest_usage();
return -1;
}
rc = sataSetTX(option);
printf("SATA TX test set to %s\n", sataTxTestStr[option]);
return rc;
}
int sataSetRX() {
int rc;
rc = write_physical_addr(SATA_CONFIG_BASE_ADDR + SATA_TESTR_REG_OFFSET,
0x00010000);
rc = write_physical_addr(SATA_CONFIG_BASE_ADDR + SATA_TEST_RX_SET1_REG_OFFSET,
0x00000000);
rc = write_physical_addr(SATA_CONFIG_BASE_ADDR + SATA_TEST_RX_SET2_REG_OFFSET,
0x00000000);
rc = write_physical_addr(SATA_CONFIG_BASE_ADDR + SATA_TEST_RX_SET3_REG_OFFSET,
0x00000000);
return rc;
}
static void satarxtest_usage() {
printf("satarxtest\n");
printf("Example:\n");
printf("satarxtest\n");
printf("setup SATA RX test\n");
}
int satarxtest(int argc, char *argv[]) {
if ((argc != 1) || (argv[0] == NULL)) {
satarxtest_usage();
return -1;
}
int rc = sataSetRX();
printf("SATA RX test setup\n");
return rc;
}
static void satabench_usage() {
printf("satabench <time in sec>\n");
printf("Example:\n");
printf("satabench 300\n");
printf("run SATA diskbench test for 300 seconds\n");
}
int satabench(int argc, char *argv[]) {
unsigned int duration;
char cmd[128], rsp[MAX_PKT_SIZE];
FILE *fp;
int i;
if (argc != 2) {
printf("%s invalid params\n", FAIL_TEXT);
satabench_usage();
return -1;
}
duration = strtoul(argv[1], NULL, 0);
if (duration == 0) {
printf("%s Cannot run test with 0 time\n", FAIL_TEXT);
return -1;
}
// Check HD present
sprintf(cmd, SATA_QUERY_INFO " | grep -i " SATA_FAILED);
fp = popen(cmd, "r");
if (fp == NULL) {
printf("%s Cannot run command smartctl\n", FAIL_TEXT);
return -1;
} else {
while (fscanf(fp, "%s", rsp) != EOF) {
if (strcmp(rsp, SATA_QUERY_HEADER) == 0) {
printf("%s No hard disk\n", FAIL_TEXT);
pclose(fp);
return -1;
}
}
pclose(fp);
}
// sprintf(cmd, "cd /var/media;diskbench -i2 -w8 -r4 -DOF -t%d;cd", duration);
sprintf(cmd, "cd /var/media;diskbench -i2 -w8 -r4 -b768 -s2048 -t%d;cd",
duration);
system_cmd(cmd);
// Check CRC
sprintf(cmd, SATA_QUERY_INFO " | grep -i " SATA_CRC);
fp = popen(cmd, "r");
if (fp == NULL) {
printf("%s Cannot run command smartctl for CRC\n", FAIL_TEXT);
return -1;
} else {
i = 0;
while (i++ < SATA_CRC_COLUMN_NUM) {
if (fscanf(fp, "%s", rsp) == EOF) break;
}
pclose(fp);
if (i < SATA_CRC_COLUMN_NUM) {
printf("smartctl query CRC num columns %d too small\n", i);
} else {
if (strcmp(rsp, SATA_CRC_PASS_VAL) != 0) {
printf("%s diskbench detect CRC %s\n", FAIL_TEXT, rsp);
return -1;
} else {
printf("diskbench CRC is %s\n", rsp);
}
}
}
return 0;
}
static void sata_link_reset_usage() {
printf("sata_link_reset <num> [period (default 5)]\n");
printf("WARNING: this command clears dmesg\n");
printf("Example:\n");
printf("sata_link_reset 300 5\n");
printf("reset SATA link 300 time every 5 seconds\n");
}
int sata_link_reset(int argc, char *argv[]) {
unsigned int duration = 5, failedNum = 0;
char cmd[128];
int num, i;
FILE *fp;
bool found = false;
if ((argc != 2) && (argc != 3)) {
sata_link_reset_usage();
return -1;
}
num = strtol(argv[1], NULL, 0);
if (argc == 3) duration = strtoul(argv[2], NULL, 0);
if (duration == 0) {
printf("Cannot run test %d second\n", duration);
return -1;
}
if ((num == 0) || (num < -1)) {
printf("Number of times can either be -1 (forever) or > 0. %d invalid\n",
num);
return -1;
}
system_cmd("dmesg -c > /tmp/t");
i = 0;
while (i != num) {
found = false;
system_cmd("echo \"0 0 0\" > /sys/class/scsi_host/host1/scan");
fp = popen("dmesg -c | grep ata", "r");
while (fscanf(fp, "%s", cmd) != EOF) {
if (strcmp(cmd, "SATA") == 0) {
if (fscanf(fp, "%s", cmd) <= 0) return -1;
if (strcmp(cmd, "link") != 0) {
continue;
}
if (fscanf(fp, "%s", cmd) <= 0) return -1;
if (strcmp(cmd, "up") != 0) {
continue;
} else {
found = true;
if (fscanf(fp, "%s", cmd) <= 0) return -1;
break;
}
}
}
pclose(fp);
if (!found) {
++failedNum;
} else if (strcmp(cmd, "3.0") != 0) {
printf("Error: %d SATA link reset up with %s Gbps.\n", i, cmd);
} else {
// printf("SATA link up %s Gbps.\n", cmd);
}
sleep(duration);
++i;
if (i == -1) i = 0;
}
printf("Run %d times, failed %d times\n", num, failedNum);
return 0;
}