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 */ 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 */ 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 */ 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 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 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 */ 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 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 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 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