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

core.c (12550B)


      1// SPDX-License-Identifier: GPL-2.0+
      2// Copyright 2019 IBM Corp.
      3#include <linux/idr.h>
      4#include "ocxl_internal.h"
      5
      6static struct ocxl_fn *ocxl_fn_get(struct ocxl_fn *fn)
      7{
      8	return (get_device(&fn->dev) == NULL) ? NULL : fn;
      9}
     10
     11static void ocxl_fn_put(struct ocxl_fn *fn)
     12{
     13	put_device(&fn->dev);
     14}
     15
     16static struct ocxl_afu *alloc_afu(struct ocxl_fn *fn)
     17{
     18	struct ocxl_afu *afu;
     19
     20	afu = kzalloc(sizeof(struct ocxl_afu), GFP_KERNEL);
     21	if (!afu)
     22		return NULL;
     23
     24	kref_init(&afu->kref);
     25	mutex_init(&afu->contexts_lock);
     26	mutex_init(&afu->afu_control_lock);
     27	idr_init(&afu->contexts_idr);
     28	afu->fn = fn;
     29	ocxl_fn_get(fn);
     30	return afu;
     31}
     32
     33static void free_afu(struct kref *kref)
     34{
     35	struct ocxl_afu *afu = container_of(kref, struct ocxl_afu, kref);
     36
     37	idr_destroy(&afu->contexts_idr);
     38	ocxl_fn_put(afu->fn);
     39	kfree(afu);
     40}
     41
     42void ocxl_afu_get(struct ocxl_afu *afu)
     43{
     44	kref_get(&afu->kref);
     45}
     46EXPORT_SYMBOL_GPL(ocxl_afu_get);
     47
     48void ocxl_afu_put(struct ocxl_afu *afu)
     49{
     50	kref_put(&afu->kref, free_afu);
     51}
     52EXPORT_SYMBOL_GPL(ocxl_afu_put);
     53
     54static int assign_afu_actag(struct ocxl_afu *afu)
     55{
     56	struct ocxl_fn *fn = afu->fn;
     57	int actag_count, actag_offset;
     58	struct pci_dev *pci_dev = to_pci_dev(fn->dev.parent);
     59
     60	/*
     61	 * if there were not enough actags for the function, each afu
     62	 * reduces its count as well
     63	 */
     64	actag_count = afu->config.actag_supported *
     65		fn->actag_enabled / fn->actag_supported;
     66	actag_offset = ocxl_actag_afu_alloc(fn, actag_count);
     67	if (actag_offset < 0) {
     68		dev_err(&pci_dev->dev, "Can't allocate %d actags for AFU: %d\n",
     69			actag_count, actag_offset);
     70		return actag_offset;
     71	}
     72	afu->actag_base = fn->actag_base + actag_offset;
     73	afu->actag_enabled = actag_count;
     74
     75	ocxl_config_set_afu_actag(pci_dev, afu->config.dvsec_afu_control_pos,
     76				afu->actag_base, afu->actag_enabled);
     77	dev_dbg(&pci_dev->dev, "actag base=%d enabled=%d\n",
     78		afu->actag_base, afu->actag_enabled);
     79	return 0;
     80}
     81
     82static void reclaim_afu_actag(struct ocxl_afu *afu)
     83{
     84	struct ocxl_fn *fn = afu->fn;
     85	int start_offset, size;
     86
     87	start_offset = afu->actag_base - fn->actag_base;
     88	size = afu->actag_enabled;
     89	ocxl_actag_afu_free(afu->fn, start_offset, size);
     90}
     91
     92static int assign_afu_pasid(struct ocxl_afu *afu)
     93{
     94	struct ocxl_fn *fn = afu->fn;
     95	int pasid_count, pasid_offset;
     96	struct pci_dev *pci_dev = to_pci_dev(fn->dev.parent);
     97
     98	/*
     99	 * We only support the case where the function configuration
    100	 * requested enough PASIDs to cover all AFUs.
    101	 */
    102	pasid_count = 1 << afu->config.pasid_supported_log;
    103	pasid_offset = ocxl_pasid_afu_alloc(fn, pasid_count);
    104	if (pasid_offset < 0) {
    105		dev_err(&pci_dev->dev, "Can't allocate %d PASIDs for AFU: %d\n",
    106			pasid_count, pasid_offset);
    107		return pasid_offset;
    108	}
    109	afu->pasid_base = fn->pasid_base + pasid_offset;
    110	afu->pasid_count = 0;
    111	afu->pasid_max = pasid_count;
    112
    113	ocxl_config_set_afu_pasid(pci_dev, afu->config.dvsec_afu_control_pos,
    114				afu->pasid_base,
    115				afu->config.pasid_supported_log);
    116	dev_dbg(&pci_dev->dev, "PASID base=%d, enabled=%d\n",
    117		afu->pasid_base, pasid_count);
    118	return 0;
    119}
    120
    121static void reclaim_afu_pasid(struct ocxl_afu *afu)
    122{
    123	struct ocxl_fn *fn = afu->fn;
    124	int start_offset, size;
    125
    126	start_offset = afu->pasid_base - fn->pasid_base;
    127	size = 1 << afu->config.pasid_supported_log;
    128	ocxl_pasid_afu_free(afu->fn, start_offset, size);
    129}
    130
    131static int reserve_fn_bar(struct ocxl_fn *fn, int bar)
    132{
    133	struct pci_dev *dev = to_pci_dev(fn->dev.parent);
    134	int rc, idx;
    135
    136	if (bar != 0 && bar != 2 && bar != 4)
    137		return -EINVAL;
    138
    139	idx = bar >> 1;
    140	if (fn->bar_used[idx]++ == 0) {
    141		rc = pci_request_region(dev, bar, "ocxl");
    142		if (rc)
    143			return rc;
    144	}
    145	return 0;
    146}
    147
    148static void release_fn_bar(struct ocxl_fn *fn, int bar)
    149{
    150	struct pci_dev *dev = to_pci_dev(fn->dev.parent);
    151	int idx;
    152
    153	if (bar != 0 && bar != 2 && bar != 4)
    154		return;
    155
    156	idx = bar >> 1;
    157	if (--fn->bar_used[idx] == 0)
    158		pci_release_region(dev, bar);
    159	WARN_ON(fn->bar_used[idx] < 0);
    160}
    161
    162static int map_mmio_areas(struct ocxl_afu *afu)
    163{
    164	int rc;
    165	struct pci_dev *pci_dev = to_pci_dev(afu->fn->dev.parent);
    166
    167	rc = reserve_fn_bar(afu->fn, afu->config.global_mmio_bar);
    168	if (rc)
    169		return rc;
    170
    171	rc = reserve_fn_bar(afu->fn, afu->config.pp_mmio_bar);
    172	if (rc) {
    173		release_fn_bar(afu->fn, afu->config.global_mmio_bar);
    174		return rc;
    175	}
    176
    177	afu->global_mmio_start =
    178		pci_resource_start(pci_dev, afu->config.global_mmio_bar) +
    179		afu->config.global_mmio_offset;
    180	afu->pp_mmio_start =
    181		pci_resource_start(pci_dev, afu->config.pp_mmio_bar) +
    182		afu->config.pp_mmio_offset;
    183
    184	afu->global_mmio_ptr = ioremap(afu->global_mmio_start,
    185				afu->config.global_mmio_size);
    186	if (!afu->global_mmio_ptr) {
    187		release_fn_bar(afu->fn, afu->config.pp_mmio_bar);
    188		release_fn_bar(afu->fn, afu->config.global_mmio_bar);
    189		dev_err(&pci_dev->dev, "Error mapping global mmio area\n");
    190		return -ENOMEM;
    191	}
    192
    193	/*
    194	 * Leave an empty page between the per-process mmio area and
    195	 * the AFU interrupt mappings
    196	 */
    197	afu->irq_base_offset = afu->config.pp_mmio_stride + PAGE_SIZE;
    198	return 0;
    199}
    200
    201static void unmap_mmio_areas(struct ocxl_afu *afu)
    202{
    203	if (afu->global_mmio_ptr) {
    204		iounmap(afu->global_mmio_ptr);
    205		afu->global_mmio_ptr = NULL;
    206	}
    207	afu->global_mmio_start = 0;
    208	afu->pp_mmio_start = 0;
    209	release_fn_bar(afu->fn, afu->config.pp_mmio_bar);
    210	release_fn_bar(afu->fn, afu->config.global_mmio_bar);
    211}
    212
    213static int configure_afu(struct ocxl_afu *afu, u8 afu_idx, struct pci_dev *dev)
    214{
    215	int rc;
    216
    217	rc = ocxl_config_read_afu(dev, &afu->fn->config, &afu->config, afu_idx);
    218	if (rc)
    219		return rc;
    220
    221	rc = assign_afu_actag(afu);
    222	if (rc)
    223		return rc;
    224
    225	rc = assign_afu_pasid(afu);
    226	if (rc)
    227		goto err_free_actag;
    228
    229	rc = map_mmio_areas(afu);
    230	if (rc)
    231		goto err_free_pasid;
    232
    233	return 0;
    234
    235err_free_pasid:
    236	reclaim_afu_pasid(afu);
    237err_free_actag:
    238	reclaim_afu_actag(afu);
    239	return rc;
    240}
    241
    242static void deconfigure_afu(struct ocxl_afu *afu)
    243{
    244	unmap_mmio_areas(afu);
    245	reclaim_afu_pasid(afu);
    246	reclaim_afu_actag(afu);
    247}
    248
    249static int activate_afu(struct pci_dev *dev, struct ocxl_afu *afu)
    250{
    251	ocxl_config_set_afu_state(dev, afu->config.dvsec_afu_control_pos, 1);
    252
    253	return 0;
    254}
    255
    256static void deactivate_afu(struct ocxl_afu *afu)
    257{
    258	struct pci_dev *dev = to_pci_dev(afu->fn->dev.parent);
    259
    260	ocxl_config_set_afu_state(dev, afu->config.dvsec_afu_control_pos, 0);
    261}
    262
    263static int init_afu(struct pci_dev *dev, struct ocxl_fn *fn, u8 afu_idx)
    264{
    265	int rc;
    266	struct ocxl_afu *afu;
    267
    268	afu = alloc_afu(fn);
    269	if (!afu)
    270		return -ENOMEM;
    271
    272	rc = configure_afu(afu, afu_idx, dev);
    273	if (rc) {
    274		ocxl_afu_put(afu);
    275		return rc;
    276	}
    277
    278	rc = activate_afu(dev, afu);
    279	if (rc) {
    280		deconfigure_afu(afu);
    281		ocxl_afu_put(afu);
    282		return rc;
    283	}
    284
    285	list_add_tail(&afu->list, &fn->afu_list);
    286
    287	return 0;
    288}
    289
    290static void remove_afu(struct ocxl_afu *afu)
    291{
    292	list_del(&afu->list);
    293	ocxl_context_detach_all(afu);
    294	deactivate_afu(afu);
    295	deconfigure_afu(afu);
    296	ocxl_afu_put(afu); // matches the implicit get in alloc_afu
    297}
    298
    299static struct ocxl_fn *alloc_function(void)
    300{
    301	struct ocxl_fn *fn;
    302
    303	fn = kzalloc(sizeof(struct ocxl_fn), GFP_KERNEL);
    304	if (!fn)
    305		return NULL;
    306
    307	INIT_LIST_HEAD(&fn->afu_list);
    308	INIT_LIST_HEAD(&fn->pasid_list);
    309	INIT_LIST_HEAD(&fn->actag_list);
    310
    311	return fn;
    312}
    313
    314static void free_function(struct ocxl_fn *fn)
    315{
    316	WARN_ON(!list_empty(&fn->afu_list));
    317	WARN_ON(!list_empty(&fn->pasid_list));
    318	kfree(fn);
    319}
    320
    321static void free_function_dev(struct device *dev)
    322{
    323	struct ocxl_fn *fn = container_of(dev, struct ocxl_fn, dev);
    324
    325	free_function(fn);
    326}
    327
    328static int set_function_device(struct ocxl_fn *fn, struct pci_dev *dev)
    329{
    330	fn->dev.parent = &dev->dev;
    331	fn->dev.release = free_function_dev;
    332	return dev_set_name(&fn->dev, "ocxlfn.%s", dev_name(&dev->dev));
    333}
    334
    335static int assign_function_actag(struct ocxl_fn *fn)
    336{
    337	struct pci_dev *dev = to_pci_dev(fn->dev.parent);
    338	u16 base, enabled, supported;
    339	int rc;
    340
    341	rc = ocxl_config_get_actag_info(dev, &base, &enabled, &supported);
    342	if (rc)
    343		return rc;
    344
    345	fn->actag_base = base;
    346	fn->actag_enabled = enabled;
    347	fn->actag_supported = supported;
    348
    349	ocxl_config_set_actag(dev, fn->config.dvsec_function_pos,
    350			fn->actag_base,	fn->actag_enabled);
    351	dev_dbg(&fn->dev, "actag range starting at %d, enabled %d\n",
    352		fn->actag_base, fn->actag_enabled);
    353	return 0;
    354}
    355
    356static int set_function_pasid(struct ocxl_fn *fn)
    357{
    358	struct pci_dev *dev = to_pci_dev(fn->dev.parent);
    359	int rc, desired_count, max_count;
    360
    361	/* A function may not require any PASID */
    362	if (fn->config.max_pasid_log < 0)
    363		return 0;
    364
    365	rc = ocxl_config_get_pasid_info(dev, &max_count);
    366	if (rc)
    367		return rc;
    368
    369	desired_count = 1 << fn->config.max_pasid_log;
    370
    371	if (desired_count > max_count) {
    372		dev_err(&fn->dev,
    373			"Function requires more PASIDs than is available (%d vs. %d)\n",
    374			desired_count, max_count);
    375		return -ENOSPC;
    376	}
    377
    378	fn->pasid_base = 0;
    379	return 0;
    380}
    381
    382static int configure_function(struct ocxl_fn *fn, struct pci_dev *dev)
    383{
    384	int rc;
    385
    386	rc = pci_enable_device(dev);
    387	if (rc) {
    388		dev_err(&dev->dev, "pci_enable_device failed: %d\n", rc);
    389		return rc;
    390	}
    391
    392	/*
    393	 * Once it has been confirmed to work on our hardware, we
    394	 * should reset the function, to force the adapter to restart
    395	 * from scratch.
    396	 * A function reset would also reset all its AFUs.
    397	 *
    398	 * Some hints for implementation:
    399	 *
    400	 * - there's not status bit to know when the reset is done. We
    401	 *   should try reading the config space to know when it's
    402	 *   done.
    403	 * - probably something like:
    404	 *	Reset
    405	 *	wait 100ms
    406	 *	issue config read
    407	 *	allow device up to 1 sec to return success on config
    408	 *	read before declaring it broken
    409	 *
    410	 * Some shared logic on the card (CFG, TLX) won't be reset, so
    411	 * there's no guarantee that it will be enough.
    412	 */
    413	rc = ocxl_config_read_function(dev, &fn->config);
    414	if (rc)
    415		return rc;
    416
    417	rc = set_function_device(fn, dev);
    418	if (rc)
    419		return rc;
    420
    421	rc = assign_function_actag(fn);
    422	if (rc)
    423		return rc;
    424
    425	rc = set_function_pasid(fn);
    426	if (rc)
    427		return rc;
    428
    429	rc = ocxl_link_setup(dev, 0, &fn->link);
    430	if (rc)
    431		return rc;
    432
    433	rc = ocxl_config_set_TL(dev, fn->config.dvsec_tl_pos);
    434	if (rc) {
    435		ocxl_link_release(dev, fn->link);
    436		return rc;
    437	}
    438	return 0;
    439}
    440
    441static void deconfigure_function(struct ocxl_fn *fn)
    442{
    443	struct pci_dev *dev = to_pci_dev(fn->dev.parent);
    444
    445	ocxl_link_release(dev, fn->link);
    446	pci_disable_device(dev);
    447}
    448
    449static struct ocxl_fn *init_function(struct pci_dev *dev)
    450{
    451	struct ocxl_fn *fn;
    452	int rc;
    453
    454	fn = alloc_function();
    455	if (!fn)
    456		return ERR_PTR(-ENOMEM);
    457
    458	rc = configure_function(fn, dev);
    459	if (rc) {
    460		free_function(fn);
    461		return ERR_PTR(rc);
    462	}
    463
    464	rc = device_register(&fn->dev);
    465	if (rc) {
    466		deconfigure_function(fn);
    467		put_device(&fn->dev);
    468		return ERR_PTR(rc);
    469	}
    470	return fn;
    471}
    472
    473// Device detection & initialisation
    474
    475struct ocxl_fn *ocxl_function_open(struct pci_dev *dev)
    476{
    477	int rc, afu_count = 0;
    478	u8 afu;
    479	struct ocxl_fn *fn;
    480
    481	if (!radix_enabled()) {
    482		dev_err(&dev->dev, "Unsupported memory model (hash)\n");
    483		return ERR_PTR(-ENODEV);
    484	}
    485
    486	fn = init_function(dev);
    487	if (IS_ERR(fn)) {
    488		dev_err(&dev->dev, "function init failed: %li\n",
    489			PTR_ERR(fn));
    490		return fn;
    491	}
    492
    493	for (afu = 0; afu <= fn->config.max_afu_index; afu++) {
    494		rc = ocxl_config_check_afu_index(dev, &fn->config, afu);
    495		if (rc > 0) {
    496			rc = init_afu(dev, fn, afu);
    497			if (rc) {
    498				dev_err(&dev->dev,
    499					"Can't initialize AFU index %d\n", afu);
    500				continue;
    501			}
    502			afu_count++;
    503		}
    504	}
    505	dev_info(&dev->dev, "%d AFU(s) configured\n", afu_count);
    506	return fn;
    507}
    508EXPORT_SYMBOL_GPL(ocxl_function_open);
    509
    510struct list_head *ocxl_function_afu_list(struct ocxl_fn *fn)
    511{
    512	return &fn->afu_list;
    513}
    514EXPORT_SYMBOL_GPL(ocxl_function_afu_list);
    515
    516struct ocxl_afu *ocxl_function_fetch_afu(struct ocxl_fn *fn, u8 afu_idx)
    517{
    518	struct ocxl_afu *afu;
    519
    520	list_for_each_entry(afu, &fn->afu_list, list) {
    521		if (afu->config.idx == afu_idx)
    522			return afu;
    523	}
    524
    525	return NULL;
    526}
    527EXPORT_SYMBOL_GPL(ocxl_function_fetch_afu);
    528
    529const struct ocxl_fn_config *ocxl_function_config(struct ocxl_fn *fn)
    530{
    531	return &fn->config;
    532}
    533EXPORT_SYMBOL_GPL(ocxl_function_config);
    534
    535void ocxl_function_close(struct ocxl_fn *fn)
    536{
    537	struct ocxl_afu *afu, *tmp;
    538
    539	list_for_each_entry_safe(afu, tmp, &fn->afu_list, list) {
    540		remove_afu(afu);
    541	}
    542
    543	deconfigure_function(fn);
    544	device_unregister(&fn->dev);
    545}
    546EXPORT_SYMBOL_GPL(ocxl_function_close);
    547
    548// AFU Metadata
    549
    550struct ocxl_afu_config *ocxl_afu_config(struct ocxl_afu *afu)
    551{
    552	return &afu->config;
    553}
    554EXPORT_SYMBOL_GPL(ocxl_afu_config);
    555
    556void ocxl_afu_set_private(struct ocxl_afu *afu, void *private)
    557{
    558	afu->private = private;
    559}
    560EXPORT_SYMBOL_GPL(ocxl_afu_set_private);
    561
    562void *ocxl_afu_get_private(struct ocxl_afu *afu)
    563{
    564	if (afu)
    565		return afu->private;
    566
    567	return NULL;
    568}
    569EXPORT_SYMBOL_GPL(ocxl_afu_get_private);