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