Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * ACPI Platform Firmware Runtime Telemetry driver
0004  *
0005  * Copyright (C) 2021 Intel Corporation
0006  * Author: Chen Yu <yu.c.chen@intel.com>
0007  *
0008  * This driver allows user space to fetch telemetry data from the
0009  * firmware with the help of the Platform Firmware Runtime Telemetry
0010  * interface.
0011  */
0012 #include <linux/acpi.h>
0013 #include <linux/device.h>
0014 #include <linux/err.h>
0015 #include <linux/errno.h>
0016 #include <linux/file.h>
0017 #include <linux/fs.h>
0018 #include <linux/miscdevice.h>
0019 #include <linux/module.h>
0020 #include <linux/mm.h>
0021 #include <linux/platform_device.h>
0022 #include <linux/string.h>
0023 #include <linux/uaccess.h>
0024 #include <linux/uio.h>
0025 #include <linux/uuid.h>
0026 
0027 #include <uapi/linux/pfrut.h>
0028 
0029 #define PFRT_LOG_EXEC_IDX   0
0030 #define PFRT_LOG_HISTORY_IDX    1
0031 
0032 #define PFRT_LOG_ERR        0
0033 #define PFRT_LOG_WARN   1
0034 #define PFRT_LOG_INFO   2
0035 #define PFRT_LOG_VERB   4
0036 
0037 #define PFRT_FUNC_SET_LEV       1
0038 #define PFRT_FUNC_GET_LEV       2
0039 #define PFRT_FUNC_GET_DATA      3
0040 
0041 #define PFRT_REVID_1        1
0042 #define PFRT_REVID_2        2
0043 #define PFRT_DEFAULT_REV_ID PFRT_REVID_1
0044 
0045 enum log_index {
0046     LOG_STATUS_IDX = 0,
0047     LOG_EXT_STATUS_IDX = 1,
0048     LOG_MAX_SZ_IDX = 2,
0049     LOG_CHUNK1_LO_IDX = 3,
0050     LOG_CHUNK1_HI_IDX = 4,
0051     LOG_CHUNK1_SZ_IDX = 5,
0052     LOG_CHUNK2_LO_IDX = 6,
0053     LOG_CHUNK2_HI_IDX = 7,
0054     LOG_CHUNK2_SZ_IDX = 8,
0055     LOG_ROLLOVER_CNT_IDX = 9,
0056     LOG_RESET_CNT_IDX = 10,
0057     LOG_NR_IDX
0058 };
0059 
0060 struct pfrt_log_device {
0061     int index;
0062     struct pfrt_log_info info;
0063     struct device *parent_dev;
0064     struct miscdevice miscdev;
0065 };
0066 
0067 /* pfrt_guid is the parameter for _DSM method */
0068 static const guid_t pfrt_log_guid =
0069     GUID_INIT(0x75191659, 0x8178, 0x4D9D, 0xB8, 0x8F, 0xAC, 0x5E,
0070           0x5E, 0x93, 0xE8, 0xBF);
0071 
0072 static DEFINE_IDA(pfrt_log_ida);
0073 
0074 static inline struct pfrt_log_device *to_pfrt_log_dev(struct file *file)
0075 {
0076     return container_of(file->private_data, struct pfrt_log_device, miscdev);
0077 }
0078 
0079 static int get_pfrt_log_data_info(struct pfrt_log_data_info *data_info,
0080                   struct pfrt_log_device *pfrt_log_dev)
0081 {
0082     acpi_handle handle = ACPI_HANDLE(pfrt_log_dev->parent_dev);
0083     union acpi_object *out_obj, in_obj, in_buf;
0084     int ret = -EBUSY;
0085 
0086     memset(data_info, 0, sizeof(*data_info));
0087     memset(&in_obj, 0, sizeof(in_obj));
0088     memset(&in_buf, 0, sizeof(in_buf));
0089     in_obj.type = ACPI_TYPE_PACKAGE;
0090     in_obj.package.count = 1;
0091     in_obj.package.elements = &in_buf;
0092     in_buf.type = ACPI_TYPE_INTEGER;
0093     in_buf.integer.value = pfrt_log_dev->info.log_type;
0094 
0095     out_obj = acpi_evaluate_dsm_typed(handle, &pfrt_log_guid,
0096                       pfrt_log_dev->info.log_revid, PFRT_FUNC_GET_DATA,
0097                       &in_obj, ACPI_TYPE_PACKAGE);
0098     if (!out_obj)
0099         return -EINVAL;
0100 
0101     if (out_obj->package.count < LOG_NR_IDX ||
0102         out_obj->package.elements[LOG_STATUS_IDX].type != ACPI_TYPE_INTEGER ||
0103         out_obj->package.elements[LOG_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER ||
0104         out_obj->package.elements[LOG_MAX_SZ_IDX].type != ACPI_TYPE_INTEGER ||
0105         out_obj->package.elements[LOG_CHUNK1_LO_IDX].type != ACPI_TYPE_INTEGER ||
0106         out_obj->package.elements[LOG_CHUNK1_HI_IDX].type != ACPI_TYPE_INTEGER ||
0107         out_obj->package.elements[LOG_CHUNK1_SZ_IDX].type != ACPI_TYPE_INTEGER ||
0108         out_obj->package.elements[LOG_CHUNK2_LO_IDX].type != ACPI_TYPE_INTEGER ||
0109         out_obj->package.elements[LOG_CHUNK2_HI_IDX].type != ACPI_TYPE_INTEGER ||
0110         out_obj->package.elements[LOG_CHUNK2_SZ_IDX].type != ACPI_TYPE_INTEGER ||
0111         out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].type != ACPI_TYPE_INTEGER ||
0112         out_obj->package.elements[LOG_RESET_CNT_IDX].type != ACPI_TYPE_INTEGER)
0113         goto free_acpi_buffer;
0114 
0115     data_info->status = out_obj->package.elements[LOG_STATUS_IDX].integer.value;
0116     data_info->ext_status =
0117         out_obj->package.elements[LOG_EXT_STATUS_IDX].integer.value;
0118     if (data_info->status != DSM_SUCCEED) {
0119         dev_dbg(pfrt_log_dev->parent_dev, "Error Status:%d\n", data_info->status);
0120         dev_dbg(pfrt_log_dev->parent_dev, "Error Extend Status:%d\n",
0121             data_info->ext_status);
0122         goto free_acpi_buffer;
0123     }
0124 
0125     data_info->max_data_size =
0126         out_obj->package.elements[LOG_MAX_SZ_IDX].integer.value;
0127     data_info->chunk1_addr_lo =
0128         out_obj->package.elements[LOG_CHUNK1_LO_IDX].integer.value;
0129     data_info->chunk1_addr_hi =
0130         out_obj->package.elements[LOG_CHUNK1_HI_IDX].integer.value;
0131     data_info->chunk1_size =
0132         out_obj->package.elements[LOG_CHUNK1_SZ_IDX].integer.value;
0133     data_info->chunk2_addr_lo =
0134         out_obj->package.elements[LOG_CHUNK2_LO_IDX].integer.value;
0135     data_info->chunk2_addr_hi =
0136         out_obj->package.elements[LOG_CHUNK2_HI_IDX].integer.value;
0137     data_info->chunk2_size =
0138         out_obj->package.elements[LOG_CHUNK2_SZ_IDX].integer.value;
0139     data_info->rollover_cnt =
0140         out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].integer.value;
0141     data_info->reset_cnt =
0142         out_obj->package.elements[LOG_RESET_CNT_IDX].integer.value;
0143 
0144     ret = 0;
0145 
0146 free_acpi_buffer:
0147     kfree(out_obj);
0148 
0149     return ret;
0150 }
0151 
0152 static int set_pfrt_log_level(int level, struct pfrt_log_device *pfrt_log_dev)
0153 {
0154     acpi_handle handle = ACPI_HANDLE(pfrt_log_dev->parent_dev);
0155     union acpi_object *out_obj, *obj, in_obj, in_buf;
0156     enum pfru_dsm_status status, ext_status;
0157     int ret = 0;
0158 
0159     memset(&in_obj, 0, sizeof(in_obj));
0160     memset(&in_buf, 0, sizeof(in_buf));
0161     in_obj.type = ACPI_TYPE_PACKAGE;
0162     in_obj.package.count = 1;
0163     in_obj.package.elements = &in_buf;
0164     in_buf.type = ACPI_TYPE_INTEGER;
0165     in_buf.integer.value = level;
0166 
0167     out_obj = acpi_evaluate_dsm_typed(handle, &pfrt_log_guid,
0168                       pfrt_log_dev->info.log_revid, PFRT_FUNC_SET_LEV,
0169                       &in_obj, ACPI_TYPE_PACKAGE);
0170     if (!out_obj)
0171         return -EINVAL;
0172 
0173     obj = &out_obj->package.elements[0];
0174     status = obj->integer.value;
0175     if (status != DSM_SUCCEED) {
0176         obj = &out_obj->package.elements[1];
0177         ext_status = obj->integer.value;
0178         dev_dbg(pfrt_log_dev->parent_dev, "Error Status:%d\n", status);
0179         dev_dbg(pfrt_log_dev->parent_dev, "Error Extend Status:%d\n", ext_status);
0180         ret = -EBUSY;
0181     }
0182 
0183     kfree(out_obj);
0184 
0185     return ret;
0186 }
0187 
0188 static int get_pfrt_log_level(struct pfrt_log_device *pfrt_log_dev)
0189 {
0190     acpi_handle handle = ACPI_HANDLE(pfrt_log_dev->parent_dev);
0191     union acpi_object *out_obj, *obj;
0192     enum pfru_dsm_status status, ext_status;
0193     int ret = -EBUSY;
0194 
0195     out_obj = acpi_evaluate_dsm_typed(handle, &pfrt_log_guid,
0196                       pfrt_log_dev->info.log_revid, PFRT_FUNC_GET_LEV,
0197                       NULL, ACPI_TYPE_PACKAGE);
0198     if (!out_obj)
0199         return -EINVAL;
0200 
0201     obj = &out_obj->package.elements[0];
0202     if (obj->type != ACPI_TYPE_INTEGER)
0203         goto free_acpi_buffer;
0204 
0205     status = obj->integer.value;
0206     if (status != DSM_SUCCEED) {
0207         obj = &out_obj->package.elements[1];
0208         ext_status = obj->integer.value;
0209         dev_dbg(pfrt_log_dev->parent_dev, "Error Status:%d\n", status);
0210         dev_dbg(pfrt_log_dev->parent_dev, "Error Extend Status:%d\n", ext_status);
0211         goto free_acpi_buffer;
0212     }
0213 
0214     obj = &out_obj->package.elements[2];
0215     if (obj->type != ACPI_TYPE_INTEGER)
0216         goto free_acpi_buffer;
0217 
0218     ret = obj->integer.value;
0219 
0220 free_acpi_buffer:
0221     kfree(out_obj);
0222 
0223     return ret;
0224 }
0225 
0226 static int valid_log_level(u32 level)
0227 {
0228     return level == PFRT_LOG_ERR || level == PFRT_LOG_WARN ||
0229            level == PFRT_LOG_INFO || level == PFRT_LOG_VERB;
0230 }
0231 
0232 static int valid_log_type(u32 type)
0233 {
0234     return type == PFRT_LOG_EXEC_IDX || type == PFRT_LOG_HISTORY_IDX;
0235 }
0236 
0237 static inline int valid_log_revid(u32 id)
0238 {
0239     return id == PFRT_REVID_1 || id == PFRT_REVID_2;
0240 }
0241 
0242 static long pfrt_log_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0243 {
0244     struct pfrt_log_device *pfrt_log_dev = to_pfrt_log_dev(file);
0245     struct pfrt_log_data_info data_info;
0246     struct pfrt_log_info info;
0247     void __user *p;
0248     int ret = 0;
0249 
0250     p = (void __user *)arg;
0251 
0252     switch (cmd) {
0253     case PFRT_LOG_IOC_SET_INFO:
0254         if (copy_from_user(&info, p, sizeof(info)))
0255             return -EFAULT;
0256 
0257         if (valid_log_revid(info.log_revid))
0258             pfrt_log_dev->info.log_revid = info.log_revid;
0259 
0260         if (valid_log_level(info.log_level)) {
0261             ret = set_pfrt_log_level(info.log_level, pfrt_log_dev);
0262             if (ret < 0)
0263                 return ret;
0264 
0265             pfrt_log_dev->info.log_level = info.log_level;
0266         }
0267 
0268         if (valid_log_type(info.log_type))
0269             pfrt_log_dev->info.log_type = info.log_type;
0270 
0271         return 0;
0272 
0273     case PFRT_LOG_IOC_GET_INFO:
0274         info.log_level = get_pfrt_log_level(pfrt_log_dev);
0275         if (ret < 0)
0276             return ret;
0277 
0278         info.log_type = pfrt_log_dev->info.log_type;
0279         info.log_revid = pfrt_log_dev->info.log_revid;
0280         if (copy_to_user(p, &info, sizeof(info)))
0281             return -EFAULT;
0282 
0283         return 0;
0284 
0285     case PFRT_LOG_IOC_GET_DATA_INFO:
0286         ret = get_pfrt_log_data_info(&data_info, pfrt_log_dev);
0287         if (ret)
0288             return ret;
0289 
0290         if (copy_to_user(p, &data_info, sizeof(struct pfrt_log_data_info)))
0291             return -EFAULT;
0292 
0293         return 0;
0294 
0295     default:
0296         return -ENOTTY;
0297     }
0298 }
0299 
0300 static int
0301 pfrt_log_mmap(struct file *file, struct vm_area_struct *vma)
0302 {
0303     struct pfrt_log_device *pfrt_log_dev;
0304     struct pfrt_log_data_info info;
0305     unsigned long psize, vsize;
0306     phys_addr_t base_addr;
0307     int ret;
0308 
0309     if (vma->vm_flags & VM_WRITE)
0310         return -EROFS;
0311 
0312     /* changing from read to write with mprotect is not allowed */
0313     vma->vm_flags &= ~VM_MAYWRITE;
0314 
0315     pfrt_log_dev = to_pfrt_log_dev(file);
0316 
0317     ret = get_pfrt_log_data_info(&info, pfrt_log_dev);
0318     if (ret)
0319         return ret;
0320 
0321     base_addr = (phys_addr_t)((info.chunk2_addr_hi << 32) | info.chunk2_addr_lo);
0322     /* pfrt update has not been launched yet */
0323     if (!base_addr)
0324         return -ENODEV;
0325 
0326     psize = info.max_data_size;
0327     /* base address and total buffer size must be page aligned */
0328     if (!PAGE_ALIGNED(base_addr) || !PAGE_ALIGNED(psize))
0329         return -ENODEV;
0330 
0331     vsize = vma->vm_end - vma->vm_start;
0332     if (vsize > psize)
0333         return -EINVAL;
0334 
0335     vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
0336     if (io_remap_pfn_range(vma, vma->vm_start, PFN_DOWN(base_addr),
0337                    vsize, vma->vm_page_prot))
0338         return -EAGAIN;
0339 
0340     return 0;
0341 }
0342 
0343 static const struct file_operations acpi_pfrt_log_fops = {
0344     .owner      = THIS_MODULE,
0345     .mmap       = pfrt_log_mmap,
0346     .unlocked_ioctl = pfrt_log_ioctl,
0347     .llseek     = noop_llseek,
0348 };
0349 
0350 static int acpi_pfrt_log_remove(struct platform_device *pdev)
0351 {
0352     struct pfrt_log_device *pfrt_log_dev = platform_get_drvdata(pdev);
0353 
0354     misc_deregister(&pfrt_log_dev->miscdev);
0355 
0356     return 0;
0357 }
0358 
0359 static void pfrt_log_put_idx(void *data)
0360 {
0361     struct pfrt_log_device *pfrt_log_dev = data;
0362 
0363     ida_free(&pfrt_log_ida, pfrt_log_dev->index);
0364 }
0365 
0366 static int acpi_pfrt_log_probe(struct platform_device *pdev)
0367 {
0368     acpi_handle handle = ACPI_HANDLE(&pdev->dev);
0369     struct pfrt_log_device *pfrt_log_dev;
0370     int ret;
0371 
0372     if (!acpi_has_method(handle, "_DSM")) {
0373         dev_dbg(&pdev->dev, "Missing _DSM\n");
0374         return -ENODEV;
0375     }
0376 
0377     pfrt_log_dev = devm_kzalloc(&pdev->dev, sizeof(*pfrt_log_dev), GFP_KERNEL);
0378     if (!pfrt_log_dev)
0379         return -ENOMEM;
0380 
0381     ret = ida_alloc(&pfrt_log_ida, GFP_KERNEL);
0382     if (ret < 0)
0383         return ret;
0384 
0385     pfrt_log_dev->index = ret;
0386     ret = devm_add_action_or_reset(&pdev->dev, pfrt_log_put_idx, pfrt_log_dev);
0387     if (ret)
0388         return ret;
0389 
0390     pfrt_log_dev->info.log_revid = PFRT_DEFAULT_REV_ID;
0391     pfrt_log_dev->parent_dev = &pdev->dev;
0392 
0393     pfrt_log_dev->miscdev.minor = MISC_DYNAMIC_MINOR;
0394     pfrt_log_dev->miscdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
0395                             "pfrt%d",
0396                             pfrt_log_dev->index);
0397     if (!pfrt_log_dev->miscdev.name)
0398         return -ENOMEM;
0399 
0400     pfrt_log_dev->miscdev.nodename = devm_kasprintf(&pdev->dev, GFP_KERNEL,
0401                             "acpi_pfr_telemetry%d",
0402                             pfrt_log_dev->index);
0403     if (!pfrt_log_dev->miscdev.nodename)
0404         return -ENOMEM;
0405 
0406     pfrt_log_dev->miscdev.fops = &acpi_pfrt_log_fops;
0407     pfrt_log_dev->miscdev.parent = &pdev->dev;
0408 
0409     ret = misc_register(&pfrt_log_dev->miscdev);
0410     if (ret)
0411         return ret;
0412 
0413     platform_set_drvdata(pdev, pfrt_log_dev);
0414 
0415     return 0;
0416 }
0417 
0418 static const struct acpi_device_id acpi_pfrt_log_ids[] = {
0419     {"INTC1081"},
0420     {}
0421 };
0422 MODULE_DEVICE_TABLE(acpi, acpi_pfrt_log_ids);
0423 
0424 static struct platform_driver acpi_pfrt_log_driver = {
0425     .driver = {
0426         .name = "pfr_telemetry",
0427         .acpi_match_table = acpi_pfrt_log_ids,
0428     },
0429     .probe = acpi_pfrt_log_probe,
0430     .remove = acpi_pfrt_log_remove,
0431 };
0432 module_platform_driver(acpi_pfrt_log_driver);
0433 
0434 MODULE_DESCRIPTION("Platform Firmware Runtime Update Telemetry driver");
0435 MODULE_LICENSE("GPL v2");