iomap.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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/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>
  19. #include <linux/mm.h>
  20. #include "nfit_test.h"
  21. static LIST_HEAD(iomap_head);
  22. static struct iomap_ops {
  23. nfit_test_lookup_fn nfit_test_lookup;
  24. struct list_head list;
  25. } iomap_ops = {
  26. .list = LIST_HEAD_INIT(iomap_ops.list),
  27. };
  28. void nfit_test_setup(nfit_test_lookup_fn lookup)
  29. {
  30. iomap_ops.nfit_test_lookup = lookup;
  31. list_add_rcu(&iomap_ops.list, &iomap_head);
  32. }
  33. EXPORT_SYMBOL(nfit_test_setup);
  34. void nfit_test_teardown(void)
  35. {
  36. list_del_rcu(&iomap_ops.list);
  37. synchronize_rcu();
  38. }
  39. EXPORT_SYMBOL(nfit_test_teardown);
  40. static struct nfit_test_resource *__get_nfit_res(resource_size_t resource)
  41. {
  42. struct iomap_ops *ops;
  43. ops = list_first_or_null_rcu(&iomap_head, typeof(*ops), list);
  44. if (ops)
  45. return ops->nfit_test_lookup(resource);
  46. return NULL;
  47. }
  48. static struct nfit_test_resource *get_nfit_res(resource_size_t resource)
  49. {
  50. struct nfit_test_resource *res;
  51. rcu_read_lock();
  52. res = __get_nfit_res(resource);
  53. rcu_read_unlock();
  54. return res;
  55. }
  56. void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size,
  57. void __iomem *(*fallback_fn)(resource_size_t, unsigned long))
  58. {
  59. struct nfit_test_resource *nfit_res = get_nfit_res(offset);
  60. if (nfit_res)
  61. return (void __iomem *) nfit_res->buf + offset
  62. - nfit_res->res->start;
  63. return fallback_fn(offset, size);
  64. }
  65. void __iomem *__wrap_devm_ioremap_nocache(struct device *dev,
  66. resource_size_t offset, unsigned long size)
  67. {
  68. struct nfit_test_resource *nfit_res = get_nfit_res(offset);
  69. if (nfit_res)
  70. return (void __iomem *) nfit_res->buf + offset
  71. - nfit_res->res->start;
  72. return devm_ioremap_nocache(dev, offset, size);
  73. }
  74. EXPORT_SYMBOL(__wrap_devm_ioremap_nocache);
  75. void *__wrap_devm_memremap(struct device *dev, resource_size_t offset,
  76. size_t size, unsigned long flags)
  77. {
  78. struct nfit_test_resource *nfit_res = get_nfit_res(offset);
  79. if (nfit_res)
  80. return nfit_res->buf + offset - nfit_res->res->start;
  81. return devm_memremap(dev, offset, size, flags);
  82. }
  83. EXPORT_SYMBOL(__wrap_devm_memremap);
  84. #ifdef __HAVE_ARCH_PTE_DEVMAP
  85. #include <linux/memremap.h>
  86. #include <linux/pfn_t.h>
  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. #else
  106. /* to be removed post 4.5-rc1 */
  107. void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res)
  108. {
  109. resource_size_t offset = res->start;
  110. struct nfit_test_resource *nfit_res = get_nfit_res(offset);
  111. if (nfit_res)
  112. return nfit_res->buf + offset - nfit_res->res->start;
  113. return devm_memremap_pages(dev, res);
  114. }
  115. EXPORT_SYMBOL(__wrap_devm_memremap_pages);
  116. #endif
  117. void *__wrap_memremap(resource_size_t offset, size_t size,
  118. unsigned long flags)
  119. {
  120. struct nfit_test_resource *nfit_res = get_nfit_res(offset);
  121. if (nfit_res)
  122. return nfit_res->buf + offset - nfit_res->res->start;
  123. return memremap(offset, size, flags);
  124. }
  125. EXPORT_SYMBOL(__wrap_memremap);
  126. void __wrap_devm_memunmap(struct device *dev, void *addr)
  127. {
  128. struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
  129. if (nfit_res)
  130. return;
  131. return devm_memunmap(dev, addr);
  132. }
  133. EXPORT_SYMBOL(__wrap_devm_memunmap);
  134. void __iomem *__wrap_ioremap_nocache(resource_size_t offset, unsigned long size)
  135. {
  136. return __nfit_test_ioremap(offset, size, ioremap_nocache);
  137. }
  138. EXPORT_SYMBOL(__wrap_ioremap_nocache);
  139. void __iomem *__wrap_ioremap_wc(resource_size_t offset, unsigned long size)
  140. {
  141. return __nfit_test_ioremap(offset, size, ioremap_wc);
  142. }
  143. EXPORT_SYMBOL(__wrap_ioremap_wc);
  144. void __wrap_iounmap(volatile void __iomem *addr)
  145. {
  146. struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
  147. if (nfit_res)
  148. return;
  149. return iounmap(addr);
  150. }
  151. EXPORT_SYMBOL(__wrap_iounmap);
  152. void __wrap_memunmap(void *addr)
  153. {
  154. struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
  155. if (nfit_res)
  156. return;
  157. return memunmap(addr);
  158. }
  159. EXPORT_SYMBOL(__wrap_memunmap);
  160. static struct resource *nfit_test_request_region(struct device *dev,
  161. struct resource *parent, resource_size_t start,
  162. resource_size_t n, const char *name, int flags)
  163. {
  164. struct nfit_test_resource *nfit_res;
  165. if (parent == &iomem_resource) {
  166. nfit_res = get_nfit_res(start);
  167. if (nfit_res) {
  168. struct resource *res = nfit_res->res + 1;
  169. if (start + n > nfit_res->res->start
  170. + resource_size(nfit_res->res)) {
  171. pr_debug("%s: start: %llx n: %llx overflow: %pr\n",
  172. __func__, start, n,
  173. nfit_res->res);
  174. return NULL;
  175. }
  176. res->start = start;
  177. res->end = start + n - 1;
  178. res->name = name;
  179. res->flags = resource_type(parent);
  180. res->flags |= IORESOURCE_BUSY | flags;
  181. pr_debug("%s: %pr\n", __func__, res);
  182. return res;
  183. }
  184. }
  185. if (dev)
  186. return __devm_request_region(dev, parent, start, n, name);
  187. return __request_region(parent, start, n, name, flags);
  188. }
  189. struct resource *__wrap___request_region(struct resource *parent,
  190. resource_size_t start, resource_size_t n, const char *name,
  191. int flags)
  192. {
  193. return nfit_test_request_region(NULL, parent, start, n, name, flags);
  194. }
  195. EXPORT_SYMBOL(__wrap___request_region);
  196. struct resource *__wrap___devm_request_region(struct device *dev,
  197. struct resource *parent, resource_size_t start,
  198. resource_size_t n, const char *name)
  199. {
  200. if (!dev)
  201. return NULL;
  202. return nfit_test_request_region(dev, parent, start, n, name, 0);
  203. }
  204. EXPORT_SYMBOL(__wrap___devm_request_region);
  205. void __wrap___release_region(struct resource *parent, resource_size_t start,
  206. resource_size_t n)
  207. {
  208. struct nfit_test_resource *nfit_res;
  209. if (parent == &iomem_resource) {
  210. nfit_res = get_nfit_res(start);
  211. if (nfit_res) {
  212. struct resource *res = nfit_res->res + 1;
  213. if (start != res->start || resource_size(res) != n)
  214. pr_info("%s: start: %llx n: %llx mismatch: %pr\n",
  215. __func__, start, n, res);
  216. else
  217. memset(res, 0, sizeof(*res));
  218. return;
  219. }
  220. }
  221. __release_region(parent, start, n);
  222. }
  223. EXPORT_SYMBOL(__wrap___release_region);
  224. MODULE_LICENSE("GPL v2");