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