11c7955c4SPeter Xu /*
21c7955c4SPeter Xu * QEMU emulation of common X86 IOMMU
31c7955c4SPeter Xu *
41c7955c4SPeter Xu * Copyright (C) 2016 Peter Xu, Red Hat <peterx@redhat.com>
51c7955c4SPeter Xu *
61c7955c4SPeter Xu * This program is free software; you can redistribute it and/or modify
71c7955c4SPeter Xu * it under the terms of the GNU General Public License as published by
81c7955c4SPeter Xu * the Free Software Foundation; either version 2 of the License, or
91c7955c4SPeter Xu * (at your option) any later version.
101c7955c4SPeter Xu
111c7955c4SPeter Xu * This program is distributed in the hope that it will be useful,
121c7955c4SPeter Xu * but WITHOUT ANY WARRANTY; without even the implied warranty of
131c7955c4SPeter Xu * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
141c7955c4SPeter Xu * GNU General Public License for more details.
151c7955c4SPeter Xu
161c7955c4SPeter Xu * You should have received a copy of the GNU General Public License along
171c7955c4SPeter Xu * with this program; if not, see <http://www.gnu.org/licenses/>.
181c7955c4SPeter Xu */
191c7955c4SPeter Xu
201c7955c4SPeter Xu #include "qemu/osdep.h"
211c7955c4SPeter Xu #include "hw/sysbus.h"
221c7955c4SPeter Xu #include "hw/i386/x86-iommu.h"
23a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
2429396ed9SMohammed Gamal #include "hw/i386/pc.h"
2529396ed9SMohammed Gamal #include "qapi/error.h"
261cf5fd57SPeter Xu #include "qemu/error-report.h"
2702a2cbc8SPeter Xu #include "trace.h"
2850662ce1SSingh, Brijesh #include "sysemu/kvm.h"
2902a2cbc8SPeter Xu
x86_iommu_iec_register_notifier(X86IOMMUState * iommu,iec_notify_fn fn,void * data)3002a2cbc8SPeter Xu void x86_iommu_iec_register_notifier(X86IOMMUState *iommu,
3102a2cbc8SPeter Xu iec_notify_fn fn, void *data)
3202a2cbc8SPeter Xu {
3302a2cbc8SPeter Xu IEC_Notifier *notifier = g_new0(IEC_Notifier, 1);
3402a2cbc8SPeter Xu
3502a2cbc8SPeter Xu notifier->iec_notify = fn;
3602a2cbc8SPeter Xu notifier->private = data;
3702a2cbc8SPeter Xu
3802a2cbc8SPeter Xu QLIST_INSERT_HEAD(&iommu->iec_notifiers, notifier, list);
3902a2cbc8SPeter Xu }
4002a2cbc8SPeter Xu
x86_iommu_iec_notify_all(X86IOMMUState * iommu,bool global,uint32_t index,uint32_t mask)4102a2cbc8SPeter Xu void x86_iommu_iec_notify_all(X86IOMMUState *iommu, bool global,
4202a2cbc8SPeter Xu uint32_t index, uint32_t mask)
4302a2cbc8SPeter Xu {
4402a2cbc8SPeter Xu IEC_Notifier *notifier;
4502a2cbc8SPeter Xu
4602a2cbc8SPeter Xu trace_x86_iommu_iec_notify(global, index, mask);
4702a2cbc8SPeter Xu
4802a2cbc8SPeter Xu QLIST_FOREACH(notifier, &iommu->iec_notifiers, list) {
4902a2cbc8SPeter Xu if (notifier->iec_notify) {
5002a2cbc8SPeter Xu notifier->iec_notify(notifier->private, global,
5102a2cbc8SPeter Xu index, mask);
5202a2cbc8SPeter Xu }
5302a2cbc8SPeter Xu }
5402a2cbc8SPeter Xu }
551cf5fd57SPeter Xu
5635c24501SSingh, Brijesh /* Generate one MSI message from VTDIrq info */
x86_iommu_irq_to_msi_message(X86IOMMUIrq * irq,MSIMessage * msg_out)5735c24501SSingh, Brijesh void x86_iommu_irq_to_msi_message(X86IOMMUIrq *irq, MSIMessage *msg_out)
5835c24501SSingh, Brijesh {
5935c24501SSingh, Brijesh X86IOMMU_MSIMessage msg = {};
6035c24501SSingh, Brijesh
6135c24501SSingh, Brijesh /* Generate address bits */
6235c24501SSingh, Brijesh msg.dest_mode = irq->dest_mode;
6335c24501SSingh, Brijesh msg.redir_hint = irq->redir_hint;
6435c24501SSingh, Brijesh msg.dest = irq->dest;
6535c24501SSingh, Brijesh msg.__addr_hi = irq->dest & 0xffffff00;
6637cf5cecSThomas Huth msg.__addr_head = 0xfee;
6735c24501SSingh, Brijesh /* Keep this from original MSI address bits */
6835c24501SSingh, Brijesh msg.__not_used = irq->msi_addr_last_bits;
6935c24501SSingh, Brijesh
7035c24501SSingh, Brijesh /* Generate data bits */
7135c24501SSingh, Brijesh msg.vector = irq->vector;
7235c24501SSingh, Brijesh msg.delivery_mode = irq->delivery_mode;
7335c24501SSingh, Brijesh msg.level = 1;
7435c24501SSingh, Brijesh msg.trigger_mode = irq->trigger_mode;
7535c24501SSingh, Brijesh
7635c24501SSingh, Brijesh msg_out->address = msg.msi_addr;
7735c24501SSingh, Brijesh msg_out->data = msg.msi_data;
7835c24501SSingh, Brijesh }
7935c24501SSingh, Brijesh
x86_iommu_get_default(void)801cf5fd57SPeter Xu X86IOMMUState *x86_iommu_get_default(void)
811cf5fd57SPeter Xu {
821b3bf138SJean-Philippe Brucker MachineState *ms = MACHINE(qdev_get_machine());
831b3bf138SJean-Philippe Brucker PCMachineState *pcms =
841b3bf138SJean-Philippe Brucker PC_MACHINE(object_dynamic_cast(OBJECT(ms), TYPE_PC_MACHINE));
851b3bf138SJean-Philippe Brucker
861b3bf138SJean-Philippe Brucker if (pcms &&
871b3bf138SJean-Philippe Brucker object_dynamic_cast(OBJECT(pcms->iommu), TYPE_X86_IOMMU_DEVICE)) {
881b3bf138SJean-Philippe Brucker return X86_IOMMU_DEVICE(pcms->iommu);
891b3bf138SJean-Philippe Brucker }
901b3bf138SJean-Philippe Brucker return NULL;
911cf5fd57SPeter Xu }
921c7955c4SPeter Xu
x86_iommu_realize(DeviceState * dev,Error ** errp)931c7955c4SPeter Xu static void x86_iommu_realize(DeviceState *dev, Error **errp)
941c7955c4SPeter Xu {
9502a2cbc8SPeter Xu X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev);
9630c60f77SEduardo Habkost X86IOMMUClass *x86_class = X86_IOMMU_DEVICE_GET_CLASS(dev);
9729396ed9SMohammed Gamal MachineState *ms = MACHINE(qdev_get_machine());
9829396ed9SMohammed Gamal MachineClass *mc = MACHINE_GET_CLASS(ms);
9929396ed9SMohammed Gamal PCMachineState *pcms =
10029396ed9SMohammed Gamal PC_MACHINE(object_dynamic_cast(OBJECT(ms), TYPE_PC_MACHINE));
10102a2cbc8SPeter Xu QLIST_INIT(&x86_iommu->iec_notifiers);
10247748bbbSPeter Xu bool irq_all_kernel = kvm_irqchip_in_kernel() && !kvm_irqchip_is_split();
10329396ed9SMohammed Gamal
104*b54a9d46SBernhard Beschow if (!pcms || !pcms->pcibus) {
10529396ed9SMohammed Gamal error_setg(errp, "Machine-type '%s' not supported by IOMMU",
10629396ed9SMohammed Gamal mc->name);
10729396ed9SMohammed Gamal return;
10829396ed9SMohammed Gamal }
10929396ed9SMohammed Gamal
110a924b3d8SPeter Xu /* If the user didn't specify IR, choose a default value for it */
111a924b3d8SPeter Xu if (x86_iommu->intr_supported == ON_OFF_AUTO_AUTO) {
11247748bbbSPeter Xu x86_iommu->intr_supported = irq_all_kernel ?
11347748bbbSPeter Xu ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON;
114a924b3d8SPeter Xu }
115a924b3d8SPeter Xu
11650662ce1SSingh, Brijesh /* Both Intel and AMD IOMMU IR only support "kernel-irqchip={off|split}" */
11747748bbbSPeter Xu if (x86_iommu_ir_supported(x86_iommu) && irq_all_kernel) {
11850662ce1SSingh, Brijesh error_setg(errp, "Interrupt Remapping cannot work with "
11950662ce1SSingh, Brijesh "kernel-irqchip=on, please use 'split|off'.");
12050662ce1SSingh, Brijesh return;
12150662ce1SSingh, Brijesh }
12250662ce1SSingh, Brijesh
1231c7955c4SPeter Xu if (x86_class->realize) {
1241c7955c4SPeter Xu x86_class->realize(dev, errp);
1251c7955c4SPeter Xu }
1261c7955c4SPeter Xu }
1271c7955c4SPeter Xu
1280b77d30aSPeter Xu static Property x86_iommu_properties[] = {
129a924b3d8SPeter Xu DEFINE_PROP_ON_OFF_AUTO("intremap", X86IOMMUState,
130a924b3d8SPeter Xu intr_supported, ON_OFF_AUTO_AUTO),
1310b77d30aSPeter Xu DEFINE_PROP_BOOL("device-iotlb", X86IOMMUState, dt_supported, false),
132dbaabb25SPeter Xu DEFINE_PROP_BOOL("pt", X86IOMMUState, pt_supported, true),
1330b77d30aSPeter Xu DEFINE_PROP_END_OF_LIST(),
1340b77d30aSPeter Xu };
1350b77d30aSPeter Xu
x86_iommu_class_init(ObjectClass * klass,void * data)1361c7955c4SPeter Xu static void x86_iommu_class_init(ObjectClass *klass, void *data)
1371c7955c4SPeter Xu {
1381c7955c4SPeter Xu DeviceClass *dc = DEVICE_CLASS(klass);
1391c7955c4SPeter Xu dc->realize = x86_iommu_realize;
1404f67d30bSMarc-André Lureau device_class_set_props(dc, x86_iommu_properties);
1411121e0afSPeter Xu }
1421121e0afSPeter Xu
x86_iommu_ir_supported(X86IOMMUState * s)143a924b3d8SPeter Xu bool x86_iommu_ir_supported(X86IOMMUState *s)
144a924b3d8SPeter Xu {
145a924b3d8SPeter Xu return s->intr_supported == ON_OFF_AUTO_ON;
146a924b3d8SPeter Xu }
147a924b3d8SPeter Xu
1481c7955c4SPeter Xu static const TypeInfo x86_iommu_info = {
1491c7955c4SPeter Xu .name = TYPE_X86_IOMMU_DEVICE,
1501c7955c4SPeter Xu .parent = TYPE_SYS_BUS_DEVICE,
1511c7955c4SPeter Xu .instance_size = sizeof(X86IOMMUState),
1521c7955c4SPeter Xu .class_init = x86_iommu_class_init,
1531c7955c4SPeter Xu .class_size = sizeof(X86IOMMUClass),
1541c7955c4SPeter Xu .abstract = true,
1551c7955c4SPeter Xu };
1561c7955c4SPeter Xu
x86_iommu_register_types(void)1571c7955c4SPeter Xu static void x86_iommu_register_types(void)
1581c7955c4SPeter Xu {
1591c7955c4SPeter Xu type_register_static(&x86_iommu_info);
1601c7955c4SPeter Xu }
1611c7955c4SPeter Xu
1621c7955c4SPeter Xu type_init(x86_iommu_register_types)
163