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