Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris: "Highlights for this window: - improved AVC hashing for SELinux by John Brooks and Stephen Smalley - addition of an unconfined label to Smack - Smack documentation update - TPM driver updates" * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (28 commits) lsm: copy comm before calling audit_log to avoid race in string printing tomoyo: Do not generate empty policy files tomoyo: Use if_changed when generating builtin-policy.h tomoyo: Use bin2c to generate builtin-policy.h selinux: increase avtab max buckets selinux: Use a better hash function for avtab selinux: convert avtab hash table to flex_array selinux: reconcile security_netlbl_secattr_to_sid() and mls_import_netlbl_cat() selinux: remove unnecessary pointer reassignment Smack: Updates for Smack documentation tpm/st33zp24/spi: Add missing device table for spi phy. tpm/st33zp24: Add proper wait for ordinal duration in case of irq mode smack: Fix gcc warning from unused smack_syslog_lock mutex in smackfs.c Smack: Allow an unconfined label in bringup mode Smack: getting the Smack security context of keys Smack: Assign smack_known_web as default smk_in label for kernel thread's socket tpm/tpm_infineon: Use struct dev_pm_ops for power management MAINTAINERS: Add Jason as designated reviewer for TPM tpm: Update KConfig text to include TPM2.0 FIFO chips tpm/st33zp24/dts/st33zp24-spi: Add dts documentation for st33zp24 spi phy ...
This commit is contained in:
commit
d488d3a4ce
|
@ -0,0 +1,34 @@
|
|||
* STMicroelectronics SAS. ST33ZP24 TPM SoC
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "st,st33zp24-spi".
|
||||
- spi-max-frequency: Maximum SPI frequency (<= 10000000).
|
||||
|
||||
Optional ST33ZP24 Properties:
|
||||
- interrupt-parent: phandle for the interrupt gpio controller
|
||||
- interrupts: GPIO interrupt to which the chip is connected
|
||||
- lpcpd-gpios: Output GPIO pin used for ST33ZP24 power management D1/D2 state.
|
||||
If set, power must be present when the platform is going into sleep/hibernate mode.
|
||||
|
||||
Optional SoC Specific Properties:
|
||||
- pinctrl-names: Contains only one value - "default".
|
||||
- pintctrl-0: Specifies the pin control groups used for this controller.
|
||||
|
||||
Example (for ARM-based BeagleBoard xM with ST33ZP24 on SPI4):
|
||||
|
||||
&mcspi4 {
|
||||
|
||||
status = "okay";
|
||||
|
||||
st33zp24@0 {
|
||||
|
||||
compatible = "st,st33zp24-spi";
|
||||
|
||||
spi-max-frequency = <10000000>;
|
||||
|
||||
interrupt-parent = <&gpio5>;
|
||||
interrupts = <7 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
lpcpd-gpios = <&gpio5 15 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
|
@ -33,11 +33,18 @@ The current git repository for Smack user space is:
|
|||
git://github.com/smack-team/smack.git
|
||||
|
||||
This should make and install on most modern distributions.
|
||||
There are three commands included in smackutil:
|
||||
There are five commands included in smackutil:
|
||||
|
||||
smackload - properly formats data for writing to /smack/load
|
||||
smackcipso - properly formats data for writing to /smack/cipso
|
||||
chsmack - display or set Smack extended attribute values
|
||||
smackctl - load the Smack access rules
|
||||
smackaccess - report if a process with one label has access
|
||||
to an object with another
|
||||
|
||||
These two commands are obsolete with the introduction of
|
||||
the smackfs/load2 and smackfs/cipso2 interfaces.
|
||||
|
||||
smackload - properly formats data for writing to smackfs/load
|
||||
smackcipso - properly formats data for writing to smackfs/cipso
|
||||
|
||||
In keeping with the intent of Smack, configuration data is
|
||||
minimal and not strictly required. The most important
|
||||
|
@ -47,9 +54,9 @@ of this, but it can be manually as well.
|
|||
|
||||
Add this line to /etc/fstab:
|
||||
|
||||
smackfs /smack smackfs smackfsdef=* 0 0
|
||||
smackfs /sys/fs/smackfs smackfs defaults 0 0
|
||||
|
||||
and create the /smack directory for mounting.
|
||||
The /sys/fs/smackfs directory is created by the kernel.
|
||||
|
||||
Smack uses extended attributes (xattrs) to store labels on filesystem
|
||||
objects. The attributes are stored in the extended attribute security
|
||||
|
@ -92,13 +99,13 @@ There are multiple ways to set a Smack label on a file:
|
|||
# attr -S -s SMACK64 -V "value" path
|
||||
# chsmack -a value path
|
||||
|
||||
A process can see the smack label it is running with by
|
||||
A process can see the Smack label it is running with by
|
||||
reading /proc/self/attr/current. A process with CAP_MAC_ADMIN
|
||||
can set the process smack by writing there.
|
||||
can set the process Smack by writing there.
|
||||
|
||||
Most Smack configuration is accomplished by writing to files
|
||||
in the smackfs filesystem. This pseudo-filesystem is usually
|
||||
mounted on /smack.
|
||||
in the smackfs filesystem. This pseudo-filesystem is mounted
|
||||
on /sys/fs/smackfs.
|
||||
|
||||
access
|
||||
This interface reports whether a subject with the specified
|
||||
|
@ -206,23 +213,30 @@ onlycap
|
|||
file or cleared by writing "-" to the file.
|
||||
ptrace
|
||||
This is used to define the current ptrace policy
|
||||
0 - default: this is the policy that relies on smack access rules.
|
||||
0 - default: this is the policy that relies on Smack access rules.
|
||||
For the PTRACE_READ a subject needs to have a read access on
|
||||
object. For the PTRACE_ATTACH a read-write access is required.
|
||||
1 - exact: this is the policy that limits PTRACE_ATTACH. Attach is
|
||||
only allowed when subject's and object's labels are equal.
|
||||
PTRACE_READ is not affected. Can be overriden with CAP_SYS_PTRACE.
|
||||
PTRACE_READ is not affected. Can be overridden with CAP_SYS_PTRACE.
|
||||
2 - draconian: this policy behaves like the 'exact' above with an
|
||||
exception that it can't be overriden with CAP_SYS_PTRACE.
|
||||
exception that it can't be overridden with CAP_SYS_PTRACE.
|
||||
revoke-subject
|
||||
Writing a Smack label here sets the access to '-' for all access
|
||||
rules with that subject label.
|
||||
unconfined
|
||||
If the kernel is configured with CONFIG_SECURITY_SMACK_BRINGUP
|
||||
a process with CAP_MAC_ADMIN can write a label into this interface.
|
||||
Thereafter, accesses that involve that label will be logged and
|
||||
the access permitted if it wouldn't be otherwise. Note that this
|
||||
is dangerous and can ruin the proper labeling of your system.
|
||||
It should never be used in production.
|
||||
|
||||
You can add access rules in /etc/smack/accesses. They take the form:
|
||||
|
||||
subjectlabel objectlabel access
|
||||
|
||||
access is a combination of the letters rwxa which specify the
|
||||
access is a combination of the letters rwxatb which specify the
|
||||
kind of access permitted a subject with subjectlabel on an
|
||||
object with objectlabel. If there is no rule no access is allowed.
|
||||
|
||||
|
@ -318,8 +332,9 @@ each of the subject and the object.
|
|||
|
||||
Labels
|
||||
|
||||
Smack labels are ASCII character strings, one to twenty-three characters in
|
||||
length. Single character labels using special characters, that being anything
|
||||
Smack labels are ASCII character strings. They can be up to 255 characters
|
||||
long, but keeping them to twenty-three characters is recommended.
|
||||
Single character labels using special characters, that being anything
|
||||
other than a letter or digit, are reserved for use by the Smack development
|
||||
team. Smack labels are unstructured, case sensitive, and the only operation
|
||||
ever performed on them is comparison for equality. Smack labels cannot
|
||||
|
@ -335,10 +350,9 @@ There are some predefined labels:
|
|||
? Pronounced "huh", a single question mark character.
|
||||
@ Pronounced "web", a single at sign character.
|
||||
|
||||
Every task on a Smack system is assigned a label. System tasks, such as
|
||||
init(8) and systems daemons, are run with the floor ("_") label. User tasks
|
||||
are assigned labels according to the specification found in the
|
||||
/etc/smack/user configuration file.
|
||||
Every task on a Smack system is assigned a label. The Smack label
|
||||
of a process will usually be assigned by the system initialization
|
||||
mechanism.
|
||||
|
||||
Access Rules
|
||||
|
||||
|
@ -393,6 +407,7 @@ describe access modes:
|
|||
w: indicates that write access should be granted.
|
||||
x: indicates that execute access should be granted.
|
||||
t: indicates that the rule requests transmutation.
|
||||
b: indicates that the rule should be reported for bring-up.
|
||||
|
||||
Uppercase values for the specification letters are allowed as well.
|
||||
Access mode specifications can be in any order. Examples of acceptable rules
|
||||
|
@ -402,6 +417,7 @@ are:
|
|||
Secret Unclass R
|
||||
Manager Game x
|
||||
User HR w
|
||||
Snap Crackle rwxatb
|
||||
New Old rRrRr
|
||||
Closed Off -
|
||||
|
||||
|
@ -413,7 +429,7 @@ Examples of unacceptable rules are:
|
|||
|
||||
Spaces are not allowed in labels. Since a subject always has access to files
|
||||
with the same label specifying a rule for that case is pointless. Only
|
||||
valid letters (rwxatRWXAT) and the dash ('-') character are allowed in
|
||||
valid letters (rwxatbRWXATB) and the dash ('-') character are allowed in
|
||||
access specifications. The dash is a placeholder, so "a-r" is the same
|
||||
as "ar". A lone dash is used to specify that no access should be allowed.
|
||||
|
||||
|
@ -462,16 +478,11 @@ receiver. The receiver is not required to have read access to the sender.
|
|||
Setting Access Rules
|
||||
|
||||
The configuration file /etc/smack/accesses contains the rules to be set at
|
||||
system startup. The contents are written to the special file /smack/load.
|
||||
Rules can be written to /smack/load at any time and take effect immediately.
|
||||
For any pair of subject and object labels there can be only one rule, with the
|
||||
most recently specified overriding any earlier specification.
|
||||
|
||||
The program smackload is provided to ensure data is formatted
|
||||
properly when written to /smack/load. This program reads lines
|
||||
of the form
|
||||
|
||||
subjectlabel objectlabel mode.
|
||||
system startup. The contents are written to the special file
|
||||
/sys/fs/smackfs/load2. Rules can be added at any time and take effect
|
||||
immediately. For any pair of subject and object labels there can be only
|
||||
one rule, with the most recently specified overriding any earlier
|
||||
specification.
|
||||
|
||||
Task Attribute
|
||||
|
||||
|
@ -488,7 +499,10 @@ only be changed by a process with privilege.
|
|||
|
||||
Privilege
|
||||
|
||||
A process with CAP_MAC_OVERRIDE is privileged.
|
||||
A process with CAP_MAC_OVERRIDE or CAP_MAC_ADMIN is privileged.
|
||||
CAP_MAC_OVERRIDE allows the process access to objects it would
|
||||
be denied otherwise. CAP_MAC_ADMIN allows a process to change
|
||||
Smack data, including rules and attributes.
|
||||
|
||||
Smack Networking
|
||||
|
||||
|
@ -510,14 +524,14 @@ intervention. Unlabeled packets that come into the system will be given the
|
|||
ambient label.
|
||||
|
||||
Smack requires configuration in the case where packets from a system that is
|
||||
not smack that speaks CIPSO may be encountered. Usually this will be a Trusted
|
||||
not Smack that speaks CIPSO may be encountered. Usually this will be a Trusted
|
||||
Solaris system, but there are other, less widely deployed systems out there.
|
||||
CIPSO provides 3 important values, a Domain Of Interpretation (DOI), a level,
|
||||
and a category set with each packet. The DOI is intended to identify a group
|
||||
of systems that use compatible labeling schemes, and the DOI specified on the
|
||||
smack system must match that of the remote system or packets will be
|
||||
discarded. The DOI is 3 by default. The value can be read from /smack/doi and
|
||||
can be changed by writing to /smack/doi.
|
||||
Smack system must match that of the remote system or packets will be
|
||||
discarded. The DOI is 3 by default. The value can be read from
|
||||
/sys/fs/smackfs/doi and can be changed by writing to /sys/fs/smackfs/doi.
|
||||
|
||||
The label and category set are mapped to a Smack label as defined in
|
||||
/etc/smack/cipso.
|
||||
|
@ -539,15 +553,13 @@ The ":" and "," characters are permitted in a Smack label but have no special
|
|||
meaning.
|
||||
|
||||
The mapping of Smack labels to CIPSO values is defined by writing to
|
||||
/smack/cipso. Again, the format of data written to this special file
|
||||
is highly restrictive, so the program smackcipso is provided to
|
||||
ensure the writes are done properly. This program takes mappings
|
||||
on the standard input and sends them to /smack/cipso properly.
|
||||
/sys/fs/smackfs/cipso2.
|
||||
|
||||
In addition to explicit mappings Smack supports direct CIPSO mappings. One
|
||||
CIPSO level is used to indicate that the category set passed in the packet is
|
||||
in fact an encoding of the Smack label. The level used is 250 by default. The
|
||||
value can be read from /smack/direct and changed by writing to /smack/direct.
|
||||
value can be read from /sys/fs/smackfs/direct and changed by writing to
|
||||
/sys/fs/smackfs/direct.
|
||||
|
||||
Socket Attributes
|
||||
|
||||
|
@ -565,8 +577,8 @@ sockets.
|
|||
Smack Netlabel Exceptions
|
||||
|
||||
You will often find that your labeled application has to talk to the outside,
|
||||
unlabeled world. To do this there's a special file /smack/netlabel where you can
|
||||
add some exceptions in the form of :
|
||||
unlabeled world. To do this there's a special file /sys/fs/smackfs/netlabel
|
||||
where you can add some exceptions in the form of :
|
||||
@IP1 LABEL1 or
|
||||
@IP2/MASK LABEL2
|
||||
|
||||
|
@ -574,22 +586,22 @@ It means that your application will have unlabeled access to @IP1 if it has
|
|||
write access on LABEL1, and access to the subnet @IP2/MASK if it has write
|
||||
access on LABEL2.
|
||||
|
||||
Entries in the /smack/netlabel file are matched by longest mask first, like in
|
||||
classless IPv4 routing.
|
||||
Entries in the /sys/fs/smackfs/netlabel file are matched by longest mask
|
||||
first, like in classless IPv4 routing.
|
||||
|
||||
A special label '@' and an option '-CIPSO' can be used there :
|
||||
@ means Internet, any application with any label has access to it
|
||||
-CIPSO means standard CIPSO networking
|
||||
|
||||
If you don't know what CIPSO is and don't plan to use it, you can just do :
|
||||
echo 127.0.0.1 -CIPSO > /smack/netlabel
|
||||
echo 0.0.0.0/0 @ > /smack/netlabel
|
||||
echo 127.0.0.1 -CIPSO > /sys/fs/smackfs/netlabel
|
||||
echo 0.0.0.0/0 @ > /sys/fs/smackfs/netlabel
|
||||
|
||||
If you use CIPSO on your 192.168.0.0/16 local network and need also unlabeled
|
||||
Internet access, you can have :
|
||||
echo 127.0.0.1 -CIPSO > /smack/netlabel
|
||||
echo 192.168.0.0/16 -CIPSO > /smack/netlabel
|
||||
echo 0.0.0.0/0 @ > /smack/netlabel
|
||||
echo 127.0.0.1 -CIPSO > /sys/fs/smackfs/netlabel
|
||||
echo 192.168.0.0/16 -CIPSO > /sys/fs/smackfs/netlabel
|
||||
echo 0.0.0.0/0 @ > /sys/fs/smackfs/netlabel
|
||||
|
||||
|
||||
Writing Applications for Smack
|
||||
|
@ -676,7 +688,7 @@ Smack auditing
|
|||
If you want Smack auditing of security events, you need to set CONFIG_AUDIT
|
||||
in your kernel configuration.
|
||||
By default, all denied events will be audited. You can change this behavior by
|
||||
writing a single character to the /smack/logging file :
|
||||
writing a single character to the /sys/fs/smackfs/logging file :
|
||||
0 : no logging
|
||||
1 : log denied (default)
|
||||
2 : log accepted
|
||||
|
@ -686,3 +698,20 @@ Events are logged as 'key=value' pairs, for each event you at least will get
|
|||
the subject, the object, the rights requested, the action, the kernel function
|
||||
that triggered the event, plus other pairs depending on the type of event
|
||||
audited.
|
||||
|
||||
Bringup Mode
|
||||
|
||||
Bringup mode provides logging features that can make application
|
||||
configuration and system bringup easier. Configure the kernel with
|
||||
CONFIG_SECURITY_SMACK_BRINGUP to enable these features. When bringup
|
||||
mode is enabled accesses that succeed due to rules marked with the "b"
|
||||
access mode will logged. When a new label is introduced for processes
|
||||
rules can be added aggressively, marked with the "b". The logging allows
|
||||
tracking of which rules actual get used for that label.
|
||||
|
||||
Another feature of bringup mode is the "unconfined" option. Writing
|
||||
a label to /sys/fs/smackfs/unconfined makes subjects with that label
|
||||
able to access any object, and objects with that label accessible to
|
||||
all subjects. Any access that is granted because a label is unconfined
|
||||
is logged. This feature is dangerous, as files and directories may
|
||||
be created in places they couldn't if the policy were being enforced.
|
||||
|
|
|
@ -9968,6 +9968,7 @@ F: drivers/media/pci/tw68/
|
|||
TPM DEVICE DRIVER
|
||||
M: Peter Huewe <peterhuewe@gmx.de>
|
||||
M: Marcel Selhorst <tpmdd@selhorst.net>
|
||||
R: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
|
||||
W: http://tpmdd.sourceforge.net
|
||||
L: tpmdd-devel@lists.sourceforge.net (moderated for non-subscribers)
|
||||
Q: git git://github.com/PeterHuewe/linux-tpmdd.git
|
||||
|
|
|
@ -25,13 +25,14 @@ menuconfig TCG_TPM
|
|||
if TCG_TPM
|
||||
|
||||
config TCG_TIS
|
||||
tristate "TPM Interface Specification 1.2 Interface"
|
||||
tristate "TPM Interface Specification 1.2 Interface / TPM 2.0 FIFO Interface"
|
||||
depends on X86
|
||||
---help---
|
||||
If you have a TPM security chip that is compliant with the
|
||||
TCG TIS 1.2 TPM specification say Yes and it will be accessible
|
||||
from within Linux. To compile this driver as a module, choose
|
||||
M here; the module will be called tpm_tis.
|
||||
TCG TIS 1.2 TPM specification (TPM1.2) or the TCG PTP FIFO
|
||||
specification (TPM2.0) say Yes and it will be accessible from
|
||||
within Linux. To compile this driver as a module, choose M here;
|
||||
the module will be called tpm_tis.
|
||||
|
||||
config TCG_TIS_I2C_ATMEL
|
||||
tristate "TPM Interface Specification 1.2 Interface (I2C - Atmel)"
|
||||
|
@ -100,16 +101,6 @@ config TCG_IBMVTPM
|
|||
will be accessible from within Linux. To compile this driver
|
||||
as a module, choose M here; the module will be called tpm_ibmvtpm.
|
||||
|
||||
config TCG_TIS_I2C_ST33
|
||||
tristate "TPM Interface Specification 1.2 Interface (I2C - STMicroelectronics)"
|
||||
depends on I2C
|
||||
depends on GPIOLIB
|
||||
---help---
|
||||
If you have a TPM security chip from STMicroelectronics working with
|
||||
an I2C bus say Yes and it will be accessible from within Linux.
|
||||
To compile this driver as a module, choose M here; the module will be
|
||||
called tpm_i2c_stm_st33.
|
||||
|
||||
config TCG_XEN
|
||||
tristate "XEN TPM Interface"
|
||||
depends on TCG_TPM && XEN
|
||||
|
@ -131,4 +122,5 @@ config TCG_CRB
|
|||
from within Linux. To compile this driver as a module, choose
|
||||
M here; the module will be called tpm_crb.
|
||||
|
||||
source "drivers/char/tpm/st33zp24/Kconfig"
|
||||
endif # TCG_TPM
|
||||
|
|
|
@ -20,6 +20,6 @@ obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
|
|||
obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
|
||||
obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
|
||||
obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
|
||||
obj-$(CONFIG_TCG_TIS_I2C_ST33) += tpm_i2c_stm_st33.o
|
||||
obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/
|
||||
obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o
|
||||
obj-$(CONFIG_TCG_CRB) += tpm_crb.o
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
config TCG_TIS_ST33ZP24
|
||||
tristate "STMicroelectronics TPM Interface Specification 1.2 Interface"
|
||||
depends on GPIOLIB
|
||||
---help---
|
||||
STMicroelectronics ST33ZP24 core driver. It implements the core
|
||||
TPM1.2 logic and hooks into the TPM kernel APIs. Physical layers will
|
||||
register against it.
|
||||
|
||||
To compile this driver as a module, choose m here. The module will be called
|
||||
tpm_st33zp24.
|
||||
|
||||
config TCG_TIS_ST33ZP24_I2C
|
||||
tristate "TPM 1.2 ST33ZP24 I2C support"
|
||||
depends on TCG_TIS_ST33ZP24
|
||||
depends on I2C
|
||||
---help---
|
||||
This module adds support for the STMicroelectronics TPM security chip
|
||||
ST33ZP24 with i2c interface.
|
||||
To compile this driver as a module, choose M here; the module will be
|
||||
called tpm_st33zp24_i2c.
|
||||
|
||||
config TCG_TIS_ST33ZP24_SPI
|
||||
tristate "TPM 1.2 ST33ZP24 SPI support"
|
||||
depends on TCG_TIS_ST33ZP24
|
||||
depends on SPI
|
||||
---help---
|
||||
This module adds support for the STMicroelectronics TPM security chip
|
||||
ST33ZP24 with spi interface.
|
||||
To compile this driver as a module, choose M here; the module will be
|
||||
called tpm_st33zp24_spi.
|
|
@ -0,0 +1,12 @@
|
|||
#
|
||||
# Makefile for ST33ZP24 TPM 1.2 driver
|
||||
#
|
||||
|
||||
tpm_st33zp24-objs = st33zp24.o
|
||||
obj-$(CONFIG_TCG_TIS_ST33ZP24) += tpm_st33zp24.o
|
||||
|
||||
tpm_st33zp24_i2c-objs = i2c.o
|
||||
obj-$(CONFIG_TCG_TIS_ST33ZP24_I2C) += tpm_st33zp24_i2c.o
|
||||
|
||||
tpm_st33zp24_spi-objs = spi.o
|
||||
obj-$(CONFIG_TCG_TIS_ST33ZP24_SPI) += tpm_st33zp24_spi.o
|
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24
|
||||
* Copyright (C) 2009 - 2015 STMicroelectronics
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/tpm.h>
|
||||
#include <linux/platform_data/st33zp24.h>
|
||||
|
||||
#include "st33zp24.h"
|
||||
|
||||
#define TPM_DUMMY_BYTE 0xAA
|
||||
|
||||
struct st33zp24_i2c_phy {
|
||||
struct i2c_client *client;
|
||||
u8 buf[TPM_BUFSIZE + 1];
|
||||
int io_lpcpd;
|
||||
};
|
||||
|
||||
/*
|
||||
* write8_reg
|
||||
* Send byte to the TIS register according to the ST33ZP24 I2C protocol.
|
||||
* @param: tpm_register, the tpm tis register where the data should be written
|
||||
* @param: tpm_data, the tpm_data to write inside the tpm_register
|
||||
* @param: tpm_size, The length of the data
|
||||
* @return: Returns negative errno, or else the number of bytes written.
|
||||
*/
|
||||
static int write8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size)
|
||||
{
|
||||
struct st33zp24_i2c_phy *phy = phy_id;
|
||||
|
||||
phy->buf[0] = tpm_register;
|
||||
memcpy(phy->buf + 1, tpm_data, tpm_size);
|
||||
return i2c_master_send(phy->client, phy->buf, tpm_size + 1);
|
||||
} /* write8_reg() */
|
||||
|
||||
/*
|
||||
* read8_reg
|
||||
* Recv byte from the TIS register according to the ST33ZP24 I2C protocol.
|
||||
* @param: tpm_register, the tpm tis register where the data should be read
|
||||
* @param: tpm_data, the TPM response
|
||||
* @param: tpm_size, tpm TPM response size to read.
|
||||
* @return: number of byte read successfully: should be one if success.
|
||||
*/
|
||||
static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size)
|
||||
{
|
||||
struct st33zp24_i2c_phy *phy = phy_id;
|
||||
u8 status = 0;
|
||||
u8 data;
|
||||
|
||||
data = TPM_DUMMY_BYTE;
|
||||
status = write8_reg(phy, tpm_register, &data, 1);
|
||||
if (status == 2)
|
||||
status = i2c_master_recv(phy->client, tpm_data, tpm_size);
|
||||
return status;
|
||||
} /* read8_reg() */
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_send
|
||||
* Send byte to the TIS register according to the ST33ZP24 I2C protocol.
|
||||
* @param: phy_id, the phy description
|
||||
* @param: tpm_register, the tpm tis register where the data should be written
|
||||
* @param: tpm_data, the tpm_data to write inside the tpm_register
|
||||
* @param: tpm_size, the length of the data
|
||||
* @return: number of byte written successfully: should be one if success.
|
||||
*/
|
||||
static int st33zp24_i2c_send(void *phy_id, u8 tpm_register, u8 *tpm_data,
|
||||
int tpm_size)
|
||||
{
|
||||
return write8_reg(phy_id, tpm_register | TPM_WRITE_DIRECTION, tpm_data,
|
||||
tpm_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_recv
|
||||
* Recv byte from the TIS register according to the ST33ZP24 I2C protocol.
|
||||
* @param: phy_id, the phy description
|
||||
* @param: tpm_register, the tpm tis register where the data should be read
|
||||
* @param: tpm_data, the TPM response
|
||||
* @param: tpm_size, tpm TPM response size to read.
|
||||
* @return: number of byte read successfully: should be one if success.
|
||||
*/
|
||||
static int st33zp24_i2c_recv(void *phy_id, u8 tpm_register, u8 *tpm_data,
|
||||
int tpm_size)
|
||||
{
|
||||
return read8_reg(phy_id, tpm_register, tpm_data, tpm_size);
|
||||
}
|
||||
|
||||
static const struct st33zp24_phy_ops i2c_phy_ops = {
|
||||
.send = st33zp24_i2c_send,
|
||||
.recv = st33zp24_i2c_recv,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy)
|
||||
{
|
||||
struct device_node *pp;
|
||||
struct i2c_client *client = phy->client;
|
||||
int gpio;
|
||||
int ret;
|
||||
|
||||
pp = client->dev.of_node;
|
||||
if (!pp) {
|
||||
dev_err(&client->dev, "No platform data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Get GPIO from device tree */
|
||||
gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0);
|
||||
if (gpio < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to retrieve lpcpd-gpios from dts.\n");
|
||||
phy->io_lpcpd = -1;
|
||||
/*
|
||||
* lpcpd pin is not specified. This is not an issue as
|
||||
* power management can be also managed by TPM specific
|
||||
* commands. So leave with a success status code.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
/* GPIO request and configuration */
|
||||
ret = devm_gpio_request_one(&client->dev, gpio,
|
||||
GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD");
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Failed to request lpcpd pin\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
phy->io_lpcpd = gpio;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int st33zp24_i2c_request_resources(struct i2c_client *client,
|
||||
struct st33zp24_i2c_phy *phy)
|
||||
{
|
||||
struct st33zp24_platform_data *pdata;
|
||||
int ret;
|
||||
|
||||
pdata = client->dev.platform_data;
|
||||
if (!pdata) {
|
||||
dev_err(&client->dev, "No platform data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* store for late use */
|
||||
phy->io_lpcpd = pdata->io_lpcpd;
|
||||
|
||||
if (gpio_is_valid(pdata->io_lpcpd)) {
|
||||
ret = devm_gpio_request_one(&client->dev,
|
||||
pdata->io_lpcpd, GPIOF_OUT_INIT_HIGH,
|
||||
"TPM IO_LPCPD");
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Failed to request lpcpd pin\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_probe initialize the TPM device
|
||||
* @param: client, the i2c_client drescription (TPM I2C description).
|
||||
* @param: id, the i2c_device_id struct.
|
||||
* @return: 0 in case of success.
|
||||
* -1 in other case.
|
||||
*/
|
||||
static int st33zp24_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct st33zp24_platform_data *pdata;
|
||||
struct st33zp24_i2c_phy *phy;
|
||||
|
||||
if (!client) {
|
||||
pr_info("%s: i2c client is NULL. Device not accessible.\n",
|
||||
__func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_info(&client->dev, "client not i2c capable\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
phy = devm_kzalloc(&client->dev, sizeof(struct st33zp24_i2c_phy),
|
||||
GFP_KERNEL);
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
phy->client = client;
|
||||
pdata = client->dev.platform_data;
|
||||
if (!pdata && client->dev.of_node) {
|
||||
ret = st33zp24_i2c_of_request_resources(phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (pdata) {
|
||||
ret = st33zp24_i2c_request_resources(client, phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return st33zp24_probe(phy, &i2c_phy_ops, &client->dev, client->irq,
|
||||
phy->io_lpcpd);
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_remove remove the TPM device
|
||||
* @param: client, the i2c_client description (TPM I2C description).
|
||||
* @return: 0 in case of success.
|
||||
*/
|
||||
static int st33zp24_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tpm_chip *chip = i2c_get_clientdata(client);
|
||||
|
||||
return st33zp24_remove(chip);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id st33zp24_i2c_id[] = {
|
||||
{TPM_ST33_I2C, 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st33zp24_i2c_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_st33zp24_i2c_match[] = {
|
||||
{ .compatible = "st,st33zp24-i2c", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match);
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(st33zp24_i2c_ops, st33zp24_pm_suspend,
|
||||
st33zp24_pm_resume);
|
||||
|
||||
static struct i2c_driver st33zp24_i2c_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = TPM_ST33_I2C,
|
||||
.pm = &st33zp24_i2c_ops,
|
||||
.of_match_table = of_match_ptr(of_st33zp24_i2c_match),
|
||||
},
|
||||
.probe = st33zp24_i2c_probe,
|
||||
.remove = st33zp24_i2c_remove,
|
||||
.id_table = st33zp24_i2c_id
|
||||
};
|
||||
|
||||
module_i2c_driver(st33zp24_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)");
|
||||
MODULE_DESCRIPTION("STM TPM 1.2 I2C ST33 Driver");
|
||||
MODULE_VERSION("1.3.0");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,399 @@
|
|||
/*
|
||||
* STMicroelectronics TPM SPI Linux driver for TPM ST33ZP24
|
||||
* Copyright (C) 2009 - 2015 STMicroelectronics
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/tpm.h>
|
||||
#include <linux/platform_data/st33zp24.h>
|
||||
|
||||
#include "st33zp24.h"
|
||||
|
||||
#define TPM_DATA_FIFO 0x24
|
||||
#define TPM_INTF_CAPABILITY 0x14
|
||||
|
||||
#define TPM_DUMMY_BYTE 0x00
|
||||
|
||||
#define MAX_SPI_LATENCY 15
|
||||
#define LOCALITY0 0
|
||||
|
||||
#define ST33ZP24_OK 0x5A
|
||||
#define ST33ZP24_UNDEFINED_ERR 0x80
|
||||
#define ST33ZP24_BADLOCALITY 0x81
|
||||
#define ST33ZP24_TISREGISTER_UKNOWN 0x82
|
||||
#define ST33ZP24_LOCALITY_NOT_ACTIVATED 0x83
|
||||
#define ST33ZP24_HASH_END_BEFORE_HASH_START 0x84
|
||||
#define ST33ZP24_BAD_COMMAND_ORDER 0x85
|
||||
#define ST33ZP24_INCORECT_RECEIVED_LENGTH 0x86
|
||||
#define ST33ZP24_TPM_FIFO_OVERFLOW 0x89
|
||||
#define ST33ZP24_UNEXPECTED_READ_FIFO 0x8A
|
||||
#define ST33ZP24_UNEXPECTED_WRITE_FIFO 0x8B
|
||||
#define ST33ZP24_CMDRDY_SET_WHEN_PROCESSING_HASH_END 0x90
|
||||
#define ST33ZP24_DUMMY_BYTES 0x00
|
||||
|
||||
/*
|
||||
* TPM command can be up to 2048 byte, A TPM response can be up to
|
||||
* 1024 byte.
|
||||
* Between command and response, there are latency byte (up to 15
|
||||
* usually on st33zp24 2 are enough).
|
||||
*
|
||||
* Overall when sending a command and expecting an answer we need if
|
||||
* worst case:
|
||||
* 2048 (for the TPM command) + 1024 (for the TPM answer). We need
|
||||
* some latency byte before the answer is available (max 15).
|
||||
* We have 2048 + 1024 + 15.
|
||||
*/
|
||||
#define ST33ZP24_SPI_BUFFER_SIZE (TPM_BUFSIZE + (TPM_BUFSIZE / 2) +\
|
||||
MAX_SPI_LATENCY)
|
||||
|
||||
|
||||
struct st33zp24_spi_phy {
|
||||
struct spi_device *spi_device;
|
||||
struct spi_transfer spi_xfer;
|
||||
u8 tx_buf[ST33ZP24_SPI_BUFFER_SIZE];
|
||||
u8 rx_buf[ST33ZP24_SPI_BUFFER_SIZE];
|
||||
|
||||
int io_lpcpd;
|
||||
int latency;
|
||||
};
|
||||
|
||||
static int st33zp24_status_to_errno(u8 code)
|
||||
{
|
||||
switch (code) {
|
||||
case ST33ZP24_OK:
|
||||
return 0;
|
||||
case ST33ZP24_UNDEFINED_ERR:
|
||||
case ST33ZP24_BADLOCALITY:
|
||||
case ST33ZP24_TISREGISTER_UKNOWN:
|
||||
case ST33ZP24_LOCALITY_NOT_ACTIVATED:
|
||||
case ST33ZP24_HASH_END_BEFORE_HASH_START:
|
||||
case ST33ZP24_BAD_COMMAND_ORDER:
|
||||
case ST33ZP24_UNEXPECTED_READ_FIFO:
|
||||
case ST33ZP24_UNEXPECTED_WRITE_FIFO:
|
||||
case ST33ZP24_CMDRDY_SET_WHEN_PROCESSING_HASH_END:
|
||||
return -EPROTO;
|
||||
case ST33ZP24_INCORECT_RECEIVED_LENGTH:
|
||||
case ST33ZP24_TPM_FIFO_OVERFLOW:
|
||||
return -EMSGSIZE;
|
||||
case ST33ZP24_DUMMY_BYTES:
|
||||
return -ENOSYS;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_spi_send
|
||||
* Send byte to the TIS register according to the ST33ZP24 SPI protocol.
|
||||
* @param: phy_id, the phy description
|
||||
* @param: tpm_register, the tpm tis register where the data should be written
|
||||
* @param: tpm_data, the tpm_data to write inside the tpm_register
|
||||
* @param: tpm_size, The length of the data
|
||||
* @return: should be zero if success else a negative error code.
|
||||
*/
|
||||
static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data,
|
||||
int tpm_size)
|
||||
{
|
||||
u8 data = 0;
|
||||
int total_length = 0, nbr_dummy_bytes = 0, ret = 0;
|
||||
struct st33zp24_spi_phy *phy = phy_id;
|
||||
struct spi_device *dev = phy->spi_device;
|
||||
u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf;
|
||||
u8 *rx_buf = phy->spi_xfer.rx_buf;
|
||||
|
||||
/* Pre-Header */
|
||||
data = TPM_WRITE_DIRECTION | LOCALITY0;
|
||||
memcpy(tx_buf + total_length, &data, sizeof(data));
|
||||
total_length++;
|
||||
data = tpm_register;
|
||||
memcpy(tx_buf + total_length, &data, sizeof(data));
|
||||
total_length++;
|
||||
|
||||
if (tpm_size > 0 && tpm_register == TPM_DATA_FIFO) {
|
||||
tx_buf[total_length++] = tpm_size >> 8;
|
||||
tx_buf[total_length++] = tpm_size;
|
||||
}
|
||||
|
||||
memcpy(&tx_buf[total_length], tpm_data, tpm_size);
|
||||
total_length += tpm_size;
|
||||
|
||||
nbr_dummy_bytes = phy->latency;
|
||||
memset(&tx_buf[total_length], TPM_DUMMY_BYTE, nbr_dummy_bytes);
|
||||
|
||||
phy->spi_xfer.len = total_length + nbr_dummy_bytes;
|
||||
|
||||
ret = spi_sync_transfer(dev, &phy->spi_xfer, 1);
|
||||
if (ret == 0)
|
||||
ret = rx_buf[total_length + nbr_dummy_bytes - 1];
|
||||
|
||||
return st33zp24_status_to_errno(ret);
|
||||
} /* st33zp24_spi_send() */
|
||||
|
||||
/*
|
||||
* read8_recv
|
||||
* Recv byte from the TIS register according to the ST33ZP24 SPI protocol.
|
||||
* @param: phy_id, the phy description
|
||||
* @param: tpm_register, the tpm tis register where the data should be read
|
||||
* @param: tpm_data, the TPM response
|
||||
* @param: tpm_size, tpm TPM response size to read.
|
||||
* @return: should be zero if success else a negative error code.
|
||||
*/
|
||||
static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size)
|
||||
{
|
||||
u8 data = 0;
|
||||
int total_length = 0, nbr_dummy_bytes, ret;
|
||||
struct st33zp24_spi_phy *phy = phy_id;
|
||||
struct spi_device *dev = phy->spi_device;
|
||||
u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf;
|
||||
u8 *rx_buf = phy->spi_xfer.rx_buf;
|
||||
|
||||
/* Pre-Header */
|
||||
data = LOCALITY0;
|
||||
memcpy(tx_buf + total_length, &data, sizeof(data));
|
||||
total_length++;
|
||||
data = tpm_register;
|
||||
memcpy(tx_buf + total_length, &data, sizeof(data));
|
||||
total_length++;
|
||||
|
||||
nbr_dummy_bytes = phy->latency;
|
||||
memset(&tx_buf[total_length], TPM_DUMMY_BYTE,
|
||||
nbr_dummy_bytes + tpm_size);
|
||||
|
||||
phy->spi_xfer.len = total_length + nbr_dummy_bytes + tpm_size;
|
||||
|
||||
/* header + status byte + size of the data + status byte */
|
||||
ret = spi_sync_transfer(dev, &phy->spi_xfer, 1);
|
||||
if (tpm_size > 0 && ret == 0) {
|
||||
ret = rx_buf[total_length + nbr_dummy_bytes - 1];
|
||||
|
||||
memcpy(tpm_data, rx_buf + total_length + nbr_dummy_bytes,
|
||||
tpm_size);
|
||||
}
|
||||
|
||||
return ret;
|
||||
} /* read8_reg() */
|
||||
|
||||
/*
|
||||
* st33zp24_spi_recv
|
||||
* Recv byte from the TIS register according to the ST33ZP24 SPI protocol.
|
||||
* @param: phy_id, the phy description
|
||||
* @param: tpm_register, the tpm tis register where the data should be read
|
||||
* @param: tpm_data, the TPM response
|
||||
* @param: tpm_size, tpm TPM response size to read.
|
||||
* @return: number of byte read successfully: should be one if success.
|
||||
*/
|
||||
static int st33zp24_spi_recv(void *phy_id, u8 tpm_register, u8 *tpm_data,
|
||||
int tpm_size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = read8_reg(phy_id, tpm_register, tpm_data, tpm_size);
|
||||
if (!st33zp24_status_to_errno(ret))
|
||||
return tpm_size;
|
||||
return ret;
|
||||
} /* st33zp24_spi_recv() */
|
||||
|
||||
static int evaluate_latency(void *phy_id)
|
||||
{
|
||||
struct st33zp24_spi_phy *phy = phy_id;
|
||||
int latency = 1, status = 0;
|
||||
u8 data = 0;
|
||||
|
||||
while (!status && latency < MAX_SPI_LATENCY) {
|
||||
phy->latency = latency;
|
||||
status = read8_reg(phy_id, TPM_INTF_CAPABILITY, &data, 1);
|
||||
latency++;
|
||||
}
|
||||
return latency - 1;
|
||||
} /* evaluate_latency() */
|
||||
|
||||
static const struct st33zp24_phy_ops spi_phy_ops = {
|
||||
.send = st33zp24_spi_send,
|
||||
.recv = st33zp24_spi_recv,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy)
|
||||
{
|
||||
struct device_node *pp;
|
||||
struct spi_device *dev = phy->spi_device;
|
||||
int gpio;
|
||||
int ret;
|
||||
|
||||
pp = dev->dev.of_node;
|
||||
if (!pp) {
|
||||
dev_err(&dev->dev, "No platform data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Get GPIO from device tree */
|
||||
gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0);
|
||||
if (gpio < 0) {
|
||||
dev_err(&dev->dev,
|
||||
"Failed to retrieve lpcpd-gpios from dts.\n");
|
||||
phy->io_lpcpd = -1;
|
||||
/*
|
||||
* lpcpd pin is not specified. This is not an issue as
|
||||
* power management can be also managed by TPM specific
|
||||
* commands. So leave with a success status code.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
/* GPIO request and configuration */
|
||||
ret = devm_gpio_request_one(&dev->dev, gpio,
|
||||
GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD");
|
||||
if (ret) {
|
||||
dev_err(&dev->dev, "Failed to request lpcpd pin\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
phy->io_lpcpd = gpio;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int tpm_stm_spi_request_resources(struct spi_device *dev,
|
||||
struct st33zp24_spi_phy *phy)
|
||||
{
|
||||
struct st33zp24_platform_data *pdata;
|
||||
int ret;
|
||||
|
||||
pdata = dev->dev.platform_data;
|
||||
if (!pdata) {
|
||||
dev_err(&dev->dev, "No platform data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* store for late use */
|
||||
phy->io_lpcpd = pdata->io_lpcpd;
|
||||
|
||||
if (gpio_is_valid(pdata->io_lpcpd)) {
|
||||
ret = devm_gpio_request_one(&dev->dev,
|
||||
pdata->io_lpcpd, GPIOF_OUT_INIT_HIGH,
|
||||
"TPM IO_LPCPD");
|
||||
if (ret) {
|
||||
dev_err(&dev->dev, "%s : reset gpio_request failed\n",
|
||||
__FILE__);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* tpm_st33_spi_probe initialize the TPM device
|
||||
* @param: dev, the spi_device drescription (TPM SPI description).
|
||||
* @return: 0 in case of success.
|
||||
* or a negative value describing the error.
|
||||
*/
|
||||
static int
|
||||
tpm_st33_spi_probe(struct spi_device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct st33zp24_platform_data *pdata;
|
||||
struct st33zp24_spi_phy *phy;
|
||||
|
||||
/* Check SPI platform functionnalities */
|
||||
if (!dev) {
|
||||
pr_info("%s: dev is NULL. Device is not accessible.\n",
|
||||
__func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
phy = devm_kzalloc(&dev->dev, sizeof(struct st33zp24_spi_phy),
|
||||
GFP_KERNEL);
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
phy->spi_device = dev;
|
||||
pdata = dev->dev.platform_data;
|
||||
if (!pdata && dev->dev.of_node) {
|
||||
ret = tpm_stm_spi_of_request_resources(phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (pdata) {
|
||||
ret = tpm_stm_spi_request_resources(dev, phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
phy->spi_xfer.tx_buf = phy->tx_buf;
|
||||
phy->spi_xfer.rx_buf = phy->rx_buf;
|
||||
|
||||
phy->latency = evaluate_latency(phy);
|
||||
if (phy->latency <= 0)
|
||||
return -ENODEV;
|
||||
|
||||
return st33zp24_probe(phy, &spi_phy_ops, &dev->dev, dev->irq,
|
||||
phy->io_lpcpd);
|
||||
}
|
||||
|
||||
/*
|
||||
* tpm_st33_spi_remove remove the TPM device
|
||||
* @param: client, the spi_device drescription (TPM SPI description).
|
||||
* @return: 0 in case of success.
|
||||
*/
|
||||
static int tpm_st33_spi_remove(struct spi_device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = spi_get_drvdata(dev);
|
||||
|
||||
return st33zp24_remove(chip);
|
||||
}
|
||||
|
||||
static const struct spi_device_id st33zp24_spi_id[] = {
|
||||
{TPM_ST33_SPI, 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st33zp24_spi_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_st33zp24_spi_match[] = {
|
||||
{ .compatible = "st,st33zp24-spi", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_st33zp24_spi_match);
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(st33zp24_spi_ops, st33zp24_pm_suspend,
|
||||
st33zp24_pm_resume);
|
||||
|
||||
static struct spi_driver tpm_st33_spi_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = TPM_ST33_SPI,
|
||||
.pm = &st33zp24_spi_ops,
|
||||
.of_match_table = of_match_ptr(of_st33zp24_spi_match),
|
||||
},
|
||||
.probe = tpm_st33_spi_probe,
|
||||
.remove = tpm_st33_spi_remove,
|
||||
.id_table = st33zp24_spi_id,
|
||||
};
|
||||
|
||||
module_spi_driver(tpm_st33_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)");
|
||||
MODULE_DESCRIPTION("STM TPM 1.2 SPI ST33 Driver");
|
||||
MODULE_VERSION("1.3.0");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,698 @@
|
|||
/*
|
||||
* STMicroelectronics TPM Linux driver for TPM ST33ZP24
|
||||
* Copyright (C) 2009 - 2015 STMicroelectronics
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "../tpm.h"
|
||||
#include "st33zp24.h"
|
||||
|
||||
#define TPM_ACCESS 0x0
|
||||
#define TPM_STS 0x18
|
||||
#define TPM_DATA_FIFO 0x24
|
||||
#define TPM_INTF_CAPABILITY 0x14
|
||||
#define TPM_INT_STATUS 0x10
|
||||
#define TPM_INT_ENABLE 0x08
|
||||
|
||||
#define LOCALITY0 0
|
||||
|
||||
enum st33zp24_access {
|
||||
TPM_ACCESS_VALID = 0x80,
|
||||
TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
|
||||
TPM_ACCESS_REQUEST_PENDING = 0x04,
|
||||
TPM_ACCESS_REQUEST_USE = 0x02,
|
||||
};
|
||||
|
||||
enum st33zp24_status {
|
||||
TPM_STS_VALID = 0x80,
|
||||
TPM_STS_COMMAND_READY = 0x40,
|
||||
TPM_STS_GO = 0x20,
|
||||
TPM_STS_DATA_AVAIL = 0x10,
|
||||
TPM_STS_DATA_EXPECT = 0x08,
|
||||
};
|
||||
|
||||
enum st33zp24_int_flags {
|
||||
TPM_GLOBAL_INT_ENABLE = 0x80,
|
||||
TPM_INTF_CMD_READY_INT = 0x080,
|
||||
TPM_INTF_FIFO_AVALAIBLE_INT = 0x040,
|
||||
TPM_INTF_WAKE_UP_READY_INT = 0x020,
|
||||
TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
|
||||
TPM_INTF_STS_VALID_INT = 0x002,
|
||||
TPM_INTF_DATA_AVAIL_INT = 0x001,
|
||||
};
|
||||
|
||||
enum tis_defaults {
|
||||
TIS_SHORT_TIMEOUT = 750,
|
||||
TIS_LONG_TIMEOUT = 2000,
|
||||
};
|
||||
|
||||
struct st33zp24_dev {
|
||||
struct tpm_chip *chip;
|
||||
void *phy_id;
|
||||
const struct st33zp24_phy_ops *ops;
|
||||
u32 intrs;
|
||||
int io_lpcpd;
|
||||
};
|
||||
|
||||
/*
|
||||
* clear_interruption clear the pending interrupt.
|
||||
* @param: tpm_dev, the tpm device device.
|
||||
* @return: the interrupt status value.
|
||||
*/
|
||||
static u8 clear_interruption(struct st33zp24_dev *tpm_dev)
|
||||
{
|
||||
u8 interrupt;
|
||||
|
||||
tpm_dev->ops->recv(tpm_dev->phy_id, TPM_INT_STATUS, &interrupt, 1);
|
||||
tpm_dev->ops->send(tpm_dev->phy_id, TPM_INT_STATUS, &interrupt, 1);
|
||||
return interrupt;
|
||||
} /* clear_interruption() */
|
||||
|
||||
/*
|
||||
* st33zp24_cancel, cancel the current command execution or
|
||||
* set STS to COMMAND READY.
|
||||
* @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h
|
||||
*/
|
||||
static void st33zp24_cancel(struct tpm_chip *chip)
|
||||
{
|
||||
struct st33zp24_dev *tpm_dev;
|
||||
u8 data;
|
||||
|
||||
tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
|
||||
|
||||
data = TPM_STS_COMMAND_READY;
|
||||
tpm_dev->ops->send(tpm_dev->phy_id, TPM_STS, &data, 1);
|
||||
} /* st33zp24_cancel() */
|
||||
|
||||
/*
|
||||
* st33zp24_status return the TPM_STS register
|
||||
* @param: chip, the tpm chip description
|
||||
* @return: the TPM_STS register value.
|
||||
*/
|
||||
static u8 st33zp24_status(struct tpm_chip *chip)
|
||||
{
|
||||
struct st33zp24_dev *tpm_dev;
|
||||
u8 data;
|
||||
|
||||
tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
|
||||
|
||||
tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS, &data, 1);
|
||||
return data;
|
||||
} /* st33zp24_status() */
|
||||
|
||||
/*
|
||||
* check_locality if the locality is active
|
||||
* @param: chip, the tpm chip description
|
||||
* @return: the active locality or -EACCESS.
|
||||
*/
|
||||
static int check_locality(struct tpm_chip *chip)
|
||||
{
|
||||
struct st33zp24_dev *tpm_dev;
|
||||
u8 data;
|
||||
u8 status;
|
||||
|
||||
tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
|
||||
|
||||
status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
|
||||
if (status && (data &
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
|
||||
return chip->vendor.locality;
|
||||
|
||||
return -EACCES;
|
||||
} /* check_locality() */
|
||||
|
||||
/*
|
||||
* request_locality request the TPM locality
|
||||
* @param: chip, the chip description
|
||||
* @return: the active locality or negative value.
|
||||
*/
|
||||
static int request_locality(struct tpm_chip *chip)
|
||||
{
|
||||
unsigned long stop;
|
||||
long ret;
|
||||
struct st33zp24_dev *tpm_dev;
|
||||
u8 data;
|
||||
|
||||
if (check_locality(chip) == chip->vendor.locality)
|
||||
return chip->vendor.locality;
|
||||
|
||||
tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
|
||||
|
||||
data = TPM_ACCESS_REQUEST_USE;
|
||||
ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
stop = jiffies + chip->vendor.timeout_a;
|
||||
|
||||
/* Request locality is usually effective after the request */
|
||||
do {
|
||||
if (check_locality(chip) >= 0)
|
||||
return chip->vendor.locality;
|
||||
msleep(TPM_TIMEOUT);
|
||||
} while (time_before(jiffies, stop));
|
||||
|
||||
/* could not get locality */
|
||||
return -EACCES;
|
||||
} /* request_locality() */
|
||||
|
||||
/*
|
||||
* release_locality release the active locality
|
||||
* @param: chip, the tpm chip description.
|
||||
*/
|
||||
static void release_locality(struct tpm_chip *chip)
|
||||
{
|
||||
struct st33zp24_dev *tpm_dev;
|
||||
u8 data;
|
||||
|
||||
tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
|
||||
data = TPM_ACCESS_ACTIVE_LOCALITY;
|
||||
|
||||
tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* get_burstcount return the burstcount value
|
||||
* @param: chip, the chip description
|
||||
* return: the burstcount or negative value.
|
||||
*/
|
||||
static int get_burstcount(struct tpm_chip *chip)
|
||||
{
|
||||
unsigned long stop;
|
||||
int burstcnt, status;
|
||||
u8 tpm_reg, temp;
|
||||
struct st33zp24_dev *tpm_dev;
|
||||
|
||||
tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
|
||||
|
||||
stop = jiffies + chip->vendor.timeout_d;
|
||||
do {
|
||||
tpm_reg = TPM_STS + 1;
|
||||
status = tpm_dev->ops->recv(tpm_dev->phy_id, tpm_reg, &temp, 1);
|
||||
if (status < 0)
|
||||
return -EBUSY;
|
||||
|
||||
tpm_reg = TPM_STS + 2;
|
||||
burstcnt = temp;
|
||||
status = tpm_dev->ops->recv(tpm_dev->phy_id, tpm_reg, &temp, 1);
|
||||
if (status < 0)
|
||||
return -EBUSY;
|
||||
|
||||
burstcnt |= temp << 8;
|
||||
if (burstcnt)
|
||||
return burstcnt;
|
||||
msleep(TPM_TIMEOUT);
|
||||
} while (time_before(jiffies, stop));
|
||||
return -EBUSY;
|
||||
} /* get_burstcount() */
|
||||
|
||||
|
||||
/*
|
||||
* wait_for_tpm_stat_cond
|
||||
* @param: chip, chip description
|
||||
* @param: mask, expected mask value
|
||||
* @param: check_cancel, does the command expected to be canceled ?
|
||||
* @param: canceled, did we received a cancel request ?
|
||||
* @return: true if status == mask or if the command is canceled.
|
||||
* false in other cases.
|
||||
*/
|
||||
static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask,
|
||||
bool check_cancel, bool *canceled)
|
||||
{
|
||||
u8 status = chip->ops->status(chip);
|
||||
|
||||
*canceled = false;
|
||||
if ((status & mask) == mask)
|
||||
return true;
|
||||
if (check_cancel && chip->ops->req_canceled(chip, status)) {
|
||||
*canceled = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* wait_for_stat wait for a TPM_STS value
|
||||
* @param: chip, the tpm chip description
|
||||
* @param: mask, the value mask to wait
|
||||
* @param: timeout, the timeout
|
||||
* @param: queue, the wait queue.
|
||||
* @param: check_cancel, does the command can be cancelled ?
|
||||
* @return: the tpm status, 0 if success, -ETIME if timeout is reached.
|
||||
*/
|
||||
static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
|
||||
wait_queue_head_t *queue, bool check_cancel)
|
||||
{
|
||||
unsigned long stop;
|
||||
int ret = 0;
|
||||
bool canceled = false;
|
||||
bool condition;
|
||||
u32 cur_intrs;
|
||||
u8 status;
|
||||
struct st33zp24_dev *tpm_dev;
|
||||
|
||||
tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
|
||||
|
||||
/* check current status */
|
||||
status = st33zp24_status(chip);
|
||||
if ((status & mask) == mask)
|
||||
return 0;
|
||||
|
||||
stop = jiffies + timeout;
|
||||
|
||||
if (chip->vendor.irq) {
|
||||
cur_intrs = tpm_dev->intrs;
|
||||
clear_interruption(tpm_dev);
|
||||
enable_irq(chip->vendor.irq);
|
||||
|
||||
do {
|
||||
if (ret == -ERESTARTSYS && freezing(current))
|
||||
clear_thread_flag(TIF_SIGPENDING);
|
||||
|
||||
timeout = stop - jiffies;
|
||||
if ((long) timeout <= 0)
|
||||
return -1;
|
||||
|
||||
ret = wait_event_interruptible_timeout(*queue,
|
||||
cur_intrs != tpm_dev->intrs,
|
||||
timeout);
|
||||
clear_interruption(tpm_dev);
|
||||
condition = wait_for_tpm_stat_cond(chip, mask,
|
||||
check_cancel, &canceled);
|
||||
if (ret >= 0 && condition) {
|
||||
if (canceled)
|
||||
return -ECANCELED;
|
||||
return 0;
|
||||
}
|
||||
} while (ret == -ERESTARTSYS && freezing(current));
|
||||
|
||||
disable_irq_nosync(chip->vendor.irq);
|
||||
|
||||
} else {
|
||||
do {
|
||||
msleep(TPM_TIMEOUT);
|
||||
status = chip->ops->status(chip);
|
||||
if ((status & mask) == mask)
|
||||
return 0;
|
||||
} while (time_before(jiffies, stop));
|
||||
}
|
||||
|
||||
return -ETIME;
|
||||
} /* wait_for_stat() */
|
||||
|
||||
/*
|
||||
* recv_data receive data
|
||||
* @param: chip, the tpm chip description
|
||||
* @param: buf, the buffer where the data are received
|
||||
* @param: count, the number of data to receive
|
||||
* @return: the number of bytes read from TPM FIFO.
|
||||
*/
|
||||
static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
{
|
||||
int size = 0, burstcnt, len, ret;
|
||||
struct st33zp24_dev *tpm_dev;
|
||||
|
||||
tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
|
||||
|
||||
while (size < count &&
|
||||
wait_for_stat(chip,
|
||||
TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
chip->vendor.timeout_c,
|
||||
&chip->vendor.read_queue, true) == 0) {
|
||||
burstcnt = get_burstcount(chip);
|
||||
if (burstcnt < 0)
|
||||
return burstcnt;
|
||||
len = min_t(int, burstcnt, count - size);
|
||||
ret = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_DATA_FIFO,
|
||||
buf + size, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
size += len;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* tpm_ioserirq_handler the serirq irq handler
|
||||
* @param: irq, the tpm chip description
|
||||
* @param: dev_id, the description of the chip
|
||||
* @return: the status of the handler.
|
||||
*/
|
||||
static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct tpm_chip *chip = dev_id;
|
||||
struct st33zp24_dev *tpm_dev;
|
||||
|
||||
tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
|
||||
|
||||
tpm_dev->intrs++;
|
||||
wake_up_interruptible(&chip->vendor.read_queue);
|
||||
disable_irq_nosync(chip->vendor.irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
} /* tpm_ioserirq_handler() */
|
||||
|
||||
/*
|
||||
* st33zp24_send send TPM commands through the I2C bus.
|
||||
*
|
||||
* @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h
|
||||
* @param: buf, the buffer to send.
|
||||
* @param: count, the number of bytes to send.
|
||||
* @return: In case of success the number of bytes sent.
|
||||
* In other case, a < 0 value describing the issue.
|
||||
*/
|
||||
static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf,
|
||||
size_t len)
|
||||
{
|
||||
u32 status, i, size, ordinal;
|
||||
int burstcnt = 0;
|
||||
int ret;
|
||||
u8 data;
|
||||
struct st33zp24_dev *tpm_dev;
|
||||
|
||||
if (!chip)
|
||||
return -EBUSY;
|
||||
if (len < TPM_HEADER_SIZE)
|
||||
return -EBUSY;
|
||||
|
||||
tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
|
||||
|
||||
ret = request_locality(chip);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
status = st33zp24_status(chip);
|
||||
if ((status & TPM_STS_COMMAND_READY) == 0) {
|
||||
st33zp24_cancel(chip);
|
||||
if (wait_for_stat
|
||||
(chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
|
||||
&chip->vendor.read_queue, false) < 0) {
|
||||
ret = -ETIME;
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < len - 1;) {
|
||||
burstcnt = get_burstcount(chip);
|
||||
if (burstcnt < 0)
|
||||
return burstcnt;
|
||||
size = min_t(int, len - i - 1, burstcnt);
|
||||
ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_DATA_FIFO,
|
||||
buf + i, size);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
i += size;
|
||||
}
|
||||
|
||||
status = st33zp24_status(chip);
|
||||
if ((status & TPM_STS_DATA_EXPECT) == 0) {
|
||||
ret = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_DATA_FIFO,
|
||||
buf + len - 1, 1);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
status = st33zp24_status(chip);
|
||||
if ((status & TPM_STS_DATA_EXPECT) != 0) {
|
||||
ret = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
data = TPM_STS_GO;
|
||||
ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_STS, &data, 1);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
if (chip->vendor.irq) {
|
||||
ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
|
||||
|
||||
ret = wait_for_stat(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
tpm_calc_ordinal_duration(chip, ordinal),
|
||||
&chip->vendor.read_queue, false);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
return len;
|
||||
out_err:
|
||||
st33zp24_cancel(chip);
|
||||
release_locality(chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_recv received TPM response through TPM phy.
|
||||
* @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h.
|
||||
* @param: buf, the buffer to store datas.
|
||||
* @param: count, the number of bytes to send.
|
||||
* @return: In case of success the number of bytes received.
|
||||
* In other case, a < 0 value describing the issue.
|
||||
*/
|
||||
static int st33zp24_recv(struct tpm_chip *chip, unsigned char *buf,
|
||||
size_t count)
|
||||
{
|
||||
int size = 0;
|
||||
int expected;
|
||||
|
||||
if (!chip)
|
||||
return -EBUSY;
|
||||
|
||||
if (count < TPM_HEADER_SIZE) {
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size = recv_data(chip, buf, TPM_HEADER_SIZE);
|
||||
if (size < TPM_HEADER_SIZE) {
|
||||
dev_err(&chip->dev, "Unable to read header\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
expected = be32_to_cpu(*(__be32 *)(buf + 2));
|
||||
if (expected > count) {
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size += recv_data(chip, &buf[TPM_HEADER_SIZE],
|
||||
expected - TPM_HEADER_SIZE);
|
||||
if (size < expected) {
|
||||
dev_err(&chip->dev, "Unable to read remainder of result\n");
|
||||
size = -ETIME;
|
||||
}
|
||||
|
||||
out:
|
||||
st33zp24_cancel(chip);
|
||||
release_locality(chip);
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_req_canceled
|
||||
* @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h.
|
||||
* @param: status, the TPM status.
|
||||
* @return: Does TPM ready to compute a new command ? true.
|
||||
*/
|
||||
static bool st33zp24_req_canceled(struct tpm_chip *chip, u8 status)
|
||||
{
|
||||
return (status == TPM_STS_COMMAND_READY);
|
||||
}
|
||||
|
||||
static const struct tpm_class_ops st33zp24_tpm = {
|
||||
.send = st33zp24_send,
|
||||
.recv = st33zp24_recv,
|
||||
.cancel = st33zp24_cancel,
|
||||
.status = st33zp24_status,
|
||||
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_canceled = st33zp24_req_canceled,
|
||||
};
|
||||
|
||||
/*
|
||||
* st33zp24_probe initialize the TPM device
|
||||
* @param: client, the i2c_client drescription (TPM I2C description).
|
||||
* @param: id, the i2c_device_id struct.
|
||||
* @return: 0 in case of success.
|
||||
* -1 in other case.
|
||||
*/
|
||||
int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops,
|
||||
struct device *dev, int irq, int io_lpcpd)
|
||||
{
|
||||
int ret;
|
||||
u8 intmask = 0;
|
||||
struct tpm_chip *chip;
|
||||
struct st33zp24_dev *tpm_dev;
|
||||
|
||||
chip = tpmm_chip_alloc(dev, &st33zp24_tpm);
|
||||
if (IS_ERR(chip))
|
||||
return PTR_ERR(chip);
|
||||
|
||||
tpm_dev = devm_kzalloc(dev, sizeof(struct st33zp24_dev),
|
||||
GFP_KERNEL);
|
||||
if (!tpm_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
TPM_VPRIV(chip) = tpm_dev;
|
||||
tpm_dev->phy_id = phy_id;
|
||||
tpm_dev->ops = ops;
|
||||
|
||||
chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
|
||||
chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
|
||||
chip->vendor.locality = LOCALITY0;
|
||||
|
||||
if (irq) {
|
||||
/* INTERRUPT Setup */
|
||||
init_waitqueue_head(&chip->vendor.read_queue);
|
||||
tpm_dev->intrs = 0;
|
||||
|
||||
if (request_locality(chip) != LOCALITY0) {
|
||||
ret = -ENODEV;
|
||||
goto _tpm_clean_answer;
|
||||
}
|
||||
|
||||
clear_interruption(tpm_dev);
|
||||
ret = devm_request_irq(dev, irq, tpm_ioserirq_handler,
|
||||
IRQF_TRIGGER_HIGH, "TPM SERIRQ management",
|
||||
chip);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->dev, "TPM SERIRQ signals %d not available\n",
|
||||
irq);
|
||||
goto _tpm_clean_answer;
|
||||
}
|
||||
|
||||
intmask |= TPM_INTF_CMD_READY_INT
|
||||
| TPM_INTF_STS_VALID_INT
|
||||
| TPM_INTF_DATA_AVAIL_INT;
|
||||
|
||||
ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_INT_ENABLE,
|
||||
&intmask, 1);
|
||||
if (ret < 0)
|
||||
goto _tpm_clean_answer;
|
||||
|
||||
intmask = TPM_GLOBAL_INT_ENABLE;
|
||||
ret = tpm_dev->ops->send(tpm_dev->phy_id, (TPM_INT_ENABLE + 3),
|
||||
&intmask, 1);
|
||||
if (ret < 0)
|
||||
goto _tpm_clean_answer;
|
||||
|
||||
chip->vendor.irq = irq;
|
||||
|
||||
disable_irq_nosync(chip->vendor.irq);
|
||||
|
||||
tpm_gen_interrupt(chip);
|
||||
}
|
||||
|
||||
tpm_get_timeouts(chip);
|
||||
tpm_do_selftest(chip);
|
||||
|
||||
return tpm_chip_register(chip);
|
||||
_tpm_clean_answer:
|
||||
dev_info(&chip->dev, "TPM initialization fail\n");
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(st33zp24_probe);
|
||||
|
||||
/*
|
||||
* st33zp24_remove remove the TPM device
|
||||
* @param: tpm_data, the tpm phy.
|
||||
* @return: 0 in case of success.
|
||||
*/
|
||||
int st33zp24_remove(struct tpm_chip *chip)
|
||||
{
|
||||
tpm_chip_unregister(chip);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(st33zp24_remove);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/*
|
||||
* st33zp24_pm_suspend suspend the TPM device
|
||||
* @param: tpm_data, the tpm phy.
|
||||
* @param: mesg, the power management message.
|
||||
* @return: 0 in case of success.
|
||||
*/
|
||||
int st33zp24_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||
struct st33zp24_dev *tpm_dev;
|
||||
int ret = 0;
|
||||
|
||||
tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
|
||||
|
||||
if (gpio_is_valid(tpm_dev->io_lpcpd))
|
||||
gpio_set_value(tpm_dev->io_lpcpd, 0);
|
||||
else
|
||||
ret = tpm_pm_suspend(dev);
|
||||
|
||||
return ret;
|
||||
} /* st33zp24_pm_suspend() */
|
||||
EXPORT_SYMBOL(st33zp24_pm_suspend);
|
||||
|
||||
/*
|
||||
* st33zp24_pm_resume resume the TPM device
|
||||
* @param: tpm_data, the tpm phy.
|
||||
* @return: 0 in case of success.
|
||||
*/
|
||||
int st33zp24_pm_resume(struct device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||
struct st33zp24_dev *tpm_dev;
|
||||
int ret = 0;
|
||||
|
||||
tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
|
||||
|
||||
if (gpio_is_valid(tpm_dev->io_lpcpd)) {
|
||||
gpio_set_value(tpm_dev->io_lpcpd, 1);
|
||||
ret = wait_for_stat(chip,
|
||||
TPM_STS_VALID, chip->vendor.timeout_b,
|
||||
&chip->vendor.read_queue, false);
|
||||
} else {
|
||||
ret = tpm_pm_resume(dev);
|
||||
if (!ret)
|
||||
tpm_do_selftest(chip);
|
||||
}
|
||||
return ret;
|
||||
} /* st33zp24_pm_resume() */
|
||||
EXPORT_SYMBOL(st33zp24_pm_resume);
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)");
|
||||
MODULE_DESCRIPTION("ST33ZP24 TPM 1.2 driver");
|
||||
MODULE_VERSION("1.3.0");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* STMicroelectronics TPM Linux driver for TPM ST33ZP24
|
||||
* Copyright (C) 2009 - 2015 STMicroelectronics
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __LOCAL_ST33ZP24_H__
|
||||
#define __LOCAL_ST33ZP24_H__
|
||||
|
||||
#define TPM_WRITE_DIRECTION 0x80
|
||||
#define TPM_BUFSIZE 2048
|
||||
|
||||
struct st33zp24_phy_ops {
|
||||
int (*send)(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size);
|
||||
int (*recv)(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
int st33zp24_pm_suspend(struct device *dev);
|
||||
int st33zp24_pm_resume(struct device *dev);
|
||||
#endif
|
||||
|
||||
int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops,
|
||||
struct device *dev, int irq, int io_lpcpd);
|
||||
int st33zp24_remove(struct tpm_chip *chip);
|
||||
#endif /* __LOCAL_ST33ZP24_H__ */
|
|
@ -170,6 +170,41 @@ static void tpm_dev_del_device(struct tpm_chip *chip)
|
|||
device_unregister(&chip->dev);
|
||||
}
|
||||
|
||||
static int tpm1_chip_register(struct tpm_chip *chip)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2)
|
||||
return 0;
|
||||
|
||||
rc = tpm_sysfs_add_device(chip);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = tpm_add_ppi(chip);
|
||||
if (rc) {
|
||||
tpm_sysfs_del_device(chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
chip->bios_dir = tpm_bios_log_setup(chip->devname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpm1_chip_unregister(struct tpm_chip *chip)
|
||||
{
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2)
|
||||
return;
|
||||
|
||||
if (chip->bios_dir)
|
||||
tpm_bios_log_teardown(chip->bios_dir);
|
||||
|
||||
tpm_remove_ppi(chip);
|
||||
|
||||
tpm_sysfs_del_device(chip);
|
||||
}
|
||||
|
||||
/*
|
||||
* tpm_chip_register() - create a character device for the TPM chip
|
||||
* @chip: TPM chip to use.
|
||||
|
@ -185,22 +220,13 @@ int tpm_chip_register(struct tpm_chip *chip)
|
|||
{
|
||||
int rc;
|
||||
|
||||
/* Populate sysfs for TPM1 devices. */
|
||||
if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
|
||||
rc = tpm_sysfs_add_device(chip);
|
||||
rc = tpm1_chip_register(chip);
|
||||
if (rc)
|
||||
goto del_misc;
|
||||
|
||||
rc = tpm_add_ppi(chip);
|
||||
if (rc)
|
||||
goto del_sysfs;
|
||||
|
||||
chip->bios_dir = tpm_bios_log_setup(chip->devname);
|
||||
}
|
||||
return rc;
|
||||
|
||||
rc = tpm_dev_add_device(chip);
|
||||
if (rc)
|
||||
return rc;
|
||||
goto out_err;
|
||||
|
||||
/* Make the chip available. */
|
||||
spin_lock(&driver_lock);
|
||||
|
@ -210,10 +236,8 @@ int tpm_chip_register(struct tpm_chip *chip)
|
|||
chip->flags |= TPM_CHIP_FLAG_REGISTERED;
|
||||
|
||||
return 0;
|
||||
del_sysfs:
|
||||
tpm_sysfs_del_device(chip);
|
||||
del_misc:
|
||||
tpm_dev_del_device(chip);
|
||||
out_err:
|
||||
tpm1_chip_unregister(chip);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_chip_register);
|
||||
|
@ -238,13 +262,7 @@ void tpm_chip_unregister(struct tpm_chip *chip)
|
|||
spin_unlock(&driver_lock);
|
||||
synchronize_rcu();
|
||||
|
||||
if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
|
||||
if (chip->bios_dir)
|
||||
tpm_bios_log_teardown(chip->bios_dir);
|
||||
tpm_remove_ppi(chip);
|
||||
tpm_sysfs_del_device(chip);
|
||||
}
|
||||
|
||||
tpm1_chip_unregister(chip);
|
||||
tpm_dev_del_device(chip);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_chip_unregister);
|
||||
|
|
|
@ -1,911 +0,0 @@
|
|||
/*
|
||||
* STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24
|
||||
* Copyright (C) 2009, 2010, 2014 STMicroelectronics
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* STMicroelectronics version 1.2.1, Copyright (C) 2014
|
||||
* STMicroelectronics comes with ABSOLUTELY NO WARRANTY.
|
||||
* This is free software, and you are welcome to redistribute it
|
||||
* under certain conditions.
|
||||
*
|
||||
* @Author: Christophe RICARD tpmsupport@st.com
|
||||
*
|
||||
* @File: tpm_stm_st33_i2c.c
|
||||
*
|
||||
* @Synopsis:
|
||||
* 09/15/2010: First shot driver tpm_tis driver for
|
||||
* lpc is used as model.
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <linux/platform_data/tpm_stm_st33.h>
|
||||
#include "tpm.h"
|
||||
|
||||
#define TPM_ACCESS 0x0
|
||||
#define TPM_STS 0x18
|
||||
#define TPM_HASH_END 0x20
|
||||
#define TPM_DATA_FIFO 0x24
|
||||
#define TPM_HASH_DATA 0x24
|
||||
#define TPM_HASH_START 0x28
|
||||
#define TPM_INTF_CAPABILITY 0x14
|
||||
#define TPM_INT_STATUS 0x10
|
||||
#define TPM_INT_ENABLE 0x08
|
||||
|
||||
#define TPM_DUMMY_BYTE 0xAA
|
||||
#define TPM_WRITE_DIRECTION 0x80
|
||||
#define TPM_HEADER_SIZE 10
|
||||
#define TPM_BUFSIZE 2048
|
||||
|
||||
#define LOCALITY0 0
|
||||
|
||||
|
||||
enum stm33zp24_access {
|
||||
TPM_ACCESS_VALID = 0x80,
|
||||
TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
|
||||
TPM_ACCESS_REQUEST_PENDING = 0x04,
|
||||
TPM_ACCESS_REQUEST_USE = 0x02,
|
||||
};
|
||||
|
||||
enum stm33zp24_status {
|
||||
TPM_STS_VALID = 0x80,
|
||||
TPM_STS_COMMAND_READY = 0x40,
|
||||
TPM_STS_GO = 0x20,
|
||||
TPM_STS_DATA_AVAIL = 0x10,
|
||||
TPM_STS_DATA_EXPECT = 0x08,
|
||||
};
|
||||
|
||||
enum stm33zp24_int_flags {
|
||||
TPM_GLOBAL_INT_ENABLE = 0x80,
|
||||
TPM_INTF_CMD_READY_INT = 0x080,
|
||||
TPM_INTF_FIFO_AVALAIBLE_INT = 0x040,
|
||||
TPM_INTF_WAKE_UP_READY_INT = 0x020,
|
||||
TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
|
||||
TPM_INTF_STS_VALID_INT = 0x002,
|
||||
TPM_INTF_DATA_AVAIL_INT = 0x001,
|
||||
};
|
||||
|
||||
enum tis_defaults {
|
||||
TIS_SHORT_TIMEOUT = 750,
|
||||
TIS_LONG_TIMEOUT = 2000,
|
||||
};
|
||||
|
||||
struct tpm_stm_dev {
|
||||
struct i2c_client *client;
|
||||
struct tpm_chip *chip;
|
||||
u8 buf[TPM_BUFSIZE + 1];
|
||||
u32 intrs;
|
||||
int io_lpcpd;
|
||||
};
|
||||
|
||||
/*
|
||||
* write8_reg
|
||||
* Send byte to the TIS register according to the ST33ZP24 I2C protocol.
|
||||
* @param: tpm_register, the tpm tis register where the data should be written
|
||||
* @param: tpm_data, the tpm_data to write inside the tpm_register
|
||||
* @param: tpm_size, The length of the data
|
||||
* @return: Returns negative errno, or else the number of bytes written.
|
||||
*/
|
||||
static int write8_reg(struct tpm_stm_dev *tpm_dev, u8 tpm_register,
|
||||
u8 *tpm_data, u16 tpm_size)
|
||||
{
|
||||
tpm_dev->buf[0] = tpm_register;
|
||||
memcpy(tpm_dev->buf + 1, tpm_data, tpm_size);
|
||||
return i2c_master_send(tpm_dev->client, tpm_dev->buf, tpm_size + 1);
|
||||
} /* write8_reg() */
|
||||
|
||||
/*
|
||||
* read8_reg
|
||||
* Recv byte from the TIS register according to the ST33ZP24 I2C protocol.
|
||||
* @param: tpm_register, the tpm tis register where the data should be read
|
||||
* @param: tpm_data, the TPM response
|
||||
* @param: tpm_size, tpm TPM response size to read.
|
||||
* @return: number of byte read successfully: should be one if success.
|
||||
*/
|
||||
static int read8_reg(struct tpm_stm_dev *tpm_dev, u8 tpm_register,
|
||||
u8 *tpm_data, int tpm_size)
|
||||
{
|
||||
u8 status = 0;
|
||||
u8 data;
|
||||
|
||||
data = TPM_DUMMY_BYTE;
|
||||
status = write8_reg(tpm_dev, tpm_register, &data, 1);
|
||||
if (status == 2)
|
||||
status = i2c_master_recv(tpm_dev->client, tpm_data, tpm_size);
|
||||
return status;
|
||||
} /* read8_reg() */
|
||||
|
||||
/*
|
||||
* I2C_WRITE_DATA
|
||||
* Send byte to the TIS register according to the ST33ZP24 I2C protocol.
|
||||
* @param: tpm_dev, the chip description
|
||||
* @param: tpm_register, the tpm tis register where the data should be written
|
||||
* @param: tpm_data, the tpm_data to write inside the tpm_register
|
||||
* @param: tpm_size, The length of the data
|
||||
* @return: number of byte written successfully: should be one if success.
|
||||
*/
|
||||
#define I2C_WRITE_DATA(tpm_dev, tpm_register, tpm_data, tpm_size) \
|
||||
(write8_reg(tpm_dev, tpm_register | \
|
||||
TPM_WRITE_DIRECTION, tpm_data, tpm_size))
|
||||
|
||||
/*
|
||||
* I2C_READ_DATA
|
||||
* Recv byte from the TIS register according to the ST33ZP24 I2C protocol.
|
||||
* @param: tpm_dev, the chip description
|
||||
* @param: tpm_register, the tpm tis register where the data should be read
|
||||
* @param: tpm_data, the TPM response
|
||||
* @param: tpm_size, tpm TPM response size to read.
|
||||
* @return: number of byte read successfully: should be one if success.
|
||||
*/
|
||||
#define I2C_READ_DATA(tpm_dev, tpm_register, tpm_data, tpm_size) \
|
||||
(read8_reg(tpm_dev, tpm_register, tpm_data, tpm_size))
|
||||
|
||||
/*
|
||||
* clear_interruption
|
||||
* clear the TPM interrupt register.
|
||||
* @param: tpm, the chip description
|
||||
* @return: the TPM_INT_STATUS value
|
||||
*/
|
||||
static u8 clear_interruption(struct tpm_stm_dev *tpm_dev)
|
||||
{
|
||||
u8 interrupt;
|
||||
|
||||
I2C_READ_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1);
|
||||
I2C_WRITE_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1);
|
||||
return interrupt;
|
||||
} /* clear_interruption() */
|
||||
|
||||
/*
|
||||
* tpm_stm_i2c_cancel, cancel is not implemented.
|
||||
* @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h
|
||||
*/
|
||||
static void tpm_stm_i2c_cancel(struct tpm_chip *chip)
|
||||
{
|
||||
struct tpm_stm_dev *tpm_dev;
|
||||
u8 data;
|
||||
|
||||
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
|
||||
|
||||
data = TPM_STS_COMMAND_READY;
|
||||
I2C_WRITE_DATA(tpm_dev, TPM_STS, &data, 1);
|
||||
} /* tpm_stm_i2c_cancel() */
|
||||
|
||||
/*
|
||||
* tpm_stm_spi_status return the TPM_STS register
|
||||
* @param: chip, the tpm chip description
|
||||
* @return: the TPM_STS register value.
|
||||
*/
|
||||
static u8 tpm_stm_i2c_status(struct tpm_chip *chip)
|
||||
{
|
||||
struct tpm_stm_dev *tpm_dev;
|
||||
u8 data;
|
||||
|
||||
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
|
||||
|
||||
I2C_READ_DATA(tpm_dev, TPM_STS, &data, 1);
|
||||
return data;
|
||||
} /* tpm_stm_i2c_status() */
|
||||
|
||||
|
||||
/*
|
||||
* check_locality if the locality is active
|
||||
* @param: chip, the tpm chip description
|
||||
* @return: the active locality or -EACCESS.
|
||||
*/
|
||||
static int check_locality(struct tpm_chip *chip)
|
||||
{
|
||||
struct tpm_stm_dev *tpm_dev;
|
||||
u8 data;
|
||||
u8 status;
|
||||
|
||||
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
|
||||
|
||||
status = I2C_READ_DATA(tpm_dev, TPM_ACCESS, &data, 1);
|
||||
if (status && (data &
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
|
||||
return chip->vendor.locality;
|
||||
|
||||
return -EACCES;
|
||||
} /* check_locality() */
|
||||
|
||||
/*
|
||||
* request_locality request the TPM locality
|
||||
* @param: chip, the chip description
|
||||
* @return: the active locality or EACCESS.
|
||||
*/
|
||||
static int request_locality(struct tpm_chip *chip)
|
||||
{
|
||||
unsigned long stop;
|
||||
long ret;
|
||||
struct tpm_stm_dev *tpm_dev;
|
||||
u8 data;
|
||||
|
||||
if (check_locality(chip) == chip->vendor.locality)
|
||||
return chip->vendor.locality;
|
||||
|
||||
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
|
||||
|
||||
data = TPM_ACCESS_REQUEST_USE;
|
||||
ret = I2C_WRITE_DATA(tpm_dev, TPM_ACCESS, &data, 1);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
|
||||
stop = jiffies + chip->vendor.timeout_a;
|
||||
|
||||
/* Request locality is usually effective after the request */
|
||||
do {
|
||||
if (check_locality(chip) >= 0)
|
||||
return chip->vendor.locality;
|
||||
msleep(TPM_TIMEOUT);
|
||||
} while (time_before(jiffies, stop));
|
||||
ret = -EACCES;
|
||||
end:
|
||||
return ret;
|
||||
} /* request_locality() */
|
||||
|
||||
/*
|
||||
* release_locality release the active locality
|
||||
* @param: chip, the tpm chip description.
|
||||
*/
|
||||
static void release_locality(struct tpm_chip *chip)
|
||||
{
|
||||
struct tpm_stm_dev *tpm_dev;
|
||||
u8 data;
|
||||
|
||||
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
|
||||
data = TPM_ACCESS_ACTIVE_LOCALITY;
|
||||
|
||||
I2C_WRITE_DATA(tpm_dev, TPM_ACCESS, &data, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* get_burstcount return the burstcount address 0x19 0x1A
|
||||
* @param: chip, the chip description
|
||||
* return: the burstcount.
|
||||
*/
|
||||
static int get_burstcount(struct tpm_chip *chip)
|
||||
{
|
||||
unsigned long stop;
|
||||
int burstcnt, status;
|
||||
u8 tpm_reg, temp;
|
||||
struct tpm_stm_dev *tpm_dev;
|
||||
|
||||
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
|
||||
|
||||
stop = jiffies + chip->vendor.timeout_d;
|
||||
do {
|
||||
tpm_reg = TPM_STS + 1;
|
||||
status = I2C_READ_DATA(tpm_dev, tpm_reg, &temp, 1);
|
||||
if (status < 0)
|
||||
goto end;
|
||||
|
||||
tpm_reg = tpm_reg + 1;
|
||||
burstcnt = temp;
|
||||
status = I2C_READ_DATA(tpm_dev, tpm_reg, &temp, 1);
|
||||
if (status < 0)
|
||||
goto end;
|
||||
|
||||
burstcnt |= temp << 8;
|
||||
if (burstcnt)
|
||||
return burstcnt;
|
||||
msleep(TPM_TIMEOUT);
|
||||
} while (time_before(jiffies, stop));
|
||||
|
||||
end:
|
||||
return -EBUSY;
|
||||
} /* get_burstcount() */
|
||||
|
||||
static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask,
|
||||
bool check_cancel, bool *canceled)
|
||||
{
|
||||
u8 status = chip->ops->status(chip);
|
||||
|
||||
*canceled = false;
|
||||
if ((status & mask) == mask)
|
||||
return true;
|
||||
if (check_cancel && chip->ops->req_canceled(chip, status)) {
|
||||
*canceled = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* interrupt_to_status
|
||||
* @param: irq_mask, the irq mask value to wait
|
||||
* @return: the corresponding tpm_sts value
|
||||
*/
|
||||
static u8 interrupt_to_status(u8 irq_mask)
|
||||
{
|
||||
u8 status = 0;
|
||||
|
||||
if ((irq_mask & TPM_INTF_STS_VALID_INT) == TPM_INTF_STS_VALID_INT)
|
||||
status |= TPM_STS_VALID;
|
||||
if ((irq_mask & TPM_INTF_DATA_AVAIL_INT) == TPM_INTF_DATA_AVAIL_INT)
|
||||
status |= TPM_STS_DATA_AVAIL;
|
||||
if ((irq_mask & TPM_INTF_CMD_READY_INT) == TPM_INTF_CMD_READY_INT)
|
||||
status |= TPM_STS_COMMAND_READY;
|
||||
|
||||
return status;
|
||||
} /* status_to_interrupt() */
|
||||
|
||||
/*
|
||||
* wait_for_stat wait for a TPM_STS value
|
||||
* @param: chip, the tpm chip description
|
||||
* @param: mask, the value mask to wait
|
||||
* @param: timeout, the timeout
|
||||
* @param: queue, the wait queue.
|
||||
* @param: check_cancel, does the command can be cancelled ?
|
||||
* @return: the tpm status, 0 if success, -ETIME if timeout is reached.
|
||||
*/
|
||||
static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
|
||||
wait_queue_head_t *queue, bool check_cancel)
|
||||
{
|
||||
unsigned long stop;
|
||||
int ret;
|
||||
bool canceled = false;
|
||||
bool condition;
|
||||
u32 cur_intrs;
|
||||
u8 interrupt, status;
|
||||
struct tpm_stm_dev *tpm_dev;
|
||||
|
||||
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
|
||||
|
||||
/* check current status */
|
||||
status = tpm_stm_i2c_status(chip);
|
||||
if ((status & mask) == mask)
|
||||
return 0;
|
||||
|
||||
stop = jiffies + timeout;
|
||||
|
||||
if (chip->vendor.irq) {
|
||||
cur_intrs = tpm_dev->intrs;
|
||||
interrupt = clear_interruption(tpm_dev);
|
||||
enable_irq(chip->vendor.irq);
|
||||
|
||||
again:
|
||||
timeout = stop - jiffies;
|
||||
if ((long) timeout <= 0)
|
||||
return -1;
|
||||
|
||||
ret = wait_event_interruptible_timeout(*queue,
|
||||
cur_intrs != tpm_dev->intrs, timeout);
|
||||
|
||||
interrupt |= clear_interruption(tpm_dev);
|
||||
status = interrupt_to_status(interrupt);
|
||||
condition = wait_for_tpm_stat_cond(chip, mask,
|
||||
check_cancel, &canceled);
|
||||
|
||||
if (ret >= 0 && condition) {
|
||||
if (canceled)
|
||||
return -ECANCELED;
|
||||
return 0;
|
||||
}
|
||||
if (ret == -ERESTARTSYS && freezing(current)) {
|
||||
clear_thread_flag(TIF_SIGPENDING);
|
||||
goto again;
|
||||
}
|
||||
disable_irq_nosync(chip->vendor.irq);
|
||||
|
||||
} else {
|
||||
do {
|
||||
msleep(TPM_TIMEOUT);
|
||||
status = chip->ops->status(chip);
|
||||
if ((status & mask) == mask)
|
||||
return 0;
|
||||
} while (time_before(jiffies, stop));
|
||||
}
|
||||
|
||||
return -ETIME;
|
||||
} /* wait_for_stat() */
|
||||
|
||||
/*
|
||||
* recv_data receive data
|
||||
* @param: chip, the tpm chip description
|
||||
* @param: buf, the buffer where the data are received
|
||||
* @param: count, the number of data to receive
|
||||
* @return: the number of bytes read from TPM FIFO.
|
||||
*/
|
||||
static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
{
|
||||
int size = 0, burstcnt, len, ret;
|
||||
struct tpm_stm_dev *tpm_dev;
|
||||
|
||||
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
|
||||
|
||||
while (size < count &&
|
||||
wait_for_stat(chip,
|
||||
TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
chip->vendor.timeout_c,
|
||||
&chip->vendor.read_queue, true) == 0) {
|
||||
burstcnt = get_burstcount(chip);
|
||||
if (burstcnt < 0)
|
||||
return burstcnt;
|
||||
len = min_t(int, burstcnt, count - size);
|
||||
ret = I2C_READ_DATA(tpm_dev, TPM_DATA_FIFO, buf + size, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
size += len;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* tpm_ioserirq_handler the serirq irq handler
|
||||
* @param: irq, the tpm chip description
|
||||
* @param: dev_id, the description of the chip
|
||||
* @return: the status of the handler.
|
||||
*/
|
||||
static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct tpm_chip *chip = dev_id;
|
||||
struct tpm_stm_dev *tpm_dev;
|
||||
|
||||
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
|
||||
|
||||
tpm_dev->intrs++;
|
||||
wake_up_interruptible(&chip->vendor.read_queue);
|
||||
disable_irq_nosync(chip->vendor.irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
} /* tpm_ioserirq_handler() */
|
||||
|
||||
|
||||
/*
|
||||
* tpm_stm_i2c_send send TPM commands through the I2C bus.
|
||||
*
|
||||
* @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h
|
||||
* @param: buf, the buffer to send.
|
||||
* @param: count, the number of bytes to send.
|
||||
* @return: In case of success the number of bytes sent.
|
||||
* In other case, a < 0 value describing the issue.
|
||||
*/
|
||||
static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf,
|
||||
size_t len)
|
||||
{
|
||||
u32 status, i, size;
|
||||
int burstcnt = 0;
|
||||
int ret;
|
||||
u8 data;
|
||||
struct i2c_client *client;
|
||||
struct tpm_stm_dev *tpm_dev;
|
||||
|
||||
if (!chip)
|
||||
return -EBUSY;
|
||||
if (len < TPM_HEADER_SIZE)
|
||||
return -EBUSY;
|
||||
|
||||
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
|
||||
client = tpm_dev->client;
|
||||
|
||||
client->flags = 0;
|
||||
|
||||
ret = request_locality(chip);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
status = tpm_stm_i2c_status(chip);
|
||||
if ((status & TPM_STS_COMMAND_READY) == 0) {
|
||||
tpm_stm_i2c_cancel(chip);
|
||||
if (wait_for_stat
|
||||
(chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
|
||||
&chip->vendor.read_queue, false) < 0) {
|
||||
ret = -ETIME;
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < len - 1;) {
|
||||
burstcnt = get_burstcount(chip);
|
||||
if (burstcnt < 0)
|
||||
return burstcnt;
|
||||
size = min_t(int, len - i - 1, burstcnt);
|
||||
ret = I2C_WRITE_DATA(tpm_dev, TPM_DATA_FIFO, buf + i, size);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
i += size;
|
||||
}
|
||||
|
||||
status = tpm_stm_i2c_status(chip);
|
||||
if ((status & TPM_STS_DATA_EXPECT) == 0) {
|
||||
ret = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
ret = I2C_WRITE_DATA(tpm_dev, TPM_DATA_FIFO, buf + len - 1, 1);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
status = tpm_stm_i2c_status(chip);
|
||||
if ((status & TPM_STS_DATA_EXPECT) != 0) {
|
||||
ret = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
data = TPM_STS_GO;
|
||||
I2C_WRITE_DATA(tpm_dev, TPM_STS, &data, 1);
|
||||
|
||||
return len;
|
||||
out_err:
|
||||
tpm_stm_i2c_cancel(chip);
|
||||
release_locality(chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* tpm_stm_i2c_recv received TPM response through the I2C bus.
|
||||
* @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h.
|
||||
* @param: buf, the buffer to store datas.
|
||||
* @param: count, the number of bytes to send.
|
||||
* @return: In case of success the number of bytes received.
|
||||
* In other case, a < 0 value describing the issue.
|
||||
*/
|
||||
static int tpm_stm_i2c_recv(struct tpm_chip *chip, unsigned char *buf,
|
||||
size_t count)
|
||||
{
|
||||
int size = 0;
|
||||
int expected;
|
||||
|
||||
if (!chip)
|
||||
return -EBUSY;
|
||||
|
||||
if (count < TPM_HEADER_SIZE) {
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size = recv_data(chip, buf, TPM_HEADER_SIZE);
|
||||
if (size < TPM_HEADER_SIZE) {
|
||||
dev_err(chip->pdev, "Unable to read header\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
expected = be32_to_cpu(*(__be32 *)(buf + 2));
|
||||
if (expected > count) {
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size += recv_data(chip, &buf[TPM_HEADER_SIZE],
|
||||
expected - TPM_HEADER_SIZE);
|
||||
if (size < expected) {
|
||||
dev_err(chip->pdev, "Unable to read remainder of result\n");
|
||||
size = -ETIME;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
chip->ops->cancel(chip);
|
||||
release_locality(chip);
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool tpm_stm_i2c_req_canceled(struct tpm_chip *chip, u8 status)
|
||||
{
|
||||
return (status == TPM_STS_COMMAND_READY);
|
||||
}
|
||||
|
||||
static const struct tpm_class_ops st_i2c_tpm = {
|
||||
.send = tpm_stm_i2c_send,
|
||||
.recv = tpm_stm_i2c_recv,
|
||||
.cancel = tpm_stm_i2c_cancel,
|
||||
.status = tpm_stm_i2c_status,
|
||||
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_canceled = tpm_stm_i2c_req_canceled,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int tpm_stm_i2c_of_request_resources(struct tpm_chip *chip)
|
||||
{
|
||||
struct device_node *pp;
|
||||
struct tpm_stm_dev *tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
|
||||
struct i2c_client *client = tpm_dev->client;
|
||||
int gpio;
|
||||
int ret;
|
||||
|
||||
pp = client->dev.of_node;
|
||||
if (!pp) {
|
||||
dev_err(chip->pdev, "No platform data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Get GPIO from device tree */
|
||||
gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0);
|
||||
if (gpio < 0) {
|
||||
dev_err(chip->pdev, "Failed to retrieve lpcpd-gpios from dts.\n");
|
||||
tpm_dev->io_lpcpd = -1;
|
||||
/*
|
||||
* lpcpd pin is not specified. This is not an issue as
|
||||
* power management can be also managed by TPM specific
|
||||
* commands. So leave with a success status code.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
/* GPIO request and configuration */
|
||||
ret = devm_gpio_request_one(&client->dev, gpio,
|
||||
GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD");
|
||||
if (ret) {
|
||||
dev_err(chip->pdev, "Failed to request lpcpd pin\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
tpm_dev->io_lpcpd = gpio;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int tpm_stm_i2c_of_request_resources(struct tpm_chip *chip)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int tpm_stm_i2c_request_resources(struct i2c_client *client,
|
||||
struct tpm_chip *chip)
|
||||
{
|
||||
struct st33zp24_platform_data *pdata;
|
||||
struct tpm_stm_dev *tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
|
||||
int ret;
|
||||
|
||||
pdata = client->dev.platform_data;
|
||||
if (!pdata) {
|
||||
dev_err(chip->pdev, "No platform data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* store for late use */
|
||||
tpm_dev->io_lpcpd = pdata->io_lpcpd;
|
||||
|
||||
if (gpio_is_valid(pdata->io_lpcpd)) {
|
||||
ret = devm_gpio_request_one(&client->dev,
|
||||
pdata->io_lpcpd, GPIOF_OUT_INIT_HIGH,
|
||||
"TPM IO_LPCPD");
|
||||
if (ret) {
|
||||
dev_err(chip->pdev, "%s : reset gpio_request failed\n",
|
||||
__FILE__);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* tpm_stm_i2c_probe initialize the TPM device
|
||||
* @param: client, the i2c_client drescription (TPM I2C description).
|
||||
* @param: id, the i2c_device_id struct.
|
||||
* @return: 0 in case of success.
|
||||
* -1 in other case.
|
||||
*/
|
||||
static int
|
||||
tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
u8 intmask = 0;
|
||||
struct tpm_chip *chip;
|
||||
struct st33zp24_platform_data *platform_data;
|
||||
struct tpm_stm_dev *tpm_dev;
|
||||
|
||||
if (!client) {
|
||||
pr_info("%s: i2c client is NULL. Device not accessible.\n",
|
||||
__func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_info(&client->dev, "client not i2c capable\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
tpm_dev = devm_kzalloc(&client->dev, sizeof(struct tpm_stm_dev),
|
||||
GFP_KERNEL);
|
||||
if (!tpm_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
chip = tpmm_chip_alloc(&client->dev, &st_i2c_tpm);
|
||||
if (IS_ERR(chip))
|
||||
return PTR_ERR(chip);
|
||||
|
||||
TPM_VPRIV(chip) = tpm_dev;
|
||||
tpm_dev->client = client;
|
||||
|
||||
platform_data = client->dev.platform_data;
|
||||
if (!platform_data && client->dev.of_node) {
|
||||
ret = tpm_stm_i2c_of_request_resources(chip);
|
||||
if (ret)
|
||||
goto _tpm_clean_answer;
|
||||
} else if (platform_data) {
|
||||
ret = tpm_stm_i2c_request_resources(client, chip);
|
||||
if (ret)
|
||||
goto _tpm_clean_answer;
|
||||
}
|
||||
|
||||
chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
|
||||
chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
|
||||
chip->vendor.locality = LOCALITY0;
|
||||
|
||||
if (client->irq) {
|
||||
/* INTERRUPT Setup */
|
||||
init_waitqueue_head(&chip->vendor.read_queue);
|
||||
tpm_dev->intrs = 0;
|
||||
|
||||
if (request_locality(chip) != LOCALITY0) {
|
||||
ret = -ENODEV;
|
||||
goto _tpm_clean_answer;
|
||||
}
|
||||
|
||||
clear_interruption(tpm_dev);
|
||||
ret = devm_request_irq(&client->dev, client->irq,
|
||||
tpm_ioserirq_handler,
|
||||
IRQF_TRIGGER_HIGH,
|
||||
"TPM SERIRQ management", chip);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->pdev, "TPM SERIRQ signals %d not available\n",
|
||||
client->irq);
|
||||
goto _tpm_clean_answer;
|
||||
}
|
||||
|
||||
intmask |= TPM_INTF_CMD_READY_INT
|
||||
| TPM_INTF_STS_VALID_INT
|
||||
| TPM_INTF_DATA_AVAIL_INT;
|
||||
|
||||
ret = I2C_WRITE_DATA(tpm_dev, TPM_INT_ENABLE, &intmask, 1);
|
||||
if (ret < 0)
|
||||
goto _tpm_clean_answer;
|
||||
|
||||
intmask = TPM_GLOBAL_INT_ENABLE;
|
||||
ret = I2C_WRITE_DATA(tpm_dev, (TPM_INT_ENABLE + 3),
|
||||
&intmask, 1);
|
||||
if (ret < 0)
|
||||
goto _tpm_clean_answer;
|
||||
|
||||
chip->vendor.irq = client->irq;
|
||||
|
||||
disable_irq_nosync(chip->vendor.irq);
|
||||
|
||||
tpm_gen_interrupt(chip);
|
||||
}
|
||||
|
||||
tpm_get_timeouts(chip);
|
||||
tpm_do_selftest(chip);
|
||||
|
||||
return tpm_chip_register(chip);
|
||||
_tpm_clean_answer:
|
||||
dev_info(chip->pdev, "TPM I2C initialisation fail\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* tpm_stm_i2c_remove remove the TPM device
|
||||
* @param: client, the i2c_client description (TPM I2C description).
|
||||
* @return: 0 in case of success.
|
||||
*/
|
||||
static int tpm_stm_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tpm_chip *chip =
|
||||
(struct tpm_chip *) i2c_get_clientdata(client);
|
||||
|
||||
if (chip)
|
||||
tpm_chip_unregister(chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/*
|
||||
* tpm_stm_i2c_pm_suspend suspend the TPM device
|
||||
* @param: client, the i2c_client drescription (TPM I2C description).
|
||||
* @param: mesg, the power management message.
|
||||
* @return: 0 in case of success.
|
||||
*/
|
||||
static int tpm_stm_i2c_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct st33zp24_platform_data *pin_infos = dev->platform_data;
|
||||
int ret = 0;
|
||||
|
||||
if (gpio_is_valid(pin_infos->io_lpcpd))
|
||||
gpio_set_value(pin_infos->io_lpcpd, 0);
|
||||
else
|
||||
ret = tpm_pm_suspend(dev);
|
||||
|
||||
return ret;
|
||||
} /* tpm_stm_i2c_suspend() */
|
||||
|
||||
/*
|
||||
* tpm_stm_i2c_pm_resume resume the TPM device
|
||||
* @param: client, the i2c_client drescription (TPM I2C description).
|
||||
* @return: 0 in case of success.
|
||||
*/
|
||||
static int tpm_stm_i2c_pm_resume(struct device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||
struct st33zp24_platform_data *pin_infos = dev->platform_data;
|
||||
|
||||
int ret = 0;
|
||||
|
||||
if (gpio_is_valid(pin_infos->io_lpcpd)) {
|
||||
gpio_set_value(pin_infos->io_lpcpd, 1);
|
||||
ret = wait_for_stat(chip,
|
||||
TPM_STS_VALID, chip->vendor.timeout_b,
|
||||
&chip->vendor.read_queue, false);
|
||||
} else {
|
||||
ret = tpm_pm_resume(dev);
|
||||
if (!ret)
|
||||
tpm_do_selftest(chip);
|
||||
}
|
||||
return ret;
|
||||
} /* tpm_stm_i2c_pm_resume() */
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id tpm_stm_i2c_id[] = {
|
||||
{TPM_ST33_I2C, 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tpm_stm_i2c_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_st33zp24_i2c_match[] = {
|
||||
{ .compatible = "st,st33zp24-i2c", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match);
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(tpm_stm_i2c_ops, tpm_stm_i2c_pm_suspend,
|
||||
tpm_stm_i2c_pm_resume);
|
||||
|
||||
static struct i2c_driver tpm_stm_i2c_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = TPM_ST33_I2C,
|
||||
.pm = &tpm_stm_i2c_ops,
|
||||
.of_match_table = of_match_ptr(of_st33zp24_i2c_match),
|
||||
},
|
||||
.probe = tpm_stm_i2c_probe,
|
||||
.remove = tpm_stm_i2c_remove,
|
||||
.id_table = tpm_stm_i2c_id
|
||||
};
|
||||
|
||||
module_i2c_driver(tpm_stm_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Christophe Ricard (tpmsupport@st.com)");
|
||||
MODULE_DESCRIPTION("STM TPM I2C ST33 Driver");
|
||||
MODULE_VERSION("1.2.1");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -591,27 +591,8 @@ static void tpm_inf_pnp_remove(struct pnp_dev *dev)
|
|||
}
|
||||
}
|
||||
|
||||
static int tpm_inf_pnp_suspend(struct pnp_dev *dev, pm_message_t pm_state)
|
||||
{
|
||||
struct tpm_chip *chip = pnp_get_drvdata(dev);
|
||||
int rc;
|
||||
if (chip) {
|
||||
u8 savestate[] = {
|
||||
0, 193, /* TPM_TAG_RQU_COMMAND */
|
||||
0, 0, 0, 10, /* blob length (in bytes) */
|
||||
0, 0, 0, 152 /* TPM_ORD_SaveState */
|
||||
};
|
||||
dev_info(&dev->dev, "saving TPM state\n");
|
||||
rc = tpm_inf_send(chip, savestate, sizeof(savestate));
|
||||
if (rc < 0) {
|
||||
dev_err(&dev->dev, "error while saving TPM state\n");
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_inf_pnp_resume(struct pnp_dev *dev)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tpm_inf_resume(struct device *dev)
|
||||
{
|
||||
/* Re-configure TPM after suspending */
|
||||
tpm_config_out(ENABLE_REGISTER_PAIR, TPM_INF_ADDR);
|
||||
|
@ -625,16 +606,19 @@ static int tpm_inf_pnp_resume(struct pnp_dev *dev)
|
|||
tpm_config_out(DISABLE_REGISTER_PAIR, TPM_INF_ADDR);
|
||||
/* disable RESET, LP and IRQC */
|
||||
tpm_data_out(RESET_LP_IRQC_DISABLE, CMD);
|
||||
return tpm_pm_resume(&dev->dev);
|
||||
return tpm_pm_resume(dev);
|
||||
}
|
||||
#endif
|
||||
static SIMPLE_DEV_PM_OPS(tpm_inf_pm, tpm_pm_suspend, tpm_inf_resume);
|
||||
|
||||
static struct pnp_driver tpm_inf_pnp_driver = {
|
||||
.name = "tpm_inf_pnp",
|
||||
.id_table = tpm_inf_pnp_tbl,
|
||||
.probe = tpm_inf_pnp_probe,
|
||||
.suspend = tpm_inf_pnp_suspend,
|
||||
.resume = tpm_inf_pnp_resume,
|
||||
.remove = tpm_inf_pnp_remove
|
||||
.remove = tpm_inf_pnp_remove,
|
||||
.driver = {
|
||||
.pm = &tpm_inf_pm,
|
||||
}
|
||||
};
|
||||
|
||||
module_pnp_driver(tpm_inf_pnp_driver);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24
|
||||
* Copyright (C) 2009, 2010 STMicroelectronics
|
||||
* STMicroelectronics TPM Linux driver for TPM 1.2 ST33ZP24
|
||||
* Copyright (C) 2009 - 2015 STMicroelectronics
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -14,20 +14,9 @@
|
|||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* STMicroelectronics version 1.2.0, Copyright (C) 2010
|
||||
* STMicroelectronics comes with ABSOLUTELY NO WARRANTY.
|
||||
* This is free software, and you are welcome to redistribute it
|
||||
* under certain conditions.
|
||||
*
|
||||
* @Author: Christophe RICARD tpmsupport@st.com
|
||||
*
|
||||
* @File: stm_st33_tpm.h
|
||||
*
|
||||
* @Date: 09/15/2010
|
||||
*/
|
||||
#ifndef __STM_ST33_TPM_H__
|
||||
#define __STM_ST33_TPM_H__
|
||||
#ifndef __ST33ZP24_H__
|
||||
#define __ST33ZP24_H__
|
||||
|
||||
#define TPM_ST33_I2C "st33zp24-i2c"
|
||||
#define TPM_ST33_SPI "st33zp24-spi"
|
||||
|
@ -36,4 +25,4 @@ struct st33zp24_platform_data {
|
|||
int io_lpcpd;
|
||||
};
|
||||
|
||||
#endif /* __STM_ST33_TPM_H__ */
|
||||
#endif /* __ST33ZP24_H__ */
|
|
@ -211,7 +211,7 @@ static inline void print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
|
|||
static void dump_common_audit_data(struct audit_buffer *ab,
|
||||
struct common_audit_data *a)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
char comm[sizeof(current->comm)];
|
||||
|
||||
/*
|
||||
* To keep stack sizes in check force programers to notice if they
|
||||
|
@ -220,8 +220,8 @@ static void dump_common_audit_data(struct audit_buffer *ab,
|
|||
*/
|
||||
BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2);
|
||||
|
||||
audit_log_format(ab, " pid=%d comm=", task_pid_nr(tsk));
|
||||
audit_log_untrustedstring(ab, tsk->comm);
|
||||
audit_log_format(ab, " pid=%d comm=", task_pid_nr(current));
|
||||
audit_log_untrustedstring(ab, memcpy(comm, current->comm, sizeof(comm)));
|
||||
|
||||
switch (a->type) {
|
||||
case LSM_AUDIT_DATA_NONE:
|
||||
|
@ -276,16 +276,19 @@ static void dump_common_audit_data(struct audit_buffer *ab,
|
|||
audit_log_format(ab, " ino=%lu", inode->i_ino);
|
||||
break;
|
||||
}
|
||||
case LSM_AUDIT_DATA_TASK:
|
||||
tsk = a->u.tsk;
|
||||
case LSM_AUDIT_DATA_TASK: {
|
||||
struct task_struct *tsk = a->u.tsk;
|
||||
if (tsk) {
|
||||
pid_t pid = task_pid_nr(tsk);
|
||||
if (pid) {
|
||||
char comm[sizeof(tsk->comm)];
|
||||
audit_log_format(ab, " pid=%d comm=", pid);
|
||||
audit_log_untrustedstring(ab, tsk->comm);
|
||||
audit_log_untrustedstring(ab,
|
||||
memcpy(comm, tsk->comm, sizeof(comm)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LSM_AUDIT_DATA_NET:
|
||||
if (a->u.net->sk) {
|
||||
struct sock *sk = a->u.net->sk;
|
||||
|
|
|
@ -724,12 +724,10 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
|
|||
rcu_read_lock();
|
||||
|
||||
node = avc_lookup(ssid, tsid, tclass);
|
||||
if (unlikely(!node)) {
|
||||
if (unlikely(!node))
|
||||
node = avc_compute_av(ssid, tsid, tclass, avd);
|
||||
} else {
|
||||
else
|
||||
memcpy(avd, &node->ae.avd, sizeof(*avd));
|
||||
avd = &node->ae.avd;
|
||||
}
|
||||
|
||||
denied = requested & ~(avd->allowed);
|
||||
if (unlikely(denied))
|
||||
|
|
|
@ -25,10 +25,43 @@
|
|||
|
||||
static struct kmem_cache *avtab_node_cachep;
|
||||
|
||||
static inline int avtab_hash(struct avtab_key *keyp, u16 mask)
|
||||
/* Based on MurmurHash3, written by Austin Appleby and placed in the
|
||||
* public domain.
|
||||
*/
|
||||
static inline int avtab_hash(struct avtab_key *keyp, u32 mask)
|
||||
{
|
||||
return ((keyp->target_class + (keyp->target_type << 2) +
|
||||
(keyp->source_type << 9)) & mask);
|
||||
static const u32 c1 = 0xcc9e2d51;
|
||||
static const u32 c2 = 0x1b873593;
|
||||
static const u32 r1 = 15;
|
||||
static const u32 r2 = 13;
|
||||
static const u32 m = 5;
|
||||
static const u32 n = 0xe6546b64;
|
||||
|
||||
u32 hash = 0;
|
||||
|
||||
#define mix(input) { \
|
||||
u32 v = input; \
|
||||
v *= c1; \
|
||||
v = (v << r1) | (v >> (32 - r1)); \
|
||||
v *= c2; \
|
||||
hash ^= v; \
|
||||
hash = (hash << r2) | (hash >> (32 - r2)); \
|
||||
hash = hash * m + n; \
|
||||
}
|
||||
|
||||
mix(keyp->target_class);
|
||||
mix(keyp->target_type);
|
||||
mix(keyp->source_type);
|
||||
|
||||
#undef mix
|
||||
|
||||
hash ^= hash >> 16;
|
||||
hash *= 0x85ebca6b;
|
||||
hash ^= hash >> 13;
|
||||
hash *= 0xc2b2ae35;
|
||||
hash ^= hash >> 16;
|
||||
|
||||
return hash & mask;
|
||||
}
|
||||
|
||||
static struct avtab_node*
|
||||
|
@ -46,8 +79,12 @@ avtab_insert_node(struct avtab *h, int hvalue,
|
|||
newnode->next = prev->next;
|
||||
prev->next = newnode;
|
||||
} else {
|
||||
newnode->next = h->htable[hvalue];
|
||||
h->htable[hvalue] = newnode;
|
||||
newnode->next = flex_array_get_ptr(h->htable, hvalue);
|
||||
if (flex_array_put_ptr(h->htable, hvalue, newnode,
|
||||
GFP_KERNEL|__GFP_ZERO)) {
|
||||
kmem_cache_free(avtab_node_cachep, newnode);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
h->nel++;
|
||||
|
@ -64,7 +101,7 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat
|
|||
return -EINVAL;
|
||||
|
||||
hvalue = avtab_hash(key, h->mask);
|
||||
for (prev = NULL, cur = h->htable[hvalue];
|
||||
for (prev = NULL, cur = flex_array_get_ptr(h->htable, hvalue);
|
||||
cur;
|
||||
prev = cur, cur = cur->next) {
|
||||
if (key->source_type == cur->key.source_type &&
|
||||
|
@ -104,7 +141,7 @@ avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datu
|
|||
if (!h || !h->htable)
|
||||
return NULL;
|
||||
hvalue = avtab_hash(key, h->mask);
|
||||
for (prev = NULL, cur = h->htable[hvalue];
|
||||
for (prev = NULL, cur = flex_array_get_ptr(h->htable, hvalue);
|
||||
cur;
|
||||
prev = cur, cur = cur->next) {
|
||||
if (key->source_type == cur->key.source_type &&
|
||||
|
@ -135,7 +172,8 @@ struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key)
|
|||
return NULL;
|
||||
|
||||
hvalue = avtab_hash(key, h->mask);
|
||||
for (cur = h->htable[hvalue]; cur; cur = cur->next) {
|
||||
for (cur = flex_array_get_ptr(h->htable, hvalue); cur;
|
||||
cur = cur->next) {
|
||||
if (key->source_type == cur->key.source_type &&
|
||||
key->target_type == cur->key.target_type &&
|
||||
key->target_class == cur->key.target_class &&
|
||||
|
@ -170,7 +208,8 @@ avtab_search_node(struct avtab *h, struct avtab_key *key)
|
|||
return NULL;
|
||||
|
||||
hvalue = avtab_hash(key, h->mask);
|
||||
for (cur = h->htable[hvalue]; cur; cur = cur->next) {
|
||||
for (cur = flex_array_get_ptr(h->htable, hvalue); cur;
|
||||
cur = cur->next) {
|
||||
if (key->source_type == cur->key.source_type &&
|
||||
key->target_type == cur->key.target_type &&
|
||||
key->target_class == cur->key.target_class &&
|
||||
|
@ -228,15 +267,14 @@ void avtab_destroy(struct avtab *h)
|
|||
return;
|
||||
|
||||
for (i = 0; i < h->nslot; i++) {
|
||||
cur = h->htable[i];
|
||||
cur = flex_array_get_ptr(h->htable, i);
|
||||
while (cur) {
|
||||
temp = cur;
|
||||
cur = cur->next;
|
||||
kmem_cache_free(avtab_node_cachep, temp);
|
||||
}
|
||||
h->htable[i] = NULL;
|
||||
}
|
||||
kfree(h->htable);
|
||||
flex_array_free(h->htable);
|
||||
h->htable = NULL;
|
||||
h->nslot = 0;
|
||||
h->mask = 0;
|
||||
|
@ -251,7 +289,7 @@ int avtab_init(struct avtab *h)
|
|||
|
||||
int avtab_alloc(struct avtab *h, u32 nrules)
|
||||
{
|
||||
u16 mask = 0;
|
||||
u32 mask = 0;
|
||||
u32 shift = 0;
|
||||
u32 work = nrules;
|
||||
u32 nslot = 0;
|
||||
|
@ -270,7 +308,8 @@ int avtab_alloc(struct avtab *h, u32 nrules)
|
|||
nslot = MAX_AVTAB_HASH_BUCKETS;
|
||||
mask = nslot - 1;
|
||||
|
||||
h->htable = kcalloc(nslot, sizeof(*(h->htable)), GFP_KERNEL);
|
||||
h->htable = flex_array_alloc(sizeof(struct avtab_node *), nslot,
|
||||
GFP_KERNEL | __GFP_ZERO);
|
||||
if (!h->htable)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -293,7 +332,7 @@ void avtab_hash_eval(struct avtab *h, char *tag)
|
|||
max_chain_len = 0;
|
||||
chain2_len_sum = 0;
|
||||
for (i = 0; i < h->nslot; i++) {
|
||||
cur = h->htable[i];
|
||||
cur = flex_array_get_ptr(h->htable, i);
|
||||
if (cur) {
|
||||
slots_used++;
|
||||
chain_len = 0;
|
||||
|
@ -534,7 +573,8 @@ int avtab_write(struct policydb *p, struct avtab *a, void *fp)
|
|||
return rc;
|
||||
|
||||
for (i = 0; i < a->nslot; i++) {
|
||||
for (cur = a->htable[i]; cur; cur = cur->next) {
|
||||
for (cur = flex_array_get_ptr(a->htable, i); cur;
|
||||
cur = cur->next) {
|
||||
rc = avtab_write_item(p, cur, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#ifndef _SS_AVTAB_H_
|
||||
#define _SS_AVTAB_H_
|
||||
|
||||
#include <linux/flex_array.h>
|
||||
|
||||
struct avtab_key {
|
||||
u16 source_type; /* source type */
|
||||
u16 target_type; /* target type */
|
||||
|
@ -51,10 +53,10 @@ struct avtab_node {
|
|||
};
|
||||
|
||||
struct avtab {
|
||||
struct avtab_node **htable;
|
||||
struct flex_array *htable;
|
||||
u32 nel; /* number of elements */
|
||||
u32 nslot; /* number of hash slots */
|
||||
u16 mask; /* mask to compute hash func */
|
||||
u32 mask; /* mask to compute hash func */
|
||||
|
||||
};
|
||||
|
||||
|
@ -84,7 +86,7 @@ struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified
|
|||
void avtab_cache_init(void);
|
||||
void avtab_cache_destroy(void);
|
||||
|
||||
#define MAX_AVTAB_HASH_BITS 11
|
||||
#define MAX_AVTAB_HASH_BITS 16
|
||||
#define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS)
|
||||
|
||||
#endif /* _SS_AVTAB_H_ */
|
||||
|
|
|
@ -654,19 +654,15 @@ int mls_import_netlbl_cat(struct context *context,
|
|||
|
||||
rc = ebitmap_netlbl_import(&context->range.level[0].cat,
|
||||
secattr->attr.mls.cat);
|
||||
if (rc != 0)
|
||||
goto import_netlbl_cat_failure;
|
||||
|
||||
rc = ebitmap_cpy(&context->range.level[1].cat,
|
||||
&context->range.level[0].cat);
|
||||
if (rc != 0)
|
||||
if (rc)
|
||||
goto import_netlbl_cat_failure;
|
||||
memcpy(&context->range.level[1].cat, &context->range.level[0].cat,
|
||||
sizeof(context->range.level[0].cat));
|
||||
|
||||
return 0;
|
||||
|
||||
import_netlbl_cat_failure:
|
||||
ebitmap_destroy(&context->range.level[0].cat);
|
||||
ebitmap_destroy(&context->range.level[1].cat);
|
||||
return rc;
|
||||
}
|
||||
#endif /* CONFIG_NETLABEL */
|
||||
|
|
|
@ -3179,13 +3179,9 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
|
|||
ctx_new.type = ctx->type;
|
||||
mls_import_netlbl_lvl(&ctx_new, secattr);
|
||||
if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
|
||||
rc = ebitmap_netlbl_import(&ctx_new.range.level[0].cat,
|
||||
secattr->attr.mls.cat);
|
||||
rc = mls_import_netlbl_cat(&ctx_new, secattr);
|
||||
if (rc)
|
||||
goto out;
|
||||
memcpy(&ctx_new.range.level[1].cat,
|
||||
&ctx_new.range.level[0].cat,
|
||||
sizeof(ctx_new.range.level[0].cat));
|
||||
}
|
||||
rc = -EIDRM;
|
||||
if (!mls_context_isvalid(&policydb, &ctx_new))
|
||||
|
|
|
@ -105,6 +105,7 @@ struct task_smack {
|
|||
#define SMK_INODE_INSTANT 0x01 /* inode is instantiated */
|
||||
#define SMK_INODE_TRANSMUTE 0x02 /* directory is transmuting */
|
||||
#define SMK_INODE_CHANGED 0x04 /* smack was transmuted */
|
||||
#define SMK_INODE_IMPURE 0x08 /* involved in an impure transaction */
|
||||
|
||||
/*
|
||||
* A label access rule.
|
||||
|
@ -193,6 +194,10 @@ struct smk_port_label {
|
|||
#define MAY_LOCK 0x00002000 /* Locks should be writes, but ... */
|
||||
#define MAY_BRINGUP 0x00004000 /* Report use of this rule */
|
||||
|
||||
#define SMACK_BRINGUP_ALLOW 1 /* Allow bringup mode */
|
||||
#define SMACK_UNCONFINED_SUBJECT 2 /* Allow unconfined label */
|
||||
#define SMACK_UNCONFINED_OBJECT 3 /* Allow unconfined label */
|
||||
|
||||
/*
|
||||
* Just to make the common cases easier to deal with
|
||||
*/
|
||||
|
@ -254,6 +259,9 @@ extern int smack_cipso_mapped;
|
|||
extern struct smack_known *smack_net_ambient;
|
||||
extern struct smack_known *smack_onlycap;
|
||||
extern struct smack_known *smack_syslog_label;
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
extern struct smack_known *smack_unconfined;
|
||||
#endif
|
||||
extern struct smack_known smack_cipso_option;
|
||||
extern int smack_ptrace_rule;
|
||||
|
||||
|
|
|
@ -130,7 +130,8 @@ int smk_access(struct smack_known *subject, struct smack_known *object,
|
|||
|
||||
/*
|
||||
* Hardcoded comparisons.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* A star subject can't access any object.
|
||||
*/
|
||||
if (subject == &smack_known_star) {
|
||||
|
@ -189,10 +190,20 @@ int smk_access(struct smack_known *subject, struct smack_known *object,
|
|||
* succeed because of "b" rules.
|
||||
*/
|
||||
if (may & MAY_BRINGUP)
|
||||
rc = MAY_BRINGUP;
|
||||
rc = SMACK_BRINGUP_ALLOW;
|
||||
#endif
|
||||
|
||||
out_audit:
|
||||
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
if (rc < 0) {
|
||||
if (object == smack_unconfined)
|
||||
rc = SMACK_UNCONFINED_OBJECT;
|
||||
if (subject == smack_unconfined)
|
||||
rc = SMACK_UNCONFINED_SUBJECT;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AUDIT
|
||||
if (a)
|
||||
smack_log(subject->smk_known, object->smk_known,
|
||||
|
@ -338,19 +349,16 @@ static void smack_log_callback(struct audit_buffer *ab, void *a)
|
|||
void smack_log(char *subject_label, char *object_label, int request,
|
||||
int result, struct smk_audit_info *ad)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
char request_buffer[SMK_NUM_ACCESS_TYPE + 5];
|
||||
#else
|
||||
char request_buffer[SMK_NUM_ACCESS_TYPE + 1];
|
||||
#endif
|
||||
struct smack_audit_data *sad;
|
||||
struct common_audit_data *a = &ad->a;
|
||||
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
/*
|
||||
* The result may be positive in bringup mode.
|
||||
*/
|
||||
if (result > 0)
|
||||
result = 0;
|
||||
#endif
|
||||
/* check if we have to log the current event */
|
||||
if (result != 0 && (log_policy & SMACK_AUDIT_DENIED) == 0)
|
||||
if (result < 0 && (log_policy & SMACK_AUDIT_DENIED) == 0)
|
||||
return;
|
||||
if (result == 0 && (log_policy & SMACK_AUDIT_ACCEPT) == 0)
|
||||
return;
|
||||
|
@ -364,6 +372,21 @@ void smack_log(char *subject_label, char *object_label, int request,
|
|||
smack_str_from_perm(request_buffer, request);
|
||||
sad->subject = subject_label;
|
||||
sad->object = object_label;
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
/*
|
||||
* The result may be positive in bringup mode.
|
||||
* A positive result is an allow, but not for normal reasons.
|
||||
* Mark it as successful, but don't filter it out even if
|
||||
* the logging policy says to do so.
|
||||
*/
|
||||
if (result == SMACK_UNCONFINED_SUBJECT)
|
||||
strcat(request_buffer, "(US)");
|
||||
else if (result == SMACK_UNCONFINED_OBJECT)
|
||||
strcat(request_buffer, "(UO)");
|
||||
|
||||
if (result > 0)
|
||||
result = 0;
|
||||
#endif
|
||||
sad->request = request_buffer;
|
||||
sad->result = result;
|
||||
|
||||
|
|
|
@ -57,6 +57,13 @@ static struct kmem_cache *smack_inode_cache;
|
|||
int smack_enabled;
|
||||
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
static char *smk_bu_mess[] = {
|
||||
"Bringup Error", /* Unused */
|
||||
"Bringup", /* SMACK_BRINGUP_ALLOW */
|
||||
"Unconfined Subject", /* SMACK_UNCONFINED_SUBJECT */
|
||||
"Unconfined Object", /* SMACK_UNCONFINED_OBJECT */
|
||||
};
|
||||
|
||||
static void smk_bu_mode(int mode, char *s)
|
||||
{
|
||||
int i = 0;
|
||||
|
@ -87,9 +94,11 @@ static int smk_bu_note(char *note, struct smack_known *sskp,
|
|||
|
||||
if (rc <= 0)
|
||||
return rc;
|
||||
if (rc > SMACK_UNCONFINED_OBJECT)
|
||||
rc = 0;
|
||||
|
||||
smk_bu_mode(mode, acc);
|
||||
pr_info("Smack Bringup: (%s %s %s) %s\n",
|
||||
pr_info("Smack %s: (%s %s %s) %s\n", smk_bu_mess[rc],
|
||||
sskp->smk_known, oskp->smk_known, acc, note);
|
||||
return 0;
|
||||
}
|
||||
|
@ -106,9 +115,11 @@ static int smk_bu_current(char *note, struct smack_known *oskp,
|
|||
|
||||
if (rc <= 0)
|
||||
return rc;
|
||||
if (rc > SMACK_UNCONFINED_OBJECT)
|
||||
rc = 0;
|
||||
|
||||
smk_bu_mode(mode, acc);
|
||||
pr_info("Smack Bringup: (%s %s %s) %s %s\n",
|
||||
pr_info("Smack %s: (%s %s %s) %s %s\n", smk_bu_mess[rc],
|
||||
tsp->smk_task->smk_known, oskp->smk_known,
|
||||
acc, current->comm, note);
|
||||
return 0;
|
||||
|
@ -126,9 +137,11 @@ static int smk_bu_task(struct task_struct *otp, int mode, int rc)
|
|||
|
||||
if (rc <= 0)
|
||||
return rc;
|
||||
if (rc > SMACK_UNCONFINED_OBJECT)
|
||||
rc = 0;
|
||||
|
||||
smk_bu_mode(mode, acc);
|
||||
pr_info("Smack Bringup: (%s %s %s) %s to %s\n",
|
||||
pr_info("Smack %s: (%s %s %s) %s to %s\n", smk_bu_mess[rc],
|
||||
tsp->smk_task->smk_known, smk_task->smk_known, acc,
|
||||
current->comm, otp->comm);
|
||||
return 0;
|
||||
|
@ -141,14 +154,25 @@ static int smk_bu_task(struct task_struct *otp, int mode, int rc)
|
|||
static int smk_bu_inode(struct inode *inode, int mode, int rc)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct inode_smack *isp = inode->i_security;
|
||||
char acc[SMK_NUM_ACCESS_TYPE + 1];
|
||||
|
||||
if (isp->smk_flags & SMK_INODE_IMPURE)
|
||||
pr_info("Smack Unconfined Corruption: inode=(%s %ld) %s\n",
|
||||
inode->i_sb->s_id, inode->i_ino, current->comm);
|
||||
|
||||
if (rc <= 0)
|
||||
return rc;
|
||||
if (rc > SMACK_UNCONFINED_OBJECT)
|
||||
rc = 0;
|
||||
if (rc == SMACK_UNCONFINED_SUBJECT &&
|
||||
(mode & (MAY_WRITE | MAY_APPEND)))
|
||||
isp->smk_flags |= SMK_INODE_IMPURE;
|
||||
|
||||
smk_bu_mode(mode, acc);
|
||||
pr_info("Smack Bringup: (%s %s %s) inode=(%s %ld) %s\n",
|
||||
tsp->smk_task->smk_known, smk_of_inode(inode)->smk_known, acc,
|
||||
|
||||
pr_info("Smack %s: (%s %s %s) inode=(%s %ld) %s\n", smk_bu_mess[rc],
|
||||
tsp->smk_task->smk_known, isp->smk_inode->smk_known, acc,
|
||||
inode->i_sb->s_id, inode->i_ino, current->comm);
|
||||
return 0;
|
||||
}
|
||||
|
@ -162,13 +186,20 @@ static int smk_bu_file(struct file *file, int mode, int rc)
|
|||
struct task_smack *tsp = current_security();
|
||||
struct smack_known *sskp = tsp->smk_task;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct inode_smack *isp = inode->i_security;
|
||||
char acc[SMK_NUM_ACCESS_TYPE + 1];
|
||||
|
||||
if (isp->smk_flags & SMK_INODE_IMPURE)
|
||||
pr_info("Smack Unconfined Corruption: inode=(%s %ld) %s\n",
|
||||
inode->i_sb->s_id, inode->i_ino, current->comm);
|
||||
|
||||
if (rc <= 0)
|
||||
return rc;
|
||||
if (rc > SMACK_UNCONFINED_OBJECT)
|
||||
rc = 0;
|
||||
|
||||
smk_bu_mode(mode, acc);
|
||||
pr_info("Smack Bringup: (%s %s %s) file=(%s %ld %pD) %s\n",
|
||||
pr_info("Smack %s: (%s %s %s) file=(%s %ld %pD) %s\n", smk_bu_mess[rc],
|
||||
sskp->smk_known, smk_of_inode(inode)->smk_known, acc,
|
||||
inode->i_sb->s_id, inode->i_ino, file,
|
||||
current->comm);
|
||||
|
@ -185,13 +216,20 @@ static int smk_bu_credfile(const struct cred *cred, struct file *file,
|
|||
struct task_smack *tsp = cred->security;
|
||||
struct smack_known *sskp = tsp->smk_task;
|
||||
struct inode *inode = file->f_inode;
|
||||
struct inode_smack *isp = inode->i_security;
|
||||
char acc[SMK_NUM_ACCESS_TYPE + 1];
|
||||
|
||||
if (isp->smk_flags & SMK_INODE_IMPURE)
|
||||
pr_info("Smack Unconfined Corruption: inode=(%s %ld) %s\n",
|
||||
inode->i_sb->s_id, inode->i_ino, current->comm);
|
||||
|
||||
if (rc <= 0)
|
||||
return rc;
|
||||
if (rc > SMACK_UNCONFINED_OBJECT)
|
||||
rc = 0;
|
||||
|
||||
smk_bu_mode(mode, acc);
|
||||
pr_info("Smack Bringup: (%s %s %s) file=(%s %ld %pD) %s\n",
|
||||
pr_info("Smack %s: (%s %s %s) file=(%s %ld %pD) %s\n", smk_bu_mess[rc],
|
||||
sskp->smk_known, smk_of_inode(inode)->smk_known, acc,
|
||||
inode->i_sb->s_id, inode->i_ino, file,
|
||||
current->comm);
|
||||
|
@ -2449,7 +2487,21 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
|
|||
static int smack_socket_post_create(struct socket *sock, int family,
|
||||
int type, int protocol, int kern)
|
||||
{
|
||||
if (family != PF_INET || sock->sk == NULL)
|
||||
struct socket_smack *ssp;
|
||||
|
||||
if (sock->sk == NULL)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Sockets created by kernel threads receive web label.
|
||||
*/
|
||||
if (unlikely(current->flags & PF_KTHREAD)) {
|
||||
ssp = sock->sk->sk_security;
|
||||
ssp->smk_in = &smack_known_web;
|
||||
ssp->smk_out = &smack_known_web;
|
||||
}
|
||||
|
||||
if (family != PF_INET)
|
||||
return 0;
|
||||
/*
|
||||
* Set the outbound netlbl.
|
||||
|
@ -3983,6 +4035,36 @@ static int smack_key_permission(key_ref_t key_ref,
|
|||
rc = smk_bu_note("key access", tkp, keyp->security, request, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* smack_key_getsecurity - Smack label tagging the key
|
||||
* @key points to the key to be queried
|
||||
* @_buffer points to a pointer that should be set to point to the
|
||||
* resulting string (if no label or an error occurs).
|
||||
* Return the length of the string (including terminating NUL) or -ve if
|
||||
* an error.
|
||||
* May also return 0 (and a NULL buffer pointer) if there is no label.
|
||||
*/
|
||||
static int smack_key_getsecurity(struct key *key, char **_buffer)
|
||||
{
|
||||
struct smack_known *skp = key->security;
|
||||
size_t length;
|
||||
char *copy;
|
||||
|
||||
if (key->security == NULL) {
|
||||
*_buffer = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
copy = kstrdup(skp->smk_known, GFP_KERNEL);
|
||||
if (copy == NULL)
|
||||
return -ENOMEM;
|
||||
length = strlen(copy) + 1;
|
||||
|
||||
*_buffer = copy;
|
||||
return length;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_KEYS */
|
||||
|
||||
/*
|
||||
|
@ -4307,6 +4389,7 @@ struct security_operations smack_ops = {
|
|||
.key_alloc = smack_key_alloc,
|
||||
.key_free = smack_key_free,
|
||||
.key_permission = smack_key_permission,
|
||||
.key_getsecurity = smack_key_getsecurity,
|
||||
#endif /* CONFIG_KEYS */
|
||||
|
||||
/* Audit hooks */
|
||||
|
|
|
@ -54,6 +54,9 @@ enum smk_inos {
|
|||
SMK_CHANGE_RULE = 19, /* change or add rules (long labels) */
|
||||
SMK_SYSLOG = 20, /* change syslog label) */
|
||||
SMK_PTRACE = 21, /* set ptrace rule */
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
SMK_UNCONFINED = 22, /* define an unconfined label */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -61,7 +64,6 @@ enum smk_inos {
|
|||
*/
|
||||
static DEFINE_MUTEX(smack_cipso_lock);
|
||||
static DEFINE_MUTEX(smack_ambient_lock);
|
||||
static DEFINE_MUTEX(smack_syslog_lock);
|
||||
static DEFINE_MUTEX(smk_netlbladdr_lock);
|
||||
|
||||
/*
|
||||
|
@ -95,6 +97,16 @@ int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT;
|
|||
*/
|
||||
struct smack_known *smack_onlycap;
|
||||
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
/*
|
||||
* Allow one label to be unconfined. This is for
|
||||
* debugging and application bring-up purposes only.
|
||||
* It is bad and wrong, but everyone seems to expect
|
||||
* to have it.
|
||||
*/
|
||||
struct smack_known *smack_unconfined;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If this value is set restrict syslog use to the label specified.
|
||||
* It can be reset via smackfs/syslog
|
||||
|
@ -1717,6 +1729,85 @@ static const struct file_operations smk_onlycap_ops = {
|
|||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
/**
|
||||
* smk_read_unconfined - read() for smackfs/unconfined
|
||||
* @filp: file pointer, not actually used
|
||||
* @buf: where to put the result
|
||||
* @cn: maximum to send along
|
||||
* @ppos: where to start
|
||||
*
|
||||
* Returns number of bytes read or error code, as appropriate
|
||||
*/
|
||||
static ssize_t smk_read_unconfined(struct file *filp, char __user *buf,
|
||||
size_t cn, loff_t *ppos)
|
||||
{
|
||||
char *smack = "";
|
||||
ssize_t rc = -EINVAL;
|
||||
int asize;
|
||||
|
||||
if (*ppos != 0)
|
||||
return 0;
|
||||
|
||||
if (smack_unconfined != NULL)
|
||||
smack = smack_unconfined->smk_known;
|
||||
|
||||
asize = strlen(smack) + 1;
|
||||
|
||||
if (cn >= asize)
|
||||
rc = simple_read_from_buffer(buf, cn, ppos, smack, asize);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_write_unconfined - write() for smackfs/unconfined
|
||||
* @file: file pointer, not actually used
|
||||
* @buf: where to get the data from
|
||||
* @count: bytes sent
|
||||
* @ppos: where to start
|
||||
*
|
||||
* Returns number of bytes written or error code, as appropriate
|
||||
*/
|
||||
static ssize_t smk_write_unconfined(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char *data;
|
||||
int rc = count;
|
||||
|
||||
if (!smack_privileged(CAP_MAC_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
data = kzalloc(count + 1, GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Should the null string be passed in unset the unconfined value.
|
||||
* This seems like something to be careful with as usually
|
||||
* smk_import only expects to return NULL for errors. It
|
||||
* is usually the case that a nullstring or "\n" would be
|
||||
* bad to pass to smk_import but in fact this is useful here.
|
||||
*
|
||||
* smk_import will also reject a label beginning with '-',
|
||||
* so "-confine" will also work.
|
||||
*/
|
||||
if (copy_from_user(data, buf, count) != 0)
|
||||
rc = -EFAULT;
|
||||
else
|
||||
smack_unconfined = smk_import_entry(data, count);
|
||||
|
||||
kfree(data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct file_operations smk_unconfined_ops = {
|
||||
.read = smk_read_unconfined,
|
||||
.write = smk_write_unconfined,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
#endif /* CONFIG_SECURITY_SMACK_BRINGUP */
|
||||
|
||||
/**
|
||||
* smk_read_logging - read() for /smack/logging
|
||||
* @filp: file pointer, not actually used
|
||||
|
@ -2384,6 +2475,10 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
|
|||
"syslog", &smk_syslog_ops, S_IRUGO|S_IWUSR},
|
||||
[SMK_PTRACE] = {
|
||||
"ptrace", &smk_ptrace_ops, S_IRUGO|S_IWUSR},
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
[SMK_UNCONFINED] = {
|
||||
"unconfined", &smk_unconfined_ops, S_IRUGO|S_IWUSR},
|
||||
#endif
|
||||
/* last one */
|
||||
{""}
|
||||
};
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
builtin-policy.h
|
||||
policy/
|
||||
policy/*.conf
|
||||
|
|
|
@ -6,6 +6,7 @@ config SECURITY_TOMOYO
|
|||
select SECURITY_PATH
|
||||
select SECURITY_NETWORK
|
||||
select SRCU
|
||||
select BUILD_BIN2C
|
||||
default n
|
||||
help
|
||||
This selects TOMOYO Linux, pathname-based access control.
|
||||
|
|
|
@ -1,48 +1,15 @@
|
|||
obj-y = audit.o common.o condition.o domain.o environ.o file.o gc.o group.o load_policy.o memory.o mount.o network.o realpath.o securityfs_if.o tomoyo.o util.o
|
||||
|
||||
$(obj)/policy/profile.conf:
|
||||
@mkdir -p $(obj)/policy/
|
||||
@echo Creating an empty policy/profile.conf
|
||||
@touch $@
|
||||
targets += builtin-policy.h
|
||||
define do_policy
|
||||
echo "static char tomoyo_builtin_$(1)[] __initdata ="; \
|
||||
$(objtree)/scripts/basic/bin2c <$(firstword $(wildcard $(obj)/policy/$(1).conf $(srctree)/$(src)/policy/$(1).conf.default) /dev/null); \
|
||||
echo ";"
|
||||
endef
|
||||
quiet_cmd_policy = POLICY $@
|
||||
cmd_policy = ($(call do_policy,profile); $(call do_policy,exception_policy); $(call do_policy,domain_policy); $(call do_policy,manager); $(call do_policy,stat)) >$@
|
||||
|
||||
$(obj)/policy/exception_policy.conf:
|
||||
@mkdir -p $(obj)/policy/
|
||||
@echo Creating a default policy/exception_policy.conf
|
||||
@echo initialize_domain /sbin/modprobe from any >> $@
|
||||
@echo initialize_domain /sbin/hotplug from any >> $@
|
||||
|
||||
$(obj)/policy/domain_policy.conf:
|
||||
@mkdir -p $(obj)/policy/
|
||||
@echo Creating an empty policy/domain_policy.conf
|
||||
@touch $@
|
||||
|
||||
$(obj)/policy/manager.conf:
|
||||
@mkdir -p $(obj)/policy/
|
||||
@echo Creating an empty policy/manager.conf
|
||||
@touch $@
|
||||
|
||||
$(obj)/policy/stat.conf:
|
||||
@mkdir -p $(obj)/policy/
|
||||
@echo Creating an empty policy/stat.conf
|
||||
@touch $@
|
||||
|
||||
$(obj)/builtin-policy.h: $(obj)/policy/profile.conf $(obj)/policy/exception_policy.conf $(obj)/policy/domain_policy.conf $(obj)/policy/manager.conf $(obj)/policy/stat.conf
|
||||
@echo Generating built-in policy for TOMOYO 2.5.x.
|
||||
@echo "static char tomoyo_builtin_profile[] __initdata =" > $@.tmp
|
||||
@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp
|
||||
@echo "\"\";" >> $@.tmp
|
||||
@echo "static char tomoyo_builtin_exception_policy[] __initdata =" >> $@.tmp
|
||||
@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/exception_policy.conf >> $@.tmp
|
||||
@echo "\"\";" >> $@.tmp
|
||||
@echo "static char tomoyo_builtin_domain_policy[] __initdata =" >> $@.tmp
|
||||
@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/domain_policy.conf >> $@.tmp
|
||||
@echo "\"\";" >> $@.tmp
|
||||
@echo "static char tomoyo_builtin_manager[] __initdata =" >> $@.tmp
|
||||
@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/manager.conf >> $@.tmp
|
||||
@echo "\"\";" >> $@.tmp
|
||||
@echo "static char tomoyo_builtin_stat[] __initdata =" >> $@.tmp
|
||||
@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/stat.conf >> $@.tmp
|
||||
@echo "\"\";" >> $@.tmp
|
||||
@mv $@.tmp $@
|
||||
$(obj)/builtin-policy.h: $(wildcard $(obj)/policy/*.conf $(src)/policy/*.conf.default) FORCE
|
||||
$(call if_changed,policy)
|
||||
|
||||
$(obj)/common.o: $(obj)/builtin-policy.h
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
initialize_domain /sbin/modprobe from any
|
||||
initialize_domain /sbin/hotplug from any
|
|
@ -1,8 +1,6 @@
|
|||
config SECURITY_YAMA
|
||||
bool "Yama support"
|
||||
depends on SECURITY
|
||||
select SECURITYFS
|
||||
select SECURITY_PATH
|
||||
default n
|
||||
help
|
||||
This selects Yama, which extends DAC support with additional
|
||||
|
|
|
@ -379,20 +379,17 @@ static struct security_operations yama_ops = {
|
|||
static int yama_dointvec_minmax(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
int rc;
|
||||
struct ctl_table table_copy;
|
||||
|
||||
if (write && !capable(CAP_SYS_PTRACE))
|
||||
return -EPERM;
|
||||
|
||||
rc = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Lock the max value if it ever gets set. */
|
||||
if (write && *(int *)table->data == *(int *)table->extra2)
|
||||
table->extra1 = table->extra2;
|
||||
table_copy = *table;
|
||||
if (*(int *)table_copy.data == *(int *)table_copy.extra2)
|
||||
table_copy.extra1 = table_copy.extra2;
|
||||
|
||||
return rc;
|
||||
return proc_dointvec_minmax(&table_copy, write, buffer, lenp, ppos);
|
||||
}
|
||||
|
||||
static int zero;
|
||||
|
|
Loading…
Reference in New Issue