]> bbs.cooldavid.org Git - net-next-2.6.git/blob - drivers/pnp/pnpbios/bioscalls.c
7cb476ed7f9183a0ef54fe2fec241b4fab93960b
[net-next-2.6.git] / drivers / pnp / pnpbios / bioscalls.c
1 /*
2  * bioscalls.c - the lowlevel layer of the PnPBIOS driver
3  *
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/pnpbios.h>
12 #include <linux/device.h>
13 #include <linux/pnp.h>
14 #include <linux/mm.h>
15 #include <linux/smp.h>
16 #include <linux/slab.h>
17 #include <linux/kmod.h>
18 #include <linux/completion.h>
19 #include <linux/spinlock.h>
20
21 #include <asm/page.h>
22 #include <asm/desc.h>
23 #include <asm/system.h>
24 #include <asm/byteorder.h>
25
26 #include "pnpbios.h"
27
28 static struct {
29         u16     offset;
30         u16     segment;
31 } pnp_bios_callpoint;
32
33
34 /* The PnP BIOS entries in the GDT */
35 #define PNP_GDT    (GDT_ENTRY_PNPBIOS_BASE * 8)
36
37 #define PNP_CS32   (PNP_GDT+0x00)       /* segment for calling fn */
38 #define PNP_CS16   (PNP_GDT+0x08)       /* code segment for BIOS */
39 #define PNP_DS     (PNP_GDT+0x10)       /* data segment for BIOS */
40 #define PNP_TS1    (PNP_GDT+0x18)       /* transfer data segment */
41 #define PNP_TS2    (PNP_GDT+0x20)       /* another data segment */
42
43 /*
44  * These are some opcodes for a "static asmlinkage"
45  * As this code is *not* executed inside the linux kernel segment, but in a
46  * alias at offset 0, we need a far return that can not be compiled by
47  * default (please, prove me wrong! this is *really* ugly!)
48  * This is the only way to get the bios to return into the kernel code,
49  * because the bios code runs in 16 bit protected mode and therefore can only
50  * return to the caller if the call is within the first 64kB, and the linux
51  * kernel begins at offset 3GB...
52  */
53
54 asmlinkage void pnp_bios_callfunc(void);
55
56 __asm__(
57         ".text                  \n"
58         __ALIGN_STR "\n"
59         "pnp_bios_callfunc:\n"
60         "       pushl %edx      \n"
61         "       pushl %ecx      \n"
62         "       pushl %ebx      \n"
63         "       pushl %eax      \n"
64         "       lcallw *pnp_bios_callpoint\n"
65         "       addl $16, %esp  \n"
66         "       lret            \n"
67         ".previous              \n"
68 );
69
70 #define Q_SET_SEL(cpu, selname, address, size) \
71 do { \
72 struct desc_struct *gdt = get_cpu_gdt_table((cpu)); \
73 set_base(gdt[(selname) >> 3], __va((u32)(address))); \
74 set_limit(gdt[(selname) >> 3], size); \
75 } while(0)
76
77 #define Q2_SET_SEL(cpu, selname, address, size) \
78 do { \
79 struct desc_struct *gdt = get_cpu_gdt_table((cpu)); \
80 set_base(gdt[(selname) >> 3], (u32)(address)); \
81 set_limit(gdt[(selname) >> 3], size); \
82 } while(0)
83
84 static struct desc_struct bad_bios_desc = { 0, 0x00409200 };
85
86 /*
87  * At some point we want to use this stack frame pointer to unwind
88  * after PnP BIOS oopses.
89  */
90
91 u32 pnp_bios_fault_esp;
92 u32 pnp_bios_fault_eip;
93 u32 pnp_bios_is_utter_crap = 0;
94
95 static spinlock_t pnp_bios_lock;
96
97
98 /*
99  * Support Functions
100  */
101
102 static inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3,
103                                 u16 arg4, u16 arg5, u16 arg6, u16 arg7,
104                                 void *ts1_base, u32 ts1_size,
105                                 void *ts2_base, u32 ts2_size)
106 {
107         unsigned long flags;
108         u16 status;
109         struct desc_struct save_desc_40;
110         int cpu;
111
112         /*
113          * PnP BIOSes are generally not terribly re-entrant.
114          * Also, don't rely on them to save everything correctly.
115          */
116         if(pnp_bios_is_utter_crap)
117                 return PNP_FUNCTION_NOT_SUPPORTED;
118
119         cpu = get_cpu();
120         save_desc_40 = get_cpu_gdt_table(cpu)[0x40 / 8];
121         get_cpu_gdt_table(cpu)[0x40 / 8] = bad_bios_desc;
122
123         /* On some boxes IRQ's during PnP BIOS calls are deadly.  */
124         spin_lock_irqsave(&pnp_bios_lock, flags);
125
126         /* The lock prevents us bouncing CPU here */
127         if (ts1_size)
128                 Q2_SET_SEL(smp_processor_id(), PNP_TS1, ts1_base, ts1_size);
129         if (ts2_size)
130                 Q2_SET_SEL(smp_processor_id(), PNP_TS2, ts2_base, ts2_size);
131
132         __asm__ __volatile__(
133                 "pushl %%ebp\n\t"
134                 "pushl %%edi\n\t"
135                 "pushl %%esi\n\t"
136                 "pushl %%ds\n\t"
137                 "pushl %%es\n\t"
138                 "pushl %%fs\n\t"
139                 "pushl %%gs\n\t"
140                 "pushfl\n\t"
141                 "movl %%esp, pnp_bios_fault_esp\n\t"
142                 "movl $1f, pnp_bios_fault_eip\n\t"
143                 "lcall %5,%6\n\t"
144                 "1:popfl\n\t"
145                 "popl %%gs\n\t"
146                 "popl %%fs\n\t"
147                 "popl %%es\n\t"
148                 "popl %%ds\n\t"
149                 "popl %%esi\n\t"
150                 "popl %%edi\n\t"
151                 "popl %%ebp\n\t"
152                 : "=a" (status)
153                 : "0" ((func) | (((u32)arg1) << 16)),
154                   "b" ((arg2) | (((u32)arg3) << 16)),
155                   "c" ((arg4) | (((u32)arg5) << 16)),
156                   "d" ((arg6) | (((u32)arg7) << 16)),
157                   "i" (PNP_CS32),
158                   "i" (0)
159                 : "memory"
160         );
161         spin_unlock_irqrestore(&pnp_bios_lock, flags);
162
163         get_cpu_gdt_table(cpu)[0x40 / 8] = save_desc_40;
164         put_cpu();
165
166         /* If we get here and this is set then the PnP BIOS faulted on us. */
167         if(pnp_bios_is_utter_crap)
168         {
169                 printk(KERN_ERR "PnPBIOS: Warning! Your PnP BIOS caused a fatal error. Attempting to continue\n");
170                 printk(KERN_ERR "PnPBIOS: You may need to reboot with the \"pnpbios=off\" option to operate stably\n");
171                 printk(KERN_ERR "PnPBIOS: Check with your vendor for an updated BIOS\n");
172         }
173
174         return status;
175 }
176
177 void pnpbios_print_status(const char * module, u16 status)
178 {
179         switch(status) {
180         case PNP_SUCCESS:
181                 printk(KERN_ERR "PnPBIOS: %s: function successful\n", module);
182                 break;
183         case PNP_NOT_SET_STATICALLY:
184                 printk(KERN_ERR "PnPBIOS: %s: unable to set static resources\n", module);
185                 break;
186         case PNP_UNKNOWN_FUNCTION:
187                 printk(KERN_ERR "PnPBIOS: %s: invalid function number passed\n", module);
188                 break;
189         case PNP_FUNCTION_NOT_SUPPORTED:
190                 printk(KERN_ERR "PnPBIOS: %s: function not supported on this system\n", module);
191                 break;
192         case PNP_INVALID_HANDLE:
193                 printk(KERN_ERR "PnPBIOS: %s: invalid handle\n", module);
194                 break;
195         case PNP_BAD_PARAMETER:
196                 printk(KERN_ERR "PnPBIOS: %s: invalid parameters were passed\n", module);
197                 break;
198         case PNP_SET_FAILED:
199                 printk(KERN_ERR "PnPBIOS: %s: unable to set resources\n", module);
200                 break;
201         case PNP_EVENTS_NOT_PENDING:
202                 printk(KERN_ERR "PnPBIOS: %s: no events are pending\n", module);
203                 break;
204         case PNP_SYSTEM_NOT_DOCKED:
205                 printk(KERN_ERR "PnPBIOS: %s: the system is not docked\n", module);
206                 break;
207         case PNP_NO_ISA_PNP_CARDS:
208                 printk(KERN_ERR "PnPBIOS: %s: no isapnp cards are installed on this system\n", module);
209                 break;
210         case PNP_UNABLE_TO_DETERMINE_DOCK_CAPABILITIES:
211                 printk(KERN_ERR "PnPBIOS: %s: cannot determine the capabilities of the docking station\n", module);
212                 break;
213         case PNP_CONFIG_CHANGE_FAILED_NO_BATTERY:
214                 printk(KERN_ERR "PnPBIOS: %s: unable to undock, the system does not have a battery\n", module);
215                 break;
216         case PNP_CONFIG_CHANGE_FAILED_RESOURCE_CONFLICT:
217                 printk(KERN_ERR "PnPBIOS: %s: could not dock due to resource conflicts\n", module);
218                 break;
219         case PNP_BUFFER_TOO_SMALL:
220                 printk(KERN_ERR "PnPBIOS: %s: the buffer passed is too small\n", module);
221                 break;
222         case PNP_USE_ESCD_SUPPORT:
223                 printk(KERN_ERR "PnPBIOS: %s: use ESCD instead\n", module);
224                 break;
225         case PNP_MESSAGE_NOT_SUPPORTED:
226                 printk(KERN_ERR "PnPBIOS: %s: the message is unsupported\n", module);
227                 break;
228         case PNP_HARDWARE_ERROR:
229                 printk(KERN_ERR "PnPBIOS: %s: a hardware failure has occured\n", module);
230                 break;
231         default:
232                 printk(KERN_ERR "PnPBIOS: %s: unexpected status 0x%x\n", module, status);
233                 break;
234         }
235 }
236
237
238 /*
239  * PnP BIOS Low Level Calls
240  */
241
242 #define PNP_GET_NUM_SYS_DEV_NODES               0x00
243 #define PNP_GET_SYS_DEV_NODE                    0x01
244 #define PNP_SET_SYS_DEV_NODE                    0x02
245 #define PNP_GET_EVENT                           0x03
246 #define PNP_SEND_MESSAGE                        0x04
247 #define PNP_GET_DOCKING_STATION_INFORMATION     0x05
248 #define PNP_SET_STATIC_ALLOCED_RES_INFO         0x09
249 #define PNP_GET_STATIC_ALLOCED_RES_INFO         0x0a
250 #define PNP_GET_APM_ID_TABLE                    0x0b
251 #define PNP_GET_PNP_ISA_CONFIG_STRUC            0x40
252 #define PNP_GET_ESCD_INFO                       0x41
253 #define PNP_READ_ESCD                           0x42
254 #define PNP_WRITE_ESCD                          0x43
255
256 /*
257  * Call PnP BIOS with function 0x00, "get number of system device nodes"
258  */
259 static int __pnp_bios_dev_node_info(struct pnp_dev_node_info *data)
260 {
261         u16 status;
262         if (!pnp_bios_present())
263                 return PNP_FUNCTION_NOT_SUPPORTED;
264         status = call_pnp_bios(PNP_GET_NUM_SYS_DEV_NODES, 0, PNP_TS1, 2, PNP_TS1, PNP_DS, 0, 0,
265                                data, sizeof(struct pnp_dev_node_info), NULL, 0);
266         data->no_nodes &= 0xff;
267         return status;
268 }
269
270 int pnp_bios_dev_node_info(struct pnp_dev_node_info *data)
271 {
272         int status = __pnp_bios_dev_node_info( data );
273         if ( status )
274                 pnpbios_print_status( "dev_node_info", status );
275         return status;
276 }
277
278 /*
279  * Note that some PnP BIOSes (e.g., on Sony Vaio laptops) die a horrible
280  * death if they are asked to access the "current" configuration.
281  * Therefore, if it's a matter of indifference, it's better to call
282  * get_dev_node() and set_dev_node() with boot=1 rather than with boot=0.
283  */
284
285 /* 
286  * Call PnP BIOS with function 0x01, "get system device node"
287  * Input: *nodenum = desired node,
288  *        boot = whether to get nonvolatile boot (!=0)
289  *               or volatile current (0) config
290  * Output: *nodenum=next node or 0xff if no more nodes
291  */
292 static int __pnp_bios_get_dev_node(u8 *nodenum, char boot, struct pnp_bios_node *data)
293 {
294         u16 status;
295         if (!pnp_bios_present())
296                 return PNP_FUNCTION_NOT_SUPPORTED;
297         if ( !boot && pnpbios_dont_use_current_config )
298                 return PNP_FUNCTION_NOT_SUPPORTED;
299         status = call_pnp_bios(PNP_GET_SYS_DEV_NODE, 0, PNP_TS1, 0, PNP_TS2, boot ? 2 : 1, PNP_DS, 0,
300                                nodenum, sizeof(char), data, 65536);
301         return status;
302 }
303
304 int pnp_bios_get_dev_node(u8 *nodenum, char boot, struct pnp_bios_node *data)
305 {
306         int status;
307         status =  __pnp_bios_get_dev_node( nodenum, boot, data );
308         if ( status )
309                 pnpbios_print_status( "get_dev_node", status );
310         return status;
311 }
312
313
314 /*
315  * Call PnP BIOS with function 0x02, "set system device node"
316  * Input: *nodenum = desired node, 
317  *        boot = whether to set nonvolatile boot (!=0)
318  *               or volatile current (0) config
319  */
320 static int __pnp_bios_set_dev_node(u8 nodenum, char boot, struct pnp_bios_node *data)
321 {
322         u16 status;
323         if (!pnp_bios_present())
324                 return PNP_FUNCTION_NOT_SUPPORTED;
325         if ( !boot && pnpbios_dont_use_current_config )
326                 return PNP_FUNCTION_NOT_SUPPORTED;
327         status = call_pnp_bios(PNP_SET_SYS_DEV_NODE, nodenum, 0, PNP_TS1, boot ? 2 : 1, PNP_DS, 0, 0,
328                                data, 65536, NULL, 0);
329         return status;
330 }
331
332 int pnp_bios_set_dev_node(u8 nodenum, char boot, struct pnp_bios_node *data)
333 {
334         int status;
335         status =  __pnp_bios_set_dev_node( nodenum, boot, data );
336         if ( status ) {
337                 pnpbios_print_status( "set_dev_node", status );
338                 return status;
339         }
340         if ( !boot ) { /* Update devlist */
341                 status =  pnp_bios_get_dev_node( &nodenum, boot, data );
342                 if ( status )
343                         return status;
344         }
345         return status;
346 }
347
348 #if needed
349 /*
350  * Call PnP BIOS with function 0x03, "get event"
351  */
352 static int pnp_bios_get_event(u16 *event)
353 {
354         u16 status;
355         if (!pnp_bios_present())
356                 return PNP_FUNCTION_NOT_SUPPORTED;
357         status = call_pnp_bios(PNP_GET_EVENT, 0, PNP_TS1, PNP_DS, 0, 0 ,0 ,0,
358                                event, sizeof(u16), NULL, 0);
359         return status;
360 }
361 #endif
362
363 #if needed
364 /*
365  * Call PnP BIOS with function 0x04, "send message"
366  */
367 static int pnp_bios_send_message(u16 message)
368 {
369         u16 status;
370         if (!pnp_bios_present())
371                 return PNP_FUNCTION_NOT_SUPPORTED;
372         status = call_pnp_bios(PNP_SEND_MESSAGE, message, PNP_DS, 0, 0, 0, 0, 0, 0, 0, 0, 0);
373         return status;
374 }
375 #endif
376
377 /*
378  * Call PnP BIOS with function 0x05, "get docking station information"
379  */
380 int pnp_bios_dock_station_info(struct pnp_docking_station_info *data)
381 {
382         u16 status;
383         if (!pnp_bios_present())
384                 return PNP_FUNCTION_NOT_SUPPORTED;
385         status = call_pnp_bios(PNP_GET_DOCKING_STATION_INFORMATION, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0,
386                                data, sizeof(struct pnp_docking_station_info), NULL, 0);
387         return status;
388 }
389
390 #if needed
391 /*
392  * Call PnP BIOS with function 0x09, "set statically allocated resource
393  * information"
394  */
395 static int pnp_bios_set_stat_res(char *info)
396 {
397         u16 status;
398         if (!pnp_bios_present())
399                 return PNP_FUNCTION_NOT_SUPPORTED;
400         status = call_pnp_bios(PNP_SET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0,
401                                info, *((u16 *) info), 0, 0);
402         return status;
403 }
404 #endif
405
406 /*
407  * Call PnP BIOS with function 0x0a, "get statically allocated resource
408  * information"
409  */
410 static int __pnp_bios_get_stat_res(char *info)
411 {
412         u16 status;
413         if (!pnp_bios_present())
414                 return PNP_FUNCTION_NOT_SUPPORTED;
415         status = call_pnp_bios(PNP_GET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0,
416                                info, 65536, NULL, 0);
417         return status;
418 }
419
420 int pnp_bios_get_stat_res(char *info)
421 {
422         int status;
423         status = __pnp_bios_get_stat_res( info );
424         if ( status )
425                 pnpbios_print_status( "get_stat_res", status );
426         return status;
427 }
428
429 #if needed
430 /*
431  * Call PnP BIOS with function 0x0b, "get APM id table"
432  */
433 static int pnp_bios_apm_id_table(char *table, u16 *size)
434 {
435         u16 status;
436         if (!pnp_bios_present())
437                 return PNP_FUNCTION_NOT_SUPPORTED;
438         status = call_pnp_bios(PNP_GET_APM_ID_TABLE, 0, PNP_TS2, 0, PNP_TS1, PNP_DS, 0, 0,
439                                table, *size, size, sizeof(u16));
440         return status;
441 }
442 #endif
443
444 /*
445  * Call PnP BIOS with function 0x40, "get isa pnp configuration structure"
446  */
447 static int __pnp_bios_isapnp_config(struct pnp_isa_config_struc *data)
448 {
449         u16 status;
450         if (!pnp_bios_present())
451                 return PNP_FUNCTION_NOT_SUPPORTED;
452         status = call_pnp_bios(PNP_GET_PNP_ISA_CONFIG_STRUC, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0,
453                                data, sizeof(struct pnp_isa_config_struc), NULL, 0);
454         return status;
455 }
456
457 int pnp_bios_isapnp_config(struct pnp_isa_config_struc *data)
458 {
459         int status;
460         status = __pnp_bios_isapnp_config( data );
461         if ( status )
462                 pnpbios_print_status( "isapnp_config", status );
463         return status;
464 }
465
466 /*
467  * Call PnP BIOS with function 0x41, "get ESCD info"
468  */
469 static int __pnp_bios_escd_info(struct escd_info_struc *data)
470 {
471         u16 status;
472         if (!pnp_bios_present())
473                 return ESCD_FUNCTION_NOT_SUPPORTED;
474         status = call_pnp_bios(PNP_GET_ESCD_INFO, 0, PNP_TS1, 2, PNP_TS1, 4, PNP_TS1, PNP_DS,
475                                data, sizeof(struct escd_info_struc), NULL, 0);
476         return status;
477 }
478
479 int pnp_bios_escd_info(struct escd_info_struc *data)
480 {
481         int status;
482         status = __pnp_bios_escd_info( data );
483         if ( status )
484                 pnpbios_print_status( "escd_info", status );
485         return status;
486 }
487
488 /*
489  * Call PnP BIOS function 0x42, "read ESCD"
490  * nvram_base is determined by calling escd_info
491  */
492 static int __pnp_bios_read_escd(char *data, u32 nvram_base)
493 {
494         u16 status;
495         if (!pnp_bios_present())
496                 return ESCD_FUNCTION_NOT_SUPPORTED;
497         status = call_pnp_bios(PNP_READ_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0,
498                                data, 65536, __va(nvram_base), 65536);
499         return status;
500 }
501
502 int pnp_bios_read_escd(char *data, u32 nvram_base)
503 {
504         int status;
505         status = __pnp_bios_read_escd( data, nvram_base );
506         if ( status )
507                 pnpbios_print_status( "read_escd", status );
508         return status;
509 }
510
511 #if needed
512 /*
513  * Call PnP BIOS function 0x43, "write ESCD"
514  */
515 static int pnp_bios_write_escd(char *data, u32 nvram_base)
516 {
517         u16 status;
518         if (!pnp_bios_present())
519                 return ESCD_FUNCTION_NOT_SUPPORTED;
520         status = call_pnp_bios(PNP_WRITE_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0,
521                                data, 65536, __va(nvram_base), 65536);
522         return status;
523 }
524 #endif
525
526
527 /*
528  * Initialization
529  */
530
531 void pnpbios_calls_init(union pnp_bios_install_struct *header)
532 {
533         int i;
534         spin_lock_init(&pnp_bios_lock);
535         pnp_bios_callpoint.offset = header->fields.pm16offset;
536         pnp_bios_callpoint.segment = PNP_CS16;
537
538         set_base(bad_bios_desc, __va((unsigned long)0x40 << 4));
539         _set_limit((char *)&bad_bios_desc, 4095 - (0x40 << 4));
540         for (i = 0; i < NR_CPUS; i++) {
541                 struct desc_struct *gdt = get_cpu_gdt_table(i);
542                 if (!gdt)
543                         continue;
544                 Q2_SET_SEL(i, PNP_CS32, &pnp_bios_callfunc, 64 * 1024);
545                 Q_SET_SEL(i, PNP_CS16, header->fields.pm16cseg, 64 * 1024);
546                 Q_SET_SEL(i, PNP_DS, header->fields.pm16dseg, 64 * 1024);
547         }
548 }