README.md
Rendering markdown...
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2018,2019 IBM Corp.
#define _GNU_SOURCE
#include "ast.h"
#include "console.h"
#include "debug.h"
#include "log.h"
#include "prompt.h"
#include "ts16.h"
#include "tty.h"
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static inline int streq(const char *a, const char *b)
{
return !strcmp(a, b);
}
static int ts16_console_init(struct debug *ctx, va_list args)
{
const char *ip, *username, *password;
struct ts16 *ts16;
int port;
int fd;
ts16 = malloc(sizeof(*ts16));
if (!ts16)
return -ENOMEM;
ip = va_arg(args, const char *);
port = va_arg(args, int);
username = va_arg(args, const char *);
password = va_arg(args, const char *);
fd = ts16_init(ts16, ip, port, username, password);
if (fd < 0) { goto cleanup_free; }
ctx->console = &ts16->console;
return fd;
cleanup_free:
free(ts16);
return fd;
}
static int tty_console_init(struct debug *ctx, const char *path)
{
struct tty *tty;
int fd;
tty = malloc(sizeof(*tty));
if (!tty)
return -ENOMEM;
fd = tty_init(tty, path);
if (fd < 0) { goto cleanup_free; }
ctx->console = &tty->console;
return fd;
cleanup_free:
free(tty);
return fd;
}
int debug_init(struct debug *ctx, ...)
{
va_list args;
int rc;
va_start(args, ctx);
rc = debug_init_v(ctx, args);
va_end(args);
return rc;
}
int debug_init_v(struct debug *ctx, va_list args)
{
const char *interface;
int rc, fd;
/*
* Sanity-check presence of the password, though we also test again below
* where we use it to avoid TOCTOU.
*/
if (!getenv("AST_DEBUG_PASSWORD")) {
loge("AST_DEBUG_PASSWORD environment variable is not defined\n");
return -ENOTSUP;
}
interface = va_arg(args, const char *);
if (!interface)
return -EINVAL;
if (streq("digi,portserver-ts-16", interface)) {
fd = ts16_console_init(ctx, args);
} else {
fd = tty_console_init(ctx, interface);
}
if (fd < 0) {
loge("Failed to initialise the console (%s): %d\n", interface, fd);
return fd;
}
rc = prompt_init(&ctx->prompt, fd, "\r", false);
if (rc < 0) { goto cleanup_ts16; }
return 0;
cleanup_ts16:
console_destroy(ctx->console);
return rc;
}
int debug_destroy(struct debug *ctx)
{
int rc = 0;
rc |= prompt_destroy(&ctx->prompt);
rc |= console_destroy(ctx->console);
return rc ? -EBADF : 0;
}
int debug_enter(struct debug *ctx)
{
const char *password;
int rc;
logi("Entering debug mode\n");
password = getenv("AST_DEBUG_PASSWORD");
if (!password) {
loge("AST_DEBUG_PASSWORD environment variable is not defined\n");
return -ENOTSUP;
}
rc = console_set_baud(ctx->console, 1200);
if (rc < 0)
return rc;
rc = prompt_write(&ctx->prompt, password, strlen(password));
if (rc < 0)
goto cleanup_port_password;
rc = prompt_expect(&ctx->prompt, "$ ");
if (rc < 0)
goto cleanup_port_password;
rc = console_set_baud(ctx->console, 115200);
if (!rc)
sleep(1);
return rc;
cleanup_port_password:
console_set_baud(ctx->console, 115200);
prompt_run(&ctx->prompt, "");
return rc;
}
int debug_exit(struct debug *ctx)
{
int rc;
logi("Exiting debug mode\n");
rc = prompt_run(&ctx->prompt, "q");
if (rc < 0)
return rc;
sleep(1);
prompt_run(&ctx->prompt, "");
return console_set_baud(ctx->console, 115200);
}
int debug_probe(struct debug *ctx)
{
int rc;
logd("Probing %s\n", ahb_interface_names[ahb_debug]);
rc = debug_enter(ctx);
if (rc < 0)
return rc;
return debug_exit(ctx);
}
static ssize_t debug_parse_d(char *line, char *buf)
{
char *eoa, *words, *token, *stream;
char *saveptr;
char *cursor;
ssize_t rc;
/* Strip leading address */
eoa = strchr(line, ':');
if (!eoa)
return -EBADE;
/* Extract 4-byte values */
words = eoa + 1;
cursor = buf;
stream = strdup(words);
while ((token = strtok_r(words, " ", &saveptr))) {
rc = sscanf(token, "%02hhx%02hhx%02hhx%02hhx",
&cursor[3], &cursor[2], &cursor[1], &cursor[0]);
if (rc < 0) {
rc = -errno;
goto done;
}
if (rc < 4) {
loge("Wanted 4 but extracted %zd bytes from token '%s' in words '%s' from line '%s'\n",
rc, token, words, line);
rc = -EBADE;
goto done;
}
cursor += rc;
words = NULL;
}
rc = cursor - buf;
done:
free(stream);
return rc;
}
static int debug_read_fixed(struct debug *ctx, char mode, uint32_t phys,
uint32_t *val)
{
char buf[100], *response, *prompt;
char *command;
unsigned long parsed;
int rc;
if (!(mode == 'i' || mode == 'r'))
return -EINVAL;
rc = asprintf(&command, "%c %x", mode, phys);
if (rc < 0)
return -errno;
prompt = &buf[0];
rc = prompt_run_expect(&ctx->prompt, command, "$ ", &prompt, sizeof(buf));
free(command);
if (rc < 0)
return rc;
/* Terminate the string by overwriting the prompt */
*prompt = '\0';
/* Discard echoed response */
response = strchr(buf, ctx->prompt.eol[0]);
if (!response)
return -EIO;
/* Extract the data */
errno = 0;
parsed = strtoul(response, NULL, 16);
if (errno == ERANGE && (parsed == LONG_MAX || parsed == LONG_MIN))
return -errno;
*val = parsed;
return 0;
}
#define DEBUG_D_MAX_LEN (128 * 1024)
ssize_t debug_read(struct debug *ctx, uint32_t phys, void *buf, size_t len)
{
char line[2 * sizeof("20002ba0:31e01002 20433002 30813003 e1a06002\r\n")];
size_t remaining = len;
size_t ingress;
char *command;
char *cursor;
ssize_t rc;
if (len < 4) {
uint32_t val = 0;
cursor = buf;
while (remaining) {
rc = debug_read_fixed(ctx, 'i', phys, &val);
if (rc < 0)
return rc;
*cursor++ = val & 0xff;
remaining--;
phys++;
}
return len;
}
cursor = buf;
do {
size_t consumed;
int found;
retry:
ingress = remaining > DEBUG_D_MAX_LEN ? DEBUG_D_MAX_LEN : remaining;
rc = asprintf(&command, "d %x %zx", phys, ingress);
if (rc < 0)
return -errno;
rc = prompt_run(&ctx->prompt, command);
free(command);
if (rc < 0)
return rc;
/* Eat the echoed command */
do {
found = prompt_gets(&ctx->prompt, line, sizeof(line));
if (found < 0)
return found;
} while (!strcmp("$ \n", line)); /* Deal any prompt from a prior run */
consumed = 0;
do {
found = prompt_gets(&ctx->prompt, line, sizeof(line));
if (found < 0)
return found;
rc = debug_parse_d(line, cursor);
if (rc < 0) {
rc = prompt_run(&ctx->prompt, "");
if (rc < 0)
return rc;
rc = prompt_expect(&ctx->prompt, "$ ");
if (rc < 0)
return rc;
loge("Failed to parse line '%s'\n", line);
loge("Retrying from address 0x%"PRIx32"\n", phys);
goto retry;
}
cursor += rc;
consumed += rc;
} while (consumed < ingress);
/*
* Normally we would prompt_expect() here, but prompt_gets() has likely
* swallowed the prompt, so we'll YOLO and just assume it's done.
*/
phys += ingress;
remaining -= ingress;
} while(remaining);
return len;
}
#define DEBUG_CMD_U_MAX 128
ssize_t debug_write(struct debug *ctx, uint32_t phys, const void *buf, size_t len)
{
const void *cursor;
size_t remaining;
size_t egress;
char *command;
char mode;
int rc;
if (len <= 4) {
size_t remaining;
remaining = len;
cursor = buf;
while (remaining) {
rc = asprintf(&command, "o %x %hhx", phys, *(const uint8_t *)cursor);
if (rc < 0)
return -errno;
rc = prompt_run(&ctx->prompt, command);
if (rc < 0)
return rc;
rc = prompt_expect(&ctx->prompt, "$ ");
if (rc < 0)
return rc;
if (rc == 0)
return -EINVAL;
cursor++;
phys++;
remaining--;
}
return len;
}
mode = 'u';
remaining = len;
cursor = buf;
do {
egress = remaining > DEBUG_CMD_U_MAX ? DEBUG_CMD_U_MAX : remaining;
rc = asprintf(&command, "%c %x %zx", mode, phys, egress);
if (rc < 0)
return -errno;
rc = prompt_run(&ctx->prompt, command);
free(command);
if (rc < 0)
return rc;
rc = prompt_write(&ctx->prompt, (const char *)cursor, egress);
if (rc < 0)
return rc;
rc = prompt_expect(&ctx->prompt, "$ ");
if (rc < 0)
return rc;
phys += egress;
cursor += egress;
remaining -= egress;
} while (remaining);
return len;
}
int debug_readl(struct debug *ctx, uint32_t phys, uint32_t *val)
{
return debug_read_fixed(ctx, 'r', phys, val);
}
int debug_writel(struct debug *ctx, uint32_t phys, uint32_t val)
{
char *command;
int rc;
rc = asprintf(&command, "w %x %x", phys, val);
if (rc < 0)
return -errno;
rc = prompt_run(&ctx->prompt, command);
if (rc < 0)
return rc;
if (!((phys & ~0x20) == (AST_G5_WDT | WDT_RELOAD) && val == 0)) {
rc = prompt_expect(&ctx->prompt, "$ ");
if (rc < 0)
return rc;
if (rc == 0)
return -EINVAL;
}
return 0;
}