xref: /linux/tools/crypto/ccp/test_dbc.py (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
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 
15 # Artificial delay between set commands
16 SET_DELAY = 0.5
17 
18 
19 class invalid_param(ctypes.Structure):
20     _fields_ = [
21         ("data", ctypes.c_uint8),
22     ]
23 
24 
25 def 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()) == 1
30     return True
31 
32 
33 class DynamicBoostControlTest(unittest.TestCase):
34     def __init__(self, data) -> None:
35         self.d = None
36         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 
50 class 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 
64 class TestInvalidIoctls(DynamicBoostControlTest):
65     def __init__(self, data) -> None:
66         self.data = invalid_param()
67         self.data.data = 1
68         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 data
82         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 data
91         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 data
100         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 data
108         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 data
116         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 
122 class 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 
166 class TestUnFusedSystem(DynamicBoostControlTest):
167     def setup_identity(self) -> None:
168         """sets up the identity of the caller"""
169         # if already authenticated these may fail
170         try:
171             get_nonce(self.d, None)
172         except PermissionError:
173             pass
174         try:
175             set_uid(self.d, self.uid, self.signature)
176         except BlockingIOError:
177             pass
178         try:
179             get_nonce(self.d, self.signature)
180         except PermissionError:
181             pass
182 
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 power
195         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         # fmax
200         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 values
205         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             pass
221         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 current
228         original = process_param(self.d, PARAM_GET_FMAX_CAP, self.signature)
229 
230         # set the fmax
231         target = original[0] - 100
232         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 current
238         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 current
246         original = process_param(self.d, PARAM_GET_PWR_CAP, self.signature)
247 
248         # set the fmax
249         target = original[0] - 10
250         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 current
256         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 day
264         # they are *expected* to fail
265         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 
276 if __name__ == "__main__":
277     unittest.main()
278