]> Git Repo - linux.git/blob - drivers/crypto/ccp/dbc.c
Linux 6.14-rc3
[linux.git] / drivers / crypto / ccp / dbc.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * AMD Secure Processor Dynamic Boost Control interface
4  *
5  * Copyright (C) 2023 Advanced Micro Devices, Inc.
6  *
7  * Author: Mario Limonciello <[email protected]>
8  */
9
10 #include <linux/mutex.h>
11
12 #include "dbc.h"
13
14 #define DBC_DEFAULT_TIMEOUT             (10 * MSEC_PER_SEC)
15 struct error_map {
16         u32 psp;
17         int ret;
18 };
19
20 #define DBC_ERROR_ACCESS_DENIED         0x0001
21 #define DBC_ERROR_EXCESS_DATA           0x0004
22 #define DBC_ERROR_BAD_PARAMETERS        0x0006
23 #define DBC_ERROR_BAD_STATE             0x0007
24 #define DBC_ERROR_NOT_IMPLEMENTED       0x0009
25 #define DBC_ERROR_BUSY                  0x000D
26 #define DBC_ERROR_MESSAGE_FAILURE       0x0307
27 #define DBC_ERROR_OVERFLOW              0x300F
28 #define DBC_ERROR_SIGNATURE_INVALID     0x3072
29
30 static struct error_map error_codes[] = {
31         {DBC_ERROR_ACCESS_DENIED,       -EACCES},
32         {DBC_ERROR_EXCESS_DATA,         -E2BIG},
33         {DBC_ERROR_BAD_PARAMETERS,      -EINVAL},
34         {DBC_ERROR_BAD_STATE,           -EAGAIN},
35         {DBC_ERROR_MESSAGE_FAILURE,     -ENOENT},
36         {DBC_ERROR_NOT_IMPLEMENTED,     -ENOENT},
37         {DBC_ERROR_BUSY,                -EBUSY},
38         {DBC_ERROR_OVERFLOW,            -ENFILE},
39         {DBC_ERROR_SIGNATURE_INVALID,   -EPERM},
40         {0x0,   0x0},
41 };
42
43 static inline int send_dbc_cmd_thru_ext(struct psp_dbc_device *dbc_dev, int msg)
44 {
45         dbc_dev->mbox->ext_req.header.sub_cmd_id = msg;
46
47         return psp_extended_mailbox_cmd(dbc_dev->psp,
48                                         DBC_DEFAULT_TIMEOUT,
49                                         (struct psp_ext_request *)dbc_dev->mbox);
50 }
51
52 static inline int send_dbc_cmd_thru_pa(struct psp_dbc_device *dbc_dev, int msg)
53 {
54         return psp_send_platform_access_msg(msg,
55                                             (struct psp_request *)dbc_dev->mbox);
56 }
57
58 static int send_dbc_cmd(struct psp_dbc_device *dbc_dev, int msg)
59 {
60         int ret;
61
62         *dbc_dev->result = 0;
63         ret = dbc_dev->use_ext ? send_dbc_cmd_thru_ext(dbc_dev, msg) :
64                                  send_dbc_cmd_thru_pa(dbc_dev, msg);
65         if (ret == -EIO) {
66                 int i;
67
68                 dev_dbg(dbc_dev->dev,
69                          "msg 0x%x failed with PSP error: 0x%x\n",
70                          msg, *dbc_dev->result);
71
72                 for (i = 0; error_codes[i].psp; i++) {
73                         if (*dbc_dev->result == error_codes[i].psp)
74                                 return error_codes[i].ret;
75                 }
76         }
77
78         return ret;
79 }
80
81 static int send_dbc_nonce(struct psp_dbc_device *dbc_dev)
82 {
83         int ret;
84
85         *dbc_dev->payload_size = dbc_dev->header_size + sizeof(struct dbc_user_nonce);
86         ret = send_dbc_cmd(dbc_dev, PSP_DYNAMIC_BOOST_GET_NONCE);
87         if (ret == -EAGAIN) {
88                 dev_dbg(dbc_dev->dev, "retrying get nonce\n");
89                 ret = send_dbc_cmd(dbc_dev, PSP_DYNAMIC_BOOST_GET_NONCE);
90         }
91
92         return ret;
93 }
94
95 static int send_dbc_parameter(struct psp_dbc_device *dbc_dev)
96 {
97         struct dbc_user_param *user_param = (struct dbc_user_param *)dbc_dev->payload;
98
99         switch (user_param->msg_index) {
100         case PARAM_SET_FMAX_CAP:
101         case PARAM_SET_PWR_CAP:
102         case PARAM_SET_GFX_MODE:
103                 return send_dbc_cmd(dbc_dev, PSP_DYNAMIC_BOOST_SET_PARAMETER);
104         case PARAM_GET_FMAX_CAP:
105         case PARAM_GET_PWR_CAP:
106         case PARAM_GET_CURR_TEMP:
107         case PARAM_GET_FMAX_MAX:
108         case PARAM_GET_FMAX_MIN:
109         case PARAM_GET_SOC_PWR_MAX:
110         case PARAM_GET_SOC_PWR_MIN:
111         case PARAM_GET_SOC_PWR_CUR:
112         case PARAM_GET_GFX_MODE:
113                 return send_dbc_cmd(dbc_dev, PSP_DYNAMIC_BOOST_GET_PARAMETER);
114         }
115
116         return -EINVAL;
117 }
118
119 void dbc_dev_destroy(struct psp_device *psp)
120 {
121         struct psp_dbc_device *dbc_dev = psp->dbc_data;
122
123         if (!dbc_dev)
124                 return;
125
126         misc_deregister(&dbc_dev->char_dev);
127         mutex_destroy(&dbc_dev->ioctl_mutex);
128         psp->dbc_data = NULL;
129 }
130
131 static long dbc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
132 {
133         struct psp_device *psp_master = psp_get_master_device();
134         void __user *argp = (void __user *)arg;
135         struct psp_dbc_device *dbc_dev;
136         int ret;
137
138         if (!psp_master || !psp_master->dbc_data)
139                 return -ENODEV;
140         dbc_dev = psp_master->dbc_data;
141
142         guard(mutex)(&dbc_dev->ioctl_mutex);
143
144         switch (cmd) {
145         case DBCIOCNONCE:
146                 if (copy_from_user(dbc_dev->payload, argp, sizeof(struct dbc_user_nonce)))
147                         return -EFAULT;
148
149                 ret = send_dbc_nonce(dbc_dev);
150                 if (ret)
151                         return ret;
152
153                 if (copy_to_user(argp, dbc_dev->payload, sizeof(struct dbc_user_nonce)))
154                         return -EFAULT;
155                 break;
156         case DBCIOCUID:
157                 if (copy_from_user(dbc_dev->payload, argp, sizeof(struct dbc_user_setuid)))
158                         return -EFAULT;
159
160                 *dbc_dev->payload_size = dbc_dev->header_size + sizeof(struct dbc_user_setuid);
161                 ret = send_dbc_cmd(dbc_dev, PSP_DYNAMIC_BOOST_SET_UID);
162                 if (ret)
163                         return ret;
164
165                 if (copy_to_user(argp, dbc_dev->payload, sizeof(struct dbc_user_setuid)))
166                         return -EFAULT;
167                 break;
168         case DBCIOCPARAM:
169                 if (copy_from_user(dbc_dev->payload, argp, sizeof(struct dbc_user_param)))
170                         return -EFAULT;
171
172                 *dbc_dev->payload_size = dbc_dev->header_size + sizeof(struct dbc_user_param);
173                 ret = send_dbc_parameter(dbc_dev);
174                 if (ret)
175                         return ret;
176
177                 if (copy_to_user(argp, dbc_dev->payload, sizeof(struct dbc_user_param)))
178                         return -EFAULT;
179                 break;
180         default:
181                 return -EINVAL;
182         }
183
184         return 0;
185 }
186
187 static const struct file_operations dbc_fops = {
188         .owner  = THIS_MODULE,
189         .unlocked_ioctl = dbc_ioctl,
190 };
191
192 int dbc_dev_init(struct psp_device *psp)
193 {
194         struct device *dev = psp->dev;
195         struct psp_dbc_device *dbc_dev;
196         int ret;
197
198         dbc_dev = devm_kzalloc(dev, sizeof(*dbc_dev), GFP_KERNEL);
199         if (!dbc_dev)
200                 return -ENOMEM;
201
202         BUILD_BUG_ON(sizeof(union dbc_buffer) > PAGE_SIZE);
203         dbc_dev->mbox = (void *)devm_get_free_pages(dev, GFP_KERNEL | __GFP_ZERO, 0);
204         if (!dbc_dev->mbox) {
205                 ret = -ENOMEM;
206                 goto cleanup_dev;
207         }
208
209         psp->dbc_data = dbc_dev;
210         dbc_dev->dev = dev;
211         dbc_dev->psp = psp;
212
213         if (psp->capability.dbc_thru_ext) {
214                 dbc_dev->use_ext = true;
215                 dbc_dev->payload_size = &dbc_dev->mbox->ext_req.header.payload_size;
216                 dbc_dev->result = &dbc_dev->mbox->ext_req.header.status;
217                 dbc_dev->payload = &dbc_dev->mbox->ext_req.buf;
218                 dbc_dev->header_size = sizeof(struct psp_ext_req_buffer_hdr);
219         } else {
220                 dbc_dev->payload_size = &dbc_dev->mbox->pa_req.header.payload_size;
221                 dbc_dev->result = &dbc_dev->mbox->pa_req.header.status;
222                 dbc_dev->payload = &dbc_dev->mbox->pa_req.buf;
223                 dbc_dev->header_size = sizeof(struct psp_req_buffer_hdr);
224         }
225
226         ret = send_dbc_nonce(dbc_dev);
227         if (ret == -EACCES) {
228                 dev_dbg(dbc_dev->dev,
229                         "dynamic boost control was previously authenticated\n");
230                 ret = 0;
231         }
232         dev_dbg(dbc_dev->dev, "dynamic boost control is %savailable\n",
233                 ret ? "un" : "");
234         if (ret) {
235                 ret = 0;
236                 goto cleanup_mbox;
237         }
238
239         dbc_dev->char_dev.minor = MISC_DYNAMIC_MINOR;
240         dbc_dev->char_dev.name = "dbc";
241         dbc_dev->char_dev.fops = &dbc_fops;
242         dbc_dev->char_dev.mode = 0600;
243         ret = misc_register(&dbc_dev->char_dev);
244         if (ret)
245                 goto cleanup_mbox;
246
247         mutex_init(&dbc_dev->ioctl_mutex);
248
249         return 0;
250
251 cleanup_mbox:
252         devm_free_pages(dev, (unsigned long)dbc_dev->mbox);
253
254 cleanup_dev:
255         psp->dbc_data = NULL;
256         devm_kfree(dev, dbc_dev);
257
258         return ret;
259 }
This page took 0.041164 seconds and 4 git commands to generate.