Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 30 Jul 2016 00:38:46 +0000 (17:38 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 30 Jul 2016 00:38:46 +0000 (17:38 -0700)
Pull security subsystem updates from James Morris:
 "Highlights:

   - TPM core and driver updates/fixes
   - IPv6 security labeling (CALIPSO)
   - Lots of Apparmor fixes
   - Seccomp: remove 2-phase API, close hole where ptrace can change
     syscall #"

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (156 commits)
  apparmor: fix SECURITY_APPARMOR_HASH_DEFAULT parameter handling
  tpm: Add TPM 2.0 support to the Nuvoton i2c driver (NPCT6xx family)
  tpm: Factor out common startup code
  tpm: use devm_add_action_or_reset
  tpm2_i2c_nuvoton: add irq validity check
  tpm: read burstcount from TPM_STS in one 32-bit transaction
  tpm: fix byte-order for the value read by tpm2_get_tpm_pt
  tpm_tis_core: convert max timeouts from msec to jiffies
  apparmor: fix arg_size computation for when setprocattr is null terminated
  apparmor: fix oops, validate buffer size in apparmor_setprocattr()
  apparmor: do not expose kernel stack
  apparmor: fix module parameters can be changed after policy is locked
  apparmor: fix oops in profile_unpack() when policy_db is not present
  apparmor: don't check for vmalloc_addr if kvzalloc() failed
  apparmor: add missing id bounds check on dfa verification
  apparmor: allow SYS_CAP_RESOURCE to be sufficient to prlimit another task
  apparmor: use list_next_entry instead of list_entry_next
  apparmor: fix refcount race when finding a child profile
  apparmor: fix ref count leak when profile sha1 hash is read
  apparmor: check that xindex is in trans_table bounds
  ...

126 files changed:
Documentation/devicetree/bindings/i2c/trivial-devices.txt
Documentation/devicetree/bindings/security/tpm/tpm_tis_spi.txt [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/ioctl/ioctl-number.txt
Documentation/tpm/tpm_vtpm_proxy.txt [new file with mode: 0644]
MAINTAINERS
arch/arm/kernel/ptrace.c
arch/arm64/kernel/ptrace.c
arch/mips/kernel/ptrace.c
arch/parisc/kernel/ptrace.c
arch/powerpc/kernel/ptrace.c
arch/s390/kernel/ptrace.c
arch/tile/kernel/ptrace.c
arch/um/kernel/skas/syscall.c
arch/x86/entry/common.c
arch/x86/entry/vsyscall/vsyscall_64.c
arch/x86/include/asm/ptrace.h
drivers/char/tpm/Kconfig
drivers/char/tpm/Makefile
drivers/char/tpm/st33zp24/Kconfig
drivers/char/tpm/st33zp24/i2c.c
drivers/char/tpm/st33zp24/spi.c
drivers/char/tpm/st33zp24/st33zp24.c
drivers/char/tpm/st33zp24/st33zp24.h
drivers/char/tpm/tpm-chip.c
drivers/char/tpm/tpm-dev.c
drivers/char/tpm/tpm-interface.c
drivers/char/tpm/tpm-sysfs.c
drivers/char/tpm/tpm.h
drivers/char/tpm/tpm2-cmd.c
drivers/char/tpm/tpm_atmel.c
drivers/char/tpm/tpm_atmel.h
drivers/char/tpm/tpm_crb.c
drivers/char/tpm/tpm_eventlog.c
drivers/char/tpm/tpm_eventlog.h
drivers/char/tpm/tpm_i2c_atmel.c
drivers/char/tpm/tpm_i2c_infineon.c
drivers/char/tpm/tpm_i2c_nuvoton.c
drivers/char/tpm/tpm_ibmvtpm.c
drivers/char/tpm/tpm_infineon.c
drivers/char/tpm/tpm_nsc.c
drivers/char/tpm/tpm_tis.c
drivers/char/tpm/tpm_tis_core.c [new file with mode: 0644]
drivers/char/tpm/tpm_tis_core.h [new file with mode: 0644]
drivers/char/tpm/tpm_tis_spi.c [new file with mode: 0644]
drivers/char/tpm/tpm_vtpm_proxy.c [new file with mode: 0644]
drivers/char/tpm/xen-tpmfront.c
include/keys/rxrpc-type.h
include/linux/capability.h
include/linux/platform_data/st33zp24.h
include/linux/seccomp.h
include/linux/tpm.h
include/net/calipso.h [new file with mode: 0644]
include/net/inet_sock.h
include/net/ipv6.h
include/net/netlabel.h
include/uapi/linux/Kbuild
include/uapi/linux/audit.h
include/uapi/linux/in6.h
include/uapi/linux/vtpm_proxy.h [new file with mode: 0644]
kernel/capability.c
kernel/seccomp.c
net/dccp/ipv6.c
net/ipv4/cipso_ipv4.c
net/ipv4/tcp_input.c
net/ipv6/Makefile
net/ipv6/af_inet6.c
net/ipv6/calipso.c [new file with mode: 0644]
net/ipv6/exthdrs.c
net/ipv6/exthdrs_core.c
net/ipv6/ipv6_sockglue.c
net/ipv6/sysctl_net_ipv6.c
net/ipv6/tcp_ipv6.c
net/iucv/af_iucv.c
net/netlabel/Kconfig
net/netlabel/Makefile
net/netlabel/netlabel_calipso.c [new file with mode: 0644]
net/netlabel/netlabel_calipso.h [new file with mode: 0644]
net/netlabel/netlabel_domainhash.c
net/netlabel/netlabel_domainhash.h
net/netlabel/netlabel_kapi.c
net/netlabel/netlabel_mgmt.c
net/netlabel/netlabel_mgmt.h
net/netlabel/netlabel_unlabeled.c
net/netlabel/netlabel_user.c
net/sysctl_net.c
samples/Kconfig
samples/seccomp/Makefile
scripts/sign-file.c
security/apparmor/Kconfig
security/apparmor/apparmorfs.c
security/apparmor/audit.c
security/apparmor/crypto.c
security/apparmor/domain.c
security/apparmor/file.c
security/apparmor/include/apparmor.h
security/apparmor/include/match.h
security/apparmor/include/policy.h
security/apparmor/lsm.c
security/apparmor/match.c
security/apparmor/path.c
security/apparmor/policy.c
security/apparmor/policy_unpack.c
security/apparmor/resource.c
security/integrity/iint.c
security/integrity/ima/ima.h
security/integrity/ima/ima_api.c
security/integrity/ima/ima_appraise.c
security/integrity/ima/ima_fs.c
security/integrity/ima/ima_init.c
security/integrity/ima/ima_main.c
security/integrity/ima/ima_policy.c
security/integrity/ima/ima_queue.c
security/integrity/integrity.h
security/keys/persistent.c
security/keys/request_key.c
security/security.c
security/selinux/hooks.c
security/selinux/include/netlabel.h
security/selinux/netlabel.c
security/selinux/selinuxfs.c
security/selinux/ss/ebitmap.c
security/selinux/ss/services.c
security/smack/smack_lsm.c
security/tomoyo/gc.c
tools/testing/selftests/seccomp/seccomp_bpf.c

index acc5cd64711c8f322db51d17797cbfe1fa471d91..a92d4f3e704482ad310f494f4525796bd8c4a8df 100644 (file)
@@ -126,6 +126,7 @@ national,lm80               Serial Interface ACPI-Compatible Microprocessor System Hardware M
 national,lm85          Temperature sensor with integrated fan control
 national,lm92          Â±0.33°C Accurate, 12-Bit + Sign Temperature Sensor and Thermal Window Comparator with Two-Wire Interface
 nuvoton,npct501                i2c trusted platform module (TPM)
+nuvoton,npct601                i2c trusted platform module (TPM2)
 nxp,pca9556            Octal SMBus and I2C registered interface
 nxp,pca9557            8-bit I2C-bus and SMBus I/O port with reset
 nxp,pcf8563            Real-time clock/calendar
diff --git a/Documentation/devicetree/bindings/security/tpm/tpm_tis_spi.txt b/Documentation/devicetree/bindings/security/tpm/tpm_tis_spi.txt
new file mode 100644 (file)
index 0000000..85741cd
--- /dev/null
@@ -0,0 +1,24 @@
+Required properties:
+- compatible: should be one of the following
+    "st,st33htpm-spi"
+    "infineon,slb9670"
+    "tcg,tpm_tis-spi"
+- spi-max-frequency: Maximum SPI frequency (depends on TPMs).
+
+Optional SoC Specific Properties:
+- pinctrl-names: Contains only one value - "default".
+- pintctrl-0: Specifies the pin control groups used for this controller.
+
+Example (for ARM-based BeagleBoard xM with TPM_TIS on SPI4):
+
+&mcspi4 {
+
+        status = "okay";
+
+        tpm_tis@0 {
+
+                compatible = "tcg,tpm_tis-spi";
+
+                spi-max-frequency = <10000000>;
+        };
+};
index d2bce2239769a31ff7c925aba568439c6a4135e4..b9361816fc3287073c3156501540c0a53c94178b 100644 (file)
@@ -128,6 +128,7 @@ idt Integrated Device Technologies, Inc.
 ifi    Ingenieurburo Fur Ic-Technologie (I/F/I)
 iom    Iomega Corporation
 img    Imagination Technologies Ltd.
+infineon Infineon Technologies
 inforce        Inforce Computing
 ingenic        Ingenic Semiconductor
 innolux        Innolux Corporation
@@ -255,6 +256,7 @@ syna        Synaptics Inc.
 synology       Synology, Inc.
 SUNW   Sun Microsystems, Inc
 tbs    TBS Technologies
+tcg    Trusted Computing Group
 tcl    Toby Churchill Ltd.
 technexion     TechNexion
 technologic    Technologic Systems
index 9369d3b0f09aebd1a930ae199b9721ac8ca516de..56af5e43e9c0280bece5b78e8f3b0577cf570aaf 100644 (file)
@@ -303,6 +303,7 @@ Code  Seq#(hex)     Include File            Comments
                                        <mailto:buk@buks.ipn.de>
 0xA0   all     linux/sdp/sdp.h         Industrial Device Project
                                        <mailto:kenji@bitgate.com>
+0xA1   0       linux/vtpm_proxy.h      TPM Emulator Proxy Driver
 0xA2   00-0F   arch/tile/include/asm/hardwall.h
 0xA3   80-8F   Port ACL                in development:
                                        <mailto:tlewis@mindspring.com>
diff --git a/Documentation/tpm/tpm_vtpm_proxy.txt b/Documentation/tpm/tpm_vtpm_proxy.txt
new file mode 100644 (file)
index 0000000..30d1902
--- /dev/null
@@ -0,0 +1,71 @@
+Virtual TPM Proxy Driver for Linux Containers
+
+Authors: Stefan Berger (IBM)
+
+This document describes the virtual Trusted Platform Module (vTPM)
+proxy device driver for Linux containers.
+
+INTRODUCTION
+------------
+
+The goal of this work is to provide TPM functionality to each Linux
+container. This allows programs to interact with a TPM in a container
+the same way they interact with a TPM on the physical system. Each
+container gets its own unique, emulated, software TPM.
+
+
+DESIGN
+------
+
+To make an emulated software TPM available to each container, the container
+management stack needs to create a device pair consisting of a client TPM
+character device /dev/tpmX (with X=0,1,2...) and a 'server side' file
+descriptor. The former is moved into the container by creating a character
+device with the appropriate major and minor numbers while the file descriptor
+is passed to the TPM emulator. Software inside the container can then send
+TPM commands using the character device and the emulator will receive the
+commands via the file descriptor and use it for sending back responses.
+
+To support this, the virtual TPM proxy driver provides a device /dev/vtpmx
+that is used to create device pairs using an ioctl. The ioctl takes as
+an input flags for configuring the device. The flags  for example indicate
+whether TPM 1.2 or TPM 2 functionality is supported by the TPM emulator.
+The result of the ioctl are the file descriptor for the 'server side'
+as well as the major and minor numbers of the character device that was created.
+Besides that the number of the TPM character device is return. If for
+example /dev/tpm10 was created, the number (dev_num) 10 is returned.
+
+The following is the data structure of the TPM_PROXY_IOC_NEW_DEV ioctl:
+
+struct vtpm_proxy_new_dev {
+       __u32 flags;         /* input */
+       __u32 tpm_num;       /* output */
+       __u32 fd;            /* output */
+       __u32 major;         /* output */
+       __u32 minor;         /* output */
+};
+
+Note that if unsupported flags are passed to the device driver, the ioctl will
+fail and errno will be set to EOPNOTSUPP. Similarly, if an unsupported ioctl is
+called on the device driver, the ioctl will fail and errno will be set to
+ENOTTY.
+
+See /usr/include/linux/vtpm_proxy.h for definitions related to the public interface
+of this vTPM device driver.
+
+Once the device has been created, the driver will immediately try to talk
+to the TPM. All commands from the driver can be read from the file descriptor
+returned by the ioctl. The commands should be responded to immediately.
+
+Depending on the version of TPM the following commands will be sent by the
+driver:
+
+- TPM 1.2:
+  - the driver will send a TPM_Startup command to the TPM emulator
+  - the driver will send commands to read the command durations and
+    interface timeouts from the TPM emulator
+- TPM 2:
+  - the driver will send a TPM2_Startup command to the TPM emulator
+
+The TPM device /dev/tpmX will only appear if all of the relevant commands
+were responded to properly.
index febb29c4d0ca5d6cb48c6ae278d45963656d4538..256f56bbb2ad26cd345be9100a3fa9b0878ecf46 100644 (file)
@@ -2837,7 +2837,7 @@ F:        include/uapi/linux/can/error.h
 F:     include/uapi/linux/can/netlink.h
 
 CAPABILITIES
-M:     Serge Hallyn <serge.hallyn@canonical.com>
+M:     Serge Hallyn <serge@hallyn.com>
 L:     linux-security-module@vger.kernel.org
 S:     Supported
 F:     include/linux/capability.h
@@ -10675,7 +10675,7 @@ SMACK SECURITY MODULE
 M:     Casey Schaufler <casey@schaufler-ca.com>
 L:     linux-security-module@vger.kernel.org
 W:     http://schaufler-ca.com
-T:     git git://git.gitorious.org/smack-next/kernel.git
+T:     git git://github.com/cschaufler/smack-next
 S:     Maintained
 F:     Documentation/security/Smack.txt
 F:     security/smack/
index 4d9375814b538e096b57f76d947916cb3ce92b7a..ce131ed5939d5ff86054afc3a14de7cb51225a79 100644 (file)
@@ -932,18 +932,19 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno)
 {
        current_thread_info()->syscall = scno;
 
-       /* Do the secure computing check first; failures should be fast. */
+       if (test_thread_flag(TIF_SYSCALL_TRACE))
+               tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER);
+
+       /* Do seccomp after ptrace; syscall may have changed. */
 #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
-       if (secure_computing() == -1)
+       if (secure_computing(NULL) == -1)
                return -1;
 #else
        /* XXX: remove this once OABI gets fixed */
-       secure_computing_strict(scno);
+       secure_computing_strict(current_thread_info()->syscall);
 #endif
 
-       if (test_thread_flag(TIF_SYSCALL_TRACE))
-               tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER);
-
+       /* Tracer or seccomp may have changed syscall. */
        scno = current_thread_info()->syscall;
 
        if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
index 030c1d5aa46ddadc3c25af32a1052eb304f0287a..e0c81da60f76270ad122fee3595464852e7d9ee2 100644 (file)
@@ -1347,13 +1347,13 @@ static void tracehook_report_syscall(struct pt_regs *regs,
 
 asmlinkage int syscall_trace_enter(struct pt_regs *regs)
 {
-       /* Do the secure computing check first; failures should be fast. */
-       if (secure_computing() == -1)
-               return -1;
-
        if (test_thread_flag(TIF_SYSCALL_TRACE))
                tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER);
 
+       /* Do the secure computing after ptrace; failures should be fast. */
+       if (secure_computing(NULL) == -1)
+               return -1;
+
        if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
                trace_sys_enter(regs, regs->syscallno);
 
index 0dcf69194473c6469cbd8ce6cdcdb06499f3b483..6103b24d1bfcb781ae436ceb2a7c94ec7801df34 100644 (file)
@@ -888,17 +888,16 @@ long arch_ptrace(struct task_struct *child, long request,
  */
 asmlinkage long syscall_trace_enter(struct pt_regs *regs, long syscall)
 {
-       long ret = 0;
        user_exit();
 
        current_thread_info()->syscall = syscall;
 
-       if (secure_computing() == -1)
-               return -1;
-
        if (test_thread_flag(TIF_SYSCALL_TRACE) &&
            tracehook_report_syscall_entry(regs))
-               ret = -1;
+               return -1;
+
+       if (secure_computing(NULL) == -1)
+               return -1;
 
        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
                trace_sys_enter(regs, regs->regs[2]);
index b5458b37fc5be2463f77a86cdddf35e4c88ab3ef..e02d7b4d2b693dd94af83e2f6a215452d8d7e04d 100644 (file)
@@ -311,10 +311,6 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 
 long do_syscall_trace_enter(struct pt_regs *regs)
 {
-       /* Do the secure computing check first. */
-       if (secure_computing() == -1)
-               return -1;
-
        if (test_thread_flag(TIF_SYSCALL_TRACE) &&
            tracehook_report_syscall_entry(regs)) {
                /*
@@ -325,6 +321,11 @@ long do_syscall_trace_enter(struct pt_regs *regs)
                regs->gr[20] = -1UL;
                goto out;
        }
+
+       /* Do the secure computing check after ptrace. */
+       if (secure_computing(NULL) == -1)
+               return -1;
+
 #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
                trace_sys_enter(regs, regs->gr[20]);
index 060b140f03c69de424e40e4dcdb5064d832ce889..134bee9ac664db9b36b3c0961bd36a1f2d72b720 100644 (file)
@@ -1783,12 +1783,12 @@ static int do_seccomp(struct pt_regs *regs)
         * have already loaded -ENOSYS into r3, or seccomp has put
         * something else in r3 (via SECCOMP_RET_ERRNO/TRACE).
         */
-       if (__secure_computing())
+       if (__secure_computing(NULL))
                return -1;
 
        /*
         * The syscall was allowed by seccomp, restore the register
-        * state to what ptrace and audit expect.
+        * state to what audit expects.
         * Note that we use orig_gpr3, which means a seccomp tracer can
         * modify the first syscall parameter (in orig_gpr3) and also
         * allow the syscall to proceed.
@@ -1822,22 +1822,25 @@ static inline int do_seccomp(struct pt_regs *regs) { return 0; }
  */
 long do_syscall_trace_enter(struct pt_regs *regs)
 {
-       bool abort = false;
-
        user_exit();
 
+       /*
+        * The tracer may decide to abort the syscall, if so tracehook
+        * will return !0. Note that the tracer may also just change
+        * regs->gpr[0] to an invalid syscall number, that is handled
+        * below on the exit path.
+        */
+       if (test_thread_flag(TIF_SYSCALL_TRACE) &&
+           tracehook_report_syscall_entry(regs))
+               goto skip;
+
+       /* Run seccomp after ptrace; allow it to set gpr[3]. */
        if (do_seccomp(regs))
                return -1;
 
-       if (test_thread_flag(TIF_SYSCALL_TRACE)) {
-               /*
-                * The tracer may decide to abort the syscall, if so tracehook
-                * will return !0. Note that the tracer may also just change
-                * regs->gpr[0] to an invalid syscall number, that is handled
-                * below on the exit path.
-                */
-               abort = tracehook_report_syscall_entry(regs) != 0;
-       }
+       /* Avoid trace and audit when syscall is invalid. */
+       if (regs->gpr[0] >= NR_syscalls)
+               goto skip;
 
        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
                trace_sys_enter(regs, regs->gpr[0]);
@@ -1854,17 +1857,16 @@ long do_syscall_trace_enter(struct pt_regs *regs)
                                    regs->gpr[5] & 0xffffffff,
                                    regs->gpr[6] & 0xffffffff);
 
-       if (abort || regs->gpr[0] >= NR_syscalls) {
-               /*
-                * If we are aborting explicitly, or if the syscall number is
-                * now invalid, set the return value to -ENOSYS.
-                */
-               regs->gpr[3] = -ENOSYS;
-               return -1;
-       }
-
        /* Return the possibly modified but valid syscall number */
        return regs->gpr[0];
+
+skip:
+       /*
+        * If we are aborting explicitly, or if the syscall number is
+        * now invalid, set the return value to -ENOSYS.
+        */
+       regs->gpr[3] = -ENOSYS;
+       return -1;
 }
 
 void do_syscall_trace_leave(struct pt_regs *regs)
index 49b1c13bf6c96372d60815e8549c10b15b40914f..cea17010448feaff4b4443e1a2fa7a6584e66ea5 100644 (file)
@@ -821,15 +821,6 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 
 asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
 {
-       long ret = 0;
-
-       /* Do the secure computing check first. */
-       if (secure_computing()) {
-               /* seccomp failures shouldn't expose any additional code. */
-               ret = -1;
-               goto out;
-       }
-
        /*
         * The sysc_tracesys code in entry.S stored the system
         * call number to gprs[2].
@@ -843,7 +834,13 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
                 * the system call and the system call restart handling.
                 */
                clear_pt_regs_flag(regs, PIF_SYSCALL);
-               ret = -1;
+               return -1;
+       }
+
+       /* Do the secure computing check after ptrace. */
+       if (secure_computing(NULL)) {
+               /* seccomp failures shouldn't expose any additional code. */
+               return -1;
        }
 
        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
@@ -852,8 +849,8 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
        audit_syscall_entry(regs->gprs[2], regs->orig_gpr2,
                            regs->gprs[3], regs->gprs[4],
                            regs->gprs[5]);
-out:
-       return ret ?: regs->gprs[2];
+
+       return regs->gprs[2];
 }
 
 asmlinkage void do_syscall_trace_exit(struct pt_regs *regs)
index 54e7b723db99252ac2df797cc6f4672e6c45a77f..d89b7011667cb4f1a6f3ad55238d2c815e229c41 100644 (file)
@@ -255,14 +255,15 @@ int do_syscall_trace_enter(struct pt_regs *regs)
 {
        u32 work = ACCESS_ONCE(current_thread_info()->flags);
 
-       if (secure_computing() == -1)
+       if ((work & _TIF_SYSCALL_TRACE) &&
+           tracehook_report_syscall_entry(regs)) {
+               regs->regs[TREG_SYSCALL_NR] = -1;
                return -1;
-
-       if (work & _TIF_SYSCALL_TRACE) {
-               if (tracehook_report_syscall_entry(regs))
-                       regs->regs[TREG_SYSCALL_NR] = -1;
        }
 
+       if (secure_computing(NULL) == -1)
+               return -1;
+
        if (work & _TIF_SYSCALL_TRACEPOINT)
                trace_sys_enter(regs, regs->regs[TREG_SYSCALL_NR]);
 
index 48b0dcbd87bef3913cfe8330b0ee6965a8c6ce6d..ef4b8f949b516df373b2d016e4a1366b53e1bc75 100644 (file)
@@ -20,12 +20,12 @@ void handle_syscall(struct uml_pt_regs *r)
        UPT_SYSCALL_NR(r) = PT_SYSCALL_NR(r->gp);
        PT_REGS_SET_SYSCALL_RETURN(regs, -ENOSYS);
 
-       /* Do the secure computing check first; failures should be fast. */
-       if (secure_computing() == -1)
+       if (syscall_trace_enter(regs))
                return;
 
-       if (syscall_trace_enter(regs))
-               goto out;
+       /* Do the seccomp check after ptrace; failures should be fast. */
+       if (secure_computing(NULL) == -1)
+               return;
 
        /* Update the syscall number after orig_ax has potentially been updated
         * with ptrace.
@@ -37,6 +37,5 @@ void handle_syscall(struct uml_pt_regs *r)
                PT_REGS_SET_SYSCALL_RETURN(regs,
                                EXECUTE_SYSCALL(syscall, regs));
 
-out:
        syscall_trace_leave(regs);
 }
index 9e1e27d31c6d6d9425f5fe4b0d916c26bd5d0abd..a1e71d431fed85f40f939e5765b8fe4bb4a7a411 100644 (file)
@@ -64,22 +64,16 @@ static void do_audit_syscall_entry(struct pt_regs *regs, u32 arch)
 }
 
 /*
- * We can return 0 to resume the syscall or anything else to go to phase
- * 2.  If we resume the syscall, we need to put something appropriate in
- * regs->orig_ax.
- *
- * NB: We don't have full pt_regs here, but regs->orig_ax and regs->ax
- * are fully functional.
- *
- * For phase 2's benefit, our return value is:
- * 0:                  resume the syscall
- * 1:                  go to phase 2; no seccomp phase 2 needed
- * anything else:      go to phase 2; pass return value to seccomp
+ * Returns the syscall nr to run (which should match regs->orig_ax) or -1
+ * to skip the syscall.
  */
-unsigned long syscall_trace_enter_phase1(struct pt_regs *regs, u32 arch)
+static long syscall_trace_enter(struct pt_regs *regs)
 {
+       u32 arch = in_ia32_syscall() ? AUDIT_ARCH_I386 : AUDIT_ARCH_X86_64;
+
        struct thread_info *ti = pt_regs_to_thread_info(regs);
        unsigned long ret = 0;
+       bool emulated = false;
        u32 work;
 
        if (IS_ENABLED(CONFIG_DEBUG_ENTRY))
@@ -87,11 +81,19 @@ unsigned long syscall_trace_enter_phase1(struct pt_regs *regs, u32 arch)
 
        work = ACCESS_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY;
 
+       if (unlikely(work & _TIF_SYSCALL_EMU))
+               emulated = true;
+
+       if ((emulated || (work & _TIF_SYSCALL_TRACE)) &&
+           tracehook_report_syscall_entry(regs))
+               return -1L;
+
+       if (emulated)
+               return -1L;
+
 #ifdef CONFIG_SECCOMP
        /*
-        * Do seccomp first -- it should minimize exposure of other
-        * code, and keeping seccomp fast is probably more valuable
-        * than the rest of this.
+        * Do seccomp after ptrace, to catch any tracer changes.
         */
        if (work & _TIF_SECCOMP) {
                struct seccomp_data sd;
@@ -118,69 +120,12 @@ unsigned long syscall_trace_enter_phase1(struct pt_regs *regs, u32 arch)
                        sd.args[5] = regs->bp;
                }
 
-               BUILD_BUG_ON(SECCOMP_PHASE1_OK != 0);
-               BUILD_BUG_ON(SECCOMP_PHASE1_SKIP != 1);
-
-               ret = seccomp_phase1(&sd);
-               if (ret == SECCOMP_PHASE1_SKIP) {
-                       regs->orig_ax = -1;
-                       ret = 0;
-               } else if (ret != SECCOMP_PHASE1_OK) {
-                       return ret;  /* Go directly to phase 2 */
-               }
-
-               work &= ~_TIF_SECCOMP;
-       }
-#endif
-
-       /* Do our best to finish without phase 2. */
-       if (work == 0)
-               return ret;  /* seccomp and/or nohz only (ret == 0 here) */
-
-#ifdef CONFIG_AUDITSYSCALL
-       if (work == _TIF_SYSCALL_AUDIT) {
-               /*
-                * If there is no more work to be done except auditing,
-                * then audit in phase 1.  Phase 2 always audits, so, if
-                * we audit here, then we can't go on to phase 2.
-                */
-               do_audit_syscall_entry(regs, arch);
-               return 0;
+               ret = __secure_computing(&sd);
+               if (ret == -1)
+                       return ret;
        }
 #endif
 
-       return 1;  /* Something is enabled that we can't handle in phase 1 */
-}
-
-/* Returns the syscall nr to run (which should match regs->orig_ax). */
-long syscall_trace_enter_phase2(struct pt_regs *regs, u32 arch,
-                               unsigned long phase1_result)
-{
-       struct thread_info *ti = pt_regs_to_thread_info(regs);
-       long ret = 0;
-       u32 work = ACCESS_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY;
-
-       if (IS_ENABLED(CONFIG_DEBUG_ENTRY))
-               BUG_ON(regs != task_pt_regs(current));
-
-#ifdef CONFIG_SECCOMP
-       /*
-        * Call seccomp_phase2 before running the other hooks so that
-        * they can see any changes made by a seccomp tracer.
-        */
-       if (phase1_result > 1 && seccomp_phase2(phase1_result)) {
-               /* seccomp failures shouldn't expose any additional code. */
-               return -1;
-       }
-#endif
-
-       if (unlikely(work & _TIF_SYSCALL_EMU))
-               ret = -1L;
-
-       if ((ret || test_thread_flag(TIF_SYSCALL_TRACE)) &&
-           tracehook_report_syscall_entry(regs))
-               ret = -1L;
-
        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
                trace_sys_enter(regs, regs->orig_ax);
 
@@ -189,17 +134,6 @@ long syscall_trace_enter_phase2(struct pt_regs *regs, u32 arch,
        return ret ?: regs->orig_ax;
 }
 
-long syscall_trace_enter(struct pt_regs *regs)
-{
-       u32 arch = in_ia32_syscall() ? AUDIT_ARCH_I386 : AUDIT_ARCH_X86_64;
-       unsigned long phase1_result = syscall_trace_enter_phase1(regs, arch);
-
-       if (phase1_result == 0)
-               return regs->orig_ax;
-       else
-               return syscall_trace_enter_phase2(regs, arch, phase1_result);
-}
-
 #define EXIT_TO_USERMODE_LOOP_FLAGS                            \
        (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_UPROBE |   \
         _TIF_NEED_RESCHED | _TIF_USER_RETURN_NOTIFY)
index 75fc719b7f31a57d27e120a9a23cd2dbe8340beb..636c4b341f36a93975c1ab732f8dfc5fb66bf4ba 100644 (file)
@@ -207,7 +207,7 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
         */
        regs->orig_ax = syscall_nr;
        regs->ax = -ENOSYS;
-       tmp = secure_computing();
+       tmp = secure_computing(NULL);
        if ((!tmp && regs->orig_ax != syscall_nr) || regs->ip != address) {
                warn_bad_vsyscall(KERN_DEBUG, regs,
                                  "seccomp tried to change syscall nr or ip");
index 6271281f947d82d5a463c849e17b0e60c8faf228..2b5d686ea9f37b3bd503d44e9481fd17682a2e6e 100644 (file)
@@ -83,12 +83,6 @@ extern void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs,
                         int error_code, int si_code);
 
 
-extern unsigned long syscall_trace_enter_phase1(struct pt_regs *, u32 arch);
-extern long syscall_trace_enter_phase2(struct pt_regs *, u32 arch,
-                                      unsigned long phase1_result);
-
-extern long syscall_trace_enter(struct pt_regs *);
-
 static inline unsigned long regs_return_value(struct pt_regs *regs)
 {
        return regs->ax;
index 3b84a8b1bfbe8f4bbd1e5a5cb1b83bd9179d333e..9faa0b1e77665a666fb1f1abcc398e6f5d61d571 100644 (file)
@@ -24,9 +24,16 @@ menuconfig TCG_TPM
 
 if TCG_TPM
 
+config TCG_TIS_CORE
+       tristate
+       ---help---
+       TCG TIS TPM core driver. It implements the TPM TCG TIS logic and hooks
+       into the TPM kernel APIs. Physical layers will register against it.
+
 config TCG_TIS
        tristate "TPM Interface Specification 1.2 Interface / TPM 2.0 FIFO Interface"
        depends on X86
+       select TCG_TIS_CORE
        ---help---
          If you have a TPM security chip that is compliant with the
          TCG TIS 1.2 TPM specification (TPM1.2) or the TCG PTP FIFO
@@ -34,6 +41,18 @@ config TCG_TIS
          within Linux. To compile this driver as a module, choose  M here;
          the module will be called tpm_tis.
 
+config TCG_TIS_SPI
+       tristate "TPM Interface Specification 1.3 Interface / TPM 2.0 FIFO Interface - (SPI)"
+       depends on SPI
+       select TCG_TIS_CORE
+       ---help---
+         If you have a TPM security chip which is connected to a regular,
+         non-tcg SPI master (i.e. most embedded platforms) that is compliant with the
+         TCG TIS 1.3 TPM specification (TPM1.2) or the TCG PTP FIFO
+         specification (TPM2.0) say Yes and it will be accessible from
+         within Linux. To compile this driver as a module, choose  M here;
+         the module will be called tpm_tis_spi.
+
 config TCG_TIS_I2C_ATMEL
        tristate "TPM Interface Specification 1.2 Interface (I2C - Atmel)"
        depends on I2C
@@ -122,5 +141,16 @@ config TCG_CRB
          from within Linux.  To compile this driver as a module, choose
          M here; the module will be called tpm_crb.
 
+config TCG_VTPM_PROXY
+       tristate "VTPM Proxy Interface"
+       depends on TCG_TPM
+       select ANON_INODES
+       ---help---
+         This driver proxies for an emulated TPM (vTPM) running in userspace.
+         A device /dev/vtpmx is provided that creates a device pair
+         /dev/vtpmX and a server-side file descriptor on which the vTPM
+         can receive commands.
+
+
 source "drivers/char/tpm/st33zp24/Kconfig"
 endif # TCG_TPM
index 56e8f1f3dc7e674febf256643fa0adfcffeb00aa..a385fb8c17de574322cecaf304f66c27a9163de6 100644 (file)
@@ -12,7 +12,9 @@ ifdef CONFIG_TCG_IBMVTPM
        tpm-y += tpm_eventlog.o tpm_of.o
 endif
 endif
+obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o
 obj-$(CONFIG_TCG_TIS) += tpm_tis.o
+obj-$(CONFIG_TCG_TIS_SPI) += tpm_tis_spi.o
 obj-$(CONFIG_TCG_TIS_I2C_ATMEL) += tpm_i2c_atmel.o
 obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
 obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o
@@ -23,3 +25,4 @@ obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
 obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/
 obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o
 obj-$(CONFIG_TCG_CRB) += tpm_crb.o
+obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o
index 19c007461d1cbd7acae8b6fee4d8d51fbcb7f5de..e74c6f29fc85210f07d1168d19bc04d357e0dd42 100644 (file)
@@ -1,6 +1,5 @@
 config TCG_TIS_ST33ZP24
-       tristate "STMicroelectronics TPM Interface Specification 1.2 Interface"
-       depends on GPIOLIB || COMPILE_TEST
+       tristate
        ---help---
          STMicroelectronics ST33ZP24 core driver. It implements the core
          TPM1.2 logic and hooks into the TPM kernel APIs. Physical layers will
@@ -10,9 +9,9 @@ config TCG_TIS_ST33ZP24
          tpm_st33zp24.
 
 config TCG_TIS_ST33ZP24_I2C
-       tristate "TPM 1.2 ST33ZP24 I2C support"
-       depends on TCG_TIS_ST33ZP24
+       tristate "STMicroelectronics TPM Interface Specification 1.2 Interface (I2C)"
        depends on I2C
+       select TCG_TIS_ST33ZP24
        ---help---
          This module adds support for the STMicroelectronics TPM security chip
          ST33ZP24 with i2c interface.
@@ -20,9 +19,9 @@ config TCG_TIS_ST33ZP24_I2C
          called tpm_st33zp24_i2c.
 
 config TCG_TIS_ST33ZP24_SPI
-       tristate "TPM 1.2 ST33ZP24 SPI support"
-       depends on TCG_TIS_ST33ZP24
+       tristate "STMicroelectronics TPM Interface Specification 1.2 Interface (SPI)"
        depends on SPI
+       select TCG_TIS_ST33ZP24
        ---help---
          This module adds support for the STMicroelectronics TPM security chip
          ST33ZP24 with spi interface.
index 309d2767c6a109c27cd73a833f9670a7360cd521..028a9cd76b63c0fada5b7bfb8a9e453e3a5e2458 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24
- * Copyright (C) 2009 - 2015 STMicroelectronics
+ * Copyright (C) 2009 - 2016 STMicroelectronics
  *
  * 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
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/of_irq.h>
 #include <linux/of_gpio.h>
+#include <linux/acpi.h>
 #include <linux/tpm.h>
 #include <linux/platform_data/st33zp24.h>
 
+#include "../tpm.h"
 #include "st33zp24.h"
 
 #define TPM_DUMMY_BYTE                 0xAA
@@ -108,11 +111,40 @@ static const struct st33zp24_phy_ops i2c_phy_ops = {
        .recv = st33zp24_i2c_recv,
 };
 
-#ifdef CONFIG_OF
-static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy)
+static int st33zp24_i2c_acpi_request_resources(struct i2c_client *client)
 {
+       struct tpm_chip *chip = i2c_get_clientdata(client);
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
+       struct st33zp24_i2c_phy *phy = tpm_dev->phy_id;
+       struct gpio_desc *gpiod_lpcpd;
+       struct device *dev = &client->dev;
+
+       /* Get LPCPD GPIO from ACPI */
+       gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1,
+                                          GPIOD_OUT_HIGH);
+       if (IS_ERR(gpiod_lpcpd)) {
+               dev_err(&client->dev,
+                       "Failed to retrieve lpcpd-gpios from acpi.\n");
+               phy->io_lpcpd = -1;
+               /*
+                * lpcpd pin is not specified. This is not an issue as
+                * power management can be also managed by TPM specific
+                * commands. So leave with a success status code.
+                */
+               return 0;
+       }
+
+       phy->io_lpcpd = desc_to_gpio(gpiod_lpcpd);
+
+       return 0;
+}
+
+static int st33zp24_i2c_of_request_resources(struct i2c_client *client)
+{
+       struct tpm_chip *chip = i2c_get_clientdata(client);
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
+       struct st33zp24_i2c_phy *phy = tpm_dev->phy_id;
        struct device_node *pp;
-       struct i2c_client *client = phy->client;
        int gpio;
        int ret;
 
@@ -146,16 +178,12 @@ static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy)
 
        return 0;
 }
-#else
-static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy)
-{
-       return -ENODEV;
-}
-#endif
 
-static int st33zp24_i2c_request_resources(struct i2c_client *client,
-                                         struct st33zp24_i2c_phy *phy)
+static int st33zp24_i2c_request_resources(struct i2c_client *client)
 {
+       struct tpm_chip *chip = i2c_get_clientdata(client);
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
+       struct st33zp24_i2c_phy *phy = tpm_dev->phy_id;
        struct st33zp24_platform_data *pdata;
        int ret;
 
@@ -212,13 +240,18 @@ static int st33zp24_i2c_probe(struct i2c_client *client,
                return -ENOMEM;
 
        phy->client = client;
+
        pdata = client->dev.platform_data;
        if (!pdata && client->dev.of_node) {
-               ret = st33zp24_i2c_of_request_resources(phy);
+               ret = st33zp24_i2c_of_request_resources(client);
                if (ret)
                        return ret;
        } else if (pdata) {
-               ret = st33zp24_i2c_request_resources(client, phy);
+               ret = st33zp24_i2c_request_resources(client);
+               if (ret)
+                       return ret;
+       } else if (ACPI_HANDLE(&client->dev)) {
+               ret = st33zp24_i2c_acpi_request_resources(client);
                if (ret)
                        return ret;
        }
@@ -245,13 +278,17 @@ static const struct i2c_device_id st33zp24_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, st33zp24_i2c_id);
 
-#ifdef CONFIG_OF
 static const struct of_device_id of_st33zp24_i2c_match[] = {
        { .compatible = "st,st33zp24-i2c", },
        {}
 };
 MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match);
-#endif
+
+static const struct acpi_device_id st33zp24_i2c_acpi_match[] = {
+       {"SMO3324"},
+       {}
+};
+MODULE_DEVICE_TABLE(acpi, st33zp24_i2c_acpi_match);
 
 static SIMPLE_DEV_PM_OPS(st33zp24_i2c_ops, st33zp24_pm_suspend,
                         st33zp24_pm_resume);
@@ -261,6 +298,7 @@ static struct i2c_driver st33zp24_i2c_driver = {
                .name = TPM_ST33_I2C,
                .pm = &st33zp24_i2c_ops,
                .of_match_table = of_match_ptr(of_st33zp24_i2c_match),
+               .acpi_match_table = ACPI_PTR(st33zp24_i2c_acpi_match),
        },
        .probe = st33zp24_i2c_probe,
        .remove = st33zp24_i2c_remove,
index f974c945c97ad79f5860a350249f0086dec1ff97..9f5a0117098c0c0fe08f7a69149f0c26ef7f190d 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * STMicroelectronics TPM SPI Linux driver for TPM ST33ZP24
- * Copyright (C) 2009 - 201 STMicroelectronics
+ * Copyright (C) 2009 - 2016 STMicroelectronics
  *
  * 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
 #include <linux/module.h>
 #include <linux/spi/spi.h>
 #include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/of_irq.h>
 #include <linux/of_gpio.h>
+#include <linux/acpi.h>
 #include <linux/tpm.h>
 #include <linux/platform_data/st33zp24.h>
 
+#include "../tpm.h"
 #include "st33zp24.h"
 
 #define TPM_DATA_FIFO           0x24
@@ -66,7 +69,7 @@
 
 struct st33zp24_spi_phy {
        struct spi_device *spi_device;
-       struct spi_transfer spi_xfer;
+
        u8 tx_buf[ST33ZP24_SPI_BUFFER_SIZE];
        u8 rx_buf[ST33ZP24_SPI_BUFFER_SIZE];
 
@@ -110,43 +113,39 @@ static int st33zp24_status_to_errno(u8 code)
 static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data,
                             int tpm_size)
 {
-       u8 data = 0;
-       int total_length = 0, nbr_dummy_bytes = 0, ret = 0;
+       int total_length = 0, ret = 0;
        struct st33zp24_spi_phy *phy = phy_id;
        struct spi_device *dev = phy->spi_device;
-       u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf;
-       u8 *rx_buf = phy->spi_xfer.rx_buf;
+       struct spi_transfer spi_xfer = {
+               .tx_buf = phy->tx_buf,
+               .rx_buf = phy->rx_buf,
+       };
 
        /* Pre-Header */
-       data = TPM_WRITE_DIRECTION | LOCALITY0;
-       memcpy(tx_buf + total_length, &data, sizeof(data));
-       total_length++;
-       data = tpm_register;
-       memcpy(tx_buf + total_length, &data, sizeof(data));
-       total_length++;
+       phy->tx_buf[total_length++] = TPM_WRITE_DIRECTION | LOCALITY0;
+       phy->tx_buf[total_length++] = tpm_register;
 
        if (tpm_size > 0 && tpm_register == TPM_DATA_FIFO) {
-               tx_buf[total_length++] = tpm_size >> 8;
-               tx_buf[total_length++] = tpm_size;
+               phy->tx_buf[total_length++] = tpm_size >> 8;
+               phy->tx_buf[total_length++] = tpm_size;
        }
 
-       memcpy(&tx_buf[total_length], tpm_data, tpm_size);
+       memcpy(&phy->tx_buf[total_length], tpm_data, tpm_size);
        total_length += tpm_size;
 
-       nbr_dummy_bytes = phy->latency;
-       memset(&tx_buf[total_length], TPM_DUMMY_BYTE, nbr_dummy_bytes);
+       memset(&phy->tx_buf[total_length], TPM_DUMMY_BYTE, phy->latency);
 
-       phy->spi_xfer.len = total_length + nbr_dummy_bytes;
+       spi_xfer.len = total_length + phy->latency;
 
-       ret = spi_sync_transfer(dev, &phy->spi_xfer, 1);
+       ret = spi_sync_transfer(dev, &spi_xfer, 1);
        if (ret == 0)
-               ret = rx_buf[total_length + nbr_dummy_bytes - 1];
+               ret = phy->rx_buf[total_length + phy->latency - 1];
 
        return st33zp24_status_to_errno(ret);
 } /* st33zp24_spi_send() */
 
 /*
- * read8_recv
+ * st33zp24_spi_read8_recv
  * Recv byte from the TIS register according to the ST33ZP24 SPI protocol.
  * @param: phy_id, the phy description
  * @param: tpm_register, the tpm tis register where the data should be read
@@ -154,40 +153,37 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data,
  * @param: tpm_size, tpm TPM response size to read.
  * @return: should be zero if success else a negative error code.
  */
-static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size)
+static int st33zp24_spi_read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data,
+                                 int tpm_size)
 {
-       u8 data = 0;
-       int total_length = 0, nbr_dummy_bytes, ret;
+       int total_length = 0, ret;
        struct st33zp24_spi_phy *phy = phy_id;
        struct spi_device *dev = phy->spi_device;
-       u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf;
-       u8 *rx_buf = phy->spi_xfer.rx_buf;
+       struct spi_transfer spi_xfer = {
+               .tx_buf = phy->tx_buf,
+               .rx_buf = phy->rx_buf,
+       };
 
        /* Pre-Header */
-       data = LOCALITY0;
-       memcpy(tx_buf + total_length, &data, sizeof(data));
-       total_length++;
-       data = tpm_register;
-       memcpy(tx_buf + total_length, &data, sizeof(data));
-       total_length++;
+       phy->tx_buf[total_length++] = LOCALITY0;
+       phy->tx_buf[total_length++] = tpm_register;
 
-       nbr_dummy_bytes = phy->latency;
-       memset(&tx_buf[total_length], TPM_DUMMY_BYTE,
-              nbr_dummy_bytes + tpm_size);
+       memset(&phy->tx_buf[total_length], TPM_DUMMY_BYTE,
+              phy->latency + tpm_size);
 
-       phy->spi_xfer.len = total_length + nbr_dummy_bytes + tpm_size;
+       spi_xfer.len = total_length + phy->latency + tpm_size;
 
        /* header + status byte + size of the data + status byte */
-       ret = spi_sync_transfer(dev, &phy->spi_xfer, 1);
+       ret = spi_sync_transfer(dev, &spi_xfer, 1);
        if (tpm_size > 0 && ret == 0) {
-               ret = rx_buf[total_length + nbr_dummy_bytes - 1];
+               ret = phy->rx_buf[total_length + phy->latency - 1];
 
-               memcpy(tpm_data, rx_buf + total_length + nbr_dummy_bytes,
+               memcpy(tpm_data, phy->rx_buf + total_length + phy->latency,
                       tpm_size);
        }
 
        return ret;
-} /* read8_reg() */
+} /* st33zp24_spi_read8_reg() */
 
 /*
  * st33zp24_spi_recv
@@ -203,13 +199,13 @@ static int st33zp24_spi_recv(void *phy_id, u8 tpm_register, u8 *tpm_data,
 {
        int ret;
 
-       ret = read8_reg(phy_id, tpm_register, tpm_data, tpm_size);
+       ret = st33zp24_spi_read8_reg(phy_id, tpm_register, tpm_data, tpm_size);
        if (!st33zp24_status_to_errno(ret))
                return tpm_size;
        return ret;
 } /* st33zp24_spi_recv() */
 
-static int evaluate_latency(void *phy_id)
+static int st33zp24_spi_evaluate_latency(void *phy_id)
 {
        struct st33zp24_spi_phy *phy = phy_id;
        int latency = 1, status = 0;
@@ -217,9 +213,15 @@ static int evaluate_latency(void *phy_id)
 
        while (!status && latency < MAX_SPI_LATENCY) {
                phy->latency = latency;
-               status = read8_reg(phy_id, TPM_INTF_CAPABILITY, &data, 1);
+               status = st33zp24_spi_read8_reg(phy_id, TPM_INTF_CAPABILITY,
+                                               &data, 1);
                latency++;
        }
+       if (status < 0)
+               return status;
+       if (latency == MAX_SPI_LATENCY)
+               return -ENODEV;
+
        return latency - 1;
 } /* evaluate_latency() */
 
@@ -228,24 +230,52 @@ static const struct st33zp24_phy_ops spi_phy_ops = {
        .recv = st33zp24_spi_recv,
 };
 
-#ifdef CONFIG_OF
-static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy)
+static int st33zp24_spi_acpi_request_resources(struct spi_device *spi_dev)
 {
+       struct tpm_chip *chip = spi_get_drvdata(spi_dev);
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
+       struct st33zp24_spi_phy *phy = tpm_dev->phy_id;
+       struct gpio_desc *gpiod_lpcpd;
+       struct device *dev = &spi_dev->dev;
+
+       /* Get LPCPD GPIO from ACPI */
+       gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1,
+                                          GPIOD_OUT_HIGH);
+       if (IS_ERR(gpiod_lpcpd)) {
+               dev_err(dev, "Failed to retrieve lpcpd-gpios from acpi.\n");
+               phy->io_lpcpd = -1;
+               /*
+                * lpcpd pin is not specified. This is not an issue as
+                * power management can be also managed by TPM specific
+                * commands. So leave with a success status code.
+                */
+               return 0;
+       }
+
+       phy->io_lpcpd = desc_to_gpio(gpiod_lpcpd);
+
+       return 0;
+}
+
+static int st33zp24_spi_of_request_resources(struct spi_device *spi_dev)
+{
+       struct tpm_chip *chip = spi_get_drvdata(spi_dev);
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
+       struct st33zp24_spi_phy *phy = tpm_dev->phy_id;
        struct device_node *pp;
-       struct spi_device *dev = phy->spi_device;
        int gpio;
        int ret;
 
-       pp = dev->dev.of_node;
+       pp = spi_dev->dev.of_node;
        if (!pp) {
-               dev_err(&dev->dev, "No platform data\n");
+               dev_err(&spi_dev->dev, "No platform data\n");
                return -ENODEV;
        }
 
        /* Get GPIO from device tree */
        gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0);
        if (gpio < 0) {
-               dev_err(&dev->dev,
+               dev_err(&spi_dev->dev,
                        "Failed to retrieve lpcpd-gpios from dts.\n");
                phy->io_lpcpd = -1;
                /*
@@ -256,26 +286,22 @@ static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy)
                return 0;
        }
        /* GPIO request and configuration */
-       ret = devm_gpio_request_one(&dev->dev, gpio,
+       ret = devm_gpio_request_one(&spi_dev->dev, gpio,
                        GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD");
        if (ret) {
-               dev_err(&dev->dev, "Failed to request lpcpd pin\n");
+               dev_err(&spi_dev->dev, "Failed to request lpcpd pin\n");
                return -ENODEV;
        }
        phy->io_lpcpd = gpio;
 
        return 0;
 }
-#else
-static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy)
-{
-       return -ENODEV;
-}
-#endif
 
-static int tpm_stm_spi_request_resources(struct spi_device *dev,
-                                        struct st33zp24_spi_phy *phy)
+static int st33zp24_spi_request_resources(struct spi_device *dev)
 {
+       struct tpm_chip *chip = spi_get_drvdata(dev);
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
+       struct st33zp24_spi_phy *phy = tpm_dev->phy_id;
        struct st33zp24_platform_data *pdata;
        int ret;
 
@@ -303,13 +329,12 @@ static int tpm_stm_spi_request_resources(struct spi_device *dev,
 }
 
 /*
- * tpm_st33_spi_probe initialize the TPM device
+ * st33zp24_spi_probe initialize the TPM device
  * @param: dev, the spi_device drescription (TPM SPI description).
  * @return: 0 in case of success.
  *      or a negative value describing the error.
  */
-static int
-tpm_st33_spi_probe(struct spi_device *dev)
+static int st33zp24_spi_probe(struct spi_device *dev)
 {
        int ret;
        struct st33zp24_platform_data *pdata;
@@ -328,21 +353,23 @@ tpm_st33_spi_probe(struct spi_device *dev)
                return -ENOMEM;
 
        phy->spi_device = dev;
+
        pdata = dev->dev.platform_data;
        if (!pdata && dev->dev.of_node) {
-               ret = tpm_stm_spi_of_request_resources(phy);
+               ret = st33zp24_spi_of_request_resources(dev);
                if (ret)
                        return ret;
        } else if (pdata) {
-               ret = tpm_stm_spi_request_resources(dev, phy);
+               ret = st33zp24_spi_request_resources(dev);
+               if (ret)
+                       return ret;
+       } else if (ACPI_HANDLE(&dev->dev)) {
+               ret = st33zp24_spi_acpi_request_resources(dev);
                if (ret)
                        return ret;
        }
 
-       phy->spi_xfer.tx_buf = phy->tx_buf;
-       phy->spi_xfer.rx_buf = phy->rx_buf;
-
-       phy->latency = evaluate_latency(phy);
+       phy->latency = st33zp24_spi_evaluate_latency(phy);
        if (phy->latency <= 0)
                return -ENODEV;
 
@@ -351,11 +378,11 @@ tpm_st33_spi_probe(struct spi_device *dev)
 }
 
 /*
- * tpm_st33_spi_remove remove the TPM device
+ * st33zp24_spi_remove remove the TPM device
  * @param: client, the spi_device drescription (TPM SPI description).
  * @return: 0 in case of success.
  */
-static int tpm_st33_spi_remove(struct spi_device *dev)
+static int st33zp24_spi_remove(struct spi_device *dev)
 {
        struct tpm_chip *chip = spi_get_drvdata(dev);
 
@@ -368,29 +395,34 @@ static const struct spi_device_id st33zp24_spi_id[] = {
 };
 MODULE_DEVICE_TABLE(spi, st33zp24_spi_id);
 
-#ifdef CONFIG_OF
 static const struct of_device_id of_st33zp24_spi_match[] = {
        { .compatible = "st,st33zp24-spi", },
        {}
 };
 MODULE_DEVICE_TABLE(of, of_st33zp24_spi_match);
-#endif
+
+static const struct acpi_device_id st33zp24_spi_acpi_match[] = {
+       {"SMO3324"},
+       {}
+};
+MODULE_DEVICE_TABLE(acpi, st33zp24_spi_acpi_match);
 
 static SIMPLE_DEV_PM_OPS(st33zp24_spi_ops, st33zp24_pm_suspend,
                         st33zp24_pm_resume);
 
-static struct spi_driver tpm_st33_spi_driver = {
+static struct spi_driver st33zp24_spi_driver = {
        .driver = {
                .name = TPM_ST33_SPI,
                .pm = &st33zp24_spi_ops,
                .of_match_table = of_match_ptr(of_st33zp24_spi_match),
+               .acpi_match_table = ACPI_PTR(st33zp24_spi_acpi_match),
        },
-       .probe = tpm_st33_spi_probe,
-       .remove = tpm_st33_spi_remove,
+       .probe = st33zp24_spi_probe,
+       .remove = st33zp24_spi_remove,
        .id_table = st33zp24_spi_id,
 };
 
-module_spi_driver(tpm_st33_spi_driver);
+module_spi_driver(st33zp24_spi_driver);
 
 MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)");
 MODULE_DESCRIPTION("STM TPM 1.2 SPI ST33 Driver");
index 8d626784cd8dd8bec4f61ceb568fe06cac2bb995..c2ee30451e41d8632b01ae538f659aeb24a6e83d 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * STMicroelectronics TPM Linux driver for TPM ST33ZP24
- * Copyright (C) 2009 - 2015 STMicroelectronics
+ * Copyright (C) 2009 - 2016 STMicroelectronics
  *
  * 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
@@ -73,14 +73,6 @@ enum tis_defaults {
        TIS_LONG_TIMEOUT = 2000,
 };
 
-struct st33zp24_dev {
-       struct tpm_chip *chip;
-       void *phy_id;
-       const struct st33zp24_phy_ops *ops;
-       u32 intrs;
-       int io_lpcpd;
-};
-
 /*
  * clear_interruption clear the pending interrupt.
  * @param: tpm_dev, the tpm device device.
@@ -102,11 +94,9 @@ static u8 clear_interruption(struct st33zp24_dev *tpm_dev)
  */
 static void st33zp24_cancel(struct tpm_chip *chip)
 {
-       struct st33zp24_dev *tpm_dev;
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
        u8 data;
 
-       tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
-
        data = TPM_STS_COMMAND_READY;
        tpm_dev->ops->send(tpm_dev->phy_id, TPM_STS, &data, 1);
 } /* st33zp24_cancel() */
@@ -118,11 +108,9 @@ static void st33zp24_cancel(struct tpm_chip *chip)
  */
 static u8 st33zp24_status(struct tpm_chip *chip)
 {
-       struct st33zp24_dev *tpm_dev;
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
        u8 data;
 
-       tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
-
        tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS, &data, 1);
        return data;
 } /* st33zp24_status() */
@@ -134,17 +122,15 @@ static u8 st33zp24_status(struct tpm_chip *chip)
  */
 static int check_locality(struct tpm_chip *chip)
 {
-       struct st33zp24_dev *tpm_dev;
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
        u8 data;
        u8 status;
 
-       tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
-
        status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
        if (status && (data &
                (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
                (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
-               return chip->vendor.locality;
+               return tpm_dev->locality;
 
        return -EACCES;
 } /* check_locality() */
@@ -156,27 +142,25 @@ static int check_locality(struct tpm_chip *chip)
  */
 static int request_locality(struct tpm_chip *chip)
 {
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
        unsigned long stop;
        long ret;
-       struct st33zp24_dev *tpm_dev;
        u8 data;
 
-       if (check_locality(chip) == chip->vendor.locality)
-               return chip->vendor.locality;
-
-       tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
+       if (check_locality(chip) == tpm_dev->locality)
+               return tpm_dev->locality;
 
        data = TPM_ACCESS_REQUEST_USE;
        ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
        if (ret < 0)
                return ret;
 
-       stop = jiffies + chip->vendor.timeout_a;
+       stop = jiffies + chip->timeout_a;
 
        /* Request locality is usually effective after the request */
        do {
                if (check_locality(chip) >= 0)
-                       return chip->vendor.locality;
+                       return tpm_dev->locality;
                msleep(TPM_TIMEOUT);
        } while (time_before(jiffies, stop));
 
@@ -190,10 +174,9 @@ static int request_locality(struct tpm_chip *chip)
  */
 static void release_locality(struct tpm_chip *chip)
 {
-       struct st33zp24_dev *tpm_dev;
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
        u8 data;
 
-       tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
        data = TPM_ACCESS_ACTIVE_LOCALITY;
 
        tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
@@ -206,23 +189,21 @@ static void release_locality(struct tpm_chip *chip)
  */
 static int get_burstcount(struct tpm_chip *chip)
 {
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
        unsigned long stop;
        int burstcnt, status;
-       u8 tpm_reg, temp;
-       struct st33zp24_dev *tpm_dev;
-
-       tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
+       u8 temp;
 
-       stop = jiffies + chip->vendor.timeout_d;
+       stop = jiffies + chip->timeout_d;
        do {
-               tpm_reg = TPM_STS + 1;
-               status = tpm_dev->ops->recv(tpm_dev->phy_id, tpm_reg, &temp, 1);
+               status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS + 1,
+                                           &temp, 1);
                if (status < 0)
                        return -EBUSY;
 
-               tpm_reg = TPM_STS + 2;
                burstcnt = temp;
-               status = tpm_dev->ops->recv(tpm_dev->phy_id, tpm_reg, &temp, 1);
+               status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS + 2,
+                                           &temp, 1);
                if (status < 0)
                        return -EBUSY;
 
@@ -271,15 +252,13 @@ static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask,
 static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
                        wait_queue_head_t *queue, bool check_cancel)
 {
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
        unsigned long stop;
        int ret = 0;
        bool canceled = false;
        bool condition;
        u32 cur_intrs;
        u8 status;
-       struct st33zp24_dev *tpm_dev;
-
-       tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
 
        /* check current status */
        status = st33zp24_status(chip);
@@ -288,10 +267,10 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
 
        stop = jiffies + timeout;
 
-       if (chip->vendor.irq) {
+       if (chip->flags & TPM_CHIP_FLAG_IRQ) {
                cur_intrs = tpm_dev->intrs;
                clear_interruption(tpm_dev);
-               enable_irq(chip->vendor.irq);
+               enable_irq(tpm_dev->irq);
 
                do {
                        if (ret == -ERESTARTSYS && freezing(current))
@@ -314,7 +293,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
                        }
                } while (ret == -ERESTARTSYS && freezing(current));
 
-               disable_irq_nosync(chip->vendor.irq);
+               disable_irq_nosync(tpm_dev->irq);
 
        } else {
                do {
@@ -337,16 +316,14 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
  */
 static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
 {
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
        int size = 0, burstcnt, len, ret;
-       struct st33zp24_dev *tpm_dev;
-
-       tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
 
        while (size < count &&
               wait_for_stat(chip,
                             TPM_STS_DATA_AVAIL | TPM_STS_VALID,
-                            chip->vendor.timeout_c,
-                            &chip->vendor.read_queue, true) == 0) {
+                            chip->timeout_c,
+                            &tpm_dev->read_queue, true) == 0) {
                burstcnt = get_burstcount(chip);
                if (burstcnt < 0)
                        return burstcnt;
@@ -370,13 +347,11 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
 static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id)
 {
        struct tpm_chip *chip = dev_id;
-       struct st33zp24_dev *tpm_dev;
-
-       tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
 
        tpm_dev->intrs++;
-       wake_up_interruptible(&chip->vendor.read_queue);
-       disable_irq_nosync(chip->vendor.irq);
+       wake_up_interruptible(&tpm_dev->read_queue);
+       disable_irq_nosync(tpm_dev->irq);
 
        return IRQ_HANDLED;
 } /* tpm_ioserirq_handler() */
@@ -393,19 +368,17 @@ static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id)
 static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf,
                         size_t len)
 {
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
        u32 status, i, size, ordinal;
        int burstcnt = 0;
        int ret;
        u8 data;
-       struct st33zp24_dev *tpm_dev;
 
        if (!chip)
                return -EBUSY;
        if (len < TPM_HEADER_SIZE)
                return -EBUSY;
 
-       tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
-
        ret = request_locality(chip);
        if (ret < 0)
                return ret;
@@ -414,8 +387,8 @@ static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf,
        if ((status & TPM_STS_COMMAND_READY) == 0) {
                st33zp24_cancel(chip);
                if (wait_for_stat
-                   (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
-                    &chip->vendor.read_queue, false) < 0) {
+                   (chip, TPM_STS_COMMAND_READY, chip->timeout_b,
+                    &tpm_dev->read_queue, false) < 0) {
                        ret = -ETIME;
                        goto out_err;
                }
@@ -456,12 +429,12 @@ static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf,
        if (ret < 0)
                goto out_err;
 
-       if (chip->vendor.irq) {
+       if (chip->flags & TPM_CHIP_FLAG_IRQ) {
                ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
 
                ret = wait_for_stat(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
                                tpm_calc_ordinal_duration(chip, ordinal),
-                               &chip->vendor.read_queue, false);
+                               &tpm_dev->read_queue, false);
                if (ret < 0)
                        goto out_err;
        }
@@ -532,6 +505,7 @@ static bool st33zp24_req_canceled(struct tpm_chip *chip, u8 status)
 }
 
 static const struct tpm_class_ops st33zp24_tpm = {
+       .flags = TPM_OPS_AUTO_STARTUP,
        .send = st33zp24_send,
        .recv = st33zp24_recv,
        .cancel = st33zp24_cancel,
@@ -565,20 +539,20 @@ int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops,
        if (!tpm_dev)
                return -ENOMEM;
 
-       TPM_VPRIV(chip) = tpm_dev;
        tpm_dev->phy_id = phy_id;
        tpm_dev->ops = ops;
+       dev_set_drvdata(&chip->dev, tpm_dev);
 
-       chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
-       chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
-       chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
-       chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+       chip->timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+       chip->timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
+       chip->timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+       chip->timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
 
-       chip->vendor.locality = LOCALITY0;
+       tpm_dev->locality = LOCALITY0;
 
        if (irq) {
                /* INTERRUPT Setup */
-               init_waitqueue_head(&chip->vendor.read_queue);
+               init_waitqueue_head(&tpm_dev->read_queue);
                tpm_dev->intrs = 0;
 
                if (request_locality(chip) != LOCALITY0) {
@@ -611,16 +585,14 @@ int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops,
                if (ret < 0)
                        goto _tpm_clean_answer;
 
-               chip->vendor.irq = irq;
+               tpm_dev->irq = irq;
+               chip->flags |= TPM_CHIP_FLAG_IRQ;
 
-               disable_irq_nosync(chip->vendor.irq);
+               disable_irq_nosync(tpm_dev->irq);
 
                tpm_gen_interrupt(chip);
        }
 
-       tpm_get_timeouts(chip);
-       tpm_do_selftest(chip);
-
        return tpm_chip_register(chip);
 _tpm_clean_answer:
        dev_info(&chip->dev, "TPM initialization fail\n");
@@ -650,10 +622,9 @@ EXPORT_SYMBOL(st33zp24_remove);
 int st33zp24_pm_suspend(struct device *dev)
 {
        struct tpm_chip *chip = dev_get_drvdata(dev);
-       struct st33zp24_dev *tpm_dev;
-       int ret = 0;
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
 
-       tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
+       int ret = 0;
 
        if (gpio_is_valid(tpm_dev->io_lpcpd))
                gpio_set_value(tpm_dev->io_lpcpd, 0);
@@ -672,16 +643,14 @@ EXPORT_SYMBOL(st33zp24_pm_suspend);
 int st33zp24_pm_resume(struct device *dev)
 {
        struct tpm_chip *chip = dev_get_drvdata(dev);
-       struct st33zp24_dev *tpm_dev;
+       struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
        int ret = 0;
 
-       tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
-
        if (gpio_is_valid(tpm_dev->io_lpcpd)) {
                gpio_set_value(tpm_dev->io_lpcpd, 1);
                ret = wait_for_stat(chip,
-                               TPM_STS_VALID, chip->vendor.timeout_b,
-                               &chip->vendor.read_queue, false);
+                               TPM_STS_VALID, chip->timeout_b,
+                               &tpm_dev->read_queue, false);
        } else {
                ret = tpm_pm_resume(dev);
                if (!ret)
index c207cebf67dd9cc9a23078d3d50613a28bb37089..6f4a4198af6aa2637fb2347dff6cdb9109054b24 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * STMicroelectronics TPM Linux driver for TPM ST33ZP24
- * Copyright (C) 2009 - 2015  STMicroelectronics
+ * Copyright (C) 2009 - 2016  STMicroelectronics
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
 #define TPM_WRITE_DIRECTION             0x80
 #define TPM_BUFSIZE                     2048
 
+struct st33zp24_dev {
+       struct tpm_chip *chip;
+       void *phy_id;
+       const struct st33zp24_phy_ops *ops;
+       int locality;
+       int irq;
+       u32 intrs;
+       int io_lpcpd;
+       wait_queue_head_t read_queue;
+};
+
+
 struct st33zp24_phy_ops {
        int (*send)(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size);
        int (*recv)(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size);
index 274dd0123237861eccfd1dc6ab76a5f9adcbdda6..e5950131bd907dbee3f4b4c3598c05572fcea416 100644 (file)
 #include "tpm.h"
 #include "tpm_eventlog.h"
 
-static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);
-static LIST_HEAD(tpm_chip_list);
-static DEFINE_SPINLOCK(driver_lock);
+DEFINE_IDR(dev_nums_idr);
+static DEFINE_MUTEX(idr_lock);
 
 struct class *tpm_class;
 dev_t tpm_devt;
 
-/*
- * tpm_chip_find_get - return tpm_chip for a given chip number
- * @chip_num the device number for the chip
+/**
+ * tpm_try_get_ops() - Get a ref to the tpm_chip
+ * @chip: Chip to ref
+ *
+ * The caller must already have some kind of locking to ensure that chip is
+ * valid. This function will lock the chip so that the ops member can be
+ * accessed safely. The locking prevents tpm_chip_unregister from
+ * completing, so it should not be held for long periods.
+ *
+ * Returns -ERRNO if the chip could not be got.
  */
-struct tpm_chip *tpm_chip_find_get(int chip_num)
+int tpm_try_get_ops(struct tpm_chip *chip)
 {
-       struct tpm_chip *pos, *chip = NULL;
+       int rc = -EIO;
 
-       rcu_read_lock();
-       list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
-               if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num)
-                       continue;
+       get_device(&chip->dev);
 
-               if (try_module_get(pos->pdev->driver->owner)) {
-                       chip = pos;
-                       break;
-               }
+       down_read(&chip->ops_sem);
+       if (!chip->ops)
+               goto out_lock;
+
+       return 0;
+out_lock:
+       up_read(&chip->ops_sem);
+       put_device(&chip->dev);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_try_get_ops);
+
+/**
+ * tpm_put_ops() - Release a ref to the tpm_chip
+ * @chip: Chip to put
+ *
+ * This is the opposite pair to tpm_try_get_ops(). After this returns chip may
+ * be kfree'd.
+ */
+void tpm_put_ops(struct tpm_chip *chip)
+{
+       up_read(&chip->ops_sem);
+       put_device(&chip->dev);
+}
+EXPORT_SYMBOL_GPL(tpm_put_ops);
+
+/**
+ * tpm_chip_find_get() - return tpm_chip for a given chip number
+ * @chip_num: id to find
+ *
+ * The return'd chip has been tpm_try_get_ops'd and must be released via
+ * tpm_put_ops
+  */
+struct tpm_chip *tpm_chip_find_get(int chip_num)
+{
+       struct tpm_chip *chip, *res = NULL;
+       int chip_prev;
+
+       mutex_lock(&idr_lock);
+
+       if (chip_num == TPM_ANY_NUM) {
+               chip_num = 0;
+               do {
+                       chip_prev = chip_num;
+                       chip = idr_get_next(&dev_nums_idr, &chip_num);
+                       if (chip && !tpm_try_get_ops(chip)) {
+                               res = chip;
+                               break;
+                       }
+               } while (chip_prev != chip_num);
+       } else {
+               chip = idr_find_slowpath(&dev_nums_idr, chip_num);
+               if (chip && !tpm_try_get_ops(chip))
+                       res = chip;
        }
-       rcu_read_unlock();
-       return chip;
+
+       mutex_unlock(&idr_lock);
+
+       return res;
 }
 
 /**
@@ -68,24 +123,25 @@ static void tpm_dev_release(struct device *dev)
 {
        struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev);
 
-       spin_lock(&driver_lock);
-       clear_bit(chip->dev_num, dev_mask);
-       spin_unlock(&driver_lock);
+       mutex_lock(&idr_lock);
+       idr_remove(&dev_nums_idr, chip->dev_num);
+       mutex_unlock(&idr_lock);
+
        kfree(chip);
 }
 
 /**
- * tpmm_chip_alloc() - allocate a new struct tpm_chip instance
- * @dev: device to which the chip is associated
+ * tpm_chip_alloc() - allocate a new struct tpm_chip instance
+ * @pdev: device to which the chip is associated
+ *        At this point pdev mst be initialized, but does not have to
+ *        be registered
  * @ops: struct tpm_class_ops instance
  *
  * Allocates a new struct tpm_chip instance and assigns a free
- * device number for it. Caller does not have to worry about
- * freeing the allocated resources. When the devices is removed
- * devres calls tpmm_chip_remove() to do the job.
+ * device number for it. Must be paired with put_device(&chip->dev).
  */
-struct tpm_chip *tpmm_chip_alloc(struct device *dev,
-                                const struct tpm_class_ops *ops)
+struct tpm_chip *tpm_chip_alloc(struct device *dev,
+                               const struct tpm_class_ops *ops)
 {
        struct tpm_chip *chip;
        int rc;
@@ -95,53 +151,75 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev,
                return ERR_PTR(-ENOMEM);
 
        mutex_init(&chip->tpm_mutex);
-       INIT_LIST_HEAD(&chip->list);
+       init_rwsem(&chip->ops_sem);
 
        chip->ops = ops;
 
-       spin_lock(&driver_lock);
-       chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES);
-       spin_unlock(&driver_lock);
-
-       if (chip->dev_num >= TPM_NUM_DEVICES) {
+       mutex_lock(&idr_lock);
+       rc = idr_alloc(&dev_nums_idr, NULL, 0, TPM_NUM_DEVICES, GFP_KERNEL);
+       mutex_unlock(&idr_lock);
+       if (rc < 0) {
                dev_err(dev, "No available tpm device numbers\n");
                kfree(chip);
-               return ERR_PTR(-ENOMEM);
+               return ERR_PTR(rc);
        }
+       chip->dev_num = rc;
 
-       set_bit(chip->dev_num, dev_mask);
-
-       scnprintf(chip->devname, sizeof(chip->devname), "tpm%d", chip->dev_num);
-
-       chip->pdev = dev;
-
-       dev_set_drvdata(dev, chip);
+       device_initialize(&chip->dev);
 
        chip->dev.class = tpm_class;
        chip->dev.release = tpm_dev_release;
-       chip->dev.parent = chip->pdev;
-#ifdef CONFIG_ACPI
+       chip->dev.parent = dev;
        chip->dev.groups = chip->groups;
-#endif
 
        if (chip->dev_num == 0)
                chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR);
        else
                chip->dev.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num);
 
-       dev_set_name(&chip->dev, "%s", chip->devname);
+       rc = dev_set_name(&chip->dev, "tpm%d", chip->dev_num);
+       if (rc)
+               goto out;
 
-       device_initialize(&chip->dev);
+       if (!dev)
+               chip->flags |= TPM_CHIP_FLAG_VIRTUAL;
 
        cdev_init(&chip->cdev, &tpm_fops);
-       chip->cdev.owner = chip->pdev->driver->owner;
+       chip->cdev.owner = THIS_MODULE;
        chip->cdev.kobj.parent = &chip->dev.kobj;
 
-       rc = devm_add_action(dev, (void (*)(void *)) put_device, &chip->dev);
-       if (rc) {
-               put_device(&chip->dev);
+       return chip;
+
+out:
+       put_device(&chip->dev);
+       return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(tpm_chip_alloc);
+
+/**
+ * tpmm_chip_alloc() - allocate a new struct tpm_chip instance
+ * @pdev: parent device to which the chip is associated
+ * @ops: struct tpm_class_ops instance
+ *
+ * Same as tpm_chip_alloc except devm is used to do the put_device
+ */
+struct tpm_chip *tpmm_chip_alloc(struct device *pdev,
+                                const struct tpm_class_ops *ops)
+{
+       struct tpm_chip *chip;
+       int rc;
+
+       chip = tpm_chip_alloc(pdev, ops);
+       if (IS_ERR(chip))
+               return chip;
+
+       rc = devm_add_action_or_reset(pdev,
+                                     (void (*)(void *)) put_device,
+                                     &chip->dev);
+       if (rc)
                return ERR_PTR(rc);
-       }
+
+       dev_set_drvdata(pdev, chip);
 
        return chip;
 }
@@ -155,7 +233,7 @@ static int tpm_add_char_device(struct tpm_chip *chip)
        if (rc) {
                dev_err(&chip->dev,
                        "unable to cdev_add() %s, major %d, minor %d, err=%d\n",
-                       chip->devname, MAJOR(chip->dev.devt),
+                       dev_name(&chip->dev), MAJOR(chip->dev.devt),
                        MINOR(chip->dev.devt), rc);
 
                return rc;
@@ -165,13 +243,18 @@ static int tpm_add_char_device(struct tpm_chip *chip)
        if (rc) {
                dev_err(&chip->dev,
                        "unable to device_register() %s, major %d, minor %d, err=%d\n",
-                       chip->devname, MAJOR(chip->dev.devt),
+                       dev_name(&chip->dev), MAJOR(chip->dev.devt),
                        MINOR(chip->dev.devt), rc);
 
                cdev_del(&chip->cdev);
                return rc;
        }
 
+       /* Make the chip available. */
+       mutex_lock(&idr_lock);
+       idr_replace(&dev_nums_idr, chip, chip->dev_num);
+       mutex_unlock(&idr_lock);
+
        return rc;
 }
 
@@ -179,20 +262,28 @@ static void tpm_del_char_device(struct tpm_chip *chip)
 {
        cdev_del(&chip->cdev);
        device_del(&chip->dev);
+
+       /* Make the chip unavailable. */
+       mutex_lock(&idr_lock);
+       idr_replace(&dev_nums_idr, NULL, chip->dev_num);
+       mutex_unlock(&idr_lock);
+
+       /* Make the driver uncallable. */
+       down_write(&chip->ops_sem);
+       if (chip->flags & TPM_CHIP_FLAG_TPM2)
+               tpm2_shutdown(chip, TPM2_SU_CLEAR);
+       chip->ops = NULL;
+       up_write(&chip->ops_sem);
 }
 
 static int tpm1_chip_register(struct tpm_chip *chip)
 {
-       int rc;
-
        if (chip->flags & TPM_CHIP_FLAG_TPM2)
                return 0;
 
-       rc = tpm_sysfs_add_device(chip);
-       if (rc)
-               return rc;
+       tpm_sysfs_add_device(chip);
 
-       chip->bios_dir = tpm_bios_log_setup(chip->devname);
+       chip->bios_dir = tpm_bios_log_setup(dev_name(&chip->dev));
 
        return 0;
 }
@@ -204,10 +295,50 @@ static void tpm1_chip_unregister(struct tpm_chip *chip)
 
        if (chip->bios_dir)
                tpm_bios_log_teardown(chip->bios_dir);
+}
+
+static void tpm_del_legacy_sysfs(struct tpm_chip *chip)
+{
+       struct attribute **i;
+
+       if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL))
+               return;
+
+       sysfs_remove_link(&chip->dev.parent->kobj, "ppi");
 
-       tpm_sysfs_del_device(chip);
+       for (i = chip->groups[0]->attrs; *i != NULL; ++i)
+               sysfs_remove_link(&chip->dev.parent->kobj, (*i)->name);
 }
 
+/* For compatibility with legacy sysfs paths we provide symlinks from the
+ * parent dev directory to selected names within the tpm chip directory. Old
+ * kernel versions created these files directly under the parent.
+ */
+static int tpm_add_legacy_sysfs(struct tpm_chip *chip)
+{
+       struct attribute **i;
+       int rc;
+
+       if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL))
+               return 0;
+
+       rc = __compat_only_sysfs_link_entry_to_kobj(
+                   &chip->dev.parent->kobj, &chip->dev.kobj, "ppi");
+       if (rc && rc != -ENOENT)
+               return rc;
+
+       /* All the names from tpm-sysfs */
+       for (i = chip->groups[0]->attrs; *i != NULL; ++i) {
+               rc = __compat_only_sysfs_link_entry_to_kobj(
+                   &chip->dev.parent->kobj, &chip->dev.kobj, (*i)->name);
+               if (rc) {
+                       tpm_del_legacy_sysfs(chip);
+                       return rc;
+               }
+       }
+
+       return 0;
+}
 /*
  * tpm_chip_register() - create a character device for the TPM chip
  * @chip: TPM chip to use.
@@ -223,6 +354,15 @@ int tpm_chip_register(struct tpm_chip *chip)
 {
        int rc;
 
+       if (chip->ops->flags & TPM_OPS_AUTO_STARTUP) {
+               if (chip->flags & TPM_CHIP_FLAG_TPM2)
+                       rc = tpm2_auto_startup(chip);
+               else
+                       rc = tpm1_auto_startup(chip);
+               if (rc)
+                       return rc;
+       }
+
        rc = tpm1_chip_register(chip);
        if (rc)
                return rc;
@@ -230,30 +370,20 @@ int tpm_chip_register(struct tpm_chip *chip)
        tpm_add_ppi(chip);
 
        rc = tpm_add_char_device(chip);
-       if (rc)
-               goto out_err;
-
-       /* Make the chip available. */
-       spin_lock(&driver_lock);
-       list_add_tail_rcu(&chip->list, &tpm_chip_list);
-       spin_unlock(&driver_lock);
+       if (rc) {
+               tpm1_chip_unregister(chip);
+               return rc;
+       }
 
        chip->flags |= TPM_CHIP_FLAG_REGISTERED;
 
-       if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
-               rc = __compat_only_sysfs_link_entry_to_kobj(&chip->pdev->kobj,
-                                                           &chip->dev.kobj,
-                                                           "ppi");
-               if (rc && rc != -ENOENT) {
-                       tpm_chip_unregister(chip);
-                       return rc;
-               }
+       rc = tpm_add_legacy_sysfs(chip);
+       if (rc) {
+               tpm_chip_unregister(chip);
+               return rc;
        }
 
        return 0;
-out_err:
-       tpm1_chip_unregister(chip);
-       return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_chip_register);
 
@@ -264,6 +394,9 @@ EXPORT_SYMBOL_GPL(tpm_chip_register);
  * Takes the chip first away from the list of available TPM chips and then
  * cleans up all the resources reserved by tpm_chip_register().
  *
+ * Once this function returns the driver call backs in 'op's will not be
+ * running and will no longer start.
+ *
  * NOTE: This function should be only called before deinitializing chip
  * resources.
  */
@@ -272,13 +405,7 @@ void tpm_chip_unregister(struct tpm_chip *chip)
        if (!(chip->flags & TPM_CHIP_FLAG_REGISTERED))
                return;
 
-       spin_lock(&driver_lock);
-       list_del_rcu(&chip->list);
-       spin_unlock(&driver_lock);
-       synchronize_rcu();
-
-       if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
-               sysfs_remove_link(&chip->pdev->kobj, "ppi");
+       tpm_del_legacy_sysfs(chip);
 
        tpm1_chip_unregister(chip);
        tpm_del_char_device(chip);
index de0337ebd6585b7a68aeabdd297bd611474b1bee..f5d452151c6bde55f1bd1d0123ebae07b428abf1 100644 (file)
@@ -61,7 +61,7 @@ static int tpm_open(struct inode *inode, struct file *file)
         * by the check of is_open variable, which is protected
         * by driver_lock. */
        if (test_and_set_bit(0, &chip->is_open)) {
-               dev_dbg(chip->pdev, "Another process owns this TPM\n");
+               dev_dbg(&chip->dev, "Another process owns this TPM\n");
                return -EBUSY;
        }
 
@@ -79,7 +79,6 @@ static int tpm_open(struct inode *inode, struct file *file)
        INIT_WORK(&priv->work, timeout_work);
 
        file->private_data = priv;
-       get_device(chip->pdev);
        return 0;
 }
 
@@ -137,9 +136,18 @@ static ssize_t tpm_write(struct file *file, const char __user *buf,
                return -EFAULT;
        }
 
-       /* atomic tpm command send and result receive */
+       /* atomic tpm command send and result receive. We only hold the ops
+        * lock during this period so that the tpm can be unregistered even if
+        * the char dev is held open.
+        */
+       if (tpm_try_get_ops(priv->chip)) {
+               mutex_unlock(&priv->buffer_mutex);
+               return -EPIPE;
+       }
        out_size = tpm_transmit(priv->chip, priv->data_buffer,
                                sizeof(priv->data_buffer));
+
+       tpm_put_ops(priv->chip);
        if (out_size < 0) {
                mutex_unlock(&priv->buffer_mutex);
                return out_size;
@@ -166,7 +174,6 @@ static int tpm_release(struct inode *inode, struct file *file)
        file->private_data = NULL;
        atomic_set(&priv->data_pending, 0);
        clear_bit(0, &priv->chip->is_open);
-       put_device(priv->chip->pdev);
        kfree(priv);
        return 0;
 }
index e2fa89c88304b29eab182311a1fa639d141ead64..1abe2d7a2610708cc9f1101b83f248b6d3f058c0 100644 (file)
@@ -319,7 +319,7 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip,
                duration_idx = tpm_ordinal_duration[ordinal];
 
        if (duration_idx != TPM_UNDEFINED)
-               duration = chip->vendor.duration[duration_idx];
+               duration = chip->duration[duration_idx];
        if (duration <= 0)
                return 2 * 60 * HZ;
        else
@@ -345,7 +345,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
        if (count == 0)
                return -ENODATA;
        if (count > bufsiz) {
-               dev_err(chip->pdev,
+               dev_err(&chip->dev,
                        "invalid count value %x %zx\n", count, bufsiz);
                return -E2BIG;
        }
@@ -354,12 +354,12 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 
        rc = chip->ops->send(chip, (u8 *) buf, count);
        if (rc < 0) {
-               dev_err(chip->pdev,
+               dev_err(&chip->dev,
                        "tpm_transmit: tpm_send: error %zd\n", rc);
                goto out;
        }
 
-       if (chip->vendor.irq)
+       if (chip->flags & TPM_CHIP_FLAG_IRQ)
                goto out_recv;
 
        if (chip->flags & TPM_CHIP_FLAG_TPM2)
@@ -373,7 +373,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
                        goto out_recv;
 
                if (chip->ops->req_canceled(chip, status)) {
-                       dev_err(chip->pdev, "Operation Canceled\n");
+                       dev_err(&chip->dev, "Operation Canceled\n");
                        rc = -ECANCELED;
                        goto out;
                }
@@ -383,14 +383,14 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
        } while (time_before(jiffies, stop));
 
        chip->ops->cancel(chip);
-       dev_err(chip->pdev, "Operation Timed out\n");
+       dev_err(&chip->dev, "Operation Timed out\n");
        rc = -ETIME;
        goto out;
 
 out_recv:
        rc = chip->ops->recv(chip, (u8 *) buf, bufsiz);
        if (rc < 0)
-               dev_err(chip->pdev,
+               dev_err(&chip->dev,
                        "tpm_transmit: tpm_recv: error %zd\n", rc);
 out:
        mutex_unlock(&chip->tpm_mutex);
@@ -416,7 +416,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd,
 
        err = be32_to_cpu(header->return_code);
        if (err != 0 && desc)
-               dev_err(chip->pdev, "A TPM error (%d) occurred %s\n", err,
+               dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err,
                        desc);
 
        return err;
@@ -432,12 +432,11 @@ static const struct tpm_input_header tpm_getcap_header = {
        .ordinal = TPM_ORD_GET_CAP
 };
 
-ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap,
+ssize_t tpm_getcap(struct tpm_chip *chip, __be32 subcap_id, cap_t *cap,
                   const char *desc)
 {
        struct tpm_cmd_t tpm_cmd;
        int rc;
-       struct tpm_chip *chip = dev_get_drvdata(dev);
 
        tpm_cmd.header.in = tpm_getcap_header;
        if (subcap_id == CAP_VERSION_1_1 || subcap_id == CAP_VERSION_1_2) {
@@ -505,15 +504,15 @@ int tpm_get_timeouts(struct tpm_chip *chip)
 
        if (chip->flags & TPM_CHIP_FLAG_TPM2) {
                /* Fixed timeouts for TPM2 */
-               chip->vendor.timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
-               chip->vendor.timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B);
-               chip->vendor.timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C);
-               chip->vendor.timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D);
-               chip->vendor.duration[TPM_SHORT] =
+               chip->timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
+               chip->timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B);
+               chip->timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C);
+               chip->timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D);
+               chip->duration[TPM_SHORT] =
                    msecs_to_jiffies(TPM2_DURATION_SHORT);
-               chip->vendor.duration[TPM_MEDIUM] =
+               chip->duration[TPM_MEDIUM] =
                    msecs_to_jiffies(TPM2_DURATION_MEDIUM);
-               chip->vendor.duration[TPM_LONG] =
+               chip->duration[TPM_LONG] =
                    msecs_to_jiffies(TPM2_DURATION_LONG);
                return 0;
        }
@@ -527,7 +526,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
        if (rc == TPM_ERR_INVALID_POSTINIT) {
                /* The TPM is not started, we are the first to talk to it.
                   Execute a startup command. */
-               dev_info(chip->pdev, "Issuing TPM_STARTUP");
+               dev_info(&chip->dev, "Issuing TPM_STARTUP");
                if (tpm_startup(chip, TPM_ST_CLEAR))
                        return rc;
 
@@ -539,7 +538,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
                                  NULL);
        }
        if (rc) {
-               dev_err(chip->pdev,
+               dev_err(&chip->dev,
                        "A TPM error (%zd) occurred attempting to determine the timeouts\n",
                        rc);
                goto duration;
@@ -561,10 +560,10 @@ int tpm_get_timeouts(struct tpm_chip *chip)
         * of misreporting.
         */
        if (chip->ops->update_timeouts != NULL)
-               chip->vendor.timeout_adjusted =
+               chip->timeout_adjusted =
                        chip->ops->update_timeouts(chip, new_timeout);
 
-       if (!chip->vendor.timeout_adjusted) {
+       if (!chip->timeout_adjusted) {
                /* Don't overwrite default if value is 0 */
                if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
                        int i;
@@ -572,13 +571,13 @@ int tpm_get_timeouts(struct tpm_chip *chip)
                        /* timeouts in msec rather usec */
                        for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
                                new_timeout[i] *= 1000;
-                       chip->vendor.timeout_adjusted = true;
+                       chip->timeout_adjusted = true;
                }
        }
 
        /* Report adjusted timeouts */
-       if (chip->vendor.timeout_adjusted) {
-               dev_info(chip->pdev,
+       if (chip->timeout_adjusted) {
+               dev_info(&chip->dev,
                         HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
                         old_timeout[0], new_timeout[0],
                         old_timeout[1], new_timeout[1],
@@ -586,10 +585,10 @@ int tpm_get_timeouts(struct tpm_chip *chip)
                         old_timeout[3], new_timeout[3]);
        }
 
-       chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
-       chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
-       chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
-       chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
+       chip->timeout_a = usecs_to_jiffies(new_timeout[0]);
+       chip->timeout_b = usecs_to_jiffies(new_timeout[1]);
+       chip->timeout_c = usecs_to_jiffies(new_timeout[2]);
+       chip->timeout_d = usecs_to_jiffies(new_timeout[3]);
 
 duration:
        tpm_cmd.header.in = tpm_getcap_header;
@@ -608,11 +607,11 @@ duration:
                return -EINVAL;
 
        duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
-       chip->vendor.duration[TPM_SHORT] =
+       chip->duration[TPM_SHORT] =
            usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
-       chip->vendor.duration[TPM_MEDIUM] =
+       chip->duration[TPM_MEDIUM] =
            usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
-       chip->vendor.duration[TPM_LONG] =
+       chip->duration[TPM_LONG] =
            usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
 
        /* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
@@ -620,12 +619,12 @@ duration:
         * fix up the resulting too-small TPM_SHORT value to make things work.
         * We also scale the TPM_MEDIUM and -_LONG values by 1000.
         */
-       if (chip->vendor.duration[TPM_SHORT] < (HZ / 100)) {
-               chip->vendor.duration[TPM_SHORT] = HZ;
-               chip->vendor.duration[TPM_MEDIUM] *= 1000;
-               chip->vendor.duration[TPM_LONG] *= 1000;
-               chip->vendor.duration_adjusted = true;
-               dev_info(chip->pdev, "Adjusting TPM timeout parameters.");
+       if (chip->duration[TPM_SHORT] < (HZ / 100)) {
+               chip->duration[TPM_SHORT] = HZ;
+               chip->duration[TPM_MEDIUM] *= 1000;
+               chip->duration[TPM_LONG] *= 1000;
+               chip->duration_adjusted = true;
+               dev_info(&chip->dev, "Adjusting TPM timeout parameters.");
        }
        return 0;
 }
@@ -700,7 +699,7 @@ int tpm_is_tpm2(u32 chip_num)
 
        rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0;
 
-       tpm_chip_put(chip);
+       tpm_put_ops(chip);
 
        return rc;
 }
@@ -729,7 +728,7 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf)
                rc = tpm2_pcr_read(chip, pcr_idx, res_buf);
        else
                rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf);
-       tpm_chip_put(chip);
+       tpm_put_ops(chip);
        return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_pcr_read);
@@ -764,7 +763,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
 
        if (chip->flags & TPM_CHIP_FLAG_TPM2) {
                rc = tpm2_pcr_extend(chip, pcr_idx, hash);
-               tpm_chip_put(chip);
+               tpm_put_ops(chip);
                return rc;
        }
 
@@ -774,7 +773,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
        rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
                              "attempting extend a PCR value");
 
-       tpm_chip_put(chip);
+       tpm_put_ops(chip);
        return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_pcr_extend);
@@ -815,7 +814,9 @@ int tpm_do_selftest(struct tpm_chip *chip)
                 * around 300ms while the self test is ongoing, keep trying
                 * until the self test duration expires. */
                if (rc == -ETIME) {
-                       dev_info(chip->pdev, HW_ERR "TPM command timed out during continue self test");
+                       dev_info(
+                           &chip->dev, HW_ERR
+                           "TPM command timed out during continue self test");
                        msleep(delay_msec);
                        continue;
                }
@@ -825,7 +826,7 @@ int tpm_do_selftest(struct tpm_chip *chip)
 
                rc = be32_to_cpu(cmd.header.out.return_code);
                if (rc == TPM_ERR_DISABLED || rc == TPM_ERR_DEACTIVATED) {
-                       dev_info(chip->pdev,
+                       dev_info(&chip->dev,
                                 "TPM is disabled/deactivated (0x%X)\n", rc);
                        /* TPM is disabled and/or deactivated; driver can
                         * proceed and TPM does handle commands for
@@ -842,6 +843,33 @@ int tpm_do_selftest(struct tpm_chip *chip)
 }
 EXPORT_SYMBOL_GPL(tpm_do_selftest);
 
+/**
+ * tpm1_auto_startup - Perform the standard automatic TPM initialization
+ *                     sequence
+ * @chip: TPM chip to use
+ *
+ * Returns 0 on success, < 0 in case of fatal error.
+ */
+int tpm1_auto_startup(struct tpm_chip *chip)
+{
+       int rc;
+
+       rc = tpm_get_timeouts(chip);
+       if (rc)
+               goto out;
+       rc = tpm_do_selftest(chip);
+       if (rc) {
+               dev_err(&chip->dev, "TPM self test failed\n");
+               goto out;
+       }
+
+       return rc;
+out:
+       if (rc > 0)
+               rc = -ENODEV;
+       return rc;
+}
+
 int tpm_send(u32 chip_num, void *cmd, size_t buflen)
 {
        struct tpm_chip *chip;
@@ -853,7 +881,7 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen)
 
        rc = tpm_transmit_cmd(chip, cmd, buflen, "attempting tpm_cmd");
 
-       tpm_chip_put(chip);
+       tpm_put_ops(chip);
        return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_send);
@@ -888,7 +916,7 @@ int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
 
        stop = jiffies + timeout;
 
-       if (chip->vendor.irq) {
+       if (chip->flags & TPM_CHIP_FLAG_IRQ) {
 again:
                timeout = stop - jiffies;
                if ((long)timeout <= 0)
@@ -978,10 +1006,10 @@ int tpm_pm_suspend(struct device *dev)
        }
 
        if (rc)
-               dev_err(chip->pdev,
+               dev_err(&chip->dev,
                        "Error (%d) sending savestate before suspend\n", rc);
        else if (try > 0)
-               dev_warn(chip->pdev, "TPM savestate took %dms\n",
+               dev_warn(&chip->dev, "TPM savestate took %dms\n",
                         try * TPM_TIMEOUT_RETRY);
 
        return rc;
@@ -1035,7 +1063,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
 
        if (chip->flags & TPM_CHIP_FLAG_TPM2) {
                err = tpm2_get_random(chip, out, max);
-               tpm_chip_put(chip);
+               tpm_put_ops(chip);
                return err;
        }
 
@@ -1057,7 +1085,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
                num_bytes -= recd;
        } while (retries-- && total < max);
 
-       tpm_chip_put(chip);
+       tpm_put_ops(chip);
        return total ? total : -EIO;
 }
 EXPORT_SYMBOL_GPL(tpm_get_random);
@@ -1083,7 +1111,7 @@ int tpm_seal_trusted(u32 chip_num, struct trusted_key_payload *payload,
 
        rc = tpm2_seal_trusted(chip, payload, options);
 
-       tpm_chip_put(chip);
+       tpm_put_ops(chip);
        return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_seal_trusted);
@@ -1109,7 +1137,8 @@ int tpm_unseal_trusted(u32 chip_num, struct trusted_key_payload *payload,
 
        rc = tpm2_unseal_trusted(chip, payload, options);
 
-       tpm_chip_put(chip);
+       tpm_put_ops(chip);
+
        return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_unseal_trusted);
@@ -1136,6 +1165,7 @@ static int __init tpm_init(void)
 
 static void __exit tpm_exit(void)
 {
+       idr_destroy(&dev_nums_idr);
        class_destroy(tpm_class);
        unregister_chrdev_region(tpm_devt, TPM_NUM_DEVICES);
 }
index ee66fd4673f3596084f75c9033ea24b223ae11d2..b46cf70c8b16b4b06b8218c31a0952dd02ad7bdf 100644 (file)
@@ -36,7 +36,7 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
        int i, rc;
        char *str = buf;
 
-       struct tpm_chip *chip = dev_get_drvdata(dev);
+       struct tpm_chip *chip = to_tpm_chip(dev);
 
        tpm_cmd.header.in = tpm_readpubek_header;
        err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
@@ -92,9 +92,9 @@ static ssize_t pcrs_show(struct device *dev, struct device_attribute *attr,
        ssize_t rc;
        int i, j, num_pcrs;
        char *str = buf;
-       struct tpm_chip *chip = dev_get_drvdata(dev);
+       struct tpm_chip *chip = to_tpm_chip(dev);
 
-       rc = tpm_getcap(dev, TPM_CAP_PROP_PCR, &cap,
+       rc = tpm_getcap(chip, TPM_CAP_PROP_PCR, &cap,
                        "attempting to determine the number of PCRS");
        if (rc)
                return 0;
@@ -119,8 +119,8 @@ static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
        cap_t cap;
        ssize_t rc;
 
-       rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
-                        "attempting to determine the permanent enabled state");
+       rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap,
+                       "attempting to determine the permanent enabled state");
        if (rc)
                return 0;
 
@@ -135,8 +135,8 @@ static ssize_t active_show(struct device *dev, struct device_attribute *attr,
        cap_t cap;
        ssize_t rc;
 
-       rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
-                        "attempting to determine the permanent active state");
+       rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap,
+                       "attempting to determine the permanent active state");
        if (rc)
                return 0;
 
@@ -151,8 +151,8 @@ static ssize_t owned_show(struct device *dev, struct device_attribute *attr,
        cap_t cap;
        ssize_t rc;
 
-       rc = tpm_getcap(dev, TPM_CAP_PROP_OWNER, &cap,
-                        "attempting to determine the owner state");
+       rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_PROP_OWNER, &cap,
+                       "attempting to determine the owner state");
        if (rc)
                return 0;
 
@@ -167,8 +167,8 @@ static ssize_t temp_deactivated_show(struct device *dev,
        cap_t cap;
        ssize_t rc;
 
-       rc = tpm_getcap(dev, TPM_CAP_FLAG_VOL, &cap,
-                        "attempting to determine the temporary state");
+       rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_VOL, &cap,
+                       "attempting to determine the temporary state");
        if (rc)
                return 0;
 
@@ -180,11 +180,12 @@ static DEVICE_ATTR_RO(temp_deactivated);
 static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
                         char *buf)
 {
+       struct tpm_chip *chip = to_tpm_chip(dev);
        cap_t cap;
        ssize_t rc;
        char *str = buf;
 
-       rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
+       rc = tpm_getcap(chip, TPM_CAP_PROP_MANUFACTURER, &cap,
                        "attempting to determine the manufacturer");
        if (rc)
                return 0;
@@ -192,8 +193,8 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
                       be32_to_cpu(cap.manufacturer_id));
 
        /* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */
-       rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap,
-                        "attempting to determine the 1.2 version");
+       rc = tpm_getcap(chip, CAP_VERSION_1_2, &cap,
+                       "attempting to determine the 1.2 version");
        if (!rc) {
                str += sprintf(str,
                               "TCG version: %d.%d\nFirmware version: %d.%d\n",
@@ -203,7 +204,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
                               cap.tpm_version_1_2.revMinor);
        } else {
                /* Otherwise just use TPM_STRUCT_VER */
-               rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap,
+               rc = tpm_getcap(chip, CAP_VERSION_1_1, &cap,
                                "attempting to determine the 1.1 version");
                if (rc)
                        return 0;
@@ -222,7 +223,7 @@ static DEVICE_ATTR_RO(caps);
 static ssize_t cancel_store(struct device *dev, struct device_attribute *attr,
                            const char *buf, size_t count)
 {
-       struct tpm_chip *chip = dev_get_drvdata(dev);
+       struct tpm_chip *chip = to_tpm_chip(dev);
        if (chip == NULL)
                return 0;
 
@@ -234,16 +235,16 @@ static DEVICE_ATTR_WO(cancel);
 static ssize_t durations_show(struct device *dev, struct device_attribute *attr,
                              char *buf)
 {
-       struct tpm_chip *chip = dev_get_drvdata(dev);
+       struct tpm_chip *chip = to_tpm_chip(dev);
 
-       if (chip->vendor.duration[TPM_LONG] == 0)
+       if (chip->duration[TPM_LONG] == 0)
                return 0;
 
        return sprintf(buf, "%d %d %d [%s]\n",
-                      jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
-                      jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
-                      jiffies_to_usecs(chip->vendor.duration[TPM_LONG]),
-                      chip->vendor.duration_adjusted
+                      jiffies_to_usecs(chip->duration[TPM_SHORT]),
+                      jiffies_to_usecs(chip->duration[TPM_MEDIUM]),
+                      jiffies_to_usecs(chip->duration[TPM_LONG]),
+                      chip->duration_adjusted
                       ? "adjusted" : "original");
 }
 static DEVICE_ATTR_RO(durations);
@@ -251,14 +252,14 @@ static DEVICE_ATTR_RO(durations);
 static ssize_t timeouts_show(struct device *dev, struct device_attribute *attr,
                             char *buf)
 {
-       struct tpm_chip *chip = dev_get_drvdata(dev);
+       struct tpm_chip *chip = to_tpm_chip(dev);
 
        return sprintf(buf, "%d %d %d %d [%s]\n",
-                      jiffies_to_usecs(chip->vendor.timeout_a),
-                      jiffies_to_usecs(chip->vendor.timeout_b),
-                      jiffies_to_usecs(chip->vendor.timeout_c),
-                      jiffies_to_usecs(chip->vendor.timeout_d),
-                      chip->vendor.timeout_adjusted
+                      jiffies_to_usecs(chip->timeout_a),
+                      jiffies_to_usecs(chip->timeout_b),
+                      jiffies_to_usecs(chip->timeout_c),
+                      jiffies_to_usecs(chip->timeout_d),
+                      chip->timeout_adjusted
                       ? "adjusted" : "original");
 }
 static DEVICE_ATTR_RO(timeouts);
@@ -281,19 +282,12 @@ static const struct attribute_group tpm_dev_group = {
        .attrs = tpm_dev_attrs,
 };
 
-int tpm_sysfs_add_device(struct tpm_chip *chip)
+void tpm_sysfs_add_device(struct tpm_chip *chip)
 {
-       int err;
-       err = sysfs_create_group(&chip->pdev->kobj,
-                                &tpm_dev_group);
-
-       if (err)
-               dev_err(chip->pdev,
-                       "failed to create sysfs attributes, %d\n", err);
-       return err;
-}
-
-void tpm_sysfs_del_device(struct tpm_chip *chip)
-{
-       sysfs_remove_group(&chip->pdev->kobj, &tpm_dev_group);
+       /* The sysfs routines rely on an implicit tpm_try_get_ops, device_del
+        * is called before ops is null'd and the sysfs core synchronizes this
+        * removal so that no callbacks are running or can run again
+        */
+       WARN_ON(chip->groups_cnt != 0);
+       chip->groups[chip->groups_cnt++] = &tpm_dev_group;
 }
index 28b477e8da6ac0ef7e1168c48f36322ad4ff8479..3e32d5bd2dc63054d644558283f453ab72061130 100644 (file)
  * License.
  *
  */
+
+#ifndef __TPM_H__
+#define __TPM_H__
+
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/fs.h>
@@ -34,7 +38,7 @@
 enum tpm_const {
        TPM_MINOR = 224,        /* officially assigned */
        TPM_BUFSIZE = 4096,
-       TPM_NUM_DEVICES = 256,
+       TPM_NUM_DEVICES = 65536,
        TPM_RETRY = 50,         /* 5 seconds */
 };
 
@@ -128,33 +132,6 @@ enum tpm2_startup_types {
        TPM2_SU_STATE   = 0x0001,
 };
 
-struct tpm_chip;
-
-struct tpm_vendor_specific {
-       void __iomem *iobase;           /* ioremapped address */
-       unsigned long base;             /* TPM base address */
-
-       int irq;
-
-       int region_size;
-       int have_region;
-
-       struct list_head list;
-       int locality;
-       unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */
-       bool timeout_adjusted;
-       unsigned long duration[3]; /* jiffies */
-       bool duration_adjusted;
-       void *priv;
-
-       wait_queue_head_t read_queue;
-       wait_queue_head_t int_queue;
-
-       u16 manufacturer_id;
-};
-
-#define TPM_VPRIV(c)     ((c)->vendor.priv)
-
 #define TPM_VID_INTEL    0x8086
 #define TPM_VID_WINBOND  0x1050
 #define TPM_VID_STM      0x104A
@@ -164,44 +141,48 @@ struct tpm_vendor_specific {
 enum tpm_chip_flags {
        TPM_CHIP_FLAG_REGISTERED        = BIT(0),
        TPM_CHIP_FLAG_TPM2              = BIT(1),
+       TPM_CHIP_FLAG_IRQ               = BIT(2),
+       TPM_CHIP_FLAG_VIRTUAL           = BIT(3),
 };
 
 struct tpm_chip {
-       struct device *pdev;    /* Device stuff */
        struct device dev;
        struct cdev cdev;
 
+       /* A driver callback under ops cannot be run unless ops_sem is held
+        * (sometimes implicitly, eg for the sysfs code). ops becomes null
+        * when the driver is unregistered, see tpm_try_get_ops.
+        */
+       struct rw_semaphore ops_sem;
        const struct tpm_class_ops *ops;
+
        unsigned int flags;
 
        int dev_num;            /* /dev/tpm# */
-       char devname[7];
        unsigned long is_open;  /* only one allowed */
-       int time_expired;
 
        struct mutex tpm_mutex; /* tpm is processing */
 
-       struct tpm_vendor_specific vendor;
+       unsigned long timeout_a; /* jiffies */
+       unsigned long timeout_b; /* jiffies */
+       unsigned long timeout_c; /* jiffies */
+       unsigned long timeout_d; /* jiffies */
+       bool timeout_adjusted;
+       unsigned long duration[3]; /* jiffies */
+       bool duration_adjusted;
 
        struct dentry **bios_dir;
 
-#ifdef CONFIG_ACPI
-       const struct attribute_group *groups[2];
+       const struct attribute_group *groups[3];
        unsigned int groups_cnt;
+#ifdef CONFIG_ACPI
        acpi_handle acpi_dev_handle;
        char ppi_version[TPM_PPI_VERSION_LEN + 1];
 #endif /* CONFIG_ACPI */
-
-       struct list_head list;
 };
 
 #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
 
-static inline void tpm_chip_put(struct tpm_chip *chip)
-{
-       module_put(chip->pdev->driver->owner);
-}
-
 static inline int tpm_read_index(int base, int index)
 {
        outb(index, base);
@@ -493,14 +474,17 @@ static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
 extern struct class *tpm_class;
 extern dev_t tpm_devt;
 extern const struct file_operations tpm_fops;
+extern struct idr dev_nums_idr;
 
-ssize_t        tpm_getcap(struct device *, __be32, cap_t *, const char *);
+ssize_t tpm_getcap(struct tpm_chip *chip, __be32 subcap_id, cap_t *cap,
+                  const char *desc);
 ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
                     size_t bufsiz);
 ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd, int len,
                         const char *desc);
 extern int tpm_get_timeouts(struct tpm_chip *);
 extern void tpm_gen_interrupt(struct tpm_chip *);
+int tpm1_auto_startup(struct tpm_chip *chip);
 extern int tpm_do_selftest(struct tpm_chip *);
 extern unsigned long tpm_calc_ordinal_duration(struct tpm_chip *, u32);
 extern int tpm_pm_suspend(struct device *);
@@ -509,13 +493,17 @@ extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
                             wait_queue_head_t *, bool);
 
 struct tpm_chip *tpm_chip_find_get(int chip_num);
-extern struct tpm_chip *tpmm_chip_alloc(struct device *dev,
+__must_check int tpm_try_get_ops(struct tpm_chip *chip);
+void tpm_put_ops(struct tpm_chip *chip);
+
+extern struct tpm_chip *tpm_chip_alloc(struct device *dev,
+                                      const struct tpm_class_ops *ops);
+extern struct tpm_chip *tpmm_chip_alloc(struct device *pdev,
                                       const struct tpm_class_ops *ops);
 extern int tpm_chip_register(struct tpm_chip *chip);
 extern void tpm_chip_unregister(struct tpm_chip *chip);
 
-int tpm_sysfs_add_device(struct tpm_chip *chip);
-void tpm_sysfs_del_device(struct tpm_chip *chip);
+void tpm_sysfs_add_device(struct tpm_chip *chip);
 
 int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf);
 
@@ -539,9 +527,9 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
 ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
                        u32 *value, const char *desc);
 
-extern int tpm2_startup(struct tpm_chip *chip, u16 startup_type);
+int tpm2_auto_startup(struct tpm_chip *chip);
 extern void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type);
 extern unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *, u32);
-extern int tpm2_do_selftest(struct tpm_chip *chip);
 extern int tpm2_gen_interrupt(struct tpm_chip *chip);
 extern int tpm2_probe(struct tpm_chip *chip);
+#endif
index b28e4da3d2cfff2f5a73b49be30b5f359557b553..08c7e23ed535bd665d3a9a6db68293d2ae93860b 100644 (file)
@@ -597,7 +597,7 @@ static void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
 
        rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
        if (rc) {
-               dev_warn(chip->pdev, "0x%08x was not flushed, out of memory\n",
+               dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
                         handle);
                return;
        }
@@ -606,7 +606,7 @@ static void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
 
        rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "flushing context");
        if (rc)
-               dev_warn(chip->pdev, "0x%08x was not flushed, rc=%d\n", handle,
+               dev_warn(&chip->dev, "0x%08x was not flushed, rc=%d\n", handle,
                         rc);
 
        tpm_buf_destroy(&buf);
@@ -703,7 +703,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,  u32 *value,
 
        rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), desc);
        if (!rc)
-               *value = cmd.params.get_tpm_pt_out.value;
+               *value = be32_to_cpu(cmd.params.get_tpm_pt_out.value);
 
        return rc;
 }
@@ -728,7 +728,7 @@ static const struct tpm_input_header tpm2_startup_header = {
  * returned it remarks a POSIX error code. If a positive number is returned
  * it remarks a TPM error.
  */
-int tpm2_startup(struct tpm_chip *chip, u16 startup_type)
+static int tpm2_startup(struct tpm_chip *chip, u16 startup_type)
 {
        struct tpm2_cmd cmd;
 
@@ -738,7 +738,6 @@ int tpm2_startup(struct tpm_chip *chip, u16 startup_type)
        return tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
                                "attempting to start the TPM");
 }
-EXPORT_SYMBOL_GPL(tpm2_startup);
 
 #define TPM2_SHUTDOWN_IN_SIZE \
        (sizeof(struct tpm_input_header) + \
@@ -770,10 +769,9 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
         * except print the error code on a system failure.
         */
        if (rc < 0)
-               dev_warn(chip->pdev, "transmit returned %d while stopping the TPM",
+               dev_warn(&chip->dev, "transmit returned %d while stopping the TPM",
                         rc);
 }
-EXPORT_SYMBOL_GPL(tpm2_shutdown);
 
 /*
  * tpm2_calc_ordinal_duration() - maximum duration for a command
@@ -793,7 +791,7 @@ unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
                index = tpm2_ordinal_duration[ordinal - TPM2_CC_FIRST];
 
        if (index != TPM_UNDEFINED)
-               duration = chip->vendor.duration[index];
+               duration = chip->duration[index];
 
        if (duration <= 0)
                duration = 2 * 60 * HZ;
@@ -837,7 +835,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full)
         * immediately. This is a workaround for that.
         */
        if (rc == TPM2_RC_TESTING) {
-               dev_warn(chip->pdev, "Got RC_TESTING, ignoring\n");
+               dev_warn(&chip->dev, "Got RC_TESTING, ignoring\n");
                rc = 0;
        }
 
@@ -855,7 +853,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full)
  * returned it remarks a POSIX error code. If a positive number is returned
  * it remarks a TPM error.
  */
-int tpm2_do_selftest(struct tpm_chip *chip)
+static int tpm2_do_selftest(struct tpm_chip *chip)
 {
        int rc;
        unsigned int loops;
@@ -895,7 +893,6 @@ int tpm2_do_selftest(struct tpm_chip *chip)
 
        return rc;
 }
-EXPORT_SYMBOL_GPL(tpm2_do_selftest);
 
 /**
  * tpm2_gen_interrupt() - generate an interrupt
@@ -943,3 +940,43 @@ int tpm2_probe(struct tpm_chip *chip)
        return 0;
 }
 EXPORT_SYMBOL_GPL(tpm2_probe);
+
+/**
+ * tpm2_auto_startup - Perform the standard automatic TPM initialization
+ *                     sequence
+ * @chip: TPM chip to use
+ *
+ * Returns 0 on success, < 0 in case of fatal error.
+ */
+int tpm2_auto_startup(struct tpm_chip *chip)
+{
+       int rc;
+
+       rc = tpm_get_timeouts(chip);
+       if (rc)
+               goto out;
+
+       rc = tpm2_do_selftest(chip);
+       if (rc != TPM2_RC_INITIALIZE) {
+               dev_err(&chip->dev, "TPM self test failed\n");
+               goto out;
+       }
+
+       if (rc == TPM2_RC_INITIALIZE) {
+               rc = tpm2_startup(chip, TPM2_SU_CLEAR);
+               if (rc)
+                       goto out;
+
+               rc = tpm2_do_selftest(chip);
+               if (rc) {
+                       dev_err(&chip->dev, "TPM self test failed\n");
+                       goto out;
+               }
+       }
+
+       return rc;
+out:
+       if (rc > 0)
+               rc = -ENODEV;
+       return rc;
+}
index dfadad0916a1e8685ca743713d6cae168c947af0..0d322ab11faabcbae304f9611665873900778962 100644 (file)
@@ -37,6 +37,7 @@ enum tpm_atmel_read_status {
 
 static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 {
+       struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
        u8 status, *hdr = buf;
        u32 size;
        int i;
@@ -47,12 +48,12 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
                return -EIO;
 
        for (i = 0; i < 6; i++) {
-               status = ioread8(chip->vendor.iobase + 1);
+               status = ioread8(priv->iobase + 1);
                if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
-                       dev_err(chip->pdev, "error reading header\n");
+                       dev_err(&chip->dev, "error reading header\n");
                        return -EIO;
                }
-               *buf++ = ioread8(chip->vendor.iobase);
+               *buf++ = ioread8(priv->iobase);
        }
 
        /* size of the data received */
@@ -60,12 +61,12 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
        size = be32_to_cpu(*native_size);
 
        if (count < size) {
-               dev_err(chip->pdev,
+               dev_err(&chip->dev,
                        "Recv size(%d) less than available space\n", size);
                for (; i < size; i++) { /* clear the waiting data anyway */
-                       status = ioread8(chip->vendor.iobase + 1);
+                       status = ioread8(priv->iobase + 1);
                        if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
-                               dev_err(chip->pdev, "error reading data\n");
+                               dev_err(&chip->dev, "error reading data\n");
                                return -EIO;
                        }
                }
@@ -74,19 +75,19 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 
        /* read all the data available */
        for (; i < size; i++) {
-               status = ioread8(chip->vendor.iobase + 1);
+               status = ioread8(priv->iobase + 1);
                if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
-                       dev_err(chip->pdev, "error reading data\n");
+                       dev_err(&chip->dev, "error reading data\n");
                        return -EIO;
                }
-               *buf++ = ioread8(chip->vendor.iobase);
+               *buf++ = ioread8(priv->iobase);
        }
 
        /* make sure data available is gone */
-       status = ioread8(chip->vendor.iobase + 1);
+       status = ioread8(priv->iobase + 1);
 
        if (status & ATML_STATUS_DATA_AVAIL) {
-               dev_err(chip->pdev, "data available is stuck\n");
+               dev_err(&chip->dev, "data available is stuck\n");
                return -EIO;
        }
 
@@ -95,12 +96,13 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 
 static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count)
 {
+       struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
        int i;
 
-       dev_dbg(chip->pdev, "tpm_atml_send:\n");
+       dev_dbg(&chip->dev, "tpm_atml_send:\n");
        for (i = 0; i < count; i++) {
-               dev_dbg(chip->pdev, "%d 0x%x(%d)\n",  i, buf[i], buf[i]);
-               iowrite8(buf[i], chip->vendor.iobase);
+               dev_dbg(&chip->dev, "%d 0x%x(%d)\n",  i, buf[i], buf[i]);
+               iowrite8(buf[i], priv->iobase);
        }
 
        return count;
@@ -108,12 +110,16 @@ static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count)
 
 static void tpm_atml_cancel(struct tpm_chip *chip)
 {
-       iowrite8(ATML_STATUS_ABORT, chip->vendor.iobase + 1);
+       struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
+
+       iowrite8(ATML_STATUS_ABORT, priv->iobase + 1);
 }
 
 static u8 tpm_atml_status(struct tpm_chip *chip)
 {
-       return ioread8(chip->vendor.iobase + 1);
+       struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
+
+       return ioread8(priv->iobase + 1);
 }
 
 static bool tpm_atml_req_canceled(struct tpm_chip *chip, u8 status)
@@ -136,13 +142,13 @@ static struct platform_device *pdev;
 static void atml_plat_remove(void)
 {
        struct tpm_chip *chip = dev_get_drvdata(&pdev->dev);
+       struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
 
        if (chip) {
                tpm_chip_unregister(chip);
-               if (chip->vendor.have_region)
-                       atmel_release_region(chip->vendor.base,
-                                            chip->vendor.region_size);
-               atmel_put_base_addr(chip->vendor.iobase);
+               if (priv->have_region)
+                       atmel_release_region(priv->base, priv->region_size);
+               atmel_put_base_addr(priv->iobase);
                platform_device_unregister(pdev);
        }
 }
@@ -163,6 +169,7 @@ static int __init init_atmel(void)
        int have_region, region_size;
        unsigned long base;
        struct  tpm_chip *chip;
+       struct tpm_atmel_priv *priv;
 
        rc = platform_driver_register(&atml_drv);
        if (rc)
@@ -183,16 +190,24 @@ static int __init init_atmel(void)
                goto err_rel_reg;
        }
 
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               rc = -ENOMEM;
+               goto err_unreg_dev;
+       }
+
+       priv->iobase = iobase;
+       priv->base = base;
+       priv->have_region = have_region;
+       priv->region_size = region_size;
+
        chip = tpmm_chip_alloc(&pdev->dev, &tpm_atmel);
        if (IS_ERR(chip)) {
                rc = PTR_ERR(chip);
                goto err_unreg_dev;
        }
 
-       chip->vendor.iobase = iobase;
-       chip->vendor.base = base;
-       chip->vendor.have_region = have_region;
-       chip->vendor.region_size = region_size;
+       dev_set_drvdata(&chip->dev, priv);
 
        rc = tpm_chip_register(chip);
        if (rc)
index 6c831f9466b70c6e4399a09bc89278aed2eeaa7e..4f96d80cdce997f1a031a323b70ba60d6b1d0954 100644 (file)
  *
  */
 
+struct tpm_atmel_priv {
+       int region_size;
+       int have_region;
+       unsigned long base;
+       void __iomem *iobase;
+};
+
 #ifdef CONFIG_PPC64
 
 #include <asm/prom.h>
 
-#define atmel_getb(chip, offset) readb(chip->vendor->iobase + offset);
-#define atmel_putb(val, chip, offset) writeb(val, chip->vendor->iobase + offset)
+#define atmel_getb(priv, offset) readb(priv->iobase + offset)
+#define atmel_putb(val, priv, offset) writeb(val, priv->iobase + offset)
 #define atmel_request_region request_mem_region
 #define atmel_release_region release_mem_region
 
@@ -78,8 +85,9 @@ static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size)
        return ioremap(*base, *region_size);
 }
 #else
-#define atmel_getb(chip, offset) inb(chip->vendor->base + offset)
-#define atmel_putb(val, chip, offset) outb(val, chip->vendor->base + offset)
+#define atmel_getb(chip, offset) inb(atmel_get_priv(chip)->base + offset)
+#define atmel_putb(val, chip, offset) \
+       outb(val, atmel_get_priv(chip)->base + offset)
 #define atmel_request_region request_region
 #define atmel_release_region release_region
 /* Atmel definitions */
index a12b3194034442ad4c5f87d9c6c6999d954edff5..018c382554ba2cefcf8cd11fff52ec9634c35495 100644 (file)
@@ -77,7 +77,6 @@ enum crb_flags {
 
 struct crb_priv {
        unsigned int flags;
-       struct resource res;
        void __iomem *iobase;
        struct crb_control_area __iomem *cca;
        u8 __iomem *cmd;
@@ -88,7 +87,7 @@ static SIMPLE_DEV_PM_OPS(crb_pm, tpm_pm_suspend, tpm_pm_resume);
 
 static u8 crb_status(struct tpm_chip *chip)
 {
-       struct crb_priv *priv = chip->vendor.priv;
+       struct crb_priv *priv = dev_get_drvdata(&chip->dev);
        u8 sts = 0;
 
        if ((ioread32(&priv->cca->start) & CRB_START_INVOKE) !=
@@ -100,7 +99,7 @@ static u8 crb_status(struct tpm_chip *chip)
 
 static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 {
-       struct crb_priv *priv = chip->vendor.priv;
+       struct crb_priv *priv = dev_get_drvdata(&chip->dev);
        unsigned int expected;
 
        /* sanity check */
@@ -140,7 +139,7 @@ static int crb_do_acpi_start(struct tpm_chip *chip)
 
 static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
 {
-       struct crb_priv *priv = chip->vendor.priv;
+       struct crb_priv *priv = dev_get_drvdata(&chip->dev);
        int rc = 0;
 
        if (len > ioread32(&priv->cca->cmd_size)) {
@@ -167,7 +166,7 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
 
 static void crb_cancel(struct tpm_chip *chip)
 {
-       struct crb_priv *priv = chip->vendor.priv;
+       struct crb_priv *priv = dev_get_drvdata(&chip->dev);
 
        iowrite32(cpu_to_le32(CRB_CANCEL_INVOKE), &priv->cca->cancel);
 
@@ -182,13 +181,14 @@ static void crb_cancel(struct tpm_chip *chip)
 
 static bool crb_req_canceled(struct tpm_chip *chip, u8 status)
 {
-       struct crb_priv *priv = chip->vendor.priv;
+       struct crb_priv *priv = dev_get_drvdata(&chip->dev);
        u32 cancel = ioread32(&priv->cca->cancel);
 
        return (cancel & CRB_CANCEL_INVOKE) == CRB_CANCEL_INVOKE;
 }
 
 static const struct tpm_class_ops tpm_crb = {
+       .flags = TPM_OPS_AUTO_STARTUP,
        .status = crb_status,
        .recv = crb_recv,
        .send = crb_send,
@@ -201,42 +201,33 @@ static const struct tpm_class_ops tpm_crb = {
 static int crb_init(struct acpi_device *device, struct crb_priv *priv)
 {
        struct tpm_chip *chip;
-       int rc;
 
        chip = tpmm_chip_alloc(&device->dev, &tpm_crb);
        if (IS_ERR(chip))
                return PTR_ERR(chip);
 
-       chip->vendor.priv = priv;
+       dev_set_drvdata(&chip->dev, priv);
        chip->acpi_dev_handle = device->handle;
        chip->flags = TPM_CHIP_FLAG_TPM2;
 
-       rc = tpm_get_timeouts(chip);
-       if (rc)
-               return rc;
-
-       rc = tpm2_do_selftest(chip);
-       if (rc)
-               return rc;
-
        return tpm_chip_register(chip);
 }
 
 static int crb_check_resource(struct acpi_resource *ares, void *data)
 {
-       struct crb_priv *priv = data;
+       struct resource *io_res = data;
        struct resource res;
 
        if (acpi_dev_resource_memory(ares, &res)) {
-               priv->res = res;
-               priv->res.name = NULL;
+               *io_res = res;
+               io_res->name = NULL;
        }
 
        return 1;
 }
 
 static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv,
-                                u64 start, u32 size)
+                                struct resource *io_res, u64 start, u32 size)
 {
        struct resource new_res = {
                .start  = start,
@@ -246,53 +237,74 @@ static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv,
 
        /* Detect a 64 bit address on a 32 bit system */
        if (start != new_res.start)
-               return ERR_PTR(-EINVAL);
+               return (void __iomem *) ERR_PTR(-EINVAL);
 
-       if (!resource_contains(&priv->res, &new_res))
+       if (!resource_contains(io_res, &new_res))
                return devm_ioremap_resource(dev, &new_res);
 
-       return priv->iobase + (new_res.start - priv->res.start);
+       return priv->iobase + (new_res.start - io_res->start);
 }
 
 static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
                      struct acpi_table_tpm2 *buf)
 {
        struct list_head resources;
+       struct resource io_res;
        struct device *dev = &device->dev;
-       u64 pa;
+       u64 cmd_pa;
+       u32 cmd_size;
+       u64 rsp_pa;
+       u32 rsp_size;
        int ret;
 
        INIT_LIST_HEAD(&resources);
        ret = acpi_dev_get_resources(device, &resources, crb_check_resource,
-                                    priv);
+                                    &io_res);
        if (ret < 0)
                return ret;
        acpi_dev_free_resource_list(&resources);
 
-       if (resource_type(&priv->res) != IORESOURCE_MEM) {
+       if (resource_type(&io_res) != IORESOURCE_MEM) {
                dev_err(dev,
                        FW_BUG "TPM2 ACPI table does not define a memory resource\n");
                return -EINVAL;
        }
 
-       priv->iobase = devm_ioremap_resource(dev, &priv->res);
+       priv->iobase = devm_ioremap_resource(dev, &io_res);
        if (IS_ERR(priv->iobase))
                return PTR_ERR(priv->iobase);
 
-       priv->cca = crb_map_res(dev, priv, buf->control_address, 0x1000);
+       priv->cca = crb_map_res(dev, priv, &io_res, buf->control_address,
+                               sizeof(struct crb_control_area));
        if (IS_ERR(priv->cca))
                return PTR_ERR(priv->cca);
 
-       pa = ((u64) ioread32(&priv->cca->cmd_pa_high) << 32) |
-             (u64) ioread32(&priv->cca->cmd_pa_low);
-       priv->cmd = crb_map_res(dev, priv, pa, ioread32(&priv->cca->cmd_size));
+       cmd_pa = ((u64) ioread32(&priv->cca->cmd_pa_high) << 32) |
+                 (u64) ioread32(&priv->cca->cmd_pa_low);
+       cmd_size = ioread32(&priv->cca->cmd_size);
+       priv->cmd = crb_map_res(dev, priv, &io_res, cmd_pa, cmd_size);
        if (IS_ERR(priv->cmd))
                return PTR_ERR(priv->cmd);
 
-       memcpy_fromio(&pa, &priv->cca->rsp_pa, 8);
-       pa = le64_to_cpu(pa);
-       priv->rsp = crb_map_res(dev, priv, pa, ioread32(&priv->cca->rsp_size));
-       return PTR_ERR_OR_ZERO(priv->rsp);
+       memcpy_fromio(&rsp_pa, &priv->cca->rsp_pa, 8);
+       rsp_pa = le64_to_cpu(rsp_pa);
+       rsp_size = ioread32(&priv->cca->rsp_size);
+
+       if (cmd_pa != rsp_pa) {
+               priv->rsp = crb_map_res(dev, priv, &io_res, rsp_pa, rsp_size);
+               return PTR_ERR_OR_ZERO(priv->rsp);
+       }
+
+       /* According to the PTP specification, overlapping command and response
+        * buffer sizes must be identical.
+        */
+       if (cmd_size != rsp_size) {
+               dev_err(dev, FW_BUG "overlapping command and response buffer sizes are not identical");
+               return -EINVAL;
+       }
+
+       priv->rsp = priv->cmd;
+       return 0;
 }
 
 static int crb_acpi_add(struct acpi_device *device)
@@ -344,9 +356,6 @@ static int crb_acpi_remove(struct acpi_device *device)
        struct device *dev = &device->dev;
        struct tpm_chip *chip = dev_get_drvdata(dev);
 
-       if (chip->flags & TPM_CHIP_FLAG_TPM2)
-               tpm2_shutdown(chip, TPM2_SU_CLEAR);
-
        tpm_chip_unregister(chip);
 
        return 0;
index 4e6940acf639b00333baed9660567d81e4b75bf0..e7228863290edea53508fd597ff6855b98c1440c 100644 (file)
@@ -403,7 +403,7 @@ static int is_bad(void *p)
        return 0;
 }
 
-struct dentry **tpm_bios_log_setup(char *name)
+struct dentry **tpm_bios_log_setup(const char *name)
 {
        struct dentry **ret = NULL, *tpm_dir, *bin_file, *ascii_file;
 
index 267bfbd1b7bbe7addf4dcd029e8c21df3a731859..8de62b09be51915b6f95a86733488162aa4b1e23 100644 (file)
@@ -77,10 +77,10 @@ int read_log(struct tpm_bios_log *log);
 
 #if defined(CONFIG_TCG_IBMVTPM) || defined(CONFIG_TCG_IBMVTPM_MODULE) || \
        defined(CONFIG_ACPI)
-extern struct dentry **tpm_bios_log_setup(char *);
+extern struct dentry **tpm_bios_log_setup(const char *);
 extern void tpm_bios_log_teardown(struct dentry **);
 #else
-static inline struct dentry **tpm_bios_log_setup(char *name)
+static inline struct dentry **tpm_bios_log_setup(const char *name)
 {
        return NULL;
 }
index 8dfb88b9739c197f01eb538157e450c67323debc..95ce2e9ccdc6e2ec40c7d52b0f35d17ba260001e 100644 (file)
@@ -51,8 +51,8 @@ struct priv_data {
 
 static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
 {
-       struct priv_data *priv = chip->vendor.priv;
-       struct i2c_client *client = to_i2c_client(chip->pdev);
+       struct priv_data *priv = dev_get_drvdata(&chip->dev);
+       struct i2c_client *client = to_i2c_client(chip->dev.parent);
        s32 status;
 
        priv->len = 0;
@@ -62,7 +62,7 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
 
        status = i2c_master_send(client, buf, len);
 
-       dev_dbg(chip->pdev,
+       dev_dbg(&chip->dev,
                "%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__,
                (int)min_t(size_t, 64, len), buf, len, status);
        return status;
@@ -70,8 +70,8 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
 
 static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 {
-       struct priv_data *priv = chip->vendor.priv;
-       struct i2c_client *client = to_i2c_client(chip->pdev);
+       struct priv_data *priv = dev_get_drvdata(&chip->dev);
+       struct i2c_client *client = to_i2c_client(chip->dev.parent);
        struct tpm_output_header *hdr =
                (struct tpm_output_header *)priv->buffer;
        u32 expected_len;
@@ -88,7 +88,7 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
                return -ENOMEM;
 
        if (priv->len >= expected_len) {
-               dev_dbg(chip->pdev,
+               dev_dbg(&chip->dev,
                        "%s early(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
                        (int)min_t(size_t, 64, expected_len), buf, count,
                        expected_len);
@@ -97,7 +97,7 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
        }
 
        rc = i2c_master_recv(client, buf, expected_len);
-       dev_dbg(chip->pdev,
+       dev_dbg(&chip->dev,
                "%s reread(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
                (int)min_t(size_t, 64, expected_len), buf, count,
                expected_len);
@@ -106,13 +106,13 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 
 static void i2c_atmel_cancel(struct tpm_chip *chip)
 {
-       dev_err(chip->pdev, "TPM operation cancellation was requested, but is not supported");
+       dev_err(&chip->dev, "TPM operation cancellation was requested, but is not supported");
 }
 
 static u8 i2c_atmel_read_status(struct tpm_chip *chip)
 {
-       struct priv_data *priv = chip->vendor.priv;
-       struct i2c_client *client = to_i2c_client(chip->pdev);
+       struct priv_data *priv = dev_get_drvdata(&chip->dev);
+       struct i2c_client *client = to_i2c_client(chip->dev.parent);
        int rc;
 
        /* The TPM fails the I2C read until it is ready, so we do the entire
@@ -125,7 +125,7 @@ static u8 i2c_atmel_read_status(struct tpm_chip *chip)
        /* Once the TPM has completed the command the command remains readable
         * until another command is issued. */
        rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer));
-       dev_dbg(chip->pdev,
+       dev_dbg(&chip->dev,
                "%s: sts=%d", __func__, rc);
        if (rc <= 0)
                return 0;
@@ -141,6 +141,7 @@ static bool i2c_atmel_req_canceled(struct tpm_chip *chip, u8 status)
 }
 
 static const struct tpm_class_ops i2c_atmel = {
+       .flags = TPM_OPS_AUTO_STARTUP,
        .status = i2c_atmel_read_status,
        .recv = i2c_atmel_recv,
        .send = i2c_atmel_send,
@@ -155,6 +156,7 @@ static int i2c_atmel_probe(struct i2c_client *client,
 {
        struct tpm_chip *chip;
        struct device *dev = &client->dev;
+       struct priv_data *priv;
 
        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
                return -ENODEV;
@@ -163,26 +165,21 @@ static int i2c_atmel_probe(struct i2c_client *client,
        if (IS_ERR(chip))
                return PTR_ERR(chip);
 
-       chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data),
-                                        GFP_KERNEL);
-       if (!chip->vendor.priv)
+       priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL);
+       if (!priv)
                return -ENOMEM;
 
        /* Default timeouts */
-       chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
-       chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
-       chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
-       chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
-       chip->vendor.irq = 0;
+       chip->timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+       chip->timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
+       chip->timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+       chip->timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+
+       dev_set_drvdata(&chip->dev, priv);
 
        /* There is no known way to probe for this device, and all version
         * information seems to be read via TPM commands. Thus we rely on the
         * TPM startup process in the common code to detect the device. */
-       if (tpm_get_timeouts(chip))
-               return -ENODEV;
-
-       if (tpm_do_selftest(chip))
-               return -ENODEV;
 
        return tpm_chip_register(chip);
 }
index 63d5d22e9e600c2e7dd1b4b0e83df61f36b6a2e5..62ee44e57ddc4478d41d9b2082d0e4e27df57b04 100644 (file)
@@ -66,6 +66,7 @@ enum i2c_chip_type {
 /* Structure to store I2C TPM specific stuff */
 struct tpm_inf_dev {
        struct i2c_client *client;
+       int locality;
        u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */
        struct tpm_chip *chip;
        enum i2c_chip_type chip_type;
@@ -288,7 +289,7 @@ static int check_locality(struct tpm_chip *chip, int loc)
 
        if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
            (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
-               chip->vendor.locality = loc;
+               tpm_dev.locality = loc;
                return loc;
        }
 
@@ -320,7 +321,7 @@ static int request_locality(struct tpm_chip *chip, int loc)
        iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
 
        /* wait for burstcount */
-       stop = jiffies + chip->vendor.timeout_a;
+       stop = jiffies + chip->timeout_a;
        do {
                if (check_locality(chip, loc) >= 0)
                        return loc;
@@ -337,7 +338,7 @@ static u8 tpm_tis_i2c_status(struct tpm_chip *chip)
        u8 i = 0;
 
        do {
-               if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0)
+               if (iic_tpm_read(TPM_STS(tpm_dev.locality), &buf, 1) < 0)
                        return 0;
 
                i++;
@@ -351,7 +352,7 @@ static void tpm_tis_i2c_ready(struct tpm_chip *chip)
 {
        /* this causes the current command to be aborted */
        u8 buf = TPM_STS_COMMAND_READY;
-       iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1);
+       iic_tpm_write_long(TPM_STS(tpm_dev.locality), &buf, 1);
 }
 
 static ssize_t get_burstcount(struct tpm_chip *chip)
@@ -362,10 +363,10 @@ static ssize_t get_burstcount(struct tpm_chip *chip)
 
        /* wait for burstcount */
        /* which timeout value, spec has 2 answers (c & d) */
-       stop = jiffies + chip->vendor.timeout_d;
+       stop = jiffies + chip->timeout_d;
        do {
                /* Note: STS is little endian */
-               if (iic_tpm_read(TPM_STS(chip->vendor.locality)+1, buf, 3) < 0)
+               if (iic_tpm_read(TPM_STS(tpm_dev.locality)+1, buf, 3) < 0)
                        burstcnt = 0;
                else
                        burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0];
@@ -419,7 +420,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
                if (burstcnt > (count - size))
                        burstcnt = count - size;
 
-               rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality),
+               rc = iic_tpm_read(TPM_DATA_FIFO(tpm_dev.locality),
                                  &(buf[size]), burstcnt);
                if (rc == 0)
                        size += burstcnt;
@@ -446,7 +447,7 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
        /* read first 10 bytes, including tag, paramsize, and result */
        size = recv_data(chip, buf, TPM_HEADER_SIZE);
        if (size < TPM_HEADER_SIZE) {
-               dev_err(chip->pdev, "Unable to read header\n");
+               dev_err(&chip->dev, "Unable to read header\n");
                goto out;
        }
 
@@ -459,14 +460,14 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
        size += recv_data(chip, &buf[TPM_HEADER_SIZE],
                          expected - TPM_HEADER_SIZE);
        if (size < expected) {
-               dev_err(chip->pdev, "Unable to read remainder of result\n");
+               dev_err(&chip->dev, "Unable to read remainder of result\n");
                size = -ETIME;
                goto out;
        }
 
-       wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
+       wait_for_stat(chip, TPM_STS_VALID, chip->timeout_c, &status);
        if (status & TPM_STS_DATA_AVAIL) {      /* retry? */
-               dev_err(chip->pdev, "Error left over data\n");
+               dev_err(&chip->dev, "Error left over data\n");
                size = -EIO;
                goto out;
        }
@@ -477,7 +478,7 @@ out:
         * so we sleep rather than keeping the bus busy
         */
        usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
-       release_locality(chip, chip->vendor.locality, 0);
+       release_locality(chip, tpm_dev.locality, 0);
        return size;
 }
 
@@ -500,7 +501,7 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
                tpm_tis_i2c_ready(chip);
                if (wait_for_stat
                    (chip, TPM_STS_COMMAND_READY,
-                    chip->vendor.timeout_b, &status) < 0) {
+                    chip->timeout_b, &status) < 0) {
                        rc = -ETIME;
                        goto out_err;
                }
@@ -516,7 +517,7 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
                if (burstcnt > (len - 1 - count))
                        burstcnt = len - 1 - count;
 
-               rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality),
+               rc = iic_tpm_write(TPM_DATA_FIFO(tpm_dev.locality),
                                   &(buf[count]), burstcnt);
                if (rc == 0)
                        count += burstcnt;
@@ -530,7 +531,7 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
                }
 
                wait_for_stat(chip, TPM_STS_VALID,
-                             chip->vendor.timeout_c, &status);
+                             chip->timeout_c, &status);
 
                if ((status & TPM_STS_DATA_EXPECT) == 0) {
                        rc = -EIO;
@@ -539,15 +540,15 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
        }
 
        /* write last byte */
-       iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1);
-       wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
+       iic_tpm_write(TPM_DATA_FIFO(tpm_dev.locality), &(buf[count]), 1);
+       wait_for_stat(chip, TPM_STS_VALID, chip->timeout_c, &status);
        if ((status & TPM_STS_DATA_EXPECT) != 0) {
                rc = -EIO;
                goto out_err;
        }
 
        /* go and do it */
-       iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1);
+       iic_tpm_write(TPM_STS(tpm_dev.locality), &sts, 1);
 
        return len;
 out_err:
@@ -556,7 +557,7 @@ out_err:
         * so we sleep rather than keeping the bus busy
         */
        usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
-       release_locality(chip, chip->vendor.locality, 0);
+       release_locality(chip, tpm_dev.locality, 0);
        return rc;
 }
 
@@ -566,6 +567,7 @@ static bool tpm_tis_i2c_req_canceled(struct tpm_chip *chip, u8 status)
 }
 
 static const struct tpm_class_ops tpm_tis_i2c = {
+       .flags = TPM_OPS_AUTO_STARTUP,
        .status = tpm_tis_i2c_status,
        .recv = tpm_tis_i2c_recv,
        .send = tpm_tis_i2c_send,
@@ -585,14 +587,11 @@ static int tpm_tis_i2c_init(struct device *dev)
        if (IS_ERR(chip))
                return PTR_ERR(chip);
 
-       /* Disable interrupts */
-       chip->vendor.irq = 0;
-
        /* Default timeouts */
-       chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
-       chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
-       chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
-       chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+       chip->timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+       chip->timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
+       chip->timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+       chip->timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
 
        if (request_locality(chip, 0) != 0) {
                dev_err(dev, "could not request locality\n");
@@ -619,15 +618,11 @@ static int tpm_tis_i2c_init(struct device *dev)
 
        dev_info(dev, "1.2 TPM (device-id 0x%X)\n", vendor >> 16);
 
-       INIT_LIST_HEAD(&chip->vendor.list);
        tpm_dev.chip = chip;
 
-       tpm_get_timeouts(chip);
-       tpm_do_selftest(chip);
-
        return tpm_chip_register(chip);
 out_release:
-       release_locality(chip, chip->vendor.locality, 1);
+       release_locality(chip, tpm_dev.locality, 1);
        tpm_dev.client = NULL;
 out_err:
        return rc;
@@ -699,7 +694,7 @@ static int tpm_tis_i2c_remove(struct i2c_client *client)
        struct tpm_chip *chip = tpm_dev.chip;
 
        tpm_chip_unregister(chip);
-       release_locality(chip, chip->vendor.locality, 1);
+       release_locality(chip, tpm_dev.locality, 1);
        tpm_dev.client = NULL;
 
        return 0;
index 847f1597fe9b283557e45f5598da955a2dbb7f8f..e3a9155ee671ff3b0348a1c9ac28cfb4692eacb1 100644 (file)
@@ -1,5 +1,5 @@
-/******************************************************************************
- * Nuvoton TPM I2C Device Driver Interface for WPCT301/NPCT501,
+ /******************************************************************************
+ * Nuvoton TPM I2C Device Driver Interface for WPCT301/NPCT501/NPCT6XX,
  * based on the TCG TPM Interface Spec version 1.2.
  * Specifications at www.trustedcomputinggroup.org
  *
@@ -31,6 +31,7 @@
 #include <linux/interrupt.h>
 #include <linux/wait.h>
 #include <linux/i2c.h>
+#include <linux/of_device.h>
 #include "tpm.h"
 
 /* I2C interface offsets */
 #define TPM_I2C_RETRY_DELAY_SHORT      2       /* msec */
 #define TPM_I2C_RETRY_DELAY_LONG       10      /* msec */
 
-#define I2C_DRIVER_NAME "tpm_i2c_nuvoton"
+#define OF_IS_TPM2 ((void *)1)
+#define I2C_IS_TPM2 1
 
 struct priv_data {
+       int irq;
        unsigned int intrs;
+       wait_queue_head_t read_queue;
 };
 
 static s32 i2c_nuvoton_read_buf(struct i2c_client *client, u8 offset, u8 size,
@@ -96,13 +100,13 @@ static s32 i2c_nuvoton_write_buf(struct i2c_client *client, u8 offset, u8 size,
 /* read TPM_STS register */
 static u8 i2c_nuvoton_read_status(struct tpm_chip *chip)
 {
-       struct i2c_client *client = to_i2c_client(chip->pdev);
+       struct i2c_client *client = to_i2c_client(chip->dev.parent);
        s32 status;
        u8 data;
 
        status = i2c_nuvoton_read_buf(client, TPM_STS, 1, &data);
        if (status <= 0) {
-               dev_err(chip->pdev, "%s() error return %d\n", __func__,
+               dev_err(&chip->dev, "%s() error return %d\n", __func__,
                        status);
                data = TPM_STS_ERR_VAL;
        }
@@ -127,13 +131,13 @@ static s32 i2c_nuvoton_write_status(struct i2c_client *client, u8 data)
 /* write commandReady to TPM_STS register */
 static void i2c_nuvoton_ready(struct tpm_chip *chip)
 {
-       struct i2c_client *client = to_i2c_client(chip->pdev);
+       struct i2c_client *client = to_i2c_client(chip->dev.parent);
        s32 status;
 
        /* this causes the current command to be aborted */
        status = i2c_nuvoton_write_status(client, TPM_STS_COMMAND_READY);
        if (status < 0)
-               dev_err(chip->pdev,
+               dev_err(&chip->dev,
                        "%s() fail to write TPM_STS.commandReady\n", __func__);
 }
 
@@ -142,7 +146,7 @@ static void i2c_nuvoton_ready(struct tpm_chip *chip)
 static int i2c_nuvoton_get_burstcount(struct i2c_client *client,
                                      struct tpm_chip *chip)
 {
-       unsigned long stop = jiffies + chip->vendor.timeout_d;
+       unsigned long stop = jiffies + chip->timeout_d;
        s32 status;
        int burst_count = -1;
        u8 data;
@@ -163,7 +167,7 @@ static int i2c_nuvoton_get_burstcount(struct i2c_client *client,
 }
 
 /*
- * WPCT301/NPCT501 SINT# supports only dataAvail
+ * WPCT301/NPCT501/NPCT6XX SINT# supports only dataAvail
  * any call to this function which is not waiting for dataAvail will
  * set queue to NULL to avoid waiting for interrupt
  */
@@ -176,12 +180,12 @@ static bool i2c_nuvoton_check_status(struct tpm_chip *chip, u8 mask, u8 value)
 static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value,
                                     u32 timeout, wait_queue_head_t *queue)
 {
-       if (chip->vendor.irq && queue) {
+       if ((chip->flags & TPM_CHIP_FLAG_IRQ) && queue) {
                s32 rc;
-               struct priv_data *priv = chip->vendor.priv;
+               struct priv_data *priv = dev_get_drvdata(&chip->dev);
                unsigned int cur_intrs = priv->intrs;
 
-               enable_irq(chip->vendor.irq);
+               enable_irq(priv->irq);
                rc = wait_event_interruptible_timeout(*queue,
                                                      cur_intrs != priv->intrs,
                                                      timeout);
@@ -212,7 +216,7 @@ static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value,
                                return 0;
                } while (time_before(jiffies, stop));
        }
-       dev_err(chip->pdev, "%s(%02x, %02x) -> timeout\n", __func__, mask,
+       dev_err(&chip->dev, "%s(%02x, %02x) -> timeout\n", __func__, mask,
                value);
        return -ETIMEDOUT;
 }
@@ -231,16 +235,17 @@ static int i2c_nuvoton_wait_for_data_avail(struct tpm_chip *chip, u32 timeout,
 static int i2c_nuvoton_recv_data(struct i2c_client *client,
                                 struct tpm_chip *chip, u8 *buf, size_t count)
 {
+       struct priv_data *priv = dev_get_drvdata(&chip->dev);
        s32 rc;
        int burst_count, bytes2read, size = 0;
 
        while (size < count &&
               i2c_nuvoton_wait_for_data_avail(chip,
-                                              chip->vendor.timeout_c,
-                                              &chip->vendor.read_queue) == 0) {
+                                              chip->timeout_c,
+                                              &priv->read_queue) == 0) {
                burst_count = i2c_nuvoton_get_burstcount(client, chip);
                if (burst_count < 0) {
-                       dev_err(chip->pdev,
+                       dev_err(&chip->dev,
                                "%s() fail to read burstCount=%d\n", __func__,
                                burst_count);
                        return -EIO;
@@ -249,12 +254,12 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client,
                rc = i2c_nuvoton_read_buf(client, TPM_DATA_FIFO_R,
                                          bytes2read, &buf[size]);
                if (rc < 0) {
-                       dev_err(chip->pdev,
+                       dev_err(&chip->dev,
                                "%s() fail on i2c_nuvoton_read_buf()=%d\n",
                                __func__, rc);
                        return -EIO;
                }
-               dev_dbg(chip->pdev, "%s(%d):", __func__, bytes2read);
+               dev_dbg(&chip->dev, "%s(%d):", __func__, bytes2read);
                size += bytes2read;
        }
 
@@ -264,7 +269,8 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client,
 /* Read TPM command results */
 static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 {
-       struct device *dev = chip->pdev;
+       struct priv_data *priv = dev_get_drvdata(&chip->dev);
+       struct device *dev = chip->dev.parent;
        struct i2c_client *client = to_i2c_client(dev);
        s32 rc;
        int expected, status, burst_count, retries, size = 0;
@@ -285,7 +291,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
                 * tag, paramsize, and result
                 */
                status = i2c_nuvoton_wait_for_data_avail(
-                       chip, chip->vendor.timeout_c, &chip->vendor.read_queue);
+                       chip, chip->timeout_c, &priv->read_queue);
                if (status != 0) {
                        dev_err(dev, "%s() timeout on dataAvail\n", __func__);
                        size = -ETIMEDOUT;
@@ -325,7 +331,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
                }
                if (i2c_nuvoton_wait_for_stat(
                            chip, TPM_STS_VALID | TPM_STS_DATA_AVAIL,
-                           TPM_STS_VALID, chip->vendor.timeout_c,
+                           TPM_STS_VALID, chip->timeout_c,
                            NULL)) {
                        dev_err(dev, "%s() error left over data\n", __func__);
                        size = -ETIMEDOUT;
@@ -334,7 +340,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
                break;
        }
        i2c_nuvoton_ready(chip);
-       dev_dbg(chip->pdev, "%s() -> %d\n", __func__, size);
+       dev_dbg(&chip->dev, "%s() -> %d\n", __func__, size);
        return size;
 }
 
@@ -347,7 +353,8 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
  */
 static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
 {
-       struct device *dev = chip->pdev;
+       struct priv_data *priv = dev_get_drvdata(&chip->dev);
+       struct device *dev = chip->dev.parent;
        struct i2c_client *client = to_i2c_client(dev);
        u32 ordinal;
        size_t count = 0;
@@ -357,7 +364,7 @@ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
                i2c_nuvoton_ready(chip);
                if (i2c_nuvoton_wait_for_stat(chip, TPM_STS_COMMAND_READY,
                                              TPM_STS_COMMAND_READY,
-                                             chip->vendor.timeout_b, NULL)) {
+                                             chip->timeout_b, NULL)) {
                        dev_err(dev, "%s() timeout on commandReady\n",
                                __func__);
                        rc = -EIO;
@@ -389,7 +396,7 @@ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
                                                       TPM_STS_EXPECT,
                                                       TPM_STS_VALID |
                                                       TPM_STS_EXPECT,
-                                                      chip->vendor.timeout_c,
+                                                      chip->timeout_c,
                                                       NULL);
                        if (rc < 0) {
                                dev_err(dev, "%s() timeout on Expect\n",
@@ -414,7 +421,7 @@ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
                rc = i2c_nuvoton_wait_for_stat(chip,
                                               TPM_STS_VALID | TPM_STS_EXPECT,
                                               TPM_STS_VALID,
-                                              chip->vendor.timeout_c, NULL);
+                                              chip->timeout_c, NULL);
                if (rc) {
                        dev_err(dev, "%s() timeout on Expect to clear\n",
                                __func__);
@@ -439,7 +446,7 @@ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
        rc = i2c_nuvoton_wait_for_data_avail(chip,
                                             tpm_calc_ordinal_duration(chip,
                                                                       ordinal),
-                                            &chip->vendor.read_queue);
+                                            &priv->read_queue);
        if (rc) {
                dev_err(dev, "%s() timeout command duration\n", __func__);
                i2c_nuvoton_ready(chip);
@@ -456,6 +463,7 @@ static bool i2c_nuvoton_req_canceled(struct tpm_chip *chip, u8 status)
 }
 
 static const struct tpm_class_ops tpm_i2c = {
+       .flags = TPM_OPS_AUTO_STARTUP,
        .status = i2c_nuvoton_read_status,
        .recv = i2c_nuvoton_recv,
        .send = i2c_nuvoton_send,
@@ -473,11 +481,11 @@ static const struct tpm_class_ops tpm_i2c = {
 static irqreturn_t i2c_nuvoton_int_handler(int dummy, void *dev_id)
 {
        struct tpm_chip *chip = dev_id;
-       struct priv_data *priv = chip->vendor.priv;
+       struct priv_data *priv = dev_get_drvdata(&chip->dev);
 
        priv->intrs++;
-       wake_up(&chip->vendor.read_queue);
-       disable_irq_nosync(chip->vendor.irq);
+       wake_up(&priv->read_queue);
+       disable_irq_nosync(priv->irq);
        return IRQ_HANDLED;
 }
 
@@ -521,6 +529,7 @@ static int i2c_nuvoton_probe(struct i2c_client *client,
        int rc;
        struct tpm_chip *chip;
        struct device *dev = &client->dev;
+       struct priv_data *priv;
        u32 vid = 0;
 
        rc = get_vid(client, &vid);
@@ -534,46 +543,56 @@ static int i2c_nuvoton_probe(struct i2c_client *client,
        if (IS_ERR(chip))
                return PTR_ERR(chip);
 
-       chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data),
-                                        GFP_KERNEL);
-       if (!chip->vendor.priv)
+       priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL);
+       if (!priv)
                return -ENOMEM;
 
-       init_waitqueue_head(&chip->vendor.read_queue);
-       init_waitqueue_head(&chip->vendor.int_queue);
+       if (dev->of_node) {
+               const struct of_device_id *of_id;
+
+               of_id = of_match_device(dev->driver->of_match_table, dev);
+               if (of_id && of_id->data == OF_IS_TPM2)
+                       chip->flags |= TPM_CHIP_FLAG_TPM2;
+       } else
+               if (id->driver_data == I2C_IS_TPM2)
+                       chip->flags |= TPM_CHIP_FLAG_TPM2;
+
+       init_waitqueue_head(&priv->read_queue);
 
        /* Default timeouts */
-       chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
-       chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
-       chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
-       chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+       chip->timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+       chip->timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
+       chip->timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+       chip->timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+
+       dev_set_drvdata(&chip->dev, priv);
 
        /*
         * I2C intfcaps (interrupt capabilitieis) in the chip are hard coded to:
         *   TPM_INTF_INT_LEVEL_LOW | TPM_INTF_DATA_AVAIL_INT
         * The IRQ should be set in the i2c_board_info (which is done
         * automatically in of_i2c_register_devices, for device tree users */
-       chip->vendor.irq = client->irq;
-
-       if (chip->vendor.irq) {
-               dev_dbg(dev, "%s() chip-vendor.irq\n", __func__);
-               rc = devm_request_irq(dev, chip->vendor.irq,
+       priv->irq = client->irq;
+       if (client->irq) {
+               dev_dbg(dev, "%s() priv->irq\n", __func__);
+               rc = devm_request_irq(dev, client->irq,
                                      i2c_nuvoton_int_handler,
                                      IRQF_TRIGGER_LOW,
-                                     chip->devname,
+                                     dev_name(&chip->dev),
                                      chip);
                if (rc) {
                        dev_err(dev, "%s() Unable to request irq: %d for use\n",
-                               __func__, chip->vendor.irq);
-                       chip->vendor.irq = 0;
+                               __func__, priv->irq);
+                       priv->irq = 0;
                } else {
+                       chip->flags |= TPM_CHIP_FLAG_IRQ;
                        /* Clear any pending interrupt */
                        i2c_nuvoton_ready(chip);
                        /* - wait for TPM_STS==0xA0 (stsValid, commandReady) */
                        rc = i2c_nuvoton_wait_for_stat(chip,
                                                       TPM_STS_COMMAND_READY,
                                                       TPM_STS_COMMAND_READY,
-                                                      chip->vendor.timeout_b,
+                                                      chip->timeout_b,
                                                       NULL);
                        if (rc == 0) {
                                /*
@@ -601,25 +620,20 @@ static int i2c_nuvoton_probe(struct i2c_client *client,
                }
        }
 
-       if (tpm_get_timeouts(chip))
-               return -ENODEV;
-
-       if (tpm_do_selftest(chip))
-               return -ENODEV;
-
        return tpm_chip_register(chip);
 }
 
 static int i2c_nuvoton_remove(struct i2c_client *client)
 {
-       struct device *dev = &(client->dev);
-       struct tpm_chip *chip = dev_get_drvdata(dev);
+       struct tpm_chip *chip = i2c_get_clientdata(client);
+
        tpm_chip_unregister(chip);
        return 0;
 }
 
 static const struct i2c_device_id i2c_nuvoton_id[] = {
-       {I2C_DRIVER_NAME, 0},
+       {"tpm_i2c_nuvoton"},
+       {"tpm2_i2c_nuvoton", .driver_data = I2C_IS_TPM2},
        {}
 };
 MODULE_DEVICE_TABLE(i2c, i2c_nuvoton_id);
@@ -628,6 +642,7 @@ MODULE_DEVICE_TABLE(i2c, i2c_nuvoton_id);
 static const struct of_device_id i2c_nuvoton_of_match[] = {
        {.compatible = "nuvoton,npct501"},
        {.compatible = "winbond,wpct301"},
+       {.compatible = "nuvoton,npct601", .data = OF_IS_TPM2},
        {},
 };
 MODULE_DEVICE_TABLE(of, i2c_nuvoton_of_match);
@@ -640,7 +655,7 @@ static struct i2c_driver i2c_nuvoton_driver = {
        .probe = i2c_nuvoton_probe,
        .remove = i2c_nuvoton_remove,
        .driver = {
-               .name = I2C_DRIVER_NAME,
+               .name = "tpm_i2c_nuvoton",
                .pm = &i2c_nuvoton_pm_ops,
                .of_match_table = of_match_ptr(i2c_nuvoton_of_match),
        },
index b0a9a9e3424143214dc53f145823cedc8c16cf43..946025a7413b6b6e771e54a66a7bb42d75ed803e 100644 (file)
@@ -53,21 +53,6 @@ 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 *)TPM_VPRIV(chip);
-       return NULL;
-}
-
 /**
  * tpm_ibmvtpm_recv - Receive data after send
  * @chip:      tpm chip struct
@@ -79,12 +64,10 @@ static struct ibmvtpm_dev *ibmvtpm_get_data(const struct device *dev)
  */
 static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 {
-       struct ibmvtpm_dev *ibmvtpm;
+       struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
        u16 len;
        int sig;
 
-       ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip);
-
        if (!ibmvtpm->rtce_buf) {
                dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
                return 0;
@@ -122,13 +105,11 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
  */
 static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
 {
-       struct ibmvtpm_dev *ibmvtpm;
+       struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
        struct ibmvtpm_crq crq;
        __be64 *word = (__be64 *)&crq;
        int rc, sig;
 
-       ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip);
-
        if (!ibmvtpm->rtce_buf) {
                dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
                return 0;
@@ -289,8 +270,8 @@ static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm)
  */
 static int tpm_ibmvtpm_remove(struct vio_dev *vdev)
 {
-       struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev);
-       struct tpm_chip *chip = dev_get_drvdata(ibmvtpm->dev);
+       struct tpm_chip *chip = dev_get_drvdata(&vdev->dev);
+       struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
        int rc = 0;
 
        tpm_chip_unregister(chip);
@@ -327,7 +308,8 @@ static int tpm_ibmvtpm_remove(struct vio_dev *vdev)
  */
 static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev)
 {
-       struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev);
+       struct tpm_chip *chip = dev_get_drvdata(&vdev->dev);
+       struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
 
        /* ibmvtpm initializes at probe time, so the data we are
        * asking for may not be set yet. Estimate that 4K required
@@ -348,7 +330,8 @@ static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev)
  */
 static int tpm_ibmvtpm_suspend(struct device *dev)
 {
-       struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev);
+       struct tpm_chip *chip = dev_get_drvdata(dev);
+       struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
        struct ibmvtpm_crq crq;
        u64 *buf = (u64 *) &crq;
        int rc = 0;
@@ -400,7 +383,8 @@ static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm)
  */
 static int tpm_ibmvtpm_resume(struct device *dev)
 {
-       struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev);
+       struct tpm_chip *chip = dev_get_drvdata(dev);
+       struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
        int rc = 0;
 
        do {
@@ -643,7 +627,7 @@ static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev,
 
        crq_q->index = 0;
 
-       TPM_VPRIV(chip) = (void *)ibmvtpm;
+       dev_set_drvdata(&chip->dev, ibmvtpm);
 
        spin_lock_init(&ibmvtpm->rtce_lock);
 
index 6c488e635fdd07ec9e5f885d8780a18595108b2d..e3cf9f3545c57abfff68752effb73a4edff0c0bc 100644 (file)
@@ -195,9 +195,9 @@ static int wait(struct tpm_chip *chip, int wait_for_bit)
        }
        if (i == TPM_MAX_TRIES) {       /* timeout occurs */
                if (wait_for_bit == STAT_XFE)
-                       dev_err(chip->pdev, "Timeout in wait(STAT_XFE)\n");
+                       dev_err(&chip->dev, "Timeout in wait(STAT_XFE)\n");
                if (wait_for_bit == STAT_RDA)
-                       dev_err(chip->pdev, "Timeout in wait(STAT_RDA)\n");
+                       dev_err(&chip->dev, "Timeout in wait(STAT_RDA)\n");
                return -EIO;
        }
        return 0;
@@ -220,7 +220,7 @@ static void wait_and_send(struct tpm_chip *chip, u8 sendbyte)
 static void tpm_wtx(struct tpm_chip *chip)
 {
        number_of_wtx++;
-       dev_info(chip->pdev, "Granting WTX (%02d / %02d)\n",
+       dev_info(&chip->dev, "Granting WTX (%02d / %02d)\n",
                 number_of_wtx, TPM_MAX_WTX_PACKAGES);
        wait_and_send(chip, TPM_VL_VER);
        wait_and_send(chip, TPM_CTRL_WTX);
@@ -231,7 +231,7 @@ static void tpm_wtx(struct tpm_chip *chip)
 
 static void tpm_wtx_abort(struct tpm_chip *chip)
 {
-       dev_info(chip->pdev, "Aborting WTX\n");
+       dev_info(&chip->dev, "Aborting WTX\n");
        wait_and_send(chip, TPM_VL_VER);
        wait_and_send(chip, TPM_CTRL_WTX_ABORT);
        wait_and_send(chip, 0x00);
@@ -257,7 +257,7 @@ recv_begin:
        }
 
        if (buf[0] != TPM_VL_VER) {
-               dev_err(chip->pdev,
+               dev_err(&chip->dev,
                        "Wrong transport protocol implementation!\n");
                return -EIO;
        }
@@ -272,7 +272,7 @@ recv_begin:
                }
 
                if ((size == 0x6D00) && (buf[1] == 0x80)) {
-                       dev_err(chip->pdev, "Error handling on vendor layer!\n");
+                       dev_err(&chip->dev, "Error handling on vendor layer!\n");
                        return -EIO;
                }
 
@@ -284,7 +284,7 @@ recv_begin:
        }
 
        if (buf[1] == TPM_CTRL_WTX) {
-               dev_info(chip->pdev, "WTX-package received\n");
+               dev_info(&chip->dev, "WTX-package received\n");
                if (number_of_wtx < TPM_MAX_WTX_PACKAGES) {
                        tpm_wtx(chip);
                        goto recv_begin;
@@ -295,14 +295,14 @@ recv_begin:
        }
 
        if (buf[1] == TPM_CTRL_WTX_ABORT_ACK) {
-               dev_info(chip->pdev, "WTX-abort acknowledged\n");
+               dev_info(&chip->dev, "WTX-abort acknowledged\n");
                return size;
        }
 
        if (buf[1] == TPM_CTRL_ERROR) {
-               dev_err(chip->pdev, "ERROR-package received:\n");
+               dev_err(&chip->dev, "ERROR-package received:\n");
                if (buf[4] == TPM_INF_NAK)
-                       dev_err(chip->pdev,
+                       dev_err(&chip->dev,
                                "-> Negative acknowledgement"
                                " - retransmit command!\n");
                return -EIO;
@@ -321,7 +321,7 @@ static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count)
 
        ret = empty_fifo(chip, 1);
        if (ret) {
-               dev_err(chip->pdev, "Timeout while clearing FIFO\n");
+               dev_err(&chip->dev, "Timeout while clearing FIFO\n");
                return -EIO;
        }
 
index 289389ecef84f039d52689b9b56152bfef5f8593..9ff0e072c4760a12d637fd9914cef50f43ddc18c 100644 (file)
@@ -64,15 +64,21 @@ enum tpm_nsc_cmd_mode {
        NSC_COMMAND_EOC = 0x03,
        NSC_COMMAND_CANCEL = 0x22
 };
+
+struct tpm_nsc_priv {
+       unsigned long base;
+};
+
 /*
  * Wait for a certain status to appear
  */
 static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
 {
+       struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
        unsigned long stop;
 
        /* status immediately available check */
-       *data = inb(chip->vendor.base + NSC_STATUS);
+       *data = inb(priv->base + NSC_STATUS);
        if ((*data & mask) == val)
                return 0;
 
@@ -80,7 +86,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
        stop = jiffies + 10 * HZ;
        do {
                msleep(TPM_TIMEOUT);
-               *data = inb(chip->vendor.base + 1);
+               *data = inb(priv->base + 1);
                if ((*data & mask) == val)
                        return 0;
        }
@@ -91,13 +97,14 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
 
 static int nsc_wait_for_ready(struct tpm_chip *chip)
 {
+       struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
        int status;
        unsigned long stop;
 
        /* status immediately available check */
-       status = inb(chip->vendor.base + NSC_STATUS);
+       status = inb(priv->base + NSC_STATUS);
        if (status & NSC_STATUS_OBF)
-               status = inb(chip->vendor.base + NSC_DATA);
+               status = inb(priv->base + NSC_DATA);
        if (status & NSC_STATUS_RDY)
                return 0;
 
@@ -105,21 +112,22 @@ static int nsc_wait_for_ready(struct tpm_chip *chip)
        stop = jiffies + 100;
        do {
                msleep(TPM_TIMEOUT);
-               status = inb(chip->vendor.base + NSC_STATUS);
+               status = inb(priv->base + NSC_STATUS);
                if (status & NSC_STATUS_OBF)
-                       status = inb(chip->vendor.base + NSC_DATA);
+                       status = inb(priv->base + NSC_DATA);
                if (status & NSC_STATUS_RDY)
                        return 0;
        }
        while (time_before(jiffies, stop));
 
-       dev_info(chip->pdev, "wait for ready failed\n");
+       dev_info(&chip->dev, "wait for ready failed\n");
        return -EBUSY;
 }
 
 
 static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
 {
+       struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
        u8 *buffer = buf;
        u8 data, *p;
        u32 size;
@@ -129,12 +137,13 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
                return -EIO;
 
        if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
-               dev_err(chip->pdev, "F0 timeout\n");
+               dev_err(&chip->dev, "F0 timeout\n");
                return -EIO;
        }
-       if ((data =
-            inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
-               dev_err(chip->pdev, "not in normal mode (0x%x)\n",
+
+       data = inb(priv->base + NSC_DATA);
+       if (data != NSC_COMMAND_NORMAL) {
+               dev_err(&chip->dev, "not in normal mode (0x%x)\n",
                        data);
                return -EIO;
        }
@@ -143,22 +152,24 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
        for (p = buffer; p < &buffer[count]; p++) {
                if (wait_for_stat
                    (chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) {
-                       dev_err(chip->pdev,
+                       dev_err(&chip->dev,
                                "OBF timeout (while reading data)\n");
                        return -EIO;
                }
                if (data & NSC_STATUS_F0)
                        break;
-               *p = inb(chip->vendor.base + NSC_DATA);
+               *p = inb(priv->base + NSC_DATA);
        }
 
        if ((data & NSC_STATUS_F0) == 0 &&
        (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0)) {
-               dev_err(chip->pdev, "F0 not set\n");
+               dev_err(&chip->dev, "F0 not set\n");
                return -EIO;
        }
-       if ((data = inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_EOC) {
-               dev_err(chip->pdev,
+
+       data = inb(priv->base + NSC_DATA);
+       if (data != NSC_COMMAND_EOC) {
+               dev_err(&chip->dev,
                        "expected end of command(0x%x)\n", data);
                return -EIO;
        }
@@ -174,6 +185,7 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
 
 static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
 {
+       struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
        u8 data;
        int i;
 
@@ -183,48 +195,52 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
         * fix it. Not sure why this is needed, we followed the flow
         * chart in the manual to the letter.
         */
-       outb(NSC_COMMAND_CANCEL, chip->vendor.base + NSC_COMMAND);
+       outb(NSC_COMMAND_CANCEL, priv->base + NSC_COMMAND);
 
        if (nsc_wait_for_ready(chip) != 0)
                return -EIO;
 
        if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
-               dev_err(chip->pdev, "IBF timeout\n");
+               dev_err(&chip->dev, "IBF timeout\n");
                return -EIO;
        }
 
-       outb(NSC_COMMAND_NORMAL, chip->vendor.base + NSC_COMMAND);
+       outb(NSC_COMMAND_NORMAL, priv->base + NSC_COMMAND);
        if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
-               dev_err(chip->pdev, "IBR timeout\n");
+               dev_err(&chip->dev, "IBR timeout\n");
                return -EIO;
        }
 
        for (i = 0; i < count; i++) {
                if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
-                       dev_err(chip->pdev,
+                       dev_err(&chip->dev,
                                "IBF timeout (while writing data)\n");
                        return -EIO;
                }
-               outb(buf[i], chip->vendor.base + NSC_DATA);
+               outb(buf[i], priv->base + NSC_DATA);
        }
 
        if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
-               dev_err(chip->pdev, "IBF timeout\n");
+               dev_err(&chip->dev, "IBF timeout\n");
                return -EIO;
        }
-       outb(NSC_COMMAND_EOC, chip->vendor.base + NSC_COMMAND);
+       outb(NSC_COMMAND_EOC, priv->base + NSC_COMMAND);
 
        return count;
 }
 
 static void tpm_nsc_cancel(struct tpm_chip *chip)
 {
-       outb(NSC_COMMAND_CANCEL, chip->vendor.base + NSC_COMMAND);
+       struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
+
+       outb(NSC_COMMAND_CANCEL, priv->base + NSC_COMMAND);
 }
 
 static u8 tpm_nsc_status(struct tpm_chip *chip)
 {
-       return inb(chip->vendor.base + NSC_STATUS);
+       struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
+
+       return inb(priv->base + NSC_STATUS);
 }
 
 static bool tpm_nsc_req_canceled(struct tpm_chip *chip, u8 status)
@@ -247,9 +263,10 @@ static struct platform_device *pdev = NULL;
 static void tpm_nsc_remove(struct device *dev)
 {
        struct tpm_chip *chip = dev_get_drvdata(dev);
+       struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
 
        tpm_chip_unregister(chip);
-       release_region(chip->vendor.base, 2);
+       release_region(priv->base, 2);
 }
 
 static SIMPLE_DEV_PM_OPS(tpm_nsc_pm, tpm_pm_suspend, tpm_pm_resume);
@@ -268,6 +285,7 @@ static int __init init_nsc(void)
        int nscAddrBase = TPM_ADDR;
        struct tpm_chip *chip;
        unsigned long base;
+       struct tpm_nsc_priv *priv;
 
        /* verify that it is a National part (SID) */
        if (tpm_read_index(TPM_ADDR, NSC_SID_INDEX) != 0xEF) {
@@ -301,6 +319,14 @@ static int __init init_nsc(void)
        if ((rc = platform_device_add(pdev)) < 0)
                goto err_put_dev;
 
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               rc = -ENOMEM;
+               goto err_del_dev;
+       }
+
+       priv->base = base;
+
        if (request_region(base, 2, "tpm_nsc0") == NULL ) {
                rc = -EBUSY;
                goto err_del_dev;
@@ -312,6 +338,8 @@ static int __init init_nsc(void)
                goto err_rel_reg;
        }
 
+       dev_set_drvdata(&chip->dev, priv);
+
        rc = tpm_chip_register(chip);
        if (rc)
                goto err_rel_reg;
@@ -349,8 +377,6 @@ static int __init init_nsc(void)
                 "NSC TPM revision %d\n",
                 tpm_read_index(nscAddrBase, 0x27) & 0x1F);
 
-       chip->vendor.base = base;
-
        return 0;
 
 err_rel_reg:
index a507006728e0976cf608e70a7f800c71401b9ccf..eaf5730d79eb9d6e1597cf92acdc03a32ad41248 100644 (file)
 #include <linux/acpi.h>
 #include <linux/freezer.h>
 #include "tpm.h"
-
-enum tis_access {
-       TPM_ACCESS_VALID = 0x80,
-       TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
-       TPM_ACCESS_REQUEST_PENDING = 0x04,
-       TPM_ACCESS_REQUEST_USE = 0x02,
-};
-
-enum tis_status {
-       TPM_STS_VALID = 0x80,
-       TPM_STS_COMMAND_READY = 0x40,
-       TPM_STS_GO = 0x20,
-       TPM_STS_DATA_AVAIL = 0x10,
-       TPM_STS_DATA_EXPECT = 0x08,
-};
-
-enum tis_int_flags {
-       TPM_GLOBAL_INT_ENABLE = 0x80000000,
-       TPM_INTF_BURST_COUNT_STATIC = 0x100,
-       TPM_INTF_CMD_READY_INT = 0x080,
-       TPM_INTF_INT_EDGE_FALLING = 0x040,
-       TPM_INTF_INT_EDGE_RISING = 0x020,
-       TPM_INTF_INT_LEVEL_LOW = 0x010,
-       TPM_INTF_INT_LEVEL_HIGH = 0x008,
-       TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
-       TPM_INTF_STS_VALID_INT = 0x002,
-       TPM_INTF_DATA_AVAIL_INT = 0x001,
-};
-
-enum tis_defaults {
-       TIS_MEM_LEN = 0x5000,
-       TIS_SHORT_TIMEOUT = 750,        /* ms */
-       TIS_LONG_TIMEOUT = 2000,        /* 2 sec */
-};
+#include "tpm_tis_core.h"
 
 struct tpm_info {
        struct resource res;
@@ -73,30 +40,30 @@ struct tpm_info {
        int irq;
 };
 
-/* Some timeout values are needed before it is known whether the chip is
- * TPM 1.0 or TPM 2.0.
- */
-#define TIS_TIMEOUT_A_MAX      max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_A)
-#define TIS_TIMEOUT_B_MAX      max(TIS_LONG_TIMEOUT, TPM2_TIMEOUT_B)
-#define TIS_TIMEOUT_C_MAX      max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_C)
-#define TIS_TIMEOUT_D_MAX      max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_D)
-
-#define        TPM_ACCESS(l)                   (0x0000 | ((l) << 12))
-#define        TPM_INT_ENABLE(l)               (0x0008 | ((l) << 12))
-#define        TPM_INT_VECTOR(l)               (0x000C | ((l) << 12))
-#define        TPM_INT_STATUS(l)               (0x0010 | ((l) << 12))
-#define        TPM_INTF_CAPS(l)                (0x0014 | ((l) << 12))
-#define        TPM_STS(l)                      (0x0018 | ((l) << 12))
-#define        TPM_STS3(l)                     (0x001b | ((l) << 12))
-#define        TPM_DATA_FIFO(l)                (0x0024 | ((l) << 12))
-
-#define        TPM_DID_VID(l)                  (0x0F00 | ((l) << 12))
-#define        TPM_RID(l)                      (0x0F04 | ((l) << 12))
-
-struct priv_data {
-       bool irq_tested;
+struct tpm_tis_tcg_phy {
+       struct tpm_tis_data priv;
+       void __iomem *iobase;
 };
 
+static inline struct tpm_tis_tcg_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *data)
+{
+       return container_of(data, struct tpm_tis_tcg_phy, priv);
+}
+
+static bool interrupts = true;
+module_param(interrupts, bool, 0444);
+MODULE_PARM_DESC(interrupts, "Enable interrupts");
+
+static bool itpm;
+module_param(itpm, bool, 0444);
+MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
+
+static bool force;
+#ifdef CONFIG_X86
+module_param(force, bool, 0444);
+MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry");
+#endif
+
 #if defined(CONFIG_PNP) && defined(CONFIG_ACPI)
 static int has_hid(struct acpi_device *dev, const char *hid)
 {
@@ -120,744 +87,82 @@ static inline int is_itpm(struct acpi_device *dev)
 }
 #endif
 
-/* Before we attempt to access the TPM we must see that the valid bit is set.
- * The specification says that this bit is 0 at reset and remains 0 until the
- * 'TPM has gone through its self test and initialization and has established
- * correct values in the other bits.' */
-static int wait_startup(struct tpm_chip *chip, int l)
-{
-       unsigned long stop = jiffies + chip->vendor.timeout_a;
-       do {
-               if (ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
-                   TPM_ACCESS_VALID)
-                       return 0;
-               msleep(TPM_TIMEOUT);
-       } while (time_before(jiffies, stop));
-       return -1;
-}
-
-static int check_locality(struct tpm_chip *chip, int l)
-{
-       if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
-            (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
-           (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
-               return chip->vendor.locality = l;
-
-       return -1;
-}
-
-static void release_locality(struct tpm_chip *chip, int l, int force)
-{
-       if (force || (ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
-                     (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
-           (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID))
-               iowrite8(TPM_ACCESS_ACTIVE_LOCALITY,
-                        chip->vendor.iobase + TPM_ACCESS(l));
-}
-
-static int request_locality(struct tpm_chip *chip, int l)
-{
-       unsigned long stop, timeout;
-       long rc;
-
-       if (check_locality(chip, l) >= 0)
-               return l;
-
-       iowrite8(TPM_ACCESS_REQUEST_USE,
-                chip->vendor.iobase + TPM_ACCESS(l));
-
-       stop = jiffies + chip->vendor.timeout_a;
-
-       if (chip->vendor.irq) {
-again:
-               timeout = stop - jiffies;
-               if ((long)timeout <= 0)
-                       return -1;
-               rc = wait_event_interruptible_timeout(chip->vendor.int_queue,
-                                                     (check_locality
-                                                      (chip, l) >= 0),
-                                                     timeout);
-               if (rc > 0)
-                       return l;
-               if (rc == -ERESTARTSYS && freezing(current)) {
-                       clear_thread_flag(TIF_SIGPENDING);
-                       goto again;
-               }
-       } else {
-               /* wait for burstcount */
-               do {
-                       if (check_locality(chip, l) >= 0)
-                               return l;
-                       msleep(TPM_TIMEOUT);
-               }
-               while (time_before(jiffies, stop));
-       }
-       return -1;
-}
-
-static u8 tpm_tis_status(struct tpm_chip *chip)
-{
-       return ioread8(chip->vendor.iobase +
-                      TPM_STS(chip->vendor.locality));
-}
-
-static void tpm_tis_ready(struct tpm_chip *chip)
-{
-       /* this causes the current command to be aborted */
-       iowrite8(TPM_STS_COMMAND_READY,
-                chip->vendor.iobase + TPM_STS(chip->vendor.locality));
-}
-
-static int get_burstcount(struct tpm_chip *chip)
-{
-       unsigned long stop;
-       int burstcnt;
-
-       /* wait for burstcount */
-       /* which timeout value, spec has 2 answers (c & d) */
-       stop = jiffies + chip->vendor.timeout_d;
-       do {
-               burstcnt = ioread8(chip->vendor.iobase +
-                                  TPM_STS(chip->vendor.locality) + 1);
-               burstcnt += ioread8(chip->vendor.iobase +
-                                   TPM_STS(chip->vendor.locality) +
-                                   2) << 8;
-               if (burstcnt)
-                       return burstcnt;
-               msleep(TPM_TIMEOUT);
-       } while (time_before(jiffies, stop));
-       return -EBUSY;
-}
-
-static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
+static int tpm_tcg_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
+                             u8 *result)
 {
-       int size = 0, burstcnt;
-       while (size < count &&
-              wait_for_tpm_stat(chip,
-                                TPM_STS_DATA_AVAIL | TPM_STS_VALID,
-                                chip->vendor.timeout_c,
-                                &chip->vendor.read_queue, true)
-              == 0) {
-               burstcnt = get_burstcount(chip);
-               for (; burstcnt > 0 && size < count; burstcnt--)
-                       buf[size++] = ioread8(chip->vendor.iobase +
-                                             TPM_DATA_FIFO(chip->vendor.
-                                                           locality));
-       }
-       return size;
-}
-
-static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
-{
-       int size = 0;
-       int expected, status;
-
-       if (count < TPM_HEADER_SIZE) {
-               size = -EIO;
-               goto out;
-       }
-
-       /* read first 10 bytes, including tag, paramsize, and result */
-       if ((size =
-            recv_data(chip, buf, TPM_HEADER_SIZE)) < TPM_HEADER_SIZE) {
-               dev_err(chip->pdev, "Unable to read header\n");
-               goto out;
-       }
-
-       expected = be32_to_cpu(*(__be32 *) (buf + 2));
-       if (expected > count) {
-               size = -EIO;
-               goto out;
-       }
-
-       if ((size +=
-            recv_data(chip, &buf[TPM_HEADER_SIZE],
-                      expected - TPM_HEADER_SIZE)) < expected) {
-               dev_err(chip->pdev, "Unable to read remainder of result\n");
-               size = -ETIME;
-               goto out;
-       }
-
-       wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
-                         &chip->vendor.int_queue, false);
-       status = tpm_tis_status(chip);
-       if (status & TPM_STS_DATA_AVAIL) {      /* retry? */
-               dev_err(chip->pdev, "Error left over data\n");
-               size = -EIO;
-               goto out;
-       }
-
-out:
-       tpm_tis_ready(chip);
-       release_locality(chip, chip->vendor.locality, 0);
-       return size;
-}
-
-static bool itpm;
-module_param(itpm, bool, 0444);
-MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
-
-/*
- * If interrupts are used (signaled by an irq set in the vendor structure)
- * tpm.c can skip polling for the data to be available as the interrupt is
- * waited for here
- */
-static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
-{
-       int rc, status, burstcnt;
-       size_t count = 0;
-
-       if (request_locality(chip, 0) < 0)
-               return -EBUSY;
-
-       status = tpm_tis_status(chip);
-       if ((status & TPM_STS_COMMAND_READY) == 0) {
-               tpm_tis_ready(chip);
-               if (wait_for_tpm_stat
-                   (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
-                    &chip->vendor.int_queue, false) < 0) {
-                       rc = -ETIME;
-                       goto out_err;
-               }
-       }
-
-       while (count < len - 1) {
-               burstcnt = get_burstcount(chip);
-               for (; burstcnt > 0 && count < len - 1; burstcnt--) {
-                       iowrite8(buf[count], chip->vendor.iobase +
-                                TPM_DATA_FIFO(chip->vendor.locality));
-                       count++;
-               }
-
-               wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
-                                 &chip->vendor.int_queue, false);
-               status = tpm_tis_status(chip);
-               if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
-                       rc = -EIO;
-                       goto out_err;
-               }
-       }
-
-       /* write last byte */
-       iowrite8(buf[count],
-                chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality));
-       wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
-                         &chip->vendor.int_queue, false);
-       status = tpm_tis_status(chip);
-       if ((status & TPM_STS_DATA_EXPECT) != 0) {
-               rc = -EIO;
-               goto out_err;
-       }
+       struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 
+       while (len--)
+               *result++ = ioread8(phy->iobase + addr);
        return 0;
-
-out_err:
-       tpm_tis_ready(chip);
-       release_locality(chip, chip->vendor.locality, 0);
-       return rc;
-}
-
-static void disable_interrupts(struct tpm_chip *chip)
-{
-       u32 intmask;
-
-       intmask =
-           ioread32(chip->vendor.iobase +
-                    TPM_INT_ENABLE(chip->vendor.locality));
-       intmask &= ~TPM_GLOBAL_INT_ENABLE;
-       iowrite32(intmask,
-                 chip->vendor.iobase +
-                 TPM_INT_ENABLE(chip->vendor.locality));
-       devm_free_irq(chip->pdev, chip->vendor.irq, chip);
-       chip->vendor.irq = 0;
-}
-
-/*
- * If interrupts are used (signaled by an irq set in the vendor structure)
- * tpm.c can skip polling for the data to be available as the interrupt is
- * waited for here
- */
-static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len)
-{
-       int rc;
-       u32 ordinal;
-       unsigned long dur;
-
-       rc = tpm_tis_send_data(chip, buf, len);
-       if (rc < 0)
-               return rc;
-
-       /* go and do it */
-       iowrite8(TPM_STS_GO,
-                chip->vendor.iobase + TPM_STS(chip->vendor.locality));
-
-       if (chip->vendor.irq) {
-               ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
-
-               if (chip->flags & TPM_CHIP_FLAG_TPM2)
-                       dur = tpm2_calc_ordinal_duration(chip, ordinal);
-               else
-                       dur = tpm_calc_ordinal_duration(chip, ordinal);
-
-               if (wait_for_tpm_stat
-                   (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, dur,
-                    &chip->vendor.read_queue, false) < 0) {
-                       rc = -ETIME;
-                       goto out_err;
-               }
-       }
-       return len;
-out_err:
-       tpm_tis_ready(chip);
-       release_locality(chip, chip->vendor.locality, 0);
-       return rc;
-}
-
-static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
-{
-       int rc, irq;
-       struct priv_data *priv = chip->vendor.priv;
-
-       if (!chip->vendor.irq || priv->irq_tested)
-               return tpm_tis_send_main(chip, buf, len);
-
-       /* Verify receipt of the expected IRQ */
-       irq = chip->vendor.irq;
-       chip->vendor.irq = 0;
-       rc = tpm_tis_send_main(chip, buf, len);
-       chip->vendor.irq = irq;
-       if (!priv->irq_tested)
-               msleep(1);
-       if (!priv->irq_tested)
-               disable_interrupts(chip);
-       priv->irq_tested = true;
-       return rc;
 }
 
-struct tis_vendor_timeout_override {
-       u32 did_vid;
-       unsigned long timeout_us[4];
-};
-
-static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
-       /* Atmel 3204 */
-       { 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
-                       (TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
-};
-
-static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
-                                   unsigned long *timeout_cap)
+static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
+                              u8 *value)
 {
-       int i;
-       u32 did_vid;
+       struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 
-       did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
-
-       for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
-               if (vendor_timeout_overrides[i].did_vid != did_vid)
-                       continue;
-               memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
-                      sizeof(vendor_timeout_overrides[i].timeout_us));
-               return true;
-       }
-
-       return false;
+       while (len--)
+               iowrite8(*value++, phy->iobase + addr);
+       return 0;
 }
 
-/*
- * Early probing for iTPM with STS_DATA_EXPECT flaw.
- * Try sending command without itpm flag set and if that
- * fails, repeat with itpm flag set.
- */
-static int probe_itpm(struct tpm_chip *chip)
+static int tpm_tcg_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
 {
-       int rc = 0;
-       u8 cmd_getticks[] = {
-               0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a,
-               0x00, 0x00, 0x00, 0xf1
-       };
-       size_t len = sizeof(cmd_getticks);
-       bool rem_itpm = itpm;
-       u16 vendor = ioread16(chip->vendor.iobase + TPM_DID_VID(0));
-
-       /* probe only iTPMS */
-       if (vendor != TPM_VID_INTEL)
-               return 0;
-
-       itpm = false;
-
-       rc = tpm_tis_send_data(chip, cmd_getticks, len);
-       if (rc == 0)
-               goto out;
-
-       tpm_tis_ready(chip);
-       release_locality(chip, chip->vendor.locality, 0);
+       struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 
-       itpm = true;
-
-       rc = tpm_tis_send_data(chip, cmd_getticks, len);
-       if (rc == 0) {
-               dev_info(chip->pdev, "Detected an iTPM.\n");
-               rc = 1;
-       } else
-               rc = -EFAULT;
-
-out:
-       itpm = rem_itpm;
-       tpm_tis_ready(chip);
-       release_locality(chip, chip->vendor.locality, 0);
-
-       return rc;
+       *result = ioread16(phy->iobase + addr);
+       return 0;
 }
 
-static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status)
+static int tpm_tcg_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
 {
-       switch (chip->vendor.manufacturer_id) {
-       case TPM_VID_WINBOND:
-               return ((status == TPM_STS_VALID) ||
-                       (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY)));
-       case TPM_VID_STM:
-               return (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY));
-       default:
-               return (status == TPM_STS_COMMAND_READY);
-       }
-}
-
-static const struct tpm_class_ops tpm_tis = {
-       .status = tpm_tis_status,
-       .recv = tpm_tis_recv,
-       .send = tpm_tis_send,
-       .cancel = tpm_tis_ready,
-       .update_timeouts = tpm_tis_update_timeouts,
-       .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
-       .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
-       .req_canceled = tpm_tis_req_canceled,
-};
+       struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 
-static irqreturn_t tis_int_handler(int dummy, void *dev_id)
-{
-       struct tpm_chip *chip = dev_id;
-       u32 interrupt;
-       int i;
-
-       interrupt = ioread32(chip->vendor.iobase +
-                            TPM_INT_STATUS(chip->vendor.locality));
-
-       if (interrupt == 0)
-               return IRQ_NONE;
-
-       ((struct priv_data *)chip->vendor.priv)->irq_tested = true;
-       if (interrupt & TPM_INTF_DATA_AVAIL_INT)
-               wake_up_interruptible(&chip->vendor.read_queue);
-       if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT)
-               for (i = 0; i < 5; i++)
-                       if (check_locality(chip, i) >= 0)
-                               break;
-       if (interrupt &
-           (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT |
-            TPM_INTF_CMD_READY_INT))
-               wake_up_interruptible(&chip->vendor.int_queue);
-
-       /* Clear interrupts handled with TPM_EOI */
-       iowrite32(interrupt,
-                 chip->vendor.iobase +
-                 TPM_INT_STATUS(chip->vendor.locality));
-       ioread32(chip->vendor.iobase + TPM_INT_STATUS(chip->vendor.locality));
-       return IRQ_HANDLED;
+       *result = ioread32(phy->iobase + addr);
+       return 0;
 }
 
-/* Register the IRQ and issue a command that will cause an interrupt. If an
- * irq is seen then leave the chip setup for IRQ operation, otherwise reverse
- * everything and leave in polling mode. Returns 0 on success.
- */
-static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask,
-                                   int flags, int irq)
+static int tpm_tcg_write32(struct tpm_tis_data *data, u32 addr, u32 value)
 {
-       struct priv_data *priv = chip->vendor.priv;
-       u8 original_int_vec;
-
-       if (devm_request_irq(chip->pdev, irq, tis_int_handler, flags,
-                            chip->devname, chip) != 0) {
-               dev_info(chip->pdev, "Unable to request irq: %d for probe\n",
-                        irq);
-               return -1;
-       }
-       chip->vendor.irq = irq;
-
-       original_int_vec = ioread8(chip->vendor.iobase +
-                                  TPM_INT_VECTOR(chip->vendor.locality));
-       iowrite8(irq,
-                chip->vendor.iobase + TPM_INT_VECTOR(chip->vendor.locality));
-
-       /* Clear all existing */
-       iowrite32(ioread32(chip->vendor.iobase +
-                          TPM_INT_STATUS(chip->vendor.locality)),
-                 chip->vendor.iobase + TPM_INT_STATUS(chip->vendor.locality));
-
-       /* Turn on */
-       iowrite32(intmask | TPM_GLOBAL_INT_ENABLE,
-                 chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
-
-       priv->irq_tested = false;
-
-       /* Generate an interrupt by having the core call through to
-        * tpm_tis_send
-        */
-       if (chip->flags & TPM_CHIP_FLAG_TPM2)
-               tpm2_gen_interrupt(chip);
-       else
-               tpm_gen_interrupt(chip);
-
-       /* tpm_tis_send will either confirm the interrupt is working or it
-        * will call disable_irq which undoes all of the above.
-        */
-       if (!chip->vendor.irq) {
-               iowrite8(original_int_vec,
-                        chip->vendor.iobase +
-                            TPM_INT_VECTOR(chip->vendor.locality));
-               return 1;
-       }
+       struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 
+       iowrite32(value, phy->iobase + addr);
        return 0;
 }
 
-/* Try to find the IRQ the TPM is using. This is for legacy x86 systems that
- * do not have ACPI/etc. We typically expect the interrupt to be declared if
- * present.
- */
-static void tpm_tis_probe_irq(struct tpm_chip *chip, u32 intmask)
-{
-       u8 original_int_vec;
-       int i;
-
-       original_int_vec = ioread8(chip->vendor.iobase +
-                                  TPM_INT_VECTOR(chip->vendor.locality));
-
-       if (!original_int_vec) {
-               if (IS_ENABLED(CONFIG_X86))
-                       for (i = 3; i <= 15; i++)
-                               if (!tpm_tis_probe_irq_single(chip, intmask, 0,
-                                                             i))
-                                       return;
-       } else if (!tpm_tis_probe_irq_single(chip, intmask, 0,
-                                            original_int_vec))
-               return;
-}
-
-static bool interrupts = true;
-module_param(interrupts, bool, 0444);
-MODULE_PARM_DESC(interrupts, "Enable interrupts");
-
-static void tpm_tis_remove(struct tpm_chip *chip)
-{
-       if (chip->flags & TPM_CHIP_FLAG_TPM2)
-               tpm2_shutdown(chip, TPM2_SU_CLEAR);
-
-       iowrite32(~TPM_GLOBAL_INT_ENABLE &
-                 ioread32(chip->vendor.iobase +
-                          TPM_INT_ENABLE(chip->vendor.
-                                         locality)),
-                 chip->vendor.iobase +
-                 TPM_INT_ENABLE(chip->vendor.locality));
-       release_locality(chip, chip->vendor.locality, 1);
-}
+static const struct tpm_tis_phy_ops tpm_tcg = {
+       .read_bytes = tpm_tcg_read_bytes,
+       .write_bytes = tpm_tcg_write_bytes,
+       .read16 = tpm_tcg_read16,
+       .read32 = tpm_tcg_read32,
+       .write32 = tpm_tcg_write32,
+};
 
 static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
                        acpi_handle acpi_dev_handle)
 {
-       u32 vendor, intfcaps, intmask;
-       int rc, probe;
-       struct tpm_chip *chip;
-       struct priv_data *priv;
+       struct tpm_tis_tcg_phy *phy;
+       int irq = -1;
 
-       priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL);
-       if (priv == NULL)
+       phy = devm_kzalloc(dev, sizeof(struct tpm_tis_tcg_phy), GFP_KERNEL);
+       if (phy == NULL)
                return -ENOMEM;
 
-       chip = tpmm_chip_alloc(dev, &tpm_tis);
-       if (IS_ERR(chip))
-               return PTR_ERR(chip);
-
-       chip->vendor.priv = priv;
-#ifdef CONFIG_ACPI
-       chip->acpi_dev_handle = acpi_dev_handle;
-#endif
+       phy->iobase = devm_ioremap_resource(dev, &tpm_info->res);
+       if (IS_ERR(phy->iobase))
+               return PTR_ERR(phy->iobase);
 
-       chip->vendor.iobase = devm_ioremap_resource(dev, &tpm_info->res);
-       if (IS_ERR(chip->vendor.iobase))
-               return PTR_ERR(chip->vendor.iobase);
-
-       /* Maximum timeouts */
-       chip->vendor.timeout_a = TIS_TIMEOUT_A_MAX;
-       chip->vendor.timeout_b = TIS_TIMEOUT_B_MAX;
-       chip->vendor.timeout_c = TIS_TIMEOUT_C_MAX;
-       chip->vendor.timeout_d = TIS_TIMEOUT_D_MAX;
-
-       if (wait_startup(chip, 0) != 0) {
-               rc = -ENODEV;
-               goto out_err;
-       }
-
-       /* Take control of the TPM's interrupt hardware and shut it off */
-       intmask = ioread32(chip->vendor.iobase +
-                          TPM_INT_ENABLE(chip->vendor.locality));
-       intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT |
-                  TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT;
-       intmask &= ~TPM_GLOBAL_INT_ENABLE;
-       iowrite32(intmask,
-                 chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
-
-       if (request_locality(chip, 0) != 0) {
-               rc = -ENODEV;
-               goto out_err;
-       }
-
-       rc = tpm2_probe(chip);
-       if (rc)
-               goto out_err;
-
-       vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
-       chip->vendor.manufacturer_id = vendor;
-
-       dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n",
-                (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2",
-                vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
-
-       if (!itpm) {
-               probe = probe_itpm(chip);
-               if (probe < 0) {
-                       rc = -ENODEV;
-                       goto out_err;
-               }
-               itpm = !!probe;
-       }
+       if (interrupts)
+               irq = tpm_info->irq;
 
        if (itpm)
-               dev_info(dev, "Intel iTPM workaround enabled\n");
-
-
-       /* Figure out the capabilities */
-       intfcaps =
-           ioread32(chip->vendor.iobase +
-                    TPM_INTF_CAPS(chip->vendor.locality));
-       dev_dbg(dev, "TPM interface capabilities (0x%x):\n",
-               intfcaps);
-       if (intfcaps & TPM_INTF_BURST_COUNT_STATIC)
-               dev_dbg(dev, "\tBurst Count Static\n");
-       if (intfcaps & TPM_INTF_CMD_READY_INT)
-               dev_dbg(dev, "\tCommand Ready Int Support\n");
-       if (intfcaps & TPM_INTF_INT_EDGE_FALLING)
-               dev_dbg(dev, "\tInterrupt Edge Falling\n");
-       if (intfcaps & TPM_INTF_INT_EDGE_RISING)
-               dev_dbg(dev, "\tInterrupt Edge Rising\n");
-       if (intfcaps & TPM_INTF_INT_LEVEL_LOW)
-               dev_dbg(dev, "\tInterrupt Level Low\n");
-       if (intfcaps & TPM_INTF_INT_LEVEL_HIGH)
-               dev_dbg(dev, "\tInterrupt Level High\n");
-       if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT)
-               dev_dbg(dev, "\tLocality Change Int Support\n");
-       if (intfcaps & TPM_INTF_STS_VALID_INT)
-               dev_dbg(dev, "\tSts Valid Int Support\n");
-       if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
-               dev_dbg(dev, "\tData Avail Int Support\n");
-
-       /* Very early on issue a command to the TPM in polling mode to make
-        * sure it works. May as well use that command to set the proper
-        *  timeouts for the driver.
-        */
-       if (tpm_get_timeouts(chip)) {
-               dev_err(dev, "Could not get TPM timeouts and durations\n");
-               rc = -ENODEV;
-               goto out_err;
-       }
-
-       /* INTERRUPT Setup */
-       init_waitqueue_head(&chip->vendor.read_queue);
-       init_waitqueue_head(&chip->vendor.int_queue);
-       if (interrupts && tpm_info->irq != -1) {
-               if (tpm_info->irq) {
-                       tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED,
-                                                tpm_info->irq);
-                       if (!chip->vendor.irq)
-                               dev_err(chip->pdev, FW_BUG
-                                       "TPM interrupt not working, polling instead\n");
-               } else
-                       tpm_tis_probe_irq(chip, intmask);
-       }
+               phy->priv.flags |= TPM_TIS_ITPM_POSSIBLE;
 
-       if (chip->flags & TPM_CHIP_FLAG_TPM2) {
-               rc = tpm2_do_selftest(chip);
-               if (rc == TPM2_RC_INITIALIZE) {
-                       dev_warn(dev, "Firmware has not started TPM\n");
-                       rc  = tpm2_startup(chip, TPM2_SU_CLEAR);
-                       if (!rc)
-                               rc = tpm2_do_selftest(chip);
-               }
-
-               if (rc) {
-                       dev_err(dev, "TPM self test failed\n");
-                       if (rc > 0)
-                               rc = -ENODEV;
-                       goto out_err;
-               }
-       } else {
-               if (tpm_do_selftest(chip)) {
-                       dev_err(dev, "TPM self test failed\n");
-                       rc = -ENODEV;
-                       goto out_err;
-               }
-       }
-
-       return tpm_chip_register(chip);
-out_err:
-       tpm_tis_remove(chip);
-       return rc;
+       return tpm_tis_core_init(dev, &phy->priv, irq, &tpm_tcg,
+                                acpi_dev_handle);
 }
 
-#ifdef CONFIG_PM_SLEEP
-static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
-{
-       u32 intmask;
-
-       /* reenable interrupts that device may have lost or
-          BIOS/firmware may have disabled */
-       iowrite8(chip->vendor.irq, chip->vendor.iobase +
-                TPM_INT_VECTOR(chip->vendor.locality));
-
-       intmask =
-           ioread32(chip->vendor.iobase +
-                    TPM_INT_ENABLE(chip->vendor.locality));
-
-       intmask |= TPM_INTF_CMD_READY_INT
-           | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
-           | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE;
-
-       iowrite32(intmask,
-                 chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
-}
-
-static int tpm_tis_resume(struct device *dev)
-{
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       int ret;
-
-       if (chip->vendor.irq)
-               tpm_tis_reenable_interrupts(chip);
-
-       ret = tpm_pm_resume(dev);
-       if (ret)
-               return ret;
-
-       /* TPM 1.2 requires self-test on resume. This function actually returns
-        * an error code but for unknown reason it isn't handled.
-        */
-       if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
-               tpm_do_selftest(chip);
-
-       return 0;
-}
-#endif
-
 static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume);
 
 static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
@@ -1058,12 +363,6 @@ static struct platform_driver tis_drv = {
        },
 };
 
-static bool force;
-#ifdef CONFIG_X86
-module_param(force, bool, 0444);
-MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry");
-#endif
-
 static int tpm_tis_force_device(void)
 {
        struct platform_device *pdev;
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
new file mode 100644 (file)
index 0000000..d66f51b
--- /dev/null
@@ -0,0 +1,835 @@
+/*
+ * Copyright (C) 2005, 2006 IBM Corporation
+ * Copyright (C) 2014, 2015 Intel Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Kylene Hall <kjhall@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 device driver implements the TPM interface as defined in
+ * the TCG TPM Interface Spec version 1.2, revision 1.0.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pnp.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/acpi.h>
+#include <linux/freezer.h>
+#include "tpm.h"
+#include "tpm_tis_core.h"
+
+/* Before we attempt to access the TPM we must see that the valid bit is set.
+ * The specification says that this bit is 0 at reset and remains 0 until the
+ * 'TPM has gone through its self test and initialization and has established
+ * correct values in the other bits.'
+ */
+static int wait_startup(struct tpm_chip *chip, int l)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       unsigned long stop = jiffies + chip->timeout_a;
+
+       do {
+               int rc;
+               u8 access;
+
+               rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access);
+               if (rc < 0)
+                       return rc;
+
+               if (access & TPM_ACCESS_VALID)
+                       return 0;
+               msleep(TPM_TIMEOUT);
+       } while (time_before(jiffies, stop));
+       return -1;
+}
+
+static int check_locality(struct tpm_chip *chip, int l)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       int rc;
+       u8 access;
+
+       rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access);
+       if (rc < 0)
+               return rc;
+
+       if ((access & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
+           (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
+               return priv->locality = l;
+
+       return -1;
+}
+
+static void release_locality(struct tpm_chip *chip, int l, int force)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       int rc;
+       u8 access;
+
+       rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access);
+       if (rc < 0)
+               return;
+
+       if (force || (access &
+                     (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
+           (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID))
+               tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY);
+
+}
+
+static int request_locality(struct tpm_chip *chip, int l)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       unsigned long stop, timeout;
+       long rc;
+
+       if (check_locality(chip, l) >= 0)
+               return l;
+
+       rc = tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_REQUEST_USE);
+       if (rc < 0)
+               return rc;
+
+       stop = jiffies + chip->timeout_a;
+
+       if (chip->flags & TPM_CHIP_FLAG_IRQ) {
+again:
+               timeout = stop - jiffies;
+               if ((long)timeout <= 0)
+                       return -1;
+               rc = wait_event_interruptible_timeout(priv->int_queue,
+                                                     (check_locality
+                                                      (chip, l) >= 0),
+                                                     timeout);
+               if (rc > 0)
+                       return l;
+               if (rc == -ERESTARTSYS && freezing(current)) {
+                       clear_thread_flag(TIF_SIGPENDING);
+                       goto again;
+               }
+       } else {
+               /* wait for burstcount */
+               do {
+                       if (check_locality(chip, l) >= 0)
+                               return l;
+                       msleep(TPM_TIMEOUT);
+               } while (time_before(jiffies, stop));
+       }
+       return -1;
+}
+
+static u8 tpm_tis_status(struct tpm_chip *chip)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       int rc;
+       u8 status;
+
+       rc = tpm_tis_read8(priv, TPM_STS(priv->locality), &status);
+       if (rc < 0)
+               return 0;
+
+       return status;
+}
+
+static void tpm_tis_ready(struct tpm_chip *chip)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+
+       /* this causes the current command to be aborted */
+       tpm_tis_write8(priv, TPM_STS(priv->locality), TPM_STS_COMMAND_READY);
+}
+
+static int get_burstcount(struct tpm_chip *chip)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       unsigned long stop;
+       int burstcnt, rc;
+       u32 value;
+
+       /* wait for burstcount */
+       /* which timeout value, spec has 2 answers (c & d) */
+       stop = jiffies + chip->timeout_d;
+       do {
+               rc = tpm_tis_read32(priv, TPM_STS(priv->locality), &value);
+               if (rc < 0)
+                       return rc;
+
+               burstcnt = (value >> 8) & 0xFFFF;
+               if (burstcnt)
+                       return burstcnt;
+               msleep(TPM_TIMEOUT);
+       } while (time_before(jiffies, stop));
+       return -EBUSY;
+}
+
+static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       int size = 0, burstcnt, rc;
+
+       while (size < count &&
+              wait_for_tpm_stat(chip,
+                                TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+                                chip->timeout_c,
+                                &priv->read_queue, true) == 0) {
+               burstcnt = min_t(int, get_burstcount(chip), count - size);
+
+               rc = tpm_tis_read_bytes(priv, TPM_DATA_FIFO(priv->locality),
+                                       burstcnt, buf + size);
+               if (rc < 0)
+                       return rc;
+
+               size += burstcnt;
+       }
+       return size;
+}
+
+static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       int size = 0;
+       int expected, status;
+
+       if (count < TPM_HEADER_SIZE) {
+               size = -EIO;
+               goto out;
+       }
+
+       size = recv_data(chip, buf, TPM_HEADER_SIZE);
+       /* read first 10 bytes, including tag, paramsize, and result */
+       if (size < TPM_HEADER_SIZE) {
+               dev_err(&chip->dev, "Unable to read header\n");
+               goto out;
+       }
+
+       expected = be32_to_cpu(*(__be32 *) (buf + 2));
+       if (expected > count) {
+               size = -EIO;
+               goto out;
+       }
+
+       size += recv_data(chip, &buf[TPM_HEADER_SIZE],
+                         expected - TPM_HEADER_SIZE);
+       if (size < expected) {
+               dev_err(&chip->dev, "Unable to read remainder of result\n");
+               size = -ETIME;
+               goto out;
+       }
+
+       wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
+                         &priv->int_queue, false);
+       status = tpm_tis_status(chip);
+       if (status & TPM_STS_DATA_AVAIL) {      /* retry? */
+               dev_err(&chip->dev, "Error left over data\n");
+               size = -EIO;
+               goto out;
+       }
+
+out:
+       tpm_tis_ready(chip);
+       release_locality(chip, priv->locality, 0);
+       return size;
+}
+
+/*
+ * If interrupts are used (signaled by an irq set in the vendor structure)
+ * tpm.c can skip polling for the data to be available as the interrupt is
+ * waited for here
+ */
+static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       int rc, status, burstcnt;
+       size_t count = 0;
+       bool itpm = priv->flags & TPM_TIS_ITPM_POSSIBLE;
+
+       if (request_locality(chip, 0) < 0)
+               return -EBUSY;
+
+       status = tpm_tis_status(chip);
+       if ((status & TPM_STS_COMMAND_READY) == 0) {
+               tpm_tis_ready(chip);
+               if (wait_for_tpm_stat
+                   (chip, TPM_STS_COMMAND_READY, chip->timeout_b,
+                    &priv->int_queue, false) < 0) {
+                       rc = -ETIME;
+                       goto out_err;
+               }
+       }
+
+       while (count < len - 1) {
+               burstcnt = min_t(int, get_burstcount(chip), len - count - 1);
+               rc = tpm_tis_write_bytes(priv, TPM_DATA_FIFO(priv->locality),
+                                        burstcnt, buf + count);
+               if (rc < 0)
+                       goto out_err;
+
+               count += burstcnt;
+
+               wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
+                                 &priv->int_queue, false);
+               status = tpm_tis_status(chip);
+               if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
+                       rc = -EIO;
+                       goto out_err;
+               }
+       }
+
+       /* write last byte */
+       rc = tpm_tis_write8(priv, TPM_DATA_FIFO(priv->locality), buf[count]);
+       if (rc < 0)
+               goto out_err;
+
+       wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
+                         &priv->int_queue, false);
+       status = tpm_tis_status(chip);
+       if (!itpm && (status & TPM_STS_DATA_EXPECT) != 0) {
+               rc = -EIO;
+               goto out_err;
+       }
+
+       return 0;
+
+out_err:
+       tpm_tis_ready(chip);
+       release_locality(chip, priv->locality, 0);
+       return rc;
+}
+
+static void disable_interrupts(struct tpm_chip *chip)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       u32 intmask;
+       int rc;
+
+       rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask);
+       if (rc < 0)
+               intmask = 0;
+
+       intmask &= ~TPM_GLOBAL_INT_ENABLE;
+       rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
+
+       devm_free_irq(chip->dev.parent, priv->irq, chip);
+       priv->irq = 0;
+       chip->flags &= ~TPM_CHIP_FLAG_IRQ;
+}
+
+/*
+ * If interrupts are used (signaled by an irq set in the vendor structure)
+ * tpm.c can skip polling for the data to be available as the interrupt is
+ * waited for here
+ */
+static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       int rc;
+       u32 ordinal;
+       unsigned long dur;
+
+       rc = tpm_tis_send_data(chip, buf, len);
+       if (rc < 0)
+               return rc;
+
+       /* go and do it */
+       rc = tpm_tis_write8(priv, TPM_STS(priv->locality), TPM_STS_GO);
+       if (rc < 0)
+               goto out_err;
+
+       if (chip->flags & TPM_CHIP_FLAG_IRQ) {
+               ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+
+               if (chip->flags & TPM_CHIP_FLAG_TPM2)
+                       dur = tpm2_calc_ordinal_duration(chip, ordinal);
+               else
+                       dur = tpm_calc_ordinal_duration(chip, ordinal);
+
+               if (wait_for_tpm_stat
+                   (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, dur,
+                    &priv->read_queue, false) < 0) {
+                       rc = -ETIME;
+                       goto out_err;
+               }
+       }
+       return len;
+out_err:
+       tpm_tis_ready(chip);
+       release_locality(chip, priv->locality, 0);
+       return rc;
+}
+
+static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+       int rc, irq;
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+
+       if (!(chip->flags & TPM_CHIP_FLAG_IRQ) || priv->irq_tested)
+               return tpm_tis_send_main(chip, buf, len);
+
+       /* Verify receipt of the expected IRQ */
+       irq = priv->irq;
+       priv->irq = 0;
+       chip->flags &= ~TPM_CHIP_FLAG_IRQ;
+       rc = tpm_tis_send_main(chip, buf, len);
+       priv->irq = irq;
+       chip->flags |= TPM_CHIP_FLAG_IRQ;
+       if (!priv->irq_tested)
+               msleep(1);
+       if (!priv->irq_tested)
+               disable_interrupts(chip);
+       priv->irq_tested = true;
+       return rc;
+}
+
+struct tis_vendor_timeout_override {
+       u32 did_vid;
+       unsigned long timeout_us[4];
+};
+
+static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
+       /* Atmel 3204 */
+       { 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
+                       (TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
+};
+
+static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
+                                   unsigned long *timeout_cap)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       int i, rc;
+       u32 did_vid;
+
+       rc = tpm_tis_read32(priv, TPM_DID_VID(0), &did_vid);
+       if (rc < 0)
+               return rc;
+
+       for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
+               if (vendor_timeout_overrides[i].did_vid != did_vid)
+                       continue;
+               memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
+                      sizeof(vendor_timeout_overrides[i].timeout_us));
+               return true;
+       }
+
+       return false;
+}
+
+/*
+ * Early probing for iTPM with STS_DATA_EXPECT flaw.
+ * Try sending command without itpm flag set and if that
+ * fails, repeat with itpm flag set.
+ */
+static int probe_itpm(struct tpm_chip *chip)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       int rc = 0;
+       u8 cmd_getticks[] = {
+               0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a,
+               0x00, 0x00, 0x00, 0xf1
+       };
+       size_t len = sizeof(cmd_getticks);
+       bool itpm;
+       u16 vendor;
+
+       rc = tpm_tis_read16(priv, TPM_DID_VID(0), &vendor);
+       if (rc < 0)
+               return rc;
+
+       /* probe only iTPMS */
+       if (vendor != TPM_VID_INTEL)
+               return 0;
+
+       itpm = false;
+
+       rc = tpm_tis_send_data(chip, cmd_getticks, len);
+       if (rc == 0)
+               goto out;
+
+       tpm_tis_ready(chip);
+       release_locality(chip, priv->locality, 0);
+
+       itpm = true;
+
+       rc = tpm_tis_send_data(chip, cmd_getticks, len);
+       if (rc == 0) {
+               dev_info(&chip->dev, "Detected an iTPM.\n");
+               rc = 1;
+       } else
+               rc = -EFAULT;
+
+out:
+       tpm_tis_ready(chip);
+       release_locality(chip, priv->locality, 0);
+
+       return rc;
+}
+
+static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+
+       switch (priv->manufacturer_id) {
+       case TPM_VID_WINBOND:
+               return ((status == TPM_STS_VALID) ||
+                       (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY)));
+       case TPM_VID_STM:
+               return (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY));
+       default:
+               return (status == TPM_STS_COMMAND_READY);
+       }
+}
+
+static irqreturn_t tis_int_handler(int dummy, void *dev_id)
+{
+       struct tpm_chip *chip = dev_id;
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       u32 interrupt;
+       int i, rc;
+
+       rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt);
+       if (rc < 0)
+               return IRQ_NONE;
+
+       if (interrupt == 0)
+               return IRQ_NONE;
+
+       priv->irq_tested = true;
+       if (interrupt & TPM_INTF_DATA_AVAIL_INT)
+               wake_up_interruptible(&priv->read_queue);
+       if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT)
+               for (i = 0; i < 5; i++)
+                       if (check_locality(chip, i) >= 0)
+                               break;
+       if (interrupt &
+           (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT |
+            TPM_INTF_CMD_READY_INT))
+               wake_up_interruptible(&priv->int_queue);
+
+       /* Clear interrupts handled with TPM_EOI */
+       rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), interrupt);
+       if (rc < 0)
+               return IRQ_NONE;
+
+       tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt);
+       return IRQ_HANDLED;
+}
+
+/* Register the IRQ and issue a command that will cause an interrupt. If an
+ * irq is seen then leave the chip setup for IRQ operation, otherwise reverse
+ * everything and leave in polling mode. Returns 0 on success.
+ */
+static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask,
+                                   int flags, int irq)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       u8 original_int_vec;
+       int rc;
+       u32 int_status;
+
+       if (devm_request_irq(chip->dev.parent, irq, tis_int_handler, flags,
+                            dev_name(&chip->dev), chip) != 0) {
+               dev_info(&chip->dev, "Unable to request irq: %d for probe\n",
+                        irq);
+               return -1;
+       }
+       priv->irq = irq;
+
+       rc = tpm_tis_read8(priv, TPM_INT_VECTOR(priv->locality),
+                          &original_int_vec);
+       if (rc < 0)
+               return rc;
+
+       rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), irq);
+       if (rc < 0)
+               return rc;
+
+       rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &int_status);
+       if (rc < 0)
+               return rc;
+
+       /* Clear all existing */
+       rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), int_status);
+       if (rc < 0)
+               return rc;
+
+       /* Turn on */
+       rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality),
+                            intmask | TPM_GLOBAL_INT_ENABLE);
+       if (rc < 0)
+               return rc;
+
+       priv->irq_tested = false;
+
+       /* Generate an interrupt by having the core call through to
+        * tpm_tis_send
+        */
+       if (chip->flags & TPM_CHIP_FLAG_TPM2)
+               tpm2_gen_interrupt(chip);
+       else
+               tpm_gen_interrupt(chip);
+
+       /* tpm_tis_send will either confirm the interrupt is working or it
+        * will call disable_irq which undoes all of the above.
+        */
+       if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) {
+               rc = tpm_tis_write8(priv, original_int_vec,
+                               TPM_INT_VECTOR(priv->locality));
+               if (rc < 0)
+                       return rc;
+
+               return 1;
+       }
+
+       return 0;
+}
+
+/* Try to find the IRQ the TPM is using. This is for legacy x86 systems that
+ * do not have ACPI/etc. We typically expect the interrupt to be declared if
+ * present.
+ */
+static void tpm_tis_probe_irq(struct tpm_chip *chip, u32 intmask)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       u8 original_int_vec;
+       int i, rc;
+
+       rc = tpm_tis_read8(priv, TPM_INT_VECTOR(priv->locality),
+                          &original_int_vec);
+       if (rc < 0)
+               return;
+
+       if (!original_int_vec) {
+               if (IS_ENABLED(CONFIG_X86))
+                       for (i = 3; i <= 15; i++)
+                               if (!tpm_tis_probe_irq_single(chip, intmask, 0,
+                                                             i))
+                                       return;
+       } else if (!tpm_tis_probe_irq_single(chip, intmask, 0,
+                                            original_int_vec))
+               return;
+}
+
+void tpm_tis_remove(struct tpm_chip *chip)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       u32 reg = TPM_INT_ENABLE(priv->locality);
+       u32 interrupt;
+       int rc;
+
+       rc = tpm_tis_read32(priv, reg, &interrupt);
+       if (rc < 0)
+               interrupt = 0;
+
+       tpm_tis_write32(priv, reg, ~TPM_GLOBAL_INT_ENABLE & interrupt);
+       release_locality(chip, priv->locality, 1);
+}
+EXPORT_SYMBOL_GPL(tpm_tis_remove);
+
+static const struct tpm_class_ops tpm_tis = {
+       .flags = TPM_OPS_AUTO_STARTUP,
+       .status = tpm_tis_status,
+       .recv = tpm_tis_recv,
+       .send = tpm_tis_send,
+       .cancel = tpm_tis_ready,
+       .update_timeouts = tpm_tis_update_timeouts,
+       .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+       .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+       .req_canceled = tpm_tis_req_canceled,
+};
+
+int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
+                     const struct tpm_tis_phy_ops *phy_ops,
+                     acpi_handle acpi_dev_handle)
+{
+       u32 vendor, intfcaps, intmask;
+       u8 rid;
+       int rc, probe;
+       struct tpm_chip *chip;
+
+       chip = tpmm_chip_alloc(dev, &tpm_tis);
+       if (IS_ERR(chip))
+               return PTR_ERR(chip);
+
+#ifdef CONFIG_ACPI
+       chip->acpi_dev_handle = acpi_dev_handle;
+#endif
+
+       /* Maximum timeouts */
+       chip->timeout_a = msecs_to_jiffies(TIS_TIMEOUT_A_MAX);
+       chip->timeout_b = msecs_to_jiffies(TIS_TIMEOUT_B_MAX);
+       chip->timeout_c = msecs_to_jiffies(TIS_TIMEOUT_C_MAX);
+       chip->timeout_d = msecs_to_jiffies(TIS_TIMEOUT_D_MAX);
+       priv->phy_ops = phy_ops;
+       dev_set_drvdata(&chip->dev, priv);
+
+       if (wait_startup(chip, 0) != 0) {
+               rc = -ENODEV;
+               goto out_err;
+       }
+
+       /* Take control of the TPM's interrupt hardware and shut it off */
+       rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask);
+       if (rc < 0)
+               goto out_err;
+
+       intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT |
+                  TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT;
+       intmask &= ~TPM_GLOBAL_INT_ENABLE;
+       tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
+
+       if (request_locality(chip, 0) != 0) {
+               rc = -ENODEV;
+               goto out_err;
+       }
+
+       rc = tpm2_probe(chip);
+       if (rc)
+               goto out_err;
+
+       rc = tpm_tis_read32(priv, TPM_DID_VID(0), &vendor);
+       if (rc < 0)
+               goto out_err;
+
+       priv->manufacturer_id = vendor;
+
+       rc = tpm_tis_read8(priv, TPM_RID(0), &rid);
+       if (rc < 0)
+               goto out_err;
+
+       dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n",
+                (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2",
+                vendor >> 16, rid);
+
+       if (!(priv->flags & TPM_TIS_ITPM_POSSIBLE)) {
+               probe = probe_itpm(chip);
+               if (probe < 0) {
+                       rc = -ENODEV;
+                       goto out_err;
+               }
+
+               if (!!probe)
+                       priv->flags |= TPM_TIS_ITPM_POSSIBLE;
+       }
+
+       /* Figure out the capabilities */
+       rc = tpm_tis_read32(priv, TPM_INTF_CAPS(priv->locality), &intfcaps);
+       if (rc < 0)
+               goto out_err;
+
+       dev_dbg(dev, "TPM interface capabilities (0x%x):\n",
+               intfcaps);
+       if (intfcaps & TPM_INTF_BURST_COUNT_STATIC)
+               dev_dbg(dev, "\tBurst Count Static\n");
+       if (intfcaps & TPM_INTF_CMD_READY_INT)
+               dev_dbg(dev, "\tCommand Ready Int Support\n");
+       if (intfcaps & TPM_INTF_INT_EDGE_FALLING)
+               dev_dbg(dev, "\tInterrupt Edge Falling\n");
+       if (intfcaps & TPM_INTF_INT_EDGE_RISING)
+               dev_dbg(dev, "\tInterrupt Edge Rising\n");
+       if (intfcaps & TPM_INTF_INT_LEVEL_LOW)
+               dev_dbg(dev, "\tInterrupt Level Low\n");
+       if (intfcaps & TPM_INTF_INT_LEVEL_HIGH)
+               dev_dbg(dev, "\tInterrupt Level High\n");
+       if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT)
+               dev_dbg(dev, "\tLocality Change Int Support\n");
+       if (intfcaps & TPM_INTF_STS_VALID_INT)
+               dev_dbg(dev, "\tSts Valid Int Support\n");
+       if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
+               dev_dbg(dev, "\tData Avail Int Support\n");
+
+       /* Very early on issue a command to the TPM in polling mode to make
+        * sure it works. May as well use that command to set the proper
+        *  timeouts for the driver.
+        */
+       if (tpm_get_timeouts(chip)) {
+               dev_err(dev, "Could not get TPM timeouts and durations\n");
+               rc = -ENODEV;
+               goto out_err;
+       }
+
+       /* INTERRUPT Setup */
+       init_waitqueue_head(&priv->read_queue);
+       init_waitqueue_head(&priv->int_queue);
+       if (irq != -1) {
+               if (irq) {
+                       tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED,
+                                                irq);
+                       if (!(chip->flags & TPM_CHIP_FLAG_IRQ))
+                               dev_err(&chip->dev, FW_BUG
+                                       "TPM interrupt not working, polling instead\n");
+               } else {
+                       tpm_tis_probe_irq(chip, intmask);
+               }
+       }
+
+       return tpm_chip_register(chip);
+out_err:
+       tpm_tis_remove(chip);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_tis_core_init);
+
+#ifdef CONFIG_PM_SLEEP
+static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
+{
+       struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+       u32 intmask;
+       int rc;
+
+       /* reenable interrupts that device may have lost or
+        * BIOS/firmware may have disabled
+        */
+       rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), priv->irq);
+       if (rc < 0)
+               return;
+
+       rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask);
+       if (rc < 0)
+               return;
+
+       intmask |= TPM_INTF_CMD_READY_INT
+           | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
+           | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE;
+
+       tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
+}
+
+int tpm_tis_resume(struct device *dev)
+{
+       struct tpm_chip *chip = dev_get_drvdata(dev);
+       int ret;
+
+       if (chip->flags & TPM_CHIP_FLAG_IRQ)
+               tpm_tis_reenable_interrupts(chip);
+
+       ret = tpm_pm_resume(dev);
+       if (ret)
+               return ret;
+
+       /* TPM 1.2 requires self-test on resume. This function actually returns
+        * an error code but for unknown reason it isn't handled.
+        */
+       if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
+               tpm_do_selftest(chip);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tpm_tis_resume);
+#endif
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h
new file mode 100644 (file)
index 0000000..9191aab
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2005, 2006 IBM Corporation
+ * Copyright (C) 2014, 2015 Intel Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Kylene Hall <kjhall@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 device driver implements the TPM interface as defined in
+ * the TCG TPM Interface Spec version 1.2, revision 1.0.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+
+#ifndef __TPM_TIS_CORE_H__
+#define __TPM_TIS_CORE_H__
+
+#include "tpm.h"
+
+enum tis_access {
+       TPM_ACCESS_VALID = 0x80,
+       TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
+       TPM_ACCESS_REQUEST_PENDING = 0x04,
+       TPM_ACCESS_REQUEST_USE = 0x02,
+};
+
+enum tis_status {
+       TPM_STS_VALID = 0x80,
+       TPM_STS_COMMAND_READY = 0x40,
+       TPM_STS_GO = 0x20,
+       TPM_STS_DATA_AVAIL = 0x10,
+       TPM_STS_DATA_EXPECT = 0x08,
+};
+
+enum tis_int_flags {
+       TPM_GLOBAL_INT_ENABLE = 0x80000000,
+       TPM_INTF_BURST_COUNT_STATIC = 0x100,
+       TPM_INTF_CMD_READY_INT = 0x080,
+       TPM_INTF_INT_EDGE_FALLING = 0x040,
+       TPM_INTF_INT_EDGE_RISING = 0x020,
+       TPM_INTF_INT_LEVEL_LOW = 0x010,
+       TPM_INTF_INT_LEVEL_HIGH = 0x008,
+       TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
+       TPM_INTF_STS_VALID_INT = 0x002,
+       TPM_INTF_DATA_AVAIL_INT = 0x001,
+};
+
+enum tis_defaults {
+       TIS_MEM_LEN = 0x5000,
+       TIS_SHORT_TIMEOUT = 750,        /* ms */
+       TIS_LONG_TIMEOUT = 2000,        /* 2 sec */
+};
+
+/* Some timeout values are needed before it is known whether the chip is
+ * TPM 1.0 or TPM 2.0.
+ */
+#define TIS_TIMEOUT_A_MAX      max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_A)
+#define TIS_TIMEOUT_B_MAX      max(TIS_LONG_TIMEOUT, TPM2_TIMEOUT_B)
+#define TIS_TIMEOUT_C_MAX      max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_C)
+#define TIS_TIMEOUT_D_MAX      max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_D)
+
+#define        TPM_ACCESS(l)                   (0x0000 | ((l) << 12))
+#define        TPM_INT_ENABLE(l)               (0x0008 | ((l) << 12))
+#define        TPM_INT_VECTOR(l)               (0x000C | ((l) << 12))
+#define        TPM_INT_STATUS(l)               (0x0010 | ((l) << 12))
+#define        TPM_INTF_CAPS(l)                (0x0014 | ((l) << 12))
+#define        TPM_STS(l)                      (0x0018 | ((l) << 12))
+#define        TPM_STS3(l)                     (0x001b | ((l) << 12))
+#define        TPM_DATA_FIFO(l)                (0x0024 | ((l) << 12))
+
+#define        TPM_DID_VID(l)                  (0x0F00 | ((l) << 12))
+#define        TPM_RID(l)                      (0x0F04 | ((l) << 12))
+
+enum tpm_tis_flags {
+       TPM_TIS_ITPM_POSSIBLE           = BIT(0),
+};
+
+struct tpm_tis_data {
+       u16 manufacturer_id;
+       int locality;
+       int irq;
+       bool irq_tested;
+       unsigned int flags;
+       wait_queue_head_t int_queue;
+       wait_queue_head_t read_queue;
+       const struct tpm_tis_phy_ops *phy_ops;
+};
+
+struct tpm_tis_phy_ops {
+       int (*read_bytes)(struct tpm_tis_data *data, u32 addr, u16 len,
+                         u8 *result);
+       int (*write_bytes)(struct tpm_tis_data *data, u32 addr, u16 len,
+                          u8 *value);
+       int (*read16)(struct tpm_tis_data *data, u32 addr, u16 *result);
+       int (*read32)(struct tpm_tis_data *data, u32 addr, u32 *result);
+       int (*write32)(struct tpm_tis_data *data, u32 addr, u32 src);
+};
+
+static inline int tpm_tis_read_bytes(struct tpm_tis_data *data, u32 addr,
+                                    u16 len, u8 *result)
+{
+       return data->phy_ops->read_bytes(data, addr, len, result);
+}
+
+static inline int tpm_tis_read8(struct tpm_tis_data *data, u32 addr, u8 *result)
+{
+       return data->phy_ops->read_bytes(data, addr, 1, result);
+}
+
+static inline int tpm_tis_read16(struct tpm_tis_data *data, u32 addr,
+                                u16 *result)
+{
+       return data->phy_ops->read16(data, addr, result);
+}
+
+static inline int tpm_tis_read32(struct tpm_tis_data *data, u32 addr,
+                                u32 *result)
+{
+       return data->phy_ops->read32(data, addr, result);
+}
+
+static inline int tpm_tis_write_bytes(struct tpm_tis_data *data, u32 addr,
+                                     u16 len, u8 *value)
+{
+       return data->phy_ops->write_bytes(data, addr, len, value);
+}
+
+static inline int tpm_tis_write8(struct tpm_tis_data *data, u32 addr, u8 value)
+{
+       return data->phy_ops->write_bytes(data, addr, 1, &value);
+}
+
+static inline int tpm_tis_write32(struct tpm_tis_data *data, u32 addr,
+                                 u32 value)
+{
+       return data->phy_ops->write32(data, addr, value);
+}
+
+void tpm_tis_remove(struct tpm_chip *chip);
+int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
+                     const struct tpm_tis_phy_ops *phy_ops,
+                     acpi_handle acpi_dev_handle);
+
+#ifdef CONFIG_PM_SLEEP
+int tpm_tis_resume(struct device *dev);
+#endif
+
+#endif
diff --git a/drivers/char/tpm/tpm_tis_spi.c b/drivers/char/tpm/tpm_tis_spi.c
new file mode 100644 (file)
index 0000000..dbaad9c
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2015 Infineon Technologies AG
+ * Copyright (C) 2016 STMicroelectronics SAS
+ *
+ * Authors:
+ * Peter Huewe <peter.huewe@infineon.com>
+ * Christophe Ricard <christophe-h.ricard@st.com>
+ *
+ * Maintained by: <tpmdd-devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * This device driver implements the TPM interface as defined in
+ * the TCG TPM Interface Spec version 1.3, revision 27 via _raw/native
+ * SPI access_.
+ *
+ * It is based on the original tpm_tis device driver from Leendert van
+ * Dorn and Kyleen Hall and Jarko Sakkinnen.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/acpi.h>
+#include <linux/freezer.h>
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/tpm.h>
+#include "tpm.h"
+#include "tpm_tis_core.h"
+
+#define MAX_SPI_FRAMESIZE 64
+
+struct tpm_tis_spi_phy {
+       struct tpm_tis_data priv;
+       struct spi_device *spi_device;
+
+       u8 tx_buf[MAX_SPI_FRAMESIZE + 4];
+       u8 rx_buf[MAX_SPI_FRAMESIZE + 4];
+};
+
+static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data)
+{
+       return container_of(data, struct tpm_tis_spi_phy, priv);
+}
+
+static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
+                                 u16 len, u8 *result)
+{
+       struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
+       int ret, i;
+       struct spi_message m;
+       struct spi_transfer spi_xfer = {
+               .tx_buf = phy->tx_buf,
+               .rx_buf = phy->rx_buf,
+               .len = 4,
+       };
+
+       if (len > MAX_SPI_FRAMESIZE)
+               return -ENOMEM;
+
+       phy->tx_buf[0] = 0x80 | (len - 1);
+       phy->tx_buf[1] = 0xd4;
+       phy->tx_buf[2] = (addr >> 8)  & 0xFF;
+       phy->tx_buf[3] = addr         & 0xFF;
+
+       spi_xfer.cs_change = 1;
+       spi_message_init(&m);
+       spi_message_add_tail(&spi_xfer, &m);
+
+       spi_bus_lock(phy->spi_device->master);
+       ret = spi_sync_locked(phy->spi_device, &m);
+       if (ret < 0)
+               goto exit;
+
+       memset(phy->tx_buf, 0, len);
+
+       /* According to TCG PTP specification, if there is no TPM present at
+        * all, then the design has a weak pull-up on MISO. If a TPM is not
+        * present, a pull-up on MISO means that the SB controller sees a 1,
+        * and will latch in 0xFF on the read.
+        */
+       for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) {
+               spi_xfer.len = 1;
+               spi_message_init(&m);
+               spi_message_add_tail(&spi_xfer, &m);
+               ret = spi_sync_locked(phy->spi_device, &m);
+               if (ret < 0)
+                       goto exit;
+       }
+
+       spi_xfer.cs_change = 0;
+       spi_xfer.len = len;
+       spi_xfer.rx_buf = result;
+
+       spi_message_init(&m);
+       spi_message_add_tail(&spi_xfer, &m);
+       ret = spi_sync_locked(phy->spi_device, &m);
+
+exit:
+       spi_bus_unlock(phy->spi_device->master);
+       return ret;
+}
+
+static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr,
+                                  u16 len, u8 *value)
+{
+       struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
+       int ret, i;
+       struct spi_message m;
+       struct spi_transfer spi_xfer = {
+               .tx_buf = phy->tx_buf,
+               .rx_buf = phy->rx_buf,
+               .len = 4,
+       };
+
+       if (len > MAX_SPI_FRAMESIZE)
+               return -ENOMEM;
+
+       phy->tx_buf[0] = len - 1;
+       phy->tx_buf[1] = 0xd4;
+       phy->tx_buf[2] = (addr >> 8)  & 0xFF;
+       phy->tx_buf[3] = addr         & 0xFF;
+
+       spi_xfer.cs_change = 1;
+       spi_message_init(&m);
+       spi_message_add_tail(&spi_xfer, &m);
+
+       spi_bus_lock(phy->spi_device->master);
+       ret = spi_sync_locked(phy->spi_device, &m);
+       if (ret < 0)
+               goto exit;
+
+       memset(phy->tx_buf, 0, len);
+
+       /* According to TCG PTP specification, if there is no TPM present at
+        * all, then the design has a weak pull-up on MISO. If a TPM is not
+        * present, a pull-up on MISO means that the SB controller sees a 1,
+        * and will latch in 0xFF on the read.
+        */
+       for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) {
+               spi_xfer.len = 1;
+               spi_message_init(&m);
+               spi_message_add_tail(&spi_xfer, &m);
+               ret = spi_sync_locked(phy->spi_device, &m);
+               if (ret < 0)
+                       goto exit;
+       }
+
+       spi_xfer.len = len;
+       spi_xfer.tx_buf = value;
+       spi_xfer.cs_change = 0;
+       spi_xfer.tx_buf = value;
+       spi_message_init(&m);
+       spi_message_add_tail(&spi_xfer, &m);
+       ret = spi_sync_locked(phy->spi_device, &m);
+
+exit:
+       spi_bus_unlock(phy->spi_device->master);
+       return ret;
+}
+
+static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
+{
+       int rc;
+
+       rc = data->phy_ops->read_bytes(data, addr, sizeof(u16), (u8 *)result);
+       if (!rc)
+               *result = le16_to_cpu(*result);
+       return rc;
+}
+
+static int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
+{
+       int rc;
+
+       rc = data->phy_ops->read_bytes(data, addr, sizeof(u32), (u8 *)result);
+       if (!rc)
+               *result = le32_to_cpu(*result);
+       return rc;
+}
+
+static int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value)
+{
+       value = cpu_to_le32(value);
+       return data->phy_ops->write_bytes(data, addr, sizeof(u32),
+                                          (u8 *)&value);
+}
+
+static const struct tpm_tis_phy_ops tpm_spi_phy_ops = {
+       .read_bytes = tpm_tis_spi_read_bytes,
+       .write_bytes = tpm_tis_spi_write_bytes,
+       .read16 = tpm_tis_spi_read16,
+       .read32 = tpm_tis_spi_read32,
+       .write32 = tpm_tis_spi_write32,
+};
+
+static int tpm_tis_spi_probe(struct spi_device *dev)
+{
+       struct tpm_tis_spi_phy *phy;
+
+       phy = devm_kzalloc(&dev->dev, sizeof(struct tpm_tis_spi_phy),
+                          GFP_KERNEL);
+       if (!phy)
+               return -ENOMEM;
+
+       phy->spi_device = dev;
+
+       return tpm_tis_core_init(&dev->dev, &phy->priv, -1, &tpm_spi_phy_ops,
+                                NULL);
+}
+
+static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume);
+
+static int tpm_tis_spi_remove(struct spi_device *dev)
+{
+       struct tpm_chip *chip = spi_get_drvdata(dev);
+
+       tpm_chip_unregister(chip);
+       tpm_tis_remove(chip);
+       return 0;
+}
+
+static const struct spi_device_id tpm_tis_spi_id[] = {
+       {"tpm_tis_spi", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(spi, tpm_tis_spi_id);
+
+static const struct of_device_id of_tis_spi_match[] = {
+       { .compatible = "st,st33htpm-spi", },
+       { .compatible = "infineon,slb9670", },
+       { .compatible = "tcg,tpm_tis-spi", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, of_tis_spi_match);
+
+static const struct acpi_device_id acpi_tis_spi_match[] = {
+       {"SMO0768", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(acpi, acpi_tis_spi_match);
+
+static struct spi_driver tpm_tis_spi_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "tpm_tis_spi",
+               .pm = &tpm_tis_pm,
+               .of_match_table = of_match_ptr(of_tis_spi_match),
+               .acpi_match_table = ACPI_PTR(acpi_tis_spi_match),
+       },
+       .probe = tpm_tis_spi_probe,
+       .remove = tpm_tis_spi_remove,
+       .id_table = tpm_tis_spi_id,
+};
+module_spi_driver(tpm_tis_spi_driver);
+
+MODULE_DESCRIPTION("TPM Driver for native SPI access");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
new file mode 100644 (file)
index 0000000..9a94033
--- /dev/null
@@ -0,0 +1,637 @@
+/*
+ * Copyright (C) 2015, 2016 IBM Corporation
+ *
+ * Author: Stefan Berger <stefanb@us.ibm.com>
+ *
+ * Maintained by: <tpmdd-devel@lists.sourceforge.net>
+ *
+ * Device driver for vTPM (vTPM proxy driver)
+ *
+ * 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/types.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/miscdevice.h>
+#include <linux/vtpm_proxy.h>
+#include <linux/file.h>
+#include <linux/anon_inodes.h>
+#include <linux/poll.h>
+#include <linux/compat.h>
+
+#include "tpm.h"
+
+#define VTPM_PROXY_REQ_COMPLETE_FLAG  BIT(0)
+
+struct proxy_dev {
+       struct tpm_chip *chip;
+
+       u32 flags;                   /* public API flags */
+
+       wait_queue_head_t wq;
+
+       struct mutex buf_lock;       /* protect buffer and flags */
+
+       long state;                  /* internal state */
+#define STATE_OPENED_FLAG        BIT(0)
+#define STATE_WAIT_RESPONSE_FLAG BIT(1)  /* waiting for emulator response */
+
+       size_t req_len;              /* length of queued TPM request */
+       size_t resp_len;             /* length of queued TPM response */
+       u8 buffer[TPM_BUFSIZE];      /* request/response buffer */
+
+       struct work_struct work;     /* task that retrieves TPM timeouts */
+};
+
+/* all supported flags */
+#define VTPM_PROXY_FLAGS_ALL  (VTPM_PROXY_FLAG_TPM2)
+
+static struct workqueue_struct *workqueue;
+
+static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev);
+
+/*
+ * Functions related to 'server side'
+ */
+
+/**
+ * vtpm_proxy_fops_read - Read TPM commands on 'server side'
+ *
+ * Return value:
+ *     Number of bytes read or negative error code
+ */
+static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
+                                   size_t count, loff_t *off)
+{
+       struct proxy_dev *proxy_dev = filp->private_data;
+       size_t len;
+       int sig, rc;
+
+       sig = wait_event_interruptible(proxy_dev->wq,
+               proxy_dev->req_len != 0 ||
+               !(proxy_dev->state & STATE_OPENED_FLAG));
+       if (sig)
+               return -EINTR;
+
+       mutex_lock(&proxy_dev->buf_lock);
+
+       if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
+               mutex_unlock(&proxy_dev->buf_lock);
+               return -EPIPE;
+       }
+
+       len = proxy_dev->req_len;
+
+       if (count < len) {
+               mutex_unlock(&proxy_dev->buf_lock);
+               pr_debug("Invalid size in recv: count=%zd, req_len=%zd\n",
+                        count, len);
+               return -EIO;
+       }
+
+       rc = copy_to_user(buf, proxy_dev->buffer, len);
+       memset(proxy_dev->buffer, 0, len);
+       proxy_dev->req_len = 0;
+
+       if (!rc)
+               proxy_dev->state |= STATE_WAIT_RESPONSE_FLAG;
+
+       mutex_unlock(&proxy_dev->buf_lock);
+
+       if (rc)
+               return -EFAULT;
+
+       return len;
+}
+
+/**
+ * vtpm_proxy_fops_write - Write TPM responses on 'server side'
+ *
+ * Return value:
+ *     Number of bytes read or negative error value
+ */
+static ssize_t vtpm_proxy_fops_write(struct file *filp, const char __user *buf,
+                                    size_t count, loff_t *off)
+{
+       struct proxy_dev *proxy_dev = filp->private_data;
+
+       mutex_lock(&proxy_dev->buf_lock);
+
+       if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
+               mutex_unlock(&proxy_dev->buf_lock);
+               return -EPIPE;
+       }
+
+       if (count > sizeof(proxy_dev->buffer) ||
+           !(proxy_dev->state & STATE_WAIT_RESPONSE_FLAG)) {
+               mutex_unlock(&proxy_dev->buf_lock);
+               return -EIO;
+       }
+
+       proxy_dev->state &= ~STATE_WAIT_RESPONSE_FLAG;
+
+       proxy_dev->req_len = 0;
+
+       if (copy_from_user(proxy_dev->buffer, buf, count)) {
+               mutex_unlock(&proxy_dev->buf_lock);
+               return -EFAULT;
+       }
+
+       proxy_dev->resp_len = count;
+
+       mutex_unlock(&proxy_dev->buf_lock);
+
+       wake_up_interruptible(&proxy_dev->wq);
+
+       return count;
+}
+
+/*
+ * vtpm_proxy_fops_poll: Poll status on 'server side'
+ *
+ * Return value:
+ *      Poll flags
+ */
+static unsigned int vtpm_proxy_fops_poll(struct file *filp, poll_table *wait)
+{
+       struct proxy_dev *proxy_dev = filp->private_data;
+       unsigned ret;
+
+       poll_wait(filp, &proxy_dev->wq, wait);
+
+       ret = POLLOUT;
+
+       mutex_lock(&proxy_dev->buf_lock);
+
+       if (proxy_dev->req_len)
+               ret |= POLLIN | POLLRDNORM;
+
+       if (!(proxy_dev->state & STATE_OPENED_FLAG))
+               ret |= POLLHUP;
+
+       mutex_unlock(&proxy_dev->buf_lock);
+
+       return ret;
+}
+
+/*
+ * vtpm_proxy_fops_open - Open vTPM device on 'server side'
+ *
+ * Called when setting up the anonymous file descriptor
+ */
+static void vtpm_proxy_fops_open(struct file *filp)
+{
+       struct proxy_dev *proxy_dev = filp->private_data;
+
+       proxy_dev->state |= STATE_OPENED_FLAG;
+}
+
+/**
+ * vtpm_proxy_fops_undo_open - counter-part to vtpm_fops_open
+ *
+ * Call to undo vtpm_proxy_fops_open
+ */
+static void vtpm_proxy_fops_undo_open(struct proxy_dev *proxy_dev)
+{
+       mutex_lock(&proxy_dev->buf_lock);
+
+       proxy_dev->state &= ~STATE_OPENED_FLAG;
+
+       mutex_unlock(&proxy_dev->buf_lock);
+
+       /* no more TPM responses -- wake up anyone waiting for them */
+       wake_up_interruptible(&proxy_dev->wq);
+}
+
+/*
+ * vtpm_proxy_fops_release: Close 'server side'
+ *
+ * Return value:
+ *      Always returns 0.
+ */
+static int vtpm_proxy_fops_release(struct inode *inode, struct file *filp)
+{
+       struct proxy_dev *proxy_dev = filp->private_data;
+
+       filp->private_data = NULL;
+
+       vtpm_proxy_delete_device(proxy_dev);
+
+       return 0;
+}
+
+static const struct file_operations vtpm_proxy_fops = {
+       .owner = THIS_MODULE,
+       .llseek = no_llseek,
+       .read = vtpm_proxy_fops_read,
+       .write = vtpm_proxy_fops_write,
+       .poll = vtpm_proxy_fops_poll,
+       .release = vtpm_proxy_fops_release,
+};
+
+/*
+ * Functions invoked by the core TPM driver to send TPM commands to
+ * 'server side' and receive responses from there.
+ */
+
+/*
+ * Called when core TPM driver reads TPM responses from 'server side'
+ *
+ * Return value:
+ *      Number of TPM response bytes read, negative error value otherwise
+ */
+static int vtpm_proxy_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+       struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
+       size_t len;
+
+       /* process gone ? */
+       mutex_lock(&proxy_dev->buf_lock);
+
+       if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
+               mutex_unlock(&proxy_dev->buf_lock);
+               return -EPIPE;
+       }
+
+       len = proxy_dev->resp_len;
+       if (count < len) {
+               dev_err(&chip->dev,
+                       "Invalid size in recv: count=%zd, resp_len=%zd\n",
+                       count, len);
+               len = -EIO;
+               goto out;
+       }
+
+       memcpy(buf, proxy_dev->buffer, len);
+       proxy_dev->resp_len = 0;
+
+out:
+       mutex_unlock(&proxy_dev->buf_lock);
+
+       return len;
+}
+
+/*
+ * Called when core TPM driver forwards TPM requests to 'server side'.
+ *
+ * Return value:
+ *      0 in case of success, negative error value otherwise.
+ */
+static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+       struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
+       int rc = 0;
+
+       if (count > sizeof(proxy_dev->buffer)) {
+               dev_err(&chip->dev,
+                       "Invalid size in send: count=%zd, buffer size=%zd\n",
+                       count, sizeof(proxy_dev->buffer));
+               return -EIO;
+       }
+
+       mutex_lock(&proxy_dev->buf_lock);
+
+       if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
+               mutex_unlock(&proxy_dev->buf_lock);
+               return -EPIPE;
+       }
+
+       proxy_dev->resp_len = 0;
+
+       proxy_dev->req_len = count;
+       memcpy(proxy_dev->buffer, buf, count);
+
+       proxy_dev->state &= ~STATE_WAIT_RESPONSE_FLAG;
+
+       mutex_unlock(&proxy_dev->buf_lock);
+
+       wake_up_interruptible(&proxy_dev->wq);
+
+       return rc;
+}
+
+static void vtpm_proxy_tpm_op_cancel(struct tpm_chip *chip)
+{
+       /* not supported */
+}
+
+static u8 vtpm_proxy_tpm_op_status(struct tpm_chip *chip)
+{
+       struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
+
+       if (proxy_dev->resp_len)
+               return VTPM_PROXY_REQ_COMPLETE_FLAG;
+
+       return 0;
+}
+
+static bool vtpm_proxy_tpm_req_canceled(struct tpm_chip  *chip, u8 status)
+{
+       struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
+       bool ret;
+
+       mutex_lock(&proxy_dev->buf_lock);
+
+       ret = !(proxy_dev->state & STATE_OPENED_FLAG);
+
+       mutex_unlock(&proxy_dev->buf_lock);
+
+       return ret;
+}
+
+static const struct tpm_class_ops vtpm_proxy_tpm_ops = {
+       .flags = TPM_OPS_AUTO_STARTUP,
+       .recv = vtpm_proxy_tpm_op_recv,
+       .send = vtpm_proxy_tpm_op_send,
+       .cancel = vtpm_proxy_tpm_op_cancel,
+       .status = vtpm_proxy_tpm_op_status,
+       .req_complete_mask = VTPM_PROXY_REQ_COMPLETE_FLAG,
+       .req_complete_val = VTPM_PROXY_REQ_COMPLETE_FLAG,
+       .req_canceled = vtpm_proxy_tpm_req_canceled,
+};
+
+/*
+ * Code related to the startup of the TPM 2 and startup of TPM 1.2 +
+ * retrieval of timeouts and durations.
+ */
+
+static void vtpm_proxy_work(struct work_struct *work)
+{
+       struct proxy_dev *proxy_dev = container_of(work, struct proxy_dev,
+                                                  work);
+       int rc;
+
+       rc = tpm_chip_register(proxy_dev->chip);
+       if (rc)
+               goto err;
+
+       return;
+
+err:
+       vtpm_proxy_fops_undo_open(proxy_dev);
+}
+
+/*
+ * vtpm_proxy_work_stop: make sure the work has finished
+ *
+ * This function is useful when user space closed the fd
+ * while the driver still determines timeouts.
+ */
+static void vtpm_proxy_work_stop(struct proxy_dev *proxy_dev)
+{
+       vtpm_proxy_fops_undo_open(proxy_dev);
+       flush_work(&proxy_dev->work);
+}
+
+/*
+ * vtpm_proxy_work_start: Schedule the work for TPM 1.2 & 2 initialization
+ */
+static inline void vtpm_proxy_work_start(struct proxy_dev *proxy_dev)
+{
+       queue_work(workqueue, &proxy_dev->work);
+}
+
+/*
+ * Code related to creation and deletion of device pairs
+ */
+static struct proxy_dev *vtpm_proxy_create_proxy_dev(void)
+{
+       struct proxy_dev *proxy_dev;
+       struct tpm_chip *chip;
+       int err;
+
+       proxy_dev = kzalloc(sizeof(*proxy_dev), GFP_KERNEL);
+       if (proxy_dev == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       init_waitqueue_head(&proxy_dev->wq);
+       mutex_init(&proxy_dev->buf_lock);
+       INIT_WORK(&proxy_dev->work, vtpm_proxy_work);
+
+       chip = tpm_chip_alloc(NULL, &vtpm_proxy_tpm_ops);
+       if (IS_ERR(chip)) {
+               err = PTR_ERR(chip);
+               goto err_proxy_dev_free;
+       }
+       dev_set_drvdata(&chip->dev, proxy_dev);
+
+       proxy_dev->chip = chip;
+
+       return proxy_dev;
+
+err_proxy_dev_free:
+       kfree(proxy_dev);
+
+       return ERR_PTR(err);
+}
+
+/*
+ * Undo what has been done in vtpm_create_proxy_dev
+ */
+static inline void vtpm_proxy_delete_proxy_dev(struct proxy_dev *proxy_dev)
+{
+       put_device(&proxy_dev->chip->dev); /* frees chip */
+       kfree(proxy_dev);
+}
+
+/*
+ * Create a /dev/tpm%d and 'server side' file descriptor pair
+ *
+ * Return value:
+ *      Returns file pointer on success, an error value otherwise
+ */
+static struct file *vtpm_proxy_create_device(
+                                struct vtpm_proxy_new_dev *vtpm_new_dev)
+{
+       struct proxy_dev *proxy_dev;
+       int rc, fd;
+       struct file *file;
+
+       if (vtpm_new_dev->flags & ~VTPM_PROXY_FLAGS_ALL)
+               return ERR_PTR(-EOPNOTSUPP);
+
+       proxy_dev = vtpm_proxy_create_proxy_dev();
+       if (IS_ERR(proxy_dev))
+               return ERR_CAST(proxy_dev);
+
+       proxy_dev->flags = vtpm_new_dev->flags;
+
+       /* setup an anonymous file for the server-side */
+       fd = get_unused_fd_flags(O_RDWR);
+       if (fd < 0) {
+               rc = fd;
+               goto err_delete_proxy_dev;
+       }
+
+       file = anon_inode_getfile("[vtpms]", &vtpm_proxy_fops, proxy_dev,
+                                 O_RDWR);
+       if (IS_ERR(file)) {
+               rc = PTR_ERR(file);
+               goto err_put_unused_fd;
+       }
+
+       /* from now on we can unwind with put_unused_fd() + fput() */
+       /* simulate an open() on the server side */
+       vtpm_proxy_fops_open(file);
+
+       if (proxy_dev->flags & VTPM_PROXY_FLAG_TPM2)
+               proxy_dev->chip->flags |= TPM_CHIP_FLAG_TPM2;
+
+       vtpm_proxy_work_start(proxy_dev);
+
+       vtpm_new_dev->fd = fd;
+       vtpm_new_dev->major = MAJOR(proxy_dev->chip->dev.devt);
+       vtpm_new_dev->minor = MINOR(proxy_dev->chip->dev.devt);
+       vtpm_new_dev->tpm_num = proxy_dev->chip->dev_num;
+
+       return file;
+
+err_put_unused_fd:
+       put_unused_fd(fd);
+
+err_delete_proxy_dev:
+       vtpm_proxy_delete_proxy_dev(proxy_dev);
+
+       return ERR_PTR(rc);
+}
+
+/*
+ * Counter part to vtpm_create_device.
+ */
+static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev)
+{
+       vtpm_proxy_work_stop(proxy_dev);
+
+       /*
+        * A client may hold the 'ops' lock, so let it know that the server
+        * side shuts down before we try to grab the 'ops' lock when
+        * unregistering the chip.
+        */
+       vtpm_proxy_fops_undo_open(proxy_dev);
+
+       tpm_chip_unregister(proxy_dev->chip);
+
+       vtpm_proxy_delete_proxy_dev(proxy_dev);
+}
+
+/*
+ * Code related to the control device /dev/vtpmx
+ */
+
+/*
+ * vtpmx_fops_ioctl: ioctl on /dev/vtpmx
+ *
+ * Return value:
+ *      Returns 0 on success, a negative error code otherwise.
+ */
+static long vtpmx_fops_ioctl(struct file *f, unsigned int ioctl,
+                                  unsigned long arg)
+{
+       void __user *argp = (void __user *)arg;
+       struct vtpm_proxy_new_dev __user *vtpm_new_dev_p;
+       struct vtpm_proxy_new_dev vtpm_new_dev;
+       struct file *file;
+
+       switch (ioctl) {
+       case VTPM_PROXY_IOC_NEW_DEV:
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+               vtpm_new_dev_p = argp;
+               if (copy_from_user(&vtpm_new_dev, vtpm_new_dev_p,
+                                  sizeof(vtpm_new_dev)))
+                       return -EFAULT;
+               file = vtpm_proxy_create_device(&vtpm_new_dev);
+               if (IS_ERR(file))
+                       return PTR_ERR(file);
+               if (copy_to_user(vtpm_new_dev_p, &vtpm_new_dev,
+                                sizeof(vtpm_new_dev))) {
+                       put_unused_fd(vtpm_new_dev.fd);
+                       fput(file);
+                       return -EFAULT;
+               }
+
+               fd_install(vtpm_new_dev.fd, file);
+               return 0;
+
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+#ifdef CONFIG_COMPAT
+static long vtpmx_fops_compat_ioctl(struct file *f, unsigned int ioctl,
+                                         unsigned long arg)
+{
+       return vtpmx_fops_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static const struct file_operations vtpmx_fops = {
+       .owner = THIS_MODULE,
+       .unlocked_ioctl = vtpmx_fops_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = vtpmx_fops_compat_ioctl,
+#endif
+       .llseek = noop_llseek,
+};
+
+static struct miscdevice vtpmx_miscdev = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "vtpmx",
+       .fops = &vtpmx_fops,
+};
+
+static int vtpmx_init(void)
+{
+       return misc_register(&vtpmx_miscdev);
+}
+
+static void vtpmx_cleanup(void)
+{
+       misc_deregister(&vtpmx_miscdev);
+}
+
+static int __init vtpm_module_init(void)
+{
+       int rc;
+
+       rc = vtpmx_init();
+       if (rc) {
+               pr_err("couldn't create vtpmx device\n");
+               return rc;
+       }
+
+       workqueue = create_workqueue("tpm-vtpm");
+       if (!workqueue) {
+               pr_err("couldn't create workqueue\n");
+               rc = -ENOMEM;
+               goto err_vtpmx_cleanup;
+       }
+
+       return 0;
+
+err_vtpmx_cleanup:
+       vtpmx_cleanup();
+
+       return rc;
+}
+
+static void __exit vtpm_module_exit(void)
+{
+       destroy_workqueue(workqueue);
+       vtpmx_cleanup();
+}
+
+module_init(vtpm_module_init);
+module_exit(vtpm_module_exit);
+
+MODULE_AUTHOR("Stefan Berger (stefanb@us.ibm.com)");
+MODULE_DESCRIPTION("vTPM Driver");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
index 3111f2778079dd49c0f8514d7852c06688a6c4f5..62028f483bbaacfc78b77b3b65ed2e39c8d1e0e7 100644 (file)
@@ -28,6 +28,8 @@ struct tpm_private {
        unsigned int evtchn;
        int ring_ref;
        domid_t backend_id;
+       int irq;
+       wait_queue_head_t read_queue;
 };
 
 enum status_bits {
@@ -39,7 +41,7 @@ enum status_bits {
 
 static u8 vtpm_status(struct tpm_chip *chip)
 {
-       struct tpm_private *priv = TPM_VPRIV(chip);
+       struct tpm_private *priv = dev_get_drvdata(&chip->dev);
        switch (priv->shr->state) {
        case VTPM_STATE_IDLE:
                return VTPM_STATUS_IDLE | VTPM_STATUS_CANCELED;
@@ -60,7 +62,7 @@ static bool vtpm_req_canceled(struct tpm_chip *chip, u8 status)
 
 static void vtpm_cancel(struct tpm_chip *chip)
 {
-       struct tpm_private *priv = TPM_VPRIV(chip);
+       struct tpm_private *priv = dev_get_drvdata(&chip->dev);
        priv->shr->state = VTPM_STATE_CANCEL;
        wmb();
        notify_remote_via_evtchn(priv->evtchn);
@@ -73,7 +75,7 @@ static unsigned int shr_data_offset(struct vtpm_shared_page *shr)
 
 static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
 {
-       struct tpm_private *priv = TPM_VPRIV(chip);
+       struct tpm_private *priv = dev_get_drvdata(&chip->dev);
        struct vtpm_shared_page *shr = priv->shr;
        unsigned int offset = shr_data_offset(shr);
 
@@ -87,8 +89,8 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
                return -EINVAL;
 
        /* Wait for completion of any existing command or cancellation */
-       if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, chip->vendor.timeout_c,
-                       &chip->vendor.read_queue, true) < 0) {
+       if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, chip->timeout_c,
+                       &priv->read_queue, true) < 0) {
                vtpm_cancel(chip);
                return -ETIME;
        }
@@ -104,7 +106,7 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
        duration = tpm_calc_ordinal_duration(chip, ordinal);
 
        if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, duration,
-                       &chip->vendor.read_queue, true) < 0) {
+                       &priv->read_queue, true) < 0) {
                /* got a signal or timeout, try to cancel */
                vtpm_cancel(chip);
                return -ETIME;
@@ -115,7 +117,7 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
 
 static int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 {
-       struct tpm_private *priv = TPM_VPRIV(chip);
+       struct tpm_private *priv = dev_get_drvdata(&chip->dev);
        struct vtpm_shared_page *shr = priv->shr;
        unsigned int offset = shr_data_offset(shr);
        size_t length = shr->length;
@@ -124,8 +126,8 @@ static int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
                return -ECANCELED;
 
        /* In theory the wait at the end of _send makes this one unnecessary */
-       if (wait_for_tpm_stat(chip, VTPM_STATUS_RESULT, chip->vendor.timeout_c,
-                       &chip->vendor.read_queue, true) < 0) {
+       if (wait_for_tpm_stat(chip, VTPM_STATUS_RESULT, chip->timeout_c,
+                       &priv->read_queue, true) < 0) {
                vtpm_cancel(chip);
                return -ETIME;
        }
@@ -161,7 +163,7 @@ static irqreturn_t tpmif_interrupt(int dummy, void *dev_id)
        switch (priv->shr->state) {
        case VTPM_STATE_IDLE:
        case VTPM_STATE_FINISH:
-               wake_up_interruptible(&priv->chip->vendor.read_queue);
+               wake_up_interruptible(&priv->read_queue);
                break;
        case VTPM_STATE_SUBMIT:
        case VTPM_STATE_CANCEL:
@@ -179,10 +181,10 @@ static int setup_chip(struct device *dev, struct tpm_private *priv)
        if (IS_ERR(chip))
                return PTR_ERR(chip);
 
-       init_waitqueue_head(&chip->vendor.read_queue);
+       init_waitqueue_head(&priv->read_queue);
 
        priv->chip = chip;
-       TPM_VPRIV(chip) = priv;
+       dev_set_drvdata(&chip->dev, priv);
 
        return 0;
 }
@@ -217,7 +219,7 @@ static int setup_ring(struct xenbus_device *dev, struct tpm_private *priv)
                xenbus_dev_fatal(dev, rv, "allocating TPM irq");
                return rv;
        }
-       priv->chip->vendor.irq = rv;
+       priv->irq = rv;
 
  again:
        rv = xenbus_transaction_start(&xbt);
@@ -277,8 +279,8 @@ static void ring_free(struct tpm_private *priv)
        else
                free_page((unsigned long)priv->shr);
 
-       if (priv->chip && priv->chip->vendor.irq)
-               unbind_from_irqhandler(priv->chip->vendor.irq, priv);
+       if (priv->irq)
+               unbind_from_irqhandler(priv->irq, priv);
 
        kfree(priv);
 }
@@ -318,10 +320,10 @@ static int tpmfront_probe(struct xenbus_device *dev,
 static int tpmfront_remove(struct xenbus_device *dev)
 {
        struct tpm_chip *chip = dev_get_drvdata(&dev->dev);
-       struct tpm_private *priv = TPM_VPRIV(chip);
+       struct tpm_private *priv = dev_get_drvdata(&chip->dev);
        tpm_chip_unregister(chip);
        ring_free(priv);
-       TPM_VPRIV(chip) = NULL;
+       dev_set_drvdata(&chip->dev, NULL);
        return 0;
 }
 
index fc487543381799c4c4eb421d6dd2f802b9543d99..5de0673f333b7ca86df00a3f930d5d7005560e53 100644 (file)
@@ -51,7 +51,7 @@ struct krb5_principal {
 struct krb5_tagged_data {
        /* for tag value, see /usr/include/krb5/krb5.h
         * - KRB5_AUTHDATA_* for auth data
-        * - 
+        * -
         */
        s32             tag;
        u32             data_len;
index 00690ff92edfed99956b04947d2c09b9ebac580f..5f3c63dde2d5acb41077b879cf67758dd63a51e3 100644 (file)
@@ -206,6 +206,7 @@ extern bool has_ns_capability_noaudit(struct task_struct *t,
                                      struct user_namespace *ns, int cap);
 extern bool capable(int cap);
 extern bool ns_capable(struct user_namespace *ns, int cap);
+extern bool ns_capable_noaudit(struct user_namespace *ns, int cap);
 #else
 static inline bool has_capability(struct task_struct *t, int cap)
 {
@@ -233,6 +234,10 @@ static inline bool ns_capable(struct user_namespace *ns, int cap)
 {
        return true;
 }
+static inline bool ns_capable_noaudit(struct user_namespace *ns, int cap)
+{
+       return true;
+}
 #endif /* CONFIG_MULTIUSER */
 extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
 extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);
index 817dfdb378851fa9763e932f512820c4245b4edb..6f0fb6ebd7dbbf0bfa3ef78f111c90b68aaeef9d 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * STMicroelectronics TPM Linux driver for TPM 1.2 ST33ZP24
- * Copyright (C) 2009 - 2015  STMicroelectronics
+ * Copyright (C) 2009 - 2016  STMicroelectronics
  *
  * 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
index 2296e6b2f690760e5b1c2ab33091349be8d9ef58..ecc296c137cd2e3e926821dc357e5c00563bedd4 100644 (file)
@@ -28,19 +28,13 @@ struct seccomp {
 };
 
 #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
-extern int __secure_computing(void);
-static inline int secure_computing(void)
+extern int __secure_computing(const struct seccomp_data *sd);
+static inline int secure_computing(const struct seccomp_data *sd)
 {
        if (unlikely(test_thread_flag(TIF_SECCOMP)))
-               return  __secure_computing();
+               return  __secure_computing(sd);
        return 0;
 }
-
-#define SECCOMP_PHASE1_OK      0
-#define SECCOMP_PHASE1_SKIP    1
-
-extern u32 seccomp_phase1(struct seccomp_data *sd);
-int seccomp_phase2(u32 phase1_result);
 #else
 extern void secure_computing_strict(int this_syscall);
 #endif
@@ -61,7 +55,7 @@ struct seccomp { };
 struct seccomp_filter { };
 
 #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
-static inline int secure_computing(void) { return 0; }
+static inline int secure_computing(struct seccomp_data *sd) { return 0; }
 #else
 static inline void secure_computing_strict(int this_syscall) { return; }
 #endif
index 706e63eea0800123d50dae79cfae327f6dacaa51..da158f06e0b2fec35f10e2faea57d806696e4d20 100644 (file)
@@ -33,7 +33,12 @@ struct tpm_chip;
 struct trusted_key_payload;
 struct trusted_key_options;
 
+enum TPM_OPS_FLAGS {
+       TPM_OPS_AUTO_STARTUP = BIT(0),
+};
+
 struct tpm_class_ops {
+       unsigned int flags;
        const u8 req_complete_mask;
        const u8 req_complete_val;
        bool (*req_canceled)(struct tpm_chip *chip, u8 status);
diff --git a/include/net/calipso.h b/include/net/calipso.h
new file mode 100644 (file)
index 0000000..b1b30cd
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * CALIPSO - Common Architecture Label IPv6 Security Option
+ *
+ * This is an implementation of the CALIPSO protocol as specified in
+ * RFC 5570.
+ *
+ * Authors: Paul Moore <paul@paul-moore.com>
+ *          Huw Davies <huw@codeweavers.com>
+ *
+ */
+
+/*
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * (c) Copyright Huw Davies <huw@codeweavers.com>, 2015
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY;  without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program;  if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _CALIPSO_H
+#define _CALIPSO_H
+
+#include <linux/types.h>
+#include <linux/rcupdate.h>
+#include <linux/list.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <net/netlabel.h>
+#include <net/request_sock.h>
+#include <linux/atomic.h>
+#include <asm/unaligned.h>
+
+/* known doi values */
+#define CALIPSO_DOI_UNKNOWN          0x00000000
+
+/* doi mapping types */
+#define CALIPSO_MAP_UNKNOWN          0
+#define CALIPSO_MAP_PASS             2
+
+/*
+ * CALIPSO DOI definitions
+ */
+
+/* DOI definition struct */
+struct calipso_doi {
+       u32 doi;
+       u32 type;
+
+       atomic_t refcount;
+       struct list_head list;
+       struct rcu_head rcu;
+};
+
+/*
+ * Sysctl Variables
+ */
+extern int calipso_cache_enabled;
+extern int calipso_cache_bucketsize;
+
+#ifdef CONFIG_NETLABEL
+int __init calipso_init(void);
+void calipso_exit(void);
+bool calipso_validate(const struct sk_buff *skb, const unsigned char *option);
+#else
+static inline int __init calipso_init(void)
+{
+       return 0;
+}
+
+static inline void calipso_exit(void)
+{
+}
+static inline bool calipso_validate(const struct sk_buff *skb,
+                                   const unsigned char *option)
+{
+       return true;
+}
+#endif /* CONFIG_NETLABEL */
+
+#endif /* _CALIPSO_H */
index 012b1f91f3ec4e3b018fb1b900692342eaefe108..236a81034fefec203e27bb73b5a1e5d7d512f8d5 100644 (file)
@@ -97,7 +97,12 @@ struct inet_request_sock {
        u32                     ir_mark;
        union {
                struct ip_options_rcu   *opt;
-               struct sk_buff          *pktopts;
+#if IS_ENABLED(CONFIG_IPV6)
+               struct {
+                       struct ipv6_txoptions   *ipv6_opt;
+                       struct sk_buff          *pktopts;
+               };
+#endif
        };
 };
 
index 11a045281948f07e638deb4515e60eaaf8c70774..8fed1cd78658a6e088b63f7703290cb02c1969b6 100644 (file)
@@ -313,11 +313,19 @@ struct ipv6_txoptions *ipv6_renew_options(struct sock *sk,
                                          int newtype,
                                          struct ipv6_opt_hdr __user *newopt,
                                          int newoptlen);
+struct ipv6_txoptions *
+ipv6_renew_options_kern(struct sock *sk,
+                       struct ipv6_txoptions *opt,
+                       int newtype,
+                       struct ipv6_opt_hdr *newopt,
+                       int newoptlen);
 struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
                                          struct ipv6_txoptions *opt);
 
 bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb,
                       const struct inet6_skb_parm *opt);
+struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
+                                          struct ipv6_txoptions *opt);
 
 static inline bool ipv6_accept_ra(struct inet6_dev *idev)
 {
@@ -943,7 +951,7 @@ enum {
 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, int target,
                  unsigned short *fragoff, int *fragflg);
 
-int ipv6_find_tlv(struct sk_buff *skb, int offset, int type);
+int ipv6_find_tlv(const struct sk_buff *skb, int offset, int type);
 
 struct in6_addr *fl6_update_dst(struct flowi6 *fl6,
                                const struct ipv6_txoptions *opt,
index 7b5a300de7f589a534ac315f24a1151674aa54a4..efe98068880f5559b97477374cd25f11957b150e 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/atomic.h>
 
 struct cipso_v4_doi;
+struct calipso_doi;
 
 /*
  * NetLabel - A management interface for maintaining network packet label
@@ -94,6 +95,8 @@ struct cipso_v4_doi;
 #define NETLBL_NLTYPE_UNLABELED_NAME    "NLBL_UNLBL"
 #define NETLBL_NLTYPE_ADDRSELECT        6
 #define NETLBL_NLTYPE_ADDRSELECT_NAME   "NLBL_ADRSEL"
+#define NETLBL_NLTYPE_CALIPSO           7
+#define NETLBL_NLTYPE_CALIPSO_NAME      "NLBL_CALIPSO"
 
 /*
  * NetLabel - Kernel API for accessing the network packet label mappings.
@@ -216,6 +219,63 @@ struct netlbl_lsm_secattr {
        } attr;
 };
 
+/**
+ * struct netlbl_calipso_ops - NetLabel CALIPSO operations
+ * @doi_add: add a CALIPSO DOI
+ * @doi_free: free a CALIPSO DOI
+ * @doi_getdef: returns a reference to a DOI
+ * @doi_putdef: releases a reference of a DOI
+ * @doi_walk: enumerate the DOI list
+ * @sock_getattr: retrieve the socket's attr
+ * @sock_setattr: set the socket's attr
+ * @sock_delattr: remove the socket's attr
+ * @req_setattr: set the req socket's attr
+ * @req_delattr: remove the req socket's attr
+ * @opt_getattr: retrieve attr from memory block
+ * @skbuff_optptr: find option in packet
+ * @skbuff_setattr: set the skbuff's attr
+ * @skbuff_delattr: remove the skbuff's attr
+ * @cache_invalidate: invalidate cache
+ * @cache_add: add cache entry
+ *
+ * Description:
+ * This structure is filled out by the CALIPSO engine and passed
+ * to the NetLabel core via a call to netlbl_calipso_ops_register().
+ * It enables the CALIPSO engine (and hence IPv6) to be compiled
+ * as a module.
+ */
+struct netlbl_calipso_ops {
+       int (*doi_add)(struct calipso_doi *doi_def,
+                      struct netlbl_audit *audit_info);
+       void (*doi_free)(struct calipso_doi *doi_def);
+       int (*doi_remove)(u32 doi, struct netlbl_audit *audit_info);
+       struct calipso_doi *(*doi_getdef)(u32 doi);
+       void (*doi_putdef)(struct calipso_doi *doi_def);
+       int (*doi_walk)(u32 *skip_cnt,
+                       int (*callback)(struct calipso_doi *doi_def, void *arg),
+                       void *cb_arg);
+       int (*sock_getattr)(struct sock *sk,
+                           struct netlbl_lsm_secattr *secattr);
+       int (*sock_setattr)(struct sock *sk,
+                           const struct calipso_doi *doi_def,
+                           const struct netlbl_lsm_secattr *secattr);
+       void (*sock_delattr)(struct sock *sk);
+       int (*req_setattr)(struct request_sock *req,
+                          const struct calipso_doi *doi_def,
+                          const struct netlbl_lsm_secattr *secattr);
+       void (*req_delattr)(struct request_sock *req);
+       int (*opt_getattr)(const unsigned char *calipso,
+                          struct netlbl_lsm_secattr *secattr);
+       unsigned char *(*skbuff_optptr)(const struct sk_buff *skb);
+       int (*skbuff_setattr)(struct sk_buff *skb,
+                             const struct calipso_doi *doi_def,
+                             const struct netlbl_lsm_secattr *secattr);
+       int (*skbuff_delattr)(struct sk_buff *skb);
+       void (*cache_invalidate)(void);
+       int (*cache_add)(const unsigned char *calipso_ptr,
+                        const struct netlbl_lsm_secattr *secattr);
+};
+
 /*
  * LSM security attribute operations (inline)
  */
@@ -385,6 +445,14 @@ int netlbl_cfg_cipsov4_map_add(u32 doi,
                               const struct in_addr *addr,
                               const struct in_addr *mask,
                               struct netlbl_audit *audit_info);
+int netlbl_cfg_calipso_add(struct calipso_doi *doi_def,
+                          struct netlbl_audit *audit_info);
+void netlbl_cfg_calipso_del(u32 doi, struct netlbl_audit *audit_info);
+int netlbl_cfg_calipso_map_add(u32 doi,
+                              const char *domain,
+                              const struct in6_addr *addr,
+                              const struct in6_addr *mask,
+                              struct netlbl_audit *audit_info);
 /*
  * LSM security attribute operations
  */
@@ -405,6 +473,12 @@ int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap,
                          unsigned long bitmap,
                          gfp_t flags);
 
+/* Bitmap functions
+ */
+int netlbl_bitmap_walk(const unsigned char *bitmap, u32 bitmap_len,
+                      u32 offset, u8 state);
+void netlbl_bitmap_setbit(unsigned char *bitmap, u32 bit, u8 state);
+
 /*
  * LSM protocol operations (NetLabel LSM/kernel API)
  */
@@ -427,13 +501,13 @@ int netlbl_skbuff_setattr(struct sk_buff *skb,
 int netlbl_skbuff_getattr(const struct sk_buff *skb,
                          u16 family,
                          struct netlbl_lsm_secattr *secattr);
-void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway);
+void netlbl_skbuff_err(struct sk_buff *skb, u16 family, int error, int gateway);
 
 /*
  * LSM label mapping cache operations
  */
 void netlbl_cache_invalidate(void);
-int netlbl_cache_add(const struct sk_buff *skb,
+int netlbl_cache_add(const struct sk_buff *skb, u16 family,
                     const struct netlbl_lsm_secattr *secattr);
 
 /*
@@ -495,6 +569,24 @@ static inline int netlbl_cfg_cipsov4_map_add(u32 doi,
 {
        return -ENOSYS;
 }
+static inline int netlbl_cfg_calipso_add(struct calipso_doi *doi_def,
+                                        struct netlbl_audit *audit_info)
+{
+       return -ENOSYS;
+}
+static inline void netlbl_cfg_calipso_del(u32 doi,
+                                         struct netlbl_audit *audit_info)
+{
+       return;
+}
+static inline int netlbl_cfg_calipso_map_add(u32 doi,
+                                            const char *domain,
+                                            const struct in6_addr *addr,
+                                            const struct in6_addr *mask,
+                                            struct netlbl_audit *audit_info)
+{
+       return -ENOSYS;
+}
 static inline int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap,
                                     u32 offset)
 {
@@ -586,7 +678,7 @@ static inline void netlbl_cache_invalidate(void)
 {
        return;
 }
-static inline int netlbl_cache_add(const struct sk_buff *skb,
+static inline int netlbl_cache_add(const struct sk_buff *skb, u16 family,
                                   const struct netlbl_lsm_secattr *secattr)
 {
        return 0;
@@ -598,4 +690,7 @@ static inline struct audit_buffer *netlbl_audit_start(int type,
 }
 #endif /* CONFIG_NETLABEL */
 
+const struct netlbl_calipso_ops *
+netlbl_calipso_ops_register(const struct netlbl_calipso_ops *ops);
+
 #endif /* _NETLABEL_H */
index ec10cfef166afb3a7cdc777ade3903e076d30376..6d4e92ccdc914460b28f9b07aaa65406676f912b 100644 (file)
@@ -455,6 +455,7 @@ header-y += virtio_scsi.h
 header-y += virtio_types.h
 header-y += vm_sockets.h
 header-y += vt.h
+header-y += vtpm_proxy.h
 header-y += wait.h
 header-y += wanrouter.h
 header-y += watchdog.h
index d820aa979620b5f75b071dffbd73429bfc360282..82e8aa59446b18cb1cc0dd5c0d79a2fe8454221e 100644 (file)
 #define AUDIT_MAC_IPSEC_EVENT  1415    /* Audit an IPSec event */
 #define AUDIT_MAC_UNLBL_STCADD 1416    /* NetLabel: add a static label */
 #define AUDIT_MAC_UNLBL_STCDEL 1417    /* NetLabel: del a static label */
+#define AUDIT_MAC_CALIPSO_ADD  1418    /* NetLabel: add CALIPSO DOI entry */
+#define AUDIT_MAC_CALIPSO_DEL  1419    /* NetLabel: del CALIPSO DOI entry */
 
 #define AUDIT_FIRST_KERN_ANOM_MSG   1700
 #define AUDIT_LAST_KERN_ANOM_MSG    1799
index 318a4828bf98c236fb156406c8c9ec189472b357..b39ea4f2e701d27edb0db76a94560557e3ae408c 100644 (file)
@@ -143,6 +143,7 @@ struct in6_flowlabel_req {
 #define IPV6_TLV_PAD1          0
 #define IPV6_TLV_PADN          1
 #define IPV6_TLV_ROUTERALERT   5
+#define IPV6_TLV_CALIPSO       7       /* RFC 5570 */
 #define IPV6_TLV_JUMBO         194
 #define IPV6_TLV_HAO           201     /* home address option */
 
diff --git a/include/uapi/linux/vtpm_proxy.h b/include/uapi/linux/vtpm_proxy.h
new file mode 100644 (file)
index 0000000..41e8e22
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Definitions for the VTPM proxy driver
+ * Copyright (c) 2015, 2016, IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef _UAPI_LINUX_VTPM_PROXY_H
+#define _UAPI_LINUX_VTPM_PROXY_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* ioctls */
+
+struct vtpm_proxy_new_dev {
+       __u32 flags;         /* input */
+       __u32 tpm_num;       /* output */
+       __u32 fd;            /* output */
+       __u32 major;         /* output */
+       __u32 minor;         /* output */
+};
+
+/* above flags */
+#define VTPM_PROXY_FLAG_TPM2  1  /* emulator is TPM 2 */
+
+#define VTPM_PROXY_IOC_NEW_DEV   _IOWR(0xa1, 0x00, struct vtpm_proxy_new_dev)
+
+#endif /* _UAPI_LINUX_VTPM_PROXY_H */
index 45432b54d5c60ca5b9cde95e480c3fae8cffae4a..00411c82dac57c4cac0254c823f9adcb2a951549 100644 (file)
@@ -361,6 +361,24 @@ bool has_capability_noaudit(struct task_struct *t, int cap)
        return has_ns_capability_noaudit(t, &init_user_ns, cap);
 }
 
+static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit)
+{
+       int capable;
+
+       if (unlikely(!cap_valid(cap))) {
+               pr_crit("capable() called with invalid cap=%u\n", cap);
+               BUG();
+       }
+
+       capable = audit ? security_capable(current_cred(), ns, cap) :
+                         security_capable_noaudit(current_cred(), ns, cap);
+       if (capable == 0) {
+               current->flags |= PF_SUPERPRIV;
+               return true;
+       }
+       return false;
+}
+
 /**
  * ns_capable - Determine if the current task has a superior capability in effect
  * @ns:  The usernamespace we want the capability in
@@ -374,19 +392,27 @@ bool has_capability_noaudit(struct task_struct *t, int cap)
  */
 bool ns_capable(struct user_namespace *ns, int cap)
 {
-       if (unlikely(!cap_valid(cap))) {
-               pr_crit("capable() called with invalid cap=%u\n", cap);
-               BUG();
-       }
-
-       if (security_capable(current_cred(), ns, cap) == 0) {
-               current->flags |= PF_SUPERPRIV;
-               return true;
-       }
-       return false;
+       return ns_capable_common(ns, cap, true);
 }
 EXPORT_SYMBOL(ns_capable);
 
+/**
+ * ns_capable_noaudit - Determine if the current task has a superior capability
+ * (unaudited) in effect
+ * @ns:  The usernamespace we want the capability in
+ * @cap: The capability to be tested for
+ *
+ * Return true if the current task has the given superior capability currently
+ * available for use, false if not.
+ *
+ * This sets PF_SUPERPRIV on the task if the capability is available on the
+ * assumption that it's about to be used.
+ */
+bool ns_capable_noaudit(struct user_namespace *ns, int cap)
+{
+       return ns_capable_common(ns, cap, false);
+}
+EXPORT_SYMBOL(ns_capable_noaudit);
 
 /**
  * capable - Determine if the current task has a superior capability in effect
index 7002796f14a493988f6da70f77e266e36bf7788d..54d15eb2b701e522ced024ce2774d83bdd68c10b 100644 (file)
@@ -173,7 +173,7 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
  *
  * Returns valid seccomp BPF response codes.
  */
-static u32 seccomp_run_filters(struct seccomp_data *sd)
+static u32 seccomp_run_filters(const struct seccomp_data *sd)
 {
        struct seccomp_data sd_local;
        u32 ret = SECCOMP_RET_ALLOW;
@@ -554,20 +554,10 @@ void secure_computing_strict(int this_syscall)
                BUG();
 }
 #else
-int __secure_computing(void)
-{
-       u32 phase1_result = seccomp_phase1(NULL);
-
-       if (likely(phase1_result == SECCOMP_PHASE1_OK))
-               return 0;
-       else if (likely(phase1_result == SECCOMP_PHASE1_SKIP))
-               return -1;
-       else
-               return seccomp_phase2(phase1_result);
-}
 
 #ifdef CONFIG_SECCOMP_FILTER
-static u32 __seccomp_phase1_filter(int this_syscall, struct seccomp_data *sd)
+static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
+                           const bool recheck_after_trace)
 {
        u32 filter_ret, action;
        int data;
@@ -599,10 +589,46 @@ static u32 __seccomp_phase1_filter(int this_syscall, struct seccomp_data *sd)
                goto skip;
 
        case SECCOMP_RET_TRACE:
-               return filter_ret;  /* Save the rest for phase 2. */
+               /* We've been put in this state by the ptracer already. */
+               if (recheck_after_trace)
+                       return 0;
+
+               /* ENOSYS these calls if there is no tracer attached. */
+               if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) {
+                       syscall_set_return_value(current,
+                                                task_pt_regs(current),
+                                                -ENOSYS, 0);
+                       goto skip;
+               }
+
+               /* Allow the BPF to provide the event message */
+               ptrace_event(PTRACE_EVENT_SECCOMP, data);
+               /*
+                * The delivery of a fatal signal during event
+                * notification may silently skip tracer notification.
+                * Terminating the task now avoids executing a system
+                * call that may not be intended.
+                */
+               if (fatal_signal_pending(current))
+                       do_exit(SIGSYS);
+               /* Check if the tracer forced the syscall to be skipped. */
+               this_syscall = syscall_get_nr(current, task_pt_regs(current));
+               if (this_syscall < 0)
+                       goto skip;
+
+               /*
+                * Recheck the syscall, since it may have changed. This
+                * intentionally uses a NULL struct seccomp_data to force
+                * a reload of all registers. This does not goto skip since
+                * a skip would have already been reported.
+                */
+               if (__seccomp_filter(this_syscall, NULL, true))
+                       return -1;
+
+               return 0;
 
        case SECCOMP_RET_ALLOW:
-               return SECCOMP_PHASE1_OK;
+               return 0;
 
        case SECCOMP_RET_KILL:
        default:
@@ -614,96 +640,38 @@ static u32 __seccomp_phase1_filter(int this_syscall, struct seccomp_data *sd)
 
 skip:
        audit_seccomp(this_syscall, 0, action);
-       return SECCOMP_PHASE1_SKIP;
+       return -1;
+}
+#else
+static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
+                           const bool recheck_after_trace)
+{
+       BUG();
 }
 #endif
 
-/**
- * seccomp_phase1() - run fast path seccomp checks on the current syscall
- * @arg sd: The seccomp_data or NULL
- *
- * This only reads pt_regs via the syscall_xyz helpers.  The only change
- * it will make to pt_regs is via syscall_set_return_value, and it will
- * only do that if it returns SECCOMP_PHASE1_SKIP.
- *
- * If sd is provided, it will not read pt_regs at all.
- *
- * It may also call do_exit or force a signal; these actions must be
- * safe.
- *
- * If it returns SECCOMP_PHASE1_OK, the syscall passes checks and should
- * be processed normally.
- *
- * If it returns SECCOMP_PHASE1_SKIP, then the syscall should not be
- * invoked.  In this case, seccomp_phase1 will have set the return value
- * using syscall_set_return_value.
- *
- * If it returns anything else, then the return value should be passed
- * to seccomp_phase2 from a context in which ptrace hooks are safe.
- */
-u32 seccomp_phase1(struct seccomp_data *sd)
+int __secure_computing(const struct seccomp_data *sd)
 {
        int mode = current->seccomp.mode;
-       int this_syscall = sd ? sd->nr :
-               syscall_get_nr(current, task_pt_regs(current));
+       int this_syscall;
 
        if (config_enabled(CONFIG_CHECKPOINT_RESTORE) &&
            unlikely(current->ptrace & PT_SUSPEND_SECCOMP))
-               return SECCOMP_PHASE1_OK;
+               return 0;
+
+       this_syscall = sd ? sd->nr :
+               syscall_get_nr(current, task_pt_regs(current));
 
        switch (mode) {
        case SECCOMP_MODE_STRICT:
                __secure_computing_strict(this_syscall);  /* may call do_exit */
-               return SECCOMP_PHASE1_OK;
-#ifdef CONFIG_SECCOMP_FILTER
+               return 0;
        case SECCOMP_MODE_FILTER:
-               return __seccomp_phase1_filter(this_syscall, sd);
-#endif
+               return __seccomp_filter(this_syscall, sd, false);
        default:
                BUG();
        }
 }
-
-/**
- * seccomp_phase2() - finish slow path seccomp work for the current syscall
- * @phase1_result: The return value from seccomp_phase1()
- *
- * This must be called from a context in which ptrace hooks can be used.
- *
- * Returns 0 if the syscall should be processed or -1 to skip the syscall.
- */
-int seccomp_phase2(u32 phase1_result)
-{
-       struct pt_regs *regs = task_pt_regs(current);
-       u32 action = phase1_result & SECCOMP_RET_ACTION;
-       int data = phase1_result & SECCOMP_RET_DATA;
-
-       BUG_ON(action != SECCOMP_RET_TRACE);
-
-       audit_seccomp(syscall_get_nr(current, regs), 0, action);
-
-       /* Skip these calls if there is no tracer. */
-       if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) {
-               syscall_set_return_value(current, regs,
-                                        -ENOSYS, 0);
-               return -1;
-       }
-
-       /* Allow the BPF to provide the event message */
-       ptrace_event(PTRACE_EVENT_SECCOMP, data);
-       /*
-        * The delivery of a fatal signal during event
-        * notification may silently skip tracer notification.
-        * Terminating the task now avoids executing a system
-        * call that may not be intended.
-        */
-       if (fatal_signal_pending(current))
-               do_exit(SIGSYS);
-       if (syscall_get_nr(current, regs) < 0)
-               return -1;  /* Explicit request to skip. */
-
-       return 0;
-}
 #endif /* CONFIG_HAVE_ARCH_SECCOMP_FILTER */
 
 long prctl_get_seccomp(void)
index 3ff137d9471d8420748fd72c8631d67eb57ffb66..3828f94b234c1104a3e745b3c0a76ab343aed4b6 100644 (file)
@@ -216,14 +216,17 @@ static int dccp_v6_send_response(const struct sock *sk, struct request_sock *req
        skb = dccp_make_response(sk, dst, req);
        if (skb != NULL) {
                struct dccp_hdr *dh = dccp_hdr(skb);
+               struct ipv6_txoptions *opt;
 
                dh->dccph_checksum = dccp_v6_csum_finish(skb,
                                                         &ireq->ir_v6_loc_addr,
                                                         &ireq->ir_v6_rmt_addr);
                fl6.daddr = ireq->ir_v6_rmt_addr;
                rcu_read_lock();
-               err = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt),
-                              np->tclass);
+               opt = ireq->ipv6_opt;
+               if (!opt)
+                       opt = rcu_dereference(np->opt);
+               err = ip6_xmit(sk, skb, &fl6, opt, np->tclass);
                rcu_read_unlock();
                err = net_xmit_eval(err);
        }
@@ -236,6 +239,7 @@ done:
 static void dccp_v6_reqsk_destructor(struct request_sock *req)
 {
        dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg);
+       kfree(inet_rsk(req)->ipv6_opt);
        kfree_skb(inet_rsk(req)->pktopts);
 }
 
@@ -494,7 +498,9 @@ static struct sock *dccp_v6_request_recv_sock(const struct sock *sk,
         * Yes, keeping reference count would be much more clever, but we make
         * one more one thing there: reattach optmem to newsk.
         */
-       opt = rcu_dereference(np->opt);
+       opt = ireq->ipv6_opt;
+       if (!opt)
+               opt = rcu_dereference(np->opt);
        if (opt) {
                opt = ipv6_dup_options(newsk, opt);
                RCU_INIT_POINTER(newnp->opt, opt);
index 40d6b87713a132666298cc8e8d875da6a6a1a387..72d6f056d863603c959e1d04b9f863909a37c758 100644 (file)
@@ -134,76 +134,6 @@ int cipso_v4_rbm_strictvalid = 1;
  * Helper Functions
  */
 
-/**
- * cipso_v4_bitmap_walk - Walk a bitmap looking for a bit
- * @bitmap: the bitmap
- * @bitmap_len: length in bits
- * @offset: starting offset
- * @state: if non-zero, look for a set (1) bit else look for a cleared (0) bit
- *
- * Description:
- * Starting at @offset, walk the bitmap from left to right until either the
- * desired bit is found or we reach the end.  Return the bit offset, -1 if
- * not found, or -2 if error.
- */
-static int cipso_v4_bitmap_walk(const unsigned char *bitmap,
-                               u32 bitmap_len,
-                               u32 offset,
-                               u8 state)
-{
-       u32 bit_spot;
-       u32 byte_offset;
-       unsigned char bitmask;
-       unsigned char byte;
-
-       /* gcc always rounds to zero when doing integer division */
-       byte_offset = offset / 8;
-       byte = bitmap[byte_offset];
-       bit_spot = offset;
-       bitmask = 0x80 >> (offset % 8);
-
-       while (bit_spot < bitmap_len) {
-               if ((state && (byte & bitmask) == bitmask) ||
-                   (state == 0 && (byte & bitmask) == 0))
-                       return bit_spot;
-
-               bit_spot++;
-               bitmask >>= 1;
-               if (bitmask == 0) {
-                       byte = bitmap[++byte_offset];
-                       bitmask = 0x80;
-               }
-       }
-
-       return -1;
-}
-
-/**
- * cipso_v4_bitmap_setbit - Sets a single bit in a bitmap
- * @bitmap: the bitmap
- * @bit: the bit
- * @state: if non-zero, set the bit (1) else clear the bit (0)
- *
- * Description:
- * Set a single bit in the bitmask.  Returns zero on success, negative values
- * on error.
- */
-static void cipso_v4_bitmap_setbit(unsigned char *bitmap,
-                                  u32 bit,
-                                  u8 state)
-{
-       u32 byte_spot;
-       u8 bitmask;
-
-       /* gcc always rounds to zero when doing integer division */
-       byte_spot = bit / 8;
-       bitmask = 0x80 >> (bit % 8);
-       if (state)
-               bitmap[byte_spot] |= bitmask;
-       else
-               bitmap[byte_spot] &= ~bitmask;
-}
-
 /**
  * cipso_v4_cache_entry_free - Frees a cache entry
  * @entry: the entry to free
@@ -840,10 +770,10 @@ static int cipso_v4_map_cat_rbm_valid(const struct cipso_v4_doi *doi_def,
                cipso_cat_size = doi_def->map.std->cat.cipso_size;
                cipso_array = doi_def->map.std->cat.cipso;
                for (;;) {
-                       cat = cipso_v4_bitmap_walk(bitmap,
-                                                  bitmap_len_bits,
-                                                  cat + 1,
-                                                  1);
+                       cat = netlbl_bitmap_walk(bitmap,
+                                                bitmap_len_bits,
+                                                cat + 1,
+                                                1);
                        if (cat < 0)
                                break;
                        if (cat >= cipso_cat_size ||
@@ -909,7 +839,7 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def,
                }
                if (net_spot >= net_clen_bits)
                        return -ENOSPC;
-               cipso_v4_bitmap_setbit(net_cat, net_spot, 1);
+               netlbl_bitmap_setbit(net_cat, net_spot, 1);
 
                if (net_spot > net_spot_max)
                        net_spot_max = net_spot;
@@ -951,10 +881,10 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def,
        }
 
        for (;;) {
-               net_spot = cipso_v4_bitmap_walk(net_cat,
-                                               net_clen_bits,
-                                               net_spot + 1,
-                                               1);
+               net_spot = netlbl_bitmap_walk(net_cat,
+                                             net_clen_bits,
+                                             net_spot + 1,
+                                             1);
                if (net_spot < 0) {
                        if (net_spot == -2)
                                return -EFAULT;
index f9f9e375d7deb176eb65dc977408dadc97fa04e6..3ebf45b38bc309f448dbc4f27fe8722cefabaf19 100644 (file)
@@ -6147,6 +6147,9 @@ struct request_sock *inet_reqsk_alloc(const struct request_sock_ops *ops,
 
                kmemcheck_annotate_bitfield(ireq, flags);
                ireq->opt = NULL;
+#if IS_ENABLED(CONFIG_IPV6)
+               ireq->pktopts = NULL;
+#endif
                atomic64_set(&ireq->ir_cookie, 0);
                ireq->ireq_state = TCP_NEW_SYN_RECV;
                write_pnet(&ireq->ireq_net, sock_net(sk_listener));
index 6d8ea099213e5084927833570e326fcf2cef628a..c174ccb340a15bca6e712932ae5e7a9c34739efb 100644 (file)
@@ -22,6 +22,7 @@ ipv6-$(CONFIG_NETFILTER) += netfilter.o
 ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o
 ipv6-$(CONFIG_PROC_FS) += proc.o
 ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o
+ipv6-$(CONFIG_NETLABEL) += calipso.o
 
 ipv6-objs += $(ipv6-y)
 
index 2076c21107d07e4e78a0a29f1d374c3414b8e1bd..b454055ba625a20ed6f470191ad9a6c6366edfe6 100644 (file)
@@ -60,6 +60,7 @@
 #ifdef CONFIG_IPV6_TUNNEL
 #include <net/ip6_tunnel.h>
 #endif
+#include <net/calipso.h>
 
 #include <asm/uaccess.h>
 #include <linux/mroute6.h>
@@ -983,6 +984,10 @@ static int __init inet6_init(void)
        if (err)
                goto pingv6_fail;
 
+       err = calipso_init();
+       if (err)
+               goto calipso_fail;
+
 #ifdef CONFIG_SYSCTL
        err = ipv6_sysctl_register();
        if (err)
@@ -993,8 +998,10 @@ out:
 
 #ifdef CONFIG_SYSCTL
 sysctl_fail:
-       pingv6_exit();
+       calipso_exit();
 #endif
+calipso_fail:
+       pingv6_exit();
 pingv6_fail:
        ipv6_packet_cleanup();
 ipv6_packet_fail:
diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c
new file mode 100644 (file)
index 0000000..c53b92c
--- /dev/null
@@ -0,0 +1,1473 @@
+/*
+ * CALIPSO - Common Architecture Label IPv6 Security Option
+ *
+ * This is an implementation of the CALIPSO protocol as specified in
+ * RFC 5570.
+ *
+ * Authors: Paul Moore <paul.moore@hp.com>
+ *          Huw Davies <huw@codeweavers.com>
+ *
+ */
+
+/* (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
+ * (c) Copyright Huw Davies <huw@codeweavers.com>, 2015
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY;  without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program;  if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/rcupdate.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/jhash.h>
+#include <linux/audit.h>
+#include <linux/slab.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/tcp.h>
+#include <net/netlabel.h>
+#include <net/calipso.h>
+#include <linux/atomic.h>
+#include <linux/bug.h>
+#include <asm/unaligned.h>
+#include <linux/crc-ccitt.h>
+
+/* Maximium size of the calipso option including
+ * the two-byte TLV header.
+ */
+#define CALIPSO_OPT_LEN_MAX (2 + 252)
+
+/* Size of the minimum calipso option including
+ * the two-byte TLV header.
+ */
+#define CALIPSO_HDR_LEN (2 + 8)
+
+/* Maximium size of the calipso option including
+ * the two-byte TLV header and upto 3 bytes of
+ * leading pad and 7 bytes of trailing pad.
+ */
+#define CALIPSO_OPT_LEN_MAX_WITH_PAD (3 + CALIPSO_OPT_LEN_MAX + 7)
+
+ /* Maximium size of u32 aligned buffer required to hold calipso
+  * option.  Max of 3 initial pad bytes starting from buffer + 3.
+  * i.e. the worst case is when the previous tlv finishes on 4n + 3.
+  */
+#define CALIPSO_MAX_BUFFER (6 + CALIPSO_OPT_LEN_MAX)
+
+/* List of available DOI definitions */
+static DEFINE_SPINLOCK(calipso_doi_list_lock);
+static LIST_HEAD(calipso_doi_list);
+
+/* Label mapping cache */
+int calipso_cache_enabled = 1;
+int calipso_cache_bucketsize = 10;
+#define CALIPSO_CACHE_BUCKETBITS     7
+#define CALIPSO_CACHE_BUCKETS        BIT(CALIPSO_CACHE_BUCKETBITS)
+#define CALIPSO_CACHE_REORDERLIMIT   10
+struct calipso_map_cache_bkt {
+       spinlock_t lock;
+       u32 size;
+       struct list_head list;
+};
+
+struct calipso_map_cache_entry {
+       u32 hash;
+       unsigned char *key;
+       size_t key_len;
+
+       struct netlbl_lsm_cache *lsm_data;
+
+       u32 activity;
+       struct list_head list;
+};
+
+static struct calipso_map_cache_bkt *calipso_cache;
+
+/* Label Mapping Cache Functions
+ */
+
+/**
+ * calipso_cache_entry_free - Frees a cache entry
+ * @entry: the entry to free
+ *
+ * Description:
+ * This function frees the memory associated with a cache entry including the
+ * LSM cache data if there are no longer any users, i.e. reference count == 0.
+ *
+ */
+static void calipso_cache_entry_free(struct calipso_map_cache_entry *entry)
+{
+       if (entry->lsm_data)
+               netlbl_secattr_cache_free(entry->lsm_data);
+       kfree(entry->key);
+       kfree(entry);
+}
+
+/**
+ * calipso_map_cache_hash - Hashing function for the CALIPSO cache
+ * @key: the hash key
+ * @key_len: the length of the key in bytes
+ *
+ * Description:
+ * The CALIPSO tag hashing function.  Returns a 32-bit hash value.
+ *
+ */
+static u32 calipso_map_cache_hash(const unsigned char *key, u32 key_len)
+{
+       return jhash(key, key_len, 0);
+}
+
+/**
+ * calipso_cache_init - Initialize the CALIPSO cache
+ *
+ * Description:
+ * Initializes the CALIPSO label mapping cache, this function should be called
+ * before any of the other functions defined in this file.  Returns zero on
+ * success, negative values on error.
+ *
+ */
+static int __init calipso_cache_init(void)
+{
+       u32 iter;
+
+       calipso_cache = kcalloc(CALIPSO_CACHE_BUCKETS,
+                               sizeof(struct calipso_map_cache_bkt),
+                               GFP_KERNEL);
+       if (!calipso_cache)
+               return -ENOMEM;
+
+       for (iter = 0; iter < CALIPSO_CACHE_BUCKETS; iter++) {
+               spin_lock_init(&calipso_cache[iter].lock);
+               calipso_cache[iter].size = 0;
+               INIT_LIST_HEAD(&calipso_cache[iter].list);
+       }
+
+       return 0;
+}
+
+/**
+ * calipso_cache_invalidate - Invalidates the current CALIPSO cache
+ *
+ * Description:
+ * Invalidates and frees any entries in the CALIPSO cache.  Returns zero on
+ * success and negative values on failure.
+ *
+ */
+static void calipso_cache_invalidate(void)
+{
+       struct calipso_map_cache_entry *entry, *tmp_entry;
+       u32 iter;
+
+       for (iter = 0; iter < CALIPSO_CACHE_BUCKETS; iter++) {
+               spin_lock_bh(&calipso_cache[iter].lock);
+               list_for_each_entry_safe(entry,
+                                        tmp_entry,
+                                        &calipso_cache[iter].list, list) {
+                       list_del(&entry->list);
+                       calipso_cache_entry_free(entry);
+               }
+               calipso_cache[iter].size = 0;
+               spin_unlock_bh(&calipso_cache[iter].lock);
+       }
+}
+
+/**
+ * calipso_cache_check - Check the CALIPSO cache for a label mapping
+ * @key: the buffer to check
+ * @key_len: buffer length in bytes
+ * @secattr: the security attribute struct to use
+ *
+ * Description:
+ * This function checks the cache to see if a label mapping already exists for
+ * the given key.  If there is a match then the cache is adjusted and the
+ * @secattr struct is populated with the correct LSM security attributes.  The
+ * cache is adjusted in the following manner if the entry is not already the
+ * first in the cache bucket:
+ *
+ *  1. The cache entry's activity counter is incremented
+ *  2. The previous (higher ranking) entry's activity counter is decremented
+ *  3. If the difference between the two activity counters is geater than
+ *     CALIPSO_CACHE_REORDERLIMIT the two entries are swapped
+ *
+ * Returns zero on success, -ENOENT for a cache miss, and other negative values
+ * on error.
+ *
+ */
+static int calipso_cache_check(const unsigned char *key,
+                              u32 key_len,
+                              struct netlbl_lsm_secattr *secattr)
+{
+       u32 bkt;
+       struct calipso_map_cache_entry *entry;
+       struct calipso_map_cache_entry *prev_entry = NULL;
+       u32 hash;
+
+       if (!calipso_cache_enabled)
+               return -ENOENT;
+
+       hash = calipso_map_cache_hash(key, key_len);
+       bkt = hash & (CALIPSO_CACHE_BUCKETS - 1);
+       spin_lock_bh(&calipso_cache[bkt].lock);
+       list_for_each_entry(entry, &calipso_cache[bkt].list, list) {
+               if (entry->hash == hash &&
+                   entry->key_len == key_len &&
+                   memcmp(entry->key, key, key_len) == 0) {
+                       entry->activity += 1;
+                       atomic_inc(&entry->lsm_data->refcount);
+                       secattr->cache = entry->lsm_data;
+                       secattr->flags |= NETLBL_SECATTR_CACHE;
+                       secattr->type = NETLBL_NLTYPE_CALIPSO;
+                       if (!prev_entry) {
+                               spin_unlock_bh(&calipso_cache[bkt].lock);
+                               return 0;
+                       }
+
+                       if (prev_entry->activity > 0)
+                               prev_entry->activity -= 1;
+                       if (entry->activity > prev_entry->activity &&
+                           entry->activity - prev_entry->activity >
+                           CALIPSO_CACHE_REORDERLIMIT) {
+                               __list_del(entry->list.prev, entry->list.next);
+                               __list_add(&entry->list,
+                                          prev_entry->list.prev,
+                                          &prev_entry->list);
+                       }
+
+                       spin_unlock_bh(&calipso_cache[bkt].lock);
+                       return 0;
+               }
+               prev_entry = entry;
+       }
+       spin_unlock_bh(&calipso_cache[bkt].lock);
+
+       return -ENOENT;
+}
+
+/**
+ * calipso_cache_add - Add an entry to the CALIPSO cache
+ * @calipso_ptr: the CALIPSO option
+ * @secattr: the packet's security attributes
+ *
+ * Description:
+ * Add a new entry into the CALIPSO label mapping cache.  Add the new entry to
+ * head of the cache bucket's list, if the cache bucket is out of room remove
+ * the last entry in the list first.  It is important to note that there is
+ * currently no checking for duplicate keys.  Returns zero on success,
+ * negative values on failure.  The key stored starts at calipso_ptr + 2,
+ * i.e. the type and length bytes are not stored, this corresponds to
+ * calipso_ptr[1] bytes of data.
+ *
+ */
+static int calipso_cache_add(const unsigned char *calipso_ptr,
+                            const struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val = -EPERM;
+       u32 bkt;
+       struct calipso_map_cache_entry *entry = NULL;
+       struct calipso_map_cache_entry *old_entry = NULL;
+       u32 calipso_ptr_len;
+
+       if (!calipso_cache_enabled || calipso_cache_bucketsize <= 0)
+               return 0;
+
+       calipso_ptr_len = calipso_ptr[1];
+
+       entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+       if (!entry)
+               return -ENOMEM;
+       entry->key = kmemdup(calipso_ptr + 2, calipso_ptr_len, GFP_ATOMIC);
+       if (!entry->key) {
+               ret_val = -ENOMEM;
+               goto cache_add_failure;
+       }
+       entry->key_len = calipso_ptr_len;
+       entry->hash = calipso_map_cache_hash(calipso_ptr, calipso_ptr_len);
+       atomic_inc(&secattr->cache->refcount);
+       entry->lsm_data = secattr->cache;
+
+       bkt = entry->hash & (CALIPSO_CACHE_BUCKETS - 1);
+       spin_lock_bh(&calipso_cache[bkt].lock);
+       if (calipso_cache[bkt].size < calipso_cache_bucketsize) {
+               list_add(&entry->list, &calipso_cache[bkt].list);
+               calipso_cache[bkt].size += 1;
+       } else {
+               old_entry = list_entry(calipso_cache[bkt].list.prev,
+                                      struct calipso_map_cache_entry, list);
+               list_del(&old_entry->list);
+               list_add(&entry->list, &calipso_cache[bkt].list);
+               calipso_cache_entry_free(old_entry);
+       }
+       spin_unlock_bh(&calipso_cache[bkt].lock);
+
+       return 0;
+
+cache_add_failure:
+       if (entry)
+               calipso_cache_entry_free(entry);
+       return ret_val;
+}
+
+/* DOI List Functions
+ */
+
+/**
+ * calipso_doi_search - Searches for a DOI definition
+ * @doi: the DOI to search for
+ *
+ * Description:
+ * Search the DOI definition list for a DOI definition with a DOI value that
+ * matches @doi.  The caller is responsible for calling rcu_read_[un]lock().
+ * Returns a pointer to the DOI definition on success and NULL on failure.
+ */
+static struct calipso_doi *calipso_doi_search(u32 doi)
+{
+       struct calipso_doi *iter;
+
+       list_for_each_entry_rcu(iter, &calipso_doi_list, list)
+               if (iter->doi == doi && atomic_read(&iter->refcount))
+                       return iter;
+       return NULL;
+}
+
+/**
+ * calipso_doi_add - Add a new DOI to the CALIPSO protocol engine
+ * @doi_def: the DOI structure
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * The caller defines a new DOI for use by the CALIPSO engine and calls this
+ * function to add it to the list of acceptable domains.  The caller must
+ * ensure that the mapping table specified in @doi_def->map meets all of the
+ * requirements of the mapping type (see calipso.h for details).  Returns
+ * zero on success and non-zero on failure.
+ *
+ */
+static int calipso_doi_add(struct calipso_doi *doi_def,
+                          struct netlbl_audit *audit_info)
+{
+       int ret_val = -EINVAL;
+       u32 doi;
+       u32 doi_type;
+       struct audit_buffer *audit_buf;
+
+       doi = doi_def->doi;
+       doi_type = doi_def->type;
+
+       if (doi_def->doi == CALIPSO_DOI_UNKNOWN)
+               goto doi_add_return;
+
+       atomic_set(&doi_def->refcount, 1);
+
+       spin_lock(&calipso_doi_list_lock);
+       if (calipso_doi_search(doi_def->doi)) {
+               spin_unlock(&calipso_doi_list_lock);
+               ret_val = -EEXIST;
+               goto doi_add_return;
+       }
+       list_add_tail_rcu(&doi_def->list, &calipso_doi_list);
+       spin_unlock(&calipso_doi_list_lock);
+       ret_val = 0;
+
+doi_add_return:
+       audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_ADD, audit_info);
+       if (audit_buf) {
+               const char *type_str;
+
+               switch (doi_type) {
+               case CALIPSO_MAP_PASS:
+                       type_str = "pass";
+                       break;
+               default:
+                       type_str = "(unknown)";
+               }
+               audit_log_format(audit_buf,
+                                " calipso_doi=%u calipso_type=%s res=%u",
+                                doi, type_str, ret_val == 0 ? 1 : 0);
+               audit_log_end(audit_buf);
+       }
+
+       return ret_val;
+}
+
+/**
+ * calipso_doi_free - Frees a DOI definition
+ * @doi_def: the DOI definition
+ *
+ * Description:
+ * This function frees all of the memory associated with a DOI definition.
+ *
+ */
+static void calipso_doi_free(struct calipso_doi *doi_def)
+{
+       kfree(doi_def);
+}
+
+/**
+ * calipso_doi_free_rcu - Frees a DOI definition via the RCU pointer
+ * @entry: the entry's RCU field
+ *
+ * Description:
+ * This function is designed to be used as a callback to the call_rcu()
+ * function so that the memory allocated to the DOI definition can be released
+ * safely.
+ *
+ */
+static void calipso_doi_free_rcu(struct rcu_head *entry)
+{
+       struct calipso_doi *doi_def;
+
+       doi_def = container_of(entry, struct calipso_doi, rcu);
+       calipso_doi_free(doi_def);
+}
+
+/**
+ * calipso_doi_remove - Remove an existing DOI from the CALIPSO protocol engine
+ * @doi: the DOI value
+ * @audit_secid: the LSM secid to use in the audit message
+ *
+ * Description:
+ * Removes a DOI definition from the CALIPSO engine.  The NetLabel routines will
+ * be called to release their own LSM domain mappings as well as our own
+ * domain list.  Returns zero on success and negative values on failure.
+ *
+ */
+static int calipso_doi_remove(u32 doi, struct netlbl_audit *audit_info)
+{
+       int ret_val;
+       struct calipso_doi *doi_def;
+       struct audit_buffer *audit_buf;
+
+       spin_lock(&calipso_doi_list_lock);
+       doi_def = calipso_doi_search(doi);
+       if (!doi_def) {
+               spin_unlock(&calipso_doi_list_lock);
+               ret_val = -ENOENT;
+               goto doi_remove_return;
+       }
+       if (!atomic_dec_and_test(&doi_def->refcount)) {
+               spin_unlock(&calipso_doi_list_lock);
+               ret_val = -EBUSY;
+               goto doi_remove_return;
+       }
+       list_del_rcu(&doi_def->list);
+       spin_unlock(&calipso_doi_list_lock);
+
+       call_rcu(&doi_def->rcu, calipso_doi_free_rcu);
+       ret_val = 0;
+
+doi_remove_return:
+       audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_DEL, audit_info);
+       if (audit_buf) {
+               audit_log_format(audit_buf,
+                                " calipso_doi=%u res=%u",
+                                doi, ret_val == 0 ? 1 : 0);
+               audit_log_end(audit_buf);
+       }
+
+       return ret_val;
+}
+
+/**
+ * calipso_doi_getdef - Returns a reference to a valid DOI definition
+ * @doi: the DOI value
+ *
+ * Description:
+ * Searches for a valid DOI definition and if one is found it is returned to
+ * the caller.  Otherwise NULL is returned.  The caller must ensure that
+ * calipso_doi_putdef() is called when the caller is done.
+ *
+ */
+static struct calipso_doi *calipso_doi_getdef(u32 doi)
+{
+       struct calipso_doi *doi_def;
+
+       rcu_read_lock();
+       doi_def = calipso_doi_search(doi);
+       if (!doi_def)
+               goto doi_getdef_return;
+       if (!atomic_inc_not_zero(&doi_def->refcount))
+               doi_def = NULL;
+
+doi_getdef_return:
+       rcu_read_unlock();
+       return doi_def;
+}
+
+/**
+ * calipso_doi_putdef - Releases a reference for the given DOI definition
+ * @doi_def: the DOI definition
+ *
+ * Description:
+ * Releases a DOI definition reference obtained from calipso_doi_getdef().
+ *
+ */
+static void calipso_doi_putdef(struct calipso_doi *doi_def)
+{
+       if (!doi_def)
+               return;
+
+       if (!atomic_dec_and_test(&doi_def->refcount))
+               return;
+       spin_lock(&calipso_doi_list_lock);
+       list_del_rcu(&doi_def->list);
+       spin_unlock(&calipso_doi_list_lock);
+
+       call_rcu(&doi_def->rcu, calipso_doi_free_rcu);
+}
+
+/**
+ * calipso_doi_walk - Iterate through the DOI definitions
+ * @skip_cnt: skip past this number of DOI definitions, updated
+ * @callback: callback for each DOI definition
+ * @cb_arg: argument for the callback function
+ *
+ * Description:
+ * Iterate over the DOI definition list, skipping the first @skip_cnt entries.
+ * For each entry call @callback, if @callback returns a negative value stop
+ * 'walking' through the list and return.  Updates the value in @skip_cnt upon
+ * return.  Returns zero on success, negative values on failure.
+ *
+ */
+static int calipso_doi_walk(u32 *skip_cnt,
+                           int (*callback)(struct calipso_doi *doi_def,
+                                           void *arg),
+                           void *cb_arg)
+{
+       int ret_val = -ENOENT;
+       u32 doi_cnt = 0;
+       struct calipso_doi *iter_doi;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(iter_doi, &calipso_doi_list, list)
+               if (atomic_read(&iter_doi->refcount) > 0) {
+                       if (doi_cnt++ < *skip_cnt)
+                               continue;
+                       ret_val = callback(iter_doi, cb_arg);
+                       if (ret_val < 0) {
+                               doi_cnt--;
+                               goto doi_walk_return;
+                       }
+               }
+
+doi_walk_return:
+       rcu_read_unlock();
+       *skip_cnt = doi_cnt;
+       return ret_val;
+}
+
+/**
+ * calipso_validate - Validate a CALIPSO option
+ * @skb: the packet
+ * @option: the start of the option
+ *
+ * Description:
+ * This routine is called to validate a CALIPSO option.
+ * If the option is valid then %true is returned, otherwise
+ * %false is returned.
+ *
+ * The caller should have already checked that the length of the
+ * option (including the TLV header) is >= 10 and that the catmap
+ * length is consistent with the option length.
+ *
+ * We leave checks on the level and categories to the socket layer.
+ */
+bool calipso_validate(const struct sk_buff *skb, const unsigned char *option)
+{
+       struct calipso_doi *doi_def;
+       bool ret_val;
+       u16 crc, len = option[1] + 2;
+       static const u8 zero[2];
+
+       /* The original CRC runs over the option including the TLV header
+        * with the CRC-16 field (at offset 8) zeroed out. */
+       crc = crc_ccitt(0xffff, option, 8);
+       crc = crc_ccitt(crc, zero, sizeof(zero));
+       if (len > 10)
+               crc = crc_ccitt(crc, option + 10, len - 10);
+       crc = ~crc;
+       if (option[8] != (crc & 0xff) || option[9] != ((crc >> 8) & 0xff))
+               return false;
+
+       rcu_read_lock();
+       doi_def = calipso_doi_search(get_unaligned_be32(option + 2));
+       ret_val = !!doi_def;
+       rcu_read_unlock();
+
+       return ret_val;
+}
+
+/**
+ * calipso_map_cat_hton - Perform a category mapping from host to network
+ * @doi_def: the DOI definition
+ * @secattr: the security attributes
+ * @net_cat: the zero'd out category bitmap in network/CALIPSO format
+ * @net_cat_len: the length of the CALIPSO bitmap in bytes
+ *
+ * Description:
+ * Perform a label mapping to translate a local MLS category bitmap to the
+ * correct CALIPSO bitmap using the given DOI definition.  Returns the minimum
+ * size in bytes of the network bitmap on success, negative values otherwise.
+ *
+ */
+static int calipso_map_cat_hton(const struct calipso_doi *doi_def,
+                               const struct netlbl_lsm_secattr *secattr,
+                               unsigned char *net_cat,
+                               u32 net_cat_len)
+{
+       int spot = -1;
+       u32 net_spot_max = 0;
+       u32 net_clen_bits = net_cat_len * 8;
+
+       for (;;) {
+               spot = netlbl_catmap_walk(secattr->attr.mls.cat,
+                                         spot + 1);
+               if (spot < 0)
+                       break;
+               if (spot >= net_clen_bits)
+                       return -ENOSPC;
+               netlbl_bitmap_setbit(net_cat, spot, 1);
+
+               if (spot > net_spot_max)
+                       net_spot_max = spot;
+       }
+
+       return (net_spot_max / 32 + 1) * 4;
+}
+
+/**
+ * calipso_map_cat_ntoh - Perform a category mapping from network to host
+ * @doi_def: the DOI definition
+ * @net_cat: the category bitmap in network/CALIPSO format
+ * @net_cat_len: the length of the CALIPSO bitmap in bytes
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Perform a label mapping to translate a CALIPSO bitmap to the correct local
+ * MLS category bitmap using the given DOI definition.  Returns zero on
+ * success, negative values on failure.
+ *
+ */
+static int calipso_map_cat_ntoh(const struct calipso_doi *doi_def,
+                               const unsigned char *net_cat,
+                               u32 net_cat_len,
+                               struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val;
+       int spot = -1;
+       u32 net_clen_bits = net_cat_len * 8;
+
+       for (;;) {
+               spot = netlbl_bitmap_walk(net_cat,
+                                         net_clen_bits,
+                                         spot + 1,
+                                         1);
+               if (spot < 0) {
+                       if (spot == -2)
+                               return -EFAULT;
+                       return 0;
+               }
+
+               ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat,
+                                              spot,
+                                              GFP_ATOMIC);
+               if (ret_val != 0)
+                       return ret_val;
+       }
+
+       return -EINVAL;
+}
+
+/**
+ * calipso_pad_write - Writes pad bytes in TLV format
+ * @buf: the buffer
+ * @offset: offset from start of buffer to write padding
+ * @count: number of pad bytes to write
+ *
+ * Description:
+ * Write @count bytes of TLV padding into @buffer starting at offset @offset.
+ * @count should be less than 8 - see RFC 4942.
+ *
+ */
+static int calipso_pad_write(unsigned char *buf, unsigned int offset,
+                            unsigned int count)
+{
+       if (WARN_ON_ONCE(count >= 8))
+               return -EINVAL;
+
+       switch (count) {
+       case 0:
+               break;
+       case 1:
+               buf[offset] = IPV6_TLV_PAD1;
+               break;
+       default:
+               buf[offset] = IPV6_TLV_PADN;
+               buf[offset + 1] = count - 2;
+               if (count > 2)
+                       memset(buf + offset + 2, 0, count - 2);
+               break;
+       }
+       return 0;
+}
+
+/**
+ * calipso_genopt - Generate a CALIPSO option
+ * @buf: the option buffer
+ * @start: offset from which to write
+ * @buf_len: the size of opt_buf
+ * @doi_def: the CALIPSO DOI to use
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Generate a CALIPSO option using the DOI definition and security attributes
+ * passed to the function. This also generates upto three bytes of leading
+ * padding that ensures that the option is 4n + 2 aligned.  It returns the
+ * number of bytes written (including any initial padding).
+ */
+static int calipso_genopt(unsigned char *buf, u32 start, u32 buf_len,
+                         const struct calipso_doi *doi_def,
+                         const struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val;
+       u32 len, pad;
+       u16 crc;
+       static const unsigned char padding[4] = {2, 1, 0, 3};
+       unsigned char *calipso;
+
+       /* CALIPSO has 4n + 2 alignment */
+       pad = padding[start & 3];
+       if (buf_len <= start + pad + CALIPSO_HDR_LEN)
+               return -ENOSPC;
+
+       if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0)
+               return -EPERM;
+
+       len = CALIPSO_HDR_LEN;
+
+       if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
+               ret_val = calipso_map_cat_hton(doi_def,
+                                              secattr,
+                                              buf + start + pad + len,
+                                              buf_len - start - pad - len);
+               if (ret_val < 0)
+                       return ret_val;
+               len += ret_val;
+       }
+
+       calipso_pad_write(buf, start, pad);
+       calipso = buf + start + pad;
+
+       calipso[0] = IPV6_TLV_CALIPSO;
+       calipso[1] = len - 2;
+       *(__be32 *)(calipso + 2) = htonl(doi_def->doi);
+       calipso[6] = (len - CALIPSO_HDR_LEN) / 4;
+       calipso[7] = secattr->attr.mls.lvl,
+       crc = ~crc_ccitt(0xffff, calipso, len);
+       calipso[8] = crc & 0xff;
+       calipso[9] = (crc >> 8) & 0xff;
+       return pad + len;
+}
+
+/* Hop-by-hop hdr helper functions
+ */
+
+/**
+ * calipso_opt_update - Replaces socket's hop options with a new set
+ * @sk: the socket
+ * @hop: new hop options
+ *
+ * Description:
+ * Replaces @sk's hop options with @hop.  @hop may be NULL to leave
+ * the socket with no hop options.
+ *
+ */
+static int calipso_opt_update(struct sock *sk, struct ipv6_opt_hdr *hop)
+{
+       struct ipv6_txoptions *old = txopt_get(inet6_sk(sk)), *txopts;
+
+       txopts = ipv6_renew_options_kern(sk, old, IPV6_HOPOPTS,
+                                        hop, hop ? ipv6_optlen(hop) : 0);
+       txopt_put(old);
+       if (IS_ERR(txopts))
+               return PTR_ERR(txopts);
+
+       txopts = ipv6_update_options(sk, txopts);
+       if (txopts) {
+               atomic_sub(txopts->tot_len, &sk->sk_omem_alloc);
+               txopt_put(txopts);
+       }
+
+       return 0;
+}
+
+/**
+ * calipso_tlv_len - Returns the length of the TLV
+ * @opt: the option header
+ * @offset: offset of the TLV within the header
+ *
+ * Description:
+ * Returns the length of the TLV option at offset @offset within
+ * the option header @opt.  Checks that the entire TLV fits inside
+ * the option header, returns a negative value if this is not the case.
+ */
+static int calipso_tlv_len(struct ipv6_opt_hdr *opt, unsigned int offset)
+{
+       unsigned char *tlv = (unsigned char *)opt;
+       unsigned int opt_len = ipv6_optlen(opt), tlv_len;
+
+       if (offset < sizeof(*opt) || offset >= opt_len)
+               return -EINVAL;
+       if (tlv[offset] == IPV6_TLV_PAD1)
+               return 1;
+       if (offset + 1 >= opt_len)
+               return -EINVAL;
+       tlv_len = tlv[offset + 1] + 2;
+       if (offset + tlv_len > opt_len)
+               return -EINVAL;
+       return tlv_len;
+}
+
+/**
+ * calipso_opt_find - Finds the CALIPSO option in an IPv6 hop options header
+ * @hop: the hop options header
+ * @start: on return holds the offset of any leading padding
+ * @end: on return holds the offset of the first non-pad TLV after CALIPSO
+ *
+ * Description:
+ * Finds the space occupied by a CALIPSO option (including any leading and
+ * trailing padding).
+ *
+ * If a CALIPSO option exists set @start and @end to the
+ * offsets within @hop of the start of padding before the first
+ * CALIPSO option and the end of padding after the first CALIPSO
+ * option.  In this case the function returns 0.
+ *
+ * In the absence of a CALIPSO option, @start and @end will be
+ * set to the start and end of any trailing padding in the header.
+ * This is useful when appending a new option, as the caller may want
+ * to overwrite some of this padding.  In this case the function will
+ * return -ENOENT.
+ */
+static int calipso_opt_find(struct ipv6_opt_hdr *hop, unsigned int *start,
+                           unsigned int *end)
+{
+       int ret_val = -ENOENT, tlv_len;
+       unsigned int opt_len, offset, offset_s = 0, offset_e = 0;
+       unsigned char *opt = (unsigned char *)hop;
+
+       opt_len = ipv6_optlen(hop);
+       offset = sizeof(*hop);
+
+       while (offset < opt_len) {
+               tlv_len = calipso_tlv_len(hop, offset);
+               if (tlv_len < 0)
+                       return tlv_len;
+
+               switch (opt[offset]) {
+               case IPV6_TLV_PAD1:
+               case IPV6_TLV_PADN:
+                       if (offset_e)
+                               offset_e = offset;
+                       break;
+               case IPV6_TLV_CALIPSO:
+                       ret_val = 0;
+                       offset_e = offset;
+                       break;
+               default:
+                       if (offset_e == 0)
+                               offset_s = offset;
+                       else
+                               goto out;
+               }
+               offset += tlv_len;
+       }
+
+out:
+       if (offset_s)
+               *start = offset_s + calipso_tlv_len(hop, offset_s);
+       else
+               *start = sizeof(*hop);
+       if (offset_e)
+               *end = offset_e + calipso_tlv_len(hop, offset_e);
+       else
+               *end = opt_len;
+
+       return ret_val;
+}
+
+/**
+ * calipso_opt_insert - Inserts a CALIPSO option into an IPv6 hop opt hdr
+ * @hop: the original hop options header
+ * @doi_def: the CALIPSO DOI to use
+ * @secattr: the specific security attributes of the socket
+ *
+ * Description:
+ * Creates a new hop options header based on @hop with a
+ * CALIPSO option added to it.  If @hop already contains a CALIPSO
+ * option this is overwritten, otherwise the new option is appended
+ * after any existing options.  If @hop is NULL then the new header
+ * will contain just the CALIPSO option and any needed padding.
+ *
+ */
+static struct ipv6_opt_hdr *
+calipso_opt_insert(struct ipv6_opt_hdr *hop,
+                  const struct calipso_doi *doi_def,
+                  const struct netlbl_lsm_secattr *secattr)
+{
+       unsigned int start, end, buf_len, pad, hop_len;
+       struct ipv6_opt_hdr *new;
+       int ret_val;
+
+       if (hop) {
+               hop_len = ipv6_optlen(hop);
+               ret_val = calipso_opt_find(hop, &start, &end);
+               if (ret_val && ret_val != -ENOENT)
+                       return ERR_PTR(ret_val);
+       } else {
+               hop_len = 0;
+               start = sizeof(*hop);
+               end = 0;
+       }
+
+       buf_len = hop_len + start - end + CALIPSO_OPT_LEN_MAX_WITH_PAD;
+       new = kzalloc(buf_len, GFP_ATOMIC);
+       if (!new)
+               return ERR_PTR(-ENOMEM);
+
+       if (start > sizeof(*hop))
+               memcpy(new, hop, start);
+       ret_val = calipso_genopt((unsigned char *)new, start, buf_len, doi_def,
+                                secattr);
+       if (ret_val < 0)
+               return ERR_PTR(ret_val);
+
+       buf_len = start + ret_val;
+       /* At this point buf_len aligns to 4n, so (buf_len & 4) pads to 8n */
+       pad = ((buf_len & 4) + (end & 7)) & 7;
+       calipso_pad_write((unsigned char *)new, buf_len, pad);
+       buf_len += pad;
+
+       if (end != hop_len) {
+               memcpy((char *)new + buf_len, (char *)hop + end, hop_len - end);
+               buf_len += hop_len - end;
+       }
+       new->nexthdr = 0;
+       new->hdrlen = buf_len / 8 - 1;
+
+       return new;
+}
+
+/**
+ * calipso_opt_del - Removes the CALIPSO option from an option header
+ * @hop: the original header
+ * @new: the new header
+ *
+ * Description:
+ * Creates a new header based on @hop without any CALIPSO option.  If @hop
+ * doesn't contain a CALIPSO option it returns -ENOENT.  If @hop contains
+ * no other non-padding options, it returns zero with @new set to NULL.
+ * Otherwise it returns zero, creates a new header without the CALIPSO
+ * option (and removing as much padding as possible) and returns with
+ * @new set to that header.
+ *
+ */
+static int calipso_opt_del(struct ipv6_opt_hdr *hop,
+                          struct ipv6_opt_hdr **new)
+{
+       int ret_val;
+       unsigned int start, end, delta, pad, hop_len;
+
+       ret_val = calipso_opt_find(hop, &start, &end);
+       if (ret_val)
+               return ret_val;
+
+       hop_len = ipv6_optlen(hop);
+       if (start == sizeof(*hop) && end == hop_len) {
+               /* There's no other option in the header so return NULL */
+               *new = NULL;
+               return 0;
+       }
+
+       delta = (end - start) & ~7;
+       *new = kzalloc(hop_len - delta, GFP_ATOMIC);
+       if (!*new)
+               return -ENOMEM;
+
+       memcpy(*new, hop, start);
+       (*new)->hdrlen -= delta / 8;
+       pad = (end - start) & 7;
+       calipso_pad_write((unsigned char *)*new, start, pad);
+       if (end != hop_len)
+               memcpy((char *)*new + start + pad, (char *)hop + end,
+                      hop_len - end);
+
+       return 0;
+}
+
+/**
+ * calipso_opt_getattr - Get the security attributes from a memory block
+ * @calipso: the CALIPSO option
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Inspect @calipso and return the security attributes in @secattr.
+ * Returns zero on success and negative values on failure.
+ *
+ */
+static int calipso_opt_getattr(const unsigned char *calipso,
+                              struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val = -ENOMSG;
+       u32 doi, len = calipso[1], cat_len = calipso[6] * 4;
+       struct calipso_doi *doi_def;
+
+       if (cat_len + 8 > len)
+               return -EINVAL;
+
+       if (calipso_cache_check(calipso + 2, calipso[1], secattr) == 0)
+               return 0;
+
+       doi = get_unaligned_be32(calipso + 2);
+       rcu_read_lock();
+       doi_def = calipso_doi_search(doi);
+       if (!doi_def)
+               goto getattr_return;
+
+       secattr->attr.mls.lvl = calipso[7];
+       secattr->flags |= NETLBL_SECATTR_MLS_LVL;
+
+       if (cat_len) {
+               ret_val = calipso_map_cat_ntoh(doi_def,
+                                              calipso + 10,
+                                              cat_len,
+                                              secattr);
+               if (ret_val != 0) {
+                       netlbl_catmap_free(secattr->attr.mls.cat);
+                       goto getattr_return;
+               }
+
+               secattr->flags |= NETLBL_SECATTR_MLS_CAT;
+       }
+
+       secattr->type = NETLBL_NLTYPE_CALIPSO;
+
+getattr_return:
+       rcu_read_unlock();
+       return ret_val;
+}
+
+/* sock functions.
+ */
+
+/**
+ * calipso_sock_getattr - Get the security attributes from a sock
+ * @sk: the sock
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Query @sk to see if there is a CALIPSO option attached to the sock and if
+ * there is return the CALIPSO security attributes in @secattr.  This function
+ * requires that @sk be locked, or privately held, but it does not do any
+ * locking itself.  Returns zero on success and negative values on failure.
+ *
+ */
+static int calipso_sock_getattr(struct sock *sk,
+                               struct netlbl_lsm_secattr *secattr)
+{
+       struct ipv6_opt_hdr *hop;
+       int opt_len, len, ret_val = -ENOMSG, offset;
+       unsigned char *opt;
+       struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk));
+
+       if (!txopts || !txopts->hopopt)
+               goto done;
+
+       hop = txopts->hopopt;
+       opt = (unsigned char *)hop;
+       opt_len = ipv6_optlen(hop);
+       offset = sizeof(*hop);
+       while (offset < opt_len) {
+               len = calipso_tlv_len(hop, offset);
+               if (len < 0) {
+                       ret_val = len;
+                       goto done;
+               }
+               switch (opt[offset]) {
+               case IPV6_TLV_CALIPSO:
+                       if (len < CALIPSO_HDR_LEN)
+                               ret_val = -EINVAL;
+                       else
+                               ret_val = calipso_opt_getattr(&opt[offset],
+                                                             secattr);
+                       goto done;
+               default:
+                       offset += len;
+                       break;
+               }
+       }
+done:
+       txopt_put(txopts);
+       return ret_val;
+}
+
+/**
+ * calipso_sock_setattr - Add a CALIPSO option to a socket
+ * @sk: the socket
+ * @doi_def: the CALIPSO DOI to use
+ * @secattr: the specific security attributes of the socket
+ *
+ * Description:
+ * Set the CALIPSO option on the given socket using the DOI definition and
+ * security attributes passed to the function.  This function requires
+ * exclusive access to @sk, which means it either needs to be in the
+ * process of being created or locked.  Returns zero on success and negative
+ * values on failure.
+ *
+ */
+static int calipso_sock_setattr(struct sock *sk,
+                               const struct calipso_doi *doi_def,
+                               const struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val;
+       struct ipv6_opt_hdr *old, *new;
+       struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk));
+
+       old = NULL;
+       if (txopts)
+               old = txopts->hopopt;
+
+       new = calipso_opt_insert(old, doi_def, secattr);
+       txopt_put(txopts);
+       if (IS_ERR(new))
+               return PTR_ERR(new);
+
+       ret_val = calipso_opt_update(sk, new);
+
+       kfree(new);
+       return ret_val;
+}
+
+/**
+ * calipso_sock_delattr - Delete the CALIPSO option from a socket
+ * @sk: the socket
+ *
+ * Description:
+ * Removes the CALIPSO option from a socket, if present.
+ *
+ */
+static void calipso_sock_delattr(struct sock *sk)
+{
+       struct ipv6_opt_hdr *new_hop;
+       struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk));
+
+       if (!txopts || !txopts->hopopt)
+               goto done;
+
+       if (calipso_opt_del(txopts->hopopt, &new_hop))
+               goto done;
+
+       calipso_opt_update(sk, new_hop);
+       kfree(new_hop);
+
+done:
+       txopt_put(txopts);
+}
+
+/* request sock functions.
+ */
+
+/**
+ * calipso_req_setattr - Add a CALIPSO option to a connection request socket
+ * @req: the connection request socket
+ * @doi_def: the CALIPSO DOI to use
+ * @secattr: the specific security attributes of the socket
+ *
+ * Description:
+ * Set the CALIPSO option on the given socket using the DOI definition and
+ * security attributes passed to the function.  Returns zero on success and
+ * negative values on failure.
+ *
+ */
+static int calipso_req_setattr(struct request_sock *req,
+                              const struct calipso_doi *doi_def,
+                              const struct netlbl_lsm_secattr *secattr)
+{
+       struct ipv6_txoptions *txopts;
+       struct inet_request_sock *req_inet = inet_rsk(req);
+       struct ipv6_opt_hdr *old, *new;
+       struct sock *sk = sk_to_full_sk(req_to_sk(req));
+
+       if (req_inet->ipv6_opt && req_inet->ipv6_opt->hopopt)
+               old = req_inet->ipv6_opt->hopopt;
+       else
+               old = NULL;
+
+       new = calipso_opt_insert(old, doi_def, secattr);
+       if (IS_ERR(new))
+               return PTR_ERR(new);
+
+       txopts = ipv6_renew_options_kern(sk, req_inet->ipv6_opt, IPV6_HOPOPTS,
+                                        new, new ? ipv6_optlen(new) : 0);
+
+       kfree(new);
+
+       if (IS_ERR(txopts))
+               return PTR_ERR(txopts);
+
+       txopts = xchg(&req_inet->ipv6_opt, txopts);
+       if (txopts) {
+               atomic_sub(txopts->tot_len, &sk->sk_omem_alloc);
+               txopt_put(txopts);
+       }
+
+       return 0;
+}
+
+/**
+ * calipso_req_delattr - Delete the CALIPSO option from a request socket
+ * @reg: the request socket
+ *
+ * Description:
+ * Removes the CALIPSO option from a request socket, if present.
+ *
+ */
+static void calipso_req_delattr(struct request_sock *req)
+{
+       struct inet_request_sock *req_inet = inet_rsk(req);
+       struct ipv6_opt_hdr *new;
+       struct ipv6_txoptions *txopts;
+       struct sock *sk = sk_to_full_sk(req_to_sk(req));
+
+       if (!req_inet->ipv6_opt || !req_inet->ipv6_opt->hopopt)
+               return;
+
+       if (calipso_opt_del(req_inet->ipv6_opt->hopopt, &new))
+               return; /* Nothing to do */
+
+       txopts = ipv6_renew_options_kern(sk, req_inet->ipv6_opt, IPV6_HOPOPTS,
+                                        new, new ? ipv6_optlen(new) : 0);
+
+       if (!IS_ERR(txopts)) {
+               txopts = xchg(&req_inet->ipv6_opt, txopts);
+               if (txopts) {
+                       atomic_sub(txopts->tot_len, &sk->sk_omem_alloc);
+                       txopt_put(txopts);
+               }
+       }
+       kfree(new);
+}
+
+/* skbuff functions.
+ */
+
+/**
+ * calipso_skbuff_optptr - Find the CALIPSO option in the packet
+ * @skb: the packet
+ *
+ * Description:
+ * Parse the packet's IP header looking for a CALIPSO option.  Returns a pointer
+ * to the start of the CALIPSO option on success, NULL if one if not found.
+ *
+ */
+static unsigned char *calipso_skbuff_optptr(const struct sk_buff *skb)
+{
+       const struct ipv6hdr *ip6_hdr = ipv6_hdr(skb);
+       int offset;
+
+       if (ip6_hdr->nexthdr != NEXTHDR_HOP)
+               return NULL;
+
+       offset = ipv6_find_tlv(skb, sizeof(*ip6_hdr), IPV6_TLV_CALIPSO);
+       if (offset >= 0)
+               return (unsigned char *)ip6_hdr + offset;
+
+       return NULL;
+}
+
+/**
+ * calipso_skbuff_setattr - Set the CALIPSO option on a packet
+ * @skb: the packet
+ * @doi_def: the CALIPSO DOI to use
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Set the CALIPSO option on the given packet based on the security attributes.
+ * Returns a pointer to the IP header on success and NULL on failure.
+ *
+ */
+static int calipso_skbuff_setattr(struct sk_buff *skb,
+                                 const struct calipso_doi *doi_def,
+                                 const struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val;
+       struct ipv6hdr *ip6_hdr;
+       struct ipv6_opt_hdr *hop;
+       unsigned char buf[CALIPSO_MAX_BUFFER];
+       int len_delta, new_end, pad;
+       unsigned int start, end;
+
+       ip6_hdr = ipv6_hdr(skb);
+       if (ip6_hdr->nexthdr == NEXTHDR_HOP) {
+               hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
+               ret_val = calipso_opt_find(hop, &start, &end);
+               if (ret_val && ret_val != -ENOENT)
+                       return ret_val;
+       } else {
+               start = 0;
+               end = 0;
+       }
+
+       memset(buf, 0, sizeof(buf));
+       ret_val = calipso_genopt(buf, start & 3, sizeof(buf), doi_def, secattr);
+       if (ret_val < 0)
+               return ret_val;
+
+       new_end = start + ret_val;
+       /* At this point new_end aligns to 4n, so (new_end & 4) pads to 8n */
+       pad = ((new_end & 4) + (end & 7)) & 7;
+       len_delta = new_end - (int)end + pad;
+       ret_val = skb_cow(skb, skb_headroom(skb) + len_delta);
+       if (ret_val < 0)
+               return ret_val;
+
+       if (len_delta) {
+               if (len_delta > 0)
+                       skb_push(skb, len_delta);
+               else
+                       skb_pull(skb, -len_delta);
+               memmove((char *)ip6_hdr - len_delta, ip6_hdr,
+                       sizeof(*ip6_hdr) + start);
+               skb_reset_network_header(skb);
+               ip6_hdr = ipv6_hdr(skb);
+       }
+
+       hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
+       if (start == 0) {
+               struct ipv6_opt_hdr *new_hop = (struct ipv6_opt_hdr *)buf;
+
+               new_hop->nexthdr = ip6_hdr->nexthdr;
+               new_hop->hdrlen = len_delta / 8 - 1;
+               ip6_hdr->nexthdr = NEXTHDR_HOP;
+       } else {
+               hop->hdrlen += len_delta / 8;
+       }
+       memcpy((char *)hop + start, buf + (start & 3), new_end - start);
+       calipso_pad_write((unsigned char *)hop, new_end, pad);
+
+       return 0;
+}
+
+/**
+ * calipso_skbuff_delattr - Delete any CALIPSO options from a packet
+ * @skb: the packet
+ *
+ * Description:
+ * Removes any and all CALIPSO options from the given packet.  Returns zero on
+ * success, negative values on failure.
+ *
+ */
+static int calipso_skbuff_delattr(struct sk_buff *skb)
+{
+       int ret_val;
+       struct ipv6hdr *ip6_hdr;
+       struct ipv6_opt_hdr *old_hop;
+       u32 old_hop_len, start = 0, end = 0, delta, size, pad;
+
+       if (!calipso_skbuff_optptr(skb))
+               return 0;
+
+       /* since we are changing the packet we should make a copy */
+       ret_val = skb_cow(skb, skb_headroom(skb));
+       if (ret_val < 0)
+               return ret_val;
+
+       ip6_hdr = ipv6_hdr(skb);
+       old_hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
+       old_hop_len = ipv6_optlen(old_hop);
+
+       ret_val = calipso_opt_find(old_hop, &start, &end);
+       if (ret_val)
+               return ret_val;
+
+       if (start == sizeof(*old_hop) && end == old_hop_len) {
+               /* There's no other option in the header so we delete
+                * the whole thing. */
+               delta = old_hop_len;
+               size = sizeof(*ip6_hdr);
+               ip6_hdr->nexthdr = old_hop->nexthdr;
+       } else {
+               delta = (end - start) & ~7;
+               if (delta)
+                       old_hop->hdrlen -= delta / 8;
+               pad = (end - start) & 7;
+               size = sizeof(*ip6_hdr) + start + pad;
+               calipso_pad_write((unsigned char *)old_hop, start, pad);
+       }
+
+       if (delta) {
+               skb_pull(skb, delta);
+               memmove((char *)ip6_hdr + delta, ip6_hdr, size);
+               skb_reset_network_header(skb);
+       }
+
+       return 0;
+}
+
+static const struct netlbl_calipso_ops ops = {
+       .doi_add          = calipso_doi_add,
+       .doi_free         = calipso_doi_free,
+       .doi_remove       = calipso_doi_remove,
+       .doi_getdef       = calipso_doi_getdef,
+       .doi_putdef       = calipso_doi_putdef,
+       .doi_walk         = calipso_doi_walk,
+       .sock_getattr     = calipso_sock_getattr,
+       .sock_setattr     = calipso_sock_setattr,
+       .sock_delattr     = calipso_sock_delattr,
+       .req_setattr      = calipso_req_setattr,
+       .req_delattr      = calipso_req_delattr,
+       .opt_getattr      = calipso_opt_getattr,
+       .skbuff_optptr    = calipso_skbuff_optptr,
+       .skbuff_setattr   = calipso_skbuff_setattr,
+       .skbuff_delattr   = calipso_skbuff_delattr,
+       .cache_invalidate = calipso_cache_invalidate,
+       .cache_add        = calipso_cache_add
+};
+
+/**
+ * calipso_init - Initialize the CALIPSO module
+ *
+ * Description:
+ * Initialize the CALIPSO module and prepare it for use.  Returns zero on
+ * success and negative values on failure.
+ *
+ */
+int __init calipso_init(void)
+{
+       int ret_val;
+
+       ret_val = calipso_cache_init();
+       if (!ret_val)
+               netlbl_calipso_ops_register(&ops);
+       return ret_val;
+}
+
+void calipso_exit(void)
+{
+       netlbl_calipso_ops_register(NULL);
+       calipso_cache_invalidate();
+       kfree(calipso_cache);
+}
index 8de5dd7aaa05cf50aede2fb7255215df527349d7..139ceb68bd3705b5bc4cedb0c5a1e39fa59fc6b8 100644 (file)
@@ -43,6 +43,7 @@
 #include <net/ndisc.h>
 #include <net/ip6_route.h>
 #include <net/addrconf.h>
+#include <net/calipso.h>
 #if IS_ENABLED(CONFIG_IPV6_MIP6)
 #include <net/xfrm.h>
 #endif
@@ -603,6 +604,28 @@ drop:
        return false;
 }
 
+/* CALIPSO RFC 5570 */
+
+static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
+{
+       const unsigned char *nh = skb_network_header(skb);
+
+       if (nh[optoff + 1] < 8)
+               goto drop;
+
+       if (nh[optoff + 6] * 4 + 8 > nh[optoff + 1])
+               goto drop;
+
+       if (!calipso_validate(skb, nh + optoff))
+               goto drop;
+
+       return true;
+
+drop:
+       kfree_skb(skb);
+       return false;
+}
+
 static const struct tlvtype_proc tlvprochopopt_lst[] = {
        {
                .type   = IPV6_TLV_ROUTERALERT,
@@ -612,6 +635,10 @@ static const struct tlvtype_proc tlvprochopopt_lst[] = {
                .type   = IPV6_TLV_JUMBO,
                .func   = ipv6_hop_jumbo,
        },
+       {
+               .type   = IPV6_TLV_CALIPSO,
+               .func   = ipv6_hop_calipso,
+       },
        { -1, }
 };
 
@@ -758,6 +785,27 @@ static int ipv6_renew_option(void *ohdr,
        return 0;
 }
 
+/**
+ * ipv6_renew_options - replace a specific ext hdr with a new one.
+ *
+ * @sk: sock from which to allocate memory
+ * @opt: original options
+ * @newtype: option type to replace in @opt
+ * @newopt: new option of type @newtype to replace (user-mem)
+ * @newoptlen: length of @newopt
+ *
+ * Returns a new set of options which is a copy of @opt with the
+ * option type @newtype replaced with @newopt.
+ *
+ * @opt may be NULL, in which case a new set of options is returned
+ * containing just @newopt.
+ *
+ * @newopt may be NULL, in which case the specified option type is
+ * not copied into the new set of options.
+ *
+ * The new set of options is allocated from the socket option memory
+ * buffer of @sk.
+ */
 struct ipv6_txoptions *
 ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
                   int newtype,
@@ -830,6 +878,34 @@ out:
        return ERR_PTR(err);
 }
 
+/**
+ * ipv6_renew_options_kern - replace a specific ext hdr with a new one.
+ *
+ * @sk: sock from which to allocate memory
+ * @opt: original options
+ * @newtype: option type to replace in @opt
+ * @newopt: new option of type @newtype to replace (kernel-mem)
+ * @newoptlen: length of @newopt
+ *
+ * See ipv6_renew_options().  The difference is that @newopt is
+ * kernel memory, rather than user memory.
+ */
+struct ipv6_txoptions *
+ipv6_renew_options_kern(struct sock *sk, struct ipv6_txoptions *opt,
+                       int newtype, struct ipv6_opt_hdr *newopt,
+                       int newoptlen)
+{
+       struct ipv6_txoptions *ret_val;
+       const mm_segment_t old_fs = get_fs();
+
+       set_fs(KERNEL_DS);
+       ret_val = ipv6_renew_options(sk, opt, newtype,
+                                    (struct ipv6_opt_hdr __user *)newopt,
+                                    newoptlen);
+       set_fs(old_fs);
+       return ret_val;
+}
+
 struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
                                          struct ipv6_txoptions *opt)
 {
index 9508a20fbf61432f561202edbe40b59e63c3489e..305e2ed730bf42438190ad2fd7ae9ac9b4eed98f 100644 (file)
@@ -112,7 +112,7 @@ int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
 }
 EXPORT_SYMBOL(ipv6_skip_exthdr);
 
-int ipv6_find_tlv(struct sk_buff *skb, int offset, int type)
+int ipv6_find_tlv(const struct sk_buff *skb, int offset, int type)
 {
        const unsigned char *nh = skb_network_header(skb);
        int packet_len = skb_tail_pointer(skb) - skb_network_header(skb);
index a9895e15ee9c270f13842121b7dc66ed97a71135..5330262ab673c022fbf700d22782a74ccd1494fe 100644 (file)
@@ -98,7 +98,6 @@ int ip6_ra_control(struct sock *sk, int sel)
        return 0;
 }
 
-static
 struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
                                           struct ipv6_txoptions *opt)
 {
index 45243bbe52536d523a7aff0b636a72b8f2e3daf5..69c50e737c5462384a4c6a82b500c96621b53392 100644 (file)
@@ -15,6 +15,9 @@
 #include <net/ipv6.h>
 #include <net/addrconf.h>
 #include <net/inet_frag.h>
+#ifdef CONFIG_NETLABEL
+#include <net/calipso.h>
+#endif
 
 static int one = 1;
 static int auto_flowlabels_min;
@@ -106,6 +109,22 @@ static struct ctl_table ipv6_rotable[] = {
                .proc_handler   = proc_dointvec_minmax,
                .extra1         = &one
        },
+#ifdef CONFIG_NETLABEL
+       {
+               .procname       = "calipso_cache_enable",
+               .data           = &calipso_cache_enabled,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "calipso_cache_bucket_size",
+               .data           = &calipso_cache_bucketsize,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif /* CONFIG_NETLABEL */
        { }
 };
 
index 37cf91323319332d6256615d367bbede62066ae9..33df8b8575cceb6f4bf50d9a50ab5a6afa79cd86 100644 (file)
@@ -443,6 +443,7 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst,
 {
        struct inet_request_sock *ireq = inet_rsk(req);
        struct ipv6_pinfo *np = inet6_sk(sk);
+       struct ipv6_txoptions *opt;
        struct flowi6 *fl6 = &fl->u.ip6;
        struct sk_buff *skb;
        int err = -ENOMEM;
@@ -463,8 +464,10 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst,
                        fl6->flowlabel = ip6_flowlabel(ipv6_hdr(ireq->pktopts));
 
                rcu_read_lock();
-               err = ip6_xmit(sk, skb, fl6, rcu_dereference(np->opt),
-                              np->tclass);
+               opt = ireq->ipv6_opt;
+               if (!opt)
+                       opt = rcu_dereference(np->opt);
+               err = ip6_xmit(sk, skb, fl6, opt, np->tclass);
                rcu_read_unlock();
                err = net_xmit_eval(err);
        }
@@ -476,6 +479,7 @@ done:
 
 static void tcp_v6_reqsk_destructor(struct request_sock *req)
 {
+       kfree(inet_rsk(req)->ipv6_opt);
        kfree_skb(inet_rsk(req)->pktopts);
 }
 
@@ -1112,7 +1116,9 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
           but we make one more one thing there: reattach optmem
           to newsk.
         */
-       opt = rcu_dereference(np->opt);
+       opt = ireq->ipv6_opt;
+       if (!opt)
+               opt = rcu_dereference(np->opt);
        if (opt) {
                opt = ipv6_dup_options(newsk, opt);
                RCU_INIT_POINTER(newnp->opt, opt);
index 37d674e6f8a9f4f1e8bc31ea49ed8a4f1333241f..02b45a8e8b35729a65c6d796136a846aa2df648b 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/skbuff.h>
 #include <linux/init.h>
 #include <linux/poll.h>
+#include <linux/security.h>
 #include <net/sock.h>
 #include <asm/ebcdic.h>
 #include <asm/cpcmd.h>
@@ -530,8 +531,10 @@ static void iucv_sock_close(struct sock *sk)
 
 static void iucv_sock_init(struct sock *sk, struct sock *parent)
 {
-       if (parent)
+       if (parent) {
                sk->sk_type = parent->sk_type;
+               security_sk_clone(parent, sk);
+       }
 }
 
 static struct sock *iucv_sock_alloc(struct socket *sock, int proto, gfp_t prio, int kern)
index 56958c85f2b448576e2aa3daa5e2d6b80569fe8a..d9eaa30ffe3fc89a5d8a3cb9651d02121cc5bf34 100644 (file)
@@ -5,6 +5,7 @@
 config NETLABEL
        bool "NetLabel subsystem support"
        depends on SECURITY
+       select CRC_CCITT if IPV6
        default n
        ---help---
          NetLabel provides support for explicit network packet labeling
index d2732fc952e2adbe224c46cb827fdcd4b38ffbaf..d341ede0dca5f6099c0a1c421c2e3ae9ce112fca 100644 (file)
@@ -12,4 +12,4 @@ obj-y += netlabel_mgmt.o
 # protocol modules
 obj-y  += netlabel_unlabeled.o
 obj-y  += netlabel_cipso_v4.o
-
+obj-$(subst m,y,$(CONFIG_IPV6)) += netlabel_calipso.o
diff --git a/net/netlabel/netlabel_calipso.c b/net/netlabel/netlabel_calipso.c
new file mode 100644 (file)
index 0000000..2ec93c5
--- /dev/null
@@ -0,0 +1,740 @@
+/*
+ * NetLabel CALIPSO/IPv6 Support
+ *
+ * This file defines the CALIPSO/IPv6 functions for the NetLabel system.  The
+ * NetLabel system manages static and dynamic label mappings for network
+ * protocols such as CIPSO and CALIPSO.
+ *
+ * Authors: Paul Moore <paul@paul-moore.com>
+ *          Huw Davies <huw@codeweavers.com>
+ *
+ */
+
+/* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * (c) Copyright Huw Davies <huw@codeweavers.com>, 2015
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY;  without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program;  if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/audit.h>
+#include <linux/slab.h>
+#include <net/sock.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include <net/netlabel.h>
+#include <net/calipso.h>
+#include <linux/atomic.h>
+
+#include "netlabel_user.h"
+#include "netlabel_calipso.h"
+#include "netlabel_mgmt.h"
+#include "netlabel_domainhash.h"
+
+/* Argument struct for calipso_doi_walk() */
+struct netlbl_calipso_doiwalk_arg {
+       struct netlink_callback *nl_cb;
+       struct sk_buff *skb;
+       u32 seq;
+};
+
+/* Argument struct for netlbl_domhsh_walk() */
+struct netlbl_domhsh_walk_arg {
+       struct netlbl_audit *audit_info;
+       u32 doi;
+};
+
+/* NetLabel Generic NETLINK CALIPSO family */
+static struct genl_family netlbl_calipso_gnl_family = {
+       .id = GENL_ID_GENERATE,
+       .hdrsize = 0,
+       .name = NETLBL_NLTYPE_CALIPSO_NAME,
+       .version = NETLBL_PROTO_VERSION,
+       .maxattr = NLBL_CALIPSO_A_MAX,
+};
+
+/* NetLabel Netlink attribute policy */
+static const struct nla_policy calipso_genl_policy[NLBL_CALIPSO_A_MAX + 1] = {
+       [NLBL_CALIPSO_A_DOI] = { .type = NLA_U32 },
+       [NLBL_CALIPSO_A_MTYPE] = { .type = NLA_U32 },
+};
+
+/* NetLabel Command Handlers
+ */
+/**
+ * netlbl_calipso_add_pass - Adds a CALIPSO pass DOI definition
+ * @info: the Generic NETLINK info block
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Create a new CALIPSO_MAP_PASS DOI definition based on the given ADD message
+ * and add it to the CALIPSO engine.  Return zero on success and non-zero on
+ * error.
+ *
+ */
+static int netlbl_calipso_add_pass(struct genl_info *info,
+                                  struct netlbl_audit *audit_info)
+{
+       int ret_val;
+       struct calipso_doi *doi_def = NULL;
+
+       doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
+       if (!doi_def)
+               return -ENOMEM;
+       doi_def->type = CALIPSO_MAP_PASS;
+       doi_def->doi = nla_get_u32(info->attrs[NLBL_CALIPSO_A_DOI]);
+       ret_val = calipso_doi_add(doi_def, audit_info);
+       if (ret_val != 0)
+               calipso_doi_free(doi_def);
+
+       return ret_val;
+}
+
+/**
+ * netlbl_calipso_add - Handle an ADD message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Create a new DOI definition based on the given ADD message and add it to the
+ * CALIPSO engine.  Returns zero on success, negative values on failure.
+ *
+ */
+static int netlbl_calipso_add(struct sk_buff *skb, struct genl_info *info)
+
+{
+       int ret_val = -EINVAL;
+       struct netlbl_audit audit_info;
+
+       if (!info->attrs[NLBL_CALIPSO_A_DOI] ||
+           !info->attrs[NLBL_CALIPSO_A_MTYPE])
+               return -EINVAL;
+
+       netlbl_netlink_auditinfo(skb, &audit_info);
+       switch (nla_get_u32(info->attrs[NLBL_CALIPSO_A_MTYPE])) {
+       case CALIPSO_MAP_PASS:
+               ret_val = netlbl_calipso_add_pass(info, &audit_info);
+               break;
+       }
+       if (ret_val == 0)
+               atomic_inc(&netlabel_mgmt_protocount);
+
+       return ret_val;
+}
+
+/**
+ * netlbl_calipso_list - Handle a LIST message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Process a user generated LIST message and respond accordingly.
+ * Returns zero on success and negative values on error.
+ *
+ */
+static int netlbl_calipso_list(struct sk_buff *skb, struct genl_info *info)
+{
+       int ret_val;
+       struct sk_buff *ans_skb = NULL;
+       void *data;
+       u32 doi;
+       struct calipso_doi *doi_def;
+
+       if (!info->attrs[NLBL_CALIPSO_A_DOI]) {
+               ret_val = -EINVAL;
+               goto list_failure;
+       }
+
+       doi = nla_get_u32(info->attrs[NLBL_CALIPSO_A_DOI]);
+
+       doi_def = calipso_doi_getdef(doi);
+       if (!doi_def) {
+               ret_val = -EINVAL;
+               goto list_failure;
+       }
+
+       ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!ans_skb) {
+               ret_val = -ENOMEM;
+               goto list_failure_put;
+       }
+       data = genlmsg_put_reply(ans_skb, info, &netlbl_calipso_gnl_family,
+                                0, NLBL_CALIPSO_C_LIST);
+       if (!data) {
+               ret_val = -ENOMEM;
+               goto list_failure_put;
+       }
+
+       ret_val = nla_put_u32(ans_skb, NLBL_CALIPSO_A_MTYPE, doi_def->type);
+       if (ret_val != 0)
+               goto list_failure_put;
+
+       calipso_doi_putdef(doi_def);
+
+       genlmsg_end(ans_skb, data);
+       return genlmsg_reply(ans_skb, info);
+
+list_failure_put:
+       calipso_doi_putdef(doi_def);
+list_failure:
+       kfree_skb(ans_skb);
+       return ret_val;
+}
+
+/**
+ * netlbl_calipso_listall_cb - calipso_doi_walk() callback for LISTALL
+ * @doi_def: the CALIPSO DOI definition
+ * @arg: the netlbl_calipso_doiwalk_arg structure
+ *
+ * Description:
+ * This function is designed to be used as a callback to the
+ * calipso_doi_walk() function for use in generating a response for a LISTALL
+ * message.  Returns the size of the message on success, negative values on
+ * failure.
+ *
+ */
+static int netlbl_calipso_listall_cb(struct calipso_doi *doi_def, void *arg)
+{
+       int ret_val = -ENOMEM;
+       struct netlbl_calipso_doiwalk_arg *cb_arg = arg;
+       void *data;
+
+       data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid,
+                          cb_arg->seq, &netlbl_calipso_gnl_family,
+                          NLM_F_MULTI, NLBL_CALIPSO_C_LISTALL);
+       if (!data)
+               goto listall_cb_failure;
+
+       ret_val = nla_put_u32(cb_arg->skb, NLBL_CALIPSO_A_DOI, doi_def->doi);
+       if (ret_val != 0)
+               goto listall_cb_failure;
+       ret_val = nla_put_u32(cb_arg->skb,
+                             NLBL_CALIPSO_A_MTYPE,
+                             doi_def->type);
+       if (ret_val != 0)
+               goto listall_cb_failure;
+
+       genlmsg_end(cb_arg->skb, data);
+       return 0;
+
+listall_cb_failure:
+       genlmsg_cancel(cb_arg->skb, data);
+       return ret_val;
+}
+
+/**
+ * netlbl_calipso_listall - Handle a LISTALL message
+ * @skb: the NETLINK buffer
+ * @cb: the NETLINK callback
+ *
+ * Description:
+ * Process a user generated LISTALL message and respond accordingly.  Returns
+ * zero on success and negative values on error.
+ *
+ */
+static int netlbl_calipso_listall(struct sk_buff *skb,
+                                 struct netlink_callback *cb)
+{
+       struct netlbl_calipso_doiwalk_arg cb_arg;
+       u32 doi_skip = cb->args[0];
+
+       cb_arg.nl_cb = cb;
+       cb_arg.skb = skb;
+       cb_arg.seq = cb->nlh->nlmsg_seq;
+
+       calipso_doi_walk(&doi_skip, netlbl_calipso_listall_cb, &cb_arg);
+
+       cb->args[0] = doi_skip;
+       return skb->len;
+}
+
+/**
+ * netlbl_calipso_remove_cb - netlbl_calipso_remove() callback for REMOVE
+ * @entry: LSM domain mapping entry
+ * @arg: the netlbl_domhsh_walk_arg structure
+ *
+ * Description:
+ * This function is intended for use by netlbl_calipso_remove() as the callback
+ * for the netlbl_domhsh_walk() function; it removes LSM domain map entries
+ * which are associated with the CALIPSO DOI specified in @arg.  Returns zero on
+ * success, negative values on failure.
+ *
+ */
+static int netlbl_calipso_remove_cb(struct netlbl_dom_map *entry, void *arg)
+{
+       struct netlbl_domhsh_walk_arg *cb_arg = arg;
+
+       if (entry->def.type == NETLBL_NLTYPE_CALIPSO &&
+           entry->def.calipso->doi == cb_arg->doi)
+               return netlbl_domhsh_remove_entry(entry, cb_arg->audit_info);
+
+       return 0;
+}
+
+/**
+ * netlbl_calipso_remove - Handle a REMOVE message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Process a user generated REMOVE message and respond accordingly.  Returns
+ * zero on success, negative values on failure.
+ *
+ */
+static int netlbl_calipso_remove(struct sk_buff *skb, struct genl_info *info)
+{
+       int ret_val = -EINVAL;
+       struct netlbl_domhsh_walk_arg cb_arg;
+       struct netlbl_audit audit_info;
+       u32 skip_bkt = 0;
+       u32 skip_chain = 0;
+
+       if (!info->attrs[NLBL_CALIPSO_A_DOI])
+               return -EINVAL;
+
+       netlbl_netlink_auditinfo(skb, &audit_info);
+       cb_arg.doi = nla_get_u32(info->attrs[NLBL_CALIPSO_A_DOI]);
+       cb_arg.audit_info = &audit_info;
+       ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain,
+                                    netlbl_calipso_remove_cb, &cb_arg);
+       if (ret_val == 0 || ret_val == -ENOENT) {
+               ret_val = calipso_doi_remove(cb_arg.doi, &audit_info);
+               if (ret_val == 0)
+                       atomic_dec(&netlabel_mgmt_protocount);
+       }
+
+       return ret_val;
+}
+
+/* NetLabel Generic NETLINK Command Definitions
+ */
+
+static const struct genl_ops netlbl_calipso_ops[] = {
+       {
+       .cmd = NLBL_CALIPSO_C_ADD,
+       .flags = GENL_ADMIN_PERM,
+       .policy = calipso_genl_policy,
+       .doit = netlbl_calipso_add,
+       .dumpit = NULL,
+       },
+       {
+       .cmd = NLBL_CALIPSO_C_REMOVE,
+       .flags = GENL_ADMIN_PERM,
+       .policy = calipso_genl_policy,
+       .doit = netlbl_calipso_remove,
+       .dumpit = NULL,
+       },
+       {
+       .cmd = NLBL_CALIPSO_C_LIST,
+       .flags = 0,
+       .policy = calipso_genl_policy,
+       .doit = netlbl_calipso_list,
+       .dumpit = NULL,
+       },
+       {
+       .cmd = NLBL_CALIPSO_C_LISTALL,
+       .flags = 0,
+       .policy = calipso_genl_policy,
+       .doit = NULL,
+       .dumpit = netlbl_calipso_listall,
+       },
+};
+
+/* NetLabel Generic NETLINK Protocol Functions
+ */
+
+/**
+ * netlbl_calipso_genl_init - Register the CALIPSO NetLabel component
+ *
+ * Description:
+ * Register the CALIPSO packet NetLabel component with the Generic NETLINK
+ * mechanism.  Returns zero on success, negative values on failure.
+ *
+ */
+int __init netlbl_calipso_genl_init(void)
+{
+       return genl_register_family_with_ops(&netlbl_calipso_gnl_family,
+                                            netlbl_calipso_ops);
+}
+
+static const struct netlbl_calipso_ops *calipso_ops;
+
+/**
+ * netlbl_calipso_ops_register - Register the CALIPSO operations
+ *
+ * Description:
+ * Register the CALIPSO packet engine operations.
+ *
+ */
+const struct netlbl_calipso_ops *
+netlbl_calipso_ops_register(const struct netlbl_calipso_ops *ops)
+{
+       return xchg(&calipso_ops, ops);
+}
+EXPORT_SYMBOL(netlbl_calipso_ops_register);
+
+static const struct netlbl_calipso_ops *netlbl_calipso_ops_get(void)
+{
+       return ACCESS_ONCE(calipso_ops);
+}
+
+/**
+ * calipso_doi_add - Add a new DOI to the CALIPSO protocol engine
+ * @doi_def: the DOI structure
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * The caller defines a new DOI for use by the CALIPSO engine and calls this
+ * function to add it to the list of acceptable domains.  The caller must
+ * ensure that the mapping table specified in @doi_def->map meets all of the
+ * requirements of the mapping type (see calipso.h for details).  Returns
+ * zero on success and non-zero on failure.
+ *
+ */
+int calipso_doi_add(struct calipso_doi *doi_def,
+                   struct netlbl_audit *audit_info)
+{
+       int ret_val = -ENOMSG;
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ret_val = ops->doi_add(doi_def, audit_info);
+       return ret_val;
+}
+
+/**
+ * calipso_doi_free - Frees a DOI definition
+ * @doi_def: the DOI definition
+ *
+ * Description:
+ * This function frees all of the memory associated with a DOI definition.
+ *
+ */
+void calipso_doi_free(struct calipso_doi *doi_def)
+{
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ops->doi_free(doi_def);
+}
+
+/**
+ * calipso_doi_remove - Remove an existing DOI from the CALIPSO protocol engine
+ * @doi: the DOI value
+ * @audit_secid: the LSM secid to use in the audit message
+ *
+ * Description:
+ * Removes a DOI definition from the CALIPSO engine.  The NetLabel routines will
+ * be called to release their own LSM domain mappings as well as our own
+ * domain list.  Returns zero on success and negative values on failure.
+ *
+ */
+int calipso_doi_remove(u32 doi, struct netlbl_audit *audit_info)
+{
+       int ret_val = -ENOMSG;
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ret_val = ops->doi_remove(doi, audit_info);
+       return ret_val;
+}
+
+/**
+ * calipso_doi_getdef - Returns a reference to a valid DOI definition
+ * @doi: the DOI value
+ *
+ * Description:
+ * Searches for a valid DOI definition and if one is found it is returned to
+ * the caller.  Otherwise NULL is returned.  The caller must ensure that
+ * calipso_doi_putdef() is called when the caller is done.
+ *
+ */
+struct calipso_doi *calipso_doi_getdef(u32 doi)
+{
+       struct calipso_doi *ret_val = NULL;
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ret_val = ops->doi_getdef(doi);
+       return ret_val;
+}
+
+/**
+ * calipso_doi_putdef - Releases a reference for the given DOI definition
+ * @doi_def: the DOI definition
+ *
+ * Description:
+ * Releases a DOI definition reference obtained from calipso_doi_getdef().
+ *
+ */
+void calipso_doi_putdef(struct calipso_doi *doi_def)
+{
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ops->doi_putdef(doi_def);
+}
+
+/**
+ * calipso_doi_walk - Iterate through the DOI definitions
+ * @skip_cnt: skip past this number of DOI definitions, updated
+ * @callback: callback for each DOI definition
+ * @cb_arg: argument for the callback function
+ *
+ * Description:
+ * Iterate over the DOI definition list, skipping the first @skip_cnt entries.
+ * For each entry call @callback, if @callback returns a negative value stop
+ * 'walking' through the list and return.  Updates the value in @skip_cnt upon
+ * return.  Returns zero on success, negative values on failure.
+ *
+ */
+int calipso_doi_walk(u32 *skip_cnt,
+                    int (*callback)(struct calipso_doi *doi_def, void *arg),
+                    void *cb_arg)
+{
+       int ret_val = -ENOMSG;
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ret_val = ops->doi_walk(skip_cnt, callback, cb_arg);
+       return ret_val;
+}
+
+/**
+ * calipso_sock_getattr - Get the security attributes from a sock
+ * @sk: the sock
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Query @sk to see if there is a CALIPSO option attached to the sock and if
+ * there is return the CALIPSO security attributes in @secattr.  This function
+ * requires that @sk be locked, or privately held, but it does not do any
+ * locking itself.  Returns zero on success and negative values on failure.
+ *
+ */
+int calipso_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val = -ENOMSG;
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ret_val = ops->sock_getattr(sk, secattr);
+       return ret_val;
+}
+
+/**
+ * calipso_sock_setattr - Add a CALIPSO option to a socket
+ * @sk: the socket
+ * @doi_def: the CALIPSO DOI to use
+ * @secattr: the specific security attributes of the socket
+ *
+ * Description:
+ * Set the CALIPSO option on the given socket using the DOI definition and
+ * security attributes passed to the function.  This function requires
+ * exclusive access to @sk, which means it either needs to be in the
+ * process of being created or locked.  Returns zero on success and negative
+ * values on failure.
+ *
+ */
+int calipso_sock_setattr(struct sock *sk,
+                        const struct calipso_doi *doi_def,
+                        const struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val = -ENOMSG;
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ret_val = ops->sock_setattr(sk, doi_def, secattr);
+       return ret_val;
+}
+
+/**
+ * calipso_sock_delattr - Delete the CALIPSO option from a socket
+ * @sk: the socket
+ *
+ * Description:
+ * Removes the CALIPSO option from a socket, if present.
+ *
+ */
+void calipso_sock_delattr(struct sock *sk)
+{
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ops->sock_delattr(sk);
+}
+
+/**
+ * calipso_req_setattr - Add a CALIPSO option to a connection request socket
+ * @req: the connection request socket
+ * @doi_def: the CALIPSO DOI to use
+ * @secattr: the specific security attributes of the socket
+ *
+ * Description:
+ * Set the CALIPSO option on the given socket using the DOI definition and
+ * security attributes passed to the function.  Returns zero on success and
+ * negative values on failure.
+ *
+ */
+int calipso_req_setattr(struct request_sock *req,
+                       const struct calipso_doi *doi_def,
+                       const struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val = -ENOMSG;
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ret_val = ops->req_setattr(req, doi_def, secattr);
+       return ret_val;
+}
+
+/**
+ * calipso_req_delattr - Delete the CALIPSO option from a request socket
+ * @reg: the request socket
+ *
+ * Description:
+ * Removes the CALIPSO option from a request socket, if present.
+ *
+ */
+void calipso_req_delattr(struct request_sock *req)
+{
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ops->req_delattr(req);
+}
+
+/**
+ * calipso_optptr - Find the CALIPSO option in the packet
+ * @skb: the packet
+ *
+ * Description:
+ * Parse the packet's IP header looking for a CALIPSO option.  Returns a pointer
+ * to the start of the CALIPSO option on success, NULL if one if not found.
+ *
+ */
+unsigned char *calipso_optptr(const struct sk_buff *skb)
+{
+       unsigned char *ret_val = NULL;
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ret_val = ops->skbuff_optptr(skb);
+       return ret_val;
+}
+
+/**
+ * calipso_getattr - Get the security attributes from a memory block.
+ * @calipso: the CALIPSO option
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Inspect @calipso and return the security attributes in @secattr.
+ * Returns zero on success and negative values on failure.
+ *
+ */
+int calipso_getattr(const unsigned char *calipso,
+                   struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val = -ENOMSG;
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ret_val = ops->opt_getattr(calipso, secattr);
+       return ret_val;
+}
+
+/**
+ * calipso_skbuff_setattr - Set the CALIPSO option on a packet
+ * @skb: the packet
+ * @doi_def: the CALIPSO DOI to use
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Set the CALIPSO option on the given packet based on the security attributes.
+ * Returns a pointer to the IP header on success and NULL on failure.
+ *
+ */
+int calipso_skbuff_setattr(struct sk_buff *skb,
+                          const struct calipso_doi *doi_def,
+                          const struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val = -ENOMSG;
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ret_val = ops->skbuff_setattr(skb, doi_def, secattr);
+       return ret_val;
+}
+
+/**
+ * calipso_skbuff_delattr - Delete any CALIPSO options from a packet
+ * @skb: the packet
+ *
+ * Description:
+ * Removes any and all CALIPSO options from the given packet.  Returns zero on
+ * success, negative values on failure.
+ *
+ */
+int calipso_skbuff_delattr(struct sk_buff *skb)
+{
+       int ret_val = -ENOMSG;
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ret_val = ops->skbuff_delattr(skb);
+       return ret_val;
+}
+
+/**
+ * calipso_cache_invalidate - Invalidates the current CALIPSO cache
+ *
+ * Description:
+ * Invalidates and frees any entries in the CALIPSO cache.  Returns zero on
+ * success and negative values on failure.
+ *
+ */
+void calipso_cache_invalidate(void)
+{
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ops->cache_invalidate();
+}
+
+/**
+ * calipso_cache_add - Add an entry to the CALIPSO cache
+ * @calipso_ptr: the CALIPSO option
+ * @secattr: the packet's security attributes
+ *
+ * Description:
+ * Add a new entry into the CALIPSO label mapping cache.
+ * Returns zero on success, negative values on failure.
+ *
+ */
+int calipso_cache_add(const unsigned char *calipso_ptr,
+                     const struct netlbl_lsm_secattr *secattr)
+
+{
+       int ret_val = -ENOMSG;
+       const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
+
+       if (ops)
+               ret_val = ops->cache_add(calipso_ptr, secattr);
+       return ret_val;
+}
diff --git a/net/netlabel/netlabel_calipso.h b/net/netlabel/netlabel_calipso.h
new file mode 100644 (file)
index 0000000..9fd291c
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * NetLabel CALIPSO Support
+ *
+ * This file defines the CALIPSO functions for the NetLabel system.  The
+ * NetLabel system manages static and dynamic label mappings for network
+ * protocols such as CIPSO and RIPSO.
+ *
+ * Authors: Paul Moore <paul@paul-moore.com>
+ *          Huw Davies <huw@codeweavers.com>
+ *
+ */
+
+/* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * (c) Copyright Huw Davies <huw@codeweavers.com>, 2015
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY;  without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program;  if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _NETLABEL_CALIPSO
+#define _NETLABEL_CALIPSO
+
+#include <net/netlabel.h>
+#include <net/calipso.h>
+
+/* The following NetLabel payloads are supported by the CALIPSO subsystem.
+ *
+ * o ADD:
+ *   Sent by an application to add a new DOI mapping table.
+ *
+ *   Required attributes:
+ *
+ *     NLBL_CALIPSO_A_DOI
+ *     NLBL_CALIPSO_A_MTYPE
+ *
+ *   If using CALIPSO_MAP_PASS no additional attributes are required.
+ *
+ * o REMOVE:
+ *   Sent by an application to remove a specific DOI mapping table from the
+ *   CALIPSO system.
+ *
+ *   Required attributes:
+ *
+ *     NLBL_CALIPSO_A_DOI
+ *
+ * o LIST:
+ *   Sent by an application to list the details of a DOI definition.  On
+ *   success the kernel should send a response using the following format.
+ *
+ *   Required attributes:
+ *
+ *     NLBL_CALIPSO_A_DOI
+ *
+ *   The valid response message format depends on the type of the DOI mapping,
+ *   the defined formats are shown below.
+ *
+ *   Required attributes:
+ *
+ *     NLBL_CALIPSO_A_MTYPE
+ *
+ *   If using CALIPSO_MAP_PASS no additional attributes are required.
+ *
+ * o LISTALL:
+ *   This message is sent by an application to list the valid DOIs on the
+ *   system.  When sent by an application there is no payload and the
+ *   NLM_F_DUMP flag should be set.  The kernel should respond with a series of
+ *   the following messages.
+ *
+ *   Required attributes:
+ *
+ *    NLBL_CALIPSO_A_DOI
+ *    NLBL_CALIPSO_A_MTYPE
+ *
+ */
+
+/* NetLabel CALIPSO commands */
+enum {
+       NLBL_CALIPSO_C_UNSPEC,
+       NLBL_CALIPSO_C_ADD,
+       NLBL_CALIPSO_C_REMOVE,
+       NLBL_CALIPSO_C_LIST,
+       NLBL_CALIPSO_C_LISTALL,
+       __NLBL_CALIPSO_C_MAX,
+};
+
+/* NetLabel CALIPSO attributes */
+enum {
+       NLBL_CALIPSO_A_UNSPEC,
+       NLBL_CALIPSO_A_DOI,
+       /* (NLA_U32)
+        * the DOI value */
+       NLBL_CALIPSO_A_MTYPE,
+       /* (NLA_U32)
+        * the mapping table type (defined in the calipso.h header as
+        * CALIPSO_MAP_*) */
+       __NLBL_CALIPSO_A_MAX,
+};
+
+#define NLBL_CALIPSO_A_MAX (__NLBL_CALIPSO_A_MAX - 1)
+
+/* NetLabel protocol functions */
+#if IS_ENABLED(CONFIG_IPV6)
+int netlbl_calipso_genl_init(void);
+#else
+static inline int netlbl_calipso_genl_init(void)
+{
+       return 0;
+}
+#endif
+
+int calipso_doi_add(struct calipso_doi *doi_def,
+                   struct netlbl_audit *audit_info);
+void calipso_doi_free(struct calipso_doi *doi_def);
+int calipso_doi_remove(u32 doi, struct netlbl_audit *audit_info);
+struct calipso_doi *calipso_doi_getdef(u32 doi);
+void calipso_doi_putdef(struct calipso_doi *doi_def);
+int calipso_doi_walk(u32 *skip_cnt,
+                    int (*callback)(struct calipso_doi *doi_def, void *arg),
+                    void *cb_arg);
+int calipso_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr);
+int calipso_sock_setattr(struct sock *sk,
+                        const struct calipso_doi *doi_def,
+                        const struct netlbl_lsm_secattr *secattr);
+void calipso_sock_delattr(struct sock *sk);
+int calipso_req_setattr(struct request_sock *req,
+                       const struct calipso_doi *doi_def,
+                       const struct netlbl_lsm_secattr *secattr);
+void calipso_req_delattr(struct request_sock *req);
+unsigned char *calipso_optptr(const struct sk_buff *skb);
+int calipso_getattr(const unsigned char *calipso,
+                   struct netlbl_lsm_secattr *secattr);
+int calipso_skbuff_setattr(struct sk_buff *skb,
+                          const struct calipso_doi *doi_def,
+                          const struct netlbl_lsm_secattr *secattr);
+int calipso_skbuff_delattr(struct sk_buff *skb);
+void calipso_cache_invalidate(void);
+int calipso_cache_add(const unsigned char *calipso_ptr,
+                     const struct netlbl_lsm_secattr *secattr);
+
+#endif
index ada67422234bc41ba73f23696607903d800d87e3..41d0e95d171e185828054733f8a26d099da65cff 100644 (file)
 #include <linux/slab.h>
 #include <net/netlabel.h>
 #include <net/cipso_ipv4.h>
+#include <net/calipso.h>
 #include <asm/bug.h>
 
 #include "netlabel_mgmt.h"
 #include "netlabel_addrlist.h"
+#include "netlabel_calipso.h"
 #include "netlabel_domainhash.h"
 #include "netlabel_user.h"
 
@@ -55,8 +57,9 @@ struct netlbl_domhsh_tbl {
 static DEFINE_SPINLOCK(netlbl_domhsh_lock);
 #define netlbl_domhsh_rcu_deref(p) \
        rcu_dereference_check(p, lockdep_is_held(&netlbl_domhsh_lock))
-static struct netlbl_domhsh_tbl *netlbl_domhsh;
-static struct netlbl_dom_map *netlbl_domhsh_def;
+static struct netlbl_domhsh_tbl __rcu *netlbl_domhsh;
+static struct netlbl_dom_map __rcu *netlbl_domhsh_def_ipv4;
+static struct netlbl_dom_map __rcu *netlbl_domhsh_def_ipv6;
 
 /*
  * Domain Hash Table Helper Functions
@@ -126,18 +129,26 @@ static u32 netlbl_domhsh_hash(const char *key)
        return val & (netlbl_domhsh_rcu_deref(netlbl_domhsh)->size - 1);
 }
 
+static bool netlbl_family_match(u16 f1, u16 f2)
+{
+       return (f1 == f2) || (f1 == AF_UNSPEC) || (f2 == AF_UNSPEC);
+}
+
 /**
  * netlbl_domhsh_search - Search for a domain entry
  * @domain: the domain
+ * @family: the address family
  *
  * Description:
  * Searches the domain hash table and returns a pointer to the hash table
- * entry if found, otherwise NULL is returned.  The caller is responsible for
+ * entry if found, otherwise NULL is returned.  @family may be %AF_UNSPEC
+ * which matches any address family entries.  The caller is responsible for
  * ensuring that the hash table is protected with either a RCU read lock or the
  * hash table lock.
  *
  */
-static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain)
+static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain,
+                                                  u16 family)
 {
        u32 bkt;
        struct list_head *bkt_list;
@@ -147,7 +158,9 @@ static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain)
                bkt = netlbl_domhsh_hash(domain);
                bkt_list = &netlbl_domhsh_rcu_deref(netlbl_domhsh)->tbl[bkt];
                list_for_each_entry_rcu(iter, bkt_list, list)
-                       if (iter->valid && strcmp(iter->domain, domain) == 0)
+                       if (iter->valid &&
+                           netlbl_family_match(iter->family, family) &&
+                           strcmp(iter->domain, domain) == 0)
                                return iter;
        }
 
@@ -157,28 +170,37 @@ static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain)
 /**
  * netlbl_domhsh_search_def - Search for a domain entry
  * @domain: the domain
- * @def: return default if no match is found
+ * @family: the address family
  *
  * Description:
  * Searches the domain hash table and returns a pointer to the hash table
  * entry if an exact match is found, if an exact match is not present in the
  * hash table then the default entry is returned if valid otherwise NULL is
- * returned.  The caller is responsible ensuring that the hash table is
+ * returned.  @family may be %AF_UNSPEC which matches any address family
+ * entries.  The caller is responsible ensuring that the hash table is
  * protected with either a RCU read lock or the hash table lock.
  *
  */
-static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain)
+static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain,
+                                                      u16 family)
 {
        struct netlbl_dom_map *entry;
 
-       entry = netlbl_domhsh_search(domain);
-       if (entry == NULL) {
-               entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def);
-               if (entry != NULL && !entry->valid)
-                       entry = NULL;
+       entry = netlbl_domhsh_search(domain, family);
+       if (entry != NULL)
+               return entry;
+       if (family == AF_INET || family == AF_UNSPEC) {
+               entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def_ipv4);
+               if (entry != NULL && entry->valid)
+                       return entry;
+       }
+       if (family == AF_INET6 || family == AF_UNSPEC) {
+               entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def_ipv6);
+               if (entry != NULL && entry->valid)
+                       return entry;
        }
 
-       return entry;
+       return NULL;
 }
 
 /**
@@ -203,6 +225,7 @@ static void netlbl_domhsh_audit_add(struct netlbl_dom_map *entry,
 {
        struct audit_buffer *audit_buf;
        struct cipso_v4_doi *cipsov4 = NULL;
+       struct calipso_doi *calipso = NULL;
        u32 type;
 
        audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info);
@@ -221,12 +244,14 @@ static void netlbl_domhsh_audit_add(struct netlbl_dom_map *entry,
                        struct netlbl_domaddr6_map *map6;
                        map6 = netlbl_domhsh_addr6_entry(addr6);
                        type = map6->def.type;
+                       calipso = map6->def.calipso;
                        netlbl_af6list_audit_addr(audit_buf, 0, NULL,
                                                  &addr6->addr, &addr6->mask);
 #endif /* IPv6 */
                } else {
                        type = entry->def.type;
                        cipsov4 = entry->def.cipso;
+                       calipso = entry->def.calipso;
                }
                switch (type) {
                case NETLBL_NLTYPE_UNLABELED:
@@ -238,6 +263,12 @@ static void netlbl_domhsh_audit_add(struct netlbl_dom_map *entry,
                                         " nlbl_protocol=cipsov4 cipso_doi=%u",
                                         cipsov4->doi);
                        break;
+               case NETLBL_NLTYPE_CALIPSO:
+                       BUG_ON(calipso == NULL);
+                       audit_log_format(audit_buf,
+                                        " nlbl_protocol=calipso calipso_doi=%u",
+                                        calipso->doi);
+                       break;
                }
                audit_log_format(audit_buf, " res=%u", result == 0 ? 1 : 0);
                audit_log_end(audit_buf);
@@ -264,13 +295,25 @@ static int netlbl_domhsh_validate(const struct netlbl_dom_map *entry)
        if (entry == NULL)
                return -EINVAL;
 
+       if (entry->family != AF_INET && entry->family != AF_INET6 &&
+           (entry->family != AF_UNSPEC ||
+            entry->def.type != NETLBL_NLTYPE_UNLABELED))
+               return -EINVAL;
+
        switch (entry->def.type) {
        case NETLBL_NLTYPE_UNLABELED:
-               if (entry->def.cipso != NULL || entry->def.addrsel != NULL)
+               if (entry->def.cipso != NULL || entry->def.calipso != NULL ||
+                   entry->def.addrsel != NULL)
                        return -EINVAL;
                break;
        case NETLBL_NLTYPE_CIPSOV4:
-               if (entry->def.cipso == NULL)
+               if (entry->family != AF_INET ||
+                   entry->def.cipso == NULL)
+                       return -EINVAL;
+               break;
+       case NETLBL_NLTYPE_CALIPSO:
+               if (entry->family != AF_INET6 ||
+                   entry->def.calipso == NULL)
                        return -EINVAL;
                break;
        case NETLBL_NLTYPE_ADDRSELECT:
@@ -294,6 +337,12 @@ static int netlbl_domhsh_validate(const struct netlbl_dom_map *entry)
                        map6 = netlbl_domhsh_addr6_entry(iter6);
                        switch (map6->def.type) {
                        case NETLBL_NLTYPE_UNLABELED:
+                               if (map6->def.calipso != NULL)
+                                       return -EINVAL;
+                               break;
+                       case NETLBL_NLTYPE_CALIPSO:
+                               if (map6->def.calipso == NULL)
+                                       return -EINVAL;
                                break;
                        default:
                                return -EINVAL;
@@ -358,15 +407,18 @@ int __init netlbl_domhsh_init(u32 size)
  *
  * Description:
  * Adds a new entry to the domain hash table and handles any updates to the
- * lower level protocol handler (i.e. CIPSO).  Returns zero on success,
- * negative on failure.
+ * lower level protocol handler (i.e. CIPSO).  @entry->family may be set to
+ * %AF_UNSPEC which will add an entry that matches all address families.  This
+ * is only useful for the unlabelled type and will only succeed if there is no
+ * existing entry for any address family with the same domain.  Returns zero
+ * on success, negative on failure.
  *
  */
 int netlbl_domhsh_add(struct netlbl_dom_map *entry,
                      struct netlbl_audit *audit_info)
 {
        int ret_val = 0;
-       struct netlbl_dom_map *entry_old;
+       struct netlbl_dom_map *entry_old, *entry_b;
        struct netlbl_af4list *iter4;
        struct netlbl_af4list *tmp4;
 #if IS_ENABLED(CONFIG_IPV6)
@@ -385,9 +437,10 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
        rcu_read_lock();
        spin_lock(&netlbl_domhsh_lock);
        if (entry->domain != NULL)
-               entry_old = netlbl_domhsh_search(entry->domain);
+               entry_old = netlbl_domhsh_search(entry->domain, entry->family);
        else
-               entry_old = netlbl_domhsh_search_def(entry->domain);
+               entry_old = netlbl_domhsh_search_def(entry->domain,
+                                                    entry->family);
        if (entry_old == NULL) {
                entry->valid = 1;
 
@@ -397,7 +450,41 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
                                    &rcu_dereference(netlbl_domhsh)->tbl[bkt]);
                } else {
                        INIT_LIST_HEAD(&entry->list);
-                       rcu_assign_pointer(netlbl_domhsh_def, entry);
+                       switch (entry->family) {
+                       case AF_INET:
+                               rcu_assign_pointer(netlbl_domhsh_def_ipv4,
+                                                  entry);
+                               break;
+                       case AF_INET6:
+                               rcu_assign_pointer(netlbl_domhsh_def_ipv6,
+                                                  entry);
+                               break;
+                       case AF_UNSPEC:
+                               if (entry->def.type !=
+                                   NETLBL_NLTYPE_UNLABELED) {
+                                       ret_val = -EINVAL;
+                                       goto add_return;
+                               }
+                               entry_b = kzalloc(sizeof(*entry_b), GFP_ATOMIC);
+                               if (entry_b == NULL) {
+                                       ret_val = -ENOMEM;
+                                       goto add_return;
+                               }
+                               entry_b->family = AF_INET6;
+                               entry_b->def.type = NETLBL_NLTYPE_UNLABELED;
+                               entry_b->valid = 1;
+                               entry->family = AF_INET;
+                               rcu_assign_pointer(netlbl_domhsh_def_ipv4,
+                                                  entry);
+                               rcu_assign_pointer(netlbl_domhsh_def_ipv6,
+                                                  entry_b);
+                               break;
+                       default:
+                               /* Already checked in
+                                * netlbl_domhsh_validate(). */
+                               ret_val = -EINVAL;
+                               goto add_return;
+                       }
                }
 
                if (entry->def.type == NETLBL_NLTYPE_ADDRSELECT) {
@@ -513,10 +600,12 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
        spin_lock(&netlbl_domhsh_lock);
        if (entry->valid) {
                entry->valid = 0;
-               if (entry != rcu_dereference(netlbl_domhsh_def))
-                       list_del_rcu(&entry->list);
+               if (entry == rcu_dereference(netlbl_domhsh_def_ipv4))
+                       RCU_INIT_POINTER(netlbl_domhsh_def_ipv4, NULL);
+               else if (entry == rcu_dereference(netlbl_domhsh_def_ipv6))
+                       RCU_INIT_POINTER(netlbl_domhsh_def_ipv6, NULL);
                else
-                       RCU_INIT_POINTER(netlbl_domhsh_def, NULL);
+                       list_del_rcu(&entry->list);
        } else
                ret_val = -ENOENT;
        spin_unlock(&netlbl_domhsh_lock);
@@ -533,6 +622,10 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
        if (ret_val == 0) {
                struct netlbl_af4list *iter4;
                struct netlbl_domaddr4_map *map4;
+#if IS_ENABLED(CONFIG_IPV6)
+               struct netlbl_af6list *iter6;
+               struct netlbl_domaddr6_map *map6;
+#endif /* IPv6 */
 
                switch (entry->def.type) {
                case NETLBL_NLTYPE_ADDRSELECT:
@@ -541,12 +634,22 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
                                map4 = netlbl_domhsh_addr4_entry(iter4);
                                cipso_v4_doi_putdef(map4->def.cipso);
                        }
-                       /* no need to check the IPv6 list since we currently
-                        * support only unlabeled protocols for IPv6 */
+#if IS_ENABLED(CONFIG_IPV6)
+                       netlbl_af6list_foreach_rcu(iter6,
+                                            &entry->def.addrsel->list6) {
+                               map6 = netlbl_domhsh_addr6_entry(iter6);
+                               calipso_doi_putdef(map6->def.calipso);
+                       }
+#endif /* IPv6 */
                        break;
                case NETLBL_NLTYPE_CIPSOV4:
                        cipso_v4_doi_putdef(entry->def.cipso);
                        break;
+#if IS_ENABLED(CONFIG_IPV6)
+               case NETLBL_NLTYPE_CALIPSO:
+                       calipso_doi_putdef(entry->def.calipso);
+                       break;
+#endif /* IPv6 */
                }
                call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
        }
@@ -583,9 +686,9 @@ int netlbl_domhsh_remove_af4(const char *domain,
        rcu_read_lock();
 
        if (domain)
-               entry_map = netlbl_domhsh_search(domain);
+               entry_map = netlbl_domhsh_search(domain, AF_INET);
        else
-               entry_map = netlbl_domhsh_search_def(domain);
+               entry_map = netlbl_domhsh_search_def(domain, AF_INET);
        if (entry_map == NULL ||
            entry_map->def.type != NETLBL_NLTYPE_ADDRSELECT)
                goto remove_af4_failure;
@@ -622,28 +725,114 @@ remove_af4_failure:
        return -ENOENT;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+/**
+ * netlbl_domhsh_remove_af6 - Removes an address selector entry
+ * @domain: the domain
+ * @addr: IPv6 address
+ * @mask: IPv6 address mask
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Removes an individual address selector from a domain mapping and potentially
+ * the entire mapping if it is empty.  Returns zero on success, negative values
+ * on failure.
+ *
+ */
+int netlbl_domhsh_remove_af6(const char *domain,
+                            const struct in6_addr *addr,
+                            const struct in6_addr *mask,
+                            struct netlbl_audit *audit_info)
+{
+       struct netlbl_dom_map *entry_map;
+       struct netlbl_af6list *entry_addr;
+       struct netlbl_af4list *iter4;
+       struct netlbl_af6list *iter6;
+       struct netlbl_domaddr6_map *entry;
+
+       rcu_read_lock();
+
+       if (domain)
+               entry_map = netlbl_domhsh_search(domain, AF_INET6);
+       else
+               entry_map = netlbl_domhsh_search_def(domain, AF_INET6);
+       if (entry_map == NULL ||
+           entry_map->def.type != NETLBL_NLTYPE_ADDRSELECT)
+               goto remove_af6_failure;
+
+       spin_lock(&netlbl_domhsh_lock);
+       entry_addr = netlbl_af6list_remove(addr, mask,
+                                          &entry_map->def.addrsel->list6);
+       spin_unlock(&netlbl_domhsh_lock);
+
+       if (entry_addr == NULL)
+               goto remove_af6_failure;
+       netlbl_af4list_foreach_rcu(iter4, &entry_map->def.addrsel->list4)
+               goto remove_af6_single_addr;
+       netlbl_af6list_foreach_rcu(iter6, &entry_map->def.addrsel->list6)
+               goto remove_af6_single_addr;
+       /* the domain mapping is empty so remove it from the mapping table */
+       netlbl_domhsh_remove_entry(entry_map, audit_info);
+
+remove_af6_single_addr:
+       rcu_read_unlock();
+       /* yick, we can't use call_rcu here because we don't have a rcu head
+        * pointer but hopefully this should be a rare case so the pause
+        * shouldn't be a problem */
+       synchronize_rcu();
+       entry = netlbl_domhsh_addr6_entry(entry_addr);
+       calipso_doi_putdef(entry->def.calipso);
+       kfree(entry);
+       return 0;
+
+remove_af6_failure:
+       rcu_read_unlock();
+       return -ENOENT;
+}
+#endif /* IPv6 */
+
 /**
  * netlbl_domhsh_remove - Removes an entry from the domain hash table
  * @domain: the domain to remove
+ * @family: address family
  * @audit_info: NetLabel audit information
  *
  * Description:
  * Removes an entry from the domain hash table and handles any updates to the
- * lower level protocol handler (i.e. CIPSO).  Returns zero on success,
- * negative on failure.
+ * lower level protocol handler (i.e. CIPSO).  @family may be %AF_UNSPEC which
+ * removes all address family entries.  Returns zero on success, negative on
+ * failure.
  *
  */
-int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
+int netlbl_domhsh_remove(const char *domain, u16 family,
+                        struct netlbl_audit *audit_info)
 {
-       int ret_val;
+       int ret_val = -EINVAL;
        struct netlbl_dom_map *entry;
 
        rcu_read_lock();
-       if (domain)
-               entry = netlbl_domhsh_search(domain);
-       else
-               entry = netlbl_domhsh_search_def(domain);
-       ret_val = netlbl_domhsh_remove_entry(entry, audit_info);
+
+       if (family == AF_INET || family == AF_UNSPEC) {
+               if (domain)
+                       entry = netlbl_domhsh_search(domain, AF_INET);
+               else
+                       entry = netlbl_domhsh_search_def(domain, AF_INET);
+               ret_val = netlbl_domhsh_remove_entry(entry, audit_info);
+               if (ret_val && ret_val != -ENOENT)
+                       goto done;
+       }
+       if (family == AF_INET6 || family == AF_UNSPEC) {
+               int ret_val2;
+
+               if (domain)
+                       entry = netlbl_domhsh_search(domain, AF_INET6);
+               else
+                       entry = netlbl_domhsh_search_def(domain, AF_INET6);
+               ret_val2 = netlbl_domhsh_remove_entry(entry, audit_info);
+               if (ret_val2 != -ENOENT)
+                       ret_val = ret_val2;
+       }
+done:
        rcu_read_unlock();
 
        return ret_val;
@@ -651,32 +840,38 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
 
 /**
  * netlbl_domhsh_remove_default - Removes the default entry from the table
+ * @family: address family
  * @audit_info: NetLabel audit information
  *
  * Description:
- * Removes/resets the default entry for the domain hash table and handles any
- * updates to the lower level protocol handler (i.e. CIPSO).  Returns zero on
- * success, non-zero on failure.
+ * Removes/resets the default entry corresponding to @family from the domain
+ * hash table and handles any updates to the lower level protocol handler
+ * (i.e. CIPSO).  @family may be %AF_UNSPEC which removes all address family
+ * entries.  Returns zero on success, negative on failure.
  *
  */
-int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info)
+int netlbl_domhsh_remove_default(u16 family, struct netlbl_audit *audit_info)
 {
-       return netlbl_domhsh_remove(NULL, audit_info);
+       return netlbl_domhsh_remove(NULL, family, audit_info);
 }
 
 /**
  * netlbl_domhsh_getentry - Get an entry from the domain hash table
  * @domain: the domain name to search for
+ * @family: address family
  *
  * Description:
  * Look through the domain hash table searching for an entry to match @domain,
- * return a pointer to a copy of the entry or NULL.  The caller is responsible
- * for ensuring that rcu_read_[un]lock() is called.
+ * with address family @family, return a pointer to a copy of the entry or
+ * NULL.  The caller is responsible for ensuring that rcu_read_[un]lock() is
+ * called.
  *
  */
-struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain)
+struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain, u16 family)
 {
-       return netlbl_domhsh_search_def(domain);
+       if (family == AF_UNSPEC)
+               return NULL;
+       return netlbl_domhsh_search_def(domain, family);
 }
 
 /**
@@ -696,7 +891,7 @@ struct netlbl_dommap_def *netlbl_domhsh_getentry_af4(const char *domain,
        struct netlbl_dom_map *dom_iter;
        struct netlbl_af4list *addr_iter;
 
-       dom_iter = netlbl_domhsh_search_def(domain);
+       dom_iter = netlbl_domhsh_search_def(domain, AF_INET);
        if (dom_iter == NULL)
                return NULL;
 
@@ -726,7 +921,7 @@ struct netlbl_dommap_def *netlbl_domhsh_getentry_af6(const char *domain,
        struct netlbl_dom_map *dom_iter;
        struct netlbl_af6list *addr_iter;
 
-       dom_iter = netlbl_domhsh_search_def(domain);
+       dom_iter = netlbl_domhsh_search_def(domain, AF_INET6);
        if (dom_iter == NULL)
                return NULL;
 
index 680caf4dff567f61ccaa41a70a9276101ac27fcd..1f92477819274013991533b8e74ba0b1e1e1e9ed 100644 (file)
@@ -51,6 +51,7 @@ struct netlbl_dommap_def {
        union {
                struct netlbl_domaddr_map *addrsel;
                struct cipso_v4_doi *cipso;
+               struct calipso_doi *calipso;
        };
 };
 #define netlbl_domhsh_addr4_entry(iter) \
@@ -70,6 +71,7 @@ struct netlbl_domaddr6_map {
 
 struct netlbl_dom_map {
        char *domain;
+       u16 family;
        struct netlbl_dommap_def def;
 
        u32 valid;
@@ -91,14 +93,23 @@ int netlbl_domhsh_remove_af4(const char *domain,
                             const struct in_addr *addr,
                             const struct in_addr *mask,
                             struct netlbl_audit *audit_info);
-int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info);
-int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info);
-struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain);
+int netlbl_domhsh_remove_af6(const char *domain,
+                            const struct in6_addr *addr,
+                            const struct in6_addr *mask,
+                            struct netlbl_audit *audit_info);
+int netlbl_domhsh_remove(const char *domain, u16 family,
+                        struct netlbl_audit *audit_info);
+int netlbl_domhsh_remove_default(u16 family, struct netlbl_audit *audit_info);
+struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain, u16 family);
 struct netlbl_dommap_def *netlbl_domhsh_getentry_af4(const char *domain,
                                                     __be32 addr);
 #if IS_ENABLED(CONFIG_IPV6)
 struct netlbl_dommap_def *netlbl_domhsh_getentry_af6(const char *domain,
                                                   const struct in6_addr *addr);
+int netlbl_domhsh_remove_af6(const char *domain,
+                            const struct in6_addr *addr,
+                            const struct in6_addr *mask,
+                            struct netlbl_audit *audit_info);
 #endif /* IPv6 */
 
 int netlbl_domhsh_walk(u32 *skip_bkt,
index 1325776daa278a1c812a5c184038eb6e69c4b5ba..28c56b95fb7ff6ebaf5a99d8cfcbf583a01e73f5 100644 (file)
 #include <net/ipv6.h>
 #include <net/netlabel.h>
 #include <net/cipso_ipv4.h>
+#include <net/calipso.h>
 #include <asm/bug.h>
 #include <linux/atomic.h>
 
 #include "netlabel_domainhash.h"
 #include "netlabel_unlabeled.h"
 #include "netlabel_cipso_v4.h"
+#include "netlabel_calipso.h"
 #include "netlabel_user.h"
 #include "netlabel_mgmt.h"
 #include "netlabel_addrlist.h"
@@ -72,12 +74,17 @@ int netlbl_cfg_map_del(const char *domain,
                       struct netlbl_audit *audit_info)
 {
        if (addr == NULL && mask == NULL) {
-               return netlbl_domhsh_remove(domain, audit_info);
+               return netlbl_domhsh_remove(domain, family, audit_info);
        } else if (addr != NULL && mask != NULL) {
                switch (family) {
                case AF_INET:
                        return netlbl_domhsh_remove_af4(domain, addr, mask,
                                                        audit_info);
+#if IS_ENABLED(CONFIG_IPV6)
+               case AF_INET6:
+                       return netlbl_domhsh_remove_af6(domain, addr, mask,
+                                                       audit_info);
+#endif /* IPv6 */
                default:
                        return -EPFNOSUPPORT;
                }
@@ -119,6 +126,7 @@ int netlbl_cfg_unlbl_map_add(const char *domain,
                if (entry->domain == NULL)
                        goto cfg_unlbl_map_add_failure;
        }
+       entry->family = family;
 
        if (addr == NULL && mask == NULL)
                entry->def.type = NETLBL_NLTYPE_UNLABELED;
@@ -345,6 +353,7 @@ int netlbl_cfg_cipsov4_map_add(u32 doi,
        entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
        if (entry == NULL)
                goto out_entry;
+       entry->family = AF_INET;
        if (domain != NULL) {
                entry->domain = kstrdup(domain, GFP_ATOMIC);
                if (entry->domain == NULL)
@@ -399,6 +408,139 @@ out_entry:
        return ret_val;
 }
 
+/**
+ * netlbl_cfg_calipso_add - Add a new CALIPSO DOI definition
+ * @doi_def: CALIPSO DOI definition
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Add a new CALIPSO DOI definition as defined by @doi_def.  Returns zero on
+ * success and negative values on failure.
+ *
+ */
+int netlbl_cfg_calipso_add(struct calipso_doi *doi_def,
+                          struct netlbl_audit *audit_info)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+       return calipso_doi_add(doi_def, audit_info);
+#else /* IPv6 */
+       return -ENOSYS;
+#endif /* IPv6 */
+}
+
+/**
+ * netlbl_cfg_calipso_del - Remove an existing CALIPSO DOI definition
+ * @doi: CALIPSO DOI
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Remove an existing CALIPSO DOI definition matching @doi.  Returns zero on
+ * success and negative values on failure.
+ *
+ */
+void netlbl_cfg_calipso_del(u32 doi, struct netlbl_audit *audit_info)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+       calipso_doi_remove(doi, audit_info);
+#endif /* IPv6 */
+}
+
+/**
+ * netlbl_cfg_calipso_map_add - Add a new CALIPSO DOI mapping
+ * @doi: the CALIPSO DOI
+ * @domain: the domain mapping to add
+ * @addr: IP address
+ * @mask: IP address mask
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Add a new NetLabel/LSM domain mapping for the given CALIPSO DOI to the
+ * NetLabel subsystem.  A @domain value of NULL adds a new default domain
+ * mapping.  Returns zero on success, negative values on failure.
+ *
+ */
+int netlbl_cfg_calipso_map_add(u32 doi,
+                              const char *domain,
+                              const struct in6_addr *addr,
+                              const struct in6_addr *mask,
+                              struct netlbl_audit *audit_info)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+       int ret_val = -ENOMEM;
+       struct calipso_doi *doi_def;
+       struct netlbl_dom_map *entry;
+       struct netlbl_domaddr_map *addrmap = NULL;
+       struct netlbl_domaddr6_map *addrinfo = NULL;
+
+       doi_def = calipso_doi_getdef(doi);
+       if (doi_def == NULL)
+               return -ENOENT;
+
+       entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+       if (entry == NULL)
+               goto out_entry;
+       entry->family = AF_INET6;
+       if (domain != NULL) {
+               entry->domain = kstrdup(domain, GFP_ATOMIC);
+               if (entry->domain == NULL)
+                       goto out_domain;
+       }
+
+       if (addr == NULL && mask == NULL) {
+               entry->def.calipso = doi_def;
+               entry->def.type = NETLBL_NLTYPE_CALIPSO;
+       } else if (addr != NULL && mask != NULL) {
+               addrmap = kzalloc(sizeof(*addrmap), GFP_ATOMIC);
+               if (addrmap == NULL)
+                       goto out_addrmap;
+               INIT_LIST_HEAD(&addrmap->list4);
+               INIT_LIST_HEAD(&addrmap->list6);
+
+               addrinfo = kzalloc(sizeof(*addrinfo), GFP_ATOMIC);
+               if (addrinfo == NULL)
+                       goto out_addrinfo;
+               addrinfo->def.calipso = doi_def;
+               addrinfo->def.type = NETLBL_NLTYPE_CALIPSO;
+               addrinfo->list.addr = *addr;
+               addrinfo->list.addr.s6_addr32[0] &= mask->s6_addr32[0];
+               addrinfo->list.addr.s6_addr32[1] &= mask->s6_addr32[1];
+               addrinfo->list.addr.s6_addr32[2] &= mask->s6_addr32[2];
+               addrinfo->list.addr.s6_addr32[3] &= mask->s6_addr32[3];
+               addrinfo->list.mask = *mask;
+               addrinfo->list.valid = 1;
+               ret_val = netlbl_af6list_add(&addrinfo->list, &addrmap->list6);
+               if (ret_val != 0)
+                       goto cfg_calipso_map_add_failure;
+
+               entry->def.addrsel = addrmap;
+               entry->def.type = NETLBL_NLTYPE_ADDRSELECT;
+       } else {
+               ret_val = -EINVAL;
+               goto out_addrmap;
+       }
+
+       ret_val = netlbl_domhsh_add(entry, audit_info);
+       if (ret_val != 0)
+               goto cfg_calipso_map_add_failure;
+
+       return 0;
+
+cfg_calipso_map_add_failure:
+       kfree(addrinfo);
+out_addrinfo:
+       kfree(addrmap);
+out_addrmap:
+       kfree(entry->domain);
+out_domain:
+       kfree(entry);
+out_entry:
+       calipso_doi_putdef(doi_def);
+       return ret_val;
+#else /* IPv6 */
+       return -ENOSYS;
+#endif /* IPv6 */
+}
+
 /*
  * Security Attribute Functions
  */
@@ -519,6 +661,7 @@ int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap, u32 offset)
 
        return -ENOENT;
 }
+EXPORT_SYMBOL(netlbl_catmap_walk);
 
 /**
  * netlbl_catmap_walkrng - Find the end of a string of set bits
@@ -609,20 +752,19 @@ int netlbl_catmap_getlong(struct netlbl_lsm_catmap *catmap,
                off = catmap->startbit;
                *offset = off;
        }
-       iter = _netlbl_catmap_getnode(&catmap, off, _CM_F_NONE, 0);
+       iter = _netlbl_catmap_getnode(&catmap, off, _CM_F_WALK, 0);
        if (iter == NULL) {
                *offset = (u32)-1;
                return 0;
        }
 
        if (off < iter->startbit) {
-               off = iter->startbit;
-               *offset = off;
+               *offset = iter->startbit;
+               off = 0;
        } else
                off -= iter->startbit;
-
        idx = off / NETLBL_CATMAP_MAPSIZE;
-       *bitmap = iter->bitmap[idx] >> (off % NETLBL_CATMAP_SIZE);
+       *bitmap = iter->bitmap[idx] >> (off % NETLBL_CATMAP_MAPSIZE);
 
        return 0;
 }
@@ -655,6 +797,7 @@ int netlbl_catmap_setbit(struct netlbl_lsm_catmap **catmap,
 
        return 0;
 }
+EXPORT_SYMBOL(netlbl_catmap_setbit);
 
 /**
  * netlbl_catmap_setrng - Set a range of bits in a LSM secattr catmap
@@ -727,6 +870,76 @@ int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap,
        return 0;
 }
 
+/* Bitmap functions
+ */
+
+/**
+ * netlbl_bitmap_walk - Walk a bitmap looking for a bit
+ * @bitmap: the bitmap
+ * @bitmap_len: length in bits
+ * @offset: starting offset
+ * @state: if non-zero, look for a set (1) bit else look for a cleared (0) bit
+ *
+ * Description:
+ * Starting at @offset, walk the bitmap from left to right until either the
+ * desired bit is found or we reach the end.  Return the bit offset, -1 if
+ * not found, or -2 if error.
+ */
+int netlbl_bitmap_walk(const unsigned char *bitmap, u32 bitmap_len,
+                      u32 offset, u8 state)
+{
+       u32 bit_spot;
+       u32 byte_offset;
+       unsigned char bitmask;
+       unsigned char byte;
+
+       byte_offset = offset / 8;
+       byte = bitmap[byte_offset];
+       bit_spot = offset;
+       bitmask = 0x80 >> (offset % 8);
+
+       while (bit_spot < bitmap_len) {
+               if ((state && (byte & bitmask) == bitmask) ||
+                   (state == 0 && (byte & bitmask) == 0))
+                       return bit_spot;
+
+               bit_spot++;
+               bitmask >>= 1;
+               if (bitmask == 0) {
+                       byte = bitmap[++byte_offset];
+                       bitmask = 0x80;
+               }
+       }
+
+       return -1;
+}
+EXPORT_SYMBOL(netlbl_bitmap_walk);
+
+/**
+ * netlbl_bitmap_setbit - Sets a single bit in a bitmap
+ * @bitmap: the bitmap
+ * @bit: the bit
+ * @state: if non-zero, set the bit (1) else clear the bit (0)
+ *
+ * Description:
+ * Set a single bit in the bitmask.  Returns zero on success, negative values
+ * on error.
+ */
+void netlbl_bitmap_setbit(unsigned char *bitmap, u32 bit, u8 state)
+{
+       u32 byte_spot;
+       u8 bitmask;
+
+       /* gcc always rounds to zero when doing integer division */
+       byte_spot = bit / 8;
+       bitmask = 0x80 >> (bit % 8);
+       if (state)
+               bitmap[byte_spot] |= bitmask;
+       else
+               bitmap[byte_spot] &= ~bitmask;
+}
+EXPORT_SYMBOL(netlbl_bitmap_setbit);
+
 /*
  * LSM Functions
  */
@@ -774,7 +987,7 @@ int netlbl_sock_setattr(struct sock *sk,
        struct netlbl_dom_map *dom_entry;
 
        rcu_read_lock();
-       dom_entry = netlbl_domhsh_getentry(secattr->domain);
+       dom_entry = netlbl_domhsh_getentry(secattr->domain, family);
        if (dom_entry == NULL) {
                ret_val = -ENOENT;
                goto socket_setattr_return;
@@ -799,9 +1012,21 @@ int netlbl_sock_setattr(struct sock *sk,
                break;
 #if IS_ENABLED(CONFIG_IPV6)
        case AF_INET6:
-               /* since we don't support any IPv6 labeling protocols right
-                * now we can optimize everything away until we do */
-               ret_val = 0;
+               switch (dom_entry->def.type) {
+               case NETLBL_NLTYPE_ADDRSELECT:
+                       ret_val = -EDESTADDRREQ;
+                       break;
+               case NETLBL_NLTYPE_CALIPSO:
+                       ret_val = calipso_sock_setattr(sk,
+                                                      dom_entry->def.calipso,
+                                                      secattr);
+                       break;
+               case NETLBL_NLTYPE_UNLABELED:
+                       ret_val = 0;
+                       break;
+               default:
+                       ret_val = -ENOENT;
+               }
                break;
 #endif /* IPv6 */
        default:
@@ -824,7 +1049,16 @@ socket_setattr_return:
  */
 void netlbl_sock_delattr(struct sock *sk)
 {
-       cipso_v4_sock_delattr(sk);
+       switch (sk->sk_family) {
+       case AF_INET:
+               cipso_v4_sock_delattr(sk);
+               break;
+#if IS_ENABLED(CONFIG_IPV6)
+       case AF_INET6:
+               calipso_sock_delattr(sk);
+               break;
+#endif /* IPv6 */
+       }
 }
 
 /**
@@ -850,7 +1084,7 @@ int netlbl_sock_getattr(struct sock *sk,
                break;
 #if IS_ENABLED(CONFIG_IPV6)
        case AF_INET6:
-               ret_val = -ENOMSG;
+               ret_val = calipso_sock_getattr(sk, secattr);
                break;
 #endif /* IPv6 */
        default:
@@ -878,6 +1112,9 @@ int netlbl_conn_setattr(struct sock *sk,
 {
        int ret_val;
        struct sockaddr_in *addr4;
+#if IS_ENABLED(CONFIG_IPV6)
+       struct sockaddr_in6 *addr6;
+#endif
        struct netlbl_dommap_def *entry;
 
        rcu_read_lock();
@@ -898,7 +1135,7 @@ int netlbl_conn_setattr(struct sock *sk,
                case NETLBL_NLTYPE_UNLABELED:
                        /* just delete the protocols we support for right now
                         * but we could remove other protocols if needed */
-                       cipso_v4_sock_delattr(sk);
+                       netlbl_sock_delattr(sk);
                        ret_val = 0;
                        break;
                default:
@@ -907,9 +1144,27 @@ int netlbl_conn_setattr(struct sock *sk,
                break;
 #if IS_ENABLED(CONFIG_IPV6)
        case AF_INET6:
-               /* since we don't support any IPv6 labeling protocols right
-                * now we can optimize everything away until we do */
-               ret_val = 0;
+               addr6 = (struct sockaddr_in6 *)addr;
+               entry = netlbl_domhsh_getentry_af6(secattr->domain,
+                                                  &addr6->sin6_addr);
+               if (entry == NULL) {
+                       ret_val = -ENOENT;
+                       goto conn_setattr_return;
+               }
+               switch (entry->type) {
+               case NETLBL_NLTYPE_CALIPSO:
+                       ret_val = calipso_sock_setattr(sk,
+                                                      entry->calipso, secattr);
+                       break;
+               case NETLBL_NLTYPE_UNLABELED:
+                       /* just delete the protocols we support for right now
+                        * but we could remove other protocols if needed */
+                       netlbl_sock_delattr(sk);
+                       ret_val = 0;
+                       break;
+               default:
+                       ret_val = -ENOENT;
+               }
                break;
 #endif /* IPv6 */
        default:
@@ -936,12 +1191,13 @@ int netlbl_req_setattr(struct request_sock *req,
 {
        int ret_val;
        struct netlbl_dommap_def *entry;
+       struct inet_request_sock *ireq = inet_rsk(req);
 
        rcu_read_lock();
        switch (req->rsk_ops->family) {
        case AF_INET:
                entry = netlbl_domhsh_getentry_af4(secattr->domain,
-                                                  inet_rsk(req)->ir_rmt_addr);
+                                                  ireq->ir_rmt_addr);
                if (entry == NULL) {
                        ret_val = -ENOENT;
                        goto req_setattr_return;
@@ -952,9 +1208,7 @@ int netlbl_req_setattr(struct request_sock *req,
                                                       entry->cipso, secattr);
                        break;
                case NETLBL_NLTYPE_UNLABELED:
-                       /* just delete the protocols we support for right now
-                        * but we could remove other protocols if needed */
-                       cipso_v4_req_delattr(req);
+                       netlbl_req_delattr(req);
                        ret_val = 0;
                        break;
                default:
@@ -963,9 +1217,24 @@ int netlbl_req_setattr(struct request_sock *req,
                break;
 #if IS_ENABLED(CONFIG_IPV6)
        case AF_INET6:
-               /* since we don't support any IPv6 labeling protocols right
-                * now we can optimize everything away until we do */
-               ret_val = 0;
+               entry = netlbl_domhsh_getentry_af6(secattr->domain,
+                                                  &ireq->ir_v6_rmt_addr);
+               if (entry == NULL) {
+                       ret_val = -ENOENT;
+                       goto req_setattr_return;
+               }
+               switch (entry->type) {
+               case NETLBL_NLTYPE_CALIPSO:
+                       ret_val = calipso_req_setattr(req,
+                                                     entry->calipso, secattr);
+                       break;
+               case NETLBL_NLTYPE_UNLABELED:
+                       netlbl_req_delattr(req);
+                       ret_val = 0;
+                       break;
+               default:
+                       ret_val = -ENOENT;
+               }
                break;
 #endif /* IPv6 */
        default:
@@ -987,7 +1256,16 @@ req_setattr_return:
 */
 void netlbl_req_delattr(struct request_sock *req)
 {
-       cipso_v4_req_delattr(req);
+       switch (req->rsk_ops->family) {
+       case AF_INET:
+               cipso_v4_req_delattr(req);
+               break;
+#if IS_ENABLED(CONFIG_IPV6)
+       case AF_INET6:
+               calipso_req_delattr(req);
+               break;
+#endif /* IPv6 */
+       }
 }
 
 /**
@@ -1007,13 +1285,17 @@ int netlbl_skbuff_setattr(struct sk_buff *skb,
 {
        int ret_val;
        struct iphdr *hdr4;
+#if IS_ENABLED(CONFIG_IPV6)
+       struct ipv6hdr *hdr6;
+#endif
        struct netlbl_dommap_def *entry;
 
        rcu_read_lock();
        switch (family) {
        case AF_INET:
                hdr4 = ip_hdr(skb);
-               entry = netlbl_domhsh_getentry_af4(secattr->domain,hdr4->daddr);
+               entry = netlbl_domhsh_getentry_af4(secattr->domain,
+                                                  hdr4->daddr);
                if (entry == NULL) {
                        ret_val = -ENOENT;
                        goto skbuff_setattr_return;
@@ -1034,9 +1316,26 @@ int netlbl_skbuff_setattr(struct sk_buff *skb,
                break;
 #if IS_ENABLED(CONFIG_IPV6)
        case AF_INET6:
-               /* since we don't support any IPv6 labeling protocols right
-                * now we can optimize everything away until we do */
-               ret_val = 0;
+               hdr6 = ipv6_hdr(skb);
+               entry = netlbl_domhsh_getentry_af6(secattr->domain,
+                                                  &hdr6->daddr);
+               if (entry == NULL) {
+                       ret_val = -ENOENT;
+                       goto skbuff_setattr_return;
+               }
+               switch (entry->type) {
+               case NETLBL_NLTYPE_CALIPSO:
+                       ret_val = calipso_skbuff_setattr(skb, entry->calipso,
+                                                        secattr);
+                       break;
+               case NETLBL_NLTYPE_UNLABELED:
+                       /* just delete the protocols we support for right now
+                        * but we could remove other protocols if needed */
+                       ret_val = calipso_skbuff_delattr(skb);
+                       break;
+               default:
+                       ret_val = -ENOENT;
+               }
                break;
 #endif /* IPv6 */
        default:
@@ -1075,6 +1374,9 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb,
                break;
 #if IS_ENABLED(CONFIG_IPV6)
        case AF_INET6:
+               ptr = calipso_optptr(skb);
+               if (ptr && calipso_getattr(ptr, secattr) == 0)
+                       return 0;
                break;
 #endif /* IPv6 */
        }
@@ -1085,6 +1387,7 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb,
 /**
  * netlbl_skbuff_err - Handle a LSM error on a sk_buff
  * @skb: the packet
+ * @family: the family
  * @error: the error code
  * @gateway: true if host is acting as a gateway, false otherwise
  *
@@ -1094,10 +1397,14 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb,
  * according to the packet's labeling protocol.
  *
  */
-void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway)
+void netlbl_skbuff_err(struct sk_buff *skb, u16 family, int error, int gateway)
 {
-       if (cipso_v4_optptr(skb))
-               cipso_v4_error(skb, error, gateway);
+       switch (family) {
+       case AF_INET:
+               if (cipso_v4_optptr(skb))
+                       cipso_v4_error(skb, error, gateway);
+               break;
+       }
 }
 
 /**
@@ -1112,11 +1419,15 @@ void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway)
 void netlbl_cache_invalidate(void)
 {
        cipso_v4_cache_invalidate();
+#if IS_ENABLED(CONFIG_IPV6)
+       calipso_cache_invalidate();
+#endif /* IPv6 */
 }
 
 /**
  * netlbl_cache_add - Add an entry to a NetLabel protocol cache
  * @skb: the packet
+ * @family: the family
  * @secattr: the packet's security attributes
  *
  * Description:
@@ -1125,7 +1436,7 @@ void netlbl_cache_invalidate(void)
  * values on error.
  *
  */
-int netlbl_cache_add(const struct sk_buff *skb,
+int netlbl_cache_add(const struct sk_buff *skb, u16 family,
                     const struct netlbl_lsm_secattr *secattr)
 {
        unsigned char *ptr;
@@ -1133,10 +1444,20 @@ int netlbl_cache_add(const struct sk_buff *skb,
        if ((secattr->flags & NETLBL_SECATTR_CACHE) == 0)
                return -ENOMSG;
 
-       ptr = cipso_v4_optptr(skb);
-       if (ptr)
-               return cipso_v4_cache_add(ptr, secattr);
-
+       switch (family) {
+       case AF_INET:
+               ptr = cipso_v4_optptr(skb);
+               if (ptr)
+                       return cipso_v4_cache_add(ptr, secattr);
+               break;
+#if IS_ENABLED(CONFIG_IPV6)
+       case AF_INET6:
+               ptr = calipso_optptr(skb);
+               if (ptr)
+                       return calipso_cache_add(ptr, secattr);
+               break;
+#endif /* IPv6 */
+       }
        return -ENOMSG;
 }
 
@@ -1161,6 +1482,7 @@ struct audit_buffer *netlbl_audit_start(int type,
 {
        return netlbl_audit_start_common(type, audit_info);
 }
+EXPORT_SYMBOL(netlbl_audit_start);
 
 /*
  * Setup Functions
index 13f777f20995bbe02edbce36a9c04f2518fe0355..f85d0e07af2dd9c7f4f4fb2f6b05fcf99b21c8eb 100644 (file)
 #include <net/ipv6.h>
 #include <net/netlabel.h>
 #include <net/cipso_ipv4.h>
+#include <net/calipso.h>
 #include <linux/atomic.h>
 
+#include "netlabel_calipso.h"
 #include "netlabel_domainhash.h"
 #include "netlabel_user.h"
 #include "netlabel_mgmt.h"
@@ -72,6 +74,8 @@ static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = {
        [NLBL_MGMT_A_PROTOCOL] = { .type = NLA_U32 },
        [NLBL_MGMT_A_VERSION] = { .type = NLA_U32 },
        [NLBL_MGMT_A_CV4DOI] = { .type = NLA_U32 },
+       [NLBL_MGMT_A_FAMILY] = { .type = NLA_U16 },
+       [NLBL_MGMT_A_CLPDOI] = { .type = NLA_U32 },
 };
 
 /*
@@ -95,6 +99,9 @@ static int netlbl_mgmt_add_common(struct genl_info *info,
        int ret_val = -EINVAL;
        struct netlbl_domaddr_map *addrmap = NULL;
        struct cipso_v4_doi *cipsov4 = NULL;
+#if IS_ENABLED(CONFIG_IPV6)
+       struct calipso_doi *calipso = NULL;
+#endif
        u32 tmp_val;
        struct netlbl_dom_map *entry = kzalloc(sizeof(*entry), GFP_KERNEL);
 
@@ -119,6 +126,11 @@ static int netlbl_mgmt_add_common(struct genl_info *info,
 
        switch (entry->def.type) {
        case NETLBL_NLTYPE_UNLABELED:
+               if (info->attrs[NLBL_MGMT_A_FAMILY])
+                       entry->family =
+                               nla_get_u16(info->attrs[NLBL_MGMT_A_FAMILY]);
+               else
+                       entry->family = AF_UNSPEC;
                break;
        case NETLBL_NLTYPE_CIPSOV4:
                if (!info->attrs[NLBL_MGMT_A_CV4DOI])
@@ -128,12 +140,30 @@ static int netlbl_mgmt_add_common(struct genl_info *info,
                cipsov4 = cipso_v4_doi_getdef(tmp_val);
                if (cipsov4 == NULL)
                        goto add_free_domain;
+               entry->family = AF_INET;
                entry->def.cipso = cipsov4;
                break;
+#if IS_ENABLED(CONFIG_IPV6)
+       case NETLBL_NLTYPE_CALIPSO:
+               if (!info->attrs[NLBL_MGMT_A_CLPDOI])
+                       goto add_free_domain;
+
+               tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CLPDOI]);
+               calipso = calipso_doi_getdef(tmp_val);
+               if (calipso == NULL)
+                       goto add_free_domain;
+               entry->family = AF_INET6;
+               entry->def.calipso = calipso;
+               break;
+#endif /* IPv6 */
        default:
                goto add_free_domain;
        }
 
+       if ((entry->family == AF_INET && info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
+           (entry->family == AF_INET6 && info->attrs[NLBL_MGMT_A_IPV4ADDR]))
+               goto add_doi_put_def;
+
        if (info->attrs[NLBL_MGMT_A_IPV4ADDR]) {
                struct in_addr *addr;
                struct in_addr *mask;
@@ -178,6 +208,7 @@ static int netlbl_mgmt_add_common(struct genl_info *info,
                        goto add_free_addrmap;
                }
 
+               entry->family = AF_INET;
                entry->def.type = NETLBL_NLTYPE_ADDRSELECT;
                entry->def.addrsel = addrmap;
 #if IS_ENABLED(CONFIG_IPV6)
@@ -220,6 +251,8 @@ static int netlbl_mgmt_add_common(struct genl_info *info,
                map->list.mask = *mask;
                map->list.valid = 1;
                map->def.type = entry->def.type;
+               if (calipso)
+                       map->def.calipso = calipso;
 
                ret_val = netlbl_af6list_add(&map->list, &addrmap->list6);
                if (ret_val != 0) {
@@ -227,6 +260,7 @@ static int netlbl_mgmt_add_common(struct genl_info *info,
                        goto add_free_addrmap;
                }
 
+               entry->family = AF_INET6;
                entry->def.type = NETLBL_NLTYPE_ADDRSELECT;
                entry->def.addrsel = addrmap;
 #endif /* IPv6 */
@@ -242,6 +276,9 @@ add_free_addrmap:
        kfree(addrmap);
 add_doi_put_def:
        cipso_v4_doi_putdef(cipsov4);
+#if IS_ENABLED(CONFIG_IPV6)
+       calipso_doi_putdef(calipso);
+#endif
 add_free_domain:
        kfree(entry->domain);
 add_free_entry:
@@ -278,6 +315,10 @@ static int netlbl_mgmt_listentry(struct sk_buff *skb,
                        return ret_val;
        }
 
+       ret_val = nla_put_u16(skb, NLBL_MGMT_A_FAMILY, entry->family);
+       if (ret_val != 0)
+               return ret_val;
+
        switch (entry->def.type) {
        case NETLBL_NLTYPE_ADDRSELECT:
                nla_a = nla_nest_start(skb, NLBL_MGMT_A_SELECTORLIST);
@@ -340,6 +381,15 @@ static int netlbl_mgmt_listentry(struct sk_buff *skb,
                        if (ret_val != 0)
                                return ret_val;
 
+                       switch (map6->def.type) {
+                       case NETLBL_NLTYPE_CALIPSO:
+                               ret_val = nla_put_u32(skb, NLBL_MGMT_A_CLPDOI,
+                                                     map6->def.calipso->doi);
+                               if (ret_val != 0)
+                                       return ret_val;
+                               break;
+                       }
+
                        nla_nest_end(skb, nla_b);
                }
 #endif /* IPv6 */
@@ -347,15 +397,25 @@ static int netlbl_mgmt_listentry(struct sk_buff *skb,
                nla_nest_end(skb, nla_a);
                break;
        case NETLBL_NLTYPE_UNLABELED:
-               ret_val = nla_put_u32(skb,NLBL_MGMT_A_PROTOCOL,entry->def.type);
+               ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
+                                     entry->def.type);
                break;
        case NETLBL_NLTYPE_CIPSOV4:
-               ret_val = nla_put_u32(skb,NLBL_MGMT_A_PROTOCOL,entry->def.type);
+               ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
+                                     entry->def.type);
                if (ret_val != 0)
                        return ret_val;
                ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
                                      entry->def.cipso->doi);
                break;
+       case NETLBL_NLTYPE_CALIPSO:
+               ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
+                                     entry->def.type);
+               if (ret_val != 0)
+                       return ret_val;
+               ret_val = nla_put_u32(skb, NLBL_MGMT_A_CLPDOI,
+                                     entry->def.calipso->doi);
+               break;
        }
 
        return ret_val;
@@ -418,7 +478,7 @@ static int netlbl_mgmt_remove(struct sk_buff *skb, struct genl_info *info)
        netlbl_netlink_auditinfo(skb, &audit_info);
 
        domain = nla_data(info->attrs[NLBL_MGMT_A_DOMAIN]);
-       return netlbl_domhsh_remove(domain, &audit_info);
+       return netlbl_domhsh_remove(domain, AF_UNSPEC, &audit_info);
 }
 
 /**
@@ -536,7 +596,7 @@ static int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info)
 
        netlbl_netlink_auditinfo(skb, &audit_info);
 
-       return netlbl_domhsh_remove_default(&audit_info);
+       return netlbl_domhsh_remove_default(AF_UNSPEC, &audit_info);
 }
 
 /**
@@ -556,6 +616,12 @@ static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info)
        struct sk_buff *ans_skb = NULL;
        void *data;
        struct netlbl_dom_map *entry;
+       u16 family;
+
+       if (info->attrs[NLBL_MGMT_A_FAMILY])
+               family = nla_get_u16(info->attrs[NLBL_MGMT_A_FAMILY]);
+       else
+               family = AF_INET;
 
        ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (ans_skb == NULL)
@@ -566,7 +632,7 @@ static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info)
                goto listdef_failure;
 
        rcu_read_lock();
-       entry = netlbl_domhsh_getentry(NULL);
+       entry = netlbl_domhsh_getentry(NULL, family);
        if (entry == NULL) {
                ret_val = -ENOENT;
                goto listdef_failure_lock;
@@ -651,6 +717,15 @@ static int netlbl_mgmt_protocols(struct sk_buff *skb,
                        goto protocols_return;
                protos_sent++;
        }
+#if IS_ENABLED(CONFIG_IPV6)
+       if (protos_sent == 2) {
+               if (netlbl_mgmt_protocols_cb(skb,
+                                            cb,
+                                            NETLBL_NLTYPE_CALIPSO) < 0)
+                       goto protocols_return;
+               protos_sent++;
+       }
+#endif
 
 protocols_return:
        cb->args[0] = protos_sent;
index 8b6e1ab62b485729dca126c8ab6a82671623d1fe..ea01e42bca783a8e02b93855a2e35f2b3bb3ba10 100644 (file)
  *
  *     NLBL_MGMT_A_CV4DOI
  *
- *   If using NETLBL_NLTYPE_UNLABELED no other attributes are required.
+ *   If using NETLBL_NLTYPE_UNLABELED no other attributes are required,
+ *   however the following attribute may optionally be sent:
+ *
+ *     NLBL_MGMT_A_FAMILY
  *
  * o REMOVE:
  *   Sent by an application to remove a domain mapping from the NetLabel
@@ -77,6 +80,7 @@
  *   Required attributes:
  *
  *     NLBL_MGMT_A_DOMAIN
+ *     NLBL_MGMT_A_FAMILY
  *
  *   If the IP address selectors are not used the following attribute is
  *   required:
  *
  *     NLBL_MGMT_A_CV4DOI
  *
- *   If using NETLBL_NLTYPE_UNLABELED no other attributes are required.
+ *   If using NETLBL_NLTYPE_UNLABELED no other attributes are required,
+ *   however the following attribute may optionally be sent:
+ *
+ *     NLBL_MGMT_A_FAMILY
  *
  * o REMOVEDEF:
  *   Sent by an application to remove the default domain mapping from the
  * o LISTDEF:
  *   This message can be sent either from an application or by the kernel in
  *   response to an application generated LISTDEF message.  When sent by an
- *   application there is no payload.  On success the kernel should send a
- *   response using the following format.
+ *   application there may be an optional payload.
  *
- *   If the IP address selectors are not used the following attribute is
+ *     NLBL_MGMT_A_FAMILY
+ *
+ *   On success the kernel should send a response using the following format:
+ *
+ *   If the IP address selectors are not used the following attributes are
  *   required:
  *
  *     NLBL_MGMT_A_PROTOCOL
+ *     NLBL_MGMT_A_FAMILY
  *
  *   If the IP address selectors are used then the following attritbute is
  *   required:
@@ -209,6 +220,12 @@ enum {
        /* (NLA_NESTED)
         * the selector list, there must be at least one
         * NLBL_MGMT_A_ADDRSELECTOR attribute */
+       NLBL_MGMT_A_FAMILY,
+       /* (NLA_U16)
+        * The address family */
+       NLBL_MGMT_A_CLPDOI,
+       /* (NLA_U32)
+        * the CALIPSO DOI value */
        __NLBL_MGMT_A_MAX,
 };
 #define NLBL_MGMT_A_MAX (__NLBL_MGMT_A_MAX - 1)
index 9eaa9a1e862960e7d37c812d17f16e6680772429..4528cff9138b5499f7577bf2801dac8f85e0fcda 100644 (file)
@@ -116,8 +116,8 @@ struct netlbl_unlhsh_walk_arg {
 static DEFINE_SPINLOCK(netlbl_unlhsh_lock);
 #define netlbl_unlhsh_rcu_deref(p) \
        rcu_dereference_check(p, lockdep_is_held(&netlbl_unlhsh_lock))
-static struct netlbl_unlhsh_tbl *netlbl_unlhsh;
-static struct netlbl_unlhsh_iface *netlbl_unlhsh_def;
+static struct netlbl_unlhsh_tbl __rcu *netlbl_unlhsh;
+static struct netlbl_unlhsh_iface __rcu *netlbl_unlhsh_def;
 
 /* Accept unlabeled packets flag */
 static u8 netlabel_unlabel_acceptflg;
@@ -1537,6 +1537,7 @@ int __init netlbl_unlabel_defconf(void)
        entry = kzalloc(sizeof(*entry), GFP_KERNEL);
        if (entry == NULL)
                return -ENOMEM;
+       entry->family = AF_UNSPEC;
        entry->def.type = NETLBL_NLTYPE_UNLABELED;
        ret_val = netlbl_domhsh_add_default(entry, &audit_info);
        if (ret_val != 0)
index adf8b7900da2ac131c8ebc61d60d1373493d30c4..58495f44c62a605ea7337056ac96a1635a8225c2 100644 (file)
@@ -44,6 +44,7 @@
 #include "netlabel_mgmt.h"
 #include "netlabel_unlabeled.h"
 #include "netlabel_cipso_v4.h"
+#include "netlabel_calipso.h"
 #include "netlabel_user.h"
 
 /*
@@ -71,6 +72,10 @@ int __init netlbl_netlink_init(void)
        if (ret_val != 0)
                return ret_val;
 
+       ret_val = netlbl_calipso_genl_init();
+       if (ret_val != 0)
+               return ret_val;
+
        return netlbl_unlabel_genl_init();
 }
 
index ed98c1fc3de1428560ea370413084102af9dff7f..46a71c701e7c44f5aa5fcc69e8571382434e385b 100644 (file)
@@ -46,7 +46,7 @@ static int net_ctl_permissions(struct ctl_table_header *head,
        kgid_t root_gid = make_kgid(net->user_ns, 0);
 
        /* Allow network administrator to have same access as root. */
-       if (ns_capable(net->user_ns, CAP_NET_ADMIN) ||
+       if (ns_capable_noaudit(net->user_ns, CAP_NET_ADMIN) ||
            uid_eq(root_uid, current_euid())) {
                int mode = (table->mode >> 6) & 7;
                return (mode << 6) | (mode << 3) | mode;
index 27a24571e96cbb456f219d7e24f1c7e5c787410f..85c405fcccb0cb08ec59efa262961e08bb3389f1 100644 (file)
@@ -92,4 +92,11 @@ config SAMPLE_CONNECTOR
          with it.
          See also Documentation/connector/connector.txt
 
+config SAMPLE_SECCOMP
+       tristate "Build seccomp sample code -- loadable modules only"
+       depends on SECCOMP_FILTER && m
+       help
+         Build samples of seccomp filters using various methods of
+         BPF filter construction.
+
 endif # SAMPLES
index 1b4e4b8f5e477de1d6e5fb6f172f5ad50d7402f6..ae7ff6f24f36ca41aa430e63c60768ba1553607f 100644 (file)
@@ -1,7 +1,7 @@
 # kbuild trick to avoid linker error. Can be omitted if a module is built.
 obj- := dummy.o
 
-hostprogs-$(CONFIG_SECCOMP_FILTER) := bpf-fancy dropper bpf-direct
+hostprogs-$(CONFIG_SAMPLE_SECCOMP) := bpf-fancy dropper bpf-direct
 
 HOSTCFLAGS_bpf-fancy.o += -I$(objtree)/usr/include
 HOSTCFLAGS_bpf-fancy.o += -idirafter $(objtree)/include
index d912d5a56a5ebf05f3e1f2fb0f93cfd028907167..53af6dc3e6c1779fbde3c5a641a880f52aa768e5 100755 (executable)
@@ -1,6 +1,6 @@
 /* Sign a module file using the given key.
  *
- * Copyright Â© 2014-2015 Red Hat, Inc. All Rights Reserved.
+ * Copyright Â© 2014-2016 Red Hat, Inc. All Rights Reserved.
  * Copyright Â© 2015      Intel Corporation.
  * Copyright Â© 2016      Hewlett Packard Enterprise Development LP
  *
@@ -167,19 +167,37 @@ static EVP_PKEY *read_private_key(const char *private_key_name)
 
 static X509 *read_x509(const char *x509_name)
 {
+       unsigned char buf[2];
        X509 *x509;
        BIO *b;
+       int n;
 
        b = BIO_new_file(x509_name, "rb");
        ERR(!b, "%s", x509_name);
-       x509 = d2i_X509_bio(b, NULL); /* Binary encoded X.509 */
-       if (!x509) {
-               ERR(BIO_reset(b) != 1, "%s", x509_name);
-               x509 = PEM_read_bio_X509(b, NULL, NULL,
-                                        NULL); /* PEM encoded X.509 */
-               if (x509)
-                       drain_openssl_errors();
+
+       /* Look at the first two bytes of the file to determine the encoding */
+       n = BIO_read(b, buf, 2);
+       if (n != 2) {
+               if (BIO_should_retry(b)) {
+                       fprintf(stderr, "%s: Read wanted retry\n", x509_name);
+                       exit(1);
+               }
+               if (n >= 0) {
+                       fprintf(stderr, "%s: Short read\n", x509_name);
+                       exit(1);
+               }
+               ERR(1, "%s", x509_name);
        }
+
+       ERR(BIO_reset(b) != 0, "%s", x509_name);
+
+       if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84)
+               /* Assume raw DER encoded X.509 */
+               x509 = d2i_X509_bio(b, NULL);
+       else
+               /* Assume PEM encoded X.509 */
+               x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
+
        BIO_free(b);
        ERR(!x509, "%s", x509_name);
 
index 232469baa94f2f17e686f2cf702a56424eda8176..be5e9414a29574c80aca1cd9cab242a99ce77508 100644 (file)
@@ -31,13 +31,26 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE
          If you are unsure how to answer this question, answer 1.
 
 config SECURITY_APPARMOR_HASH
-       bool "SHA1 hash of loaded profiles"
+       bool "Enable introspection of sha1 hashes for loaded profiles"
        depends on SECURITY_APPARMOR
        select CRYPTO
        select CRYPTO_SHA1
        default y
 
        help
-         This option selects whether sha1 hashing is done against loaded
-          profiles and exported for inspection to user space via the apparmor
-          filesystem.
+         This option selects whether introspection of loaded policy
+         is available to userspace via the apparmor filesystem.
+
+config SECURITY_APPARMOR_HASH_DEFAULT
+       bool "Enable policy hash introspection by default"
+       depends on SECURITY_APPARMOR_HASH
+       default y
+
+       help
+         This option selects whether sha1 hashing of loaded policy
+        is enabled by default. The generation of sha1 hashes for
+        loaded policy provide system administrators a quick way
+        to verify that policy in the kernel matches what is expected,
+        however it can slow down policy load on some devices. In
+        these cases policy hashing can be disabled by default and
+        enabled only if needed.
index ad4fa49ad1db23a70dfa16751a9c7f8ec006e854..729e595119eded446298c84386dd4ba1511aafd1 100644 (file)
@@ -331,6 +331,7 @@ static int aa_fs_seq_hash_show(struct seq_file *seq, void *v)
                        seq_printf(seq, "%.2x", profile->hash[i]);
                seq_puts(seq, "\n");
        }
+       aa_put_profile(profile);
 
        return 0;
 }
@@ -379,6 +380,8 @@ void __aa_fs_profile_migrate_dents(struct aa_profile *old,
 
        for (i = 0; i < AAFS_PROF_SIZEOF; i++) {
                new->dents[i] = old->dents[i];
+               if (new->dents[i])
+                       new->dents[i]->d_inode->i_mtime = CURRENT_TIME;
                old->dents[i] = NULL;
        }
 }
@@ -550,8 +553,6 @@ fail2:
 }
 
 
-#define list_entry_next(pos, member) \
-       list_entry(pos->member.next, typeof(*pos), member)
 #define list_entry_is_head(pos, head, member) (&pos->member == (head))
 
 /**
@@ -582,7 +583,7 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root,
        parent = ns->parent;
        while (ns != root) {
                mutex_unlock(&ns->lock);
-               next = list_entry_next(ns, base.list);
+               next = list_next_entry(ns, base.list);
                if (!list_entry_is_head(next, &parent->sub_ns, base.list)) {
                        mutex_lock(&next->lock);
                        return next;
@@ -636,7 +637,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p)
        parent = rcu_dereference_protected(p->parent,
                                           mutex_is_locked(&p->ns->lock));
        while (parent) {
-               p = list_entry_next(p, base.list);
+               p = list_next_entry(p, base.list);
                if (!list_entry_is_head(p, &parent->base.profiles, base.list))
                        return p;
                p = parent;
@@ -645,7 +646,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p)
        }
 
        /* is next another profile in the namespace */
-       p = list_entry_next(p, base.list);
+       p = list_next_entry(p, base.list);
        if (!list_entry_is_head(p, &ns->base.profiles, base.list))
                return p;
 
index 89c78658031f10bfc0527030ed970aebe3ff5471..3a7f1da1425ec34f944b50a70d03963e4db4a1dd 100644 (file)
@@ -200,7 +200,8 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp,
 
        if (sa->aad->type == AUDIT_APPARMOR_KILL)
                (void)send_sig_info(SIGKILL, NULL,
-                                   sa->u.tsk ?  sa->u.tsk : current);
+                       sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ?
+                                   sa->u.tsk : current);
 
        if (sa->aad->type == AUDIT_APPARMOR_ALLOWED)
                return complain_error(sa->aad->error);
index 532471d0b3a0facd6c7cb70a930e4028ca6d0761..b75dab0df1cb2a7f60933ac7150933a6966a7017 100644 (file)
@@ -39,6 +39,9 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
        int error = -ENOMEM;
        u32 le32_version = cpu_to_le32(version);
 
+       if (!aa_g_hash_policy)
+               return 0;
+
        if (!apparmor_tfm)
                return 0;
 
index dc0027b28b049fa86b83543e2ce0a0640e591c89..fc3036b34e5128cc73910eb6e3b2bbd2d94d9e50 100644 (file)
@@ -346,7 +346,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
                file_inode(bprm->file)->i_uid,
                file_inode(bprm->file)->i_mode
        };
-       const char *name = NULL, *target = NULL, *info = NULL;
+       const char *name = NULL, *info = NULL;
        int error = 0;
 
        if (bprm->cred_prepared)
@@ -399,6 +399,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
        if (cxt->onexec) {
                struct file_perms cp;
                info = "change_profile onexec";
+               new_profile = aa_get_newest_profile(cxt->onexec);
                if (!(perms.allow & AA_MAY_ONEXEC))
                        goto audit;
 
@@ -413,7 +414,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
 
                if (!(cp.allow & AA_MAY_ONEXEC))
                        goto audit;
-               new_profile = aa_get_newest_profile(cxt->onexec);
                goto apply;
        }
 
@@ -433,7 +433,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
                                new_profile = aa_get_newest_profile(ns->unconfined);
                                info = "ux fallback";
                        } else {
-                               error = -ENOENT;
+                               error = -EACCES;
                                info = "profile not found";
                                /* remove MAY_EXEC to audit as failure */
                                perms.allow &= ~MAY_EXEC;
@@ -445,10 +445,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
                if (!new_profile) {
                        error = -ENOMEM;
                        info = "could not create null profile";
-               } else {
+               } else
                        error = -EACCES;
-                       target = new_profile->base.hname;
-               }
                perms.xindex |= AA_X_UNSAFE;
        } else
                /* fail exec */
@@ -459,7 +457,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
         * fail the exec.
         */
        if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) {
-               aa_put_profile(new_profile);
                error = -EPERM;
                goto cleanup;
        }
@@ -474,10 +471,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
 
        if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
                error = may_change_ptraced_domain(new_profile);
-               if (error) {
-                       aa_put_profile(new_profile);
+               if (error)
                        goto audit;
-               }
        }
 
        /* Determine if secure exec is needed.
@@ -498,7 +493,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
                bprm->unsafe |= AA_SECURE_X_NEEDED;
        }
 apply:
-       target = new_profile->base.hname;
        /* when transitioning profiles clear unsafe personality bits */
        bprm->per_clear |= PER_CLEAR_ON_SETID;
 
@@ -506,15 +500,19 @@ x_clear:
        aa_put_profile(cxt->profile);
        /* transfer new profile reference will be released when cxt is freed */
        cxt->profile = new_profile;
+       new_profile = NULL;
 
        /* clear out all temporary/transitional state from the context */
        aa_clear_task_cxt_trans(cxt);
 
 audit:
        error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC,
-                             name, target, cond.uid, info, error);
+                             name,
+                             new_profile ? new_profile->base.hname : NULL,
+                             cond.uid, info, error);
 
 cleanup:
+       aa_put_profile(new_profile);
        aa_put_profile(profile);
        kfree(buffer);
 
index d186674f973a37df1aadc38c0a313042163f099d..4d2af4b01033fd40623172c988985fece9b4abde 100644 (file)
@@ -110,7 +110,8 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms,
        int type = AUDIT_APPARMOR_AUTO;
        struct common_audit_data sa;
        struct apparmor_audit_data aad = {0,};
-       sa.type = LSM_AUDIT_DATA_NONE;
+       sa.type = LSM_AUDIT_DATA_TASK;
+       sa.u.tsk = NULL;
        sa.aad = &aad;
        aad.op = op,
        aad.fs.request = request;
index e4ea6266386662c2c88445743ff2e107b665230a..5d721e990876d8b282867523283e4acda7c22930 100644 (file)
@@ -37,6 +37,7 @@
 extern enum audit_mode aa_g_audit;
 extern bool aa_g_audit_header;
 extern bool aa_g_debug;
+extern bool aa_g_hash_policy;
 extern bool aa_g_lock_policy;
 extern bool aa_g_logsyscall;
 extern bool aa_g_paranoid_load;
index 001c43aa04065b99bdc3f6484b85213de3a2bf7e..a1c04fe8679022dd21e9eb468947b314de58452f 100644 (file)
@@ -62,6 +62,7 @@ struct table_set_header {
 #define YYTD_ID_ACCEPT2 6
 #define YYTD_ID_NXT    7
 #define YYTD_ID_TSIZE  8
+#define YYTD_ID_MAX    8
 
 #define YYTD_DATA8     1
 #define YYTD_DATA16    2
index c28b0f20ab53ed69da7c1e6b8c60a8099370a4f8..52275f040a5f685e4c20b4d829d47c4c8223644f 100644 (file)
@@ -403,6 +403,8 @@ static inline int AUDIT_MODE(struct aa_profile *profile)
        return profile->audit;
 }
 
+bool policy_view_capable(void);
+bool policy_admin_capable(void);
 bool aa_may_manage_policy(int op);
 
 #endif /* __AA_POLICY_H */
index 7798e1608f4f4bb429c94017e2c6f73b07a7afc9..41b8cb115801724172ce48cbb3ac6b3d96a8d6f6 100644 (file)
@@ -529,7 +529,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
        if (!*args)
                goto out;
 
-       arg_size = size - (args - (char *) value);
+       arg_size = size - (args - (largs ? largs : (char *) value));
        if (strcmp(name, "current") == 0) {
                if (strcmp(command, "changehat") == 0) {
                        error = aa_setprocattr_changehat(args, arg_size,
@@ -671,6 +671,12 @@ enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE;
 module_param_call(mode, param_set_mode, param_get_mode,
                  &aa_g_profile_mode, S_IRUSR | S_IWUSR);
 
+#ifdef CONFIG_SECURITY_APPARMOR_HASH
+/* whether policy verification hashing is enabled */
+bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT);
+module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR);
+#endif
+
 /* Debug mode */
 bool aa_g_debug;
 module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR);
@@ -728,51 +734,49 @@ __setup("apparmor=", apparmor_enabled_setup);
 /* set global flag turning off the ability to load policy */
 static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
-       if (aa_g_lock_policy)
-               return -EACCES;
        return param_set_bool(val, kp);
 }
 
 static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_view_capable())
                return -EPERM;
        return param_get_bool(buffer, kp);
 }
 
 static int param_set_aabool(const char *val, const struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
        return param_set_bool(val, kp);
 }
 
 static int param_get_aabool(char *buffer, const struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_view_capable())
                return -EPERM;
        return param_get_bool(buffer, kp);
 }
 
 static int param_set_aauint(const char *val, const struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
        return param_set_uint(val, kp);
 }
 
 static int param_get_aauint(char *buffer, const struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_view_capable())
                return -EPERM;
        return param_get_uint(buffer, kp);
 }
 
 static int param_get_audit(char *buffer, struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_view_capable())
                return -EPERM;
 
        if (!apparmor_enabled)
@@ -784,7 +788,7 @@ static int param_get_audit(char *buffer, struct kernel_param *kp)
 static int param_set_audit(const char *val, struct kernel_param *kp)
 {
        int i;
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
 
        if (!apparmor_enabled)
@@ -805,7 +809,7 @@ static int param_set_audit(const char *val, struct kernel_param *kp)
 
 static int param_get_mode(char *buffer, struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
 
        if (!apparmor_enabled)
@@ -817,7 +821,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp)
 static int param_set_mode(const char *val, struct kernel_param *kp)
 {
        int i;
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
 
        if (!apparmor_enabled)
index 727eb4200d5c922d8818a6f0a84ace38fd759306..3f900fcca8fb583b64e90ca7e52e451a600828b1 100644 (file)
@@ -47,6 +47,8 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
         * it every time we use td_id as an index
         */
        th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1;
+       if (th.td_id > YYTD_ID_MAX)
+               goto out;
        th.td_flags = be16_to_cpu(*(u16 *) (blob + 2));
        th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8));
        blob += sizeof(struct table_header);
@@ -61,7 +63,9 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
 
        table = kvzalloc(tsize);
        if (table) {
-               *table = th;
+               table->td_id = th.td_id;
+               table->td_flags = th.td_flags;
+               table->td_lolen = th.td_lolen;
                if (th.td_flags == YYTD_DATA8)
                        UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
                                     u8, byte_to_byte);
@@ -73,14 +77,14 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
                                     u32, be32_to_cpu);
                else
                        goto fail;
+               /* if table was vmalloced make sure the page tables are synced
+                * before it is used, as it goes live to all cpus.
+                */
+               if (is_vmalloc_addr(table))
+                       vm_unmap_aliases();
        }
 
 out:
-       /* if table was vmalloced make sure the page tables are synced
-        * before it is used, as it goes live to all cpus.
-        */
-       if (is_vmalloc_addr(table))
-               vm_unmap_aliases();
        return table;
 fail:
        kvfree(table);
index edddc026406b4ef47b53e163e039d1d83f741fe2..a8fc7d08c144bb542ac65cfd8312945c00a1ef50 100644 (file)
@@ -25,7 +25,6 @@
 #include "include/path.h"
 #include "include/policy.h"
 
-
 /* modified from dcache.c */
 static int prepend(char **buffer, int buflen, const char *str, int namelen)
 {
@@ -39,6 +38,38 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen)
 
 #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT)
 
+/* If the path is not connected to the expected root,
+ * check if it is a sysctl and handle specially else remove any
+ * leading / that __d_path may have returned.
+ * Unless
+ *     specifically directed to connect the path,
+ * OR
+ *     if in a chroot and doing chroot relative paths and the path
+ *     resolves to the namespace root (would be connected outside
+ *     of chroot) and specifically directed to connect paths to
+ *     namespace root.
+ */
+static int disconnect(const struct path *path, char *buf, char **name,
+                     int flags)
+{
+       int error = 0;
+
+       if (!(flags & PATH_CONNECT_PATH) &&
+           !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) &&
+             our_mnt(path->mnt))) {
+               /* disconnected path, don't return pathname starting
+                * with '/'
+                */
+               error = -EACCES;
+               if (**name == '/')
+                       *name = *name + 1;
+       } else if (**name != '/')
+               /* CONNECT_PATH with missing root */
+               error = prepend(name, *name - buf, "/", 1);
+
+       return error;
+}
+
 /**
  * d_namespace_path - lookup a name associated with a given path
  * @path: path to lookup  (NOT NULL)
@@ -74,7 +105,8 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen,
                         * control instead of hard coded /proc
                         */
                        return prepend(name, *name - buf, "/proc", 5);
-               }
+               } else
+                       return disconnect(path, buf, name, flags);
                return 0;
        }
 
@@ -120,29 +152,8 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen,
                        goto out;
        }
 
-       /* If the path is not connected to the expected root,
-        * check if it is a sysctl and handle specially else remove any
-        * leading / that __d_path may have returned.
-        * Unless
-        *     specifically directed to connect the path,
-        * OR
-        *     if in a chroot and doing chroot relative paths and the path
-        *     resolves to the namespace root (would be connected outside
-        *     of chroot) and specifically directed to connect paths to
-        *     namespace root.
-        */
-       if (!connected) {
-               if (!(flags & PATH_CONNECT_PATH) &&
-                          !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) &&
-                            our_mnt(path->mnt))) {
-                       /* disconnected path, don't return pathname starting
-                        * with '/'
-                        */
-                       error = -EACCES;
-                       if (*res == '/')
-                               *name = res + 1;
-               }
-       }
+       if (!connected)
+               error = disconnect(path, buf, name, flags);
 
 out:
        return error;
index 705c2879d3a94a79e10d1190468483c2f9d7192a..179e68d7dc5f968e9d21e16cff26327ca0b7997a 100644 (file)
@@ -766,7 +766,9 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name)
        struct aa_profile *profile;
 
        rcu_read_lock();
-       profile = aa_get_profile(__find_child(&parent->base.profiles, name));
+       do {
+               profile = __find_child(&parent->base.profiles, name);
+       } while (profile && !aa_get_profile_not0(profile));
        rcu_read_unlock();
 
        /* refcount released by caller */
@@ -916,6 +918,22 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info,
                        &sa, NULL);
 }
 
+bool policy_view_capable(void)
+{
+       struct user_namespace *user_ns = current_user_ns();
+       bool response = false;
+
+       if (ns_capable(user_ns, CAP_MAC_ADMIN))
+               response = true;
+
+       return response;
+}
+
+bool policy_admin_capable(void)
+{
+       return policy_view_capable() && !aa_g_lock_policy;
+}
+
 /**
  * aa_may_manage_policy - can the current task manage policy
  * @op: the policy manipulation operation being done
@@ -930,7 +948,7 @@ bool aa_may_manage_policy(int op)
                return 0;
        }
 
-       if (!capable(CAP_MAC_ADMIN)) {
+       if (!policy_admin_capable()) {
                audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES);
                return 0;
        }
@@ -1067,7 +1085,7 @@ static int __lookup_replace(struct aa_namespace *ns, const char *hname,
  */
 ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
 {
-       const char *ns_name, *name = NULL, *info = NULL;
+       const char *ns_name, *info = NULL;
        struct aa_namespace *ns = NULL;
        struct aa_load_ent *ent, *tmp;
        int op = OP_PROF_REPL;
@@ -1082,18 +1100,15 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
        /* released below */
        ns = aa_prepare_namespace(ns_name);
        if (!ns) {
-               info = "failed to prepare namespace";
-               error = -ENOMEM;
-               name = ns_name;
-               goto fail;
+               error = audit_policy(op, GFP_KERNEL, ns_name,
+                                    "failed to prepare namespace", -ENOMEM);
+               goto free;
        }
 
        mutex_lock(&ns->lock);
        /* setup parent and ns info */
        list_for_each_entry(ent, &lh, list) {
                struct aa_policy *policy;
-
-               name = ent->new->base.hname;
                error = __lookup_replace(ns, ent->new->base.hname, noreplace,
                                         &ent->old, &info);
                if (error)
@@ -1121,7 +1136,6 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
                        if (!p) {
                                error = -ENOENT;
                                info = "parent does not exist";
-                               name = ent->new->base.hname;
                                goto fail_lock;
                        }
                        rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
@@ -1163,7 +1177,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
                list_del_init(&ent->list);
                op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL;
 
-               audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error);
+               audit_policy(op, GFP_ATOMIC, ent->new->base.hname, NULL, error);
 
                if (ent->old) {
                        __replace_profile(ent->old, ent->new, 1);
@@ -1187,14 +1201,14 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
                        /* parent replaced in this atomic set? */
                        if (newest != parent) {
                                aa_get_profile(newest);
-                               aa_put_profile(parent);
                                rcu_assign_pointer(ent->new->parent, newest);
-                       } else
-                               aa_put_profile(newest);
+                               aa_put_profile(parent);
+                       }
                        /* aafs interface uses replacedby */
                        rcu_assign_pointer(ent->new->replacedby->profile,
                                           aa_get_profile(ent->new));
-                       __list_add_profile(&parent->base.profiles, ent->new);
+                       __list_add_profile(&newest->base.profiles, ent->new);
+                       aa_put_profile(newest);
                } else {
                        /* aafs interface uses replacedby */
                        rcu_assign_pointer(ent->new->replacedby->profile,
@@ -1214,9 +1228,22 @@ out:
 
 fail_lock:
        mutex_unlock(&ns->lock);
-fail:
-       error = audit_policy(op, GFP_KERNEL, name, info, error);
 
+       /* audit cause of failure */
+       op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
+       audit_policy(op, GFP_KERNEL, ent->new->base.hname, info, error);
+       /* audit status that rest of profiles in the atomic set failed too */
+       info = "valid profile in failed atomic policy load";
+       list_for_each_entry(tmp, &lh, list) {
+               if (tmp == ent) {
+                       info = "unchecked profile in failed atomic policy load";
+                       /* skip entry that caused failure */
+                       continue;
+               }
+               op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
+               audit_policy(op, GFP_KERNEL, tmp->new->base.hname, info, error);
+       }
+free:
        list_for_each_entry_safe(ent, tmp, &lh, list) {
                list_del_init(&ent->list);
                aa_load_ent_free(ent);
index a689f10930b5e825c4da751508e274d8715ef2b5..138120698f839e049a5cf2460572559f3529e337 100644 (file)
@@ -583,6 +583,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
                        error = PTR_ERR(profile->policy.dfa);
                        profile->policy.dfa = NULL;
                        goto fail;
+               } else if (!profile->policy.dfa) {
+                       error = -EPROTO;
+                       goto fail;
                }
                if (!unpack_u32(e, &profile->policy.start[0], "start"))
                        /* default start state */
@@ -676,7 +679,7 @@ static bool verify_xindex(int xindex, int table_size)
        int index, xtype;
        xtype = xindex & AA_X_TYPE_MASK;
        index = xindex & AA_X_INDEX_MASK;
-       if (xtype == AA_X_TABLE && index > table_size)
+       if (xtype == AA_X_TABLE && index >= table_size)
                return 0;
        return 1;
 }
@@ -776,7 +779,7 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
                        goto fail_profile;
 
                error = aa_calc_profile_hash(profile, e.version, start,
-                                            e.pos - start);
+                                                    e.pos - start);
                if (error)
                        goto fail_profile;
 
index 748bf0ca6c9f74572c601ebdaf033c6da224a20e..67a6072ead4b9ed8efe98bdc57080c615839c32a 100644 (file)
@@ -101,9 +101,11 @@ int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task,
        /* TODO: extend resource control to handle other (non current)
         * profiles.  AppArmor rules currently have the implicit assumption
         * that the task is setting the resource of a task confined with
-        * the same profile.
+        * the same profile or that the task setting the resource of another
+        * task has CAP_SYS_RESOURCE.
         */
-       if (profile != task_profile ||
+       if ((profile != task_profile &&
+            aa_capable(profile, CAP_SYS_RESOURCE, 1)) ||
            (profile->rlimits.mask & (1 << resource) &&
             new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max))
                error = -EACCES;
index 345b75997e4c2fdd6308dd76ab4e322388a4ae82..c710d22042f96c4e9d42a73943ce2d8f2689f89f 100644 (file)
@@ -79,6 +79,7 @@ static void iint_free(struct integrity_iint_cache *iint)
        iint->ima_bprm_status = INTEGRITY_UNKNOWN;
        iint->ima_read_status = INTEGRITY_UNKNOWN;
        iint->evm_status = INTEGRITY_UNKNOWN;
+       iint->measured_pcrs = 0;
        kmem_cache_free(iint_cache, iint);
 }
 
@@ -159,6 +160,7 @@ static void init_once(void *foo)
        iint->ima_bprm_status = INTEGRITY_UNKNOWN;
        iint->ima_read_status = INTEGRITY_UNKNOWN;
        iint->evm_status = INTEGRITY_UNKNOWN;
+       iint->measured_pcrs = 0;
 }
 
 static int __init integrity_iintcache_init(void)
index d3a939bf278115a2f4c94d7b12754b4da789d4c9..db25f54a04fe5bd28cdd90dbb82d794bca5e9262 100644 (file)
@@ -88,6 +88,7 @@ struct ima_template_desc {
 };
 
 struct ima_template_entry {
+       int pcr;
        u8 digest[TPM_DIGEST_SIZE];     /* sha1 or md5 measurement hash */
        struct ima_template_desc *template_desc; /* template descriptor */
        u32 template_data_len;
@@ -154,7 +155,8 @@ enum ima_hooks {
 };
 
 /* LIM API function definitions */
-int ima_get_action(struct inode *inode, int mask, enum ima_hooks func);
+int ima_get_action(struct inode *inode, int mask,
+                  enum ima_hooks func, int *pcr);
 int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
 int ima_collect_measurement(struct integrity_iint_cache *iint,
                            struct file *file, void *buf, loff_t size,
@@ -162,19 +164,20 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
 void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
                           const unsigned char *filename,
                           struct evm_ima_xattr_data *xattr_value,
-                          int xattr_len);
+                          int xattr_len, int pcr);
 void ima_audit_measurement(struct integrity_iint_cache *iint,
                           const unsigned char *filename);
 int ima_alloc_init_template(struct ima_event_data *event_data,
                            struct ima_template_entry **entry);
 int ima_store_template(struct ima_template_entry *entry, int violation,
-                      struct inode *inode, const unsigned char *filename);
+                      struct inode *inode,
+                      const unsigned char *filename, int pcr);
 void ima_free_template_entry(struct ima_template_entry *entry);
 const char *ima_d_path(const struct path *path, char **pathbuf);
 
 /* IMA policy related functions */
 int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
-                    int flags);
+                    int flags, int *pcr);
 void ima_init_policy(void);
 void ima_update_policy(void);
 void ima_update_policy_flag(void);
index 5a2218fe877aea2fca2088dba8c9bd7ab4bf8316..9df26a2b75ba692ee9d164fb14317779a117ca16 100644 (file)
@@ -87,7 +87,7 @@ out:
  */
 int ima_store_template(struct ima_template_entry *entry,
                       int violation, struct inode *inode,
-                      const unsigned char *filename)
+                      const unsigned char *filename, int pcr)
 {
        static const char op[] = "add_template_measure";
        static const char audit_cause[] = "hashing_error";
@@ -114,6 +114,7 @@ int ima_store_template(struct ima_template_entry *entry,
                }
                memcpy(entry->digest, hash.hdr.digest, hash.hdr.length);
        }
+       entry->pcr = pcr;
        result = ima_add_template_entry(entry, violation, op, inode, filename);
        return result;
 }
@@ -144,7 +145,8 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
                result = -ENOMEM;
                goto err_out;
        }
-       result = ima_store_template(entry, violation, inode, filename);
+       result = ima_store_template(entry, violation, inode,
+                                   filename, CONFIG_IMA_MEASURE_PCR_IDX);
        if (result < 0)
                ima_free_template_entry(entry);
 err_out:
@@ -157,6 +159,7 @@ err_out:
  * @inode: pointer to inode to measure
  * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
  * @func: caller identifier
+ * @pcr: pointer filled in if matched measure policy sets pcr=
  *
  * The policy is defined in terms of keypairs:
  *             subj=, obj=, type=, func=, mask=, fsmagic=
@@ -168,13 +171,13 @@ err_out:
  * Returns IMA_MEASURE, IMA_APPRAISE mask.
  *
  */
-int ima_get_action(struct inode *inode, int mask, enum ima_hooks func)
+int ima_get_action(struct inode *inode, int mask, enum ima_hooks func, int *pcr)
 {
        int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE;
 
        flags &= ima_policy_flag;
 
-       return ima_match_policy(inode, func, mask, flags);
+       return ima_match_policy(inode, func, mask, flags, pcr);
 }
 
 /*
@@ -252,7 +255,7 @@ out:
 void ima_store_measurement(struct integrity_iint_cache *iint,
                           struct file *file, const unsigned char *filename,
                           struct evm_ima_xattr_data *xattr_value,
-                          int xattr_len)
+                          int xattr_len, int pcr)
 {
        static const char op[] = "add_template_measure";
        static const char audit_cause[] = "ENOMEM";
@@ -263,7 +266,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
                                            xattr_len, NULL};
        int violation = 0;
 
-       if (iint->flags & IMA_MEASURED)
+       if (iint->measured_pcrs & (0x1 << pcr))
                return;
 
        result = ima_alloc_init_template(&event_data, &entry);
@@ -273,9 +276,11 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
                return;
        }
 
-       result = ima_store_template(entry, violation, inode, filename);
-       if (!result || result == -EEXIST)
+       result = ima_store_template(entry, violation, inode, filename, pcr);
+       if (!result || result == -EEXIST) {
                iint->flags |= IMA_MEASURED;
+               iint->measured_pcrs |= (0x1 << pcr);
+       }
        if (result < 0)
                ima_free_template_entry(entry);
 }
index 1bcbc12e03d94ff2cb2582e38fd4c83367d2d0db..4b9b4a4e1b89ced4167c3a318580b46ae23f2621 100644 (file)
@@ -41,7 +41,7 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
        if (!ima_appraise)
                return 0;
 
-       return ima_match_policy(inode, func, mask, IMA_APPRAISE);
+       return ima_match_policy(inode, func, mask, IMA_APPRAISE, NULL);
 }
 
 static int ima_fix_xattr(struct dentry *dentry,
@@ -370,6 +370,7 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig)
                return;
 
        iint->flags &= ~IMA_DONE_MASK;
+       iint->measured_pcrs = 0;
        if (digsig)
                iint->flags |= IMA_DIGSIG;
        return;
index 60d011aaec389dc26e77091e86455cd550892c64..c07a3844ea0a290efcee652b3212483e4e58f067 100644 (file)
@@ -123,7 +123,6 @@ static int ima_measurements_show(struct seq_file *m, void *v)
        struct ima_template_entry *e;
        char *template_name;
        int namelen;
-       u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX;
        bool is_ima_template = false;
        int i;
 
@@ -137,10 +136,10 @@ static int ima_measurements_show(struct seq_file *m, void *v)
 
        /*
         * 1st: PCRIndex
-        * PCR used is always the same (config option) in
-        * little-endian format
+        * PCR used defaults to the same (config option) in
+        * little-endian format, unless set in policy
         */
-       ima_putc(m, &pcr, sizeof(pcr));
+       ima_putc(m, &e->pcr, sizeof(e->pcr));
 
        /* 2nd: template digest */
        ima_putc(m, e->digest, TPM_DIGEST_SIZE);
@@ -219,7 +218,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
            e->template_desc->name : e->template_desc->fmt;
 
        /* 1st: PCR used (config option) */
-       seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX);
+       seq_printf(m, "%2d ", e->pcr);
 
        /* 2nd: SHA1 template hash */
        ima_print_digest(m, e->digest, TPM_DIGEST_SIZE);
index 5d679a6856162d738db8d258a06e171724565806..32912bd54eadda1d73bf24d30f0d7a6a9f849c42 100644 (file)
@@ -79,7 +79,8 @@ static int __init ima_add_boot_aggregate(void)
        }
 
        result = ima_store_template(entry, violation, NULL,
-                                   boot_aggregate_name);
+                                   boot_aggregate_name,
+                                   CONFIG_IMA_MEASURE_PCR_IDX);
        if (result < 0) {
                ima_free_template_entry(entry);
                audit_cause = "store_entry";
index 68b26c340acd46c908c43fe7cde58afd9aabb3e8..596ef616ac2183fe1eb65a4899b9941af48b67db 100644 (file)
@@ -125,6 +125,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
                if ((iint->version != inode->i_version) ||
                    (iint->flags & IMA_NEW_FILE)) {
                        iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
+                       iint->measured_pcrs = 0;
                        if (iint->flags & IMA_APPRAISE)
                                ima_update_xattr(iint, file);
                }
@@ -162,6 +163,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
        char *pathbuf = NULL;
        const char *pathname = NULL;
        int rc = -ENOMEM, action, must_appraise;
+       int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
        struct evm_ima_xattr_data *xattr_value = NULL;
        int xattr_len = 0;
        bool violation_check;
@@ -174,7 +176,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
         * bitmask based on the appraise/audit/measurement policy.
         * Included is the appraise submask.
         */
-       action = ima_get_action(inode, mask, func);
+       action = ima_get_action(inode, mask, func, &pcr);
        violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
                           (ima_policy_flag & IMA_MEASURE));
        if (!action && !violation_check)
@@ -209,7 +211,11 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
         */
        iint->flags |= action;
        action &= IMA_DO_MASK;
-       action &= ~((iint->flags & IMA_DONE_MASK) >> 1);
+       action &= ~((iint->flags & (IMA_DONE_MASK ^ IMA_MEASURED)) >> 1);
+
+       /* If target pcr is already measured, unset IMA_MEASURE action */
+       if ((action & IMA_MEASURE) && (iint->measured_pcrs & (0x1 << pcr)))
+               action ^= IMA_MEASURE;
 
        /* Nothing to do, just return existing appraised status */
        if (!action) {
@@ -238,7 +244,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
 
        if (action & IMA_MEASURE)
                ima_store_measurement(iint, file, pathname,
-                                     xattr_value, xattr_len);
+                                     xattr_value, xattr_len, pcr);
        if (action & IMA_APPRAISE_SUBMASK)
                rc = ima_appraise_measurement(func, iint, file, pathname,
                                              xattr_value, xattr_len, opened);
index 0f887a564a29f982f6b3a8483b3f730b0a71098d..aed47b777a57fbf495f14fa9038dbfb1759dd33a 100644 (file)
@@ -32,6 +32,7 @@
 #define IMA_FSUUID     0x0020
 #define IMA_INMASK     0x0040
 #define IMA_EUID       0x0080
+#define IMA_PCR                0x0100
 
 #define UNKNOWN                0
 #define MEASURE                0x0001  /* same as IMA_MEASURE */
@@ -40,6 +41,9 @@
 #define DONT_APPRAISE  0x0008
 #define AUDIT          0x0040
 
+#define INVALID_PCR(a) (((a) < 0) || \
+       (a) >= (FIELD_SIZEOF(struct integrity_iint_cache, measured_pcrs) * 8))
+
 int ima_policy_flag;
 static int temp_ima_appraise;
 
@@ -60,6 +64,7 @@ struct ima_rule_entry {
        u8 fsuuid[16];
        kuid_t uid;
        kuid_t fowner;
+       int pcr;
        struct {
                void *rule;     /* LSM file metadata specific */
                void *args_p;   /* audit value */
@@ -319,6 +324,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
  * @inode: pointer to an inode for which the policy decision is being made
  * @func: IMA hook identifier
  * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
+ * @pcr: set the pcr to extend
  *
  * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
  * conditions.
@@ -328,7 +334,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
  * than writes so ima_match_policy() is classical RCU candidate.
  */
 int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
-                    int flags)
+                    int flags, int *pcr)
 {
        struct ima_rule_entry *entry;
        int action = 0, actmask = flags | (flags << 1);
@@ -353,6 +359,9 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
                else
                        actmask &= ~(entry->action | entry->action >> 1);
 
+               if ((pcr) && (entry->flags & IMA_PCR))
+                       *pcr = entry->pcr;
+
                if (!actmask)
                        break;
        }
@@ -478,7 +487,8 @@ enum {
        Opt_subj_user, Opt_subj_role, Opt_subj_type,
        Opt_func, Opt_mask, Opt_fsmagic,
        Opt_fsuuid, Opt_uid, Opt_euid, Opt_fowner,
-       Opt_appraise_type, Opt_permit_directio
+       Opt_appraise_type, Opt_permit_directio,
+       Opt_pcr
 };
 
 static match_table_t policy_tokens = {
@@ -502,6 +512,7 @@ static match_table_t policy_tokens = {
        {Opt_fowner, "fowner=%s"},
        {Opt_appraise_type, "appraise_type=%s"},
        {Opt_permit_directio, "permit_directio"},
+       {Opt_pcr, "pcr=%s"},
        {Opt_err, NULL}
 };
 
@@ -773,6 +784,20 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
                        break;
                case Opt_permit_directio:
                        entry->flags |= IMA_PERMIT_DIRECTIO;
+                       break;
+               case Opt_pcr:
+                       if (entry->action != MEASURE) {
+                               result = -EINVAL;
+                               break;
+                       }
+                       ima_log_string(ab, "pcr", args[0].from);
+
+                       result = kstrtoint(args[0].from, 10, &entry->pcr);
+                       if (result || INVALID_PCR(entry->pcr))
+                               result = -EINVAL;
+                       else
+                               entry->flags |= IMA_PCR;
+
                        break;
                case Opt_err:
                        ima_log_string(ab, "UNKNOWN", p);
@@ -1011,6 +1036,12 @@ int ima_policy_show(struct seq_file *m, void *v)
                seq_puts(m, " ");
        }
 
+       if (entry->flags & IMA_PCR) {
+               snprintf(tbuf, sizeof(tbuf), "%d", entry->pcr);
+               seq_printf(m, pt(Opt_pcr), tbuf);
+               seq_puts(m, " ");
+       }
+
        if (entry->flags & IMA_FSUUID) {
                seq_printf(m, "fsuuid=%pU", entry->fsuuid);
                seq_puts(m, " ");
index 552705d5a78d0bbd518cb416e146846cf23df7e4..32f6ac0f96dfb0ed3c8d296c817e97946834c28e 100644 (file)
@@ -44,7 +44,8 @@ struct ima_h_table ima_htable = {
 static DEFINE_MUTEX(ima_extend_list_mutex);
 
 /* lookup up the digest value in the hash table, and return the entry */
-static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)
+static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value,
+                                                      int pcr)
 {
        struct ima_queue_entry *qe, *ret = NULL;
        unsigned int key;
@@ -54,7 +55,7 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)
        rcu_read_lock();
        hlist_for_each_entry_rcu(qe, &ima_htable.queue[key], hnext) {
                rc = memcmp(qe->entry->digest, digest_value, TPM_DIGEST_SIZE);
-               if (rc == 0) {
+               if ((rc == 0) && (qe->entry->pcr == pcr)) {
                        ret = qe;
                        break;
                }
@@ -89,14 +90,14 @@ static int ima_add_digest_entry(struct ima_template_entry *entry)
        return 0;
 }
 
-static int ima_pcr_extend(const u8 *hash)
+static int ima_pcr_extend(const u8 *hash, int pcr)
 {
        int result = 0;
 
        if (!ima_used_chip)
                return result;
 
-       result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash);
+       result = tpm_pcr_extend(TPM_ANY_NUM, pcr, hash);
        if (result != 0)
                pr_err("Error Communicating to TPM chip, result: %d\n", result);
        return result;
@@ -118,7 +119,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
        mutex_lock(&ima_extend_list_mutex);
        if (!violation) {
                memcpy(digest, entry->digest, sizeof(digest));
-               if (ima_lookup_digest_entry(digest)) {
+               if (ima_lookup_digest_entry(digest, entry->pcr)) {
                        audit_cause = "hash_exists";
                        result = -EEXIST;
                        goto out;
@@ -135,7 +136,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
        if (violation)          /* invalidate pcr */
                memset(digest, 0xff, sizeof(digest));
 
-       tpmresult = ima_pcr_extend(digest);
+       tpmresult = ima_pcr_extend(digest, entry->pcr);
        if (tpmresult != 0) {
                snprintf(tpm_audit_cause, AUDIT_CAUSE_LEN_MAX, "TPM_error(%d)",
                         tpmresult);
index 90bc57d796ec1e23495a27a76104f64e8d664e11..24520b4ef3b081887f15d660aac17371fe6c9788 100644 (file)
@@ -103,6 +103,7 @@ struct integrity_iint_cache {
        struct inode *inode;    /* back pointer to inode in question */
        u64 version;            /* track inode changes */
        unsigned long flags;
+       unsigned long measured_pcrs;
        enum integrity_status ima_file_status:4;
        enum integrity_status ima_mmap_status:4;
        enum integrity_status ima_bprm_status:4;
index 2ef45b319dd9f07961840db9722325d2d7290f67..1edc1f0a0ce2c47babfbc53ab88ed6f825bb82cf 100644 (file)
@@ -114,7 +114,7 @@ found:
                ret = key_link(key_ref_to_ptr(dest_ref), persistent);
                if (ret == 0) {
                        key_set_timeout(persistent, persistent_keyring_expiry);
-                       ret = persistent->serial;               
+                       ret = persistent->serial;
                }
        }
 
index a29e3554751e0862003b430ce116f52d182c27bb..43affcf10b22f2d0d4cc033a472b374596d4c578 100644 (file)
@@ -442,7 +442,7 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx,
 
        if (ctx->index_key.type == &key_type_keyring)
                return ERR_PTR(-EPERM);
-       
+
        user = key_user_lookup(current_fsuid());
        if (!user)
                return ERR_PTR(-ENOMEM);
index 709569305d329c6e7742e4e0d0513e9d9110b4a3..c4bb47db30ee53aba57746a83e6660be37c918b6 100644 (file)
@@ -700,18 +700,39 @@ int security_inode_killpriv(struct dentry *dentry)
 
 int security_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc)
 {
+       struct security_hook_list *hp;
+       int rc;
+
        if (unlikely(IS_PRIVATE(inode)))
                return -EOPNOTSUPP;
-       return call_int_hook(inode_getsecurity, -EOPNOTSUPP, inode, name,
-                               buffer, alloc);
+       /*
+        * Only one module will provide an attribute with a given name.
+        */
+       list_for_each_entry(hp, &security_hook_heads.inode_getsecurity, list) {
+               rc = hp->hook.inode_getsecurity(inode, name, buffer, alloc);
+               if (rc != -EOPNOTSUPP)
+                       return rc;
+       }
+       return -EOPNOTSUPP;
 }
 
 int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags)
 {
+       struct security_hook_list *hp;
+       int rc;
+
        if (unlikely(IS_PRIVATE(inode)))
                return -EOPNOTSUPP;
-       return call_int_hook(inode_setsecurity, -EOPNOTSUPP, inode, name,
-                               value, size, flags);
+       /*
+        * Only one module will provide an attribute with a given name.
+        */
+       list_for_each_entry(hp, &security_hook_heads.inode_setsecurity, list) {
+               rc = hp->hook.inode_setsecurity(inode, name, value, size,
+                                                               flags);
+               if (rc != -EOPNOTSUPP)
+                       return rc;
+       }
+       return -EOPNOTSUPP;
 }
 
 int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
index 19be9d39c7424e2c9d9c8615b00936aa6a014963..ec30880c4b98cfc15ef77d0cdcc916c346b57a8e 100644 (file)
@@ -4627,13 +4627,13 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
                err = selinux_inet_sys_rcv_skb(sock_net(sk), skb->skb_iif,
                                               addrp, family, peer_sid, &ad);
                if (err) {
-                       selinux_netlbl_err(skb, err, 0);
+                       selinux_netlbl_err(skb, family, err, 0);
                        return err;
                }
                err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,
                                   PEER__RECV, &ad);
                if (err) {
-                       selinux_netlbl_err(skb, err, 0);
+                       selinux_netlbl_err(skb, family, err, 0);
                        return err;
                }
        }
@@ -5001,7 +5001,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb,
                err = selinux_inet_sys_rcv_skb(dev_net(indev), indev->ifindex,
                                               addrp, family, peer_sid, &ad);
                if (err) {
-                       selinux_netlbl_err(skb, err, 1);
+                       selinux_netlbl_err(skb, family, err, 1);
                        return NF_DROP;
                }
        }
@@ -5087,6 +5087,15 @@ static unsigned int selinux_ipv4_output(void *priv,
        return selinux_ip_output(skb, PF_INET);
 }
 
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static unsigned int selinux_ipv6_output(void *priv,
+                                       struct sk_buff *skb,
+                                       const struct nf_hook_state *state)
+{
+       return selinux_ip_output(skb, PF_INET6);
+}
+#endif /* IPV6 */
+
 static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
                                                int ifindex,
                                                u16 family)
@@ -6321,6 +6330,12 @@ static struct nf_hook_ops selinux_nf_ops[] = {
                .hooknum =      NF_INET_FORWARD,
                .priority =     NF_IP6_PRI_SELINUX_FIRST,
        },
+       {
+               .hook =         selinux_ipv6_output,
+               .pf =           NFPROTO_IPV6,
+               .hooknum =      NF_INET_LOCAL_OUT,
+               .priority =     NF_IP6_PRI_SELINUX_FIRST,
+       },
 #endif /* IPV6 */
 };
 
index 8c59b8f150e888edf09195dedd1c687f9b0784fe..75686d53df074fcf542d5ae11518528148d4a0e7 100644 (file)
@@ -40,7 +40,8 @@
 #ifdef CONFIG_NETLABEL
 void selinux_netlbl_cache_invalidate(void);
 
-void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway);
+void selinux_netlbl_err(struct sk_buff *skb, u16 family, int error,
+                       int gateway);
 
 void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec);
 void selinux_netlbl_sk_security_reset(struct sk_security_struct *sksec);
@@ -72,6 +73,7 @@ static inline void selinux_netlbl_cache_invalidate(void)
 }
 
 static inline void selinux_netlbl_err(struct sk_buff *skb,
+                                     u16 family,
                                      int error,
                                      int gateway)
 {
index 1f989a539fd4abe08e3797b5d49f7b26ded4fe41..aaba6677ee2e62c84cfe7211803c1b077294d00c 100644 (file)
@@ -54,6 +54,7 @@
  *
  */
 static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
+                                          u16 family,
                                           struct netlbl_lsm_secattr *secattr,
                                           u32 *sid)
 {
@@ -63,7 +64,7 @@ static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
        if (rc == 0 &&
            (secattr->flags & NETLBL_SECATTR_CACHEABLE) &&
            (secattr->flags & NETLBL_SECATTR_CACHE))
-               netlbl_cache_add(skb, secattr);
+               netlbl_cache_add(skb, family, secattr);
 
        return rc;
 }
@@ -151,9 +152,9 @@ void selinux_netlbl_cache_invalidate(void)
  * present on the packet, NetLabel is smart enough to only act when it should.
  *
  */
-void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway)
+void selinux_netlbl_err(struct sk_buff *skb, u16 family, int error, int gateway)
 {
-       netlbl_skbuff_err(skb, error, gateway);
+       netlbl_skbuff_err(skb, family, error, gateway);
 }
 
 /**
@@ -214,7 +215,8 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
        netlbl_secattr_init(&secattr);
        rc = netlbl_skbuff_getattr(skb, family, &secattr);
        if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
-               rc = selinux_netlbl_sidlookup_cached(skb, &secattr, sid);
+               rc = selinux_netlbl_sidlookup_cached(skb, family,
+                                                    &secattr, sid);
        else
                *sid = SECSID_NULL;
        *type = secattr.type;
@@ -284,7 +286,7 @@ int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family)
        int rc;
        struct netlbl_lsm_secattr secattr;
 
-       if (family != PF_INET)
+       if (family != PF_INET && family != PF_INET6)
                return 0;
 
        netlbl_secattr_init(&secattr);
@@ -333,7 +335,7 @@ int selinux_netlbl_socket_post_create(struct sock *sk, u16 family)
        struct sk_security_struct *sksec = sk->sk_security;
        struct netlbl_lsm_secattr *secattr;
 
-       if (family != PF_INET)
+       if (family != PF_INET && family != PF_INET6)
                return 0;
 
        secattr = selinux_netlbl_sock_genattr(sk);
@@ -382,7 +384,8 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
        netlbl_secattr_init(&secattr);
        rc = netlbl_skbuff_getattr(skb, family, &secattr);
        if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
-               rc = selinux_netlbl_sidlookup_cached(skb, &secattr, &nlbl_sid);
+               rc = selinux_netlbl_sidlookup_cached(skb, family,
+                                                    &secattr, &nlbl_sid);
        else
                nlbl_sid = SECINITSID_UNLABELED;
        netlbl_secattr_destroy(&secattr);
@@ -405,10 +408,25 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
                return 0;
 
        if (nlbl_sid != SECINITSID_UNLABELED)
-               netlbl_skbuff_err(skb, rc, 0);
+               netlbl_skbuff_err(skb, family, rc, 0);
        return rc;
 }
 
+/**
+ * selinux_netlbl_option - Is this a NetLabel option
+ * @level: the socket level or protocol
+ * @optname: the socket option name
+ *
+ * Description:
+ * Returns true if @level and @optname refer to a NetLabel option.
+ * Helper for selinux_netlbl_socket_setsockopt().
+ */
+static inline int selinux_netlbl_option(int level, int optname)
+{
+       return (level == IPPROTO_IP && optname == IP_OPTIONS) ||
+               (level == IPPROTO_IPV6 && optname == IPV6_HOPOPTS);
+}
+
 /**
  * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel
  * @sock: the socket
@@ -431,7 +449,7 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
        struct sk_security_struct *sksec = sk->sk_security;
        struct netlbl_lsm_secattr secattr;
 
-       if (level == IPPROTO_IP && optname == IP_OPTIONS &&
+       if (selinux_netlbl_option(level, optname) &&
            (sksec->nlbl_state == NLBL_LABELED ||
             sksec->nlbl_state == NLBL_CONNLABELED)) {
                netlbl_secattr_init(&secattr);
index 1b1fd27de632692dc307e5a72cf65898f0e245b3..0765c5b053b52ce44819292acbef59319ffe8a6f 100644 (file)
@@ -1347,7 +1347,7 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file,
 {
        char *page;
        ssize_t ret;
-       int new_value;
+       unsigned int new_value;
 
        ret = task_has_security(current, SECURITY__SETSECPARAM);
        if (ret)
index 57644b1dc42e1a38b5f1fd0d24ab75164b3bdbf1..894b6cdc11c5410591aaf46a3170eeea13dc444a 100644 (file)
@@ -165,7 +165,7 @@ int ebitmap_netlbl_import(struct ebitmap *ebmap,
                        e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC);
                        if (e_iter == NULL)
                                goto netlbl_import_failure;
-                       e_iter->startbit = offset & ~(EBITMAP_SIZE - 1);
+                       e_iter->startbit = offset - (offset % EBITMAP_SIZE);
                        if (e_prev == NULL)
                                ebmap->node = e_iter;
                        else
index 89df64672b8974ab2fec0151ecdc1d35576f14a5..082b20c78363c8e604f7d3b490ca898b4e8c4730 100644 (file)
@@ -543,7 +543,7 @@ static void type_attribute_bounds_av(struct context *scontext,
                                     struct av_decision *avd)
 {
        struct context lo_scontext;
-       struct context lo_tcontext;
+       struct context lo_tcontext, *tcontextp = tcontext;
        struct av_decision lo_avd;
        struct type_datum *source;
        struct type_datum *target;
@@ -553,67 +553,41 @@ static void type_attribute_bounds_av(struct context *scontext,
                                    scontext->type - 1);
        BUG_ON(!source);
 
+       if (!source->bounds)
+               return;
+
        target = flex_array_get_ptr(policydb.type_val_to_struct_array,
                                    tcontext->type - 1);
        BUG_ON(!target);
 
-       if (source->bounds) {
-               memset(&lo_avd, 0, sizeof(lo_avd));
-
-               memcpy(&lo_scontext, scontext, sizeof(lo_scontext));
-               lo_scontext.type = source->bounds;
+       memset(&lo_avd, 0, sizeof(lo_avd));
 
-               context_struct_compute_av(&lo_scontext,
-                                         tcontext,
-                                         tclass,
-                                         &lo_avd,
-                                         NULL);
-               if ((lo_avd.allowed & avd->allowed) == avd->allowed)
-                       return;         /* no masked permission */
-               masked = ~lo_avd.allowed & avd->allowed;
-       }
+       memcpy(&lo_scontext, scontext, sizeof(lo_scontext));
+       lo_scontext.type = source->bounds;
 
        if (target->bounds) {
-               memset(&lo_avd, 0, sizeof(lo_avd));
-
                memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext));
                lo_tcontext.type = target->bounds;
-
-               context_struct_compute_av(scontext,
-                                         &lo_tcontext,
-                                         tclass,
-                                         &lo_avd,
-                                         NULL);
-               if ((lo_avd.allowed & avd->allowed) == avd->allowed)
-                       return;         /* no masked permission */
-               masked = ~lo_avd.allowed & avd->allowed;
+               tcontextp = &lo_tcontext;
        }
 
-       if (source->bounds && target->bounds) {
-               memset(&lo_avd, 0, sizeof(lo_avd));
-               /*
-                * lo_scontext and lo_tcontext are already
-                * set up.
-                */
+       context_struct_compute_av(&lo_scontext,
+                                 tcontextp,
+                                 tclass,
+                                 &lo_avd,
+                                 NULL);
 
-               context_struct_compute_av(&lo_scontext,
-                                         &lo_tcontext,
-                                         tclass,
-                                         &lo_avd,
-                                         NULL);
-               if ((lo_avd.allowed & avd->allowed) == avd->allowed)
-                       return;         /* no masked permission */
-               masked = ~lo_avd.allowed & avd->allowed;
-       }
+       masked = ~lo_avd.allowed & avd->allowed;
 
-       if (masked) {
-               /* mask violated permissions */
-               avd->allowed &= ~masked;
+       if (likely(!masked))
+               return;         /* no masked permission */
 
-               /* audit masked permissions */
-               security_dump_masked_av(scontext, tcontext,
-                                       tclass, masked, "bounds");
-       }
+       /* mask violated permissions */
+       avd->allowed &= ~masked;
+
+       /* audit masked permissions */
+       security_dump_masked_av(scontext, tcontext,
+                               tclass, masked, "bounds");
 }
 
 /*
index b75634dbf53ba24f4e39be091f9474f697ccb0d9..87a9741b0d02d798e12235ec2d2c496494c3d142 100644 (file)
@@ -2255,6 +2255,9 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
        struct smack_known *tkp = smk_of_task_struct(p);
        int rc;
 
+       if (!sig)
+               return 0; /* null signal; existence test */
+
        smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
        smk_ad_setfield_u_tsk(&ad, p);
        /*
@@ -4020,7 +4023,7 @@ access_check:
                rc = smk_bu_note("IPv4 delivery", skp, ssp->smk_in,
                                        MAY_WRITE, rc);
                if (rc != 0)
-                       netlbl_skbuff_err(skb, rc, 0);
+                       netlbl_skbuff_err(skb, sk->sk_family, rc, 0);
                break;
 #if IS_ENABLED(CONFIG_IPV6)
        case PF_INET6:
index 986a6a75686843f4a6afeed9b6492650db79d055..540bc29e1b5a72ffaba56fab5ca4060a5d5705a1 100644 (file)
@@ -645,11 +645,6 @@ void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register)
                }
        }
        spin_unlock(&tomoyo_io_buffer_list_lock);
-       if (is_write) {
-               struct task_struct *task = kthread_create(tomoyo_gc_thread,
-                                                         NULL,
-                                                         "GC for TOMOYO");
-               if (!IS_ERR(task))
-                       wake_up_process(task);
-       }
+       if (is_write)
+               kthread_run(tomoyo_gc_thread, NULL, "GC for TOMOYO");
 }
index 2e58549b2f0211ac11747d35b71c05145e81fbe7..03f1fa495d743f4bb9032486eb42f201a33bc01d 100644 (file)
@@ -1021,8 +1021,8 @@ void tracer_stop(int sig)
 typedef void tracer_func_t(struct __test_metadata *_metadata,
                           pid_t tracee, int status, void *args);
 
-void tracer(struct __test_metadata *_metadata, int fd, pid_t tracee,
-           tracer_func_t tracer_func, void *args)
+void start_tracer(struct __test_metadata *_metadata, int fd, pid_t tracee,
+           tracer_func_t tracer_func, void *args, bool ptrace_syscall)
 {
        int ret = -1;
        struct sigaction action = {
@@ -1042,12 +1042,16 @@ void tracer(struct __test_metadata *_metadata, int fd, pid_t tracee,
        /* Wait for attach stop */
        wait(NULL);
 
-       ret = ptrace(PTRACE_SETOPTIONS, tracee, NULL, PTRACE_O_TRACESECCOMP);
+       ret = ptrace(PTRACE_SETOPTIONS, tracee, NULL, ptrace_syscall ?
+                                                     PTRACE_O_TRACESYSGOOD :
+                                                     PTRACE_O_TRACESECCOMP);
        ASSERT_EQ(0, ret) {
                TH_LOG("Failed to set PTRACE_O_TRACESECCOMP");
                kill(tracee, SIGKILL);
        }
-       ptrace(PTRACE_CONT, tracee, NULL, 0);
+       ret = ptrace(ptrace_syscall ? PTRACE_SYSCALL : PTRACE_CONT,
+                    tracee, NULL, 0);
+       ASSERT_EQ(0, ret);
 
        /* Unblock the tracee */
        ASSERT_EQ(1, write(fd, "A", 1));
@@ -1063,12 +1067,13 @@ void tracer(struct __test_metadata *_metadata, int fd, pid_t tracee,
                        /* Child is dead. Time to go. */
                        return;
 
-               /* Make sure this is a seccomp event. */
-               ASSERT_EQ(true, IS_SECCOMP_EVENT(status));
+               /* Check if this is a seccomp event. */
+               ASSERT_EQ(!ptrace_syscall, IS_SECCOMP_EVENT(status));
 
                tracer_func(_metadata, tracee, status, args);
 
-               ret = ptrace(PTRACE_CONT, tracee, NULL, NULL);
+               ret = ptrace(ptrace_syscall ? PTRACE_SYSCALL : PTRACE_CONT,
+                            tracee, NULL, 0);
                ASSERT_EQ(0, ret);
        }
        /* Directly report the status of our test harness results. */
@@ -1079,7 +1084,7 @@ void tracer(struct __test_metadata *_metadata, int fd, pid_t tracee,
 void cont_handler(int num)
 { }
 pid_t setup_trace_fixture(struct __test_metadata *_metadata,
-                         tracer_func_t func, void *args)
+                         tracer_func_t func, void *args, bool ptrace_syscall)
 {
        char sync;
        int pipefd[2];
@@ -1095,7 +1100,8 @@ pid_t setup_trace_fixture(struct __test_metadata *_metadata,
        signal(SIGALRM, cont_handler);
        if (tracer_pid == 0) {
                close(pipefd[0]);
-               tracer(_metadata, pipefd[1], tracee, func, args);
+               start_tracer(_metadata, pipefd[1], tracee, func, args,
+                            ptrace_syscall);
                syscall(__NR_exit, 0);
        }
        close(pipefd[1]);
@@ -1177,7 +1183,7 @@ FIXTURE_SETUP(TRACE_poke)
 
        /* Launch tracer. */
        self->tracer = setup_trace_fixture(_metadata, tracer_poke,
-                                          &self->tracer_args);
+                                          &self->tracer_args, false);
 }
 
 FIXTURE_TEARDOWN(TRACE_poke)
@@ -1399,6 +1405,29 @@ void tracer_syscall(struct __test_metadata *_metadata, pid_t tracee,
 
 }
 
+void tracer_ptrace(struct __test_metadata *_metadata, pid_t tracee,
+                  int status, void *args)
+{
+       int ret, nr;
+       unsigned long msg;
+       static bool entry;
+
+       /* Make sure we got an empty message. */
+       ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg);
+       EXPECT_EQ(0, ret);
+       EXPECT_EQ(0, msg);
+
+       /* The only way to tell PTRACE_SYSCALL entry/exit is by counting. */
+       entry = !entry;
+       if (!entry)
+               return;
+
+       nr = get_syscall(_metadata, tracee);
+
+       if (nr == __NR_getpid)
+               change_syscall(_metadata, tracee, __NR_getppid);
+}
+
 FIXTURE_DATA(TRACE_syscall) {
        struct sock_fprog prog;
        pid_t tracer, mytid, mypid, parent;
@@ -1440,7 +1469,8 @@ FIXTURE_SETUP(TRACE_syscall)
        ASSERT_NE(self->parent, self->mypid);
 
        /* Launch tracer. */
-       self->tracer = setup_trace_fixture(_metadata, tracer_syscall, NULL);
+       self->tracer = setup_trace_fixture(_metadata, tracer_syscall, NULL,
+                                          false);
 }
 
 FIXTURE_TEARDOWN(TRACE_syscall)
@@ -1500,6 +1530,130 @@ TEST_F(TRACE_syscall, syscall_dropped)
        EXPECT_NE(self->mytid, syscall(__NR_gettid));
 }
 
+TEST_F(TRACE_syscall, skip_after_RET_TRACE)
+{
+       struct sock_filter filter[] = {
+               BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+                       offsetof(struct seccomp_data, nr)),
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1),
+               BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | EPERM),
+               BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+       };
+       struct sock_fprog prog = {
+               .len = (unsigned short)ARRAY_SIZE(filter),
+               .filter = filter,
+       };
+       long ret;
+
+       ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+       ASSERT_EQ(0, ret);
+
+       /* Install fixture filter. */
+       ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
+       ASSERT_EQ(0, ret);
+
+       /* Install "errno on getppid" filter. */
+       ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
+       ASSERT_EQ(0, ret);
+
+       /* Tracer will redirect getpid to getppid, and we should see EPERM. */
+       EXPECT_EQ(-1, syscall(__NR_getpid));
+       EXPECT_EQ(EPERM, errno);
+}
+
+TEST_F_SIGNAL(TRACE_syscall, kill_after_RET_TRACE, SIGSYS)
+{
+       struct sock_filter filter[] = {
+               BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+                       offsetof(struct seccomp_data, nr)),
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1),
+               BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
+               BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+       };
+       struct sock_fprog prog = {
+               .len = (unsigned short)ARRAY_SIZE(filter),
+               .filter = filter,
+       };
+       long ret;
+
+       ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+       ASSERT_EQ(0, ret);
+
+       /* Install fixture filter. */
+       ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
+       ASSERT_EQ(0, ret);
+
+       /* Install "death on getppid" filter. */
+       ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
+       ASSERT_EQ(0, ret);
+
+       /* Tracer will redirect getpid to getppid, and we should die. */
+       EXPECT_NE(self->mypid, syscall(__NR_getpid));
+}
+
+TEST_F(TRACE_syscall, skip_after_ptrace)
+{
+       struct sock_filter filter[] = {
+               BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+                       offsetof(struct seccomp_data, nr)),
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1),
+               BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | EPERM),
+               BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+       };
+       struct sock_fprog prog = {
+               .len = (unsigned short)ARRAY_SIZE(filter),
+               .filter = filter,
+       };
+       long ret;
+
+       /* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */
+       teardown_trace_fixture(_metadata, self->tracer);
+       self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL,
+                                          true);
+
+       ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+       ASSERT_EQ(0, ret);
+
+       /* Install "errno on getppid" filter. */
+       ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
+       ASSERT_EQ(0, ret);
+
+       /* Tracer will redirect getpid to getppid, and we should see EPERM. */
+       EXPECT_EQ(-1, syscall(__NR_getpid));
+       EXPECT_EQ(EPERM, errno);
+}
+
+TEST_F_SIGNAL(TRACE_syscall, kill_after_ptrace, SIGSYS)
+{
+       struct sock_filter filter[] = {
+               BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+                       offsetof(struct seccomp_data, nr)),
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1),
+               BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
+               BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+       };
+       struct sock_fprog prog = {
+               .len = (unsigned short)ARRAY_SIZE(filter),
+               .filter = filter,
+       };
+       long ret;
+
+       /* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */
+       teardown_trace_fixture(_metadata, self->tracer);
+       self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL,
+                                          true);
+
+       ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+       ASSERT_EQ(0, ret);
+
+       /* Install "death on getppid" filter. */
+       ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
+       ASSERT_EQ(0, ret);
+
+       /* Tracer will redirect getpid to getppid, and we should die. */
+       EXPECT_NE(self->mypid, syscall(__NR_getpid));
+}
+
 #ifndef __NR_seccomp
 # if defined(__i386__)
 #  define __NR_seccomp 354