diff options
author | James Morris | 2017-07-25 10:44:18 +1000 |
---|---|---|
committer | James Morris | 2017-07-25 10:44:18 +1000 |
commit | 53a2ebaaabc1eb8458796fec3bc1e0e80746b642 (patch) | |
tree | 9d1f9227b49392cdd2edcc01057517da4f4b09c2 /drivers/char | |
parent | 3cf29931453215536916d0c4da953fce1911ced3 (diff) | |
parent | 520eccdfe187591a51ea9ab4c1a024ae4d0f68d9 (diff) |
sync to Linus v4.13-rc2 for subsystem developers to work against
Diffstat (limited to 'drivers/char')
28 files changed, 1129 insertions, 1437 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 31adbebf812e..ccd239ab879f 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -17,6 +17,8 @@ config DEVMEM config DEVKMEM bool "/dev/kmem virtual device support" + # On arm64, VMALLOC_START < PAGE_OFFSET, which confuses kmem read/write + depends on !ARM64 help Say Y here if you want to support the /dev/kmem device. The /dev/kmem device is rarely used, but can be used for certain @@ -539,15 +541,6 @@ config HANGCHECK_TIMER out to lunch past a certain margin. It can reboot the system or merely print a warning. -config MMTIMER - tristate "MMTIMER Memory mapped RTC for SGI Altix" - depends on IA64_GENERIC || IA64_SGI_SN2 - depends on POSIX_TIMERS - default y - help - The mmtimer device allows direct userspace access to the - Altix system timer. - config UV_MMTIMER tristate "UV_MMTIMER Memory mapped RTC for SGI UV" depends on X86_UV diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 6e6c244a66a0..53e33720818c 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o obj-$(CONFIG_RAW_DRIVER) += raw.o obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o obj-$(CONFIG_MSPEC) += mspec.o -obj-$(CONFIG_MMTIMER) += mmtimer.o obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o obj-$(CONFIG_IBM_BSR) += bsr.o obj-$(CONFIG_SGI_MBCS) += mbcs.o diff --git a/drivers/char/hw_random/mtk-rng.c b/drivers/char/hw_random/mtk-rng.c index df8eb54fd5a3..8da7bcf54105 100644 --- a/drivers/char/hw_random/mtk-rng.c +++ b/drivers/char/hw_random/mtk-rng.c @@ -25,6 +25,10 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +/* Runtime PM autosuspend timeout: */ +#define RNG_AUTOSUSPEND_TIMEOUT 100 #define USEC_POLL 2 #define TIMEOUT_POLL 20 @@ -90,6 +94,8 @@ static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) struct mtk_rng *priv = to_mtk_rng(rng); int retval = 0; + pm_runtime_get_sync((struct device *)priv->rng.priv); + while (max >= sizeof(u32)) { if (!mtk_rng_wait_ready(rng, wait)) break; @@ -100,6 +106,9 @@ static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) max -= sizeof(u32); } + pm_runtime_mark_last_busy((struct device *)priv->rng.priv); + pm_runtime_put_sync_autosuspend((struct device *)priv->rng.priv); + return retval || !wait ? retval : -EIO; } @@ -120,9 +129,12 @@ static int mtk_rng_probe(struct platform_device *pdev) return -ENOMEM; priv->rng.name = pdev->name; +#ifndef CONFIG_PM priv->rng.init = mtk_rng_init; priv->rng.cleanup = mtk_rng_cleanup; +#endif priv->rng.read = mtk_rng_read; + priv->rng.priv = (unsigned long)&pdev->dev; priv->clk = devm_clk_get(&pdev->dev, "rng"); if (IS_ERR(priv->clk)) { @@ -142,11 +154,40 @@ static int mtk_rng_probe(struct platform_device *pdev) return ret; } + dev_set_drvdata(&pdev->dev, priv); + pm_runtime_set_autosuspend_delay(&pdev->dev, RNG_AUTOSUSPEND_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + dev_info(&pdev->dev, "registered RNG driver\n"); return 0; } +#ifdef CONFIG_PM +static int mtk_rng_runtime_suspend(struct device *dev) +{ + struct mtk_rng *priv = dev_get_drvdata(dev); + + mtk_rng_cleanup(&priv->rng); + + return 0; +} + +static int mtk_rng_runtime_resume(struct device *dev) +{ + struct mtk_rng *priv = dev_get_drvdata(dev); + + return mtk_rng_init(&priv->rng); +} + +static UNIVERSAL_DEV_PM_OPS(mtk_rng_pm_ops, mtk_rng_runtime_suspend, + mtk_rng_runtime_resume, NULL); +#define MTK_RNG_PM_OPS (&mtk_rng_pm_ops) +#else /* CONFIG_PM */ +#define MTK_RNG_PM_OPS NULL +#endif /* CONFIG_PM */ + static const struct of_device_id mtk_rng_match[] = { { .compatible = "mediatek,mt7623-rng" }, {}, @@ -157,6 +198,7 @@ static struct platform_driver mtk_rng_driver = { .probe = mtk_rng_probe, .driver = { .name = MTK_RNG_DEV, + .pm = MTK_RNG_PM_OPS, .of_match_table = mtk_rng_match, }, }; diff --git a/drivers/char/hw_random/omap3-rom-rng.c b/drivers/char/hw_random/omap3-rom-rng.c index 37a58d78aab3..38b719017186 100644 --- a/drivers/char/hw_random/omap3-rom-rng.c +++ b/drivers/char/hw_random/omap3-rom-rng.c @@ -53,7 +53,10 @@ static int omap3_rom_rng_get_random(void *buf, unsigned int count) cancel_delayed_work_sync(&idle_work); if (rng_idle) { - clk_prepare_enable(rng_clk); + r = clk_prepare_enable(rng_clk); + if (r) + return r; + r = omap3_rom_rng_call(0, 0, RNG_GEN_PRNG_HW_INIT); if (r != 0) { clk_disable_unprepare(rng_clk); @@ -88,6 +91,8 @@ static struct hwrng omap3_rom_rng_ops = { static int omap3_rom_rng_probe(struct platform_device *pdev) { + int ret = 0; + pr_info("initializing\n"); omap3_rom_rng_call = pdev->dev.platform_data; @@ -104,7 +109,9 @@ static int omap3_rom_rng_probe(struct platform_device *pdev) } /* Leave the RNG in reset state. */ - clk_prepare_enable(rng_clk); + ret = clk_prepare_enable(rng_clk); + if (ret) + return ret; omap3_rom_rng_idle(0); return hwrng_register(&omap3_rom_rng_ops); diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c index a0faa5f05deb..03ff5483d865 100644 --- a/drivers/char/hw_random/timeriomem-rng.c +++ b/drivers/char/hw_random/timeriomem-rng.c @@ -151,8 +151,15 @@ static int timeriomem_rng_probe(struct platform_device *pdev) dev_err(&pdev->dev, "missing period\n"); return -EINVAL; } + + if (!of_property_read_u32(pdev->dev.of_node, + "quality", &i)) + priv->rng_ops.quality = i; + else + priv->rng_ops.quality = 0; } else { period = pdata->period; + priv->rng_ops.quality = pdata->quality; } priv->period = ns_to_ktime(period * NSEC_PER_USEC); diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 90f3edffb067..f6fa056a52fc 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -5,6 +5,7 @@ menuconfig IPMI_HANDLER tristate 'IPMI top-level message handler' depends on HAS_IOMEM + select IPMI_DMI_DECODE if DMI help This enables the central IPMI message handler, required for IPMI to work. @@ -16,6 +17,9 @@ menuconfig IPMI_HANDLER If unsure, say N. +config IPMI_DMI_DECODE + bool + if IPMI_HANDLER config IPMI_PANIC_EVENT diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index 0d98cd91def1..eefb0b301e83 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -7,6 +7,7 @@ ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o obj-$(CONFIG_IPMI_SI) += ipmi_si.o +obj-$(CONFIG_IPMI_DMI_DECODE) += ipmi_dmi.o obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index f45119c5337d..2ffca4232686 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -231,6 +231,102 @@ static int handle_send_req(ipmi_user_t user, return rv; } +static int handle_recv(struct ipmi_file_private *priv, + bool trunc, struct ipmi_recv *rsp, + int (*copyout)(struct ipmi_recv *, void __user *), + void __user *to) +{ + int addr_len; + struct list_head *entry; + struct ipmi_recv_msg *msg; + unsigned long flags; + int rv = 0; + + /* We claim a mutex because we don't want two + users getting something from the queue at a time. + Since we have to release the spinlock before we can + copy the data to the user, it's possible another + user will grab something from the queue, too. Then + the messages might get out of order if something + fails and the message gets put back onto the + queue. This mutex prevents that problem. */ + mutex_lock(&priv->recv_mutex); + + /* Grab the message off the list. */ + spin_lock_irqsave(&(priv->recv_msg_lock), flags); + if (list_empty(&(priv->recv_msgs))) { + spin_unlock_irqrestore(&(priv->recv_msg_lock), flags); + rv = -EAGAIN; + goto recv_err; + } + entry = priv->recv_msgs.next; + msg = list_entry(entry, struct ipmi_recv_msg, link); + list_del(entry); + spin_unlock_irqrestore(&(priv->recv_msg_lock), flags); + + addr_len = ipmi_addr_length(msg->addr.addr_type); + if (rsp->addr_len < addr_len) + { + rv = -EINVAL; + goto recv_putback_on_err; + } + + if (copy_to_user(rsp->addr, &(msg->addr), addr_len)) { + rv = -EFAULT; + goto recv_putback_on_err; + } + rsp->addr_len = addr_len; + + rsp->recv_type = msg->recv_type; + rsp->msgid = msg->msgid; + rsp->msg.netfn = msg->msg.netfn; + rsp->msg.cmd = msg->msg.cmd; + + if (msg->msg.data_len > 0) { + if (rsp->msg.data_len < msg->msg.data_len) { + rv = -EMSGSIZE; + if (trunc) + msg->msg.data_len = rsp->msg.data_len; + else + goto recv_putback_on_err; + } + + if (copy_to_user(rsp->msg.data, + msg->msg.data, + msg->msg.data_len)) + { + rv = -EFAULT; + goto recv_putback_on_err; + } + rsp->msg.data_len = msg->msg.data_len; + } else { + rsp->msg.data_len = 0; + } + + rv = copyout(rsp, to); + if (rv) + goto recv_putback_on_err; + + mutex_unlock(&priv->recv_mutex); + ipmi_free_recv_msg(msg); + return 0; + +recv_putback_on_err: + /* If we got an error, put the message back onto + the head of the queue. */ + spin_lock_irqsave(&(priv->recv_msg_lock), flags); + list_add(entry, &(priv->recv_msgs)); + spin_unlock_irqrestore(&(priv->recv_msg_lock), flags); +recv_err: + mutex_unlock(&priv->recv_mutex); + return rv; +} + +static int copyout_recv(struct ipmi_recv *rsp, void __user *to) +{ + return copy_to_user(to, rsp, sizeof(struct ipmi_recv)) ? -EFAULT : 0; +} + static int ipmi_ioctl(struct file *file, unsigned int cmd, unsigned long data) @@ -277,100 +373,12 @@ static int ipmi_ioctl(struct file *file, case IPMICTL_RECEIVE_MSG_TRUNC: { struct ipmi_recv rsp; - int addr_len; - struct list_head *entry; - struct ipmi_recv_msg *msg; - unsigned long flags; - - - rv = 0; - if (copy_from_user(&rsp, arg, sizeof(rsp))) { - rv = -EFAULT; - break; - } - - /* We claim a mutex because we don't want two - users getting something from the queue at a time. - Since we have to release the spinlock before we can - copy the data to the user, it's possible another - user will grab something from the queue, too. Then - the messages might get out of order if something - fails and the message gets put back onto the - queue. This mutex prevents that problem. */ - mutex_lock(&priv->recv_mutex); - - /* Grab the message off the list. */ - spin_lock_irqsave(&(priv->recv_msg_lock), flags); - if (list_empty(&(priv->recv_msgs))) { - spin_unlock_irqrestore(&(priv->recv_msg_lock), flags); - rv = -EAGAIN; - goto recv_err; - } - entry = priv->recv_msgs.next; - msg = list_entry(entry, struct ipmi_recv_msg, link); - list_del(entry); - spin_unlock_irqrestore(&(priv->recv_msg_lock), flags); - - addr_len = ipmi_addr_length(msg->addr.addr_type); - if (rsp.addr_len < addr_len) - { - rv = -EINVAL; - goto recv_putback_on_err; - } - if (copy_to_user(rsp.addr, &(msg->addr), addr_len)) { + if (copy_from_user(&rsp, arg, sizeof(rsp))) rv = -EFAULT; - goto recv_putback_on_err; - } - rsp.addr_len = addr_len; - - rsp.recv_type = msg->recv_type; - rsp.msgid = msg->msgid; - rsp.msg.netfn = msg->msg.netfn; - rsp.msg.cmd = msg->msg.cmd; - - if (msg->msg.data_len > 0) { - if (rsp.msg.data_len < msg->msg.data_len) { - rv = -EMSGSIZE; - if (cmd == IPMICTL_RECEIVE_MSG_TRUNC) { - msg->msg.data_len = rsp.msg.data_len; - } else { - goto recv_putback_on_err; - } - } - - if (copy_to_user(rsp.msg.data, - msg->msg.data, - msg->msg.data_len)) - { - rv = -EFAULT; - goto recv_putback_on_err; - } - rsp.msg.data_len = msg->msg.data_len; - } else { - rsp.msg.data_len = 0; - } - - if (copy_to_user(arg, &rsp, sizeof(rsp))) { - rv = -EFAULT; - goto recv_putback_on_err; - } - - mutex_unlock(&priv->recv_mutex); - ipmi_free_recv_msg(msg); - break; - - recv_putback_on_err: - /* If we got an error, put the message back onto - the head of the queue. */ - spin_lock_irqsave(&(priv->recv_msg_lock), flags); - list_add(entry, &(priv->recv_msgs)); - spin_unlock_irqrestore(&(priv->recv_msg_lock), flags); - mutex_unlock(&priv->recv_mutex); - break; - - recv_err: - mutex_unlock(&priv->recv_mutex); + else + rv = handle_recv(priv, cmd == IPMICTL_RECEIVE_MSG_TRUNC, + &rsp, copyout_recv, arg); break; } @@ -696,85 +704,56 @@ struct compat_ipmi_req_settime { /* * Define some helper functions for copying IPMI data */ -static long get_compat_ipmi_msg(struct ipmi_msg *p64, - struct compat_ipmi_msg __user *p32) -{ - compat_uptr_t tmp; - - if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || - __get_user(p64->netfn, &p32->netfn) || - __get_user(p64->cmd, &p32->cmd) || - __get_user(p64->data_len, &p32->data_len) || - __get_user(tmp, &p32->data)) - return -EFAULT; - p64->data = compat_ptr(tmp); - return 0; -} - -static long put_compat_ipmi_msg(struct ipmi_msg *p64, - struct compat_ipmi_msg __user *p32) +static void get_compat_ipmi_msg(struct ipmi_msg *p64, + struct compat_ipmi_msg *p32) { - if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || - __put_user(p64->netfn, &p32->netfn) || - __put_user(p64->cmd, &p32->cmd) || - __put_user(p64->data_len, &p32->data_len)) - return -EFAULT; - return 0; + p64->netfn = p32->netfn; + p64->cmd = p32->cmd; + p64->data_len = p32->data_len; + p64->data = compat_ptr(p32->data); } -static long get_compat_ipmi_req(struct ipmi_req *p64, - struct compat_ipmi_req __user *p32) +static void get_compat_ipmi_req(struct ipmi_req *p64, + struct compat_ipmi_req *p32) { - - compat_uptr_t tmp; - - if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || - __get_user(tmp, &p32->addr) || - __get_user(p64->addr_len, &p32->addr_len) || - __get_user(p64->msgid, &p32->msgid) || - get_compat_ipmi_msg(&p64->msg, &p32->msg)) - return -EFAULT; - p64->addr = compat_ptr(tmp); - return 0; + p64->addr = compat_ptr(p32->addr); + p64->addr_len = p32->addr_len; + p64->msgid = p32->msgid; + get_compat_ipmi_msg(&p64->msg, &p32->msg); } -static long get_compat_ipmi_req_settime(struct ipmi_req_settime *p64, - struct compat_ipmi_req_settime __user *p32) +static void get_compat_ipmi_req_settime(struct ipmi_req_settime *p64, + struct compat_ipmi_req_settime *p32) { - if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || - get_compat_ipmi_req(&p64->req, &p32->req) || - __get_user(p64->retries, &p32->retries) || - __get_user(p64->retry_time_ms, &p32->retry_time_ms)) - return -EFAULT; - return 0; + get_compat_ipmi_req(&p64->req, &p32->req); + p64->retries = p32->retries; + p64->retry_time_ms = p32->retry_time_ms; } -static long get_compat_ipmi_recv(struct ipmi_recv *p64, - struct compat_ipmi_recv __user *p32) +static void get_compat_ipmi_recv(struct ipmi_recv *p64, + struct compat_ipmi_recv *p32) { - compat_uptr_t tmp; - - if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || - __get_user(p64->recv_type, &p32->recv_type) || - __get_user(tmp, &p32->addr) || - __get_user(p64->addr_len, &p32->addr_len) || - __get_user(p64->msgid, &p32->msgid) || - get_compat_ipmi_msg(&p64->msg, &p32->msg)) - return -EFAULT; - p64->addr = compat_ptr(tmp); - return 0; + memset(p64, 0, sizeof(struct ipmi_recv)); + p64->recv_type = p32->recv_type; + p64->addr = compat_ptr(p32->addr); + p64->addr_len = p32->addr_len; + p64->msgid = p32->msgid; + get_compat_ipmi_msg(&p64->msg, &p32->msg); } -static long put_compat_ipmi_recv(struct ipmi_recv *p64, - struct compat_ipmi_recv __user *p32) +static int copyout_recv32(struct ipmi_recv *p64, void __user *to) { - if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || - __put_user(p64->recv_type, &p32->recv_type) || - __put_user(p64->addr_len, &p32->addr_len) || - __put_user(p64->msgid, &p32->msgid) || - put_compat_ipmi_msg(&p64->msg, &p32->msg)) - return -EFAULT; - return 0; + struct compat_ipmi_recv v32; + memset(&v32, 0, sizeof(struct compat_ipmi_recv)); + v32.recv_type = p64->recv_type; + v32.addr = ptr_to_compat(p64->addr); + v32.addr_len = p64->addr_len; + v32.msgid = p64->msgid; + v32.msg.netfn = p64->msg.netfn; + v32.msg.cmd = p64->msg.cmd; + v32.msg.data_len = p64->msg.data_len; + v32.msg.data = ptr_to_compat(p64->msg.data); + return copy_to_user(to, &v32, sizeof(v32)) ? -EFAULT : 0; } /* @@ -783,17 +762,19 @@ static long put_compat_ipmi_recv(struct ipmi_recv *p64, static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { - int rc; struct ipmi_file_private *priv = filep->private_data; switch(cmd) { case COMPAT_IPMICTL_SEND_COMMAND: { struct ipmi_req rp; + struct compat_ipmi_req r32; - if (get_compat_ipmi_req(&rp, compat_ptr(arg))) + if (copy_from_user(&r32, compat_ptr(arg), sizeof(r32))) return -EFAULT; + get_compat_ipmi_req(&rp, &r32); + return handle_send_req(priv->user, &rp, priv->default_retries, priv->default_retry_time_ms); @@ -801,42 +782,30 @@ static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, case COMPAT_IPMICTL_SEND_COMMAND_SETTIME: { struct ipmi_req_settime sp; + struct compat_ipmi_req_settime sp32; - if (get_compat_ipmi_req_settime(&sp, compat_ptr(arg))) + if (copy_from_user(&sp32, compat_ptr(arg), sizeof(sp32))) return -EFAULT; + get_compat_ipmi_req_settime(&sp, &sp32); + return handle_send_req(priv->user, &sp.req, sp.retries, sp.retry_time_ms); } case COMPAT_IPMICTL_RECEIVE_MSG: case COMPAT_IPMICTL_RECEIVE_MSG_TRUNC: { - struct ipmi_recv __user *precv64; struct ipmi_recv recv64; + struct compat_ipmi_recv recv32; - memset(&recv64, 0, sizeof(recv64)); - if (get_compat_ipmi_recv(&recv64, compat_ptr(arg))) + if (copy_from_user(&recv32, compat_ptr(arg), sizeof(recv32))) return -EFAULT; - precv64 = compat_alloc_user_space(sizeof(recv64)); - if (copy_to_user(precv64, &recv64, sizeof(recv64))) - return -EFAULT; - - rc = ipmi_ioctl(filep, - ((cmd == COMPAT_IPMICTL_RECEIVE_MSG) - ? IPMICTL_RECEIVE_MSG - : IPMICTL_RECEIVE_MSG_TRUNC), - (unsigned long) precv64); - if (rc != 0) - return rc; - - if (copy_from_user(&recv64, precv64, sizeof(recv64))) - return -EFAULT; - - if (put_compat_ipmi_recv(&recv64, compat_ptr(arg))) - return -EFAULT; + get_compat_ipmi_recv(&recv64, &recv32); - return rc; + return handle_recv(priv, + cmd == COMPAT_IPMICTL_RECEIVE_MSG_TRUNC, + &recv64, copyout_recv32, compat_ptr(arg)); } default: return ipmi_ioctl(filep, cmd, arg); diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c new file mode 100644 index 000000000000..2a84401dea05 --- /dev/null +++ b/drivers/char/ipmi/ipmi_dmi.c @@ -0,0 +1,273 @@ +/* + * A hack to create a platform device from a DMI entry. This will + * allow autoloading of the IPMI drive based on SMBIOS entries. + */ + +#include <linux/ipmi.h> +#include <linux/init.h> +#include <linux/dmi.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include "ipmi_dmi.h" + +struct ipmi_dmi_info { + int type; + u32 flags; + unsigned long addr; + u8 slave_addr; + struct ipmi_dmi_info *next; +}; + +static struct ipmi_dmi_info *ipmi_dmi_infos; + +static int ipmi_dmi_nr __initdata; + +static void __init dmi_add_platform_ipmi(unsigned long base_addr, + u32 flags, + u8 slave_addr, + int irq, + int offset, + int type) +{ + struct platform_device *pdev; + struct resource r[4]; + unsigned int num_r = 1, size; + struct property_entry p[4] = { + PROPERTY_ENTRY_U8("slave-addr", slave_addr), + PROPERTY_ENTRY_U8("ipmi-type", type), + PROPERTY_ENTRY_U16("i2c-addr", base_addr), + { } + }; + char *name, *override; + int rv; + struct ipmi_dmi_info *info; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + pr_warn("ipmi:dmi: Could not allocate dmi info\n"); + } else { + info->type = type; + info->flags = flags; + info->addr = base_addr; + info->slave_addr = slave_addr; + info->next = ipmi_dmi_infos; + ipmi_dmi_infos = info; + } + + name = "dmi-ipmi-si"; + override = "ipmi_si"; + switch (type) { + case IPMI_DMI_TYPE_SSIF: + name = "dmi-ipmi-ssif"; + override = "ipmi_ssif"; + offset = 1; + size = 1; + break; + case IPMI_DMI_TYPE_BT: + size = 3; + break; + case IPMI_DMI_TYPE_KCS: + case IPMI_DMI_TYPE_SMIC: + size = 2; + break; + default: + pr_err("ipmi:dmi: Invalid IPMI type: %d", type); + return; + } + + pdev = platform_device_alloc(name, ipmi_dmi_nr); + if (!pdev) { + pr_err("ipmi:dmi: Error allocation IPMI platform device"); + return; + } + pdev->driver_override = override; + + if (type == IPMI_DMI_TYPE_SSIF) + goto add_properties; + + memset(r, 0, sizeof(r)); + + r[0].start = base_addr; + r[0].end = r[0].start + offset - 1; + r[0].name = "IPMI Address 1"; + r[0].flags = flags; + + if (size > 1) { + r[1].start = r[0].start + offset; + r[1].end = r[1].start + offset - 1; + r[1].name = "IPMI Address 2"; + r[1].flags = flags; + num_r++; + } + + if (size > 2) { + r[2].start = r[1].start + offset; + r[2].end = r[2].start + offset - 1; + r[2].name = "IPMI Address 3"; + r[2].flags = flags; + num_r++; + } + + if (irq) { + r[num_r].start = irq; + r[num_r].end = irq; + r[num_r].name = "IPMI IRQ"; + r[num_r].flags = IORESOURCE_IRQ; + num_r++; + } + + rv = platform_device_add_resources(pdev, r, num_r); + if (rv) { + dev_err(&pdev->dev, + "ipmi:dmi: Unable to add resources: %d\n", rv); + goto err; + } + +add_properties: + rv = platform_device_add_properties(pdev, p); + if (rv) { + dev_err(&pdev->dev, + "ipmi:dmi: Unable to add properties: %d\n", rv); + goto err; + } + + rv = platform_device_add(pdev); + if (rv) { + dev_err(&pdev->dev, "ipmi:dmi: Unable to add device: %d\n", rv); + goto err; + } + + ipmi_dmi_nr++; + return; + +err: + platform_device_put(pdev); +} + +/* + * Look up the slave address for a given interface. This is here + * because ACPI doesn't have a slave address while SMBIOS does, but we + * prefer using ACPI so the ACPI code can use the IPMI namespace. + * This function allows an ACPI-specified IPMI device to look up the + * slave address from the DMI table. + */ +int ipmi_dmi_get_slave_addr(int type, u32 flags, unsigned long base_addr) +{ + struct ipmi_dmi_info *info = ipmi_dmi_infos; + + while (info) { + if (info->type == type && + info->flags == flags && + info->addr == base_addr) + return info->slave_addr; + info = info->next; + } + + return 0; +} +EXPORT_SYMBOL(ipmi_dmi_get_slave_addr); + +#define DMI_IPMI_MIN_LENGTH 0x10 +#define DMI_IPMI_VER2_LENGTH 0x12 +#define DMI_IPMI_TYPE 4 +#define DMI_IPMI_SLAVEADDR 6 +#define DMI_IPMI_ADDR 8 +#define DMI_IPMI_ACCESS 0x10 +#define DMI_IPMI_IRQ 0x11 +#define DMI_IPMI_IO_MASK 0xfffe + +static void __init dmi_decode_ipmi(const struct dmi_header *dm) +{ + const u8 *data = (const u8 *) dm; + u32 flags = IORESOURCE_IO; + unsigned long base_addr; + u8 len = dm->length; + u8 slave_addr; + int irq = 0, offset; + int type; + + if (len < DMI_IPMI_MIN_LENGTH) + return; + + type = data[DMI_IPMI_TYPE]; + slave_addr = data[DMI_IPMI_SLAVEADDR]; + + memcpy(&base_addr, data + DMI_IPMI_ADDR, sizeof(unsigned long)); + if (len >= DMI_IPMI_VER2_LENGTH) { + if (type == IPMI_DMI_TYPE_SSIF) { + offset = 0; + flags = 0; + base_addr = data[DMI_IPMI_ADDR] >> 1; + if (base_addr == 0) { + /* + * Some broken systems put the I2C address in + * the slave address field. We try to + * accommodate them here. + */ + base_addr = data[DMI_IPMI_SLAVEADDR] >> 1; + slave_addr = 0; + } + } else { + if (base_addr & 1) { + /* I/O */ + base_addr &= DMI_IPMI_IO_MASK; + } else { + /* Memory */ + flags = IORESOURCE_MEM; + } + + /* + * If bit 4 of byte 0x10 is set, then the lsb + * for the address is odd. + */ + base_addr |= (data[DMI_IPMI_ACCESS] >> 4) & 1; + + irq = data[DMI_IPMI_IRQ]; + + /* + * The top two bits of byte 0x10 hold the + * register spacing. + */ + switch ((data[DMI_IPMI_ACCESS] >> 6) & 3) { + case 0: /* Byte boundaries */ + offset = 1; + break; + case 1: /* 32-bit boundaries */ + offset = 4; + break; + case 2: /* 16-byte boundaries */ + offset = 16; + break; + default: + pr_err("ipmi:dmi: Invalid offset: 0"); + return; + } + } + } else { + /* Old DMI spec. */ + /* + * Note that technically, the lower bit of the base + * address should be 1 if the address is I/O and 0 if + * the address is in memory. So many systems get that + * wrong (and all that I have seen are I/O) so we just + * ignore that bit and assume I/O. Systems that use + * memory should use the newer spec, anyway. + */ + base_addr = base_addr & DMI_IPMI_IO_MASK; + offset = 1; + } + + dmi_add_platform_ipmi(base_addr, flags, slave_addr, irq, + offset, type); +} + +static int __init scan_for_dmi_ipmi(void) +{ + const struct dmi_device *dev = NULL; + + while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) + dmi_decode_ipmi((const struct dmi_header *) dev->device_data); + + return 0; +} +subsys_initcall(scan_for_dmi_ipmi); diff --git a/drivers/char/ipmi/ipmi_dmi.h b/drivers/char/ipmi/ipmi_dmi.h new file mode 100644 index 000000000000..0a1afe5ceb1e --- /dev/null +++ b/drivers/char/ipmi/ipmi_dmi.h @@ -0,0 +1,12 @@ +/* + * DMI defines for use by IPMI + */ + +#define IPMI_DMI_TYPE_KCS 0x01 +#define IPMI_DMI_TYPE_SMIC 0x02 +#define IPMI_DMI_TYPE_BT 0x03 +#define IPMI_DMI_TYPE_SSIF 0x04 + +#ifdef CONFIG_IPMI_DMI_DECODE +int ipmi_dmi_get_slave_addr(int type, u32 flags, unsigned long base_addr); +#endif diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 9f699951b75a..810b138f5897 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -2397,7 +2397,7 @@ static umode_t bmc_dev_attr_is_visible(struct kobject *kobj, return mode; } -static struct attribute_group bmc_dev_attr_group = { +static const struct attribute_group bmc_dev_attr_group = { .attrs = bmc_dev_attrs, .is_visible = bmc_dev_attr_is_visible, }; @@ -2407,7 +2407,7 @@ static const struct attribute_group *bmc_dev_attr_groups[] = { NULL }; -static struct device_type bmc_device_type = { +static const struct device_type bmc_device_type = { .groups = bmc_dev_attr_groups, }; @@ -3878,6 +3878,9 @@ static void smi_recv_tasklet(unsigned long val) * because the lower layer is allowed to hold locks while calling * message delivery. */ + + rcu_read_lock(); + if (!run_to_completion) spin_lock_irqsave(&intf->xmit_msgs_lock, flags); if (intf->curr_msg == NULL && !intf->in_shutdown) { @@ -3900,6 +3903,8 @@ static void smi_recv_tasklet(unsigned long val) if (newmsg) intf->handlers->sender(intf->send_info, newmsg); + rcu_read_unlock(); + handle_new_recv_msgs(intf); } diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 59ee93ea84eb..985973855005 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -61,6 +61,7 @@ #include <linux/ipmi_smi.h> #include <asm/io.h> #include "ipmi_si_sm.h" +#include "ipmi_dmi.h" #include <linux/dmi.h> #include <linux/string.h> #include <linux/ctype.h> @@ -1942,7 +1943,7 @@ static int hotmod_handler(const char *val, struct kernel_param *kp) info->io.regspacing = DEFAULT_REGSPACING; info->io.regsize = regsize; if (!info->io.regsize) - info->io.regsize = DEFAULT_REGSPACING; + info->io.regsize = DEFAULT_REGSIZE; info->io.regshift = regshift; info->irq = irq; if (info->irq) @@ -2036,7 +2037,7 @@ static int hardcode_find_bmc(void) info->io.regspacing = DEFAULT_REGSPACING; info->io.regsize = regsizes[i]; if (!info->io.regsize) - info->io.regsize = DEFAULT_REGSPACING; + info->io.regsize = DEFAULT_REGSIZE; info->io.regshift = regshifts[i]; info->irq = irqs[i]; if (info->irq) @@ -2273,136 +2274,105 @@ static void spmi_find_bmc(void) } #endif -#ifdef CONFIG_DMI -struct dmi_ipmi_data { - u8 type; - u8 addr_space; - unsigned long base_addr; - u8 irq; - u8 offset; - u8 slave_addr; -}; - -static int decode_dmi(const struct dmi_header *dm, - struct dmi_ipmi_data *dmi) +#if defined(CONFIG_DMI) || defined(CONFIG_ACPI) +struct resource *ipmi_get_info_from_resources(struct platform_device *pdev, + struct smi_info *info) { - const u8 *data = (const u8 *)dm; - unsigned long base_addr; - u8 reg_spacing; - u8 len = dm->length; - - dmi->type = data[4]; + struct resource *res, *res_second; - memcpy(&base_addr, data+8, sizeof(unsigned long)); - if (len >= 0x11) { - if (base_addr & 1) { - /* I/O */ - base_addr &= 0xFFFE; - dmi->addr_space = IPMI_IO_ADDR_SPACE; - } else - /* Memory */ - dmi->addr_space = IPMI_MEM_ADDR_SPACE; - - /* If bit 4 of byte 0x10 is set, then the lsb for the address - is odd. */ - dmi->base_addr = base_addr | ((data[0x10] & 0x10) >> 4); - - dmi->irq = data[0x11]; - - /* The top two bits of byte 0x10 hold the register spacing. */ - reg_spacing = (data[0x10] & 0xC0) >> 6; - switch (reg_spacing) { - case 0x00: /* Byte boundaries */ - dmi->offset = 1; - break; - case 0x01: /* 32-bit boundaries */ - dmi->offset = 4; - break; - case 0x02: /* 16-byte boundaries */ - dmi->offset = 16; - break; - default: - /* Some other interface, just ignore it. */ - return -EIO; - } + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (res) { + info->io_setup = port_setup; + info->io.addr_type = IPMI_IO_ADDR_SPACE; } else { - /* Old DMI spec. */ - /* - * Note that technically, the lower bit of the base - * address should be 1 if the address is I/O and 0 if - * the address is in memory. So many systems get that - * wrong (and all that I have seen are I/O) so we just - * ignore that bit and assume I/O. Systems that use - * memory should use the newer spec, anyway. - */ - dmi->base_addr = base_addr & 0xfffe; - dmi->addr_space = IPMI_IO_ADDR_SPACE; - dmi->offset = 1; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) { + info->io_setup = mem_setup; + info->io.addr_type = IPMI_MEM_ADDR_SPACE; + } } + if (!res) { + dev_err(&pdev->dev, "no I/O or memory address\n"); + return NULL; + } + info->io.addr_data = res->start; - dmi->slave_addr = data[6]; + info->io.regspacing = DEFAULT_REGSPACING; + res_second = platform_get_resource(pdev, + (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? + IORESOURCE_IO : IORESOURCE_MEM, + 1); + if (res_second) { + if (res_second->start > info->io.addr_data) + info->io.regspacing = + res_second->start - info->io.addr_data; + } + info->io.regsize = DEFAULT_REGSIZE; + info->io.regshift = 0; - return 0; + return res; } -static void try_init_dmi(struct dmi_ipmi_data *ipmi_data) +#endif + +#ifdef CONFIG_DMI +static int dmi_ipmi_probe(struct platform_device *pdev) { struct smi_info *info; + u8 type, slave_addr; + int rv; + + if (!si_trydmi) + return -ENODEV; + + rv = device_property_read_u8(&pdev->dev, "ipmi-type", &type); + if (rv) + return -ENODEV; info = smi_info_alloc(); if (!info) { pr_err(PFX "Could not allocate SI data\n"); - return; + return -ENOMEM; } info->addr_source = SI_SMBIOS; pr_info(PFX "probing via SMBIOS\n"); - switch (ipmi_data->type) { - case 0x01: /* KCS */ + switch (type) { + case IPMI_DMI_TYPE_KCS: info->si_type = SI_KCS; break; - case 0x02: /* SMIC */ + case IPMI_DMI_TYPE_SMIC: info->si_type = SI_SMIC; break; - case 0x03: /* BT */ + case IPMI_DMI_TYPE_BT: info->si_type = SI_BT; break; default: kfree(info); - return; + return -EINVAL; } - switch (ipmi_data->addr_space) { - case IPMI_MEM_ADDR_SPACE: - info->io_setup = mem_setup; - info->io.addr_type = IPMI_MEM_ADDR_SPACE; - break; - - case IPMI_IO_ADDR_SPACE: - info->io_setup = port_setup; - info->io.addr_type = IPMI_IO_ADDR_SPACE; - break; - - default: - kfree(info); - pr_warn(PFX "Unknown SMBIOS I/O Address type: %d\n", - ipmi_data->addr_space); - return; + if (!ipmi_get_info_from_resources(pdev, info)) { + rv = -EINVAL; + goto err_free; } - info->io.addr_data = ipmi_data->base_addr; - info->io.regspacing = ipmi_data->offset; - if (!info->io.regspacing) - info->io.regspacing = DEFAULT_REGSPACING; - info->io.regsize = DEFAULT_REGSPACING; - info->io.regshift = 0; - - info->slave_addr = ipmi_data->slave_addr; + rv = device_property_read_u8(&pdev->dev, "slave-addr", &slave_addr); + if (rv) { + dev_warn(&pdev->dev, "device has no slave-addr property"); + info->slave_addr = 0x20; + } else { + info->slave_addr = slave_addr; + } - info->irq = ipmi_data->irq; - if (info->irq) + info->irq = platform_get_irq(pdev, 0); + if (info->irq > 0) info->irq_setup = std_irq_setup; + else + info->irq = 0; + + info->dev = &pdev->dev; pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n", (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem", @@ -2411,21 +2381,17 @@ static void try_init_dmi(struct dmi_ipmi_data *ipmi_data) if (add_smi(info)) kfree(info); -} -static void dmi_find_bmc(void) -{ - const struct dmi_device *dev = NULL; - struct dmi_ipmi_data data; - int rv; + return 0; - while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) { - memset(&data, 0, sizeof(data)); - rv = decode_dmi((const struct dmi_header *) dev->device_data, - &data); - if (!rv) - try_init_dmi(&data); - } +err_free: + kfree(info); + return rv; +} +#else +static int dmi_ipmi_probe(struct platform_device *pdev) +{ + return -ENODEV; } #endif /* CONFIG_DMI */ @@ -2684,17 +2650,47 @@ static int of_ipmi_probe(struct platform_device *dev) #endif #ifdef CONFIG_ACPI +static int find_slave_address(struct smi_info *info, int slave_addr) +{ +#ifdef CONFIG_IPMI_DMI_DECODE + if (!slave_addr) { + int type = -1; + u32 flags = IORESOURCE_IO; + + switch (info->si_type) { + case SI_KCS: + type = IPMI_DMI_TYPE_KCS; + break; + case SI_BT: + type = IPMI_DMI_TYPE_BT; + break; + case SI_SMIC: + type = IPMI_DMI_TYPE_SMIC; + break; + } + + if (info->io.addr_type == IPMI_MEM_ADDR_SPACE) + flags = IORESOURCE_MEM; + + slave_addr = ipmi_dmi_get_slave_addr(type, flags, + info->io.addr_data); + } +#endif + + return slave_addr; +} + static int acpi_ipmi_probe(struct platform_device *dev) { struct smi_info *info; - struct resource *res, *res_second; acpi_handle handle; acpi_status status; unsigned long long tmp; + struct resource *res; int rv = -EINVAL; if (!si_tryacpi) - return 0; + return -ENODEV; handle = ACPI_HANDLE(&dev->dev); if (!handle) @@ -2734,35 +2730,11 @@ static int acpi_ipmi_probe(struct platform_device *dev) goto err_free; } - res = platform_get_resource(dev, IORESOURCE_IO, 0); - if (res) { - info->io_setup = port_setup; - info->io.addr_type = IPMI_IO_ADDR_SPACE; - } else { - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (res) { - info->io_setup = mem_setup; - info->io.addr_type = IPMI_MEM_ADDR_SPACE; - } - } + res = ipmi_get_info_from_resources(dev, info); if (!res) { - dev_err(&dev->dev, "no I/O or memory address\n"); + rv = -EINVAL; goto err_free; } - info->io.addr_data = res->start; - - info->io.regspacing = DEFAULT_REGSPACING; - res_second = platform_get_resource(dev, - (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? - IORESOURCE_IO : IORESOURCE_MEM, - 1); - if (res_second) { - if (res_second->start > info->io.addr_data) - info->io.regspacing = - res_second->start - info->io.addr_data; - } - info->io.regsize = DEFAULT_REGSPACING; - info->io.regshift = 0; /* If _GPE exists, use it; otherwise use standard interrupts */ status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp); @@ -2778,6 +2750,8 @@ static int acpi_ipmi_probe(struct platform_device *dev) } } + info->slave_addr = find_slave_address(info, info->slave_addr); + info->dev = &dev->dev; platform_set_drvdata(dev, info); @@ -2813,7 +2787,10 @@ static int ipmi_probe(struct platform_device *dev) if (of_ipmi_probe(dev) == 0) return 0; - return acpi_ipmi_probe(dev); + if (acpi_ipmi_probe(dev) == 0) + return 0; + + return dmi_ipmi_probe(dev); } static int ipmi_remove(struct platform_device *dev) @@ -3786,11 +3763,6 @@ static int init_ipmi_si(void) } #endif -#ifdef CONFIG_DMI - if (si_trydmi) - dmi_find_bmc(); -#endif - #ifdef CONFIG_ACPI if (si_tryacpi) spmi_find_bmc(); @@ -3938,6 +3910,7 @@ static void cleanup_ipmi_si(void) } module_exit(cleanup_ipmi_si); +MODULE_ALIAS("platform:dmi-ipmi-si"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Corey Minyard <minyard@mvista.com>"); MODULE_DESCRIPTION("Interface to the IPMI driver for the KCS, SMIC, and BT" diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 0b22a9be5029..0aea3bcb6158 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -53,6 +53,7 @@ #include <linux/acpi.h> #include <linux/ctype.h> #include <linux/time64.h> +#include "ipmi_dmi.h" #define PFX "ipmi_ssif: " #define DEVICE_NAME "ipmi_ssif" @@ -180,6 +181,8 @@ struct ssif_addr_info { int slave_addr; enum ipmi_addr_src addr_src; union ipmi_smi_info_union addr_info; + struct device *dev; + struct i2c_client *client; struct mutex clients_mutex; struct list_head clients; @@ -408,6 +411,7 @@ static void start_event_fetch(struct ssif_info *ssif_info, unsigned long *flags) msg = ipmi_alloc_smi_msg(); if (!msg) { ssif_info->ssif_state = SSIF_NORMAL; + ipmi_ssif_unlock_cond(ssif_info, flags); return; } @@ -430,6 +434,7 @@ static void start_recv_msg_fetch(struct ssif_info *ssif_info, msg = ipmi_alloc_smi_msg(); if (!msg) { ssif_info->ssif_state = SSIF_NORMAL; + ipmi_ssif_unlock_cond(ssif_info, flags); return; } @@ -761,6 +766,11 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, result, len, data[2]); } else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || data[1] != IPMI_GET_MSG_FLAGS_CMD) { + /* + * Don't abort here, maybe it was a queued + * response to a previous command. + */ + ipmi_ssif_unlock_cond(ssif_info, flags); pr_warn(PFX "Invalid response getting flags: %x %x\n", data[0], data[1]); } else { @@ -1094,7 +1104,7 @@ static int inc_usecount(void *send_info) { struct ssif_info *ssif_info = send_info; - if (!i2c_get_adapter(ssif_info->client->adapter->nr)) + if (!i2c_get_adapter(i2c_adapter_id(ssif_info->client->adapter))) return -ENODEV; i2c_use_client(ssif_info->client); @@ -1169,6 +1179,7 @@ static LIST_HEAD(ssif_infos); static int ssif_remove(struct i2c_client *client) { struct ssif_info *ssif_info = i2c_get_clientdata(client); + struct ssif_addr_info *addr_info; int rv; if (!ssif_info) @@ -1196,6 +1207,13 @@ static int ssif_remove(struct i2c_client *client) kthread_stop(ssif_info->thread); } + list_for_each_entry(addr_info, &ssif_infos, link) { + if (addr_info->client == client) { + addr_info->client = NULL; + break; + } + } + /* * No message can be outstanding now, we have removed the * upper layer and it permitted us to do so. @@ -1404,28 +1422,13 @@ static bool check_acpi(struct ssif_info *ssif_info, struct device *dev) static int find_slave_address(struct i2c_client *client, int slave_addr) { - struct ssif_addr_info *info; - - if (slave_addr) - return slave_addr; - - /* - * Came in without a slave address, search around to see if - * the other sources have a slave address. This lets us pick - * up an SMBIOS slave address when using ACPI. - */ - list_for_each_entry(info, &ssif_infos, link) { - if (info->binfo.addr != client->addr) - continue; - if (info->adapter_name && client->adapter->name && - strcmp_nospace(info->adapter_name, - client->adapter->name)) - continue; - if (info->slave_addr) { - slave_addr = info->slave_addr; - break; - } - } +#ifdef CONFIG_IPMI_DMI_DECODE + if (!slave_addr) + slave_addr = ipmi_dmi_get_slave_addr( + IPMI_DMI_TYPE_SSIF, + i2c_adapter_id(client->adapter), + client->addr); +#endif return slave_addr; } @@ -1447,7 +1450,6 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) u8 slave_addr = 0; struct ssif_addr_info *addr_info = NULL; - resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); if (!resp) return -ENOMEM; @@ -1468,6 +1470,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ssif_info->addr_source = addr_info->addr_src; ssif_info->ssif_debug = addr_info->debug; ssif_info->addr_info = addr_info->addr_info; + addr_info->client = client; slave_addr = addr_info->slave_addr; } } @@ -1664,7 +1667,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) { unsigned int thread_num; - thread_num = ((ssif_info->client->adapter->nr << 8) | + thread_num = ((i2c_adapter_id(ssif_info->client->adapter) + << 8) | ssif_info->client->addr); init_completion(&ssif_info->wake_thread); ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info, @@ -1705,8 +1709,19 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) } out: - if (rv) + if (rv) { + /* + * Note that if addr_info->client is assigned, we + * leave it. The i2c client hangs around even if we + * return a failure here, and the failure here is not + * propagated back to the i2c code. This seems to be + * design intent, strange as it may be. But if we + * don't leave it, ssif_platform_remove will not remove + * the client like it should. + */ + dev_err(&client->dev, "Unable to start IPMI SSIF: %d\n", rv); kfree(ssif_info); + } kfree(resp); return rv; @@ -1731,7 +1746,8 @@ static int ssif_adapter_handler(struct device *adev, void *opaque) static int new_ssif_client(int addr, char *adapter_name, int debug, int slave_addr, - enum ipmi_addr_src addr_src) + enum ipmi_addr_src addr_src, + struct device *dev) { struct ssif_addr_info *addr_info; int rv = 0; @@ -1764,6 +1780,10 @@ static int new_ssif_client(int addr, char *adapter_name, addr_info->debug = debug; addr_info->slave_addr = slave_addr; addr_info->addr_src = addr_src; + addr_info->dev = dev; + + if (dev) + dev_set_drvdata(dev, addr_info); list_add_tail(&addr_info->link, &ssif_infos); @@ -1902,7 +1922,7 @@ static int try_init_spmi(struct SPMITable *spmi) myaddr = spmi->addr.address & 0x7f; - return new_ssif_client(myaddr, NULL, 0, 0, SI_SPMI); + return new_ssif_client(myaddr, NULL, 0, 0, SI_SPMI, NULL); } static void spmi_find_bmc(void) @@ -1931,48 +1951,40 @@ static void spmi_find_bmc(void) { } #endif #ifdef CONFIG_DMI -static int decode_dmi(const struct dmi_device *dmi_dev) +static int dmi_ipmi_probe(struct platform_device *pdev) { - struct dmi_header *dm = dmi_dev->device_data; - u8 *data = (u8 *) dm; - u8 len = dm->length; - unsigned short myaddr; - int slave_addr; + u8 type, slave_addr = 0; + u16 i2c_addr; + int rv; - if (num_addrs >= MAX_SSIF_BMCS) - return -1; + if (!ssif_trydmi) + return -ENODEV; - if (len < 9) - return -1; + rv = device_property_read_u8(&pdev->dev, "ipmi-type", &type); + if (rv) + return -ENODEV; - if (data[0x04] != 4) /* Not SSIF */ - return -1; + if (type != IPMI_DMI_TYPE_SSIF) + return -ENODEV; - if ((data[8] >> 1) == 0) { - /* - * Some broken systems put the I2C address in - * the slave address field. We try to - * accommodate them here. - */ - myaddr = data[6] >> 1; - slave_addr = 0; - } else { - myaddr = data[8] >> 1; - slave_addr = data[6]; + rv = device_property_read_u16(&pdev->dev, "i2c-addr", &i2c_addr); + if (rv) { + dev_warn(&pdev->dev, PFX "No i2c-addr property\n"); + return -ENODEV; } - return new_ssif_client(myaddr, NULL, 0, slave_addr, SI_SMBIOS); -} - -static void dmi_iterator(void) -{ - const struct dmi_device *dev = NULL; + rv = device_property_read_u8(&pdev->dev, "slave-addr", &slave_addr); + if (rv) + dev_warn(&pdev->dev, "device has no slave-addr property"); - while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) - decode_dmi(dev); + return new_ssif_client(i2c_addr, NULL, 0, + slave_addr, SI_SMBIOS, &pdev->dev); } #else -static void dmi_iterator(void) { } +static int dmi_ipmi_probe(struct platform_device *pdev) +{ + return -ENODEV; +} #endif static const struct i2c_device_id ssif_id[] = { @@ -1993,6 +2005,36 @@ static struct i2c_driver ssif_i2c_driver = { .detect = ssif_detect }; +static int ssif_platform_probe(struct platform_device *dev) +{ + return dmi_ipmi_probe(dev); +} + +static int ssif_platform_remove(struct platform_device *dev) +{ + struct ssif_addr_info *addr_info = dev_get_drvdata(&dev->dev); + + if (!addr_info) + return 0; + + mutex_lock(&ssif_infos_mutex); + if (addr_info->client) + i2c_unregister_device(addr_info->client); + + list_del(&addr_info->link); + kfree(addr_info); + mutex_unlock(&ssif_infos_mutex); + return 0; +} + +static struct platform_driver ipmi_driver = { + .driver = { + .name = DEVICE_NAME, + }, + .probe = ssif_platform_probe, + .remove = ssif_platform_remove, +}; + static int init_ipmi_ssif(void) { int i; @@ -2007,7 +2049,7 @@ static int init_ipmi_ssif(void) for (i = 0; i < num_addrs; i++) { rv = new_ssif_client(addr[i], adapter_name[i], dbg[i], slave_addrs[i], - SI_HARDCODED); + SI_HARDCODED, NULL); if (rv) pr_err(PFX "Couldn't add hardcoded device at addr 0x%x\n", @@ -2017,11 +2059,16 @@ static int init_ipmi_ssif(void) if (ssif_tryacpi) ssif_i2c_driver.driver.acpi_match_table = ACPI_PTR(ssif_acpi_match); - if (ssif_trydmi) - dmi_iterator(); + if (ssif_tryacpi) spmi_find_bmc(); + if (ssif_trydmi) { + rv = platform_driver_register(&ipmi_driver); + if (rv) + pr_err(PFX "Unable to register driver: %d\n", rv); + } + ssif_i2c_driver.address_list = ssif_address_list(); rv = i2c_add_driver(&ssif_i2c_driver); @@ -2041,10 +2088,13 @@ static void cleanup_ipmi_ssif(void) i2c_del_driver(&ssif_i2c_driver); + platform_driver_unregister(&ipmi_driver); + free_ssif_clients(); } module_exit(cleanup_ipmi_ssif); +MODULE_ALIAS("platform:dmi-ipmi-ssif"); MODULE_AUTHOR("Todd C Davis <todd.c.davis@intel.com>, Corey Minyard <minyard@acm.org>"); MODULE_DESCRIPTION("IPMI driver for management controllers on a SMBus"); MODULE_LICENSE("GPL"); diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index d165af8abe36..3d832d0362a4 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -821,7 +821,7 @@ static ssize_t ipmi_read(struct file *file, loff_t *ppos) { int rv = 0; - wait_queue_t wait; + wait_queue_entry_t wait; if (count <= 0) return 0; @@ -1163,10 +1163,11 @@ static int wdog_reboot_handler(struct notifier_block *this, ipmi_watchdog_state = WDOG_TIMEOUT_NONE; ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); } else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) { - /* Set a long timer to let the reboot happens, but - reboot if it hangs, but only if the watchdog + /* Set a long timer to let the reboot happen or + reset if it hangs, but only if the watchdog timer was already running. */ - timeout = 120; + if (timeout < 120) + timeout = 120; pretimeout = 0; ipmi_watchdog_state = WDOG_TIMEOUT_RESET; ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 6e0cbe092220..593a8818aca9 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -343,7 +343,7 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma) phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; /* It's illegal to wrap around the end of the physical address space. */ - if (offset + (phys_addr_t)size < offset) + if (offset + (phys_addr_t)size - 1 < offset) return -EINVAL; if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c deleted file mode 100644 index 0e7fcb04f01e..000000000000 --- a/drivers/char/mmtimer.c +++ /dev/null @@ -1,858 +0,0 @@ -/* - * Timer device implementation for SGI SN platforms. - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2001-2006 Silicon Graphics, Inc. All rights reserved. - * - * This driver exports an API that should be supportable by any HPET or IA-PC - * multimedia timer. The code below is currently specific to the SGI Altix - * SHub RTC, however. - * - * 11/01/01 - jbarnes - initial revision - * 9/10/04 - Christoph Lameter - remove interrupt support for kernel inclusion - * 10/1/04 - Christoph Lameter - provide posix clock CLOCK_SGI_CYCLE - * 10/13/04 - Christoph Lameter, Dimitri Sivanich - provide timer interrupt - * support via the posix timer interface - */ - -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/ioctl.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/errno.h> -#include <linux/mm.h> -#include <linux/fs.h> -#include <linux/mmtimer.h> -#include <linux/miscdevice.h> -#include <linux/posix-timers.h> -#include <linux/interrupt.h> -#include <linux/time.h> -#include <linux/math64.h> -#include <linux/mutex.h> -#include <linux/slab.h> - -#include <linux/uaccess.h> -#include <asm/sn/addrs.h> -#include <asm/sn/intr.h> -#include <asm/sn/shub_mmr.h> -#include <asm/sn/nodepda.h> -#include <asm/sn/shubio.h> - -MODULE_AUTHOR("Jesse Barnes <jbarnes@sgi.com>"); -MODULE_DESCRIPTION("SGI Altix RTC Timer"); -MODULE_LICENSE("GPL"); - -/* name of the device, usually in /dev */ -#define MMTIMER_NAME "mmtimer" -#define MMTIMER_DESC "SGI Altix RTC Timer" -#define MMTIMER_VERSION "2.1" - -#define RTC_BITS 55 /* 55 bits for this implementation */ - -static struct k_clock sgi_clock; - -extern unsigned long sn_rtc_cycles_per_second; - -#define RTC_COUNTER_ADDR ((long *)LOCAL_MMR_ADDR(SH_RTC)) - -#define rtc_time() (*RTC_COUNTER_ADDR) - -static DEFINE_MUTEX(mmtimer_mutex); -static long mmtimer_ioctl(struct file *file, unsigned int cmd, - unsigned long arg); -static int mmtimer_mmap(struct file *file, struct vm_area_struct *vma); - -/* - * Period in femtoseconds (10^-15 s) - */ -static unsigned long mmtimer_femtoperiod = 0; - -static const struct file_operations mmtimer_fops = { - .owner = THIS_MODULE, - .mmap = mmtimer_mmap, - .unlocked_ioctl = mmtimer_ioctl, - .llseek = noop_llseek, -}; - -/* - * We only have comparison registers RTC1-4 currently available per - * node. RTC0 is used by SAL. - */ -/* Check for an RTC interrupt pending */ -static int mmtimer_int_pending(int comparator) -{ - if (HUB_L((unsigned long *)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED)) & - SH_EVENT_OCCURRED_RTC1_INT_MASK << comparator) - return 1; - else - return 0; -} - -/* Clear the RTC interrupt pending bit */ -static void mmtimer_clr_int_pending(int comparator) -{ - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED_ALIAS), - SH_EVENT_OCCURRED_RTC1_INT_MASK << comparator); -} - -/* Setup timer on comparator RTC1 */ -static void mmtimer_setup_int_0(int cpu, u64 expires) -{ - u64 val; - - /* Disable interrupt */ - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_ENABLE), 0UL); - - /* Initialize comparator value */ - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPB), -1L); - - /* Clear pending bit */ - mmtimer_clr_int_pending(0); - - val = ((u64)SGI_MMTIMER_VECTOR << SH_RTC1_INT_CONFIG_IDX_SHFT) | - ((u64)cpu_physical_id(cpu) << - SH_RTC1_INT_CONFIG_PID_SHFT); - - /* Set configuration */ - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_CONFIG), val); - - /* Enable RTC interrupts */ - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_ENABLE), 1UL); - - /* Initialize comparator value */ - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPB), expires); - - -} - -/* Setup timer on comparator RTC2 */ -static void mmtimer_setup_int_1(int cpu, u64 expires) -{ - u64 val; - - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_ENABLE), 0UL); - - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPC), -1L); - - mmtimer_clr_int_pending(1); - - val = ((u64)SGI_MMTIMER_VECTOR << SH_RTC2_INT_CONFIG_IDX_SHFT) | - ((u64)cpu_physical_id(cpu) << - SH_RTC2_INT_CONFIG_PID_SHFT); - - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_CONFIG), val); - - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_ENABLE), 1UL); - - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPC), expires); -} - -/* Setup timer on comparator RTC3 */ -static void mmtimer_setup_int_2(int cpu, u64 expires) -{ - u64 val; - - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_ENABLE), 0UL); - - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPD), -1L); - - mmtimer_clr_int_pending(2); - - val = ((u64)SGI_MMTIMER_VECTOR << SH_RTC3_INT_CONFIG_IDX_SHFT) | - ((u64)cpu_physical_id(cpu) << - SH_RTC3_INT_CONFIG_PID_SHFT); - - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_CONFIG), val); - - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_ENABLE), 1UL); - - HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPD), expires); -} - -/* - * This function must be called with interrupts disabled and preemption off - * in order to insure that the setup succeeds in a deterministic time frame. - * It will check if the interrupt setup succeeded. - */ -static int mmtimer_setup(int cpu, int comparator, unsigned long expires, - u64 *set_completion_time) -{ - switch (comparator) { - case 0: - mmtimer_setup_int_0(cpu, expires); - break; - case 1: - mmtimer_setup_int_1(cpu, expires); - break; - case 2: - mmtimer_setup_int_2(cpu, expires); - break; - } - /* We might've missed our expiration time */ - *set_completion_time = rtc_time(); - if (*set_completion_time <= expires) - return 1; - - /* - * If an interrupt is already pending then its okay - * if not then we failed - */ - return mmtimer_int_pending(comparator); -} - -static int mmtimer_disable_int(long nasid, int comparator) -{ - switch (comparator) { - case 0: - nasid == -1 ? HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_ENABLE), - 0UL) : REMOTE_HUB_S(nasid, SH_RTC1_INT_ENABLE, 0UL); - break; - case 1: - nasid == -1 ? HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_ENABLE), - 0UL) : REMOTE_HUB_S(nasid, SH_RTC2_INT_ENABLE, 0UL); - break; - case 2: - nasid == -1 ? HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_ENABLE), - 0UL) : REMOTE_HUB_S(nasid, SH_RTC3_INT_ENABLE, 0UL); - break; - default: - return -EFAULT; - } - return 0; -} - -#define COMPARATOR 1 /* The comparator to use */ - -#define TIMER_OFF 0xbadcabLL /* Timer is not setup */ -#define TIMER_SET 0 /* Comparator is set for this timer */ - -#define MMTIMER_INTERVAL_RETRY_INCREMENT_DEFAULT 40 - -/* There is one of these for each timer */ -struct mmtimer { - struct rb_node list; - struct k_itimer *timer; - int cpu; -}; - -struct mmtimer_node { - spinlock_t lock ____cacheline_aligned; - struct rb_root timer_head; - struct rb_node *next; - struct tasklet_struct tasklet; -}; -static struct mmtimer_node *timers; - -static unsigned mmtimer_interval_retry_increment = - MMTIMER_INTERVAL_RETRY_INCREMENT_DEFAULT; -module_param(mmtimer_interval_retry_increment, uint, 0644); -MODULE_PARM_DESC(mmtimer_interval_retry_increment, - "RTC ticks to add to expiration on interval retry (default 40)"); - -/* - * Add a new mmtimer struct to the node's mmtimer list. - * This function assumes the struct mmtimer_node is locked. - */ -static void mmtimer_add_list(struct mmtimer *n) -{ - int nodeid = n->timer->it.mmtimer.node; - unsigned long expires = n->timer->it.mmtimer.expires; - struct rb_node **link = &timers[nodeid].timer_head.rb_node; - struct rb_node *parent = NULL; - struct mmtimer *x; - - /* - * Find the right place in the rbtree: - */ - while (*link) { - parent = *link; - x = rb_entry(parent, struct mmtimer, list); - - if (expires < x->timer->it.mmtimer.expires) - link = &(*link)->rb_left; - else - link = &(*link)->rb_right; - } - - /* - * Insert the timer to the rbtree and check whether it - * replaces the first pending timer - */ - rb_link_node(&n->list, parent, link); - rb_insert_color(&n->list, &timers[nodeid].timer_head); - - if (!timers[nodeid].next || expires < rb_entry(timers[nodeid].next, - struct mmtimer, list)->timer->it.mmtimer.expires) - timers[nodeid].next = &n->list; -} - -/* - * Set the comparator for the next timer. - * This function assumes the struct mmtimer_node is locked. - */ -static void mmtimer_set_next_timer(int nodeid) -{ - struct mmtimer_node *n = &timers[nodeid]; - struct mmtimer *x; - struct k_itimer *t; - u64 expires, exp, set_completion_time; - int i; - -restart: - if (n->next == NULL) - return; - - x = rb_entry(n->next, struct mmtimer, list); - t = x->timer; - if (!t->it.mmtimer.incr) { - /* Not an interval timer */ - if (!mmtimer_setup(x->cpu, COMPARATOR, - t->it.mmtimer.expires, - &set_completion_time)) { - /* Late setup, fire now */ - tasklet_schedule(&n->tasklet); - } - return; - } - - /* Interval timer */ - i = 0; - expires = exp = t->it.mmtimer.expires; - while (!mmtimer_setup(x->cpu, COMPARATOR, expires, - &set_completion_time)) { - int to; - - i++; - expires = set_completion_time + - mmtimer_interval_retry_increment + (1 << i); - /* Calculate overruns as we go. */ - to = ((u64)(expires - exp) / t->it.mmtimer.incr); - if (to) { - t->it_overrun += to; - t->it.mmtimer.expires += t->it.mmtimer.incr * to; - exp = t->it.mmtimer.expires; - } - if (i > 20) { - printk(KERN_ALERT "mmtimer: cannot reschedule timer\n"); - t->it.mmtimer.clock = TIMER_OFF; - n->next = rb_next(&x->list); - rb_erase(&x->list, &n->timer_head); - kfree(x); - goto restart; - } - } -} - -/** - * mmtimer_ioctl - ioctl interface for /dev/mmtimer - * @file: file structure for the device - * @cmd: command to execute - * @arg: optional argument to command - * - * Executes the command specified by @cmd. Returns 0 for success, < 0 for - * failure. - * - * Valid commands: - * - * %MMTIMER_GETOFFSET - Should return the offset (relative to the start - * of the page where the registers are mapped) for the counter in question. - * - * %MMTIMER_GETRES - Returns the resolution of the clock in femto (10^-15) - * seconds - * - * %MMTIMER_GETFREQ - Copies the frequency of the clock in Hz to the address - * specified by @arg - * - * %MMTIMER_GETBITS - Returns the number of bits in the clock's counter - * - * %MMTIMER_MMAPAVAIL - Returns 1 if the registers can be mmap'd into userspace - * - * %MMTIMER_GETCOUNTER - Gets the current value in the counter and places it - * in the address specified by @arg. - */ -static long mmtimer_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - int ret = 0; - - mutex_lock(&mmtimer_mutex); - - switch (cmd) { - case MMTIMER_GETOFFSET: /* offset of the counter */ - /* - * SN RTC registers are on their own 64k page - */ - if(PAGE_SIZE <= (1 << 16)) - ret = (((long)RTC_COUNTER_ADDR) & (PAGE_SIZE-1)) / 8; - else - ret = -ENOSYS; - break; - - case MMTIMER_GETRES: /* resolution of the clock in 10^-15 s */ - if(copy_to_user((unsigned long __user *)arg, - &mmtimer_femtoperiod, sizeof(unsigned long))) - ret = -EFAULT; - break; - - case MMTIMER_GETFREQ: /* frequency in Hz */ - if(copy_to_user((unsigned long __user *)arg, - &sn_rtc_cycles_per_second, - sizeof(unsigned long))) - ret = -EFAULT; - break; - - case MMTIMER_GETBITS: /* number of bits in the clock */ - ret = RTC_BITS; - break; - - case MMTIMER_MMAPAVAIL: /* can we mmap the clock into userspace? */ - ret = (PAGE_SIZE <= (1 << 16)) ? 1 : 0; - break; - - case MMTIMER_GETCOUNTER: - if(copy_to_user((unsigned long __user *)arg, - RTC_COUNTER_ADDR, sizeof(unsigned long))) - ret = -EFAULT; - break; - default: - ret = -ENOTTY; - break; - } - mutex_unlock(&mmtimer_mutex); - return ret; -} - -/** - * mmtimer_mmap - maps the clock's registers into userspace - * @file: file structure for the device - * @vma: VMA to map the registers into - * - * Calls remap_pfn_range() to map the clock's registers into - * the calling process' address space. - */ -static int mmtimer_mmap(struct file *file, struct vm_area_struct *vma) -{ - unsigned long mmtimer_addr; - - if (vma->vm_end - vma->vm_start != PAGE_SIZE) - return -EINVAL; - - if (vma->vm_flags & VM_WRITE) - return -EPERM; - - if (PAGE_SIZE > (1 << 16)) - return -ENOSYS; - - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - - mmtimer_addr = __pa(RTC_COUNTER_ADDR); - mmtimer_addr &= ~(PAGE_SIZE - 1); - mmtimer_addr &= 0xfffffffffffffffUL; - - if (remap_pfn_range(vma, vma->vm_start, mmtimer_addr >> PAGE_SHIFT, - PAGE_SIZE, vma->vm_page_prot)) { - printk(KERN_ERR "remap_pfn_range failed in mmtimer.c\n"); - return -EAGAIN; - } - - return 0; -} - -static struct miscdevice mmtimer_miscdev = { - .minor = SGI_MMTIMER, - .name = MMTIMER_NAME, - .fops = &mmtimer_fops -}; - -static struct timespec sgi_clock_offset; -static int sgi_clock_period; - -/* - * Posix Timer Interface - */ - -static struct timespec sgi_clock_offset; -static int sgi_clock_period; - -static int sgi_clock_get(clockid_t clockid, struct timespec64 *tp) -{ - u64 nsec; - - nsec = rtc_time() * sgi_clock_period - + sgi_clock_offset.tv_nsec; - *tp = ns_to_timespec64(nsec); - tp->tv_sec += sgi_clock_offset.tv_sec; - return 0; -}; - -static int sgi_clock_set(const clockid_t clockid, const struct timespec64 *tp) -{ - - u64 nsec; - u32 rem; - - nsec = rtc_time() * sgi_clock_period; - - sgi_clock_offset.tv_sec = tp->tv_sec - div_u64_rem(nsec, NSEC_PER_SEC, &rem); - - if (rem <= tp->tv_nsec) - sgi_clock_offset.tv_nsec = tp->tv_sec - rem; - else { - sgi_clock_offset.tv_nsec = tp->tv_sec + NSEC_PER_SEC - rem; - sgi_clock_offset.tv_sec--; - } - return 0; -} - -/** - * mmtimer_interrupt - timer interrupt handler - * @irq: irq received - * @dev_id: device the irq came from - * - * Called when one of the comarators matches the counter, This - * routine will send signals to processes that have requested - * them. - * - * This interrupt is run in an interrupt context - * by the SHUB. It is therefore safe to locally access SHub - * registers. - */ -static irqreturn_t -mmtimer_interrupt(int irq, void *dev_id) -{ - unsigned long expires = 0; - int result = IRQ_NONE; - unsigned indx = cpu_to_node(smp_processor_id()); - struct mmtimer *base; - - spin_lock(&timers[indx].lock); - base = rb_entry(timers[indx].next, struct mmtimer, list); - if (base == NULL) { - spin_unlock(&timers[indx].lock); - return result; - } - - if (base->cpu == smp_processor_id()) { - if (base->timer) - expires = base->timer->it.mmtimer.expires; - /* expires test won't work with shared irqs */ - if ((mmtimer_int_pending(COMPARATOR) > 0) || - (expires && (expires <= rtc_time()))) { - mmtimer_clr_int_pending(COMPARATOR); - tasklet_schedule(&timers[indx].tasklet); - result = IRQ_HANDLED; - } - } - spin_unlock(&timers[indx].lock); - return result; -} - -static void mmtimer_tasklet(unsigned long data) -{ - int nodeid = data; - struct mmtimer_node *mn = &timers[nodeid]; - struct mmtimer *x; - struct k_itimer *t; - unsigned long flags; - - /* Send signal and deal with periodic signals */ - spin_lock_irqsave(&mn->lock, flags); - if (!mn->next) - goto out; - - x = rb_entry(mn->next, struct mmtimer, list); - t = x->timer; - - if (t->it.mmtimer.clock == TIMER_OFF) - goto out; - - t->it_overrun = 0; - - mn->next = rb_next(&x->list); - rb_erase(&x->list, &mn->timer_head); - - if (posix_timer_event(t, 0) != 0) - t->it_overrun++; - - if(t->it.mmtimer.incr) { - t->it.mmtimer.expires += t->it.mmtimer.incr; - mmtimer_add_list(x); - } else { - /* Ensure we don't false trigger in mmtimer_interrupt */ - t->it.mmtimer.clock = TIMER_OFF; - t->it.mmtimer.expires = 0; - kfree(x); - } - /* Set comparator for next timer, if there is one */ - mmtimer_set_next_timer(nodeid); - - t->it_overrun_last = t->it_overrun; -out: - spin_unlock_irqrestore(&mn->lock, flags); -} - -static int sgi_timer_create(struct k_itimer *timer) -{ - /* Insure that a newly created timer is off */ - timer->it.mmtimer.clock = TIMER_OFF; - return 0; -} - -/* This does not really delete a timer. It just insures - * that the timer is not active - * - * Assumption: it_lock is already held with irq's disabled - */ -static int sgi_timer_del(struct k_itimer *timr) -{ - cnodeid_t nodeid = timr->it.mmtimer.node; - unsigned long irqflags; - - spin_lock_irqsave(&timers[nodeid].lock, irqflags); - if (timr->it.mmtimer.clock != TIMER_OFF) { - unsigned long expires = timr->it.mmtimer.expires; - struct rb_node *n = timers[nodeid].timer_head.rb_node; - struct mmtimer *uninitialized_var(t); - int r = 0; - - timr->it.mmtimer.clock = TIMER_OFF; - timr->it.mmtimer.expires = 0; - - while (n) { - t = rb_entry(n, struct mmtimer, list); - if (t->timer == timr) - break; - - if (expires < t->timer->it.mmtimer.expires) - n = n->rb_left; - else - n = n->rb_right; - } - - if (!n) { - spin_unlock_irqrestore(&timers[nodeid].lock, irqflags); - return 0; - } - - if (timers[nodeid].next == n) { - timers[nodeid].next = rb_next(n); - r = 1; - } - - rb_erase(n, &timers[nodeid].timer_head); - kfree(t); - - if (r) { - mmtimer_disable_int(cnodeid_to_nasid(nodeid), - COMPARATOR); - mmtimer_set_next_timer(nodeid); - } - } - spin_unlock_irqrestore(&timers[nodeid].lock, irqflags); - return 0; -} - -/* Assumption: it_lock is already held with irq's disabled */ -static void sgi_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting) -{ - - if (timr->it.mmtimer.clock == TIMER_OFF) { - cur_setting->it_interval.tv_nsec = 0; - cur_setting->it_interval.tv_sec = 0; - cur_setting->it_value.tv_nsec = 0; - cur_setting->it_value.tv_sec =0; - return; - } - - cur_setting->it_interval = ns_to_timespec64(timr->it.mmtimer.incr * sgi_clock_period); - cur_setting->it_value = ns_to_timespec64((timr->it.mmtimer.expires - rtc_time()) * sgi_clock_period); -} - - -static int sgi_timer_set(struct k_itimer *timr, int flags, - struct itimerspec64 *new_setting, - struct itimerspec64 *old_setting) -{ - unsigned long when, period, irqflags; - int err = 0; - cnodeid_t nodeid; - struct mmtimer *base; - struct rb_node *n; - - if (old_setting) - sgi_timer_get(timr, old_setting); - - sgi_timer_del(timr); - when = timespec64_to_ns(&new_setting->it_value); - period = timespec64_to_ns(&new_setting->it_interval); - - if (when == 0) - /* Clear timer */ - return 0; - - base = kmalloc(sizeof(struct mmtimer), GFP_KERNEL); - if (base == NULL) - return -ENOMEM; - - if (flags & TIMER_ABSTIME) { - struct timespec64 n; - unsigned long now; - - getnstimeofday64(&n); - now = timespec64_to_ns(&n); - if (when > now) - when -= now; - else - /* Fire the timer immediately */ - when = 0; - } - - /* - * Convert to sgi clock period. Need to keep rtc_time() as near as possible - * to getnstimeofday() in order to be as faithful as possible to the time - * specified. - */ - when = (when + sgi_clock_period - 1) / sgi_clock_period + rtc_time(); - period = (period + sgi_clock_period - 1) / sgi_clock_period; - - /* - * We are allocating a local SHub comparator. If we would be moved to another - * cpu then another SHub may be local to us. Prohibit that by switching off - * preemption. - */ - preempt_disable(); - - nodeid = cpu_to_node(smp_processor_id()); - - /* Lock the node timer structure */ - spin_lock_irqsave(&timers[nodeid].lock, irqflags); - - base->timer = timr; - base->cpu = smp_processor_id(); - - timr->it.mmtimer.clock = TIMER_SET; - timr->it.mmtimer.node = nodeid; - timr->it.mmtimer.incr = period; - timr->it.mmtimer.expires = when; - - n = timers[nodeid].next; - - /* Add the new struct mmtimer to node's timer list */ - mmtimer_add_list(base); - - if (timers[nodeid].next == n) { - /* No need to reprogram comparator for now */ - spin_unlock_irqrestore(&timers[nodeid].lock, irqflags); - preempt_enable(); - return err; - } - - /* We need to reprogram the comparator */ - if (n) - mmtimer_disable_int(cnodeid_to_nasid(nodeid), COMPARATOR); - - mmtimer_set_next_timer(nodeid); - - /* Unlock the node timer structure */ - spin_unlock_irqrestore(&timers[nodeid].lock, irqflags); - - preempt_enable(); - - return err; -} - -static int sgi_clock_getres(const clockid_t which_clock, struct timespec64 *tp) -{ - tp->tv_sec = 0; - tp->tv_nsec = sgi_clock_period; - return 0; -} - -static struct k_clock sgi_clock = { - .clock_set = sgi_clock_set, - .clock_get = sgi_clock_get, - .clock_getres = sgi_clock_getres, - .timer_create = sgi_timer_create, - .timer_set = sgi_timer_set, - .timer_del = sgi_timer_del, - .timer_get = sgi_timer_get -}; - -/** - * mmtimer_init - device initialization routine - * - * Does initial setup for the mmtimer device. - */ -static int __init mmtimer_init(void) -{ - cnodeid_t node, maxn = -1; - - if (!ia64_platform_is("sn2")) - return 0; - - /* - * Sanity check the cycles/sec variable - */ - if (sn_rtc_cycles_per_second < 100000) { - printk(KERN_ERR "%s: unable to determine clock frequency\n", - MMTIMER_NAME); - goto out1; - } - - mmtimer_femtoperiod = ((unsigned long)1E15 + sn_rtc_cycles_per_second / - 2) / sn_rtc_cycles_per_second; - - if (request_irq(SGI_MMTIMER_VECTOR, mmtimer_interrupt, IRQF_PERCPU, MMTIMER_NAME, NULL)) { - printk(KERN_WARNING "%s: unable to allocate interrupt.", - MMTIMER_NAME); - goto out1; - } - - if (misc_register(&mmtimer_miscdev)) { - printk(KERN_ERR "%s: failed to register device\n", - MMTIMER_NAME); - goto out2; - } - - /* Get max numbered node, calculate slots needed */ - for_each_online_node(node) { - maxn = node; - } - maxn++; - - /* Allocate list of node ptrs to mmtimer_t's */ - timers = kzalloc(sizeof(struct mmtimer_node)*maxn, GFP_KERNEL); - if (!timers) { - printk(KERN_ERR "%s: failed to allocate memory for device\n", - MMTIMER_NAME); - goto out3; - } - - /* Initialize struct mmtimer's for each online node */ - for_each_online_node(node) { - spin_lock_init(&timers[node].lock); - tasklet_init(&timers[node].tasklet, mmtimer_tasklet, - (unsigned long) node); - } - - sgi_clock_period = NSEC_PER_SEC / sn_rtc_cycles_per_second; - posix_timers_register_clock(CLOCK_SGI_CYCLE, &sgi_clock); - - printk(KERN_INFO "%s: v%s, %ld MHz\n", MMTIMER_DESC, MMTIMER_VERSION, - sn_rtc_cycles_per_second/(unsigned long)1E6); - - return 0; - -out3: - misc_deregister(&mmtimer_miscdev); -out2: - free_irq(SGI_MMTIMER_VECTOR, NULL); -out1: - return -1; -} - -module_init(mmtimer_init); diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index d4dbd8d8e524..382c864814d9 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -374,7 +374,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf, rc = write_sync_reg(SCR_HOST_TO_READER_START, dev); if (rc <= 0) { - DEBUGP(5, dev, "write_sync_reg c=%.2Zx\n", rc); + DEBUGP(5, dev, "write_sync_reg c=%.2zx\n", rc); DEBUGP(2, dev, "<- cm4040_write (failed)\n"); if (rc == -ERESTARTSYS) return rc; @@ -387,7 +387,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf, for (i = 0; i < bytes_to_write; i++) { rc = wait_for_bulk_out_ready(dev); if (rc <= 0) { - DEBUGP(5, dev, "wait_for_bulk_out_ready rc=%.2Zx\n", + DEBUGP(5, dev, "wait_for_bulk_out_ready rc=%.2zx\n", rc); DEBUGP(2, dev, "<- cm4040_write (failed)\n"); if (rc == -ERESTARTSYS) @@ -403,7 +403,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf, rc = write_sync_reg(SCR_HOST_TO_READER_DONE, dev); if (rc <= 0) { - DEBUGP(5, dev, "write_sync_reg c=%.2Zx\n", rc); + DEBUGP(5, dev, "write_sync_reg c=%.2zx\n", rc); DEBUGP(2, dev, "<- cm4040_write (failed)\n"); if (rc == -ERESTARTSYS) return rc; diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index d136db1a10f0..62be953e5fb0 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -4235,7 +4235,7 @@ static void hdlcdev_rx(MGSLPC_INFO *info, char *buf, int size) return; } - memcpy(skb_put(skb, size), buf, size); + skb_put_data(skb, buf, size); skb->protocol = hdlc_type_trans(skb, dev); diff --git a/drivers/char/random.c b/drivers/char/random.c index 0ab024918907..afa3ce7d3e72 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1,6 +1,9 @@ /* * random.c -- A strong random number generator * + * Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All + * Rights Reserved. + * * Copyright Matt Mackall <mpm@selenic.com>, 2003, 2004, 2005 * * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998, 1999. All @@ -285,7 +288,6 @@ #define SEC_XFER_SIZE 512 #define EXTRACT_SIZE 10 -#define DEBUG_RANDOM_BOOT 0 #define LONGS(x) (((x) + sizeof(unsigned long) - 1)/sizeof(unsigned long)) @@ -434,6 +436,7 @@ static void _extract_crng(struct crng_state *crng, static void _crng_backtrack_protect(struct crng_state *crng, __u8 tmp[CHACHA20_BLOCK_SIZE], int used); static void process_random_ready_list(void); +static void _get_random_bytes(void *buf, int nbytes); /********************************************************************** * @@ -762,6 +765,8 @@ static DECLARE_WAIT_QUEUE_HEAD(crng_init_wait); static struct crng_state **crng_node_pool __read_mostly; #endif +static void invalidate_batched_entropy(void); + static void crng_initialize(struct crng_state *crng) { int i; @@ -772,7 +777,7 @@ static void crng_initialize(struct crng_state *crng) _extract_entropy(&input_pool, &crng->state[4], sizeof(__u32) * 12, 0); else - get_random_bytes(&crng->state[4], sizeof(__u32) * 12); + _get_random_bytes(&crng->state[4], sizeof(__u32) * 12); for (i = 4; i < 16; i++) { if (!arch_get_random_seed_long(&rv) && !arch_get_random_long(&rv)) @@ -798,12 +803,13 @@ static int crng_fast_load(const char *cp, size_t len) p[crng_init_cnt % CHACHA20_KEY_SIZE] ^= *cp; cp++; crng_init_cnt++; len--; } + spin_unlock_irqrestore(&primary_crng.lock, flags); if (crng_init_cnt >= CRNG_INIT_CNT_THRESH) { + invalidate_batched_entropy(); crng_init = 1; wake_up_interruptible(&crng_init_wait); pr_notice("random: fast init done\n"); } - spin_unlock_irqrestore(&primary_crng.lock, flags); return 1; } @@ -835,18 +841,14 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r) } memzero_explicit(&buf, sizeof(buf)); crng->init_time = jiffies; + spin_unlock_irqrestore(&primary_crng.lock, flags); if (crng == &primary_crng && crng_init < 2) { + invalidate_batched_entropy(); crng_init = 2; process_random_ready_list(); wake_up_interruptible(&crng_init_wait); pr_notice("random: crng init done\n"); } - spin_unlock_irqrestore(&primary_crng.lock, flags); -} - -static inline void crng_wait_ready(void) -{ - wait_event_interruptible(crng_init_wait, crng_ready()); } static void _extract_crng(struct crng_state *crng, @@ -980,6 +982,11 @@ void add_device_randomness(const void *buf, unsigned int size) unsigned long time = random_get_entropy() ^ jiffies; unsigned long flags; + if (!crng_ready()) { + crng_fast_load(buf, size); + return; + } + trace_add_device_randomness(size, _RET_IP_); spin_lock_irqsave(&input_pool.lock, flags); _mix_pool_bytes(&input_pool, buf, size); @@ -1097,12 +1104,16 @@ static void add_interrupt_bench(cycles_t start) static __u32 get_reg(struct fast_pool *f, struct pt_regs *regs) { __u32 *ptr = (__u32 *) regs; + unsigned int idx; if (regs == NULL) return 0; - if (f->reg_idx >= sizeof(struct pt_regs) / sizeof(__u32)) - f->reg_idx = 0; - return *(ptr + f->reg_idx++); + idx = READ_ONCE(f->reg_idx); + if (idx >= sizeof(struct pt_regs) / sizeof(__u32)) + idx = 0; + ptr += idx++; + WRITE_ONCE(f->reg_idx, idx); + return *ptr; } void add_interrupt_randomness(int irq, int irq_flags) @@ -1461,22 +1472,44 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf, return ret; } +#define warn_unseeded_randomness(previous) \ + _warn_unseeded_randomness(__func__, (void *) _RET_IP_, (previous)) + +static void _warn_unseeded_randomness(const char *func_name, void *caller, + void **previous) +{ +#ifdef CONFIG_WARN_ALL_UNSEEDED_RANDOM + const bool print_once = false; +#else + static bool print_once __read_mostly; +#endif + + if (print_once || + crng_ready() || + (previous && (caller == READ_ONCE(*previous)))) + return; + WRITE_ONCE(*previous, caller); +#ifndef CONFIG_WARN_ALL_UNSEEDED_RANDOM + print_once = true; +#endif + pr_notice("random: %s called from %pF with crng_init=%d\n", + func_name, caller, crng_init); +} + /* * This function is the exported kernel interface. It returns some * number of good random numbers, suitable for key generation, seeding * TCP sequence numbers, etc. It does not rely on the hardware random * number generator. For random bytes direct from the hardware RNG - * (when available), use get_random_bytes_arch(). + * (when available), use get_random_bytes_arch(). In order to ensure + * that the randomness provided by this function is okay, the function + * wait_for_random_bytes() should be called and return 0 at least once + * at any point prior. */ -void get_random_bytes(void *buf, int nbytes) +static void _get_random_bytes(void *buf, int nbytes) { __u8 tmp[CHACHA20_BLOCK_SIZE]; -#if DEBUG_RANDOM_BOOT > 0 - if (!crng_ready()) - printk(KERN_NOTICE "random: %pF get_random_bytes called " - "with crng_init = %d\n", (void *) _RET_IP_, crng_init); -#endif trace_get_random_bytes(nbytes, _RET_IP_); while (nbytes >= CHACHA20_BLOCK_SIZE) { @@ -1493,9 +1526,35 @@ void get_random_bytes(void *buf, int nbytes) crng_backtrack_protect(tmp, CHACHA20_BLOCK_SIZE); memzero_explicit(tmp, sizeof(tmp)); } + +void get_random_bytes(void *buf, int nbytes) +{ + static void *previous; + + warn_unseeded_randomness(&previous); + _get_random_bytes(buf, nbytes); +} EXPORT_SYMBOL(get_random_bytes); /* + * Wait for the urandom pool to be seeded and thus guaranteed to supply + * cryptographically secure random numbers. This applies to: the /dev/urandom + * device, the get_random_bytes function, and the get_random_{u32,u64,int,long} + * family of functions. Using any of these functions without first calling + * this function forfeits the guarantee of security. + * + * Returns: 0 if the urandom pool has been seeded. + * -ERESTARTSYS if the function was interrupted by a signal. + */ +int wait_for_random_bytes(void) +{ + if (likely(crng_ready())) + return 0; + return wait_event_interruptible(crng_init_wait, crng_ready()); +} +EXPORT_SYMBOL(wait_for_random_bytes); + +/* * Add a callback function that will be invoked when the nonblocking * pool is initialised. * @@ -1849,6 +1908,8 @@ const struct file_operations urandom_fops = { SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count, unsigned int, flags) { + int ret; + if (flags & ~(GRND_NONBLOCK|GRND_RANDOM)) return -EINVAL; @@ -1861,9 +1922,9 @@ SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count, if (!crng_ready()) { if (flags & GRND_NONBLOCK) return -EAGAIN; - crng_wait_ready(); - if (signal_pending(current)) - return -ERESTARTSYS; + ret = wait_for_random_bytes(); + if (unlikely(ret)) + return ret; } return urandom_read(NULL, buf, count, NULL); } @@ -2019,17 +2080,24 @@ struct batched_entropy { }; unsigned int position; }; +static rwlock_t batched_entropy_reset_lock = __RW_LOCK_UNLOCKED(batched_entropy_reset_lock); /* * Get a random word for internal kernel use only. The quality of the random * number is either as good as RDRAND or as good as /dev/urandom, with the - * goal of being quite fast and not depleting entropy. + * goal of being quite fast and not depleting entropy. In order to ensure + * that the randomness provided by this function is okay, the function + * wait_for_random_bytes() should be called and return 0 at least once + * at any point prior. */ static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_u64); u64 get_random_u64(void) { u64 ret; + bool use_lock; + unsigned long flags = 0; struct batched_entropy *batch; + static void *previous; #if BITS_PER_LONG == 64 if (arch_get_random_long((unsigned long *)&ret)) @@ -2040,12 +2108,19 @@ u64 get_random_u64(void) return ret; #endif + warn_unseeded_randomness(&previous); + + use_lock = READ_ONCE(crng_init) < 2; batch = &get_cpu_var(batched_entropy_u64); + if (use_lock) + read_lock_irqsave(&batched_entropy_reset_lock, flags); if (batch->position % ARRAY_SIZE(batch->entropy_u64) == 0) { extract_crng((u8 *)batch->entropy_u64); batch->position = 0; } ret = batch->entropy_u64[batch->position++]; + if (use_lock) + read_unlock_irqrestore(&batched_entropy_reset_lock, flags); put_cpu_var(batched_entropy_u64); return ret; } @@ -2055,22 +2130,49 @@ static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_u32); u32 get_random_u32(void) { u32 ret; + bool use_lock; + unsigned long flags = 0; struct batched_entropy *batch; + static void *previous; if (arch_get_random_int(&ret)) return ret; + warn_unseeded_randomness(&previous); + + use_lock = READ_ONCE(crng_init) < 2; batch = &get_cpu_var(batched_entropy_u32); + if (use_lock) + read_lock_irqsave(&batched_entropy_reset_lock, flags); if (batch->position % ARRAY_SIZE(batch->entropy_u32) == 0) { extract_crng((u8 *)batch->entropy_u32); batch->position = 0; } ret = batch->entropy_u32[batch->position++]; + if (use_lock) + read_unlock_irqrestore(&batched_entropy_reset_lock, flags); put_cpu_var(batched_entropy_u32); return ret; } EXPORT_SYMBOL(get_random_u32); +/* It's important to invalidate all potential batched entropy that might + * be stored before the crng is initialized, which we can do lazily by + * simply resetting the counter to zero so that it's re-extracted on the + * next usage. */ +static void invalidate_batched_entropy(void) +{ + int cpu; + unsigned long flags; + + write_lock_irqsave(&batched_entropy_reset_lock, flags); + for_each_possible_cpu (cpu) { + per_cpu_ptr(&batched_entropy_u32, cpu)->position = 0; + per_cpu_ptr(&batched_entropy_u64, cpu)->position = 0; + } + write_unlock_irqrestore(&batched_entropy_reset_lock, flags); +} + /** * randomize_page - Generate a random, page aligned address * @start: The smallest acceptable address the caller will take. diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 322b8a51ffc6..67ec9d3d04f5 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -143,6 +143,39 @@ static void tpm_devs_release(struct device *dev) } /** + * tpm_class_shutdown() - prepare the TPM device for loss of power. + * @dev: device to which the chip is associated. + * + * Issues a TPM2_Shutdown command prior to loss of power, as required by the + * TPM 2.0 spec. + * Then, calls bus- and device- specific shutdown code. + * + * XXX: This codepath relies on the fact that sysfs is not enabled for + * TPM2: sysfs uses an implicit lock on chip->ops, so this could race if TPM2 + * has sysfs support enabled before TPM sysfs's implicit locking is fixed. + */ +static int tpm_class_shutdown(struct device *dev) +{ + struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev); + + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + down_write(&chip->ops_sem); + tpm2_shutdown(chip, TPM2_SU_CLEAR); + chip->ops = NULL; + up_write(&chip->ops_sem); + } + /* Allow bus- and device-specific code to run. Note: since chip->ops + * is NULL, more-specific shutdown code will not be able to issue TPM + * commands. + */ + if (dev->bus && dev->bus->shutdown) + dev->bus->shutdown(dev); + else if (dev->driver && dev->driver->shutdown) + dev->driver->shutdown(dev); + return 0; +} + +/** * tpm_chip_alloc() - allocate a new struct tpm_chip instance * @pdev: device to which the chip is associated * At this point pdev mst be initialized, but does not have to @@ -181,6 +214,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev, device_initialize(&chip->devs); chip->dev.class = tpm_class; + chip->dev.class->shutdown = tpm_class_shutdown; chip->dev.release = tpm_dev_release; chip->dev.parent = pdev; chip->dev.groups = chip->groups; diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index d2b4df6d9894..fe597e6c55c4 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -540,6 +540,47 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, } EXPORT_SYMBOL_GPL(tpm_transmit_cmd); +#define TPM_ORD_STARTUP 153 +#define TPM_ST_CLEAR 1 + +/** + * tpm_startup - turn on the TPM + * @chip: TPM chip to use + * + * Normally the firmware should start the TPM. This function is provided as a + * workaround if this does not happen. A legal case for this could be for + * example when a TPM emulator is used. + * + * Return: same as tpm_transmit_cmd() + */ +int tpm_startup(struct tpm_chip *chip) +{ + struct tpm_buf buf; + int rc; + + dev_info(&chip->dev, "starting up the TPM manually\n"); + + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP); + if (rc < 0) + return rc; + + tpm_buf_append_u16(&buf, TPM2_SU_CLEAR); + } else { + rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_STARTUP); + if (rc < 0) + return rc; + + tpm_buf_append_u16(&buf, TPM_ST_CLEAR); + } + + rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, + "attempting to start the TPM"); + + tpm_buf_destroy(&buf); + return rc; +} + #define TPM_DIGEST_SIZE 20 #define TPM_RET_CODE_IDX 6 #define TPM_INTERNAL_RESULT_SIZE 200 @@ -586,27 +627,6 @@ ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, } EXPORT_SYMBOL_GPL(tpm_getcap); -#define TPM_ORD_STARTUP 153 -#define TPM_ST_CLEAR cpu_to_be16(1) -#define TPM_ST_STATE cpu_to_be16(2) -#define TPM_ST_DEACTIVATED cpu_to_be16(3) -static const struct tpm_input_header tpm_startup_header = { - .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), - .length = cpu_to_be32(12), - .ordinal = cpu_to_be32(TPM_ORD_STARTUP) -}; - -static int tpm_startup(struct tpm_chip *chip, __be16 startup_type) -{ - struct tpm_cmd_t start_cmd; - start_cmd.header.in = tpm_startup_header; - - start_cmd.params.startup_in.startup_type = startup_type; - return tpm_transmit_cmd(chip, NULL, &start_cmd, - TPM_INTERNAL_RESULT_SIZE, 0, - 0, "attempting to start the TPM"); -} - int tpm_get_timeouts(struct tpm_chip *chip) { cap_t cap; @@ -636,10 +656,7 @@ int tpm_get_timeouts(struct tpm_chip *chip) rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, NULL, sizeof(cap.timeout)); if (rc == TPM_ERR_INVALID_POSTINIT) { - /* The TPM is not started, we are the first to talk to it. - Execute a startup command. */ - dev_info(&chip->dev, "Issuing TPM_STARTUP\n"); - if (tpm_startup(chip, TPM_ST_CLEAR)) + if (tpm_startup(chip)) return rc; rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, @@ -1102,6 +1119,9 @@ int tpm_pm_suspend(struct device *dev) if (chip == NULL) return -ENODEV; + if (chip->flags & TPM_CHIP_FLAG_ALWAYS_POWERED) + return 0; + if (chip->flags & TPM_CHIP_FLAG_TPM2) { tpm2_shutdown(chip, TPM2_SU_STATE); return 0; diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index 4bd0997cfa2d..86f38d239476 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -36,9 +36,10 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, ssize_t err; int i, rc; char *str = buf; - struct tpm_chip *chip = to_tpm_chip(dev); + memset(&tpm_cmd, 0, sizeof(tpm_cmd)); + tpm_cmd.header.in = tpm_readpubek_header; err = tpm_transmit_cmd(chip, NULL, &tpm_cmd, READ_PUBEK_RESULT_SIZE, READ_PUBEK_RESULT_MIN_BODY_SIZE, 0, @@ -294,6 +295,9 @@ static const struct attribute_group tpm_dev_group = { void tpm_sysfs_add_device(struct tpm_chip *chip) { + /* XXX: If you wish to remove this restriction, you must first update + * tpm_sysfs to explicitly lock chip->ops. + */ if (chip->flags & TPM_CHIP_FLAG_TPM2) return; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 1df0521138d3..04fbff2edbf3 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -36,6 +36,10 @@ #include <linux/highmem.h> #include <crypto/hash_info.h> +#ifdef CONFIG_X86 +#include <asm/intel-family.h> +#endif + enum tpm_const { TPM_MINOR = 224, /* officially assigned */ TPM_BUFSIZE = 4096, @@ -170,6 +174,7 @@ enum tpm_chip_flags { TPM_CHIP_FLAG_IRQ = BIT(2), TPM_CHIP_FLAG_VIRTUAL = BIT(3), TPM_CHIP_FLAG_HAVE_TIMEOUTS = BIT(4), + TPM_CHIP_FLAG_ALWAYS_POWERED = BIT(5), }; struct tpm_bios_log { @@ -378,10 +383,6 @@ struct tpm_getrandom_in { __be32 num_bytes; } __packed; -struct tpm_startup_in { - __be16 startup_type; -} __packed; - typedef union { struct tpm_readpubek_params_out readpubek_out; u8 readpubek_out_buffer[sizeof(struct tpm_readpubek_params_out)]; @@ -389,7 +390,6 @@ typedef union { struct tpm_pcrread_out pcrread_out; struct tpm_getrandom_in getrandom_in; struct tpm_getrandom_out getrandom_out; - struct tpm_startup_in startup_in; } tpm_cmd_params; struct tpm_cmd_t { @@ -515,6 +515,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, const void *buf, size_t bufsiz, size_t min_rsp_body_length, unsigned int flags, const char *desc); +int tpm_startup(struct tpm_chip *chip); ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, const char *desc, size_t min_cap_length); int tpm_get_timeouts(struct tpm_chip *); diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 3a9964326279..f7f34b2aa981 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -35,24 +35,6 @@ struct tpm2_self_test_in { u8 full_test; } __packed; -struct tpm2_pcr_read_in { - __be32 pcr_selects_cnt; - __be16 hash_alg; - u8 pcr_select_size; - u8 pcr_select[TPM2_PCR_SELECT_MIN]; -} __packed; - -struct tpm2_pcr_read_out { - __be32 update_cnt; - __be32 pcr_selects_cnt; - __be16 hash_alg; - u8 pcr_select_size; - u8 pcr_select[TPM2_PCR_SELECT_MIN]; - __be32 digests_cnt; - __be16 digest_size; - u8 digest[TPM_DIGEST_SIZE]; -} __packed; - struct tpm2_get_tpm_pt_in { __be32 cap_id; __be32 property_id; @@ -79,8 +61,6 @@ struct tpm2_get_random_out { union tpm2_cmd_params { struct tpm2_startup_in startup_in; struct tpm2_self_test_in selftest_in; - struct tpm2_pcr_read_in pcrread_in; - struct tpm2_pcr_read_out pcrread_out; struct tpm2_get_tpm_pt_in get_tpm_pt_in; struct tpm2_get_tpm_pt_out get_tpm_pt_out; struct tpm2_get_random_in getrandom_in; @@ -227,18 +207,16 @@ static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = { TPM_UNDEFINED /* 18f */ }; -#define TPM2_PCR_READ_IN_SIZE \ - (sizeof(struct tpm_input_header) + \ - sizeof(struct tpm2_pcr_read_in)) - -#define TPM2_PCR_READ_RESP_BODY_SIZE \ - sizeof(struct tpm2_pcr_read_out) - -static const struct tpm_input_header tpm2_pcrread_header = { - .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), - .length = cpu_to_be32(TPM2_PCR_READ_IN_SIZE), - .ordinal = cpu_to_be32(TPM2_CC_PCR_READ) -}; +struct tpm2_pcr_read_out { + __be32 update_cnt; + __be32 pcr_selects_cnt; + __be16 hash_alg; + u8 pcr_select_size; + u8 pcr_select[TPM2_PCR_SELECT_MIN]; + __be32 digests_cnt; + __be16 digest_size; + u8 digest[]; +} __packed; /** * tpm2_pcr_read() - read a PCR value @@ -251,29 +229,33 @@ static const struct tpm_input_header tpm2_pcrread_header = { int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) { int rc; - struct tpm2_cmd cmd; - u8 *buf; + struct tpm_buf buf; + struct tpm2_pcr_read_out *out; + u8 pcr_select[TPM2_PCR_SELECT_MIN] = {0}; if (pcr_idx >= TPM2_PLATFORM_PCR) return -EINVAL; - cmd.header.in = tpm2_pcrread_header; - cmd.params.pcrread_in.pcr_selects_cnt = cpu_to_be32(1); - cmd.params.pcrread_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1); - cmd.params.pcrread_in.pcr_select_size = TPM2_PCR_SELECT_MIN; + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ); + if (rc) + return rc; + + pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7); - memset(cmd.params.pcrread_in.pcr_select, 0, - sizeof(cmd.params.pcrread_in.pcr_select)); - cmd.params.pcrread_in.pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7); + tpm_buf_append_u32(&buf, 1); + tpm_buf_append_u16(&buf, TPM2_ALG_SHA1); + tpm_buf_append_u8(&buf, TPM2_PCR_SELECT_MIN); + tpm_buf_append(&buf, (const unsigned char *)pcr_select, + sizeof(pcr_select)); - rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), - TPM2_PCR_READ_RESP_BODY_SIZE, - 0, "attempting to read a pcr value"); - if (rc == 0) { - buf = cmd.params.pcrread_out.digest; - memcpy(res_buf, buf, TPM_DIGEST_SIZE); + rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, + res_buf ? "attempting to read a pcr value" : NULL); + if (rc == 0 && res_buf) { + out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE]; + memcpy(res_buf, out->digest, SHA1_DIGEST_SIZE); } + tpm_buf_destroy(&buf); return rc; } @@ -779,36 +761,6 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, } EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt); -#define TPM2_STARTUP_IN_SIZE \ - (sizeof(struct tpm_input_header) + \ - sizeof(struct tpm2_startup_in)) - -static const struct tpm_input_header tpm2_startup_header = { - .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), - .length = cpu_to_be32(TPM2_STARTUP_IN_SIZE), - .ordinal = cpu_to_be32(TPM2_CC_STARTUP) -}; - -/** - * tpm2_startup() - send startup command to the TPM chip - * - * @chip: TPM chip to use. - * @startup_type: startup type. The value is either - * TPM_SU_CLEAR or TPM_SU_STATE. - * - * Return: Same as with tpm_transmit_cmd. - */ -static int tpm2_startup(struct tpm_chip *chip, u16 startup_type) -{ - struct tpm2_cmd cmd; - - cmd.header.in = tpm2_startup_header; - - cmd.params.startup_in.startup_type = cpu_to_be16(startup_type); - return tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0, - "attempting to start the TPM"); -} - #define TPM2_SHUTDOWN_IN_SIZE \ (sizeof(struct tpm_input_header) + \ sizeof(struct tpm2_startup_in)) @@ -928,7 +880,6 @@ static int tpm2_do_selftest(struct tpm_chip *chip) unsigned int loops; unsigned int delay_msec = 100; unsigned long duration; - struct tpm2_cmd cmd; int i; duration = tpm2_calc_ordinal_duration(chip, TPM2_CC_SELF_TEST); @@ -941,20 +892,10 @@ static int tpm2_do_selftest(struct tpm_chip *chip) for (i = 0; i < loops; i++) { /* Attempt to read a PCR value */ - cmd.header.in = tpm2_pcrread_header; - cmd.params.pcrread_in.pcr_selects_cnt = cpu_to_be32(1); - cmd.params.pcrread_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1); - cmd.params.pcrread_in.pcr_select_size = TPM2_PCR_SELECT_MIN; - cmd.params.pcrread_in.pcr_select[0] = 0x01; - cmd.params.pcrread_in.pcr_select[1] = 0x00; - cmd.params.pcrread_in.pcr_select[2] = 0x00; - - rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0, - NULL); + rc = tpm2_pcr_read(chip, 0, NULL); if (rc < 0) break; - rc = be32_to_cpu(cmd.header.out.return_code); if (rc != TPM2_RC_TESTING) break; @@ -1150,7 +1091,7 @@ int tpm2_auto_startup(struct tpm_chip *chip) } if (rc == TPM2_RC_INITIALIZE) { - rc = tpm2_startup(chip, TPM2_SU_CLEAR); + rc = tpm_startup(chip); if (rc) goto out; diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index b917b9d5f710..a4ac63a21d8a 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -27,10 +27,9 @@ #define ACPI_SIG_TPM2 "TPM2" -static const u8 CRB_ACPI_START_UUID[] = { - /* 0000 */ 0xAB, 0x6C, 0xBF, 0x6B, 0x63, 0x54, 0x14, 0x47, - /* 0008 */ 0xB7, 0xCD, 0xF0, 0x20, 0x3C, 0x03, 0x68, 0xD4 -}; +static const guid_t crb_acpi_start_guid = + GUID_INIT(0x6BBF6CAB, 0x5463, 0x4714, + 0xB7, 0xCD, 0xF0, 0x20, 0x3C, 0x03, 0x68, 0xD4); enum crb_defaults { CRB_ACPI_START_REVISION_ID = 1, @@ -266,7 +265,7 @@ static int crb_do_acpi_start(struct tpm_chip *chip) int rc; obj = acpi_evaluate_dsm(chip->acpi_dev_handle, - CRB_ACPI_START_UUID, + &crb_acpi_start_guid, CRB_ACPI_START_REVISION_ID, CRB_ACPI_START_INDEX, NULL); @@ -515,11 +514,12 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, goto out; } - priv->cmd_size = cmd_size; - priv->rsp = priv->cmd; out: + if (!ret) + priv->cmd_size = cmd_size; + crb_go_idle(dev, priv); return ret; @@ -564,12 +564,12 @@ static int crb_acpi_add(struct acpi_device *device) sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) priv->flags |= CRB_FL_ACPI_START; - if (sm == ACPI_TPM2_COMMAND_BUFFER_WITH_SMC) { + if (sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC) { if (buf->header.length < (sizeof(*buf) + sizeof(*crb_smc))) { dev_err(dev, FW_BUG "TPM2 ACPI table has wrong size %u for start method type %d\n", buf->header.length, - ACPI_TPM2_COMMAND_BUFFER_WITH_SMC); + ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC); return -EINVAL; } crb_smc = ACPI_ADD_PTR(struct tpm2_crb_smc, buf, sizeof(*buf)); diff --git a/drivers/char/tpm/tpm_of.c b/drivers/char/tpm/tpm_of.c index de57d4ac8901..aadb7f464076 100644 --- a/drivers/char/tpm/tpm_of.c +++ b/drivers/char/tpm/tpm_of.c @@ -36,6 +36,9 @@ int tpm_read_log_of(struct tpm_chip *chip) else return -ENODEV; + if (of_property_read_bool(np, "powered-while-suspended")) + chip->flags |= TPM_CHIP_FLAG_ALWAYS_POWERED; + sizep = of_get_property(np, "linux,sml-size", NULL); basep = of_get_property(np, "linux,sml-base", NULL); if (sizep == NULL && basep == NULL) diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index 692a2c6ae036..86dd8521feef 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -32,20 +32,16 @@ #define PPI_VS_REQ_START 128 #define PPI_VS_REQ_END 255 -static const u8 tpm_ppi_uuid[] = { - 0xA6, 0xFA, 0xDD, 0x3D, - 0x1B, 0x36, - 0xB4, 0x4E, - 0xA4, 0x24, - 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53 -}; +static const guid_t tpm_ppi_guid = + GUID_INIT(0x3DDDFAA6, 0x361B, 0x4EB4, + 0xA4, 0x24, 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53); static inline union acpi_object * tpm_eval_dsm(acpi_handle ppi_handle, int func, acpi_object_type type, union acpi_object *argv4) { BUG_ON(!ppi_handle); - return acpi_evaluate_dsm_typed(ppi_handle, tpm_ppi_uuid, + return acpi_evaluate_dsm_typed(ppi_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID, func, argv4, type); } @@ -107,7 +103,7 @@ static ssize_t tpm_store_ppi_request(struct device *dev, * is updated with function index from SUBREQ to SUBREQ2 since PPI * version 1.1 */ - if (acpi_check_dsm(chip->acpi_dev_handle, tpm_ppi_uuid, + if (acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_SUBREQ2)) func = TPM_PPI_FN_SUBREQ2; @@ -268,7 +264,7 @@ static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start, "User not required", }; - if (!acpi_check_dsm(dev_handle, tpm_ppi_uuid, TPM_PPI_REVISION_ID, + if (!acpi_check_dsm(dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_GETOPR)) return -EPERM; @@ -341,12 +337,12 @@ void tpm_add_ppi(struct tpm_chip *chip) if (!chip->acpi_dev_handle) return; - if (!acpi_check_dsm(chip->acpi_dev_handle, tpm_ppi_uuid, + if (!acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_VERSION)) return; /* Cache PPI version string. */ - obj = acpi_evaluate_dsm_typed(chip->acpi_dev_handle, tpm_ppi_uuid, + obj = acpi_evaluate_dsm_typed(chip->acpi_dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID, TPM_PPI_FN_VERSION, NULL, ACPI_TYPE_STRING); if (obj) { diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index b14d4aa97af8..7e55aa9ce680 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -132,13 +132,93 @@ static int check_acpi_tpm2(struct device *dev) } #endif +#ifdef CONFIG_X86 +#define INTEL_LEGACY_BLK_BASE_ADDR 0xFED08000 +#define ILB_REMAP_SIZE 0x100 +#define LPC_CNTRL_REG_OFFSET 0x84 +#define LPC_CLKRUN_EN (1 << 2) + +static void __iomem *ilb_base_addr; + +static inline bool is_bsw(void) +{ + return ((boot_cpu_data.x86_model == INTEL_FAM6_ATOM_AIRMONT) ? 1 : 0); +} + +/** + * tpm_platform_begin_xfer() - clear LPC CLKRUN_EN i.e. clocks will be running + */ +static void tpm_platform_begin_xfer(void) +{ + u32 clkrun_val; + + if (!is_bsw()) + return; + + clkrun_val = ioread32(ilb_base_addr + LPC_CNTRL_REG_OFFSET); + + /* Disable LPC CLKRUN# */ + clkrun_val &= ~LPC_CLKRUN_EN; + iowrite32(clkrun_val, ilb_base_addr + LPC_CNTRL_REG_OFFSET); + + /* + * Write any random value on port 0x80 which is on LPC, to make + * sure LPC clock is running before sending any TPM command. + */ + outb(0xCC, 0x80); + +} + +/** + * tpm_platform_end_xfer() - set LPC CLKRUN_EN i.e. clocks can be turned off + */ +static void tpm_platform_end_xfer(void) +{ + u32 clkrun_val; + + if (!is_bsw()) + return; + + clkrun_val = ioread32(ilb_base_addr + LPC_CNTRL_REG_OFFSET); + + /* Enable LPC CLKRUN# */ + clkrun_val |= LPC_CLKRUN_EN; + iowrite32(clkrun_val, ilb_base_addr + LPC_CNTRL_REG_OFFSET); + + /* + * Write any random value on port 0x80 which is on LPC, to make + * sure LPC clock is running before sending any TPM command. + */ + outb(0xCC, 0x80); + +} +#else +static inline bool is_bsw(void) +{ + return false; +} + +static void tpm_platform_begin_xfer(void) +{ +} + +static void tpm_platform_end_xfer(void) +{ +} +#endif + static int tpm_tcg_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len, u8 *result) { struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data); + tpm_platform_begin_xfer(); + while (len--) *result++ = ioread8(phy->iobase + addr); + + tpm_platform_end_xfer(); + return 0; } @@ -147,8 +227,13 @@ static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len, { struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data); + tpm_platform_begin_xfer(); + while (len--) iowrite8(*value++, phy->iobase + addr); + + tpm_platform_end_xfer(); + return 0; } @@ -156,7 +241,12 @@ static int tpm_tcg_read16(struct tpm_tis_data *data, u32 addr, u16 *result) { struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data); + tpm_platform_begin_xfer(); + *result = ioread16(phy->iobase + addr); + + tpm_platform_end_xfer(); + return 0; } @@ -164,7 +254,12 @@ static int tpm_tcg_read32(struct tpm_tis_data *data, u32 addr, u32 *result) { struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data); + tpm_platform_begin_xfer(); + *result = ioread32(phy->iobase + addr); + + tpm_platform_end_xfer(); + return 0; } @@ -172,7 +267,12 @@ static int tpm_tcg_write32(struct tpm_tis_data *data, u32 addr, u32 value) { struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data); + tpm_platform_begin_xfer(); + iowrite32(value, phy->iobase + addr); + + tpm_platform_end_xfer(); + return 0; } @@ -360,6 +460,11 @@ static int __init init_tis(void) if (rc) goto err_force; +#ifdef CONFIG_X86 + if (is_bsw()) + ilb_base_addr = ioremap(INTEL_LEGACY_BLK_BASE_ADDR, + ILB_REMAP_SIZE); +#endif rc = platform_driver_register(&tis_drv); if (rc) goto err_platform; @@ -378,6 +483,10 @@ err_pnp: err_platform: if (force_pdev) platform_device_unregister(force_pdev); +#ifdef CONFIG_X86 + if (is_bsw()) + iounmap(ilb_base_addr); +#endif err_force: return rc; } @@ -387,6 +496,10 @@ static void __exit cleanup_tis(void) pnp_unregister_driver(&tis_pnp_driver); platform_driver_unregister(&tis_drv); +#ifdef CONFIG_X86 + if (is_bsw()) + iounmap(ilb_base_addr); +#endif if (force_pdev) platform_device_unregister(force_pdev); } |