virDomainDefParseBootOptions(virDomainDefPtr def,
                             xmlXPathContextPtr ctxt)
{
    char *name = NULL;
    size_t i;
    int n;
    g_autofree xmlNodePtr *nodes = NULL;
    g_autofree char *tmp = NULL;

    /*
     * Booting options for different OS types....
     *
     *   - A bootloader (and optional kernel+initrd)  (xen)
     *   - A kernel + initrd                          (xen)
     *   - A boot device (and optional kernel+initrd) (hvm)
     *   - An init script                             (exe)
     */

    if (def->os.type == VIR_DOMAIN_OSTYPE_EXE) {
        def->os.init = virXPathString("string(./os/init[1])", ctxt);
        def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt);
        def->os.initdir = virXPathString("string(./os/initdir[1])", ctxt);
        def->os.inituser = virXPathString("string(./os/inituser[1])", ctxt);
        def->os.initgroup = virXPathString("string(./os/initgroup[1])", ctxt);

        if ((n = virXPathNodeSet("./os/initarg", ctxt, &nodes)) < 0)
            return -1;

        if (VIR_ALLOC_N(def->os.initargv, n+1) < 0)
            return -1;
        for (i = 0; i < n; i++) {
            if (!nodes[i]->children ||
                !nodes[i]->children->content) {
                virReportError(VIR_ERR_XML_ERROR, "%s",
                               _("No data supplied for <initarg> element"));
                return -1;
            }
            def->os.initargv[i] = g_strdup((const char *)nodes[i]->children->content);
        }
        def->os.initargv[n] = NULL;
        VIR_FREE(nodes);

        if ((n = virXPathNodeSet("./os/initenv", ctxt, &nodes)) < 0)
            return -1;

        if (VIR_ALLOC_N(def->os.initenv, n+1) < 0)
            return -1;
        for (i = 0; i < n; i++) {
            if (!(name = virXMLPropString(nodes[i], "name"))) {
                virReportError(VIR_ERR_XML_ERROR, "%s",
                                _("No name supplied for <initenv> element"));
                return -1;
            }

            if (!nodes[i]->children ||
                !nodes[i]->children->content) {
                virReportError(VIR_ERR_XML_ERROR,
                               _("No value supplied for <initenv name='%s'> element"),
                               name);
                return -1;
            }

            if (VIR_ALLOC(def->os.initenv[i]) < 0)
                return -1;

            def->os.initenv[i]->name = name;
            def->os.initenv[i]->value = g_strdup((const char *)nodes[i]->children->content);
        }
        def->os.initenv[n] = NULL;
        VIR_FREE(nodes);
    }

    if (def->os.type == VIR_DOMAIN_OSTYPE_XEN ||
        def->os.type == VIR_DOMAIN_OSTYPE_XENPVH ||
        def->os.type == VIR_DOMAIN_OSTYPE_HVM ||
        def->os.type == VIR_DOMAIN_OSTYPE_UML) {
        g_autofree char *firmware = NULL;
        xmlNodePtr loader_node;

        def->os.kernel = virXPathString("string(./os/kernel[1])", ctxt);
        def->os.initrd = virXPathString("string(./os/initrd[1])", ctxt);
        def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt);
        def->os.dtb = virXPathString("string(./os/dtb[1])", ctxt);
        def->os.root = virXPathString("string(./os/root[1])", ctxt);

        if (def->os.type == VIR_DOMAIN_OSTYPE_HVM &&
            (firmware = virXPathString("string(./os/@firmware)", ctxt))) {
            int fw = virDomainOsDefFirmwareTypeFromString(firmware);

            if (fw <= 0) {
                virReportError(VIR_ERR_XML_ERROR,
                               _("unknown firmware value %s"),
                               firmware);
                return -1;
            }

            def->os.firmware = fw;
        }

        if ((loader_node = virXPathNode("./os/loader[1]", ctxt))) {
            const bool fwAutoSelect = def->os.firmware != VIR_DOMAIN_OS_DEF_FIRMWARE_NONE;

            if (VIR_ALLOC(def->os.loader) < 0)
                return -1;

            if (virDomainLoaderDefParseXML(loader_node,
                                           def->os.loader,
                                           fwAutoSelect) < 0)
                return -1;

            def->os.loader->nvram = virXPathString("string(./os/nvram[1])", ctxt);
            if (!fwAutoSelect)
                def->os.loader->templt = virXPathString("string(./os/nvram[1]/@template)", ctxt);
        }
    }

    if (def->os.type == VIR_DOMAIN_OSTYPE_HVM) {
        if ((n = virXPathNodeSet("./os/acpi/table", ctxt, &nodes)) < 0)
            return -1;

        if (n > 1) {
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("Only one acpi table is supported"));
            return -1;
        }

        if (n == 1) {
            tmp = virXMLPropString(nodes[0], "type");

            if (!tmp) {
                virReportError(VIR_ERR_XML_ERROR, "%s",
                               _("Missing acpi table type"));
                return -1;
            }

            if (STREQ_NULLABLE(tmp, "slic")) {
                VIR_FREE(tmp);
                tmp = virXMLNodeContentString(nodes[0]);
                def->os.slic_table = virFileSanitizePath(tmp);
            } else {
                virReportError(VIR_ERR_XML_ERROR,
                               _("Unknown acpi table type: %s"),
                               tmp);
                return -1;
            }
        }

        if (virDomainDefParseBootXML(ctxt, def) < 0)
            return -1;
    }

    return 0;
}