linux/drivers/pnp/pnpbios/bioscalls.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * bioscalls.c - the lowlevel layer of the PnPBIOS driver
   4 */
   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 a
  33 * alias at offset 0, we need a far return that can not be compiled by
  34 * 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 only
  37 * return to the caller if the call is within the first 64kB, and the linux
  38 * 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(0x4092,
  64                        (unsigned long)__va(0x400UL), PAGE_SIZE - 0x400 - 1);
  65
  66/*
  67 * At some point we want to use this stack frame pointer to unwind
  68 * 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 Functions
  79 */
  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_ERR
 144                       "PnPBIOS: Warning! Your PnP BIOS caused a fatal error. Attempting to continue\n");
 145                printk(KERN_ERR
 146                       "PnPBIOS: You may need to reboot with the \"pnpbios=off\" option to operate stably\n");
 147                printk(KERN_ERR
 148                       "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_ERR
 170                       "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_ERR
 193                       "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_ERR
 198                       "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_ERR
 203                       "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_ERR
 208                       "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 Calls
 235 */
 236
 237#define PNP_GET_NUM_SYS_DEV_NODES               0x00
 238#define PNP_GET_SYS_DEV_NODE                    0x01
 239#define PNP_SET_SYS_DEV_NODE                    0x02
 240#define PNP_GET_EVENT                           0x03
 241#define PNP_SEND_MESSAGE                        0x04
 242#define PNP_GET_DOCKING_STATION_INFORMATION     0x05
 243#define PNP_SET_STATIC_ALLOCED_RES_INFO         0x09
 244#define PNP_GET_STATIC_ALLOCED_RES_INFO         0x0a
 245#define PNP_GET_APM_ID_TABLE                    0x0b
 246#define PNP_GET_PNP_ISA_CONFIG_STRUC            0x40
 247#define PNP_GET_ESCD_INFO                       0x41
 248#define PNP_READ_ESCD                           0x42
 249#define PNP_WRITE_ESCD                          0x43
 250
 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 horrible
 278 * death if they are asked to access the "current" configuration.
 279 * Therefore, if it's a matter of indifference, it's better to call
 280 * 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) config
 288 * Output: *nodenum=next node or 0xff if no more nodes
 289 */
 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) config
 323 */
 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 resource
 374 * 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_info
 450 */
 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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.