1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Intel Platform Monitory Technology Telemetry driver
4  *
5  * Copyright (c) 2020, Intel Corporation.
6  * All Rights Reserved.
7  *
8  * Author: "Alexander Duyck" <alexander.h.duyck@linux.intel.com>
9  */
10 
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/mm.h>
14 #include <linux/pci.h>
15 
16 #include "../vsec.h"
17 #include "class.h"
18 
19 #define PMT_XA_START		0
20 #define PMT_XA_MAX		INT_MAX
21 #define PMT_XA_LIMIT		XA_LIMIT(PMT_XA_START, PMT_XA_MAX)
22 
23 bool intel_pmt_is_early_client_hw(struct device *dev)
24 {
25 	struct intel_vsec_device *ivdev = dev_to_ivdev(dev);
26 
27 	/*
28 	 * Early implementations of PMT on client platforms have some
29 	 * differences from the server platforms (which use the Out Of Band
30 	 * Management Services Module OOBMSM).
31 	 */
32 	return !!(ivdev->info->quirks & VSEC_QUIRK_EARLY_HW);
33 }
34 EXPORT_SYMBOL_GPL(intel_pmt_is_early_client_hw);
35 
36 /*
37  * sysfs
38  */
39 static ssize_t
40 intel_pmt_read(struct file *filp, struct kobject *kobj,
41 	       struct bin_attribute *attr, char *buf, loff_t off,
42 	       size_t count)
43 {
44 	struct intel_pmt_entry *entry = container_of(attr,
45 						     struct intel_pmt_entry,
46 						     pmt_bin_attr);
47 
48 	if (off < 0)
49 		return -EINVAL;
50 
51 	if (off >= entry->size)
52 		return 0;
53 
54 	if (count > entry->size - off)
55 		count = entry->size - off;
56 
57 	memcpy_fromio(buf, entry->base + off, count);
58 
59 	return count;
60 }
61 
62 static int
63 intel_pmt_mmap(struct file *filp, struct kobject *kobj,
64 		struct bin_attribute *attr, struct vm_area_struct *vma)
65 {
66 	struct intel_pmt_entry *entry = container_of(attr,
67 						     struct intel_pmt_entry,
68 						     pmt_bin_attr);
69 	unsigned long vsize = vma->vm_end - vma->vm_start;
70 	struct device *dev = kobj_to_dev(kobj);
71 	unsigned long phys = entry->base_addr;
72 	unsigned long pfn = PFN_DOWN(phys);
73 	unsigned long psize;
74 
75 	if (vma->vm_flags & (VM_WRITE | VM_MAYWRITE))
76 		return -EROFS;
77 
78 	psize = (PFN_UP(entry->base_addr + entry->size) - pfn) * PAGE_SIZE;
79 	if (vsize > psize) {
80 		dev_err(dev, "Requested mmap size is too large\n");
81 		return -EINVAL;
82 	}
83 
84 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
85 	if (io_remap_pfn_range(vma, vma->vm_start, pfn,
86 		vsize, vma->vm_page_prot))
87 		return -EAGAIN;
88 
89 	return 0;
90 }
91 
92 static ssize_t
93 guid_show(struct device *dev, struct device_attribute *attr, char *buf)
94 {
95 	struct intel_pmt_entry *entry = dev_get_drvdata(dev);
96 
97 	return sprintf(buf, "0x%x\n", entry->guid);
98 }
99 static DEVICE_ATTR_RO(guid);
100 
101 static ssize_t size_show(struct device *dev, struct device_attribute *attr,
102 			 char *buf)
103 {
104 	struct intel_pmt_entry *entry = dev_get_drvdata(dev);
105 
106 	return sprintf(buf, "%zu\n", entry->size);
107 }
108 static DEVICE_ATTR_RO(size);
109 
110 static ssize_t
111 offset_show(struct device *dev, struct device_attribute *attr, char *buf)
112 {
113 	struct intel_pmt_entry *entry = dev_get_drvdata(dev);
114 
115 	return sprintf(buf, "%lu\n", offset_in_page(entry->base_addr));
116 }
117 static DEVICE_ATTR_RO(offset);
118 
119 static struct attribute *intel_pmt_attrs[] = {
120 	&dev_attr_guid.attr,
121 	&dev_attr_size.attr,
122 	&dev_attr_offset.attr,
123 	NULL
124 };
125 ATTRIBUTE_GROUPS(intel_pmt);
126 
127 static struct class intel_pmt_class = {
128 	.name = "intel_pmt",
129 	.owner = THIS_MODULE,
130 	.dev_groups = intel_pmt_groups,
131 };
132 
133 static int intel_pmt_populate_entry(struct intel_pmt_entry *entry,
134 				    struct intel_pmt_header *header,
135 				    struct device *dev,
136 				    struct resource *disc_res)
137 {
138 	struct pci_dev *pci_dev = to_pci_dev(dev->parent);
139 	u8 bir;
140 
141 	/*
142 	 * The base offset should always be 8 byte aligned.
143 	 *
144 	 * For non-local access types the lower 3 bits of base offset
145 	 * contains the index of the base address register where the
146 	 * telemetry can be found.
147 	 */
148 	bir = GET_BIR(header->base_offset);
149 
150 	/* Local access and BARID only for now */
151 	switch (header->access_type) {
152 	case ACCESS_LOCAL:
153 		if (bir) {
154 			dev_err(dev,
155 				"Unsupported BAR index %d for access type %d\n",
156 				bir, header->access_type);
157 			return -EINVAL;
158 		}
159 		/*
160 		 * For access_type LOCAL, the base address is as follows:
161 		 * base address = end of discovery region + base offset
162 		 */
163 		entry->base_addr = disc_res->end + 1 + header->base_offset;
164 
165 		/*
166 		 * Some hardware use a different calculation for the base address
167 		 * when access_type == ACCESS_LOCAL. On the these systems
168 		 * ACCCESS_LOCAL refers to an address in the same BAR as the
169 		 * header but at a fixed offset. But as the header address was
170 		 * supplied to the driver, we don't know which BAR it was in.
171 		 * So search for the bar whose range includes the header address.
172 		 */
173 		if (intel_pmt_is_early_client_hw(dev)) {
174 			int i;
175 
176 			entry->base_addr = 0;
177 			for (i = 0; i < 6; i++)
178 				if (disc_res->start >= pci_resource_start(pci_dev, i) &&
179 				   (disc_res->start <= pci_resource_end(pci_dev, i))) {
180 					entry->base_addr = pci_resource_start(pci_dev, i) +
181 							   header->base_offset;
182 					break;
183 				}
184 			if (!entry->base_addr)
185 				return -EINVAL;
186 		}
187 
188 		break;
189 	case ACCESS_BARID:
190 		/*
191 		 * If another BAR was specified then the base offset
192 		 * represents the offset within that BAR. SO retrieve the
193 		 * address from the parent PCI device and add offset.
194 		 */
195 		entry->base_addr = pci_resource_start(pci_dev, bir) +
196 				   GET_ADDRESS(header->base_offset);
197 		break;
198 	default:
199 		dev_err(dev, "Unsupported access type %d\n",
200 			header->access_type);
201 		return -EINVAL;
202 	}
203 
204 	entry->guid = header->guid;
205 	entry->size = header->size;
206 
207 	return 0;
208 }
209 
210 static int intel_pmt_dev_register(struct intel_pmt_entry *entry,
211 				  struct intel_pmt_namespace *ns,
212 				  struct device *parent)
213 {
214 	struct resource res = {0};
215 	struct device *dev;
216 	int ret;
217 
218 	ret = xa_alloc(ns->xa, &entry->devid, entry, PMT_XA_LIMIT, GFP_KERNEL);
219 	if (ret)
220 		return ret;
221 
222 	dev = device_create(&intel_pmt_class, parent, MKDEV(0, 0), entry,
223 			    "%s%d", ns->name, entry->devid);
224 
225 	if (IS_ERR(dev)) {
226 		dev_err(parent, "Could not create %s%d device node\n",
227 			ns->name, entry->devid);
228 		ret = PTR_ERR(dev);
229 		goto fail_dev_create;
230 	}
231 
232 	entry->kobj = &dev->kobj;
233 
234 	if (ns->attr_grp) {
235 		ret = sysfs_create_group(entry->kobj, ns->attr_grp);
236 		if (ret)
237 			goto fail_sysfs;
238 	}
239 
240 	/* if size is 0 assume no data buffer, so no file needed */
241 	if (!entry->size)
242 		return 0;
243 
244 	res.start = entry->base_addr;
245 	res.end = res.start + entry->size - 1;
246 	res.flags = IORESOURCE_MEM;
247 
248 	entry->base = devm_ioremap_resource(dev, &res);
249 	if (IS_ERR(entry->base)) {
250 		ret = PTR_ERR(entry->base);
251 		goto fail_ioremap;
252 	}
253 
254 	sysfs_bin_attr_init(&entry->pmt_bin_attr);
255 	entry->pmt_bin_attr.attr.name = ns->name;
256 	entry->pmt_bin_attr.attr.mode = 0440;
257 	entry->pmt_bin_attr.mmap = intel_pmt_mmap;
258 	entry->pmt_bin_attr.read = intel_pmt_read;
259 	entry->pmt_bin_attr.size = entry->size;
260 
261 	ret = sysfs_create_bin_file(&dev->kobj, &entry->pmt_bin_attr);
262 	if (!ret)
263 		return 0;
264 
265 fail_ioremap:
266 	if (ns->attr_grp)
267 		sysfs_remove_group(entry->kobj, ns->attr_grp);
268 fail_sysfs:
269 	device_unregister(dev);
270 fail_dev_create:
271 	xa_erase(ns->xa, entry->devid);
272 
273 	return ret;
274 }
275 
276 int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns,
277 			 struct intel_vsec_device *intel_vsec_dev, int idx)
278 {
279 	struct device *dev = &intel_vsec_dev->auxdev.dev;
280 	struct intel_pmt_header header;
281 	struct resource	*disc_res;
282 	int ret;
283 
284 	disc_res = &intel_vsec_dev->resource[idx];
285 
286 	entry->disc_table = devm_ioremap_resource(dev, disc_res);
287 	if (IS_ERR(entry->disc_table))
288 		return PTR_ERR(entry->disc_table);
289 
290 	ret = ns->pmt_header_decode(entry, &header, dev);
291 	if (ret)
292 		return ret;
293 
294 	ret = intel_pmt_populate_entry(entry, &header, dev, disc_res);
295 	if (ret)
296 		return ret;
297 
298 	return intel_pmt_dev_register(entry, ns, dev);
299 
300 }
301 EXPORT_SYMBOL_GPL(intel_pmt_dev_create);
302 
303 void intel_pmt_dev_destroy(struct intel_pmt_entry *entry,
304 			   struct intel_pmt_namespace *ns)
305 {
306 	struct device *dev = kobj_to_dev(entry->kobj);
307 
308 	if (entry->size)
309 		sysfs_remove_bin_file(entry->kobj, &entry->pmt_bin_attr);
310 
311 	if (ns->attr_grp)
312 		sysfs_remove_group(entry->kobj, ns->attr_grp);
313 
314 	device_unregister(dev);
315 	xa_erase(ns->xa, entry->devid);
316 }
317 EXPORT_SYMBOL_GPL(intel_pmt_dev_destroy);
318 
319 static int __init pmt_class_init(void)
320 {
321 	return class_register(&intel_pmt_class);
322 }
323 
324 static void __exit pmt_class_exit(void)
325 {
326 	class_unregister(&intel_pmt_class);
327 }
328 
329 module_init(pmt_class_init);
330 module_exit(pmt_class_exit);
331 
332 MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>");
333 MODULE_DESCRIPTION("Intel PMT Class driver");
334 MODULE_LICENSE("GPL v2");
335