Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

crypto: ccp - Add unit tests for dynamic boost control

Interacting with dynamic boost control messages requires the caller
to supply a signature. To allow validation of individual dynamic
boost control components, introduce a set of tests that can be run.

The tests can be run in 3 distinct different environments, and so
certain tests will be skipped depending on the environment.

1. Systems that do not support DBC.
2. Production systems that support DBC but are secured silicon.
3. Pre-production systems that support DBC but are unsecured silicon.

Unsecured silicon does not validate the signature, and so this allows
testing more of the state machine and functionality.

Acked-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

authored by

Mario Limonciello and committed by
Herbert Xu
15f8aa7b f40d42f1

+266
+266
tools/crypto/ccp/test_dbc.py
··· 1 + #!/usr/bin/python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + import unittest 4 + import os 5 + import time 6 + import glob 7 + from dbc import * 8 + 9 + # Artificial delay between set commands 10 + SET_DELAY = 0.5 11 + 12 + 13 + class invalid_param(ctypes.Structure): 14 + _fields_ = [ 15 + ("data", ctypes.c_uint8), 16 + ] 17 + 18 + 19 + def system_is_secured() -> bool: 20 + fused_part = glob.glob("/sys/bus/pci/drivers/ccp/**/fused_part")[0] 21 + if os.path.exists(fused_part): 22 + with open(fused_part, "r") as r: 23 + return int(r.read()) == 1 24 + return True 25 + 26 + 27 + class DynamicBoostControlTest(unittest.TestCase): 28 + def __init__(self, data) -> None: 29 + self.d = None 30 + self.signature = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" 31 + self.uid = "1111111111111111" 32 + super().__init__(data) 33 + 34 + def setUp(self) -> None: 35 + self.d = open(DEVICE_NODE) 36 + return super().setUp() 37 + 38 + def tearDown(self) -> None: 39 + if self.d: 40 + self.d.close() 41 + return super().tearDown() 42 + 43 + 44 + class TestUnsupportedSystem(DynamicBoostControlTest): 45 + def setUp(self) -> None: 46 + if os.path.exists(DEVICE_NODE): 47 + self.skipTest("system is supported") 48 + with self.assertRaises(FileNotFoundError) as error: 49 + super().setUp() 50 + self.assertEqual(error.exception.errno, 2) 51 + 52 + def test_unauthenticated_nonce(self) -> None: 53 + """fetch unauthenticated nonce""" 54 + with self.assertRaises(ValueError) as error: 55 + get_nonce(self.d, None) 56 + 57 + 58 + class TestInvalidIoctls(DynamicBoostControlTest): 59 + def __init__(self, data) -> None: 60 + self.data = invalid_param() 61 + self.data.data = 1 62 + super().__init__(data) 63 + 64 + def setUp(self) -> None: 65 + if not os.path.exists(DEVICE_NODE): 66 + self.skipTest("system is unsupported") 67 + return super().setUp() 68 + 69 + def test_invalid_nonce_ioctl(self) -> None: 70 + """tries to call get_nonce ioctl with invalid data structures""" 71 + 72 + # 0x1 (get nonce), and invalid data 73 + INVALID1 = IOWR(ord("D"), 0x01, invalid_param) 74 + with self.assertRaises(OSError) as error: 75 + fcntl.ioctl(self.d, INVALID1, self.data, True) 76 + self.assertEqual(error.exception.errno, 22) 77 + 78 + def test_invalid_setuid_ioctl(self) -> None: 79 + """tries to call set_uid ioctl with invalid data structures""" 80 + 81 + # 0x2 (set uid), and invalid data 82 + INVALID2 = IOW(ord("D"), 0x02, invalid_param) 83 + with self.assertRaises(OSError) as error: 84 + fcntl.ioctl(self.d, INVALID2, self.data, True) 85 + self.assertEqual(error.exception.errno, 22) 86 + 87 + def test_invalid_setuid_rw_ioctl(self) -> None: 88 + """tries to call set_uid ioctl with invalid data structures""" 89 + 90 + # 0x2 as RW (set uid), and invalid data 91 + INVALID3 = IOWR(ord("D"), 0x02, invalid_param) 92 + with self.assertRaises(OSError) as error: 93 + fcntl.ioctl(self.d, INVALID3, self.data, True) 94 + self.assertEqual(error.exception.errno, 22) 95 + 96 + def test_invalid_param_ioctl(self) -> None: 97 + """tries to call param ioctl with invalid data structures""" 98 + # 0x3 (param), and invalid data 99 + INVALID4 = IOWR(ord("D"), 0x03, invalid_param) 100 + with self.assertRaises(OSError) as error: 101 + fcntl.ioctl(self.d, INVALID4, self.data, True) 102 + self.assertEqual(error.exception.errno, 22) 103 + 104 + def test_invalid_call_ioctl(self) -> None: 105 + """tries to call the DBC ioctl with invalid data structures""" 106 + # 0x4, and invalid data 107 + INVALID5 = IOWR(ord("D"), 0x04, invalid_param) 108 + with self.assertRaises(OSError) as error: 109 + fcntl.ioctl(self.d, INVALID5, self.data, True) 110 + self.assertEqual(error.exception.errno, 22) 111 + 112 + 113 + class TestInvalidSignature(DynamicBoostControlTest): 114 + def setUp(self) -> None: 115 + if not os.path.exists(DEVICE_NODE): 116 + self.skipTest("system is unsupported") 117 + if not system_is_secured(): 118 + self.skipTest("system is unfused") 119 + return super().setUp() 120 + 121 + def test_unauthenticated_nonce(self) -> None: 122 + """fetch unauthenticated nonce""" 123 + get_nonce(self.d, None) 124 + 125 + def test_multiple_unauthenticated_nonce(self) -> None: 126 + """ensure state machine always returns nonce""" 127 + for count in range(0, 2): 128 + get_nonce(self.d, None) 129 + 130 + def test_authenticated_nonce(self) -> None: 131 + """fetch authenticated nonce""" 132 + with self.assertRaises(OSError) as error: 133 + get_nonce(self.d, self.signature) 134 + self.assertEqual(error.exception.errno, 1) 135 + 136 + def test_set_uid(self) -> None: 137 + """set uid""" 138 + with self.assertRaises(OSError) as error: 139 + set_uid(self.d, self.uid, self.signature) 140 + self.assertEqual(error.exception.errno, 1) 141 + 142 + def test_get_param(self) -> None: 143 + """fetch a parameter""" 144 + with self.assertRaises(OSError) as error: 145 + process_param(self.d, PARAM_GET_SOC_PWR_CUR, self.signature) 146 + self.assertEqual(error.exception.errno, 1) 147 + 148 + def test_set_param(self) -> None: 149 + """set a parameter""" 150 + with self.assertRaises(OSError) as error: 151 + process_param(self.d, PARAM_SET_PWR_CAP, self.signature, 1000) 152 + self.assertEqual(error.exception.errno, 1) 153 + 154 + 155 + class TestUnFusedSystem(DynamicBoostControlTest): 156 + def setup_identity(self) -> None: 157 + """sets up the identity of the caller""" 158 + # if already authenticated these may fail 159 + try: 160 + get_nonce(self.d, None) 161 + except PermissionError: 162 + pass 163 + try: 164 + set_uid(self.d, self.uid, self.signature) 165 + except BlockingIOError: 166 + pass 167 + try: 168 + get_nonce(self.d, self.signature) 169 + except PermissionError: 170 + pass 171 + 172 + def setUp(self) -> None: 173 + if not os.path.exists(DEVICE_NODE): 174 + self.skipTest("system is unsupported") 175 + if system_is_secured(): 176 + self.skipTest("system is fused") 177 + super().setUp() 178 + self.setup_identity() 179 + time.sleep(SET_DELAY) 180 + 181 + def test_get_valid_param(self) -> None: 182 + """fetch all possible parameters""" 183 + # SOC power 184 + soc_power_max = process_param(self.d, PARAM_GET_SOC_PWR_MAX, self.signature) 185 + soc_power_min = process_param(self.d, PARAM_GET_SOC_PWR_MIN, self.signature) 186 + self.assertGreater(soc_power_max.parameter, soc_power_min.parameter) 187 + 188 + # fmax 189 + fmax_max = process_param(self.d, PARAM_GET_FMAX_MAX, self.signature) 190 + fmax_min = process_param(self.d, PARAM_GET_FMAX_MIN, self.signature) 191 + self.assertGreater(fmax_max.parameter, fmax_min.parameter) 192 + 193 + # cap values 194 + keys = { 195 + "fmax-cap": PARAM_GET_FMAX_CAP, 196 + "power-cap": PARAM_GET_PWR_CAP, 197 + "current-temp": PARAM_GET_CURR_TEMP, 198 + "soc-power-cur": PARAM_GET_SOC_PWR_CUR, 199 + } 200 + for k in keys: 201 + result = process_param(self.d, keys[k], self.signature) 202 + self.assertGreater(result.parameter, 0) 203 + 204 + def test_get_invalid_param(self) -> None: 205 + """fetch an invalid parameter""" 206 + try: 207 + set_uid(self.d, self.uid, self.signature) 208 + except OSError: 209 + pass 210 + with self.assertRaises(OSError) as error: 211 + process_param(self.d, (0xF,), self.signature) 212 + self.assertEqual(error.exception.errno, 22) 213 + 214 + def test_set_fmax(self) -> None: 215 + """get/set fmax limit""" 216 + # fetch current 217 + original = process_param(self.d, PARAM_GET_FMAX_CAP, self.signature) 218 + 219 + # set the fmax 220 + target = original.parameter - 100 221 + process_param(self.d, PARAM_SET_FMAX_CAP, self.signature, target) 222 + time.sleep(SET_DELAY) 223 + new = process_param(self.d, PARAM_GET_FMAX_CAP, self.signature) 224 + self.assertEqual(new.parameter, target) 225 + 226 + # revert back to current 227 + process_param(self.d, PARAM_SET_FMAX_CAP, self.signature, original.parameter) 228 + time.sleep(SET_DELAY) 229 + cur = process_param(self.d, PARAM_GET_FMAX_CAP, self.signature) 230 + self.assertEqual(cur.parameter, original.parameter) 231 + 232 + def test_set_power_cap(self) -> None: 233 + """get/set power cap limit""" 234 + # fetch current 235 + original = process_param(self.d, PARAM_GET_PWR_CAP, self.signature) 236 + 237 + # set the fmax 238 + target = original.parameter - 10 239 + process_param(self.d, PARAM_SET_PWR_CAP, self.signature, target) 240 + time.sleep(SET_DELAY) 241 + new = process_param(self.d, PARAM_GET_PWR_CAP, self.signature) 242 + self.assertEqual(new.parameter, target) 243 + 244 + # revert back to current 245 + process_param(self.d, PARAM_SET_PWR_CAP, self.signature, original.parameter) 246 + time.sleep(SET_DELAY) 247 + cur = process_param(self.d, PARAM_GET_PWR_CAP, self.signature) 248 + self.assertEqual(cur.parameter, original.parameter) 249 + 250 + def test_set_3d_graphics_mode(self) -> None: 251 + """set/get 3d graphics mode""" 252 + # these aren't currently implemented but may be some day 253 + # they are *expected* to fail 254 + with self.assertRaises(OSError) as error: 255 + process_param(self.d, PARAM_GET_GFX_MODE, self.signature) 256 + self.assertEqual(error.exception.errno, 2) 257 + 258 + time.sleep(SET_DELAY) 259 + 260 + with self.assertRaises(OSError) as error: 261 + process_param(self.d, PARAM_SET_GFX_MODE, self.signature, 1) 262 + self.assertEqual(error.exception.errno, 2) 263 + 264 + 265 + if __name__ == "__main__": 266 + unittest.main()