summaryrefslogtreecommitdiffstats
path: root/src/main.c
diff options
context:
space:
mode:
authorCyril Bur <cyrilbur@gmail.com>2017-09-21 17:29:33 +1000
committerAlistair Popple <alistair@popple.id.au>2017-09-22 12:12:11 +1000
commite979fefacdd2e4f8acb543bbb735dc45edb1bc89 (patch)
tree5c797fe0b6b5fdda5a3a2c25a5af94ff8f7adeef /src/main.c
parent1f168ffb6713df414907fcbc3b5bb0f094cfaf50 (diff)
downloadpdbg-e979fefacdd2e4f8acb543bbb735dc45edb1bc89.tar.gz
pdbg-e979fefacdd2e4f8acb543bbb735dc45edb1bc89.zip
Hardware Trace Macro (HTM)
This is not Hardware Transactional Memory, rather it is the Hardware Trace Macro (HTM) which can be used to gather data about hardware activity. Currently only Nest HTM is implemented. Only runs on a PPC host on a kernel built with CONFIG_PPC_MEMTRACE and CONFIG_PPC_SCOM. Signed-off-by: Cyril Bur <cyrilbur@gmail.com> Signed-off-by: Alistair Popple <alistair@popple.id.au>
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c249
1 files changed, 248 insertions, 1 deletions
diff --git a/src/main.c b/src/main.c
index 0dd52c7..7c410a1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define _GNU_SOURCE
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
@@ -37,11 +38,15 @@
#undef PR_DEBUG
#define PR_DEBUG(...)
+#define HTM_DUMP_BASENAME "htm.dump"
+
enum command { GETCFAM = 1, PUTCFAM, GETSCOM, PUTSCOM, \
GETMEM, PUTMEM, GETGPR, GETNIA, GETSPR, \
GETMSR, PUTGPR, PUTNIA, PUTSPR, PUTMSR, \
STOP, START, THREADSTATUS, STEP, PROBE, \
- GETVMEM, SRESET };
+ GETVMEM, SRESET, HTM_STOP, HTM_ANALYSE, \
+ HTM_START, HTM_DUMP, HTM_RESET, HTM_GO, \
+ HTM_TRACE, HTM_STATUS };
#define MAX_CMD_ARGS 3
enum command cmd = 0;
@@ -117,6 +122,13 @@ static void print_usage(char *pname)
printf("\tstop\n");
printf("\tthreadstatus\n");
printf("\tprobe\n");
+ printf("\thtm_start\n");
+ printf("\thtm_stop\n");
+ printf("\thtm_status\n");
+ printf("\thtm_reset\n");
+ printf("\thtm_dump\n");
+ printf("\thtm_trace\n");
+ printf("\thtm_analyse\n");
}
enum command parse_cmd(char *optarg)
@@ -194,6 +206,30 @@ enum command parse_cmd(char *optarg)
} else if (strcmp(optarg, "probe") == 0) {
cmd = PROBE;
cmd_min_arg_count = 0;
+ } else if (strcmp(optarg, "htm_start") == 0) {
+ cmd = HTM_START;
+ cmd_min_arg_count = 0;
+ } else if (strcmp(optarg, "htm_go") == 0) {
+ cmd = HTM_START;
+ cmd_min_arg_count = 0;
+ } else if (strcmp(optarg, "htm_stop") == 0) {
+ cmd = HTM_STOP;
+ cmd_min_arg_count = 0;
+ } else if (strcmp(optarg, "htm_status") == 0) {
+ cmd = HTM_STATUS;
+ cmd_min_arg_count = 0;
+ } else if (strcmp(optarg, "htm_reset") == 0) {
+ cmd = HTM_RESET;
+ cmd_min_arg_count = 0;
+ } else if (strcmp(optarg, "htm_dump") == 0) {
+ cmd = HTM_DUMP;
+ cmd_min_arg_count = 0;
+ } else if (strcmp(optarg, "htm_trace") == 0) {
+ cmd = HTM_TRACE;
+ cmd_min_arg_count = 0;
+ } else if (strcmp(optarg, "htm_analyse") == 0) {
+ cmd = HTM_ANALYSE;
+ cmd_min_arg_count = 0;
}
if (cmd_min_arg_count && !cmd_max_arg_count)
@@ -613,6 +649,195 @@ static void disable_dn(struct dt_node *dn)
dt_add_property_string(dn, "status", "disabled");
}
+static char *get_htm_dump_filename(void)
+{
+ char *filename;
+ int i;
+
+ filename = strdup(HTM_DUMP_BASENAME);
+ if (!filename)
+ return NULL;
+
+ i = 0;
+ while (access(filename, F_OK) == 0) {
+ free(filename);
+ if (asprintf(&filename, "%s.%d", HTM_DUMP_BASENAME, i) == -1)
+ return NULL;
+ i++;
+ }
+
+ return filename;
+}
+
+static int run_htm_start(void)
+{
+ struct target *target;
+ int rc = 0;
+
+ for_each_class_target("htm", target) {
+ printf("Starting HTM@%d#%d\n",
+ dt_get_chip_id(target->dn), target->index);
+ if (htm_start(target) != 1)
+ printf("Couldn't start HTM@%d#%d\n",
+ dt_get_chip_id(target->dn), target->index);
+ rc++;
+ }
+
+ return rc;
+}
+
+static int run_htm_stop(void)
+{
+ struct target *target;
+ int rc = 0;
+
+ for_each_class_target("htm", target) {
+ printf("Stopping HTM@%d#%d\n",
+ dt_get_chip_id(target->dn), target->index);
+ if (htm_stop(target) != 1)
+ printf("Couldn't stop HTM@%d#%d\n",
+ dt_get_chip_id(target->dn), target->index);
+ rc++;
+ }
+
+ return rc;
+}
+
+static int run_htm_status(void)
+{
+ struct target *target;
+ int rc = 0;
+
+ for_each_class_target("htm", target) {
+ printf("HTM@%d#%d\n",
+ dt_get_chip_id(target->dn), target->index);
+ if (htm_status(target) != 1)
+ printf("Couldn't get HTM@%d#%d status\n",
+ dt_get_chip_id(target->dn), target->index);
+ rc++;
+ printf("\n\n");
+ }
+
+ return rc;
+}
+
+static int run_htm_reset(void)
+{
+ uint64_t old_base = 0, base, size;
+ struct target *target;
+ int rc = 0;
+
+ for_each_class_target("htm", target) {
+ printf("Resetting HTM@%d#%d\n",
+ dt_get_chip_id(target->dn), target->index);
+ if (htm_reset(target, &base, &size) != 1)
+ printf("Couldn't reset HTM@%d#%d\n",
+ dt_get_chip_id(target->dn), target->index);
+ if (old_base != base) {
+ printf("The kernel has initialised HTM memory at:\n");
+ printf("base: 0x%016" PRIx64 " for 0x%016" PRIx64 " size\n",
+ base, size);
+ printf("In case of system crash/xstop use the following to dump the trace on the BMC:\n");
+ printf("./pdbg getmem 0x%016" PRIx64 " 0x%016" PRIx64 " > htm.dump\n",
+ base, size);
+ }
+ rc++;
+ }
+
+ return rc;
+}
+
+static int run_htm_dump(void)
+{
+ struct target *target;
+ char *filename;
+ int rc = 0;
+
+ filename = get_htm_dump_filename();
+ if (!filename)
+ return 0;
+
+ /* size = 0 will dump everything */
+ printf("Dumping HTM trace to file [chip].[#]%s\n", filename);
+ for_each_class_target("htm", target) {
+ printf("Dumping HTM@%d#%d\n",
+ dt_get_chip_id(target->dn), target->index);
+ if (htm_dump(target, 0, filename) == 1)
+ printf("Couldn't dump HTM@%d#%d\n",
+ dt_get_chip_id(target->dn), target->index);
+ rc++;
+ }
+ free(filename);
+
+ return rc;
+}
+
+static int run_htm_trace(void)
+{
+ uint64_t old_base = 0, base, size;
+ struct target *target;
+ int rc = 0;
+
+ for_each_class_target("htm", target) {
+ /*
+ * Don't mind if stop fails, it will fail if it wasn't
+ * running, if anything bad is happening reset will fail
+ */
+ htm_stop(target);
+ printf("Resetting HTM@%d#%d\n",
+ dt_get_chip_id(target->dn), target->index);
+ if (htm_reset(target, &base, &size) != 1)
+ printf("Couldn't reset HTM@%d#%d\n",
+ dt_get_chip_id(target->dn), target->index);
+ if (old_base != base) {
+ printf("The kernel has initialised HTM memory at:\n");
+ printf("base: 0x%016" PRIx64 " for 0x%016" PRIx64 " size\n",
+ base, size);
+ printf("./pdbg getmem 0x%016" PRIx64 " 0x%016" PRIx64 " > htm.dump\n\n",
+ base, size);
+ }
+ old_base = base;
+ }
+
+ for_each_class_target("htm", target) {
+ printf("Starting HTM@%d#%d\n",
+ dt_get_chip_id(target->dn), target->index);
+ if (htm_start(target) != 1)
+ printf("Couldn't start HTM@%d#%d\n",
+ dt_get_chip_id(target->dn), target->index);
+ rc++;
+ }
+
+ return rc;
+}
+
+static int run_htm_analyse(void)
+{
+ struct target *target;
+ char *filename;
+ int rc = 0;
+
+ for_each_class_target("htm", target)
+ htm_stop(target);
+
+ filename = get_htm_dump_filename();
+ if (!filename)
+ return 0;
+
+ printf("Dumping HTM trace to file [chip].[#]%s\n", filename);
+ for_each_class_target("htm", target) {
+ printf("Dumping HTM@%d#%d\n",
+ dt_get_chip_id(target->dn), target->index);
+ if (htm_dump(target, 0, filename) != 1)
+ printf("Couldn't dump HTM@%d#%d\n",
+ dt_get_chip_id(target->dn), target->index);
+ rc++;
+ }
+ free(filename);
+
+ return rc;
+}
+
/* TODO: It would be nice to have a more dynamic way of doing this */
extern unsigned char _binary_p8_i2c_dtb_o_start;
extern unsigned char _binary_p8_i2c_dtb_o_end;
@@ -870,6 +1095,28 @@ int main(int argc, char *argv[])
printf("\nNote that only selected targets will be shown above. If none are shown\n"
"try adding '-a' to select all targets\n");
break;
+ case HTM_GO:
+ case HTM_START:
+ rc = run_htm_start();
+ break;
+ case HTM_STOP:
+ rc = run_htm_stop();
+ break;
+ case HTM_STATUS:
+ rc = run_htm_status();
+ break;
+ case HTM_RESET:
+ rc = run_htm_reset();
+ break;
+ case HTM_DUMP:
+ rc = run_htm_dump();
+ break;
+ case HTM_TRACE:
+ rc = run_htm_trace();
+ break;
+ case HTM_ANALYSE:
+ rc = run_htm_analyse();
+ break;
default:
PR_ERROR("Unsupported command\n");
break;
OpenPOWER on IntegriCloud