xref: /openbmc/linux/tools/crypto/ccp/test_dbc.py (revision fa840ba4)
1#!/usr/bin/python3
2# SPDX-License-Identifier: GPL-2.0
3import unittest
4import os
5import time
6import glob
7import fcntl
8try:
9    import ioctl_opt as ioctl
10except ImportError:
11    ioctl = None
12    pass
13from dbc import *
14
15# Artificial delay between set commands
16SET_DELAY = 0.5
17
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()) == 1
30    return True
31
32
33class 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
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 = 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
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        with self.assertRaises(OSError) as error:
142            get_nonce(self.d, self.signature)
143        self.assertEqual(error.exception.errno, 1)
144
145    def test_set_uid(self) -> None:
146        """set uid"""
147        with self.assertRaises(OSError) as error:
148            set_uid(self.d, self.uid, self.signature)
149        self.assertEqual(error.exception.errno, 1)
150
151    def test_get_param(self) -> None:
152        """fetch a parameter"""
153        with self.assertRaises(OSError) as error:
154            process_param(self.d, PARAM_GET_SOC_PWR_CUR, self.signature)
155        self.assertEqual(error.exception.errno, 1)
156
157    def test_set_param(self) -> None:
158        """set a parameter"""
159        with self.assertRaises(OSError) as error:
160            process_param(self.d, PARAM_SET_PWR_CAP, self.signature, 1000)
161        self.assertEqual(error.exception.errno, 1)
162
163
164class TestUnFusedSystem(DynamicBoostControlTest):
165    def setup_identity(self) -> None:
166        """sets up the identity of the caller"""
167        # if already authenticated these may fail
168        try:
169            get_nonce(self.d, None)
170        except PermissionError:
171            pass
172        try:
173            set_uid(self.d, self.uid, self.signature)
174        except BlockingIOError:
175            pass
176        try:
177            get_nonce(self.d, self.signature)
178        except PermissionError:
179            pass
180
181    def setUp(self) -> None:
182        if not os.path.exists(DEVICE_NODE):
183            self.skipTest("system is unsupported")
184        if system_is_secured():
185            self.skipTest("system is fused")
186        super().setUp()
187        self.setup_identity()
188        time.sleep(SET_DELAY)
189
190    def test_get_valid_param(self) -> None:
191        """fetch all possible parameters"""
192        # SOC power
193        soc_power_max = process_param(self.d, PARAM_GET_SOC_PWR_MAX, self.signature)
194        soc_power_min = process_param(self.d, PARAM_GET_SOC_PWR_MIN, self.signature)
195        self.assertGreater(soc_power_max[0], soc_power_min[0])
196
197        # fmax
198        fmax_max = process_param(self.d, PARAM_GET_FMAX_MAX, self.signature)
199        fmax_min = process_param(self.d, PARAM_GET_FMAX_MIN, self.signature)
200        self.assertGreater(fmax_max[0], fmax_min[0])
201
202        # cap values
203        keys = {
204            "fmax-cap": PARAM_GET_FMAX_CAP,
205            "power-cap": PARAM_GET_PWR_CAP,
206            "current-temp": PARAM_GET_CURR_TEMP,
207            "soc-power-cur": PARAM_GET_SOC_PWR_CUR,
208        }
209        for k in keys:
210            result = process_param(self.d, keys[k], self.signature)
211            self.assertGreater(result[0], 0)
212
213    def test_get_invalid_param(self) -> None:
214        """fetch an invalid parameter"""
215        try:
216            set_uid(self.d, self.uid, self.signature)
217        except OSError:
218            pass
219        with self.assertRaises(OSError) as error:
220            process_param(self.d, (0xF,), self.signature)
221        self.assertEqual(error.exception.errno, 22)
222
223    def test_set_fmax(self) -> None:
224        """get/set fmax limit"""
225        # fetch current
226        original = process_param(self.d, PARAM_GET_FMAX_CAP, self.signature)
227
228        # set the fmax
229        target = original[0] - 100
230        process_param(self.d, PARAM_SET_FMAX_CAP, self.signature, target)
231        time.sleep(SET_DELAY)
232        new = process_param(self.d, PARAM_GET_FMAX_CAP, self.signature)
233        self.assertEqual(new[0], target)
234
235        # revert back to current
236        process_param(self.d, PARAM_SET_FMAX_CAP, self.signature, original[0])
237        time.sleep(SET_DELAY)
238        cur = process_param(self.d, PARAM_GET_FMAX_CAP, self.signature)
239        self.assertEqual(cur[0], original[0])
240
241    def test_set_power_cap(self) -> None:
242        """get/set power cap limit"""
243        # fetch current
244        original = process_param(self.d, PARAM_GET_PWR_CAP, self.signature)
245
246        # set the fmax
247        target = original[0] - 10
248        process_param(self.d, PARAM_SET_PWR_CAP, self.signature, target)
249        time.sleep(SET_DELAY)
250        new = process_param(self.d, PARAM_GET_PWR_CAP, self.signature)
251        self.assertEqual(new[0], target)
252
253        # revert back to current
254        process_param(self.d, PARAM_SET_PWR_CAP, self.signature, original[0])
255        time.sleep(SET_DELAY)
256        cur = process_param(self.d, PARAM_GET_PWR_CAP, self.signature)
257        self.assertEqual(cur[0], original[0])
258
259    def test_set_3d_graphics_mode(self) -> None:
260        """set/get 3d graphics mode"""
261        # these aren't currently implemented but may be some day
262        # they are *expected* to fail
263        with self.assertRaises(OSError) as error:
264            process_param(self.d, PARAM_GET_GFX_MODE, self.signature)
265        self.assertEqual(error.exception.errno, 2)
266
267        time.sleep(SET_DELAY)
268
269        with self.assertRaises(OSError) as error:
270            process_param(self.d, PARAM_SET_GFX_MODE, self.signature, 1)
271        self.assertEqual(error.exception.errno, 2)
272
273
274if __name__ == "__main__":
275    unittest.main()
276