1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Intel Vendor Specific Extended Capabilities auxiliary bus driver 4 * 5 * Copyright (c) 2021, Intel Corporation. 6 * All Rights Reserved. 7 * 8 * Author: David E. Box <david.e.box@linux.intel.com> 9 * 10 * This driver discovers and creates auxiliary devices for Intel defined PCIe 11 * "Vendor Specific" and "Designated Vendor Specific" Extended Capabilities, 12 * VSEC and DVSEC respectively. The driver supports features on specific PCIe 13 * endpoints that exist primarily to expose them. 14 */ 15 16 #include <linux/auxiliary_bus.h> 17 #include <linux/bits.h> 18 #include <linux/delay.h> 19 #include <linux/kernel.h> 20 #include <linux/idr.h> 21 #include <linux/module.h> 22 #include <linux/pci.h> 23 #include <linux/types.h> 24 25 #include "vsec.h" 26 27 /* Intel DVSEC offsets */ 28 #define INTEL_DVSEC_ENTRIES 0xA 29 #define INTEL_DVSEC_SIZE 0xB 30 #define INTEL_DVSEC_TABLE 0xC 31 #define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) 32 #define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) 33 #define TABLE_OFFSET_SHIFT 3 34 #define PMT_XA_START 0 35 #define PMT_XA_MAX INT_MAX 36 #define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX) 37 38 static DEFINE_IDA(intel_vsec_ida); 39 static DEFINE_IDA(intel_vsec_sdsi_ida); 40 static DEFINE_XARRAY_ALLOC(auxdev_array); 41 42 /** 43 * struct intel_vsec_header - Common fields of Intel VSEC and DVSEC registers. 44 * @rev: Revision ID of the VSEC/DVSEC register space 45 * @length: Length of the VSEC/DVSEC register space 46 * @id: ID of the feature 47 * @num_entries: Number of instances of the feature 48 * @entry_size: Size of the discovery table for each feature 49 * @tbir: BAR containing the discovery tables 50 * @offset: BAR offset of start of the first discovery table 51 */ 52 struct intel_vsec_header { 53 u8 rev; 54 u16 length; 55 u16 id; 56 u8 num_entries; 57 u8 entry_size; 58 u8 tbir; 59 u32 offset; 60 }; 61 62 enum intel_vsec_id { 63 VSEC_ID_TELEMETRY = 2, 64 VSEC_ID_WATCHER = 3, 65 VSEC_ID_CRASHLOG = 4, 66 VSEC_ID_SDSI = 65, 67 VSEC_ID_TPMI = 66, 68 }; 69 70 static const char *intel_vsec_name(enum intel_vsec_id id) 71 { 72 switch (id) { 73 case VSEC_ID_TELEMETRY: 74 return "telemetry"; 75 76 case VSEC_ID_WATCHER: 77 return "watcher"; 78 79 case VSEC_ID_CRASHLOG: 80 return "crashlog"; 81 82 case VSEC_ID_SDSI: 83 return "sdsi"; 84 85 case VSEC_ID_TPMI: 86 return "tpmi"; 87 88 default: 89 return NULL; 90 } 91 } 92 93 static bool intel_vsec_supported(u16 id, unsigned long caps) 94 { 95 switch (id) { 96 case VSEC_ID_TELEMETRY: 97 return !!(caps & VSEC_CAP_TELEMETRY); 98 case VSEC_ID_WATCHER: 99 return !!(caps & VSEC_CAP_WATCHER); 100 case VSEC_ID_CRASHLOG: 101 return !!(caps & VSEC_CAP_CRASHLOG); 102 case VSEC_ID_SDSI: 103 return !!(caps & VSEC_CAP_SDSI); 104 case VSEC_ID_TPMI: 105 return !!(caps & VSEC_CAP_TPMI); 106 default: 107 return false; 108 } 109 } 110 111 static void intel_vsec_remove_aux(void *data) 112 { 113 auxiliary_device_delete(data); 114 auxiliary_device_uninit(data); 115 } 116 117 static DEFINE_MUTEX(vsec_ida_lock); 118 119 static void intel_vsec_dev_release(struct device *dev) 120 { 121 struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(dev); 122 123 xa_erase(&auxdev_array, intel_vsec_dev->id); 124 125 mutex_lock(&vsec_ida_lock); 126 ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id); 127 mutex_unlock(&vsec_ida_lock); 128 129 kfree(intel_vsec_dev->resource); 130 kfree(intel_vsec_dev); 131 } 132 133 int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, 134 struct intel_vsec_device *intel_vsec_dev, 135 const char *name) 136 { 137 struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev; 138 int ret, id; 139 140 ret = xa_alloc(&auxdev_array, &intel_vsec_dev->id, intel_vsec_dev, 141 PMT_XA_LIMIT, GFP_KERNEL); 142 if (ret < 0) { 143 kfree(intel_vsec_dev->resource); 144 kfree(intel_vsec_dev); 145 return ret; 146 } 147 148 mutex_lock(&vsec_ida_lock); 149 id = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL); 150 mutex_unlock(&vsec_ida_lock); 151 if (id < 0) { 152 xa_erase(&auxdev_array, intel_vsec_dev->id); 153 kfree(intel_vsec_dev->resource); 154 kfree(intel_vsec_dev); 155 return id; 156 } 157 158 if (!parent) 159 parent = &pdev->dev; 160 161 auxdev->id = id; 162 auxdev->name = name; 163 auxdev->dev.parent = parent; 164 auxdev->dev.release = intel_vsec_dev_release; 165 166 ret = auxiliary_device_init(auxdev); 167 if (ret < 0) { 168 intel_vsec_dev_release(&auxdev->dev); 169 return ret; 170 } 171 172 ret = auxiliary_device_add(auxdev); 173 if (ret < 0) { 174 auxiliary_device_uninit(auxdev); 175 return ret; 176 } 177 178 ret = devm_add_action_or_reset(parent, intel_vsec_remove_aux, 179 auxdev); 180 if (ret < 0) 181 return ret; 182 183 return 0; 184 } 185 EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, INTEL_VSEC); 186 187 static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, 188 struct intel_vsec_platform_info *info) 189 { 190 struct intel_vsec_device *intel_vsec_dev; 191 struct resource *res, *tmp; 192 unsigned long quirks = info->quirks; 193 int i; 194 195 if (!intel_vsec_supported(header->id, info->caps)) 196 return -EINVAL; 197 198 if (!header->num_entries) { 199 dev_dbg(&pdev->dev, "Invalid 0 entry count for header id %d\n", header->id); 200 return -EINVAL; 201 } 202 203 if (!header->entry_size) { 204 dev_dbg(&pdev->dev, "Invalid 0 entry size for header id %d\n", header->id); 205 return -EINVAL; 206 } 207 208 intel_vsec_dev = kzalloc(sizeof(*intel_vsec_dev), GFP_KERNEL); 209 if (!intel_vsec_dev) 210 return -ENOMEM; 211 212 res = kcalloc(header->num_entries, sizeof(*res), GFP_KERNEL); 213 if (!res) { 214 kfree(intel_vsec_dev); 215 return -ENOMEM; 216 } 217 218 if (quirks & VSEC_QUIRK_TABLE_SHIFT) 219 header->offset >>= TABLE_OFFSET_SHIFT; 220 221 /* 222 * The DVSEC/VSEC contains the starting offset and count for a block of 223 * discovery tables. Create a resource array of these tables to the 224 * auxiliary device driver. 225 */ 226 for (i = 0, tmp = res; i < header->num_entries; i++, tmp++) { 227 tmp->start = pdev->resource[header->tbir].start + 228 header->offset + i * (header->entry_size * sizeof(u32)); 229 tmp->end = tmp->start + (header->entry_size * sizeof(u32)) - 1; 230 tmp->flags = IORESOURCE_MEM; 231 } 232 233 intel_vsec_dev->pcidev = pdev; 234 intel_vsec_dev->resource = res; 235 intel_vsec_dev->num_resources = header->num_entries; 236 intel_vsec_dev->info = info; 237 238 if (header->id == VSEC_ID_SDSI) 239 intel_vsec_dev->ida = &intel_vsec_sdsi_ida; 240 else 241 intel_vsec_dev->ida = &intel_vsec_ida; 242 243 return intel_vsec_add_aux(pdev, NULL, intel_vsec_dev, 244 intel_vsec_name(header->id)); 245 } 246 247 static bool intel_vsec_walk_header(struct pci_dev *pdev, 248 struct intel_vsec_platform_info *info) 249 { 250 struct intel_vsec_header **header = info->headers; 251 bool have_devices = false; 252 int ret; 253 254 for ( ; *header; header++) { 255 ret = intel_vsec_add_dev(pdev, *header, info); 256 if (ret) 257 dev_info(&pdev->dev, "Could not add device for VSEC id %d\n", 258 (*header)->id); 259 else 260 have_devices = true; 261 } 262 263 return have_devices; 264 } 265 266 static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, 267 struct intel_vsec_platform_info *info) 268 { 269 bool have_devices = false; 270 int pos = 0; 271 272 do { 273 struct intel_vsec_header header; 274 u32 table, hdr; 275 u16 vid; 276 int ret; 277 278 pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC); 279 if (!pos) 280 break; 281 282 pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER1, &hdr); 283 vid = PCI_DVSEC_HEADER1_VID(hdr); 284 if (vid != PCI_VENDOR_ID_INTEL) 285 continue; 286 287 /* Support only revision 1 */ 288 header.rev = PCI_DVSEC_HEADER1_REV(hdr); 289 if (header.rev != 1) { 290 dev_info(&pdev->dev, "Unsupported DVSEC revision %d\n", header.rev); 291 continue; 292 } 293 294 header.length = PCI_DVSEC_HEADER1_LEN(hdr); 295 296 pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, &header.num_entries); 297 pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, &header.entry_size); 298 pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, &table); 299 300 header.tbir = INTEL_DVSEC_TABLE_BAR(table); 301 header.offset = INTEL_DVSEC_TABLE_OFFSET(table); 302 303 pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr); 304 header.id = PCI_DVSEC_HEADER2_ID(hdr); 305 306 ret = intel_vsec_add_dev(pdev, &header, info); 307 if (ret) 308 continue; 309 310 have_devices = true; 311 } while (true); 312 313 return have_devices; 314 } 315 316 static bool intel_vsec_walk_vsec(struct pci_dev *pdev, 317 struct intel_vsec_platform_info *info) 318 { 319 bool have_devices = false; 320 int pos = 0; 321 322 do { 323 struct intel_vsec_header header; 324 u32 table, hdr; 325 int ret; 326 327 pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_VNDR); 328 if (!pos) 329 break; 330 331 pci_read_config_dword(pdev, pos + PCI_VNDR_HEADER, &hdr); 332 333 /* Support only revision 1 */ 334 header.rev = PCI_VNDR_HEADER_REV(hdr); 335 if (header.rev != 1) { 336 dev_info(&pdev->dev, "Unsupported VSEC revision %d\n", header.rev); 337 continue; 338 } 339 340 header.id = PCI_VNDR_HEADER_ID(hdr); 341 header.length = PCI_VNDR_HEADER_LEN(hdr); 342 343 /* entry, size, and table offset are the same as DVSEC */ 344 pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, &header.num_entries); 345 pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, &header.entry_size); 346 pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, &table); 347 348 header.tbir = INTEL_DVSEC_TABLE_BAR(table); 349 header.offset = INTEL_DVSEC_TABLE_OFFSET(table); 350 351 ret = intel_vsec_add_dev(pdev, &header, info); 352 if (ret) 353 continue; 354 355 have_devices = true; 356 } while (true); 357 358 return have_devices; 359 } 360 361 static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 362 { 363 struct intel_vsec_platform_info *info; 364 bool have_devices = false; 365 int ret; 366 367 ret = pcim_enable_device(pdev); 368 if (ret) 369 return ret; 370 371 pci_save_state(pdev); 372 info = (struct intel_vsec_platform_info *)id->driver_data; 373 if (!info) 374 return -EINVAL; 375 376 if (intel_vsec_walk_dvsec(pdev, info)) 377 have_devices = true; 378 379 if (intel_vsec_walk_vsec(pdev, info)) 380 have_devices = true; 381 382 if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) && 383 intel_vsec_walk_header(pdev, info)) 384 have_devices = true; 385 386 if (!have_devices) 387 return -ENODEV; 388 389 return 0; 390 } 391 392 /* DG1 info */ 393 static struct intel_vsec_header dg1_header = { 394 .length = 0x10, 395 .id = 2, 396 .num_entries = 1, 397 .entry_size = 3, 398 .tbir = 0, 399 .offset = 0x466000, 400 }; 401 402 static struct intel_vsec_header *dg1_headers[] = { 403 &dg1_header, 404 NULL 405 }; 406 407 static const struct intel_vsec_platform_info dg1_info = { 408 .caps = VSEC_CAP_TELEMETRY, 409 .headers = dg1_headers, 410 .quirks = VSEC_QUIRK_NO_DVSEC | VSEC_QUIRK_EARLY_HW, 411 }; 412 413 /* MTL info */ 414 static const struct intel_vsec_platform_info mtl_info = { 415 .caps = VSEC_CAP_TELEMETRY, 416 }; 417 418 /* OOBMSM info */ 419 static const struct intel_vsec_platform_info oobmsm_info = { 420 .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_SDSI | VSEC_CAP_TPMI, 421 }; 422 423 /* TGL info */ 424 static const struct intel_vsec_platform_info tgl_info = { 425 .caps = VSEC_CAP_TELEMETRY, 426 .quirks = VSEC_QUIRK_TABLE_SHIFT | VSEC_QUIRK_EARLY_HW, 427 }; 428 429 #define PCI_DEVICE_ID_INTEL_VSEC_ADL 0x467d 430 #define PCI_DEVICE_ID_INTEL_VSEC_DG1 0x490e 431 #define PCI_DEVICE_ID_INTEL_VSEC_MTL_M 0x7d0d 432 #define PCI_DEVICE_ID_INTEL_VSEC_MTL_S 0xad0d 433 #define PCI_DEVICE_ID_INTEL_VSEC_OOBMSM 0x09a7 434 #define PCI_DEVICE_ID_INTEL_VSEC_RPL 0xa77d 435 #define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d 436 static const struct pci_device_id intel_vsec_pci_ids[] = { 437 { PCI_DEVICE_DATA(INTEL, VSEC_ADL, &tgl_info) }, 438 { PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) }, 439 { PCI_DEVICE_DATA(INTEL, VSEC_MTL_M, &mtl_info) }, 440 { PCI_DEVICE_DATA(INTEL, VSEC_MTL_S, &mtl_info) }, 441 { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &oobmsm_info) }, 442 { PCI_DEVICE_DATA(INTEL, VSEC_RPL, &tgl_info) }, 443 { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, 444 { } 445 }; 446 MODULE_DEVICE_TABLE(pci, intel_vsec_pci_ids); 447 448 static pci_ers_result_t intel_vsec_pci_error_detected(struct pci_dev *pdev, 449 pci_channel_state_t state) 450 { 451 pci_ers_result_t status = PCI_ERS_RESULT_NEED_RESET; 452 453 dev_info(&pdev->dev, "PCI error detected, state %d", state); 454 455 if (state == pci_channel_io_perm_failure) 456 status = PCI_ERS_RESULT_DISCONNECT; 457 else 458 pci_disable_device(pdev); 459 460 return status; 461 } 462 463 static pci_ers_result_t intel_vsec_pci_slot_reset(struct pci_dev *pdev) 464 { 465 struct intel_vsec_device *intel_vsec_dev; 466 pci_ers_result_t status = PCI_ERS_RESULT_DISCONNECT; 467 const struct pci_device_id *pci_dev_id; 468 unsigned long index; 469 470 dev_info(&pdev->dev, "Resetting PCI slot\n"); 471 472 msleep(2000); 473 if (pci_enable_device(pdev)) { 474 dev_info(&pdev->dev, 475 "Failed to re-enable PCI device after reset.\n"); 476 goto out; 477 } 478 479 status = PCI_ERS_RESULT_RECOVERED; 480 481 xa_for_each(&auxdev_array, index, intel_vsec_dev) { 482 /* check if pdev doesn't match */ 483 if (pdev != intel_vsec_dev->pcidev) 484 continue; 485 devm_release_action(&pdev->dev, intel_vsec_remove_aux, 486 &intel_vsec_dev->auxdev); 487 } 488 pci_disable_device(pdev); 489 pci_restore_state(pdev); 490 pci_dev_id = pci_match_id(intel_vsec_pci_ids, pdev); 491 intel_vsec_pci_probe(pdev, pci_dev_id); 492 493 out: 494 return status; 495 } 496 497 static void intel_vsec_pci_resume(struct pci_dev *pdev) 498 { 499 dev_info(&pdev->dev, "Done resuming PCI device\n"); 500 } 501 502 static const struct pci_error_handlers intel_vsec_pci_err_handlers = { 503 .error_detected = intel_vsec_pci_error_detected, 504 .slot_reset = intel_vsec_pci_slot_reset, 505 .resume = intel_vsec_pci_resume, 506 }; 507 508 static struct pci_driver intel_vsec_pci_driver = { 509 .name = "intel_vsec", 510 .id_table = intel_vsec_pci_ids, 511 .probe = intel_vsec_pci_probe, 512 .err_handler = &intel_vsec_pci_err_handlers, 513 }; 514 module_pci_driver(intel_vsec_pci_driver); 515 516 MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 517 MODULE_DESCRIPTION("Intel Extended Capabilities auxiliary bus driver"); 518 MODULE_LICENSE("GPL v2"); 519