📄 pnp_bios.c
字号:
/* * PnP bios services * * Originally (C) 1998 Christian Schmidt (chr.schmidt@tu-bs.de) * Modifications (c) 1998 Tom Lees <tom@lpsg.demon.co.uk> * Minor reorganizations by David Hinds <dahinds@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Reference: * Compaq Computer Corporation, Phoenix Technologies Ltd., Intel * Corporation. * Plug and Play BIOS Specification, Version 1.0A, May 5, 1994 * Plug and Play BIOS Clarification Paper, October 6, 1994 * */#define __NO_VERSION__#include <linux/types.h>#include <linux/module.h>#include <linux/linkage.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/pnp_bios.h>#include <linux/spinlock.h>#include <linux/sched.h>#include <asm/page.h>#include <asm/system.h>#include <asm/desc.h>/* PnP bios signature: "$PnP" */#define PNP_SIGNATURE (('$' << 0) + ('P' << 8) + ('n' << 16) + ('P' << 24))#ifdef MODULEstatic struct desc_struct *gdt;#endif/* * This is the standard structure used to identify the entry point * to the Plug and Play bios */#pragma pack(1)union pnpbios { struct { u32 signature; /* "$PnP" */ u8 version; /* in BCD */ u8 length; /* length in bytes, currently 21h */ u16 control; /* system capabilities */ u8 checksum; /* all bytes must add up to 0 */ u32 eventflag; /* phys. address of the event flag */ u16 rmoffset; /* real mode entry point */ u16 rmcseg; u16 pm16offset; /* 16 bit protected mode entry */ u32 pm16cseg; u32 deviceID; /* EISA encoded system ID or 0 */ u16 rmdseg; /* real mode data segment */ u32 pm16dseg; /* 16 bit pm data segment base */ } fields; char chars[0x21]; /* To calculate the checksum */};#pragma pack()/* * Local Variables */static struct { u32 offset; u16 segment;} pnp_bios_callpoint;static union pnpbios * pnp_bios_inst_struc = NULL;/* The PnP entries in the GDT */#define PNP_GDT 0x0038#define PNP_CS32 (PNP_GDT+0x00) /* segment for calling fn */#define PNP_CS16 (PNP_GDT+0x08) /* code segment for bios */#define PNP_DS (PNP_GDT+0x10) /* data segment for bios */#define PNP_TS1 (PNP_GDT+0x18) /* transfer data segment */#define PNP_TS2 (PNP_GDT+0x20) /* another data segment */static struct desc_struct saved_gdt[5];static struct desc_struct pnp_gdt[] = { { 0, 0x00c09a00 }, /* 32-bit code */ { 0, 0x00809a00 }, /* 16-bit code */ { 0, 0x00809200 }, /* 16-bit data */ { 0, 0x00809200 }, /* 16-bit data */ { 0, 0x00809200 } /* 16-bit data */};/* * GDT abuse: since we want this to work when loaded as a module on a * normal kernel, we drop the PnP GDT entries on top of the APM stuff * and protect it with a spin lock ( */static long gdt_flags;#ifdef USE_SPIN_LOCKSstatic spinlock_t gdt_lock = SPIN_LOCK_UNLOCKED;#endifstatic void push_pnp_gdt(void){ spin_lock_irqsave(&gdt_lock, gdt_flags); memcpy(saved_gdt, &gdt[PNP_GDT >> 3], sizeof(saved_gdt)); memcpy(&gdt[PNP_GDT >> 3], pnp_gdt, sizeof(pnp_gdt));}static void pop_pnp_gdt(void){ memcpy(pnp_gdt, &gdt[PNP_GDT >> 3], sizeof(pnp_gdt)); memcpy(&gdt[PNP_GDT >> 3], saved_gdt, sizeof(saved_gdt)); spin_unlock_irqrestore(&gdt_lock, gdt_flags);}/* * These are some opcodes for a "static asmlinkage" * As this code is *not* executed inside the linux kernel segment, but in a * alias at offset 0, we need a far return that can not be compiled by * default (please, prove me wrong! this is *really* ugly!) * This is the only way to get the bios to return into the kernel code, * because the bios code runs in 16 bit protected mode and therefore can only * return to the caller if the call is within the first 64kB, and the linux * kernel begins at offset 1MB... */static u8 pnp_bios_callfunc[] ={ 0x52, /* push edx */ 0x51, /* push ecx */ 0x53, /* push ebx */ 0x50, /* push eax */ 0x66, 0x9a, 0, 0, /* call far pnp_cs16:0, offset */ (PNP_CS16) & 0xff, (PNP_CS16) >> 8, /* becomes fixed up later */ 0x83, 0xc4, 0x10, /* add esp, 16 */ 0xcb}; /* retf */#define Q_SET_SEL(selname, address, size) \set_base (gdt [(selname) >> 3], __va((u32)(address))); \set_limit (gdt [(selname) >> 3], size)#define Q2_SET_SEL(selname, address, size) \set_base (gdt [(selname) >> 3], (u32)(address)); \set_limit (gdt [(selname) >> 3], size)/* * Callable Functions */#define PNP_GET_NUM_SYS_DEV_NODES 0x00#define PNP_GET_SYS_DEV_NODE 0x01#define PNP_SET_SYS_DEV_NODE 0x02#define PNP_GET_EVENT 0x03#define PNP_SEND_MESSAGE 0x04#define PNP_GET_DOCKING_STATION_INFORMATION 0x05#define PNP_SET_STATIC_ALLOCED_RES_INFO 0x09#define PNP_GET_STATIC_ALLOCED_RES_INFO 0x0a#define PNP_GET_APM_ID_TABLE 0x0b#define PNP_GET_PNP_ISA_CONFIG_STRUC 0x40#define PNP_GET_ESCD_INFO 0x41#define PNP_READ_ESCD 0x42#define PNP_WRITE_ESCD 0x43/* * Call pnp bios with function 0x00, "get number of system device nodes" */int pnp_bios_dev_node_info(struct pnp_dev_node_info *data){ u16 status; if (!pnp_bios_present ()) return PNP_FUNCTION_NOT_SUPPORTED; push_pnp_gdt(); Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_dev_node_info)); __asm__ __volatile__ ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" :"=a"(status) :"a"(PNP_GET_NUM_SYS_DEV_NODES), "b"((2 << 16) | PNP_TS1), "c"((PNP_DS << 16) | PNP_TS1) :"memory"); data->no_nodes &= 0xff; pop_pnp_gdt(); return status;}/* * Call pnp bios with function 0x01, "get system device node" * Input: *nodenum=desired node, * static=1: config (dynamic) config, else boot (static) config, * Output: *nodenum=next node or 0xff if no more nodes */int pnp_bios_get_dev_node(u8 *nodenum, char config, struct pnp_bios_node *data){ u16 status; if (!pnp_bios_present ()) return PNP_FUNCTION_NOT_SUPPORTED; push_pnp_gdt(); Q2_SET_SEL(PNP_TS1, nodenum, sizeof(char)); Q2_SET_SEL(PNP_TS2, data, 64 * 1024); __asm__ __volatile__ ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" :"=a"(status) :"a"(PNP_GET_SYS_DEV_NODE), "b"(PNP_TS1), "c"(((config ? 1 : 2) <<16) | PNP_TS2), "d"(PNP_DS) :"memory"); pop_pnp_gdt(); return status;}/* * Call pnp bios with function 0x02, "set system device node" * Input: nodenum=desired node, * static=1: config (dynamic) config, else boot (static) config, */int pnp_bios_set_dev_node(u8 nodenum, char config, struct pnp_bios_node *data){ u16 status; if (!pnp_bios_present ()) return PNP_FUNCTION_NOT_SUPPORTED; push_pnp_gdt(); Q2_SET_SEL(PNP_TS1, data, /* *((u16 *) data)*/ 65536); __asm__ __volatile__ ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" :"=a"(status) :"a"(((u32) nodenum << 16) | PNP_SET_SYS_DEV_NODE), "b"(PNP_TS1 << 16), "c"((PNP_DS << 16) | (config ? 1 : 2)) :"memory"); pop_pnp_gdt(); return status;}/* * Call pnp bios with function 0x03, "get event" */#if neededint pnp_bios_get_event(u16 *event){ u16 status; if (!pnp_bios_present ()) return PNP_FUNCTION_NOT_SUPPORTED; push_pnp_gdt(); Q2_SET_SEL(PNP_TS1, event, sizeof(u16)); __asm__ __volatile__ ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" :"=a"(status) :"a"(PNP_GET_EVENT), "b"((PNP_DS << 16) | PNP_TS1) :"memory"); pop_pnp_gdt(); return status;}#endif/* * Call pnp bios with function 0x04, "send message"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -