Skip to content

Commit 15f8aa7

Browse files
superm1herbertx
authored andcommitted
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 <[email protected]> Signed-off-by: Mario Limonciello <[email protected]> Signed-off-by: Herbert Xu <[email protected]>
1 parent f40d42f commit 15f8aa7

File tree

1 file changed

+266
-0
lines changed

1 file changed

+266
-0
lines changed

tools/crypto/ccp/test_dbc.py

+266
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
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()

0 commit comments

Comments
 (0)