From cb7acb1bfef4f3fe82d263a395915a7d52e73a99 Mon Sep 17 00:00:00 2001 From: Tan Siewert Date: Wed, 3 Jun 2026 10:55:32 +0200 Subject: [PATCH 1/4] gitignore: add .cache directory The clangd LSP in Emacs configures itself to cache in the project by default. Signed-off-by: Tan Siewert --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 40a272d..879697e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ subprojects/* !subprojects/*.wrap +# LSP cache +.cache/ + # segfault core file */core From 319b86f1db1ad37d0d719c932865e9f5f168302e Mon Sep 17 00:00:00 2001 From: Tan Siewert Date: Wed, 3 Jun 2026 11:40:07 +0200 Subject: [PATCH 2/4] cmd / bridge: fix bunch of memory leaks Fix the easy stuff that valgrind has found. Signed-off-by: Tan Siewert --- src/bridge/debug.c | 3 ++- src/bridge/devmem.c | 2 +- src/bridge/ilpc.c | 2 +- src/bridge/l2a.c | 2 +- src/bridge/p2a.c | 2 +- src/cmd/probe.c | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/bridge/debug.c b/src/bridge/debug.c index f37d0c1..e00a68d 100644 --- a/src/bridge/debug.c +++ b/src/bridge/debug.c @@ -550,7 +550,7 @@ static struct ahb *debug_driver_probe(struct connection_args *connection) struct debug *ctx; int rc; - ctx = malloc(sizeof(*ctx)); + ctx = calloc(1, sizeof(*ctx)); if (!ctx) { return NULL; } @@ -558,6 +558,7 @@ static struct ahb *debug_driver_probe(struct connection_args *connection) /* Early abort if no interface at all is defined */ if (connection->interface == NULL) { logd("No interface for debug provided, skipping...\n"); + free(ctx); return NULL; } diff --git a/src/bridge/devmem.c b/src/bridge/devmem.c index a9dce5e..6911683 100644 --- a/src/bridge/devmem.c +++ b/src/bridge/devmem.c @@ -245,7 +245,7 @@ devmem_driver_probe(struct connection_args *connection __unused) struct devmem *ctx; int rc; - ctx = malloc(sizeof(*ctx)); + ctx = calloc(1, sizeof(*ctx)); if (!ctx) { return NULL; } diff --git a/src/bridge/ilpc.c b/src/bridge/ilpc.c index 6276d58..85a98af 100644 --- a/src/bridge/ilpc.c +++ b/src/bridge/ilpc.c @@ -335,7 +335,7 @@ ilpcb_driver_probe(struct connection_args *connection __unused) struct ilpcb *ctx; int rc; - ctx = malloc(sizeof(*ctx)); + ctx = calloc(1, sizeof(*ctx)); if (!ctx) { return NULL; } diff --git a/src/bridge/l2a.c b/src/bridge/l2a.c index bc62987..259e0ee 100644 --- a/src/bridge/l2a.c +++ b/src/bridge/l2a.c @@ -240,7 +240,7 @@ l2ab_driver_probe(struct connection_args *connection __unused) struct l2ab *ctx; int rc; - ctx = malloc(sizeof(*ctx)); + ctx = calloc(1, sizeof(*ctx)); if (!ctx) { return NULL; } diff --git a/src/bridge/p2a.c b/src/bridge/p2a.c index 1e07f4f..206fd2e 100644 --- a/src/bridge/p2a.c +++ b/src/bridge/p2a.c @@ -280,7 +280,7 @@ p2ab_driver_probe(struct connection_args *connection __unused) struct p2ab *ctx; int rc; - ctx = malloc(sizeof(*ctx)); + ctx = calloc(1, sizeof(*ctx)); if (!ctx) { return NULL; } diff --git a/src/cmd/probe.c b/src/cmd/probe.c index f03df09..42c2a10 100644 --- a/src/cmd/probe.c +++ b/src/cmd/probe.c @@ -141,7 +141,7 @@ static int do_probe(int argc, char **argv) host_destroy(host); done: - exit(rc); + return rc; } static const struct cmd probe_cmd = { From 80207ea4db36c69843c980848f05887478c1e6b4 Mon Sep 17 00:00:00 2001 From: Tan Siewert Date: Wed, 3 Jun 2026 10:58:39 +0200 Subject: [PATCH 3/4] debug: pass force-quit from context to connection args Signed-off-by: Tan Siewert --- src/bridge/debug.c | 2 ++ src/connection.h | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/bridge/debug.c b/src/bridge/debug.c index e00a68d..0f5765b 100644 --- a/src/bridge/debug.c +++ b/src/bridge/debug.c @@ -562,6 +562,8 @@ static struct ahb *debug_driver_probe(struct connection_args *connection) return NULL; } + ctx->force_quit = connection->force_quit; + if (!connection->internet_args) { /* Local debug interface */ if ((rc = debug_init(ctx, connection->interface)) < 0) { diff --git a/src/connection.h b/src/connection.h index 6253a66..c86013a 100644 --- a/src/connection.h +++ b/src/connection.h @@ -42,5 +42,12 @@ struct connection_args { * (IP, username, etc.) are set. */ bool internet_args; + + /** + * Blindly send an escape + quit sequence to the debug UART, before + * entering debug mode, in case a previous session was left open. + * Only relevant for the debug-uart bridge driver. + */ + bool force_quit; }; #endif From c8a953924c3d5b7eb2307e94d99efb7c8d557dcd Mon Sep 17 00:00:00 2001 From: Tan Siewert Date: Wed, 3 Jun 2026 10:59:14 +0200 Subject: [PATCH 4/4] cmd: consolidate bridge cmds into read/write cmd With the existence of the `via` keyword, we do not need separate commands anymore for each bridge. The only exception in this case is the P2A bridge, as it has some code to allow the user to specify if they want to access the AHB bridge via the VGA, or the BMC device. Closes: https://github.com/amboar/culvert/discussions/82 Signed-off-by: Tan Siewert --- src/cmd/debug.c | 192 -------------------------------------------- src/cmd/devmem.c | 135 ------------------------------- src/cmd/ilpc.c | 135 ------------------------------- src/cmd/meson.build | 3 - src/cmd/read.c | 81 +++++++++++++++---- src/cmd/write.c | 97 ++++++++++++++++------ 6 files changed, 141 insertions(+), 502 deletions(-) delete mode 100644 src/cmd/debug.c delete mode 100644 src/cmd/devmem.c delete mode 100644 src/cmd/ilpc.c diff --git a/src/cmd/debug.c b/src/cmd/debug.c deleted file mode 100644 index fb6d1b8..0000000 --- a/src/cmd/debug.c +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (C) 2018,2021 IBM Corp. - -#include "ahb.h" -#include "ast.h" -#include "bridge/debug.h" -#include "cmd.h" -#include "connection.h" -#include "host.h" -#include "log.h" - -#include -#include -#include -#include - -static char cmd_debug_args_doc[] = - " via DRIVER INTERFACE [IP PORT USERNAME PASSWORD]"; -static char cmd_debug_doc[] = "\n" - "Debug command" - "\v" - "Supported commands:\n" - " read Read data via debug UART\n" - " write Write data via debug UART\n"; - -static struct argp_option cmd_debug_options[] = { - { "force-quit", 'F', 0, 0, "Blindly exit debug mode before entering", - 0 }, - { 0 }, -}; - -struct cmd_debug_args { - struct connection_args connection; - struct ast_ahb_args ahb_args; - bool force_quit; -}; - -static error_t cmd_debug_parse_opt(int key, char *arg, struct argp_state *state) -{ - struct cmd_debug_args *arguments = state->input; - int rc; - - switch (key) { - case 'F': - arguments->force_quit = 1; - break; - case ARGP_KEY_ARG: - if (!strcmp(arg, "via")) { - rc = cmd_parse_via(state->next - 1, state, - &arguments->connection); - if (rc != 0) - argp_error( - state, - "Failed to parse connection arguments. Returned code %d", - rc); - break; - } - - switch (state->arg_num) { - case 0: - if (strcmp("read", arg) && strcmp("write", arg)) - argp_error(state, "Invalid command '%s'", arg); - - if (!strcmp("read", arg)) - arguments->ahb_args.read = true; - break; - case 1: - arguments->ahb_args.address = strtoul(arg, NULL, 0); - break; - case 2: - if (!arguments->ahb_args.read) { - static uint32_t write_val; - write_val = strtoul(arg, NULL, 0); - arguments->ahb_args.write_value = &write_val; - } else { - logd("Found third argument in read mode, ignoring...\n"); - } - break; - } - break; - case ARGP_KEY_END: - if (state->arg_num < 2) - argp_error( - state, - "Not enough arguments provided. Need at least an address."); - - if (!arguments->ahb_args.read && state->arg_num < 3) - argp_error( - state, - "Write mode detected but either no address or value detected"); - - if (arguments->connection.interface == NULL) - argp_error(state, - "Debug interface path must be defined"); - break; - default: - return ARGP_ERR_UNKNOWN; - } - - return 0; -} - -static struct argp cmd_debug_argp = { - .options = cmd_debug_options, - .parser = cmd_debug_parse_opt, - .args_doc = cmd_debug_args_doc, - .doc = cmd_debug_doc, -}; - -static int do_debug(int argc, char **argv) -{ - struct debug _debug, *debug = &_debug; - struct ahb *ahb; - int rc, cleanup; - - /* ./culvert debug read 0x1e6e207c digi,portserver-ts-16 */ - struct cmd_debug_args arguments = { 0 }; - rc = argp_parse(&cmd_debug_argp, argc, argv, ARGP_IN_ORDER, 0, - &arguments); - if (rc != 0) - return rc; - - /* Ensure that force_quit is passed to debug ctx */ - debug->force_quit = arguments.force_quit; - - logi("Initialising debug interface\n"); - if (!arguments.connection.internet_args) { - /* Local debug interface */ - if ((rc = debug_init(debug, arguments.connection.interface)) < - 0) { - loge("Failed to initialise local debug interace on %s: %d\n", - arguments.connection.interface, rc); - return rc; - } - } else { - /* Remote debug interface */ - rc = debug_init(debug, arguments.connection.interface, - arguments.connection.ip, - arguments.connection.port, - arguments.connection.username, - arguments.connection.password); - if (rc < 0) { - loge("Failed to initialise remote debug interface: %d\n", - rc); - return rc; - } - } - - if (rc < 0) { - errno = -rc; - perror("debug_init"); - return rc; - } - - rc = debug_enter(debug); - if (rc < 0) { - errno = -rc; - perror("debug_enter"); - goto destroy_ctx; - } - - ahb = debug_as_ahb(debug); - rc = ast_ahb_access(&arguments.ahb_args, ahb); - if (rc) { - errno = -rc; - perror("ast_ahb_access"); - return rc; - } - - cleanup = debug_exit(debug); - if (cleanup < 0) { - errno = -cleanup; - perror("debug_exit"); - } - -destroy_ctx: - logi("Destroying debug interface\n"); - cleanup = debug_destroy(debug); - if (cleanup < 0) { - errno = -cleanup; - perror("debug_destroy"); - } - - return rc; -} - -static const struct cmd debug_cmd = { - .name = "debug", - .description = "Read or write 4 bytes of data via the AHB bridge", - .fn = do_debug, -}; -REGISTER_CMD(debug_cmd); diff --git a/src/cmd/devmem.c b/src/cmd/devmem.c deleted file mode 100644 index bc5df9b..0000000 --- a/src/cmd/devmem.c +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (C) 2018,2021 IBM Corp. - -#include "ahb.h" -#include "ast.h" -#include "bridge/devmem.h" -#include "cmd.h" -#include "priv.h" - -#include -#include -#include -#include - -static char cmd_devmem_args_doc[] = ""; -static char cmd_devmem_doc[] = "\n" - "Devmem command" - "\v" - "Supported commands:\n" - " read Read data from /dev/mem\n" - " write Write data to /dev/mem\n"; - -static struct argp_option cmd_devmem_options[] = { - { 0 }, -}; - -struct cmd_devmem_args { - struct ast_ahb_args ahb_args; -}; - -static error_t cmd_devmem_parse_opt(int key, char *arg, - struct argp_state *state) -{ - struct cmd_devmem_args *arguments = state->input; - - switch (key) { - case ARGP_KEY_ARG: - switch (state->arg_num) { - case 0: - if (strcmp("read", arg) && strcmp("write", arg)) - argp_error(state, "Invalid command '%s'", arg); - - if (!strcmp("read", arg)) - arguments->ahb_args.read = true; - break; - case 1: - arguments->ahb_args.address = strtoul(arg, NULL, 0); - break; - case 2: - if (!arguments->ahb_args.read) { - static uint32_t write_val; - write_val = strtoul(arg, NULL, 0); - arguments->ahb_args.write_value = &write_val; - } else - logd("Found third argument in read mode, ignoring...\n"); - break; - } - break; - case ARGP_KEY_END: - if (state->arg_num < 2) - argp_error( - state, - "Not enough arguments provided. Need at least an address."); - - if (!arguments->ahb_args.read && state->arg_num < 3) - argp_error( - state, - "Write mode detected but either no address or value detected"); - break; - default: - return ARGP_ERR_UNKNOWN; - } - - return 0; -} - -static struct argp cmd_devmem_argp = { - .options = cmd_devmem_options, - .parser = cmd_devmem_parse_opt, - .args_doc = cmd_devmem_args_doc, - .doc = cmd_devmem_doc, -}; - -static int do_devmem(int argc, char **argv) -{ - struct devmem _devmem, *devmem = &_devmem; - struct ahb *ahb; - int cleanup, rc; - - struct cmd_devmem_args arguments = { 0 }; - rc = argp_parse(&cmd_devmem_argp, argc, argv, ARGP_IN_ORDER, 0, - &arguments); - if (rc != 0) - return rc; - - /* - * We do not use debug_drvier_probe here as we need data - * that's not being returned there. - */ - rc = devmem_init(devmem); - if (rc < 0) { - bool denied = (rc == -EACCES || rc == -EPERM); - if (denied && !priv_am_root()) { - priv_print_unprivileged(program_invocation_short_name); - } else { - errno = -rc; - perror("devmem_init"); - } - return rc; - } - - ahb = devmem_as_ahb(devmem); - rc = ast_ahb_access(&arguments.ahb_args, ahb); - if (rc) { - errno = -rc; - perror("ast_ahb_access"); - return rc; - } - - cleanup = devmem_destroy(devmem); - if (cleanup) { - errno = -cleanup; - perror("devmem_destroy"); - return cleanup; - } - - return 0; -} - -static const struct cmd devmem_cmd = { - .name = "devmem", - .description = "Read or write data to /dev/mem", - .fn = do_devmem, -}; -REGISTER_CMD(devmem_cmd); diff --git a/src/cmd/ilpc.c b/src/cmd/ilpc.c deleted file mode 100644 index a5e46eb..0000000 --- a/src/cmd/ilpc.c +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (C) 2018,2021 IBM Corp. - -#include "ahb.h" -#include "ast.h" -#include "bridge/ilpc.h" -#include "cmd.h" -#include "priv.h" - -#include -#include -#include -#include - -static char cmd_ilpc_args_doc[] = ""; -static char cmd_ilpc_doc[] = "\n" - "ilpc command" - "\v" - "Supported commands:\n" - " read Read data via iLPC\n" - " write Write data via iLPC\n"; - -static struct argp_option cmd_ilpc_options[] = { - { 0 }, -}; - -struct cmd_ilpc_args { - struct ast_ahb_args ahb_args; -}; - -static error_t cmd_ilpc_parse_opt(int key, char *arg, struct argp_state *state) -{ - struct cmd_ilpc_args *arguments = state->input; - - switch (key) { - case ARGP_KEY_ARG: - switch (state->arg_num) { - case 0: - if (strcmp("read", arg) && strcmp("write", arg)) - argp_error(state, "Invalid command '%s'", arg); - - if (!strcmp("read", arg)) - arguments->ahb_args.read = true; - break; - case 1: - arguments->ahb_args.address = strtoul(arg, NULL, 0); - break; - case 2: - if (!arguments->ahb_args.read) { - static uint32_t write_val; - write_val = strtoul(arg, NULL, 0); - arguments->ahb_args.write_value = &write_val; - } else { - logd("Found third argument in read mode, ignoring...\n"); - } - break; - } - break; - case ARGP_KEY_END: - if (state->arg_num < 2) - argp_error( - state, - "Not enough arguments provided. Need at least an address."); - - if (!arguments->ahb_args.read && state->arg_num < 3) - argp_error( - state, - "Write mode detected but either no address or value detected"); - break; - default: - return ARGP_ERR_UNKNOWN; - } - - return 0; -} - -static struct argp cmd_ilpc_argp = { - .options = cmd_ilpc_options, - .parser = cmd_ilpc_parse_opt, - .args_doc = cmd_ilpc_args_doc, - .doc = cmd_ilpc_doc, -}; - -static int do_ilpc(int argc, char **argv) -{ - struct ilpcb _ilpcb, *ilpcb = &_ilpcb; - struct ahb *ahb; - int cleanup, rc; - - struct cmd_ilpc_args arguments = { 0 }; - rc = argp_parse(&cmd_ilpc_argp, argc, argv, ARGP_IN_ORDER, 0, - &arguments); - if (rc != 0) - return rc; - - /* - * We do not use debug_drvier_probe here as we need data - * that's not being returned there. - */ - rc = ilpcb_init(ilpcb); - if (rc < 0) { - bool denied = (rc == -EACCES || rc == -EPERM); - if (denied && !priv_am_root()) { - priv_print_unprivileged(program_invocation_short_name); - } else { - errno = -rc; - perror("ilpcb_init"); - } - return rc; - } - - ahb = ilpcb_as_ahb(ilpcb); - rc = ast_ahb_access(&arguments.ahb_args, ahb); - if (rc) { - errno = -rc; - perror("ast_ahb_access"); - return rc; - } - - cleanup = ilpcb_destroy(ilpcb); - if (cleanup) { - errno = -cleanup; - perror("ilpcb_destroy"); - return rc; - } - - return 0; -} - -static const struct cmd ilpc_cmd = { - .name = "ilpc", - .description = "Read or write via iLPC", - .fn = do_ilpc, -}; -REGISTER_CMD(ilpc_cmd); diff --git a/src/cmd/meson.build b/src/cmd/meson.build index a956d67..4ef4836 100644 --- a/src/cmd/meson.build +++ b/src/cmd/meson.build @@ -1,9 +1,6 @@ src += files( 'console.c', 'coprocessor.c', - 'debug.c', - 'devmem.c', - 'ilpc.c', 'jtag.c', 'otp.c', 'p2a.c', diff --git a/src/cmd/read.c b/src/cmd/read.c index 573ff89..928b97c 100644 --- a/src/cmd/read.c +++ b/src/cmd/read.c @@ -23,35 +23,42 @@ #include static char cmd_read_args_doc[] = - "--type= [
] " + "--type= [[
] ] " "[via DRIVER [INTERFACE [IP PORT USERNAME PASSWORD]]]"; static char cmd_read_doc[] = "\n" "Read command" "\v" - "NOTE: Only the 'ram' type can parse address and length!\n\n" + "NOTE: Only the 'ram' type can parse both address and length!\n" + "The 'reg' type only accepts an address!\n\n" "All data will be written to stdout.\n\n" "Supported types:\n" " firmware Read the content from the FMC\n" + " reg Read the content of a specific register " + "(requires the use of the via keyword)\n" " ram Read the content from the memory\n"; enum cmd_read_mode { none, firmware, ram, + reg, }; static struct argp_option cmd_read_options[] = { { "type", 't', "TYPE", 0, "Type to be read from", 0 }, + { "force-quit", 'F', 0, 0, "Blindly exit debug mode before entering", + 0 }, { 0 }, }; struct cmd_read_args { unsigned long mem_base; unsigned long mem_size; - struct connection_args connection; enum cmd_read_mode mode; + struct connection_args connection; + struct ast_ahb_args ahb_args; }; static error_t cmd_read_parse_opt(int key, char *arg, struct argp_state *state) @@ -61,13 +68,19 @@ static error_t cmd_read_parse_opt(int key, char *arg, struct argp_state *state) switch (key) { case 't': - if (strcmp(arg, "firmware") && strcmp(arg, "ram")) + if (strcmp(arg, "firmware") && strcmp(arg, "ram") && + strcmp(arg, "reg")) argp_error(state, "Invalid type '%s'", arg); if (!strcmp(arg, "firmware")) arguments->mode = firmware; - else + else if (!strcmp(arg, "ram")) arguments->mode = ram; + else + arguments->mode = reg; + break; + case 'F': + arguments->connection.force_quit = true; break; case ARGP_KEY_ARG: if (!strcmp(arg, "via")) { @@ -76,19 +89,20 @@ static error_t cmd_read_parse_opt(int key, char *arg, struct argp_state *state) if (rc != 0) argp_error( state, - "Failed to parse connection arguments. Returned code %d", + "Failed to parse connection arguments." + " Returned code %d", rc); break; } - /* If mode is not ram, skip any further args */ - if (arguments->mode != ram) + if (arguments->mode != ram && arguments->mode != reg) break; if (state->arg_num == 0) - parse_mem_arg(state, "read RAM base", - &arguments->mem_base, arg); - else if (state->arg_num == 1) + parse_mem_arg(state, "read base", &arguments->mem_base, + arg); + + if (arguments->mode == ram && state->arg_num == 1) parse_mem_arg(state, "read RAM size", &arguments->mem_size, arg); @@ -96,11 +110,20 @@ static error_t cmd_read_parse_opt(int key, char *arg, struct argp_state *state) case ARGP_KEY_END: if (arguments->mode == none) argp_error(state, "No type to be read from defined..."); - /* It is possible to not pass the ram base and size */ + + if (arguments->mode == reg && state->arg_num < 1) + argp_error(state, + "Not enough arguments for 'reg' mode..."); + + if (arguments->mode == reg && + arguments->connection.bridge_driver == NULL) + argp_error(state, + "Connection arguments (via) missing..."); + if (arguments->mode == ram && (state->arg_num > 0 && state->arg_num < 2)) argp_error(state, - "Not enough arguments for ram mode..."); + "Not enough arguments for 'ram' mode..."); break; default: return ARGP_ERR_UNKNOWN; @@ -262,6 +285,33 @@ static int cmd_read_ram(struct cmd_read_args *arguments) return rc; } +static int cmd_read_reg(struct cmd_read_args *arguments) +{ + struct host _host, *host = &_host; + struct ahb *ahb; + int rc; + + if ((rc = host_init(host, &arguments->connection)) < 0) { + loge("Failed to initialise host interfaces: %d\n", rc); + return rc; + } + + if (!(ahb = host_get_ahb(host))) { + loge("Failed to acquire AHB interface, exiting\n"); + rc = -ENODEV; + goto cleanup_host; + } + + arguments->ahb_args.read = true; + arguments->ahb_args.address = arguments->mem_base; + rc = ast_ahb_access(&arguments->ahb_args, ahb); + +cleanup_host: + host_destroy(host); + + return rc; +} + static int do_read(int argc, char **argv) { int rc; @@ -280,6 +330,9 @@ static int do_read(int argc, char **argv) case ram: rc = cmd_read_ram(&arguments); break; + case reg: + rc = cmd_read_reg(&arguments); + break; /* If it reaches none here, then the argument parse logic is broken. */ case none: loge("read: Reached 'none' mode after argument parsing. This is a bug!\n"); @@ -292,7 +345,7 @@ static int do_read(int argc, char **argv) static const struct cmd read_cmd = { .name = "read", - .description = "Read data from the FMC or RAM", + .description = "Read data from the FMC, RAM or a register", .fn = do_read, }; REGISTER_CMD(read_cmd); diff --git a/src/cmd/write.c b/src/cmd/write.c index 662a19a..1c24a82 100644 --- a/src/cmd/write.c +++ b/src/cmd/write.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -27,35 +28,43 @@ #define SFC_FLASH_WIN (64 << 10) static char cmd_write_args_doc[] = - "--type= [
] " + "--type= [
] " "[via DRIVER [INTERFACE [IP PORT USERNAME PASSWORD]]]"; static char cmd_write_doc[] = "\n" "Write command" "\v" - "NOTE: Only the 'ram' type can parse address and length and requires them!\n\n" - "All data will be read from stdin.\n\n" + "NOTE: Both the 'ram' and 'reg' types require both " + "address and length!\n\n" + "All data for 'firmware' and 'ram' will be read from stdin.\n\n" "Supported types:\n" " firmware Write stdin to the FMC\n" + " reg Write a value to a specific register " + "(requires the use of the via keyword)\n" " ram Write stdin to the memory\n"; enum cmd_write_mode { none, firmware, ram, + reg, }; static struct argp_option cmd_write_options[] = { - { "type", 't', "TYPE", 0, "Type to be write to", 0 }, + { "type", 't', "TYPE", 0, "Type to write to", 0 }, + { "force-quit", 'F', 0, 0, "Blindly exit debug mode before entering", + 0 }, { 0 }, }; struct cmd_write_args { unsigned long mem_base; unsigned long mem_size; - struct connection_args connection; enum cmd_write_mode mode; + struct connection_args connection; + struct ast_ahb_args ahb_args; + uint32_t reg_value; }; static error_t cmd_write_parse_opt(int key, char *arg, struct argp_state *state) @@ -65,13 +74,19 @@ static error_t cmd_write_parse_opt(int key, char *arg, struct argp_state *state) switch (key) { case 't': - if (strcmp(arg, "firmware") && strcmp(arg, "ram")) + if (strcmp(arg, "firmware") && strcmp(arg, "ram") && + strcmp(arg, "reg")) argp_error(state, "Invalid type '%s'", arg); if (!strcmp(arg, "firmware")) arguments->mode = firmware; - else + else if (!strcmp(arg, "ram")) arguments->mode = ram; + else + arguments->mode = reg; + break; + case 'F': + arguments->connection.force_quit = true; break; case ARGP_KEY_ARG: if (!strcmp(arg, "via")) { @@ -80,34 +95,39 @@ static error_t cmd_write_parse_opt(int key, char *arg, struct argp_state *state) if (rc != 0) argp_error( state, - "Failed to parse connection arguments. Returned code %d", + "Failed to parse connection arguments." + " Returned code %d", rc); break; } - /* If mode is not ram, skip any further args */ - if (arguments->mode != ram) + if (arguments->mode != ram && arguments->mode != reg) break; - switch (state->arg_num) { - case 0: - parse_mem_arg(state, "write RAM base", - &arguments->mem_base, arg); - break; - case 1: + if (state->arg_num == 0) + parse_mem_arg(state, "write base", &arguments->mem_base, + arg); + + if (arguments->mode == ram && state->arg_num == 1) parse_mem_arg(state, "write RAM size", &arguments->mem_size, arg); - break; - } + + if (arguments->mode == reg && state->arg_num == 1) + arguments->reg_value = strtoul(arg, NULL, 0); break; case ARGP_KEY_END: if (arguments->mode == none) argp_error(state, "No type to write to defined..."); - /* Other than the read command,this requires the arguments for ram */ - if (arguments->mode == ram && state->arg_num < 2) + + if (arguments->mode == reg && + arguments->connection.bridge_driver == NULL) + argp_error(state, + "Connection arguments (via) missing..."); + + if (arguments->mode != firmware && state->arg_num < 2) argp_error(state, - "Not enough arguments for ram mode..."); + "Not enough arguments for selected mode..."); break; default: return ARGP_ERR_UNKNOWN; @@ -336,6 +356,34 @@ static int cmd_write_ram(struct cmd_write_args *arguments) return rc; } +static int cmd_write_reg(struct cmd_write_args *arguments) +{ + struct host _host, *host = &_host; + struct ahb *ahb; + int rc; + + if ((rc = host_init(host, &arguments->connection)) < 0) { + loge("Failed to initialise host interfaces: %d\n", rc); + return rc; + } + + if (!(ahb = host_get_ahb(host))) { + loge("Failed to acquire AHB interface, exiting\n"); + rc = -ENODEV; + goto cleanup_host; + } + + arguments->ahb_args.read = false; + arguments->ahb_args.address = arguments->mem_base; + arguments->ahb_args.write_value = &arguments->reg_value; + rc = ast_ahb_access(&arguments->ahb_args, ahb); + +cleanup_host: + host_destroy(host); + + return rc; +} + static int do_write(int argc, char **argv) { int rc; @@ -353,9 +401,12 @@ static int do_write(int argc, char **argv) case ram: rc = cmd_write_ram(&arguments); break; + case reg: + rc = cmd_write_reg(&arguments); + break; /* If it reaches none here, then the argument parse logic is broken. */ case none: - loge("read: Reached 'none' mode after argument parsing. This is a bug!\n"); + loge("write: Reached 'none' mode after argument parsing. This is a bug!\n"); rc = -EINVAL; break; } @@ -365,7 +416,7 @@ static int do_write(int argc, char **argv) static const struct cmd write_cmd = { .name = "write", - .description = "Write data to the FMC or RAM", + .description = "Write data to the FMC, RAM or a register", .fn = do_write, }; REGISTER_CMD(write_cmd);