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