From ce7240e445303de3ca66e6d08f17a2ec278a5bf6 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 17 Jul 2012 17:06:20 +0100 Subject: 8250: three way resolve of the 8250 diffs This resolves the differences between the original 8250 patch, the revised 8250 patch and the independant clean up of the octeon driver (to use platform devices properly yay!) Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/mwave/mwavedd.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c index 1d82d5838f0c..164544afd680 100644 --- a/drivers/char/mwave/mwavedd.c +++ b/drivers/char/mwave/mwavedd.c @@ -430,7 +430,7 @@ static ssize_t mwave_write(struct file *file, const char __user *buf, static int register_serial_portandirq(unsigned int port, int irq) { - struct uart_port uart; + struct uart_8250_port uart; switch ( port ) { case 0x3f8: @@ -462,14 +462,14 @@ static int register_serial_portandirq(unsigned int port, int irq) } /* switch */ /* irq is okay */ - memset(&uart, 0, sizeof(struct uart_port)); + memset(&uart, 0, sizeof(uart)); - uart.uartclk = 1843200; - uart.iobase = port; - uart.irq = irq; - uart.iotype = UPIO_PORT; - uart.flags = UPF_SHARE_IRQ; - return serial8250_register_port(&uart); + uart.port.uartclk = 1843200; + uart.port.iobase = port; + uart.port.irq = irq; + uart.port.iotype = UPIO_PORT; + uart.port.flags = UPF_SHARE_IRQ; + return serial8250_register_8250_port(&uart); } -- cgit v1.2.3 From 373f5aedbc6fb73d30f00eeb0dc7313ecfede908 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 19 Jul 2012 12:23:28 +0100 Subject: pcmcia,synclink_cs: fix termios port I missed Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/pcmcia/synclink_cs.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 0a484b4a1b02..d0cbd29ecfd7 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -1344,7 +1344,7 @@ static void shutdown(MGSLPC_INFO * info, struct tty_struct *tty) /* TODO:disable interrupts instead of reset to preserve signal states */ reset_device(info); - if (!tty || tty->termios->c_cflag & HUPCL) { + if (!tty || tty->termios.c_cflag & HUPCL) { info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS); set_signals(info); } @@ -1385,7 +1385,7 @@ static void mgslpc_program_hw(MGSLPC_INFO *info, struct tty_struct *tty) port_irq_enable(info, (unsigned char) PVR_DSR | PVR_RI); get_signals(info); - if (info->netcount || (tty && (tty->termios->c_cflag & CREAD))) + if (info->netcount || (tty && (tty->termios.c_cflag & CREAD))) rx_start(info); spin_unlock_irqrestore(&info->lock,flags); @@ -1398,14 +1398,14 @@ static void mgslpc_change_params(MGSLPC_INFO *info, struct tty_struct *tty) unsigned cflag; int bits_per_char; - if (!tty || !tty->termios) + if (!tty) return; if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgslpc_change_params(%s)\n", __FILE__,__LINE__, info->device_name ); - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; /* if B0 rate (hangup) specified then negate DTR and RTS */ /* otherwise assert DTR and RTS */ @@ -1728,7 +1728,7 @@ static void mgslpc_throttle(struct tty_struct * tty) if (I_IXOFF(tty)) mgslpc_send_xchar(tty, STOP_CHAR(tty)); - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { spin_lock_irqsave(&info->lock,flags); info->serial_signals &= ~SerialSignal_RTS; set_signals(info); @@ -1757,7 +1757,7 @@ static void mgslpc_unthrottle(struct tty_struct * tty) mgslpc_send_xchar(tty, START_CHAR(tty)); } - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { spin_lock_irqsave(&info->lock,flags); info->serial_signals |= SerialSignal_RTS; set_signals(info); @@ -2293,8 +2293,8 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term tty->driver->name ); /* just return if nothing has changed */ - if ((tty->termios->c_cflag == old_termios->c_cflag) - && (RELEVANT_IFLAG(tty->termios->c_iflag) + if ((tty->termios.c_cflag == old_termios->c_cflag) + && (RELEVANT_IFLAG(tty->termios.c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) return; @@ -2302,7 +2302,7 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term /* Handle transition to B0 status */ if (old_termios->c_cflag & CBAUD && - !(tty->termios->c_cflag & CBAUD)) { + !(tty->termios.c_cflag & CBAUD)) { info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); spin_lock_irqsave(&info->lock,flags); set_signals(info); @@ -2311,9 +2311,9 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && - tty->termios->c_cflag & CBAUD) { + tty->termios.c_cflag & CBAUD) { info->serial_signals |= SerialSignal_DTR; - if (!(tty->termios->c_cflag & CRTSCTS) || + if (!(tty->termios.c_cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) { info->serial_signals |= SerialSignal_RTS; } @@ -2324,7 +2324,7 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term /* Handle turning off CRTSCTS */ if (old_termios->c_cflag & CRTSCTS && - !(tty->termios->c_cflag & CRTSCTS)) { + !(tty->termios.c_cflag & CRTSCTS)) { tty->hw_stopped = 0; tx_release(tty); } -- cgit v1.2.3 From 51891a431354d0b6d0408746f398364b15068d65 Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Mon, 26 Mar 2012 18:56:08 +0800 Subject: char: nwflash: remove old led event code Signed-off-by: Bryan Wu --- drivers/char/nwflash.c | 34 ---------------------------------- 1 file changed, 34 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c index d45c3345b4af..a0e2f7d70355 100644 --- a/drivers/char/nwflash.c +++ b/drivers/char/nwflash.c @@ -30,7 +30,6 @@ #include #include -#include #include #include @@ -179,9 +178,6 @@ static ssize_t flash_write(struct file *file, const char __user *buf, written = 0; - leds_event(led_claim); - leds_event(led_green_on); - nBlock = (int) p >> 16; //block # of 64K bytes /* @@ -258,11 +254,6 @@ static ssize_t flash_write(struct file *file, const char __user *buf, printk(KERN_DEBUG "flash_write: written 0x%X bytes OK.\n", written); } - /* - * restore reg on exit - */ - leds_event(led_release); - mutex_unlock(&nwflash_mutex); return written; @@ -333,11 +324,6 @@ static int erase_block(int nBlock) unsigned long timeout; int temp, temp1; - /* - * orange LED == erase - */ - leds_event(led_amber_on); - /* * reset footbridge to the correct offset 0 (...0..3) */ @@ -446,12 +432,6 @@ static int write_block(unsigned long p, const char __user *buf, int count) unsigned long timeout; unsigned long timeout1; - /* - * red LED == write - */ - leds_event(led_amber_off); - leds_event(led_red_on); - pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p)); /* @@ -557,18 +537,10 @@ static int write_block(unsigned long p, const char __user *buf, int count) printk(KERN_DEBUG "write_block: Retrying write at 0x%X)n", pWritePtr - FLASH_BASE); - /* - * no LED == waiting - */ - leds_event(led_amber_off); /* * wait couple ms */ msleep(10); - /* - * red LED == write - */ - leds_event(led_red_on); goto WriteRetry; } else { @@ -583,12 +555,6 @@ static int write_block(unsigned long p, const char __user *buf, int count) } } - /* - * green LED == read/verify - */ - leds_event(led_amber_off); - leds_event(led_green_on); - msleep(10); pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p)); -- cgit v1.2.3 From f06fb543c1d0cbd721250b72ee1244178a38aa9d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 7 Aug 2012 21:47:38 +0200 Subject: TTY: ttyprintk, unregister tty driver on failure When the tty_printk driver fails to create a node in sysfs, the system crashes. It is because the driver registers a tty driver and frees it without deregistering it first. The fix is easy: add a call to tty_unregister_driver to the fail path. This is very unlikely to happen in usual environment => no need for stable. The crash occurs at some place where we iterate over tty drivers first. It may look like this: BUG: unable to handle kernel paging request at ffffffffffffff84 IP: [] tty_open+0xd6/0x650 PGD 1a0d067 PUD 1a0e067 PMD 0 Oops: 0000 [#1] PREEMPT SMP Modules linked in: CPU 0 Pid: 1183, comm: boot.localnet Tainted: G W 3.5.0-rc7-next-20120716+ #369 Bochs Bochs RIP: 0010:[] [] tty_open+0xd6/0x650 RSP: 0018:ffff8800162b3b98 EFLAGS: 00010207 RAX: 0000000000000000 RBX: ffff880016ba6200 RCX: 0000000000002208 RDX: 0000000000000000 RSI: 00000000000000d0 RDI: ffffffff81a35080 RBP: ffff8800162b3c08 R08: ffffffff81276f42 R09: 0000000000400040 R10: ffff8800161dc005 R11: ffff8800188ee048 R12: 0000000000000000 R13: ffffffffffffff58 R14: 0000000000400040 R15: 0000000000008000 FS: 00007f3684abd700(0000) GS:ffff880018e00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffffffffffffff84 CR3: 000000001503e000 CR4: 00000000000006f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process boot.localnet (pid: 1183, threadinfo ffff8800162b2000, task ffff8800188c5880) Stack: ffff8800162b3c08 ffffffff81363d63 ffffffff81a62940 ffff8800189b4e88 ffff8800188c5880 ffffffff81123180 0000000000000000 ffffffff18b20600 0000000000000000 ffff8800189b4e88 ffff880016ba6200 ffff880018b20600 Call Trace: [] ? kobj_lookup+0x103/0x160 [] ? mount_fs+0x110/0x110 [] chrdev_open+0x9c/0x1a0 [] ? cdev_put+0x30/0x30 [] do_dentry_open.isra.19+0x1e6/0x270 [] finish_open+0x65/0xa0 [] do_last.isra.52+0x26e/0xd80 [] ? inode_permission+0x13/0x50 [] ? link_path_walk+0x63/0x940 [] path_openat+0xab/0x3d0 [] do_filp_open+0x3d/0xa0 [] ? alloc_fd+0xd2/0x120 [] do_sys_open+0xf3/0x1d0 [] sys_open+0x1c/0x20 [] system_call_fastpath+0x16/0x1b Signed-off-by: Jiri Slaby Cc: Samo Pogacnik Signed-off-by: Greg Kroah-Hartman --- drivers/char/ttyprintk.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/char') diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index 46b77ede84c0..9b1e5e0cdc65 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -217,6 +217,7 @@ static int __init ttyprintk_init(void) return 0; error: + tty_unregister_driver(ttyprintk_driver); put_tty_driver(ttyprintk_driver); ttyprintk_driver = NULL; return ret; -- cgit v1.2.3 From ee8b593affdf893012e57f4c54a21984d1b0d92e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 7 Aug 2012 21:47:39 +0200 Subject: TTY: ttyprintk, don't touch behind tty->write_buf If a user provides a buffer larger than a tty->write_buf chunk and passes '\r' at the end of the buffer, we touch an out-of-bound memory. Add a check there to prevent this. Signed-off-by: Jiri Slaby Cc: stable@vger.kernel.org (everything maintained past v2.6.37) Cc: Samo Pogacnik Signed-off-by: Greg Kroah-Hartman --- drivers/char/ttyprintk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index 9b1e5e0cdc65..08755c52fc5f 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -67,7 +67,7 @@ static int tpk_printk(const unsigned char *buf, int count) tmp[tpk_curr + 1] = '\0'; printk(KERN_INFO "%s%s", tpk_tag, tmp); tpk_curr = 0; - if (buf[i + 1] == '\n') + if ((i + 1) < count && buf[i + 1] == '\n') i++; break; case '\n': -- cgit v1.2.3 From 536a3440a27f8687090bdf33468b003bc0f810cf Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 7 Aug 2012 21:47:40 +0200 Subject: TTY: ttyprintk, initialize tty_port earlier After tty_register_driver is called, it is too late to initialize a guy with which we operate in open. When a process already called open(2) on that node, the structures may be in use uninitialized. Move the initialization prior to tty_register_driver. Signed-off-by: Jiri Slaby Cc: Samo Pogacnik Signed-off-by: Greg Kroah-Hartman --- drivers/char/ttyprintk.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index 08755c52fc5f..be1c3fb186c1 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -180,6 +180,10 @@ static int __init ttyprintk_init(void) int ret = -ENOMEM; void *rp; + tty_port_init(&tpk_port.port); + tpk_port.port.ops = &null_ops; + mutex_init(&tpk_port.port_write_mutex); + ttyprintk_driver = alloc_tty_driver(1); if (!ttyprintk_driver) return ret; @@ -210,10 +214,6 @@ static int __init ttyprintk_init(void) goto error; } - tty_port_init(&tpk_port.port); - tpk_port.port.ops = &null_ops; - mutex_init(&tpk_port.port_write_mutex); - return 0; error: -- cgit v1.2.3 From 0019b4089ccef8148d8be83cc8adfc81a75b47d4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 8 Aug 2012 22:26:43 +0200 Subject: TTY: add support for unnumbered device nodes This allows drivers like ttyprintk to avoid hacks to create an unnumbered node in /dev. It used to set TTY_DRIVER_DYNAMIC_DEV in flags and call device_create on its own. That is incorrect, because TTY_DRIVER_DYNAMIC_DEV may be set only if tty_register_device is called explicitly. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/ttyprintk.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index be1c3fb186c1..9e6272f0b98c 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -178,13 +178,15 @@ static struct tty_driver *ttyprintk_driver; static int __init ttyprintk_init(void) { int ret = -ENOMEM; - void *rp; tty_port_init(&tpk_port.port); tpk_port.port.ops = &null_ops; mutex_init(&tpk_port.port_write_mutex); - ttyprintk_driver = alloc_tty_driver(1); + ttyprintk_driver = tty_alloc_driver(1, + TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_UNNUMBERED_NODE); if (!ttyprintk_driver) return ret; @@ -195,8 +197,6 @@ static int __init ttyprintk_init(void) ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE; ttyprintk_driver->init_termios = tty_std_termios; ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET; - ttyprintk_driver->flags = TTY_DRIVER_RESET_TERMIOS | - TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; tty_set_operations(ttyprintk_driver, &ttyprintk_ops); ret = tty_register_driver(ttyprintk_driver); @@ -205,15 +205,6 @@ static int __init ttyprintk_init(void) goto error; } - /* create our unnumbered device */ - rp = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 3), NULL, - ttyprintk_driver->name); - if (IS_ERR(rp)) { - printk(KERN_ERR "Couldn't create ttyprintk device\n"); - ret = PTR_ERR(rp); - goto error; - } - return 0; error: -- cgit v1.2.3 From b19e2ca77ee4becadc85341bb0c1cee454dd4fd5 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 7 Aug 2012 21:47:51 +0200 Subject: TTY: use tty_port_link_device So now for those drivers that can use neither tty_port_install nor tty_port_register_driver but still have tty_port available before tty_register_driver we use newly added tty_port_link_device. The rest of the drivers that still do not provide tty_struct <-> tty_port link will have to be converted to implement tty->ops->install. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/ttyprintk.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/char') diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index 9e6272f0b98c..561f8aa7cd87 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -198,6 +198,7 @@ static int __init ttyprintk_init(void) ttyprintk_driver->init_termios = tty_std_termios; ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET; tty_set_operations(ttyprintk_driver, &ttyprintk_ops); + tty_port_link_device(&tpk_port.port, ttyprintk_driver, 0); ret = tty_register_driver(ttyprintk_driver); if (ret < 0) { -- cgit v1.2.3 From 737586fe51e6d0031f83d781c0cb2f3abf8caada Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 7 Aug 2012 21:47:52 +0200 Subject: TTY: synclink_cs, sanitize fail paths We will need to change the order of tty and pcmcia drivers initializations (see the reason later in this series). And the fail path handling is currently performed in a separate function that as well takes care of proper deinitialization in module_exit. It is hard to read and will need to be adjusted by our changes anyway. Instead, get rid of this helper function and do the fail paths handling directly in the init function. (And move the body of the function to module_exit.) Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/pcmcia/synclink_cs.c | 41 +++++++++++++-------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index d0cbd29ecfd7..0606586e76ab 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2798,23 +2798,6 @@ static const struct tty_operations mgslpc_ops = { .proc_fops = &mgslpc_proc_fops, }; -static void synclink_cs_cleanup(void) -{ - int rc; - - while(mgslpc_device_list) - mgslpc_remove_device(mgslpc_device_list); - - if (serial_driver) { - if ((rc = tty_unregister_driver(serial_driver))) - printk("%s(%d) failed to unregister tty driver err=%d\n", - __FILE__,__LINE__,rc); - put_tty_driver(serial_driver); - } - - pcmcia_unregister_driver(&mgslpc_driver); -} - static int __init synclink_cs_init(void) { int rc; @@ -2830,7 +2813,7 @@ static int __init synclink_cs_init(void) serial_driver = alloc_tty_driver(MAX_DEVICE_COUNT); if (!serial_driver) { rc = -ENOMEM; - goto error; + goto err_pcmcia_drv; } /* Initialize the tty_driver structure */ @@ -2850,25 +2833,29 @@ static int __init synclink_cs_init(void) if ((rc = tty_register_driver(serial_driver)) < 0) { printk("%s(%d):Couldn't register serial driver\n", __FILE__,__LINE__); - put_tty_driver(serial_driver); - serial_driver = NULL; - goto error; + goto err_put_tty; } printk("%s %s, tty major#%d\n", driver_name, driver_version, serial_driver->major); - return 0; - -error: - synclink_cs_cleanup(); - return rc; + return 0; +err_put_tty: + put_tty_driver(serial_driver); +err_pcmcia_drv: + pcmcia_unregister_driver(&mgslpc_driver); + return rc; } static void __exit synclink_cs_exit(void) { - synclink_cs_cleanup(); + while (mgslpc_device_list) + mgslpc_remove_device(mgslpc_device_list); + + tty_unregister_driver(serial_driver); + put_tty_driver(serial_driver); + pcmcia_unregister_driver(&mgslpc_driver); } module_init(synclink_cs_init); -- cgit v1.2.3 From 16a1065f2113b2c068ea108a681fb6b1f3a02ce0 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 7 Aug 2012 21:47:53 +0200 Subject: TTY: synclink_cs, use dynamic tty devices This allows us to provide the tty layer with information about tty_port for each link. And it also allows us to get rid of the remove_device loop in synclink_cs_exit because we had to reorder pcmcia and tty driver registration in init. This was because we need to have serial_driver initialized when calling tty_port_register_device from pcmcia ->probe. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/pcmcia/synclink_cs.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 0606586e76ab..ce277f74bd10 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2731,6 +2731,8 @@ static void mgslpc_add_device(MGSLPC_INFO *info) #if SYNCLINK_GENERIC_HDLC hdlcdev_init(info); #endif + tty_port_register_device(&info->port, serial_driver, info->line, + &info->p_dev.dev); } static void mgslpc_remove_device(MGSLPC_INFO *remove_info) @@ -2744,6 +2746,7 @@ static void mgslpc_remove_device(MGSLPC_INFO *remove_info) last->next_device = info->next_device; else mgslpc_device_list = info->next_device; + tty_unregister_device(serial_driver, info->line); #if SYNCLINK_GENERIC_HDLC hdlcdev_exit(info); #endif @@ -2807,13 +2810,12 @@ static int __init synclink_cs_init(void) BREAKPOINT(); } - if ((rc = pcmcia_register_driver(&mgslpc_driver)) < 0) - return rc; - - serial_driver = alloc_tty_driver(MAX_DEVICE_COUNT); + serial_driver = tty_alloc_driver(MAX_DEVICE_COUNT, + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); if (!serial_driver) { rc = -ENOMEM; - goto err_pcmcia_drv; + goto err; } /* Initialize the tty_driver structure */ @@ -2827,7 +2829,6 @@ static int __init synclink_cs_init(void) serial_driver->init_termios = tty_std_termios; serial_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - serial_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(serial_driver, &mgslpc_ops); if ((rc = tty_register_driver(serial_driver)) < 0) { @@ -2836,26 +2837,28 @@ static int __init synclink_cs_init(void) goto err_put_tty; } + rc = pcmcia_register_driver(&mgslpc_driver); + if (rc < 0) + goto err_unreg_tty; + printk("%s %s, tty major#%d\n", driver_name, driver_version, serial_driver->major); return 0; +err_unreg_tty: + tty_unregister_driver(serial_driver); err_put_tty: put_tty_driver(serial_driver); -err_pcmcia_drv: - pcmcia_unregister_driver(&mgslpc_driver); +err: return rc; } static void __exit synclink_cs_exit(void) { - while (mgslpc_device_list) - mgslpc_remove_device(mgslpc_device_list); - + pcmcia_unregister_driver(&mgslpc_driver); tty_unregister_driver(serial_driver); put_tty_driver(serial_driver); - pcmcia_unregister_driver(&mgslpc_driver); } module_init(synclink_cs_init); -- cgit v1.2.3 From cc93441eed0d39af9d99ba1642b9f733b195435c Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 7 Aug 2012 21:47:54 +0200 Subject: TTY: synclink_cs, final cleanup in synclink_cs_init * use for indentation * add KERN_* to printks * no more assignments in if's like if ((rc = function())) Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/pcmcia/synclink_cs.c | 65 +++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 33 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index ce277f74bd10..923c3a47db0c 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2803,47 +2803,46 @@ static const struct tty_operations mgslpc_ops = { static int __init synclink_cs_init(void) { - int rc; + int rc; - if (break_on_load) { - mgslpc_get_text_ptr(); - BREAKPOINT(); - } + if (break_on_load) { + mgslpc_get_text_ptr(); + BREAKPOINT(); + } - serial_driver = tty_alloc_driver(MAX_DEVICE_COUNT, - TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV); - if (!serial_driver) { - rc = -ENOMEM; - goto err; - } + serial_driver = tty_alloc_driver(MAX_DEVICE_COUNT, + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); + if (!serial_driver) { + rc = -ENOMEM; + goto err; + } - /* Initialize the tty_driver structure */ - - serial_driver->driver_name = "synclink_cs"; - serial_driver->name = "ttySLP"; - serial_driver->major = ttymajor; - serial_driver->minor_start = 64; - serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - serial_driver->subtype = SERIAL_TYPE_NORMAL; - serial_driver->init_termios = tty_std_termios; - serial_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - tty_set_operations(serial_driver, &mgslpc_ops); - - if ((rc = tty_register_driver(serial_driver)) < 0) { - printk("%s(%d):Couldn't register serial driver\n", - __FILE__,__LINE__); - goto err_put_tty; - } + /* Initialize the tty_driver structure */ + serial_driver->driver_name = "synclink_cs"; + serial_driver->name = "ttySLP"; + serial_driver->major = ttymajor; + serial_driver->minor_start = 64; + serial_driver->type = TTY_DRIVER_TYPE_SERIAL; + serial_driver->subtype = SERIAL_TYPE_NORMAL; + serial_driver->init_termios = tty_std_termios; + serial_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + tty_set_operations(serial_driver, &mgslpc_ops); + + rc = tty_register_driver(serial_driver); + if (rc < 0) { + printk(KERN_ERR "%s(%d):Couldn't register serial driver\n", + __FILE__, __LINE__); + goto err_put_tty; + } rc = pcmcia_register_driver(&mgslpc_driver); if (rc < 0) goto err_unreg_tty; - printk("%s %s, tty major#%d\n", - driver_name, driver_version, - serial_driver->major); + printk(KERN_INFO "%s %s, tty major#%d\n", driver_name, driver_version, + serial_driver->major); return 0; err_unreg_tty: -- cgit v1.2.3 From a33ba827460cdab644e907fc0c740dc7fde56c17 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 14 Aug 2012 10:23:08 +0200 Subject: TTY: synclink_cs, fix build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit "TTY: synclink_cs, use dynamic tty devices" added a call to tty_port_register_device with a proper device as the last argument. But it was not correct and it causes build failures: synclink_cs.c: In function ‘mgslpc_add_device’: synclink_cs.c:2735:16: error: request for member ‘dev’ in something not a structure or union info->p_dev is a pointer, so act as that. I wonder why my build scripts did not notice. I have to re-check them. Signed-off-by: Jiri Slaby Reported-by: Fengguang Wu Signed-off-by: Greg Kroah-Hartman --- drivers/char/pcmcia/synclink_cs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 923c3a47db0c..d9335ae1fadf 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2732,7 +2732,7 @@ static void mgslpc_add_device(MGSLPC_INFO *info) hdlcdev_init(info); #endif tty_port_register_device(&info->port, serial_driver, info->line, - &info->p_dev.dev); + &info->p_dev->dev); } static void mgslpc_remove_device(MGSLPC_INFO *remove_info) -- cgit v1.2.3 From e045907c0a08d09cbc992cd00f788ef0dd83889d Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 5 Aug 2012 11:52:35 +0200 Subject: drivers/char/tlclk.c: fix error return code Convert a 0 error return code to a negative one, as returned elsewhere in the function. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier ret; expression e,e1,e2,e3,e4,x; @@ ( if (\(ret != 0\|ret < 0\) || ...) { ... return ...; } | ret = 0 ) ... when != ret = e1 *x = \(kmalloc\|kzalloc\|kcalloc\|devm_kzalloc\|ioremap\|ioremap_nocache\|devm_ioremap\|devm_ioremap_nocache\)(...); ... when != x = e2 when != ret = e3 *if (x == NULL || ...) { ... when != ret = e4 * return ret; } // Signed-off-by: Julia Lawall Acked-by: Mark Gross Cc: Arnd Bergmann Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/char/tlclk.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c index ce29e7cce528..e95e0ab0bd87 100644 --- a/drivers/char/tlclk.c +++ b/drivers/char/tlclk.c @@ -784,8 +784,10 @@ static int __init tlclk_init(void) } tlclk_major = ret; alarm_events = kzalloc( sizeof(struct tlclk_alarms), GFP_KERNEL); - if (!alarm_events) + if (!alarm_events) { + ret = -ENOMEM; goto out1; + } /* Read telecom clock IRQ number (Set by BIOS) */ if (!request_region(TLCLK_BASE, 8, "telco_clock")) { -- cgit v1.2.3 From c3a6344ae475763b6fb0fb2ec3639004f500d0f1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 16 Aug 2012 16:16:56 +0300 Subject: TTY: tty_alloc_driver() returns error pointers We changed these from alloc_tty_driver() to tty_alloc_driver() so the error handling needs to modified to check for IS_ERR() instead of NULL. Signed-off-by: Dan Carpenter Acked-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/char/pcmcia/synclink_cs.c | 4 ++-- drivers/char/ttyprintk.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index d9335ae1fadf..5db08c78beb5 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2813,8 +2813,8 @@ static int __init synclink_cs_init(void) serial_driver = tty_alloc_driver(MAX_DEVICE_COUNT, TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV); - if (!serial_driver) { - rc = -ENOMEM; + if (IS_ERR(serial_driver)) { + rc = PTR_ERR(serial_driver); goto err; } diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index 561f8aa7cd87..af98f6d6509b 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -187,8 +187,8 @@ static int __init ttyprintk_init(void) TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_UNNUMBERED_NODE); - if (!ttyprintk_driver) - return ret; + if (IS_ERR(ttyprintk_driver)) + return PTR_ERR(ttyprintk_driver); ttyprintk_driver->driver_name = "ttyprintk"; ttyprintk_driver->name = "ttyprintk"; -- cgit v1.2.3 From 4dbb845ddedff87fc1289ad0e2d780a4a7918356 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 4 Aug 2012 18:50:46 +0200 Subject: drivers/char/hw_random/octeon-rng.c: drop frees of devm allocated data devm_kfree and devm_iounmap should not have to be explicitly used. The semantic patch that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression x,d; @@ x = devm_kzalloc(...) ... ?-devm_kfree(d,x); // Signed-off-by: Julia Lawall Signed-off-by: Herbert Xu --- drivers/char/hw_random/octeon-rng.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/octeon-rng.c b/drivers/char/hw_random/octeon-rng.c index 0943edc782a1..5c34c092af71 100644 --- a/drivers/char/hw_random/octeon-rng.c +++ b/drivers/char/hw_random/octeon-rng.c @@ -75,42 +75,35 @@ static int __devinit octeon_rng_probe(struct platform_device *pdev) res_ports = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res_ports) - goto err_ports; + return -ENOENT; res_result = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res_result) - goto err_ports; + return -ENOENT; rng->control_status = devm_ioremap_nocache(&pdev->dev, res_ports->start, sizeof(u64)); if (!rng->control_status) - goto err_ports; + return -ENOENT; rng->result = devm_ioremap_nocache(&pdev->dev, res_result->start, sizeof(u64)); if (!rng->result) - goto err_r; + return -ENOENT; rng->ops = ops; dev_set_drvdata(&pdev->dev, &rng->ops); ret = hwrng_register(&rng->ops); if (ret) - goto err; + return -ENOENT; dev_info(&pdev->dev, "Octeon Random Number Generator\n"); return 0; -err: - devm_iounmap(&pdev->dev, rng->control_status); -err_r: - devm_iounmap(&pdev->dev, rng->result); -err_ports: - devm_kfree(&pdev->dev, rng); - return -ENOENT; } static int __exit octeon_rng_remove(struct platform_device *pdev) -- cgit v1.2.3 From 43829731dd372d04d6706c51052b9dabab9ca356 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 20 Aug 2012 14:51:24 -0700 Subject: workqueue: deprecate flush[_delayed]_work_sync() flush[_delayed]_work_sync() are now spurious. Mark them deprecated and convert all users to flush[_delayed]_work(). If you're cc'd and wondering what's going on: Now all workqueues are non-reentrant and the regular flushes guarantee that the work item is not pending or running on any CPU on return, so there's no reason to use the sync flushes at all and they're going away. This patch doesn't make any functional difference. Signed-off-by: Tejun Heo Cc: Russell King Cc: Paul Mundt Cc: Ian Campbell Cc: Jens Axboe Cc: Mattia Dongili Cc: Kent Yoder Cc: David Airlie Cc: Jiri Kosina Cc: Karsten Keil Cc: Bryan Wu Cc: Benjamin Herrenschmidt Cc: Alasdair Kergon Cc: Mauro Carvalho Chehab Cc: Florian Tobias Schandinat Cc: David Woodhouse Cc: "David S. Miller" Cc: linux-wireless@vger.kernel.org Cc: Anton Vorontsov Cc: Sangbeom Kim Cc: "James E.J. Bottomley" Cc: Greg Kroah-Hartman Cc: Eric Van Hensbergen Cc: Takashi Iwai Cc: Steven Whitehouse Cc: Petr Vandrovec Cc: Mark Fasheh Cc: Christoph Hellwig Cc: Avi Kivity --- drivers/char/sonypi.c | 2 +- drivers/char/tpm/tpm.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index f87780502b41..320debbe32fa 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1433,7 +1433,7 @@ static int __devexit sonypi_remove(struct platform_device *dev) sonypi_disable(); synchronize_irq(sonypi_device.irq); - flush_work_sync(&sonypi_device.input_work); + flush_work(&sonypi_device.input_work); if (useinput) { input_unregister_device(sonypi_device.input_key_dev); diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 817f0ee202b6..3af9f4d1a23f 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1172,7 +1172,7 @@ int tpm_release(struct inode *inode, struct file *file) struct tpm_chip *chip = file->private_data; del_singleshot_timer_sync(&chip->user_read_timer); - flush_work_sync(&chip->work); + flush_work(&chip->work); file->private_data = NULL; atomic_set(&chip->data_pending, 0); kfree(chip->data_buffer); @@ -1225,7 +1225,7 @@ ssize_t tpm_read(struct file *file, char __user *buf, int rc; del_singleshot_timer_sync(&chip->user_read_timer); - flush_work_sync(&chip->work); + flush_work(&chip->work); ret_size = atomic_read(&chip->data_pending); atomic_set(&chip->data_pending, 0); if (ret_size > 0) { /* relay data */ -- cgit v1.2.3 From aad628c1d91a6db57e572e4c1f35e863d81061d7 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Tue, 7 Aug 2012 11:42:32 +0200 Subject: char/tpm: Add new driver for Infineon I2C TIS TPM This patch adds a driver to support Infineon's SLB 9635 TT 1.2 Soft I2C TPMs which follow the TGC TIS 1.2 TPM specification[1] and Infineon's I2C Protocol Stack Specification 0.20. The I2C Protocol Stack Specification is a simple adaption of the LPC TIS Protocol to the I2C Bus. The I2C TPMs can be used when LPC Bus is not available (i.e. non x86 architectures like ARM). The driver is based on the tpm_tis.c driver by Leendert van Dorn and Kyleen Hall and has quite similar functionality. Tested on Nvidia ARM Tegra2 Development Platform and Beagleboard (ARM OMAP) Tested with the Trousers[2] TSS API Testsuite v 0.3 [3] Compile-tested on x86 (32/64-bit) Updates since version 2.1.4: - included "Lock the I2C adapter for a sequence of requests", by Bryan Freed - use __i2c_transfer instead of own implementation of unlocked i2c_transfer - use struct dev_pm_ops for power management via SIMPLE_DEV_PM_OPS Updates since version 2.1.3: - use proper probing mechanism * either add the tpm using I2C_BOARD_INFO to your board file or probe it * during runtime e.g on BeagleBoard using : * "echo tpm_i2c_infineon 0x20 > /sys/bus/i2c/devices/i2c-2/new_device" - fix possible endless loop if hardware misbehaves - improved return codes - consistent spelling i2c/tpm -> I2C/TPM - remove hardcoded sleep values and msleep usage - removed debug statements - added check for I2C functionality - renaming to tpm_i2c_infineon Updates since version 2.1.2: - added sysfs entries for duration and timeouts - updated to new tpm_do_selftest Updates since version 2.1.0: - improved error handling - implemented workarounds needed by the tpm - fixed typos References: [1] http://www.trustedcomputinggroup.org/resources/pc_client_work_group_pc_client_ specific_tpm_interface_specification_tis_version_12/ [2] http://trousers.sourceforge.net/ [3] http://sourceforge.net/projects/trousers/files/TSS%20API%20test%20suite/0.3/ Reviewed-by: Andi Shyti Acked-by: Marcel Selhorst Signed-off-by: Peter Huewe Signed-off-by: Bryan Freed Signed-off-by: Kent Yoder --- drivers/char/tpm/Kconfig | 11 + drivers/char/tpm/Makefile | 1 + drivers/char/tpm/tpm_i2c_infineon.c | 695 ++++++++++++++++++++++++++++++++++++ 3 files changed, 707 insertions(+) create mode 100644 drivers/char/tpm/tpm_i2c_infineon.c (limited to 'drivers/char') diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index a048199ce866..c4aac486ade5 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -33,6 +33,17 @@ config TCG_TIS from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_tis. +config TCG_TIS_I2C_INFINEON + tristate "TPM Interface Specification 1.2 Interface (I2C - Infineon)" + depends on I2C + ---help--- + If you have a TPM security chip that is compliant with the + TCG TIS 1.2 TPM specification and Infineon's I2C Protocol Stack + Specification 0.20 say Yes and it will be accessible from within + Linux. + To compile this driver as a module, choose M here; the module + will be called tpm_tis_i2c_infineon. + config TCG_NSC tristate "National Semiconductor TPM Interface" depends on X86 diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index ea3a1e02a824..a9c3afc92dbc 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -6,6 +6,7 @@ ifdef CONFIG_ACPI obj-$(CONFIG_TCG_TPM) += tpm_bios.o endif obj-$(CONFIG_TCG_TIS) += tpm_tis.o +obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o obj-$(CONFIG_TCG_NSC) += tpm_nsc.o obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c new file mode 100644 index 000000000000..5a831aec9d4b --- /dev/null +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -0,0 +1,695 @@ +/* + * Copyright (C) 2012 Infineon Technologies + * + * Authors: + * Peter Huewe + * + * Device driver for TCG/TCPA TPM (trusted platform module). + * Specifications at www.trustedcomputinggroup.org + * + * This device driver implements the TPM interface as defined in + * the TCG TPM Interface Spec version 1.2, revision 1.0 and the + * Infineon I2C Protocol Stack Specification v0.20. + * + * It is based on the original tpm_tis device driver from Leendert van + * Dorn and Kyleen Hall. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * + */ +#include +#include +#include +#include +#include +#include "tpm.h" + +/* max. buffer size supported by our TPM */ +#define TPM_BUFSIZE 1260 + +/* max. number of iterations after I2C NAK */ +#define MAX_COUNT 3 + +#define SLEEP_DURATION_LOW 55 +#define SLEEP_DURATION_HI 65 + +/* max. number of iterations after I2C NAK for 'long' commands + * we need this especially for sending TPM_READY, since the cleanup after the + * transtion to the ready state may take some time, but it is unpredictable + * how long it will take. + */ +#define MAX_COUNT_LONG 50 + +#define SLEEP_DURATION_LONG_LOW 200 +#define SLEEP_DURATION_LONG_HI 220 + +/* After sending TPM_READY to 'reset' the TPM we have to sleep even longer */ +#define SLEEP_DURATION_RESET_LOW 2400 +#define SLEEP_DURATION_RESET_HI 2600 + +/* we want to use usleep_range instead of msleep for the 5ms TPM_TIMEOUT */ +#define TPM_TIMEOUT_US_LOW (TPM_TIMEOUT * 1000) +#define TPM_TIMEOUT_US_HI (TPM_TIMEOUT_US_LOW + 2000) + +/* expected value for DIDVID register */ +#define TPM_TIS_I2C_DID_VID 0x000b15d1L + +/* Structure to store I2C TPM specific stuff */ +struct tpm_inf_dev { + struct i2c_client *client; + u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */ + struct tpm_chip *chip; +}; + +static struct tpm_inf_dev tpm_dev; +static struct i2c_driver tpm_tis_i2c_driver; + +/* + * iic_tpm_read() - read from TPM register + * @addr: register address to read from + * @buffer: provided by caller + * @len: number of bytes to read + * + * Read len bytes from TPM register and put them into + * buffer (little-endian format, i.e. first byte is put into buffer[0]). + * + * NOTE: TPM is big-endian for multi-byte values. Multi-byte + * values have to be swapped. + * + * NOTE: We can't unfortunately use the combined read/write functions + * provided by the i2c core as the TPM currently does not support the + * repeated start condition and due to it's special requirements. + * The i2c_smbus* functions do not work for this chip. + * + * Return -EIO on error, 0 on success. + */ +static int iic_tpm_read(u8 addr, u8 *buffer, size_t len) +{ + + struct i2c_msg msg1 = { tpm_dev.client->addr, 0, 1, &addr }; + struct i2c_msg msg2 = { tpm_dev.client->addr, I2C_M_RD, len, buffer }; + + int rc; + int count; + + /* Lock the adapter for the duration of the whole sequence. */ + if (!tpm_dev.client->adapter->algo->master_xfer) + return -EOPNOTSUPP; + i2c_lock_adapter(tpm_dev.client->adapter); + + for (count = 0; count < MAX_COUNT; count++) { + rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1); + if (rc > 0) + break; /* break here to skip sleep */ + + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); + } + + if (rc <= 0) + goto out; + + /* After the TPM has successfully received the register address it needs + * some time, thus we're sleeping here again, before retrieving the data + */ + for (count = 0; count < MAX_COUNT; count++) { + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); + rc = __i2c_transfer(tpm_dev.client->adapter, &msg2, 1); + if (rc > 0) + break; + + } + +out: + i2c_unlock_adapter(tpm_dev.client->adapter); + if (rc <= 0) + return -EIO; + + return 0; +} + +static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, + unsigned int sleep_low, + unsigned int sleep_hi, u8 max_count) +{ + int rc = -EIO; + int count; + + struct i2c_msg msg1 = { tpm_dev.client->addr, 0, len + 1, tpm_dev.buf }; + + if (len > TPM_BUFSIZE) + return -EINVAL; + + if (!tpm_dev.client->adapter->algo->master_xfer) + return -EOPNOTSUPP; + i2c_lock_adapter(tpm_dev.client->adapter); + + /* prepend the 'register address' to the buffer */ + tpm_dev.buf[0] = addr; + memcpy(&(tpm_dev.buf[1]), buffer, len); + + /* + * NOTE: We have to use these special mechanisms here and unfortunately + * cannot rely on the standard behavior of i2c_transfer. + */ + for (count = 0; count < max_count; count++) { + rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1); + if (rc > 0) + break; + + usleep_range(sleep_low, sleep_hi); + } + + i2c_unlock_adapter(tpm_dev.client->adapter); + if (rc <= 0) + return -EIO; + + return 0; +} + +/* + * iic_tpm_write() - write to TPM register + * @addr: register address to write to + * @buffer: containing data to be written + * @len: number of bytes to write + * + * Write len bytes from provided buffer to TPM register (little + * endian format, i.e. buffer[0] is written as first byte). + * + * NOTE: TPM is big-endian for multi-byte values. Multi-byte + * values have to be swapped. + * + * NOTE: use this function instead of the iic_tpm_write_generic function. + * + * Return -EIO on error, 0 on success + */ +static int iic_tpm_write(u8 addr, u8 *buffer, size_t len) +{ + return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LOW, + SLEEP_DURATION_HI, MAX_COUNT); +} + +/* + * This function is needed especially for the cleanup situation after + * sending TPM_READY + * */ +static int iic_tpm_write_long(u8 addr, u8 *buffer, size_t len) +{ + return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG_LOW, + SLEEP_DURATION_LONG_HI, MAX_COUNT_LONG); +} + +enum tis_access { + TPM_ACCESS_VALID = 0x80, + TPM_ACCESS_ACTIVE_LOCALITY = 0x20, + TPM_ACCESS_REQUEST_PENDING = 0x04, + TPM_ACCESS_REQUEST_USE = 0x02, +}; + +enum tis_status { + TPM_STS_VALID = 0x80, + TPM_STS_COMMAND_READY = 0x40, + TPM_STS_GO = 0x20, + TPM_STS_DATA_AVAIL = 0x10, + TPM_STS_DATA_EXPECT = 0x08, +}; + +enum tis_defaults { + TIS_SHORT_TIMEOUT = 750, /* ms */ + TIS_LONG_TIMEOUT = 2000, /* 2 sec */ +}; + +#define TPM_ACCESS(l) (0x0000 | ((l) << 4)) +#define TPM_STS(l) (0x0001 | ((l) << 4)) +#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4)) +#define TPM_DID_VID(l) (0x0006 | ((l) << 4)) + +static int check_locality(struct tpm_chip *chip, int loc) +{ + u8 buf; + int rc; + + rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1); + if (rc < 0) + return rc; + + if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) { + chip->vendor.locality = loc; + return loc; + } + + return -EIO; +} + +/* implementation similar to tpm_tis */ +static void release_locality(struct tpm_chip *chip, int loc, int force) +{ + u8 buf; + if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0) + return; + + if (force || (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == + (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) { + buf = TPM_ACCESS_ACTIVE_LOCALITY; + iic_tpm_write(TPM_ACCESS(loc), &buf, 1); + } +} + +static int request_locality(struct tpm_chip *chip, int loc) +{ + unsigned long stop; + u8 buf = TPM_ACCESS_REQUEST_USE; + + if (check_locality(chip, loc) >= 0) + return loc; + + iic_tpm_write(TPM_ACCESS(loc), &buf, 1); + + /* wait for burstcount */ + stop = jiffies + chip->vendor.timeout_a; + do { + if (check_locality(chip, loc) >= 0) + return loc; + usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI); + } while (time_before(jiffies, stop)); + + return -ETIME; +} + +static u8 tpm_tis_i2c_status(struct tpm_chip *chip) +{ + /* NOTE: since I2C read may fail, return 0 in this case --> time-out */ + u8 buf; + if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0) + return 0; + else + return buf; +} + +static void tpm_tis_i2c_ready(struct tpm_chip *chip) +{ + /* this causes the current command to be aborted */ + u8 buf = TPM_STS_COMMAND_READY; + iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1); +} + +static ssize_t get_burstcount(struct tpm_chip *chip) +{ + unsigned long stop; + ssize_t burstcnt; + u8 buf[3]; + + /* wait for burstcount */ + /* which timeout value, spec has 2 answers (c & d) */ + stop = jiffies + chip->vendor.timeout_d; + do { + /* Note: STS is little endian */ + if (iic_tpm_read(TPM_STS(chip->vendor.locality)+1, buf, 3) < 0) + burstcnt = 0; + else + burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0]; + + if (burstcnt) + return burstcnt; + + usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI); + } while (time_before(jiffies, stop)); + return -EBUSY; +} + +static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, + int *status) +{ + unsigned long stop; + + /* check current status */ + *status = tpm_tis_i2c_status(chip); + if ((*status & mask) == mask) + return 0; + + stop = jiffies + timeout; + do { + /* since we just checked the status, give the TPM some time */ + usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI); + *status = tpm_tis_i2c_status(chip); + if ((*status & mask) == mask) + return 0; + + } while (time_before(jiffies, stop)); + + return -ETIME; +} + +static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) +{ + size_t size = 0; + ssize_t burstcnt; + u8 retries = 0; + int rc; + + while (size < count) { + burstcnt = get_burstcount(chip); + + /* burstcnt < 0 = TPM is busy */ + if (burstcnt < 0) + return burstcnt; + + /* limit received data to max. left */ + if (burstcnt > (count - size)) + burstcnt = count - size; + + rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality), + &(buf[size]), burstcnt); + if (rc == 0) + size += burstcnt; + else if (rc < 0) + retries++; + + /* avoid endless loop in case of broken HW */ + if (retries > MAX_COUNT_LONG) + return -EIO; + + } + return size; +} + +static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count) +{ + int size = 0; + int expected, status; + + if (count < TPM_HEADER_SIZE) { + size = -EIO; + goto out; + } + + /* read first 10 bytes, including tag, paramsize, and result */ + size = recv_data(chip, buf, TPM_HEADER_SIZE); + if (size < TPM_HEADER_SIZE) { + dev_err(chip->dev, "Unable to read header\n"); + goto out; + } + + expected = be32_to_cpu(*(__be32 *)(buf + 2)); + if ((size_t) expected > count) { + size = -EIO; + goto out; + } + + size += recv_data(chip, &buf[TPM_HEADER_SIZE], + expected - TPM_HEADER_SIZE); + if (size < expected) { + dev_err(chip->dev, "Unable to read remainder of result\n"); + size = -ETIME; + goto out; + } + + wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status); + if (status & TPM_STS_DATA_AVAIL) { /* retry? */ + dev_err(chip->dev, "Error left over data\n"); + size = -EIO; + goto out; + } + +out: + tpm_tis_i2c_ready(chip); + /* The TPM needs some time to clean up here, + * so we sleep rather than keeping the bus busy + */ + usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI); + release_locality(chip, chip->vendor.locality, 0); + return size; +} + +static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) +{ + int rc, status; + ssize_t burstcnt; + size_t count = 0; + u8 retries = 0; + u8 sts = TPM_STS_GO; + + if (len > TPM_BUFSIZE) + return -E2BIG; /* command is too long for our tpm, sorry */ + + if (request_locality(chip, 0) < 0) + return -EBUSY; + + status = tpm_tis_i2c_status(chip); + if ((status & TPM_STS_COMMAND_READY) == 0) { + tpm_tis_i2c_ready(chip); + if (wait_for_stat + (chip, TPM_STS_COMMAND_READY, + chip->vendor.timeout_b, &status) < 0) { + rc = -ETIME; + goto out_err; + } + } + + while (count < len - 1) { + burstcnt = get_burstcount(chip); + + /* burstcnt < 0 = TPM is busy */ + if (burstcnt < 0) + return burstcnt; + + if (burstcnt > (len - 1 - count)) + burstcnt = len - 1 - count; + + rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), + &(buf[count]), burstcnt); + if (rc == 0) + count += burstcnt; + else if (rc < 0) + retries++; + + /* avoid endless loop in case of broken HW */ + if (retries > MAX_COUNT_LONG) { + rc = -EIO; + goto out_err; + } + + wait_for_stat(chip, TPM_STS_VALID, + chip->vendor.timeout_c, &status); + + if ((status & TPM_STS_DATA_EXPECT) == 0) { + rc = -EIO; + goto out_err; + } + + } + + /* write last byte */ + iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1); + wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status); + if ((status & TPM_STS_DATA_EXPECT) != 0) { + rc = -EIO; + goto out_err; + } + + /* go and do it */ + iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1); + + return len; +out_err: + tpm_tis_i2c_ready(chip); + /* The TPM needs some time to clean up here, + * so we sleep rather than keeping the bus busy + */ + usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI); + release_locality(chip, chip->vendor.locality, 0); + return rc; +} + +static const struct file_operations tis_ops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = tpm_open, + .read = tpm_read, + .write = tpm_write, + .release = tpm_release, +}; + +static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); +static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); +static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL); +static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL); +static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL); +static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL); +static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL); +static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); +static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL); +static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL); + +static struct attribute *tis_attrs[] = { + &dev_attr_pubek.attr, + &dev_attr_pcrs.attr, + &dev_attr_enabled.attr, + &dev_attr_active.attr, + &dev_attr_owned.attr, + &dev_attr_temp_deactivated.attr, + &dev_attr_caps.attr, + &dev_attr_cancel.attr, + &dev_attr_durations.attr, + &dev_attr_timeouts.attr, + NULL, +}; + +static struct attribute_group tis_attr_grp = { + .attrs = tis_attrs +}; + +static struct tpm_vendor_specific tpm_tis_i2c = { + .status = tpm_tis_i2c_status, + .recv = tpm_tis_i2c_recv, + .send = tpm_tis_i2c_send, + .cancel = tpm_tis_i2c_ready, + .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_canceled = TPM_STS_COMMAND_READY, + .attr_group = &tis_attr_grp, + .miscdev.fops = &tis_ops, +}; + +static int __devinit tpm_tis_i2c_init(struct device *dev) +{ + u32 vendor; + int rc = 0; + struct tpm_chip *chip; + + chip = tpm_register_hardware(dev, &tpm_tis_i2c); + if (!chip) { + rc = -ENODEV; + goto out_err; + } + + /* Disable interrupts */ + chip->vendor.irq = 0; + + /* Default timeouts */ + chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); + chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + + if (request_locality(chip, 0) != 0) { + rc = -ENODEV; + goto out_vendor; + } + + /* read four bytes from DID_VID register */ + if (iic_tpm_read(TPM_DID_VID(0), (u8 *)&vendor, 4) < 0) { + rc = -EIO; + goto out_release; + } + + /* create DID_VID register value, after swapping to little-endian */ + vendor = be32_to_cpu((__be32) vendor); + + if (vendor != TPM_TIS_I2C_DID_VID) { + rc = -ENODEV; + goto out_release; + } + + dev_info(dev, "1.2 TPM (device-id 0x%X)\n", vendor >> 16); + + INIT_LIST_HEAD(&chip->vendor.list); + tpm_dev.chip = chip; + + tpm_get_timeouts(chip); + tpm_do_selftest(chip); + + return 0; + +out_release: + release_locality(chip, chip->vendor.locality, 1); + +out_vendor: + /* close file handles */ + tpm_dev_vendor_release(chip); + + /* remove hardware */ + tpm_remove_hardware(chip->dev); + + /* reset these pointers, otherwise we oops */ + chip->dev->release = NULL; + chip->release = NULL; + tpm_dev.client = NULL; + dev_set_drvdata(chip->dev, chip); +out_err: + return rc; +} + +static const struct i2c_device_id tpm_tis_i2c_table[] = { + {"tpm_i2c_infineon", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_table); +static SIMPLE_DEV_PM_OPS(tpm_tis_i2c_ops, tpm_pm_suspend, tpm_pm_resume); + +static int __devinit tpm_tis_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc; + if (tpm_dev.client != NULL) + return -EBUSY; /* We only support one client */ + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, + "no algorithms associated to the i2c bus\n"); + return -ENODEV; + } + + client->driver = &tpm_tis_i2c_driver; + tpm_dev.client = client; + rc = tpm_tis_i2c_init(&client->dev); + if (rc != 0) { + client->driver = NULL; + tpm_dev.client = NULL; + rc = -ENODEV; + } + return rc; +} + +static int __devexit tpm_tis_i2c_remove(struct i2c_client *client) +{ + struct tpm_chip *chip = tpm_dev.chip; + release_locality(chip, chip->vendor.locality, 1); + + /* close file handles */ + tpm_dev_vendor_release(chip); + + /* remove hardware */ + tpm_remove_hardware(chip->dev); + + /* reset these pointers, otherwise we oops */ + chip->dev->release = NULL; + chip->release = NULL; + tpm_dev.client = NULL; + dev_set_drvdata(chip->dev, chip); + + return 0; +} + +static struct i2c_driver tpm_tis_i2c_driver = { + + .id_table = tpm_tis_i2c_table, + .probe = tpm_tis_i2c_probe, + .remove = tpm_tis_i2c_remove, + .driver = { + .name = "tpm_i2c_infineon", + .owner = THIS_MODULE, + .pm = &tpm_tis_i2c_ops, + }, +}; + +module_i2c_driver(tpm_tis_i2c_driver); +MODULE_AUTHOR("Peter Huewe "); +MODULE_DESCRIPTION("TPM TIS I2C Infineon Driver"); +MODULE_VERSION("2.1.5"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From e5dcd87fee12ed64a9ea911102025facc0c7d10c Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Wed, 11 Jul 2012 10:08:12 -0500 Subject: tpm: modularize event log collection Break ACPI-specific pieces of the event log handling into their own file and create tpm_eventlog.[ch] to store common event log handling code. This will be required to integrate future event log sources on platforms without ACPI tables. Signed-off-by: Kent Yoder --- drivers/char/tpm/Makefile | 1 + drivers/char/tpm/tpm.c | 1 + drivers/char/tpm/tpm_acpi.c | 104 ++++++++ drivers/char/tpm/tpm_bios.c | 556 ---------------------------------------- drivers/char/tpm/tpm_eventlog.c | 419 ++++++++++++++++++++++++++++++ drivers/char/tpm/tpm_eventlog.h | 71 +++++ 6 files changed, 596 insertions(+), 556 deletions(-) create mode 100644 drivers/char/tpm/tpm_acpi.c delete mode 100644 drivers/char/tpm/tpm_bios.c create mode 100644 drivers/char/tpm/tpm_eventlog.c create mode 100644 drivers/char/tpm/tpm_eventlog.h (limited to 'drivers/char') diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index a9c3afc92dbc..beac52f61a82 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_TCG_TPM) += tpm.o ifdef CONFIG_ACPI obj-$(CONFIG_TCG_TPM) += tpm_bios.o + tpm_bios-objs += tpm_eventlog.o tpm_acpi.o endif obj-$(CONFIG_TCG_TIS) += tpm_tis.o obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 817f0ee202b6..677c6e26593f 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -30,6 +30,7 @@ #include #include "tpm.h" +#include "tpm_eventlog.h" enum tpm_const { TPM_MINOR = 224, /* officially assigned */ diff --git a/drivers/char/tpm/tpm_acpi.c b/drivers/char/tpm/tpm_acpi.c new file mode 100644 index 000000000000..a1bb5a182df9 --- /dev/null +++ b/drivers/char/tpm/tpm_acpi.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Seiji Munetoh + * Stefan Berger + * Reiner Sailer + * Kylene Hall + * + * Maintained by: + * + * Access to the eventlog extended by the TCG BIOS of PC platform + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "tpm.h" +#include "tpm_eventlog.h" + +struct acpi_tcpa { + struct acpi_table_header hdr; + u16 platform_class; + union { + struct client_hdr { + u32 log_max_len __attribute__ ((packed)); + u64 log_start_addr __attribute__ ((packed)); + } client; + struct server_hdr { + u16 reserved; + u64 log_max_len __attribute__ ((packed)); + u64 log_start_addr __attribute__ ((packed)); + } server; + }; +}; + +/* read binary bios log */ +int read_log(struct tpm_bios_log *log) +{ + struct acpi_tcpa *buff; + acpi_status status; + struct acpi_table_header *virt; + u64 len, start; + + if (log->bios_event_log != NULL) { + printk(KERN_ERR + "%s: ERROR - Eventlog already initialized\n", + __func__); + return -EFAULT; + } + + /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */ + status = acpi_get_table(ACPI_SIG_TCPA, 1, + (struct acpi_table_header **)&buff); + + if (ACPI_FAILURE(status)) { + printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n", + __func__); + return -EIO; + } + + switch(buff->platform_class) { + case BIOS_SERVER: + len = buff->server.log_max_len; + start = buff->server.log_start_addr; + break; + case BIOS_CLIENT: + default: + len = buff->client.log_max_len; + start = buff->client.log_start_addr; + break; + } + if (!len) { + printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__); + return -EIO; + } + + /* malloc EventLog space */ + log->bios_event_log = kmalloc(len, GFP_KERNEL); + if (!log->bios_event_log) { + printk("%s: ERROR - Not enough Memory for BIOS measurements\n", + __func__); + return -ENOMEM; + } + + log->bios_event_log_end = log->bios_event_log + len; + + virt = acpi_os_map_memory(start, len); + + memcpy(log->bios_event_log, virt, len); + + acpi_os_unmap_memory(virt, len); + return 0; +} diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_bios.c deleted file mode 100644 index 0636520fa9bf..000000000000 --- a/drivers/char/tpm/tpm_bios.c +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Copyright (C) 2005 IBM Corporation - * - * Authors: - * Seiji Munetoh - * Stefan Berger - * Reiner Sailer - * Kylene Hall - * - * Maintained by: - * - * Access to the eventlog extended by the TCG BIOS of PC platform - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include "tpm.h" - -#define TCG_EVENT_NAME_LEN_MAX 255 -#define MAX_TEXT_EVENT 1000 /* Max event string length */ -#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */ - -enum bios_platform_class { - BIOS_CLIENT = 0x00, - BIOS_SERVER = 0x01, -}; - -struct tpm_bios_log { - void *bios_event_log; - void *bios_event_log_end; -}; - -struct acpi_tcpa { - struct acpi_table_header hdr; - u16 platform_class; - union { - struct client_hdr { - u32 log_max_len __attribute__ ((packed)); - u64 log_start_addr __attribute__ ((packed)); - } client; - struct server_hdr { - u16 reserved; - u64 log_max_len __attribute__ ((packed)); - u64 log_start_addr __attribute__ ((packed)); - } server; - }; -}; - -struct tcpa_event { - u32 pcr_index; - u32 event_type; - u8 pcr_value[20]; /* SHA1 */ - u32 event_size; - u8 event_data[0]; -}; - -enum tcpa_event_types { - PREBOOT = 0, - POST_CODE, - UNUSED, - NO_ACTION, - SEPARATOR, - ACTION, - EVENT_TAG, - SCRTM_CONTENTS, - SCRTM_VERSION, - CPU_MICROCODE, - PLATFORM_CONFIG_FLAGS, - TABLE_OF_DEVICES, - COMPACT_HASH, - IPL, - IPL_PARTITION_DATA, - NONHOST_CODE, - NONHOST_CONFIG, - NONHOST_INFO, -}; - -static const char* tcpa_event_type_strings[] = { - "PREBOOT", - "POST CODE", - "", - "NO ACTION", - "SEPARATOR", - "ACTION", - "EVENT TAG", - "S-CRTM Contents", - "S-CRTM Version", - "CPU Microcode", - "Platform Config Flags", - "Table of Devices", - "Compact Hash", - "IPL", - "IPL Partition Data", - "Non-Host Code", - "Non-Host Config", - "Non-Host Info" -}; - -struct tcpa_pc_event { - u32 event_id; - u32 event_size; - u8 event_data[0]; -}; - -enum tcpa_pc_event_ids { - SMBIOS = 1, - BIS_CERT, - POST_BIOS_ROM, - ESCD, - CMOS, - NVRAM, - OPTION_ROM_EXEC, - OPTION_ROM_CONFIG, - OPTION_ROM_MICROCODE = 10, - S_CRTM_VERSION, - S_CRTM_CONTENTS, - POST_CONTENTS, - HOST_TABLE_OF_DEVICES, -}; - -static const char* tcpa_pc_event_id_strings[] = { - "", - "SMBIOS", - "BIS Certificate", - "POST BIOS ", - "ESCD ", - "CMOS", - "NVRAM", - "Option ROM", - "Option ROM config", - "", - "Option ROM microcode ", - "S-CRTM Version", - "S-CRTM Contents ", - "POST Contents ", - "Table of Devices", -}; - -/* returns pointer to start of pos. entry of tcg log */ -static void *tpm_bios_measurements_start(struct seq_file *m, loff_t *pos) -{ - loff_t i; - struct tpm_bios_log *log = m->private; - void *addr = log->bios_event_log; - void *limit = log->bios_event_log_end; - struct tcpa_event *event; - - /* read over *pos measurements */ - for (i = 0; i < *pos; i++) { - event = addr; - - if ((addr + sizeof(struct tcpa_event)) < limit) { - if (event->event_type == 0 && event->event_size == 0) - return NULL; - addr += sizeof(struct tcpa_event) + event->event_size; - } - } - - /* now check if current entry is valid */ - if ((addr + sizeof(struct tcpa_event)) >= limit) - return NULL; - - event = addr; - - if ((event->event_type == 0 && event->event_size == 0) || - ((addr + sizeof(struct tcpa_event) + event->event_size) >= limit)) - return NULL; - - return addr; -} - -static void *tpm_bios_measurements_next(struct seq_file *m, void *v, - loff_t *pos) -{ - struct tcpa_event *event = v; - struct tpm_bios_log *log = m->private; - void *limit = log->bios_event_log_end; - - v += sizeof(struct tcpa_event) + event->event_size; - - /* now check if current entry is valid */ - if ((v + sizeof(struct tcpa_event)) >= limit) - return NULL; - - event = v; - - if (event->event_type == 0 && event->event_size == 0) - return NULL; - - if ((event->event_type == 0 && event->event_size == 0) || - ((v + sizeof(struct tcpa_event) + event->event_size) >= limit)) - return NULL; - - (*pos)++; - return v; -} - -static void tpm_bios_measurements_stop(struct seq_file *m, void *v) -{ -} - -static int get_event_name(char *dest, struct tcpa_event *event, - unsigned char * event_entry) -{ - const char *name = ""; - /* 41 so there is room for 40 data and 1 nul */ - char data[41] = ""; - int i, n_len = 0, d_len = 0; - struct tcpa_pc_event *pc_event; - - switch(event->event_type) { - case PREBOOT: - case POST_CODE: - case UNUSED: - case NO_ACTION: - case SCRTM_CONTENTS: - case SCRTM_VERSION: - case CPU_MICROCODE: - case PLATFORM_CONFIG_FLAGS: - case TABLE_OF_DEVICES: - case COMPACT_HASH: - case IPL: - case IPL_PARTITION_DATA: - case NONHOST_CODE: - case NONHOST_CONFIG: - case NONHOST_INFO: - name = tcpa_event_type_strings[event->event_type]; - n_len = strlen(name); - break; - case SEPARATOR: - case ACTION: - if (MAX_TEXT_EVENT > event->event_size) { - name = event_entry; - n_len = event->event_size; - } - break; - case EVENT_TAG: - pc_event = (struct tcpa_pc_event *)event_entry; - - /* ToDo Row data -> Base64 */ - - switch (pc_event->event_id) { - case SMBIOS: - case BIS_CERT: - case CMOS: - case NVRAM: - case OPTION_ROM_EXEC: - case OPTION_ROM_CONFIG: - case S_CRTM_VERSION: - name = tcpa_pc_event_id_strings[pc_event->event_id]; - n_len = strlen(name); - break; - /* hash data */ - case POST_BIOS_ROM: - case ESCD: - case OPTION_ROM_MICROCODE: - case S_CRTM_CONTENTS: - case POST_CONTENTS: - name = tcpa_pc_event_id_strings[pc_event->event_id]; - n_len = strlen(name); - for (i = 0; i < 20; i++) - d_len += sprintf(&data[2*i], "%02x", - pc_event->event_data[i]); - break; - default: - break; - } - default: - break; - } - - return snprintf(dest, MAX_TEXT_EVENT, "[%.*s%.*s]", - n_len, name, d_len, data); - -} - -static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v) -{ - struct tcpa_event *event = v; - char *data = v; - int i; - - for (i = 0; i < sizeof(struct tcpa_event) + event->event_size; i++) - seq_putc(m, data[i]); - - return 0; -} - -static int tpm_bios_measurements_release(struct inode *inode, - struct file *file) -{ - struct seq_file *seq = file->private_data; - struct tpm_bios_log *log = seq->private; - - if (log) { - kfree(log->bios_event_log); - kfree(log); - } - - return seq_release(inode, file); -} - -static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) -{ - int len = 0; - int i; - char *eventname; - struct tcpa_event *event = v; - unsigned char *event_entry = - (unsigned char *) (v + sizeof(struct tcpa_event)); - - eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL); - if (!eventname) { - printk(KERN_ERR "%s: ERROR - No Memory for event name\n ", - __func__); - return -EFAULT; - } - - seq_printf(m, "%2d ", event->pcr_index); - - /* 2nd: SHA1 */ - for (i = 0; i < 20; i++) - seq_printf(m, "%02x", event->pcr_value[i]); - - /* 3rd: event type identifier */ - seq_printf(m, " %02x", event->event_type); - - len += get_event_name(eventname, event, event_entry); - - /* 4th: eventname <= max + \'0' delimiter */ - seq_printf(m, " %s\n", eventname); - - kfree(eventname); - return 0; -} - -static const struct seq_operations tpm_ascii_b_measurments_seqops = { - .start = tpm_bios_measurements_start, - .next = tpm_bios_measurements_next, - .stop = tpm_bios_measurements_stop, - .show = tpm_ascii_bios_measurements_show, -}; - -static const struct seq_operations tpm_binary_b_measurments_seqops = { - .start = tpm_bios_measurements_start, - .next = tpm_bios_measurements_next, - .stop = tpm_bios_measurements_stop, - .show = tpm_binary_bios_measurements_show, -}; - -/* read binary bios log */ -static int read_log(struct tpm_bios_log *log) -{ - struct acpi_tcpa *buff; - acpi_status status; - struct acpi_table_header *virt; - u64 len, start; - - if (log->bios_event_log != NULL) { - printk(KERN_ERR - "%s: ERROR - Eventlog already initialized\n", - __func__); - return -EFAULT; - } - - /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */ - status = acpi_get_table(ACPI_SIG_TCPA, 1, - (struct acpi_table_header **)&buff); - - if (ACPI_FAILURE(status)) { - printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n", - __func__); - return -EIO; - } - - switch(buff->platform_class) { - case BIOS_SERVER: - len = buff->server.log_max_len; - start = buff->server.log_start_addr; - break; - case BIOS_CLIENT: - default: - len = buff->client.log_max_len; - start = buff->client.log_start_addr; - break; - } - if (!len) { - printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__); - return -EIO; - } - - /* malloc EventLog space */ - log->bios_event_log = kmalloc(len, GFP_KERNEL); - if (!log->bios_event_log) { - printk("%s: ERROR - Not enough Memory for BIOS measurements\n", - __func__); - return -ENOMEM; - } - - log->bios_event_log_end = log->bios_event_log + len; - - virt = acpi_os_map_memory(start, len); - - memcpy(log->bios_event_log, virt, len); - - acpi_os_unmap_memory(virt, len); - return 0; -} - -static int tpm_ascii_bios_measurements_open(struct inode *inode, - struct file *file) -{ - int err; - struct tpm_bios_log *log; - struct seq_file *seq; - - log = kzalloc(sizeof(struct tpm_bios_log), GFP_KERNEL); - if (!log) - return -ENOMEM; - - if ((err = read_log(log))) - goto out_free; - - /* now register seq file */ - err = seq_open(file, &tpm_ascii_b_measurments_seqops); - if (!err) { - seq = file->private_data; - seq->private = log; - } else { - goto out_free; - } - -out: - return err; -out_free: - kfree(log->bios_event_log); - kfree(log); - goto out; -} - -static const struct file_operations tpm_ascii_bios_measurements_ops = { - .open = tpm_ascii_bios_measurements_open, - .read = seq_read, - .llseek = seq_lseek, - .release = tpm_bios_measurements_release, -}; - -static int tpm_binary_bios_measurements_open(struct inode *inode, - struct file *file) -{ - int err; - struct tpm_bios_log *log; - struct seq_file *seq; - - log = kzalloc(sizeof(struct tpm_bios_log), GFP_KERNEL); - if (!log) - return -ENOMEM; - - if ((err = read_log(log))) - goto out_free; - - /* now register seq file */ - err = seq_open(file, &tpm_binary_b_measurments_seqops); - if (!err) { - seq = file->private_data; - seq->private = log; - } else { - goto out_free; - } - -out: - return err; -out_free: - kfree(log->bios_event_log); - kfree(log); - goto out; -} - -static const struct file_operations tpm_binary_bios_measurements_ops = { - .open = tpm_binary_bios_measurements_open, - .read = seq_read, - .llseek = seq_lseek, - .release = tpm_bios_measurements_release, -}; - -static int is_bad(void *p) -{ - if (!p) - return 1; - if (IS_ERR(p) && (PTR_ERR(p) != -ENODEV)) - return 1; - return 0; -} - -struct dentry **tpm_bios_log_setup(char *name) -{ - struct dentry **ret = NULL, *tpm_dir, *bin_file, *ascii_file; - - tpm_dir = securityfs_create_dir(name, NULL); - if (is_bad(tpm_dir)) - goto out; - - bin_file = - securityfs_create_file("binary_bios_measurements", - S_IRUSR | S_IRGRP, tpm_dir, NULL, - &tpm_binary_bios_measurements_ops); - if (is_bad(bin_file)) - goto out_tpm; - - ascii_file = - securityfs_create_file("ascii_bios_measurements", - S_IRUSR | S_IRGRP, tpm_dir, NULL, - &tpm_ascii_bios_measurements_ops); - if (is_bad(ascii_file)) - goto out_bin; - - ret = kmalloc(3 * sizeof(struct dentry *), GFP_KERNEL); - if (!ret) - goto out_ascii; - - ret[0] = ascii_file; - ret[1] = bin_file; - ret[2] = tpm_dir; - - return ret; - -out_ascii: - securityfs_remove(ascii_file); -out_bin: - securityfs_remove(bin_file); -out_tpm: - securityfs_remove(tpm_dir); -out: - return NULL; -} -EXPORT_SYMBOL_GPL(tpm_bios_log_setup); - -void tpm_bios_log_teardown(struct dentry **lst) -{ - int i; - - for (i = 0; i < 3; i++) - securityfs_remove(lst[i]); -} -EXPORT_SYMBOL_GPL(tpm_bios_log_teardown); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/tpm_eventlog.c b/drivers/char/tpm/tpm_eventlog.c new file mode 100644 index 000000000000..84ddc557b8f8 --- /dev/null +++ b/drivers/char/tpm/tpm_eventlog.c @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2005, 2012 IBM Corporation + * + * Authors: + * Kent Yoder + * Seiji Munetoh + * Stefan Berger + * Reiner Sailer + * Kylene Hall + * + * Maintained by: + * + * Access to the eventlog created by a system's firmware / BIOS + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include + +#include "tpm.h" +#include "tpm_eventlog.h" + + +static const char* tcpa_event_type_strings[] = { + "PREBOOT", + "POST CODE", + "", + "NO ACTION", + "SEPARATOR", + "ACTION", + "EVENT TAG", + "S-CRTM Contents", + "S-CRTM Version", + "CPU Microcode", + "Platform Config Flags", + "Table of Devices", + "Compact Hash", + "IPL", + "IPL Partition Data", + "Non-Host Code", + "Non-Host Config", + "Non-Host Info" +}; + +static const char* tcpa_pc_event_id_strings[] = { + "", + "SMBIOS", + "BIS Certificate", + "POST BIOS ", + "ESCD ", + "CMOS", + "NVRAM", + "Option ROM", + "Option ROM config", + "", + "Option ROM microcode ", + "S-CRTM Version", + "S-CRTM Contents ", + "POST Contents ", + "Table of Devices", +}; + +/* returns pointer to start of pos. entry of tcg log */ +static void *tpm_bios_measurements_start(struct seq_file *m, loff_t *pos) +{ + loff_t i; + struct tpm_bios_log *log = m->private; + void *addr = log->bios_event_log; + void *limit = log->bios_event_log_end; + struct tcpa_event *event; + + /* read over *pos measurements */ + for (i = 0; i < *pos; i++) { + event = addr; + + if ((addr + sizeof(struct tcpa_event)) < limit) { + if (event->event_type == 0 && event->event_size == 0) + return NULL; + addr += sizeof(struct tcpa_event) + event->event_size; + } + } + + /* now check if current entry is valid */ + if ((addr + sizeof(struct tcpa_event)) >= limit) + return NULL; + + event = addr; + + if ((event->event_type == 0 && event->event_size == 0) || + ((addr + sizeof(struct tcpa_event) + event->event_size) >= limit)) + return NULL; + + return addr; +} + +static void *tpm_bios_measurements_next(struct seq_file *m, void *v, + loff_t *pos) +{ + struct tcpa_event *event = v; + struct tpm_bios_log *log = m->private; + void *limit = log->bios_event_log_end; + + v += sizeof(struct tcpa_event) + event->event_size; + + /* now check if current entry is valid */ + if ((v + sizeof(struct tcpa_event)) >= limit) + return NULL; + + event = v; + + if (event->event_type == 0 && event->event_size == 0) + return NULL; + + if ((event->event_type == 0 && event->event_size == 0) || + ((v + sizeof(struct tcpa_event) + event->event_size) >= limit)) + return NULL; + + (*pos)++; + return v; +} + +static void tpm_bios_measurements_stop(struct seq_file *m, void *v) +{ +} + +static int get_event_name(char *dest, struct tcpa_event *event, + unsigned char * event_entry) +{ + const char *name = ""; + /* 41 so there is room for 40 data and 1 nul */ + char data[41] = ""; + int i, n_len = 0, d_len = 0; + struct tcpa_pc_event *pc_event; + + switch(event->event_type) { + case PREBOOT: + case POST_CODE: + case UNUSED: + case NO_ACTION: + case SCRTM_CONTENTS: + case SCRTM_VERSION: + case CPU_MICROCODE: + case PLATFORM_CONFIG_FLAGS: + case TABLE_OF_DEVICES: + case COMPACT_HASH: + case IPL: + case IPL_PARTITION_DATA: + case NONHOST_CODE: + case NONHOST_CONFIG: + case NONHOST_INFO: + name = tcpa_event_type_strings[event->event_type]; + n_len = strlen(name); + break; + case SEPARATOR: + case ACTION: + if (MAX_TEXT_EVENT > event->event_size) { + name = event_entry; + n_len = event->event_size; + } + break; + case EVENT_TAG: + pc_event = (struct tcpa_pc_event *)event_entry; + + /* ToDo Row data -> Base64 */ + + switch (pc_event->event_id) { + case SMBIOS: + case BIS_CERT: + case CMOS: + case NVRAM: + case OPTION_ROM_EXEC: + case OPTION_ROM_CONFIG: + case S_CRTM_VERSION: + name = tcpa_pc_event_id_strings[pc_event->event_id]; + n_len = strlen(name); + break; + /* hash data */ + case POST_BIOS_ROM: + case ESCD: + case OPTION_ROM_MICROCODE: + case S_CRTM_CONTENTS: + case POST_CONTENTS: + name = tcpa_pc_event_id_strings[pc_event->event_id]; + n_len = strlen(name); + for (i = 0; i < 20; i++) + d_len += sprintf(&data[2*i], "%02x", + pc_event->event_data[i]); + break; + default: + break; + } + default: + break; + } + + return snprintf(dest, MAX_TEXT_EVENT, "[%.*s%.*s]", + n_len, name, d_len, data); + +} + +static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v) +{ + struct tcpa_event *event = v; + char *data = v; + int i; + + for (i = 0; i < sizeof(struct tcpa_event) + event->event_size; i++) + seq_putc(m, data[i]); + + return 0; +} + +static int tpm_bios_measurements_release(struct inode *inode, + struct file *file) +{ + struct seq_file *seq = file->private_data; + struct tpm_bios_log *log = seq->private; + + if (log) { + kfree(log->bios_event_log); + kfree(log); + } + + return seq_release(inode, file); +} + +static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) +{ + int len = 0; + int i; + char *eventname; + struct tcpa_event *event = v; + unsigned char *event_entry = + (unsigned char *) (v + sizeof(struct tcpa_event)); + + eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL); + if (!eventname) { + printk(KERN_ERR "%s: ERROR - No Memory for event name\n ", + __func__); + return -EFAULT; + } + + seq_printf(m, "%2d ", event->pcr_index); + + /* 2nd: SHA1 */ + for (i = 0; i < 20; i++) + seq_printf(m, "%02x", event->pcr_value[i]); + + /* 3rd: event type identifier */ + seq_printf(m, " %02x", event->event_type); + + len += get_event_name(eventname, event, event_entry); + + /* 4th: eventname <= max + \'0' delimiter */ + seq_printf(m, " %s\n", eventname); + + kfree(eventname); + return 0; +} + +static const struct seq_operations tpm_ascii_b_measurments_seqops = { + .start = tpm_bios_measurements_start, + .next = tpm_bios_measurements_next, + .stop = tpm_bios_measurements_stop, + .show = tpm_ascii_bios_measurements_show, +}; + +static const struct seq_operations tpm_binary_b_measurments_seqops = { + .start = tpm_bios_measurements_start, + .next = tpm_bios_measurements_next, + .stop = tpm_bios_measurements_stop, + .show = tpm_binary_bios_measurements_show, +}; + +static int tpm_ascii_bios_measurements_open(struct inode *inode, + struct file *file) +{ + int err; + struct tpm_bios_log *log; + struct seq_file *seq; + + log = kzalloc(sizeof(struct tpm_bios_log), GFP_KERNEL); + if (!log) + return -ENOMEM; + + if ((err = read_log(log))) + goto out_free; + + /* now register seq file */ + err = seq_open(file, &tpm_ascii_b_measurments_seqops); + if (!err) { + seq = file->private_data; + seq->private = log; + } else { + goto out_free; + } + +out: + return err; +out_free: + kfree(log->bios_event_log); + kfree(log); + goto out; +} + +static const struct file_operations tpm_ascii_bios_measurements_ops = { + .open = tpm_ascii_bios_measurements_open, + .read = seq_read, + .llseek = seq_lseek, + .release = tpm_bios_measurements_release, +}; + +static int tpm_binary_bios_measurements_open(struct inode *inode, + struct file *file) +{ + int err; + struct tpm_bios_log *log; + struct seq_file *seq; + + log = kzalloc(sizeof(struct tpm_bios_log), GFP_KERNEL); + if (!log) + return -ENOMEM; + + if ((err = read_log(log))) + goto out_free; + + /* now register seq file */ + err = seq_open(file, &tpm_binary_b_measurments_seqops); + if (!err) { + seq = file->private_data; + seq->private = log; + } else { + goto out_free; + } + +out: + return err; +out_free: + kfree(log->bios_event_log); + kfree(log); + goto out; +} + +static const struct file_operations tpm_binary_bios_measurements_ops = { + .open = tpm_binary_bios_measurements_open, + .read = seq_read, + .llseek = seq_lseek, + .release = tpm_bios_measurements_release, +}; + +static int is_bad(void *p) +{ + if (!p) + return 1; + if (IS_ERR(p) && (PTR_ERR(p) != -ENODEV)) + return 1; + return 0; +} + +struct dentry **tpm_bios_log_setup(char *name) +{ + struct dentry **ret = NULL, *tpm_dir, *bin_file, *ascii_file; + + tpm_dir = securityfs_create_dir(name, NULL); + if (is_bad(tpm_dir)) + goto out; + + bin_file = + securityfs_create_file("binary_bios_measurements", + S_IRUSR | S_IRGRP, tpm_dir, NULL, + &tpm_binary_bios_measurements_ops); + if (is_bad(bin_file)) + goto out_tpm; + + ascii_file = + securityfs_create_file("ascii_bios_measurements", + S_IRUSR | S_IRGRP, tpm_dir, NULL, + &tpm_ascii_bios_measurements_ops); + if (is_bad(ascii_file)) + goto out_bin; + + ret = kmalloc(3 * sizeof(struct dentry *), GFP_KERNEL); + if (!ret) + goto out_ascii; + + ret[0] = ascii_file; + ret[1] = bin_file; + ret[2] = tpm_dir; + + return ret; + +out_ascii: + securityfs_remove(ascii_file); +out_bin: + securityfs_remove(bin_file); +out_tpm: + securityfs_remove(tpm_dir); +out: + return NULL; +} +EXPORT_SYMBOL_GPL(tpm_bios_log_setup); + +void tpm_bios_log_teardown(struct dentry **lst) +{ + int i; + + for (i = 0; i < 3; i++) + securityfs_remove(lst[i]); +} +EXPORT_SYMBOL_GPL(tpm_bios_log_teardown); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/tpm_eventlog.h b/drivers/char/tpm/tpm_eventlog.h new file mode 100644 index 000000000000..8e23ccdf8a83 --- /dev/null +++ b/drivers/char/tpm/tpm_eventlog.h @@ -0,0 +1,71 @@ + +#ifndef __TPM_EVENTLOG_H__ +#define __TPM_EVENTLOG_H__ + +#define TCG_EVENT_NAME_LEN_MAX 255 +#define MAX_TEXT_EVENT 1000 /* Max event string length */ +#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */ + +enum bios_platform_class { + BIOS_CLIENT = 0x00, + BIOS_SERVER = 0x01, +}; + +struct tpm_bios_log { + void *bios_event_log; + void *bios_event_log_end; +}; + +struct tcpa_event { + u32 pcr_index; + u32 event_type; + u8 pcr_value[20]; /* SHA1 */ + u32 event_size; + u8 event_data[0]; +}; + +enum tcpa_event_types { + PREBOOT = 0, + POST_CODE, + UNUSED, + NO_ACTION, + SEPARATOR, + ACTION, + EVENT_TAG, + SCRTM_CONTENTS, + SCRTM_VERSION, + CPU_MICROCODE, + PLATFORM_CONFIG_FLAGS, + TABLE_OF_DEVICES, + COMPACT_HASH, + IPL, + IPL_PARTITION_DATA, + NONHOST_CODE, + NONHOST_CONFIG, + NONHOST_INFO, +}; + +struct tcpa_pc_event { + u32 event_id; + u32 event_size; + u8 event_data[0]; +}; + +enum tcpa_pc_event_ids { + SMBIOS = 1, + BIS_CERT, + POST_BIOS_ROM, + ESCD, + CMOS, + NVRAM, + OPTION_ROM_EXEC, + OPTION_ROM_CONFIG, + OPTION_ROM_MICROCODE = 10, + S_CRTM_VERSION, + S_CRTM_CONTENTS, + POST_CONTENTS, + HOST_TABLE_OF_DEVICES, +}; + +int read_log(struct tpm_bios_log *log); +#endif -- cgit v1.2.3 From 41ab999c80f1d368f32a2554ba8f44feff26f54d Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Thu, 7 Jun 2012 13:47:14 -0500 Subject: tpm: Move tpm_get_random api into the TPM device driver Move the tpm_get_random api from the trusted keys code into the TPM device driver itself so that other callers can make use of it. Also, change the api slightly so that the number of bytes read is returned in the call, since the TPM command can potentially return fewer bytes than requested. Acked-by: David Safford Reviewed-by: H. Peter Anvin Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.c | 59 +++++++++++++++++++++++++++++++++++++++++++++----- drivers/char/tpm/tpm.h | 23 ++++++++++++++++++++ 2 files changed, 76 insertions(+), 6 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 677c6e26593f..36e43e50dcef 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -32,12 +32,6 @@ #include "tpm.h" #include "tpm_eventlog.h" -enum tpm_const { - TPM_MINOR = 224, /* officially assigned */ - TPM_BUFSIZE = 4096, - TPM_NUM_DEVICES = 256, -}; - enum tpm_duration { TPM_SHORT = 0, TPM_MEDIUM = 1, @@ -483,6 +477,7 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd, #define TPM_INTERNAL_RESULT_SIZE 200 #define TPM_TAG_RQU_COMMAND cpu_to_be16(193) #define TPM_ORD_GET_CAP cpu_to_be32(101) +#define TPM_ORD_GET_RANDOM cpu_to_be32(70) static const struct tpm_input_header tpm_getcap_header = { .tag = TPM_TAG_RQU_COMMAND, @@ -1327,6 +1322,58 @@ int tpm_pm_resume(struct device *dev) } EXPORT_SYMBOL_GPL(tpm_pm_resume); +#define TPM_GETRANDOM_RESULT_SIZE 18 +static struct tpm_input_header tpm_getrandom_header = { + .tag = TPM_TAG_RQU_COMMAND, + .length = cpu_to_be32(14), + .ordinal = TPM_ORD_GET_RANDOM +}; + +/** + * tpm_get_random() - Get random bytes from the tpm's RNG + * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @out: destination buffer for the random bytes + * @max: the max number of bytes to write to @out + * + * Returns < 0 on error and the number of bytes read on success + */ +int tpm_get_random(u32 chip_num, u8 *out, size_t max) +{ + struct tpm_chip *chip; + struct tpm_cmd_t tpm_cmd; + u32 recd, num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA); + int err, total = 0, retries = 5; + u8 *dest = out; + + chip = tpm_chip_find_get(chip_num); + if (chip == NULL) + return -ENODEV; + + if (!out || !num_bytes || max > TPM_MAX_RNG_DATA) + return -EINVAL; + + do { + tpm_cmd.header.in = tpm_getrandom_header; + tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes); + + err = transmit_cmd(chip, &tpm_cmd, + TPM_GETRANDOM_RESULT_SIZE + num_bytes, + "attempting get random"); + if (err) + break; + + recd = be32_to_cpu(tpm_cmd.params.getrandom_out.rng_data_len); + memcpy(dest, tpm_cmd.params.getrandom_out.rng_data, recd); + + dest += recd; + total += recd; + num_bytes -= recd; + } while (retries-- && total < max); + + return total ? total : -EIO; +} +EXPORT_SYMBOL_GPL(tpm_get_random); + /* In case vendor provided release function, call it too.*/ void tpm_dev_vendor_release(struct tpm_chip *chip) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 917f727e6740..645136eea890 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -28,6 +28,12 @@ #include #include +enum tpm_const { + TPM_MINOR = 224, /* officially assigned */ + TPM_BUFSIZE = 4096, + TPM_NUM_DEVICES = 256, +}; + enum tpm_timeout { TPM_TIMEOUT = 5, /* msecs */ }; @@ -269,6 +275,21 @@ struct tpm_pcrextend_in { u8 hash[TPM_DIGEST_SIZE]; }__attribute__((packed)); +/* 128 bytes is an arbitrary cap. This could be as large as TPM_BUFSIZE - 18 + * bytes, but 128 is still a relatively large number of random bytes and + * anything much bigger causes users of struct tpm_cmd_t to start getting + * compiler warnings about stack frame size. */ +#define TPM_MAX_RNG_DATA 128 + +struct tpm_getrandom_out { + __be32 rng_data_len; + u8 rng_data[TPM_MAX_RNG_DATA]; +}__attribute__((packed)); + +struct tpm_getrandom_in { + __be32 num_bytes; +}__attribute__((packed)); + typedef union { struct tpm_getcap_params_out getcap_out; struct tpm_readpubek_params_out readpubek_out; @@ -277,6 +298,8 @@ typedef union { struct tpm_pcrread_in pcrread_in; struct tpm_pcrread_out pcrread_out; struct tpm_pcrextend_in pcrextend_in; + struct tpm_getrandom_in getrandom_in; + struct tpm_getrandom_out getrandom_out; } tpm_cmd_params; struct tpm_cmd_t { -- cgit v1.2.3 From 578b016fdc91464c08c096f0c5952cae549fdb8f Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Wed, 8 Aug 2012 14:18:57 -0500 Subject: hw_random: add support for the TPM chip as a hardware RNG source This driver will make use of any available TPM chip on the system as a hwrng source. Acked-by: David Safford Signed-off-by: Kent Yoder --- drivers/char/hw_random/Kconfig | 13 +++++++++++ drivers/char/hw_random/Makefile | 1 + drivers/char/hw_random/tpm-rng.c | 50 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 drivers/char/hw_random/tpm-rng.c (limited to 'drivers/char') diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 7c0d391996b5..fbd9b2b850ef 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -289,3 +289,16 @@ config HW_RANDOM_EXYNOS module will be called exynos-rng. If unsure, say Y. + +config HW_RANDOM_TPM + tristate "TPM HW Random Number Generator support" + depends on HW_RANDOM && TCG_TPM + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator in the Trusted Platform Module + + To compile this driver as a module, choose M here: the + module will be called tpm-rng. + + If unsure, say Y. diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 39a757ca15b6..1fd7eec9fbf6 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -25,3 +25,4 @@ obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o +obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o diff --git a/drivers/char/hw_random/tpm-rng.c b/drivers/char/hw_random/tpm-rng.c new file mode 100644 index 000000000000..d6d448266f07 --- /dev/null +++ b/drivers/char/hw_random/tpm-rng.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 Kent Yoder IBM Corporation + * + * HWRNG interfaces to pull RNG data from a TPM + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#define MODULE_NAME "tpm-rng" + +static int tpm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ + return tpm_get_random(TPM_ANY_NUM, data, max); +} + +static struct hwrng tpm_rng = { + .name = MODULE_NAME, + .read = tpm_rng_read, +}; + +static int __init rng_init(void) +{ + return hwrng_register(&tpm_rng); +} +module_init(rng_init); + +static void __exit rng_exit(void) +{ + hwrng_unregister(&tpm_rng); +} +module_exit(rng_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Kent Yoder "); +MODULE_DESCRIPTION("RNG driver for TPM devices"); -- cgit v1.2.3 From dd7da132f7f04f34074efd134847a818ea29ddd7 Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Wed, 25 Jul 2012 14:14:02 -0500 Subject: tpm: fix double write race and tpm_release free issue Moved the atomic_set of the data_pending variable until after the tpm_read has completed processing. The existing code had a window of time where a second write to the driver could clobber the tpm command buffer. Also fixed an issue where if close was called on the tpm device before a read completed, the tpm command buffer would be returned to the OS, which could contain sensitive information. Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 36e43e50dcef..0a75638e3e56 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1171,7 +1171,7 @@ int tpm_release(struct inode *inode, struct file *file) flush_work_sync(&chip->work); file->private_data = NULL; atomic_set(&chip->data_pending, 0); - kfree(chip->data_buffer); + kzfree(chip->data_buffer); clear_bit(0, &chip->is_open); put_device(chip->dev); return 0; @@ -1223,7 +1223,6 @@ ssize_t tpm_read(struct file *file, char __user *buf, del_singleshot_timer_sync(&chip->user_read_timer); flush_work_sync(&chip->work); ret_size = atomic_read(&chip->data_pending); - atomic_set(&chip->data_pending, 0); if (ret_size > 0) { /* relay data */ ssize_t orig_ret_size = ret_size; if (size < ret_size) @@ -1238,6 +1237,8 @@ ssize_t tpm_read(struct file *file, char __user *buf, mutex_unlock(&chip->buffer_mutex); } + atomic_set(&chip->data_pending, 0); + return ret_size; } EXPORT_SYMBOL_GPL(tpm_read); -- cgit v1.2.3 From 7e72fe73bfc7e4219b8dd212026c7113f4e37f91 Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Thu, 9 Aug 2012 09:20:15 -0500 Subject: tpm: compile out unused code in the PNP and PM cases The tpm_tis driver doesn't use tpm_tis_resume except when PM is configured and doesn't make use of tpm_tis_reenable_interrupts except when PM or PNP is configured. Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_tis.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index c4be3519a587..6bdf2671254f 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -705,6 +705,7 @@ out_err: return rc; } +#if defined(CONFIG_PNP) || defined(CONFIG_PM_SLEEP) static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) { u32 intmask; @@ -725,7 +726,7 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) iowrite32(intmask, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); } - +#endif #ifdef CONFIG_PNP static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, -- cgit v1.2.3 From 0a4af1473a7d81fc90e195fb5b241ab5fcf933ca Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 16 Aug 2012 16:46:08 -0600 Subject: sgi-agp: Use list_for_each_entry() for bus->devices traversal Replace list_for_each() + pci_dev_b() with the simpler list_for_each_entry(). Signed-off-by: Bjorn Helgaas Acked-by: Yinghai Lu CC: David Airlie --- drivers/char/agp/sgi-agp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c index 192000377737..3a5af2f9b015 100644 --- a/drivers/char/agp/sgi-agp.c +++ b/drivers/char/agp/sgi-agp.c @@ -289,12 +289,11 @@ static int __devinit agp_sgi_init(void) j = 0; list_for_each_entry(info, &tioca_list, ca_list) { - struct list_head *tmp; if (list_empty(info->ca_devices)) continue; - list_for_each(tmp, info->ca_devices) { + list_for_each_entry(pdev, info->ca_devices, bus_list) { u8 cap_ptr; - pdev = pci_dev_b(tmp); + if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8)) continue; cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); -- cgit v1.2.3 From 132f7629474424418a5cdd666796ad3cfa4dc0c5 Mon Sep 17 00:00:00 2001 From: Ashley Lai Date: Wed, 22 Aug 2012 16:17:43 -0500 Subject: drivers/char/tpm: Add new device driver to support IBM vTPM This patch adds a new device driver to support IBM virtual TPM (vTPM) for PPC64. IBM vTPM is supported through the adjunct partition with firmware release 740 or higher. With vTPM support, each lpar is able to have its own vTPM without the physical TPM hardware. This driver provides TPM functionalities by communicating with the vTPM adjunct partition through Hypervisor calls (Hcalls) and Command/Response Queue (CRQ) commands. Signed-off-by: Ashley Lai Signed-off-by: Kent Yoder --- drivers/char/tpm/Kconfig | 8 + drivers/char/tpm/Makefile | 1 + drivers/char/tpm/tpm.h | 1 + drivers/char/tpm/tpm_ibmvtpm.c | 749 +++++++++++++++++++++++++++++++++++++++++ drivers/char/tpm/tpm_ibmvtpm.h | 77 +++++ 5 files changed, 836 insertions(+) create mode 100644 drivers/char/tpm/tpm_ibmvtpm.c create mode 100644 drivers/char/tpm/tpm_ibmvtpm.h (limited to 'drivers/char') diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index c4aac486ade5..915875e431d2 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -73,4 +73,12 @@ config TCG_INFINEON Further information on this driver and the supported hardware can be found at http://www.trust.rub.de/projects/linux-device-driver-infineon-tpm/ +config TCG_IBMVTPM + tristate "IBM VTPM Interface" + depends on PPC64 + ---help--- + If you have IBM virtual TPM (VTPM) support say Yes and it + will be accessible from within Linux. To compile this driver + as a module, choose M here; the module will be called tpm_ibmvtpm. + endif # TCG_TPM diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index beac52f61a82..547509d02046 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o obj-$(CONFIG_TCG_NSC) += tpm_nsc.o obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o +obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 645136eea890..870fde7459c5 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -100,6 +100,7 @@ struct tpm_vendor_specific { bool timeout_adjusted; unsigned long duration[3]; /* jiffies */ bool duration_adjusted; + void *data; wait_queue_head_t read_queue; wait_queue_head_t int_queue; diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c new file mode 100644 index 000000000000..efc4ab36a9d6 --- /dev/null +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -0,0 +1,749 @@ +/* + * Copyright (C) 2012 IBM Corporation + * + * Author: Ashley Lai + * + * Maintained by: + * + * Device driver for TCG/TCPA TPM (trusted platform module). + * Specifications at www.trustedcomputinggroup.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tpm.h" +#include "tpm_ibmvtpm.h" + +static const char tpm_ibmvtpm_driver_name[] = "tpm_ibmvtpm"; + +static struct vio_device_id tpm_ibmvtpm_device_table[] __devinitdata = { + { "IBM,vtpm", "IBM,vtpm"}, + { "", "" } +}; +MODULE_DEVICE_TABLE(vio, tpm_ibmvtpm_device_table); + +DECLARE_WAIT_QUEUE_HEAD(wq); + +/** + * ibmvtpm_send_crq - Send a CRQ request + * @vdev: vio device struct + * @w1: first word + * @w2: second word + * + * Return value: + * 0 -Sucess + * Non-zero - Failure + */ +static int ibmvtpm_send_crq(struct vio_dev *vdev, u64 w1, u64 w2) +{ + return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, w1, w2); +} + +/** + * ibmvtpm_get_data - Retrieve ibm vtpm data + * @dev: device struct + * + * Return value: + * vtpm device struct + */ +static struct ibmvtpm_dev *ibmvtpm_get_data(const struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + if (chip) + return (struct ibmvtpm_dev *)chip->vendor.data; + return NULL; +} + +/** + * tpm_ibmvtpm_recv - Receive data after send + * @chip: tpm chip struct + * @buf: buffer to read + * count: size of buffer + * + * Return value: + * Number of bytes read + */ +static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct ibmvtpm_dev *ibmvtpm; + u16 len; + + ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data; + + if (!ibmvtpm->rtce_buf) { + dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n"); + return 0; + } + + wait_event_interruptible(wq, ibmvtpm->crq_res.len != 0); + + if (count < ibmvtpm->crq_res.len) { + dev_err(ibmvtpm->dev, + "Invalid size in recv: count=%ld, crq_size=%d\n", + count, ibmvtpm->crq_res.len); + return -EIO; + } + + spin_lock(&ibmvtpm->rtce_lock); + memcpy((void *)buf, (void *)ibmvtpm->rtce_buf, ibmvtpm->crq_res.len); + memset(ibmvtpm->rtce_buf, 0, ibmvtpm->crq_res.len); + ibmvtpm->crq_res.valid = 0; + ibmvtpm->crq_res.msg = 0; + len = ibmvtpm->crq_res.len; + ibmvtpm->crq_res.len = 0; + spin_unlock(&ibmvtpm->rtce_lock); + return len; +} + +/** + * tpm_ibmvtpm_send - Send tpm request + * @chip: tpm chip struct + * @buf: buffer contains data to send + * count: size of buffer + * + * Return value: + * Number of bytes sent + */ +static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct ibmvtpm_dev *ibmvtpm; + struct ibmvtpm_crq crq; + u64 *word = (u64 *) &crq; + int rc; + + ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data; + + if (!ibmvtpm->rtce_buf) { + dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n"); + return 0; + } + + if (count > ibmvtpm->rtce_size) { + dev_err(ibmvtpm->dev, + "Invalid size in send: count=%ld, rtce_size=%d\n", + count, ibmvtpm->rtce_size); + return -EIO; + } + + spin_lock(&ibmvtpm->rtce_lock); + memcpy((void *)ibmvtpm->rtce_buf, (void *)buf, count); + crq.valid = (u8)IBMVTPM_VALID_CMD; + crq.msg = (u8)VTPM_TPM_COMMAND; + crq.len = (u16)count; + crq.data = ibmvtpm->rtce_dma_handle; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, word[0], word[1]); + if (rc != H_SUCCESS) { + dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc); + rc = 0; + } else + rc = count; + + spin_unlock(&ibmvtpm->rtce_lock); + return rc; +} + +static void tpm_ibmvtpm_cancel(struct tpm_chip *chip) +{ + return; +} + +static u8 tpm_ibmvtpm_status(struct tpm_chip *chip) +{ + return 0; +} + +/** + * ibmvtpm_crq_get_rtce_size - Send a CRQ request to get rtce size + * @ibmvtpm: vtpm device struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int ibmvtpm_crq_get_rtce_size(struct ibmvtpm_dev *ibmvtpm) +{ + struct ibmvtpm_crq crq; + u64 *buf = (u64 *) &crq; + int rc; + + crq.valid = (u8)IBMVTPM_VALID_CMD; + crq.msg = (u8)VTPM_GET_RTCE_BUFFER_SIZE; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "ibmvtpm_crq_get_rtce_size failed rc=%d\n", rc); + + return rc; +} + +/** + * ibmvtpm_crq_get_version - Send a CRQ request to get vtpm version + * - Note that this is vtpm version and not tpm version + * @ibmvtpm: vtpm device struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int ibmvtpm_crq_get_version(struct ibmvtpm_dev *ibmvtpm) +{ + struct ibmvtpm_crq crq; + u64 *buf = (u64 *) &crq; + int rc; + + crq.valid = (u8)IBMVTPM_VALID_CMD; + crq.msg = (u8)VTPM_GET_VERSION; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "ibmvtpm_crq_get_version failed rc=%d\n", rc); + + return rc; +} + +/** + * ibmvtpm_crq_send_init_complete - Send a CRQ initialize complete message + * @ibmvtpm: vtpm device struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int ibmvtpm_crq_send_init_complete(struct ibmvtpm_dev *ibmvtpm) +{ + int rc; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, INIT_CRQ_COMP_CMD, 0); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "ibmvtpm_crq_send_init_complete failed rc=%d\n", rc); + + return rc; +} + +/** + * ibmvtpm_crq_send_init - Send a CRQ initialize message + * @ibmvtpm: vtpm device struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm) +{ + int rc; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, INIT_CRQ_CMD, 0); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "ibmvtpm_crq_send_init failed rc=%d\n", rc); + + return rc; +} + +/** + * tpm_ibmvtpm_remove - ibm vtpm remove entry point + * @vdev: vio device struct + * + * Return value: + * 0 + */ +static int __devexit tpm_ibmvtpm_remove(struct vio_dev *vdev) +{ + struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev); + int rc = 0; + + free_irq(vdev->irq, ibmvtpm); + tasklet_kill(&ibmvtpm->tasklet); + + do { + if (rc) + msleep(100); + rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); + } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); + + dma_unmap_single(ibmvtpm->dev, ibmvtpm->crq_dma_handle, + CRQ_RES_BUF_SIZE, DMA_BIDIRECTIONAL); + free_page((unsigned long)ibmvtpm->crq_queue.crq_addr); + + if (ibmvtpm->rtce_buf) { + dma_unmap_single(ibmvtpm->dev, ibmvtpm->rtce_dma_handle, + ibmvtpm->rtce_size, DMA_BIDIRECTIONAL); + kfree(ibmvtpm->rtce_buf); + } + + tpm_remove_hardware(ibmvtpm->dev); + + kfree(ibmvtpm); + + return 0; +} + +/** + * tpm_ibmvtpm_get_desired_dma - Get DMA size needed by this driver + * @vdev: vio device struct + * + * Return value: + * Number of bytes the driver needs to DMA map + */ +static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev) +{ + struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev); + return CRQ_RES_BUF_SIZE + ibmvtpm->rtce_size; +} + +/** + * tpm_ibmvtpm_suspend - Suspend + * @dev: device struct + * + * Return value: + * 0 + */ +static int tpm_ibmvtpm_suspend(struct device *dev) +{ + struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev); + struct ibmvtpm_crq crq; + u64 *buf = (u64 *) &crq; + int rc = 0; + + crq.valid = (u8)IBMVTPM_VALID_CMD; + crq.msg = (u8)VTPM_PREPARE_TO_SUSPEND; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "tpm_ibmvtpm_suspend failed rc=%d\n", rc); + + return rc; +} + +/** + * ibmvtpm_reset_crq - Reset CRQ + * @ibmvtpm: ibm vtpm struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm) +{ + int rc = 0; + + do { + if (rc) + msleep(100); + rc = plpar_hcall_norets(H_FREE_CRQ, + ibmvtpm->vdev->unit_address); + } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); + + memset(ibmvtpm->crq_queue.crq_addr, 0, CRQ_RES_BUF_SIZE); + ibmvtpm->crq_queue.index = 0; + + return plpar_hcall_norets(H_REG_CRQ, ibmvtpm->vdev->unit_address, + ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE); +} + +/** + * tpm_ibmvtpm_resume - Resume from suspend + * @dev: device struct + * + * Return value: + * 0 + */ +static int tpm_ibmvtpm_resume(struct device *dev) +{ + struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev); + unsigned long flags; + int rc = 0; + + do { + if (rc) + msleep(100); + rc = plpar_hcall_norets(H_ENABLE_CRQ, + ibmvtpm->vdev->unit_address); + } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc)); + + if (rc) { + dev_err(dev, "Error enabling ibmvtpm rc=%d\n", rc); + return rc; + } + + spin_lock_irqsave(&ibmvtpm->lock, flags); + vio_disable_interrupts(ibmvtpm->vdev); + tasklet_schedule(&ibmvtpm->tasklet); + spin_unlock_irqrestore(&ibmvtpm->lock, flags); + + rc = ibmvtpm_crq_send_init(ibmvtpm); + if (rc) + dev_err(dev, "Error send_init rc=%d\n", rc); + + return rc; +} + +static const struct file_operations ibmvtpm_ops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = tpm_open, + .read = tpm_read, + .write = tpm_write, + .release = tpm_release, +}; + +static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); +static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); +static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL); +static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL); +static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL); +static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, + NULL); +static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL); +static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); +static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL); +static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL); + +static struct attribute *ibmvtpm_attrs[] = { + &dev_attr_pubek.attr, + &dev_attr_pcrs.attr, + &dev_attr_enabled.attr, + &dev_attr_active.attr, + &dev_attr_owned.attr, + &dev_attr_temp_deactivated.attr, + &dev_attr_caps.attr, + &dev_attr_cancel.attr, + &dev_attr_durations.attr, + &dev_attr_timeouts.attr, NULL, +}; + +static struct attribute_group ibmvtpm_attr_grp = { .attrs = ibmvtpm_attrs }; + +static const struct tpm_vendor_specific tpm_ibmvtpm = { + .recv = tpm_ibmvtpm_recv, + .send = tpm_ibmvtpm_send, + .cancel = tpm_ibmvtpm_cancel, + .status = tpm_ibmvtpm_status, + .req_complete_mask = 0, + .req_complete_val = 0, + .req_canceled = 0, + .attr_group = &ibmvtpm_attr_grp, + .miscdev = { .fops = &ibmvtpm_ops, }, +}; + +static const struct dev_pm_ops tpm_ibmvtpm_pm_ops = { + .suspend = tpm_ibmvtpm_suspend, + .resume = tpm_ibmvtpm_resume, +}; + +/** + * ibmvtpm_crq_get_next - Get next responded crq + * @ibmvtpm vtpm device struct + * + * Return value: + * vtpm crq pointer + */ +static struct ibmvtpm_crq *ibmvtpm_crq_get_next(struct ibmvtpm_dev *ibmvtpm) +{ + struct ibmvtpm_crq_queue *crq_q = &ibmvtpm->crq_queue; + struct ibmvtpm_crq *crq = &crq_q->crq_addr[crq_q->index]; + + if (crq->valid & VTPM_MSG_RES) { + if (++crq_q->index == crq_q->num_entry) + crq_q->index = 0; + rmb(); + } else + crq = NULL; + return crq; +} + +/** + * ibmvtpm_crq_process - Process responded crq + * @crq crq to be processed + * @ibmvtpm vtpm device struct + * + * Return value: + * Nothing + */ +static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq, + struct ibmvtpm_dev *ibmvtpm) +{ + int rc = 0; + + switch (crq->valid) { + case VALID_INIT_CRQ: + switch (crq->msg) { + case INIT_CRQ_RES: + dev_info(ibmvtpm->dev, "CRQ initialized\n"); + rc = ibmvtpm_crq_send_init_complete(ibmvtpm); + if (rc) + dev_err(ibmvtpm->dev, "Unable to send CRQ init complete rc=%d\n", rc); + return; + case INIT_CRQ_COMP_RES: + dev_info(ibmvtpm->dev, + "CRQ initialization completed\n"); + return; + default: + dev_err(ibmvtpm->dev, "Unknown crq message type: %d\n", crq->msg); + return; + } + return; + case IBMVTPM_VALID_CMD: + switch (crq->msg) { + case VTPM_GET_RTCE_BUFFER_SIZE_RES: + if (crq->len <= 0) { + dev_err(ibmvtpm->dev, "Invalid rtce size\n"); + return; + } + ibmvtpm->rtce_size = crq->len; + ibmvtpm->rtce_buf = kmalloc(ibmvtpm->rtce_size, + GFP_KERNEL); + if (!ibmvtpm->rtce_buf) { + dev_err(ibmvtpm->dev, "Failed to allocate memory for rtce buffer\n"); + return; + } + + ibmvtpm->rtce_dma_handle = dma_map_single(ibmvtpm->dev, + ibmvtpm->rtce_buf, ibmvtpm->rtce_size, + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(ibmvtpm->dev, + ibmvtpm->rtce_dma_handle)) { + kfree(ibmvtpm->rtce_buf); + ibmvtpm->rtce_buf = NULL; + dev_err(ibmvtpm->dev, "Failed to dma map rtce buffer\n"); + } + + return; + case VTPM_GET_VERSION_RES: + ibmvtpm->vtpm_version = crq->data; + return; + case VTPM_TPM_COMMAND_RES: + ibmvtpm->crq_res.valid = crq->valid; + ibmvtpm->crq_res.msg = crq->msg; + ibmvtpm->crq_res.len = crq->len; + ibmvtpm->crq_res.data = crq->data; + wake_up_interruptible(&wq); + return; + default: + return; + } + } + return; +} + +/** + * ibmvtpm_interrupt - Interrupt handler + * @irq: irq number to handle + * @vtpm_instance: vtpm that received interrupt + * + * Returns: + * IRQ_HANDLED + **/ +static irqreturn_t ibmvtpm_interrupt(int irq, void *vtpm_instance) +{ + struct ibmvtpm_dev *ibmvtpm = (struct ibmvtpm_dev *) vtpm_instance; + unsigned long flags; + + spin_lock_irqsave(&ibmvtpm->lock, flags); + vio_disable_interrupts(ibmvtpm->vdev); + tasklet_schedule(&ibmvtpm->tasklet); + spin_unlock_irqrestore(&ibmvtpm->lock, flags); + + return IRQ_HANDLED; +} + +/** + * ibmvtpm_tasklet - Interrupt handler tasklet + * @data: ibm vtpm device struct + * + * Returns: + * Nothing + **/ +static void ibmvtpm_tasklet(void *data) +{ + struct ibmvtpm_dev *ibmvtpm = data; + struct ibmvtpm_crq *crq; + unsigned long flags; + + spin_lock_irqsave(&ibmvtpm->lock, flags); + while ((crq = ibmvtpm_crq_get_next(ibmvtpm)) != NULL) { + ibmvtpm_crq_process(crq, ibmvtpm); + crq->valid = 0; + wmb(); + } + + vio_enable_interrupts(ibmvtpm->vdev); + spin_unlock_irqrestore(&ibmvtpm->lock, flags); +} + +/** + * tpm_ibmvtpm_probe - ibm vtpm initialize entry point + * @vio_dev: vio device struct + * @id: vio device id struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int __devinit tpm_ibmvtpm_probe(struct vio_dev *vio_dev, + const struct vio_device_id *id) +{ + struct ibmvtpm_dev *ibmvtpm; + struct device *dev = &vio_dev->dev; + struct ibmvtpm_crq_queue *crq_q; + struct tpm_chip *chip; + int rc = -ENOMEM, rc1; + + chip = tpm_register_hardware(dev, &tpm_ibmvtpm); + if (!chip) { + dev_err(dev, "tpm_register_hardware failed\n"); + return -ENODEV; + } + + ibmvtpm = kzalloc(sizeof(struct ibmvtpm_dev), GFP_KERNEL); + if (!ibmvtpm) { + dev_err(dev, "kzalloc for ibmvtpm failed\n"); + goto cleanup; + } + + crq_q = &ibmvtpm->crq_queue; + crq_q->crq_addr = (struct ibmvtpm_crq *)get_zeroed_page(GFP_KERNEL); + if (!crq_q->crq_addr) { + dev_err(dev, "Unable to allocate memory for crq_addr\n"); + goto cleanup; + } + + crq_q->num_entry = CRQ_RES_BUF_SIZE / sizeof(*crq_q->crq_addr); + ibmvtpm->crq_dma_handle = dma_map_single(dev, crq_q->crq_addr, + CRQ_RES_BUF_SIZE, + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(dev, ibmvtpm->crq_dma_handle)) { + dev_err(dev, "dma mapping failed\n"); + goto cleanup; + } + + rc = plpar_hcall_norets(H_REG_CRQ, vio_dev->unit_address, + ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE); + if (rc == H_RESOURCE) + rc = ibmvtpm_reset_crq(ibmvtpm); + + if (rc) { + dev_err(dev, "Unable to register CRQ rc=%d\n", rc); + goto reg_crq_cleanup; + } + + tasklet_init(&ibmvtpm->tasklet, (void *)ibmvtpm_tasklet, + (unsigned long)ibmvtpm); + + rc = request_irq(vio_dev->irq, ibmvtpm_interrupt, 0, + tpm_ibmvtpm_driver_name, ibmvtpm); + if (rc) { + dev_err(dev, "Error %d register irq 0x%x\n", rc, vio_dev->irq); + goto init_irq_cleanup; + } + + rc = vio_enable_interrupts(vio_dev); + if (rc) { + dev_err(dev, "Error %d enabling interrupts\n", rc); + goto init_irq_cleanup; + } + + crq_q->index = 0; + + ibmvtpm->dev = dev; + ibmvtpm->vdev = vio_dev; + chip->vendor.data = (void *)ibmvtpm; + + spin_lock_init(&ibmvtpm->lock); + spin_lock_init(&ibmvtpm->rtce_lock); + + rc = ibmvtpm_crq_send_init(ibmvtpm); + if (rc) + goto init_irq_cleanup; + + rc = ibmvtpm_crq_get_version(ibmvtpm); + if (rc) + goto init_irq_cleanup; + + rc = ibmvtpm_crq_get_rtce_size(ibmvtpm); + if (rc) + goto init_irq_cleanup; + + return rc; +init_irq_cleanup: + tasklet_kill(&ibmvtpm->tasklet); + do { + rc1 = plpar_hcall_norets(H_FREE_CRQ, vio_dev->unit_address); + } while (rc1 == H_BUSY || H_IS_LONG_BUSY(rc1)); +reg_crq_cleanup: + dma_unmap_single(dev, ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE, + DMA_BIDIRECTIONAL); +cleanup: + if (ibmvtpm) { + if (crq_q->crq_addr) + free_page((unsigned long)crq_q->crq_addr); + kfree(ibmvtpm); + } + + tpm_remove_hardware(dev); + + return rc; +} + +static struct vio_driver ibmvtpm_driver = { + .id_table = tpm_ibmvtpm_device_table, + .probe = tpm_ibmvtpm_probe, + .remove = tpm_ibmvtpm_remove, + .get_desired_dma = tpm_ibmvtpm_get_desired_dma, + .name = tpm_ibmvtpm_driver_name, + .pm = &tpm_ibmvtpm_pm_ops, +}; + +/** + * ibmvtpm_module_init - Initialize ibm vtpm module + * + * Return value: + * 0 -Success + * Non-zero - Failure + */ +static int __init ibmvtpm_module_init(void) +{ + return vio_register_driver(&ibmvtpm_driver); +} + +/** + * ibmvtpm_module_exit - Teardown ibm vtpm module + * + * Return value: + * Nothing + */ +static void __exit ibmvtpm_module_exit(void) +{ + vio_unregister_driver(&ibmvtpm_driver); +} + +module_init(ibmvtpm_module_init); +module_exit(ibmvtpm_module_exit); + +MODULE_AUTHOR("adlai@us.ibm.com"); +MODULE_DESCRIPTION("IBM vTPM Driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/tpm_ibmvtpm.h b/drivers/char/tpm/tpm_ibmvtpm.h new file mode 100644 index 000000000000..4296eb4b4d82 --- /dev/null +++ b/drivers/char/tpm/tpm_ibmvtpm.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 IBM Corporation + * + * Author: Ashley Lai + * + * Maintained by: + * + * Device driver for TCG/TCPA TPM (trusted platform module). + * Specifications at www.trustedcomputinggroup.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + */ + +#ifndef __TPM_IBMVTPM_H__ +#define __TPM_IBMVTPM_H__ + +/* vTPM Message Format 1 */ +struct ibmvtpm_crq { + u8 valid; + u8 msg; + u16 len; + u32 data; + u64 reserved; +} __attribute__((packed, aligned(8))); + +struct ibmvtpm_crq_queue { + struct ibmvtpm_crq *crq_addr; + u32 index; + u32 num_entry; +}; + +struct ibmvtpm_dev { + struct device *dev; + struct vio_dev *vdev; + struct ibmvtpm_crq_queue crq_queue; + dma_addr_t crq_dma_handle; + spinlock_t lock; + struct tasklet_struct tasklet; + u32 rtce_size; + void __iomem *rtce_buf; + dma_addr_t rtce_dma_handle; + spinlock_t rtce_lock; + struct ibmvtpm_crq crq_res; + u32 vtpm_version; +}; + +#define CRQ_RES_BUF_SIZE PAGE_SIZE + +/* Initialize CRQ */ +#define INIT_CRQ_CMD 0xC001000000000000LL /* Init cmd */ +#define INIT_CRQ_COMP_CMD 0xC002000000000000LL /* Init complete cmd */ +#define INIT_CRQ_RES 0x01 /* Init respond */ +#define INIT_CRQ_COMP_RES 0x02 /* Init complete respond */ +#define VALID_INIT_CRQ 0xC0 /* Valid command for init crq */ + +/* vTPM CRQ response is the message type | 0x80 */ +#define VTPM_MSG_RES 0x80 +#define IBMVTPM_VALID_CMD 0x80 + +/* vTPM CRQ message types */ +#define VTPM_GET_VERSION 0x01 +#define VTPM_GET_VERSION_RES (0x01 | VTPM_MSG_RES) + +#define VTPM_TPM_COMMAND 0x02 +#define VTPM_TPM_COMMAND_RES (0x02 | VTPM_MSG_RES) + +#define VTPM_GET_RTCE_BUFFER_SIZE 0x03 +#define VTPM_GET_RTCE_BUFFER_SIZE_RES (0x03 | VTPM_MSG_RES) + +#define VTPM_PREPARE_TO_SUSPEND 0x04 +#define VTPM_PREPARE_TO_SUSPEND_RES (0x04 | VTPM_MSG_RES) + +#endif -- cgit v1.2.3 From c5df39262dd59dbbffb1017fca0f1661408ac9d5 Mon Sep 17 00:00:00 2001 From: Ashley Lai Date: Tue, 14 Aug 2012 18:35:32 -0500 Subject: drivers/char/tpm: Add securityfs support for event log This patch retrieves the event log data from the device tree during file open. The event log data will then displayed through securityfs. Signed-off-by: Ashley Lai Signed-off-by: Kent Yoder --- drivers/char/tpm/Makefile | 5 +++ drivers/char/tpm/tpm.h | 12 ------- drivers/char/tpm/tpm_eventlog.h | 15 +++++++++ drivers/char/tpm/tpm_of.c | 73 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 drivers/char/tpm/tpm_of.c (limited to 'drivers/char') diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 547509d02046..9080cc44e3c4 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -5,6 +5,11 @@ obj-$(CONFIG_TCG_TPM) += tpm.o ifdef CONFIG_ACPI obj-$(CONFIG_TCG_TPM) += tpm_bios.o tpm_bios-objs += tpm_eventlog.o tpm_acpi.o +else +ifdef CONFIG_TCG_IBMVTPM + obj-$(CONFIG_TCG_TPM) += tpm_bios.o + tpm_bios-objs += tpm_eventlog.o tpm_of.o +endif endif obj-$(CONFIG_TCG_TIS) += tpm_tis.o obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 870fde7459c5..f1af73821101 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -327,15 +327,3 @@ extern int tpm_pm_suspend(struct device *); extern int tpm_pm_resume(struct device *); extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long, wait_queue_head_t *); -#ifdef CONFIG_ACPI -extern struct dentry ** tpm_bios_log_setup(char *); -extern void tpm_bios_log_teardown(struct dentry **); -#else -static inline struct dentry ** tpm_bios_log_setup(char *name) -{ - return NULL; -} -static inline void tpm_bios_log_teardown(struct dentry **dir) -{ -} -#endif diff --git a/drivers/char/tpm/tpm_eventlog.h b/drivers/char/tpm/tpm_eventlog.h index 8e23ccdf8a83..e7da086d6928 100644 --- a/drivers/char/tpm/tpm_eventlog.h +++ b/drivers/char/tpm/tpm_eventlog.h @@ -68,4 +68,19 @@ enum tcpa_pc_event_ids { }; int read_log(struct tpm_bios_log *log); + +#if defined(CONFIG_TCG_IBMVTPM) || defined(CONFIG_TCG_IBMVTPM_MODULE) || \ + defined(CONFIG_ACPI) +extern struct dentry **tpm_bios_log_setup(char *); +extern void tpm_bios_log_teardown(struct dentry **); +#else +static inline struct dentry **tpm_bios_log_setup(char *name) +{ + return NULL; +} +static inline void tpm_bios_log_teardown(struct dentry **dir) +{ +} +#endif + #endif diff --git a/drivers/char/tpm/tpm_of.c b/drivers/char/tpm/tpm_of.c new file mode 100644 index 000000000000..98ba2bd1a355 --- /dev/null +++ b/drivers/char/tpm/tpm_of.c @@ -0,0 +1,73 @@ +/* + * Copyright 2012 IBM Corporation + * + * Author: Ashley Lai + * + * Maintained by: + * + * Read the event log created by the firmware on PPC64 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include + +#include "tpm.h" +#include "tpm_eventlog.h" + +int read_log(struct tpm_bios_log *log) +{ + struct device_node *np; + const u32 *sizep; + const __be64 *basep; + + if (log->bios_event_log != NULL) { + pr_err("%s: ERROR - Eventlog already initialized\n", __func__); + return -EFAULT; + } + + np = of_find_node_by_name(NULL, "ibm,vtpm"); + if (!np) { + pr_err("%s: ERROR - IBMVTPM not supported\n", __func__); + return -ENODEV; + } + + sizep = of_get_property(np, "linux,sml-size", NULL); + if (sizep == NULL) { + pr_err("%s: ERROR - SML size not found\n", __func__); + goto cleanup_eio; + } + if (*sizep == 0) { + pr_err("%s: ERROR - event log area empty\n", __func__); + goto cleanup_eio; + } + + basep = of_get_property(np, "linux,sml-base", NULL); + if (basep == NULL) { + pr_err(KERN_ERR "%s: ERROR - SML not found\n", __func__); + goto cleanup_eio; + } + + of_node_put(np); + log->bios_event_log = kmalloc(*sizep, GFP_KERNEL); + if (!log->bios_event_log) { + pr_err("%s: ERROR - Not enough memory for BIOS measurements\n", + __func__); + return -ENOMEM; + } + + log->bios_event_log_end = log->bios_event_log + *sizep; + + memcpy(log->bios_event_log, __va(be64_to_cpup(basep)), *sizep); + + return 0; + +cleanup_eio: + of_node_put(np); + return -EIO; +} -- cgit v1.2.3 From f334ac8da82478b3f8c52e3c01849ad7fe509d5b Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Thu, 16 Aug 2012 00:16:33 +0200 Subject: tpm: Do not dereference NULL pointer if acpi_os_map_memory() fails. In drivers/char/tpm/tpm_acpi.c::read_log() we call acpi_os_map_memory(). That call may fail for a number of reasons (invalid address, out of memory etc). If the call fails it returns NULL and we just pass that to memcpy() unconditionally, which will go bad when it tries to dereference the pointer. Unfortunately we just get NULL back, so we can't really tell the user exactely what went wrong, but we can at least avoid crashing and return an error (-EIO seemed more generic and more suitable here than -ENOMEM or something else, so I picked that). Signed-off-by: Jesper Juhl Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_acpi.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm_acpi.c b/drivers/char/tpm/tpm_acpi.c index a1bb5a182df9..fe3fa9431dc9 100644 --- a/drivers/char/tpm/tpm_acpi.c +++ b/drivers/char/tpm/tpm_acpi.c @@ -96,6 +96,11 @@ int read_log(struct tpm_bios_log *log) log->bios_event_log_end = log->bios_event_log + len; virt = acpi_os_map_memory(start, len); + if (!virt) { + kfree(log->bios_event_log); + printk("%s: ERROR - Unable to map memory\n", __func__); + return -EIO; + } memcpy(log->bios_event_log, virt, len); -- cgit v1.2.3 From f84fdff0fdcda7e509ce530e0ee612233a2104fb Mon Sep 17 00:00:00 2001 From: Xiaoyan Zhang Date: Wed, 22 Aug 2012 18:47:22 +0800 Subject: driver: add PPI support in tpm driver The Physical Presence Interface enables the OS and the BIOS to cooperate and provides a simple and straightforward platform user experience for administering the TPM without sacrificing security. V2: separate the patch out in a separate source file, add #ifdef CONFIG_ACPI so it compiles out on ppc, use standard error instead of ACPI error as return code of show/store fns. V3: move #ifdef CONFIG_ACPI from .c file to .h file. V4: move tpm_ppi code from tpm module to tpm_bios module. V5: modify sys_add_ppi() so that ppi_attr_grp doesn't need to be exported Signed-off-by: Xiaoyan Zhang Signed-off-by: Kent Yoder --- drivers/char/tpm/Makefile | 2 +- drivers/char/tpm/tpm.c | 5 + drivers/char/tpm/tpm.h | 9 + drivers/char/tpm/tpm_ppi.c | 460 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 drivers/char/tpm/tpm_ppi.c (limited to 'drivers/char') diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 9080cc44e3c4..5b3fc8bc6c13 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_TCG_TPM) += tpm.o ifdef CONFIG_ACPI obj-$(CONFIG_TCG_TPM) += tpm_bios.o - tpm_bios-objs += tpm_eventlog.o tpm_acpi.o + tpm_bios-objs += tpm_eventlog.o tpm_acpi.o tpm_ppi.o else ifdef CONFIG_TCG_IBMVTPM obj-$(CONFIG_TCG_TPM) += tpm_bios.o diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 0a75638e3e56..39526c053158 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1476,6 +1476,11 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, goto put_device; } + if (sys_add_ppi(&dev->kobj)) { + misc_deregister(&chip->vendor.miscdev); + goto put_device; + } + chip->bios_dir = tpm_bios_log_setup(devname); /* Make chip available */ diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index f1af73821101..02c266aa2bf7 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -327,3 +327,12 @@ extern int tpm_pm_suspend(struct device *); extern int tpm_pm_resume(struct device *); extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long, wait_queue_head_t *); + +#ifdef CONFIG_ACPI +extern ssize_t sys_add_ppi(struct kobject *parent); +#else +static inline ssize_t sys_add_ppi(struct kobject *parent) +{ + return 0; +} +#endif diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c new file mode 100644 index 000000000000..440fa1c2d301 --- /dev/null +++ b/drivers/char/tpm/tpm_ppi.c @@ -0,0 +1,460 @@ +#include +#include +#include "tpm.h" + +static const u8 tpm_ppi_uuid[] = { + 0xA6, 0xFA, 0xDD, 0x3D, + 0x1B, 0x36, + 0xB4, 0x4E, + 0xA4, 0x24, + 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53 +}; +static char *tpm_device_name = "TPM"; + +#define TPM_PPI_REVISION_ID 1 +#define TPM_PPI_FN_VERSION 1 +#define TPM_PPI_FN_SUBREQ 2 +#define TPM_PPI_FN_GETREQ 3 +#define TPM_PPI_FN_GETACT 4 +#define TPM_PPI_FN_GETRSP 5 +#define TPM_PPI_FN_SUBREQ2 7 +#define TPM_PPI_FN_GETOPR 8 +#define PPI_TPM_REQ_MAX 22 +#define PPI_VS_REQ_START 128 +#define PPI_VS_REQ_END 255 +#define PPI_VERSION_LEN 3 + +static acpi_status ppi_callback(acpi_handle handle, u32 level, void *context, + void **return_value) +{ + acpi_status status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + if (strstr(buffer.pointer, context) != NULL) { + *return_value = handle; + kfree(buffer.pointer); + return AE_CTRL_TERMINATE; + } + return AE_OK; +} + +static inline void ppi_assign_params(union acpi_object params[4], + u64 function_num) +{ + params[0].type = ACPI_TYPE_BUFFER; + params[0].buffer.length = sizeof(tpm_ppi_uuid); + params[0].buffer.pointer = (char *)tpm_ppi_uuid; + params[1].type = ACPI_TYPE_INTEGER; + params[1].integer.value = TPM_PPI_REVISION_ID; + params[2].type = ACPI_TYPE_INTEGER; + params[2].integer.value = function_num; + params[3].type = ACPI_TYPE_PACKAGE; + params[3].package.count = 0; + params[3].package.elements = NULL; +} + +ssize_t tpm_show_ppi_version(struct device *dev, struct device_attribute *attr, + char *buf) +{ + acpi_handle handle; + acpi_status status; + struct acpi_object_list input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object params[4]; + union acpi_object *obj; + + input.count = 4; + ppi_assign_params(params, TPM_PPI_FN_VERSION); + input.pointer = params; + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, ppi_callback, NULL, + tpm_device_name, &handle); + if (ACPI_FAILURE(status)) + return -ENXIO; + + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_STRING); + if (ACPI_FAILURE(status)) + return -ENOMEM; + obj = (union acpi_object *)output.pointer; + status = scnprintf(buf, PAGE_SIZE, "%s\n", obj->string.pointer); + kfree(output.pointer); + return status; +} + +ssize_t tpm_show_ppi_request(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + acpi_handle handle; + acpi_status status; + struct acpi_object_list input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object params[4]; + union acpi_object *ret_obj; + + input.count = 4; + ppi_assign_params(params, TPM_PPI_FN_GETREQ); + input.pointer = params; + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, ppi_callback, NULL, + tpm_device_name, &handle); + if (ACPI_FAILURE(status)) + return -ENXIO; + + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + return -ENOMEM; + /* + * output.pointer should be of package type, including two integers. + * The first is function return code, 0 means success and 1 means + * error. The second is pending TPM operation requested by the OS, 0 + * means none and >0 means operation value. + */ + ret_obj = ((union acpi_object *)output.pointer)->package.elements; + if (ret_obj->type == ACPI_TYPE_INTEGER) { + if (ret_obj->integer.value) { + status = -EFAULT; + goto cleanup; + } + ret_obj++; + if (ret_obj->type == ACPI_TYPE_INTEGER) + status = scnprintf(buf, PAGE_SIZE, "%llu\n", + ret_obj->integer.value); + else + status = -EINVAL; + } else { + status = -EINVAL; + } +cleanup: + kfree(output.pointer); + return status; +} + +ssize_t tpm_store_ppi_request(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char version[PPI_VERSION_LEN + 1]; + acpi_handle handle; + acpi_status status; + struct acpi_object_list input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object params[4]; + union acpi_object obj; + u32 req; + u64 ret; + + input.count = 4; + ppi_assign_params(params, TPM_PPI_FN_VERSION); + input.pointer = params; + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, ppi_callback, NULL, + tpm_device_name, &handle); + if (ACPI_FAILURE(status)) + return -ENXIO; + + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_STRING); + if (ACPI_FAILURE(status)) + return -ENOMEM; + strncpy(version, + ((union acpi_object *)output.pointer)->string.pointer, + PPI_VERSION_LEN); + kfree(output.pointer); + output.length = ACPI_ALLOCATE_BUFFER; + output.pointer = NULL; + /* + * the function to submit TPM operation request to pre-os environment + * is updated with function index from SUBREQ to SUBREQ2 since PPI + * version 1.1 + */ + if (strcmp(version, "1.1") == -1) + params[2].integer.value = TPM_PPI_FN_SUBREQ; + else + params[2].integer.value = TPM_PPI_FN_SUBREQ2; + /* + * PPI spec defines params[3].type as ACPI_TYPE_PACKAGE. Some BIOS + * accept buffer/string/integer type, but some BIOS accept buffer/ + * string/package type. For PPI version 1.0 and 1.1, use buffer type + * for compatibility, and use package type since 1.2 according to spec. + */ + if (strcmp(version, "1.2") == -1) { + params[3].type = ACPI_TYPE_BUFFER; + params[3].buffer.length = sizeof(req); + sscanf(buf, "%d", &req); + params[3].buffer.pointer = (char *)&req; + } else { + params[3].package.count = 1; + obj.type = ACPI_TYPE_INTEGER; + sscanf(buf, "%llu", &obj.integer.value); + params[3].package.elements = &obj; + } + + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_INTEGER); + if (ACPI_FAILURE(status)) + return -ENOMEM; + ret = ((union acpi_object *)output.pointer)->integer.value; + if (ret == 0) + status = (acpi_status)count; + else if (ret == 1) + status = -EPERM; + else + status = -EFAULT; + kfree(output.pointer); + return status; +} + +ssize_t tpm_show_ppi_transition_action(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + char version[PPI_VERSION_LEN + 1]; + acpi_handle handle; + acpi_status status; + struct acpi_object_list input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object params[4]; + u32 ret; + char *info[] = { + "None", + "Shutdown", + "Reboot", + "OS Vendor-specific", + "Error", + }; + input.count = 4; + ppi_assign_params(params, TPM_PPI_FN_VERSION); + input.pointer = params; + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, ppi_callback, NULL, + tpm_device_name, &handle); + if (ACPI_FAILURE(status)) + return -ENXIO; + + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_STRING); + if (ACPI_FAILURE(status)) + return -ENOMEM; + strncpy(version, + ((union acpi_object *)output.pointer)->string.pointer, + PPI_VERSION_LEN); + /* + * PPI spec defines params[3].type as empty package, but some platforms + * (e.g. Capella with PPI 1.0) need integer/string/buffer type, so for + * compatibility, define params[3].type as buffer, if PPI version < 1.2 + */ + if (strcmp(version, "1.2") == -1) { + params[3].type = ACPI_TYPE_BUFFER; + params[3].buffer.length = 0; + params[3].buffer.pointer = NULL; + } + params[2].integer.value = TPM_PPI_FN_GETACT; + kfree(output.pointer); + output.length = ACPI_ALLOCATE_BUFFER; + output.pointer = NULL; + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_INTEGER); + if (ACPI_FAILURE(status)) + return -ENOMEM; + ret = ((union acpi_object *)output.pointer)->integer.value; + if (ret < ARRAY_SIZE(info) - 1) + status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]); + else + status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, + info[ARRAY_SIZE(info)-1]); + kfree(output.pointer); + return status; +} + +ssize_t tpm_show_ppi_response(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + acpi_handle handle; + acpi_status status; + struct acpi_object_list input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object params[4]; + union acpi_object *ret_obj; + u64 req; + + input.count = 4; + ppi_assign_params(params, TPM_PPI_FN_GETRSP); + input.pointer = params; + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, ppi_callback, NULL, + tpm_device_name, &handle); + if (ACPI_FAILURE(status)) + return -ENXIO; + + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + return -ENOMEM; + /* + * parameter output.pointer should be of package type, including + * 3 integers. The first means function return code, the second means + * most recent TPM operation request, and the last means response to + * the most recent TPM operation request. Only if the first is 0, and + * the second integer is not 0, the response makes sense. + */ + ret_obj = ((union acpi_object *)output.pointer)->package.elements; + if (ret_obj->type != ACPI_TYPE_INTEGER) { + status = -EINVAL; + goto cleanup; + } + if (ret_obj->integer.value) { + status = -EFAULT; + goto cleanup; + } + ret_obj++; + if (ret_obj->type != ACPI_TYPE_INTEGER) { + status = -EINVAL; + goto cleanup; + } + if (ret_obj->integer.value) { + req = ret_obj->integer.value; + ret_obj++; + if (ret_obj->type != ACPI_TYPE_INTEGER) { + status = -EINVAL; + goto cleanup; + } + if (ret_obj->integer.value == 0) + status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, + "0: Success"); + else if (ret_obj->integer.value == 0xFFFFFFF0) + status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, + "0xFFFFFFF0: User Abort"); + else if (ret_obj->integer.value == 0xFFFFFFF1) + status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, + "0xFFFFFFF1: BIOS Failure"); + else if (ret_obj->integer.value >= 1 && + ret_obj->integer.value <= 0x00000FFF) + status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", + req, ret_obj->integer.value, + "Corresponding TPM error"); + else + status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", + req, ret_obj->integer.value, + "Error"); + } else { + status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n", + ret_obj->integer.value, "No Recent Request"); + } +cleanup: + kfree(output.pointer); + return status; +} + +static ssize_t show_ppi_operations(char *buf, u32 start, u32 end) +{ + char *str = buf; + char version[PPI_VERSION_LEN]; + acpi_handle handle; + acpi_status status; + struct acpi_object_list input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object params[4]; + union acpi_object obj; + int i; + u32 ret; + char *info[] = { + "Not implemented", + "BIOS only", + "Blocked for OS by BIOS", + "User required", + "User not required", + }; + input.count = 4; + ppi_assign_params(params, TPM_PPI_FN_VERSION); + input.pointer = params; + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, ppi_callback, NULL, + tpm_device_name, &handle); + if (ACPI_FAILURE(status)) + return -ENXIO; + + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_STRING); + if (ACPI_FAILURE(status)) + return -ENOMEM; + + strncpy(version, + ((union acpi_object *)output.pointer)->string.pointer, + PPI_VERSION_LEN); + kfree(output.pointer); + output.length = ACPI_ALLOCATE_BUFFER; + output.pointer = NULL; + if (strcmp(version, "1.2") == -1) + return -EPERM; + + params[2].integer.value = TPM_PPI_FN_GETOPR; + params[3].package.count = 1; + obj.type = ACPI_TYPE_INTEGER; + params[3].package.elements = &obj; + for (i = start; i <= end; i++) { + obj.integer.value = i; + status = acpi_evaluate_object_typed(handle, "_DSM", + &input, &output, ACPI_TYPE_INTEGER); + if (ACPI_FAILURE(status)) + return -ENOMEM; + + ret = ((union acpi_object *)output.pointer)->integer.value; + if (ret > 0 && ret < ARRAY_SIZE(info)) + str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n", + i, ret, info[ret]); + kfree(output.pointer); + output.length = ACPI_ALLOCATE_BUFFER; + output.pointer = NULL; + } + return str - buf; +} + +ssize_t tpm_show_ppi_tcg_operations(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return show_ppi_operations(buf, 0, PPI_TPM_REQ_MAX); +} + +ssize_t tpm_show_ppi_vs_operations(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return show_ppi_operations(buf, PPI_VS_REQ_START, PPI_VS_REQ_END); +} + +static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL); +static DEVICE_ATTR(request, S_IRUGO | S_IWUSR | S_IWGRP, + tpm_show_ppi_request, tpm_store_ppi_request); +static DEVICE_ATTR(transition_action, S_IRUGO, + tpm_show_ppi_transition_action, NULL); +static DEVICE_ATTR(response, S_IRUGO, tpm_show_ppi_response, NULL); +static DEVICE_ATTR(tcg_operations, S_IRUGO, tpm_show_ppi_tcg_operations, NULL); +static DEVICE_ATTR(vs_operations, S_IRUGO, tpm_show_ppi_vs_operations, NULL); + +static struct attribute *ppi_attrs[] = { + &dev_attr_version.attr, + &dev_attr_request.attr, + &dev_attr_transition_action.attr, + &dev_attr_response.attr, + &dev_attr_tcg_operations.attr, + &dev_attr_vs_operations.attr, NULL, +}; +static struct attribute_group ppi_attr_grp = { + .attrs = ppi_attrs +}; + +ssize_t sys_add_ppi(struct kobject *parent) +{ + struct kobject *ppi; + ppi = kobject_create_and_add("ppi", parent); + if (sysfs_create_group(ppi, &ppi_attr_grp)) + return -EFAULT; + else + return 0; +} +EXPORT_SYMBOL_GPL(sys_add_ppi); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 81198078d7da4240f3cbfc2c6a8ea6cd417f51a7 Mon Sep 17 00:00:00 2001 From: Xiaoyan Zhang Date: Thu, 29 Aug 2013 20:39:11 +0800 Subject: driver/char/tpm: declare internal symbols as static This patch declares the internal struct and functions as static to provide more security. Signed-off-by: Xiaoyan Zhang Signed-off-by: Fengguang Wu Reviewed-by: Kent Yoder Signed-off-by: James Morris --- drivers/char/tpm/tpm.c | 4 ++-- drivers/char/tpm/tpm_ppi.c | 37 +++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 20 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 39526c053158..6724615a4fdd 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -915,7 +915,7 @@ EXPORT_SYMBOL_GPL(tpm_show_pcrs); #define READ_PUBEK_RESULT_SIZE 314 #define TPM_ORD_READPUBEK cpu_to_be32(124) -struct tpm_input_header tpm_readpubek_header = { +static struct tpm_input_header tpm_readpubek_header = { .tag = TPM_TAG_RQU_COMMAND, .length = cpu_to_be32(30), .ordinal = TPM_ORD_READPUBEK @@ -1395,7 +1395,7 @@ EXPORT_SYMBOL_GPL(tpm_dev_vendor_release); * Once all references to platform device are down to 0, * release all allocated structures. */ -void tpm_dev_release(struct device *dev) +static void tpm_dev_release(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index 440fa1c2d301..f27b58cfae98 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -53,8 +53,8 @@ static inline void ppi_assign_params(union acpi_object params[4], params[3].package.elements = NULL; } -ssize_t tpm_show_ppi_version(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t tpm_show_ppi_version(struct device *dev, + struct device_attribute *attr, char *buf) { acpi_handle handle; acpi_status status; @@ -82,9 +82,8 @@ ssize_t tpm_show_ppi_version(struct device *dev, struct device_attribute *attr, return status; } -ssize_t tpm_show_ppi_request(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t tpm_show_ppi_request(struct device *dev, + struct device_attribute *attr, char *buf) { acpi_handle handle; acpi_status status; @@ -132,9 +131,9 @@ cleanup: return status; } -ssize_t tpm_store_ppi_request(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t tpm_store_ppi_request(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { char version[PPI_VERSION_LEN + 1]; acpi_handle handle; @@ -207,9 +206,9 @@ ssize_t tpm_store_ppi_request(struct device *dev, return status; } -ssize_t tpm_show_ppi_transition_action(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t tpm_show_ppi_transition_action(struct device *dev, + struct device_attribute *attr, + char *buf) { char version[PPI_VERSION_LEN + 1]; acpi_handle handle; @@ -269,9 +268,9 @@ ssize_t tpm_show_ppi_transition_action(struct device *dev, return status; } -ssize_t tpm_show_ppi_response(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t tpm_show_ppi_response(struct device *dev, + struct device_attribute *attr, + char *buf) { acpi_handle handle; acpi_status status; @@ -413,14 +412,16 @@ static ssize_t show_ppi_operations(char *buf, u32 start, u32 end) return str - buf; } -ssize_t tpm_show_ppi_tcg_operations(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t tpm_show_ppi_tcg_operations(struct device *dev, + struct device_attribute *attr, + char *buf) { return show_ppi_operations(buf, 0, PPI_TPM_REQ_MAX); } -ssize_t tpm_show_ppi_vs_operations(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t tpm_show_ppi_vs_operations(struct device *dev, + struct device_attribute *attr, + char *buf) { return show_ppi_operations(buf, PPI_VS_REQ_START, PPI_VS_REQ_END); } -- cgit v1.2.3 From f21ec3d2d46e5f2ffc06f31fe2704fdcea7a58f3 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Wed, 22 Aug 2012 22:13:36 -0400 Subject: serial: add a new helper function In most of the time, the driver needs to check if the cts flow control is enabled. But now, the driver checks the ASYNC_CTS_FLOW flag manually, which is not a grace way. So add a new wraper function to make the code tidy and clean. Signed-off-by: Huang Shijie Signed-off-by: Greg Kroah-Hartman --- drivers/char/pcmcia/synclink_cs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 5db08c78beb5..3f57d5de3957 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -1050,7 +1050,7 @@ static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty) wake_up_interruptible(&info->status_event_wait_q); wake_up_interruptible(&info->event_wait_q); - if (info->port.flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(&info->port)) { if (tty->hw_stopped) { if (info->serial_signals & SerialSignal_CTS) { if (debug_level >= DEBUG_LEVEL_ISR) -- cgit v1.2.3 From 9a32bb39d6a48d435135695763e5e4a06652eeda Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sun, 2 Sep 2012 00:31:58 +0400 Subject: ppdev: ppdev_init: do not return zero in case of failure Error handling of parport_register_driver() in ppdev_init() is broken because it deallocates all resources but still returns zero. Currently parport_register_driver() always succeeds. Nevertheless it is worth to fix the issue. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Greg Kroah-Hartman --- drivers/char/ppdev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 3fcf80ff12f2..d0d824ebf2c1 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -783,7 +783,8 @@ static int __init ppdev_init (void) err = PTR_ERR(ppdev_class); goto out_chrdev; } - if (parport_register_driver(&pp_driver)) { + err = parport_register_driver(&pp_driver); + if (err < 0) { printk (KERN_WARNING CHRDEV ": unable to register with parport\n"); goto out_class; } -- cgit v1.2.3 From efda0ad4aa92439d9244d77a13339e23df5e1dc1 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 1 Sep 2012 23:49:37 +0400 Subject: virtio: console: fix error handling in init() function If register_virtio_driver() fails, virtio-ports class is not destroyed. The patch adds error handling of register_virtio_driver(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Greg Kroah-Hartman --- drivers/char/virtio_console.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index cdf2f5451c76..060a672ebb7b 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1941,7 +1941,17 @@ static int __init init(void) INIT_LIST_HEAD(&pdrvdata.consoles); INIT_LIST_HEAD(&pdrvdata.portdevs); - return register_virtio_driver(&virtio_console); + err = register_virtio_driver(&virtio_console); + if (err < 0) { + pr_err("Error %d registering virtio driver\n", err); + goto free; + } + return 0; +free: + if (pdrvdata.debugfs_dir) + debugfs_remove_recursive(pdrvdata.debugfs_dir); + class_destroy(pdrvdata.class); + return err; } static void __exit fini(void) -- cgit v1.2.3 From a9ccb7bd0a781bce85db3b182e4c0c03688553c1 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 4 Sep 2012 18:35:21 -0300 Subject: hwrng: mxc-rnga - Adapt clocks to new i.mx clock framework Adapt clocks to the new i.mx clock framework and fix the following warning: ------------[ cut here ]------------ WARNING: at drivers/clk/clk.c:511 __clk_enable+0x9c/0xac() Modules linked in: Backtrace: [<800124c8>] (dump_backtrace+0x0/0x10c) from [<804172dc>] (dump_stack+0x18/0x1c) r7:00000009 r6:000001ff r5:8032cb50 r4:00000000 [<804172c4>] (dump_stack+0x0/0x1c) from [<80021834>] (warn_slowpath_common+0x54) [<800217e0>] (warn_slowpath_common+0x0/0x6c) from [<80021870>] (warn_slowpath_n) r9:80581cac r8:8700a9c0 r7:805ab070 r6:80000013 r5:806133d4 r4:8700a9c0 [<8002184c>] (warn_slowpath_null+0x0/0x2c) from [<8032cb50>] (__clk_enable+0x9c) [<8032cab4>] (__clk_enable+0x0/0xac) from [<8032cb88>] (clk_enable+0x28/0x44) r5:806133d4 r4:8700a9c0 [<8032cb60>] (clk_enable+0x0/0x44) from [<80560f14>] (mxc_rnga_probe+0x68/0x164) r7:805ab070 r6:8706ec00 r5:80611314 r4:00000000 [<80560eac>] (mxc_rnga_probe+0x0/0x164) from [<8025914c>] (platform_drv_probe+0) [<8025912c>] (platform_drv_probe+0x0/0x24) from [<80257c7c>] (driver_probe_devi) [<80257bfc>] (driver_probe_device+0x0/0x204) from [<80257e94>] (__driver_attach) r9:80581cac r8:0000008e r7:00000000 r6:8706ec3c r5:805ab070 r4:8706ec08 [<80257e00>] (__driver_attach+0x0/0x98) from [<8025642c>] (bus_for_each_dev+0x6) r7:00000000 r6:80257e00 r5:87035e98 r4:805ab070 [<802563c4>] (bus_for_each_dev+0x0/0x94) from [<80257adc>] (driver_attach+0x20/) r7:00000000 r6:873f2380 r5:805ab338 r4:805ab070 [<80257abc>] (driver_attach+0x0/0x28) from [<80256d50>] (bus_add_driver+0x18c/0) [<80256bc4>] (bus_add_driver+0x0/0x268) from [<802584c4>] (driver_register+0x80) [<80258444>] (driver_register+0x0/0x134) from [<802594f4>] (platform_driver_reg) r7:00000000 r6:805c2e00 r5:00000007 r4:805ab05c [<802594a8>] (platform_driver_register+0x0/0x60) from [<80259528>] (platform_dr) [<80259508>] (platform_driver_probe+0x0/0xa4) from [<80560ea0>] (mod_init+0x18/) r7:00000000 r6:805c2e00 r5:00000007 r4:87034000 [<80560e88>] (mod_init+0x0/0x24) from [<800086b4>] (do_one_initcall+0x40/0x194) [<80008674>] (do_one_initcall+0x0/0x194) from [<8053d3f4>] (kernel_init+0xfc/0x) [<8053d2f8>] (kernel_init+0x0/0x1cc) from [<80027190>] (do_exit+0x0/0x7ec) ---[ end trace 4198eed02050f461 ]--- Acked-by: Sascha Hauer Signed-off-by: Fabio Estevam Signed-off-by: Herbert Xu --- drivers/char/hw_random/mxc-rnga.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/mxc-rnga.c b/drivers/char/hw_random/mxc-rnga.c index 85074de5042e..62c7efe375ab 100644 --- a/drivers/char/hw_random/mxc-rnga.c +++ b/drivers/char/hw_random/mxc-rnga.c @@ -152,14 +152,14 @@ static int __init mxc_rnga_probe(struct platform_device *pdev) if (rng_dev) return -EBUSY; - clk = clk_get(&pdev->dev, "rng"); + clk = clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { dev_err(&pdev->dev, "Could not get rng_clk!\n"); err = PTR_ERR(clk); goto out; } - clk_enable(clk); + clk_prepare_enable(clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -201,7 +201,7 @@ err_ioremap: release_mem_region(res->start, resource_size(res)); err_region: - clk_disable(clk); + clk_disable_unprepare(clk); clk_put(clk); out: @@ -212,7 +212,7 @@ static int __exit mxc_rnga_remove(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); void __iomem *rng_base = (void __iomem *)mxc_rnga.priv; - struct clk *clk = clk_get(&pdev->dev, "rng"); + struct clk *clk = clk_get(&pdev->dev, NULL); hwrng_unregister(&mxc_rnga); @@ -220,7 +220,7 @@ static int __exit mxc_rnga_remove(struct platform_device *pdev) release_mem_region(res->start, resource_size(res)); - clk_disable(clk); + clk_disable_unprepare(clk); clk_put(clk); return 0; -- cgit v1.2.3 From 821873abc20e28fe3faa98591dab91d9b00700a5 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 4 Sep 2012 18:35:22 -0300 Subject: hwrng: mxc-rnga - Access data via structure In current driver, everytime we need to access the rng clock ,ie to enable or disable it, a call to clk_get is done. This is not correct and the preferred way is to provide a rng data structure that could be used for accessing rng resources. Acked-by: Sascha Hauer Signed-off-by: Fabio Estevam Signed-off-by: Herbert Xu --- drivers/char/hw_random/mxc-rnga.c | 108 ++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 57 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/mxc-rnga.c b/drivers/char/hw_random/mxc-rnga.c index 62c7efe375ab..f05d85713fd3 100644 --- a/drivers/char/hw_random/mxc-rnga.c +++ b/drivers/char/hw_random/mxc-rnga.c @@ -59,16 +59,21 @@ #define RNGA_STATUS_LAST_READ_STATUS 0x00000002 #define RNGA_STATUS_SECURITY_VIOLATION 0x00000001 -static struct platform_device *rng_dev; +struct mxc_rng { + struct device *dev; + struct hwrng rng; + void __iomem *mem; + struct clk *clk; +}; static int mxc_rnga_data_present(struct hwrng *rng, int wait) { - void __iomem *rng_base = (void __iomem *)rng->priv; int i; + struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); for (i = 0; i < 20; i++) { /* how many random numbers are in FIFO? [0-16] */ - int level = (__raw_readl(rng_base + RNGA_STATUS) & + int level = (__raw_readl(mxc_rng->mem + RNGA_STATUS) & RNGA_STATUS_LEVEL_MASK) >> 8; if (level || !wait) return !!level; @@ -81,20 +86,20 @@ static int mxc_rnga_data_read(struct hwrng *rng, u32 * data) { int err; u32 ctrl; - void __iomem *rng_base = (void __iomem *)rng->priv; + struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); /* retrieve a random number from FIFO */ - *data = __raw_readl(rng_base + RNGA_OUTPUT_FIFO); + *data = __raw_readl(mxc_rng->mem + RNGA_OUTPUT_FIFO); /* some error while reading this random number? */ - err = __raw_readl(rng_base + RNGA_STATUS) & RNGA_STATUS_ERROR_INT; + err = __raw_readl(mxc_rng->mem + RNGA_STATUS) & RNGA_STATUS_ERROR_INT; /* if error: clear error interrupt, but doesn't return random number */ if (err) { - dev_dbg(&rng_dev->dev, "Error while reading random number!\n"); - ctrl = __raw_readl(rng_base + RNGA_CONTROL); + dev_dbg(mxc_rng->dev, "Error while reading random number!\n"); + ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL); __raw_writel(ctrl | RNGA_CONTROL_CLEAR_INT, - rng_base + RNGA_CONTROL); + mxc_rng->mem + RNGA_CONTROL); return 0; } else return 4; @@ -103,22 +108,22 @@ static int mxc_rnga_data_read(struct hwrng *rng, u32 * data) static int mxc_rnga_init(struct hwrng *rng) { u32 ctrl, osc; - void __iomem *rng_base = (void __iomem *)rng->priv; + struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); /* wake up */ - ctrl = __raw_readl(rng_base + RNGA_CONTROL); - __raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, rng_base + RNGA_CONTROL); + ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL); + __raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, mxc_rng->mem + RNGA_CONTROL); /* verify if oscillator is working */ - osc = __raw_readl(rng_base + RNGA_STATUS); + osc = __raw_readl(mxc_rng->mem + RNGA_STATUS); if (osc & RNGA_STATUS_OSC_DEAD) { - dev_err(&rng_dev->dev, "RNGA Oscillator is dead!\n"); + dev_err(mxc_rng->dev, "RNGA Oscillator is dead!\n"); return -ENODEV; } /* go running */ - ctrl = __raw_readl(rng_base + RNGA_CONTROL); - __raw_writel(ctrl | RNGA_CONTROL_GO, rng_base + RNGA_CONTROL); + ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL); + __raw_writel(ctrl | RNGA_CONTROL_GO, mxc_rng->mem + RNGA_CONTROL); return 0; } @@ -126,40 +131,40 @@ static int mxc_rnga_init(struct hwrng *rng) static void mxc_rnga_cleanup(struct hwrng *rng) { u32 ctrl; - void __iomem *rng_base = (void __iomem *)rng->priv; + struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); - ctrl = __raw_readl(rng_base + RNGA_CONTROL); + ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL); /* stop rnga */ - __raw_writel(ctrl & ~RNGA_CONTROL_GO, rng_base + RNGA_CONTROL); + __raw_writel(ctrl & ~RNGA_CONTROL_GO, mxc_rng->mem + RNGA_CONTROL); } -static struct hwrng mxc_rnga = { - .name = "mxc-rnga", - .init = mxc_rnga_init, - .cleanup = mxc_rnga_cleanup, - .data_present = mxc_rnga_data_present, - .data_read = mxc_rnga_data_read -}; - static int __init mxc_rnga_probe(struct platform_device *pdev) { int err = -ENODEV; - struct clk *clk; struct resource *res, *mem; - void __iomem *rng_base = NULL; - - if (rng_dev) - return -EBUSY; - - clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) { + struct mxc_rng *mxc_rng; + + mxc_rng = devm_kzalloc(&pdev->dev, sizeof(struct mxc_rng), + GFP_KERNEL); + if (!mxc_rng) + return -ENOMEM; + + mxc_rng->dev = &pdev->dev; + mxc_rng->rng.name = "mxc-rnga"; + mxc_rng->rng.init = mxc_rnga_init; + mxc_rng->rng.cleanup = mxc_rnga_cleanup, + mxc_rng->rng.data_present = mxc_rnga_data_present, + mxc_rng->rng.data_read = mxc_rnga_data_read, + + mxc_rng->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mxc_rng->clk)) { dev_err(&pdev->dev, "Could not get rng_clk!\n"); - err = PTR_ERR(clk); + err = PTR_ERR(mxc_rng->clk); goto out; } - clk_prepare_enable(clk); + clk_prepare_enable(mxc_rng->clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -173,36 +178,27 @@ static int __init mxc_rnga_probe(struct platform_device *pdev) goto err_region; } - rng_base = ioremap(res->start, resource_size(res)); - if (!rng_base) { + mxc_rng->mem = ioremap(res->start, resource_size(res)); + if (!mxc_rng->mem) { err = -ENOMEM; goto err_ioremap; } - mxc_rnga.priv = (unsigned long)rng_base; - - err = hwrng_register(&mxc_rnga); + err = hwrng_register(&mxc_rng->rng); if (err) { dev_err(&pdev->dev, "MXC RNGA registering failed (%d)\n", err); - goto err_register; + goto err_ioremap; } - rng_dev = pdev; - dev_info(&pdev->dev, "MXC RNGA Registered.\n"); return 0; -err_register: - iounmap(rng_base); - rng_base = NULL; - err_ioremap: release_mem_region(res->start, resource_size(res)); err_region: - clk_disable_unprepare(clk); - clk_put(clk); + clk_disable_unprepare(mxc_rng->clk); out: return err; @@ -211,17 +207,15 @@ out: static int __exit mxc_rnga_remove(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - void __iomem *rng_base = (void __iomem *)mxc_rnga.priv; - struct clk *clk = clk_get(&pdev->dev, NULL); + struct mxc_rng *mxc_rng = platform_get_drvdata(pdev); - hwrng_unregister(&mxc_rnga); + hwrng_unregister(&mxc_rng->rng); - iounmap(rng_base); + iounmap(mxc_rng->mem); release_mem_region(res->start, resource_size(res)); - clk_disable_unprepare(clk); - clk_put(clk); + clk_disable_unprepare(mxc_rng->clk); return 0; } -- cgit v1.2.3 From a40695edad6a69561b299272028c172e2d981666 Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Tue, 4 Sep 2012 11:18:21 -0500 Subject: tpm: fix tpm_acpi sparse warning on different address spaces acpi_os_map_memory expects its return value to be in the __iomem address space. Tag the variable we're using as such and use memcpy_fromio to avoid further sparse warnings. Signed-off-by: Kent Yoder Signed-off-by: James Morris --- drivers/char/tpm/tpm_acpi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm_acpi.c b/drivers/char/tpm/tpm_acpi.c index fe3fa9431dc9..56051d0c97a2 100644 --- a/drivers/char/tpm/tpm_acpi.c +++ b/drivers/char/tpm/tpm_acpi.c @@ -49,7 +49,7 @@ int read_log(struct tpm_bios_log *log) { struct acpi_tcpa *buff; acpi_status status; - struct acpi_table_header *virt; + void __iomem *virt; u64 len, start; if (log->bios_event_log != NULL) { @@ -102,7 +102,7 @@ int read_log(struct tpm_bios_log *log) return -EIO; } - memcpy(log->bios_event_log, virt, len); + memcpy_fromio(log->bios_event_log, virt, len); acpi_os_unmap_memory(virt, len); return 0; -- cgit v1.2.3 From 65929215d85702dd4a76db0ba9fbfc022ab99e55 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 18 Sep 2012 16:14:53 +0100 Subject: char/misc: remove CONFIG_EXPERIMENTAL dependencies As discussed at the kernel summit this year, CONFIG_EXPERIMENTAL means nothing, so let's get rid of it. Cc: Kees Cook Cc: Arnd Bergmann Cc: Tomas Winkler Cc: Jiri Kosina Cc: Paul Bolle Cc: Andrew Morton Cc: Anatolij Gustschin Signed-off-by: Greg Kroah-Hartman --- drivers/char/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index ea6f6325f9ba..72bedad6bf8c 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -418,8 +418,8 @@ config APPLICOM If unsure, say N. config SONYPI - tristate "Sony Vaio Programmable I/O Control Device support (EXPERIMENTAL)" - depends on EXPERIMENTAL && X86 && PCI && INPUT && !64BIT + tristate "Sony Vaio Programmable I/O Control Device support" + depends on X86 && PCI && INPUT && !64BIT ---help--- This driver enables access to the Sony Programmable I/O Control Device which can be found in many (all ?) Sony Vaio laptops. @@ -566,7 +566,7 @@ source "drivers/char/tpm/Kconfig" config TELCLOCK tristate "Telecom clock driver for ATCA SBC" - depends on EXPERIMENTAL && X86 + depends on X86 default n help The telecom clock device is specific to the MPCBL0010 and MPCBL0050 -- cgit v1.2.3 From 9da3da660d8c19a54f6e93361d147509be3fff84 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 1 Jun 2012 15:20:22 +0100 Subject: drm/i915: Replace the array of pages with a scatterlist Rather than have multiple data structures for describing our page layout in conjunction with the array of pages, we can migrate all users over to a scatterlist. One major advantage, other than unifying the page tracking structures, this offers is that we replace the vmalloc'ed array (which can be up to a megabyte in size) with a chain of individual pages which helps reduce memory pressure. The disadvantage is that we then do not have a simple array to iterate, or to access randomly. The common case for this is in the relocation processing, which will typically fit within a single scatterlist page and so be almost the same cost as the simple array. For iterating over the array, the extra function call could be optimised away, but in reality is an insignificant cost of either binding the pages, or performing the pwrite/pread. v2: Fix drm_clflush_sg() to not invoke wbinvd as well! And fix the trivial compile error from rebasing. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 51 +++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 29 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 58e32f7c3229..7fa655ac24d8 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -84,40 +84,33 @@ static struct _intel_private { #define IS_IRONLAKE intel_private.driver->is_ironlake #define HAS_PGTBL_EN intel_private.driver->has_pgtbl_enable -int intel_gtt_map_memory(struct page **pages, unsigned int num_entries, - struct scatterlist **sg_list, int *num_sg) +static int intel_gtt_map_memory(struct page **pages, + unsigned int num_entries, + struct sg_table *st) { - struct sg_table st; struct scatterlist *sg; int i; - if (*sg_list) - return 0; /* already mapped (for e.g. resume */ - DBG("try mapping %lu pages\n", (unsigned long)num_entries); - if (sg_alloc_table(&st, num_entries, GFP_KERNEL)) + if (sg_alloc_table(st, num_entries, GFP_KERNEL)) goto err; - *sg_list = sg = st.sgl; - - for (i = 0 ; i < num_entries; i++, sg = sg_next(sg)) + for_each_sg(st->sgl, sg, num_entries, i) sg_set_page(sg, pages[i], PAGE_SIZE, 0); - *num_sg = pci_map_sg(intel_private.pcidev, *sg_list, - num_entries, PCI_DMA_BIDIRECTIONAL); - if (unlikely(!*num_sg)) + if (!pci_map_sg(intel_private.pcidev, + st->sgl, st->nents, PCI_DMA_BIDIRECTIONAL)) goto err; return 0; err: - sg_free_table(&st); + sg_free_table(st); return -ENOMEM; } -EXPORT_SYMBOL(intel_gtt_map_memory); -void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg) +static void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg) { struct sg_table st; DBG("try unmapping %lu pages\n", (unsigned long)mem->page_count); @@ -130,7 +123,6 @@ void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg) sg_free_table(&st); } -EXPORT_SYMBOL(intel_gtt_unmap_memory); static void intel_fake_agp_enable(struct agp_bridge_data *bridge, u32 mode) { @@ -879,8 +871,7 @@ static bool i830_check_flags(unsigned int flags) return false; } -void intel_gtt_insert_sg_entries(struct scatterlist *sg_list, - unsigned int sg_len, +void intel_gtt_insert_sg_entries(struct sg_table *st, unsigned int pg_start, unsigned int flags) { @@ -892,12 +883,11 @@ void intel_gtt_insert_sg_entries(struct scatterlist *sg_list, /* sg may merge pages, but we have to separate * per-page addr for GTT */ - for_each_sg(sg_list, sg, sg_len, i) { + for_each_sg(st->sgl, sg, st->nents, i) { len = sg_dma_len(sg) >> PAGE_SHIFT; for (m = 0; m < len; m++) { dma_addr_t addr = sg_dma_address(sg) + (m << PAGE_SHIFT); - intel_private.driver->write_entry(addr, - j, flags); + intel_private.driver->write_entry(addr, j, flags); j++; } } @@ -905,8 +895,10 @@ void intel_gtt_insert_sg_entries(struct scatterlist *sg_list, } EXPORT_SYMBOL(intel_gtt_insert_sg_entries); -void intel_gtt_insert_pages(unsigned int first_entry, unsigned int num_entries, - struct page **pages, unsigned int flags) +static void intel_gtt_insert_pages(unsigned int first_entry, + unsigned int num_entries, + struct page **pages, + unsigned int flags) { int i, j; @@ -917,7 +909,6 @@ void intel_gtt_insert_pages(unsigned int first_entry, unsigned int num_entries, } readl(intel_private.gtt+j-1); } -EXPORT_SYMBOL(intel_gtt_insert_pages); static int intel_fake_agp_insert_entries(struct agp_memory *mem, off_t pg_start, int type) @@ -953,13 +944,15 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem, global_cache_flush(); if (intel_private.base.needs_dmar) { - ret = intel_gtt_map_memory(mem->pages, mem->page_count, - &mem->sg_list, &mem->num_sg); + struct sg_table st; + + ret = intel_gtt_map_memory(mem->pages, mem->page_count, &st); if (ret != 0) return ret; - intel_gtt_insert_sg_entries(mem->sg_list, mem->num_sg, - pg_start, type); + intel_gtt_insert_sg_entries(&st, pg_start, type); + mem->sg_list = st.sgl; + mem->num_sg = st.nents; } else intel_gtt_insert_pages(pg_start, mem->page_count, mem->pages, type); -- cgit v1.2.3 From edef7e685da05c13cce50c0126189c80fe2c8f71 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 14 Sep 2012 11:57:47 +0100 Subject: agp/intel: Use a write-combining map for updating PTEs Rewriting the PTE entries using an WC mapping is roughly an order of magnitude faster than through the uncached mapping. This makes an observable difference on workloads that cycle through large numbers of buffers, for example Chromium using ShmPixmaps where virtually all the CPU time is currently spent rebinding the userptr. v2: Limit the WC mapping to older generations as we have observed that the TLB invalidation on SandyBridge+ is unreliable with WC updates. See i-g-t/tests/gem_gtt_cpu_tlb Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 7fa655ac24d8..e01f5eaaec82 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -666,9 +666,14 @@ static int intel_gtt_init(void) gtt_map_size = intel_private.base.gtt_total_entries * 4; - intel_private.gtt = ioremap(intel_private.gtt_bus_addr, - gtt_map_size); - if (!intel_private.gtt) { + intel_private.gtt = NULL; + if (INTEL_GTT_GEN < 6) + intel_private.gtt = ioremap_wc(intel_private.gtt_bus_addr, + gtt_map_size); + if (intel_private.gtt == NULL) + intel_private.gtt = ioremap(intel_private.gtt_bus_addr, + gtt_map_size); + if (intel_private.gtt == NULL) { intel_private.driver->cleanup(); iounmap(intel_private.registers); return -ENOMEM; -- cgit v1.2.3 From 02666360c92af7885c47163f936b58dc86213a53 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sun, 23 Sep 2012 17:28:26 -0600 Subject: hwrng: OMAP: store per-device data in per-device variables, not file statics Encapsulate all of the RNG per-device state into a single per-device structure record, as opposed to a set of per-driver file variables. Signed-off-by: Paul Walmsley Cc: Matt Mackall Cc: Herbert Xu Acked-by: Herbert Xu --- drivers/char/hw_random/omap-rng.c | 115 +++++++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 38 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index 4fbdceb6f773..e0f0b9839862 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -46,26 +47,38 @@ #define RNG_SYSSTATUS 0x44 /* System status [0] = RESETDONE */ -static void __iomem *rng_base; -static struct clk *rng_ick; -static struct platform_device *rng_dev; +/** + * struct omap_rng_private_data - RNG IP block-specific data + * @base: virtual address of the beginning of the RNG IP block registers + * @clk: RNG clock + * @mem_res: struct resource * for the IP block registers physical memory + */ +struct omap_rng_private_data { + void __iomem *base; + struct clk *clk; + struct resource *mem_res; +}; -static inline u32 omap_rng_read_reg(int reg) +static inline u32 omap_rng_read_reg(struct omap_rng_private_data *priv, int reg) { - return __raw_readl(rng_base + reg); + return __raw_readl(priv->base + reg); } -static inline void omap_rng_write_reg(int reg, u32 val) +static inline void omap_rng_write_reg(struct omap_rng_private_data *priv, + int reg, u32 val) { - __raw_writel(val, rng_base + reg); + __raw_writel(val, priv->base + reg); } static int omap_rng_data_present(struct hwrng *rng, int wait) { + struct omap_rng_private_data *priv; int data, i; + priv = (struct omap_rng_private_data *)rng->priv; + for (i = 0; i < 20; i++) { - data = omap_rng_read_reg(RNG_STAT_REG) ? 0 : 1; + data = omap_rng_read_reg(priv, RNG_STAT_REG) ? 0 : 1; if (data || !wait) break; /* RNG produces data fast enough (2+ MBit/sec, even @@ -80,9 +93,13 @@ static int omap_rng_data_present(struct hwrng *rng, int wait) static int omap_rng_data_read(struct hwrng *rng, u32 *data) { - *data = omap_rng_read_reg(RNG_OUT_REG); + struct omap_rng_private_data *priv; + + priv = (struct omap_rng_private_data *)rng->priv; + + *data = omap_rng_read_reg(priv, RNG_OUT_REG); - return 4; + return sizeof(u32); } static struct hwrng omap_rng_ops = { @@ -93,69 +110,85 @@ static struct hwrng omap_rng_ops = { static int __devinit omap_rng_probe(struct platform_device *pdev) { - struct resource *res; + struct omap_rng_private_data *priv; int ret; - /* - * A bit ugly, and it will never actually happen but there can - * be only one RNG and this catches any bork - */ - if (rng_dev) - return -EBUSY; + priv = kzalloc(sizeof(struct omap_rng_private_data), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "could not allocate memory\n"); + return -ENOMEM; + }; + + omap_rng_ops.priv = (unsigned long)priv; + dev_set_drvdata(&pdev->dev, priv); if (cpu_is_omap24xx()) { - rng_ick = clk_get(&pdev->dev, "ick"); - if (IS_ERR(rng_ick)) { + priv->clk = clk_get(&pdev->dev, "ick"); + if (IS_ERR(priv->clk)) { dev_err(&pdev->dev, "Could not get rng_ick\n"); - ret = PTR_ERR(rng_ick); + ret = PTR_ERR(priv->clk); return ret; - } else - clk_enable(rng_ick); + } else { + clk_enable(priv->clk); + } } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!priv->mem_res) { + ret = -ENOENT; + goto err_ioremap; + } - rng_base = devm_request_and_ioremap(&pdev->dev, res); - if (!rng_base) { + priv->base = devm_request_and_ioremap(&pdev->dev, priv->mem_res); + if (!priv->base) { ret = -ENOMEM; goto err_ioremap; } - dev_set_drvdata(&pdev->dev, res); + dev_set_drvdata(&pdev->dev, priv); ret = hwrng_register(&omap_rng_ops); if (ret) goto err_register; dev_info(&pdev->dev, "OMAP Random Number Generator ver. %02x\n", - omap_rng_read_reg(RNG_REV_REG)); - omap_rng_write_reg(RNG_MASK_REG, 0x1); + omap_rng_read_reg(priv, RNG_REV_REG)); + - rng_dev = pdev; + omap_rng_write_reg(priv, RNG_MASK_REG, 0x1); return 0; err_register: - rng_base = NULL; + priv->base = NULL; err_ioremap: if (cpu_is_omap24xx()) { - clk_disable(rng_ick); - clk_put(rng_ick); + clk_disable(priv->clk); + clk_put(priv->clk); } + + kfree(priv); + return ret; } static int __exit omap_rng_remove(struct platform_device *pdev) { + struct omap_rng_private_data *priv = dev_get_drvdata(&pdev->dev); + hwrng_unregister(&omap_rng_ops); - omap_rng_write_reg(RNG_MASK_REG, 0x0); + omap_rng_write_reg(priv, RNG_MASK_REG, 0x0); + + iounmap(priv->base); if (cpu_is_omap24xx()) { - clk_disable(rng_ick); - clk_put(rng_ick); + clk_disable(priv->clk); + clk_put(priv->clk); } - rng_base = NULL; + release_mem_region(priv->mem_res->start, resource_size(priv->mem_res)); + + kfree(priv); return 0; } @@ -164,13 +197,19 @@ static int __exit omap_rng_remove(struct platform_device *pdev) static int omap_rng_suspend(struct device *dev) { - omap_rng_write_reg(RNG_MASK_REG, 0x0); + struct omap_rng_private_data *priv = dev_get_drvdata(dev); + + omap_rng_write_reg(priv, RNG_MASK_REG, 0x0); + return 0; } static int omap_rng_resume(struct device *dev) { - omap_rng_write_reg(RNG_MASK_REG, 0x1); + struct omap_rng_private_data *priv = dev_get_drvdata(dev); + + omap_rng_write_reg(priv, RNG_MASK_REG, 0x1); + return 0; } -- cgit v1.2.3 From 665d92fa85b5cb4c0a3f36524ac6cc8f9e3d9c2d Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sun, 23 Sep 2012 17:28:26 -0600 Subject: hwrng: OMAP: convert to use runtime PM Convert the OMAP onboard hardware RNG driver to use runtime PM. This allows us to remove some OMAP-specific cpu_is_omap*() calls from the RNG driver. Signed-off-by: Paul Walmsley Cc: Matt Mackall Cc: Herbert Xu Acked-by: Herbert Xu --- drivers/char/hw_random/omap-rng.c | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index e0f0b9839862..748fcc8892f3 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -18,12 +18,12 @@ #include #include #include -#include #include #include #include #include #include +#include #include @@ -50,12 +50,10 @@ /** * struct omap_rng_private_data - RNG IP block-specific data * @base: virtual address of the beginning of the RNG IP block registers - * @clk: RNG clock * @mem_res: struct resource * for the IP block registers physical memory */ struct omap_rng_private_data { void __iomem *base; - struct clk *clk; struct resource *mem_res; }; @@ -122,17 +120,6 @@ static int __devinit omap_rng_probe(struct platform_device *pdev) omap_rng_ops.priv = (unsigned long)priv; dev_set_drvdata(&pdev->dev, priv); - if (cpu_is_omap24xx()) { - priv->clk = clk_get(&pdev->dev, "ick"); - if (IS_ERR(priv->clk)) { - dev_err(&pdev->dev, "Could not get rng_ick\n"); - ret = PTR_ERR(priv->clk); - return ret; - } else { - clk_enable(priv->clk); - } - } - priv->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!priv->mem_res) { ret = -ENOENT; @@ -146,6 +133,9 @@ static int __devinit omap_rng_probe(struct platform_device *pdev) } dev_set_drvdata(&pdev->dev, priv); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + ret = hwrng_register(&omap_rng_ops); if (ret) goto err_register; @@ -153,19 +143,14 @@ static int __devinit omap_rng_probe(struct platform_device *pdev) dev_info(&pdev->dev, "OMAP Random Number Generator ver. %02x\n", omap_rng_read_reg(priv, RNG_REV_REG)); - omap_rng_write_reg(priv, RNG_MASK_REG, 0x1); return 0; err_register: priv->base = NULL; + pm_runtime_disable(&pdev->dev); err_ioremap: - if (cpu_is_omap24xx()) { - clk_disable(priv->clk); - clk_put(priv->clk); - } - kfree(priv); return ret; @@ -179,12 +164,8 @@ static int __exit omap_rng_remove(struct platform_device *pdev) omap_rng_write_reg(priv, RNG_MASK_REG, 0x0); - iounmap(priv->base); - - if (cpu_is_omap24xx()) { - clk_disable(priv->clk); - clk_put(priv->clk); - } + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); release_mem_region(priv->mem_res->start, resource_size(priv->mem_res)); @@ -200,6 +181,7 @@ static int omap_rng_suspend(struct device *dev) struct omap_rng_private_data *priv = dev_get_drvdata(dev); omap_rng_write_reg(priv, RNG_MASK_REG, 0x0); + pm_runtime_put_sync(dev); return 0; } @@ -208,6 +190,7 @@ static int omap_rng_resume(struct device *dev) { struct omap_rng_private_data *priv = dev_get_drvdata(dev); + pm_runtime_get_sync(dev); omap_rng_write_reg(priv, RNG_MASK_REG, 0x1); return 0; -- cgit v1.2.3 From fe47c58ba82c967729c76869a5f10a94c38754f3 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sun, 23 Sep 2012 17:28:27 -0600 Subject: hwrng: OMAP: remove SoC restrictions from driver registration Remove the SoC restriction code from the OMAP RNG driver. The integration code in arch/arm/*omap* should handle this. The device shouldn't be created if it doesn't exist on the currently-booted SoC. This allows us to remove some OMAP-specific cpu_is_omap*() calls from the driver. Also, if other OMAP chips have RNGs that can be used by Linux, there will be no need to modify the driver. Signed-off-by: Paul Walmsley Cc: Matt Mackall Cc: Herbert Xu Acked-by: Herbert Xu --- drivers/char/hw_random/omap-rng.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index 748fcc8892f3..a5effd813abd 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -220,9 +220,6 @@ static struct platform_driver omap_rng_driver = { static int __init omap_rng_init(void) { - if (!cpu_is_omap16xx() && !cpu_is_omap24xx()) - return -ENODEV; - return platform_driver_register(&omap_rng_driver); } -- cgit v1.2.3 From b87570f5d349661814b262dd5fc40787700f80d6 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Wed, 26 Sep 2012 07:46:40 +0200 Subject: Fix a crash when block device is read and block size is changed at the same time The kernel may crash when block size is changed and I/O is issued simultaneously. Because some subsystems (udev or lvm) may read any block device anytime, the bug actually puts any code that changes a block device size in jeopardy. The crash can be reproduced if you place "msleep(1000)" to blkdev_get_blocks just before "bh->b_size = max_blocks << inode->i_blkbits;". Then, run "dd if=/dev/ram0 of=/dev/null bs=4k count=1 iflag=direct" While it is waiting in msleep, run "blockdev --setbsz 2048 /dev/ram0" You get a BUG. The direct and non-direct I/O is written with the assumption that block size does not change. It doesn't seem practical to fix these crashes one-by-one there may be many crash possibilities when block size changes at a certain place and it is impossible to find them all and verify the code. This patch introduces a new rw-lock bd_block_size_semaphore. The lock is taken for read during I/O. It is taken for write when changing block size. Consequently, block size can't be changed while I/O is being submitted. For asynchronous I/O, the patch only prevents block size change while the I/O is being submitted. The block size can change when the I/O is in progress or when the I/O is being finished. This is acceptable because there are no accesses to block size when asynchronous I/O is being finished. The patch prevents block size changing while the device is mapped with mmap. Signed-off-by: Mikulas Patocka Signed-off-by: Jens Axboe --- drivers/char/raw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/raw.c b/drivers/char/raw.c index 54a3a6d09819..0bb207eaef2f 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -285,7 +285,7 @@ static long raw_ctl_compat_ioctl(struct file *file, unsigned int cmd, static const struct file_operations raw_fops = { .read = do_sync_read, - .aio_read = generic_file_aio_read, + .aio_read = blkdev_aio_read, .write = do_sync_write, .aio_write = blkdev_aio_write, .fsync = blkdev_fsync, -- cgit v1.2.3 From f8885c26d59e683185002271108e8acb812fcd07 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Wed, 12 Sep 2012 19:03:18 +0200 Subject: drivers/char: removes unnecessary semicolon removes unnecessary semicolon Found by Coccinelle: http://coccinelle.lip6.fr/ Signed-off-by: Peter Senna Tschudin Signed-off-by: Greg Kroah-Hartman --- drivers/char/nwbutton.c | 4 ++-- drivers/char/rtc.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/nwbutton.c b/drivers/char/nwbutton.c index 04a480f86c6c..cfdfe493c6af 100644 --- a/drivers/char/nwbutton.c +++ b/drivers/char/nwbutton.c @@ -93,9 +93,9 @@ int button_del_callback (void (*callback) (void)) button_callback_list [lp].count = 0; callback_count--; return 0; - }; + } lp--; - }; + } return -EINVAL; } diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index af9437488b6c..91470fdbab2a 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -411,7 +411,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) case RTC_IRQP_READ: case RTC_IRQP_SET: return -EINVAL; - }; + } } #endif -- cgit v1.2.3 From 08b79dd10bb24f5fb12446d21d32c87a8136c770 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Wed, 12 Sep 2012 17:06:45 +0200 Subject: drivers/char/mmtimer.c: Remove useless kfree Remove useless kfree() and clean up code related to the removal. The semantic patch that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r exists@ position p1,p2; expression x; @@ if (x@p1 == NULL) { ... kfree@p2(x); ... return ...; } @unchanged exists@ position r.p1,r.p2; expression e <= r.x,x,e1; iterator I; statement S; @@ if (x@p1 == NULL) { ... when != I(x,...) S when != e = e1 when != e += e1 when != e -= e1 when != ++e when != --e when != e++ when != e-- when != &e kfree@p2(x); ... return ...; } @ok depends on unchanged exists@ position any r.p1; position r.p2; expression x; @@ ... when != true x@p1 == NULL kfree@p2(x); @depends on !ok && unchanged@ position r.p2; expression x; @@ *kfree@p2(x); // Signed-off-by: Peter Senna Tschudin Signed-off-by: Greg Kroah-Hartman --- drivers/char/mmtimer.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c index 33dc2298af73..3d6c0671e996 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c @@ -826,7 +826,7 @@ static int __init mmtimer_init(void) /* Allocate list of node ptrs to mmtimer_t's */ timers = kzalloc(sizeof(struct mmtimer_node)*maxn, GFP_KERNEL); - if (timers == NULL) { + if (!timers) { printk(KERN_ERR "%s: failed to allocate memory for device\n", MMTIMER_NAME); goto out3; @@ -848,7 +848,6 @@ static int __init mmtimer_init(void) return 0; out3: - kfree(timers); misc_deregister(&mmtimer_miscdev); out2: free_irq(SGI_MMTIMER_VECTOR, NULL); -- cgit v1.2.3 From 221b7b5796b520b51c34c40239d77921ba39225b Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 15 Sep 2012 01:29:37 +0400 Subject: pcmcia: synclink_cs: fix potential tty NULL dereference tty_port_tty_get() can return NULL after port hangup that may happen anytime. The patch adds checks that tty_port_tty_get() returns nonNULL around places where tty is actually used. I have no actual hardware to test the patch, so I have updated rx side processing from common sense only. v2: rx handling updated according Alan Cox feedback. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Greg Kroah-Hartman --- drivers/char/pcmcia/synclink_cs.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 0a484b4a1b02..a6b8ddea2227 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -891,6 +891,14 @@ static void rx_ready_async(MGSLPC_INFO *info, int tcd, struct tty_struct *tty) int work = 0; struct mgsl_icount *icount = &info->icount; + if (!tty) { + /* tty is not available anymore */ + issue_command(info, CHA, CMD_RXRESET); + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):rx_ready_async(tty=NULL)\n",__FILE__,__LINE__); + return; + } + if (tcd) { /* early termination, get FIFO count from RBCL register */ fifo_count = (unsigned char)(read_reg(info, CHA+RBCL) & 0x1f); @@ -980,7 +988,7 @@ static void tx_done(MGSLPC_INFO *info, struct tty_struct *tty) else #endif { - if (tty->stopped || tty->hw_stopped) { + if (tty && (tty->stopped || tty->hw_stopped)) { tx_stop(info); return; } @@ -1000,7 +1008,7 @@ static void tx_ready(MGSLPC_INFO *info, struct tty_struct *tty) if (!info->tx_active) return; } else { - if (tty->stopped || tty->hw_stopped) { + if (tty && (tty->stopped || tty->hw_stopped)) { tx_stop(info); return; } @@ -1050,13 +1058,12 @@ static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty) wake_up_interruptible(&info->status_event_wait_q); wake_up_interruptible(&info->event_wait_q); - if (info->port.flags & ASYNC_CTS_FLOW) { + if (tty && (info->port.flags & ASYNC_CTS_FLOW)) { if (tty->hw_stopped) { if (info->serial_signals & SerialSignal_CTS) { if (debug_level >= DEBUG_LEVEL_ISR) printk("CTS tx start..."); - if (tty) - tty->hw_stopped = 0; + tty->hw_stopped = 0; tx_start(info, tty); info->pending_bh |= BH_TRANSMIT; return; @@ -1065,8 +1072,7 @@ static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty) if (!(info->serial_signals & SerialSignal_CTS)) { if (debug_level >= DEBUG_LEVEL_ISR) printk("CTS tx stop..."); - if (tty) - tty->hw_stopped = 1; + tty->hw_stopped = 1; tx_stop(info); } } -- cgit v1.2.3 From eb5e89fc70bb3f115b3206ed0c57d3aba1fdd155 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 9 Aug 2012 21:30:39 +0900 Subject: virtio/console: Add splice_write support Enable to use splice_write from pipe to virtio-console port. This steals pages from pipe and directly send it to host. Note that this may accelerate only the guest to host path. Changes in v2: - Use GFP_KERNEL instead of GFP_ATOMIC in syscall context function. Signed-off-by: Masami Hiramatsu Acked-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 136 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 128 insertions(+), 8 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index cdf2f5451c76..730816cdeb45 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -227,6 +229,7 @@ struct port { bool guest_connected; }; +#define MAX_SPLICE_PAGES 32 /* This is the very early arch-specified put chars function. */ static int (*early_put_chars)(u32, const char *, int); @@ -474,26 +477,52 @@ static ssize_t send_control_msg(struct port *port, unsigned int event, return 0; } +struct buffer_token { + union { + void *buf; + struct scatterlist *sg; + } u; + bool sgpages; +}; + +static void reclaim_sg_pages(struct scatterlist *sg) +{ + int i; + struct page *page; + + for (i = 0; i < MAX_SPLICE_PAGES; i++) { + page = sg_page(&sg[i]); + if (!page) + break; + put_page(page); + } + kfree(sg); +} + /* Callers must take the port->outvq_lock */ static void reclaim_consumed_buffers(struct port *port) { - void *buf; + struct buffer_token *tok; unsigned int len; if (!port->portdev) { /* Device has been unplugged. vqs are already gone. */ return; } - while ((buf = virtqueue_get_buf(port->out_vq, &len))) { - kfree(buf); + while ((tok = virtqueue_get_buf(port->out_vq, &len))) { + if (tok->sgpages) + reclaim_sg_pages(tok->u.sg); + else + kfree(tok->u.buf); + kfree(tok); port->outvq_full = false; } } -static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, - bool nonblock) +static ssize_t __send_to_port(struct port *port, struct scatterlist *sg, + int nents, size_t in_count, + struct buffer_token *tok, bool nonblock) { - struct scatterlist sg[1]; struct virtqueue *out_vq; ssize_t ret; unsigned long flags; @@ -505,8 +534,7 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, reclaim_consumed_buffers(port); - sg_init_one(sg, in_buf, in_count); - ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf, GFP_ATOMIC); + ret = virtqueue_add_buf(out_vq, sg, nents, 0, tok, GFP_ATOMIC); /* Tell Host to go! */ virtqueue_kick(out_vq); @@ -544,6 +572,37 @@ done: return in_count; } +static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, + bool nonblock) +{ + struct scatterlist sg[1]; + struct buffer_token *tok; + + tok = kmalloc(sizeof(*tok), GFP_ATOMIC); + if (!tok) + return -ENOMEM; + tok->sgpages = false; + tok->u.buf = in_buf; + + sg_init_one(sg, in_buf, in_count); + + return __send_to_port(port, sg, 1, in_count, tok, nonblock); +} + +static ssize_t send_pages(struct port *port, struct scatterlist *sg, int nents, + size_t in_count, bool nonblock) +{ + struct buffer_token *tok; + + tok = kmalloc(sizeof(*tok), GFP_ATOMIC); + if (!tok) + return -ENOMEM; + tok->sgpages = true; + tok->u.sg = sg; + + return __send_to_port(port, sg, nents, in_count, tok, nonblock); +} + /* * Give out the data that's requested from the buffer that we have * queued up. @@ -725,6 +784,66 @@ out: return ret; } +struct sg_list { + unsigned int n; + size_t len; + struct scatterlist *sg; +}; + +static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf, + struct splice_desc *sd) +{ + struct sg_list *sgl = sd->u.data; + unsigned int len = 0; + + if (sgl->n == MAX_SPLICE_PAGES) + return 0; + + /* Try lock this page */ + if (buf->ops->steal(pipe, buf) == 0) { + /* Get reference and unlock page for moving */ + get_page(buf->page); + unlock_page(buf->page); + + len = min(buf->len, sd->len); + sg_set_page(&(sgl->sg[sgl->n]), buf->page, len, buf->offset); + sgl->n++; + sgl->len += len; + } + + return len; +} + +/* Faster zero-copy write by splicing */ +static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, + struct file *filp, loff_t *ppos, + size_t len, unsigned int flags) +{ + struct port *port = filp->private_data; + struct sg_list sgl; + ssize_t ret; + struct splice_desc sd = { + .total_len = len, + .flags = flags, + .pos = *ppos, + .u.data = &sgl, + }; + + sgl.n = 0; + sgl.len = 0; + sgl.sg = kmalloc(sizeof(struct scatterlist) * MAX_SPLICE_PAGES, + GFP_KERNEL); + if (unlikely(!sgl.sg)) + return -ENOMEM; + + sg_init_table(sgl.sg, MAX_SPLICE_PAGES); + ret = __splice_from_pipe(pipe, &sd, pipe_to_sg); + if (likely(ret > 0)) + ret = send_pages(port, sgl.sg, sgl.n, sgl.len, true); + + return ret; +} + static unsigned int port_fops_poll(struct file *filp, poll_table *wait) { struct port *port; @@ -856,6 +975,7 @@ static const struct file_operations port_fops = { .open = port_fops_open, .read = port_fops_read, .write = port_fops_write, + .splice_write = port_fops_splice_write, .poll = port_fops_poll, .release = port_fops_release, .fasync = port_fops_fasync, -- cgit v1.2.3 From ec8fc870156b2b144f55b6a5a7d135018f04b30e Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 9 Aug 2012 21:30:50 +0900 Subject: virtio/console: Add a failback for unstealable pipe buffer Add a failback memcpy path for unstealable pipe buffer. If buf->ops->steal() fails, virtio-serial tries to copy the page contents to an allocated page, instead of just failing splice(). Signed-off-by: Masami Hiramatsu Acked-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 730816cdeb45..22b737353017 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -794,7 +794,7 @@ static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf, struct splice_desc *sd) { struct sg_list *sgl = sd->u.data; - unsigned int len = 0; + unsigned int offset, len; if (sgl->n == MAX_SPLICE_PAGES) return 0; @@ -807,9 +807,31 @@ static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf, len = min(buf->len, sd->len); sg_set_page(&(sgl->sg[sgl->n]), buf->page, len, buf->offset); - sgl->n++; - sgl->len += len; + } else { + /* Failback to copying a page */ + struct page *page = alloc_page(GFP_KERNEL); + char *src = buf->ops->map(pipe, buf, 1); + char *dst; + + if (!page) + return -ENOMEM; + dst = kmap(page); + + offset = sd->pos & ~PAGE_MASK; + + len = sd->len; + if (len + offset > PAGE_SIZE) + len = PAGE_SIZE - offset; + + memcpy(dst + offset, src + buf->offset, len); + + kunmap(page); + buf->ops->unmap(pipe, buf, src); + + sg_set_page(&(sgl->sg[sgl->n]), page, len, offset); } + sgl->n++; + sgl->len += len; return len; } -- cgit v1.2.3 From efe75d24a69fc39bb09d882ca2d5b90d4da02afe Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 9 Aug 2012 21:31:00 +0900 Subject: virtio/console: Wait until the port is ready on splice Wait if the port is not connected or full on splice like as write is doing. Signed-off-by: Masami Hiramatsu Acked-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 22b737353017..b2fc2abedc79 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -724,6 +724,26 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf, return fill_readbuf(port, ubuf, count, true); } +static int wait_port_writable(struct port *port, bool nonblock) +{ + int ret; + + if (will_write_block(port)) { + if (nonblock) + return -EAGAIN; + + ret = wait_event_freezable(port->waitqueue, + !will_write_block(port)); + if (ret < 0) + return ret; + } + /* Port got hot-unplugged. */ + if (!port->guest_connected) + return -ENODEV; + + return 0; +} + static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, size_t count, loff_t *offp) { @@ -740,18 +760,9 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, nonblock = filp->f_flags & O_NONBLOCK; - if (will_write_block(port)) { - if (nonblock) - return -EAGAIN; - - ret = wait_event_freezable(port->waitqueue, - !will_write_block(port)); - if (ret < 0) - return ret; - } - /* Port got hot-unplugged. */ - if (!port->guest_connected) - return -ENODEV; + ret = wait_port_writable(port, nonblock); + if (ret < 0) + return ret; count = min((size_t)(32 * 1024), count); @@ -851,6 +862,10 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, .u.data = &sgl, }; + ret = wait_port_writable(port, filp->f_flags & O_NONBLOCK); + if (ret < 0) + return ret; + sgl.n = 0; sgl.len = 0; sgl.sg = kmalloc(sizeof(struct scatterlist) * MAX_SPLICE_PAGES, -- cgit v1.2.3 From 8ca84a50e5b39487ea1de8809d0ee1c8474f6a5c Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 9 Aug 2012 21:31:20 +0900 Subject: virtio/console: Allocate scatterlist according to the current pipe size Allocate scatterlist according to the current pipe size. This allows splicing bigger buffer if the pipe size has been changed by fcntl. Changes in v2: - Just a minor fix for avoiding a confliction with previous patch. Signed-off-by: Masami Hiramatsu Acked-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index b2fc2abedc79..e88f84390428 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -229,7 +229,6 @@ struct port { bool guest_connected; }; -#define MAX_SPLICE_PAGES 32 /* This is the very early arch-specified put chars function. */ static int (*early_put_chars)(u32, const char *, int); @@ -482,15 +481,16 @@ struct buffer_token { void *buf; struct scatterlist *sg; } u; - bool sgpages; + /* If sgpages == 0 then buf is used, else sg is used */ + unsigned int sgpages; }; -static void reclaim_sg_pages(struct scatterlist *sg) +static void reclaim_sg_pages(struct scatterlist *sg, unsigned int nrpages) { int i; struct page *page; - for (i = 0; i < MAX_SPLICE_PAGES; i++) { + for (i = 0; i < nrpages; i++) { page = sg_page(&sg[i]); if (!page) break; @@ -511,7 +511,7 @@ static void reclaim_consumed_buffers(struct port *port) } while ((tok = virtqueue_get_buf(port->out_vq, &len))) { if (tok->sgpages) - reclaim_sg_pages(tok->u.sg); + reclaim_sg_pages(tok->u.sg, tok->sgpages); else kfree(tok->u.buf); kfree(tok); @@ -581,7 +581,7 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, tok = kmalloc(sizeof(*tok), GFP_ATOMIC); if (!tok) return -ENOMEM; - tok->sgpages = false; + tok->sgpages = 0; tok->u.buf = in_buf; sg_init_one(sg, in_buf, in_count); @@ -597,7 +597,7 @@ static ssize_t send_pages(struct port *port, struct scatterlist *sg, int nents, tok = kmalloc(sizeof(*tok), GFP_ATOMIC); if (!tok) return -ENOMEM; - tok->sgpages = true; + tok->sgpages = nents; tok->u.sg = sg; return __send_to_port(port, sg, nents, in_count, tok, nonblock); @@ -797,6 +797,7 @@ out: struct sg_list { unsigned int n; + unsigned int size; size_t len; struct scatterlist *sg; }; @@ -807,7 +808,7 @@ static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf, struct sg_list *sgl = sd->u.data; unsigned int offset, len; - if (sgl->n == MAX_SPLICE_PAGES) + if (sgl->n == sgl->size) return 0; /* Try lock this page */ @@ -868,12 +869,12 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, sgl.n = 0; sgl.len = 0; - sgl.sg = kmalloc(sizeof(struct scatterlist) * MAX_SPLICE_PAGES, - GFP_KERNEL); + sgl.size = pipe->nrbufs; + sgl.sg = kmalloc(sizeof(struct scatterlist) * sgl.size, GFP_KERNEL); if (unlikely(!sgl.sg)) return -ENOMEM; - sg_init_table(sgl.sg, MAX_SPLICE_PAGES); + sg_init_table(sgl.sg, sgl.size); ret = __splice_from_pipe(pipe, &sd, pipe_to_sg); if (likely(ret > 0)) ret = send_pages(port, sgl.sg, sgl.n, sgl.len, true); -- cgit v1.2.3 From 33e1afc3d82697599ccc8dc8f2fa44ffff5ae329 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 1 Sep 2012 23:49:37 +0400 Subject: virtio: console: fix error handling in init() function If register_virtio_driver() fails, virtio-ports class is not destroyed. The patch adds error handling of register_virtio_driver(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Acked-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index e88f84390428..8ab9c3d4bf13 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -2099,7 +2099,17 @@ static int __init init(void) INIT_LIST_HEAD(&pdrvdata.consoles); INIT_LIST_HEAD(&pdrvdata.portdevs); - return register_virtio_driver(&virtio_console); + err = register_virtio_driver(&virtio_console); + if (err < 0) { + pr_err("Error %d registering virtio driver\n", err); + goto free; + } + return 0; +free: + if (pdrvdata.debugfs_dir) + debugfs_remove_recursive(pdrvdata.debugfs_dir); + class_destroy(pdrvdata.class); + return err; } static void __exit fini(void) -- cgit v1.2.3 From 1849f23d6ff57cf9d57741488adb14e0d7942406 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 4 Oct 2012 17:11:47 -0700 Subject: sections: fix section conflicts in drivers/char Signed-off-by: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/mbcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/mbcs.c b/drivers/char/mbcs.c index 47ff7e470d87..0c7d340b9ab9 100644 --- a/drivers/char/mbcs.c +++ b/drivers/char/mbcs.c @@ -799,7 +799,7 @@ static int mbcs_remove(struct cx_dev *dev) return 0; } -static const struct cx_device_id __devinitdata mbcs_id_table[] = { +static const struct cx_device_id __devinitconst mbcs_id_table[] = { { .part_num = MBCS_PART_NUM, .mfg_num = MBCS_MFG_NUM, -- cgit v1.2.3 From 45ef6ac6f5d4d4ea441a042fee3790b3f33cba73 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sun, 5 Aug 2012 14:59:12 +0000 Subject: ARM: footbridge: nw_gpio_lock is raw_spin_lock bd31b85960a "locking, ARM: Annotate low level hw locks as raw" made nw_gpio_lock a raw spinlock, but did not change all the users in device drivers. This fixes the remaining ones. sound/oss/waveartist.c: In function 'vnc_mute_spkr': sound/oss/waveartist.c:1485:2: warning: passing argument 1 of 'spinlock_check' from incompatible pointer type [enabled by default] include/linux/spinlock.h:272:102: note: expected 'struct spinlock_t *' but argument is of type 'struct raw_spinlock_t *' drivers/char/ds1620.c: In function 'netwinder_lock': drivers/char/ds1620.c:77:2: warning: passing argument 1 of 'spinlock_check' from incompatible pointer type [enabled by default] include/linux/spinlock.h:272:102: note: expected 'struct spinlock_t *' but argument is of type 'struct raw_spinlock_t *' drivers/char/nwflash.c: In function 'kick_open': drivers/char/nwflash.c:620:2: warning: passing argument 1 of 'spinlock_check' from incompatible pointer type [enabled by default] include/linux/spinlock.h:272:102: note: expected 'struct spinlock_t *' but argument is of type 'struct raw_spinlock_t *' Signed-off-by: Arnd Bergmann Cc: Thomas Gleixner Cc: Russell King --- drivers/char/ds1620.c | 8 ++++---- drivers/char/nwflash.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/ds1620.c b/drivers/char/ds1620.c index aab9605f0b43..24ffd8cec51e 100644 --- a/drivers/char/ds1620.c +++ b/drivers/char/ds1620.c @@ -74,21 +74,21 @@ static inline void netwinder_ds1620_reset(void) static inline void netwinder_lock(unsigned long *flags) { - spin_lock_irqsave(&nw_gpio_lock, *flags); + raw_spin_lock_irqsave(&nw_gpio_lock, *flags); } static inline void netwinder_unlock(unsigned long *flags) { - spin_unlock_irqrestore(&nw_gpio_lock, *flags); + raw_spin_unlock_irqrestore(&nw_gpio_lock, *flags); } static inline void netwinder_set_fan(int i) { unsigned long flags; - spin_lock_irqsave(&nw_gpio_lock, flags); + raw_spin_lock_irqsave(&nw_gpio_lock, flags); nw_gpio_modify_op(GPIO_FAN, i ? GPIO_FAN : 0); - spin_unlock_irqrestore(&nw_gpio_lock, flags); + raw_spin_unlock_irqrestore(&nw_gpio_lock, flags); } static inline int netwinder_get_fan(void) diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c index d45c3345b4af..04e2a9491fca 100644 --- a/drivers/char/nwflash.c +++ b/drivers/char/nwflash.c @@ -617,9 +617,9 @@ static void kick_open(void) * we want to write a bit pattern XXX1 to Xilinx to enable * the write gate, which will be open for about the next 2ms. */ - spin_lock_irqsave(&nw_gpio_lock, flags); + raw_spin_lock_irqsave(&nw_gpio_lock, flags); nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE); - spin_unlock_irqrestore(&nw_gpio_lock, flags); + raw_spin_unlock_irqrestore(&nw_gpio_lock, flags); /* * let the ISA bus to catch on... -- cgit v1.2.3 From 314e51b9851b4f4e8ab302243ff5a6fc6147f379 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Mon, 8 Oct 2012 16:29:02 -0700 Subject: mm: kill vma flag VM_RESERVED and mm->reserved_vm counter A long time ago, in v2.4, VM_RESERVED kept swapout process off VMA, currently it lost original meaning but still has some effects: | effect | alternative flags -+------------------------+--------------------------------------------- 1| account as reserved_vm | VM_IO 2| skip in core dump | VM_IO, VM_DONTDUMP 3| do not merge or expand | VM_IO, VM_DONTEXPAND, VM_HUGETLB, VM_PFNMAP 4| do not mlock | VM_IO, VM_DONTEXPAND, VM_HUGETLB, VM_PFNMAP This patch removes reserved_vm counter from mm_struct. Seems like nobody cares about it, it does not exported into userspace directly, it only reduces total_vm showed in proc. Thus VM_RESERVED can be replaced with VM_IO or pair VM_DONTEXPAND | VM_DONTDUMP. remap_pfn_range() and io_remap_pfn_range() set VM_IO|VM_DONTEXPAND|VM_DONTDUMP. remap_vmalloc_range() set VM_DONTEXPAND | VM_DONTDUMP. [akpm@linux-foundation.org: drivers/vfio/pci/vfio_pci.c fixup] Signed-off-by: Konstantin Khlebnikov Cc: Alexander Viro Cc: Carsten Otte Cc: Chris Metcalf Cc: Cyrill Gorcunov Cc: Eric Paris Cc: H. Peter Anvin Cc: Hugh Dickins Cc: Ingo Molnar Cc: James Morris Cc: Jason Baron Cc: Kentaro Takeda Cc: Matt Helsley Cc: Nick Piggin Cc: Oleg Nesterov Cc: Peter Zijlstra Cc: Robert Richter Cc: Suresh Siddha Cc: Tetsuo Handa Cc: Venkatesh Pallipadi Acked-by: Linus Torvalds Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/mbcs.c | 2 +- drivers/char/mem.c | 2 +- drivers/char/mspec.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/mbcs.c b/drivers/char/mbcs.c index 0c7d340b9ab9..f74e892711dd 100644 --- a/drivers/char/mbcs.c +++ b/drivers/char/mbcs.c @@ -507,7 +507,7 @@ static int mbcs_gscr_mmap(struct file *fp, struct vm_area_struct *vma) vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */ + /* Remap-pfn-range will mark the range VM_IO */ if (remap_pfn_range(vma, vma->vm_start, __pa(soft->gscr_addr) >> PAGE_SHIFT, diff --git a/drivers/char/mem.c b/drivers/char/mem.c index e5eedfa24c91..0537903c985b 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -322,7 +322,7 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma) vma->vm_ops = &mmap_mem_ops; - /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */ + /* Remap-pfn-range will mark the range VM_IO */ if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index 845f97fd1832..e1f60f968fdd 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c @@ -286,7 +286,7 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma, atomic_set(&vdata->refcnt, 1); vma->vm_private_data = vdata; - vma->vm_flags |= (VM_IO | VM_RESERVED | VM_PFNMAP | VM_DONTEXPAND); + vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP; if (vdata->type == MSPEC_FETCHOP || vdata->type == MSPEC_UNCACHED) vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); vma->vm_ops = &mspec_vm_ops; -- cgit v1.2.3 From 1631cfb7cee28388b04aef6c0a73050f6fd76e4d Mon Sep 17 00:00:00 2001 From: Gang Wei Date: Tue, 9 Oct 2012 17:35:22 +0800 Subject: driver/char/tpm: fix regression causesd by ppi This patch try to fix the S3 regression https://lkml.org/lkml/2012/10/5/433, which includes below line: [ 1554.684638] sysfs: cannot create duplicate filename '/devices/pnp0/00:0c/ppi' The root cause is that ppi sysfs teardown code is MIA, so while S3 resume, the ppi kobject will be created again upon existing one. To make the tear down code simple, change the ppi subfolder creation from using kobject_create_and_add to just using a named ppi attribute_group. Then ppi sysfs teardown could be done with a simple sysfs_remove_group call. Adjusted the name & return type for ppi sysfs init function. Reported-by: Ben Guthro Signed-off-by: Gang Wei Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.c | 3 ++- drivers/char/tpm/tpm.h | 9 +++++++-- drivers/char/tpm/tpm_ppi.c | 18 ++++++++++-------- 3 files changed, 19 insertions(+), 11 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index f26afdb1a702..b429f1ea1b97 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1259,6 +1259,7 @@ void tpm_remove_hardware(struct device *dev) misc_deregister(&chip->vendor.miscdev); sysfs_remove_group(&dev->kobj, chip->vendor.attr_group); + tpm_remove_ppi(&dev->kobj); tpm_bios_log_teardown(chip->bios_dir); /* write it this way to be explicit (chip->dev == dev) */ @@ -1476,7 +1477,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, goto put_device; } - if (sys_add_ppi(&dev->kobj)) { + if (tpm_add_ppi(&dev->kobj)) { misc_deregister(&chip->vendor.miscdev); goto put_device; } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 02c266aa2bf7..8ef7649a50aa 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -329,10 +329,15 @@ extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long, wait_queue_head_t *); #ifdef CONFIG_ACPI -extern ssize_t sys_add_ppi(struct kobject *parent); +extern int tpm_add_ppi(struct kobject *); +extern void tpm_remove_ppi(struct kobject *); #else -static inline ssize_t sys_add_ppi(struct kobject *parent) +static inline int tpm_add_ppi(struct kobject *parent) { return 0; } + +static inline void tpm_remove_ppi(struct kobject *parent) +{ +} #endif diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index f27b58cfae98..720ebcf29fdf 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -444,18 +444,20 @@ static struct attribute *ppi_attrs[] = { &dev_attr_vs_operations.attr, NULL, }; static struct attribute_group ppi_attr_grp = { + .name = "ppi", .attrs = ppi_attrs }; -ssize_t sys_add_ppi(struct kobject *parent) +int tpm_add_ppi(struct kobject *parent) { - struct kobject *ppi; - ppi = kobject_create_and_add("ppi", parent); - if (sysfs_create_group(ppi, &ppi_attr_grp)) - return -EFAULT; - else - return 0; + return sysfs_create_group(parent, &ppi_attr_grp); +} +EXPORT_SYMBOL_GPL(tpm_add_ppi); + +void tpm_remove_ppi(struct kobject *parent) +{ + sysfs_remove_group(parent, &ppi_attr_grp); } -EXPORT_SYMBOL_GPL(sys_add_ppi); +EXPORT_SYMBOL_GPL(tpm_remove_ppi); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From abce9ac292e13da367bbd22c1f7669f988d931ac Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Thu, 27 Sep 2012 16:09:33 +0200 Subject: tpm: Propagate error from tpm_transmit to fix a timeout hang tpm_write calls tpm_transmit without checking the return value and assigns the return value unconditionally to chip->pending_data, even if it's an error value. This causes three bugs. So if we write to /dev/tpm0 with a tpm_param_size bigger than TPM_BUFSIZE=0x1000 (e.g. 0x100a) and a bufsize also bigger than TPM_BUFSIZE (e.g. 0x100a) tpm_transmit returns -E2BIG which is assigned to chip->pending_data as -7, but tpm_write returns that TPM_BUFSIZE bytes have been successfully been written to the TPM, altough this is not true (bug #1). As we did write more than than TPM_BUFSIZE bytes but tpm_write reports that only TPM_BUFSIZE bytes have been written the vfs tries to write the remaining bytes (in this case 10 bytes) to the tpm device driver via tpm_write which then blocks at /* cannot perform a write until the read has cleared either via tpm_read or a user_read_timer timeout */ while (atomic_read(&chip->data_pending) != 0) msleep(TPM_TIMEOUT); for 60 seconds, since data_pending is -7 and nobody is able to read it (since tpm_read luckily checks if data_pending is greater than 0) (#bug 2). After that the remaining bytes are written to the TPM which are interpreted by the tpm as a normal command. (bug #3) So if the last bytes of the command stream happen to be a e.g. tpm_force_clear this gets accidentally sent to the TPM. This patch fixes all three bugs, by propagating the error code of tpm_write and returning -E2BIG if the input buffer is too big, since the response from the tpm for a truncated value is bogus anyway. Moreover it returns -EBUSY to userspace if there is a response ready to be read. Signed-off-by: Peter Huewe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 6724615a4fdd..4caef331a705 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1182,17 +1182,20 @@ ssize_t tpm_write(struct file *file, const char __user *buf, size_t size, loff_t *off) { struct tpm_chip *chip = file->private_data; - size_t in_size = size, out_size; + size_t in_size = size; + ssize_t out_size; /* cannot perform a write until the read has cleared - either via tpm_read or a user_read_timer timeout */ - while (atomic_read(&chip->data_pending) != 0) - msleep(TPM_TIMEOUT); - - mutex_lock(&chip->buffer_mutex); + either via tpm_read or a user_read_timer timeout. + This also prevents splitted buffered writes from blocking here. + */ + if (atomic_read(&chip->data_pending) != 0) + return -EBUSY; if (in_size > TPM_BUFSIZE) - in_size = TPM_BUFSIZE; + return -E2BIG; + + mutex_lock(&chip->buffer_mutex); if (copy_from_user (chip->data_buffer, (void __user *) buf, in_size)) { @@ -1202,6 +1205,10 @@ ssize_t tpm_write(struct file *file, const char __user *buf, /* atomic tpm command send and result receive */ out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE); + if (out_size < 0) { + mutex_unlock(&chip->buffer_mutex); + return out_size; + } atomic_set(&chip->data_pending, out_size); mutex_unlock(&chip->buffer_mutex); -- cgit v1.2.3 From 9169d3a88072b20f42e68a946e916bd7dfbc7f2c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 10 Oct 2012 23:14:01 +0200 Subject: drm/i915: disable wc gtt pte mappings on gen2 It doesn't work since the gtt pte range sits in the middle of the mmio bar. We didn't notice that since both my and Chris' gen2 machines don't support PAT and hence all wc io mapping request will automatically be demoted to uc. This regression has been introduce in commit edef7e685da05c13cce50c0126189c80fe2c8f71 Author: Chris Wilson Date: Fri Sep 14 11:57:47 2012 +0100 agp/intel: Use a write-combining map for updating PTEs Reported-by: Egbert Eich Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=55834 Acked-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index e01f5eaaec82..38390f7c6ab6 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -667,7 +667,7 @@ static int intel_gtt_init(void) gtt_map_size = intel_private.base.gtt_total_entries * 4; intel_private.gtt = NULL; - if (INTEL_GTT_GEN < 6) + if (INTEL_GTT_GEN < 6 && INTEL_GTT_GEN > 2) intel_private.gtt = ioremap_wc(intel_private.gtt_bus_addr, gtt_map_size); if (intel_private.gtt == NULL) -- cgit v1.2.3 From 061475b65c126454aaaaddcbd40e7118424fc860 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 16 Oct 2012 15:53:38 -0500 Subject: IPMI: Change link order IPMI must be initialised before ACPI in order to ensure that any IPMI services are available before ACPI driver initialisation attempts to use any IPMI operation regions. Signed-off-by: Matthew Garrett Signed-off-by: Corey Minyard Signed-off-by: Linus Torvalds --- drivers/char/Makefile | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/Makefile b/drivers/char/Makefile index d0b27a39f1d4..7ff1d0d208a7 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -52,7 +52,6 @@ obj-$(CONFIG_TELCLOCK) += tlclk.o obj-$(CONFIG_MWAVE) += mwave/ obj-$(CONFIG_AGP) += agp/ obj-$(CONFIG_PCMCIA) += pcmcia/ -obj-$(CONFIG_IPMI_HANDLER) += ipmi/ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o obj-$(CONFIG_TCG_TPM) += tpm/ -- cgit v1.2.3 From 9ebca93bf3b350910c66516cbec68899eeddfd8e Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Tue, 16 Oct 2012 15:53:39 -0500 Subject: IPMI: Fix some uninitialized warning There was a spot where the compiler couldn't tell some variables would be set. So initialize them to make the warning go away. Signed-off-by: Corey Minyard Signed-off-by: Linus Torvalds --- drivers/char/ipmi/ipmi_msghandler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 2c29942b1326..a0c84bb30856 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -1880,7 +1880,7 @@ int ipmi_request_supply_msgs(ipmi_user_t user, struct ipmi_recv_msg *supplied_recv, int priority) { - unsigned char saddr, lun; + unsigned char saddr = 0, lun = 0; int rv; if (!user) -- cgit v1.2.3 From a6c16c2803089f032f86fe15e952176c0713d6e3 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Tue, 16 Oct 2012 15:53:40 -0500 Subject: IPMI: Detect register spacing on PCI interfaces The IPMI spec defines a way to detect register spacing for PCI interfaces, so implement it. Signed-off-by: Steven Hsieh Signed-off-by: Corey Minyard Signed-off-by: Linus Torvalds --- drivers/char/ipmi/ipmi_si_intf.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 83f85cf7fb1b..32a6c7e256bd 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2424,6 +2424,38 @@ static void ipmi_pci_cleanup(struct smi_info *info) pci_disable_device(pdev); } +static int __devinit ipmi_pci_probe_regspacing(struct smi_info *info) +{ + if (info->si_type == SI_KCS) { + unsigned char status; + int regspacing; + + info->io.regsize = DEFAULT_REGSIZE; + info->io.regshift = 0; + info->io_size = 2; + info->handlers = &kcs_smi_handlers; + + /* detect 1, 4, 16byte spacing */ + for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) { + info->io.regspacing = regspacing; + if (info->io_setup(info)) { + dev_err(info->dev, + "Could not setup I/O space\n"); + return DEFAULT_REGSPACING; + } + /* write invalid cmd */ + info->io.outputb(&info->io, 1, 0x10); + /* read status back */ + status = info->io.inputb(&info->io, 1); + info->io_cleanup(info); + if (status) + return regspacing; + regspacing *= 4; + } + } + return DEFAULT_REGSPACING; +} + static int __devinit ipmi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -2476,8 +2508,8 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, } info->io.addr_data = pci_resource_start(pdev, 0); - info->io.regspacing = DEFAULT_REGSPACING; - info->io.regsize = DEFAULT_REGSPACING; + info->io.regspacing = ipmi_pci_probe_regspacing(info); + info->io.regsize = DEFAULT_REGSIZE; info->io.regshift = 0; info->irq = pdev->irq; -- cgit v1.2.3 From 2cb55a2f47a3c695a0105b7fc04a3b70c3bc4e4f Mon Sep 17 00:00:00 2001 From: Yuanhan Liu Date: Tue, 16 Oct 2012 22:59:04 +0800 Subject: sonypi: suspend/resume callbacks should be conditionally compiled on CONFIG_PM_SLEEP This will fix warnings like following when CONFIG_PM_SLEEP is not set: warning: 'xxx_suspend' defined but not used [-Wunused-function] warning: 'xxx_resume' defined but not used [-Wunused-function] Because SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) Only references the callbacks on CONFIG_PM_SLEEP (instead of CONFIG_PM). Cc: Mattia Dongili Cc: Arnd Bergmann Cc: platform-driver-x86@vger.kernel.org Signed-off-by: Yuanhan Liu Signed-off-by: Fengguang Wu Signed-off-by: Greg Kroah-Hartman --- drivers/char/sonypi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 320debbe32fa..9b4f0116ff21 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1456,7 +1456,7 @@ static int __devexit sonypi_remove(struct platform_device *dev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int old_camera_power; static int sonypi_suspend(struct device *dev) -- cgit v1.2.3