// SPDX-License-Identifier: GPL-2.0-only /* * Driver to enumerate TPMI features and create devices * * Copyright (c) 2023, Intel Corporation. * All Rights Reserved. * * The TPMI (Topology Aware Register and PM Capsule Interface) provides a * flexible, extendable and PCIe enumerable MMIO interface for PM features. * * For example Intel RAPL (Running Average Power Limit) provides a MMIO * interface using TPMI. This has advantage over traditional MSR * (Model Specific Register) interface, where a thread needs to be scheduled * on the target CPU to read or write. Also the RAPL features vary between * CPU models, and hence lot of model specific code. Here TPMI provides an * architectural interface by providing hierarchical tables and fields, * which will not need any model specific implementation. * * The TPMI interface uses a PCI VSEC structure to expose the location of * MMIO region. * * This VSEC structure is present in the PCI configuration space of the * Intel Out-of-Band (OOB) device, which is handled by the Intel VSEC * driver. The Intel VSEC driver parses VSEC structures present in the PCI * configuration space of the given device and creates an auxiliary device * object for each of them. In particular, it creates an auxiliary device * object representing TPMI that can be bound by an auxiliary driver. * * This TPMI driver will bind to the TPMI auxiliary device object created * by the Intel VSEC driver. * * The TPMI specification defines a PFS (PM Feature Structure) table. * This table is present in the TPMI MMIO region. The starting address * of PFS is derived from the tBIR (Bar Indicator Register) and "Address" * field from the VSEC header. * * Each TPMI PM feature has one entry in the PFS with a unique TPMI * ID and its access details. The TPMI driver creates device nodes * for the supported PM features. * * The names of the devices created by the TPMI driver start with the * "intel_vsec.tpmi-" prefix which is followed by a specific name of the * given PM feature (for example, "intel_vsec.tpmi-rapl.0"). * * The device nodes are create by using interface "intel_vsec_add_aux()" * provided by the Intel VSEC driver.
*/
/** * struct intel_tpmi_pfs_entry - TPMI PM Feature Structure (PFS) entry * @tpmi_id: TPMI feature identifier (what the feature is and its data format). * @num_entries: Number of feature interface instances present in the PFS. * This represents the maximum number of Power domains in the SoC. * @entry_size: Interface instance entry size in 32-bit words. * @cap_offset: Offset from the PM_Features base address to the base of the PM VSEC * register bank in KB. * @attribute: Feature attribute: 0=BIOS. 1=OS. 2-3=Reserved. * @reserved: Bits for use in the future. * * Represents one TPMI feature entry data in the PFS retrieved as is * from the hardware.
*/ struct intel_tpmi_pfs_entry {
u64 tpmi_id:8;
u64 num_entries:8;
u64 entry_size:16;
u64 cap_offset:16;
u64 attribute:2;
u64 reserved:14;
} __packed;
/** * struct intel_tpmi_pm_feature - TPMI PM Feature information for a TPMI ID * @pfs_header: PFS header retireved from the hardware. * @vsec_offset: Starting MMIO address for this feature in bytes. Essentially * this offset = "Address" from VSEC header + PFS Capability * offset for this feature entry. * @vsec_dev: Pointer to intel_vsec_device structure for this TPMI device * * Represents TPMI instance information for one TPMI ID.
*/ struct intel_tpmi_pm_feature { struct intel_tpmi_pfs_entry pfs_header;
u64 vsec_offset; struct intel_vsec_device *vsec_dev;
};
/** * struct intel_tpmi_info - TPMI information for all IDs in an instance * @tpmi_features: Pointer to a list of TPMI feature instances * @vsec_dev: Pointer to intel_vsec_device structure for this TPMI device * @feature_count: Number of TPMI of TPMI instances pointed by tpmi_features * @pfs_start: Start of PFS offset for the TPMI instances in this device * @plat_info: Stores platform info which can be used by the client drivers * @tpmi_control_mem: Memory mapped IO for getting control information * @dbgfs_dir: debugfs entry pointer * * Stores the information for all TPMI devices enumerated from a single PCI device.
*/ struct intel_tpmi_info { struct intel_tpmi_pm_feature *tpmi_features; struct intel_vsec_device *vsec_dev; int feature_count;
u64 pfs_start; struct oobmsm_plat_info plat_info; void __iomem *tpmi_control_mem; struct dentry *dbgfs_dir;
};
/** * struct tpmi_info_header - CPU package ID to PCI device mapping information * @fn: PCI function number * @dev: PCI device number * @bus: PCI bus number * @pkg: CPU Package id * @segment: PCI segment id * @partition: Package Partition id * @cdie_mask: Bitmap of compute dies in the current partition * @reserved: Reserved for future use * @lock: When set to 1 the register is locked and becomes read-only * until next reset. Not for use by the OS driver. * * The structure to read hardware provided mapping information.
*/ struct tpmi_info_header {
u64 fn:3;
u64 dev:5;
u64 bus:8;
u64 pkg:8;
u64 segment:8;
u64 partition:2;
u64 cdie_mask:16;
u64 reserved:13;
u64 lock:1;
} __packed;
/** * struct tpmi_feature_state - Structure to read hardware state of a feature * @enabled: Enable state of a feature, 1: enabled, 0: disabled * @reserved_1: Reserved for future use * @write_blocked: Writes are blocked means all write operations are ignored * @read_blocked: Reads are blocked means will read 0xFFs * @pcs_select: Interface used by out of band software, not used in OS * @reserved_2: Reserved for future use * @id: TPMI ID of the feature * @reserved_3: Reserved for future use * @locked: When set to 1, OS can't change this register. * * The structure is used to read hardware state of a TPMI feature. This * information is used for debug and restricting operations for this feature.
*/ struct tpmi_feature_state {
u32 enabled:1;
u32 reserved_1:3;
u32 write_blocked:1;
u32 read_blocked:1;
u32 pcs_select:1;
u32 reserved_2:1;
u32 id:8;
u32 reserved_3:15;
u32 locked:1;
} __packed;
/* * The size from hardware is in u32 units. This size is from a trusted hardware, * but better to verify for pre silicon platforms. Set size to 0, when invalid.
*/ #define TPMI_GET_SINGLE_ENTRY_SIZE(pfs) \
({ \
pfs->pfs_header.entry_size > SZ_1K ? 0 : pfs->pfs_header.entry_size << 2; \
})
/* Used during auxbus device creation */ static DEFINE_IDA(intel_vsec_tpmi_ida);
/* * Spec is calling for max 1 seconds to get ownership at the worst * case. Read at 10 ms timeouts and repeat up to 1 second.
*/ #define TPMI_CONTROL_TIMEOUT_US (10 * USEC_PER_MSEC) #define TPMI_CONTROL_TIMEOUT_MAX_US (1 * USEC_PER_SEC)
ret = tpmi_read_feature_status(tpmi_info, pfs->pfs_header.tpmi_id, &feature_state); if (ret) return ret;
/* * If not enabled, continue to look at other features in the PFS, so return -EOPNOTSUPP. * This will not cause failure of loading of this driver.
*/ if (!feature_state.enabled) return -EOPNOTSUPP;
name = intel_tpmi_name(pfs->pfs_header.tpmi_id); if (!name) return -EOPNOTSUPP;
res = kcalloc(pfs->pfs_header.num_entries, sizeof(*res), GFP_KERNEL); if (!res) return -ENOMEM;
/* * intel_vsec_add_aux() is resource managed, no explicit * delete is required on error or on module unload. * feature_vsec_dev and res memory are also freed as part of * device deletion.
*/ return intel_vsec_add_aux(vsec_dev->pcidev, &vsec_dev->auxdev.dev,
feature_vsec_dev, feature_id_name);
}
for (i = 0; i < vsec_dev->num_resources; i++) {
ret = tpmi_create_device(tpmi_info, &tpmi_info->tpmi_features[i],
tpmi_info->pfs_start); /* * Fail, if the supported features fails to create device, * otherwise, continue. Even if one device failed to create, * fail the loading of driver. Since intel_vsec_add_aux() * is resource managed, no clean up is required for the * successfully created devices.
*/ if (ret && ret != -EOPNOTSUPP) return ret;
}
/* * Process TPMI_INFO to get PCI device to CPU package ID. * Device nodes for TPMI features are not created in this * for loop. So, the mapping information will be available * when actual device nodes created outside this * loop via tpmi_create_devices().
*/ if (pfs->pfs_header.tpmi_id == TPMI_INFO_ID) {
ret = tpmi_process_info(tpmi_info, pfs); if (ret) return ret;
ret = intel_vsec_set_mapping(&tpmi_info->plat_info, vsec_dev); if (ret) return ret;
}
if (pfs->pfs_header.tpmi_id == TPMI_CONTROL_ID)
tpmi_set_control_base(auxdev, tpmi_info, pfs);
}
tpmi_info->pfs_start = pfs_start;
auxiliary_set_drvdata(auxdev, tpmi_info);
ret = tpmi_create_devices(tpmi_info); if (ret) return ret;
/* * Allow debugfs when security policy allows. Everything this debugfs * interface provides, can also be done via /dev/mem access. If * /dev/mem interface is locked, don't allow debugfs to present any * information. Also check for CAP_SYS_RAWIO as /dev/mem interface.
*/ if (!security_locked_down(LOCKDOWN_DEV_MEM) && capable(CAP_SYS_RAWIO))
tpmi_dbgfs_register(tpmi_info);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.