xref: /openbmc/qemu/tests/qtest/aspeed_scu-test.c (revision a876b05d38c813501e60fb50c8a45b30a965e902)
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