brintos

brintos / linux-shallow public Read only

0
0
Text · 13.0 KiB · 1f31dce Raw
491 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * bioscalls.c - the lowlevel layer of the PnPBIOS driver4 */5 6#include <linux/types.h>7#include <linux/module.h>8#include <linux/init.h>9#include <linux/linkage.h>10#include <linux/kernel.h>11#include <linux/device.h>12#include <linux/pnp.h>13#include <linux/mm.h>14#include <linux/smp.h>15#include <linux/kmod.h>16#include <linux/completion.h>17#include <linux/spinlock.h>18 19#include <asm/page.h>20#include <asm/desc.h>21#include <asm/byteorder.h>22 23#include "pnpbios.h"24 25__visible struct {26	u16 offset;27	u16 segment;28} pnp_bios_callpoint;29 30/*31 * These are some opcodes for a "static asmlinkage"32 * As this code is *not* executed inside the linux kernel segment, but in a33 * alias at offset 0, we need a far return that can not be compiled by34 * default (please, prove me wrong! this is *really* ugly!)35 * This is the only way to get the bios to return into the kernel code,36 * because the bios code runs in 16 bit protected mode and therefore can only37 * return to the caller if the call is within the first 64kB, and the linux38 * kernel begins at offset 3GB...39 */40 41asmlinkage __visible void pnp_bios_callfunc(void);42 43__asm__(".text			\n"44	__ALIGN_STR "\n"45	".globl pnp_bios_callfunc\n"46	"pnp_bios_callfunc:\n"47	"	pushl %edx	\n"48	"	pushl %ecx	\n"49	"	pushl %ebx	\n"50	"	pushl %eax	\n"51	"	lcallw *pnp_bios_callpoint\n"52	"	addl $16, %esp	\n"53	"	lret		\n"54	".previous		\n");55 56#define Q2_SET_SEL(cpu, selname, address, size) \57do { \58	struct desc_struct *gdt = get_cpu_gdt_rw((cpu)); \59	set_desc_base(&gdt[(selname) >> 3], (u32)(address)); \60	set_desc_limit(&gdt[(selname) >> 3], (size) - 1); \61} while(0)62 63static struct desc_struct bad_bios_desc = GDT_ENTRY_INIT(DESC_DATA32_BIOS,64			(unsigned long)__va(0x400UL), PAGE_SIZE - 0x400 - 1);65 66/*67 * At some point we want to use this stack frame pointer to unwind68 * after PnP BIOS oopses.69 */70 71__visible u32 pnp_bios_fault_esp;72__visible u32 pnp_bios_fault_eip;73__visible u32 pnp_bios_is_utter_crap = 0;74 75static DEFINE_SPINLOCK(pnp_bios_lock);76 77/*78 * Support Functions79 */80 81static inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3,82				u16 arg4, u16 arg5, u16 arg6, u16 arg7,83				void *ts1_base, u32 ts1_size,84				void *ts2_base, u32 ts2_size)85{86	unsigned long flags;87	u16 status;88	struct desc_struct save_desc_40;89	int cpu;90 91	/*92	 * PnP BIOSes are generally not terribly re-entrant.93	 * Also, don't rely on them to save everything correctly.94	 */95	if (pnp_bios_is_utter_crap)96		return PNP_FUNCTION_NOT_SUPPORTED;97 98	cpu = get_cpu();99	save_desc_40 = get_cpu_gdt_rw(cpu)[0x40 / 8];100	get_cpu_gdt_rw(cpu)[0x40 / 8] = bad_bios_desc;101 102	/* On some boxes IRQ's during PnP BIOS calls are deadly.  */103	spin_lock_irqsave(&pnp_bios_lock, flags);104 105	/* The lock prevents us bouncing CPU here */106	if (ts1_size)107		Q2_SET_SEL(smp_processor_id(), PNP_TS1, ts1_base, ts1_size);108	if (ts2_size)109		Q2_SET_SEL(smp_processor_id(), PNP_TS2, ts2_base, ts2_size);110 111	__asm__ __volatile__("pushl %%ebp\n\t"112			     "pushl %%edi\n\t"113			     "pushl %%esi\n\t"114			     "pushl %%ds\n\t"115			     "pushl %%es\n\t"116			     "pushl %%fs\n\t"117			     "pushl %%gs\n\t"118			     "pushfl\n\t"119			     "movl %%esp, pnp_bios_fault_esp\n\t"120			     "movl $1f, pnp_bios_fault_eip\n\t"121			     "lcall %5,%6\n\t"122			     "1:popfl\n\t"123			     "popl %%gs\n\t"124			     "popl %%fs\n\t"125			     "popl %%es\n\t"126			     "popl %%ds\n\t"127			     "popl %%esi\n\t"128			     "popl %%edi\n\t"129			     "popl %%ebp\n\t":"=a"(status)130			     :"0"((func) | (((u32) arg1) << 16)),131			     "b"((arg2) | (((u32) arg3) << 16)),132			     "c"((arg4) | (((u32) arg5) << 16)),133			     "d"((arg6) | (((u32) arg7) << 16)),134			     "i"(PNP_CS32), "i"(0)135			     :"memory");136	spin_unlock_irqrestore(&pnp_bios_lock, flags);137 138	get_cpu_gdt_rw(cpu)[0x40 / 8] = save_desc_40;139	put_cpu();140 141	/* If we get here and this is set then the PnP BIOS faulted on us. */142	if (pnp_bios_is_utter_crap) {143		printk(KERN_ERR144		       "PnPBIOS: Warning! Your PnP BIOS caused a fatal error. Attempting to continue\n");145		printk(KERN_ERR146		       "PnPBIOS: You may need to reboot with the \"pnpbios=off\" option to operate stably\n");147		printk(KERN_ERR148		       "PnPBIOS: Check with your vendor for an updated BIOS\n");149	}150 151	return status;152}153 154void pnpbios_print_status(const char *module, u16 status)155{156	switch (status) {157	case PNP_SUCCESS:158		printk(KERN_ERR "PnPBIOS: %s: function successful\n", module);159		break;160	case PNP_NOT_SET_STATICALLY:161		printk(KERN_ERR "PnPBIOS: %s: unable to set static resources\n",162		       module);163		break;164	case PNP_UNKNOWN_FUNCTION:165		printk(KERN_ERR "PnPBIOS: %s: invalid function number passed\n",166		       module);167		break;168	case PNP_FUNCTION_NOT_SUPPORTED:169		printk(KERN_ERR170		       "PnPBIOS: %s: function not supported on this system\n",171		       module);172		break;173	case PNP_INVALID_HANDLE:174		printk(KERN_ERR "PnPBIOS: %s: invalid handle\n", module);175		break;176	case PNP_BAD_PARAMETER:177		printk(KERN_ERR "PnPBIOS: %s: invalid parameters were passed\n",178		       module);179		break;180	case PNP_SET_FAILED:181		printk(KERN_ERR "PnPBIOS: %s: unable to set resources\n",182		       module);183		break;184	case PNP_EVENTS_NOT_PENDING:185		printk(KERN_ERR "PnPBIOS: %s: no events are pending\n", module);186		break;187	case PNP_SYSTEM_NOT_DOCKED:188		printk(KERN_ERR "PnPBIOS: %s: the system is not docked\n",189		       module);190		break;191	case PNP_NO_ISA_PNP_CARDS:192		printk(KERN_ERR193		       "PnPBIOS: %s: no isapnp cards are installed on this system\n",194		       module);195		break;196	case PNP_UNABLE_TO_DETERMINE_DOCK_CAPABILITIES:197		printk(KERN_ERR198		       "PnPBIOS: %s: cannot determine the capabilities of the docking station\n",199		       module);200		break;201	case PNP_CONFIG_CHANGE_FAILED_NO_BATTERY:202		printk(KERN_ERR203		       "PnPBIOS: %s: unable to undock, the system does not have a battery\n",204		       module);205		break;206	case PNP_CONFIG_CHANGE_FAILED_RESOURCE_CONFLICT:207		printk(KERN_ERR208		       "PnPBIOS: %s: could not dock due to resource conflicts\n",209		       module);210		break;211	case PNP_BUFFER_TOO_SMALL:212		printk(KERN_ERR "PnPBIOS: %s: the buffer passed is too small\n",213		       module);214		break;215	case PNP_USE_ESCD_SUPPORT:216		printk(KERN_ERR "PnPBIOS: %s: use ESCD instead\n", module);217		break;218	case PNP_MESSAGE_NOT_SUPPORTED:219		printk(KERN_ERR "PnPBIOS: %s: the message is unsupported\n",220		       module);221		break;222	case PNP_HARDWARE_ERROR:223		printk(KERN_ERR "PnPBIOS: %s: a hardware failure has occurred\n",224		       module);225		break;226	default:227		printk(KERN_ERR "PnPBIOS: %s: unexpected status 0x%x\n", module,228		       status);229		break;230	}231}232 233/*234 * PnP BIOS Low Level Calls235 */236 237#define PNP_GET_NUM_SYS_DEV_NODES		0x00238#define PNP_GET_SYS_DEV_NODE			0x01239#define PNP_SET_SYS_DEV_NODE			0x02240#define PNP_GET_EVENT				0x03241#define PNP_SEND_MESSAGE			0x04242#define PNP_GET_DOCKING_STATION_INFORMATION	0x05243#define PNP_SET_STATIC_ALLOCED_RES_INFO		0x09244#define PNP_GET_STATIC_ALLOCED_RES_INFO		0x0a245#define PNP_GET_APM_ID_TABLE			0x0b246#define PNP_GET_PNP_ISA_CONFIG_STRUC		0x40247#define PNP_GET_ESCD_INFO			0x41248#define PNP_READ_ESCD				0x42249#define PNP_WRITE_ESCD				0x43250 251/*252 * Call PnP BIOS with function 0x00, "get number of system device nodes"253 */254static int __pnp_bios_dev_node_info(struct pnp_dev_node_info *data)255{256	u16 status;257 258	if (!pnp_bios_present())259		return PNP_FUNCTION_NOT_SUPPORTED;260	status = call_pnp_bios(PNP_GET_NUM_SYS_DEV_NODES, 0, PNP_TS1, 2,261			       PNP_TS1, PNP_DS, 0, 0, data,262			       sizeof(struct pnp_dev_node_info), NULL, 0);263	data->no_nodes &= 0xff;264	return status;265}266 267int pnp_bios_dev_node_info(struct pnp_dev_node_info *data)268{269	int status = __pnp_bios_dev_node_info(data);270 271	if (status)272		pnpbios_print_status("dev_node_info", status);273	return status;274}275 276/*277 * Note that some PnP BIOSes (e.g., on Sony Vaio laptops) die a horrible278 * death if they are asked to access the "current" configuration.279 * Therefore, if it's a matter of indifference, it's better to call280 * get_dev_node() and set_dev_node() with boot=1 rather than with boot=0.281 */282 283/* 284 * Call PnP BIOS with function 0x01, "get system device node"285 * Input: *nodenum = desired node,286 *        boot = whether to get nonvolatile boot (!=0)287 *               or volatile current (0) config288 * Output: *nodenum=next node or 0xff if no more nodes289 */290static int __pnp_bios_get_dev_node(u8 *nodenum, char boot,291				   struct pnp_bios_node *data)292{293	u16 status;294	u16 tmp_nodenum;295 296	if (!pnp_bios_present())297		return PNP_FUNCTION_NOT_SUPPORTED;298	if (!boot && pnpbios_dont_use_current_config)299		return PNP_FUNCTION_NOT_SUPPORTED;300	tmp_nodenum = *nodenum;301	status = call_pnp_bios(PNP_GET_SYS_DEV_NODE, 0, PNP_TS1, 0, PNP_TS2,302			       boot ? 2 : 1, PNP_DS, 0, &tmp_nodenum,303			       sizeof(tmp_nodenum), data, 65536);304	*nodenum = tmp_nodenum;305	return status;306}307 308int pnp_bios_get_dev_node(u8 *nodenum, char boot, struct pnp_bios_node *data)309{310	int status;311 312	status = __pnp_bios_get_dev_node(nodenum, boot, data);313	if (status)314		pnpbios_print_status("get_dev_node", status);315	return status;316}317 318/*319 * Call PnP BIOS with function 0x02, "set system device node"320 * Input: *nodenum = desired node, 321 *        boot = whether to set nonvolatile boot (!=0)322 *               or volatile current (0) config323 */324static int __pnp_bios_set_dev_node(u8 nodenum, char boot,325				   struct pnp_bios_node *data)326{327	u16 status;328 329	if (!pnp_bios_present())330		return PNP_FUNCTION_NOT_SUPPORTED;331	if (!boot && pnpbios_dont_use_current_config)332		return PNP_FUNCTION_NOT_SUPPORTED;333	status = call_pnp_bios(PNP_SET_SYS_DEV_NODE, nodenum, 0, PNP_TS1,334			       boot ? 2 : 1, PNP_DS, 0, 0, data, 65536, NULL,335			       0);336	return status;337}338 339int pnp_bios_set_dev_node(u8 nodenum, char boot, struct pnp_bios_node *data)340{341	int status;342 343	status = __pnp_bios_set_dev_node(nodenum, boot, data);344	if (status) {345		pnpbios_print_status("set_dev_node", status);346		return status;347	}348	if (!boot) {		/* Update devlist */349		status = pnp_bios_get_dev_node(&nodenum, boot, data);350		if (status)351			return status;352	}353	return status;354}355 356/*357 * Call PnP BIOS with function 0x05, "get docking station information"358 */359int pnp_bios_dock_station_info(struct pnp_docking_station_info *data)360{361	u16 status;362 363	if (!pnp_bios_present())364		return PNP_FUNCTION_NOT_SUPPORTED;365	status = call_pnp_bios(PNP_GET_DOCKING_STATION_INFORMATION, 0, PNP_TS1,366			       PNP_DS, 0, 0, 0, 0, data,367			       sizeof(struct pnp_docking_station_info), NULL,368			       0);369	return status;370}371 372/*373 * Call PnP BIOS with function 0x0a, "get statically allocated resource374 * information"375 */376static int __pnp_bios_get_stat_res(char *info)377{378	u16 status;379 380	if (!pnp_bios_present())381		return PNP_FUNCTION_NOT_SUPPORTED;382	status = call_pnp_bios(PNP_GET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1,383			       PNP_DS, 0, 0, 0, 0, info, 65536, NULL, 0);384	return status;385}386 387int pnp_bios_get_stat_res(char *info)388{389	int status;390 391	status = __pnp_bios_get_stat_res(info);392	if (status)393		pnpbios_print_status("get_stat_res", status);394	return status;395}396 397/*398 * Call PnP BIOS with function 0x40, "get isa pnp configuration structure"399 */400static int __pnp_bios_isapnp_config(struct pnp_isa_config_struc *data)401{402	u16 status;403 404	if (!pnp_bios_present())405		return PNP_FUNCTION_NOT_SUPPORTED;406	status = call_pnp_bios(PNP_GET_PNP_ISA_CONFIG_STRUC, 0, PNP_TS1, PNP_DS,407			       0, 0, 0, 0, data,408			       sizeof(struct pnp_isa_config_struc), NULL, 0);409	return status;410}411 412int pnp_bios_isapnp_config(struct pnp_isa_config_struc *data)413{414	int status;415 416	status = __pnp_bios_isapnp_config(data);417	if (status)418		pnpbios_print_status("isapnp_config", status);419	return status;420}421 422/*423 * Call PnP BIOS with function 0x41, "get ESCD info"424 */425static int __pnp_bios_escd_info(struct escd_info_struc *data)426{427	u16 status;428 429	if (!pnp_bios_present())430		return ESCD_FUNCTION_NOT_SUPPORTED;431	status = call_pnp_bios(PNP_GET_ESCD_INFO, 0, PNP_TS1, 2, PNP_TS1, 4,432			       PNP_TS1, PNP_DS, data,433			       sizeof(struct escd_info_struc), NULL, 0);434	return status;435}436 437int pnp_bios_escd_info(struct escd_info_struc *data)438{439	int status;440 441	status = __pnp_bios_escd_info(data);442	if (status)443		pnpbios_print_status("escd_info", status);444	return status;445}446 447/*448 * Call PnP BIOS function 0x42, "read ESCD"449 * nvram_base is determined by calling escd_info450 */451static int __pnp_bios_read_escd(char *data, u32 nvram_base)452{453	u16 status;454 455	if (!pnp_bios_present())456		return ESCD_FUNCTION_NOT_SUPPORTED;457	status = call_pnp_bios(PNP_READ_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0,458			       0, data, 65536, __va(nvram_base), 65536);459	return status;460}461 462int pnp_bios_read_escd(char *data, u32 nvram_base)463{464	int status;465 466	status = __pnp_bios_read_escd(data, nvram_base);467	if (status)468		pnpbios_print_status("read_escd", status);469	return status;470}471 472void pnpbios_calls_init(union pnp_bios_install_struct *header)473{474	int i;475 476	pnp_bios_callpoint.offset = header->fields.pm16offset;477	pnp_bios_callpoint.segment = PNP_CS16;478 479	for_each_possible_cpu(i) {480		struct desc_struct *gdt = get_cpu_gdt_rw(i);481		if (!gdt)482			continue;483		set_desc_base(&gdt[GDT_ENTRY_PNPBIOS_CS32],484			 (unsigned long)&pnp_bios_callfunc);485		set_desc_base(&gdt[GDT_ENTRY_PNPBIOS_CS16],486			 (unsigned long)__va(header->fields.pm16cseg));487		set_desc_base(&gdt[GDT_ENTRY_PNPBIOS_DS],488			 (unsigned long)__va(header->fields.pm16dseg));489	}490}491