iomap.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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. static LIST_HEAD(iomap_head);
  24. static 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. void nfit_test_setup(nfit_test_lookup_fn lookup)
  31. {
  32. iomap_ops.nfit_test_lookup = lookup;
  33. list_add_rcu(&iomap_ops.list, &iomap_head);
  34. }
  35. EXPORT_SYMBOL(nfit_test_setup);
  36. void nfit_test_teardown(void)
  37. {
  38. list_del_rcu(&iomap_ops.list);
  39. synchronize_rcu();
  40. }
  41. EXPORT_SYMBOL(nfit_test_teardown);
  42. static struct nfit_test_resource *__get_nfit_res(resource_size_t resource)
  43. {
  44. struct iomap_ops *ops;
  45. ops = list_first_or_null_rcu(&iomap_head, typeof(*ops), list);
  46. if (ops)
  47. return ops->nfit_test_lookup(resource);
  48. return NULL;
  49. }
  50. struct nfit_test_resource *get_nfit_res(resource_size_t resource)
  51. {
  52. struct nfit_test_resource *res;
  53. rcu_read_lock();
  54. res = __get_nfit_res(resource);
  55. rcu_read_unlock();
  56. return res;
  57. }
  58. EXPORT_SYMBOL(get_nfit_res);
  59. void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size,
  60. void __iomem *(*fallback_fn)(resource_size_t, unsigned long))
  61. {
  62. struct nfit_test_resource *nfit_res = get_nfit_res(offset);
  63. if (nfit_res)
  64. return (void __iomem *) nfit_res->buf + offset
  65. - nfit_res->res->start;
  66. return fallback_fn(offset, size);
  67. }
  68. void __iomem *__wrap_devm_ioremap_nocache(struct device *dev,
  69. resource_size_t offset, unsigned long size)
  70. {
  71. struct nfit_test_resource *nfit_res = get_nfit_res(offset);
  72. if (nfit_res)
  73. return (void __iomem *) nfit_res->buf + offset
  74. - nfit_res->res->start;
  75. return devm_ioremap_nocache(dev, offset, size);
  76. }
  77. EXPORT_SYMBOL(__wrap_devm_ioremap_nocache);
  78. void *__wrap_devm_memremap(struct device *dev, resource_size_t offset,
  79. size_t size, unsigned long flags)
  80. {
  81. struct nfit_test_resource *nfit_res = get_nfit_res(offset);
  82. if (nfit_res)
  83. return nfit_res->buf + offset - nfit_res->res->start;
  84. return devm_memremap(dev, offset, size, flags);
  85. }
  86. EXPORT_SYMBOL(__wrap_devm_memremap);
  87. void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res,
  88. struct percpu_ref *ref, struct vmem_altmap *altmap)
  89. {
  90. resource_size_t offset = res->start;
  91. struct nfit_test_resource *nfit_res = get_nfit_res(offset);
  92. if (nfit_res)
  93. return nfit_res->buf + offset - nfit_res->res->start;
  94. return devm_memremap_pages(dev, res, ref, altmap);
  95. }
  96. EXPORT_SYMBOL(__wrap_devm_memremap_pages);
  97. pfn_t __wrap_phys_to_pfn_t(phys_addr_t addr, unsigned long flags)
  98. {
  99. struct nfit_test_resource *nfit_res = get_nfit_res(addr);
  100. if (nfit_res)
  101. flags &= ~PFN_MAP;
  102. return phys_to_pfn_t(addr, flags);
  103. }
  104. EXPORT_SYMBOL(__wrap_phys_to_pfn_t);
  105. void *__wrap_memremap(resource_size_t offset, size_t size,
  106. unsigned long flags)
  107. {
  108. struct nfit_test_resource *nfit_res = get_nfit_res(offset);
  109. if (nfit_res)
  110. return nfit_res->buf + offset - nfit_res->res->start;
  111. return memremap(offset, size, flags);
  112. }
  113. EXPORT_SYMBOL(__wrap_memremap);
  114. void __wrap_devm_memunmap(struct device *dev, void *addr)
  115. {
  116. struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
  117. if (nfit_res)
  118. return;
  119. return devm_memunmap(dev, addr);
  120. }
  121. EXPORT_SYMBOL(__wrap_devm_memunmap);
  122. void __iomem *__wrap_ioremap_nocache(resource_size_t offset, unsigned long size)
  123. {
  124. return __nfit_test_ioremap(offset, size, ioremap_nocache);
  125. }
  126. EXPORT_SYMBOL(__wrap_ioremap_nocache);
  127. void __iomem *__wrap_ioremap_wc(resource_size_t offset, unsigned long size)
  128. {
  129. return __nfit_test_ioremap(offset, size, ioremap_wc);
  130. }
  131. EXPORT_SYMBOL(__wrap_ioremap_wc);
  132. void __wrap_iounmap(volatile void __iomem *addr)
  133. {
  134. struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
  135. if (nfit_res)
  136. return;
  137. return iounmap(addr);
  138. }
  139. EXPORT_SYMBOL(__wrap_iounmap);
  140. void __wrap_memunmap(void *addr)
  141. {
  142. struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
  143. if (nfit_res)
  144. return;
  145. return memunmap(addr);
  146. }
  147. EXPORT_SYMBOL(__wrap_memunmap);
  148. static struct resource *nfit_test_request_region(struct device *dev,
  149. struct resource *parent, resource_size_t start,
  150. resource_size_t n, const char *name, int flags)
  151. {
  152. struct nfit_test_resource *nfit_res;
  153. if (parent == &iomem_resource) {
  154. nfit_res = get_nfit_res(start);
  155. if (nfit_res) {
  156. struct resource *res = nfit_res->res + 1;
  157. if (start + n > nfit_res->res->start
  158. + resource_size(nfit_res->res)) {
  159. pr_debug("%s: start: %llx n: %llx overflow: %pr\n",
  160. __func__, start, n,
  161. nfit_res->res);
  162. return NULL;
  163. }
  164. res->start = start;
  165. res->end = start + n - 1;
  166. res->name = name;
  167. res->flags = resource_type(parent);
  168. res->flags |= IORESOURCE_BUSY | flags;
  169. pr_debug("%s: %pr\n", __func__, res);
  170. return res;
  171. }
  172. }
  173. if (dev)
  174. return __devm_request_region(dev, parent, start, n, name);
  175. return __request_region(parent, start, n, name, flags);
  176. }
  177. struct resource *__wrap___request_region(struct resource *parent,
  178. resource_size_t start, resource_size_t n, const char *name,
  179. int flags)
  180. {
  181. return nfit_test_request_region(NULL, parent, start, n, name, flags);
  182. }
  183. EXPORT_SYMBOL(__wrap___request_region);
  184. int __wrap_insert_resource(struct resource *parent, struct resource *res)
  185. {
  186. if (get_nfit_res(res->start))
  187. return 0;
  188. return insert_resource(parent, res);
  189. }
  190. EXPORT_SYMBOL(__wrap_insert_resource);
  191. int __wrap_remove_resource(struct resource *res)
  192. {
  193. if (get_nfit_res(res->start))
  194. return 0;
  195. return remove_resource(res);
  196. }
  197. EXPORT_SYMBOL(__wrap_remove_resource);
  198. struct resource *__wrap___devm_request_region(struct device *dev,
  199. struct resource *parent, resource_size_t start,
  200. resource_size_t n, const char *name)
  201. {
  202. if (!dev)
  203. return NULL;
  204. return nfit_test_request_region(dev, parent, start, n, name, 0);
  205. }
  206. EXPORT_SYMBOL(__wrap___devm_request_region);
  207. static bool nfit_test_release_region(struct resource *parent,
  208. resource_size_t start, resource_size_t n)
  209. {
  210. if (parent == &iomem_resource) {
  211. struct nfit_test_resource *nfit_res = get_nfit_res(start);
  212. if (nfit_res) {
  213. struct resource *res = nfit_res->res + 1;
  214. if (start != res->start || resource_size(res) != n)
  215. pr_info("%s: start: %llx n: %llx mismatch: %pr\n",
  216. __func__, start, n, res);
  217. else
  218. memset(res, 0, sizeof(*res));
  219. return true;
  220. }
  221. }
  222. return false;
  223. }
  224. void __wrap___release_region(struct resource *parent, resource_size_t start,
  225. resource_size_t n)
  226. {
  227. if (!nfit_test_release_region(parent, start, n))
  228. __release_region(parent, start, n);
  229. }
  230. EXPORT_SYMBOL(__wrap___release_region);
  231. void __wrap___devm_release_region(struct device *dev, struct resource *parent,
  232. resource_size_t start, resource_size_t n)
  233. {
  234. if (!nfit_test_release_region(parent, start, n))
  235. __devm_release_region(dev, parent, start, n);
  236. }
  237. EXPORT_SYMBOL(__wrap___devm_release_region);
  238. MODULE_LICENSE("GPL v2");