1e184b1e5SDavid E. Box // SPDX-License-Identifier: GPL-2.0
2e184b1e5SDavid E. Box /*
3e184b1e5SDavid E. Box * Intel Platform Monitory Technology Telemetry driver
4e184b1e5SDavid E. Box *
5e184b1e5SDavid E. Box * Copyright (c) 2020, Intel Corporation.
6e184b1e5SDavid E. Box * All Rights Reserved.
7e184b1e5SDavid E. Box *
8e184b1e5SDavid E. Box * Author: "David E. Box" <david.e.box@linux.intel.com>
9e184b1e5SDavid E. Box */
10e184b1e5SDavid E. Box
11a3c8f906SDavid E. Box #include <linux/auxiliary_bus.h>
12fc9aef43SDavid E. Box #include <linux/intel_vsec.h>
13e184b1e5SDavid E. Box #include <linux/kernel.h>
14e184b1e5SDavid E. Box #include <linux/module.h>
15e184b1e5SDavid E. Box #include <linux/pci.h>
16e184b1e5SDavid E. Box #include <linux/slab.h>
17e184b1e5SDavid E. Box #include <linux/uaccess.h>
18e184b1e5SDavid E. Box #include <linux/overflow.h>
19e184b1e5SDavid E. Box
20e184b1e5SDavid E. Box #include "class.h"
21e184b1e5SDavid E. Box
22e184b1e5SDavid E. Box #define TELEM_SIZE_OFFSET 0x0
23e184b1e5SDavid E. Box #define TELEM_GUID_OFFSET 0x4
24e184b1e5SDavid E. Box #define TELEM_BASE_OFFSET 0x8
25e184b1e5SDavid E. Box #define TELEM_ACCESS(v) ((v) & GENMASK(3, 0))
26ba7e421eSDavid E. Box #define TELEM_TYPE(v) (((v) & GENMASK(7, 4)) >> 4)
27e184b1e5SDavid E. Box /* size is in bytes */
28e184b1e5SDavid E. Box #define TELEM_SIZE(v) (((v) & GENMASK(27, 12)) >> 10)
29e184b1e5SDavid E. Box
30e184b1e5SDavid E. Box /* Used by client hardware to identify a fixed telemetry entry*/
31e184b1e5SDavid E. Box #define TELEM_CLIENT_FIXED_BLOCK_GUID 0x10000000
32e184b1e5SDavid E. Box
33416eeb2eSDavid E. Box #define NUM_BYTES_QWORD(v) ((v) << 3)
34416eeb2eSDavid E. Box #define SAMPLE_ID_OFFSET(v) ((v) << 3)
35416eeb2eSDavid E. Box
36416eeb2eSDavid E. Box #define NUM_BYTES_DWORD(v) ((v) << 2)
37416eeb2eSDavid E. Box #define SAMPLE_ID_OFFSET32(v) ((v) << 2)
38416eeb2eSDavid E. Box
39416eeb2eSDavid E. Box /* Protects access to the xarray of telemetry endpoint handles */
40416eeb2eSDavid E. Box static DEFINE_MUTEX(ep_lock);
41416eeb2eSDavid E. Box
42ba7e421eSDavid E. Box enum telem_type {
43ba7e421eSDavid E. Box TELEM_TYPE_PUNIT = 0,
44ba7e421eSDavid E. Box TELEM_TYPE_CRASHLOG,
45ba7e421eSDavid E. Box TELEM_TYPE_PUNIT_FIXED,
46ba7e421eSDavid E. Box };
47ba7e421eSDavid E. Box
48e184b1e5SDavid E. Box struct pmt_telem_priv {
49e184b1e5SDavid E. Box int num_entries;
50e184b1e5SDavid E. Box struct intel_pmt_entry entry[];
51e184b1e5SDavid E. Box };
52e184b1e5SDavid E. Box
pmt_telem_region_overlaps(struct intel_pmt_entry * entry,struct device * dev)53e184b1e5SDavid E. Box static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry,
54e184b1e5SDavid E. Box struct device *dev)
55e184b1e5SDavid E. Box {
56e184b1e5SDavid E. Box u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET);
57e184b1e5SDavid E. Box
58ba7e421eSDavid E. Box if (intel_pmt_is_early_client_hw(dev)) {
59ba7e421eSDavid E. Box u32 type = TELEM_TYPE(readl(entry->disc_table));
60e184b1e5SDavid E. Box
61ba7e421eSDavid E. Box if ((type == TELEM_TYPE_PUNIT_FIXED) ||
62ba7e421eSDavid E. Box (guid == TELEM_CLIENT_FIXED_BLOCK_GUID))
63ba7e421eSDavid E. Box return true;
64ba7e421eSDavid E. Box }
65ba7e421eSDavid E. Box
66ba7e421eSDavid E. Box return false;
67e184b1e5SDavid E. Box }
68e184b1e5SDavid E. Box
pmt_telem_header_decode(struct intel_pmt_entry * entry,struct device * dev)69e184b1e5SDavid E. Box static int pmt_telem_header_decode(struct intel_pmt_entry *entry,
70e184b1e5SDavid E. Box struct device *dev)
71e184b1e5SDavid E. Box {
72e184b1e5SDavid E. Box void __iomem *disc_table = entry->disc_table;
734d1b7efeSDavid E. Box struct intel_pmt_header *header = &entry->header;
74e184b1e5SDavid E. Box
75e184b1e5SDavid E. Box if (pmt_telem_region_overlaps(entry, dev))
76e184b1e5SDavid E. Box return 1;
77e184b1e5SDavid E. Box
78e184b1e5SDavid E. Box header->access_type = TELEM_ACCESS(readl(disc_table));
79e184b1e5SDavid E. Box header->guid = readl(disc_table + TELEM_GUID_OFFSET);
80e184b1e5SDavid E. Box header->base_offset = readl(disc_table + TELEM_BASE_OFFSET);
81e184b1e5SDavid E. Box
82e184b1e5SDavid E. Box /* Size is measured in DWORDS, but accessor returns bytes */
83e184b1e5SDavid E. Box header->size = TELEM_SIZE(readl(disc_table));
84e184b1e5SDavid E. Box
85ef195e8aSDavid E. Box /*
86ef195e8aSDavid E. Box * Some devices may expose non-functioning entries that are
87ef195e8aSDavid E. Box * reserved for future use. They have zero size. Do not fail
88ef195e8aSDavid E. Box * probe for these. Just ignore them.
89ef195e8aSDavid E. Box */
903f581602SDavid E. Box if (header->size == 0 || header->access_type == 0xF)
91ef195e8aSDavid E. Box return 1;
92ef195e8aSDavid E. Box
93e184b1e5SDavid E. Box return 0;
94e184b1e5SDavid E. Box }
95e184b1e5SDavid E. Box
pmt_telem_add_endpoint(struct intel_vsec_device * ivdev,struct intel_pmt_entry * entry)96*045a5130SDavid E. Box static int pmt_telem_add_endpoint(struct intel_vsec_device *ivdev,
97*045a5130SDavid E. Box struct intel_pmt_entry *entry)
98416eeb2eSDavid E. Box {
99416eeb2eSDavid E. Box struct telem_endpoint *ep;
100416eeb2eSDavid E. Box
101416eeb2eSDavid E. Box /* Endpoint lifetimes are managed by kref, not devres */
102416eeb2eSDavid E. Box entry->ep = kzalloc(sizeof(*(entry->ep)), GFP_KERNEL);
103416eeb2eSDavid E. Box if (!entry->ep)
104416eeb2eSDavid E. Box return -ENOMEM;
105416eeb2eSDavid E. Box
106416eeb2eSDavid E. Box ep = entry->ep;
107*045a5130SDavid E. Box ep->pcidev = ivdev->pcidev;
108416eeb2eSDavid E. Box ep->header.access_type = entry->header.access_type;
109416eeb2eSDavid E. Box ep->header.guid = entry->header.guid;
110416eeb2eSDavid E. Box ep->header.base_offset = entry->header.base_offset;
111416eeb2eSDavid E. Box ep->header.size = entry->header.size;
112416eeb2eSDavid E. Box ep->base = entry->base;
113416eeb2eSDavid E. Box ep->present = true;
114*045a5130SDavid E. Box ep->cb = ivdev->priv_data;
115416eeb2eSDavid E. Box
116416eeb2eSDavid E. Box kref_init(&ep->kref);
117416eeb2eSDavid E. Box
118416eeb2eSDavid E. Box return 0;
119416eeb2eSDavid E. Box }
120416eeb2eSDavid E. Box
121e184b1e5SDavid E. Box static DEFINE_XARRAY_ALLOC(telem_array);
122e184b1e5SDavid E. Box static struct intel_pmt_namespace pmt_telem_ns = {
123e184b1e5SDavid E. Box .name = "telem",
124e184b1e5SDavid E. Box .xa = &telem_array,
125e184b1e5SDavid E. Box .pmt_header_decode = pmt_telem_header_decode,
126416eeb2eSDavid E. Box .pmt_add_endpoint = pmt_telem_add_endpoint,
127e184b1e5SDavid E. Box };
128e184b1e5SDavid E. Box
129416eeb2eSDavid E. Box /* Called when all users unregister and the device is removed */
pmt_telem_ep_release(struct kref * kref)130416eeb2eSDavid E. Box static void pmt_telem_ep_release(struct kref *kref)
131416eeb2eSDavid E. Box {
132416eeb2eSDavid E. Box struct telem_endpoint *ep;
133416eeb2eSDavid E. Box
134416eeb2eSDavid E. Box ep = container_of(kref, struct telem_endpoint, kref);
135416eeb2eSDavid E. Box kfree(ep);
136416eeb2eSDavid E. Box }
137416eeb2eSDavid E. Box
pmt_telem_get_next_endpoint(unsigned long start)138416eeb2eSDavid E. Box unsigned long pmt_telem_get_next_endpoint(unsigned long start)
139416eeb2eSDavid E. Box {
140416eeb2eSDavid E. Box struct intel_pmt_entry *entry;
141416eeb2eSDavid E. Box unsigned long found_idx;
142416eeb2eSDavid E. Box
143416eeb2eSDavid E. Box mutex_lock(&ep_lock);
144416eeb2eSDavid E. Box xa_for_each_start(&telem_array, found_idx, entry, start) {
145416eeb2eSDavid E. Box /*
146416eeb2eSDavid E. Box * Return first found index after start.
147416eeb2eSDavid E. Box * 0 is not valid id.
148416eeb2eSDavid E. Box */
149416eeb2eSDavid E. Box if (found_idx > start)
150416eeb2eSDavid E. Box break;
151416eeb2eSDavid E. Box }
152416eeb2eSDavid E. Box mutex_unlock(&ep_lock);
153416eeb2eSDavid E. Box
154416eeb2eSDavid E. Box return found_idx == start ? 0 : found_idx;
155416eeb2eSDavid E. Box }
156416eeb2eSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmt_telem_get_next_endpoint, INTEL_PMT_TELEMETRY);
157416eeb2eSDavid E. Box
pmt_telem_register_endpoint(int devid)158416eeb2eSDavid E. Box struct telem_endpoint *pmt_telem_register_endpoint(int devid)
159416eeb2eSDavid E. Box {
160416eeb2eSDavid E. Box struct intel_pmt_entry *entry;
161416eeb2eSDavid E. Box unsigned long index = devid;
162416eeb2eSDavid E. Box
163416eeb2eSDavid E. Box mutex_lock(&ep_lock);
164416eeb2eSDavid E. Box entry = xa_find(&telem_array, &index, index, XA_PRESENT);
165416eeb2eSDavid E. Box if (!entry) {
166416eeb2eSDavid E. Box mutex_unlock(&ep_lock);
167416eeb2eSDavid E. Box return ERR_PTR(-ENXIO);
168416eeb2eSDavid E. Box }
169416eeb2eSDavid E. Box
170416eeb2eSDavid E. Box kref_get(&entry->ep->kref);
171416eeb2eSDavid E. Box mutex_unlock(&ep_lock);
172416eeb2eSDavid E. Box
173416eeb2eSDavid E. Box return entry->ep;
174416eeb2eSDavid E. Box }
175416eeb2eSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmt_telem_register_endpoint, INTEL_PMT_TELEMETRY);
176416eeb2eSDavid E. Box
pmt_telem_unregister_endpoint(struct telem_endpoint * ep)177416eeb2eSDavid E. Box void pmt_telem_unregister_endpoint(struct telem_endpoint *ep)
178416eeb2eSDavid E. Box {
179416eeb2eSDavid E. Box kref_put(&ep->kref, pmt_telem_ep_release);
180416eeb2eSDavid E. Box }
181416eeb2eSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmt_telem_unregister_endpoint, INTEL_PMT_TELEMETRY);
182416eeb2eSDavid E. Box
pmt_telem_get_endpoint_info(int devid,struct telem_endpoint_info * info)183416eeb2eSDavid E. Box int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info)
184416eeb2eSDavid E. Box {
185416eeb2eSDavid E. Box struct intel_pmt_entry *entry;
186416eeb2eSDavid E. Box unsigned long index = devid;
187416eeb2eSDavid E. Box int err = 0;
188416eeb2eSDavid E. Box
189416eeb2eSDavid E. Box if (!info)
190416eeb2eSDavid E. Box return -EINVAL;
191416eeb2eSDavid E. Box
192416eeb2eSDavid E. Box mutex_lock(&ep_lock);
193416eeb2eSDavid E. Box entry = xa_find(&telem_array, &index, index, XA_PRESENT);
194416eeb2eSDavid E. Box if (!entry) {
195416eeb2eSDavid E. Box err = -ENXIO;
196416eeb2eSDavid E. Box goto unlock;
197416eeb2eSDavid E. Box }
198416eeb2eSDavid E. Box
199416eeb2eSDavid E. Box info->pdev = entry->ep->pcidev;
200416eeb2eSDavid E. Box info->header = entry->ep->header;
201416eeb2eSDavid E. Box
202416eeb2eSDavid E. Box unlock:
203416eeb2eSDavid E. Box mutex_unlock(&ep_lock);
204416eeb2eSDavid E. Box return err;
205416eeb2eSDavid E. Box
206416eeb2eSDavid E. Box }
207416eeb2eSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmt_telem_get_endpoint_info, INTEL_PMT_TELEMETRY);
208416eeb2eSDavid E. Box
pmt_telem_read(struct telem_endpoint * ep,u32 id,u64 * data,u32 count)209416eeb2eSDavid E. Box int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count)
210416eeb2eSDavid E. Box {
211416eeb2eSDavid E. Box u32 offset, size;
212416eeb2eSDavid E. Box
213416eeb2eSDavid E. Box if (!ep->present)
214416eeb2eSDavid E. Box return -ENODEV;
215416eeb2eSDavid E. Box
216416eeb2eSDavid E. Box offset = SAMPLE_ID_OFFSET(id);
217416eeb2eSDavid E. Box size = ep->header.size;
218416eeb2eSDavid E. Box
219416eeb2eSDavid E. Box if (offset + NUM_BYTES_QWORD(count) > size)
220416eeb2eSDavid E. Box return -EINVAL;
221416eeb2eSDavid E. Box
222*045a5130SDavid E. Box pmt_telem_read_mmio(ep->pcidev, ep->cb, ep->header.guid, data, ep->base + offset,
223*045a5130SDavid E. Box NUM_BYTES_QWORD(count));
224416eeb2eSDavid E. Box
225416eeb2eSDavid E. Box return ep->present ? 0 : -EPIPE;
226416eeb2eSDavid E. Box }
227416eeb2eSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmt_telem_read, INTEL_PMT_TELEMETRY);
228416eeb2eSDavid E. Box
pmt_telem_read32(struct telem_endpoint * ep,u32 id,u32 * data,u32 count)229416eeb2eSDavid E. Box int pmt_telem_read32(struct telem_endpoint *ep, u32 id, u32 *data, u32 count)
230416eeb2eSDavid E. Box {
231416eeb2eSDavid E. Box u32 offset, size;
232416eeb2eSDavid E. Box
233416eeb2eSDavid E. Box if (!ep->present)
234416eeb2eSDavid E. Box return -ENODEV;
235416eeb2eSDavid E. Box
236416eeb2eSDavid E. Box offset = SAMPLE_ID_OFFSET32(id);
237416eeb2eSDavid E. Box size = ep->header.size;
238416eeb2eSDavid E. Box
239416eeb2eSDavid E. Box if (offset + NUM_BYTES_DWORD(count) > size)
240416eeb2eSDavid E. Box return -EINVAL;
241416eeb2eSDavid E. Box
242416eeb2eSDavid E. Box memcpy_fromio(data, ep->base + offset, NUM_BYTES_DWORD(count));
243416eeb2eSDavid E. Box
244416eeb2eSDavid E. Box return ep->present ? 0 : -EPIPE;
245416eeb2eSDavid E. Box }
246416eeb2eSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmt_telem_read32, INTEL_PMT_TELEMETRY);
247416eeb2eSDavid E. Box
248416eeb2eSDavid E. Box struct telem_endpoint *
pmt_telem_find_and_register_endpoint(struct pci_dev * pcidev,u32 guid,u16 pos)249416eeb2eSDavid E. Box pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, u32 guid, u16 pos)
250416eeb2eSDavid E. Box {
251416eeb2eSDavid E. Box int devid = 0;
252416eeb2eSDavid E. Box int inst = 0;
253416eeb2eSDavid E. Box int err = 0;
254416eeb2eSDavid E. Box
255416eeb2eSDavid E. Box while ((devid = pmt_telem_get_next_endpoint(devid))) {
256416eeb2eSDavid E. Box struct telem_endpoint_info ep_info;
257416eeb2eSDavid E. Box
258416eeb2eSDavid E. Box err = pmt_telem_get_endpoint_info(devid, &ep_info);
259416eeb2eSDavid E. Box if (err)
260416eeb2eSDavid E. Box return ERR_PTR(err);
261416eeb2eSDavid E. Box
262416eeb2eSDavid E. Box if (ep_info.header.guid == guid && ep_info.pdev == pcidev) {
263416eeb2eSDavid E. Box if (inst == pos)
264416eeb2eSDavid E. Box return pmt_telem_register_endpoint(devid);
265416eeb2eSDavid E. Box ++inst;
266416eeb2eSDavid E. Box }
267416eeb2eSDavid E. Box }
268416eeb2eSDavid E. Box
269416eeb2eSDavid E. Box return ERR_PTR(-ENXIO);
270416eeb2eSDavid E. Box }
271416eeb2eSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmt_telem_find_and_register_endpoint, INTEL_PMT_TELEMETRY);
272416eeb2eSDavid E. Box
pmt_telem_remove(struct auxiliary_device * auxdev)273a3c8f906SDavid E. Box static void pmt_telem_remove(struct auxiliary_device *auxdev)
274e184b1e5SDavid E. Box {
275a3c8f906SDavid E. Box struct pmt_telem_priv *priv = auxiliary_get_drvdata(auxdev);
276e184b1e5SDavid E. Box int i;
277e184b1e5SDavid E. Box
278416eeb2eSDavid E. Box mutex_lock(&ep_lock);
279416eeb2eSDavid E. Box for (i = 0; i < priv->num_entries; i++) {
280416eeb2eSDavid E. Box struct intel_pmt_entry *entry = &priv->entry[i];
281416eeb2eSDavid E. Box
282416eeb2eSDavid E. Box kref_put(&entry->ep->kref, pmt_telem_ep_release);
283416eeb2eSDavid E. Box intel_pmt_dev_destroy(entry, &pmt_telem_ns);
284e184b1e5SDavid E. Box }
285416eeb2eSDavid E. Box mutex_unlock(&ep_lock);
286416eeb2eSDavid E. Box };
287e184b1e5SDavid E. Box
pmt_telem_probe(struct auxiliary_device * auxdev,const struct auxiliary_device_id * id)288a3c8f906SDavid E. Box static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
289e184b1e5SDavid E. Box {
290a3c8f906SDavid E. Box struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev);
291e184b1e5SDavid E. Box struct pmt_telem_priv *priv;
292e184b1e5SDavid E. Box size_t size;
293e184b1e5SDavid E. Box int i, ret;
294e184b1e5SDavid E. Box
295a3c8f906SDavid E. Box size = struct_size(priv, entry, intel_vsec_dev->num_resources);
296a3c8f906SDavid E. Box priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL);
297e184b1e5SDavid E. Box if (!priv)
298e184b1e5SDavid E. Box return -ENOMEM;
299e184b1e5SDavid E. Box
300a3c8f906SDavid E. Box auxiliary_set_drvdata(auxdev, priv);
301e184b1e5SDavid E. Box
302a3c8f906SDavid E. Box for (i = 0; i < intel_vsec_dev->num_resources; i++) {
3032cdfa0c2SPrarit Bhargava struct intel_pmt_entry *entry = &priv->entry[priv->num_entries];
304e184b1e5SDavid E. Box
305416eeb2eSDavid E. Box mutex_lock(&ep_lock);
306a3c8f906SDavid E. Box ret = intel_pmt_dev_create(entry, &pmt_telem_ns, intel_vsec_dev, i);
307416eeb2eSDavid E. Box mutex_unlock(&ep_lock);
308e184b1e5SDavid E. Box if (ret < 0)
309e184b1e5SDavid E. Box goto abort_probe;
310e184b1e5SDavid E. Box if (ret)
311e184b1e5SDavid E. Box continue;
312e184b1e5SDavid E. Box
313e184b1e5SDavid E. Box priv->num_entries++;
314e184b1e5SDavid E. Box }
315e184b1e5SDavid E. Box
316e184b1e5SDavid E. Box return 0;
317e184b1e5SDavid E. Box abort_probe:
318a3c8f906SDavid E. Box pmt_telem_remove(auxdev);
319e184b1e5SDavid E. Box return ret;
320e184b1e5SDavid E. Box }
321e184b1e5SDavid E. Box
322a3c8f906SDavid E. Box static const struct auxiliary_device_id pmt_telem_id_table[] = {
323a3c8f906SDavid E. Box { .name = "intel_vsec.telemetry" },
324a3c8f906SDavid E. Box {}
325a3c8f906SDavid E. Box };
326a3c8f906SDavid E. Box MODULE_DEVICE_TABLE(auxiliary, pmt_telem_id_table);
327a3c8f906SDavid E. Box
328a3c8f906SDavid E. Box static struct auxiliary_driver pmt_telem_aux_driver = {
329a3c8f906SDavid E. Box .id_table = pmt_telem_id_table,
330e184b1e5SDavid E. Box .remove = pmt_telem_remove,
331e184b1e5SDavid E. Box .probe = pmt_telem_probe,
332e184b1e5SDavid E. Box };
333e184b1e5SDavid E. Box
pmt_telem_init(void)334e184b1e5SDavid E. Box static int __init pmt_telem_init(void)
335e184b1e5SDavid E. Box {
336a3c8f906SDavid E. Box return auxiliary_driver_register(&pmt_telem_aux_driver);
337e184b1e5SDavid E. Box }
338e184b1e5SDavid E. Box module_init(pmt_telem_init);
339e184b1e5SDavid E. Box
pmt_telem_exit(void)340e184b1e5SDavid E. Box static void __exit pmt_telem_exit(void)
341e184b1e5SDavid E. Box {
342a3c8f906SDavid E. Box auxiliary_driver_unregister(&pmt_telem_aux_driver);
343e184b1e5SDavid E. Box xa_destroy(&telem_array);
344e184b1e5SDavid E. Box }
345e184b1e5SDavid E. Box module_exit(pmt_telem_exit);
346e184b1e5SDavid E. Box
347e184b1e5SDavid E. Box MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
348e184b1e5SDavid E. Box MODULE_DESCRIPTION("Intel PMT Telemetry driver");
349e184b1e5SDavid E. Box MODULE_LICENSE("GPL v2");
350d9080843SDavid E. Box MODULE_IMPORT_NS(INTEL_PMT);
351