RE: [PATCH v3 05/16] tests/qtest/aspeed-hace: Test the crypto command on the AST2600

Kane Chen <[email protected]>
Newsgroups org.nongnu.qemu-arm,org.nongnu.qemu-devel
Message-ID <SI6PR06MB76318CB68A5D38535AEA4C73F7DD2@SI6PR06MB7631.apcprd06.prod.outlook.com>
> -----Original Message-----
> From: Jamin Lin <[email protected]>
> Sent: Tuesday, August 11, 2026 11:11 AM
> To: Daniel P. Berrangé <[email protected]>; Cédric Le Goater
> <[email protected]>; Peter Maydell <[email protected]>; Steven Lee
> <[email protected]>; Troy Lee <[email protected]>; Kane Chen
> <[email protected]>; Andrew Jeffery
> <[email protected]>; Joel Stanley <[email protected]>; Eric Blake
> <[email protected]>; Markus Armbruster <[email protected]>; Fabiano
> Rosas <[email protected]>; Laurent Vivier <[email protected]>; Paolo Bonzini
> <[email protected]>; open list:All patches CC here
> <[email protected]>; open list:ASPEED BMCs
> <[email protected]>
> Cc: Jamin Lin <[email protected]>; Troy Lee
> <[email protected]>
> Subject: [PATCH v3 05/16] tests/qtest/aspeed-hace: Test the crypto command
> on the AST2600
> 
> Extend the crypto known-answer tests to cover the AST2600 crypto engine,
> which drives the source and destination through scatter-gather lists and adds
> CTR mode on top of the ECB/CBC modes shared with the AST2500.
> 
> Add a scatter-gather runner that describes each buffer with three non-adjacent
> fragments to exercise the gather/scatter path, add AES/DES/3DES CTR vectors
> (verifying the counter written back to the context buffer), and give
> aspeed_add_crypto_tests() a mode mask and a scatter-gather flag so each SoC
> registers exactly the modes and transfer method it supports. Register the
> AST2600 with ECB/CBC/CTR in scatter-gather mode.
> 
> Signed-off-by: Jamin Lin <[email protected]>
> ---
>  tests/qtest/aspeed-hace-utils.h |   7 +-
>  tests/qtest/aspeed-hace-utils.c | 217 +++++++++++++++++++++++++++++++-
>  tests/qtest/aspeed_hace-test.c  |   8 +-
>  3 files changed, 224 insertions(+), 8 deletions(-)
> 
> diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h
> index 13feaa61e4..82b0b3f93d 100644
> --- a/tests/qtest/aspeed-hace-utils.h
> +++ b/tests/qtest/aspeed-hace-utils.h
> @@ -85,15 +85,18 @@ void aspeed_test_addresses(const char *machine,
> const uint32_t base,  enum {
>      CRYPT_MODE_ECB = 1 << 0,
>      CRYPT_MODE_CBC = 1 << 1,
> +    CRYPT_MODE_CTR = 1 << 2,
>  };
> 
>  /*
>   * Register the crypto known-answer tests that @modes selects (a mask of
>   * CRYPT_MODE_*) for the given machine. Each test is named
> - * "<prefix>/hace/crypto/<mode>".
> + * "<prefix>/hace/crypto/<mode>". @sg selects scatter-gather mode (used
> + by the
> + * AST2600 and later) instead of the AST2500 direct access mode.
>   */
>  void aspeed_add_crypto_tests(const char *prefix, const char *machine,
> -                             uint32_t base, uint64_t dram, uint32_t
> modes);
> +                             uint32_t base, uint64_t dram, uint32_t
> modes,
> +                             bool sg);
> 
>  #endif /* TESTS_ASPEED_HACE_UTILS_H */
> 
> diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c
> index 0355dd47af..f582c88ef5 100644
> --- a/tests/qtest/aspeed-hace-utils.c
> +++ b/tests/qtest/aspeed-hace-utils.c
> @@ -665,6 +665,7 @@ void aspeed_test_addresses(const char *machine,
> const uint32_t base,
>  #define HACE_CMD_OP_MODE_MASK    (0x7 << 4)
>  #define HACE_CMD_ECB             (0x0 << 4)
>  #define HACE_CMD_CBC             (0x1 << 4)
> +#define HACE_CMD_CTR             (0x4 << 4)
>  #define HACE_CMD_AES128          (0x0 << 2)
> 
>  /* Context buffer layout: IV (DES at +8), key at +0x10 */ @@ -750,6 +751,49
> @@ static const uint8_t tdes_cbc_ptext[8] = {  static const uint8_t
> tdes_cbc_ctext[8] = {
>      0x0e, 0x2d, 0xb6, 0x97, 0x3c, 0x56, 0x33, 0xf4 };
> 
> +/* aes_ctr_tv_template[0] (NIST SP800-38A F.5.1), first block */ static
> +const uint8_t aes128_ctr_key[16] = {
> +    0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
> +    0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; static const
> +uint8_t aes128_ctr_iv[16] = {
> +    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
> +    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; static const
> +uint8_t aes128_ctr_ptext[16] = {
> +    0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
> +    0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a }; static const
> +uint8_t aes128_ctr_ctext[16] = {
> +    0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
> +    0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce }; static const
> +uint8_t aes128_ctr_ivout[16] = {
> +    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
> +    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xff, 0x00 };
> +
> +/* des_ctr_tv_template[0] (Crypto++), first block */ static const
> +uint8_t des_ctr_key[8] = {
> +    0xc9, 0x83, 0xa6, 0xc9, 0xec, 0x0f, 0x32, 0x55 }; static const
> +uint8_t des_ctr_iv[8] = {
> +    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd }; static const
> +uint8_t des_ctr_ptext[8] = {
> +    0x50, 0xb9, 0x22, 0xae, 0x17, 0x80, 0x0c, 0x75 }; static const
> +uint8_t des_ctr_ctext[8] = {
> +    0x2f, 0x96, 0x06, 0x0f, 0x50, 0xc9, 0x68, 0x03 }; static const
> +uint8_t des_ctr_ivout[8] = {
> +    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe };
> +
> +/* des3_ede_ctr_tv_template[0] (Crypto++), first block */ static const
> +uint8_t tdes_ctr_key[24] = {
> +    0x9c, 0xd6, 0xf3, 0x9c, 0xb9, 0x5a, 0x67, 0x00,
> +    0x5a, 0x67, 0x00, 0x2d, 0xce, 0xeb, 0x2d, 0xce,
> +    0xeb, 0xb4, 0x51, 0x72, 0xb4, 0x51, 0x72, 0x1f }; static const
> +uint8_t tdes_ctr_iv[8] = {
> +    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static const
> +uint8_t tdes_ctr_ptext[8] = {
> +    0x05, 0xec, 0x77, 0xfb, 0x42, 0xd5, 0x59, 0x20 }; static const
> +uint8_t tdes_ctr_ctext[8] = {
> +    0x07, 0xc2, 0x08, 0x20, 0x72, 0x1f, 0x49, 0xef }; static const
> +uint8_t tdes_ctr_ivout[8] = {
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
> +
>  typedef struct CryptTest {
>      QCryptoCipherMode mode;
>      QCryptoCipherAlgo alg;
> @@ -841,12 +885,65 @@ static const CryptTest crypt_tests[] = {
>          .ctext = tdes_cbc_ctext,
>          .len = sizeof(tdes_cbc_ptext),
>      },
> +    {
> +        .name = "aes128-ctr",
> +        .cmd = HACE_CMD_AES128 | HACE_CMD_CTR,
> +        .alg = QCRYPTO_CIPHER_ALGO_AES_128,
> +        .mode = QCRYPTO_CIPHER_MODE_CTR,
> +        .key = aes128_ctr_key,
> +        .keylen = sizeof(aes128_ctr_key),
> +        .iv = aes128_ctr_iv,
> +        .ivlen = sizeof(aes128_ctr_iv),
> +        .ptext = aes128_ctr_ptext,
> +        .ctext = aes128_ctr_ctext,
> +        .iv_out = aes128_ctr_ivout,
> +        .len = sizeof(aes128_ctr_ptext),
> +    },
> +    {
> +        .name = "des-ctr",
> +        .cmd = HACE_CMD_DES_SELECT | HACE_CMD_CTR,
> +        .alg = QCRYPTO_CIPHER_ALGO_DES,
> +        .mode = QCRYPTO_CIPHER_MODE_CTR,
> +        .key = des_ctr_key,
> +        .keylen = sizeof(des_ctr_key),
> +        .iv = des_ctr_iv,
> +        .ivlen = sizeof(des_ctr_iv),
> +        .ptext = des_ctr_ptext,
> +        .ctext = des_ctr_ctext,
> +        .iv_out = des_ctr_ivout,
> +        .len = sizeof(des_ctr_ptext),
> +    },
> +    {
> +        .name = "des3_ede-ctr",
> +        .cmd = HACE_CMD_DES_SELECT | HACE_CMD_TRIPLE_DES |
> HACE_CMD_CTR,
> +        .alg = QCRYPTO_CIPHER_ALGO_3DES,
> +        .mode = QCRYPTO_CIPHER_MODE_CTR,
> +        .key = tdes_ctr_key,
> +        .keylen = sizeof(tdes_ctr_key),
> +        .iv = tdes_ctr_iv,
> +        .ivlen = sizeof(tdes_ctr_iv),
> +        .ptext = tdes_ctr_ptext,
> +        .ctext = tdes_ctr_ctext,
> +        .iv_out = tdes_ctr_ivout,
> +        .len = sizeof(tdes_ctr_ptext),
> +    },
>  };
> 
>  /* DRAM offsets for the crypto test source, destination and context buffers. */
>  #define CRYPT_OFF_SRC   0x10000
>  #define CRYPT_OFF_DST   0x20000
>  #define CRYPT_OFF_CTX   0x30000
> +/* Scatter-gather list offsets (each list has CRYPT_SG_FRAGS entries).
> +*/ #define CRYPT_OFF_SRC_SG  0x40000 #define CRYPT_OFF_DST_SG
> 0x50000
> +/*
> + * The scatter-gather tests split each buffer into CRYPT_SG_FRAGS
> +fragments,
> + * each placed CRYPT_SG_FRAG_STRIDE apart so the fragments never abut.
> +The gaps
> + * make the test fail if the engine ignores the list and reads one
> +contiguous
> + * block.
> + */
> +#define CRYPT_SG_FRAGS         3
> +#define CRYPT_SG_FRAG_STRIDE   0x1000
> 
>  /* Describes one registered crypto test (qtest_add_data_func() data pointer).
> */  typedef struct AspeedCryptoTest { @@ -854,6 +951,7 @@ typedef struct
> AspeedCryptoTest {
>      uint64_t dram;
>      uint32_t base;
>      int index;
> +    bool sg;
>  } AspeedCryptoTest;
> 
>  /* Map a command's operation mode (HACE10[6:4]) to a CRYPT_MODE_*
> flag. */ @@ -864,6 +962,8 @@ static uint32_t crypt_mode_flag(uint32_t cmd)
>          return CRYPT_MODE_ECB;
>      case HACE_CMD_CBC:
>          return CRYPT_MODE_CBC;
> +    case HACE_CMD_CTR:
> +        return CRYPT_MODE_CTR;
>      default:
>          return 0;
>      }
> @@ -912,7 +1012,104 @@ static void crypt_run_direct(QTestState *s, uint32_t
> base, uint64_t dram,
>      qtest_memread(s, dst, out, t->len);  }
> 
> -static void aspeed_test_crypto_direct(const void *data)
> +/*
> + * Byte range [*frag_off, *frag_off + *frag_len) of fragment @index
> +when an
> + * @len-byte buffer is split into CRYPT_SG_FRAGS pieces; the last piece
> +takes
> + * the remainder of an uneven split.
> + */
> +static void crypt_frag_range(uint32_t len, int index,
> +                             uint32_t *frag_off, uint32_t *frag_len) {
> +    uint32_t base = len / CRYPT_SG_FRAGS;
> +
> +    *frag_off = base * index;
> +    *frag_len = (index == CRYPT_SG_FRAGS - 1) ? len - *frag_off : base;
> +}
> +
> +/*
> + * Scatter [in, len) across CRYPT_SG_FRAGS buffers based at @base_off
> +and spaced
> + * CRYPT_SG_FRAG_STRIDE apart, then build the SG list describing them at
> @list.
> + * When @in is NULL only the list is built (used for the destination,
> +which the
> + * engine fills in).
> + */
> +static void crypt_make_sg(QTestState *s, uint64_t dram, uint32_t base_off,
> +                          uint64_t list, const uint8_t *in, uint32_t
> +len) {
> +    struct AspeedSgList sg[CRYPT_SG_FRAGS];
> +    uint32_t frag_off;
> +    uint32_t frag_len;
> +    uint64_t buf;
> +    int i;
> +
> +    for (i = 0; i < CRYPT_SG_FRAGS; i++) {
> +        crypt_frag_range(len, i, &frag_off, &frag_len);
> +        buf = dram + base_off + i * CRYPT_SG_FRAG_STRIDE;
> +
> +        if (in) {
> +            qtest_memwrite(s, buf, in + frag_off, frag_len);
> +        }
> +        sg[i].len = cpu_to_le32(frag_len | (i == CRYPT_SG_FRAGS - 1 ?
> +                                            SG_LIST_LEN_LAST :
> 0));
> +        sg[i].addr = cpu_to_le32((uint32_t)buf);
> +    }
> +
> +    qtest_memwrite(s, list, sg, sizeof(sg)); }
> +
> +/* Gather a scatter-gathered result back from the CRYPT_SG_FRAGS
> +buffers. */ static void crypt_gather_sg(QTestState *s, uint64_t dram, uint32_t
> base_off,
> +                            uint8_t *out, uint32_t len) {
> +    uint32_t frag_off;
> +    uint32_t frag_len;
> +    int i;
> +
> +    for (i = 0; i < CRYPT_SG_FRAGS; i++) {
> +        crypt_frag_range(len, i, &frag_off, &frag_len);
> +        qtest_memread(s, dram + base_off + i * CRYPT_SG_FRAG_STRIDE,
> +                      out + frag_off, frag_len);
> +    }
> +}
> +
> +/*
> + * Run one block-cipher (ECB/CBC/CTR) operation in scatter-gather mode
> +and read
> + * back the result. The source and destination are each split across
> + * CRYPT_SG_FRAGS non-adjacent DRAM buffers described by an SG list;
> +the gaps
> + * ensure the test fails if the engine ignores the list and reads one
> + * contiguous block.
> + */
> +static void crypt_run_sg(QTestState *s, uint32_t base, uint64_t dram,
> +                         const CryptTest *t, bool encrypt, uint8_t
> +*out) {
> +    const uint8_t *in = encrypt ? t->ptext : t->ctext;
> +    uint64_t src_sg = dram + CRYPT_OFF_SRC_SG;
> +    uint64_t dst_sg = dram + CRYPT_OFF_DST_SG;
> +    uint64_t ctx = dram + CRYPT_OFF_CTX;
> +    uint32_t cmd = t->cmd | HACE_CMD_ISR_EN |
> HACE_CMD_SRC_SG_CTRL |
> +                   HACE_CMD_DST_SG_CTRL;
> +
> +    if (encrypt) {
> +        cmd |= HACE_CMD_ENCRYPT;
> +    }
> +
> +    crypt_write_ctx(s, ctx, t);
> +    crypt_make_sg(s, dram, CRYPT_OFF_SRC, src_sg, in, t->len);
> +    crypt_make_sg(s, dram, CRYPT_OFF_DST, dst_sg, NULL, t->len);
> +
> +    qtest_writel(s, base + HACE_CRYPTO_SRC, (uint32_t)src_sg);
> +    qtest_writel(s, base + HACE_CRYPTO_DEST, (uint32_t)dst_sg);
> +    qtest_writel(s, base + HACE_CRYPTO_CONTEXT, (uint32_t)ctx);
> +    qtest_writel(s, base + HACE_CRYPTO_DATA_LEN, t->len);
> +    qtest_writel(s, base + HACE_CRYPTO_CMD, cmd);
> +
> +    g_assert_cmphex(qtest_readl(s, base + HACE_STS) & HACE_CRYPTO_ISR,
> ==,
> +                    HACE_CRYPTO_ISR);
> +    qtest_writel(s, base + HACE_STS, HACE_CRYPTO_ISR);
> +
> +    crypt_gather_sg(s, dram, CRYPT_OFF_DST, out, t->len); }
> +
> +static void aspeed_test_crypto(const void *data)
>  {
>      const AspeedCryptoTest *c = data;
>      const CryptTest *t = &crypt_tests[c->index]; @@ -924,7 +1121,11 @@
> static void aspeed_test_crypto_direct(const void *data)
>      g_assert_cmpuint(t->len, <=, sizeof(out));
> 
>      /* Encrypt: ptext -> ctext */
> -    crypt_run_direct(s, c->base, c->dram, t, true, out);
> +    if (c->sg) {
> +        crypt_run_sg(s, c->base, c->dram, t, true, out);
> +    } else {
> +        crypt_run_direct(s, c->base, c->dram, t, true, out);
> +    }
>      g_assert_cmpmem(out, t->len, t->ctext, t->len);
> 
>      if (t->iv_out) {
> @@ -934,14 +1135,19 @@ static void aspeed_test_crypto_direct(const void
> *data)
>      }
> 
>      /* Decrypt: ctext -> ptext */
> -    crypt_run_direct(s, c->base, c->dram, t, false, out);
> +    if (c->sg) {
> +        crypt_run_sg(s, c->base, c->dram, t, false, out);
> +    } else {
> +        crypt_run_direct(s, c->base, c->dram, t, false, out);
> +    }
>      g_assert_cmpmem(out, t->len, t->ptext, t->len);
> 
>      qtest_quit(s);
>  }
> 
>  void aspeed_add_crypto_tests(const char *prefix, const char *machine,
> -                             uint32_t base, uint64_t dram, uint32_t
> modes)
> +                             uint32_t base, uint64_t dram, uint32_t
> modes,
> +                             bool sg)
>  {
>      int i;
> 
> @@ -966,7 +1172,8 @@ void aspeed_add_crypto_tests(const char *prefix,
> const char *machine,
>          t->base = base;
>          t->dram = dram;
>          t->index = i;
> -        qtest_add_data_func_full(path, t, aspeed_test_crypto_direct,
> g_free);
> +        t->sg = sg;
> +        qtest_add_data_func_full(path, t, aspeed_test_crypto, g_free);
>      }
>  }
> 
> diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c
> index 4cb4c475e9..61a3e3feb5 100644
> --- a/tests/qtest/aspeed_hace-test.c
> +++ b/tests/qtest/aspeed_hace-test.c
> @@ -224,6 +224,12 @@ int main(int argc, char **argv)
>      qtest_add_func("ast2600/hace/sha384_accum",
> test_sha384_accum_ast2600);
>      qtest_add_func("ast2600/hace/sha256_accum",
> test_sha256_accum_ast2600);
> 
> +    /* The AST2600 crypto engine uses scatter-gather mode and adds CTR.
> */
> +    aspeed_add_crypto_tests("ast2600", "-machine ast2600-evb",
> 0x1e6d0000,
> +                            0x80000000,
> +                            CRYPT_MODE_ECB | CRYPT_MODE_CBC |
> CRYPT_MODE_CTR,
> +                            true);
> +
>      qtest_add_func("ast2500/hace/addresses", test_addresses_ast2500);
>      qtest_add_func("ast2500/hace/sha512", test_sha512_ast2500);
>      qtest_add_func("ast2500/hace/sha256", test_sha256_ast2500); @@
> -233,7 +239,7 @@ int main(int argc, char **argv)
>       * The AST2500 crypto engine uses direct access mode and supports
> ECB/CBC.
>       */
>      aspeed_add_crypto_tests("ast2500", "-machine ast2500-evb",
> 0x1e6e3000,
> -                            0x80000000, CRYPT_MODE_ECB |
> CRYPT_MODE_CBC);
> +                            0x80000000, CRYPT_MODE_ECB |
> + CRYPT_MODE_CBC, false);
> 
>      qtest_add_func("ast2400/hace/addresses", test_addresses_ast2400);
>      qtest_add_func("ast2400/hace/sha512", test_sha512_ast2400);
> --
> 2.43.0

Reviewed-by: Kane Chen <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.