cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fpga-region.c (7696B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * FPGA Region - Support for FPGA programming under Linux
      4 *
      5 *  Copyright (C) 2013-2016 Altera Corporation
      6 *  Copyright (C) 2017 Intel Corporation
      7 */
      8#include <linux/fpga/fpga-bridge.h>
      9#include <linux/fpga/fpga-mgr.h>
     10#include <linux/fpga/fpga-region.h>
     11#include <linux/idr.h>
     12#include <linux/kernel.h>
     13#include <linux/list.h>
     14#include <linux/module.h>
     15#include <linux/slab.h>
     16#include <linux/spinlock.h>
     17
     18static DEFINE_IDA(fpga_region_ida);
     19static struct class *fpga_region_class;
     20
     21struct fpga_region *
     22fpga_region_class_find(struct device *start, const void *data,
     23		       int (*match)(struct device *, const void *))
     24{
     25	struct device *dev;
     26
     27	dev = class_find_device(fpga_region_class, start, data, match);
     28	if (!dev)
     29		return NULL;
     30
     31	return to_fpga_region(dev);
     32}
     33EXPORT_SYMBOL_GPL(fpga_region_class_find);
     34
     35/**
     36 * fpga_region_get - get an exclusive reference to an fpga region
     37 * @region: FPGA Region struct
     38 *
     39 * Caller should call fpga_region_put() when done with region.
     40 *
     41 * Return fpga_region struct if successful.
     42 * Return -EBUSY if someone already has a reference to the region.
     43 * Return -ENODEV if @np is not an FPGA Region.
     44 */
     45static struct fpga_region *fpga_region_get(struct fpga_region *region)
     46{
     47	struct device *dev = &region->dev;
     48
     49	if (!mutex_trylock(&region->mutex)) {
     50		dev_dbg(dev, "%s: FPGA Region already in use\n", __func__);
     51		return ERR_PTR(-EBUSY);
     52	}
     53
     54	get_device(dev);
     55	if (!try_module_get(dev->parent->driver->owner)) {
     56		put_device(dev);
     57		mutex_unlock(&region->mutex);
     58		return ERR_PTR(-ENODEV);
     59	}
     60
     61	dev_dbg(dev, "get\n");
     62
     63	return region;
     64}
     65
     66/**
     67 * fpga_region_put - release a reference to a region
     68 *
     69 * @region: FPGA region
     70 */
     71static void fpga_region_put(struct fpga_region *region)
     72{
     73	struct device *dev = &region->dev;
     74
     75	dev_dbg(dev, "put\n");
     76
     77	module_put(dev->parent->driver->owner);
     78	put_device(dev);
     79	mutex_unlock(&region->mutex);
     80}
     81
     82/**
     83 * fpga_region_program_fpga - program FPGA
     84 *
     85 * @region: FPGA region
     86 *
     87 * Program an FPGA using fpga image info (region->info).
     88 * If the region has a get_bridges function, the exclusive reference for the
     89 * bridges will be held if programming succeeds.  This is intended to prevent
     90 * reprogramming the region until the caller considers it safe to do so.
     91 * The caller will need to call fpga_bridges_put() before attempting to
     92 * reprogram the region.
     93 *
     94 * Return 0 for success or negative error code.
     95 */
     96int fpga_region_program_fpga(struct fpga_region *region)
     97{
     98	struct device *dev = &region->dev;
     99	struct fpga_image_info *info = region->info;
    100	int ret;
    101
    102	region = fpga_region_get(region);
    103	if (IS_ERR(region)) {
    104		dev_err(dev, "failed to get FPGA region\n");
    105		return PTR_ERR(region);
    106	}
    107
    108	ret = fpga_mgr_lock(region->mgr);
    109	if (ret) {
    110		dev_err(dev, "FPGA manager is busy\n");
    111		goto err_put_region;
    112	}
    113
    114	/*
    115	 * In some cases, we already have a list of bridges in the
    116	 * fpga region struct.  Or we don't have any bridges.
    117	 */
    118	if (region->get_bridges) {
    119		ret = region->get_bridges(region);
    120		if (ret) {
    121			dev_err(dev, "failed to get fpga region bridges\n");
    122			goto err_unlock_mgr;
    123		}
    124	}
    125
    126	ret = fpga_bridges_disable(&region->bridge_list);
    127	if (ret) {
    128		dev_err(dev, "failed to disable bridges\n");
    129		goto err_put_br;
    130	}
    131
    132	ret = fpga_mgr_load(region->mgr, info);
    133	if (ret) {
    134		dev_err(dev, "failed to load FPGA image\n");
    135		goto err_put_br;
    136	}
    137
    138	ret = fpga_bridges_enable(&region->bridge_list);
    139	if (ret) {
    140		dev_err(dev, "failed to enable region bridges\n");
    141		goto err_put_br;
    142	}
    143
    144	fpga_mgr_unlock(region->mgr);
    145	fpga_region_put(region);
    146
    147	return 0;
    148
    149err_put_br:
    150	if (region->get_bridges)
    151		fpga_bridges_put(&region->bridge_list);
    152err_unlock_mgr:
    153	fpga_mgr_unlock(region->mgr);
    154err_put_region:
    155	fpga_region_put(region);
    156
    157	return ret;
    158}
    159EXPORT_SYMBOL_GPL(fpga_region_program_fpga);
    160
    161static ssize_t compat_id_show(struct device *dev,
    162			      struct device_attribute *attr, char *buf)
    163{
    164	struct fpga_region *region = to_fpga_region(dev);
    165
    166	if (!region->compat_id)
    167		return -ENOENT;
    168
    169	return sprintf(buf, "%016llx%016llx\n",
    170		       (unsigned long long)region->compat_id->id_h,
    171		       (unsigned long long)region->compat_id->id_l);
    172}
    173
    174static DEVICE_ATTR_RO(compat_id);
    175
    176static struct attribute *fpga_region_attrs[] = {
    177	&dev_attr_compat_id.attr,
    178	NULL,
    179};
    180ATTRIBUTE_GROUPS(fpga_region);
    181
    182/**
    183 * fpga_region_register_full - create and register an FPGA Region device
    184 * @parent: device parent
    185 * @info: parameters for FPGA Region
    186 *
    187 * Return: struct fpga_region or ERR_PTR()
    188 */
    189struct fpga_region *
    190fpga_region_register_full(struct device *parent, const struct fpga_region_info *info)
    191{
    192	struct fpga_region *region;
    193	int id, ret = 0;
    194
    195	if (!info) {
    196		dev_err(parent,
    197			"Attempt to register without required info structure\n");
    198		return ERR_PTR(-EINVAL);
    199	}
    200
    201	region = kzalloc(sizeof(*region), GFP_KERNEL);
    202	if (!region)
    203		return ERR_PTR(-ENOMEM);
    204
    205	id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL);
    206	if (id < 0) {
    207		ret = id;
    208		goto err_free;
    209	}
    210
    211	region->mgr = info->mgr;
    212	region->compat_id = info->compat_id;
    213	region->priv = info->priv;
    214	region->get_bridges = info->get_bridges;
    215
    216	mutex_init(&region->mutex);
    217	INIT_LIST_HEAD(&region->bridge_list);
    218
    219	region->dev.class = fpga_region_class;
    220	region->dev.parent = parent;
    221	region->dev.of_node = parent->of_node;
    222	region->dev.id = id;
    223
    224	ret = dev_set_name(&region->dev, "region%d", id);
    225	if (ret)
    226		goto err_remove;
    227
    228	ret = device_register(&region->dev);
    229	if (ret) {
    230		put_device(&region->dev);
    231		return ERR_PTR(ret);
    232	}
    233
    234	return region;
    235
    236err_remove:
    237	ida_simple_remove(&fpga_region_ida, id);
    238err_free:
    239	kfree(region);
    240
    241	return ERR_PTR(ret);
    242}
    243EXPORT_SYMBOL_GPL(fpga_region_register_full);
    244
    245/**
    246 * fpga_region_register - create and register an FPGA Region device
    247 * @parent: device parent
    248 * @mgr: manager that programs this region
    249 * @get_bridges: optional function to get bridges to a list
    250 *
    251 * This simple version of the register function should be sufficient for most users.
    252 * The fpga_region_register_full() function is available for users that need to
    253 * pass additional, optional parameters.
    254 *
    255 * Return: struct fpga_region or ERR_PTR()
    256 */
    257struct fpga_region *
    258fpga_region_register(struct device *parent, struct fpga_manager *mgr,
    259		     int (*get_bridges)(struct fpga_region *))
    260{
    261	struct fpga_region_info info = { 0 };
    262
    263	info.mgr = mgr;
    264	info.get_bridges = get_bridges;
    265
    266	return fpga_region_register_full(parent, &info);
    267}
    268EXPORT_SYMBOL_GPL(fpga_region_register);
    269
    270/**
    271 * fpga_region_unregister - unregister an FPGA region
    272 * @region: FPGA region
    273 *
    274 * This function is intended for use in an FPGA region driver's remove function.
    275 */
    276void fpga_region_unregister(struct fpga_region *region)
    277{
    278	device_unregister(&region->dev);
    279}
    280EXPORT_SYMBOL_GPL(fpga_region_unregister);
    281
    282static void fpga_region_dev_release(struct device *dev)
    283{
    284	struct fpga_region *region = to_fpga_region(dev);
    285
    286	ida_simple_remove(&fpga_region_ida, region->dev.id);
    287	kfree(region);
    288}
    289
    290/**
    291 * fpga_region_init - init function for fpga_region class
    292 * Creates the fpga_region class and registers a reconfig notifier.
    293 */
    294static int __init fpga_region_init(void)
    295{
    296	fpga_region_class = class_create(THIS_MODULE, "fpga_region");
    297	if (IS_ERR(fpga_region_class))
    298		return PTR_ERR(fpga_region_class);
    299
    300	fpga_region_class->dev_groups = fpga_region_groups;
    301	fpga_region_class->dev_release = fpga_region_dev_release;
    302
    303	return 0;
    304}
    305
    306static void __exit fpga_region_exit(void)
    307{
    308	class_destroy(fpga_region_class);
    309	ida_destroy(&fpga_region_ida);
    310}
    311
    312subsys_initcall(fpga_region_init);
    313module_exit(fpga_region_exit);
    314
    315MODULE_DESCRIPTION("FPGA Region");
    316MODULE_AUTHOR("Alan Tull <atull@kernel.org>");
    317MODULE_LICENSE("GPL v2");