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