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

manager.c (10057B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * manager.c - Resource Management, Conflict Resolution, Activation and Disabling of Devices
      4 *
      5 * based on isapnp.c resource management (c) Jaroslav Kysela <perex@perex.cz>
      6 * Copyright 2003 Adam Belay <ambx1@neo.rr.com>
      7 * Copyright (C) 2008 Hewlett-Packard Development Company, L.P.
      8 *	Bjorn Helgaas <bjorn.helgaas@hp.com>
      9 */
     10
     11#include <linux/errno.h>
     12#include <linux/module.h>
     13#include <linux/init.h>
     14#include <linux/kernel.h>
     15#include <linux/pnp.h>
     16#include <linux/bitmap.h>
     17#include <linux/mutex.h>
     18#include "base.h"
     19
     20DEFINE_MUTEX(pnp_res_mutex);
     21
     22static struct resource *pnp_find_resource(struct pnp_dev *dev,
     23					  unsigned char rule,
     24					  unsigned long type,
     25					  unsigned int bar)
     26{
     27	struct resource *res = pnp_get_resource(dev, type, bar);
     28
     29	/* when the resource already exists, set its resource bits from rule */
     30	if (res) {
     31		res->flags &= ~IORESOURCE_BITS;
     32		res->flags |= rule & IORESOURCE_BITS;
     33	}
     34
     35	return res;
     36}
     37
     38static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx)
     39{
     40	struct resource *res, local_res;
     41
     42	res = pnp_find_resource(dev, rule->flags, IORESOURCE_IO, idx);
     43	if (res) {
     44		pnp_dbg(&dev->dev, "  io %d already set to %#llx-%#llx "
     45			"flags %#lx\n", idx, (unsigned long long) res->start,
     46			(unsigned long long) res->end, res->flags);
     47		return 0;
     48	}
     49
     50	res = &local_res;
     51	res->flags = rule->flags | IORESOURCE_AUTO;
     52	res->start = 0;
     53	res->end = 0;
     54
     55	if (!rule->size) {
     56		res->flags |= IORESOURCE_DISABLED;
     57		pnp_dbg(&dev->dev, "  io %d disabled\n", idx);
     58		goto __add;
     59	}
     60
     61	res->start = rule->min;
     62	res->end = res->start + rule->size - 1;
     63
     64	while (!pnp_check_port(dev, res)) {
     65		res->start += rule->align;
     66		res->end = res->start + rule->size - 1;
     67		if (res->start > rule->max || !rule->align) {
     68			pnp_dbg(&dev->dev, "  couldn't assign io %d "
     69				"(min %#llx max %#llx)\n", idx,
     70				(unsigned long long) rule->min,
     71				(unsigned long long) rule->max);
     72			return -EBUSY;
     73		}
     74	}
     75
     76__add:
     77	pnp_add_io_resource(dev, res->start, res->end, res->flags);
     78	return 0;
     79}
     80
     81static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx)
     82{
     83	struct resource *res, local_res;
     84
     85	res = pnp_find_resource(dev, rule->flags, IORESOURCE_MEM, idx);
     86	if (res) {
     87		pnp_dbg(&dev->dev, "  mem %d already set to %#llx-%#llx "
     88			"flags %#lx\n", idx, (unsigned long long) res->start,
     89			(unsigned long long) res->end, res->flags);
     90		return 0;
     91	}
     92
     93	res = &local_res;
     94	res->flags = rule->flags | IORESOURCE_AUTO;
     95	res->start = 0;
     96	res->end = 0;
     97
     98	/* ??? rule->flags restricted to 8 bits, all tests bogus ??? */
     99	if (!(rule->flags & IORESOURCE_MEM_WRITEABLE))
    100		res->flags |= IORESOURCE_READONLY;
    101	if (rule->flags & IORESOURCE_MEM_RANGELENGTH)
    102		res->flags |= IORESOURCE_RANGELENGTH;
    103	if (rule->flags & IORESOURCE_MEM_SHADOWABLE)
    104		res->flags |= IORESOURCE_SHADOWABLE;
    105
    106	if (!rule->size) {
    107		res->flags |= IORESOURCE_DISABLED;
    108		pnp_dbg(&dev->dev, "  mem %d disabled\n", idx);
    109		goto __add;
    110	}
    111
    112	res->start = rule->min;
    113	res->end = res->start + rule->size - 1;
    114
    115	while (!pnp_check_mem(dev, res)) {
    116		res->start += rule->align;
    117		res->end = res->start + rule->size - 1;
    118		if (res->start > rule->max || !rule->align) {
    119			pnp_dbg(&dev->dev, "  couldn't assign mem %d "
    120				"(min %#llx max %#llx)\n", idx,
    121				(unsigned long long) rule->min,
    122				(unsigned long long) rule->max);
    123			return -EBUSY;
    124		}
    125	}
    126
    127__add:
    128	pnp_add_mem_resource(dev, res->start, res->end, res->flags);
    129	return 0;
    130}
    131
    132static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx)
    133{
    134	struct resource *res, local_res;
    135	int i;
    136
    137	/* IRQ priority: this table is good for i386 */
    138	static unsigned short xtab[16] = {
    139		5, 10, 11, 12, 9, 14, 15, 7, 3, 4, 13, 0, 1, 6, 8, 2
    140	};
    141
    142	res = pnp_find_resource(dev, rule->flags, IORESOURCE_IRQ, idx);
    143	if (res) {
    144		pnp_dbg(&dev->dev, "  irq %d already set to %d flags %#lx\n",
    145			idx, (int) res->start, res->flags);
    146		return 0;
    147	}
    148
    149	res = &local_res;
    150	res->flags = rule->flags | IORESOURCE_AUTO;
    151	res->start = -1;
    152	res->end = -1;
    153
    154	if (bitmap_empty(rule->map.bits, PNP_IRQ_NR)) {
    155		res->flags |= IORESOURCE_DISABLED;
    156		pnp_dbg(&dev->dev, "  irq %d disabled\n", idx);
    157		goto __add;
    158	}
    159
    160	/* TBD: need check for >16 IRQ */
    161	res->start = find_next_bit(rule->map.bits, PNP_IRQ_NR, 16);
    162	if (res->start < PNP_IRQ_NR) {
    163		res->end = res->start;
    164		goto __add;
    165	}
    166	for (i = 0; i < 16; i++) {
    167		if (test_bit(xtab[i], rule->map.bits)) {
    168			res->start = res->end = xtab[i];
    169			if (pnp_check_irq(dev, res))
    170				goto __add;
    171		}
    172	}
    173
    174	if (rule->flags & IORESOURCE_IRQ_OPTIONAL) {
    175		res->start = -1;
    176		res->end = -1;
    177		res->flags |= IORESOURCE_DISABLED;
    178		pnp_dbg(&dev->dev, "  irq %d disabled (optional)\n", idx);
    179		goto __add;
    180	}
    181
    182	pnp_dbg(&dev->dev, "  couldn't assign irq %d\n", idx);
    183	return -EBUSY;
    184
    185__add:
    186	pnp_add_irq_resource(dev, res->start, res->flags);
    187	return 0;
    188}
    189
    190#ifdef CONFIG_ISA_DMA_API
    191static int pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx)
    192{
    193	struct resource *res, local_res;
    194	int i;
    195
    196	/* DMA priority: this table is good for i386 */
    197	static unsigned short xtab[8] = {
    198		1, 3, 5, 6, 7, 0, 2, 4
    199	};
    200
    201	res = pnp_find_resource(dev, rule->flags, IORESOURCE_DMA, idx);
    202	if (res) {
    203		pnp_dbg(&dev->dev, "  dma %d already set to %d flags %#lx\n",
    204			idx, (int) res->start, res->flags);
    205		return 0;
    206	}
    207
    208	res = &local_res;
    209	res->flags = rule->flags | IORESOURCE_AUTO;
    210	res->start = -1;
    211	res->end = -1;
    212
    213	if (!rule->map) {
    214		res->flags |= IORESOURCE_DISABLED;
    215		pnp_dbg(&dev->dev, "  dma %d disabled\n", idx);
    216		goto __add;
    217	}
    218
    219	for (i = 0; i < 8; i++) {
    220		if (rule->map & (1 << xtab[i])) {
    221			res->start = res->end = xtab[i];
    222			if (pnp_check_dma(dev, res))
    223				goto __add;
    224		}
    225	}
    226
    227	pnp_dbg(&dev->dev, "  couldn't assign dma %d\n", idx);
    228	return -EBUSY;
    229
    230__add:
    231	pnp_add_dma_resource(dev, res->start, res->flags);
    232	return 0;
    233}
    234#endif /* CONFIG_ISA_DMA_API */
    235
    236void pnp_init_resources(struct pnp_dev *dev)
    237{
    238	pnp_free_resources(dev);
    239}
    240
    241static void pnp_clean_resource_table(struct pnp_dev *dev)
    242{
    243	struct pnp_resource *pnp_res, *tmp;
    244
    245	list_for_each_entry_safe(pnp_res, tmp, &dev->resources, list) {
    246		if (pnp_res->res.flags & IORESOURCE_AUTO)
    247			pnp_free_resource(pnp_res);
    248	}
    249}
    250
    251/**
    252 * pnp_assign_resources - assigns resources to the device based on the specified dependent number
    253 * @dev: pointer to the desired device
    254 * @set: the dependent function number
    255 */
    256static int pnp_assign_resources(struct pnp_dev *dev, int set)
    257{
    258	struct pnp_option *option;
    259	int nport = 0, nmem = 0, nirq = 0;
    260	int ndma __maybe_unused = 0;
    261	int ret = 0;
    262
    263	pnp_dbg(&dev->dev, "pnp_assign_resources, try dependent set %d\n", set);
    264	mutex_lock(&pnp_res_mutex);
    265	pnp_clean_resource_table(dev);
    266
    267	list_for_each_entry(option, &dev->options, list) {
    268		if (pnp_option_is_dependent(option) &&
    269		    pnp_option_set(option) != set)
    270				continue;
    271
    272		switch (option->type) {
    273		case IORESOURCE_IO:
    274			ret = pnp_assign_port(dev, &option->u.port, nport++);
    275			break;
    276		case IORESOURCE_MEM:
    277			ret = pnp_assign_mem(dev, &option->u.mem, nmem++);
    278			break;
    279		case IORESOURCE_IRQ:
    280			ret = pnp_assign_irq(dev, &option->u.irq, nirq++);
    281			break;
    282#ifdef CONFIG_ISA_DMA_API
    283		case IORESOURCE_DMA:
    284			ret = pnp_assign_dma(dev, &option->u.dma, ndma++);
    285			break;
    286#endif
    287		default:
    288			ret = -EINVAL;
    289			break;
    290		}
    291		if (ret < 0)
    292			break;
    293	}
    294
    295	mutex_unlock(&pnp_res_mutex);
    296	if (ret < 0) {
    297		pnp_dbg(&dev->dev, "pnp_assign_resources failed (%d)\n", ret);
    298		pnp_clean_resource_table(dev);
    299	} else
    300		dbg_pnp_show_resources(dev, "pnp_assign_resources succeeded");
    301	return ret;
    302}
    303
    304/**
    305 * pnp_auto_config_dev - automatically assigns resources to a device
    306 * @dev: pointer to the desired device
    307 */
    308int pnp_auto_config_dev(struct pnp_dev *dev)
    309{
    310	int i, ret;
    311
    312	if (!pnp_can_configure(dev)) {
    313		pnp_dbg(&dev->dev, "configuration not supported\n");
    314		return -ENODEV;
    315	}
    316
    317	ret = pnp_assign_resources(dev, 0);
    318	if (ret == 0)
    319		return 0;
    320
    321	for (i = 1; i < dev->num_dependent_sets; i++) {
    322		ret = pnp_assign_resources(dev, i);
    323		if (ret == 0)
    324			return 0;
    325	}
    326
    327	dev_err(&dev->dev, "unable to assign resources\n");
    328	return ret;
    329}
    330
    331/**
    332 * pnp_start_dev - low-level start of the PnP device
    333 * @dev: pointer to the desired device
    334 *
    335 * assumes that resources have already been allocated
    336 */
    337int pnp_start_dev(struct pnp_dev *dev)
    338{
    339	if (!pnp_can_write(dev)) {
    340		pnp_dbg(&dev->dev, "activation not supported\n");
    341		return -EINVAL;
    342	}
    343
    344	dbg_pnp_show_resources(dev, "pnp_start_dev");
    345	if (dev->protocol->set(dev) < 0) {
    346		dev_err(&dev->dev, "activation failed\n");
    347		return -EIO;
    348	}
    349
    350	dev_info(&dev->dev, "activated\n");
    351	return 0;
    352}
    353EXPORT_SYMBOL(pnp_start_dev);
    354
    355/**
    356 * pnp_stop_dev - low-level disable of the PnP device
    357 * @dev: pointer to the desired device
    358 *
    359 * does not free resources
    360 */
    361int pnp_stop_dev(struct pnp_dev *dev)
    362{
    363	if (!pnp_can_disable(dev)) {
    364		pnp_dbg(&dev->dev, "disabling not supported\n");
    365		return -EINVAL;
    366	}
    367	if (dev->protocol->disable(dev) < 0) {
    368		dev_err(&dev->dev, "disable failed\n");
    369		return -EIO;
    370	}
    371
    372	dev_info(&dev->dev, "disabled\n");
    373	return 0;
    374}
    375EXPORT_SYMBOL(pnp_stop_dev);
    376
    377/**
    378 * pnp_activate_dev - activates a PnP device for use
    379 * @dev: pointer to the desired device
    380 *
    381 * does not validate or set resources so be careful.
    382 */
    383int pnp_activate_dev(struct pnp_dev *dev)
    384{
    385	int error;
    386
    387	if (dev->active)
    388		return 0;
    389
    390	/* ensure resources are allocated */
    391	if (pnp_auto_config_dev(dev))
    392		return -EBUSY;
    393
    394	error = pnp_start_dev(dev);
    395	if (error)
    396		return error;
    397
    398	dev->active = 1;
    399	return 0;
    400}
    401EXPORT_SYMBOL(pnp_activate_dev);
    402
    403/**
    404 * pnp_disable_dev - disables device
    405 * @dev: pointer to the desired device
    406 *
    407 * inform the correct pnp protocol so that resources can be used by other devices
    408 */
    409int pnp_disable_dev(struct pnp_dev *dev)
    410{
    411	int error;
    412
    413	if (!dev->active)
    414		return 0;
    415
    416	error = pnp_stop_dev(dev);
    417	if (error)
    418		return error;
    419
    420	dev->active = 0;
    421
    422	/* release the resources so that other devices can use them */
    423	mutex_lock(&pnp_res_mutex);
    424	pnp_clean_resource_table(dev);
    425	mutex_unlock(&pnp_res_mutex);
    426
    427	return 0;
    428}
    429EXPORT_SYMBOL(pnp_disable_dev);