1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46#define TOSH_VERSION "1.11 26/9/2001"
47#define TOSH_DEBUG 0
48
49#include <linux/module.h>
50#include <linux/kernel.h>
51#include <linux/types.h>
52#include <linux/fcntl.h>
53#include <linux/miscdevice.h>
54#include <linux/ioport.h>
55#include <asm/io.h>
56#include <linux/uaccess.h>
57#include <linux/init.h>
58#include <linux/stat.h>
59#include <linux/proc_fs.h>
60#include <linux/seq_file.h>
61#include <linux/mutex.h>
62#include <linux/toshiba.h>
63
64MODULE_LICENSE("GPL");
65MODULE_AUTHOR("Jonathan Buzzard <jonathan@buzzard.org.uk>");
66MODULE_DESCRIPTION("Toshiba laptop SMM driver");
67
68static DEFINE_MUTEX(tosh_mutex);
69static int tosh_fn;
70module_param_named(fn, tosh_fn, int, 0);
71MODULE_PARM_DESC(fn, "User specified Fn key detection port");
72
73static int tosh_id;
74static int tosh_bios;
75static int tosh_date;
76static int tosh_sci;
77static int tosh_fan;
78
79static long tosh_ioctl(struct file *, unsigned int,
80 unsigned long);
81
82
83static const struct file_operations tosh_fops = {
84 .owner = THIS_MODULE,
85 .unlocked_ioctl = tosh_ioctl,
86 .llseek = noop_llseek,
87};
88
89static struct miscdevice tosh_device = {
90 TOSH_MINOR_DEV,
91 "toshiba",
92 &tosh_fops
93};
94
95
96
97
98#ifdef CONFIG_PROC_FS
99static int tosh_fn_status(void)
100{
101 unsigned char scan;
102 unsigned long flags;
103
104 if (tosh_fn!=0) {
105 scan = inb(tosh_fn);
106 } else {
107 local_irq_save(flags);
108 outb(0x8e, 0xe4);
109 scan = inb(0xe5);
110 local_irq_restore(flags);
111 }
112
113 return (int) scan;
114}
115#endif
116
117
118
119
120
121static int tosh_emulate_fan(SMMRegisters *regs)
122{
123 unsigned long eax,ecx,flags;
124 unsigned char al;
125
126 eax = regs->eax & 0xff00;
127 ecx = regs->ecx & 0xffff;
128
129
130
131 if (tosh_id==0xfccb) {
132 if (eax==0xfe00) {
133
134 local_irq_save(flags);
135 outb(0xbe, 0xe4);
136 al = inb(0xe5);
137 local_irq_restore(flags);
138 regs->eax = 0x00;
139 regs->ecx = (unsigned int) (al & 0x01);
140 }
141 if ((eax==0xff00) && (ecx==0x0000)) {
142
143 local_irq_save(flags);
144 outb(0xbe, 0xe4);
145 al = inb(0xe5);
146 outb(0xbe, 0xe4);
147 outb (al | 0x01, 0xe5);
148 local_irq_restore(flags);
149 regs->eax = 0x00;
150 regs->ecx = 0x00;
151 }
152 if ((eax==0xff00) && (ecx==0x0001)) {
153
154 local_irq_save(flags);
155 outb(0xbe, 0xe4);
156 al = inb(0xe5);
157 outb(0xbe, 0xe4);
158 outb(al & 0xfe, 0xe5);
159 local_irq_restore(flags);
160 regs->eax = 0x00;
161 regs->ecx = 0x01;
162 }
163 }
164
165
166
167 if (tosh_id==0xfccc) {
168 if (eax==0xfe00) {
169
170 local_irq_save(flags);
171 outb(0xe0, 0xe4);
172 al = inb(0xe5);
173 local_irq_restore(flags);
174 regs->eax = 0x00;
175 regs->ecx = al & 0x01;
176 }
177 if ((eax==0xff00) && (ecx==0x0000)) {
178
179 local_irq_save(flags);
180 outb(0xe0, 0xe4);
181 al = inb(0xe5);
182 outw(0xe0 | ((al & 0xfe) << 8), 0xe4);
183 local_irq_restore(flags);
184 regs->eax = 0x00;
185 regs->ecx = 0x00;
186 }
187 if ((eax==0xff00) && (ecx==0x0001)) {
188
189 local_irq_save(flags);
190 outb(0xe0, 0xe4);
191 al = inb(0xe5);
192 outw(0xe0 | ((al | 0x01) << 8), 0xe4);
193 local_irq_restore(flags);
194 regs->eax = 0x00;
195 regs->ecx = 0x01;
196 }
197 }
198
199 return 0;
200}
201
202
203
204
205
206int tosh_smm(SMMRegisters *regs)
207{
208 int eax;
209
210 asm ("# load the values into the registers\n\t" \
211 "pushl %%eax\n\t" \
212 "movl 0(%%eax),%%edx\n\t" \
213 "push %%edx\n\t" \
214 "movl 4(%%eax),%%ebx\n\t" \
215 "movl 8(%%eax),%%ecx\n\t" \
216 "movl 12(%%eax),%%edx\n\t" \
217 "movl 16(%%eax),%%esi\n\t" \
218 "movl 20(%%eax),%%edi\n\t" \
219 "popl %%eax\n\t" \
220 "# call the System Management mode\n\t" \
221 "inb $0xb2,%%al\n\t"
222 "# fill out the memory with the values in the registers\n\t" \
223 "xchgl %%eax,(%%esp)\n\t"
224 "movl %%ebx,4(%%eax)\n\t" \
225 "movl %%ecx,8(%%eax)\n\t" \
226 "movl %%edx,12(%%eax)\n\t" \
227 "movl %%esi,16(%%eax)\n\t" \
228 "movl %%edi,20(%%eax)\n\t" \
229 "popl %%edx\n\t" \
230 "movl %%edx,0(%%eax)\n\t" \
231 "# setup the return value to the carry flag\n\t" \
232 "lahf\n\t" \
233 "shrl $8,%%eax\n\t" \
234 "andl $1,%%eax\n" \
235 : "=a" (eax)
236 : "a" (regs)
237 : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
238
239 return eax;
240}
241EXPORT_SYMBOL(tosh_smm);
242
243
244static long tosh_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
245{
246 SMMRegisters regs;
247 SMMRegisters __user *argp = (SMMRegisters __user *)arg;
248 unsigned short ax,bx;
249 int err;
250
251 if (!argp)
252 return -EINVAL;
253
254 if (copy_from_user(®s, argp, sizeof(SMMRegisters)))
255 return -EFAULT;
256
257 switch (cmd) {
258 case TOSH_SMM:
259 ax = regs.eax & 0xff00;
260 bx = regs.ebx & 0xffff;
261
262 if (((ax==0xff00) || (ax==0xfe00)) && (bx>0x0069))
263 return -EINVAL;
264
265
266 mutex_lock(&tosh_mutex);
267 if (tosh_fan==1) {
268 if (((ax==0xf300) || (ax==0xf400)) && (bx==0x0004)) {
269 err = tosh_emulate_fan(®s);
270 mutex_unlock(&tosh_mutex);
271 break;
272 }
273 }
274 err = tosh_smm(®s);
275 mutex_unlock(&tosh_mutex);
276 break;
277 default:
278 return -EINVAL;
279 }
280
281 if (copy_to_user(argp, ®s, sizeof(SMMRegisters)))
282 return -EFAULT;
283
284 return (err==0) ? 0:-EINVAL;
285}
286
287
288
289
290
291#ifdef CONFIG_PROC_FS
292static int proc_toshiba_show(struct seq_file *m, void *v)
293{
294 int key;
295
296 key = tosh_fn_status();
297
298
299
300
301
302
303
304
305
306 seq_printf(m, "1.1 0x%04x %d.%d %d.%d 0x%04x 0x%02x\n",
307 tosh_id,
308 (tosh_sci & 0xff00)>>8,
309 tosh_sci & 0xff,
310 (tosh_bios & 0xff00)>>8,
311 tosh_bios & 0xff,
312 tosh_date,
313 key);
314 return 0;
315}
316#endif
317
318
319
320
321
322static void tosh_set_fn_port(void)
323{
324 switch (tosh_id) {
325 case 0xfc02: case 0xfc04: case 0xfc09: case 0xfc0a: case 0xfc10:
326 case 0xfc11: case 0xfc13: case 0xfc15: case 0xfc1a: case 0xfc1b:
327 case 0xfc5a:
328 tosh_fn = 0x62;
329 break;
330 case 0xfc08: case 0xfc17: case 0xfc1d: case 0xfcd1: case 0xfce0:
331 case 0xfce2:
332 tosh_fn = 0x68;
333 break;
334 default:
335 tosh_fn = 0x00;
336 break;
337 }
338
339 return;
340}
341
342
343
344
345
346static int tosh_get_machine_id(void __iomem *bios)
347{
348 int id;
349 SMMRegisters regs;
350 unsigned short bx,cx;
351 unsigned long address;
352
353 id = (0x100*(int) readb(bios+0xfffe))+((int) readb(bios+0xfffa));
354
355
356
357 if (id==0xfc2f) {
358
359
360
361 regs.eax = 0xc000;
362 regs.ebx = 0x0000;
363 regs.ecx = 0x0000;
364 tosh_smm(®s);
365 bx = (unsigned short) (regs.ebx & 0xffff);
366
367
368
369
370
371
372#if TOSH_DEBUG
373 pr_debug("toshiba: debugging ID ebx=0x%04x\n", regs.ebx);
374#endif
375 bx = 0xe6f5;
376
377
378
379 address = bx;
380 cx = readw(bios + address);
381 address = 9+bx+cx;
382 cx = readw(bios + address);
383 address = 0xa+cx;
384 cx = readw(bios + address);
385
386
387
388 id = ((cx & 0xff)<<8)+((cx & 0xff00)>>8);
389 }
390
391 return id;
392}
393
394
395
396
397
398
399
400
401
402static int tosh_probe(void)
403{
404 int i,major,minor,day,year,month,flag;
405 unsigned char signature[7] = { 0x54,0x4f,0x53,0x48,0x49,0x42,0x41 };
406 SMMRegisters regs;
407 void __iomem *bios = ioremap(0xf0000, 0x10000);
408
409 if (!bios)
410 return -ENOMEM;
411
412
413
414
415 for (i=0;i<7;i++) {
416 if (readb(bios+0xe010+i)!=signature[i]) {
417 pr_err("toshiba: not a supported Toshiba laptop\n");
418 iounmap(bios);
419 return -ENODEV;
420 }
421 }
422
423
424
425 regs.eax = 0xf0f0;
426 regs.ebx = 0x0000;
427 regs.ecx = 0x0000;
428 flag = tosh_smm(®s);
429
430
431
432 if ((flag==1) || ((regs.eax & 0xff00)==0x8600)) {
433 pr_err("toshiba: not a supported Toshiba laptop\n");
434 iounmap(bios);
435 return -ENODEV;
436 }
437
438
439
440 tosh_sci = regs.edx & 0xffff;
441
442
443
444 tosh_id = tosh_get_machine_id(bios);
445
446
447
448 major = readb(bios+0xe009)-'0';
449 minor = ((readb(bios+0xe00b)-'0')*10)+(readb(bios+0xe00c)-'0');
450 tosh_bios = (major*0x100)+minor;
451
452
453
454 day = ((readb(bios+0xfff5)-'0')*10)+(readb(bios+0xfff6)-'0');
455 month = ((readb(bios+0xfff8)-'0')*10)+(readb(bios+0xfff9)-'0');
456 year = ((readb(bios+0xfffb)-'0')*10)+(readb(bios+0xfffc)-'0');
457 tosh_date = (((year-90) & 0x1f)<<10) | ((month & 0xf)<<6)
458 | ((day & 0x1f)<<1);
459
460
461
462
463
464
465
466
467
468
469
470 if ((tosh_id==0xfccb) || (tosh_id==0xfccc))
471 tosh_fan = 1;
472
473 iounmap(bios);
474
475 return 0;
476}
477
478static int __init toshiba_init(void)
479{
480 int retval;
481
482
483 if (tosh_probe())
484 return -ENODEV;
485
486 pr_info("Toshiba System Management Mode driver v" TOSH_VERSION "\n");
487
488
489 if (tosh_fn==0x00)
490 tosh_set_fn_port();
491
492
493 retval = misc_register(&tosh_device);
494 if (retval < 0)
495 return retval;
496
497#ifdef CONFIG_PROC_FS
498 {
499 struct proc_dir_entry *pde;
500
501 pde = proc_create_single("toshiba", 0, NULL, proc_toshiba_show);
502 if (!pde) {
503 misc_deregister(&tosh_device);
504 return -ENOMEM;
505 }
506 }
507#endif
508
509 return 0;
510}
511
512static void __exit toshiba_exit(void)
513{
514 remove_proc_entry("toshiba", NULL);
515 misc_deregister(&tosh_device);
516}
517
518module_init(toshiba_init);
519module_exit(toshiba_exit);
520
521