xref: /openbmc/linux/drivers/crypto/ccp/dbc.c (revision 4c2c057d)
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