brintos

brintos / linux-shallow public Read only

0
0
Text · 16.7 KiB · 70af782 Raw
811 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * rsparser.c - parses and encodes pnpbios resource data streams4 */5 6#include <linux/ctype.h>7#include <linux/pnp.h>8#include <linux/string.h>9 10#ifdef CONFIG_PCI11#include <linux/pci.h>12#else13inline void pcibios_penalize_isa_irq(int irq, int active)14{15}16#endif				/* CONFIG_PCI */17 18#include "../base.h"19#include "pnpbios.h"20 21/* standard resource tags */22#define SMALL_TAG_PNPVERNO		0x0123#define SMALL_TAG_LOGDEVID		0x0224#define SMALL_TAG_COMPATDEVID		0x0325#define SMALL_TAG_IRQ			0x0426#define SMALL_TAG_DMA			0x0527#define SMALL_TAG_STARTDEP		0x0628#define SMALL_TAG_ENDDEP		0x0729#define SMALL_TAG_PORT			0x0830#define SMALL_TAG_FIXEDPORT		0x0931#define SMALL_TAG_VENDOR		0x0e32#define SMALL_TAG_END			0x0f33#define LARGE_TAG			0x8034#define LARGE_TAG_MEM			0x8135#define LARGE_TAG_ANSISTR		0x8236#define LARGE_TAG_UNICODESTR		0x8337#define LARGE_TAG_VENDOR		0x8438#define LARGE_TAG_MEM32			0x8539#define LARGE_TAG_FIXEDMEM32		0x8640 41/*42 * Resource Data Stream Format:43 *44 * Allocated Resources (required)45 * end tag ->46 * Resource Configuration Options (optional)47 * end tag ->48 * Compitable Device IDs (optional)49 * final end tag ->50 */51 52/*53 * Allocated Resources54 */55 56static void pnpbios_parse_allocated_ioresource(struct pnp_dev *dev,57					       int start, int len)58{59	int flags = 0;60	int end = start + len - 1;61 62	if (len <= 0 || end >= 0x10003)63		flags |= IORESOURCE_DISABLED;64 65	pnp_add_io_resource(dev, start, end, flags);66}67 68static void pnpbios_parse_allocated_memresource(struct pnp_dev *dev,69						int start, int len)70{71	int flags = 0;72	int end = start + len - 1;73 74	if (len <= 0)75		flags |= IORESOURCE_DISABLED;76 77	pnp_add_mem_resource(dev, start, end, flags);78}79 80static unsigned char *pnpbios_parse_allocated_resource_data(struct pnp_dev *dev,81							    unsigned char *p,82							    unsigned char *end)83{84	unsigned int len, tag;85	int io, size, mask, i, flags;86 87	if (!p)88		return NULL;89 90	pnp_dbg(&dev->dev, "parse allocated resources\n");91 92	pnp_init_resources(dev);93 94	while ((char *)p < (char *)end) {95 96		/* determine the type of tag */97		if (p[0] & LARGE_TAG) {	/* large tag */98			len = (p[2] << 8) | p[1];99			tag = p[0];100		} else {	/* small tag */101			len = p[0] & 0x07;102			tag = ((p[0] >> 3) & 0x0f);103		}104 105		switch (tag) {106 107		case LARGE_TAG_MEM:108			if (len != 9)109				goto len_err;110			io = *(short *)&p[4];111			size = *(short *)&p[10];112			pnpbios_parse_allocated_memresource(dev, io, size);113			break;114 115		case LARGE_TAG_ANSISTR:116			/* ignore this for now */117			break;118 119		case LARGE_TAG_VENDOR:120			/* do nothing */121			break;122 123		case LARGE_TAG_MEM32:124			if (len != 17)125				goto len_err;126			io = *(int *)&p[4];127			size = *(int *)&p[16];128			pnpbios_parse_allocated_memresource(dev, io, size);129			break;130 131		case LARGE_TAG_FIXEDMEM32:132			if (len != 9)133				goto len_err;134			io = *(int *)&p[4];135			size = *(int *)&p[8];136			pnpbios_parse_allocated_memresource(dev, io, size);137			break;138 139		case SMALL_TAG_IRQ:140			if (len < 2 || len > 3)141				goto len_err;142			flags = 0;143			io = -1;144			mask = p[1] + p[2] * 256;145			for (i = 0; i < 16; i++, mask = mask >> 1)146				if (mask & 0x01)147					io = i;148			if (io != -1)149				pcibios_penalize_isa_irq(io, 1);150			else151				flags = IORESOURCE_DISABLED;152			pnp_add_irq_resource(dev, io, flags);153			break;154 155		case SMALL_TAG_DMA:156			if (len != 2)157				goto len_err;158			flags = 0;159			io = -1;160			mask = p[1];161			for (i = 0; i < 8; i++, mask = mask >> 1)162				if (mask & 0x01)163					io = i;164			if (io == -1)165				flags = IORESOURCE_DISABLED;166			pnp_add_dma_resource(dev, io, flags);167			break;168 169		case SMALL_TAG_PORT:170			if (len != 7)171				goto len_err;172			io = p[2] + p[3] * 256;173			size = p[7];174			pnpbios_parse_allocated_ioresource(dev, io, size);175			break;176 177		case SMALL_TAG_VENDOR:178			/* do nothing */179			break;180 181		case SMALL_TAG_FIXEDPORT:182			if (len != 3)183				goto len_err;184			io = p[1] + p[2] * 256;185			size = p[3];186			pnpbios_parse_allocated_ioresource(dev, io, size);187			break;188 189		case SMALL_TAG_END:190			p = p + 2;191			return (unsigned char *)p;192			break;193 194		default:	/* an unknown tag */195len_err:196			dev_err(&dev->dev, "unknown tag %#x length %d\n",197				tag, len);198			break;199		}200 201		/* continue to the next tag */202		if (p[0] & LARGE_TAG)203			p += len + 3;204		else205			p += len + 1;206	}207 208	dev_err(&dev->dev, "no end tag in resource structure\n");209 210	return NULL;211}212 213/*214 * Resource Configuration Options215 */216 217static __init void pnpbios_parse_mem_option(struct pnp_dev *dev,218					    unsigned char *p, int size,219					    unsigned int option_flags)220{221	resource_size_t min, max, align, len;222	unsigned char flags;223 224	min = ((p[5] << 8) | p[4]) << 8;225	max = ((p[7] << 8) | p[6]) << 8;226	align = (p[9] << 8) | p[8];227	len = ((p[11] << 8) | p[10]) << 8;228	flags = p[3];229	pnp_register_mem_resource(dev, option_flags, min, max, align, len,230				  flags);231}232 233static __init void pnpbios_parse_mem32_option(struct pnp_dev *dev,234					      unsigned char *p, int size,235					      unsigned int option_flags)236{237	resource_size_t min, max, align, len;238	unsigned char flags;239 240	min = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4];241	max = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8];242	align = (p[15] << 24) | (p[14] << 16) | (p[13] << 8) | p[12];243	len = (p[19] << 24) | (p[18] << 16) | (p[17] << 8) | p[16];244	flags = p[3];245	pnp_register_mem_resource(dev, option_flags, min, max, align, len,246				  flags);247}248 249static __init void pnpbios_parse_fixed_mem32_option(struct pnp_dev *dev,250						    unsigned char *p, int size,251						    unsigned int option_flags)252{253	resource_size_t base, len;254	unsigned char flags;255 256	base = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4];257	len = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8];258	flags = p[3];259	pnp_register_mem_resource(dev, option_flags, base, base, 0, len, flags);260}261 262static __init void pnpbios_parse_irq_option(struct pnp_dev *dev,263					    unsigned char *p, int size,264					    unsigned int option_flags)265{266	unsigned long bits;267	pnp_irq_mask_t map;268	unsigned char flags = IORESOURCE_IRQ_HIGHEDGE;269 270	bits = (p[2] << 8) | p[1];271 272	bitmap_zero(map.bits, PNP_IRQ_NR);273	bitmap_copy(map.bits, &bits, 16);274 275	if (size > 2)276		flags = p[3];277 278	pnp_register_irq_resource(dev, option_flags, &map, flags);279}280 281static __init void pnpbios_parse_dma_option(struct pnp_dev *dev,282					    unsigned char *p, int size,283					    unsigned int option_flags)284{285	pnp_register_dma_resource(dev, option_flags, p[1], p[2]);286}287 288static __init void pnpbios_parse_port_option(struct pnp_dev *dev,289					     unsigned char *p, int size,290					     unsigned int option_flags)291{292	resource_size_t min, max, align, len;293	unsigned char flags;294 295	min = (p[3] << 8) | p[2];296	max = (p[5] << 8) | p[4];297	align = p[6];298	len = p[7];299	flags = p[1] ? IORESOURCE_IO_16BIT_ADDR : 0;300	pnp_register_port_resource(dev, option_flags, min, max, align, len,301				   flags);302}303 304static __init void pnpbios_parse_fixed_port_option(struct pnp_dev *dev,305						   unsigned char *p, int size,306						   unsigned int option_flags)307{308	resource_size_t base, len;309 310	base = (p[2] << 8) | p[1];311	len = p[3];312	pnp_register_port_resource(dev, option_flags, base, base, 0, len,313				   IORESOURCE_IO_FIXED);314}315 316static __init unsigned char *317pnpbios_parse_resource_option_data(unsigned char *p, unsigned char *end,318				   struct pnp_dev *dev)319{320	unsigned int len, tag;321	int priority;322	unsigned int option_flags;323 324	if (!p)325		return NULL;326 327	pnp_dbg(&dev->dev, "parse resource options\n");328	option_flags = 0;329	while ((char *)p < (char *)end) {330 331		/* determine the type of tag */332		if (p[0] & LARGE_TAG) {	/* large tag */333			len = (p[2] << 8) | p[1];334			tag = p[0];335		} else {	/* small tag */336			len = p[0] & 0x07;337			tag = ((p[0] >> 3) & 0x0f);338		}339 340		switch (tag) {341 342		case LARGE_TAG_MEM:343			if (len != 9)344				goto len_err;345			pnpbios_parse_mem_option(dev, p, len, option_flags);346			break;347 348		case LARGE_TAG_MEM32:349			if (len != 17)350				goto len_err;351			pnpbios_parse_mem32_option(dev, p, len, option_flags);352			break;353 354		case LARGE_TAG_FIXEDMEM32:355			if (len != 9)356				goto len_err;357			pnpbios_parse_fixed_mem32_option(dev, p, len,358							 option_flags);359			break;360 361		case SMALL_TAG_IRQ:362			if (len < 2 || len > 3)363				goto len_err;364			pnpbios_parse_irq_option(dev, p, len, option_flags);365			break;366 367		case SMALL_TAG_DMA:368			if (len != 2)369				goto len_err;370			pnpbios_parse_dma_option(dev, p, len, option_flags);371			break;372 373		case SMALL_TAG_PORT:374			if (len != 7)375				goto len_err;376			pnpbios_parse_port_option(dev, p, len, option_flags);377			break;378 379		case SMALL_TAG_VENDOR:380			/* do nothing */381			break;382 383		case SMALL_TAG_FIXEDPORT:384			if (len != 3)385				goto len_err;386			pnpbios_parse_fixed_port_option(dev, p, len,387							option_flags);388			break;389 390		case SMALL_TAG_STARTDEP:391			if (len > 1)392				goto len_err;393			priority = PNP_RES_PRIORITY_ACCEPTABLE;394			if (len > 0)395				priority = p[1];396			option_flags = pnp_new_dependent_set(dev, priority);397			break;398 399		case SMALL_TAG_ENDDEP:400			if (len != 0)401				goto len_err;402			option_flags = 0;403			break;404 405		case SMALL_TAG_END:406			return p + 2;407 408		default:	/* an unknown tag */409len_err:410			dev_err(&dev->dev, "unknown tag %#x length %d\n",411				tag, len);412			break;413		}414 415		/* continue to the next tag */416		if (p[0] & LARGE_TAG)417			p += len + 3;418		else419			p += len + 1;420	}421 422	dev_err(&dev->dev, "no end tag in resource structure\n");423 424	return NULL;425}426 427/*428 * Compatible Device IDs429 */430 431static unsigned char *pnpbios_parse_compatible_ids(unsigned char *p,432						   unsigned char *end,433						   struct pnp_dev *dev)434{435	int len, tag;436	u32 eisa_id;437	char id[8];438	struct pnp_id *dev_id;439 440	if (!p)441		return NULL;442 443	while ((char *)p < (char *)end) {444 445		/* determine the type of tag */446		if (p[0] & LARGE_TAG) {	/* large tag */447			len = (p[2] << 8) | p[1];448			tag = p[0];449		} else {	/* small tag */450			len = p[0] & 0x07;451			tag = ((p[0] >> 3) & 0x0f);452		}453 454		switch (tag) {455 456		case LARGE_TAG_ANSISTR:457			memcpy(dev->name, p + 3,458			       len >= PNP_NAME_LEN ? PNP_NAME_LEN - 2 : len);459			dev->name[len >=460				  PNP_NAME_LEN ? PNP_NAME_LEN - 1 : len] = '\0';461			break;462 463		case SMALL_TAG_COMPATDEVID:	/* compatible ID */464			if (len != 4)465				goto len_err;466			eisa_id = p[1] | p[2] << 8 | p[3] << 16 | p[4] << 24;467			pnp_eisa_id_to_string(eisa_id & PNP_EISA_ID_MASK, id);468			dev_id = pnp_add_id(dev, id);469			if (!dev_id)470				return NULL;471			break;472 473		case SMALL_TAG_END:474			p = p + 2;475			return (unsigned char *)p;476			break;477 478		default:	/* an unknown tag */479len_err:480			dev_err(&dev->dev, "unknown tag %#x length %d\n",481				tag, len);482			break;483		}484 485		/* continue to the next tag */486		if (p[0] & LARGE_TAG)487			p += len + 3;488		else489			p += len + 1;490	}491 492	dev_err(&dev->dev, "no end tag in resource structure\n");493 494	return NULL;495}496 497/*498 * Allocated Resource Encoding499 */500 501static void pnpbios_encode_mem(struct pnp_dev *dev, unsigned char *p,502			       struct resource *res)503{504	unsigned long base;505	unsigned long len;506 507	if (pnp_resource_enabled(res)) {508		base = res->start;509		len = resource_size(res);510	} else {511		base = 0;512		len = 0;513	}514 515	p[4] = (base >> 8) & 0xff;516	p[5] = ((base >> 8) >> 8) & 0xff;517	p[6] = (base >> 8) & 0xff;518	p[7] = ((base >> 8) >> 8) & 0xff;519	p[10] = (len >> 8) & 0xff;520	p[11] = ((len >> 8) >> 8) & 0xff;521 522	pnp_dbg(&dev->dev, "  encode mem %#lx-%#lx\n", base, base + len - 1);523}524 525static void pnpbios_encode_mem32(struct pnp_dev *dev, unsigned char *p,526				 struct resource *res)527{528	unsigned long base;529	unsigned long len;530 531	if (pnp_resource_enabled(res)) {532		base = res->start;533		len = resource_size(res);534	} else {535		base = 0;536		len = 0;537	}538 539	p[4] = base & 0xff;540	p[5] = (base >> 8) & 0xff;541	p[6] = (base >> 16) & 0xff;542	p[7] = (base >> 24) & 0xff;543	p[8] = base & 0xff;544	p[9] = (base >> 8) & 0xff;545	p[10] = (base >> 16) & 0xff;546	p[11] = (base >> 24) & 0xff;547	p[16] = len & 0xff;548	p[17] = (len >> 8) & 0xff;549	p[18] = (len >> 16) & 0xff;550	p[19] = (len >> 24) & 0xff;551 552	pnp_dbg(&dev->dev, "  encode mem32 %#lx-%#lx\n", base, base + len - 1);553}554 555static void pnpbios_encode_fixed_mem32(struct pnp_dev *dev, unsigned char *p,556				       struct resource *res)557{558	unsigned long base;559	unsigned long len;560 561	if (pnp_resource_enabled(res)) {562		base = res->start;563		len = resource_size(res);564	} else {565		base = 0;566		len = 0;567	}568 569	p[4] = base & 0xff;570	p[5] = (base >> 8) & 0xff;571	p[6] = (base >> 16) & 0xff;572	p[7] = (base >> 24) & 0xff;573	p[8] = len & 0xff;574	p[9] = (len >> 8) & 0xff;575	p[10] = (len >> 16) & 0xff;576	p[11] = (len >> 24) & 0xff;577 578	pnp_dbg(&dev->dev, "  encode fixed_mem32 %#lx-%#lx\n", base,579		base + len - 1);580}581 582static void pnpbios_encode_irq(struct pnp_dev *dev, unsigned char *p,583			       struct resource *res)584{585	unsigned long map;586 587	if (pnp_resource_enabled(res))588		map = 1 << res->start;589	else590		map = 0;591 592	p[1] = map & 0xff;593	p[2] = (map >> 8) & 0xff;594 595	pnp_dbg(&dev->dev, "  encode irq mask %#lx\n", map);596}597 598static void pnpbios_encode_dma(struct pnp_dev *dev, unsigned char *p,599			       struct resource *res)600{601	unsigned long map;602 603	if (pnp_resource_enabled(res))604		map = 1 << res->start;605	else606		map = 0;607 608	p[1] = map & 0xff;609 610	pnp_dbg(&dev->dev, "  encode dma mask %#lx\n", map);611}612 613static void pnpbios_encode_port(struct pnp_dev *dev, unsigned char *p,614				struct resource *res)615{616	unsigned long base;617	unsigned long len;618 619	if (pnp_resource_enabled(res)) {620		base = res->start;621		len = resource_size(res);622	} else {623		base = 0;624		len = 0;625	}626 627	p[2] = base & 0xff;628	p[3] = (base >> 8) & 0xff;629	p[4] = base & 0xff;630	p[5] = (base >> 8) & 0xff;631	p[7] = len & 0xff;632 633	pnp_dbg(&dev->dev, "  encode io %#lx-%#lx\n", base, base + len - 1);634}635 636static void pnpbios_encode_fixed_port(struct pnp_dev *dev, unsigned char *p,637				      struct resource *res)638{639	unsigned long base = res->start;640	unsigned long len = resource_size(res);641 642	if (pnp_resource_enabled(res)) {643		base = res->start;644		len = resource_size(res);645	} else {646		base = 0;647		len = 0;648	}649 650	p[1] = base & 0xff;651	p[2] = (base >> 8) & 0xff;652	p[3] = len & 0xff;653 654	pnp_dbg(&dev->dev, "  encode fixed_io %#lx-%#lx\n", base,655		base + len - 1);656}657 658static unsigned char *pnpbios_encode_allocated_resource_data(struct pnp_dev659								*dev,660							     unsigned char *p,661							     unsigned char *end)662{663	unsigned int len, tag;664	int port = 0, irq = 0, dma = 0, mem = 0;665 666	if (!p)667		return NULL;668 669	while ((char *)p < (char *)end) {670 671		/* determine the type of tag */672		if (p[0] & LARGE_TAG) {	/* large tag */673			len = (p[2] << 8) | p[1];674			tag = p[0];675		} else {	/* small tag */676			len = p[0] & 0x07;677			tag = ((p[0] >> 3) & 0x0f);678		}679 680		switch (tag) {681 682		case LARGE_TAG_MEM:683			if (len != 9)684				goto len_err;685			pnpbios_encode_mem(dev, p,686				pnp_get_resource(dev, IORESOURCE_MEM, mem));687			mem++;688			break;689 690		case LARGE_TAG_MEM32:691			if (len != 17)692				goto len_err;693			pnpbios_encode_mem32(dev, p,694				pnp_get_resource(dev, IORESOURCE_MEM, mem));695			mem++;696			break;697 698		case LARGE_TAG_FIXEDMEM32:699			if (len != 9)700				goto len_err;701			pnpbios_encode_fixed_mem32(dev, p,702				pnp_get_resource(dev, IORESOURCE_MEM, mem));703			mem++;704			break;705 706		case SMALL_TAG_IRQ:707			if (len < 2 || len > 3)708				goto len_err;709			pnpbios_encode_irq(dev, p,710				pnp_get_resource(dev, IORESOURCE_IRQ, irq));711			irq++;712			break;713 714		case SMALL_TAG_DMA:715			if (len != 2)716				goto len_err;717			pnpbios_encode_dma(dev, p,718				pnp_get_resource(dev, IORESOURCE_DMA, dma));719			dma++;720			break;721 722		case SMALL_TAG_PORT:723			if (len != 7)724				goto len_err;725			pnpbios_encode_port(dev, p,726				pnp_get_resource(dev, IORESOURCE_IO, port));727			port++;728			break;729 730		case SMALL_TAG_VENDOR:731			/* do nothing */732			break;733 734		case SMALL_TAG_FIXEDPORT:735			if (len != 3)736				goto len_err;737			pnpbios_encode_fixed_port(dev, p,738				pnp_get_resource(dev, IORESOURCE_IO, port));739			port++;740			break;741 742		case SMALL_TAG_END:743			p = p + 2;744			return (unsigned char *)p;745			break;746 747		default:	/* an unknown tag */748len_err:749			dev_err(&dev->dev, "unknown tag %#x length %d\n",750				tag, len);751			break;752		}753 754		/* continue to the next tag */755		if (p[0] & LARGE_TAG)756			p += len + 3;757		else758			p += len + 1;759	}760 761	dev_err(&dev->dev, "no end tag in resource structure\n");762 763	return NULL;764}765 766/*767 * Core Parsing Functions768 */769 770int __init pnpbios_parse_data_stream(struct pnp_dev *dev,771					struct pnp_bios_node *node)772{773	unsigned char *p = (char *)node->data;774	unsigned char *end = (char *)(node->data + node->size);775 776	p = pnpbios_parse_allocated_resource_data(dev, p, end);777	if (!p)778		return -EIO;779	p = pnpbios_parse_resource_option_data(p, end, dev);780	if (!p)781		return -EIO;782	p = pnpbios_parse_compatible_ids(p, end, dev);783	if (!p)784		return -EIO;785	return 0;786}787 788int pnpbios_read_resources_from_node(struct pnp_dev *dev,789				     struct pnp_bios_node *node)790{791	unsigned char *p = (char *)node->data;792	unsigned char *end = (char *)(node->data + node->size);793 794	p = pnpbios_parse_allocated_resource_data(dev, p, end);795	if (!p)796		return -EIO;797	return 0;798}799 800int pnpbios_write_resources_to_node(struct pnp_dev *dev,801				    struct pnp_bios_node *node)802{803	unsigned char *p = (char *)node->data;804	unsigned char *end = (char *)(node->data + node->size);805 806	p = pnpbios_encode_allocated_resource_data(dev, p, end);807	if (!p)808		return -EIO;809	return 0;810}811