18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * bioscalls.c - the lowlevel layer of the PnPBIOS driver 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/types.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/linkage.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/pnp.h> 138c2ecf20Sopenharmony_ci#include <linux/mm.h> 148c2ecf20Sopenharmony_ci#include <linux/smp.h> 158c2ecf20Sopenharmony_ci#include <linux/kmod.h> 168c2ecf20Sopenharmony_ci#include <linux/completion.h> 178c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <asm/page.h> 208c2ecf20Sopenharmony_ci#include <asm/desc.h> 218c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "pnpbios.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci__visible struct { 268c2ecf20Sopenharmony_ci u16 offset; 278c2ecf20Sopenharmony_ci u16 segment; 288c2ecf20Sopenharmony_ci} pnp_bios_callpoint; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * These are some opcodes for a "static asmlinkage" 328c2ecf20Sopenharmony_ci * As this code is *not* executed inside the linux kernel segment, but in a 338c2ecf20Sopenharmony_ci * alias at offset 0, we need a far return that can not be compiled by 348c2ecf20Sopenharmony_ci * default (please, prove me wrong! this is *really* ugly!) 358c2ecf20Sopenharmony_ci * This is the only way to get the bios to return into the kernel code, 368c2ecf20Sopenharmony_ci * because the bios code runs in 16 bit protected mode and therefore can only 378c2ecf20Sopenharmony_ci * return to the caller if the call is within the first 64kB, and the linux 388c2ecf20Sopenharmony_ci * kernel begins at offset 3GB... 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ciasmlinkage __visible void pnp_bios_callfunc(void); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci__asm__(".text \n" 448c2ecf20Sopenharmony_ci __ALIGN_STR "\n" 458c2ecf20Sopenharmony_ci ".globl pnp_bios_callfunc\n" 468c2ecf20Sopenharmony_ci "pnp_bios_callfunc:\n" 478c2ecf20Sopenharmony_ci " pushl %edx \n" 488c2ecf20Sopenharmony_ci " pushl %ecx \n" 498c2ecf20Sopenharmony_ci " pushl %ebx \n" 508c2ecf20Sopenharmony_ci " pushl %eax \n" 518c2ecf20Sopenharmony_ci " lcallw *pnp_bios_callpoint\n" 528c2ecf20Sopenharmony_ci " addl $16, %esp \n" 538c2ecf20Sopenharmony_ci " lret \n" 548c2ecf20Sopenharmony_ci ".previous \n"); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define Q2_SET_SEL(cpu, selname, address, size) \ 578c2ecf20Sopenharmony_cido { \ 588c2ecf20Sopenharmony_ci struct desc_struct *gdt = get_cpu_gdt_rw((cpu)); \ 598c2ecf20Sopenharmony_ci set_desc_base(&gdt[(selname) >> 3], (u32)(address)); \ 608c2ecf20Sopenharmony_ci set_desc_limit(&gdt[(selname) >> 3], (size) - 1); \ 618c2ecf20Sopenharmony_ci} while(0) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic struct desc_struct bad_bios_desc = GDT_ENTRY_INIT(0x4092, 648c2ecf20Sopenharmony_ci (unsigned long)__va(0x400UL), PAGE_SIZE - 0x400 - 1); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* 678c2ecf20Sopenharmony_ci * At some point we want to use this stack frame pointer to unwind 688c2ecf20Sopenharmony_ci * after PnP BIOS oopses. 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci__visible u32 pnp_bios_fault_esp; 728c2ecf20Sopenharmony_ci__visible u32 pnp_bios_fault_eip; 738c2ecf20Sopenharmony_ci__visible u32 pnp_bios_is_utter_crap = 0; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic spinlock_t pnp_bios_lock; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* 788c2ecf20Sopenharmony_ci * Support Functions 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3, 828c2ecf20Sopenharmony_ci u16 arg4, u16 arg5, u16 arg6, u16 arg7, 838c2ecf20Sopenharmony_ci void *ts1_base, u32 ts1_size, 848c2ecf20Sopenharmony_ci void *ts2_base, u32 ts2_size) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci unsigned long flags; 878c2ecf20Sopenharmony_ci u16 status; 888c2ecf20Sopenharmony_ci struct desc_struct save_desc_40; 898c2ecf20Sopenharmony_ci int cpu; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* 928c2ecf20Sopenharmony_ci * PnP BIOSes are generally not terribly re-entrant. 938c2ecf20Sopenharmony_ci * Also, don't rely on them to save everything correctly. 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_ci if (pnp_bios_is_utter_crap) 968c2ecf20Sopenharmony_ci return PNP_FUNCTION_NOT_SUPPORTED; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci cpu = get_cpu(); 998c2ecf20Sopenharmony_ci save_desc_40 = get_cpu_gdt_rw(cpu)[0x40 / 8]; 1008c2ecf20Sopenharmony_ci get_cpu_gdt_rw(cpu)[0x40 / 8] = bad_bios_desc; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* On some boxes IRQ's during PnP BIOS calls are deadly. */ 1038c2ecf20Sopenharmony_ci spin_lock_irqsave(&pnp_bios_lock, flags); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* The lock prevents us bouncing CPU here */ 1068c2ecf20Sopenharmony_ci if (ts1_size) 1078c2ecf20Sopenharmony_ci Q2_SET_SEL(smp_processor_id(), PNP_TS1, ts1_base, ts1_size); 1088c2ecf20Sopenharmony_ci if (ts2_size) 1098c2ecf20Sopenharmony_ci Q2_SET_SEL(smp_processor_id(), PNP_TS2, ts2_base, ts2_size); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci __asm__ __volatile__("pushl %%ebp\n\t" 1128c2ecf20Sopenharmony_ci "pushl %%edi\n\t" 1138c2ecf20Sopenharmony_ci "pushl %%esi\n\t" 1148c2ecf20Sopenharmony_ci "pushl %%ds\n\t" 1158c2ecf20Sopenharmony_ci "pushl %%es\n\t" 1168c2ecf20Sopenharmony_ci "pushl %%fs\n\t" 1178c2ecf20Sopenharmony_ci "pushl %%gs\n\t" 1188c2ecf20Sopenharmony_ci "pushfl\n\t" 1198c2ecf20Sopenharmony_ci "movl %%esp, pnp_bios_fault_esp\n\t" 1208c2ecf20Sopenharmony_ci "movl $1f, pnp_bios_fault_eip\n\t" 1218c2ecf20Sopenharmony_ci "lcall %5,%6\n\t" 1228c2ecf20Sopenharmony_ci "1:popfl\n\t" 1238c2ecf20Sopenharmony_ci "popl %%gs\n\t" 1248c2ecf20Sopenharmony_ci "popl %%fs\n\t" 1258c2ecf20Sopenharmony_ci "popl %%es\n\t" 1268c2ecf20Sopenharmony_ci "popl %%ds\n\t" 1278c2ecf20Sopenharmony_ci "popl %%esi\n\t" 1288c2ecf20Sopenharmony_ci "popl %%edi\n\t" 1298c2ecf20Sopenharmony_ci "popl %%ebp\n\t":"=a"(status) 1308c2ecf20Sopenharmony_ci :"0"((func) | (((u32) arg1) << 16)), 1318c2ecf20Sopenharmony_ci "b"((arg2) | (((u32) arg3) << 16)), 1328c2ecf20Sopenharmony_ci "c"((arg4) | (((u32) arg5) << 16)), 1338c2ecf20Sopenharmony_ci "d"((arg6) | (((u32) arg7) << 16)), 1348c2ecf20Sopenharmony_ci "i"(PNP_CS32), "i"(0) 1358c2ecf20Sopenharmony_ci :"memory"); 1368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pnp_bios_lock, flags); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci get_cpu_gdt_rw(cpu)[0x40 / 8] = save_desc_40; 1398c2ecf20Sopenharmony_ci put_cpu(); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* If we get here and this is set then the PnP BIOS faulted on us. */ 1428c2ecf20Sopenharmony_ci if (pnp_bios_is_utter_crap) { 1438c2ecf20Sopenharmony_ci printk(KERN_ERR 1448c2ecf20Sopenharmony_ci "PnPBIOS: Warning! Your PnP BIOS caused a fatal error. Attempting to continue\n"); 1458c2ecf20Sopenharmony_ci printk(KERN_ERR 1468c2ecf20Sopenharmony_ci "PnPBIOS: You may need to reboot with the \"pnpbios=off\" option to operate stably\n"); 1478c2ecf20Sopenharmony_ci printk(KERN_ERR 1488c2ecf20Sopenharmony_ci "PnPBIOS: Check with your vendor for an updated BIOS\n"); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return status; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_civoid pnpbios_print_status(const char *module, u16 status) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci switch (status) { 1578c2ecf20Sopenharmony_ci case PNP_SUCCESS: 1588c2ecf20Sopenharmony_ci printk(KERN_ERR "PnPBIOS: %s: function successful\n", module); 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci case PNP_NOT_SET_STATICALLY: 1618c2ecf20Sopenharmony_ci printk(KERN_ERR "PnPBIOS: %s: unable to set static resources\n", 1628c2ecf20Sopenharmony_ci module); 1638c2ecf20Sopenharmony_ci break; 1648c2ecf20Sopenharmony_ci case PNP_UNKNOWN_FUNCTION: 1658c2ecf20Sopenharmony_ci printk(KERN_ERR "PnPBIOS: %s: invalid function number passed\n", 1668c2ecf20Sopenharmony_ci module); 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci case PNP_FUNCTION_NOT_SUPPORTED: 1698c2ecf20Sopenharmony_ci printk(KERN_ERR 1708c2ecf20Sopenharmony_ci "PnPBIOS: %s: function not supported on this system\n", 1718c2ecf20Sopenharmony_ci module); 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci case PNP_INVALID_HANDLE: 1748c2ecf20Sopenharmony_ci printk(KERN_ERR "PnPBIOS: %s: invalid handle\n", module); 1758c2ecf20Sopenharmony_ci break; 1768c2ecf20Sopenharmony_ci case PNP_BAD_PARAMETER: 1778c2ecf20Sopenharmony_ci printk(KERN_ERR "PnPBIOS: %s: invalid parameters were passed\n", 1788c2ecf20Sopenharmony_ci module); 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci case PNP_SET_FAILED: 1818c2ecf20Sopenharmony_ci printk(KERN_ERR "PnPBIOS: %s: unable to set resources\n", 1828c2ecf20Sopenharmony_ci module); 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci case PNP_EVENTS_NOT_PENDING: 1858c2ecf20Sopenharmony_ci printk(KERN_ERR "PnPBIOS: %s: no events are pending\n", module); 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci case PNP_SYSTEM_NOT_DOCKED: 1888c2ecf20Sopenharmony_ci printk(KERN_ERR "PnPBIOS: %s: the system is not docked\n", 1898c2ecf20Sopenharmony_ci module); 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci case PNP_NO_ISA_PNP_CARDS: 1928c2ecf20Sopenharmony_ci printk(KERN_ERR 1938c2ecf20Sopenharmony_ci "PnPBIOS: %s: no isapnp cards are installed on this system\n", 1948c2ecf20Sopenharmony_ci module); 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case PNP_UNABLE_TO_DETERMINE_DOCK_CAPABILITIES: 1978c2ecf20Sopenharmony_ci printk(KERN_ERR 1988c2ecf20Sopenharmony_ci "PnPBIOS: %s: cannot determine the capabilities of the docking station\n", 1998c2ecf20Sopenharmony_ci module); 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci case PNP_CONFIG_CHANGE_FAILED_NO_BATTERY: 2028c2ecf20Sopenharmony_ci printk(KERN_ERR 2038c2ecf20Sopenharmony_ci "PnPBIOS: %s: unable to undock, the system does not have a battery\n", 2048c2ecf20Sopenharmony_ci module); 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci case PNP_CONFIG_CHANGE_FAILED_RESOURCE_CONFLICT: 2078c2ecf20Sopenharmony_ci printk(KERN_ERR 2088c2ecf20Sopenharmony_ci "PnPBIOS: %s: could not dock due to resource conflicts\n", 2098c2ecf20Sopenharmony_ci module); 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci case PNP_BUFFER_TOO_SMALL: 2128c2ecf20Sopenharmony_ci printk(KERN_ERR "PnPBIOS: %s: the buffer passed is too small\n", 2138c2ecf20Sopenharmony_ci module); 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci case PNP_USE_ESCD_SUPPORT: 2168c2ecf20Sopenharmony_ci printk(KERN_ERR "PnPBIOS: %s: use ESCD instead\n", module); 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci case PNP_MESSAGE_NOT_SUPPORTED: 2198c2ecf20Sopenharmony_ci printk(KERN_ERR "PnPBIOS: %s: the message is unsupported\n", 2208c2ecf20Sopenharmony_ci module); 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci case PNP_HARDWARE_ERROR: 2238c2ecf20Sopenharmony_ci printk(KERN_ERR "PnPBIOS: %s: a hardware failure has occurred\n", 2248c2ecf20Sopenharmony_ci module); 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci default: 2278c2ecf20Sopenharmony_ci printk(KERN_ERR "PnPBIOS: %s: unexpected status 0x%x\n", module, 2288c2ecf20Sopenharmony_ci status); 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/* 2348c2ecf20Sopenharmony_ci * PnP BIOS Low Level Calls 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci#define PNP_GET_NUM_SYS_DEV_NODES 0x00 2388c2ecf20Sopenharmony_ci#define PNP_GET_SYS_DEV_NODE 0x01 2398c2ecf20Sopenharmony_ci#define PNP_SET_SYS_DEV_NODE 0x02 2408c2ecf20Sopenharmony_ci#define PNP_GET_EVENT 0x03 2418c2ecf20Sopenharmony_ci#define PNP_SEND_MESSAGE 0x04 2428c2ecf20Sopenharmony_ci#define PNP_GET_DOCKING_STATION_INFORMATION 0x05 2438c2ecf20Sopenharmony_ci#define PNP_SET_STATIC_ALLOCED_RES_INFO 0x09 2448c2ecf20Sopenharmony_ci#define PNP_GET_STATIC_ALLOCED_RES_INFO 0x0a 2458c2ecf20Sopenharmony_ci#define PNP_GET_APM_ID_TABLE 0x0b 2468c2ecf20Sopenharmony_ci#define PNP_GET_PNP_ISA_CONFIG_STRUC 0x40 2478c2ecf20Sopenharmony_ci#define PNP_GET_ESCD_INFO 0x41 2488c2ecf20Sopenharmony_ci#define PNP_READ_ESCD 0x42 2498c2ecf20Sopenharmony_ci#define PNP_WRITE_ESCD 0x43 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* 2528c2ecf20Sopenharmony_ci * Call PnP BIOS with function 0x00, "get number of system device nodes" 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_cistatic int __pnp_bios_dev_node_info(struct pnp_dev_node_info *data) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci u16 status; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (!pnp_bios_present()) 2598c2ecf20Sopenharmony_ci return PNP_FUNCTION_NOT_SUPPORTED; 2608c2ecf20Sopenharmony_ci status = call_pnp_bios(PNP_GET_NUM_SYS_DEV_NODES, 0, PNP_TS1, 2, 2618c2ecf20Sopenharmony_ci PNP_TS1, PNP_DS, 0, 0, data, 2628c2ecf20Sopenharmony_ci sizeof(struct pnp_dev_node_info), NULL, 0); 2638c2ecf20Sopenharmony_ci data->no_nodes &= 0xff; 2648c2ecf20Sopenharmony_ci return status; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ciint pnp_bios_dev_node_info(struct pnp_dev_node_info *data) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci int status = __pnp_bios_dev_node_info(data); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (status) 2728c2ecf20Sopenharmony_ci pnpbios_print_status("dev_node_info", status); 2738c2ecf20Sopenharmony_ci return status; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/* 2778c2ecf20Sopenharmony_ci * Note that some PnP BIOSes (e.g., on Sony Vaio laptops) die a horrible 2788c2ecf20Sopenharmony_ci * death if they are asked to access the "current" configuration. 2798c2ecf20Sopenharmony_ci * Therefore, if it's a matter of indifference, it's better to call 2808c2ecf20Sopenharmony_ci * get_dev_node() and set_dev_node() with boot=1 rather than with boot=0. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/* 2848c2ecf20Sopenharmony_ci * Call PnP BIOS with function 0x01, "get system device node" 2858c2ecf20Sopenharmony_ci * Input: *nodenum = desired node, 2868c2ecf20Sopenharmony_ci * boot = whether to get nonvolatile boot (!=0) 2878c2ecf20Sopenharmony_ci * or volatile current (0) config 2888c2ecf20Sopenharmony_ci * Output: *nodenum=next node or 0xff if no more nodes 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_cistatic int __pnp_bios_get_dev_node(u8 *nodenum, char boot, 2918c2ecf20Sopenharmony_ci struct pnp_bios_node *data) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci u16 status; 2948c2ecf20Sopenharmony_ci u16 tmp_nodenum; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (!pnp_bios_present()) 2978c2ecf20Sopenharmony_ci return PNP_FUNCTION_NOT_SUPPORTED; 2988c2ecf20Sopenharmony_ci if (!boot && pnpbios_dont_use_current_config) 2998c2ecf20Sopenharmony_ci return PNP_FUNCTION_NOT_SUPPORTED; 3008c2ecf20Sopenharmony_ci tmp_nodenum = *nodenum; 3018c2ecf20Sopenharmony_ci status = call_pnp_bios(PNP_GET_SYS_DEV_NODE, 0, PNP_TS1, 0, PNP_TS2, 3028c2ecf20Sopenharmony_ci boot ? 2 : 1, PNP_DS, 0, &tmp_nodenum, 3038c2ecf20Sopenharmony_ci sizeof(tmp_nodenum), data, 65536); 3048c2ecf20Sopenharmony_ci *nodenum = tmp_nodenum; 3058c2ecf20Sopenharmony_ci return status; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ciint pnp_bios_get_dev_node(u8 *nodenum, char boot, struct pnp_bios_node *data) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci int status; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci status = __pnp_bios_get_dev_node(nodenum, boot, data); 3138c2ecf20Sopenharmony_ci if (status) 3148c2ecf20Sopenharmony_ci pnpbios_print_status("get_dev_node", status); 3158c2ecf20Sopenharmony_ci return status; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci/* 3198c2ecf20Sopenharmony_ci * Call PnP BIOS with function 0x02, "set system device node" 3208c2ecf20Sopenharmony_ci * Input: *nodenum = desired node, 3218c2ecf20Sopenharmony_ci * boot = whether to set nonvolatile boot (!=0) 3228c2ecf20Sopenharmony_ci * or volatile current (0) config 3238c2ecf20Sopenharmony_ci */ 3248c2ecf20Sopenharmony_cistatic int __pnp_bios_set_dev_node(u8 nodenum, char boot, 3258c2ecf20Sopenharmony_ci struct pnp_bios_node *data) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci u16 status; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (!pnp_bios_present()) 3308c2ecf20Sopenharmony_ci return PNP_FUNCTION_NOT_SUPPORTED; 3318c2ecf20Sopenharmony_ci if (!boot && pnpbios_dont_use_current_config) 3328c2ecf20Sopenharmony_ci return PNP_FUNCTION_NOT_SUPPORTED; 3338c2ecf20Sopenharmony_ci status = call_pnp_bios(PNP_SET_SYS_DEV_NODE, nodenum, 0, PNP_TS1, 3348c2ecf20Sopenharmony_ci boot ? 2 : 1, PNP_DS, 0, 0, data, 65536, NULL, 3358c2ecf20Sopenharmony_ci 0); 3368c2ecf20Sopenharmony_ci return status; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ciint pnp_bios_set_dev_node(u8 nodenum, char boot, struct pnp_bios_node *data) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci int status; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci status = __pnp_bios_set_dev_node(nodenum, boot, data); 3448c2ecf20Sopenharmony_ci if (status) { 3458c2ecf20Sopenharmony_ci pnpbios_print_status("set_dev_node", status); 3468c2ecf20Sopenharmony_ci return status; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci if (!boot) { /* Update devlist */ 3498c2ecf20Sopenharmony_ci status = pnp_bios_get_dev_node(&nodenum, boot, data); 3508c2ecf20Sopenharmony_ci if (status) 3518c2ecf20Sopenharmony_ci return status; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci return status; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci/* 3578c2ecf20Sopenharmony_ci * Call PnP BIOS with function 0x05, "get docking station information" 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_ciint pnp_bios_dock_station_info(struct pnp_docking_station_info *data) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci u16 status; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (!pnp_bios_present()) 3648c2ecf20Sopenharmony_ci return PNP_FUNCTION_NOT_SUPPORTED; 3658c2ecf20Sopenharmony_ci status = call_pnp_bios(PNP_GET_DOCKING_STATION_INFORMATION, 0, PNP_TS1, 3668c2ecf20Sopenharmony_ci PNP_DS, 0, 0, 0, 0, data, 3678c2ecf20Sopenharmony_ci sizeof(struct pnp_docking_station_info), NULL, 3688c2ecf20Sopenharmony_ci 0); 3698c2ecf20Sopenharmony_ci return status; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci/* 3738c2ecf20Sopenharmony_ci * Call PnP BIOS with function 0x0a, "get statically allocated resource 3748c2ecf20Sopenharmony_ci * information" 3758c2ecf20Sopenharmony_ci */ 3768c2ecf20Sopenharmony_cistatic int __pnp_bios_get_stat_res(char *info) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci u16 status; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (!pnp_bios_present()) 3818c2ecf20Sopenharmony_ci return PNP_FUNCTION_NOT_SUPPORTED; 3828c2ecf20Sopenharmony_ci status = call_pnp_bios(PNP_GET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, 3838c2ecf20Sopenharmony_ci PNP_DS, 0, 0, 0, 0, info, 65536, NULL, 0); 3848c2ecf20Sopenharmony_ci return status; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ciint pnp_bios_get_stat_res(char *info) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci int status; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci status = __pnp_bios_get_stat_res(info); 3928c2ecf20Sopenharmony_ci if (status) 3938c2ecf20Sopenharmony_ci pnpbios_print_status("get_stat_res", status); 3948c2ecf20Sopenharmony_ci return status; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci/* 3988c2ecf20Sopenharmony_ci * Call PnP BIOS with function 0x40, "get isa pnp configuration structure" 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_cistatic int __pnp_bios_isapnp_config(struct pnp_isa_config_struc *data) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci u16 status; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (!pnp_bios_present()) 4058c2ecf20Sopenharmony_ci return PNP_FUNCTION_NOT_SUPPORTED; 4068c2ecf20Sopenharmony_ci status = call_pnp_bios(PNP_GET_PNP_ISA_CONFIG_STRUC, 0, PNP_TS1, PNP_DS, 4078c2ecf20Sopenharmony_ci 0, 0, 0, 0, data, 4088c2ecf20Sopenharmony_ci sizeof(struct pnp_isa_config_struc), NULL, 0); 4098c2ecf20Sopenharmony_ci return status; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ciint pnp_bios_isapnp_config(struct pnp_isa_config_struc *data) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci int status; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci status = __pnp_bios_isapnp_config(data); 4178c2ecf20Sopenharmony_ci if (status) 4188c2ecf20Sopenharmony_ci pnpbios_print_status("isapnp_config", status); 4198c2ecf20Sopenharmony_ci return status; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci/* 4238c2ecf20Sopenharmony_ci * Call PnP BIOS with function 0x41, "get ESCD info" 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_cistatic int __pnp_bios_escd_info(struct escd_info_struc *data) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci u16 status; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (!pnp_bios_present()) 4308c2ecf20Sopenharmony_ci return ESCD_FUNCTION_NOT_SUPPORTED; 4318c2ecf20Sopenharmony_ci status = call_pnp_bios(PNP_GET_ESCD_INFO, 0, PNP_TS1, 2, PNP_TS1, 4, 4328c2ecf20Sopenharmony_ci PNP_TS1, PNP_DS, data, 4338c2ecf20Sopenharmony_ci sizeof(struct escd_info_struc), NULL, 0); 4348c2ecf20Sopenharmony_ci return status; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ciint pnp_bios_escd_info(struct escd_info_struc *data) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci int status; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci status = __pnp_bios_escd_info(data); 4428c2ecf20Sopenharmony_ci if (status) 4438c2ecf20Sopenharmony_ci pnpbios_print_status("escd_info", status); 4448c2ecf20Sopenharmony_ci return status; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci/* 4488c2ecf20Sopenharmony_ci * Call PnP BIOS function 0x42, "read ESCD" 4498c2ecf20Sopenharmony_ci * nvram_base is determined by calling escd_info 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_cistatic int __pnp_bios_read_escd(char *data, u32 nvram_base) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci u16 status; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (!pnp_bios_present()) 4568c2ecf20Sopenharmony_ci return ESCD_FUNCTION_NOT_SUPPORTED; 4578c2ecf20Sopenharmony_ci status = call_pnp_bios(PNP_READ_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 4588c2ecf20Sopenharmony_ci 0, data, 65536, __va(nvram_base), 65536); 4598c2ecf20Sopenharmony_ci return status; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ciint pnp_bios_read_escd(char *data, u32 nvram_base) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci int status; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci status = __pnp_bios_read_escd(data, nvram_base); 4678c2ecf20Sopenharmony_ci if (status) 4688c2ecf20Sopenharmony_ci pnpbios_print_status("read_escd", status); 4698c2ecf20Sopenharmony_ci return status; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_civoid pnpbios_calls_init(union pnp_bios_install_struct *header) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci int i; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci spin_lock_init(&pnp_bios_lock); 4778c2ecf20Sopenharmony_ci pnp_bios_callpoint.offset = header->fields.pm16offset; 4788c2ecf20Sopenharmony_ci pnp_bios_callpoint.segment = PNP_CS16; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 4818c2ecf20Sopenharmony_ci struct desc_struct *gdt = get_cpu_gdt_rw(i); 4828c2ecf20Sopenharmony_ci if (!gdt) 4838c2ecf20Sopenharmony_ci continue; 4848c2ecf20Sopenharmony_ci set_desc_base(&gdt[GDT_ENTRY_PNPBIOS_CS32], 4858c2ecf20Sopenharmony_ci (unsigned long)&pnp_bios_callfunc); 4868c2ecf20Sopenharmony_ci set_desc_base(&gdt[GDT_ENTRY_PNPBIOS_CS16], 4878c2ecf20Sopenharmony_ci (unsigned long)__va(header->fields.pm16cseg)); 4888c2ecf20Sopenharmony_ci set_desc_base(&gdt[GDT_ENTRY_PNPBIOS_DS], 4898c2ecf20Sopenharmony_ci (unsigned long)__va(header->fields.pm16dseg)); 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci} 492