[PATCH v2] Loading new machines and devices from external modules

Drap Anton posted 1 patch 1 year, 9 months ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20220719115922.306265-1-anton.drap@auriga.com
Maintainers: Markus Armbruster <armbru@redhat.com>, John Snow <jsnow@redhat.com>, Cleber Rosa <crosa@redhat.com>, Paolo Bonzini <pbonzini@redhat.com>
There is a newer version of this series
include/qapi/qmp/qjson.h    |   2 +
include/qemu/module.h       |   4 +-
qemu-options.hx             |  38 ++++++
qobject/qjson.c             |  14 ++
scripts/modinfo-generate.py |   7 +-
softmmu/vl.c                |  14 ++
util/module.c               | 249 +++++++++++++++++++++++++++++++++++-
7 files changed, 320 insertions(+), 8 deletions(-)
[PATCH v2] Loading new machines and devices from external modules
Posted by Drap Anton 1 year, 9 months ago
From: "Drap, Anton" <anton.drap@auriga.com>

There is no mechanism to load external machines and classes from modules
at the moment. This patch is to add two parameters `add_machine` and
`add_modinfo` for it.
`add_machine` is to add machines from external modules.
`add_modinfo` is to add devices from external modules, needed for a new
machine, for example.
Main aim is to have possibility to develop independent models and be able
to use it with mainline QEMU. It will help to make develop new models of
proprietary boards, simplify to use Qemu by hardware developers and extend
number of supporting boards and devices in QEMU. It will be easier for
small hardware manufacturers to use QEMU to develop their own board models
and use them to shift left of FW/SW development.

Signed-off-by: Drap Anton <anton.drap@auriga.com>
---
v2:
  - subject is fixed
---
 include/qapi/qmp/qjson.h    |   2 +
 include/qemu/module.h       |   4 +-
 qemu-options.hx             |  38 ++++++
 qobject/qjson.c             |  14 ++
 scripts/modinfo-generate.py |   7 +-
 softmmu/vl.c                |  14 ++
 util/module.c               | 249 +++++++++++++++++++++++++++++++++++-
 7 files changed, 320 insertions(+), 8 deletions(-)

diff --git a/include/qapi/qmp/qjson.h b/include/qapi/qmp/qjson.h
index 7bd8d2de1b..bd1a44ce90 100644
--- a/include/qapi/qmp/qjson.h
+++ b/include/qapi/qmp/qjson.h
@@ -25,6 +25,8 @@ QDict *qdict_from_vjsonf_nofail(const char *string, va_list ap)
 QDict *qdict_from_jsonf_nofail(const char *string, ...)
     G_GNUC_PRINTF(1, 2);
 
+QDict *qdict_from_json_nofail_nofmt(const char *string);
+
 GString *qobject_to_json(const QObject *obj);
 GString *qobject_to_json_pretty(const QObject *obj, bool pretty);
 
diff --git a/include/qemu/module.h b/include/qemu/module.h
index bd73607104..cdd3a3ceef 100644
--- a/include/qemu/module.h
+++ b/include/qemu/module.h
@@ -73,6 +73,8 @@ bool module_load_one(const char *prefix, const char *lib_name, bool mayfail);
 void module_load_qom_one(const char *type);
 void module_load_qom_all(void);
 void module_allow_arch(const char *arch);
+bool load_external_modules(const char *mods_list);
+bool add_modinfo(const char *filename);
 
 /**
  * DOC: module info annotation macros
@@ -154,7 +156,7 @@ void module_allow_arch(const char *arch);
 typedef struct QemuModinfo QemuModinfo;
 struct QemuModinfo {
     const char *name;
-    const char *arch;
+    const char **arch;
     const char **objs;
     const char **deps;
     const char **opts;
diff --git a/qemu-options.hx b/qemu-options.hx
index 377d22fbd8..804d15e0f0 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -5476,6 +5476,44 @@ SRST
             (qemu) qom-set /objects/iothread1 poll-max-ns 100000
 ERST
 
+DEF("add_machines", HAS_ARG, QEMU_OPTION_add_machines, \
+    "-add_machines libname[,...]\n",
+    QEMU_ARCH_ALL)
+SRST
+``-add_machines libname[,...]``
+    Add machines from external modules.
+    For example:
+
+    ::
+
+        -add_machines custom-arm-machine,custom-arm-machine2
+ERST
+
+DEF("add_modinfo", HAS_ARG, QEMU_OPTION_add_modinfo, \
+    "-add_modinfo filename\n",
+    QEMU_ARCH_ALL)
+SRST
+``-add_modinfo filename``
+    Extend modinfo from file. Used to add devices from external modules.
+    Modinfo extention file is a JSON file with dictionary of modules:
+    {
+    "short name of module": {"name": "module-name",
+                             "arch": ["supported_arch_1", "supported_arch_2",],
+                             "objs": ["object1_description", "object2_description",],
+                             "deps": ["depend_of_module_name1", "depend_of_module_name2",],
+                             "opts": ["option1", "option2",]
+                            }
+    }
+
+    Architectures should be designated as they are printed by ./configure --help in target list
+    without "-softmmu" or "-linux-user" suffixes. e.g.: "arm", "x86_64", "riscv32", etc.
+
+    For example:
+
+    ::
+
+        -add_modinfo modinfo_extention.json
+ERST
 
 HXCOMM This is the last statement. Insert new options before this line!
 
diff --git a/qobject/qjson.c b/qobject/qjson.c
index 167fcb429c..6045264594 100644
--- a/qobject/qjson.c
+++ b/qobject/qjson.c
@@ -148,6 +148,20 @@ QDict *qdict_from_jsonf_nofail(const char *string, ...)
     return qdict;
 }
 
+/*
+ * Parse @string as JSON object without %-escapes interpolated.
+ * Abort on error.  Do not use with untrusted @string.
+ * Return the resulting QDict.  It is never null.
+ */
+QDict *qdict_from_json_nofail_nofmt(const char *string)
+{
+    QDict *qdict;
+    qdict = qobject_to(QDict, qobject_from_json(string, &error_abort));
+    assert(qdict);
+    return qdict;
+}
+
+
 static void to_json(JSONWriter *writer, const char *name,
                     const QObject *obj)
 {
diff --git a/scripts/modinfo-generate.py b/scripts/modinfo-generate.py
index b1538fcced..ddfe444fea 100755
--- a/scripts/modinfo-generate.py
+++ b/scripts/modinfo-generate.py
@@ -33,7 +33,7 @@ def parse_line(line):
     return (kind, data)
 
 def generate(name, lines, enabled):
-    arch = ""
+    arch = []
     objs = []
     deps = []
     opts = []
@@ -47,7 +47,7 @@ def generate(name, lines, enabled):
             elif kind == 'opts':
                 opts.append(data)
             elif kind == 'arch':
-                arch = data;
+                arch.append(data)
             elif kind == 'kconfig':
                 # don't add a module which dependency is not enabled
                 # in kconfig
@@ -61,8 +61,7 @@ def generate(name, lines, enabled):
                 exit(1)
 
     print("    .name = \"%s\"," % name)
-    if arch != "":
-        print("    .arch = %s," % arch)
+    print_array("arch", arch)
     print_array("objs", objs)
     print_array("deps", deps)
     print_array("opts", opts)
diff --git a/softmmu/vl.c b/softmmu/vl.c
index aabd82e09a..17293eeda7 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -3465,6 +3465,20 @@ void qemu_init(int argc, char **argv, char **envp)
             case QEMU_OPTION_enable_sync_profile:
                 qsp_enable();
                 break;
+            case QEMU_OPTION_add_machines:
+                info_report("External machines loading: %s", optarg);
+                if (!load_external_modules(optarg)) {
+                    error_report("Modules loading error. Modules %s", optarg);
+                    exit(1);
+                }
+                break;
+            case QEMU_OPTION_add_modinfo:
+                info_report("Modinfo parsing: %s", optarg);
+                if (!add_modinfo(optarg)) {
+                    error_report("Modinfo (%s) adding error", optarg);
+                    exit(1);
+                }
+                break;
             case QEMU_OPTION_nouserconfig:
                 /* Nothing to be parsed here. Especially, do not error out below. */
                 break;
diff --git a/util/module.c b/util/module.c
index 8ddb0e18f5..c2eec5d1c3 100644
--- a/util/module.c
+++ b/util/module.c
@@ -21,6 +21,10 @@
 #include "qemu/module.h"
 #include "qemu/cutils.h"
 #include "qemu/config-file.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qlist.h"
+#include "qapi/qmp/qstring.h"
 #ifdef CONFIG_MODULE_UPGRADES
 #include "qemu-version.h"
 #endif
@@ -124,6 +128,7 @@ void module_init_info(const QemuModinfo *info)
     module_info = info;
 }
 
+
 void module_allow_arch(const char *arch)
 {
     module_arch = arch;
@@ -136,10 +141,20 @@ static bool module_check_arch(const QemuModinfo *modinfo)
             /* no arch set -> ignore all */
             return false;
         }
-        if (strcmp(module_arch, modinfo->arch) != 0) {
-            /* mismatch */
-            return false;
+
+        const char **arch_list = modinfo->arch;
+        const char *arch;
+
+        while ((arch = *(arch_list++))) {
+
+            if (strcmp(module_arch, arch) == 0) {
+                return true;
+            }
         }
+
+        /* modinfo->arch is not empty but no match found */
+        /* current arch is not supported */
+        return false;
     }
     return true;
 }
@@ -314,6 +329,32 @@ bool module_load_one(const char *prefix, const char *lib_name, bool mayfail)
 
 static bool module_loaded_qom_all;
 
+static void modinfo_prepend(QemuModinfo **modinfo, uint32_t mod_count,
+                     const QemuModinfo *modinfo_ext) {
+    const QemuModinfo *pinfo;
+    uint32_t mod_count_new;
+    uint32_t mod_count_ext = 0;
+    uint32_t i;
+
+    for (pinfo = modinfo_ext; pinfo->name != NULL; ++pinfo) {
+        ++mod_count_ext;
+    }
+
+    /* 1 for end of list */
+    mod_count_new = mod_count + mod_count_ext + 1;
+    *modinfo = g_realloc(*modinfo, mod_count_new * sizeof(**modinfo));
+    memmove((*modinfo) + mod_count_ext,
+            *modinfo,
+            mod_count * sizeof(**modinfo));
+    /* last entry with null name treat as end of array */
+    (*modinfo)[mod_count_new - 1].name = NULL;
+
+    for (pinfo = modinfo_ext, i = 0; pinfo->name != NULL; ++pinfo, ++i) {
+        (*modinfo)[i] = *pinfo;
+    }
+}
+
+
 void module_load_qom_one(const char *type)
 {
     const QemuModinfo *modinfo;
@@ -376,11 +417,213 @@ void qemu_load_module_for_opts(const char *group)
     }
 }
 
+bool load_external_modules(const char *mods_list)
+{
+    bool res = false;
+    g_auto(GStrv) mod_names = NULL;
+
+    mod_names = g_strsplit(mods_list, ",", -1);
+    for (int i = 0; mod_names[i]; ++i) {
+        res = module_load_one("", mod_names[i], false);
+        if (!res) {
+            error_report("Module %s not found", mod_names[i]);
+            break;
+        }
+        info_report("Module %s loaded", mod_names[i]);
+    }
+
+    return res;
+}
+
+bool add_modinfo(const char *filename)
+{
+    g_autofree char *buf = NULL;
+    gsize buflen;
+    GError *gerr = NULL;
+    QDict *modinfo_dict;
+    QList *arch;
+    QList *objs;
+    QList *deps;
+    QList *opts;
+    const QDictEntry *entry;
+    uint32_t i = 0;
+    uint32_t mod_count = 0;
+    QemuModinfo *modinfo_ext;
+
+    if (!g_file_get_contents(filename, &buf, &buflen, &gerr)) {
+        fprintf(stderr, "Cannot open modinfo extension file %s: %s\n",
+                filename, gerr->message);
+        g_error_free(gerr);
+        return false;
+    }
+
+    modinfo_dict = qdict_from_json_nofail_nofmt(buf);
+
+    if (!modinfo_dict) {
+        fprintf(stderr, "Invalid modinfo (%s) format: parsing json error\n",
+                filename);
+        g_error_free(gerr);
+        return false;
+    }
+
+    for (entry = qdict_first(modinfo_dict); entry;
+         entry = qdict_next(modinfo_dict, entry)) {
+        mod_count++;
+    }
+    if (mod_count == 0) {
+        return true;
+    }
+
+    modinfo_ext = g_malloc0(sizeof(*modinfo_ext) * (mod_count + 1));
+    /* last entry with null name treat as end of array */
+    modinfo_ext[mod_count].name = NULL;
+
+    for (entry = qdict_first(modinfo_dict), i = 0; entry;
+         entry = qdict_next(modinfo_dict, entry), ++i) {
+
+        QListEntry *qlist_entry;
+        QDict *module_dict;
+        QemuModinfo *modinfo;
+        size_t list_size;
+        uint32_t n = 0;
+
+        if (qobject_type(entry->value) != QTYPE_QDICT) {
+            fprintf(stderr, "Invalid modinfo (%s) format: entry is"
+                    " not dictionary\n", filename);
+            return false;
+        }
+
+        module_dict = qobject_to(QDict, entry->value);
+        modinfo = &modinfo_ext[i];
+
+        modinfo->name = g_strdup(qdict_get_str(module_dict, "name"));
+
+        arch = qdict_get_qlist(module_dict, "arch");
+        if (arch) {
+            n = 0;
+            list_size = qlist_size(arch);
+            modinfo->arch = g_malloc((list_size + 1) * sizeof(*modinfo->arch));
+            modinfo->arch[list_size] = NULL;
+            QLIST_FOREACH_ENTRY(arch, qlist_entry) {
+                if (qobject_type(qlist_entry->value) != QTYPE_QSTRING) {
+                    fprintf(stderr, "Invalid modinfo (%s) format: arch\n\n",
+                            filename);
+                    return false;
+                }
+                QString *qstr = qobject_to(QString, qlist_entry->value);
+                modinfo->arch[n++] = g_strdup(qstring_get_str(qstr));
+            }
+        } else {
+             modinfo->arch = NULL;
+        }
+
+        objs = qdict_get_qlist(module_dict, "objs");
+        if (objs) {
+            n = 0;
+            list_size = qlist_size(objs);
+            modinfo->objs = g_malloc((list_size + 1) * sizeof(*modinfo->objs));
+            modinfo->objs[list_size] = NULL;
+            QLIST_FOREACH_ENTRY(objs, qlist_entry) {
+                if (qobject_type(qlist_entry->value) != QTYPE_QSTRING) {
+                    fprintf(stderr, "Invalid modinfo (%s) format: objs\n\n",
+                            filename);
+                    return false;
+                }
+                QString *qstr = qobject_to(QString, qlist_entry->value);
+                modinfo->objs[n++] = g_strdup(qstring_get_str(qstr));
+            }
+        } else {
+             modinfo->objs = NULL;
+        }
+
+        deps = qdict_get_qlist(module_dict, "deps");
+        if (deps) {
+            n = 0;
+            list_size = qlist_size(deps);
+            modinfo->deps = g_malloc((list_size + 1) * sizeof(*modinfo->deps));
+            modinfo->deps[list_size] = NULL;
+            QLIST_FOREACH_ENTRY(deps, qlist_entry) {
+                if (qobject_type(qlist_entry->value) != QTYPE_QSTRING) {
+                    fprintf(stderr, "Invalid modinfo (%s) format: deps",
+                            filename);
+                    return false;
+                }
+                QString *qstr = qobject_to(QString, qlist_entry->value);
+                modinfo->deps[n++] = g_strdup(qstring_get_str(qstr));
+            }
+        } else {
+             modinfo->deps = NULL;
+        }
+
+        opts = qdict_get_qlist(module_dict, "opts");
+        if (opts) {
+            n = 0;
+            list_size = qlist_size(opts);
+            modinfo->opts = g_malloc((list_size + 1) * sizeof(*modinfo->opts));
+            modinfo->opts[list_size] = NULL;
+            QLIST_FOREACH_ENTRY(opts, qlist_entry) {
+                if (qobject_type(qlist_entry->value) != QTYPE_QSTRING) {
+                    fprintf(stderr, "Invalid modinfo (%s) format: opts\n",
+                            filename);
+                    return false;
+                }
+                QString *qstr = qobject_to(QString, qlist_entry->value);
+                modinfo->opts[n++] = g_strdup(qstring_get_str(qstr));
+            }
+        } else {
+             modinfo->opts = NULL;
+        }
+    }
+
+    qobject_unref(modinfo_dict);
+
+    modinfo_prepend(&modinfo_ext, mod_count, module_info);
+    module_init_info(modinfo_ext);
+    return true;
+}
+
+void modinfo_prepend(QemuModinfo **modinfo, uint32_t mod_count,
+                     const QemuModinfo *modinfo_ext)
+{
+    const QemuModinfo *pinfo;
+    uint32_t mod_count_new;
+    uint32_t mod_count_ext = 0;
+    uint32_t i;
+
+    for (pinfo = modinfo_ext; pinfo->name != NULL; ++pinfo) {
+        ++mod_count_ext;
+    }
+
+    /* 1 for end of list */
+    mod_count_new = mod_count + mod_count_ext + 1;
+    *modinfo = g_realloc(*modinfo, mod_count_new * sizeof(**modinfo));
+    memmove((*modinfo) + mod_count_ext,
+            *modinfo,
+            mod_count * sizeof(**modinfo));
+    /* last entry with null name treat as end of array */
+    (*modinfo)[mod_count_new - 1].name = NULL;
+
+    for (pinfo = modinfo_ext, i = 0; pinfo->name != NULL; ++pinfo, ++i) {
+        (*modinfo)[i] = *pinfo;
+    }
+}
+
+
 #else
 
 void module_allow_arch(const char *arch) {}
 void qemu_load_module_for_opts(const char *group) {}
 void module_load_qom_one(const char *type) {}
 void module_load_qom_all(void) {}
+bool load_external_modules(const char *mods_list)
+{
+    fprintf(stderr, "Modules are not enabled\n");
+    return false;
+}
+bool add_modinfo(const char *filename)
+{
+    fprintf(stderr, "Modules are not enabled\n");
+    return false;
+}
 
 #endif
-- 
2.34.1
Re: [PATCH v2] Loading new machines and devices from external modules
Posted by Daniel P. Berrangé 1 year, 9 months ago
On Tue, Jul 19, 2022 at 04:59:22PM +0500, Drap Anton wrote:
> From: "Drap, Anton" <anton.drap@auriga.com>
> 
> There is no mechanism to load external machines and classes from modules
> at the moment. This patch is to add two parameters `add_machine` and
> `add_modinfo` for it.
> `add_machine` is to add machines from external modules.
> `add_modinfo` is to add devices from external modules, needed for a new
> machine, for example.
> Main aim is to have possibility to develop independent models and be able
> to use it with mainline QEMU. It will help to make develop new models of
> proprietary boards, simplify to use Qemu by hardware developers and extend
> number of supporting boards and devices in QEMU. It will be easier for
> small hardware manufacturers to use QEMU to develop their own board models
> and use them to shift left of FW/SW development.

IIUC, this is suggesting QEMU load pre-built .so files created from
non-upstream code, to arbitrarily extend QEMU's functionality. Such
.so files will inherantly have to be GPLd as they'll derive from
QEMU's internal APIs which are GPL. Given the proposed use case is
to emulate non-released proprietary hardware, I struggle to see how
you'll fullfill the requirements for GPL licensing of the loaded .so,
without revealing your proprietary hardware design to any who receive
the .so files.


More generally, QEMU's existing loadable module usage is explicitly
designed to try to *prevent* loading of non-upstream code. It aims
to only load code that was built as part of the integrated QEMU
build process. ie, QEMU's loadable module system is about making
it possible to build many QEMU features, but then selectively load
them at runtime to reduce footprint/attack surface. It is *not*
intended to allow non-upstream code to be loaded.


Aside from our goal to prevent/discourage GPL violation through
closed source loadable modules, QEMU also has a strong desire to
not lock ourselves into supporting a public API for loadable
modules. Maintainers wish to retain flexibility to change the
internal APIs at any time.


Partially related to this topic, there is some work taking place
with the goal of making it possible to define new machine types
in QEMU from a QAPI based JSON description.  The actual hardware
devices and CPUs would still need code to be built into QEMU
and upstream, but the way the hardware devices & CPUs are wired
together would be customizable via the JSON config.  That could
get some, but not all, of the benefits you seek without the
downsides the QEMU maintainers wish to avoid.  This isn't ready
to consume yet and we don't have any firm ETA either I'm
afraid.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
Re: [PATCH v2] Loading new machines and devices from external modules
Posted by Drap, Anton 1 year, 9 months ago
Hi Guys,



Let me clarify my position about out of tree devices. Yes, I understand that current QEMU politics is to have all the supported platforms inside QEMU source tree, but actually simulator core development, development of the devices standard library and development of virtual platforms are three different tasks. Moreover different people interested in different parts of QEMU. QEMU core developers not interested in supporting and maintaining tons of platforms available on the market. Virtual platform developers not interested and usually don’t have resources to merge their changes upstream. So we have a lots of abandoned QEMU forks for different platforms. For example we’re now working on Raspberry Pi 4b implementation for our internal needs and we’re planning to merge it upstream. It’s based on some QEMU fork author of which wasn’t able to complete it and commit upstream. And it can’t be used with later QEMU without some efforts to port it to newer QEMU version. Nobody supports and maintaining it since constant efforts necessary to be in sync with QEMU mainline. So my opinion is that core development, core device library and virtual platform development should be divided to make life easier for everybody. And this changes is first step to it.



About legal reasons and GPL violations. Possibility to make .so with machine separately and load it without providing sources is a legal risk and can’t be completely solved with technical actions. Ban on external modules just makes it more difficult for everybody to use not upstream code (including GPL violators, but not only for them) and doesn’t block ability to distribute full QEMU fork with closed models without providing sources. So I don’t see any reason to make technical limitations which actually can’t solve legal problem.



Best Regards,

Anton

Software engineer from Auriga LLC<http://www.auriga.com/>

________________________________
От: Daniel P. Berrangé <berrange@redhat.com>
Отправлено: 19 июля 2022 г. 19:25
Кому: Drap Anton
Копия: qemu-devel@nongnu.org; Drap, Anton
Тема: Re: [PATCH v2] Loading new machines and devices from external modules

On Tue, Jul 19, 2022 at 04:59:22PM +0500, Drap Anton wrote:
> From: "Drap, Anton" <anton.drap@auriga.com>
>
> There is no mechanism to load external machines and classes from modules
> at the moment. This patch is to add two parameters `add_machine` and
> `add_modinfo` for it.
> `add_machine` is to add machines from external modules.
> `add_modinfo` is to add devices from external modules, needed for a new
> machine, for example.
> Main aim is to have possibility to develop independent models and be able
> to use it with mainline QEMU. It will help to make develop new models of
> proprietary boards, simplify to use Qemu by hardware developers and extend
> number of supporting boards and devices in QEMU. It will be easier for
> small hardware manufacturers to use QEMU to develop their own board models
> and use them to shift left of FW/SW development.

IIUC, this is suggesting QEMU load pre-built .so files created from
non-upstream code, to arbitrarily extend QEMU's functionality. Such
.so files will inherantly have to be GPLd as they'll derive from
QEMU's internal APIs which are GPL. Given the proposed use case is
to emulate non-released proprietary hardware, I struggle to see how
you'll fullfill the requirements for GPL licensing of the loaded .so,
without revealing your proprietary hardware design to any who receive
the .so files.


More generally, QEMU's existing loadable module usage is explicitly
designed to try to *prevent* loading of non-upstream code. It aims
to only load code that was built as part of the integrated QEMU
build process. ie, QEMU's loadable module system is about making
it possible to build many QEMU features, but then selectively load
them at runtime to reduce footprint/attack surface. It is *not*
intended to allow non-upstream code to be loaded.


Aside from our goal to prevent/discourage GPL violation through
closed source loadable modules, QEMU also has a strong desire to
not lock ourselves into supporting a public API for loadable
modules. Maintainers wish to retain flexibility to change the
internal APIs at any time.


Partially related to this topic, there is some work taking place
with the goal of making it possible to define new machine types
in QEMU from a QAPI based JSON description.  The actual hardware
devices and CPUs would still need code to be built into QEMU
and upstream, but the way the hardware devices & CPUs are wired
together would be customizable via the JSON config.  That could
get some, but not all, of the benefits you seek without the
downsides the QEMU maintainers wish to avoid.  This isn't ready
to consume yet and we don't have any firm ETA either I'm
afraid.

With regards,
Daniel
--
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
libvirt: The virtualization API<https://libvirt.org/>
libvirt.org
libvirt, virtualization, virtualization API



|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

Re: [PATCH v2] Loading new machines and devices from external modules
Posted by Peter Maydell 1 year, 9 months ago
On Tue, 19 Jul 2022 at 14:57, Drap Anton <drapas86@gmail.com> wrote:
>
> From: "Drap, Anton" <anton.drap@auriga.com>
>
> There is no mechanism to load external machines and classes from modules
> at the moment. This patch is to add two parameters `add_machine` and
> `add_modinfo` for it.
> `add_machine` is to add machines from external modules.
> `add_modinfo` is to add devices from external modules, needed for a new
> machine, for example.
> Main aim is to have possibility to develop independent models and be able
> to use it with mainline QEMU.

This is deliberate, as a policy decision. We don't really
want to encourage people to develop device and machine
models externally -- we'd rather they contributed them
upstream. Code to support out-of-tree devices and boards
is pure burden for upstream, with no benefit to us.

thanks
-- PMM