xref: /linux/arch/alpha/kernel/pci-sysfs.c (revision 805185b7c7a1069e407b6f7b3bc98e44d415f484)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * arch/alpha/kernel/pci-sysfs.c
4  *
5  * Copyright (C) 2009 Ivan Kokshaysky
6  *
7  * Alpha PCI resource files.
8  *
9  * Loosely based on generic HAVE_PCI_MMAP implementation in
10  * drivers/pci/pci-sysfs.c
11  */
12 
13 #include <linux/sched.h>
14 #include <linux/security.h>
15 #include <linux/pci.h>
16 
17 static int hose_mmap_page_range(struct pci_controller *hose,
18 				struct vm_area_struct *vma,
19 				enum pci_mmap_state mmap_type, int sparse)
20 {
21 	unsigned long base;
22 
23 	if (mmap_type == pci_mmap_mem)
24 		base = sparse ? hose->sparse_mem_base : hose->dense_mem_base;
25 	else
26 		base = sparse ? hose->sparse_io_base : hose->dense_io_base;
27 
28 	vma->vm_pgoff += base >> PAGE_SHIFT;
29 
30 	return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
31 				  vma->vm_end - vma->vm_start,
32 				  vma->vm_page_prot);
33 }
34 
35 static int __pci_mmap_fits(struct pci_dev *pdev, int num,
36 			   struct vm_area_struct *vma, int sparse)
37 {
38 	resource_size_t len = pci_resource_len(pdev, num);
39 	unsigned long nr, start, size;
40 	int shift = sparse ? 5 : 0;
41 
42 	if (!len)
43 		return 0;
44 
45 	nr = vma_pages(vma);
46 	start = vma->vm_pgoff;
47 	size = ((len - 1) >> (PAGE_SHIFT - shift)) + 1;
48 
49 	return start < size && size - start >= nr;
50 }
51 
52 /**
53  * pci_mmap_resource - map a PCI resource into user memory space
54  * @kobj: kobject for mapping
55  * @attr: struct bin_attribute for the file being mapped
56  * @vma: struct vm_area_struct passed into the mmap
57  * @sparse: address space type
58  *
59  * Use the bus mapping routines to map a PCI resource into userspace.
60  *
61  * Return: %0 on success, negative error code otherwise
62  */
63 static int pci_mmap_resource(struct kobject *kobj,
64 			     const struct bin_attribute *attr,
65 			     struct vm_area_struct *vma, int sparse)
66 {
67 	struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
68 	int barno = (unsigned long)attr->private;
69 	enum pci_mmap_state mmap_type;
70 	struct pci_bus_region bar;
71 	int ret;
72 
73 	ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
74 	if (ret)
75 		return ret;
76 
77 	if (pci_resource_is_mem(pdev, barno) &&
78 	    iomem_is_exclusive(pci_resource_start(pdev, barno)))
79 		return -EINVAL;
80 
81 	if (!__pci_mmap_fits(pdev, barno, vma, sparse))
82 		return -EINVAL;
83 
84 	pcibios_resource_to_bus(pdev->bus, &bar, pci_resource_n(pdev, barno));
85 	vma->vm_pgoff += bar.start >> (PAGE_SHIFT - (sparse ? 5 : 0));
86 	mmap_type = pci_resource_is_mem(pdev, barno) ? pci_mmap_mem : pci_mmap_io;
87 
88 	return hose_mmap_page_range(pdev->sysdata, vma, mmap_type, sparse);
89 }
90 
91 static int pci_mmap_resource_sparse(struct file *filp, struct kobject *kobj,
92 				    const struct bin_attribute *attr,
93 				    struct vm_area_struct *vma)
94 {
95 	return pci_mmap_resource(kobj, attr, vma, 1);
96 }
97 
98 static int pci_mmap_resource_dense(struct file *filp, struct kobject *kobj,
99 				   const struct bin_attribute *attr,
100 				   struct vm_area_struct *vma)
101 {
102 	return pci_mmap_resource(kobj, attr, vma, 0);
103 }
104 
105 #define __pci_dev_resource_attr(_bar, _name, _suffix, _mmap)		\
106 static const struct bin_attribute					\
107 pci_dev_resource##_bar##_suffix##_attr = {				\
108 	.attr = { .name = __stringify(_name), .mode = 0600 },		\
109 	.private = (void *)(unsigned long)(_bar),			\
110 	.mmap = (_mmap),						\
111 }
112 
113 #define pci_dev_resource_attr(_bar)					\
114 	__pci_dev_resource_attr(_bar, resource##_bar,,			\
115 			    pci_mmap_resource_dense)
116 
117 #define pci_dev_resource_sparse_attr(_bar)				\
118 	__pci_dev_resource_attr(_bar, resource##_bar##_sparse, _sparse,	\
119 			    pci_mmap_resource_sparse)
120 
121 #define pci_dev_resource_dense_attr(_bar)				\
122 	__pci_dev_resource_attr(_bar, resource##_bar##_dense, _dense,	\
123 			    pci_mmap_resource_dense)
124 
125 static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num)
126 {
127 	struct pci_bus_region bar;
128 	struct pci_controller *hose = pdev->sysdata;
129 	long dense_offset;
130 	unsigned long sparse_size;
131 
132 	pcibios_resource_to_bus(pdev->bus, &bar, pci_resource_n(pdev, num));
133 
134 	/* All core logic chips have 4G sparse address space, except
135 	   CIA which has 16G (see xxx_SPARSE_MEM and xxx_DENSE_MEM
136 	   definitions in asm/core_xxx.h files). This corresponds
137 	   to 128M or 512M of the bus space. */
138 	dense_offset = (long)(hose->dense_mem_base - hose->sparse_mem_base);
139 	sparse_size = dense_offset >= 0x400000000UL ? 0x20000000 : 0x8000000;
140 
141 	return bar.end < sparse_size;
142 }
143 
144 /* Legacy I/O bus mapping stuff. */
145 
146 static int __legacy_mmap_fits(struct vm_area_struct *vma,
147 			      unsigned long res_size)
148 {
149 	unsigned long nr, start, size;
150 
151 	nr = vma_pages(vma);
152 	start = vma->vm_pgoff;
153 	size = ((res_size - 1) >> PAGE_SHIFT) + 1;
154 
155 	return start < size && size - start >= nr;
156 }
157 
158 static inline int has_sparse(struct pci_controller *hose,
159 			     enum pci_mmap_state mmap_type)
160 {
161 	unsigned long base;
162 
163 	base = (mmap_type == pci_mmap_mem) ? hose->sparse_mem_base :
164 					     hose->sparse_io_base;
165 
166 	return base != 0;
167 }
168 
169 int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma,
170 			       enum pci_mmap_state mmap_type)
171 {
172 	struct pci_controller *hose = bus->sysdata;
173 	int sparse = has_sparse(hose, mmap_type);
174 	unsigned long res_size;
175 
176 	res_size = (mmap_type == pci_mmap_mem) ? PCI_LEGACY_MEM_SIZE :
177 						 PCI_LEGACY_IO_SIZE;
178 	if (sparse)
179 		res_size <<= 5;
180 
181 	if (!__legacy_mmap_fits(vma, res_size))
182 		return -EINVAL;
183 
184 	return hose_mmap_page_range(hose, vma, mmap_type, sparse);
185 }
186 
187 bool pci_legacy_has_sparse(struct pci_bus *bus, enum pci_mmap_state type)
188 {
189 	struct pci_controller *hose = bus->sysdata;
190 
191 	return has_sparse(hose, type);
192 }
193 
194 /* Legacy I/O bus read/write functions */
195 int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t size)
196 {
197 	struct pci_controller *hose = bus->sysdata;
198 
199 	port += hose->io_space->start;
200 
201 	switch(size) {
202 	case 1:
203 		*((u8 *)val) = inb(port);
204 		return 1;
205 	case 2:
206 		if (port & 1)
207 			return -EINVAL;
208 		*((u16 *)val) = inw(port);
209 		return 2;
210 	case 4:
211 		if (port & 3)
212 			return -EINVAL;
213 		*((u32 *)val) = inl(port);
214 		return 4;
215 	}
216 	return -EINVAL;
217 }
218 
219 int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size)
220 {
221 	struct pci_controller *hose = bus->sysdata;
222 
223 	port += hose->io_space->start;
224 
225 	switch(size) {
226 	case 1:
227 		outb(port, val);
228 		return 1;
229 	case 2:
230 		if (port & 1)
231 			return -EINVAL;
232 		outw(port, val);
233 		return 2;
234 	case 4:
235 		if (port & 3)
236 			return -EINVAL;
237 		outl(port, val);
238 		return 4;
239 	}
240 	return -EINVAL;
241 }
242 
243 pci_dev_resource_attr(0);
244 pci_dev_resource_attr(1);
245 pci_dev_resource_attr(2);
246 pci_dev_resource_attr(3);
247 pci_dev_resource_attr(4);
248 pci_dev_resource_attr(5);
249 
250 pci_dev_resource_sparse_attr(0);
251 pci_dev_resource_sparse_attr(1);
252 pci_dev_resource_sparse_attr(2);
253 pci_dev_resource_sparse_attr(3);
254 pci_dev_resource_sparse_attr(4);
255 pci_dev_resource_sparse_attr(5);
256 
257 pci_dev_resource_dense_attr(0);
258 pci_dev_resource_dense_attr(1);
259 pci_dev_resource_dense_attr(2);
260 pci_dev_resource_dense_attr(3);
261 pci_dev_resource_dense_attr(4);
262 pci_dev_resource_dense_attr(5);
263 
264 static inline enum pci_mmap_state pci_bar_mmap_type(struct pci_dev *pdev,
265 						    int bar)
266 {
267 	return pci_resource_is_mem(pdev, bar) ? pci_mmap_mem : pci_mmap_io;
268 }
269 
270 static inline umode_t __pci_resource_attr_is_visible(struct kobject *kobj,
271 						     const struct bin_attribute *a,
272 						     int bar)
273 {
274 	struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
275 
276 	if (!pci_resource_len(pdev, bar))
277 		return 0;
278 
279 	return a->attr.mode;
280 }
281 
282 static umode_t pci_dev_resource_is_visible(struct kobject *kobj,
283 					   const struct bin_attribute *a,
284 					   int bar)
285 {
286 	struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
287 	struct pci_controller *hose = pdev->sysdata;
288 
289 	if (has_sparse(hose, pci_bar_mmap_type(pdev, bar)))
290 		return 0;
291 
292 	return __pci_resource_attr_is_visible(kobj, a, bar);
293 }
294 
295 static umode_t pci_dev_resource_sparse_is_visible(struct kobject *kobj,
296 						  const struct bin_attribute *a,
297 						  int bar)
298 {
299 	struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
300 	struct pci_controller *hose = pdev->sysdata;
301 	enum pci_mmap_state type = pci_bar_mmap_type(pdev, bar);
302 
303 	if (!has_sparse(hose, type))
304 		return 0;
305 
306 	if (type == pci_mmap_mem && !sparse_mem_mmap_fits(pdev, bar))
307 		return 0;
308 
309 	return __pci_resource_attr_is_visible(kobj, a, bar);
310 }
311 
312 static umode_t pci_dev_resource_dense_is_visible(struct kobject *kobj,
313 						 const struct bin_attribute *a,
314 						 int bar)
315 {
316 	struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
317 	struct pci_controller *hose = pdev->sysdata;
318 	enum pci_mmap_state type = pci_bar_mmap_type(pdev, bar);
319 	unsigned long dense_base;
320 
321 	if (!has_sparse(hose, type))
322 		return 0;
323 
324 	if (type == pci_mmap_mem && !sparse_mem_mmap_fits(pdev, bar))
325 		return __pci_resource_attr_is_visible(kobj, a, bar);
326 
327 	dense_base = (type == pci_mmap_mem) ? hose->dense_mem_base :
328 					      hose->dense_io_base;
329 	if (!dense_base)
330 		return 0;
331 
332 	return __pci_resource_attr_is_visible(kobj, a, bar);
333 }
334 
335 static inline size_t __pci_dev_resource_bin_size(struct kobject *kobj,
336 						 int bar, bool sparse)
337 {
338 	struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
339 	size_t size = pci_resource_len(pdev, bar);
340 
341 	return sparse ? size << 5 : size;
342 }
343 
344 static size_t pci_dev_resource_bin_size(struct kobject *kobj,
345 					const struct bin_attribute *a,
346 					int bar)
347 {
348 	return __pci_dev_resource_bin_size(kobj, bar, false);
349 }
350 
351 static size_t pci_dev_resource_sparse_bin_size(struct kobject *kobj,
352 					       const struct bin_attribute *a,
353 					       int bar)
354 {
355 	return __pci_dev_resource_bin_size(kobj, bar, true);
356 }
357 
358 static const struct bin_attribute *const pci_dev_resource_attrs[] = {
359 	&pci_dev_resource0_attr,
360 	&pci_dev_resource1_attr,
361 	&pci_dev_resource2_attr,
362 	&pci_dev_resource3_attr,
363 	&pci_dev_resource4_attr,
364 	&pci_dev_resource5_attr,
365 	NULL,
366 };
367 
368 static const struct bin_attribute *const pci_dev_resource_sparse_attrs[] = {
369 	&pci_dev_resource0_sparse_attr,
370 	&pci_dev_resource1_sparse_attr,
371 	&pci_dev_resource2_sparse_attr,
372 	&pci_dev_resource3_sparse_attr,
373 	&pci_dev_resource4_sparse_attr,
374 	&pci_dev_resource5_sparse_attr,
375 	NULL,
376 };
377 
378 static const struct bin_attribute *const pci_dev_resource_dense_attrs[] = {
379 	&pci_dev_resource0_dense_attr,
380 	&pci_dev_resource1_dense_attr,
381 	&pci_dev_resource2_dense_attr,
382 	&pci_dev_resource3_dense_attr,
383 	&pci_dev_resource4_dense_attr,
384 	&pci_dev_resource5_dense_attr,
385 	NULL,
386 };
387 
388 const struct attribute_group pci_dev_resource_attr_group = {
389 	.bin_attrs = pci_dev_resource_attrs,
390 	.is_bin_visible = pci_dev_resource_is_visible,
391 	.bin_size = pci_dev_resource_bin_size,
392 };
393 
394 const struct attribute_group pci_dev_resource_sparse_attr_group = {
395 	.bin_attrs = pci_dev_resource_sparse_attrs,
396 	.is_bin_visible = pci_dev_resource_sparse_is_visible,
397 	.bin_size = pci_dev_resource_sparse_bin_size,
398 };
399 
400 const struct attribute_group pci_dev_resource_dense_attr_group = {
401 	.bin_attrs = pci_dev_resource_dense_attrs,
402 	.is_bin_visible = pci_dev_resource_dense_is_visible,
403 	.bin_size = pci_dev_resource_bin_size,
404 };
405