| 1 | |
| 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 |
|
| 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 |
|
| 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 |
|
| 92 | if (i > 0)
|
| 93 | cb(pf, data);
|
| 94 |
|
| 95 |
|
| 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 |
|
| 124 | if (pci_pcie_type(pdev) == PCI_EXP_TYPE_UPSTREAM && is_dsm(pdev))
|
| 125 | pci_walk_bus_reverse(pdev->subordinate, cb, data);
|
| 126 |
|
| 127 |
|
| 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 |
|
| 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 |
|
| 149 | if (i > 0)
|
| 150 | cb(pf, data);
|
| 151 | }
|
| 152 | }
|
| 153 |
|
| 154 | static void link_sysfs_disable(struct pci_dev *pdev)
|
| 155 | {
|
| 156 | sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group);
|
| 157 | sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group);
|
| 158 | }
|
| 159 |
|
| 160 | static void link_sysfs_enable(struct pci_dev *pdev)
|
| 161 | {
|
| 162 | bool tee = has_tee(pdev);
|
| 163 |
|
| 164 | pci_dbg(pdev, "%s Security Manager detected (%s%s%s)\n",
|
| 165 | pdev->tsm ? "Device" : "Platform TEE",
|
| 166 | pdev->ide_cap ? "IDE" : "", pdev->ide_cap && tee ? " " : "",
|
| 167 | tee ? "TEE" : "");
|
| 168 |
|
| 169 | sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group);
|
| 170 | sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group);
|
| 171 | }
|
| 172 |
|
| 173 | static int probe_fn(struct pci_dev *pdev, void *dsm)
|
| 174 | {
|
| 175 | struct pci_dev *dsm_dev = dsm;
|
| 176 | const struct pci_tsm_ops *ops = to_pci_tsm_ops(dsm_dev->tsm);
|
| 177 |
|
| 178 | pdev->tsm = ops->probe(dsm_dev->tsm->tsm_dev, pdev);
|
| 179 | pci_dbg(pdev, "setup TSM context: DSM: %s status: %s\n",
|
| 180 | pci_name(dsm_dev), pdev->tsm ? "success" : "failed");
|
| 181 | if (pdev->tsm)
|
| 182 | link_sysfs_enable(pdev);
|
| 183 | return 0;
|
| 184 | }
|
| 185 |
|
| 186 | static int pci_tsm_connect(struct pci_dev *pdev, struct tsm_dev *tsm_dev)
|
| 187 | {
|
| 188 | int rc;
|
| 189 | struct pci_tsm_pf0 *tsm_pf0;
|
| 190 | const struct pci_tsm_ops *ops = tsm_dev->pci_ops;
|
| 191 | struct pci_tsm *pci_tsm __free(tsm_remove) = ops->probe(tsm_dev, pdev);
|
| 192 |
|
| 193 |
|
| 194 | lockdep_assert_held_write(&pci_tsm_rwsem);
|
| 195 |
|
| 196 | if (!pci_tsm)
|
| 197 | return -ENXIO;
|
| 198 |
|
| 199 | pdev->tsm = pci_tsm;
|
| 200 | tsm_pf0 = to_pci_tsm_pf0(pdev->tsm);
|
| 201 |
|
| 202 |
|
| 203 | ACQUIRE(mutex_intr, lock)(&tsm_pf0->lock);
|
| 204 | if ((rc = ACQUIRE_ERR(mutex_intr, &lock)))
|
| 205 | return rc;
|
| 206 |
|
| 207 | rc = ops->connect(pdev);
|
| 208 | if (rc)
|
| 209 | return rc;
|
| 210 |
|
| 211 | pdev->tsm = no_free_ptr(pci_tsm);
|
| 212 |
|
| 213 | |
| 214 | * Now that the DSM is established, probe() all the potential
|
| 215 | * dependent functions. Failure to probe a function is not fatal
|
| 216 | * to connect(), it just disables subsequent security operations
|
| 217 | * for that function.
|
| 218 | *
|
| 219 | * Note this is done unconditionally, without regard to finding
|
| 220 | * PCI_EXP_DEVCAP_TEE on the dependent function, for robustness. The DSM
|
| 221 | * is the ultimate arbiter of security state relative to a given
|
| 222 | * interface id, and if it says it can manage TDISP state of a function,
|
| 223 | * let it.
|
| 224 |
|
| 225 | if (has_tee(pdev))
|
| 226 | pci_tsm_walk_fns(pdev, probe_fn, pdev);
|
| 227 | return 0;
|
| 228 | }
|
| 229 |
|
| 230 | static ssize_t connect_show(struct device *dev, struct device_attribute *attr,
|
| 231 | char *buf)
|
| 232 | {
|
| 233 | struct pci_dev *pdev = to_pci_dev(dev);
|
| 234 | struct tsm_dev *tsm_dev;
|
| 235 | int rc;
|
| 236 |
|
| 237 | ACQUIRE(rwsem_read_intr, lock)(&pci_tsm_rwsem);
|
| 238 | if ((rc = ACQUIRE_ERR(rwsem_read_intr, &lock)))
|
| 239 | return rc;
|
| 240 |
|
| 241 | if (!pdev->tsm)
|
| 242 | return sysfs_emit(buf, "\n");
|
| 243 |
|
| 244 | tsm_dev = pdev->tsm->tsm_dev;
|
| 245 | return sysfs_emit(buf, "%s\n", dev_name(&tsm_dev->dev));
|
| 246 | }
|
| 247 |
|
| 248 |
|
| 249 | static bool is_link_tsm(struct tsm_dev *tsm_dev)
|
| 250 | {
|
| 251 | return tsm_dev && tsm_dev->pci_ops && tsm_dev->pci_ops->link_ops.probe;
|
| 252 | }
|
| 253 |
|
| 254 |
|
| 255 | static bool is_devsec_tsm(struct tsm_dev *tsm_dev)
|
| 256 | {
|
| 257 | return tsm_dev && tsm_dev->pci_ops && tsm_dev->pci_ops->devsec_ops.lock;
|
| 258 | }
|
| 259 |
|
| 260 | static ssize_t connect_store(struct device *dev, struct device_attribute *attr,
|
| 261 | const char *buf, size_t len)
|
| 262 | {
|
| 263 | struct pci_dev *pdev = to_pci_dev(dev);
|
| 264 | int rc, id;
|
| 265 |
|
| 266 | rc = sscanf(buf, "tsm%d\n", &id);
|
| 267 | if (rc != 1)
|
| 268 | return -EINVAL;
|
| 269 |
|
| 270 | ACQUIRE(rwsem_write_kill, lock)(&pci_tsm_rwsem);
|
| 271 | if ((rc = ACQUIRE_ERR(rwsem_write_kill, &lock)))
|
| 272 | return rc;
|
| 273 |
|
| 274 | if (pdev->tsm)
|
| 275 | return -EBUSY;
|
| 276 |
|
| 277 | struct tsm_dev *tsm_dev __free(put_tsm_dev) = find_tsm_dev(id);
|
| 278 | if (!is_link_tsm(tsm_dev))
|
| 279 | return -ENXIO;
|
| 280 |
|
| 281 | rc = pci_tsm_connect(pdev, tsm_dev);
|
| 282 | if (rc)
|
| 283 | return rc;
|
| 284 | return len;
|
| 285 | }
|
| 286 | static DEVICE_ATTR_RW(connect);
|
| 287 |
|
| 288 | static int remove_fn(struct pci_dev *pdev, void *data)
|
| 289 | {
|
| 290 | tsm_remove(pdev->tsm);
|
| 291 | link_sysfs_disable(pdev);
|
| 292 | return 0;
|
| 293 | }
|
| 294 |
|
| 295 | |
| 296 | * Note, this helper only returns an error code and takes an argument for
|
| 297 | * compatibility with the pci_walk_bus() callback prototype. pci_tsm_unbind()
|
| 298 | * always succeeds.
|
| 299 |
|
| 300 | static int __pci_tsm_unbind(struct pci_dev *pdev, void *data)
|
| 301 | {
|
| 302 | struct pci_tdi *tdi;
|
| 303 | struct pci_tsm_pf0 *tsm_pf0;
|
| 304 |
|
| 305 | lockdep_assert_held(&pci_tsm_rwsem);
|
| 306 |
|
| 307 | if (!pdev->tsm)
|
| 308 | return 0;
|
| 309 |
|
| 310 | tsm_pf0 = to_pci_tsm_pf0(pdev->tsm);
|
| 311 | guard(mutex)(&tsm_pf0->lock);
|
| 312 |
|
| 313 | tdi = pdev->tsm->tdi;
|
| 314 | if (!tdi)
|
| 315 | return 0;
|
| 316 |
|
| 317 | to_pci_tsm_ops(pdev->tsm)->unbind(tdi);
|
| 318 | pdev->tsm->tdi = NULL;
|
| 319 |
|
| 320 | return 0;
|
| 321 | }
|
| 322 |
|
| 323 | void pci_tsm_unbind(struct pci_dev *pdev)
|
| 324 | {
|
| 325 | guard(rwsem_read)(&pci_tsm_rwsem);
|
| 326 | __pci_tsm_unbind(pdev, NULL);
|
| 327 | }
|
| 328 | EXPORT_SYMBOL_GPL(pci_tsm_unbind);
|
| 329 |
|
| 330 | |
| 331 | * pci_tsm_bind() - Bind @pdev as a TDI for @kvm
|
| 332 | * @pdev: PCI device function to bind
|
| 333 | * @kvm: Private memory attach context
|
| 334 | * @tdi_id: Identifier (virtual BDF) for the TDI as referenced by the TSM and DSM
|
| 335 | *
|
| 336 | * Returns 0 on success, or a negative error code on failure.
|
| 337 | *
|
| 338 | * Context: Caller is responsible for constraining the bind lifetime to the
|
| 339 | * registered state of the device. For example, pci_tsm_bind() /
|
| 340 | * pci_tsm_unbind() limited to the VFIO driver bound state of the device.
|
| 341 |
|
| 342 | int pci_tsm_bind(struct pci_dev *pdev, struct kvm *kvm, u32 tdi_id)
|
| 343 | {
|
| 344 | struct pci_tsm_pf0 *tsm_pf0;
|
| 345 | struct pci_tdi *tdi;
|
| 346 |
|
| 347 | if (!kvm)
|
| 348 | return -EINVAL;
|
| 349 |
|
| 350 | guard(rwsem_read)(&pci_tsm_rwsem);
|
| 351 |
|
| 352 | if (!pdev->tsm)
|
| 353 | return -EINVAL;
|
| 354 |
|
| 355 | if (!is_link_tsm(pdev->tsm->tsm_dev))
|
| 356 | return -ENXIO;
|
| 357 |
|
| 358 | tsm_pf0 = to_pci_tsm_pf0(pdev->tsm);
|
| 359 | guard(mutex)(&tsm_pf0->lock);
|
| 360 |
|
| 361 |
|
| 362 | if (pdev->tsm->tdi) {
|
| 363 | if (pdev->tsm->tdi->kvm != kvm)
|
| 364 | return -EBUSY;
|
| 365 | return 0;
|
| 366 | }
|
| 367 |
|
| 368 | tdi = to_pci_tsm_ops(pdev->tsm)->bind(pdev, kvm, tdi_id);
|
| 369 | if (IS_ERR(tdi))
|
| 370 | return PTR_ERR(tdi);
|
| 371 |
|
| 372 | pdev->tsm->tdi = tdi;
|
| 373 |
|
| 374 | return 0;
|
| 375 | }
|
| 376 | EXPORT_SYMBOL_GPL(pci_tsm_bind);
|
| 377 |
|
| 378 | |
| 379 | * pci_tsm_guest_req() - helper to marshal guest requests to the TSM driver
|
| 380 | * @pdev: @pdev representing a bound tdi
|
| 381 | * @scope: caller asserts this passthrough request is limited to TDISP operations
|
| 382 | * @req_in: Input payload forwarded from the guest
|
| 383 | * @in_len: Length of @req_in
|
| 384 | * @req_out: Output payload buffer response to the guest
|
| 385 | * @out_len: Length of @req_out on input, bytes filled in @req_out on output
|
| 386 | * @tsm_code: Optional TSM arch specific result code for the guest TSM
|
| 387 | *
|
| 388 | * This is a common entry point for requests triggered by userspace KVM-exit
|
| 389 | * service handlers responding to TDI information or state change requests. The
|
| 390 | * scope parameter limits requests to TDISP state management, or limited debug.
|
| 391 | * This path is only suitable for commands and results that are the host kernel
|
| 392 | * has no use, the host is only facilitating guest to TSM communication.
|
| 393 | *
|
| 394 | * Returns 0 on success and -error on failure and positive "residue" on success
|
| 395 | * but @req_out is filled with less then @out_len, or @req_out is NULL and a
|
| 396 | * residue number of bytes were not consumed from @req_in. On success or
|
| 397 | * failure @tsm_code may be populated with a TSM implementation specific result
|
| 398 | * code for the guest to consume.
|
| 399 | *
|
| 400 | * Context: Caller is responsible for calling this within the pci_tsm_bind()
|
| 401 | * state of the TDI.
|
| 402 |
|
| 403 | ssize_t pci_tsm_guest_req(struct pci_dev *pdev, enum pci_tsm_req_scope scope,
|
| 404 | sockptr_t req_in, size_t in_len, sockptr_t req_out,
|
| 405 | size_t out_len, u64 *tsm_code)
|
| 406 | {
|
| 407 | struct pci_tsm_pf0 *tsm_pf0;
|
| 408 | struct pci_tdi *tdi;
|
| 409 | int rc;
|
| 410 |
|
| 411 |
|
| 412 | if (scope > PCI_TSM_REQ_STATE_CHANGE)
|
| 413 | return -EINVAL;
|
| 414 |
|
| 415 | ACQUIRE(rwsem_read_intr, lock)(&pci_tsm_rwsem);
|
| 416 | if ((rc = ACQUIRE_ERR(rwsem_read_intr, &lock)))
|
| 417 | return rc;
|
| 418 |
|
| 419 | if (!pdev->tsm)
|
| 420 | return -ENXIO;
|
| 421 |
|
| 422 | if (!is_link_tsm(pdev->tsm->tsm_dev))
|
| 423 | return -ENXIO;
|
| 424 |
|
| 425 | tsm_pf0 = to_pci_tsm_pf0(pdev->tsm);
|
| 426 | ACQUIRE(mutex_intr, ops_lock)(&tsm_pf0->lock);
|
| 427 | if ((rc = ACQUIRE_ERR(mutex_intr, &ops_lock)))
|
| 428 | return rc;
|
| 429 |
|
| 430 | tdi = pdev->tsm->tdi;
|
| 431 | if (!tdi)
|
| 432 | return -ENXIO;
|
| 433 | return to_pci_tsm_ops(pdev->tsm)->guest_req(tdi, scope, req_in, in_len,
|
| 434 | req_out, out_len, tsm_code);
|
| 435 | }
|
| 436 | EXPORT_SYMBOL_GPL(pci_tsm_guest_req);
|
| 437 |
|
| 438 | static void pci_tsm_unbind_all(struct pci_dev *pdev)
|
| 439 | {
|
| 440 | pci_tsm_walk_fns_reverse(pdev, __pci_tsm_unbind, NULL);
|
| 441 | __pci_tsm_unbind(pdev, NULL);
|
| 442 | }
|
| 443 |
|
| 444 | static void __pci_tsm_disconnect(struct pci_dev *pdev)
|
| 445 | {
|
| 446 | struct pci_tsm_pf0 *tsm_pf0 = to_pci_tsm_pf0(pdev->tsm);
|
| 447 | const struct pci_tsm_ops *ops = to_pci_tsm_ops(pdev->tsm);
|
| 448 |
|
| 449 |
|
| 450 | lockdep_assert_held_write(&pci_tsm_rwsem);
|
| 451 |
|
| 452 | pci_tsm_unbind_all(pdev);
|
| 453 |
|
| 454 | |
| 455 | * disconnect() is uninterruptible as it may be called for device
|
| 456 | * teardown
|
| 457 |
|
| 458 | guard(mutex)(&tsm_pf0->lock);
|
| 459 | pci_tsm_walk_fns_reverse(pdev, remove_fn, NULL);
|
| 460 | ops->disconnect(pdev);
|
| 461 | }
|
| 462 |
|
| 463 | static void pci_tsm_disconnect(struct pci_dev *pdev)
|
| 464 | {
|
| 465 | __pci_tsm_disconnect(pdev);
|
| 466 | tsm_remove(pdev->tsm);
|
| 467 | }
|
| 468 |
|
| 469 | static ssize_t disconnect_store(struct device *dev,
|
| 470 | struct device_attribute *attr, const char *buf,
|
| 471 | size_t len)
|
| 472 | {
|
| 473 | struct pci_dev *pdev = to_pci_dev(dev);
|
| 474 | struct tsm_dev *tsm_dev;
|
| 475 | int rc;
|
| 476 |
|
| 477 | ACQUIRE(rwsem_write_kill, lock)(&pci_tsm_rwsem);
|
| 478 | if ((rc = ACQUIRE_ERR(rwsem_write_kill, &lock)))
|
| 479 | return rc;
|
| 480 |
|
| 481 | if (!pdev->tsm)
|
| 482 | return -ENXIO;
|
| 483 |
|
| 484 | tsm_dev = pdev->tsm->tsm_dev;
|
| 485 | if (!sysfs_streq(buf, dev_name(&tsm_dev->dev)))
|
| 486 | return -EINVAL;
|
| 487 |
|
| 488 | pci_tsm_disconnect(pdev);
|
| 489 | return len;
|
| 490 | }
|
| 491 | static DEVICE_ATTR_WO(disconnect);
|
| 492 |
|
| 493 | static ssize_t bound_show(struct device *dev,
|
| 494 | struct device_attribute *attr, char *buf)
|
| 495 | {
|
| 496 | struct pci_dev *pdev = to_pci_dev(dev);
|
| 497 | struct pci_tsm_pf0 *tsm_pf0;
|
| 498 | struct pci_tsm *tsm;
|
| 499 | int rc;
|
| 500 |
|
| 501 | ACQUIRE(rwsem_read_intr, lock)(&pci_tsm_rwsem);
|
| 502 | if ((rc = ACQUIRE_ERR(rwsem_read_intr, &lock)))
|
| 503 | return rc;
|
| 504 |
|
| 505 | tsm = pdev->tsm;
|
| 506 | if (!tsm)
|
| 507 | return sysfs_emit(buf, "\n");
|
| 508 | tsm_pf0 = to_pci_tsm_pf0(tsm);
|
| 509 |
|
| 510 | ACQUIRE(mutex_intr, ops_lock)(&tsm_pf0->lock);
|
| 511 | if ((rc = ACQUIRE_ERR(mutex_intr, &ops_lock)))
|
| 512 | return rc;
|
| 513 |
|
| 514 | if (!tsm->tdi)
|
| 515 | return sysfs_emit(buf, "\n");
|
| 516 | return sysfs_emit(buf, "%s\n", dev_name(&tsm->tsm_dev->dev));
|
| 517 | }
|
| 518 | static DEVICE_ATTR_RO(bound);
|
| 519 |
|
| 520 | static ssize_t dsm_show(struct device *dev, struct device_attribute *attr,
|
| 521 | char *buf)
|
| 522 | {
|
| 523 | struct pci_dev *pdev = to_pci_dev(dev);
|
| 524 | struct pci_tsm *tsm;
|
| 525 | int rc;
|
| 526 |
|
| 527 | ACQUIRE(rwsem_read_intr, lock)(&pci_tsm_rwsem);
|
| 528 | if ((rc = ACQUIRE_ERR(rwsem_read_intr, &lock)))
|
| 529 | return rc;
|
| 530 |
|
| 531 | tsm = pdev->tsm;
|
| 532 | if (!tsm)
|
| 533 | return sysfs_emit(buf, "\n");
|
| 534 |
|
| 535 | return sysfs_emit(buf, "%s\n", pci_name(tsm->dsm_dev));
|
| 536 | }
|
| 537 | static DEVICE_ATTR_RO(dsm);
|
| 538 |
|
| 539 |
|
| 540 | static bool pci_tsm_link_group_visible(struct kobject *kobj)
|
| 541 | {
|
| 542 | struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
|
| 543 |
|
| 544 | if (!pci_tsm_link_count)
|
| 545 | return false;
|
| 546 |
|
| 547 | if (!pci_is_pcie(pdev))
|
| 548 | return false;
|
| 549 |
|
| 550 | if (is_pci_tsm_pf0(pdev))
|
| 551 | return true;
|
| 552 |
|
| 553 | |
| 554 | * Show 'authenticated' and other attributes for the managed
|
| 555 | * sub-functions of a DSM.
|
| 556 |
|
| 557 | if (pdev->tsm)
|
| 558 | return true;
|
| 559 |
|
| 560 | return false;
|
| 561 | }
|
| 562 | DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(pci_tsm_link);
|
| 563 |
|
| 564 | |
| 565 | * 'link' and 'devsec' TSMs share the same 'tsm/' sysfs group, so the TSM type
|
| 566 | * specific attributes need individual visibility checks.
|
| 567 |
|
| 568 | static umode_t pci_tsm_attr_visible(struct kobject *kobj,
|
| 569 | struct attribute *attr, int n)
|
| 570 | {
|
| 571 | if (pci_tsm_link_group_visible(kobj)) {
|
| 572 | struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
|
| 573 |
|
| 574 | if (attr == &dev_attr_bound.attr) {
|
| 575 | if (is_pci_tsm_pf0(pdev) && has_tee(pdev))
|
| 576 | return attr->mode;
|
| 577 | if (pdev->tsm && has_tee(pdev->tsm->dsm_dev))
|
| 578 | return attr->mode;
|
| 579 | }
|
| 580 |
|
| 581 | if (attr == &dev_attr_dsm.attr) {
|
| 582 | if (is_pci_tsm_pf0(pdev))
|
| 583 | return attr->mode;
|
| 584 | if (pdev->tsm && has_tee(pdev->tsm->dsm_dev))
|
| 585 | return attr->mode;
|
| 586 | }
|
| 587 |
|
| 588 | if (attr == &dev_attr_connect.attr ||
|
| 589 | attr == &dev_attr_disconnect.attr) {
|
| 590 | if (is_pci_tsm_pf0(pdev))
|
| 591 | return attr->mode;
|
| 592 | }
|
| 593 | }
|
| 594 |
|
| 595 | return 0;
|
| 596 | }
|
| 597 |
|
| 598 | static bool pci_tsm_group_visible(struct kobject *kobj)
|
| 599 | {
|
| 600 | return pci_tsm_link_group_visible(kobj);
|
| 601 | }
|
| 602 | DEFINE_SYSFS_GROUP_VISIBLE(pci_tsm);
|
| 603 |
|
| 604 | static struct attribute *pci_tsm_attrs[] = {
|
| 605 | &dev_attr_connect.attr,
|
| 606 | &dev_attr_disconnect.attr,
|
| 607 | &dev_attr_bound.attr,
|
| 608 | &dev_attr_dsm.attr,
|
| 609 | NULL
|
| 610 | };
|
| 611 |
|
| 612 | const struct attribute_group pci_tsm_attr_group = {
|
| 613 | .name = "tsm",
|
| 614 | .attrs = pci_tsm_attrs,
|
| 615 | .is_visible = SYSFS_GROUP_VISIBLE(pci_tsm),
|
| 616 | };
|
| 617 |
|
| 618 | static ssize_t authenticated_show(struct device *dev,
|
| 619 | struct device_attribute *attr, char *buf)
|
| 620 | {
|
| 621 | |
| 622 | * When the SPDM session established via TSM the 'authenticated' state
|
| 623 | * of the device is identical to the connect state.
|
| 624 |
|
| 625 | return connect_show(dev, attr, buf);
|
| 626 | }
|
| 627 | static DEVICE_ATTR_RO(authenticated);
|
| 628 |
|
| 629 | static struct attribute *pci_tsm_auth_attrs[] = {
|
| 630 | &dev_attr_authenticated.attr,
|
| 631 | NULL
|
| 632 | };
|
| 633 |
|
| 634 | const struct attribute_group pci_tsm_auth_attr_group = {
|
| 635 | .attrs = pci_tsm_auth_attrs,
|
| 636 | .is_visible = SYSFS_GROUP_VISIBLE(pci_tsm_link),
|
| 637 | };
|
| 638 |
|
| 639 | |
| 640 | * Retrieve physical function0 device whether it has TEE capability or not
|
| 641 |
|
| 642 | static struct pci_dev *pf0_dev_get(struct pci_dev *pdev)
|
| 643 | {
|
| 644 | struct pci_dev *pf_dev = pci_physfn(pdev);
|
| 645 |
|
| 646 | if (PCI_FUNC(pf_dev->devfn) == 0)
|
| 647 | return pci_dev_get(pf_dev);
|
| 648 |
|
| 649 | return pci_get_slot(pf_dev->bus,
|
| 650 | pf_dev->devfn - PCI_FUNC(pf_dev->devfn));
|
| 651 | }
|
| 652 |
|
| 653 | |
| 654 | * Find the PCI Device instance that serves as the Device Security Manager (DSM)
|
| 655 | * for @pdev. Note that no additional reference is held for the resulting device
|
| 656 | * because that resulting object always has a registered lifetime
|
| 657 | * greater-than-or-equal to that of the @pdev argument. This is by virtue of
|
| 658 | * @pdev being a descendant of, or identical to, the returned DSM device.
|
| 659 |
|
| 660 | static struct pci_dev *find_dsm_dev(struct pci_dev *pdev)
|
| 661 | {
|
| 662 | struct device *grandparent;
|
| 663 | struct pci_dev *uport;
|
| 664 |
|
| 665 | if (is_pci_tsm_pf0(pdev))
|
| 666 | return pdev;
|
| 667 |
|
| 668 | struct pci_dev *pf0 __free(pci_dev_put) = pf0_dev_get(pdev);
|
| 669 | if (!pf0)
|
| 670 | return NULL;
|
| 671 |
|
| 672 | if (is_dsm(pf0))
|
| 673 | return pf0;
|
| 674 |
|
| 675 | |
| 676 | * For cases where a switch may be hosting TDISP services on behalf of
|
| 677 | * downstream devices, check the first upstream port relative to this
|
| 678 | * endpoint.
|
| 679 |
|
| 680 | if (!pdev->dev.parent)
|
| 681 | return NULL;
|
| 682 | grandparent = pdev->dev.parent->parent;
|
| 683 | if (!grandparent)
|
| 684 | return NULL;
|
| 685 | if (!dev_is_pci(grandparent))
|
| 686 | return NULL;
|
| 687 | uport = to_pci_dev(grandparent);
|
| 688 | if (!pci_is_pcie(uport) ||
|
| 689 | pci_pcie_type(uport) != PCI_EXP_TYPE_UPSTREAM)
|
| 690 | return NULL;
|
| 691 |
|
| 692 | if (is_dsm(uport))
|
| 693 | return uport;
|
| 694 | return NULL;
|
| 695 | }
|
| 696 |
|
| 697 | |
| 698 | * pci_tsm_tdi_constructor() - base 'struct pci_tdi' initialization for link TSMs
|
| 699 | * @pdev: PCI device function representing the TDI
|
| 700 | * @tdi: context to initialize
|
| 701 | * @kvm: Private memory attach context
|
| 702 | * @tdi_id: Identifier (virtual BDF) for the TDI as referenced by the TSM and DSM
|
| 703 |
|
| 704 | void pci_tsm_tdi_constructor(struct pci_dev *pdev, struct pci_tdi *tdi,
|
| 705 | struct kvm *kvm, u32 tdi_id)
|
| 706 | {
|
| 707 | tdi->pdev = pdev;
|
| 708 | tdi->kvm = kvm;
|
| 709 | tdi->tdi_id = tdi_id;
|
| 710 | }
|
| 711 | EXPORT_SYMBOL_GPL(pci_tsm_tdi_constructor);
|
| 712 |
|
| 713 | |
| 714 | * pci_tsm_link_constructor() - base 'struct pci_tsm' initialization for link TSMs
|
| 715 | * @pdev: The PCI device
|
| 716 | * @tsm: context to initialize
|
| 717 | * @tsm_dev: Platform TEE Security Manager, initiator of security operations
|
| 718 |
|
| 719 | int pci_tsm_link_constructor(struct pci_dev *pdev, struct pci_tsm *tsm,
|
| 720 | struct tsm_dev *tsm_dev)
|
| 721 | {
|
| 722 | if (!is_link_tsm(tsm_dev))
|
| 723 | return -EINVAL;
|
| 724 |
|
| 725 | tsm->dsm_dev = find_dsm_dev(pdev);
|
| 726 | if (!tsm->dsm_dev) {
|
| 727 | pci_warn(pdev, "failed to find Device Security Manager\n");
|
| 728 | return -ENXIO;
|
| 729 | }
|
| 730 | tsm->pdev = pdev;
|
| 731 | tsm->tsm_dev = tsm_dev;
|
| 732 |
|
| 733 | return 0;
|
| 734 | }
|
| 735 | EXPORT_SYMBOL_GPL(pci_tsm_link_constructor);
|
| 736 |
|
| 737 | |
| 738 | * pci_tsm_pf0_constructor() - common 'struct pci_tsm_pf0' (DSM) initialization
|
| 739 | * @pdev: Physical Function 0 PCI device (as indicated by is_pci_tsm_pf0())
|
| 740 | * @tsm: context to initialize
|
| 741 | * @tsm_dev: Platform TEE Security Manager, initiator of security operations
|
| 742 |
|
| 743 | int pci_tsm_pf0_constructor(struct pci_dev *pdev, struct pci_tsm_pf0 *tsm,
|
| 744 | struct tsm_dev *tsm_dev)
|
| 745 | {
|
| 746 | mutex_init(&tsm->lock);
|
| 747 | tsm->doe_mb = pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG,
|
| 748 | PCI_DOE_FEATURE_CMA);
|
| 749 | if (!tsm->doe_mb) {
|
| 750 | pci_warn(pdev, "TSM init failure, no CMA mailbox\n");
|
| 751 | return -ENODEV;
|
| 752 | }
|
| 753 |
|
| 754 | return pci_tsm_link_constructor(pdev, &tsm->base_tsm, tsm_dev);
|
| 755 | }
|
| 756 | EXPORT_SYMBOL_GPL(pci_tsm_pf0_constructor);
|
| 757 |
|
| 758 | void pci_tsm_pf0_destructor(struct pci_tsm_pf0 *pf0_tsm)
|
| 759 | {
|
| 760 | mutex_destroy(&pf0_tsm->lock);
|
| 761 | }
|
| 762 | EXPORT_SYMBOL_GPL(pci_tsm_pf0_destructor);
|
| 763 |
|
| 764 | int pci_tsm_register(struct tsm_dev *tsm_dev)
|
| 765 | {
|
| 766 | struct pci_dev *pdev = NULL;
|
| 767 |
|
| 768 | if (!tsm_dev)
|
| 769 | return -EINVAL;
|
| 770 |
|
| 771 |
|
| 772 | if (!is_link_tsm(tsm_dev) && !is_devsec_tsm(tsm_dev))
|
| 773 | return -EINVAL;
|
| 774 |
|
| 775 | if (is_link_tsm(tsm_dev) && is_devsec_tsm(tsm_dev))
|
| 776 | return -EINVAL;
|
| 777 |
|
| 778 | guard(rwsem_write)(&pci_tsm_rwsem);
|
| 779 |
|
| 780 |
|
| 781 | if (is_link_tsm(tsm_dev) && pci_tsm_link_count++ == 0) {
|
| 782 | for_each_pci_dev(pdev)
|
| 783 | if (is_pci_tsm_pf0(pdev))
|
| 784 | link_sysfs_enable(pdev);
|
| 785 | } else if (is_devsec_tsm(tsm_dev)) {
|
| 786 | pci_tsm_devsec_count++;
|
| 787 | }
|
| 788 |
|
| 789 | return 0;
|
| 790 | }
|
| 791 |
|
| 792 | static void pci_tsm_fn_exit(struct pci_dev *pdev)
|
| 793 | {
|
| 794 | __pci_tsm_unbind(pdev, NULL);
|
| 795 | tsm_remove(pdev->tsm);
|
| 796 | }
|
| 797 |
|
| 798 | |
| 799 | * __pci_tsm_destroy() - destroy the TSM context for @pdev
|
| 800 | * @pdev: device to cleanup
|
| 801 | * @tsm_dev: the TSM device being removed, or NULL if @pdev is being removed.
|
| 802 | *
|
| 803 | * At device removal or TSM unregistration all established context
|
| 804 | * with the TSM is torn down. Additionally, if there are no more TSMs
|
| 805 | * registered, the PCI tsm/ sysfs attributes are hidden.
|
| 806 |
|
| 807 | static void __pci_tsm_destroy(struct pci_dev *pdev, struct tsm_dev *tsm_dev)
|
| 808 | {
|
| 809 | struct pci_tsm *tsm = pdev->tsm;
|
| 810 |
|
| 811 | lockdep_assert_held_write(&pci_tsm_rwsem);
|
| 812 |
|
| 813 | |
| 814 | * First, handle the TSM removal case to shutdown @pdev sysfs, this is
|
| 815 | * skipped if the device itself is being removed since sysfs goes away
|
| 816 | * naturally at that point
|
| 817 |
|
| 818 | if (is_link_tsm(tsm_dev) && is_pci_tsm_pf0(pdev) && !pci_tsm_link_count)
|
| 819 | link_sysfs_disable(pdev);
|
| 820 |
|
| 821 |
|
| 822 | if (!tsm)
|
| 823 | return;
|
| 824 |
|
| 825 |
|
| 826 | if (!tsm_dev)
|
| 827 | tsm_dev = tsm->tsm_dev;
|
| 828 | else if (tsm_dev != tsm->tsm_dev)
|
| 829 | return;
|
| 830 |
|
| 831 | if (is_link_tsm(tsm_dev) && is_pci_tsm_pf0(pdev))
|
| 832 | pci_tsm_disconnect(pdev);
|
| 833 | else
|
| 834 | pci_tsm_fn_exit(pdev);
|
| 835 | }
|
| 836 |
|
| 837 | void pci_tsm_destroy(struct pci_dev *pdev)
|
| 838 | {
|
| 839 | guard(rwsem_write)(&pci_tsm_rwsem);
|
| 840 | __pci_tsm_destroy(pdev, NULL);
|
| 841 | }
|
| 842 |
|
| 843 | void pci_tsm_init(struct pci_dev *pdev)
|
| 844 | {
|
| 845 | guard(rwsem_read)(&pci_tsm_rwsem);
|
| 846 |
|
| 847 | |
| 848 | * Subfunctions are either probed synchronous with connect() or later
|
| 849 | * when either the SR-IOV configuration is changed, or, unlikely,
|
| 850 | * connect() raced initial bus scanning.
|
| 851 |
|
| 852 | if (pdev->tsm)
|
| 853 | return;
|
| 854 |
|
| 855 | if (pci_tsm_link_count) {
|
| 856 | struct pci_dev *dsm = find_dsm_dev(pdev);
|
| 857 |
|
| 858 | if (!dsm)
|
| 859 | return;
|
| 860 |
|
| 861 | |
| 862 | * The only path to init a Device Security Manager capable
|
| 863 | * device is via connect().
|
| 864 |
|
| 865 | if (!dsm->tsm)
|
| 866 | return;
|
| 867 |
|
| 868 | probe_fn(pdev, dsm);
|
| 869 | }
|
| 870 | }
|
| 871 |
|
| 872 | void pci_tsm_unregister(struct tsm_dev *tsm_dev)
|
| 873 | {
|
| 874 | struct pci_dev *pdev = NULL;
|
| 875 |
|
| 876 | guard(rwsem_write)(&pci_tsm_rwsem);
|
| 877 | if (is_link_tsm(tsm_dev))
|
| 878 | pci_tsm_link_count--;
|
| 879 | if (is_devsec_tsm(tsm_dev))
|
| 880 | pci_tsm_devsec_count--;
|
| 881 | for_each_pci_dev_reverse(pdev)
|
| 882 | __pci_tsm_destroy(pdev, tsm_dev);
|
| 883 | }
|
| 884 |
|
| 885 | int pci_tsm_doe_transfer(struct pci_dev *pdev, u8 type, const void *req,
|
| 886 | size_t req_sz, void *resp, size_t resp_sz)
|
| 887 | {
|
| 888 | struct pci_tsm_pf0 *tsm;
|
| 889 |
|
| 890 | if (!pdev->tsm || !is_pci_tsm_pf0(pdev))
|
| 891 | return -ENXIO;
|
| 892 |
|
| 893 | tsm = to_pci_tsm_pf0(pdev->tsm);
|
| 894 | if (!tsm->doe_mb)
|
| 895 | return -ENXIO;
|
| 896 |
|
| 897 | return pci_doe(tsm->doe_mb, PCI_VENDOR_ID_PCI_SIG, type, req, req_sz,
|
| 898 | resp, resp_sz);
|
| 899 | }
|
| 900 | EXPORT_SYMBOL_GPL(pci_tsm_doe_transfer);
|