pfr_telemetry.c 12 KB

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