test_dbc.py 9.4 KB

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