0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
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
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
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
0323 if (!base_addr)
0324 return -ENODEV;
0325
0326 psize = info.max_data_size;
0327
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");