mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 17:57:38 -04:00
Merge branch 'master' into for-next
Sync up with Linus' tree to be able to apply Cesar's patch against newer version of the code. Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
@@ -418,8 +418,8 @@ config APPLICOM
|
||||
If unsure, say N.
|
||||
|
||||
config SONYPI
|
||||
tristate "Sony Vaio Programmable I/O Control Device support (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && X86 && PCI && INPUT && !64BIT
|
||||
tristate "Sony Vaio Programmable I/O Control Device support"
|
||||
depends on X86 && PCI && INPUT && !64BIT
|
||||
---help---
|
||||
This driver enables access to the Sony Programmable I/O Control
|
||||
Device which can be found in many (all ?) Sony Vaio laptops.
|
||||
@@ -566,7 +566,7 @@ source "drivers/char/tpm/Kconfig"
|
||||
|
||||
config TELCLOCK
|
||||
tristate "Telecom clock driver for ATCA SBC"
|
||||
depends on EXPERIMENTAL && X86
|
||||
depends on X86
|
||||
default n
|
||||
help
|
||||
The telecom clock device is specific to the MPCBL0010 and MPCBL0050
|
||||
|
||||
@@ -52,7 +52,6 @@ obj-$(CONFIG_TELCLOCK) += tlclk.o
|
||||
obj-$(CONFIG_MWAVE) += mwave/
|
||||
obj-$(CONFIG_AGP) += agp/
|
||||
obj-$(CONFIG_PCMCIA) += pcmcia/
|
||||
obj-$(CONFIG_IPMI_HANDLER) += ipmi/
|
||||
|
||||
obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
|
||||
obj-$(CONFIG_TCG_TPM) += tpm/
|
||||
|
||||
@@ -84,40 +84,33 @@ static struct _intel_private {
|
||||
#define IS_IRONLAKE intel_private.driver->is_ironlake
|
||||
#define HAS_PGTBL_EN intel_private.driver->has_pgtbl_enable
|
||||
|
||||
int intel_gtt_map_memory(struct page **pages, unsigned int num_entries,
|
||||
struct scatterlist **sg_list, int *num_sg)
|
||||
static int intel_gtt_map_memory(struct page **pages,
|
||||
unsigned int num_entries,
|
||||
struct sg_table *st)
|
||||
{
|
||||
struct sg_table st;
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
if (*sg_list)
|
||||
return 0; /* already mapped (for e.g. resume */
|
||||
|
||||
DBG("try mapping %lu pages\n", (unsigned long)num_entries);
|
||||
|
||||
if (sg_alloc_table(&st, num_entries, GFP_KERNEL))
|
||||
if (sg_alloc_table(st, num_entries, GFP_KERNEL))
|
||||
goto err;
|
||||
|
||||
*sg_list = sg = st.sgl;
|
||||
|
||||
for (i = 0 ; i < num_entries; i++, sg = sg_next(sg))
|
||||
for_each_sg(st->sgl, sg, num_entries, i)
|
||||
sg_set_page(sg, pages[i], PAGE_SIZE, 0);
|
||||
|
||||
*num_sg = pci_map_sg(intel_private.pcidev, *sg_list,
|
||||
num_entries, PCI_DMA_BIDIRECTIONAL);
|
||||
if (unlikely(!*num_sg))
|
||||
if (!pci_map_sg(intel_private.pcidev,
|
||||
st->sgl, st->nents, PCI_DMA_BIDIRECTIONAL))
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
sg_free_table(&st);
|
||||
sg_free_table(st);
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL(intel_gtt_map_memory);
|
||||
|
||||
void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg)
|
||||
static void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg)
|
||||
{
|
||||
struct sg_table st;
|
||||
DBG("try unmapping %lu pages\n", (unsigned long)mem->page_count);
|
||||
@@ -130,7 +123,6 @@ void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg)
|
||||
|
||||
sg_free_table(&st);
|
||||
}
|
||||
EXPORT_SYMBOL(intel_gtt_unmap_memory);
|
||||
|
||||
static void intel_fake_agp_enable(struct agp_bridge_data *bridge, u32 mode)
|
||||
{
|
||||
@@ -674,9 +666,14 @@ static int intel_gtt_init(void)
|
||||
|
||||
gtt_map_size = intel_private.base.gtt_total_entries * 4;
|
||||
|
||||
intel_private.gtt = ioremap(intel_private.gtt_bus_addr,
|
||||
gtt_map_size);
|
||||
if (!intel_private.gtt) {
|
||||
intel_private.gtt = NULL;
|
||||
if (INTEL_GTT_GEN < 6 && INTEL_GTT_GEN > 2)
|
||||
intel_private.gtt = ioremap_wc(intel_private.gtt_bus_addr,
|
||||
gtt_map_size);
|
||||
if (intel_private.gtt == NULL)
|
||||
intel_private.gtt = ioremap(intel_private.gtt_bus_addr,
|
||||
gtt_map_size);
|
||||
if (intel_private.gtt == NULL) {
|
||||
intel_private.driver->cleanup();
|
||||
iounmap(intel_private.registers);
|
||||
return -ENOMEM;
|
||||
@@ -879,8 +876,7 @@ static bool i830_check_flags(unsigned int flags)
|
||||
return false;
|
||||
}
|
||||
|
||||
void intel_gtt_insert_sg_entries(struct scatterlist *sg_list,
|
||||
unsigned int sg_len,
|
||||
void intel_gtt_insert_sg_entries(struct sg_table *st,
|
||||
unsigned int pg_start,
|
||||
unsigned int flags)
|
||||
{
|
||||
@@ -892,12 +888,11 @@ void intel_gtt_insert_sg_entries(struct scatterlist *sg_list,
|
||||
|
||||
/* sg may merge pages, but we have to separate
|
||||
* per-page addr for GTT */
|
||||
for_each_sg(sg_list, sg, sg_len, i) {
|
||||
for_each_sg(st->sgl, sg, st->nents, i) {
|
||||
len = sg_dma_len(sg) >> PAGE_SHIFT;
|
||||
for (m = 0; m < len; m++) {
|
||||
dma_addr_t addr = sg_dma_address(sg) + (m << PAGE_SHIFT);
|
||||
intel_private.driver->write_entry(addr,
|
||||
j, flags);
|
||||
intel_private.driver->write_entry(addr, j, flags);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
@@ -905,8 +900,10 @@ void intel_gtt_insert_sg_entries(struct scatterlist *sg_list,
|
||||
}
|
||||
EXPORT_SYMBOL(intel_gtt_insert_sg_entries);
|
||||
|
||||
void intel_gtt_insert_pages(unsigned int first_entry, unsigned int num_entries,
|
||||
struct page **pages, unsigned int flags)
|
||||
static void intel_gtt_insert_pages(unsigned int first_entry,
|
||||
unsigned int num_entries,
|
||||
struct page **pages,
|
||||
unsigned int flags)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
@@ -917,7 +914,6 @@ void intel_gtt_insert_pages(unsigned int first_entry, unsigned int num_entries,
|
||||
}
|
||||
readl(intel_private.gtt+j-1);
|
||||
}
|
||||
EXPORT_SYMBOL(intel_gtt_insert_pages);
|
||||
|
||||
static int intel_fake_agp_insert_entries(struct agp_memory *mem,
|
||||
off_t pg_start, int type)
|
||||
@@ -953,13 +949,15 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem,
|
||||
global_cache_flush();
|
||||
|
||||
if (intel_private.base.needs_dmar) {
|
||||
ret = intel_gtt_map_memory(mem->pages, mem->page_count,
|
||||
&mem->sg_list, &mem->num_sg);
|
||||
struct sg_table st;
|
||||
|
||||
ret = intel_gtt_map_memory(mem->pages, mem->page_count, &st);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
intel_gtt_insert_sg_entries(mem->sg_list, mem->num_sg,
|
||||
pg_start, type);
|
||||
intel_gtt_insert_sg_entries(&st, pg_start, type);
|
||||
mem->sg_list = st.sgl;
|
||||
mem->num_sg = st.nents;
|
||||
} else
|
||||
intel_gtt_insert_pages(pg_start, mem->page_count, mem->pages,
|
||||
type);
|
||||
|
||||
@@ -289,12 +289,11 @@ static int __devinit agp_sgi_init(void)
|
||||
|
||||
j = 0;
|
||||
list_for_each_entry(info, &tioca_list, ca_list) {
|
||||
struct list_head *tmp;
|
||||
if (list_empty(info->ca_devices))
|
||||
continue;
|
||||
list_for_each(tmp, info->ca_devices) {
|
||||
list_for_each_entry(pdev, info->ca_devices, bus_list) {
|
||||
u8 cap_ptr;
|
||||
pdev = pci_dev_b(tmp);
|
||||
|
||||
if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8))
|
||||
continue;
|
||||
cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
|
||||
|
||||
@@ -74,21 +74,21 @@ static inline void netwinder_ds1620_reset(void)
|
||||
|
||||
static inline void netwinder_lock(unsigned long *flags)
|
||||
{
|
||||
spin_lock_irqsave(&nw_gpio_lock, *flags);
|
||||
raw_spin_lock_irqsave(&nw_gpio_lock, *flags);
|
||||
}
|
||||
|
||||
static inline void netwinder_unlock(unsigned long *flags)
|
||||
{
|
||||
spin_unlock_irqrestore(&nw_gpio_lock, *flags);
|
||||
raw_spin_unlock_irqrestore(&nw_gpio_lock, *flags);
|
||||
}
|
||||
|
||||
static inline void netwinder_set_fan(int i)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&nw_gpio_lock, flags);
|
||||
raw_spin_lock_irqsave(&nw_gpio_lock, flags);
|
||||
nw_gpio_modify_op(GPIO_FAN, i ? GPIO_FAN : 0);
|
||||
spin_unlock_irqrestore(&nw_gpio_lock, flags);
|
||||
raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
|
||||
}
|
||||
|
||||
static inline int netwinder_get_fan(void)
|
||||
|
||||
@@ -289,3 +289,16 @@ config HW_RANDOM_EXYNOS
|
||||
module will be called exynos-rng.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config HW_RANDOM_TPM
|
||||
tristate "TPM HW Random Number Generator support"
|
||||
depends on HW_RANDOM && TCG_TPM
|
||||
default HW_RANDOM
|
||||
---help---
|
||||
This driver provides kernel-side support for the Random Number
|
||||
Generator in the Trusted Platform Module
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called tpm-rng.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
@@ -25,3 +25,4 @@ obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o
|
||||
obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o
|
||||
obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o
|
||||
obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o
|
||||
obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o
|
||||
|
||||
@@ -59,16 +59,21 @@
|
||||
#define RNGA_STATUS_LAST_READ_STATUS 0x00000002
|
||||
#define RNGA_STATUS_SECURITY_VIOLATION 0x00000001
|
||||
|
||||
static struct platform_device *rng_dev;
|
||||
struct mxc_rng {
|
||||
struct device *dev;
|
||||
struct hwrng rng;
|
||||
void __iomem *mem;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static int mxc_rnga_data_present(struct hwrng *rng, int wait)
|
||||
{
|
||||
void __iomem *rng_base = (void __iomem *)rng->priv;
|
||||
int i;
|
||||
struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng);
|
||||
|
||||
for (i = 0; i < 20; i++) {
|
||||
/* how many random numbers are in FIFO? [0-16] */
|
||||
int level = (__raw_readl(rng_base + RNGA_STATUS) &
|
||||
int level = (__raw_readl(mxc_rng->mem + RNGA_STATUS) &
|
||||
RNGA_STATUS_LEVEL_MASK) >> 8;
|
||||
if (level || !wait)
|
||||
return !!level;
|
||||
@@ -81,20 +86,20 @@ static int mxc_rnga_data_read(struct hwrng *rng, u32 * data)
|
||||
{
|
||||
int err;
|
||||
u32 ctrl;
|
||||
void __iomem *rng_base = (void __iomem *)rng->priv;
|
||||
struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng);
|
||||
|
||||
/* retrieve a random number from FIFO */
|
||||
*data = __raw_readl(rng_base + RNGA_OUTPUT_FIFO);
|
||||
*data = __raw_readl(mxc_rng->mem + RNGA_OUTPUT_FIFO);
|
||||
|
||||
/* some error while reading this random number? */
|
||||
err = __raw_readl(rng_base + RNGA_STATUS) & RNGA_STATUS_ERROR_INT;
|
||||
err = __raw_readl(mxc_rng->mem + RNGA_STATUS) & RNGA_STATUS_ERROR_INT;
|
||||
|
||||
/* if error: clear error interrupt, but doesn't return random number */
|
||||
if (err) {
|
||||
dev_dbg(&rng_dev->dev, "Error while reading random number!\n");
|
||||
ctrl = __raw_readl(rng_base + RNGA_CONTROL);
|
||||
dev_dbg(mxc_rng->dev, "Error while reading random number!\n");
|
||||
ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL);
|
||||
__raw_writel(ctrl | RNGA_CONTROL_CLEAR_INT,
|
||||
rng_base + RNGA_CONTROL);
|
||||
mxc_rng->mem + RNGA_CONTROL);
|
||||
return 0;
|
||||
} else
|
||||
return 4;
|
||||
@@ -103,22 +108,22 @@ static int mxc_rnga_data_read(struct hwrng *rng, u32 * data)
|
||||
static int mxc_rnga_init(struct hwrng *rng)
|
||||
{
|
||||
u32 ctrl, osc;
|
||||
void __iomem *rng_base = (void __iomem *)rng->priv;
|
||||
struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng);
|
||||
|
||||
/* wake up */
|
||||
ctrl = __raw_readl(rng_base + RNGA_CONTROL);
|
||||
__raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, rng_base + RNGA_CONTROL);
|
||||
ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL);
|
||||
__raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, mxc_rng->mem + RNGA_CONTROL);
|
||||
|
||||
/* verify if oscillator is working */
|
||||
osc = __raw_readl(rng_base + RNGA_STATUS);
|
||||
osc = __raw_readl(mxc_rng->mem + RNGA_STATUS);
|
||||
if (osc & RNGA_STATUS_OSC_DEAD) {
|
||||
dev_err(&rng_dev->dev, "RNGA Oscillator is dead!\n");
|
||||
dev_err(mxc_rng->dev, "RNGA Oscillator is dead!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* go running */
|
||||
ctrl = __raw_readl(rng_base + RNGA_CONTROL);
|
||||
__raw_writel(ctrl | RNGA_CONTROL_GO, rng_base + RNGA_CONTROL);
|
||||
ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL);
|
||||
__raw_writel(ctrl | RNGA_CONTROL_GO, mxc_rng->mem + RNGA_CONTROL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -126,40 +131,40 @@ static int mxc_rnga_init(struct hwrng *rng)
|
||||
static void mxc_rnga_cleanup(struct hwrng *rng)
|
||||
{
|
||||
u32 ctrl;
|
||||
void __iomem *rng_base = (void __iomem *)rng->priv;
|
||||
struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng);
|
||||
|
||||
ctrl = __raw_readl(rng_base + RNGA_CONTROL);
|
||||
ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL);
|
||||
|
||||
/* stop rnga */
|
||||
__raw_writel(ctrl & ~RNGA_CONTROL_GO, rng_base + RNGA_CONTROL);
|
||||
__raw_writel(ctrl & ~RNGA_CONTROL_GO, mxc_rng->mem + RNGA_CONTROL);
|
||||
}
|
||||
|
||||
static struct hwrng mxc_rnga = {
|
||||
.name = "mxc-rnga",
|
||||
.init = mxc_rnga_init,
|
||||
.cleanup = mxc_rnga_cleanup,
|
||||
.data_present = mxc_rnga_data_present,
|
||||
.data_read = mxc_rnga_data_read
|
||||
};
|
||||
|
||||
static int __init mxc_rnga_probe(struct platform_device *pdev)
|
||||
{
|
||||
int err = -ENODEV;
|
||||
struct clk *clk;
|
||||
struct resource *res, *mem;
|
||||
void __iomem *rng_base = NULL;
|
||||
struct mxc_rng *mxc_rng;
|
||||
|
||||
if (rng_dev)
|
||||
return -EBUSY;
|
||||
mxc_rng = devm_kzalloc(&pdev->dev, sizeof(struct mxc_rng),
|
||||
GFP_KERNEL);
|
||||
if (!mxc_rng)
|
||||
return -ENOMEM;
|
||||
|
||||
clk = clk_get(&pdev->dev, "rng");
|
||||
if (IS_ERR(clk)) {
|
||||
mxc_rng->dev = &pdev->dev;
|
||||
mxc_rng->rng.name = "mxc-rnga";
|
||||
mxc_rng->rng.init = mxc_rnga_init;
|
||||
mxc_rng->rng.cleanup = mxc_rnga_cleanup,
|
||||
mxc_rng->rng.data_present = mxc_rnga_data_present,
|
||||
mxc_rng->rng.data_read = mxc_rnga_data_read,
|
||||
|
||||
mxc_rng->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(mxc_rng->clk)) {
|
||||
dev_err(&pdev->dev, "Could not get rng_clk!\n");
|
||||
err = PTR_ERR(clk);
|
||||
err = PTR_ERR(mxc_rng->clk);
|
||||
goto out;
|
||||
}
|
||||
|
||||
clk_enable(clk);
|
||||
clk_prepare_enable(mxc_rng->clk);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
@@ -173,36 +178,27 @@ static int __init mxc_rnga_probe(struct platform_device *pdev)
|
||||
goto err_region;
|
||||
}
|
||||
|
||||
rng_base = ioremap(res->start, resource_size(res));
|
||||
if (!rng_base) {
|
||||
mxc_rng->mem = ioremap(res->start, resource_size(res));
|
||||
if (!mxc_rng->mem) {
|
||||
err = -ENOMEM;
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
mxc_rnga.priv = (unsigned long)rng_base;
|
||||
|
||||
err = hwrng_register(&mxc_rnga);
|
||||
err = hwrng_register(&mxc_rng->rng);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "MXC RNGA registering failed (%d)\n", err);
|
||||
goto err_register;
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
rng_dev = pdev;
|
||||
|
||||
dev_info(&pdev->dev, "MXC RNGA Registered.\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_register:
|
||||
iounmap(rng_base);
|
||||
rng_base = NULL;
|
||||
|
||||
err_ioremap:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
err_region:
|
||||
clk_disable(clk);
|
||||
clk_put(clk);
|
||||
clk_disable_unprepare(mxc_rng->clk);
|
||||
|
||||
out:
|
||||
return err;
|
||||
@@ -211,17 +207,15 @@ static int __init mxc_rnga_probe(struct platform_device *pdev)
|
||||
static int __exit mxc_rnga_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
void __iomem *rng_base = (void __iomem *)mxc_rnga.priv;
|
||||
struct clk *clk = clk_get(&pdev->dev, "rng");
|
||||
struct mxc_rng *mxc_rng = platform_get_drvdata(pdev);
|
||||
|
||||
hwrng_unregister(&mxc_rnga);
|
||||
hwrng_unregister(&mxc_rng->rng);
|
||||
|
||||
iounmap(rng_base);
|
||||
iounmap(mxc_rng->mem);
|
||||
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
clk_disable(clk);
|
||||
clk_put(clk);
|
||||
clk_disable_unprepare(mxc_rng->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -75,42 +75,35 @@ static int __devinit octeon_rng_probe(struct platform_device *pdev)
|
||||
|
||||
res_ports = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res_ports)
|
||||
goto err_ports;
|
||||
return -ENOENT;
|
||||
|
||||
res_result = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res_result)
|
||||
goto err_ports;
|
||||
return -ENOENT;
|
||||
|
||||
|
||||
rng->control_status = devm_ioremap_nocache(&pdev->dev,
|
||||
res_ports->start,
|
||||
sizeof(u64));
|
||||
if (!rng->control_status)
|
||||
goto err_ports;
|
||||
return -ENOENT;
|
||||
|
||||
rng->result = devm_ioremap_nocache(&pdev->dev,
|
||||
res_result->start,
|
||||
sizeof(u64));
|
||||
if (!rng->result)
|
||||
goto err_r;
|
||||
return -ENOENT;
|
||||
|
||||
rng->ops = ops;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, &rng->ops);
|
||||
ret = hwrng_register(&rng->ops);
|
||||
if (ret)
|
||||
goto err;
|
||||
return -ENOENT;
|
||||
|
||||
dev_info(&pdev->dev, "Octeon Random Number Generator\n");
|
||||
|
||||
return 0;
|
||||
err:
|
||||
devm_iounmap(&pdev->dev, rng->control_status);
|
||||
err_r:
|
||||
devm_iounmap(&pdev->dev, rng->result);
|
||||
err_ports:
|
||||
devm_kfree(&pdev->dev, rng);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int __exit octeon_rng_remove(struct platform_device *pdev)
|
||||
|
||||
@@ -18,11 +18,12 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/hw_random.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
@@ -46,26 +47,36 @@
|
||||
#define RNG_SYSSTATUS 0x44 /* System status
|
||||
[0] = RESETDONE */
|
||||
|
||||
static void __iomem *rng_base;
|
||||
static struct clk *rng_ick;
|
||||
static struct platform_device *rng_dev;
|
||||
/**
|
||||
* struct omap_rng_private_data - RNG IP block-specific data
|
||||
* @base: virtual address of the beginning of the RNG IP block registers
|
||||
* @mem_res: struct resource * for the IP block registers physical memory
|
||||
*/
|
||||
struct omap_rng_private_data {
|
||||
void __iomem *base;
|
||||
struct resource *mem_res;
|
||||
};
|
||||
|
||||
static inline u32 omap_rng_read_reg(int reg)
|
||||
static inline u32 omap_rng_read_reg(struct omap_rng_private_data *priv, int reg)
|
||||
{
|
||||
return __raw_readl(rng_base + reg);
|
||||
return __raw_readl(priv->base + reg);
|
||||
}
|
||||
|
||||
static inline void omap_rng_write_reg(int reg, u32 val)
|
||||
static inline void omap_rng_write_reg(struct omap_rng_private_data *priv,
|
||||
int reg, u32 val)
|
||||
{
|
||||
__raw_writel(val, rng_base + reg);
|
||||
__raw_writel(val, priv->base + reg);
|
||||
}
|
||||
|
||||
static int omap_rng_data_present(struct hwrng *rng, int wait)
|
||||
{
|
||||
struct omap_rng_private_data *priv;
|
||||
int data, i;
|
||||
|
||||
priv = (struct omap_rng_private_data *)rng->priv;
|
||||
|
||||
for (i = 0; i < 20; i++) {
|
||||
data = omap_rng_read_reg(RNG_STAT_REG) ? 0 : 1;
|
||||
data = omap_rng_read_reg(priv, RNG_STAT_REG) ? 0 : 1;
|
||||
if (data || !wait)
|
||||
break;
|
||||
/* RNG produces data fast enough (2+ MBit/sec, even
|
||||
@@ -80,9 +91,13 @@ static int omap_rng_data_present(struct hwrng *rng, int wait)
|
||||
|
||||
static int omap_rng_data_read(struct hwrng *rng, u32 *data)
|
||||
{
|
||||
*data = omap_rng_read_reg(RNG_OUT_REG);
|
||||
struct omap_rng_private_data *priv;
|
||||
|
||||
return 4;
|
||||
priv = (struct omap_rng_private_data *)rng->priv;
|
||||
|
||||
*data = omap_rng_read_reg(priv, RNG_OUT_REG);
|
||||
|
||||
return sizeof(u32);
|
||||
}
|
||||
|
||||
static struct hwrng omap_rng_ops = {
|
||||
@@ -93,69 +108,68 @@ static struct hwrng omap_rng_ops = {
|
||||
|
||||
static int __devinit omap_rng_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct omap_rng_private_data *priv;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* A bit ugly, and it will never actually happen but there can
|
||||
* be only one RNG and this catches any bork
|
||||
*/
|
||||
if (rng_dev)
|
||||
return -EBUSY;
|
||||
priv = kzalloc(sizeof(struct omap_rng_private_data), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&pdev->dev, "could not allocate memory\n");
|
||||
return -ENOMEM;
|
||||
};
|
||||
|
||||
if (cpu_is_omap24xx()) {
|
||||
rng_ick = clk_get(&pdev->dev, "ick");
|
||||
if (IS_ERR(rng_ick)) {
|
||||
dev_err(&pdev->dev, "Could not get rng_ick\n");
|
||||
ret = PTR_ERR(rng_ick);
|
||||
return ret;
|
||||
} else
|
||||
clk_enable(rng_ick);
|
||||
omap_rng_ops.priv = (unsigned long)priv;
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
|
||||
priv->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!priv->mem_res) {
|
||||
ret = -ENOENT;
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
rng_base = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!rng_base) {
|
||||
priv->base = devm_request_and_ioremap(&pdev->dev, priv->mem_res);
|
||||
if (!priv->base) {
|
||||
ret = -ENOMEM;
|
||||
goto err_ioremap;
|
||||
}
|
||||
dev_set_drvdata(&pdev->dev, res);
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
ret = hwrng_register(&omap_rng_ops);
|
||||
if (ret)
|
||||
goto err_register;
|
||||
|
||||
dev_info(&pdev->dev, "OMAP Random Number Generator ver. %02x\n",
|
||||
omap_rng_read_reg(RNG_REV_REG));
|
||||
omap_rng_write_reg(RNG_MASK_REG, 0x1);
|
||||
omap_rng_read_reg(priv, RNG_REV_REG));
|
||||
|
||||
rng_dev = pdev;
|
||||
omap_rng_write_reg(priv, RNG_MASK_REG, 0x1);
|
||||
|
||||
return 0;
|
||||
|
||||
err_register:
|
||||
rng_base = NULL;
|
||||
priv->base = NULL;
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
err_ioremap:
|
||||
if (cpu_is_omap24xx()) {
|
||||
clk_disable(rng_ick);
|
||||
clk_put(rng_ick);
|
||||
}
|
||||
kfree(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit omap_rng_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_rng_private_data *priv = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
hwrng_unregister(&omap_rng_ops);
|
||||
|
||||
omap_rng_write_reg(RNG_MASK_REG, 0x0);
|
||||
omap_rng_write_reg(priv, RNG_MASK_REG, 0x0);
|
||||
|
||||
if (cpu_is_omap24xx()) {
|
||||
clk_disable(rng_ick);
|
||||
clk_put(rng_ick);
|
||||
}
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
rng_base = NULL;
|
||||
release_mem_region(priv->mem_res->start, resource_size(priv->mem_res));
|
||||
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -164,13 +178,21 @@ static int __exit omap_rng_remove(struct platform_device *pdev)
|
||||
|
||||
static int omap_rng_suspend(struct device *dev)
|
||||
{
|
||||
omap_rng_write_reg(RNG_MASK_REG, 0x0);
|
||||
struct omap_rng_private_data *priv = dev_get_drvdata(dev);
|
||||
|
||||
omap_rng_write_reg(priv, RNG_MASK_REG, 0x0);
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_rng_resume(struct device *dev)
|
||||
{
|
||||
omap_rng_write_reg(RNG_MASK_REG, 0x1);
|
||||
struct omap_rng_private_data *priv = dev_get_drvdata(dev);
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
omap_rng_write_reg(priv, RNG_MASK_REG, 0x1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -198,9 +220,6 @@ static struct platform_driver omap_rng_driver = {
|
||||
|
||||
static int __init omap_rng_init(void)
|
||||
{
|
||||
if (!cpu_is_omap16xx() && !cpu_is_omap24xx())
|
||||
return -ENODEV;
|
||||
|
||||
return platform_driver_register(&omap_rng_driver);
|
||||
}
|
||||
|
||||
|
||||
50
drivers/char/hw_random/tpm-rng.c
Normal file
50
drivers/char/hw_random/tpm-rng.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Kent Yoder IBM Corporation
|
||||
*
|
||||
* HWRNG interfaces to pull RNG data from a TPM
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/hw_random.h>
|
||||
#include <linux/tpm.h>
|
||||
|
||||
#define MODULE_NAME "tpm-rng"
|
||||
|
||||
static int tpm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
|
||||
{
|
||||
return tpm_get_random(TPM_ANY_NUM, data, max);
|
||||
}
|
||||
|
||||
static struct hwrng tpm_rng = {
|
||||
.name = MODULE_NAME,
|
||||
.read = tpm_rng_read,
|
||||
};
|
||||
|
||||
static int __init rng_init(void)
|
||||
{
|
||||
return hwrng_register(&tpm_rng);
|
||||
}
|
||||
module_init(rng_init);
|
||||
|
||||
static void __exit rng_exit(void)
|
||||
{
|
||||
hwrng_unregister(&tpm_rng);
|
||||
}
|
||||
module_exit(rng_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Kent Yoder <key@linux.vnet.ibm.com>");
|
||||
MODULE_DESCRIPTION("RNG driver for TPM devices");
|
||||
@@ -1880,7 +1880,7 @@ int ipmi_request_supply_msgs(ipmi_user_t user,
|
||||
struct ipmi_recv_msg *supplied_recv,
|
||||
int priority)
|
||||
{
|
||||
unsigned char saddr, lun;
|
||||
unsigned char saddr = 0, lun = 0;
|
||||
int rv;
|
||||
|
||||
if (!user)
|
||||
|
||||
@@ -2424,6 +2424,38 @@ static void ipmi_pci_cleanup(struct smi_info *info)
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static int __devinit ipmi_pci_probe_regspacing(struct smi_info *info)
|
||||
{
|
||||
if (info->si_type == SI_KCS) {
|
||||
unsigned char status;
|
||||
int regspacing;
|
||||
|
||||
info->io.regsize = DEFAULT_REGSIZE;
|
||||
info->io.regshift = 0;
|
||||
info->io_size = 2;
|
||||
info->handlers = &kcs_smi_handlers;
|
||||
|
||||
/* detect 1, 4, 16byte spacing */
|
||||
for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) {
|
||||
info->io.regspacing = regspacing;
|
||||
if (info->io_setup(info)) {
|
||||
dev_err(info->dev,
|
||||
"Could not setup I/O space\n");
|
||||
return DEFAULT_REGSPACING;
|
||||
}
|
||||
/* write invalid cmd */
|
||||
info->io.outputb(&info->io, 1, 0x10);
|
||||
/* read status back */
|
||||
status = info->io.inputb(&info->io, 1);
|
||||
info->io_cleanup(info);
|
||||
if (status)
|
||||
return regspacing;
|
||||
regspacing *= 4;
|
||||
}
|
||||
}
|
||||
return DEFAULT_REGSPACING;
|
||||
}
|
||||
|
||||
static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
@@ -2476,8 +2508,8 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
|
||||
}
|
||||
info->io.addr_data = pci_resource_start(pdev, 0);
|
||||
|
||||
info->io.regspacing = DEFAULT_REGSPACING;
|
||||
info->io.regsize = DEFAULT_REGSPACING;
|
||||
info->io.regspacing = ipmi_pci_probe_regspacing(info);
|
||||
info->io.regsize = DEFAULT_REGSIZE;
|
||||
info->io.regshift = 0;
|
||||
|
||||
info->irq = pdev->irq;
|
||||
|
||||
@@ -507,7 +507,7 @@ static int mbcs_gscr_mmap(struct file *fp, struct vm_area_struct *vma)
|
||||
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
|
||||
/* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
|
||||
/* Remap-pfn-range will mark the range VM_IO */
|
||||
if (remap_pfn_range(vma,
|
||||
vma->vm_start,
|
||||
__pa(soft->gscr_addr) >> PAGE_SHIFT,
|
||||
@@ -799,7 +799,7 @@ static int mbcs_remove(struct cx_dev *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cx_device_id __devinitdata mbcs_id_table[] = {
|
||||
static const struct cx_device_id __devinitconst mbcs_id_table[] = {
|
||||
{
|
||||
.part_num = MBCS_PART_NUM,
|
||||
.mfg_num = MBCS_MFG_NUM,
|
||||
|
||||
@@ -322,7 +322,7 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma)
|
||||
|
||||
vma->vm_ops = &mmap_mem_ops;
|
||||
|
||||
/* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
|
||||
/* Remap-pfn-range will mark the range VM_IO */
|
||||
if (remap_pfn_range(vma,
|
||||
vma->vm_start,
|
||||
vma->vm_pgoff,
|
||||
|
||||
@@ -826,7 +826,7 @@ static int __init mmtimer_init(void)
|
||||
|
||||
/* Allocate list of node ptrs to mmtimer_t's */
|
||||
timers = kzalloc(sizeof(struct mmtimer_node)*maxn, GFP_KERNEL);
|
||||
if (timers == NULL) {
|
||||
if (!timers) {
|
||||
printk(KERN_ERR "%s: failed to allocate memory for device\n",
|
||||
MMTIMER_NAME);
|
||||
goto out3;
|
||||
@@ -848,7 +848,6 @@ static int __init mmtimer_init(void)
|
||||
return 0;
|
||||
|
||||
out3:
|
||||
kfree(timers);
|
||||
misc_deregister(&mmtimer_miscdev);
|
||||
out2:
|
||||
free_irq(SGI_MMTIMER_VECTOR, NULL);
|
||||
|
||||
@@ -286,7 +286,7 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma,
|
||||
atomic_set(&vdata->refcnt, 1);
|
||||
vma->vm_private_data = vdata;
|
||||
|
||||
vma->vm_flags |= (VM_IO | VM_RESERVED | VM_PFNMAP | VM_DONTEXPAND);
|
||||
vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
|
||||
if (vdata->type == MSPEC_FETCHOP || vdata->type == MSPEC_UNCACHED)
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
vma->vm_ops = &mspec_vm_ops;
|
||||
|
||||
@@ -430,7 +430,7 @@ static ssize_t mwave_write(struct file *file, const char __user *buf,
|
||||
|
||||
static int register_serial_portandirq(unsigned int port, int irq)
|
||||
{
|
||||
struct uart_port uart;
|
||||
struct uart_8250_port uart;
|
||||
|
||||
switch ( port ) {
|
||||
case 0x3f8:
|
||||
@@ -462,14 +462,14 @@ static int register_serial_portandirq(unsigned int port, int irq)
|
||||
} /* switch */
|
||||
/* irq is okay */
|
||||
|
||||
memset(&uart, 0, sizeof(struct uart_port));
|
||||
memset(&uart, 0, sizeof(uart));
|
||||
|
||||
uart.uartclk = 1843200;
|
||||
uart.iobase = port;
|
||||
uart.irq = irq;
|
||||
uart.iotype = UPIO_PORT;
|
||||
uart.flags = UPF_SHARE_IRQ;
|
||||
return serial8250_register_port(&uart);
|
||||
uart.port.uartclk = 1843200;
|
||||
uart.port.iobase = port;
|
||||
uart.port.irq = irq;
|
||||
uart.port.iotype = UPIO_PORT;
|
||||
uart.port.flags = UPF_SHARE_IRQ;
|
||||
return serial8250_register_8250_port(&uart);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -93,9 +93,9 @@ int button_del_callback (void (*callback) (void))
|
||||
button_callback_list [lp].count = 0;
|
||||
callback_count--;
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
lp--;
|
||||
};
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
|
||||
#include <asm/hardware/dec21285.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/leds.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
@@ -179,9 +178,6 @@ static ssize_t flash_write(struct file *file, const char __user *buf,
|
||||
|
||||
written = 0;
|
||||
|
||||
leds_event(led_claim);
|
||||
leds_event(led_green_on);
|
||||
|
||||
nBlock = (int) p >> 16; //block # of 64K bytes
|
||||
|
||||
/*
|
||||
@@ -258,11 +254,6 @@ static ssize_t flash_write(struct file *file, const char __user *buf,
|
||||
printk(KERN_DEBUG "flash_write: written 0x%X bytes OK.\n", written);
|
||||
}
|
||||
|
||||
/*
|
||||
* restore reg on exit
|
||||
*/
|
||||
leds_event(led_release);
|
||||
|
||||
mutex_unlock(&nwflash_mutex);
|
||||
|
||||
return written;
|
||||
@@ -333,11 +324,6 @@ static int erase_block(int nBlock)
|
||||
unsigned long timeout;
|
||||
int temp, temp1;
|
||||
|
||||
/*
|
||||
* orange LED == erase
|
||||
*/
|
||||
leds_event(led_amber_on);
|
||||
|
||||
/*
|
||||
* reset footbridge to the correct offset 0 (...0..3)
|
||||
*/
|
||||
@@ -446,12 +432,6 @@ static int write_block(unsigned long p, const char __user *buf, int count)
|
||||
unsigned long timeout;
|
||||
unsigned long timeout1;
|
||||
|
||||
/*
|
||||
* red LED == write
|
||||
*/
|
||||
leds_event(led_amber_off);
|
||||
leds_event(led_red_on);
|
||||
|
||||
pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p));
|
||||
|
||||
/*
|
||||
@@ -557,18 +537,10 @@ static int write_block(unsigned long p, const char __user *buf, int count)
|
||||
printk(KERN_DEBUG "write_block: Retrying write at 0x%X)n",
|
||||
pWritePtr - FLASH_BASE);
|
||||
|
||||
/*
|
||||
* no LED == waiting
|
||||
*/
|
||||
leds_event(led_amber_off);
|
||||
/*
|
||||
* wait couple ms
|
||||
*/
|
||||
msleep(10);
|
||||
/*
|
||||
* red LED == write
|
||||
*/
|
||||
leds_event(led_red_on);
|
||||
|
||||
goto WriteRetry;
|
||||
} else {
|
||||
@@ -583,12 +555,6 @@ static int write_block(unsigned long p, const char __user *buf, int count)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* green LED == read/verify
|
||||
*/
|
||||
leds_event(led_amber_off);
|
||||
leds_event(led_green_on);
|
||||
|
||||
msleep(10);
|
||||
|
||||
pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p));
|
||||
@@ -617,9 +583,9 @@ static void kick_open(void)
|
||||
* we want to write a bit pattern XXX1 to Xilinx to enable
|
||||
* the write gate, which will be open for about the next 2ms.
|
||||
*/
|
||||
spin_lock_irqsave(&nw_gpio_lock, flags);
|
||||
raw_spin_lock_irqsave(&nw_gpio_lock, flags);
|
||||
nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE);
|
||||
spin_unlock_irqrestore(&nw_gpio_lock, flags);
|
||||
raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
|
||||
|
||||
/*
|
||||
* let the ISA bus to catch on...
|
||||
|
||||
@@ -891,6 +891,14 @@ static void rx_ready_async(MGSLPC_INFO *info, int tcd, struct tty_struct *tty)
|
||||
int work = 0;
|
||||
struct mgsl_icount *icount = &info->icount;
|
||||
|
||||
if (!tty) {
|
||||
/* tty is not available anymore */
|
||||
issue_command(info, CHA, CMD_RXRESET);
|
||||
if (debug_level >= DEBUG_LEVEL_ISR)
|
||||
printk("%s(%d):rx_ready_async(tty=NULL)\n",__FILE__,__LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tcd) {
|
||||
/* early termination, get FIFO count from RBCL register */
|
||||
fifo_count = (unsigned char)(read_reg(info, CHA+RBCL) & 0x1f);
|
||||
@@ -980,7 +988,7 @@ static void tx_done(MGSLPC_INFO *info, struct tty_struct *tty)
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (tty->stopped || tty->hw_stopped) {
|
||||
if (tty && (tty->stopped || tty->hw_stopped)) {
|
||||
tx_stop(info);
|
||||
return;
|
||||
}
|
||||
@@ -1000,7 +1008,7 @@ static void tx_ready(MGSLPC_INFO *info, struct tty_struct *tty)
|
||||
if (!info->tx_active)
|
||||
return;
|
||||
} else {
|
||||
if (tty->stopped || tty->hw_stopped) {
|
||||
if (tty && (tty->stopped || tty->hw_stopped)) {
|
||||
tx_stop(info);
|
||||
return;
|
||||
}
|
||||
@@ -1050,13 +1058,12 @@ static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty)
|
||||
wake_up_interruptible(&info->status_event_wait_q);
|
||||
wake_up_interruptible(&info->event_wait_q);
|
||||
|
||||
if (info->port.flags & ASYNC_CTS_FLOW) {
|
||||
if (tty && tty_port_cts_enabled(&info->port)) {
|
||||
if (tty->hw_stopped) {
|
||||
if (info->serial_signals & SerialSignal_CTS) {
|
||||
if (debug_level >= DEBUG_LEVEL_ISR)
|
||||
printk("CTS tx start...");
|
||||
if (tty)
|
||||
tty->hw_stopped = 0;
|
||||
tty->hw_stopped = 0;
|
||||
tx_start(info, tty);
|
||||
info->pending_bh |= BH_TRANSMIT;
|
||||
return;
|
||||
@@ -1065,8 +1072,7 @@ static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty)
|
||||
if (!(info->serial_signals & SerialSignal_CTS)) {
|
||||
if (debug_level >= DEBUG_LEVEL_ISR)
|
||||
printk("CTS tx stop...");
|
||||
if (tty)
|
||||
tty->hw_stopped = 1;
|
||||
tty->hw_stopped = 1;
|
||||
tx_stop(info);
|
||||
}
|
||||
}
|
||||
@@ -1344,7 +1350,7 @@ static void shutdown(MGSLPC_INFO * info, struct tty_struct *tty)
|
||||
/* TODO:disable interrupts instead of reset to preserve signal states */
|
||||
reset_device(info);
|
||||
|
||||
if (!tty || tty->termios->c_cflag & HUPCL) {
|
||||
if (!tty || tty->termios.c_cflag & HUPCL) {
|
||||
info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
|
||||
set_signals(info);
|
||||
}
|
||||
@@ -1385,7 +1391,7 @@ static void mgslpc_program_hw(MGSLPC_INFO *info, struct tty_struct *tty)
|
||||
port_irq_enable(info, (unsigned char) PVR_DSR | PVR_RI);
|
||||
get_signals(info);
|
||||
|
||||
if (info->netcount || (tty && (tty->termios->c_cflag & CREAD)))
|
||||
if (info->netcount || (tty && (tty->termios.c_cflag & CREAD)))
|
||||
rx_start(info);
|
||||
|
||||
spin_unlock_irqrestore(&info->lock,flags);
|
||||
@@ -1398,14 +1404,14 @@ static void mgslpc_change_params(MGSLPC_INFO *info, struct tty_struct *tty)
|
||||
unsigned cflag;
|
||||
int bits_per_char;
|
||||
|
||||
if (!tty || !tty->termios)
|
||||
if (!tty)
|
||||
return;
|
||||
|
||||
if (debug_level >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d):mgslpc_change_params(%s)\n",
|
||||
__FILE__,__LINE__, info->device_name );
|
||||
|
||||
cflag = tty->termios->c_cflag;
|
||||
cflag = tty->termios.c_cflag;
|
||||
|
||||
/* if B0 rate (hangup) specified then negate DTR and RTS */
|
||||
/* otherwise assert DTR and RTS */
|
||||
@@ -1728,7 +1734,7 @@ static void mgslpc_throttle(struct tty_struct * tty)
|
||||
if (I_IXOFF(tty))
|
||||
mgslpc_send_xchar(tty, STOP_CHAR(tty));
|
||||
|
||||
if (tty->termios->c_cflag & CRTSCTS) {
|
||||
if (tty->termios.c_cflag & CRTSCTS) {
|
||||
spin_lock_irqsave(&info->lock,flags);
|
||||
info->serial_signals &= ~SerialSignal_RTS;
|
||||
set_signals(info);
|
||||
@@ -1757,7 +1763,7 @@ static void mgslpc_unthrottle(struct tty_struct * tty)
|
||||
mgslpc_send_xchar(tty, START_CHAR(tty));
|
||||
}
|
||||
|
||||
if (tty->termios->c_cflag & CRTSCTS) {
|
||||
if (tty->termios.c_cflag & CRTSCTS) {
|
||||
spin_lock_irqsave(&info->lock,flags);
|
||||
info->serial_signals |= SerialSignal_RTS;
|
||||
set_signals(info);
|
||||
@@ -2293,8 +2299,8 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term
|
||||
tty->driver->name );
|
||||
|
||||
/* just return if nothing has changed */
|
||||
if ((tty->termios->c_cflag == old_termios->c_cflag)
|
||||
&& (RELEVANT_IFLAG(tty->termios->c_iflag)
|
||||
if ((tty->termios.c_cflag == old_termios->c_cflag)
|
||||
&& (RELEVANT_IFLAG(tty->termios.c_iflag)
|
||||
== RELEVANT_IFLAG(old_termios->c_iflag)))
|
||||
return;
|
||||
|
||||
@@ -2302,7 +2308,7 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term
|
||||
|
||||
/* Handle transition to B0 status */
|
||||
if (old_termios->c_cflag & CBAUD &&
|
||||
!(tty->termios->c_cflag & CBAUD)) {
|
||||
!(tty->termios.c_cflag & CBAUD)) {
|
||||
info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
|
||||
spin_lock_irqsave(&info->lock,flags);
|
||||
set_signals(info);
|
||||
@@ -2311,9 +2317,9 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term
|
||||
|
||||
/* Handle transition away from B0 status */
|
||||
if (!(old_termios->c_cflag & CBAUD) &&
|
||||
tty->termios->c_cflag & CBAUD) {
|
||||
tty->termios.c_cflag & CBAUD) {
|
||||
info->serial_signals |= SerialSignal_DTR;
|
||||
if (!(tty->termios->c_cflag & CRTSCTS) ||
|
||||
if (!(tty->termios.c_cflag & CRTSCTS) ||
|
||||
!test_bit(TTY_THROTTLED, &tty->flags)) {
|
||||
info->serial_signals |= SerialSignal_RTS;
|
||||
}
|
||||
@@ -2324,7 +2330,7 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term
|
||||
|
||||
/* Handle turning off CRTSCTS */
|
||||
if (old_termios->c_cflag & CRTSCTS &&
|
||||
!(tty->termios->c_cflag & CRTSCTS)) {
|
||||
!(tty->termios.c_cflag & CRTSCTS)) {
|
||||
tty->hw_stopped = 0;
|
||||
tx_release(tty);
|
||||
}
|
||||
@@ -2731,6 +2737,8 @@ static void mgslpc_add_device(MGSLPC_INFO *info)
|
||||
#if SYNCLINK_GENERIC_HDLC
|
||||
hdlcdev_init(info);
|
||||
#endif
|
||||
tty_port_register_device(&info->port, serial_driver, info->line,
|
||||
&info->p_dev->dev);
|
||||
}
|
||||
|
||||
static void mgslpc_remove_device(MGSLPC_INFO *remove_info)
|
||||
@@ -2744,6 +2752,7 @@ static void mgslpc_remove_device(MGSLPC_INFO *remove_info)
|
||||
last->next_device = info->next_device;
|
||||
else
|
||||
mgslpc_device_list = info->next_device;
|
||||
tty_unregister_device(serial_driver, info->line);
|
||||
#if SYNCLINK_GENERIC_HDLC
|
||||
hdlcdev_exit(info);
|
||||
#endif
|
||||
@@ -2798,77 +2807,63 @@ static const struct tty_operations mgslpc_ops = {
|
||||
.proc_fops = &mgslpc_proc_fops,
|
||||
};
|
||||
|
||||
static void synclink_cs_cleanup(void)
|
||||
static int __init synclink_cs_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
while(mgslpc_device_list)
|
||||
mgslpc_remove_device(mgslpc_device_list);
|
||||
|
||||
if (serial_driver) {
|
||||
if ((rc = tty_unregister_driver(serial_driver)))
|
||||
printk("%s(%d) failed to unregister tty driver err=%d\n",
|
||||
__FILE__,__LINE__,rc);
|
||||
put_tty_driver(serial_driver);
|
||||
if (break_on_load) {
|
||||
mgslpc_get_text_ptr();
|
||||
BREAKPOINT();
|
||||
}
|
||||
|
||||
pcmcia_unregister_driver(&mgslpc_driver);
|
||||
}
|
||||
serial_driver = tty_alloc_driver(MAX_DEVICE_COUNT,
|
||||
TTY_DRIVER_REAL_RAW |
|
||||
TTY_DRIVER_DYNAMIC_DEV);
|
||||
if (IS_ERR(serial_driver)) {
|
||||
rc = PTR_ERR(serial_driver);
|
||||
goto err;
|
||||
}
|
||||
|
||||
static int __init synclink_cs_init(void)
|
||||
{
|
||||
int rc;
|
||||
/* Initialize the tty_driver structure */
|
||||
serial_driver->driver_name = "synclink_cs";
|
||||
serial_driver->name = "ttySLP";
|
||||
serial_driver->major = ttymajor;
|
||||
serial_driver->minor_start = 64;
|
||||
serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
||||
serial_driver->subtype = SERIAL_TYPE_NORMAL;
|
||||
serial_driver->init_termios = tty_std_termios;
|
||||
serial_driver->init_termios.c_cflag =
|
||||
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
|
||||
tty_set_operations(serial_driver, &mgslpc_ops);
|
||||
|
||||
if (break_on_load) {
|
||||
mgslpc_get_text_ptr();
|
||||
BREAKPOINT();
|
||||
}
|
||||
rc = tty_register_driver(serial_driver);
|
||||
if (rc < 0) {
|
||||
printk(KERN_ERR "%s(%d):Couldn't register serial driver\n",
|
||||
__FILE__, __LINE__);
|
||||
goto err_put_tty;
|
||||
}
|
||||
|
||||
if ((rc = pcmcia_register_driver(&mgslpc_driver)) < 0)
|
||||
return rc;
|
||||
rc = pcmcia_register_driver(&mgslpc_driver);
|
||||
if (rc < 0)
|
||||
goto err_unreg_tty;
|
||||
|
||||
serial_driver = alloc_tty_driver(MAX_DEVICE_COUNT);
|
||||
if (!serial_driver) {
|
||||
rc = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
printk(KERN_INFO "%s %s, tty major#%d\n", driver_name, driver_version,
|
||||
serial_driver->major);
|
||||
|
||||
/* Initialize the tty_driver structure */
|
||||
|
||||
serial_driver->driver_name = "synclink_cs";
|
||||
serial_driver->name = "ttySLP";
|
||||
serial_driver->major = ttymajor;
|
||||
serial_driver->minor_start = 64;
|
||||
serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
||||
serial_driver->subtype = SERIAL_TYPE_NORMAL;
|
||||
serial_driver->init_termios = tty_std_termios;
|
||||
serial_driver->init_termios.c_cflag =
|
||||
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
|
||||
serial_driver->flags = TTY_DRIVER_REAL_RAW;
|
||||
tty_set_operations(serial_driver, &mgslpc_ops);
|
||||
|
||||
if ((rc = tty_register_driver(serial_driver)) < 0) {
|
||||
printk("%s(%d):Couldn't register serial driver\n",
|
||||
__FILE__,__LINE__);
|
||||
put_tty_driver(serial_driver);
|
||||
serial_driver = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
printk("%s %s, tty major#%d\n",
|
||||
driver_name, driver_version,
|
||||
serial_driver->major);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
synclink_cs_cleanup();
|
||||
return rc;
|
||||
return 0;
|
||||
err_unreg_tty:
|
||||
tty_unregister_driver(serial_driver);
|
||||
err_put_tty:
|
||||
put_tty_driver(serial_driver);
|
||||
err:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit synclink_cs_exit(void)
|
||||
{
|
||||
synclink_cs_cleanup();
|
||||
pcmcia_unregister_driver(&mgslpc_driver);
|
||||
tty_unregister_driver(serial_driver);
|
||||
put_tty_driver(serial_driver);
|
||||
}
|
||||
|
||||
module_init(synclink_cs_init);
|
||||
|
||||
@@ -779,7 +779,8 @@ static int __init ppdev_init (void)
|
||||
err = PTR_ERR(ppdev_class);
|
||||
goto out_chrdev;
|
||||
}
|
||||
if (parport_register_driver(&pp_driver)) {
|
||||
err = parport_register_driver(&pp_driver);
|
||||
if (err < 0) {
|
||||
printk (KERN_WARNING CHRDEV ": unable to register with parport\n");
|
||||
goto out_class;
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ static long raw_ctl_compat_ioctl(struct file *file, unsigned int cmd,
|
||||
|
||||
static const struct file_operations raw_fops = {
|
||||
.read = do_sync_read,
|
||||
.aio_read = generic_file_aio_read,
|
||||
.aio_read = blkdev_aio_read,
|
||||
.write = do_sync_write,
|
||||
.aio_write = blkdev_aio_write,
|
||||
.fsync = blkdev_fsync,
|
||||
|
||||
@@ -411,7 +411,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
|
||||
case RTC_IRQP_READ:
|
||||
case RTC_IRQP_SET:
|
||||
return -EINVAL;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1433,7 +1433,7 @@ static int __devexit sonypi_remove(struct platform_device *dev)
|
||||
sonypi_disable();
|
||||
|
||||
synchronize_irq(sonypi_device.irq);
|
||||
flush_work_sync(&sonypi_device.input_work);
|
||||
flush_work(&sonypi_device.input_work);
|
||||
|
||||
if (useinput) {
|
||||
input_unregister_device(sonypi_device.input_key_dev);
|
||||
@@ -1456,7 +1456,7 @@ static int __devexit sonypi_remove(struct platform_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int old_camera_power;
|
||||
|
||||
static int sonypi_suspend(struct device *dev)
|
||||
|
||||
@@ -784,8 +784,10 @@ static int __init tlclk_init(void)
|
||||
}
|
||||
tlclk_major = ret;
|
||||
alarm_events = kzalloc( sizeof(struct tlclk_alarms), GFP_KERNEL);
|
||||
if (!alarm_events)
|
||||
if (!alarm_events) {
|
||||
ret = -ENOMEM;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
/* Read telecom clock IRQ number (Set by BIOS) */
|
||||
if (!request_region(TLCLK_BASE, 8, "telco_clock")) {
|
||||
|
||||
@@ -33,6 +33,17 @@ config TCG_TIS
|
||||
from within Linux. To compile this driver as a module, choose
|
||||
M here; the module will be called tpm_tis.
|
||||
|
||||
config TCG_TIS_I2C_INFINEON
|
||||
tristate "TPM Interface Specification 1.2 Interface (I2C - Infineon)"
|
||||
depends on I2C
|
||||
---help---
|
||||
If you have a TPM security chip that is compliant with the
|
||||
TCG TIS 1.2 TPM specification and Infineon's I2C Protocol Stack
|
||||
Specification 0.20 say Yes and it will be accessible from within
|
||||
Linux.
|
||||
To compile this driver as a module, choose M here; the module
|
||||
will be called tpm_tis_i2c_infineon.
|
||||
|
||||
config TCG_NSC
|
||||
tristate "National Semiconductor TPM Interface"
|
||||
depends on X86
|
||||
@@ -62,4 +73,12 @@ config TCG_INFINEON
|
||||
Further information on this driver and the supported hardware
|
||||
can be found at http://www.trust.rub.de/projects/linux-device-driver-infineon-tpm/
|
||||
|
||||
config TCG_IBMVTPM
|
||||
tristate "IBM VTPM Interface"
|
||||
depends on PPC64
|
||||
---help---
|
||||
If you have IBM virtual TPM (VTPM) support say Yes and it
|
||||
will be accessible from within Linux. To compile this driver
|
||||
as a module, choose M here; the module will be called tpm_ibmvtpm.
|
||||
|
||||
endif # TCG_TPM
|
||||
|
||||
@@ -4,8 +4,16 @@
|
||||
obj-$(CONFIG_TCG_TPM) += tpm.o
|
||||
ifdef CONFIG_ACPI
|
||||
obj-$(CONFIG_TCG_TPM) += tpm_bios.o
|
||||
tpm_bios-objs += tpm_eventlog.o tpm_acpi.o tpm_ppi.o
|
||||
else
|
||||
ifdef CONFIG_TCG_IBMVTPM
|
||||
obj-$(CONFIG_TCG_TPM) += tpm_bios.o
|
||||
tpm_bios-objs += tpm_eventlog.o tpm_of.o
|
||||
endif
|
||||
endif
|
||||
obj-$(CONFIG_TCG_TIS) += tpm_tis.o
|
||||
obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
|
||||
obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
|
||||
obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
|
||||
obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
|
||||
obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
|
||||
|
||||
@@ -30,12 +30,7 @@
|
||||
#include <linux/freezer.h>
|
||||
|
||||
#include "tpm.h"
|
||||
|
||||
enum tpm_const {
|
||||
TPM_MINOR = 224, /* officially assigned */
|
||||
TPM_BUFSIZE = 4096,
|
||||
TPM_NUM_DEVICES = 256,
|
||||
};
|
||||
#include "tpm_eventlog.h"
|
||||
|
||||
enum tpm_duration {
|
||||
TPM_SHORT = 0,
|
||||
@@ -482,6 +477,7 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
|
||||
#define TPM_INTERNAL_RESULT_SIZE 200
|
||||
#define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
|
||||
#define TPM_ORD_GET_CAP cpu_to_be32(101)
|
||||
#define TPM_ORD_GET_RANDOM cpu_to_be32(70)
|
||||
|
||||
static const struct tpm_input_header tpm_getcap_header = {
|
||||
.tag = TPM_TAG_RQU_COMMAND,
|
||||
@@ -919,7 +915,7 @@ EXPORT_SYMBOL_GPL(tpm_show_pcrs);
|
||||
|
||||
#define READ_PUBEK_RESULT_SIZE 314
|
||||
#define TPM_ORD_READPUBEK cpu_to_be32(124)
|
||||
struct tpm_input_header tpm_readpubek_header = {
|
||||
static struct tpm_input_header tpm_readpubek_header = {
|
||||
.tag = TPM_TAG_RQU_COMMAND,
|
||||
.length = cpu_to_be32(30),
|
||||
.ordinal = TPM_ORD_READPUBEK
|
||||
@@ -1172,10 +1168,10 @@ int tpm_release(struct inode *inode, struct file *file)
|
||||
struct tpm_chip *chip = file->private_data;
|
||||
|
||||
del_singleshot_timer_sync(&chip->user_read_timer);
|
||||
flush_work_sync(&chip->work);
|
||||
flush_work(&chip->work);
|
||||
file->private_data = NULL;
|
||||
atomic_set(&chip->data_pending, 0);
|
||||
kfree(chip->data_buffer);
|
||||
kzfree(chip->data_buffer);
|
||||
clear_bit(0, &chip->is_open);
|
||||
put_device(chip->dev);
|
||||
return 0;
|
||||
@@ -1186,17 +1182,20 @@ ssize_t tpm_write(struct file *file, const char __user *buf,
|
||||
size_t size, loff_t *off)
|
||||
{
|
||||
struct tpm_chip *chip = file->private_data;
|
||||
size_t in_size = size, out_size;
|
||||
size_t in_size = size;
|
||||
ssize_t out_size;
|
||||
|
||||
/* cannot perform a write until the read has cleared
|
||||
either via tpm_read or a user_read_timer timeout */
|
||||
while (atomic_read(&chip->data_pending) != 0)
|
||||
msleep(TPM_TIMEOUT);
|
||||
|
||||
mutex_lock(&chip->buffer_mutex);
|
||||
either via tpm_read or a user_read_timer timeout.
|
||||
This also prevents splitted buffered writes from blocking here.
|
||||
*/
|
||||
if (atomic_read(&chip->data_pending) != 0)
|
||||
return -EBUSY;
|
||||
|
||||
if (in_size > TPM_BUFSIZE)
|
||||
in_size = TPM_BUFSIZE;
|
||||
return -E2BIG;
|
||||
|
||||
mutex_lock(&chip->buffer_mutex);
|
||||
|
||||
if (copy_from_user
|
||||
(chip->data_buffer, (void __user *) buf, in_size)) {
|
||||
@@ -1206,6 +1205,10 @@ ssize_t tpm_write(struct file *file, const char __user *buf,
|
||||
|
||||
/* atomic tpm command send and result receive */
|
||||
out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE);
|
||||
if (out_size < 0) {
|
||||
mutex_unlock(&chip->buffer_mutex);
|
||||
return out_size;
|
||||
}
|
||||
|
||||
atomic_set(&chip->data_pending, out_size);
|
||||
mutex_unlock(&chip->buffer_mutex);
|
||||
@@ -1225,9 +1228,8 @@ ssize_t tpm_read(struct file *file, char __user *buf,
|
||||
int rc;
|
||||
|
||||
del_singleshot_timer_sync(&chip->user_read_timer);
|
||||
flush_work_sync(&chip->work);
|
||||
flush_work(&chip->work);
|
||||
ret_size = atomic_read(&chip->data_pending);
|
||||
atomic_set(&chip->data_pending, 0);
|
||||
if (ret_size > 0) { /* relay data */
|
||||
ssize_t orig_ret_size = ret_size;
|
||||
if (size < ret_size)
|
||||
@@ -1242,6 +1244,8 @@ ssize_t tpm_read(struct file *file, char __user *buf,
|
||||
mutex_unlock(&chip->buffer_mutex);
|
||||
}
|
||||
|
||||
atomic_set(&chip->data_pending, 0);
|
||||
|
||||
return ret_size;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_read);
|
||||
@@ -1262,6 +1266,7 @@ void tpm_remove_hardware(struct device *dev)
|
||||
|
||||
misc_deregister(&chip->vendor.miscdev);
|
||||
sysfs_remove_group(&dev->kobj, chip->vendor.attr_group);
|
||||
tpm_remove_ppi(&dev->kobj);
|
||||
tpm_bios_log_teardown(chip->bios_dir);
|
||||
|
||||
/* write it this way to be explicit (chip->dev == dev) */
|
||||
@@ -1326,6 +1331,58 @@ int tpm_pm_resume(struct device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_pm_resume);
|
||||
|
||||
#define TPM_GETRANDOM_RESULT_SIZE 18
|
||||
static struct tpm_input_header tpm_getrandom_header = {
|
||||
.tag = TPM_TAG_RQU_COMMAND,
|
||||
.length = cpu_to_be32(14),
|
||||
.ordinal = TPM_ORD_GET_RANDOM
|
||||
};
|
||||
|
||||
/**
|
||||
* tpm_get_random() - Get random bytes from the tpm's RNG
|
||||
* @chip_num: A specific chip number for the request or TPM_ANY_NUM
|
||||
* @out: destination buffer for the random bytes
|
||||
* @max: the max number of bytes to write to @out
|
||||
*
|
||||
* Returns < 0 on error and the number of bytes read on success
|
||||
*/
|
||||
int tpm_get_random(u32 chip_num, u8 *out, size_t max)
|
||||
{
|
||||
struct tpm_chip *chip;
|
||||
struct tpm_cmd_t tpm_cmd;
|
||||
u32 recd, num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA);
|
||||
int err, total = 0, retries = 5;
|
||||
u8 *dest = out;
|
||||
|
||||
chip = tpm_chip_find_get(chip_num);
|
||||
if (chip == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if (!out || !num_bytes || max > TPM_MAX_RNG_DATA)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
tpm_cmd.header.in = tpm_getrandom_header;
|
||||
tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes);
|
||||
|
||||
err = transmit_cmd(chip, &tpm_cmd,
|
||||
TPM_GETRANDOM_RESULT_SIZE + num_bytes,
|
||||
"attempting get random");
|
||||
if (err)
|
||||
break;
|
||||
|
||||
recd = be32_to_cpu(tpm_cmd.params.getrandom_out.rng_data_len);
|
||||
memcpy(dest, tpm_cmd.params.getrandom_out.rng_data, recd);
|
||||
|
||||
dest += recd;
|
||||
total += recd;
|
||||
num_bytes -= recd;
|
||||
} while (retries-- && total < max);
|
||||
|
||||
return total ? total : -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_get_random);
|
||||
|
||||
/* In case vendor provided release function, call it too.*/
|
||||
|
||||
void tpm_dev_vendor_release(struct tpm_chip *chip)
|
||||
@@ -1346,7 +1403,7 @@ EXPORT_SYMBOL_GPL(tpm_dev_vendor_release);
|
||||
* Once all references to platform device are down to 0,
|
||||
* release all allocated structures.
|
||||
*/
|
||||
void tpm_dev_release(struct device *dev)
|
||||
static void tpm_dev_release(struct device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||
|
||||
@@ -1427,6 +1484,11 @@ struct tpm_chip *tpm_register_hardware(struct device *dev,
|
||||
goto put_device;
|
||||
}
|
||||
|
||||
if (tpm_add_ppi(&dev->kobj)) {
|
||||
misc_deregister(&chip->vendor.miscdev);
|
||||
goto put_device;
|
||||
}
|
||||
|
||||
chip->bios_dir = tpm_bios_log_setup(devname);
|
||||
|
||||
/* Make chip available */
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/tpm.h>
|
||||
|
||||
enum tpm_const {
|
||||
TPM_MINOR = 224, /* officially assigned */
|
||||
TPM_BUFSIZE = 4096,
|
||||
TPM_NUM_DEVICES = 256,
|
||||
};
|
||||
|
||||
enum tpm_timeout {
|
||||
TPM_TIMEOUT = 5, /* msecs */
|
||||
};
|
||||
@@ -94,6 +100,7 @@ struct tpm_vendor_specific {
|
||||
bool timeout_adjusted;
|
||||
unsigned long duration[3]; /* jiffies */
|
||||
bool duration_adjusted;
|
||||
void *data;
|
||||
|
||||
wait_queue_head_t read_queue;
|
||||
wait_queue_head_t int_queue;
|
||||
@@ -269,6 +276,21 @@ struct tpm_pcrextend_in {
|
||||
u8 hash[TPM_DIGEST_SIZE];
|
||||
}__attribute__((packed));
|
||||
|
||||
/* 128 bytes is an arbitrary cap. This could be as large as TPM_BUFSIZE - 18
|
||||
* bytes, but 128 is still a relatively large number of random bytes and
|
||||
* anything much bigger causes users of struct tpm_cmd_t to start getting
|
||||
* compiler warnings about stack frame size. */
|
||||
#define TPM_MAX_RNG_DATA 128
|
||||
|
||||
struct tpm_getrandom_out {
|
||||
__be32 rng_data_len;
|
||||
u8 rng_data[TPM_MAX_RNG_DATA];
|
||||
}__attribute__((packed));
|
||||
|
||||
struct tpm_getrandom_in {
|
||||
__be32 num_bytes;
|
||||
}__attribute__((packed));
|
||||
|
||||
typedef union {
|
||||
struct tpm_getcap_params_out getcap_out;
|
||||
struct tpm_readpubek_params_out readpubek_out;
|
||||
@@ -277,6 +299,8 @@ typedef union {
|
||||
struct tpm_pcrread_in pcrread_in;
|
||||
struct tpm_pcrread_out pcrread_out;
|
||||
struct tpm_pcrextend_in pcrextend_in;
|
||||
struct tpm_getrandom_in getrandom_in;
|
||||
struct tpm_getrandom_out getrandom_out;
|
||||
} tpm_cmd_params;
|
||||
|
||||
struct tpm_cmd_t {
|
||||
@@ -303,15 +327,17 @@ extern int tpm_pm_suspend(struct device *);
|
||||
extern int tpm_pm_resume(struct device *);
|
||||
extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
|
||||
wait_queue_head_t *);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
extern struct dentry ** tpm_bios_log_setup(char *);
|
||||
extern void tpm_bios_log_teardown(struct dentry **);
|
||||
extern int tpm_add_ppi(struct kobject *);
|
||||
extern void tpm_remove_ppi(struct kobject *);
|
||||
#else
|
||||
static inline struct dentry ** tpm_bios_log_setup(char *name)
|
||||
static inline int tpm_add_ppi(struct kobject *parent)
|
||||
{
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
static inline void tpm_bios_log_teardown(struct dentry **dir)
|
||||
|
||||
static inline void tpm_remove_ppi(struct kobject *parent)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
109
drivers/char/tpm/tpm_acpi.c
Normal file
109
drivers/char/tpm/tpm_acpi.c
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2005 IBM Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Seiji Munetoh <munetoh@jp.ibm.com>
|
||||
* Stefan Berger <stefanb@us.ibm.com>
|
||||
* Reiner Sailer <sailer@watson.ibm.com>
|
||||
* Kylene Hall <kjhall@us.ibm.com>
|
||||
*
|
||||
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
||||
*
|
||||
* Access to the eventlog extended by the TCG BIOS of PC platform
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <acpi/acpi.h>
|
||||
|
||||
#include "tpm.h"
|
||||
#include "tpm_eventlog.h"
|
||||
|
||||
struct acpi_tcpa {
|
||||
struct acpi_table_header hdr;
|
||||
u16 platform_class;
|
||||
union {
|
||||
struct client_hdr {
|
||||
u32 log_max_len __attribute__ ((packed));
|
||||
u64 log_start_addr __attribute__ ((packed));
|
||||
} client;
|
||||
struct server_hdr {
|
||||
u16 reserved;
|
||||
u64 log_max_len __attribute__ ((packed));
|
||||
u64 log_start_addr __attribute__ ((packed));
|
||||
} server;
|
||||
};
|
||||
};
|
||||
|
||||
/* read binary bios log */
|
||||
int read_log(struct tpm_bios_log *log)
|
||||
{
|
||||
struct acpi_tcpa *buff;
|
||||
acpi_status status;
|
||||
void __iomem *virt;
|
||||
u64 len, start;
|
||||
|
||||
if (log->bios_event_log != NULL) {
|
||||
printk(KERN_ERR
|
||||
"%s: ERROR - Eventlog already initialized\n",
|
||||
__func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */
|
||||
status = acpi_get_table(ACPI_SIG_TCPA, 1,
|
||||
(struct acpi_table_header **)&buff);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch(buff->platform_class) {
|
||||
case BIOS_SERVER:
|
||||
len = buff->server.log_max_len;
|
||||
start = buff->server.log_start_addr;
|
||||
break;
|
||||
case BIOS_CLIENT:
|
||||
default:
|
||||
len = buff->client.log_max_len;
|
||||
start = buff->client.log_start_addr;
|
||||
break;
|
||||
}
|
||||
if (!len) {
|
||||
printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* malloc EventLog space */
|
||||
log->bios_event_log = kmalloc(len, GFP_KERNEL);
|
||||
if (!log->bios_event_log) {
|
||||
printk("%s: ERROR - Not enough Memory for BIOS measurements\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
log->bios_event_log_end = log->bios_event_log + len;
|
||||
|
||||
virt = acpi_os_map_memory(start, len);
|
||||
if (!virt) {
|
||||
kfree(log->bios_event_log);
|
||||
printk("%s: ERROR - Unable to map memory\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
memcpy_fromio(log->bios_event_log, virt, len);
|
||||
|
||||
acpi_os_unmap_memory(virt, len);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2005 IBM Corporation
|
||||
* Copyright (C) 2005, 2012 IBM Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Kent Yoder <key@linux.vnet.ibm.com>
|
||||
* Seiji Munetoh <munetoh@jp.ibm.com>
|
||||
* Stefan Berger <stefanb@us.ibm.com>
|
||||
* Reiner Sailer <sailer@watson.ibm.com>
|
||||
@@ -9,7 +10,7 @@
|
||||
*
|
||||
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
||||
*
|
||||
* Access to the eventlog extended by the TCG BIOS of PC platform
|
||||
* Access to the eventlog created by a system's firmware / BIOS
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -23,67 +24,10 @@
|
||||
#include <linux/security.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <acpi/acpi.h>
|
||||
|
||||
#include "tpm.h"
|
||||
#include "tpm_eventlog.h"
|
||||
|
||||
#define TCG_EVENT_NAME_LEN_MAX 255
|
||||
#define MAX_TEXT_EVENT 1000 /* Max event string length */
|
||||
#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */
|
||||
|
||||
enum bios_platform_class {
|
||||
BIOS_CLIENT = 0x00,
|
||||
BIOS_SERVER = 0x01,
|
||||
};
|
||||
|
||||
struct tpm_bios_log {
|
||||
void *bios_event_log;
|
||||
void *bios_event_log_end;
|
||||
};
|
||||
|
||||
struct acpi_tcpa {
|
||||
struct acpi_table_header hdr;
|
||||
u16 platform_class;
|
||||
union {
|
||||
struct client_hdr {
|
||||
u32 log_max_len __attribute__ ((packed));
|
||||
u64 log_start_addr __attribute__ ((packed));
|
||||
} client;
|
||||
struct server_hdr {
|
||||
u16 reserved;
|
||||
u64 log_max_len __attribute__ ((packed));
|
||||
u64 log_start_addr __attribute__ ((packed));
|
||||
} server;
|
||||
};
|
||||
};
|
||||
|
||||
struct tcpa_event {
|
||||
u32 pcr_index;
|
||||
u32 event_type;
|
||||
u8 pcr_value[20]; /* SHA1 */
|
||||
u32 event_size;
|
||||
u8 event_data[0];
|
||||
};
|
||||
|
||||
enum tcpa_event_types {
|
||||
PREBOOT = 0,
|
||||
POST_CODE,
|
||||
UNUSED,
|
||||
NO_ACTION,
|
||||
SEPARATOR,
|
||||
ACTION,
|
||||
EVENT_TAG,
|
||||
SCRTM_CONTENTS,
|
||||
SCRTM_VERSION,
|
||||
CPU_MICROCODE,
|
||||
PLATFORM_CONFIG_FLAGS,
|
||||
TABLE_OF_DEVICES,
|
||||
COMPACT_HASH,
|
||||
IPL,
|
||||
IPL_PARTITION_DATA,
|
||||
NONHOST_CODE,
|
||||
NONHOST_CONFIG,
|
||||
NONHOST_INFO,
|
||||
};
|
||||
|
||||
static const char* tcpa_event_type_strings[] = {
|
||||
"PREBOOT",
|
||||
@@ -106,28 +50,6 @@ static const char* tcpa_event_type_strings[] = {
|
||||
"Non-Host Info"
|
||||
};
|
||||
|
||||
struct tcpa_pc_event {
|
||||
u32 event_id;
|
||||
u32 event_size;
|
||||
u8 event_data[0];
|
||||
};
|
||||
|
||||
enum tcpa_pc_event_ids {
|
||||
SMBIOS = 1,
|
||||
BIS_CERT,
|
||||
POST_BIOS_ROM,
|
||||
ESCD,
|
||||
CMOS,
|
||||
NVRAM,
|
||||
OPTION_ROM_EXEC,
|
||||
OPTION_ROM_CONFIG,
|
||||
OPTION_ROM_MICROCODE = 10,
|
||||
S_CRTM_VERSION,
|
||||
S_CRTM_CONTENTS,
|
||||
POST_CONTENTS,
|
||||
HOST_TABLE_OF_DEVICES,
|
||||
};
|
||||
|
||||
static const char* tcpa_pc_event_id_strings[] = {
|
||||
"",
|
||||
"SMBIOS",
|
||||
@@ -358,65 +280,6 @@ static const struct seq_operations tpm_binary_b_measurments_seqops = {
|
||||
.show = tpm_binary_bios_measurements_show,
|
||||
};
|
||||
|
||||
/* read binary bios log */
|
||||
static int read_log(struct tpm_bios_log *log)
|
||||
{
|
||||
struct acpi_tcpa *buff;
|
||||
acpi_status status;
|
||||
struct acpi_table_header *virt;
|
||||
u64 len, start;
|
||||
|
||||
if (log->bios_event_log != NULL) {
|
||||
printk(KERN_ERR
|
||||
"%s: ERROR - Eventlog already initialized\n",
|
||||
__func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */
|
||||
status = acpi_get_table(ACPI_SIG_TCPA, 1,
|
||||
(struct acpi_table_header **)&buff);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch(buff->platform_class) {
|
||||
case BIOS_SERVER:
|
||||
len = buff->server.log_max_len;
|
||||
start = buff->server.log_start_addr;
|
||||
break;
|
||||
case BIOS_CLIENT:
|
||||
default:
|
||||
len = buff->client.log_max_len;
|
||||
start = buff->client.log_start_addr;
|
||||
break;
|
||||
}
|
||||
if (!len) {
|
||||
printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* malloc EventLog space */
|
||||
log->bios_event_log = kmalloc(len, GFP_KERNEL);
|
||||
if (!log->bios_event_log) {
|
||||
printk("%s: ERROR - Not enough Memory for BIOS measurements\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
log->bios_event_log_end = log->bios_event_log + len;
|
||||
|
||||
virt = acpi_os_map_memory(start, len);
|
||||
|
||||
memcpy(log->bios_event_log, virt, len);
|
||||
|
||||
acpi_os_unmap_memory(virt, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_ascii_bios_measurements_open(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
86
drivers/char/tpm/tpm_eventlog.h
Normal file
86
drivers/char/tpm/tpm_eventlog.h
Normal file
@@ -0,0 +1,86 @@
|
||||
|
||||
#ifndef __TPM_EVENTLOG_H__
|
||||
#define __TPM_EVENTLOG_H__
|
||||
|
||||
#define TCG_EVENT_NAME_LEN_MAX 255
|
||||
#define MAX_TEXT_EVENT 1000 /* Max event string length */
|
||||
#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */
|
||||
|
||||
enum bios_platform_class {
|
||||
BIOS_CLIENT = 0x00,
|
||||
BIOS_SERVER = 0x01,
|
||||
};
|
||||
|
||||
struct tpm_bios_log {
|
||||
void *bios_event_log;
|
||||
void *bios_event_log_end;
|
||||
};
|
||||
|
||||
struct tcpa_event {
|
||||
u32 pcr_index;
|
||||
u32 event_type;
|
||||
u8 pcr_value[20]; /* SHA1 */
|
||||
u32 event_size;
|
||||
u8 event_data[0];
|
||||
};
|
||||
|
||||
enum tcpa_event_types {
|
||||
PREBOOT = 0,
|
||||
POST_CODE,
|
||||
UNUSED,
|
||||
NO_ACTION,
|
||||
SEPARATOR,
|
||||
ACTION,
|
||||
EVENT_TAG,
|
||||
SCRTM_CONTENTS,
|
||||
SCRTM_VERSION,
|
||||
CPU_MICROCODE,
|
||||
PLATFORM_CONFIG_FLAGS,
|
||||
TABLE_OF_DEVICES,
|
||||
COMPACT_HASH,
|
||||
IPL,
|
||||
IPL_PARTITION_DATA,
|
||||
NONHOST_CODE,
|
||||
NONHOST_CONFIG,
|
||||
NONHOST_INFO,
|
||||
};
|
||||
|
||||
struct tcpa_pc_event {
|
||||
u32 event_id;
|
||||
u32 event_size;
|
||||
u8 event_data[0];
|
||||
};
|
||||
|
||||
enum tcpa_pc_event_ids {
|
||||
SMBIOS = 1,
|
||||
BIS_CERT,
|
||||
POST_BIOS_ROM,
|
||||
ESCD,
|
||||
CMOS,
|
||||
NVRAM,
|
||||
OPTION_ROM_EXEC,
|
||||
OPTION_ROM_CONFIG,
|
||||
OPTION_ROM_MICROCODE = 10,
|
||||
S_CRTM_VERSION,
|
||||
S_CRTM_CONTENTS,
|
||||
POST_CONTENTS,
|
||||
HOST_TABLE_OF_DEVICES,
|
||||
};
|
||||
|
||||
int read_log(struct tpm_bios_log *log);
|
||||
|
||||
#if defined(CONFIG_TCG_IBMVTPM) || defined(CONFIG_TCG_IBMVTPM_MODULE) || \
|
||||
defined(CONFIG_ACPI)
|
||||
extern struct dentry **tpm_bios_log_setup(char *);
|
||||
extern void tpm_bios_log_teardown(struct dentry **);
|
||||
#else
|
||||
static inline struct dentry **tpm_bios_log_setup(char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline void tpm_bios_log_teardown(struct dentry **dir)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
695
drivers/char/tpm/tpm_i2c_infineon.c
Normal file
695
drivers/char/tpm/tpm_i2c_infineon.c
Normal file
@@ -0,0 +1,695 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Infineon Technologies
|
||||
*
|
||||
* Authors:
|
||||
* Peter Huewe <peter.huewe@infineon.com>
|
||||
*
|
||||
* Device driver for TCG/TCPA TPM (trusted platform module).
|
||||
* Specifications at www.trustedcomputinggroup.org
|
||||
*
|
||||
* This device driver implements the TPM interface as defined in
|
||||
* the TCG TPM Interface Spec version 1.2, revision 1.0 and the
|
||||
* Infineon I2C Protocol Stack Specification v0.20.
|
||||
*
|
||||
* It is based on the original tpm_tis device driver from Leendert van
|
||||
* Dorn and Kyleen Hall.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, version 2 of the
|
||||
* License.
|
||||
*
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/wait.h>
|
||||
#include "tpm.h"
|
||||
|
||||
/* max. buffer size supported by our TPM */
|
||||
#define TPM_BUFSIZE 1260
|
||||
|
||||
/* max. number of iterations after I2C NAK */
|
||||
#define MAX_COUNT 3
|
||||
|
||||
#define SLEEP_DURATION_LOW 55
|
||||
#define SLEEP_DURATION_HI 65
|
||||
|
||||
/* max. number of iterations after I2C NAK for 'long' commands
|
||||
* we need this especially for sending TPM_READY, since the cleanup after the
|
||||
* transtion to the ready state may take some time, but it is unpredictable
|
||||
* how long it will take.
|
||||
*/
|
||||
#define MAX_COUNT_LONG 50
|
||||
|
||||
#define SLEEP_DURATION_LONG_LOW 200
|
||||
#define SLEEP_DURATION_LONG_HI 220
|
||||
|
||||
/* After sending TPM_READY to 'reset' the TPM we have to sleep even longer */
|
||||
#define SLEEP_DURATION_RESET_LOW 2400
|
||||
#define SLEEP_DURATION_RESET_HI 2600
|
||||
|
||||
/* we want to use usleep_range instead of msleep for the 5ms TPM_TIMEOUT */
|
||||
#define TPM_TIMEOUT_US_LOW (TPM_TIMEOUT * 1000)
|
||||
#define TPM_TIMEOUT_US_HI (TPM_TIMEOUT_US_LOW + 2000)
|
||||
|
||||
/* expected value for DIDVID register */
|
||||
#define TPM_TIS_I2C_DID_VID 0x000b15d1L
|
||||
|
||||
/* Structure to store I2C TPM specific stuff */
|
||||
struct tpm_inf_dev {
|
||||
struct i2c_client *client;
|
||||
u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */
|
||||
struct tpm_chip *chip;
|
||||
};
|
||||
|
||||
static struct tpm_inf_dev tpm_dev;
|
||||
static struct i2c_driver tpm_tis_i2c_driver;
|
||||
|
||||
/*
|
||||
* iic_tpm_read() - read from TPM register
|
||||
* @addr: register address to read from
|
||||
* @buffer: provided by caller
|
||||
* @len: number of bytes to read
|
||||
*
|
||||
* Read len bytes from TPM register and put them into
|
||||
* buffer (little-endian format, i.e. first byte is put into buffer[0]).
|
||||
*
|
||||
* NOTE: TPM is big-endian for multi-byte values. Multi-byte
|
||||
* values have to be swapped.
|
||||
*
|
||||
* NOTE: We can't unfortunately use the combined read/write functions
|
||||
* provided by the i2c core as the TPM currently does not support the
|
||||
* repeated start condition and due to it's special requirements.
|
||||
* The i2c_smbus* functions do not work for this chip.
|
||||
*
|
||||
* Return -EIO on error, 0 on success.
|
||||
*/
|
||||
static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
|
||||
{
|
||||
|
||||
struct i2c_msg msg1 = { tpm_dev.client->addr, 0, 1, &addr };
|
||||
struct i2c_msg msg2 = { tpm_dev.client->addr, I2C_M_RD, len, buffer };
|
||||
|
||||
int rc;
|
||||
int count;
|
||||
|
||||
/* Lock the adapter for the duration of the whole sequence. */
|
||||
if (!tpm_dev.client->adapter->algo->master_xfer)
|
||||
return -EOPNOTSUPP;
|
||||
i2c_lock_adapter(tpm_dev.client->adapter);
|
||||
|
||||
for (count = 0; count < MAX_COUNT; count++) {
|
||||
rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1);
|
||||
if (rc > 0)
|
||||
break; /* break here to skip sleep */
|
||||
|
||||
usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
|
||||
}
|
||||
|
||||
if (rc <= 0)
|
||||
goto out;
|
||||
|
||||
/* After the TPM has successfully received the register address it needs
|
||||
* some time, thus we're sleeping here again, before retrieving the data
|
||||
*/
|
||||
for (count = 0; count < MAX_COUNT; count++) {
|
||||
usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
|
||||
rc = __i2c_transfer(tpm_dev.client->adapter, &msg2, 1);
|
||||
if (rc > 0)
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
out:
|
||||
i2c_unlock_adapter(tpm_dev.client->adapter);
|
||||
if (rc <= 0)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len,
|
||||
unsigned int sleep_low,
|
||||
unsigned int sleep_hi, u8 max_count)
|
||||
{
|
||||
int rc = -EIO;
|
||||
int count;
|
||||
|
||||
struct i2c_msg msg1 = { tpm_dev.client->addr, 0, len + 1, tpm_dev.buf };
|
||||
|
||||
if (len > TPM_BUFSIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (!tpm_dev.client->adapter->algo->master_xfer)
|
||||
return -EOPNOTSUPP;
|
||||
i2c_lock_adapter(tpm_dev.client->adapter);
|
||||
|
||||
/* prepend the 'register address' to the buffer */
|
||||
tpm_dev.buf[0] = addr;
|
||||
memcpy(&(tpm_dev.buf[1]), buffer, len);
|
||||
|
||||
/*
|
||||
* NOTE: We have to use these special mechanisms here and unfortunately
|
||||
* cannot rely on the standard behavior of i2c_transfer.
|
||||
*/
|
||||
for (count = 0; count < max_count; count++) {
|
||||
rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1);
|
||||
if (rc > 0)
|
||||
break;
|
||||
|
||||
usleep_range(sleep_low, sleep_hi);
|
||||
}
|
||||
|
||||
i2c_unlock_adapter(tpm_dev.client->adapter);
|
||||
if (rc <= 0)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* iic_tpm_write() - write to TPM register
|
||||
* @addr: register address to write to
|
||||
* @buffer: containing data to be written
|
||||
* @len: number of bytes to write
|
||||
*
|
||||
* Write len bytes from provided buffer to TPM register (little
|
||||
* endian format, i.e. buffer[0] is written as first byte).
|
||||
*
|
||||
* NOTE: TPM is big-endian for multi-byte values. Multi-byte
|
||||
* values have to be swapped.
|
||||
*
|
||||
* NOTE: use this function instead of the iic_tpm_write_generic function.
|
||||
*
|
||||
* Return -EIO on error, 0 on success
|
||||
*/
|
||||
static int iic_tpm_write(u8 addr, u8 *buffer, size_t len)
|
||||
{
|
||||
return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LOW,
|
||||
SLEEP_DURATION_HI, MAX_COUNT);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is needed especially for the cleanup situation after
|
||||
* sending TPM_READY
|
||||
* */
|
||||
static int iic_tpm_write_long(u8 addr, u8 *buffer, size_t len)
|
||||
{
|
||||
return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG_LOW,
|
||||
SLEEP_DURATION_LONG_HI, MAX_COUNT_LONG);
|
||||
}
|
||||
|
||||
enum tis_access {
|
||||
TPM_ACCESS_VALID = 0x80,
|
||||
TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
|
||||
TPM_ACCESS_REQUEST_PENDING = 0x04,
|
||||
TPM_ACCESS_REQUEST_USE = 0x02,
|
||||
};
|
||||
|
||||
enum tis_status {
|
||||
TPM_STS_VALID = 0x80,
|
||||
TPM_STS_COMMAND_READY = 0x40,
|
||||
TPM_STS_GO = 0x20,
|
||||
TPM_STS_DATA_AVAIL = 0x10,
|
||||
TPM_STS_DATA_EXPECT = 0x08,
|
||||
};
|
||||
|
||||
enum tis_defaults {
|
||||
TIS_SHORT_TIMEOUT = 750, /* ms */
|
||||
TIS_LONG_TIMEOUT = 2000, /* 2 sec */
|
||||
};
|
||||
|
||||
#define TPM_ACCESS(l) (0x0000 | ((l) << 4))
|
||||
#define TPM_STS(l) (0x0001 | ((l) << 4))
|
||||
#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4))
|
||||
#define TPM_DID_VID(l) (0x0006 | ((l) << 4))
|
||||
|
||||
static int check_locality(struct tpm_chip *chip, int loc)
|
||||
{
|
||||
u8 buf;
|
||||
int rc;
|
||||
|
||||
rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
|
||||
chip->vendor.locality = loc;
|
||||
return loc;
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* implementation similar to tpm_tis */
|
||||
static void release_locality(struct tpm_chip *chip, int loc, int force)
|
||||
{
|
||||
u8 buf;
|
||||
if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0)
|
||||
return;
|
||||
|
||||
if (force || (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) {
|
||||
buf = TPM_ACCESS_ACTIVE_LOCALITY;
|
||||
iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int request_locality(struct tpm_chip *chip, int loc)
|
||||
{
|
||||
unsigned long stop;
|
||||
u8 buf = TPM_ACCESS_REQUEST_USE;
|
||||
|
||||
if (check_locality(chip, loc) >= 0)
|
||||
return loc;
|
||||
|
||||
iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
|
||||
|
||||
/* wait for burstcount */
|
||||
stop = jiffies + chip->vendor.timeout_a;
|
||||
do {
|
||||
if (check_locality(chip, loc) >= 0)
|
||||
return loc;
|
||||
usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
|
||||
} while (time_before(jiffies, stop));
|
||||
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
static u8 tpm_tis_i2c_status(struct tpm_chip *chip)
|
||||
{
|
||||
/* NOTE: since I2C read may fail, return 0 in this case --> time-out */
|
||||
u8 buf;
|
||||
if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0)
|
||||
return 0;
|
||||
else
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void tpm_tis_i2c_ready(struct tpm_chip *chip)
|
||||
{
|
||||
/* this causes the current command to be aborted */
|
||||
u8 buf = TPM_STS_COMMAND_READY;
|
||||
iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1);
|
||||
}
|
||||
|
||||
static ssize_t get_burstcount(struct tpm_chip *chip)
|
||||
{
|
||||
unsigned long stop;
|
||||
ssize_t burstcnt;
|
||||
u8 buf[3];
|
||||
|
||||
/* wait for burstcount */
|
||||
/* which timeout value, spec has 2 answers (c & d) */
|
||||
stop = jiffies + chip->vendor.timeout_d;
|
||||
do {
|
||||
/* Note: STS is little endian */
|
||||
if (iic_tpm_read(TPM_STS(chip->vendor.locality)+1, buf, 3) < 0)
|
||||
burstcnt = 0;
|
||||
else
|
||||
burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0];
|
||||
|
||||
if (burstcnt)
|
||||
return burstcnt;
|
||||
|
||||
usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
|
||||
} while (time_before(jiffies, stop));
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
|
||||
int *status)
|
||||
{
|
||||
unsigned long stop;
|
||||
|
||||
/* check current status */
|
||||
*status = tpm_tis_i2c_status(chip);
|
||||
if ((*status & mask) == mask)
|
||||
return 0;
|
||||
|
||||
stop = jiffies + timeout;
|
||||
do {
|
||||
/* since we just checked the status, give the TPM some time */
|
||||
usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
|
||||
*status = tpm_tis_i2c_status(chip);
|
||||
if ((*status & mask) == mask)
|
||||
return 0;
|
||||
|
||||
} while (time_before(jiffies, stop));
|
||||
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
{
|
||||
size_t size = 0;
|
||||
ssize_t burstcnt;
|
||||
u8 retries = 0;
|
||||
int rc;
|
||||
|
||||
while (size < count) {
|
||||
burstcnt = get_burstcount(chip);
|
||||
|
||||
/* burstcnt < 0 = TPM is busy */
|
||||
if (burstcnt < 0)
|
||||
return burstcnt;
|
||||
|
||||
/* limit received data to max. left */
|
||||
if (burstcnt > (count - size))
|
||||
burstcnt = count - size;
|
||||
|
||||
rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality),
|
||||
&(buf[size]), burstcnt);
|
||||
if (rc == 0)
|
||||
size += burstcnt;
|
||||
else if (rc < 0)
|
||||
retries++;
|
||||
|
||||
/* avoid endless loop in case of broken HW */
|
||||
if (retries > MAX_COUNT_LONG)
|
||||
return -EIO;
|
||||
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
{
|
||||
int size = 0;
|
||||
int expected, status;
|
||||
|
||||
if (count < TPM_HEADER_SIZE) {
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* read first 10 bytes, including tag, paramsize, and result */
|
||||
size = recv_data(chip, buf, TPM_HEADER_SIZE);
|
||||
if (size < TPM_HEADER_SIZE) {
|
||||
dev_err(chip->dev, "Unable to read header\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
expected = be32_to_cpu(*(__be32 *)(buf + 2));
|
||||
if ((size_t) expected > count) {
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size += recv_data(chip, &buf[TPM_HEADER_SIZE],
|
||||
expected - TPM_HEADER_SIZE);
|
||||
if (size < expected) {
|
||||
dev_err(chip->dev, "Unable to read remainder of result\n");
|
||||
size = -ETIME;
|
||||
goto out;
|
||||
}
|
||||
|
||||
wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
|
||||
if (status & TPM_STS_DATA_AVAIL) { /* retry? */
|
||||
dev_err(chip->dev, "Error left over data\n");
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
tpm_tis_i2c_ready(chip);
|
||||
/* The TPM needs some time to clean up here,
|
||||
* so we sleep rather than keeping the bus busy
|
||||
*/
|
||||
usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
|
||||
release_locality(chip, chip->vendor.locality, 0);
|
||||
return size;
|
||||
}
|
||||
|
||||
static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
{
|
||||
int rc, status;
|
||||
ssize_t burstcnt;
|
||||
size_t count = 0;
|
||||
u8 retries = 0;
|
||||
u8 sts = TPM_STS_GO;
|
||||
|
||||
if (len > TPM_BUFSIZE)
|
||||
return -E2BIG; /* command is too long for our tpm, sorry */
|
||||
|
||||
if (request_locality(chip, 0) < 0)
|
||||
return -EBUSY;
|
||||
|
||||
status = tpm_tis_i2c_status(chip);
|
||||
if ((status & TPM_STS_COMMAND_READY) == 0) {
|
||||
tpm_tis_i2c_ready(chip);
|
||||
if (wait_for_stat
|
||||
(chip, TPM_STS_COMMAND_READY,
|
||||
chip->vendor.timeout_b, &status) < 0) {
|
||||
rc = -ETIME;
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
while (count < len - 1) {
|
||||
burstcnt = get_burstcount(chip);
|
||||
|
||||
/* burstcnt < 0 = TPM is busy */
|
||||
if (burstcnt < 0)
|
||||
return burstcnt;
|
||||
|
||||
if (burstcnt > (len - 1 - count))
|
||||
burstcnt = len - 1 - count;
|
||||
|
||||
rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality),
|
||||
&(buf[count]), burstcnt);
|
||||
if (rc == 0)
|
||||
count += burstcnt;
|
||||
else if (rc < 0)
|
||||
retries++;
|
||||
|
||||
/* avoid endless loop in case of broken HW */
|
||||
if (retries > MAX_COUNT_LONG) {
|
||||
rc = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
wait_for_stat(chip, TPM_STS_VALID,
|
||||
chip->vendor.timeout_c, &status);
|
||||
|
||||
if ((status & TPM_STS_DATA_EXPECT) == 0) {
|
||||
rc = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* write last byte */
|
||||
iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1);
|
||||
wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
|
||||
if ((status & TPM_STS_DATA_EXPECT) != 0) {
|
||||
rc = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* go and do it */
|
||||
iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1);
|
||||
|
||||
return len;
|
||||
out_err:
|
||||
tpm_tis_i2c_ready(chip);
|
||||
/* The TPM needs some time to clean up here,
|
||||
* so we sleep rather than keeping the bus busy
|
||||
*/
|
||||
usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
|
||||
release_locality(chip, chip->vendor.locality, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct file_operations tis_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.open = tpm_open,
|
||||
.read = tpm_read,
|
||||
.write = tpm_write,
|
||||
.release = tpm_release,
|
||||
};
|
||||
|
||||
static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
|
||||
static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
|
||||
static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
|
||||
static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
|
||||
static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
|
||||
static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL);
|
||||
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
|
||||
static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
|
||||
static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
|
||||
static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
|
||||
|
||||
static struct attribute *tis_attrs[] = {
|
||||
&dev_attr_pubek.attr,
|
||||
&dev_attr_pcrs.attr,
|
||||
&dev_attr_enabled.attr,
|
||||
&dev_attr_active.attr,
|
||||
&dev_attr_owned.attr,
|
||||
&dev_attr_temp_deactivated.attr,
|
||||
&dev_attr_caps.attr,
|
||||
&dev_attr_cancel.attr,
|
||||
&dev_attr_durations.attr,
|
||||
&dev_attr_timeouts.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group tis_attr_grp = {
|
||||
.attrs = tis_attrs
|
||||
};
|
||||
|
||||
static struct tpm_vendor_specific tpm_tis_i2c = {
|
||||
.status = tpm_tis_i2c_status,
|
||||
.recv = tpm_tis_i2c_recv,
|
||||
.send = tpm_tis_i2c_send,
|
||||
.cancel = tpm_tis_i2c_ready,
|
||||
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_canceled = TPM_STS_COMMAND_READY,
|
||||
.attr_group = &tis_attr_grp,
|
||||
.miscdev.fops = &tis_ops,
|
||||
};
|
||||
|
||||
static int __devinit tpm_tis_i2c_init(struct device *dev)
|
||||
{
|
||||
u32 vendor;
|
||||
int rc = 0;
|
||||
struct tpm_chip *chip;
|
||||
|
||||
chip = tpm_register_hardware(dev, &tpm_tis_i2c);
|
||||
if (!chip) {
|
||||
rc = -ENODEV;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* Disable interrupts */
|
||||
chip->vendor.irq = 0;
|
||||
|
||||
/* Default timeouts */
|
||||
chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
|
||||
chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
|
||||
if (request_locality(chip, 0) != 0) {
|
||||
rc = -ENODEV;
|
||||
goto out_vendor;
|
||||
}
|
||||
|
||||
/* read four bytes from DID_VID register */
|
||||
if (iic_tpm_read(TPM_DID_VID(0), (u8 *)&vendor, 4) < 0) {
|
||||
rc = -EIO;
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
/* create DID_VID register value, after swapping to little-endian */
|
||||
vendor = be32_to_cpu((__be32) vendor);
|
||||
|
||||
if (vendor != TPM_TIS_I2C_DID_VID) {
|
||||
rc = -ENODEV;
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
dev_info(dev, "1.2 TPM (device-id 0x%X)\n", vendor >> 16);
|
||||
|
||||
INIT_LIST_HEAD(&chip->vendor.list);
|
||||
tpm_dev.chip = chip;
|
||||
|
||||
tpm_get_timeouts(chip);
|
||||
tpm_do_selftest(chip);
|
||||
|
||||
return 0;
|
||||
|
||||
out_release:
|
||||
release_locality(chip, chip->vendor.locality, 1);
|
||||
|
||||
out_vendor:
|
||||
/* close file handles */
|
||||
tpm_dev_vendor_release(chip);
|
||||
|
||||
/* remove hardware */
|
||||
tpm_remove_hardware(chip->dev);
|
||||
|
||||
/* reset these pointers, otherwise we oops */
|
||||
chip->dev->release = NULL;
|
||||
chip->release = NULL;
|
||||
tpm_dev.client = NULL;
|
||||
dev_set_drvdata(chip->dev, chip);
|
||||
out_err:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tpm_tis_i2c_table[] = {
|
||||
{"tpm_i2c_infineon", 0},
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_table);
|
||||
static SIMPLE_DEV_PM_OPS(tpm_tis_i2c_ops, tpm_pm_suspend, tpm_pm_resume);
|
||||
|
||||
static int __devinit tpm_tis_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int rc;
|
||||
if (tpm_dev.client != NULL)
|
||||
return -EBUSY; /* We only support one client */
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev,
|
||||
"no algorithms associated to the i2c bus\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
client->driver = &tpm_tis_i2c_driver;
|
||||
tpm_dev.client = client;
|
||||
rc = tpm_tis_i2c_init(&client->dev);
|
||||
if (rc != 0) {
|
||||
client->driver = NULL;
|
||||
tpm_dev.client = NULL;
|
||||
rc = -ENODEV;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __devexit tpm_tis_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tpm_chip *chip = tpm_dev.chip;
|
||||
release_locality(chip, chip->vendor.locality, 1);
|
||||
|
||||
/* close file handles */
|
||||
tpm_dev_vendor_release(chip);
|
||||
|
||||
/* remove hardware */
|
||||
tpm_remove_hardware(chip->dev);
|
||||
|
||||
/* reset these pointers, otherwise we oops */
|
||||
chip->dev->release = NULL;
|
||||
chip->release = NULL;
|
||||
tpm_dev.client = NULL;
|
||||
dev_set_drvdata(chip->dev, chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver tpm_tis_i2c_driver = {
|
||||
|
||||
.id_table = tpm_tis_i2c_table,
|
||||
.probe = tpm_tis_i2c_probe,
|
||||
.remove = tpm_tis_i2c_remove,
|
||||
.driver = {
|
||||
.name = "tpm_i2c_infineon",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &tpm_tis_i2c_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(tpm_tis_i2c_driver);
|
||||
MODULE_AUTHOR("Peter Huewe <peter.huewe@infineon.com>");
|
||||
MODULE_DESCRIPTION("TPM TIS I2C Infineon Driver");
|
||||
MODULE_VERSION("2.1.5");
|
||||
MODULE_LICENSE("GPL");
|
||||
749
drivers/char/tpm/tpm_ibmvtpm.c
Normal file
749
drivers/char/tpm/tpm_ibmvtpm.c
Normal file
@@ -0,0 +1,749 @@
|
||||
/*
|
||||
* Copyright (C) 2012 IBM Corporation
|
||||
*
|
||||
* Author: Ashley Lai <adlai@us.ibm.com>
|
||||
*
|
||||
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
||||
*
|
||||
* Device driver for TCG/TCPA TPM (trusted platform module).
|
||||
* Specifications at www.trustedcomputinggroup.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, version 2 of the
|
||||
* License.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/vio.h>
|
||||
#include <asm/irq.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/prom.h>
|
||||
|
||||
#include "tpm.h"
|
||||
#include "tpm_ibmvtpm.h"
|
||||
|
||||
static const char tpm_ibmvtpm_driver_name[] = "tpm_ibmvtpm";
|
||||
|
||||
static struct vio_device_id tpm_ibmvtpm_device_table[] __devinitdata = {
|
||||
{ "IBM,vtpm", "IBM,vtpm"},
|
||||
{ "", "" }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(vio, tpm_ibmvtpm_device_table);
|
||||
|
||||
DECLARE_WAIT_QUEUE_HEAD(wq);
|
||||
|
||||
/**
|
||||
* ibmvtpm_send_crq - Send a CRQ request
|
||||
* @vdev: vio device struct
|
||||
* @w1: first word
|
||||
* @w2: second word
|
||||
*
|
||||
* Return value:
|
||||
* 0 -Sucess
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int ibmvtpm_send_crq(struct vio_dev *vdev, u64 w1, u64 w2)
|
||||
{
|
||||
return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, w1, w2);
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_get_data - Retrieve ibm vtpm data
|
||||
* @dev: device struct
|
||||
*
|
||||
* Return value:
|
||||
* vtpm device struct
|
||||
*/
|
||||
static struct ibmvtpm_dev *ibmvtpm_get_data(const struct device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||
if (chip)
|
||||
return (struct ibmvtpm_dev *)chip->vendor.data;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_ibmvtpm_recv - Receive data after send
|
||||
* @chip: tpm chip struct
|
||||
* @buf: buffer to read
|
||||
* count: size of buffer
|
||||
*
|
||||
* Return value:
|
||||
* Number of bytes read
|
||||
*/
|
||||
static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm;
|
||||
u16 len;
|
||||
|
||||
ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data;
|
||||
|
||||
if (!ibmvtpm->rtce_buf) {
|
||||
dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
wait_event_interruptible(wq, ibmvtpm->crq_res.len != 0);
|
||||
|
||||
if (count < ibmvtpm->crq_res.len) {
|
||||
dev_err(ibmvtpm->dev,
|
||||
"Invalid size in recv: count=%ld, crq_size=%d\n",
|
||||
count, ibmvtpm->crq_res.len);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
spin_lock(&ibmvtpm->rtce_lock);
|
||||
memcpy((void *)buf, (void *)ibmvtpm->rtce_buf, ibmvtpm->crq_res.len);
|
||||
memset(ibmvtpm->rtce_buf, 0, ibmvtpm->crq_res.len);
|
||||
ibmvtpm->crq_res.valid = 0;
|
||||
ibmvtpm->crq_res.msg = 0;
|
||||
len = ibmvtpm->crq_res.len;
|
||||
ibmvtpm->crq_res.len = 0;
|
||||
spin_unlock(&ibmvtpm->rtce_lock);
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_ibmvtpm_send - Send tpm request
|
||||
* @chip: tpm chip struct
|
||||
* @buf: buffer contains data to send
|
||||
* count: size of buffer
|
||||
*
|
||||
* Return value:
|
||||
* Number of bytes sent
|
||||
*/
|
||||
static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm;
|
||||
struct ibmvtpm_crq crq;
|
||||
u64 *word = (u64 *) &crq;
|
||||
int rc;
|
||||
|
||||
ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data;
|
||||
|
||||
if (!ibmvtpm->rtce_buf) {
|
||||
dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count > ibmvtpm->rtce_size) {
|
||||
dev_err(ibmvtpm->dev,
|
||||
"Invalid size in send: count=%ld, rtce_size=%d\n",
|
||||
count, ibmvtpm->rtce_size);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
spin_lock(&ibmvtpm->rtce_lock);
|
||||
memcpy((void *)ibmvtpm->rtce_buf, (void *)buf, count);
|
||||
crq.valid = (u8)IBMVTPM_VALID_CMD;
|
||||
crq.msg = (u8)VTPM_TPM_COMMAND;
|
||||
crq.len = (u16)count;
|
||||
crq.data = ibmvtpm->rtce_dma_handle;
|
||||
|
||||
rc = ibmvtpm_send_crq(ibmvtpm->vdev, word[0], word[1]);
|
||||
if (rc != H_SUCCESS) {
|
||||
dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc);
|
||||
rc = 0;
|
||||
} else
|
||||
rc = count;
|
||||
|
||||
spin_unlock(&ibmvtpm->rtce_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void tpm_ibmvtpm_cancel(struct tpm_chip *chip)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static u8 tpm_ibmvtpm_status(struct tpm_chip *chip)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_crq_get_rtce_size - Send a CRQ request to get rtce size
|
||||
* @ibmvtpm: vtpm device struct
|
||||
*
|
||||
* Return value:
|
||||
* 0 - Success
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int ibmvtpm_crq_get_rtce_size(struct ibmvtpm_dev *ibmvtpm)
|
||||
{
|
||||
struct ibmvtpm_crq crq;
|
||||
u64 *buf = (u64 *) &crq;
|
||||
int rc;
|
||||
|
||||
crq.valid = (u8)IBMVTPM_VALID_CMD;
|
||||
crq.msg = (u8)VTPM_GET_RTCE_BUFFER_SIZE;
|
||||
|
||||
rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]);
|
||||
if (rc != H_SUCCESS)
|
||||
dev_err(ibmvtpm->dev,
|
||||
"ibmvtpm_crq_get_rtce_size failed rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_crq_get_version - Send a CRQ request to get vtpm version
|
||||
* - Note that this is vtpm version and not tpm version
|
||||
* @ibmvtpm: vtpm device struct
|
||||
*
|
||||
* Return value:
|
||||
* 0 - Success
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int ibmvtpm_crq_get_version(struct ibmvtpm_dev *ibmvtpm)
|
||||
{
|
||||
struct ibmvtpm_crq crq;
|
||||
u64 *buf = (u64 *) &crq;
|
||||
int rc;
|
||||
|
||||
crq.valid = (u8)IBMVTPM_VALID_CMD;
|
||||
crq.msg = (u8)VTPM_GET_VERSION;
|
||||
|
||||
rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]);
|
||||
if (rc != H_SUCCESS)
|
||||
dev_err(ibmvtpm->dev,
|
||||
"ibmvtpm_crq_get_version failed rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_crq_send_init_complete - Send a CRQ initialize complete message
|
||||
* @ibmvtpm: vtpm device struct
|
||||
*
|
||||
* Return value:
|
||||
* 0 - Success
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int ibmvtpm_crq_send_init_complete(struct ibmvtpm_dev *ibmvtpm)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ibmvtpm_send_crq(ibmvtpm->vdev, INIT_CRQ_COMP_CMD, 0);
|
||||
if (rc != H_SUCCESS)
|
||||
dev_err(ibmvtpm->dev,
|
||||
"ibmvtpm_crq_send_init_complete failed rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_crq_send_init - Send a CRQ initialize message
|
||||
* @ibmvtpm: vtpm device struct
|
||||
*
|
||||
* Return value:
|
||||
* 0 - Success
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ibmvtpm_send_crq(ibmvtpm->vdev, INIT_CRQ_CMD, 0);
|
||||
if (rc != H_SUCCESS)
|
||||
dev_err(ibmvtpm->dev,
|
||||
"ibmvtpm_crq_send_init failed rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_ibmvtpm_remove - ibm vtpm remove entry point
|
||||
* @vdev: vio device struct
|
||||
*
|
||||
* Return value:
|
||||
* 0
|
||||
*/
|
||||
static int __devexit tpm_ibmvtpm_remove(struct vio_dev *vdev)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev);
|
||||
int rc = 0;
|
||||
|
||||
free_irq(vdev->irq, ibmvtpm);
|
||||
tasklet_kill(&ibmvtpm->tasklet);
|
||||
|
||||
do {
|
||||
if (rc)
|
||||
msleep(100);
|
||||
rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
|
||||
} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
|
||||
|
||||
dma_unmap_single(ibmvtpm->dev, ibmvtpm->crq_dma_handle,
|
||||
CRQ_RES_BUF_SIZE, DMA_BIDIRECTIONAL);
|
||||
free_page((unsigned long)ibmvtpm->crq_queue.crq_addr);
|
||||
|
||||
if (ibmvtpm->rtce_buf) {
|
||||
dma_unmap_single(ibmvtpm->dev, ibmvtpm->rtce_dma_handle,
|
||||
ibmvtpm->rtce_size, DMA_BIDIRECTIONAL);
|
||||
kfree(ibmvtpm->rtce_buf);
|
||||
}
|
||||
|
||||
tpm_remove_hardware(ibmvtpm->dev);
|
||||
|
||||
kfree(ibmvtpm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_ibmvtpm_get_desired_dma - Get DMA size needed by this driver
|
||||
* @vdev: vio device struct
|
||||
*
|
||||
* Return value:
|
||||
* Number of bytes the driver needs to DMA map
|
||||
*/
|
||||
static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev);
|
||||
return CRQ_RES_BUF_SIZE + ibmvtpm->rtce_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_ibmvtpm_suspend - Suspend
|
||||
* @dev: device struct
|
||||
*
|
||||
* Return value:
|
||||
* 0
|
||||
*/
|
||||
static int tpm_ibmvtpm_suspend(struct device *dev)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev);
|
||||
struct ibmvtpm_crq crq;
|
||||
u64 *buf = (u64 *) &crq;
|
||||
int rc = 0;
|
||||
|
||||
crq.valid = (u8)IBMVTPM_VALID_CMD;
|
||||
crq.msg = (u8)VTPM_PREPARE_TO_SUSPEND;
|
||||
|
||||
rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]);
|
||||
if (rc != H_SUCCESS)
|
||||
dev_err(ibmvtpm->dev,
|
||||
"tpm_ibmvtpm_suspend failed rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_reset_crq - Reset CRQ
|
||||
* @ibmvtpm: ibm vtpm struct
|
||||
*
|
||||
* Return value:
|
||||
* 0 - Success
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
do {
|
||||
if (rc)
|
||||
msleep(100);
|
||||
rc = plpar_hcall_norets(H_FREE_CRQ,
|
||||
ibmvtpm->vdev->unit_address);
|
||||
} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
|
||||
|
||||
memset(ibmvtpm->crq_queue.crq_addr, 0, CRQ_RES_BUF_SIZE);
|
||||
ibmvtpm->crq_queue.index = 0;
|
||||
|
||||
return plpar_hcall_norets(H_REG_CRQ, ibmvtpm->vdev->unit_address,
|
||||
ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_ibmvtpm_resume - Resume from suspend
|
||||
* @dev: device struct
|
||||
*
|
||||
* Return value:
|
||||
* 0
|
||||
*/
|
||||
static int tpm_ibmvtpm_resume(struct device *dev)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev);
|
||||
unsigned long flags;
|
||||
int rc = 0;
|
||||
|
||||
do {
|
||||
if (rc)
|
||||
msleep(100);
|
||||
rc = plpar_hcall_norets(H_ENABLE_CRQ,
|
||||
ibmvtpm->vdev->unit_address);
|
||||
} while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
|
||||
|
||||
if (rc) {
|
||||
dev_err(dev, "Error enabling ibmvtpm rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ibmvtpm->lock, flags);
|
||||
vio_disable_interrupts(ibmvtpm->vdev);
|
||||
tasklet_schedule(&ibmvtpm->tasklet);
|
||||
spin_unlock_irqrestore(&ibmvtpm->lock, flags);
|
||||
|
||||
rc = ibmvtpm_crq_send_init(ibmvtpm);
|
||||
if (rc)
|
||||
dev_err(dev, "Error send_init rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct file_operations ibmvtpm_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.open = tpm_open,
|
||||
.read = tpm_read,
|
||||
.write = tpm_write,
|
||||
.release = tpm_release,
|
||||
};
|
||||
|
||||
static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
|
||||
static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
|
||||
static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
|
||||
static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
|
||||
static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
|
||||
static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
|
||||
NULL);
|
||||
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
|
||||
static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
|
||||
static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
|
||||
static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
|
||||
|
||||
static struct attribute *ibmvtpm_attrs[] = {
|
||||
&dev_attr_pubek.attr,
|
||||
&dev_attr_pcrs.attr,
|
||||
&dev_attr_enabled.attr,
|
||||
&dev_attr_active.attr,
|
||||
&dev_attr_owned.attr,
|
||||
&dev_attr_temp_deactivated.attr,
|
||||
&dev_attr_caps.attr,
|
||||
&dev_attr_cancel.attr,
|
||||
&dev_attr_durations.attr,
|
||||
&dev_attr_timeouts.attr, NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group ibmvtpm_attr_grp = { .attrs = ibmvtpm_attrs };
|
||||
|
||||
static const struct tpm_vendor_specific tpm_ibmvtpm = {
|
||||
.recv = tpm_ibmvtpm_recv,
|
||||
.send = tpm_ibmvtpm_send,
|
||||
.cancel = tpm_ibmvtpm_cancel,
|
||||
.status = tpm_ibmvtpm_status,
|
||||
.req_complete_mask = 0,
|
||||
.req_complete_val = 0,
|
||||
.req_canceled = 0,
|
||||
.attr_group = &ibmvtpm_attr_grp,
|
||||
.miscdev = { .fops = &ibmvtpm_ops, },
|
||||
};
|
||||
|
||||
static const struct dev_pm_ops tpm_ibmvtpm_pm_ops = {
|
||||
.suspend = tpm_ibmvtpm_suspend,
|
||||
.resume = tpm_ibmvtpm_resume,
|
||||
};
|
||||
|
||||
/**
|
||||
* ibmvtpm_crq_get_next - Get next responded crq
|
||||
* @ibmvtpm vtpm device struct
|
||||
*
|
||||
* Return value:
|
||||
* vtpm crq pointer
|
||||
*/
|
||||
static struct ibmvtpm_crq *ibmvtpm_crq_get_next(struct ibmvtpm_dev *ibmvtpm)
|
||||
{
|
||||
struct ibmvtpm_crq_queue *crq_q = &ibmvtpm->crq_queue;
|
||||
struct ibmvtpm_crq *crq = &crq_q->crq_addr[crq_q->index];
|
||||
|
||||
if (crq->valid & VTPM_MSG_RES) {
|
||||
if (++crq_q->index == crq_q->num_entry)
|
||||
crq_q->index = 0;
|
||||
rmb();
|
||||
} else
|
||||
crq = NULL;
|
||||
return crq;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_crq_process - Process responded crq
|
||||
* @crq crq to be processed
|
||||
* @ibmvtpm vtpm device struct
|
||||
*
|
||||
* Return value:
|
||||
* Nothing
|
||||
*/
|
||||
static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq,
|
||||
struct ibmvtpm_dev *ibmvtpm)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
switch (crq->valid) {
|
||||
case VALID_INIT_CRQ:
|
||||
switch (crq->msg) {
|
||||
case INIT_CRQ_RES:
|
||||
dev_info(ibmvtpm->dev, "CRQ initialized\n");
|
||||
rc = ibmvtpm_crq_send_init_complete(ibmvtpm);
|
||||
if (rc)
|
||||
dev_err(ibmvtpm->dev, "Unable to send CRQ init complete rc=%d\n", rc);
|
||||
return;
|
||||
case INIT_CRQ_COMP_RES:
|
||||
dev_info(ibmvtpm->dev,
|
||||
"CRQ initialization completed\n");
|
||||
return;
|
||||
default:
|
||||
dev_err(ibmvtpm->dev, "Unknown crq message type: %d\n", crq->msg);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
case IBMVTPM_VALID_CMD:
|
||||
switch (crq->msg) {
|
||||
case VTPM_GET_RTCE_BUFFER_SIZE_RES:
|
||||
if (crq->len <= 0) {
|
||||
dev_err(ibmvtpm->dev, "Invalid rtce size\n");
|
||||
return;
|
||||
}
|
||||
ibmvtpm->rtce_size = crq->len;
|
||||
ibmvtpm->rtce_buf = kmalloc(ibmvtpm->rtce_size,
|
||||
GFP_KERNEL);
|
||||
if (!ibmvtpm->rtce_buf) {
|
||||
dev_err(ibmvtpm->dev, "Failed to allocate memory for rtce buffer\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ibmvtpm->rtce_dma_handle = dma_map_single(ibmvtpm->dev,
|
||||
ibmvtpm->rtce_buf, ibmvtpm->rtce_size,
|
||||
DMA_BIDIRECTIONAL);
|
||||
|
||||
if (dma_mapping_error(ibmvtpm->dev,
|
||||
ibmvtpm->rtce_dma_handle)) {
|
||||
kfree(ibmvtpm->rtce_buf);
|
||||
ibmvtpm->rtce_buf = NULL;
|
||||
dev_err(ibmvtpm->dev, "Failed to dma map rtce buffer\n");
|
||||
}
|
||||
|
||||
return;
|
||||
case VTPM_GET_VERSION_RES:
|
||||
ibmvtpm->vtpm_version = crq->data;
|
||||
return;
|
||||
case VTPM_TPM_COMMAND_RES:
|
||||
ibmvtpm->crq_res.valid = crq->valid;
|
||||
ibmvtpm->crq_res.msg = crq->msg;
|
||||
ibmvtpm->crq_res.len = crq->len;
|
||||
ibmvtpm->crq_res.data = crq->data;
|
||||
wake_up_interruptible(&wq);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_interrupt - Interrupt handler
|
||||
* @irq: irq number to handle
|
||||
* @vtpm_instance: vtpm that received interrupt
|
||||
*
|
||||
* Returns:
|
||||
* IRQ_HANDLED
|
||||
**/
|
||||
static irqreturn_t ibmvtpm_interrupt(int irq, void *vtpm_instance)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm = (struct ibmvtpm_dev *) vtpm_instance;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ibmvtpm->lock, flags);
|
||||
vio_disable_interrupts(ibmvtpm->vdev);
|
||||
tasklet_schedule(&ibmvtpm->tasklet);
|
||||
spin_unlock_irqrestore(&ibmvtpm->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_tasklet - Interrupt handler tasklet
|
||||
* @data: ibm vtpm device struct
|
||||
*
|
||||
* Returns:
|
||||
* Nothing
|
||||
**/
|
||||
static void ibmvtpm_tasklet(void *data)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm = data;
|
||||
struct ibmvtpm_crq *crq;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ibmvtpm->lock, flags);
|
||||
while ((crq = ibmvtpm_crq_get_next(ibmvtpm)) != NULL) {
|
||||
ibmvtpm_crq_process(crq, ibmvtpm);
|
||||
crq->valid = 0;
|
||||
wmb();
|
||||
}
|
||||
|
||||
vio_enable_interrupts(ibmvtpm->vdev);
|
||||
spin_unlock_irqrestore(&ibmvtpm->lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_ibmvtpm_probe - ibm vtpm initialize entry point
|
||||
* @vio_dev: vio device struct
|
||||
* @id: vio device id struct
|
||||
*
|
||||
* Return value:
|
||||
* 0 - Success
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int __devinit tpm_ibmvtpm_probe(struct vio_dev *vio_dev,
|
||||
const struct vio_device_id *id)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm;
|
||||
struct device *dev = &vio_dev->dev;
|
||||
struct ibmvtpm_crq_queue *crq_q;
|
||||
struct tpm_chip *chip;
|
||||
int rc = -ENOMEM, rc1;
|
||||
|
||||
chip = tpm_register_hardware(dev, &tpm_ibmvtpm);
|
||||
if (!chip) {
|
||||
dev_err(dev, "tpm_register_hardware failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ibmvtpm = kzalloc(sizeof(struct ibmvtpm_dev), GFP_KERNEL);
|
||||
if (!ibmvtpm) {
|
||||
dev_err(dev, "kzalloc for ibmvtpm failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
crq_q = &ibmvtpm->crq_queue;
|
||||
crq_q->crq_addr = (struct ibmvtpm_crq *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!crq_q->crq_addr) {
|
||||
dev_err(dev, "Unable to allocate memory for crq_addr\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
crq_q->num_entry = CRQ_RES_BUF_SIZE / sizeof(*crq_q->crq_addr);
|
||||
ibmvtpm->crq_dma_handle = dma_map_single(dev, crq_q->crq_addr,
|
||||
CRQ_RES_BUF_SIZE,
|
||||
DMA_BIDIRECTIONAL);
|
||||
|
||||
if (dma_mapping_error(dev, ibmvtpm->crq_dma_handle)) {
|
||||
dev_err(dev, "dma mapping failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = plpar_hcall_norets(H_REG_CRQ, vio_dev->unit_address,
|
||||
ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE);
|
||||
if (rc == H_RESOURCE)
|
||||
rc = ibmvtpm_reset_crq(ibmvtpm);
|
||||
|
||||
if (rc) {
|
||||
dev_err(dev, "Unable to register CRQ rc=%d\n", rc);
|
||||
goto reg_crq_cleanup;
|
||||
}
|
||||
|
||||
tasklet_init(&ibmvtpm->tasklet, (void *)ibmvtpm_tasklet,
|
||||
(unsigned long)ibmvtpm);
|
||||
|
||||
rc = request_irq(vio_dev->irq, ibmvtpm_interrupt, 0,
|
||||
tpm_ibmvtpm_driver_name, ibmvtpm);
|
||||
if (rc) {
|
||||
dev_err(dev, "Error %d register irq 0x%x\n", rc, vio_dev->irq);
|
||||
goto init_irq_cleanup;
|
||||
}
|
||||
|
||||
rc = vio_enable_interrupts(vio_dev);
|
||||
if (rc) {
|
||||
dev_err(dev, "Error %d enabling interrupts\n", rc);
|
||||
goto init_irq_cleanup;
|
||||
}
|
||||
|
||||
crq_q->index = 0;
|
||||
|
||||
ibmvtpm->dev = dev;
|
||||
ibmvtpm->vdev = vio_dev;
|
||||
chip->vendor.data = (void *)ibmvtpm;
|
||||
|
||||
spin_lock_init(&ibmvtpm->lock);
|
||||
spin_lock_init(&ibmvtpm->rtce_lock);
|
||||
|
||||
rc = ibmvtpm_crq_send_init(ibmvtpm);
|
||||
if (rc)
|
||||
goto init_irq_cleanup;
|
||||
|
||||
rc = ibmvtpm_crq_get_version(ibmvtpm);
|
||||
if (rc)
|
||||
goto init_irq_cleanup;
|
||||
|
||||
rc = ibmvtpm_crq_get_rtce_size(ibmvtpm);
|
||||
if (rc)
|
||||
goto init_irq_cleanup;
|
||||
|
||||
return rc;
|
||||
init_irq_cleanup:
|
||||
tasklet_kill(&ibmvtpm->tasklet);
|
||||
do {
|
||||
rc1 = plpar_hcall_norets(H_FREE_CRQ, vio_dev->unit_address);
|
||||
} while (rc1 == H_BUSY || H_IS_LONG_BUSY(rc1));
|
||||
reg_crq_cleanup:
|
||||
dma_unmap_single(dev, ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE,
|
||||
DMA_BIDIRECTIONAL);
|
||||
cleanup:
|
||||
if (ibmvtpm) {
|
||||
if (crq_q->crq_addr)
|
||||
free_page((unsigned long)crq_q->crq_addr);
|
||||
kfree(ibmvtpm);
|
||||
}
|
||||
|
||||
tpm_remove_hardware(dev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct vio_driver ibmvtpm_driver = {
|
||||
.id_table = tpm_ibmvtpm_device_table,
|
||||
.probe = tpm_ibmvtpm_probe,
|
||||
.remove = tpm_ibmvtpm_remove,
|
||||
.get_desired_dma = tpm_ibmvtpm_get_desired_dma,
|
||||
.name = tpm_ibmvtpm_driver_name,
|
||||
.pm = &tpm_ibmvtpm_pm_ops,
|
||||
};
|
||||
|
||||
/**
|
||||
* ibmvtpm_module_init - Initialize ibm vtpm module
|
||||
*
|
||||
* Return value:
|
||||
* 0 -Success
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int __init ibmvtpm_module_init(void)
|
||||
{
|
||||
return vio_register_driver(&ibmvtpm_driver);
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_module_exit - Teardown ibm vtpm module
|
||||
*
|
||||
* Return value:
|
||||
* Nothing
|
||||
*/
|
||||
static void __exit ibmvtpm_module_exit(void)
|
||||
{
|
||||
vio_unregister_driver(&ibmvtpm_driver);
|
||||
}
|
||||
|
||||
module_init(ibmvtpm_module_init);
|
||||
module_exit(ibmvtpm_module_exit);
|
||||
|
||||
MODULE_AUTHOR("adlai@us.ibm.com");
|
||||
MODULE_DESCRIPTION("IBM vTPM Driver");
|
||||
MODULE_VERSION("1.0");
|
||||
MODULE_LICENSE("GPL");
|
||||
77
drivers/char/tpm/tpm_ibmvtpm.h
Normal file
77
drivers/char/tpm/tpm_ibmvtpm.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2012 IBM Corporation
|
||||
*
|
||||
* Author: Ashley Lai <adlai@us.ibm.com>
|
||||
*
|
||||
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
||||
*
|
||||
* Device driver for TCG/TCPA TPM (trusted platform module).
|
||||
* Specifications at www.trustedcomputinggroup.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, version 2 of the
|
||||
* License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TPM_IBMVTPM_H__
|
||||
#define __TPM_IBMVTPM_H__
|
||||
|
||||
/* vTPM Message Format 1 */
|
||||
struct ibmvtpm_crq {
|
||||
u8 valid;
|
||||
u8 msg;
|
||||
u16 len;
|
||||
u32 data;
|
||||
u64 reserved;
|
||||
} __attribute__((packed, aligned(8)));
|
||||
|
||||
struct ibmvtpm_crq_queue {
|
||||
struct ibmvtpm_crq *crq_addr;
|
||||
u32 index;
|
||||
u32 num_entry;
|
||||
};
|
||||
|
||||
struct ibmvtpm_dev {
|
||||
struct device *dev;
|
||||
struct vio_dev *vdev;
|
||||
struct ibmvtpm_crq_queue crq_queue;
|
||||
dma_addr_t crq_dma_handle;
|
||||
spinlock_t lock;
|
||||
struct tasklet_struct tasklet;
|
||||
u32 rtce_size;
|
||||
void __iomem *rtce_buf;
|
||||
dma_addr_t rtce_dma_handle;
|
||||
spinlock_t rtce_lock;
|
||||
struct ibmvtpm_crq crq_res;
|
||||
u32 vtpm_version;
|
||||
};
|
||||
|
||||
#define CRQ_RES_BUF_SIZE PAGE_SIZE
|
||||
|
||||
/* Initialize CRQ */
|
||||
#define INIT_CRQ_CMD 0xC001000000000000LL /* Init cmd */
|
||||
#define INIT_CRQ_COMP_CMD 0xC002000000000000LL /* Init complete cmd */
|
||||
#define INIT_CRQ_RES 0x01 /* Init respond */
|
||||
#define INIT_CRQ_COMP_RES 0x02 /* Init complete respond */
|
||||
#define VALID_INIT_CRQ 0xC0 /* Valid command for init crq */
|
||||
|
||||
/* vTPM CRQ response is the message type | 0x80 */
|
||||
#define VTPM_MSG_RES 0x80
|
||||
#define IBMVTPM_VALID_CMD 0x80
|
||||
|
||||
/* vTPM CRQ message types */
|
||||
#define VTPM_GET_VERSION 0x01
|
||||
#define VTPM_GET_VERSION_RES (0x01 | VTPM_MSG_RES)
|
||||
|
||||
#define VTPM_TPM_COMMAND 0x02
|
||||
#define VTPM_TPM_COMMAND_RES (0x02 | VTPM_MSG_RES)
|
||||
|
||||
#define VTPM_GET_RTCE_BUFFER_SIZE 0x03
|
||||
#define VTPM_GET_RTCE_BUFFER_SIZE_RES (0x03 | VTPM_MSG_RES)
|
||||
|
||||
#define VTPM_PREPARE_TO_SUSPEND 0x04
|
||||
#define VTPM_PREPARE_TO_SUSPEND_RES (0x04 | VTPM_MSG_RES)
|
||||
|
||||
#endif
|
||||
73
drivers/char/tpm/tpm_of.c
Normal file
73
drivers/char/tpm/tpm_of.c
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2012 IBM Corporation
|
||||
*
|
||||
* Author: Ashley Lai <adlai@us.ibm.com>
|
||||
*
|
||||
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
||||
*
|
||||
* Read the event log created by the firmware on PPC64
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "tpm.h"
|
||||
#include "tpm_eventlog.h"
|
||||
|
||||
int read_log(struct tpm_bios_log *log)
|
||||
{
|
||||
struct device_node *np;
|
||||
const u32 *sizep;
|
||||
const __be64 *basep;
|
||||
|
||||
if (log->bios_event_log != NULL) {
|
||||
pr_err("%s: ERROR - Eventlog already initialized\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
np = of_find_node_by_name(NULL, "ibm,vtpm");
|
||||
if (!np) {
|
||||
pr_err("%s: ERROR - IBMVTPM not supported\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sizep = of_get_property(np, "linux,sml-size", NULL);
|
||||
if (sizep == NULL) {
|
||||
pr_err("%s: ERROR - SML size not found\n", __func__);
|
||||
goto cleanup_eio;
|
||||
}
|
||||
if (*sizep == 0) {
|
||||
pr_err("%s: ERROR - event log area empty\n", __func__);
|
||||
goto cleanup_eio;
|
||||
}
|
||||
|
||||
basep = of_get_property(np, "linux,sml-base", NULL);
|
||||
if (basep == NULL) {
|
||||
pr_err(KERN_ERR "%s: ERROR - SML not found\n", __func__);
|
||||
goto cleanup_eio;
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
log->bios_event_log = kmalloc(*sizep, GFP_KERNEL);
|
||||
if (!log->bios_event_log) {
|
||||
pr_err("%s: ERROR - Not enough memory for BIOS measurements\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
log->bios_event_log_end = log->bios_event_log + *sizep;
|
||||
|
||||
memcpy(log->bios_event_log, __va(be64_to_cpup(basep)), *sizep);
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup_eio:
|
||||
of_node_put(np);
|
||||
return -EIO;
|
||||
}
|
||||
463
drivers/char/tpm/tpm_ppi.c
Normal file
463
drivers/char/tpm/tpm_ppi.c
Normal file
@@ -0,0 +1,463 @@
|
||||
#include <linux/acpi.h>
|
||||
#include <acpi/acpi_drivers.h>
|
||||
#include "tpm.h"
|
||||
|
||||
static const u8 tpm_ppi_uuid[] = {
|
||||
0xA6, 0xFA, 0xDD, 0x3D,
|
||||
0x1B, 0x36,
|
||||
0xB4, 0x4E,
|
||||
0xA4, 0x24,
|
||||
0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53
|
||||
};
|
||||
static char *tpm_device_name = "TPM";
|
||||
|
||||
#define TPM_PPI_REVISION_ID 1
|
||||
#define TPM_PPI_FN_VERSION 1
|
||||
#define TPM_PPI_FN_SUBREQ 2
|
||||
#define TPM_PPI_FN_GETREQ 3
|
||||
#define TPM_PPI_FN_GETACT 4
|
||||
#define TPM_PPI_FN_GETRSP 5
|
||||
#define TPM_PPI_FN_SUBREQ2 7
|
||||
#define TPM_PPI_FN_GETOPR 8
|
||||
#define PPI_TPM_REQ_MAX 22
|
||||
#define PPI_VS_REQ_START 128
|
||||
#define PPI_VS_REQ_END 255
|
||||
#define PPI_VERSION_LEN 3
|
||||
|
||||
static acpi_status ppi_callback(acpi_handle handle, u32 level, void *context,
|
||||
void **return_value)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
|
||||
if (strstr(buffer.pointer, context) != NULL) {
|
||||
*return_value = handle;
|
||||
kfree(buffer.pointer);
|
||||
return AE_CTRL_TERMINATE;
|
||||
}
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static inline void ppi_assign_params(union acpi_object params[4],
|
||||
u64 function_num)
|
||||
{
|
||||
params[0].type = ACPI_TYPE_BUFFER;
|
||||
params[0].buffer.length = sizeof(tpm_ppi_uuid);
|
||||
params[0].buffer.pointer = (char *)tpm_ppi_uuid;
|
||||
params[1].type = ACPI_TYPE_INTEGER;
|
||||
params[1].integer.value = TPM_PPI_REVISION_ID;
|
||||
params[2].type = ACPI_TYPE_INTEGER;
|
||||
params[2].integer.value = function_num;
|
||||
params[3].type = ACPI_TYPE_PACKAGE;
|
||||
params[3].package.count = 0;
|
||||
params[3].package.elements = NULL;
|
||||
}
|
||||
|
||||
static ssize_t tpm_show_ppi_version(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
struct acpi_object_list input;
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object params[4];
|
||||
union acpi_object *obj;
|
||||
|
||||
input.count = 4;
|
||||
ppi_assign_params(params, TPM_PPI_FN_VERSION);
|
||||
input.pointer = params;
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, ppi_callback, NULL,
|
||||
tpm_device_name, &handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENXIO;
|
||||
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_STRING);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
obj = (union acpi_object *)output.pointer;
|
||||
status = scnprintf(buf, PAGE_SIZE, "%s\n", obj->string.pointer);
|
||||
kfree(output.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t tpm_show_ppi_request(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
struct acpi_object_list input;
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object params[4];
|
||||
union acpi_object *ret_obj;
|
||||
|
||||
input.count = 4;
|
||||
ppi_assign_params(params, TPM_PPI_FN_GETREQ);
|
||||
input.pointer = params;
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, ppi_callback, NULL,
|
||||
tpm_device_name, &handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENXIO;
|
||||
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_PACKAGE);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* output.pointer should be of package type, including two integers.
|
||||
* The first is function return code, 0 means success and 1 means
|
||||
* error. The second is pending TPM operation requested by the OS, 0
|
||||
* means none and >0 means operation value.
|
||||
*/
|
||||
ret_obj = ((union acpi_object *)output.pointer)->package.elements;
|
||||
if (ret_obj->type == ACPI_TYPE_INTEGER) {
|
||||
if (ret_obj->integer.value) {
|
||||
status = -EFAULT;
|
||||
goto cleanup;
|
||||
}
|
||||
ret_obj++;
|
||||
if (ret_obj->type == ACPI_TYPE_INTEGER)
|
||||
status = scnprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
ret_obj->integer.value);
|
||||
else
|
||||
status = -EINVAL;
|
||||
} else {
|
||||
status = -EINVAL;
|
||||
}
|
||||
cleanup:
|
||||
kfree(output.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t tpm_store_ppi_request(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
char version[PPI_VERSION_LEN + 1];
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
struct acpi_object_list input;
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object params[4];
|
||||
union acpi_object obj;
|
||||
u32 req;
|
||||
u64 ret;
|
||||
|
||||
input.count = 4;
|
||||
ppi_assign_params(params, TPM_PPI_FN_VERSION);
|
||||
input.pointer = params;
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, ppi_callback, NULL,
|
||||
tpm_device_name, &handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENXIO;
|
||||
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_STRING);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
strncpy(version,
|
||||
((union acpi_object *)output.pointer)->string.pointer,
|
||||
PPI_VERSION_LEN);
|
||||
kfree(output.pointer);
|
||||
output.length = ACPI_ALLOCATE_BUFFER;
|
||||
output.pointer = NULL;
|
||||
/*
|
||||
* the function to submit TPM operation request to pre-os environment
|
||||
* is updated with function index from SUBREQ to SUBREQ2 since PPI
|
||||
* version 1.1
|
||||
*/
|
||||
if (strcmp(version, "1.1") == -1)
|
||||
params[2].integer.value = TPM_PPI_FN_SUBREQ;
|
||||
else
|
||||
params[2].integer.value = TPM_PPI_FN_SUBREQ2;
|
||||
/*
|
||||
* PPI spec defines params[3].type as ACPI_TYPE_PACKAGE. Some BIOS
|
||||
* accept buffer/string/integer type, but some BIOS accept buffer/
|
||||
* string/package type. For PPI version 1.0 and 1.1, use buffer type
|
||||
* for compatibility, and use package type since 1.2 according to spec.
|
||||
*/
|
||||
if (strcmp(version, "1.2") == -1) {
|
||||
params[3].type = ACPI_TYPE_BUFFER;
|
||||
params[3].buffer.length = sizeof(req);
|
||||
sscanf(buf, "%d", &req);
|
||||
params[3].buffer.pointer = (char *)&req;
|
||||
} else {
|
||||
params[3].package.count = 1;
|
||||
obj.type = ACPI_TYPE_INTEGER;
|
||||
sscanf(buf, "%llu", &obj.integer.value);
|
||||
params[3].package.elements = &obj;
|
||||
}
|
||||
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_INTEGER);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
ret = ((union acpi_object *)output.pointer)->integer.value;
|
||||
if (ret == 0)
|
||||
status = (acpi_status)count;
|
||||
else if (ret == 1)
|
||||
status = -EPERM;
|
||||
else
|
||||
status = -EFAULT;
|
||||
kfree(output.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t tpm_show_ppi_transition_action(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
char version[PPI_VERSION_LEN + 1];
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
struct acpi_object_list input;
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object params[4];
|
||||
u32 ret;
|
||||
char *info[] = {
|
||||
"None",
|
||||
"Shutdown",
|
||||
"Reboot",
|
||||
"OS Vendor-specific",
|
||||
"Error",
|
||||
};
|
||||
input.count = 4;
|
||||
ppi_assign_params(params, TPM_PPI_FN_VERSION);
|
||||
input.pointer = params;
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, ppi_callback, NULL,
|
||||
tpm_device_name, &handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENXIO;
|
||||
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_STRING);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
strncpy(version,
|
||||
((union acpi_object *)output.pointer)->string.pointer,
|
||||
PPI_VERSION_LEN);
|
||||
/*
|
||||
* PPI spec defines params[3].type as empty package, but some platforms
|
||||
* (e.g. Capella with PPI 1.0) need integer/string/buffer type, so for
|
||||
* compatibility, define params[3].type as buffer, if PPI version < 1.2
|
||||
*/
|
||||
if (strcmp(version, "1.2") == -1) {
|
||||
params[3].type = ACPI_TYPE_BUFFER;
|
||||
params[3].buffer.length = 0;
|
||||
params[3].buffer.pointer = NULL;
|
||||
}
|
||||
params[2].integer.value = TPM_PPI_FN_GETACT;
|
||||
kfree(output.pointer);
|
||||
output.length = ACPI_ALLOCATE_BUFFER;
|
||||
output.pointer = NULL;
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_INTEGER);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
ret = ((union acpi_object *)output.pointer)->integer.value;
|
||||
if (ret < ARRAY_SIZE(info) - 1)
|
||||
status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]);
|
||||
else
|
||||
status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret,
|
||||
info[ARRAY_SIZE(info)-1]);
|
||||
kfree(output.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t tpm_show_ppi_response(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
struct acpi_object_list input;
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object params[4];
|
||||
union acpi_object *ret_obj;
|
||||
u64 req;
|
||||
|
||||
input.count = 4;
|
||||
ppi_assign_params(params, TPM_PPI_FN_GETRSP);
|
||||
input.pointer = params;
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, ppi_callback, NULL,
|
||||
tpm_device_name, &handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENXIO;
|
||||
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_PACKAGE);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* parameter output.pointer should be of package type, including
|
||||
* 3 integers. The first means function return code, the second means
|
||||
* most recent TPM operation request, and the last means response to
|
||||
* the most recent TPM operation request. Only if the first is 0, and
|
||||
* the second integer is not 0, the response makes sense.
|
||||
*/
|
||||
ret_obj = ((union acpi_object *)output.pointer)->package.elements;
|
||||
if (ret_obj->type != ACPI_TYPE_INTEGER) {
|
||||
status = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
if (ret_obj->integer.value) {
|
||||
status = -EFAULT;
|
||||
goto cleanup;
|
||||
}
|
||||
ret_obj++;
|
||||
if (ret_obj->type != ACPI_TYPE_INTEGER) {
|
||||
status = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
if (ret_obj->integer.value) {
|
||||
req = ret_obj->integer.value;
|
||||
ret_obj++;
|
||||
if (ret_obj->type != ACPI_TYPE_INTEGER) {
|
||||
status = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
if (ret_obj->integer.value == 0)
|
||||
status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
|
||||
"0: Success");
|
||||
else if (ret_obj->integer.value == 0xFFFFFFF0)
|
||||
status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
|
||||
"0xFFFFFFF0: User Abort");
|
||||
else if (ret_obj->integer.value == 0xFFFFFFF1)
|
||||
status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
|
||||
"0xFFFFFFF1: BIOS Failure");
|
||||
else if (ret_obj->integer.value >= 1 &&
|
||||
ret_obj->integer.value <= 0x00000FFF)
|
||||
status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
|
||||
req, ret_obj->integer.value,
|
||||
"Corresponding TPM error");
|
||||
else
|
||||
status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
|
||||
req, ret_obj->integer.value,
|
||||
"Error");
|
||||
} else {
|
||||
status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n",
|
||||
ret_obj->integer.value, "No Recent Request");
|
||||
}
|
||||
cleanup:
|
||||
kfree(output.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t show_ppi_operations(char *buf, u32 start, u32 end)
|
||||
{
|
||||
char *str = buf;
|
||||
char version[PPI_VERSION_LEN];
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
struct acpi_object_list input;
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object params[4];
|
||||
union acpi_object obj;
|
||||
int i;
|
||||
u32 ret;
|
||||
char *info[] = {
|
||||
"Not implemented",
|
||||
"BIOS only",
|
||||
"Blocked for OS by BIOS",
|
||||
"User required",
|
||||
"User not required",
|
||||
};
|
||||
input.count = 4;
|
||||
ppi_assign_params(params, TPM_PPI_FN_VERSION);
|
||||
input.pointer = params;
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, ppi_callback, NULL,
|
||||
tpm_device_name, &handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENXIO;
|
||||
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_STRING);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
|
||||
strncpy(version,
|
||||
((union acpi_object *)output.pointer)->string.pointer,
|
||||
PPI_VERSION_LEN);
|
||||
kfree(output.pointer);
|
||||
output.length = ACPI_ALLOCATE_BUFFER;
|
||||
output.pointer = NULL;
|
||||
if (strcmp(version, "1.2") == -1)
|
||||
return -EPERM;
|
||||
|
||||
params[2].integer.value = TPM_PPI_FN_GETOPR;
|
||||
params[3].package.count = 1;
|
||||
obj.type = ACPI_TYPE_INTEGER;
|
||||
params[3].package.elements = &obj;
|
||||
for (i = start; i <= end; i++) {
|
||||
obj.integer.value = i;
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM",
|
||||
&input, &output, ACPI_TYPE_INTEGER);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
|
||||
ret = ((union acpi_object *)output.pointer)->integer.value;
|
||||
if (ret > 0 && ret < ARRAY_SIZE(info))
|
||||
str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n",
|
||||
i, ret, info[ret]);
|
||||
kfree(output.pointer);
|
||||
output.length = ACPI_ALLOCATE_BUFFER;
|
||||
output.pointer = NULL;
|
||||
}
|
||||
return str - buf;
|
||||
}
|
||||
|
||||
static ssize_t tpm_show_ppi_tcg_operations(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return show_ppi_operations(buf, 0, PPI_TPM_REQ_MAX);
|
||||
}
|
||||
|
||||
static ssize_t tpm_show_ppi_vs_operations(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return show_ppi_operations(buf, PPI_VS_REQ_START, PPI_VS_REQ_END);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL);
|
||||
static DEVICE_ATTR(request, S_IRUGO | S_IWUSR | S_IWGRP,
|
||||
tpm_show_ppi_request, tpm_store_ppi_request);
|
||||
static DEVICE_ATTR(transition_action, S_IRUGO,
|
||||
tpm_show_ppi_transition_action, NULL);
|
||||
static DEVICE_ATTR(response, S_IRUGO, tpm_show_ppi_response, NULL);
|
||||
static DEVICE_ATTR(tcg_operations, S_IRUGO, tpm_show_ppi_tcg_operations, NULL);
|
||||
static DEVICE_ATTR(vs_operations, S_IRUGO, tpm_show_ppi_vs_operations, NULL);
|
||||
|
||||
static struct attribute *ppi_attrs[] = {
|
||||
&dev_attr_version.attr,
|
||||
&dev_attr_request.attr,
|
||||
&dev_attr_transition_action.attr,
|
||||
&dev_attr_response.attr,
|
||||
&dev_attr_tcg_operations.attr,
|
||||
&dev_attr_vs_operations.attr, NULL,
|
||||
};
|
||||
static struct attribute_group ppi_attr_grp = {
|
||||
.name = "ppi",
|
||||
.attrs = ppi_attrs
|
||||
};
|
||||
|
||||
int tpm_add_ppi(struct kobject *parent)
|
||||
{
|
||||
return sysfs_create_group(parent, &ppi_attr_grp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_add_ppi);
|
||||
|
||||
void tpm_remove_ppi(struct kobject *parent)
|
||||
{
|
||||
sysfs_remove_group(parent, &ppi_attr_grp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_remove_ppi);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -705,6 +705,7 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PNP) || defined(CONFIG_PM_SLEEP)
|
||||
static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
|
||||
{
|
||||
u32 intmask;
|
||||
@@ -725,7 +726,7 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
|
||||
iowrite32(intmask,
|
||||
chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PNP
|
||||
static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
|
||||
|
||||
@@ -67,7 +67,7 @@ static int tpk_printk(const unsigned char *buf, int count)
|
||||
tmp[tpk_curr + 1] = '\0';
|
||||
printk(KERN_INFO "%s%s", tpk_tag, tmp);
|
||||
tpk_curr = 0;
|
||||
if (buf[i + 1] == '\n')
|
||||
if ((i + 1) < count && buf[i + 1] == '\n')
|
||||
i++;
|
||||
break;
|
||||
case '\n':
|
||||
@@ -178,11 +178,17 @@ static struct tty_driver *ttyprintk_driver;
|
||||
static int __init ttyprintk_init(void)
|
||||
{
|
||||
int ret = -ENOMEM;
|
||||
void *rp;
|
||||
|
||||
ttyprintk_driver = alloc_tty_driver(1);
|
||||
if (!ttyprintk_driver)
|
||||
return ret;
|
||||
tty_port_init(&tpk_port.port);
|
||||
tpk_port.port.ops = &null_ops;
|
||||
mutex_init(&tpk_port.port_write_mutex);
|
||||
|
||||
ttyprintk_driver = tty_alloc_driver(1,
|
||||
TTY_DRIVER_RESET_TERMIOS |
|
||||
TTY_DRIVER_REAL_RAW |
|
||||
TTY_DRIVER_UNNUMBERED_NODE);
|
||||
if (IS_ERR(ttyprintk_driver))
|
||||
return PTR_ERR(ttyprintk_driver);
|
||||
|
||||
ttyprintk_driver->driver_name = "ttyprintk";
|
||||
ttyprintk_driver->name = "ttyprintk";
|
||||
@@ -191,9 +197,8 @@ static int __init ttyprintk_init(void)
|
||||
ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE;
|
||||
ttyprintk_driver->init_termios = tty_std_termios;
|
||||
ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET;
|
||||
ttyprintk_driver->flags = TTY_DRIVER_RESET_TERMIOS |
|
||||
TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
|
||||
tty_set_operations(ttyprintk_driver, &ttyprintk_ops);
|
||||
tty_port_link_device(&tpk_port.port, ttyprintk_driver, 0);
|
||||
|
||||
ret = tty_register_driver(ttyprintk_driver);
|
||||
if (ret < 0) {
|
||||
@@ -201,22 +206,10 @@ static int __init ttyprintk_init(void)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* create our unnumbered device */
|
||||
rp = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 3), NULL,
|
||||
ttyprintk_driver->name);
|
||||
if (IS_ERR(rp)) {
|
||||
printk(KERN_ERR "Couldn't create ttyprintk device\n");
|
||||
ret = PTR_ERR(rp);
|
||||
goto error;
|
||||
}
|
||||
|
||||
tty_port_init(&tpk_port.port);
|
||||
tpk_port.port.ops = &null_ops;
|
||||
mutex_init(&tpk_port.port_write_mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
tty_unregister_driver(ttyprintk_driver);
|
||||
put_tty_driver(ttyprintk_driver);
|
||||
ttyprintk_driver = NULL;
|
||||
return ret;
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/splice.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/poll.h>
|
||||
@@ -474,26 +476,53 @@ static ssize_t send_control_msg(struct port *port, unsigned int event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct buffer_token {
|
||||
union {
|
||||
void *buf;
|
||||
struct scatterlist *sg;
|
||||
} u;
|
||||
/* If sgpages == 0 then buf is used, else sg is used */
|
||||
unsigned int sgpages;
|
||||
};
|
||||
|
||||
static void reclaim_sg_pages(struct scatterlist *sg, unsigned int nrpages)
|
||||
{
|
||||
int i;
|
||||
struct page *page;
|
||||
|
||||
for (i = 0; i < nrpages; i++) {
|
||||
page = sg_page(&sg[i]);
|
||||
if (!page)
|
||||
break;
|
||||
put_page(page);
|
||||
}
|
||||
kfree(sg);
|
||||
}
|
||||
|
||||
/* Callers must take the port->outvq_lock */
|
||||
static void reclaim_consumed_buffers(struct port *port)
|
||||
{
|
||||
void *buf;
|
||||
struct buffer_token *tok;
|
||||
unsigned int len;
|
||||
|
||||
if (!port->portdev) {
|
||||
/* Device has been unplugged. vqs are already gone. */
|
||||
return;
|
||||
}
|
||||
while ((buf = virtqueue_get_buf(port->out_vq, &len))) {
|
||||
kfree(buf);
|
||||
while ((tok = virtqueue_get_buf(port->out_vq, &len))) {
|
||||
if (tok->sgpages)
|
||||
reclaim_sg_pages(tok->u.sg, tok->sgpages);
|
||||
else
|
||||
kfree(tok->u.buf);
|
||||
kfree(tok);
|
||||
port->outvq_full = false;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count,
|
||||
bool nonblock)
|
||||
static ssize_t __send_to_port(struct port *port, struct scatterlist *sg,
|
||||
int nents, size_t in_count,
|
||||
struct buffer_token *tok, bool nonblock)
|
||||
{
|
||||
struct scatterlist sg[1];
|
||||
struct virtqueue *out_vq;
|
||||
ssize_t ret;
|
||||
unsigned long flags;
|
||||
@@ -505,8 +534,7 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count,
|
||||
|
||||
reclaim_consumed_buffers(port);
|
||||
|
||||
sg_init_one(sg, in_buf, in_count);
|
||||
ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf, GFP_ATOMIC);
|
||||
ret = virtqueue_add_buf(out_vq, sg, nents, 0, tok, GFP_ATOMIC);
|
||||
|
||||
/* Tell Host to go! */
|
||||
virtqueue_kick(out_vq);
|
||||
@@ -544,6 +572,37 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count,
|
||||
return in_count;
|
||||
}
|
||||
|
||||
static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count,
|
||||
bool nonblock)
|
||||
{
|
||||
struct scatterlist sg[1];
|
||||
struct buffer_token *tok;
|
||||
|
||||
tok = kmalloc(sizeof(*tok), GFP_ATOMIC);
|
||||
if (!tok)
|
||||
return -ENOMEM;
|
||||
tok->sgpages = 0;
|
||||
tok->u.buf = in_buf;
|
||||
|
||||
sg_init_one(sg, in_buf, in_count);
|
||||
|
||||
return __send_to_port(port, sg, 1, in_count, tok, nonblock);
|
||||
}
|
||||
|
||||
static ssize_t send_pages(struct port *port, struct scatterlist *sg, int nents,
|
||||
size_t in_count, bool nonblock)
|
||||
{
|
||||
struct buffer_token *tok;
|
||||
|
||||
tok = kmalloc(sizeof(*tok), GFP_ATOMIC);
|
||||
if (!tok)
|
||||
return -ENOMEM;
|
||||
tok->sgpages = nents;
|
||||
tok->u.sg = sg;
|
||||
|
||||
return __send_to_port(port, sg, nents, in_count, tok, nonblock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Give out the data that's requested from the buffer that we have
|
||||
* queued up.
|
||||
@@ -665,6 +724,26 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf,
|
||||
return fill_readbuf(port, ubuf, count, true);
|
||||
}
|
||||
|
||||
static int wait_port_writable(struct port *port, bool nonblock)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (will_write_block(port)) {
|
||||
if (nonblock)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = wait_event_freezable(port->waitqueue,
|
||||
!will_write_block(port));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
/* Port got hot-unplugged. */
|
||||
if (!port->guest_connected)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
|
||||
size_t count, loff_t *offp)
|
||||
{
|
||||
@@ -681,18 +760,9 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
|
||||
|
||||
nonblock = filp->f_flags & O_NONBLOCK;
|
||||
|
||||
if (will_write_block(port)) {
|
||||
if (nonblock)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = wait_event_freezable(port->waitqueue,
|
||||
!will_write_block(port));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
/* Port got hot-unplugged. */
|
||||
if (!port->guest_connected)
|
||||
return -ENODEV;
|
||||
ret = wait_port_writable(port, nonblock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
count = min((size_t)(32 * 1024), count);
|
||||
|
||||
@@ -725,6 +795,93 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sg_list {
|
||||
unsigned int n;
|
||||
unsigned int size;
|
||||
size_t len;
|
||||
struct scatterlist *sg;
|
||||
};
|
||||
|
||||
static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
|
||||
struct splice_desc *sd)
|
||||
{
|
||||
struct sg_list *sgl = sd->u.data;
|
||||
unsigned int offset, len;
|
||||
|
||||
if (sgl->n == sgl->size)
|
||||
return 0;
|
||||
|
||||
/* Try lock this page */
|
||||
if (buf->ops->steal(pipe, buf) == 0) {
|
||||
/* Get reference and unlock page for moving */
|
||||
get_page(buf->page);
|
||||
unlock_page(buf->page);
|
||||
|
||||
len = min(buf->len, sd->len);
|
||||
sg_set_page(&(sgl->sg[sgl->n]), buf->page, len, buf->offset);
|
||||
} else {
|
||||
/* Failback to copying a page */
|
||||
struct page *page = alloc_page(GFP_KERNEL);
|
||||
char *src = buf->ops->map(pipe, buf, 1);
|
||||
char *dst;
|
||||
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
dst = kmap(page);
|
||||
|
||||
offset = sd->pos & ~PAGE_MASK;
|
||||
|
||||
len = sd->len;
|
||||
if (len + offset > PAGE_SIZE)
|
||||
len = PAGE_SIZE - offset;
|
||||
|
||||
memcpy(dst + offset, src + buf->offset, len);
|
||||
|
||||
kunmap(page);
|
||||
buf->ops->unmap(pipe, buf, src);
|
||||
|
||||
sg_set_page(&(sgl->sg[sgl->n]), page, len, offset);
|
||||
}
|
||||
sgl->n++;
|
||||
sgl->len += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Faster zero-copy write by splicing */
|
||||
static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
|
||||
struct file *filp, loff_t *ppos,
|
||||
size_t len, unsigned int flags)
|
||||
{
|
||||
struct port *port = filp->private_data;
|
||||
struct sg_list sgl;
|
||||
ssize_t ret;
|
||||
struct splice_desc sd = {
|
||||
.total_len = len,
|
||||
.flags = flags,
|
||||
.pos = *ppos,
|
||||
.u.data = &sgl,
|
||||
};
|
||||
|
||||
ret = wait_port_writable(port, filp->f_flags & O_NONBLOCK);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
sgl.n = 0;
|
||||
sgl.len = 0;
|
||||
sgl.size = pipe->nrbufs;
|
||||
sgl.sg = kmalloc(sizeof(struct scatterlist) * sgl.size, GFP_KERNEL);
|
||||
if (unlikely(!sgl.sg))
|
||||
return -ENOMEM;
|
||||
|
||||
sg_init_table(sgl.sg, sgl.size);
|
||||
ret = __splice_from_pipe(pipe, &sd, pipe_to_sg);
|
||||
if (likely(ret > 0))
|
||||
ret = send_pages(port, sgl.sg, sgl.n, sgl.len, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int port_fops_poll(struct file *filp, poll_table *wait)
|
||||
{
|
||||
struct port *port;
|
||||
@@ -856,6 +1013,7 @@ static const struct file_operations port_fops = {
|
||||
.open = port_fops_open,
|
||||
.read = port_fops_read,
|
||||
.write = port_fops_write,
|
||||
.splice_write = port_fops_splice_write,
|
||||
.poll = port_fops_poll,
|
||||
.release = port_fops_release,
|
||||
.fasync = port_fops_fasync,
|
||||
@@ -1941,7 +2099,17 @@ static int __init init(void)
|
||||
INIT_LIST_HEAD(&pdrvdata.consoles);
|
||||
INIT_LIST_HEAD(&pdrvdata.portdevs);
|
||||
|
||||
return register_virtio_driver(&virtio_console);
|
||||
err = register_virtio_driver(&virtio_console);
|
||||
if (err < 0) {
|
||||
pr_err("Error %d registering virtio driver\n", err);
|
||||
goto free;
|
||||
}
|
||||
return 0;
|
||||
free:
|
||||
if (pdrvdata.debugfs_dir)
|
||||
debugfs_remove_recursive(pdrvdata.debugfs_dir);
|
||||
class_destroy(pdrvdata.class);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit fini(void)
|
||||
|
||||
Reference in New Issue
Block a user