{"id":2235163,"url":"http://patchwork.ozlabs.org/api/1.2/patches/2235163/?format=json","web_url":"http://patchwork.ozlabs.org/project/qemu-devel/patch/20260508183717.193630-3-tdave@nvidia.com/","project":{"id":14,"url":"http://patchwork.ozlabs.org/api/1.2/projects/14/?format=json","name":"QEMU Development","link_name":"qemu-devel","list_id":"qemu-devel.nongnu.org","list_email":"qemu-devel@nongnu.org","web_url":"","scm_url":"","webscm_url":"","list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20260508183717.193630-3-tdave@nvidia.com>","list_archive_url":null,"date":"2026-05-08T18:37:11","name":"[RFC,2/8] hw/pci: enumerate PCI bus and program bridge bus numbers","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"9c450aa462da4c858f844896a31188a244d3b962","submitter":{"id":89928,"url":"http://patchwork.ozlabs.org/api/1.2/people/89928/?format=json","name":"Tushar Dave","email":"tdave@nvidia.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/qemu-devel/patch/20260508183717.193630-3-tdave@nvidia.com/mbox/","series":[{"id":503429,"url":"http://patchwork.ozlabs.org/api/1.2/series/503429/?format=json","web_url":"http://patchwork.ozlabs.org/project/qemu-devel/list/?series=503429","date":"2026-05-08T18:37:09","name":"hw/arm/virt, hw/pci: PCI pre-enumeration and fixed BAR allocation","version":1,"mbox":"http://patchwork.ozlabs.org/series/503429/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2235163/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2235163/checks/","tags":{},"related":[],"headers":{"Return-Path":"<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=Nvidia.com header.i=@Nvidia.com header.a=rsa-sha256\n header.s=selector2 header.b=CDlF19tL;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists1p.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)","dkim=none (message not signed)\n header.d=none;dmarc=none action=none header.from=nvidia.com;"],"Received":["from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4gByYk5pm3z1yKm\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 09 May 2026 04:38:33 +1000 (AEST)","from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists1p.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1wLQ5O-0006fy-Kx; Fri, 08 May 2026 14:38:06 -0400","from eggs.gnu.org ([2001:470:142:3::10])\n by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <tdave@nvidia.com>)\n id 1wLQ5G-0006er-GH; Fri, 08 May 2026 14:37:58 -0400","from mail-centralusazlp170100005.outbound.protection.outlook.com\n ([2a01:111:f403:c111::5] helo=DM1PR04CU001.outbound.protection.outlook.com)\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <tdave@nvidia.com>)\n id 1wLQ5E-0002Yg-Qd; Fri, 08 May 2026 14:37:58 -0400","from BY5PR12MB4179.namprd12.prod.outlook.com (2603:10b6:a03:211::8)\n by CH3PR12MB8482.namprd12.prod.outlook.com (2603:10b6:610:15b::8)\n with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9891.20; Fri, 8 May\n 2026 18:37:44 +0000","from BY5PR12MB4179.namprd12.prod.outlook.com\n ([fe80::2036:e8b:9b3:f325]) by BY5PR12MB4179.namprd12.prod.outlook.com\n ([fe80::2036:e8b:9b3:f325%4]) with mapi id 15.20.9891.019; Fri, 8 May 2026\n 18:37:44 +0000"],"ARC-Seal":"i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none;\n b=J/FiBKmMAjIn5yztW+zyDoF93kKLjiP6DAzoZFUtbz2f3Uy5L0SHtwId4ACWHfQeD9WGj4efIFRFCwQMiQw48DYdBiuWCrF5KX6RyrkIrSquX5LiyGHfuJbo6lPZDR9Xygm9rhGXOhRi7y77yIw6UkXsMuQ+lG1hfMKb+SaZOdWxlVmbJf8c/haapWf58S2o8oivgFhztjucWqGpOFsgz1FBtcjZOFrHQmGYJjxzP/ZEA66nFtpvV0Gnad8Vk98hjGmuEsXvo5pIZnJQVegtnhj5CMEDEyBtQRoi8hjXCT6rvA5m0MS5Fly+6Dni3Gf11Csu2ko27/56vtvOxuN4Dg==","ARC-Message-Signature":"i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;\n s=arcselector10001;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1;\n bh=Vjsk54Bj0Tn0vx32otBq+7newk94c9VNDoiW2FCunpU=;\n b=Vxz3iYhQ7yAdiwT/UTk0rrc9LwhDjrtJpnVLQFi4R/mjsKshvr0S7/tNDuNj9njfjcgAeoXIlyJywf1Ktnd1XN2AjTtaCnB2v4/MybKbGRfDkSbc9oyW8VKPxg0vISPFGnyaYpKep6AVgTKxMG65dpnYMgcXWuE4/0tVi4QlgXliIn7CAgdQxfx1NX7PZVHTFhQCuTmShNi55DKbNw2sQakzOs1JmCRKYwiP3c2QrrNFuqUrUwn41eMBbQE18wmUjEgQNZ7p8KHOxTkfsmETIri8kDt7UVxoGlxG5pGt7V4TS10pTyM5QFWueoh5TxloY1vm29AvR4+9/VHoBCz9Fw==","ARC-Authentication-Results":"i=1; mx.microsoft.com 1; spf=pass\n smtp.mailfrom=nvidia.com; dmarc=pass action=none header.from=nvidia.com;\n dkim=pass header.d=nvidia.com; arc=none","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com;\n s=selector2;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n bh=Vjsk54Bj0Tn0vx32otBq+7newk94c9VNDoiW2FCunpU=;\n b=CDlF19tL1Zn8alZT2tRQj8dqw33oCFNieJ/IGws1NrjTvET+hZxBkpjqzRGcucaqelUx7gpOVhIo2alwCuiKdtZi/F4QeKpNAuHGBLCrdWXjZie8AoAc8FVv8xKQ4g2y57WW7jRS+/95QZmYzEvQn8GM2zVMmQA2fIubz328xlGPhCQw/LEZSYiRl48gGTEDRSaG6KPACFIea1VkU4y2dqHGg4bGlAHsFpyvakdR8cERV4anSVZ672NiZXqeP6YAEYdI6CAqXil4mR2mBnWdba9lhwTFFyTP9uUsPYuv9C2TvTQn8oP/EkdwGUeKTVEwc12Ccil6uLYdsTtk7bnaCQ==","From":"Tushar Dave <tdave@nvidia.com>","To":"qemu-devel@nongnu.org","Cc":"alwilliamson@nvidia.com, jgg@nvidia.com, skolothumtho@nvidia.com,\n qemu-arm@nongnu.org, peter.maydell@linaro.org, mst@redhat.com,\n marcel.apfelbaum@gmail.com, devel@edk2.groups.io","Subject":"[RFC PATCH 2/8] hw/pci: enumerate PCI bus and program bridge bus\n numbers","Date":"Fri,  8 May 2026 13:37:11 -0500","Message-Id":"<20260508183717.193630-3-tdave@nvidia.com>","X-Mailer":"git-send-email 2.34.1","In-Reply-To":"<20260508183717.193630-1-tdave@nvidia.com>","References":"<20260508183717.193630-1-tdave@nvidia.com>","Content-Transfer-Encoding":"8bit","Content-Type":"text/plain","X-ClientProxiedBy":"BN9PR03CA0948.namprd03.prod.outlook.com\n (2603:10b6:408:108::23) To BY5PR12MB4179.namprd12.prod.outlook.com\n (2603:10b6:a03:211::8)","MIME-Version":"1.0","X-MS-PublicTrafficType":"Email","X-MS-TrafficTypeDiagnostic":"BY5PR12MB4179:EE_|CH3PR12MB8482:EE_","X-MS-Office365-Filtering-Correlation-Id":"1efcef15-8cf5-437e-a4c2-08dead30e4ce","X-MS-Exchange-SenderADCheck":"1","X-MS-Exchange-AntiSpam-Relay":"0","X-Microsoft-Antispam":"BCL:0;\n ARA:13230040|366016|376014|1800799024|18002099003|56012099003|22082099003|3023799003;","X-Microsoft-Antispam-Message-Info":"\n +0Siqv6Q70OETSdoUC0w0Qme3tzBt5d7G/0vTEvfIarNsheGLO4Z2rUTnwL069RE/zOUjz3gT/yIBiOEZsp+BNz9Kx2c22VXc8YLc2C/BMxVQc8wmHR9iEAupvuucl1tNEnsXo7mXwBiflaFCchHsSMTwG1MVPv876rMgqJWsrU1nheeheNdZ0pnmh00dKEU6FEWf3X7zmlpM1i3lhRhZDoxPQYR76ObirafHDaGTs71o1PhDv3Zu9D5RfbRgxcMHPerJSY7wPOL5MxtUYyimmu3DIaBPCmDFZVKcce3FiiLEraJwTkqxcyZKqTf50qjG07zOKhlt7wIq50BDXag6f5GMzRFtr3h0PeNj5BVnhDTcOnpAnb6L8S9qKWPP7aIEp7ZjKw+G9vNXbMzxxpcjQS8WuYEwJ6czRerAimWAiiiq1BhSYXixNiIZeXnDofWvlNrZmYtb4eZAu7pxukNsDyUgL+Yr3jxcJ9gqy1mI6e7FtmCPk5IDXQf0fPiX38+v6RIWfDjOMiXvScHemFSlXIeZvaq+og5xKSc6RKYWAWmx+fCYEh3MQrmFtmjGsqv/yJPxfUKKiAODxIM9mgVSTpVu9FhJ2ocSpNbuAW19HQbF6yx4BE6NqT+Vm80w8UMw7BWdFKNcyWqGRCb78Kx/gECYzRwUAN2JF8Mw+1PSj5Yz7rlEEOYU6687BZDhbIv","X-Forefront-Antispam-Report":"CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:;\n IPV:NLI; SFV:NSPM; H:BY5PR12MB4179.namprd12.prod.outlook.com; PTR:; CAT:NONE;\n SFS:(13230040)(366016)(376014)(1800799024)(18002099003)(56012099003)(22082099003)(3023799003);\n DIR:OUT; SFP:1101;","X-MS-Exchange-AntiSpam-MessageData-ChunkCount":"1","X-MS-Exchange-AntiSpam-MessageData-0":"\n 6SU17shNrWdGVFL/Mvc3KsvkDgvFkM8lEyfkaYpUltbgTiQhFfC4K3nVO4SqhXy4m5CG44niWE8oXJeRkB0V4Q8bUHpx2oE3wthl72JzRPKW6ZQW284qd29eAfoJwOqmTf2siNPt2uzs0jDZhjYHCb+ogYti2x3wCBQXqqnTLn7vm8fpzPnA3wGi5WVXa9Q7eFNvH0GuY15YTCB4ZMERZzuDGmlrGTZpx7Jsy4/oU2VNUQnXgovjnPyPCmoU+wlCif8wSQ7QBUWlVgAoyLjU35dsnwekRSPhIV/4P608CSfzDDyuEdNF7Hpw98FC2wgjvs5QnANetJqoUmno39E1lBTsVFmkj5HMrLFywAPjoo8tbfAeyDgz9lwrcFluXMWOgM6qMkMIWfs6VLlhgQxmsNEoRj733Hw6uFa6Ecle+lppWG83x7hpyh2af1TNdz4ARxlFVWbBxsg6j4AemTBlg+8dPXi8AgRYg3D/W4Ge1EQXmExM3pIE0LcwHRgCQ5QQugZhnQJnYtFd3FkvXXtKBOMmb1LKiW+eLZnBAXWS4HcyR72xhKMEUkg6e9QEOhmc2RzkI5AlIbLY1ZMp5ob1wntueA5J53oDG3WwrESjbm7s2Kw2ifBUVcZRY3Vtq7vhN1Ef2Ro6edsq7otiWp6yb+U/k1aJVCmbvZZ8rAazqpT+kdVtxJ+BfIvLAdEFJnQ7d2aE69o0DTsOzqSYAALHhH2xVZHlAomx0oxuLLlzMF2C/Hab+tt956Lylae+CgY5bU4N8tyL+gYPLk+Kvw1ZoQU1PF2qNyROYPgttwqxtmVJN/+bNhSta+m5VO3Ryi64IMmJMiZrVeoFCqeceWnxUVUNYOkb0HFF4lYxv+5O4cLFTxO65IhKwLfqNbPrpIuiz4o4NSp+cnOPpfbT0QHnzd4bFq/mm1ekHMCGtyxhIFH1eP16OUfp3pn3PUUK/jtGcpmq0CBCKbg/C4TuJJ6hvSKi227oIyRpkmWwEMpn/qEhxvgPCoP7V9Mp9AfZVVV4mvL77Z+C5A665CuAKGSKJBN66vx7XiKkvwh4J8RJN4gyzxDdH7Bd8JK+vXtvlT9eNaGKS7aoJdyRSjU5+LPDtvRQ6bNyBSdnWpFz4rboIjP7UOPC/snUqPNXcPUe30VHl/8A7Fc/qsVmzgpbKZjU4gfUitsbODoOZPKbHZMTzMWjDuAWY0DOK2v9nlEz2rl8G5wn4zHCYBnmpLQo9pPldxqIuXwARvpagVw723okiyVXtpeVe7nk0e1U8+ReQjmNm/RWFBOU5bufS2oWXGOS1T57ER3EOnTzWgcbxN+NVqCdXpFqTDgtYbsJbNNRpxEtpv3sgCC79LJMsz2oF1LMnKB/GHGyYg8hdbhE529dUSyki8h5DVErmHHbpYoCr+K7DaElzFMfpAP4biZC0WLdmJrtGFXkv9sVIBwI/W0wEx90ieDdXDCNLW4e0ACiwdEigUoPQ/s9QrxkdhHh91ex2YRd43OJfonsFjlSCw95mNJX2qaQawBy4gmT6wovD0aDT4iaD3BOB56/gDTTH0FNByClmKbUrXBO4b6A1hQ0MCkdZWT8c/seWpx9oE1CzQP/M3Bc8z9Hp5/cET6O7ZExAk1eklPGirOx4LwMGcpowVkS88ARdHGFBoyWpxW2n8CuhzKFPuASaSqFIW4Pp9J1bHKCSRfWKVhwAkrl10IAp5BXRIpT1DRPROGg79Iaxs2jjUQRB0ZMTWCxx/sz0S2iEQ==","X-OriginatorOrg":"Nvidia.com","X-MS-Exchange-CrossTenant-Network-Message-Id":"\n 1efcef15-8cf5-437e-a4c2-08dead30e4ce","X-MS-Exchange-CrossTenant-AuthSource":"BY5PR12MB4179.namprd12.prod.outlook.com","X-MS-Exchange-CrossTenant-AuthAs":"Internal","X-MS-Exchange-CrossTenant-OriginalArrivalTime":"08 May 2026 18:37:44.3208 (UTC)","X-MS-Exchange-CrossTenant-FromEntityHeader":"Hosted","X-MS-Exchange-CrossTenant-Id":"43083d15-7273-40c1-b7db-39efd9ccc17a","X-MS-Exchange-CrossTenant-MailboxType":"HOSTED","X-MS-Exchange-CrossTenant-UserPrincipalName":"\n d8Pcfdavw3TYi6JEUpVHmzJUUGglXVzk3NRQX6IoyynMd1OXSfUwUJLvVYQ3y0zfzaqsTexHtG+zFiTRhINbEw==","X-MS-Exchange-Transport-CrossTenantHeadersStamped":"CH3PR12MB8482","Received-SPF":"permerror client-ip=2a01:111:f403:c111::5;\n envelope-from=tdave@nvidia.com;\n helo=DM1PR04CU001.outbound.protection.outlook.com","X-Spam_score_int":"-14","X-Spam_score":"-1.5","X-Spam_bar":"-","X-Spam_report":"(-1.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.44,\n DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1,\n FORGED_SPF_HELO=1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_PASS=-0.001,\n SPF_NONE=0.001 autolearn=no autolearn_force=no","X-Spam_action":"no action","X-BeenThere":"qemu-devel@nongnu.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"qemu development <qemu-devel.nongnu.org>","List-Unsubscribe":"<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>","List-Archive":"<https://lists.nongnu.org/archive/html/qemu-devel>","List-Post":"<mailto:qemu-devel@nongnu.org>","List-Help":"<mailto:qemu-devel-request@nongnu.org?subject=help>","List-Subscribe":"<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>","Errors-To":"qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org","Sender":"qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org"},"content":"When guest firmware is told not to perform bus enumeration, QEMU\nmust program bridge primary, secondary, and subordinate bus number\nregisters before handing control to firmware. Walk the hierarchy\nunder the root bus, assign secondary bus numbers in firmware-like\norder (PXB roots first by bus number, then PCI bridges by devfn),\nand program those bridge registers.\n\nNote that SR-IOV bus number allocation (VF offset/stride/NumVFs) is not\nhandled in this commit and requires additional work.\n\nSigned-off-by: Tushar Dave <tdave@nvidia.com>\n---\n hw/pci/meson.build     |   1 +\n hw/pci/pci-enumerate.c | 144 +++++++++++++++++++++++++++++++++++++++++\n hw/pci/pci-enumerate.h |  15 +++++\n 3 files changed, 160 insertions(+)\n create mode 100644 hw/pci/pci-enumerate.c\n create mode 100644 hw/pci/pci-enumerate.h","diff":"diff --git a/hw/pci/meson.build b/hw/pci/meson.build\nindex a6cbd89c0a..7e8f5bb87d 100644\n--- a/hw/pci/meson.build\n+++ b/hw/pci/meson.build\n@@ -5,6 +5,7 @@ pci_ss.add(files(\n   'pci.c',\n   'pci_bridge.c',\n   'pci_host.c',\n+  'pci-enumerate.c',\n   'pci-hmp-cmds.c',\n   'pci-qmp-cmds.c',\n   'pcie_sriov.c',\ndiff --git a/hw/pci/pci-enumerate.c b/hw/pci/pci-enumerate.c\nnew file mode 100644\nindex 0000000000..2c6d25b25d\n--- /dev/null\n+++ b/hw/pci/pci-enumerate.c\n@@ -0,0 +1,144 @@\n+/*\n+ * Copyright (C) 2026 NVIDIA\n+ * Written by Tushar Dave\n+ *\n+ * SPDX-License-Identifier: GPL-2.0-or-later\n+ */\n+\n+#include \"qemu/osdep.h\"\n+#include \"hw/pci/pci.h\"\n+#include \"hw/pci/pci_bridge.h\"\n+#include \"hw/pci/pci_bus.h\"\n+#include \"hw/pci/pci-enumerate.h\"\n+\n+/* Forward declaration */\n+static uint8_t pci_program_bus_numbers(PCIBus *bus, uint8_t current_bus_num,\n+                                       uint8_t *next_bus_num);\n+\n+static int cmp_bus_by_devfn(gconstpointer a, gconstpointer b)\n+{\n+    PCIBus *bus_a = *(PCIBus * const *)a;\n+    PCIBus *bus_b = *(PCIBus * const *)b;\n+    return (int)bus_a->parent_dev->devfn - (int)bus_b->parent_dev->devfn;\n+}\n+\n+static int cmp_bus_by_num(gconstpointer a, gconstpointer b)\n+{\n+    PCIBus *bus_a = *(PCIBus * const *)a;\n+    PCIBus *bus_b = *(PCIBus * const *)b;\n+    return pci_bus_num(bus_a) - pci_bus_num(bus_b);\n+}\n+\n+/*\n+ * Program one bridge's primary, secondary and subordinate bus numbers\n+ * and recurse. Return the max subordinate bus number.\n+ */\n+static uint8_t pci_program_bridge(PCIDevice *bridge, PCIBus *child_bus,\n+                                  uint8_t current_bus_num,\n+                                  uint8_t *next_bus_num)\n+{\n+    uint8_t secondary, max_child;\n+\n+    /* Bus number space exhausted; no bus number to assign. */\n+    if (*next_bus_num == 0) {\n+        return current_bus_num;\n+    }\n+    secondary = *next_bus_num;\n+    (*next_bus_num)++;\n+\n+    pci_default_write_config(bridge, PCI_PRIMARY_BUS, current_bus_num, 1);\n+    pci_default_write_config(bridge, PCI_SECONDARY_BUS, secondary, 1);\n+    /*\n+     * Unlike real hardware, QEMU does not require opening a subordinate\n+     * aperture before scanning downstream devices.  Write secondary as\n+     * a placeholder; the final value is set after recursion below.\n+     */\n+    pci_default_write_config(bridge, PCI_SUBORDINATE_BUS, secondary, 1);\n+\n+    max_child = pci_program_bus_numbers(child_bus, secondary, next_bus_num);\n+    pci_default_write_config(bridge, PCI_SUBORDINATE_BUS, max_child, 1);\n+    return max_child;\n+}\n+\n+/*\n+ * Program bus numbers for this bus and all subordinates.\n+ * - current_bus_num: this bus' number (0 for root, or already set for PXB).\n+ * - next_bus_num: next free bus number to assign to a bridge.\n+ *\n+ * Children come from bus->child only. Two kinds:\n+ * 1) PXB (extra root): child has PCI_BUS_IS_ROOT. Has bus number\n+ *    already set, recurse only.\n+ * 2) Normal bridge: parent is IS_PCI_BRIDGE. Assign secondary = *next_bus_num,\n+ *    program primary, secondary and subordinate bus numbers, and recurse.\n+ *\n+ * Order matches EDK2 PciBusDxe enumeration: process PXB children first\n+ * (sorted by bus number), then bridges (sorted by devfn).\n+ */\n+static uint8_t pci_program_bus_numbers(PCIBus *bus, uint8_t current_bus_num,\n+                                       uint8_t *next_bus_num)\n+{\n+    PCIBus *child_bus;\n+    GArray *pxb_buses = g_array_new(false, false, sizeof(PCIBus *));\n+    GArray *bridges = g_array_new(false, false, sizeof(PCIBus *));\n+    uint8_t max_subordinate = current_bus_num;\n+    uint8_t child_num;\n+    uint8_t one_max;\n+    guint i;\n+\n+    /* Single pass over bus->child: split into PXB vs bridge */\n+    QLIST_FOREACH(child_bus, &bus->child, sibling) {\n+        if (!child_bus->parent_dev) {\n+            continue;\n+        }\n+        if (pci_bus_is_root(child_bus)) {\n+            /* PXB or similar: bus number already set (e.g. bus_nr=1, 9) */\n+            g_array_append_val(pxb_buses, child_bus);\n+        } else if (IS_PCI_BRIDGE(child_bus->parent_dev)) {\n+            g_array_append_val(bridges, child_bus);\n+        }\n+    }\n+\n+    /* PXB first, sorted by bus number (e.g. 1 before 9) */\n+    if (pxb_buses->len > 1) {\n+        g_array_sort(pxb_buses, cmp_bus_by_num);\n+    }\n+    for (i = 0; i < pxb_buses->len; i++) {\n+        child_bus = g_array_index(pxb_buses, PCIBus *, i);\n+        child_num = (uint8_t)pci_bus_num(child_bus);\n+        if (child_num + 1 > *next_bus_num) {\n+            *next_bus_num = child_num + 1;\n+        }\n+        one_max = pci_program_bus_numbers(child_bus, child_num, next_bus_num);\n+        if (one_max > max_subordinate) {\n+            max_subordinate = one_max;\n+        }\n+    }\n+    g_array_free(pxb_buses, true);\n+\n+    /* Bridges second, sorted by devfn */\n+    if (bridges->len > 1) {\n+        g_array_sort(bridges, cmp_bus_by_devfn);\n+    }\n+    for (i = 0; i < bridges->len; i++) {\n+        child_bus = g_array_index(bridges, PCIBus *, i);\n+        one_max = pci_program_bridge(child_bus->parent_dev, child_bus,\n+                                     current_bus_num, next_bus_num);\n+        if (one_max > max_subordinate) {\n+            max_subordinate = one_max;\n+        }\n+    }\n+    g_array_free(bridges, true);\n+\n+    return max_subordinate;\n+}\n+\n+void pci_enumerate_bus(PCIBus *root_bus)\n+{\n+    uint8_t next_bus_num;\n+\n+    if (!root_bus) {\n+        return;\n+    }\n+    next_bus_num = 1;\n+    pci_program_bus_numbers(root_bus, 0, &next_bus_num);\n+}\ndiff --git a/hw/pci/pci-enumerate.h b/hw/pci/pci-enumerate.h\nnew file mode 100644\nindex 0000000000..b1e4b989f1\n--- /dev/null\n+++ b/hw/pci/pci-enumerate.h\n@@ -0,0 +1,15 @@\n+/*\n+ * Copyright (C) 2026 NVIDIA\n+ * Written by Tushar Dave\n+ *\n+ * SPDX-License-Identifier: GPL-2.0-or-later\n+ */\n+\n+#ifndef HW_PCI_PCI_ENUMERATE_H\n+#define HW_PCI_PCI_ENUMERATE_H\n+\n+#include \"hw/pci/pci_bus.h\"\n+\n+void pci_enumerate_bus(PCIBus *root_bus);\n+\n+#endif\n","prefixes":["RFC","2/8"]}