iomap.c 6.0 KB

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