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 <[email protected]> Signed-off-by: Luigi Leonardi <[email protected]> --- hw/nvme/ns.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 7f0f9ac766..83786b4401 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -727,6 +727,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; @@ -891,6 +895,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); + } } } @@ -1117,10 +1134,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 = { --- base-commit: 9696bf5dc5a5bf0b4a9d05b6cdfe5f13990f97aa change-id: 20260818-fix_nvme-d8cfaa3368c9 Best regards, -- Luigi Leonardi <[email protected]>
