OSDN Git Service

crypto: ccp - Add support for setting user ID for dynamic boost control
[tomoyo/tomoyo-test1.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 void dbc_dev_destroy(struct psp_device *psp)
78 {
79         struct psp_dbc_device *dbc_dev = psp->dbc_data;
80
81         if (!dbc_dev)
82                 return;
83
84         misc_deregister(&dbc_dev->char_dev);
85         mutex_destroy(&dbc_dev->ioctl_mutex);
86         psp->dbc_data = NULL;
87 }
88
89 static long dbc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
90 {
91         struct psp_device *psp_master = psp_get_master_device();
92         void __user *argp = (void __user *)arg;
93         struct psp_dbc_device *dbc_dev;
94         int ret;
95
96         if (!psp_master || !psp_master->dbc_data)
97                 return -ENODEV;
98         dbc_dev = psp_master->dbc_data;
99
100         mutex_lock(&dbc_dev->ioctl_mutex);
101
102         switch (cmd) {
103         case DBCIOCNONCE:
104                 if (copy_from_user(&dbc_dev->mbox->dbc_nonce.user, argp,
105                                    sizeof(struct dbc_user_nonce))) {
106                         ret = -EFAULT;
107                         goto unlock;
108                 }
109
110                 ret = send_dbc_nonce(dbc_dev);
111                 if (ret)
112                         goto unlock;
113
114                 if (copy_to_user(argp, &dbc_dev->mbox->dbc_nonce.user,
115                                  sizeof(struct dbc_user_nonce))) {
116                         ret = -EFAULT;
117                         goto unlock;
118                 }
119                 break;
120         case DBCIOCUID:
121                 dbc_dev->mbox->req.header.payload_size = sizeof(dbc_dev->mbox->dbc_set_uid);
122                 if (copy_from_user(&dbc_dev->mbox->dbc_set_uid.user, argp,
123                                    sizeof(struct dbc_user_setuid))) {
124                         ret = -EFAULT;
125                         goto unlock;
126                 }
127
128                 ret = send_dbc_cmd(dbc_dev, PSP_DYNAMIC_BOOST_SET_UID);
129                 if (ret)
130                         goto unlock;
131
132                 if (copy_to_user(argp, &dbc_dev->mbox->dbc_set_uid.user,
133                                  sizeof(struct dbc_user_setuid))) {
134                         ret = -EFAULT;
135                         goto unlock;
136                 }
137                 break;
138         default:
139                 ret = -EINVAL;
140
141         }
142 unlock:
143         mutex_unlock(&dbc_dev->ioctl_mutex);
144
145         return ret;
146 }
147
148 static const struct file_operations dbc_fops = {
149         .owner  = THIS_MODULE,
150         .unlocked_ioctl = dbc_ioctl,
151 };
152
153 int dbc_dev_init(struct psp_device *psp)
154 {
155         struct device *dev = psp->dev;
156         struct psp_dbc_device *dbc_dev;
157         int ret;
158
159         if (!PSP_FEATURE(psp, DBC))
160                 return 0;
161
162         dbc_dev = devm_kzalloc(dev, sizeof(*dbc_dev), GFP_KERNEL);
163         if (!dbc_dev)
164                 return -ENOMEM;
165
166         BUILD_BUG_ON(sizeof(union dbc_buffer) > PAGE_SIZE);
167         dbc_dev->mbox = (void *)devm_get_free_pages(dev, GFP_KERNEL, 0);
168         if (!dbc_dev->mbox) {
169                 ret = -ENOMEM;
170                 goto cleanup_dev;
171         }
172
173         psp->dbc_data = dbc_dev;
174         dbc_dev->dev = dev;
175
176         ret = send_dbc_nonce(dbc_dev);
177         if (ret == -EACCES) {
178                 dev_dbg(dbc_dev->dev,
179                         "dynamic boost control was previously authenticated\n");
180                 ret = 0;
181         }
182         dev_dbg(dbc_dev->dev, "dynamic boost control is %savailable\n",
183                 ret ? "un" : "");
184         if (ret) {
185                 ret = 0;
186                 goto cleanup_mbox;
187         }
188
189         dbc_dev->char_dev.minor = MISC_DYNAMIC_MINOR;
190         dbc_dev->char_dev.name = "dbc";
191         dbc_dev->char_dev.fops = &dbc_fops;
192         dbc_dev->char_dev.mode = 0600;
193         ret = misc_register(&dbc_dev->char_dev);
194         if (ret)
195                 goto cleanup_mbox;
196
197         mutex_init(&dbc_dev->ioctl_mutex);
198
199         return 0;
200
201 cleanup_mbox:
202         devm_free_pages(dev, (unsigned long)dbc_dev->mbox);
203
204 cleanup_dev:
205         psp->dbc_data = NULL;
206         devm_kfree(dev, dbc_dev);
207
208         return ret;
209 }