1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2012-2016, Intel Corporation. All rights reserved 4 * Intel Management Engine Interface (Intel MEI) Linux driver 5 */ 6 7 #include <linux/slab.h> 8 #include <linux/kernel.h> 9 #include <linux/device.h> 10 #include <linux/debugfs.h> 11 12 #include <linux/mei.h> 13 14 #include "mei_dev.h" 15 #include "client.h" 16 #include "hw.h" 17 18 static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf, 19 size_t cnt, loff_t *ppos) 20 { 21 struct mei_device *dev = fp->private_data; 22 struct mei_me_client *me_cl; 23 size_t bufsz = 1; 24 char *buf; 25 int i = 0; 26 int pos = 0; 27 int ret; 28 29 #define HDR \ 30 " |id|fix| UUID |con|msg len|sb|refc|\n" 31 32 down_read(&dev->me_clients_rwsem); 33 list_for_each_entry(me_cl, &dev->me_clients, list) 34 bufsz++; 35 36 bufsz *= sizeof(HDR) + 1; 37 buf = kzalloc(bufsz, GFP_KERNEL); 38 if (!buf) { 39 up_read(&dev->me_clients_rwsem); 40 return -ENOMEM; 41 } 42 43 pos += scnprintf(buf + pos, bufsz - pos, HDR); 44 #undef HDR 45 46 /* if the driver is not enabled the list won't be consistent */ 47 if (dev->dev_state != MEI_DEV_ENABLED) 48 goto out; 49 50 list_for_each_entry(me_cl, &dev->me_clients, list) { 51 52 if (mei_me_cl_get(me_cl)) { 53 pos += scnprintf(buf + pos, bufsz - pos, 54 "%2d|%2d|%3d|%pUl|%3d|%7d|%2d|%4d|\n", 55 i++, me_cl->client_id, 56 me_cl->props.fixed_address, 57 &me_cl->props.protocol_name, 58 me_cl->props.max_number_of_connections, 59 me_cl->props.max_msg_length, 60 me_cl->props.single_recv_buf, 61 kref_read(&me_cl->refcnt)); 62 63 mei_me_cl_put(me_cl); 64 } 65 } 66 67 out: 68 up_read(&dev->me_clients_rwsem); 69 ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); 70 kfree(buf); 71 return ret; 72 } 73 74 static const struct file_operations mei_dbgfs_fops_meclients = { 75 .open = simple_open, 76 .read = mei_dbgfs_read_meclients, 77 .llseek = generic_file_llseek, 78 }; 79 80 static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, 81 size_t cnt, loff_t *ppos) 82 { 83 struct mei_device *dev = fp->private_data; 84 struct mei_cl *cl; 85 size_t bufsz = 1; 86 char *buf; 87 int i = 0; 88 int pos = 0; 89 int ret; 90 91 #define HDR " |me|host|state|rd|wr|wrq\n" 92 93 if (!dev) 94 return -ENODEV; 95 96 mutex_lock(&dev->device_lock); 97 98 /* 99 * if the driver is not enabled the list won't be consistent, 100 * we output empty table 101 */ 102 if (dev->dev_state == MEI_DEV_ENABLED) 103 list_for_each_entry(cl, &dev->file_list, link) 104 bufsz++; 105 106 bufsz *= sizeof(HDR) + 1; 107 108 buf = kzalloc(bufsz, GFP_KERNEL); 109 if (!buf) { 110 mutex_unlock(&dev->device_lock); 111 return -ENOMEM; 112 } 113 114 pos += scnprintf(buf + pos, bufsz - pos, HDR); 115 #undef HDR 116 117 /* if the driver is not enabled the list won't be consistent */ 118 if (dev->dev_state != MEI_DEV_ENABLED) 119 goto out; 120 121 list_for_each_entry(cl, &dev->file_list, link) { 122 123 pos += scnprintf(buf + pos, bufsz - pos, 124 "%3d|%2d|%4d|%5d|%2d|%2d|%3u\n", 125 i, mei_cl_me_id(cl), cl->host_client_id, cl->state, 126 !list_empty(&cl->rd_completed), cl->writing_state, 127 cl->tx_cb_queued); 128 i++; 129 } 130 out: 131 mutex_unlock(&dev->device_lock); 132 ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); 133 kfree(buf); 134 return ret; 135 } 136 137 static const struct file_operations mei_dbgfs_fops_active = { 138 .open = simple_open, 139 .read = mei_dbgfs_read_active, 140 .llseek = generic_file_llseek, 141 }; 142 143 static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, 144 size_t cnt, loff_t *ppos) 145 { 146 struct mei_device *dev = fp->private_data; 147 const size_t bufsz = 1024; 148 char *buf = kzalloc(bufsz, GFP_KERNEL); 149 int pos = 0; 150 int ret; 151 152 if (!buf) 153 return -ENOMEM; 154 155 pos += scnprintf(buf + pos, bufsz - pos, "dev: %s\n", 156 mei_dev_state_str(dev->dev_state)); 157 pos += scnprintf(buf + pos, bufsz - pos, "hbm: %s\n", 158 mei_hbm_state_str(dev->hbm_state)); 159 160 if (dev->hbm_state >= MEI_HBM_ENUM_CLIENTS && 161 dev->hbm_state <= MEI_HBM_STARTED) { 162 pos += scnprintf(buf + pos, bufsz - pos, "hbm features:\n"); 163 pos += scnprintf(buf + pos, bufsz - pos, "\tPG: %01d\n", 164 dev->hbm_f_pg_supported); 165 pos += scnprintf(buf + pos, bufsz - pos, "\tDC: %01d\n", 166 dev->hbm_f_dc_supported); 167 pos += scnprintf(buf + pos, bufsz - pos, "\tIE: %01d\n", 168 dev->hbm_f_ie_supported); 169 pos += scnprintf(buf + pos, bufsz - pos, "\tDOT: %01d\n", 170 dev->hbm_f_dot_supported); 171 pos += scnprintf(buf + pos, bufsz - pos, "\tEV: %01d\n", 172 dev->hbm_f_ev_supported); 173 pos += scnprintf(buf + pos, bufsz - pos, "\tFA: %01d\n", 174 dev->hbm_f_fa_supported); 175 pos += scnprintf(buf + pos, bufsz - pos, "\tOS: %01d\n", 176 dev->hbm_f_os_supported); 177 pos += scnprintf(buf + pos, bufsz - pos, "\tDR: %01d\n", 178 dev->hbm_f_dr_supported); 179 } 180 181 pos += scnprintf(buf + pos, bufsz - pos, "pg: %s, %s\n", 182 mei_pg_is_enabled(dev) ? "ENABLED" : "DISABLED", 183 mei_pg_state_str(mei_pg_state(dev))); 184 ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); 185 kfree(buf); 186 return ret; 187 } 188 static const struct file_operations mei_dbgfs_fops_devstate = { 189 .open = simple_open, 190 .read = mei_dbgfs_read_devstate, 191 .llseek = generic_file_llseek, 192 }; 193 194 static ssize_t mei_dbgfs_write_allow_fa(struct file *file, 195 const char __user *user_buf, 196 size_t count, loff_t *ppos) 197 { 198 struct mei_device *dev; 199 int ret; 200 201 dev = container_of(file->private_data, 202 struct mei_device, allow_fixed_address); 203 204 ret = debugfs_write_file_bool(file, user_buf, count, ppos); 205 if (ret < 0) 206 return ret; 207 dev->override_fixed_address = true; 208 return ret; 209 } 210 211 static const struct file_operations mei_dbgfs_fops_allow_fa = { 212 .open = simple_open, 213 .read = debugfs_read_file_bool, 214 .write = mei_dbgfs_write_allow_fa, 215 .llseek = generic_file_llseek, 216 }; 217 218 /** 219 * mei_dbgfs_deregister - Remove the debugfs files and directories 220 * 221 * @dev: the mei device structure 222 */ 223 void mei_dbgfs_deregister(struct mei_device *dev) 224 { 225 if (!dev->dbgfs_dir) 226 return; 227 debugfs_remove_recursive(dev->dbgfs_dir); 228 dev->dbgfs_dir = NULL; 229 } 230 231 /** 232 * mei_dbgfs_register - Add the debugfs files 233 * 234 * @dev: the mei device structure 235 * @name: the mei device name 236 * 237 * Return: 0 on success, <0 on failure. 238 */ 239 int mei_dbgfs_register(struct mei_device *dev, const char *name) 240 { 241 struct dentry *dir, *f; 242 243 dir = debugfs_create_dir(name, NULL); 244 if (!dir) 245 return -ENOMEM; 246 247 dev->dbgfs_dir = dir; 248 249 f = debugfs_create_file("meclients", S_IRUSR, dir, 250 dev, &mei_dbgfs_fops_meclients); 251 if (!f) { 252 dev_err(dev->dev, "meclients: registration failed\n"); 253 goto err; 254 } 255 f = debugfs_create_file("active", S_IRUSR, dir, 256 dev, &mei_dbgfs_fops_active); 257 if (!f) { 258 dev_err(dev->dev, "active: registration failed\n"); 259 goto err; 260 } 261 f = debugfs_create_file("devstate", S_IRUSR, dir, 262 dev, &mei_dbgfs_fops_devstate); 263 if (!f) { 264 dev_err(dev->dev, "devstate: registration failed\n"); 265 goto err; 266 } 267 f = debugfs_create_file("allow_fixed_address", S_IRUSR | S_IWUSR, dir, 268 &dev->allow_fixed_address, 269 &mei_dbgfs_fops_allow_fa); 270 if (!f) { 271 dev_err(dev->dev, "allow_fixed_address: registration failed\n"); 272 goto err; 273 } 274 return 0; 275 err: 276 mei_dbgfs_deregister(dev); 277 return -ENODEV; 278 } 279 280