cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

toshiba.c (12740B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/* toshiba.c -- Linux driver for accessing the SMM on Toshiba laptops
      3 *
      4 * Copyright (c) 1996-2001  Jonathan A. Buzzard (jonathan@buzzard.org.uk)
      5 *
      6 * Valuable assistance and patches from:
      7 *     Tom May <tom@you-bastards.com>
      8 *     Rob Napier <rnapier@employees.org>
      9 *
     10 * Fn status port numbers for machine ID's courtesy of
     11 *     0xfc02: Scott Eisert <scott.e@sky-eye.com>
     12 *     0xfc04: Steve VanDevender <stevev@efn.org>
     13 *     0xfc08: Garth Berry <garth@itsbruce.net>
     14 *     0xfc0a: Egbert Eich <eich@xfree86.org>
     15 *     0xfc10: Andrew Lofthouse <Andrew.Lofthouse@robins.af.mil>
     16 *     0xfc11: Spencer Olson <solson@novell.com>
     17 *     0xfc13: Claudius Frankewitz <kryp@gmx.de>
     18 *     0xfc15: Tom May <tom@you-bastards.com>
     19 *     0xfc17: Dave Konrad <konrad@xenia.it>
     20 *     0xfc1a: George Betzos <betzos@engr.colostate.edu>
     21 *     0xfc1b: Munemasa Wada <munemasa@jnovel.co.jp>
     22 *     0xfc1d: Arthur Liu <armie@slap.mine.nu>
     23 *     0xfc5a: Jacques L'helgoualc'h <lhh@free.fr>
     24 *     0xfcd1: Mr. Dave Konrad <konrad@xenia.it>
     25 *
     26 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
     27 *
     28 *   This code is covered by the GNU GPL and you are free to make any
     29 *   changes you wish to it under the terms of the license. However the
     30 *   code has the potential to render your computer and/or someone else's
     31 *   unusable. Please proceed with care when modifying the code.
     32 *
     33 * Note: Unfortunately the laptop hardware can close the System Configuration
     34 *       Interface on it's own accord. It is therefore necessary for *all*
     35 *       programs using this driver to be aware that *any* SCI call can fail at
     36 *       *any* time. It is up to any program to be aware of this eventuality
     37 *       and take appropriate steps.
     38 *
     39 * The information used to write this driver has been obtained by reverse
     40 * engineering the software supplied by Toshiba for their portable computers in
     41 * strict accordance with the European Council Directive 92/250/EEC on the legal
     42 * protection of computer programs, and it's implementation into English Law by
     43 * the Copyright (Computer Programs) Regulations 1992 (S.I. 1992 No.3233).
     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 * Read the Fn key status
     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 * For the Portage 610CT and the Tecra 700CS/700CDT emulate the HCI fan function
    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	/* Portage 610CT */
    130
    131	if (tosh_id==0xfccb) {
    132		if (eax==0xfe00) {
    133			/* fan status */
    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			/* fan off */
    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			/* fan on */
    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	/* Tecra 700CS/CDT */
    166
    167	if (tosh_id==0xfccc) {
    168		if (eax==0xfe00) {
    169			/* fan status */
    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			/* fan off */
    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			/* fan on */
    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 * Put the laptop into System Management Mode
    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(&regs, 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			/* block HCI calls to read/write memory & PCI devices */
    262			if (((ax==0xff00) || (ax==0xfe00)) && (bx>0x0069))
    263				return -EINVAL;
    264
    265			/* do we need to emulate the fan ? */
    266			mutex_lock(&tosh_mutex);
    267			if (tosh_fan==1) {
    268				if (((ax==0xf300) || (ax==0xf400)) && (bx==0x0004)) {
    269					err = tosh_emulate_fan(&regs);
    270					mutex_unlock(&tosh_mutex);
    271					break;
    272				}
    273			}
    274			err = tosh_smm(&regs);
    275			mutex_unlock(&tosh_mutex);
    276			break;
    277		default:
    278			return -EINVAL;
    279	}
    280
    281        if (copy_to_user(argp, &regs, sizeof(SMMRegisters)))
    282        	return -EFAULT;
    283
    284	return (err==0) ? 0:-EINVAL;
    285}
    286
    287
    288/*
    289 * Print the information for /proc/toshiba
    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	/* Arguments
    299	     0) Linux driver version (this will change if format changes)
    300	     1) Machine ID
    301	     2) SCI version
    302	     3) BIOS version (major, minor)
    303	     4) BIOS date (in SCI date format)
    304	     5) Fn Key status
    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 * Determine which port to use for the Fn key status
    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 * Get the machine identification number of the current model
    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	/* do we have a SCTTable machine identication number on our hands */
    356
    357	if (id==0xfc2f) {
    358
    359		/* start by getting a pointer into the BIOS */
    360
    361		regs.eax = 0xc000;
    362		regs.ebx = 0x0000;
    363		regs.ecx = 0x0000;
    364		tosh_smm(&regs);
    365		bx = (unsigned short) (regs.ebx & 0xffff);
    366
    367		/* At this point in the Toshiba routines under MS Windows
    368		   the bx register holds 0xe6f5. However my code is producing
    369		   a different value! For the time being I will just fudge the
    370		   value. This has been verified on a Satellite Pro 430CDT,
    371		   Tecra 750CDT, Tecra 780DVD and Satellite 310CDT. */
    372#if TOSH_DEBUG
    373		pr_debug("toshiba: debugging ID ebx=0x%04x\n", regs.ebx);
    374#endif
    375		bx = 0xe6f5;
    376
    377		/* now twiddle with our pointer a bit */
    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		/* now construct our machine identification number */
    387
    388		id = ((cx & 0xff)<<8)+((cx & 0xff00)>>8);
    389	}
    390
    391	return id;
    392}
    393
    394
    395/*
    396 * Probe for the presence of a Toshiba laptop
    397 *
    398 *   returns and non-zero if unable to detect the presence of a Toshiba
    399 *   laptop, otherwise zero and determines the Machine ID, BIOS version and
    400 *   date, and SCI version.
    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	/* extra sanity check for the string "TOSHIBA" in the BIOS because
    413	   some machines that are not Toshiba's pass the next test */
    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	/* call the Toshiba SCI support check routine */
    424
    425	regs.eax = 0xf0f0;
    426	regs.ebx = 0x0000;
    427	regs.ecx = 0x0000;
    428	flag = tosh_smm(&regs);
    429
    430	/* if this is not a Toshiba laptop carry flag is set and ah=0x86 */
    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	/* if we get this far then we are running on a Toshiba (probably)! */
    439
    440	tosh_sci = regs.edx & 0xffff;
    441
    442	/* next get the machine ID of the current laptop */
    443
    444	tosh_id = tosh_get_machine_id(bios);
    445
    446	/* get the BIOS version */
    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	/* get the BIOS date */
    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	/* in theory we should check the ports we are going to use for the
    462	   fn key detection (and the fan on the Portage 610/Tecra700), and
    463	   then request them to stop other drivers using them. However as
    464	   the keyboard driver grabs 0x60-0x6f and the pic driver grabs
    465	   0xa0-0xbf we can't. We just have to live dangerously and use the
    466	   ports anyway, oh boy! */
    467
    468	/* do we need to emulate the fan? */
    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	/* are we running on a Toshiba laptop */
    482
    483	if (tosh_probe())
    484		return -ENODEV;
    485
    486	pr_info("Toshiba System Management Mode driver v" TOSH_VERSION "\n");
    487
    488	/* set the port to use for Fn status if not specified as a parameter */
    489	if (tosh_fn==0x00)
    490		tosh_set_fn_port();
    491
    492	/* register the device file */
    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