]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 26 Mar 2009 18:05:17 +0000 (11:05 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 26 Mar 2009 18:05:17 +0000 (11:05 -0700)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (430 commits)
  ALSA: hda - Add quirk for Acer Ferrari 5000
  ALSA: hda - Use cached calls to get widget caps and pin caps
  ALSA: hda - Don't create empty/single-item input source
  ALSA: hda - Fix the wrong pin-cap check in patch_realtek.c
  ALSA: hda - Cache pin-cap values
  ALSA: hda - Avoid output amp manipulation to digital mic pins
  ALSA: hda - Add function id to proc output
  ALSA: pcm - Safer boundary checks
  ALSA: hda - Detect digital-mic inputs on ALC663 / ALC272
  ALSA: sound/ali5451: typo: s/resouces/resources/
  ALSA: hda - Don't show the current connection for power widgets
  ALSA: Fix wrong pointer to dev_err() in arm/pxa2xx-ac97-lib.c
  ASoC: Declare Headset as Mic and Headphone widgets for SDP3430
  ASoC: OMAP: N810: Add more jack functions
  ASoC: OMAP: N810: Mark not connected input pins
  ASoC: Add FLL support for WM8400
  ALSA: hda - Don't reset stream at each prepare callback
  ALSA: hda - Don't reset BDL unnecessarily
  ALSA: pcm - Fix delta calculation at boundary overlap
  ALSA: pcm - Reset invalid position even without debug option
  ...

162 files changed:
Documentation/ABI/testing/ima_policy [new file with mode: 0644]
Documentation/cpu-freq/governors.txt
Documentation/cpu-freq/user-guide.txt
Documentation/kernel-parameters.txt
MAINTAINERS
arch/mips/include/asm/mipsregs.h
arch/mips/kernel/linux32.c
arch/mips/kernel/traps.c
arch/mips/mm/c-r4k.c
arch/mips/mm/dma-default.c
arch/powerpc/boot/dts/canyonlands.dts
arch/powerpc/boot/dts/kilauea.dts
arch/s390/crypto/sha.h
arch/s390/crypto/sha1_s390.c
arch/s390/crypto/sha256_s390.c
arch/s390/crypto/sha512_s390.c
arch/s390/crypto/sha_common.c
arch/x86/crypto/Makefile
arch/x86/crypto/aes-i586-asm_32.S
arch/x86/crypto/aes-x86_64-asm_64.S
arch/x86/crypto/aes_glue.c
arch/x86/crypto/aesni-intel_asm.S [new file with mode: 0644]
arch/x86/crypto/aesni-intel_glue.c [new file with mode: 0644]
arch/x86/include/asm/aes.h [new file with mode: 0644]
arch/x86/include/asm/cpufeature.h
arch/x86/include/asm/timer.h
arch/x86/kernel/cpu/cpufreq/Kconfig
arch/x86/kernel/cpu/cpufreq/Makefile
arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c
arch/x86/kernel/cpu/cpufreq/cpufreq-nforce2.c
arch/x86/kernel/cpu/cpufreq/e_powersaver.c
arch/x86/kernel/cpu/cpufreq/elanfreq.c
arch/x86/kernel/cpu/cpufreq/gx-suspmod.c
arch/x86/kernel/cpu/cpufreq/longhaul.c
arch/x86/kernel/cpu/cpufreq/longhaul.h
arch/x86/kernel/cpu/cpufreq/longrun.c
arch/x86/kernel/cpu/cpufreq/p4-clockmod.c
arch/x86/kernel/cpu/cpufreq/powernow-k6.c
arch/x86/kernel/cpu/cpufreq/powernow-k7.c
arch/x86/kernel/cpu/cpufreq/powernow-k8.c
arch/x86/kernel/cpu/cpufreq/powernow-k8.h
arch/x86/kernel/cpu/cpufreq/sc520_freq.c
arch/x86/kernel/cpu/cpufreq/speedstep-ich.c
arch/x86/kernel/cpu/cpufreq/speedstep-lib.c
arch/x86/kernel/cpu/cpufreq/speedstep-lib.h
arch/x86/kernel/cpu/cpufreq/speedstep-smi.c
arch/x86/kernel/tsc.c
crypto/Kconfig
crypto/Makefile
crypto/ablkcipher.c
crypto/aead.c
crypto/algboss.c
crypto/ansi_cprng.c
crypto/api.c
crypto/blkcipher.c
crypto/chainiv.c
crypto/cryptd.c
crypto/crypto_wq.c [new file with mode: 0644]
crypto/gf128mul.c
crypto/internal.h
crypto/pcompress.c [new file with mode: 0644]
crypto/sha256_generic.c
crypto/shash.c
crypto/tcrypt.c
crypto/testmgr.c
crypto/testmgr.h
crypto/zlib.c [new file with mode: 0644]
drivers/char/hw_random/Kconfig
drivers/char/hw_random/Makefile
drivers/char/hw_random/timeriomem-rng.c [new file with mode: 0644]
drivers/char/tpm/tpm.c
drivers/char/tpm/tpm.h
drivers/cpufreq/cpufreq.c
drivers/cpufreq/cpufreq_conservative.c
drivers/cpufreq/cpufreq_ondemand.c
drivers/cpufreq/cpufreq_stats.c
drivers/cpufreq/cpufreq_userspace.c
drivers/cpufreq/freq_table.c
drivers/crypto/Kconfig
drivers/crypto/Makefile
drivers/crypto/amcc/Makefile [new file with mode: 0644]
drivers/crypto/amcc/crypto4xx_alg.c [new file with mode: 0644]
drivers/crypto/amcc/crypto4xx_core.c [new file with mode: 0644]
drivers/crypto/amcc/crypto4xx_core.h [new file with mode: 0644]
drivers/crypto/amcc/crypto4xx_reg_def.h [new file with mode: 0644]
drivers/crypto/amcc/crypto4xx_sa.c [new file with mode: 0644]
drivers/crypto/amcc/crypto4xx_sa.h [new file with mode: 0644]
fs/compat.c
fs/exec.c
fs/file_table.c
fs/inode.c
fs/namei.c
fs/namespace.c
include/crypto/aes.h
include/crypto/compress.h [new file with mode: 0644]
include/crypto/cryptd.h [new file with mode: 0644]
include/crypto/crypto_wq.h [new file with mode: 0644]
include/crypto/hash.h
include/crypto/internal/compress.h [new file with mode: 0644]
include/linux/audit.h
include/linux/crypto.h
include/linux/fs.h
include/linux/ima.h [new file with mode: 0644]
include/linux/mount.h
include/linux/sched.h
include/linux/timeriomem-rng.h [new file with mode: 0644]
include/linux/tpm.h [new file with mode: 0644]
ipc/shm.c
kernel/user.c
lib/Kconfig
lib/Makefile
lib/nlattr.c [moved from net/netlink/attr.c with 99% similarity]
mm/mmap.c
mm/page-writeback.c
mm/shmem.c
net/Kconfig
net/netlink/Makefile
security/Kconfig
security/Makefile
security/inode.c
security/integrity/ima/Kconfig [new file with mode: 0644]
security/integrity/ima/Makefile [new file with mode: 0644]
security/integrity/ima/ima.h [new file with mode: 0644]
security/integrity/ima/ima_api.c [new file with mode: 0644]
security/integrity/ima/ima_audit.c [new file with mode: 0644]
security/integrity/ima/ima_crypto.c [new file with mode: 0644]
security/integrity/ima/ima_fs.c [new file with mode: 0644]
security/integrity/ima/ima_iint.c [new file with mode: 0644]
security/integrity/ima/ima_init.c [new file with mode: 0644]
security/integrity/ima/ima_main.c [new file with mode: 0644]
security/integrity/ima/ima_policy.c [new file with mode: 0644]
security/integrity/ima/ima_queue.c [new file with mode: 0644]
security/keys/internal.h
security/keys/key.c
security/keys/keyctl.c
security/keys/keyring.c
security/keys/permission.c
security/keys/proc.c
security/keys/process_keys.c
security/keys/request_key.c
security/selinux/avc.c
security/selinux/hooks.c
security/selinux/include/av_perm_to_string.h
security/selinux/include/av_permissions.h
security/selinux/include/objsec.h
security/selinux/include/security.h
security/selinux/nlmsgtab.c
security/selinux/selinuxfs.c
security/selinux/ss/services.c
security/smack/smack_access.c
security/smack/smack_lsm.c
security/smack/smackfs.c
security/tomoyo/Kconfig [new file with mode: 0644]
security/tomoyo/Makefile [new file with mode: 0644]
security/tomoyo/common.c [new file with mode: 0644]
security/tomoyo/common.h [new file with mode: 0644]
security/tomoyo/domain.c [new file with mode: 0644]
security/tomoyo/file.c [new file with mode: 0644]
security/tomoyo/realpath.c [new file with mode: 0644]
security/tomoyo/realpath.h [new file with mode: 0644]
security/tomoyo/tomoyo.c [new file with mode: 0644]
security/tomoyo/tomoyo.h [new file with mode: 0644]

diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
new file mode 100644 (file)
index 0000000..6434f0d
--- /dev/null
@@ -0,0 +1,61 @@
+What:          security/ima/policy
+Date:          May 2008
+Contact:       Mimi Zohar <zohar@us.ibm.com>
+Description:
+               The Trusted Computing Group(TCG) runtime Integrity
+               Measurement Architecture(IMA) maintains a list of hash
+               values of executables and other sensitive system files
+               loaded into the run-time of this system.  At runtime,
+               the policy can be constrained based on LSM specific data.
+               Policies are loaded into the securityfs file ima/policy
+               by opening the file, writing the rules one at a time and
+               then closing the file.  The new policy takes effect after
+               the file ima/policy is closed.
+
+               rule format: action [condition ...]
+
+               action: measure | dont_measure
+               condition:= base | lsm
+                       base:   [[func=] [mask=] [fsmagic=] [uid=]]
+                       lsm:    [[subj_user=] [subj_role=] [subj_type=]
+                                [obj_user=] [obj_role=] [obj_type=]]
+
+               base:   func:= [BPRM_CHECK][FILE_MMAP][INODE_PERMISSION]
+                       mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC]
+                       fsmagic:= hex value
+                       uid:= decimal value
+               lsm:    are LSM specific
+
+               default policy:
+                       # PROC_SUPER_MAGIC
+                       dont_measure fsmagic=0x9fa0
+                       # SYSFS_MAGIC
+                       dont_measure fsmagic=0x62656572
+                       # DEBUGFS_MAGIC
+                       dont_measure fsmagic=0x64626720
+                       # TMPFS_MAGIC
+                       dont_measure fsmagic=0x01021994
+                       # SECURITYFS_MAGIC
+                       dont_measure fsmagic=0x73636673
+
+                       measure func=BPRM_CHECK
+                       measure func=FILE_MMAP mask=MAY_EXEC
+                       measure func=INODE_PERM mask=MAY_READ uid=0
+
+               The default policy measures all executables in bprm_check,
+               all files mmapped executable in file_mmap, and all files
+               open for read by root in inode_permission.
+
+               Examples of LSM specific definitions:
+
+               SELinux:
+                       # SELINUX_MAGIC
+                       dont_measure fsmagic=0xF97CFF8C
+
+                       dont_measure obj_type=var_log_t
+                       dont_measure obj_type=auditd_log_t
+                       measure subj_user=system_u func=INODE_PERM mask=MAY_READ
+                       measure subj_role=system_r func=INODE_PERM mask=MAY_READ
+
+               Smack:
+                       measure subj_user=_ func=INODE_PERM mask=MAY_READ
index 5b0cfa67aff9c89ebc6d0f9dd4d467e8be8ae71f..ce73f3eb5ddb1a99a1b4571d6e058ac5c9823765 100644 (file)
@@ -117,10 +117,28 @@ accessible parameters:
 sampling_rate: measured in uS (10^-6 seconds), this is how often you
 want the kernel to look at the CPU usage and to make decisions on
 what to do about the frequency.  Typically this is set to values of
-around '10000' or more.
-
-show_sampling_rate_(min|max): the minimum and maximum sampling rates
-available that you may set 'sampling_rate' to.
+around '10000' or more. It's default value is (cmp. with users-guide.txt):
+transition_latency * 1000
+The lowest value you can set is:
+transition_latency * 100 or it may get restricted to a value where it
+makes not sense for the kernel anymore to poll that often which depends
+on your HZ config variable (HZ=1000: max=20000us, HZ=250: max=5000).
+Be aware that transition latency is in ns and sampling_rate is in us, so you
+get the same sysfs value by default.
+Sampling rate should always get adjusted considering the transition latency
+To set the sampling rate 750 times as high as the transition latency
+in the bash (as said, 1000 is default), do:
+echo `$(($(cat cpuinfo_transition_latency) * 750 / 1000)) \
+    >ondemand/sampling_rate
+
+show_sampling_rate_(min|max): THIS INTERFACE IS DEPRECATED, DON'T USE IT.
+You can use wider ranges now and the general
+cpuinfo_transition_latency variable (cmp. with user-guide.txt) can be
+used to obtain exactly the same info:
+show_sampling_rate_min = transtition_latency * 500    / 1000
+show_sampling_rate_max = transtition_latency * 500000 / 1000
+(divided by 1000 is to illustrate that sampling rate is in us and
+transition latency is exported ns).
 
 up_threshold: defines what the average CPU usage between the samplings
 of 'sampling_rate' needs to be for the kernel to make a decision on
index 917918f84fc75ac09061be1f1f242159823983cf..75f41193f3e1858b2370a2f4500caf49d2b90360 100644 (file)
@@ -152,6 +152,18 @@ cpuinfo_min_freq :         this file shows the minimum operating
                                frequency the processor can run at(in kHz) 
 cpuinfo_max_freq :             this file shows the maximum operating
                                frequency the processor can run at(in kHz) 
+cpuinfo_transition_latency     The time it takes on this CPU to
+                               switch between two frequencies in nano
+                               seconds. If unknown or known to be
+                               that high that the driver does not
+                               work with the ondemand governor, -1
+                               (CPUFREQ_ETERNAL) will be returned.
+                               Using this information can be useful
+                               to choose an appropriate polling
+                               frequency for a kernel governor or
+                               userspace daemon. Make sure to not
+                               switch the frequency too often
+                               resulting in performance loss.
 scaling_driver :               this file shows what cpufreq driver is
                                used to set the frequency on this CPU
 
index 54f21a5c262b02ef6cb4718f72b8357693197cf3..224263e7711f78d98fcc0becec0a92db2aac6f38 100644 (file)
@@ -44,6 +44,7 @@ parameter is applicable:
        FB      The frame buffer device is enabled.
        HW      Appropriate hardware is enabled.
        IA-64   IA-64 architecture is enabled.
+       IMA     Integrity measurement architecture is enabled.
        IOSCHED More than one I/O scheduler is enabled.
        IP_PNP  IP DHCP, BOOTP, or RARP is enabled.
        ISAPNP  ISA PnP code is enabled.
@@ -902,6 +903,15 @@ and is between 256 and 4096 characters. It is defined in the file
        ihash_entries=  [KNL]
                        Set number of hash buckets for inode cache.
 
+       ima_audit=      [IMA]
+                       Format: { "0" | "1" }
+                       0 -- integrity auditing messages. (Default)
+                       1 -- enable informational integrity auditing messages.
+
+       ima_hash=       [IMA]
+                       Formt: { "sha1" | "md5" }
+                       default: "sha1"
+
        in2000=         [HW,SCSI]
                        See header of drivers/scsi/in2000.c.
 
index 5d460c9d1c2c19ca871fabf53eb786572a64d5aa..1978fb205bf75b6b00df8f19f6b67ccf879ebeca 100644 (file)
@@ -1269,6 +1269,12 @@ L:       linux-crypto@vger.kernel.org
 T:     git kernel.org:/pub/scm/linux/kernel/git/herbert/crypto-2.6.git
 S:     Maintained
 
+CRYPTOGRAPHIC RANDOM NUMBER GENERATOR
+P:     Neil Horman
+M:     nhorman@tuxdriver.com
+L:     linux-crypto@vger.kernel.org
+S:     Maintained
+
 CS5535 Audio ALSA driver
 P:     Jaya Kumar
 M:     jayakumar.alsa@gmail.com
@@ -2216,6 +2222,11 @@ M:       stefanr@s5r6.in-berlin.de
 L:     linux1394-devel@lists.sourceforge.net
 S:     Maintained
 
+INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
+P:     Mimi Zohar
+M:     zohar@us.ibm.com
+S:     Supported
+
 IMS TWINTURBO FRAMEBUFFER DRIVER
 L:     linux-fbdev-devel@lists.sourceforge.net (moderated for non-subscribers)
 S:     Orphan
@@ -3844,6 +3855,7 @@ M:        jmorris@namei.org
 L:     linux-kernel@vger.kernel.org
 L:     linux-security-module@vger.kernel.org (suggested Cc:)
 T:     git kernel.org:pub/scm/linux/kernel/git/jmorris/security-testing-2.6.git
+W:     http://security.wiki.kernel.org/
 S:     Supported
 
 SECURITY CONTACT
@@ -4285,6 +4297,19 @@ L:       tlan-devel@lists.sourceforge.net (subscribers-only)
 W:     http://sourceforge.net/projects/tlan/
 S:     Maintained
 
+TOMOYO SECURITY MODULE
+P:     Kentaro Takeda
+M:     takedakn@nttdata.co.jp
+P:     Tetsuo Handa
+M:     penguin-kernel@I-love.SAKURA.ne.jp
+L:     linux-kernel@vger.kernel.org (kernel issues)
+L:     tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for developers and users in English)
+L:     tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese)
+L:     tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese)
+W:     http://tomoyo.sourceforge.jp/
+T:     quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.2.x/tomoyo-lsm/patches/
+S:     Maintained
+
 TOSHIBA ACPI EXTRAS DRIVER
 P:     John Belmonte
 M:     toshiba_acpi@memebeam.org
index 0417516503f634484bea51436d9befa8059b4a83..526f327475cea59bf93f0db86505fdd2ef4f86c3 100644 (file)
@@ -1391,11 +1391,11 @@ static inline void tlb_write_random(void)
 static inline unsigned int                                     \
 set_c0_##name(unsigned int set)                                        \
 {                                                              \
-       unsigned int res;                                       \
+       unsigned int res, new;                                  \
                                                                \
        res = read_c0_##name();                                 \
-       res |= set;                                             \
-       write_c0_##name(res);                                   \
+       new = res | set;                                        \
+       write_c0_##name(new);                                   \
                                                                \
        return res;                                             \
 }                                                              \
@@ -1403,24 +1403,24 @@ set_c0_##name(unsigned int set)                                 \
 static inline unsigned int                                     \
 clear_c0_##name(unsigned int clear)                            \
 {                                                              \
-       unsigned int res;                                       \
+       unsigned int res, new;                                  \
                                                                \
        res = read_c0_##name();                                 \
-       res &= ~clear;                                          \
-       write_c0_##name(res);                                   \
+       new = res & ~clear;                                     \
+       write_c0_##name(new);                                   \
                                                                \
        return res;                                             \
 }                                                              \
                                                                \
 static inline unsigned int                                     \
-change_c0_##name(unsigned int change, unsigned int new)                \
+change_c0_##name(unsigned int change, unsigned int val)                \
 {                                                              \
-       unsigned int res;                                       \
+       unsigned int res, new;                                  \
                                                                \
        res = read_c0_##name();                                 \
-       res &= ~change;                                         \
-       res |= (new & change);                                  \
-       write_c0_##name(res);                                   \
+       new = res & ~change;                                    \
+       new |= (val & change);                                  \
+       write_c0_##name(new);                                   \
                                                                \
        return res;                                             \
 }
index 1a86f84fa9470f31030c53fdc9ad45c792d08425..49aac6e17df932a89658840302c7332a8c27f012 100644 (file)
@@ -32,7 +32,6 @@
 #include <linux/module.h>
 #include <linux/binfmts.h>
 #include <linux/security.h>
-#include <linux/syscalls.h>
 #include <linux/compat.h>
 #include <linux/vfs.h>
 #include <linux/ipc.h>
index b2d7041341b8f5afa3e313302c21b90e34e5ea3d..29fadaccecddef31d5da3ea33f819b1ad3941fe0 100644 (file)
@@ -1520,7 +1520,9 @@ void __cpuinit per_cpu_trap_init(void)
 #endif /* CONFIG_MIPS_MT_SMTC */
 
        if (cpu_has_veic || cpu_has_vint) {
+               unsigned long sr = set_c0_status(ST0_BEV);
                write_c0_ebase(ebase);
+               write_c0_status(sr);
                /* Setting vector spacing enables EI/VI mode  */
                change_c0_intctl(0x3e0, VECTORSPACING);
        }
@@ -1602,8 +1604,6 @@ void __cpuinit set_uncached_handler(unsigned long offset, void *addr,
 #ifdef CONFIG_64BIT
        unsigned long uncached_ebase = TO_UNCAC(ebase);
 #endif
-       if (cpu_has_mips_r2)
-               uncached_ebase += (read_c0_ebase() & 0x3ffff000);
 
        if (!addr)
                panic(panic_null_cerr);
@@ -1635,9 +1635,11 @@ void __init trap_init(void)
                return; /* Already done */
 #endif
 
-       if (cpu_has_veic || cpu_has_vint)
-               ebase = (unsigned long) alloc_bootmem_low_pages(0x200 + VECTORSPACING*64);
-       else {
+       if (cpu_has_veic || cpu_has_vint) {
+               unsigned long size = 0x200 + VECTORSPACING*64;
+               ebase = (unsigned long)
+                       __alloc_bootmem(size, 1 << fls(size), 0);
+       } else {
                ebase = CAC_BASE;
                if (cpu_has_mips_r2)
                        ebase += (read_c0_ebase() & 0x3ffff000);
index c43f4b26a69010b63154a734618060c0981b692e..871e828bc62ac2e7db45405f4347b598374301a5 100644 (file)
@@ -780,7 +780,7 @@ static void __cpuinit probe_pcache(void)
                c->dcache.ways = 2;
                c->dcache.waybit = 0;
 
-               c->options |= MIPS_CPU_CACHE_CDEX_P;
+               c->options |= MIPS_CPU_CACHE_CDEX_P | MIPS_CPU_PREFETCH;
                break;
 
        case CPU_TX49XX:
index 546e6977d4ffb0bd1910a57114803bdb1bffe858..bed56f1ac83709887aa1c4b60922d9fa6602d354 100644 (file)
@@ -225,7 +225,7 @@ void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size,
        if (!plat_device_is_coherent(dev) && direction != DMA_TO_DEVICE) {
                unsigned long addr;
 
-               addr = plat_dma_addr_to_phys(dma_address);
+               addr = dma_addr_to_virt(dma_address);
                dma_cache_wback_inv(addr, size);
        }
 
index 8b5ba8261a36ad54d80863881da214d3bc273d71..4447def69dc5175f78afb33a7546e901cb9d99b4 100644 (file)
                        dcr-reg = <0x010 0x002>;
                };
 
+               CRYPTO: crypto@180000 {
+                       compatible = "amcc,ppc460ex-crypto", "amcc,ppc4xx-crypto";
+                       reg = <4 0x00180000 0x80400>;
+                       interrupt-parent = <&UIC0>;
+                       interrupts = <0x1d 0x4>;
+               };
+
                MAL0: mcmal {
                        compatible = "ibm,mcmal-460ex", "ibm,mcmal2";
                        dcr-reg = <0x180 0x062>;
index 2804444812e5e75e0a0e19c7ea4c82ad92712700..5e6b08ff6f6701e158aa94241cabe3681024d520 100644 (file)
                                      0x6 0x4>; /* ECC SEC Error */ 
                };
 
+               CRYPTO: crypto@ef700000 {
+                       compatible = "amcc,ppc405ex-crypto", "amcc,ppc4xx-crypto";
+                       reg = <0xef700000 0x80400>;
+                       interrupt-parent = <&UIC0>;
+                       interrupts = <0x17 0x2>;
+               };
+
                MAL0: mcmal {
                        compatible = "ibm,mcmal-405ex", "ibm,mcmal2";
                        dcr-reg = <0x180 0x062>;
index 1ceafa571eab5e2c87276a7da7f8cd1ae66e4dd5..f4e9dc71675f7e5d572c2e6d97312b097e5d89a2 100644 (file)
@@ -29,7 +29,9 @@ struct s390_sha_ctx {
        int func;               /* KIMD function to use */
 };
 
-void s390_sha_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len);
-void s390_sha_final(struct crypto_tfm *tfm, u8 *out);
+struct shash_desc;
+
+int s390_sha_update(struct shash_desc *desc, const u8 *data, unsigned int len);
+int s390_sha_final(struct shash_desc *desc, u8 *out);
 
 #endif
index b3cb5a89b00d08a37030c80ca53940ef9bc3af16..e85ba348722a95007d26bb8ce73e4fe5913c944f 100644 (file)
  * any later version.
  *
  */
+#include <crypto/internal/hash.h>
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/crypto.h>
 #include <crypto/sha.h>
 
 #include "crypt_s390.h"
 #include "sha.h"
 
-static void sha1_init(struct crypto_tfm *tfm)
+static int sha1_init(struct shash_desc *desc)
 {
-       struct s390_sha_ctx *sctx = crypto_tfm_ctx(tfm);
+       struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
 
        sctx->state[0] = SHA1_H0;
        sctx->state[1] = SHA1_H1;
@@ -42,34 +42,36 @@ static void sha1_init(struct crypto_tfm *tfm)
        sctx->state[4] = SHA1_H4;
        sctx->count = 0;
        sctx->func = KIMD_SHA_1;
+
+       return 0;
 }
 
-static struct crypto_alg alg = {
-       .cra_name       =       "sha1",
-       .cra_driver_name=       "sha1-s390",
-       .cra_priority   =       CRYPT_S390_PRIORITY,
-       .cra_flags      =       CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  =       SHA1_BLOCK_SIZE,
-       .cra_ctxsize    =       sizeof(struct s390_sha_ctx),
-       .cra_module     =       THIS_MODULE,
-       .cra_list       =       LIST_HEAD_INIT(alg.cra_list),
-       .cra_u          =       { .digest = {
-       .dia_digestsize =       SHA1_DIGEST_SIZE,
-       .dia_init       =       sha1_init,
-       .dia_update     =       s390_sha_update,
-       .dia_final      =       s390_sha_final } }
+static struct shash_alg alg = {
+       .digestsize     =       SHA1_DIGEST_SIZE,
+       .init           =       sha1_init,
+       .update         =       s390_sha_update,
+       .final          =       s390_sha_final,
+       .descsize       =       sizeof(struct s390_sha_ctx),
+       .base           =       {
+               .cra_name       =       "sha1",
+               .cra_driver_name=       "sha1-s390",
+               .cra_priority   =       CRYPT_S390_PRIORITY,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA1_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
 static int __init sha1_s390_init(void)
 {
        if (!crypt_s390_func_available(KIMD_SHA_1))
                return -EOPNOTSUPP;
-       return crypto_register_alg(&alg);
+       return crypto_register_shash(&alg);
 }
 
 static void __exit sha1_s390_fini(void)
 {
-       crypto_unregister_alg(&alg);
+       crypto_unregister_shash(&alg);
 }
 
 module_init(sha1_s390_init);
index 19c03fb6ba7eee64d773ab0cdb5d0c1d556c05c0..f9fefc5696329fd4f64e272ff6dac0c4e5ec0f91 100644 (file)
  * any later version.
  *
  */
+#include <crypto/internal/hash.h>
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/crypto.h>
 #include <crypto/sha.h>
 
 #include "crypt_s390.h"
 #include "sha.h"
 
-static void sha256_init(struct crypto_tfm *tfm)
+static int sha256_init(struct shash_desc *desc)
 {
-       struct s390_sha_ctx *sctx = crypto_tfm_ctx(tfm);
+       struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
 
        sctx->state[0] = SHA256_H0;
        sctx->state[1] = SHA256_H1;
@@ -38,22 +38,24 @@ static void sha256_init(struct crypto_tfm *tfm)
        sctx->state[7] = SHA256_H7;
        sctx->count = 0;
        sctx->func = KIMD_SHA_256;
+
+       return 0;
 }
 
-static struct crypto_alg alg = {
-       .cra_name       =       "sha256",
-       .cra_driver_name =      "sha256-s390",
-       .cra_priority   =       CRYPT_S390_PRIORITY,
-       .cra_flags      =       CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  =       SHA256_BLOCK_SIZE,
-       .cra_ctxsize    =       sizeof(struct s390_sha_ctx),
-       .cra_module     =       THIS_MODULE,
-       .cra_list       =       LIST_HEAD_INIT(alg.cra_list),
-       .cra_u          =       { .digest = {
-       .dia_digestsize =       SHA256_DIGEST_SIZE,
-       .dia_init       =       sha256_init,
-       .dia_update     =       s390_sha_update,
-       .dia_final      =       s390_sha_final } }
+static struct shash_alg alg = {
+       .digestsize     =       SHA256_DIGEST_SIZE,
+       .init           =       sha256_init,
+       .update         =       s390_sha_update,
+       .final          =       s390_sha_final,
+       .descsize       =       sizeof(struct s390_sha_ctx),
+       .base           =       {
+               .cra_name       =       "sha256",
+               .cra_driver_name=       "sha256-s390",
+               .cra_priority   =       CRYPT_S390_PRIORITY,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA256_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
 static int sha256_s390_init(void)
@@ -61,12 +63,12 @@ static int sha256_s390_init(void)
        if (!crypt_s390_func_available(KIMD_SHA_256))
                return -EOPNOTSUPP;
 
-       return crypto_register_alg(&alg);
+       return crypto_register_shash(&alg);
 }
 
 static void __exit sha256_s390_fini(void)
 {
-       crypto_unregister_alg(&alg);
+       crypto_unregister_shash(&alg);
 }
 
 module_init(sha256_s390_init);
index 23c7861f6aeb4d132c2380aca491998be609174c..83192bfc80480bd1477433fd906b6450ab7c4d38 100644 (file)
  * any later version.
  *
  */
+#include <crypto/internal/hash.h>
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/crypto.h>
 
 #include "sha.h"
 #include "crypt_s390.h"
 
-static void sha512_init(struct crypto_tfm *tfm)
+static int sha512_init(struct shash_desc *desc)
 {
-       struct s390_sha_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct s390_sha_ctx *ctx = shash_desc_ctx(desc);
 
        *(__u64 *)&ctx->state[0] = 0x6a09e667f3bcc908ULL;
        *(__u64 *)&ctx->state[2] = 0xbb67ae8584caa73bULL;
@@ -33,29 +33,31 @@ static void sha512_init(struct crypto_tfm *tfm)
        *(__u64 *)&ctx->state[14] = 0x5be0cd19137e2179ULL;
        ctx->count = 0;
        ctx->func = KIMD_SHA_512;
+
+       return 0;
 }
 
-static struct crypto_alg sha512_alg = {
-       .cra_name       =       "sha512",
-       .cra_driver_name =      "sha512-s390",
-       .cra_priority   =       CRYPT_S390_PRIORITY,
-       .cra_flags      =       CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  =       SHA512_BLOCK_SIZE,
-       .cra_ctxsize    =       sizeof(struct s390_sha_ctx),
-       .cra_module     =       THIS_MODULE,
-       .cra_list       =       LIST_HEAD_INIT(sha512_alg.cra_list),
-       .cra_u          =       { .digest = {
-       .dia_digestsize =       SHA512_DIGEST_SIZE,
-       .dia_init       =       sha512_init,
-       .dia_update     =       s390_sha_update,
-       .dia_final      =       s390_sha_final } }
+static struct shash_alg sha512_alg = {
+       .digestsize     =       SHA512_DIGEST_SIZE,
+       .init           =       sha512_init,
+       .update         =       s390_sha_update,
+       .final          =       s390_sha_final,
+       .descsize       =       sizeof(struct s390_sha_ctx),
+       .base           =       {
+               .cra_name       =       "sha512",
+               .cra_driver_name=       "sha512-s390",
+               .cra_priority   =       CRYPT_S390_PRIORITY,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA512_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
 MODULE_ALIAS("sha512");
 
-static void sha384_init(struct crypto_tfm *tfm)
+static int sha384_init(struct shash_desc *desc)
 {
-       struct s390_sha_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct s390_sha_ctx *ctx = shash_desc_ctx(desc);
 
        *(__u64 *)&ctx->state[0] = 0xcbbb9d5dc1059ed8ULL;
        *(__u64 *)&ctx->state[2] = 0x629a292a367cd507ULL;
@@ -67,22 +69,25 @@ static void sha384_init(struct crypto_tfm *tfm)
        *(__u64 *)&ctx->state[14] = 0x47b5481dbefa4fa4ULL;
        ctx->count = 0;
        ctx->func = KIMD_SHA_512;
+
+       return 0;
 }
 
-static struct crypto_alg sha384_alg = {
-       .cra_name       =       "sha384",
-       .cra_driver_name =      "sha384-s390",
-       .cra_priority   =       CRYPT_S390_PRIORITY,
-       .cra_flags      =       CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  =       SHA384_BLOCK_SIZE,
-       .cra_ctxsize    =       sizeof(struct s390_sha_ctx),
-       .cra_module     =       THIS_MODULE,
-       .cra_list       =       LIST_HEAD_INIT(sha384_alg.cra_list),
-       .cra_u          =       { .digest = {
-       .dia_digestsize =       SHA384_DIGEST_SIZE,
-       .dia_init       =       sha384_init,
-       .dia_update     =       s390_sha_update,
-       .dia_final      =       s390_sha_final } }
+static struct shash_alg sha384_alg = {
+       .digestsize     =       SHA384_DIGEST_SIZE,
+       .init           =       sha384_init,
+       .update         =       s390_sha_update,
+       .final          =       s390_sha_final,
+       .descsize       =       sizeof(struct s390_sha_ctx),
+       .base           =       {
+               .cra_name       =       "sha384",
+               .cra_driver_name=       "sha384-s390",
+               .cra_priority   =       CRYPT_S390_PRIORITY,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA384_BLOCK_SIZE,
+               .cra_ctxsize    =       sizeof(struct s390_sha_ctx),
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
 MODULE_ALIAS("sha384");
@@ -93,18 +98,18 @@ static int __init init(void)
 
        if (!crypt_s390_func_available(KIMD_SHA_512))
                return -EOPNOTSUPP;
-       if ((ret = crypto_register_alg(&sha512_alg)) < 0)
+       if ((ret = crypto_register_shash(&sha512_alg)) < 0)
                goto out;
-       if ((ret = crypto_register_alg(&sha384_alg)) < 0)
-               crypto_unregister_alg(&sha512_alg);
+       if ((ret = crypto_register_shash(&sha384_alg)) < 0)
+               crypto_unregister_shash(&sha512_alg);
 out:
        return ret;
 }
 
 static void __exit fini(void)
 {
-       crypto_unregister_alg(&sha512_alg);
-       crypto_unregister_alg(&sha384_alg);
+       crypto_unregister_shash(&sha512_alg);
+       crypto_unregister_shash(&sha384_alg);
 }
 
 module_init(init);
index 9d6eb8c3d37e3fac5df1312fd00d05554adbac3c..7903ec47e6b9c4a48ab9ffd6b2f47db7ee601678 100644 (file)
  *
  */
 
-#include <linux/crypto.h>
+#include <crypto/internal/hash.h>
 #include "sha.h"
 #include "crypt_s390.h"
 
-void s390_sha_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
+int s390_sha_update(struct shash_desc *desc, const u8 *data, unsigned int len)
 {
-       struct s390_sha_ctx *ctx = crypto_tfm_ctx(tfm);
-       unsigned int bsize = crypto_tfm_alg_blocksize(tfm);
+       struct s390_sha_ctx *ctx = shash_desc_ctx(desc);
+       unsigned int bsize = crypto_shash_blocksize(desc->tfm);
        unsigned int index;
        int ret;
 
@@ -51,13 +51,15 @@ void s390_sha_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
 store:
        if (len)
                memcpy(ctx->buf + index , data, len);
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(s390_sha_update);
 
-void s390_sha_final(struct crypto_tfm *tfm, u8 *out)
+int s390_sha_final(struct shash_desc *desc, u8 *out)
 {
-       struct s390_sha_ctx *ctx = crypto_tfm_ctx(tfm);
-       unsigned int bsize = crypto_tfm_alg_blocksize(tfm);
+       struct s390_sha_ctx *ctx = shash_desc_ctx(desc);
+       unsigned int bsize = crypto_shash_blocksize(desc->tfm);
        u64 bits;
        unsigned int index, end, plen;
        int ret;
@@ -87,9 +89,11 @@ void s390_sha_final(struct crypto_tfm *tfm, u8 *out)
        BUG_ON(ret != end);
 
        /* copy digest to out */
-       memcpy(out, ctx->state, crypto_hash_digestsize(crypto_hash_cast(tfm)));
+       memcpy(out, ctx->state, crypto_shash_digestsize(desc->tfm));
        /* wipe context */
        memset(ctx, 0, sizeof *ctx);
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(s390_sha_final);
 
index 903de4aa509496205cf792980d1b6f57b3d87177..ebe7deedd5b42b27974b1b8884e1b68bdebb82cb 100644 (file)
@@ -9,6 +9,7 @@ obj-$(CONFIG_CRYPTO_SALSA20_586) += salsa20-i586.o
 obj-$(CONFIG_CRYPTO_AES_X86_64) += aes-x86_64.o
 obj-$(CONFIG_CRYPTO_TWOFISH_X86_64) += twofish-x86_64.o
 obj-$(CONFIG_CRYPTO_SALSA20_X86_64) += salsa20-x86_64.o
+obj-$(CONFIG_CRYPTO_AES_NI_INTEL) += aesni-intel.o
 
 obj-$(CONFIG_CRYPTO_CRC32C_INTEL) += crc32c-intel.o
 
@@ -19,3 +20,5 @@ salsa20-i586-y := salsa20-i586-asm_32.o salsa20_glue.o
 aes-x86_64-y := aes-x86_64-asm_64.o aes_glue.o
 twofish-x86_64-y := twofish-x86_64-asm_64.o twofish_glue.o
 salsa20-x86_64-y := salsa20-x86_64-asm_64.o salsa20_glue.o
+
+aesni-intel-y := aesni-intel_asm.o aesni-intel_glue.o
index e41b147f45093009d9dbda30c314d1d4d748d3a5..b949ec2f9af444e06377492d39b9db9960f3a626 100644 (file)
 #define tlen 1024   // length of each of 4 'xor' arrays (256 32-bit words)
 
 /* offsets to parameters with one register pushed onto stack */
-#define tfm 8
+#define ctx 8
 #define out_blk 12
 #define in_blk 16
 
-/* offsets in crypto_tfm structure */
-#define klen (crypto_tfm_ctx_offset + 0)
-#define ekey (crypto_tfm_ctx_offset + 4)
-#define dkey (crypto_tfm_ctx_offset + 244)
+/* offsets in crypto_aes_ctx structure */
+#define klen (480)
+#define ekey (0)
+#define dkey (240)
 
 // register mapping for encrypt and decrypt subroutines
 
        do_col (table, r5,r0,r1,r4, r2,r3);             /* idx=r5 */
 
 // AES (Rijndael) Encryption Subroutine
-/* void aes_enc_blk(struct crypto_tfm *tfm, u8 *out_blk, const u8 *in_blk) */
+/* void aes_enc_blk(struct crypto_aes_ctx *ctx, u8 *out_blk, const u8 *in_blk) */
 
 .global  aes_enc_blk
 
 
 aes_enc_blk:
        push    %ebp
-       mov     tfm(%esp),%ebp
+       mov     ctx(%esp),%ebp
 
 // CAUTION: the order and the values used in these assigns 
 // rely on the register mappings
@@ -292,7 +292,7 @@ aes_enc_blk:
        ret
 
 // AES (Rijndael) Decryption Subroutine
-/* void aes_dec_blk(struct crypto_tfm *tfm, u8 *out_blk, const u8 *in_blk) */
+/* void aes_dec_blk(struct crypto_aes_ctx *ctx, u8 *out_blk, const u8 *in_blk) */
 
 .global  aes_dec_blk
 
@@ -303,7 +303,7 @@ aes_enc_blk:
 
 aes_dec_blk:
        push    %ebp
-       mov     tfm(%esp),%ebp
+       mov     ctx(%esp),%ebp
 
 // CAUTION: the order and the values used in these assigns 
 // rely on the register mappings
index a120f526c3df9d9c547b996867f1b035b44b006b..5b577d5a059b03db65d587e1eb15495d0ba99fa1 100644 (file)
@@ -17,8 +17,6 @@
 
 #include <asm/asm-offsets.h>
 
-#define BASE crypto_tfm_ctx_offset
-
 #define R1     %rax
 #define R1E    %eax
 #define R1X    %ax
        .align  8;                      \
 FUNC:  movq    r1,r2;                  \
        movq    r3,r4;                  \
-       leaq    BASE+KEY+48+4(r8),r9;   \
+       leaq    KEY+48(r8),r9;          \
        movq    r10,r11;                \
        movl    (r7),r5 ## E;           \
        movl    4(r7),r1 ## E;          \
        movl    8(r7),r6 ## E;          \
        movl    12(r7),r7 ## E;         \
-       movl    BASE+0(r8),r10 ## E;    \
+       movl    480(r8),r10 ## E;       \
        xorl    -48(r9),r5 ## E;        \
        xorl    -44(r9),r1 ## E;        \
        xorl    -40(r9),r6 ## E;        \
index 71f457827116bee654f4f5c67edebfe8117697c9..49ae9fe32b22079dc22c8d0bf121410285010d3d 100644 (file)
@@ -5,17 +5,29 @@
 
 #include <crypto/aes.h>
 
-asmlinkage void aes_enc_blk(struct crypto_tfm *tfm, u8 *out, const u8 *in);
-asmlinkage void aes_dec_blk(struct crypto_tfm *tfm, u8 *out, const u8 *in);
+asmlinkage void aes_enc_blk(struct crypto_aes_ctx *ctx, u8 *out, const u8 *in);
+asmlinkage void aes_dec_blk(struct crypto_aes_ctx *ctx, u8 *out, const u8 *in);
+
+void crypto_aes_encrypt_x86(struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src)
+{
+       aes_enc_blk(ctx, dst, src);
+}
+EXPORT_SYMBOL_GPL(crypto_aes_encrypt_x86);
+
+void crypto_aes_decrypt_x86(struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src)
+{
+       aes_dec_blk(ctx, dst, src);
+}
+EXPORT_SYMBOL_GPL(crypto_aes_decrypt_x86);
 
 static void aes_encrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src)
 {
-       aes_enc_blk(tfm, dst, src);
+       aes_enc_blk(crypto_tfm_ctx(tfm), dst, src);
 }
 
 static void aes_decrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src)
 {
-       aes_dec_blk(tfm, dst, src);
+       aes_dec_blk(crypto_tfm_ctx(tfm), dst, src);
 }
 
 static struct crypto_alg aes_alg = {
diff --git a/arch/x86/crypto/aesni-intel_asm.S b/arch/x86/crypto/aesni-intel_asm.S
new file mode 100644 (file)
index 0000000..caba996
--- /dev/null
@@ -0,0 +1,896 @@
+/*
+ * Implement AES algorithm in Intel AES-NI instructions.
+ *
+ * The white paper of AES-NI instructions can be downloaded from:
+ *   http://softwarecommunity.intel.com/isn/downloads/intelavx/AES-Instructions-Set_WP.pdf
+ *
+ * Copyright (C) 2008, Intel Corp.
+ *    Author: Huang Ying <ying.huang@intel.com>
+ *            Vinodh Gopal <vinodh.gopal@intel.com>
+ *            Kahraman Akdemir
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/linkage.h>
+
+.text
+
+#define STATE1 %xmm0
+#define STATE2 %xmm4
+#define STATE3 %xmm5
+#define STATE4 %xmm6
+#define STATE  STATE1
+#define IN1    %xmm1
+#define IN2    %xmm7
+#define IN3    %xmm8
+#define IN4    %xmm9
+#define IN     IN1
+#define KEY    %xmm2
+#define IV     %xmm3
+
+#define KEYP   %rdi
+#define OUTP   %rsi
+#define INP    %rdx
+#define LEN    %rcx
+#define IVP    %r8
+#define KLEN   %r9d
+#define T1     %r10
+#define TKEYP  T1
+#define T2     %r11
+
+_key_expansion_128:
+_key_expansion_256a:
+       pshufd $0b11111111, %xmm1, %xmm1
+       shufps $0b00010000, %xmm0, %xmm4
+       pxor %xmm4, %xmm0
+       shufps $0b10001100, %xmm0, %xmm4
+       pxor %xmm4, %xmm0
+       pxor %xmm1, %xmm0
+       movaps %xmm0, (%rcx)
+       add $0x10, %rcx
+       ret
+
+_key_expansion_192a:
+       pshufd $0b01010101, %xmm1, %xmm1
+       shufps $0b00010000, %xmm0, %xmm4
+       pxor %xmm4, %xmm0
+       shufps $0b10001100, %xmm0, %xmm4
+       pxor %xmm4, %xmm0
+       pxor %xmm1, %xmm0
+
+       movaps %xmm2, %xmm5
+       movaps %xmm2, %xmm6
+       pslldq $4, %xmm5
+       pshufd $0b11111111, %xmm0, %xmm3
+       pxor %xmm3, %xmm2
+       pxor %xmm5, %xmm2
+
+       movaps %xmm0, %xmm1
+       shufps $0b01000100, %xmm0, %xmm6
+       movaps %xmm6, (%rcx)
+       shufps $0b01001110, %xmm2, %xmm1
+       movaps %xmm1, 16(%rcx)
+       add $0x20, %rcx
+       ret
+
+_key_expansion_192b:
+       pshufd $0b01010101, %xmm1, %xmm1
+       shufps $0b00010000, %xmm0, %xmm4
+       pxor %xmm4, %xmm0
+       shufps $0b10001100, %xmm0, %xmm4
+       pxor %xmm4, %xmm0
+       pxor %xmm1, %xmm0
+
+       movaps %xmm2, %xmm5
+       pslldq $4, %xmm5
+       pshufd $0b11111111, %xmm0, %xmm3
+       pxor %xmm3, %xmm2
+       pxor %xmm5, %xmm2
+
+       movaps %xmm0, (%rcx)
+       add $0x10, %rcx
+       ret
+
+_key_expansion_256b:
+       pshufd $0b10101010, %xmm1, %xmm1
+       shufps $0b00010000, %xmm2, %xmm4
+       pxor %xmm4, %xmm2
+       shufps $0b10001100, %xmm2, %xmm4
+       pxor %xmm4, %xmm2
+       pxor %xmm1, %xmm2
+       movaps %xmm2, (%rcx)
+       add $0x10, %rcx
+       ret
+
+/*
+ * int aesni_set_key(struct crypto_aes_ctx *ctx, const u8 *in_key,
+ *                   unsigned int key_len)
+ */
+ENTRY(aesni_set_key)
+       movups (%rsi), %xmm0            # user key (first 16 bytes)
+       movaps %xmm0, (%rdi)
+       lea 0x10(%rdi), %rcx            # key addr
+       movl %edx, 480(%rdi)
+       pxor %xmm4, %xmm4               # xmm4 is assumed 0 in _key_expansion_x
+       cmp $24, %dl
+       jb .Lenc_key128
+       je .Lenc_key192
+       movups 0x10(%rsi), %xmm2        # other user key
+       movaps %xmm2, (%rcx)
+       add $0x10, %rcx
+       # aeskeygenassist $0x1, %xmm2, %xmm1    # round 1
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x01
+       call _key_expansion_256a
+       # aeskeygenassist $0x1, %xmm0, %xmm1
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x01
+       call _key_expansion_256b
+       # aeskeygenassist $0x2, %xmm2, %xmm1    # round 2
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x02
+       call _key_expansion_256a
+       # aeskeygenassist $0x2, %xmm0, %xmm1
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x02
+       call _key_expansion_256b
+       # aeskeygenassist $0x4, %xmm2, %xmm1    # round 3
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x04
+       call _key_expansion_256a
+       # aeskeygenassist $0x4, %xmm0, %xmm1
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x04
+       call _key_expansion_256b
+       # aeskeygenassist $0x8, %xmm2, %xmm1    # round 4
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x08
+       call _key_expansion_256a
+       # aeskeygenassist $0x8, %xmm0, %xmm1
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x08
+       call _key_expansion_256b
+       # aeskeygenassist $0x10, %xmm2, %xmm1   # round 5
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x10
+       call _key_expansion_256a
+       # aeskeygenassist $0x10, %xmm0, %xmm1
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x10
+       call _key_expansion_256b
+       # aeskeygenassist $0x20, %xmm2, %xmm1   # round 6
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x20
+       call _key_expansion_256a
+       # aeskeygenassist $0x20, %xmm0, %xmm1
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x20
+       call _key_expansion_256b
+       # aeskeygenassist $0x40, %xmm2, %xmm1   # round 7
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x40
+       call _key_expansion_256a
+       jmp .Ldec_key
+.Lenc_key192:
+       movq 0x10(%rsi), %xmm2          # other user key
+       # aeskeygenassist $0x1, %xmm2, %xmm1    # round 1
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x01
+       call _key_expansion_192a
+       # aeskeygenassist $0x2, %xmm2, %xmm1    # round 2
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x02
+       call _key_expansion_192b
+       # aeskeygenassist $0x4, %xmm2, %xmm1    # round 3
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x04
+       call _key_expansion_192a
+       # aeskeygenassist $0x8, %xmm2, %xmm1    # round 4
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x08
+       call _key_expansion_192b
+       # aeskeygenassist $0x10, %xmm2, %xmm1   # round 5
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x10
+       call _key_expansion_192a
+       # aeskeygenassist $0x20, %xmm2, %xmm1   # round 6
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x20
+       call _key_expansion_192b
+       # aeskeygenassist $0x40, %xmm2, %xmm1   # round 7
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x40
+       call _key_expansion_192a
+       # aeskeygenassist $0x80, %xmm2, %xmm1   # round 8
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x80
+       call _key_expansion_192b
+       jmp .Ldec_key
+.Lenc_key128:
+       # aeskeygenassist $0x1, %xmm0, %xmm1    # round 1
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x01
+       call _key_expansion_128
+       # aeskeygenassist $0x2, %xmm0, %xmm1    # round 2
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x02
+       call _key_expansion_128
+       # aeskeygenassist $0x4, %xmm0, %xmm1    # round 3
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x04
+       call _key_expansion_128
+       # aeskeygenassist $0x8, %xmm0, %xmm1    # round 4
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x08
+       call _key_expansion_128
+       # aeskeygenassist $0x10, %xmm0, %xmm1   # round 5
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x10
+       call _key_expansion_128
+       # aeskeygenassist $0x20, %xmm0, %xmm1   # round 6
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x20
+       call _key_expansion_128
+       # aeskeygenassist $0x40, %xmm0, %xmm1   # round 7
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x40
+       call _key_expansion_128
+       # aeskeygenassist $0x80, %xmm0, %xmm1   # round 8
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x80
+       call _key_expansion_128
+       # aeskeygenassist $0x1b, %xmm0, %xmm1   # round 9
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x1b
+       call _key_expansion_128
+       # aeskeygenassist $0x36, %xmm0, %xmm1   # round 10
+       .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x36
+       call _key_expansion_128
+.Ldec_key:
+       sub $0x10, %rcx
+       movaps (%rdi), %xmm0
+       movaps (%rcx), %xmm1
+       movaps %xmm0, 240(%rcx)
+       movaps %xmm1, 240(%rdi)
+       add $0x10, %rdi
+       lea 240-16(%rcx), %rsi
+.align 4
+.Ldec_key_loop:
+       movaps (%rdi), %xmm0
+       # aesimc %xmm0, %xmm1
+       .byte 0x66, 0x0f, 0x38, 0xdb, 0xc8
+       movaps %xmm1, (%rsi)
+       add $0x10, %rdi
+       sub $0x10, %rsi
+       cmp %rcx, %rdi
+       jb .Ldec_key_loop
+       xor %rax, %rax
+       ret
+
+/*
+ * void aesni_enc(struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src)
+ */
+ENTRY(aesni_enc)
+       movl 480(KEYP), KLEN            # key length
+       movups (INP), STATE             # input
+       call _aesni_enc1
+       movups STATE, (OUTP)            # output
+       ret
+
+/*
+ * _aesni_enc1:                internal ABI
+ * input:
+ *     KEYP:           key struct pointer
+ *     KLEN:           round count
+ *     STATE:          initial state (input)
+ * output:
+ *     STATE:          finial state (output)
+ * changed:
+ *     KEY
+ *     TKEYP (T1)
+ */
+_aesni_enc1:
+       movaps (KEYP), KEY              # key
+       mov KEYP, TKEYP
+       pxor KEY, STATE         # round 0
+       add $0x30, TKEYP
+       cmp $24, KLEN
+       jb .Lenc128
+       lea 0x20(TKEYP), TKEYP
+       je .Lenc192
+       add $0x20, TKEYP
+       movaps -0x60(TKEYP), KEY
+       # aesenc KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       movaps -0x50(TKEYP), KEY
+       # aesenc KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+.align 4
+.Lenc192:
+       movaps -0x40(TKEYP), KEY
+       # aesenc KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       movaps -0x30(TKEYP), KEY
+       # aesenc KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+.align 4
+.Lenc128:
+       movaps -0x20(TKEYP), KEY
+       # aesenc KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       movaps -0x10(TKEYP), KEY
+       # aesenc KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       movaps (TKEYP), KEY
+       # aesenc KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       movaps 0x10(TKEYP), KEY
+       # aesenc KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       movaps 0x20(TKEYP), KEY
+       # aesenc KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       movaps 0x30(TKEYP), KEY
+       # aesenc KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       movaps 0x40(TKEYP), KEY
+       # aesenc KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       movaps 0x50(TKEYP), KEY
+       # aesenc KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       movaps 0x60(TKEYP), KEY
+       # aesenc KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       movaps 0x70(TKEYP), KEY
+       # aesenclast KEY, STATE # last round
+       .byte 0x66, 0x0f, 0x38, 0xdd, 0xc2
+       ret
+
+/*
+ * _aesni_enc4:        internal ABI
+ * input:
+ *     KEYP:           key struct pointer
+ *     KLEN:           round count
+ *     STATE1:         initial state (input)
+ *     STATE2
+ *     STATE3
+ *     STATE4
+ * output:
+ *     STATE1:         finial state (output)
+ *     STATE2
+ *     STATE3
+ *     STATE4
+ * changed:
+ *     KEY
+ *     TKEYP (T1)
+ */
+_aesni_enc4:
+       movaps (KEYP), KEY              # key
+       mov KEYP, TKEYP
+       pxor KEY, STATE1                # round 0
+       pxor KEY, STATE2
+       pxor KEY, STATE3
+       pxor KEY, STATE4
+       add $0x30, TKEYP
+       cmp $24, KLEN
+       jb .L4enc128
+       lea 0x20(TKEYP), TKEYP
+       je .L4enc192
+       add $0x20, TKEYP
+       movaps -0x60(TKEYP), KEY
+       # aesenc KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       # aesenc KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+       # aesenc KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+       # aesenc KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+       movaps -0x50(TKEYP), KEY
+       # aesenc KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       # aesenc KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+       # aesenc KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+       # aesenc KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+#.align 4
+.L4enc192:
+       movaps -0x40(TKEYP), KEY
+       # aesenc KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       # aesenc KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+       # aesenc KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+       # aesenc KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+       movaps -0x30(TKEYP), KEY
+       # aesenc KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       # aesenc KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+       # aesenc KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+       # aesenc KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+#.align 4
+.L4enc128:
+       movaps -0x20(TKEYP), KEY
+       # aesenc KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       # aesenc KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+       # aesenc KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+       # aesenc KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+       movaps -0x10(TKEYP), KEY
+       # aesenc KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       # aesenc KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+       # aesenc KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+       # aesenc KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+       movaps (TKEYP), KEY
+       # aesenc KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       # aesenc KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+       # aesenc KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+       # aesenc KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+       movaps 0x10(TKEYP), KEY
+       # aesenc KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       # aesenc KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+       # aesenc KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+       # aesenc KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+       movaps 0x20(TKEYP), KEY
+       # aesenc KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       # aesenc KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+       # aesenc KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+       # aesenc KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+       movaps 0x30(TKEYP), KEY
+       # aesenc KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       # aesenc KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+       # aesenc KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+       # aesenc KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+       movaps 0x40(TKEYP), KEY
+       # aesenc KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       # aesenc KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+       # aesenc KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+       # aesenc KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+       movaps 0x50(TKEYP), KEY
+       # aesenc KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       # aesenc KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+       # aesenc KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+       # aesenc KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+       movaps 0x60(TKEYP), KEY
+       # aesenc KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+       # aesenc KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+       # aesenc KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+       # aesenc KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+       movaps 0x70(TKEYP), KEY
+       # aesenclast KEY, STATE1        # last round
+       .byte 0x66, 0x0f, 0x38, 0xdd, 0xc2
+       # aesenclast KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xdd, 0xe2
+       # aesenclast KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xdd, 0xea
+       # aesenclast KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xdd, 0xf2
+       ret
+
+/*
+ * void aesni_dec (struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src)
+ */
+ENTRY(aesni_dec)
+       mov 480(KEYP), KLEN             # key length
+       add $240, KEYP
+       movups (INP), STATE             # input
+       call _aesni_dec1
+       movups STATE, (OUTP)            #output
+       ret
+
+/*
+ * _aesni_dec1:                internal ABI
+ * input:
+ *     KEYP:           key struct pointer
+ *     KLEN:           key length
+ *     STATE:          initial state (input)
+ * output:
+ *     STATE:          finial state (output)
+ * changed:
+ *     KEY
+ *     TKEYP (T1)
+ */
+_aesni_dec1:
+       movaps (KEYP), KEY              # key
+       mov KEYP, TKEYP
+       pxor KEY, STATE         # round 0
+       add $0x30, TKEYP
+       cmp $24, KLEN
+       jb .Ldec128
+       lea 0x20(TKEYP), TKEYP
+       je .Ldec192
+       add $0x20, TKEYP
+       movaps -0x60(TKEYP), KEY
+       # aesdec KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       movaps -0x50(TKEYP), KEY
+       # aesdec KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+.align 4
+.Ldec192:
+       movaps -0x40(TKEYP), KEY
+       # aesdec KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       movaps -0x30(TKEYP), KEY
+       # aesdec KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+.align 4
+.Ldec128:
+       movaps -0x20(TKEYP), KEY
+       # aesdec KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       movaps -0x10(TKEYP), KEY
+       # aesdec KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       movaps (TKEYP), KEY
+       # aesdec KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       movaps 0x10(TKEYP), KEY
+       # aesdec KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       movaps 0x20(TKEYP), KEY
+       # aesdec KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       movaps 0x30(TKEYP), KEY
+       # aesdec KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       movaps 0x40(TKEYP), KEY
+       # aesdec KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       movaps 0x50(TKEYP), KEY
+       # aesdec KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       movaps 0x60(TKEYP), KEY
+       # aesdec KEY, STATE
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       movaps 0x70(TKEYP), KEY
+       # aesdeclast KEY, STATE         # last round
+       .byte 0x66, 0x0f, 0x38, 0xdf, 0xc2
+       ret
+
+/*
+ * _aesni_dec4:        internal ABI
+ * input:
+ *     KEYP:           key struct pointer
+ *     KLEN:           key length
+ *     STATE1:         initial state (input)
+ *     STATE2
+ *     STATE3
+ *     STATE4
+ * output:
+ *     STATE1:         finial state (output)
+ *     STATE2
+ *     STATE3
+ *     STATE4
+ * changed:
+ *     KEY
+ *     TKEYP (T1)
+ */
+_aesni_dec4:
+       movaps (KEYP), KEY              # key
+       mov KEYP, TKEYP
+       pxor KEY, STATE1                # round 0
+       pxor KEY, STATE2
+       pxor KEY, STATE3
+       pxor KEY, STATE4
+       add $0x30, TKEYP
+       cmp $24, KLEN
+       jb .L4dec128
+       lea 0x20(TKEYP), TKEYP
+       je .L4dec192
+       add $0x20, TKEYP
+       movaps -0x60(TKEYP), KEY
+       # aesdec KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       # aesdec KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+       # aesdec KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+       # aesdec KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+       movaps -0x50(TKEYP), KEY
+       # aesdec KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       # aesdec KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+       # aesdec KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+       # aesdec KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+.align 4
+.L4dec192:
+       movaps -0x40(TKEYP), KEY
+       # aesdec KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       # aesdec KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+       # aesdec KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+       # aesdec KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+       movaps -0x30(TKEYP), KEY
+       # aesdec KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       # aesdec KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+       # aesdec KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+       # aesdec KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+.align 4
+.L4dec128:
+       movaps -0x20(TKEYP), KEY
+       # aesdec KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       # aesdec KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+       # aesdec KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+       # aesdec KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+       movaps -0x10(TKEYP), KEY
+       # aesdec KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       # aesdec KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+       # aesdec KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+       # aesdec KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+       movaps (TKEYP), KEY
+       # aesdec KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       # aesdec KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+       # aesdec KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+       # aesdec KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+       movaps 0x10(TKEYP), KEY
+       # aesdec KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       # aesdec KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+       # aesdec KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+       # aesdec KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+       movaps 0x20(TKEYP), KEY
+       # aesdec KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       # aesdec KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+       # aesdec KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+       # aesdec KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+       movaps 0x30(TKEYP), KEY
+       # aesdec KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       # aesdec KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+       # aesdec KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+       # aesdec KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+       movaps 0x40(TKEYP), KEY
+       # aesdec KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       # aesdec KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+       # aesdec KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+       # aesdec KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+       movaps 0x50(TKEYP), KEY
+       # aesdec KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       # aesdec KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+       # aesdec KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+       # aesdec KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+       movaps 0x60(TKEYP), KEY
+       # aesdec KEY, STATE1
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+       # aesdec KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+       # aesdec KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+       # aesdec KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+       movaps 0x70(TKEYP), KEY
+       # aesdeclast KEY, STATE1        # last round
+       .byte 0x66, 0x0f, 0x38, 0xdf, 0xc2
+       # aesdeclast KEY, STATE2
+       .byte 0x66, 0x0f, 0x38, 0xdf, 0xe2
+       # aesdeclast KEY, STATE3
+       .byte 0x66, 0x0f, 0x38, 0xdf, 0xea
+       # aesdeclast KEY, STATE4
+       .byte 0x66, 0x0f, 0x38, 0xdf, 0xf2
+       ret
+
+/*
+ * void aesni_ecb_enc(struct crypto_aes_ctx *ctx, const u8 *dst, u8 *src,
+ *                   size_t len)
+ */
+ENTRY(aesni_ecb_enc)
+       test LEN, LEN           # check length
+       jz .Lecb_enc_ret
+       mov 480(KEYP), KLEN
+       cmp $16, LEN
+       jb .Lecb_enc_ret
+       cmp $64, LEN
+       jb .Lecb_enc_loop1
+.align 4
+.Lecb_enc_loop4:
+       movups (INP), STATE1
+       movups 0x10(INP), STATE2
+       movups 0x20(INP), STATE3
+       movups 0x30(INP), STATE4
+       call _aesni_enc4
+       movups STATE1, (OUTP)
+       movups STATE2, 0x10(OUTP)
+       movups STATE3, 0x20(OUTP)
+       movups STATE4, 0x30(OUTP)
+       sub $64, LEN
+       add $64, INP
+       add $64, OUTP
+       cmp $64, LEN
+       jge .Lecb_enc_loop4
+       cmp $16, LEN
+       jb .Lecb_enc_ret
+.align 4
+.Lecb_enc_loop1:
+       movups (INP), STATE1
+       call _aesni_enc1
+       movups STATE1, (OUTP)
+       sub $16, LEN
+       add $16, INP
+       add $16, OUTP
+       cmp $16, LEN
+       jge .Lecb_enc_loop1
+.Lecb_enc_ret:
+       ret
+
+/*
+ * void aesni_ecb_dec(struct crypto_aes_ctx *ctx, const u8 *dst, u8 *src,
+ *                   size_t len);
+ */
+ENTRY(aesni_ecb_dec)
+       test LEN, LEN
+       jz .Lecb_dec_ret
+       mov 480(KEYP), KLEN
+       add $240, KEYP
+       cmp $16, LEN
+       jb .Lecb_dec_ret
+       cmp $64, LEN
+       jb .Lecb_dec_loop1
+.align 4
+.Lecb_dec_loop4:
+       movups (INP), STATE1
+       movups 0x10(INP), STATE2
+       movups 0x20(INP), STATE3
+       movups 0x30(INP), STATE4
+       call _aesni_dec4
+       movups STATE1, (OUTP)
+       movups STATE2, 0x10(OUTP)
+       movups STATE3, 0x20(OUTP)
+       movups STATE4, 0x30(OUTP)
+       sub $64, LEN
+       add $64, INP
+       add $64, OUTP
+       cmp $64, LEN
+       jge .Lecb_dec_loop4
+       cmp $16, LEN
+       jb .Lecb_dec_ret
+.align 4
+.Lecb_dec_loop1:
+       movups (INP), STATE1
+       call _aesni_dec1
+       movups STATE1, (OUTP)
+       sub $16, LEN
+       add $16, INP
+       add $16, OUTP
+       cmp $16, LEN
+       jge .Lecb_dec_loop1
+.Lecb_dec_ret:
+       ret
+
+/*
+ * void aesni_cbc_enc(struct crypto_aes_ctx *ctx, const u8 *dst, u8 *src,
+ *                   size_t len, u8 *iv)
+ */
+ENTRY(aesni_cbc_enc)
+       cmp $16, LEN
+       jb .Lcbc_enc_ret
+       mov 480(KEYP), KLEN
+       movups (IVP), STATE     # load iv as initial state
+.align 4
+.Lcbc_enc_loop:
+       movups (INP), IN        # load input
+       pxor IN, STATE
+       call _aesni_enc1
+       movups STATE, (OUTP)    # store output
+       sub $16, LEN
+       add $16, INP
+       add $16, OUTP
+       cmp $16, LEN
+       jge .Lcbc_enc_loop
+       movups STATE, (IVP)
+.Lcbc_enc_ret:
+       ret
+
+/*
+ * void aesni_cbc_dec(struct crypto_aes_ctx *ctx, const u8 *dst, u8 *src,
+ *                   size_t len, u8 *iv)
+ */
+ENTRY(aesni_cbc_dec)
+       cmp $16, LEN
+       jb .Lcbc_dec_ret
+       mov 480(KEYP), KLEN
+       add $240, KEYP
+       movups (IVP), IV
+       cmp $64, LEN
+       jb .Lcbc_dec_loop1
+.align 4
+.Lcbc_dec_loop4:
+       movups (INP), IN1
+       movaps IN1, STATE1
+       movups 0x10(INP), IN2
+       movaps IN2, STATE2
+       movups 0x20(INP), IN3
+       movaps IN3, STATE3
+       movups 0x30(INP), IN4
+       movaps IN4, STATE4
+       call _aesni_dec4
+       pxor IV, STATE1
+       pxor IN1, STATE2
+       pxor IN2, STATE3
+       pxor IN3, STATE4
+       movaps IN4, IV
+       movups STATE1, (OUTP)
+       movups STATE2, 0x10(OUTP)
+       movups STATE3, 0x20(OUTP)
+       movups STATE4, 0x30(OUTP)
+       sub $64, LEN
+       add $64, INP
+       add $64, OUTP
+       cmp $64, LEN
+       jge .Lcbc_dec_loop4
+       cmp $16, LEN
+       jb .Lcbc_dec_ret
+.align 4
+.Lcbc_dec_loop1:
+       movups (INP), IN
+       movaps IN, STATE
+       call _aesni_dec1
+       pxor IV, STATE
+       movups STATE, (OUTP)
+       movaps IN, IV
+       sub $16, LEN
+       add $16, INP
+       add $16, OUTP
+       cmp $16, LEN
+       jge .Lcbc_dec_loop1
+       movups IV, (IVP)
+.Lcbc_dec_ret:
+       ret
diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c
new file mode 100644 (file)
index 0000000..02af0af
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * Support for Intel AES-NI instructions. This file contains glue
+ * code, the real AES implementation is in intel-aes_asm.S.
+ *
+ * Copyright (C) 2008, Intel Corp.
+ *    Author: Huang Ying <ying.huang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/hardirq.h>
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <linux/err.h>
+#include <crypto/algapi.h>
+#include <crypto/aes.h>
+#include <crypto/cryptd.h>
+#include <asm/i387.h>
+#include <asm/aes.h>
+
+struct async_aes_ctx {
+       struct cryptd_ablkcipher *cryptd_tfm;
+};
+
+#define AESNI_ALIGN    16
+#define AES_BLOCK_MASK (~(AES_BLOCK_SIZE-1))
+
+asmlinkage int aesni_set_key(struct crypto_aes_ctx *ctx, const u8 *in_key,
+                            unsigned int key_len);
+asmlinkage void aesni_enc(struct crypto_aes_ctx *ctx, u8 *out,
+                         const u8 *in);
+asmlinkage void aesni_dec(struct crypto_aes_ctx *ctx, u8 *out,
+                         const u8 *in);
+asmlinkage void aesni_ecb_enc(struct crypto_aes_ctx *ctx, u8 *out,
+                             const u8 *in, unsigned int len);
+asmlinkage void aesni_ecb_dec(struct crypto_aes_ctx *ctx, u8 *out,
+                             const u8 *in, unsigned int len);
+asmlinkage void aesni_cbc_enc(struct crypto_aes_ctx *ctx, u8 *out,
+                             const u8 *in, unsigned int len, u8 *iv);
+asmlinkage void aesni_cbc_dec(struct crypto_aes_ctx *ctx, u8 *out,
+                             const u8 *in, unsigned int len, u8 *iv);
+
+static inline int kernel_fpu_using(void)
+{
+       if (in_interrupt() && !(read_cr0() & X86_CR0_TS))
+               return 1;
+       return 0;
+}
+
+static inline struct crypto_aes_ctx *aes_ctx(void *raw_ctx)
+{
+       unsigned long addr = (unsigned long)raw_ctx;
+       unsigned long align = AESNI_ALIGN;
+
+       if (align <= crypto_tfm_ctx_alignment())
+               align = 1;
+       return (struct crypto_aes_ctx *)ALIGN(addr, align);
+}
+
+static int aes_set_key_common(struct crypto_tfm *tfm, void *raw_ctx,
+                             const u8 *in_key, unsigned int key_len)
+{
+       struct crypto_aes_ctx *ctx = aes_ctx(raw_ctx);
+       u32 *flags = &tfm->crt_flags;
+       int err;
+
+       if (key_len != AES_KEYSIZE_128 && key_len != AES_KEYSIZE_192 &&
+           key_len != AES_KEYSIZE_256) {
+               *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+               return -EINVAL;
+       }
+
+       if (kernel_fpu_using())
+               err = crypto_aes_expand_key(ctx, in_key, key_len);
+       else {
+               kernel_fpu_begin();
+               err = aesni_set_key(ctx, in_key, key_len);
+               kernel_fpu_end();
+       }
+
+       return err;
+}
+
+static int aes_set_key(struct crypto_tfm *tfm, const u8 *in_key,
+                      unsigned int key_len)
+{
+       return aes_set_key_common(tfm, crypto_tfm_ctx(tfm), in_key, key_len);
+}
+
+static void aes_encrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src)
+{
+       struct crypto_aes_ctx *ctx = aes_ctx(crypto_tfm_ctx(tfm));
+
+       if (kernel_fpu_using())
+               crypto_aes_encrypt_x86(ctx, dst, src);
+       else {
+               kernel_fpu_begin();
+               aesni_enc(ctx, dst, src);
+               kernel_fpu_end();
+       }
+}
+
+static void aes_decrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src)
+{
+       struct crypto_aes_ctx *ctx = aes_ctx(crypto_tfm_ctx(tfm));
+
+       if (kernel_fpu_using())
+               crypto_aes_decrypt_x86(ctx, dst, src);
+       else {
+               kernel_fpu_begin();
+               aesni_dec(ctx, dst, src);
+               kernel_fpu_end();
+       }
+}
+
+static struct crypto_alg aesni_alg = {
+       .cra_name               = "aes",
+       .cra_driver_name        = "aes-aesni",
+       .cra_priority           = 300,
+       .cra_flags              = CRYPTO_ALG_TYPE_CIPHER,
+       .cra_blocksize          = AES_BLOCK_SIZE,
+       .cra_ctxsize            = sizeof(struct crypto_aes_ctx)+AESNI_ALIGN-1,
+       .cra_alignmask          = 0,
+       .cra_module             = THIS_MODULE,
+       .cra_list               = LIST_HEAD_INIT(aesni_alg.cra_list),
+       .cra_u  = {
+               .cipher = {
+                       .cia_min_keysize        = AES_MIN_KEY_SIZE,
+                       .cia_max_keysize        = AES_MAX_KEY_SIZE,
+                       .cia_setkey             = aes_set_key,
+                       .cia_encrypt            = aes_encrypt,
+                       .cia_decrypt            = aes_decrypt
+               }
+       }
+};
+
+static int ecb_encrypt(struct blkcipher_desc *desc,
+                      struct scatterlist *dst, struct scatterlist *src,
+                      unsigned int nbytes)
+{
+       struct crypto_aes_ctx *ctx = aes_ctx(crypto_blkcipher_ctx(desc->tfm));
+       struct blkcipher_walk walk;
+       int err;
+
+       blkcipher_walk_init(&walk, dst, src, nbytes);
+       err = blkcipher_walk_virt(desc, &walk);
+
+       kernel_fpu_begin();
+       while ((nbytes = walk.nbytes)) {
+               aesni_ecb_enc(ctx, walk.dst.virt.addr, walk.src.virt.addr,
+                             nbytes & AES_BLOCK_MASK);
+               nbytes &= AES_BLOCK_SIZE - 1;
+               err = blkcipher_walk_done(desc, &walk, nbytes);
+       }
+       kernel_fpu_end();
+
+       return err;
+}
+
+static int ecb_decrypt(struct blkcipher_desc *desc,
+                      struct scatterlist *dst, struct scatterlist *src,
+                      unsigned int nbytes)
+{
+       struct crypto_aes_ctx *ctx = aes_ctx(crypto_blkcipher_ctx(desc->tfm));
+       struct blkcipher_walk walk;
+       int err;
+
+       blkcipher_walk_init(&walk, dst, src, nbytes);
+       err = blkcipher_walk_virt(desc, &walk);
+
+       kernel_fpu_begin();
+       while ((nbytes = walk.nbytes)) {
+               aesni_ecb_dec(ctx, walk.dst.virt.addr, walk.src.virt.addr,
+                             nbytes & AES_BLOCK_MASK);
+               nbytes &= AES_BLOCK_SIZE - 1;
+               err = blkcipher_walk_done(desc, &walk, nbytes);
+       }
+       kernel_fpu_end();
+
+       return err;
+}
+
+static struct crypto_alg blk_ecb_alg = {
+       .cra_name               = "__ecb-aes-aesni",
+       .cra_driver_name        = "__driver-ecb-aes-aesni",
+       .cra_priority           = 0,
+       .cra_flags              = CRYPTO_ALG_TYPE_BLKCIPHER,
+       .cra_blocksize          = AES_BLOCK_SIZE,
+       .cra_ctxsize            = sizeof(struct crypto_aes_ctx)+AESNI_ALIGN-1,
+       .cra_alignmask          = 0,
+       .cra_type               = &crypto_blkcipher_type,
+       .cra_module             = THIS_MODULE,
+       .cra_list               = LIST_HEAD_INIT(blk_ecb_alg.cra_list),
+       .cra_u = {
+               .blkcipher = {
+                       .min_keysize    = AES_MIN_KEY_SIZE,
+                       .max_keysize    = AES_MAX_KEY_SIZE,
+                       .setkey         = aes_set_key,
+                       .encrypt        = ecb_encrypt,
+                       .decrypt        = ecb_decrypt,
+               },
+       },
+};
+
+static int cbc_encrypt(struct blkcipher_desc *desc,
+                      struct scatterlist *dst, struct scatterlist *src,
+                      unsigned int nbytes)
+{
+       struct crypto_aes_ctx *ctx = aes_ctx(crypto_blkcipher_ctx(desc->tfm));
+       struct blkcipher_walk walk;
+       int err;
+
+       blkcipher_walk_init(&walk, dst, src, nbytes);
+       err = blkcipher_walk_virt(desc, &walk);
+
+       kernel_fpu_begin();
+       while ((nbytes = walk.nbytes)) {
+               aesni_cbc_enc(ctx, walk.dst.virt.addr, walk.src.virt.addr,
+                             nbytes & AES_BLOCK_MASK, walk.iv);
+               nbytes &= AES_BLOCK_SIZE - 1;
+               err = blkcipher_walk_done(desc, &walk, nbytes);
+       }
+       kernel_fpu_end();
+
+       return err;
+}
+
+static int cbc_decrypt(struct blkcipher_desc *desc,
+                      struct scatterlist *dst, struct scatterlist *src,
+                      unsigned int nbytes)
+{
+       struct crypto_aes_ctx *ctx = aes_ctx(crypto_blkcipher_ctx(desc->tfm));
+       struct blkcipher_walk walk;
+       int err;
+
+       blkcipher_walk_init(&walk, dst, src, nbytes);
+       err = blkcipher_walk_virt(desc, &walk);
+
+       kernel_fpu_begin();
+       while ((nbytes = walk.nbytes)) {
+               aesni_cbc_dec(ctx, walk.dst.virt.addr, walk.src.virt.addr,
+                             nbytes & AES_BLOCK_MASK, walk.iv);
+               nbytes &= AES_BLOCK_SIZE - 1;
+               err = blkcipher_walk_done(desc, &walk, nbytes);
+       }
+       kernel_fpu_end();
+
+       return err;
+}
+
+static struct crypto_alg blk_cbc_alg = {
+       .cra_name               = "__cbc-aes-aesni",
+       .cra_driver_name        = "__driver-cbc-aes-aesni",
+       .cra_priority           = 0,
+       .cra_flags              = CRYPTO_ALG_TYPE_BLKCIPHER,
+       .cra_blocksize          = AES_BLOCK_SIZE,
+       .cra_ctxsize            = sizeof(struct crypto_aes_ctx)+AESNI_ALIGN-1,
+       .cra_alignmask          = 0,
+       .cra_type               = &crypto_blkcipher_type,
+       .cra_module             = THIS_MODULE,
+       .cra_list               = LIST_HEAD_INIT(blk_cbc_alg.cra_list),
+       .cra_u = {
+               .blkcipher = {
+                       .min_keysize    = AES_MIN_KEY_SIZE,
+                       .max_keysize    = AES_MAX_KEY_SIZE,
+                       .setkey         = aes_set_key,
+                       .encrypt        = cbc_encrypt,
+                       .decrypt        = cbc_decrypt,
+               },
+       },
+};
+
+static int ablk_set_key(struct crypto_ablkcipher *tfm, const u8 *key,
+                       unsigned int key_len)
+{
+       struct async_aes_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+
+       return crypto_ablkcipher_setkey(&ctx->cryptd_tfm->base, key, key_len);
+}
+
+static int ablk_encrypt(struct ablkcipher_request *req)
+{
+       struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+       struct async_aes_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+
+       if (kernel_fpu_using()) {
+               struct ablkcipher_request *cryptd_req =
+                       ablkcipher_request_ctx(req);
+               memcpy(cryptd_req, req, sizeof(*req));
+               ablkcipher_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
+               return crypto_ablkcipher_encrypt(cryptd_req);
+       } else {
+               struct blkcipher_desc desc;
+               desc.tfm = cryptd_ablkcipher_child(ctx->cryptd_tfm);
+               desc.info = req->info;
+               desc.flags = 0;
+               return crypto_blkcipher_crt(desc.tfm)->encrypt(
+                       &desc, req->dst, req->src, req->nbytes);
+       }
+}
+
+static int ablk_decrypt(struct ablkcipher_request *req)
+{
+       struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+       struct async_aes_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+
+       if (kernel_fpu_using()) {
+               struct ablkcipher_request *cryptd_req =
+                       ablkcipher_request_ctx(req);
+               memcpy(cryptd_req, req, sizeof(*req));
+               ablkcipher_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
+               return crypto_ablkcipher_decrypt(cryptd_req);
+       } else {
+               struct blkcipher_desc desc;
+               desc.tfm = cryptd_ablkcipher_child(ctx->cryptd_tfm);
+               desc.info = req->info;
+               desc.flags = 0;
+               return crypto_blkcipher_crt(desc.tfm)->decrypt(
+                       &desc, req->dst, req->src, req->nbytes);
+       }
+}
+
+static void ablk_exit(struct crypto_tfm *tfm)
+{
+       struct async_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       cryptd_free_ablkcipher(ctx->cryptd_tfm);
+}
+
+static void ablk_init_common(struct crypto_tfm *tfm,
+                            struct cryptd_ablkcipher *cryptd_tfm)
+{
+       struct async_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       ctx->cryptd_tfm = cryptd_tfm;
+       tfm->crt_ablkcipher.reqsize = sizeof(struct ablkcipher_request) +
+               crypto_ablkcipher_reqsize(&cryptd_tfm->base);
+}
+
+static int ablk_ecb_init(struct crypto_tfm *tfm)
+{
+       struct cryptd_ablkcipher *cryptd_tfm;
+
+       cryptd_tfm = cryptd_alloc_ablkcipher("__driver-ecb-aes-aesni", 0, 0);
+       if (IS_ERR(cryptd_tfm))
+               return PTR_ERR(cryptd_tfm);
+       ablk_init_common(tfm, cryptd_tfm);
+       return 0;
+}
+
+static struct crypto_alg ablk_ecb_alg = {
+       .cra_name               = "ecb(aes)",
+       .cra_driver_name        = "ecb-aes-aesni",
+       .cra_priority           = 400,
+       .cra_flags              = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC,
+       .cra_blocksize          = AES_BLOCK_SIZE,
+       .cra_ctxsize            = sizeof(struct async_aes_ctx),
+       .cra_alignmask          = 0,
+       .cra_type               = &crypto_ablkcipher_type,
+       .cra_module             = THIS_MODULE,
+       .cra_list               = LIST_HEAD_INIT(ablk_ecb_alg.cra_list),
+       .cra_init               = ablk_ecb_init,
+       .cra_exit               = ablk_exit,
+       .cra_u = {
+               .ablkcipher = {
+                       .min_keysize    = AES_MIN_KEY_SIZE,
+                       .max_keysize    = AES_MAX_KEY_SIZE,
+                       .setkey         = ablk_set_key,
+                       .encrypt        = ablk_encrypt,
+                       .decrypt        = ablk_decrypt,
+               },
+       },
+};
+
+static int ablk_cbc_init(struct crypto_tfm *tfm)
+{
+       struct cryptd_ablkcipher *cryptd_tfm;
+
+       cryptd_tfm = cryptd_alloc_ablkcipher("__driver-cbc-aes-aesni", 0, 0);
+       if (IS_ERR(cryptd_tfm))
+               return PTR_ERR(cryptd_tfm);
+       ablk_init_common(tfm, cryptd_tfm);
+       return 0;
+}
+
+static struct crypto_alg ablk_cbc_alg = {
+       .cra_name               = "cbc(aes)",
+       .cra_driver_name        = "cbc-aes-aesni",
+       .cra_priority           = 400,
+       .cra_flags              = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC,
+       .cra_blocksize          = AES_BLOCK_SIZE,
+       .cra_ctxsize            = sizeof(struct async_aes_ctx),
+       .cra_alignmask          = 0,
+       .cra_type               = &crypto_ablkcipher_type,
+       .cra_module             = THIS_MODULE,
+       .cra_list               = LIST_HEAD_INIT(ablk_cbc_alg.cra_list),
+       .cra_init               = ablk_cbc_init,
+       .cra_exit               = ablk_exit,
+       .cra_u = {
+               .ablkcipher = {
+                       .min_keysize    = AES_MIN_KEY_SIZE,
+                       .max_keysize    = AES_MAX_KEY_SIZE,
+                       .ivsize         = AES_BLOCK_SIZE,
+                       .setkey         = ablk_set_key,
+                       .encrypt        = ablk_encrypt,
+                       .decrypt        = ablk_decrypt,
+               },
+       },
+};
+
+static int __init aesni_init(void)
+{
+       int err;
+
+       if (!cpu_has_aes) {
+               printk(KERN_ERR "Intel AES-NI instructions are not detected.\n");
+               return -ENODEV;
+       }
+       if ((err = crypto_register_alg(&aesni_alg)))
+               goto aes_err;
+       if ((err = crypto_register_alg(&blk_ecb_alg)))
+               goto blk_ecb_err;
+       if ((err = crypto_register_alg(&blk_cbc_alg)))
+               goto blk_cbc_err;
+       if ((err = crypto_register_alg(&ablk_ecb_alg)))
+               goto ablk_ecb_err;
+       if ((err = crypto_register_alg(&ablk_cbc_alg)))
+               goto ablk_cbc_err;
+
+       return err;
+
+ablk_cbc_err:
+       crypto_unregister_alg(&ablk_ecb_alg);
+ablk_ecb_err:
+       crypto_unregister_alg(&blk_cbc_alg);
+blk_cbc_err:
+       crypto_unregister_alg(&blk_ecb_alg);
+blk_ecb_err:
+       crypto_unregister_alg(&aesni_alg);
+aes_err:
+       return err;
+}
+
+static void __exit aesni_exit(void)
+{
+       crypto_unregister_alg(&ablk_cbc_alg);
+       crypto_unregister_alg(&ablk_ecb_alg);
+       crypto_unregister_alg(&blk_cbc_alg);
+       crypto_unregister_alg(&blk_ecb_alg);
+       crypto_unregister_alg(&aesni_alg);
+}
+
+module_init(aesni_init);
+module_exit(aesni_exit);
+
+MODULE_DESCRIPTION("Rijndael (AES) Cipher Algorithm, Intel AES-NI instructions optimized");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("aes");
diff --git a/arch/x86/include/asm/aes.h b/arch/x86/include/asm/aes.h
new file mode 100644 (file)
index 0000000..80545a1
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef ASM_X86_AES_H
+#define ASM_X86_AES_H
+
+#include <linux/crypto.h>
+#include <crypto/aes.h>
+
+void crypto_aes_encrypt_x86(struct crypto_aes_ctx *ctx, u8 *dst,
+                           const u8 *src);
+void crypto_aes_decrypt_x86(struct crypto_aes_ctx *ctx, u8 *dst,
+                           const u8 *src);
+#endif
index 7301e60dc4a83c56e28b6e8867ab6a15419347bf..0beba0d1468db24bceaa1e81cdf8c652eb5180ee 100644 (file)
@@ -213,6 +213,7 @@ extern const char * const x86_power_flags[32];
 #define cpu_has_xmm            boot_cpu_has(X86_FEATURE_XMM)
 #define cpu_has_xmm2           boot_cpu_has(X86_FEATURE_XMM2)
 #define cpu_has_xmm3           boot_cpu_has(X86_FEATURE_XMM3)
+#define cpu_has_aes            boot_cpu_has(X86_FEATURE_AES)
 #define cpu_has_ht             boot_cpu_has(X86_FEATURE_HT)
 #define cpu_has_mp             boot_cpu_has(X86_FEATURE_MP)
 #define cpu_has_nx             boot_cpu_has(X86_FEATURE_NX)
index 2bb6a835c453c0ced2d9b7e78e998e7fde5d8c3e..4f5c247248567708f0b4b599c8b618b12ddb458d 100644 (file)
@@ -11,8 +11,8 @@ unsigned long native_calibrate_tsc(void);
 
 #ifdef CONFIG_X86_32
 extern int timer_ack;
+#endif
 extern int recalibrate_cpu_khz(void);
-#endif /* CONFIG_X86_32 */
 
 extern int no_timer_check;
 
index 65792c2cc4625f7c44337ddb043bbcd92090aee5..52c839875478571711df3d861ffc1a3d4c5eb413 100644 (file)
@@ -87,30 +87,15 @@ config X86_POWERNOW_K7_ACPI
 config X86_POWERNOW_K8
        tristate "AMD Opteron/Athlon64 PowerNow!"
        select CPU_FREQ_TABLE
+       depends on ACPI && ACPI_PROCESSOR
        help
-         This adds the CPUFreq driver for mobile AMD Opteron/Athlon64 processors.
+         This adds the CPUFreq driver for K8/K10 Opteron/Athlon64 processors.
 
          To compile this driver as a module, choose M here: the
          module will be called powernow-k8.
 
          For details, take a look at <file:Documentation/cpu-freq/>.
 
-         If in doubt, say N.
-
-config X86_POWERNOW_K8_ACPI
-       bool
-       prompt "ACPI Support" if X86_32
-       depends on ACPI && X86_POWERNOW_K8 && ACPI_PROCESSOR
-       depends on !(X86_POWERNOW_K8 = y && ACPI_PROCESSOR = m)
-       default y
-       help
-         This provides access to the K8s Processor Performance States via ACPI.
-         This driver is probably required for CPUFreq to work with multi-socket and
-         SMP systems.  It is not required on at least some single-socket yet
-         multi-core systems, even if SMP is enabled.
-
-         It is safe to say Y here.
-
 config X86_GX_SUSPMOD
        tristate "Cyrix MediaGX/NatSemi Geode Suspend Modulation"
        depends on X86_32 && PCI
index 560f7760dae5c3dd18fa4b85845531bca0d30db4..509296df294d9432ed6459f8c6b3cccbbb04e1ef 100644 (file)
@@ -1,6 +1,11 @@
+# Link order matters. K8 is preferred to ACPI because of firmware bugs in early
+# K8 systems. ACPI is preferred to all other hardware-specific drivers.
+# speedstep-* is preferred over p4-clockmod.
+
+obj-$(CONFIG_X86_POWERNOW_K8)          += powernow-k8.o
+obj-$(CONFIG_X86_ACPI_CPUFREQ)         += acpi-cpufreq.o
 obj-$(CONFIG_X86_POWERNOW_K6)          += powernow-k6.o
 obj-$(CONFIG_X86_POWERNOW_K7)          += powernow-k7.o
-obj-$(CONFIG_X86_POWERNOW_K8)          += powernow-k8.o
 obj-$(CONFIG_X86_LONGHAUL)             += longhaul.o
 obj-$(CONFIG_X86_E_POWERSAVER)         += e_powersaver.o
 obj-$(CONFIG_ELAN_CPUFREQ)             += elanfreq.o
@@ -10,7 +15,6 @@ obj-$(CONFIG_X86_GX_SUSPMOD)          += gx-suspmod.o
 obj-$(CONFIG_X86_SPEEDSTEP_ICH)                += speedstep-ich.o
 obj-$(CONFIG_X86_SPEEDSTEP_LIB)                += speedstep-lib.o
 obj-$(CONFIG_X86_SPEEDSTEP_SMI)                += speedstep-smi.o
-obj-$(CONFIG_X86_ACPI_CPUFREQ)         += acpi-cpufreq.o
 obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO)   += speedstep-centrino.o
 obj-$(CONFIG_X86_P4_CLOCKMOD)          += p4-clockmod.o
 obj-$(CONFIG_X86_CPUFREQ_NFORCE2)      += cpufreq-nforce2.o
index 4b1c319d30c368592e990663fb1c8efd5abea963..3babe1f1e912eba061378087b5d4211022e4f772 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * acpi-cpufreq.c - ACPI Processor P-States Driver ($Revision: 1.4 $)
+ * acpi-cpufreq.c - ACPI Processor P-States Driver
  *
  *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
  *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
 #include <linux/ftrace.h>
 
 #include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+
 #include <acpi/processor.h>
 
-#include <asm/io.h>
 #include <asm/msr.h>
 #include <asm/processor.h>
 #include <asm/cpufeature.h>
-#include <asm/delay.h>
-#include <asm/uaccess.h>
 
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "acpi-cpufreq", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+               "acpi-cpufreq", msg)
 
 MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski");
 MODULE_DESCRIPTION("ACPI Processor P-States Driver");
@@ -95,7 +97,7 @@ static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data)
 
        perf = data->acpi_data;
 
-       for (i=0; i<perf->state_count; i++) {
+       for (i = 0; i < perf->state_count; i++) {
                if (value == perf->states[i].status)
                        return data->freq_table[i].frequency;
        }
@@ -110,7 +112,7 @@ static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data)
        msr &= INTEL_MSR_RANGE;
        perf = data->acpi_data;
 
-       for (i=0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
+       for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
                if (msr == perf->states[data->freq_table[i].index].status)
                        return data->freq_table[i].frequency;
        }
@@ -138,15 +140,13 @@ struct io_addr {
        u8 bit_width;
 };
 
-typedef union {
-       struct msr_addr msr;
-       struct io_addr io;
-} drv_addr_union;
-
 struct drv_cmd {
        unsigned int type;
        const struct cpumask *mask;
-       drv_addr_union addr;
+       union {
+               struct msr_addr msr;
+               struct io_addr io;
+       } addr;
        u32 val;
 };
 
@@ -369,7 +369,7 @@ static unsigned int check_freqs(const struct cpumask *mask, unsigned int freq,
        unsigned int cur_freq;
        unsigned int i;
 
-       for (i=0; i<100; i++) {
+       for (i = 0; i < 100; i++) {
                cur_freq = extract_freq(get_cur_val(mask), data);
                if (cur_freq == freq)
                        return 1;
@@ -494,7 +494,7 @@ acpi_cpufreq_guess_freq(struct acpi_cpufreq_data *data, unsigned int cpu)
                unsigned long freq;
                unsigned long freqn = perf->states[0].core_frequency * 1000;
 
-               for (i=0; i<(perf->state_count-1); i++) {
+               for (i = 0; i < (perf->state_count-1); i++) {
                        freq = freqn;
                        freqn = perf->states[i+1].core_frequency * 1000;
                        if ((2 * cpu_khz) > (freqn + freq)) {
@@ -673,7 +673,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
 
        /* detect transition latency */
        policy->cpuinfo.transition_latency = 0;
-       for (i=0; i<perf->state_count; i++) {
+       for (i = 0; i < perf->state_count; i++) {
                if ((perf->states[i].transition_latency * 1000) >
                    policy->cpuinfo.transition_latency)
                        policy->cpuinfo.transition_latency =
@@ -682,8 +682,8 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
 
        data->max_freq = perf->states[0].core_frequency * 1000;
        /* table init */
-       for (i=0; i<perf->state_count; i++) {
-               if (i>0 && perf->states[i].core_frequency >=
+       for (i = 0; i < perf->state_count; i++) {
+               if (i > 0 && perf->states[i].core_frequency >=
                    data->freq_table[valid_states-1].frequency / 1000)
                        continue;
 
index 965ea52767ac65f5059673a3448764103a0e8543..733093d6043698ecba3eca01249b3d301f6008b2 100644 (file)
@@ -32,7 +32,7 @@
  * nforce2_chipset:
  * FSB is changed using the chipset
  */
-static struct pci_dev *nforce2_chipset_dev;
+static struct pci_dev *nforce2_dev;
 
 /* fid:
  * multiplier * 10
@@ -56,7 +56,9 @@ MODULE_PARM_DESC(fid, "CPU multiplier to use (11.5 = 115)");
 MODULE_PARM_DESC(min_fsb,
                "Minimum FSB to use, if not defined: current FSB - 50");
 
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "cpufreq-nforce2", msg)
+#define PFX "cpufreq-nforce2: "
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+               "cpufreq-nforce2", msg)
 
 /**
  * nforce2_calc_fsb - calculate FSB
@@ -118,11 +120,11 @@ static void nforce2_write_pll(int pll)
        int temp;
 
        /* Set the pll addr. to 0x00 */
-       pci_write_config_dword(nforce2_chipset_dev, NFORCE2_PLLADR, 0);
+       pci_write_config_dword(nforce2_dev, NFORCE2_PLLADR, 0);
 
        /* Now write the value in all 64 registers */
        for (temp = 0; temp <= 0x3f; temp++)
-               pci_write_config_dword(nforce2_chipset_dev, NFORCE2_PLLREG, pll);
+               pci_write_config_dword(nforce2_dev, NFORCE2_PLLREG, pll);
 
        return;
 }
@@ -139,8 +141,8 @@ static unsigned int nforce2_fsb_read(int bootfsb)
        u32 fsb, temp = 0;
 
        /* Get chipset boot FSB from subdevice 5 (FSB at boot-time) */
-       nforce2_sub5 = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
-                                               0x01EF, PCI_ANY_ID, PCI_ANY_ID, NULL);
+       nforce2_sub5 = pci_get_subsys(PCI_VENDOR_ID_NVIDIA, 0x01EF,
+                               PCI_ANY_ID, PCI_ANY_ID, NULL);
        if (!nforce2_sub5)
                return 0;
 
@@ -148,13 +150,13 @@ static unsigned int nforce2_fsb_read(int bootfsb)
        fsb /= 1000000;
 
        /* Check if PLL register is already set */
-       pci_read_config_byte(nforce2_chipset_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
+       pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
 
        if (bootfsb || !temp)
                return fsb;
 
        /* Use PLL register FSB value */
-       pci_read_config_dword(nforce2_chipset_dev, NFORCE2_PLLREG, &temp);
+       pci_read_config_dword(nforce2_dev, NFORCE2_PLLREG, &temp);
        fsb = nforce2_calc_fsb(temp);
 
        return fsb;
@@ -174,18 +176,18 @@ static int nforce2_set_fsb(unsigned int fsb)
        int pll = 0;
 
        if ((fsb > max_fsb) || (fsb < NFORCE2_MIN_FSB)) {
-               printk(KERN_ERR "cpufreq: FSB %d is out of range!\n", fsb);
+               printk(KERN_ERR PFX "FSB %d is out of range!\n", fsb);
                return -EINVAL;
        }
 
        tfsb = nforce2_fsb_read(0);
        if (!tfsb) {
-               printk(KERN_ERR "cpufreq: Error while reading the FSB\n");
+               printk(KERN_ERR PFX "Error while reading the FSB\n");
                return -EINVAL;
        }
 
        /* First write? Then set actual value */
-       pci_read_config_byte(nforce2_chipset_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
+       pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
        if (!temp) {
                pll = nforce2_calc_pll(tfsb);
 
@@ -197,7 +199,7 @@ static int nforce2_set_fsb(unsigned int fsb)
 
        /* Enable write access */
        temp = 0x01;
-       pci_write_config_byte(nforce2_chipset_dev, NFORCE2_PLLENABLE, (u8)temp);
+       pci_write_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8)temp);
 
        diff = tfsb - fsb;
 
@@ -222,7 +224,7 @@ static int nforce2_set_fsb(unsigned int fsb)
        }
 
        temp = 0x40;
-       pci_write_config_byte(nforce2_chipset_dev, NFORCE2_PLLADR, (u8)temp);
+       pci_write_config_byte(nforce2_dev, NFORCE2_PLLADR, (u8)temp);
 
        return 0;
 }
@@ -244,7 +246,8 @@ static unsigned int nforce2_get(unsigned int cpu)
  * nforce2_target - set a new CPUFreq policy
  * @policy: new policy
  * @target_freq: the target frequency
- * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
+ * @relation: how that frequency relates to achieved frequency
+ *  (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
  *
  * Sets a new CPUFreq policy.
  */
@@ -276,7 +279,7 @@ static int nforce2_target(struct cpufreq_policy *policy,
        /* local_irq_save(flags); */
 
        if (nforce2_set_fsb(target_fsb) < 0)
-               printk(KERN_ERR "cpufreq: Changing FSB to %d failed\n",
+               printk(KERN_ERR PFX "Changing FSB to %d failed\n",
                        target_fsb);
        else
                dprintk("Changed FSB successfully to %d\n",
@@ -327,8 +330,8 @@ static int nforce2_cpu_init(struct cpufreq_policy *policy)
        /* FIX: Get FID from CPU */
        if (!fid) {
                if (!cpu_khz) {
-                       printk(KERN_WARNING
-                              "cpufreq: cpu_khz not set, can't calculate multiplier!\n");
+                       printk(KERN_WARNING PFX
+                       "cpu_khz not set, can't calculate multiplier!\n");
                        return -ENODEV;
                }
 
@@ -343,7 +346,7 @@ static int nforce2_cpu_init(struct cpufreq_policy *policy)
                }
        }
 
-       printk(KERN_INFO "cpufreq: FSB currently at %i MHz, FID %d.%d\n", fsb,
+       printk(KERN_INFO PFX "FSB currently at %i MHz, FID %d.%d\n", fsb,
               fid / 10, fid % 10);
 
        /* Set maximum FSB to FSB at boot time */
@@ -392,17 +395,18 @@ static struct cpufreq_driver nforce2_driver = {
  */
 static unsigned int nforce2_detect_chipset(void)
 {
-       nforce2_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
+       nforce2_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
                                        PCI_DEVICE_ID_NVIDIA_NFORCE2,
                                        PCI_ANY_ID, PCI_ANY_ID, NULL);
 
-       if (nforce2_chipset_dev == NULL)
+       if (nforce2_dev == NULL)
                return -ENODEV;
 
-       printk(KERN_INFO "cpufreq: Detected nForce2 chipset revision %X\n",
-              nforce2_chipset_dev->revision);
-       printk(KERN_INFO
-              "cpufreq: FSB changing is maybe unstable and can lead to crashes and data loss.\n");
+       printk(KERN_INFO PFX "Detected nForce2 chipset revision %X\n",
+              nforce2_dev->revision);
+       printk(KERN_INFO PFX
+              "FSB changing is maybe unstable and can lead to "
+              "crashes and data loss.\n");
 
        return 0;
 }
@@ -420,7 +424,7 @@ static int __init nforce2_init(void)
 
        /* detect chipset */
        if (nforce2_detect_chipset()) {
-               printk(KERN_ERR "cpufreq: No nForce2 chipset.\n");
+               printk(KERN_INFO PFX "No nForce2 chipset.\n");
                return -ENODEV;
        }
 
index c2f930d8664091932c1a7b614d860f8f365b7f10..3f83ea12c47a5972c2a32e3f5750e6d79fa9f105 100644 (file)
 #include <linux/cpufreq.h>
 #include <linux/ioport.h>
 #include <linux/slab.h>
+#include <linux/timex.h>
+#include <linux/io.h>
+#include <linux/delay.h>
 
 #include <asm/msr.h>
 #include <asm/tsc.h>
-#include <asm/timex.h>
-#include <asm/io.h>
-#include <asm/delay.h>
 
 #define EPS_BRAND_C7M  0
 #define EPS_BRAND_C7   1
@@ -184,7 +184,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
                break;
        }
 
-       switch(brand) {
+       switch (brand) {
        case EPS_BRAND_C7M:
                printk(KERN_CONT "C7-M\n");
                break;
@@ -218,17 +218,20 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
        /* Print voltage and multiplier */
        rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
        current_voltage = lo & 0xff;
-       printk(KERN_INFO "eps: Current voltage = %dmV\n", current_voltage * 16 + 700);
+       printk(KERN_INFO "eps: Current voltage = %dmV\n",
+                       current_voltage * 16 + 700);
        current_multiplier = (lo >> 8) & 0xff;
        printk(KERN_INFO "eps: Current multiplier = %d\n", current_multiplier);
 
        /* Print limits */
        max_voltage = hi & 0xff;
-       printk(KERN_INFO "eps: Highest voltage = %dmV\n", max_voltage * 16 + 700);
+       printk(KERN_INFO "eps: Highest voltage = %dmV\n",
+                       max_voltage * 16 + 700);
        max_multiplier = (hi >> 8) & 0xff;
        printk(KERN_INFO "eps: Highest multiplier = %d\n", max_multiplier);
        min_voltage = (hi >> 16) & 0xff;
-       printk(KERN_INFO "eps: Lowest voltage = %dmV\n", min_voltage * 16 + 700);
+       printk(KERN_INFO "eps: Lowest voltage = %dmV\n",
+                       min_voltage * 16 + 700);
        min_multiplier = (hi >> 24) & 0xff;
        printk(KERN_INFO "eps: Lowest multiplier = %d\n", min_multiplier);
 
@@ -318,7 +321,7 @@ static int eps_cpu_exit(struct cpufreq_policy *policy)
        return 0;
 }
 
-static struct freq_attreps_attr[] = {
+static struct freq_attr *eps_attr[] = {
        &cpufreq_freq_attr_scaling_available_freqs,
        NULL,
 };
@@ -356,7 +359,7 @@ static void __exit eps_exit(void)
        cpufreq_unregister_driver(&eps_driver);
 }
 
-MODULE_AUTHOR("Rafa³ Bilski <rafalbilski@interia.pl>");
+MODULE_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>");
 MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");
 MODULE_LICENSE("GPL");
 
index fe613c93b3667d73e78ca6747f691232a9233702..006b278b0d5d973f794e3c0f17a0e9f925369f52 100644 (file)
@@ -184,7 +184,8 @@ static int elanfreq_target(struct cpufreq_policy *policy,
 {
        unsigned int newstate = 0;
 
-       if (cpufreq_frequency_table_target(policy, &elanfreq_table[0], target_freq, relation, &newstate))
+       if (cpufreq_frequency_table_target(policy, &elanfreq_table[0],
+                               target_freq, relation, &newstate))
                return -EINVAL;
 
        elanfreq_set_cpu_state(newstate);
@@ -301,7 +302,8 @@ static void __exit elanfreq_exit(void)
 module_param(max_freq, int, 0444);
 
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Robert Schwebel <r.schwebel@pengutronix.de>, Sven Geggus <sven@geggus.net>");
+MODULE_AUTHOR("Robert Schwebel <r.schwebel@pengutronix.de>, "
+               "Sven Geggus <sven@geggus.net>");
 MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs");
 
 module_init(elanfreq_init);
index 9d9eae82e60fae3baec7333269ddb6c46b7d1d27..ac27ec2264d50afd3601ae7a5a10caf93bb94858 100644 (file)
@@ -79,8 +79,9 @@
 #include <linux/smp.h>
 #include <linux/cpufreq.h>
 #include <linux/pci.h>
+#include <linux/errno.h>
+
 #include <asm/processor-cyrix.h>
-#include <asm/errno.h>
 
 /* PCI config registers, all at F0 */
 #define PCI_PMER1      0x80    /* power management enable register 1 */
@@ -122,8 +123,8 @@ static struct gxfreq_params *gx_params;
 static int stock_freq;
 
 /* PCI bus clock - defaults to 30.000 if cpu_khz is not available */
-static int pci_busclk = 0;
-module_param (pci_busclk, int, 0444);
+static int pci_busclk;
+module_param(pci_busclk, int, 0444);
 
 /* maximum duration for which the cpu may be suspended
  * (32us * MAX_DURATION). If no parameter is given, this defaults
@@ -132,7 +133,7 @@ module_param (pci_busclk, int, 0444);
  * is suspended -- processing power is just 0.39% of what it used to be,
  * though. 781.25 kHz(!) for a 200 MHz processor -- wow. */
 static int max_duration = 255;
-module_param (max_duration, int, 0444);
+module_param(max_duration, int, 0444);
 
 /* For the default policy, we want at least some processing power
  * - let's say 5%. (min = maxfreq / POLICY_MIN_DIV)
@@ -140,7 +141,8 @@ module_param (max_duration, int, 0444);
 #define POLICY_MIN_DIV 20
 
 
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "gx-suspmod", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+               "gx-suspmod", msg)
 
 /**
  * we can detect a core multipiler from dir0_lsb
@@ -166,12 +168,20 @@ static int gx_freq_mult[16] = {
  *     Low Level chipset interface                             *
  ****************************************************************/
 static struct pci_device_id gx_chipset_tbl[] __initdata = {
-       { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, PCI_ANY_ID, PCI_ANY_ID },
-       { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, PCI_ANY_ID, PCI_ANY_ID },
-       { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510, PCI_ANY_ID, PCI_ANY_ID },
+       { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510,
+               PCI_ANY_ID, PCI_ANY_ID },
        { 0, },
 };
 
+static void gx_write_byte(int reg, int value)
+{
+       pci_write_config_byte(gx_params->cs55x0, reg, value);
+}
+
 /**
  * gx_detect_chipset:
  *
@@ -200,7 +210,8 @@ static __init struct pci_dev *gx_detect_chipset(void)
 /**
  * gx_get_cpuspeed:
  *
- * Finds out at which efficient frequency the Cyrix MediaGX/NatSemi Geode CPU runs.
+ * Finds out at which efficient frequency the Cyrix MediaGX/NatSemi
+ * Geode CPU runs.
  */
 static unsigned int gx_get_cpuspeed(unsigned int cpu)
 {
@@ -217,17 +228,18 @@ static unsigned int gx_get_cpuspeed(unsigned int cpu)
  *
  **/
 
-static unsigned int gx_validate_speed(unsigned int khz, u8 *on_duration, u8 *off_duration)
+static unsigned int gx_validate_speed(unsigned int khz, u8 *on_duration,
+               u8 *off_duration)
 {
        unsigned int i;
        u8 tmp_on, tmp_off;
        int old_tmp_freq = stock_freq;
        int tmp_freq;
 
-       *off_duration=1;
-       *on_duration=0;
+       *off_duration = 1;
+       *on_duration = 0;
 
-       for (i=max_duration; i>0; i--) {
+       for (i = max_duration; i > 0; i--) {
                tmp_off = ((khz * i) / stock_freq) & 0xff;
                tmp_on = i - tmp_off;
                tmp_freq = (stock_freq * tmp_off) / i;
@@ -259,26 +271,34 @@ static void gx_set_cpuspeed(unsigned int khz)
        freqs.cpu = 0;
        freqs.old = gx_get_cpuspeed(0);
 
-       new_khz = gx_validate_speed(khz, &gx_params->on_duration, &gx_params->off_duration);
+       new_khz = gx_validate_speed(khz, &gx_params->on_duration,
+                       &gx_params->off_duration);
 
        freqs.new = new_khz;
 
        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
        local_irq_save(flags);
 
-       if (new_khz != stock_freq) {  /* if new khz == 100% of CPU speed, it is special case */
+
+
+       if (new_khz != stock_freq) {
+               /* if new khz == 100% of CPU speed, it is special case */
                switch (gx_params->cs55x0->device) {
                case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
                        pmer1 = gx_params->pci_pmer1 | IRQ_SPDUP | VID_SPDUP;
                        /* FIXME: need to test other values -- Zwane,Miura */
-                       pci_write_config_byte(gx_params->cs55x0, PCI_IRQTC, 4); /* typical 2 to 4ms */
-                       pci_write_config_byte(gx_params->cs55x0, PCI_VIDTC, 100);/* typical 50 to 100ms */
-                       pci_write_config_byte(gx_params->cs55x0, PCI_PMER1, pmer1);
-
-                       if (gx_params->cs55x0->revision < 0x10) {   /* CS5530(rev 1.2, 1.3) */
-                               suscfg = gx_params->pci_suscfg | SUSMOD;
-                       } else {                           /* CS5530A,B.. */
-                               suscfg = gx_params->pci_suscfg | SUSMOD | PWRSVE;
+                       /* typical 2 to 4ms */
+                       gx_write_byte(PCI_IRQTC, 4);
+                       /* typical 50 to 100ms */
+                       gx_write_byte(PCI_VIDTC, 100);
+                       gx_write_byte(PCI_PMER1, pmer1);
+
+                       if (gx_params->cs55x0->revision < 0x10) {
+                               /* CS5530(rev 1.2, 1.3) */
+                               suscfg = gx_params->pci_suscfg|SUSMOD;
+                       } else {
+                               /* CS5530A,B.. */
+                               suscfg = gx_params->pci_suscfg|SUSMOD|PWRSVE;
                        }
                        break;
                case PCI_DEVICE_ID_CYRIX_5520:
@@ -294,13 +314,13 @@ static void gx_set_cpuspeed(unsigned int khz)
                suscfg = gx_params->pci_suscfg & ~(SUSMOD);
                gx_params->off_duration = 0;
                gx_params->on_duration = 0;
-               dprintk("suspend modulation disabled: cpu runs 100 percent speed.\n");
+               dprintk("suspend modulation disabled: cpu runs 100%% speed.\n");
        }
 
-       pci_write_config_byte(gx_params->cs55x0, PCI_MODOFF, gx_params->off_duration);
-       pci_write_config_byte(gx_params->cs55x0, PCI_MODON, gx_params->on_duration);
+       gx_write_byte(PCI_MODOFF, gx_params->off_duration);
+       gx_write_byte(PCI_MODON, gx_params->on_duration);
 
-       pci_write_config_byte(gx_params->cs55x0, PCI_SUSCFG, suscfg);
+       gx_write_byte(PCI_SUSCFG, suscfg);
        pci_read_config_byte(gx_params->cs55x0, PCI_SUSCFG, &suscfg);
 
        local_irq_restore(flags);
@@ -334,7 +354,8 @@ static int cpufreq_gx_verify(struct cpufreq_policy *policy)
                return -EINVAL;
 
        policy->cpu = 0;
-       cpufreq_verify_within_limits(policy, (stock_freq / max_duration), stock_freq);
+       cpufreq_verify_within_limits(policy, (stock_freq / max_duration),
+                       stock_freq);
 
        /* it needs to be assured that at least one supported frequency is
         * within policy->min and policy->max. If it is not, policy->max
@@ -354,7 +375,8 @@ static int cpufreq_gx_verify(struct cpufreq_policy *policy)
        policy->max = tmp_freq;
        if (policy->max < policy->min)
                policy->max = policy->min;
-       cpufreq_verify_within_limits(policy, (stock_freq / max_duration), stock_freq);
+       cpufreq_verify_within_limits(policy, (stock_freq / max_duration),
+                       stock_freq);
 
        return 0;
 }
@@ -398,18 +420,18 @@ static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy)
                return -ENODEV;
 
        /* determine maximum frequency */
-       if (pci_busclk) {
+       if (pci_busclk)
                maxfreq = pci_busclk * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
-       } else if (cpu_khz) {
+       else if (cpu_khz)
                maxfreq = cpu_khz;
-       } else {
+       else
                maxfreq = 30000 * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
-       }
+
        stock_freq = maxfreq;
        curfreq = gx_get_cpuspeed(0);
 
        dprintk("cpu max frequency is %d.\n", maxfreq);
-       dprintk("cpu current frequency is %dkHz.\n",curfreq);
+       dprintk("cpu current frequency is %dkHz.\n", curfreq);
 
        /* setup basic struct for cpufreq API */
        policy->cpu = 0;
@@ -447,7 +469,8 @@ static int __init cpufreq_gx_init(void)
        struct pci_dev *gx_pci;
 
        /* Test if we have the right hardware */
-       if ((gx_pci = gx_detect_chipset()) == NULL)
+       gx_pci = gx_detect_chipset();
+       if (gx_pci == NULL)
                return -ENODEV;
 
        /* check whether module parameters are sane */
@@ -468,9 +491,11 @@ static int __init cpufreq_gx_init(void)
        pci_read_config_byte(params->cs55x0, PCI_PMER1, &(params->pci_pmer1));
        pci_read_config_byte(params->cs55x0, PCI_PMER2, &(params->pci_pmer2));
        pci_read_config_byte(params->cs55x0, PCI_MODON, &(params->on_duration));
-       pci_read_config_byte(params->cs55x0, PCI_MODOFF, &(params->off_duration));
+       pci_read_config_byte(params->cs55x0, PCI_MODOFF,
+                       &(params->off_duration));
 
-       if ((ret = cpufreq_register_driver(&gx_suspmod_driver))) {
+       ret = cpufreq_register_driver(&gx_suspmod_driver);
+       if (ret) {
                kfree(params);
                return ret;                   /* register error! */
        }
@@ -485,9 +510,9 @@ static void __exit cpufreq_gx_exit(void)
        kfree(gx_params);
 }
 
-MODULE_AUTHOR ("Hiroshi Miura <miura@da-cha.org>");
-MODULE_DESCRIPTION ("Cpufreq driver for Cyrix MediaGX and NatSemi Geode");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Hiroshi Miura <miura@da-cha.org>");
+MODULE_DESCRIPTION("Cpufreq driver for Cyrix MediaGX and NatSemi Geode");
+MODULE_LICENSE("GPL");
 
 module_init(cpufreq_gx_init);
 module_exit(cpufreq_gx_exit);
index a4cff5d6e380fa8b027eaf3413f1911fbe821599..f1c51aea064df73fb28c3793da94c080703d4580 100644 (file)
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/delay.h>
+#include <linux/timex.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+#include <linux/kernel.h>
 
 #include <asm/msr.h>
-#include <asm/timex.h>
-#include <asm/io.h>
-#include <asm/acpi.h>
-#include <linux/acpi.h>
 #include <acpi/processor.h>
 
 #include "longhaul.h"
@@ -58,7 +58,7 @@
 #define USE_NORTHBRIDGE                (1 << 2)
 
 static int cpu_model;
-static unsigned int numscales=16;
+static unsigned int numscales = 16;
 static unsigned int fsb;
 
 static const struct mV_pos *vrm_mV_table;
@@ -67,8 +67,8 @@ static const unsigned char *mV_vrm_table;
 static unsigned int highest_speed, lowest_speed; /* kHz */
 static unsigned int minmult, maxmult;
 static int can_scale_voltage;
-static struct acpi_processor *pr = NULL;
-static struct acpi_processor_cx *cx = NULL;
+static struct acpi_processor *pr;
+static struct acpi_processor_cx *cx;
 static u32 acpi_regs_addr;
 static u8 longhaul_flags;
 static unsigned int longhaul_index;
@@ -78,12 +78,13 @@ static int scale_voltage;
 static int disable_acpi_c3;
 static int revid_errata;
 
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "longhaul", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+               "longhaul", msg)
 
 
 /* Clock ratios multiplied by 10 */
-static int clock_ratio[32];
-static int eblcr_table[32];
+static int mults[32];
+static int eblcr[32];
 static int longhaul_version;
 static struct cpufreq_frequency_table *longhaul_table;
 
@@ -93,7 +94,7 @@ static char speedbuffer[8];
 static char *print_speed(int speed)
 {
        if (speed < 1000) {
-               snprintf(speedbuffer, sizeof(speedbuffer),"%dMHz", speed);
+               snprintf(speedbuffer, sizeof(speedbuffer), "%dMHz", speed);
                return speedbuffer;
        }
 
@@ -122,27 +123,28 @@ static unsigned int calc_speed(int mult)
 
 static int longhaul_get_cpu_mult(void)
 {
-       unsigned long invalue=0,lo, hi;
+       unsigned long invalue = 0, lo, hi;
 
-       rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi);
-       invalue = (lo & (1<<22|1<<23|1<<24|1<<25)) >>22;
-       if (longhaul_version==TYPE_LONGHAUL_V2 || longhaul_version==TYPE_POWERSAVER) {
+       rdmsr(MSR_IA32_EBL_CR_POWERON, lo, hi);
+       invalue = (lo & (1<<22|1<<23|1<<24|1<<25))>>22;
+       if (longhaul_version == TYPE_LONGHAUL_V2 ||
+           longhaul_version == TYPE_POWERSAVER) {
                if (lo & (1<<27))
-                       invalue+=16;
+                       invalue += 16;
        }
-       return eblcr_table[invalue];
+       return eblcr[invalue];
 }
 
 /* For processor with BCR2 MSR */
 
-static void do_longhaul1(unsigned int clock_ratio_index)
+static void do_longhaul1(unsigned int mults_index)
 {
        union msr_bcr2 bcr2;
 
        rdmsrl(MSR_VIA_BCR2, bcr2.val);
        /* Enable software clock multiplier */
        bcr2.bits.ESOFTBF = 1;
-       bcr2.bits.CLOCKMUL = clock_ratio_index & 0xff;
+       bcr2.bits.CLOCKMUL = mults_index & 0xff;
 
        /* Sync to timer tick */
        safe_halt();
@@ -161,7 +163,7 @@ static void do_longhaul1(unsigned int clock_ratio_index)
 
 /* For processor with Longhaul MSR */
 
-static void do_powersaver(int cx_address, unsigned int clock_ratio_index,
+static void do_powersaver(int cx_address, unsigned int mults_index,
                          unsigned int dir)
 {
        union msr_longhaul longhaul;
@@ -173,11 +175,11 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index,
                longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
        else
                longhaul.bits.RevisionKey = 0;
-       longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf;
-       longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
+       longhaul.bits.SoftBusRatio = mults_index & 0xf;
+       longhaul.bits.SoftBusRatio4 = (mults_index & 0x10) >> 4;
        /* Setup new voltage */
        if (can_scale_voltage)
-               longhaul.bits.SoftVID = (clock_ratio_index >> 8) & 0x1f;
+               longhaul.bits.SoftVID = (mults_index >> 8) & 0x1f;
        /* Sync to timer tick */
        safe_halt();
        /* Raise voltage if necessary */
@@ -240,14 +242,14 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index,
 
 /**
  * longhaul_set_cpu_frequency()
- * @clock_ratio_index : bitpattern of the new multiplier.
+ * @mults_index : bitpattern of the new multiplier.
  *
  * Sets a new clock ratio.
  */
 
 static void longhaul_setstate(unsigned int table_index)
 {
-       unsigned int clock_ratio_index;
+       unsigned int mults_index;
        int speed, mult;
        struct cpufreq_freqs freqs;
        unsigned long flags;
@@ -256,9 +258,9 @@ static void longhaul_setstate(unsigned int table_index)
        u32 bm_timeout = 1000;
        unsigned int dir = 0;
 
-       clock_ratio_index = longhaul_table[table_index].index;
+       mults_index = longhaul_table[table_index].index;
        /* Safety precautions */
-       mult = clock_ratio[clock_ratio_index & 0x1f];
+       mult = mults[mults_index & 0x1f];
        if (mult == -1)
                return;
        speed = calc_speed(mult);
@@ -274,7 +276,7 @@ static void longhaul_setstate(unsigned int table_index)
 
        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 
-       dprintk ("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n",
+       dprintk("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n",
                        fsb, mult/10, mult%10, print_speed(speed/1000));
 retry_loop:
        preempt_disable();
@@ -282,8 +284,8 @@ retry_loop:
 
        pic2_mask = inb(0xA1);
        pic1_mask = inb(0x21);  /* works on C3. save mask. */
-       outb(0xFF,0xA1);        /* Overkill */
-       outb(0xFE,0x21);        /* TMR0 only */
+       outb(0xFF, 0xA1);       /* Overkill */
+       outb(0xFE, 0x21);       /* TMR0 only */
 
        /* Wait while PCI bus is busy. */
        if (acpi_regs_addr && (longhaul_flags & USE_NORTHBRIDGE
@@ -312,7 +314,7 @@ retry_loop:
         * Software controlled multipliers only.
         */
        case TYPE_LONGHAUL_V1:
-               do_longhaul1(clock_ratio_index);
+               do_longhaul1(mults_index);
                break;
 
        /*
@@ -327,9 +329,9 @@ retry_loop:
                if (longhaul_flags & USE_ACPI_C3) {
                        /* Don't allow wakeup */
                        acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0);
-                       do_powersaver(cx->address, clock_ratio_index, dir);
+                       do_powersaver(cx->address, mults_index, dir);
                } else {
-                       do_powersaver(0, clock_ratio_index, dir);
+                       do_powersaver(0, mults_index, dir);
                }
                break;
        }
@@ -341,8 +343,8 @@ retry_loop:
                /* Enable bus master arbitration */
                acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0);
        }
-       outb(pic2_mask,0xA1);   /* restore mask */
-       outb(pic1_mask,0x21);
+       outb(pic2_mask, 0xA1);  /* restore mask */
+       outb(pic1_mask, 0x21);
 
        local_irq_restore(flags);
        preempt_enable();
@@ -392,7 +394,8 @@ retry_loop:
        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 
        if (!bm_timeout)
-               printk(KERN_INFO PFX "Warning: Timeout while waiting for idle PCI bus.\n");
+               printk(KERN_INFO PFX "Warning: Timeout while waiting for "
+                               "idle PCI bus.\n");
 }
 
 /*
@@ -458,31 +461,32 @@ static int __init longhaul_get_ranges(void)
                break;
        }
 
-       dprintk ("MinMult:%d.%dx MaxMult:%d.%dx\n",
+       dprintk("MinMult:%d.%dx MaxMult:%d.%dx\n",
                 minmult/10, minmult%10, maxmult/10, maxmult%10);
 
        highest_speed = calc_speed(maxmult);
        lowest_speed = calc_speed(minmult);
-       dprintk ("FSB:%dMHz  Lowest speed: %s   Highest speed:%s\n", fsb,
+       dprintk("FSB:%dMHz  Lowest speed: %s   Highest speed:%s\n", fsb,
                 print_speed(lowest_speed/1000),
                 print_speed(highest_speed/1000));
 
        if (lowest_speed == highest_speed) {
-               printk (KERN_INFO PFX "highestspeed == lowest, aborting.\n");
+               printk(KERN_INFO PFX "highestspeed == lowest, aborting.\n");
                return -EINVAL;
        }
        if (lowest_speed > highest_speed) {
-               printk (KERN_INFO PFX "nonsense! lowest (%d > %d) !\n",
+               printk(KERN_INFO PFX "nonsense! lowest (%d > %d) !\n",
                        lowest_speed, highest_speed);
                return -EINVAL;
        }
 
-       longhaul_table = kmalloc((numscales + 1) * sizeof(struct cpufreq_frequency_table), GFP_KERNEL);
-       if(!longhaul_table)
+       longhaul_table = kmalloc((numscales + 1) * sizeof(*longhaul_table),
+                       GFP_KERNEL);
+       if (!longhaul_table)
                return -ENOMEM;
 
        for (j = 0; j < numscales; j++) {
-               ratio = clock_ratio[j];
+               ratio = mults[j];
                if (ratio == -1)
                        continue;
                if (ratio > maxmult || ratio < minmult)
@@ -507,13 +511,10 @@ static int __init longhaul_get_ranges(void)
                        }
                }
                if (min_i != j) {
-                       unsigned int temp;
-                       temp = longhaul_table[j].frequency;
-                       longhaul_table[j].frequency = longhaul_table[min_i].frequency;
-                       longhaul_table[min_i].frequency = temp;
-                       temp = longhaul_table[j].index;
-                       longhaul_table[j].index = longhaul_table[min_i].index;
-                       longhaul_table[min_i].index = temp;
+                       swap(longhaul_table[j].frequency,
+                            longhaul_table[min_i].frequency);
+                       swap(longhaul_table[j].index,
+                            longhaul_table[min_i].index);
                }
        }
 
@@ -521,7 +522,7 @@ static int __init longhaul_get_ranges(void)
 
        /* Find index we are running on */
        for (j = 0; j < k; j++) {
-               if (clock_ratio[longhaul_table[j].index & 0x1f] == mult) {
+               if (mults[longhaul_table[j].index & 0x1f] == mult) {
                        longhaul_index = j;
                        break;
                }
@@ -559,20 +560,22 @@ static void __init longhaul_setup_voltagescaling(void)
        maxvid = vrm_mV_table[longhaul.bits.MaximumVID];
 
        if (minvid.mV == 0 || maxvid.mV == 0 || minvid.mV > maxvid.mV) {
-               printk (KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. "
+               printk(KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. "
                                        "Voltage scaling disabled.\n",
-                                       minvid.mV/1000, minvid.mV%1000, maxvid.mV/1000, maxvid.mV%1000);
+                                       minvid.mV/1000, minvid.mV%1000,
+                                       maxvid.mV/1000, maxvid.mV%1000);
                return;
        }
 
        if (minvid.mV == maxvid.mV) {
-               printk (KERN_INFO PFX "Claims to support voltage scaling but min & max are "
-                               "both %d.%03d. Voltage scaling disabled\n",
+               printk(KERN_INFO PFX "Claims to support voltage scaling but "
+                               "min & max are both %d.%03d. "
+                               "Voltage scaling disabled\n",
                                maxvid.mV/1000, maxvid.mV%1000);
                return;
        }
 
-       /* How many voltage steps */
+       /* How many voltage steps*/
        numvscales = maxvid.pos - minvid.pos + 1;
        printk(KERN_INFO PFX
                "Max VID=%d.%03d  "
@@ -586,7 +589,7 @@ static void __init longhaul_setup_voltagescaling(void)
        j = longhaul.bits.MinMHzBR;
        if (longhaul.bits.MinMHzBR4)
                j += 16;
-       min_vid_speed = eblcr_table[j];
+       min_vid_speed = eblcr[j];
        if (min_vid_speed == -1)
                return;
        switch (longhaul.bits.MinMHzFSB) {
@@ -617,7 +620,8 @@ static void __init longhaul_setup_voltagescaling(void)
                        pos = minvid.pos;
                longhaul_table[j].index |= mV_vrm_table[pos] << 8;
                vid = vrm_mV_table[mV_vrm_table[pos]];
-               printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n", speed, j, vid.mV);
+               printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n",
+                               speed, j, vid.mV);
                j++;
        }
 
@@ -640,7 +644,8 @@ static int longhaul_target(struct cpufreq_policy *policy,
        unsigned int dir = 0;
        u8 vid, current_vid;
 
-       if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq, relation, &table_index))
+       if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq,
+                               relation, &table_index))
                return -EINVAL;
 
        /* Don't set same frequency again */
@@ -656,7 +661,8 @@ static int longhaul_target(struct cpufreq_policy *policy,
                 * this in hardware, C3 is old and we need to do this
                 * in software. */
                i = longhaul_index;
-               current_vid = (longhaul_table[longhaul_index].index >> 8) & 0x1f;
+               current_vid = (longhaul_table[longhaul_index].index >> 8);
+               current_vid &= 0x1f;
                if (table_index > longhaul_index)
                        dir = 1;
                while (i != table_index) {
@@ -691,9 +697,9 @@ static acpi_status longhaul_walk_callback(acpi_handle obj_handle,
 {
        struct acpi_device *d;
 
-       if ( acpi_bus_get_device(obj_handle, &d) ) {
+       if (acpi_bus_get_device(obj_handle, &d))
                return 0;
-       }
+
        *return_value = acpi_driver_data(d);
        return 1;
 }
@@ -750,7 +756,7 @@ static int longhaul_setup_southbridge(void)
        /* Find VT8235 southbridge */
        dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, NULL);
        if (dev == NULL)
-       /* Find VT8237 southbridge */
+               /* Find VT8237 southbridge */
                dev = pci_get_device(PCI_VENDOR_ID_VIA,
                                     PCI_DEVICE_ID_VIA_8237, NULL);
        if (dev != NULL) {
@@ -769,7 +775,8 @@ static int longhaul_setup_southbridge(void)
                if (pci_cmd & 1 << 7) {
                        pci_read_config_dword(dev, 0x88, &acpi_regs_addr);
                        acpi_regs_addr &= 0xff00;
-                       printk(KERN_INFO PFX "ACPI I/O at 0x%x\n", acpi_regs_addr);
+                       printk(KERN_INFO PFX "ACPI I/O at 0x%x\n",
+                                       acpi_regs_addr);
                }
 
                pci_dev_put(dev);
@@ -781,7 +788,7 @@ static int longhaul_setup_southbridge(void)
 static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
 {
        struct cpuinfo_x86 *c = &cpu_data(0);
-       char *cpuname=NULL;
+       char *cpuname = NULL;
        int ret;
        u32 lo, hi;
 
@@ -791,8 +798,8 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
                cpu_model = CPU_SAMUEL;
                cpuname = "C3 'Samuel' [C5A]";
                longhaul_version = TYPE_LONGHAUL_V1;
-               memcpy (clock_ratio, samuel1_clock_ratio, sizeof(samuel1_clock_ratio));
-               memcpy (eblcr_table, samuel1_eblcr, sizeof(samuel1_eblcr));
+               memcpy(mults, samuel1_mults, sizeof(samuel1_mults));
+               memcpy(eblcr, samuel1_eblcr, sizeof(samuel1_eblcr));
                break;
 
        case 7:
@@ -803,10 +810,8 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
                        cpuname = "C3 'Samuel 2' [C5B]";
                        /* Note, this is not a typo, early Samuel2's had
                         * Samuel1 ratios. */
-                       memcpy(clock_ratio, samuel1_clock_ratio,
-                               sizeof(samuel1_clock_ratio));
-                       memcpy(eblcr_table, samuel2_eblcr,
-                               sizeof(samuel2_eblcr));
+                       memcpy(mults, samuel1_mults, sizeof(samuel1_mults));
+                       memcpy(eblcr, samuel2_eblcr, sizeof(samuel2_eblcr));
                        break;
                case 1 ... 15:
                        longhaul_version = TYPE_LONGHAUL_V1;
@@ -817,10 +822,8 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
                                cpu_model = CPU_EZRA;
                                cpuname = "C3 'Ezra' [C5C]";
                        }
-                       memcpy(clock_ratio, ezra_clock_ratio,
-                               sizeof(ezra_clock_ratio));
-                       memcpy(eblcr_table, ezra_eblcr,
-                               sizeof(ezra_eblcr));
+                       memcpy(mults, ezra_mults, sizeof(ezra_mults));
+                       memcpy(eblcr, ezra_eblcr, sizeof(ezra_eblcr));
                        break;
                }
                break;
@@ -829,18 +832,16 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
                cpu_model = CPU_EZRA_T;
                cpuname = "C3 'Ezra-T' [C5M]";
                longhaul_version = TYPE_POWERSAVER;
-               numscales=32;
-               memcpy (clock_ratio, ezrat_clock_ratio, sizeof(ezrat_clock_ratio));
-               memcpy (eblcr_table, ezrat_eblcr, sizeof(ezrat_eblcr));
+               numscales = 32;
+               memcpy(mults, ezrat_mults, sizeof(ezrat_mults));
+               memcpy(eblcr, ezrat_eblcr, sizeof(ezrat_eblcr));
                break;
 
        case 9:
                longhaul_version = TYPE_POWERSAVER;
                numscales = 32;
-               memcpy(clock_ratio,
-                      nehemiah_clock_ratio,
-                      sizeof(nehemiah_clock_ratio));
-               memcpy(eblcr_table, nehemiah_eblcr, sizeof(nehemiah_eblcr));
+               memcpy(mults, nehemiah_mults, sizeof(nehemiah_mults));
+               memcpy(eblcr, nehemiah_eblcr, sizeof(nehemiah_eblcr));
                switch (c->x86_mask) {
                case 0 ... 1:
                        cpu_model = CPU_NEHEMIAH;
@@ -869,14 +870,14 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
                        longhaul_version = TYPE_LONGHAUL_V1;
        }
 
-       printk (KERN_INFO PFX "VIA %s CPU detected.  ", cpuname);
+       printk(KERN_INFO PFX "VIA %s CPU detected.  ", cpuname);
        switch (longhaul_version) {
        case TYPE_LONGHAUL_V1:
        case TYPE_LONGHAUL_V2:
-               printk ("Longhaul v%d supported.\n", longhaul_version);
+               printk(KERN_CONT "Longhaul v%d supported.\n", longhaul_version);
                break;
        case TYPE_POWERSAVER:
-               printk ("Powersaver supported.\n");
+               printk(KERN_CONT "Powersaver supported.\n");
                break;
        };
 
@@ -940,7 +941,7 @@ static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy)
        return 0;
 }
 
-static struct freq_attrlonghaul_attr[] = {
+static struct freq_attr *longhaul_attr[] = {
        &cpufreq_freq_attr_scaling_available_freqs,
        NULL,
 };
@@ -966,13 +967,15 @@ static int __init longhaul_init(void)
 
 #ifdef CONFIG_SMP
        if (num_online_cpus() > 1) {
-               printk(KERN_ERR PFX "More than 1 CPU detected, longhaul disabled.\n");
+               printk(KERN_ERR PFX "More than 1 CPU detected, "
+                               "longhaul disabled.\n");
                return -ENODEV;
        }
 #endif
 #ifdef CONFIG_X86_IO_APIC
        if (cpu_has_apic) {
-               printk(KERN_ERR PFX "APIC detected. Longhaul is currently broken in this configuration.\n");
+               printk(KERN_ERR PFX "APIC detected. Longhaul is currently "
+                               "broken in this configuration.\n");
                return -ENODEV;
        }
 #endif
@@ -993,8 +996,8 @@ static void __exit longhaul_exit(void)
 {
        int i;
 
-       for (i=0; i < numscales; i++) {
-               if (clock_ratio[i] == maxmult) {
+       for (i = 0; i < numscales; i++) {
+               if (mults[i] == maxmult) {
                        longhaul_setstate(i);
                        break;
                }
@@ -1007,11 +1010,11 @@ static void __exit longhaul_exit(void)
 /* Even if BIOS is exporting ACPI C3 state, and it is used
  * with success when CPU is idle, this state doesn't
  * trigger frequency transition in some cases. */
-module_param (disable_acpi_c3, int, 0644);
+module_param(disable_acpi_c3, int, 0644);
 MODULE_PARM_DESC(disable_acpi_c3, "Don't use ACPI C3 support");
 /* Change CPU voltage with frequency. Very usefull to save
  * power, but most VIA C3 processors aren't supporting it. */
-module_param (scale_voltage, int, 0644);
+module_param(scale_voltage, int, 0644);
 MODULE_PARM_DESC(scale_voltage, "Scale voltage of processor");
 /* Force revision key to 0 for processors which doesn't
  * support voltage scaling, but are introducing itself as
@@ -1019,9 +1022,9 @@ MODULE_PARM_DESC(scale_voltage, "Scale voltage of processor");
 module_param(revid_errata, int, 0644);
 MODULE_PARM_DESC(revid_errata, "Ignore CPU Revision ID");
 
-MODULE_AUTHOR ("Dave Jones <davej@redhat.com>");
-MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors.");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
+MODULE_DESCRIPTION("Longhaul driver for VIA Cyrix processors.");
+MODULE_LICENSE("GPL");
 
 late_initcall(longhaul_init);
 module_exit(longhaul_exit);
index 4fcc320997dfd5b9462d21f9282ca2bdcb0c85f0..e2360a469f793b50ab0a5a2662cce15edf25c2aa 100644 (file)
@@ -49,14 +49,14 @@ union msr_longhaul {
 
 /*
  * Clock ratio tables. Div/Mod by 10 to get ratio.
- * The eblcr ones specify the ratio read from the CPU.
- * The clock_ratio ones specify what to write to the CPU.
+ * The eblcr values specify the ratio read from the CPU.
+ * The mults values specify what to write to the CPU.
  */
 
 /*
  * VIA C3 Samuel 1  & Samuel 2 (stepping 0)
  */
-static const int __initdata samuel1_clock_ratio[16] = {
+static const int __initdata samuel1_mults[16] = {
        -1, /* 0000 -> RESERVED */
        30, /* 0001 ->  3.0x */
        40, /* 0010 ->  4.0x */
@@ -119,7 +119,7 @@ static const int __initdata samuel2_eblcr[16] = {
 /*
  * VIA C3 Ezra
  */
-static const int __initdata ezra_clock_ratio[16] = {
+static const int __initdata ezra_mults[16] = {
        100, /* 0000 -> 10.0x */
        30,  /* 0001 ->  3.0x */
        40,  /* 0010 ->  4.0x */
@@ -160,7 +160,7 @@ static const int __initdata ezra_eblcr[16] = {
 /*
  * VIA C3 (Ezra-T) [C5M].
  */
-static const int __initdata ezrat_clock_ratio[32] = {
+static const int __initdata ezrat_mults[32] = {
        100, /* 0000 -> 10.0x */
        30,  /* 0001 ->  3.0x */
        40,  /* 0010 ->  4.0x */
@@ -235,7 +235,7 @@ static const int __initdata ezrat_eblcr[32] = {
 /*
  * VIA C3 Nehemiah */
 
-static const int __initdata  nehemiah_clock_ratio[32] = {
+static const int __initdata  nehemiah_mults[32] = {
        100, /* 0000 -> 10.0x */
        -1, /* 0001 -> 16.0x */
        40,  /* 0010 ->  4.0x */
index 777a7ff075ded63d0c460a4d3c5c2db64ae764e3..da5f70fcb766d1f4a257466e5710c7da62af6d5e 100644 (file)
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/cpufreq.h>
+#include <linux/timex.h>
 
 #include <asm/msr.h>
 #include <asm/processor.h>
-#include <asm/timex.h>
 
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "longrun", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+               "longrun", msg)
 
 static struct cpufreq_driver   longrun_driver;
 
@@ -51,7 +52,7 @@ static void __init longrun_get_policy(struct cpufreq_policy *policy)
        msr_lo &= 0x0000007F;
        msr_hi &= 0x0000007F;
 
-       if ( longrun_high_freq <= longrun_low_freq ) {
+       if (longrun_high_freq <= longrun_low_freq) {
                /* Assume degenerate Longrun table */
                policy->min = policy->max = longrun_high_freq;
        } else {
@@ -79,7 +80,7 @@ static int longrun_set_policy(struct cpufreq_policy *policy)
        if (!policy)
                return -EINVAL;
 
-       if ( longrun_high_freq <= longrun_low_freq ) {
+       if (longrun_high_freq <= longrun_low_freq) {
                /* Assume degenerate Longrun table */
                pctg_lo = pctg_hi = 100;
        } else {
@@ -152,7 +153,7 @@ static unsigned int longrun_get(unsigned int cpu)
        cpuid(0x80860007, &eax, &ebx, &ecx, &edx);
        dprintk("cpuid eax is %u\n", eax);
 
-       return (eax * 1000);
+       return eax * 1000;
 }
 
 /**
@@ -196,7 +197,8 @@ static unsigned int __init longrun_determine_freqs(unsigned int *low_freq,
                rdmsr(MSR_TMTA_LRTI_VOLT_MHZ, msr_lo, msr_hi);
                *high_freq = msr_lo * 1000; /* to kHz */
 
-               dprintk("longrun table interface told %u - %u kHz\n", *low_freq, *high_freq);
+               dprintk("longrun table interface told %u - %u kHz\n",
+                               *low_freq, *high_freq);
 
                if (*low_freq > *high_freq)
                        *low_freq = *high_freq;
@@ -219,7 +221,7 @@ static unsigned int __init longrun_determine_freqs(unsigned int *low_freq,
        cpuid(0x80860007, &eax, &ebx, &ecx, &edx);
        /* try decreasing in 10% steps, some processors react only
         * on some barrier values */
-       for (try_hi = 80; try_hi > 0 && ecx > 90; try_hi -=10) {
+       for (try_hi = 80; try_hi > 0 && ecx > 90; try_hi -= 10) {
                /* set to 0 to try_hi perf_pctg */
                msr_lo &= 0xFFFFFF80;
                msr_hi &= 0xFFFFFF80;
@@ -236,7 +238,7 @@ static unsigned int __init longrun_determine_freqs(unsigned int *low_freq,
 
        /* performance_pctg = (current_freq - low_freq)/(high_freq - low_freq)
         * eqals
-        * low_freq * ( 1 - perf_pctg) = (cur_freq - high_freq * perf_pctg)
+        * low_freq * (1 - perf_pctg) = (cur_freq - high_freq * perf_pctg)
         *
         * high_freq * perf_pctg is stored tempoarily into "ebx".
         */
@@ -317,9 +319,10 @@ static void __exit longrun_exit(void)
 }
 
 
-MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>");
-MODULE_DESCRIPTION ("LongRun driver for Transmeta Crusoe and Efficeon processors.");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
+MODULE_DESCRIPTION("LongRun driver for Transmeta Crusoe and "
+               "Efficeon processors.");
+MODULE_LICENSE("GPL");
 
 module_init(longrun_init);
 module_exit(longrun_exit);
index 3178c3acd97ebb4aa515d19da8a7f75b1178b08a..41ed94915f974ce0310510d9864b54d8e96f37df 100644 (file)
 #include <linux/cpufreq.h>
 #include <linux/slab.h>
 #include <linux/cpumask.h>
+#include <linux/timex.h>
 
 #include <asm/processor.h>
 #include <asm/msr.h>
-#include <asm/timex.h>
+#include <asm/timer.h>
 
 #include "speedstep-lib.h"
 
 #define PFX    "p4-clockmod: "
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "p4-clockmod", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+               "p4-clockmod", msg)
 
 /*
  * Duty Cycle (3bits), note DC_DISABLE is not specified in
@@ -58,7 +60,8 @@ static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate)
 {
        u32 l, h;
 
-       if (!cpu_online(cpu) || (newstate > DC_DISABLE) || (newstate == DC_RESV))
+       if (!cpu_online(cpu) ||
+           (newstate > DC_DISABLE) || (newstate == DC_RESV))
                return -EINVAL;
 
        rdmsr_on_cpu(cpu, MSR_IA32_THERM_STATUS, &l, &h);
@@ -66,7 +69,8 @@ static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate)
        if (l & 0x01)
                dprintk("CPU#%d currently thermal throttled\n", cpu);
 
-       if (has_N44_O17_errata[cpu] && (newstate == DC_25PT || newstate == DC_DFLT))
+       if (has_N44_O17_errata[cpu] &&
+           (newstate == DC_25PT || newstate == DC_DFLT))
                newstate = DC_38PT;
 
        rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h);
@@ -112,7 +116,8 @@ static int cpufreq_p4_target(struct cpufreq_policy *policy,
        struct cpufreq_freqs freqs;
        int i;
 
-       if (cpufreq_frequency_table_target(policy, &p4clockmod_table[0], target_freq, relation, &newstate))
+       if (cpufreq_frequency_table_target(policy, &p4clockmod_table[0],
+                               target_freq, relation, &newstate))
                return -EINVAL;
 
        freqs.old = cpufreq_p4_get(policy->cpu);
@@ -127,7 +132,8 @@ static int cpufreq_p4_target(struct cpufreq_policy *policy,
                cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
        }
 
-       /* run on each logical CPU, see section 13.15.3 of IA32 Intel Architecture Software
+       /* run on each logical CPU,
+        * see section 13.15.3 of IA32 Intel Architecture Software
         * Developer's Manual, Volume 3
         */
        for_each_cpu(i, policy->cpus)
@@ -153,28 +159,30 @@ static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c)
 {
        if (c->x86 == 0x06) {
                if (cpu_has(c, X86_FEATURE_EST))
-                       printk(KERN_WARNING PFX "Warning: EST-capable CPU detected. "
-                              "The acpi-cpufreq module offers voltage scaling"
-                              " in addition of frequency scaling. You should use "
-                              "that instead of p4-clockmod, if possible.\n");
+                       printk(KERN_WARNING PFX "Warning: EST-capable CPU "
+                              "detected. The acpi-cpufreq module offers "
+                              "voltage scaling in addition of frequency "
+                              "scaling. You should use that instead of "
+                              "p4-clockmod, if possible.\n");
                switch (c->x86_model) {
                case 0x0E: /* Core */
                case 0x0F: /* Core Duo */
                case 0x16: /* Celeron Core */
                        p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
-                       return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_PCORE);
+                       return speedstep_get_frequency(SPEEDSTEP_CPU_PCORE);
                case 0x0D: /* Pentium M (Dothan) */
                        p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
                        /* fall through */
                case 0x09: /* Pentium M (Banias) */
-                       return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_PM);
+                       return speedstep_get_frequency(SPEEDSTEP_CPU_PM);
                }
        }
 
        if (c->x86 != 0xF) {
                if (!cpu_has(c, X86_FEATURE_EST))
-                       printk(KERN_WARNING PFX "Unknown p4-clockmod-capable CPU. "
-                               "Please send an e-mail to <cpufreq@vger.kernel.org>\n");
+                       printk(KERN_WARNING PFX "Unknown CPU. "
+                               "Please send an e-mail to "
+                               "<cpufreq@vger.kernel.org>\n");
                return 0;
        }
 
@@ -182,16 +190,16 @@ static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c)
         * throttling is active or not. */
        p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
 
-       if (speedstep_detect_processor() == SPEEDSTEP_PROCESSOR_P4M) {
+       if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4M) {
                printk(KERN_WARNING PFX "Warning: Pentium 4-M detected. "
                       "The speedstep-ich or acpi cpufreq modules offer "
                       "voltage scaling in addition of frequency scaling. "
                       "You should use either one instead of p4-clockmod, "
                       "if possible.\n");
-               return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_P4M);
+               return speedstep_get_frequency(SPEEDSTEP_CPU_P4M);
        }
 
-       return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_P4D);
+       return speedstep_get_frequency(SPEEDSTEP_CPU_P4D);
 }
 
 
@@ -217,14 +225,20 @@ static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
                dprintk("has errata -- disabling low frequencies\n");
        }
 
+       if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4D &&
+           c->x86_model < 2) {
+               /* switch to maximum frequency and measure result */
+               cpufreq_p4_setdc(policy->cpu, DC_DISABLE);
+               recalibrate_cpu_khz();
+       }
        /* get max frequency */
        stock_freq = cpufreq_p4_get_frequency(c);
        if (!stock_freq)
                return -EINVAL;
 
        /* table init */
-       for (i=1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) {
-               if ((i<2) && (has_N44_O17_errata[policy->cpu]))
+       for (i = 1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) {
+               if ((i < 2) && (has_N44_O17_errata[policy->cpu]))
                        p4clockmod_table[i].frequency = CPUFREQ_ENTRY_INVALID;
                else
                        p4clockmod_table[i].frequency = (stock_freq * i)/8;
@@ -232,7 +246,10 @@ static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
        cpufreq_frequency_table_get_attr(p4clockmod_table, policy->cpu);
 
        /* cpuinfo and default policy values */
-       policy->cpuinfo.transition_latency = 1000000; /* assumed */
+
+       /* the transition latency is set to be 1 higher than the maximum
+        * transition latency of the ondemand governor */
+       policy->cpuinfo.transition_latency = 10000001;
        policy->cur = stock_freq;
 
        return cpufreq_frequency_table_cpuinfo(policy, &p4clockmod_table[0]);
@@ -258,12 +275,12 @@ static unsigned int cpufreq_p4_get(unsigned int cpu)
                l = DC_DISABLE;
 
        if (l != DC_DISABLE)
-               return (stock_freq * l / 8);
+               return stock_freq * l / 8;
 
        return stock_freq;
 }
 
-static struct freq_attrp4clockmod_attr[] = {
+static struct freq_attr *p4clockmod_attr[] = {
        &cpufreq_freq_attr_scaling_available_freqs,
        NULL,
 };
@@ -298,9 +315,10 @@ static int __init cpufreq_p4_init(void)
 
        ret = cpufreq_register_driver(&p4clockmod_driver);
        if (!ret)
-               printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock Modulation available\n");
+               printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock "
+                               "Modulation available\n");
 
-       return (ret);
+       return ret;
 }
 
 
@@ -310,9 +328,9 @@ static void __exit cpufreq_p4_exit(void)
 }
 
 
-MODULE_AUTHOR ("Zwane Mwaikambo <zwane@commfireservices.com>");
-MODULE_DESCRIPTION ("cpufreq driver for Pentium(TM) 4/Xeon(TM)");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
+MODULE_DESCRIPTION("cpufreq driver for Pentium(TM) 4/Xeon(TM)");
+MODULE_LICENSE("GPL");
 
 late_initcall(cpufreq_p4_init);
 module_exit(cpufreq_p4_exit);
index c1ac5790c63e34ec65cfe062f6fbf34c6d3607ce..f10dea409f40c3755754fbad0ae1b91eb6d1f604 100644 (file)
@@ -1,6 +1,7 @@
 /*
  *  This file was based upon code in Powertweak Linux (http://powertweak.sf.net)
- *  (C) 2000-2003  Dave Jones, Arjan van de Ven, Janne Pänkälä, Dominik Brodowski.
+ *  (C) 2000-2003  Dave Jones, Arjan van de Ven, Janne Pänkälä,
+ *                 Dominik Brodowski.
  *
  *  Licensed under the terms of the GNU GPL License version 2.
  *
 #include <linux/cpufreq.h>
 #include <linux/ioport.h>
 #include <linux/slab.h>
-
-#include <asm/msr.h>
 #include <linux/timex.h>
 #include <linux/io.h>
 
+#include <asm/msr.h>
+
 #define POWERNOW_IOPORT 0xfff0          /* it doesn't matter where, as long
                                           as it is unused */
 
+#define PFX "powernow-k6: "
 static unsigned int                     busfreq;   /* FSB, in 10 kHz */
 static unsigned int                     max_multiplier;
 
@@ -47,8 +49,8 @@ static struct cpufreq_frequency_table clock_ratio[] = {
  */
 static int powernow_k6_get_cpu_multiplier(void)
 {
-       u64             invalue = 0;
-       u32             msrval;
+       u64 invalue = 0;
+       u32 msrval;
 
        msrval = POWERNOW_IOPORT + 0x1;
        wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
@@ -68,12 +70,12 @@ static int powernow_k6_get_cpu_multiplier(void)
  */
 static void powernow_k6_set_state(unsigned int best_i)
 {
-       unsigned long           outvalue = 0, invalue = 0;
-       unsigned long           msrval;
-       struct cpufreq_freqs    freqs;
+       unsigned long outvalue = 0, invalue = 0;
+       unsigned long msrval;
+       struct cpufreq_freqs freqs;
 
        if (clock_ratio[best_i].index > max_multiplier) {
-               printk(KERN_ERR "cpufreq: invalid target frequency\n");
+               printk(KERN_ERR PFX "invalid target frequency\n");
                return;
        }
 
@@ -119,7 +121,8 @@ static int powernow_k6_verify(struct cpufreq_policy *policy)
  * powernow_k6_setpolicy - sets a new CPUFreq policy
  * @policy: new policy
  * @target_freq: the target frequency
- * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
+ * @relation: how that frequency relates to achieved frequency
+ *  (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
  *
  * sets a new CPUFreq policy
  */
@@ -127,9 +130,10 @@ static int powernow_k6_target(struct cpufreq_policy *policy,
                               unsigned int target_freq,
                               unsigned int relation)
 {
-       unsigned int    newstate = 0;
+       unsigned int newstate = 0;
 
-       if (cpufreq_frequency_table_target(policy, &clock_ratio[0], target_freq, relation, &newstate))
+       if (cpufreq_frequency_table_target(policy, &clock_ratio[0],
+                               target_freq, relation, &newstate))
                return -EINVAL;
 
        powernow_k6_set_state(newstate);
@@ -140,7 +144,7 @@ static int powernow_k6_target(struct cpufreq_policy *policy,
 
 static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
 {
-       unsigned int i;
+       unsigned int i, f;
        int result;
 
        if (policy->cpu != 0)
@@ -152,10 +156,11 @@ static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
 
        /* table init */
        for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
-               if (clock_ratio[i].index > max_multiplier)
+               f = clock_ratio[i].index;
+               if (f > max_multiplier)
                        clock_ratio[i].frequency = CPUFREQ_ENTRY_INVALID;
                else
-                       clock_ratio[i].frequency = busfreq * clock_ratio[i].index;
+                       clock_ratio[i].frequency = busfreq * f;
        }
 
        /* cpuinfo and default policy values */
@@ -185,7 +190,9 @@ static int powernow_k6_cpu_exit(struct cpufreq_policy *policy)
 
 static unsigned int powernow_k6_get(unsigned int cpu)
 {
-       return busfreq * powernow_k6_get_cpu_multiplier();
+       unsigned int ret;
+       ret = (busfreq * powernow_k6_get_cpu_multiplier());
+       return ret;
 }
 
 static struct freq_attr *powernow_k6_attr[] = {
@@ -221,7 +228,7 @@ static int __init powernow_k6_init(void)
                return -ENODEV;
 
        if (!request_region(POWERNOW_IOPORT, 16, "PowerNow!")) {
-               printk("cpufreq: PowerNow IOPORT region already used.\n");
+               printk(KERN_INFO PFX "PowerNow IOPORT region already used.\n");
                return -EIO;
        }
 
@@ -246,7 +253,8 @@ static void __exit powernow_k6_exit(void)
 }
 
 
-MODULE_AUTHOR("Arjan van de Ven, Dave Jones <davej@redhat.com>, Dominik Brodowski <linux@brodo.de>");
+MODULE_AUTHOR("Arjan van de Ven, Dave Jones <davej@redhat.com>, "
+               "Dominik Brodowski <linux@brodo.de>");
 MODULE_DESCRIPTION("PowerNow! driver for AMD K6-2+ / K6-3+ processors.");
 MODULE_LICENSE("GPL");
 
index 1b446d79a8fdd2d2a92f8ea04dbaa41da57a4f88..3c28ccd49742f26022a49ab3bb6a81cfc1713c4a 100644 (file)
@@ -6,10 +6,12 @@
  *  Licensed under the terms of the GNU GPL License version 2.
  *  Based upon datasheets & sample CPUs kindly provided by AMD.
  *
- * Errata 5: Processor may fail to execute a FID/VID change in presence of interrupt.
- * - We cli/sti on stepping A0 CPUs around the FID/VID transition.
- * Errata 15: Processors with half frequency multipliers may hang upon wakeup from disconnect.
- * - We disable half multipliers if ACPI is used on A0 stepping CPUs.
+ * Errata 5:
+ *  CPU may fail to execute a FID/VID change in presence of interrupt.
+ *  - We cli/sti on stepping A0 CPUs around the FID/VID transition.
+ * Errata 15:
+ *  CPU with half frequency multipliers may hang upon wakeup from disconnect.
+ *  - We disable half multipliers if ACPI is used on A0 stepping CPUs.
  */
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/dmi.h>
+#include <linux/timex.h>
+#include <linux/io.h>
 
+#include <asm/timer.h>         /* Needed for recalibrate_cpu_khz() */
 #include <asm/msr.h>
-#include <asm/timer.h>
-#include <asm/timex.h>
-#include <asm/io.h>
 #include <asm/system.h>
 
 #ifdef CONFIG_X86_POWERNOW_K7_ACPI
@@ -58,9 +60,9 @@ struct pst_s {
 union powernow_acpi_control_t {
        struct {
                unsigned long fid:5,
-               vid:5,
-               sgtc:20,
-               res1:2;
+                       vid:5,
+                       sgtc:20,
+                       res1:2;
        } bits;
        unsigned long val;
 };
@@ -94,14 +96,15 @@ static struct cpufreq_frequency_table *powernow_table;
 
 static unsigned int can_scale_bus;
 static unsigned int can_scale_vid;
-static unsigned int minimum_speed=-1;
+static unsigned int minimum_speed = -1;
 static unsigned int maximum_speed;
 static unsigned int number_scales;
 static unsigned int fsb;
 static unsigned int latency;
 static char have_a0;
 
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "powernow-k7", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+               "powernow-k7", msg)
 
 static int check_fsb(unsigned int fsbspeed)
 {
@@ -109,7 +112,7 @@ static int check_fsb(unsigned int fsbspeed)
        unsigned int f = fsb / 1000;
 
        delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;
-       return (delta < 5);
+       return delta < 5;
 }
 
 static int check_powernow(void)
@@ -117,24 +120,26 @@ static int check_powernow(void)
        struct cpuinfo_x86 *c = &cpu_data(0);
        unsigned int maxei, eax, ebx, ecx, edx;
 
-       if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 !=6)) {
+       if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 6)) {
 #ifdef MODULE
-               printk (KERN_INFO PFX "This module only works with AMD K7 CPUs\n");
+               printk(KERN_INFO PFX "This module only works with "
+                               "AMD K7 CPUs\n");
 #endif
                return 0;
        }
 
        /* Get maximum capabilities */
-       maxei = cpuid_eax (0x80000000);
+       maxei = cpuid_eax(0x80000000);
        if (maxei < 0x80000007) {       /* Any powernow info ? */
 #ifdef MODULE
-               printk (KERN_INFO PFX "No powernow capabilities detected\n");
+               printk(KERN_INFO PFX "No powernow capabilities detected\n");
 #endif
                return 0;
        }
 
        if ((c->x86_model == 6) && (c->x86_mask == 0)) {
-               printk (KERN_INFO PFX "K7 660[A0] core detected, enabling errata workarounds\n");
+               printk(KERN_INFO PFX "K7 660[A0] core detected, "
+                               "enabling errata workarounds\n");
                have_a0 = 1;
        }
 
@@ -144,37 +149,42 @@ static int check_powernow(void)
        if (!(edx & (1 << 1 | 1 << 2)))
                return 0;
 
-       printk (KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");
+       printk(KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");
 
        if (edx & 1 << 1) {
-               printk ("frequency");
-               can_scale_bus=1;
+               printk("frequency");
+               can_scale_bus = 1;
        }
 
        if ((edx & (1 << 1 | 1 << 2)) == 0x6)
-               printk (" and ");
+               printk(" and ");
 
        if (edx & 1 << 2) {
-               printk ("voltage");
-               can_scale_vid=1;
+               printk("voltage");
+               can_scale_vid = 1;
        }
 
-       printk (".\n");
+       printk(".\n");
        return 1;
 }
 
+static void invalidate_entry(unsigned int entry)
+{
+       powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
+}
 
-static int get_ranges (unsigned char *pst)
+static int get_ranges(unsigned char *pst)
 {
        unsigned int j;
        unsigned int speed;
        u8 fid, vid;
 
-       powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) * (number_scales + 1)), GFP_KERNEL);
+       powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
+                               (number_scales + 1)), GFP_KERNEL);
        if (!powernow_table)
                return -ENOMEM;
 
-       for (j=0 ; j < number_scales; j++) {
+       for (j = 0 ; j < number_scales; j++) {
                fid = *pst++;
 
                powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
@@ -182,10 +192,10 @@ static int get_ranges (unsigned char *pst)
 
                speed = powernow_table[j].frequency;
 
-               if ((fid_codes[fid] % 10)==5) {
+               if ((fid_codes[fid] % 10) == 5) {
 #ifdef CONFIG_X86_POWERNOW_K7_ACPI
                        if (have_a0 == 1)
-                               powernow_table[j].frequency = CPUFREQ_ENTRY_INVALID;
+                               invalidate_entry(j);
 #endif
                }
 
@@ -197,7 +207,7 @@ static int get_ranges (unsigned char *pst)
                vid = *pst++;
                powernow_table[j].index |= (vid << 8); /* upper 8 bits */
 
-               dprintk ("   FID: 0x%x (%d.%dx [%dMHz])  "
+               dprintk("   FID: 0x%x (%d.%dx [%dMHz])  "
                         "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
                         fid_codes[fid] % 10, speed/1000, vid,
                         mobile_vid_table[vid]/1000,
@@ -214,13 +224,13 @@ static void change_FID(int fid)
 {
        union msr_fidvidctl fidvidctl;
 
-       rdmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
+       rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
        if (fidvidctl.bits.FID != fid) {
                fidvidctl.bits.SGTC = latency;
                fidvidctl.bits.FID = fid;
                fidvidctl.bits.VIDC = 0;
                fidvidctl.bits.FIDC = 1;
-               wrmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
+               wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
        }
 }
 
@@ -229,18 +239,18 @@ static void change_VID(int vid)
 {
        union msr_fidvidctl fidvidctl;
 
-       rdmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
+       rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
        if (fidvidctl.bits.VID != vid) {
                fidvidctl.bits.SGTC = latency;
                fidvidctl.bits.VID = vid;
                fidvidctl.bits.FIDC = 0;
                fidvidctl.bits.VIDC = 1;
-               wrmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
+               wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
        }
 }
 
 
-static void change_speed (unsigned int index)
+static void change_speed(unsigned int index)
 {
        u8 fid, vid;
        struct cpufreq_freqs freqs;
@@ -257,7 +267,7 @@ static void change_speed (unsigned int index)
 
        freqs.cpu = 0;
 
-       rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
+       rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
        cfid = fidvidstatus.bits.CFID;
        freqs.old = fsb * fid_codes[cfid] / 10;
 
@@ -321,12 +331,14 @@ static int powernow_acpi_init(void)
                goto err1;
        }
 
-       if (acpi_processor_perf->control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) {
+       if (acpi_processor_perf->control_register.space_id !=
+                       ACPI_ADR_SPACE_FIXED_HARDWARE) {
                retval = -ENODEV;
                goto err2;
        }
 
-       if (acpi_processor_perf->status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) {
+       if (acpi_processor_perf->status_register.space_id !=
+                       ACPI_ADR_SPACE_FIXED_HARDWARE) {
                retval = -ENODEV;
                goto err2;
        }
@@ -338,7 +350,8 @@ static int powernow_acpi_init(void)
                goto err2;
        }
 
-       powernow_table = kzalloc((number_scales + 1) * (sizeof(struct cpufreq_frequency_table)), GFP_KERNEL);
+       powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
+                               (number_scales + 1)), GFP_KERNEL);
        if (!powernow_table) {
                retval = -ENOMEM;
                goto err2;
@@ -352,7 +365,7 @@ static int powernow_acpi_init(void)
                unsigned int speed, speed_mhz;
 
                pc.val = (unsigned long) state->control;
-               dprintk ("acpi:  P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
+               dprintk("acpi:  P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
                         i,
                         (u32) state->core_frequency,
                         (u32) state->power,
@@ -381,12 +394,12 @@ static int powernow_acpi_init(void)
                if (speed % 1000 > 0)
                        speed_mhz++;
 
-               if ((fid_codes[fid] % 10)==5) {
+               if ((fid_codes[fid] % 10) == 5) {
                        if (have_a0 == 1)
-                               powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+                               invalidate_entry(i);
                }
 
-               dprintk ("   FID: 0x%x (%d.%dx [%dMHz])  "
+               dprintk("   FID: 0x%x (%d.%dx [%dMHz])  "
                         "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
                         fid_codes[fid] % 10, speed_mhz, vid,
                         mobile_vid_table[vid]/1000,
@@ -422,7 +435,8 @@ err1:
 err05:
        kfree(acpi_processor_perf);
 err0:
-       printk(KERN_WARNING PFX "ACPI perflib can not be used in this platform\n");
+       printk(KERN_WARNING PFX "ACPI perflib can not be used on "
+                       "this platform\n");
        acpi_processor_perf = NULL;
        return retval;
 }
@@ -435,7 +449,14 @@ static int powernow_acpi_init(void)
 }
 #endif
 
-static int powernow_decode_bios (int maxfid, int startvid)
+static void print_pst_entry(struct pst_s *pst, unsigned int j)
+{
+       dprintk("PST:%d (@%p)\n", j, pst);
+       dprintk(" cpuid: 0x%x  fsb: %d  maxFID: 0x%x  startvid: 0x%x\n",
+               pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid);
+}
+
+static int powernow_decode_bios(int maxfid, int startvid)
 {
        struct psb_s *psb;
        struct pst_s *pst;
@@ -446,61 +467,67 @@ static int powernow_decode_bios (int maxfid, int startvid)
 
        etuple = cpuid_eax(0x80000001);
 
-       for (i=0xC0000; i < 0xffff0 ; i+=16) {
+       for (i = 0xC0000; i < 0xffff0 ; i += 16) {
 
                p = phys_to_virt(i);
 
-               if (memcmp(p, "AMDK7PNOW!",  10) == 0){
-                       dprintk ("Found PSB header at %p\n", p);
+               if (memcmp(p, "AMDK7PNOW!",  10) == 0) {
+                       dprintk("Found PSB header at %p\n", p);
                        psb = (struct psb_s *) p;
-                       dprintk ("Table version: 0x%x\n", psb->tableversion);
+                       dprintk("Table version: 0x%x\n", psb->tableversion);
                        if (psb->tableversion != 0x12) {
-                               printk (KERN_INFO PFX "Sorry, only v1.2 tables supported right now\n");
+                               printk(KERN_INFO PFX "Sorry, only v1.2 tables"
+                                               " supported right now\n");
                                return -ENODEV;
                        }
 
-                       dprintk ("Flags: 0x%x\n", psb->flags);
-                       if ((psb->flags & 1)==0) {
-                               dprintk ("Mobile voltage regulator\n");
-                       } else {
-                               dprintk ("Desktop voltage regulator\n");
-                       }
+                       dprintk("Flags: 0x%x\n", psb->flags);
+                       if ((psb->flags & 1) == 0)
+                               dprintk("Mobile voltage regulator\n");
+                       else
+                               dprintk("Desktop voltage regulator\n");
 
                        latency = psb->settlingtime;
                        if (latency < 100) {
-                               printk(KERN_INFO PFX "BIOS set settling time to %d microseconds. "
-                                               "Should be at least 100. Correcting.\n", latency);
+                               printk(KERN_INFO PFX "BIOS set settling time "
+                                               "to %d microseconds. "
+                                               "Should be at least 100. "
+                                               "Correcting.\n", latency);
                                latency = 100;
                        }
-                       dprintk ("Settling Time: %d microseconds.\n", psb->settlingtime);
-                       dprintk ("Has %d PST tables. (Only dumping ones relevant to this CPU).\n", psb->numpst);
+                       dprintk("Settling Time: %d microseconds.\n",
+                                       psb->settlingtime);
+                       dprintk("Has %d PST tables. (Only dumping ones "
+                                       "relevant to this CPU).\n",
+                                       psb->numpst);
 
-                       p += sizeof (struct psb_s);
+                       p += sizeof(struct psb_s);
 
                        pst = (struct pst_s *) p;
 
-                       for (j=0; j<psb->numpst; j++) {
+                       for (j = 0; j < psb->numpst; j++) {
                                pst = (struct pst_s *) p;
                                number_scales = pst->numpstates;
 
-                               if ((etuple == pst->cpuid) && check_fsb(pst->fsbspeed) &&
-                                   (maxfid==pst->maxfid) && (startvid==pst->startvid))
-                               {
-                                       dprintk ("PST:%d (@%p)\n", j, pst);
-                                       dprintk (" cpuid: 0x%x  fsb: %d  maxFID: 0x%x  startvid: 0x%x\n",
-                                                pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid);
-
-                                       ret = get_ranges ((char *) pst + sizeof (struct pst_s));
+                               if ((etuple == pst->cpuid) &&
+                                   check_fsb(pst->fsbspeed) &&
+                                   (maxfid == pst->maxfid) &&
+                                   (startvid == pst->startvid)) {
+                                       print_pst_entry(pst, j);
+                                       p = (char *)pst + sizeof(struct pst_s);
+                                       ret = get_ranges(p);
                                        return ret;
                                } else {
                                        unsigned int k;
-                                       p = (char *) pst + sizeof (struct pst_s);
-                                       for (k=0; k<number_scales; k++)
-                                               p+=2;
+                                       p = (char *)pst + sizeof(struct pst_s);
+                                       for (k = 0; k < number_scales; k++)
+                                               p += 2;
                                }
                        }
-                       printk (KERN_INFO PFX "No PST tables match this cpuid (0x%x)\n", etuple);
-                       printk (KERN_INFO PFX "This is indicative of a broken BIOS.\n");
+                       printk(KERN_INFO PFX "No PST tables match this cpuid "
+                                       "(0x%x)\n", etuple);
+                       printk(KERN_INFO PFX "This is indicative of a broken "
+                                       "BIOS.\n");
 
                        return -EINVAL;
                }
@@ -511,13 +538,14 @@ static int powernow_decode_bios (int maxfid, int startvid)
 }
 
 
-static int powernow_target (struct cpufreq_policy *policy,
+static int powernow_target(struct cpufreq_policy *policy,
                            unsigned int target_freq,
                            unsigned int relation)
 {
        unsigned int newstate;
 
-       if (cpufreq_frequency_table_target(policy, powernow_table, target_freq, relation, &newstate))
+       if (cpufreq_frequency_table_target(policy, powernow_table, target_freq,
+                               relation, &newstate))
                return -EINVAL;
 
        change_speed(newstate);
@@ -526,7 +554,7 @@ static int powernow_target (struct cpufreq_policy *policy,
 }
 
 
-static int powernow_verify (struct cpufreq_policy *policy)
+static int powernow_verify(struct cpufreq_policy *policy)
 {
        return cpufreq_frequency_table_verify(policy, powernow_table);
 }
@@ -566,18 +594,23 @@ static unsigned int powernow_get(unsigned int cpu)
 
        if (cpu)
                return 0;
-       rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
+       rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
        cfid = fidvidstatus.bits.CFID;
 
-       return (fsb * fid_codes[cfid] / 10);
+       return fsb * fid_codes[cfid] / 10;
 }
 
 
 static int __init acer_cpufreq_pst(const struct dmi_system_id *d)
 {
-       printk(KERN_WARNING "%s laptop with broken PST tables in BIOS detected.\n", d->ident);
-       printk(KERN_WARNING "You need to downgrade to 3A21 (09/09/2002), or try a newer BIOS than 3A71 (01/20/2003)\n");
-       printk(KERN_WARNING "cpufreq scaling has been disabled as a result of this.\n");
+       printk(KERN_WARNING PFX
+               "%s laptop with broken PST tables in BIOS detected.\n",
+               d->ident);
+       printk(KERN_WARNING PFX
+               "You need to downgrade to 3A21 (09/09/2002), or try a newer "
+               "BIOS than 3A71 (01/20/2003)\n");
+       printk(KERN_WARNING PFX
+               "cpufreq scaling has been disabled as a result of this.\n");
        return 0;
 }
 
@@ -598,7 +631,7 @@ static struct dmi_system_id __initdata powernow_dmi_table[] = {
        { }
 };
 
-static int __init powernow_cpu_init (struct cpufreq_policy *policy)
+static int __init powernow_cpu_init(struct cpufreq_policy *policy)
 {
        union msr_fidvidstatus fidvidstatus;
        int result;
@@ -606,7 +639,7 @@ static int __init powernow_cpu_init (struct cpufreq_policy *policy)
        if (policy->cpu != 0)
                return -ENODEV;
 
-       rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
+       rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
 
        recalibrate_cpu_khz();
 
@@ -618,19 +651,21 @@ static int __init powernow_cpu_init (struct cpufreq_policy *policy)
        dprintk("FSB: %3dMHz\n", fsb/1000);
 
        if (dmi_check_system(powernow_dmi_table) || acpi_force) {
-               printk (KERN_INFO PFX "PSB/PST known to be broken.  Trying ACPI instead\n");
+               printk(KERN_INFO PFX "PSB/PST known to be broken.  "
+                               "Trying ACPI instead\n");
                result = powernow_acpi_init();
        } else {
-               result = powernow_decode_bios(fidvidstatus.bits.MFID, fidvidstatus.bits.SVID);
+               result = powernow_decode_bios(fidvidstatus.bits.MFID,
+                               fidvidstatus.bits.SVID);
                if (result) {
-                       printk (KERN_INFO PFX "Trying ACPI perflib\n");
+                       printk(KERN_INFO PFX "Trying ACPI perflib\n");
                        maximum_speed = 0;
                        minimum_speed = -1;
                        latency = 0;
                        result = powernow_acpi_init();
                        if (result) {
-                               printk (KERN_INFO PFX "ACPI and legacy methods failed\n");
-                               printk (KERN_INFO PFX "See http://www.codemonkey.org.uk/projects/cpufreq/powernow-k7.html\n");
+                               printk(KERN_INFO PFX
+                                       "ACPI and legacy methods failed\n");
                        }
                } else {
                        /* SGTC use the bus clock as timer */
@@ -642,10 +677,11 @@ static int __init powernow_cpu_init (struct cpufreq_policy *policy)
        if (result)
                return result;
 
-       printk (KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n",
+       printk(KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n",
                                minimum_speed/1000, maximum_speed/1000);
 
-       policy->cpuinfo.transition_latency = cpufreq_scale(2000000UL, fsb, latency);
+       policy->cpuinfo.transition_latency =
+               cpufreq_scale(2000000UL, fsb, latency);
 
        policy->cur = powernow_get(0);
 
@@ -654,7 +690,8 @@ static int __init powernow_cpu_init (struct cpufreq_policy *policy)
        return cpufreq_frequency_table_cpuinfo(policy, powernow_table);
 }
 
-static int powernow_cpu_exit (struct cpufreq_policy *policy) {
+static int powernow_cpu_exit(struct cpufreq_policy *policy)
+{
        cpufreq_frequency_table_put_attr(policy->cpu);
 
 #ifdef CONFIG_X86_POWERNOW_K7_ACPI
@@ -669,7 +706,7 @@ static int powernow_cpu_exit (struct cpufreq_policy *policy) {
        return 0;
 }
 
-static struct freq_attrpowernow_table_attr[] = {
+static struct freq_attr *powernow_table_attr[] = {
        &cpufreq_freq_attr_scaling_available_freqs,
        NULL,
 };
@@ -685,15 +722,15 @@ static struct cpufreq_driver powernow_driver = {
        .attr   = powernow_table_attr,
 };
 
-static int __init powernow_init (void)
+static int __init powernow_init(void)
 {
-       if (check_powernow()==0)
+       if (check_powernow() == 0)
                return -ENODEV;
        return cpufreq_register_driver(&powernow_driver);
 }
 
 
-static void __exit powernow_exit (void)
+static void __exit powernow_exit(void)
 {
        cpufreq_unregister_driver(&powernow_driver);
 }
@@ -701,9 +738,9 @@ static void __exit powernow_exit (void)
 module_param(acpi_force,  int, 0444);
 MODULE_PARM_DESC(acpi_force, "Force ACPI to be used.");
 
-MODULE_AUTHOR ("Dave Jones <davej@redhat.com>");
-MODULE_DESCRIPTION ("Powernow driver for AMD K7 processors.");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
+MODULE_DESCRIPTION("Powernow driver for AMD K7 processors.");
+MODULE_LICENSE("GPL");
 
 late_initcall(powernow_init);
 module_exit(powernow_exit);
index 6428aa17b40e794683b5359ed0589dc3dbfbf592..a15ac94e0b9b8c2ad1ee540fc7bad1b5415aff36 100644 (file)
 #include <linux/string.h>
 #include <linux/cpumask.h>
 #include <linux/sched.h>       /* for current / set_cpus_allowed() */
+#include <linux/io.h>
+#include <linux/delay.h>
 
 #include <asm/msr.h>
-#include <asm/io.h>
-#include <asm/delay.h>
 
-#ifdef CONFIG_X86_POWERNOW_K8_ACPI
 #include <linux/acpi.h>
 #include <linux/mutex.h>
 #include <acpi/processor.h>
-#endif
 
 #define PFX "powernow-k8: "
 #define VERSION "version 2.20.00"
@@ -71,7 +69,8 @@ static u32 find_khz_freq_from_fid(u32 fid)
        return 1000 * find_freq_from_fid(fid);
 }
 
-static u32 find_khz_freq_from_pstate(struct cpufreq_frequency_table *data, u32 pstate)
+static u32 find_khz_freq_from_pstate(struct cpufreq_frequency_table *data,
+               u32 pstate)
 {
        return data[pstate].frequency;
 }
@@ -186,7 +185,9 @@ static int write_new_fid(struct powernow_k8_data *data, u32 fid)
                return 1;
        }
 
-       lo = fid | (data->currvid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
+       lo = fid;
+       lo |= (data->currvid << MSR_C_LO_VID_SHIFT);
+       lo |= MSR_C_LO_INIT_FID_VID;
 
        dprintk("writing fid 0x%x, lo 0x%x, hi 0x%x\n",
                fid, lo, data->plllock * PLL_LOCK_CONVERSION);
@@ -194,7 +195,9 @@ static int write_new_fid(struct powernow_k8_data *data, u32 fid)
        do {
                wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
                if (i++ > 100) {
-                       printk(KERN_ERR PFX "Hardware error - pending bit very stuck - no further pstate changes possible\n");
+                       printk(KERN_ERR PFX
+                               "Hardware error - pending bit very stuck - "
+                               "no further pstate changes possible\n");
                        return 1;
                }
        } while (query_current_values_with_pending_wait(data));
@@ -202,14 +205,16 @@ static int write_new_fid(struct powernow_k8_data *data, u32 fid)
        count_off_irt(data);
 
        if (savevid != data->currvid) {
-               printk(KERN_ERR PFX "vid change on fid trans, old 0x%x, new 0x%x\n",
-                      savevid, data->currvid);
+               printk(KERN_ERR PFX
+                       "vid change on fid trans, old 0x%x, new 0x%x\n",
+                       savevid, data->currvid);
                return 1;
        }
 
        if (fid != data->currfid) {
-               printk(KERN_ERR PFX "fid trans failed, fid 0x%x, curr 0x%x\n", fid,
-                       data->currfid);
+               printk(KERN_ERR PFX
+                       "fid trans failed, fid 0x%x, curr 0x%x\n", fid,
+                       data->currfid);
                return 1;
        }
 
@@ -228,7 +233,9 @@ static int write_new_vid(struct powernow_k8_data *data, u32 vid)
                return 1;
        }
 
-       lo = data->currfid | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
+       lo = data->currfid;
+       lo |= (vid << MSR_C_LO_VID_SHIFT);
+       lo |= MSR_C_LO_INIT_FID_VID;
 
        dprintk("writing vid 0x%x, lo 0x%x, hi 0x%x\n",
                vid, lo, STOP_GRANT_5NS);
@@ -236,20 +243,24 @@ static int write_new_vid(struct powernow_k8_data *data, u32 vid)
        do {
                wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
                if (i++ > 100) {
-                       printk(KERN_ERR PFX "internal error - pending bit very stuck - no further pstate changes possible\n");
+                       printk(KERN_ERR PFX "internal error - pending bit "
+                                       "very stuck - no further pstate "
+                                       "changes possible\n");
                        return 1;
                }
        } while (query_current_values_with_pending_wait(data));
 
        if (savefid != data->currfid) {
-               printk(KERN_ERR PFX "fid changed on vid trans, old 0x%x new 0x%x\n",
+               printk(KERN_ERR PFX "fid changed on vid trans, old "
+                       "0x%x new 0x%x\n",
                       savefid, data->currfid);
                return 1;
        }
 
        if (vid != data->currvid) {
-               printk(KERN_ERR PFX "vid trans failed, vid 0x%x, curr 0x%x\n", vid,
-                               data->currvid);
+               printk(KERN_ERR PFX "vid trans failed, vid 0x%x, "
+                               "curr 0x%x\n",
+                               vid, data->currvid);
                return 1;
        }
 
@@ -261,7 +272,8 @@ static int write_new_vid(struct powernow_k8_data *data, u32 vid)
  * Decreasing vid codes represent increasing voltages:
  * vid of 0 is 1.550V, vid of 0x1e is 0.800V, vid of VID_OFF is off.
  */
-static int decrease_vid_code_by_step(struct powernow_k8_data *data, u32 reqvid, u32 step)
+static int decrease_vid_code_by_step(struct powernow_k8_data *data,
+               u32 reqvid, u32 step)
 {
        if ((data->currvid - reqvid) > step)
                reqvid = data->currvid - step;
@@ -283,7 +295,8 @@ static int transition_pstate(struct powernow_k8_data *data, u32 pstate)
 }
 
 /* Change Opteron/Athlon64 fid and vid, by the 3 phases. */
-static int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 reqvid)
+static int transition_fid_vid(struct powernow_k8_data *data,
+               u32 reqfid, u32 reqvid)
 {
        if (core_voltage_pre_transition(data, reqvid))
                return 1;
@@ -298,7 +311,8 @@ static int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 req
                return 1;
 
        if ((reqfid != data->currfid) || (reqvid != data->currvid)) {
-               printk(KERN_ERR PFX "failed (cpu%d): req 0x%x 0x%x, curr 0x%x 0x%x\n",
+               printk(KERN_ERR PFX "failed (cpu%d): req 0x%x 0x%x, "
+                               "curr 0x%x 0x%x\n",
                                smp_processor_id(),
                                reqfid, reqvid, data->currfid, data->currvid);
                return 1;
@@ -311,13 +325,15 @@ static int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 req
 }
 
 /* Phase 1 - core voltage transition ... setup voltage */
-static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid)
+static int core_voltage_pre_transition(struct powernow_k8_data *data,
+               u32 reqvid)
 {
        u32 rvosteps = data->rvo;
        u32 savefid = data->currfid;
        u32 maxvid, lo;
 
-       dprintk("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n",
+       dprintk("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, "
+               "reqvid 0x%x, rvo 0x%x\n",
                smp_processor_id(),
                data->currfid, data->currvid, reqvid, data->rvo);
 
@@ -340,7 +356,7 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid
                } else {
                        dprintk("ph1: changing vid for rvo, req 0x%x\n",
                                data->currvid - 1);
-                       if (decrease_vid_code_by_step(data, data->currvid - 1, 1))
+                       if (decrease_vid_code_by_step(data, data->currvid-1, 1))
                                return 1;
                        rvosteps--;
                }
@@ -350,7 +366,8 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid
                return 1;
 
        if (savefid != data->currfid) {
-               printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n", data->currfid);
+               printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n",
+                               data->currfid);
                return 1;
        }
 
@@ -363,20 +380,24 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid
 /* Phase 2 - core frequency transition */
 static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
 {
-       u32 vcoreqfid, vcocurrfid, vcofiddiff, fid_interval, savevid = data->currvid;
+       u32 vcoreqfid, vcocurrfid, vcofiddiff;
+       u32 fid_interval, savevid = data->currvid;
 
-       if ((reqfid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
-               printk(KERN_ERR PFX "ph2: illegal lo-lo transition 0x%x 0x%x\n",
-                       reqfid, data->currfid);
+       if ((reqfid < HI_FID_TABLE_BOTTOM) &&
+           (data->currfid < HI_FID_TABLE_BOTTOM)) {
+               printk(KERN_ERR PFX "ph2: illegal lo-lo transition "
+                               "0x%x 0x%x\n", reqfid, data->currfid);
                return 1;
        }
 
        if (data->currfid == reqfid) {
-               printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n", data->currfid);
+               printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n",
+                               data->currfid);
                return 0;
        }
 
-       dprintk("ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n",
+       dprintk("ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, "
+               "reqfid 0x%x\n",
                smp_processor_id(),
                data->currfid, data->currvid, reqfid);
 
@@ -390,14 +411,14 @@ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
 
                if (reqfid > data->currfid) {
                        if (data->currfid > LO_FID_TABLE_TOP) {
-                               if (write_new_fid(data, data->currfid + fid_interval)) {
+                               if (write_new_fid(data,
+                                               data->currfid + fid_interval))
                                        return 1;
-                               }
                        } else {
                                if (write_new_fid
-                                   (data, 2 + convert_fid_to_vco_fid(data->currfid))) {
+                                   (data,
+                                    2 + convert_fid_to_vco_fid(data->currfid)))
                                        return 1;
-                               }
                        }
                } else {
                        if (write_new_fid(data, data->currfid - fid_interval))
@@ -417,7 +438,8 @@ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
 
        if (data->currfid != reqfid) {
                printk(KERN_ERR PFX
-                       "ph2: mismatch, failed fid transition, curr 0x%x, req 0x%x\n",
+                       "ph2: mismatch, failed fid transition, "
+                       "curr 0x%x, req 0x%x\n",
                        data->currfid, reqfid);
                return 1;
        }
@@ -435,7 +457,8 @@ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
 }
 
 /* Phase 3 - core voltage transition flow ... jump to the final vid. */
-static int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid)
+static int core_voltage_post_transition(struct powernow_k8_data *data,
+               u32 reqvid)
 {
        u32 savefid = data->currfid;
        u32 savereqvid = reqvid;
@@ -457,7 +480,8 @@ static int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvi
 
                if (data->currvid != reqvid) {
                        printk(KERN_ERR PFX
-                              "ph3: failed vid transition\n, req 0x%x, curr 0x%x",
+                              "ph3: failed vid transition\n, "
+                              "req 0x%x, curr 0x%x",
                               reqvid, data->currvid);
                        return 1;
                }
@@ -508,7 +532,8 @@ static int check_supported_cpu(unsigned int cpu)
        if ((eax & CPUID_XFAM) == CPUID_XFAM_K8) {
                if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) ||
                    ((eax & CPUID_XMOD) > CPUID_XMOD_REV_MASK)) {
-                       printk(KERN_INFO PFX "Processor cpuid %x not supported\n", eax);
+                       printk(KERN_INFO PFX
+                               "Processor cpuid %x not supported\n", eax);
                        goto out;
                }
 
@@ -520,8 +545,10 @@ static int check_supported_cpu(unsigned int cpu)
                }
 
                cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
-               if ((edx & P_STATE_TRANSITION_CAPABLE) != P_STATE_TRANSITION_CAPABLE) {
-                       printk(KERN_INFO PFX "Power state transitions not supported\n");
+               if ((edx & P_STATE_TRANSITION_CAPABLE)
+                       != P_STATE_TRANSITION_CAPABLE) {
+                       printk(KERN_INFO PFX
+                               "Power state transitions not supported\n");
                        goto out;
                }
        } else { /* must be a HW Pstate capable processor */
@@ -539,7 +566,8 @@ out:
        return rc;
 }
 
-static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
+static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst,
+               u8 maxvid)
 {
        unsigned int j;
        u8 lastfid = 0xff;
@@ -550,12 +578,14 @@ static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8
                               j, pst[j].vid);
                        return -EINVAL;
                }
-               if (pst[j].vid < data->rvo) {   /* vid + rvo >= 0 */
+               if (pst[j].vid < data->rvo) {
+                       /* vid + rvo >= 0 */
                        printk(KERN_ERR FW_BUG PFX "0 vid exceeded with pstate"
                               " %d\n", j);
                        return -ENODEV;
                }
-               if (pst[j].vid < maxvid + data->rvo) {  /* vid + rvo >= maxvid */
+               if (pst[j].vid < maxvid + data->rvo) {
+                       /* vid + rvo >= maxvid */
                        printk(KERN_ERR FW_BUG PFX "maxvid exceeded with pstate"
                               " %d\n", j);
                        return -ENODEV;
@@ -579,23 +609,31 @@ static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8
                return -EINVAL;
        }
        if (lastfid > LO_FID_TABLE_TOP)
-               printk(KERN_INFO FW_BUG PFX  "first fid not from lo freq table\n");
+               printk(KERN_INFO FW_BUG PFX
+                       "first fid not from lo freq table\n");
 
        return 0;
 }
 
+static void invalidate_entry(struct powernow_k8_data *data, unsigned int entry)
+{
+       data->powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
+}
+
 static void print_basics(struct powernow_k8_data *data)
 {
        int j;
        for (j = 0; j < data->numps; j++) {
-               if (data->powernow_table[j].frequency != CPUFREQ_ENTRY_INVALID) {
+               if (data->powernow_table[j].frequency !=
+                               CPUFREQ_ENTRY_INVALID) {
                        if (cpu_family == CPU_HW_PSTATE) {
-                               printk(KERN_INFO PFX "   %d : pstate %d (%d MHz)\n",
-                                       j,
+                               printk(KERN_INFO PFX
+                                       "   %d : pstate %d (%d MHz)\n", j,
                                        data->powernow_table[j].index,
                                        data->powernow_table[j].frequency/1000);
                        } else {
-                               printk(KERN_INFO PFX "   %d : fid 0x%x (%d MHz), vid 0x%x\n",
+                               printk(KERN_INFO PFX
+                                       "   %d : fid 0x%x (%d MHz), vid 0x%x\n",
                                        j,
                                        data->powernow_table[j].index & 0xff,
                                        data->powernow_table[j].frequency/1000,
@@ -604,20 +642,25 @@ static void print_basics(struct powernow_k8_data *data)
                }
        }
        if (data->batps)
-               printk(KERN_INFO PFX "Only %d pstates on battery\n", data->batps);
+               printk(KERN_INFO PFX "Only %d pstates on battery\n",
+                               data->batps);
 }
 
-static int fill_powernow_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
+static int fill_powernow_table(struct powernow_k8_data *data,
+               struct pst_s *pst, u8 maxvid)
 {
        struct cpufreq_frequency_table *powernow_table;
        unsigned int j;
 
-       if (data->batps) {    /* use ACPI support to get full speed on mains power */
-               printk(KERN_WARNING PFX "Only %d pstates usable (use ACPI driver for full range\n", data->batps);
+       if (data->batps) {
+               /* use ACPI support to get full speed on mains power */
+               printk(KERN_WARNING PFX
+                       "Only %d pstates usable (use ACPI driver for full "
+                       "range\n", data->batps);
                data->numps = data->batps;
        }
 
-       for ( j=1; j<data->numps; j++ ) {
+       for (j = 1; j < data->numps; j++) {
                if (pst[j-1].fid >= pst[j].fid) {
                        printk(KERN_ERR PFX "PST out of sequence\n");
                        return -EINVAL;
@@ -640,9 +683,11 @@ static int fill_powernow_table(struct powernow_k8_data *data, struct pst_s *pst,
        }
 
        for (j = 0; j < data->numps; j++) {
+               int freq;
                powernow_table[j].index = pst[j].fid; /* lower 8 bits */
                powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */
-               powernow_table[j].frequency = find_khz_freq_from_fid(pst[j].fid);
+               freq = find_khz_freq_from_fid(pst[j].fid);
+               powernow_table[j].frequency = freq;
        }
        powernow_table[data->numps].frequency = CPUFREQ_TABLE_END;
        powernow_table[data->numps].index = 0;
@@ -658,7 +703,8 @@ static int fill_powernow_table(struct powernow_k8_data *data, struct pst_s *pst,
                print_basics(data);
 
        for (j = 0; j < data->numps; j++)
-               if ((pst[j].fid==data->currfid) && (pst[j].vid==data->currvid))
+               if ((pst[j].fid == data->currfid) &&
+                   (pst[j].vid == data->currvid))
                        return 0;
 
        dprintk("currfid/vid do not match PST, ignoring\n");
@@ -698,7 +744,8 @@ static int find_psb_table(struct powernow_k8_data *data)
                }
 
                data->vstable = psb->vstable;
-               dprintk("voltage stabilization time: %d(*20us)\n", data->vstable);
+               dprintk("voltage stabilization time: %d(*20us)\n",
+                               data->vstable);
 
                dprintk("flags2: 0x%x\n", psb->flags2);
                data->rvo = psb->flags2 & 3;
@@ -713,11 +760,12 @@ static int find_psb_table(struct powernow_k8_data *data)
 
                dprintk("numpst: 0x%x\n", psb->num_tables);
                cpst = psb->num_tables;
-               if ((psb->cpuid == 0x00000fc0) || (psb->cpuid == 0x00000fe0) ){
+               if ((psb->cpuid == 0x00000fc0) ||
+                   (psb->cpuid == 0x00000fe0)) {
                        thiscpuid = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
-                       if ((thiscpuid == 0x00000fc0) || (thiscpuid == 0x00000fe0) ) {
+                       if ((thiscpuid == 0x00000fc0) ||
+                           (thiscpuid == 0x00000fe0))
                                cpst = 1;
-                       }
                }
                if (cpst != 1) {
                        printk(KERN_ERR FW_BUG PFX "numpst must be 1\n");
@@ -732,7 +780,8 @@ static int find_psb_table(struct powernow_k8_data *data)
 
                data->numps = psb->numps;
                dprintk("numpstates: 0x%x\n", data->numps);
-               return fill_powernow_table(data, (struct pst_s *)(psb+1), maxvid);
+               return fill_powernow_table(data,
+                               (struct pst_s *)(psb+1), maxvid);
        }
        /*
         * If you see this message, complain to BIOS manufacturer. If
@@ -745,28 +794,31 @@ static int find_psb_table(struct powernow_k8_data *data)
         * BIOS and Kernel Developer's Guide, which is available on
         * www.amd.com
         */
-       printk(KERN_ERR PFX "BIOS error - no PSB or ACPI _PSS objects\n");
+       printk(KERN_ERR FW_BUG PFX "No PSB or ACPI _PSS objects\n");
        return -ENODEV;
 }
 
-#ifdef CONFIG_X86_POWERNOW_K8_ACPI
-static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index)
+static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data,
+               unsigned int index)
 {
+       acpi_integer control;
+
        if (!data->acpi_data.state_count || (cpu_family == CPU_HW_PSTATE))
                return;
 
-       data->irt = (data->acpi_data.states[index].control >> IRT_SHIFT) & IRT_MASK;
-       data->rvo = (data->acpi_data.states[index].control >> RVO_SHIFT) & RVO_MASK;
-       data->exttype = (data->acpi_data.states[index].control >> EXT_TYPE_SHIFT) & EXT_TYPE_MASK;
-       data->plllock = (data->acpi_data.states[index].control >> PLL_L_SHIFT) & PLL_L_MASK;
-       data->vidmvs = 1 << ((data->acpi_data.states[index].control >> MVS_SHIFT) & MVS_MASK);
-       data->vstable = (data->acpi_data.states[index].control >> VST_SHIFT) & VST_MASK;
-}
+       control = data->acpi_data.states[index].control; data->irt = (control
+                       >> IRT_SHIFT) & IRT_MASK; data->rvo = (control >>
+                               RVO_SHIFT) & RVO_MASK; data->exttype = (control
+                                       >> EXT_TYPE_SHIFT) & EXT_TYPE_MASK;
+       data->plllock = (control >> PLL_L_SHIFT) & PLL_L_MASK; data->vidmvs = 1
+               << ((control >> MVS_SHIFT) & MVS_MASK); data->vstable =
+               (control >> VST_SHIFT) & VST_MASK; }
 
 static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
 {
        struct cpufreq_frequency_table *powernow_table;
        int ret_val = -ENODEV;
+       acpi_integer space_id;
 
        if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) {
                dprintk("register performance failed: bad ACPI data\n");
@@ -779,11 +831,12 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
                goto err_out;
        }
 
-       if ((data->acpi_data.control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) ||
-               (data->acpi_data.status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) {
+       space_id = data->acpi_data.control_register.space_id;
+       if ((space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) ||
+               (space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) {
                dprintk("Invalid control/status registers (%x - %x)\n",
                        data->acpi_data.control_register.space_id,
-                       data->acpi_data.status_register.space_id);
+                       space_id);
                goto err_out;
        }
 
@@ -802,7 +855,8 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
        if (ret_val)
                goto err_out_mem;
 
-       powernow_table[data->acpi_data.state_count].frequency = CPUFREQ_TABLE_END;
+       powernow_table[data->acpi_data.state_count].frequency =
+               CPUFREQ_TABLE_END;
        powernow_table[data->acpi_data.state_count].index = 0;
        data->powernow_table = powernow_table;
 
@@ -830,13 +884,15 @@ err_out_mem:
 err_out:
        acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
 
-       /* data->acpi_data.state_count informs us at ->exit() whether ACPI was used */
+       /* data->acpi_data.state_count informs us at ->exit()
+        * whether ACPI was used */
        data->acpi_data.state_count = 0;
 
        return ret_val;
 }
 
-static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table)
+static int fill_powernow_table_pstate(struct powernow_k8_data *data,
+               struct cpufreq_frequency_table *powernow_table)
 {
        int i;
        u32 hi = 0, lo = 0;
@@ -848,84 +904,101 @@ static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpuf
 
                index = data->acpi_data.states[i].control & HW_PSTATE_MASK;
                if (index > data->max_hw_pstate) {
-                       printk(KERN_ERR PFX "invalid pstate %d - bad value %d.\n", i, index);
-                       printk(KERN_ERR PFX "Please report to BIOS manufacturer\n");
-                       powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+                       printk(KERN_ERR PFX "invalid pstate %d - "
+                                       "bad value %d.\n", i, index);
+                       printk(KERN_ERR PFX "Please report to BIOS "
+                                       "manufacturer\n");
+                       invalidate_entry(data, i);
                        continue;
                }
                rdmsr(MSR_PSTATE_DEF_BASE + index, lo, hi);
                if (!(hi & HW_PSTATE_VALID_MASK)) {
                        dprintk("invalid pstate %d, ignoring\n", index);
-                       powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+                       invalidate_entry(data, i);
                        continue;
                }
 
                powernow_table[i].index = index;
 
-               powernow_table[i].frequency = data->acpi_data.states[i].core_frequency * 1000;
+               powernow_table[i].frequency =
+                       data->acpi_data.states[i].core_frequency * 1000;
        }
        return 0;
 }
 
-static int fill_powernow_table_fidvid(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table)
+static int fill_powernow_table_fidvid(struct powernow_k8_data *data,
+               struct cpufreq_frequency_table *powernow_table)
 {
        int i;
        int cntlofreq = 0;
+
        for (i = 0; i < data->acpi_data.state_count; i++) {
                u32 fid;
                u32 vid;
+               u32 freq, index;
+               acpi_integer status, control;
 
                if (data->exttype) {
-                       fid = data->acpi_data.states[i].status & EXT_FID_MASK;
-                       vid = (data->acpi_data.states[i].status >> VID_SHIFT) & EXT_VID_MASK;
+                       status =  data->acpi_data.states[i].status;
+                       fid = status & EXT_FID_MASK;
+                       vid = (status >> VID_SHIFT) & EXT_VID_MASK;
                } else {
-                       fid = data->acpi_data.states[i].control & FID_MASK;
-                       vid = (data->acpi_data.states[i].control >> VID_SHIFT) & VID_MASK;
+                       control =  data->acpi_data.states[i].control;
+                       fid = control & FID_MASK;
+                       vid = (control >> VID_SHIFT) & VID_MASK;
                }
 
                dprintk("   %d : fid 0x%x, vid 0x%x\n", i, fid, vid);
 
-               powernow_table[i].index = fid; /* lower 8 bits */
-               powernow_table[i].index |= (vid << 8); /* upper 8 bits */
-               powernow_table[i].frequency = find_khz_freq_from_fid(fid);
+               index = fid | (vid<<8);
+               powernow_table[i].index = index;
+
+               freq = find_khz_freq_from_fid(fid);
+               powernow_table[i].frequency = freq;
 
                /* verify frequency is OK */
-               if ((powernow_table[i].frequency > (MAX_FREQ * 1000)) ||
-                       (powernow_table[i].frequency < (MIN_FREQ * 1000))) {
-                       dprintk("invalid freq %u kHz, ignoring\n", powernow_table[i].frequency);
-                       powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+               if ((freq > (MAX_FREQ * 1000)) || (freq < (MIN_FREQ * 1000))) {
+                       dprintk("invalid freq %u kHz, ignoring\n", freq);
+                       invalidate_entry(data, i);
                        continue;
                }
 
-               /* verify voltage is OK - BIOSs are using "off" to indicate invalid */
+               /* verify voltage is OK -
+                * BIOSs are using "off" to indicate invalid */
                if (vid == VID_OFF) {
                        dprintk("invalid vid %u, ignoring\n", vid);
-                       powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+                       invalidate_entry(data, i);
                        continue;
                }
 
                /* verify only 1 entry from the lo frequency table */
                if (fid < HI_FID_TABLE_BOTTOM) {
                        if (cntlofreq) {
-                               /* if both entries are the same, ignore this one ... */
-                               if ((powernow_table[i].frequency != powernow_table[cntlofreq].frequency) ||
-                                   (powernow_table[i].index != powernow_table[cntlofreq].index)) {
-                                       printk(KERN_ERR PFX "Too many lo freq table entries\n");
+                               /* if both entries are the same,
+                                * ignore this one ... */
+                               if ((freq != powernow_table[cntlofreq].frequency) ||
+                                   (index != powernow_table[cntlofreq].index)) {
+                                       printk(KERN_ERR PFX
+                                               "Too many lo freq table "
+                                               "entries\n");
                                        return 1;
                                }
 
-                               dprintk("double low frequency table entry, ignoring it.\n");
-                               powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+                               dprintk("double low frequency table entry, "
+                                               "ignoring it.\n");
+                               invalidate_entry(data, i);
                                continue;
                        } else
                                cntlofreq = i;
                }
 
-               if (powernow_table[i].frequency != (data->acpi_data.states[i].core_frequency * 1000)) {
-                       printk(KERN_INFO PFX "invalid freq entries %u kHz vs. %u kHz\n",
-                               powernow_table[i].frequency,
-                               (unsigned int) (data->acpi_data.states[i].core_frequency * 1000));
-                       powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+               if (freq != (data->acpi_data.states[i].core_frequency * 1000)) {
+                       printk(KERN_INFO PFX "invalid freq entries "
+                               "%u kHz vs. %u kHz\n", freq,
+                               (unsigned int)
+                               (data->acpi_data.states[i].core_frequency
+                                * 1000));
+                       invalidate_entry(data, i);
                        continue;
                }
        }
@@ -935,7 +1008,8 @@ static int fill_powernow_table_fidvid(struct powernow_k8_data *data, struct cpuf
 static void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data)
 {
        if (data->acpi_data.state_count)
-               acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
+               acpi_processor_unregister_performance(&data->acpi_data,
+                               data->cpu);
        free_cpumask_var(data->acpi_data.shared_cpu_map);
 }
 
@@ -953,15 +1027,9 @@ static int get_transition_latency(struct powernow_k8_data *data)
        return 1000 * max_latency;
 }
 
-#else
-static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) { return -ENODEV; }
-static void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data) { return; }
-static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index) { return; }
-static int get_transition_latency(struct powernow_k8_data *data) { return 0; }
-#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
-
 /* Take a frequency, and issue the fid/vid transition command */
-static int transition_frequency_fidvid(struct powernow_k8_data *data, unsigned int index)
+static int transition_frequency_fidvid(struct powernow_k8_data *data,
+               unsigned int index)
 {
        u32 fid = 0;
        u32 vid = 0;
@@ -989,7 +1057,8 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data, unsigned i
                return 0;
        }
 
-       if ((fid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
+       if ((fid < HI_FID_TABLE_BOTTOM) &&
+           (data->currfid < HI_FID_TABLE_BOTTOM)) {
                printk(KERN_ERR PFX
                       "ignoring illegal change in lo freq table-%x to 0x%x\n",
                       data->currfid, fid);
@@ -1017,7 +1086,8 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data, unsigned i
 }
 
 /* Take a frequency, and issue the hardware pstate transition command */
-static int transition_frequency_pstate(struct powernow_k8_data *data, unsigned int index)
+static int transition_frequency_pstate(struct powernow_k8_data *data,
+               unsigned int index)
 {
        u32 pstate = 0;
        int res, i;
@@ -1029,7 +1099,8 @@ static int transition_frequency_pstate(struct powernow_k8_data *data, unsigned i
        pstate = index & HW_PSTATE_MASK;
        if (pstate > data->max_hw_pstate)
                return 0;
-       freqs.old = find_khz_freq_from_pstate(data->powernow_table, data->currpstate);
+       freqs.old = find_khz_freq_from_pstate(data->powernow_table,
+                       data->currpstate);
        freqs.new = find_khz_freq_from_pstate(data->powernow_table, pstate);
 
        for_each_cpu_mask_nr(i, *(data->available_cores)) {
@@ -1048,7 +1119,8 @@ static int transition_frequency_pstate(struct powernow_k8_data *data, unsigned i
 }
 
 /* Driver entry point to switch to the target frequency */
-static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation)
+static int powernowk8_target(struct cpufreq_policy *pol,
+               unsigned targfreq, unsigned relation)
 {
        cpumask_t oldmask;
        struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu);
@@ -1087,14 +1159,18 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi
                dprintk("targ: curr fid 0x%x, vid 0x%x\n",
                data->currfid, data->currvid);
 
-               if ((checkvid != data->currvid) || (checkfid != data->currfid)) {
+               if ((checkvid != data->currvid) ||
+                   (checkfid != data->currfid)) {
                        printk(KERN_INFO PFX
-                               "error - out of sync, fix 0x%x 0x%x, vid 0x%x 0x%x\n",
-                               checkfid, data->currfid, checkvid, data->currvid);
+                               "error - out of sync, fix 0x%x 0x%x, "
+                               "vid 0x%x 0x%x\n",
+                               checkfid, data->currfid,
+                               checkvid, data->currvid);
                }
        }
 
-       if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate))
+       if (cpufreq_frequency_table_target(pol, data->powernow_table,
+                               targfreq, relation, &newstate))
                goto err_out;
 
        mutex_lock(&fidvid_mutex);
@@ -1114,7 +1190,8 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi
        mutex_unlock(&fidvid_mutex);
 
        if (cpu_family == CPU_HW_PSTATE)
-               pol->cur = find_khz_freq_from_pstate(data->powernow_table, newstate);
+               pol->cur = find_khz_freq_from_pstate(data->powernow_table,
+                               newstate);
        else
                pol->cur = find_khz_freq_from_fid(data->currfid);
        ret = 0;
@@ -1141,6 +1218,7 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
        struct powernow_k8_data *data;
        cpumask_t oldmask;
        int rc;
+       static int print_once;
 
        if (!cpu_online(pol->cpu))
                return -ENODEV;
@@ -1163,33 +1241,31 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
                 * an UP version, and is deprecated by AMD.
                 */
                if (num_online_cpus() != 1) {
-#ifndef CONFIG_ACPI_PROCESSOR
-                       printk(KERN_ERR PFX "ACPI Processor support is required "
-                              "for SMP systems but is absent. Please load the "
-                              "ACPI Processor module before starting this "
-                              "driver.\n");
-#else
-                       printk(KERN_ERR FW_BUG PFX "Your BIOS does not provide"
-                              " ACPI _PSS objects in a way that Linux "
-                              "understands. Please report this to the Linux "
-                              "ACPI maintainers and complain to your BIOS "
-                              "vendor.\n");
-#endif
-                       kfree(data);
-                       return -ENODEV;
+                       /*
+                        * Replace this one with print_once as soon as such a
+                        * thing gets introduced
+                        */
+                       if (!print_once) {
+                               WARN_ONCE(1, KERN_ERR FW_BUG PFX "Your BIOS "
+                                       "does not provide ACPI _PSS objects "
+                                       "in a way that Linux understands. "
+                                       "Please report this to the Linux ACPI"
+                                       " maintainers and complain to your "
+                                       "BIOS vendor.\n");
+                               print_once++;
+                       }
+                       goto err_out;
                }
                if (pol->cpu != 0) {
                        printk(KERN_ERR FW_BUG PFX "No ACPI _PSS objects for "
                               "CPU other than CPU0. Complain to your BIOS "
                               "vendor.\n");
-                       kfree(data);
-                       return -ENODEV;
+                       goto err_out;
                }
                rc = find_psb_table(data);
-               if (rc) {
-                       kfree(data);
-                       return -ENODEV;
-               }
+               if (rc)
+                       goto err_out;
+
                /* Take a crude guess here.
                 * That guess was in microseconds, so multiply with 1000 */
                pol->cpuinfo.transition_latency = (
@@ -1204,16 +1280,16 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
 
        if (smp_processor_id() != pol->cpu) {
                printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu);
-               goto err_out;
+               goto err_out_unmask;
        }
 
        if (pending_bit_stuck()) {
                printk(KERN_ERR PFX "failing init, change pending bit set\n");
-               goto err_out;
+               goto err_out_unmask;
        }
 
        if (query_current_values_with_pending_wait(data))
-               goto err_out;
+               goto err_out_unmask;
 
        if (cpu_family == CPU_OPTERON)
                fidvid_msr_init();
@@ -1228,7 +1304,8 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
        data->available_cores = pol->cpus;
 
        if (cpu_family == CPU_HW_PSTATE)
-               pol->cur = find_khz_freq_from_pstate(data->powernow_table, data->currpstate);
+               pol->cur = find_khz_freq_from_pstate(data->powernow_table,
+                               data->currpstate);
        else
                pol->cur = find_khz_freq_from_fid(data->currfid);
        dprintk("policy current frequency %d kHz\n", pol->cur);
@@ -1245,7 +1322,8 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
        cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu);
 
        if (cpu_family == CPU_HW_PSTATE)
-               dprintk("cpu_init done, current pstate 0x%x\n", data->currpstate);
+               dprintk("cpu_init done, current pstate 0x%x\n",
+                               data->currpstate);
        else
                dprintk("cpu_init done, current fid 0x%x, vid 0x%x\n",
                        data->currfid, data->currvid);
@@ -1254,15 +1332,16 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
 
        return 0;
 
-err_out:
+err_out_unmask:
        set_cpus_allowed_ptr(current, &oldmask);
        powernow_k8_cpu_exit_acpi(data);
 
+err_out:
        kfree(data);
        return -ENODEV;
 }
 
-static int __devexit powernowk8_cpu_exit (struct cpufreq_policy *pol)
+static int __devexit powernowk8_cpu_exit(struct cpufreq_policy *pol)
 {
        struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu);
 
@@ -1279,7 +1358,7 @@ static int __devexit powernowk8_cpu_exit (struct cpufreq_policy *pol)
        return 0;
 }
 
-static unsigned int powernowk8_get (unsigned int cpu)
+static unsigned int powernowk8_get(unsigned int cpu)
 {
        struct powernow_k8_data *data;
        cpumask_t oldmask = current->cpus_allowed;
@@ -1315,7 +1394,7 @@ out:
        return khz;
 }
 
-static struct freq_attrpowernow_k8_attr[] = {
+static struct freq_attr *powernow_k8_attr[] = {
        &cpufreq_freq_attr_scaling_available_freqs,
        NULL,
 };
@@ -1360,7 +1439,8 @@ static void __exit powernowk8_exit(void)
        cpufreq_unregister_driver(&cpufreq_amd64_driver);
 }
 
-MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com> and Mark Langsdorf <mark.langsdorf@amd.com>");
+MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com> and "
+               "Mark Langsdorf <mark.langsdorf@amd.com>");
 MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver.");
 MODULE_LICENSE("GPL");
 
index 8ecc75b6c7c3b39338ff2991bb413cedf94eebb9..6c6698feade15f7ab034d665874a8f4e4fde8384 100644 (file)
@@ -45,11 +45,10 @@ struct powernow_k8_data {
         * frequency is in kHz */
        struct cpufreq_frequency_table  *powernow_table;
 
-#ifdef CONFIG_X86_POWERNOW_K8_ACPI
        /* the acpi table needs to be kept. it's only available if ACPI was
         * used to determine valid frequency/vid/fid states */
        struct acpi_processor_performance acpi_data;
-#endif
+
        /* we need to keep track of associated cores, but let cpufreq
         * handle hotplug events - so just point at cpufreq pol->cpus
         * structure */
@@ -222,10 +221,8 @@ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid);
 
 static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index);
 
-#ifdef CONFIG_X86_POWERNOW_K8_ACPI
 static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table);
 static int fill_powernow_table_fidvid(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table);
-#endif
 
 #ifdef CONFIG_SMP
 static inline void define_siblings(int cpu, cpumask_t cpu_sharedcore_mask[])
index 42da9bd677d6ee91ed3cb41f2b09c62d3bf5b26b..435a996a613a6cb3e1a5da201c45ff48b7ac9f3e 100644 (file)
 
 #include <linux/delay.h>
 #include <linux/cpufreq.h>
+#include <linux/timex.h>
+#include <linux/io.h>
 
 #include <asm/msr.h>
-#include <asm/timex.h>
-#include <asm/io.h>
 
 #define MMCR_BASE      0xfffef000      /* The default base address */
 #define OFFS_CPUCTL    0x2   /* CPU Control Register */
 
 static __u8 __iomem *cpuctl;
 
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "sc520_freq", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+               "sc520_freq", msg)
+#define PFX "sc520_freq: "
 
 static struct cpufreq_frequency_table sc520_freq_table[] = {
        {0x01,  100000},
@@ -43,7 +45,8 @@ static unsigned int sc520_freq_get_cpu_frequency(unsigned int cpu)
 
        switch (clockspeed_reg & 0x03) {
        default:
-               printk(KERN_ERR "sc520_freq: error: cpuctl register has unexpected value %02x\n", clockspeed_reg);
+               printk(KERN_ERR PFX "error: cpuctl register has unexpected "
+                               "value %02x\n", clockspeed_reg);
        case 0x01:
                return 100000;
        case 0x02:
@@ -51,7 +54,7 @@ static unsigned int sc520_freq_get_cpu_frequency(unsigned int cpu)
        }
 }
 
-static void sc520_freq_set_cpu_state (unsigned int state)
+static void sc520_freq_set_cpu_state(unsigned int state)
 {
 
        struct cpufreq_freqs    freqs;
@@ -76,18 +79,19 @@ static void sc520_freq_set_cpu_state (unsigned int state)
        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 };
 
-static int sc520_freq_verify (struct cpufreq_policy *policy)
+static int sc520_freq_verify(struct cpufreq_policy *policy)
 {
        return cpufreq_frequency_table_verify(policy, &sc520_freq_table[0]);
 }
 
-static int sc520_freq_target (struct cpufreq_policy *policy,
+static int sc520_freq_target(struct cpufreq_policy *policy,
                            unsigned int target_freq,
                            unsigned int relation)
 {
        unsigned int newstate = 0;
 
-       if (cpufreq_frequency_table_target(policy, sc520_freq_table, target_freq, relation, &newstate))
+       if (cpufreq_frequency_table_target(policy, sc520_freq_table,
+                               target_freq, relation, &newstate))
                return -EINVAL;
 
        sc520_freq_set_cpu_state(newstate);
@@ -116,7 +120,7 @@ static int sc520_freq_cpu_init(struct cpufreq_policy *policy)
 
        result = cpufreq_frequency_table_cpuinfo(policy, sc520_freq_table);
        if (result)
-               return (result);
+               return result;
 
        cpufreq_frequency_table_get_attr(sc520_freq_table, policy->cpu);
 
@@ -131,7 +135,7 @@ static int sc520_freq_cpu_exit(struct cpufreq_policy *policy)
 }
 
 
-static struct freq_attrsc520_freq_attr[] = {
+static struct freq_attr *sc520_freq_attr[] = {
        &cpufreq_freq_attr_scaling_available_freqs,
        NULL,
 };
@@ -155,13 +159,13 @@ static int __init sc520_freq_init(void)
        int err;
 
        /* Test if we have the right hardware */
-       if(c->x86_vendor != X86_VENDOR_AMD ||
-                               c->x86 != 4 || c->x86_model != 9) {
+       if (c->x86_vendor != X86_VENDOR_AMD ||
+           c->x86 != 4 || c->x86_model != 9) {
                dprintk("no Elan SC520 processor found!\n");
                return -ENODEV;
        }
        cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1);
-       if(!cpuctl) {
+       if (!cpuctl) {
                printk(KERN_ERR "sc520_freq: error: failed to remap memory\n");
                return -ENOMEM;
        }
index dedc1e98f1683c4e35e0b194b81aed354ab3e55a..8bbb11adb3157d1e0d436910dd6b2423cec025ef 100644 (file)
@@ -39,7 +39,7 @@ static struct pci_dev *speedstep_chipset_dev;
 
 /* speedstep_processor
  */
-static unsigned int speedstep_processor = 0;
+static unsigned int speedstep_processor;
 
 static u32 pmbase;
 
@@ -54,7 +54,8 @@ static struct cpufreq_frequency_table speedstep_freqs[] = {
 };
 
 
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "speedstep-ich", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+               "speedstep-ich", msg)
 
 
 /**
@@ -62,7 +63,7 @@ static struct cpufreq_frequency_table speedstep_freqs[] = {
  *
  * Returns: -ENODEV if no register could be found
  */
-static int speedstep_find_register (void)
+static int speedstep_find_register(void)
 {
        if (!speedstep_chipset_dev)
                return -ENODEV;
@@ -90,7 +91,7 @@ static int speedstep_find_register (void)
  *
  *   Tries to change the SpeedStep state.
  */
-static void speedstep_set_state (unsigned int state)
+static void speedstep_set_state(unsigned int state)
 {
        u8 pm2_blk;
        u8 value;
@@ -133,11 +134,11 @@ static void speedstep_set_state (unsigned int state)
 
        dprintk("read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value);
 
-       if (state == (value & 0x1)) {
-               dprintk("change to %u MHz succeeded\n", (speedstep_get_processor_frequency(speedstep_processor) / 1000));
-       } else {
-               printk (KERN_ERR "cpufreq: change failed - I/O error\n");
-       }
+       if (state == (value & 0x1))
+               dprintk("change to %u MHz succeeded\n",
+                       speedstep_get_frequency(speedstep_processor) / 1000);
+       else
+               printk(KERN_ERR "cpufreq: change failed - I/O error\n");
 
        return;
 }
@@ -149,7 +150,7 @@ static void speedstep_set_state (unsigned int state)
  *   Tries to activate the SpeedStep status and control registers.
  * Returns -EINVAL on an unsupported chipset, and zero on success.
  */
-static int speedstep_activate (void)
+static int speedstep_activate(void)
 {
        u16 value = 0;
 
@@ -175,20 +176,18 @@ static int speedstep_activate (void)
  * functions. Returns the SPEEDSTEP_CHIPSET_-number for the detected
  * chipset, or zero on failure.
  */
-static unsigned int speedstep_detect_chipset (void)
+static unsigned int speedstep_detect_chipset(void)
 {
        speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
                              PCI_DEVICE_ID_INTEL_82801DB_12,
-                             PCI_ANY_ID,
-                             PCI_ANY_ID,
+                             PCI_ANY_ID, PCI_ANY_ID,
                              NULL);
        if (speedstep_chipset_dev)
                return 4; /* 4-M */
 
        speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
                              PCI_DEVICE_ID_INTEL_82801CA_12,
-                             PCI_ANY_ID,
-                             PCI_ANY_ID,
+                             PCI_ANY_ID, PCI_ANY_ID,
                              NULL);
        if (speedstep_chipset_dev)
                return 3; /* 3-M */
@@ -196,8 +195,7 @@ static unsigned int speedstep_detect_chipset (void)
 
        speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
                              PCI_DEVICE_ID_INTEL_82801BA_10,
-                             PCI_ANY_ID,
-                             PCI_ANY_ID,
+                             PCI_ANY_ID, PCI_ANY_ID,
                              NULL);
        if (speedstep_chipset_dev) {
                /* speedstep.c causes lockups on Dell Inspirons 8000 and
@@ -208,8 +206,7 @@ static unsigned int speedstep_detect_chipset (void)
 
                hostbridge  = pci_get_subsys(PCI_VENDOR_ID_INTEL,
                              PCI_DEVICE_ID_INTEL_82815_MC,
-                             PCI_ANY_ID,
-                             PCI_ANY_ID,
+                             PCI_ANY_ID, PCI_ANY_ID,
                              NULL);
 
                if (!hostbridge)
@@ -236,7 +233,7 @@ static unsigned int _speedstep_get(const struct cpumask *cpus)
 
        cpus_allowed = current->cpus_allowed;
        set_cpus_allowed_ptr(current, cpus);
-       speed = speedstep_get_processor_frequency(speedstep_processor);
+       speed = speedstep_get_frequency(speedstep_processor);
        set_cpus_allowed_ptr(current, &cpus_allowed);
        dprintk("detected %u kHz as current frequency\n", speed);
        return speed;
@@ -251,11 +248,12 @@ static unsigned int speedstep_get(unsigned int cpu)
  * speedstep_target - set a new CPUFreq policy
  * @policy: new policy
  * @target_freq: the target frequency
- * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
+ * @relation: how that frequency relates to achieved frequency
+ *     (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
  *
  * Sets a new CPUFreq policy.
  */
-static int speedstep_target (struct cpufreq_policy *policy,
+static int speedstep_target(struct cpufreq_policy *policy,
                             unsigned int target_freq,
                             unsigned int relation)
 {
@@ -264,7 +262,8 @@ static int speedstep_target (struct cpufreq_policy *policy,
        cpumask_t cpus_allowed;
        int i;
 
-       if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], target_freq, relation, &newstate))
+       if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0],
+                               target_freq, relation, &newstate))
                return -EINVAL;
 
        freqs.old = _speedstep_get(policy->cpus);
@@ -308,7 +307,7 @@ static int speedstep_target (struct cpufreq_policy *policy,
  * Limit must be within speedstep_low_freq and speedstep_high_freq, with
  * at least one border included.
  */
-static int speedstep_verify (struct cpufreq_policy *policy)
+static int speedstep_verify(struct cpufreq_policy *policy)
 {
        return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
 }
@@ -344,7 +343,8 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy)
                return -EIO;
 
        dprintk("currently at %s speed setting - %i MHz\n",
-               (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high",
+               (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency)
+               ? "low" : "high",
                (speed / 1000));
 
        /* cpuinfo and default policy values */
@@ -352,9 +352,9 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy)
 
        result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs);
        if (result)
-               return (result);
+               return result;
 
-        cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu);
+       cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu);
 
        return 0;
 }
@@ -366,7 +366,7 @@ static int speedstep_cpu_exit(struct cpufreq_policy *policy)
        return 0;
 }
 
-static struct freq_attrspeedstep_attr[] = {
+static struct freq_attr *speedstep_attr[] = {
        &cpufreq_freq_attr_scaling_available_freqs,
        NULL,
 };
@@ -396,13 +396,15 @@ static int __init speedstep_init(void)
        /* detect processor */
        speedstep_processor = speedstep_detect_processor();
        if (!speedstep_processor) {
-               dprintk("Intel(R) SpeedStep(TM) capable processor not found\n");
+               dprintk("Intel(R) SpeedStep(TM) capable processor "
+                               "not found\n");
                return -ENODEV;
        }
 
        /* detect chipset */
        if (!speedstep_detect_chipset()) {
-               dprintk("Intel(R) SpeedStep(TM) for this chipset not (yet) available.\n");
+               dprintk("Intel(R) SpeedStep(TM) for this chipset not "
+                               "(yet) available.\n");
                return -ENODEV;
        }
 
@@ -431,9 +433,11 @@ static void __exit speedstep_exit(void)
 }
 
 
-MODULE_AUTHOR ("Dave Jones <davej@redhat.com>, Dominik Brodowski <linux@brodo.de>");
-MODULE_DESCRIPTION ("Speedstep driver for Intel mobile processors on chipsets with ICH-M southbridges.");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Dave Jones <davej@redhat.com>, "
+               "Dominik Brodowski <linux@brodo.de>");
+MODULE_DESCRIPTION("Speedstep driver for Intel mobile processors on chipsets "
+               "with ICH-M southbridges.");
+MODULE_LICENSE("GPL");
 
 module_init(speedstep_init);
 module_exit(speedstep_exit);
index cdac7d62369b645a00b15c41c4d617e78b86c3b1..2e3c6862657b2c0b61d5c445edc5bf6927cc626f 100644 (file)
 #include <linux/slab.h>
 
 #include <asm/msr.h>
+#include <asm/tsc.h>
 #include "speedstep-lib.h"
 
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "speedstep-lib", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+               "speedstep-lib", msg)
+
+#define PFX "speedstep-lib: "
 
 #ifdef CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK
-static int relaxed_check = 0;
+static int relaxed_check;
 #else
 #define relaxed_check 0
 #endif
@@ -30,14 +34,14 @@ static int relaxed_check = 0;
  *                   GET PROCESSOR CORE SPEED IN KHZ                 *
  *********************************************************************/
 
-static unsigned int pentium3_get_frequency (unsigned int processor)
+static unsigned int pentium3_get_frequency(unsigned int processor)
 {
-        /* See table 14 of p3_ds.pdf and table 22 of 29834003.pdf */
+       /* See table 14 of p3_ds.pdf and table 22 of 29834003.pdf */
        struct {
                unsigned int ratio;     /* Frequency Multiplier (x10) */
                u8 bitmap;              /* power on configuration bits
                                        [27, 25:22] (in MSR 0x2a) */
-       } msr_decode_mult [] = {
+       } msr_decode_mult[] = {
                { 30, 0x01 },
                { 35, 0x05 },
                { 40, 0x02 },
@@ -52,7 +56,7 @@ static unsigned int pentium3_get_frequency (unsigned int processor)
                { 85, 0x26 },
                { 90, 0x20 },
                { 100, 0x2b },
-               { 0, 0xff }     /* error or unknown value */
+               { 0, 0xff }     /* error or unknown value */
        };
 
        /* PIII(-M) FSB settings: see table b1-b of 24547206.pdf */
@@ -60,7 +64,7 @@ static unsigned int pentium3_get_frequency (unsigned int processor)
                unsigned int value;     /* Front Side Bus speed in MHz */
                u8 bitmap;              /* power on configuration bits [18: 19]
                                        (in MSR 0x2a) */
-       } msr_decode_fsb [] = {
+       } msr_decode_fsb[] = {
                {  66, 0x0 },
                { 100, 0x2 },
                { 133, 0x1 },
@@ -85,7 +89,7 @@ static unsigned int pentium3_get_frequency (unsigned int processor)
        }
 
        /* decode the multiplier */
-       if (processor == SPEEDSTEP_PROCESSOR_PIII_C_EARLY) {
+       if (processor == SPEEDSTEP_CPU_PIII_C_EARLY) {
                dprintk("workaround for early PIIIs\n");
                msr_lo &= 0x03c00000;
        } else
@@ -97,9 +101,10 @@ static unsigned int pentium3_get_frequency (unsigned int processor)
                j++;
        }
 
-       dprintk("speed is %u\n", (msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100));
+       dprintk("speed is %u\n",
+               (msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100));
 
-       return (msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100);
+       return msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100;
 }
 
 
@@ -112,20 +117,23 @@ static unsigned int pentiumM_get_frequency(void)
 
        /* see table B-2 of 24547212.pdf */
        if (msr_lo & 0x00040000) {
-               printk(KERN_DEBUG "speedstep-lib: PM - invalid FSB: 0x%x 0x%x\n", msr_lo, msr_tmp);
+               printk(KERN_DEBUG PFX "PM - invalid FSB: 0x%x 0x%x\n",
+                               msr_lo, msr_tmp);
                return 0;
        }
 
        msr_tmp = (msr_lo >> 22) & 0x1f;
-       dprintk("bits 22-26 are 0x%x, speed is %u\n", msr_tmp, (msr_tmp * 100 * 1000));
+       dprintk("bits 22-26 are 0x%x, speed is %u\n",
+                       msr_tmp, (msr_tmp * 100 * 1000));
 
-       return (msr_tmp * 100 * 1000);
+       return msr_tmp * 100 * 1000;
 }
 
 static unsigned int pentium_core_get_frequency(void)
 {
        u32 fsb = 0;
        u32 msr_lo, msr_tmp;
+       int ret;
 
        rdmsr(MSR_FSB_FREQ, msr_lo, msr_tmp);
        /* see table B-2 of 25366920.pdf */
@@ -153,12 +161,15 @@ static unsigned int pentium_core_get_frequency(void)
        }
 
        rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp);
-       dprintk("PCORE - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", msr_lo, msr_tmp);
+       dprintk("PCORE - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n",
+                       msr_lo, msr_tmp);
 
        msr_tmp = (msr_lo >> 22) & 0x1f;
-       dprintk("bits 22-26 are 0x%x, speed is %u\n", msr_tmp, (msr_tmp * fsb));
+       dprintk("bits 22-26 are 0x%x, speed is %u\n",
+                       msr_tmp, (msr_tmp * fsb));
 
-       return (msr_tmp * fsb);
+       ret = (msr_tmp * fsb);
+       return ret;
 }
 
 
@@ -167,6 +178,16 @@ static unsigned int pentium4_get_frequency(void)
        struct cpuinfo_x86 *c = &boot_cpu_data;
        u32 msr_lo, msr_hi, mult;
        unsigned int fsb = 0;
+       unsigned int ret;
+       u8 fsb_code;
+
+       /* Pentium 4 Model 0 and 1 do not have the Core Clock Frequency
+        * to System Bus Frequency Ratio Field in the Processor Frequency
+        * Configuration Register of the MSR. Therefore the current
+        * frequency cannot be calculated and has to be measured.
+        */
+       if (c->x86_model < 2)
+               return cpu_khz;
 
        rdmsr(0x2c, msr_lo, msr_hi);
 
@@ -177,62 +198,61 @@ static unsigned int pentium4_get_frequency(void)
         * revision #12 in Table B-1: MSRs in the Pentium 4 and
         * Intel Xeon Processors, on page B-4 and B-5.
         */
-       if (c->x86_model < 2)
+       fsb_code = (msr_lo >> 16) & 0x7;
+       switch (fsb_code) {
+       case 0:
                fsb = 100 * 1000;
-       else {
-               u8 fsb_code = (msr_lo >> 16) & 0x7;
-               switch (fsb_code) {
-               case 0:
-                       fsb = 100 * 1000;
-                       break;
-               case 1:
-                       fsb = 13333 * 10;
-                       break;
-               case 2:
-                       fsb = 200 * 1000;
-                       break;
-               }
+               break;
+       case 1:
+               fsb = 13333 * 10;
+               break;
+       case 2:
+               fsb = 200 * 1000;
+               break;
        }
 
        if (!fsb)
-               printk(KERN_DEBUG "speedstep-lib: couldn't detect FSB speed. Please send an e-mail to <linux@brodo.de>\n");
+               printk(KERN_DEBUG PFX "couldn't detect FSB speed. "
+                               "Please send an e-mail to <linux@brodo.de>\n");
 
        /* Multiplier. */
        mult = msr_lo >> 24;
 
-       dprintk("P4 - FSB %u kHz; Multiplier %u; Speed %u kHz\n", fsb, mult, (fsb * mult));
+       dprintk("P4 - FSB %u kHz; Multiplier %u; Speed %u kHz\n",
+                       fsb, mult, (fsb * mult));
 
-       return (fsb * mult);
+       ret = (fsb * mult);
+       return ret;
 }
 
 
-unsigned int speedstep_get_processor_frequency(unsigned int processor)
+unsigned int speedstep_get_frequency(unsigned int processor)
 {
        switch (processor) {
-       case SPEEDSTEP_PROCESSOR_PCORE:
+       case SPEEDSTEP_CPU_PCORE:
                return pentium_core_get_frequency();
-       case SPEEDSTEP_PROCESSOR_PM:
+       case SPEEDSTEP_CPU_PM:
                return pentiumM_get_frequency();
-       case SPEEDSTEP_PROCESSOR_P4D:
-       case SPEEDSTEP_PROCESSOR_P4M:
+       case SPEEDSTEP_CPU_P4D:
+       case SPEEDSTEP_CPU_P4M:
                return pentium4_get_frequency();
-       case SPEEDSTEP_PROCESSOR_PIII_T:
-       case SPEEDSTEP_PROCESSOR_PIII_C:
-       case SPEEDSTEP_PROCESSOR_PIII_C_EARLY:
+       case SPEEDSTEP_CPU_PIII_T:
+       case SPEEDSTEP_CPU_PIII_C:
+       case SPEEDSTEP_CPU_PIII_C_EARLY:
                return pentium3_get_frequency(processor);
        default:
                return 0;
        };
        return 0;
 }
-EXPORT_SYMBOL_GPL(speedstep_get_processor_frequency);
+EXPORT_SYMBOL_GPL(speedstep_get_frequency);
 
 
 /*********************************************************************
  *                 DETECT SPEEDSTEP-CAPABLE PROCESSOR                *
  *********************************************************************/
 
-unsigned int speedstep_detect_processor (void)
+unsigned int speedstep_detect_processor(void)
 {
        struct cpuinfo_x86 *c = &cpu_data(0);
        u32 ebx, msr_lo, msr_hi;
@@ -261,7 +281,7 @@ unsigned int speedstep_detect_processor (void)
                         * sample has ebx = 0x0f, production has 0x0e.
                         */
                        if ((ebx == 0x0e) || (ebx == 0x0f))
-                               return SPEEDSTEP_PROCESSOR_P4M;
+                               return SPEEDSTEP_CPU_P4M;
                        break;
                case 7:
                        /*
@@ -272,7 +292,7 @@ unsigned int speedstep_detect_processor (void)
                         * samples are only of B-stepping...
                         */
                        if (ebx == 0x0e)
-                               return SPEEDSTEP_PROCESSOR_P4M;
+                               return SPEEDSTEP_CPU_P4M;
                        break;
                case 9:
                        /*
@@ -288,10 +308,13 @@ unsigned int speedstep_detect_processor (void)
                         * M-P4-Ms may have either ebx=0xe or 0xf [see above]
                         * M-P4/533 have either ebx=0xe or 0xf. [25317607.pdf]
                         * also, M-P4M HTs have ebx=0x8, too
-                        * For now, they are distinguished by the model_id string
+                        * For now, they are distinguished by the model_id
+                        * string
                         */
-                       if ((ebx == 0x0e) || (strstr(c->x86_model_id,"Mobile Intel(R) Pentium(R) 4") != NULL))
-                               return SPEEDSTEP_PROCESSOR_P4M;
+                       if ((ebx == 0x0e) ||
+                               (strstr(c->x86_model_id,
+                                   "Mobile Intel(R) Pentium(R) 4") != NULL))
+                               return SPEEDSTEP_CPU_P4M;
                        break;
                default:
                        break;
@@ -301,7 +324,8 @@ unsigned int speedstep_detect_processor (void)
 
        switch (c->x86_model) {
        case 0x0B: /* Intel PIII [Tualatin] */
-               /* cpuid_ebx(1) is 0x04 for desktop PIII, 0x06 for mobile PIII-M */
+               /* cpuid_ebx(1) is 0x04 for desktop PIII,
+                * 0x06 for mobile PIII-M */
                ebx = cpuid_ebx(0x00000001);
                dprintk("ebx is %x\n", ebx);
 
@@ -313,14 +337,15 @@ unsigned int speedstep_detect_processor (void)
                /* So far all PIII-M processors support SpeedStep. See
                 * Intel's 24540640.pdf of June 2003
                 */
-               return SPEEDSTEP_PROCESSOR_PIII_T;
+               return SPEEDSTEP_CPU_PIII_T;
 
        case 0x08: /* Intel PIII [Coppermine] */
 
                /* all mobile PIII Coppermines have FSB 100 MHz
                 * ==> sort out a few desktop PIIIs. */
                rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_hi);
-               dprintk("Coppermine: MSR_IA32_EBL_CR_POWERON is 0x%x, 0x%x\n", msr_lo, msr_hi);
+               dprintk("Coppermine: MSR_IA32_EBL_CR_POWERON is 0x%x, 0x%x\n",
+                               msr_lo, msr_hi);
                msr_lo &= 0x00c0000;
                if (msr_lo != 0x0080000)
                        return 0;
@@ -332,13 +357,15 @@ unsigned int speedstep_detect_processor (void)
                 * bit 56 or 57 is set
                 */
                rdmsr(MSR_IA32_PLATFORM_ID, msr_lo, msr_hi);
-               dprintk("Coppermine: MSR_IA32_PLATFORM ID is 0x%x, 0x%x\n", msr_lo, msr_hi);
-               if ((msr_hi & (1<<18)) && (relaxed_check ? 1 : (msr_hi & (3<<24)))) {
+               dprintk("Coppermine: MSR_IA32_PLATFORM ID is 0x%x, 0x%x\n",
+                               msr_lo, msr_hi);
+               if ((msr_hi & (1<<18)) &&
+                   (relaxed_check ? 1 : (msr_hi & (3<<24)))) {
                        if (c->x86_mask == 0x01) {
                                dprintk("early PIII version\n");
-                               return SPEEDSTEP_PROCESSOR_PIII_C_EARLY;
+                               return SPEEDSTEP_CPU_PIII_C_EARLY;
                        } else
-                               return SPEEDSTEP_PROCESSOR_PIII_C;
+                               return SPEEDSTEP_CPU_PIII_C;
                }
 
        default:
@@ -369,7 +396,7 @@ unsigned int speedstep_get_freqs(unsigned int processor,
        dprintk("trying to determine both speeds\n");
 
        /* get current speed */
-       prev_speed = speedstep_get_processor_frequency(processor);
+       prev_speed = speedstep_get_frequency(processor);
        if (!prev_speed)
                return -EIO;
 
@@ -379,7 +406,7 @@ unsigned int speedstep_get_freqs(unsigned int processor,
 
        /* switch to low state */
        set_state(SPEEDSTEP_LOW);
-       *low_speed = speedstep_get_processor_frequency(processor);
+       *low_speed = speedstep_get_frequency(processor);
        if (!*low_speed) {
                ret = -EIO;
                goto out;
@@ -398,7 +425,7 @@ unsigned int speedstep_get_freqs(unsigned int processor,
        if (transition_latency)
                do_gettimeofday(&tv2);
 
-       *high_speed = speedstep_get_processor_frequency(processor);
+       *high_speed = speedstep_get_frequency(processor);
        if (!*high_speed) {
                ret = -EIO;
                goto out;
@@ -426,9 +453,12 @@ unsigned int speedstep_get_freqs(unsigned int processor,
                /* check if the latency measurement is too high or too low
                 * and set it to a safe value (500uSec) in that case
                 */
-               if (*transition_latency > 10000000 || *transition_latency < 50000) {
-                       printk (KERN_WARNING "speedstep: frequency transition measured seems out of "
-                                       "range (%u nSec), falling back to a safe one of %u nSec.\n",
+               if (*transition_latency > 10000000 ||
+                   *transition_latency < 50000) {
+                       printk(KERN_WARNING PFX "frequency transition "
+                                       "measured seems out of range (%u "
+                                       "nSec), falling back to a safe one of"
+                                       "%u nSec.\n",
                                        *transition_latency, 500000);
                        *transition_latency = 500000;
                }
@@ -436,15 +466,16 @@ unsigned int speedstep_get_freqs(unsigned int processor,
 
 out:
        local_irq_restore(flags);
-       return (ret);
+       return ret;
 }
 EXPORT_SYMBOL_GPL(speedstep_get_freqs);
 
 #ifdef CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK
 module_param(relaxed_check, int, 0444);
-MODULE_PARM_DESC(relaxed_check, "Don't do all checks for speedstep capability.");
+MODULE_PARM_DESC(relaxed_check,
+               "Don't do all checks for speedstep capability.");
 #endif
 
-MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>");
-MODULE_DESCRIPTION ("Library for Intel SpeedStep 1 or 2 cpufreq drivers.");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
+MODULE_DESCRIPTION("Library for Intel SpeedStep 1 or 2 cpufreq drivers.");
+MODULE_LICENSE("GPL");
index b11bcc608cac5cbe4b823ba8c3675e48932f560c..2b6c04e5a30429a011a78f20e74bf7413548f6ad 100644 (file)
 
 /* processors */
 
-#define SPEEDSTEP_PROCESSOR_PIII_C_EARLY       0x00000001  /* Coppermine core */
-#define SPEEDSTEP_PROCESSOR_PIII_C             0x00000002  /* Coppermine core */
-#define SPEEDSTEP_PROCESSOR_PIII_T             0x00000003  /* Tualatin core */
-#define SPEEDSTEP_PROCESSOR_P4M                        0x00000004  /* P4-M  */
+#define SPEEDSTEP_CPU_PIII_C_EARLY     0x00000001  /* Coppermine core */
+#define SPEEDSTEP_CPU_PIII_C           0x00000002  /* Coppermine core */
+#define SPEEDSTEP_CPU_PIII_T           0x00000003  /* Tualatin core */
+#define SPEEDSTEP_CPU_P4M              0x00000004  /* P4-M  */
 
 /* the following processors are not speedstep-capable and are not auto-detected
  * in speedstep_detect_processor(). However, their speed can be detected using
- * the speedstep_get_processor_frequency() call. */
-#define SPEEDSTEP_PROCESSOR_PM                 0xFFFFFF03  /* Pentium M  */
-#define SPEEDSTEP_PROCESSOR_P4D                        0xFFFFFF04  /* desktop P4  */
-#define SPEEDSTEP_PROCESSOR_PCORE              0xFFFFFF05  /* Core */
+ * the speedstep_get_frequency() call. */
+#define SPEEDSTEP_CPU_PM               0xFFFFFF03  /* Pentium M  */
+#define SPEEDSTEP_CPU_P4D              0xFFFFFF04  /* desktop P4  */
+#define SPEEDSTEP_CPU_PCORE            0xFFFFFF05  /* Core */
 
 /* speedstep states -- only two of them */
 
@@ -34,7 +34,7 @@
 extern unsigned int speedstep_detect_processor (void);
 
 /* detect the current speed (in khz) of the processor */
-extern unsigned int speedstep_get_processor_frequency(unsigned int processor);
+extern unsigned int speedstep_get_frequency(unsigned int processor);
 
 
 /* detect the low and high speeds of the processor. The callback
index 8a85c93bd62a4678f58be9cdcf8040875b564756..befea088e4f58b472259f955a8304e5c834e083f 100644 (file)
@@ -19,8 +19,8 @@
 #include <linux/cpufreq.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
+#include <linux/io.h>
 #include <asm/ist.h>
-#include <asm/io.h>
 
 #include "speedstep-lib.h"
 
  * If user gives it, these are used.
  *
  */
-static int smi_port = 0;
-static int smi_cmd = 0;
-static unsigned int smi_sig = 0;
+static int smi_port;
+static int smi_cmd;
+static unsigned int smi_sig;
 
 /* info about the processor */
-static unsigned int speedstep_processor = 0;
+static unsigned int speedstep_processor;
 
 /*
  * There are only two frequency states for each processor. Values
@@ -56,12 +56,13 @@ static struct cpufreq_frequency_table speedstep_freqs[] = {
  * of DMA activity going on? */
 #define SMI_TRIES 5
 
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "speedstep-smi", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+               "speedstep-smi", msg)
 
 /**
  * speedstep_smi_ownership
  */
-static int speedstep_smi_ownership (void)
+static int speedstep_smi_ownership(void)
 {
        u32 command, result, magic, dummy;
        u32 function = GET_SPEEDSTEP_OWNER;
@@ -70,16 +71,18 @@ static int speedstep_smi_ownership (void)
        command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
        magic = virt_to_phys(magic_data);
 
-       dprintk("trying to obtain ownership with command %x at port %x\n", command, smi_port);
+       dprintk("trying to obtain ownership with command %x at port %x\n",
+                       command, smi_port);
 
        __asm__ __volatile__(
                "push %%ebp\n"
                "out %%al, (%%dx)\n"
                "pop %%ebp\n"
-               : "=D" (result), "=a" (dummy), "=b" (dummy), "=c" (dummy), "=d" (dummy),
-                       "=S" (dummy)
+               : "=D" (result),
+                 "=a" (dummy), "=b" (dummy), "=c" (dummy), "=d" (dummy),
+                 "=S" (dummy)
                : "a" (command), "b" (function), "c" (0), "d" (smi_port),
-                       "D" (0), "S" (magic)
+                 "D" (0), "S" (magic)
                : "memory"
        );
 
@@ -97,10 +100,10 @@ static int speedstep_smi_ownership (void)
  * even hangs [cf. bugme.osdl.org # 1422] on earlier systems. Empirical testing
  * shows that the latter occurs if !(ist_info.event & 0xFFFF).
  */
-static int speedstep_smi_get_freqs (unsigned int *low, unsigned int *high)
+static int speedstep_smi_get_freqs(unsigned int *low, unsigned int *high)
 {
        u32 command, result = 0, edi, high_mhz, low_mhz, dummy;
-       u32 state=0;
+       u32 state = 0;
        u32 function = GET_SPEEDSTEP_FREQS;
 
        if (!(ist_info.event & 0xFFFF)) {
@@ -110,17 +113,25 @@ static int speedstep_smi_get_freqs (unsigned int *low, unsigned int *high)
 
        command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
 
-       dprintk("trying to determine frequencies with command %x at port %x\n", command, smi_port);
+       dprintk("trying to determine frequencies with command %x at port %x\n",
+                       command, smi_port);
 
        __asm__ __volatile__(
                "push %%ebp\n"
                "out %%al, (%%dx)\n"
                "pop %%ebp"
-               : "=a" (result), "=b" (high_mhz), "=c" (low_mhz), "=d" (state), "=D" (edi), "=S" (dummy)
-               : "a" (command), "b" (function), "c" (state), "d" (smi_port), "S" (0), "D" (0)
+               : "=a" (result),
+                 "=b" (high_mhz),
+                 "=c" (low_mhz),
+                 "=d" (state), "=D" (edi), "=S" (dummy)
+               : "a" (command),
+                 "b" (function),
+                 "c" (state),
+                 "d" (smi_port), "S" (0), "D" (0)
        );
 
-       dprintk("result %x, low_freq %u, high_freq %u\n", result, low_mhz, high_mhz);
+       dprintk("result %x, low_freq %u, high_freq %u\n",
+                       result, low_mhz, high_mhz);
 
        /* abort if results are obviously incorrect... */
        if ((high_mhz + low_mhz) < 600)
@@ -137,26 +148,30 @@ static int speedstep_smi_get_freqs (unsigned int *low, unsigned int *high)
  * @state: processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
  *
  */
-static int speedstep_get_state (void)
+static int speedstep_get_state(void)
 {
-       u32 function=GET_SPEEDSTEP_STATE;
+       u32 function = GET_SPEEDSTEP_STATE;
        u32 result, state, edi, command, dummy;
 
        command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
 
-       dprintk("trying to determine current setting with command %x at port %x\n", command, smi_port);
+       dprintk("trying to determine current setting with command %x "
+               "at port %x\n", command, smi_port);
 
        __asm__ __volatile__(
                "push %%ebp\n"
                "out %%al, (%%dx)\n"
                "pop %%ebp\n"
-               : "=a" (result), "=b" (state), "=D" (edi), "=c" (dummy), "=d" (dummy), "=S" (dummy)
-               : "a" (command), "b" (function), "c" (0), "d" (smi_port), "S" (0), "D" (0)
+               : "=a" (result),
+                 "=b" (state), "=D" (edi),
+                 "=c" (dummy), "=d" (dummy), "=S" (dummy)
+               : "a" (command), "b" (function), "c" (0),
+                 "d" (smi_port), "S" (0), "D" (0)
        );
 
        dprintk("state is %x, result is %x\n", state, result);
 
-       return (state & 1);
+       return state & 1;
 }
 
 
@@ -165,11 +180,11 @@ static int speedstep_get_state (void)
  * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
  *
  */
-static void speedstep_set_state (unsigned int state)
+static void speedstep_set_state(unsigned int state)
 {
        unsigned int result = 0, command, new_state, dummy;
        unsigned long flags;
-       unsigned int function=SET_SPEEDSTEP_STATE;
+       unsigned int function = SET_SPEEDSTEP_STATE;
        unsigned int retry = 0;
 
        if (state > 0x1)
@@ -180,11 +195,14 @@ static void speedstep_set_state (unsigned int state)
 
        command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
 
-       dprintk("trying to set frequency to state %u with command %x at port %x\n", state, command, smi_port);
+       dprintk("trying to set frequency to state %u "
+               "with command %x at port %x\n",
+               state, command, smi_port);
 
        do {
                if (retry) {
-                       dprintk("retry %u, previous result %u, waiting...\n", retry, result);
+                       dprintk("retry %u, previous result %u, waiting...\n",
+                                       retry, result);
                        mdelay(retry * 50);
                }
                retry++;
@@ -192,20 +210,26 @@ static void speedstep_set_state (unsigned int state)
                        "push %%ebp\n"
                        "out %%al, (%%dx)\n"
                        "pop %%ebp"
-                       : "=b" (new_state), "=D" (result), "=c" (dummy), "=a" (dummy),
-                               "=d" (dummy), "=S" (dummy)
-                       : "a" (command), "b" (function), "c" (state), "d" (smi_port), "S" (0), "D" (0)
+                       : "=b" (new_state), "=D" (result),
+                         "=c" (dummy), "=a" (dummy),
+                         "=d" (dummy), "=S" (dummy)
+                       : "a" (command), "b" (function), "c" (state),
+                         "d" (smi_port), "S" (0), "D" (0)
                        );
        } while ((new_state != state) && (retry <= SMI_TRIES));
 
        /* enable IRQs */
        local_irq_restore(flags);
 
-       if (new_state == state) {
-               dprintk("change to %u MHz succeeded after %u tries with result %u\n", (speedstep_freqs[new_state].frequency / 1000), retry, result);
-       } else {
-               printk(KERN_ERR "cpufreq: change to state %u failed with new_state %u and result %u\n", state, new_state, result);
-       }
+       if (new_state == state)
+               dprintk("change to %u MHz succeeded after %u tries "
+                       "with result %u\n",
+                       (speedstep_freqs[new_state].frequency / 1000),
+                       retry, result);
+       else
+               printk(KERN_ERR "cpufreq: change to state %u "
+                       "failed with new_state %u and result %u\n",
+                       state, new_state, result);
 
        return;
 }
@@ -219,13 +243,14 @@ static void speedstep_set_state (unsigned int state)
  *
  * Sets a new CPUFreq policy/freq.
  */
-static int speedstep_target (struct cpufreq_policy *policy,
+static int speedstep_target(struct cpufreq_policy *policy,
                        unsigned int target_freq, unsigned int relation)
 {
        unsigned int newstate = 0;
        struct cpufreq_freqs freqs;
 
-       if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], target_freq, relation, &newstate))
+       if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0],
+                               target_freq, relation, &newstate))
                return -EINVAL;
 
        freqs.old = speedstep_freqs[speedstep_get_state()].frequency;
@@ -250,7 +275,7 @@ static int speedstep_target (struct cpufreq_policy *policy,
  * Limit must be within speedstep_low_freq and speedstep_high_freq, with
  * at least one border included.
  */
-static int speedstep_verify (struct cpufreq_policy *policy)
+static int speedstep_verify(struct cpufreq_policy *policy)
 {
        return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
 }
@@ -259,7 +284,8 @@ static int speedstep_verify (struct cpufreq_policy *policy)
 static int speedstep_cpu_init(struct cpufreq_policy *policy)
 {
        int result;
-       unsigned int speed,state;
+       unsigned int speed, state;
+       unsigned int *low, *high;
 
        /* capability check */
        if (policy->cpu != 0)
@@ -272,19 +298,23 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy)
        }
 
        /* detect low and high frequency */
-       result = speedstep_smi_get_freqs(&speedstep_freqs[SPEEDSTEP_LOW].frequency,
-                               &speedstep_freqs[SPEEDSTEP_HIGH].frequency);
+       low = &speedstep_freqs[SPEEDSTEP_LOW].frequency;
+       high = &speedstep_freqs[SPEEDSTEP_HIGH].frequency;
+
+       result = speedstep_smi_get_freqs(low, high);
        if (result) {
-               /* fall back to speedstep_lib.c dection mechanism: try both states out */
-               dprintk("could not detect low and high frequencies by SMI call.\n");
+               /* fall back to speedstep_lib.c dection mechanism:
+                * try both states out */
+               dprintk("could not detect low and high frequencies "
+                               "by SMI call.\n");
                result = speedstep_get_freqs(speedstep_processor,
-                               &speedstep_freqs[SPEEDSTEP_LOW].frequency,
-                               &speedstep_freqs[SPEEDSTEP_HIGH].frequency,
+                               low, high,
                                NULL,
                                &speedstep_set_state);
 
                if (result) {
-                       dprintk("could not detect two different speeds -- aborting.\n");
+                       dprintk("could not detect two different speeds"
+                                       " -- aborting.\n");
                        return result;
                } else
                        dprintk("workaround worked.\n");
@@ -295,7 +325,8 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy)
        speed = speedstep_freqs[state].frequency;
 
        dprintk("currently at %s speed setting - %i MHz\n",
-               (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high",
+               (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency)
+               ? "low" : "high",
                (speed / 1000));
 
        /* cpuinfo and default policy values */
@@ -304,7 +335,7 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy)
 
        result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs);
        if (result)
-               return (result);
+               return result;
 
        cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu);
 
@@ -321,7 +352,7 @@ static unsigned int speedstep_get(unsigned int cpu)
 {
        if (cpu)
                return -ENODEV;
-       return speedstep_get_processor_frequency(speedstep_processor);
+       return speedstep_get_frequency(speedstep_processor);
 }
 
 
@@ -335,7 +366,7 @@ static int speedstep_resume(struct cpufreq_policy *policy)
        return result;
 }
 
-static struct freq_attrspeedstep_attr[] = {
+static struct freq_attr *speedstep_attr[] = {
        &cpufreq_freq_attr_scaling_available_freqs,
        NULL,
 };
@@ -364,21 +395,23 @@ static int __init speedstep_init(void)
        speedstep_processor = speedstep_detect_processor();
 
        switch (speedstep_processor) {
-       case SPEEDSTEP_PROCESSOR_PIII_T:
-       case SPEEDSTEP_PROCESSOR_PIII_C:
-       case SPEEDSTEP_PROCESSOR_PIII_C_EARLY:
+       case SPEEDSTEP_CPU_PIII_T:
+       case SPEEDSTEP_CPU_PIII_C:
+       case SPEEDSTEP_CPU_PIII_C_EARLY:
                break;
        default:
                speedstep_processor = 0;
        }
 
        if (!speedstep_processor) {
-               dprintk ("No supported Intel CPU detected.\n");
+               dprintk("No supported Intel CPU detected.\n");
                return -ENODEV;
        }
 
-       dprintk("signature:0x%.8lx, command:0x%.8lx, event:0x%.8lx, perf_level:0x%.8lx.\n",
-               ist_info.signature, ist_info.command, ist_info.event, ist_info.perf_level);
+       dprintk("signature:0x%.8lx, command:0x%.8lx, "
+               "event:0x%.8lx, perf_level:0x%.8lx.\n",
+               ist_info.signature, ist_info.command,
+               ist_info.event, ist_info.perf_level);
 
        /* Error if no IST-SMI BIOS or no PARM
                 sig= 'ISGE' aka 'Intel Speedstep Gate E' */
@@ -416,17 +449,20 @@ static void __exit speedstep_exit(void)
        cpufreq_unregister_driver(&speedstep_driver);
 }
 
-module_param(smi_port,  int, 0444);
-module_param(smi_cmd,   int, 0444);
-module_param(smi_sig,  uint, 0444);
+module_param(smi_port, int, 0444);
+module_param(smi_cmd,  int, 0444);
+module_param(smi_sig, uint, 0444);
 
-MODULE_PARM_DESC(smi_port, "Override the BIOS-given IST port with this value -- Intel's default setting is 0xb2");
-MODULE_PARM_DESC(smi_cmd, "Override the BIOS-given IST command with this value -- Intel's default setting is 0x82");
-MODULE_PARM_DESC(smi_sig, "Set to 1 to fake the IST signature when using the SMI interface.");
+MODULE_PARM_DESC(smi_port, "Override the BIOS-given IST port with this value "
+               "-- Intel's default setting is 0xb2");
+MODULE_PARM_DESC(smi_cmd, "Override the BIOS-given IST command with this value "
+               "-- Intel's default setting is 0x82");
+MODULE_PARM_DESC(smi_sig, "Set to 1 to fake the IST signature when using the "
+               "SMI interface.");
 
-MODULE_AUTHOR ("Hiroshi Miura");
-MODULE_DESCRIPTION ("Speedstep driver for IST applet SMI interface.");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Hiroshi Miura");
+MODULE_DESCRIPTION("Speedstep driver for IST applet SMI interface.");
+MODULE_LICENSE("GPL");
 
 module_init(speedstep_init);
 module_exit(speedstep_exit);
index d5cebb52d45ba3e30844b8171e275eb76647493c..b8e7aaf7ef751eee1fc338b79094a6a15dba6d4d 100644 (file)
@@ -543,8 +543,6 @@ unsigned long native_calibrate_tsc(void)
        return tsc_pit_min;
 }
 
-#ifdef CONFIG_X86_32
-/* Only called from the Powernow K7 cpu freq driver */
 int recalibrate_cpu_khz(void)
 {
 #ifndef CONFIG_SMP
@@ -566,7 +564,6 @@ int recalibrate_cpu_khz(void)
 
 EXPORT_SYMBOL(recalibrate_cpu_khz);
 
-#endif /* CONFIG_X86_32 */
 
 /* Accelerators for sched_clock()
  * convert from cycles(64bits) => nanoseconds (64bits)
index 8dde4fcf99c947b7c292be0a775b102e43260879..74d0e622a5153beb758ca95123da6c07628ebc23 100644 (file)
@@ -56,6 +56,7 @@ config CRYPTO_BLKCIPHER2
        tristate
        select CRYPTO_ALGAPI2
        select CRYPTO_RNG2
+       select CRYPTO_WORKQUEUE
 
 config CRYPTO_HASH
        tristate
@@ -75,6 +76,10 @@ config CRYPTO_RNG2
        tristate
        select CRYPTO_ALGAPI2
 
+config CRYPTO_PCOMP
+       tristate
+       select CRYPTO_ALGAPI2
+
 config CRYPTO_MANAGER
        tristate "Cryptographic algorithm manager"
        select CRYPTO_MANAGER2
@@ -87,6 +92,7 @@ config CRYPTO_MANAGER2
        select CRYPTO_AEAD2
        select CRYPTO_HASH2
        select CRYPTO_BLKCIPHER2
+       select CRYPTO_PCOMP
 
 config CRYPTO_GF128MUL
        tristate "GF(2^128) multiplication functions (EXPERIMENTAL)"
@@ -106,11 +112,15 @@ config CRYPTO_NULL
        help
          These are 'Null' algorithms, used by IPsec, which do nothing.
 
+config CRYPTO_WORKQUEUE
+       tristate
+
 config CRYPTO_CRYPTD
        tristate "Software async crypto daemon"
        select CRYPTO_BLKCIPHER
        select CRYPTO_HASH
        select CRYPTO_MANAGER
+       select CRYPTO_WORKQUEUE
        help
          This is a generic software asynchronous crypto daemon that
          converts an arbitrary synchronous software crypto algorithm
@@ -470,6 +480,31 @@ config CRYPTO_AES_X86_64
 
          See <http://csrc.nist.gov/encryption/aes/> for more information.
 
+config CRYPTO_AES_NI_INTEL
+       tristate "AES cipher algorithms (AES-NI)"
+       depends on (X86 || UML_X86) && 64BIT
+       select CRYPTO_AES_X86_64
+       select CRYPTO_CRYPTD
+       select CRYPTO_ALGAPI
+       help
+         Use Intel AES-NI instructions for AES algorithm.
+
+         AES cipher algorithms (FIPS-197). AES uses the Rijndael
+         algorithm.
+
+         Rijndael appears to be consistently a very good performer in
+         both hardware and software across a wide range of computing
+         environments regardless of its use in feedback or non-feedback
+         modes. Its key setup time is excellent, and its key agility is
+         good. Rijndael's very low memory requirements make it very well
+         suited for restricted-space environments, in which it also
+         demonstrates excellent performance. Rijndael's operations are
+         among the easiest to defend against power and timing attacks.
+
+         The AES specifies three key sizes: 128, 192 and 256 bits
+
+         See <http://csrc.nist.gov/encryption/aes/> for more information.
+
 config CRYPTO_ANUBIS
        tristate "Anubis cipher algorithm"
        select CRYPTO_ALGAPI
@@ -714,6 +749,15 @@ config CRYPTO_DEFLATE
 
          You will most probably want this if using IPSec.
 
+config CRYPTO_ZLIB
+       tristate "Zlib compression algorithm"
+       select CRYPTO_PCOMP
+       select ZLIB_INFLATE
+       select ZLIB_DEFLATE
+       select NLATTR
+       help
+         This is the zlib algorithm.
+
 config CRYPTO_LZO
        tristate "LZO compression algorithm"
        select CRYPTO_ALGAPI
index 46b08bf2035fdfd5e404953aa2b5f8740efa4c82..673d9f7c1bda564be9869c763caf722b9bfc5d8b 100644 (file)
@@ -5,6 +5,8 @@
 obj-$(CONFIG_CRYPTO) += crypto.o
 crypto-objs := api.o cipher.o digest.o compress.o
 
+obj-$(CONFIG_CRYPTO_WORKQUEUE) += crypto_wq.o
+
 obj-$(CONFIG_CRYPTO_FIPS) += fips.o
 
 crypto_algapi-$(CONFIG_PROC_FS) += proc.o
@@ -25,6 +27,8 @@ crypto_hash-objs += ahash.o
 crypto_hash-objs += shash.o
 obj-$(CONFIG_CRYPTO_HASH2) += crypto_hash.o
 
+obj-$(CONFIG_CRYPTO_PCOMP) += pcompress.o
+
 cryptomgr-objs := algboss.o testmgr.o
 
 obj-$(CONFIG_CRYPTO_MANAGER2) += cryptomgr.o
@@ -70,6 +74,7 @@ obj-$(CONFIG_CRYPTO_ANUBIS) += anubis.o
 obj-$(CONFIG_CRYPTO_SEED) += seed.o
 obj-$(CONFIG_CRYPTO_SALSA20) += salsa20_generic.o
 obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o
+obj-$(CONFIG_CRYPTO_ZLIB) += zlib.o
 obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o
 obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o
 obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o
index 94140b3756fcd598d49179deac34ed5a80a83ed5..e11ce37c71043c4948b492a1fc97074a8164a436 100644 (file)
@@ -282,6 +282,25 @@ static struct crypto_alg *crypto_lookup_skcipher(const char *name, u32 type,
                                          alg->cra_ablkcipher.ivsize))
                return alg;
 
+       crypto_mod_put(alg);
+       alg = crypto_alg_mod_lookup(name, type | CRYPTO_ALG_TESTED,
+                                   mask & ~CRYPTO_ALG_TESTED);
+       if (IS_ERR(alg))
+               return alg;
+
+       if ((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
+           CRYPTO_ALG_TYPE_GIVCIPHER) {
+               if ((alg->cra_flags ^ type ^ ~mask) & CRYPTO_ALG_TESTED) {
+                       crypto_mod_put(alg);
+                       alg = ERR_PTR(-ENOENT);
+               }
+               return alg;
+       }
+
+       BUG_ON(!((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
+                CRYPTO_ALG_TYPE_BLKCIPHER ? alg->cra_blkcipher.ivsize :
+                                            alg->cra_ablkcipher.ivsize));
+
        return ERR_PTR(crypto_givcipher_default(alg, type, mask));
 }
 
index 3a6f3f52c7c71911ac6cd7506493fb24ccd8eb50..d9aa733db1647b51c1eea32de4840dc21cd9659d 100644 (file)
@@ -422,6 +422,22 @@ static struct crypto_alg *crypto_lookup_aead(const char *name, u32 type,
        if (!alg->cra_aead.ivsize)
                return alg;
 
+       crypto_mod_put(alg);
+       alg = crypto_alg_mod_lookup(name, type | CRYPTO_ALG_TESTED,
+                                   mask & ~CRYPTO_ALG_TESTED);
+       if (IS_ERR(alg))
+               return alg;
+
+       if (alg->cra_type == &crypto_aead_type) {
+               if ((alg->cra_flags ^ type ^ ~mask) & CRYPTO_ALG_TESTED) {
+                       crypto_mod_put(alg);
+                       alg = ERR_PTR(-ENOENT);
+               }
+               return alg;
+       }
+
+       BUG_ON(!alg->cra_aead.ivsize);
+
        return ERR_PTR(crypto_nivaead_default(alg, type, mask));
 }
 
index 4601e4267c886182d6b037983fe2630fffb5d8dd..6906f92aeac03330653a9539550861f1f6cffb9f 100644 (file)
@@ -10,7 +10,7 @@
  *
  */
 
-#include <linux/crypto.h>
+#include <crypto/internal/aead.h>
 #include <linux/ctype.h>
 #include <linux/err.h>
 #include <linux/init.h>
@@ -206,8 +206,7 @@ static int cryptomgr_test(void *data)
        u32 type = param->type;
        int err = 0;
 
-       if (!((type ^ CRYPTO_ALG_TYPE_BLKCIPHER) &
-             CRYPTO_ALG_TYPE_BLKCIPHER_MASK) && !(type & CRYPTO_ALG_GENIV))
+       if (type & CRYPTO_ALG_TESTED)
                goto skiptest;
 
        err = alg_test(param->driver, param->alg, type, CRYPTO_ALG_TESTED);
@@ -223,6 +222,7 @@ static int cryptomgr_schedule_test(struct crypto_alg *alg)
 {
        struct task_struct *thread;
        struct crypto_test_param *param;
+       u32 type;
 
        if (!try_module_get(THIS_MODULE))
                goto err;
@@ -233,7 +233,19 @@ static int cryptomgr_schedule_test(struct crypto_alg *alg)
 
        memcpy(param->driver, alg->cra_driver_name, sizeof(param->driver));
        memcpy(param->alg, alg->cra_name, sizeof(param->alg));
-       param->type = alg->cra_flags;
+       type = alg->cra_flags;
+
+       /* This piece of crap needs to disappear into per-type test hooks. */
+       if ((!((type ^ CRYPTO_ALG_TYPE_BLKCIPHER) &
+              CRYPTO_ALG_TYPE_BLKCIPHER_MASK) && !(type & CRYPTO_ALG_GENIV) &&
+            ((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
+             CRYPTO_ALG_TYPE_BLKCIPHER ? alg->cra_blkcipher.ivsize :
+                                         alg->cra_ablkcipher.ivsize)) ||
+           (!((type ^ CRYPTO_ALG_TYPE_AEAD) & CRYPTO_ALG_TYPE_MASK) &&
+            alg->cra_type == &crypto_nivaead_type && alg->cra_aead.ivsize))
+               type |= CRYPTO_ALG_TESTED;
+
+       param->type = type;
 
        thread = kthread_run(cryptomgr_test, param, "cryptomgr_test");
        if (IS_ERR(thread))
index 0fac8ffc2fb7ef5dc6d39cca05d0292b584bb472..d80ed4c1e009da061b4d35d2e4bf3187e267b454 100644 (file)
@@ -132,9 +132,15 @@ static int _get_more_prng_bytes(struct prng_context *ctx)
                         */
                        if (!memcmp(ctx->rand_data, ctx->last_rand_data,
                                        DEFAULT_BLK_SZ)) {
+                               if (fips_enabled) {
+                                       panic("cprng %p Failed repetition check!\n",
+                                               ctx);
+                               }
+
                                printk(KERN_ERR
                                        "ctx %p Failed repetition check!\n",
                                        ctx);
+
                                ctx->flags |= PRNG_NEED_RESET;
                                return -EINVAL;
                        }
@@ -338,7 +344,16 @@ static int cprng_init(struct crypto_tfm *tfm)
 
        spin_lock_init(&ctx->prng_lock);
 
-       return reset_prng_context(ctx, NULL, DEFAULT_PRNG_KSZ, NULL, NULL);
+       if (reset_prng_context(ctx, NULL, DEFAULT_PRNG_KSZ, NULL, NULL) < 0)
+               return -EINVAL;
+
+       /*
+        * after allocation, we should always force the user to reset
+        * so they don't inadvertently use the insecure default values
+        * without specifying them intentially
+        */
+       ctx->flags |= PRNG_NEED_RESET;
+       return 0;
 }
 
 static void cprng_exit(struct crypto_tfm *tfm)
index 38a2bc02a98c7f6648f7a7a3388c6182b88c07f9..314dab96840e2792ee7af4b205b2ab63722faac4 100644 (file)
@@ -255,7 +255,7 @@ struct crypto_alg *crypto_alg_mod_lookup(const char *name, u32 type, u32 mask)
        struct crypto_alg *larval;
        int ok;
 
-       if (!(mask & CRYPTO_ALG_TESTED)) {
+       if (!((type | mask) & CRYPTO_ALG_TESTED)) {
                type |= CRYPTO_ALG_TESTED;
                mask |= CRYPTO_ALG_TESTED;
        }
@@ -464,8 +464,8 @@ err:
 }
 EXPORT_SYMBOL_GPL(crypto_alloc_base);
 
-struct crypto_tfm *crypto_create_tfm(struct crypto_alg *alg,
-                                    const struct crypto_type *frontend)
+void *crypto_create_tfm(struct crypto_alg *alg,
+                       const struct crypto_type *frontend)
 {
        char *mem;
        struct crypto_tfm *tfm = NULL;
@@ -499,9 +499,9 @@ out_free_tfm:
                crypto_shoot_alg(alg);
        kfree(mem);
 out_err:
-       tfm = ERR_PTR(err);
+       mem = ERR_PTR(err);
 out:
-       return tfm;
+       return mem;
 }
 EXPORT_SYMBOL_GPL(crypto_create_tfm);
 
@@ -525,12 +525,11 @@ EXPORT_SYMBOL_GPL(crypto_create_tfm);
  *
  *     In case of error the return value is an error pointer.
  */
-struct crypto_tfm *crypto_alloc_tfm(const char *alg_name,
-                                   const struct crypto_type *frontend,
-                                   u32 type, u32 mask)
+void *crypto_alloc_tfm(const char *alg_name,
+                      const struct crypto_type *frontend, u32 type, u32 mask)
 {
        struct crypto_alg *(*lookup)(const char *name, u32 type, u32 mask);
-       struct crypto_tfm *tfm;
+       void *tfm;
        int err;
 
        type &= frontend->maskclear;
index d70a41c002df192e49f77871cd0837732fbad861..90d26c91f4e9e183b2a41363318344e3d646c20a 100644 (file)
@@ -521,7 +521,7 @@ static int crypto_grab_nivcipher(struct crypto_skcipher_spawn *spawn,
        int err;
 
        type = crypto_skcipher_type(type);
-       mask = crypto_skcipher_mask(mask) | CRYPTO_ALG_GENIV;
+       mask = crypto_skcipher_mask(mask)| CRYPTO_ALG_GENIV;
 
        alg = crypto_alg_mod_lookup(name, type, mask);
        if (IS_ERR(alg))
index 7c37a497b860a27733eac07c4f7ff018c0abb4a3..ba200b07449d259cebf846a43e3f768e335606ef 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <crypto/internal/skcipher.h>
 #include <crypto/rng.h>
+#include <crypto/crypto_wq.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -133,7 +134,7 @@ static int async_chainiv_schedule_work(struct async_chainiv_ctx *ctx)
                        goto out;
        }
 
-       queued = schedule_work(&ctx->postponed);
+       queued = queue_work(kcrypto_wq, &ctx->postponed);
        BUG_ON(!queued);
 
 out:
index d29e06b350ffffb55d91fe62a00e7a804a324bbd..d14b22658d7a57d907344ba9145ae6c2c809660a 100644 (file)
 
 #include <crypto/algapi.h>
 #include <crypto/internal/hash.h>
+#include <crypto/cryptd.h>
+#include <crypto/crypto_wq.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
-#include <linux/kthread.h>
 #include <linux/list.h>
 #include <linux/module.h>
-#include <linux/mutex.h>
 #include <linux/scatterlist.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
-#include <linux/spinlock.h>
 
-#define CRYPTD_MAX_QLEN 100
+#define CRYPTD_MAX_CPU_QLEN 100
 
-struct cryptd_state {
-       spinlock_t lock;
-       struct mutex mutex;
+struct cryptd_cpu_queue {
        struct crypto_queue queue;
-       struct task_struct *task;
+       struct work_struct work;
+};
+
+struct cryptd_queue {
+       struct cryptd_cpu_queue *cpu_queue;
 };
 
 struct cryptd_instance_ctx {
        struct crypto_spawn spawn;
-       struct cryptd_state *state;
+       struct cryptd_queue *queue;
 };
 
 struct cryptd_blkcipher_ctx {
@@ -54,11 +55,85 @@ struct cryptd_hash_request_ctx {
        crypto_completion_t complete;
 };
 
-static inline struct cryptd_state *cryptd_get_state(struct crypto_tfm *tfm)
+static void cryptd_queue_worker(struct work_struct *work);
+
+static int cryptd_init_queue(struct cryptd_queue *queue,
+                            unsigned int max_cpu_qlen)
+{
+       int cpu;
+       struct cryptd_cpu_queue *cpu_queue;
+
+       queue->cpu_queue = alloc_percpu(struct cryptd_cpu_queue);
+       if (!queue->cpu_queue)
+               return -ENOMEM;
+       for_each_possible_cpu(cpu) {
+               cpu_queue = per_cpu_ptr(queue->cpu_queue, cpu);
+               crypto_init_queue(&cpu_queue->queue, max_cpu_qlen);
+               INIT_WORK(&cpu_queue->work, cryptd_queue_worker);
+       }
+       return 0;
+}
+
+static void cryptd_fini_queue(struct cryptd_queue *queue)
+{
+       int cpu;
+       struct cryptd_cpu_queue *cpu_queue;
+
+       for_each_possible_cpu(cpu) {
+               cpu_queue = per_cpu_ptr(queue->cpu_queue, cpu);
+               BUG_ON(cpu_queue->queue.qlen);
+       }
+       free_percpu(queue->cpu_queue);
+}
+
+static int cryptd_enqueue_request(struct cryptd_queue *queue,
+                                 struct crypto_async_request *request)
+{
+       int cpu, err;
+       struct cryptd_cpu_queue *cpu_queue;
+
+       cpu = get_cpu();
+       cpu_queue = per_cpu_ptr(queue->cpu_queue, cpu);
+       err = crypto_enqueue_request(&cpu_queue->queue, request);
+       queue_work_on(cpu, kcrypto_wq, &cpu_queue->work);
+       put_cpu();
+
+       return err;
+}
+
+/* Called in workqueue context, do one real cryption work (via
+ * req->complete) and reschedule itself if there are more work to
+ * do. */
+static void cryptd_queue_worker(struct work_struct *work)
+{
+       struct cryptd_cpu_queue *cpu_queue;
+       struct crypto_async_request *req, *backlog;
+
+       cpu_queue = container_of(work, struct cryptd_cpu_queue, work);
+       /* Only handle one request at a time to avoid hogging crypto
+        * workqueue. preempt_disable/enable is used to prevent
+        * being preempted by cryptd_enqueue_request() */
+       preempt_disable();
+       backlog = crypto_get_backlog(&cpu_queue->queue);
+       req = crypto_dequeue_request(&cpu_queue->queue);
+       preempt_enable();
+
+       if (!req)
+               return;
+
+       if (backlog)
+               backlog->complete(backlog, -EINPROGRESS);
+       req->complete(req, 0);
+
+       if (cpu_queue->queue.qlen)
+               queue_work(kcrypto_wq, &cpu_queue->work);
+}
+
+static inline struct cryptd_queue *cryptd_get_queue(struct crypto_tfm *tfm)
 {
        struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
        struct cryptd_instance_ctx *ictx = crypto_instance_ctx(inst);
-       return ictx->state;
+       return ictx->queue;
 }
 
 static int cryptd_blkcipher_setkey(struct crypto_ablkcipher *parent,
@@ -130,19 +205,13 @@ static int cryptd_blkcipher_enqueue(struct ablkcipher_request *req,
 {
        struct cryptd_blkcipher_request_ctx *rctx = ablkcipher_request_ctx(req);
        struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
-       struct cryptd_state *state =
-               cryptd_get_state(crypto_ablkcipher_tfm(tfm));
-       int err;
+       struct cryptd_queue *queue;
 
+       queue = cryptd_get_queue(crypto_ablkcipher_tfm(tfm));
        rctx->complete = req->base.complete;
        req->base.complete = complete;
 
-       spin_lock_bh(&state->lock);
-       err = ablkcipher_enqueue_request(&state->queue, req);
-       spin_unlock_bh(&state->lock);
-
-       wake_up_process(state->task);
-       return err;
+       return cryptd_enqueue_request(queue, &req->base);
 }
 
 static int cryptd_blkcipher_encrypt_enqueue(struct ablkcipher_request *req)
@@ -176,21 +245,12 @@ static int cryptd_blkcipher_init_tfm(struct crypto_tfm *tfm)
 static void cryptd_blkcipher_exit_tfm(struct crypto_tfm *tfm)
 {
        struct cryptd_blkcipher_ctx *ctx = crypto_tfm_ctx(tfm);
-       struct cryptd_state *state = cryptd_get_state(tfm);
-       int active;
-
-       mutex_lock(&state->mutex);
-       active = ablkcipher_tfm_in_queue(&state->queue,
-                                        __crypto_ablkcipher_cast(tfm));
-       mutex_unlock(&state->mutex);
-
-       BUG_ON(active);
 
        crypto_free_blkcipher(ctx->child);
 }
 
 static struct crypto_instance *cryptd_alloc_instance(struct crypto_alg *alg,
-                                                    struct cryptd_state *state)
+                                                    struct cryptd_queue *queue)
 {
        struct crypto_instance *inst;
        struct cryptd_instance_ctx *ctx;
@@ -213,7 +273,7 @@ static struct crypto_instance *cryptd_alloc_instance(struct crypto_alg *alg,
        if (err)
                goto out_free_inst;
 
-       ctx->state = state;
+       ctx->queue = queue;
 
        memcpy(inst->alg.cra_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
 
@@ -231,7 +291,7 @@ out_free_inst:
 }
 
 static struct crypto_instance *cryptd_alloc_blkcipher(
-       struct rtattr **tb, struct cryptd_state *state)
+       struct rtattr **tb, struct cryptd_queue *queue)
 {
        struct crypto_instance *inst;
        struct crypto_alg *alg;
@@ -241,7 +301,7 @@ static struct crypto_instance *cryptd_alloc_blkcipher(
        if (IS_ERR(alg))
                return ERR_CAST(alg);
 
-       inst = cryptd_alloc_instance(alg, state);
+       inst = cryptd_alloc_instance(alg, queue);
        if (IS_ERR(inst))
                goto out_put_alg;
 
@@ -289,15 +349,6 @@ static int cryptd_hash_init_tfm(struct crypto_tfm *tfm)
 static void cryptd_hash_exit_tfm(struct crypto_tfm *tfm)
 {
        struct cryptd_hash_ctx *ctx = crypto_tfm_ctx(tfm);
-       struct cryptd_state *state = cryptd_get_state(tfm);
-       int active;
-
-       mutex_lock(&state->mutex);
-       active = ahash_tfm_in_queue(&state->queue,
-                               __crypto_ahash_cast(tfm));
-       mutex_unlock(&state->mutex);
-
-       BUG_ON(active);
 
        crypto_free_hash(ctx->child);
 }
@@ -323,19 +374,13 @@ static int cryptd_hash_enqueue(struct ahash_request *req,
 {
        struct cryptd_hash_request_ctx *rctx = ahash_request_ctx(req);
        struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
-       struct cryptd_state *state =
-               cryptd_get_state(crypto_ahash_tfm(tfm));
-       int err;
+       struct cryptd_queue *queue =
+               cryptd_get_queue(crypto_ahash_tfm(tfm));
 
        rctx->complete = req->base.complete;
        req->base.complete = complete;
 
-       spin_lock_bh(&state->lock);
-       err = ahash_enqueue_request(&state->queue, req);
-       spin_unlock_bh(&state->lock);
-
-       wake_up_process(state->task);
-       return err;
+       return cryptd_enqueue_request(queue, &req->base);
 }
 
 static void cryptd_hash_init(struct crypto_async_request *req_async, int err)
@@ -468,7 +513,7 @@ static int cryptd_hash_digest_enqueue(struct ahash_request *req)
 }
 
 static struct crypto_instance *cryptd_alloc_hash(
-       struct rtattr **tb, struct cryptd_state *state)
+       struct rtattr **tb, struct cryptd_queue *queue)
 {
        struct crypto_instance *inst;
        struct crypto_alg *alg;
@@ -478,7 +523,7 @@ static struct crypto_instance *cryptd_alloc_hash(
        if (IS_ERR(alg))
                return ERR_PTR(PTR_ERR(alg));
 
-       inst = cryptd_alloc_instance(alg, state);
+       inst = cryptd_alloc_instance(alg, queue);
        if (IS_ERR(inst))
                goto out_put_alg;
 
@@ -502,7 +547,7 @@ out_put_alg:
        return inst;
 }
 
-static struct cryptd_state state;
+static struct cryptd_queue queue;
 
 static struct crypto_instance *cryptd_alloc(struct rtattr **tb)
 {
@@ -514,9 +559,9 @@ static struct crypto_instance *cryptd_alloc(struct rtattr **tb)
 
        switch (algt->type & algt->mask & CRYPTO_ALG_TYPE_MASK) {
        case CRYPTO_ALG_TYPE_BLKCIPHER:
-               return cryptd_alloc_blkcipher(tb, &state);
+               return cryptd_alloc_blkcipher(tb, &queue);
        case CRYPTO_ALG_TYPE_DIGEST:
-               return cryptd_alloc_hash(tb, &state);
+               return cryptd_alloc_hash(tb, &queue);
        }
 
        return ERR_PTR(-EINVAL);
@@ -537,82 +582,58 @@ static struct crypto_template cryptd_tmpl = {
        .module = THIS_MODULE,
 };
 
-static inline int cryptd_create_thread(struct cryptd_state *state,
-                                      int (*fn)(void *data), const char *name)
-{
-       spin_lock_init(&state->lock);
-       mutex_init(&state->mutex);
-       crypto_init_queue(&state->queue, CRYPTD_MAX_QLEN);
-
-       state->task = kthread_run(fn, state, name);
-       if (IS_ERR(state->task))
-               return PTR_ERR(state->task);
+struct cryptd_ablkcipher *cryptd_alloc_ablkcipher(const char *alg_name,
+                                                 u32 type, u32 mask)
+{
+       char cryptd_alg_name[CRYPTO_MAX_ALG_NAME];
+       struct crypto_ablkcipher *tfm;
+
+       if (snprintf(cryptd_alg_name, CRYPTO_MAX_ALG_NAME,
+                    "cryptd(%s)", alg_name) >= CRYPTO_MAX_ALG_NAME)
+               return ERR_PTR(-EINVAL);
+       tfm = crypto_alloc_ablkcipher(cryptd_alg_name, type, mask);
+       if (IS_ERR(tfm))
+               return ERR_CAST(tfm);
+       if (crypto_ablkcipher_tfm(tfm)->__crt_alg->cra_module != THIS_MODULE) {
+               crypto_free_ablkcipher(tfm);
+               return ERR_PTR(-EINVAL);
+       }
 
-       return 0;
+       return __cryptd_ablkcipher_cast(tfm);
 }
+EXPORT_SYMBOL_GPL(cryptd_alloc_ablkcipher);
 
-static inline void cryptd_stop_thread(struct cryptd_state *state)
+struct crypto_blkcipher *cryptd_ablkcipher_child(struct cryptd_ablkcipher *tfm)
 {
-       BUG_ON(state->queue.qlen);
-       kthread_stop(state->task);
+       struct cryptd_blkcipher_ctx *ctx = crypto_ablkcipher_ctx(&tfm->base);
+       return ctx->child;
 }
+EXPORT_SYMBOL_GPL(cryptd_ablkcipher_child);
 
-static int cryptd_thread(void *data)
+void cryptd_free_ablkcipher(struct cryptd_ablkcipher *tfm)
 {
-       struct cryptd_state *state = data;
-       int stop;
-
-       current->flags |= PF_NOFREEZE;
-
-       do {
-               struct crypto_async_request *req, *backlog;
-
-               mutex_lock(&state->mutex);
-               __set_current_state(TASK_INTERRUPTIBLE);
-
-               spin_lock_bh(&state->lock);
-               backlog = crypto_get_backlog(&state->queue);
-               req = crypto_dequeue_request(&state->queue);
-               spin_unlock_bh(&state->lock);
-
-               stop = kthread_should_stop();
-
-               if (stop || req) {
-                       __set_current_state(TASK_RUNNING);
-                       if (req) {
-                               if (backlog)
-                                       backlog->complete(backlog,
-                                                         -EINPROGRESS);
-                               req->complete(req, 0);
-                       }
-               }
-
-               mutex_unlock(&state->mutex);
-
-               schedule();
-       } while (!stop);
-
-       return 0;
+       crypto_free_ablkcipher(&tfm->base);
 }
+EXPORT_SYMBOL_GPL(cryptd_free_ablkcipher);
 
 static int __init cryptd_init(void)
 {
        int err;
 
-       err = cryptd_create_thread(&state, cryptd_thread, "cryptd");
+       err = cryptd_init_queue(&queue, CRYPTD_MAX_CPU_QLEN);
        if (err)
                return err;
 
        err = crypto_register_template(&cryptd_tmpl);
        if (err)
-               kthread_stop(state.task);
+               cryptd_fini_queue(&queue);
 
        return err;
 }
 
 static void __exit cryptd_exit(void)
 {
-       cryptd_stop_thread(&state);
+       cryptd_fini_queue(&queue);
        crypto_unregister_template(&cryptd_tmpl);
 }
 
diff --git a/crypto/crypto_wq.c b/crypto/crypto_wq.c
new file mode 100644 (file)
index 0000000..fdcf624
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Workqueue for crypto subsystem
+ *
+ * Copyright (c) 2009 Intel Corp.
+ *   Author: Huang Ying <ying.huang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+
+#include <linux/workqueue.h>
+#include <crypto/algapi.h>
+#include <crypto/crypto_wq.h>
+
+struct workqueue_struct *kcrypto_wq;
+EXPORT_SYMBOL_GPL(kcrypto_wq);
+
+static int __init crypto_wq_init(void)
+{
+       kcrypto_wq = create_workqueue("crypto");
+       if (unlikely(!kcrypto_wq))
+               return -ENOMEM;
+       return 0;
+}
+
+static void __exit crypto_wq_exit(void)
+{
+       destroy_workqueue(kcrypto_wq);
+}
+
+module_init(crypto_wq_init);
+module_exit(crypto_wq_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Workqueue for crypto subsystem");
index ecbeaa1f17e1eae7f17b04c05e922283b0472dd1..a90d260528d4fbe09e3df8832736dc627f25de21 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (c) 2006, Rik Snel <rsnel@cube.dyndns.org>
  *
  * Based on Dr Brian Gladman's (GPL'd) work published at
- * http://fp.gladman.plus.com/cryptography_technology/index.htm
+ * http://gladman.plushost.co.uk/oldsite/cryptography_technology/index.php
  * See the original copyright notice below.
  *
  * This program is free software; you can redistribute it and/or modify it
index 3c19a27a7563cd628513a777a59121a24caadf85..fc76e1f37fc35d0fd4928ec9326b330c22937d53 100644 (file)
@@ -109,8 +109,10 @@ void crypto_alg_tested(const char *name, int err);
 void crypto_shoot_alg(struct crypto_alg *alg);
 struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
                                      u32 mask);
-struct crypto_tfm *crypto_create_tfm(struct crypto_alg *alg,
-                                    const struct crypto_type *frontend);
+void *crypto_create_tfm(struct crypto_alg *alg,
+                       const struct crypto_type *frontend);
+void *crypto_alloc_tfm(const char *alg_name,
+                      const struct crypto_type *frontend, u32 type, u32 mask);
 
 int crypto_register_instance(struct crypto_template *tmpl,
                             struct crypto_instance *inst);
diff --git a/crypto/pcompress.c b/crypto/pcompress.c
new file mode 100644 (file)
index 0000000..ca9a4af
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Cryptographic API.
+ *
+ * Partial (de)compression operations.
+ *
+ * Copyright 2008 Sony Corporation
+ *
+ * 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.
+ *
+ * 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/crypto.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/string.h>
+
+#include <crypto/compress.h>
+
+#include "internal.h"
+
+
+static int crypto_pcomp_init(struct crypto_tfm *tfm, u32 type, u32 mask)
+{
+       return 0;
+}
+
+static unsigned int crypto_pcomp_extsize(struct crypto_alg *alg,
+                                        const struct crypto_type *frontend)
+{
+       return alg->cra_ctxsize;
+}
+
+static int crypto_pcomp_init_tfm(struct crypto_tfm *tfm,
+                                const struct crypto_type *frontend)
+{
+       return 0;
+}
+
+static void crypto_pcomp_show(struct seq_file *m, struct crypto_alg *alg)
+       __attribute__ ((unused));
+static void crypto_pcomp_show(struct seq_file *m, struct crypto_alg *alg)
+{
+       seq_printf(m, "type         : pcomp\n");
+}
+
+static const struct crypto_type crypto_pcomp_type = {
+       .extsize        = crypto_pcomp_extsize,
+       .init           = crypto_pcomp_init,
+       .init_tfm       = crypto_pcomp_init_tfm,
+#ifdef CONFIG_PROC_FS
+       .show           = crypto_pcomp_show,
+#endif
+       .maskclear      = ~CRYPTO_ALG_TYPE_MASK,
+       .maskset        = CRYPTO_ALG_TYPE_MASK,
+       .type           = CRYPTO_ALG_TYPE_PCOMPRESS,
+       .tfmsize        = offsetof(struct crypto_pcomp, base),
+};
+
+struct crypto_pcomp *crypto_alloc_pcomp(const char *alg_name, u32 type,
+                                       u32 mask)
+{
+       return crypto_alloc_tfm(alg_name, &crypto_pcomp_type, type, mask);
+}
+EXPORT_SYMBOL_GPL(crypto_alloc_pcomp);
+
+int crypto_register_pcomp(struct pcomp_alg *alg)
+{
+       struct crypto_alg *base = &alg->base;
+
+       base->cra_type = &crypto_pcomp_type;
+       base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
+       base->cra_flags |= CRYPTO_ALG_TYPE_PCOMPRESS;
+
+       return crypto_register_alg(base);
+}
+EXPORT_SYMBOL_GPL(crypto_register_pcomp);
+
+int crypto_unregister_pcomp(struct pcomp_alg *alg)
+{
+       return crypto_unregister_alg(&alg->base);
+}
+EXPORT_SYMBOL_GPL(crypto_unregister_pcomp);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Partial (de)compression type");
+MODULE_AUTHOR("Sony Corporation");
index caa3542e6ce8943a2b5ed5f9a0f0aaded9a486cc..6349d8339d379cc6862dba1fadf54bc2cdfe3eb3 100644 (file)
@@ -2,7 +2,7 @@
  * Cryptographic API.
  *
  * SHA-256, as specified in
- * http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf
+ * http://csrc.nist.gov/groups/STM/cavp/documents/shs/sha256-384-512.pdf
  *
  * SHA-256 code by Jean-Luc Cooke <jlcooke@certainkey.com>.
  *
index d5a2b619c55f79e3b7cbff1f1d06d3048364b49c..7a659733f94a4e1087fabcfc8d5e48fd6c5c4521 100644 (file)
 #include <linux/slab.h>
 #include <linux/seq_file.h>
 
-static const struct crypto_type crypto_shash_type;
-
-static inline struct crypto_shash *__crypto_shash_cast(struct crypto_tfm *tfm)
-{
-       return container_of(tfm, struct crypto_shash, base);
-}
-
 #include "internal.h"
 
+static const struct crypto_type crypto_shash_type;
+
 static int shash_setkey_unaligned(struct crypto_shash *tfm, const u8 *key,
                                  unsigned int keylen)
 {
@@ -282,8 +277,7 @@ static int crypto_init_shash_ops_async(struct crypto_tfm *tfm)
        if (!crypto_mod_get(calg))
                return -EAGAIN;
 
-       shash = __crypto_shash_cast(crypto_create_tfm(
-               calg, &crypto_shash_type));
+       shash = crypto_create_tfm(calg, &crypto_shash_type);
        if (IS_ERR(shash)) {
                crypto_mod_put(calg);
                return PTR_ERR(shash);
@@ -391,8 +385,7 @@ static int crypto_init_shash_ops_compat(struct crypto_tfm *tfm)
        if (!crypto_mod_get(calg))
                return -EAGAIN;
 
-       shash = __crypto_shash_cast(crypto_create_tfm(
-               calg, &crypto_shash_type));
+       shash = crypto_create_tfm(calg, &crypto_shash_type);
        if (IS_ERR(shash)) {
                crypto_mod_put(calg);
                return PTR_ERR(shash);
@@ -442,8 +435,6 @@ static unsigned int crypto_shash_ctxsize(struct crypto_alg *alg, u32 type,
 static int crypto_shash_init_tfm(struct crypto_tfm *tfm,
                                 const struct crypto_type *frontend)
 {
-       if (frontend->type != CRYPTO_ALG_TYPE_SHASH)
-               return -EINVAL;
        return 0;
 }
 
@@ -482,8 +473,7 @@ static const struct crypto_type crypto_shash_type = {
 struct crypto_shash *crypto_alloc_shash(const char *alg_name, u32 type,
                                        u32 mask)
 {
-       return __crypto_shash_cast(
-               crypto_alloc_tfm(alg_name, &crypto_shash_type, type, mask));
+       return crypto_alloc_tfm(alg_name, &crypto_shash_type, type, mask);
 }
 EXPORT_SYMBOL_GPL(crypto_alloc_shash);
 
index 28a45a1e6f423603a563ef40c821736a88ef266b..c3c9124209a1901baae3f8e6d9b1a6e437e97901 100644 (file)
@@ -53,7 +53,7 @@ static char *check[] = {
        "cast6", "arc4", "michael_mic", "deflate", "crc32c", "tea", "xtea",
        "khazad", "wp512", "wp384", "wp256", "tnepres", "xeta",  "fcrypt",
        "camellia", "seed", "salsa20", "rmd128", "rmd160", "rmd256", "rmd320",
-       "lzo", "cts", NULL
+       "lzo", "cts", "zlib", NULL
 };
 
 static int test_cipher_jiffies(struct blkcipher_desc *desc, int enc,
@@ -661,6 +661,10 @@ static void do_test(int m)
                tcrypt_test("ecb(seed)");
                break;
 
+       case 44:
+               tcrypt_test("zlib");
+               break;
+
        case 100:
                tcrypt_test("hmac(md5)");
                break;
index a75f11ffb957257d15d67b7eecf55c666466b0f5..b50c3c6b17a26301a8a799bf62a29fe5e0c89d46 100644 (file)
@@ -72,6 +72,13 @@ struct comp_test_suite {
        } comp, decomp;
 };
 
+struct pcomp_test_suite {
+       struct {
+               struct pcomp_testvec *vecs;
+               unsigned int count;
+       } comp, decomp;
+};
+
 struct hash_test_suite {
        struct hash_testvec *vecs;
        unsigned int count;
@@ -86,6 +93,7 @@ struct alg_test_desc {
                struct aead_test_suite aead;
                struct cipher_test_suite cipher;
                struct comp_test_suite comp;
+               struct pcomp_test_suite pcomp;
                struct hash_test_suite hash;
        } suite;
 };
@@ -898,6 +906,159 @@ out:
        return ret;
 }
 
+static int test_pcomp(struct crypto_pcomp *tfm,
+                     struct pcomp_testvec *ctemplate,
+                     struct pcomp_testvec *dtemplate, int ctcount,
+                     int dtcount)
+{
+       const char *algo = crypto_tfm_alg_driver_name(crypto_pcomp_tfm(tfm));
+       unsigned int i;
+       char result[COMP_BUF_SIZE];
+       int error;
+
+       for (i = 0; i < ctcount; i++) {
+               struct comp_request req;
+
+               error = crypto_compress_setup(tfm, ctemplate[i].params,
+                                             ctemplate[i].paramsize);
+               if (error) {
+                       pr_err("alg: pcomp: compression setup failed on test "
+                              "%d for %s: error=%d\n", i + 1, algo, error);
+                       return error;
+               }
+
+               error = crypto_compress_init(tfm);
+               if (error) {
+                       pr_err("alg: pcomp: compression init failed on test "
+                              "%d for %s: error=%d\n", i + 1, algo, error);
+                       return error;
+               }
+
+               memset(result, 0, sizeof(result));
+
+               req.next_in = ctemplate[i].input;
+               req.avail_in = ctemplate[i].inlen / 2;
+               req.next_out = result;
+               req.avail_out = ctemplate[i].outlen / 2;
+
+               error = crypto_compress_update(tfm, &req);
+               if (error && (error != -EAGAIN || req.avail_in)) {
+                       pr_err("alg: pcomp: compression update failed on test "
+                              "%d for %s: error=%d\n", i + 1, algo, error);
+                       return error;
+               }
+
+               /* Add remaining input data */
+               req.avail_in += (ctemplate[i].inlen + 1) / 2;
+
+               error = crypto_compress_update(tfm, &req);
+               if (error && (error != -EAGAIN || req.avail_in)) {
+                       pr_err("alg: pcomp: compression update failed on test "
+                              "%d for %s: error=%d\n", i + 1, algo, error);
+                       return error;
+               }
+
+               /* Provide remaining output space */
+               req.avail_out += COMP_BUF_SIZE - ctemplate[i].outlen / 2;
+
+               error = crypto_compress_final(tfm, &req);
+               if (error) {
+                       pr_err("alg: pcomp: compression final failed on test "
+                              "%d for %s: error=%d\n", i + 1, algo, error);
+                       return error;
+               }
+
+               if (COMP_BUF_SIZE - req.avail_out != ctemplate[i].outlen) {
+                       pr_err("alg: comp: Compression test %d failed for %s: "
+                              "output len = %d (expected %d)\n", i + 1, algo,
+                              COMP_BUF_SIZE - req.avail_out,
+                              ctemplate[i].outlen);
+                       return -EINVAL;
+               }
+
+               if (memcmp(result, ctemplate[i].output, ctemplate[i].outlen)) {
+                       pr_err("alg: pcomp: Compression test %d failed for "
+                              "%s\n", i + 1, algo);
+                       hexdump(result, ctemplate[i].outlen);
+                       return -EINVAL;
+               }
+       }
+
+       for (i = 0; i < dtcount; i++) {
+               struct comp_request req;
+
+               error = crypto_decompress_setup(tfm, dtemplate[i].params,
+                                               dtemplate[i].paramsize);
+               if (error) {
+                       pr_err("alg: pcomp: decompression setup failed on "
+                              "test %d for %s: error=%d\n", i + 1, algo,
+                              error);
+                       return error;
+               }
+
+               error = crypto_decompress_init(tfm);
+               if (error) {
+                       pr_err("alg: pcomp: decompression init failed on test "
+                              "%d for %s: error=%d\n", i + 1, algo, error);
+                       return error;
+               }
+
+               memset(result, 0, sizeof(result));
+
+               req.next_in = dtemplate[i].input;
+               req.avail_in = dtemplate[i].inlen / 2;
+               req.next_out = result;
+               req.avail_out = dtemplate[i].outlen / 2;
+
+               error = crypto_decompress_update(tfm, &req);
+               if (error  && (error != -EAGAIN || req.avail_in)) {
+                       pr_err("alg: pcomp: decompression update failed on "
+                              "test %d for %s: error=%d\n", i + 1, algo,
+                              error);
+                       return error;
+               }
+
+               /* Add remaining input data */
+               req.avail_in += (dtemplate[i].inlen + 1) / 2;
+
+               error = crypto_decompress_update(tfm, &req);
+               if (error  && (error != -EAGAIN || req.avail_in)) {
+                       pr_err("alg: pcomp: decompression update failed on "
+                              "test %d for %s: error=%d\n", i + 1, algo,
+                              error);
+                       return error;
+               }
+
+               /* Provide remaining output space */
+               req.avail_out += COMP_BUF_SIZE - dtemplate[i].outlen / 2;
+
+               error = crypto_decompress_final(tfm, &req);
+               if (error  && (error != -EAGAIN || req.avail_in)) {
+                       pr_err("alg: pcomp: decompression final failed on "
+                              "test %d for %s: error=%d\n", i + 1, algo,
+                              error);
+                       return error;
+               }
+
+               if (COMP_BUF_SIZE - req.avail_out != dtemplate[i].outlen) {
+                       pr_err("alg: comp: Decompression test %d failed for "
+                              "%s: output len = %d (expected %d)\n", i + 1,
+                              algo, COMP_BUF_SIZE - req.avail_out,
+                              dtemplate[i].outlen);
+                       return -EINVAL;
+               }
+
+               if (memcmp(result, dtemplate[i].output, dtemplate[i].outlen)) {
+                       pr_err("alg: pcomp: Decompression test %d failed for "
+                              "%s\n", i + 1, algo);
+                       hexdump(result, dtemplate[i].outlen);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 static int alg_test_aead(const struct alg_test_desc *desc, const char *driver,
                         u32 type, u32 mask)
 {
@@ -1007,6 +1168,28 @@ static int alg_test_comp(const struct alg_test_desc *desc, const char *driver,
        return err;
 }
 
+static int alg_test_pcomp(const struct alg_test_desc *desc, const char *driver,
+                         u32 type, u32 mask)
+{
+       struct crypto_pcomp *tfm;
+       int err;
+
+       tfm = crypto_alloc_pcomp(driver, type, mask);
+       if (IS_ERR(tfm)) {
+               pr_err("alg: pcomp: Failed to load transform for %s: %ld\n",
+                      driver, PTR_ERR(tfm));
+               return PTR_ERR(tfm);
+       }
+
+       err = test_pcomp(tfm, desc->suite.pcomp.comp.vecs,
+                        desc->suite.pcomp.decomp.vecs,
+                        desc->suite.pcomp.comp.count,
+                        desc->suite.pcomp.decomp.count);
+
+       crypto_free_pcomp(tfm);
+       return err;
+}
+
 static int alg_test_hash(const struct alg_test_desc *desc, const char *driver,
                         u32 type, u32 mask)
 {
@@ -1835,6 +2018,21 @@ static const struct alg_test_desc alg_test_descs[] = {
                                }
                        }
                }
+       }, {
+               .alg = "zlib",
+               .test = alg_test_pcomp,
+               .suite = {
+                       .pcomp = {
+                               .comp = {
+                                       .vecs = zlib_comp_tv_template,
+                                       .count = ZLIB_COMP_TEST_VECTORS
+                               },
+                               .decomp = {
+                                       .vecs = zlib_decomp_tv_template,
+                                       .count = ZLIB_DECOMP_TEST_VECTORS
+                               }
+                       }
+               }
        }
 };
 
index 132953e144d34bcb0766f4eebfce32107c7a489b..526f00a9c72feba6acd49b42f0281a79944657e5 100644 (file)
 #ifndef _CRYPTO_TESTMGR_H
 #define _CRYPTO_TESTMGR_H
 
+#include <linux/netlink.h>
+#include <linux/zlib.h>
+
+#include <crypto/compress.h>
+
 #define MAX_DIGEST_SIZE                64
 #define MAX_TAP                        8
 
@@ -8347,10 +8352,19 @@ struct comp_testvec {
        char output[COMP_BUF_SIZE];
 };
 
+struct pcomp_testvec {
+       void *params;
+       unsigned int paramsize;
+       int inlen, outlen;
+       char input[COMP_BUF_SIZE];
+       char output[COMP_BUF_SIZE];
+};
+
 /*
  * Deflate test vectors (null-terminated strings).
  * Params: winbits=-11, Z_DEFAULT_COMPRESSION, MAX_MEM_LEVEL.
  */
+
 #define DEFLATE_COMP_TEST_VECTORS 2
 #define DEFLATE_DECOMP_TEST_VECTORS 2
 
@@ -8426,6 +8440,139 @@ static struct comp_testvec deflate_decomp_tv_template[] = {
        },
 };
 
+#define ZLIB_COMP_TEST_VECTORS 2
+#define ZLIB_DECOMP_TEST_VECTORS 2
+
+static const struct {
+       struct nlattr nla;
+       int val;
+} deflate_comp_params[] = {
+       {
+               .nla = {
+                       .nla_len        = NLA_HDRLEN + sizeof(int),
+                       .nla_type       = ZLIB_COMP_LEVEL,
+               },
+               .val                    = Z_DEFAULT_COMPRESSION,
+       }, {
+               .nla = {
+                       .nla_len        = NLA_HDRLEN + sizeof(int),
+                       .nla_type       = ZLIB_COMP_METHOD,
+               },
+               .val                    = Z_DEFLATED,
+       }, {
+               .nla = {
+                       .nla_len        = NLA_HDRLEN + sizeof(int),
+                       .nla_type       = ZLIB_COMP_WINDOWBITS,
+               },
+               .val                    = -11,
+       }, {
+               .nla = {
+                       .nla_len        = NLA_HDRLEN + sizeof(int),
+                       .nla_type       = ZLIB_COMP_MEMLEVEL,
+               },
+               .val                    = MAX_MEM_LEVEL,
+       }, {
+               .nla = {
+                       .nla_len        = NLA_HDRLEN + sizeof(int),
+                       .nla_type       = ZLIB_COMP_STRATEGY,
+               },
+               .val                    = Z_DEFAULT_STRATEGY,
+       }
+};
+
+static const struct {
+       struct nlattr nla;
+       int val;
+} deflate_decomp_params[] = {
+       {
+               .nla = {
+                       .nla_len        = NLA_HDRLEN + sizeof(int),
+                       .nla_type       = ZLIB_DECOMP_WINDOWBITS,
+               },
+               .val                    = -11,
+       }
+};
+
+static struct pcomp_testvec zlib_comp_tv_template[] = {
+       {
+               .params = &deflate_comp_params,
+               .paramsize = sizeof(deflate_comp_params),
+               .inlen  = 70,
+               .outlen = 38,
+               .input  = "Join us now and share the software "
+                       "Join us now and share the software ",
+               .output = "\xf3\xca\xcf\xcc\x53\x28\x2d\x56"
+                         "\xc8\xcb\x2f\x57\x48\xcc\x4b\x51"
+                         "\x28\xce\x48\x2c\x4a\x55\x28\xc9"
+                         "\x48\x55\x28\xce\x4f\x2b\x29\x07"
+                         "\x71\xbc\x08\x2b\x01\x00",
+       }, {
+               .params = &deflate_comp_params,
+               .paramsize = sizeof(deflate_comp_params),
+               .inlen  = 191,
+               .outlen = 122,
+               .input  = "This document describes a compression method based on the DEFLATE"
+                       "compression algorithm.  This document defines the application of "
+                       "the DEFLATE algorithm to the IP Payload Compression Protocol.",
+               .output = "\x5d\x8d\x31\x0e\xc2\x30\x10\x04"
+                         "\xbf\xb2\x2f\xc8\x1f\x10\x04\x09"
+                         "\x89\xc2\x85\x3f\x70\xb1\x2f\xf8"
+                         "\x24\xdb\x67\xd9\x47\xc1\xef\x49"
+                         "\x68\x12\x51\xae\x76\x67\xd6\x27"
+                         "\x19\x88\x1a\xde\x85\xab\x21\xf2"
+                         "\x08\x5d\x16\x1e\x20\x04\x2d\xad"
+                         "\xf3\x18\xa2\x15\x85\x2d\x69\xc4"
+                         "\x42\x83\x23\xb6\x6c\x89\x71\x9b"
+                         "\xef\xcf\x8b\x9f\xcf\x33\xca\x2f"
+                         "\xed\x62\xa9\x4c\x80\xff\x13\xaf"
+                         "\x52\x37\xed\x0e\x52\x6b\x59\x02"
+                         "\xd9\x4e\xe8\x7a\x76\x1d\x02\x98"
+                         "\xfe\x8a\x87\x83\xa3\x4f\x56\x8a"
+                         "\xb8\x9e\x8e\x5c\x57\xd3\xa0\x79"
+                         "\xfa\x02",
+       },
+};
+
+static struct pcomp_testvec zlib_decomp_tv_template[] = {
+       {
+               .params = &deflate_decomp_params,
+               .paramsize = sizeof(deflate_decomp_params),
+               .inlen  = 122,
+               .outlen = 191,
+               .input  = "\x5d\x8d\x31\x0e\xc2\x30\x10\x04"
+                         "\xbf\xb2\x2f\xc8\x1f\x10\x04\x09"
+                         "\x89\xc2\x85\x3f\x70\xb1\x2f\xf8"
+                         "\x24\xdb\x67\xd9\x47\xc1\xef\x49"
+                         "\x68\x12\x51\xae\x76\x67\xd6\x27"
+                         "\x19\x88\x1a\xde\x85\xab\x21\xf2"
+                         "\x08\x5d\x16\x1e\x20\x04\x2d\xad"
+                         "\xf3\x18\xa2\x15\x85\x2d\x69\xc4"
+                         "\x42\x83\x23\xb6\x6c\x89\x71\x9b"
+                         "\xef\xcf\x8b\x9f\xcf\x33\xca\x2f"
+                         "\xed\x62\xa9\x4c\x80\xff\x13\xaf"
+                         "\x52\x37\xed\x0e\x52\x6b\x59\x02"
+                         "\xd9\x4e\xe8\x7a\x76\x1d\x02\x98"
+                         "\xfe\x8a\x87\x83\xa3\x4f\x56\x8a"
+                         "\xb8\x9e\x8e\x5c\x57\xd3\xa0\x79"
+                         "\xfa\x02",
+               .output = "This document describes a compression method based on the DEFLATE"
+                       "compression algorithm.  This document defines the application of "
+                       "the DEFLATE algorithm to the IP Payload Compression Protocol.",
+       }, {
+               .params = &deflate_decomp_params,
+               .paramsize = sizeof(deflate_decomp_params),
+               .inlen  = 38,
+               .outlen = 70,
+               .input  = "\xf3\xca\xcf\xcc\x53\x28\x2d\x56"
+                         "\xc8\xcb\x2f\x57\x48\xcc\x4b\x51"
+                         "\x28\xce\x48\x2c\x4a\x55\x28\xc9"
+                         "\x48\x55\x28\xce\x4f\x2b\x29\x07"
+                         "\x71\xbc\x08\x2b\x01\x00",
+               .output = "Join us now and share the software "
+                       "Join us now and share the software ",
+       },
+};
+
 /*
  * LZO test vectors (null-terminated strings).
  */
diff --git a/crypto/zlib.c b/crypto/zlib.c
new file mode 100644 (file)
index 0000000..33609ba
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * Cryptographic API.
+ *
+ * Zlib algorithm
+ *
+ * Copyright 2008 Sony Corporation
+ *
+ * Based on deflate.c, which is
+ * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
+ *
+ * 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.
+ *
+ * FIXME: deflate transforms will require up to a total of about 436k of kernel
+ * memory on i386 (390k for compression, the rest for decompression), as the
+ * current zlib kernel code uses a worst case pre-allocation system by default.
+ * This needs to be fixed so that the amount of memory required is properly
+ * related to the winbits and memlevel parameters.
+ */
+
+#define pr_fmt(fmt)    "%s: " fmt, __func__
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/zlib.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/net.h>
+#include <linux/slab.h>
+
+#include <crypto/internal/compress.h>
+
+#include <net/netlink.h>
+
+
+struct zlib_ctx {
+       struct z_stream_s comp_stream;
+       struct z_stream_s decomp_stream;
+       int decomp_windowBits;
+};
+
+
+static void zlib_comp_exit(struct zlib_ctx *ctx)
+{
+       struct z_stream_s *stream = &ctx->comp_stream;
+
+       if (stream->workspace) {
+               zlib_deflateEnd(stream);
+               vfree(stream->workspace);
+               stream->workspace = NULL;
+       }
+}
+
+static void zlib_decomp_exit(struct zlib_ctx *ctx)
+{
+       struct z_stream_s *stream = &ctx->decomp_stream;
+
+       if (stream->workspace) {
+               zlib_inflateEnd(stream);
+               kfree(stream->workspace);
+               stream->workspace = NULL;
+       }
+}
+
+static int zlib_init(struct crypto_tfm *tfm)
+{
+       return 0;
+}
+
+static void zlib_exit(struct crypto_tfm *tfm)
+{
+       struct zlib_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       zlib_comp_exit(ctx);
+       zlib_decomp_exit(ctx);
+}
+
+
+static int zlib_compress_setup(struct crypto_pcomp *tfm, void *params,
+                              unsigned int len)
+{
+       struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+       struct z_stream_s *stream = &ctx->comp_stream;
+       struct nlattr *tb[ZLIB_COMP_MAX + 1];
+       size_t workspacesize;
+       int ret;
+
+       ret = nla_parse(tb, ZLIB_COMP_MAX, params, len, NULL);
+       if (ret)
+               return ret;
+
+       zlib_comp_exit(ctx);
+
+       workspacesize = zlib_deflate_workspacesize();
+       stream->workspace = vmalloc(workspacesize);
+       if (!stream->workspace)
+               return -ENOMEM;
+
+       memset(stream->workspace, 0, workspacesize);
+       ret = zlib_deflateInit2(stream,
+                               tb[ZLIB_COMP_LEVEL]
+                                       ? nla_get_u32(tb[ZLIB_COMP_LEVEL])
+                                       : Z_DEFAULT_COMPRESSION,
+                               tb[ZLIB_COMP_METHOD]
+                                       ? nla_get_u32(tb[ZLIB_COMP_METHOD])
+                                       : Z_DEFLATED,
+                               tb[ZLIB_COMP_WINDOWBITS]
+                                       ? nla_get_u32(tb[ZLIB_COMP_WINDOWBITS])
+                                       : MAX_WBITS,
+                               tb[ZLIB_COMP_MEMLEVEL]
+                                       ? nla_get_u32(tb[ZLIB_COMP_MEMLEVEL])
+                                       : DEF_MEM_LEVEL,
+                               tb[ZLIB_COMP_STRATEGY]
+                                       ? nla_get_u32(tb[ZLIB_COMP_STRATEGY])
+                                       : Z_DEFAULT_STRATEGY);
+       if (ret != Z_OK) {
+               vfree(stream->workspace);
+               stream->workspace = NULL;
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int zlib_compress_init(struct crypto_pcomp *tfm)
+{
+       int ret;
+       struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+       struct z_stream_s *stream = &dctx->comp_stream;
+
+       ret = zlib_deflateReset(stream);
+       if (ret != Z_OK)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int zlib_compress_update(struct crypto_pcomp *tfm,
+                               struct comp_request *req)
+{
+       int ret;
+       struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+       struct z_stream_s *stream = &dctx->comp_stream;
+
+       pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
+       stream->next_in = req->next_in;
+       stream->avail_in = req->avail_in;
+       stream->next_out = req->next_out;
+       stream->avail_out = req->avail_out;
+
+       ret = zlib_deflate(stream, Z_NO_FLUSH);
+       switch (ret) {
+       case Z_OK:
+               break;
+
+       case Z_BUF_ERROR:
+               pr_debug("zlib_deflate could not make progress\n");
+               return -EAGAIN;
+
+       default:
+               pr_debug("zlib_deflate failed %d\n", ret);
+               return -EINVAL;
+       }
+
+       pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
+                stream->avail_in, stream->avail_out,
+                req->avail_in - stream->avail_in,
+                req->avail_out - stream->avail_out);
+       req->next_in = stream->next_in;
+       req->avail_in = stream->avail_in;
+       req->next_out = stream->next_out;
+       req->avail_out = stream->avail_out;
+       return 0;
+}
+
+static int zlib_compress_final(struct crypto_pcomp *tfm,
+                              struct comp_request *req)
+{
+       int ret;
+       struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+       struct z_stream_s *stream = &dctx->comp_stream;
+
+       pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
+       stream->next_in = req->next_in;
+       stream->avail_in = req->avail_in;
+       stream->next_out = req->next_out;
+       stream->avail_out = req->avail_out;
+
+       ret = zlib_deflate(stream, Z_FINISH);
+       if (ret != Z_STREAM_END) {
+               pr_debug("zlib_deflate failed %d\n", ret);
+               return -EINVAL;
+       }
+
+       pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
+                stream->avail_in, stream->avail_out,
+                req->avail_in - stream->avail_in,
+                req->avail_out - stream->avail_out);
+       req->next_in = stream->next_in;
+       req->avail_in = stream->avail_in;
+       req->next_out = stream->next_out;
+       req->avail_out = stream->avail_out;
+       return 0;
+}
+
+
+static int zlib_decompress_setup(struct crypto_pcomp *tfm, void *params,
+                                unsigned int len)
+{
+       struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+       struct z_stream_s *stream = &ctx->decomp_stream;
+       struct nlattr *tb[ZLIB_DECOMP_MAX + 1];
+       int ret = 0;
+
+       ret = nla_parse(tb, ZLIB_DECOMP_MAX, params, len, NULL);
+       if (ret)
+               return ret;
+
+       zlib_decomp_exit(ctx);
+
+       ctx->decomp_windowBits = tb[ZLIB_DECOMP_WINDOWBITS]
+                                ? nla_get_u32(tb[ZLIB_DECOMP_WINDOWBITS])
+                                : DEF_WBITS;
+
+       stream->workspace = kzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
+       if (!stream->workspace)
+               return -ENOMEM;
+
+       ret = zlib_inflateInit2(stream, ctx->decomp_windowBits);
+       if (ret != Z_OK) {
+               kfree(stream->workspace);
+               stream->workspace = NULL;
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int zlib_decompress_init(struct crypto_pcomp *tfm)
+{
+       int ret;
+       struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+       struct z_stream_s *stream = &dctx->decomp_stream;
+
+       ret = zlib_inflateReset(stream);
+       if (ret != Z_OK)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int zlib_decompress_update(struct crypto_pcomp *tfm,
+                                 struct comp_request *req)
+{
+       int ret;
+       struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+       struct z_stream_s *stream = &dctx->decomp_stream;
+
+       pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
+       stream->next_in = req->next_in;
+       stream->avail_in = req->avail_in;
+       stream->next_out = req->next_out;
+       stream->avail_out = req->avail_out;
+
+       ret = zlib_inflate(stream, Z_SYNC_FLUSH);
+       switch (ret) {
+       case Z_OK:
+       case Z_STREAM_END:
+               break;
+
+       case Z_BUF_ERROR:
+               pr_debug("zlib_inflate could not make progress\n");
+               return -EAGAIN;
+
+       default:
+               pr_debug("zlib_inflate failed %d\n", ret);
+               return -EINVAL;
+       }
+
+       pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
+                stream->avail_in, stream->avail_out,
+                req->avail_in - stream->avail_in,
+                req->avail_out - stream->avail_out);
+       req->next_in = stream->next_in;
+       req->avail_in = stream->avail_in;
+       req->next_out = stream->next_out;
+       req->avail_out = stream->avail_out;
+       return 0;
+}
+
+static int zlib_decompress_final(struct crypto_pcomp *tfm,
+                                struct comp_request *req)
+{
+       int ret;
+       struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+       struct z_stream_s *stream = &dctx->decomp_stream;
+
+       pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
+       stream->next_in = req->next_in;
+       stream->avail_in = req->avail_in;
+       stream->next_out = req->next_out;
+       stream->avail_out = req->avail_out;
+
+       if (dctx->decomp_windowBits < 0) {
+               ret = zlib_inflate(stream, Z_SYNC_FLUSH);
+               /*
+                * Work around a bug in zlib, which sometimes wants to taste an
+                * extra byte when being used in the (undocumented) raw deflate
+                * mode. (From USAGI).
+                */
+               if (ret == Z_OK && !stream->avail_in && stream->avail_out) {
+                       const void *saved_next_in = stream->next_in;
+                       u8 zerostuff = 0;
+
+                       stream->next_in = &zerostuff;
+                       stream->avail_in = 1;
+                       ret = zlib_inflate(stream, Z_FINISH);
+                       stream->next_in = saved_next_in;
+                       stream->avail_in = 0;
+               }
+       } else
+               ret = zlib_inflate(stream, Z_FINISH);
+       if (ret != Z_STREAM_END) {
+               pr_debug("zlib_inflate failed %d\n", ret);
+               return -EINVAL;
+       }
+
+       pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
+                stream->avail_in, stream->avail_out,
+                req->avail_in - stream->avail_in,
+                req->avail_out - stream->avail_out);
+       req->next_in = stream->next_in;
+       req->avail_in = stream->avail_in;
+       req->next_out = stream->next_out;
+       req->avail_out = stream->avail_out;
+       return 0;
+}
+
+
+static struct pcomp_alg zlib_alg = {
+       .compress_setup         = zlib_compress_setup,
+       .compress_init          = zlib_compress_init,
+       .compress_update        = zlib_compress_update,
+       .compress_final         = zlib_compress_final,
+       .decompress_setup       = zlib_decompress_setup,
+       .decompress_init        = zlib_decompress_init,
+       .decompress_update      = zlib_decompress_update,
+       .decompress_final       = zlib_decompress_final,
+
+       .base                   = {
+               .cra_name       = "zlib",
+               .cra_flags      = CRYPTO_ALG_TYPE_PCOMPRESS,
+               .cra_ctxsize    = sizeof(struct zlib_ctx),
+               .cra_module     = THIS_MODULE,
+               .cra_init       = zlib_init,
+               .cra_exit       = zlib_exit,
+       }
+};
+
+static int __init zlib_mod_init(void)
+{
+       return crypto_register_pcomp(&zlib_alg);
+}
+
+static void __exit zlib_mod_fini(void)
+{
+       crypto_unregister_pcomp(&zlib_alg);
+}
+
+module_init(zlib_mod_init);
+module_exit(zlib_mod_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Zlib Compression Algorithm");
+MODULE_AUTHOR("Sony Corporation");
index 8822eca58ffaca1b573f96a11c2f3949b61b93e1..5fab6470f4b2beabbdd353063946bd513ef07554 100644 (file)
@@ -20,6 +20,20 @@ config HW_RANDOM
 
          If unsure, say Y.
 
+config HW_RANDOM_TIMERIOMEM
+       tristate "Timer IOMEM HW Random Number Generator support"
+       depends on HW_RANDOM && HAS_IOMEM
+       ---help---
+         This driver provides kernel-side support for a generic Random
+         Number Generator used by reading a 'dumb' iomem address that
+         is to be read no faster than, for example, once a second;
+         the default FPGA bitstream on the TS-7800 has such functionality.
+
+         To compile this driver as a module, choose M here: the
+         module will be called timeriomem-rng.
+
+         If unsure, say Y.
+
 config HW_RANDOM_INTEL
        tristate "Intel HW Random Number Generator support"
        depends on HW_RANDOM && (X86 || IA64) && PCI
index b6effb7522c2997570d811414cc121b53779c0a9..e81d21a5f28fb443c370fe5b00bc1eee3be85abb 100644 (file)
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_HW_RANDOM) += rng-core.o
 rng-core-y := core.o
+obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o
 obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o
 obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o
 obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o
diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c
new file mode 100644 (file)
index 0000000..10ad41b
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * drivers/char/hw_random/timeriomem-rng.c
+ *
+ * Copyright (C) 2009 Alexander Clouter <alex@digriz.org.uk>
+ *
+ * Derived from drivers/char/hw_random/omap-rng.c
+ *   Copyright 2005 (c) MontaVista Software, Inc.
+ *   Author: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Overview:
+ *   This driver is useful for platforms that have an IO range that provides
+ *   periodic random data from a single IO memory address.  All the platform
+ *   has to do is provide the address and 'wait time' that new data becomes
+ *   available.
+ *
+ * TODO: add support for reading sizes other than 32bits and masking
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/timeriomem-rng.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/completion.h>
+
+static struct timeriomem_rng_data *timeriomem_rng_data;
+
+static void timeriomem_rng_trigger(unsigned long);
+static DEFINE_TIMER(timeriomem_rng_timer, timeriomem_rng_trigger, 0, 0);
+
+/*
+ * have data return 1, however return 0 if we have nothing
+ */
+static int timeriomem_rng_data_present(struct hwrng *rng, int wait)
+{
+       if (rng->priv == 0)
+               return 1;
+
+       if (!wait || timeriomem_rng_data->present)
+               return timeriomem_rng_data->present;
+
+       wait_for_completion(&timeriomem_rng_data->completion);
+
+       return 1;
+}
+
+static int timeriomem_rng_data_read(struct hwrng *rng, u32 *data)
+{
+       unsigned long cur;
+       s32 delay;
+
+       *data = readl(timeriomem_rng_data->address);
+
+       if (rng->priv != 0) {
+               cur = jiffies;
+
+               delay = cur - timeriomem_rng_timer.expires;
+               delay = rng->priv - (delay % rng->priv);
+
+               timeriomem_rng_timer.expires = cur + delay;
+               timeriomem_rng_data->present = 0;
+
+               init_completion(&timeriomem_rng_data->completion);
+               add_timer(&timeriomem_rng_timer);
+       }
+
+       return 4;
+}
+
+static void timeriomem_rng_trigger(unsigned long dummy)
+{
+       timeriomem_rng_data->present = 1;
+       complete(&timeriomem_rng_data->completion);
+}
+
+static struct hwrng timeriomem_rng_ops = {
+       .name           = "timeriomem",
+       .data_present   = timeriomem_rng_data_present,
+       .data_read      = timeriomem_rng_data_read,
+       .priv           = 0,
+};
+
+static int __init timeriomem_rng_probe(struct platform_device *pdev)
+{
+       int ret;
+
+       timeriomem_rng_data = pdev->dev.platform_data;
+
+       if (timeriomem_rng_data->period != 0
+               && usecs_to_jiffies(timeriomem_rng_data->period) > 0) {
+               timeriomem_rng_timer.expires = jiffies;
+
+               timeriomem_rng_ops.priv = usecs_to_jiffies(
+                                               timeriomem_rng_data->period);
+       }
+       timeriomem_rng_data->present = 1;
+
+       ret = hwrng_register(&timeriomem_rng_ops);
+       if (ret) {
+               dev_err(&pdev->dev, "problem registering\n");
+               return ret;
+       }
+
+       dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n",
+                       timeriomem_rng_data->address,
+                       timeriomem_rng_data->period);
+
+       return 0;
+}
+
+static int __devexit timeriomem_rng_remove(struct platform_device *pdev)
+{
+       del_timer_sync(&timeriomem_rng_timer);
+       hwrng_unregister(&timeriomem_rng_ops);
+
+       return 0;
+}
+
+static struct platform_driver timeriomem_rng_driver = {
+       .driver = {
+               .name           = "timeriomem_rng",
+               .owner          = THIS_MODULE,
+       },
+       .probe          = timeriomem_rng_probe,
+       .remove         = __devexit_p(timeriomem_rng_remove),
+};
+
+static int __init timeriomem_rng_init(void)
+{
+       return platform_driver_register(&timeriomem_rng_driver);
+}
+
+static void __exit timeriomem_rng_exit(void)
+{
+       platform_driver_unregister(&timeriomem_rng_driver);
+}
+
+module_init(timeriomem_rng_init);
+module_exit(timeriomem_rng_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>");
+MODULE_DESCRIPTION("Timer IOMEM H/W RNG driver");
index 9c47dc48c9fd4526ac8e8972fb80c6b21ba2cae8..ccdd828adcef2711e2ed91a7c52f38b1a6dca844 100644 (file)
@@ -429,134 +429,148 @@ out:
 #define TPM_DIGEST_SIZE 20
 #define TPM_ERROR_SIZE 10
 #define TPM_RET_CODE_IDX 6
-#define TPM_GET_CAP_RET_SIZE_IDX 10
-#define TPM_GET_CAP_RET_UINT32_1_IDX 14
-#define TPM_GET_CAP_RET_UINT32_2_IDX 18
-#define TPM_GET_CAP_RET_UINT32_3_IDX 22
-#define TPM_GET_CAP_RET_UINT32_4_IDX 26
-#define TPM_GET_CAP_PERM_DISABLE_IDX 16
-#define TPM_GET_CAP_PERM_INACTIVE_IDX 18
-#define TPM_GET_CAP_RET_BOOL_1_IDX 14
-#define TPM_GET_CAP_TEMP_INACTIVE_IDX 16
-
-#define TPM_CAP_IDX 13
-#define TPM_CAP_SUBCAP_IDX 21
 
 enum tpm_capabilities {
-       TPM_CAP_FLAG = 4,
-       TPM_CAP_PROP = 5,
+       TPM_CAP_FLAG = cpu_to_be32(4),
+       TPM_CAP_PROP = cpu_to_be32(5),
+       CAP_VERSION_1_1 = cpu_to_be32(0x06),
+       CAP_VERSION_1_2 = cpu_to_be32(0x1A)
 };
 
 enum tpm_sub_capabilities {
-       TPM_CAP_PROP_PCR = 0x1,
-       TPM_CAP_PROP_MANUFACTURER = 0x3,
-       TPM_CAP_FLAG_PERM = 0x8,
-       TPM_CAP_FLAG_VOL = 0x9,
-       TPM_CAP_PROP_OWNER = 0x11,
-       TPM_CAP_PROP_TIS_TIMEOUT = 0x15,
-       TPM_CAP_PROP_TIS_DURATION = 0x20,
-};
+       TPM_CAP_PROP_PCR = cpu_to_be32(0x101),
+       TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103),
+       TPM_CAP_FLAG_PERM = cpu_to_be32(0x108),
+       TPM_CAP_FLAG_VOL = cpu_to_be32(0x109),
+       TPM_CAP_PROP_OWNER = cpu_to_be32(0x111),
+       TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115),
+       TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120),
 
-/*
- * This is a semi generic GetCapability command for use
- * with the capability type TPM_CAP_PROP or TPM_CAP_FLAG
- * and their associated sub_capabilities.
- */
-
-static const u8 tpm_cap[] = {
-       0, 193,                 /* TPM_TAG_RQU_COMMAND */
-       0, 0, 0, 22,            /* length */
-       0, 0, 0, 101,           /* TPM_ORD_GetCapability */
-       0, 0, 0, 0,             /* TPM_CAP_<TYPE> */
-       0, 0, 0, 4,             /* TPM_CAP_SUB_<TYPE> size */
-       0, 0, 1, 0              /* TPM_CAP_SUB_<TYPE> */
 };
 
-static ssize_t transmit_cmd(struct tpm_chip *chip, u8 *data, int len,
-                           char *desc)
+static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
+                           int len, const char *desc)
 {
        int err;
 
-       len = tpm_transmit(chip, data, len);
+       len = tpm_transmit(chip,(u8 *) cmd, len);
        if (len <  0)
                return len;
        if (len == TPM_ERROR_SIZE) {
-               err = be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX)));
+               err = be32_to_cpu(cmd->header.out.return_code);
                dev_dbg(chip->dev, "A TPM error (%d) occurred %s\n", err, desc);
                return err;
        }
        return 0;
 }
 
+#define TPM_INTERNAL_RESULT_SIZE 200
+#define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
+#define TPM_ORD_GET_CAP cpu_to_be32(101)
+
+static const struct tpm_input_header tpm_getcap_header = {
+       .tag = TPM_TAG_RQU_COMMAND,
+       .length = cpu_to_be32(22),
+       .ordinal = TPM_ORD_GET_CAP
+};
+
+ssize_t tpm_getcap(struct device *dev, __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) {
+               tpm_cmd.params.getcap_in.cap = subcap_id;
+               /*subcap field not necessary */
+               tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(0);
+               tpm_cmd.header.in.length -= cpu_to_be32(sizeof(__be32));
+       } else {
+               if (subcap_id == TPM_CAP_FLAG_PERM ||
+                   subcap_id == TPM_CAP_FLAG_VOL)
+                       tpm_cmd.params.getcap_in.cap = TPM_CAP_FLAG;
+               else
+                       tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+               tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+               tpm_cmd.params.getcap_in.subcap = subcap_id;
+       }
+       rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
+       if (!rc)
+               *cap = tpm_cmd.params.getcap_out.cap;
+       return rc;
+}
+
 void tpm_gen_interrupt(struct tpm_chip *chip)
 {
-       u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)];
+       struct  tpm_cmd_t tpm_cmd;
        ssize_t rc;
 
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_PROP;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT;
+       tpm_cmd.header.in = tpm_getcap_header;
+       tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+       tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+       tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
 
-       rc = transmit_cmd(chip, data, sizeof(data),
+       rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
                        "attempting to determine the timeouts");
 }
 EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
 
 void tpm_get_timeouts(struct tpm_chip *chip)
 {
-       u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)];
+       struct tpm_cmd_t tpm_cmd;
+       struct timeout_t *timeout_cap;
+       struct duration_t *duration_cap;
        ssize_t rc;
        u32 timeout;
 
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_PROP;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT;
+       tpm_cmd.header.in = tpm_getcap_header;
+       tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+       tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+       tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
 
-       rc = transmit_cmd(chip, data, sizeof(data),
+       rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
                        "attempting to determine the timeouts");
        if (rc)
                goto duration;
 
-       if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX)))
+       if (be32_to_cpu(tpm_cmd.header.out.length)
            != 4 * sizeof(u32))
                goto duration;
 
+       timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
        /* Don't overwrite default if value is 0 */
-       timeout =
-           be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX)));
+       timeout = be32_to_cpu(timeout_cap->a);
        if (timeout)
                chip->vendor.timeout_a = usecs_to_jiffies(timeout);
-       timeout =
-           be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX)));
+       timeout = be32_to_cpu(timeout_cap->b);
        if (timeout)
                chip->vendor.timeout_b = usecs_to_jiffies(timeout);
-       timeout =
-           be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX)));
+       timeout = be32_to_cpu(timeout_cap->c);
        if (timeout)
                chip->vendor.timeout_c = usecs_to_jiffies(timeout);
-       timeout =
-           be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_4_IDX)));
+       timeout = be32_to_cpu(timeout_cap->d);
        if (timeout)
                chip->vendor.timeout_d = usecs_to_jiffies(timeout);
 
 duration:
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_PROP;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_DURATION;
+       tpm_cmd.header.in = tpm_getcap_header;
+       tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+       tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+       tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
 
-       rc = transmit_cmd(chip, data, sizeof(data),
+       rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
                        "attempting to determine the durations");
        if (rc)
                return;
 
-       if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX)))
+       if (be32_to_cpu(tpm_cmd.header.out.return_code)
            != 3 * sizeof(u32))
                return;
-
+       duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
        chip->vendor.duration[TPM_SHORT] =
-           usecs_to_jiffies(be32_to_cpu
-                            (*((__be32 *) (data +
-                                           TPM_GET_CAP_RET_UINT32_1_IDX))));
+           usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
        /* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
         * value wrong and apparently reports msecs rather than usecs. So we
         * fix up the resulting too-small TPM_SHORT value to make things work.
@@ -565,13 +579,9 @@ duration:
                chip->vendor.duration[TPM_SHORT] = HZ;
 
        chip->vendor.duration[TPM_MEDIUM] =
-           usecs_to_jiffies(be32_to_cpu
-                            (*((__be32 *) (data +
-                                           TPM_GET_CAP_RET_UINT32_2_IDX))));
+           usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
        chip->vendor.duration[TPM_LONG] =
-           usecs_to_jiffies(be32_to_cpu
-                            (*((__be32 *) (data +
-                                           TPM_GET_CAP_RET_UINT32_3_IDX))));
+           usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
 }
 EXPORT_SYMBOL_GPL(tpm_get_timeouts);
 
@@ -587,36 +597,18 @@ void tpm_continue_selftest(struct tpm_chip *chip)
 }
 EXPORT_SYMBOL_GPL(tpm_continue_selftest);
 
-#define  TPM_INTERNAL_RESULT_SIZE 200
-
 ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
                        char *buf)
 {
-       u8 *data;
+       cap_t cap;
        ssize_t rc;
 
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
-
-       data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_FLAG;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM;
-
-       rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-                       "attemtping to determine the permanent enabled state");
-       if (rc) {
-               kfree(data);
+       rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
+                        "attempting to determine the permanent enabled state");
+       if (rc)
                return 0;
-       }
-
-       rc = sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_DISABLE_IDX]);
 
-       kfree(data);
+       rc = sprintf(buf, "%d\n", !cap.perm_flags.disable);
        return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_show_enabled);
@@ -624,31 +616,15 @@ EXPORT_SYMBOL_GPL(tpm_show_enabled);
 ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr,
                        char *buf)
 {
-       u8 *data;
+       cap_t cap;
        ssize_t rc;
 
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
-
-       data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_FLAG;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM;
-
-       rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-                       "attemtping to determine the permanent active state");
-       if (rc) {
-               kfree(data);
+       rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
+                        "attempting to determine the permanent active state");
+       if (rc)
                return 0;
-       }
 
-       rc = sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_INACTIVE_IDX]);
-
-       kfree(data);
+       rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated);
        return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_show_active);
@@ -656,31 +632,15 @@ EXPORT_SYMBOL_GPL(tpm_show_active);
 ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr,
                        char *buf)
 {
-       u8 *data;
+       cap_t cap;
        ssize_t rc;
 
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
-
-       data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_PROP;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_OWNER;
-
-       rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-                       "attempting to determine the owner state");
-       if (rc) {
-               kfree(data);
+       rc = tpm_getcap(dev, TPM_CAP_PROP_OWNER, &cap,
+                        "attempting to determine the owner state");
+       if (rc)
                return 0;
-       }
-
-       rc = sprintf(buf, "%d\n", data[TPM_GET_CAP_RET_BOOL_1_IDX]);
 
-       kfree(data);
+       rc = sprintf(buf, "%d\n", cap.owned);
        return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_show_owned);
@@ -688,116 +648,180 @@ EXPORT_SYMBOL_GPL(tpm_show_owned);
 ssize_t tpm_show_temp_deactivated(struct device * dev,
                                struct device_attribute * attr, char *buf)
 {
-       u8 *data;
+       cap_t cap;
        ssize_t rc;
 
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
+       rc = tpm_getcap(dev, TPM_CAP_FLAG_VOL, &cap,
+                        "attempting to determine the temporary state");
+       if (rc)
+               return 0;
 
-       data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
+       rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated);
 
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_FLAG;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_VOL;
+/*
+ * tpm_chip_find_get - return tpm_chip for given chip number
+ */
+static struct tpm_chip *tpm_chip_find_get(int chip_num)
+{
+       struct tpm_chip *pos, *chip = NULL;
 
-       rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-                       "attempting to determine the temporary state");
-       if (rc) {
-               kfree(data);
-               return 0;
+       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;
+
+               if (try_module_get(pos->dev->driver->owner)) {
+                       chip = pos;
+                       break;
+               }
        }
+       rcu_read_unlock();
+       return chip;
+}
 
-       rc = sprintf(buf, "%d\n", data[TPM_GET_CAP_TEMP_INACTIVE_IDX]);
+#define TPM_ORDINAL_PCRREAD cpu_to_be32(21)
+#define READ_PCR_RESULT_SIZE 30
+static struct tpm_input_header pcrread_header = {
+       .tag = TPM_TAG_RQU_COMMAND,
+       .length = cpu_to_be32(14),
+       .ordinal = TPM_ORDINAL_PCRREAD
+};
 
-       kfree(data);
+int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
+{
+       int rc;
+       struct tpm_cmd_t cmd;
+
+       cmd.header.in = pcrread_header;
+       cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx);
+       BUILD_BUG_ON(cmd.header.in.length > READ_PCR_RESULT_SIZE);
+       rc = transmit_cmd(chip, &cmd, cmd.header.in.length,
+                         "attempting to read a pcr value");
+
+       if (rc == 0)
+               memcpy(res_buf, cmd.params.pcrread_out.pcr_result,
+                      TPM_DIGEST_SIZE);
        return rc;
 }
-EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated);
 
-static const u8 pcrread[] = {
-       0, 193,                 /* TPM_TAG_RQU_COMMAND */
-       0, 0, 0, 14,            /* length */
-       0, 0, 0, 21,            /* TPM_ORD_PcrRead */
-       0, 0, 0, 0              /* PCR index */
+/**
+ * tpm_pcr_read - read a pcr value
+ * @chip_num:  tpm idx # or ANY
+ * @pcr_idx:   pcr idx to retrieve
+ * @res_buf:   TPM_PCR value
+ *             size of res_buf is 20 bytes (or NULL if you don't care)
+ *
+ * The TPM driver should be built-in, but for whatever reason it
+ * isn't, protect against the chip disappearing, by incrementing
+ * the module usage count.
+ */
+int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf)
+{
+       struct tpm_chip *chip;
+       int rc;
+
+       chip = tpm_chip_find_get(chip_num);
+       if (chip == NULL)
+               return -ENODEV;
+       rc = __tpm_pcr_read(chip, pcr_idx, res_buf);
+       module_put(chip->dev->driver->owner);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_pcr_read);
+
+/**
+ * tpm_pcr_extend - extend pcr value with hash
+ * @chip_num:  tpm idx # or AN&
+ * @pcr_idx:   pcr idx to extend
+ * @hash:      hash value used to extend pcr value
+ *
+ * The TPM driver should be built-in, but for whatever reason it
+ * isn't, protect against the chip disappearing, by incrementing
+ * the module usage count.
+ */
+#define TPM_ORD_PCR_EXTEND cpu_to_be32(20)
+#define EXTEND_PCR_SIZE 34
+static struct tpm_input_header pcrextend_header = {
+       .tag = TPM_TAG_RQU_COMMAND,
+       .length = cpu_to_be32(34),
+       .ordinal = TPM_ORD_PCR_EXTEND
 };
 
+int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
+{
+       struct tpm_cmd_t cmd;
+       int rc;
+       struct tpm_chip *chip;
+
+       chip = tpm_chip_find_get(chip_num);
+       if (chip == NULL)
+               return -ENODEV;
+
+       cmd.header.in = pcrextend_header;
+       BUILD_BUG_ON(be32_to_cpu(cmd.header.in.length) > EXTEND_PCR_SIZE);
+       cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx);
+       memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE);
+       rc = transmit_cmd(chip, &cmd, cmd.header.in.length,
+                         "attempting extend a PCR value");
+
+       module_put(chip->dev->driver->owner);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_pcr_extend);
+
 ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr,
                      char *buf)
 {
-       u8 *data;
+       cap_t cap;
+       u8 digest[TPM_DIGEST_SIZE];
        ssize_t rc;
        int i, j, num_pcrs;
-       __be32 index;
        char *str = buf;
-
        struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
 
-       data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_PROP;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_PCR;
-
-       rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
+       rc = tpm_getcap(dev, TPM_CAP_PROP_PCR, &cap,
                        "attempting to determine the number of PCRS");
-       if (rc) {
-               kfree(data);
+       if (rc)
                return 0;
-       }
 
-       num_pcrs = be32_to_cpu(*((__be32 *) (data + 14)));
+       num_pcrs = be32_to_cpu(cap.num_pcrs);
        for (i = 0; i < num_pcrs; i++) {
-               memcpy(data, pcrread, sizeof(pcrread));
-               index = cpu_to_be32(i);
-               memcpy(data + 10, &index, 4);
-               rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-                               "attempting to read a PCR");
+               rc = __tpm_pcr_read(chip, i, digest);
                if (rc)
-                       goto out;
+                       break;
                str += sprintf(str, "PCR-%02d: ", i);
                for (j = 0; j < TPM_DIGEST_SIZE; j++)
-                       str += sprintf(str, "%02X ", *(data + 10 + j));
+                       str += sprintf(str, "%02X ", digest[j]);
                str += sprintf(str, "\n");
        }
-out:
-       kfree(data);
        return str - buf;
 }
 EXPORT_SYMBOL_GPL(tpm_show_pcrs);
 
 #define  READ_PUBEK_RESULT_SIZE 314
-static const u8 readpubek[] = {
-       0, 193,                 /* TPM_TAG_RQU_COMMAND */
-       0, 0, 0, 30,            /* length */
-       0, 0, 0, 124,           /* TPM_ORD_ReadPubek */
+#define TPM_ORD_READPUBEK cpu_to_be32(124)
+struct tpm_input_header tpm_readpubek_header = {
+       .tag = TPM_TAG_RQU_COMMAND,
+       .length = cpu_to_be32(30),
+       .ordinal = TPM_ORD_READPUBEK
 };
 
 ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
                       char *buf)
 {
        u8 *data;
+       struct tpm_cmd_t tpm_cmd;
        ssize_t err;
        int i, rc;
        char *str = buf;
 
        struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
 
-       data = kzalloc(READ_PUBEK_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       memcpy(data, readpubek, sizeof(readpubek));
-
-       err = transmit_cmd(chip, data, READ_PUBEK_RESULT_SIZE,
+       tpm_cmd.header.in = tpm_readpubek_header;
+       err = transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
                        "attempting to read the PUBEK");
        if (err)
                goto out;
@@ -812,7 +836,7 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
           256 byte modulus
           ignore checksum 20 bytes
         */
-
+       data = tpm_cmd.params.readpubek_out_buffer;
        str +=
            sprintf(str,
                    "Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n"
@@ -832,65 +856,33 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
        }
 out:
        rc = str - buf;
-       kfree(data);
        return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_show_pubek);
 
-#define CAP_VERSION_1_1 6
-#define CAP_VERSION_1_2 0x1A
-#define CAP_VERSION_IDX 13
-static const u8 cap_version[] = {
-       0, 193,                 /* TPM_TAG_RQU_COMMAND */
-       0, 0, 0, 18,            /* length */
-       0, 0, 0, 101,           /* TPM_ORD_GetCapability */
-       0, 0, 0, 0,
-       0, 0, 0, 0
-};
 
 ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr,
                      char *buf)
 {
-       u8 *data;
+       cap_t cap;
        ssize_t rc;
        char *str = buf;
 
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
-
-       data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_PROP;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER;
-
-       rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
+       rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
                        "attempting to determine the manufacturer");
-       if (rc) {
-               kfree(data);
+       if (rc)
                return 0;
-       }
-
        str += sprintf(str, "Manufacturer: 0x%x\n",
-                      be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))));
+                      be32_to_cpu(cap.manufacturer_id));
 
-       memcpy(data, cap_version, sizeof(cap_version));
-       data[CAP_VERSION_IDX] = CAP_VERSION_1_1;
-       rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-                       "attempting to determine the 1.1 version");
+       rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap,
+                       "attempting to determine the 1.1 version");
        if (rc)
-               goto out;
-
+               return 0;
        str += sprintf(str,
                       "TCG version: %d.%d\nFirmware version: %d.%d\n",
-                      (int) data[14], (int) data[15], (int) data[16],
-                      (int) data[17]);
-
-out:
-       kfree(data);
+                      cap.tpm_version.Major, cap.tpm_version.Minor,
+                      cap.tpm_version.revMajor, cap.tpm_version.revMinor);
        return str - buf;
 }
 EXPORT_SYMBOL_GPL(tpm_show_caps);
@@ -898,51 +890,25 @@ EXPORT_SYMBOL_GPL(tpm_show_caps);
 ssize_t tpm_show_caps_1_2(struct device * dev,
                          struct device_attribute * attr, char *buf)
 {
-       u8 *data;
-       ssize_t len;
+       cap_t cap;
+       ssize_t rc;
        char *str = buf;
 
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
-
-       data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_PROP;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER;
-
-       len = tpm_transmit(chip, data, TPM_INTERNAL_RESULT_SIZE);
-       if (len <= TPM_ERROR_SIZE) {
-               dev_dbg(chip->dev, "A TPM error (%d) occurred "
-                       "attempting to determine the manufacturer\n",
-                       be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX))));
-               kfree(data);
+       rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
+                       "attempting to determine the manufacturer");
+       if (rc)
                return 0;
-       }
-
        str += sprintf(str, "Manufacturer: 0x%x\n",
-                      be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))));
-
-       memcpy(data, cap_version, sizeof(cap_version));
-       data[CAP_VERSION_IDX] = CAP_VERSION_1_2;
-
-       len = tpm_transmit(chip, data, TPM_INTERNAL_RESULT_SIZE);
-       if (len <= TPM_ERROR_SIZE) {
-               dev_err(chip->dev, "A TPM error (%d) occurred "
-                       "attempting to determine the 1.2 version\n",
-                       be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX))));
-               goto out;
-       }
+                      be32_to_cpu(cap.manufacturer_id));
+       rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap,
+                        "attempting to determine the 1.2 version");
+       if (rc)
+               return 0;
        str += sprintf(str,
                       "TCG version: %d.%d\nFirmware version: %d.%d\n",
-                      (int) data[16], (int) data[17], (int) data[18],
-                      (int) data[19]);
-
-out:
-       kfree(data);
+                      cap.tpm_version_1_2.Major, cap.tpm_version_1_2.Minor,
+                      cap.tpm_version_1_2.revMajor,
+                      cap.tpm_version_1_2.revMinor);
        return str - buf;
 }
 EXPORT_SYMBOL_GPL(tpm_show_caps_1_2);
index 8e30df4a4388166343af7bfed2aadefdd4f809bc..8e00b4ddd0830699bca8bab65c3e855657b21978 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/miscdevice.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
+#include <linux/tpm.h>
 
 enum tpm_timeout {
        TPM_TIMEOUT = 5,        /* msecs */
@@ -123,6 +124,147 @@ static inline void tpm_write_index(int base, int index, int value)
        outb(index, base);
        outb(value & 0xFF, base+1);
 }
+struct tpm_input_header {
+       __be16  tag;
+       __be32  length;
+       __be32  ordinal;
+}__attribute__((packed));
+
+struct tpm_output_header {
+       __be16  tag;
+       __be32  length;
+       __be32  return_code;
+}__attribute__((packed));
+
+struct stclear_flags_t {
+       __be16  tag;
+       u8      deactivated;
+       u8      disableForceClear;
+       u8      physicalPresence;
+       u8      physicalPresenceLock;
+       u8      bGlobalLock;
+}__attribute__((packed));
+
+struct tpm_version_t {
+       u8      Major;
+       u8      Minor;
+       u8      revMajor;
+       u8      revMinor;
+}__attribute__((packed));
+
+struct tpm_version_1_2_t {
+       __be16  tag;
+       u8      Major;
+       u8      Minor;
+       u8      revMajor;
+       u8      revMinor;
+}__attribute__((packed));
+
+struct timeout_t {
+       __be32  a;
+       __be32  b;
+       __be32  c;
+       __be32  d;
+}__attribute__((packed));
+
+struct duration_t {
+       __be32  tpm_short;
+       __be32  tpm_medium;
+       __be32  tpm_long;
+}__attribute__((packed));
+
+struct permanent_flags_t {
+       __be16  tag;
+       u8      disable;
+       u8      ownership;
+       u8      deactivated;
+       u8      readPubek;
+       u8      disableOwnerClear;
+       u8      allowMaintenance;
+       u8      physicalPresenceLifetimeLock;
+       u8      physicalPresenceHWEnable;
+       u8      physicalPresenceCMDEnable;
+       u8      CEKPUsed;
+       u8      TPMpost;
+       u8      TPMpostLock;
+       u8      FIPS;
+       u8      operator;
+       u8      enableRevokeEK;
+       u8      nvLocked;
+       u8      readSRKPub;
+       u8      tpmEstablished;
+       u8      maintenanceDone;
+       u8      disableFullDALogicInfo;
+}__attribute__((packed));
+
+typedef union {
+       struct  permanent_flags_t perm_flags;
+       struct  stclear_flags_t stclear_flags;
+       bool    owned;
+       __be32  num_pcrs;
+       struct  tpm_version_t   tpm_version;
+       struct  tpm_version_1_2_t tpm_version_1_2;
+       __be32  manufacturer_id;
+       struct timeout_t  timeout;
+       struct duration_t duration;
+} cap_t;
+
+struct tpm_getcap_params_in {
+       __be32  cap;
+       __be32  subcap_size;
+       __be32  subcap;
+}__attribute__((packed));
+
+struct tpm_getcap_params_out {
+       __be32  cap_size;
+       cap_t   cap;
+}__attribute__((packed));
+
+struct tpm_readpubek_params_out {
+       u8      algorithm[4];
+       u8      encscheme[2];
+       u8      sigscheme[2];
+       u8      parameters[12]; /*assuming RSA*/
+       __be32  keysize;
+       u8      modulus[256];
+       u8      checksum[20];
+}__attribute__((packed));
+
+typedef union {
+       struct  tpm_input_header in;
+       struct  tpm_output_header out;
+} tpm_cmd_header;
+
+#define TPM_DIGEST_SIZE 20
+struct tpm_pcrread_out {
+       u8      pcr_result[TPM_DIGEST_SIZE];
+}__attribute__((packed));
+
+struct tpm_pcrread_in {
+       __be32  pcr_idx;
+}__attribute__((packed));
+
+struct tpm_pcrextend_in {
+       __be32  pcr_idx;
+       u8      hash[TPM_DIGEST_SIZE];
+}__attribute__((packed));
+
+typedef union {
+       struct  tpm_getcap_params_out getcap_out;
+       struct  tpm_readpubek_params_out readpubek_out;
+       u8      readpubek_out_buffer[sizeof(struct tpm_readpubek_params_out)];
+       struct  tpm_getcap_params_in getcap_in;
+       struct  tpm_pcrread_in  pcrread_in;
+       struct  tpm_pcrread_out pcrread_out;
+       struct  tpm_pcrextend_in pcrextend_in;
+} tpm_cmd_params;
+
+struct tpm_cmd_t {
+       tpm_cmd_header  header;
+       tpm_cmd_params  params;
+}__attribute__((packed));
+
+ssize_t        tpm_getcap(struct device *, __be32, cap_t *, const char *);
 
 extern void tpm_get_timeouts(struct tpm_chip *);
 extern void tpm_gen_interrupt(struct tpm_chip *);
index d6daf3c507d3491b0e78a75f2366820114027f03..d270e8eb3e67f3fb0af2af1b6d4ea7a36521cf07 100644 (file)
@@ -104,7 +104,8 @@ EXPORT_SYMBOL_GPL(unlock_policy_rwsem_write);
 
 
 /* internal prototypes */
-static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event);
+static int __cpufreq_governor(struct cpufreq_policy *policy,
+               unsigned int event);
 static unsigned int __cpufreq_get(unsigned int cpu);
 static void handle_update(struct work_struct *work);
 
@@ -128,7 +129,7 @@ static int __init init_cpufreq_transition_notifier_list(void)
 pure_initcall(init_cpufreq_transition_notifier_list);
 
 static LIST_HEAD(cpufreq_governor_list);
-static DEFINE_MUTEX (cpufreq_governor_mutex);
+static DEFINE_MUTEX(cpufreq_governor_mutex);
 
 struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
 {
@@ -371,7 +372,7 @@ static struct cpufreq_governor *__find_governor(const char *str_governor)
        struct cpufreq_governor *t;
 
        list_for_each_entry(t, &cpufreq_governor_list, governor_list)
-               if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN))
+               if (!strnicmp(str_governor, t->name, CPUFREQ_NAME_LEN))
                        return t;
 
        return NULL;
@@ -429,15 +430,11 @@ static int cpufreq_parse_governor(char *str_governor, unsigned int *policy,
 
                mutex_unlock(&cpufreq_governor_mutex);
        }
-  out:
+out:
        return err;
 }
 
 
-/* drivers/base/cpu.c */
-extern struct sysdev_class cpu_sysdev_class;
-
-
 /**
  * cpufreq_per_cpu_attr_read() / show_##file_name() -
  * print out cpufreq information
@@ -450,11 +447,12 @@ extern struct sysdev_class cpu_sysdev_class;
 static ssize_t show_##file_name                                \
 (struct cpufreq_policy *policy, char *buf)             \
 {                                                      \
-       return sprintf (buf, "%u\n", policy->object);   \
+       return sprintf(buf, "%u\n", policy->object);    \
 }
 
 show_one(cpuinfo_min_freq, cpuinfo.min_freq);
 show_one(cpuinfo_max_freq, cpuinfo.max_freq);
+show_one(cpuinfo_transition_latency, cpuinfo.transition_latency);
 show_one(scaling_min_freq, min);
 show_one(scaling_max_freq, max);
 show_one(scaling_cur_freq, cur);
@@ -476,7 +474,7 @@ static ssize_t store_##file_name                                    \
        if (ret)                                                        \
                return -EINVAL;                                         \
                                                                        \
-       ret = sscanf (buf, "%u", &new_policy.object);                   \
+       ret = sscanf(buf, "%u", &new_policy.object);                    \
        if (ret != 1)                                                   \
                return -EINVAL;                                         \
                                                                        \
@@ -486,8 +484,8 @@ static ssize_t store_##file_name                                    \
        return ret ? ret : count;                                       \
 }
 
-store_one(scaling_min_freq,min);
-store_one(scaling_max_freq,max);
+store_one(scaling_min_freq, min);
+store_one(scaling_max_freq, max);
 
 /**
  * show_cpuinfo_cur_freq - current CPU frequency as detected by hardware
@@ -507,12 +505,13 @@ static ssize_t show_cpuinfo_cur_freq(struct cpufreq_policy *policy,
  */
 static ssize_t show_scaling_governor(struct cpufreq_policy *policy, char *buf)
 {
-       if(policy->policy == CPUFREQ_POLICY_POWERSAVE)
+       if (policy->policy == CPUFREQ_POLICY_POWERSAVE)
                return sprintf(buf, "powersave\n");
        else if (policy->policy == CPUFREQ_POLICY_PERFORMANCE)
                return sprintf(buf, "performance\n");
        else if (policy->governor)
-               return scnprintf(buf, CPUFREQ_NAME_LEN, "%s\n", policy->governor->name);
+               return scnprintf(buf, CPUFREQ_NAME_LEN, "%s\n",
+                               policy->governor->name);
        return -EINVAL;
 }
 
@@ -531,7 +530,7 @@ static ssize_t store_scaling_governor(struct cpufreq_policy *policy,
        if (ret)
                return ret;
 
-       ret = sscanf (buf, "%15s", str_governor);
+       ret = sscanf(buf, "%15s", str_governor);
        if (ret != 1)
                return -EINVAL;
 
@@ -575,7 +574,8 @@ static ssize_t show_scaling_available_governors(struct cpufreq_policy *policy,
        }
 
        list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
-               if (i >= (ssize_t) ((PAGE_SIZE / sizeof(char)) - (CPUFREQ_NAME_LEN + 2)))
+               if (i >= (ssize_t) ((PAGE_SIZE / sizeof(char))
+                   - (CPUFREQ_NAME_LEN + 2)))
                        goto out;
                i += scnprintf(&buf[i], CPUFREQ_NAME_LEN, "%s ", t->name);
        }
@@ -594,7 +594,7 @@ static ssize_t show_cpus(const struct cpumask *mask, char *buf)
                        i += scnprintf(&buf[i], (PAGE_SIZE - i - 2), " ");
                i += scnprintf(&buf[i], (PAGE_SIZE - i - 2), "%u", cpu);
                if (i >= (PAGE_SIZE - 5))
-                   break;
+                       break;
        }
        i += sprintf(&buf[i], "\n");
        return i;
@@ -660,6 +660,7 @@ __ATTR(_name, 0644, show_##_name, store_##_name)
 define_one_ro0400(cpuinfo_cur_freq);
 define_one_ro(cpuinfo_min_freq);
 define_one_ro(cpuinfo_max_freq);
+define_one_ro(cpuinfo_transition_latency);
 define_one_ro(scaling_available_governors);
 define_one_ro(scaling_driver);
 define_one_ro(scaling_cur_freq);
@@ -673,6 +674,7 @@ define_one_rw(scaling_setspeed);
 static struct attribute *default_attrs[] = {
        &cpuinfo_min_freq.attr,
        &cpuinfo_max_freq.attr,
+       &cpuinfo_transition_latency.attr,
        &scaling_min_freq.attr,
        &scaling_max_freq.attr,
        &affected_cpus.attr,
@@ -684,10 +686,10 @@ static struct attribute *default_attrs[] = {
        NULL
 };
 
-#define to_policy(k) container_of(k,struct cpufreq_policy,kobj)
-#define to_attr(a) container_of(a,struct freq_attr,attr)
+#define to_policy(k) container_of(k, struct cpufreq_policy, kobj)
+#define to_attr(a) container_of(a, struct freq_attr, attr)
 
-static ssize_t show(struct kobject *kobj, struct attribute *attr ,char *buf)
+static ssize_t show(struct kobject *kobj, struct attribute *attrchar *buf)
 {
        struct cpufreq_policy *policy = to_policy(kobj);
        struct freq_attr *fattr = to_attr(attr);
@@ -853,10 +855,10 @@ static int cpufreq_add_dev(struct sys_device *sys_dev)
                if (cpu == j)
                        continue;
 
-               /* check for existing affected CPUs.  They may not be aware
-                * of it due to CPU Hotplug.
+               /* Check for existing affected CPUs.
+                * They may not be aware of it due to CPU Hotplug.
                 */
-               managed_policy = cpufreq_cpu_get(j);            // FIXME: Where is this released?  What about error paths?
+               managed_policy = cpufreq_cpu_get(j);            /* FIXME: Where is this released?  What about error paths? */
                if (unlikely(managed_policy)) {
 
                        /* Set proper policy_cpu */
@@ -1127,8 +1129,8 @@ static void handle_update(struct work_struct *work)
  *     @old_freq: CPU frequency the kernel thinks the CPU runs at
  *     @new_freq: CPU frequency the CPU actually runs at
  *
- *     We adjust to current frequency first, and need to clean up later. So either call
- *     to cpufreq_update_policy() or schedule handle_update()).
+ *     We adjust to current frequency first, and need to clean up later.
+ *     So either call to cpufreq_update_policy() or schedule handle_update()).
  */
 static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq,
                                unsigned int new_freq)
@@ -1610,7 +1612,8 @@ EXPORT_SYMBOL_GPL(cpufreq_unregister_governor);
 
 /**
  * cpufreq_get_policy - get the current cpufreq_policy
- * @policy: struct cpufreq_policy into which the current cpufreq_policy is written
+ * @policy: struct cpufreq_policy into which the current cpufreq_policy
+ *     is written
  *
  * Reads the current cpufreq policy.
  */
index 0320962c4ec5c73b20fefc8dd2b5145f3d5d2411..2ecd95e4ab1aca33c6e858698d0c16f1b28a19ba 100644 (file)
@@ -4,7 +4,7 @@
  *  Copyright (C)  2001 Russell King
  *            (C)  2003 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
  *                      Jun Nakajima <jun.nakajima@intel.com>
- *            (C)  2004 Alexander Clouter <alex-kernel@digriz.org.uk>
+ *            (C)  2009 Alexander Clouter <alex@digriz.org.uk>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/smp.h>
 #include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/ctype.h>
 #include <linux/cpufreq.h>
-#include <linux/sysctl.h>
-#include <linux/types.h>
-#include <linux/fs.h>
-#include <linux/sysfs.h>
 #include <linux/cpu.h>
-#include <linux/kmod.h>
-#include <linux/workqueue.h>
 #include <linux/jiffies.h>
 #include <linux/kernel_stat.h>
-#include <linux/percpu.h>
 #include <linux/mutex.h>
+#include <linux/hrtimer.h>
+#include <linux/tick.h>
+#include <linux/ktime.h>
+#include <linux/sched.h>
+
 /*
  * dbs is used in this file as a shortform for demandbased switching
  * It helps to keep variable names smaller, simpler
  * latency of the processor. The governor will work on any processor with
  * transition latency <= 10mS, using appropriate sampling
  * rate.
- * For CPUs with transition latency > 10mS (mostly drivers
- * with CPUFREQ_ETERNAL), this governor will not work.
+ * For CPUs with transition latency > 10mS (mostly drivers with CPUFREQ_ETERNAL)
+ * this governor will not work.
  * All times here are in uS.
  */
 static unsigned int def_sampling_rate;
 #define MIN_SAMPLING_RATE_RATIO                        (2)
 /* for correct statistics, we need at least 10 ticks between each measure */
-#define MIN_STAT_SAMPLING_RATE                 \
+#define MIN_STAT_SAMPLING_RATE                         \
                        (MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10))
 #define MIN_SAMPLING_RATE                      \
                        (def_sampling_rate / MIN_SAMPLING_RATE_RATIO)
+/* Above MIN_SAMPLING_RATE will vanish with its sysfs file soon
+ * Define the minimal settable sampling rate to the greater of:
+ *   - "HW transition latency" * 100 (same as default sampling / 10)
+ *   - MIN_STAT_SAMPLING_RATE
+ * To avoid that userspace shoots itself.
+*/
+static unsigned int minimum_sampling_rate(void)
+{
+       return max(def_sampling_rate / 10, MIN_STAT_SAMPLING_RATE);
+}
+
+/* This will also vanish soon with removing sampling_rate_max */
 #define MAX_SAMPLING_RATE                      (500 * def_sampling_rate)
-#define DEF_SAMPLING_RATE_LATENCY_MULTIPLIER   (1000)
+#define LATENCY_MULTIPLIER                     (1000)
 #define DEF_SAMPLING_DOWN_FACTOR               (1)
 #define MAX_SAMPLING_DOWN_FACTOR               (10)
 #define TRANSITION_LATENCY_LIMIT               (10 * 1000 * 1000)
@@ -63,12 +70,15 @@ static unsigned int def_sampling_rate;
 static void do_dbs_timer(struct work_struct *work);
 
 struct cpu_dbs_info_s {
+       cputime64_t prev_cpu_idle;
+       cputime64_t prev_cpu_wall;
+       cputime64_t prev_cpu_nice;
        struct cpufreq_policy *cur_policy;
-       unsigned int prev_cpu_idle_up;
-       unsigned int prev_cpu_idle_down;
-       unsigned int enable;
+       struct delayed_work work;
        unsigned int down_skip;
        unsigned int requested_freq;
+       int cpu;
+       unsigned int enable:1;
 };
 static DEFINE_PER_CPU(struct cpu_dbs_info_s, cpu_dbs_info);
 
@@ -82,19 +92,18 @@ static unsigned int dbs_enable;     /* number of CPUs using this policy */
  * cpu_hotplug lock should be taken before that. Note that cpu_hotplug lock
  * is recursive for the same process. -Venki
  */
-static DEFINE_MUTEX (dbs_mutex);
-static DECLARE_DELAYED_WORK(dbs_work, do_dbs_timer);
+static DEFINE_MUTEX(dbs_mutex);
 
-struct dbs_tuners {
+static struct workqueue_struct *kconservative_wq;
+
+static struct dbs_tuners {
        unsigned int sampling_rate;
        unsigned int sampling_down_factor;
        unsigned int up_threshold;
        unsigned int down_threshold;
        unsigned int ignore_nice;
        unsigned int freq_step;
-};
-
-static struct dbs_tuners dbs_tuners_ins = {
+} dbs_tuners_ins = {
        .up_threshold = DEF_FREQUENCY_UP_THRESHOLD,
        .down_threshold = DEF_FREQUENCY_DOWN_THRESHOLD,
        .sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR,
@@ -102,18 +111,37 @@ static struct dbs_tuners dbs_tuners_ins = {
        .freq_step = 5,
 };
 
-static inline unsigned int get_cpu_idle_time(unsigned int cpu)
+static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu,
+                                                       cputime64_t *wall)
 {
-       unsigned int add_nice = 0, ret;
+       cputime64_t idle_time;
+       cputime64_t cur_wall_time;
+       cputime64_t busy_time;
 
-       if (dbs_tuners_ins.ignore_nice)
-               add_nice = kstat_cpu(cpu).cpustat.nice;
+       cur_wall_time = jiffies64_to_cputime64(get_jiffies_64());
+       busy_time = cputime64_add(kstat_cpu(cpu).cpustat.user,
+                       kstat_cpu(cpu).cpustat.system);
+
+       busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.irq);
+       busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.softirq);
+       busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.steal);
+       busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.nice);
+
+       idle_time = cputime64_sub(cur_wall_time, busy_time);
+       if (wall)
+               *wall = cur_wall_time;
+
+       return idle_time;
+}
+
+static inline cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall)
+{
+       u64 idle_time = get_cpu_idle_time_us(cpu, wall);
 
-       ret = kstat_cpu(cpu).cpustat.idle +
-               kstat_cpu(cpu).cpustat.iowait +
-               add_nice;
+       if (idle_time == -1ULL)
+               return get_cpu_idle_time_jiffy(cpu, wall);
 
-       return ret;
+       return idle_time;
 }
 
 /* keep track of frequency transitions */
@@ -125,10 +153,21 @@ dbs_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
        struct cpu_dbs_info_s *this_dbs_info = &per_cpu(cpu_dbs_info,
                                                        freq->cpu);
 
+       struct cpufreq_policy *policy;
+
        if (!this_dbs_info->enable)
                return 0;
 
-       this_dbs_info->requested_freq = freq->new;
+       policy = this_dbs_info->cur_policy;
+
+       /*
+        * we only care if our internally tracked freq moves outside
+        * the 'valid' ranges of freqency available to us otherwise
+        * we do not change it
+       */
+       if (this_dbs_info->requested_freq > policy->max
+                       || this_dbs_info->requested_freq < policy->min)
+               this_dbs_info->requested_freq = freq->new;
 
        return 0;
 }
@@ -140,16 +179,31 @@ static struct notifier_block dbs_cpufreq_notifier_block = {
 /************************** sysfs interface ************************/
 static ssize_t show_sampling_rate_max(struct cpufreq_policy *policy, char *buf)
 {
-       return sprintf (buf, "%u\n", MAX_SAMPLING_RATE);
+       static int print_once;
+
+       if (!print_once) {
+               printk(KERN_INFO "CPUFREQ: conservative sampling_rate_max "
+                      "sysfs file is deprecated - used by: %s\n",
+                      current->comm);
+               print_once = 1;
+       }
+       return sprintf(buf, "%u\n", MAX_SAMPLING_RATE);
 }
 
 static ssize_t show_sampling_rate_min(struct cpufreq_policy *policy, char *buf)
 {
-       return sprintf (buf, "%u\n", MIN_SAMPLING_RATE);
+       static int print_once;
+
+       if (!print_once) {
+               printk(KERN_INFO "CPUFREQ: conservative sampling_rate_max "
+                      "sysfs file is deprecated - used by: %s\n", current->comm);
+               print_once = 1;
+       }
+       return sprintf(buf, "%u\n", MIN_SAMPLING_RATE);
 }
 
-#define define_one_ro(_name)                           \
-static struct freq_attr _name =                                \
+#define define_one_ro(_name)           \
+static struct freq_attr _name =                \
 __ATTR(_name, 0444, show_##_name, NULL)
 
 define_one_ro(sampling_rate_max);
@@ -174,7 +228,8 @@ static ssize_t store_sampling_down_factor(struct cpufreq_policy *unused,
 {
        unsigned int input;
        int ret;
-       ret = sscanf (buf, "%u", &input);
+       ret = sscanf(buf, "%u", &input);
+
        if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1)
                return -EINVAL;
 
@@ -190,15 +245,13 @@ static ssize_t store_sampling_rate(struct cpufreq_policy *unused,
 {
        unsigned int input;
        int ret;
-       ret = sscanf (buf, "%u", &input);
+       ret = sscanf(buf, "%u", &input);
 
-       mutex_lock(&dbs_mutex);
-       if (ret != 1 || input > MAX_SAMPLING_RATE || input < MIN_SAMPLING_RATE) {
-               mutex_unlock(&dbs_mutex);
+       if (ret != 1)
                return -EINVAL;
-       }
 
-       dbs_tuners_ins.sampling_rate = input;
+       mutex_lock(&dbs_mutex);
+       dbs_tuners_ins.sampling_rate = max(input, minimum_sampling_rate());
        mutex_unlock(&dbs_mutex);
 
        return count;
@@ -209,10 +262,11 @@ static ssize_t store_up_threshold(struct cpufreq_policy *unused,
 {
        unsigned int input;
        int ret;
-       ret = sscanf (buf, "%u", &input);
+       ret = sscanf(buf, "%u", &input);
 
        mutex_lock(&dbs_mutex);
-       if (ret != 1 || input > 100 || input <= dbs_tuners_ins.down_threshold) {
+       if (ret != 1 || input > 100 ||
+                       input <= dbs_tuners_ins.down_threshold) {
                mutex_unlock(&dbs_mutex);
                return -EINVAL;
        }
@@ -228,10 +282,12 @@ static ssize_t store_down_threshold(struct cpufreq_policy *unused,
 {
        unsigned int input;
        int ret;
-       ret = sscanf (buf, "%u", &input);
+       ret = sscanf(buf, "%u", &input);
 
        mutex_lock(&dbs_mutex);
-       if (ret != 1 || input > 100 || input >= dbs_tuners_ins.up_threshold) {
+       /* cannot be lower than 11 otherwise freq will not fall */
+       if (ret != 1 || input < 11 || input > 100 ||
+                       input >= dbs_tuners_ins.up_threshold) {
                mutex_unlock(&dbs_mutex);
                return -EINVAL;
        }
@@ -264,12 +320,14 @@ static ssize_t store_ignore_nice_load(struct cpufreq_policy *policy,
        }
        dbs_tuners_ins.ignore_nice = input;
 
-       /* we need to re-evaluate prev_cpu_idle_up and prev_cpu_idle_down */
+       /* we need to re-evaluate prev_cpu_idle */
        for_each_online_cpu(j) {
-               struct cpu_dbs_info_s *j_dbs_info;
-               j_dbs_info = &per_cpu(cpu_dbs_info, j);
-               j_dbs_info->prev_cpu_idle_up = get_cpu_idle_time(j);
-               j_dbs_info->prev_cpu_idle_down = j_dbs_info->prev_cpu_idle_up;
+               struct cpu_dbs_info_s *dbs_info;
+               dbs_info = &per_cpu(cpu_dbs_info, j);
+               dbs_info->prev_cpu_idle = get_cpu_idle_time(j,
+                                               &dbs_info->prev_cpu_wall);
+               if (dbs_tuners_ins.ignore_nice)
+                       dbs_info->prev_cpu_nice = kstat_cpu(j).cpustat.nice;
        }
        mutex_unlock(&dbs_mutex);
 
@@ -281,7 +339,6 @@ static ssize_t store_freq_step(struct cpufreq_policy *policy,
 {
        unsigned int input;
        int ret;
-
        ret = sscanf(buf, "%u", &input);
 
        if (ret != 1)
@@ -310,7 +367,7 @@ define_one_rw(down_threshold);
 define_one_rw(ignore_nice_load);
 define_one_rw(freq_step);
 
-static struct attribute * dbs_attributes[] = {
+static struct attribute *dbs_attributes[] = {
        &sampling_rate_max.attr,
        &sampling_rate_min.attr,
        &sampling_rate.attr,
@@ -329,55 +386,78 @@ static struct attribute_group dbs_attr_group = {
 
 /************************** sysfs end ************************/
 
-static void dbs_check_cpu(int cpu)
+static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
 {
-       unsigned int idle_ticks, up_idle_ticks, down_idle_ticks;
-       unsigned int tmp_idle_ticks, total_idle_ticks;
+       unsigned int load = 0;
        unsigned int freq_target;
-       unsigned int freq_down_sampling_rate;
-       struct cpu_dbs_info_s *this_dbs_info = &per_cpu(cpu_dbs_info, cpu);
-       struct cpufreq_policy *policy;
 
-       if (!this_dbs_info->enable)
-               return;
+       struct cpufreq_policy *policy;
+       unsigned int j;
 
        policy = this_dbs_info->cur_policy;
 
        /*
-        * The default safe range is 20% to 80%
-        * Every sampling_rate, we check
-        *      - If current idle time is less than 20%, then we try to
-        *        increase frequency
-        * Every sampling_rate*sampling_down_factor, we check
-        *      - If current idle time is more than 80%, then we try to
-        *        decrease frequency
+        * Every sampling_rate, we check, if current idle time is less
+        * than 20% (default), then we try to increase frequency
+        * Every sampling_rate*sampling_down_factor, we check, if current
+        * idle time is more than 80%, then we try to decrease frequency
         *
         * Any frequency increase takes it to the maximum frequency.
         * Frequency reduction happens at minimum steps of
-        * 5% (default) of max_frequency
+        * 5% (default) of maximum frequency
         */
 
-       /* Check for frequency increase */
-       idle_ticks = UINT_MAX;
+       /* Get Absolute Load */
+       for_each_cpu(j, policy->cpus) {
+               struct cpu_dbs_info_s *j_dbs_info;
+               cputime64_t cur_wall_time, cur_idle_time;
+               unsigned int idle_time, wall_time;
 
-       /* Check for frequency increase */
-       total_idle_ticks = get_cpu_idle_time(cpu);
-       tmp_idle_ticks = total_idle_ticks -
-               this_dbs_info->prev_cpu_idle_up;
-       this_dbs_info->prev_cpu_idle_up = total_idle_ticks;
+               j_dbs_info = &per_cpu(cpu_dbs_info, j);
+
+               cur_idle_time = get_cpu_idle_time(j, &cur_wall_time);
+
+               wall_time = (unsigned int) cputime64_sub(cur_wall_time,
+                               j_dbs_info->prev_cpu_wall);
+               j_dbs_info->prev_cpu_wall = cur_wall_time;
+
+               idle_time = (unsigned int) cputime64_sub(cur_idle_time,
+                               j_dbs_info->prev_cpu_idle);
+               j_dbs_info->prev_cpu_idle = cur_idle_time;
+
+               if (dbs_tuners_ins.ignore_nice) {
+                       cputime64_t cur_nice;
+                       unsigned long cur_nice_jiffies;
+
+                       cur_nice = cputime64_sub(kstat_cpu(j).cpustat.nice,
+                                        j_dbs_info->prev_cpu_nice);
+                       /*
+                        * Assumption: nice time between sampling periods will
+                        * be less than 2^32 jiffies for 32 bit sys
+                        */
+                       cur_nice_jiffies = (unsigned long)
+                                       cputime64_to_jiffies64(cur_nice);
 
-       if (tmp_idle_ticks < idle_ticks)
-               idle_ticks = tmp_idle_ticks;
+                       j_dbs_info->prev_cpu_nice = kstat_cpu(j).cpustat.nice;
+                       idle_time += jiffies_to_usecs(cur_nice_jiffies);
+               }
+
+               if (unlikely(!wall_time || wall_time < idle_time))
+                       continue;
+
+               load = 100 * (wall_time - idle_time) / wall_time;
+       }
 
-       /* Scale idle ticks by 100 and compare with up and down ticks */
-       idle_ticks *= 100;
-       up_idle_ticks = (100 - dbs_tuners_ins.up_threshold) *
-                       usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+       /*
+        * break out if we 'cannot' reduce the speed as the user might
+        * want freq_step to be zero
+        */
+       if (dbs_tuners_ins.freq_step == 0)
+               return;
 
-       if (idle_ticks < up_idle_ticks) {
+       /* Check for frequency increase */
+       if (load > dbs_tuners_ins.up_threshold) {
                this_dbs_info->down_skip = 0;
-               this_dbs_info->prev_cpu_idle_down =
-                       this_dbs_info->prev_cpu_idle_up;
 
                /* if we are already at full speed then break out early */
                if (this_dbs_info->requested_freq == policy->max)
@@ -398,49 +478,24 @@ static void dbs_check_cpu(int cpu)
                return;
        }
 
-       /* Check for frequency decrease */
-       this_dbs_info->down_skip++;
-       if (this_dbs_info->down_skip < dbs_tuners_ins.sampling_down_factor)
-               return;
-
-       /* Check for frequency decrease */
-       total_idle_ticks = this_dbs_info->prev_cpu_idle_up;
-       tmp_idle_ticks = total_idle_ticks -
-               this_dbs_info->prev_cpu_idle_down;
-       this_dbs_info->prev_cpu_idle_down = total_idle_ticks;
-
-       if (tmp_idle_ticks < idle_ticks)
-               idle_ticks = tmp_idle_ticks;
-
-       /* Scale idle ticks by 100 and compare with up and down ticks */
-       idle_ticks *= 100;
-       this_dbs_info->down_skip = 0;
-
-       freq_down_sampling_rate = dbs_tuners_ins.sampling_rate *
-               dbs_tuners_ins.sampling_down_factor;
-       down_idle_ticks = (100 - dbs_tuners_ins.down_threshold) *
-               usecs_to_jiffies(freq_down_sampling_rate);
-
-       if (idle_ticks > down_idle_ticks) {
-               /*
-                * if we are already at the lowest speed then break out early
-                * or if we 'cannot' reduce the speed as the user might want
-                * freq_target to be zero
-                */
-               if (this_dbs_info->requested_freq == policy->min
-                               || dbs_tuners_ins.freq_step == 0)
-                       return;
-
+       /*
+        * The optimal frequency is the frequency that is the lowest that
+        * can support the current CPU usage without triggering the up
+        * policy. To be safe, we focus 10 points under the threshold.
+        */
+       if (load < (dbs_tuners_ins.down_threshold - 10)) {
                freq_target = (dbs_tuners_ins.freq_step * policy->max) / 100;
 
-               /* max freq cannot be less than 100. But who knows.... */
-               if (unlikely(freq_target == 0))
-                       freq_target = 5;
-
                this_dbs_info->requested_freq -= freq_target;
                if (this_dbs_info->requested_freq < policy->min)
                        this_dbs_info->requested_freq = policy->min;
 
+               /*
+                * if we cannot reduce the frequency anymore, break out early
+                */
+               if (policy->cur == policy->min)
+                       return;
+
                __cpufreq_driver_target(policy, this_dbs_info->requested_freq,
                                CPUFREQ_RELATION_H);
                return;
@@ -449,27 +504,45 @@ static void dbs_check_cpu(int cpu)
 
 static void do_dbs_timer(struct work_struct *work)
 {
-       int i;
-       mutex_lock(&dbs_mutex);
-       for_each_online_cpu(i)
-               dbs_check_cpu(i);
-       schedule_delayed_work(&dbs_work,
-                       usecs_to_jiffies(dbs_tuners_ins.sampling_rate));
-       mutex_unlock(&dbs_mutex);
+       struct cpu_dbs_info_s *dbs_info =
+               container_of(work, struct cpu_dbs_info_s, work.work);
+       unsigned int cpu = dbs_info->cpu;
+
+       /* We want all CPUs to do sampling nearly on same jiffy */
+       int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+
+       delay -= jiffies % delay;
+
+       if (lock_policy_rwsem_write(cpu) < 0)
+               return;
+
+       if (!dbs_info->enable) {
+               unlock_policy_rwsem_write(cpu);
+               return;
+       }
+
+       dbs_check_cpu(dbs_info);
+
+       queue_delayed_work_on(cpu, kconservative_wq, &dbs_info->work, delay);
+       unlock_policy_rwsem_write(cpu);
 }
 
-static inline void dbs_timer_init(void)
+static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info)
 {
-       init_timer_deferrable(&dbs_work.timer);
-       schedule_delayed_work(&dbs_work,
-                       usecs_to_jiffies(dbs_tuners_ins.sampling_rate));
-       return;
+       /* We want all CPUs to do sampling nearly on same jiffy */
+       int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+       delay -= jiffies % delay;
+
+       dbs_info->enable = 1;
+       INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer);
+       queue_delayed_work_on(dbs_info->cpu, kconservative_wq, &dbs_info->work,
+                               delay);
 }
 
-static inline void dbs_timer_exit(void)
+static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info)
 {
-       cancel_delayed_work(&dbs_work);
-       return;
+       dbs_info->enable = 0;
+       cancel_delayed_work(&dbs_info->work);
 }
 
 static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
@@ -503,11 +576,13 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
                        j_dbs_info = &per_cpu(cpu_dbs_info, j);
                        j_dbs_info->cur_policy = policy;
 
-                       j_dbs_info->prev_cpu_idle_up = get_cpu_idle_time(cpu);
-                       j_dbs_info->prev_cpu_idle_down
-                               = j_dbs_info->prev_cpu_idle_up;
+                       j_dbs_info->prev_cpu_idle = get_cpu_idle_time(j,
+                                               &j_dbs_info->prev_cpu_wall);
+                       if (dbs_tuners_ins.ignore_nice) {
+                               j_dbs_info->prev_cpu_nice =
+                                               kstat_cpu(j).cpustat.nice;
+                       }
                }
-               this_dbs_info->enable = 1;
                this_dbs_info->down_skip = 0;
                this_dbs_info->requested_freq = policy->cur;
 
@@ -523,38 +598,36 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
                        if (latency == 0)
                                latency = 1;
 
-                       def_sampling_rate = 10 * latency *
-                                       DEF_SAMPLING_RATE_LATENCY_MULTIPLIER;
-
-                       if (def_sampling_rate < MIN_STAT_SAMPLING_RATE)
-                               def_sampling_rate = MIN_STAT_SAMPLING_RATE;
+                       def_sampling_rate =
+                               max(latency * LATENCY_MULTIPLIER,
+                                   MIN_STAT_SAMPLING_RATE);
 
                        dbs_tuners_ins.sampling_rate = def_sampling_rate;
 
-                       dbs_timer_init();
                        cpufreq_register_notifier(
                                        &dbs_cpufreq_notifier_block,
                                        CPUFREQ_TRANSITION_NOTIFIER);
                }
+               dbs_timer_init(this_dbs_info);
 
                mutex_unlock(&dbs_mutex);
+
                break;
 
        case CPUFREQ_GOV_STOP:
                mutex_lock(&dbs_mutex);
-               this_dbs_info->enable = 0;
+               dbs_timer_exit(this_dbs_info);
                sysfs_remove_group(&policy->kobj, &dbs_attr_group);
                dbs_enable--;
+
                /*
                 * Stop the timerschedule work, when this governor
                 * is used for first time
                 */
-               if (dbs_enable == 0) {
-                       dbs_timer_exit();
+               if (dbs_enable == 0)
                        cpufreq_unregister_notifier(
                                        &dbs_cpufreq_notifier_block,
                                        CPUFREQ_TRANSITION_NOTIFIER);
-               }
 
                mutex_unlock(&dbs_mutex);
 
@@ -571,6 +644,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
                                        this_dbs_info->cur_policy,
                                        policy->min, CPUFREQ_RELATION_L);
                mutex_unlock(&dbs_mutex);
+
                break;
        }
        return 0;
@@ -588,23 +662,33 @@ struct cpufreq_governor cpufreq_gov_conservative = {
 
 static int __init cpufreq_gov_dbs_init(void)
 {
-       return cpufreq_register_governor(&cpufreq_gov_conservative);
+       int err;
+
+       kconservative_wq = create_workqueue("kconservative");
+       if (!kconservative_wq) {
+               printk(KERN_ERR "Creation of kconservative failed\n");
+               return -EFAULT;
+       }
+
+       err = cpufreq_register_governor(&cpufreq_gov_conservative);
+       if (err)
+               destroy_workqueue(kconservative_wq);
+
+       return err;
 }
 
 static void __exit cpufreq_gov_dbs_exit(void)
 {
-       /* Make sure that the scheduled work is indeed not running */
-       flush_scheduled_work();
-
        cpufreq_unregister_governor(&cpufreq_gov_conservative);
+       destroy_workqueue(kconservative_wq);
 }
 
 
-MODULE_AUTHOR ("Alexander Clouter <alex-kernel@digriz.org.uk>");
-MODULE_DESCRIPTION ("'cpufreq_conservative' - A dynamic cpufreq governor for "
+MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>");
+MODULE_DESCRIPTION("'cpufreq_conservative' - A dynamic cpufreq governor for "
                "Low Latency Frequency Transition capable processors "
                "optimised for use in a battery environment");
-MODULE_LICENSE ("GPL");
+MODULE_LICENSE("GPL");
 
 #ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE
 fs_initcall(cpufreq_gov_dbs_init);
index 6f45b1658a67d0bee41c5156cdca709e639d0b67..338f428a15b765ceaef4686225715d5d2f9f93e3 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/hrtimer.h>
 #include <linux/tick.h>
 #include <linux/ktime.h>
+#include <linux/sched.h>
 
 /*
  * dbs is used in this file as a shortform for demandbased switching
@@ -51,8 +52,20 @@ static unsigned int def_sampling_rate;
                        (MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10))
 #define MIN_SAMPLING_RATE                      \
                        (def_sampling_rate / MIN_SAMPLING_RATE_RATIO)
+/* Above MIN_SAMPLING_RATE will vanish with its sysfs file soon
+ * Define the minimal settable sampling rate to the greater of:
+ *   - "HW transition latency" * 100 (same as default sampling / 10)
+ *   - MIN_STAT_SAMPLING_RATE
+ * To avoid that userspace shoots itself.
+*/
+static unsigned int minimum_sampling_rate(void)
+{
+       return max(def_sampling_rate / 10, MIN_STAT_SAMPLING_RATE);
+}
+
+/* This will also vanish soon with removing sampling_rate_max */
 #define MAX_SAMPLING_RATE                      (500 * def_sampling_rate)
-#define DEF_SAMPLING_RATE_LATENCY_MULTIPLIER   (1000)
+#define LATENCY_MULTIPLIER                     (1000)
 #define TRANSITION_LATENCY_LIMIT               (10 * 1000 * 1000)
 
 static void do_dbs_timer(struct work_struct *work);
@@ -65,14 +78,14 @@ struct cpu_dbs_info_s {
        cputime64_t prev_cpu_wall;
        cputime64_t prev_cpu_nice;
        struct cpufreq_policy *cur_policy;
-       struct delayed_work work;
+       struct delayed_work work;
        struct cpufreq_frequency_table *freq_table;
        unsigned int freq_lo;
        unsigned int freq_lo_jiffies;
        unsigned int freq_hi_jiffies;
        int cpu;
        unsigned int enable:1,
-                    sample_type:1;
+               sample_type:1;
 };
 static DEFINE_PER_CPU(struct cpu_dbs_info_s, cpu_dbs_info);
 
@@ -203,12 +216,28 @@ static void ondemand_powersave_bias_init(void)
 /************************** sysfs interface ************************/
 static ssize_t show_sampling_rate_max(struct cpufreq_policy *policy, char *buf)
 {
-       return sprintf (buf, "%u\n", MAX_SAMPLING_RATE);
+       static int print_once;
+
+       if (!print_once) {
+               printk(KERN_INFO "CPUFREQ: ondemand sampling_rate_max "
+                      "sysfs file is deprecated - used by: %s\n",
+                      current->comm);
+               print_once = 1;
+       }
+       return sprintf(buf, "%u\n", MAX_SAMPLING_RATE);
 }
 
 static ssize_t show_sampling_rate_min(struct cpufreq_policy *policy, char *buf)
 {
-       return sprintf (buf, "%u\n", MIN_SAMPLING_RATE);
+       static int print_once;
+
+       if (!print_once) {
+               printk(KERN_INFO "CPUFREQ: ondemand sampling_rate_min "
+                      "sysfs file is deprecated - used by: %s\n",
+                      current->comm);
+               print_once = 1;
+       }
+       return sprintf(buf, "%u\n", MIN_SAMPLING_RATE);
 }
 
 #define define_one_ro(_name)           \
@@ -238,13 +267,11 @@ static ssize_t store_sampling_rate(struct cpufreq_policy *unused,
        ret = sscanf(buf, "%u", &input);
 
        mutex_lock(&dbs_mutex);
-       if (ret != 1 || input > MAX_SAMPLING_RATE
-                    || input < MIN_SAMPLING_RATE) {
+       if (ret != 1) {
                mutex_unlock(&dbs_mutex);
                return -EINVAL;
        }
-
-       dbs_tuners_ins.sampling_rate = input;
+       dbs_tuners_ins.sampling_rate = max(input, minimum_sampling_rate());
        mutex_unlock(&dbs_mutex);
 
        return count;
@@ -279,14 +306,14 @@ static ssize_t store_ignore_nice_load(struct cpufreq_policy *policy,
        unsigned int j;
 
        ret = sscanf(buf, "%u", &input);
-       if ( ret != 1 )
+       if (ret != 1)
                return -EINVAL;
 
-       if ( input > 1 )
+       if (input > 1)
                input = 1;
 
        mutex_lock(&dbs_mutex);
-       if ( input == dbs_tuners_ins.ignore_nice ) { /* nothing to do */
+       if (input == dbs_tuners_ins.ignore_nice) { /* nothing to do */
                mutex_unlock(&dbs_mutex);
                return count;
        }
@@ -337,7 +364,7 @@ define_one_rw(up_threshold);
 define_one_rw(ignore_nice_load);
 define_one_rw(powersave_bias);
 
-static struct attribute * dbs_attributes[] = {
+static struct attribute *dbs_attributes[] = {
        &sampling_rate_max.attr,
        &sampling_rate_min.attr,
        &sampling_rate.attr,
@@ -512,8 +539,7 @@ static void do_dbs_timer(struct work_struct *work)
                }
        } else {
                __cpufreq_driver_target(dbs_info->cur_policy,
-                                       dbs_info->freq_lo,
-                                       CPUFREQ_RELATION_H);
+                       dbs_info->freq_lo, CPUFREQ_RELATION_H);
        }
        queue_delayed_work_on(cpu, kondemand_wq, &dbs_info->work, delay);
        unlock_policy_rwsem_write(cpu);
@@ -530,7 +556,7 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info)
        dbs_info->sample_type = DBS_NORMAL_SAMPLE;
        INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer);
        queue_delayed_work_on(dbs_info->cpu, kondemand_wq, &dbs_info->work,
-                             delay);
+               delay);
 }
 
 static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info)
@@ -591,11 +617,9 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
                        if (latency == 0)
                                latency = 1;
 
-                       def_sampling_rate = latency *
-                                       DEF_SAMPLING_RATE_LATENCY_MULTIPLIER;
-
-                       if (def_sampling_rate < MIN_STAT_SAMPLING_RATE)
-                               def_sampling_rate = MIN_STAT_SAMPLING_RATE;
+                       def_sampling_rate =
+                               max(latency * LATENCY_MULTIPLIER,
+                                   MIN_STAT_SAMPLING_RATE);
 
                        dbs_tuners_ins.sampling_rate = def_sampling_rate;
                }
@@ -617,12 +641,10 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
                mutex_lock(&dbs_mutex);
                if (policy->max < this_dbs_info->cur_policy->cur)
                        __cpufreq_driver_target(this_dbs_info->cur_policy,
-                                               policy->max,
-                                               CPUFREQ_RELATION_H);
+                               policy->max, CPUFREQ_RELATION_H);
                else if (policy->min > this_dbs_info->cur_policy->cur)
                        __cpufreq_driver_target(this_dbs_info->cur_policy,
-                                               policy->min,
-                                               CPUFREQ_RELATION_L);
+                               policy->min, CPUFREQ_RELATION_L);
                mutex_unlock(&dbs_mutex);
                break;
        }
@@ -677,7 +699,7 @@ static void __exit cpufreq_gov_dbs_exit(void)
 MODULE_AUTHOR("Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>");
 MODULE_AUTHOR("Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>");
 MODULE_DESCRIPTION("'cpufreq_ondemand' - A dynamic cpufreq governor for "
-                   "Low Latency Frequency Transition capable processors");
+       "Low Latency Frequency Transition capable processors");
 MODULE_LICENSE("GPL");
 
 #ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND
index c0ff97d375d79c83cdd3e52dbf65dc900c94c7a3..5a62d678dd1932f777f17fec0bdbda8d6c9398f9 100644 (file)
@@ -2,7 +2,7 @@
  *  drivers/cpufreq/cpufreq_stats.c
  *
  *  Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
- *           (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
+ *  (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -23,7 +23,7 @@
 
 static spinlock_t cpufreq_stats_lock;
 
-#define CPUFREQ_STATDEVICE_ATTR(_name,_mode,_show) \
+#define CPUFREQ_STATDEVICE_ATTR(_name, _mode, _show) \
 static struct freq_attr _attr_##_name = {\
        .attr = {.name = __stringify(_name), .mode = _mode, }, \
        .show = _show,\
@@ -50,8 +50,7 @@ struct cpufreq_stats_attribute {
        ssize_t(*show) (struct cpufreq_stats *, char *);
 };
 
-static int
-cpufreq_stats_update (unsigned int cpu)
+static int cpufreq_stats_update(unsigned int cpu)
 {
        struct cpufreq_stats *stat;
        unsigned long long cur_time;
@@ -68,8 +67,7 @@ cpufreq_stats_update (unsigned int cpu)
        return 0;
 }
 
-static ssize_t
-show_total_trans(struct cpufreq_policy *policy, char *buf)
+static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf)
 {
        struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
        if (!stat)
@@ -78,8 +76,7 @@ show_total_trans(struct cpufreq_policy *policy, char *buf)
                        per_cpu(cpufreq_stats_table, stat->cpu)->total_trans);
 }
 
-static ssize_t
-show_time_in_state(struct cpufreq_policy *policy, char *buf)
+static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
 {
        ssize_t len = 0;
        int i;
@@ -89,14 +86,14 @@ show_time_in_state(struct cpufreq_policy *policy, char *buf)
        cpufreq_stats_update(stat->cpu);
        for (i = 0; i < stat->state_num; i++) {
                len += sprintf(buf + len, "%u %llu\n", stat->freq_table[i],
-                       (unsigned long long)cputime64_to_clock_t(stat->time_in_state[i]));
+                       (unsigned long long)
+                       cputime64_to_clock_t(stat->time_in_state[i]));
        }
        return len;
 }
 
 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
-static ssize_t
-show_trans_table(struct cpufreq_policy *policy, char *buf)
+static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
 {
        ssize_t len = 0;
        int i, j;
@@ -139,11 +136,11 @@ show_trans_table(struct cpufreq_policy *policy, char *buf)
                return PAGE_SIZE;
        return len;
 }
-CPUFREQ_STATDEVICE_ATTR(trans_table,0444,show_trans_table);
+CPUFREQ_STATDEVICE_ATTR(trans_table, 0444, show_trans_table);
 #endif
 
-CPUFREQ_STATDEVICE_ATTR(total_trans,0444,show_total_trans);
-CPUFREQ_STATDEVICE_ATTR(time_in_state,0444,show_time_in_state);
+CPUFREQ_STATDEVICE_ATTR(total_trans, 0444, show_total_trans);
+CPUFREQ_STATDEVICE_ATTR(time_in_state, 0444, show_time_in_state);
 
 static struct attribute *default_attrs[] = {
        &_attr_total_trans.attr,
@@ -158,8 +155,7 @@ static struct attribute_group stats_attr_group = {
        .name = "stats"
 };
 
-static int
-freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq)
+static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq)
 {
        int index;
        for (index = 0; index < stat->max_state; index++)
@@ -183,8 +179,7 @@ static void cpufreq_stats_free_table(unsigned int cpu)
                cpufreq_cpu_put(policy);
 }
 
-static int
-cpufreq_stats_create_table (struct cpufreq_policy *policy,
+static int cpufreq_stats_create_table(struct cpufreq_policy *policy,
                struct cpufreq_frequency_table *table)
 {
        unsigned int i, j, count = 0, ret = 0;
@@ -194,7 +189,8 @@ cpufreq_stats_create_table (struct cpufreq_policy *policy,
        unsigned int cpu = policy->cpu;
        if (per_cpu(cpufreq_stats_table, cpu))
                return -EBUSY;
-       if ((stat = kzalloc(sizeof(struct cpufreq_stats), GFP_KERNEL)) == NULL)
+       stat = kzalloc(sizeof(struct cpufreq_stats), GFP_KERNEL);
+       if ((stat) == NULL)
                return -ENOMEM;
 
        data = cpufreq_cpu_get(cpu);
@@ -203,13 +199,14 @@ cpufreq_stats_create_table (struct cpufreq_policy *policy,
                goto error_get_fail;
        }
 
-       if ((ret = sysfs_create_group(&data->kobj, &stats_attr_group)))
+       ret = sysfs_create_group(&data->kobj, &stats_attr_group);
+       if (ret)
                goto error_out;
 
        stat->cpu = cpu;
        per_cpu(cpufreq_stats_table, cpu) = stat;
 
-       for (i=0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
+       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
                unsigned int freq = table[i].frequency;
                if (freq == CPUFREQ_ENTRY_INVALID)
                        continue;
@@ -255,9 +252,8 @@ error_get_fail:
        return ret;
 }
 
-static int
-cpufreq_stat_notifier_policy (struct notifier_block *nb, unsigned long val,
-               void *data)
+static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
+               unsigned long val, void *data)
 {
        int ret;
        struct cpufreq_policy *policy = data;
@@ -268,14 +264,14 @@ cpufreq_stat_notifier_policy (struct notifier_block *nb, unsigned long val,
        table = cpufreq_frequency_get_table(cpu);
        if (!table)
                return 0;
-       if ((ret = cpufreq_stats_create_table(policy, table)))
+       ret = cpufreq_stats_create_table(policy, table);
+       if (ret)
                return ret;
        return 0;
 }
 
-static int
-cpufreq_stat_notifier_trans (struct notifier_block *nb, unsigned long val,
-               void *data)
+static int cpufreq_stat_notifier_trans(struct notifier_block *nb,
+               unsigned long val, void *data)
 {
        struct cpufreq_freqs *freq = data;
        struct cpufreq_stats *stat;
@@ -340,19 +336,20 @@ static struct notifier_block notifier_trans_block = {
        .notifier_call = cpufreq_stat_notifier_trans
 };
 
-static int
-__init cpufreq_stats_init(void)
+static int __init cpufreq_stats_init(void)
 {
        int ret;
        unsigned int cpu;
 
        spin_lock_init(&cpufreq_stats_lock);
-       if ((ret = cpufreq_register_notifier(&notifier_policy_block,
-                               CPUFREQ_POLICY_NOTIFIER)))
+       ret = cpufreq_register_notifier(&notifier_policy_block,
+                               CPUFREQ_POLICY_NOTIFIER);
+       if (ret)
                return ret;
 
-       if ((ret = cpufreq_register_notifier(&notifier_trans_block,
-                               CPUFREQ_TRANSITION_NOTIFIER))) {
+       ret = cpufreq_register_notifier(&notifier_trans_block,
+                               CPUFREQ_TRANSITION_NOTIFIER);
+       if (ret) {
                cpufreq_unregister_notifier(&notifier_policy_block,
                                CPUFREQ_POLICY_NOTIFIER);
                return ret;
@@ -364,8 +361,7 @@ __init cpufreq_stats_init(void)
        }
        return 0;
 }
-static void
-__exit cpufreq_stats_exit(void)
+static void __exit cpufreq_stats_exit(void)
 {
        unsigned int cpu;
 
@@ -379,10 +375,10 @@ __exit cpufreq_stats_exit(void)
        }
 }
 
-MODULE_AUTHOR ("Zou Nan hai <nanhai.zou@intel.com>");
-MODULE_DESCRIPTION ("'cpufreq_stats' - A driver to export cpufreq stats "
+MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>");
+MODULE_DESCRIPTION("'cpufreq_stats' - A driver to export cpufreq stats "
                                "through sysfs filesystem");
-MODULE_LICENSE ("GPL");
+MODULE_LICENSE("GPL");
 
 module_init(cpufreq_stats_init);
 module_exit(cpufreq_stats_exit);
index 1442bbada05303bae02adcd3026f57aed4d55a4d..66d2d1d6c80f1dbc0c17388e4dbea81c18c5628e 100644 (file)
@@ -24,9 +24,6 @@
 #include <linux/sysfs.h>
 #include <linux/mutex.h>
 
-#include <asm/uaccess.h>
-
-
 /**
  * A few values needed by the userspace governor
  */
@@ -37,7 +34,7 @@ static DEFINE_PER_CPU(unsigned int, cpu_set_freq); /* CPU freq desired by
                                                        userspace */
 static DEFINE_PER_CPU(unsigned int, cpu_is_managed);
 
-static DEFINE_MUTEX    (userspace_mutex);
+static DEFINE_MUTEX(userspace_mutex);
 static int cpus_using_userspace_governor;
 
 #define dprintk(msg...) \
@@ -46,9 +43,9 @@ static int cpus_using_userspace_governor;
 /* keep track of frequency transitions */
 static int
 userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
-                       void *data)
+       void *data)
 {
-        struct cpufreq_freqs *freq = data;
+       struct cpufreq_freqs *freq = data;
 
        if (!per_cpu(cpu_is_managed, freq->cpu))
                return 0;
@@ -57,11 +54,11 @@ userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
                        freq->cpu, freq->new);
        per_cpu(cpu_cur_freq, freq->cpu) = freq->new;
 
-        return 0;
+       return 0;
 }
 
 static struct notifier_block userspace_cpufreq_notifier_block = {
-        .notifier_call  = userspace_cpufreq_notifier
+       .notifier_call  = userspace_cpufreq_notifier
 };
 
 
@@ -93,8 +90,11 @@ static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq)
         * We're safe from concurrent calls to ->target() here
         * as we hold the userspace_mutex lock. If we were calling
         * cpufreq_driver_target, a deadlock situation might occur:
-        * A: cpufreq_set (lock userspace_mutex) -> cpufreq_driver_target(lock policy->lock)
-        * B: cpufreq_set_policy(lock policy->lock) -> __cpufreq_governor -> cpufreq_governor_userspace (lock userspace_mutex)
+        * A: cpufreq_set (lock userspace_mutex) ->
+        *      cpufreq_driver_target(lock policy->lock)
+        * B: cpufreq_set_policy(lock policy->lock) ->
+        *      __cpufreq_governor ->
+        *         cpufreq_governor_userspace (lock userspace_mutex)
         */
        ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L);
 
@@ -210,9 +210,10 @@ static void __exit cpufreq_gov_userspace_exit(void)
 }
 
 
-MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>, Russell King <rmk@arm.linux.org.uk>");
-MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>, "
+               "Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("CPUfreq policy governor 'userspace'");
+MODULE_LICENSE("GPL");
 
 #ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE
 fs_initcall(cpufreq_gov_userspace_init);
index 9071d80fbba2427d6b29c911832e169bf201466a..a9bd3a05a684548cc7f36aa34fe8a1057d2bfbb3 100644 (file)
@@ -28,7 +28,7 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
        unsigned int max_freq = 0;
        unsigned int i;
 
-       for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
+       for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
                unsigned int freq = table[i].frequency;
                if (freq == CPUFREQ_ENTRY_INVALID) {
                        dprintk("table entry %u is invalid, skipping\n", i);
@@ -70,7 +70,7 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
        cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
                                     policy->cpuinfo.max_freq);
 
-       for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
+       for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
                unsigned int freq = table[i].frequency;
                if (freq == CPUFREQ_ENTRY_INVALID)
                        continue;
@@ -125,13 +125,13 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
        if (!cpu_online(policy->cpu))
                return -EINVAL;
 
-       for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
+       for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
                unsigned int freq = table[i].frequency;
                if (freq == CPUFREQ_ENTRY_INVALID)
                        continue;
                if ((freq < policy->min) || (freq > policy->max))
                        continue;
-               switch(relation) {
+               switch (relation) {
                case CPUFREQ_RELATION_H:
                        if (freq <= target_freq) {
                                if (freq >= optimal.frequency) {
@@ -178,7 +178,7 @@ static DEFINE_PER_CPU(struct cpufreq_frequency_table *, show_table);
 /**
  * show_available_freqs - show available frequencies for the specified CPU
  */
-static ssize_t show_available_freqs (struct cpufreq_policy *policy, char *buf)
+static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf)
 {
        unsigned int i = 0;
        unsigned int cpu = policy->cpu;
@@ -190,7 +190,7 @@ static ssize_t show_available_freqs (struct cpufreq_policy *policy, char *buf)
 
        table = per_cpu(show_table, cpu);
 
-       for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
+       for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
                if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
                        continue;
                count += sprintf(&buf[count], "%d ", table[i].frequency);
@@ -234,6 +234,6 @@ struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu)
 }
 EXPORT_SYMBOL_GPL(cpufreq_frequency_get_table);
 
-MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>");
-MODULE_DESCRIPTION ("CPUfreq frequency table helpers");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
+MODULE_DESCRIPTION("CPUfreq frequency table helpers");
+MODULE_LICENSE("GPL");
index e522144cba3af9bc9d9e5e9bbc1e5dcb05e693c0..01afd758072f8242f550fc70b30b9c124aa8fa2b 100644 (file)
@@ -86,7 +86,7 @@ config ZCRYPT_MONOLITHIC
 config CRYPTO_SHA1_S390
        tristate "SHA1 digest algorithm"
        depends on S390
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          This is the s390 hardware accelerated implementation of the
          SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2).
@@ -94,7 +94,7 @@ config CRYPTO_SHA1_S390
 config CRYPTO_SHA256_S390
        tristate "SHA256 digest algorithm"
        depends on S390
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          This is the s390 hardware accelerated implementation of the
          SHA256 secure hash standard (DFIPS 180-2).
@@ -105,7 +105,7 @@ config CRYPTO_SHA256_S390
 config CRYPTO_SHA512_S390
        tristate "SHA384 and SHA512 digest algorithm"
        depends on S390
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          This is the s390 hardware accelerated implementation of the
          SHA512 secure hash standard.
@@ -200,4 +200,13 @@ config CRYPTO_DEV_IXP4XX
        help
          Driver for the IXP4xx NPE crypto engine.
 
+config CRYPTO_DEV_PPC4XX
+       tristate "Driver AMCC PPC4xx crypto accelerator"
+       depends on PPC && 4xx
+       select CRYPTO_HASH
+       select CRYPTO_ALGAPI
+       select CRYPTO_BLKCIPHER
+       help
+         This option allows you to have support for AMCC crypto acceleration.
+
 endif # CRYPTO_HW
index 73557b2968d33710ae4c0a5745d6ac4bce0dedef..9bf4a2bc88461efdcbaca356a39728d84a9ca420 100644 (file)
@@ -4,3 +4,4 @@ obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o
 obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
 obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o
 obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
+obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += amcc/
diff --git a/drivers/crypto/amcc/Makefile b/drivers/crypto/amcc/Makefile
new file mode 100644 (file)
index 0000000..aa376e8
--- /dev/null
@@ -0,0 +1,2 @@
+obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += crypto4xx.o
+crypto4xx-objs :=  crypto4xx_core.o crypto4xx_alg.o crypto4xx_sa.o
diff --git a/drivers/crypto/amcc/crypto4xx_alg.c b/drivers/crypto/amcc/crypto4xx_alg.c
new file mode 100644 (file)
index 0000000..61b6e1b
--- /dev/null
@@ -0,0 +1,293 @@
+/**
+ * AMCC SoC PPC4xx Crypto Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <jhsiao@amcc.com>
+ *
+ * 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.
+ *
+ * This file implements the Linux crypto algorithms.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock_types.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/hash.h>
+#include <crypto/internal/hash.h>
+#include <linux/dma-mapping.h>
+#include <crypto/algapi.h>
+#include <crypto/aes.h>
+#include <crypto/sha.h>
+#include "crypto4xx_reg_def.h"
+#include "crypto4xx_sa.h"
+#include "crypto4xx_core.h"
+
+void set_dynamic_sa_command_0(struct dynamic_sa_ctl *sa, u32 save_h,
+                             u32 save_iv, u32 ld_h, u32 ld_iv, u32 hdr_proc,
+                             u32 h, u32 c, u32 pad_type, u32 op_grp, u32 op,
+                             u32 dir)
+{
+       sa->sa_command_0.w = 0;
+       sa->sa_command_0.bf.save_hash_state = save_h;
+       sa->sa_command_0.bf.save_iv = save_iv;
+       sa->sa_command_0.bf.load_hash_state = ld_h;
+       sa->sa_command_0.bf.load_iv = ld_iv;
+       sa->sa_command_0.bf.hdr_proc = hdr_proc;
+       sa->sa_command_0.bf.hash_alg = h;
+       sa->sa_command_0.bf.cipher_alg = c;
+       sa->sa_command_0.bf.pad_type = pad_type & 3;
+       sa->sa_command_0.bf.extend_pad = pad_type >> 2;
+       sa->sa_command_0.bf.op_group = op_grp;
+       sa->sa_command_0.bf.opcode = op;
+       sa->sa_command_0.bf.dir = dir;
+}
+
+void set_dynamic_sa_command_1(struct dynamic_sa_ctl *sa, u32 cm, u32 hmac_mc,
+                             u32 cfb, u32 esn, u32 sn_mask, u32 mute,
+                             u32 cp_pad, u32 cp_pay, u32 cp_hdr)
+{
+       sa->sa_command_1.w = 0;
+       sa->sa_command_1.bf.crypto_mode31 = (cm & 4) >> 2;
+       sa->sa_command_1.bf.crypto_mode9_8 = cm & 3;
+       sa->sa_command_1.bf.feedback_mode = cfb,
+       sa->sa_command_1.bf.sa_rev = 1;
+       sa->sa_command_1.bf.extended_seq_num = esn;
+       sa->sa_command_1.bf.seq_num_mask = sn_mask;
+       sa->sa_command_1.bf.mutable_bit_proc = mute;
+       sa->sa_command_1.bf.copy_pad = cp_pad;
+       sa->sa_command_1.bf.copy_payload = cp_pay;
+       sa->sa_command_1.bf.copy_hdr = cp_hdr;
+}
+
+int crypto4xx_encrypt(struct ablkcipher_request *req)
+{
+       struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+
+       ctx->direction = DIR_OUTBOUND;
+       ctx->hash_final = 0;
+       ctx->is_hash = 0;
+       ctx->pd_ctl = 0x1;
+
+       return crypto4xx_build_pd(&req->base, ctx, req->src, req->dst,
+                                 req->nbytes, req->info,
+                                 get_dynamic_sa_iv_size(ctx));
+}
+
+int crypto4xx_decrypt(struct ablkcipher_request *req)
+{
+       struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+
+       ctx->direction = DIR_INBOUND;
+       ctx->hash_final = 0;
+       ctx->is_hash = 0;
+       ctx->pd_ctl = 1;
+
+       return crypto4xx_build_pd(&req->base, ctx, req->src, req->dst,
+                                 req->nbytes, req->info,
+                                 get_dynamic_sa_iv_size(ctx));
+}
+
+/**
+ * AES Functions
+ */
+static int crypto4xx_setkey_aes(struct crypto_ablkcipher *cipher,
+                               const u8 *key,
+                               unsigned int keylen,
+                               unsigned char cm,
+                               u8 fb)
+{
+       struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+       struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct dynamic_sa_ctl *sa;
+       int    rc;
+
+       if (keylen != AES_KEYSIZE_256 &&
+               keylen != AES_KEYSIZE_192 && keylen != AES_KEYSIZE_128) {
+               crypto_ablkcipher_set_flags(cipher,
+                               CRYPTO_TFM_RES_BAD_KEY_LEN);
+               return -EINVAL;
+       }
+
+       /* Create SA */
+       if (ctx->sa_in_dma_addr || ctx->sa_out_dma_addr)
+               crypto4xx_free_sa(ctx);
+
+       rc = crypto4xx_alloc_sa(ctx, SA_AES128_LEN + (keylen-16) / 4);
+       if (rc)
+               return rc;
+
+       if (ctx->state_record_dma_addr == 0) {
+               rc = crypto4xx_alloc_state_record(ctx);
+               if (rc) {
+                       crypto4xx_free_sa(ctx);
+                       return rc;
+               }
+       }
+       /* Setup SA */
+       sa = (struct dynamic_sa_ctl *) ctx->sa_in;
+       ctx->hash_final = 0;
+
+       set_dynamic_sa_command_0(sa, SA_NOT_SAVE_HASH, SA_NOT_SAVE_IV,
+                                SA_LOAD_HASH_FROM_SA, SA_LOAD_IV_FROM_STATE,
+                                SA_NO_HEADER_PROC, SA_HASH_ALG_NULL,
+                                SA_CIPHER_ALG_AES, SA_PAD_TYPE_ZERO,
+                                SA_OP_GROUP_BASIC, SA_OPCODE_DECRYPT,
+                                DIR_INBOUND);
+
+       set_dynamic_sa_command_1(sa, cm, SA_HASH_MODE_HASH,
+                                fb, SA_EXTENDED_SN_OFF,
+                                SA_SEQ_MASK_OFF, SA_MC_ENABLE,
+                                SA_NOT_COPY_PAD, SA_NOT_COPY_PAYLOAD,
+                                SA_NOT_COPY_HDR);
+       crypto4xx_memcpy_le(ctx->sa_in + get_dynamic_sa_offset_key_field(ctx),
+                           key, keylen);
+       sa->sa_contents = SA_AES_CONTENTS | (keylen << 2);
+       sa->sa_command_1.bf.key_len = keylen >> 3;
+       ctx->is_hash = 0;
+       ctx->direction = DIR_INBOUND;
+       memcpy(ctx->sa_in + get_dynamic_sa_offset_state_ptr_field(ctx),
+                       (void *)&ctx->state_record_dma_addr, 4);
+       ctx->offset_to_sr_ptr = get_dynamic_sa_offset_state_ptr_field(ctx);
+
+       memcpy(ctx->sa_out, ctx->sa_in, ctx->sa_len * 4);
+       sa = (struct dynamic_sa_ctl *) ctx->sa_out;
+       sa->sa_command_0.bf.dir = DIR_OUTBOUND;
+
+       return 0;
+}
+
+int crypto4xx_setkey_aes_cbc(struct crypto_ablkcipher *cipher,
+                            const u8 *key, unsigned int keylen)
+{
+       return crypto4xx_setkey_aes(cipher, key, keylen, CRYPTO_MODE_CBC,
+                                   CRYPTO_FEEDBACK_MODE_NO_FB);
+}
+
+/**
+ * HASH SHA1 Functions
+ */
+static int crypto4xx_hash_alg_init(struct crypto_tfm *tfm,
+                                  unsigned int sa_len,
+                                  unsigned char ha,
+                                  unsigned char hm)
+{
+       struct crypto_alg *alg = tfm->__crt_alg;
+       struct crypto4xx_alg *my_alg = crypto_alg_to_crypto4xx_alg(alg);
+       struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct dynamic_sa_ctl *sa;
+       struct dynamic_sa_hash160 *sa_in;
+       int rc;
+
+       ctx->dev   = my_alg->dev;
+       ctx->is_hash = 1;
+       ctx->hash_final = 0;
+
+       /* Create SA */
+       if (ctx->sa_in_dma_addr || ctx->sa_out_dma_addr)
+               crypto4xx_free_sa(ctx);
+
+       rc = crypto4xx_alloc_sa(ctx, sa_len);
+       if (rc)
+               return rc;
+
+       if (ctx->state_record_dma_addr == 0) {
+               crypto4xx_alloc_state_record(ctx);
+               if (!ctx->state_record_dma_addr) {
+                       crypto4xx_free_sa(ctx);
+                       return -ENOMEM;
+               }
+       }
+
+       tfm->crt_ahash.reqsize = sizeof(struct crypto4xx_ctx);
+       sa = (struct dynamic_sa_ctl *) ctx->sa_in;
+       set_dynamic_sa_command_0(sa, SA_SAVE_HASH, SA_NOT_SAVE_IV,
+                                SA_NOT_LOAD_HASH, SA_LOAD_IV_FROM_SA,
+                                SA_NO_HEADER_PROC, ha, SA_CIPHER_ALG_NULL,
+                                SA_PAD_TYPE_ZERO, SA_OP_GROUP_BASIC,
+                                SA_OPCODE_HASH, DIR_INBOUND);
+       set_dynamic_sa_command_1(sa, 0, SA_HASH_MODE_HASH,
+                                CRYPTO_FEEDBACK_MODE_NO_FB, SA_EXTENDED_SN_OFF,
+                                SA_SEQ_MASK_OFF, SA_MC_ENABLE,
+                                SA_NOT_COPY_PAD, SA_NOT_COPY_PAYLOAD,
+                                SA_NOT_COPY_HDR);
+       ctx->direction = DIR_INBOUND;
+       sa->sa_contents = SA_HASH160_CONTENTS;
+       sa_in = (struct dynamic_sa_hash160 *) ctx->sa_in;
+       /* Need to zero hash digest in SA */
+       memset(sa_in->inner_digest, 0, sizeof(sa_in->inner_digest));
+       memset(sa_in->outer_digest, 0, sizeof(sa_in->outer_digest));
+       sa_in->state_ptr = ctx->state_record_dma_addr;
+       ctx->offset_to_sr_ptr = get_dynamic_sa_offset_state_ptr_field(ctx);
+
+       return 0;
+}
+
+int crypto4xx_hash_init(struct ahash_request *req)
+{
+       struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+       int ds;
+       struct dynamic_sa_ctl *sa;
+
+       sa = (struct dynamic_sa_ctl *) ctx->sa_in;
+       ds = crypto_ahash_digestsize(
+                       __crypto_ahash_cast(req->base.tfm));
+       sa->sa_command_0.bf.digest_len = ds >> 2;
+       sa->sa_command_0.bf.load_hash_state = SA_LOAD_HASH_FROM_SA;
+       ctx->is_hash = 1;
+       ctx->direction = DIR_INBOUND;
+
+       return 0;
+}
+
+int crypto4xx_hash_update(struct ahash_request *req)
+{
+       struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+
+       ctx->is_hash = 1;
+       ctx->hash_final = 0;
+       ctx->pd_ctl = 0x11;
+       ctx->direction = DIR_INBOUND;
+
+       return crypto4xx_build_pd(&req->base, ctx, req->src,
+                                 (struct scatterlist *) req->result,
+                                 req->nbytes, NULL, 0);
+}
+
+int crypto4xx_hash_final(struct ahash_request *req)
+{
+       return 0;
+}
+
+int crypto4xx_hash_digest(struct ahash_request *req)
+{
+       struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+
+       ctx->hash_final = 1;
+       ctx->pd_ctl = 0x11;
+       ctx->direction = DIR_INBOUND;
+
+       return crypto4xx_build_pd(&req->base, ctx, req->src,
+                                 (struct scatterlist *) req->result,
+                                 req->nbytes, NULL, 0);
+}
+
+/**
+ * SHA1 Algorithm
+ */
+int crypto4xx_sha1_alg_init(struct crypto_tfm *tfm)
+{
+       return crypto4xx_hash_alg_init(tfm, SA_HASH160_LEN, SA_HASH_ALG_SHA1,
+                                      SA_HASH_MODE_HASH);
+}
+
+
diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c
new file mode 100644 (file)
index 0000000..4c0dfb2
--- /dev/null
@@ -0,0 +1,1310 @@
+/**
+ * AMCC SoC PPC4xx Crypto Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <jhsiao@amcc.com>
+ *
+ * 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.
+ *
+ * This file implements AMCC crypto offload Linux device driver for use with
+ * Linux CryptoAPI.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock_types.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/of_platform.h>
+#include <asm/dcr.h>
+#include <asm/dcr-regs.h>
+#include <asm/cacheflush.h>
+#include <crypto/internal/hash.h>
+#include <crypto/algapi.h>
+#include <crypto/aes.h>
+#include <crypto/sha.h>
+#include "crypto4xx_reg_def.h"
+#include "crypto4xx_core.h"
+#include "crypto4xx_sa.h"
+
+#define PPC4XX_SEC_VERSION_STR                 "0.5"
+
+/**
+ * PPC4xx Crypto Engine Initialization Routine
+ */
+static void crypto4xx_hw_init(struct crypto4xx_device *dev)
+{
+       union ce_ring_size ring_size;
+       union ce_ring_contol ring_ctrl;
+       union ce_part_ring_size part_ring_size;
+       union ce_io_threshold io_threshold;
+       u32 rand_num;
+       union ce_pe_dma_cfg pe_dma_cfg;
+
+       writel(PPC4XX_BYTE_ORDER, dev->ce_base + CRYPTO4XX_BYTE_ORDER_CFG);
+       /* setup pe dma, include reset sg, pdr and pe, then release reset */
+       pe_dma_cfg.w = 0;
+       pe_dma_cfg.bf.bo_sgpd_en = 1;
+       pe_dma_cfg.bf.bo_data_en = 0;
+       pe_dma_cfg.bf.bo_sa_en = 1;
+       pe_dma_cfg.bf.bo_pd_en = 1;
+       pe_dma_cfg.bf.dynamic_sa_en = 1;
+       pe_dma_cfg.bf.reset_sg = 1;
+       pe_dma_cfg.bf.reset_pdr = 1;
+       pe_dma_cfg.bf.reset_pe = 1;
+       writel(pe_dma_cfg.w, dev->ce_base + CRYPTO4XX_PE_DMA_CFG);
+       /* un reset pe,sg and pdr */
+       pe_dma_cfg.bf.pe_mode = 0;
+       pe_dma_cfg.bf.reset_sg = 0;
+       pe_dma_cfg.bf.reset_pdr = 0;
+       pe_dma_cfg.bf.reset_pe = 0;
+       pe_dma_cfg.bf.bo_td_en = 0;
+       writel(pe_dma_cfg.w, dev->ce_base + CRYPTO4XX_PE_DMA_CFG);
+       writel(dev->pdr_pa, dev->ce_base + CRYPTO4XX_PDR_BASE);
+       writel(dev->pdr_pa, dev->ce_base + CRYPTO4XX_RDR_BASE);
+       writel(PPC4XX_PRNG_CTRL_AUTO_EN, dev->ce_base + CRYPTO4XX_PRNG_CTRL);
+       get_random_bytes(&rand_num, sizeof(rand_num));
+       writel(rand_num, dev->ce_base + CRYPTO4XX_PRNG_SEED_L);
+       get_random_bytes(&rand_num, sizeof(rand_num));
+       writel(rand_num, dev->ce_base + CRYPTO4XX_PRNG_SEED_H);
+       ring_size.w = 0;
+       ring_size.bf.ring_offset = PPC4XX_PD_SIZE;
+       ring_size.bf.ring_size   = PPC4XX_NUM_PD;
+       writel(ring_size.w, dev->ce_base + CRYPTO4XX_RING_SIZE);
+       ring_ctrl.w = 0;
+       writel(ring_ctrl.w, dev->ce_base + CRYPTO4XX_RING_CTRL);
+       writel(PPC4XX_DC_3DES_EN, dev->ce_base + CRYPTO4XX_DEVICE_CTRL);
+       writel(dev->gdr_pa, dev->ce_base + CRYPTO4XX_GATH_RING_BASE);
+       writel(dev->sdr_pa, dev->ce_base + CRYPTO4XX_SCAT_RING_BASE);
+       part_ring_size.w = 0;
+       part_ring_size.bf.sdr_size = PPC4XX_SDR_SIZE;
+       part_ring_size.bf.gdr_size = PPC4XX_GDR_SIZE;
+       writel(part_ring_size.w, dev->ce_base + CRYPTO4XX_PART_RING_SIZE);
+       writel(PPC4XX_SD_BUFFER_SIZE, dev->ce_base + CRYPTO4XX_PART_RING_CFG);
+       io_threshold.w = 0;
+       io_threshold.bf.output_threshold = PPC4XX_OUTPUT_THRESHOLD;
+       io_threshold.bf.input_threshold  = PPC4XX_INPUT_THRESHOLD;
+       writel(io_threshold.w, dev->ce_base + CRYPTO4XX_IO_THRESHOLD);
+       writel(0, dev->ce_base + CRYPTO4XX_PDR_BASE_UADDR);
+       writel(0, dev->ce_base + CRYPTO4XX_RDR_BASE_UADDR);
+       writel(0, dev->ce_base + CRYPTO4XX_PKT_SRC_UADDR);
+       writel(0, dev->ce_base + CRYPTO4XX_PKT_DEST_UADDR);
+       writel(0, dev->ce_base + CRYPTO4XX_SA_UADDR);
+       writel(0, dev->ce_base + CRYPTO4XX_GATH_RING_BASE_UADDR);
+       writel(0, dev->ce_base + CRYPTO4XX_SCAT_RING_BASE_UADDR);
+       /* un reset pe,sg and pdr */
+       pe_dma_cfg.bf.pe_mode = 1;
+       pe_dma_cfg.bf.reset_sg = 0;
+       pe_dma_cfg.bf.reset_pdr = 0;
+       pe_dma_cfg.bf.reset_pe = 0;
+       pe_dma_cfg.bf.bo_td_en = 0;
+       writel(pe_dma_cfg.w, dev->ce_base + CRYPTO4XX_PE_DMA_CFG);
+       /*clear all pending interrupt*/
+       writel(PPC4XX_INTERRUPT_CLR, dev->ce_base + CRYPTO4XX_INT_CLR);
+       writel(PPC4XX_INT_DESCR_CNT, dev->ce_base + CRYPTO4XX_INT_DESCR_CNT);
+       writel(PPC4XX_INT_DESCR_CNT, dev->ce_base + CRYPTO4XX_INT_DESCR_CNT);
+       writel(PPC4XX_INT_CFG, dev->ce_base + CRYPTO4XX_INT_CFG);
+       writel(PPC4XX_PD_DONE_INT, dev->ce_base + CRYPTO4XX_INT_EN);
+}
+
+int crypto4xx_alloc_sa(struct crypto4xx_ctx *ctx, u32 size)
+{
+       ctx->sa_in = dma_alloc_coherent(ctx->dev->core_dev->device, size * 4,
+                                       &ctx->sa_in_dma_addr, GFP_ATOMIC);
+       if (ctx->sa_in == NULL)
+               return -ENOMEM;
+
+       ctx->sa_out = dma_alloc_coherent(ctx->dev->core_dev->device, size * 4,
+                                        &ctx->sa_out_dma_addr, GFP_ATOMIC);
+       if (ctx->sa_out == NULL) {
+               dma_free_coherent(ctx->dev->core_dev->device,
+                                 ctx->sa_len * 4,
+                                 ctx->sa_in, ctx->sa_in_dma_addr);
+               return -ENOMEM;
+       }
+
+       memset(ctx->sa_in, 0, size * 4);
+       memset(ctx->sa_out, 0, size * 4);
+       ctx->sa_len = size;
+
+       return 0;
+}
+
+void crypto4xx_free_sa(struct crypto4xx_ctx *ctx)
+{
+       if (ctx->sa_in != NULL)
+               dma_free_coherent(ctx->dev->core_dev->device, ctx->sa_len * 4,
+                                 ctx->sa_in, ctx->sa_in_dma_addr);
+       if (ctx->sa_out != NULL)
+               dma_free_coherent(ctx->dev->core_dev->device, ctx->sa_len * 4,
+                                 ctx->sa_out, ctx->sa_out_dma_addr);
+
+       ctx->sa_in_dma_addr = 0;
+       ctx->sa_out_dma_addr = 0;
+       ctx->sa_len = 0;
+}
+
+u32 crypto4xx_alloc_state_record(struct crypto4xx_ctx *ctx)
+{
+       ctx->state_record = dma_alloc_coherent(ctx->dev->core_dev->device,
+                               sizeof(struct sa_state_record),
+                               &ctx->state_record_dma_addr, GFP_ATOMIC);
+       if (!ctx->state_record_dma_addr)
+               return -ENOMEM;
+       memset(ctx->state_record, 0, sizeof(struct sa_state_record));
+
+       return 0;
+}
+
+void crypto4xx_free_state_record(struct crypto4xx_ctx *ctx)
+{
+       if (ctx->state_record != NULL)
+               dma_free_coherent(ctx->dev->core_dev->device,
+                                 sizeof(struct sa_state_record),
+                                 ctx->state_record,
+                                 ctx->state_record_dma_addr);
+       ctx->state_record_dma_addr = 0;
+}
+
+/**
+ * alloc memory for the gather ring
+ * no need to alloc buf for the ring
+ * gdr_tail, gdr_head and gdr_count are initialized by this function
+ */
+static u32 crypto4xx_build_pdr(struct crypto4xx_device *dev)
+{
+       int i;
+       struct pd_uinfo *pd_uinfo;
+       dev->pdr = dma_alloc_coherent(dev->core_dev->device,
+                                     sizeof(struct ce_pd) * PPC4XX_NUM_PD,
+                                     &dev->pdr_pa, GFP_ATOMIC);
+       if (!dev->pdr)
+               return -ENOMEM;
+
+       dev->pdr_uinfo = kzalloc(sizeof(struct pd_uinfo) * PPC4XX_NUM_PD,
+                               GFP_KERNEL);
+       if (!dev->pdr_uinfo) {
+               dma_free_coherent(dev->core_dev->device,
+                                 sizeof(struct ce_pd) * PPC4XX_NUM_PD,
+                                 dev->pdr,
+                                 dev->pdr_pa);
+               return -ENOMEM;
+       }
+       memset(dev->pdr, 0,  sizeof(struct ce_pd) * PPC4XX_NUM_PD);
+       dev->shadow_sa_pool = dma_alloc_coherent(dev->core_dev->device,
+                                  256 * PPC4XX_NUM_PD,
+                                  &dev->shadow_sa_pool_pa,
+                                  GFP_ATOMIC);
+       if (!dev->shadow_sa_pool)
+               return -ENOMEM;
+
+       dev->shadow_sr_pool = dma_alloc_coherent(dev->core_dev->device,
+                        sizeof(struct sa_state_record) * PPC4XX_NUM_PD,
+                        &dev->shadow_sr_pool_pa, GFP_ATOMIC);
+       if (!dev->shadow_sr_pool)
+               return -ENOMEM;
+       for (i = 0; i < PPC4XX_NUM_PD; i++) {
+               pd_uinfo = (struct pd_uinfo *) (dev->pdr_uinfo +
+                                               sizeof(struct pd_uinfo) * i);
+
+               /* alloc 256 bytes which is enough for any kind of dynamic sa */
+               pd_uinfo->sa_va = dev->shadow_sa_pool + 256 * i;
+               pd_uinfo->sa_pa = dev->shadow_sa_pool_pa + 256 * i;
+
+               /* alloc state record */
+               pd_uinfo->sr_va = dev->shadow_sr_pool +
+                   sizeof(struct sa_state_record) * i;
+               pd_uinfo->sr_pa = dev->shadow_sr_pool_pa +
+                   sizeof(struct sa_state_record) * i;
+       }
+
+       return 0;
+}
+
+static void crypto4xx_destroy_pdr(struct crypto4xx_device *dev)
+{
+       if (dev->pdr != NULL)
+               dma_free_coherent(dev->core_dev->device,
+                                 sizeof(struct ce_pd) * PPC4XX_NUM_PD,
+                                 dev->pdr, dev->pdr_pa);
+       if (dev->shadow_sa_pool)
+               dma_free_coherent(dev->core_dev->device, 256 * PPC4XX_NUM_PD,
+                                 dev->shadow_sa_pool, dev->shadow_sa_pool_pa);
+       if (dev->shadow_sr_pool)
+               dma_free_coherent(dev->core_dev->device,
+                       sizeof(struct sa_state_record) * PPC4XX_NUM_PD,
+                       dev->shadow_sr_pool, dev->shadow_sr_pool_pa);
+
+       kfree(dev->pdr_uinfo);
+}
+
+static u32 crypto4xx_get_pd_from_pdr_nolock(struct crypto4xx_device *dev)
+{
+       u32 retval;
+       u32 tmp;
+
+       retval = dev->pdr_head;
+       tmp = (dev->pdr_head + 1) % PPC4XX_NUM_PD;
+
+       if (tmp == dev->pdr_tail)
+               return ERING_WAS_FULL;
+
+       dev->pdr_head = tmp;
+
+       return retval;
+}
+
+static u32 crypto4xx_put_pd_to_pdr(struct crypto4xx_device *dev, u32 idx)
+{
+       struct pd_uinfo *pd_uinfo;
+       unsigned long flags;
+
+       pd_uinfo = (struct pd_uinfo *)(dev->pdr_uinfo +
+                                      sizeof(struct pd_uinfo) * idx);
+       spin_lock_irqsave(&dev->core_dev->lock, flags);
+       if (dev->pdr_tail != PPC4XX_LAST_PD)
+               dev->pdr_tail++;
+       else
+               dev->pdr_tail = 0;
+       pd_uinfo->state = PD_ENTRY_FREE;
+       spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+
+       return 0;
+}
+
+static struct ce_pd *crypto4xx_get_pdp(struct crypto4xx_device *dev,
+                                      dma_addr_t *pd_dma, u32 idx)
+{
+       *pd_dma = dev->pdr_pa + sizeof(struct ce_pd) * idx;
+
+       return dev->pdr + sizeof(struct ce_pd) * idx;
+}
+
+/**
+ * alloc memory for the gather ring
+ * no need to alloc buf for the ring
+ * gdr_tail, gdr_head and gdr_count are initialized by this function
+ */
+static u32 crypto4xx_build_gdr(struct crypto4xx_device *dev)
+{
+       dev->gdr = dma_alloc_coherent(dev->core_dev->device,
+                                     sizeof(struct ce_gd) * PPC4XX_NUM_GD,
+                                     &dev->gdr_pa, GFP_ATOMIC);
+       if (!dev->gdr)
+               return -ENOMEM;
+
+       memset(dev->gdr, 0, sizeof(struct ce_gd) * PPC4XX_NUM_GD);
+
+       return 0;
+}
+
+static inline void crypto4xx_destroy_gdr(struct crypto4xx_device *dev)
+{
+       dma_free_coherent(dev->core_dev->device,
+                         sizeof(struct ce_gd) * PPC4XX_NUM_GD,
+                         dev->gdr, dev->gdr_pa);
+}
+
+/*
+ * when this function is called.
+ * preemption or interrupt must be disabled
+ */
+u32 crypto4xx_get_n_gd(struct crypto4xx_device *dev, int n)
+{
+       u32 retval;
+       u32 tmp;
+       if (n >= PPC4XX_NUM_GD)
+               return ERING_WAS_FULL;
+
+       retval = dev->gdr_head;
+       tmp = (dev->gdr_head + n) % PPC4XX_NUM_GD;
+       if (dev->gdr_head > dev->gdr_tail) {
+               if (tmp < dev->gdr_head && tmp >= dev->gdr_tail)
+                       return ERING_WAS_FULL;
+       } else if (dev->gdr_head < dev->gdr_tail) {
+               if (tmp < dev->gdr_head || tmp >= dev->gdr_tail)
+                       return ERING_WAS_FULL;
+       }
+       dev->gdr_head = tmp;
+
+       return retval;
+}
+
+static u32 crypto4xx_put_gd_to_gdr(struct crypto4xx_device *dev)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->core_dev->lock, flags);
+       if (dev->gdr_tail == dev->gdr_head) {
+               spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+               return 0;
+       }
+
+       if (dev->gdr_tail != PPC4XX_LAST_GD)
+               dev->gdr_tail++;
+       else
+               dev->gdr_tail = 0;
+
+       spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+
+       return 0;
+}
+
+static inline struct ce_gd *crypto4xx_get_gdp(struct crypto4xx_device *dev,
+                                             dma_addr_t *gd_dma, u32 idx)
+{
+       *gd_dma = dev->gdr_pa + sizeof(struct ce_gd) * idx;
+
+       return (struct ce_gd *) (dev->gdr + sizeof(struct ce_gd) * idx);
+}
+
+/**
+ * alloc memory for the scatter ring
+ * need to alloc buf for the ring
+ * sdr_tail, sdr_head and sdr_count are initialized by this function
+ */
+static u32 crypto4xx_build_sdr(struct crypto4xx_device *dev)
+{
+       int i;
+       struct ce_sd *sd_array;
+
+       /* alloc memory for scatter descriptor ring */
+       dev->sdr = dma_alloc_coherent(dev->core_dev->device,
+                                     sizeof(struct ce_sd) * PPC4XX_NUM_SD,
+                                     &dev->sdr_pa, GFP_ATOMIC);
+       if (!dev->sdr)
+               return -ENOMEM;
+
+       dev->scatter_buffer_size = PPC4XX_SD_BUFFER_SIZE;
+       dev->scatter_buffer_va =
+               dma_alloc_coherent(dev->core_dev->device,
+                       dev->scatter_buffer_size * PPC4XX_NUM_SD,
+                       &dev->scatter_buffer_pa, GFP_ATOMIC);
+       if (!dev->scatter_buffer_va) {
+               dma_free_coherent(dev->core_dev->device,
+                                 sizeof(struct ce_sd) * PPC4XX_NUM_SD,
+                                 dev->sdr, dev->sdr_pa);
+               return -ENOMEM;
+       }
+
+       sd_array = dev->sdr;
+
+       for (i = 0; i < PPC4XX_NUM_SD; i++) {
+               sd_array[i].ptr = dev->scatter_buffer_pa +
+                                 dev->scatter_buffer_size * i;
+       }
+
+       return 0;
+}
+
+static void crypto4xx_destroy_sdr(struct crypto4xx_device *dev)
+{
+       if (dev->sdr != NULL)
+               dma_free_coherent(dev->core_dev->device,
+                                 sizeof(struct ce_sd) * PPC4XX_NUM_SD,
+                                 dev->sdr, dev->sdr_pa);
+
+       if (dev->scatter_buffer_va != NULL)
+               dma_free_coherent(dev->core_dev->device,
+                                 dev->scatter_buffer_size * PPC4XX_NUM_SD,
+                                 dev->scatter_buffer_va,
+                                 dev->scatter_buffer_pa);
+}
+
+/*
+ * when this function is called.
+ * preemption or interrupt must be disabled
+ */
+static u32 crypto4xx_get_n_sd(struct crypto4xx_device *dev, int n)
+{
+       u32 retval;
+       u32 tmp;
+
+       if (n >= PPC4XX_NUM_SD)
+               return ERING_WAS_FULL;
+
+       retval = dev->sdr_head;
+       tmp = (dev->sdr_head + n) % PPC4XX_NUM_SD;
+       if (dev->sdr_head > dev->gdr_tail) {
+               if (tmp < dev->sdr_head && tmp >= dev->sdr_tail)
+                       return ERING_WAS_FULL;
+       } else if (dev->sdr_head < dev->sdr_tail) {
+               if (tmp < dev->sdr_head || tmp >= dev->sdr_tail)
+                       return ERING_WAS_FULL;
+       } /* the head = tail, or empty case is already take cared */
+       dev->sdr_head = tmp;
+
+       return retval;
+}
+
+static u32 crypto4xx_put_sd_to_sdr(struct crypto4xx_device *dev)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->core_dev->lock, flags);
+       if (dev->sdr_tail == dev->sdr_head) {
+               spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+               return 0;
+       }
+       if (dev->sdr_tail != PPC4XX_LAST_SD)
+               dev->sdr_tail++;
+       else
+               dev->sdr_tail = 0;
+       spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+
+       return 0;
+}
+
+static inline struct ce_sd *crypto4xx_get_sdp(struct crypto4xx_device *dev,
+                                             dma_addr_t *sd_dma, u32 idx)
+{
+       *sd_dma = dev->sdr_pa + sizeof(struct ce_sd) * idx;
+
+       return  (struct ce_sd *)(dev->sdr + sizeof(struct ce_sd) * idx);
+}
+
+static u32 crypto4xx_fill_one_page(struct crypto4xx_device *dev,
+                                  dma_addr_t *addr, u32 *length,
+                                  u32 *idx, u32 *offset, u32 *nbytes)
+{
+       u32 len;
+
+       if (*length > dev->scatter_buffer_size) {
+               memcpy(phys_to_virt(*addr),
+                       dev->scatter_buffer_va +
+                       *idx * dev->scatter_buffer_size + *offset,
+                       dev->scatter_buffer_size);
+               *offset = 0;
+               *length -= dev->scatter_buffer_size;
+               *nbytes -= dev->scatter_buffer_size;
+               if (*idx == PPC4XX_LAST_SD)
+                       *idx = 0;
+               else
+                       (*idx)++;
+               *addr = *addr +  dev->scatter_buffer_size;
+               return 1;
+       } else if (*length < dev->scatter_buffer_size) {
+               memcpy(phys_to_virt(*addr),
+                       dev->scatter_buffer_va +
+                       *idx * dev->scatter_buffer_size + *offset, *length);
+               if ((*offset + *length) == dev->scatter_buffer_size) {
+                       if (*idx == PPC4XX_LAST_SD)
+                               *idx = 0;
+                       else
+                               (*idx)++;
+                       *nbytes -= *length;
+                       *offset = 0;
+               } else {
+                       *nbytes -= *length;
+                       *offset += *length;
+               }
+
+               return 0;
+       } else {
+               len = (*nbytes <= dev->scatter_buffer_size) ?
+                               (*nbytes) : dev->scatter_buffer_size;
+               memcpy(phys_to_virt(*addr),
+                       dev->scatter_buffer_va +
+                       *idx * dev->scatter_buffer_size + *offset,
+                       len);
+               *offset = 0;
+               *nbytes -= len;
+
+               if (*idx == PPC4XX_LAST_SD)
+                       *idx = 0;
+               else
+                       (*idx)++;
+
+               return 0;
+    }
+}
+
+static void crypto4xx_copy_pkt_to_dst(struct crypto4xx_device *dev,
+                                     struct ce_pd *pd,
+                                     struct pd_uinfo *pd_uinfo,
+                                     u32 nbytes,
+                                     struct scatterlist *dst)
+{
+       dma_addr_t addr;
+       u32 this_sd;
+       u32 offset;
+       u32 len;
+       u32 i;
+       u32 sg_len;
+       struct scatterlist *sg;
+
+       this_sd = pd_uinfo->first_sd;
+       offset = 0;
+       i = 0;
+
+       while (nbytes) {
+               sg = &dst[i];
+               sg_len = sg->length;
+               addr = dma_map_page(dev->core_dev->device, sg_page(sg),
+                               sg->offset, sg->length, DMA_TO_DEVICE);
+
+               if (offset == 0) {
+                       len = (nbytes <= sg->length) ? nbytes : sg->length;
+                       while (crypto4xx_fill_one_page(dev, &addr, &len,
+                               &this_sd, &offset, &nbytes))
+                               ;
+                       if (!nbytes)
+                               return;
+                       i++;
+               } else {
+                       len = (nbytes <= (dev->scatter_buffer_size - offset)) ?
+                               nbytes : (dev->scatter_buffer_size - offset);
+                       len = (sg->length < len) ? sg->length : len;
+                       while (crypto4xx_fill_one_page(dev, &addr, &len,
+                                              &this_sd, &offset, &nbytes))
+                               ;
+                       if (!nbytes)
+                               return;
+                       sg_len -= len;
+                       if (sg_len) {
+                               addr += len;
+                               while (crypto4xx_fill_one_page(dev, &addr,
+                                       &sg_len, &this_sd, &offset, &nbytes))
+                                       ;
+                       }
+                       i++;
+               }
+       }
+}
+
+static u32 crypto4xx_copy_digest_to_dst(struct pd_uinfo *pd_uinfo,
+                                       struct crypto4xx_ctx *ctx)
+{
+       struct dynamic_sa_ctl *sa = (struct dynamic_sa_ctl *) ctx->sa_in;
+       struct sa_state_record *state_record =
+                               (struct sa_state_record *) pd_uinfo->sr_va;
+
+       if (sa->sa_command_0.bf.hash_alg == SA_HASH_ALG_SHA1) {
+               memcpy((void *) pd_uinfo->dest_va, state_record->save_digest,
+                      SA_HASH_ALG_SHA1_DIGEST_SIZE);
+       }
+
+       return 0;
+}
+
+static void crypto4xx_ret_sg_desc(struct crypto4xx_device *dev,
+                                 struct pd_uinfo *pd_uinfo)
+{
+       int i;
+       if (pd_uinfo->num_gd) {
+               for (i = 0; i < pd_uinfo->num_gd; i++)
+                       crypto4xx_put_gd_to_gdr(dev);
+               pd_uinfo->first_gd = 0xffffffff;
+               pd_uinfo->num_gd = 0;
+       }
+       if (pd_uinfo->num_sd) {
+               for (i = 0; i < pd_uinfo->num_sd; i++)
+                       crypto4xx_put_sd_to_sdr(dev);
+
+               pd_uinfo->first_sd = 0xffffffff;
+               pd_uinfo->num_sd = 0;
+       }
+}
+
+static u32 crypto4xx_ablkcipher_done(struct crypto4xx_device *dev,
+                                    struct pd_uinfo *pd_uinfo,
+                                    struct ce_pd *pd)
+{
+       struct crypto4xx_ctx *ctx;
+       struct ablkcipher_request *ablk_req;
+       struct scatterlist *dst;
+       dma_addr_t addr;
+
+       ablk_req = ablkcipher_request_cast(pd_uinfo->async_req);
+       ctx  = crypto_tfm_ctx(ablk_req->base.tfm);
+
+       if (pd_uinfo->using_sd) {
+               crypto4xx_copy_pkt_to_dst(dev, pd, pd_uinfo, ablk_req->nbytes,
+                                         ablk_req->dst);
+       } else {
+               dst = pd_uinfo->dest_va;
+               addr = dma_map_page(dev->core_dev->device, sg_page(dst),
+                                   dst->offset, dst->length, DMA_FROM_DEVICE);
+       }
+       crypto4xx_ret_sg_desc(dev, pd_uinfo);
+       if (ablk_req->base.complete != NULL)
+               ablk_req->base.complete(&ablk_req->base, 0);
+
+       return 0;
+}
+
+static u32 crypto4xx_ahash_done(struct crypto4xx_device *dev,
+                               struct pd_uinfo *pd_uinfo)
+{
+       struct crypto4xx_ctx *ctx;
+       struct ahash_request *ahash_req;
+
+       ahash_req = ahash_request_cast(pd_uinfo->async_req);
+       ctx  = crypto_tfm_ctx(ahash_req->base.tfm);
+
+       crypto4xx_copy_digest_to_dst(pd_uinfo,
+                                    crypto_tfm_ctx(ahash_req->base.tfm));
+       crypto4xx_ret_sg_desc(dev, pd_uinfo);
+       /* call user provided callback function x */
+       if (ahash_req->base.complete != NULL)
+               ahash_req->base.complete(&ahash_req->base, 0);
+
+       return 0;
+}
+
+static u32 crypto4xx_pd_done(struct crypto4xx_device *dev, u32 idx)
+{
+       struct ce_pd *pd;
+       struct pd_uinfo *pd_uinfo;
+
+       pd =  dev->pdr + sizeof(struct ce_pd)*idx;
+       pd_uinfo = dev->pdr_uinfo + sizeof(struct pd_uinfo)*idx;
+       if (crypto_tfm_alg_type(pd_uinfo->async_req->tfm) ==
+                       CRYPTO_ALG_TYPE_ABLKCIPHER)
+               return crypto4xx_ablkcipher_done(dev, pd_uinfo, pd);
+       else
+               return crypto4xx_ahash_done(dev, pd_uinfo);
+}
+
+/**
+ * Note: Only use this function to copy items that is word aligned.
+ */
+void crypto4xx_memcpy_le(unsigned int *dst,
+                        const unsigned char *buf,
+                        int len)
+{
+       u8 *tmp;
+       for (; len >= 4; buf += 4, len -= 4)
+               *dst++ = cpu_to_le32(*(unsigned int *) buf);
+
+       tmp = (u8 *)dst;
+       switch (len) {
+       case 3:
+               *tmp++ = 0;
+               *tmp++ = *(buf+2);
+               *tmp++ = *(buf+1);
+               *tmp++ = *buf;
+               break;
+       case 2:
+               *tmp++ = 0;
+               *tmp++ = 0;
+               *tmp++ = *(buf+1);
+               *tmp++ = *buf;
+               break;
+       case 1:
+               *tmp++ = 0;
+               *tmp++ = 0;
+               *tmp++ = 0;
+               *tmp++ = *buf;
+               break;
+       default:
+               break;
+       }
+}
+
+static void crypto4xx_stop_all(struct crypto4xx_core_device *core_dev)
+{
+       crypto4xx_destroy_pdr(core_dev->dev);
+       crypto4xx_destroy_gdr(core_dev->dev);
+       crypto4xx_destroy_sdr(core_dev->dev);
+       dev_set_drvdata(core_dev->device, NULL);
+       iounmap(core_dev->dev->ce_base);
+       kfree(core_dev->dev);
+       kfree(core_dev);
+}
+
+void crypto4xx_return_pd(struct crypto4xx_device *dev,
+                        u32 pd_entry, struct ce_pd *pd,
+                        struct pd_uinfo *pd_uinfo)
+{
+       /* irq should be already disabled */
+       dev->pdr_head = pd_entry;
+       pd->pd_ctl.w = 0;
+       pd->pd_ctl_len.w = 0;
+       pd_uinfo->state = PD_ENTRY_FREE;
+}
+
+/*
+ * derive number of elements in scatterlist
+ * Shamlessly copy from talitos.c
+ */
+static int get_sg_count(struct scatterlist *sg_list, int nbytes)
+{
+       struct scatterlist *sg = sg_list;
+       int sg_nents = 0;
+
+       while (nbytes) {
+               sg_nents++;
+               if (sg->length > nbytes)
+                       break;
+               nbytes -= sg->length;
+               sg = sg_next(sg);
+       }
+
+       return sg_nents;
+}
+
+static u32 get_next_gd(u32 current)
+{
+       if (current != PPC4XX_LAST_GD)
+               return current + 1;
+       else
+               return 0;
+}
+
+static u32 get_next_sd(u32 current)
+{
+       if (current != PPC4XX_LAST_SD)
+               return current + 1;
+       else
+               return 0;
+}
+
+u32 crypto4xx_build_pd(struct crypto_async_request *req,
+                      struct crypto4xx_ctx *ctx,
+                      struct scatterlist *src,
+                      struct scatterlist *dst,
+                      unsigned int datalen,
+                      void *iv, u32 iv_len)
+{
+       struct crypto4xx_device *dev = ctx->dev;
+       dma_addr_t addr, pd_dma, sd_dma, gd_dma;
+       struct dynamic_sa_ctl *sa;
+       struct scatterlist *sg;
+       struct ce_gd *gd;
+       struct ce_pd *pd;
+       u32 num_gd, num_sd;
+       u32 fst_gd = 0xffffffff;
+       u32 fst_sd = 0xffffffff;
+       u32 pd_entry;
+       unsigned long flags;
+       struct pd_uinfo *pd_uinfo = NULL;
+       unsigned int nbytes = datalen, idx;
+       unsigned int ivlen = 0;
+       u32 gd_idx = 0;
+
+       /* figure how many gd is needed */
+       num_gd = get_sg_count(src, datalen);
+       if (num_gd == 1)
+               num_gd = 0;
+
+       /* figure how many sd is needed */
+       if (sg_is_last(dst) || ctx->is_hash) {
+               num_sd = 0;
+       } else {
+               if (datalen > PPC4XX_SD_BUFFER_SIZE) {
+                       num_sd = datalen / PPC4XX_SD_BUFFER_SIZE;
+                       if (datalen % PPC4XX_SD_BUFFER_SIZE)
+                               num_sd++;
+               } else {
+                       num_sd = 1;
+               }
+       }
+
+       /*
+        * The follow section of code needs to be protected
+        * The gather ring and scatter ring needs to be consecutive
+        * In case of run out of any kind of descriptor, the descriptor
+        * already got must be return the original place.
+        */
+       spin_lock_irqsave(&dev->core_dev->lock, flags);
+       if (num_gd) {
+               fst_gd = crypto4xx_get_n_gd(dev, num_gd);
+               if (fst_gd == ERING_WAS_FULL) {
+                       spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+                       return -EAGAIN;
+               }
+       }
+       if (num_sd) {
+               fst_sd = crypto4xx_get_n_sd(dev, num_sd);
+               if (fst_sd == ERING_WAS_FULL) {
+                       if (num_gd)
+                               dev->gdr_head = fst_gd;
+                       spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+                       return -EAGAIN;
+               }
+       }
+       pd_entry = crypto4xx_get_pd_from_pdr_nolock(dev);
+       if (pd_entry == ERING_WAS_FULL) {
+               if (num_gd)
+                       dev->gdr_head = fst_gd;
+               if (num_sd)
+                       dev->sdr_head = fst_sd;
+               spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+               return -EAGAIN;
+       }
+       spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+
+       pd_uinfo = (struct pd_uinfo *)(dev->pdr_uinfo +
+                                      sizeof(struct pd_uinfo) * pd_entry);
+       pd = crypto4xx_get_pdp(dev, &pd_dma, pd_entry);
+       pd_uinfo->async_req = req;
+       pd_uinfo->num_gd = num_gd;
+       pd_uinfo->num_sd = num_sd;
+
+       if (iv_len || ctx->is_hash) {
+               ivlen = iv_len;
+               pd->sa = pd_uinfo->sa_pa;
+               sa = (struct dynamic_sa_ctl *) pd_uinfo->sa_va;
+               if (ctx->direction == DIR_INBOUND)
+                       memcpy(sa, ctx->sa_in, ctx->sa_len * 4);
+               else
+                       memcpy(sa, ctx->sa_out, ctx->sa_len * 4);
+
+               memcpy((void *) sa + ctx->offset_to_sr_ptr,
+                       &pd_uinfo->sr_pa, 4);
+
+               if (iv_len)
+                       crypto4xx_memcpy_le(pd_uinfo->sr_va, iv, iv_len);
+       } else {
+               if (ctx->direction == DIR_INBOUND) {
+                       pd->sa = ctx->sa_in_dma_addr;
+                       sa = (struct dynamic_sa_ctl *) ctx->sa_in;
+               } else {
+                       pd->sa = ctx->sa_out_dma_addr;
+                       sa = (struct dynamic_sa_ctl *) ctx->sa_out;
+               }
+       }
+       pd->sa_len = ctx->sa_len;
+       if (num_gd) {
+               /* get first gd we are going to use */
+               gd_idx = fst_gd;
+               pd_uinfo->first_gd = fst_gd;
+               pd_uinfo->num_gd = num_gd;
+               gd = crypto4xx_get_gdp(dev, &gd_dma, gd_idx);
+               pd->src = gd_dma;
+               /* enable gather */
+               sa->sa_command_0.bf.gather = 1;
+               idx = 0;
+               src = &src[0];
+               /* walk the sg, and setup gather array */
+               while (nbytes) {
+                       sg = &src[idx];
+                       addr = dma_map_page(dev->core_dev->device, sg_page(sg),
+                                   sg->offset, sg->length, DMA_TO_DEVICE);
+                       gd->ptr = addr;
+                       gd->ctl_len.len = sg->length;
+                       gd->ctl_len.done = 0;
+                       gd->ctl_len.ready = 1;
+                       if (sg->length >= nbytes)
+                               break;
+                       nbytes -= sg->length;
+                       gd_idx = get_next_gd(gd_idx);
+                       gd = crypto4xx_get_gdp(dev, &gd_dma, gd_idx);
+                       idx++;
+               }
+       } else {
+               pd->src = (u32)dma_map_page(dev->core_dev->device, sg_page(src),
+                               src->offset, src->length, DMA_TO_DEVICE);
+               /*
+                * Disable gather in sa command
+                */
+               sa->sa_command_0.bf.gather = 0;
+               /*
+                * Indicate gather array is not used
+                */
+               pd_uinfo->first_gd = 0xffffffff;
+               pd_uinfo->num_gd = 0;
+       }
+       if (ctx->is_hash || sg_is_last(dst)) {
+               /*
+                * we know application give us dst a whole piece of memory
+                * no need to use scatter ring.
+                * In case of is_hash, the icv is always at end of src data.
+                */
+               pd_uinfo->using_sd = 0;
+               pd_uinfo->first_sd = 0xffffffff;
+               pd_uinfo->num_sd = 0;
+               pd_uinfo->dest_va = dst;
+               sa->sa_command_0.bf.scatter = 0;
+               if (ctx->is_hash)
+                       pd->dest = virt_to_phys((void *)dst);
+               else
+                       pd->dest = (u32)dma_map_page(dev->core_dev->device,
+                                       sg_page(dst), dst->offset,
+                                       dst->length, DMA_TO_DEVICE);
+       } else {
+               struct ce_sd *sd = NULL;
+               u32 sd_idx = fst_sd;
+               nbytes = datalen;
+               sa->sa_command_0.bf.scatter = 1;
+               pd_uinfo->using_sd = 1;
+               pd_uinfo->dest_va = dst;
+               pd_uinfo->first_sd = fst_sd;
+               pd_uinfo->num_sd = num_sd;
+               sd = crypto4xx_get_sdp(dev, &sd_dma, sd_idx);
+               pd->dest = sd_dma;
+               /* setup scatter descriptor */
+               sd->ctl.done = 0;
+               sd->ctl.rdy = 1;
+               /* sd->ptr should be setup by sd_init routine*/
+               idx = 0;
+               if (nbytes >= PPC4XX_SD_BUFFER_SIZE)
+                       nbytes -= PPC4XX_SD_BUFFER_SIZE;
+               else
+                       nbytes = 0;
+               while (nbytes) {
+                       sd_idx = get_next_sd(sd_idx);
+                       sd = crypto4xx_get_sdp(dev, &sd_dma, sd_idx);
+                       /* setup scatter descriptor */
+                       sd->ctl.done = 0;
+                       sd->ctl.rdy = 1;
+                       if (nbytes >= PPC4XX_SD_BUFFER_SIZE)
+                               nbytes -= PPC4XX_SD_BUFFER_SIZE;
+                       else
+                               /*
+                                * SD entry can hold PPC4XX_SD_BUFFER_SIZE,
+                                * which is more than nbytes, so done.
+                                */
+                               nbytes = 0;
+               }
+       }
+
+       sa->sa_command_1.bf.hash_crypto_offset = 0;
+       pd->pd_ctl.w = ctx->pd_ctl;
+       pd->pd_ctl_len.w = 0x00400000 | (ctx->bypass << 24) | datalen;
+       pd_uinfo->state = PD_ENTRY_INUSE;
+       wmb();
+       /* write any value to push engine to read a pd */
+       writel(1, dev->ce_base + CRYPTO4XX_INT_DESCR_RD);
+       return -EINPROGRESS;
+}
+
+/**
+ * Algorithm Registration Functions
+ */
+static int crypto4xx_alg_init(struct crypto_tfm *tfm)
+{
+       struct crypto_alg *alg = tfm->__crt_alg;
+       struct crypto4xx_alg *amcc_alg = crypto_alg_to_crypto4xx_alg(alg);
+       struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       ctx->dev = amcc_alg->dev;
+       ctx->sa_in = NULL;
+       ctx->sa_out = NULL;
+       ctx->sa_in_dma_addr = 0;
+       ctx->sa_out_dma_addr = 0;
+       ctx->sa_len = 0;
+
+       if (alg->cra_type == &crypto_ablkcipher_type)
+               tfm->crt_ablkcipher.reqsize = sizeof(struct crypto4xx_ctx);
+       else if (alg->cra_type == &crypto_ahash_type)
+               tfm->crt_ahash.reqsize = sizeof(struct crypto4xx_ctx);
+
+       return 0;
+}
+
+static void crypto4xx_alg_exit(struct crypto_tfm *tfm)
+{
+       struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       crypto4xx_free_sa(ctx);
+       crypto4xx_free_state_record(ctx);
+}
+
+int crypto4xx_register_alg(struct crypto4xx_device *sec_dev,
+                          struct crypto_alg *crypto_alg, int array_size)
+{
+       struct crypto4xx_alg *alg;
+       int i;
+       int rc = 0;
+
+       for (i = 0; i < array_size; i++) {
+               alg = kzalloc(sizeof(struct crypto4xx_alg), GFP_KERNEL);
+               if (!alg)
+                       return -ENOMEM;
+
+               alg->alg = crypto_alg[i];
+               INIT_LIST_HEAD(&alg->alg.cra_list);
+               if (alg->alg.cra_init == NULL)
+                       alg->alg.cra_init = crypto4xx_alg_init;
+               if (alg->alg.cra_exit == NULL)
+                       alg->alg.cra_exit = crypto4xx_alg_exit;
+               alg->dev = sec_dev;
+               rc = crypto_register_alg(&alg->alg);
+               if (rc) {
+                       list_del(&alg->entry);
+                       kfree(alg);
+               } else {
+                       list_add_tail(&alg->entry, &sec_dev->alg_list);
+               }
+       }
+
+       return 0;
+}
+
+static void crypto4xx_unregister_alg(struct crypto4xx_device *sec_dev)
+{
+       struct crypto4xx_alg *alg, *tmp;
+
+       list_for_each_entry_safe(alg, tmp, &sec_dev->alg_list, entry) {
+               list_del(&alg->entry);
+               crypto_unregister_alg(&alg->alg);
+               kfree(alg);
+       }
+}
+
+static void crypto4xx_bh_tasklet_cb(unsigned long data)
+{
+       struct device *dev = (struct device *)data;
+       struct crypto4xx_core_device *core_dev = dev_get_drvdata(dev);
+       struct pd_uinfo *pd_uinfo;
+       struct ce_pd *pd;
+       u32 tail;
+
+       while (core_dev->dev->pdr_head != core_dev->dev->pdr_tail) {
+               tail = core_dev->dev->pdr_tail;
+               pd_uinfo = core_dev->dev->pdr_uinfo +
+                       sizeof(struct pd_uinfo)*tail;
+               pd =  core_dev->dev->pdr + sizeof(struct ce_pd) * tail;
+               if ((pd_uinfo->state == PD_ENTRY_INUSE) &&
+                                  pd->pd_ctl.bf.pe_done &&
+                                  !pd->pd_ctl.bf.host_ready) {
+                       pd->pd_ctl.bf.pe_done = 0;
+                       crypto4xx_pd_done(core_dev->dev, tail);
+                       crypto4xx_put_pd_to_pdr(core_dev->dev, tail);
+                       pd_uinfo->state = PD_ENTRY_FREE;
+               } else {
+                       /* if tail not done, break */
+                       break;
+               }
+       }
+}
+
+/**
+ * Top Half of isr.
+ */
+static irqreturn_t crypto4xx_ce_interrupt_handler(int irq, void *data)
+{
+       struct device *dev = (struct device *)data;
+       struct crypto4xx_core_device *core_dev = dev_get_drvdata(dev);
+
+       if (core_dev->dev->ce_base == 0)
+               return 0;
+
+       writel(PPC4XX_INTERRUPT_CLR,
+              core_dev->dev->ce_base + CRYPTO4XX_INT_CLR);
+       tasklet_schedule(&core_dev->tasklet);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * Supported Crypto Algorithms
+ */
+struct crypto_alg crypto4xx_alg[] = {
+       /* Crypto AES modes */
+       {
+               .cra_name       = "cbc(aes)",
+               .cra_driver_name = "cbc-aes-ppc4xx",
+               .cra_priority   = CRYPTO4XX_CRYPTO_PRIORITY,
+               .cra_flags      = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+               .cra_blocksize  = AES_BLOCK_SIZE,
+               .cra_ctxsize    = sizeof(struct crypto4xx_ctx),
+               .cra_alignmask  = 0,
+               .cra_type       = &crypto_ablkcipher_type,
+               .cra_module     = THIS_MODULE,
+               .cra_u          = {
+                       .ablkcipher = {
+                               .min_keysize    = AES_MIN_KEY_SIZE,
+                               .max_keysize    = AES_MAX_KEY_SIZE,
+                               .ivsize         = AES_IV_SIZE,
+                               .setkey         = crypto4xx_setkey_aes_cbc,
+                               .encrypt        = crypto4xx_encrypt,
+                               .decrypt        = crypto4xx_decrypt,
+                       }
+               }
+       },
+       /* Hash SHA1 */
+       {
+               .cra_name       = "sha1",
+               .cra_driver_name = "sha1-ppc4xx",
+               .cra_priority   = CRYPTO4XX_CRYPTO_PRIORITY,
+               .cra_flags      = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC,
+               .cra_blocksize  = SHA1_BLOCK_SIZE,
+               .cra_ctxsize    = sizeof(struct crypto4xx_ctx),
+               .cra_alignmask  = 0,
+               .cra_type       = &crypto_ahash_type,
+               .cra_init       = crypto4xx_sha1_alg_init,
+               .cra_module     = THIS_MODULE,
+               .cra_u          = {
+                       .ahash = {
+                               .digestsize     = SHA1_DIGEST_SIZE,
+                               .init           = crypto4xx_hash_init,
+                               .update         = crypto4xx_hash_update,
+                               .final          = crypto4xx_hash_final,
+                               .digest         = crypto4xx_hash_digest,
+                       }
+               }
+       },
+};
+
+/**
+ * Module Initialization Routine
+ */
+static int __init crypto4xx_probe(struct of_device *ofdev,
+                                 const struct of_device_id *match)
+{
+       int rc;
+       struct resource res;
+       struct device *dev = &ofdev->dev;
+       struct crypto4xx_core_device *core_dev;
+
+       rc = of_address_to_resource(ofdev->node, 0, &res);
+       if (rc)
+               return -ENODEV;
+
+       if (of_find_compatible_node(NULL, NULL, "amcc,ppc460ex-crypto")) {
+               mtdcri(SDR0, PPC460EX_SDR0_SRST,
+                      mfdcri(SDR0, PPC460EX_SDR0_SRST) | PPC460EX_CE_RESET);
+               mtdcri(SDR0, PPC460EX_SDR0_SRST,
+                      mfdcri(SDR0, PPC460EX_SDR0_SRST) & ~PPC460EX_CE_RESET);
+       } else if (of_find_compatible_node(NULL, NULL,
+                       "amcc,ppc405ex-crypto")) {
+               mtdcri(SDR0, PPC405EX_SDR0_SRST,
+                      mfdcri(SDR0, PPC405EX_SDR0_SRST) | PPC405EX_CE_RESET);
+               mtdcri(SDR0, PPC405EX_SDR0_SRST,
+                      mfdcri(SDR0, PPC405EX_SDR0_SRST) & ~PPC405EX_CE_RESET);
+       } else if (of_find_compatible_node(NULL, NULL,
+                       "amcc,ppc460sx-crypto")) {
+               mtdcri(SDR0, PPC460SX_SDR0_SRST,
+                      mfdcri(SDR0, PPC460SX_SDR0_SRST) | PPC460SX_CE_RESET);
+               mtdcri(SDR0, PPC460SX_SDR0_SRST,
+                      mfdcri(SDR0, PPC460SX_SDR0_SRST) & ~PPC460SX_CE_RESET);
+       } else {
+               printk(KERN_ERR "Crypto Function Not supported!\n");
+               return -EINVAL;
+       }
+
+       core_dev = kzalloc(sizeof(struct crypto4xx_core_device), GFP_KERNEL);
+       if (!core_dev)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, core_dev);
+       core_dev->ofdev = ofdev;
+       core_dev->dev = kzalloc(sizeof(struct crypto4xx_device), GFP_KERNEL);
+       if (!core_dev->dev)
+               goto err_alloc_dev;
+
+       core_dev->dev->core_dev = core_dev;
+       core_dev->device = dev;
+       spin_lock_init(&core_dev->lock);
+       INIT_LIST_HEAD(&core_dev->dev->alg_list);
+       rc = crypto4xx_build_pdr(core_dev->dev);
+       if (rc)
+               goto err_build_pdr;
+
+       rc = crypto4xx_build_gdr(core_dev->dev);
+       if (rc)
+               goto err_build_gdr;
+
+       rc = crypto4xx_build_sdr(core_dev->dev);
+       if (rc)
+               goto err_build_sdr;
+
+       /* Init tasklet for bottom half processing */
+       tasklet_init(&core_dev->tasklet, crypto4xx_bh_tasklet_cb,
+                    (unsigned long) dev);
+
+       /* Register for Crypto isr, Crypto Engine IRQ */
+       core_dev->irq = irq_of_parse_and_map(ofdev->node, 0);
+       rc = request_irq(core_dev->irq, crypto4xx_ce_interrupt_handler, 0,
+                        core_dev->dev->name, dev);
+       if (rc)
+               goto err_request_irq;
+
+       core_dev->dev->ce_base = of_iomap(ofdev->node, 0);
+       if (!core_dev->dev->ce_base) {
+               dev_err(dev, "failed to of_iomap\n");
+               goto err_iomap;
+       }
+
+       /* need to setup pdr, rdr, gdr and sdr before this */
+       crypto4xx_hw_init(core_dev->dev);
+
+       /* Register security algorithms with Linux CryptoAPI */
+       rc = crypto4xx_register_alg(core_dev->dev, crypto4xx_alg,
+                              ARRAY_SIZE(crypto4xx_alg));
+       if (rc)
+               goto err_start_dev;
+
+       return 0;
+
+err_start_dev:
+       iounmap(core_dev->dev->ce_base);
+err_iomap:
+       free_irq(core_dev->irq, dev);
+       irq_dispose_mapping(core_dev->irq);
+       tasklet_kill(&core_dev->tasklet);
+err_request_irq:
+       crypto4xx_destroy_sdr(core_dev->dev);
+err_build_sdr:
+       crypto4xx_destroy_gdr(core_dev->dev);
+err_build_gdr:
+       crypto4xx_destroy_pdr(core_dev->dev);
+err_build_pdr:
+       kfree(core_dev->dev);
+err_alloc_dev:
+       kfree(core_dev);
+
+       return rc;
+}
+
+static int __exit crypto4xx_remove(struct of_device *ofdev)
+{
+       struct device *dev = &ofdev->dev;
+       struct crypto4xx_core_device *core_dev = dev_get_drvdata(dev);
+
+       free_irq(core_dev->irq, dev);
+       irq_dispose_mapping(core_dev->irq);
+
+       tasklet_kill(&core_dev->tasklet);
+       /* Un-register with Linux CryptoAPI */
+       crypto4xx_unregister_alg(core_dev->dev);
+       /* Free all allocated memory */
+       crypto4xx_stop_all(core_dev);
+
+       return 0;
+}
+
+static struct of_device_id crypto4xx_match[] = {
+       { .compatible      = "amcc,ppc4xx-crypto",},
+       { },
+};
+
+static struct of_platform_driver crypto4xx_driver = {
+       .name           = "crypto4xx",
+       .match_table    = crypto4xx_match,
+       .probe          = crypto4xx_probe,
+       .remove         = crypto4xx_remove,
+};
+
+static int __init crypto4xx_init(void)
+{
+       return of_register_platform_driver(&crypto4xx_driver);
+}
+
+static void __exit crypto4xx_exit(void)
+{
+       of_unregister_platform_driver(&crypto4xx_driver);
+}
+
+module_init(crypto4xx_init);
+module_exit(crypto4xx_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("James Hsiao <jhsiao@amcc.com>");
+MODULE_DESCRIPTION("Driver for AMCC PPC4xx crypto accelerator");
+
diff --git a/drivers/crypto/amcc/crypto4xx_core.h b/drivers/crypto/amcc/crypto4xx_core.h
new file mode 100644 (file)
index 0000000..1ef1034
--- /dev/null
@@ -0,0 +1,177 @@
+/**
+ * AMCC SoC PPC4xx Crypto Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <jhsiao@amcc.com>
+ *
+ * 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.
+ *
+ * This is the header file for AMCC Crypto offload Linux device driver for
+ * use with Linux CryptoAPI.
+
+ */
+
+#ifndef __CRYPTO4XX_CORE_H__
+#define __CRYPTO4XX_CORE_H__
+
+#define PPC460SX_SDR0_SRST                      0x201
+#define PPC405EX_SDR0_SRST                      0x200
+#define PPC460EX_SDR0_SRST                      0x201
+#define PPC460EX_CE_RESET                       0x08000000
+#define PPC460SX_CE_RESET                       0x20000000
+#define PPC405EX_CE_RESET                       0x00000008
+
+#define CRYPTO4XX_CRYPTO_PRIORITY              300
+#define PPC4XX_LAST_PD                         63
+#define PPC4XX_NUM_PD                          64
+#define PPC4XX_LAST_GD                         1023
+#define PPC4XX_NUM_GD                          1024
+#define PPC4XX_LAST_SD                         63
+#define PPC4XX_NUM_SD                          64
+#define PPC4XX_SD_BUFFER_SIZE                  2048
+
+#define PD_ENTRY_INUSE                         1
+#define PD_ENTRY_FREE                          0
+#define ERING_WAS_FULL                         0xffffffff
+
+struct crypto4xx_device;
+
+struct pd_uinfo {
+       struct crypto4xx_device *dev;
+       u32   state;
+       u32 using_sd;
+       u32 first_gd;           /* first gather discriptor
+                               used by this packet */
+       u32 num_gd;             /* number of gather discriptor
+                               used by this packet */
+       u32 first_sd;           /* first scatter discriptor
+                               used by this packet */
+       u32 num_sd;             /* number of scatter discriptors
+                               used by this packet */
+       void *sa_va;            /* shadow sa, when using cp from ctx->sa */
+       u32 sa_pa;
+       void *sr_va;            /* state record for shadow sa */
+       u32 sr_pa;
+       struct scatterlist *dest_va;
+       struct crypto_async_request *async_req;         /* base crypto request
+                                                       for this packet */
+};
+
+struct crypto4xx_device {
+       struct crypto4xx_core_device *core_dev;
+       char *name;
+       u64  ce_phy_address;
+       void __iomem *ce_base;
+
+       void *pdr;                      /* base address of packet
+                                       descriptor ring */
+       dma_addr_t pdr_pa;              /* physical address used to
+                                       program ce pdr_base_register */
+       void *gdr;                      /* gather descriptor ring */
+       dma_addr_t gdr_pa;              /* physical address used to
+                                       program ce gdr_base_register */
+       void *sdr;                      /* scatter descriptor ring */
+       dma_addr_t sdr_pa;              /* physical address used to
+                                       program ce sdr_base_register */
+       void *scatter_buffer_va;
+       dma_addr_t scatter_buffer_pa;
+       u32 scatter_buffer_size;
+
+       void *shadow_sa_pool;           /* pool of memory for sa in pd_uinfo */
+       dma_addr_t shadow_sa_pool_pa;
+       void *shadow_sr_pool;           /* pool of memory for sr in pd_uinfo */
+       dma_addr_t shadow_sr_pool_pa;
+       u32 pdr_tail;
+       u32 pdr_head;
+       u32 gdr_tail;
+       u32 gdr_head;
+       u32 sdr_tail;
+       u32 sdr_head;
+       void *pdr_uinfo;
+       struct list_head alg_list;      /* List of algorithm supported
+                                       by this device */
+};
+
+struct crypto4xx_core_device {
+       struct device *device;
+       struct of_device *ofdev;
+       struct crypto4xx_device *dev;
+       u32 int_status;
+       u32 irq;
+       struct tasklet_struct tasklet;
+       spinlock_t lock;
+};
+
+struct crypto4xx_ctx {
+       struct crypto4xx_device *dev;
+       void *sa_in;
+       dma_addr_t sa_in_dma_addr;
+       void *sa_out;
+       dma_addr_t sa_out_dma_addr;
+       void *state_record;
+       dma_addr_t state_record_dma_addr;
+       u32 sa_len;
+       u32 offset_to_sr_ptr;           /* offset to state ptr, in dynamic sa */
+       u32 direction;
+       u32 next_hdr;
+       u32 save_iv;
+       u32 pd_ctl_len;
+       u32 pd_ctl;
+       u32 bypass;
+       u32 is_hash;
+       u32 hash_final;
+};
+
+struct crypto4xx_req_ctx {
+       struct crypto4xx_device *dev;   /* Device in which
+                                       operation to send to */
+       void *sa;
+       u32 sa_dma_addr;
+       u16 sa_len;
+};
+
+struct crypto4xx_alg {
+       struct list_head  entry;
+       struct crypto_alg alg;
+       struct crypto4xx_device *dev;
+};
+
+#define crypto_alg_to_crypto4xx_alg(x) \
+               container_of(x, struct crypto4xx_alg, alg)
+
+extern int crypto4xx_alloc_sa(struct crypto4xx_ctx *ctx, u32 size);
+extern void crypto4xx_free_sa(struct crypto4xx_ctx *ctx);
+extern u32 crypto4xx_alloc_sa_rctx(struct crypto4xx_ctx *ctx,
+                                  struct crypto4xx_ctx *rctx);
+extern void crypto4xx_free_sa_rctx(struct crypto4xx_ctx *rctx);
+extern void crypto4xx_free_ctx(struct crypto4xx_ctx *ctx);
+extern u32 crypto4xx_alloc_state_record(struct crypto4xx_ctx *ctx);
+extern u32 get_dynamic_sa_offset_state_ptr_field(struct crypto4xx_ctx *ctx);
+extern u32 get_dynamic_sa_offset_key_field(struct crypto4xx_ctx *ctx);
+extern u32 get_dynamic_sa_iv_size(struct crypto4xx_ctx *ctx);
+extern void crypto4xx_memcpy_le(unsigned int *dst,
+                               const unsigned char *buf, int len);
+extern u32 crypto4xx_build_pd(struct crypto_async_request *req,
+                             struct crypto4xx_ctx *ctx,
+                             struct scatterlist *src,
+                             struct scatterlist *dst,
+                             unsigned int datalen,
+                             void *iv, u32 iv_len);
+extern int crypto4xx_setkey_aes_cbc(struct crypto_ablkcipher *cipher,
+                                   const u8 *key, unsigned int keylen);
+extern int crypto4xx_encrypt(struct ablkcipher_request *req);
+extern int crypto4xx_decrypt(struct ablkcipher_request *req);
+extern int crypto4xx_sha1_alg_init(struct crypto_tfm *tfm);
+extern int crypto4xx_hash_digest(struct ahash_request *req);
+extern int crypto4xx_hash_final(struct ahash_request *req);
+extern int crypto4xx_hash_update(struct ahash_request *req);
+extern int crypto4xx_hash_init(struct ahash_request *req);
+#endif
diff --git a/drivers/crypto/amcc/crypto4xx_reg_def.h b/drivers/crypto/amcc/crypto4xx_reg_def.h
new file mode 100644 (file)
index 0000000..7d4edb0
--- /dev/null
@@ -0,0 +1,284 @@
+/**
+ * AMCC SoC PPC4xx Crypto Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <jhsiao@amcc.com>
+ *
+ * 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.
+ *
+ * This filr defines the register set for Security Subsystem
+ */
+
+#ifndef __CRYPTO4XX_REG_DEF_H__
+#define __CRYPTO4XX_REG_DEF_H__
+
+/* CRYPTO4XX Register offset */
+#define CRYPTO4XX_DESCRIPTOR                   0x00000000
+#define CRYPTO4XX_CTRL_STAT                    0x00000000
+#define CRYPTO4XX_SOURCE                       0x00000004
+#define CRYPTO4XX_DEST                         0x00000008
+#define CRYPTO4XX_SA                           0x0000000C
+#define CRYPTO4XX_SA_LENGTH                    0x00000010
+#define CRYPTO4XX_LENGTH                       0x00000014
+
+#define CRYPTO4XX_PE_DMA_CFG                   0x00000040
+#define CRYPTO4XX_PE_DMA_STAT                  0x00000044
+#define CRYPTO4XX_PDR_BASE                     0x00000048
+#define CRYPTO4XX_RDR_BASE                     0x0000004c
+#define CRYPTO4XX_RING_SIZE                    0x00000050
+#define CRYPTO4XX_RING_CTRL                    0x00000054
+#define CRYPTO4XX_INT_RING_STAT                        0x00000058
+#define CRYPTO4XX_EXT_RING_STAT                        0x0000005c
+#define CRYPTO4XX_IO_THRESHOLD                 0x00000060
+#define CRYPTO4XX_GATH_RING_BASE               0x00000064
+#define CRYPTO4XX_SCAT_RING_BASE               0x00000068
+#define CRYPTO4XX_PART_RING_SIZE               0x0000006c
+#define CRYPTO4XX_PART_RING_CFG                        0x00000070
+
+#define CRYPTO4XX_PDR_BASE_UADDR               0x00000080
+#define CRYPTO4XX_RDR_BASE_UADDR               0x00000084
+#define CRYPTO4XX_PKT_SRC_UADDR                        0x00000088
+#define CRYPTO4XX_PKT_DEST_UADDR               0x0000008c
+#define CRYPTO4XX_SA_UADDR                     0x00000090
+#define CRYPTO4XX_GATH_RING_BASE_UADDR         0x000000A0
+#define CRYPTO4XX_SCAT_RING_BASE_UADDR         0x000000A4
+
+#define CRYPTO4XX_SEQ_RD                       0x00000408
+#define CRYPTO4XX_SEQ_MASK_RD                  0x0000040C
+
+#define CRYPTO4XX_SA_CMD_0                     0x00010600
+#define CRYPTO4XX_SA_CMD_1                     0x00010604
+
+#define CRYPTO4XX_STATE_PTR                    0x000106dc
+#define CRYPTO4XX_STATE_IV                     0x00010700
+#define CRYPTO4XX_STATE_HASH_BYTE_CNT_0                0x00010710
+#define CRYPTO4XX_STATE_HASH_BYTE_CNT_1                0x00010714
+
+#define CRYPTO4XX_STATE_IDIGEST_0              0x00010718
+#define CRYPTO4XX_STATE_IDIGEST_1              0x0001071c
+
+#define CRYPTO4XX_DATA_IN                      0x00018000
+#define CRYPTO4XX_DATA_OUT                     0x0001c000
+
+#define CRYPTO4XX_INT_UNMASK_STAT              0x000500a0
+#define CRYPTO4XX_INT_MASK_STAT                        0x000500a4
+#define CRYPTO4XX_INT_CLR                      0x000500a4
+#define CRYPTO4XX_INT_EN                       0x000500a8
+
+#define CRYPTO4XX_INT_PKA                      0x00000002
+#define CRYPTO4XX_INT_PDR_DONE                 0x00008000
+#define CRYPTO4XX_INT_MA_WR_ERR                        0x00020000
+#define CRYPTO4XX_INT_MA_RD_ERR                        0x00010000
+#define CRYPTO4XX_INT_PE_ERR                   0x00000200
+#define CRYPTO4XX_INT_USER_DMA_ERR             0x00000040
+#define CRYPTO4XX_INT_SLAVE_ERR                        0x00000010
+#define CRYPTO4XX_INT_MASTER_ERR               0x00000008
+#define CRYPTO4XX_INT_ERROR                    0x00030258
+
+#define CRYPTO4XX_INT_CFG                      0x000500ac
+#define CRYPTO4XX_INT_DESCR_RD                 0x000500b0
+#define CRYPTO4XX_INT_DESCR_CNT                        0x000500b4
+#define CRYPTO4XX_INT_TIMEOUT_CNT              0x000500b8
+
+#define CRYPTO4XX_DEVICE_CTRL                  0x00060080
+#define CRYPTO4XX_DEVICE_ID                    0x00060084
+#define CRYPTO4XX_DEVICE_INFO                  0x00060088
+#define CRYPTO4XX_DMA_USER_SRC                 0x00060094
+#define CRYPTO4XX_DMA_USER_DEST                        0x00060098
+#define CRYPTO4XX_DMA_USER_CMD                 0x0006009C
+
+#define CRYPTO4XX_DMA_CFG                      0x000600d4
+#define CRYPTO4XX_BYTE_ORDER_CFG               0x000600d8
+#define CRYPTO4XX_ENDIAN_CFG                   0x000600d8
+
+#define CRYPTO4XX_PRNG_STAT                    0x00070000
+#define CRYPTO4XX_PRNG_CTRL                    0x00070004
+#define CRYPTO4XX_PRNG_SEED_L                  0x00070008
+#define CRYPTO4XX_PRNG_SEED_H                  0x0007000c
+
+#define CRYPTO4XX_PRNG_RES_0                   0x00070020
+#define CRYPTO4XX_PRNG_RES_1                   0x00070024
+#define CRYPTO4XX_PRNG_RES_2                   0x00070028
+#define CRYPTO4XX_PRNG_RES_3                   0x0007002C
+
+#define CRYPTO4XX_PRNG_LFSR_L                  0x00070030
+#define CRYPTO4XX_PRNG_LFSR_H                  0x00070034
+
+/**
+ * Initilize CRYPTO ENGINE registers, and memory bases.
+ */
+#define PPC4XX_PDR_POLL                                0x3ff
+#define PPC4XX_OUTPUT_THRESHOLD                        2
+#define PPC4XX_INPUT_THRESHOLD                 2
+#define PPC4XX_PD_SIZE                         6
+#define PPC4XX_CTX_DONE_INT                    0x2000
+#define PPC4XX_PD_DONE_INT                     0x8000
+#define PPC4XX_BYTE_ORDER                      0x22222
+#define PPC4XX_INTERRUPT_CLR                   0x3ffff
+#define PPC4XX_PRNG_CTRL_AUTO_EN               0x3
+#define PPC4XX_DC_3DES_EN                      1
+#define PPC4XX_INT_DESCR_CNT                   4
+#define PPC4XX_INT_TIMEOUT_CNT                 0
+#define PPC4XX_INT_CFG                         1
+/**
+ * all follow define are ad hoc
+ */
+#define PPC4XX_RING_RETRY                      100
+#define PPC4XX_RING_POLL                       100
+#define PPC4XX_SDR_SIZE                                PPC4XX_NUM_SD
+#define PPC4XX_GDR_SIZE                                PPC4XX_NUM_GD
+
+/**
+  * Generic Security Association (SA) with all possible fields. These will
+ * never likely used except for reference purpose. These structure format
+ * can be not changed as the hardware expects them to be layout as defined.
+ * Field can be removed or reduced but ordering can not be changed.
+ */
+#define CRYPTO4XX_DMA_CFG_OFFSET               0x40
+union ce_pe_dma_cfg {
+       struct {
+               u32 rsv:7;
+               u32 dir_host:1;
+               u32 rsv1:2;
+               u32 bo_td_en:1;
+               u32 dis_pdr_upd:1;
+               u32 bo_sgpd_en:1;
+               u32 bo_data_en:1;
+               u32 bo_sa_en:1;
+               u32 bo_pd_en:1;
+               u32 rsv2:4;
+               u32 dynamic_sa_en:1;
+               u32 pdr_mode:2;
+               u32 pe_mode:1;
+               u32 rsv3:5;
+               u32 reset_sg:1;
+               u32 reset_pdr:1;
+               u32 reset_pe:1;
+       } bf;
+    u32 w;
+} __attribute__((packed));
+
+#define CRYPTO4XX_PDR_BASE_OFFSET              0x48
+#define CRYPTO4XX_RDR_BASE_OFFSET              0x4c
+#define CRYPTO4XX_RING_SIZE_OFFSET             0x50
+union ce_ring_size {
+       struct {
+               u32 ring_offset:16;
+               u32 rsv:6;
+               u32 ring_size:10;
+       } bf;
+    u32 w;
+} __attribute__((packed));
+
+#define CRYPTO4XX_RING_CONTROL_OFFSET          0x54
+union ce_ring_contol {
+       struct {
+               u32 continuous:1;
+               u32 rsv:5;
+               u32 ring_retry_divisor:10;
+               u32 rsv1:4;
+               u32 ring_poll_divisor:10;
+       } bf;
+    u32 w;
+} __attribute__((packed));
+
+#define CRYPTO4XX_IO_THRESHOLD_OFFSET          0x60
+union ce_io_threshold {
+       struct {
+               u32 rsv:6;
+               u32 output_threshold:10;
+               u32 rsv1:6;
+               u32 input_threshold:10;
+       } bf;
+    u32 w;
+} __attribute__((packed));
+
+#define CRYPTO4XX_GATHER_RING_BASE_OFFSET      0x64
+#define CRYPTO4XX_SCATTER_RING_BASE_OFFSET     0x68
+
+union ce_part_ring_size  {
+       struct {
+               u32 sdr_size:16;
+               u32 gdr_size:16;
+       } bf;
+    u32 w;
+} __attribute__((packed));
+
+#define MAX_BURST_SIZE_32                      0
+#define MAX_BURST_SIZE_64                      1
+#define MAX_BURST_SIZE_128                     2
+#define MAX_BURST_SIZE_256                     3
+
+/* gather descriptor control length */
+struct gd_ctl_len {
+       u32 len:16;
+       u32 rsv:14;
+       u32 done:1;
+       u32 ready:1;
+} __attribute__((packed));
+
+struct ce_gd {
+       u32 ptr;
+       struct gd_ctl_len ctl_len;
+} __attribute__((packed));
+
+struct sd_ctl {
+       u32 ctl:30;
+       u32 done:1;
+       u32 rdy:1;
+} __attribute__((packed));
+
+struct ce_sd {
+    u32 ptr;
+       struct sd_ctl ctl;
+} __attribute__((packed));
+
+#define PD_PAD_CTL_32  0x10
+#define PD_PAD_CTL_64  0x20
+#define PD_PAD_CTL_128 0x40
+#define PD_PAD_CTL_256 0x80
+union ce_pd_ctl {
+       struct {
+               u32 pd_pad_ctl:8;
+               u32 status:8;
+               u32 next_hdr:8;
+               u32 rsv:2;
+               u32 cached_sa:1;
+               u32 hash_final:1;
+               u32 init_arc4:1;
+               u32 rsv1:1;
+               u32 pe_done:1;
+               u32 host_ready:1;
+       } bf;
+       u32 w;
+} __attribute__((packed));
+
+union ce_pd_ctl_len {
+       struct {
+               u32 bypass:8;
+               u32 pe_done:1;
+               u32 host_ready:1;
+               u32 rsv:2;
+               u32 pkt_len:20;
+       } bf;
+       u32 w;
+} __attribute__((packed));
+
+struct ce_pd {
+       union ce_pd_ctl   pd_ctl;
+       u32 src;
+       u32 dest;
+       u32 sa;                 /* get from ctx->sa_dma_addr */
+       u32 sa_len;             /* only if dynamic sa is used */
+       union ce_pd_ctl_len pd_ctl_len;
+
+} __attribute__((packed));
+#endif
diff --git a/drivers/crypto/amcc/crypto4xx_sa.c b/drivers/crypto/amcc/crypto4xx_sa.c
new file mode 100644 (file)
index 0000000..466fd94
--- /dev/null
@@ -0,0 +1,108 @@
+/**
+ * AMCC SoC PPC4xx Crypto Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <jhsiao@amcc.com>
+ *
+ * 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.
+ *
+ * @file crypto4xx_sa.c
+ *
+ * This file implements the security context
+ * assoicate format.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mod_devicetable.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock_types.h>
+#include <linux/highmem.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <crypto/algapi.h>
+#include <crypto/des.h>
+#include "crypto4xx_reg_def.h"
+#include "crypto4xx_sa.h"
+#include "crypto4xx_core.h"
+
+u32 get_dynamic_sa_offset_iv_field(struct crypto4xx_ctx *ctx)
+{
+       u32 offset;
+       union dynamic_sa_contents cts;
+
+       if (ctx->direction == DIR_INBOUND)
+               cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_in))->sa_contents;
+       else
+               cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_out))->sa_contents;
+       offset = cts.bf.key_size
+               + cts.bf.inner_size
+               + cts.bf.outer_size
+               + cts.bf.spi
+               + cts.bf.seq_num0
+               + cts.bf.seq_num1
+               + cts.bf.seq_num_mask0
+               + cts.bf.seq_num_mask1
+               + cts.bf.seq_num_mask2
+               + cts.bf.seq_num_mask3;
+
+       return sizeof(struct dynamic_sa_ctl) + offset * 4;
+}
+
+u32 get_dynamic_sa_offset_state_ptr_field(struct crypto4xx_ctx *ctx)
+{
+       u32 offset;
+       union dynamic_sa_contents cts;
+
+       if (ctx->direction == DIR_INBOUND)
+               cts.w = ((struct dynamic_sa_ctl *) ctx->sa_in)->sa_contents;
+       else
+               cts.w = ((struct dynamic_sa_ctl *) ctx->sa_out)->sa_contents;
+       offset = cts.bf.key_size
+               + cts.bf.inner_size
+               + cts.bf.outer_size
+               + cts.bf.spi
+               + cts.bf.seq_num0
+               + cts.bf.seq_num1
+               + cts.bf.seq_num_mask0
+               + cts.bf.seq_num_mask1
+               + cts.bf.seq_num_mask2
+               + cts.bf.seq_num_mask3
+               + cts.bf.iv0
+               + cts.bf.iv1
+               + cts.bf.iv2
+               + cts.bf.iv3;
+
+       return sizeof(struct dynamic_sa_ctl) + offset * 4;
+}
+
+u32 get_dynamic_sa_iv_size(struct crypto4xx_ctx *ctx)
+{
+       union dynamic_sa_contents cts;
+
+       if (ctx->direction == DIR_INBOUND)
+               cts.w = ((struct dynamic_sa_ctl *) ctx->sa_in)->sa_contents;
+       else
+               cts.w = ((struct dynamic_sa_ctl *) ctx->sa_out)->sa_contents;
+       return (cts.bf.iv0 + cts.bf.iv1 + cts.bf.iv2 + cts.bf.iv3) * 4;
+}
+
+u32 get_dynamic_sa_offset_key_field(struct crypto4xx_ctx *ctx)
+{
+       union dynamic_sa_contents cts;
+
+       if (ctx->direction == DIR_INBOUND)
+               cts.w = ((struct dynamic_sa_ctl *) ctx->sa_in)->sa_contents;
+       else
+               cts.w = ((struct dynamic_sa_ctl *) ctx->sa_out)->sa_contents;
+
+       return sizeof(struct dynamic_sa_ctl);
+}
diff --git a/drivers/crypto/amcc/crypto4xx_sa.h b/drivers/crypto/amcc/crypto4xx_sa.h
new file mode 100644 (file)
index 0000000..4b83ed7
--- /dev/null
@@ -0,0 +1,243 @@
+/**
+ * AMCC SoC PPC4xx Crypto Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <jhsiao@amcc.com>
+ *
+ * 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.
+ *
+ * This file defines the security context
+ * assoicate format.
+ */
+
+#ifndef __CRYPTO4XX_SA_H__
+#define __CRYPTO4XX_SA_H__
+
+#define AES_IV_SIZE                            16
+
+/**
+ * Contents of Dynamic Security Association (SA) with all possible fields
+ */
+union dynamic_sa_contents {
+       struct {
+               u32 arc4_state_ptr:1;
+               u32 arc4_ij_ptr:1;
+               u32 state_ptr:1;
+               u32 iv3:1;
+               u32 iv2:1;
+               u32 iv1:1;
+               u32 iv0:1;
+               u32 seq_num_mask3:1;
+               u32 seq_num_mask2:1;
+               u32 seq_num_mask1:1;
+               u32 seq_num_mask0:1;
+               u32 seq_num1:1;
+               u32 seq_num0:1;
+               u32 spi:1;
+               u32 outer_size:5;
+               u32 inner_size:5;
+               u32 key_size:4;
+               u32 cmd_size:4;
+       } bf;
+       u32 w;
+} __attribute__((packed));
+
+#define DIR_OUTBOUND                           0
+#define DIR_INBOUND                            1
+#define SA_OP_GROUP_BASIC                      0
+#define SA_OPCODE_ENCRYPT                      0
+#define SA_OPCODE_DECRYPT                      0
+#define SA_OPCODE_HASH                         3
+#define SA_CIPHER_ALG_DES                      0
+#define SA_CIPHER_ALG_3DES                     1
+#define SA_CIPHER_ALG_ARC4                     2
+#define SA_CIPHER_ALG_AES                      3
+#define SA_CIPHER_ALG_KASUMI                   4
+#define SA_CIPHER_ALG_NULL                     15
+
+#define SA_HASH_ALG_MD5                                0
+#define SA_HASH_ALG_SHA1                       1
+#define SA_HASH_ALG_NULL                       15
+#define SA_HASH_ALG_SHA1_DIGEST_SIZE           20
+
+#define SA_LOAD_HASH_FROM_SA                   0
+#define SA_LOAD_HASH_FROM_STATE                        2
+#define SA_NOT_LOAD_HASH                       3
+#define SA_LOAD_IV_FROM_SA                     0
+#define SA_LOAD_IV_FROM_INPUT                  1
+#define SA_LOAD_IV_FROM_STATE                  2
+#define SA_LOAD_IV_GEN_IV                      3
+
+#define SA_PAD_TYPE_CONSTANT                   2
+#define SA_PAD_TYPE_ZERO                       3
+#define SA_PAD_TYPE_TLS                                5
+#define SA_PAD_TYPE_DTLS                       5
+#define SA_NOT_SAVE_HASH                       0
+#define SA_SAVE_HASH                           1
+#define SA_NOT_SAVE_IV                         0
+#define SA_SAVE_IV                             1
+#define SA_HEADER_PROC                         1
+#define SA_NO_HEADER_PROC                      0
+
+union sa_command_0 {
+       struct {
+               u32 scatter:1;
+               u32 gather:1;
+               u32 save_hash_state:1;
+               u32 save_iv:1;
+               u32 load_hash_state:2;
+               u32 load_iv:2;
+               u32 digest_len:4;
+               u32 hdr_proc:1;
+               u32 extend_pad:1;
+               u32 stream_cipher_pad:1;
+               u32 rsv:1;
+               u32 hash_alg:4;
+               u32 cipher_alg:4;
+               u32 pad_type:2;
+               u32 op_group:2;
+               u32 dir:1;
+               u32 opcode:3;
+       } bf;
+       u32 w;
+} __attribute__((packed));
+
+#define CRYPTO_MODE_ECB                                0
+#define CRYPTO_MODE_CBC                                1
+
+#define CRYPTO_FEEDBACK_MODE_NO_FB             0
+#define CRYPTO_FEEDBACK_MODE_64BIT_OFB         0
+#define CRYPTO_FEEDBACK_MODE_8BIT_CFB          1
+#define CRYPTO_FEEDBACK_MODE_1BIT_CFB          2
+#define CRYPTO_FEEDBACK_MODE_128BIT_CFB                3
+
+#define SA_AES_KEY_LEN_128                     2
+#define SA_AES_KEY_LEN_192                     3
+#define SA_AES_KEY_LEN_256                     4
+
+#define SA_REV2                                        1
+/**
+ * The follow defines bits sa_command_1
+ * In Basic hash mode  this bit define simple hash or hmac.
+ * In IPsec mode, this bit define muting control.
+ */
+#define SA_HASH_MODE_HASH                      0
+#define SA_HASH_MODE_HMAC                      1
+#define SA_MC_ENABLE                           0
+#define SA_MC_DISABLE                          1
+#define SA_NOT_COPY_HDR                                0
+#define SA_COPY_HDR                            1
+#define SA_NOT_COPY_PAD                                0
+#define SA_COPY_PAD                            1
+#define SA_NOT_COPY_PAYLOAD                    0
+#define SA_COPY_PAYLOAD                                1
+#define SA_EXTENDED_SN_OFF                     0
+#define SA_EXTENDED_SN_ON                      1
+#define SA_SEQ_MASK_OFF                                0
+#define SA_SEQ_MASK_ON                         1
+
+union sa_command_1 {
+       struct {
+               u32 crypto_mode31:1;
+               u32 save_arc4_state:1;
+               u32 arc4_stateful:1;
+               u32 key_len:5;
+               u32 hash_crypto_offset:8;
+               u32 sa_rev:2;
+               u32 byte_offset:1;
+               u32 hmac_muting:1;
+               u32 feedback_mode:2;
+               u32 crypto_mode9_8:2;
+               u32 extended_seq_num:1;
+               u32 seq_num_mask:1;
+               u32 mutable_bit_proc:1;
+               u32 ip_version:1;
+               u32 copy_pad:1;
+               u32 copy_payload:1;
+               u32 copy_hdr:1;
+               u32 rsv1:1;
+       } bf;
+       u32 w;
+} __attribute__((packed));
+
+struct dynamic_sa_ctl {
+       u32 sa_contents;
+       union sa_command_0 sa_command_0;
+       union sa_command_1 sa_command_1;
+} __attribute__((packed));
+
+/**
+ * State Record for Security Association (SA)
+ */
+struct  sa_state_record {
+       u32 save_iv[4];
+       u32 save_hash_byte_cnt[2];
+       u32 save_digest[16];
+} __attribute__((packed));
+
+/**
+ * Security Association (SA) for AES128
+ *
+ */
+struct dynamic_sa_aes128 {
+       struct dynamic_sa_ctl   ctrl;
+       u32 key[4];
+       u32 iv[4]; /* for CBC, OFC, and CFB mode */
+       u32 state_ptr;
+       u32 reserved;
+} __attribute__((packed));
+
+#define SA_AES128_LEN          (sizeof(struct dynamic_sa_aes128)/4)
+#define SA_AES128_CONTENTS     0x3e000042
+
+/*
+ * Security Association (SA) for AES192
+ */
+struct dynamic_sa_aes192 {
+       struct dynamic_sa_ctl ctrl;
+       u32 key[6];
+       u32 iv[4]; /* for CBC, OFC, and CFB mode */
+       u32 state_ptr;
+       u32 reserved;
+} __attribute__((packed));
+
+#define SA_AES192_LEN          (sizeof(struct dynamic_sa_aes192)/4)
+#define SA_AES192_CONTENTS     0x3e000062
+
+/**
+ * Security Association (SA) for AES256
+ */
+struct dynamic_sa_aes256 {
+       struct dynamic_sa_ctl ctrl;
+       u32 key[8];
+       u32 iv[4]; /* for CBC, OFC, and CFB mode */
+       u32 state_ptr;
+       u32 reserved;
+} __attribute__((packed));
+
+#define SA_AES256_LEN          (sizeof(struct dynamic_sa_aes256)/4)
+#define SA_AES256_CONTENTS     0x3e000082
+#define SA_AES_CONTENTS                0x3e000002
+
+/**
+ * Security Association (SA) for HASH160: HMAC-SHA1
+ */
+struct dynamic_sa_hash160 {
+       struct dynamic_sa_ctl ctrl;
+       u32 inner_digest[5];
+       u32 outer_digest[5];
+       u32 state_ptr;
+       u32 reserved;
+} __attribute__((packed));
+#define SA_HASH160_LEN         (sizeof(struct dynamic_sa_hash160)/4)
+#define SA_HASH160_CONTENTS     0x2000a502
+
+#endif
index d0145ca27572890b958765c72795a2b291628c20..0949b43794a4896827dd70714d3b3b7b9a0ae6ed 100644 (file)
@@ -1402,6 +1402,7 @@ int compat_do_execve(char * filename,
        retval = mutex_lock_interruptible(&current->cred_exec_mutex);
        if (retval < 0)
                goto out_free;
+       current->in_execve = 1;
 
        retval = -ENOMEM;
        bprm->cred = prepare_exec_creds();
@@ -1454,6 +1455,7 @@ int compat_do_execve(char * filename,
                goto out;
 
        /* execve succeeded */
+       current->in_execve = 0;
        mutex_unlock(&current->cred_exec_mutex);
        acct_update_integrals(current);
        free_bprm(bprm);
@@ -1470,6 +1472,7 @@ out_file:
        }
 
 out_unlock:
+       current->in_execve = 0;
        mutex_unlock(&current->cred_exec_mutex);
 
 out_free:
index 929b58004b7eace02d784a2a4fbba17fd02b0533..b9f1c144b7a1037b6eb6e449812f75e204b1fed2 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -45,6 +45,7 @@
 #include <linux/proc_fs.h>
 #include <linux/mount.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/syscalls.h>
 #include <linux/tsacct_kern.h>
 #include <linux/cn_proc.h>
@@ -127,6 +128,9 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
                                 MAY_READ | MAY_EXEC | MAY_OPEN);
        if (error)
                goto exit;
+       error = ima_path_check(&nd.path, MAY_READ | MAY_EXEC | MAY_OPEN);
+       if (error)
+               goto exit;
 
        file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
        error = PTR_ERR(file);
@@ -672,6 +676,9 @@ struct file *open_exec(const char *name)
                goto out_path_put;
 
        err = inode_permission(nd.path.dentry->d_inode, MAY_EXEC | MAY_OPEN);
+       if (err)
+               goto out_path_put;
+       err = ima_path_check(&nd.path, MAY_EXEC | MAY_OPEN);
        if (err)
                goto out_path_put;
 
@@ -1182,6 +1189,9 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
        struct linux_binfmt *fmt;
 
        retval = security_bprm_check(bprm);
+       if (retval)
+               return retval;
+       retval = ima_bprm_check(bprm);
        if (retval)
                return retval;
 
@@ -1284,6 +1294,7 @@ int do_execve(char * filename,
        retval = mutex_lock_interruptible(&current->cred_exec_mutex);
        if (retval < 0)
                goto out_free;
+       current->in_execve = 1;
 
        retval = -ENOMEM;
        bprm->cred = prepare_exec_creds();
@@ -1337,6 +1348,7 @@ int do_execve(char * filename,
                goto out;
 
        /* execve succeeded */
+       current->in_execve = 0;
        mutex_unlock(&current->cred_exec_mutex);
        acct_update_integrals(current);
        free_bprm(bprm);
@@ -1355,6 +1367,7 @@ out_file:
        }
 
 out_unlock:
+       current->in_execve = 0;
        mutex_unlock(&current->cred_exec_mutex);
 
 out_free:
index bbeeac6efa1a85eca90bd59e85c620022502f3d7..da806aceae3f7c350c449821defb7647234c2840 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/eventpoll.h>
 #include <linux/rcupdate.h>
 #include <linux/mount.h>
@@ -279,6 +280,7 @@ void __fput(struct file *file)
        if (file->f_op && file->f_op->release)
                file->f_op->release(inode, file);
        security_file_free(file);
+       ima_file_free(file);
        if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL))
                cdev_put(inode->i_cdev);
        fops_put(file->f_op);
index 826fb0b9d1c38f4dbe4ad16c6ec938a7e31cdc88..643ac43e5a5c7d6f43fe938fd4c0df6c91e2e0ce 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/hash.h>
 #include <linux/swap.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/pagemap.h>
 #include <linux/cdev.h>
 #include <linux/bootmem.h>
@@ -147,13 +148,13 @@ struct inode *inode_init_always(struct super_block *sb, struct inode *inode)
        inode->i_cdev = NULL;
        inode->i_rdev = 0;
        inode->dirtied_when = 0;
-       if (security_inode_alloc(inode)) {
-               if (inode->i_sb->s_op->destroy_inode)
-                       inode->i_sb->s_op->destroy_inode(inode);
-               else
-                       kmem_cache_free(inode_cachep, (inode));
-               return NULL;
-       }
+
+       if (security_inode_alloc(inode))
+               goto out_free_inode;
+
+       /* allocate and initialize an i_integrity */
+       if (ima_inode_alloc(inode))
+               goto out_free_security;
 
        spin_lock_init(&inode->i_lock);
        lockdep_set_class(&inode->i_lock, &sb->s_type->i_lock_key);
@@ -189,6 +190,15 @@ struct inode *inode_init_always(struct super_block *sb, struct inode *inode)
        inode->i_mapping = mapping;
 
        return inode;
+
+out_free_security:
+       security_inode_free(inode);
+out_free_inode:
+       if (inode->i_sb->s_op->destroy_inode)
+               inode->i_sb->s_op->destroy_inode(inode);
+       else
+               kmem_cache_free(inode_cachep, (inode));
+       return NULL;
 }
 EXPORT_SYMBOL(inode_init_always);
 
@@ -1290,6 +1300,40 @@ sector_t bmap(struct inode * inode, sector_t block)
 }
 EXPORT_SYMBOL(bmap);
 
+/*
+ * With relative atime, only update atime if the previous atime is
+ * earlier than either the ctime or mtime or if at least a day has
+ * passed since the last atime update.
+ */
+static int relatime_need_update(struct vfsmount *mnt, struct inode *inode,
+                            struct timespec now)
+{
+
+       if (!(mnt->mnt_flags & MNT_RELATIME))
+               return 1;
+       /*
+        * Is mtime younger than atime? If yes, update atime:
+        */
+       if (timespec_compare(&inode->i_mtime, &inode->i_atime) >= 0)
+               return 1;
+       /*
+        * Is ctime younger than atime? If yes, update atime:
+        */
+       if (timespec_compare(&inode->i_ctime, &inode->i_atime) >= 0)
+               return 1;
+
+       /*
+        * Is the previous atime value older than a day? If yes,
+        * update atime:
+        */
+       if ((long)(now.tv_sec - inode->i_atime.tv_sec) >= 24*60*60)
+               return 1;
+       /*
+        * Good, we can skip the atime update:
+        */
+       return 0;
+}
+
 /**
  *     touch_atime     -       update the access time
  *     @mnt: mount the inode is accessed on
@@ -1317,17 +1361,12 @@ void touch_atime(struct vfsmount *mnt, struct dentry *dentry)
                goto out;
        if ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode))
                goto out;
-       if (mnt->mnt_flags & MNT_RELATIME) {
-               /*
-                * With relative atime, only update atime if the previous
-                * atime is earlier than either the ctime or mtime.
-                */
-               if (timespec_compare(&inode->i_mtime, &inode->i_atime) < 0 &&
-                   timespec_compare(&inode->i_ctime, &inode->i_atime) < 0)
-                       goto out;
-       }
 
        now = current_fs_time(inode->i_sb);
+
+       if (!relatime_need_update(mnt, inode, now))
+               goto out;
+
        if (timespec_equal(&inode->i_atime, &now))
                goto out;
 
index bbc15c237558f82c7fbe2b661e55ee8fa1b9849d..199317642ad6d731f47dd61693a2374145f99337 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/fsnotify.h>
 #include <linux/personality.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/syscalls.h>
 #include <linux/mount.h>
 #include <linux/audit.h>
@@ -850,6 +851,8 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
                if (err == -EAGAIN)
                        err = inode_permission(nd->path.dentry->d_inode,
                                               MAY_EXEC);
+               if (!err)
+                       err = ima_path_check(&nd->path, MAY_EXEC);
                if (err)
                        break;
 
@@ -1509,6 +1512,11 @@ int may_open(struct path *path, int acc_mode, int flag)
        error = inode_permission(inode, acc_mode);
        if (error)
                return error;
+
+       error = ima_path_check(path,
+                              acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));
+       if (error)
+               return error;
        /*
         * An append-only file must be opened in append mode for writing.
         */
index 06f8e63f6cb1dc929e0ad9c0627320066db57cb1..f0e753097353b93f581d1ea6e1646df99dcf630a 100644 (file)
@@ -780,6 +780,7 @@ static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt)
                { MNT_NOATIME, ",noatime" },
                { MNT_NODIRATIME, ",nodiratime" },
                { MNT_RELATIME, ",relatime" },
+               { MNT_STRICTATIME, ",strictatime" },
                { 0, NULL }
        };
        const struct proc_fs_info *fs_infop;
@@ -1919,6 +1920,9 @@ long do_mount(char *dev_name, char *dir_name, char *type_page,
        if (data_page)
                ((char *)data_page)[PAGE_SIZE - 1] = 0;
 
+       /* Default to relatime */
+       mnt_flags |= MNT_RELATIME;
+
        /* Separate the per-mountpoint flags */
        if (flags & MS_NOSUID)
                mnt_flags |= MNT_NOSUID;
@@ -1930,13 +1934,14 @@ long do_mount(char *dev_name, char *dir_name, char *type_page,
                mnt_flags |= MNT_NOATIME;
        if (flags & MS_NODIRATIME)
                mnt_flags |= MNT_NODIRATIME;
-       if (flags & MS_RELATIME)
-               mnt_flags |= MNT_RELATIME;
+       if (flags & MS_STRICTATIME)
+               mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);
        if (flags & MS_RDONLY)
                mnt_flags |= MNT_READONLY;
 
        flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE |
-                  MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT);
+                  MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
+                  MS_STRICTATIME);
 
        /* ... and get the mountpoint */
        retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
index 656a4c66a568f39cf769210bbfaab6d7dc6c7a07..7524ba3b6f3c8f2c028215e926afd960297a3728 100644 (file)
 #define AES_MAX_KEYLENGTH      (15 * 16)
 #define AES_MAX_KEYLENGTH_U32  (AES_MAX_KEYLENGTH / sizeof(u32))
 
+/*
+ * Please ensure that the first two fields are 16-byte aligned
+ * relative to the start of the structure, i.e., don't move them!
+ */
 struct crypto_aes_ctx {
-       u32 key_length;
        u32 key_enc[AES_MAX_KEYLENGTH_U32];
        u32 key_dec[AES_MAX_KEYLENGTH_U32];
+       u32 key_length;
 };
 
 extern const u32 crypto_ft_tab[4][256];
diff --git a/include/crypto/compress.h b/include/crypto/compress.h
new file mode 100644 (file)
index 0000000..86163ef
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Compress: Compression algorithms under the cryptographic API.
+ *
+ * Copyright 2008 Sony Corporation
+ *
+ * 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.
+ *
+ * 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 _CRYPTO_COMPRESS_H
+#define _CRYPTO_COMPRESS_H
+
+#include <linux/crypto.h>
+
+
+struct comp_request {
+       const void *next_in;            /* next input byte */
+       void *next_out;                 /* next output byte */
+       unsigned int avail_in;          /* bytes available at next_in */
+       unsigned int avail_out;         /* bytes available at next_out */
+};
+
+enum zlib_comp_params {
+       ZLIB_COMP_LEVEL = 1,            /* e.g. Z_DEFAULT_COMPRESSION */
+       ZLIB_COMP_METHOD,               /* e.g. Z_DEFLATED */
+       ZLIB_COMP_WINDOWBITS,           /* e.g. MAX_WBITS */
+       ZLIB_COMP_MEMLEVEL,             /* e.g. DEF_MEM_LEVEL */
+       ZLIB_COMP_STRATEGY,             /* e.g. Z_DEFAULT_STRATEGY */
+       __ZLIB_COMP_MAX,
+};
+
+#define ZLIB_COMP_MAX  (__ZLIB_COMP_MAX - 1)
+
+
+enum zlib_decomp_params {
+       ZLIB_DECOMP_WINDOWBITS = 1,     /* e.g. DEF_WBITS */
+       __ZLIB_DECOMP_MAX,
+};
+
+#define ZLIB_DECOMP_MAX        (__ZLIB_DECOMP_MAX - 1)
+
+
+struct crypto_pcomp {
+       struct crypto_tfm base;
+};
+
+struct pcomp_alg {
+       int (*compress_setup)(struct crypto_pcomp *tfm, void *params,
+                             unsigned int len);
+       int (*compress_init)(struct crypto_pcomp *tfm);
+       int (*compress_update)(struct crypto_pcomp *tfm,
+                              struct comp_request *req);
+       int (*compress_final)(struct crypto_pcomp *tfm,
+                             struct comp_request *req);
+       int (*decompress_setup)(struct crypto_pcomp *tfm, void *params,
+                               unsigned int len);
+       int (*decompress_init)(struct crypto_pcomp *tfm);
+       int (*decompress_update)(struct crypto_pcomp *tfm,
+                                struct comp_request *req);
+       int (*decompress_final)(struct crypto_pcomp *tfm,
+                               struct comp_request *req);
+
+       struct crypto_alg base;
+};
+
+extern struct crypto_pcomp *crypto_alloc_pcomp(const char *alg_name, u32 type,
+                                              u32 mask);
+
+static inline struct crypto_tfm *crypto_pcomp_tfm(struct crypto_pcomp *tfm)
+{
+       return &tfm->base;
+}
+
+static inline void crypto_free_pcomp(struct crypto_pcomp *tfm)
+{
+       crypto_destroy_tfm(tfm, crypto_pcomp_tfm(tfm));
+}
+
+static inline struct pcomp_alg *__crypto_pcomp_alg(struct crypto_alg *alg)
+{
+       return container_of(alg, struct pcomp_alg, base);
+}
+
+static inline struct pcomp_alg *crypto_pcomp_alg(struct crypto_pcomp *tfm)
+{
+       return __crypto_pcomp_alg(crypto_pcomp_tfm(tfm)->__crt_alg);
+}
+
+static inline int crypto_compress_setup(struct crypto_pcomp *tfm,
+                                       void *params, unsigned int len)
+{
+       return crypto_pcomp_alg(tfm)->compress_setup(tfm, params, len);
+}
+
+static inline int crypto_compress_init(struct crypto_pcomp *tfm)
+{
+       return crypto_pcomp_alg(tfm)->compress_init(tfm);
+}
+
+static inline int crypto_compress_update(struct crypto_pcomp *tfm,
+                                        struct comp_request *req)
+{
+       return crypto_pcomp_alg(tfm)->compress_update(tfm, req);
+}
+
+static inline int crypto_compress_final(struct crypto_pcomp *tfm,
+                                       struct comp_request *req)
+{
+       return crypto_pcomp_alg(tfm)->compress_final(tfm, req);
+}
+
+static inline int crypto_decompress_setup(struct crypto_pcomp *tfm,
+                                         void *params, unsigned int len)
+{
+       return crypto_pcomp_alg(tfm)->decompress_setup(tfm, params, len);
+}
+
+static inline int crypto_decompress_init(struct crypto_pcomp *tfm)
+{
+       return crypto_pcomp_alg(tfm)->decompress_init(tfm);
+}
+
+static inline int crypto_decompress_update(struct crypto_pcomp *tfm,
+                                          struct comp_request *req)
+{
+       return crypto_pcomp_alg(tfm)->decompress_update(tfm, req);
+}
+
+static inline int crypto_decompress_final(struct crypto_pcomp *tfm,
+                                         struct comp_request *req)
+{
+       return crypto_pcomp_alg(tfm)->decompress_final(tfm, req);
+}
+
+#endif /* _CRYPTO_COMPRESS_H */
diff --git a/include/crypto/cryptd.h b/include/crypto/cryptd.h
new file mode 100644 (file)
index 0000000..55fa7bb
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Software async crypto daemon
+ */
+
+#ifndef _CRYPTO_CRYPT_H
+#define _CRYPTO_CRYPT_H
+
+#include <linux/crypto.h>
+#include <linux/kernel.h>
+
+struct cryptd_ablkcipher {
+       struct crypto_ablkcipher base;
+};
+
+static inline struct cryptd_ablkcipher *__cryptd_ablkcipher_cast(
+       struct crypto_ablkcipher *tfm)
+{
+       return (struct cryptd_ablkcipher *)tfm;
+}
+
+/* alg_name should be algorithm to be cryptd-ed */
+struct cryptd_ablkcipher *cryptd_alloc_ablkcipher(const char *alg_name,
+                                                 u32 type, u32 mask);
+struct crypto_blkcipher *cryptd_ablkcipher_child(struct cryptd_ablkcipher *tfm);
+void cryptd_free_ablkcipher(struct cryptd_ablkcipher *tfm);
+
+#endif
diff --git a/include/crypto/crypto_wq.h b/include/crypto/crypto_wq.h
new file mode 100644 (file)
index 0000000..a7d252d
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef CRYPTO_WQ_H
+#define CRYPTO_WQ_H
+
+#include <linux/workqueue.h>
+
+extern struct workqueue_struct *kcrypto_wq;
+#endif
index d797e119e3d5e9677a60457670ed097982916691..d56bb71617c315e5cf13505f1ddc32c0cf698377 100644 (file)
@@ -231,6 +231,11 @@ static inline unsigned int crypto_shash_alignmask(
        return crypto_tfm_alg_alignmask(crypto_shash_tfm(tfm));
 }
 
+static inline unsigned int crypto_shash_blocksize(struct crypto_shash *tfm)
+{
+       return crypto_tfm_alg_blocksize(crypto_shash_tfm(tfm));
+}
+
 static inline struct shash_alg *__crypto_shash_alg(struct crypto_alg *alg)
 {
        return container_of(alg, struct shash_alg, base);
diff --git a/include/crypto/internal/compress.h b/include/crypto/internal/compress.h
new file mode 100644 (file)
index 0000000..178a888
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Compress: Compression algorithms under the cryptographic API.
+ *
+ * Copyright 2008 Sony Corporation
+ *
+ * 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.
+ *
+ * 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 _CRYPTO_INTERNAL_COMPRESS_H
+#define _CRYPTO_INTERNAL_COMPRESS_H
+
+#include <crypto/compress.h>
+
+extern int crypto_register_pcomp(struct pcomp_alg *alg);
+extern int crypto_unregister_pcomp(struct pcomp_alg *alg);
+
+#endif /* _CRYPTO_INTERNAL_COMPRESS_H */
index 67e5dbfc296140fe4825490ac952773921516c3b..4fa2810b675e6a157293a0aa431e1af95d71d99b 100644 (file)
@@ -36,7 +36,8 @@
  * 1500 - 1599 kernel LSPP events
  * 1600 - 1699 kernel crypto events
  * 1700 - 1799 kernel anomaly records
- * 1800 - 1999 future kernel use (maybe integrity labels and related events)
+ * 1800 - 1899 kernel integrity events
+ * 1900 - 1999 future kernel use
  * 2000 is for otherwise unclassified kernel audit messages (legacy)
  * 2001 - 2099 unused (kernel)
  * 2100 - 2199 user space anomaly records
 #define AUDIT_LAST_KERN_ANOM_MSG    1799
 #define AUDIT_ANOM_PROMISCUOUS      1700 /* Device changed promiscuous mode */
 #define AUDIT_ANOM_ABEND            1701 /* Process ended abnormally */
+#define AUDIT_INTEGRITY_DATA       1800 /* Data integrity verification */
+#define AUDIT_INTEGRITY_METADATA    1801 /* Metadata integrity verification */
+#define AUDIT_INTEGRITY_STATUS     1802 /* Integrity enable status */
+#define AUDIT_INTEGRITY_HASH       1803 /* Integrity HASH type */
+#define AUDIT_INTEGRITY_PCR        1804 /* PCR invalidation msgs */
+#define AUDIT_INTEGRITY_RULE       1805 /* policy rule */
 
 #define AUDIT_KERNEL           2000    /* Asynchronous audit record. NOT A REQUEST. */
 
index 1f2e9020acc667d5f0dd0f2e501e6f85f0fff140..ec29fa268b94e278e5125380f8e2b1faa20c10c9 100644 (file)
@@ -40,6 +40,7 @@
 #define CRYPTO_ALG_TYPE_SHASH          0x00000009
 #define CRYPTO_ALG_TYPE_AHASH          0x0000000a
 #define CRYPTO_ALG_TYPE_RNG            0x0000000c
+#define CRYPTO_ALG_TYPE_PCOMPRESS      0x0000000f
 
 #define CRYPTO_ALG_TYPE_HASH_MASK      0x0000000e
 #define CRYPTO_ALG_TYPE_AHASH_MASK     0x0000000c
@@ -548,9 +549,6 @@ struct crypto_attr_u32 {
  * Transform user interface.
  */
  
-struct crypto_tfm *crypto_alloc_tfm(const char *alg_name,
-                                   const struct crypto_type *frontend,
-                                   u32 type, u32 mask);
 struct crypto_tfm *crypto_alloc_base(const char *alg_name, u32 type, u32 mask);
 void crypto_destroy_tfm(void *mem, struct crypto_tfm *tfm);
 
index 92734c0012e679cb3326ba25be08f52dce81fbc8..5bc81c4a98c1913cb0e15adf8d70e9c083956bb8 100644 (file)
@@ -141,6 +141,7 @@ struct inodes_stat_t {
 #define MS_RELATIME    (1<<21) /* Update atime relative to mtime/ctime. */
 #define MS_KERNMOUNT   (1<<22) /* this is a kern_mount call */
 #define MS_I_VERSION   (1<<23) /* Update inode I_version field */
+#define MS_STRICTATIME (1<<24) /* Always perform atime updates */
 #define MS_ACTIVE      (1<<30)
 #define MS_NOUSER      (1<<31)
 
diff --git a/include/linux/ima.h b/include/linux/ima.h
new file mode 100644 (file)
index 0000000..0e2aa45
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ * Author: Mimi Zohar <zohar@us.ibm.com>
+ *
+ * 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 _LINUX_IMA_H
+#define _LINUX_IMA_H
+
+#include <linux/fs.h>
+struct linux_binprm;
+
+#ifdef CONFIG_IMA
+extern int ima_bprm_check(struct linux_binprm *bprm);
+extern int ima_inode_alloc(struct inode *inode);
+extern void ima_inode_free(struct inode *inode);
+extern int ima_path_check(struct path *path, int mask);
+extern void ima_file_free(struct file *file);
+extern int ima_file_mmap(struct file *file, unsigned long prot);
+extern void ima_shm_check(struct file *file);
+
+#else
+static inline int ima_bprm_check(struct linux_binprm *bprm)
+{
+       return 0;
+}
+
+static inline int ima_inode_alloc(struct inode *inode)
+{
+       return 0;
+}
+
+static inline void ima_inode_free(struct inode *inode)
+{
+       return;
+}
+
+static inline int ima_path_check(struct path *path, int mask)
+{
+       return 0;
+}
+
+static inline void ima_file_free(struct file *file)
+{
+       return;
+}
+
+static inline int ima_file_mmap(struct file *file, unsigned long prot)
+{
+       return 0;
+}
+
+static inline void ima_shm_check(struct file *file)
+{
+       return;
+}
+#endif /* CONFIG_IMA_H */
+#endif /* _LINUX_IMA_H */
index cab2a85e2ee82f556cf1515eb14bd6d88ad344b0..51f55f903aff5140b9cda4ca104df8d3e2cb4426 100644 (file)
@@ -27,6 +27,7 @@ struct mnt_namespace;
 #define MNT_NODIRATIME 0x10
 #define MNT_RELATIME   0x20
 #define MNT_READONLY   0x40    /* does the user want this to be r/o? */
+#define MNT_STRICTATIME 0x80
 
 #define MNT_SHRINKABLE 0x100
 #define MNT_IMBALANCED_WRITE_COUNT     0x200 /* just for debugging */
index 011db2f4c94c056595625d19b452adf7c6f65625..2c36f62e754441901e06600ae89cd3de1c4cd4c6 100644 (file)
@@ -1175,6 +1175,8 @@ struct task_struct {
        /* ??? */
        unsigned int personality;
        unsigned did_exec:1;
+       unsigned in_execve:1;   /* Tell the LSMs that the process is doing an
+                                * execve */
        pid_t pid;
        pid_t tgid;
 
diff --git a/include/linux/timeriomem-rng.h b/include/linux/timeriomem-rng.h
new file mode 100644 (file)
index 0000000..dd25317
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * linux/include/linux/timeriomem-rng.h
+ *
+ * Copyright (c) 2009 Alexander Clouter <alex@digriz.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/completion.h>
+
+struct timeriomem_rng_data {
+       struct completion       completion;
+       unsigned int            present:1;
+
+       u32 __iomem             *address;
+
+       /* measures in usecs */
+       unsigned int            period;
+};
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
new file mode 100644 (file)
index 0000000..3338b3f
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2004,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ * Debora Velarde <dvelarde@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ */
+#ifndef __LINUX_TPM_H__
+#define __LINUX_TPM_H__
+
+/*
+ * Chip num is this value or a valid tpm idx
+ */
+#define        TPM_ANY_NUM 0xFFFF
+
+#if defined(CONFIG_TCG_TPM)
+
+extern int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf);
+extern int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash);
+#endif
+#endif
index 05d51d2a792c116630f8d4d1cd28bbdcf754c81f..f239d87e0d37eea4a83106804035432f6c91c331 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -39,6 +39,7 @@
 #include <linux/nsproxy.h>
 #include <linux/mount.h>
 #include <linux/ipc_namespace.h>
+#include <linux/ima.h>
 
 #include <asm/uaccess.h>
 
@@ -383,6 +384,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
        error = PTR_ERR(file);
        if (IS_ERR(file))
                goto no_file;
+       ima_shm_check(file);
 
        id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
        if (id < 0) {
@@ -887,6 +889,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
        file = alloc_file(path.mnt, path.dentry, f_mode, &shm_file_operations);
        if (!file)
                goto out_free;
+       ima_shm_check(file);
 
        file->private_data = sfd;
        file->f_mapping = shp->shm_file->f_mapping;
index fbb300e6191f09d376ed26abb330265fe51bf0a7..850e0ba41c1e60f7983f2c3e7890bee4b2bec53c 100644 (file)
@@ -20,7 +20,7 @@
 
 struct user_namespace init_user_ns = {
        .kref = {
-               .refcount       = ATOMIC_INIT(1),
+               .refcount       = ATOMIC_INIT(2),
        },
        .creator = &root_user,
 };
index 03c2c24b9083c17aff9372224ec2b22912691ffb..cea9e30a88ff2b26f5b2535f31f227c91ac5625a 100644 (file)
@@ -174,4 +174,10 @@ config DISABLE_OBSOLETE_CPUMASK_FUNCTIONS
        bool "Disable obsolete cpumask functions" if DEBUG_PER_CPU_MAPS
        depends on EXPERIMENTAL && BROKEN
 
+#
+# Netlink attribute parsing support is select'ed if needed
+#
+config NLATTR
+       bool
+
 endmenu
index 32b0e64ded27d7c1f614fdc974d0857abfc19bbc..b2c09da02cae13fa974ec949600dee89ff7a0f66 100644 (file)
@@ -84,6 +84,8 @@ obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o
 
 obj-$(CONFIG_DYNAMIC_PRINTK_DEBUG) += dynamic_printk.o
 
+obj-$(CONFIG_NLATTR) += nlattr.o
+
 hostprogs-y    := gen_crc32table
 clean-files    := crc32table.h
 
similarity index 99%
rename from net/netlink/attr.c
rename to lib/nlattr.c
index 56c3ce7fe29af8b2bf47dd8bdc41edb93576c70a..80009a24e21dd553a6204310b083ccd5e8bf6cf3 100644 (file)
@@ -281,6 +281,7 @@ int nla_strcmp(const struct nlattr *nla, const char *str)
        return d;
 }
 
+#ifdef CONFIG_NET
 /**
  * __nla_reserve - reserve room for attribute on the skb
  * @skb: socket buffer to reserve room on
@@ -305,6 +306,7 @@ struct nlattr *__nla_reserve(struct sk_buff *skb, int attrtype, int attrlen)
 
        return nla;
 }
+EXPORT_SYMBOL(__nla_reserve);
 
 /**
  * __nla_reserve_nohdr - reserve room for attribute without header
@@ -325,6 +327,7 @@ void *__nla_reserve_nohdr(struct sk_buff *skb, int attrlen)
 
        return start;
 }
+EXPORT_SYMBOL(__nla_reserve_nohdr);
 
 /**
  * nla_reserve - reserve room for attribute on the skb
@@ -345,6 +348,7 @@ struct nlattr *nla_reserve(struct sk_buff *skb, int attrtype, int attrlen)
 
        return __nla_reserve(skb, attrtype, attrlen);
 }
+EXPORT_SYMBOL(nla_reserve);
 
 /**
  * nla_reserve_nohdr - reserve room for attribute without header
@@ -363,6 +367,7 @@ void *nla_reserve_nohdr(struct sk_buff *skb, int attrlen)
 
        return __nla_reserve_nohdr(skb, attrlen);
 }
+EXPORT_SYMBOL(nla_reserve_nohdr);
 
 /**
  * __nla_put - Add a netlink attribute to a socket buffer
@@ -382,6 +387,7 @@ void __nla_put(struct sk_buff *skb, int attrtype, int attrlen,
        nla = __nla_reserve(skb, attrtype, attrlen);
        memcpy(nla_data(nla), data, attrlen);
 }
+EXPORT_SYMBOL(__nla_put);
 
 /**
  * __nla_put_nohdr - Add a netlink attribute without header
@@ -399,6 +405,7 @@ void __nla_put_nohdr(struct sk_buff *skb, int attrlen, const void *data)
        start = __nla_reserve_nohdr(skb, attrlen);
        memcpy(start, data, attrlen);
 }
+EXPORT_SYMBOL(__nla_put_nohdr);
 
 /**
  * nla_put - Add a netlink attribute to a socket buffer
@@ -418,6 +425,7 @@ int nla_put(struct sk_buff *skb, int attrtype, int attrlen, const void *data)
        __nla_put(skb, attrtype, attrlen, data);
        return 0;
 }
+EXPORT_SYMBOL(nla_put);
 
 /**
  * nla_put_nohdr - Add a netlink attribute without header
@@ -436,6 +444,7 @@ int nla_put_nohdr(struct sk_buff *skb, int attrlen, const void *data)
        __nla_put_nohdr(skb, attrlen, data);
        return 0;
 }
+EXPORT_SYMBOL(nla_put_nohdr);
 
 /**
  * nla_append - Add a netlink attribute without header or padding
@@ -454,20 +463,13 @@ int nla_append(struct sk_buff *skb, int attrlen, const void *data)
        memcpy(skb_put(skb, attrlen), data, attrlen);
        return 0;
 }
+EXPORT_SYMBOL(nla_append);
+#endif
 
 EXPORT_SYMBOL(nla_validate);
 EXPORT_SYMBOL(nla_parse);
 EXPORT_SYMBOL(nla_find);
 EXPORT_SYMBOL(nla_strlcpy);
-EXPORT_SYMBOL(__nla_reserve);
-EXPORT_SYMBOL(__nla_reserve_nohdr);
-EXPORT_SYMBOL(nla_reserve);
-EXPORT_SYMBOL(nla_reserve_nohdr);
-EXPORT_SYMBOL(__nla_put);
-EXPORT_SYMBOL(__nla_put_nohdr);
-EXPORT_SYMBOL(nla_put);
-EXPORT_SYMBOL(nla_put_nohdr);
 EXPORT_SYMBOL(nla_memcpy);
 EXPORT_SYMBOL(nla_memcmp);
 EXPORT_SYMBOL(nla_strcmp);
-EXPORT_SYMBOL(nla_append);
index 00ced3ee49a8cd393d199656fc816980d4e7b1e6..1abb9185a686d2787125c7af61120b68e726ccb5 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -20,6 +20,7 @@
 #include <linux/fs.h>
 #include <linux/personality.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/hugetlb.h>
 #include <linux/profile.h>
 #include <linux/module.h>
@@ -1047,6 +1048,9 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
        }
 
        error = security_file_mmap(file, reqprot, prot, flags, addr, 0);
+       if (error)
+               return error;
+       error = ima_file_mmap(file, prot);
        if (error)
                return error;
 
index 74dc57c74349ff124dd31a3e04531ee503ecedca..40ca7cdb653ec280d8115ff3d8b35cb6ee57669e 100644 (file)
@@ -66,7 +66,7 @@ static inline long sync_writeback_pages(void)
 /*
  * Start background writeback (via pdflush) at this percentage
  */
-int dirty_background_ratio = 5;
+int dirty_background_ratio = 10;
 
 /*
  * dirty_background_bytes starts at 0 (disabled) so that it is a function of
@@ -83,7 +83,7 @@ int vm_highmem_is_dirtyable;
 /*
  * The generator of dirty data starts writeback at this percentage
  */
-int vm_dirty_ratio = 10;
+int vm_dirty_ratio = 20;
 
 /*
  * vm_dirty_bytes starts at 0 (disabled) so that it is a function of
index 4103a239ce843326faca51c61c5b0b105797b24b..7ec78e24a30dcc549670b88643078509bbd12e4d 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/swap.h>
+#include <linux/ima.h>
 
 static struct vfsmount *shm_mnt;
 
@@ -2665,6 +2666,7 @@ int shmem_zero_setup(struct vm_area_struct *vma)
        if (IS_ERR(file))
                return PTR_ERR(file);
 
+       ima_shm_check(file);
        if (vma->vm_file)
                fput(vma->vm_file);
        vma->vm_file = file;
index cdb8fdef6c4aaac30b1ba6cf5ace134ea6bfcb89..eab40a481356cdeebafe444b304c0ef83a0de8cd 100644 (file)
@@ -4,6 +4,7 @@
 
 menuconfig NET
        bool "Networking support"
+       select NLATTR
        ---help---
          Unless you really know what you are doing, you should say Y here.
          The reason is that some programs need kernel networking support even
index e3589c2de49e9b5e6c76c1d4a4e02a58dda5dd46..bdd6ddf4e95beb401ddd883f7caa00b9be389b9d 100644 (file)
@@ -2,4 +2,4 @@
 # Makefile for the netlink driver.
 #
 
-obj-y                                  := af_netlink.o attr.o genetlink.o
+obj-y                                  := af_netlink.o genetlink.o
index 9438535d7fd0f2bec51905538bd27b7f73f3ad25..bb244774e9d765ae10e41768f5dfd5c40f1f3069 100644 (file)
@@ -55,7 +55,8 @@ config SECURITYFS
        bool "Enable the securityfs filesystem"
        help
          This will build the securityfs filesystem.  It is currently used by
-         the TPM bios character driver.  It is not used by SELinux or SMACK.
+         the TPM bios character driver and IMA, an integrity provider.  It is
+         not used by SELinux or SMACK.
 
          If you are unsure how to answer this question, answer N.
 
@@ -134,6 +135,9 @@ config SECURITY_DEFAULT_MMAP_MIN_ADDR
 
 source security/selinux/Kconfig
 source security/smack/Kconfig
+source security/tomoyo/Kconfig
+
+source security/integrity/ima/Kconfig
 
 endmenu
 
index c05c127fff9a795d9b3c21d7c1a510c6b7a5a4cc..fa77021d9778ac1f32d7ec10e5322a0e6825e5c9 100644 (file)
@@ -5,6 +5,7 @@
 obj-$(CONFIG_KEYS)                     += keys/
 subdir-$(CONFIG_SECURITY_SELINUX)      += selinux
 subdir-$(CONFIG_SECURITY_SMACK)                += smack
+subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
 
 # always enable default capabilities
 obj-y          += commoncap.o
@@ -15,5 +16,10 @@ obj-$(CONFIG_SECURITYFS)             += inode.o
 # Must precede capability.o in order to stack properly.
 obj-$(CONFIG_SECURITY_SELINUX)         += selinux/built-in.o
 obj-$(CONFIG_SECURITY_SMACK)           += smack/built-in.o
+obj-$(CONFIG_SECURITY_TOMOYO)          += tomoyo/built-in.o
 obj-$(CONFIG_SECURITY_ROOTPLUG)                += root_plug.o
 obj-$(CONFIG_CGROUP_DEVICE)            += device_cgroup.o
+
+# Object integrity file lists
+subdir-$(CONFIG_IMA)                   += integrity/ima
+obj-$(CONFIG_IMA)                      += integrity/ima/built-in.o
index 007ef252dde7b966661f2d1f9d8df4f47cb72927..f3b91bfbe4cb9483ea55e7e64c5c1c52b8c71d0d 100644 (file)
@@ -202,12 +202,11 @@ static int create_by_name(const char *name, mode_t mode,
  * This function returns a pointer to a dentry if it succeeds.  This
  * pointer must be passed to the securityfs_remove() function when the file is
  * to be removed (no automatic cleanup happens if your module is unloaded,
- * you are responsible here).  If an error occurs, %NULL is returned.
+ * you are responsible here).  If an error occurs, the function will return
+ * the erorr value (via ERR_PTR).
  *
  * If securityfs is not enabled in the kernel, the value %-ENODEV is
- * returned.  It is not wise to check for this value, but rather, check for
- * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
- * code.
+ * returned.
  */
 struct dentry *securityfs_create_file(const char *name, mode_t mode,
                                   struct dentry *parent, void *data,
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
new file mode 100644 (file)
index 0000000..53d9764
--- /dev/null
@@ -0,0 +1,55 @@
+# IBM Integrity Measurement Architecture
+#
+config IMA
+       bool "Integrity Measurement Architecture(IMA)"
+       depends on ACPI
+       select SECURITYFS
+       select CRYPTO
+       select CRYPTO_HMAC
+       select CRYPTO_MD5
+       select CRYPTO_SHA1
+       select TCG_TPM
+       select TCG_TIS
+       help
+         The Trusted Computing Group(TCG) runtime Integrity
+         Measurement Architecture(IMA) maintains a list of hash
+         values of executables and other sensitive system files,
+         as they are read or executed. If an attacker manages
+         to change the contents of an important system file
+         being measured, we can tell.
+
+         If your system has a TPM chip, then IMA also maintains
+         an aggregate integrity value over this list inside the
+         TPM hardware, so that the TPM can prove to a third party
+         whether or not critical system files have been modified.
+         Read <http://www.usenix.org/events/sec04/tech/sailer.html>
+         to learn more about IMA.
+         If unsure, say N.
+
+config IMA_MEASURE_PCR_IDX
+       int
+       depends on IMA
+       range 8 14
+       default 10
+       help
+         IMA_MEASURE_PCR_IDX determines the TPM PCR register index
+         that IMA uses to maintain the integrity aggregate of the
+         measurement list.  If unsure, use the default 10.
+
+config IMA_AUDIT
+       bool
+       depends on IMA
+       default y
+       help
+         This option adds a kernel parameter 'ima_audit', which
+         allows informational auditing messages to be enabled
+         at boot.  If this option is selected, informational integrity
+         auditing messages can be enabled with 'ima_audit=1' on
+         the kernel command line.
+
+config IMA_LSM_RULES
+       bool
+       depends on IMA && AUDIT && (SECURITY_SELINUX || SECURITY_SMACK)
+       default y
+       help
+         Disabling this option will disregard LSM based policy rules.
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
new file mode 100644 (file)
index 0000000..787c4cb
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Makefile for building Trusted Computing Group's(TCG) runtime Integrity
+# Measurement Architecture(IMA).
+#
+
+obj-$(CONFIG_IMA) += ima.o
+
+ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
+        ima_policy.o ima_iint.o ima_audit.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
new file mode 100644 (file)
index 0000000..165eb53
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * 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.
+ *
+ * File: ima.h
+ *     internal Integrity Measurement Architecture (IMA) definitions
+ */
+
+#ifndef __LINUX_IMA_H
+#define __LINUX_IMA_H
+
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <linux/security.h>
+#include <linux/hash.h>
+#include <linux/tpm.h>
+#include <linux/audit.h>
+
+enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
+enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
+
+/* digest size for IMA, fits SHA1 or MD5 */
+#define IMA_DIGEST_SIZE                20
+#define IMA_EVENT_NAME_LEN_MAX 255
+
+#define IMA_HASH_BITS 9
+#define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
+
+/* set during initialization */
+extern int ima_initialized;
+extern int ima_used_chip;
+extern char *ima_hash;
+
+/* IMA inode template definition */
+struct ima_template_data {
+       u8 digest[IMA_DIGEST_SIZE];     /* sha1/md5 measurement hash */
+       char file_name[IMA_EVENT_NAME_LEN_MAX + 1];     /* name + \0 */
+};
+
+struct ima_template_entry {
+       u8 digest[IMA_DIGEST_SIZE];     /* sha1 or md5 measurement hash */
+       const char *template_name;
+       int template_len;
+       struct ima_template_data template;
+};
+
+struct ima_queue_entry {
+       struct hlist_node hnext;        /* place in hash collision list */
+       struct list_head later;         /* place in ima_measurements list */
+       struct ima_template_entry *entry;
+};
+extern struct list_head ima_measurements;      /* list of all measurements */
+
+/* declarations */
+void integrity_audit_msg(int audit_msgno, struct inode *inode,
+                        const unsigned char *fname, const char *op,
+                        const char *cause, int result, int info);
+
+/* Internal IMA function definitions */
+void ima_iintcache_init(void);
+int ima_init(void);
+void ima_cleanup(void);
+int ima_fs_init(void);
+void ima_fs_cleanup(void);
+int ima_add_template_entry(struct ima_template_entry *entry, int violation,
+                          const char *op, struct inode *inode);
+int ima_calc_hash(struct file *file, char *digest);
+int ima_calc_template_hash(int template_len, void *template, char *digest);
+int ima_calc_boot_aggregate(char *digest);
+void ima_add_violation(struct inode *inode, const unsigned char *filename,
+                      const char *op, const char *cause);
+
+/*
+ * used to protect h_table and sha_table
+ */
+extern spinlock_t ima_queue_lock;
+
+struct ima_h_table {
+       atomic_long_t len;      /* number of stored measurements in the list */
+       atomic_long_t violations;
+       struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
+};
+extern struct ima_h_table ima_htable;
+
+static inline unsigned long ima_hash_key(u8 *digest)
+{
+       return hash_long(*digest, IMA_HASH_BITS);
+}
+
+/* iint cache flags */
+#define IMA_MEASURED           1
+#define IMA_IINT_DUMP_STACK    512
+
+/* integrity data associated with an inode */
+struct ima_iint_cache {
+       u64 version;            /* track inode changes */
+       unsigned long flags;
+       u8 digest[IMA_DIGEST_SIZE];
+       struct mutex mutex;     /* protects: version, flags, digest */
+       long readcount;         /* measured files readcount */
+       long writecount;        /* measured files writecount */
+       long opencount;         /* opens reference count */
+       struct kref refcount;   /* ima_iint_cache reference count */
+       struct rcu_head rcu;
+};
+
+/* LIM API function definitions */
+int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
+                    int mask, int function);
+int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file);
+void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
+                          const unsigned char *filename);
+int ima_store_template(struct ima_template_entry *entry, int violation,
+                      struct inode *inode);
+void ima_template_show(struct seq_file *m, void *e,
+                      enum ima_show_type show);
+
+/* radix tree calls to lookup, insert, delete
+ * integrity data associated with an inode.
+ */
+struct ima_iint_cache *ima_iint_insert(struct inode *inode);
+struct ima_iint_cache *ima_iint_find_get(struct inode *inode);
+struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode);
+void ima_iint_delete(struct inode *inode);
+void iint_free(struct kref *kref);
+void iint_rcu_free(struct rcu_head *rcu);
+
+/* IMA policy related functions */
+enum ima_hooks { PATH_CHECK = 1, FILE_MMAP, BPRM_CHECK };
+
+int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
+void ima_init_policy(void);
+void ima_update_policy(void);
+int ima_parse_add_rule(char *);
+void ima_delete_rules(void);
+
+/* LSM based policy rules require audit */
+#ifdef CONFIG_IMA_LSM_RULES
+
+#define security_filter_rule_init security_audit_rule_init
+#define security_filter_rule_match security_audit_rule_match
+
+#else
+
+static inline int security_filter_rule_init(u32 field, u32 op, char *rulestr,
+                                           void **lsmrule)
+{
+       return -EINVAL;
+}
+
+static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
+                                            void *lsmrule,
+                                            struct audit_context *actx)
+{
+       return -EINVAL;
+}
+#endif /* CONFIG_IMA_LSM_RULES */
+#endif
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
new file mode 100644 (file)
index 0000000..3cd58b6
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ *
+ * Author: Mimi Zohar <zohar@us.ibm.com>
+ *
+ * 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.
+ *
+ * File: ima_api.c
+ *     Implements must_measure, collect_measurement, store_measurement,
+ *     and store_template.
+ */
+#include <linux/module.h>
+
+#include "ima.h"
+static const char *IMA_TEMPLATE_NAME = "ima";
+
+/*
+ * ima_store_template - store ima template measurements
+ *
+ * Calculate the hash of a template entry, add the template entry
+ * to an ordered list of measurement entries maintained inside the kernel,
+ * and also update the aggregate integrity value (maintained inside the
+ * configured TPM PCR) over the hashes of the current list of measurement
+ * entries.
+ *
+ * Applications retrieve the current kernel-held measurement list through
+ * the securityfs entries in /sys/kernel/security/ima. The signed aggregate
+ * TPM PCR (called quote) can be retrieved using a TPM user space library
+ * and is used to validate the measurement list.
+ *
+ * Returns 0 on success, error code otherwise
+ */
+int ima_store_template(struct ima_template_entry *entry,
+                      int violation, struct inode *inode)
+{
+       const char *op = "add_template_measure";
+       const char *audit_cause = "hashing_error";
+       int result;
+
+       memset(entry->digest, 0, sizeof(entry->digest));
+       entry->template_name = IMA_TEMPLATE_NAME;
+       entry->template_len = sizeof(entry->template);
+
+       if (!violation) {
+               result = ima_calc_template_hash(entry->template_len,
+                                               &entry->template,
+                                               entry->digest);
+               if (result < 0) {
+                       integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
+                                           entry->template_name, op,
+                                           audit_cause, result, 0);
+                       return result;
+               }
+       }
+       result = ima_add_template_entry(entry, violation, op, inode);
+       return result;
+}
+
+/*
+ * ima_add_violation - add violation to measurement list.
+ *
+ * Violations are flagged in the measurement list with zero hash values.
+ * By extending the PCR with 0xFF's instead of with zeroes, the PCR
+ * value is invalidated.
+ */
+void ima_add_violation(struct inode *inode, const unsigned char *filename,
+                      const char *op, const char *cause)
+{
+       struct ima_template_entry *entry;
+       int violation = 1;
+       int result;
+
+       /* can overflow, only indicator */
+       atomic_long_inc(&ima_htable.violations);
+
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry) {
+               result = -ENOMEM;
+               goto err_out;
+       }
+       memset(&entry->template, 0, sizeof(entry->template));
+       strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
+       result = ima_store_template(entry, violation, inode);
+       if (result < 0)
+               kfree(entry);
+err_out:
+       integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
+                           op, cause, result, 0);
+}
+
+/**
+ * ima_must_measure - measure decision based on policy.
+ * @inode: pointer to inode to measure
+ * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
+ * @function: calling function (PATH_CHECK, BPRM_CHECK, FILE_MMAP)
+ *
+ * The policy is defined in terms of keypairs:
+ *             subj=, obj=, type=, func=, mask=, fsmagic=
+ *     subj,obj, and type: are LSM specific.
+ *     func: PATH_CHECK | BPRM_CHECK | FILE_MMAP
+ *     mask: contains the permission mask
+ *     fsmagic: hex value
+ *
+ * Must be called with iint->mutex held.
+ *
+ * Return 0 to measure. Return 1 if already measured.
+ * For matching a DONT_MEASURE policy, no policy, or other
+ * error, return an error code.
+*/
+int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
+                    int mask, int function)
+{
+       int must_measure;
+
+       if (iint->flags & IMA_MEASURED)
+               return 1;
+
+       must_measure = ima_match_policy(inode, function, mask);
+       return must_measure ? 0 : -EACCES;
+}
+
+/*
+ * ima_collect_measurement - collect file measurement
+ *
+ * Calculate the file hash, if it doesn't already exist,
+ * storing the measurement and i_version in the iint.
+ *
+ * Must be called with iint->mutex held.
+ *
+ * Return 0 on success, error code otherwise
+ */
+int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
+{
+       int result = -EEXIST;
+
+       if (!(iint->flags & IMA_MEASURED)) {
+               u64 i_version = file->f_dentry->d_inode->i_version;
+
+               memset(iint->digest, 0, IMA_DIGEST_SIZE);
+               result = ima_calc_hash(file, iint->digest);
+               if (!result)
+                       iint->version = i_version;
+       }
+       return result;
+}
+
+/*
+ * ima_store_measurement - store file measurement
+ *
+ * Create an "ima" template and then store the template by calling
+ * ima_store_template.
+ *
+ * We only get here if the inode has not already been measured,
+ * but the measurement could already exist:
+ *     - multiple copies of the same file on either the same or
+ *       different filesystems.
+ *     - the inode was previously flushed as well as the iint info,
+ *       containing the hashing info.
+ *
+ * Must be called with iint->mutex held.
+ */
+void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
+                          const unsigned char *filename)
+{
+       const char *op = "add_template_measure";
+       const char *audit_cause = "ENOMEM";
+       int result = -ENOMEM;
+       struct inode *inode = file->f_dentry->d_inode;
+       struct ima_template_entry *entry;
+       int violation = 0;
+
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry) {
+               integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
+                                   op, audit_cause, result, 0);
+               return;
+       }
+       memset(&entry->template, 0, sizeof(entry->template));
+       memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE);
+       strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
+
+       result = ima_store_template(entry, violation, inode);
+       if (!result)
+               iint->flags |= IMA_MEASURED;
+       else
+               kfree(entry);
+}
diff --git a/security/integrity/ima/ima_audit.c b/security/integrity/ima/ima_audit.c
new file mode 100644 (file)
index 0000000..1e082bb
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ * Author: Mimi Zohar <zohar@us.ibm.com>
+ *
+ * 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.
+ *
+ * File: integrity_audit.c
+ *     Audit calls for the integrity subsystem
+ */
+
+#include <linux/fs.h>
+#include <linux/audit.h>
+#include "ima.h"
+
+static int ima_audit;
+
+#ifdef CONFIG_IMA_AUDIT
+
+/* ima_audit_setup - enable informational auditing messages */
+static int __init ima_audit_setup(char *str)
+{
+       unsigned long audit;
+       int rc, result = 0;
+       char *op = "ima_audit";
+       char *cause;
+
+       rc = strict_strtoul(str, 0, &audit);
+       if (rc || audit > 1)
+               result = 1;
+       else
+               ima_audit = audit;
+       cause = ima_audit ? "enabled" : "not_enabled";
+       integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
+                           op, cause, result, 0);
+       return 1;
+}
+__setup("ima_audit=", ima_audit_setup);
+#endif
+
+void integrity_audit_msg(int audit_msgno, struct inode *inode,
+                        const unsigned char *fname, const char *op,
+                        const char *cause, int result, int audit_info)
+{
+       struct audit_buffer *ab;
+
+       if (!ima_audit && audit_info == 1) /* Skip informational messages */
+               return;
+
+       ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno);
+       audit_log_format(ab, "integrity: pid=%d uid=%u auid=%u ses=%u",
+                        current->pid, current->cred->uid,
+                        audit_get_loginuid(current),
+                        audit_get_sessionid(current));
+       audit_log_task_context(ab);
+       switch (audit_msgno) {
+       case AUDIT_INTEGRITY_DATA:
+       case AUDIT_INTEGRITY_METADATA:
+       case AUDIT_INTEGRITY_PCR:
+       case AUDIT_INTEGRITY_STATUS:
+               audit_log_format(ab, " op=%s cause=%s", op, cause);
+               break;
+       case AUDIT_INTEGRITY_HASH:
+               audit_log_format(ab, " op=%s hash=%s", op, cause);
+               break;
+       default:
+               audit_log_format(ab, " op=%s", op);
+       }
+       audit_log_format(ab, " comm=");
+       audit_log_untrustedstring(ab, current->comm);
+       if (fname) {
+               audit_log_format(ab, " name=");
+               audit_log_untrustedstring(ab, fname);
+       }
+       if (inode)
+               audit_log_format(ab, " dev=%s ino=%lu",
+                                inode->i_sb->s_id, inode->i_ino);
+       audit_log_format(ab, " res=%d", !result ? 0 : 1);
+       audit_log_end(ab);
+}
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
new file mode 100644 (file)
index 0000000..50d572b
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * 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.
+ *
+ * File: ima_crypto.c
+ *     Calculates md5/sha1 file hash, template hash, boot-aggreate hash
+ */
+
+#include <linux/kernel.h>
+#include <linux/file.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <linux/err.h>
+#include "ima.h"
+
+static int init_desc(struct hash_desc *desc)
+{
+       int rc;
+
+       desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(desc->tfm)) {
+               pr_info("failed to load %s transform: %ld\n",
+                       ima_hash, PTR_ERR(desc->tfm));
+               rc = PTR_ERR(desc->tfm);
+               return rc;
+       }
+       desc->flags = 0;
+       rc = crypto_hash_init(desc);
+       if (rc)
+               crypto_free_hash(desc->tfm);
+       return rc;
+}
+
+/*
+ * Calculate the MD5/SHA1 file digest
+ */
+int ima_calc_hash(struct file *file, char *digest)
+{
+       struct hash_desc desc;
+       struct scatterlist sg[1];
+       loff_t i_size;
+       char *rbuf;
+       int rc, offset = 0;
+
+       rc = init_desc(&desc);
+       if (rc != 0)
+               return rc;
+
+       rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!rbuf) {
+               rc = -ENOMEM;
+               goto out;
+       }
+       i_size = i_size_read(file->f_dentry->d_inode);
+       while (offset < i_size) {
+               int rbuf_len;
+
+               rbuf_len = kernel_read(file, offset, rbuf, PAGE_SIZE);
+               if (rbuf_len < 0) {
+                       rc = rbuf_len;
+                       break;
+               }
+               offset += rbuf_len;
+               sg_init_one(sg, rbuf, rbuf_len);
+
+               rc = crypto_hash_update(&desc, sg, rbuf_len);
+               if (rc)
+                       break;
+       }
+       kfree(rbuf);
+       if (!rc)
+               rc = crypto_hash_final(&desc, digest);
+out:
+       crypto_free_hash(desc.tfm);
+       return rc;
+}
+
+/*
+ * Calculate the hash of a given template
+ */
+int ima_calc_template_hash(int template_len, void *template, char *digest)
+{
+       struct hash_desc desc;
+       struct scatterlist sg[1];
+       int rc;
+
+       rc = init_desc(&desc);
+       if (rc != 0)
+               return rc;
+
+       sg_init_one(sg, template, template_len);
+       rc = crypto_hash_update(&desc, sg, template_len);
+       if (!rc)
+               rc = crypto_hash_final(&desc, digest);
+       crypto_free_hash(desc.tfm);
+       return rc;
+}
+
+static void ima_pcrread(int idx, u8 *pcr)
+{
+       if (!ima_used_chip)
+               return;
+
+       if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0)
+               pr_err("Error Communicating to TPM chip\n");
+}
+
+/*
+ * Calculate the boot aggregate hash
+ */
+int ima_calc_boot_aggregate(char *digest)
+{
+       struct hash_desc desc;
+       struct scatterlist sg;
+       u8 pcr_i[IMA_DIGEST_SIZE];
+       int rc, i;
+
+       rc = init_desc(&desc);
+       if (rc != 0)
+               return rc;
+
+       /* cumulative sha1 over tpm registers 0-7 */
+       for (i = TPM_PCR0; i < TPM_PCR8; i++) {
+               ima_pcrread(i, pcr_i);
+               /* now accumulate with current aggregate */
+               sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE);
+               rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE);
+       }
+       if (!rc)
+               crypto_hash_final(&desc, digest);
+       crypto_free_hash(desc.tfm);
+       return rc;
+}
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
new file mode 100644 (file)
index 0000000..ffbe259
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Kylene Hall <kjhall@us.ibm.com>
+ * Reiner Sailer <sailer@us.ibm.com>
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * 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.
+ *
+ * File: ima_fs.c
+ *     implemenents security file system for reporting
+ *     current measurement list and IMA statistics
+ */
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/rculist.h>
+#include <linux/rcupdate.h>
+#include <linux/parser.h>
+
+#include "ima.h"
+
+static int valid_policy = 1;
+#define TMPBUFLEN 12
+static ssize_t ima_show_htable_value(char __user *buf, size_t count,
+                                    loff_t *ppos, atomic_long_t *val)
+{
+       char tmpbuf[TMPBUFLEN];
+       ssize_t len;
+
+       len = scnprintf(tmpbuf, TMPBUFLEN, "%li\n", atomic_long_read(val));
+       return simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
+}
+
+static ssize_t ima_show_htable_violations(struct file *filp,
+                                         char __user *buf,
+                                         size_t count, loff_t *ppos)
+{
+       return ima_show_htable_value(buf, count, ppos, &ima_htable.violations);
+}
+
+static struct file_operations ima_htable_violations_ops = {
+       .read = ima_show_htable_violations
+};
+
+static ssize_t ima_show_measurements_count(struct file *filp,
+                                          char __user *buf,
+                                          size_t count, loff_t *ppos)
+{
+       return ima_show_htable_value(buf, count, ppos, &ima_htable.len);
+
+}
+
+static struct file_operations ima_measurements_count_ops = {
+       .read = ima_show_measurements_count
+};
+
+/* returns pointer to hlist_node */
+static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
+{
+       loff_t l = *pos;
+       struct ima_queue_entry *qe;
+
+       /* we need a lock since pos could point beyond last element */
+       rcu_read_lock();
+       list_for_each_entry_rcu(qe, &ima_measurements, later) {
+               if (!l--) {
+                       rcu_read_unlock();
+                       return qe;
+               }
+       }
+       rcu_read_unlock();
+       return NULL;
+}
+
+static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
+{
+       struct ima_queue_entry *qe = v;
+
+       /* lock protects when reading beyond last element
+        * against concurrent list-extension
+        */
+       rcu_read_lock();
+       qe = list_entry(rcu_dereference(qe->later.next),
+                       struct ima_queue_entry, later);
+       rcu_read_unlock();
+       (*pos)++;
+
+       return (&qe->later == &ima_measurements) ? NULL : qe;
+}
+
+static void ima_measurements_stop(struct seq_file *m, void *v)
+{
+}
+
+static void ima_putc(struct seq_file *m, void *data, int datalen)
+{
+       while (datalen--)
+               seq_putc(m, *(char *)data++);
+}
+
+/* print format:
+ *       32bit-le=pcr#
+ *       char[20]=template digest
+ *       32bit-le=template name size
+ *       char[n]=template name
+ *       eventdata[n]=template specific data
+ */
+static int ima_measurements_show(struct seq_file *m, void *v)
+{
+       /* the list never shrinks, so we don't need a lock here */
+       struct ima_queue_entry *qe = v;
+       struct ima_template_entry *e;
+       int namelen;
+       u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX;
+
+       /* get entry */
+       e = qe->entry;
+       if (e == NULL)
+               return -1;
+
+       /*
+        * 1st: PCRIndex
+        * PCR used is always the same (config option) in
+        * little-endian format
+        */
+       ima_putc(m, &pcr, sizeof pcr);
+
+       /* 2nd: template digest */
+       ima_putc(m, e->digest, IMA_DIGEST_SIZE);
+
+       /* 3rd: template name size */
+       namelen = strlen(e->template_name);
+       ima_putc(m, &namelen, sizeof namelen);
+
+       /* 4th:  template name */
+       ima_putc(m, (void *)e->template_name, namelen);
+
+       /* 5th:  template specific data */
+       ima_template_show(m, (struct ima_template_data *)&e->template,
+                         IMA_SHOW_BINARY);
+       return 0;
+}
+
+static struct seq_operations ima_measurments_seqops = {
+       .start = ima_measurements_start,
+       .next = ima_measurements_next,
+       .stop = ima_measurements_stop,
+       .show = ima_measurements_show
+};
+
+static int ima_measurements_open(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &ima_measurments_seqops);
+}
+
+static struct file_operations ima_measurements_ops = {
+       .open = ima_measurements_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = seq_release,
+};
+
+static void ima_print_digest(struct seq_file *m, u8 *digest)
+{
+       int i;
+
+       for (i = 0; i < IMA_DIGEST_SIZE; i++)
+               seq_printf(m, "%02x", *(digest + i));
+}
+
+void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show)
+{
+       struct ima_template_data *entry = e;
+       int namelen;
+
+       switch (show) {
+       case IMA_SHOW_ASCII:
+               ima_print_digest(m, entry->digest);
+               seq_printf(m, " %s\n", entry->file_name);
+               break;
+       case IMA_SHOW_BINARY:
+               ima_putc(m, entry->digest, IMA_DIGEST_SIZE);
+
+               namelen = strlen(entry->file_name);
+               ima_putc(m, &namelen, sizeof namelen);
+               ima_putc(m, entry->file_name, namelen);
+       default:
+               break;
+       }
+}
+
+/* print in ascii */
+static int ima_ascii_measurements_show(struct seq_file *m, void *v)
+{
+       /* the list never shrinks, so we don't need a lock here */
+       struct ima_queue_entry *qe = v;
+       struct ima_template_entry *e;
+
+       /* get entry */
+       e = qe->entry;
+       if (e == NULL)
+               return -1;
+
+       /* 1st: PCR used (config option) */
+       seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX);
+
+       /* 2nd: SHA1 template hash */
+       ima_print_digest(m, e->digest);
+
+       /* 3th:  template name */
+       seq_printf(m, " %s ", e->template_name);
+
+       /* 4th:  template specific data */
+       ima_template_show(m, (struct ima_template_data *)&e->template,
+                         IMA_SHOW_ASCII);
+       return 0;
+}
+
+static struct seq_operations ima_ascii_measurements_seqops = {
+       .start = ima_measurements_start,
+       .next = ima_measurements_next,
+       .stop = ima_measurements_stop,
+       .show = ima_ascii_measurements_show
+};
+
+static int ima_ascii_measurements_open(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &ima_ascii_measurements_seqops);
+}
+
+static struct file_operations ima_ascii_measurements_ops = {
+       .open = ima_ascii_measurements_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = seq_release,
+};
+
+static ssize_t ima_write_policy(struct file *file, const char __user *buf,
+                               size_t datalen, loff_t *ppos)
+{
+       char *data;
+       int rc;
+
+       if (datalen >= PAGE_SIZE)
+               return -ENOMEM;
+       if (*ppos != 0) {
+               /* No partial writes. */
+               return -EINVAL;
+       }
+       data = kmalloc(datalen + 1, GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       if (copy_from_user(data, buf, datalen)) {
+               kfree(data);
+               return -EFAULT;
+       }
+       *(data + datalen) = '\0';
+       rc = ima_parse_add_rule(data);
+       if (rc < 0) {
+               datalen = -EINVAL;
+               valid_policy = 0;
+       }
+
+       kfree(data);
+       return datalen;
+}
+
+static struct dentry *ima_dir;
+static struct dentry *binary_runtime_measurements;
+static struct dentry *ascii_runtime_measurements;
+static struct dentry *runtime_measurements_count;
+static struct dentry *violations;
+static struct dentry *ima_policy;
+
+static atomic_t policy_opencount = ATOMIC_INIT(1);
+/*
+ * ima_open_policy: sequentialize access to the policy file
+ */
+int ima_open_policy(struct inode * inode, struct file * filp)
+{
+       if (atomic_dec_and_test(&policy_opencount))
+               return 0;
+       return -EBUSY;
+}
+
+/*
+ * ima_release_policy - start using the new measure policy rules.
+ *
+ * Initially, ima_measure points to the default policy rules, now
+ * point to the new policy rules, and remove the securityfs policy file,
+ * assuming a valid policy.
+ */
+static int ima_release_policy(struct inode *inode, struct file *file)
+{
+       if (!valid_policy) {
+               ima_delete_rules();
+               valid_policy = 1;
+               atomic_set(&policy_opencount, 1);
+               return 0;
+       }
+       ima_update_policy();
+       securityfs_remove(ima_policy);
+       ima_policy = NULL;
+       return 0;
+}
+
+static struct file_operations ima_measure_policy_ops = {
+       .open = ima_open_policy,
+       .write = ima_write_policy,
+       .release = ima_release_policy
+};
+
+int ima_fs_init(void)
+{
+       ima_dir = securityfs_create_dir("ima", NULL);
+       if (IS_ERR(ima_dir))
+               return -1;
+
+       binary_runtime_measurements =
+           securityfs_create_file("binary_runtime_measurements",
+                                  S_IRUSR | S_IRGRP, ima_dir, NULL,
+                                  &ima_measurements_ops);
+       if (IS_ERR(binary_runtime_measurements))
+               goto out;
+
+       ascii_runtime_measurements =
+           securityfs_create_file("ascii_runtime_measurements",
+                                  S_IRUSR | S_IRGRP, ima_dir, NULL,
+                                  &ima_ascii_measurements_ops);
+       if (IS_ERR(ascii_runtime_measurements))
+               goto out;
+
+       runtime_measurements_count =
+           securityfs_create_file("runtime_measurements_count",
+                                  S_IRUSR | S_IRGRP, ima_dir, NULL,
+                                  &ima_measurements_count_ops);
+       if (IS_ERR(runtime_measurements_count))
+               goto out;
+
+       violations =
+           securityfs_create_file("violations", S_IRUSR | S_IRGRP,
+                                  ima_dir, NULL, &ima_htable_violations_ops);
+       if (IS_ERR(violations))
+               goto out;
+
+       ima_policy = securityfs_create_file("policy",
+                                           S_IRUSR | S_IRGRP | S_IWUSR,
+                                           ima_dir, NULL,
+                                           &ima_measure_policy_ops);
+       if (IS_ERR(ima_policy))
+               goto out;
+
+       return 0;
+out:
+       securityfs_remove(runtime_measurements_count);
+       securityfs_remove(ascii_runtime_measurements);
+       securityfs_remove(binary_runtime_measurements);
+       securityfs_remove(ima_dir);
+       securityfs_remove(ima_policy);
+       return -1;
+}
+
+void __exit ima_fs_cleanup(void)
+{
+       securityfs_remove(violations);
+       securityfs_remove(runtime_measurements_count);
+       securityfs_remove(ascii_runtime_measurements);
+       securityfs_remove(binary_runtime_measurements);
+       securityfs_remove(ima_dir);
+       securityfs_remove(ima_policy);
+}
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
new file mode 100644 (file)
index 0000000..ec79f1e
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * 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.
+ *
+ * File: ima_iint.c
+ *     - implements the IMA hooks: ima_inode_alloc, ima_inode_free
+ *     - cache integrity information associated with an inode
+ *       using a radix tree.
+ */
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/radix-tree.h>
+#include "ima.h"
+
+#define ima_iint_delete ima_inode_free
+
+RADIX_TREE(ima_iint_store, GFP_ATOMIC);
+DEFINE_SPINLOCK(ima_iint_lock);
+
+static struct kmem_cache *iint_cache __read_mostly;
+
+/* ima_iint_find_get - return the iint associated with an inode
+ *
+ * ima_iint_find_get gets a reference to the iint. Caller must
+ * remember to put the iint reference.
+ */
+struct ima_iint_cache *ima_iint_find_get(struct inode *inode)
+{
+       struct ima_iint_cache *iint;
+
+       rcu_read_lock();
+       iint = radix_tree_lookup(&ima_iint_store, (unsigned long)inode);
+       if (!iint)
+               goto out;
+       kref_get(&iint->refcount);
+out:
+       rcu_read_unlock();
+       return iint;
+}
+
+/* Allocate memory for the iint associated with the inode
+ * from the iint_cache slab, initialize the iint, and
+ * insert it into the radix tree.
+ *
+ * On success return a pointer to the iint; on failure return NULL.
+ */
+struct ima_iint_cache *ima_iint_insert(struct inode *inode)
+{
+       struct ima_iint_cache *iint = NULL;
+       int rc = 0;
+
+       if (!ima_initialized)
+               return iint;
+       iint = kmem_cache_alloc(iint_cache, GFP_KERNEL);
+       if (!iint)
+               return iint;
+
+       rc = radix_tree_preload(GFP_KERNEL);
+       if (rc < 0)
+               goto out;
+
+       spin_lock(&ima_iint_lock);
+       rc = radix_tree_insert(&ima_iint_store, (unsigned long)inode, iint);
+       spin_unlock(&ima_iint_lock);
+out:
+       if (rc < 0) {
+               kmem_cache_free(iint_cache, iint);
+               if (rc == -EEXIST) {
+                       spin_lock(&ima_iint_lock);
+                       iint = radix_tree_lookup(&ima_iint_store,
+                                                (unsigned long)inode);
+                       spin_unlock(&ima_iint_lock);
+               } else
+                       iint = NULL;
+       }
+       radix_tree_preload_end();
+       return iint;
+}
+
+/**
+ * ima_inode_alloc - allocate an iint associated with an inode
+ * @inode: pointer to the inode
+ *
+ * Return 0 on success, 1 on failure.
+ */
+int ima_inode_alloc(struct inode *inode)
+{
+       struct ima_iint_cache *iint;
+
+       if (!ima_initialized)
+               return 0;
+
+       iint = ima_iint_insert(inode);
+       if (!iint)
+               return 1;
+       return 0;
+}
+
+/* ima_iint_find_insert_get - get the iint associated with an inode
+ *
+ * Most insertions are done at inode_alloc, except those allocated
+ * before late_initcall. When the iint does not exist, allocate it,
+ * initialize and insert it, and increment the iint refcount.
+ *
+ * (Can't initialize at security_initcall before any inodes are
+ * allocated, got to wait at least until proc_init.)
+ *
+ *  Return the iint.
+ */
+struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode)
+{
+       struct ima_iint_cache *iint = NULL;
+
+       iint = ima_iint_find_get(inode);
+       if (iint)
+               return iint;
+
+       iint = ima_iint_insert(inode);
+       if (iint)
+               kref_get(&iint->refcount);
+
+       return iint;
+}
+EXPORT_SYMBOL_GPL(ima_iint_find_insert_get);
+
+/* iint_free - called when the iint refcount goes to zero */
+void iint_free(struct kref *kref)
+{
+       struct ima_iint_cache *iint = container_of(kref, struct ima_iint_cache,
+                                                  refcount);
+       iint->version = 0;
+       iint->flags = 0UL;
+       if (iint->readcount != 0) {
+               printk(KERN_INFO "%s: readcount: %ld\n", __FUNCTION__,
+                      iint->readcount);
+               iint->readcount = 0;
+       }
+       if (iint->writecount != 0) {
+               printk(KERN_INFO "%s: writecount: %ld\n", __FUNCTION__,
+                      iint->writecount);
+               iint->writecount = 0;
+       }
+       if (iint->opencount != 0) {
+               printk(KERN_INFO "%s: opencount: %ld\n", __FUNCTION__,
+                      iint->opencount);
+               iint->opencount = 0;
+       }
+       kref_set(&iint->refcount, 1);
+       kmem_cache_free(iint_cache, iint);
+}
+
+void iint_rcu_free(struct rcu_head *rcu_head)
+{
+       struct ima_iint_cache *iint = container_of(rcu_head,
+                                                  struct ima_iint_cache, rcu);
+       kref_put(&iint->refcount, iint_free);
+}
+
+/**
+ * ima_iint_delete - called on integrity_inode_free
+ * @inode: pointer to the inode
+ *
+ * Free the integrity information(iint) associated with an inode.
+ */
+void ima_iint_delete(struct inode *inode)
+{
+       struct ima_iint_cache *iint;
+
+       if (!ima_initialized)
+               return;
+       spin_lock(&ima_iint_lock);
+       iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode);
+       spin_unlock(&ima_iint_lock);
+       if (iint)
+               call_rcu(&iint->rcu, iint_rcu_free);
+}
+
+static void init_once(void *foo)
+{
+       struct ima_iint_cache *iint = foo;
+
+       memset(iint, 0, sizeof *iint);
+       iint->version = 0;
+       iint->flags = 0UL;
+       mutex_init(&iint->mutex);
+       iint->readcount = 0;
+       iint->writecount = 0;
+       iint->opencount = 0;
+       kref_set(&iint->refcount, 1);
+}
+
+void ima_iintcache_init(void)
+{
+       iint_cache =
+           kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0,
+                             SLAB_PANIC, init_once);
+}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
new file mode 100644 (file)
index 0000000..0b0bb8c
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer      <sailer@watson.ibm.com>
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Mimi Zohar         <zohar@us.ibm.com>
+ *
+ * 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.
+ *
+ * File: ima_init.c
+ *             initialization and cleanup functions
+ */
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/err.h>
+#include "ima.h"
+
+/* name for boot aggregate entry */
+static const char *boot_aggregate_name = "boot_aggregate";
+int ima_used_chip;
+
+/* Add the boot aggregate to the IMA measurement list and extend
+ * the PCR register.
+ *
+ * Calculate the boot aggregate, a SHA1 over tpm registers 0-7,
+ * assuming a TPM chip exists, and zeroes if the TPM chip does not
+ * exist.  Add the boot aggregate measurement to the measurement
+ * list and extend the PCR register.
+ *
+ * If a tpm chip does not exist, indicate the core root of trust is
+ * not hardware based by invalidating the aggregate PCR value.
+ * (The aggregate PCR value is invalidated by adding one value to
+ * the measurement list and extending the aggregate PCR value with
+ * a different value.) Violations add a zero entry to the measurement
+ * list and extend the aggregate PCR value with ff...ff's.
+ */
+static void ima_add_boot_aggregate(void)
+{
+       struct ima_template_entry *entry;
+       const char *op = "add_boot_aggregate";
+       const char *audit_cause = "ENOMEM";
+       int result = -ENOMEM;
+       int violation = 1;
+
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               goto err_out;
+
+       memset(&entry->template, 0, sizeof(entry->template));
+       strncpy(entry->template.file_name, boot_aggregate_name,
+               IMA_EVENT_NAME_LEN_MAX);
+       if (ima_used_chip) {
+               violation = 0;
+               result = ima_calc_boot_aggregate(entry->template.digest);
+               if (result < 0) {
+                       audit_cause = "hashing_error";
+                       kfree(entry);
+                       goto err_out;
+               }
+       }
+       result = ima_store_template(entry, violation, NULL);
+       if (result < 0)
+               kfree(entry);
+       return;
+err_out:
+       integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op,
+                           audit_cause, result, 0);
+}
+
+int ima_init(void)
+{
+       u8 pcr_i[IMA_DIGEST_SIZE];
+       int rc;
+
+       ima_used_chip = 0;
+       rc = tpm_pcr_read(TPM_ANY_NUM, 0, pcr_i);
+       if (rc == 0)
+               ima_used_chip = 1;
+
+       if (!ima_used_chip)
+               pr_info("No TPM chip found, activating TPM-bypass!\n");
+
+       ima_add_boot_aggregate();       /* boot aggregate must be first entry */
+       ima_init_policy();
+
+       return ima_fs_init();
+}
+
+void __exit ima_cleanup(void)
+{
+       ima_fs_cleanup();
+}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
new file mode 100644 (file)
index 0000000..f4e7266
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Serge Hallyn <serue@us.ibm.com>
+ * Kylene Hall <kylene@us.ibm.com>
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * 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.
+ *
+ * File: ima_main.c
+ *             implements the IMA hooks: ima_bprm_check, ima_file_mmap,
+ *             and ima_path_check.
+ */
+#include <linux/module.h>
+#include <linux/file.h>
+#include <linux/binfmts.h>
+#include <linux/mount.h>
+#include <linux/mman.h>
+
+#include "ima.h"
+
+int ima_initialized;
+
+char *ima_hash = "sha1";
+static int __init hash_setup(char *str)
+{
+       const char *op = "hash_setup";
+       const char *hash = "sha1";
+       int result = 0;
+       int audit_info = 0;
+
+       if (strncmp(str, "md5", 3) == 0) {
+               hash = "md5";
+               ima_hash = str;
+       } else if (strncmp(str, "sha1", 4) != 0) {
+               hash = "invalid_hash_type";
+               result = 1;
+       }
+       integrity_audit_msg(AUDIT_INTEGRITY_HASH, NULL, NULL, op, hash,
+                           result, audit_info);
+       return 1;
+}
+__setup("ima_hash=", hash_setup);
+
+/**
+ * ima_file_free - called on __fput()
+ * @file: pointer to file structure being freed
+ *
+ * Flag files that changed, based on i_version;
+ * and decrement the iint readcount/writecount.
+ */
+void ima_file_free(struct file *file)
+{
+       struct inode *inode = file->f_dentry->d_inode;
+       struct ima_iint_cache *iint;
+
+       if (!ima_initialized || !S_ISREG(inode->i_mode))
+               return;
+       iint = ima_iint_find_get(inode);
+       if (!iint)
+               return;
+
+       mutex_lock(&iint->mutex);
+       if (iint->opencount <= 0) {
+               printk(KERN_INFO
+                      "%s: %s open/free imbalance (r:%ld w:%ld o:%ld f:%ld)\n",
+                      __FUNCTION__, file->f_dentry->d_name.name,
+                      iint->readcount, iint->writecount,
+                      iint->opencount, atomic_long_read(&file->f_count));
+               if (!(iint->flags & IMA_IINT_DUMP_STACK)) {
+                       dump_stack();
+                       iint->flags |= IMA_IINT_DUMP_STACK;
+               }
+       }
+       iint->opencount--;
+
+       if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
+               iint->readcount--;
+
+       if (file->f_mode & FMODE_WRITE) {
+               iint->writecount--;
+               if (iint->writecount == 0) {
+                       if (iint->version != inode->i_version)
+                               iint->flags &= ~IMA_MEASURED;
+               }
+       }
+       mutex_unlock(&iint->mutex);
+       kref_put(&iint->refcount, iint_free);
+}
+
+/* ima_read_write_check - reflect possible reading/writing errors in the PCR.
+ *
+ * When opening a file for read, if the file is already open for write,
+ * the file could change, resulting in a file measurement error.
+ *
+ * Opening a file for write, if the file is already open for read, results
+ * in a time of measure, time of use (ToMToU) error.
+ *
+ * In either case invalidate the PCR.
+ */
+enum iint_pcr_error { TOMTOU, OPEN_WRITERS };
+static void ima_read_write_check(enum iint_pcr_error error,
+                                struct ima_iint_cache *iint,
+                                struct inode *inode,
+                                const unsigned char *filename)
+{
+       switch (error) {
+       case TOMTOU:
+               if (iint->readcount > 0)
+                       ima_add_violation(inode, filename, "invalid_pcr",
+                                         "ToMToU");
+               break;
+       case OPEN_WRITERS:
+               if (iint->writecount > 0)
+                       ima_add_violation(inode, filename, "invalid_pcr",
+                                         "open_writers");
+               break;
+       }
+}
+
+static int get_path_measurement(struct ima_iint_cache *iint, struct file *file,
+                               const unsigned char *filename)
+{
+       int rc = 0;
+
+       if (IS_ERR(file)) {
+               pr_info("%s dentry_open failed\n", filename);
+               return rc;
+       }
+       iint->opencount++;
+       iint->readcount++;
+
+       rc = ima_collect_measurement(iint, file);
+       if (!rc)
+               ima_store_measurement(iint, file, filename);
+       return rc;
+}
+
+/**
+ * ima_path_check - based on policy, collect/store measurement.
+ * @path: contains a pointer to the path to be measured
+ * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE
+ *
+ * Measure the file being open for readonly, based on the
+ * ima_must_measure() policy decision.
+ *
+ * Keep read/write counters for all files, but only
+ * invalidate the PCR for measured files:
+ *     - Opening a file for write when already open for read,
+ *       results in a time of measure, time of use (ToMToU) error.
+ *     - Opening a file for read when already open for write,
+ *       could result in a file measurement error.
+ *
+ * Return 0 on success, an error code on failure.
+ * (Based on the results of appraise_measurement().)
+ */
+int ima_path_check(struct path *path, int mask)
+{
+       struct inode *inode = path->dentry->d_inode;
+       struct ima_iint_cache *iint;
+       struct file *file = NULL;
+       int rc;
+
+       if (!ima_initialized || !S_ISREG(inode->i_mode))
+               return 0;
+       iint = ima_iint_find_insert_get(inode);
+       if (!iint)
+               return 0;
+
+       mutex_lock(&iint->mutex);
+       iint->opencount++;
+       if ((mask & MAY_WRITE) || (mask == 0))
+               iint->writecount++;
+       else if (mask & (MAY_READ | MAY_EXEC))
+               iint->readcount++;
+
+       rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK);
+       if (rc < 0)
+               goto out;
+
+       if ((mask & MAY_WRITE) || (mask == 0))
+               ima_read_write_check(TOMTOU, iint, inode,
+                                    path->dentry->d_name.name);
+
+       if ((mask & (MAY_WRITE | MAY_READ | MAY_EXEC)) != MAY_READ)
+               goto out;
+
+       ima_read_write_check(OPEN_WRITERS, iint, inode,
+                            path->dentry->d_name.name);
+       if (!(iint->flags & IMA_MEASURED)) {
+               struct dentry *dentry = dget(path->dentry);
+               struct vfsmount *mnt = mntget(path->mnt);
+
+               file = dentry_open(dentry, mnt, O_RDONLY, current->cred);
+               rc = get_path_measurement(iint, file, dentry->d_name.name);
+       }
+out:
+       mutex_unlock(&iint->mutex);
+       if (file)
+               fput(file);
+       kref_put(&iint->refcount, iint_free);
+       return 0;
+}
+
+static int process_measurement(struct file *file, const unsigned char *filename,
+                              int mask, int function)
+{
+       struct inode *inode = file->f_dentry->d_inode;
+       struct ima_iint_cache *iint;
+       int rc;
+
+       if (!ima_initialized || !S_ISREG(inode->i_mode))
+               return 0;
+       iint = ima_iint_find_insert_get(inode);
+       if (!iint)
+               return -ENOMEM;
+
+       mutex_lock(&iint->mutex);
+       rc = ima_must_measure(iint, inode, mask, function);
+       if (rc != 0)
+               goto out;
+
+       rc = ima_collect_measurement(iint, file);
+       if (!rc)
+               ima_store_measurement(iint, file, filename);
+out:
+       mutex_unlock(&iint->mutex);
+       kref_put(&iint->refcount, iint_free);
+       return rc;
+}
+
+static void opencount_get(struct file *file)
+{
+       struct inode *inode = file->f_dentry->d_inode;
+       struct ima_iint_cache *iint;
+
+       if (!ima_initialized || !S_ISREG(inode->i_mode))
+               return;
+       iint = ima_iint_find_insert_get(inode);
+       if (!iint)
+               return;
+       mutex_lock(&iint->mutex);
+       iint->opencount++;
+       mutex_unlock(&iint->mutex);
+}
+
+/**
+ * ima_file_mmap - based on policy, collect/store measurement.
+ * @file: pointer to the file to be measured (May be NULL)
+ * @prot: contains the protection that will be applied by the kernel.
+ *
+ * Measure files being mmapped executable based on the ima_must_measure()
+ * policy decision.
+ *
+ * Return 0 on success, an error code on failure.
+ * (Based on the results of appraise_measurement().)
+ */
+int ima_file_mmap(struct file *file, unsigned long prot)
+{
+       int rc;
+
+       if (!file)
+               return 0;
+       if (prot & PROT_EXEC)
+               rc = process_measurement(file, file->f_dentry->d_name.name,
+                                        MAY_EXEC, FILE_MMAP);
+       return 0;
+}
+
+/*
+ * ima_shm_check - IPC shm and shmat create/fput a file
+ *
+ * Maintain the opencount for these files to prevent unnecessary
+ * imbalance messages.
+ */
+void ima_shm_check(struct file *file)
+{
+       opencount_get(file);
+       return;
+}
+
+/**
+ * ima_bprm_check - based on policy, collect/store measurement.
+ * @bprm: contains the linux_binprm structure
+ *
+ * The OS protects against an executable file, already open for write,
+ * from being executed in deny_write_access() and an executable file,
+ * already open for execute, from being modified in get_write_access().
+ * So we can be certain that what we verify and measure here is actually
+ * what is being executed.
+ *
+ * Return 0 on success, an error code on failure.
+ * (Based on the results of appraise_measurement().)
+ */
+int ima_bprm_check(struct linux_binprm *bprm)
+{
+       int rc;
+
+       rc = process_measurement(bprm->file, bprm->filename,
+                                MAY_EXEC, BPRM_CHECK);
+       return 0;
+}
+
+static int __init init_ima(void)
+{
+       int error;
+
+       ima_iintcache_init();
+       error = ima_init();
+       ima_initialized = 1;
+       return error;
+}
+
+static void __exit cleanup_ima(void)
+{
+       ima_cleanup();
+}
+
+late_initcall(init_ima);       /* Start IMA after the TPM is available */
+
+MODULE_DESCRIPTION("Integrity Measurement Architecture");
+MODULE_LICENSE("GPL");
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
new file mode 100644 (file)
index 0000000..b5291ad
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ * Author: Mimi Zohar <zohar@us.ibm.com>
+ *
+ * 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.
+ *
+ * ima_policy.c
+ *     - initialize default measure policy rules
+ *
+ */
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/security.h>
+#include <linux/magic.h>
+#include <linux/parser.h>
+
+#include "ima.h"
+
+/* flags definitions */
+#define IMA_FUNC       0x0001
+#define IMA_MASK       0x0002
+#define IMA_FSMAGIC    0x0004
+#define IMA_UID                0x0008
+
+enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE };
+
+#define MAX_LSM_RULES 6
+enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
+       LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
+};
+
+struct ima_measure_rule_entry {
+       struct list_head list;
+       enum ima_action action;
+       unsigned int flags;
+       enum ima_hooks func;
+       int mask;
+       unsigned long fsmagic;
+       uid_t uid;
+       struct {
+               void *rule;     /* LSM file metadata specific */
+               int type;       /* audit type */
+       } lsm[MAX_LSM_RULES];
+};
+
+/* Without LSM specific knowledge, the default policy can only be
+ * written in terms of .action, .func, .mask, .fsmagic, and .uid
+ */
+static struct ima_measure_rule_entry default_rules[] = {
+       {.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,
+        .flags = IMA_FSMAGIC},
+       {.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
+       {.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
+       {.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
+       {.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,
+        .flags = IMA_FSMAGIC},
+       {.action = DONT_MEASURE,.fsmagic = 0xF97CFF8C,.flags = IMA_FSMAGIC},
+       {.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC,
+        .flags = IMA_FUNC | IMA_MASK},
+       {.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
+        .flags = IMA_FUNC | IMA_MASK},
+       {.action = MEASURE,.func = PATH_CHECK,.mask = MAY_READ,.uid = 0,
+        .flags = IMA_FUNC | IMA_MASK | IMA_UID}
+};
+
+static LIST_HEAD(measure_default_rules);
+static LIST_HEAD(measure_policy_rules);
+static struct list_head *ima_measure;
+
+static DEFINE_MUTEX(ima_measure_mutex);
+
+/**
+ * ima_match_rules - determine whether an inode matches the measure rule.
+ * @rule: a pointer to a rule
+ * @inode: a pointer to an inode
+ * @func: LIM hook identifier
+ * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
+ *
+ * Returns true on rule match, false on failure.
+ */
+static bool ima_match_rules(struct ima_measure_rule_entry *rule,
+                           struct inode *inode, enum ima_hooks func, int mask)
+{
+       struct task_struct *tsk = current;
+       int i;
+
+       if ((rule->flags & IMA_FUNC) && rule->func != func)
+               return false;
+       if ((rule->flags & IMA_MASK) && rule->mask != mask)
+               return false;
+       if ((rule->flags & IMA_FSMAGIC)
+           && rule->fsmagic != inode->i_sb->s_magic)
+               return false;
+       if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid)
+               return false;
+       for (i = 0; i < MAX_LSM_RULES; i++) {
+               int rc;
+               u32 osid, sid;
+
+               if (!rule->lsm[i].rule)
+                       continue;
+
+               switch (i) {
+               case LSM_OBJ_USER:
+               case LSM_OBJ_ROLE:
+               case LSM_OBJ_TYPE:
+                       security_inode_getsecid(inode, &osid);
+                       rc = security_filter_rule_match(osid,
+                                                       rule->lsm[i].type,
+                                                       AUDIT_EQUAL,
+                                                       rule->lsm[i].rule,
+                                                       NULL);
+                       break;
+               case LSM_SUBJ_USER:
+               case LSM_SUBJ_ROLE:
+               case LSM_SUBJ_TYPE:
+                       security_task_getsecid(tsk, &sid);
+                       rc = security_filter_rule_match(sid,
+                                                       rule->lsm[i].type,
+                                                       AUDIT_EQUAL,
+                                                       rule->lsm[i].rule,
+                                                       NULL);
+               default:
+                       break;
+               }
+               if (!rc)
+                       return false;
+       }
+       return true;
+}
+
+/**
+ * ima_match_policy - decision based on LSM and other conditions
+ * @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)
+ *
+ * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
+ * conditions.
+ *
+ * (There is no need for locking when walking the policy list,
+ * as elements in the list are never deleted, nor does the list
+ * change.)
+ */
+int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask)
+{
+       struct ima_measure_rule_entry *entry;
+
+       list_for_each_entry(entry, ima_measure, list) {
+               bool rc;
+
+               rc = ima_match_rules(entry, inode, func, mask);
+               if (rc)
+                       return entry->action;
+       }
+       return 0;
+}
+
+/**
+ * ima_init_policy - initialize the default measure rules.
+ *
+ * ima_measure points to either the measure_default_rules or the
+ * the new measure_policy_rules.
+ */
+void ima_init_policy(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(default_rules); i++)
+               list_add_tail(&default_rules[i].list, &measure_default_rules);
+       ima_measure = &measure_default_rules;
+}
+
+/**
+ * ima_update_policy - update default_rules with new measure rules
+ *
+ * Called on file .release to update the default rules with a complete new
+ * policy.  Once updated, the policy is locked, no additional rules can be
+ * added to the policy.
+ */
+void ima_update_policy(void)
+{
+       const char *op = "policy_update";
+       const char *cause = "already exists";
+       int result = 1;
+       int audit_info = 0;
+
+       if (ima_measure == &measure_default_rules) {
+               ima_measure = &measure_policy_rules;
+               cause = "complete";
+               result = 0;
+       }
+       integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+                           NULL, op, cause, result, audit_info);
+}
+
+enum {
+       Opt_err = -1,
+       Opt_measure = 1, Opt_dont_measure,
+       Opt_obj_user, Opt_obj_role, Opt_obj_type,
+       Opt_subj_user, Opt_subj_role, Opt_subj_type,
+       Opt_func, Opt_mask, Opt_fsmagic, Opt_uid
+};
+
+static match_table_t policy_tokens = {
+       {Opt_measure, "measure"},
+       {Opt_dont_measure, "dont_measure"},
+       {Opt_obj_user, "obj_user=%s"},
+       {Opt_obj_role, "obj_role=%s"},
+       {Opt_obj_type, "obj_type=%s"},
+       {Opt_subj_user, "subj_user=%s"},
+       {Opt_subj_role, "subj_role=%s"},
+       {Opt_subj_type, "subj_type=%s"},
+       {Opt_func, "func=%s"},
+       {Opt_mask, "mask=%s"},
+       {Opt_fsmagic, "fsmagic=%s"},
+       {Opt_uid, "uid=%s"},
+       {Opt_err, NULL}
+};
+
+static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry,
+                            char *args, int lsm_rule, int audit_type)
+{
+       int result;
+
+       entry->lsm[lsm_rule].type = audit_type;
+       result = security_filter_rule_init(entry->lsm[lsm_rule].type,
+                                          AUDIT_EQUAL, args,
+                                          &entry->lsm[lsm_rule].rule);
+       return result;
+}
+
+static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
+{
+       struct audit_buffer *ab;
+       char *p;
+       int result = 0;
+
+       ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE);
+
+       entry->action = -1;
+       while ((p = strsep(&rule, " \n")) != NULL) {
+               substring_t args[MAX_OPT_ARGS];
+               int token;
+               unsigned long lnum;
+
+               if (result < 0)
+                       break;
+               if (!*p)
+                       continue;
+               token = match_token(p, policy_tokens, args);
+               switch (token) {
+               case Opt_measure:
+                       audit_log_format(ab, "%s ", "measure");
+                       entry->action = MEASURE;
+                       break;
+               case Opt_dont_measure:
+                       audit_log_format(ab, "%s ", "dont_measure");
+                       entry->action = DONT_MEASURE;
+                       break;
+               case Opt_func:
+                       audit_log_format(ab, "func=%s ", args[0].from);
+                       if (strcmp(args[0].from, "PATH_CHECK") == 0)
+                               entry->func = PATH_CHECK;
+                       else if (strcmp(args[0].from, "FILE_MMAP") == 0)
+                               entry->func = FILE_MMAP;
+                       else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
+                               entry->func = BPRM_CHECK;
+                       else
+                               result = -EINVAL;
+                       if (!result)
+                               entry->flags |= IMA_FUNC;
+                       break;
+               case Opt_mask:
+                       audit_log_format(ab, "mask=%s ", args[0].from);
+                       if ((strcmp(args[0].from, "MAY_EXEC")) == 0)
+                               entry->mask = MAY_EXEC;
+                       else if (strcmp(args[0].from, "MAY_WRITE") == 0)
+                               entry->mask = MAY_WRITE;
+                       else if (strcmp(args[0].from, "MAY_READ") == 0)
+                               entry->mask = MAY_READ;
+                       else if (strcmp(args[0].from, "MAY_APPEND") == 0)
+                               entry->mask = MAY_APPEND;
+                       else
+                               result = -EINVAL;
+                       if (!result)
+                               entry->flags |= IMA_MASK;
+                       break;
+               case Opt_fsmagic:
+                       audit_log_format(ab, "fsmagic=%s ", args[0].from);
+                       result = strict_strtoul(args[0].from, 16,
+                                               &entry->fsmagic);
+                       if (!result)
+                               entry->flags |= IMA_FSMAGIC;
+                       break;
+               case Opt_uid:
+                       audit_log_format(ab, "uid=%s ", args[0].from);
+                       result = strict_strtoul(args[0].from, 10, &lnum);
+                       if (!result) {
+                               entry->uid = (uid_t) lnum;
+                               if (entry->uid != lnum)
+                                       result = -EINVAL;
+                               else
+                                       entry->flags |= IMA_UID;
+                       }
+                       break;
+               case Opt_obj_user:
+                       audit_log_format(ab, "obj_user=%s ", args[0].from);
+                       result = ima_lsm_rule_init(entry, args[0].from,
+                                                  LSM_OBJ_USER,
+                                                  AUDIT_OBJ_USER);
+                       break;
+               case Opt_obj_role:
+                       audit_log_format(ab, "obj_role=%s ", args[0].from);
+                       result = ima_lsm_rule_init(entry, args[0].from,
+                                                  LSM_OBJ_ROLE,
+                                                  AUDIT_OBJ_ROLE);
+                       break;
+               case Opt_obj_type:
+                       audit_log_format(ab, "obj_type=%s ", args[0].from);
+                       result = ima_lsm_rule_init(entry, args[0].from,
+                                                  LSM_OBJ_TYPE,
+                                                  AUDIT_OBJ_TYPE);
+                       break;
+               case Opt_subj_user:
+                       audit_log_format(ab, "subj_user=%s ", args[0].from);
+                       result = ima_lsm_rule_init(entry, args[0].from,
+                                                  LSM_SUBJ_USER,
+                                                  AUDIT_SUBJ_USER);
+                       break;
+               case Opt_subj_role:
+                       audit_log_format(ab, "subj_role=%s ", args[0].from);
+                       result = ima_lsm_rule_init(entry, args[0].from,
+                                                  LSM_SUBJ_ROLE,
+                                                  AUDIT_SUBJ_ROLE);
+                       break;
+               case Opt_subj_type:
+                       audit_log_format(ab, "subj_type=%s ", args[0].from);
+                       result = ima_lsm_rule_init(entry, args[0].from,
+                                                  LSM_SUBJ_TYPE,
+                                                  AUDIT_SUBJ_TYPE);
+                       break;
+               case Opt_err:
+                       audit_log_format(ab, "UNKNOWN=%s ", p);
+                       break;
+               }
+       }
+       if (entry->action == UNKNOWN)
+               result = -EINVAL;
+
+       audit_log_format(ab, "res=%d", !result ? 0 : 1);
+       audit_log_end(ab);
+       return result;
+}
+
+/**
+ * ima_parse_add_rule - add a rule to measure_policy_rules
+ * @rule - ima measurement policy rule
+ *
+ * Uses a mutex to protect the policy list from multiple concurrent writers.
+ * Returns 0 on success, an error code on failure.
+ */
+int ima_parse_add_rule(char *rule)
+{
+       const char *op = "update_policy";
+       struct ima_measure_rule_entry *entry;
+       int result = 0;
+       int audit_info = 0;
+
+       /* Prevent installed policy from changing */
+       if (ima_measure != &measure_default_rules) {
+               integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+                                   NULL, op, "already exists",
+                                   -EACCES, audit_info);
+               return -EACCES;
+       }
+
+       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry) {
+               integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+                                   NULL, op, "-ENOMEM", -ENOMEM, audit_info);
+               return -ENOMEM;
+       }
+
+       INIT_LIST_HEAD(&entry->list);
+
+       result = ima_parse_rule(rule, entry);
+       if (!result) {
+               mutex_lock(&ima_measure_mutex);
+               list_add_tail(&entry->list, &measure_policy_rules);
+               mutex_unlock(&ima_measure_mutex);
+       } else {
+               kfree(entry);
+               integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+                                   NULL, op, "invalid policy", result,
+                                   audit_info);
+       }
+       return result;
+}
+
+/* ima_delete_rules called to cleanup invalid policy */
+void ima_delete_rules(void)
+{
+       struct ima_measure_rule_entry *entry, *tmp;
+
+       mutex_lock(&ima_measure_mutex);
+       list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) {
+               list_del(&entry->list);
+               kfree(entry);
+       }
+       mutex_unlock(&ima_measure_mutex);
+}
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
new file mode 100644 (file)
index 0000000..7ec9431
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Serge Hallyn <serue@us.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * 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.
+ *
+ * File: ima_queue.c
+ *       Implements queues that store template measurements and
+ *       maintains aggregate over the stored measurements
+ *       in the pre-configured TPM PCR (if available).
+ *       The measurement list is append-only. No entry is
+ *       ever removed or changed during the boot-cycle.
+ */
+#include <linux/module.h>
+#include <linux/rculist.h>
+#include "ima.h"
+
+LIST_HEAD(ima_measurements);   /* list of all measurements */
+
+/* key: inode (before secure-hashing a file) */
+struct ima_h_table ima_htable = {
+       .len = ATOMIC_LONG_INIT(0),
+       .violations = ATOMIC_LONG_INIT(0),
+       .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
+};
+
+/* mutex protects atomicity of extending measurement list
+ * and extending the TPM PCR aggregate. Since tpm_extend can take
+ * long (and the tpm driver uses a mutex), we can't use the spinlock.
+ */
+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)
+{
+       struct ima_queue_entry *qe, *ret = NULL;
+       unsigned int key;
+       struct hlist_node *pos;
+       int rc;
+
+       key = ima_hash_key(digest_value);
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(qe, pos, &ima_htable.queue[key], hnext) {
+               rc = memcmp(qe->entry->digest, digest_value, IMA_DIGEST_SIZE);
+               if (rc == 0) {
+                       ret = qe;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+       return ret;
+}
+
+/* ima_add_template_entry helper function:
+ * - Add template entry to measurement list and hash table.
+ *
+ * (Called with ima_extend_list_mutex held.)
+ */
+static int ima_add_digest_entry(struct ima_template_entry *entry)
+{
+       struct ima_queue_entry *qe;
+       unsigned int key;
+
+       qe = kmalloc(sizeof(*qe), GFP_KERNEL);
+       if (qe == NULL) {
+               pr_err("OUT OF MEMORY ERROR creating queue entry.\n");
+               return -ENOMEM;
+       }
+       qe->entry = entry;
+
+       INIT_LIST_HEAD(&qe->later);
+       list_add_tail_rcu(&qe->later, &ima_measurements);
+
+       atomic_long_inc(&ima_htable.len);
+       key = ima_hash_key(entry->digest);
+       hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);
+       return 0;
+}
+
+static int ima_pcr_extend(const u8 *hash)
+{
+       int result = 0;
+
+       if (!ima_used_chip)
+               return result;
+
+       result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash);
+       if (result != 0)
+               pr_err("Error Communicating to TPM chip\n");
+       return result;
+}
+
+/* Add template entry to the measurement list and hash table,
+ * and extend the pcr.
+ */
+int ima_add_template_entry(struct ima_template_entry *entry, int violation,
+                          const char *op, struct inode *inode)
+{
+       u8 digest[IMA_DIGEST_SIZE];
+       const char *audit_cause = "hash_added";
+       int audit_info = 1;
+       int result = 0;
+
+       mutex_lock(&ima_extend_list_mutex);
+       if (!violation) {
+               memcpy(digest, entry->digest, sizeof digest);
+               if (ima_lookup_digest_entry(digest)) {
+                       audit_cause = "hash_exists";
+                       goto out;
+               }
+       }
+
+       result = ima_add_digest_entry(entry);
+       if (result < 0) {
+               audit_cause = "ENOMEM";
+               audit_info = 0;
+               goto out;
+       }
+
+       if (violation)          /* invalidate pcr */
+               memset(digest, 0xff, sizeof digest);
+
+       result = ima_pcr_extend(digest);
+       if (result != 0) {
+               audit_cause = "TPM error";
+               audit_info = 0;
+       }
+out:
+       mutex_unlock(&ima_extend_list_mutex);
+       integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, entry->template_name,
+                           op, audit_cause, result, audit_info);
+       return result;
+}
index 81932abefe7b1b3120c06c44365c07f889a397ec..9fb679c66b8af3c90f85b7c016ff5657268685d2 100644 (file)
@@ -53,6 +53,7 @@ struct key_user {
        atomic_t                nkeys;          /* number of keys */
        atomic_t                nikeys;         /* number of instantiated keys */
        uid_t                   uid;
+       struct user_namespace   *user_ns;
        int                     qnkeys;         /* number of keys allocated to this user */
        int                     qnbytes;        /* number of bytes allocated to this user */
 };
@@ -61,7 +62,8 @@ extern struct rb_root key_user_tree;
 extern spinlock_t      key_user_lock;
 extern struct key_user root_key_user;
 
-extern struct key_user *key_user_lookup(uid_t uid);
+extern struct key_user *key_user_lookup(uid_t uid,
+                                       struct user_namespace *user_ns);
 extern void key_user_put(struct key_user *user);
 
 /*
index f76c8a546fd3688e4cc2cadd076e05debe43f9a3..4a1297d1ada4f41d97fd314a8848d7e132112da4 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/workqueue.h>
 #include <linux/random.h>
 #include <linux/err.h>
+#include <linux/user_namespace.h>
 #include "internal.h"
 
 static struct kmem_cache       *key_jar;
@@ -60,7 +61,7 @@ void __key_check(const struct key *key)
  * get the key quota record for a user, allocating a new record if one doesn't
  * already exist
  */
-struct key_user *key_user_lookup(uid_t uid)
+struct key_user *key_user_lookup(uid_t uid, struct user_namespace *user_ns)
 {
        struct key_user *candidate = NULL, *user;
        struct rb_node *parent = NULL;
@@ -79,6 +80,10 @@ struct key_user *key_user_lookup(uid_t uid)
                        p = &(*p)->rb_left;
                else if (uid > user->uid)
                        p = &(*p)->rb_right;
+               else if (user_ns < user->user_ns)
+                       p = &(*p)->rb_left;
+               else if (user_ns > user->user_ns)
+                       p = &(*p)->rb_right;
                else
                        goto found;
        }
@@ -106,6 +111,7 @@ struct key_user *key_user_lookup(uid_t uid)
        atomic_set(&candidate->nkeys, 0);
        atomic_set(&candidate->nikeys, 0);
        candidate->uid = uid;
+       candidate->user_ns = get_user_ns(user_ns);
        candidate->qnkeys = 0;
        candidate->qnbytes = 0;
        spin_lock_init(&candidate->lock);
@@ -136,6 +142,7 @@ void key_user_put(struct key_user *user)
        if (atomic_dec_and_lock(&user->usage, &key_user_lock)) {
                rb_erase(&user->node, &key_user_tree);
                spin_unlock(&key_user_lock);
+               put_user_ns(user->user_ns);
 
                kfree(user);
        }
@@ -234,7 +241,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
        quotalen = desclen + type->def_datalen;
 
        /* get hold of the key tracking for this user */
-       user = key_user_lookup(uid);
+       user = key_user_lookup(uid, cred->user->user_ns);
        if (!user)
                goto no_memory_1;
 
index b1ec3b4ee17df919101b3aee3e3853f4b4ea75bb..7f09fb897d2b49e1b4a1e2f96e942094b9239474 100644 (file)
@@ -726,7 +726,7 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
        /* change the UID */
        if (uid != (uid_t) -1 && uid != key->uid) {
                ret = -ENOMEM;
-               newowner = key_user_lookup(uid);
+               newowner = key_user_lookup(uid, current_user_ns());
                if (!newowner)
                        goto error_put;
 
index ed851574d07301ecc07f6920ffaee5be86635efd..3dba81c2eba360a615d9116e124cb36d86e89427 100644 (file)
@@ -539,6 +539,9 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
                                    &keyring_name_hash[bucket],
                                    type_data.link
                                    ) {
+                       if (keyring->user->user_ns != current_user_ns())
+                               continue;
+
                        if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
                                continue;
 
index 5d9fc7b93f2e988f0b96c8fb2e89caacac4147cb..0ed802c9e698151b9c04440b4a6e8ab43eb9b9d4 100644 (file)
@@ -35,6 +35,9 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred,
 
        key = key_ref_to_ptr(key_ref);
 
+       if (key->user->user_ns != cred->user->user_ns)
+               goto use_other_perms;
+
        /* use the second 8-bits of permissions for keys the caller owns */
        if (key->uid == cred->fsuid) {
                kperm = key->perm >> 16;
@@ -56,6 +59,8 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred,
                }
        }
 
+use_other_perms:
+
        /* otherwise use the least-significant 8-bits */
        kperm = key->perm;
 
index 7f508def50e319a5110032a7e169a19a5ec02fab..769f9bdfd2b33aaeadbd7eb3efb895a364d0536a 100644 (file)
@@ -91,6 +91,28 @@ __initcall(key_proc_init);
  */
 #ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
 
+static struct rb_node *__key_serial_next(struct rb_node *n)
+{
+       while (n) {
+               struct key *key = rb_entry(n, struct key, serial_node);
+               if (key->user->user_ns == current_user_ns())
+                       break;
+               n = rb_next(n);
+       }
+       return n;
+}
+
+static struct rb_node *key_serial_next(struct rb_node *n)
+{
+       return __key_serial_next(rb_next(n));
+}
+
+static struct rb_node *key_serial_first(struct rb_root *r)
+{
+       struct rb_node *n = rb_first(r);
+       return __key_serial_next(n);
+}
+
 static int proc_keys_open(struct inode *inode, struct file *file)
 {
        return seq_open(file, &proc_keys_ops);
@@ -104,10 +126,10 @@ static void *proc_keys_start(struct seq_file *p, loff_t *_pos)
 
        spin_lock(&key_serial_lock);
 
-       _p = rb_first(&key_serial_tree);
+       _p = key_serial_first(&key_serial_tree);
        while (pos > 0 && _p) {
                pos--;
-               _p = rb_next(_p);
+               _p = key_serial_next(_p);
        }
 
        return _p;
@@ -117,7 +139,7 @@ static void *proc_keys_start(struct seq_file *p, loff_t *_pos)
 static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
 {
        (*_pos)++;
-       return rb_next((struct rb_node *) v);
+       return key_serial_next((struct rb_node *) v);
 
 }
 
@@ -203,6 +225,27 @@ static int proc_keys_show(struct seq_file *m, void *v)
 
 #endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */
 
+static struct rb_node *__key_user_next(struct rb_node *n)
+{
+       while (n) {
+               struct key_user *user = rb_entry(n, struct key_user, node);
+               if (user->user_ns == current_user_ns())
+                       break;
+               n = rb_next(n);
+       }
+       return n;
+}
+
+static struct rb_node *key_user_next(struct rb_node *n)
+{
+       return __key_user_next(rb_next(n));
+}
+
+static struct rb_node *key_user_first(struct rb_root *r)
+{
+       struct rb_node *n = rb_first(r);
+       return __key_user_next(n);
+}
 /*****************************************************************************/
 /*
  * implement "/proc/key-users" to provides a list of the key users
@@ -220,10 +263,10 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
 
        spin_lock(&key_user_lock);
 
-       _p = rb_first(&key_user_tree);
+       _p = key_user_first(&key_user_tree);
        while (pos > 0 && _p) {
                pos--;
-               _p = rb_next(_p);
+               _p = key_user_next(_p);
        }
 
        return _p;
@@ -233,7 +276,7 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
 static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)
 {
        (*_pos)++;
-       return rb_next((struct rb_node *) v);
+       return key_user_next((struct rb_node *) v);
 
 }
 
index 2f5d89e92b853a3a4e0ea910aa2e023fc749690a..276d27882ce84394d7d6ce5b0c12d82e05f89987 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/fs.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
+#include <linux/user_namespace.h>
 #include <asm/uaccess.h>
 #include "internal.h"
 
@@ -34,6 +35,7 @@ struct key_user root_key_user = {
        .nkeys          = ATOMIC_INIT(2),
        .nikeys         = ATOMIC_INIT(2),
        .uid            = 0,
+       .user_ns        = &init_user_ns,
 };
 
 /*****************************************************************************/
index 0e04f72ef2d4339c8ac50691d0171e138dc11df2..22a31582bfaae26ffe5911acf72dc2c09e8d06ec 100644 (file)
@@ -365,7 +365,7 @@ static struct key *construct_key_and_link(struct key_type *type,
 
        kenter("");
 
-       user = key_user_lookup(current_fsuid());
+       user = key_user_lookup(current_fsuid(), current_user_ns());
        if (!user)
                return ERR_PTR(-ENOMEM);
 
index eb41f43e2772d8cc35261103efcd6942c7f6bf12..7f9b5fac87793a19faf3d310a4e04f8a158bb476 100644 (file)
@@ -88,17 +88,16 @@ struct avc_entry {
        u32                     tsid;
        u16                     tclass;
        struct av_decision      avd;
-       atomic_t                used;   /* used recently */
 };
 
 struct avc_node {
        struct avc_entry        ae;
-       struct list_head        list;
+       struct hlist_node       list; /* anchored in avc_cache->slots[i] */
        struct rcu_head         rhead;
 };
 
 struct avc_cache {
-       struct list_head        slots[AVC_CACHE_SLOTS];
+       struct hlist_head       slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */
        spinlock_t              slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */
        atomic_t                lru_hint;       /* LRU hint for reclaim scan */
        atomic_t                active_nodes;
@@ -234,7 +233,7 @@ void __init avc_init(void)
        int i;
 
        for (i = 0; i < AVC_CACHE_SLOTS; i++) {
-               INIT_LIST_HEAD(&avc_cache.slots[i]);
+               INIT_HLIST_HEAD(&avc_cache.slots[i]);
                spin_lock_init(&avc_cache.slots_lock[i]);
        }
        atomic_set(&avc_cache.active_nodes, 0);
@@ -250,16 +249,20 @@ int avc_get_hash_stats(char *page)
 {
        int i, chain_len, max_chain_len, slots_used;
        struct avc_node *node;
+       struct hlist_head *head;
 
        rcu_read_lock();
 
        slots_used = 0;
        max_chain_len = 0;
        for (i = 0; i < AVC_CACHE_SLOTS; i++) {
-               if (!list_empty(&avc_cache.slots[i])) {
+               head = &avc_cache.slots[i];
+               if (!hlist_empty(head)) {
+                       struct hlist_node *next;
+
                        slots_used++;
                        chain_len = 0;
-                       list_for_each_entry_rcu(node, &avc_cache.slots[i], list)
+                       hlist_for_each_entry_rcu(node, next, head, list)
                                chain_len++;
                        if (chain_len > max_chain_len)
                                max_chain_len = chain_len;
@@ -283,7 +286,7 @@ static void avc_node_free(struct rcu_head *rhead)
 
 static void avc_node_delete(struct avc_node *node)
 {
-       list_del_rcu(&node->list);
+       hlist_del_rcu(&node->list);
        call_rcu(&node->rhead, avc_node_free);
        atomic_dec(&avc_cache.active_nodes);
 }
@@ -297,7 +300,7 @@ static void avc_node_kill(struct avc_node *node)
 
 static void avc_node_replace(struct avc_node *new, struct avc_node *old)
 {
-       list_replace_rcu(&old->list, &new->list);
+       hlist_replace_rcu(&old->list, &new->list);
        call_rcu(&old->rhead, avc_node_free);
        atomic_dec(&avc_cache.active_nodes);
 }
@@ -307,29 +310,31 @@ static inline int avc_reclaim_node(void)
        struct avc_node *node;
        int hvalue, try, ecx;
        unsigned long flags;
+       struct hlist_head *head;
+       struct hlist_node *next;
+       spinlock_t *lock;
 
        for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++) {
                hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1);
+               head = &avc_cache.slots[hvalue];
+               lock = &avc_cache.slots_lock[hvalue];
 
-               if (!spin_trylock_irqsave(&avc_cache.slots_lock[hvalue], flags))
+               if (!spin_trylock_irqsave(lock, flags))
                        continue;
 
                rcu_read_lock();
-               list_for_each_entry(node, &avc_cache.slots[hvalue], list) {
-                       if (atomic_dec_and_test(&node->ae.used)) {
-                               /* Recently Unused */
-                               avc_node_delete(node);
-                               avc_cache_stats_incr(reclaims);
-                               ecx++;
-                               if (ecx >= AVC_CACHE_RECLAIM) {
-                                       rcu_read_unlock();
-                                       spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags);
-                                       goto out;
-                               }
+               hlist_for_each_entry(node, next, head, list) {
+                       avc_node_delete(node);
+                       avc_cache_stats_incr(reclaims);
+                       ecx++;
+                       if (ecx >= AVC_CACHE_RECLAIM) {
+                               rcu_read_unlock();
+                               spin_unlock_irqrestore(lock, flags);
+                               goto out;
                        }
                }
                rcu_read_unlock();
-               spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags);
+               spin_unlock_irqrestore(lock, flags);
        }
 out:
        return ecx;
@@ -344,8 +349,7 @@ static struct avc_node *avc_alloc_node(void)
                goto out;
 
        INIT_RCU_HEAD(&node->rhead);
-       INIT_LIST_HEAD(&node->list);
-       atomic_set(&node->ae.used, 1);
+       INIT_HLIST_NODE(&node->list);
        avc_cache_stats_incr(allocations);
 
        if (atomic_inc_return(&avc_cache.active_nodes) > avc_cache_threshold)
@@ -355,21 +359,24 @@ out:
        return node;
 }
 
-static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae)
+static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd)
 {
        node->ae.ssid = ssid;
        node->ae.tsid = tsid;
        node->ae.tclass = tclass;
-       memcpy(&node->ae.avd, &ae->avd, sizeof(node->ae.avd));
+       memcpy(&node->ae.avd, avd, sizeof(node->ae.avd));
 }
 
 static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
 {
        struct avc_node *node, *ret = NULL;
        int hvalue;
+       struct hlist_head *head;
+       struct hlist_node *next;
 
        hvalue = avc_hash(ssid, tsid, tclass);
-       list_for_each_entry_rcu(node, &avc_cache.slots[hvalue], list) {
+       head = &avc_cache.slots[hvalue];
+       hlist_for_each_entry_rcu(node, next, head, list) {
                if (ssid == node->ae.ssid &&
                    tclass == node->ae.tclass &&
                    tsid == node->ae.tsid) {
@@ -378,15 +385,6 @@ static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
                }
        }
 
-       if (ret == NULL) {
-               /* cache miss */
-               goto out;
-       }
-
-       /* cache hit */
-       if (atomic_read(&ret->ae.used) != 1)
-               atomic_set(&ret->ae.used, 1);
-out:
        return ret;
 }
 
@@ -395,30 +393,25 @@ out:
  * @ssid: source security identifier
  * @tsid: target security identifier
  * @tclass: target security class
- * @requested: requested permissions, interpreted based on @tclass
  *
  * Look up an AVC entry that is valid for the
- * @requested permissions between the SID pair
  * (@ssid, @tsid), interpreting the permissions
  * based on @tclass.  If a valid AVC entry exists,
  * then this function return the avc_node.
  * Otherwise, this function returns NULL.
  */
-static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass, u32 requested)
+static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass)
 {
        struct avc_node *node;
 
        avc_cache_stats_incr(lookups);
        node = avc_search_node(ssid, tsid, tclass);
 
-       if (node && ((node->ae.avd.decided & requested) == requested)) {
+       if (node)
                avc_cache_stats_incr(hits);
-               goto out;
-       }
+       else
+               avc_cache_stats_incr(misses);
 
-       node = NULL;
-       avc_cache_stats_incr(misses);
-out:
        return node;
 }
 
@@ -449,34 +442,41 @@ static int avc_latest_notif_update(int seqno, int is_insert)
  * @ssid: source security identifier
  * @tsid: target security identifier
  * @tclass: target security class
- * @ae: AVC entry
+ * @avd: resulting av decision
  *
  * Insert an AVC entry for the SID pair
  * (@ssid, @tsid) and class @tclass.
  * The access vectors and the sequence number are
  * normally provided by the security server in
  * response to a security_compute_av() call.  If the
- * sequence number @ae->avd.seqno is not less than the latest
+ * sequence number @avd->seqno is not less than the latest
  * revocation notification, then the function copies
  * the access vectors into a cache entry, returns
  * avc_node inserted. Otherwise, this function returns NULL.
  */
-static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae)
+static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd)
 {
        struct avc_node *pos, *node = NULL;
        int hvalue;
        unsigned long flag;
 
-       if (avc_latest_notif_update(ae->avd.seqno, 1))
+       if (avc_latest_notif_update(avd->seqno, 1))
                goto out;
 
        node = avc_alloc_node();
        if (node) {
+               struct hlist_head *head;
+               struct hlist_node *next;
+               spinlock_t *lock;
+
                hvalue = avc_hash(ssid, tsid, tclass);
-               avc_node_populate(node, ssid, tsid, tclass, ae);
+               avc_node_populate(node, ssid, tsid, tclass, avd);
+
+               head = &avc_cache.slots[hvalue];
+               lock = &avc_cache.slots_lock[hvalue];
 
-               spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag);
-               list_for_each_entry(pos, &avc_cache.slots[hvalue], list) {
+               spin_lock_irqsave(lock, flag);
+               hlist_for_each_entry(pos, next, head, list) {
                        if (pos->ae.ssid == ssid &&
                            pos->ae.tsid == tsid &&
                            pos->ae.tclass == tclass) {
@@ -484,9 +484,9 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct avc_en
                                goto found;
                        }
                }
-               list_add_rcu(&node->list, &avc_cache.slots[hvalue]);
+               hlist_add_head_rcu(&node->list, head);
 found:
-               spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag);
+               spin_unlock_irqrestore(lock, flag);
        }
 out:
        return node;
@@ -742,17 +742,22 @@ static inline int avc_sidcmp(u32 x, u32 y)
  * @event : Updating event
  * @perms : Permission mask bits
  * @ssid,@tsid,@tclass : identifier of an AVC entry
+ * @seqno : sequence number when decision was made
  *
  * if a valid AVC entry doesn't exist,this function returns -ENOENT.
  * if kmalloc() called internal returns NULL, this function returns -ENOMEM.
  * otherwise, this function update the AVC entry. The original AVC-entry object
  * will release later by RCU.
  */
-static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass)
+static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
+                          u32 seqno)
 {
        int hvalue, rc = 0;
        unsigned long flag;
        struct avc_node *pos, *node, *orig = NULL;
+       struct hlist_head *head;
+       struct hlist_node *next;
+       spinlock_t *lock;
 
        node = avc_alloc_node();
        if (!node) {
@@ -762,12 +767,17 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass)
 
        /* Lock the target slot */
        hvalue = avc_hash(ssid, tsid, tclass);
-       spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag);
 
-       list_for_each_entry(pos, &avc_cache.slots[hvalue], list) {
+       head = &avc_cache.slots[hvalue];
+       lock = &avc_cache.slots_lock[hvalue];
+
+       spin_lock_irqsave(lock, flag);
+
+       hlist_for_each_entry(pos, next, head, list) {
                if (ssid == pos->ae.ssid &&
                    tsid == pos->ae.tsid &&
-                   tclass == pos->ae.tclass){
+                   tclass == pos->ae.tclass &&
+                   seqno == pos->ae.avd.seqno){
                        orig = pos;
                        break;
                }
@@ -783,7 +793,7 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass)
         * Copy and replace original node.
         */
 
-       avc_node_populate(node, ssid, tsid, tclass, &orig->ae);
+       avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd);
 
        switch (event) {
        case AVC_CALLBACK_GRANT:
@@ -808,7 +818,7 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass)
        }
        avc_node_replace(node, orig);
 out_unlock:
-       spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag);
+       spin_unlock_irqrestore(lock, flag);
 out:
        return rc;
 }
@@ -823,18 +833,24 @@ int avc_ss_reset(u32 seqno)
        int i, rc = 0, tmprc;
        unsigned long flag;
        struct avc_node *node;
+       struct hlist_head *head;
+       struct hlist_node *next;
+       spinlock_t *lock;
 
        for (i = 0; i < AVC_CACHE_SLOTS; i++) {
-               spin_lock_irqsave(&avc_cache.slots_lock[i], flag);
+               head = &avc_cache.slots[i];
+               lock = &avc_cache.slots_lock[i];
+
+               spin_lock_irqsave(lock, flag);
                /*
                 * With preemptable RCU, the outer spinlock does not
                 * prevent RCU grace periods from ending.
                 */
                rcu_read_lock();
-               list_for_each_entry(node, &avc_cache.slots[i], list)
+               hlist_for_each_entry(node, next, head, list)
                        avc_node_delete(node);
                rcu_read_unlock();
-               spin_unlock_irqrestore(&avc_cache.slots_lock[i], flag);
+               spin_unlock_irqrestore(lock, flag);
        }
 
        for (c = avc_callbacks; c; c = c->next) {
@@ -875,10 +891,10 @@ int avc_ss_reset(u32 seqno)
 int avc_has_perm_noaudit(u32 ssid, u32 tsid,
                         u16 tclass, u32 requested,
                         unsigned flags,
-                        struct av_decision *avd)
+                        struct av_decision *in_avd)
 {
        struct avc_node *node;
-       struct avc_entry entry, *p_ae;
+       struct av_decision avd_entry, *avd;
        int rc = 0;
        u32 denied;
 
@@ -886,29 +902,34 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
 
        rcu_read_lock();
 
-       node = avc_lookup(ssid, tsid, tclass, requested);
+       node = avc_lookup(ssid, tsid, tclass);
        if (!node) {
                rcu_read_unlock();
-               rc = security_compute_av(ssid, tsid, tclass, requested, &entry.avd);
+
+               if (in_avd)
+                       avd = in_avd;
+               else
+                       avd = &avd_entry;
+
+               rc = security_compute_av(ssid, tsid, tclass, requested, avd);
                if (rc)
                        goto out;
                rcu_read_lock();
-               node = avc_insert(ssid, tsid, tclass, &entry);
+               node = avc_insert(ssid, tsid, tclass, avd);
+       } else {
+               if (in_avd)
+                       memcpy(in_avd, &node->ae.avd, sizeof(*in_avd));
+               avd = &node->ae.avd;
        }
 
-       p_ae = node ? &node->ae : &entry;
-
-       if (avd)
-               memcpy(avd, &p_ae->avd, sizeof(*avd));
-
-       denied = requested & ~(p_ae->avd.allowed);
+       denied = requested & ~(avd->allowed);
 
        if (denied) {
                if (flags & AVC_STRICT)
                        rc = -EACCES;
                else if (!selinux_enforcing || security_permissive_sid(ssid))
                        avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
-                                       tsid, tclass);
+                                       tsid, tclass, avd->seqno);
                else
                        rc = -EACCES;
        }
index 00815973d4126492d596f278431c342bc7b6efc6..7c52ba243c6490acf9d22f719763a3931fbaabfb 100644 (file)
@@ -89,7 +89,7 @@
 #define XATTR_SELINUX_SUFFIX "selinux"
 #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
 
-#define NUM_SEL_MNT_OPTS 4
+#define NUM_SEL_MNT_OPTS 5
 
 extern unsigned int policydb_loaded_version;
 extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
@@ -353,6 +353,7 @@ enum {
        Opt_fscontext = 2,
        Opt_defcontext = 3,
        Opt_rootcontext = 4,
+       Opt_labelsupport = 5,
 };
 
 static const match_table_t tokens = {
@@ -360,6 +361,7 @@ static const match_table_t tokens = {
        {Opt_fscontext, FSCONTEXT_STR "%s"},
        {Opt_defcontext, DEFCONTEXT_STR "%s"},
        {Opt_rootcontext, ROOTCONTEXT_STR "%s"},
+       {Opt_labelsupport, LABELSUPP_STR},
        {Opt_error, NULL},
 };
 
@@ -431,7 +433,7 @@ static int sb_finish_set_opts(struct super_block *sb)
                }
        }
 
-       sbsec->initialized = 1;
+       sbsec->flags |= (SE_SBINITIALIZED | SE_SBLABELSUPP);
 
        if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
                printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
@@ -441,6 +443,12 @@ static int sb_finish_set_opts(struct super_block *sb)
                       sb->s_id, sb->s_type->name,
                       labeling_behaviors[sbsec->behavior-1]);
 
+       if (sbsec->behavior == SECURITY_FS_USE_GENFS ||
+           sbsec->behavior == SECURITY_FS_USE_MNTPOINT ||
+           sbsec->behavior == SECURITY_FS_USE_NONE ||
+           sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
+               sbsec->flags &= ~SE_SBLABELSUPP;
+
        /* Initialize the root inode. */
        rc = inode_doinit_with_dentry(root_inode, root);
 
@@ -487,23 +495,22 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
 
        security_init_mnt_opts(opts);
 
-       if (!sbsec->initialized)
+       if (!(sbsec->flags & SE_SBINITIALIZED))
                return -EINVAL;
 
        if (!ss_initialized)
                return -EINVAL;
 
-       /*
-        * if we ever use sbsec flags for anything other than tracking mount
-        * settings this is going to need a mask
-        */
-       tmp = sbsec->flags;
+       tmp = sbsec->flags & SE_MNTMASK;
        /* count the number of mount options for this sb */
        for (i = 0; i < 8; i++) {
                if (tmp & 0x01)
                        opts->num_mnt_opts++;
                tmp >>= 1;
        }
+       /* Check if the Label support flag is set */
+       if (sbsec->flags & SE_SBLABELSUPP)
+               opts->num_mnt_opts++;
 
        opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC);
        if (!opts->mnt_opts) {
@@ -549,6 +556,10 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
                opts->mnt_opts[i] = context;
                opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;
        }
+       if (sbsec->flags & SE_SBLABELSUPP) {
+               opts->mnt_opts[i] = NULL;
+               opts->mnt_opts_flags[i++] = SE_SBLABELSUPP;
+       }
 
        BUG_ON(i != opts->num_mnt_opts);
 
@@ -562,8 +573,10 @@ out_free:
 static int bad_option(struct superblock_security_struct *sbsec, char flag,
                      u32 old_sid, u32 new_sid)
 {
+       char mnt_flags = sbsec->flags & SE_MNTMASK;
+
        /* check if the old mount command had the same options */
-       if (sbsec->initialized)
+       if (sbsec->flags & SE_SBINITIALIZED)
                if (!(sbsec->flags & flag) ||
                    (old_sid != new_sid))
                        return 1;
@@ -571,8 +584,8 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
        /* check if we were passed the same options twice,
         * aka someone passed context=a,context=b
         */
-       if (!sbsec->initialized)
-               if (sbsec->flags & flag)
+       if (!(sbsec->flags & SE_SBINITIALIZED))
+               if (mnt_flags & flag)
                        return 1;
        return 0;
 }
@@ -626,7 +639,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
         * this sb does not set any security options.  (The first options
         * will be used for both mounts)
         */
-       if (sbsec->initialized && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
+       if ((sbsec->flags & SE_SBINITIALIZED) && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
            && (num_opts == 0))
                goto out;
 
@@ -637,6 +650,9 @@ static int selinux_set_mnt_opts(struct super_block *sb,
         */
        for (i = 0; i < num_opts; i++) {
                u32 sid;
+
+               if (flags[i] == SE_SBLABELSUPP)
+                       continue;
                rc = security_context_to_sid(mount_options[i],
                                             strlen(mount_options[i]), &sid);
                if (rc) {
@@ -690,19 +706,19 @@ static int selinux_set_mnt_opts(struct super_block *sb,
                }
        }
 
-       if (sbsec->initialized) {
+       if (sbsec->flags & SE_SBINITIALIZED) {
                /* previously mounted with options, but not on this attempt? */
-               if (sbsec->flags && !num_opts)
+               if ((sbsec->flags & SE_MNTMASK) && !num_opts)
                        goto out_double_mount;
                rc = 0;
                goto out;
        }
 
        if (strcmp(sb->s_type->name, "proc") == 0)
-               sbsec->proc = 1;
+               sbsec->flags |= SE_SBPROC;
 
        /* Determine the labeling behavior to use for this filesystem type. */
-       rc = security_fs_use(sbsec->proc ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid);
+       rc = security_fs_use((sbsec->flags & SE_SBPROC) ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid);
        if (rc) {
                printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
                       __func__, sb->s_type->name, rc);
@@ -806,10 +822,10 @@ static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
        }
 
        /* how can we clone if the old one wasn't set up?? */
-       BUG_ON(!oldsbsec->initialized);
+       BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED));
 
        /* if fs is reusing a sb, just let its options stand... */
-       if (newsbsec->initialized)
+       if (newsbsec->flags & SE_SBINITIALIZED)
                return;
 
        mutex_lock(&newsbsec->lock);
@@ -917,7 +933,8 @@ static int selinux_parse_opts_str(char *options,
                                goto out_err;
                        }
                        break;
-
+               case Opt_labelsupport:
+                       break;
                default:
                        rc = -EINVAL;
                        printk(KERN_WARNING "SELinux:  unknown mount option\n");
@@ -999,7 +1016,12 @@ static void selinux_write_opts(struct seq_file *m,
        char *prefix;
 
        for (i = 0; i < opts->num_mnt_opts; i++) {
-               char *has_comma = strchr(opts->mnt_opts[i], ',');
+               char *has_comma;
+
+               if (opts->mnt_opts[i])
+                       has_comma = strchr(opts->mnt_opts[i], ',');
+               else
+                       has_comma = NULL;
 
                switch (opts->mnt_opts_flags[i]) {
                case CONTEXT_MNT:
@@ -1014,6 +1036,10 @@ static void selinux_write_opts(struct seq_file *m,
                case DEFCONTEXT_MNT:
                        prefix = DEFCONTEXT_STR;
                        break;
+               case SE_SBLABELSUPP:
+                       seq_putc(m, ',');
+                       seq_puts(m, LABELSUPP_STR);
+                       continue;
                default:
                        BUG();
                };
@@ -1209,7 +1235,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                goto out_unlock;
 
        sbsec = inode->i_sb->s_security;
-       if (!sbsec->initialized) {
+       if (!(sbsec->flags & SE_SBINITIALIZED)) {
                /* Defer initialization until selinux_complete_init,
                   after the initial policy is loaded and the security
                   server is ready to handle calls. */
@@ -1237,19 +1263,26 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                        dentry = d_find_alias(inode);
                }
                if (!dentry) {
-                       printk(KERN_WARNING "SELinux: %s:  no dentry for dev=%s "
-                              "ino=%ld\n", __func__, inode->i_sb->s_id,
-                              inode->i_ino);
+                       /*
+                        * this is can be hit on boot when a file is accessed
+                        * before the policy is loaded.  When we load policy we
+                        * may find inodes that have no dentry on the
+                        * sbsec->isec_head list.  No reason to complain as these
+                        * will get fixed up the next time we go through
+                        * inode_doinit with a dentry, before these inodes could
+                        * be used again by userspace.
+                        */
                        goto out_unlock;
                }
 
                len = INITCONTEXTLEN;
-               context = kmalloc(len, GFP_NOFS);
+               context = kmalloc(len+1, GFP_NOFS);
                if (!context) {
                        rc = -ENOMEM;
                        dput(dentry);
                        goto out_unlock;
                }
+               context[len] = '\0';
                rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
                                           context, len);
                if (rc == -ERANGE) {
@@ -1262,12 +1295,13 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                        }
                        kfree(context);
                        len = rc;
-                       context = kmalloc(len, GFP_NOFS);
+                       context = kmalloc(len+1, GFP_NOFS);
                        if (!context) {
                                rc = -ENOMEM;
                                dput(dentry);
                                goto out_unlock;
                        }
+                       context[len] = '\0';
                        rc = inode->i_op->getxattr(dentry,
                                                   XATTR_NAME_SELINUX,
                                                   context, len);
@@ -1289,10 +1323,19 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                                                             sbsec->def_sid,
                                                             GFP_NOFS);
                        if (rc) {
-                               printk(KERN_WARNING "SELinux: %s:  context_to_sid(%s) "
-                                      "returned %d for dev=%s ino=%ld\n",
-                                      __func__, context, -rc,
-                                      inode->i_sb->s_id, inode->i_ino);
+                               char *dev = inode->i_sb->s_id;
+                               unsigned long ino = inode->i_ino;
+
+                               if (rc == -EINVAL) {
+                                       if (printk_ratelimit())
+                                               printk(KERN_NOTICE "SELinux: inode=%lu on dev=%s was found to have an invalid "
+                                                       "context=%s.  This indicates you may need to relabel the inode or the "
+                                                       "filesystem in question.\n", ino, dev, context);
+                               } else {
+                                       printk(KERN_WARNING "SELinux: %s:  context_to_sid(%s) "
+                                              "returned %d for dev=%s ino=%ld\n",
+                                              __func__, context, -rc, dev, ino);
+                               }
                                kfree(context);
                                /* Leave with the unlabeled SID */
                                rc = 0;
@@ -1326,7 +1369,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                /* Default to the fs superblock SID. */
                isec->sid = sbsec->sid;
 
-               if (sbsec->proc && !S_ISLNK(inode->i_mode)) {
+               if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) {
                        struct proc_inode *proci = PROC_I(inode);
                        if (proci->pde) {
                                isec->sclass = inode_mode_to_security_class(inode->i_mode);
@@ -1587,7 +1630,7 @@ static int may_create(struct inode *dir,
        if (rc)
                return rc;
 
-       if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+       if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
                rc = security_transition_sid(sid, dsec->sid, tclass, &newsid);
                if (rc)
                        return rc;
@@ -1801,6 +1844,8 @@ static inline u32 open_file_to_av(struct file *file)
                        av |= FIFO_FILE__OPEN;
                else if (S_ISDIR(mode))
                        av |= DIR__OPEN;
+               else if (S_ISSOCK(mode))
+                       av |= SOCK_FILE__OPEN;
                else
                        printk(KERN_ERR "SELinux: WARNING: inside %s with "
                                "unknown mode:%o\n", __func__, mode);
@@ -1815,7 +1860,7 @@ static int selinux_ptrace_may_access(struct task_struct *child,
 {
        int rc;
 
-       rc = secondary_ops->ptrace_may_access(child, mode);
+       rc = cap_ptrace_may_access(child, mode);
        if (rc)
                return rc;
 
@@ -1832,7 +1877,7 @@ static int selinux_ptrace_traceme(struct task_struct *parent)
 {
        int rc;
 
-       rc = secondary_ops->ptrace_traceme(parent);
+       rc = cap_ptrace_traceme(parent);
        if (rc)
                return rc;
 
@@ -1848,7 +1893,7 @@ static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
        if (error)
                return error;
 
-       return secondary_ops->capget(target, effective, inheritable, permitted);
+       return cap_capget(target, effective, inheritable, permitted);
 }
 
 static int selinux_capset(struct cred *new, const struct cred *old,
@@ -1858,7 +1903,7 @@ static int selinux_capset(struct cred *new, const struct cred *old,
 {
        int error;
 
-       error = secondary_ops->capset(new, old,
+       error = cap_capset(new, old,
                                      effective, inheritable, permitted);
        if (error)
                return error;
@@ -1866,12 +1911,22 @@ static int selinux_capset(struct cred *new, const struct cred *old,
        return cred_has_perm(old, new, PROCESS__SETCAP);
 }
 
+/*
+ * (This comment used to live with the selinux_task_setuid hook,
+ * which was removed).
+ *
+ * Since setuid only affects the current process, and since the SELinux
+ * controls are not based on the Linux identity attributes, SELinux does not
+ * need to control this operation.  However, SELinux does control the use of
+ * the CAP_SETUID and CAP_SETGID capabilities using the capable hook.
+ */
+
 static int selinux_capable(struct task_struct *tsk, const struct cred *cred,
                           int cap, int audit)
 {
        int rc;
 
-       rc = secondary_ops->capable(tsk, cred, cap, audit);
+       rc = cap_capable(tsk, cred, cap, audit);
        if (rc)
                return rc;
 
@@ -1997,7 +2052,7 @@ static int selinux_syslog(int type)
 {
        int rc;
 
-       rc = secondary_ops->syslog(type);
+       rc = cap_syslog(type);
        if (rc)
                return rc;
 
@@ -2028,10 +2083,6 @@ static int selinux_syslog(int type)
  * mapping. 0 means there is enough memory for the allocation to
  * succeed and -ENOMEM implies there is not.
  *
- * Note that secondary_ops->capable and task_has_perm_noaudit return 0
- * if the capability is granted, but __vm_enough_memory requires 1 if
- * the capability is granted.
- *
  * Do not audit the selinux permission check, as this is applied to all
  * processes that allocate mappings.
  */
@@ -2058,7 +2109,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
        struct inode *inode = bprm->file->f_path.dentry->d_inode;
        int rc;
 
-       rc = secondary_ops->bprm_set_creds(bprm);
+       rc = cap_bprm_set_creds(bprm);
        if (rc)
                return rc;
 
@@ -2156,11 +2207,6 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
        return 0;
 }
 
-static int selinux_bprm_check_security(struct linux_binprm *bprm)
-{
-       return secondary_ops->bprm_check_security(bprm);
-}
-
 static int selinux_bprm_secureexec(struct linux_binprm *bprm)
 {
        const struct cred *cred = current_cred();
@@ -2180,7 +2226,7 @@ static int selinux_bprm_secureexec(struct linux_binprm *bprm)
                                        PROCESS__NOATSECURE, NULL);
        }
 
-       return (atsecure || secondary_ops->bprm_secureexec(bprm));
+       return (atsecure || cap_bprm_secureexec(bprm));
 }
 
 extern struct vfsmount *selinuxfs_mount;
@@ -2290,8 +2336,6 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
        struct rlimit *rlim, *initrlim;
        int rc, i;
 
-       secondary_ops->bprm_committing_creds(bprm);
-
        new_tsec = bprm->cred->security;
        if (new_tsec->sid == new_tsec->osid)
                return;
@@ -2337,8 +2381,6 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
        int rc, i;
        unsigned long flags;
 
-       secondary_ops->bprm_committed_creds(bprm);
-
        osid = tsec->osid;
        sid = tsec->sid;
 
@@ -2400,7 +2442,8 @@ static inline int selinux_option(char *option, int len)
        return (match_prefix(CONTEXT_STR, sizeof(CONTEXT_STR)-1, option, len) ||
                match_prefix(FSCONTEXT_STR, sizeof(FSCONTEXT_STR)-1, option, len) ||
                match_prefix(DEFCONTEXT_STR, sizeof(DEFCONTEXT_STR)-1, option, len) ||
-               match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len));
+               match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len) ||
+               match_prefix(LABELSUPP_STR, sizeof(LABELSUPP_STR)-1, option, len));
 }
 
 static inline void take_option(char **to, char *from, int *first, int len)
@@ -2513,11 +2556,6 @@ static int selinux_mount(char *dev_name,
                         void *data)
 {
        const struct cred *cred = current_cred();
-       int rc;
-
-       rc = secondary_ops->sb_mount(dev_name, path, type, flags, data);
-       if (rc)
-               return rc;
 
        if (flags & MS_REMOUNT)
                return superblock_has_perm(cred, path->mnt->mnt_sb,
@@ -2530,11 +2568,6 @@ static int selinux_mount(char *dev_name,
 static int selinux_umount(struct vfsmount *mnt, int flags)
 {
        const struct cred *cred = current_cred();
-       int rc;
-
-       rc = secondary_ops->sb_umount(mnt, flags);
-       if (rc)
-               return rc;
 
        return superblock_has_perm(cred, mnt->mnt_sb,
                                   FILESYSTEM__UNMOUNT, NULL);
@@ -2570,7 +2603,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
        sid = tsec->sid;
        newsid = tsec->create_sid;
 
-       if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+       if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
                rc = security_transition_sid(sid, dsec->sid,
                                             inode_mode_to_security_class(inode->i_mode),
                                             &newsid);
@@ -2585,14 +2618,14 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
        }
 
        /* Possibly defer initialization to selinux_complete_init. */
-       if (sbsec->initialized) {
+       if (sbsec->flags & SE_SBINITIALIZED) {
                struct inode_security_struct *isec = inode->i_security;
                isec->sclass = inode_mode_to_security_class(inode->i_mode);
                isec->sid = newsid;
                isec->initialized = 1;
        }
 
-       if (!ss_initialized || sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
+       if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP))
                return -EOPNOTSUPP;
 
        if (name) {
@@ -2622,21 +2655,11 @@ static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int ma
 
 static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
 {
-       int rc;
-
-       rc = secondary_ops->inode_link(old_dentry, dir, new_dentry);
-       if (rc)
-               return rc;
        return may_link(dir, old_dentry, MAY_LINK);
 }
 
 static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
 {
-       int rc;
-
-       rc = secondary_ops->inode_unlink(dir, dentry);
-       if (rc)
-               return rc;
        return may_link(dir, dentry, MAY_UNLINK);
 }
 
@@ -2657,12 +2680,6 @@ static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
 
 static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
 {
-       int rc;
-
-       rc = secondary_ops->inode_mknod(dir, dentry, mode, dev);
-       if (rc)
-               return rc;
-
        return may_create(dir, dentry, inode_mode_to_security_class(mode));
 }
 
@@ -2682,22 +2699,13 @@ static int selinux_inode_readlink(struct dentry *dentry)
 static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
 {
        const struct cred *cred = current_cred();
-       int rc;
 
-       rc = secondary_ops->inode_follow_link(dentry, nameidata);
-       if (rc)
-               return rc;
        return dentry_has_perm(cred, NULL, dentry, FILE__READ);
 }
 
 static int selinux_inode_permission(struct inode *inode, int mask)
 {
        const struct cred *cred = current_cred();
-       int rc;
-
-       rc = secondary_ops->inode_permission(inode, mask);
-       if (rc)
-               return rc;
 
        if (!mask) {
                /* No permission to check.  Existence test. */
@@ -2711,11 +2719,6 @@ static int selinux_inode_permission(struct inode *inode, int mask)
 static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
 {
        const struct cred *cred = current_cred();
-       int rc;
-
-       rc = secondary_ops->inode_setattr(dentry, iattr);
-       if (rc)
-               return rc;
 
        if (iattr->ia_valid & ATTR_FORCE)
                return 0;
@@ -2769,7 +2772,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
                return selinux_inode_setotherxattr(dentry, name);
 
        sbsec = inode->i_sb->s_security;
-       if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
+       if (!(sbsec->flags & SE_SBLABELSUPP))
                return -EOPNOTSUPP;
 
        if (!is_owner_or_cap(inode))
@@ -2931,16 +2934,6 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
        return len;
 }
 
-static int selinux_inode_need_killpriv(struct dentry *dentry)
-{
-       return secondary_ops->inode_need_killpriv(dentry);
-}
-
-static int selinux_inode_killpriv(struct dentry *dentry)
-{
-       return secondary_ops->inode_killpriv(dentry);
-}
-
 static void selinux_inode_getsecid(const struct inode *inode, u32 *secid)
 {
        struct inode_security_struct *isec = inode->i_security;
@@ -3078,18 +3071,13 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
                                 unsigned long prot)
 {
        const struct cred *cred = current_cred();
-       int rc;
-
-       rc = secondary_ops->file_mprotect(vma, reqprot, prot);
-       if (rc)
-               return rc;
 
        if (selinux_checkreqprot)
                prot = reqprot;
 
 #ifndef CONFIG_PPC32
        if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
-               rc = 0;
+               int rc = 0;
                if (vma->vm_start >= vma->vm_mm->start_brk &&
                    vma->vm_end <= vma->vm_mm->brk) {
                        rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP);
@@ -3239,12 +3227,6 @@ static int selinux_dentry_open(struct file *file, const struct cred *cred)
 
 static int selinux_task_create(unsigned long clone_flags)
 {
-       int rc;
-
-       rc = secondary_ops->task_create(clone_flags);
-       if (rc)
-               return rc;
-
        return current_has_perm(current, PROCESS__FORK);
 }
 
@@ -3277,14 +3259,6 @@ static int selinux_cred_prepare(struct cred *new, const struct cred *old,
        return 0;
 }
 
-/*
- * commit new credentials
- */
-static void selinux_cred_commit(struct cred *new, const struct cred *old)
-{
-       secondary_ops->cred_commit(new, old);
-}
-
 /*
  * set the security data for a kernel service
  * - all the creation contexts are set to unlabelled
@@ -3329,29 +3303,6 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
        return 0;
 }
 
-static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
-{
-       /* Since setuid only affects the current process, and
-          since the SELinux controls are not based on the Linux
-          identity attributes, SELinux does not need to control
-          this operation.  However, SELinux does control the use
-          of the CAP_SETUID and CAP_SETGID capabilities using the
-          capable hook. */
-       return 0;
-}
-
-static int selinux_task_fix_setuid(struct cred *new, const struct cred *old,
-                                  int flags)
-{
-       return secondary_ops->task_fix_setuid(new, old, flags);
-}
-
-static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
-{
-       /* See the comment for setuid above. */
-       return 0;
-}
-
 static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
 {
        return current_has_perm(p, PROCESS__SETPGID);
@@ -3372,17 +3323,11 @@ static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
        *secid = task_sid(p);
 }
 
-static int selinux_task_setgroups(struct group_info *group_info)
-{
-       /* See the comment for setuid above. */
-       return 0;
-}
-
 static int selinux_task_setnice(struct task_struct *p, int nice)
 {
        int rc;
 
-       rc = secondary_ops->task_setnice(p, nice);
+       rc = cap_task_setnice(p, nice);
        if (rc)
                return rc;
 
@@ -3393,7 +3338,7 @@ static int selinux_task_setioprio(struct task_struct *p, int ioprio)
 {
        int rc;
 
-       rc = secondary_ops->task_setioprio(p, ioprio);
+       rc = cap_task_setioprio(p, ioprio);
        if (rc)
                return rc;
 
@@ -3408,11 +3353,6 @@ static int selinux_task_getioprio(struct task_struct *p)
 static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
 {
        struct rlimit *old_rlim = current->signal->rlim + resource;
-       int rc;
-
-       rc = secondary_ops->task_setrlimit(resource, new_rlim);
-       if (rc)
-               return rc;
 
        /* Control the ability to change the hard limit (whether
           lowering or raising it), so that the hard limit can
@@ -3428,7 +3368,7 @@ static int selinux_task_setscheduler(struct task_struct *p, int policy, struct s
 {
        int rc;
 
-       rc = secondary_ops->task_setscheduler(p, policy, lp);
+       rc = cap_task_setscheduler(p, policy, lp);
        if (rc)
                return rc;
 
@@ -3451,10 +3391,6 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
        u32 perm;
        int rc;
 
-       rc = secondary_ops->task_kill(p, info, sig, secid);
-       if (rc)
-               return rc;
-
        if (!sig)
                perm = PROCESS__SIGNULL; /* null signal; existence test */
        else
@@ -3467,18 +3403,6 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
        return rc;
 }
 
-static int selinux_task_prctl(int option,
-                             unsigned long arg2,
-                             unsigned long arg3,
-                             unsigned long arg4,
-                             unsigned long arg5)
-{
-       /* The current prctl operations do not appear to require
-          any SELinux controls since they merely observe or modify
-          the state of the current process. */
-       return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5);
-}
-
 static int selinux_task_wait(struct task_struct *p)
 {
        return task_has_perm(p, current, PROCESS__SIGCHLD);
@@ -4047,10 +3971,6 @@ static int selinux_socket_unix_stream_connect(struct socket *sock,
        struct avc_audit_data ad;
        int err;
 
-       err = secondary_ops->unix_stream_connect(sock, other, newsk);
-       if (err)
-               return err;
-
        isec = SOCK_INODE(sock)->i_security;
        other_isec = SOCK_INODE(other)->i_security;
 
@@ -4844,7 +4764,7 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
 {
        int err;
 
-       err = secondary_ops->netlink_send(sk, skb);
+       err = cap_netlink_send(sk, skb);
        if (err)
                return err;
 
@@ -4859,7 +4779,7 @@ static int selinux_netlink_recv(struct sk_buff *skb, int capability)
        int err;
        struct avc_audit_data ad;
 
-       err = secondary_ops->netlink_recv(skb, capability);
+       err = cap_netlink_recv(skb, capability);
        if (err)
                return err;
 
@@ -5167,11 +5087,6 @@ static int selinux_shm_shmat(struct shmid_kernel *shp,
                             char __user *shmaddr, int shmflg)
 {
        u32 perms;
-       int rc;
-
-       rc = secondary_ops->shm_shmat(shp, shmaddr, shmflg);
-       if (rc)
-               return rc;
 
        if (shmflg & SHM_RDONLY)
                perms = SHM__READ;
@@ -5581,7 +5496,6 @@ static struct security_operations selinux_ops = {
        .netlink_recv =                 selinux_netlink_recv,
 
        .bprm_set_creds =               selinux_bprm_set_creds,
-       .bprm_check_security =          selinux_bprm_check_security,
        .bprm_committing_creds =        selinux_bprm_committing_creds,
        .bprm_committed_creds =         selinux_bprm_committed_creds,
        .bprm_secureexec =              selinux_bprm_secureexec,
@@ -5623,8 +5537,6 @@ static struct security_operations selinux_ops = {
        .inode_getsecurity =            selinux_inode_getsecurity,
        .inode_setsecurity =            selinux_inode_setsecurity,
        .inode_listsecurity =           selinux_inode_listsecurity,
-       .inode_need_killpriv =          selinux_inode_need_killpriv,
-       .inode_killpriv =               selinux_inode_killpriv,
        .inode_getsecid =               selinux_inode_getsecid,
 
        .file_permission =              selinux_file_permission,
@@ -5644,17 +5556,12 @@ static struct security_operations selinux_ops = {
        .task_create =                  selinux_task_create,
        .cred_free =                    selinux_cred_free,
        .cred_prepare =                 selinux_cred_prepare,
-       .cred_commit =                  selinux_cred_commit,
        .kernel_act_as =                selinux_kernel_act_as,
        .kernel_create_files_as =       selinux_kernel_create_files_as,
-       .task_setuid =                  selinux_task_setuid,
-       .task_fix_setuid =              selinux_task_fix_setuid,
-       .task_setgid =                  selinux_task_setgid,
        .task_setpgid =                 selinux_task_setpgid,
        .task_getpgid =                 selinux_task_getpgid,
        .task_getsid =                  selinux_task_getsid,
        .task_getsecid =                selinux_task_getsecid,
-       .task_setgroups =               selinux_task_setgroups,
        .task_setnice =                 selinux_task_setnice,
        .task_setioprio =               selinux_task_setioprio,
        .task_getioprio =               selinux_task_getioprio,
@@ -5664,7 +5571,6 @@ static struct security_operations selinux_ops = {
        .task_movememory =              selinux_task_movememory,
        .task_kill =                    selinux_task_kill,
        .task_wait =                    selinux_task_wait,
-       .task_prctl =                   selinux_task_prctl,
        .task_to_inode =                selinux_task_to_inode,
 
        .ipc_permission =               selinux_ipc_permission,
index c0c885427b91fe2ba57eebf5fa14f1a986410605..31df1d7c1aee2c0ead5536846a3ab7a46a074278 100644 (file)
@@ -24,6 +24,7 @@
    S_(SECCLASS_CHR_FILE, CHR_FILE__EXECMOD, "execmod")
    S_(SECCLASS_CHR_FILE, CHR_FILE__OPEN, "open")
    S_(SECCLASS_BLK_FILE, BLK_FILE__OPEN, "open")
+   S_(SECCLASS_SOCK_FILE, SOCK_FILE__OPEN, "open")
    S_(SECCLASS_FIFO_FILE, FIFO_FILE__OPEN, "open")
    S_(SECCLASS_FD, FD__USE, "use")
    S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__CONNECTTO, "connectto")
    S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE, "nlmsg_write")
    S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_RELAY, "nlmsg_relay")
    S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV, "nlmsg_readpriv")
+   S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT, "nlmsg_tty_audit")
    S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_READ, "nlmsg_read")
    S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_WRITE, "nlmsg_write")
    S_(SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, "sendto")
index 0ba79fe00e11e834bd6c91cfd5020ff4563325f1..d645192ee950aaf74f2693943e38cd7dda9bfdbf 100644 (file)
 #define SOCK_FILE__SWAPON                         0x00004000UL
 #define SOCK_FILE__QUOTAON                        0x00008000UL
 #define SOCK_FILE__MOUNTON                        0x00010000UL
+#define SOCK_FILE__OPEN                           0x00020000UL
 #define FIFO_FILE__IOCTL                          0x00000001UL
 #define FIFO_FILE__READ                           0x00000002UL
 #define FIFO_FILE__WRITE                          0x00000004UL
 #define NETLINK_AUDIT_SOCKET__NLMSG_WRITE         0x00800000UL
 #define NETLINK_AUDIT_SOCKET__NLMSG_RELAY         0x01000000UL
 #define NETLINK_AUDIT_SOCKET__NLMSG_READPRIV      0x02000000UL
+#define NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT     0x04000000UL
 #define NETLINK_IP6FW_SOCKET__IOCTL               0x00000001UL
 #define NETLINK_IP6FW_SOCKET__READ                0x00000002UL
 #define NETLINK_IP6FW_SOCKET__WRITE               0x00000004UL
index 3cc45168f674fbe6c98c89f9597398af6778a95a..c4e062336ef3a6d6265d99f750e6ea8c6ba8f8f7 100644 (file)
@@ -60,9 +60,7 @@ struct superblock_security_struct {
        u32 def_sid;                    /* default SID for labeling */
        u32 mntpoint_sid;               /* SECURITY_FS_USE_MNTPOINT context for files */
        unsigned int behavior;          /* labeling behavior */
-       unsigned char initialized;      /* initialization flag */
        unsigned char flags;            /* which mount options were specified */
-       unsigned char proc;             /* proc fs */
        struct mutex lock;
        struct list_head isec_head;
        spinlock_t isec_lock;
index 72447370bc959f4551583d421993138dc9c513b2..5c3434f7626fdd5cbe81ff0120ad3f092713fd95 100644 (file)
 #define POLICYDB_VERSION_MAX   POLICYDB_VERSION_BOUNDARY
 #endif
 
+/* Mask for just the mount related flags */
+#define SE_MNTMASK     0x0f
+/* Super block security struct flags for mount options */
 #define CONTEXT_MNT    0x01
 #define FSCONTEXT_MNT  0x02
 #define ROOTCONTEXT_MNT        0x04
 #define DEFCONTEXT_MNT 0x08
+/* Non-mount related flags */
+#define SE_SBINITIALIZED       0x10
+#define SE_SBPROC              0x20
+#define SE_SBLABELSUPP 0x40
 
 #define CONTEXT_STR    "context="
 #define FSCONTEXT_STR  "fscontext="
 #define ROOTCONTEXT_STR        "rootcontext="
 #define DEFCONTEXT_STR "defcontext="
+#define LABELSUPP_STR "seclabel"
 
 struct netlbl_lsm_secattr;
 
@@ -80,7 +88,6 @@ int security_policycap_supported(unsigned int req_cap);
 #define SEL_VEC_MAX 32
 struct av_decision {
        u32 allowed;
-       u32 decided;
        u32 auditallow;
        u32 auditdeny;
        u32 seqno;
index 4ed7bab89c5938675fd21442550a0791826077d8..c6875fd3b9d61445009f22d3ea8b91037b864d25 100644 (file)
@@ -113,7 +113,7 @@ static struct nlmsg_perm nlmsg_audit_perms[] =
        { AUDIT_USER,           NETLINK_AUDIT_SOCKET__NLMSG_RELAY    },
        { AUDIT_SIGNAL_INFO,    NETLINK_AUDIT_SOCKET__NLMSG_READ     },
        { AUDIT_TTY_GET,        NETLINK_AUDIT_SOCKET__NLMSG_READ     },
-       { AUDIT_TTY_SET,        NETLINK_AUDIT_SOCKET__NLMSG_WRITE    },
+       { AUDIT_TTY_SET,        NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT   },
 };
 
 
index 01ec6d2c6b97d310490b7c70410f5461eecdab3b..d3c8b982cfb0e179cb11d130318f3ebf3fd5b50d 100644 (file)
@@ -595,7 +595,7 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
 
        length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT,
                          "%x %x %x %x %u",
-                         avd.allowed, avd.decided,
+                         avd.allowed, 0xffffffff,
                          avd.auditallow, avd.auditdeny,
                          avd.seqno);
 out2:
index c65e4fe4a0f176fc561717e6c28f0053332250e4..deeec6c013aef6dee9d664e3fe46b0a892d9f27e 100644 (file)
@@ -407,7 +407,6 @@ static int context_struct_compute_av(struct context *scontext,
         * Initialize the access vectors to the default values.
         */
        avd->allowed = 0;
-       avd->decided = 0xffffffff;
        avd->auditallow = 0;
        avd->auditdeny = 0xffffffff;
        avd->seqno = latest_granting;
@@ -743,7 +742,6 @@ int security_compute_av(u32 ssid,
 
        if (!ss_initialized) {
                avd->allowed = 0xffffffff;
-               avd->decided = 0xffffffff;
                avd->auditallow = 0;
                avd->auditdeny = 0xffffffff;
                avd->seqno = latest_granting;
index 2e0b83e77ffea727375a23e0abe29e894573a251..cfa19ca125e3596ed2b80b3bb0d2a0f05a594903 100644 (file)
@@ -162,8 +162,8 @@ int smk_access(char *subject_label, char *object_label, int request)
 
 /**
  * smk_curacc - determine if current has a specific access to an object
- * @object_label: a pointer to the object's Smack label
- * @request: the access requested, in "MAY" format
+ * @obj_label: a pointer to the object's Smack label
+ * @mode: the access requested, in "MAY" format
  *
  * This function checks the current subject label/object label pair
  * in the access rule list and returns 0 if the access is permitted,
index e7ded1326b0ff835cec48f4e448b0c69f8df1d04..84b62b5e9e2c681b3837a5da2a8cd2f39ce409f6 100644 (file)
@@ -91,6 +91,7 @@ struct inode_smack *new_inode_smack(char *smack)
 /**
  * smack_ptrace_may_access - Smack approval on PTRACE_ATTACH
  * @ctp: child task pointer
+ * @mode: ptrace attachment mode
  *
  * Returns 0 if access is OK, an error code otherwise
  *
@@ -203,9 +204,8 @@ static void smack_sb_free_security(struct super_block *sb)
 
 /**
  * smack_sb_copy_data - copy mount options data for processing
- * @type: file system type
  * @orig: where to start
- * @smackopts
+ * @smackopts: mount options string
  *
  * Returns 0 on success or -ENOMEM on error.
  *
@@ -331,7 +331,7 @@ static int smack_sb_statfs(struct dentry *dentry)
 /**
  * smack_sb_mount - Smack check for mounting
  * @dev_name: unused
- * @nd: mount point
+ * @path: mount point
  * @type: unused
  * @flags: unused
  * @data: unused
@@ -370,7 +370,7 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)
 
 /**
  * smack_inode_alloc_security - allocate an inode blob
- * @inode - the inode in need of a blob
+ * @inode: the inode in need of a blob
  *
  * Returns 0 if it gets a blob, -ENOMEM otherwise
  */
@@ -384,7 +384,7 @@ static int smack_inode_alloc_security(struct inode *inode)
 
 /**
  * smack_inode_free_security - free an inode blob
- * @inode - the inode with a blob
+ * @inode: the inode with a blob
  *
  * Clears the blob pointer in inode
  */
@@ -538,7 +538,6 @@ static int smack_inode_rename(struct inode *old_inode,
  * smack_inode_permission - Smack version of permission()
  * @inode: the inode in question
  * @mask: the access requested
- * @nd: unused
  *
  * This is the important Smack hook.
  *
@@ -701,8 +700,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name)
  * @inode: the object
  * @name: attribute name
  * @buffer: where to put the result
- * @size: size of the buffer
- * @err: unused
+ * @alloc: unused
  *
  * Returns the size of the attribute or an error code
  */
@@ -864,7 +862,7 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd,
 /**
  * smack_file_lock - Smack check on file locking
  * @file: the object
- * @cmd unused
+ * @cmd: unused
  *
  * Returns 0 if current has write access, error code otherwise
  */
@@ -1003,8 +1001,8 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
        return 0;
 }
 
-/*
- * commit new credentials
+/**
+ * smack_cred_commit - commit new credentials
  * @new: the new credentials
  * @old: the original credentials
  */
@@ -1014,8 +1012,8 @@ static void smack_cred_commit(struct cred *new, const struct cred *old)
 
 /**
  * smack_kernel_act_as - Set the subjective context in a set of credentials
- * @new points to the set of credentials to be modified.
- * @secid specifies the security ID to be set
+ * @new: points to the set of credentials to be modified.
+ * @secid: specifies the security ID to be set
  *
  * Set the security data for a kernel service.
  */
@@ -1032,8 +1030,8 @@ static int smack_kernel_act_as(struct cred *new, u32 secid)
 
 /**
  * smack_kernel_create_files_as - Set the file creation label in a set of creds
- * @new points to the set of credentials to be modified
- * @inode points to the inode to use as a reference
+ * @new: points to the set of credentials to be modified
+ * @inode: points to the inode to use as a reference
  *
  * Set the file creation context in a set of credentials to the same
  * as the objective context of the specified inode
@@ -1242,7 +1240,7 @@ static int smack_task_wait(struct task_struct *p)
 /**
  * smack_task_to_inode - copy task smack into the inode blob
  * @p: task to copy from
- * inode: inode to copy to
+ * @inode: inode to copy to
  *
  * Sets the smack pointer in the inode security blob
  */
@@ -1260,7 +1258,7 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
  * smack_sk_alloc_security - Allocate a socket blob
  * @sk: the socket
  * @family: unused
- * @priority: memory allocation priority
+ * @gfp_flags: memory allocation flags
  *
  * Assign Smack pointers to current
  *
@@ -1974,7 +1972,7 @@ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag)
 
 /**
  * smack_ipc_getsecid - Extract smack security id
- * @ipcp: the object permissions
+ * @ipp: the object permissions
  * @secid: where result will be saved
  */
 static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid)
@@ -2251,7 +2249,7 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other)
 /**
  * smack_socket_sendmsg - Smack check based on destination host
  * @sock: the socket
- * @msghdr: the message
+ * @msg: the message
  * @size: the size of the message
  *
  * Return 0 if the current subject can write to the destination
@@ -2292,8 +2290,7 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
 
 
 /**
- * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat
- *     pair to smack
+ * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat pair to smack
  * @sap: netlabel secattr
  * @sip: where to put the result
  *
@@ -2414,7 +2411,7 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
  * @sock: the socket
  * @optval: user's destination
  * @optlen: size thereof
- * @len: max thereoe
+ * @len: max thereof
  *
  * returns zero on success, an error code otherwise
  */
@@ -2749,7 +2746,7 @@ static void smack_audit_rule_free(void *vrule)
 
 #endif /* CONFIG_AUDIT */
 
-/*
+/**
  * smack_secid_to_secctx - return the smack label for a secid
  * @secid: incoming integer
  * @secdata: destination
@@ -2766,7 +2763,7 @@ static int smack_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
        return 0;
 }
 
-/*
+/**
  * smack_secctx_to_secid - return the secid for a smack label
  * @secdata: smack label
  * @seclen: how long result is
@@ -2780,11 +2777,10 @@ static int smack_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
        return 0;
 }
 
-/*
+/**
  * smack_release_secctx - don't do anything.
- * @key_ref: unused
- * @context: unused
- * @perm: unused
+ * @secdata: unused
+ * @seclen: unused
  *
  * Exists to make sure nothing gets done, and properly
  */
index 51f0efc50dab46a9200f98b6d22db0039a9a0653..a1b57e4dba3e0865902a7a3691ee1d3afc1f8748 100644 (file)
@@ -245,7 +245,7 @@ out:
 
 /**
  * smk_write_load - write() for /smack/load
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @count: bytes sent
  * @ppos: where to start - must be 0
@@ -402,6 +402,7 @@ static void smk_cipso_doi(void)
 
 /**
  * smk_unlbl_ambient - initialize the unlabeled domain
+ * @oldambient: previous domain string
  */
 static void smk_unlbl_ambient(char *oldambient)
 {
@@ -513,7 +514,7 @@ static int smk_open_cipso(struct inode *inode, struct file *file)
 
 /**
  * smk_write_cipso - write() for /smack/cipso
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @count: bytes sent
  * @ppos: where to start
@@ -733,7 +734,7 @@ static void smk_netlbladdr_insert(struct smk_netlbladdr *new)
 
 /**
  * smk_write_netlbladdr - write() for /smack/netlabel
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @count: bytes sent
  * @ppos: where to start
@@ -884,7 +885,7 @@ static ssize_t smk_read_doi(struct file *filp, char __user *buf,
 
 /**
  * smk_write_doi - write() for /smack/doi
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @count: bytes sent
  * @ppos: where to start
@@ -949,7 +950,7 @@ static ssize_t smk_read_direct(struct file *filp, char __user *buf,
 
 /**
  * smk_write_direct - write() for /smack/direct
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @count: bytes sent
  * @ppos: where to start
@@ -1024,7 +1025,7 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf,
 
 /**
  * smk_write_ambient - write() for /smack/ambient
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @count: bytes sent
  * @ppos: where to start
@@ -1099,7 +1100,7 @@ static ssize_t smk_read_onlycap(struct file *filp, char __user *buf,
 
 /**
  * smk_write_onlycap - write() for /smack/onlycap
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @count: bytes sent
  * @ppos: where to start
diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig
new file mode 100644 (file)
index 0000000..c8f3857
--- /dev/null
@@ -0,0 +1,11 @@
+config SECURITY_TOMOYO
+       bool "TOMOYO Linux Support"
+       depends on SECURITY
+       select SECURITYFS
+       select SECURITY_PATH
+       default n
+       help
+         This selects TOMOYO Linux, pathname-based access control.
+         Required userspace tools and further information may be
+         found at <http://tomoyo.sourceforge.jp/>.
+         If you are unsure how to answer this question, answer N.
diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile
new file mode 100644 (file)
index 0000000..10ccd68
--- /dev/null
@@ -0,0 +1 @@
+obj-y = common.o realpath.o tomoyo.o domain.o file.o
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
new file mode 100644 (file)
index 0000000..92cea65
--- /dev/null
@@ -0,0 +1,2206 @@
+/*
+ * security/tomoyo/common.c
+ *
+ * Common functions for TOMOYO.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#include <linux/uaccess.h>
+#include <linux/security.h>
+#include <linux/hardirq.h>
+#include "realpath.h"
+#include "common.h"
+#include "tomoyo.h"
+
+/* Has loading policy done? */
+bool tomoyo_policy_loaded;
+
+/* String table for functionality that takes 4 modes. */
+static const char *tomoyo_mode_4[4] = {
+       "disabled", "learning", "permissive", "enforcing"
+};
+/* String table for functionality that takes 2 modes. */
+static const char *tomoyo_mode_2[4] = {
+       "disabled", "enabled", "enabled", "enabled"
+};
+
+/* Table for profile. */
+static struct {
+       const char *keyword;
+       unsigned int current_value;
+       const unsigned int max_value;
+} tomoyo_control_array[TOMOYO_MAX_CONTROL_INDEX] = {
+       [TOMOYO_MAC_FOR_FILE]     = { "MAC_FOR_FILE",        0,       3 },
+       [TOMOYO_MAX_ACCEPT_ENTRY] = { "MAX_ACCEPT_ENTRY", 2048, INT_MAX },
+       [TOMOYO_VERBOSE]          = { "TOMOYO_VERBOSE",      1,       1 },
+};
+
+/* Profile table. Memory is allocated as needed. */
+static struct tomoyo_profile {
+       unsigned int value[TOMOYO_MAX_CONTROL_INDEX];
+       const struct tomoyo_path_info *comment;
+} *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES];
+
+/* Permit policy management by non-root user? */
+static bool tomoyo_manage_by_non_root;
+
+/* Utility functions. */
+
+/* Open operation for /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_open_control(const u8 type, struct file *file);
+/* Close /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_close_control(struct file *file);
+/* Read operation for /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_read_control(struct file *file, char __user *buffer,
+                              const int buffer_len);
+/* Write operation for /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_write_control(struct file *file, const char __user *buffer,
+                               const int buffer_len);
+
+/**
+ * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value.
+ *
+ * @str: Pointer to the string.
+ *
+ * Returns true if @str is a \ooo style octal value, false otherwise.
+ *
+ * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF.
+ * This function verifies that \ooo is in valid range.
+ */
+static inline bool tomoyo_is_byte_range(const char *str)
+{
+       return *str >= '0' && *str++ <= '3' &&
+               *str >= '0' && *str++ <= '7' &&
+               *str >= '0' && *str <= '7';
+}
+
+/**
+ * tomoyo_is_alphabet_char - Check whether the character is an alphabet.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an alphabet character, false otherwise.
+ */
+static inline bool tomoyo_is_alphabet_char(const char c)
+{
+       return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+
+/**
+ * tomoyo_make_byte - Make byte value from three octal characters.
+ *
+ * @c1: The first character.
+ * @c2: The second character.
+ * @c3: The third character.
+ *
+ * Returns byte value.
+ */
+static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3)
+{
+       return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0');
+}
+
+/**
+ * tomoyo_str_starts - Check whether the given string starts with the given keyword.
+ *
+ * @src:  Pointer to pointer to the string.
+ * @find: Pointer to the keyword.
+ *
+ * Returns true if @src starts with @find, false otherwise.
+ *
+ * The @src is updated to point the first character after the @find
+ * if @src starts with @find.
+ */
+static bool tomoyo_str_starts(char **src, const char *find)
+{
+       const int len = strlen(find);
+       char *tmp = *src;
+
+       if (strncmp(tmp, find, len))
+               return false;
+       tmp += len;
+       *src = tmp;
+       return true;
+}
+
+/**
+ * tomoyo_normalize_line - Format string.
+ *
+ * @buffer: The line to normalize.
+ *
+ * Leading and trailing whitespaces are removed.
+ * Multiple whitespaces are packed into single space.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_normalize_line(unsigned char *buffer)
+{
+       unsigned char *sp = buffer;
+       unsigned char *dp = buffer;
+       bool first = true;
+
+       while (tomoyo_is_invalid(*sp))
+               sp++;
+       while (*sp) {
+               if (!first)
+                       *dp++ = ' ';
+               first = false;
+               while (tomoyo_is_valid(*sp))
+                       *dp++ = *sp++;
+               while (tomoyo_is_invalid(*sp))
+                       sp++;
+       }
+       *dp = '\0';
+}
+
+/**
+ * tomoyo_is_correct_path - Validate a pathname.
+ * @filename:     The pathname to check.
+ * @start_type:   Should the pathname start with '/'?
+ *                1 = must / -1 = must not / 0 = don't care
+ * @pattern_type: Can the pathname contain a wildcard?
+ *                1 = must / -1 = must not / 0 = don't care
+ * @end_type:     Should the pathname end with '/'?
+ *                1 = must / -1 = must not / 0 = don't care
+ * @function:     The name of function calling me.
+ *
+ * Check whether the given filename follows the naming rules.
+ * Returns true if @filename follows the naming rules, false otherwise.
+ */
+bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
+                           const s8 pattern_type, const s8 end_type,
+                           const char *function)
+{
+       bool contains_pattern = false;
+       unsigned char c;
+       unsigned char d;
+       unsigned char e;
+       const char *original_filename = filename;
+
+       if (!filename)
+               goto out;
+       c = *filename;
+       if (start_type == 1) { /* Must start with '/' */
+               if (c != '/')
+                       goto out;
+       } else if (start_type == -1) { /* Must not start with '/' */
+               if (c == '/')
+                       goto out;
+       }
+       if (c)
+               c = *(filename + strlen(filename) - 1);
+       if (end_type == 1) { /* Must end with '/' */
+               if (c != '/')
+                       goto out;
+       } else if (end_type == -1) { /* Must not end with '/' */
+               if (c == '/')
+                       goto out;
+       }
+       while ((c = *filename++) != '\0') {
+               if (c == '\\') {
+                       switch ((c = *filename++)) {
+                       case '\\':  /* "\\" */
+                               continue;
+                       case '$':   /* "\$" */
+                       case '+':   /* "\+" */
+                       case '?':   /* "\?" */
+                       case '*':   /* "\*" */
+                       case '@':   /* "\@" */
+                       case 'x':   /* "\x" */
+                       case 'X':   /* "\X" */
+                       case 'a':   /* "\a" */
+                       case 'A':   /* "\A" */
+                       case '-':   /* "\-" */
+                               if (pattern_type == -1)
+                                       break; /* Must not contain pattern */
+                               contains_pattern = true;
+                               continue;
+                       case '0':   /* "\ooo" */
+                       case '1':
+                       case '2':
+                       case '3':
+                               d = *filename++;
+                               if (d < '0' || d > '7')
+                                       break;
+                               e = *filename++;
+                               if (e < '0' || e > '7')
+                                       break;
+                               c = tomoyo_make_byte(c, d, e);
+                               if (tomoyo_is_invalid(c))
+                                       continue; /* pattern is not \000 */
+                       }
+                       goto out;
+               } else if (tomoyo_is_invalid(c)) {
+                       goto out;
+               }
+       }
+       if (pattern_type == 1) { /* Must contain pattern */
+               if (!contains_pattern)
+                       goto out;
+       }
+       return true;
+ out:
+       printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function,
+              original_filename);
+       return false;
+}
+
+/**
+ * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules.
+ * @domainname:   The domainname to check.
+ * @function:     The name of function calling me.
+ *
+ * Returns true if @domainname follows the naming rules, false otherwise.
+ */
+bool tomoyo_is_correct_domain(const unsigned char *domainname,
+                             const char *function)
+{
+       unsigned char c;
+       unsigned char d;
+       unsigned char e;
+       const char *org_domainname = domainname;
+
+       if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME,
+                                  TOMOYO_ROOT_NAME_LEN))
+               goto out;
+       domainname += TOMOYO_ROOT_NAME_LEN;
+       if (!*domainname)
+               return true;
+       do {
+               if (*domainname++ != ' ')
+                       goto out;
+               if (*domainname++ != '/')
+                       goto out;
+               while ((c = *domainname) != '\0' && c != ' ') {
+                       domainname++;
+                       if (c == '\\') {
+                               c = *domainname++;
+                               switch ((c)) {
+                               case '\\':  /* "\\" */
+                                       continue;
+                               case '0':   /* "\ooo" */
+                               case '1':
+                               case '2':
+                               case '3':
+                                       d = *domainname++;
+                                       if (d < '0' || d > '7')
+                                               break;
+                                       e = *domainname++;
+                                       if (e < '0' || e > '7')
+                                               break;
+                                       c = tomoyo_make_byte(c, d, e);
+                                       if (tomoyo_is_invalid(c))
+                                               /* pattern is not \000 */
+                                               continue;
+                               }
+                               goto out;
+                       } else if (tomoyo_is_invalid(c)) {
+                               goto out;
+                       }
+               }
+       } while (*domainname);
+       return true;
+ out:
+       printk(KERN_DEBUG "%s: Invalid domainname '%s'\n", function,
+              org_domainname);
+       return false;
+}
+
+/**
+ * tomoyo_is_domain_def - Check whether the given token can be a domainname.
+ *
+ * @buffer: The token to check.
+ *
+ * Returns true if @buffer possibly be a domainname, false otherwise.
+ */
+bool tomoyo_is_domain_def(const unsigned char *buffer)
+{
+       return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN);
+}
+
+/**
+ * tomoyo_find_domain - Find a domain by the given name.
+ *
+ * @domainname: The domainname to find.
+ *
+ * Caller must call down_read(&tomoyo_domain_list_lock); or
+ * down_write(&tomoyo_domain_list_lock); .
+ *
+ * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ */
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
+{
+       struct tomoyo_domain_info *domain;
+       struct tomoyo_path_info name;
+
+       name.name = domainname;
+       tomoyo_fill_path_info(&name);
+       list_for_each_entry(domain, &tomoyo_domain_list, list) {
+               if (!domain->is_deleted &&
+                   !tomoyo_pathcmp(&name, domain->domainname))
+                       return domain;
+       }
+       return NULL;
+}
+
+/**
+ * tomoyo_path_depth - Evaluate the number of '/' in a string.
+ *
+ * @pathname: The string to evaluate.
+ *
+ * Returns path depth of the string.
+ *
+ * I score 2 for each of the '/' in the @pathname
+ * and score 1 if the @pathname ends with '/'.
+ */
+static int tomoyo_path_depth(const char *pathname)
+{
+       int i = 0;
+
+       if (pathname) {
+               const char *ep = pathname + strlen(pathname);
+               if (pathname < ep--) {
+                       if (*ep != '/')
+                               i++;
+                       while (pathname <= ep)
+                               if (*ep-- == '/')
+                                       i += 2;
+               }
+       }
+       return i;
+}
+
+/**
+ * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token.
+ *
+ * @filename: The string to evaluate.
+ *
+ * Returns the initial length without a pattern in @filename.
+ */
+static int tomoyo_const_part_length(const char *filename)
+{
+       char c;
+       int len = 0;
+
+       if (!filename)
+               return 0;
+       while ((c = *filename++) != '\0') {
+               if (c != '\\') {
+                       len++;
+                       continue;
+               }
+               c = *filename++;
+               switch (c) {
+               case '\\':  /* "\\" */
+                       len += 2;
+                       continue;
+               case '0':   /* "\ooo" */
+               case '1':
+               case '2':
+               case '3':
+                       c = *filename++;
+                       if (c < '0' || c > '7')
+                               break;
+                       c = *filename++;
+                       if (c < '0' || c > '7')
+                               break;
+                       len += 4;
+                       continue;
+               }
+               break;
+       }
+       return len;
+}
+
+/**
+ * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members.
+ *
+ * @ptr: Pointer to "struct tomoyo_path_info" to fill in.
+ *
+ * The caller sets "struct tomoyo_path_info"->name.
+ */
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr)
+{
+       const char *name = ptr->name;
+       const int len = strlen(name);
+
+       ptr->total_len = len;
+       ptr->const_len = tomoyo_const_part_length(name);
+       ptr->is_dir = len && (name[len - 1] == '/');
+       ptr->is_patterned = (ptr->const_len < len);
+       ptr->hash = full_name_hash(name, len);
+       ptr->depth = tomoyo_path_depth(name);
+}
+
+/**
+ * tomoyo_file_matches_to_pattern2 - Pattern matching without '/' character
+ * and "\-" pattern.
+ *
+ * @filename:     The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern:      The start of pattern to compare.
+ * @pattern_end:  The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool tomoyo_file_matches_to_pattern2(const char *filename,
+                                           const char *filename_end,
+                                           const char *pattern,
+                                           const char *pattern_end)
+{
+       while (filename < filename_end && pattern < pattern_end) {
+               char c;
+               if (*pattern != '\\') {
+                       if (*filename++ != *pattern++)
+                               return false;
+                       continue;
+               }
+               c = *filename;
+               pattern++;
+               switch (*pattern) {
+                       int i;
+                       int j;
+               case '?':
+                       if (c == '/') {
+                               return false;
+                       } else if (c == '\\') {
+                               if (filename[1] == '\\')
+                                       filename++;
+                               else if (tomoyo_is_byte_range(filename + 1))
+                                       filename += 3;
+                               else
+                                       return false;
+                       }
+                       break;
+               case '\\':
+                       if (c != '\\')
+                               return false;
+                       if (*++filename != '\\')
+                               return false;
+                       break;
+               case '+':
+                       if (!isdigit(c))
+                               return false;
+                       break;
+               case 'x':
+                       if (!isxdigit(c))
+                               return false;
+                       break;
+               case 'a':
+                       if (!tomoyo_is_alphabet_char(c))
+                               return false;
+                       break;
+               case '0':
+               case '1':
+               case '2':
+               case '3':
+                       if (c == '\\' && tomoyo_is_byte_range(filename + 1)
+                           && strncmp(filename + 1, pattern, 3) == 0) {
+                               filename += 3;
+                               pattern += 2;
+                               break;
+                       }
+                       return false; /* Not matched. */
+               case '*':
+               case '@':
+                       for (i = 0; i <= filename_end - filename; i++) {
+                               if (tomoyo_file_matches_to_pattern2(
+                                                   filename + i, filename_end,
+                                                   pattern + 1, pattern_end))
+                                       return true;
+                               c = filename[i];
+                               if (c == '.' && *pattern == '@')
+                                       break;
+                               if (c != '\\')
+                                       continue;
+                               if (filename[i + 1] == '\\')
+                                       i++;
+                               else if (tomoyo_is_byte_range(filename + i + 1))
+                                       i += 3;
+                               else
+                                       break; /* Bad pattern. */
+                       }
+                       return false; /* Not matched. */
+               default:
+                       j = 0;
+                       c = *pattern;
+                       if (c == '$') {
+                               while (isdigit(filename[j]))
+                                       j++;
+                       } else if (c == 'X') {
+                               while (isxdigit(filename[j]))
+                                       j++;
+                       } else if (c == 'A') {
+                               while (tomoyo_is_alphabet_char(filename[j]))
+                                       j++;
+                       }
+                       for (i = 1; i <= j; i++) {
+                               if (tomoyo_file_matches_to_pattern2(
+                                                   filename + i, filename_end,
+                                                   pattern + 1, pattern_end))
+                                       return true;
+                       }
+                       return false; /* Not matched or bad pattern. */
+               }
+               filename++;
+               pattern++;
+       }
+       while (*pattern == '\\' &&
+              (*(pattern + 1) == '*' || *(pattern + 1) == '@'))
+               pattern += 2;
+       return filename == filename_end && pattern == pattern_end;
+}
+
+/**
+ * tomoyo_file_matches_to_pattern - Pattern matching without without '/' character.
+ *
+ * @filename:     The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern:      The start of pattern to compare.
+ * @pattern_end:  The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool tomoyo_file_matches_to_pattern(const char *filename,
+                                          const char *filename_end,
+                                          const char *pattern,
+                                          const char *pattern_end)
+{
+       const char *pattern_start = pattern;
+       bool first = true;
+       bool result;
+
+       while (pattern < pattern_end - 1) {
+               /* Split at "\-" pattern. */
+               if (*pattern++ != '\\' || *pattern++ != '-')
+                       continue;
+               result = tomoyo_file_matches_to_pattern2(filename,
+                                                        filename_end,
+                                                        pattern_start,
+                                                        pattern - 2);
+               if (first)
+                       result = !result;
+               if (result)
+                       return false;
+               first = false;
+               pattern_start = pattern;
+       }
+       result = tomoyo_file_matches_to_pattern2(filename, filename_end,
+                                                pattern_start, pattern_end);
+       return first ? result : !result;
+}
+
+/**
+ * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern.
+ * @filename: The filename to check.
+ * @pattern:  The pattern to compare.
+ *
+ * Returns true if matches, false otherwise.
+ *
+ * The following patterns are available.
+ *   \\     \ itself.
+ *   \ooo   Octal representation of a byte.
+ *   \*     More than or equals to 0 character other than '/'.
+ *   \@     More than or equals to 0 character other than '/' or '.'.
+ *   \?     1 byte character other than '/'.
+ *   \$     More than or equals to 1 decimal digit.
+ *   \+     1 decimal digit.
+ *   \X     More than or equals to 1 hexadecimal digit.
+ *   \x     1 hexadecimal digit.
+ *   \A     More than or equals to 1 alphabet character.
+ *   \a     1 alphabet character.
+ *   \-     Subtraction operator.
+ */
+bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
+                                const struct tomoyo_path_info *pattern)
+{
+       /*
+         if (!filename || !pattern)
+         return false;
+       */
+       const char *f = filename->name;
+       const char *p = pattern->name;
+       const int len = pattern->const_len;
+
+       /* If @pattern doesn't contain pattern, I can use strcmp(). */
+       if (!pattern->is_patterned)
+               return !tomoyo_pathcmp(filename, pattern);
+       /* Dont compare if the number of '/' differs. */
+       if (filename->depth != pattern->depth)
+               return false;
+       /* Compare the initial length without patterns. */
+       if (strncmp(f, p, len))
+               return false;
+       f += len;
+       p += len;
+       /* Main loop. Compare each directory component. */
+       while (*f && *p) {
+               const char *f_delimiter = strchr(f, '/');
+               const char *p_delimiter = strchr(p, '/');
+               if (!f_delimiter)
+                       f_delimiter = f + strlen(f);
+               if (!p_delimiter)
+                       p_delimiter = p + strlen(p);
+               if (!tomoyo_file_matches_to_pattern(f, f_delimiter,
+                                                   p, p_delimiter))
+                       return false;
+               f = f_delimiter;
+               if (*f)
+                       f++;
+               p = p_delimiter;
+               if (*p)
+                       p++;
+       }
+       /* Ignore trailing "\*" and "\@" in @pattern. */
+       while (*p == '\\' &&
+              (*(p + 1) == '*' || *(p + 1) == '@'))
+               p += 2;
+       return !*f && !*p;
+}
+
+/**
+ * tomoyo_io_printf - Transactional printf() to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @fmt:  The printf()'s format string, followed by parameters.
+ *
+ * Returns true if output was written, false otherwise.
+ *
+ * The snprintf() will truncate, but tomoyo_io_printf() won't.
+ */
+bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+{
+       va_list args;
+       int len;
+       int pos = head->read_avail;
+       int size = head->readbuf_size - pos;
+
+       if (size <= 0)
+               return false;
+       va_start(args, fmt);
+       len = vsnprintf(head->read_buf + pos, size, fmt, args);
+       va_end(args);
+       if (pos + len >= head->readbuf_size)
+               return false;
+       head->read_avail += len;
+       return true;
+}
+
+/**
+ * tomoyo_get_exe - Get tomoyo_realpath() of current process.
+ *
+ * Returns the tomoyo_realpath() of current process on success, NULL otherwise.
+ *
+ * This function uses tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if this function didn't return NULL.
+ */
+static const char *tomoyo_get_exe(void)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma;
+       const char *cp = NULL;
+
+       if (!mm)
+               return NULL;
+       down_read(&mm->mmap_sem);
+       for (vma = mm->mmap; vma; vma = vma->vm_next) {
+               if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) {
+                       cp = tomoyo_realpath_from_path(&vma->vm_file->f_path);
+                       break;
+               }
+       }
+       up_read(&mm->mmap_sem);
+       return cp;
+}
+
+/**
+ * tomoyo_get_msg - Get warning message.
+ *
+ * @is_enforce: Is it enforcing mode?
+ *
+ * Returns "ERROR" or "WARNING".
+ */
+const char *tomoyo_get_msg(const bool is_enforce)
+{
+       if (is_enforce)
+               return "ERROR";
+       else
+               return "WARNING";
+}
+
+/**
+ * tomoyo_check_flags - Check mode for specified functionality.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @index:  The functionality to check mode.
+ *
+ * TOMOYO checks only process context.
+ * This code disables TOMOYO's enforcement in case the function is called from
+ * interrupt context.
+ */
+unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
+                               const u8 index)
+{
+       const u8 profile = domain->profile;
+
+       if (WARN_ON(in_interrupt()))
+               return 0;
+       return tomoyo_policy_loaded && index < TOMOYO_MAX_CONTROL_INDEX
+#if TOMOYO_MAX_PROFILES != 256
+               && profile < TOMOYO_MAX_PROFILES
+#endif
+               && tomoyo_profile_ptr[profile] ?
+               tomoyo_profile_ptr[profile]->value[index] : 0;
+}
+
+/**
+ * tomoyo_verbose_mode - Check whether TOMOYO is verbose mode.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns true if domain policy violation warning should be printed to
+ * console.
+ */
+bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain)
+{
+       return tomoyo_check_flags(domain, TOMOYO_VERBOSE) != 0;
+}
+
+/**
+ * tomoyo_domain_quota_is_ok - Check for domain's quota.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns true if the domain is not exceeded quota, false otherwise.
+ */
+bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
+{
+       unsigned int count = 0;
+       struct tomoyo_acl_info *ptr;
+
+       if (!domain)
+               return true;
+       down_read(&tomoyo_domain_acl_info_list_lock);
+       list_for_each_entry(ptr, &domain->acl_info_list, list) {
+               if (ptr->type & TOMOYO_ACL_DELETED)
+                       continue;
+               switch (tomoyo_acl_type2(ptr)) {
+                       struct tomoyo_single_path_acl_record *acl1;
+                       struct tomoyo_double_path_acl_record *acl2;
+                       u16 perm;
+               case TOMOYO_TYPE_SINGLE_PATH_ACL:
+                       acl1 = container_of(ptr,
+                                   struct tomoyo_single_path_acl_record,
+                                           head);
+                       perm = acl1->perm;
+                       if (perm & (1 << TOMOYO_TYPE_EXECUTE_ACL))
+                               count++;
+                       if (perm &
+                           ((1 << TOMOYO_TYPE_READ_ACL) |
+                            (1 << TOMOYO_TYPE_WRITE_ACL)))
+                               count++;
+                       if (perm & (1 << TOMOYO_TYPE_CREATE_ACL))
+                               count++;
+                       if (perm & (1 << TOMOYO_TYPE_UNLINK_ACL))
+                               count++;
+                       if (perm & (1 << TOMOYO_TYPE_MKDIR_ACL))
+                               count++;
+                       if (perm & (1 << TOMOYO_TYPE_RMDIR_ACL))
+                               count++;
+                       if (perm & (1 << TOMOYO_TYPE_MKFIFO_ACL))
+                               count++;
+                       if (perm & (1 << TOMOYO_TYPE_MKSOCK_ACL))
+                               count++;
+                       if (perm & (1 << TOMOYO_TYPE_MKBLOCK_ACL))
+                               count++;
+                       if (perm & (1 << TOMOYO_TYPE_MKCHAR_ACL))
+                               count++;
+                       if (perm & (1 << TOMOYO_TYPE_TRUNCATE_ACL))
+                               count++;
+                       if (perm & (1 << TOMOYO_TYPE_SYMLINK_ACL))
+                               count++;
+                       if (perm & (1 << TOMOYO_TYPE_REWRITE_ACL))
+                               count++;
+                       break;
+               case TOMOYO_TYPE_DOUBLE_PATH_ACL:
+                       acl2 = container_of(ptr,
+                                   struct tomoyo_double_path_acl_record,
+                                           head);
+                       perm = acl2->perm;
+                       if (perm & (1 << TOMOYO_TYPE_LINK_ACL))
+                               count++;
+                       if (perm & (1 << TOMOYO_TYPE_RENAME_ACL))
+                               count++;
+                       break;
+               }
+       }
+       up_read(&tomoyo_domain_acl_info_list_lock);
+       if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY))
+               return true;
+       if (!domain->quota_warned) {
+               domain->quota_warned = true;
+               printk(KERN_WARNING "TOMOYO-WARNING: "
+                      "Domain '%s' has so many ACLs to hold. "
+                      "Stopped learning mode.\n", domain->domainname->name);
+       }
+       return false;
+}
+
+/**
+ * tomoyo_find_or_assign_new_profile - Create a new profile.
+ *
+ * @profile: Profile number to create.
+ *
+ * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise.
+ */
+static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned
+                                                               int profile)
+{
+       static DEFINE_MUTEX(lock);
+       struct tomoyo_profile *ptr = NULL;
+       int i;
+
+       if (profile >= TOMOYO_MAX_PROFILES)
+               return NULL;
+       /***** EXCLUSIVE SECTION START *****/
+       mutex_lock(&lock);
+       ptr = tomoyo_profile_ptr[profile];
+       if (ptr)
+               goto ok;
+       ptr = tomoyo_alloc_element(sizeof(*ptr));
+       if (!ptr)
+               goto ok;
+       for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++)
+               ptr->value[i] = tomoyo_control_array[i].current_value;
+       mb(); /* Avoid out-of-order execution. */
+       tomoyo_profile_ptr[profile] = ptr;
+ ok:
+       mutex_unlock(&lock);
+       /***** EXCLUSIVE SECTION END *****/
+       return ptr;
+}
+
+/**
+ * tomoyo_write_profile - Write to profile table.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
+{
+       char *data = head->write_buf;
+       unsigned int i;
+       unsigned int value;
+       char *cp;
+       struct tomoyo_profile *profile;
+       unsigned long num;
+
+       cp = strchr(data, '-');
+       if (cp)
+               *cp = '\0';
+       if (strict_strtoul(data, 10, &num))
+               return -EINVAL;
+       if (cp)
+               data = cp + 1;
+       profile = tomoyo_find_or_assign_new_profile(num);
+       if (!profile)
+               return -EINVAL;
+       cp = strchr(data, '=');
+       if (!cp)
+               return -EINVAL;
+       *cp = '\0';
+       if (!strcmp(data, "COMMENT")) {
+               profile->comment = tomoyo_save_name(cp + 1);
+               return 0;
+       }
+       for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) {
+               if (strcmp(data, tomoyo_control_array[i].keyword))
+                       continue;
+               if (sscanf(cp + 1, "%u", &value) != 1) {
+                       int j;
+                       const char **modes;
+                       switch (i) {
+                       case TOMOYO_VERBOSE:
+                               modes = tomoyo_mode_2;
+                               break;
+                       default:
+                               modes = tomoyo_mode_4;
+                               break;
+                       }
+                       for (j = 0; j < 4; j++) {
+                               if (strcmp(cp + 1, modes[j]))
+                                       continue;
+                               value = j;
+                               break;
+                       }
+                       if (j == 4)
+                               return -EINVAL;
+               } else if (value > tomoyo_control_array[i].max_value) {
+                       value = tomoyo_control_array[i].max_value;
+               }
+               profile->value[i] = value;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+/**
+ * tomoyo_read_profile - Read from profile table.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_read_profile(struct tomoyo_io_buffer *head)
+{
+       static const int total = TOMOYO_MAX_CONTROL_INDEX + 1;
+       int step;
+
+       if (head->read_eof)
+               return 0;
+       for (step = head->read_step; step < TOMOYO_MAX_PROFILES * total;
+            step++) {
+               const u8 index = step / total;
+               u8 type = step % total;
+               const struct tomoyo_profile *profile
+                       = tomoyo_profile_ptr[index];
+               head->read_step = step;
+               if (!profile)
+                       continue;
+               if (!type) { /* Print profile' comment tag. */
+                       if (!tomoyo_io_printf(head, "%u-COMMENT=%s\n",
+                                             index, profile->comment ?
+                                             profile->comment->name : ""))
+                               break;
+                       continue;
+               }
+               type--;
+               if (type < TOMOYO_MAX_CONTROL_INDEX) {
+                       const unsigned int value = profile->value[type];
+                       const char **modes = NULL;
+                       const char *keyword
+                               = tomoyo_control_array[type].keyword;
+                       switch (tomoyo_control_array[type].max_value) {
+                       case 3:
+                               modes = tomoyo_mode_4;
+                               break;
+                       case 1:
+                               modes = tomoyo_mode_2;
+                               break;
+                       }
+                       if (modes) {
+                               if (!tomoyo_io_printf(head, "%u-%s=%s\n", index,
+                                                     keyword, modes[value]))
+                                       break;
+                       } else {
+                               if (!tomoyo_io_printf(head, "%u-%s=%u\n", index,
+                                                     keyword, value))
+                                       break;
+                       }
+               }
+       }
+       if (step == TOMOYO_MAX_PROFILES * total)
+               head->read_eof = true;
+       return 0;
+}
+
+/* Structure for policy manager. */
+struct tomoyo_policy_manager_entry {
+       struct list_head list;
+       /* A path to program or a domainname. */
+       const struct tomoyo_path_info *manager;
+       bool is_domain;  /* True if manager is a domainname. */
+       bool is_deleted; /* True if this entry is deleted. */
+};
+
+/* The list for "struct tomoyo_policy_manager_entry". */
+static LIST_HEAD(tomoyo_policy_manager_list);
+static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
+
+/**
+ * tomoyo_update_manager_entry - Add a manager entry.
+ *
+ * @manager:   The path to manager or the domainnamme.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_manager_entry(const char *manager,
+                                      const bool is_delete)
+{
+       struct tomoyo_policy_manager_entry *new_entry;
+       struct tomoyo_policy_manager_entry *ptr;
+       const struct tomoyo_path_info *saved_manager;
+       int error = -ENOMEM;
+       bool is_domain = false;
+
+       if (tomoyo_is_domain_def(manager)) {
+               if (!tomoyo_is_correct_domain(manager, __func__))
+                       return -EINVAL;
+               is_domain = true;
+       } else {
+               if (!tomoyo_is_correct_path(manager, 1, -1, -1, __func__))
+                       return -EINVAL;
+       }
+       saved_manager = tomoyo_save_name(manager);
+       if (!saved_manager)
+               return -ENOMEM;
+       /***** EXCLUSIVE SECTION START *****/
+       down_write(&tomoyo_policy_manager_list_lock);
+       list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+               if (ptr->manager != saved_manager)
+                       continue;
+               ptr->is_deleted = is_delete;
+               error = 0;
+               goto out;
+       }
+       if (is_delete) {
+               error = -ENOENT;
+               goto out;
+       }
+       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+       if (!new_entry)
+               goto out;
+       new_entry->manager = saved_manager;
+       new_entry->is_domain = is_domain;
+       list_add_tail(&new_entry->list, &tomoyo_policy_manager_list);
+       error = 0;
+ out:
+       up_write(&tomoyo_policy_manager_list_lock);
+       /***** EXCLUSIVE SECTION END *****/
+       return error;
+}
+
+/**
+ * tomoyo_write_manager_policy - Write manager policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
+{
+       char *data = head->write_buf;
+       bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
+
+       if (!strcmp(data, "manage_by_non_root")) {
+               tomoyo_manage_by_non_root = !is_delete;
+               return 0;
+       }
+       return tomoyo_update_manager_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_manager_policy - Read manager policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
+{
+       struct list_head *pos;
+       bool done = true;
+
+       if (head->read_eof)
+               return 0;
+       down_read(&tomoyo_policy_manager_list_lock);
+       list_for_each_cookie(pos, head->read_var2,
+                            &tomoyo_policy_manager_list) {
+               struct tomoyo_policy_manager_entry *ptr;
+               ptr = list_entry(pos, struct tomoyo_policy_manager_entry,
+                                list);
+               if (ptr->is_deleted)
+                       continue;
+               if (!tomoyo_io_printf(head, "%s\n", ptr->manager->name)) {
+                       done = false;
+                       break;
+               }
+       }
+       up_read(&tomoyo_policy_manager_list_lock);
+       head->read_eof = done;
+       return 0;
+}
+
+/**
+ * tomoyo_is_policy_manager - Check whether the current process is a policy manager.
+ *
+ * Returns true if the current process is permitted to modify policy
+ * via /sys/kernel/security/tomoyo/ interface.
+ */
+static bool tomoyo_is_policy_manager(void)
+{
+       struct tomoyo_policy_manager_entry *ptr;
+       const char *exe;
+       const struct task_struct *task = current;
+       const struct tomoyo_path_info *domainname = tomoyo_domain()->domainname;
+       bool found = false;
+
+       if (!tomoyo_policy_loaded)
+               return true;
+       if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
+               return false;
+       down_read(&tomoyo_policy_manager_list_lock);
+       list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+               if (!ptr->is_deleted && ptr->is_domain
+                   && !tomoyo_pathcmp(domainname, ptr->manager)) {
+                       found = true;
+                       break;
+               }
+       }
+       up_read(&tomoyo_policy_manager_list_lock);
+       if (found)
+               return true;
+       exe = tomoyo_get_exe();
+       if (!exe)
+               return false;
+       down_read(&tomoyo_policy_manager_list_lock);
+       list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+               if (!ptr->is_deleted && !ptr->is_domain
+                   && !strcmp(exe, ptr->manager->name)) {
+                       found = true;
+                       break;
+               }
+       }
+       up_read(&tomoyo_policy_manager_list_lock);
+       if (!found) { /* Reduce error messages. */
+               static pid_t last_pid;
+               const pid_t pid = current->pid;
+               if (last_pid != pid) {
+                       printk(KERN_WARNING "%s ( %s ) is not permitted to "
+                              "update policies.\n", domainname->name, exe);
+                       last_pid = pid;
+               }
+       }
+       tomoyo_free(exe);
+       return found;
+}
+
+/**
+ * tomoyo_is_select_one - Parse select command.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @data: String to parse.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
+                                const char *data)
+{
+       unsigned int pid;
+       struct tomoyo_domain_info *domain = NULL;
+
+       if (sscanf(data, "pid=%u", &pid) == 1) {
+               struct task_struct *p;
+               /***** CRITICAL SECTION START *****/
+               read_lock(&tasklist_lock);
+               p = find_task_by_vpid(pid);
+               if (p)
+                       domain = tomoyo_real_domain(p);
+               read_unlock(&tasklist_lock);
+               /***** CRITICAL SECTION END *****/
+       } else if (!strncmp(data, "domain=", 7)) {
+               if (tomoyo_is_domain_def(data + 7)) {
+                       down_read(&tomoyo_domain_list_lock);
+                       domain = tomoyo_find_domain(data + 7);
+                       up_read(&tomoyo_domain_list_lock);
+               }
+       } else
+               return false;
+       head->write_var1 = domain;
+       /* Accessing read_buf is safe because head->io_sem is held. */
+       if (!head->read_buf)
+               return true; /* Do nothing if open(O_WRONLY). */
+       head->read_avail = 0;
+       tomoyo_io_printf(head, "# select %s\n", data);
+       head->read_single_domain = true;
+       head->read_eof = !domain;
+       if (domain) {
+               struct tomoyo_domain_info *d;
+               head->read_var1 = NULL;
+               down_read(&tomoyo_domain_list_lock);
+               list_for_each_entry(d, &tomoyo_domain_list, list) {
+                       if (d == domain)
+                               break;
+                       head->read_var1 = &d->list;
+               }
+               up_read(&tomoyo_domain_list_lock);
+               head->read_var2 = NULL;
+               head->read_bit = 0;
+               head->read_step = 0;
+               if (domain->is_deleted)
+                       tomoyo_io_printf(head, "# This is a deleted domain.\n");
+       }
+       return true;
+}
+
+/**
+ * tomoyo_write_domain_policy - Write domain policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
+{
+       char *data = head->write_buf;
+       struct tomoyo_domain_info *domain = head->write_var1;
+       bool is_delete = false;
+       bool is_select = false;
+       bool is_undelete = false;
+       unsigned int profile;
+
+       if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE))
+               is_delete = true;
+       else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT))
+               is_select = true;
+       else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_UNDELETE))
+               is_undelete = true;
+       if (is_select && tomoyo_is_select_one(head, data))
+               return 0;
+       /* Don't allow updating policies by non manager programs. */
+       if (!tomoyo_is_policy_manager())
+               return -EPERM;
+       if (tomoyo_is_domain_def(data)) {
+               domain = NULL;
+               if (is_delete)
+                       tomoyo_delete_domain(data);
+               else if (is_select) {
+                       down_read(&tomoyo_domain_list_lock);
+                       domain = tomoyo_find_domain(data);
+                       up_read(&tomoyo_domain_list_lock);
+               } else if (is_undelete)
+                       domain = tomoyo_undelete_domain(data);
+               else
+                       domain = tomoyo_find_or_assign_new_domain(data, 0);
+               head->write_var1 = domain;
+               return 0;
+       }
+       if (!domain)
+               return -EINVAL;
+
+       if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1
+           && profile < TOMOYO_MAX_PROFILES) {
+               if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded)
+                       domain->profile = (u8) profile;
+               return 0;
+       }
+       if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {
+               tomoyo_set_domain_flag(domain, is_delete,
+                              TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ);
+               return 0;
+       }
+       return tomoyo_write_file_policy(data, domain, is_delete);
+}
+
+/**
+ * tomoyo_print_single_path_acl - Print a single path ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_single_path_acl_record".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_single_path_acl(struct tomoyo_io_buffer *head,
+                                        struct tomoyo_single_path_acl_record *
+                                        ptr)
+{
+       int pos;
+       u8 bit;
+       const char *atmark = "";
+       const char *filename;
+       const u16 perm = ptr->perm;
+
+       filename = ptr->filename->name;
+       for (bit = head->read_bit; bit < TOMOYO_MAX_SINGLE_PATH_OPERATION;
+            bit++) {
+               const char *msg;
+               if (!(perm & (1 << bit)))
+                       continue;
+               /* Print "read/write" instead of "read" and "write". */
+               if ((bit == TOMOYO_TYPE_READ_ACL ||
+                    bit == TOMOYO_TYPE_WRITE_ACL)
+                   && (perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL)))
+                       continue;
+               msg = tomoyo_sp2keyword(bit);
+               pos = head->read_avail;
+               if (!tomoyo_io_printf(head, "allow_%s %s%s\n", msg,
+                                     atmark, filename))
+                       goto out;
+       }
+       head->read_bit = 0;
+       return true;
+ out:
+       head->read_bit = bit;
+       head->read_avail = pos;
+       return false;
+}
+
+/**
+ * tomoyo_print_double_path_acl - Print a double path ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_double_path_acl_record".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_double_path_acl(struct tomoyo_io_buffer *head,
+                                        struct tomoyo_double_path_acl_record *
+                                        ptr)
+{
+       int pos;
+       const char *atmark1 = "";
+       const char *atmark2 = "";
+       const char *filename1;
+       const char *filename2;
+       const u8 perm = ptr->perm;
+       u8 bit;
+
+       filename1 = ptr->filename1->name;
+       filename2 = ptr->filename2->name;
+       for (bit = head->read_bit; bit < TOMOYO_MAX_DOUBLE_PATH_OPERATION;
+            bit++) {
+               const char *msg;
+               if (!(perm & (1 << bit)))
+                       continue;
+               msg = tomoyo_dp2keyword(bit);
+               pos = head->read_avail;
+               if (!tomoyo_io_printf(head, "allow_%s %s%s %s%s\n", msg,
+                                     atmark1, filename1, atmark2, filename2))
+                       goto out;
+       }
+       head->read_bit = 0;
+       return true;
+ out:
+       head->read_bit = bit;
+       head->read_avail = pos;
+       return false;
+}
+
+/**
+ * tomoyo_print_entry - Print an ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to an ACL entry.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
+                              struct tomoyo_acl_info *ptr)
+{
+       const u8 acl_type = tomoyo_acl_type2(ptr);
+
+       if (acl_type & TOMOYO_ACL_DELETED)
+               return true;
+       if (acl_type == TOMOYO_TYPE_SINGLE_PATH_ACL) {
+               struct tomoyo_single_path_acl_record *acl
+                       = container_of(ptr,
+                                      struct tomoyo_single_path_acl_record,
+                                      head);
+               return tomoyo_print_single_path_acl(head, acl);
+       }
+       if (acl_type == TOMOYO_TYPE_DOUBLE_PATH_ACL) {
+               struct tomoyo_double_path_acl_record *acl
+                       = container_of(ptr,
+                                      struct tomoyo_double_path_acl_record,
+                                      head);
+               return tomoyo_print_double_path_acl(head, acl);
+       }
+       BUG(); /* This must not happen. */
+       return false;
+}
+
+/**
+ * tomoyo_read_domain_policy - Read domain policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
+{
+       struct list_head *dpos;
+       struct list_head *apos;
+       bool done = true;
+
+       if (head->read_eof)
+               return 0;
+       if (head->read_step == 0)
+               head->read_step = 1;
+       down_read(&tomoyo_domain_list_lock);
+       list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) {
+               struct tomoyo_domain_info *domain;
+               const char *quota_exceeded = "";
+               const char *transition_failed = "";
+               const char *ignore_global_allow_read = "";
+               domain = list_entry(dpos, struct tomoyo_domain_info, list);
+               if (head->read_step != 1)
+                       goto acl_loop;
+               if (domain->is_deleted && !head->read_single_domain)
+                       continue;
+               /* Print domainname and flags. */
+               if (domain->quota_warned)
+                       quota_exceeded = "quota_exceeded\n";
+               if (domain->flags & TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED)
+                       transition_failed = "transition_failed\n";
+               if (domain->flags &
+                   TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ)
+                       ignore_global_allow_read
+                               = TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n";
+               if (!tomoyo_io_printf(head,
+                                     "%s\n" TOMOYO_KEYWORD_USE_PROFILE "%u\n"
+                                     "%s%s%s\n", domain->domainname->name,
+                                     domain->profile, quota_exceeded,
+                                     transition_failed,
+                                     ignore_global_allow_read)) {
+                       done = false;
+                       break;
+               }
+               head->read_step = 2;
+acl_loop:
+               if (head->read_step == 3)
+                       goto tail_mark;
+               /* Print ACL entries in the domain. */
+               down_read(&tomoyo_domain_acl_info_list_lock);
+               list_for_each_cookie(apos, head->read_var2,
+                                     &domain->acl_info_list) {
+                       struct tomoyo_acl_info *ptr
+                               = list_entry(apos, struct tomoyo_acl_info,
+                                             list);
+                       if (!tomoyo_print_entry(head, ptr)) {
+                               done = false;
+                               break;
+                       }
+               }
+               up_read(&tomoyo_domain_acl_info_list_lock);
+               if (!done)
+                       break;
+               head->read_step = 3;
+tail_mark:
+               if (!tomoyo_io_printf(head, "\n")) {
+                       done = false;
+                       break;
+               }
+               head->read_step = 1;
+               if (head->read_single_domain)
+                       break;
+       }
+       up_read(&tomoyo_domain_list_lock);
+       head->read_eof = done;
+       return 0;
+}
+
+/**
+ * tomoyo_write_domain_profile - Assign profile for specified domain.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ *
+ * This is equivalent to doing
+ *
+ *     ( echo "select " $domainname; echo "use_profile " $profile ) |
+ *     /usr/lib/ccs/loadpolicy -d
+ */
+static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
+{
+       char *data = head->write_buf;
+       char *cp = strchr(data, ' ');
+       struct tomoyo_domain_info *domain;
+       unsigned long profile;
+
+       if (!cp)
+               return -EINVAL;
+       *cp = '\0';
+       down_read(&tomoyo_domain_list_lock);
+       domain = tomoyo_find_domain(cp + 1);
+       up_read(&tomoyo_domain_list_lock);
+       if (strict_strtoul(data, 10, &profile))
+               return -EINVAL;
+       if (domain && profile < TOMOYO_MAX_PROFILES
+           && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded))
+               domain->profile = (u8) profile;
+       return 0;
+}
+
+/**
+ * tomoyo_read_domain_profile - Read only domainname and profile.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns list of profile number and domainname pairs.
+ *
+ * This is equivalent to doing
+ *
+ *     grep -A 1 '^<kernel>' /sys/kernel/security/tomoyo/domain_policy |
+ *     awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
+ *     domainname = $0; } else if ( $1 == "use_profile" ) {
+ *     print $2 " " domainname; domainname = ""; } } ; '
+ */
+static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
+{
+       struct list_head *pos;
+       bool done = true;
+
+       if (head->read_eof)
+               return 0;
+       down_read(&tomoyo_domain_list_lock);
+       list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) {
+               struct tomoyo_domain_info *domain;
+               domain = list_entry(pos, struct tomoyo_domain_info, list);
+               if (domain->is_deleted)
+                       continue;
+               if (!tomoyo_io_printf(head, "%u %s\n", domain->profile,
+                                     domain->domainname->name)) {
+                       done = false;
+                       break;
+               }
+       }
+       up_read(&tomoyo_domain_list_lock);
+       head->read_eof = done;
+       return 0;
+}
+
+/**
+ * tomoyo_write_pid: Specify PID to obtain domainname.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_write_pid(struct tomoyo_io_buffer *head)
+{
+       unsigned long pid;
+       /* No error check. */
+       strict_strtoul(head->write_buf, 10, &pid);
+       head->read_step = (int) pid;
+       head->read_eof = false;
+       return 0;
+}
+
+/**
+ * tomoyo_read_pid - Get domainname of the specified PID.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns the domainname which the specified PID is in on success,
+ * empty string otherwise.
+ * The PID is specified by tomoyo_write_pid() so that the user can obtain
+ * using read()/write() interface rather than sysctl() interface.
+ */
+static int tomoyo_read_pid(struct tomoyo_io_buffer *head)
+{
+       if (head->read_avail == 0 && !head->read_eof) {
+               const int pid = head->read_step;
+               struct task_struct *p;
+               struct tomoyo_domain_info *domain = NULL;
+               /***** CRITICAL SECTION START *****/
+               read_lock(&tasklist_lock);
+               p = find_task_by_vpid(pid);
+               if (p)
+                       domain = tomoyo_real_domain(p);
+               read_unlock(&tasklist_lock);
+               /***** CRITICAL SECTION END *****/
+               if (domain)
+                       tomoyo_io_printf(head, "%d %u %s", pid, domain->profile,
+                                        domain->domainname->name);
+               head->read_eof = true;
+       }
+       return 0;
+}
+
+/**
+ * tomoyo_write_exception_policy - Write exception policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
+{
+       char *data = head->write_buf;
+       bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
+
+       if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_KEEP_DOMAIN))
+               return tomoyo_write_domain_keeper_policy(data, false,
+                                                        is_delete);
+       if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_KEEP_DOMAIN))
+               return tomoyo_write_domain_keeper_policy(data, true, is_delete);
+       if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_INITIALIZE_DOMAIN))
+               return tomoyo_write_domain_initializer_policy(data, false,
+                                                             is_delete);
+       if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN))
+               return tomoyo_write_domain_initializer_policy(data, true,
+                                                             is_delete);
+       if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALIAS))
+               return tomoyo_write_alias_policy(data, is_delete);
+       if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_READ))
+               return tomoyo_write_globally_readable_policy(data, is_delete);
+       if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_FILE_PATTERN))
+               return tomoyo_write_pattern_policy(data, is_delete);
+       if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DENY_REWRITE))
+               return tomoyo_write_no_rewrite_policy(data, is_delete);
+       return -EINVAL;
+}
+
+/**
+ * tomoyo_read_exception_policy - Read exception policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ */
+static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
+{
+       if (!head->read_eof) {
+               switch (head->read_step) {
+               case 0:
+                       head->read_var2 = NULL;
+                       head->read_step = 1;
+               case 1:
+                       if (!tomoyo_read_domain_keeper_policy(head))
+                               break;
+                       head->read_var2 = NULL;
+                       head->read_step = 2;
+               case 2:
+                       if (!tomoyo_read_globally_readable_policy(head))
+                               break;
+                       head->read_var2 = NULL;
+                       head->read_step = 3;
+               case 3:
+                       head->read_var2 = NULL;
+                       head->read_step = 4;
+               case 4:
+                       if (!tomoyo_read_domain_initializer_policy(head))
+                               break;
+                       head->read_var2 = NULL;
+                       head->read_step = 5;
+               case 5:
+                       if (!tomoyo_read_alias_policy(head))
+                               break;
+                       head->read_var2 = NULL;
+                       head->read_step = 6;
+               case 6:
+                       head->read_var2 = NULL;
+                       head->read_step = 7;
+               case 7:
+                       if (!tomoyo_read_file_pattern(head))
+                               break;
+                       head->read_var2 = NULL;
+                       head->read_step = 8;
+               case 8:
+                       if (!tomoyo_read_no_rewrite_policy(head))
+                               break;
+                       head->read_var2 = NULL;
+                       head->read_step = 9;
+               case 9:
+                       head->read_eof = true;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+
+/* path to policy loader */
+static const char *tomoyo_loader = "/sbin/tomoyo-init";
+
+/**
+ * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists.
+ *
+ * Returns true if /sbin/tomoyo-init exists, false otherwise.
+ */
+static bool tomoyo_policy_loader_exists(void)
+{
+       /*
+        * Don't activate MAC if the policy loader doesn't exist.
+        * If the initrd includes /sbin/init but real-root-dev has not
+        * mounted on / yet, activating MAC will block the system since
+        * policies are not loaded yet.
+        * Thus, let do_execve() call this function everytime.
+        */
+       struct nameidata nd;
+
+       if (path_lookup(tomoyo_loader, LOOKUP_FOLLOW, &nd)) {
+               printk(KERN_INFO "Not activating Mandatory Access Control now "
+                      "since %s doesn't exist.\n", tomoyo_loader);
+               return false;
+       }
+       path_put(&nd.path);
+       return true;
+}
+
+/**
+ * tomoyo_load_policy - Run external policy loader to load policy.
+ *
+ * @filename: The program about to start.
+ *
+ * This function checks whether @filename is /sbin/init , and if so
+ * invoke /sbin/tomoyo-init and wait for the termination of /sbin/tomoyo-init
+ * and then continues invocation of /sbin/init.
+ * /sbin/tomoyo-init reads policy files in /etc/tomoyo/ directory and
+ * writes to /sys/kernel/security/tomoyo/ interfaces.
+ *
+ * Returns nothing.
+ */
+void tomoyo_load_policy(const char *filename)
+{
+       char *argv[2];
+       char *envp[3];
+
+       if (tomoyo_policy_loaded)
+               return;
+       /*
+        * Check filename is /sbin/init or /sbin/tomoyo-start.
+        * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't
+        * be passed.
+        * You can create /sbin/tomoyo-start by
+        * "ln -s /bin/true /sbin/tomoyo-start".
+        */
+       if (strcmp(filename, "/sbin/init") &&
+           strcmp(filename, "/sbin/tomoyo-start"))
+               return;
+       if (!tomoyo_policy_loader_exists())
+               return;
+
+       printk(KERN_INFO "Calling %s to load policy. Please wait.\n",
+              tomoyo_loader);
+       argv[0] = (char *) tomoyo_loader;
+       argv[1] = NULL;
+       envp[0] = "HOME=/";
+       envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+       envp[2] = NULL;
+       call_usermodehelper(argv[0], argv, envp, 1);
+
+       printk(KERN_INFO "TOMOYO: 2.2.0-pre   2009/02/01\n");
+       printk(KERN_INFO "Mandatory Access Control activated.\n");
+       tomoyo_policy_loaded = true;
+       { /* Check all profiles currently assigned to domains are defined. */
+               struct tomoyo_domain_info *domain;
+               down_read(&tomoyo_domain_list_lock);
+               list_for_each_entry(domain, &tomoyo_domain_list, list) {
+                       const u8 profile = domain->profile;
+                       if (tomoyo_profile_ptr[profile])
+                               continue;
+                       panic("Profile %u (used by '%s') not defined.\n",
+                             profile, domain->domainname->name);
+               }
+               up_read(&tomoyo_domain_list_lock);
+       }
+}
+
+/**
+ * tomoyo_read_version: Get version.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns version information.
+ */
+static int tomoyo_read_version(struct tomoyo_io_buffer *head)
+{
+       if (!head->read_eof) {
+               tomoyo_io_printf(head, "2.2.0-pre");
+               head->read_eof = true;
+       }
+       return 0;
+}
+
+/**
+ * tomoyo_read_self_domain - Get the current process's domainname.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns the current process's domainname.
+ */
+static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
+{
+       if (!head->read_eof) {
+               /*
+                * tomoyo_domain()->domainname != NULL
+                * because every process belongs to a domain and
+                * the domain's name cannot be NULL.
+                */
+               tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name);
+               head->read_eof = true;
+       }
+       return 0;
+}
+
+/**
+ * tomoyo_open_control - open() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @type: Type of interface.
+ * @file: Pointer to "struct file".
+ *
+ * Associates policy handler and returns 0 on success, -ENOMEM otherwise.
+ */
+static int tomoyo_open_control(const u8 type, struct file *file)
+{
+       struct tomoyo_io_buffer *head = tomoyo_alloc(sizeof(*head));
+
+       if (!head)
+               return -ENOMEM;
+       mutex_init(&head->io_sem);
+       switch (type) {
+       case TOMOYO_DOMAINPOLICY:
+               /* /sys/kernel/security/tomoyo/domain_policy */
+               head->write = tomoyo_write_domain_policy;
+               head->read = tomoyo_read_domain_policy;
+               break;
+       case TOMOYO_EXCEPTIONPOLICY:
+               /* /sys/kernel/security/tomoyo/exception_policy */
+               head->write = tomoyo_write_exception_policy;
+               head->read = tomoyo_read_exception_policy;
+               break;
+       case TOMOYO_SELFDOMAIN:
+               /* /sys/kernel/security/tomoyo/self_domain */
+               head->read = tomoyo_read_self_domain;
+               break;
+       case TOMOYO_DOMAIN_STATUS:
+               /* /sys/kernel/security/tomoyo/.domain_status */
+               head->write = tomoyo_write_domain_profile;
+               head->read = tomoyo_read_domain_profile;
+               break;
+       case TOMOYO_PROCESS_STATUS:
+               /* /sys/kernel/security/tomoyo/.process_status */
+               head->write = tomoyo_write_pid;
+               head->read = tomoyo_read_pid;
+               break;
+       case TOMOYO_VERSION:
+               /* /sys/kernel/security/tomoyo/version */
+               head->read = tomoyo_read_version;
+               head->readbuf_size = 128;
+               break;
+       case TOMOYO_MEMINFO:
+               /* /sys/kernel/security/tomoyo/meminfo */
+               head->write = tomoyo_write_memory_quota;
+               head->read = tomoyo_read_memory_counter;
+               head->readbuf_size = 512;
+               break;
+       case TOMOYO_PROFILE:
+               /* /sys/kernel/security/tomoyo/profile */
+               head->write = tomoyo_write_profile;
+               head->read = tomoyo_read_profile;
+               break;
+       case TOMOYO_MANAGER:
+               /* /sys/kernel/security/tomoyo/manager */
+               head->write = tomoyo_write_manager_policy;
+               head->read = tomoyo_read_manager_policy;
+               break;
+       }
+       if (!(file->f_mode & FMODE_READ)) {
+               /*
+                * No need to allocate read_buf since it is not opened
+                * for reading.
+                */
+               head->read = NULL;
+       } else {
+               if (!head->readbuf_size)
+                       head->readbuf_size = 4096 * 2;
+               head->read_buf = tomoyo_alloc(head->readbuf_size);
+               if (!head->read_buf) {
+                       tomoyo_free(head);
+                       return -ENOMEM;
+               }
+       }
+       if (!(file->f_mode & FMODE_WRITE)) {
+               /*
+                * No need to allocate write_buf since it is not opened
+                * for writing.
+                */
+               head->write = NULL;
+       } else if (head->write) {
+               head->writebuf_size = 4096 * 2;
+               head->write_buf = tomoyo_alloc(head->writebuf_size);
+               if (!head->write_buf) {
+                       tomoyo_free(head->read_buf);
+                       tomoyo_free(head);
+                       return -ENOMEM;
+               }
+       }
+       file->private_data = head;
+       /*
+        * Call the handler now if the file is
+        * /sys/kernel/security/tomoyo/self_domain
+        * so that the user can use
+        * cat < /sys/kernel/security/tomoyo/self_domain"
+        * to know the current process's domainname.
+        */
+       if (type == TOMOYO_SELFDOMAIN)
+               tomoyo_read_control(file, NULL, 0);
+       return 0;
+}
+
+/**
+ * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file:       Pointer to "struct file".
+ * @buffer:     Poiner to buffer to write to.
+ * @buffer_len: Size of @buffer.
+ *
+ * Returns bytes read on success, negative value otherwise.
+ */
+static int tomoyo_read_control(struct file *file, char __user *buffer,
+                              const int buffer_len)
+{
+       int len = 0;
+       struct tomoyo_io_buffer *head = file->private_data;
+       char *cp;
+
+       if (!head->read)
+               return -ENOSYS;
+       if (mutex_lock_interruptible(&head->io_sem))
+               return -EINTR;
+       /* Call the policy handler. */
+       len = head->read(head);
+       if (len < 0)
+               goto out;
+       /* Write to buffer. */
+       len = head->read_avail;
+       if (len > buffer_len)
+               len = buffer_len;
+       if (!len)
+               goto out;
+       /* head->read_buf changes by some functions. */
+       cp = head->read_buf;
+       if (copy_to_user(buffer, cp, len)) {
+               len = -EFAULT;
+               goto out;
+       }
+       head->read_avail -= len;
+       memmove(cp, cp + len, head->read_avail);
+ out:
+       mutex_unlock(&head->io_sem);
+       return len;
+}
+
+/**
+ * tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file:       Pointer to "struct file".
+ * @buffer:     Pointer to buffer to read from.
+ * @buffer_len: Size of @buffer.
+ *
+ * Returns @buffer_len on success, negative value otherwise.
+ */
+static int tomoyo_write_control(struct file *file, const char __user *buffer,
+                               const int buffer_len)
+{
+       struct tomoyo_io_buffer *head = file->private_data;
+       int error = buffer_len;
+       int avail_len = buffer_len;
+       char *cp0 = head->write_buf;
+
+       if (!head->write)
+               return -ENOSYS;
+       if (!access_ok(VERIFY_READ, buffer, buffer_len))
+               return -EFAULT;
+       /* Don't allow updating policies by non manager programs. */
+       if (head->write != tomoyo_write_pid &&
+           head->write != tomoyo_write_domain_policy &&
+           !tomoyo_is_policy_manager())
+               return -EPERM;
+       if (mutex_lock_interruptible(&head->io_sem))
+               return -EINTR;
+       /* Read a line and dispatch it to the policy handler. */
+       while (avail_len > 0) {
+               char c;
+               if (head->write_avail >= head->writebuf_size - 1) {
+                       error = -ENOMEM;
+                       break;
+               } else if (get_user(c, buffer)) {
+                       error = -EFAULT;
+                       break;
+               }
+               buffer++;
+               avail_len--;
+               cp0[head->write_avail++] = c;
+               if (c != '\n')
+                       continue;
+               cp0[head->write_avail - 1] = '\0';
+               head->write_avail = 0;
+               tomoyo_normalize_line(cp0);
+               head->write(head);
+       }
+       mutex_unlock(&head->io_sem);
+       return error;
+}
+
+/**
+ * tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ *
+ * Releases memory and returns 0.
+ */
+static int tomoyo_close_control(struct file *file)
+{
+       struct tomoyo_io_buffer *head = file->private_data;
+
+       /* Release memory used for policy I/O. */
+       tomoyo_free(head->read_buf);
+       head->read_buf = NULL;
+       tomoyo_free(head->write_buf);
+       head->write_buf = NULL;
+       tomoyo_free(head);
+       head = NULL;
+       file->private_data = NULL;
+       return 0;
+}
+
+/**
+ * tomoyo_alloc_acl_element - Allocate permanent memory for ACL entry.
+ *
+ * @acl_type:  Type of ACL entry.
+ *
+ * Returns pointer to the ACL entry on success, NULL otherwise.
+ */
+void *tomoyo_alloc_acl_element(const u8 acl_type)
+{
+       int len;
+       struct tomoyo_acl_info *ptr;
+
+       switch (acl_type) {
+       case TOMOYO_TYPE_SINGLE_PATH_ACL:
+               len = sizeof(struct tomoyo_single_path_acl_record);
+               break;
+       case TOMOYO_TYPE_DOUBLE_PATH_ACL:
+               len = sizeof(struct tomoyo_double_path_acl_record);
+               break;
+       default:
+               return NULL;
+       }
+       ptr = tomoyo_alloc_element(len);
+       if (!ptr)
+               return NULL;
+       ptr->type = acl_type;
+       return ptr;
+}
+
+/**
+ * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @inode: Pointer to "struct inode".
+ * @file:  Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_open(struct inode *inode, struct file *file)
+{
+       const int key = ((u8 *) file->f_path.dentry->d_inode->i_private)
+               - ((u8 *) NULL);
+       return tomoyo_open_control(key, file);
+}
+
+/**
+ * tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @inode: Pointer to "struct inode".
+ * @file:  Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_release(struct inode *inode, struct file *file)
+{
+       return tomoyo_close_control(file);
+}
+
+/**
+ * tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file:  Pointer to "struct file".
+ * @buf:   Pointer to buffer.
+ * @count: Size of @buf.
+ * @ppos:  Unused.
+ *
+ * Returns bytes read on success, negative value otherwise.
+ */
+static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count,
+                          loff_t *ppos)
+{
+       return tomoyo_read_control(file, buf, count);
+}
+
+/**
+ * tomoyo_write - write() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file:  Pointer to "struct file".
+ * @buf:   Pointer to buffer.
+ * @count: Size of @buf.
+ * @ppos:  Unused.
+ *
+ * Returns @count on success, negative value otherwise.
+ */
+static ssize_t tomoyo_write(struct file *file, const char __user *buf,
+                           size_t count, loff_t *ppos)
+{
+       return tomoyo_write_control(file, buf, count);
+}
+
+/* Operations for /sys/kernel/security/tomoyo/ interface. */
+static const struct file_operations tomoyo_operations = {
+       .open    = tomoyo_open,
+       .release = tomoyo_release,
+       .read    = tomoyo_read,
+       .write   = tomoyo_write,
+};
+
+/**
+ * tomoyo_create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory.
+ *
+ * @name:   The name of the interface file.
+ * @mode:   The permission of the interface file.
+ * @parent: The parent directory.
+ * @key:    Type of interface.
+ *
+ * Returns nothing.
+ */
+static void __init tomoyo_create_entry(const char *name, const mode_t mode,
+                                      struct dentry *parent, const u8 key)
+{
+       securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key,
+                              &tomoyo_operations);
+}
+
+/**
+ * tomoyo_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface.
+ *
+ * Returns 0.
+ */
+static int __init tomoyo_initerface_init(void)
+{
+       struct dentry *tomoyo_dir;
+
+       /* Don't create securityfs entries unless registered. */
+       if (current_cred()->security != &tomoyo_kernel_domain)
+               return 0;
+
+       tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
+       tomoyo_create_entry("domain_policy",    0600, tomoyo_dir,
+                           TOMOYO_DOMAINPOLICY);
+       tomoyo_create_entry("exception_policy", 0600, tomoyo_dir,
+                           TOMOYO_EXCEPTIONPOLICY);
+       tomoyo_create_entry("self_domain",      0400, tomoyo_dir,
+                           TOMOYO_SELFDOMAIN);
+       tomoyo_create_entry(".domain_status",   0600, tomoyo_dir,
+                           TOMOYO_DOMAIN_STATUS);
+       tomoyo_create_entry(".process_status",  0600, tomoyo_dir,
+                           TOMOYO_PROCESS_STATUS);
+       tomoyo_create_entry("meminfo",          0600, tomoyo_dir,
+                           TOMOYO_MEMINFO);
+       tomoyo_create_entry("profile",          0600, tomoyo_dir,
+                           TOMOYO_PROFILE);
+       tomoyo_create_entry("manager",          0600, tomoyo_dir,
+                           TOMOYO_MANAGER);
+       tomoyo_create_entry("version",          0400, tomoyo_dir,
+                           TOMOYO_VERSION);
+       return 0;
+}
+
+fs_initcall(tomoyo_initerface_init);
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
new file mode 100644 (file)
index 0000000..6dcb7cc
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * security/tomoyo/common.h
+ *
+ * Common functions for TOMOYO.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#ifndef _SECURITY_TOMOYO_COMMON_H
+#define _SECURITY_TOMOYO_COMMON_H
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/kmod.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/list.h>
+
+struct dentry;
+struct vfsmount;
+
+/* Temporary buffer for holding pathnames. */
+struct tomoyo_page_buffer {
+       char buffer[4096];
+};
+
+/* Structure for holding a token. */
+struct tomoyo_path_info {
+       const char *name;
+       u32 hash;          /* = full_name_hash(name, strlen(name)) */
+       u16 total_len;     /* = strlen(name)                       */
+       u16 const_len;     /* = tomoyo_const_part_length(name)     */
+       bool is_dir;       /* = tomoyo_strendswith(name, "/")      */
+       bool is_patterned; /* = tomoyo_path_contains_pattern(name) */
+       u16 depth;         /* = tomoyo_path_depth(name)            */
+};
+
+/*
+ * This is the max length of a token.
+ *
+ * A token consists of only ASCII printable characters.
+ * Non printable characters in a token is represented in \ooo style
+ * octal string. Thus, \ itself is represented as \\.
+ */
+#define TOMOYO_MAX_PATHNAME_LEN 4000
+
+/* Structure for holding requested pathname. */
+struct tomoyo_path_info_with_data {
+       /* Keep "head" first, for this pointer is passed to tomoyo_free(). */
+       struct tomoyo_path_info head;
+       char bariier1[16]; /* Safeguard for overrun. */
+       char body[TOMOYO_MAX_PATHNAME_LEN];
+       char barrier2[16]; /* Safeguard for overrun. */
+};
+
+/*
+ * Common header for holding ACL entries.
+ *
+ * Packing "struct tomoyo_acl_info" allows
+ * "struct tomoyo_single_path_acl_record" to embed "u16" and
+ * "struct tomoyo_double_path_acl_record" to embed "u8"
+ * without enlarging their structure size.
+ */
+struct tomoyo_acl_info {
+       struct list_head list;
+       /*
+        * Type of this ACL entry.
+        *
+        * MSB is is_deleted flag.
+        */
+       u8 type;
+} __packed;
+
+/* This ACL entry is deleted.           */
+#define TOMOYO_ACL_DELETED        0x80
+
+/* Structure for domain information. */
+struct tomoyo_domain_info {
+       struct list_head list;
+       struct list_head acl_info_list;
+       /* Name of this domain. Never NULL.          */
+       const struct tomoyo_path_info *domainname;
+       u8 profile;        /* Profile number to use. */
+       u8 is_deleted;     /* Delete flag.
+                             0 = active.
+                             1 = deleted but undeletable.
+                             255 = deleted and no longer undeletable. */
+       bool quota_warned; /* Quota warnning flag.   */
+       /* DOMAIN_FLAGS_*. Use tomoyo_set_domain_flag() to modify. */
+       u8 flags;
+};
+
+/* Profile number is an integer between 0 and 255. */
+#define TOMOYO_MAX_PROFILES 256
+
+/* Ignore "allow_read" directive in exception policy. */
+#define TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ 1
+/*
+ * This domain was unable to create a new domain at tomoyo_find_next_domain()
+ * because the name of the domain to be created was too long or
+ * it could not allocate memory.
+ * More than one process continued execve() without domain transition.
+ */
+#define TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED        2
+
+/*
+ * Structure for "allow_read/write", "allow_execute", "allow_read",
+ * "allow_write", "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir",
+ * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar",
+ * "allow_truncate", "allow_symlink" and "allow_rewrite" directive.
+ */
+struct tomoyo_single_path_acl_record {
+       struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_SINGLE_PATH_ACL */
+       u16 perm;
+       /* Pointer to single pathname. */
+       const struct tomoyo_path_info *filename;
+};
+
+/* Structure for "allow_rename" and "allow_link" directive. */
+struct tomoyo_double_path_acl_record {
+       struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_DOUBLE_PATH_ACL */
+       u8 perm;
+       /* Pointer to single pathname. */
+       const struct tomoyo_path_info *filename1;
+       /* Pointer to single pathname. */
+       const struct tomoyo_path_info *filename2;
+};
+
+/* Keywords for ACLs. */
+#define TOMOYO_KEYWORD_ALIAS                     "alias "
+#define TOMOYO_KEYWORD_ALLOW_READ                "allow_read "
+#define TOMOYO_KEYWORD_DELETE                    "delete "
+#define TOMOYO_KEYWORD_DENY_REWRITE              "deny_rewrite "
+#define TOMOYO_KEYWORD_FILE_PATTERN              "file_pattern "
+#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN         "initialize_domain "
+#define TOMOYO_KEYWORD_KEEP_DOMAIN               "keep_domain "
+#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN      "no_initialize_domain "
+#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN            "no_keep_domain "
+#define TOMOYO_KEYWORD_SELECT                    "select "
+#define TOMOYO_KEYWORD_UNDELETE                  "undelete "
+#define TOMOYO_KEYWORD_USE_PROFILE               "use_profile "
+#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ  "ignore_global_allow_read"
+/* A domain definition starts with <kernel>. */
+#define TOMOYO_ROOT_NAME                         "<kernel>"
+#define TOMOYO_ROOT_NAME_LEN                     (sizeof(TOMOYO_ROOT_NAME) - 1)
+
+/* Index numbers for Access Controls. */
+#define TOMOYO_MAC_FOR_FILE                  0  /* domain_policy.conf */
+#define TOMOYO_MAX_ACCEPT_ENTRY              1
+#define TOMOYO_VERBOSE                       2
+#define TOMOYO_MAX_CONTROL_INDEX             3
+
+/* Structure for reading/writing policy via securityfs interfaces. */
+struct tomoyo_io_buffer {
+       int (*read) (struct tomoyo_io_buffer *);
+       int (*write) (struct tomoyo_io_buffer *);
+       /* Exclusive lock for this structure.   */
+       struct mutex io_sem;
+       /* The position currently reading from. */
+       struct list_head *read_var1;
+       /* Extra variables for reading.         */
+       struct list_head *read_var2;
+       /* The position currently writing to.   */
+       struct tomoyo_domain_info *write_var1;
+       /* The step for reading.                */
+       int read_step;
+       /* Buffer for reading.                  */
+       char *read_buf;
+       /* EOF flag for reading.                */
+       bool read_eof;
+       /* Read domain ACL of specified PID?    */
+       bool read_single_domain;
+       /* Extra variable for reading.          */
+       u8 read_bit;
+       /* Bytes available for reading.         */
+       int read_avail;
+       /* Size of read buffer.                 */
+       int readbuf_size;
+       /* Buffer for writing.                  */
+       char *write_buf;
+       /* Bytes available for writing.         */
+       int write_avail;
+       /* Size of write buffer.                */
+       int writebuf_size;
+};
+
+/* Check whether the domain has too many ACL entries to hold. */
+bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain);
+/* Transactional sprintf() for policy dump. */
+bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+       __attribute__ ((format(printf, 2, 3)));
+/* Check whether the domainname is correct. */
+bool tomoyo_is_correct_domain(const unsigned char *domainname,
+                             const char *function);
+/* Check whether the token is correct. */
+bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
+                           const s8 pattern_type, const s8 end_type,
+                           const char *function);
+/* Check whether the token can be a domainname. */
+bool tomoyo_is_domain_def(const unsigned char *buffer);
+/* Check whether the given filename matches the given pattern. */
+bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
+                                const struct tomoyo_path_info *pattern);
+/* Read "alias" entry in exception policy. */
+bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head);
+/*
+ * Read "initialize_domain" and "no_initialize_domain" entry
+ * in exception policy.
+ */
+bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head);
+/* Read "keep_domain" and "no_keep_domain" entry in exception policy. */
+bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head);
+/* Read "file_pattern" entry in exception policy. */
+bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head);
+/* Read "allow_read" entry in exception policy. */
+bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head);
+/* Read "deny_rewrite" entry in exception policy. */
+bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head);
+/* Write domain policy violation warning message to console? */
+bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain);
+/* Convert double path operation to operation name. */
+const char *tomoyo_dp2keyword(const u8 operation);
+/* Get the last component of the given domainname. */
+const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain);
+/* Get warning message. */
+const char *tomoyo_get_msg(const bool is_enforce);
+/* Convert single path operation to operation name. */
+const char *tomoyo_sp2keyword(const u8 operation);
+/* Delete a domain. */
+int tomoyo_delete_domain(char *data);
+/* Create "alias" entry in exception policy. */
+int tomoyo_write_alias_policy(char *data, const bool is_delete);
+/*
+ * Create "initialize_domain" and "no_initialize_domain" entry
+ * in exception policy.
+ */
+int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
+                                          const bool is_delete);
+/* Create "keep_domain" and "no_keep_domain" entry in exception policy. */
+int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
+                                     const bool is_delete);
+/*
+ * Create "allow_read/write", "allow_execute", "allow_read", "allow_write",
+ * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir",
+ * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar",
+ * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and
+ * "allow_link" entry in domain policy.
+ */
+int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
+                            const bool is_delete);
+/* Create "allow_read" entry in exception policy. */
+int tomoyo_write_globally_readable_policy(char *data, const bool is_delete);
+/* Create "deny_rewrite" entry in exception policy. */
+int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete);
+/* Create "file_pattern" entry in exception policy. */
+int tomoyo_write_pattern_policy(char *data, const bool is_delete);
+/* Find a domain by the given name. */
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
+/* Find or create a domain by the given name. */
+struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
+                                                           domainname,
+                                                           const u8 profile);
+/* Undelete a domain. */
+struct tomoyo_domain_info *tomoyo_undelete_domain(const char *domainname);
+/* Check mode for specified functionality. */
+unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
+                               const u8 index);
+/* Allocate memory for structures. */
+void *tomoyo_alloc_acl_element(const u8 acl_type);
+/* Fill in "struct tomoyo_path_info" members. */
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
+/* Run policy loader when /sbin/init starts. */
+void tomoyo_load_policy(const char *filename);
+/* Change "struct tomoyo_domain_info"->flags. */
+void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
+                           const bool is_delete, const u8 flags);
+
+/* strcmp() for "struct tomoyo_path_info" structure. */
+static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
+                                 const struct tomoyo_path_info *b)
+{
+       return a->hash != b->hash || strcmp(a->name, b->name);
+}
+
+/* Get type of an ACL entry. */
+static inline u8 tomoyo_acl_type1(struct tomoyo_acl_info *ptr)
+{
+       return ptr->type & ~TOMOYO_ACL_DELETED;
+}
+
+/* Get type of an ACL entry. */
+static inline u8 tomoyo_acl_type2(struct tomoyo_acl_info *ptr)
+{
+       return ptr->type;
+}
+
+/**
+ * tomoyo_is_valid - Check whether the character is a valid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is a valid character, false otherwise.
+ */
+static inline bool tomoyo_is_valid(const unsigned char c)
+{
+       return c > ' ' && c < 127;
+}
+
+/**
+ * tomoyo_is_invalid - Check whether the character is an invalid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an invalid character, false otherwise.
+ */
+static inline bool tomoyo_is_invalid(const unsigned char c)
+{
+       return c && (c <= ' ' || c >= 127);
+}
+
+/* The list for "struct tomoyo_domain_info". */
+extern struct list_head tomoyo_domain_list;
+extern struct rw_semaphore tomoyo_domain_list_lock;
+
+/* Lock for domain->acl_info_list. */
+extern struct rw_semaphore tomoyo_domain_acl_info_list_lock;
+
+/* Has /sbin/init started? */
+extern bool tomoyo_policy_loaded;
+
+/* The kernel's domain. */
+extern struct tomoyo_domain_info tomoyo_kernel_domain;
+
+/**
+ * list_for_each_cookie - iterate over a list with cookie.
+ * @pos:        the &struct list_head to use as a loop cursor.
+ * @cookie:     the &struct list_head to use as a cookie.
+ * @head:       the head for your list.
+ *
+ * Same with list_for_each() except that this primitive uses @cookie
+ * so that we can continue iteration.
+ * @cookie must be NULL when iteration starts, and @cookie will become
+ * NULL when iteration finishes.
+ */
+#define list_for_each_cookie(pos, cookie, head)                       \
+       for (({ if (!cookie)                                          \
+                                    cookie = head; }),               \
+            pos = (cookie)->next;                                    \
+            prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
+            (cookie) = pos, pos = pos->next)
+
+#endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
new file mode 100644 (file)
index 0000000..093a756
--- /dev/null
@@ -0,0 +1,878 @@
+/*
+ * security/tomoyo/domain.c
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#include "common.h"
+#include "tomoyo.h"
+#include "realpath.h"
+#include <linux/binfmts.h>
+
+/* Variables definitions.*/
+
+/* The initial domain. */
+struct tomoyo_domain_info tomoyo_kernel_domain;
+
+/* The list for "struct tomoyo_domain_info". */
+LIST_HEAD(tomoyo_domain_list);
+DECLARE_RWSEM(tomoyo_domain_list_lock);
+
+/* Structure for "initialize_domain" and "no_initialize_domain" keyword. */
+struct tomoyo_domain_initializer_entry {
+       struct list_head list;
+       const struct tomoyo_path_info *domainname;    /* This may be NULL */
+       const struct tomoyo_path_info *program;
+       bool is_deleted;
+       bool is_not;       /* True if this entry is "no_initialize_domain".  */
+       /* True if the domainname is tomoyo_get_last_name(). */
+       bool is_last_name;
+};
+
+/* Structure for "keep_domain" and "no_keep_domain" keyword. */
+struct tomoyo_domain_keeper_entry {
+       struct list_head list;
+       const struct tomoyo_path_info *domainname;
+       const struct tomoyo_path_info *program;       /* This may be NULL */
+       bool is_deleted;
+       bool is_not;       /* True if this entry is "no_keep_domain".        */
+       /* True if the domainname is tomoyo_get_last_name(). */
+       bool is_last_name;
+};
+
+/* Structure for "alias" keyword. */
+struct tomoyo_alias_entry {
+       struct list_head list;
+       const struct tomoyo_path_info *original_name;
+       const struct tomoyo_path_info *aliased_name;
+       bool is_deleted;
+};
+
+/**
+ * tomoyo_set_domain_flag - Set or clear domain's attribute flags.
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ * @flags:     Flags to set or clear.
+ *
+ * Returns nothing.
+ */
+void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
+                           const bool is_delete, const u8 flags)
+{
+       /* We need to serialize because this is bitfield operation. */
+       static DEFINE_SPINLOCK(lock);
+       /***** CRITICAL SECTION START *****/
+       spin_lock(&lock);
+       if (!is_delete)
+               domain->flags |= flags;
+       else
+               domain->flags &= ~flags;
+       spin_unlock(&lock);
+       /***** CRITICAL SECTION END *****/
+}
+
+/**
+ * tomoyo_get_last_name - Get last component of a domainname.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns the last component of the domainname.
+ */
+const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain)
+{
+       const char *cp0 = domain->domainname->name;
+       const char *cp1 = strrchr(cp0, ' ');
+
+       if (cp1)
+               return cp1 + 1;
+       return cp0;
+}
+
+/* The list for "struct tomoyo_domain_initializer_entry". */
+static LIST_HEAD(tomoyo_domain_initializer_list);
+static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
+
+/**
+ * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @domainname: The name of domain. May be NULL.
+ * @program:    The name of program.
+ * @is_not:     True if it is "no_initialize_domain" entry.
+ * @is_delete:  True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_domain_initializer_entry(const char *domainname,
+                                                 const char *program,
+                                                 const bool is_not,
+                                                 const bool is_delete)
+{
+       struct tomoyo_domain_initializer_entry *new_entry;
+       struct tomoyo_domain_initializer_entry *ptr;
+       const struct tomoyo_path_info *saved_program;
+       const struct tomoyo_path_info *saved_domainname = NULL;
+       int error = -ENOMEM;
+       bool is_last_name = false;
+
+       if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
+               return -EINVAL; /* No patterns allowed. */
+       if (domainname) {
+               if (!tomoyo_is_domain_def(domainname) &&
+                   tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
+                       is_last_name = true;
+               else if (!tomoyo_is_correct_domain(domainname, __func__))
+                       return -EINVAL;
+               saved_domainname = tomoyo_save_name(domainname);
+               if (!saved_domainname)
+                       return -ENOMEM;
+       }
+       saved_program = tomoyo_save_name(program);
+       if (!saved_program)
+               return -ENOMEM;
+       /***** EXCLUSIVE SECTION START *****/
+       down_write(&tomoyo_domain_initializer_list_lock);
+       list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+               if (ptr->is_not != is_not ||
+                   ptr->domainname != saved_domainname ||
+                   ptr->program != saved_program)
+                       continue;
+               ptr->is_deleted = is_delete;
+               error = 0;
+               goto out;
+       }
+       if (is_delete) {
+               error = -ENOENT;
+               goto out;
+       }
+       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+       if (!new_entry)
+               goto out;
+       new_entry->domainname = saved_domainname;
+       new_entry->program = saved_program;
+       new_entry->is_not = is_not;
+       new_entry->is_last_name = is_last_name;
+       list_add_tail(&new_entry->list, &tomoyo_domain_initializer_list);
+       error = 0;
+ out:
+       up_write(&tomoyo_domain_initializer_list_lock);
+       /***** EXCLUSIVE SECTION END *****/
+       return error;
+}
+
+/**
+ * tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
+{
+       struct list_head *pos;
+       bool done = true;
+
+       down_read(&tomoyo_domain_initializer_list_lock);
+       list_for_each_cookie(pos, head->read_var2,
+                            &tomoyo_domain_initializer_list) {
+               const char *no;
+               const char *from = "";
+               const char *domain = "";
+               struct tomoyo_domain_initializer_entry *ptr;
+               ptr = list_entry(pos, struct tomoyo_domain_initializer_entry,
+                                 list);
+               if (ptr->is_deleted)
+                       continue;
+               no = ptr->is_not ? "no_" : "";
+               if (ptr->domainname) {
+                       from = " from ";
+                       domain = ptr->domainname->name;
+               }
+               if (!tomoyo_io_printf(head,
+                                     "%s" TOMOYO_KEYWORD_INITIALIZE_DOMAIN
+                                     "%s%s%s\n", no, ptr->program->name, from,
+                                     domain)) {
+                       done = false;
+                       break;
+               }
+       }
+       up_read(&tomoyo_domain_initializer_list_lock);
+       return done;
+}
+
+/**
+ * tomoyo_write_domain_initializer_policy - Write "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_not:    True if it is "no_initialize_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
+                                          const bool is_delete)
+{
+       char *cp = strstr(data, " from ");
+
+       if (cp) {
+               *cp = '\0';
+               return tomoyo_update_domain_initializer_entry(cp + 6, data,
+                                                             is_not,
+                                                             is_delete);
+       }
+       return tomoyo_update_domain_initializer_entry(NULL, data, is_not,
+                                                     is_delete);
+}
+
+/**
+ * tomoyo_is_domain_initializer - Check whether the given program causes domainname reinitialization.
+ *
+ * @domainname: The name of domain.
+ * @program:    The name of program.
+ * @last_name:  The last component of @domainname.
+ *
+ * Returns true if executing @program reinitializes domain transition,
+ * false otherwise.
+ */
+static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
+                                        domainname,
+                                        const struct tomoyo_path_info *program,
+                                        const struct tomoyo_path_info *
+                                        last_name)
+{
+       struct tomoyo_domain_initializer_entry *ptr;
+       bool flag = false;
+
+       down_read(&tomoyo_domain_initializer_list_lock);
+       list_for_each_entry(ptr,  &tomoyo_domain_initializer_list, list) {
+               if (ptr->is_deleted)
+                       continue;
+               if (ptr->domainname) {
+                       if (!ptr->is_last_name) {
+                               if (ptr->domainname != domainname)
+                                       continue;
+                       } else {
+                               if (tomoyo_pathcmp(ptr->domainname, last_name))
+                                       continue;
+                       }
+               }
+               if (tomoyo_pathcmp(ptr->program, program))
+                       continue;
+               if (ptr->is_not) {
+                       flag = false;
+                       break;
+               }
+               flag = true;
+       }
+       up_read(&tomoyo_domain_initializer_list_lock);
+       return flag;
+}
+
+/* The list for "struct tomoyo_domain_keeper_entry". */
+static LIST_HEAD(tomoyo_domain_keeper_list);
+static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
+
+/**
+ * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @domainname: The name of domain.
+ * @program:    The name of program. May be NULL.
+ * @is_not:     True if it is "no_keep_domain" entry.
+ * @is_delete:  True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_domain_keeper_entry(const char *domainname,
+                                            const char *program,
+                                            const bool is_not,
+                                            const bool is_delete)
+{
+       struct tomoyo_domain_keeper_entry *new_entry;
+       struct tomoyo_domain_keeper_entry *ptr;
+       const struct tomoyo_path_info *saved_domainname;
+       const struct tomoyo_path_info *saved_program = NULL;
+       static DEFINE_MUTEX(lock);
+       int error = -ENOMEM;
+       bool is_last_name = false;
+
+       if (!tomoyo_is_domain_def(domainname) &&
+           tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
+               is_last_name = true;
+       else if (!tomoyo_is_correct_domain(domainname, __func__))
+               return -EINVAL;
+       if (program) {
+               if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
+                       return -EINVAL;
+               saved_program = tomoyo_save_name(program);
+               if (!saved_program)
+                       return -ENOMEM;
+       }
+       saved_domainname = tomoyo_save_name(domainname);
+       if (!saved_domainname)
+               return -ENOMEM;
+       /***** EXCLUSIVE SECTION START *****/
+       down_write(&tomoyo_domain_keeper_list_lock);
+       list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+               if (ptr->is_not != is_not ||
+                   ptr->domainname != saved_domainname ||
+                   ptr->program != saved_program)
+                       continue;
+               ptr->is_deleted = is_delete;
+               error = 0;
+               goto out;
+       }
+       if (is_delete) {
+               error = -ENOENT;
+               goto out;
+       }
+       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+       if (!new_entry)
+               goto out;
+       new_entry->domainname = saved_domainname;
+       new_entry->program = saved_program;
+       new_entry->is_not = is_not;
+       new_entry->is_last_name = is_last_name;
+       list_add_tail(&new_entry->list, &tomoyo_domain_keeper_list);
+       error = 0;
+ out:
+       up_write(&tomoyo_domain_keeper_list_lock);
+       /***** EXCLUSIVE SECTION END *****/
+       return error;
+}
+
+/**
+ * tomoyo_write_domain_keeper_policy - Write "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_not:    True if it is "no_keep_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ */
+int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
+                                     const bool is_delete)
+{
+       char *cp = strstr(data, " from ");
+
+       if (cp) {
+               *cp = '\0';
+               return tomoyo_update_domain_keeper_entry(cp + 6, data, is_not,
+                                                        is_delete);
+       }
+       return tomoyo_update_domain_keeper_entry(data, NULL, is_not, is_delete);
+}
+
+/**
+ * tomoyo_read_domain_keeper_policy - Read "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
+{
+       struct list_head *pos;
+       bool done = true;
+
+       down_read(&tomoyo_domain_keeper_list_lock);
+       list_for_each_cookie(pos, head->read_var2,
+                            &tomoyo_domain_keeper_list) {
+               struct tomoyo_domain_keeper_entry *ptr;
+               const char *no;
+               const char *from = "";
+               const char *program = "";
+
+               ptr = list_entry(pos, struct tomoyo_domain_keeper_entry, list);
+               if (ptr->is_deleted)
+                       continue;
+               no = ptr->is_not ? "no_" : "";
+               if (ptr->program) {
+                       from = " from ";
+                       program = ptr->program->name;
+               }
+               if (!tomoyo_io_printf(head,
+                                     "%s" TOMOYO_KEYWORD_KEEP_DOMAIN
+                                     "%s%s%s\n", no, program, from,
+                                     ptr->domainname->name)) {
+                       done = false;
+                       break;
+               }
+       }
+       up_read(&tomoyo_domain_keeper_list_lock);
+       return done;
+}
+
+/**
+ * tomoyo_is_domain_keeper - Check whether the given program causes domain transition suppression.
+ *
+ * @domainname: The name of domain.
+ * @program:    The name of program.
+ * @last_name:  The last component of @domainname.
+ *
+ * Returns true if executing @program supresses domain transition,
+ * false otherwise.
+ */
+static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
+                                   const struct tomoyo_path_info *program,
+                                   const struct tomoyo_path_info *last_name)
+{
+       struct tomoyo_domain_keeper_entry *ptr;
+       bool flag = false;
+
+       down_read(&tomoyo_domain_keeper_list_lock);
+       list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+               if (ptr->is_deleted)
+                       continue;
+               if (!ptr->is_last_name) {
+                       if (ptr->domainname != domainname)
+                               continue;
+               } else {
+                       if (tomoyo_pathcmp(ptr->domainname, last_name))
+                               continue;
+               }
+               if (ptr->program && tomoyo_pathcmp(ptr->program, program))
+                       continue;
+               if (ptr->is_not) {
+                       flag = false;
+                       break;
+               }
+               flag = true;
+       }
+       up_read(&tomoyo_domain_keeper_list_lock);
+       return flag;
+}
+
+/* The list for "struct tomoyo_alias_entry". */
+static LIST_HEAD(tomoyo_alias_list);
+static DECLARE_RWSEM(tomoyo_alias_list_lock);
+
+/**
+ * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list.
+ *
+ * @original_name: The original program's real name.
+ * @aliased_name:  The symbolic program's symbolic link's name.
+ * @is_delete:     True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_alias_entry(const char *original_name,
+                                    const char *aliased_name,
+                                    const bool is_delete)
+{
+       struct tomoyo_alias_entry *new_entry;
+       struct tomoyo_alias_entry *ptr;
+       const struct tomoyo_path_info *saved_original_name;
+       const struct tomoyo_path_info *saved_aliased_name;
+       int error = -ENOMEM;
+
+       if (!tomoyo_is_correct_path(original_name, 1, -1, -1, __func__) ||
+           !tomoyo_is_correct_path(aliased_name, 1, -1, -1, __func__))
+               return -EINVAL; /* No patterns allowed. */
+       saved_original_name = tomoyo_save_name(original_name);
+       saved_aliased_name = tomoyo_save_name(aliased_name);
+       if (!saved_original_name || !saved_aliased_name)
+               return -ENOMEM;
+       /***** EXCLUSIVE SECTION START *****/
+       down_write(&tomoyo_alias_list_lock);
+       list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+               if (ptr->original_name != saved_original_name ||
+                   ptr->aliased_name != saved_aliased_name)
+                       continue;
+               ptr->is_deleted = is_delete;
+               error = 0;
+               goto out;
+       }
+       if (is_delete) {
+               error = -ENOENT;
+               goto out;
+       }
+       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+       if (!new_entry)
+               goto out;
+       new_entry->original_name = saved_original_name;
+       new_entry->aliased_name = saved_aliased_name;
+       list_add_tail(&new_entry->list, &tomoyo_alias_list);
+       error = 0;
+ out:
+       up_write(&tomoyo_alias_list_lock);
+       /***** EXCLUSIVE SECTION END *****/
+       return error;
+}
+
+/**
+ * tomoyo_read_alias_policy - Read "struct tomoyo_alias_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
+{
+       struct list_head *pos;
+       bool done = true;
+
+       down_read(&tomoyo_alias_list_lock);
+       list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
+               struct tomoyo_alias_entry *ptr;
+
+               ptr = list_entry(pos, struct tomoyo_alias_entry, list);
+               if (ptr->is_deleted)
+                       continue;
+               if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n",
+                                     ptr->original_name->name,
+                                     ptr->aliased_name->name)) {
+                       done = false;
+                       break;
+               }
+       }
+       up_read(&tomoyo_alias_list_lock);
+       return done;
+}
+
+/**
+ * tomoyo_write_alias_policy - Write "struct tomoyo_alias_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_alias_policy(char *data, const bool is_delete)
+{
+       char *cp = strchr(data, ' ');
+
+       if (!cp)
+               return -EINVAL;
+       *cp++ = '\0';
+       return tomoyo_update_alias_entry(data, cp, is_delete);
+}
+
+/* Domain create/delete/undelete handler. */
+
+/* #define TOMOYO_DEBUG_DOMAIN_UNDELETE */
+
+/**
+ * tomoyo_delete_domain - Delete a domain.
+ *
+ * @domainname: The name of domain.
+ *
+ * Returns 0.
+ */
+int tomoyo_delete_domain(char *domainname)
+{
+       struct tomoyo_domain_info *domain;
+       struct tomoyo_path_info name;
+
+       name.name = domainname;
+       tomoyo_fill_path_info(&name);
+       /***** EXCLUSIVE SECTION START *****/
+       down_write(&tomoyo_domain_list_lock);
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+       printk(KERN_DEBUG "tomoyo_delete_domain %s\n", domainname);
+       list_for_each_entry(domain, &tomoyo_domain_list, list) {
+               if (tomoyo_pathcmp(domain->domainname, &name))
+                       continue;
+               printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted);
+       }
+#endif
+       /* Is there an active domain? */
+       list_for_each_entry(domain, &tomoyo_domain_list, list) {
+               struct tomoyo_domain_info *domain2;
+               /* Never delete tomoyo_kernel_domain */
+               if (domain == &tomoyo_kernel_domain)
+                       continue;
+               if (domain->is_deleted ||
+                   tomoyo_pathcmp(domain->domainname, &name))
+                       continue;
+               /* Mark already deleted domains as non undeletable. */
+               list_for_each_entry(domain2, &tomoyo_domain_list, list) {
+                       if (!domain2->is_deleted ||
+                           tomoyo_pathcmp(domain2->domainname, &name))
+                               continue;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+                       if (domain2->is_deleted != 255)
+                               printk(KERN_DEBUG
+                                      "Marked %p as non undeletable\n",
+                                      domain2);
+#endif
+                       domain2->is_deleted = 255;
+               }
+               /* Delete and mark active domain as undeletable. */
+               domain->is_deleted = 1;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+               printk(KERN_DEBUG "Marked %p as undeletable\n", domain);
+#endif
+               break;
+       }
+       up_write(&tomoyo_domain_list_lock);
+       /***** EXCLUSIVE SECTION END *****/
+       return 0;
+}
+
+/**
+ * tomoyo_undelete_domain - Undelete a domain.
+ *
+ * @domainname: The name of domain.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ */
+struct tomoyo_domain_info *tomoyo_undelete_domain(const char *domainname)
+{
+       struct tomoyo_domain_info *domain;
+       struct tomoyo_domain_info *candidate_domain = NULL;
+       struct tomoyo_path_info name;
+
+       name.name = domainname;
+       tomoyo_fill_path_info(&name);
+       /***** EXCLUSIVE SECTION START *****/
+       down_write(&tomoyo_domain_list_lock);
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+       printk(KERN_DEBUG "tomoyo_undelete_domain %s\n", domainname);
+       list_for_each_entry(domain, &tomoyo_domain_list, list) {
+               if (tomoyo_pathcmp(domain->domainname, &name))
+                       continue;
+               printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted);
+       }
+#endif
+       list_for_each_entry(domain, &tomoyo_domain_list, list) {
+               if (tomoyo_pathcmp(&name, domain->domainname))
+                       continue;
+               if (!domain->is_deleted) {
+                       /* This domain is active. I can't undelete. */
+                       candidate_domain = NULL;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+                       printk(KERN_DEBUG "%p is active. I can't undelete.\n",
+                              domain);
+#endif
+                       break;
+               }
+               /* Is this domain undeletable? */
+               if (domain->is_deleted == 1)
+                       candidate_domain = domain;
+       }
+       if (candidate_domain) {
+               candidate_domain->is_deleted = 0;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+               printk(KERN_DEBUG "%p was undeleted.\n", candidate_domain);
+#endif
+       }
+       up_write(&tomoyo_domain_list_lock);
+       /***** EXCLUSIVE SECTION END *****/
+       return candidate_domain;
+}
+
+/**
+ * tomoyo_find_or_assign_new_domain - Create a domain.
+ *
+ * @domainname: The name of domain.
+ * @profile:    Profile number to assign if the domain was newly created.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ */
+struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
+                                                           domainname,
+                                                           const u8 profile)
+{
+       struct tomoyo_domain_info *domain = NULL;
+       const struct tomoyo_path_info *saved_domainname;
+
+       /***** EXCLUSIVE SECTION START *****/
+       down_write(&tomoyo_domain_list_lock);
+       domain = tomoyo_find_domain(domainname);
+       if (domain)
+               goto out;
+       if (!tomoyo_is_correct_domain(domainname, __func__))
+               goto out;
+       saved_domainname = tomoyo_save_name(domainname);
+       if (!saved_domainname)
+               goto out;
+       /* Can I reuse memory of deleted domain? */
+       list_for_each_entry(domain, &tomoyo_domain_list, list) {
+               struct task_struct *p;
+               struct tomoyo_acl_info *ptr;
+               bool flag;
+               if (!domain->is_deleted ||
+                   domain->domainname != saved_domainname)
+                       continue;
+               flag = false;
+               /***** CRITICAL SECTION START *****/
+               read_lock(&tasklist_lock);
+               for_each_process(p) {
+                       if (tomoyo_real_domain(p) != domain)
+                               continue;
+                       flag = true;
+                       break;
+               }
+               read_unlock(&tasklist_lock);
+               /***** CRITICAL SECTION END *****/
+               if (flag)
+                       continue;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+               printk(KERN_DEBUG "Reusing %p %s\n", domain,
+                      domain->domainname->name);
+#endif
+               list_for_each_entry(ptr, &domain->acl_info_list, list) {
+                       ptr->type |= TOMOYO_ACL_DELETED;
+               }
+               tomoyo_set_domain_flag(domain, true, domain->flags);
+               domain->profile = profile;
+               domain->quota_warned = false;
+               mb(); /* Avoid out-of-order execution. */
+               domain->is_deleted = 0;
+               goto out;
+       }
+       /* No memory reusable. Create using new memory. */
+       domain = tomoyo_alloc_element(sizeof(*domain));
+       if (domain) {
+               INIT_LIST_HEAD(&domain->acl_info_list);
+               domain->domainname = saved_domainname;
+               domain->profile = profile;
+               list_add_tail(&domain->list, &tomoyo_domain_list);
+       }
+ out:
+       up_write(&tomoyo_domain_list_lock);
+       /***** EXCLUSIVE SECTION END *****/
+       return domain;
+}
+
+/**
+ * tomoyo_find_next_domain - Find a domain.
+ *
+ * @bprm:           Pointer to "struct linux_binprm".
+ * @next_domain:    Pointer to pointer to "struct tomoyo_domain_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_find_next_domain(struct linux_binprm *bprm,
+                           struct tomoyo_domain_info **next_domain)
+{
+       /*
+        * This function assumes that the size of buffer returned by
+        * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN.
+        */
+       struct tomoyo_page_buffer *tmp = tomoyo_alloc(sizeof(*tmp));
+       struct tomoyo_domain_info *old_domain = tomoyo_domain();
+       struct tomoyo_domain_info *domain = NULL;
+       const char *old_domain_name = old_domain->domainname->name;
+       const char *original_name = bprm->filename;
+       char *new_domain_name = NULL;
+       char *real_program_name = NULL;
+       char *symlink_program_name = NULL;
+       const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE);
+       const bool is_enforce = (mode == 3);
+       int retval = -ENOMEM;
+       struct tomoyo_path_info r; /* real name */
+       struct tomoyo_path_info s; /* symlink name */
+       struct tomoyo_path_info l; /* last name */
+       static bool initialized;
+
+       if (!tmp)
+               goto out;
+
+       if (!initialized) {
+               /*
+                * Built-in initializers. This is needed because policies are
+                * not loaded until starting /sbin/init.
+                */
+               tomoyo_update_domain_initializer_entry(NULL, "/sbin/hotplug",
+                                                      false, false);
+               tomoyo_update_domain_initializer_entry(NULL, "/sbin/modprobe",
+                                                      false, false);
+               initialized = true;
+       }
+
+       /* Get tomoyo_realpath of program. */
+       retval = -ENOENT;
+       /* I hope tomoyo_realpath() won't fail with -ENOMEM. */
+       real_program_name = tomoyo_realpath(original_name);
+       if (!real_program_name)
+               goto out;
+       /* Get tomoyo_realpath of symbolic link. */
+       symlink_program_name = tomoyo_realpath_nofollow(original_name);
+       if (!symlink_program_name)
+               goto out;
+
+       r.name = real_program_name;
+       tomoyo_fill_path_info(&r);
+       s.name = symlink_program_name;
+       tomoyo_fill_path_info(&s);
+       l.name = tomoyo_get_last_name(old_domain);
+       tomoyo_fill_path_info(&l);
+
+       /* Check 'alias' directive. */
+       if (tomoyo_pathcmp(&r, &s)) {
+               struct tomoyo_alias_entry *ptr;
+               /* Is this program allowed to be called via symbolic links? */
+               down_read(&tomoyo_alias_list_lock);
+               list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+                       if (ptr->is_deleted ||
+                           tomoyo_pathcmp(&r, ptr->original_name) ||
+                           tomoyo_pathcmp(&s, ptr->aliased_name))
+                               continue;
+                       memset(real_program_name, 0, TOMOYO_MAX_PATHNAME_LEN);
+                       strncpy(real_program_name, ptr->aliased_name->name,
+                               TOMOYO_MAX_PATHNAME_LEN - 1);
+                       tomoyo_fill_path_info(&r);
+                       break;
+               }
+               up_read(&tomoyo_alias_list_lock);
+       }
+
+       /* Check execute permission. */
+       retval = tomoyo_check_exec_perm(old_domain, &r, tmp);
+       if (retval < 0)
+               goto out;
+
+       new_domain_name = tmp->buffer;
+       if (tomoyo_is_domain_initializer(old_domain->domainname, &r, &l)) {
+               /* Transit to the child of tomoyo_kernel_domain domain. */
+               snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1,
+                        TOMOYO_ROOT_NAME " " "%s", real_program_name);
+       } else if (old_domain == &tomoyo_kernel_domain &&
+                  !tomoyo_policy_loaded) {
+               /*
+                * Needn't to transit from kernel domain before starting
+                * /sbin/init. But transit from kernel domain if executing
+                * initializers because they might start before /sbin/init.
+                */
+               domain = old_domain;
+       } else if (tomoyo_is_domain_keeper(old_domain->domainname, &r, &l)) {
+               /* Keep current domain. */
+               domain = old_domain;
+       } else {
+               /* Normal domain transition. */
+               snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1,
+                        "%s %s", old_domain_name, real_program_name);
+       }
+       if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
+               goto done;
+       down_read(&tomoyo_domain_list_lock);
+       domain = tomoyo_find_domain(new_domain_name);
+       up_read(&tomoyo_domain_list_lock);
+       if (domain)
+               goto done;
+       if (is_enforce)
+               goto done;
+       domain = tomoyo_find_or_assign_new_domain(new_domain_name,
+                                                 old_domain->profile);
+ done:
+       if (domain)
+               goto out;
+       printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n",
+              new_domain_name);
+       if (is_enforce)
+               retval = -EPERM;
+       else
+               tomoyo_set_domain_flag(old_domain, false,
+                                      TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED);
+ out:
+       tomoyo_free(real_program_name);
+       tomoyo_free(symlink_program_name);
+       *next_domain = domain ? domain : old_domain;
+       tomoyo_free(tmp);
+       return retval;
+}
diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c
new file mode 100644 (file)
index 0000000..65f50c1
--- /dev/null
@@ -0,0 +1,1241 @@
+/*
+ * security/tomoyo/file.c
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#include "common.h"
+#include "tomoyo.h"
+#include "realpath.h"
+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
+
+/* Structure for "allow_read" keyword. */
+struct tomoyo_globally_readable_file_entry {
+       struct list_head list;
+       const struct tomoyo_path_info *filename;
+       bool is_deleted;
+};
+
+/* Structure for "file_pattern" keyword. */
+struct tomoyo_pattern_entry {
+       struct list_head list;
+       const struct tomoyo_path_info *pattern;
+       bool is_deleted;
+};
+
+/* Structure for "deny_rewrite" keyword. */
+struct tomoyo_no_rewrite_entry {
+       struct list_head list;
+       const struct tomoyo_path_info *pattern;
+       bool is_deleted;
+};
+
+/* Keyword array for single path operations. */
+static const char *tomoyo_sp_keyword[TOMOYO_MAX_SINGLE_PATH_OPERATION] = {
+       [TOMOYO_TYPE_READ_WRITE_ACL] = "read/write",
+       [TOMOYO_TYPE_EXECUTE_ACL]    = "execute",
+       [TOMOYO_TYPE_READ_ACL]       = "read",
+       [TOMOYO_TYPE_WRITE_ACL]      = "write",
+       [TOMOYO_TYPE_CREATE_ACL]     = "create",
+       [TOMOYO_TYPE_UNLINK_ACL]     = "unlink",
+       [TOMOYO_TYPE_MKDIR_ACL]      = "mkdir",
+       [TOMOYO_TYPE_RMDIR_ACL]      = "rmdir",
+       [TOMOYO_TYPE_MKFIFO_ACL]     = "mkfifo",
+       [TOMOYO_TYPE_MKSOCK_ACL]     = "mksock",
+       [TOMOYO_TYPE_MKBLOCK_ACL]    = "mkblock",
+       [TOMOYO_TYPE_MKCHAR_ACL]     = "mkchar",
+       [TOMOYO_TYPE_TRUNCATE_ACL]   = "truncate",
+       [TOMOYO_TYPE_SYMLINK_ACL]    = "symlink",
+       [TOMOYO_TYPE_REWRITE_ACL]    = "rewrite",
+};
+
+/* Keyword array for double path operations. */
+static const char *tomoyo_dp_keyword[TOMOYO_MAX_DOUBLE_PATH_OPERATION] = {
+       [TOMOYO_TYPE_LINK_ACL]    = "link",
+       [TOMOYO_TYPE_RENAME_ACL]  = "rename",
+};
+
+/**
+ * tomoyo_sp2keyword - Get the name of single path operation.
+ *
+ * @operation: Type of operation.
+ *
+ * Returns the name of single path operation.
+ */
+const char *tomoyo_sp2keyword(const u8 operation)
+{
+       return (operation < TOMOYO_MAX_SINGLE_PATH_OPERATION)
+               ? tomoyo_sp_keyword[operation] : NULL;
+}
+
+/**
+ * tomoyo_dp2keyword - Get the name of double path operation.
+ *
+ * @operation: Type of operation.
+ *
+ * Returns the name of double path operation.
+ */
+const char *tomoyo_dp2keyword(const u8 operation)
+{
+       return (operation < TOMOYO_MAX_DOUBLE_PATH_OPERATION)
+               ? tomoyo_dp_keyword[operation] : NULL;
+}
+
+/**
+ * tomoyo_strendswith - Check whether the token ends with the given token.
+ *
+ * @name: The token to check.
+ * @tail: The token to find.
+ *
+ * Returns true if @name ends with @tail, false otherwise.
+ */
+static bool tomoyo_strendswith(const char *name, const char *tail)
+{
+       int len;
+
+       if (!name || !tail)
+               return false;
+       len = strlen(name) - strlen(tail);
+       return len >= 0 && !strcmp(name + len, tail);
+}
+
+/**
+ * tomoyo_get_path - Get realpath.
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
+ */
+static struct tomoyo_path_info *tomoyo_get_path(struct path *path)
+{
+       int error;
+       struct tomoyo_path_info_with_data *buf = tomoyo_alloc(sizeof(*buf));
+
+       if (!buf)
+               return NULL;
+       /* Reserve one byte for appending "/". */
+       error = tomoyo_realpath_from_path2(path, buf->body,
+                                          sizeof(buf->body) - 2);
+       if (!error) {
+               buf->head.name = buf->body;
+               tomoyo_fill_path_info(&buf->head);
+               return &buf->head;
+       }
+       tomoyo_free(buf);
+       return NULL;
+}
+
+/* Lock for domain->acl_info_list. */
+DECLARE_RWSEM(tomoyo_domain_acl_info_list_lock);
+
+static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
+                                        const char *filename2,
+                                        struct tomoyo_domain_info *
+                                        const domain, const bool is_delete);
+static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
+                                        struct tomoyo_domain_info *
+                                        const domain, const bool is_delete);
+
+/* The list for "struct tomoyo_globally_readable_file_entry". */
+static LIST_HEAD(tomoyo_globally_readable_list);
+static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
+
+/**
+ * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
+ *
+ * @filename:  Filename unconditionally permitted to open() for reading.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_globally_readable_entry(const char *filename,
+                                                const bool is_delete)
+{
+       struct tomoyo_globally_readable_file_entry *new_entry;
+       struct tomoyo_globally_readable_file_entry *ptr;
+       const struct tomoyo_path_info *saved_filename;
+       int error = -ENOMEM;
+
+       if (!tomoyo_is_correct_path(filename, 1, 0, -1, __func__))
+               return -EINVAL;
+       saved_filename = tomoyo_save_name(filename);
+       if (!saved_filename)
+               return -ENOMEM;
+       /***** EXCLUSIVE SECTION START *****/
+       down_write(&tomoyo_globally_readable_list_lock);
+       list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+               if (ptr->filename != saved_filename)
+                       continue;
+               ptr->is_deleted = is_delete;
+               error = 0;
+               goto out;
+       }
+       if (is_delete) {
+               error = -ENOENT;
+               goto out;
+       }
+       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+       if (!new_entry)
+               goto out;
+       new_entry->filename = saved_filename;
+       list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
+       error = 0;
+ out:
+       up_write(&tomoyo_globally_readable_list_lock);
+       /***** EXCLUSIVE SECTION END *****/
+       return error;
+}
+
+/**
+ * tomoyo_is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
+ *
+ * @filename: The filename to check.
+ *
+ * Returns true if any domain can open @filename for reading, false otherwise.
+ */
+static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
+                                            filename)
+{
+       struct tomoyo_globally_readable_file_entry *ptr;
+       bool found = false;
+       down_read(&tomoyo_globally_readable_list_lock);
+       list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+               if (!ptr->is_deleted &&
+                   tomoyo_path_matches_pattern(filename, ptr->filename)) {
+                       found = true;
+                       break;
+               }
+       }
+       up_read(&tomoyo_globally_readable_list_lock);
+       return found;
+}
+
+/**
+ * tomoyo_write_globally_readable_policy - Write "struct tomoyo_globally_readable_file_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
+{
+       return tomoyo_update_globally_readable_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_globally_readable_policy - Read "struct tomoyo_globally_readable_file_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
+{
+       struct list_head *pos;
+       bool done = true;
+
+       down_read(&tomoyo_globally_readable_list_lock);
+       list_for_each_cookie(pos, head->read_var2,
+                            &tomoyo_globally_readable_list) {
+               struct tomoyo_globally_readable_file_entry *ptr;
+               ptr = list_entry(pos,
+                                struct tomoyo_globally_readable_file_entry,
+                                list);
+               if (ptr->is_deleted)
+                       continue;
+               if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n",
+                                     ptr->filename->name)) {
+                       done = false;
+                       break;
+               }
+       }
+       up_read(&tomoyo_globally_readable_list_lock);
+       return done;
+}
+
+/* The list for "struct tomoyo_pattern_entry". */
+static LIST_HEAD(tomoyo_pattern_list);
+static DECLARE_RWSEM(tomoyo_pattern_list_lock);
+
+/**
+ * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
+ *
+ * @pattern:   Pathname pattern.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_file_pattern_entry(const char *pattern,
+                                           const bool is_delete)
+{
+       struct tomoyo_pattern_entry *new_entry;
+       struct tomoyo_pattern_entry *ptr;
+       const struct tomoyo_path_info *saved_pattern;
+       int error = -ENOMEM;
+
+       if (!tomoyo_is_correct_path(pattern, 0, 1, 0, __func__))
+               return -EINVAL;
+       saved_pattern = tomoyo_save_name(pattern);
+       if (!saved_pattern)
+               return -ENOMEM;
+       /***** EXCLUSIVE SECTION START *****/
+       down_write(&tomoyo_pattern_list_lock);
+       list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+               if (saved_pattern != ptr->pattern)
+                       continue;
+               ptr->is_deleted = is_delete;
+               error = 0;
+               goto out;
+       }
+       if (is_delete) {
+               error = -ENOENT;
+               goto out;
+       }
+       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+       if (!new_entry)
+               goto out;
+       new_entry->pattern = saved_pattern;
+       list_add_tail(&new_entry->list, &tomoyo_pattern_list);
+       error = 0;
+ out:
+       up_write(&tomoyo_pattern_list_lock);
+       /***** EXCLUSIVE SECTION END *****/
+       return error;
+}
+
+/**
+ * tomoyo_get_file_pattern - Get patterned pathname.
+ *
+ * @filename: The filename to find patterned pathname.
+ *
+ * Returns pointer to pathname pattern if matched, @filename otherwise.
+ */
+static const struct tomoyo_path_info *
+tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
+{
+       struct tomoyo_pattern_entry *ptr;
+       const struct tomoyo_path_info *pattern = NULL;
+
+       down_read(&tomoyo_pattern_list_lock);
+       list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+               if (ptr->is_deleted)
+                       continue;
+               if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
+                       continue;
+               pattern = ptr->pattern;
+               if (tomoyo_strendswith(pattern->name, "/\\*")) {
+                       /* Do nothing. Try to find the better match. */
+               } else {
+                       /* This would be the better match. Use this. */
+                       break;
+               }
+       }
+       up_read(&tomoyo_pattern_list_lock);
+       if (pattern)
+               filename = pattern;
+       return filename;
+}
+
+/**
+ * tomoyo_write_pattern_policy - Write "struct tomoyo_pattern_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_pattern_policy(char *data, const bool is_delete)
+{
+       return tomoyo_update_file_pattern_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_file_pattern - Read "struct tomoyo_pattern_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
+{
+       struct list_head *pos;
+       bool done = true;
+
+       down_read(&tomoyo_pattern_list_lock);
+       list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
+               struct tomoyo_pattern_entry *ptr;
+               ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
+               if (ptr->is_deleted)
+                       continue;
+               if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN "%s\n",
+                                     ptr->pattern->name)) {
+                       done = false;
+                       break;
+               }
+       }
+       up_read(&tomoyo_pattern_list_lock);
+       return done;
+}
+
+/* The list for "struct tomoyo_no_rewrite_entry". */
+static LIST_HEAD(tomoyo_no_rewrite_list);
+static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);
+
+/**
+ * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
+ *
+ * @pattern:   Pathname pattern that are not rewritable by default.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_no_rewrite_entry(const char *pattern,
+                                         const bool is_delete)
+{
+       struct tomoyo_no_rewrite_entry *new_entry, *ptr;
+       const struct tomoyo_path_info *saved_pattern;
+       int error = -ENOMEM;
+
+       if (!tomoyo_is_correct_path(pattern, 0, 0, 0, __func__))
+               return -EINVAL;
+       saved_pattern = tomoyo_save_name(pattern);
+       if (!saved_pattern)
+               return -ENOMEM;
+       /***** EXCLUSIVE SECTION START *****/
+       down_write(&tomoyo_no_rewrite_list_lock);
+       list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+               if (ptr->pattern != saved_pattern)
+                       continue;
+               ptr->is_deleted = is_delete;
+               error = 0;
+               goto out;
+       }
+       if (is_delete) {
+               error = -ENOENT;
+               goto out;
+       }
+       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+       if (!new_entry)
+               goto out;
+       new_entry->pattern = saved_pattern;
+       list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
+       error = 0;
+ out:
+       up_write(&tomoyo_no_rewrite_list_lock);
+       /***** EXCLUSIVE SECTION END *****/
+       return error;
+}
+
+/**
+ * tomoyo_is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
+ *
+ * @filename: Filename to check.
+ *
+ * Returns true if @filename is specified by "deny_rewrite" directive,
+ * false otherwise.
+ */
+static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
+{
+       struct tomoyo_no_rewrite_entry *ptr;
+       bool found = false;
+
+       down_read(&tomoyo_no_rewrite_list_lock);
+       list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+               if (ptr->is_deleted)
+                       continue;
+               if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
+                       continue;
+               found = true;
+               break;
+       }
+       up_read(&tomoyo_no_rewrite_list_lock);
+       return found;
+}
+
+/**
+ * tomoyo_write_no_rewrite_policy - Write "struct tomoyo_no_rewrite_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
+{
+       return tomoyo_update_no_rewrite_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_no_rewrite_policy - Read "struct tomoyo_no_rewrite_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
+{
+       struct list_head *pos;
+       bool done = true;
+
+       down_read(&tomoyo_no_rewrite_list_lock);
+       list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
+               struct tomoyo_no_rewrite_entry *ptr;
+               ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
+               if (ptr->is_deleted)
+                       continue;
+               if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE "%s\n",
+                                     ptr->pattern->name)) {
+                       done = false;
+                       break;
+               }
+       }
+       up_read(&tomoyo_no_rewrite_list_lock);
+       return done;
+}
+
+/**
+ * tomoyo_update_file_acl - Update file's read/write/execute ACL.
+ *
+ * @filename:  Filename.
+ * @perm:      Permission (between 1 to 7).
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * This is legacy support interface for older policy syntax.
+ * Current policy syntax uses "allow_read/write" instead of "6",
+ * "allow_read" instead of "4", "allow_write" instead of "2",
+ * "allow_execute" instead of "1".
+ */
+static int tomoyo_update_file_acl(const char *filename, u8 perm,
+                                 struct tomoyo_domain_info * const domain,
+                                 const bool is_delete)
+{
+       if (perm > 7 || !perm) {
+               printk(KERN_DEBUG "%s: Invalid permission '%d %s'\n",
+                      __func__, perm, filename);
+               return -EINVAL;
+       }
+       if (filename[0] != '@' && tomoyo_strendswith(filename, "/"))
+               /*
+                * Only 'allow_mkdir' and 'allow_rmdir' are valid for
+                * directory permissions.
+                */
+               return 0;
+       if (perm & 4)
+               tomoyo_update_single_path_acl(TOMOYO_TYPE_READ_ACL, filename,
+                                             domain, is_delete);
+       if (perm & 2)
+               tomoyo_update_single_path_acl(TOMOYO_TYPE_WRITE_ACL, filename,
+                                             domain, is_delete);
+       if (perm & 1)
+               tomoyo_update_single_path_acl(TOMOYO_TYPE_EXECUTE_ACL,
+                                             filename, domain, is_delete);
+       return 0;
+}
+
+/**
+ * tomoyo_check_single_path_acl2 - Check permission for single path operation.
+ *
+ * @domain:          Pointer to "struct tomoyo_domain_info".
+ * @filename:        Filename to check.
+ * @perm:            Permission.
+ * @may_use_pattern: True if patterned ACL is permitted.
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ */
+static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
+                                        domain,
+                                        const struct tomoyo_path_info *
+                                        filename,
+                                        const u16 perm,
+                                        const bool may_use_pattern)
+{
+       struct tomoyo_acl_info *ptr;
+       int error = -EPERM;
+
+       down_read(&tomoyo_domain_acl_info_list_lock);
+       list_for_each_entry(ptr, &domain->acl_info_list, list) {
+               struct tomoyo_single_path_acl_record *acl;
+               if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
+                       continue;
+               acl = container_of(ptr, struct tomoyo_single_path_acl_record,
+                                  head);
+               if (!(acl->perm & perm))
+                       continue;
+               if (may_use_pattern || !acl->filename->is_patterned) {
+                       if (!tomoyo_path_matches_pattern(filename,
+                                                        acl->filename))
+                               continue;
+               } else {
+                       continue;
+               }
+               error = 0;
+               break;
+       }
+       up_read(&tomoyo_domain_acl_info_list_lock);
+       return error;
+}
+
+/**
+ * tomoyo_check_file_acl - Check permission for opening files.
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @filename:  Filename to check.
+ * @operation: Mode ("read" or "write" or "read/write" or "execute").
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ */
+static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
+                                const struct tomoyo_path_info *filename,
+                                const u8 operation)
+{
+       u16 perm = 0;
+
+       if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
+               return 0;
+       if (operation == 6)
+               perm = 1 << TOMOYO_TYPE_READ_WRITE_ACL;
+       else if (operation == 4)
+               perm = 1 << TOMOYO_TYPE_READ_ACL;
+       else if (operation == 2)
+               perm = 1 << TOMOYO_TYPE_WRITE_ACL;
+       else if (operation == 1)
+               perm = 1 << TOMOYO_TYPE_EXECUTE_ACL;
+       else
+               BUG();
+       return tomoyo_check_single_path_acl2(domain, filename, perm,
+                                            operation != 1);
+}
+
+/**
+ * tomoyo_check_file_perm2 - Check permission for opening files.
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @filename:  Filename to check.
+ * @perm:      Mode ("read" or "write" or "read/write" or "execute").
+ * @operation: Operation name passed used for verbose mode.
+ * @mode:      Access control mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
+                                  const struct tomoyo_path_info *filename,
+                                  const u8 perm, const char *operation,
+                                  const u8 mode)
+{
+       const bool is_enforce = (mode == 3);
+       const char *msg = "<unknown>";
+       int error = 0;
+
+       if (!filename)
+               return 0;
+       error = tomoyo_check_file_acl(domain, filename, perm);
+       if (error && perm == 4 &&
+           (domain->flags & TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ) == 0
+           && tomoyo_is_globally_readable_file(filename))
+               error = 0;
+       if (perm == 6)
+               msg = tomoyo_sp2keyword(TOMOYO_TYPE_READ_WRITE_ACL);
+       else if (perm == 4)
+               msg = tomoyo_sp2keyword(TOMOYO_TYPE_READ_ACL);
+       else if (perm == 2)
+               msg = tomoyo_sp2keyword(TOMOYO_TYPE_WRITE_ACL);
+       else if (perm == 1)
+               msg = tomoyo_sp2keyword(TOMOYO_TYPE_EXECUTE_ACL);
+       else
+               BUG();
+       if (!error)
+               return 0;
+       if (tomoyo_verbose_mode(domain))
+               printk(KERN_WARNING "TOMOYO-%s: Access '%s(%s) %s' denied "
+                      "for %s\n", tomoyo_get_msg(is_enforce), msg, operation,
+                      filename->name, tomoyo_get_last_name(domain));
+       if (is_enforce)
+               return error;
+       if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
+               /* Don't use patterns for execute permission. */
+               const struct tomoyo_path_info *patterned_file = (perm != 1) ?
+                       tomoyo_get_file_pattern(filename) : filename;
+               tomoyo_update_file_acl(patterned_file->name, perm,
+                                      domain, false);
+       }
+       return 0;
+}
+
+/**
+ * tomoyo_write_file_policy - Update file related list.
+ *
+ * @data:      String to parse.
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
+                            const bool is_delete)
+{
+       char *filename = strchr(data, ' ');
+       char *filename2;
+       unsigned int perm;
+       u8 type;
+
+       if (!filename)
+               return -EINVAL;
+       *filename++ = '\0';
+       if (sscanf(data, "%u", &perm) == 1)
+               return tomoyo_update_file_acl(filename, (u8) perm, domain,
+                                             is_delete);
+       if (strncmp(data, "allow_", 6))
+               goto out;
+       data += 6;
+       for (type = 0; type < TOMOYO_MAX_SINGLE_PATH_OPERATION; type++) {
+               if (strcmp(data, tomoyo_sp_keyword[type]))
+                       continue;
+               return tomoyo_update_single_path_acl(type, filename,
+                                                    domain, is_delete);
+       }
+       filename2 = strchr(filename, ' ');
+       if (!filename2)
+               goto out;
+       *filename2++ = '\0';
+       for (type = 0; type < TOMOYO_MAX_DOUBLE_PATH_OPERATION; type++) {
+               if (strcmp(data, tomoyo_dp_keyword[type]))
+                       continue;
+               return tomoyo_update_double_path_acl(type, filename, filename2,
+                                                    domain, is_delete);
+       }
+ out:
+       return -EINVAL;
+}
+
+/**
+ * tomoyo_update_single_path_acl - Update "struct tomoyo_single_path_acl_record" list.
+ *
+ * @type:      Type of operation.
+ * @filename:  Filename.
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
+                                        struct tomoyo_domain_info *
+                                        const domain, const bool is_delete)
+{
+       static const u16 rw_mask =
+               (1 << TOMOYO_TYPE_READ_ACL) | (1 << TOMOYO_TYPE_WRITE_ACL);
+       const struct tomoyo_path_info *saved_filename;
+       struct tomoyo_acl_info *ptr;
+       struct tomoyo_single_path_acl_record *acl;
+       int error = -ENOMEM;
+       const u16 perm = 1 << type;
+
+       if (!domain)
+               return -EINVAL;
+       if (!tomoyo_is_correct_path(filename, 0, 0, 0, __func__))
+               return -EINVAL;
+       saved_filename = tomoyo_save_name(filename);
+       if (!saved_filename)
+               return -ENOMEM;
+       /***** EXCLUSIVE SECTION START *****/
+       down_write(&tomoyo_domain_acl_info_list_lock);
+       if (is_delete)
+               goto delete;
+       list_for_each_entry(ptr, &domain->acl_info_list, list) {
+               if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
+                       continue;
+               acl = container_of(ptr, struct tomoyo_single_path_acl_record,
+                                  head);
+               if (acl->filename != saved_filename)
+                       continue;
+               /* Special case. Clear all bits if marked as deleted. */
+               if (ptr->type & TOMOYO_ACL_DELETED)
+                       acl->perm = 0;
+               acl->perm |= perm;
+               if ((acl->perm & rw_mask) == rw_mask)
+                       acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE_ACL;
+               else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL))
+                       acl->perm |= rw_mask;
+               ptr->type &= ~TOMOYO_ACL_DELETED;
+               error = 0;
+               goto out;
+       }
+       /* Not found. Append it to the tail. */
+       acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_SINGLE_PATH_ACL);
+       if (!acl)
+               goto out;
+       acl->perm = perm;
+       if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
+               acl->perm |= rw_mask;
+       acl->filename = saved_filename;
+       list_add_tail(&acl->head.list, &domain->acl_info_list);
+       error = 0;
+       goto out;
+ delete:
+       error = -ENOENT;
+       list_for_each_entry(ptr, &domain->acl_info_list, list) {
+               if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
+                       continue;
+               acl = container_of(ptr, struct tomoyo_single_path_acl_record,
+                                  head);
+               if (acl->filename != saved_filename)
+                       continue;
+               acl->perm &= ~perm;
+               if ((acl->perm & rw_mask) != rw_mask)
+                       acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE_ACL);
+               else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL)))
+                       acl->perm &= ~rw_mask;
+               if (!acl->perm)
+                       ptr->type |= TOMOYO_ACL_DELETED;
+               error = 0;
+               break;
+       }
+ out:
+       up_write(&tomoyo_domain_acl_info_list_lock);
+       /***** EXCLUSIVE SECTION END *****/
+       return error;
+}
+
+/**
+ * tomoyo_update_double_path_acl - Update "struct tomoyo_double_path_acl_record" list.
+ *
+ * @type:      Type of operation.
+ * @filename1: First filename.
+ * @filename2: Second filename.
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
+                                        const char *filename2,
+                                        struct tomoyo_domain_info *
+                                        const domain, const bool is_delete)
+{
+       const struct tomoyo_path_info *saved_filename1;
+       const struct tomoyo_path_info *saved_filename2;
+       struct tomoyo_acl_info *ptr;
+       struct tomoyo_double_path_acl_record *acl;
+       int error = -ENOMEM;
+       const u8 perm = 1 << type;
+
+       if (!domain)
+               return -EINVAL;
+       if (!tomoyo_is_correct_path(filename1, 0, 0, 0, __func__) ||
+           !tomoyo_is_correct_path(filename2, 0, 0, 0, __func__))
+               return -EINVAL;
+       saved_filename1 = tomoyo_save_name(filename1);
+       saved_filename2 = tomoyo_save_name(filename2);
+       if (!saved_filename1 || !saved_filename2)
+               return -ENOMEM;
+       /***** EXCLUSIVE SECTION START *****/
+       down_write(&tomoyo_domain_acl_info_list_lock);
+       if (is_delete)
+               goto delete;
+       list_for_each_entry(ptr, &domain->acl_info_list, list) {
+               if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+                       continue;
+               acl = container_of(ptr, struct tomoyo_double_path_acl_record,
+                                  head);
+               if (acl->filename1 != saved_filename1 ||
+                   acl->filename2 != saved_filename2)
+                       continue;
+               /* Special case. Clear all bits if marked as deleted. */
+               if (ptr->type & TOMOYO_ACL_DELETED)
+                       acl->perm = 0;
+               acl->perm |= perm;
+               ptr->type &= ~TOMOYO_ACL_DELETED;
+               error = 0;
+               goto out;
+       }
+       /* Not found. Append it to the tail. */
+       acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_DOUBLE_PATH_ACL);
+       if (!acl)
+               goto out;
+       acl->perm = perm;
+       acl->filename1 = saved_filename1;
+       acl->filename2 = saved_filename2;
+       list_add_tail(&acl->head.list, &domain->acl_info_list);
+       error = 0;
+       goto out;
+ delete:
+       error = -ENOENT;
+       list_for_each_entry(ptr, &domain->acl_info_list, list) {
+               if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+                       continue;
+               acl = container_of(ptr, struct tomoyo_double_path_acl_record,
+                                  head);
+               if (acl->filename1 != saved_filename1 ||
+                   acl->filename2 != saved_filename2)
+                       continue;
+               acl->perm &= ~perm;
+               if (!acl->perm)
+                       ptr->type |= TOMOYO_ACL_DELETED;
+               error = 0;
+               break;
+       }
+ out:
+       up_write(&tomoyo_domain_acl_info_list_lock);
+       /***** EXCLUSIVE SECTION END *****/
+       return error;
+}
+
+/**
+ * tomoyo_check_single_path_acl - Check permission for single path operation.
+ *
+ * @domain:   Pointer to "struct tomoyo_domain_info".
+ * @type:     Type of operation.
+ * @filename: Filename to check.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
+                                       const u8 type,
+                                       const struct tomoyo_path_info *filename)
+{
+       if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
+               return 0;
+       return tomoyo_check_single_path_acl2(domain, filename, 1 << type, 1);
+}
+
+/**
+ * tomoyo_check_double_path_acl - Check permission for double path operation.
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @type:      Type of operation.
+ * @filename1: First filename to check.
+ * @filename2: Second filename to check.
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ */
+static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
+                                       const u8 type,
+                                       const struct tomoyo_path_info *
+                                       filename1,
+                                       const struct tomoyo_path_info *
+                                       filename2)
+{
+       struct tomoyo_acl_info *ptr;
+       const u8 perm = 1 << type;
+       int error = -EPERM;
+
+       if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
+               return 0;
+       down_read(&tomoyo_domain_acl_info_list_lock);
+       list_for_each_entry(ptr, &domain->acl_info_list, list) {
+               struct tomoyo_double_path_acl_record *acl;
+               if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+                       continue;
+               acl = container_of(ptr, struct tomoyo_double_path_acl_record,
+                                  head);
+               if (!(acl->perm & perm))
+                       continue;
+               if (!tomoyo_path_matches_pattern(filename1, acl->filename1))
+                       continue;
+               if (!tomoyo_path_matches_pattern(filename2, acl->filename2))
+                       continue;
+               error = 0;
+               break;
+       }
+       up_read(&tomoyo_domain_acl_info_list_lock);
+       return error;
+}
+
+/**
+ * tomoyo_check_single_path_permission2 - Check permission for single path operation.
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @operation: Type of operation.
+ * @filename:  Filename to check.
+ * @mode:      Access control mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
+                                               const domain, u8 operation,
+                                               const struct tomoyo_path_info *
+                                               filename, const u8 mode)
+{
+       const char *msg;
+       int error;
+       const bool is_enforce = (mode == 3);
+
+       if (!mode)
+               return 0;
+ next:
+       error = tomoyo_check_single_path_acl(domain, operation, filename);
+       msg = tomoyo_sp2keyword(operation);
+       if (!error)
+               goto ok;
+       if (tomoyo_verbose_mode(domain))
+               printk(KERN_WARNING "TOMOYO-%s: Access '%s %s' denied for %s\n",
+                      tomoyo_get_msg(is_enforce), msg, filename->name,
+                      tomoyo_get_last_name(domain));
+       if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
+               const char *name = tomoyo_get_file_pattern(filename)->name;
+               tomoyo_update_single_path_acl(operation, name, domain, false);
+       }
+       if (!is_enforce)
+               error = 0;
+ ok:
+       /*
+        * Since "allow_truncate" doesn't imply "allow_rewrite" permission,
+        * we need to check "allow_rewrite" permission if the filename is
+        * specified by "deny_rewrite" keyword.
+        */
+       if (!error && operation == TOMOYO_TYPE_TRUNCATE_ACL &&
+           tomoyo_is_no_rewrite_file(filename)) {
+               operation = TOMOYO_TYPE_REWRITE_ACL;
+               goto next;
+       }
+       return error;
+}
+
+/**
+ * tomoyo_check_file_perm - Check permission for sysctl()'s "read" and "write".
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @filename:  Filename to check.
+ * @perm:      Mode ("read" or "write" or "read/write").
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_file_perm(struct tomoyo_domain_info *domain,
+                          const char *filename, const u8 perm)
+{
+       struct tomoyo_path_info name;
+       const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+
+       if (!mode)
+               return 0;
+       name.name = filename;
+       tomoyo_fill_path_info(&name);
+       return tomoyo_check_file_perm2(domain, &name, perm, "sysctl", mode);
+}
+
+/**
+ * tomoyo_check_exec_perm - Check permission for "execute".
+ *
+ * @domain:   Pointer to "struct tomoyo_domain_info".
+ * @filename: Check permission for "execute".
+ * @tmp:      Buffer for temporary use.
+ *
+ * Returns 0 on success, negativevalue otherwise.
+ */
+int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
+                          const struct tomoyo_path_info *filename,
+                          struct tomoyo_page_buffer *tmp)
+{
+       const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+
+       if (!mode)
+               return 0;
+       return tomoyo_check_file_perm2(domain, filename, 1, "do_execve", mode);
+}
+
+/**
+ * tomoyo_check_open_permission - Check permission for "read" and "write".
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @path:   Pointer to "struct path".
+ * @flag:   Flags for open().
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
+                                struct path *path, const int flag)
+{
+       const u8 acc_mode = ACC_MODE(flag);
+       int error = -ENOMEM;
+       struct tomoyo_path_info *buf;
+       const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+       const bool is_enforce = (mode == 3);
+
+       if (!mode || !path->mnt)
+               return 0;
+       if (acc_mode == 0)
+               return 0;
+       if (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode))
+               /*
+                * I don't check directories here because mkdir() and rmdir()
+                * don't call me.
+                */
+               return 0;
+       buf = tomoyo_get_path(path);
+       if (!buf)
+               goto out;
+       error = 0;
+       /*
+        * If the filename is specified by "deny_rewrite" keyword,
+        * we need to check "allow_rewrite" permission when the filename is not
+        * opened for append mode or the filename is truncated at open time.
+        */
+       if ((acc_mode & MAY_WRITE) &&
+           ((flag & O_TRUNC) || !(flag & O_APPEND)) &&
+           (tomoyo_is_no_rewrite_file(buf))) {
+               error = tomoyo_check_single_path_permission2(domain,
+                                                    TOMOYO_TYPE_REWRITE_ACL,
+                                                            buf, mode);
+       }
+       if (!error)
+               error = tomoyo_check_file_perm2(domain, buf, acc_mode, "open",
+                                               mode);
+       if (!error && (flag & O_TRUNC))
+               error = tomoyo_check_single_path_permission2(domain,
+                                                    TOMOYO_TYPE_TRUNCATE_ACL,
+                                                            buf, mode);
+ out:
+       tomoyo_free(buf);
+       if (!is_enforce)
+               error = 0;
+       return error;
+}
+
+/**
+ * tomoyo_check_1path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate" and "symlink".
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @operation: Type of operation.
+ * @path:      Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
+                           const u8 operation, struct path *path)
+{
+       int error = -ENOMEM;
+       struct tomoyo_path_info *buf;
+       const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+       const bool is_enforce = (mode == 3);
+
+       if (!mode || !path->mnt)
+               return 0;
+       buf = tomoyo_get_path(path);
+       if (!buf)
+               goto out;
+       switch (operation) {
+       case TOMOYO_TYPE_MKDIR_ACL:
+       case TOMOYO_TYPE_RMDIR_ACL:
+               if (!buf->is_dir) {
+                       /*
+                        * tomoyo_get_path() reserves space for appending "/."
+                        */
+                       strcat((char *) buf->name, "/");
+                       tomoyo_fill_path_info(buf);
+               }
+       }
+       error = tomoyo_check_single_path_permission2(domain, operation, buf,
+                                                    mode);
+ out:
+       tomoyo_free(buf);
+       if (!is_enforce)
+               error = 0;
+       return error;
+}
+
+/**
+ * tomoyo_check_rewrite_permission - Check permission for "rewrite".
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @filp: Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
+                                   struct file *filp)
+{
+       int error = -ENOMEM;
+       const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+       const bool is_enforce = (mode == 3);
+       struct tomoyo_path_info *buf;
+
+       if (!mode || !filp->f_path.mnt)
+               return 0;
+       buf = tomoyo_get_path(&filp->f_path);
+       if (!buf)
+               goto out;
+       if (!tomoyo_is_no_rewrite_file(buf)) {
+               error = 0;
+               goto out;
+       }
+       error = tomoyo_check_single_path_permission2(domain,
+                                                    TOMOYO_TYPE_REWRITE_ACL,
+                                                    buf, mode);
+ out:
+       tomoyo_free(buf);
+       if (!is_enforce)
+               error = 0;
+       return error;
+}
+
+/**
+ * tomoyo_check_2path_perm - Check permission for "rename" and "link".
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @operation: Type of operation.
+ * @path1:      Pointer to "struct path".
+ * @path2:      Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
+                           const u8 operation, struct path *path1,
+                           struct path *path2)
+{
+       int error = -ENOMEM;
+       struct tomoyo_path_info *buf1, *buf2;
+       const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+       const bool is_enforce = (mode == 3);
+       const char *msg;
+
+       if (!mode || !path1->mnt || !path2->mnt)
+               return 0;
+       buf1 = tomoyo_get_path(path1);
+       buf2 = tomoyo_get_path(path2);
+       if (!buf1 || !buf2)
+               goto out;
+       {
+               struct dentry *dentry = path1->dentry;
+               if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
+                       /*
+                        * tomoyo_get_path() reserves space for appending "/."
+                        */
+                       if (!buf1->is_dir) {
+                               strcat((char *) buf1->name, "/");
+                               tomoyo_fill_path_info(buf1);
+                       }
+                       if (!buf2->is_dir) {
+                               strcat((char *) buf2->name, "/");
+                               tomoyo_fill_path_info(buf2);
+                       }
+               }
+       }
+       error = tomoyo_check_double_path_acl(domain, operation, buf1, buf2);
+       msg = tomoyo_dp2keyword(operation);
+       if (!error)
+               goto out;
+       if (tomoyo_verbose_mode(domain))
+               printk(KERN_WARNING "TOMOYO-%s: Access '%s %s %s' "
+                      "denied for %s\n", tomoyo_get_msg(is_enforce),
+                      msg, buf1->name, buf2->name,
+                      tomoyo_get_last_name(domain));
+       if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
+               const char *name1 = tomoyo_get_file_pattern(buf1)->name;
+               const char *name2 = tomoyo_get_file_pattern(buf2)->name;
+               tomoyo_update_double_path_acl(operation, name1, name2, domain,
+                                             false);
+       }
+ out:
+       tomoyo_free(buf1);
+       tomoyo_free(buf2);
+       if (!is_enforce)
+               error = 0;
+       return error;
+}
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
new file mode 100644 (file)
index 0000000..d47f16b
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * security/tomoyo/realpath.c
+ *
+ * Get the canonicalized absolute pathnames. The basis for TOMOYO.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/mount.h>
+#include <linux/mnt_namespace.h>
+#include "common.h"
+#include "realpath.h"
+
+/**
+ * tomoyo_encode: Convert binary string to ascii string.
+ *
+ * @buffer:  Buffer for ASCII string.
+ * @buflen:  Size of @buffer.
+ * @str:     Binary string.
+ *
+ * Returns 0 on success, -ENOMEM otherwise.
+ */
+int tomoyo_encode(char *buffer, int buflen, const char *str)
+{
+       while (1) {
+               const unsigned char c = *(unsigned char *) str++;
+
+               if (tomoyo_is_valid(c)) {
+                       if (--buflen <= 0)
+                               break;
+                       *buffer++ = (char) c;
+                       if (c != '\\')
+                               continue;
+                       if (--buflen <= 0)
+                               break;
+                       *buffer++ = (char) c;
+                       continue;
+               }
+               if (!c) {
+                       if (--buflen <= 0)
+                               break;
+                       *buffer = '\0';
+                       return 0;
+               }
+               buflen -= 4;
+               if (buflen <= 0)
+                       break;
+               *buffer++ = '\\';
+               *buffer++ = (c >> 6) + '0';
+               *buffer++ = ((c >> 3) & 7) + '0';
+               *buffer++ = (c & 7) + '0';
+       }
+       return -ENOMEM;
+}
+
+/**
+ * tomoyo_realpath_from_path2 - Returns realpath(3) of the given dentry but ignores chroot'ed root.
+ *
+ * @path:        Pointer to "struct path".
+ * @newname:     Pointer to buffer to return value in.
+ * @newname_len: Size of @newname.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * If dentry is a directory, trailing '/' is appended.
+ * Characters out of 0x20 < c < 0x7F range are converted to
+ * \ooo style octal string.
+ * Character \ is converted to \\ string.
+ */
+int tomoyo_realpath_from_path2(struct path *path, char *newname,
+                              int newname_len)
+{
+       int error = -ENOMEM;
+       struct dentry *dentry = path->dentry;
+       char *sp;
+
+       if (!dentry || !path->mnt || !newname || newname_len <= 2048)
+               return -EINVAL;
+       if (dentry->d_op && dentry->d_op->d_dname) {
+               /* For "socket:[\$]" and "pipe:[\$]". */
+               static const int offset = 1536;
+               sp = dentry->d_op->d_dname(dentry, newname + offset,
+                                          newname_len - offset);
+       } else {
+               /* Taken from d_namespace_path(). */
+               struct path root;
+               struct path ns_root = { };
+               struct path tmp;
+
+               read_lock(&current->fs->lock);
+               root = current->fs->root;
+               path_get(&root);
+               read_unlock(&current->fs->lock);
+               spin_lock(&vfsmount_lock);
+               if (root.mnt && root.mnt->mnt_ns)
+                       ns_root.mnt = mntget(root.mnt->mnt_ns->root);
+               if (ns_root.mnt)
+                       ns_root.dentry = dget(ns_root.mnt->mnt_root);
+               spin_unlock(&vfsmount_lock);
+               spin_lock(&dcache_lock);
+               tmp = ns_root;
+               sp = __d_path(path, &tmp, newname, newname_len);
+               spin_unlock(&dcache_lock);
+               path_put(&root);
+               path_put(&ns_root);
+       }
+       if (IS_ERR(sp))
+               error = PTR_ERR(sp);
+       else
+               error = tomoyo_encode(newname, sp - newname, sp);
+       /* Append trailing '/' if dentry is a directory. */
+       if (!error && dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)
+           && *newname) {
+               sp = newname + strlen(newname);
+               if (*(sp - 1) != '/') {
+                       if (sp < newname + newname_len - 4) {
+                               *sp++ = '/';
+                               *sp = '\0';
+                       } else {
+                               error = -ENOMEM;
+                       }
+               }
+       }
+       if (error)
+               printk(KERN_WARNING "tomoyo_realpath: Pathname too long.\n");
+       return error;
+}
+
+/**
+ * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns the realpath of the given @path on success, NULL otherwise.
+ *
+ * These functions use tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if these functions didn't return NULL.
+ */
+char *tomoyo_realpath_from_path(struct path *path)
+{
+       char *buf = tomoyo_alloc(sizeof(struct tomoyo_page_buffer));
+
+       BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer)
+                    <= TOMOYO_MAX_PATHNAME_LEN - 1);
+       if (!buf)
+               return NULL;
+       if (tomoyo_realpath_from_path2(path, buf,
+                                      TOMOYO_MAX_PATHNAME_LEN - 1) == 0)
+               return buf;
+       tomoyo_free(buf);
+       return NULL;
+}
+
+/**
+ * tomoyo_realpath - Get realpath of a pathname.
+ *
+ * @pathname: The pathname to solve.
+ *
+ * Returns the realpath of @pathname on success, NULL otherwise.
+ */
+char *tomoyo_realpath(const char *pathname)
+{
+       struct nameidata nd;
+
+       if (pathname && path_lookup(pathname, LOOKUP_FOLLOW, &nd) == 0) {
+               char *buf = tomoyo_realpath_from_path(&nd.path);
+               path_put(&nd.path);
+               return buf;
+       }
+       return NULL;
+}
+
+/**
+ * tomoyo_realpath_nofollow - Get realpath of a pathname.
+ *
+ * @pathname: The pathname to solve.
+ *
+ * Returns the realpath of @pathname on success, NULL otherwise.
+ */
+char *tomoyo_realpath_nofollow(const char *pathname)
+{
+       struct nameidata nd;
+
+       if (pathname && path_lookup(pathname, 0, &nd) == 0) {
+               char *buf = tomoyo_realpath_from_path(&nd.path);
+               path_put(&nd.path);
+               return buf;
+       }
+       return NULL;
+}
+
+/* Memory allocated for non-string data. */
+static unsigned int tomoyo_allocated_memory_for_elements;
+/* Quota for holding non-string data. */
+static unsigned int tomoyo_quota_for_elements;
+
+/**
+ * tomoyo_alloc_element - Allocate permanent memory for structures.
+ *
+ * @size: Size in bytes.
+ *
+ * Returns pointer to allocated memory on success, NULL otherwise.
+ *
+ * Memory has to be zeroed.
+ * The RAM is chunked, so NEVER try to kfree() the returned pointer.
+ */
+void *tomoyo_alloc_element(const unsigned int size)
+{
+       static char *buf;
+       static DEFINE_MUTEX(lock);
+       static unsigned int buf_used_len = PATH_MAX;
+       char *ptr = NULL;
+       /*Assumes sizeof(void *) >= sizeof(long) is true. */
+       const unsigned int word_aligned_size
+               = roundup(size, max(sizeof(void *), sizeof(long)));
+       if (word_aligned_size > PATH_MAX)
+               return NULL;
+       /***** EXCLUSIVE SECTION START *****/
+       mutex_lock(&lock);
+       if (buf_used_len + word_aligned_size > PATH_MAX) {
+               if (!tomoyo_quota_for_elements ||
+                   tomoyo_allocated_memory_for_elements
+                   + PATH_MAX <= tomoyo_quota_for_elements)
+                       ptr = kzalloc(PATH_MAX, GFP_KERNEL);
+               if (!ptr) {
+                       printk(KERN_WARNING "ERROR: Out of memory "
+                              "for tomoyo_alloc_element().\n");
+                       if (!tomoyo_policy_loaded)
+                               panic("MAC Initialization failed.\n");
+               } else {
+                       buf = ptr;
+                       tomoyo_allocated_memory_for_elements += PATH_MAX;
+                       buf_used_len = word_aligned_size;
+                       ptr = buf;
+               }
+       } else if (word_aligned_size) {
+               int i;
+               ptr = buf + buf_used_len;
+               buf_used_len += word_aligned_size;
+               for (i = 0; i < word_aligned_size; i++) {
+                       if (!ptr[i])
+                               continue;
+                       printk(KERN_ERR "WARNING: Reserved memory was tainted! "
+                              "The system might go wrong.\n");
+                       ptr[i] = '\0';
+               }
+       }
+       mutex_unlock(&lock);
+       /***** EXCLUSIVE SECTION END *****/
+       return ptr;
+}
+
+/* Memory allocated for string data in bytes. */
+static unsigned int tomoyo_allocated_memory_for_savename;
+/* Quota for holding string data in bytes. */
+static unsigned int tomoyo_quota_for_savename;
+
+/*
+ * TOMOYO uses this hash only when appending a string into the string
+ * table. Frequency of appending strings is very low. So we don't need
+ * large (e.g. 64k) hash size. 256 will be sufficient.
+ */
+#define TOMOYO_MAX_HASH 256
+
+/* Structure for string data. */
+struct tomoyo_name_entry {
+       struct list_head list;
+       struct tomoyo_path_info entry;
+};
+
+/* Structure for available memory region. */
+struct tomoyo_free_memory_block_list {
+       struct list_head list;
+       char *ptr;             /* Pointer to a free area. */
+       int len;               /* Length of the area.     */
+};
+
+/*
+ * The list for "struct tomoyo_name_entry".
+ *
+ * This list is updated only inside tomoyo_save_name(), thus
+ * no global mutex exists.
+ */
+static struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
+
+/**
+ * tomoyo_save_name - Allocate permanent memory for string data.
+ *
+ * @name: The string to store into the permernent memory.
+ *
+ * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
+ *
+ * The RAM is shared, so NEVER try to modify or kfree() the returned name.
+ */
+const struct tomoyo_path_info *tomoyo_save_name(const char *name)
+{
+       static LIST_HEAD(fmb_list);
+       static DEFINE_MUTEX(lock);
+       struct tomoyo_name_entry *ptr;
+       unsigned int hash;
+       /* fmb contains available size in bytes.
+          fmb is removed from the fmb_list when fmb->len becomes 0. */
+       struct tomoyo_free_memory_block_list *fmb;
+       int len;
+       char *cp;
+
+       if (!name)
+               return NULL;
+       len = strlen(name) + 1;
+       if (len > TOMOYO_MAX_PATHNAME_LEN) {
+               printk(KERN_WARNING "ERROR: Name too long "
+                      "for tomoyo_save_name().\n");
+               return NULL;
+       }
+       hash = full_name_hash((const unsigned char *) name, len - 1);
+       /***** EXCLUSIVE SECTION START *****/
+       mutex_lock(&lock);
+       list_for_each_entry(ptr, &tomoyo_name_list[hash % TOMOYO_MAX_HASH],
+                            list) {
+               if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name))
+                       goto out;
+       }
+       list_for_each_entry(fmb, &fmb_list, list) {
+               if (len <= fmb->len)
+                       goto ready;
+       }
+       if (!tomoyo_quota_for_savename ||
+           tomoyo_allocated_memory_for_savename + PATH_MAX
+           <= tomoyo_quota_for_savename)
+               cp = kzalloc(PATH_MAX, GFP_KERNEL);
+       else
+               cp = NULL;
+       fmb = kzalloc(sizeof(*fmb), GFP_KERNEL);
+       if (!cp || !fmb) {
+               kfree(cp);
+               kfree(fmb);
+               printk(KERN_WARNING "ERROR: Out of memory "
+                      "for tomoyo_save_name().\n");
+               if (!tomoyo_policy_loaded)
+                       panic("MAC Initialization failed.\n");
+               ptr = NULL;
+               goto out;
+       }
+       tomoyo_allocated_memory_for_savename += PATH_MAX;
+       list_add(&fmb->list, &fmb_list);
+       fmb->ptr = cp;
+       fmb->len = PATH_MAX;
+ ready:
+       ptr = tomoyo_alloc_element(sizeof(*ptr));
+       if (!ptr)
+               goto out;
+       ptr->entry.name = fmb->ptr;
+       memmove(fmb->ptr, name, len);
+       tomoyo_fill_path_info(&ptr->entry);
+       fmb->ptr += len;
+       fmb->len -= len;
+       list_add_tail(&ptr->list, &tomoyo_name_list[hash % TOMOYO_MAX_HASH]);
+       if (fmb->len == 0) {
+               list_del(&fmb->list);
+               kfree(fmb);
+       }
+ out:
+       mutex_unlock(&lock);
+       /***** EXCLUSIVE SECTION END *****/
+       return ptr ? &ptr->entry : NULL;
+}
+
+/**
+ * tomoyo_realpath_init - Initialize realpath related code.
+ */
+void __init tomoyo_realpath_init(void)
+{
+       int i;
+
+       BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX);
+       for (i = 0; i < TOMOYO_MAX_HASH; i++)
+               INIT_LIST_HEAD(&tomoyo_name_list[i]);
+       INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
+       tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME);
+       list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
+       down_read(&tomoyo_domain_list_lock);
+       if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
+               panic("Can't register tomoyo_kernel_domain");
+       up_read(&tomoyo_domain_list_lock);
+}
+
+/* Memory allocated for temporary purpose. */
+static atomic_t tomoyo_dynamic_memory_size;
+
+/**
+ * tomoyo_alloc - Allocate memory for temporary purpose.
+ *
+ * @size: Size in bytes.
+ *
+ * Returns pointer to allocated memory on success, NULL otherwise.
+ */
+void *tomoyo_alloc(const size_t size)
+{
+       void *p = kzalloc(size, GFP_KERNEL);
+       if (p)
+               atomic_add(ksize(p), &tomoyo_dynamic_memory_size);
+       return p;
+}
+
+/**
+ * tomoyo_free - Release memory allocated by tomoyo_alloc().
+ *
+ * @p: Pointer returned by tomoyo_alloc(). May be NULL.
+ *
+ * Returns nothing.
+ */
+void tomoyo_free(const void *p)
+{
+       if (p) {
+               atomic_sub(ksize(p), &tomoyo_dynamic_memory_size);
+               kfree(p);
+       }
+}
+
+/**
+ * tomoyo_read_memory_counter - Check for memory usage in bytes.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns memory usage.
+ */
+int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head)
+{
+       if (!head->read_eof) {
+               const unsigned int shared
+                       = tomoyo_allocated_memory_for_savename;
+               const unsigned int private
+                       = tomoyo_allocated_memory_for_elements;
+               const unsigned int dynamic
+                       = atomic_read(&tomoyo_dynamic_memory_size);
+               char buffer[64];
+
+               memset(buffer, 0, sizeof(buffer));
+               if (tomoyo_quota_for_savename)
+                       snprintf(buffer, sizeof(buffer) - 1,
+                                "   (Quota: %10u)",
+                                tomoyo_quota_for_savename);
+               else
+                       buffer[0] = '\0';
+               tomoyo_io_printf(head, "Shared:  %10u%s\n", shared, buffer);
+               if (tomoyo_quota_for_elements)
+                       snprintf(buffer, sizeof(buffer) - 1,
+                                "   (Quota: %10u)",
+                                tomoyo_quota_for_elements);
+               else
+                       buffer[0] = '\0';
+               tomoyo_io_printf(head, "Private: %10u%s\n", private, buffer);
+               tomoyo_io_printf(head, "Dynamic: %10u\n", dynamic);
+               tomoyo_io_printf(head, "Total:   %10u\n",
+                                shared + private + dynamic);
+               head->read_eof = true;
+       }
+       return 0;
+}
+
+/**
+ * tomoyo_write_memory_quota - Set memory quota.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head)
+{
+       char *data = head->write_buf;
+       unsigned int size;
+
+       if (sscanf(data, "Shared: %u", &size) == 1)
+               tomoyo_quota_for_savename = size;
+       else if (sscanf(data, "Private: %u", &size) == 1)
+               tomoyo_quota_for_elements = size;
+       return 0;
+}
diff --git a/security/tomoyo/realpath.h b/security/tomoyo/realpath.h
new file mode 100644 (file)
index 0000000..7ec9fc9
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * security/tomoyo/realpath.h
+ *
+ * Get the canonicalized absolute pathnames. The basis for TOMOYO.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#ifndef _SECURITY_TOMOYO_REALPATH_H
+#define _SECURITY_TOMOYO_REALPATH_H
+
+struct path;
+struct tomoyo_path_info;
+struct tomoyo_io_buffer;
+
+/* Convert binary string to ascii string. */
+int tomoyo_encode(char *buffer, int buflen, const char *str);
+
+/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */
+int tomoyo_realpath_from_path2(struct path *path, char *newname,
+                              int newname_len);
+
+/*
+ * Returns realpath(3) of the given pathname but ignores chroot'ed root.
+ * These functions use tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if these functions didn't return NULL.
+ */
+char *tomoyo_realpath(const char *pathname);
+/*
+ * Same with tomoyo_realpath() except that it doesn't follow the final symlink.
+ */
+char *tomoyo_realpath_nofollow(const char *pathname);
+/* Same with tomoyo_realpath() except that the pathname is already solved. */
+char *tomoyo_realpath_from_path(struct path *path);
+
+/*
+ * Allocate memory for ACL entry.
+ * The RAM is chunked, so NEVER try to kfree() the returned pointer.
+ */
+void *tomoyo_alloc_element(const unsigned int size);
+
+/*
+ * Keep the given name on the RAM.
+ * The RAM is shared, so NEVER try to modify or kfree() the returned name.
+ */
+const struct tomoyo_path_info *tomoyo_save_name(const char *name);
+
+/* Allocate memory for temporary use (e.g. permission checks). */
+void *tomoyo_alloc(const size_t size);
+
+/* Free memory allocated by tomoyo_alloc(). */
+void tomoyo_free(const void *p);
+
+/* Check for memory usage. */
+int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head);
+
+/* Set memory quota. */
+int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head);
+
+/* Initialize realpath related code. */
+void __init tomoyo_realpath_init(void);
+
+#endif /* !defined(_SECURITY_TOMOYO_REALPATH_H) */
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
new file mode 100644 (file)
index 0000000..3eeeae1
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * security/tomoyo/tomoyo.c
+ *
+ * LSM hooks for TOMOYO Linux.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#include <linux/security.h>
+#include "common.h"
+#include "tomoyo.h"
+#include "realpath.h"
+
+static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
+                              gfp_t gfp)
+{
+       /*
+        * Since "struct tomoyo_domain_info *" is a sharable pointer,
+        * we don't need to duplicate.
+        */
+       new->security = old->security;
+       return 0;
+}
+
+static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
+{
+       /*
+        * Do only if this function is called for the first time of an execve
+        * operation.
+        */
+       if (bprm->cred_prepared)
+               return 0;
+       /*
+        * Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
+        * for the first time.
+        */
+       if (!tomoyo_policy_loaded)
+               tomoyo_load_policy(bprm->filename);
+       /*
+        * Tell tomoyo_bprm_check_security() is called for the first time of an
+        * execve operation.
+        */
+       bprm->cred->security = NULL;
+       return 0;
+}
+
+static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
+{
+       struct tomoyo_domain_info *domain = bprm->cred->security;
+
+       /*
+        * Execute permission is checked against pathname passed to do_execve()
+        * using current domain.
+        */
+       if (!domain) {
+               struct tomoyo_domain_info *next_domain = NULL;
+               int retval = tomoyo_find_next_domain(bprm, &next_domain);
+
+               if (!retval)
+                       bprm->cred->security = next_domain;
+               return retval;
+       }
+       /*
+        * Read permission is checked against interpreters using next domain.
+        * '1' is the result of open_to_namei_flags(O_RDONLY).
+        */
+       return tomoyo_check_open_permission(domain, &bprm->file->f_path, 1);
+}
+
+#ifdef CONFIG_SYSCTL
+
+static int tomoyo_prepend(char **buffer, int *buflen, const char *str)
+{
+       int namelen = strlen(str);
+
+       if (*buflen < namelen)
+               return -ENOMEM;
+       *buflen -= namelen;
+       *buffer -= namelen;
+       memcpy(*buffer, str, namelen);
+       return 0;
+}
+
+/**
+ * tomoyo_sysctl_path - return the realpath of a ctl_table.
+ * @table: pointer to "struct ctl_table".
+ *
+ * Returns realpath(3) of the @table on success.
+ * Returns NULL on failure.
+ *
+ * This function uses tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if this function didn't return NULL.
+ */
+static char *tomoyo_sysctl_path(struct ctl_table *table)
+{
+       int buflen = TOMOYO_MAX_PATHNAME_LEN;
+       char *buf = tomoyo_alloc(buflen);
+       char *end = buf + buflen;
+       int error = -ENOMEM;
+
+       if (!buf)
+               return NULL;
+
+       *--end = '\0';
+       buflen--;
+       while (table) {
+               char num[32];
+               const char *sp = table->procname;
+
+               if (!sp) {
+                       memset(num, 0, sizeof(num));
+                       snprintf(num, sizeof(num) - 1, "=%d=", table->ctl_name);
+                       sp = num;
+               }
+               if (tomoyo_prepend(&end, &buflen, sp) ||
+                   tomoyo_prepend(&end, &buflen, "/"))
+                       goto out;
+               table = table->parent;
+       }
+       if (tomoyo_prepend(&end, &buflen, "/proc/sys"))
+               goto out;
+       error = tomoyo_encode(buf, end - buf, end);
+ out:
+       if (!error)
+               return buf;
+       tomoyo_free(buf);
+       return NULL;
+}
+
+static int tomoyo_sysctl(struct ctl_table *table, int op)
+{
+       int error;
+       char *name;
+
+       op &= MAY_READ | MAY_WRITE;
+       if (!op)
+               return 0;
+       name = tomoyo_sysctl_path(table);
+       if (!name)
+               return -ENOMEM;
+       error = tomoyo_check_file_perm(tomoyo_domain(), name, op);
+       tomoyo_free(name);
+       return error;
+}
+#endif
+
+static int tomoyo_path_truncate(struct path *path, loff_t length,
+                               unsigned int time_attrs)
+{
+       return tomoyo_check_1path_perm(tomoyo_domain(),
+                                      TOMOYO_TYPE_TRUNCATE_ACL,
+                                      path);
+}
+
+static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)
+{
+       struct path path = { parent->mnt, dentry };
+       return tomoyo_check_1path_perm(tomoyo_domain(),
+                                      TOMOYO_TYPE_UNLINK_ACL,
+                                      &path);
+}
+
+static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
+                            int mode)
+{
+       struct path path = { parent->mnt, dentry };
+       return tomoyo_check_1path_perm(tomoyo_domain(),
+                                      TOMOYO_TYPE_MKDIR_ACL,
+                                      &path);
+}
+
+static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)
+{
+       struct path path = { parent->mnt, dentry };
+       return tomoyo_check_1path_perm(tomoyo_domain(),
+                                      TOMOYO_TYPE_RMDIR_ACL,
+                                      &path);
+}
+
+static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,
+                              const char *old_name)
+{
+       struct path path = { parent->mnt, dentry };
+       return tomoyo_check_1path_perm(tomoyo_domain(),
+                                      TOMOYO_TYPE_SYMLINK_ACL,
+                                      &path);
+}
+
+static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
+                            int mode, unsigned int dev)
+{
+       struct path path = { parent->mnt, dentry };
+       int type = TOMOYO_TYPE_CREATE_ACL;
+
+       switch (mode & S_IFMT) {
+       case S_IFCHR:
+               type = TOMOYO_TYPE_MKCHAR_ACL;
+               break;
+       case S_IFBLK:
+               type = TOMOYO_TYPE_MKBLOCK_ACL;
+               break;
+       case S_IFIFO:
+               type = TOMOYO_TYPE_MKFIFO_ACL;
+               break;
+       case S_IFSOCK:
+               type = TOMOYO_TYPE_MKSOCK_ACL;
+               break;
+       }
+       return tomoyo_check_1path_perm(tomoyo_domain(),
+                                      type, &path);
+}
+
+static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
+                           struct dentry *new_dentry)
+{
+       struct path path1 = { new_dir->mnt, old_dentry };
+       struct path path2 = { new_dir->mnt, new_dentry };
+       return tomoyo_check_2path_perm(tomoyo_domain(),
+                                      TOMOYO_TYPE_LINK_ACL,
+                                      &path1, &path2);
+}
+
+static int tomoyo_path_rename(struct path *old_parent,
+                             struct dentry *old_dentry,
+                             struct path *new_parent,
+                             struct dentry *new_dentry)
+{
+       struct path path1 = { old_parent->mnt, old_dentry };
+       struct path path2 = { new_parent->mnt, new_dentry };
+       return tomoyo_check_2path_perm(tomoyo_domain(),
+                                      TOMOYO_TYPE_RENAME_ACL,
+                                      &path1, &path2);
+}
+
+static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
+                            unsigned long arg)
+{
+       if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))
+               return tomoyo_check_rewrite_permission(tomoyo_domain(), file);
+       return 0;
+}
+
+static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
+{
+       int flags = f->f_flags;
+
+       if ((flags + 1) & O_ACCMODE)
+               flags++;
+       flags |= f->f_flags & (O_APPEND | O_TRUNC);
+       /* Don't check read permission here if called from do_execve(). */
+       if (current->in_execve)
+               return 0;
+       return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
+}
+
+static struct security_operations tomoyo_security_ops = {
+       .name                = "tomoyo",
+       .cred_prepare        = tomoyo_cred_prepare,
+       .bprm_set_creds      = tomoyo_bprm_set_creds,
+       .bprm_check_security = tomoyo_bprm_check_security,
+#ifdef CONFIG_SYSCTL
+       .sysctl              = tomoyo_sysctl,
+#endif
+       .file_fcntl          = tomoyo_file_fcntl,
+       .dentry_open         = tomoyo_dentry_open,
+       .path_truncate       = tomoyo_path_truncate,
+       .path_unlink         = tomoyo_path_unlink,
+       .path_mkdir          = tomoyo_path_mkdir,
+       .path_rmdir          = tomoyo_path_rmdir,
+       .path_symlink        = tomoyo_path_symlink,
+       .path_mknod          = tomoyo_path_mknod,
+       .path_link           = tomoyo_path_link,
+       .path_rename         = tomoyo_path_rename,
+};
+
+static int __init tomoyo_init(void)
+{
+       struct cred *cred = (struct cred *) current_cred();
+
+       if (!security_module_enable(&tomoyo_security_ops))
+               return 0;
+       /* register ourselves with the security framework */
+       if (register_security(&tomoyo_security_ops))
+               panic("Failure registering TOMOYO Linux");
+       printk(KERN_INFO "TOMOYO Linux initialized\n");
+       cred->security = &tomoyo_kernel_domain;
+       tomoyo_realpath_init();
+       return 0;
+}
+
+security_initcall(tomoyo_init);
diff --git a/security/tomoyo/tomoyo.h b/security/tomoyo/tomoyo.h
new file mode 100644 (file)
index 0000000..a0c8f6e
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * security/tomoyo/tomoyo.h
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#ifndef _SECURITY_TOMOYO_TOMOYO_H
+#define _SECURITY_TOMOYO_TOMOYO_H
+
+struct tomoyo_path_info;
+struct path;
+struct inode;
+struct linux_binprm;
+struct pt_regs;
+struct tomoyo_page_buffer;
+
+int tomoyo_check_file_perm(struct tomoyo_domain_info *domain,
+                          const char *filename, const u8 perm);
+int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
+                          const struct tomoyo_path_info *filename,
+                          struct tomoyo_page_buffer *buf);
+int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
+                                struct path *path, const int flag);
+int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
+                           const u8 operation, struct path *path);
+int tomoyo_check_2path_perm(struct tomoyo_domain_info *domain,
+                           const u8 operation, struct path *path1,
+                           struct path *path2);
+int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
+                                   struct file *filp);
+int tomoyo_find_next_domain(struct linux_binprm *bprm,
+                           struct tomoyo_domain_info **next_domain);
+
+/* Index numbers for Access Controls. */
+
+#define TOMOYO_TYPE_SINGLE_PATH_ACL                 0
+#define TOMOYO_TYPE_DOUBLE_PATH_ACL                 1
+
+/* Index numbers for File Controls. */
+
+/*
+ * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set
+ * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and
+ * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set.
+ * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or
+ * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are
+ * automatically cleared if TYPE_READ_WRITE_ACL is cleared.
+ */
+
+#define TOMOYO_TYPE_READ_WRITE_ACL    0
+#define TOMOYO_TYPE_EXECUTE_ACL       1
+#define TOMOYO_TYPE_READ_ACL          2
+#define TOMOYO_TYPE_WRITE_ACL         3
+#define TOMOYO_TYPE_CREATE_ACL        4
+#define TOMOYO_TYPE_UNLINK_ACL        5
+#define TOMOYO_TYPE_MKDIR_ACL         6
+#define TOMOYO_TYPE_RMDIR_ACL         7
+#define TOMOYO_TYPE_MKFIFO_ACL        8
+#define TOMOYO_TYPE_MKSOCK_ACL        9
+#define TOMOYO_TYPE_MKBLOCK_ACL      10
+#define TOMOYO_TYPE_MKCHAR_ACL       11
+#define TOMOYO_TYPE_TRUNCATE_ACL     12
+#define TOMOYO_TYPE_SYMLINK_ACL      13
+#define TOMOYO_TYPE_REWRITE_ACL      14
+#define TOMOYO_MAX_SINGLE_PATH_OPERATION 15
+
+#define TOMOYO_TYPE_LINK_ACL         0
+#define TOMOYO_TYPE_RENAME_ACL       1
+#define TOMOYO_MAX_DOUBLE_PATH_OPERATION 2
+
+#define TOMOYO_DOMAINPOLICY          0
+#define TOMOYO_EXCEPTIONPOLICY       1
+#define TOMOYO_DOMAIN_STATUS         2
+#define TOMOYO_PROCESS_STATUS        3
+#define TOMOYO_MEMINFO               4
+#define TOMOYO_SELFDOMAIN            5
+#define TOMOYO_VERSION               6
+#define TOMOYO_PROFILE               7
+#define TOMOYO_MANAGER               8
+
+extern struct tomoyo_domain_info tomoyo_kernel_domain;
+
+static inline struct tomoyo_domain_info *tomoyo_domain(void)
+{
+       return current_cred()->security;
+}
+
+/* Caller holds tasklist_lock spinlock. */
+static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
+                                                           *task)
+{
+       /***** CRITICAL SECTION START *****/
+       const struct cred *cred = get_task_cred(task);
+       struct tomoyo_domain_info *domain = cred->security;
+
+       put_cred(cred);
+       return domain;
+       /***** CRITICAL SECTION END *****/
+}
+
+#endif /* !defined(_SECURITY_TOMOYO_TOMOYO_H) */