Statistics
| Branch: | Revision:

root / hw / xen_pt_msi.c @ 5e22c276

History | View | Annotate | Download (16.8 kB)

1 3854ca57 Jiang Yunhong
/*
2 3854ca57 Jiang Yunhong
 * Copyright (c) 2007, Intel Corporation.
3 3854ca57 Jiang Yunhong
 *
4 3854ca57 Jiang Yunhong
 * This work is licensed under the terms of the GNU GPL, version 2.  See
5 3854ca57 Jiang Yunhong
 * the COPYING file in the top-level directory.
6 3854ca57 Jiang Yunhong
 *
7 3854ca57 Jiang Yunhong
 * Jiang Yunhong <yunhong.jiang@intel.com>
8 3854ca57 Jiang Yunhong
 *
9 3854ca57 Jiang Yunhong
 * This file implements direct PCI assignment to a HVM guest
10 3854ca57 Jiang Yunhong
 */
11 3854ca57 Jiang Yunhong
12 3854ca57 Jiang Yunhong
#include <sys/mman.h>
13 3854ca57 Jiang Yunhong
14 3854ca57 Jiang Yunhong
#include "xen_backend.h"
15 3854ca57 Jiang Yunhong
#include "xen_pt.h"
16 3854ca57 Jiang Yunhong
#include "apic-msidef.h"
17 3854ca57 Jiang Yunhong
18 3854ca57 Jiang Yunhong
19 3854ca57 Jiang Yunhong
#define XEN_PT_AUTO_ASSIGN -1
20 3854ca57 Jiang Yunhong
21 3854ca57 Jiang Yunhong
/* shift count for gflags */
22 3854ca57 Jiang Yunhong
#define XEN_PT_GFLAGS_SHIFT_DEST_ID        0
23 3854ca57 Jiang Yunhong
#define XEN_PT_GFLAGS_SHIFT_RH             8
24 3854ca57 Jiang Yunhong
#define XEN_PT_GFLAGS_SHIFT_DM             9
25 3854ca57 Jiang Yunhong
#define XEN_PT_GFLAGSSHIFT_DELIV_MODE     12
26 3854ca57 Jiang Yunhong
#define XEN_PT_GFLAGSSHIFT_TRG_MODE       15
27 3854ca57 Jiang Yunhong
28 3854ca57 Jiang Yunhong
29 3854ca57 Jiang Yunhong
/*
30 3854ca57 Jiang Yunhong
 * Helpers
31 3854ca57 Jiang Yunhong
 */
32 3854ca57 Jiang Yunhong
33 3854ca57 Jiang Yunhong
static inline uint8_t msi_vector(uint32_t data)
34 3854ca57 Jiang Yunhong
{
35 3854ca57 Jiang Yunhong
    return (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT;
36 3854ca57 Jiang Yunhong
}
37 3854ca57 Jiang Yunhong
38 3854ca57 Jiang Yunhong
static inline uint8_t msi_dest_id(uint32_t addr)
39 3854ca57 Jiang Yunhong
{
40 3854ca57 Jiang Yunhong
    return (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT;
41 3854ca57 Jiang Yunhong
}
42 3854ca57 Jiang Yunhong
43 3854ca57 Jiang Yunhong
static inline uint32_t msi_ext_dest_id(uint32_t addr_hi)
44 3854ca57 Jiang Yunhong
{
45 3854ca57 Jiang Yunhong
    return addr_hi & 0xffffff00;
46 3854ca57 Jiang Yunhong
}
47 3854ca57 Jiang Yunhong
48 3854ca57 Jiang Yunhong
static uint32_t msi_gflags(uint32_t data, uint64_t addr)
49 3854ca57 Jiang Yunhong
{
50 3854ca57 Jiang Yunhong
    uint32_t result = 0;
51 3854ca57 Jiang Yunhong
    int rh, dm, dest_id, deliv_mode, trig_mode;
52 3854ca57 Jiang Yunhong
53 3854ca57 Jiang Yunhong
    rh = (addr >> MSI_ADDR_REDIRECTION_SHIFT) & 0x1;
54 3854ca57 Jiang Yunhong
    dm = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1;
55 3854ca57 Jiang Yunhong
    dest_id = msi_dest_id(addr);
56 3854ca57 Jiang Yunhong
    deliv_mode = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7;
57 3854ca57 Jiang Yunhong
    trig_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1;
58 3854ca57 Jiang Yunhong
59 3854ca57 Jiang Yunhong
    result = dest_id | (rh << XEN_PT_GFLAGS_SHIFT_RH)
60 3854ca57 Jiang Yunhong
        | (dm << XEN_PT_GFLAGS_SHIFT_DM)
61 3854ca57 Jiang Yunhong
        | (deliv_mode << XEN_PT_GFLAGSSHIFT_DELIV_MODE)
62 3854ca57 Jiang Yunhong
        | (trig_mode << XEN_PT_GFLAGSSHIFT_TRG_MODE);
63 3854ca57 Jiang Yunhong
64 3854ca57 Jiang Yunhong
    return result;
65 3854ca57 Jiang Yunhong
}
66 3854ca57 Jiang Yunhong
67 3854ca57 Jiang Yunhong
static inline uint64_t msi_addr64(XenPTMSI *msi)
68 3854ca57 Jiang Yunhong
{
69 3854ca57 Jiang Yunhong
    return (uint64_t)msi->addr_hi << 32 | msi->addr_lo;
70 3854ca57 Jiang Yunhong
}
71 3854ca57 Jiang Yunhong
72 3854ca57 Jiang Yunhong
static int msi_msix_enable(XenPCIPassthroughState *s,
73 3854ca57 Jiang Yunhong
                           uint32_t address,
74 3854ca57 Jiang Yunhong
                           uint16_t flag,
75 3854ca57 Jiang Yunhong
                           bool enable)
76 3854ca57 Jiang Yunhong
{
77 3854ca57 Jiang Yunhong
    uint16_t val = 0;
78 3854ca57 Jiang Yunhong
79 3854ca57 Jiang Yunhong
    if (!address) {
80 3854ca57 Jiang Yunhong
        return -1;
81 3854ca57 Jiang Yunhong
    }
82 3854ca57 Jiang Yunhong
83 3854ca57 Jiang Yunhong
    xen_host_pci_get_word(&s->real_device, address, &val);
84 3854ca57 Jiang Yunhong
    if (enable) {
85 3854ca57 Jiang Yunhong
        val |= flag;
86 3854ca57 Jiang Yunhong
    } else {
87 3854ca57 Jiang Yunhong
        val &= ~flag;
88 3854ca57 Jiang Yunhong
    }
89 3854ca57 Jiang Yunhong
    xen_host_pci_set_word(&s->real_device, address, val);
90 3854ca57 Jiang Yunhong
    return 0;
91 3854ca57 Jiang Yunhong
}
92 3854ca57 Jiang Yunhong
93 3854ca57 Jiang Yunhong
static int msi_msix_setup(XenPCIPassthroughState *s,
94 3854ca57 Jiang Yunhong
                          uint64_t addr,
95 3854ca57 Jiang Yunhong
                          uint32_t data,
96 3854ca57 Jiang Yunhong
                          int *ppirq,
97 3854ca57 Jiang Yunhong
                          bool is_msix,
98 3854ca57 Jiang Yunhong
                          int msix_entry,
99 3854ca57 Jiang Yunhong
                          bool is_not_mapped)
100 3854ca57 Jiang Yunhong
{
101 3854ca57 Jiang Yunhong
    uint8_t gvec = msi_vector(data);
102 3854ca57 Jiang Yunhong
    int rc = 0;
103 3854ca57 Jiang Yunhong
104 3854ca57 Jiang Yunhong
    assert((!is_msix && msix_entry == 0) || is_msix);
105 3854ca57 Jiang Yunhong
106 3854ca57 Jiang Yunhong
    if (gvec == 0) {
107 3854ca57 Jiang Yunhong
        /* if gvec is 0, the guest is asking for a particular pirq that
108 3854ca57 Jiang Yunhong
         * is passed as dest_id */
109 3854ca57 Jiang Yunhong
        *ppirq = msi_ext_dest_id(addr >> 32) | msi_dest_id(addr);
110 3854ca57 Jiang Yunhong
        if (!*ppirq) {
111 3854ca57 Jiang Yunhong
            /* this probably identifies an misconfiguration of the guest,
112 3854ca57 Jiang Yunhong
             * try the emulated path */
113 3854ca57 Jiang Yunhong
            *ppirq = XEN_PT_UNASSIGNED_PIRQ;
114 3854ca57 Jiang Yunhong
        } else {
115 3854ca57 Jiang Yunhong
            XEN_PT_LOG(&s->dev, "requested pirq %d for MSI%s"
116 3854ca57 Jiang Yunhong
                       " (vec: %#x, entry: %#x)\n",
117 3854ca57 Jiang Yunhong
                       *ppirq, is_msix ? "-X" : "", gvec, msix_entry);
118 3854ca57 Jiang Yunhong
        }
119 3854ca57 Jiang Yunhong
    }
120 3854ca57 Jiang Yunhong
121 3854ca57 Jiang Yunhong
    if (is_not_mapped) {
122 3854ca57 Jiang Yunhong
        uint64_t table_base = 0;
123 3854ca57 Jiang Yunhong
124 3854ca57 Jiang Yunhong
        if (is_msix) {
125 3854ca57 Jiang Yunhong
            table_base = s->msix->table_base;
126 3854ca57 Jiang Yunhong
        }
127 3854ca57 Jiang Yunhong
128 3854ca57 Jiang Yunhong
        rc = xc_physdev_map_pirq_msi(xen_xc, xen_domid, XEN_PT_AUTO_ASSIGN,
129 3854ca57 Jiang Yunhong
                                     ppirq, PCI_DEVFN(s->real_device.dev,
130 3854ca57 Jiang Yunhong
                                                      s->real_device.func),
131 3854ca57 Jiang Yunhong
                                     s->real_device.bus,
132 3854ca57 Jiang Yunhong
                                     msix_entry, table_base);
133 3854ca57 Jiang Yunhong
        if (rc) {
134 3854ca57 Jiang Yunhong
            XEN_PT_ERR(&s->dev,
135 3854ca57 Jiang Yunhong
                       "Mapping of MSI%s (rc: %i, vec: %#x, entry %#x)\n",
136 3854ca57 Jiang Yunhong
                       is_msix ? "-X" : "", rc, gvec, msix_entry);
137 3854ca57 Jiang Yunhong
            return rc;
138 3854ca57 Jiang Yunhong
        }
139 3854ca57 Jiang Yunhong
    }
140 3854ca57 Jiang Yunhong
141 3854ca57 Jiang Yunhong
    return 0;
142 3854ca57 Jiang Yunhong
}
143 3854ca57 Jiang Yunhong
static int msi_msix_update(XenPCIPassthroughState *s,
144 3854ca57 Jiang Yunhong
                           uint64_t addr,
145 3854ca57 Jiang Yunhong
                           uint32_t data,
146 3854ca57 Jiang Yunhong
                           int pirq,
147 3854ca57 Jiang Yunhong
                           bool is_msix,
148 3854ca57 Jiang Yunhong
                           int msix_entry,
149 3854ca57 Jiang Yunhong
                           int *old_pirq)
150 3854ca57 Jiang Yunhong
{
151 3854ca57 Jiang Yunhong
    PCIDevice *d = &s->dev;
152 3854ca57 Jiang Yunhong
    uint8_t gvec = msi_vector(data);
153 3854ca57 Jiang Yunhong
    uint32_t gflags = msi_gflags(data, addr);
154 3854ca57 Jiang Yunhong
    int rc = 0;
155 3854ca57 Jiang Yunhong
    uint64_t table_addr = 0;
156 3854ca57 Jiang Yunhong
157 3854ca57 Jiang Yunhong
    XEN_PT_LOG(d, "Updating MSI%s with pirq %d gvec %#x gflags %#x"
158 3854ca57 Jiang Yunhong
               " (entry: %#x)\n",
159 3854ca57 Jiang Yunhong
               is_msix ? "-X" : "", pirq, gvec, gflags, msix_entry);
160 3854ca57 Jiang Yunhong
161 3854ca57 Jiang Yunhong
    if (is_msix) {
162 3854ca57 Jiang Yunhong
        table_addr = s->msix->mmio_base_addr;
163 3854ca57 Jiang Yunhong
    }
164 3854ca57 Jiang Yunhong
165 3854ca57 Jiang Yunhong
    rc = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec,
166 3854ca57 Jiang Yunhong
                                  pirq, gflags, table_addr);
167 3854ca57 Jiang Yunhong
168 3854ca57 Jiang Yunhong
    if (rc) {
169 3854ca57 Jiang Yunhong
        XEN_PT_ERR(d, "Updating of MSI%s failed. (rc: %d)\n",
170 3854ca57 Jiang Yunhong
                   is_msix ? "-X" : "", rc);
171 3854ca57 Jiang Yunhong
172 3854ca57 Jiang Yunhong
        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, *old_pirq)) {
173 3854ca57 Jiang Yunhong
            XEN_PT_ERR(d, "Unmapping of MSI%s pirq %d failed.\n",
174 3854ca57 Jiang Yunhong
                       is_msix ? "-X" : "", *old_pirq);
175 3854ca57 Jiang Yunhong
        }
176 3854ca57 Jiang Yunhong
        *old_pirq = XEN_PT_UNASSIGNED_PIRQ;
177 3854ca57 Jiang Yunhong
    }
178 3854ca57 Jiang Yunhong
    return rc;
179 3854ca57 Jiang Yunhong
}
180 3854ca57 Jiang Yunhong
181 3854ca57 Jiang Yunhong
static int msi_msix_disable(XenPCIPassthroughState *s,
182 3854ca57 Jiang Yunhong
                            uint64_t addr,
183 3854ca57 Jiang Yunhong
                            uint32_t data,
184 3854ca57 Jiang Yunhong
                            int pirq,
185 3854ca57 Jiang Yunhong
                            bool is_msix,
186 3854ca57 Jiang Yunhong
                            bool is_binded)
187 3854ca57 Jiang Yunhong
{
188 3854ca57 Jiang Yunhong
    PCIDevice *d = &s->dev;
189 3854ca57 Jiang Yunhong
    uint8_t gvec = msi_vector(data);
190 3854ca57 Jiang Yunhong
    uint32_t gflags = msi_gflags(data, addr);
191 3854ca57 Jiang Yunhong
    int rc = 0;
192 3854ca57 Jiang Yunhong
193 3854ca57 Jiang Yunhong
    if (pirq == XEN_PT_UNASSIGNED_PIRQ) {
194 3854ca57 Jiang Yunhong
        return 0;
195 3854ca57 Jiang Yunhong
    }
196 3854ca57 Jiang Yunhong
197 3854ca57 Jiang Yunhong
    if (is_binded) {
198 3854ca57 Jiang Yunhong
        XEN_PT_LOG(d, "Unbind MSI%s with pirq %d, gvec %#x\n",
199 3854ca57 Jiang Yunhong
                   is_msix ? "-X" : "", pirq, gvec);
200 3854ca57 Jiang Yunhong
        rc = xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec, pirq, gflags);
201 3854ca57 Jiang Yunhong
        if (rc) {
202 3854ca57 Jiang Yunhong
            XEN_PT_ERR(d, "Unbinding of MSI%s failed. (pirq: %d, gvec: %#x)\n",
203 3854ca57 Jiang Yunhong
                       is_msix ? "-X" : "", pirq, gvec);
204 3854ca57 Jiang Yunhong
            return rc;
205 3854ca57 Jiang Yunhong
        }
206 3854ca57 Jiang Yunhong
    }
207 3854ca57 Jiang Yunhong
208 3854ca57 Jiang Yunhong
    XEN_PT_LOG(d, "Unmap MSI%s pirq %d\n", is_msix ? "-X" : "", pirq);
209 3854ca57 Jiang Yunhong
    rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, pirq);
210 3854ca57 Jiang Yunhong
    if (rc) {
211 3854ca57 Jiang Yunhong
        XEN_PT_ERR(d, "Unmapping of MSI%s pirq %d failed. (rc: %i)\n",
212 3854ca57 Jiang Yunhong
                   is_msix ? "-X" : "", pirq, rc);
213 3854ca57 Jiang Yunhong
        return rc;
214 3854ca57 Jiang Yunhong
    }
215 3854ca57 Jiang Yunhong
216 3854ca57 Jiang Yunhong
    return 0;
217 3854ca57 Jiang Yunhong
}
218 3854ca57 Jiang Yunhong
219 3854ca57 Jiang Yunhong
/*
220 3854ca57 Jiang Yunhong
 * MSI virtualization functions
221 3854ca57 Jiang Yunhong
 */
222 3854ca57 Jiang Yunhong
223 3854ca57 Jiang Yunhong
int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool enable)
224 3854ca57 Jiang Yunhong
{
225 3854ca57 Jiang Yunhong
    XEN_PT_LOG(&s->dev, "%s MSI.\n", enable ? "enabling" : "disabling");
226 3854ca57 Jiang Yunhong
227 3854ca57 Jiang Yunhong
    if (!s->msi) {
228 3854ca57 Jiang Yunhong
        return -1;
229 3854ca57 Jiang Yunhong
    }
230 3854ca57 Jiang Yunhong
231 3854ca57 Jiang Yunhong
    return msi_msix_enable(s, s->msi->ctrl_offset, PCI_MSI_FLAGS_ENABLE,
232 3854ca57 Jiang Yunhong
                           enable);
233 3854ca57 Jiang Yunhong
}
234 3854ca57 Jiang Yunhong
235 3854ca57 Jiang Yunhong
/* setup physical msi, but don't enable it */
236 3854ca57 Jiang Yunhong
int xen_pt_msi_setup(XenPCIPassthroughState *s)
237 3854ca57 Jiang Yunhong
{
238 3854ca57 Jiang Yunhong
    int pirq = XEN_PT_UNASSIGNED_PIRQ;
239 3854ca57 Jiang Yunhong
    int rc = 0;
240 3854ca57 Jiang Yunhong
    XenPTMSI *msi = s->msi;
241 3854ca57 Jiang Yunhong
242 3854ca57 Jiang Yunhong
    if (msi->initialized) {
243 3854ca57 Jiang Yunhong
        XEN_PT_ERR(&s->dev,
244 3854ca57 Jiang Yunhong
                   "Setup physical MSI when it has been properly initialized.\n");
245 3854ca57 Jiang Yunhong
        return -1;
246 3854ca57 Jiang Yunhong
    }
247 3854ca57 Jiang Yunhong
248 3854ca57 Jiang Yunhong
    rc = msi_msix_setup(s, msi_addr64(msi), msi->data, &pirq, false, 0, true);
249 3854ca57 Jiang Yunhong
    if (rc) {
250 3854ca57 Jiang Yunhong
        return rc;
251 3854ca57 Jiang Yunhong
    }
252 3854ca57 Jiang Yunhong
253 3854ca57 Jiang Yunhong
    if (pirq < 0) {
254 3854ca57 Jiang Yunhong
        XEN_PT_ERR(&s->dev, "Invalid pirq number: %d.\n", pirq);
255 3854ca57 Jiang Yunhong
        return -1;
256 3854ca57 Jiang Yunhong
    }
257 3854ca57 Jiang Yunhong
258 3854ca57 Jiang Yunhong
    msi->pirq = pirq;
259 3854ca57 Jiang Yunhong
    XEN_PT_LOG(&s->dev, "MSI mapped with pirq %d.\n", pirq);
260 3854ca57 Jiang Yunhong
261 3854ca57 Jiang Yunhong
    return 0;
262 3854ca57 Jiang Yunhong
}
263 3854ca57 Jiang Yunhong
264 3854ca57 Jiang Yunhong
int xen_pt_msi_update(XenPCIPassthroughState *s)
265 3854ca57 Jiang Yunhong
{
266 3854ca57 Jiang Yunhong
    XenPTMSI *msi = s->msi;
267 3854ca57 Jiang Yunhong
    return msi_msix_update(s, msi_addr64(msi), msi->data, msi->pirq,
268 3854ca57 Jiang Yunhong
                           false, 0, &msi->pirq);
269 3854ca57 Jiang Yunhong
}
270 3854ca57 Jiang Yunhong
271 3854ca57 Jiang Yunhong
void xen_pt_msi_disable(XenPCIPassthroughState *s)
272 3854ca57 Jiang Yunhong
{
273 3854ca57 Jiang Yunhong
    XenPTMSI *msi = s->msi;
274 3854ca57 Jiang Yunhong
275 3854ca57 Jiang Yunhong
    if (!msi) {
276 3854ca57 Jiang Yunhong
        return;
277 3854ca57 Jiang Yunhong
    }
278 3854ca57 Jiang Yunhong
279 3854ca57 Jiang Yunhong
    xen_pt_msi_set_enable(s, false);
280 3854ca57 Jiang Yunhong
281 3854ca57 Jiang Yunhong
    msi_msix_disable(s, msi_addr64(msi), msi->data, msi->pirq, false,
282 3854ca57 Jiang Yunhong
                     msi->initialized);
283 3854ca57 Jiang Yunhong
284 3854ca57 Jiang Yunhong
    /* clear msi info */
285 3854ca57 Jiang Yunhong
    msi->flags = 0;
286 3854ca57 Jiang Yunhong
    msi->mapped = false;
287 3854ca57 Jiang Yunhong
    msi->pirq = XEN_PT_UNASSIGNED_PIRQ;
288 3854ca57 Jiang Yunhong
}
289 3854ca57 Jiang Yunhong
290 3854ca57 Jiang Yunhong
/*
291 3854ca57 Jiang Yunhong
 * MSI-X virtualization functions
292 3854ca57 Jiang Yunhong
 */
293 3854ca57 Jiang Yunhong
294 3854ca57 Jiang Yunhong
static int msix_set_enable(XenPCIPassthroughState *s, bool enabled)
295 3854ca57 Jiang Yunhong
{
296 3854ca57 Jiang Yunhong
    XEN_PT_LOG(&s->dev, "%s MSI-X.\n", enabled ? "enabling" : "disabling");
297 3854ca57 Jiang Yunhong
298 3854ca57 Jiang Yunhong
    if (!s->msix) {
299 3854ca57 Jiang Yunhong
        return -1;
300 3854ca57 Jiang Yunhong
    }
301 3854ca57 Jiang Yunhong
302 3854ca57 Jiang Yunhong
    return msi_msix_enable(s, s->msix->ctrl_offset, PCI_MSIX_FLAGS_ENABLE,
303 3854ca57 Jiang Yunhong
                           enabled);
304 3854ca57 Jiang Yunhong
}
305 3854ca57 Jiang Yunhong
306 3854ca57 Jiang Yunhong
static int xen_pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr)
307 3854ca57 Jiang Yunhong
{
308 3854ca57 Jiang Yunhong
    XenPTMSIXEntry *entry = NULL;
309 3854ca57 Jiang Yunhong
    int pirq;
310 3854ca57 Jiang Yunhong
    int rc;
311 3854ca57 Jiang Yunhong
312 3854ca57 Jiang Yunhong
    if (entry_nr < 0 || entry_nr >= s->msix->total_entries) {
313 3854ca57 Jiang Yunhong
        return -EINVAL;
314 3854ca57 Jiang Yunhong
    }
315 3854ca57 Jiang Yunhong
316 3854ca57 Jiang Yunhong
    entry = &s->msix->msix_entry[entry_nr];
317 3854ca57 Jiang Yunhong
318 3854ca57 Jiang Yunhong
    if (!entry->updated) {
319 3854ca57 Jiang Yunhong
        return 0;
320 3854ca57 Jiang Yunhong
    }
321 3854ca57 Jiang Yunhong
322 3854ca57 Jiang Yunhong
    pirq = entry->pirq;
323 3854ca57 Jiang Yunhong
324 044b99c6 Stefano Stabellini
    rc = msi_msix_setup(s, entry->addr, entry->data, &pirq, true, entry_nr,
325 3854ca57 Jiang Yunhong
                        entry->pirq == XEN_PT_UNASSIGNED_PIRQ);
326 3854ca57 Jiang Yunhong
    if (rc) {
327 3854ca57 Jiang Yunhong
        return rc;
328 3854ca57 Jiang Yunhong
    }
329 3854ca57 Jiang Yunhong
    if (entry->pirq == XEN_PT_UNASSIGNED_PIRQ) {
330 3854ca57 Jiang Yunhong
        entry->pirq = pirq;
331 3854ca57 Jiang Yunhong
    }
332 3854ca57 Jiang Yunhong
333 3854ca57 Jiang Yunhong
    rc = msi_msix_update(s, entry->addr, entry->data, pirq, true,
334 3854ca57 Jiang Yunhong
                         entry_nr, &entry->pirq);
335 3854ca57 Jiang Yunhong
336 3854ca57 Jiang Yunhong
    if (!rc) {
337 3854ca57 Jiang Yunhong
        entry->updated = false;
338 3854ca57 Jiang Yunhong
    }
339 3854ca57 Jiang Yunhong
340 3854ca57 Jiang Yunhong
    return rc;
341 3854ca57 Jiang Yunhong
}
342 3854ca57 Jiang Yunhong
343 3854ca57 Jiang Yunhong
int xen_pt_msix_update(XenPCIPassthroughState *s)
344 3854ca57 Jiang Yunhong
{
345 3854ca57 Jiang Yunhong
    XenPTMSIX *msix = s->msix;
346 3854ca57 Jiang Yunhong
    int i;
347 3854ca57 Jiang Yunhong
348 3854ca57 Jiang Yunhong
    for (i = 0; i < msix->total_entries; i++) {
349 3854ca57 Jiang Yunhong
        xen_pt_msix_update_one(s, i);
350 3854ca57 Jiang Yunhong
    }
351 3854ca57 Jiang Yunhong
352 3854ca57 Jiang Yunhong
    return 0;
353 3854ca57 Jiang Yunhong
}
354 3854ca57 Jiang Yunhong
355 3854ca57 Jiang Yunhong
void xen_pt_msix_disable(XenPCIPassthroughState *s)
356 3854ca57 Jiang Yunhong
{
357 3854ca57 Jiang Yunhong
    int i = 0;
358 3854ca57 Jiang Yunhong
359 3854ca57 Jiang Yunhong
    msix_set_enable(s, false);
360 3854ca57 Jiang Yunhong
361 3854ca57 Jiang Yunhong
    for (i = 0; i < s->msix->total_entries; i++) {
362 3854ca57 Jiang Yunhong
        XenPTMSIXEntry *entry = &s->msix->msix_entry[i];
363 3854ca57 Jiang Yunhong
364 3854ca57 Jiang Yunhong
        msi_msix_disable(s, entry->addr, entry->data, entry->pirq, true, true);
365 3854ca57 Jiang Yunhong
366 3854ca57 Jiang Yunhong
        /* clear MSI-X info */
367 3854ca57 Jiang Yunhong
        entry->pirq = XEN_PT_UNASSIGNED_PIRQ;
368 3854ca57 Jiang Yunhong
        entry->updated = false;
369 3854ca57 Jiang Yunhong
    }
370 3854ca57 Jiang Yunhong
}
371 3854ca57 Jiang Yunhong
372 3854ca57 Jiang Yunhong
int xen_pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index)
373 3854ca57 Jiang Yunhong
{
374 3854ca57 Jiang Yunhong
    XenPTMSIXEntry *entry;
375 3854ca57 Jiang Yunhong
    int i, ret;
376 3854ca57 Jiang Yunhong
377 3854ca57 Jiang Yunhong
    if (!(s->msix && s->msix->bar_index == bar_index)) {
378 3854ca57 Jiang Yunhong
        return 0;
379 3854ca57 Jiang Yunhong
    }
380 3854ca57 Jiang Yunhong
381 3854ca57 Jiang Yunhong
    for (i = 0; i < s->msix->total_entries; i++) {
382 3854ca57 Jiang Yunhong
        entry = &s->msix->msix_entry[i];
383 3854ca57 Jiang Yunhong
        if (entry->pirq != XEN_PT_UNASSIGNED_PIRQ) {
384 3854ca57 Jiang Yunhong
            ret = xc_domain_unbind_pt_irq(xen_xc, xen_domid, entry->pirq,
385 3854ca57 Jiang Yunhong
                                          PT_IRQ_TYPE_MSI, 0, 0, 0, 0);
386 3854ca57 Jiang Yunhong
            if (ret) {
387 3854ca57 Jiang Yunhong
                XEN_PT_ERR(&s->dev, "unbind MSI-X entry %d failed\n",
388 3854ca57 Jiang Yunhong
                           entry->pirq);
389 3854ca57 Jiang Yunhong
            }
390 3854ca57 Jiang Yunhong
            entry->updated = true;
391 3854ca57 Jiang Yunhong
        }
392 3854ca57 Jiang Yunhong
    }
393 3854ca57 Jiang Yunhong
    return xen_pt_msix_update(s);
394 3854ca57 Jiang Yunhong
}
395 3854ca57 Jiang Yunhong
396 3854ca57 Jiang Yunhong
static uint32_t get_entry_value(XenPTMSIXEntry *e, int offset)
397 3854ca57 Jiang Yunhong
{
398 3854ca57 Jiang Yunhong
    switch (offset) {
399 3854ca57 Jiang Yunhong
    case PCI_MSIX_ENTRY_LOWER_ADDR:
400 3854ca57 Jiang Yunhong
        return e->addr & UINT32_MAX;
401 3854ca57 Jiang Yunhong
    case PCI_MSIX_ENTRY_UPPER_ADDR:
402 3854ca57 Jiang Yunhong
        return e->addr >> 32;
403 3854ca57 Jiang Yunhong
    case PCI_MSIX_ENTRY_DATA:
404 3854ca57 Jiang Yunhong
        return e->data;
405 3854ca57 Jiang Yunhong
    case PCI_MSIX_ENTRY_VECTOR_CTRL:
406 3854ca57 Jiang Yunhong
        return e->vector_ctrl;
407 3854ca57 Jiang Yunhong
    default:
408 3854ca57 Jiang Yunhong
        return 0;
409 3854ca57 Jiang Yunhong
    }
410 3854ca57 Jiang Yunhong
}
411 3854ca57 Jiang Yunhong
412 3854ca57 Jiang Yunhong
static void set_entry_value(XenPTMSIXEntry *e, int offset, uint32_t val)
413 3854ca57 Jiang Yunhong
{
414 3854ca57 Jiang Yunhong
    switch (offset) {
415 3854ca57 Jiang Yunhong
    case PCI_MSIX_ENTRY_LOWER_ADDR:
416 3854ca57 Jiang Yunhong
        e->addr = (e->addr & ((uint64_t)UINT32_MAX << 32)) | val;
417 3854ca57 Jiang Yunhong
        break;
418 3854ca57 Jiang Yunhong
    case PCI_MSIX_ENTRY_UPPER_ADDR:
419 3854ca57 Jiang Yunhong
        e->addr = (uint64_t)val << 32 | (e->addr & UINT32_MAX);
420 3854ca57 Jiang Yunhong
        break;
421 3854ca57 Jiang Yunhong
    case PCI_MSIX_ENTRY_DATA:
422 3854ca57 Jiang Yunhong
        e->data = val;
423 3854ca57 Jiang Yunhong
        break;
424 3854ca57 Jiang Yunhong
    case PCI_MSIX_ENTRY_VECTOR_CTRL:
425 3854ca57 Jiang Yunhong
        e->vector_ctrl = val;
426 3854ca57 Jiang Yunhong
        break;
427 3854ca57 Jiang Yunhong
    }
428 3854ca57 Jiang Yunhong
}
429 3854ca57 Jiang Yunhong
430 a8170e5e Avi Kivity
static void pci_msix_write(void *opaque, hwaddr addr,
431 3854ca57 Jiang Yunhong
                           uint64_t val, unsigned size)
432 3854ca57 Jiang Yunhong
{
433 3854ca57 Jiang Yunhong
    XenPCIPassthroughState *s = opaque;
434 3854ca57 Jiang Yunhong
    XenPTMSIX *msix = s->msix;
435 3854ca57 Jiang Yunhong
    XenPTMSIXEntry *entry;
436 3854ca57 Jiang Yunhong
    int entry_nr, offset;
437 3854ca57 Jiang Yunhong
438 3854ca57 Jiang Yunhong
    entry_nr = addr / PCI_MSIX_ENTRY_SIZE;
439 3854ca57 Jiang Yunhong
    if (entry_nr < 0 || entry_nr >= msix->total_entries) {
440 3854ca57 Jiang Yunhong
        XEN_PT_ERR(&s->dev, "asked MSI-X entry '%i' invalid!\n", entry_nr);
441 3854ca57 Jiang Yunhong
        return;
442 3854ca57 Jiang Yunhong
    }
443 3854ca57 Jiang Yunhong
    entry = &msix->msix_entry[entry_nr];
444 3854ca57 Jiang Yunhong
    offset = addr % PCI_MSIX_ENTRY_SIZE;
445 3854ca57 Jiang Yunhong
446 3854ca57 Jiang Yunhong
    if (offset != PCI_MSIX_ENTRY_VECTOR_CTRL) {
447 3854ca57 Jiang Yunhong
        const volatile uint32_t *vec_ctrl;
448 3854ca57 Jiang Yunhong
449 3854ca57 Jiang Yunhong
        if (get_entry_value(entry, offset) == val) {
450 3854ca57 Jiang Yunhong
            return;
451 3854ca57 Jiang Yunhong
        }
452 3854ca57 Jiang Yunhong
453 3854ca57 Jiang Yunhong
        /*
454 3854ca57 Jiang Yunhong
         * If Xen intercepts the mask bit access, entry->vec_ctrl may not be
455 3854ca57 Jiang Yunhong
         * up-to-date. Read from hardware directly.
456 3854ca57 Jiang Yunhong
         */
457 3854ca57 Jiang Yunhong
        vec_ctrl = s->msix->phys_iomem_base + entry_nr * PCI_MSIX_ENTRY_SIZE
458 3854ca57 Jiang Yunhong
            + PCI_MSIX_ENTRY_VECTOR_CTRL;
459 3854ca57 Jiang Yunhong
460 3854ca57 Jiang Yunhong
        if (msix->enabled && !(*vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
461 3854ca57 Jiang Yunhong
            XEN_PT_ERR(&s->dev, "Can't update msix entry %d since MSI-X is"
462 3854ca57 Jiang Yunhong
                       " already enabled.\n", entry_nr);
463 3854ca57 Jiang Yunhong
            return;
464 3854ca57 Jiang Yunhong
        }
465 3854ca57 Jiang Yunhong
466 3854ca57 Jiang Yunhong
        entry->updated = true;
467 3854ca57 Jiang Yunhong
    }
468 3854ca57 Jiang Yunhong
469 3854ca57 Jiang Yunhong
    set_entry_value(entry, offset, val);
470 3854ca57 Jiang Yunhong
471 3854ca57 Jiang Yunhong
    if (offset == PCI_MSIX_ENTRY_VECTOR_CTRL) {
472 3854ca57 Jiang Yunhong
        if (msix->enabled && !(val & PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
473 3854ca57 Jiang Yunhong
            xen_pt_msix_update_one(s, entry_nr);
474 3854ca57 Jiang Yunhong
        }
475 3854ca57 Jiang Yunhong
    }
476 3854ca57 Jiang Yunhong
}
477 3854ca57 Jiang Yunhong
478 a8170e5e Avi Kivity
static uint64_t pci_msix_read(void *opaque, hwaddr addr,
479 3854ca57 Jiang Yunhong
                              unsigned size)
480 3854ca57 Jiang Yunhong
{
481 3854ca57 Jiang Yunhong
    XenPCIPassthroughState *s = opaque;
482 3854ca57 Jiang Yunhong
    XenPTMSIX *msix = s->msix;
483 3854ca57 Jiang Yunhong
    int entry_nr, offset;
484 3854ca57 Jiang Yunhong
485 3854ca57 Jiang Yunhong
    entry_nr = addr / PCI_MSIX_ENTRY_SIZE;
486 3854ca57 Jiang Yunhong
    if (entry_nr < 0) {
487 3854ca57 Jiang Yunhong
        XEN_PT_ERR(&s->dev, "asked MSI-X entry '%i' invalid!\n", entry_nr);
488 3854ca57 Jiang Yunhong
        return 0;
489 3854ca57 Jiang Yunhong
    }
490 3854ca57 Jiang Yunhong
491 3854ca57 Jiang Yunhong
    offset = addr % PCI_MSIX_ENTRY_SIZE;
492 3854ca57 Jiang Yunhong
493 3854ca57 Jiang Yunhong
    if (addr < msix->total_entries * PCI_MSIX_ENTRY_SIZE) {
494 3854ca57 Jiang Yunhong
        return get_entry_value(&msix->msix_entry[entry_nr], offset);
495 3854ca57 Jiang Yunhong
    } else {
496 3854ca57 Jiang Yunhong
        /* Pending Bit Array (PBA) */
497 3854ca57 Jiang Yunhong
        return *(uint32_t *)(msix->phys_iomem_base + addr);
498 3854ca57 Jiang Yunhong
    }
499 3854ca57 Jiang Yunhong
}
500 3854ca57 Jiang Yunhong
501 3854ca57 Jiang Yunhong
static const MemoryRegionOps pci_msix_ops = {
502 3854ca57 Jiang Yunhong
    .read = pci_msix_read,
503 3854ca57 Jiang Yunhong
    .write = pci_msix_write,
504 3854ca57 Jiang Yunhong
    .endianness = DEVICE_NATIVE_ENDIAN,
505 3854ca57 Jiang Yunhong
    .valid = {
506 3854ca57 Jiang Yunhong
        .min_access_size = 4,
507 3854ca57 Jiang Yunhong
        .max_access_size = 4,
508 3854ca57 Jiang Yunhong
        .unaligned = false,
509 3854ca57 Jiang Yunhong
    },
510 3854ca57 Jiang Yunhong
};
511 3854ca57 Jiang Yunhong
512 3854ca57 Jiang Yunhong
int xen_pt_msix_init(XenPCIPassthroughState *s, uint32_t base)
513 3854ca57 Jiang Yunhong
{
514 3854ca57 Jiang Yunhong
    uint8_t id = 0;
515 3854ca57 Jiang Yunhong
    uint16_t control = 0;
516 3854ca57 Jiang Yunhong
    uint32_t table_off = 0;
517 3854ca57 Jiang Yunhong
    int i, total_entries, bar_index;
518 3854ca57 Jiang Yunhong
    XenHostPCIDevice *hd = &s->real_device;
519 3854ca57 Jiang Yunhong
    PCIDevice *d = &s->dev;
520 3854ca57 Jiang Yunhong
    int fd = -1;
521 3854ca57 Jiang Yunhong
    XenPTMSIX *msix = NULL;
522 3854ca57 Jiang Yunhong
    int rc = 0;
523 3854ca57 Jiang Yunhong
524 3854ca57 Jiang Yunhong
    rc = xen_host_pci_get_byte(hd, base + PCI_CAP_LIST_ID, &id);
525 3854ca57 Jiang Yunhong
    if (rc) {
526 3854ca57 Jiang Yunhong
        return rc;
527 3854ca57 Jiang Yunhong
    }
528 3854ca57 Jiang Yunhong
529 3854ca57 Jiang Yunhong
    if (id != PCI_CAP_ID_MSIX) {
530 3854ca57 Jiang Yunhong
        XEN_PT_ERR(d, "Invalid id %#x base %#x\n", id, base);
531 3854ca57 Jiang Yunhong
        return -1;
532 3854ca57 Jiang Yunhong
    }
533 3854ca57 Jiang Yunhong
534 3854ca57 Jiang Yunhong
    xen_host_pci_get_word(hd, base + PCI_MSIX_FLAGS, &control);
535 3854ca57 Jiang Yunhong
    total_entries = control & PCI_MSIX_FLAGS_QSIZE;
536 3854ca57 Jiang Yunhong
    total_entries += 1;
537 3854ca57 Jiang Yunhong
538 3854ca57 Jiang Yunhong
    s->msix = g_malloc0(sizeof (XenPTMSIX)
539 3854ca57 Jiang Yunhong
                        + total_entries * sizeof (XenPTMSIXEntry));
540 3854ca57 Jiang Yunhong
    msix = s->msix;
541 3854ca57 Jiang Yunhong
542 3854ca57 Jiang Yunhong
    msix->total_entries = total_entries;
543 3854ca57 Jiang Yunhong
    for (i = 0; i < total_entries; i++) {
544 3854ca57 Jiang Yunhong
        msix->msix_entry[i].pirq = XEN_PT_UNASSIGNED_PIRQ;
545 3854ca57 Jiang Yunhong
    }
546 3854ca57 Jiang Yunhong
547 3854ca57 Jiang Yunhong
    memory_region_init_io(&msix->mmio, &pci_msix_ops, s, "xen-pci-pt-msix",
548 3854ca57 Jiang Yunhong
                          (total_entries * PCI_MSIX_ENTRY_SIZE
549 3854ca57 Jiang Yunhong
                           + XC_PAGE_SIZE - 1)
550 3854ca57 Jiang Yunhong
                          & XC_PAGE_MASK);
551 3854ca57 Jiang Yunhong
552 3854ca57 Jiang Yunhong
    xen_host_pci_get_long(hd, base + PCI_MSIX_TABLE, &table_off);
553 3854ca57 Jiang Yunhong
    bar_index = msix->bar_index = table_off & PCI_MSIX_FLAGS_BIRMASK;
554 3854ca57 Jiang Yunhong
    table_off = table_off & ~PCI_MSIX_FLAGS_BIRMASK;
555 3854ca57 Jiang Yunhong
    msix->table_base = s->real_device.io_regions[bar_index].base_addr;
556 3854ca57 Jiang Yunhong
    XEN_PT_LOG(d, "get MSI-X table BAR base 0x%"PRIx64"\n", msix->table_base);
557 3854ca57 Jiang Yunhong
558 3854ca57 Jiang Yunhong
    fd = open("/dev/mem", O_RDWR);
559 3854ca57 Jiang Yunhong
    if (fd == -1) {
560 3854ca57 Jiang Yunhong
        rc = -errno;
561 3854ca57 Jiang Yunhong
        XEN_PT_ERR(d, "Can't open /dev/mem: %s\n", strerror(errno));
562 3854ca57 Jiang Yunhong
        goto error_out;
563 3854ca57 Jiang Yunhong
    }
564 3854ca57 Jiang Yunhong
    XEN_PT_LOG(d, "table_off = %#x, total_entries = %d\n",
565 3854ca57 Jiang Yunhong
               table_off, total_entries);
566 3854ca57 Jiang Yunhong
    msix->table_offset_adjust = table_off & 0x0fff;
567 3854ca57 Jiang Yunhong
    msix->phys_iomem_base =
568 3854ca57 Jiang Yunhong
        mmap(NULL,
569 3854ca57 Jiang Yunhong
             total_entries * PCI_MSIX_ENTRY_SIZE + msix->table_offset_adjust,
570 3854ca57 Jiang Yunhong
             PROT_READ,
571 3854ca57 Jiang Yunhong
             MAP_SHARED | MAP_LOCKED,
572 3854ca57 Jiang Yunhong
             fd,
573 3854ca57 Jiang Yunhong
             msix->table_base + table_off - msix->table_offset_adjust);
574 3854ca57 Jiang Yunhong
    close(fd);
575 3854ca57 Jiang Yunhong
    if (msix->phys_iomem_base == MAP_FAILED) {
576 3854ca57 Jiang Yunhong
        rc = -errno;
577 3854ca57 Jiang Yunhong
        XEN_PT_ERR(d, "Can't map physical MSI-X table: %s\n", strerror(errno));
578 3854ca57 Jiang Yunhong
        goto error_out;
579 3854ca57 Jiang Yunhong
    }
580 3854ca57 Jiang Yunhong
    msix->phys_iomem_base = (char *)msix->phys_iomem_base
581 3854ca57 Jiang Yunhong
        + msix->table_offset_adjust;
582 3854ca57 Jiang Yunhong
583 3854ca57 Jiang Yunhong
    XEN_PT_LOG(d, "mapping physical MSI-X table to %p\n",
584 3854ca57 Jiang Yunhong
               msix->phys_iomem_base);
585 3854ca57 Jiang Yunhong
586 3854ca57 Jiang Yunhong
    memory_region_add_subregion_overlap(&s->bar[bar_index], table_off,
587 3854ca57 Jiang Yunhong
                                        &msix->mmio,
588 3854ca57 Jiang Yunhong
                                        2); /* Priority: pci default + 1 */
589 3854ca57 Jiang Yunhong
590 3854ca57 Jiang Yunhong
    return 0;
591 3854ca57 Jiang Yunhong
592 3854ca57 Jiang Yunhong
error_out:
593 3854ca57 Jiang Yunhong
    memory_region_destroy(&msix->mmio);
594 3854ca57 Jiang Yunhong
    g_free(s->msix);
595 3854ca57 Jiang Yunhong
    s->msix = NULL;
596 3854ca57 Jiang Yunhong
    return rc;
597 3854ca57 Jiang Yunhong
}
598 3854ca57 Jiang Yunhong
599 3854ca57 Jiang Yunhong
void xen_pt_msix_delete(XenPCIPassthroughState *s)
600 3854ca57 Jiang Yunhong
{
601 3854ca57 Jiang Yunhong
    XenPTMSIX *msix = s->msix;
602 3854ca57 Jiang Yunhong
603 3854ca57 Jiang Yunhong
    if (!msix) {
604 3854ca57 Jiang Yunhong
        return;
605 3854ca57 Jiang Yunhong
    }
606 3854ca57 Jiang Yunhong
607 3854ca57 Jiang Yunhong
    /* unmap the MSI-X memory mapped register area */
608 3854ca57 Jiang Yunhong
    if (msix->phys_iomem_base) {
609 3854ca57 Jiang Yunhong
        XEN_PT_LOG(&s->dev, "unmapping physical MSI-X table from %p\n",
610 3854ca57 Jiang Yunhong
                   msix->phys_iomem_base);
611 3854ca57 Jiang Yunhong
        munmap(msix->phys_iomem_base, msix->total_entries * PCI_MSIX_ENTRY_SIZE
612 3854ca57 Jiang Yunhong
               + msix->table_offset_adjust);
613 3854ca57 Jiang Yunhong
    }
614 3854ca57 Jiang Yunhong
615 3854ca57 Jiang Yunhong
    memory_region_del_subregion(&s->bar[msix->bar_index], &msix->mmio);
616 3854ca57 Jiang Yunhong
    memory_region_destroy(&msix->mmio);
617 3854ca57 Jiang Yunhong
618 3854ca57 Jiang Yunhong
    g_free(s->msix);
619 3854ca57 Jiang Yunhong
    s->msix = NULL;
620 3854ca57 Jiang Yunhong
}