From 44bcb6739898a0d9c80c65028864f4ccfcce2c63 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Fri, 12 Apr 2024 16:21:52 +0930 Subject: [PATCH] Add coprocessor subcommand 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 --- src/cmd/coprocessor.c | 209 ++++++++++++++++++++++++++++++++++++++++++ src/cmd/meson.build | 1 + src/culvert.c | 3 + 3 files changed, 213 insertions(+) create mode 100644 src/cmd/coprocessor.c diff --git a/src/cmd/coprocessor.c b/src/cmd/coprocessor.c new file mode 100644 index 0000000..180cdcd --- /dev/null +++ b/src/cmd/coprocessor.c @@ -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 +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/cmd/meson.build b/src/cmd/meson.build index 28f4709..2c612c3 100644 --- a/src/cmd/meson.build +++ b/src/cmd/meson.build @@ -1,4 +1,5 @@ src += files('console.c', + 'coprocessor.c', 'debug.c', 'devmem.c', 'ilpc.c', diff --git a/src/culvert.c b/src/culvert.c index ad00a41..d50b1b6 100644 --- a/src/culvert.c +++ b/src/culvert.c @@ -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) { @@ -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 { @@ -87,6 +89,7 @@ static const struct command cmds[] = { { "sfc", cmd_sfc }, { "otp", cmd_otp }, { "trace", cmd_trace }, + { "coprocessor", cmd_coprocessor}, { }, };