linux/tools/testing/nvdimm/test/iomap.c
<<
>>
Prefs
   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/acpi.h>
  21#include <linux/io.h>
  22#include <linux/mm.h>
  23#include "nfit_test.h"
  24
  25static LIST_HEAD(iomap_head);
  26
  27static struct iomap_ops {
  28        nfit_test_lookup_fn nfit_test_lookup;
  29        nfit_test_evaluate_dsm_fn evaluate_dsm;
  30        struct list_head list;
  31} iomap_ops = {
  32        .list = LIST_HEAD_INIT(iomap_ops.list),
  33};
  34
  35void nfit_test_setup(nfit_test_lookup_fn lookup,
  36                nfit_test_evaluate_dsm_fn evaluate)
  37{
  38        iomap_ops.nfit_test_lookup = lookup;
  39        iomap_ops.evaluate_dsm = evaluate;
  40        list_add_rcu(&iomap_ops.list, &iomap_head);
  41}
  42EXPORT_SYMBOL(nfit_test_setup);
  43
  44void nfit_test_teardown(void)
  45{
  46        list_del_rcu(&iomap_ops.list);
  47        synchronize_rcu();
  48}
  49EXPORT_SYMBOL(nfit_test_teardown);
  50
  51static struct nfit_test_resource *__get_nfit_res(resource_size_t resource)
  52{
  53        struct iomap_ops *ops;
  54
  55        ops = list_first_or_null_rcu(&iomap_head, typeof(*ops), list);
  56        if (ops)
  57                return ops->nfit_test_lookup(resource);
  58        return NULL;
  59}
  60
  61struct nfit_test_resource *get_nfit_res(resource_size_t resource)
  62{
  63        struct nfit_test_resource *res;
  64
  65        rcu_read_lock();
  66        res = __get_nfit_res(resource);
  67        rcu_read_unlock();
  68
  69        return res;
  70}
  71EXPORT_SYMBOL(get_nfit_res);
  72
  73void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size,
  74                void __iomem *(*fallback_fn)(resource_size_t, unsigned long))
  75{
  76        struct nfit_test_resource *nfit_res = get_nfit_res(offset);
  77
  78        if (nfit_res)
  79                return (void __iomem *) nfit_res->buf + offset
  80                        - nfit_res->res.start;
  81        return fallback_fn(offset, size);
  82}
  83
  84void __iomem *__wrap_devm_ioremap_nocache(struct device *dev,
  85                resource_size_t offset, unsigned long size)
  86{
  87        struct nfit_test_resource *nfit_res = get_nfit_res(offset);
  88
  89        if (nfit_res)
  90                return (void __iomem *) nfit_res->buf + offset
  91                        - nfit_res->res.start;
  92        return devm_ioremap_nocache(dev, offset, size);
  93}
  94EXPORT_SYMBOL(__wrap_devm_ioremap_nocache);
  95
  96void *__wrap_devm_memremap(struct device *dev, resource_size_t offset,
  97                size_t size, unsigned long flags)
  98{
  99        struct nfit_test_resource *nfit_res = get_nfit_res(offset);
 100
 101        if (nfit_res)
 102                return nfit_res->buf + offset - nfit_res->res.start;
 103        return devm_memremap(dev, offset, size, flags);
 104}
 105EXPORT_SYMBOL(__wrap_devm_memremap);
 106
 107void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res,
 108                struct percpu_ref *ref, struct vmem_altmap *altmap)
 109{
 110        resource_size_t offset = res->start;
 111        struct nfit_test_resource *nfit_res = get_nfit_res(offset);
 112
 113        if (nfit_res)
 114                return nfit_res->buf + offset - nfit_res->res.start;
 115        return devm_memremap_pages(dev, res, ref, altmap);
 116}
 117EXPORT_SYMBOL(__wrap_devm_memremap_pages);
 118
 119pfn_t __wrap_phys_to_pfn_t(phys_addr_t addr, unsigned long flags)
 120{
 121        struct nfit_test_resource *nfit_res = get_nfit_res(addr);
 122
 123        if (nfit_res)
 124                flags &= ~PFN_MAP;
 125        return phys_to_pfn_t(addr, flags);
 126}
 127EXPORT_SYMBOL(__wrap_phys_to_pfn_t);
 128
 129void *__wrap_memremap(resource_size_t offset, size_t size,
 130                unsigned long flags)
 131{
 132        struct nfit_test_resource *nfit_res = get_nfit_res(offset);
 133
 134        if (nfit_res)
 135                return nfit_res->buf + offset - nfit_res->res.start;
 136        return memremap(offset, size, flags);
 137}
 138EXPORT_SYMBOL(__wrap_memremap);
 139
 140void __wrap_devm_memunmap(struct device *dev, void *addr)
 141{
 142        struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
 143
 144        if (nfit_res)
 145                return;
 146        return devm_memunmap(dev, addr);
 147}
 148EXPORT_SYMBOL(__wrap_devm_memunmap);
 149
 150void __iomem *__wrap_ioremap_nocache(resource_size_t offset, unsigned long size)
 151{
 152        return __nfit_test_ioremap(offset, size, ioremap_nocache);
 153}
 154EXPORT_SYMBOL(__wrap_ioremap_nocache);
 155
 156void __iomem *__wrap_ioremap_wc(resource_size_t offset, unsigned long size)
 157{
 158        return __nfit_test_ioremap(offset, size, ioremap_wc);
 159}
 160EXPORT_SYMBOL(__wrap_ioremap_wc);
 161
 162void __wrap_iounmap(volatile void __iomem *addr)
 163{
 164        struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
 165        if (nfit_res)
 166                return;
 167        return iounmap(addr);
 168}
 169EXPORT_SYMBOL(__wrap_iounmap);
 170
 171void __wrap_memunmap(void *addr)
 172{
 173        struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
 174
 175        if (nfit_res)
 176                return;
 177        return memunmap(addr);
 178}
 179EXPORT_SYMBOL(__wrap_memunmap);
 180
 181static bool nfit_test_release_region(struct device *dev,
 182                struct resource *parent, resource_size_t start,
 183                resource_size_t n);
 184
 185static void nfit_devres_release(struct device *dev, void *data)
 186{
 187        struct resource *res = *((struct resource **) data);
 188
 189        WARN_ON(!nfit_test_release_region(NULL, &iomem_resource, res->start,
 190                        resource_size(res)));
 191}
 192
 193static int match(struct device *dev, void *__res, void *match_data)
 194{
 195        struct resource *res = *((struct resource **) __res);
 196        resource_size_t start = *((resource_size_t *) match_data);
 197
 198        return res->start == start;
 199}
 200
 201static bool nfit_test_release_region(struct device *dev,
 202                struct resource *parent, resource_size_t start,
 203                resource_size_t n)
 204{
 205        if (parent == &iomem_resource) {
 206                struct nfit_test_resource *nfit_res = get_nfit_res(start);
 207
 208                if (nfit_res) {
 209                        struct nfit_test_request *req;
 210                        struct resource *res = NULL;
 211
 212                        if (dev) {
 213                                devres_release(dev, nfit_devres_release, match,
 214                                                &start);
 215                                return true;
 216                        }
 217
 218                        spin_lock(&nfit_res->lock);
 219                        list_for_each_entry(req, &nfit_res->requests, list)
 220                                if (req->res.start == start) {
 221                                        res = &req->res;
 222                                        list_del(&req->list);
 223                                        break;
 224                                }
 225                        spin_unlock(&nfit_res->lock);
 226
 227                        WARN(!res || resource_size(res) != n,
 228                                        "%s: start: %llx n: %llx mismatch: %pr\n",
 229                                                __func__, start, n, res);
 230                        if (res)
 231                                kfree(req);
 232                        return true;
 233                }
 234        }
 235        return false;
 236}
 237
 238static struct resource *nfit_test_request_region(struct device *dev,
 239                struct resource *parent, resource_size_t start,
 240                resource_size_t n, const char *name, int flags)
 241{
 242        struct nfit_test_resource *nfit_res;
 243
 244        if (parent == &iomem_resource) {
 245                nfit_res = get_nfit_res(start);
 246                if (nfit_res) {
 247                        struct nfit_test_request *req;
 248                        struct resource *res = NULL;
 249
 250                        if (start + n > nfit_res->res.start
 251                                        + resource_size(&nfit_res->res)) {
 252                                pr_debug("%s: start: %llx n: %llx overflow: %pr\n",
 253                                                __func__, start, n,
 254                                                &nfit_res->res);
 255                                return NULL;
 256                        }
 257
 258                        spin_lock(&nfit_res->lock);
 259                        list_for_each_entry(req, &nfit_res->requests, list)
 260                                if (start == req->res.start) {
 261                                        res = &req->res;
 262                                        break;
 263                                }
 264                        spin_unlock(&nfit_res->lock);
 265
 266                        if (res) {
 267                                WARN(1, "%pr already busy\n", res);
 268                                return NULL;
 269                        }
 270
 271                        req = kzalloc(sizeof(*req), GFP_KERNEL);
 272                        if (!req)
 273                                return NULL;
 274                        INIT_LIST_HEAD(&req->list);
 275                        res = &req->res;
 276
 277                        res->start = start;
 278                        res->end = start + n - 1;
 279                        res->name = name;
 280                        res->flags = resource_type(parent);
 281                        res->flags |= IORESOURCE_BUSY | flags;
 282                        spin_lock(&nfit_res->lock);
 283                        list_add(&req->list, &nfit_res->requests);
 284                        spin_unlock(&nfit_res->lock);
 285
 286                        if (dev) {
 287                                struct resource **d;
 288
 289                                d = devres_alloc(nfit_devres_release,
 290                                                sizeof(struct resource *),
 291                                                GFP_KERNEL);
 292                                if (!d)
 293                                        return NULL;
 294                                *d = res;
 295                                devres_add(dev, d);
 296                        }
 297
 298                        pr_debug("%s: %pr\n", __func__, res);
 299                        return res;
 300                }
 301        }
 302        if (dev)
 303                return __devm_request_region(dev, parent, start, n, name);
 304        return __request_region(parent, start, n, name, flags);
 305}
 306
 307struct resource *__wrap___request_region(struct resource *parent,
 308                resource_size_t start, resource_size_t n, const char *name,
 309                int flags)
 310{
 311        return nfit_test_request_region(NULL, parent, start, n, name, flags);
 312}
 313EXPORT_SYMBOL(__wrap___request_region);
 314
 315int __wrap_insert_resource(struct resource *parent, struct resource *res)
 316{
 317        if (get_nfit_res(res->start))
 318                return 0;
 319        return insert_resource(parent, res);
 320}
 321EXPORT_SYMBOL(__wrap_insert_resource);
 322
 323int __wrap_remove_resource(struct resource *res)
 324{
 325        if (get_nfit_res(res->start))
 326                return 0;
 327        return remove_resource(res);
 328}
 329EXPORT_SYMBOL(__wrap_remove_resource);
 330
 331struct resource *__wrap___devm_request_region(struct device *dev,
 332                struct resource *parent, resource_size_t start,
 333                resource_size_t n, const char *name)
 334{
 335        if (!dev)
 336                return NULL;
 337        return nfit_test_request_region(dev, parent, start, n, name, 0);
 338}
 339EXPORT_SYMBOL(__wrap___devm_request_region);
 340
 341void __wrap___release_region(struct resource *parent, resource_size_t start,
 342                resource_size_t n)
 343{
 344        if (!nfit_test_release_region(NULL, parent, start, n))
 345                __release_region(parent, start, n);
 346}
 347EXPORT_SYMBOL(__wrap___release_region);
 348
 349void __wrap___devm_release_region(struct device *dev, struct resource *parent,
 350                resource_size_t start, resource_size_t n)
 351{
 352        if (!nfit_test_release_region(dev, parent, start, n))
 353                __devm_release_region(dev, parent, start, n);
 354}
 355EXPORT_SYMBOL(__wrap___devm_release_region);
 356
 357acpi_status __wrap_acpi_evaluate_object(acpi_handle handle, acpi_string path,
 358                struct acpi_object_list *p, struct acpi_buffer *buf)
 359{
 360        struct nfit_test_resource *nfit_res = get_nfit_res((long) handle);
 361        union acpi_object **obj;
 362
 363        if (!nfit_res || strcmp(path, "_FIT") || !buf)
 364                return acpi_evaluate_object(handle, path, p, buf);
 365
 366        obj = nfit_res->buf;
 367        buf->length = sizeof(union acpi_object);
 368        buf->pointer = *obj;
 369        return AE_OK;
 370}
 371EXPORT_SYMBOL(__wrap_acpi_evaluate_object);
 372
 373union acpi_object * __wrap_acpi_evaluate_dsm(acpi_handle handle, const guid_t *guid,
 374                u64 rev, u64 func, union acpi_object *argv4)
 375{
 376        union acpi_object *obj = ERR_PTR(-ENXIO);
 377        struct iomap_ops *ops;
 378
 379        rcu_read_lock();
 380        ops = list_first_or_null_rcu(&iomap_head, typeof(*ops), list);
 381        if (ops)
 382                obj = ops->evaluate_dsm(handle, guid, rev, func, argv4);
 383        rcu_read_unlock();
 384
 385        if (IS_ERR(obj))
 386                return acpi_evaluate_dsm(handle, guid, rev, func, argv4);
 387        return obj;
 388}
 389EXPORT_SYMBOL(__wrap_acpi_evaluate_dsm);
 390
 391MODULE_LICENSE("GPL v2");
 392