1#!/usr/bin/python3 2# SPDX-License-Identifier: GPL-2.0 3import unittest 4import os 5import time 6import glob 7from dbc import * 8 9# Artificial delay between set commands 10SET_DELAY = 0.5 11 12 13class invalid_param(ctypes.Structure): 14 _fields_ = [ 15 ("data", ctypes.c_uint8), 16 ] 17 18 19def 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 27class 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 44class 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 58class 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 113class 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 155class 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 265if __name__ == "__main__": 266 unittest.main() 267