1 /*
2 * Copyright (C) 2021-2022 Intel Corporation.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include <errno.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <sys/queue.h>
13
14 #include "pcireg.h"
15 #include "pciaccess.h"
16 #include "pci_core.h"
17 #include "ptm.h"
18 #include "passthru.h"
19 #include "pci_util.h"
20 #include "vmmapi.h"
21 #include "acrn_common.h"
22
23 #define PTM_ROOT_PORT_VENDOR 0x8086U
24 #define PTM_ROOT_PORT_DEVICE 0x9d14U
25
26 /* PTM capability register ID*/
27 #define PCIZ_PTM 0x1fU
28
29 /* PTM register Definitions */
30 /* PTM capability register */
31 #define PCIR_PTM_CAP 0x04U
32 #define PCIM_PTM_CAP_REQ 0x01U /* Requestor capable */
33 #define PCIM_PTM_CAP_ROOT 0x4U /* Root capable */
34 #define PCIM_PTM_GRANULARITY 0xFF00 /* Clock granularity */
35 /* PTM control register */
36 #define PCIR_PTM_CTRL 0x08U
37 #define PCIM_PTM_CTRL_ENABLE 0x1U /* PTM enable */
38 #define PCIM_PTM_CTRL_ROOT_SELECT 0x2U /* Root select */
39
40 /* virtual root port secondary bus */
41 static int ptm_root_port_secondary_bus;
42
43 /* get ptm capability register value */
44 static int
get_ptm_reg_value(struct pci_device * pdev,int reg)45 get_ptm_reg_value(struct pci_device *pdev, int reg)
46 {
47 int pos;
48 uint32_t reg_val;
49
50 pos = pci_find_ext_cap(pdev, PCIZ_PTM);
51
52 if (!pos) {
53 return 0;
54 }
55
56 pci_device_cfg_read_u32(pdev, ®_val, pos + reg);
57
58 pr_notice("<PTM>-%s: device [%x:%x.%x]: ptm pos=0x%x, ptm reg val=0x%x.\n",
59 __func__, pdev->bus, pdev->dev, pdev->func, pos, reg_val);
60
61 return reg_val;
62 }
63
64 /* add virtual root port to hv */
65 static int
add_vroot_port(struct vmctx * ctx,struct passthru_dev * ptdev,struct pci_device * root_port,int ptm_cap_offset)66 add_vroot_port(struct vmctx *ctx, struct passthru_dev *ptdev, struct pci_device *root_port, int ptm_cap_offset)
67 {
68 int error = 0;
69 int offset = 0;
70 uint32_t dev_cap = 0;
71
72 struct acrn_vdev rp_vdev = {};
73 struct vrp_config *rp_priv = (struct vrp_config *)&rp_vdev.args;
74
75 rp_vdev.id.fields.vendor = PTM_ROOT_PORT_VENDOR;
76 rp_vdev.id.fields.device = PTM_ROOT_PORT_DEVICE;
77
78 // virtual root port takes bdf from its downstream device
79 rp_vdev.slot = PCI_BDF(ptdev->dev->bus, ptdev->dev->slot, ptdev->dev->func);
80
81 rp_priv->phy_bdf = PCI_BDF(root_port->bus, root_port->dev, root_port->func);
82
83 rp_priv->primary_bus = ptdev->dev->bus;
84
85 rp_priv->secondary_bus = ++ptm_root_port_secondary_bus;
86
87 // only passthru device is connected to virtual root port
88 rp_priv->subordinate_bus = rp_priv->secondary_bus;
89
90 rp_priv->ptm_capable = 1;
91
92 rp_priv->ptm_cap_offset = ptm_cap_offset;
93
94 /* It seems important that passthru device's max payload settings match
95 * the settings on the native device otherwise passthru device may not work.
96 * So we have to set vrp's max payload capacity the same as native root port
97 * otherwise we may accidentally change passthru device's max payload since
98 * during guest OS's pci enumeration, pass-thru device will renegotiate
99 * its max payload's setting with vrp.
100 */
101 offset = pci_find_cap(root_port, PCIY_EXPRESS);
102 pci_device_cfg_read_u32(root_port, &dev_cap, offset + PCIER_DEVICE_CAP);
103 rp_priv->max_payload = dev_cap & PCIEM_CAP_MAX_PAYLOAD;
104 pr_info("%s: virtual root port info: vbdf=0x%x, phy_bdf=0x%x, prim_bus=%x, sec_bus=%x, sub_bus=%x, ptm_cpa_offset=0x%x, max_payload=0x%x.\n",
105 __func__, rp_vdev.slot, rp_priv->phy_bdf, rp_priv->primary_bus, rp_priv->secondary_bus, rp_priv->subordinate_bus,
106 rp_priv->ptm_cap_offset, rp_priv->max_payload);
107
108 error = vm_add_hv_vdev(ctx, &rp_vdev);
109 if (error) {
110 pr_err("failed to add virtual root.\n");
111 return -1;
112 } else
113 return rp_priv->secondary_bus;
114 }
115
116 /* Probe whether device and its root port support PTM */
ptm_probe(struct vmctx * ctx,struct passthru_dev * ptdev,int * vrp_sec_bus)117 int ptm_probe(struct vmctx *ctx, struct passthru_dev *ptdev, int *vrp_sec_bus)
118 {
119 int pos, pcie_type, cap, rp_ptm_offset;
120 struct pci_device *phys_dev = ptdev->phys_dev;
121 struct pci_device *rp;
122
123 *vrp_sec_bus = 0;
124
125 if (!ptdev->pcie_cap) {
126 pr_err("%s Error: %x:%x.%x is not a pci-e device.\n", __func__,
127 phys_dev->bus, phys_dev->dev, phys_dev->func);
128 return -EINVAL;
129 }
130
131 pos = pci_find_ext_cap(phys_dev, PCIZ_PTM);
132 if (!pos) {
133 pr_err("%s Error: %x:%x.%x doesn't support ptm.\n", __func__,
134 phys_dev->bus, phys_dev->dev, phys_dev->func);
135 return -EINVAL;
136 }
137
138 pcie_type = pci_get_pcie_type(phys_dev);
139
140 /* The following sanity checks are based on these assumptions:
141 * 1. PTM requestor can be enabled on pci ep or rcie.
142 * 2. HW implements this simple PTM hierarchy: PTM requestor (EP) is
143 * directly connected to PTM root (root port), or requestor itself is RCIE.
144 * 3. There is no intermediate nodes (such as switch) in between the PTM
145 * root and PTM requestor.
146 * 4. HW only implements one PCI domain in the system and only one PTM
147 * domain implemented in this PCI domain
148 */
149 if (pcie_type == PCIEM_TYPE_ENDPOINT) {
150 cap = get_ptm_reg_value(phys_dev, PCIR_PTM_CAP);
151 if (!(cap & PCIM_PTM_CAP_REQ)) {
152 pr_err("%s Error: %x:%x.%x must be PTM requestor.\n", __func__,
153 phys_dev->bus, phys_dev->dev, phys_dev->func);
154 return -EINVAL;
155 }
156
157 /* Do not support switch */
158 rp = pci_device_get_parent_bridge(phys_dev);
159 if ((rp == NULL) || !is_root_port(rp)) {
160 pr_err("%s Error: Cannot find root port of %x:%x.%x.\n", __func__,
161 phys_dev->bus, phys_dev->dev, phys_dev->func);
162 return -ENODEV;
163 }
164
165 /* check whether root port is PTM root-capable */
166 cap = get_ptm_reg_value(rp, PCIR_PTM_CAP);
167 if (!(cap & PCIM_PTM_CAP_ROOT)) {
168 pr_err("%s Error: root port %x:%x.%x of %x:%x.%x is not PTM root capable.\n",
169 __func__, rp->bus, rp->dev, rp->func,
170 phys_dev->bus, phys_dev->dev, phys_dev->func);
171 return -EINVAL;
172 }
173
174 /* TODO: Support multiple PFs device later as it needs to consider to prevent P2P
175 * attack through ACS or passthrough all PFs together.
176 */
177 if (is_mfdev(phys_dev)) {
178 pr_err("%s: Failed to enable PTM on root port [%x:%x.%x], multi-func dev is not supported.\n",
179 __func__, rp->bus, rp->dev, rp->func);
180 return -EINVAL;
181 }
182
183 /* hv is responsible to ensure that PTM is enabled on hw root port if
184 * root port is PTM root-capable. If PTM root is not enabled already in physical
185 * root port before guest launch, guest OS can only enable it in root port's virtual
186 * config space and PTM may not function as desired so we are not going to allow user
187 * to enable PTM on pass-thru device.
188 */
189 cap = get_ptm_reg_value(rp, PCIR_PTM_CTRL);
190 if (!(cap & PCIM_PTM_CTRL_ENABLE) || !(cap & PCIM_PTM_CTRL_ROOT_SELECT)) {
191 pr_err("%s Warning: guest is not allowed to enable PTM on root port %x:%x.%x.\n",
192 __func__, rp->bus, rp->dev, rp->func);
193
194 return -EINVAL;
195 }
196
197 rp_ptm_offset = pci_find_ext_cap(rp, PCIZ_PTM);
198
199 /* add virtual root port */
200 *vrp_sec_bus = add_vroot_port(ctx, ptdev, rp, rp_ptm_offset);
201 } else if (pcie_type == PCIEM_TYPE_ROOT_INT_EP) {
202 /* Do NOT emulate root port if ptm requestor is RCIE */
203 pr_notice("%s: ptm requestor is root complex integrated device.\n", __func__);
204 } else {
205 pr_err("%s Error: PTM can only be enabled on pci root complex integrated device or endpoint device.\n",
206 __func__);
207 return -EINVAL;
208 }
209
210 return 0;
211 }
212