You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
qemu/tests/qtest/aspeed_scu-test.c

232 lines
7.6 KiB
C

/*
* QTest testcase for the ASPEED AST2500 and AST2600 SCU.
*
* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright (C) 2025 Tan Siewert
*/
#include "qemu/osdep.h"
#include "libqtest-single.h"
/*
* SCU base, as well as protection key are
* the same on AST2500 and 2600.
*/
#define AST_SCU_BASE 0x1E6E2000
#define AST_SCU_PROT_LOCK_STATE 0x0
#define AST_SCU_PROT_LOCK_VALUE 0x2
#define AST_SCU_PROT_UNLOCK_STATE 0x1
#define AST_SCU_PROT_UNLOCK_VALUE 0x1688A8A8
#define AST2500_MACHINE "-machine ast2500-evb"
#define AST2500_SCU_PROT_REG 0x00
#define AST2500_SCU_MISC_2_CONTROL_REG 0x4C
#define AST2600_MACHINE "-machine ast2600-evb"
/* AST2600 has two protection registers */
#define AST2600_SCU_PROT_REG 0x000
#define AST2600_SCU_PROT_REG2 0x010
#define AST2600_SCU_MISC_2_CONTROL_REG 0x0C4
#define TEST_LOCK_ARBITRARY_VALUE 0xABCDEFAB
/**
* Assert that a given register matches an expected value.
*
* Reads the register and checks if its value equals the expected value.
*
* @param *s - QTest machine state
* @param reg - Address of the register to be checked
* @param expected - Expected register value
*/
static inline void assert_register_eq(QTestState *s,
uint32_t reg,
uint32_t expected)
{
uint32_t value = qtest_readl(s, reg);
g_assert_cmphex(value, ==, expected);
}
/**
* Assert that a given register does not match a specific value.
*
* Reads the register and checks that its value is not equal to the
* provided value.
*
* @param *s - QTest machine state
* @param reg - Address of the register to be checked
* @param not_expected - Value the register must not contain
*/
static inline void assert_register_neq(QTestState *s,
uint32_t reg,
uint32_t not_expected)
{
uint32_t value = qtest_readl(s, reg);
g_assert_cmphex(value, !=, not_expected);
}
/**
* Test whether the SCU can be locked and unlocked correctly.
*
* When testing multiple registers, this function assumes that writing
* to the first register also affects the others. However, writing to
* any other register only affects itself.
*
* @param *machine - input machine configuration, passed directly
* to QTest
* @param regs[] - List of registers to be checked
* @param regc - amount of arguments for registers to be checked
*/
static void test_protection_register(const char *machine,
const uint32_t regs[],
const int regc)
{
QTestState *s = qtest_init(machine);
for (int i = 0; i < regc; i++) {
uint32_t reg = regs[i];
qtest_writel(s, reg, AST_SCU_PROT_UNLOCK_VALUE);
assert_register_eq(s, reg, AST_SCU_PROT_UNLOCK_STATE);
/**
* Check that other registers are unlocked too, if more
* than one is available.
*/
if (regc > 1 && i == 0) {
/* Initialise at 1 instead of 0 to skip first */
for (int j = 1; j < regc; j++) {
uint32_t add_reg = regs[j];
assert_register_eq(s, add_reg, AST_SCU_PROT_UNLOCK_STATE);
}
}
/* Lock the register again */
qtest_writel(s, reg, AST_SCU_PROT_LOCK_VALUE);
assert_register_eq(s, reg, AST_SCU_PROT_LOCK_STATE);
/* And the same for locked state */
if (regc > 1 && i == 0) {
/* Initialise at 1 instead of 0 to skip first */
for (int j = 1; j < regc; j++) {
uint32_t add_reg = regs[j];
assert_register_eq(s, add_reg, AST_SCU_PROT_LOCK_STATE);
}
}
}
qtest_quit(s);
}
static void test_2500_protection_register(void)
{
uint32_t regs[] = { AST_SCU_BASE + AST2500_SCU_PROT_REG };
test_protection_register(AST2500_MACHINE,
regs,
ARRAY_SIZE(regs));
}
static void test_2600_protection_register(void)
{
/**
* The AST2600 has two protection registers, both
* being required to be unlocked to do any operation.
*
* Modifying SCU000 also modifies SCU010, but modifying
* SCU010 only will keep SCU000 untouched.
*/
uint32_t regs[] = { AST_SCU_BASE + AST2600_SCU_PROT_REG,
AST_SCU_BASE + AST2600_SCU_PROT_REG2 };
test_protection_register(AST2600_MACHINE,
regs,
ARRAY_SIZE(regs));
}
/**
* Test if SCU register writes are correctly allowed or blocked
* depending on the protection register state.
*
* The test first locks the protection register and verifies that
* writes to the target SCU register are rejected. It then unlocks
* the protection register and confirms that the written value is
* retained when unlocked.
*
* @param *machine - input machine configuration, passed directly
* to QTest
* @param protection_register - first SCU protection key register
* (only one for keeping it simple)
* @param test_register - Register to be used for writing arbitrary
* values
*/
static void test_write_permission_lock_state(const char *machine,
const uint32_t protection_register,
const uint32_t test_register)
{
QTestState *s = qtest_init(machine);
/* Arbitrary value to lock provided SCU protection register */
qtest_writel(s, protection_register, AST_SCU_PROT_LOCK_VALUE);
/* Ensure that the SCU is really locked */
assert_register_eq(s, protection_register, AST_SCU_PROT_LOCK_STATE);
/* Write a known arbitrary value to test that the write is blocked */
qtest_writel(s, test_register, TEST_LOCK_ARBITRARY_VALUE);
/* We do not want to have the written value to be saved */
assert_register_neq(s, test_register, TEST_LOCK_ARBITRARY_VALUE);
/**
* Unlock the SCU and verify that it can be written to.
* Assumes that the first SCU protection register is sufficient to
* unlock all protection registers, if multiple are present.
*/
qtest_writel(s, protection_register, AST_SCU_PROT_UNLOCK_VALUE);
assert_register_eq(s, protection_register, AST_SCU_PROT_UNLOCK_STATE);
/* Write a known arbitrary value to test that the write works */
qtest_writel(s, test_register, TEST_LOCK_ARBITRARY_VALUE);
/* Ensure that the written value is retained */
assert_register_eq(s, test_register, TEST_LOCK_ARBITRARY_VALUE);
qtest_quit(s);
}
static void test_2500_write_permission_lock_state(void)
{
test_write_permission_lock_state(
AST2500_MACHINE,
AST_SCU_BASE + AST2500_SCU_PROT_REG,
AST_SCU_BASE + AST2500_SCU_MISC_2_CONTROL_REG
);
}
static void test_2600_write_permission_lock_state(void)
{
test_write_permission_lock_state(
AST2600_MACHINE,
AST_SCU_BASE + AST2600_SCU_PROT_REG,
AST_SCU_BASE + AST2600_SCU_MISC_2_CONTROL_REG
);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
qtest_add_func("/ast2500/scu/protection_register",
test_2500_protection_register);
qtest_add_func("/ast2600/scu/protection_register",
test_2600_protection_register);
qtest_add_func("/ast2500/scu/write_permission_lock_state",
test_2500_write_permission_lock_state);
qtest_add_func("/ast2600/scu/write_permission_lock_state",
test_2600_write_permission_lock_state);
return g_test_run();
}