summaryrefslogtreecommitdiffstats
path: root/drivers/thunderbolt/tb.h
diff options
context:
space:
mode:
authorMika Westerberg2017-06-06 14:25:17 +0200
committerGreg Kroah-Hartman2017-06-09 11:42:43 +0200
commite6b245ccd524441f462f1ca1fe726123dcedeeee (patch)
treee4b37479fce488999f3948c75e6f6c9e71e93440 /drivers/thunderbolt/tb.h
parentthunderbolt: Add support for Internal Connection Manager (ICM) (diff)
downloadkernel-qcow2-linux-e6b245ccd524441f462f1ca1fe726123dcedeeee.tar.gz
kernel-qcow2-linux-e6b245ccd524441f462f1ca1fe726123dcedeeee.tar.xz
kernel-qcow2-linux-e6b245ccd524441f462f1ca1fe726123dcedeeee.zip
thunderbolt: Add support for host and device NVM firmware upgrade
Starting from Intel Falcon Ridge the NVM firmware can be upgraded by using DMA configuration based mailbox commands. If we detect that the host or device (device support starts from Intel Alpine Ridge) has the DMA configuration based mailbox we expose NVM information to the userspace as two separate Linux NVMem devices: nvm_active and nvm_non_active. The former is read-only portion of the active NVM which firmware upgrade tools can be use to find out suitable NVM image if the device identification strings are not enough. The latter is write-only portion where the new NVM image is to be written by the userspace. It is up to the userspace to find out right NVM image (the kernel does very minimal validation). The ICM firmware itself authenticates the new NVM firmware and fails the operation if it is not what is expected. We also expose two new sysfs files per each switch: nvm_version and nvm_authenticate which can be used to read the active NVM version and start the upgrade process. We also introduce safe mode which is the mode a switch goes when it does not have properly authenticated firmware. In this mode the switch only accepts a couple of commands including flashing a new NVM firmware image and triggering power cycle. This code is based on the work done by Amir Levy and Michael Jamet. Signed-off-by: Michael Jamet <michael.jamet@intel.com> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Reviewed-by: Yehezkel Bernat <yehezkel.bernat@intel.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Andreas Noever <andreas.noever@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/thunderbolt/tb.h')
-rw-r--r--drivers/thunderbolt/tb.h40
1 files changed, 39 insertions, 1 deletions
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index a998b3a251d5..3d9f64676e58 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -7,6 +7,7 @@
#ifndef TB_H_
#define TB_H_
+#include <linux/nvmem-provider.h>
#include <linux/pci.h>
#include <linux/uuid.h>
@@ -15,6 +16,30 @@
#include "dma_port.h"
/**
+ * struct tb_switch_nvm - Structure holding switch NVM information
+ * @major: Major version number of the active NVM portion
+ * @minor: Minor version number of the active NVM portion
+ * @id: Identifier used with both NVM portions
+ * @active: Active portion NVMem device
+ * @non_active: Non-active portion NVMem device
+ * @buf: Buffer where the NVM image is stored before it is written to
+ * the actual NVM flash device
+ * @buf_data_size: Number of bytes actually consumed by the new NVM
+ * image
+ * @authenticating: The switch is authenticating the new NVM
+ */
+struct tb_switch_nvm {
+ u8 major;
+ u8 minor;
+ int id;
+ struct nvmem_device *active;
+ struct nvmem_device *non_active;
+ void *buf;
+ size_t buf_data_size;
+ bool authenticating;
+};
+
+/**
* enum tb_security_level - Thunderbolt security level
* @TB_SECURITY_NONE: No security, legacy mode
* @TB_SECURITY_USER: User approval required at minimum
@@ -39,7 +64,8 @@ enum tb_security_level {
* @ports: Ports in this switch
* @dma_port: If the switch has port supporting DMA configuration based
* mailbox this will hold the pointer to that (%NULL
- * otherwise).
+ * otherwise). If set it also means the switch has
+ * upgradeable NVM.
* @tb: Pointer to the domain the switch belongs to
* @uid: Unique ID of the switch
* @uuid: UUID of the switch (or %NULL if not supported)
@@ -51,6 +77,9 @@ enum tb_security_level {
* @cap_plug_events: Offset to the plug events capability (%0 if not found)
* @is_unplugged: The switch is going away
* @drom: DROM of the switch (%NULL if not found)
+ * @nvm: Pointer to the NVM if the switch has one (%NULL otherwise)
+ * @no_nvm_upgrade: Prevent NVM upgrade of this switch
+ * @safe_mode: The switch is in safe-mode
* @authorized: Whether the switch is authorized by user or policy
* @work: Work used to automatically authorize a switch
* @security_level: Switch supported security level
@@ -81,6 +110,9 @@ struct tb_switch {
int cap_plug_events;
bool is_unplugged;
u8 *drom;
+ struct tb_switch_nvm *nvm;
+ bool no_nvm_upgrade;
+ bool safe_mode;
unsigned int authorized;
struct work_struct work;
enum tb_security_level security_level;
@@ -172,6 +204,7 @@ struct tb_path {
* @approve_switch: Approve switch
* @add_switch_key: Add key to switch
* @challenge_switch_key: Challenge switch using key
+ * @disconnect_pcie_paths: Disconnects PCIe paths before NVM update
*/
struct tb_cm_ops {
int (*driver_ready)(struct tb *tb);
@@ -187,6 +220,7 @@ struct tb_cm_ops {
int (*add_switch_key)(struct tb *tb, struct tb_switch *sw);
int (*challenge_switch_key)(struct tb *tb, struct tb_switch *sw,
const u8 *challenge, u8 *response);
+ int (*disconnect_pcie_paths)(struct tb *tb);
};
/**
@@ -340,6 +374,7 @@ extern struct device_type tb_switch_type;
int tb_domain_init(void);
void tb_domain_exit(void);
+void tb_switch_exit(void);
struct tb *tb_domain_alloc(struct tb_nhi *nhi, size_t privsize);
int tb_domain_add(struct tb *tb);
@@ -351,6 +386,7 @@ void tb_domain_complete(struct tb *tb);
int tb_domain_approve_switch(struct tb *tb, struct tb_switch *sw);
int tb_domain_approve_switch_key(struct tb *tb, struct tb_switch *sw);
int tb_domain_challenge_switch_key(struct tb *tb, struct tb_switch *sw);
+int tb_domain_disconnect_pcie_paths(struct tb *tb);
static inline void tb_domain_put(struct tb *tb)
{
@@ -359,6 +395,8 @@ static inline void tb_domain_put(struct tb *tb)
struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent,
u64 route);
+struct tb_switch *tb_switch_alloc_safe_mode(struct tb *tb,
+ struct device *parent, u64 route);
int tb_switch_configure(struct tb_switch *sw);
int tb_switch_add(struct tb_switch *sw);
void tb_switch_remove(struct tb_switch *sw);