Cover the AST1040 Caliptra mailbox execute flow through the external peer protocol and verify that the SCU CPTRA page register remaps the MCI aperture between CSR and SRAM pages.
Signed-off-by: Steven Lee <[email protected]> --- tests/qtest/aspeed_cptra_mbox-test.c | 223 +++++++++++++++++++++++++++ tests/qtest/meson.build | 3 +- 2 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/aspeed_cptra_mbox-test.c diff --git a/tests/qtest/aspeed_cptra_mbox-test.c b/tests/qtest/aspeed_cptra_mbox-test.c new file mode 100644 index 0000000000..4d0e1682da --- /dev/null +++ b/tests/qtest/aspeed_cptra_mbox-test.c @@ -0,0 +1,223 @@ +/* + * QTest for the ASPEED Caliptra mailbox on the ast1040-evb machine. + * + * Copyright (C) 2026 ASPEED Technology Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "qemu/sockets.h" + +/* AST1040 guest-visible mailbox bases. */ +#define SRAM_BASE 0x21400000ULL +#define CSR_BASE 0x21600000ULL + +#define CSR_LOCK 0x000 +#define CSR_USER 0x004 +#define CSR_CMD 0x010 +#define CSR_DLEN 0x014 +#define CSR_EXECUTE 0x018 +#define CSR_CMD_STATUS 0x020 + +#define STATUS_BUSY 0 +#define STATUS_COMPLETE 2 +#define STATUS_CMD_FAILURE 3 + +/* SCU CPTRA page-select register (SCU base 0x74C02000 + 0x120). */ +#define SCU_CPTRA_PAGE_REG0 0x74C02120ULL +/* MCI remap aperture. */ +#define MCI_WINDOW 0x74200000ULL + +/* Wire protocol. */ +#define PROTO_MAGIC 0x4D424F58u +#define PROTO_VERSION 1u +#define CMD_EXECUTE 1u +#define CMD_RESPONSE 2u + +static int peer_lfd = -1; +static int peer_fd = -1; + +static void peer_read(void *buf, size_t len) +{ + size_t off = 0; + + while (off < len) { + ssize_t r = read(peer_fd, (uint8_t *)buf + off, len - off); + g_assert_cmpint(r, >, 0); + off += r; + } +} + +static void peer_write(const void *buf, size_t len) +{ + size_t off = 0; + + while (off < len) { + ssize_t w = write(peer_fd, (const uint8_t *)buf + off, len - off); + g_assert_cmpint(w, >, 0); + off += w; + } +} + +static uint32_t ld32(const uint8_t *p) +{ + return p[0] | (p[1] << 8) | (p[2] << 16) | ((uint32_t)p[3] << 24); +} + +static void st32(uint8_t *p, uint32_t v) +{ + p[0] = v; p[1] = v >> 8; p[2] = v >> 16; p[3] = v >> 24; +} + +/* + * Service one MBOX_EXECUTE: read the request, optionally check it, then reply + * with MBOX_RESPONSE carrying @rsp_status and @rsp_data. + */ +static void peer_serve_execute(uint32_t expect_cmd, uint32_t expect_dlen, + uint32_t rsp_status, + const uint8_t *rsp_data, uint32_t rsp_dlen) +{ + uint8_t hdr[12]; + uint32_t plen, cmd, dlen; + g_autofree uint8_t *payload = NULL; + uint32_t rsp_padded = (rsp_dlen + 3) & ~3u; + uint32_t rsp_plen = 8 + rsp_padded; + g_autofree uint8_t *rsp = g_malloc0(12 + rsp_plen); + + peer_read(hdr, sizeof(hdr)); + g_assert_cmpuint(ld32(hdr), ==, PROTO_MAGIC); + g_assert_cmpuint(hdr[4] | (hdr[5] << 8), ==, PROTO_VERSION); + g_assert_cmpuint(hdr[6] | (hdr[7] << 8), ==, CMD_EXECUTE); + plen = ld32(hdr + 8); + g_assert_cmpuint(plen, >=, 8); + + payload = g_malloc(plen); + peer_read(payload, plen); + cmd = ld32(payload); + dlen = ld32(payload + 4); + g_assert_cmpuint(cmd, ==, expect_cmd); + g_assert_cmpuint(dlen, ==, expect_dlen); + + st32(rsp, PROTO_MAGIC); + rsp[4] = PROTO_VERSION; rsp[5] = 0; + rsp[6] = CMD_RESPONSE; rsp[7] = 0; + st32(rsp + 8, rsp_plen); + st32(rsp + 12, rsp_status); + st32(rsp + 16, rsp_dlen); + if (rsp_dlen) { + memcpy(rsp + 20, rsp_data, rsp_dlen); + } + peer_write(rsp, 12 + rsp_plen); +} + +static uint32_t poll_cmd_status(void) +{ + uint32_t s = STATUS_BUSY; + int tries = 1000; + + while (tries--) { + s = qtest_readl(global_qtest, CSR_BASE + CSR_CMD_STATUS); + if (s != STATUS_BUSY) { + break; + } + } + return s; +} + +/* + * Realistic mailbox transaction: acquire the lock, run an EXECUTE roundtrip + * against the peer, verify the response lands in SRAM, then release. + */ +static void test_execute(void) +{ + static const uint8_t resp[] = { + 0x11, 0xf9, 0xff, 0xff, /* checksum */ + 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, /* inner dlen = 20 */ + 'C', 'a', 'l', 'i', 'p', 't', 'r', 'a', + '_', 'C', 'o', 'r', 'e', '_', 'v', '2', '.', '0', '.', '0', + }; + uint32_t status; + + /* + * Acquire: first read returns 0, USER reflects the SoC agent, busy after. + */ + g_assert_cmpuint(qtest_readl(global_qtest, CSR_BASE + CSR_LOCK), ==, 0); + g_assert_cmpuint(qtest_readl(global_qtest, CSR_BASE + CSR_USER), ==, 1); + g_assert_cmpuint(qtest_readl(global_qtest, CSR_BASE + CSR_LOCK), ==, 1); + + qtest_writel(global_qtest, SRAM_BASE + 0, 0xfffffec0); + qtest_writel(global_qtest, CSR_BASE + CSR_CMD, 0x4d465756); + qtest_writel(global_qtest, CSR_BASE + CSR_DLEN, 8); + qtest_writel(global_qtest, CSR_BASE + CSR_EXECUTE, 1); + + peer_serve_execute(0x4d465756, 8, STATUS_COMPLETE, resp, sizeof(resp)); + + status = poll_cmd_status(); + g_assert_cmpuint(status, ==, STATUS_COMPLETE); + g_assert_cmpuint(qtest_readl(global_qtest, CSR_BASE + CSR_DLEN), + ==, sizeof(resp)); + g_assert_cmpuint(qtest_readl(global_qtest, SRAM_BASE + 0), ==, 0xfffff911); + g_assert_cmpuint(qtest_readl(global_qtest, SRAM_BASE + 8), ==, 0x14); + g_assert_cmpuint(qtest_readl(global_qtest, SRAM_BASE + 12), ==, 0x696c6143); + + /* Completing the transaction (EXECUTE 1->0) releases the lock. */ + qtest_writel(global_qtest, CSR_BASE + CSR_EXECUTE, 0); + g_assert_cmpuint(qtest_readl(global_qtest, CSR_BASE + CSR_LOCK), ==, 0); + qtest_writel(global_qtest, CSR_BASE + CSR_EXECUTE, 0); +} + +/* SCU CPTRA_PAGE_REG0 selects the target page seen through the MCI window. */ +static void test_scu_remap(void) +{ + /* Window pointed at the CSR page exposes the CMD register. */ + qtest_writel(global_qtest, CSR_BASE + CSR_CMD, 0xdeadbeef); + qtest_writel(global_qtest, SCU_CPTRA_PAGE_REG0, CSR_BASE); + g_assert_cmpuint(qtest_readl(global_qtest, MCI_WINDOW + CSR_CMD), + ==, 0xdeadbeef); + + /* Re-point at the SRAM page. */ + qtest_writel(global_qtest, SRAM_BASE + 0, 0x12345678); + qtest_writel(global_qtest, SCU_CPTRA_PAGE_REG0, SRAM_BASE); + g_assert_cmpuint(qtest_readl(global_qtest, MCI_WINDOW + 0), + ==, 0x12345678); +} + +int main(int argc, char **argv) +{ + g_autofree char *sock_path = NULL; + g_autofree char *cmdline = NULL; + int ret; + + g_test_init(&argc, &argv, NULL); + + sock_path = g_strdup_printf("%s/cptra-mbox-%u.sock", + g_get_tmp_dir(), getpid()); + unlink(sock_path); + peer_lfd = qtest_socket_server(sock_path); + + cmdline = g_strdup_printf( + "-machine ast1040-evb,cptra-peer=peer0 " + "-chardev socket,id=cptra0,path=%s " + "-device cptra-mbox-peer-extern,id=peer0,chardev=cptra0", sock_path); + qtest_start(cmdline); + + peer_fd = accept(peer_lfd, NULL, NULL); + g_assert_cmpint(peer_fd, >=, 0); + + qtest_add_func("/cptra-mbox/execute", test_execute); + qtest_add_func("/cptra-mbox/scu-remap", test_scu_remap); + + ret = g_test_run(); + + qtest_end(); + if (peer_fd >= 0) { + close(peer_fd); + } + close(peer_lfd); + unlink(sock_path); + + return ret; +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 728dde54b3..c50da850f7 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -219,7 +219,8 @@ qtests_aspeed = \ ['aspeed_gpio-test', 'aspeed_hace-test', 'aspeed_scu-test', - 'aspeed_smc-test'] + 'aspeed_smc-test', + 'aspeed_cptra_mbox-test'] qtests_aspeed64 = \ ['ast2700-gpio-test', 'ast2700-hace-test', -- 2.43.0
