1*116bf243STan Siewert /*
2*116bf243STan Siewert * QTest testcase for the ASPEED AST2500 and AST2600 SCU.
3*116bf243STan Siewert *
4*116bf243STan Siewert * SPDX-License-Identifier: GPL-2.0-or-later
5*116bf243STan Siewert * Copyright (C) 2025 Tan Siewert
6*116bf243STan Siewert */
7*116bf243STan Siewert
8*116bf243STan Siewert #include "qemu/osdep.h"
9*116bf243STan Siewert #include "libqtest-single.h"
10*116bf243STan Siewert
11*116bf243STan Siewert /*
12*116bf243STan Siewert * SCU base, as well as protection key are
13*116bf243STan Siewert * the same on AST2500 and 2600.
14*116bf243STan Siewert */
15*116bf243STan Siewert #define AST_SCU_BASE 0x1E6E2000
16*116bf243STan Siewert #define AST_SCU_PROT_LOCK_STATE 0x0
17*116bf243STan Siewert #define AST_SCU_PROT_LOCK_VALUE 0x2
18*116bf243STan Siewert #define AST_SCU_PROT_UNLOCK_STATE 0x1
19*116bf243STan Siewert #define AST_SCU_PROT_UNLOCK_VALUE 0x1688A8A8
20*116bf243STan Siewert
21*116bf243STan Siewert #define AST2500_MACHINE "-machine ast2500-evb"
22*116bf243STan Siewert #define AST2500_SCU_PROT_REG 0x00
23*116bf243STan Siewert #define AST2500_SCU_MISC_2_CONTROL_REG 0x4C
24*116bf243STan Siewert
25*116bf243STan Siewert #define AST2600_MACHINE "-machine ast2600-evb"
26*116bf243STan Siewert /* AST2600 has two protection registers */
27*116bf243STan Siewert #define AST2600_SCU_PROT_REG 0x000
28*116bf243STan Siewert #define AST2600_SCU_PROT_REG2 0x010
29*116bf243STan Siewert #define AST2600_SCU_MISC_2_CONTROL_REG 0x0C4
30*116bf243STan Siewert
31*116bf243STan Siewert #define TEST_LOCK_ARBITRARY_VALUE 0xABCDEFAB
32*116bf243STan Siewert
33*116bf243STan Siewert /**
34*116bf243STan Siewert * Assert that a given register matches an expected value.
35*116bf243STan Siewert *
36*116bf243STan Siewert * Reads the register and checks if its value equals the expected value.
37*116bf243STan Siewert *
38*116bf243STan Siewert * @param *s - QTest machine state
39*116bf243STan Siewert * @param reg - Address of the register to be checked
40*116bf243STan Siewert * @param expected - Expected register value
41*116bf243STan Siewert */
assert_register_eq(QTestState * s,uint32_t reg,uint32_t expected)42*116bf243STan Siewert static inline void assert_register_eq(QTestState *s,
43*116bf243STan Siewert uint32_t reg,
44*116bf243STan Siewert uint32_t expected)
45*116bf243STan Siewert {
46*116bf243STan Siewert uint32_t value = qtest_readl(s, reg);
47*116bf243STan Siewert g_assert_cmphex(value, ==, expected);
48*116bf243STan Siewert }
49*116bf243STan Siewert
50*116bf243STan Siewert /**
51*116bf243STan Siewert * Assert that a given register does not match a specific value.
52*116bf243STan Siewert *
53*116bf243STan Siewert * Reads the register and checks that its value is not equal to the
54*116bf243STan Siewert * provided value.
55*116bf243STan Siewert *
56*116bf243STan Siewert * @param *s - QTest machine state
57*116bf243STan Siewert * @param reg - Address of the register to be checked
58*116bf243STan Siewert * @param not_expected - Value the register must not contain
59*116bf243STan Siewert */
assert_register_neq(QTestState * s,uint32_t reg,uint32_t not_expected)60*116bf243STan Siewert static inline void assert_register_neq(QTestState *s,
61*116bf243STan Siewert uint32_t reg,
62*116bf243STan Siewert uint32_t not_expected)
63*116bf243STan Siewert {
64*116bf243STan Siewert uint32_t value = qtest_readl(s, reg);
65*116bf243STan Siewert g_assert_cmphex(value, !=, not_expected);
66*116bf243STan Siewert }
67*116bf243STan Siewert
68*116bf243STan Siewert /**
69*116bf243STan Siewert * Test whether the SCU can be locked and unlocked correctly.
70*116bf243STan Siewert *
71*116bf243STan Siewert * When testing multiple registers, this function assumes that writing
72*116bf243STan Siewert * to the first register also affects the others. However, writing to
73*116bf243STan Siewert * any other register only affects itself.
74*116bf243STan Siewert *
75*116bf243STan Siewert * @param *machine - input machine configuration, passed directly
76*116bf243STan Siewert * to QTest
77*116bf243STan Siewert * @param regs[] - List of registers to be checked
78*116bf243STan Siewert * @param regc - amount of arguments for registers to be checked
79*116bf243STan Siewert */
test_protection_register(const char * machine,const uint32_t regs[],const int regc)80*116bf243STan Siewert static void test_protection_register(const char *machine,
81*116bf243STan Siewert const uint32_t regs[],
82*116bf243STan Siewert const int regc)
83*116bf243STan Siewert {
84*116bf243STan Siewert QTestState *s = qtest_init(machine);
85*116bf243STan Siewert
86*116bf243STan Siewert for (int i = 0; i < regc; i++) {
87*116bf243STan Siewert uint32_t reg = regs[i];
88*116bf243STan Siewert
89*116bf243STan Siewert qtest_writel(s, reg, AST_SCU_PROT_UNLOCK_VALUE);
90*116bf243STan Siewert assert_register_eq(s, reg, AST_SCU_PROT_UNLOCK_STATE);
91*116bf243STan Siewert
92*116bf243STan Siewert /**
93*116bf243STan Siewert * Check that other registers are unlocked too, if more
94*116bf243STan Siewert * than one is available.
95*116bf243STan Siewert */
96*116bf243STan Siewert if (regc > 1 && i == 0) {
97*116bf243STan Siewert /* Initialise at 1 instead of 0 to skip first */
98*116bf243STan Siewert for (int j = 1; j < regc; j++) {
99*116bf243STan Siewert uint32_t add_reg = regs[j];
100*116bf243STan Siewert assert_register_eq(s, add_reg, AST_SCU_PROT_UNLOCK_STATE);
101*116bf243STan Siewert }
102*116bf243STan Siewert }
103*116bf243STan Siewert
104*116bf243STan Siewert /* Lock the register again */
105*116bf243STan Siewert qtest_writel(s, reg, AST_SCU_PROT_LOCK_VALUE);
106*116bf243STan Siewert assert_register_eq(s, reg, AST_SCU_PROT_LOCK_STATE);
107*116bf243STan Siewert
108*116bf243STan Siewert /* And the same for locked state */
109*116bf243STan Siewert if (regc > 1 && i == 0) {
110*116bf243STan Siewert /* Initialise at 1 instead of 0 to skip first */
111*116bf243STan Siewert for (int j = 1; j < regc; j++) {
112*116bf243STan Siewert uint32_t add_reg = regs[j];
113*116bf243STan Siewert assert_register_eq(s, add_reg, AST_SCU_PROT_LOCK_STATE);
114*116bf243STan Siewert }
115*116bf243STan Siewert }
116*116bf243STan Siewert }
117*116bf243STan Siewert
118*116bf243STan Siewert qtest_quit(s);
119*116bf243STan Siewert }
120*116bf243STan Siewert
test_2500_protection_register(void)121*116bf243STan Siewert static void test_2500_protection_register(void)
122*116bf243STan Siewert {
123*116bf243STan Siewert uint32_t regs[] = { AST_SCU_BASE + AST2500_SCU_PROT_REG };
124*116bf243STan Siewert
125*116bf243STan Siewert test_protection_register(AST2500_MACHINE,
126*116bf243STan Siewert regs,
127*116bf243STan Siewert ARRAY_SIZE(regs));
128*116bf243STan Siewert }
129*116bf243STan Siewert
test_2600_protection_register(void)130*116bf243STan Siewert static void test_2600_protection_register(void)
131*116bf243STan Siewert {
132*116bf243STan Siewert /**
133*116bf243STan Siewert * The AST2600 has two protection registers, both
134*116bf243STan Siewert * being required to be unlocked to do any operation.
135*116bf243STan Siewert *
136*116bf243STan Siewert * Modifying SCU000 also modifies SCU010, but modifying
137*116bf243STan Siewert * SCU010 only will keep SCU000 untouched.
138*116bf243STan Siewert */
139*116bf243STan Siewert uint32_t regs[] = { AST_SCU_BASE + AST2600_SCU_PROT_REG,
140*116bf243STan Siewert AST_SCU_BASE + AST2600_SCU_PROT_REG2 };
141*116bf243STan Siewert
142*116bf243STan Siewert test_protection_register(AST2600_MACHINE,
143*116bf243STan Siewert regs,
144*116bf243STan Siewert ARRAY_SIZE(regs));
145*116bf243STan Siewert }
146*116bf243STan Siewert
147*116bf243STan Siewert /**
148*116bf243STan Siewert * Test if SCU register writes are correctly allowed or blocked
149*116bf243STan Siewert * depending on the protection register state.
150*116bf243STan Siewert *
151*116bf243STan Siewert * The test first locks the protection register and verifies that
152*116bf243STan Siewert * writes to the target SCU register are rejected. It then unlocks
153*116bf243STan Siewert * the protection register and confirms that the written value is
154*116bf243STan Siewert * retained when unlocked.
155*116bf243STan Siewert *
156*116bf243STan Siewert * @param *machine - input machine configuration, passed directly
157*116bf243STan Siewert * to QTest
158*116bf243STan Siewert * @param protection_register - first SCU protection key register
159*116bf243STan Siewert * (only one for keeping it simple)
160*116bf243STan Siewert * @param test_register - Register to be used for writing arbitrary
161*116bf243STan Siewert * values
162*116bf243STan Siewert */
test_write_permission_lock_state(const char * machine,const uint32_t protection_register,const uint32_t test_register)163*116bf243STan Siewert static void test_write_permission_lock_state(const char *machine,
164*116bf243STan Siewert const uint32_t protection_register,
165*116bf243STan Siewert const uint32_t test_register)
166*116bf243STan Siewert {
167*116bf243STan Siewert QTestState *s = qtest_init(machine);
168*116bf243STan Siewert
169*116bf243STan Siewert /* Arbitrary value to lock provided SCU protection register */
170*116bf243STan Siewert qtest_writel(s, protection_register, AST_SCU_PROT_LOCK_VALUE);
171*116bf243STan Siewert
172*116bf243STan Siewert /* Ensure that the SCU is really locked */
173*116bf243STan Siewert assert_register_eq(s, protection_register, AST_SCU_PROT_LOCK_STATE);
174*116bf243STan Siewert
175*116bf243STan Siewert /* Write a known arbitrary value to test that the write is blocked */
176*116bf243STan Siewert qtest_writel(s, test_register, TEST_LOCK_ARBITRARY_VALUE);
177*116bf243STan Siewert
178*116bf243STan Siewert /* We do not want to have the written value to be saved */
179*116bf243STan Siewert assert_register_neq(s, test_register, TEST_LOCK_ARBITRARY_VALUE);
180*116bf243STan Siewert
181*116bf243STan Siewert /**
182*116bf243STan Siewert * Unlock the SCU and verify that it can be written to.
183*116bf243STan Siewert * Assumes that the first SCU protection register is sufficient to
184*116bf243STan Siewert * unlock all protection registers, if multiple are present.
185*116bf243STan Siewert */
186*116bf243STan Siewert qtest_writel(s, protection_register, AST_SCU_PROT_UNLOCK_VALUE);
187*116bf243STan Siewert assert_register_eq(s, protection_register, AST_SCU_PROT_UNLOCK_STATE);
188*116bf243STan Siewert
189*116bf243STan Siewert /* Write a known arbitrary value to test that the write works */
190*116bf243STan Siewert qtest_writel(s, test_register, TEST_LOCK_ARBITRARY_VALUE);
191*116bf243STan Siewert
192*116bf243STan Siewert /* Ensure that the written value is retained */
193*116bf243STan Siewert assert_register_eq(s, test_register, TEST_LOCK_ARBITRARY_VALUE);
194*116bf243STan Siewert
195*116bf243STan Siewert qtest_quit(s);
196*116bf243STan Siewert }
197*116bf243STan Siewert
test_2500_write_permission_lock_state(void)198*116bf243STan Siewert static void test_2500_write_permission_lock_state(void)
199*116bf243STan Siewert {
200*116bf243STan Siewert test_write_permission_lock_state(
201*116bf243STan Siewert AST2500_MACHINE,
202*116bf243STan Siewert AST_SCU_BASE + AST2500_SCU_PROT_REG,
203*116bf243STan Siewert AST_SCU_BASE + AST2500_SCU_MISC_2_CONTROL_REG
204*116bf243STan Siewert );
205*116bf243STan Siewert }
206*116bf243STan Siewert
test_2600_write_permission_lock_state(void)207*116bf243STan Siewert static void test_2600_write_permission_lock_state(void)
208*116bf243STan Siewert {
209*116bf243STan Siewert test_write_permission_lock_state(
210*116bf243STan Siewert AST2600_MACHINE,
211*116bf243STan Siewert AST_SCU_BASE + AST2600_SCU_PROT_REG,
212*116bf243STan Siewert AST_SCU_BASE + AST2600_SCU_MISC_2_CONTROL_REG
213*116bf243STan Siewert );
214*116bf243STan Siewert }
215*116bf243STan Siewert
main(int argc,char ** argv)216*116bf243STan Siewert int main(int argc, char **argv)
217*116bf243STan Siewert {
218*116bf243STan Siewert g_test_init(&argc, &argv, NULL);
219*116bf243STan Siewert
220*116bf243STan Siewert qtest_add_func("/ast2500/scu/protection_register",
221*116bf243STan Siewert test_2500_protection_register);
222*116bf243STan Siewert qtest_add_func("/ast2600/scu/protection_register",
223*116bf243STan Siewert test_2600_protection_register);
224*116bf243STan Siewert
225*116bf243STan Siewert qtest_add_func("/ast2500/scu/write_permission_lock_state",
226*116bf243STan Siewert test_2500_write_permission_lock_state);
227*116bf243STan Siewert qtest_add_func("/ast2600/scu/write_permission_lock_state",
228*116bf243STan Siewert test_2600_write_permission_lock_state);
229*116bf243STan Siewert
230*116bf243STan Siewert return g_test_run();
231*116bf243STan Siewert }
232