1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * ACPI Platform Firmware Runtime Telemetry driver 4 * 5 * Copyright (C) 2021 Intel Corporation 6 * Author: Chen Yu <yu.c.chen@intel.com> 7 * 8 * This driver allows user space to fetch telemetry data from the 9 * firmware with the help of the Platform Firmware Runtime Telemetry 10 * interface. 11 */ 12 #include <linux/acpi.h> 13 #include <linux/device.h> 14 #include <linux/err.h> 15 #include <linux/errno.h> 16 #include <linux/file.h> 17 #include <linux/fs.h> 18 #include <linux/miscdevice.h> 19 #include <linux/module.h> 20 #include <linux/mm.h> 21 #include <linux/platform_device.h> 22 #include <linux/string.h> 23 #include <linux/uaccess.h> 24 #include <linux/uio.h> 25 #include <linux/uuid.h> 26 27 #include <uapi/linux/pfrut.h> 28 29 #define PFRT_LOG_EXEC_IDX 0 30 #define PFRT_LOG_HISTORY_IDX 1 31 32 #define PFRT_LOG_ERR 0 33 #define PFRT_LOG_WARN 1 34 #define PFRT_LOG_INFO 2 35 #define PFRT_LOG_VERB 4 36 37 #define PFRT_FUNC_SET_LEV 1 38 #define PFRT_FUNC_GET_LEV 2 39 #define PFRT_FUNC_GET_DATA 3 40 41 #define PFRT_REVID_1 1 42 #define PFRT_REVID_2 2 43 #define PFRT_DEFAULT_REV_ID PFRT_REVID_1 44 45 enum log_index { 46 LOG_STATUS_IDX = 0, 47 LOG_EXT_STATUS_IDX = 1, 48 LOG_MAX_SZ_IDX = 2, 49 LOG_CHUNK1_LO_IDX = 3, 50 LOG_CHUNK1_HI_IDX = 4, 51 LOG_CHUNK1_SZ_IDX = 5, 52 LOG_CHUNK2_LO_IDX = 6, 53 LOG_CHUNK2_HI_IDX = 7, 54 LOG_CHUNK2_SZ_IDX = 8, 55 LOG_ROLLOVER_CNT_IDX = 9, 56 LOG_RESET_CNT_IDX = 10, 57 LOG_NR_IDX 58 }; 59 60 struct pfrt_log_device { 61 int index; 62 struct pfrt_log_info info; 63 struct device *parent_dev; 64 struct miscdevice miscdev; 65 }; 66 67 /* pfrt_guid is the parameter for _DSM method */ 68 static const guid_t pfrt_log_guid = 69 GUID_INIT(0x75191659, 0x8178, 0x4D9D, 0xB8, 0x8F, 0xAC, 0x5E, 70 0x5E, 0x93, 0xE8, 0xBF); 71 72 static DEFINE_IDA(pfrt_log_ida); 73 74 static inline struct pfrt_log_device *to_pfrt_log_dev(struct file *file) 75 { 76 return container_of(file->private_data, struct pfrt_log_device, miscdev); 77 } 78 79 static int get_pfrt_log_data_info(struct pfrt_log_data_info *data_info, 80 struct pfrt_log_device *pfrt_log_dev) 81 { 82 acpi_handle handle = ACPI_HANDLE(pfrt_log_dev->parent_dev); 83 union acpi_object *out_obj, in_obj, in_buf; 84 int ret = -EBUSY; 85 86 memset(data_info, 0, sizeof(*data_info)); 87 memset(&in_obj, 0, sizeof(in_obj)); 88 memset(&in_buf, 0, sizeof(in_buf)); 89 in_obj.type = ACPI_TYPE_PACKAGE; 90 in_obj.package.count = 1; 91 in_obj.package.elements = &in_buf; 92 in_buf.type = ACPI_TYPE_INTEGER; 93 in_buf.integer.value = pfrt_log_dev->info.log_type; 94 95 out_obj = acpi_evaluate_dsm_typed(handle, &pfrt_log_guid, 96 pfrt_log_dev->info.log_revid, PFRT_FUNC_GET_DATA, 97 &in_obj, ACPI_TYPE_PACKAGE); 98 if (!out_obj) 99 return -EINVAL; 100 101 if (out_obj->package.count < LOG_NR_IDX || 102 out_obj->package.elements[LOG_STATUS_IDX].type != ACPI_TYPE_INTEGER || 103 out_obj->package.elements[LOG_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER || 104 out_obj->package.elements[LOG_MAX_SZ_IDX].type != ACPI_TYPE_INTEGER || 105 out_obj->package.elements[LOG_CHUNK1_LO_IDX].type != ACPI_TYPE_INTEGER || 106 out_obj->package.elements[LOG_CHUNK1_HI_IDX].type != ACPI_TYPE_INTEGER || 107 out_obj->package.elements[LOG_CHUNK1_SZ_IDX].type != ACPI_TYPE_INTEGER || 108 out_obj->package.elements[LOG_CHUNK2_LO_IDX].type != ACPI_TYPE_INTEGER || 109 out_obj->package.elements[LOG_CHUNK2_HI_IDX].type != ACPI_TYPE_INTEGER || 110 out_obj->package.elements[LOG_CHUNK2_SZ_IDX].type != ACPI_TYPE_INTEGER || 111 out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].type != ACPI_TYPE_INTEGER || 112 out_obj->package.elements[LOG_RESET_CNT_IDX].type != ACPI_TYPE_INTEGER) 113 goto free_acpi_buffer; 114 115 data_info->status = out_obj->package.elements[LOG_STATUS_IDX].integer.value; 116 data_info->ext_status = 117 out_obj->package.elements[LOG_EXT_STATUS_IDX].integer.value; 118 if (data_info->status != DSM_SUCCEED) { 119 dev_dbg(pfrt_log_dev->parent_dev, "Error Status:%d\n", data_info->status); 120 dev_dbg(pfrt_log_dev->parent_dev, "Error Extend Status:%d\n", 121 data_info->ext_status); 122 goto free_acpi_buffer; 123 } 124 125 data_info->max_data_size = 126 out_obj->package.elements[LOG_MAX_SZ_IDX].integer.value; 127 data_info->chunk1_addr_lo = 128 out_obj->package.elements[LOG_CHUNK1_LO_IDX].integer.value; 129 data_info->chunk1_addr_hi = 130 out_obj->package.elements[LOG_CHUNK1_HI_IDX].integer.value; 131 data_info->chunk1_size = 132 out_obj->package.elements[LOG_CHUNK1_SZ_IDX].integer.value; 133 data_info->chunk2_addr_lo = 134 out_obj->package.elements[LOG_CHUNK2_LO_IDX].integer.value; 135 data_info->chunk2_addr_hi = 136 out_obj->package.elements[LOG_CHUNK2_HI_IDX].integer.value; 137 data_info->chunk2_size = 138 out_obj->package.elements[LOG_CHUNK2_SZ_IDX].integer.value; 139 data_info->rollover_cnt = 140 out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].integer.value; 141 data_info->reset_cnt = 142 out_obj->package.elements[LOG_RESET_CNT_IDX].integer.value; 143 144 ret = 0; 145 146 free_acpi_buffer: 147 ACPI_FREE(out_obj); 148 149 return ret; 150 } 151 152 static int set_pfrt_log_level(int level, struct pfrt_log_device *pfrt_log_dev) 153 { 154 acpi_handle handle = ACPI_HANDLE(pfrt_log_dev->parent_dev); 155 union acpi_object *out_obj, *obj, in_obj, in_buf; 156 enum pfru_dsm_status status, ext_status; 157 int ret = 0; 158 159 memset(&in_obj, 0, sizeof(in_obj)); 160 memset(&in_buf, 0, sizeof(in_buf)); 161 in_obj.type = ACPI_TYPE_PACKAGE; 162 in_obj.package.count = 1; 163 in_obj.package.elements = &in_buf; 164 in_buf.type = ACPI_TYPE_INTEGER; 165 in_buf.integer.value = level; 166 167 out_obj = acpi_evaluate_dsm_typed(handle, &pfrt_log_guid, 168 pfrt_log_dev->info.log_revid, PFRT_FUNC_SET_LEV, 169 &in_obj, ACPI_TYPE_PACKAGE); 170 if (!out_obj) 171 return -EINVAL; 172 173 obj = &out_obj->package.elements[0]; 174 status = obj->integer.value; 175 if (status != DSM_SUCCEED) { 176 obj = &out_obj->package.elements[1]; 177 ext_status = obj->integer.value; 178 dev_dbg(pfrt_log_dev->parent_dev, "Error Status:%d\n", status); 179 dev_dbg(pfrt_log_dev->parent_dev, "Error Extend Status:%d\n", ext_status); 180 ret = -EBUSY; 181 } 182 183 ACPI_FREE(out_obj); 184 185 return ret; 186 } 187 188 static int get_pfrt_log_level(struct pfrt_log_device *pfrt_log_dev) 189 { 190 acpi_handle handle = ACPI_HANDLE(pfrt_log_dev->parent_dev); 191 union acpi_object *out_obj, *obj; 192 enum pfru_dsm_status status, ext_status; 193 int ret = -EBUSY; 194 195 out_obj = acpi_evaluate_dsm_typed(handle, &pfrt_log_guid, 196 pfrt_log_dev->info.log_revid, PFRT_FUNC_GET_LEV, 197 NULL, ACPI_TYPE_PACKAGE); 198 if (!out_obj) 199 return -EINVAL; 200 201 obj = &out_obj->package.elements[0]; 202 if (obj->type != ACPI_TYPE_INTEGER) 203 goto free_acpi_buffer; 204 205 status = obj->integer.value; 206 if (status != DSM_SUCCEED) { 207 obj = &out_obj->package.elements[1]; 208 ext_status = obj->integer.value; 209 dev_dbg(pfrt_log_dev->parent_dev, "Error Status:%d\n", status); 210 dev_dbg(pfrt_log_dev->parent_dev, "Error Extend Status:%d\n", ext_status); 211 goto free_acpi_buffer; 212 } 213 214 obj = &out_obj->package.elements[2]; 215 if (obj->type != ACPI_TYPE_INTEGER) 216 goto free_acpi_buffer; 217 218 ret = obj->integer.value; 219 220 free_acpi_buffer: 221 ACPI_FREE(out_obj); 222 223 return ret; 224 } 225 226 static int valid_log_level(u32 level) 227 { 228 return level == PFRT_LOG_ERR || level == PFRT_LOG_WARN || 229 level == PFRT_LOG_INFO || level == PFRT_LOG_VERB; 230 } 231 232 static int valid_log_type(u32 type) 233 { 234 return type == PFRT_LOG_EXEC_IDX || type == PFRT_LOG_HISTORY_IDX; 235 } 236 237 static inline int valid_log_revid(u32 id) 238 { 239 return id == PFRT_REVID_1 || id == PFRT_REVID_2; 240 } 241 242 static long pfrt_log_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 243 { 244 struct pfrt_log_device *pfrt_log_dev = to_pfrt_log_dev(file); 245 struct pfrt_log_data_info data_info; 246 struct pfrt_log_info info; 247 void __user *p; 248 int ret = 0; 249 250 p = (void __user *)arg; 251 252 switch (cmd) { 253 case PFRT_LOG_IOC_SET_INFO: 254 if (copy_from_user(&info, p, sizeof(info))) 255 return -EFAULT; 256 257 if (valid_log_revid(info.log_revid)) 258 pfrt_log_dev->info.log_revid = info.log_revid; 259 260 if (valid_log_level(info.log_level)) { 261 ret = set_pfrt_log_level(info.log_level, pfrt_log_dev); 262 if (ret < 0) 263 return ret; 264 265 pfrt_log_dev->info.log_level = info.log_level; 266 } 267 268 if (valid_log_type(info.log_type)) 269 pfrt_log_dev->info.log_type = info.log_type; 270 271 return 0; 272 273 case PFRT_LOG_IOC_GET_INFO: 274 info.log_level = get_pfrt_log_level(pfrt_log_dev); 275 info.log_type = pfrt_log_dev->info.log_type; 276 info.log_revid = pfrt_log_dev->info.log_revid; 277 if (copy_to_user(p, &info, sizeof(info))) 278 return -EFAULT; 279 280 return 0; 281 282 case PFRT_LOG_IOC_GET_DATA_INFO: 283 ret = get_pfrt_log_data_info(&data_info, pfrt_log_dev); 284 if (ret) 285 return ret; 286 287 if (copy_to_user(p, &data_info, sizeof(struct pfrt_log_data_info))) 288 return -EFAULT; 289 290 return 0; 291 292 default: 293 return -ENOTTY; 294 } 295 } 296 297 static int 298 pfrt_log_mmap(struct file *file, struct vm_area_struct *vma) 299 { 300 struct pfrt_log_device *pfrt_log_dev; 301 struct pfrt_log_data_info info; 302 unsigned long psize, vsize; 303 phys_addr_t base_addr; 304 int ret; 305 306 if (vma->vm_flags & VM_WRITE) 307 return -EROFS; 308 309 /* changing from read to write with mprotect is not allowed */ 310 vm_flags_clear(vma, VM_MAYWRITE); 311 312 pfrt_log_dev = to_pfrt_log_dev(file); 313 314 ret = get_pfrt_log_data_info(&info, pfrt_log_dev); 315 if (ret) 316 return ret; 317 318 base_addr = (phys_addr_t)((info.chunk2_addr_hi << 32) | info.chunk2_addr_lo); 319 /* pfrt update has not been launched yet */ 320 if (!base_addr) 321 return -ENODEV; 322 323 psize = info.max_data_size; 324 /* base address and total buffer size must be page aligned */ 325 if (!PAGE_ALIGNED(base_addr) || !PAGE_ALIGNED(psize)) 326 return -ENODEV; 327 328 vsize = vma->vm_end - vma->vm_start; 329 if (vsize > psize) 330 return -EINVAL; 331 332 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 333 if (io_remap_pfn_range(vma, vma->vm_start, PFN_DOWN(base_addr), 334 vsize, vma->vm_page_prot)) 335 return -EAGAIN; 336 337 return 0; 338 } 339 340 static const struct file_operations acpi_pfrt_log_fops = { 341 .owner = THIS_MODULE, 342 .mmap = pfrt_log_mmap, 343 .unlocked_ioctl = pfrt_log_ioctl, 344 .llseek = noop_llseek, 345 }; 346 347 static void acpi_pfrt_log_remove(struct platform_device *pdev) 348 { 349 struct pfrt_log_device *pfrt_log_dev = platform_get_drvdata(pdev); 350 351 misc_deregister(&pfrt_log_dev->miscdev); 352 } 353 354 static void pfrt_log_put_idx(void *data) 355 { 356 struct pfrt_log_device *pfrt_log_dev = data; 357 358 ida_free(&pfrt_log_ida, pfrt_log_dev->index); 359 } 360 361 static int acpi_pfrt_log_probe(struct platform_device *pdev) 362 { 363 acpi_handle handle = ACPI_HANDLE(&pdev->dev); 364 struct pfrt_log_device *pfrt_log_dev; 365 int ret; 366 367 if (!acpi_has_method(handle, "_DSM")) { 368 dev_dbg(&pdev->dev, "Missing _DSM\n"); 369 return -ENODEV; 370 } 371 372 pfrt_log_dev = devm_kzalloc(&pdev->dev, sizeof(*pfrt_log_dev), GFP_KERNEL); 373 if (!pfrt_log_dev) 374 return -ENOMEM; 375 376 ret = ida_alloc(&pfrt_log_ida, GFP_KERNEL); 377 if (ret < 0) 378 return ret; 379 380 pfrt_log_dev->index = ret; 381 ret = devm_add_action_or_reset(&pdev->dev, pfrt_log_put_idx, pfrt_log_dev); 382 if (ret) 383 return ret; 384 385 pfrt_log_dev->info.log_revid = PFRT_DEFAULT_REV_ID; 386 pfrt_log_dev->parent_dev = &pdev->dev; 387 388 pfrt_log_dev->miscdev.minor = MISC_DYNAMIC_MINOR; 389 pfrt_log_dev->miscdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, 390 "pfrt%d", 391 pfrt_log_dev->index); 392 if (!pfrt_log_dev->miscdev.name) 393 return -ENOMEM; 394 395 pfrt_log_dev->miscdev.nodename = devm_kasprintf(&pdev->dev, GFP_KERNEL, 396 "acpi_pfr_telemetry%d", 397 pfrt_log_dev->index); 398 if (!pfrt_log_dev->miscdev.nodename) 399 return -ENOMEM; 400 401 pfrt_log_dev->miscdev.fops = &acpi_pfrt_log_fops; 402 pfrt_log_dev->miscdev.parent = &pdev->dev; 403 404 ret = misc_register(&pfrt_log_dev->miscdev); 405 if (ret) 406 return ret; 407 408 platform_set_drvdata(pdev, pfrt_log_dev); 409 410 return 0; 411 } 412 413 static const struct acpi_device_id acpi_pfrt_log_ids[] = { 414 {"INTC1081"}, 415 {} 416 }; 417 MODULE_DEVICE_TABLE(acpi, acpi_pfrt_log_ids); 418 419 static struct platform_driver acpi_pfrt_log_driver = { 420 .driver = { 421 .name = "pfr_telemetry", 422 .acpi_match_table = acpi_pfrt_log_ids, 423 }, 424 .probe = acpi_pfrt_log_probe, 425 .remove = acpi_pfrt_log_remove, 426 }; 427 module_platform_driver(acpi_pfrt_log_driver); 428 429 MODULE_DESCRIPTION("Platform Firmware Runtime Update Telemetry driver"); 430 MODULE_LICENSE("GPL v2"); 431