Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0073485
host: rework connection handling to use common struct
sinuscosinustan Jul 17, 2025
c3132da
host: add helper to look up bridge_driver by name
sinuscosinustan Jul 19, 2025
a3909fe
ast: rework ahb access logic to use common struct
sinuscosinustan Jul 19, 2025
6172b5d
bridge: add bool for path requirements
sinuscosinustan Jul 29, 2025
308bd07
bridge: debug: Always try to exit debug first
Manawyrm Oct 14, 2024
118e3f7
bridge: debug: use connection struct
sinuscosinustan Jul 17, 2025
2c22e95
bridge: devmem: use connection struct
sinuscosinustan Jul 20, 2025
db05380
bridge: ilpc: use connection struct
sinuscosinustan Jul 20, 2025
4d535c7
bridge: l2a: use common struct
sinuscosinustan Jul 23, 2025
2b52a86
bridge: p2a: use common struct
sinuscosinustan Jul 23, 2025
0385bba
cmd: refactor to use argp instead of getopt
sinuscosinustan Jul 17, 2025
0fc70a0
cmd / host: allow explicit defining of bridge driver on via
sinuscosinustan Jul 29, 2025
a120c4f
cmd: probe: rework to argp and common connection struct
sinuscosinustan Jul 17, 2025
424448d
cmd: console: rework to argp and common connection struct
sinuscosinustan Jul 17, 2025
f9ef184
cmd: coprocessor: rework to argp and common connection struct
sinuscosinustan Jul 17, 2025
92ba2f0
cmd: debug: rework to argp and common connection struct
sinuscosinustan Jul 19, 2025
87badc5
cmd: devmem: rework to use argp
sinuscosinustan Jul 20, 2025
c1719f8
cmd: ilpc: rework to use argp
sinuscosinustan Jul 20, 2025
8052e46
cmd: p2a: rework to use argp
sinuscosinustan Jul 20, 2025
188fb33
cmd: reset: rework to argp and common connection struct
sinuscosinustan Jul 20, 2025
bd6fd7d
cmd: otp: rework for argp and common connection struct
sinuscosinustan Jul 23, 2025
ce8c0b2
cmd: sfc: rework for argp and common connection struct
sinuscosinustan Jul 23, 2025
9f68e62
cmd: read: rework to use argp and common connection struct
sinuscosinustan Jul 29, 2025
4bce908
cmd: write: rework for argp and common connection struct
sinuscosinustan Jul 30, 2025
9461899
cmd: replace: rework for argp and connection struct
sinuscosinustan Jul 30, 2025
92acb1b
cmd: trace: rework for argp and common connection struct
sinuscosinustan Jul 31, 2025
0c1a489
cmd: jtag: rework to argp and common connection struct
sinuscosinustan Jul 20, 2025
73e07ce
cmd: jtag: reformat to match editorconfig settings
sinuscosinustan Jul 20, 2025
faf5038
readme: Update after argp migration
sinuscosinustan Jul 17, 2025
14276a3
devcontainer: do not use meson-asdf
sinuscosinustan Jul 11, 2025
b9a499e
gitignore: add vscode and subprojects to list
sinuscosinustan Dec 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ RUN apt-get update && apt-get install -y \
flex \
swig \
bison \
meson \
ninja-build \
device-tree-compiler \
libyaml-dev \
cmake \
pkg-config \
python3 \
python3-pip \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

RUN pip3 install meson

ARG USERNAME=vscode
ARG GROUPNAME=vscode

Expand Down
6 changes: 2 additions & 4 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
},
"features": {
"ghcr.io/devcontainers/features/python:1": {},
"ghcr.io/devcontainers-contrib/features/meson-asdf:2": {},
"ghcr.io/devcontainers/features/git:1": {}
},
"customizations": {
Expand All @@ -22,6 +21,5 @@
"python.pythonPath": "/usr/local/bin/python3"
}
}
},
"postCreateCommand": "pip install meson"
}
}
}
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# Visual Studio Code config
.vscode/

# Subprojects pulled via meson
subprojects/*
!subprojects/*.wrap

# segfault core file
*/core

# Output files
*.d
*.o
*.swp
Expand Down
103 changes: 83 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,31 +106,44 @@ $ meson setup build-arm --cross-file meson/arm-linux-gnueabi-gcc.ini && meson co

#### Dependencies (Debian)
```
apt install build-essential flex swig bison meson device-tree-compiler libyaml-dev
apt install build-essential flex swig bison meson device-tree-compiler libyaml-dev qemu-user
```

## Execution and Example output

```
$ ./build/src/culvert -h
culvert: v0.4.0-196-g34001fbe828a
Usage:

culvert console HOST_UART BMC_UART BAUD USER PASSWORD
culvert coprocessor <run ADDRESS LENGTH|stop> [INTERFACE [IP PORT USERNAME PASSWORD]]
culvert debug <read ADDRESS|write ADDRESS VALUE> INTERFACE [IP PORT USERNAME PASSWORD]
culvert devmem <read ADDRESS|write ADDRESS VALUE>
culvert ilpc <read ADDRESS|write ADDRESS VALUE
culvert jtag [INTERFACE [IP PORT USERNAME PASSWORD]]
culvert otp <read <conf|strap>|write <conf WORD BIT|strap BIT VALUE>> [INTERFACE [IP PORT USERNAME PASSWORD]]
culvert p2a vga <read ADDRESS|write ADDRESS VALUE>
culvert probe [INTERFACE [IP PORT USERNAME PASSWORD]]
culvert read <firmware|ram ADDRESS LENGTH> [INTERFACE [IP PORT USERNAME PASSWORD]]
culvert replace ram MATCH REPLACE
culvert reset TYPE WDT [INTERFACE [IP PORT USERNAME PASSWORD]]
culvert sfc fmc <erase|read|write> ADDRESS LENGTH [INTERFACE [IP PORT USERNAME PASSWORD]]
culvert trace ADDRESS WIDTH MODE [INTERFACE [IP PORT USERNAME PASSWORD]]
culvert write <firmware|ram ADDRESS LENGTH> [INTERFACE [IP PORT USERNAME PASSWORD]]
$ ./build/src/culvert --help
Usage: culvert [OPTION...] <cmd> [CMD_OPTIONS]...

Culvert — A Test and Debug Tool for BMC AHB Interfaces

-l, --list-bridges List available bridge drivers
-q, --quiet Don't produce any output
-s, --skip-bridge=BRIDGE Skip BRIDGE driver
-v, --verbose Get verbose output
-?, --help Give this help list
--usage Give a short usage message
-V, --version Print program version

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.

Available commands:
console Start a getty on the BMC console
coprocessor Do things on the coprocessors of the AST2600
debug Read or write 4 bytes of data via the AHB bridge
devmem Read or write data to /dev/mem
ilpc Read or write via iLPC
jtag Start a JTAG OpenOCD Bitbang server
otp Read and write the OTP configuration (AST2600-only)
p2a Read or write data via p2a devices
probe Probe for any BMC
read Read data from the FMC or RAM
replace Replace a portion in the memory
reset Reset a component of the BMC chip
sfc Read, write or erase areas of a supported SFC
trace Trace what happens on a register
write Write data to the FMC or RAM
```

```
Expand Down Expand Up @@ -158,3 +171,53 @@ ilpc: Disabled
1
#
```

```
# ./culvert probe --help
Usage: culvert probe [OPTION...]
[-l] [-r REQUIREMENT] [via DRIVER [INTERFACE [IP PORT USERNAME
PASSWORD]]]

Probe command

-l, --list-interfaces List available interfaces
-r, --require=REQUIREMENT Requirement to probe for
-?, --help Give this help list
--usage Give a short usage message
-V, --version Print program version

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.

Supported requirements:
integrity Require integrity
confidentiality Require confidentiality

Report bugs to GitHub amboar/culvert.

# ./culvert probe via debug-uart /dev/ttyUSB0
[*] Opening /dev/ttyUSB0
[*] Entering debug mode
xdma: Restricted
BMC: Disabled
VGA: Enabled
XDMA on VGA: Enabled
XDMA is constrained: Yes
p2a: Permissive
BMC: Disabled
VGA: Enabled
MMIO on VGA: Enabled
[0x00000000 - 0x0fffffff] Firmware: Writable
[0x10000000 - 0x1fffffff] SoC IO: Writable
[0x20000000 - 0x2fffffff] BMC Flash: Writable
[0x30000000 - 0x3fffffff] Host Flash: Writable
[0x40000000 - 0x5fffffff] Reserved: Writable
[0x60000000 - 0x7fffffff] LPC Host: Writable
[0x80000000 - 0xffffffff] DRAM: Writable
debug: Permissive
Debug UART port: UART1
debug: Permissive
Debug UART port: UART5
ilpc: Disabled
[*] Exiting debug mode
```
54 changes: 21 additions & 33 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,62 +21,50 @@
#include <string.h>
#include <unistd.h>

int ast_ahb_access(const char *name __unused, int argc, char *argv[],
struct ahb *ahb)
/**
* Access raw data via the AHB bridge in the memory-mapped regions.
Comment thread
sinuscosinustan marked this conversation as resolved.
*
* Read behaviour: If the default read width of 4 is being overwritten,
* then the data will be written to stdout without any modification.
* This may be useful for reading FMC regions.
*
* Write behaviour: If the pointer of the write value is NULL, then it
* will need data to be written to stdin.
*/
int ast_ahb_access(struct ast_ahb_args *args, struct ahb *ahb)
{
uint32_t address, data;
bool action_read;
uint32_t data;
int rc;

if (argc < 2) {
loge("Not enough arguments for AHB access\n");
exit(EXIT_FAILURE);
}

if (!strcmp("read", argv[0]))
action_read = true;
else if (!strcmp("write", argv[0]))
action_read = false;
else {
loge("Unknown action: %s\n", argv[0]);
exit(EXIT_FAILURE);
}

address = strtoul(argv[1], NULL, 0);

if (action_read) {
if (args->read) {
unsigned long len = 4;

if (argc >= 3) {
len = strtoul(argv[2], NULL, 0);
}
if (args->read_length != NULL)
len = *args->read_length;

if (len > 4) {
if ((rc = ahb_siphon_out(ahb, address, len, STDOUT_FILENO))) {
if ((rc = ahb_siphon_out(ahb, args->address, len, STDOUT_FILENO))) {
errno = -rc;
perror("ahb_siphon_in");
exit(EXIT_FAILURE);
}
} else {
if ((rc = ahb_readl(ahb, address, &data))) {
if ((rc = ahb_readl(ahb, args->address, &data))) {
errno = -rc;
perror("ahb_readl");
exit(EXIT_FAILURE);
}
printf("0x%08x: 0x%08x\n", address, le32toh(data));
printf("0x%08x: 0x%08x\n", args->address, le32toh(data));
}
} else {
unsigned long data;

if (argc >= 3) {
data = strtoul(argv[2], NULL, 0);
if ((rc = ahb_writel(ahb, address, htole32(data)))) {
if (args->write_value != NULL) {
if ((rc = ahb_writel(ahb, args->address, htole32(*args->write_value)))) {
errno = -rc;
perror("ahb_writel");
exit(EXIT_FAILURE);
}
} else {
if ((rc = ahb_siphon_in(ahb, address, -1, STDIN_FILENO))) {
if ((rc = ahb_siphon_in(ahb, args->address, -1, STDIN_FILENO))) {
errno = -rc;
perror("ahb_writel");
exit(EXIT_FAILURE);
Expand Down
21 changes: 20 additions & 1 deletion src/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@

#include "ahb.h"

int ast_ahb_access(const char *name, int argc, char *argv[], struct ahb *ahb);
#include <stdbool.h>
#include <stdlib.h>

struct ast_ahb_args {
/**
* read_length defines the width of an address to be read and is
* not always required.
*/
uint32_t *read_length;

/**
* write_value is only required for bit-wise operations like register
* modifications. For binary data, please consider to pipe the data to stdin.
*/
uint32_t *write_value;
uint32_t address;
bool read;
};

int ast_ahb_access(struct ast_ahb_args *args, struct ahb *ahb);

#endif
6 changes: 5 additions & 1 deletion src/bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@
#include <stdbool.h>

#include "ahb.h"
#include "connection.h"

#include "ccan/autodata/autodata.h"

struct bridge_driver {
const char *name;
struct ahb *(*probe)(int argc, char *argv[]);
struct ahb *(*probe)(struct connection_args *connection);
int (*release)(struct ahb *ahb);
int (*reinit)(struct ahb *ahb);
void (*destroy)(struct ahb *ahb);

/* Whether or not an explicit device path is required (i.e. debug-uart) */
bool path_required;

/*
* Whether or not this driver is for running culvert on the BMC itself
* (i.e. devmem)
Expand Down
42 changes: 31 additions & 11 deletions src/bridge/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "ast.h"
#include "bridge.h"
#include "console.h"
#include "connection.h"
#include "debug.h"
#include "log.h"
#include "prompt.h"
Expand Down Expand Up @@ -44,8 +45,23 @@ int debug_enter(struct debug *ctx)
{
int rc;

if (ctx->force_quit) {
logi("Foce quit requested\n");
rc = console_set_baud(ctx->console, 115200);
if (rc < 0)
return rc;

// Escape character should cancel previous commands
// q will exit debug mode
rc = prompt_write(&ctx->prompt,
"\x1Bq\r\n\x1Bq\r\n", strlen("\x1Bq\r\n\x1Bq\r\n"));
if (rc < 0)
return rc;
}

logi("Entering debug mode\n");

// Enter debug mode
rc = console_set_baud(ctx->console, 1200);
if (rc < 0)
return rc;
Expand Down Expand Up @@ -457,7 +473,7 @@ static const struct ahb_ops debug_ahb_ops = {
.writel = debug_writel
};

static struct ahb *debug_driver_probe(int argc, char *argv[]);
static struct ahb *debug_driver_probe(struct connection_args *connection);
static void debug_driver_destroy(struct ahb *ahb);
static int debug_driver_release(struct ahb *ahb);
static int debug_driver_reinit(struct ahb *ahb);
Expand All @@ -468,6 +484,7 @@ static struct bridge_driver debug_driver = {
.destroy = debug_driver_destroy,
.release = debug_driver_release,
.reinit = debug_driver_reinit,
.path_required = true,
};
REGISTER_BRIDGE_DRIVER(debug_driver);

Expand Down Expand Up @@ -515,7 +532,7 @@ int debug_destroy(struct debug *ctx)
return rc ? -EBADF : 0;
}

static struct ahb *debug_driver_probe(int argc, char *argv[])
static struct ahb *debug_driver_probe(struct connection_args *connection)
{
struct debug *ctx;
int rc;
Expand All @@ -525,24 +542,27 @@ static struct ahb *debug_driver_probe(int argc, char *argv[])
return NULL;
}

if (argc == 1) {
/* Early abort if no interface at all is defined */
if (connection->interface == NULL) {
logd("No interface for debug provided, skipping...\n");
return NULL;
}

if (!connection->internet_args) {
/* Local debug interface */
if ((rc = debug_init(ctx, argv[0])) < 0) {
if ((rc = debug_init(ctx, connection->interface)) < 0) {
loge("Failed to initialise local debug interace on %s: %d\n",
argv[0], rc);
connection->interface, rc);
goto cleanup_ctx;
}
} else if (argc == 5) {
} else {
/* Remote debug interface */
rc = debug_init(ctx, argv[0], argv[1], strtoul(argv[2], NULL, 0),
argv[3], argv[4]);
rc = debug_init(ctx, connection->interface, connection->ip, connection->port,
connection->username, connection->password);
if (rc < 0) {
loge("Failed to initialise remote debug interface: %d\n", rc);
goto cleanup_ctx;
}
} else {
logd("Unrecognised argument list for debug interface (%d)\n", argc);
return NULL;
}

if ((rc = debug_enter(ctx)) < 0) {
Expand Down
Loading