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