162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * bioscalls.c - the lowlevel layer of the PnPBIOS driver
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/types.h>
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/init.h>
962306a36Sopenharmony_ci#include <linux/linkage.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/device.h>
1262306a36Sopenharmony_ci#include <linux/pnp.h>
1362306a36Sopenharmony_ci#include <linux/mm.h>
1462306a36Sopenharmony_ci#include <linux/smp.h>
1562306a36Sopenharmony_ci#include <linux/kmod.h>
1662306a36Sopenharmony_ci#include <linux/completion.h>
1762306a36Sopenharmony_ci#include <linux/spinlock.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <asm/page.h>
2062306a36Sopenharmony_ci#include <asm/desc.h>
2162306a36Sopenharmony_ci#include <asm/byteorder.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "pnpbios.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci__visible struct {
2662306a36Sopenharmony_ci	u16 offset;
2762306a36Sopenharmony_ci	u16 segment;
2862306a36Sopenharmony_ci} pnp_bios_callpoint;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*
3162306a36Sopenharmony_ci * These are some opcodes for a "static asmlinkage"
3262306a36Sopenharmony_ci * As this code is *not* executed inside the linux kernel segment, but in a
3362306a36Sopenharmony_ci * alias at offset 0, we need a far return that can not be compiled by
3462306a36Sopenharmony_ci * default (please, prove me wrong! this is *really* ugly!)
3562306a36Sopenharmony_ci * This is the only way to get the bios to return into the kernel code,
3662306a36Sopenharmony_ci * because the bios code runs in 16 bit protected mode and therefore can only
3762306a36Sopenharmony_ci * return to the caller if the call is within the first 64kB, and the linux
3862306a36Sopenharmony_ci * kernel begins at offset 3GB...
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ciasmlinkage __visible void pnp_bios_callfunc(void);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci__asm__(".text			\n"
4462306a36Sopenharmony_ci	__ALIGN_STR "\n"
4562306a36Sopenharmony_ci	".globl pnp_bios_callfunc\n"
4662306a36Sopenharmony_ci	"pnp_bios_callfunc:\n"
4762306a36Sopenharmony_ci	"	pushl %edx	\n"
4862306a36Sopenharmony_ci	"	pushl %ecx	\n"
4962306a36Sopenharmony_ci	"	pushl %ebx	\n"
5062306a36Sopenharmony_ci	"	pushl %eax	\n"
5162306a36Sopenharmony_ci	"	lcallw *pnp_bios_callpoint\n"
5262306a36Sopenharmony_ci	"	addl $16, %esp	\n"
5362306a36Sopenharmony_ci	"	lret		\n"
5462306a36Sopenharmony_ci	".previous		\n");
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define Q2_SET_SEL(cpu, selname, address, size) \
5762306a36Sopenharmony_cido { \
5862306a36Sopenharmony_ci	struct desc_struct *gdt = get_cpu_gdt_rw((cpu)); \
5962306a36Sopenharmony_ci	set_desc_base(&gdt[(selname) >> 3], (u32)(address)); \
6062306a36Sopenharmony_ci	set_desc_limit(&gdt[(selname) >> 3], (size) - 1); \
6162306a36Sopenharmony_ci} while(0)
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic struct desc_struct bad_bios_desc = GDT_ENTRY_INIT(0x4092,
6462306a36Sopenharmony_ci			(unsigned long)__va(0x400UL), PAGE_SIZE - 0x400 - 1);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/*
6762306a36Sopenharmony_ci * At some point we want to use this stack frame pointer to unwind
6862306a36Sopenharmony_ci * after PnP BIOS oopses.
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci__visible u32 pnp_bios_fault_esp;
7262306a36Sopenharmony_ci__visible u32 pnp_bios_fault_eip;
7362306a36Sopenharmony_ci__visible u32 pnp_bios_is_utter_crap = 0;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(pnp_bios_lock);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/*
7862306a36Sopenharmony_ci * Support Functions
7962306a36Sopenharmony_ci */
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3,
8262306a36Sopenharmony_ci				u16 arg4, u16 arg5, u16 arg6, u16 arg7,
8362306a36Sopenharmony_ci				void *ts1_base, u32 ts1_size,
8462306a36Sopenharmony_ci				void *ts2_base, u32 ts2_size)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	unsigned long flags;
8762306a36Sopenharmony_ci	u16 status;
8862306a36Sopenharmony_ci	struct desc_struct save_desc_40;
8962306a36Sopenharmony_ci	int cpu;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/*
9262306a36Sopenharmony_ci	 * PnP BIOSes are generally not terribly re-entrant.
9362306a36Sopenharmony_ci	 * Also, don't rely on them to save everything correctly.
9462306a36Sopenharmony_ci	 */
9562306a36Sopenharmony_ci	if (pnp_bios_is_utter_crap)
9662306a36Sopenharmony_ci		return PNP_FUNCTION_NOT_SUPPORTED;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	cpu = get_cpu();
9962306a36Sopenharmony_ci	save_desc_40 = get_cpu_gdt_rw(cpu)[0x40 / 8];
10062306a36Sopenharmony_ci	get_cpu_gdt_rw(cpu)[0x40 / 8] = bad_bios_desc;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* On some boxes IRQ's during PnP BIOS calls are deadly.  */
10362306a36Sopenharmony_ci	spin_lock_irqsave(&pnp_bios_lock, flags);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/* The lock prevents us bouncing CPU here */
10662306a36Sopenharmony_ci	if (ts1_size)
10762306a36Sopenharmony_ci		Q2_SET_SEL(smp_processor_id(), PNP_TS1, ts1_base, ts1_size);
10862306a36Sopenharmony_ci	if (ts2_size)
10962306a36Sopenharmony_ci		Q2_SET_SEL(smp_processor_id(), PNP_TS2, ts2_base, ts2_size);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	__asm__ __volatile__("pushl %%ebp\n\t"
11262306a36Sopenharmony_ci			     "pushl %%edi\n\t"
11362306a36Sopenharmony_ci			     "pushl %%esi\n\t"
11462306a36Sopenharmony_ci			     "pushl %%ds\n\t"
11562306a36Sopenharmony_ci			     "pushl %%es\n\t"
11662306a36Sopenharmony_ci			     "pushl %%fs\n\t"
11762306a36Sopenharmony_ci			     "pushl %%gs\n\t"
11862306a36Sopenharmony_ci			     "pushfl\n\t"
11962306a36Sopenharmony_ci			     "movl %%esp, pnp_bios_fault_esp\n\t"
12062306a36Sopenharmony_ci			     "movl $1f, pnp_bios_fault_eip\n\t"
12162306a36Sopenharmony_ci			     "lcall %5,%6\n\t"
12262306a36Sopenharmony_ci			     "1:popfl\n\t"
12362306a36Sopenharmony_ci			     "popl %%gs\n\t"
12462306a36Sopenharmony_ci			     "popl %%fs\n\t"
12562306a36Sopenharmony_ci			     "popl %%es\n\t"
12662306a36Sopenharmony_ci			     "popl %%ds\n\t"
12762306a36Sopenharmony_ci			     "popl %%esi\n\t"
12862306a36Sopenharmony_ci			     "popl %%edi\n\t"
12962306a36Sopenharmony_ci			     "popl %%ebp\n\t":"=a"(status)
13062306a36Sopenharmony_ci			     :"0"((func) | (((u32) arg1) << 16)),
13162306a36Sopenharmony_ci			     "b"((arg2) | (((u32) arg3) << 16)),
13262306a36Sopenharmony_ci			     "c"((arg4) | (((u32) arg5) << 16)),
13362306a36Sopenharmony_ci			     "d"((arg6) | (((u32) arg7) << 16)),
13462306a36Sopenharmony_ci			     "i"(PNP_CS32), "i"(0)
13562306a36Sopenharmony_ci			     :"memory");
13662306a36Sopenharmony_ci	spin_unlock_irqrestore(&pnp_bios_lock, flags);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	get_cpu_gdt_rw(cpu)[0x40 / 8] = save_desc_40;
13962306a36Sopenharmony_ci	put_cpu();
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* If we get here and this is set then the PnP BIOS faulted on us. */
14262306a36Sopenharmony_ci	if (pnp_bios_is_utter_crap) {
14362306a36Sopenharmony_ci		printk(KERN_ERR
14462306a36Sopenharmony_ci		       "PnPBIOS: Warning! Your PnP BIOS caused a fatal error. Attempting to continue\n");
14562306a36Sopenharmony_ci		printk(KERN_ERR
14662306a36Sopenharmony_ci		       "PnPBIOS: You may need to reboot with the \"pnpbios=off\" option to operate stably\n");
14762306a36Sopenharmony_ci		printk(KERN_ERR
14862306a36Sopenharmony_ci		       "PnPBIOS: Check with your vendor for an updated BIOS\n");
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return status;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_civoid pnpbios_print_status(const char *module, u16 status)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	switch (status) {
15762306a36Sopenharmony_ci	case PNP_SUCCESS:
15862306a36Sopenharmony_ci		printk(KERN_ERR "PnPBIOS: %s: function successful\n", module);
15962306a36Sopenharmony_ci		break;
16062306a36Sopenharmony_ci	case PNP_NOT_SET_STATICALLY:
16162306a36Sopenharmony_ci		printk(KERN_ERR "PnPBIOS: %s: unable to set static resources\n",
16262306a36Sopenharmony_ci		       module);
16362306a36Sopenharmony_ci		break;
16462306a36Sopenharmony_ci	case PNP_UNKNOWN_FUNCTION:
16562306a36Sopenharmony_ci		printk(KERN_ERR "PnPBIOS: %s: invalid function number passed\n",
16662306a36Sopenharmony_ci		       module);
16762306a36Sopenharmony_ci		break;
16862306a36Sopenharmony_ci	case PNP_FUNCTION_NOT_SUPPORTED:
16962306a36Sopenharmony_ci		printk(KERN_ERR
17062306a36Sopenharmony_ci		       "PnPBIOS: %s: function not supported on this system\n",
17162306a36Sopenharmony_ci		       module);
17262306a36Sopenharmony_ci		break;
17362306a36Sopenharmony_ci	case PNP_INVALID_HANDLE:
17462306a36Sopenharmony_ci		printk(KERN_ERR "PnPBIOS: %s: invalid handle\n", module);
17562306a36Sopenharmony_ci		break;
17662306a36Sopenharmony_ci	case PNP_BAD_PARAMETER:
17762306a36Sopenharmony_ci		printk(KERN_ERR "PnPBIOS: %s: invalid parameters were passed\n",
17862306a36Sopenharmony_ci		       module);
17962306a36Sopenharmony_ci		break;
18062306a36Sopenharmony_ci	case PNP_SET_FAILED:
18162306a36Sopenharmony_ci		printk(KERN_ERR "PnPBIOS: %s: unable to set resources\n",
18262306a36Sopenharmony_ci		       module);
18362306a36Sopenharmony_ci		break;
18462306a36Sopenharmony_ci	case PNP_EVENTS_NOT_PENDING:
18562306a36Sopenharmony_ci		printk(KERN_ERR "PnPBIOS: %s: no events are pending\n", module);
18662306a36Sopenharmony_ci		break;
18762306a36Sopenharmony_ci	case PNP_SYSTEM_NOT_DOCKED:
18862306a36Sopenharmony_ci		printk(KERN_ERR "PnPBIOS: %s: the system is not docked\n",
18962306a36Sopenharmony_ci		       module);
19062306a36Sopenharmony_ci		break;
19162306a36Sopenharmony_ci	case PNP_NO_ISA_PNP_CARDS:
19262306a36Sopenharmony_ci		printk(KERN_ERR
19362306a36Sopenharmony_ci		       "PnPBIOS: %s: no isapnp cards are installed on this system\n",
19462306a36Sopenharmony_ci		       module);
19562306a36Sopenharmony_ci		break;
19662306a36Sopenharmony_ci	case PNP_UNABLE_TO_DETERMINE_DOCK_CAPABILITIES:
19762306a36Sopenharmony_ci		printk(KERN_ERR
19862306a36Sopenharmony_ci		       "PnPBIOS: %s: cannot determine the capabilities of the docking station\n",
19962306a36Sopenharmony_ci		       module);
20062306a36Sopenharmony_ci		break;
20162306a36Sopenharmony_ci	case PNP_CONFIG_CHANGE_FAILED_NO_BATTERY:
20262306a36Sopenharmony_ci		printk(KERN_ERR
20362306a36Sopenharmony_ci		       "PnPBIOS: %s: unable to undock, the system does not have a battery\n",
20462306a36Sopenharmony_ci		       module);
20562306a36Sopenharmony_ci		break;
20662306a36Sopenharmony_ci	case PNP_CONFIG_CHANGE_FAILED_RESOURCE_CONFLICT:
20762306a36Sopenharmony_ci		printk(KERN_ERR
20862306a36Sopenharmony_ci		       "PnPBIOS: %s: could not dock due to resource conflicts\n",
20962306a36Sopenharmony_ci		       module);
21062306a36Sopenharmony_ci		break;
21162306a36Sopenharmony_ci	case PNP_BUFFER_TOO_SMALL:
21262306a36Sopenharmony_ci		printk(KERN_ERR "PnPBIOS: %s: the buffer passed is too small\n",
21362306a36Sopenharmony_ci		       module);
21462306a36Sopenharmony_ci		break;
21562306a36Sopenharmony_ci	case PNP_USE_ESCD_SUPPORT:
21662306a36Sopenharmony_ci		printk(KERN_ERR "PnPBIOS: %s: use ESCD instead\n", module);
21762306a36Sopenharmony_ci		break;
21862306a36Sopenharmony_ci	case PNP_MESSAGE_NOT_SUPPORTED:
21962306a36Sopenharmony_ci		printk(KERN_ERR "PnPBIOS: %s: the message is unsupported\n",
22062306a36Sopenharmony_ci		       module);
22162306a36Sopenharmony_ci		break;
22262306a36Sopenharmony_ci	case PNP_HARDWARE_ERROR:
22362306a36Sopenharmony_ci		printk(KERN_ERR "PnPBIOS: %s: a hardware failure has occurred\n",
22462306a36Sopenharmony_ci		       module);
22562306a36Sopenharmony_ci		break;
22662306a36Sopenharmony_ci	default:
22762306a36Sopenharmony_ci		printk(KERN_ERR "PnPBIOS: %s: unexpected status 0x%x\n", module,
22862306a36Sopenharmony_ci		       status);
22962306a36Sopenharmony_ci		break;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/*
23462306a36Sopenharmony_ci * PnP BIOS Low Level Calls
23562306a36Sopenharmony_ci */
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci#define PNP_GET_NUM_SYS_DEV_NODES		0x00
23862306a36Sopenharmony_ci#define PNP_GET_SYS_DEV_NODE			0x01
23962306a36Sopenharmony_ci#define PNP_SET_SYS_DEV_NODE			0x02
24062306a36Sopenharmony_ci#define PNP_GET_EVENT				0x03
24162306a36Sopenharmony_ci#define PNP_SEND_MESSAGE			0x04
24262306a36Sopenharmony_ci#define PNP_GET_DOCKING_STATION_INFORMATION	0x05
24362306a36Sopenharmony_ci#define PNP_SET_STATIC_ALLOCED_RES_INFO		0x09
24462306a36Sopenharmony_ci#define PNP_GET_STATIC_ALLOCED_RES_INFO		0x0a
24562306a36Sopenharmony_ci#define PNP_GET_APM_ID_TABLE			0x0b
24662306a36Sopenharmony_ci#define PNP_GET_PNP_ISA_CONFIG_STRUC		0x40
24762306a36Sopenharmony_ci#define PNP_GET_ESCD_INFO			0x41
24862306a36Sopenharmony_ci#define PNP_READ_ESCD				0x42
24962306a36Sopenharmony_ci#define PNP_WRITE_ESCD				0x43
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci/*
25262306a36Sopenharmony_ci * Call PnP BIOS with function 0x00, "get number of system device nodes"
25362306a36Sopenharmony_ci */
25462306a36Sopenharmony_cistatic int __pnp_bios_dev_node_info(struct pnp_dev_node_info *data)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	u16 status;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (!pnp_bios_present())
25962306a36Sopenharmony_ci		return PNP_FUNCTION_NOT_SUPPORTED;
26062306a36Sopenharmony_ci	status = call_pnp_bios(PNP_GET_NUM_SYS_DEV_NODES, 0, PNP_TS1, 2,
26162306a36Sopenharmony_ci			       PNP_TS1, PNP_DS, 0, 0, data,
26262306a36Sopenharmony_ci			       sizeof(struct pnp_dev_node_info), NULL, 0);
26362306a36Sopenharmony_ci	data->no_nodes &= 0xff;
26462306a36Sopenharmony_ci	return status;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ciint pnp_bios_dev_node_info(struct pnp_dev_node_info *data)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	int status = __pnp_bios_dev_node_info(data);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (status)
27262306a36Sopenharmony_ci		pnpbios_print_status("dev_node_info", status);
27362306a36Sopenharmony_ci	return status;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/*
27762306a36Sopenharmony_ci * Note that some PnP BIOSes (e.g., on Sony Vaio laptops) die a horrible
27862306a36Sopenharmony_ci * death if they are asked to access the "current" configuration.
27962306a36Sopenharmony_ci * Therefore, if it's a matter of indifference, it's better to call
28062306a36Sopenharmony_ci * get_dev_node() and set_dev_node() with boot=1 rather than with boot=0.
28162306a36Sopenharmony_ci */
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci/*
28462306a36Sopenharmony_ci * Call PnP BIOS with function 0x01, "get system device node"
28562306a36Sopenharmony_ci * Input: *nodenum = desired node,
28662306a36Sopenharmony_ci *        boot = whether to get nonvolatile boot (!=0)
28762306a36Sopenharmony_ci *               or volatile current (0) config
28862306a36Sopenharmony_ci * Output: *nodenum=next node or 0xff if no more nodes
28962306a36Sopenharmony_ci */
29062306a36Sopenharmony_cistatic int __pnp_bios_get_dev_node(u8 *nodenum, char boot,
29162306a36Sopenharmony_ci				   struct pnp_bios_node *data)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	u16 status;
29462306a36Sopenharmony_ci	u16 tmp_nodenum;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (!pnp_bios_present())
29762306a36Sopenharmony_ci		return PNP_FUNCTION_NOT_SUPPORTED;
29862306a36Sopenharmony_ci	if (!boot && pnpbios_dont_use_current_config)
29962306a36Sopenharmony_ci		return PNP_FUNCTION_NOT_SUPPORTED;
30062306a36Sopenharmony_ci	tmp_nodenum = *nodenum;
30162306a36Sopenharmony_ci	status = call_pnp_bios(PNP_GET_SYS_DEV_NODE, 0, PNP_TS1, 0, PNP_TS2,
30262306a36Sopenharmony_ci			       boot ? 2 : 1, PNP_DS, 0, &tmp_nodenum,
30362306a36Sopenharmony_ci			       sizeof(tmp_nodenum), data, 65536);
30462306a36Sopenharmony_ci	*nodenum = tmp_nodenum;
30562306a36Sopenharmony_ci	return status;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ciint pnp_bios_get_dev_node(u8 *nodenum, char boot, struct pnp_bios_node *data)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	int status;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	status = __pnp_bios_get_dev_node(nodenum, boot, data);
31362306a36Sopenharmony_ci	if (status)
31462306a36Sopenharmony_ci		pnpbios_print_status("get_dev_node", status);
31562306a36Sopenharmony_ci	return status;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci/*
31962306a36Sopenharmony_ci * Call PnP BIOS with function 0x02, "set system device node"
32062306a36Sopenharmony_ci * Input: *nodenum = desired node,
32162306a36Sopenharmony_ci *        boot = whether to set nonvolatile boot (!=0)
32262306a36Sopenharmony_ci *               or volatile current (0) config
32362306a36Sopenharmony_ci */
32462306a36Sopenharmony_cistatic int __pnp_bios_set_dev_node(u8 nodenum, char boot,
32562306a36Sopenharmony_ci				   struct pnp_bios_node *data)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	u16 status;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (!pnp_bios_present())
33062306a36Sopenharmony_ci		return PNP_FUNCTION_NOT_SUPPORTED;
33162306a36Sopenharmony_ci	if (!boot && pnpbios_dont_use_current_config)
33262306a36Sopenharmony_ci		return PNP_FUNCTION_NOT_SUPPORTED;
33362306a36Sopenharmony_ci	status = call_pnp_bios(PNP_SET_SYS_DEV_NODE, nodenum, 0, PNP_TS1,
33462306a36Sopenharmony_ci			       boot ? 2 : 1, PNP_DS, 0, 0, data, 65536, NULL,
33562306a36Sopenharmony_ci			       0);
33662306a36Sopenharmony_ci	return status;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ciint pnp_bios_set_dev_node(u8 nodenum, char boot, struct pnp_bios_node *data)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	int status;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	status = __pnp_bios_set_dev_node(nodenum, boot, data);
34462306a36Sopenharmony_ci	if (status) {
34562306a36Sopenharmony_ci		pnpbios_print_status("set_dev_node", status);
34662306a36Sopenharmony_ci		return status;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci	if (!boot) {		/* Update devlist */
34962306a36Sopenharmony_ci		status = pnp_bios_get_dev_node(&nodenum, boot, data);
35062306a36Sopenharmony_ci		if (status)
35162306a36Sopenharmony_ci			return status;
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci	return status;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci/*
35762306a36Sopenharmony_ci * Call PnP BIOS with function 0x05, "get docking station information"
35862306a36Sopenharmony_ci */
35962306a36Sopenharmony_ciint pnp_bios_dock_station_info(struct pnp_docking_station_info *data)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	u16 status;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (!pnp_bios_present())
36462306a36Sopenharmony_ci		return PNP_FUNCTION_NOT_SUPPORTED;
36562306a36Sopenharmony_ci	status = call_pnp_bios(PNP_GET_DOCKING_STATION_INFORMATION, 0, PNP_TS1,
36662306a36Sopenharmony_ci			       PNP_DS, 0, 0, 0, 0, data,
36762306a36Sopenharmony_ci			       sizeof(struct pnp_docking_station_info), NULL,
36862306a36Sopenharmony_ci			       0);
36962306a36Sopenharmony_ci	return status;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/*
37362306a36Sopenharmony_ci * Call PnP BIOS with function 0x0a, "get statically allocated resource
37462306a36Sopenharmony_ci * information"
37562306a36Sopenharmony_ci */
37662306a36Sopenharmony_cistatic int __pnp_bios_get_stat_res(char *info)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	u16 status;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	if (!pnp_bios_present())
38162306a36Sopenharmony_ci		return PNP_FUNCTION_NOT_SUPPORTED;
38262306a36Sopenharmony_ci	status = call_pnp_bios(PNP_GET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1,
38362306a36Sopenharmony_ci			       PNP_DS, 0, 0, 0, 0, info, 65536, NULL, 0);
38462306a36Sopenharmony_ci	return status;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ciint pnp_bios_get_stat_res(char *info)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	int status;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	status = __pnp_bios_get_stat_res(info);
39262306a36Sopenharmony_ci	if (status)
39362306a36Sopenharmony_ci		pnpbios_print_status("get_stat_res", status);
39462306a36Sopenharmony_ci	return status;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci/*
39862306a36Sopenharmony_ci * Call PnP BIOS with function 0x40, "get isa pnp configuration structure"
39962306a36Sopenharmony_ci */
40062306a36Sopenharmony_cistatic int __pnp_bios_isapnp_config(struct pnp_isa_config_struc *data)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	u16 status;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (!pnp_bios_present())
40562306a36Sopenharmony_ci		return PNP_FUNCTION_NOT_SUPPORTED;
40662306a36Sopenharmony_ci	status = call_pnp_bios(PNP_GET_PNP_ISA_CONFIG_STRUC, 0, PNP_TS1, PNP_DS,
40762306a36Sopenharmony_ci			       0, 0, 0, 0, data,
40862306a36Sopenharmony_ci			       sizeof(struct pnp_isa_config_struc), NULL, 0);
40962306a36Sopenharmony_ci	return status;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ciint pnp_bios_isapnp_config(struct pnp_isa_config_struc *data)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	int status;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	status = __pnp_bios_isapnp_config(data);
41762306a36Sopenharmony_ci	if (status)
41862306a36Sopenharmony_ci		pnpbios_print_status("isapnp_config", status);
41962306a36Sopenharmony_ci	return status;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci/*
42362306a36Sopenharmony_ci * Call PnP BIOS with function 0x41, "get ESCD info"
42462306a36Sopenharmony_ci */
42562306a36Sopenharmony_cistatic int __pnp_bios_escd_info(struct escd_info_struc *data)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	u16 status;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (!pnp_bios_present())
43062306a36Sopenharmony_ci		return ESCD_FUNCTION_NOT_SUPPORTED;
43162306a36Sopenharmony_ci	status = call_pnp_bios(PNP_GET_ESCD_INFO, 0, PNP_TS1, 2, PNP_TS1, 4,
43262306a36Sopenharmony_ci			       PNP_TS1, PNP_DS, data,
43362306a36Sopenharmony_ci			       sizeof(struct escd_info_struc), NULL, 0);
43462306a36Sopenharmony_ci	return status;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ciint pnp_bios_escd_info(struct escd_info_struc *data)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	int status;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	status = __pnp_bios_escd_info(data);
44262306a36Sopenharmony_ci	if (status)
44362306a36Sopenharmony_ci		pnpbios_print_status("escd_info", status);
44462306a36Sopenharmony_ci	return status;
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci/*
44862306a36Sopenharmony_ci * Call PnP BIOS function 0x42, "read ESCD"
44962306a36Sopenharmony_ci * nvram_base is determined by calling escd_info
45062306a36Sopenharmony_ci */
45162306a36Sopenharmony_cistatic int __pnp_bios_read_escd(char *data, u32 nvram_base)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	u16 status;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (!pnp_bios_present())
45662306a36Sopenharmony_ci		return ESCD_FUNCTION_NOT_SUPPORTED;
45762306a36Sopenharmony_ci	status = call_pnp_bios(PNP_READ_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0,
45862306a36Sopenharmony_ci			       0, data, 65536, __va(nvram_base), 65536);
45962306a36Sopenharmony_ci	return status;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ciint pnp_bios_read_escd(char *data, u32 nvram_base)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	int status;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	status = __pnp_bios_read_escd(data, nvram_base);
46762306a36Sopenharmony_ci	if (status)
46862306a36Sopenharmony_ci		pnpbios_print_status("read_escd", status);
46962306a36Sopenharmony_ci	return status;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_civoid pnpbios_calls_init(union pnp_bios_install_struct *header)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	int i;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	pnp_bios_callpoint.offset = header->fields.pm16offset;
47762306a36Sopenharmony_ci	pnp_bios_callpoint.segment = PNP_CS16;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	for_each_possible_cpu(i) {
48062306a36Sopenharmony_ci		struct desc_struct *gdt = get_cpu_gdt_rw(i);
48162306a36Sopenharmony_ci		if (!gdt)
48262306a36Sopenharmony_ci			continue;
48362306a36Sopenharmony_ci		set_desc_base(&gdt[GDT_ENTRY_PNPBIOS_CS32],
48462306a36Sopenharmony_ci			 (unsigned long)&pnp_bios_callfunc);
48562306a36Sopenharmony_ci		set_desc_base(&gdt[GDT_ENTRY_PNPBIOS_CS16],
48662306a36Sopenharmony_ci			 (unsigned long)__va(header->fields.pm16cseg));
48762306a36Sopenharmony_ci		set_desc_base(&gdt[GDT_ENTRY_PNPBIOS_DS],
48862306a36Sopenharmony_ci			 (unsigned long)__va(header->fields.pm16dseg));
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci}
491