* [PATCH v2 5/6] mm: make clear_huge_page cache clear only around the fault address
From: Kirill A. Shutemov @ 2012-08-09 15:03 UTC (permalink / raw)
To: linux-mm
Cc: linux-mips, linux-sh, Jan Beulich, H. Peter Anvin, sparclinux,
Andrea Arcangeli, Andi Kleen, Robert Richter, x86, Hugh Dickins,
Ingo Molnar, Mel Gorman, Alex Shi, Thomas Gleixner,
KAMEZAWA Hiroyuki, Tim Chen, linux-kernel, Andy Lutomirski,
Johannes Weiner, Andrew Morton, linuxppc-dev, Kirill A. Shutemov
In-Reply-To: <1344524583-1096-1-git-send-email-kirill.shutemov@linux.intel.com>
From: Andi Kleen <ak@linux.intel.com>
Clearing a 2MB huge page will typically blow away several levels
of CPU caches. To avoid this only cache clear the 4K area
around the fault address and use a cache avoiding clears
for the rest of the 2MB area.
Signed-off-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
---
mm/memory.c | 30 +++++++++++++++++++++++++++---
1 files changed, 27 insertions(+), 3 deletions(-)
diff --git a/mm/memory.c b/mm/memory.c
index b47199a..e9a75c2 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3969,18 +3969,35 @@ EXPORT_SYMBOL(might_fault);
#endif
#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_HUGETLBFS)
+
+#ifndef ARCH_HAS_USER_NOCACHE
+#define ARCH_HAS_USER_NOCACHE 0
+#endif
+
+#if ARCH_HAS_USER_NOCACHE == 0
+#define clear_user_highpage_nocache clear_user_highpage
+#endif
+
static void clear_gigantic_page(struct page *page,
unsigned long addr,
unsigned int pages_per_huge_page)
{
int i;
struct page *p = page;
+ unsigned long vaddr;
+ unsigned long haddr = addr & HPAGE_PMD_MASK;
+ int target = (addr - haddr) >> PAGE_SHIFT;
might_sleep();
+ vaddr = haddr;
for (i = 0; i < pages_per_huge_page;
i++, p = mem_map_next(p, page, i)) {
cond_resched();
- clear_user_highpage(p, addr + i * PAGE_SIZE);
+ vaddr = haddr + i*PAGE_SIZE;
+ if (!ARCH_HAS_USER_NOCACHE || i == target)
+ clear_user_highpage(p, vaddr);
+ else
+ clear_user_highpage_nocache(p, vaddr);
}
}
void clear_huge_page(struct page *page,
@@ -3988,16 +4005,23 @@ void clear_huge_page(struct page *page,
{
int i;
unsigned long haddr = addr & HPAGE_PMD_MASK;
+ unsigned long vaddr;
+ int target = (addr - haddr) >> PAGE_SHIFT;
if (unlikely(pages_per_huge_page > MAX_ORDER_NR_PAGES)) {
- clear_gigantic_page(page, haddr, pages_per_huge_page);
+ clear_gigantic_page(page, addr, pages_per_huge_page);
return;
}
might_sleep();
+ vaddr = haddr;
for (i = 0; i < pages_per_huge_page; i++) {
cond_resched();
- clear_user_highpage(page + i, haddr + i * PAGE_SIZE);
+ vaddr = haddr + i*PAGE_SIZE;
+ if (!ARCH_HAS_USER_NOCACHE || i == target)
+ clear_user_highpage(page + i, vaddr);
+ else
+ clear_user_highpage_nocache(page + i, vaddr);
}
}
--
1.7.7.6
^ permalink raw reply related
* [PATCH v2 6/6] x86: switch the 64bit uncached page clear to SSE/AVX v2
From: Kirill A. Shutemov @ 2012-08-09 15:03 UTC (permalink / raw)
To: linux-mm
Cc: linux-mips, linux-sh, Jan Beulich, H. Peter Anvin, sparclinux,
Andrea Arcangeli, Andi Kleen, Robert Richter, x86, Hugh Dickins,
Ingo Molnar, Mel Gorman, Alex Shi, Thomas Gleixner,
KAMEZAWA Hiroyuki, Tim Chen, linux-kernel, Andy Lutomirski,
Johannes Weiner, Andrew Morton, linuxppc-dev, Kirill A. Shutemov
In-Reply-To: <1344524583-1096-1-git-send-email-kirill.shutemov@linux.intel.com>
From: Andi Kleen <ak@linux.intel.com>
With multiple threads vector stores are more efficient, so use them.
This will cause the page clear to run non preemptable and add some
overhead. However on 32bit it was already non preempable (due to
kmap_atomic) and there is an preemption opportunity every 4K unit.
On a NPB (Nasa Parallel Benchmark) 128GB run on a Westmere this improves
the performance regression of enabling transparent huge pages
by ~2% (2.81% to 0.81%), near the runtime variability now.
On a system with AVX support more is expected.
Signed-off-by: Andi Kleen <ak@linux.intel.com>
[kirill.shutemov@linux.intel.com: Properly save/restore arguments]
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
---
arch/x86/lib/clear_page_nocache_64.S | 91 ++++++++++++++++++++++++++++-----
1 files changed, 77 insertions(+), 14 deletions(-)
diff --git a/arch/x86/lib/clear_page_nocache_64.S b/arch/x86/lib/clear_page_nocache_64.S
index ee16d15..c092919 100644
--- a/arch/x86/lib/clear_page_nocache_64.S
+++ b/arch/x86/lib/clear_page_nocache_64.S
@@ -1,29 +1,92 @@
+/*
+ * Clear pages with cache bypass.
+ *
+ * Copyright (C) 2011, 2012 Intel Corporation
+ * Author: Andi Kleen
+ *
+ * This software may be redistributed and/or modified under the terms of
+ * the GNU General Public License ("GPL") version 2 only as published by the
+ * Free Software Foundation.
+ */
+
#include <linux/linkage.h>
+#include <asm/alternative-asm.h>
+#include <asm/cpufeature.h>
#include <asm/dwarf2.h>
+#define SSE_UNROLL 128
+
/*
* Zero a page avoiding the caches
* rdi page
*/
ENTRY(clear_page_nocache)
CFI_STARTPROC
- xorl %eax,%eax
- movl $4096/64,%ecx
+ push %rdi
+ call kernel_fpu_begin
+ pop %rdi
+ sub $16,%rsp
+ CFI_ADJUST_CFA_OFFSET 16
+ movdqu %xmm0,(%rsp)
+ xorpd %xmm0,%xmm0
+ movl $4096/SSE_UNROLL,%ecx
.p2align 4
.Lloop:
decl %ecx
-#define PUT(x) movnti %rax,x*8(%rdi)
- movnti %rax,(%rdi)
- PUT(1)
- PUT(2)
- PUT(3)
- PUT(4)
- PUT(5)
- PUT(6)
- PUT(7)
- leaq 64(%rdi),%rdi
+ .set x,0
+ .rept SSE_UNROLL/16
+ movntdq %xmm0,x(%rdi)
+ .set x,x+16
+ .endr
+ leaq SSE_UNROLL(%rdi),%rdi
jnz .Lloop
- nop
- ret
+ movdqu (%rsp),%xmm0
+ addq $16,%rsp
+ CFI_ADJUST_CFA_OFFSET -16
+ jmp kernel_fpu_end
CFI_ENDPROC
ENDPROC(clear_page_nocache)
+
+#ifdef CONFIG_AS_AVX
+
+ .section .altinstr_replacement,"ax"
+1: .byte 0xeb /* jmp <disp8> */
+ .byte (clear_page_nocache_avx - clear_page_nocache) - (2f - 1b)
+ /* offset */
+2:
+ .previous
+ .section .altinstructions,"a"
+ altinstruction_entry clear_page_nocache,1b,X86_FEATURE_AVX,\
+ 16, 2b-1b
+ .previous
+
+#define AVX_UNROLL 256 /* TUNE ME */
+
+ENTRY(clear_page_nocache_avx)
+ CFI_STARTPROC
+ push %rdi
+ call kernel_fpu_begin
+ pop %rdi
+ sub $32,%rsp
+ CFI_ADJUST_CFA_OFFSET 32
+ vmovdqu %ymm0,(%rsp)
+ vxorpd %ymm0,%ymm0,%ymm0
+ movl $4096/AVX_UNROLL,%ecx
+ .p2align 4
+.Lloop_avx:
+ decl %ecx
+ .set x,0
+ .rept AVX_UNROLL/32
+ vmovntdq %ymm0,x(%rdi)
+ .set x,x+32
+ .endr
+ leaq AVX_UNROLL(%rdi),%rdi
+ jnz .Lloop_avx
+ vmovdqu (%rsp),%ymm0
+ addq $32,%rsp
+ CFI_ADJUST_CFA_OFFSET -32
+ jmp kernel_fpu_end
+ CFI_ENDPROC
+ENDPROC(clear_page_nocache_avx)
+
+#endif
--
1.7.7.6
^ permalink raw reply related
* [PATCH v2 4/6] x86: Add clear_page_nocache
From: Kirill A. Shutemov @ 2012-08-09 15:03 UTC (permalink / raw)
To: linux-mm
Cc: linux-mips, linux-sh, Jan Beulich, H. Peter Anvin, sparclinux,
Andrea Arcangeli, Andi Kleen, Robert Richter, x86, Hugh Dickins,
Ingo Molnar, Mel Gorman, Alex Shi, Thomas Gleixner,
KAMEZAWA Hiroyuki, Tim Chen, linux-kernel, Andy Lutomirski,
Johannes Weiner, Andrew Morton, linuxppc-dev, Kirill A. Shutemov
In-Reply-To: <1344524583-1096-1-git-send-email-kirill.shutemov@linux.intel.com>
From: Andi Kleen <ak@linux.intel.com>
Add a cache avoiding version of clear_page. Straight forward integer variant
of the existing 64bit clear_page, for both 32bit and 64bit.
Also add the necessary glue for highmem including a layer that non cache
coherent architectures that use the virtual address for flushing can
hook in. This is not needed on x86 of course.
If an architecture wants to provide cache avoiding version of clear_page
it should to define ARCH_HAS_USER_NOCACHE to 1 and implement
clear_page_nocache() and clear_user_highpage_nocache().
Signed-off-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
---
arch/x86/include/asm/page.h | 2 ++
arch/x86/include/asm/string_32.h | 5 +++++
arch/x86/include/asm/string_64.h | 5 +++++
arch/x86/lib/Makefile | 1 +
arch/x86/lib/clear_page_nocache_32.S | 30 ++++++++++++++++++++++++++++++
arch/x86/lib/clear_page_nocache_64.S | 29 +++++++++++++++++++++++++++++
arch/x86/mm/fault.c | 7 +++++++
7 files changed, 79 insertions(+), 0 deletions(-)
create mode 100644 arch/x86/lib/clear_page_nocache_32.S
create mode 100644 arch/x86/lib/clear_page_nocache_64.S
diff --git a/arch/x86/include/asm/page.h b/arch/x86/include/asm/page.h
index 8ca8283..aa83a1b 100644
--- a/arch/x86/include/asm/page.h
+++ b/arch/x86/include/asm/page.h
@@ -29,6 +29,8 @@ static inline void copy_user_page(void *to, void *from, unsigned long vaddr,
copy_page(to, from);
}
+void clear_user_highpage_nocache(struct page *page, unsigned long vaddr);
+
#define __alloc_zeroed_user_highpage(movableflags, vma, vaddr) \
alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO | movableflags, vma, vaddr)
#define __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE
diff --git a/arch/x86/include/asm/string_32.h b/arch/x86/include/asm/string_32.h
index 3d3e835..3f2fbcf 100644
--- a/arch/x86/include/asm/string_32.h
+++ b/arch/x86/include/asm/string_32.h
@@ -3,6 +3,8 @@
#ifdef __KERNEL__
+#include <linux/linkage.h>
+
/* Let gcc decide whether to inline or use the out of line functions */
#define __HAVE_ARCH_STRCPY
@@ -337,6 +339,9 @@ void *__constant_c_and_count_memset(void *s, unsigned long pattern,
#define __HAVE_ARCH_MEMSCAN
extern void *memscan(void *addr, int c, size_t size);
+#define ARCH_HAS_USER_NOCACHE 1
+asmlinkage void clear_page_nocache(void *page);
+
#endif /* __KERNEL__ */
#endif /* _ASM_X86_STRING_32_H */
diff --git a/arch/x86/include/asm/string_64.h b/arch/x86/include/asm/string_64.h
index 19e2c46..ca23d1d 100644
--- a/arch/x86/include/asm/string_64.h
+++ b/arch/x86/include/asm/string_64.h
@@ -3,6 +3,8 @@
#ifdef __KERNEL__
+#include <linux/linkage.h>
+
/* Written 2002 by Andi Kleen */
/* Only used for special circumstances. Stolen from i386/string.h */
@@ -63,6 +65,9 @@ char *strcpy(char *dest, const char *src);
char *strcat(char *dest, const char *src);
int strcmp(const char *cs, const char *ct);
+#define ARCH_HAS_USER_NOCACHE 1
+asmlinkage void clear_page_nocache(void *page);
+
#endif /* __KERNEL__ */
#endif /* _ASM_X86_STRING_64_H */
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index b00f678..a8ad6dd 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -23,6 +23,7 @@ lib-y += memcpy_$(BITS).o
lib-$(CONFIG_SMP) += rwlock.o
lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
lib-$(CONFIG_INSTRUCTION_DECODER) += insn.o inat.o
+lib-y += clear_page_nocache_$(BITS).o
obj-y += msr.o msr-reg.o msr-reg-export.o
diff --git a/arch/x86/lib/clear_page_nocache_32.S b/arch/x86/lib/clear_page_nocache_32.S
new file mode 100644
index 0000000..2394e0c
--- /dev/null
+++ b/arch/x86/lib/clear_page_nocache_32.S
@@ -0,0 +1,30 @@
+#include <linux/linkage.h>
+#include <asm/dwarf2.h>
+
+/*
+ * Zero a page avoiding the caches
+ * rdi page
+ */
+ENTRY(clear_page_nocache)
+ CFI_STARTPROC
+ mov %eax,%edi
+ xorl %eax,%eax
+ movl $4096/64,%ecx
+ .p2align 4
+.Lloop:
+ decl %ecx
+#define PUT(x) movnti %eax,x*8(%edi) ; movnti %eax,x*8+4(%edi)
+ PUT(0)
+ PUT(1)
+ PUT(2)
+ PUT(3)
+ PUT(4)
+ PUT(5)
+ PUT(6)
+ PUT(7)
+ lea 64(%edi),%edi
+ jnz .Lloop
+ nop
+ ret
+ CFI_ENDPROC
+ENDPROC(clear_page_nocache)
diff --git a/arch/x86/lib/clear_page_nocache_64.S b/arch/x86/lib/clear_page_nocache_64.S
new file mode 100644
index 0000000..ee16d15
--- /dev/null
+++ b/arch/x86/lib/clear_page_nocache_64.S
@@ -0,0 +1,29 @@
+#include <linux/linkage.h>
+#include <asm/dwarf2.h>
+
+/*
+ * Zero a page avoiding the caches
+ * rdi page
+ */
+ENTRY(clear_page_nocache)
+ CFI_STARTPROC
+ xorl %eax,%eax
+ movl $4096/64,%ecx
+ .p2align 4
+.Lloop:
+ decl %ecx
+#define PUT(x) movnti %rax,x*8(%rdi)
+ movnti %rax,(%rdi)
+ PUT(1)
+ PUT(2)
+ PUT(3)
+ PUT(4)
+ PUT(5)
+ PUT(6)
+ PUT(7)
+ leaq 64(%rdi),%rdi
+ jnz .Lloop
+ nop
+ ret
+ CFI_ENDPROC
+ENDPROC(clear_page_nocache)
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 76dcd9d..20888b4 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -1209,3 +1209,10 @@ good_area:
up_read(&mm->mmap_sem);
}
+
+void clear_user_highpage_nocache(struct page *page, unsigned long vaddr)
+{
+ void *p = kmap_atomic(page, KM_USER0);
+ clear_page_nocache(p);
+ kunmap_atomic(p);
+}
--
1.7.7.6
^ permalink raw reply related
* [PATCH v2 2/6] mm: make clear_huge_page tolerate non aligned address
From: Kirill A. Shutemov @ 2012-08-09 15:02 UTC (permalink / raw)
To: linux-mm
Cc: linux-mips, linux-sh, Jan Beulich, H. Peter Anvin, sparclinux,
Andrea Arcangeli, Andi Kleen, Robert Richter, x86, Hugh Dickins,
Ingo Molnar, Mel Gorman, Alex Shi, Thomas Gleixner,
KAMEZAWA Hiroyuki, Tim Chen, linux-kernel, Andy Lutomirski,
Johannes Weiner, Andrew Morton, linuxppc-dev, Kirill A. Shutemov
In-Reply-To: <1344524583-1096-1-git-send-email-kirill.shutemov@linux.intel.com>
From: Andi Kleen <ak@linux.intel.com>
hugetlb does not necessarily pass in an aligned address, so the
low level address computation is wrong.
This will fix architectures that actually use the address for flushing
the cleared address (very few, like xtensa/sparc/...?)
Signed-off-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
---
mm/memory.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/mm/memory.c b/mm/memory.c
index 5736170..b47199a 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3987,16 +3987,17 @@ void clear_huge_page(struct page *page,
unsigned long addr, unsigned int pages_per_huge_page)
{
int i;
+ unsigned long haddr = addr & HPAGE_PMD_MASK;
if (unlikely(pages_per_huge_page > MAX_ORDER_NR_PAGES)) {
- clear_gigantic_page(page, addr, pages_per_huge_page);
+ clear_gigantic_page(page, haddr, pages_per_huge_page);
return;
}
might_sleep();
for (i = 0; i < pages_per_huge_page; i++) {
cond_resched();
- clear_user_highpage(page + i, addr + i * PAGE_SIZE);
+ clear_user_highpage(page + i, haddr + i * PAGE_SIZE);
}
}
--
1.7.7.6
^ permalink raw reply related
* [PATCH v2 3/6] THP: Pass real, not rounded, address to clear_huge_page
From: Kirill A. Shutemov @ 2012-08-09 15:03 UTC (permalink / raw)
To: linux-mm
Cc: linux-mips, linux-sh, Jan Beulich, H. Peter Anvin, sparclinux,
Andrea Arcangeli, Andi Kleen, Robert Richter, x86, Hugh Dickins,
Ingo Molnar, Mel Gorman, Alex Shi, Thomas Gleixner,
KAMEZAWA Hiroyuki, Tim Chen, linux-kernel, Andy Lutomirski,
Johannes Weiner, Andrew Morton, linuxppc-dev, Kirill A. Shutemov
In-Reply-To: <1344524583-1096-1-git-send-email-kirill.shutemov@linux.intel.com>
From: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
---
mm/huge_memory.c | 9 +++++----
1 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 70737ec..ecd93f8 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -633,7 +633,8 @@ static inline pmd_t maybe_pmd_mkwrite(pmd_t pmd, struct vm_area_struct *vma)
static int __do_huge_pmd_anonymous_page(struct mm_struct *mm,
struct vm_area_struct *vma,
- unsigned long haddr, pmd_t *pmd,
+ unsigned long haddr,
+ unsigned long address, pmd_t *pmd,
struct page *page)
{
pgtable_t pgtable;
@@ -643,7 +644,7 @@ static int __do_huge_pmd_anonymous_page(struct mm_struct *mm,
if (unlikely(!pgtable))
return VM_FAULT_OOM;
- clear_huge_page(page, haddr, HPAGE_PMD_NR);
+ clear_huge_page(page, address, HPAGE_PMD_NR);
__SetPageUptodate(page);
spin_lock(&mm->page_table_lock);
@@ -720,8 +721,8 @@ int do_huge_pmd_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,
put_page(page);
goto out;
}
- if (unlikely(__do_huge_pmd_anonymous_page(mm, vma, haddr, pmd,
- page))) {
+ if (unlikely(__do_huge_pmd_anonymous_page(mm, vma, haddr,
+ address, pmd, page))) {
mem_cgroup_uncharge_page(page);
put_page(page);
goto out;
--
1.7.7.6
^ permalink raw reply related
* [PATCH v2 1/6] THP: Use real address for NUMA policy
From: Kirill A. Shutemov @ 2012-08-09 15:02 UTC (permalink / raw)
To: linux-mm
Cc: linux-mips, linux-sh, Jan Beulich, H. Peter Anvin, sparclinux,
Andrea Arcangeli, Andi Kleen, Robert Richter, x86, Hugh Dickins,
Ingo Molnar, Mel Gorman, Alex Shi, Thomas Gleixner,
KAMEZAWA Hiroyuki, Tim Chen, linux-kernel, Andy Lutomirski,
Johannes Weiner, Andrew Morton, linuxppc-dev, Kirill A. Shutemov
In-Reply-To: <1344524583-1096-1-git-send-email-kirill.shutemov@linux.intel.com>
From: Andi Kleen <ak@linux.intel.com>
Use the fault address, not the rounded down hpage address for NUMA
policy purposes. In some circumstances this can give more exact
NUMA policy.
Signed-off-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
---
mm/huge_memory.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 57c4b93..70737ec 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -681,11 +681,11 @@ static inline gfp_t alloc_hugepage_gfpmask(int defrag, gfp_t extra_gfp)
static inline struct page *alloc_hugepage_vma(int defrag,
struct vm_area_struct *vma,
- unsigned long haddr, int nd,
+ unsigned long address, int nd,
gfp_t extra_gfp)
{
return alloc_pages_vma(alloc_hugepage_gfpmask(defrag, extra_gfp),
- HPAGE_PMD_ORDER, vma, haddr, nd);
+ HPAGE_PMD_ORDER, vma, address, nd);
}
#ifndef CONFIG_NUMA
@@ -710,7 +710,7 @@ int do_huge_pmd_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,
if (unlikely(khugepaged_enter(vma)))
return VM_FAULT_OOM;
page = alloc_hugepage_vma(transparent_hugepage_defrag(vma),
- vma, haddr, numa_node_id(), 0);
+ vma, address, numa_node_id(), 0);
if (unlikely(!page)) {
count_vm_event(THP_FAULT_FALLBACK);
goto out;
@@ -944,7 +944,7 @@ int do_huge_pmd_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
if (transparent_hugepage_enabled(vma) &&
!transparent_hugepage_debug_cow())
new_page = alloc_hugepage_vma(transparent_hugepage_defrag(vma),
- vma, haddr, numa_node_id(), 0);
+ vma, address, numa_node_id(), 0);
else
new_page = NULL;
--
1.7.7.6
^ permalink raw reply related
* Re: [PATCH v3 0/7] mv643xx.c: Add basic device tree support.
From: Ian Molton @ 2012-08-09 15:21 UTC (permalink / raw)
To: Arnd Bergmann
Cc: thomas.petazzoni, andrew, netdev, devicetree-discuss, ben.dooks,
linuxppc-dev, David Miller, linux-arm-kernel
In-Reply-To: <201208091143.32972.arnd@arndb.de>
On 09/08/12 12:43, Arnd Bergmann wrote:
> On 08/08/12 14:19, Ian Molton wrote:
> > On 08/08/12 13:39, Arnd Bergmann wrote:
> >> On Wednesday 08 August 2012, Ian Molton wrote:
> >>> This method would require a small amount of rework in the driver to
> >>> set up <n> ports, rather than just one.
> >> This looks quite nice, but it is still very much incompatible with the
> >> existing binding. Obviously we can abandon an existing binding and
> >> introduce a second one for the same hardware, but that should not
> >> be taken lightly.
> > Fair, however the existing users aren't anywhere near as
> > numerous as the new ones.
>
> Depends on how you count the numbers. I see at least three machines
> supported in the kernel with the old binding and none with the new one
> so far ;-)
I'm curious as to how any of those actually work, given the
apparent total lack of a mv64360-mdio device binding...
> > As you can see, instead of putting port1 at +0x1700 or so,
> > marvell have overlapped the register files - in fact, doubly
> > so, since port1 + 0x1080 is right in the middle of
> > (port0 + 0x1000) -> (port0 + 0x16ff), so one cant simply map two
> > sets of regs like 0x0000->0x03ff and 0x1000->0x16ff for port one
> > either.
>
> This could theoretically be dealt with by having 5 register ranges
I make that three...
> per device, but that would cause extra overhead and also be
> incompatible with the existing binding.
Indeed.
> I think showing one
> parent device with children at address 0, 1 and 2 is ok.
Is it acceptable for the child devices to directly access the
parents register space? because there would be no other
way for that to work.
> The driver
> already knows all those offsets and they are always the same
> for all variants of mv643xx, right?
Yes, but its not clean. And no amount of refactoring is
really going to make a nice driver that also fits the ancient
(and badly thought out) OF bindings.
If we have to break things, we can at least go for a nice
clean design, surely?
The ports arent really child devices of the MAC. The MAC
just has 3 ports.
Luckily, it looks like the existing users don't actually use
the device tree to set up the driver at all, preferring to
translate their D-T bindings to calls to
platform_device_register() so all we'd need to do to
support them is completely ignore them.
We're going to have to maintain a legacy
platform_device -> DT bindings hack somewhere anyway,
at least until the remaining other users of the driver
convert to D-T.
-Ian
^ permalink raw reply
* Re: [PATCH v2 6/6] x86: switch the 64bit uncached page clear to SSE/AVX v2
From: Jan Beulich @ 2012-08-09 15:28 UTC (permalink / raw)
To: Andi Kleen, Kirill A. Shutemov
Cc: Andrea Arcangeli, linux-mips, H. Peter Anvin, Alex Shi,
Robert Richter, linuxppc-dev, x86, Hugh Dickins, linux-kernel,
Andy Lutomirski, linux-mm, linux-sh, Ingo Molnar, Mel Gorman,
Johannes Weiner, sparclinux, Thomas Gleixner, Tim Chen,
Andrew Morton, KAMEZAWA Hiroyuki
In-Reply-To: <1344524583-1096-7-git-send-email-kirill.shutemov@linux.intel.com>
>>> On 09.08.12 at 17:03, "Kirill A. Shutemov" <kirill.shutemov@linux.intel=
.com> wrote:
> ENTRY(clear_page_nocache)
> CFI_STARTPROC
> - xorl %eax,%eax
> - movl $4096/64,%ecx
> + push %rdi
> + call kernel_fpu_begin
> + pop %rdi
You use CFI annotations elsewhere, so why don't you use
pushq_cfi/popq_cfi here?
Jan
^ permalink raw reply
* Re: [PATCH v2 4/6] x86: Add clear_page_nocache
From: H. Peter Anvin @ 2012-08-09 15:23 UTC (permalink / raw)
To: Kirill A. Shutemov
Cc: Andrea Arcangeli, linux-mips, Andi Kleen, Alex Shi,
Robert Richter, linuxppc-dev, x86, Hugh Dickins, linux-kernel,
Jan Beulich, Andy Lutomirski, linux-mm, linux-sh, Ingo Molnar,
Mel Gorman, Johannes Weiner, sparclinux, Thomas Gleixner,
Tim Chen, Andrew Morton, KAMEZAWA Hiroyuki
In-Reply-To: <1344524583-1096-5-git-send-email-kirill.shutemov@linux.intel.com>
On 08/09/2012 08:03 AM, Kirill A. Shutemov wrote:
> From: Andi Kleen <ak@linux.intel.com>
>
> Add a cache avoiding version of clear_page. Straight forward integer variant
> of the existing 64bit clear_page, for both 32bit and 64bit.
>
> Also add the necessary glue for highmem including a layer that non cache
> coherent architectures that use the virtual address for flushing can
> hook in. This is not needed on x86 of course.
>
> If an architecture wants to provide cache avoiding version of clear_page
> it should to define ARCH_HAS_USER_NOCACHE to 1 and implement
> clear_page_nocache() and clear_user_highpage_nocache().
>
Compile failure:
/home/hpa/kernel/tip.x86-mm/arch/x86/mm/fault.c: In function
‘clear_user_highpage_nocache’:
/home/hpa/kernel/tip.x86-mm/arch/x86/mm/fault.c:1215:30: error:
‘KM_USER0’ undeclared (first use in this function)
/home/hpa/kernel/tip.x86-mm/arch/x86/mm/fault.c:1215:30: note: each
undeclared identifier is reported only once for each function it appears in
/home/hpa/kernel/tip.x86-mm/arch/x86/mm/fault.c:1215:2: error: too many
arguments to function ‘kmap_atomic’
In file included from
/home/hpa/kernel/tip.x86-mm/include/linux/pagemap.h:10:0,
from
/home/hpa/kernel/tip.x86-mm/include/linux/mempolicy.h:70,
from
/home/hpa/kernel/tip.x86-mm/include/linux/hugetlb.h:15,
from /home/hpa/kernel/tip.x86-mm/arch/x86/mm/fault.c:14:
/home/hpa/kernel/tip.x86-mm/include/linux/highmem.h:66:21: note:
declared here
make[4]: *** [arch/x86/mm/fault.o] Error 1
make[3]: *** [arch/x86/mm] Error 2
make[2]: *** [arch/x86] Error 2
make[1]: *** [sub-make] Error 2
make[1]: Leaving directory `/home/hpa/kernel/tip.x86-mm'
This happens on *all* my test configurations, including both x86-64 and
i386 allyesconfig. I suspect your patchset base is stale.
-hpa
--
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel. I don't speak on their behalf.
^ permalink raw reply
* Re: [RFC PATCH V6 04/19] memory-hotplug: offline and remove memory when removing the memory device
From: Vasilis Liaskovitis @ 2012-08-09 15:41 UTC (permalink / raw)
To: wency
Cc: linux-s390, linux-ia64, linux-acpi, len.brown, linux-sh,
linux-kernel, cmetcalf, linux-mm, isimatu.yasuaki, paulus,
minchan.kim, kosaki.motohiro, rientjes, cl, linuxppc-dev, akpm,
liuj97
In-Reply-To: <1343980161-14254-5-git-send-email-wency@cn.fujitsu.com>
Hi,
> We should offline and remove memory when removing the memory device.
> The memory device can be removed by 2 ways:
> 1. send eject request by SCI
> 2. echo 1 >/sys/bus/pci/devices/PNP0C80:XX/eject
>
[snip]
> +
> +static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
> +{
> + int result;
> +
> + /*
> + * Ask the VM to offline this memory range.
> + * Note: Assume that this function returns zero on success
> + */
> + result = acpi_memory_device_remove_memory(mem_device);
> +
here we should check the result of acpi_memory_device_remove_memory()
and not continue if it failed.
> /* Power-off and eject the device */
> result = acpi_memory_powerdown_device(mem_device);
> if (result) {
thanks,
- Vasilis
^ permalink raw reply
* Re: [PATCH v2 4/6] x86: Add clear_page_nocache
From: Jan Beulich @ 2012-08-09 15:22 UTC (permalink / raw)
To: Andi Kleen, Kirill A. Shutemov
Cc: Andrea Arcangeli, linux-mips, H. Peter Anvin, Alex Shi,
Robert Richter, linuxppc-dev, x86, Hugh Dickins, linux-kernel,
Andy Lutomirski, linux-mm, linux-sh, Ingo Molnar, Mel Gorman,
Johannes Weiner, sparclinux, Thomas Gleixner, Tim Chen,
Andrew Morton, KAMEZAWA Hiroyuki
In-Reply-To: <1344524583-1096-5-git-send-email-kirill.shutemov@linux.intel.com>
>>> On 09.08.12 at 17:03, "Kirill A. Shutemov" <kirill.shutemov@linux.intel=
.com> wrote:
> From: Andi Kleen <ak@linux.intel.com>
>=20
> Add a cache avoiding version of clear_page. Straight forward integer =
variant
> of the existing 64bit clear_page, for both 32bit and 64bit.
While on 64-bit this is fine, I fail to see how you avoid using the
SSE2 instruction on non-SSE2 systems.
> Also add the necessary glue for highmem including a layer that non cache
> coherent architectures that use the virtual address for flushing can
> hook in. This is not needed on x86 of course.
>=20
> If an architecture wants to provide cache avoiding version of clear_page
> it should to define ARCH_HAS_USER_NOCACHE to 1 and implement
> clear_page_nocache() and clear_user_highpage_nocache().
>=20
> Signed-off-by: Andi Kleen <ak@linux.intel.com>
> Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
> ---
> arch/x86/include/asm/page.h | 2 ++
> arch/x86/include/asm/string_32.h | 5 +++++
> arch/x86/include/asm/string_64.h | 5 +++++
> arch/x86/lib/Makefile | 1 +
> arch/x86/lib/clear_page_nocache_32.S | 30 ++++++++++++++++++++++++++++=
++
> arch/x86/lib/clear_page_nocache_64.S | 29 ++++++++++++++++++++++++++++=
+
Couldn't this more reasonably go into clear_page_{32,64}.S?
> arch/x86/mm/fault.c | 7 +++++++
> 7 files changed, 79 insertions(+), 0 deletions(-)
> create mode 100644 arch/x86/lib/clear_page_nocache_32.S
> create mode 100644 arch/x86/lib/clear_page_nocache_64.S
>...
>--- /dev/null
>+++ b/arch/x86/lib/clear_page_nocache_32.S
>@@ -0,0 +1,30 @@
>+#include <linux/linkage.h>
>+#include <asm/dwarf2.h>
>+
>+/*
>+ * Zero a page avoiding the caches
>+ * rdi page
Wrong comment.
>+ */
>+ENTRY(clear_page_nocache)
>+ CFI_STARTPROC
>+ mov %eax,%edi
You need to pick a different register here (e.g. %edx), since
%edi has to be preserved by all functions called from C.
>+ xorl %eax,%eax
>+ movl $4096/64,%ecx
>+ .p2align 4
>+.Lloop:
>+ decl %ecx
>+#define PUT(x) movnti %eax,x*8(%edi) ; movnti %eax,x*8+4(%edi)
Is doing twice as much unrolling as on 64-bit really worth it?
Jan
^ permalink raw reply
* Re: [PATCH 3/3] drivers/char/tpm: Add securityfs support for event log
From: Kent Yoder @ 2012-08-09 15:57 UTC (permalink / raw)
To: Ashley Lai
Cc: linux-kernel, linux-security-module, tpmdd-devel, adlai, rcj,
linuxppc-dev
In-Reply-To: <1344369479.24990.9.camel@footlong>
Hi Ashley,
> diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
> index 547509d..b53da57 100644
> --- a/drivers/char/tpm/Makefile
> +++ b/drivers/char/tpm/Makefile
> @@ -2,9 +2,15 @@
> # Makefile for the kernel tpm device drivers.
> #
> obj-$(CONFIG_TCG_TPM) += tpm.o
> +obj-$(CONFIG_TCG_TPM) += tpm_bios.o
> ifdef CONFIG_ACPI
> - obj-$(CONFIG_TCG_TPM) += tpm_bios.o
> tpm_bios-objs += tpm_eventlog.o tpm_acpi.o
> +else
> +ifdef CONFIG_TCG_IBMVTPM
> + tpm_bios-objs += tpm_eventlog.o tpm_of.o
> +else
> + tpm_bios-objs += tpm_eventlog.o tpm_noeventlog.o
tpm_eventlog.c is included in all cases here...
> diff --git a/drivers/char/tpm/tpm_eventlog.h b/drivers/char/tpm/tpm_eventlog.h
> index 8e23ccd..5fbea94 100644
> --- a/drivers/char/tpm/tpm_eventlog.h
> +++ b/drivers/char/tpm/tpm_eventlog.h
> @@ -68,4 +68,28 @@ enum tcpa_pc_event_ids {
> };
>
> int read_log(struct tpm_bios_log *log);
> +
> +#if defined(CONFIG_ACPI) || defined(CONFIG_PPC64)
> +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
But here, without acpi or ppc64, we compile in stubs, which breaks
things. It looks like we don't need these stubs at all anymore, and can
rely on TPM_NO_EVENT_LOG to bail out in the cases on x86 where ACPI is
disabled.
Kent
> +
> +#ifdef CONFIG_PPC64
> +#define TPM_NO_EVENT_LOG !of_find_node_by_name(NULL, "ibm,vtpm")
> +#else
> +#ifdef CONFIG_ACPI
> +#define TPM_NO_EVENT_LOG 0
> +#else
> +#define TPM_NO_EVENT_LOG 1
> +#endif
> +#endif
> +
> #endif
> diff --git a/drivers/char/tpm/tpm_noeventlog.c b/drivers/char/tpm/tpm_noeventlog.c
> new file mode 100644
> index 0000000..f30a2bf
> --- /dev/null
> +++ b/drivers/char/tpm/tpm_noeventlog.c
> @@ -0,0 +1,21 @@
> +/*
> + * Copyright (C) 2012 IBM Corporation
> + *
> + * Author: Ashley Lai <adlai@us.ibm.com>
> + *
> + * Maintained by: <tpmdd-devel@lists.sourceforge.net>
> + *
> + * 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 "tpm_eventlog.h"
> +
> +int read_log(struct tpm_bios_log *log)
> +{
> + return -EINVAL;
> +}
> diff --git a/drivers/char/tpm/tpm_of.c b/drivers/char/tpm/tpm_of.c
> new file mode 100644
> index 0000000..6d44adb
> --- /dev/null
> +++ b/drivers/char/tpm/tpm_of.c
> @@ -0,0 +1,68 @@
> +/*
> + * 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__);
> + return -EIO;
> + }
> + if (*sizep == 0) {
> + pr_err("%s: ERROR - event log area empty\n", __func__);
> + return -EIO;
> + }
> +
> + basep = of_get_property(np, "linux,sml-base", NULL);
> + if (basep == NULL) {
> + pr_err(KERN_ERR "%s: ERROR - SML not found\n", __func__);
> + return -EIO;
> + }
> +
> + 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;
> +}
> --
> 1.7.1
>
>
^ permalink raw reply
* Re: [PATCH V5 3/3] powerpc/fsl-pci: Unify pci/pcie initialization code
From: Scott Wood @ 2012-08-09 17:00 UTC (permalink / raw)
To: Jia Hongtao-B38951
Cc: Wood Scott-B07421, Gala Kumar-B11780,
linuxppc-dev@lists.ozlabs.org, Li Yang-R58472
In-Reply-To: <412C8208B4A0464FA894C5F0C278CD5D01A51DF9@039-SN1MPN1-002.039d.mgd.msft.net>
On 08/08/2012 10:48 PM, Jia Hongtao-B38951 wrote:
>
>
>> -----Original Message-----
>> From: Wood Scott-B07421
>> Sent: Wednesday, August 08, 2012 11:58 PM
>> To: Jia Hongtao-B38951
>> Cc: Wood Scott-B07421; Li Yang-R58472; linuxppc-dev@lists.ozlabs.org;
>> Gala Kumar-B11780
>> Subject: Re: [PATCH V5 3/3] powerpc/fsl-pci: Unify pci/pcie
>> initialization code
>>
>> On 08/08/2012 04:03 AM, Jia Hongtao-B38951 wrote:
>>>
>>>
>>>> -----Original Message-----
>>>> From: Wood Scott-B07421
>>>> Sent: Tuesday, August 07, 2012 11:25 PM
>>>> To: Li Yang-R58472
>>>> Cc: Wood Scott-B07421; linuxppc-dev@lists.ozlabs.org; Li Yang-R58472;
>>>> Jia
>>>> Hongtao-B38951
>>>> Subject: Re: [PATCH V5 3/3] powerpc/fsl-pci: Unify pci/pcie
>>>> initialization code
>>>>
>>>> On 08/06/2012 11:20 PM, Li Yang wrote:
>>>>> On Mon, Aug 6, 2012 at 11:09 PM, Scott Wood
>>>>> <scottwood@freescale.com>
>>>> wrote:
>>>>>> On 08/05/2012 10:07 PM, Jia Hongtao-B38951 wrote:
>>>>>>>
>>>>>>>
>>>>>>>> -----Original Message-----
>>>>>>>> From: Wood Scott-B07421
>>>>>>>> Sent: Saturday, August 04, 2012 12:28 AM
>>>>>>>> To: Jia Hongtao-B38951
>>>>>>>> Cc: linuxppc-dev@lists.ozlabs.org; galak@kernel.crashing.org; Li
>>>>>>>> Yang- R58472; Wood Scott-B07421
>>>>>>>> Subject: Re: [PATCH V5 3/3] powerpc/fsl-pci: Unify pci/pcie
>>>>>>>> initialization code
>>>>>>>>
>>>>>>>> As I explained before, this has to be done globally, not from the
>>>>>>>> probe function, so we can assign a default primary bus if there
>>>> isn't any ISA.
>>>>>>>> There are bugs in the Linux PPC PCI code relating to not having
>>>>>>>> any primary bus.
>>>>>>>>
>>>>>>>> -Scott
>>>>>>>
>>>>>>> In my way of searching ISA you can also assign a default primary
>>>>>>> bus in board specific files.
>>>>>>
>>>>>> That was meant for when the board file had an alternate way of
>>>>>> searching for the primary bus (e.g. look for i8259), not as a
>>>>>> replacement for the mechanism that guarantees there's a primary bus.
>>>>>>
>>>>>> You are causing a regression in the qemu_e500.c platform.
>>>>>
>>>>> Can we fix the qemu device tree to address the problem if we do make
>>>>> it a rule to use the ISA node to indicate the primary bus?
>>>>
>>>> No. There is no ISA, and we're not going to lie and say there is.
>>>
>>> But we can assign a default primary for qemu.
>>
>> Not in the device tree. What other mechanism do you propose? And why do
>> you want to fix it only for QEMU and not other boards, where things
>> happen to work but not as designed?
>>
>> Kumar, can you speak up here as maintainer so we can stop going back and
>> forth endlessly?
>>
>>>> I really don't understand what the problem is with leaving the
>>>> primary detection code as global. Either fix the bugs so we don't
>>>> need a primary, or accept some "impurity" in the workaround.
>>>>
>>>> -Scott
>>>
>>> Global detection for primary is ok but we think our way is deeper
>> unified.
>>
>> So my way works and "is ok", and your way doesn't work but is
>> theoretically cleaner.
>
> Sorry, I meant global detection is ok but I didn't mean that your logic
> is ok. The concern is in some cases there is no isa node in device tree
> and the primary is not the first bus. Your logic assigned a wrong primary
> there. Is that a problem? Take ge_imp3a as an example.
>
> So maybe we should fix this exceptional board.
Boards like that are why the code first checks for whether
fsl_pci_primary is NULL. Board code can set fsl_pci_primary based on
other criteria before calling fsl_pci_init().
This is why we need to allow for a gradual conversion, so boards like
that can get personal attention by someone who can test the change.
-Scott
^ permalink raw reply
* Re: [PATCH v7 0/8] Raid: enable talitos xor offload for improving performance
From: Ira W. Snyder @ 2012-08-09 17:03 UTC (permalink / raw)
To: qiang.liu
Cc: arnd, vinod.koul, gregkh, linux-kernel, dan.j.williams, herbert,
linux-crypto, dan.j.williams, linuxppc-dev
In-Reply-To: <1344500375-8457-1-git-send-email-qiang.liu@freescale.com>
On Thu, Aug 09, 2012 at 04:19:35PM +0800, qiang.liu@freescale.com wrote:
> Hi all,
>
> The following 8 patches enabling fsl-dma and talitos offload raid
> operations for improving raid performance and balancing CPU load.
>
> These patches include talitos, fsl-dma and carma module (caram uses
> some features of fsl-dma).
>
> Write performance will be improved by 25-30% tested by iozone.
> Write performance is improved about 2% after using spin_lock_bh replace
> spin_lock_irqsave.
> CPU load will be reduced by 8%.
>
> "fwiw, I gave v5 a test-drive, setting up a RAID5 array on ramdisks
> [1], and this patchseries, along with FSL_DMA && NET_DMA set seems
> to be holding water, so this series gets my:"
>
> Tested-by: Kim Phillips <kim.phillips@freescale.com>
>
The fsldma parts of the series all look great to me.
Thanks,
Ira
> [1] mdadm --create --verbose --force /dev/md0 --level=raid5 --raid-devices=4 \
> /dev/ram[0123]
>
> Changes in v7:
> - add test result which is provided by Kim Phillips;
> - correct one coding style issue in patch 5/8;
> - add comments by Arnd Bergmann in patch 6/8;
>
> Changes in v6:
> - swap the order of original patch 3/6 and 4/6;
> - merge Ira's patch to reduce the size of original patch;
> - merge Ira's patch of carma in 8/8;
> - update documents and descriptions according to Ira's advice;
>
> Changes in v5:
> - add detail description in patch 3/6 about the process of completed
> descriptor, the process is in align with fsl-dma Reference Manual,
> illustrate the potential risk and how to reproduce it;
> - drop the patch 7/7 in v4 according to Timur's comments;
>
> Changes in v4:
> - fix an error in talitos when dest addr is same with src addr, dest
> should be freed only one time if src is same with dest addr;
> - correct coding style in fsl-dma according to Ira's comments;
> - fix a race condition in fsl-dma fsl_tx_status(), remove the interface
> which is used to free descriptors in queue ld_completed, this interface
> has been included in fsldma_cleanup_descriptor(), in v3, there is one
> place missed spin_lock protect;
> - split the original patch 3/4 up to 2 patches 3/7 and 4/7 according to
> Li Yang's comments;
> - fix a warning of unitialized cookie;
> - add memory copy self test in fsl-dma;
> - add more detail description about use spin_lock_bh() to instead of
> spin_lock_irqsave() according to Timur's comments.
>
> Changes in v3:
> - change release process of fsl-dma descriptor for resolve the
> potential race condition;
> - add test result when use spin_lock_bh replace spin_lock_irqsave;
> - modify the benchmark results according to the latest patch.
>
> Changes in v2:
> - rebase onto cryptodev tree;
> - split the patch 3/4 up to 3 independent patches;
> - remove the patch 4/4, the fix is not for cryptodev tree;
>
> Qiang Liu (8):
> Talitos: Support for async_tx XOR offload
> fsl-dma: remove attribute DMA_INTERRUPT of dmaengine
> fsl-dma: add fsl_dma_free_descriptor() to reduce code duplication
> fsl-dma: move functions to avoid forward declarations
> fsl-dma: change release process of dma descriptor for supporting async_tx
> fsl-dma: use spin_lock_bh to instead of spin_lock_irqsave
> fsl-dma: fix a warning of unitialized cookie
> carma: remove unnecessary DMA_INTERRUPT capability
>
> drivers/crypto/Kconfig | 9 +
> drivers/crypto/talitos.c | 413 ++++++++++++++++++++++++++
> drivers/crypto/talitos.h | 53 ++++
> drivers/dma/fsldma.c | 488 +++++++++++++++++--------------
> drivers/dma/fsldma.h | 17 +-
> drivers/misc/carma/carma-fpga-program.c | 1 -
> drivers/misc/carma/carma-fpga.c | 2 +-
> 7 files changed, 761 insertions(+), 222 deletions(-)
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
* Re: [PATCH 3/3] drivers/char/tpm: Add securityfs support for event log
From: Ashley D Lai @ 2012-08-09 20:31 UTC (permalink / raw)
To: Kent Yoder
Cc: linux-kernel, Ashley Lai, linux-security-module, tpmdd-devel, rcj,
linuxppc-dev
In-Reply-To: <20120809155750.GA7352@linux.vnet.ibm.com>
[-- Attachment #1: Type: text/plain, Size: 721 bytes --]
Hi Kent,
> > +ifdef CONFIG_TCG_IBMVTPM
> > + tpm_bios-objs += tpm_eventlog.o tpm_of.o
> > +else
> > + tpm_bios-objs += tpm_eventlog.o tpm_noeventlog.o
>
> tpm_eventlog.c is included in all cases here...
>
> > +static inline struct dentry **tpm_bios_log_setup(char *name)
> > +{
> > + return NULL;
> > +}
> > +static inline void tpm_bios_log_teardown(struct dentry **dir)
> > +{
> > +}
> > +#endif
>
> But here, without acpi or ppc64, we compile in stubs, which breaks
> things. It looks like we don't need these stubs at all anymore, and can
> rely on TPM_NO_EVENT_LOG to bail out in the cases on x86 where ACPI is
> disabled.
>
> Kent
Thanks for testing it out. I will fix it in the next version.
--Ashley
[-- Attachment #2: Type: text/html, Size: 1035 bytes --]
^ permalink raw reply
* Re: [PATCH 3/3] drivers/char/tpm: Add securityfs support for event log
From: Ashley Lai @ 2012-08-09 20:52 UTC (permalink / raw)
To: linux-kernel
Cc: rcj, linux-security-module, tpmdd-devel, adlai, key, linuxppc-dev
In-Reply-To: <1344369479.24990.9.camel@footlong>
Hi Kent,
Thanks for looking into it. I'll fix in the next version.
Sorry if you got this message twice. I have some issues with my mail
server.
Thanks,
--Ashley Lai
^ permalink raw reply
* Re: [PATCH V5 3/3] powerpc/fsl-pci: Unify pci/pcie initialization code
From: Scott Wood @ 2012-08-09 22:20 UTC (permalink / raw)
To: Jia Hongtao-B38951
Cc: Wood Scott-B07421, linuxppc-dev@lists.ozlabs.org, Li Yang-R58472
In-Reply-To: <412C8208B4A0464FA894C5F0C278CD5D01A51E00@039-SN1MPN1-002.039d.mgd.msft.net>
On 08/08/2012 10:48 PM, Jia Hongtao-B38951 wrote:
>
>
>> -----Original Message-----
>> From: Wood Scott-B07421
>> Sent: Thursday, August 09, 2012 12:02 AM
>> To: Jia Hongtao-B38951
>> Cc: Wood Scott-B07421; linuxppc-dev@lists.ozlabs.org;
>> galak@kernel.crashing.org; Li Yang-R58472
>> Subject: Re: [PATCH V5 3/3] powerpc/fsl-pci: Unify pci/pcie
>> initialization code
>>
>> On 08/08/2012 04:39 AM, Jia Hongtao-B38951 wrote:
>>>
>>>
>>>> -----Original Message-----
>>>> From: Wood Scott-B07421
>>>> Sent: Tuesday, August 07, 2012 11:29 PM
>>>> To: Jia Hongtao-B38951
>>>> Cc: Wood Scott-B07421; linuxppc-dev@lists.ozlabs.org;
>>>> galak@kernel.crashing.org; Li Yang-R58472
>>>> Subject: Re: [PATCH V5 3/3] powerpc/fsl-pci: Unify pci/pcie
>>>> initialization code
>>>>
>>>> On 08/07/2012 03:09 AM, Jia Hongtao-B38951 wrote:
>>>>> I am really not sure that all boards need primary bus. Could you
>>>>> give me the link of discussion about primary that you mentioned?
>>>>
>>>> https://lists.ozlabs.org/pipermail/linuxppc-dev/2012-June/098586.html
>>>>
>>>> -Scott
>>>
>>>
>>> It seems in qemu isa_io_base must be non-zero.
>>
>> In all cases. It just shows up worse under QEMU because of a different
>> issue.
>>
>>> If there is no isa bridge should isa_io_base be non-zero for other
>> boards?
>>
>> Yes, until the bugs are fixed.
>>
>>> If not maybe we should fix qemu bug.
>>
>> If you want to try to make QEMU accept I/O BARs with address zero, go
>> ahead, but you don't get to assume that someone else will do it, we still
>> need to be compatible with older QEMUs (this bug is not so severe that
>> compatibility is unreasonable), and it still doesn't address the fact
>> that things are not functioning as designed. IIRC there are some real
>> hardware PCI cards that don't like getting an address of zero either.
>>
>>> Or "quick fix" in the link is a workaround.
>>
>> I think that "quick fix" may have problems if there is a primary bus but
>> it's not the first one detected. In any case, any fix or workaround has
>> to happen before you make changes that rely on it.
>>
>> -Scott
>
> If there is no primary assigned and accidently the primary is not the
> first one this "quick fix" may have problem. But this -accident- only happened
> in ge_imp3a board if I didn't miss other boards.
How is it an accident? It's a perfectly legitimate situation.
> So if there is no primary assigned but the primary is the first bus detected
> this "quick fix" is right. That means the "quick fix" is the equivalent
> substitution for "arbitrarily designate one as primary".
It's not equivalent because I didn't try to convert the ge_imp3a board,
and if I did I would have added special code to the ge_imp3a board to
set the fsl_pci_primary before calling fsl_pci_init().
-Scott
^ permalink raw reply
* [PATCH V2 0/3] tpm: Add new vTPM device driver for PPC64
From: Ashley Lai @ 2012-08-09 23:00 UTC (permalink / raw)
To: linux-kernel
Cc: rcj, adlai, linux-security-module, tpmdd-devel, adlai, key,
linuxppc-dev
Change log V2:
- Removed unnecessary tpm_bios_log_setup and tpm_bios_log_teardown
functions in tpm_eventlog.h (patch 3/3).
- Added more descriptions on vTPM (patch 1/3).
These patches add support for IBM vTPM for PPC64. This new device driver
works on firmware that supports vTPM (firmware release 740 or higher).
Tested on Power7+ system with firmware level ZM770_001.
Applied to Kent Yoder patch to modularize event log located at:
https://github.com/shpedoikal/linux/tree/tpmdd-next
Ashley Lai (3):
drivers/char/tpm: Add new device driver to support IBM vTPM
PPC64: Add support for instantiating SML from Open Firmware
drivers/char/tpm: Add securityfs support for event log
arch/powerpc/kernel/prom_init.c | 64 ++++
drivers/char/tpm/Kconfig | 8 +
drivers/char/tpm/Makefile | 9 +-
drivers/char/tpm/tpm.h | 13 +-
drivers/char/tpm/tpm_eventlog.c | 7 +
drivers/char/tpm/tpm_eventlog.h | 14 +
drivers/char/tpm/tpm_ibmvtpm.c | 750 +++++++++++++++++++++++++++++++++++++
drivers/char/tpm/tpm_ibmvtpm.h | 83 ++++
drivers/char/tpm/tpm_noeventlog.c | 21 +
drivers/char/tpm/tpm_of.c | 68 ++++
10 files changed, 1024 insertions(+), 13 deletions(-)
create mode 100644 drivers/char/tpm/tpm_ibmvtpm.c
create mode 100644 drivers/char/tpm/tpm_ibmvtpm.h
create mode 100644 drivers/char/tpm/tpm_noeventlog.c
create mode 100644 drivers/char/tpm/tpm_of.c
^ permalink raw reply
* [PATCH V2 2/3] PPC64: Add support for instantiating SML from Open Firmware
From: Ashley Lai @ 2012-08-09 23:12 UTC (permalink / raw)
To: linux-kernel
Cc: rcj, adlai, linux-security-module, tpmdd-devel, adlai, key,
linuxppc-dev
In-Reply-To: <1344553253.28791.7.camel@footlong>
This patch instantiate Stored Measurement Log (SML) and put the
log address and size in the device tree.
Signed-off-by: Ashley Lai <adlai@us.ibm.com>
---
| 64 +++++++++++++++++++++++++++++++++++++++
1 files changed, 64 insertions(+), 0 deletions(-)
--git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index 1b488e5..3f54bd4 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -1624,6 +1624,65 @@ static void __init prom_instantiate_rtas(void)
#ifdef CONFIG_PPC64
/*
+ * Allocate room for and instantiate Stored Measurement Log (SML)
+ */
+static void __init prom_instantiate_sml(void)
+{
+ phandle ibmvtpm_node;
+ ihandle ibmvtpm_inst;
+ u32 entry = 0, size = 0;
+ u64 base;
+
+ prom_debug("prom_instantiate_sml: start...\n");
+
+ ibmvtpm_node = call_prom("finddevice", 1, 1, ADDR("/ibm,vtpm"));
+ prom_debug("ibmvtpm_node: %x\n", ibmvtpm_node);
+ if (!PHANDLE_VALID(ibmvtpm_node))
+ return;
+
+ ibmvtpm_inst = call_prom("open", 1, 1, ADDR("/ibm,vtpm"));
+ if (!IHANDLE_VALID(ibmvtpm_inst)) {
+ prom_printf("opening vtpm package failed (%x)\n", ibmvtpm_inst);
+ return;
+ }
+
+ if (call_prom_ret("call-method", 2, 2, &size,
+ ADDR("sml-get-handover-size"),
+ ibmvtpm_inst) != 0) {
+ prom_printf("SML get handover size failed\n");
+ return;
+ }
+ if (size == 0)
+ return;
+
+ base = alloc_down(size, PAGE_SIZE, 0);
+ if (base == 0)
+ prom_panic("Could not allocate memory for sml\n");
+
+ prom_printf("instantiating sml at 0x%x...", base);
+
+ if (call_prom_ret("call-method", 4, 2, &entry,
+ ADDR("sml-handover"),
+ ibmvtpm_inst, size, base) != 0 || entry == 0) {
+ prom_printf("SML handover failed\n");
+ return;
+ }
+ prom_printf(" done\n");
+
+ reserve_mem(base, size);
+
+ prom_setprop(ibmvtpm_node, "/ibm,vtpm", "linux,sml-base",
+ &base, sizeof(base));
+ prom_setprop(ibmvtpm_node, "/ibm,vtpm", "linux,sml-size",
+ &size, sizeof(size));
+
+ prom_debug("sml base = 0x%x\n", base);
+ prom_debug("sml size = 0x%x\n", (long)size);
+
+ prom_debug("prom_instantiate_sml: end...\n");
+}
+
+/*
* Allocate room for and initialize TCE tables
*/
static void __init prom_initialize_tce_table(void)
@@ -2916,6 +2975,11 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
prom_instantiate_opal();
#endif
+#ifdef CONFIG_PPC64
+ /* instantiate sml */
+ prom_instantiate_sml();
+#endif
+
/*
* On non-powermacs, put all CPUs in spin-loops.
*
--
1.7.1
^ permalink raw reply related
* [PATCH V2 3/3] drivers/char/tpm: Add securityfs support for event log
From: Ashley Lai @ 2012-08-09 23:13 UTC (permalink / raw)
To: linux-kernel
Cc: rcj, adlai, linux-security-module, tpmdd-devel, adlai, key,
linuxppc-dev
In-Reply-To: <1344553253.28791.7.camel@footlong>
This patch retrieves the event log data from the device tree
during file open. The event log data will then displayed through
securityfs.
Signed-off-by: Ashley Lai <adlai@us.ibm.com>
---
| 8 ++++-
| 12 ------
| 7 ++++
| 14 +++++++
| 21 +++++++++++
| 68 +++++++++++++++++++++++++++++++++++++
6 files changed, 117 insertions(+), 13 deletions(-)
create mode 100644 drivers/char/tpm/tpm_noeventlog.c
create mode 100644 drivers/char/tpm/tpm_of.c
--git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 547509d..b53da57 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -2,9 +2,15 @@
# Makefile for the kernel tpm device drivers.
#
obj-$(CONFIG_TCG_TPM) += tpm.o
+obj-$(CONFIG_TCG_TPM) += tpm_bios.o
ifdef CONFIG_ACPI
- obj-$(CONFIG_TCG_TPM) += tpm_bios.o
tpm_bios-objs += tpm_eventlog.o tpm_acpi.o
+else
+ifdef CONFIG_TCG_IBMVTPM
+ tpm_bios-objs += tpm_eventlog.o tpm_of.o
+else
+ tpm_bios-objs += tpm_eventlog.o tpm_noeventlog.o
+endif
endif
obj-$(CONFIG_TCG_TIS) += tpm_tis.o
obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
--git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index aece857..08f6a07 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -304,15 +304,3 @@ extern int tpm_pm_suspend(struct device *, pm_message_t);
extern int tpm_pm_resume(struct device *);
extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
wait_queue_head_t *);
-#ifdef CONFIG_ACPI
-extern struct dentry ** tpm_bios_log_setup(char *);
-extern void tpm_bios_log_teardown(struct dentry **);
-#else
-static inline struct dentry ** tpm_bios_log_setup(char *name)
-{
- return NULL;
-}
-static inline void tpm_bios_log_teardown(struct dentry **dir)
-{
-}
-#endif
--git a/drivers/char/tpm/tpm_eventlog.c b/drivers/char/tpm/tpm_eventlog.c
index 84ddc55..814b202 100644
--- a/drivers/char/tpm/tpm_eventlog.c
+++ b/drivers/char/tpm/tpm_eventlog.c
@@ -24,6 +24,7 @@
#include <linux/security.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/of.h>
#include "tpm.h"
#include "tpm_eventlog.h"
@@ -369,6 +370,9 @@ struct dentry **tpm_bios_log_setup(char *name)
{
struct dentry **ret = NULL, *tpm_dir, *bin_file, *ascii_file;
+ if (TPM_NO_EVENT_LOG)
+ return NULL;
+
tpm_dir = securityfs_create_dir(name, NULL);
if (is_bad(tpm_dir))
goto out;
@@ -412,6 +416,9 @@ void tpm_bios_log_teardown(struct dentry **lst)
{
int i;
+ if (TPM_NO_EVENT_LOG)
+ return;
+
for (i = 0; i < 3; i++)
securityfs_remove(lst[i]);
}
--git a/drivers/char/tpm/tpm_eventlog.h b/drivers/char/tpm/tpm_eventlog.h
index 8e23ccd..21ac6af 100644
--- a/drivers/char/tpm/tpm_eventlog.h
+++ b/drivers/char/tpm/tpm_eventlog.h
@@ -68,4 +68,18 @@ enum tcpa_pc_event_ids {
};
int read_log(struct tpm_bios_log *log);
+
+extern struct dentry **tpm_bios_log_setup(char *);
+extern void tpm_bios_log_teardown(struct dentry **);
+
+#ifdef CONFIG_PPC64
+#define TPM_NO_EVENT_LOG !of_find_node_by_name(NULL, "ibm,vtpm")
+#else
+#ifdef CONFIG_ACPI
+#define TPM_NO_EVENT_LOG 0
+#else
+#define TPM_NO_EVENT_LOG 1
+#endif
+#endif
+
#endif
--git a/drivers/char/tpm/tpm_noeventlog.c b/drivers/char/tpm/tpm_noeventlog.c
new file mode 100644
index 0000000..f30a2bf
--- /dev/null
+++ b/drivers/char/tpm/tpm_noeventlog.c
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012 IBM Corporation
+ *
+ * Author: Ashley Lai <adlai@us.ibm.com>
+ *
+ * Maintained by: <tpmdd-devel@lists.sourceforge.net>
+ *
+ * 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 "tpm_eventlog.h"
+
+int read_log(struct tpm_bios_log *log)
+{
+ return -EINVAL;
+}
--git a/drivers/char/tpm/tpm_of.c b/drivers/char/tpm/tpm_of.c
new file mode 100644
index 0000000..6d44adb
--- /dev/null
+++ b/drivers/char/tpm/tpm_of.c
@@ -0,0 +1,68 @@
+/*
+ * 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__);
+ return -EIO;
+ }
+ if (*sizep == 0) {
+ pr_err("%s: ERROR - event log area empty\n", __func__);
+ return -EIO;
+ }
+
+ basep = of_get_property(np, "linux,sml-base", NULL);
+ if (basep == NULL) {
+ pr_err(KERN_ERR "%s: ERROR - SML not found\n", __func__);
+ return -EIO;
+ }
+
+ 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;
+}
--
1.7.1
^ permalink raw reply related
* [PATCH V2 1/3] drivers/char/tpm: Add new device driver to support IBM vTPM
From: Ashley Lai @ 2012-08-09 23:12 UTC (permalink / raw)
To: linux-kernel
Cc: rcj, adlai, linux-security-module, tpmdd-devel, adlai, key,
linuxppc-dev
In-Reply-To: <1344553253.28791.7.camel@footlong>
This patch adds a new device driver to support IBM virtual TPM
(vTPM) for PPC64. IBM vTPM is supported through the adjunct
partition with firmware release 740 or higher. With vTPM
support, each lpar is able to have its own vTPM without the
physical TPM hardware.
This driver provides TPM functionalities by communicating with
the vTPM adjunct partition through Hypervisor calls (Hcalls)
and Command/Response Queue (CRQ) commands.
Signed-off-by: Ashley Lai <adlai@us.ibm.com>
---
| 8 +
| 1 +
| 1 +
| 750 ++++++++++++++++++++++++++++++++++++++++
| 83 +++++
5 files changed, 843 insertions(+), 0 deletions(-)
create mode 100644 drivers/char/tpm/tpm_ibmvtpm.c
create mode 100644 drivers/char/tpm/tpm_ibmvtpm.h
--git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index c4aac48..915875e 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -73,4 +73,12 @@ config TCG_INFINEON
Further information on this driver and the supported hardware
can be found at http://www.trust.rub.de/projects/linux-device-driver-infineon-tpm/
+config TCG_IBMVTPM
+ tristate "IBM VTPM Interface"
+ depends on PPC64
+ ---help---
+ If you have IBM virtual TPM (VTPM) support say Yes and it
+ will be accessible from within Linux. To compile this driver
+ as a module, choose M here; the module will be called tpm_ibmvtpm.
+
endif # TCG_TPM
--git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index beac52f..547509d 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
+obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
--git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index b1c5280..aece857 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -94,6 +94,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;
--git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c
new file mode 100644
index 0000000..491133f
--- /dev/null
+++ b/drivers/char/tpm/tpm_ibmvtpm.c
@@ -0,0 +1,750 @@
+/*
+ * 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");
--git a/drivers/char/tpm/tpm_ibmvtpm.h b/drivers/char/tpm/tpm_ibmvtpm.h
new file mode 100644
index 0000000..fb930dd
--- /dev/null
+++ b/drivers/char/tpm/tpm_ibmvtpm.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ *
+ * These difference are required on power because the device must be
+ * discovered through the device tree and iomap must be used to get
+ * around the need for holes in the io_page_mask. This does not happen
+ * automatically because the tpm is not a normal pci device and lives
+ * under the root node.
+ *
+ */
+
+#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
--
1.7.1
^ permalink raw reply related
* RE: [PATCH 5/6] powerpc/fsl-pci: Add pci inbound/outbound PM support
From: Jia Hongtao-B38951 @ 2012-08-10 2:17 UTC (permalink / raw)
To: Kumar Gala, Li Yang-R58472
Cc: Wood Scott-B07421, linuxppc-dev@lists.ozlabs.org, Li Yang-R58472
In-Reply-To: <90115399-5F52-44AC-99F7-86738AABDA66@kernel.crashing.org>
> -----Original Message-----
> From: Kumar Gala [mailto:galak@kernel.crashing.org]
> Sent: Thursday, August 09, 2012 9:08 PM
> To: Li Yang-R58472
> Cc: Jia Hongtao-B38951; Wood Scott-B07421; linuxppc-dev@lists.ozlabs.org;
> Li Yang-R58472
> Subject: Re: [PATCH 5/6] powerpc/fsl-pci: Add pci inbound/outbound PM
> support
>=20
>=20
> On Aug 9, 2012, at 12:05 AM, Li Yang wrote:
>=20
> > On Thu, Aug 9, 2012 at 10:52 AM, Jia Hongtao-B38951
> > <B38951@freescale.com> wrote:
> >>
> >>
> >>> -----Original Message-----
> >>> From: Linuxppc-dev [mailto:linuxppc-dev-
> >>> bounces+b38951=3Dfreescale.com@lists.ozlabs.org] On Behalf Of Kumar
> >>> bounces+Gala
> >>> Sent: Wednesday, August 08, 2012 8:47 PM
> >>> To: Jia Hongtao-B38951
> >>> Cc: Wood Scott-B07421; linuxppc-dev@lists.ozlabs.org; Li Yang-R58472
> >>> Subject: Re: [PATCH 5/6] powerpc/fsl-pci: Add pci inbound/outbound
> >>> PM support
> >>>
> >>>>>>>>>
> >>>>>>>>> On Jul 24, 2012, at 5:20 AM, Jia Hongtao wrote:
> >>>>>>>>>
> >>>>>>>>>> Power supply for PCI inbound/outbound window registers is off
> >>>>>>>>>> when
> >>>>>>>>> system
> >>>>>>>>>> go to deep-sleep state. We save the values of registers
> >>>>>>>>>> before suspend and restore to registers after resume.
> >>>>>>>>>>
> >>>>>>>>>> Signed-off-by: Jiang Yutang <b14898@freescale.com>
> >>>>>>>>>> Signed-off-by: Jia Hongtao <B38951@freescale.com>
> >>>>>>>>>> Signed-off-by: Li Yang <leoli@freescale.com>
> >>>>>>>>>> ---
> >>>>>>>>>> arch/powerpc/include/asm/pci-bridge.h | 2 +-
> >>>>>>>>>> arch/powerpc/sysdev/fsl_pci.c | 121
> >>>>>>>>> +++++++++++++++++++++++++++++++++
> >>>>>>>>>> 2 files changed, 122 insertions(+), 1 deletions(-)
> >>>>>>>>>
> >>>>>>>>> Remind me why we need to save/restore PCI ATMUs, why not just
> >>>>>>>>> re-parse the device tree to restore?
> >>>>>>>>>
> >>>>>>>>> - k
> >>>>>>>>
> >>>>>>>> Save/restore is the more efficient way. Latency of sleep/wakeup
> >>>>>>>> is one of most important features in power management.
> >>>>>>>>
> >>>>>>>> -Hongtao.
> >>>>>>>
> >>>>>>> I don't think the time it takes to run through setup_pci_atmu()
> >>>>>>> is that long compared to fsl_pci_resume().
> >>>>>>>
> >>>>>>> Also, don't you need to setup PCICCSRBAR and do setup_pci_cmd()
> >>>>>>> on
> >>>>> resume?
> >>>>>>>
> >>>>>>> - k
> >>>>>>
> >>>>>> Hi Kumar,
> >>>>>> I did some tests on P1022DS and found out that PCI_CMD and
> >>>>>> PCICSRBAR is not lost when system in deep sleep. We don't need to
> save it.
> >>>>>
> >>>>> How does the PCI code know you're entering deep sleep and not
> >>> hibernation?
> >>>>>
> >>>>> -Scott
> >>>>
> >>>> When system come back from hibernation PCI will be initialized again=
.
> >>>> So no need to save PCI_CMD and PEXCSRBAR.
> >>>>
> >>>> -Hongtao.
> >>>>
> >>>
> >>> What do you mean PCI will be initialized again? What code path are
> >>> you talking about that would set PCI_CMD & PEXCSRBAR?
> >>>
> >>> - k
> >>
> >>
> >> In hibernation mode:
> >>
> >> When system come back from hibernation kernel will start up again.
> >> Before loading hibernation image PCI initialization has already done.
> >> Some other hardware also re-init again.
> >
> > In current Linux implementation, restoring from hibernation image is
> > using late initcall. By that time, all the platform devices are
> > already initialized like a fresh boot.
> >
> > - Leo
>=20
> I ask against, what specific code path (I'd like a call trace) would
> cause PCI_CMD and PEXCSRBAR to be set in the wakeup case?
>=20
> - k
The code path is the same as a fresh boot:
fsl_add_bridge()
-> setup_pci_cmd() [PCI_CMD is set]
-> setup_pci_atmu() [PEXCSRBAR is set]
- Hongtao.
^ permalink raw reply
* [PATCH v2 1/2] powerpc/mpic: Add Open-PIC global timer document
From: Dongsheng.wang @ 2012-08-10 5:53 UTC (permalink / raw)
To: benh, paulus
Cc: devicetree-discuss, Wang Dongsheng, scottwood, linuxppc-dev,
kumar.gala
From: Wang Dongsheng <Dongsheng.Wang@freescale.com>
Add a description of the OPEN-PIC global timer in the OPEN-PIC document.
Moidfy mpic-timer document. 1.Add a TFRR register region. This register
is written by software to report the clocking frequency of the PIC timers.
2.Add a device_type. The global timer in line with the OPEN-PIC specification.
Signed-off-by: Wang Dongsheng <Dongsheng.Wang@freescale.com>
Signed-off-by: Li Yang <leoli@freescale.com>
---
Documentation/devicetree/bindings/open-pic.txt | 46 ++++++++++++++++++++
.../devicetree/bindings/powerpc/fsl/mpic-timer.txt | 21 +++++----
arch/powerpc/boot/dts/fsl/pq3-mpic-timer-B.dtsi | 7 ++-
arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi | 7 ++-
4 files changed, 66 insertions(+), 15 deletions(-)
diff --git a/Documentation/devicetree/bindings/open-pic.txt b/Documentation/devicetree/bindings/open-pic.txt
index 909a902..045c2e9 100644
--- a/Documentation/devicetree/bindings/open-pic.txt
+++ b/Documentation/devicetree/bindings/open-pic.txt
@@ -92,6 +92,52 @@ Example 2:
* References
+* Open PIC global timers
+
+Required properties:
+- compatible: "open-pic,global-timer"
+
+- reg : Contains two regions. The first is the timer frequency reporting
+ register for the group. The second is the main timer register bank
+ (GTCCR, GTBCR, GTVPR, GTDR).
+
+- available-ranges: use <start count> style section to define which
+ timer interrupts can be used. This property is optional; without this,
+ all timers within the group can be used.
+
+- interrupts: one interrupt per timer in the group, in order, starting
+ with timer zero. If available-ranges is present, only the interrupts
+ that correspond to available timers shall be present.
+
+* Examples
+
+Example 1:
+
+ /* Note that this requires #interrupt-cells to be 4 */
+ timer: timer@010f0 {
+ compatible = "open-pic,global-timer";
+ device_type = "open-pic";
+ reg = <0x010f0 4 0x01100 0x100>;
+
+ /* Another AMP partition is using timer */
+ available-ranges = <2 2>;
+
+ interrupts = <2 0 3 0
+ 3 0 3 0>;
+ };
+
+Example 2:
+
+ timer: timer@010f0 {
+ compatible = "open-pic,global-timer";
+ device_type = "open-pic";
+ reg = <0x010f0 4 0x01100 0x100>;
+ interrupts = <0 0 3 0
+ 1 0 3 0
+ 2 0 3 0
+ 3 0 3 0>;
+ };
+
[1] Power.org (TM) Standard for Embedded Power Architecture (TM) Platform
Requirements (ePAPR), Version 1.0, July 2008.
(http://www.power.org/resources/downloads/Power_ePAPR_APPROVED_v1.0.pdf)
diff --git a/Documentation/devicetree/bindings/powerpc/fsl/mpic-timer.txt b/Documentation/devicetree/bindings/powerpc/fsl/mpic-timer.txt
index df41958..5aafca0 100644
--- a/Documentation/devicetree/bindings/powerpc/fsl/mpic-timer.txt
+++ b/Documentation/devicetree/bindings/powerpc/fsl/mpic-timer.txt
@@ -1,13 +1,14 @@
* Freescale MPIC timers
Required properties:
-- compatible: "fsl,mpic-global-timer"
+- compatible: "fsl,global-timer"
-- reg : Contains two regions. The first is the main timer register bank
- (GTCCRxx, GTBCRxx, GTVPRxx, GTDRxx). The second is the timer control
+- reg : Contains three regions. The first is the timer frequency reporting
+ register (TFRRx) for the group. The second is the main timer register
+ bank (GTCCRxx, GTBCRxx, GTVPRxx, GTDRxx). The third is the timer control
register (TCRx) for the group.
-- fsl,available-ranges: use <start count> style section to define which
+- available-ranges: use <start count> style section to define which
timer interrupts can be used. This property is optional; without this,
all timers within the group can be used.
@@ -18,19 +19,21 @@ Required properties:
Example:
/* Note that this requires #interrupt-cells to be 4 */
timer0: timer@41100 {
- compatible = "fsl,mpic-global-timer";
- reg = <0x41100 0x100 0x41300 4>;
+ compatible = "fsl,global-timer";
+ device_type = "open-pic";
+ reg = <0x410f0 4 0x41100 0x100 0x41300 4>;
/* Another AMP partition is using timers 0 and 1 */
- fsl,available-ranges = <2 2>;
+ available-ranges = <2 2>;
interrupts = <2 0 3 0
3 0 3 0>;
};
timer1: timer@42100 {
- compatible = "fsl,mpic-global-timer";
- reg = <0x42100 0x100 0x42300 4>;
+ compatible = "fsl,global-timer";
+ device_type = "open-pic";
+ reg = <0x420f0 4 0x42100 0x100 0x42300 4>;
interrupts = <4 0 3 0
5 0 3 0
6 0 3 0
diff --git a/arch/powerpc/boot/dts/fsl/pq3-mpic-timer-B.dtsi b/arch/powerpc/boot/dts/fsl/pq3-mpic-timer-B.dtsi
index 8734cff..01cd33c 100644
--- a/arch/powerpc/boot/dts/fsl/pq3-mpic-timer-B.dtsi
+++ b/arch/powerpc/boot/dts/fsl/pq3-mpic-timer-B.dtsi
@@ -32,9 +32,10 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-timer@42100 {
- compatible = "fsl,mpic-global-timer";
- reg = <0x42100 0x100 0x42300 4>;
+timer@420f0 {
+ compatible = "fsl,global-timer";
+ device_type = "open-pic";
+ reg = <0x420f0 4 0x42100 0x100 0x42300 4>;
interrupts = <4 0 3 0
5 0 3 0
6 0 3 0
diff --git a/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi b/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi
index 71c30eb..c71d8e0 100644
--- a/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi
+++ b/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi
@@ -44,9 +44,10 @@ mpic: pic@40000 {
last-interrupt-source = <255>;
};
-timer@41100 {
- compatible = "fsl,mpic-global-timer";
- reg = <0x41100 0x100 0x41300 4>;
+timer@410f0 {
+ compatible = "fsl,global-timer";
+ device_type = "open-pic";
+ reg = <0x410f0 4 0x41100 0x100 0x41300 4>;
interrupts = <0 0 3 0
1 0 3 0
2 0 3 0
--
1.7.5.1
^ permalink raw reply related
* [PATCH v2 2/2] powerpc/mpic: add global timer support
From: Dongsheng.wang @ 2012-08-10 5:54 UTC (permalink / raw)
To: benh, paulus; +Cc: scottwood, kumar.gala, linuxppc-dev, Wang Dongsheng
From: Wang Dongsheng <Dongsheng.Wang@freescale.com>
The MPIC global timer is a hardware timer inside the Freescale PIC comply
to Open-PIC standard. When the timer is timeout of the specified interval,
the hardware timer generates an interrupt. The driver currently is only
tested on fsl chip, but it can potentially support other global timers
complying to Open-PIC standard.
The two independent groups of global timer on fsl chip, group A and group B,
are identical in their functionality, except that they appear at different
locations within the PIC register map. The hardware timer can be cascaded to
create timers larger than the default 31-bit global timers. Timer cascade
fields allow configuration of up to two 63-bit timers. But These two groups
of timers cannot be cascaded together.
It can be used as a wakeup source for low power modes. It also could be used
as periodical timer for protocols, drivers and etc.
Signed-off-by: Wang Dongsheng <Dongsheng.Wang@freescale.com>
Signed-off-by: Li Yang <leoli@freescale.com>
---
arch/powerpc/include/asm/mpic_timer.h | 15 +
arch/powerpc/platforms/Kconfig | 5 +
arch/powerpc/sysdev/Makefile | 1 +
arch/powerpc/sysdev/mpic_timer.c | 525 +++++++++++++++++++++++++++++++++
4 files changed, 546 insertions(+), 0 deletions(-)
create mode 100644 arch/powerpc/include/asm/mpic_timer.h
create mode 100644 arch/powerpc/sysdev/mpic_timer.c
diff --git a/arch/powerpc/include/asm/mpic_timer.h b/arch/powerpc/include/asm/mpic_timer.h
new file mode 100644
index 0000000..01d58a2
--- /dev/null
+++ b/arch/powerpc/include/asm/mpic_timer.h
@@ -0,0 +1,15 @@
+#ifndef __MPIC_TIMER__
+#define __MPIC_TIMER__
+
+#include <linux/interrupt.h>
+#include <linux/time.h>
+
+struct mpic_timer *mpic_request_timer(irq_handler_t fn, void *dev,
+ const struct timeval *time);
+
+void mpic_start_timer(struct mpic_timer *handle);
+
+void mpic_stop_timer(struct mpic_timer *handle);
+
+void mpic_free_timer(struct mpic_timer *handle);
+#endif
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index e7a896a..d82a822 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -87,6 +87,11 @@ config MPIC
bool
default n
+config MPIC_TIMER
+ bool "MPIC Global Timer"
+ depends on MPIC && FSL_SOC
+ default n
+
config PPC_EPAPR_HV_PIC
bool
default n
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 1bd7ecb..b1a9e4d 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -6,6 +6,7 @@ mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o
obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y)
mpic-msgr-obj-$(CONFIG_MPIC_MSGR) += mpic_msgr.o
obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) $(mpic-msgr-obj-y)
+obj-$(CONFIG_MPIC_TIMER) += mpic_timer.o
obj-$(CONFIG_PPC_EPAPR_HV_PIC) += ehv_pic.o
fsl-msi-obj-$(CONFIG_PCI_MSI) += fsl_msi.o
obj-$(CONFIG_PPC_MSI_BITMAP) += msi_bitmap.o
diff --git a/arch/powerpc/sysdev/mpic_timer.c b/arch/powerpc/sysdev/mpic_timer.c
new file mode 100644
index 0000000..1b3ee59
--- /dev/null
+++ b/arch/powerpc/sysdev/mpic_timer.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright (c) 2012 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <sysdev/fsl_soc.h>
+#include <asm/mpic_timer.h>
+
+#define FSL_GLOBAL_TIMER 0x1
+
+#define MPIC_TIMER_TCR_CLKDIV_64 0x00000300
+#define MPIC_TIMER_TCR_ROVR_OFFSET 24
+
+#define TIMER_STOP 0x80000000
+#define ALL_TIMER 4
+#define MAX_TICKS (~0U>>1)
+#define MAX_TICKS_CASCADE (~0U)
+#define TIMER_OFFSET(num) (1 << (ALL_TIMER - 1 - num))
+#define ONE_SECOND 1000000
+
+struct timer_regs {
+ u32 gtccr;
+ u32 res0[3];
+ u32 gtbcr;
+ u32 res1[3];
+ u32 gtvpr;
+ u32 res2[3];
+ u32 gtdr;
+ u32 res3[3];
+};
+
+struct cascade_priv {
+ u32 tcr_value; /* TCR register: CASC & ROVR value */
+ unsigned int cascade_map; /* cascade map */
+ unsigned int timer_num; /* cascade control timer */
+};
+
+struct mpic_timer {
+ void *dev;
+ struct cascade_priv *cascade_handle;
+ unsigned int num;
+ int irq;
+};
+
+struct group_priv {
+ struct timer_regs __iomem *regs;
+ struct mpic_timer timer[ALL_TIMER];
+ struct list_head node;
+ unsigned int timerfreq;
+ unsigned int idle;
+ unsigned int flags;
+ spinlock_t lock;
+ void __iomem *group_tcr;
+ void __iomem *group_tfrr;
+};
+
+static struct cascade_priv cascade_timer[] = {
+ /* cascade timer 0 and 1 */
+ {0x1, 0xc, 0x1},
+ /* cascade timer 1 and 2 */
+ {0x2, 0x6, 0x2},
+ /* cascade timer 2 and 3 */
+ {0x4, 0x3, 0x3}
+};
+
+static u32 ccbfreq;
+static LIST_HEAD(group_list);
+
+/* the time set by the user is converted to "ticks" */
+static int transform_time(struct group_priv *priv, const struct timeval *time,
+ u64 *ticks)
+{
+ u64 max_value; /* prevent u64 overflow */
+ u64 tmp = 0;
+
+ u64 tmp_sec = 0;
+ u64 tmp_ms = 0;
+ u64 tmp_us = 0;
+ u32 div = 0;
+
+ if (priv->flags & FSL_GLOBAL_TIMER)
+ max_value = div_u64(ULLONG_MAX, ccbfreq);
+ else
+ max_value = div_u64(ULLONG_MAX, priv->timerfreq);
+
+ if (time->tv_sec > max_value ||
+ (time->tv_sec == max_value && time->tv_usec > 0))
+ return -EINVAL;
+
+ if (!(priv->flags & FSL_GLOBAL_TIMER)) {
+ tmp = time->tv_sec * priv->timerfreq;
+ *ticks = tmp;
+
+ return 0;
+ }
+
+ div = (1 << (MPIC_TIMER_TCR_CLKDIV_64 >> 8)) * 8;
+
+ tmp_sec = div_u64((u64)time->tv_sec * (u64)ccbfreq, div);
+ tmp += tmp_sec;
+
+ tmp_ms = time->tv_usec / 1000;
+ tmp_ms = div_u64((u64)tmp_ms * (u64)ccbfreq, div * 1000);
+ tmp += tmp_ms;
+
+ tmp_us = time->tv_usec % 1000;
+ tmp_us = div_u64((u64)tmp_us * (u64)ccbfreq, div * 1000000);
+ tmp += tmp_us;
+
+ *ticks = tmp;
+
+ return 0;
+}
+
+/* detect whether there is a cascade timer available */
+static struct mpic_timer *detect_idle_cascade_timer(struct group_priv *priv)
+{
+ struct cascade_priv *casc_priv;
+ unsigned int tmp;
+ unsigned int array_size = ARRAY_SIZE(cascade_timer);
+ unsigned int num;
+ unsigned int i;
+ unsigned long flags;
+
+ casc_priv = cascade_timer;
+ for (i = 0; i < array_size; i++) {
+ spin_lock_irqsave(&priv->lock, flags);
+ tmp = casc_priv->cascade_map & priv->idle;
+ if (tmp == casc_priv->cascade_map) {
+ num = casc_priv->timer_num;
+ priv->timer[num].cascade_handle = casc_priv;
+
+ /* set timer busy */
+ priv->idle &= ~casc_priv->cascade_map;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return &priv->timer[num];
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+ casc_priv++;
+ }
+
+ return NULL;
+}
+
+static int set_cascade_timer(struct group_priv *priv, u64 ticks,
+ unsigned int num)
+{
+ struct cascade_priv *casc_priv;
+ u32 tmp;
+ u32 tmp_ticks;
+ u32 rem_ticks;
+
+ /* set group tcr reg for cascade */
+ casc_priv = priv->timer[num].cascade_handle;
+ if (!casc_priv)
+ return -EINVAL;
+
+ tmp = casc_priv->tcr_value |
+ (casc_priv->tcr_value << MPIC_TIMER_TCR_ROVR_OFFSET);
+ setbits32(priv->group_tcr, tmp);
+
+ tmp_ticks = div_u64_rem(ticks, MAX_TICKS_CASCADE, &rem_ticks);
+
+ out_be32(&priv->regs[num].gtccr, 0);
+ out_be32(&priv->regs[num].gtbcr, tmp_ticks | TIMER_STOP);
+
+ out_be32(&priv->regs[num - 1].gtccr, 0);
+ out_be32(&priv->regs[num - 1].gtbcr, rem_ticks);
+
+ return 0;
+}
+
+static struct mpic_timer *get_cascade_timer(struct group_priv *priv, u64 ticks)
+{
+ struct mpic_timer *allocated_timer = NULL;
+
+ /* Two cascade timers: Support the maximum time */
+ const u64 max_ticks = (u64)MAX_TICKS * (u64)MAX_TICKS_CASCADE;
+ int ret;
+
+ if (ticks > max_ticks)
+ return NULL;
+
+ /* detect idle timer */
+ allocated_timer = detect_idle_cascade_timer(priv);
+ if (!allocated_timer)
+ return NULL;
+
+ /* set ticks to timer */
+ ret = set_cascade_timer(priv, ticks, allocated_timer->num);
+ if (ret < 0)
+ return NULL;
+
+ return allocated_timer;
+}
+
+static struct mpic_timer *get_timer(const struct timeval *time)
+{
+ struct group_priv *priv;
+ struct mpic_timer *timer = NULL;
+
+ u64 ticks = 0;
+ unsigned int num;
+ unsigned int i;
+ unsigned long flags;
+ int ret = 0;
+
+ list_for_each_entry(priv, &group_list, node) {
+ ret = transform_time(priv, time, &ticks);
+ if (ret < 0)
+ return NULL;
+
+ if (ticks > MAX_TICKS) {
+ if (!(priv->flags & FSL_GLOBAL_TIMER))
+ return NULL;
+
+ timer = get_cascade_timer(priv, ticks);
+ if (!timer)
+ continue;
+ else
+ return timer;
+ }
+
+ for (i = 0; i < ALL_TIMER; i++) {
+ /* one timer: Reverse allocation */
+ num = ALL_TIMER - 1 - i;
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->idle & (1 << i)) {
+ /* set timer busy */
+ priv->idle &= ~(1 << i);
+ /* set ticks & stop timer */
+ out_be32(&priv->regs[num].gtbcr,
+ ticks | TIMER_STOP);
+ out_be32(&priv->regs[num].gtccr, 0);
+ priv->timer[num].cascade_handle = NULL;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return &priv->timer[num];
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * mpic_request_timer - get a hardware timer
+ * @fn: interrupt handler function
+ * @dev: callback function of the data
+ * @time: time for timer
+ *
+ * This executes the "request_irq", returning NULL
+ * else "handle" on success.
+ */
+struct mpic_timer *mpic_request_timer(irq_handler_t fn, void *dev,
+ const struct timeval *time)
+{
+ struct mpic_timer *allocated_timer = NULL;
+
+ int ret = 0;
+
+ if (list_empty(&group_list))
+ return NULL;
+
+ if ((time->tv_sec + time->tv_usec) == 0 ||
+ time->tv_sec < 0 || time->tv_usec < 0)
+ return NULL;
+
+ if (time->tv_usec > ONE_SECOND)
+ return NULL;
+
+ allocated_timer = get_timer(time);
+ if (!allocated_timer)
+ return NULL;
+
+ ret = request_irq(allocated_timer->irq, fn, IRQF_TRIGGER_LOW,
+ "global-timer", dev);
+ if (ret)
+ return NULL;
+
+ allocated_timer->dev = dev;
+
+ return allocated_timer;
+}
+EXPORT_SYMBOL_GPL(mpic_request_timer);
+
+/**
+ * mpic_start_timer - start hardware timer
+ * @handle: the timer to be started.
+ *
+ * It will do ->fn(->dev) callback from the hardware interrupt at
+ * the ->timeval point in the future.
+ */
+void mpic_start_timer(struct mpic_timer *handle)
+{
+ struct group_priv *priv = container_of(handle, struct group_priv,
+ timer[handle->num]);
+
+ clrbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP);
+}
+EXPORT_SYMBOL_GPL(mpic_start_timer);
+
+/**
+ * mpic_stop_timer - stop hardware timer
+ * @handle: the timer to be stoped
+ *
+ * The timer periodically generates an interrupt. Unless user stops the timer.
+ */
+void mpic_stop_timer(struct mpic_timer *handle)
+{
+ struct group_priv *priv = container_of(handle, struct group_priv,
+ timer[handle->num]);
+
+ setbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP);
+}
+EXPORT_SYMBOL_GPL(mpic_stop_timer);
+
+/**
+ * mpic_free_timer - free hardware timer
+ * @handle: the timer to be removed.
+ *
+ * Free the timer.
+ *
+ * Note: can not be used in interrupt context.
+ */
+void mpic_free_timer(struct mpic_timer *handle)
+{
+ struct group_priv *priv = container_of(handle, struct group_priv,
+ timer[handle->num]);
+
+ struct cascade_priv *casc_priv = NULL;
+ unsigned long flags;
+
+ mpic_stop_timer(handle);
+
+ casc_priv = priv->timer[handle->num].cascade_handle;
+
+ free_irq(priv->timer[handle->num].irq, priv->timer[handle->num].dev);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (casc_priv) {
+ u32 tmp;
+ tmp = casc_priv->tcr_value | (casc_priv->tcr_value <<
+ MPIC_TIMER_TCR_ROVR_OFFSET);
+ clrbits32(priv->group_tcr, tmp);
+ priv->idle |= casc_priv->cascade_map;
+ priv->timer[handle->num].cascade_handle = NULL;
+ } else {
+ priv->idle |= TIMER_OFFSET(handle->num);
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+EXPORT_SYMBOL_GPL(mpic_free_timer);
+
+static int group_get_freq(struct group_priv *priv)
+{
+ if (priv->flags & FSL_GLOBAL_TIMER) {
+ ccbfreq = fsl_get_sys_freq();
+ priv->timerfreq = ccbfreq;
+ } else {
+ priv->timerfreq = in_be32(priv->group_tfrr);
+ }
+
+ if (priv->timerfreq <= 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int group_init_regmap(struct device_node *np, struct group_priv *priv)
+{
+ priv->group_tfrr = of_iomap(np, 0);
+ if (!priv->group_tfrr) {
+ pr_err("%s: cannot ioremap tfrr address.\n",
+ np->full_name);
+ return -EINVAL;
+ }
+
+ priv->regs = of_iomap(np, 1);
+ if (!priv->regs) {
+ pr_err("%s: cannot ioremap timer register address.\n",
+ np->full_name);
+ return -EINVAL;
+ }
+
+ if (!(priv->flags & FSL_GLOBAL_TIMER))
+ return 0;
+
+ priv->group_tcr = of_iomap(np, 2);
+ if (!priv->group_tcr) {
+ pr_err("%s: cannot ioremap tcr address.\n", np->full_name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int group_get_irq(struct device_node *np, struct group_priv *priv)
+{
+ const u32 all_timer[] = { 0, ALL_TIMER };
+ const u32 *p = NULL;
+ u32 offset;
+ u32 count;
+
+ unsigned int i = 0;
+ unsigned int j = 0;
+ unsigned int irq_index = 0;
+ int irq = 0;
+ int len = 0;
+
+ p = of_get_property(np, "available-ranges", &len);
+ if (p && len % (2 * sizeof(u32)) != 0) {
+ pr_err("%s: malformed fsl,available-ranges property.\n",
+ np->full_name);
+ return -EINVAL;
+ }
+
+ if (!p) {
+ p = all_timer;
+ len = sizeof(all_timer);
+ }
+
+ len /= 2 * sizeof(u32);
+
+ for (i = 0; i < len; i++) {
+ offset = p[i * 2];
+ count = p[i * 2 + 1];
+ for (j = 0; j < count; j++) {
+ irq = irq_of_parse_and_map(np, irq_index);
+ if (!irq)
+ break;
+ /* Set timer idle */
+ priv->idle |= TIMER_OFFSET((offset + j));
+ priv->timer[offset + j].irq = irq;
+ priv->timer[offset + j].num = offset + j;
+ irq_index++;
+ }
+ }
+
+ return 0;
+}
+
+static void group_init(struct device_node *np)
+{
+ struct group_priv *priv = NULL;
+ int ret = 0;
+
+ priv = kzalloc(sizeof(struct group_priv), GFP_KERNEL);
+ if (!priv) {
+ pr_err("%s: cannot allocate memory for group.\n",
+ np->full_name);
+ return;
+ }
+
+ if (of_device_is_compatible(np, "fsl,global-timer"))
+ priv->flags |= FSL_GLOBAL_TIMER;
+
+ ret = group_init_regmap(np, priv);
+ if (ret < 0)
+ goto out;
+
+ ret = group_get_freq(priv);
+ if (ret < 0)
+ goto out;
+
+ ret = group_get_irq(np, priv);
+ if (ret < 0)
+ goto out;
+
+ spin_lock_init(&priv->lock);
+
+ /* Init FSL timer hardware */
+ if (priv->flags & FSL_GLOBAL_TIMER)
+ setbits32(priv->group_tcr, MPIC_TIMER_TCR_CLKDIV_64);
+
+ list_add_tail(&priv->node, &group_list);
+
+ return;
+out:
+ if (priv->group_tcr)
+ iounmap(priv->group_tcr);
+
+ if (priv->regs)
+ iounmap(priv->regs);
+
+ if (priv->group_tfrr)
+ iounmap(priv->group_tfrr);
+
+ kfree(priv);
+}
+
+static const struct of_device_id mpic_timer_ids[] = {
+ { .compatible = "open-pic,global-timer", },
+ { .compatible = "fsl,global-timer", },
+ {},
+};
+
+static int __init mpic_timer_init(void)
+{
+ struct device_node *np = NULL;
+
+ for_each_node_by_type(np, "open-pic")
+ if (of_match_node(mpic_timer_ids, np))
+ group_init(np);
+
+ if (list_empty(&group_list))
+ return -ENODEV;
+
+ return 0;
+}
+arch_initcall(mpic_timer_init);
--
1.7.5.1
^ permalink raw reply related
* Re: [PATCH V2 3/3] drivers/char/tpm: Add securityfs support for event log
From: Michael Ellerman @ 2012-08-10 7:23 UTC (permalink / raw)
To: Ashley Lai
Cc: key, linux-kernel, linux-security-module, tpmdd-devel, adlai, rcj,
linuxppc-dev
In-Reply-To: <1344553986.28791.13.camel@footlong>
On Thu, 2012-08-09 at 18:13 -0500, Ashley Lai wrote:
> This patch retrieves the event log data from the device tree
> during file open. The event log data will then displayed through
> securityfs.
Hi Ashley,
Comments inline ..
> diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
> index 547509d..b53da57 100644
> --- a/drivers/char/tpm/Makefile
> +++ b/drivers/char/tpm/Makefile
> @@ -2,9 +2,15 @@
> # Makefile for the kernel tpm device drivers.
> #
> obj-$(CONFIG_TCG_TPM) += tpm.o
> +obj-$(CONFIG_TCG_TPM) += tpm_bios.o
> ifdef CONFIG_ACPI
> - obj-$(CONFIG_TCG_TPM) += tpm_bios.o
> tpm_bios-objs += tpm_eventlog.o tpm_acpi.o
> +else
> +ifdef CONFIG_TCG_IBMVTPM
> + tpm_bios-objs += tpm_eventlog.o tpm_of.o
> +else
> + tpm_bios-objs += tpm_eventlog.o tpm_noeventlog.o
What does it mean to build tpm_eventlog and tpm_noeventlog ?
> diff --git a/drivers/char/tpm/tpm_eventlog.h b/drivers/char/tpm/tpm_eventlog.h
> index 8e23ccd..21ac6af 100644
> --- a/drivers/char/tpm/tpm_eventlog.h
> +++ b/drivers/char/tpm/tpm_eventlog.h
> @@ -68,4 +68,18 @@ enum tcpa_pc_event_ids {
> };
>
> int read_log(struct tpm_bios_log *log);
> +
> +extern struct dentry **tpm_bios_log_setup(char *);
> +extern void tpm_bios_log_teardown(struct dentry **);
> +
> +#ifdef CONFIG_PPC64
> +#define TPM_NO_EVENT_LOG !of_find_node_by_name(NULL, "ibm,vtpm")
> +#else
> +#ifdef CONFIG_ACPI
> +#define TPM_NO_EVENT_LOG 0
> +#else
> +#define TPM_NO_EVENT_LOG 1
> +#endif
> +#endif
This should be a static inline, with the ifdefs inside.
How often is this called? of_find_node_by_name() is not particularly
efficient. It might be better to search once and save a flag indicating
whether you found it.
Also you must call of_node_put() on the result of
of_find_node_by_name(), so at the least you must do:
struct device_node *np;
np = of_find_node_by_name(NULL, "ibm,vtpm");
of_node_put(np);
return (np != NULL);
> #endif
> diff --git a/drivers/char/tpm/tpm_noeventlog.c b/drivers/char/tpm/tpm_noeventlog.c
> new file mode 100644
> index 0000000..f30a2bf
> --- /dev/null
> +++ b/drivers/char/tpm/tpm_noeventlog.c
> @@ -0,0 +1,21 @@
> +/*
> + * Copyright (C) 2012 IBM Corporation
^^
Don't use (C)
> + *
> + * Author: Ashley Lai <adlai@us.ibm.com>
> + *
> + * Maintained by: <tpmdd-devel@lists.sourceforge.net>
> + *
> + * 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 "tpm_eventlog.h"
> +
> +int read_log(struct tpm_bios_log *log)
> +{
> + return -EINVAL;
> +}
Wouldn't it be simpler to put this in a header with the appropriate
ifdefs, and then you'd need less logic in the Makefile?
> diff --git a/drivers/char/tpm/tpm_of.c b/drivers/char/tpm/tpm_of.c
> new file mode 100644
> index 0000000..6d44adb
> --- /dev/null
> +++ b/drivers/char/tpm/tpm_of.c
> @@ -0,0 +1,68 @@
> +/*
> + * 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;
> + }
All your return paths below here must call of_node_put(np).
> + sizep = of_get_property(np, "linux,sml-size", NULL);
> + if (sizep == NULL) {
> + pr_err("%s: ERROR - SML size not found\n", __func__);
> + return -EIO;
> + }
> + if (*sizep == 0) {
> + pr_err("%s: ERROR - event log area empty\n", __func__);
> + return -EIO;
> + }
> +
> + basep = of_get_property(np, "linux,sml-base", NULL);
> + if (basep == NULL) {
> + pr_err(KERN_ERR "%s: ERROR - SML not found\n", __func__);
> + return -EIO;
> + }
> +
> + 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;
> +}
cheers
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox