brintos

brintos / linux-shallow public Read only

0
0
Text · 13.4 KiB · f7e86ae Raw
576 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * pnpbios -- PnP BIOS driver4 *5 * This driver provides access to Plug-'n'-Play services provided by6 * the PnP BIOS firmware, described in the following documents:7 *   Plug and Play BIOS Specification, Version 1.0A, 5 May 19948 *   Plug and Play BIOS Clarification Paper, 6 October 19949 *     Compaq Computer Corporation, Phoenix Technologies Ltd., Intel Corp.10 * 11 * Originally (C) 1998 Christian Schmidt <schmidt@digadd.de>12 * Modifications (C) 1998 Tom Lees <tom@lpsg.demon.co.uk>13 * Minor reorganizations by David Hinds <dahinds@users.sourceforge.net>14 * Further modifications (C) 2001, 2002 by:15 *   Alan Cox <alan@redhat.com>16 *   Thomas Hood17 *   Brian Gerst <bgerst@didntduck.org>18 *19 * Ported to the PnP Layer and several additional improvements (C) 200220 * by Adam Belay <ambx1@neo.rr.com>21 */22 23/* Change Log24 *25 * Adam Belay - <ambx1@neo.rr.com> - March 16, 200326 * rev 1.01	Only call pnp_bios_dev_node_info once27 *		Added pnpbios_print_status28 *		Added several new error messages and info messages29 *		Added pnpbios_interface_attach_device30 *		integrated core and proc init system31 *		Introduced PNPMODE flags32 *		Removed some useless includes33 */34 35#include <linux/types.h>36#include <linux/init.h>37#include <linux/linkage.h>38#include <linux/kernel.h>39#include <linux/device.h>40#include <linux/pnp.h>41#include <linux/mm.h>42#include <linux/smp.h>43#include <linux/slab.h>44#include <linux/completion.h>45#include <linux/spinlock.h>46#include <linux/dmi.h>47#include <linux/delay.h>48#include <linux/acpi.h>49#include <linux/freezer.h>50#include <linux/kmod.h>51#include <linux/kthread.h>52 53#include <asm/page.h>54#include <asm/desc.h>55#include <asm/byteorder.h>56 57#include "../base.h"58#include "pnpbios.h"59 60/*61 *62 * PnP BIOS INTERFACE63 *64 */65 66static union pnp_bios_install_struct *pnp_bios_install = NULL;67 68int pnp_bios_present(void)69{70	return (pnp_bios_install != NULL);71}72 73struct pnp_dev_node_info node_info;74 75/*76 *77 * DOCKING FUNCTIONS78 *79 */80 81static struct completion unload_sem;82 83/*84 * (Much of this belongs in a shared routine somewhere)85 */86static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)87{88	static char const sbin_pnpbios[] = "/sbin/pnpbios";89	char *argv[3], **envp, *buf, *scratch;90	int i = 0, value;91 92	if (!(envp = kcalloc(20, sizeof(char *), GFP_KERNEL)))93		return -ENOMEM;94	if (!(buf = kzalloc(256, GFP_KERNEL))) {95		kfree(envp);96		return -ENOMEM;97	}98 99	/* FIXME: if there are actual users of this, it should be100	 * integrated into the driver core and use the usual infrastructure101	 * like sysfs and uevents102	 */103	argv[0] = (char *)sbin_pnpbios;104	argv[1] = "dock";105	argv[2] = NULL;106 107	/* minimal command environment */108	envp[i++] = "HOME=/";109	envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";110 111#ifdef	DEBUG112	/* hint that policy agent should enter no-stdout debug mode */113	envp[i++] = "DEBUG=kernel";114#endif115	/* extensible set of named bus-specific parameters,116	 * supporting multiple driver selection algorithms.117	 */118	scratch = buf;119 120	/* action:  add, remove */121	envp[i++] = scratch;122	scratch += sprintf(scratch, "ACTION=%s", dock ? "add" : "remove") + 1;123 124	/* Report the ident for the dock */125	envp[i++] = scratch;126	scratch += sprintf(scratch, "DOCK=%x/%x/%x",127			   info->location_id, info->serial, info->capabilities);128	envp[i] = NULL;129 130	value = call_usermodehelper(sbin_pnpbios, argv, envp, UMH_WAIT_EXEC);131	kfree(buf);132	kfree(envp);133	return 0;134}135 136/*137 * Poll the PnP docking at regular intervals138 */139static int pnp_dock_thread(void *unused)140{141	static struct pnp_docking_station_info now;142	int docked = -1, d = 0;143 144	set_freezable();145	while (1) {146		int status;147 148		/*149		 * Poll every 2 seconds150		 */151		msleep_interruptible(2000);152 153		if (try_to_freeze())154			continue;155 156		status = pnp_bios_dock_station_info(&now);157 158		switch (status) {159			/*160			 * No dock to manage161			 */162		case PNP_FUNCTION_NOT_SUPPORTED:163			kthread_complete_and_exit(&unload_sem, 0);164		case PNP_SYSTEM_NOT_DOCKED:165			d = 0;166			break;167		case PNP_SUCCESS:168			d = 1;169			break;170		default:171			pnpbios_print_status("pnp_dock_thread", status);172			printk(KERN_WARNING "PnPBIOS: disabling dock monitoring.\n");173			kthread_complete_and_exit(&unload_sem, 0);174		}175		if (d != docked) {176			if (pnp_dock_event(d, &now) == 0) {177				docked = d;178#if 0179				printk(KERN_INFO180				       "PnPBIOS: Docking station %stached\n",181				       docked ? "at" : "de");182#endif183			}184		}185	}186	kthread_complete_and_exit(&unload_sem, 0);187}188 189static int pnpbios_get_resources(struct pnp_dev *dev)190{191	u8 nodenum = dev->number;192	struct pnp_bios_node *node;193 194	if (!pnpbios_is_dynamic(dev))195		return -EPERM;196 197	pnp_dbg(&dev->dev, "get resources\n");198	node = kzalloc(node_info.max_node_size, GFP_KERNEL);199	if (!node)200		return -1;201	if (pnp_bios_get_dev_node(&nodenum, (char)PNPMODE_DYNAMIC, node)) {202		kfree(node);203		return -ENODEV;204	}205	pnpbios_read_resources_from_node(dev, node);206	dev->active = pnp_is_active(dev);207	kfree(node);208	return 0;209}210 211static int pnpbios_set_resources(struct pnp_dev *dev)212{213	u8 nodenum = dev->number;214	struct pnp_bios_node *node;215	int ret;216 217	if (!pnpbios_is_dynamic(dev))218		return -EPERM;219 220	pnp_dbg(&dev->dev, "set resources\n");221	node = kzalloc(node_info.max_node_size, GFP_KERNEL);222	if (!node)223		return -1;224	if (pnp_bios_get_dev_node(&nodenum, (char)PNPMODE_DYNAMIC, node)) {225		kfree(node);226		return -ENODEV;227	}228	if (pnpbios_write_resources_to_node(dev, node) < 0) {229		kfree(node);230		return -1;231	}232	ret = pnp_bios_set_dev_node(node->handle, (char)PNPMODE_DYNAMIC, node);233	kfree(node);234	if (ret > 0)235		ret = -1;236	return ret;237}238 239static void pnpbios_zero_data_stream(struct pnp_bios_node *node)240{241	unsigned char *p = (char *)node->data;242	unsigned char *end = (char *)(node->data + node->size);243	unsigned int len;244	int i;245 246	while ((char *)p < (char *)end) {247		if (p[0] & 0x80) {	/* large tag */248			len = (p[2] << 8) | p[1];249			p += 3;250		} else {251			if (((p[0] >> 3) & 0x0f) == 0x0f)252				return;253			len = p[0] & 0x07;254			p += 1;255		}256		for (i = 0; i < len; i++)257			p[i] = 0;258		p += len;259	}260	printk(KERN_ERR261	       "PnPBIOS: Resource structure did not contain an end tag.\n");262}263 264static int pnpbios_disable_resources(struct pnp_dev *dev)265{266	struct pnp_bios_node *node;267	u8 nodenum = dev->number;268	int ret;269 270	if (dev->flags & PNPBIOS_NO_DISABLE || !pnpbios_is_dynamic(dev))271		return -EPERM;272 273	node = kzalloc(node_info.max_node_size, GFP_KERNEL);274	if (!node)275		return -ENOMEM;276 277	if (pnp_bios_get_dev_node(&nodenum, (char)PNPMODE_DYNAMIC, node)) {278		kfree(node);279		return -ENODEV;280	}281	pnpbios_zero_data_stream(node);282 283	ret = pnp_bios_set_dev_node(dev->number, (char)PNPMODE_DYNAMIC, node);284	kfree(node);285	if (ret > 0)286		ret = -1;287	return ret;288}289 290/* PnP Layer support */291 292struct pnp_protocol pnpbios_protocol = {293	.name = "Plug and Play BIOS",294	.get = pnpbios_get_resources,295	.set = pnpbios_set_resources,296	.disable = pnpbios_disable_resources,297};298 299static int __init insert_device(struct pnp_bios_node *node)300{301	struct pnp_dev *dev;302	char id[8];303	int error;304 305	/* check if the device is already added */306	list_for_each_entry(dev, &pnpbios_protocol.devices, protocol_list) {307		if (dev->number == node->handle)308			return -EEXIST;309	}310 311	pnp_eisa_id_to_string(node->eisa_id & PNP_EISA_ID_MASK, id);312	dev = pnp_alloc_dev(&pnpbios_protocol, node->handle, id);313	if (!dev)314		return -ENOMEM;315 316	pnpbios_parse_data_stream(dev, node);317	dev->active = pnp_is_active(dev);318	dev->flags = node->flags;319	if (!(dev->flags & PNPBIOS_NO_CONFIG))320		dev->capabilities |= PNP_CONFIGURABLE;321	if (!(dev->flags & PNPBIOS_NO_DISABLE) && pnpbios_is_dynamic(dev))322		dev->capabilities |= PNP_DISABLE;323	dev->capabilities |= PNP_READ;324	if (pnpbios_is_dynamic(dev))325		dev->capabilities |= PNP_WRITE;326	if (dev->flags & PNPBIOS_REMOVABLE)327		dev->capabilities |= PNP_REMOVABLE;328 329	/* clear out the damaged flags */330	if (!dev->active)331		pnp_init_resources(dev);332 333	error = pnp_add_device(dev);334	if (error) {335		put_device(&dev->dev);336		return error;337	}338 339	pnpbios_interface_attach_device(node);340 341	return 0;342}343 344static void __init build_devlist(void)345{346	u8 nodenum;347	unsigned int nodes_got = 0;348	unsigned int devs = 0;349	struct pnp_bios_node *node;350 351	node = kzalloc(node_info.max_node_size, GFP_KERNEL);352	if (!node)353		return;354 355	for (nodenum = 0; nodenum < 0xff;) {356		u8 thisnodenum = nodenum;357		/* eventually we will want to use PNPMODE_STATIC here but for now358		 * dynamic will help us catch buggy bioses to add to the blacklist.359		 */360		if (!pnpbios_dont_use_current_config) {361			if (pnp_bios_get_dev_node362			    (&nodenum, (char)PNPMODE_DYNAMIC, node))363				break;364		} else {365			if (pnp_bios_get_dev_node366			    (&nodenum, (char)PNPMODE_STATIC, node))367				break;368		}369		nodes_got++;370		if (insert_device(node) == 0)371			devs++;372		if (nodenum <= thisnodenum) {373			printk(KERN_ERR374			       "PnPBIOS: build_devlist: Node number 0x%x is out of sequence following node 0x%x. Aborting.\n",375			       (unsigned int)nodenum,376			       (unsigned int)thisnodenum);377			break;378		}379	}380	kfree(node);381 382	printk(KERN_INFO383	       "PnPBIOS: %i node%s reported by PnP BIOS; %i recorded by driver\n",384	       nodes_got, nodes_got != 1 ? "s" : "", devs);385}386 387/*388 *389 * INIT AND EXIT390 *391 */392 393static int pnpbios_disabled;394int pnpbios_dont_use_current_config;395 396static int __init pnpbios_setup(char *str)397{398	int invert;399 400	while ((str != NULL) && (*str != '\0')) {401		if (strncmp(str, "off", 3) == 0)402			pnpbios_disabled = 1;403		if (strncmp(str, "on", 2) == 0)404			pnpbios_disabled = 0;405		invert = (strncmp(str, "no-", 3) == 0);406		if (invert)407			str += 3;408		if (strncmp(str, "curr", 4) == 0)409			pnpbios_dont_use_current_config = invert;410		str = strchr(str, ',');411		if (str != NULL)412			str += strspn(str, ", \t");413	}414 415	return 1;416}417 418__setup("pnpbios=", pnpbios_setup);419 420/* PnP BIOS signature: "$PnP" */421#define PNP_SIGNATURE   (('$' << 0) + ('P' << 8) + ('n' << 16) + ('P' << 24))422 423static int __init pnpbios_probe_system(void)424{425	union pnp_bios_install_struct *check;426	u8 sum;427	int length, i;428 429	printk(KERN_INFO "PnPBIOS: Scanning system for PnP BIOS support...\n");430 431	/*432	 * Search the defined area (0xf0000-0xffff0) for a valid PnP BIOS433	 * structure and, if one is found, sets up the selectors and434	 * entry points435	 */436	for (check = (union pnp_bios_install_struct *)__va(0xf0000);437	     check < (union pnp_bios_install_struct *)__va(0xffff0);438	     check = (void *)check + 16) {439		if (check->fields.signature != PNP_SIGNATURE)440			continue;441		printk(KERN_INFO442		       "PnPBIOS: Found PnP BIOS installation structure at 0x%p\n",443		       check);444		length = check->fields.length;445		if (!length) {446			printk(KERN_ERR447			       "PnPBIOS: installation structure is invalid, skipping\n");448			continue;449		}450		for (sum = 0, i = 0; i < length; i++)451			sum += check->chars[i];452		if (sum) {453			printk(KERN_ERR454			       "PnPBIOS: installation structure is corrupted, skipping\n");455			continue;456		}457		if (check->fields.version < 0x10) {458			printk(KERN_WARNING459			       "PnPBIOS: PnP BIOS version %d.%d is not supported\n",460			       check->fields.version >> 4,461			       check->fields.version & 15);462			continue;463		}464		printk(KERN_INFO465		       "PnPBIOS: PnP BIOS version %d.%d, entry 0x%x:0x%x, dseg 0x%x\n",466		       check->fields.version >> 4, check->fields.version & 15,467		       check->fields.pm16cseg, check->fields.pm16offset,468		       check->fields.pm16dseg);469		pnp_bios_install = check;470		return 1;471	}472 473	printk(KERN_INFO "PnPBIOS: PnP BIOS support was not detected.\n");474	return 0;475}476 477static int __init exploding_pnp_bios(const struct dmi_system_id *d)478{479	printk(KERN_WARNING "%s detected. Disabling PnPBIOS\n", d->ident);480	return 0;481}482 483static const struct dmi_system_id pnpbios_dmi_table[] __initconst = {484	{			/* PnPBIOS GPF on boot */485	 .callback = exploding_pnp_bios,486	 .ident = "Higraded P14H",487	 .matches = {488		     DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),489		     DMI_MATCH(DMI_BIOS_VERSION, "07.00T"),490		     DMI_MATCH(DMI_SYS_VENDOR, "Higraded"),491		     DMI_MATCH(DMI_PRODUCT_NAME, "P14H"),492		     },493	 },494	{			/* PnPBIOS GPF on boot */495	 .callback = exploding_pnp_bios,496	 .ident = "ASUS P4P800",497	 .matches = {498		     DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."),499		     DMI_MATCH(DMI_BOARD_NAME, "P4P800"),500		     },501	 },502	{}503};504 505static int __init pnpbios_init(void)506{507	int ret;508 509	if (pnpbios_disabled || dmi_check_system(pnpbios_dmi_table) ||510	    arch_pnpbios_disabled()) {511		printk(KERN_INFO "PnPBIOS: Disabled\n");512		return -ENODEV;513	}514 515#ifdef CONFIG_PNPACPI516	if (!acpi_disabled && !pnpacpi_disabled) {517		pnpbios_disabled = 1;518		printk(KERN_INFO "PnPBIOS: Disabled by ACPI PNP\n");519		return -ENODEV;520	}521#endif				/* CONFIG_ACPI */522 523	/* scan the system for pnpbios support */524	if (!pnpbios_probe_system())525		return -ENODEV;526 527	/* make preparations for bios calls */528	pnpbios_calls_init(pnp_bios_install);529 530	/* read the node info */531	ret = pnp_bios_dev_node_info(&node_info);532	if (ret) {533		printk(KERN_ERR534		       "PnPBIOS: Unable to get node info.  Aborting.\n");535		return ret;536	}537 538	/* register with the pnp layer */539	ret = pnp_register_protocol(&pnpbios_protocol);540	if (ret) {541		printk(KERN_ERR542		       "PnPBIOS: Unable to register driver.  Aborting.\n");543		return ret;544	}545 546	/* start the proc interface */547	ret = pnpbios_proc_init();548	if (ret)549		printk(KERN_ERR "PnPBIOS: Failed to create proc interface.\n");550 551	/* scan for pnpbios devices */552	build_devlist();553 554	pnp_platform_devices = 1;555	return 0;556}557 558fs_initcall(pnpbios_init);559 560static int __init pnpbios_thread_init(void)561{562	struct task_struct *task;563 564	if (pnpbios_disabled)565		return 0;566 567	init_completion(&unload_sem);568	task = kthread_run(pnp_dock_thread, NULL, "kpnpbiosd");569	return PTR_ERR_OR_ZERO(task);570}571 572/* Start the kernel thread later: */573device_initcall(pnpbios_thread_init);574 575EXPORT_SYMBOL(pnpbios_protocol);576