Commit f6c0588fd7 for qemu.org
commit f6c0588fd7c22caefc5b31f485c8bb3ee994a58b
Author: Luigi Leonardi <leonardi@redhat.com>
Date: Wed Aug 19 10:04:42 2026 +0200
hw/nvme: fix firmware boot path when nvme-ns has a bootindex
The correct way to register an NVMe namespace in fw_boot_order is to use
the NVMe controller as the device pointer with the namespace ID appended
as a suffix, producing:
/pci@i0cf8/pci1b36,0010@4/namespace@1,0
This is how the controller-level bootindex works.
Explicit nvme-ns devices instead register the nvme-ns device itself,
which is wrong: realize() reparents the namespace onto the NVMe subsystem
bus, disconnecting it from the controller in the bus hierarchy.
qdev_get_fw_dev_path() therefore produces a path without the controller's
address:
/nvme-ns/namespace@1,0
Fix by re-registering in realize(), once the controller and final nsid
are known, using the controller as the device pointer with the namespace
suffix. The old entry (registered against the nvme-ns device) must be
removed first since add_boot_device_path() only removes entries matching
the same device pointer, and leaving both would cause a
duplicate-bootindex error.
device_add_bootindex_property() cannot be called in realize() because
QEMU parses command-line properties before realize() runs, so the
bootindex property must exist by instance_init() time. The final nsid is
not yet known then either as it's assigned in realize().
The fix is restricted to non-shared namespaces: for shared namespaces
there is no single controller to register the boot path against.
Also add the symmetric unrealize() cleanup so that hot-unplugging a
non-shared nvme-ns with a bootindex removes the fw_boot_order entry that
was registered against the controller in realize().
Reported-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Luigi Leonardi <leonardi@redhat.com>
Reviewed-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index de15921b61..2ae26a9acb 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -792,6 +792,10 @@ static void nvme_ns_unrealize(DeviceState *dev)
nvme_ns_shutdown(ns);
nvme_ns_cleanup(ns);
+ if (!ns->params.shared && ns->bootindex >= 0) {
+ del_boot_device_path(DEVICE(ns->ctrl), ns->bootindex_suffix);
+ }
+
/* Symmetric with nvme_ns_realize() which sets subsys->namespaces[nsid]. */
if (subsys && nsid && subsys->namespaces[nsid] == ns) {
subsys->namespaces[nsid] = NULL;
@@ -956,6 +960,19 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp)
if (!ns->params.shared) {
ns->ctrl = n;
+
+ /*
+ * Register the boot device path using the NVMe controller
+ * so the OFW path includes the controller's PCI address:
+ * /pci@i0cf8/pci1b36,0010@<slot>,0/namespace@<nsid>,0
+ */
+ if (ns->bootindex >= 0) {
+ del_boot_device_path(dev, NULL);
+ snprintf(ns->bootindex_suffix, sizeof(ns->bootindex_suffix),
+ "/namespace@%" PRIu32 ",0", nsid);
+ add_boot_device_path(ns->bootindex, DEVICE(n),
+ ns->bootindex_suffix);
+ }
}
}
@@ -1182,10 +1199,8 @@ static void nvme_ns_instance_init(Object *obj)
{
NvmeNamespace *ns = NVME_NS(obj);
- sprintf(ns->bootindex_suffix, "/namespace@%" PRIu32 ",0", ns->params.nsid);
-
device_add_bootindex_property(obj, &ns->bootindex, "bootindex",
- ns->bootindex_suffix, DEVICE(obj));
+ NULL, DEVICE(obj));
}
static const TypeInfo nvme_ns_info = {