1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2016, Anish Gupta (anish@freebsd.org)
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
12 * disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/malloc.h>
38 #include <sys/pcpu.h>
39 #include <sys/rman.h>
40 #include <sys/smp.h>
41 #include <sys/sysctl.h>
42
43 #include <vm/vm.h>
44 #include <vm/pmap.h>
45
46 #include <dev/pci/pcivar.h>
47 #include <dev/pci/pcireg.h>
48
49 #include <machine/resource.h>
50 #include <machine/vmm.h>
51 #include <machine/pmap.h>
52 #include <machine/vmparam.h>
53 #include <machine/pci_cfgreg.h>
54
55 #include "ivhd_if.h"
56 #include "pcib_if.h"
57
58 #include "io/iommu.h"
59 #include "amdvi_priv.h"
60
61 SYSCTL_DECL(_hw_vmm);
62 SYSCTL_NODE(_hw_vmm, OID_AUTO, amdvi, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL,
63 NULL);
64
65 #define MOD_INC(a, s, m) (((a) + (s)) % ((m) * (s)))
66 #define MOD_DEC(a, s, m) (((a) - (s)) % ((m) * (s)))
67
68 /* Print RID or device ID in PCI string format. */
69 #define RID2PCI_STR(d) PCI_RID2BUS(d), PCI_RID2SLOT(d), PCI_RID2FUNC(d)
70
71 static void amdvi_dump_cmds(struct amdvi_softc *softc, int count);
72 static void amdvi_print_dev_cap(struct amdvi_softc *softc);
73
74 MALLOC_DEFINE(M_AMDVI, "amdvi", "amdvi");
75
76 extern device_t *ivhd_devs;
77
78 extern int ivhd_count;
79 SYSCTL_INT(_hw_vmm_amdvi, OID_AUTO, count, CTLFLAG_RDTUN, &ivhd_count,
80 0, NULL);
81
82 static int amdvi_enable_user = 0;
83 SYSCTL_INT(_hw_vmm_amdvi, OID_AUTO, enable, CTLFLAG_RDTUN,
84 &amdvi_enable_user, 0, NULL);
85 TUNABLE_INT("hw.vmm.amdvi_enable", &amdvi_enable_user);
86
87 #ifdef AMDVI_ATS_ENABLE
88 /* XXX: ATS is not tested. */
89 static int amdvi_enable_iotlb = 1;
90 SYSCTL_INT(_hw_vmm_amdvi, OID_AUTO, iotlb_enabled, CTLFLAG_RDTUN,
91 &amdvi_enable_iotlb, 0, NULL);
92 TUNABLE_INT("hw.vmm.enable_iotlb", &amdvi_enable_iotlb);
93 #endif
94
95 static int amdvi_host_ptp = 1; /* Use page tables for host. */
96 SYSCTL_INT(_hw_vmm_amdvi, OID_AUTO, host_ptp, CTLFLAG_RDTUN,
97 &amdvi_host_ptp, 0, NULL);
98 TUNABLE_INT("hw.vmm.amdvi.host_ptp", &amdvi_host_ptp);
99
100 /* Page table level used <= supported by h/w[v1=7]. */
101 int amdvi_ptp_level = 4;
102 SYSCTL_INT(_hw_vmm_amdvi, OID_AUTO, ptp_level, CTLFLAG_RDTUN,
103 &amdvi_ptp_level, 0, NULL);
104 TUNABLE_INT("hw.vmm.amdvi.ptp_level", &amdvi_ptp_level);
105
106 /* Disable fault event reporting. */
107 static int amdvi_disable_io_fault = 0;
108 SYSCTL_INT(_hw_vmm_amdvi, OID_AUTO, disable_io_fault, CTLFLAG_RDTUN,
109 &amdvi_disable_io_fault, 0, NULL);
110 TUNABLE_INT("hw.vmm.amdvi.disable_io_fault", &amdvi_disable_io_fault);
111
112 static uint32_t amdvi_dom_id = 0; /* 0 is reserved for host. */
113 SYSCTL_UINT(_hw_vmm_amdvi, OID_AUTO, domain_id, CTLFLAG_RD,
114 &amdvi_dom_id, 0, NULL);
115 /*
116 * Device table entry.
117 * Bus(256) x Dev(32) x Fun(8) x DTE(256 bits or 32 bytes).
118 * = 256 * 2 * PAGE_SIZE.
119 */
120 static struct amdvi_dte amdvi_dte[PCI_NUM_DEV_MAX] __aligned(PAGE_SIZE);
121 CTASSERT(PCI_NUM_DEV_MAX == 0x10000);
122 CTASSERT(sizeof(amdvi_dte) == 0x200000);
123
124 static SLIST_HEAD (, amdvi_domain) dom_head;
125
126 static inline uint32_t
127 amdvi_pci_read(struct amdvi_softc *softc, int off)
128 {
129
130 return (pci_cfgregread(PCI_RID2BUS(softc->pci_rid),
131 PCI_RID2SLOT(softc->pci_rid), PCI_RID2FUNC(softc->pci_rid),
132 off, 4));
133 }
134
135 #ifdef AMDVI_ATS_ENABLE
136 /* XXX: Should be in pci.c */
137 /*
138 * Check if device has ATS capability and its enabled.
139 * If ATS is absent or disabled, return (-1), otherwise ATS
140 * queue length.
141 */
142 static int
143 amdvi_find_ats_qlen(uint16_t devid)
144 {
145 device_t dev;
146 uint32_t off, cap;
147 int qlen = -1;
148
149 dev = pci_find_bsf(PCI_RID2BUS(devid), PCI_RID2SLOT(devid),
150 PCI_RID2FUNC(devid));
151
152 if (!dev) {
153 return (-1);
154 }
155 #define PCIM_ATS_EN BIT(31)
156
157 if (pci_find_extcap(dev, PCIZ_ATS, &off) == 0) {
158 cap = pci_read_config(dev, off + 4, 4);
159 qlen = (cap & 0x1F);
160 qlen = qlen ? qlen : 32;
161 printf("AMD-Vi: PCI device %d.%d.%d ATS %s qlen=%d\n",
162 RID2PCI_STR(devid),
163 (cap & PCIM_ATS_EN) ? "enabled" : "Disabled",
164 qlen);
165 qlen = (cap & PCIM_ATS_EN) ? qlen : -1;
166 }
167
168 return (qlen);
169 }
170
171 /*
172 * Check if an endpoint device support device IOTLB or ATS.
173 */
174 static inline bool
175 amdvi_dev_support_iotlb(struct amdvi_softc *softc, uint16_t devid)
176 {
177 struct ivhd_dev_cfg *cfg;
178 int qlen, i;
179 bool pci_ats, ivhd_ats;
180
181 qlen = amdvi_find_ats_qlen(devid);
182 if (qlen < 0)
183 return (false);
184
185 KASSERT(softc, ("softc is NULL"));
186 cfg = softc->dev_cfg;
187
188 ivhd_ats = false;
189 for (i = 0; i < softc->dev_cfg_cnt; i++) {
190 if ((cfg->start_id <= devid) && (cfg->end_id >= devid)) {
191 ivhd_ats = cfg->enable_ats;
192 break;
193 }
194 cfg++;
195 }
196
197 pci_ats = (qlen < 0) ? false : true;
198 if (pci_ats != ivhd_ats)
199 device_printf(softc->dev,
200 "BIOS bug: mismatch in ATS setting for %d.%d.%d,"
201 "ATS inv qlen = %d\n", RID2PCI_STR(devid), qlen);
202
203 /* Ignore IVRS setting and respect PCI setting. */
204 return (pci_ats);
205 }
206 #endif
207
208 /* Enable IOTLB support for IOMMU if its supported. */
209 static inline void
210 amdvi_hw_enable_iotlb(struct amdvi_softc *softc)
211 {
212 #ifndef AMDVI_ATS_ENABLE
213 softc->iotlb = false;
214 #else
215 bool supported;
216
217 supported = (softc->ivhd_flag & IVHD_FLAG_IOTLB) ? true : false;
218
219 if (softc->pci_cap & AMDVI_PCI_CAP_IOTLB) {
220 if (!supported)
221 device_printf(softc->dev, "IOTLB disabled by BIOS.\n");
222
223 if (supported && !amdvi_enable_iotlb) {
224 device_printf(softc->dev, "IOTLB disabled by user.\n");
225 supported = false;
226 }
227 } else
228 supported = false;
229
230 softc->iotlb = supported;
231
232 #endif
233 }
234
235 static int
236 amdvi_init_cmd(struct amdvi_softc *softc)
237 {
238 struct amdvi_ctrl *ctrl = softc->ctrl;
239
240 ctrl->cmd.len = 8; /* Use 256 command buffer entries. */
241 softc->cmd_max = 1 << ctrl->cmd.len;
242
243 softc->cmd = malloc(sizeof(struct amdvi_cmd) *
244 softc->cmd_max, M_AMDVI, M_WAITOK | M_ZERO);
245
246 if ((uintptr_t)softc->cmd & PAGE_MASK)
247 panic("AMDVi: Command buffer not aligned on page boundary.");
248
249 ctrl->cmd.base = vtophys(softc->cmd) / PAGE_SIZE;
250 /*
251 * XXX: Reset the h/w pointers in case IOMMU is restarting,
252 * h/w doesn't clear these pointers based on empirical data.
253 */
254 ctrl->cmd_tail = 0;
255 ctrl->cmd_head = 0;
256
257 return (0);
258 }
259
260 /*
261 * Note: Update tail pointer after we have written the command since tail
262 * pointer update cause h/w to execute new commands, see section 3.3
263 * of AMD IOMMU spec ver 2.0.
264 */
265 /* Get the command tail pointer w/o updating it. */
266 static struct amdvi_cmd *
267 amdvi_get_cmd_tail(struct amdvi_softc *softc)
268 {
269 struct amdvi_ctrl *ctrl;
270 struct amdvi_cmd *tail;
271
272 KASSERT(softc, ("softc is NULL"));
273 KASSERT(softc->cmd != NULL, ("cmd is NULL"));
274
275 ctrl = softc->ctrl;
276 KASSERT(ctrl != NULL, ("ctrl is NULL"));
277
278 tail = (struct amdvi_cmd *)((uint8_t *)softc->cmd +
279 ctrl->cmd_tail);
280
281 return (tail);
282 }
283
284 /*
285 * Update the command tail pointer which will start command execution.
286 */
287 static void
288 amdvi_update_cmd_tail(struct amdvi_softc *softc)
289 {
290 struct amdvi_ctrl *ctrl;
291 int size;
292
293 size = sizeof(struct amdvi_cmd);
294 KASSERT(softc->cmd != NULL, ("cmd is NULL"));
295
296 ctrl = softc->ctrl;
297 KASSERT(ctrl != NULL, ("ctrl is NULL"));
298
299 ctrl->cmd_tail = MOD_INC(ctrl->cmd_tail, size, softc->cmd_max);
300 softc->total_cmd++;
301
302 #ifdef AMDVI_DEBUG_CMD
303 device_printf(softc->dev, "cmd_tail: %s Tail:0x%x, Head:0x%x.\n",
304 ctrl->cmd_tail,
305 ctrl->cmd_head);
306 #endif
307
308 }
309
310 /*
311 * Various commands supported by IOMMU.
312 */
313
314 /* Completion wait command. */
315 static void
316 amdvi_cmd_cmp(struct amdvi_softc *softc, const uint64_t data)
317 {
318 struct amdvi_cmd *cmd;
319 uint64_t pa;
320
321 cmd = amdvi_get_cmd_tail(softc);
322 KASSERT(cmd != NULL, ("Cmd is NULL"));
323
324 pa = vtophys(&softc->cmp_data);
325 cmd->opcode = AMDVI_CMP_WAIT_OPCODE;
326 cmd->word0 = (pa & 0xFFFFFFF8) | AMDVI_CMP_WAIT_STORE;
327 cmd->word1 = (pa >> 32) & 0xFFFFF;
328 cmd->addr = data;
329
330 amdvi_update_cmd_tail(softc);
331 }
332
333 /* Invalidate device table entry. */
334 static void
335 amdvi_cmd_inv_dte(struct amdvi_softc *softc, uint16_t devid)
336 {
337 struct amdvi_cmd *cmd;
338
339 cmd = amdvi_get_cmd_tail(softc);
340 KASSERT(cmd != NULL, ("Cmd is NULL"));
341 cmd->opcode = AMDVI_INVD_DTE_OPCODE;
342 cmd->word0 = devid;
343 amdvi_update_cmd_tail(softc);
344 #ifdef AMDVI_DEBUG_CMD
345 device_printf(softc->dev, "Invalidated DTE:0x%x\n", devid);
346 #endif
347 }
348
349 /* Invalidate IOMMU page, use for invalidation of domain. */
350 static void
351 amdvi_cmd_inv_iommu_pages(struct amdvi_softc *softc, uint16_t domain_id,
352 uint64_t addr, bool guest_nested,
353 bool pde, bool page)
354 {
355 struct amdvi_cmd *cmd;
356
357 cmd = amdvi_get_cmd_tail(softc);
358 KASSERT(cmd != NULL, ("Cmd is NULL"));
359
360 cmd->opcode = AMDVI_INVD_PAGE_OPCODE;
361 cmd->word1 = domain_id;
362 /*
363 * Invalidate all addresses for this domain.
364 */
365 cmd->addr = addr;
366 cmd->addr |= pde ? AMDVI_INVD_PAGE_PDE : 0;
367 cmd->addr |= page ? AMDVI_INVD_PAGE_S : 0;
368
369 amdvi_update_cmd_tail(softc);
370 }
371
372 #ifdef AMDVI_ATS_ENABLE
373 /* Invalidate device IOTLB. */
374 static void
375 amdvi_cmd_inv_iotlb(struct amdvi_softc *softc, uint16_t devid)
376 {
377 struct amdvi_cmd *cmd;
378 int qlen;
379
380 if (!softc->iotlb)
381 return;
382
383 qlen = amdvi_find_ats_qlen(devid);
384 if (qlen < 0) {
385 panic("AMDVI: Invalid ATS qlen(%d) for device %d.%d.%d\n",
386 qlen, RID2PCI_STR(devid));
387 }
388 cmd = amdvi_get_cmd_tail(softc);
389 KASSERT(cmd != NULL, ("Cmd is NULL"));
390
391 #ifdef AMDVI_DEBUG_CMD
392 device_printf(softc->dev, "Invalidate IOTLB devID 0x%x"
393 " Qlen:%d\n", devid, qlen);
394 #endif
395 cmd->opcode = AMDVI_INVD_IOTLB_OPCODE;
396 cmd->word0 = devid;
397 cmd->word1 = qlen;
398 cmd->addr = AMDVI_INVD_IOTLB_ALL_ADDR |
399 AMDVI_INVD_IOTLB_S;
400 amdvi_update_cmd_tail(softc);
401 }
402 #endif
403
404 #ifdef notyet /* For Interrupt Remap. */
405 static void
406 amdvi_cmd_inv_intr_map(struct amdvi_softc *softc,
407 uint16_t devid)
408 {
409 struct amdvi_cmd *cmd;
410
411 cmd = amdvi_get_cmd_tail(softc);
412 KASSERT(cmd != NULL, ("Cmd is NULL"));
413 cmd->opcode = AMDVI_INVD_INTR_OPCODE;
414 cmd->word0 = devid;
415 amdvi_update_cmd_tail(softc);
416 #ifdef AMDVI_DEBUG_CMD
417 device_printf(softc->dev, "Invalidate INTR map of devID 0x%x\n", devid);
418 #endif
419 }
420 #endif
421
422 /* Invalidate domain using INVALIDATE_IOMMU_PAGES command. */
423 static void
424 amdvi_inv_domain(struct amdvi_softc *softc, uint16_t domain_id)
425 {
426 struct amdvi_cmd *cmd __diagused;
427
428 cmd = amdvi_get_cmd_tail(softc);
429 KASSERT(cmd != NULL, ("Cmd is NULL"));
430
431 /*
432 * See section 3.3.3 of IOMMU spec rev 2.0, software note
433 * for invalidating domain.
434 */
435 amdvi_cmd_inv_iommu_pages(softc, domain_id, AMDVI_INVD_PAGE_ALL_ADDR,
436 false, true, true);
437
438 #ifdef AMDVI_DEBUG_CMD
439 device_printf(softc->dev, "Invalidate domain:0x%x\n", domain_id);
440
441 #endif
442 }
443
444 static bool
445 amdvi_cmp_wait(struct amdvi_softc *softc)
446 {
447 #ifdef AMDVI_DEBUG_CMD
448 struct amdvi_ctrl *ctrl = softc->ctrl;
449 #endif
450 const uint64_t VERIFY = 0xA5A5;
451 volatile uint64_t *read;
452 int i;
453 bool status;
454
455 read = &softc->cmp_data;
456 *read = 0;
457 amdvi_cmd_cmp(softc, VERIFY);
458 /* Wait for h/w to update completion data. */
459 for (i = 0; i < 100 && (*read != VERIFY); i++) {
460 DELAY(1000); /* 1 ms */
461 }
462 status = (VERIFY == softc->cmp_data) ? true : false;
463
464 #ifdef AMDVI_DEBUG_CMD
465 if (status)
466 device_printf(softc->dev, "CMD completion DONE Tail:0x%x, "
467 "Head:0x%x, loop:%d.\n", ctrl->cmd_tail,
468 ctrl->cmd_head, loop);
469 #endif
470 return (status);
471 }
472
473 static void
474 amdvi_wait(struct amdvi_softc *softc)
475 {
476 struct amdvi_ctrl *ctrl;
477 int i;
478
479 KASSERT(softc, ("softc is NULL"));
480
481 ctrl = softc->ctrl;
482 KASSERT(ctrl != NULL, ("ctrl is NULL"));
483 /* Don't wait if h/w is not enabled. */
484 if ((ctrl->control & AMDVI_CTRL_EN) == 0)
485 return;
486
487 for (i = 0; i < 10; i++) {
488 if (amdvi_cmp_wait(softc))
489 return;
490 }
491
492 device_printf(softc->dev, "Error: completion failed"
493 " tail:0x%x, head:0x%x.\n",
494 ctrl->cmd_tail, ctrl->cmd_head);
495 /* Dump the last command. */
496 amdvi_dump_cmds(softc, 1);
497 }
498
499 static void
500 amdvi_dump_cmds(struct amdvi_softc *softc, int count)
501 {
502 struct amdvi_ctrl *ctrl;
503 struct amdvi_cmd *cmd;
504 int off, i;
505
506 ctrl = softc->ctrl;
507 device_printf(softc->dev, "Dump last %d command(s):\n", count);
508 /*
509 * If h/w is stuck in completion, it is the previous command,
510 * start dumping from previous command onward.
511 */
512 off = MOD_DEC(ctrl->cmd_head, sizeof(struct amdvi_cmd),
513 softc->cmd_max);
514 for (i = 0; off != ctrl->cmd_tail && i < count; i++) {
515 cmd = (struct amdvi_cmd *)((uint8_t *)softc->cmd + off);
516 printf(" [CMD%d, off:0x%x] opcode= 0x%x 0x%x"
517 " 0x%x 0x%lx\n", i, off, cmd->opcode,
518 cmd->word0, cmd->word1, cmd->addr);
519 off = MOD_INC(off, sizeof(struct amdvi_cmd), softc->cmd_max);
520 }
521 }
522
523 static int
524 amdvi_init_event(struct amdvi_softc *softc)
525 {
526 struct amdvi_ctrl *ctrl;
527
528 ctrl = softc->ctrl;
529 ctrl->event.len = 8;
530 softc->event_max = 1 << ctrl->event.len;
531 softc->event = malloc(sizeof(struct amdvi_event) *
532 softc->event_max, M_AMDVI, M_WAITOK | M_ZERO);
533 if ((uintptr_t)softc->event & PAGE_MASK) {
534 device_printf(softc->dev, "Event buffer not aligned on page.");
535 return (false);
536 }
537 ctrl->event.base = vtophys(softc->event) / PAGE_SIZE;
538
539 /* Reset the pointers. */
540 ctrl->evt_head = 0;
541 ctrl->evt_tail = 0;
542
543 return (0);
544 }
545
546 static inline void
547 amdvi_decode_evt_flag(uint16_t flag)
548 {
549
550 flag &= AMDVI_EVENT_FLAG_MASK;
551 printf(" 0x%b]\n", flag,
552 "\020"
553 "\001GN"
554 "\002NX"
555 "\003US"
556 "\004I"
557 "\005PR"
558 "\006RW"
559 "\007PE"
560 "\010RZ"
561 "\011TR"
562 );
563 }
564
565 /* See section 2.5.4 of AMD IOMMU spec ver 2.62.*/
566 static inline void
567 amdvi_decode_evt_flag_type(uint8_t type)
568 {
569
570 switch (AMDVI_EVENT_FLAG_TYPE(type)) {
571 case 0:
572 printf("RSVD\n");
573 break;
574 case 1:
575 printf("Master Abort\n");
576 break;
577 case 2:
578 printf("Target Abort\n");
579 break;
580 case 3:
581 printf("Data Err\n");
582 break;
583 default:
584 break;
585 }
586 }
587
588 static void
589 amdvi_decode_inv_dte_evt(uint16_t devid, uint16_t domid, uint64_t addr,
590 uint16_t flag)
591 {
592
593 printf("\t[IO_PAGE_FAULT EVT: devId:0x%x DomId:0x%x"
594 " Addr:0x%lx",
595 devid, domid, addr);
596 amdvi_decode_evt_flag(flag);
597 }
598
599 static void
600 amdvi_decode_pf_evt(uint16_t devid, uint16_t domid, uint64_t addr,
601 uint16_t flag)
602 {
603
604 printf("\t[IO_PAGE_FAULT EVT: devId:0x%x DomId:0x%x"
605 " Addr:0x%lx",
606 devid, domid, addr);
607 amdvi_decode_evt_flag(flag);
608 }
609
610 static void
611 amdvi_decode_dte_hwerr_evt(uint16_t devid, uint16_t domid,
612 uint64_t addr, uint16_t flag)
613 {
614
615 printf("\t[DEV_TAB_HW_ERR EVT: devId:0x%x DomId:0x%x"
616 " Addr:0x%lx", devid, domid, addr);
617 amdvi_decode_evt_flag(flag);
618 amdvi_decode_evt_flag_type(flag);
619 }
620
621 static void
622 amdvi_decode_page_hwerr_evt(uint16_t devid, uint16_t domid, uint64_t addr,
623 uint16_t flag)
624 {
625
626 printf("\t[PAGE_TAB_HW_ERR EVT: devId:0x%x DomId:0x%x"
627 " Addr:0x%lx", devid, domid, addr);
628 amdvi_decode_evt_flag(flag);
629 amdvi_decode_evt_flag_type(AMDVI_EVENT_FLAG_TYPE(flag));
630 }
631
632 static void
633 amdvi_decode_evt(struct amdvi_event *evt)
634 {
635 struct amdvi_cmd *cmd;
636
637 switch (evt->opcode) {
638 case AMDVI_EVENT_INVALID_DTE:
639 amdvi_decode_inv_dte_evt(evt->devid, evt->pasid_domid,
640 evt->addr, evt->flag);
641 break;
642
643 case AMDVI_EVENT_PFAULT:
644 amdvi_decode_pf_evt(evt->devid, evt->pasid_domid,
645 evt->addr, evt->flag);
646 break;
647
648 case AMDVI_EVENT_DTE_HW_ERROR:
649 amdvi_decode_dte_hwerr_evt(evt->devid, evt->pasid_domid,
650 evt->addr, evt->flag);
651 break;
652
653 case AMDVI_EVENT_PAGE_HW_ERROR:
654 amdvi_decode_page_hwerr_evt(evt->devid, evt->pasid_domid,
655 evt->addr, evt->flag);
656 break;
657
658 case AMDVI_EVENT_ILLEGAL_CMD:
659 /* FALL THROUGH */
660 case AMDVI_EVENT_CMD_HW_ERROR:
661 printf("\t[%s EVT]\n", (evt->opcode == AMDVI_EVENT_ILLEGAL_CMD) ?
662 "ILLEGAL CMD" : "CMD HW ERR");
663 cmd = (struct amdvi_cmd *)PHYS_TO_DMAP(evt->addr);
664 printf("\tCMD opcode= 0x%x 0x%x 0x%x 0x%lx\n",
665 cmd->opcode, cmd->word0, cmd->word1, cmd->addr);
666 break;
667
668 case AMDVI_EVENT_IOTLB_TIMEOUT:
669 printf("\t[IOTLB_INV_TIMEOUT devid:0x%x addr:0x%lx]\n",
670 evt->devid, evt->addr);
671 break;
672
673 case AMDVI_EVENT_INVALID_DTE_REQ:
674 printf("\t[INV_DTE devid:0x%x addr:0x%lx type:0x%x tr:%d]\n",
675 evt->devid, evt->addr, evt->flag >> 9,
676 (evt->flag >> 8) & 1);
677 break;
678
679 case AMDVI_EVENT_INVALID_PPR_REQ:
680 case AMDVI_EVENT_COUNTER_ZERO:
681 printf("AMD-Vi: v2 events.\n");
682 break;
683
684 default:
685 printf("Unsupported AMD-Vi event:%d\n", evt->opcode);
686 }
687 }
688
689 static void
690 amdvi_print_events(struct amdvi_softc *softc)
691 {
692 struct amdvi_ctrl *ctrl;
693 struct amdvi_event *event;
694 int i, size;
695
696 ctrl = softc->ctrl;
697 size = sizeof(struct amdvi_event);
698 for (i = 0; i < softc->event_max; i++) {
699 event = &softc->event[ctrl->evt_head / size];
700 if (!event->opcode)
701 break;
702 device_printf(softc->dev, "\t[Event%d: Head:0x%x Tail:0x%x]\n",
703 i, ctrl->evt_head, ctrl->evt_tail);
704 amdvi_decode_evt(event);
705 ctrl->evt_head = MOD_INC(ctrl->evt_head, size,
706 softc->event_max);
707 }
708 }
709
710 static int
711 amdvi_init_dte(struct amdvi_softc *softc)
712 {
713 struct amdvi_ctrl *ctrl;
714
715 ctrl = softc->ctrl;
716 ctrl->dte.base = vtophys(amdvi_dte) / PAGE_SIZE;
717 ctrl->dte.size = 0x1FF; /* 2MB device table. */
718
719 return (0);
720 }
721
722 /*
723 * Not all capabilities of IOMMU are available in ACPI IVHD flag
724 * or EFR entry, read directly from device.
725 */
726 static int
727 amdvi_print_pci_cap(device_t dev)
728 {
729 struct amdvi_softc *softc;
730 uint32_t off, cap;
731
732 softc = device_get_softc(dev);
733 off = softc->cap_off;
734
735 /*
736 * Section 3.7.1 of IOMMU sepc rev 2.0.
737 * Read capability from device.
738 */
739 cap = amdvi_pci_read(softc, off);
740
741 /* Make sure capability type[18:16] is 3. */
742 KASSERT((((cap >> 16) & 0x7) == 0x3),
743 ("Not a IOMMU capability 0x%x@0x%x", cap, off));
744
745 softc->pci_cap = cap >> 24;
746 device_printf(softc->dev, "PCI cap 0x%x@0x%x feature:%b\n",
747 cap, off, softc->pci_cap,
748 "\2\1IOTLB\2HT\3NPCache\4EFR\5CapExt");
749
750 return (0);
751 }
752
753 static void
754 amdvi_event_intr(void *arg)
755 {
756 struct amdvi_softc *softc;
757 struct amdvi_ctrl *ctrl;
758
759 softc = (struct amdvi_softc *)arg;
760 ctrl = softc->ctrl;
761 device_printf(softc->dev, "EVT INTR %ld Status:0x%x"
762 " EVT Head:0x%x Tail:0x%x]\n", softc->event_intr_cnt++,
763 ctrl->status, ctrl->evt_head, ctrl->evt_tail);
764 printf(" [CMD Total 0x%lx] Tail:0x%x, Head:0x%x.\n",
765 softc->total_cmd, ctrl->cmd_tail, ctrl->cmd_head);
766
767 amdvi_print_events(softc);
768 ctrl->status &= AMDVI_STATUS_EV_OF | AMDVI_STATUS_EV_INTR;
769 }
770
771 static void
772 amdvi_free_evt_intr_res(device_t dev)
773 {
774
775 struct amdvi_softc *softc;
776 device_t mmio_dev;
777
778 softc = device_get_softc(dev);
779 mmio_dev = softc->pci_dev;
780
781 IVHD_TEARDOWN_INTR(mmio_dev);
782 }
783
784 static bool
785 amdvi_alloc_intr_resources(struct amdvi_softc *softc)
786 {
787 struct amdvi_ctrl *ctrl;
788 device_t dev, mmio_dev;
789 int err;
790
791 dev = softc->dev;
792 mmio_dev = softc->pci_dev;
793
794 /* Clear interrupt status bits. */
795 ctrl = softc->ctrl;
796 ctrl->status &= AMDVI_STATUS_EV_OF | AMDVI_STATUS_EV_INTR;
797
798 err = IVHD_SETUP_INTR(mmio_dev, amdvi_event_intr, softc, "fault");
799 if (err)
800 device_printf(dev, "Interrupt setup failed on %s\n",
801 device_get_nameunit(mmio_dev));
802 return (err);
803 }
804
805 static void
806 amdvi_print_dev_cap(struct amdvi_softc *softc)
807 {
808 struct ivhd_dev_cfg *cfg;
809 int i;
810
811 cfg = softc->dev_cfg;
812 for (i = 0; i < softc->dev_cfg_cnt; i++) {
813 device_printf(softc->dev, "device [0x%x - 0x%x] "
814 "config:%b%s\n", cfg->start_id, cfg->end_id,
815 cfg->data,
816 "\020\001INIT\002ExtInt\003NMI"
817 "\007LINT0\010LINT1",
818 cfg->enable_ats ? "ATS enabled" : "");
819 cfg++;
820 }
821 }
822
823 static int
824 amdvi_handle_sysctl(SYSCTL_HANDLER_ARGS)
825 {
826 struct amdvi_softc *softc;
827 int result, type, error = 0;
828
829 softc = (struct amdvi_softc *)arg1;
830 type = arg2;
831
832 switch (type) {
833 case 0:
834 result = softc->ctrl->cmd_head;
835 error = sysctl_handle_int(oidp, &result, 0,
836 req);
837 break;
838 case 1:
839 result = softc->ctrl->cmd_tail;
840 error = sysctl_handle_int(oidp, &result, 0,
841 req);
842 break;
843 case 2:
844 result = softc->ctrl->evt_head;
845 error = sysctl_handle_int(oidp, &result, 0,
846 req);
847 break;
848 case 3:
849 result = softc->ctrl->evt_tail;
850 error = sysctl_handle_int(oidp, &result, 0,
851 req);
852 break;
853
854 default:
855 device_printf(softc->dev, "Unknown sysctl:%d\n", type);
856 }
857
858 return (error);
859 }
860
861 static void
862 amdvi_add_sysctl(struct amdvi_softc *softc)
863 {
864 struct sysctl_oid_list *child;
865 struct sysctl_ctx_list *ctx;
866 device_t dev;
867
868 dev = softc->dev;
869 ctx = device_get_sysctl_ctx(dev);
870 child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
871
872 SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "event_intr_count", CTLFLAG_RD,
873 &softc->event_intr_cnt, "Event interrupt count");
874 SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "command_count", CTLFLAG_RD,
875 &softc->total_cmd, "Command submitted count");
876 SYSCTL_ADD_U16(ctx, child, OID_AUTO, "pci_rid", CTLFLAG_RD,
877 &softc->pci_rid, 0, "IOMMU RID");
878 SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "command_head",
879 CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE, softc, 0,
880 amdvi_handle_sysctl, "IU", "Command head");
881 SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "command_tail",
882 CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE, softc, 1,
883 amdvi_handle_sysctl, "IU", "Command tail");
884 SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "event_head",
885 CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE, softc, 2,
886 amdvi_handle_sysctl, "IU", "Command head");
887 SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "event_tail",
888 CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE, softc, 3,
889 amdvi_handle_sysctl, "IU", "Command tail");
890 }
891
892 int
893 amdvi_setup_hw(struct amdvi_softc *softc)
894 {
895 device_t dev;
896 int status;
897
898 dev = softc->dev;
899
900 amdvi_hw_enable_iotlb(softc);
901
902 amdvi_print_dev_cap(softc);
903
904 if ((status = amdvi_print_pci_cap(dev)) != 0) {
905 device_printf(dev, "PCI capability.\n");
906 return (status);
907 }
908 if ((status = amdvi_init_cmd(softc)) != 0) {
909 device_printf(dev, "Couldn't configure command buffer.\n");
910 return (status);
911 }
912 if ((status = amdvi_init_event(softc)) != 0) {
913 device_printf(dev, "Couldn't configure event buffer.\n");
914 return (status);
915 }
916 if ((status = amdvi_init_dte(softc)) != 0) {
917 device_printf(dev, "Couldn't configure device table.\n");
918 return (status);
919 }
920 if ((status = amdvi_alloc_intr_resources(softc)) != 0) {
921 return (status);
922 }
923 amdvi_add_sysctl(softc);
924 return (0);
925 }
926
927 int
928 amdvi_teardown_hw(struct amdvi_softc *softc)
929 {
930 device_t dev;
931
932 dev = softc->dev;
933
934 /*
935 * Called after disable, h/w is stopped by now, free all the resources.
936 */
937 amdvi_free_evt_intr_res(dev);
938
939 if (softc->cmd)
940 free(softc->cmd, M_AMDVI);
941
942 if (softc->event)
943 free(softc->event, M_AMDVI);
944
945 return (0);
946 }
947
948 /*********** bhyve interfaces *********************/
949 static int
950 amdvi_init(void)
951 {
952 if (!ivhd_count) {
953 return (EIO);
954 }
955 if (!amdvi_enable_user && ivhd_count) {
956 printf("bhyve: Found %d AMD-Vi/IOMMU device(s), "
957 "use hw.vmm.amdvi.enable=1 to enable pass-through.\n",
958 ivhd_count);
959 return (EINVAL);
960 }
961 return (0);
962 }
963
964 static void
965 amdvi_cleanup(void)
966 {
967 /* Nothing. */
968 }
969
970 static uint16_t
971 amdvi_domainId(void)
972 {
973
974 /*
975 * If we hit maximum domain limit, rollover leaving host
976 * domain(0).
977 * XXX: make sure that this domain is not used.
978 */
979 if (amdvi_dom_id == AMDVI_MAX_DOMAIN)
980 amdvi_dom_id = 1;
981
982 return ((uint16_t)amdvi_dom_id++);
983 }
984
985 static void
986 amdvi_do_inv_domain(uint16_t domain_id, bool create)
987 {
988 struct amdvi_softc *softc;
989 int i;
990
991 for (i = 0; i < ivhd_count; i++) {
992 softc = device_get_softc(ivhd_devs[i]);
993 KASSERT(softc, ("softc is NULL"));
994 /*
995 * If not present pages are cached, invalidate page after
996 * creating domain.
997 */
998 #if 0
999 if (create && ((softc->pci_cap & AMDVI_PCI_CAP_NPCACHE) == 0))
1000 continue;
1001 #endif
1002 amdvi_inv_domain(softc, domain_id);
1003 amdvi_wait(softc);
1004 }
1005 }
1006
1007 static void *
1008 amdvi_create_domain(vm_paddr_t maxaddr)
1009 {
1010 struct amdvi_domain *dom;
1011
1012 dom = malloc(sizeof(struct amdvi_domain), M_AMDVI, M_ZERO | M_WAITOK);
1013 dom->id = amdvi_domainId();
1014 //dom->maxaddr = maxaddr;
1015 #ifdef AMDVI_DEBUG_CMD
1016 printf("Created domain #%d\n", dom->id);
1017 #endif
1018 /*
1019 * Host domain(#0) don't create translation table.
1020 */
1021 if (dom->id || amdvi_host_ptp)
1022 dom->ptp = malloc(PAGE_SIZE, M_AMDVI, M_WAITOK | M_ZERO);
1023
1024 dom->ptp_level = amdvi_ptp_level;
1025
1026 amdvi_do_inv_domain(dom->id, true);
1027 SLIST_INSERT_HEAD(&dom_head, dom, next);
1028
1029 return (dom);
1030 }
1031
1032 static void
1033 amdvi_free_ptp(uint64_t *ptp, int level)
1034 {
1035 int i;
1036
1037 if (level < 1)
1038 return;
1039
1040 for (i = 0; i < NPTEPG ; i++) {
1041 if ((ptp[i] & AMDVI_PT_PRESENT) == 0)
1042 continue;
1043 /* XXX: Add super-page or PTE mapping > 4KB. */
1044 #ifdef notyet
1045 /* Super-page mapping. */
1046 if (AMDVI_PD_SUPER(ptp[i]))
1047 continue;
1048 #endif
1049
1050 amdvi_free_ptp((uint64_t *)PHYS_TO_DMAP(ptp[i]
1051 & AMDVI_PT_MASK), level - 1);
1052 }
1053
1054 free(ptp, M_AMDVI);
1055 }
1056
1057 static void
1058 amdvi_destroy_domain(void *arg)
1059 {
1060 struct amdvi_domain *domain;
1061
1062 domain = (struct amdvi_domain *)arg;
1063 KASSERT(domain, ("domain is NULL"));
1064 #ifdef AMDVI_DEBUG_CMD
1065 printf("Destroying domain %d\n", domain->id);
1066 #endif
1067 if (domain->ptp)
1068 amdvi_free_ptp(domain->ptp, domain->ptp_level);
1069
1070 amdvi_do_inv_domain(domain->id, false);
1071 SLIST_REMOVE(&dom_head, domain, amdvi_domain, next);
1072 free(domain, M_AMDVI);
1073 }
1074
1075 static uint64_t
1076 amdvi_set_pt(uint64_t *pt, int level, vm_paddr_t gpa,
1077 vm_paddr_t hpa, uint64_t pg_size, bool create)
1078 {
1079 uint64_t *page, pa;
1080 int shift, index;
1081 const int PT_SHIFT = 9;
1082 const int PT_INDEX_MASK = (1 << PT_SHIFT) - 1; /* Based on PT_SHIFT */
1083
1084 if (!pg_size)
1085 return (0);
1086
1087 if (hpa & (pg_size - 1)) {
1088 printf("HPA is not size aligned.\n");
1089 return (0);
1090 }
1091 if (gpa & (pg_size - 1)) {
1092 printf("HPA is not size aligned.\n");
1093 return (0);
1094 }
1095 shift = PML4SHIFT;
1096 while ((shift > PAGE_SHIFT) && (pg_size < (1UL << shift))) {
1097 index = (gpa >> shift) & PT_INDEX_MASK;
1098
1099 if ((pt[index] == 0) && create) {
1100 page = malloc(PAGE_SIZE, M_AMDVI, M_WAITOK | M_ZERO);
1101 pa = vtophys(page);
1102 pt[index] = pa | AMDVI_PT_PRESENT | AMDVI_PT_RW |
1103 ((level - 1) << AMDVI_PD_LEVEL_SHIFT);
1104 }
1105 #ifdef AMDVI_DEBUG_PTE
1106 if ((gpa % 0x1000000) == 0)
1107 printf("[level%d, shift = %d]PTE:0x%lx\n",
1108 level, shift, pt[index]);
1109 #endif
1110 #define PTE2PA(x) ((uint64_t)(x) & AMDVI_PT_MASK)
1111 pa = PTE2PA(pt[index]);
1112 pt = (uint64_t *)PHYS_TO_DMAP(pa);
1113 shift -= PT_SHIFT;
1114 level--;
1115 }
1116
1117 /* Leaf entry. */
1118 index = (gpa >> shift) & PT_INDEX_MASK;
1119
1120 if (create) {
1121 pt[index] = hpa | AMDVI_PT_RW | AMDVI_PT_PRESENT;
1122 } else
1123 pt[index] = 0;
1124
1125 #ifdef AMDVI_DEBUG_PTE
1126 if ((gpa % 0x1000000) == 0)
1127 printf("[Last level%d, shift = %d]PTE:0x%lx\n",
1128 level, shift, pt[index]);
1129 #endif
1130 return (1ULL << shift);
1131 }
1132
1133 static uint64_t
1134 amdvi_update_mapping(struct amdvi_domain *domain, vm_paddr_t gpa,
1135 vm_paddr_t hpa, uint64_t size, bool create)
1136 {
1137 uint64_t mapped, *ptp, len;
1138 int level;
1139
1140 KASSERT(domain, ("domain is NULL"));
1141 level = domain->ptp_level;
1142 KASSERT(level, ("Page table level is 0"));
1143
1144 ptp = domain->ptp;
1145 KASSERT(ptp, ("PTP is NULL"));
1146 mapped = 0;
1147 while (mapped < size) {
1148 len = amdvi_set_pt(ptp, level, gpa + mapped, hpa + mapped,
1149 PAGE_SIZE, create);
1150 if (!len) {
1151 printf("Error: Couldn't map HPA:0x%lx GPA:0x%lx\n",
1152 hpa, gpa);
1153 return (0);
1154 }
1155 mapped += len;
1156 }
1157
1158 return (mapped);
1159 }
1160
1161 static uint64_t
1162 amdvi_create_mapping(void *arg, vm_paddr_t gpa, vm_paddr_t hpa,
1163 uint64_t len)
1164 {
1165 struct amdvi_domain *domain;
1166
1167 domain = (struct amdvi_domain *)arg;
1168
1169 if (domain->id && !domain->ptp) {
1170 printf("ptp is NULL");
1171 return (-1);
1172 }
1173
1174 /*
1175 * If host domain is created w/o page table, skip IOMMU page
1176 * table set-up.
1177 */
1178 if (domain->ptp)
1179 return (amdvi_update_mapping(domain, gpa, hpa, len, true));
1180 else
1181 return (len);
1182 }
1183
1184 static uint64_t
1185 amdvi_remove_mapping(void *arg, vm_paddr_t gpa, uint64_t len)
1186 {
1187 struct amdvi_domain *domain;
1188
1189 domain = (struct amdvi_domain *)arg;
1190 /*
1191 * If host domain is created w/o page table, skip IOMMU page
1192 * table set-up.
1193 */
1194 if (domain->ptp)
1195 return (amdvi_update_mapping(domain, gpa, 0, len, false));
1196 return
1197 (len);
1198 }
1199
1200 static struct amdvi_softc *
1201 amdvi_find_iommu(uint16_t devid)
1202 {
1203 struct amdvi_softc *softc;
1204 int i, j;
1205
1206 for (i = 0; i < ivhd_count; i++) {
1207 softc = device_get_softc(ivhd_devs[i]);
1208 for (j = 0; j < softc->dev_cfg_cnt; j++)
1209 if ((devid >= softc->dev_cfg[j].start_id) &&
1210 (devid <= softc->dev_cfg[j].end_id))
1211 return (softc);
1212 }
1213
1214 return (NULL);
1215 }
1216
1217 /*
1218 * Set-up device table entry.
1219 * IOMMU spec Rev 2.0, section 3.2.2.2, some of the fields must
1220 * be set concurrently, e.g. read and write bits.
1221 */
1222 static void
1223 amdvi_set_dte(struct amdvi_domain *domain, struct amdvi_softc *softc,
1224 uint16_t devid, bool enable)
1225 {
1226 struct amdvi_dte* temp;
1227
1228 KASSERT(domain, ("domain is NULL for pci_rid:0x%x\n", devid));
1229 KASSERT(softc, ("softc is NULL for pci_rid:0x%x\n", devid));
1230
1231 temp = &amdvi_dte[devid];
1232
1233 #ifdef AMDVI_ATS_ENABLE
1234 /* If IOMMU and device support IOTLB, enable it. */
1235 if (amdvi_dev_support_iotlb(softc, devid) && softc->iotlb)
1236 temp->iotlb_enable = 1;
1237 #endif
1238
1239 /* Avoid duplicate I/O faults. */
1240 temp->sup_second_io_fault = 1;
1241 temp->sup_all_io_fault = amdvi_disable_io_fault;
1242
1243 temp->dt_valid = 1;
1244 temp->domain_id = domain->id;
1245
1246 if (enable) {
1247 if (domain->ptp) {
1248 temp->pt_base = vtophys(domain->ptp) >> 12;
1249 temp->pt_level = amdvi_ptp_level;
1250 }
1251 /*
1252 * XXX: Page table valid[TV] bit must be set even if host domain
1253 * page tables are not enabled.
1254 */
1255 temp->pt_valid = 1;
1256 temp->read_allow = 1;
1257 temp->write_allow = 1;
1258 }
1259 }
1260
1261 static void
1262 amdvi_inv_device(struct amdvi_softc *softc, uint16_t devid)
1263 {
1264 KASSERT(softc, ("softc is NULL"));
1265
1266 amdvi_cmd_inv_dte(softc, devid);
1267 #ifdef AMDVI_ATS_ENABLE
1268 if (amdvi_dev_support_iotlb(softc, devid))
1269 amdvi_cmd_inv_iotlb(softc, devid);
1270 #endif
1271 amdvi_wait(softc);
1272 }
1273
1274 static void
1275 amdvi_add_device(void *arg, uint16_t devid)
1276 {
1277 struct amdvi_domain *domain;
1278 struct amdvi_softc *softc;
1279
1280 domain = (struct amdvi_domain *)arg;
1281 KASSERT(domain != NULL, ("domain is NULL"));
1282 #ifdef AMDVI_DEBUG_CMD
1283 printf("Assigning device(%d.%d.%d) to domain:%d\n",
1284 RID2PCI_STR(devid), domain->id);
1285 #endif
1286 softc = amdvi_find_iommu(devid);
1287 if (softc == NULL)
1288 return;
1289 amdvi_set_dte(domain, softc, devid, true);
1290 amdvi_inv_device(softc, devid);
1291 }
1292
1293 static void
1294 amdvi_remove_device(void *arg, uint16_t devid)
1295 {
1296 struct amdvi_domain *domain;
1297 struct amdvi_softc *softc;
1298
1299 domain = (struct amdvi_domain *)arg;
1300 #ifdef AMDVI_DEBUG_CMD
1301 printf("Remove device(0x%x) from domain:%d\n",
1302 devid, domain->id);
1303 #endif
1304 softc = amdvi_find_iommu(devid);
1305 if (softc == NULL)
1306 return;
1307 amdvi_set_dte(domain, softc, devid, false);
1308 amdvi_inv_device(softc, devid);
1309 }
1310
1311 static void
1312 amdvi_enable(void)
1313 {
1314 struct amdvi_ctrl *ctrl;
1315 struct amdvi_softc *softc;
1316 uint64_t val;
1317 int i;
1318
1319 for (i = 0; i < ivhd_count; i++) {
1320 softc = device_get_softc(ivhd_devs[i]);
1321 KASSERT(softc, ("softc is NULL\n"));
1322 ctrl = softc->ctrl;
1323 KASSERT(ctrl, ("ctrl is NULL\n"));
1324
1325 val = ( AMDVI_CTRL_EN |
1326 AMDVI_CTRL_CMD |
1327 AMDVI_CTRL_ELOG |
1328 AMDVI_CTRL_ELOGINT |
1329 AMDVI_CTRL_INV_TO_1S);
1330
1331 if (softc->ivhd_flag & IVHD_FLAG_COH)
1332 val |= AMDVI_CTRL_COH;
1333 if (softc->ivhd_flag & IVHD_FLAG_HTT)
1334 val |= AMDVI_CTRL_HTT;
1335 if (softc->ivhd_flag & IVHD_FLAG_RPPW)
1336 val |= AMDVI_CTRL_RPPW;
1337 if (softc->ivhd_flag & IVHD_FLAG_PPW)
1338 val |= AMDVI_CTRL_PPW;
1339 if (softc->ivhd_flag & IVHD_FLAG_ISOC)
1340 val |= AMDVI_CTRL_ISOC;
1341
1342 ctrl->control = val;
1343 }
1344 }
1345
1346 static void
1347 amdvi_disable(void)
1348 {
1349 struct amdvi_ctrl *ctrl;
1350 struct amdvi_softc *softc;
1351 int i;
1352
1353 for (i = 0; i < ivhd_count; i++) {
1354 softc = device_get_softc(ivhd_devs[i]);
1355 KASSERT(softc, ("softc is NULL\n"));
1356 ctrl = softc->ctrl;
1357 KASSERT(ctrl, ("ctrl is NULL\n"));
1358
1359 ctrl->control = 0;
1360 }
1361 }
1362
1363 static void
1364 amdvi_invalidate_tlb(void *arg)
1365 {
1366 struct amdvi_domain *domain;
1367
1368 domain = (struct amdvi_domain *)arg;
1369 KASSERT(domain, ("domain is NULL"));
1370 amdvi_do_inv_domain(domain->id, false);
1371 }
1372
1373 const struct iommu_ops iommu_ops_amd = {
1374 .init = amdvi_init,
1375 .cleanup = amdvi_cleanup,
1376 .enable = amdvi_enable,
1377 .disable = amdvi_disable,
1378 .create_domain = amdvi_create_domain,
1379 .destroy_domain = amdvi_destroy_domain,
1380 .create_mapping = amdvi_create_mapping,
1381 .remove_mapping = amdvi_remove_mapping,
1382 .add_device = amdvi_add_device,
1383 .remove_device = amdvi_remove_device,
1384 .invalidate_tlb = amdvi_invalidate_tlb
1385 };
Cache object: 189a0c447f6d74098527922393b35241
|