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

dfl-fme-pr.c (11652B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Driver for FPGA Management Engine (FME) Partial Reconfiguration
      4 *
      5 * Copyright (C) 2017-2018 Intel Corporation, Inc.
      6 *
      7 * Authors:
      8 *   Kang Luwei <luwei.kang@intel.com>
      9 *   Xiao Guangrong <guangrong.xiao@linux.intel.com>
     10 *   Wu Hao <hao.wu@intel.com>
     11 *   Joseph Grecco <joe.grecco@intel.com>
     12 *   Enno Luebbers <enno.luebbers@intel.com>
     13 *   Tim Whisonant <tim.whisonant@intel.com>
     14 *   Ananda Ravuri <ananda.ravuri@intel.com>
     15 *   Christopher Rauer <christopher.rauer@intel.com>
     16 *   Henry Mitchel <henry.mitchel@intel.com>
     17 */
     18
     19#include <linux/types.h>
     20#include <linux/device.h>
     21#include <linux/vmalloc.h>
     22#include <linux/uaccess.h>
     23#include <linux/fpga/fpga-mgr.h>
     24#include <linux/fpga/fpga-bridge.h>
     25#include <linux/fpga/fpga-region.h>
     26#include <linux/fpga-dfl.h>
     27
     28#include "dfl.h"
     29#include "dfl-fme.h"
     30#include "dfl-fme-pr.h"
     31
     32static struct dfl_fme_region *
     33dfl_fme_region_find_by_port_id(struct dfl_fme *fme, int port_id)
     34{
     35	struct dfl_fme_region *fme_region;
     36
     37	list_for_each_entry(fme_region, &fme->region_list, node)
     38		if (fme_region->port_id == port_id)
     39			return fme_region;
     40
     41	return NULL;
     42}
     43
     44static int dfl_fme_region_match(struct device *dev, const void *data)
     45{
     46	return dev->parent == data;
     47}
     48
     49static struct fpga_region *dfl_fme_region_find(struct dfl_fme *fme, int port_id)
     50{
     51	struct dfl_fme_region *fme_region;
     52	struct fpga_region *region;
     53
     54	fme_region = dfl_fme_region_find_by_port_id(fme, port_id);
     55	if (!fme_region)
     56		return NULL;
     57
     58	region = fpga_region_class_find(NULL, &fme_region->region->dev,
     59					dfl_fme_region_match);
     60	if (!region)
     61		return NULL;
     62
     63	return region;
     64}
     65
     66static int fme_pr(struct platform_device *pdev, unsigned long arg)
     67{
     68	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
     69	void __user *argp = (void __user *)arg;
     70	struct dfl_fpga_fme_port_pr port_pr;
     71	struct fpga_image_info *info;
     72	struct fpga_region *region;
     73	void __iomem *fme_hdr;
     74	struct dfl_fme *fme;
     75	unsigned long minsz;
     76	void *buf = NULL;
     77	size_t length;
     78	int ret = 0;
     79	u64 v;
     80
     81	minsz = offsetofend(struct dfl_fpga_fme_port_pr, buffer_address);
     82
     83	if (copy_from_user(&port_pr, argp, minsz))
     84		return -EFAULT;
     85
     86	if (port_pr.argsz < minsz || port_pr.flags)
     87		return -EINVAL;
     88
     89	/* get fme header region */
     90	fme_hdr = dfl_get_feature_ioaddr_by_id(&pdev->dev,
     91					       FME_FEATURE_ID_HEADER);
     92
     93	/* check port id */
     94	v = readq(fme_hdr + FME_HDR_CAP);
     95	if (port_pr.port_id >= FIELD_GET(FME_CAP_NUM_PORTS, v)) {
     96		dev_dbg(&pdev->dev, "port number more than maximum\n");
     97		return -EINVAL;
     98	}
     99
    100	/*
    101	 * align PR buffer per PR bandwidth, as HW ignores the extra padding
    102	 * data automatically.
    103	 */
    104	length = ALIGN(port_pr.buffer_size, 4);
    105
    106	buf = vmalloc(length);
    107	if (!buf)
    108		return -ENOMEM;
    109
    110	if (copy_from_user(buf,
    111			   (void __user *)(unsigned long)port_pr.buffer_address,
    112			   port_pr.buffer_size)) {
    113		ret = -EFAULT;
    114		goto free_exit;
    115	}
    116
    117	/* prepare fpga_image_info for PR */
    118	info = fpga_image_info_alloc(&pdev->dev);
    119	if (!info) {
    120		ret = -ENOMEM;
    121		goto free_exit;
    122	}
    123
    124	info->flags |= FPGA_MGR_PARTIAL_RECONFIG;
    125
    126	mutex_lock(&pdata->lock);
    127	fme = dfl_fpga_pdata_get_private(pdata);
    128	/* fme device has been unregistered. */
    129	if (!fme) {
    130		ret = -EINVAL;
    131		goto unlock_exit;
    132	}
    133
    134	region = dfl_fme_region_find(fme, port_pr.port_id);
    135	if (!region) {
    136		ret = -EINVAL;
    137		goto unlock_exit;
    138	}
    139
    140	fpga_image_info_free(region->info);
    141
    142	info->buf = buf;
    143	info->count = length;
    144	info->region_id = port_pr.port_id;
    145	region->info = info;
    146
    147	ret = fpga_region_program_fpga(region);
    148
    149	/*
    150	 * it allows userspace to reset the PR region's logic by disabling and
    151	 * reenabling the bridge to clear things out between acceleration runs.
    152	 * so no need to hold the bridges after partial reconfiguration.
    153	 */
    154	if (region->get_bridges)
    155		fpga_bridges_put(&region->bridge_list);
    156
    157	put_device(&region->dev);
    158unlock_exit:
    159	mutex_unlock(&pdata->lock);
    160free_exit:
    161	vfree(buf);
    162	return ret;
    163}
    164
    165/**
    166 * dfl_fme_create_mgr - create fpga mgr platform device as child device
    167 *
    168 * @pdata: fme platform_device's pdata
    169 *
    170 * Return: mgr platform device if successful, and error code otherwise.
    171 */
    172static struct platform_device *
    173dfl_fme_create_mgr(struct dfl_feature_platform_data *pdata,
    174		   struct dfl_feature *feature)
    175{
    176	struct platform_device *mgr, *fme = pdata->dev;
    177	struct dfl_fme_mgr_pdata mgr_pdata;
    178	int ret = -ENOMEM;
    179
    180	if (!feature->ioaddr)
    181		return ERR_PTR(-ENODEV);
    182
    183	mgr_pdata.ioaddr = feature->ioaddr;
    184
    185	/*
    186	 * Each FME has only one fpga-mgr, so allocate platform device using
    187	 * the same FME platform device id.
    188	 */
    189	mgr = platform_device_alloc(DFL_FPGA_FME_MGR, fme->id);
    190	if (!mgr)
    191		return ERR_PTR(ret);
    192
    193	mgr->dev.parent = &fme->dev;
    194
    195	ret = platform_device_add_data(mgr, &mgr_pdata, sizeof(mgr_pdata));
    196	if (ret)
    197		goto create_mgr_err;
    198
    199	ret = platform_device_add(mgr);
    200	if (ret)
    201		goto create_mgr_err;
    202
    203	return mgr;
    204
    205create_mgr_err:
    206	platform_device_put(mgr);
    207	return ERR_PTR(ret);
    208}
    209
    210/**
    211 * dfl_fme_destroy_mgr - destroy fpga mgr platform device
    212 * @pdata: fme platform device's pdata
    213 */
    214static void dfl_fme_destroy_mgr(struct dfl_feature_platform_data *pdata)
    215{
    216	struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata);
    217
    218	platform_device_unregister(priv->mgr);
    219}
    220
    221/**
    222 * dfl_fme_create_bridge - create fme fpga bridge platform device as child
    223 *
    224 * @pdata: fme platform device's pdata
    225 * @port_id: port id for the bridge to be created.
    226 *
    227 * Return: bridge platform device if successful, and error code otherwise.
    228 */
    229static struct dfl_fme_bridge *
    230dfl_fme_create_bridge(struct dfl_feature_platform_data *pdata, int port_id)
    231{
    232	struct device *dev = &pdata->dev->dev;
    233	struct dfl_fme_br_pdata br_pdata;
    234	struct dfl_fme_bridge *fme_br;
    235	int ret = -ENOMEM;
    236
    237	fme_br = devm_kzalloc(dev, sizeof(*fme_br), GFP_KERNEL);
    238	if (!fme_br)
    239		return ERR_PTR(ret);
    240
    241	br_pdata.cdev = pdata->dfl_cdev;
    242	br_pdata.port_id = port_id;
    243
    244	fme_br->br = platform_device_alloc(DFL_FPGA_FME_BRIDGE,
    245					   PLATFORM_DEVID_AUTO);
    246	if (!fme_br->br)
    247		return ERR_PTR(ret);
    248
    249	fme_br->br->dev.parent = dev;
    250
    251	ret = platform_device_add_data(fme_br->br, &br_pdata, sizeof(br_pdata));
    252	if (ret)
    253		goto create_br_err;
    254
    255	ret = platform_device_add(fme_br->br);
    256	if (ret)
    257		goto create_br_err;
    258
    259	return fme_br;
    260
    261create_br_err:
    262	platform_device_put(fme_br->br);
    263	return ERR_PTR(ret);
    264}
    265
    266/**
    267 * dfl_fme_destroy_bridge - destroy fpga bridge platform device
    268 * @fme_br: fme bridge to destroy
    269 */
    270static void dfl_fme_destroy_bridge(struct dfl_fme_bridge *fme_br)
    271{
    272	platform_device_unregister(fme_br->br);
    273}
    274
    275/**
    276 * dfl_fme_destroy_bridge - destroy all fpga bridge platform device
    277 * @pdata: fme platform device's pdata
    278 */
    279static void dfl_fme_destroy_bridges(struct dfl_feature_platform_data *pdata)
    280{
    281	struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata);
    282	struct dfl_fme_bridge *fbridge, *tmp;
    283
    284	list_for_each_entry_safe(fbridge, tmp, &priv->bridge_list, node) {
    285		list_del(&fbridge->node);
    286		dfl_fme_destroy_bridge(fbridge);
    287	}
    288}
    289
    290/**
    291 * dfl_fme_create_region - create fpga region platform device as child
    292 *
    293 * @pdata: fme platform device's pdata
    294 * @mgr: mgr platform device needed for region
    295 * @br: br platform device needed for region
    296 * @port_id: port id
    297 *
    298 * Return: fme region if successful, and error code otherwise.
    299 */
    300static struct dfl_fme_region *
    301dfl_fme_create_region(struct dfl_feature_platform_data *pdata,
    302		      struct platform_device *mgr,
    303		      struct platform_device *br, int port_id)
    304{
    305	struct dfl_fme_region_pdata region_pdata;
    306	struct device *dev = &pdata->dev->dev;
    307	struct dfl_fme_region *fme_region;
    308	int ret = -ENOMEM;
    309
    310	fme_region = devm_kzalloc(dev, sizeof(*fme_region), GFP_KERNEL);
    311	if (!fme_region)
    312		return ERR_PTR(ret);
    313
    314	region_pdata.mgr = mgr;
    315	region_pdata.br = br;
    316
    317	/*
    318	 * Each FPGA device may have more than one port, so allocate platform
    319	 * device using the same port platform device id.
    320	 */
    321	fme_region->region = platform_device_alloc(DFL_FPGA_FME_REGION, br->id);
    322	if (!fme_region->region)
    323		return ERR_PTR(ret);
    324
    325	fme_region->region->dev.parent = dev;
    326
    327	ret = platform_device_add_data(fme_region->region, &region_pdata,
    328				       sizeof(region_pdata));
    329	if (ret)
    330		goto create_region_err;
    331
    332	ret = platform_device_add(fme_region->region);
    333	if (ret)
    334		goto create_region_err;
    335
    336	fme_region->port_id = port_id;
    337
    338	return fme_region;
    339
    340create_region_err:
    341	platform_device_put(fme_region->region);
    342	return ERR_PTR(ret);
    343}
    344
    345/**
    346 * dfl_fme_destroy_region - destroy fme region
    347 * @fme_region: fme region to destroy
    348 */
    349static void dfl_fme_destroy_region(struct dfl_fme_region *fme_region)
    350{
    351	platform_device_unregister(fme_region->region);
    352}
    353
    354/**
    355 * dfl_fme_destroy_regions - destroy all fme regions
    356 * @pdata: fme platform device's pdata
    357 */
    358static void dfl_fme_destroy_regions(struct dfl_feature_platform_data *pdata)
    359{
    360	struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata);
    361	struct dfl_fme_region *fme_region, *tmp;
    362
    363	list_for_each_entry_safe(fme_region, tmp, &priv->region_list, node) {
    364		list_del(&fme_region->node);
    365		dfl_fme_destroy_region(fme_region);
    366	}
    367}
    368
    369static int pr_mgmt_init(struct platform_device *pdev,
    370			struct dfl_feature *feature)
    371{
    372	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
    373	struct dfl_fme_region *fme_region;
    374	struct dfl_fme_bridge *fme_br;
    375	struct platform_device *mgr;
    376	struct dfl_fme *priv;
    377	void __iomem *fme_hdr;
    378	int ret = -ENODEV, i = 0;
    379	u64 fme_cap, port_offset;
    380
    381	fme_hdr = dfl_get_feature_ioaddr_by_id(&pdev->dev,
    382					       FME_FEATURE_ID_HEADER);
    383
    384	mutex_lock(&pdata->lock);
    385	priv = dfl_fpga_pdata_get_private(pdata);
    386
    387	/* Initialize the region and bridge sub device list */
    388	INIT_LIST_HEAD(&priv->region_list);
    389	INIT_LIST_HEAD(&priv->bridge_list);
    390
    391	/* Create fpga mgr platform device */
    392	mgr = dfl_fme_create_mgr(pdata, feature);
    393	if (IS_ERR(mgr)) {
    394		dev_err(&pdev->dev, "fail to create fpga mgr pdev\n");
    395		goto unlock;
    396	}
    397
    398	priv->mgr = mgr;
    399
    400	/* Read capability register to check number of regions and bridges */
    401	fme_cap = readq(fme_hdr + FME_HDR_CAP);
    402	for (; i < FIELD_GET(FME_CAP_NUM_PORTS, fme_cap); i++) {
    403		port_offset = readq(fme_hdr + FME_HDR_PORT_OFST(i));
    404		if (!(port_offset & FME_PORT_OFST_IMP))
    405			continue;
    406
    407		/* Create bridge for each port */
    408		fme_br = dfl_fme_create_bridge(pdata, i);
    409		if (IS_ERR(fme_br)) {
    410			ret = PTR_ERR(fme_br);
    411			goto destroy_region;
    412		}
    413
    414		list_add(&fme_br->node, &priv->bridge_list);
    415
    416		/* Create region for each port */
    417		fme_region = dfl_fme_create_region(pdata, mgr,
    418						   fme_br->br, i);
    419		if (IS_ERR(fme_region)) {
    420			ret = PTR_ERR(fme_region);
    421			goto destroy_region;
    422		}
    423
    424		list_add(&fme_region->node, &priv->region_list);
    425	}
    426	mutex_unlock(&pdata->lock);
    427
    428	return 0;
    429
    430destroy_region:
    431	dfl_fme_destroy_regions(pdata);
    432	dfl_fme_destroy_bridges(pdata);
    433	dfl_fme_destroy_mgr(pdata);
    434unlock:
    435	mutex_unlock(&pdata->lock);
    436	return ret;
    437}
    438
    439static void pr_mgmt_uinit(struct platform_device *pdev,
    440			  struct dfl_feature *feature)
    441{
    442	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
    443
    444	mutex_lock(&pdata->lock);
    445
    446	dfl_fme_destroy_regions(pdata);
    447	dfl_fme_destroy_bridges(pdata);
    448	dfl_fme_destroy_mgr(pdata);
    449	mutex_unlock(&pdata->lock);
    450}
    451
    452static long fme_pr_ioctl(struct platform_device *pdev,
    453			 struct dfl_feature *feature,
    454			 unsigned int cmd, unsigned long arg)
    455{
    456	long ret;
    457
    458	switch (cmd) {
    459	case DFL_FPGA_FME_PORT_PR:
    460		ret = fme_pr(pdev, arg);
    461		break;
    462	default:
    463		ret = -ENODEV;
    464	}
    465
    466	return ret;
    467}
    468
    469const struct dfl_feature_id fme_pr_mgmt_id_table[] = {
    470	{.id = FME_FEATURE_ID_PR_MGMT,},
    471	{0}
    472};
    473
    474const struct dfl_feature_ops fme_pr_mgmt_ops = {
    475	.init = pr_mgmt_init,
    476	.uinit = pr_mgmt_uinit,
    477	.ioctl = fme_pr_ioctl,
    478};