From 4b7f6284eaa3e3b15360e25848fc68b37ee6c768 Mon Sep 17 00:00:00 2001
From: zhaotianrui <zhaotianrui@loongson.cn>
Date: Wed, 11 Jan 2023 11:56:03 -0500
Subject: [PATCH 3/4] Config some capabilities for loongarch virt machine

Config some capabilities for loongarch virt machine such as
PCI multi bus and the path of loongarch uefi binary.

Signed-off-by: zhaotianrui <zhaotianrui@loongson.cn>
---
 src/qemu/qemu.conf                 |  3 +-
 src/qemu/qemu_capabilities.c       |  4 ++
 src/qemu/qemu_conf.c               |  3 +-
 src/qemu/qemu_domain.c             | 31 ++++++++++-
 src/qemu/qemu_domain.h             |  1 +
 src/qemu/qemu_domain_address.c     | 86 ++++++++++++++++++++++++++++++
 src/qemu/test_libvirtd_qemu.aug.in |  1 +
 7 files changed, 125 insertions(+), 4 deletions(-)

diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf
index abdbf07fec..3856087248 100644
--- a/src/qemu/qemu.conf
+++ b/src/qemu/qemu.conf
@@ -771,7 +771,8 @@
 #   "/usr/share/OVMF/OVMF_CODE.fd:/usr/share/OVMF/OVMF_VARS.fd",
 #   "/usr/share/OVMF/OVMF_CODE.secboot.fd:/usr/share/OVMF/OVMF_VARS.fd",
 #   "/usr/share/AAVMF/AAVMF_CODE.fd:/usr/share/AAVMF/AAVMF_VARS.fd",
-#   "/usr/share/AAVMF/AAVMF32_CODE.fd:/usr/share/AAVMF/AAVMF32_VARS.fd"
+#   "/usr/share/AAVMF/AAVMF32_CODE.fd:/usr/share/AAVMF/AAVMF32_VARS.fd",
+#   "/usr/share/qemu/loongarch_bios.bin:/usr/share/qemu/loongarch_vars.bin"
 #]
 
 # The backend to use for handling stdout/stderr output from
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 0c3eb148b2..21b477cd4d 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -1983,6 +1983,10 @@ bool virQEMUCapsHasPCIMultiBus(virQEMUCapsPtr qemuCaps,
      * since forever */
     if (ARCH_IS_SW64(def->os.arch))
         return true;
+    /* loongarch64 support PCI-multibus on all machine types
+     * since forever */
+    if (ARCH_IS_LOONGARCH(def->os.arch))
+        return true;
 
     if (def->os.arch == VIR_ARCH_PPC ||
         ARCH_IS_PPC64(def->os.arch)) {
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 28319a1baf..3253875d6e 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -98,7 +98,8 @@ qemuDriverUnlock(virQEMUDriverPtr driver)
     "/usr/share/OVMF/OVMF_CODE.fd:/usr/share/OVMF/OVMF_VARS.fd:" \
     "/usr/share/OVMF/OVMF_CODE.secboot.fd:/usr/share/OVMF/OVMF_VARS.fd:" \
     "/usr/share/AAVMF/AAVMF_CODE.fd:/usr/share/AAVMF/AAVMF_VARS.fd:" \
-    "/usr/share/AAVMF/AAVMF32_CODE.fd:/usr/share/AAVMF/AAVMF32_VARS.fd"
+    "/usr/share/AAVMF/AAVMF32_CODE.fd:/usr/share/AAVMF/AAVMF32_VARS.fd:" \
+    "/usr/share/qemu/loongarch_bios.bin:/usr/share/qemu/loongarch_vars.bin"
 #endif
 
 
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 37dac3694b..152c8615d5 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -5186,7 +5186,7 @@ qemuDomainDefValidateFeatures(const virDomainDef *def,
         switch ((virDomainFeature) i) {
         case VIR_DOMAIN_FEATURE_IOAPIC:
             if (def->features[i] != VIR_DOMAIN_IOAPIC_NONE) {
-                if (!ARCH_IS_X86(def->os.arch)) {
+                if (!ARCH_IS_X86(def->os.arch) && !ARCH_IS_LOONGARCH(def->os.arch)) {
                     virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                    _("The '%s' feature is not supported for "
                                      "architecture '%s' or machine type '%s'"),
@@ -9089,6 +9089,11 @@ qemuDomainControllerDefPostParse(virDomainControllerDefPtr cont,
                     cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
                 else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI))
                     cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI;
+            } else if (ARCH_IS_LOONGARCH(def->os.arch)) {
+                if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI))
+                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
+                else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI))
+                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI;
             }
         }
         /* forbid usb model 'qusb1' and 'qusb2' in this kind of hyperviosr */
@@ -12985,6 +12990,20 @@ qemuDomainMachineIsPSeries(const char *machine,
     return false;
 }
 
+static bool
+qemuDomainMachineIsLoongson(const char *machine,
+                            const virArch arch)
+{
+    if (!ARCH_IS_LOONGARCH(arch))
+        return false;
+
+    if (STREQ(machine, "loongson7a") ||
+        STRPREFIX(machine, "loongson7a-")) {
+        return true;
+    }
+
+    return false;
+}
 
 /* You should normally avoid this function and use
  * qemuDomainHasBuiltinIDE() instead. */
@@ -12996,7 +13015,8 @@ qemuDomainMachineHasBuiltinIDE(const char *machine,
         STREQ(machine, "malta") ||
         STREQ(machine, "sun4u") ||
         STREQ(machine, "core3") ||
-        STREQ(machine, "g3beige");
+        STREQ(machine, "g3beige") ||
+        STREQ(machine, "loongson7a");
 }
 
 
@@ -13066,6 +13086,13 @@ qemuDomainIsPSeries(const virDomainDef *def)
 }
 
 
+bool
+qemuDomainIsLoongson(const virDomainDef *def)
+{
+    return qemuDomainMachineIsLoongson(def->os.machine, def->os.arch);
+}
+
+
 bool
 qemuDomainHasPCIRoot(const virDomainDef *def)
 {
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 7603724ccd..368fc4030f 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -999,6 +999,7 @@ bool qemuDomainIsS390CCW(const virDomainDef *def);
 bool qemuDomainIsARMVirt(const virDomainDef *def);
 bool qemuDomainIsRISCVVirt(const virDomainDef *def);
 bool qemuDomainIsPSeries(const virDomainDef *def);
+bool qemuDomainIsLoongson(const virDomainDef *def);
 bool qemuDomainHasPCIRoot(const virDomainDef *def);
 bool qemuDomainHasPCIeRoot(const virDomainDef *def);
 bool qemuDomainHasBuiltinIDE(const virDomainDef *def);
diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c
index ab6bce19f4..7f48773832 100644
--- a/src/qemu/qemu_domain_address.c
+++ b/src/qemu/qemu_domain_address.c
@@ -2078,6 +2078,87 @@ qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def,
     return 0;
 }
 
+static int
+qemuDomainValidateDevicePCISlotsLoongson(virDomainDefPtr def,
+                                         virQEMUCapsPtr qemuCaps,
+                                         virDomainPCIAddressSetPtr addrs)
+{
+    int ret = -1;
+    virPCIDeviceAddress tmp_addr;
+    bool qemuDeviceVideoUsable = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY);
+    g_autofree char *addrStr = NULL;
+    virDomainPCIConnectFlags flags = VIR_PCI_CONNECT_TYPE_PCI_DEVICE;
+
+    if (addrs->nbuses) {
+        memset(&tmp_addr, 0, sizeof(tmp_addr));
+        tmp_addr.slot = 1;
+        /* pci-ohci at 00:01.0 */
+        if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0) < 0)
+            return -1;
+    }
+
+    if (def->nvideos > 0 &&
+        def->videos[0]->type != VIR_DOMAIN_VIDEO_TYPE_NONE &&
+        def->videos[0]->type != VIR_DOMAIN_VIDEO_TYPE_RAMFB) {
+        /* reserve slot 2 for vga device */
+        virDomainVideoDefPtr primaryVideo = def->videos[0];
+
+        if (virDeviceInfoPCIAddressIsWanted(&primaryVideo->info)) {
+            memset(&tmp_addr, 0, sizeof(tmp_addr));
+            tmp_addr.slot = 2;
+
+            if (!(addrStr = virPCIDeviceAddressAsString(&tmp_addr)))
+                return -1;
+            if (!virDomainPCIAddressValidate(addrs, &tmp_addr,
+                                             addrStr, flags, true))
+                return ret;
+
+            if (virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) {
+                if (qemuDeviceVideoUsable) {
+                    if (qemuDomainPCIAddressReserveNextAddr(addrs,
+                                                            &primaryVideo->info) < 0) {
+                        return ret;
+                    }
+                } else {
+                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                   _("PCI address 0:0:2.0 is in use, "
+                                     "QEMU needs it for primary video"));
+                    return ret;
+                }
+            } else {
+                if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0) < 0)
+                    return ret;
+                primaryVideo->info.addr.pci = tmp_addr;
+                primaryVideo->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+            }
+        } else if (!qemuDeviceVideoUsable) {
+            if (primaryVideo->info.addr.pci.domain != 0 ||
+                primaryVideo->info.addr.pci.bus != 0 ||
+                primaryVideo->info.addr.pci.slot != 2 ||
+                primaryVideo->info.addr.pci.function != 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Primary video card must have PCI address 0:0:2.0"));
+                return ret;
+            }
+            /* If TYPE == PCI, then qemuDomainCollectPCIAddress() function
+             * has already reserved the address, so we must skip */
+        }
+    } else if (addrs->nbuses && !qemuDeviceVideoUsable) {
+        memset(&tmp_addr, 0, sizeof(tmp_addr));
+        tmp_addr.slot = 2;
+
+        if (virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) {
+            VIR_DEBUG("PCI address 0:0:2.0 in use, future addition of a video"
+                      " device will not be possible without manual"
+                      " intervention");
+        } else if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0) < 0) {
+            return ret;
+        }
+    }
+
+    ret = 0;
+    return ret;
+}
 
 static int
 qemuDomainValidateDevicePCISlotsChipsets(virDomainDefPtr def,
@@ -2094,6 +2175,11 @@ qemuDomainValidateDevicePCISlotsChipsets(virDomainDefPtr def,
         return -1;
     }
 
+    if (qemuDomainIsLoongson(def) &&
+        qemuDomainValidateDevicePCISlotsLoongson(def, qemuCaps, addrs) < 0) {
+        return -1;
+    }
+
     return 0;
 }
 
diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in
index 19da591aae..caa06a45cb 100644
--- a/src/qemu/test_libvirtd_qemu.aug.in
+++ b/src/qemu/test_libvirtd_qemu.aug.in
@@ -95,6 +95,7 @@ module Test_libvirtd_qemu =
     { "2" = "/usr/share/OVMF/OVMF_CODE.secboot.fd:/usr/share/OVMF/OVMF_VARS.fd" }
     { "3" = "/usr/share/AAVMF/AAVMF_CODE.fd:/usr/share/AAVMF/AAVMF_VARS.fd" }
     { "4" = "/usr/share/AAVMF/AAVMF32_CODE.fd:/usr/share/AAVMF/AAVMF32_VARS.fd" }
+    { "5" = "/usr/share/qemu/loongarch_bios.bin:/usr/share/qemu/loongarch_vars.bin" }
 }
 { "stdio_handler" = "logd" }
 { "gluster_debug_level" = "9" }
-- 
2.25.1

