TPM: Add new TPMs to the tail of the list to prevent inadvertent change of dev
[pandora-kernel.git] / drivers / char / tpm / tpm.c
index 361a1df..f7395c4 100644 (file)
@@ -533,11 +533,10 @@ EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
 void tpm_get_timeouts(struct tpm_chip *chip)
 {
        struct tpm_cmd_t tpm_cmd;
-       struct timeout_t *timeout_cap;
+       unsigned long new_timeout[4];
+       unsigned long old_timeout[4];
        struct duration_t *duration_cap;
        ssize_t rc;
-       u32 timeout;
-       unsigned int scale = 1;
 
        tpm_cmd.header.in = tpm_getcap_header;
        tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
@@ -554,25 +553,46 @@ void tpm_get_timeouts(struct tpm_chip *chip)
            != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32))
                return;
 
-       timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
-       /* Don't overwrite default if value is 0 */
-       timeout = be32_to_cpu(timeout_cap->a);
-       if (timeout && timeout < 1000) {
-               /* timeouts in msec rather usec */
-               scale = 1000;
-               chip->vendor.timeout_adjusted = true;
+       old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
+       old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
+       old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
+       old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
+       memcpy(new_timeout, old_timeout, sizeof(new_timeout));
+
+       /*
+        * Provide ability for vendor overrides of timeout values in case
+        * of misreporting.
+        */
+       if (chip->vendor.update_timeouts != NULL)
+               chip->vendor.timeout_adjusted =
+                       chip->vendor.update_timeouts(chip, new_timeout);
+
+       if (!chip->vendor.timeout_adjusted) {
+               /* Don't overwrite default if value is 0 */
+               if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
+                       int i;
+
+                       /* timeouts in msec rather usec */
+                       for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
+                               new_timeout[i] *= 1000;
+                       chip->vendor.timeout_adjusted = true;
+               }
        }
-       if (timeout)
-               chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale);
-       timeout = be32_to_cpu(timeout_cap->b);
-       if (timeout)
-               chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale);
-       timeout = be32_to_cpu(timeout_cap->c);
-       if (timeout)
-               chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale);
-       timeout = be32_to_cpu(timeout_cap->d);
-       if (timeout)
-               chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale);
+
+       /* Report adjusted timeouts */
+       if (chip->vendor.timeout_adjusted) {
+               dev_info(chip->dev,
+                        HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
+                        old_timeout[0], new_timeout[0],
+                        old_timeout[1], new_timeout[1],
+                        old_timeout[2], new_timeout[2],
+                        old_timeout[3], new_timeout[3]);
+       }
+
+       chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
+       chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
+       chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
+       chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
 
 duration:
        tpm_cmd.header.in = tpm_getcap_header;
@@ -1072,17 +1092,20 @@ ssize_t tpm_write(struct file *file, const char __user *buf,
                  size_t size, loff_t *off)
 {
        struct tpm_chip *chip = file->private_data;
-       size_t in_size = size, out_size;
+       size_t in_size = size;
+       ssize_t out_size;
 
        /* cannot perform a write until the read has cleared
-          either via tpm_read or a user_read_timer timeout */
-       while (atomic_read(&chip->data_pending) != 0)
-               msleep(TPM_TIMEOUT);
-
-       mutex_lock(&chip->buffer_mutex);
+          either via tpm_read or a user_read_timer timeout.
+          This also prevents splitted buffered writes from blocking here.
+       */
+       if (atomic_read(&chip->data_pending) != 0)
+               return -EBUSY;
 
        if (in_size > TPM_BUFSIZE)
-               in_size = TPM_BUFSIZE;
+               return -E2BIG;
+
+       mutex_lock(&chip->buffer_mutex);
 
        if (copy_from_user
            (chip->data_buffer, (void __user *) buf, in_size)) {
@@ -1092,6 +1115,10 @@ ssize_t tpm_write(struct file *file, const char __user *buf,
 
        /* atomic tpm command send and result receive */
        out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE);
+       if (out_size < 0) {
+               mutex_unlock(&chip->buffer_mutex);
+               return out_size;
+       }
 
        atomic_set(&chip->data_pending, out_size);
        mutex_unlock(&chip->buffer_mutex);
@@ -1115,12 +1142,13 @@ ssize_t tpm_read(struct file *file, char __user *buf,
        ret_size = atomic_read(&chip->data_pending);
        atomic_set(&chip->data_pending, 0);
        if (ret_size > 0) {     /* relay data */
+               ssize_t orig_ret_size = ret_size;
                if (size < ret_size)
                        ret_size = size;
 
                mutex_lock(&chip->buffer_mutex);
                rc = copy_to_user(buf, chip->data_buffer, ret_size);
-               memset(chip->data_buffer, 0, ret_size);
+               memset(chip->data_buffer, 0, orig_ret_size);
                if (rc)
                        ret_size = -EFAULT;
 
@@ -1313,7 +1341,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev,
 
        /* Make chip available */
        spin_lock(&driver_lock);
-       list_add_rcu(&chip->list, &tpm_chip_list);
+       list_add_tail_rcu(&chip->list, &tpm_chip_list);
        spin_unlock(&driver_lock);
 
        return chip;