summaryrefslogtreecommitdiff
path: root/tests/qtest
diff options
context:
space:
mode:
Diffstat (limited to 'tests/qtest')
-rw-r--r--tests/qtest/aspeed-acry-test.c406
-rw-r--r--tests/qtest/aspeed-hace-utils.c24
-rw-r--r--tests/qtest/aspeed-sbc-test.c194
-rw-r--r--tests/qtest/meson.build7
-rw-r--r--tests/qtest/tmp105-test.c359
5 files changed, 981 insertions, 9 deletions
diff --git a/tests/qtest/aspeed-acry-test.c b/tests/qtest/aspeed-acry-test.c
new file mode 100644
index 0000000000..ec348c2206
--- /dev/null
+++ b/tests/qtest/aspeed-acry-test.c
@@ -0,0 +1,406 @@
+/*
+ * QTest testcase for the ASPEED ACRY Engine
+ *
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/bitops.h"
+#include "crypto/akcipher.h"
+
+#define ACRY_TRIGGER 0x000
+#define ACRY_TRIGGER_RSA_DMA_DATA BIT(1)
+#define ACRY_TRIGGER_RSA_START BIT(0)
+#define ACRY_DMA_CMD 0x048
+#define ACRY_DMA_CMD_DMEM_AHB BIT(8)
+#define ACRY_DMA_CMD_SRAM_MODE_RSA (0x3 << 4)
+#define ACRY_DMA_SRC 0x04C
+#define ACRY_DMA_LEN 0x050
+#define ACRY_RSA_KEY_LEN 0x058
+#define ACRY_INT_MASK 0x3F8
+#define ACRY_INT_MASK_RSA_DMA_MASK BIT(2)
+#define ACRY_INT_MASK_RSA_ENG_MASK BIT(1)
+#define ACRY_STATUS 0x3FC
+#define ACRY_STATUS_RSA_DMA_DONE BIT(2)
+#define ACRY_STATUS_RSA_ENG_DONE BIT(1)
+#define ACRY_STATUS_RSA_DONE (ACRY_STATUS_RSA_ENG_DONE | \
+ ACRY_STATUS_RSA_DMA_DONE)
+
+#define ACRY_DATA_MAX_LEN 0x800
+#define ACRY_SRAM_SIZE (3 * ACRY_DATA_MAX_LEN)
+#define ACRY_MAX_BITS 4096
+#define ACRY_MAX_BYTES (ACRY_MAX_BITS / 8)
+
+#define ACRY_BYTES_PER_DWORD 4
+#define ACRY_LANES_PER_BLOCK 4
+/* Each block holds 3 regions (exp, mod, data) of LANES_PER_BLOCK dwords. */
+#define ACRY_DWORDS_PER_BLOCK (3 * ACRY_LANES_PER_BLOCK)
+
+/* Dwords into each block where each operand's region starts. */
+#define ACRY_EXP_OFFSET (0 * ACRY_LANES_PER_BLOCK)
+#define ACRY_MOD_OFFSET (1 * ACRY_LANES_PER_BLOCK)
+#define ACRY_DATA_OFFSET (2 * ACRY_LANES_PER_BLOCK)
+
+/*
+ * Raw (unpadded) RSA known-answer vectors: c = m^e mod n with
+ * e = 0x10001 (65537) - exactly what the "rsa" akcipher transform backed by
+ * the ACRY FW computes. PKCS1 padding is a separate "pkcs1pad(rsa)" template
+ * layered on top in Linux, not done by the ACRY hardware.
+ *
+ * rsa2048_* and rsa4096_* are from the Linux kernel crypto/testmgr.h (v6.18),
+ * rsa_tv_template[2] and [3]. Grep that file's own comment to find them:
+ *
+ * RSA test vectors. Borrowed from openSSL.
+ *
+ * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/crypto/testmgr.h?h=v6.18
+ *
+ * There the modulus n is DER-encoded in the .key field (not a raw field), the
+ * ciphertext is .c, and every entry reuses the same 8-byte .m message.
+ */
+/* rsa_tv_template[].m, shared by every entry */
+static const uint8_t rsa_m[8] = {
+ 0x54, 0x85, 0x9B, 0x34, 0x2C, 0x49, 0xEA, 0x2A,
+};
+/* public exponent e = 65537, DER-encoded in every .key */
+static const uint8_t rsa_e[3] = { 0x01, 0x00, 0x01 };
+
+/* rsa_tv_template[2].key, modulus n */
+static const uint8_t rsa2048_n[256] = {
+ 0xDB, 0x10, 0x1A, 0xC2, 0xA3, 0xF1, 0xDC, 0xFF, 0x13, 0x6B, 0xED, 0x44,
+ 0xDF, 0xF0, 0x02, 0x6D, 0x13, 0xC7, 0x88, 0xDA, 0x70, 0x6B, 0x54, 0xF1,
+ 0xE8, 0x27, 0xDC, 0xC3, 0x0F, 0x99, 0x6A, 0xFA, 0xC6, 0x67, 0xFF, 0x1D,
+ 0x1E, 0x3C, 0x1D, 0xC1, 0xB5, 0x5F, 0x6C, 0xC0, 0xB2, 0x07, 0x3A, 0x6D,
+ 0x41, 0xE4, 0x25, 0x99, 0xAC, 0xFC, 0xD2, 0x0F, 0x02, 0xD3, 0xD1, 0x54,
+ 0x06, 0x1A, 0x51, 0x77, 0xBD, 0xB6, 0xBF, 0xEA, 0xA7, 0x5C, 0x06, 0xA9,
+ 0x5D, 0x69, 0x84, 0x45, 0xD7, 0xF5, 0x05, 0xBA, 0x47, 0xF0, 0x1B, 0xD7,
+ 0x2B, 0x24, 0xEC, 0xCB, 0x9B, 0x1B, 0x10, 0x8D, 0x81, 0xA0, 0xBE, 0xB1,
+ 0x8C, 0x33, 0xE4, 0x36, 0xB8, 0x43, 0xEB, 0x19, 0x2A, 0x81, 0x8D, 0xDE,
+ 0x81, 0x0A, 0x99, 0x48, 0xB6, 0xF6, 0xBC, 0xCD, 0x49, 0x34, 0x3A, 0x8F,
+ 0x26, 0x94, 0xE3, 0x28, 0x82, 0x1A, 0x7C, 0x8F, 0x59, 0x9F, 0x45, 0xE8,
+ 0x5D, 0x1A, 0x45, 0x76, 0x04, 0x56, 0x05, 0xA1, 0xD0, 0x1B, 0x8C, 0x77,
+ 0x6D, 0xAF, 0x53, 0xFA, 0x71, 0xE2, 0x67, 0xE0, 0x9A, 0xFE, 0x03, 0xA9,
+ 0x85, 0xD2, 0xC9, 0xAA, 0xBA, 0x2A, 0xBC, 0xF4, 0xA0, 0x08, 0xF5, 0x13,
+ 0x98, 0x13, 0x5D, 0xF0, 0xD9, 0x33, 0x34, 0x2A, 0x61, 0xC3, 0x89, 0x55,
+ 0xF0, 0xAE, 0x1A, 0x9C, 0x22, 0xEE, 0x19, 0x05, 0x8D, 0x32, 0xFE, 0xEC,
+ 0x9C, 0x84, 0xBA, 0xB7, 0xF9, 0x6C, 0x3A, 0x4F, 0x07, 0xFC, 0x45, 0xEB,
+ 0x12, 0xE5, 0x7B, 0xFD, 0x55, 0xE6, 0x29, 0x69, 0xD1, 0xC2, 0xE8, 0xB9,
+ 0x78, 0x59, 0xF6, 0x79, 0x10, 0xC6, 0x4E, 0xEB, 0x6A, 0x5E, 0xB9, 0x9A,
+ 0xC7, 0xC4, 0x5B, 0x63, 0xDA, 0xA3, 0x3F, 0x5E, 0x92, 0x7A, 0x81, 0x5E,
+ 0xD6, 0xB0, 0xE2, 0x62, 0x8F, 0x74, 0x26, 0xC2, 0x0C, 0xD3, 0x9A, 0x17,
+ 0x47, 0xE6, 0x8E, 0xAB,
+};
+/* rsa_tv_template[2].c */
+static const uint8_t rsa2048_c[256] = {
+ 0xB2, 0x97, 0x76, 0xB4, 0xAE, 0x3E, 0x38, 0x3C, 0x7E, 0x64, 0x1F, 0xCC,
+ 0xA2, 0x7F, 0xF6, 0xBE, 0xCF, 0x49, 0xBC, 0x48, 0xD3, 0x6C, 0x8F, 0x0A,
+ 0x0E, 0xC1, 0x73, 0xBD, 0x7B, 0x55, 0x79, 0x36, 0x0E, 0xA1, 0x87, 0x88,
+ 0xB9, 0x2C, 0x90, 0xA6, 0x53, 0x5E, 0xE9, 0xEF, 0xC4, 0xE2, 0x4D, 0xDD,
+ 0xF7, 0xA6, 0x69, 0x82, 0x3F, 0x56, 0xA4, 0x7B, 0xFB, 0x62, 0xE0, 0xAE,
+ 0xB8, 0xD3, 0x04, 0xB3, 0xAC, 0x5A, 0x15, 0x2A, 0xE3, 0x19, 0x9B, 0x03,
+ 0x9A, 0x0B, 0x41, 0xDA, 0x64, 0xEC, 0x0A, 0x69, 0xFC, 0xF2, 0x10, 0x92,
+ 0xF3, 0xC1, 0xBF, 0x84, 0x7F, 0xFD, 0x2C, 0xAE, 0xC8, 0xB5, 0xF6, 0x41,
+ 0x70, 0xC5, 0x47, 0x03, 0x8A, 0xF8, 0xFF, 0x6F, 0x3F, 0xD2, 0x6F, 0x09,
+ 0xB4, 0x22, 0xF3, 0x30, 0xBE, 0xA9, 0x85, 0xCB, 0x9C, 0x8D, 0xF9, 0x8F,
+ 0xEB, 0x32, 0x91, 0xA2, 0x25, 0x84, 0x8F, 0xF5, 0xDC, 0xC7, 0x06, 0x9C,
+ 0x2D, 0xE5, 0x11, 0x2C, 0x09, 0x09, 0x87, 0x09, 0xA9, 0xF6, 0x33, 0x73,
+ 0x90, 0xF1, 0x60, 0xF2, 0x65, 0xDD, 0x30, 0xA5, 0x66, 0xCE, 0x62, 0x7B,
+ 0xD0, 0xF8, 0x2D, 0x3D, 0x19, 0x82, 0x77, 0xE3, 0x0A, 0x5F, 0x75, 0x2F,
+ 0x8E, 0xB1, 0xE5, 0xE8, 0x91, 0x35, 0x1B, 0x3B, 0x33, 0xB7, 0x66, 0x92,
+ 0xD1, 0xF2, 0x8E, 0x6F, 0xE5, 0x75, 0x0C, 0xAD, 0x36, 0xFB, 0x4E, 0xD0,
+ 0x66, 0x61, 0xBD, 0x49, 0xFE, 0xF4, 0x1A, 0xA2, 0x2B, 0x49, 0xFE, 0x03,
+ 0x4C, 0x74, 0x47, 0x8D, 0x9A, 0x66, 0xB2, 0x49, 0x46, 0x4D, 0x77, 0xEA,
+ 0x33, 0x4D, 0x6B, 0x3C, 0xB4, 0x49, 0x4A, 0xC6, 0x7D, 0x3D, 0xB5, 0xB9,
+ 0x56, 0x41, 0x15, 0x67, 0x0F, 0x94, 0x3C, 0x93, 0x65, 0x27, 0xE0, 0x21,
+ 0x5D, 0x59, 0xC3, 0x62, 0xD5, 0xA6, 0xDA, 0x38, 0x26, 0x22, 0x5E, 0x34,
+ 0x1C, 0x94, 0xAF, 0x98,
+};
+
+/* rsa_tv_template[3].key, modulus n */
+static const uint8_t rsa4096_n[512] = {
+ 0xC3, 0x8B, 0x55, 0x7B, 0x73, 0x4D, 0xFF, 0xE9, 0x9B, 0xC6, 0xDC, 0x67,
+ 0x3C, 0xB4, 0x8E, 0xA0, 0x86, 0xED, 0xF2, 0xB9, 0x50, 0x5C, 0x54, 0x5C,
+ 0xBA, 0xE4, 0xA1, 0xB2, 0xA7, 0xAE, 0x2F, 0x1B, 0x7D, 0xF1, 0xFB, 0xAC,
+ 0x79, 0xC5, 0xDF, 0x1A, 0x00, 0xC9, 0xB2, 0xC1, 0x61, 0x25, 0x33, 0xE6,
+ 0x9C, 0xE9, 0xCF, 0xD6, 0x27, 0xC4, 0x4E, 0x44, 0x30, 0x44, 0x5E, 0x08,
+ 0xA1, 0x87, 0x52, 0xCC, 0x6B, 0x97, 0x70, 0x8C, 0xBC, 0xA5, 0x06, 0x31,
+ 0x0C, 0xD4, 0x2F, 0xD5, 0x7D, 0x26, 0x24, 0xA2, 0xE2, 0xAC, 0x78, 0xF4,
+ 0x53, 0x14, 0xCE, 0xF7, 0x19, 0x2E, 0xD7, 0xF7, 0xE6, 0x0C, 0xB9, 0x56,
+ 0x7F, 0x0B, 0xF1, 0xB1, 0xE2, 0x43, 0x70, 0xBD, 0x86, 0x1D, 0xA1, 0xCC,
+ 0x2B, 0x19, 0x08, 0x76, 0xEF, 0x91, 0xAC, 0xBF, 0x20, 0x24, 0x0D, 0x38,
+ 0xC0, 0x89, 0xB8, 0x9A, 0x70, 0xB3, 0x64, 0xD9, 0x8F, 0x80, 0x41, 0x10,
+ 0x5B, 0x9F, 0xB1, 0xCB, 0x76, 0x43, 0x00, 0x21, 0x25, 0x36, 0xD4, 0x19,
+ 0xFC, 0x55, 0x95, 0x10, 0xE4, 0x26, 0x74, 0x98, 0x2C, 0xD9, 0xBD, 0x0B,
+ 0x2B, 0x04, 0xC2, 0xAC, 0x82, 0x38, 0xB4, 0xDD, 0x4C, 0x04, 0x7E, 0x51,
+ 0x36, 0x40, 0x1E, 0x0B, 0xC4, 0x7C, 0x25, 0xDD, 0x4B, 0xB2, 0xE7, 0x20,
+ 0x0A, 0x57, 0xF9, 0xB4, 0x94, 0xC3, 0x08, 0x33, 0x22, 0x6F, 0x8B, 0x48,
+ 0xDB, 0x03, 0x68, 0x5A, 0x5B, 0xBA, 0xAE, 0xF3, 0xAD, 0xCF, 0xC3, 0x6D,
+ 0xBA, 0xF1, 0x28, 0x67, 0x7E, 0x6C, 0x79, 0x07, 0xDE, 0xFC, 0xED, 0xE7,
+ 0x96, 0xE3, 0x6C, 0xE0, 0x2C, 0x87, 0xF8, 0x02, 0x01, 0x28, 0x38, 0x43,
+ 0x21, 0x53, 0x84, 0x69, 0x75, 0x78, 0x15, 0x7E, 0xEE, 0xD2, 0x1B, 0xB9,
+ 0x23, 0x40, 0xA8, 0x86, 0x1E, 0x38, 0x83, 0xB2, 0x73, 0x1D, 0x53, 0xFB,
+ 0x9E, 0x2A, 0x8A, 0xB2, 0x75, 0x35, 0x01, 0xC3, 0xC3, 0xC4, 0x94, 0xE8,
+ 0x84, 0x86, 0x64, 0x81, 0xF4, 0x42, 0xAA, 0x3C, 0x0E, 0xD6, 0x4F, 0xBC,
+ 0x0A, 0x09, 0x2D, 0xE7, 0x1B, 0xD4, 0x10, 0xA8, 0x54, 0xEA, 0x89, 0x84,
+ 0x8A, 0xCB, 0xF7, 0x5A, 0x3C, 0xCA, 0x76, 0x08, 0x29, 0x62, 0xB4, 0x6A,
+ 0x22, 0xDF, 0x14, 0x95, 0x71, 0xFD, 0xB6, 0x86, 0x39, 0xB8, 0x8B, 0xF8,
+ 0x91, 0x7F, 0x38, 0xAA, 0x14, 0xCD, 0xE5, 0xF5, 0x1D, 0xC2, 0x6D, 0x53,
+ 0x69, 0x52, 0x84, 0x7F, 0xA3, 0x1A, 0x5E, 0x26, 0x04, 0x83, 0x06, 0x73,
+ 0x52, 0x56, 0xCF, 0x76, 0x26, 0xC9, 0xDD, 0x75, 0xD7, 0xFC, 0xF4, 0x69,
+ 0xD8, 0x7B, 0x55, 0xB7, 0x68, 0x13, 0x53, 0xB9, 0xE7, 0x89, 0xC3, 0xE8,
+ 0xD6, 0x6E, 0xA7, 0x6D, 0xEA, 0x81, 0xFD, 0xC4, 0xB7, 0x05, 0x5A, 0xB7,
+ 0x41, 0x0A, 0x23, 0x8E, 0x03, 0x8A, 0x1C, 0xAE, 0xD3, 0x1E, 0xCE, 0xE3,
+ 0x5E, 0xFC, 0x19, 0x4A, 0xEE, 0x61, 0x9B, 0x8E, 0xE5, 0xE5, 0xDD, 0x85,
+ 0xF9, 0x41, 0xEC, 0x14, 0x53, 0x92, 0xF7, 0xDD, 0x06, 0x85, 0x02, 0x91,
+ 0xE3, 0xEB, 0x6C, 0x43, 0x03, 0xB1, 0x36, 0x7B, 0x89, 0x5A, 0xA8, 0xEB,
+ 0xFC, 0xD5, 0xA8, 0x35, 0xDC, 0x81, 0xD9, 0x5C, 0xBD, 0xCA, 0xDC, 0x9B,
+ 0x98, 0x0B, 0x06, 0x5D, 0x0C, 0x5B, 0xEE, 0xF3, 0xD5, 0xCC, 0x57, 0xC9,
+ 0x71, 0x2F, 0x90, 0x3B, 0x3C, 0xF0, 0x8E, 0x4E, 0x35, 0x48, 0xAE, 0x63,
+ 0x74, 0xA9, 0xFC, 0x72, 0x75, 0x8E, 0x34, 0xA8, 0xF2, 0x1F, 0xEA, 0xDF,
+ 0x3A, 0x37, 0x2D, 0xE5, 0x39, 0x39, 0xF8, 0x57, 0x58, 0x3C, 0x04, 0xFE,
+ 0x87, 0x06, 0x98, 0xBC, 0x7B, 0xD3, 0x21, 0x36, 0x60, 0x25, 0x54, 0xA7,
+ 0x3D, 0xFA, 0x91, 0xCC, 0xA8, 0x0B, 0x92, 0x8E, 0xB4, 0xF7, 0x06, 0xFF,
+ 0x1E, 0x95, 0xCB, 0x07, 0x76, 0x97, 0x3B, 0x9D,
+};
+/* rsa_tv_template[3].c */
+static const uint8_t rsa4096_c[512] = {
+ 0x5C, 0xCE, 0x9C, 0xD7, 0x9A, 0x9E, 0xA1, 0xFE, 0x7A, 0x82, 0x3C, 0x68,
+ 0x27, 0x98, 0xE3, 0x5D, 0xD5, 0xD7, 0x07, 0x29, 0xF5, 0xFB, 0xC3, 0x1A,
+ 0x7F, 0x63, 0x1E, 0x62, 0x31, 0x3B, 0x19, 0x87, 0x79, 0x4F, 0xEC, 0x7B,
+ 0xF3, 0xCB, 0xEA, 0x9B, 0x95, 0x52, 0x3A, 0x40, 0xE5, 0x87, 0x7B, 0x72,
+ 0xD1, 0x72, 0xC9, 0xFB, 0x54, 0x63, 0xD8, 0xC9, 0xD7, 0x2C, 0xFC, 0x7B,
+ 0xC3, 0x14, 0x1E, 0xBC, 0x18, 0xB4, 0x34, 0xA1, 0xBF, 0x14, 0xB1, 0x37,
+ 0x31, 0x6E, 0xF0, 0x1B, 0x35, 0x19, 0x54, 0x07, 0xF7, 0x99, 0xEC, 0x3E,
+ 0x63, 0xE2, 0xCD, 0x61, 0x28, 0x65, 0xC3, 0xCD, 0xB1, 0x38, 0x36, 0xA5,
+ 0xB2, 0xD7, 0xB0, 0xDC, 0x1F, 0xF5, 0xEF, 0x19, 0xC7, 0x53, 0x32, 0x2D,
+ 0x1C, 0x26, 0xDA, 0xE4, 0x0D, 0xD6, 0x90, 0x7E, 0x28, 0xD8, 0xDC, 0xE4,
+ 0x61, 0x05, 0xD2, 0x25, 0x90, 0x01, 0xD3, 0x96, 0x6D, 0xA6, 0xCF, 0x58,
+ 0x20, 0xBB, 0x03, 0xF4, 0x01, 0xBC, 0x79, 0xB9, 0x18, 0xD8, 0xB8, 0xBA,
+ 0xBD, 0x93, 0xFC, 0xF2, 0x62, 0x5D, 0x8C, 0x66, 0x1E, 0x0E, 0x84, 0x59,
+ 0x93, 0xDD, 0xE2, 0x93, 0xA2, 0x62, 0x7D, 0x08, 0x82, 0x7A, 0xDD, 0xFC,
+ 0xB8, 0xBC, 0xC5, 0x4F, 0x9C, 0x4E, 0xBF, 0xB4, 0xFC, 0xF4, 0xC5, 0x01,
+ 0xE8, 0x00, 0x70, 0x4D, 0x28, 0x26, 0xCC, 0x2E, 0xFE, 0x0E, 0x58, 0x41,
+ 0x8B, 0xEC, 0xAF, 0x7C, 0x4B, 0x54, 0xD0, 0xA0, 0x64, 0xF9, 0x32, 0xF4,
+ 0x2E, 0x47, 0x65, 0x0A, 0x67, 0x88, 0x39, 0x3A, 0xDB, 0xB2, 0xDB, 0x7B,
+ 0xB5, 0xF6, 0x17, 0xA8, 0xD9, 0xC6, 0x5E, 0x28, 0x13, 0x82, 0x8A, 0x99,
+ 0xDB, 0x60, 0x08, 0xA5, 0x23, 0x37, 0xFA, 0x88, 0x90, 0x31, 0xC8, 0x9D,
+ 0x8F, 0xEC, 0xFB, 0x85, 0x9F, 0xB1, 0xCE, 0xA6, 0x24, 0x50, 0x46, 0x44,
+ 0x47, 0xCB, 0x65, 0xD1, 0xDF, 0xC0, 0xB1, 0x6C, 0x90, 0x1F, 0x99, 0x8E,
+ 0x4D, 0xD5, 0x9E, 0x31, 0x07, 0x66, 0x87, 0xDF, 0x01, 0xAA, 0x56, 0x3C,
+ 0x71, 0xE0, 0x2B, 0x6F, 0x67, 0x3B, 0x23, 0xED, 0xC2, 0xBD, 0x03, 0x30,
+ 0x79, 0x76, 0x02, 0x10, 0x10, 0x98, 0x85, 0x8A, 0xFF, 0xFD, 0x0B, 0xDA,
+ 0xA5, 0xD9, 0x32, 0x48, 0x02, 0xA0, 0x0B, 0xB9, 0x2A, 0x8A, 0x18, 0xCA,
+ 0xC6, 0x8F, 0x3F, 0xBB, 0x16, 0xB2, 0xAA, 0x98, 0x27, 0xE3, 0x60, 0x43,
+ 0xED, 0x15, 0x70, 0xD4, 0x57, 0x15, 0xFE, 0x19, 0xD4, 0x9B, 0x13, 0x78,
+ 0x8A, 0xF7, 0x21, 0xF1, 0xA2, 0xA2, 0x2D, 0xB3, 0x09, 0xCF, 0x44, 0x91,
+ 0x6E, 0x08, 0x3A, 0x30, 0x81, 0x3E, 0x90, 0x93, 0x8A, 0x67, 0x33, 0x00,
+ 0x59, 0x54, 0x9A, 0x25, 0xD3, 0x49, 0x8E, 0x9F, 0xC1, 0x4B, 0xE5, 0x86,
+ 0xF3, 0x50, 0x4C, 0xBC, 0xC5, 0xD3, 0xF5, 0x3A, 0x54, 0xE1, 0x36, 0x3F,
+ 0xE2, 0x5A, 0xB4, 0x37, 0xC0, 0xEB, 0x70, 0x35, 0xEC, 0xF6, 0xB7, 0xE8,
+ 0x44, 0x3B, 0x7B, 0xF3, 0xF1, 0xF2, 0x1E, 0xDB, 0x60, 0x7D, 0xD5, 0xBE,
+ 0xF0, 0x71, 0x34, 0x90, 0x4C, 0xCB, 0xD4, 0x35, 0x51, 0xC7, 0xDD, 0xD8,
+ 0xC9, 0x81, 0xF5, 0x5D, 0x57, 0x46, 0x2C, 0xB1, 0x7B, 0x9B, 0xAA, 0xCB,
+ 0xD1, 0x22, 0x25, 0x49, 0x44, 0xA3, 0xD4, 0x6B, 0x29, 0x7B, 0xD8, 0xB2,
+ 0x07, 0x93, 0xBF, 0x3D, 0x52, 0x49, 0x84, 0x79, 0xEF, 0xB8, 0xE5, 0xC4,
+ 0xAD, 0xCA, 0xA8, 0xC6, 0xF6, 0xA6, 0x76, 0x70, 0x5B, 0x0B, 0xE5, 0x83,
+ 0xC6, 0x0E, 0xEF, 0x55, 0xF2, 0xE7, 0xFF, 0x04, 0xEA, 0xE6, 0x13, 0xBE,
+ 0x40, 0xE1, 0x40, 0x45, 0x48, 0x66, 0x75, 0x31, 0xAE, 0x35, 0x64, 0x91,
+ 0x11, 0x6F, 0xDA, 0xEE, 0x26, 0x86, 0x45, 0x6F, 0x0B, 0xD5, 0x9F, 0x03,
+ 0xB1, 0x65, 0x5B, 0xDB, 0xA4, 0xE4, 0xF9, 0x45,
+};
+
+/*
+ * Offset in the scattered buffer of byte 'op_byte' (0 = least significant)
+ * of the operand whose region starts 'region' dwords into each 12-dword
+ * block (0 = exp, 4 = mod, 8 = data). Reproduces the byte layout the ACRY
+ * engine mandates, so the test lays out its DMA input and decodes the
+ * result the same way the hardware does.
+ */
+static int acry_operand_offset(int region, int op_byte)
+{
+ int byte_in_dword;
+ int op_dword;
+ int offset;
+ int block;
+ int lane;
+
+ op_dword = op_byte / ACRY_BYTES_PER_DWORD;
+ byte_in_dword = op_byte % ACRY_BYTES_PER_DWORD;
+ block = op_dword / ACRY_LANES_PER_BLOCK;
+ lane = op_dword % ACRY_LANES_PER_BLOCK;
+
+ offset = (block * ACRY_DWORDS_PER_BLOCK + region + lane)
+ * ACRY_BYTES_PER_DWORD + byte_in_dword;
+ g_assert_cmpint(offset, <, ACRY_SRAM_SIZE);
+
+ return offset;
+}
+
+/*
+ * Write a big-endian (most significant byte first) bignum of 'be_len' bytes
+ * into the scattered buffer region 'region' (exp, mod, or data), placing
+ * significance level k at acry_operand_offset(region, k).
+ */
+static void put_bignum_be_bytes(uint8_t *buf, int region,
+ const uint8_t *be, int be_len)
+{
+ int be_index;
+ int offset;
+ int k;
+
+ /* be[0] (MSB) maps to the highest level; be_index walks up from 0. */
+ be_index = 0;
+ for (k = be_len - 1; k >= 0; k--) {
+ offset = acry_operand_offset(region, k);
+ buf[offset] = be[be_index++];
+ }
+}
+
+/* Inverse of put_bignum_be_bytes(): gather a big-endian bignum back out. */
+static void get_bignum_be_bytes(const uint8_t *buf, int region,
+ uint8_t *out_be, int be_len)
+{
+ int be_index;
+ int offset;
+ int k;
+
+ /* Inverse of put_bignum_be_bytes(): highest level -> out_be[0] (MSB). */
+ be_index = 0;
+ for (k = be_len - 1; k >= 0; k--) {
+ offset = acry_operand_offset(region, k);
+ out_be[be_index++] = buf[offset];
+ }
+}
+
+typedef struct AspeedACRYModExp {
+ const char *name;
+ const uint8_t *n;
+ size_t n_len;
+ const uint8_t *e;
+ size_t e_len;
+ const uint8_t *m;
+ size_t m_len;
+ const uint8_t *c;
+ size_t c_len;
+} AspeedACRYModExp;
+
+static const AspeedACRYModExp acry_modexp_tests[] = {
+ {
+ .name = "modexp_rsa2048",
+ .n = rsa2048_n,
+ .n_len = sizeof(rsa2048_n),
+ .e = rsa_e,
+ .e_len = sizeof(rsa_e),
+ .m = rsa_m,
+ .m_len = sizeof(rsa_m),
+ .c = rsa2048_c,
+ .c_len = sizeof(rsa2048_c),
+ },
+ {
+ .name = "modexp_rsa4096",
+ .n = rsa4096_n,
+ .n_len = sizeof(rsa4096_n),
+ .e = rsa_e,
+ .e_len = sizeof(rsa_e),
+ .m = rsa_m,
+ .m_len = sizeof(rsa_m),
+ .c = rsa4096_c,
+ .c_len = sizeof(rsa4096_c),
+ },
+};
+
+typedef struct AspeedACRYTest {
+ const char *machine;
+ uint64_t dram_addr;
+ uint64_t sram_addr;
+ uint64_t acry_addr;
+ int index;
+} AspeedACRYTest;
+
+static void test_modexp_rsa(const void *opaque)
+{
+ const AspeedACRYTest *c = opaque;
+ const AspeedACRYModExp *t = &acry_modexp_tests[c->index];
+ QCryptoAkCipherOptions opts = {
+ .alg = QCRYPTO_AK_CIPHER_ALGO_RSA,
+ .u.rsa.padding_alg = QCRYPTO_RSA_PADDING_ALGO_RAW,
+ };
+ uint8_t dram_buf[ACRY_SRAM_SIZE] = { 0 };
+ uint8_t sram_buf[ACRY_SRAM_SIZE] = { 0 };
+ uint8_t result[ACRY_MAX_BYTES] = { 0 };
+ QTestState *qts;
+
+ if (!qcrypto_akcipher_supports(&opts)) {
+ g_test_skip("raw RSA not supported by the crypto backend");
+ return;
+ }
+
+ qts = qtest_init(c->machine);
+
+ g_assert_cmpuint(t->c_len, <=, sizeof(result));
+
+ put_bignum_be_bytes(dram_buf, ACRY_EXP_OFFSET, t->e, t->e_len);
+ put_bignum_be_bytes(dram_buf, ACRY_MOD_OFFSET, t->n, t->n_len);
+ put_bignum_be_bytes(dram_buf, ACRY_DATA_OFFSET, t->m, t->m_len);
+
+ qtest_memwrite(qts, c->dram_addr, dram_buf, sizeof(dram_buf));
+
+ qtest_writel(qts, c->acry_addr + ACRY_DMA_CMD, ACRY_DMA_CMD_DMEM_AHB);
+ qtest_writel(qts, c->acry_addr + ACRY_DMA_SRC, c->dram_addr);
+ qtest_writel(qts, c->acry_addr + ACRY_RSA_KEY_LEN,
+ ((uint32_t)(t->e_len * 8) << 16) | (uint32_t)(t->n_len * 8));
+ qtest_writel(qts, c->acry_addr + ACRY_DMA_LEN, ACRY_SRAM_SIZE);
+ qtest_writel(qts, c->acry_addr + ACRY_INT_MASK,
+ ACRY_INT_MASK_RSA_ENG_MASK | ACRY_INT_MASK_RSA_DMA_MASK);
+ qtest_writel(qts, c->acry_addr + ACRY_DMA_CMD, ACRY_DMA_CMD_SRAM_MODE_RSA);
+ qtest_writel(qts, c->acry_addr + ACRY_TRIGGER,
+ ACRY_TRIGGER_RSA_START | ACRY_TRIGGER_RSA_DMA_DATA);
+
+ /* Completion requires both RSA_ENG_DONE and RSA_DMA_DONE to be asserted. */
+ g_assert_cmphex(qtest_readl(qts, c->acry_addr + ACRY_STATUS), ==,
+ ACRY_STATUS_RSA_DONE);
+
+ qtest_memread(qts, c->sram_addr, sram_buf, sizeof(sram_buf));
+ get_bignum_be_bytes(sram_buf, ACRY_DATA_OFFSET, result, t->c_len);
+ g_assert_cmpmem(result, t->c_len, t->c, t->c_len);
+
+ /* Clear IRQ status and check it is deasserted */
+ qtest_writel(qts, c->acry_addr + ACRY_STATUS, ACRY_STATUS_RSA_DONE);
+ g_assert_cmphex(qtest_readl(qts, c->acry_addr + ACRY_STATUS), ==, 0);
+
+ qtest_quit(qts);
+}
+
+static void aspeed_add_acry_tests(const char *prefix, const char *machine,
+ uint64_t acry_addr, uint64_t sram_addr,
+ uint64_t dram_addr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(acry_modexp_tests); i++) {
+ g_autofree char *path = NULL;
+ AspeedACRYTest *t;
+
+ path = g_strdup_printf("%s/acry/%s", prefix,
+ acry_modexp_tests[i].name);
+ t = g_new0(AspeedACRYTest, 1);
+ t->machine = machine;
+ t->acry_addr = acry_addr;
+ t->sram_addr = sram_addr;
+ t->dram_addr = dram_addr;
+ t->index = i;
+ qtest_add_data_func_full(path, t, test_modexp_rsa, g_free);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ aspeed_add_acry_tests("ast2600", "-machine ast2600-evb",
+ 0x1e6fa000, 0x1e710000, 0x80001000);
+
+ return g_test_run();
+}
diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c
index 260eec043c..d20562fac9 100644
--- a/tests/qtest/aspeed-hace-utils.c
+++ b/tests/qtest/aspeed-hace-utils.c
@@ -1248,11 +1248,18 @@ static void aspeed_test_crypto(const void *data)
{
const AspeedCryptoTest *c = data;
const CryptTest *t = &crypt_tests[c->index];
- QTestState *s = qtest_init(c->machine);
+ QTestState *s;
uint8_t out[64];
uint8_t iv[16];
size_t iv_off;
+ if (!qcrypto_cipher_supports(t->alg, t->mode)) {
+ g_test_skip("cipher not supported by the crypto backend");
+ return;
+ }
+
+ s = qtest_init(c->machine);
+
g_assert_cmpuint(t->len, <=, sizeof(out));
/* Encrypt: ptext -> ctext */
@@ -1284,10 +1291,17 @@ static void aspeed_test_crypto_gcm(const void *data)
{
const AspeedCryptoTest *c = data;
const CryptTest *t = &crypt_tests[c->index];
- QTestState *s = qtest_init(c->machine);
+ QTestState *s;
uint8_t out[64];
uint8_t tag[16];
+ if (!qcrypto_cipher_supports(t->alg, t->mode)) {
+ g_test_skip("cipher not supported by the crypto backend");
+ return;
+ }
+
+ s = qtest_init(c->machine);
+
g_assert_cmpuint(t->len, <=, sizeof(out));
/* Encrypt: ptext -> ctext, then check the authentication tag. */
@@ -1318,12 +1332,6 @@ void aspeed_add_crypto_tests(const char *prefix, const char *machine,
continue;
}
- if (!qcrypto_cipher_supports(crypt_tests[i].alg,
- crypt_tests[i].mode)) {
- g_printerr("# skip unsupported %s\n", crypt_tests[i].name);
- continue;
- }
-
path = g_strdup_printf("%s/hace/crypto/%s", prefix,
crypt_tests[i].name);
t = g_new0(AspeedCryptoTest, 1);
diff --git a/tests/qtest/aspeed-sbc-test.c b/tests/qtest/aspeed-sbc-test.c
new file mode 100644
index 0000000000..c445dac47a
--- /dev/null
+++ b/tests/qtest/aspeed-sbc-test.c
@@ -0,0 +1,194 @@
+/*
+ * QTest testcase for the ASPEED Secure Boot Controller (SBC)
+ *
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/bitops.h"
+#include "crypto/akcipher.h"
+
+/* SBC register block */
+#define SBC_STATUS 0x014
+#define SBC_ECDSA_VERIFY_PASS BIT(21)
+#define SBC_ECDSA_VERIFY_DONE BIT(20)
+#define SBC_SEC_TRIGGER 0x0bc
+#define SBC_ECDSA_CMD_TRIGGER BIT(1)
+
+/*
+ * SEC SRAM operand offsets for a secp384r1 ECDSA verify. Every operand is a
+ * 48-byte big-endian integer.
+ */
+#define ECDSA_SRAM_QX 0x2080
+#define ECDSA_SRAM_QY 0x20c0
+#define ECDSA_SRAM_R 0x21c0
+#define ECDSA_SRAM_S 0x2200
+#define ECDSA_SRAM_M 0x2240
+
+/*
+ * ECDSA secp384r1 / SHA-384 known-answer vector from the Linux kernel crypto
+ * self-test manager (ecdsa_nist_p384_tv_template, sha384 entry in
+ * crypto/testmgr.h, v6.18), decoded into raw big-endian form: public key
+ * Qx || Qy, signature r || s and the SHA-384 message digest.
+ *
+ * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/crypto/testmgr.h?h=v6.18
+ */
+static const uint8_t ecdsa_p384_pubkey[96] = {
+ /* Qx */
+ 0x3a, 0x2f, 0x62, 0xe7, 0x1a, 0xcf, 0x24, 0xd0,
+ 0x0b, 0x7c, 0xe0, 0xed, 0x46, 0x0a, 0x4f, 0x74,
+ 0x16, 0x43, 0xe9, 0x1a, 0x25, 0x7c, 0x55, 0xff,
+ 0xf0, 0x29, 0x68, 0x66, 0x20, 0x91, 0xf9, 0xdb,
+ 0x2b, 0xf6, 0xb3, 0x6c, 0x54, 0x01, 0xca, 0xc7,
+ 0x6a, 0x5c, 0x0d, 0xeb, 0x68, 0xd9, 0x3c, 0xf1,
+ /* Qy */
+ 0x01, 0x74, 0x1f, 0xf9, 0x6c, 0xe5, 0x5b, 0x60,
+ 0xe9, 0x7f, 0x5d, 0xb3, 0x12, 0x80, 0x2a, 0xd8,
+ 0x67, 0x92, 0xc9, 0x0e, 0x4c, 0x4c, 0x6b, 0xa1,
+ 0xb2, 0xa8, 0x1e, 0xac, 0x1c, 0x97, 0xd9, 0x21,
+ 0x67, 0xe5, 0x1b, 0x5a, 0x52, 0x31, 0x68, 0xd6,
+ 0xee, 0xf0, 0x19, 0xb0, 0x55, 0xed, 0x89, 0x9e,
+};
+
+static const uint8_t ecdsa_p384_signature[96] = {
+ /* r */
+ 0x9b, 0x28, 0x68, 0xc0, 0xa1, 0xea, 0x8c, 0x50,
+ 0xee, 0x2e, 0x62, 0x35, 0x46, 0xfa, 0x00, 0xd8,
+ 0x2d, 0x7a, 0x91, 0x5f, 0x49, 0x2d, 0x22, 0x08,
+ 0x29, 0xe6, 0xfb, 0xca, 0x8c, 0xd6, 0xb6, 0xb4,
+ 0x3b, 0x1f, 0x07, 0x8f, 0x15, 0x02, 0xfe, 0x1d,
+ 0xa2, 0xa4, 0xc8, 0xf2, 0xea, 0x9d, 0x11, 0x1f,
+ /* s */
+ 0xfc, 0x50, 0xf6, 0x43, 0xbd, 0x50, 0x82, 0x0e,
+ 0xbf, 0xe3, 0x75, 0x24, 0x49, 0xac, 0xfb, 0xc8,
+ 0x71, 0xcd, 0x8f, 0x18, 0x99, 0xf0, 0x0f, 0x13,
+ 0x44, 0x92, 0x8c, 0x86, 0x99, 0x65, 0xb3, 0x97,
+ 0x96, 0x17, 0x04, 0xc9, 0x05, 0x77, 0xf1, 0x8e,
+ 0xab, 0x8d, 0x4e, 0xde, 0xe6, 0x6d, 0x9b, 0x66,
+};
+
+static const uint8_t ecdsa_p384_dgst[48] = {
+ 0x8d, 0xf2, 0xc0, 0xe9, 0xa8, 0xf3, 0x8e, 0x44,
+ 0xc4, 0x8c, 0x1a, 0xa0, 0xb8, 0xd7, 0x17, 0xdf,
+ 0xf2, 0x37, 0x1b, 0xc6, 0xe3, 0xf5, 0x62, 0xcc,
+ 0x68, 0xf5, 0xd5, 0x0b, 0xbf, 0x73, 0x2b, 0xb1,
+ 0xb0, 0x4c, 0x04, 0x00, 0x31, 0xab, 0xfe, 0xc8,
+ 0xd6, 0x09, 0xc8, 0xf2, 0xea, 0xd3, 0x28, 0xff,
+};
+
+typedef struct AspeedSBCECDSA {
+ const char *name;
+ QCryptoCurveID curve_id;
+ const uint8_t *pubkey;
+ const uint8_t *signature;
+ const uint8_t *dgst;
+ size_t coord_len;
+} AspeedSBCECDSA;
+
+static const AspeedSBCECDSA sbc_ecdsa_tests[] = {
+ {
+ .name = "secp384r1",
+ .curve_id = QCRYPTO_CURVE_ID_SECP384R1,
+ .pubkey = ecdsa_p384_pubkey,
+ .signature = ecdsa_p384_signature,
+ .dgst = ecdsa_p384_dgst,
+ .coord_len = 48,
+ },
+};
+
+typedef struct AspeedSBCTest {
+ const char *machine;
+ uint32_t sec_addr;
+ uint64_t sram_addr;
+ int index;
+} AspeedSBCTest;
+
+static void sbc_ecdsa_stage(QTestState *qts, const AspeedSBCTest *c,
+ const AspeedSBCECDSA *t)
+{
+ uint32_t len = t->coord_len;
+
+ qtest_memwrite(qts, c->sram_addr + ECDSA_SRAM_QX, t->pubkey, len);
+ qtest_memwrite(qts, c->sram_addr + ECDSA_SRAM_QY, t->pubkey + len, len);
+ qtest_memwrite(qts, c->sram_addr + ECDSA_SRAM_R, t->signature, len);
+ qtest_memwrite(qts, c->sram_addr + ECDSA_SRAM_S, t->signature + len, len);
+ qtest_memwrite(qts, c->sram_addr + ECDSA_SRAM_M, t->dgst, len);
+}
+
+/*
+ * Drive one ECDSA verify through the engine the way the firmware does:
+ * stage the operands in the SEC SRAM, trigger the command register and read
+ * back the done/pass bits in the status register.
+ */
+static void test_ecdsa_verify(const void *opaque)
+{
+ const AspeedSBCTest *c = opaque;
+ const AspeedSBCECDSA *t = &sbc_ecdsa_tests[c->index];
+ QCryptoAkCipherOptions opts = {
+ .alg = QCRYPTO_AK_CIPHER_ALGO_ECDSA,
+ .u.ecdsa.curve_id = t->curve_id,
+ };
+ QTestState *qts;
+ uint32_t status;
+ uint8_t bad;
+
+ if (!qcrypto_akcipher_supports(&opts)) {
+ g_test_skip("ECDSA is not supported by the crypto backend");
+ return;
+ }
+
+ qts = qtest_init(c->machine);
+
+ /* A valid signature verifies */
+ sbc_ecdsa_stage(qts, c, t);
+ qtest_writel(qts, c->sec_addr + SBC_SEC_TRIGGER, SBC_ECDSA_CMD_TRIGGER);
+ status = qtest_readl(qts, c->sec_addr + SBC_STATUS);
+ g_assert_cmphex(status & (SBC_ECDSA_VERIFY_DONE | SBC_ECDSA_VERIFY_PASS),
+ ==, SBC_ECDSA_VERIFY_DONE | SBC_ECDSA_VERIFY_PASS);
+
+ /* A tampered signature must fail */
+ bad = t->signature[0] ^ 0xff;
+ qtest_memwrite(qts, c->sram_addr + ECDSA_SRAM_R, &bad, 1);
+ qtest_writel(qts, c->sec_addr + SBC_SEC_TRIGGER, SBC_ECDSA_CMD_TRIGGER);
+ status = qtest_readl(qts, c->sec_addr + SBC_STATUS);
+ g_assert_cmphex(status & SBC_ECDSA_VERIFY_DONE, ==, SBC_ECDSA_VERIFY_DONE);
+ g_assert_cmphex(status & SBC_ECDSA_VERIFY_PASS, ==, 0);
+
+ qtest_quit(qts);
+}
+
+static void aspeed_add_sbc_ecdsa_tests(const char *prefix, const char *machine,
+ uint32_t sec_addr, uint64_t sram_addr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sbc_ecdsa_tests); i++) {
+ g_autofree char *path = NULL;
+ AspeedSBCTest *t;
+
+ path = g_strdup_printf("%s/sbc/ecdsa/%s", prefix,
+ sbc_ecdsa_tests[i].name);
+ t = g_new0(AspeedSBCTest, 1);
+ t->machine = machine;
+ t->sec_addr = sec_addr;
+ t->sram_addr = sram_addr;
+ t->index = i;
+ qtest_add_data_func_full(path, t, test_ecdsa_verify, g_free);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ aspeed_add_sbc_ecdsa_tests("ast1030", "-machine ast1030-evb",
+ 0x7e6f2000, 0x79000000);
+
+ aspeed_add_sbc_ecdsa_tests("ast1060", "-machine ast1060-evb",
+ 0x7e6f2000, 0x79000000);
+
+ return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 17870e339b..c3593f7530 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -230,8 +230,10 @@ qtests_npcm7xx = \
qtests_npcm8xx = \
['npcm_gmac-test']
qtests_aspeed = \
- ['aspeed_gpio-test',
+ ['aspeed-acry-test',
+ 'aspeed_gpio-test',
'aspeed_hace-test',
+ 'aspeed-sbc-test',
'aspeed_scu-test',
'aspeed_smc-test']
qtests_aspeed64 = \
@@ -402,8 +404,11 @@ if get_option('replication').allowed()
endif
qtests = {
+ 'aspeed-acry-test': [files('aspeed-acry-test.c'),
+ crypto],
'aspeed_hace-test': [files('aspeed-hace-utils.c', 'aspeed_hace-test.c'),
crypto],
+ 'aspeed-sbc-test': [files('aspeed-sbc-test.c'), crypto],
'aspeed_smc-test': files('aspeed-smc-utils.c', 'aspeed_smc-test.c'),
'ast2700-hace-test': [files('aspeed-hace-utils.c', 'ast2700-hace-test.c'),
crypto],
diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c
index 3b114a50f5..504f6b7202 100644
--- a/tests/qtest/tmp105-test.c
+++ b/tests/qtest/tmp105-test.c
@@ -17,6 +17,24 @@
#define TMP105_TEST_ID "tmp105-test"
#define TMP105_TEST_ADDR 0x49
+#define TMP105_TEST_PATH "/machine/peripheral/" TMP105_TEST_ID
+
+#define TMP75_TEST_ID "tmp75-test"
+#define TMP75_TEST_PATH "/machine/peripheral/" TMP75_TEST_ID
+
+#define TMP175_TEST_ID "tmp175-test"
+#define TMP175_TEST_PATH "/machine/peripheral/" TMP175_TEST_ID
+
+#define LM75B_TEST_ID "lm75b-test"
+#define LM75B_TEST_PATH "/machine/peripheral/" LM75B_TEST_ID
+
+#define TMP105_CONFIG_POL (1 << 2) /* ALERT active-high when set */
+#define TMP105_CONFIG_TM (1 << 1) /* interrupt (thermostat) mode */
+#define TMP105_CONFIG_FQ_1 (0 << 3) /* fault queue: 1 consecutive fault */
+#define TMP105_CONFIG_FQ_4 (2 << 3) /* fault queue: 4 consecutive faults */
+#define TMP105_CONFIG_FQ(f) ((f) << 3) /* raw F1:F0 fault-queue field value */
+#define TMP105_CONFIG_SD (1 << 0) /* shutdown mode */
+#define TMP105_CONFIG_OS (1 << 7) /* one-shot conversion */
static int qmp_tmp105_get_temperature(const char *id)
{
@@ -105,6 +123,302 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc)
g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4230);
}
+/*
+ * The TMP105 exposes its alarm state only through the ALERT pin.
+ */
+static void test_alert_single_fault(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+ qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1);
+ g_assert_false(get_irq(0));
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_true(get_irq(0));
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
+ g_assert_false(get_irq(0));
+}
+
+static void test_fault_queue(void *obj, void *data, QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
+
+ /* Comparator mode, active-high ALERT, fault queue of four. */
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
+ g_assert_false(get_irq(0));
+
+ for (i = 0; i < 3; i++) {
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_false(get_irq(0));
+ }
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 25000);
+ g_assert_false(get_irq(0));
+
+ for (i = 0; i < 3; i++) {
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_false(get_irq(0));
+ }
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_true(get_irq(0));
+
+ for (i = 0; i < 3; i++) {
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
+ g_assert_true(get_irq(0));
+ }
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
+ g_assert_false(get_irq(0));
+}
+
+/*
+ * Drive @need consecutive over-limit conversions and check that the ALERT pin
+ * only asserts on the last one. This exercises the fault-queue length.
+ */
+static void check_fault_queue(QI2CDevice *i2cdev, const char *id,
+ const char *path, uint8_t fq_field, int need)
+{
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, path);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ(fq_field));
+ g_assert_false(get_irq(0));
+
+ for (i = 0; i < need - 1; i++) {
+ qmp_tmp105_set_temperature(id, 85000);
+ g_assert_false(get_irq(0));
+ }
+ qmp_tmp105_set_temperature(id, 85000);
+ g_assert_true(get_irq(0));
+}
+
+/*
+ * The one-shot (OS) bit starts a conversion only in shutdown mode. In
+ * continuous mode it is ignored, so writing it must not advance the fault
+ * queue; in shutdown each OS write performs one conversion that does.
+ */
+static void test_one_shot(void *obj, void *data, QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_false(get_irq(0));
+
+ for (i = 0; i < 8; i++) {
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4 | TMP105_CONFIG_OS);
+ g_assert_false(get_irq(0));
+ }
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4 | TMP105_CONFIG_SD);
+ for (i = 0; i < 2; i++) {
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL |
+ TMP105_CONFIG_FQ_4 | TMP105_CONFIG_SD | TMP105_CONFIG_OS);
+ g_assert_false(get_irq(0));
+ }
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL |
+ TMP105_CONFIG_FQ_4 | TMP105_CONFIG_SD | TMP105_CONFIG_OS);
+ g_assert_true(get_irq(0));
+}
+
+/*
+ * Configuration and limit-register writes are not conversions and must not
+ * advance the fault queue.
+ */
+static void test_fault_queue_ignores_writes(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
+ g_assert_false(get_irq(0));
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_false(get_irq(0));
+
+ for (i = 0; i < 8; i++) {
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
+ i2c_set16(i2cdev, TMP105_REG_T_HIGH, 0x5000);
+ i2c_set16(i2cdev, TMP105_REG_T_LOW, 0x4b00);
+ g_assert_false(get_irq(0));
+ }
+
+ for (i = 0; i < 2; i++) {
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_false(get_irq(0));
+ }
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_true(get_irq(0));
+}
+
+/*
+ * Leaving shutdown (SD 1->0) resumes continuous conversion, which must
+ * re-evaluate the current temperature against the limits.
+ */
+static void test_wake_from_shutdown(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+ qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1);
+ g_assert_false(get_irq(0));
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_true(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1 | TMP105_CONFIG_SD);
+ g_assert_true(get_irq(0));
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
+ g_assert_true(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1);
+ g_assert_false(get_irq(0));
+}
+
+/* The TMP75 maps F1:F0 = 10b to 3 consecutive faults. */
+static void test_tmp75_fault_queue(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ check_fault_queue(obj, TMP75_TEST_ID, TMP75_TEST_PATH, 2, 3);
+}
+
+/* The TMP175 keeps the TMP105 mapping: F1:F0 = 10b means 4 faults. */
+static void test_tmp175_fault_queue(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ check_fault_queue(obj, TMP175_TEST_ID, TMP175_TEST_PATH, 2, 4);
+}
+
+/*
+ * Toggling the thermostat mode (TM) bit clears any active alert on the TMP75.
+ */
+static void test_tmp75_tm_clears_alert(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, TMP75_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3));
+ for (i = 0; i < 4; i++) {
+ qmp_tmp105_set_temperature(TMP75_TEST_ID, 85000);
+ }
+ g_assert_true(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3) | TMP105_CONFIG_TM);
+ g_assert_false(get_irq(0));
+}
+
+/*
+ * Entering shutdown clears the ALERT in interrupt mode but leaves it asserted
+ * in comparator mode.
+ */
+static void test_tmp75_shutdown_clears_alert(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, TMP75_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_TM | TMP105_CONFIG_FQ(3));
+ for (i = 0; i < 4; i++) {
+ qmp_tmp105_set_temperature(TMP75_TEST_ID, 85000);
+ }
+ g_assert_true(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_TM | TMP105_CONFIG_FQ(3) |
+ TMP105_CONFIG_SD);
+ g_assert_false(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3));
+ for (i = 0; i < 4; i++) {
+ qmp_tmp105_set_temperature(TMP75_TEST_ID, 85000);
+ }
+ g_assert_true(get_irq(0));
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3) | TMP105_CONFIG_SD);
+ g_assert_true(get_irq(0));
+}
+
+/*
+ * The LM75B has a fixed 11-bit (0.125 C) converter: the resolution and one-shot
+ * Config bits are reserved (read/write as zero) and the temperature register is
+ * always masked to 11 bits regardless of what is written to Config.
+ */
+static void test_lm75b_resolution(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ uint16_t value;
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, 0x60);
+ g_assert_cmphex(i2c_get8(i2cdev, TMP105_REG_CONFIG), ==, 0x00);
+
+ qmp_tmp105_set_temperature(LM75B_TEST_ID, 20938);
+ value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE);
+ g_assert_cmphex(value, ==, 0x14e0);
+}
+
+/* The LM75B set-point registers store only 9 bits. */
+static void test_lm75b_limits(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+ i2c_set16(i2cdev, TMP105_REG_T_HIGH, 0x4231);
+ g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4200);
+
+ i2c_set16(i2cdev, TMP105_REG_T_LOW, 0x12b4);
+ g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_LOW), ==, 0x1280);
+}
+
+/* The LM75B likewise resets its OS output on shutdown in interrupt mode. */
+static void test_lm75b_shutdown_clears_alert(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+ qtest_irq_intercept_out(global_qtest, LM75B_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_TM);
+ qmp_tmp105_set_temperature(LM75B_TEST_ID, 85000);
+ g_assert_true(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_TM | TMP105_CONFIG_SD);
+ g_assert_false(get_irq(0));
+}
+
static void tmp105_register_nodes(void)
{
QOSGraphEdgeOptions opts = {
@@ -116,5 +430,50 @@ static void tmp105_register_nodes(void)
qos_node_consumes("tmp105", "i2c-bus", &opts);
qos_add_test("tx-rx", "tmp105", send_and_receive, NULL);
+ qos_add_test("alert-single-fault", "tmp105", test_alert_single_fault, NULL);
+ qos_add_test("fault-queue", "tmp105", test_fault_queue, NULL);
+ qos_add_test("fault-queue-ignores-writes", "tmp105",
+ test_fault_queue_ignores_writes, NULL);
+ qos_add_test("one-shot", "tmp105", test_one_shot, NULL);
+ qos_add_test("wake-from-shutdown", "tmp105", test_wake_from_shutdown, NULL);
+
+ /* TMP75: register-compatible, but with a 1/2/3/4 fault queue. */
+ QOSGraphEdgeOptions tmp75_opts = {
+ .extra_device_opts = "id=" TMP75_TEST_ID ",address=0x48"
+ };
+ add_qi2c_address(&tmp75_opts, &(QI2CAddress) { 0x48 });
+
+ qos_node_create_driver("tmp75", i2c_device_create);
+ qos_node_consumes("tmp75", "i2c-bus", &tmp75_opts);
+
+ qos_add_test("fault-queue", "tmp75", test_tmp75_fault_queue, NULL);
+ qos_add_test("tm-clears-alert", "tmp75", test_tmp75_tm_clears_alert, NULL);
+ qos_add_test("shutdown-clears-alert", "tmp75",
+ test_tmp75_shutdown_clears_alert, NULL);
+
+ /* TMP175: like the TMP105, with a 1/2/4/6 fault queue. */
+ QOSGraphEdgeOptions tmp175_opts = {
+ .extra_device_opts = "id=" TMP175_TEST_ID ",address=0x4a"
+ };
+ add_qi2c_address(&tmp175_opts, &(QI2CAddress) { 0x4a });
+
+ qos_node_create_driver("tmp175", i2c_device_create);
+ qos_node_consumes("tmp175", "i2c-bus", &tmp175_opts);
+
+ qos_add_test("fault-queue", "tmp175", test_tmp175_fault_queue, NULL);
+
+ /* LM75B: fixed 11-bit conversion and 9-bit set-point registers. */
+ QOSGraphEdgeOptions lm75b_opts = {
+ .extra_device_opts = "id=" LM75B_TEST_ID ",address=0x4c"
+ };
+ add_qi2c_address(&lm75b_opts, &(QI2CAddress) { 0x4c });
+
+ qos_node_create_driver("lm75b", i2c_device_create);
+ qos_node_consumes("lm75b", "i2c-bus", &lm75b_opts);
+
+ qos_add_test("resolution", "lm75b", test_lm75b_resolution, NULL);
+ qos_add_test("limits", "lm75b", test_lm75b_limits, NULL);
+ qos_add_test("shutdown-clears-alert", "lm75b",
+ test_lm75b_shutdown_clears_alert, NULL);
}
libqos_init(tmp105_register_nodes);