Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI/TSM: Establish Secure Sessions and Link Encryption

The PCIe 7.0 specification, section 11, defines the Trusted Execution
Environment (TEE) Device Interface Security Protocol (TDISP). This
protocol definition builds upon Component Measurement and Authentication
(CMA), and link Integrity and Data Encryption (IDE). It adds support for
assigning devices (PCI physical or virtual function) to a confidential VM
such that the assigned device is enabled to access guest private memory
protected by technologies like Intel TDX, AMD SEV-SNP, RISCV COVE, or ARM
CCA.

The "TSM" (TEE Security Manager) is a concept in the TDISP specification
of an agent that mediates between a "DSM" (Device Security Manager) and
system software in both a VMM and a confidential VM. A VMM uses TSM ABIs
to setup link security and assign devices. A confidential VM uses TSM
ABIs to transition an assigned device into the TDISP "RUN" state and
validate its configuration. From a Linux perspective the TSM abstracts
many of the details of TDISP, IDE, and CMA. Some of those details leak
through at times, but for the most part TDISP is an internal
implementation detail of the TSM.

CONFIG_PCI_TSM adds an "authenticated" attribute and "tsm/" subdirectory
to pci-sysfs. Consider that the TSM driver may itself be a PCI driver.
Userspace can watch for the arrival of a "TSM" device,
/sys/class/tsm/tsm0/uevent KOBJ_CHANGE, to know when the PCI core has
initialized TSM services.

The operations that can be executed against a PCI device are split into
two mutually exclusive operation sets, "Link" and "Security" (struct
pci_tsm_{link,security}_ops). The "Link" operations manage physical link
security properties and communication with the device's Device Security
Manager firmware. These are the host side operations in TDISP. The
"Security" operations coordinate the security state of the assigned
virtual device (TDI). These are the guest side operations in TDISP.

Only "link" (Secure Session and physical Link Encryption) operations are
defined at this stage. There are placeholders for the device security
(Trusted Computing Base entry / exit) operations.

The locking allows for multiple devices to be executing commands
simultaneously, one outstanding command per-device and an rwsem
synchronizes the implementation relative to TSM registration/unregistration
events.

Thanks to Wu Hao for his work on an early draft of this support.

Cc: Lukas Wunner <lukas@wunner.de>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Alexey Kardashevskiy <aik@amd.com>
Co-developed-by: Xu Yilun <yilun.xu@linux.intel.com>
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Link: https://patch.msgid.link/20251031212902.2256310-5-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>

+971 -6
+51
Documentation/ABI/testing/sysfs-bus-pci
··· 621 621 number extended capability. The file is read only and due to 622 622 the possible sensitivity of accessible serial numbers, admin 623 623 only. 624 + 625 + What: /sys/bus/pci/devices/.../tsm/ 626 + Contact: linux-coco@lists.linux.dev 627 + Description: 628 + This directory only appears if a physical device function 629 + supports authentication (PCIe CMA-SPDM), interface security 630 + (PCIe TDISP), and is accepted for secure operation by the 631 + platform TSM driver. This attribute directory appears 632 + dynamically after the platform TSM driver loads. So, only after 633 + the /sys/class/tsm/tsm0 device arrives can tools assume that 634 + devices without a tsm/ attribute directory will never have one; 635 + before that, the security capabilities of the device relative to 636 + the platform TSM are unknown. See 637 + Documentation/ABI/testing/sysfs-class-tsm. 638 + 639 + What: /sys/bus/pci/devices/.../tsm/connect 640 + Contact: linux-coco@lists.linux.dev 641 + Description: 642 + (RW) Write the name of a TSM (TEE Security Manager) device from 643 + /sys/class/tsm to this file to establish a connection with the 644 + device. This typically includes an SPDM (DMTF Security 645 + Protocols and Data Models) session over PCIe DOE (Data Object 646 + Exchange) and may also include PCIe IDE (Integrity and Data 647 + Encryption) establishment. Reads from this attribute return the 648 + name of the connected TSM or the empty string if not 649 + connected. A TSM device signals its readiness to accept PCI 650 + connection via a KOBJ_CHANGE event. 651 + 652 + What: /sys/bus/pci/devices/.../tsm/disconnect 653 + Contact: linux-coco@lists.linux.dev 654 + Description: 655 + (WO) Write the name of the TSM device that was specified 656 + to 'connect' to teardown the connection. 657 + 658 + What: /sys/bus/pci/devices/.../authenticated 659 + Contact: linux-pci@vger.kernel.org 660 + Description: 661 + When the device's tsm/ directory is present device 662 + authentication (PCIe CMA-SPDM) and link encryption (PCIe IDE) 663 + are handled by the platform TSM (TEE Security Manager). When the 664 + tsm/ directory is not present this attribute reflects only the 665 + native CMA-SPDM authentication state with the kernel's 666 + certificate store. 667 + 668 + If the attribute is not present, it indicates that 669 + authentication is unsupported by the device, or the TSM has no 670 + available authentication methods for the device. 671 + 672 + When present and the tsm/ attribute directory is present, the 673 + authenticated attribute is an alias for the device 'connect' 674 + state. See the 'tsm/connect' attribute for more details.
+1
Documentation/driver-api/pci/index.rst
··· 10 10 11 11 pci 12 12 p2pdma 13 + tsm 13 14 14 15 .. only:: subproject and html 15 16
+21
Documentation/driver-api/pci/tsm.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + .. include:: <isonum.txt> 3 + 4 + ======================================================== 5 + PCI Trusted Execution Environment Security Manager (TSM) 6 + ======================================================== 7 + 8 + Subsystem Interfaces 9 + ==================== 10 + 11 + .. kernel-doc:: include/linux/pci-ide.h 12 + :internal: 13 + 14 + .. kernel-doc:: drivers/pci/ide.c 15 + :export: 16 + 17 + .. kernel-doc:: include/linux/pci-tsm.h 18 + :internal: 19 + 20 + .. kernel-doc:: drivers/pci/tsm.c 21 + :export:
+3 -1
MAINTAINERS
··· 26118 26118 S: Maintained 26119 26119 F: Documentation/ABI/testing/configfs-tsm-report 26120 26120 F: Documentation/driver-api/coco/ 26121 + F: Documentation/driver-api/pci/tsm.rst 26122 + F: drivers/pci/tsm.c 26121 26123 F: drivers/virt/coco/guest/ 26122 - F: include/linux/tsm*.h 26124 + F: include/linux/*tsm*.h 26123 26125 F: samples/tsm-mr/ 26124 26126 26125 26127 TRUSTED SERVICES TEE DRIVER
+15
drivers/pci/Kconfig
··· 125 125 config PCI_IDE 126 126 bool 127 127 128 + config PCI_TSM 129 + bool "PCI TSM: Device security protocol support" 130 + select PCI_IDE 131 + select PCI_DOE 132 + select TSM 133 + help 134 + The TEE (Trusted Execution Environment) Device Interface 135 + Security Protocol (TDISP) defines a "TSM" as a platform agent 136 + that manages device authentication, link encryption, link 137 + integrity protection, and assignment of PCI device functions 138 + (virtual or physical) to confidential computing VMs that can 139 + access (DMA) guest private memory. 140 + 141 + Enable a platform TSM driver to use this capability. 142 + 128 143 config PCI_DOE 129 144 bool "Enable PCI Data Object Exchange (DOE) support" 130 145 help
+1
drivers/pci/Makefile
··· 35 35 obj-$(CONFIG_VGA_ARB) += vgaarb.o 36 36 obj-$(CONFIG_PCI_DOE) += doe.o 37 37 obj-$(CONFIG_PCI_IDE) += ide.o 38 + obj-$(CONFIG_PCI_TSM) += tsm.o 38 39 obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o 39 40 obj-$(CONFIG_PCI_NPEM) += npem.o 40 41 obj-$(CONFIG_PCIE_TPH) += tph.o
-2
drivers/pci/doe.c
··· 24 24 25 25 #include "pci.h" 26 26 27 - #define PCI_DOE_FEATURE_DISCOVERY 0 28 - 29 27 /* Timeout of 1 second from 6.30.2 Operation, PCI Spec r6.0 */ 30 28 #define PCI_DOE_TIMEOUT HZ 31 29 #define PCI_DOE_POLL_INTERVAL (PCI_DOE_TIMEOUT / 128)
+4
drivers/pci/pci-sysfs.c
··· 1869 1869 #ifdef CONFIG_PCI_DOE 1870 1870 &pci_doe_sysfs_group, 1871 1871 #endif 1872 + #ifdef CONFIG_PCI_TSM 1873 + &pci_tsm_auth_attr_group, 1874 + &pci_tsm_attr_group, 1875 + #endif 1872 1876 NULL, 1873 1877 };
+10
drivers/pci/pci.h
··· 619 619 static inline void pci_ide_init(struct pci_dev *dev) { } 620 620 #endif 621 621 622 + #ifdef CONFIG_PCI_TSM 623 + void pci_tsm_init(struct pci_dev *pdev); 624 + void pci_tsm_destroy(struct pci_dev *pdev); 625 + extern const struct attribute_group pci_tsm_attr_group; 626 + extern const struct attribute_group pci_tsm_auth_attr_group; 627 + #else 628 + static inline void pci_tsm_init(struct pci_dev *pdev) { } 629 + static inline void pci_tsm_destroy(struct pci_dev *pdev) { } 630 + #endif 631 + 622 632 /** 623 633 * pci_dev_set_io_state - Set the new error state if possible. 624 634 *
+3
drivers/pci/probe.c
··· 2763 2763 ret = device_add(&dev->dev); 2764 2764 WARN_ON(ret < 0); 2765 2765 2766 + /* Establish pdev->tsm for newly added (e.g. new SR-IOV VFs) */ 2767 + pci_tsm_init(dev); 2768 + 2766 2769 pci_npem_create(dev); 2767 2770 2768 2771 pci_doe_sysfs_init(dev);
+6
drivers/pci/remove.c
··· 57 57 pci_doe_sysfs_teardown(dev); 58 58 pci_npem_remove(dev); 59 59 60 + /* 61 + * While device is in D0 drop the device from TSM link operations 62 + * including unbind and disconnect (IDE + SPDM teardown). 63 + */ 64 + pci_tsm_destroy(dev); 65 + 60 66 device_del(&dev->dev); 61 67 62 68 down_write(&pci_bus_sem);
+643
drivers/pci/tsm.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Interface with platform TEE Security Manager (TSM) objects as defined by 4 + * PCIe r7.0 section 11 TEE Device Interface Security Protocol (TDISP) 5 + * 6 + * Copyright(c) 2024-2025 Intel Corporation. All rights reserved. 7 + */ 8 + 9 + #define dev_fmt(fmt) "PCI/TSM: " fmt 10 + 11 + #include <linux/bitfield.h> 12 + #include <linux/pci.h> 13 + #include <linux/pci-doe.h> 14 + #include <linux/pci-tsm.h> 15 + #include <linux/sysfs.h> 16 + #include <linux/tsm.h> 17 + #include <linux/xarray.h> 18 + #include "pci.h" 19 + 20 + /* 21 + * Provide a read/write lock against the init / exit of pdev tsm 22 + * capabilities and arrival/departure of a TSM instance 23 + */ 24 + static DECLARE_RWSEM(pci_tsm_rwsem); 25 + 26 + /* 27 + * Count of TSMs registered that support physical link operations vs device 28 + * security state management. 29 + */ 30 + static int pci_tsm_link_count; 31 + static int pci_tsm_devsec_count; 32 + 33 + static const struct pci_tsm_ops *to_pci_tsm_ops(struct pci_tsm *tsm) 34 + { 35 + return tsm->tsm_dev->pci_ops; 36 + } 37 + 38 + static inline bool is_dsm(struct pci_dev *pdev) 39 + { 40 + return pdev->tsm && pdev->tsm->dsm_dev == pdev; 41 + } 42 + 43 + static inline bool has_tee(struct pci_dev *pdev) 44 + { 45 + return pdev->devcap & PCI_EXP_DEVCAP_TEE; 46 + } 47 + 48 + /* 'struct pci_tsm_pf0' wraps 'struct pci_tsm' when ->dsm_dev == ->pdev (self) */ 49 + static struct pci_tsm_pf0 *to_pci_tsm_pf0(struct pci_tsm *tsm) 50 + { 51 + /* 52 + * All "link" TSM contexts reference the device that hosts the DSM 53 + * interface for a set of devices. Walk to the DSM device and cast its 54 + * ->tsm context to a 'struct pci_tsm_pf0 *'. 55 + */ 56 + struct pci_dev *pf0 = tsm->dsm_dev; 57 + 58 + if (!is_pci_tsm_pf0(pf0) || !is_dsm(pf0)) { 59 + pci_WARN_ONCE(tsm->pdev, 1, "invalid context object\n"); 60 + return NULL; 61 + } 62 + 63 + return container_of(pf0->tsm, struct pci_tsm_pf0, base_tsm); 64 + } 65 + 66 + static void tsm_remove(struct pci_tsm *tsm) 67 + { 68 + struct pci_dev *pdev; 69 + 70 + if (!tsm) 71 + return; 72 + 73 + pdev = tsm->pdev; 74 + to_pci_tsm_ops(tsm)->remove(tsm); 75 + pdev->tsm = NULL; 76 + } 77 + DEFINE_FREE(tsm_remove, struct pci_tsm *, if (_T) tsm_remove(_T)) 78 + 79 + static void pci_tsm_walk_fns(struct pci_dev *pdev, 80 + int (*cb)(struct pci_dev *pdev, void *data), 81 + void *data) 82 + { 83 + /* Walk subordinate physical functions */ 84 + for (int i = 0; i < 8; i++) { 85 + struct pci_dev *pf __free(pci_dev_put) = pci_get_slot( 86 + pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), i)); 87 + 88 + if (!pf) 89 + continue; 90 + 91 + /* on entry function 0 has already run @cb */ 92 + if (i > 0) 93 + cb(pf, data); 94 + 95 + /* walk virtual functions of each pf */ 96 + for (int j = 0; j < pci_num_vf(pf); j++) { 97 + struct pci_dev *vf __free(pci_dev_put) = 98 + pci_get_domain_bus_and_slot( 99 + pci_domain_nr(pf->bus), 100 + pci_iov_virtfn_bus(pf, j), 101 + pci_iov_virtfn_devfn(pf, j)); 102 + 103 + if (!vf) 104 + continue; 105 + 106 + cb(vf, data); 107 + } 108 + } 109 + 110 + /* 111 + * Walk downstream devices, assumes that an upstream DSM is 112 + * limited to downstream physical functions 113 + */ 114 + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_UPSTREAM && is_dsm(pdev)) 115 + pci_walk_bus(pdev->subordinate, cb, data); 116 + } 117 + 118 + static void pci_tsm_walk_fns_reverse(struct pci_dev *pdev, 119 + int (*cb)(struct pci_dev *pdev, 120 + void *data), 121 + void *data) 122 + { 123 + /* Reverse walk downstream devices */ 124 + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_UPSTREAM && is_dsm(pdev)) 125 + pci_walk_bus_reverse(pdev->subordinate, cb, data); 126 + 127 + /* Reverse walk subordinate physical functions */ 128 + for (int i = 7; i >= 0; i--) { 129 + struct pci_dev *pf __free(pci_dev_put) = pci_get_slot( 130 + pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), i)); 131 + 132 + if (!pf) 133 + continue; 134 + 135 + /* reverse walk virtual functions */ 136 + for (int j = pci_num_vf(pf) - 1; j >= 0; j--) { 137 + struct pci_dev *vf __free(pci_dev_put) = 138 + pci_get_domain_bus_and_slot( 139 + pci_domain_nr(pf->bus), 140 + pci_iov_virtfn_bus(pf, j), 141 + pci_iov_virtfn_devfn(pf, j)); 142 + 143 + if (!vf) 144 + continue; 145 + cb(vf, data); 146 + } 147 + 148 + /* on exit, caller will run @cb on function 0 */ 149 + if (i > 0) 150 + cb(pf, data); 151 + } 152 + } 153 + 154 + static int probe_fn(struct pci_dev *pdev, void *dsm) 155 + { 156 + struct pci_dev *dsm_dev = dsm; 157 + const struct pci_tsm_ops *ops = to_pci_tsm_ops(dsm_dev->tsm); 158 + 159 + pdev->tsm = ops->probe(dsm_dev->tsm->tsm_dev, pdev); 160 + pci_dbg(pdev, "setup TSM context: DSM: %s status: %s\n", 161 + pci_name(dsm_dev), pdev->tsm ? "success" : "failed"); 162 + return 0; 163 + } 164 + 165 + static int pci_tsm_connect(struct pci_dev *pdev, struct tsm_dev *tsm_dev) 166 + { 167 + int rc; 168 + struct pci_tsm_pf0 *tsm_pf0; 169 + const struct pci_tsm_ops *ops = tsm_dev->pci_ops; 170 + struct pci_tsm *pci_tsm __free(tsm_remove) = ops->probe(tsm_dev, pdev); 171 + 172 + /* connect() mutually exclusive with subfunction pci_tsm_init() */ 173 + lockdep_assert_held_write(&pci_tsm_rwsem); 174 + 175 + if (!pci_tsm) 176 + return -ENXIO; 177 + 178 + pdev->tsm = pci_tsm; 179 + tsm_pf0 = to_pci_tsm_pf0(pdev->tsm); 180 + 181 + /* mutex_intr assumes connect() is always sysfs/user driven */ 182 + ACQUIRE(mutex_intr, lock)(&tsm_pf0->lock); 183 + if ((rc = ACQUIRE_ERR(mutex_intr, &lock))) 184 + return rc; 185 + 186 + rc = ops->connect(pdev); 187 + if (rc) 188 + return rc; 189 + 190 + pdev->tsm = no_free_ptr(pci_tsm); 191 + 192 + /* 193 + * Now that the DSM is established, probe() all the potential 194 + * dependent functions. Failure to probe a function is not fatal 195 + * to connect(), it just disables subsequent security operations 196 + * for that function. 197 + * 198 + * Note this is done unconditionally, without regard to finding 199 + * PCI_EXP_DEVCAP_TEE on the dependent function, for robustness. The DSM 200 + * is the ultimate arbiter of security state relative to a given 201 + * interface id, and if it says it can manage TDISP state of a function, 202 + * let it. 203 + */ 204 + if (has_tee(pdev)) 205 + pci_tsm_walk_fns(pdev, probe_fn, pdev); 206 + return 0; 207 + } 208 + 209 + static ssize_t connect_show(struct device *dev, struct device_attribute *attr, 210 + char *buf) 211 + { 212 + struct pci_dev *pdev = to_pci_dev(dev); 213 + struct tsm_dev *tsm_dev; 214 + int rc; 215 + 216 + ACQUIRE(rwsem_read_intr, lock)(&pci_tsm_rwsem); 217 + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &lock))) 218 + return rc; 219 + 220 + if (!pdev->tsm) 221 + return sysfs_emit(buf, "\n"); 222 + 223 + tsm_dev = pdev->tsm->tsm_dev; 224 + return sysfs_emit(buf, "%s\n", dev_name(&tsm_dev->dev)); 225 + } 226 + 227 + /* Is @tsm_dev managing physical link / session properties... */ 228 + static bool is_link_tsm(struct tsm_dev *tsm_dev) 229 + { 230 + return tsm_dev && tsm_dev->pci_ops && tsm_dev->pci_ops->link_ops.probe; 231 + } 232 + 233 + /* ...or is @tsm_dev managing device security state ? */ 234 + static bool is_devsec_tsm(struct tsm_dev *tsm_dev) 235 + { 236 + return tsm_dev && tsm_dev->pci_ops && tsm_dev->pci_ops->devsec_ops.lock; 237 + } 238 + 239 + static ssize_t connect_store(struct device *dev, struct device_attribute *attr, 240 + const char *buf, size_t len) 241 + { 242 + struct pci_dev *pdev = to_pci_dev(dev); 243 + int rc, id; 244 + 245 + rc = sscanf(buf, "tsm%d\n", &id); 246 + if (rc != 1) 247 + return -EINVAL; 248 + 249 + ACQUIRE(rwsem_write_kill, lock)(&pci_tsm_rwsem); 250 + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &lock))) 251 + return rc; 252 + 253 + if (pdev->tsm) 254 + return -EBUSY; 255 + 256 + struct tsm_dev *tsm_dev __free(put_tsm_dev) = find_tsm_dev(id); 257 + if (!is_link_tsm(tsm_dev)) 258 + return -ENXIO; 259 + 260 + rc = pci_tsm_connect(pdev, tsm_dev); 261 + if (rc) 262 + return rc; 263 + return len; 264 + } 265 + static DEVICE_ATTR_RW(connect); 266 + 267 + static int remove_fn(struct pci_dev *pdev, void *data) 268 + { 269 + tsm_remove(pdev->tsm); 270 + return 0; 271 + } 272 + 273 + static void __pci_tsm_disconnect(struct pci_dev *pdev) 274 + { 275 + struct pci_tsm_pf0 *tsm_pf0 = to_pci_tsm_pf0(pdev->tsm); 276 + const struct pci_tsm_ops *ops = to_pci_tsm_ops(pdev->tsm); 277 + 278 + /* disconnect() mutually exclusive with subfunction pci_tsm_init() */ 279 + lockdep_assert_held_write(&pci_tsm_rwsem); 280 + 281 + /* 282 + * disconnect() is uninterruptible as it may be called for device 283 + * teardown 284 + */ 285 + guard(mutex)(&tsm_pf0->lock); 286 + pci_tsm_walk_fns_reverse(pdev, remove_fn, NULL); 287 + ops->disconnect(pdev); 288 + } 289 + 290 + static void pci_tsm_disconnect(struct pci_dev *pdev) 291 + { 292 + __pci_tsm_disconnect(pdev); 293 + tsm_remove(pdev->tsm); 294 + } 295 + 296 + static ssize_t disconnect_store(struct device *dev, 297 + struct device_attribute *attr, const char *buf, 298 + size_t len) 299 + { 300 + struct pci_dev *pdev = to_pci_dev(dev); 301 + struct tsm_dev *tsm_dev; 302 + int rc; 303 + 304 + ACQUIRE(rwsem_write_kill, lock)(&pci_tsm_rwsem); 305 + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &lock))) 306 + return rc; 307 + 308 + if (!pdev->tsm) 309 + return -ENXIO; 310 + 311 + tsm_dev = pdev->tsm->tsm_dev; 312 + if (!sysfs_streq(buf, dev_name(&tsm_dev->dev))) 313 + return -EINVAL; 314 + 315 + pci_tsm_disconnect(pdev); 316 + return len; 317 + } 318 + static DEVICE_ATTR_WO(disconnect); 319 + 320 + /* The 'authenticated' attribute is exclusive to the presence of a 'link' TSM */ 321 + static bool pci_tsm_link_group_visible(struct kobject *kobj) 322 + { 323 + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 324 + 325 + return pci_tsm_link_count && is_pci_tsm_pf0(pdev); 326 + } 327 + DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(pci_tsm_link); 328 + 329 + /* 330 + * 'link' and 'devsec' TSMs share the same 'tsm/' sysfs group, so the TSM type 331 + * specific attributes need individual visibility checks. 332 + */ 333 + static umode_t pci_tsm_attr_visible(struct kobject *kobj, 334 + struct attribute *attr, int n) 335 + { 336 + if (pci_tsm_link_group_visible(kobj)) { 337 + if (attr == &dev_attr_connect.attr || 338 + attr == &dev_attr_disconnect.attr) 339 + return attr->mode; 340 + } 341 + 342 + return 0; 343 + } 344 + 345 + static bool pci_tsm_group_visible(struct kobject *kobj) 346 + { 347 + return pci_tsm_link_group_visible(kobj); 348 + } 349 + DEFINE_SYSFS_GROUP_VISIBLE(pci_tsm); 350 + 351 + static struct attribute *pci_tsm_attrs[] = { 352 + &dev_attr_connect.attr, 353 + &dev_attr_disconnect.attr, 354 + NULL 355 + }; 356 + 357 + const struct attribute_group pci_tsm_attr_group = { 358 + .name = "tsm", 359 + .attrs = pci_tsm_attrs, 360 + .is_visible = SYSFS_GROUP_VISIBLE(pci_tsm), 361 + }; 362 + 363 + static ssize_t authenticated_show(struct device *dev, 364 + struct device_attribute *attr, char *buf) 365 + { 366 + /* 367 + * When the SPDM session established via TSM the 'authenticated' state 368 + * of the device is identical to the connect state. 369 + */ 370 + return connect_show(dev, attr, buf); 371 + } 372 + static DEVICE_ATTR_RO(authenticated); 373 + 374 + static struct attribute *pci_tsm_auth_attrs[] = { 375 + &dev_attr_authenticated.attr, 376 + NULL 377 + }; 378 + 379 + const struct attribute_group pci_tsm_auth_attr_group = { 380 + .attrs = pci_tsm_auth_attrs, 381 + .is_visible = SYSFS_GROUP_VISIBLE(pci_tsm_link), 382 + }; 383 + 384 + /* 385 + * Retrieve physical function0 device whether it has TEE capability or not 386 + */ 387 + static struct pci_dev *pf0_dev_get(struct pci_dev *pdev) 388 + { 389 + struct pci_dev *pf_dev = pci_physfn(pdev); 390 + 391 + if (PCI_FUNC(pf_dev->devfn) == 0) 392 + return pci_dev_get(pf_dev); 393 + 394 + return pci_get_slot(pf_dev->bus, 395 + pf_dev->devfn - PCI_FUNC(pf_dev->devfn)); 396 + } 397 + 398 + /* 399 + * Find the PCI Device instance that serves as the Device Security Manager (DSM) 400 + * for @pdev. Note that no additional reference is held for the resulting device 401 + * because that resulting object always has a registered lifetime 402 + * greater-than-or-equal to that of the @pdev argument. This is by virtue of 403 + * @pdev being a descendant of, or identical to, the returned DSM device. 404 + */ 405 + static struct pci_dev *find_dsm_dev(struct pci_dev *pdev) 406 + { 407 + struct device *grandparent; 408 + struct pci_dev *uport; 409 + 410 + if (is_pci_tsm_pf0(pdev)) 411 + return pdev; 412 + 413 + struct pci_dev *pf0 __free(pci_dev_put) = pf0_dev_get(pdev); 414 + if (!pf0) 415 + return NULL; 416 + 417 + if (is_dsm(pf0)) 418 + return pf0; 419 + 420 + /* 421 + * For cases where a switch may be hosting TDISP services on behalf of 422 + * downstream devices, check the first upstream port relative to this 423 + * endpoint. 424 + */ 425 + if (!pdev->dev.parent) 426 + return NULL; 427 + grandparent = pdev->dev.parent->parent; 428 + if (!grandparent) 429 + return NULL; 430 + if (!dev_is_pci(grandparent)) 431 + return NULL; 432 + uport = to_pci_dev(grandparent); 433 + if (!pci_is_pcie(uport) || 434 + pci_pcie_type(uport) != PCI_EXP_TYPE_UPSTREAM) 435 + return NULL; 436 + 437 + if (is_dsm(uport)) 438 + return uport; 439 + return NULL; 440 + } 441 + 442 + /** 443 + * pci_tsm_link_constructor() - base 'struct pci_tsm' initialization for link TSMs 444 + * @pdev: The PCI device 445 + * @tsm: context to initialize 446 + * @tsm_dev: Platform TEE Security Manager, initiator of security operations 447 + */ 448 + int pci_tsm_link_constructor(struct pci_dev *pdev, struct pci_tsm *tsm, 449 + struct tsm_dev *tsm_dev) 450 + { 451 + if (!is_link_tsm(tsm_dev)) 452 + return -EINVAL; 453 + 454 + tsm->dsm_dev = find_dsm_dev(pdev); 455 + if (!tsm->dsm_dev) { 456 + pci_warn(pdev, "failed to find Device Security Manager\n"); 457 + return -ENXIO; 458 + } 459 + tsm->pdev = pdev; 460 + tsm->tsm_dev = tsm_dev; 461 + 462 + return 0; 463 + } 464 + EXPORT_SYMBOL_GPL(pci_tsm_link_constructor); 465 + 466 + /** 467 + * pci_tsm_pf0_constructor() - common 'struct pci_tsm_pf0' (DSM) initialization 468 + * @pdev: Physical Function 0 PCI device (as indicated by is_pci_tsm_pf0()) 469 + * @tsm: context to initialize 470 + * @tsm_dev: Platform TEE Security Manager, initiator of security operations 471 + */ 472 + int pci_tsm_pf0_constructor(struct pci_dev *pdev, struct pci_tsm_pf0 *tsm, 473 + struct tsm_dev *tsm_dev) 474 + { 475 + mutex_init(&tsm->lock); 476 + tsm->doe_mb = pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG, 477 + PCI_DOE_FEATURE_CMA); 478 + if (!tsm->doe_mb) { 479 + pci_warn(pdev, "TSM init failure, no CMA mailbox\n"); 480 + return -ENODEV; 481 + } 482 + 483 + return pci_tsm_link_constructor(pdev, &tsm->base_tsm, tsm_dev); 484 + } 485 + EXPORT_SYMBOL_GPL(pci_tsm_pf0_constructor); 486 + 487 + void pci_tsm_pf0_destructor(struct pci_tsm_pf0 *pf0_tsm) 488 + { 489 + mutex_destroy(&pf0_tsm->lock); 490 + } 491 + EXPORT_SYMBOL_GPL(pci_tsm_pf0_destructor); 492 + 493 + static void pf0_sysfs_enable(struct pci_dev *pdev) 494 + { 495 + bool tee = has_tee(pdev); 496 + 497 + pci_dbg(pdev, "Device Security Manager detected (%s%s%s)\n", 498 + pdev->ide_cap ? "IDE" : "", pdev->ide_cap && tee ? " " : "", 499 + tee ? "TEE" : ""); 500 + 501 + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group); 502 + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group); 503 + } 504 + 505 + int pci_tsm_register(struct tsm_dev *tsm_dev) 506 + { 507 + struct pci_dev *pdev = NULL; 508 + 509 + if (!tsm_dev) 510 + return -EINVAL; 511 + 512 + /* The TSM device must only implement one of link_ops or devsec_ops */ 513 + if (!is_link_tsm(tsm_dev) && !is_devsec_tsm(tsm_dev)) 514 + return -EINVAL; 515 + 516 + if (is_link_tsm(tsm_dev) && is_devsec_tsm(tsm_dev)) 517 + return -EINVAL; 518 + 519 + guard(rwsem_write)(&pci_tsm_rwsem); 520 + 521 + /* On first enable, update sysfs groups */ 522 + if (is_link_tsm(tsm_dev) && pci_tsm_link_count++ == 0) { 523 + for_each_pci_dev(pdev) 524 + if (is_pci_tsm_pf0(pdev)) 525 + pf0_sysfs_enable(pdev); 526 + } else if (is_devsec_tsm(tsm_dev)) { 527 + pci_tsm_devsec_count++; 528 + } 529 + 530 + return 0; 531 + } 532 + 533 + static void pci_tsm_fn_exit(struct pci_dev *pdev) 534 + { 535 + /* TODO: unbind the fn */ 536 + tsm_remove(pdev->tsm); 537 + } 538 + 539 + /** 540 + * __pci_tsm_destroy() - destroy the TSM context for @pdev 541 + * @pdev: device to cleanup 542 + * @tsm_dev: the TSM device being removed, or NULL if @pdev is being removed. 543 + * 544 + * At device removal or TSM unregistration all established context 545 + * with the TSM is torn down. Additionally, if there are no more TSMs 546 + * registered, the PCI tsm/ sysfs attributes are hidden. 547 + */ 548 + static void __pci_tsm_destroy(struct pci_dev *pdev, struct tsm_dev *tsm_dev) 549 + { 550 + struct pci_tsm *tsm = pdev->tsm; 551 + 552 + lockdep_assert_held_write(&pci_tsm_rwsem); 553 + 554 + /* 555 + * First, handle the TSM removal case to shutdown @pdev sysfs, this is 556 + * skipped if the device itself is being removed since sysfs goes away 557 + * naturally at that point 558 + */ 559 + if (is_link_tsm(tsm_dev) && is_pci_tsm_pf0(pdev) && !pci_tsm_link_count) { 560 + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group); 561 + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group); 562 + } 563 + 564 + /* Nothing else to do if this device never attached to the departing TSM */ 565 + if (!tsm) 566 + return; 567 + 568 + /* Now lookup the tsm_dev to destroy TSM context */ 569 + if (!tsm_dev) 570 + tsm_dev = tsm->tsm_dev; 571 + else if (tsm_dev != tsm->tsm_dev) 572 + return; 573 + 574 + if (is_link_tsm(tsm_dev) && is_pci_tsm_pf0(pdev)) 575 + pci_tsm_disconnect(pdev); 576 + else 577 + pci_tsm_fn_exit(pdev); 578 + } 579 + 580 + void pci_tsm_destroy(struct pci_dev *pdev) 581 + { 582 + guard(rwsem_write)(&pci_tsm_rwsem); 583 + __pci_tsm_destroy(pdev, NULL); 584 + } 585 + 586 + void pci_tsm_init(struct pci_dev *pdev) 587 + { 588 + guard(rwsem_read)(&pci_tsm_rwsem); 589 + 590 + /* 591 + * Subfunctions are either probed synchronous with connect() or later 592 + * when either the SR-IOV configuration is changed, or, unlikely, 593 + * connect() raced initial bus scanning. 594 + */ 595 + if (pdev->tsm) 596 + return; 597 + 598 + if (pci_tsm_link_count) { 599 + struct pci_dev *dsm = find_dsm_dev(pdev); 600 + 601 + if (!dsm) 602 + return; 603 + 604 + /* 605 + * The only path to init a Device Security Manager capable 606 + * device is via connect(). 607 + */ 608 + if (!dsm->tsm) 609 + return; 610 + 611 + probe_fn(pdev, dsm); 612 + } 613 + } 614 + 615 + void pci_tsm_unregister(struct tsm_dev *tsm_dev) 616 + { 617 + struct pci_dev *pdev = NULL; 618 + 619 + guard(rwsem_write)(&pci_tsm_rwsem); 620 + if (is_link_tsm(tsm_dev)) 621 + pci_tsm_link_count--; 622 + if (is_devsec_tsm(tsm_dev)) 623 + pci_tsm_devsec_count--; 624 + for_each_pci_dev_reverse(pdev) 625 + __pci_tsm_destroy(pdev, tsm_dev); 626 + } 627 + 628 + int pci_tsm_doe_transfer(struct pci_dev *pdev, u8 type, const void *req, 629 + size_t req_sz, void *resp, size_t resp_sz) 630 + { 631 + struct pci_tsm_pf0 *tsm; 632 + 633 + if (!pdev->tsm || !is_pci_tsm_pf0(pdev)) 634 + return -ENXIO; 635 + 636 + tsm = to_pci_tsm_pf0(pdev->tsm); 637 + if (!tsm->doe_mb) 638 + return -ENXIO; 639 + 640 + return pci_doe(tsm->doe_mb, PCI_VENDOR_ID_PCI_SIG, type, req, req_sz, 641 + resp, resp_sz); 642 + } 643 + EXPORT_SYMBOL_GPL(pci_tsm_doe_transfer);
+44 -2
drivers/virt/coco/tsm-core.c
··· 8 8 #include <linux/device.h> 9 9 #include <linux/module.h> 10 10 #include <linux/cleanup.h> 11 + #include <linux/pci-tsm.h> 11 12 12 13 static struct class *tsm_class; 13 14 static DECLARE_RWSEM(tsm_rwsem); 14 15 static DEFINE_IDA(tsm_ida); 16 + 17 + static int match_id(struct device *dev, const void *data) 18 + { 19 + struct tsm_dev *tsm_dev = container_of(dev, struct tsm_dev, dev); 20 + int id = *(const int *)data; 21 + 22 + return tsm_dev->id == id; 23 + } 24 + 25 + struct tsm_dev *find_tsm_dev(int id) 26 + { 27 + struct device *dev = class_find_device(tsm_class, NULL, &id, match_id); 28 + 29 + if (!dev) 30 + return NULL; 31 + return container_of(dev, struct tsm_dev, dev); 32 + } 15 33 16 34 static struct tsm_dev *alloc_tsm_dev(struct device *parent) 17 35 { ··· 54 36 return no_free_ptr(tsm_dev); 55 37 } 56 38 57 - struct tsm_dev *tsm_register(struct device *parent) 39 + static struct tsm_dev *tsm_register_pci_or_reset(struct tsm_dev *tsm_dev, 40 + struct pci_tsm_ops *pci_ops) 41 + { 42 + int rc; 43 + 44 + if (!pci_ops) 45 + return tsm_dev; 46 + 47 + tsm_dev->pci_ops = pci_ops; 48 + rc = pci_tsm_register(tsm_dev); 49 + if (rc) { 50 + dev_err(tsm_dev->dev.parent, 51 + "PCI/TSM registration failure: %d\n", rc); 52 + device_unregister(&tsm_dev->dev); 53 + return ERR_PTR(rc); 54 + } 55 + 56 + /* Notify TSM userspace that PCI/TSM operations are now possible */ 57 + kobject_uevent(&tsm_dev->dev.kobj, KOBJ_CHANGE); 58 + return tsm_dev; 59 + } 60 + 61 + struct tsm_dev *tsm_register(struct device *parent, struct pci_tsm_ops *pci_ops) 58 62 { 59 63 struct tsm_dev *tsm_dev __free(put_tsm_dev) = alloc_tsm_dev(parent); 60 64 struct device *dev; ··· 94 54 if (rc) 95 55 return ERR_PTR(rc); 96 56 97 - return no_free_ptr(tsm_dev); 57 + return tsm_register_pci_or_reset(no_free_ptr(tsm_dev), pci_ops); 98 58 } 99 59 EXPORT_SYMBOL_GPL(tsm_register); 100 60 101 61 void tsm_unregister(struct tsm_dev *tsm_dev) 102 62 { 63 + if (tsm_dev->pci_ops) 64 + pci_tsm_unregister(tsm_dev); 103 65 device_unregister(&tsm_dev->dev); 104 66 } 105 67 EXPORT_SYMBOL_GPL(tsm_unregister);
+4
include/linux/pci-doe.h
··· 15 15 16 16 struct pci_doe_mb; 17 17 18 + #define PCI_DOE_FEATURE_DISCOVERY 0 19 + #define PCI_DOE_FEATURE_CMA 1 20 + #define PCI_DOE_FEATURE_SSESSION 2 21 + 18 22 struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor, 19 23 u8 type); 20 24
+157
include/linux/pci-tsm.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef __PCI_TSM_H 3 + #define __PCI_TSM_H 4 + #include <linux/mutex.h> 5 + #include <linux/pci.h> 6 + 7 + struct pci_tsm; 8 + struct tsm_dev; 9 + 10 + /* 11 + * struct pci_tsm_ops - manage confidential links and security state 12 + * @link_ops: Coordinate PCIe SPDM and IDE establishment via a platform TSM. 13 + * Provide a secure session transport for TDISP state management 14 + * (typically bare metal physical function operations). 15 + * @devsec_ops: Lock, unlock, and interrogate the security state of the 16 + * function via the platform TSM (typically virtual function 17 + * operations). 18 + * 19 + * This operations are mutually exclusive either a tsm_dev instance 20 + * manages physical link properties or it manages function security 21 + * states like TDISP lock/unlock. 22 + */ 23 + struct pci_tsm_ops { 24 + /* 25 + * struct pci_tsm_link_ops - Manage physical link and the TSM/DSM session 26 + * @probe: establish context with the TSM (allocate / wrap 'struct 27 + * pci_tsm') for follow-on link operations 28 + * @remove: destroy link operations context 29 + * @connect: establish / validate a secure connection (e.g. IDE) 30 + * with the device 31 + * @disconnect: teardown the secure link 32 + * 33 + * Context: @probe, @remove, @connect, and @disconnect run under 34 + * pci_tsm_rwsem held for write to sync with TSM unregistration and 35 + * mutual exclusion of @connect and @disconnect. @connect and 36 + * @disconnect additionally run under the DSM lock (struct 37 + * pci_tsm_pf0::lock) as well as @probe and @remove of the subfunctions. 38 + */ 39 + struct_group_tagged(pci_tsm_link_ops, link_ops, 40 + struct pci_tsm *(*probe)(struct tsm_dev *tsm_dev, 41 + struct pci_dev *pdev); 42 + void (*remove)(struct pci_tsm *tsm); 43 + int (*connect)(struct pci_dev *pdev); 44 + void (*disconnect)(struct pci_dev *pdev); 45 + ); 46 + 47 + /* 48 + * struct pci_tsm_devsec_ops - Manage the security state of the function 49 + * @lock: establish context with the TSM (allocate / wrap 'struct 50 + * pci_tsm') for follow-on security state transitions from the 51 + * LOCKED state 52 + * @unlock: destroy TSM context and return device to UNLOCKED state 53 + * 54 + * Context: @lock and @unlock run under pci_tsm_rwsem held for write to 55 + * sync with TSM unregistration and each other 56 + */ 57 + struct_group_tagged(pci_tsm_devsec_ops, devsec_ops, 58 + struct pci_tsm *(*lock)(struct tsm_dev *tsm_dev, 59 + struct pci_dev *pdev); 60 + void (*unlock)(struct pci_tsm *tsm); 61 + ); 62 + }; 63 + 64 + /** 65 + * struct pci_tsm - Core TSM context for a given PCIe endpoint 66 + * @pdev: Back ref to device function, distinguishes type of pci_tsm context 67 + * @dsm_dev: PCI Device Security Manager for link operations on @pdev 68 + * @tsm_dev: PCI TEE Security Manager device for Link Confidentiality or Device 69 + * Function Security operations 70 + * 71 + * This structure is wrapped by low level TSM driver data and returned by 72 + * probe()/lock(), it is freed by the corresponding remove()/unlock(). 73 + * 74 + * For link operations it serves to cache the association between a Device 75 + * Security Manager (DSM) and the functions that manager can assign to a TVM. 76 + * That can be "self", for assigning function0 of a TEE I/O device, a 77 + * sub-function (SR-IOV virtual function, or non-function0 78 + * multifunction-device), or a downstream endpoint (PCIe upstream switch-port as 79 + * DSM). 80 + */ 81 + struct pci_tsm { 82 + struct pci_dev *pdev; 83 + struct pci_dev *dsm_dev; 84 + struct tsm_dev *tsm_dev; 85 + }; 86 + 87 + /** 88 + * struct pci_tsm_pf0 - Physical Function 0 TDISP link context 89 + * @base_tsm: generic core "tsm" context 90 + * @lock: mutual exclustion for pci_tsm_ops invocation 91 + * @doe_mb: PCIe Data Object Exchange mailbox 92 + */ 93 + struct pci_tsm_pf0 { 94 + struct pci_tsm base_tsm; 95 + struct mutex lock; 96 + struct pci_doe_mb *doe_mb; 97 + }; 98 + 99 + /* physical function0 and capable of 'connect' */ 100 + static inline bool is_pci_tsm_pf0(struct pci_dev *pdev) 101 + { 102 + if (!pdev) 103 + return false; 104 + 105 + if (!pci_is_pcie(pdev)) 106 + return false; 107 + 108 + if (pdev->is_virtfn) 109 + return false; 110 + 111 + /* 112 + * Allow for a Device Security Manager (DSM) associated with function0 113 + * of an Endpoint to coordinate TDISP requests for other functions 114 + * (physical or virtual) of the device, or allow for an Upstream Port 115 + * DSM to accept TDISP requests for the Endpoints downstream of the 116 + * switch. 117 + */ 118 + switch (pci_pcie_type(pdev)) { 119 + case PCI_EXP_TYPE_ENDPOINT: 120 + case PCI_EXP_TYPE_UPSTREAM: 121 + case PCI_EXP_TYPE_RC_END: 122 + if (pdev->ide_cap || (pdev->devcap & PCI_EXP_DEVCAP_TEE)) 123 + break; 124 + fallthrough; 125 + default: 126 + return false; 127 + } 128 + 129 + return PCI_FUNC(pdev->devfn) == 0; 130 + } 131 + 132 + #ifdef CONFIG_PCI_TSM 133 + int pci_tsm_register(struct tsm_dev *tsm_dev); 134 + void pci_tsm_unregister(struct tsm_dev *tsm_dev); 135 + int pci_tsm_link_constructor(struct pci_dev *pdev, struct pci_tsm *tsm, 136 + struct tsm_dev *tsm_dev); 137 + int pci_tsm_pf0_constructor(struct pci_dev *pdev, struct pci_tsm_pf0 *tsm, 138 + struct tsm_dev *tsm_dev); 139 + void pci_tsm_pf0_destructor(struct pci_tsm_pf0 *tsm); 140 + int pci_tsm_doe_transfer(struct pci_dev *pdev, u8 type, const void *req, 141 + size_t req_sz, void *resp, size_t resp_sz); 142 + #else 143 + static inline int pci_tsm_register(struct tsm_dev *tsm_dev) 144 + { 145 + return 0; 146 + } 147 + static inline void pci_tsm_unregister(struct tsm_dev *tsm_dev) 148 + { 149 + } 150 + static inline int pci_tsm_doe_transfer(struct pci_dev *pdev, u8 type, 151 + const void *req, size_t req_sz, 152 + void *resp, size_t resp_sz) 153 + { 154 + return -ENXIO; 155 + } 156 + #endif 157 + #endif /*__PCI_TSM_H */
+3
include/linux/pci.h
··· 547 547 unsigned int ide_cfg:1; /* Config cycles over IDE */ 548 548 unsigned int ide_tee_limit:1; /* Disallow T=0 traffic over IDE */ 549 549 #endif 550 + #ifdef CONFIG_PCI_TSM 551 + struct pci_tsm *tsm; /* TSM operation state */ 552 + #endif 550 553 u16 acs_cap; /* ACS Capability offset */ 551 554 u8 supported_speeds; /* Supported Link Speeds Vector */ 552 555 phys_addr_t rom; /* Physical address if not from BAR */
+4 -1
include/linux/tsm.h
··· 108 108 bool (*report_bin_attr_visible)(int n); 109 109 }; 110 110 111 + struct pci_tsm_ops; 111 112 struct tsm_dev { 112 113 struct device dev; 113 114 int id; 115 + const struct pci_tsm_ops *pci_ops; 114 116 }; 115 117 116 118 DEFINE_FREE(put_tsm_dev, struct tsm_dev *, ··· 120 118 121 119 int tsm_report_register(const struct tsm_report_ops *ops, void *priv); 122 120 int tsm_report_unregister(const struct tsm_report_ops *ops); 123 - struct tsm_dev *tsm_register(struct device *parent); 121 + struct tsm_dev *tsm_register(struct device *parent, struct pci_tsm_ops *ops); 124 122 void tsm_unregister(struct tsm_dev *tsm_dev); 123 + struct tsm_dev *find_tsm_dev(int id); 125 124 #endif /* __TSM_H */
+1
include/uapi/linux/pci_regs.h
··· 503 503 #define PCI_EXP_DEVCAP_PWR_VAL 0x03fc0000 /* Slot Power Limit Value */ 504 504 #define PCI_EXP_DEVCAP_PWR_SCL 0x0c000000 /* Slot Power Limit Scale */ 505 505 #define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */ 506 + #define PCI_EXP_DEVCAP_TEE 0x40000000 /* TEE I/O (TDISP) Support */ 506 507 #define PCI_EXP_DEVCTL 0x08 /* Device Control */ 507 508 #define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ 508 509 #define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */