Skip to content

Commit

Permalink
Add coprocessor subcommand
Browse files Browse the repository at this point in the history
Useful for launching firmware on the coprocessor without requiring
kernel support on the main BMC core.

```
root@bmc:~ # ./culvert coprocessor run 0xba000000 $((32 * 1024 * 1024)) < zephyr.bin
```

Yields the following output on UART11:

```
*** Booting Zephyr OS build v00.02.01 ***
Hello World! ast2600_ssp_evb
```

Signed-off-by: Andrew Jeffery <[email protected]>
  • Loading branch information
amboar committed Apr 15, 2024
1 parent 9e29560 commit 44bcb67
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 0 deletions.
209 changes: 209 additions & 0 deletions src/cmd/coprocessor.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2024 Code Construct

#include "bits.h"
#include "compiler.h"
#include "host.h"
#include "log.h"
#include "rev.h"
#include "soc.h"
#include "soc/scu.h"
#include "soc/sdmc.h"

#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define COPROC_CACHED_MEM_SIZE (16 * 1024 * 1024)
#define COPROC_TOTAL_MEM_SIZE (32 * 1024 * 1024)

#define SCU_COPROC_CTRL 0xa00
#define SCU_COPROC_CTRL_RESET_ASSERT BIT(1)
#define SCU_COPROC_CTRL_EN BIT(0)

#define SCU_COPROC_MEM_BASE 0xa04
#define SCU_COPROC_IMEM_LIMIT 0xa08
#define SCU_COPROC_DMEM_LIMIT 0xa0c
#define SCU_COPROC_CACHE_RANGE 0xa40
#define SCU_COPROC_CACHE_1ST_16MB_EN BIT(0)

int cmd_coprocessor(const char *name __unused, int argc, char *argv[])
{
const char *arg_subcmd, *arg_mem_base, *arg_mem_size;
struct host _host, *host = &_host;
unsigned long mem_base, mem_size;
struct soc _soc, *soc = &_soc;
struct soc_region dram;
struct sdmc *sdmc;
struct ahb *ahb;
struct scu *scu;
ssize_t src;
char *endp;
int rc;

if (argc < 3) {
loge("Not enough arguments for coprocessor command\n");
return EXIT_FAILURE;
}

arg_subcmd = argv[0];
arg_mem_base = argv[1];
arg_mem_size = argv[2];

if (strcmp("run", arg_subcmd)) {
loge("Unknown coprocessor subcommand '%s'\n", arg_subcmd);
return EXIT_FAILURE;
}

errno = 0;
mem_base = strtoul(arg_mem_base, &endp, 0);
if (mem_base == ULONG_MAX && errno) {
loge("Failed to parse coprocessor RAM base '%s': %d\n", arg_mem_base, errno);
return EXIT_FAILURE;
} else if (arg_mem_base == endp || *endp) {
loge("Failed to parse coprocessor RAM base '%s'\n", arg_mem_base);
return EXIT_FAILURE;
}

errno = 0;
mem_size = strtoul(arg_mem_size, &endp, 0);
if (mem_size == ULONG_MAX && errno) {
loge("Failed to parse coprocessor RAM size '%s': %d\n", arg_mem_size, errno);
} else if (arg_mem_size == endp || *endp) {
loge("Failed to parse coprocessor RAM size '%s'\n", arg_mem_size);
return EXIT_FAILURE;
}

if (mem_size != COPROC_TOTAL_MEM_SIZE) {
loge("We currently only support assigning 32M of memory to the coprocessor\n");
return EXIT_FAILURE;
}

if ((rc = host_init(host, argc - 3, argv + 3)) < 0) {
loge("Failed to initialise host interface: %d\n", rc);
return EXIT_FAILURE;
}

if (!(ahb = host_get_ahb(host))) {
loge("Failed to acquire AHB interface\n");
rc = EXIT_FAILURE;
goto cleanup_host;
}

if ((rc = soc_probe(soc, ahb)) < 0) {
loge("Failed to probe SoC: %d\n", rc);
rc = EXIT_FAILURE;
goto cleanup_host;
}

if (soc_generation(soc) != ast_g6) {
loge("We currently only support the AST2600-series coprocessor\n");
rc = EXIT_FAILURE;
goto cleanup_soc;
}

if (!(sdmc = sdmc_get(soc))) {
loge("Failed to acquire SDRAM memory controller\n");
rc = EXIT_FAILURE;
goto cleanup_soc;
}

if ((rc = sdmc_get_dram(sdmc, &dram))) {
loge("Failed to locate DRAM: %d\n", rc);
rc = EXIT_FAILURE;
goto cleanup_soc;
}

#if ULONG_MAX > UINT32_MAX
if (mem_base > UINT32_MAX) {
loge("Provided RAM base 0x%ux exceeds SoC physical address space\n", mem_base);
rc = EXIT_FAILURE;
goto cleanup_soc;
}
#endif

if (((mem_base + mem_size) & UINT32_MAX) < mem_base) {
loge("Invalid RAM region provided for coprocessor\n");
rc = EXIT_FAILURE;
goto cleanup_soc;
}

if (mem_base < dram.start || (mem_base + mem_size) > (dram.start + dram.length)) {
loge("Ill-formed RAM region provided for coprocessor\n");
rc = EXIT_FAILURE;
goto cleanup_soc;
}

if (!(scu = scu_get(soc))) {
loge("Failed to acquire SCU driver\n");
rc = EXIT_FAILURE;
goto cleanup_soc;
}

if ((rc = scu_writel(scu, SCU_COPROC_CTRL, 0)) < 0) {
loge("Failed to disable coprocoessor: %d\n", rc);
rc = EXIT_FAILURE;
goto cleanup_scu;
}

/* TODO: Verify firmware fits inside specified region, somehow? */
if ((src = soc_siphon_out(soc, mem_base, STDIN_FILENO)) < 0) {
loge("Failed to load coprocessor firmware to provided region: %d\n", src);
rc = EXIT_FAILURE;
goto cleanup_scu;
}

if (scu_writel(scu, SCU_COPROC_MEM_BASE, mem_base) ||
scu_writel(scu, SCU_COPROC_IMEM_LIMIT,
mem_base + COPROC_CACHED_MEM_SIZE) ||
scu_writel(scu, SCU_COPROC_DMEM_LIMIT, mem_base + mem_size) ||
scu_writel(scu, SCU_COPROC_CACHE_RANGE, SCU_COPROC_CACHE_1ST_16MB_EN)) {
loge("Failed to configure coprocessor control registers\n");
rc = EXIT_FAILURE;
goto cleanup_scu;
}

if ((rc = scu_writel(scu, SCU_COPROC_CTRL, SCU_COPROC_CTRL_RESET_ASSERT)) < 0) {
loge("Failed to assert the coprocessor reset: %d", rc);
rc = EXIT_FAILURE;
goto cleanup_scu;
}

if (usleep(1000) == -1) {
loge("Coprocessor reset pre-delay failed: %d", -errno);
rc = EXIT_FAILURE;
goto cleanup_scu;
}

if ((rc = scu_writel(scu, SCU_COPROC_CTRL, 0)) < 0) {
loge("Failed to disable coprocessor: %d\n", rc);
}

if (usleep(1000) == -1) {
loge("Coprocessor reset post-delay failed: %d", -errno);
rc = EXIT_FAILURE;
goto cleanup_scu;
}

if ((rc = scu_writel(scu, SCU_COPROC_CTRL, SCU_COPROC_CTRL_EN)) < 0) {
loge("Failed to start coprocessor: %d\n", rc);
rc = EXIT_FAILURE;
goto cleanup_scu;
}

rc = EXIT_SUCCESS;

cleanup_scu:
scu_put(scu);

cleanup_soc:
soc_destroy(soc);

cleanup_host:
host_destroy(host);

return rc;
}
1 change: 1 addition & 0 deletions src/cmd/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
src += files('console.c',
'coprocessor.c',
'debug.c',
'devmem.c',
'ilpc.c',
Expand Down
3 changes: 3 additions & 0 deletions src/culvert.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ int cmd_reset(const char *name, int argc, char *argv[]);
int cmd_sfc(const char *name, int argc, char *argv[]);
int cmd_otp(const char *name, int argc, char *argv[]);
int cmd_trace(const char *name, int argc, char *argv[]);
int cmd_coprocessor(const char *name, int argc, char *argv[]);

static void print_version(const char *name)
{
Expand Down Expand Up @@ -66,6 +67,7 @@ static void print_help(const char *name)
printf("%s otp write strap BIT VALUE [INTERFACE [IP PORT USERNAME PASSWORD]]\n", name);
printf("%s otp write conf WORD BIT [INTERFACE [IP PORT USERNAME PASSWORD]]\n", name);
printf("%s trace ADDRESS WIDTH MODE [INTERFACE [IP PORT USERNAME PASSWORD]]\n", name);
printf("%s coprocessor run ADDRESS LENGTH [INTERFACE [IP PORT USERNAME PASSWORD]]\n", name);
}

struct command {
Expand All @@ -87,6 +89,7 @@ static const struct command cmds[] = {
{ "sfc", cmd_sfc },
{ "otp", cmd_otp },
{ "trace", cmd_trace },
{ "coprocessor", cmd_coprocessor},
{ },
};

Expand Down

0 comments on commit 44bcb67

Please sign in to comment.