crypto: ccp - Get a free page to use while fetching initial nonce
[platform/kernel/linux-starfive.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 <mario.limonciello@amd.com>
8  */
9
10 #include "dbc.h"
11
12 struct error_map {
13         u32 psp;
14         int ret;
15 };
16
17 #define DBC_ERROR_ACCESS_DENIED         0x0001
18 #define DBC_ERROR_EXCESS_DATA           0x0004
19 #define DBC_ERROR_BAD_PARAMETERS        0x0006
20 #define DBC_ERROR_BAD_STATE             0x0007
21 #define DBC_ERROR_NOT_IMPLEMENTED       0x0009
22 #define DBC_ERROR_BUSY                  0x000D
23 #define DBC_ERROR_MESSAGE_FAILURE       0x0307
24 #define DBC_ERROR_OVERFLOW              0x300F
25 #define DBC_ERROR_SIGNATURE_INVALID     0x3072
26
27 static struct error_map error_codes[] = {
28         {DBC_ERROR_ACCESS_DENIED,       -EACCES},
29         {DBC_ERROR_EXCESS_DATA,         -E2BIG},
30         {DBC_ERROR_BAD_PARAMETERS,      -EINVAL},
31         {DBC_ERROR_BAD_STATE,           -EAGAIN},
32         {DBC_ERROR_MESSAGE_FAILURE,     -ENOENT},
33         {DBC_ERROR_NOT_IMPLEMENTED,     -ENOENT},
34         {DBC_ERROR_BUSY,                -EBUSY},
35         {DBC_ERROR_OVERFLOW,            -ENFILE},
36         {DBC_ERROR_SIGNATURE_INVALID,   -EPERM},
37         {0x0,   0x0},
38 };
39
40 static int send_dbc_cmd(struct psp_dbc_device *dbc_dev,
41                         enum psp_platform_access_msg msg)
42 {
43         int ret;
44
45         dbc_dev->mbox->req.header.status = 0;
46         ret = psp_send_platform_access_msg(msg, (struct psp_request *)dbc_dev->mbox);
47         if (ret == -EIO) {
48                 int i;
49
50                 dev_dbg(dbc_dev->dev,
51                          "msg 0x%x failed with PSP error: 0x%x\n",
52                          msg, dbc_dev->mbox->req.header.status);
53
54                 for (i = 0; error_codes[i].psp; i++) {
55                         if (dbc_dev->mbox->req.header.status == error_codes[i].psp)
56                                 return error_codes[i].ret;
57                 }
58         }
59
60         return ret;
61 }
62
63 static int send_dbc_nonce(struct psp_dbc_device *dbc_dev)
64 {
65         int ret;
66
67         dbc_dev->mbox->req.header.payload_size = sizeof(dbc_dev->mbox->dbc_nonce);
68         ret = send_dbc_cmd(dbc_dev, PSP_DYNAMIC_BOOST_GET_NONCE);
69         if (ret == -EAGAIN) {
70                 dev_dbg(dbc_dev->dev, "retrying get nonce\n");
71                 ret = send_dbc_cmd(dbc_dev, PSP_DYNAMIC_BOOST_GET_NONCE);
72         }
73
74         return ret;
75 }
76
77 static int send_dbc_parameter(struct psp_dbc_device *dbc_dev)
78 {
79         dbc_dev->mbox->req.header.payload_size = sizeof(dbc_dev->mbox->dbc_param);
80
81         switch (dbc_dev->mbox->dbc_param.user.msg_index) {
82         case PARAM_SET_FMAX_CAP:
83         case PARAM_SET_PWR_CAP:
84         case PARAM_SET_GFX_MODE:
85                 return send_dbc_cmd(dbc_dev, PSP_DYNAMIC_BOOST_SET_PARAMETER);
86         case PARAM_GET_FMAX_CAP:
87         case PARAM_GET_PWR_CAP:
88         case PARAM_GET_CURR_TEMP:
89         case PARAM_GET_FMAX_MAX:
90         case PARAM_GET_FMAX_MIN:
91         case PARAM_GET_SOC_PWR_MAX:
92         case PARAM_GET_SOC_PWR_MIN:
93         case PARAM_GET_SOC_PWR_CUR:
94         case PARAM_GET_GFX_MODE:
95                 return send_dbc_cmd(dbc_dev, PSP_DYNAMIC_BOOST_GET_PARAMETER);
96         }
97
98         return -EINVAL;
99 }
100
101 void dbc_dev_destroy(struct psp_device *psp)
102 {
103         struct psp_dbc_device *dbc_dev = psp->dbc_data;
104
105         if (!dbc_dev)
106                 return;
107
108         misc_deregister(&dbc_dev->char_dev);
109         mutex_destroy(&dbc_dev->ioctl_mutex);
110         psp->dbc_data = NULL;
111 }
112
113 static long dbc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
114 {
115         struct psp_device *psp_master = psp_get_master_device();
116         void __user *argp = (void __user *)arg;
117         struct psp_dbc_device *dbc_dev;
118         int ret;
119
120         if (!psp_master || !psp_master->dbc_data)
121                 return -ENODEV;
122         dbc_dev = psp_master->dbc_data;
123
124         mutex_lock(&dbc_dev->ioctl_mutex);
125
126         switch (cmd) {
127         case DBCIOCNONCE:
128                 if (copy_from_user(&dbc_dev->mbox->dbc_nonce.user, argp,
129                                    sizeof(struct dbc_user_nonce))) {
130                         ret = -EFAULT;
131                         goto unlock;
132                 }
133
134                 ret = send_dbc_nonce(dbc_dev);
135                 if (ret)
136                         goto unlock;
137
138                 if (copy_to_user(argp, &dbc_dev->mbox->dbc_nonce.user,
139                                  sizeof(struct dbc_user_nonce))) {
140                         ret = -EFAULT;
141                         goto unlock;
142                 }
143                 break;
144         case DBCIOCUID:
145                 dbc_dev->mbox->req.header.payload_size = sizeof(dbc_dev->mbox->dbc_set_uid);
146                 if (copy_from_user(&dbc_dev->mbox->dbc_set_uid.user, argp,
147                                    sizeof(struct dbc_user_setuid))) {
148                         ret = -EFAULT;
149                         goto unlock;
150                 }
151
152                 ret = send_dbc_cmd(dbc_dev, PSP_DYNAMIC_BOOST_SET_UID);
153                 if (ret)
154                         goto unlock;
155
156                 if (copy_to_user(argp, &dbc_dev->mbox->dbc_set_uid.user,
157                                  sizeof(struct dbc_user_setuid))) {
158                         ret = -EFAULT;
159                         goto unlock;
160                 }
161                 break;
162         case DBCIOCPARAM:
163                 if (copy_from_user(&dbc_dev->mbox->dbc_param.user, argp,
164                                    sizeof(struct dbc_user_param))) {
165                         ret = -EFAULT;
166                         goto unlock;
167                 }
168
169                 ret = send_dbc_parameter(dbc_dev);
170                 if (ret)
171                         goto unlock;
172
173                 if (copy_to_user(argp, &dbc_dev->mbox->dbc_param.user,
174                                  sizeof(struct dbc_user_param)))  {
175                         ret = -EFAULT;
176                         goto unlock;
177                 }
178                 break;
179         default:
180                 ret = -EINVAL;
181
182         }
183 unlock:
184         mutex_unlock(&dbc_dev->ioctl_mutex);
185
186         return ret;
187 }
188
189 static const struct file_operations dbc_fops = {
190         .owner  = THIS_MODULE,
191         .unlocked_ioctl = dbc_ioctl,
192 };
193
194 int dbc_dev_init(struct psp_device *psp)
195 {
196         struct device *dev = psp->dev;
197         struct psp_dbc_device *dbc_dev;
198         int ret;
199
200         if (!PSP_FEATURE(psp, DBC))
201                 return 0;
202
203         dbc_dev = devm_kzalloc(dev, sizeof(*dbc_dev), GFP_KERNEL);
204         if (!dbc_dev)
205                 return -ENOMEM;
206
207         BUILD_BUG_ON(sizeof(union dbc_buffer) > PAGE_SIZE);
208         dbc_dev->mbox = (void *)devm_get_free_pages(dev, GFP_KERNEL | __GFP_ZERO, 0);
209         if (!dbc_dev->mbox) {
210                 ret = -ENOMEM;
211                 goto cleanup_dev;
212         }
213
214         psp->dbc_data = dbc_dev;
215         dbc_dev->dev = dev;
216
217         ret = send_dbc_nonce(dbc_dev);
218         if (ret == -EACCES) {
219                 dev_dbg(dbc_dev->dev,
220                         "dynamic boost control was previously authenticated\n");
221                 ret = 0;
222         }
223         dev_dbg(dbc_dev->dev, "dynamic boost control is %savailable\n",
224                 ret ? "un" : "");
225         if (ret) {
226                 ret = 0;
227                 goto cleanup_mbox;
228         }
229
230         dbc_dev->char_dev.minor = MISC_DYNAMIC_MINOR;
231         dbc_dev->char_dev.name = "dbc";
232         dbc_dev->char_dev.fops = &dbc_fops;
233         dbc_dev->char_dev.mode = 0600;
234         ret = misc_register(&dbc_dev->char_dev);
235         if (ret)
236                 goto cleanup_mbox;
237
238         mutex_init(&dbc_dev->ioctl_mutex);
239
240         return 0;
241
242 cleanup_mbox:
243         devm_free_pages(dev, (unsigned long)dbc_dev->mbox);
244
245 cleanup_dev:
246         psp->dbc_data = NULL;
247         devm_kfree(dev, dbc_dev);
248
249         return ret;
250 }