blob: 804705f0f3a130618956aff77281e1ebce0b2241 [file] [log] [blame]
Dan Williams6bc75612015-06-17 17:23:32 -04001/*
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/rculist.h>
14#include <linux/export.h>
15#include <linux/ioport.h>
16#include <linux/module.h>
17#include <linux/types.h>
18#include <linux/io.h>
Dan Williams9bfa8492015-12-11 11:20:16 -080019#include <linux/mm.h>
Dan Williams6bc75612015-06-17 17:23:32 -040020#include "nfit_test.h"
21
22static LIST_HEAD(iomap_head);
23
24static struct iomap_ops {
25 nfit_test_lookup_fn nfit_test_lookup;
26 struct list_head list;
27} iomap_ops = {
28 .list = LIST_HEAD_INIT(iomap_ops.list),
29};
30
31void nfit_test_setup(nfit_test_lookup_fn lookup)
32{
33 iomap_ops.nfit_test_lookup = lookup;
34 list_add_rcu(&iomap_ops.list, &iomap_head);
35}
36EXPORT_SYMBOL(nfit_test_setup);
37
38void nfit_test_teardown(void)
39{
40 list_del_rcu(&iomap_ops.list);
41 synchronize_rcu();
42}
43EXPORT_SYMBOL(nfit_test_teardown);
44
Dan Williams9bfa8492015-12-11 11:20:16 -080045static struct nfit_test_resource *__get_nfit_res(resource_size_t resource)
Dan Williams6bc75612015-06-17 17:23:32 -040046{
47 struct iomap_ops *ops;
48
49 ops = list_first_or_null_rcu(&iomap_head, typeof(*ops), list);
50 if (ops)
51 return ops->nfit_test_lookup(resource);
52 return NULL;
53}
54
Dan Williamsf295e532016-06-17 11:08:06 -070055struct nfit_test_resource *get_nfit_res(resource_size_t resource)
Dan Williams9bfa8492015-12-11 11:20:16 -080056{
57 struct nfit_test_resource *res;
58
59 rcu_read_lock();
60 res = __get_nfit_res(resource);
61 rcu_read_unlock();
62
63 return res;
64}
Dan Williamsf295e532016-06-17 11:08:06 -070065EXPORT_SYMBOL(get_nfit_res);
Dan Williams9bfa8492015-12-11 11:20:16 -080066
Dan Williams6bc75612015-06-17 17:23:32 -040067void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size,
68 void __iomem *(*fallback_fn)(resource_size_t, unsigned long))
69{
Dan Williams9bfa8492015-12-11 11:20:16 -080070 struct nfit_test_resource *nfit_res = get_nfit_res(offset);
Dan Williams6bc75612015-06-17 17:23:32 -040071
Dan Williams6bc75612015-06-17 17:23:32 -040072 if (nfit_res)
73 return (void __iomem *) nfit_res->buf + offset
74 - nfit_res->res->start;
75 return fallback_fn(offset, size);
76}
77
Dan Williams9d27a872015-07-10 14:07:03 -040078void __iomem *__wrap_devm_ioremap_nocache(struct device *dev,
79 resource_size_t offset, unsigned long size)
80{
Dan Williams9bfa8492015-12-11 11:20:16 -080081 struct nfit_test_resource *nfit_res = get_nfit_res(offset);
Dan Williams9d27a872015-07-10 14:07:03 -040082
Dan Williams9d27a872015-07-10 14:07:03 -040083 if (nfit_res)
84 return (void __iomem *) nfit_res->buf + offset
85 - nfit_res->res->start;
86 return devm_ioremap_nocache(dev, offset, size);
87}
88EXPORT_SYMBOL(__wrap_devm_ioremap_nocache);
89
Christoph Hellwig708ab622015-08-10 23:07:08 -040090void *__wrap_devm_memremap(struct device *dev, resource_size_t offset,
91 size_t size, unsigned long flags)
Dan Williams6bc75612015-06-17 17:23:32 -040092{
Dan Williams9bfa8492015-12-11 11:20:16 -080093 struct nfit_test_resource *nfit_res = get_nfit_res(offset);
Dan Williamse836a252015-08-12 18:42:56 -040094
Dan Williamse836a252015-08-12 18:42:56 -040095 if (nfit_res)
Ross Zwisler67a3e8f2015-08-27 13:14:20 -060096 return nfit_res->buf + offset - nfit_res->res->start;
Christoph Hellwig708ab622015-08-10 23:07:08 -040097 return devm_memremap(dev, offset, size, flags);
Dan Williams6bc75612015-06-17 17:23:32 -040098}
Christoph Hellwig708ab622015-08-10 23:07:08 -040099EXPORT_SYMBOL(__wrap_devm_memremap);
Dan Williams6bc75612015-06-17 17:23:32 -0400100
Dan Williams979fccf2015-12-15 00:34:21 -0800101#ifdef __HAVE_ARCH_PTE_DEVMAP
102#include <linux/memremap.h>
103#include <linux/pfn_t.h>
104
105void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res,
106 struct percpu_ref *ref, struct vmem_altmap *altmap)
107{
108 resource_size_t offset = res->start;
Dan Williams9bfa8492015-12-11 11:20:16 -0800109 struct nfit_test_resource *nfit_res = get_nfit_res(offset);
Dan Williams979fccf2015-12-15 00:34:21 -0800110
Dan Williams979fccf2015-12-15 00:34:21 -0800111 if (nfit_res)
112 return nfit_res->buf + offset - nfit_res->res->start;
113 return devm_memremap_pages(dev, res, ref, altmap);
114}
115EXPORT_SYMBOL(__wrap_devm_memremap_pages);
116
Dan Williams76e9f0e2016-01-22 09:43:28 -0800117pfn_t __wrap_phys_to_pfn_t(phys_addr_t addr, unsigned long flags)
Dan Williams979fccf2015-12-15 00:34:21 -0800118{
Dan Williams9bfa8492015-12-11 11:20:16 -0800119 struct nfit_test_resource *nfit_res = get_nfit_res(addr);
Dan Williams979fccf2015-12-15 00:34:21 -0800120
Dan Williams979fccf2015-12-15 00:34:21 -0800121 if (nfit_res)
122 flags &= ~PFN_MAP;
123 return phys_to_pfn_t(addr, flags);
124}
125EXPORT_SYMBOL(__wrap_phys_to_pfn_t);
126#else
127/* to be removed post 4.5-rc1 */
128void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res)
129{
130 resource_size_t offset = res->start;
Dan Williams9bfa8492015-12-11 11:20:16 -0800131 struct nfit_test_resource *nfit_res = get_nfit_res(offset);
Dan Williams979fccf2015-12-15 00:34:21 -0800132
Dan Williams979fccf2015-12-15 00:34:21 -0800133 if (nfit_res)
134 return nfit_res->buf + offset - nfit_res->res->start;
135 return devm_memremap_pages(dev, res);
136}
137EXPORT_SYMBOL(__wrap_devm_memremap_pages);
138#endif
139
Ross Zwisler67a3e8f2015-08-27 13:14:20 -0600140void *__wrap_memremap(resource_size_t offset, size_t size,
141 unsigned long flags)
142{
Dan Williams9bfa8492015-12-11 11:20:16 -0800143 struct nfit_test_resource *nfit_res = get_nfit_res(offset);
Ross Zwisler67a3e8f2015-08-27 13:14:20 -0600144
Ross Zwisler67a3e8f2015-08-27 13:14:20 -0600145 if (nfit_res)
146 return nfit_res->buf + offset - nfit_res->res->start;
147 return memremap(offset, size, flags);
148}
149EXPORT_SYMBOL(__wrap_memremap);
150
Dan Williams32ab0a3f2015-08-01 02:16:37 -0400151void __wrap_devm_memunmap(struct device *dev, void *addr)
152{
Dan Williams9bfa8492015-12-11 11:20:16 -0800153 struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
Dan Williams32ab0a3f2015-08-01 02:16:37 -0400154
Dan Williams32ab0a3f2015-08-01 02:16:37 -0400155 if (nfit_res)
156 return;
157 return devm_memunmap(dev, addr);
158}
159EXPORT_SYMBOL(__wrap_devm_memunmap);
160
Dan Williams6bc75612015-06-17 17:23:32 -0400161void __iomem *__wrap_ioremap_nocache(resource_size_t offset, unsigned long size)
162{
163 return __nfit_test_ioremap(offset, size, ioremap_nocache);
164}
165EXPORT_SYMBOL(__wrap_ioremap_nocache);
166
Dan Williams9d27a872015-07-10 14:07:03 -0400167void __iomem *__wrap_ioremap_wc(resource_size_t offset, unsigned long size)
168{
169 return __nfit_test_ioremap(offset, size, ioremap_wc);
170}
171EXPORT_SYMBOL(__wrap_ioremap_wc);
172
Dan Williams6bc75612015-06-17 17:23:32 -0400173void __wrap_iounmap(volatile void __iomem *addr)
174{
Dan Williams9bfa8492015-12-11 11:20:16 -0800175 struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
Dan Williams6bc75612015-06-17 17:23:32 -0400176 if (nfit_res)
177 return;
178 return iounmap(addr);
179}
180EXPORT_SYMBOL(__wrap_iounmap);
181
Ross Zwisler67a3e8f2015-08-27 13:14:20 -0600182void __wrap_memunmap(void *addr)
183{
Dan Williams9bfa8492015-12-11 11:20:16 -0800184 struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
Ross Zwisler67a3e8f2015-08-27 13:14:20 -0600185
Ross Zwisler67a3e8f2015-08-27 13:14:20 -0600186 if (nfit_res)
187 return;
188 return memunmap(addr);
189}
190EXPORT_SYMBOL(__wrap_memunmap);
191
Christoph Hellwig708ab622015-08-10 23:07:08 -0400192static struct resource *nfit_test_request_region(struct device *dev,
193 struct resource *parent, resource_size_t start,
194 resource_size_t n, const char *name, int flags)
Dan Williams6bc75612015-06-17 17:23:32 -0400195{
196 struct nfit_test_resource *nfit_res;
197
198 if (parent == &iomem_resource) {
Dan Williams6bc75612015-06-17 17:23:32 -0400199 nfit_res = get_nfit_res(start);
Dan Williams6bc75612015-06-17 17:23:32 -0400200 if (nfit_res) {
201 struct resource *res = nfit_res->res + 1;
202
203 if (start + n > nfit_res->res->start
204 + resource_size(nfit_res->res)) {
205 pr_debug("%s: start: %llx n: %llx overflow: %pr\n",
206 __func__, start, n,
207 nfit_res->res);
208 return NULL;
209 }
210
211 res->start = start;
212 res->end = start + n - 1;
213 res->name = name;
214 res->flags = resource_type(parent);
215 res->flags |= IORESOURCE_BUSY | flags;
216 pr_debug("%s: %pr\n", __func__, res);
217 return res;
218 }
219 }
Christoph Hellwig708ab622015-08-10 23:07:08 -0400220 if (dev)
221 return __devm_request_region(dev, parent, start, n, name);
Dan Williams6bc75612015-06-17 17:23:32 -0400222 return __request_region(parent, start, n, name, flags);
223}
Christoph Hellwig708ab622015-08-10 23:07:08 -0400224
225struct resource *__wrap___request_region(struct resource *parent,
226 resource_size_t start, resource_size_t n, const char *name,
227 int flags)
228{
229 return nfit_test_request_region(NULL, parent, start, n, name, flags);
230}
Dan Williams6bc75612015-06-17 17:23:32 -0400231EXPORT_SYMBOL(__wrap___request_region);
232
Dan Williamsee8520fe2016-06-15 20:34:17 -0700233int __wrap_insert_resource(struct resource *parent, struct resource *res)
234{
235 if (get_nfit_res(res->start))
236 return 0;
237 return insert_resource(parent, res);
238}
239EXPORT_SYMBOL(__wrap_insert_resource);
240
241int __wrap_remove_resource(struct resource *res)
242{
243 if (get_nfit_res(res->start))
244 return 0;
245 return remove_resource(res);
246}
247EXPORT_SYMBOL(__wrap_remove_resource);
248
Christoph Hellwig708ab622015-08-10 23:07:08 -0400249struct resource *__wrap___devm_request_region(struct device *dev,
250 struct resource *parent, resource_size_t start,
251 resource_size_t n, const char *name)
252{
253 if (!dev)
254 return NULL;
255 return nfit_test_request_region(dev, parent, start, n, name, 0);
256}
257EXPORT_SYMBOL(__wrap___devm_request_region);
258
Dan Williams200c79d2016-03-22 00:22:16 -0700259static bool nfit_test_release_region(struct resource *parent,
260 resource_size_t start, resource_size_t n)
Dan Williams6bc75612015-06-17 17:23:32 -0400261{
Dan Williams6bc75612015-06-17 17:23:32 -0400262 if (parent == &iomem_resource) {
Dan Williams200c79d2016-03-22 00:22:16 -0700263 struct nfit_test_resource *nfit_res = get_nfit_res(start);
Dan Williams6bc75612015-06-17 17:23:32 -0400264 if (nfit_res) {
265 struct resource *res = nfit_res->res + 1;
266
267 if (start != res->start || resource_size(res) != n)
268 pr_info("%s: start: %llx n: %llx mismatch: %pr\n",
269 __func__, start, n, res);
270 else
271 memset(res, 0, sizeof(*res));
Dan Williams200c79d2016-03-22 00:22:16 -0700272 return true;
Dan Williams6bc75612015-06-17 17:23:32 -0400273 }
274 }
Dan Williams200c79d2016-03-22 00:22:16 -0700275 return false;
276}
277
278void __wrap___release_region(struct resource *parent, resource_size_t start,
279 resource_size_t n)
280{
281 if (!nfit_test_release_region(parent, start, n))
282 __release_region(parent, start, n);
Dan Williams6bc75612015-06-17 17:23:32 -0400283}
284EXPORT_SYMBOL(__wrap___release_region);
285
Dan Williams200c79d2016-03-22 00:22:16 -0700286void __wrap___devm_release_region(struct device *dev, struct resource *parent,
287 resource_size_t start, resource_size_t n)
288{
289 if (!nfit_test_release_region(parent, start, n))
290 __devm_release_region(dev, parent, start, n);
291}
292EXPORT_SYMBOL(__wrap___devm_release_region);
293
Dan Williams6bc75612015-06-17 17:23:32 -0400294MODULE_LICENSE("GPL v2");