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

resource.c (17122B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * resource.c - Contains functions for registering and analyzing resource information
      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/module.h>
     12#include <linux/slab.h>
     13#include <linux/errno.h>
     14#include <linux/interrupt.h>
     15#include <linux/kernel.h>
     16#include <asm/io.h>
     17#include <asm/dma.h>
     18#include <asm/irq.h>
     19#include <linux/pci.h>
     20#include <linux/ioport.h>
     21#include <linux/init.h>
     22
     23#include <linux/pnp.h>
     24#include "base.h"
     25
     26static int pnp_reserve_irq[16] = {[0 ... 15] = -1 };	/* reserve (don't use) some IRQ */
     27static int pnp_reserve_dma[8] = {[0 ... 7] = -1 };	/* reserve (don't use) some DMA */
     28static int pnp_reserve_io[16] = {[0 ... 15] = -1 };	/* reserve (don't use) some I/O region */
     29static int pnp_reserve_mem[16] = {[0 ... 15] = -1 };	/* reserve (don't use) some memory region */
     30
     31/*
     32 * option registration
     33 */
     34
     35static struct pnp_option *pnp_build_option(struct pnp_dev *dev, unsigned long type,
     36				    unsigned int option_flags)
     37{
     38	struct pnp_option *option;
     39
     40	option = kzalloc(sizeof(struct pnp_option), GFP_KERNEL);
     41	if (!option)
     42		return NULL;
     43
     44	option->flags = option_flags;
     45	option->type = type;
     46
     47	list_add_tail(&option->list, &dev->options);
     48	return option;
     49}
     50
     51int pnp_register_irq_resource(struct pnp_dev *dev, unsigned int option_flags,
     52			      pnp_irq_mask_t *map, unsigned char flags)
     53{
     54	struct pnp_option *option;
     55	struct pnp_irq *irq;
     56
     57	option = pnp_build_option(dev, IORESOURCE_IRQ, option_flags);
     58	if (!option)
     59		return -ENOMEM;
     60
     61	irq = &option->u.irq;
     62	irq->map = *map;
     63	irq->flags = flags;
     64
     65#ifdef CONFIG_PCI
     66	{
     67		int i;
     68
     69		for (i = 0; i < 16; i++)
     70			if (test_bit(i, irq->map.bits))
     71				pcibios_penalize_isa_irq(i, 0);
     72	}
     73#endif
     74
     75	dbg_pnp_show_option(dev, option);
     76	return 0;
     77}
     78
     79int pnp_register_dma_resource(struct pnp_dev *dev, unsigned int option_flags,
     80			      unsigned char map, unsigned char flags)
     81{
     82	struct pnp_option *option;
     83	struct pnp_dma *dma;
     84
     85	option = pnp_build_option(dev, IORESOURCE_DMA, option_flags);
     86	if (!option)
     87		return -ENOMEM;
     88
     89	dma = &option->u.dma;
     90	dma->map = map;
     91	dma->flags = flags;
     92
     93	dbg_pnp_show_option(dev, option);
     94	return 0;
     95}
     96
     97int pnp_register_port_resource(struct pnp_dev *dev, unsigned int option_flags,
     98			       resource_size_t min, resource_size_t max,
     99			       resource_size_t align, resource_size_t size,
    100			       unsigned char flags)
    101{
    102	struct pnp_option *option;
    103	struct pnp_port *port;
    104
    105	option = pnp_build_option(dev, IORESOURCE_IO, option_flags);
    106	if (!option)
    107		return -ENOMEM;
    108
    109	port = &option->u.port;
    110	port->min = min;
    111	port->max = max;
    112	port->align = align;
    113	port->size = size;
    114	port->flags = flags;
    115
    116	dbg_pnp_show_option(dev, option);
    117	return 0;
    118}
    119
    120int pnp_register_mem_resource(struct pnp_dev *dev, unsigned int option_flags,
    121			      resource_size_t min, resource_size_t max,
    122			      resource_size_t align, resource_size_t size,
    123			      unsigned char flags)
    124{
    125	struct pnp_option *option;
    126	struct pnp_mem *mem;
    127
    128	option = pnp_build_option(dev, IORESOURCE_MEM, option_flags);
    129	if (!option)
    130		return -ENOMEM;
    131
    132	mem = &option->u.mem;
    133	mem->min = min;
    134	mem->max = max;
    135	mem->align = align;
    136	mem->size = size;
    137	mem->flags = flags;
    138
    139	dbg_pnp_show_option(dev, option);
    140	return 0;
    141}
    142
    143void pnp_free_options(struct pnp_dev *dev)
    144{
    145	struct pnp_option *option, *tmp;
    146
    147	list_for_each_entry_safe(option, tmp, &dev->options, list) {
    148		list_del(&option->list);
    149		kfree(option);
    150	}
    151}
    152
    153/*
    154 * resource validity checking
    155 */
    156
    157#define length(start, end) (*(end) - *(start) + 1)
    158
    159/* Two ranges conflict if one doesn't end before the other starts */
    160#define ranged_conflict(starta, enda, startb, endb) \
    161	!((*(enda) < *(startb)) || (*(endb) < *(starta)))
    162
    163#define cannot_compare(flags) \
    164((flags) & IORESOURCE_DISABLED)
    165
    166int pnp_check_port(struct pnp_dev *dev, struct resource *res)
    167{
    168	int i;
    169	struct pnp_dev *tdev;
    170	struct resource *tres;
    171	resource_size_t *port, *end, *tport, *tend;
    172
    173	port = &res->start;
    174	end = &res->end;
    175
    176	/* if the resource doesn't exist, don't complain about it */
    177	if (cannot_compare(res->flags))
    178		return 1;
    179
    180	/* check if the resource is already in use, skip if the
    181	 * device is active because it itself may be in use */
    182	if (!dev->active) {
    183		if (!request_region(*port, length(port, end), "pnp"))
    184			return 0;
    185		release_region(*port, length(port, end));
    186	}
    187
    188	/* check if the resource is reserved */
    189	for (i = 0; i < 8; i++) {
    190		int rport = pnp_reserve_io[i << 1];
    191		int rend = pnp_reserve_io[(i << 1) + 1] + rport - 1;
    192		if (ranged_conflict(port, end, &rport, &rend))
    193			return 0;
    194	}
    195
    196	/* check for internal conflicts */
    197	for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_IO, i)); i++) {
    198		if (tres != res && tres->flags & IORESOURCE_IO) {
    199			tport = &tres->start;
    200			tend = &tres->end;
    201			if (ranged_conflict(port, end, tport, tend))
    202				return 0;
    203		}
    204	}
    205
    206	/* check for conflicts with other pnp devices */
    207	pnp_for_each_dev(tdev) {
    208		if (tdev == dev)
    209			continue;
    210		for (i = 0;
    211		     (tres = pnp_get_resource(tdev, IORESOURCE_IO, i));
    212		     i++) {
    213			if (tres->flags & IORESOURCE_IO) {
    214				if (cannot_compare(tres->flags))
    215					continue;
    216				if (tres->flags & IORESOURCE_WINDOW)
    217					continue;
    218				tport = &tres->start;
    219				tend = &tres->end;
    220				if (ranged_conflict(port, end, tport, tend))
    221					return 0;
    222			}
    223		}
    224	}
    225
    226	return 1;
    227}
    228
    229int pnp_check_mem(struct pnp_dev *dev, struct resource *res)
    230{
    231	int i;
    232	struct pnp_dev *tdev;
    233	struct resource *tres;
    234	resource_size_t *addr, *end, *taddr, *tend;
    235
    236	addr = &res->start;
    237	end = &res->end;
    238
    239	/* if the resource doesn't exist, don't complain about it */
    240	if (cannot_compare(res->flags))
    241		return 1;
    242
    243	/* check if the resource is already in use, skip if the
    244	 * device is active because it itself may be in use */
    245	if (!dev->active) {
    246		if (!request_mem_region(*addr, length(addr, end), "pnp"))
    247			return 0;
    248		release_mem_region(*addr, length(addr, end));
    249	}
    250
    251	/* check if the resource is reserved */
    252	for (i = 0; i < 8; i++) {
    253		int raddr = pnp_reserve_mem[i << 1];
    254		int rend = pnp_reserve_mem[(i << 1) + 1] + raddr - 1;
    255		if (ranged_conflict(addr, end, &raddr, &rend))
    256			return 0;
    257	}
    258
    259	/* check for internal conflicts */
    260	for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_MEM, i)); i++) {
    261		if (tres != res && tres->flags & IORESOURCE_MEM) {
    262			taddr = &tres->start;
    263			tend = &tres->end;
    264			if (ranged_conflict(addr, end, taddr, tend))
    265				return 0;
    266		}
    267	}
    268
    269	/* check for conflicts with other pnp devices */
    270	pnp_for_each_dev(tdev) {
    271		if (tdev == dev)
    272			continue;
    273		for (i = 0;
    274		     (tres = pnp_get_resource(tdev, IORESOURCE_MEM, i));
    275		     i++) {
    276			if (tres->flags & IORESOURCE_MEM) {
    277				if (cannot_compare(tres->flags))
    278					continue;
    279				if (tres->flags & IORESOURCE_WINDOW)
    280					continue;
    281				taddr = &tres->start;
    282				tend = &tres->end;
    283				if (ranged_conflict(addr, end, taddr, tend))
    284					return 0;
    285			}
    286		}
    287	}
    288
    289	return 1;
    290}
    291
    292static irqreturn_t pnp_test_handler(int irq, void *dev_id)
    293{
    294	return IRQ_HANDLED;
    295}
    296
    297#ifdef CONFIG_PCI
    298static int pci_dev_uses_irq(struct pnp_dev *pnp, struct pci_dev *pci,
    299			    unsigned int irq)
    300{
    301	u32 class;
    302	u8 progif;
    303
    304	if (pci->irq == irq) {
    305		pnp_dbg(&pnp->dev, "  device %s using irq %d\n",
    306			pci_name(pci), irq);
    307		return 1;
    308	}
    309
    310	/*
    311	 * See pci_setup_device() and ata_pci_sff_activate_host() for
    312	 * similar IDE legacy detection.
    313	 */
    314	pci_read_config_dword(pci, PCI_CLASS_REVISION, &class);
    315	class >>= 8;		/* discard revision ID */
    316	progif = class & 0xff;
    317	class >>= 8;
    318
    319	if (class == PCI_CLASS_STORAGE_IDE) {
    320		/*
    321		 * Unless both channels are native-PCI mode only,
    322		 * treat the compatibility IRQs as busy.
    323		 */
    324		if ((progif & 0x5) != 0x5)
    325			if (pci_get_legacy_ide_irq(pci, 0) == irq ||
    326			    pci_get_legacy_ide_irq(pci, 1) == irq) {
    327				pnp_dbg(&pnp->dev, "  legacy IDE device %s "
    328					"using irq %d\n", pci_name(pci), irq);
    329				return 1;
    330			}
    331	}
    332
    333	return 0;
    334}
    335#endif
    336
    337static int pci_uses_irq(struct pnp_dev *pnp, unsigned int irq)
    338{
    339#ifdef CONFIG_PCI
    340	struct pci_dev *pci = NULL;
    341
    342	for_each_pci_dev(pci) {
    343		if (pci_dev_uses_irq(pnp, pci, irq)) {
    344			pci_dev_put(pci);
    345			return 1;
    346		}
    347	}
    348#endif
    349	return 0;
    350}
    351
    352int pnp_check_irq(struct pnp_dev *dev, struct resource *res)
    353{
    354	int i;
    355	struct pnp_dev *tdev;
    356	struct resource *tres;
    357	resource_size_t *irq;
    358
    359	irq = &res->start;
    360
    361	/* if the resource doesn't exist, don't complain about it */
    362	if (cannot_compare(res->flags))
    363		return 1;
    364
    365	/* check if the resource is valid */
    366	if (*irq > 15)
    367		return 0;
    368
    369	/* check if the resource is reserved */
    370	for (i = 0; i < 16; i++) {
    371		if (pnp_reserve_irq[i] == *irq)
    372			return 0;
    373	}
    374
    375	/* check for internal conflicts */
    376	for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_IRQ, i)); i++) {
    377		if (tres != res && tres->flags & IORESOURCE_IRQ) {
    378			if (tres->start == *irq)
    379				return 0;
    380		}
    381	}
    382
    383	/* check if the resource is being used by a pci device */
    384	if (pci_uses_irq(dev, *irq))
    385		return 0;
    386
    387	/* check if the resource is already in use, skip if the
    388	 * device is active because it itself may be in use */
    389	if (!dev->active) {
    390		if (request_irq(*irq, pnp_test_handler,
    391				IRQF_PROBE_SHARED, "pnp", NULL))
    392			return 0;
    393		free_irq(*irq, NULL);
    394	}
    395
    396	/* check for conflicts with other pnp devices */
    397	pnp_for_each_dev(tdev) {
    398		if (tdev == dev)
    399			continue;
    400		for (i = 0;
    401		     (tres = pnp_get_resource(tdev, IORESOURCE_IRQ, i));
    402		     i++) {
    403			if (tres->flags & IORESOURCE_IRQ) {
    404				if (cannot_compare(tres->flags))
    405					continue;
    406				if (tres->start == *irq)
    407					return 0;
    408			}
    409		}
    410	}
    411
    412	return 1;
    413}
    414
    415#ifdef CONFIG_ISA_DMA_API
    416int pnp_check_dma(struct pnp_dev *dev, struct resource *res)
    417{
    418	int i;
    419	struct pnp_dev *tdev;
    420	struct resource *tres;
    421	resource_size_t *dma;
    422
    423	dma = &res->start;
    424
    425	/* if the resource doesn't exist, don't complain about it */
    426	if (cannot_compare(res->flags))
    427		return 1;
    428
    429	/* check if the resource is valid */
    430	if (*dma == 4 || *dma > 7)
    431		return 0;
    432
    433	/* check if the resource is reserved */
    434	for (i = 0; i < 8; i++) {
    435		if (pnp_reserve_dma[i] == *dma)
    436			return 0;
    437	}
    438
    439	/* check for internal conflicts */
    440	for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_DMA, i)); i++) {
    441		if (tres != res && tres->flags & IORESOURCE_DMA) {
    442			if (tres->start == *dma)
    443				return 0;
    444		}
    445	}
    446
    447	/* check if the resource is already in use, skip if the
    448	 * device is active because it itself may be in use */
    449	if (!dev->active) {
    450		if (request_dma(*dma, "pnp"))
    451			return 0;
    452		free_dma(*dma);
    453	}
    454
    455	/* check for conflicts with other pnp devices */
    456	pnp_for_each_dev(tdev) {
    457		if (tdev == dev)
    458			continue;
    459		for (i = 0;
    460		     (tres = pnp_get_resource(tdev, IORESOURCE_DMA, i));
    461		     i++) {
    462			if (tres->flags & IORESOURCE_DMA) {
    463				if (cannot_compare(tres->flags))
    464					continue;
    465				if (tres->start == *dma)
    466					return 0;
    467			}
    468		}
    469	}
    470
    471	return 1;
    472}
    473#endif /* CONFIG_ISA_DMA_API */
    474
    475unsigned long pnp_resource_type(struct resource *res)
    476{
    477	return res->flags & (IORESOURCE_IO  | IORESOURCE_MEM |
    478			     IORESOURCE_IRQ | IORESOURCE_DMA |
    479			     IORESOURCE_BUS);
    480}
    481
    482struct resource *pnp_get_resource(struct pnp_dev *dev,
    483				  unsigned long type, unsigned int num)
    484{
    485	struct pnp_resource *pnp_res;
    486	struct resource *res;
    487
    488	list_for_each_entry(pnp_res, &dev->resources, list) {
    489		res = &pnp_res->res;
    490		if (pnp_resource_type(res) == type && num-- == 0)
    491			return res;
    492	}
    493	return NULL;
    494}
    495EXPORT_SYMBOL(pnp_get_resource);
    496
    497static struct pnp_resource *pnp_new_resource(struct pnp_dev *dev)
    498{
    499	struct pnp_resource *pnp_res;
    500
    501	pnp_res = kzalloc(sizeof(struct pnp_resource), GFP_KERNEL);
    502	if (!pnp_res)
    503		return NULL;
    504
    505	list_add_tail(&pnp_res->list, &dev->resources);
    506	return pnp_res;
    507}
    508
    509struct pnp_resource *pnp_add_resource(struct pnp_dev *dev,
    510				      struct resource *res)
    511{
    512	struct pnp_resource *pnp_res;
    513
    514	pnp_res = pnp_new_resource(dev);
    515	if (!pnp_res) {
    516		dev_err(&dev->dev, "can't add resource %pR\n", res);
    517		return NULL;
    518	}
    519
    520	pnp_res->res = *res;
    521	pnp_res->res.name = dev->name;
    522	dev_dbg(&dev->dev, "%pR\n", res);
    523	return pnp_res;
    524}
    525
    526struct pnp_resource *pnp_add_irq_resource(struct pnp_dev *dev, int irq,
    527					  int flags)
    528{
    529	struct pnp_resource *pnp_res;
    530	struct resource *res;
    531
    532	pnp_res = pnp_new_resource(dev);
    533	if (!pnp_res) {
    534		dev_err(&dev->dev, "can't add resource for IRQ %d\n", irq);
    535		return NULL;
    536	}
    537
    538	res = &pnp_res->res;
    539	res->flags = IORESOURCE_IRQ | flags;
    540	res->start = irq;
    541	res->end = irq;
    542
    543	dev_dbg(&dev->dev, "%pR\n", res);
    544	return pnp_res;
    545}
    546
    547struct pnp_resource *pnp_add_dma_resource(struct pnp_dev *dev, int dma,
    548					  int flags)
    549{
    550	struct pnp_resource *pnp_res;
    551	struct resource *res;
    552
    553	pnp_res = pnp_new_resource(dev);
    554	if (!pnp_res) {
    555		dev_err(&dev->dev, "can't add resource for DMA %d\n", dma);
    556		return NULL;
    557	}
    558
    559	res = &pnp_res->res;
    560	res->flags = IORESOURCE_DMA | flags;
    561	res->start = dma;
    562	res->end = dma;
    563
    564	dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res);
    565	return pnp_res;
    566}
    567
    568struct pnp_resource *pnp_add_io_resource(struct pnp_dev *dev,
    569					 resource_size_t start,
    570					 resource_size_t end, int flags)
    571{
    572	struct pnp_resource *pnp_res;
    573	struct resource *res;
    574
    575	pnp_res = pnp_new_resource(dev);
    576	if (!pnp_res) {
    577		dev_err(&dev->dev, "can't add resource for IO %#llx-%#llx\n",
    578			(unsigned long long) start,
    579			(unsigned long long) end);
    580		return NULL;
    581	}
    582
    583	res = &pnp_res->res;
    584	res->flags = IORESOURCE_IO | flags;
    585	res->start = start;
    586	res->end = end;
    587
    588	dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res);
    589	return pnp_res;
    590}
    591
    592struct pnp_resource *pnp_add_mem_resource(struct pnp_dev *dev,
    593					  resource_size_t start,
    594					  resource_size_t end, int flags)
    595{
    596	struct pnp_resource *pnp_res;
    597	struct resource *res;
    598
    599	pnp_res = pnp_new_resource(dev);
    600	if (!pnp_res) {
    601		dev_err(&dev->dev, "can't add resource for MEM %#llx-%#llx\n",
    602			(unsigned long long) start,
    603			(unsigned long long) end);
    604		return NULL;
    605	}
    606
    607	res = &pnp_res->res;
    608	res->flags = IORESOURCE_MEM | flags;
    609	res->start = start;
    610	res->end = end;
    611
    612	dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res);
    613	return pnp_res;
    614}
    615
    616struct pnp_resource *pnp_add_bus_resource(struct pnp_dev *dev,
    617					  resource_size_t start,
    618					  resource_size_t end)
    619{
    620	struct pnp_resource *pnp_res;
    621	struct resource *res;
    622
    623	pnp_res = pnp_new_resource(dev);
    624	if (!pnp_res) {
    625		dev_err(&dev->dev, "can't add resource for BUS %#llx-%#llx\n",
    626			(unsigned long long) start,
    627			(unsigned long long) end);
    628		return NULL;
    629	}
    630
    631	res = &pnp_res->res;
    632	res->flags = IORESOURCE_BUS;
    633	res->start = start;
    634	res->end = end;
    635
    636	dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res);
    637	return pnp_res;
    638}
    639
    640/*
    641 * Determine whether the specified resource is a possible configuration
    642 * for this device.
    643 */
    644int pnp_possible_config(struct pnp_dev *dev, int type, resource_size_t start,
    645			resource_size_t size)
    646{
    647	struct pnp_option *option;
    648	struct pnp_port *port;
    649	struct pnp_mem *mem;
    650	struct pnp_irq *irq;
    651	struct pnp_dma *dma;
    652
    653	list_for_each_entry(option, &dev->options, list) {
    654		if (option->type != type)
    655			continue;
    656
    657		switch (option->type) {
    658		case IORESOURCE_IO:
    659			port = &option->u.port;
    660			if (port->min == start && port->size == size)
    661				return 1;
    662			break;
    663		case IORESOURCE_MEM:
    664			mem = &option->u.mem;
    665			if (mem->min == start && mem->size == size)
    666				return 1;
    667			break;
    668		case IORESOURCE_IRQ:
    669			irq = &option->u.irq;
    670			if (start < PNP_IRQ_NR &&
    671			    test_bit(start, irq->map.bits))
    672				return 1;
    673			break;
    674		case IORESOURCE_DMA:
    675			dma = &option->u.dma;
    676			if (dma->map & (1 << start))
    677				return 1;
    678			break;
    679		}
    680	}
    681
    682	return 0;
    683}
    684EXPORT_SYMBOL(pnp_possible_config);
    685
    686int pnp_range_reserved(resource_size_t start, resource_size_t end)
    687{
    688	struct pnp_dev *dev;
    689	struct pnp_resource *pnp_res;
    690	resource_size_t *dev_start, *dev_end;
    691
    692	pnp_for_each_dev(dev) {
    693		list_for_each_entry(pnp_res, &dev->resources, list) {
    694			dev_start = &pnp_res->res.start;
    695			dev_end   = &pnp_res->res.end;
    696			if (ranged_conflict(&start, &end, dev_start, dev_end))
    697				return 1;
    698		}
    699	}
    700	return 0;
    701}
    702EXPORT_SYMBOL(pnp_range_reserved);
    703
    704/* format is: pnp_reserve_irq=irq1[,irq2] .... */
    705static int __init pnp_setup_reserve_irq(char *str)
    706{
    707	int i;
    708
    709	for (i = 0; i < 16; i++)
    710		if (get_option(&str, &pnp_reserve_irq[i]) != 2)
    711			break;
    712	return 1;
    713}
    714
    715__setup("pnp_reserve_irq=", pnp_setup_reserve_irq);
    716
    717/* format is: pnp_reserve_dma=dma1[,dma2] .... */
    718static int __init pnp_setup_reserve_dma(char *str)
    719{
    720	int i;
    721
    722	for (i = 0; i < 8; i++)
    723		if (get_option(&str, &pnp_reserve_dma[i]) != 2)
    724			break;
    725	return 1;
    726}
    727
    728__setup("pnp_reserve_dma=", pnp_setup_reserve_dma);
    729
    730/* format is: pnp_reserve_io=io1,size1[,io2,size2] .... */
    731static int __init pnp_setup_reserve_io(char *str)
    732{
    733	int i;
    734
    735	for (i = 0; i < 16; i++)
    736		if (get_option(&str, &pnp_reserve_io[i]) != 2)
    737			break;
    738	return 1;
    739}
    740
    741__setup("pnp_reserve_io=", pnp_setup_reserve_io);
    742
    743/* format is: pnp_reserve_mem=mem1,size1[,mem2,size2] .... */
    744static int __init pnp_setup_reserve_mem(char *str)
    745{
    746	int i;
    747
    748	for (i = 0; i < 16; i++)
    749		if (get_option(&str, &pnp_reserve_mem[i]) != 2)
    750			break;
    751	return 1;
    752}
    753
    754__setup("pnp_reserve_mem=", pnp_setup_reserve_mem);