mirror of https://gitlab.com/qemu-project/qemu
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.
232 lines
7.6 KiB
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();
|
|
}
|