xref: /openbmc/linux/tools/testing/nvdimm/test/iomap.c (revision 293d5b43)
1 /*
2  * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of version 2 of the GNU General Public License as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  */
13 #include <linux/memremap.h>
14 #include <linux/rculist.h>
15 #include <linux/export.h>
16 #include <linux/ioport.h>
17 #include <linux/module.h>
18 #include <linux/types.h>
19 #include <linux/pfn_t.h>
20 #include <linux/io.h>
21 #include <linux/mm.h>
22 #include "nfit_test.h"
23 
24 static LIST_HEAD(iomap_head);
25 
26 static struct iomap_ops {
27 	nfit_test_lookup_fn nfit_test_lookup;
28 	struct list_head list;
29 } iomap_ops = {
30 	.list = LIST_HEAD_INIT(iomap_ops.list),
31 };
32 
33 void nfit_test_setup(nfit_test_lookup_fn lookup)
34 {
35 	iomap_ops.nfit_test_lookup = lookup;
36 	list_add_rcu(&iomap_ops.list, &iomap_head);
37 }
38 EXPORT_SYMBOL(nfit_test_setup);
39 
40 void nfit_test_teardown(void)
41 {
42 	list_del_rcu(&iomap_ops.list);
43 	synchronize_rcu();
44 }
45 EXPORT_SYMBOL(nfit_test_teardown);
46 
47 static struct nfit_test_resource *__get_nfit_res(resource_size_t resource)
48 {
49 	struct iomap_ops *ops;
50 
51 	ops = list_first_or_null_rcu(&iomap_head, typeof(*ops), list);
52 	if (ops)
53 		return ops->nfit_test_lookup(resource);
54 	return NULL;
55 }
56 
57 struct nfit_test_resource *get_nfit_res(resource_size_t resource)
58 {
59 	struct nfit_test_resource *res;
60 
61 	rcu_read_lock();
62 	res = __get_nfit_res(resource);
63 	rcu_read_unlock();
64 
65 	return res;
66 }
67 EXPORT_SYMBOL(get_nfit_res);
68 
69 void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size,
70 		void __iomem *(*fallback_fn)(resource_size_t, unsigned long))
71 {
72 	struct nfit_test_resource *nfit_res = get_nfit_res(offset);
73 
74 	if (nfit_res)
75 		return (void __iomem *) nfit_res->buf + offset
76 			- nfit_res->res->start;
77 	return fallback_fn(offset, size);
78 }
79 
80 void __iomem *__wrap_devm_ioremap_nocache(struct device *dev,
81 		resource_size_t offset, unsigned long size)
82 {
83 	struct nfit_test_resource *nfit_res = get_nfit_res(offset);
84 
85 	if (nfit_res)
86 		return (void __iomem *) nfit_res->buf + offset
87 			- nfit_res->res->start;
88 	return devm_ioremap_nocache(dev, offset, size);
89 }
90 EXPORT_SYMBOL(__wrap_devm_ioremap_nocache);
91 
92 void *__wrap_devm_memremap(struct device *dev, resource_size_t offset,
93 		size_t size, unsigned long flags)
94 {
95 	struct nfit_test_resource *nfit_res = get_nfit_res(offset);
96 
97 	if (nfit_res)
98 		return nfit_res->buf + offset - nfit_res->res->start;
99 	return devm_memremap(dev, offset, size, flags);
100 }
101 EXPORT_SYMBOL(__wrap_devm_memremap);
102 
103 void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res,
104 		struct percpu_ref *ref, struct vmem_altmap *altmap)
105 {
106 	resource_size_t offset = res->start;
107 	struct nfit_test_resource *nfit_res = get_nfit_res(offset);
108 
109 	if (nfit_res)
110 		return nfit_res->buf + offset - nfit_res->res->start;
111 	return devm_memremap_pages(dev, res, ref, altmap);
112 }
113 EXPORT_SYMBOL(__wrap_devm_memremap_pages);
114 
115 pfn_t __wrap_phys_to_pfn_t(phys_addr_t addr, unsigned long flags)
116 {
117 	struct nfit_test_resource *nfit_res = get_nfit_res(addr);
118 
119 	if (nfit_res)
120 		flags &= ~PFN_MAP;
121         return phys_to_pfn_t(addr, flags);
122 }
123 EXPORT_SYMBOL(__wrap_phys_to_pfn_t);
124 
125 void *__wrap_memremap(resource_size_t offset, size_t size,
126 		unsigned long flags)
127 {
128 	struct nfit_test_resource *nfit_res = get_nfit_res(offset);
129 
130 	if (nfit_res)
131 		return nfit_res->buf + offset - nfit_res->res->start;
132 	return memremap(offset, size, flags);
133 }
134 EXPORT_SYMBOL(__wrap_memremap);
135 
136 void __wrap_devm_memunmap(struct device *dev, void *addr)
137 {
138 	struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
139 
140 	if (nfit_res)
141 		return;
142 	return devm_memunmap(dev, addr);
143 }
144 EXPORT_SYMBOL(__wrap_devm_memunmap);
145 
146 void __iomem *__wrap_ioremap_nocache(resource_size_t offset, unsigned long size)
147 {
148 	return __nfit_test_ioremap(offset, size, ioremap_nocache);
149 }
150 EXPORT_SYMBOL(__wrap_ioremap_nocache);
151 
152 void __iomem *__wrap_ioremap_wc(resource_size_t offset, unsigned long size)
153 {
154 	return __nfit_test_ioremap(offset, size, ioremap_wc);
155 }
156 EXPORT_SYMBOL(__wrap_ioremap_wc);
157 
158 void __wrap_iounmap(volatile void __iomem *addr)
159 {
160 	struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
161 	if (nfit_res)
162 		return;
163 	return iounmap(addr);
164 }
165 EXPORT_SYMBOL(__wrap_iounmap);
166 
167 void __wrap_memunmap(void *addr)
168 {
169 	struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
170 
171 	if (nfit_res)
172 		return;
173 	return memunmap(addr);
174 }
175 EXPORT_SYMBOL(__wrap_memunmap);
176 
177 static struct resource *nfit_test_request_region(struct device *dev,
178 		struct resource *parent, resource_size_t start,
179 		resource_size_t n, const char *name, int flags)
180 {
181 	struct nfit_test_resource *nfit_res;
182 
183 	if (parent == &iomem_resource) {
184 		nfit_res = get_nfit_res(start);
185 		if (nfit_res) {
186 			struct resource *res = nfit_res->res + 1;
187 
188 			if (start + n > nfit_res->res->start
189 					+ resource_size(nfit_res->res)) {
190 				pr_debug("%s: start: %llx n: %llx overflow: %pr\n",
191 						__func__, start, n,
192 						nfit_res->res);
193 				return NULL;
194 			}
195 
196 			res->start = start;
197 			res->end = start + n - 1;
198 			res->name = name;
199 			res->flags = resource_type(parent);
200 			res->flags |= IORESOURCE_BUSY | flags;
201 			pr_debug("%s: %pr\n", __func__, res);
202 			return res;
203 		}
204 	}
205 	if (dev)
206 		return __devm_request_region(dev, parent, start, n, name);
207 	return __request_region(parent, start, n, name, flags);
208 }
209 
210 struct resource *__wrap___request_region(struct resource *parent,
211 		resource_size_t start, resource_size_t n, const char *name,
212 		int flags)
213 {
214 	return nfit_test_request_region(NULL, parent, start, n, name, flags);
215 }
216 EXPORT_SYMBOL(__wrap___request_region);
217 
218 int __wrap_insert_resource(struct resource *parent, struct resource *res)
219 {
220 	if (get_nfit_res(res->start))
221 		return 0;
222 	return insert_resource(parent, res);
223 }
224 EXPORT_SYMBOL(__wrap_insert_resource);
225 
226 int __wrap_remove_resource(struct resource *res)
227 {
228 	if (get_nfit_res(res->start))
229 		return 0;
230 	return remove_resource(res);
231 }
232 EXPORT_SYMBOL(__wrap_remove_resource);
233 
234 struct resource *__wrap___devm_request_region(struct device *dev,
235 		struct resource *parent, resource_size_t start,
236 		resource_size_t n, const char *name)
237 {
238 	if (!dev)
239 		return NULL;
240 	return nfit_test_request_region(dev, parent, start, n, name, 0);
241 }
242 EXPORT_SYMBOL(__wrap___devm_request_region);
243 
244 static bool nfit_test_release_region(struct resource *parent,
245 		resource_size_t start, resource_size_t n)
246 {
247 	if (parent == &iomem_resource) {
248 		struct nfit_test_resource *nfit_res = get_nfit_res(start);
249 		if (nfit_res) {
250 			struct resource *res = nfit_res->res + 1;
251 
252 			if (start != res->start || resource_size(res) != n)
253 				pr_info("%s: start: %llx n: %llx mismatch: %pr\n",
254 						__func__, start, n, res);
255 			else
256 				memset(res, 0, sizeof(*res));
257 			return true;
258 		}
259 	}
260 	return false;
261 }
262 
263 void __wrap___release_region(struct resource *parent, resource_size_t start,
264 		resource_size_t n)
265 {
266 	if (!nfit_test_release_region(parent, start, n))
267 		__release_region(parent, start, n);
268 }
269 EXPORT_SYMBOL(__wrap___release_region);
270 
271 void __wrap___devm_release_region(struct device *dev, struct resource *parent,
272 		resource_size_t start, resource_size_t n)
273 {
274 	if (!nfit_test_release_region(parent, start, n))
275 		__devm_release_region(dev, parent, start, n);
276 }
277 EXPORT_SYMBOL(__wrap___devm_release_region);
278 
279 MODULE_LICENSE("GPL v2");
280