From nobody Sat Apr 11 18:38:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1775374266; cv=none; d=zohomail.com; s=zohoarc; b=AnLeXAnk3DAp4lCFU0kxRf4050UsnP4H7Lx0A+Y8LScCA0HmT+gGnX7sBtj77KGW4jx64OCbBTyljv0UieH4pPWwenayQr3CUcHuedmW8+e1pbkM/pFHHOcK30uzJEyY8BvDqWM/XTS5FVdbROJSVmN/xV8lDtjZEuca60Cbcm0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775374266; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=o/AIm7dcgDves1AjHp0rGGsirlJpDLatrdTujVTNwp8=; b=WEC5reXoeIbvOTH/wXjv8ty9b/XMM0kk8vzxrUHjAKgSRKCrTdZE378PTRcjc2xfJpJWN6Hsne/Rw+++0T8LIiYvIBMrEnOg/BbRCQeSXn8BZV/I84WtyP33t3YwkfNkA4NARj/AwGBK/ZNcfyR8+/uD/y5bHglA2wMnTTK7RiY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1775374266195884.6474207046002; Sun, 5 Apr 2026 00:31:06 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w9HvI-0003uO-Ks; Sun, 05 Apr 2026 03:29:32 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w9HvG-0003ta-DK for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:30 -0400 Received: from mail-dy1-x1330.google.com ([2607:f8b0:4864:20::1330]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w9HvE-0007EY-LM for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:30 -0400 Received: by mail-dy1-x1330.google.com with SMTP id 5a478bee46e88-2c54c68db4dso6147454eec.0 for ; Sun, 05 Apr 2026 00:29:28 -0700 (PDT) Received: from localhost.localdomain ([2601:645:8200:47:41e4:ff2b:ff70:4d75]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2cb92ea0ef1sm7636502eec.21.2026.04.05.00.29.24 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 05 Apr 2026 00:29:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1775374167; x=1775978967; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=o/AIm7dcgDves1AjHp0rGGsirlJpDLatrdTujVTNwp8=; b=RiNQvrvxM434JDdJoPHd1ljF5moiN3w0vLR/XMRwe149bL+yq35jB+VIz7ijyPJznt rlrDnrRRi0yAiJQ+fFV3QrHXAQ+8lNjNLvgZ9hgCUUCnCshaasXzwRFiQLYIZLrYCYFm dssnZwd5ZEDUidM13YhQTXyLgxRxuoTgX0AKqL0BqYJl3yWKpjevsZRjB7pfuLl+mav4 8utkZEaaz8RTYE6S3OmuQmlvvBaaEFdnBrKazcMNkJYxpbEMZ0MKcyWj0XpCiPhLcVCd OdfZI4RmmXPBlk6kX5wRZcw6OPdSNgFpm1gK3MAbCBZrZHA70R8vEhG3yJEIip0pJC42 WTPA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775374167; x=1775978967; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=o/AIm7dcgDves1AjHp0rGGsirlJpDLatrdTujVTNwp8=; b=F+8bomq4UVfkLqBq2RdU89ycX6aW8j9lWG7IZk59A2MN86UGtJa12TON3uOd2uFBMY 4D5x85+X4ZGsphQqS8UfUAxRqygS/UlGrKltndqNjM6rMuB/tx70BuT1OpH21/QDwqV8 pk/fJden+pzRJMmrwnk6/PrNbbMgiLAi7T2A2SO0FBxr4Twa145qJVxC8ayVR1FTrf4q q+utD1A/K48ynkaOfCGEDWCXmQSEGTYguSZxE239M3ANkQfV0NviR7QNAOW8pj18Vrqc /zb3QgtkuRFUKPTiI/0F6KEQhaRCuO5gxY+aPMhHFgCl0MKIBb6PVS79FniWsqEooS+v Dffg== X-Gm-Message-State: AOJu0YzqQJ1NNgH2NjyMl2V2E2mYJmxxhe/QSfYv0iSwZDP1gkFmfade pWoQm9MldPJro+ZTbxhe4cOTC77/h2CTKtIHmJnrv0Jx/g+Ohsu2vkvj9BvPvPWXQXQ= X-Gm-Gg: AeBDieuadQxAPfWjamKDQr98rz0Mk6vApArOeIjYmtFfOir0WApIfYh7YkqduKhgfar idBFHL/hlaSfeIIRKSmXBHe+yTR6RoV8rsa4sH1CXVme+q1142dFTXc3KglRPg3VnnoxfZVuMgo OYoTsh3WQK3rGC1AQq8eUSipMHVpvsHrC8FlNnFJgGA/E7/DZy7b9rH05vPrSxK4+3T7dMFWAX2 5McKCFyk88B83V6PYnA3jqoHrOHBliGoMEWPCgxSrpCfEgAdU1Tdlom3+OmvDzKE3RIcJ4kuHy+ +kDwLaasrf4+XxvvxeZutUmRvoQzDZaP8KzizhaMAGHlTw2HqHboY0PxntNvKGnMlIo3Ho6Js70 P/fGRdoqy56tKZ1nGLESShttt+92GoDP5yN2I/OuYs7FXjR5x8iA7tzLXG3kE00KzJHNZdtGqW2 VFUYOY1d9wiD93IVKFt04geKa7CHw7rSqSBw2S6JxG9+qURN8gpNtb0MSPh9LQ54WEOseAcEj7g lcol44CmzZr9UnANj0BEec018I= X-Received: by 2002:a05:7300:571e:b0:2c4:4276:709f with SMTP id 5a478bee46e88-2cbf9afdabfmr4594191eec.1.1775374166740; Sun, 05 Apr 2026 00:29:26 -0700 (PDT) From: "Scott J. Goldman" To: qemu-devel@nongnu.org Cc: alex@shazbot.org, clg@redhat.com, pbonzini@redhat.com, rbolshakov@ddn.com, phil@philjordan.eu, mst@redhat.com, john.levon@nutanix.com, thanos.makatos@nutanix.com, qemu-s390x@nongnu.org, "Scott J. Goldman" Subject: [RFC PATCH 01/10] vfio/pci: Use the write side of EventNotifier for IRQ signaling Date: Sun, 5 Apr 2026 00:28:45 -0700 Message-ID: <20260405072857.66484-2-scottjgo@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260405072857.66484-1-scottjgo@gmail.com> References: <20260405072857.66484-1-scottjgo@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::1330; envelope-from=scottjgo@gmail.com; helo=mail-dy1-x1330.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1775374267979154100 Content-Type: text/plain; charset="utf-8" When passing an fd to a vfio-user server for interrupt signaling, the write side of the EventNotifier must be used. For eventfd-backed notifiers the read and write descriptors are the same, so the existing kernel VFIO path is unchanged. However, on hosts that emulate EventNotifier with a pipe pair (e.g. macOS), event_notifier_get_fd() returns the read side, which the vfio-user server cannot write to. Introduce vfio_irq_signal_fd() which returns event_notifier_get_wfd() and use it at every site in vfio/pci that hands an fd to vfio_device_irq_set_signaling() or the bulk IRQ path. The qemu_set_fd_handler() calls continue to use the read fd as before. Signed-off-by: Scott J. Goldman --- hw/vfio/pci.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 1945751ffd..ee1a42e7e0 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -80,6 +80,17 @@ static bool vfio_notifier_init(VFIOPCIDevice *vdev, Even= tNotifier *e, return true; } =20 +/* + * Return the fd that the vfio kernel driver or vfio-user server should + * write to in order to signal an interrupt. For eventfd-backed notifiers + * this is the same descriptor QEMU reads, but on hosts that emulate + * EventNotifier with a pipe pair the write side must be used instead. + */ +static int vfio_irq_signal_fd(EventNotifier *e) +{ + return event_notifier_get_wfd(e); +} + static void vfio_notifier_cleanup(VFIOPCIDevice *vdev, EventNotifier *e, const char *name, int nr) { @@ -378,7 +389,9 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error= **errp) } =20 if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_= INDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { + VFIO_IRQ_SET_ACTION_TRIGGER, + vfio_irq_signal_fd(&vdev->intx.interrupt), + errp)) { qemu_set_fd_handler(fd, NULL, NULL, vdev); vfio_notifier_cleanup(vdev, &vdev->intx.interrupt, "intx-interrupt= ", 0); return false; @@ -548,9 +561,9 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, boo= l msix) if (vdev->msi_vectors[i].use) { if (vdev->msi_vectors[i].virq < 0 || (msix && msix_is_masked(pdev, i))) { - fd =3D event_notifier_get_fd(&vdev->msi_vectors[i].interru= pt); + fd =3D vfio_irq_signal_fd(&vdev->msi_vectors[i].interrupt); } else { - fd =3D event_notifier_get_fd(&vdev->msi_vectors[i].kvm_int= errupt); + fd =3D vfio_irq_signal_fd(&vdev->msi_vectors[i].kvm_interr= upt); } } =20 @@ -628,9 +641,9 @@ static void set_irq_signalling(VFIODevice *vbasedev, VF= IOMSIVector *vector, int32_t fd; =20 if (vector->virq >=3D 0) { - fd =3D event_notifier_get_fd(&vector->kvm_interrupt); + fd =3D vfio_irq_signal_fd(&vector->kvm_interrupt); } else { - fd =3D event_notifier_get_fd(&vector->interrupt); + fd =3D vfio_irq_signal_fd(&vector->interrupt); } =20 if (!vfio_device_irq_set_signaling(vbasedev, VFIO_PCI_MSIX_IRQ_INDEX, = nr, @@ -770,7 +783,7 @@ static void vfio_msix_vector_release(PCIDevice *pdev, u= nsigned int nr) * be re-asserted on unmask. Nothing to do if already using QEMU mode. */ if (vector->virq >=3D 0) { - int32_t fd =3D event_notifier_get_fd(&vector->interrupt); + int32_t fd =3D vfio_irq_signal_fd(&vector->interrupt); Error *err =3D NULL; =20 if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_MSIX_= IRQ_INDEX, @@ -3181,7 +3194,9 @@ void vfio_pci_register_err_notifier(VFIOPCIDevice *vd= ev) } =20 if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_I= NDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &e= rr)) { + VFIO_IRQ_SET_ACTION_TRIGGER, + vfio_irq_signal_fd(&vdev->err_notif= ier), + &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); qemu_set_fd_handler(fd, NULL, NULL, vdev); vfio_notifier_cleanup(vdev, &vdev->err_notifier, "err_notifier", 0= ); @@ -3254,7 +3269,9 @@ void vfio_pci_register_req_notifier(VFIOPCIDevice *vd= ev) } =20 if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_I= NDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &e= rr)) { + VFIO_IRQ_SET_ACTION_TRIGGER, + vfio_irq_signal_fd(&vdev->req_notif= ier), + &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); qemu_set_fd_handler(fd, NULL, NULL, vdev); vfio_notifier_cleanup(vdev, &vdev->req_notifier, "req_notifier", 0= ); --=20 2.50.1 (Apple Git-155) From nobody Sat Apr 11 18:38:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1775374273; cv=none; d=zohomail.com; s=zohoarc; b=c07MNFqjOyLkR3ipjeCiGSgreTklp0+WwNRnVbJmEC3nIUosZ9BIP7f8i1yIzQxGxKQUgfvmoPLp9SgNsNvuZuXKrOFGMO8i2u5b+lGeAIP6pS9YQlkW0LGUl1goGPIwUJNtQM/xIgA8VNHrF6FPEFwGEgLRsHceW0AdCz2/0u4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775374273; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=U5URZfSAg9FIccdRM6Usf30iSs9MBUi6bDCXKqbZ7TQ=; b=egKQsqJLVOkS5b2Hb+itTZkAE9FIhH7UfUV8U7tAsex6hknyzPNvT+TR8vQNiEh/HDnuIEwbCnTLSfZDvmnVj2G4qp3htwsnSDBpcMHMezFD8qkjMIYWUKc5wqIaa0hlj0xt8RD+aBruJ3Xpli1fG+w6z9igc3u+dSFRznC4mjE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 177537427321013.724163615494604; Sun, 5 Apr 2026 00:31:13 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w9HvI-0003uP-Ot; Sun, 05 Apr 2026 03:29:32 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w9HvH-0003tu-DD for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:31 -0400 Received: from mail-dy1-x1333.google.com ([2607:f8b0:4864:20::1333]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w9HvF-0007Eo-Tl for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:31 -0400 Received: by mail-dy1-x1333.google.com with SMTP id 5a478bee46e88-2c54c68db4dso6147470eec.0 for ; Sun, 05 Apr 2026 00:29:29 -0700 (PDT) Received: from localhost.localdomain ([2601:645:8200:47:41e4:ff2b:ff70:4d75]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2cb92ea0ef1sm7636502eec.21.2026.04.05.00.29.26 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 05 Apr 2026 00:29:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1775374168; x=1775978968; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=U5URZfSAg9FIccdRM6Usf30iSs9MBUi6bDCXKqbZ7TQ=; b=ng7a5H+2Cmrfc/S1PAJei6UV03yf5u7x/k7KDzrLQMtpVJQL2pebzgW81yT2bnqpbe eIMUrWHlgWgXk6+sK3VMu2Ts+vaC1ZNuU6tznst2S7TczQm6ajbVeAE0vvSVzNpVVQl8 eFKGs+JT8Llo/uvKW5VGor9eQD8olOPP40yhiOmSHAmvwSvJGiOdh7/qjdVnDj34pOmb 6Yx5jSFAbGDipQiHksu+mIGcbaZcM027mSot7A68nry8kjil/tMUg0tI5TVhYJvcvmTW +fJr5ET0Ncb+PlUls3FddMyLZZx0NS17nS6X73y4NrN4zvHAoEaAq4nKO5gaSaADyOD9 LcDw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775374168; x=1775978968; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=U5URZfSAg9FIccdRM6Usf30iSs9MBUi6bDCXKqbZ7TQ=; b=haeZho7RHoK3tXFbwc15gbkG+CJVm6HZ50A0Q2h8lRBl5fudUeMUJooFvGtENy6tJ4 FsXgAmHsqxWuMGS4mx6k8Op56FTaMORRkOBcEpgCPffbEpcuJKVueBmSRW7nG/my8120 AeOnAeQNvC+YDyA9a/eQYlPn2JYGPJR5Ktniwei9uzCdVos9RNH9fmdafeiWGLPcfDll 2XM1G+/9+dRfAU/V5qbkz3q/Z9ALeoDkwYgV6kkbcCJsW9mjYlHsDol8e0yHg2fwZfcz fSZnEKAsALLCx8Tepzf78aq65+D6UE4c/yMXkNd68r8U7lsPgD3f6X7/KYC5jBkmk1kl x+Cw== X-Gm-Message-State: AOJu0YwwiSDBiFBmHvUODpWM3KlGvWbqITmm3Lo6Z5fTWNBPpMGBi4p7 SUx5HWLDtaYEby8aMUl5DQCAJ+0Bmz7TqoG3QYJWUTcmiF4qIsqbmPkMFv9D4wvwEI0= X-Gm-Gg: AeBDiev4iG1HXPZSN0wWDx+TRoCYhYwtoBTcNMUKd4fxJLg+1pZzhkvGu6U/FEhNzkB ZbzhPLaQt1GR8V1Hlpa4/yaOwXOokpD6YX0eAPCV/M5xAWsG4H6WdxiTu7/KkSCo9kiG2LURM9I Ak2w1I1KR91nhnFNz3GrErf3T8UwZgQSopAhw8/MpKIs0cVwQEapVjg6oUtLYQDYE4NY1aaN4Ke SbXAaGSZ7SmUvf7uy7VdoOAmNYVDaOR2WcV7IBZGYayrIaLV87t14soVQ3q7eNPSx32zlF1h7rr WHJ0szH5kHOxaLO9H9ZxgUECWL1+HLWPtxPmaQZKSMJLeZBNS3K4i3zo/fJVdxPWEyDgf0Aij/X f1EOUhrz03FI93djv1hSETYLriQZdTLAJKdd7H+SnjwjX3wweUYFuiIOxycYd64/ojV8aUvUB9/ 2Mj405/mbzkXHsmh+Be7BasY0X/6hdTEmWKN6jui8L9iP/3O8yMqVIE2nM3G3tGKFKed4IZjTvm 80zh7SnuzawXG76t73nbfwLM74= X-Received: by 2002:a05:693c:3009:b0:2c1:5b23:1752 with SMTP id 5a478bee46e88-2cbfc463645mr4676211eec.23.1775374168034; Sun, 05 Apr 2026 00:29:28 -0700 (PDT) From: "Scott J. Goldman" To: qemu-devel@nongnu.org Cc: alex@shazbot.org, clg@redhat.com, pbonzini@redhat.com, rbolshakov@ddn.com, phil@philjordan.eu, mst@redhat.com, john.levon@nutanix.com, thanos.makatos@nutanix.com, qemu-s390x@nongnu.org, "Scott J. Goldman" Subject: [RFC PATCH 02/10] accel/hvf: avoid executable mappings for RAM-device memory Date: Sun, 5 Apr 2026 00:28:46 -0700 Message-ID: <20260405072857.66484-3-scottjgo@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260405072857.66484-1-scottjgo@gmail.com> References: <20260405072857.66484-1-scottjgo@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::1333; envelope-from=scottjgo@gmail.com; helo=mail-dy1-x1333.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1775374275816154100 Content-Type: text/plain; charset="utf-8" On macOS, HVF can panic the host kernel if a guest accesses device-backed memory through an executable mapping. Leave RAM-device/MMIO regions mapped read/write only and keep EXEC for ordinary guest RAM. This works around the immediate crash seen with passthrough BAR mappings. There are still platform-specific performance issues with guest write-combining mappings, but uncached mappings behave much more like the host-side mapping and this at least avoids the panic. Signed-off-by: Scott J. Goldman --- accel/hvf/hvf-all.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index 5f357c6d19..76cec4655b 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -114,7 +114,15 @@ static void hvf_set_phys_mem(MemoryRegionSection *sect= ion, bool add) return; } =20 - flags =3D HV_MEMORY_READ | HV_MEMORY_EXEC | (writable ? HV_MEMORY_WRIT= E : 0); + flags =3D HV_MEMORY_READ | (writable ? HV_MEMORY_WRITE : 0); + /* + * Leave RAM-device/MMIO mappings RW-only: on macOS, accessing them th= rough + * executable HVF mappings can panic the host kernel. Ordinary guest R= AM + * still needs EXEC. + */ + if (!memory_region_is_ram_device(area)) { + flags |=3D HV_MEMORY_EXEC; + } mem =3D memory_region_get_ram_ptr(area) + section->offset_within_regio= n; =20 trace_hvf_vm_map(gpa, size, mem, flags, --=20 2.50.1 (Apple Git-155) From nobody Sat Apr 11 18:38:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1775374240; cv=none; d=zohomail.com; s=zohoarc; b=WeTPy3N8PilN++Jva6zzRoVdoi89+IKHU23DI3tFIy+Bi0QwFPKsei1zJ7bx08bqBLmBqqkLTppuIfjP4HSoCkPvqbvdjQYGlqvROCHvpV2ZM3zXjnd1PykQ6Oh9SNkbB1tYZqOtBxlIUsYtqOn1n2IFe/BnZgqOOtZ3SGiY8EE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775374240; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=lhuQFBcbzpwqAcvo/lQJH+U4zsmLQEc/b+KbhHOCZWI=; b=gE/2MpfU3J+VDkHypWA+l9DfFJcu8LuZCHRBi8vI6RqoHSaWhcTfmUHxpUBbtIsifQbEhmGuYBULXMvwo8EBUjnWp8oIkvSGKqunnR3EmgCJcAVdAJR1FIoVUYf+qL7PEw3VTqJrm2nbgby0PgBUiK1aDy1QGMGKmpkjHce4E6E= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1775374240822417.56805998370453; Sun, 5 Apr 2026 00:30:40 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w9HvL-0003vV-1o; Sun, 05 Apr 2026 03:29:35 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w9HvJ-0003uk-I6 for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:33 -0400 Received: from mail-dy1-x1332.google.com ([2607:f8b0:4864:20::1332]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w9HvH-0007Fx-Jg for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:33 -0400 Received: by mail-dy1-x1332.google.com with SMTP id 5a478bee46e88-2b4520f6b32so3154571eec.0 for ; Sun, 05 Apr 2026 00:29:30 -0700 (PDT) Received: from localhost.localdomain ([2601:645:8200:47:41e4:ff2b:ff70:4d75]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2cb92ea0ef1sm7636502eec.21.2026.04.05.00.29.28 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 05 Apr 2026 00:29:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1775374169; x=1775978969; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=lhuQFBcbzpwqAcvo/lQJH+U4zsmLQEc/b+KbhHOCZWI=; b=jQnvhuDBJDXIXon65tRLtAoto8ZxK6xMOn0vwFTEGZ3F94fgATA9iw727JOWBX/Tkt YXlImx+ak1lpLRoMnLxGGVTzjVhI9Rmzn3XB5MJl6Km6vorZcjOHWYcI+sbpRyFogk3q C8LLZJ3VZ1mE5xFLzBcDkHi7/0iOb+UvgcjeA4pS6bYRju+txpGNf+q5nZxOoa7V2s/I 7hgqnsgPMr1VK78CNcOf0ZEkGIBkDXDFQBwEKf9k7z11g2ywLU/3++6YLIY7IzkzDVcX PmOXOO52w8Ri0c8YQtFrXPQvjJwploabUTNDPkKMXaeBKtjK/LauK/XYeQrGq2zfQeuu RtNQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775374169; x=1775978969; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=lhuQFBcbzpwqAcvo/lQJH+U4zsmLQEc/b+KbhHOCZWI=; b=jRBXxrxhGvv2cQNEVVrlkA9ANSG9m8ZnhDGGiDMDazFR0JgYFNGvC58QsYX/Zm71at TCufHv91y/KKSTBMxqpUWltQWdzfDcH+sD2V5H8NevBzIVxQJKp3A/X1S+VPefT8zunY D1b9ln7+ljFe4D/yrPXOZa8b2Yp8dsqT5lagjeJYkwAqNpybpE10LtRCVNfKG3R2Cw65 rXfrHOHrgt1/LaccD/lELOK+MC7SDxZ1vAcMVJyKn8gwCRoshMI9BBdh4e4+jjiaCRb9 OrRwyQycqcn+vKEjAKza2MEzc4HnbRTp2mVTjNuJIpBLC+y/p/d+lMNso758IypoRANq ftCA== X-Gm-Message-State: AOJu0Yw5u8EAYcTJym+EMYeNfWI327LSHMZ1XGexhghVui24Z7NzEqLE rO16bQIcCW4iMMbd6MDm5HHAHnm/9kpCjtHMgrBxr5Z/SbyHrlN2hDw8E/TuhvlVOjs= X-Gm-Gg: AeBDies1NH6Fbo589dfxPjTDFxtjrhd3NA/1QSYu78JXaxjmSUaUqcOd5tQ4Ce6iOn0 N1CYD/IXcgwrS7Co5OI3ZMHWK9oN7UqjxQOqk3ovpPZa2zq9z3kfc/oo1zpR+X3z5Otzlkthdhm pbDzUmTQpV01aWYNRMGpOTBNl895oAE2N2S7mcUHVH4cTZjgW2XNhi3gIKrden+c2y0Xp7861vB YNApBfiEtLp78RB36VWHyQkGJMTTHDRxMrFSjd4ZKIFb2mnBD7MqJ4tAN58iAF0XdDRfp+evWas qWIefy6hoVYBtRZUjVvvsy8fFa4nWaTccLsreXzdQ61pecD5oTxgaPD9nzxdbTavm/Wh6pSTMnB luWRSOzv1BQ+0d84n0EOeAjIme7ePKPBmCnz8V8+xPPpQPdMQmwQi+pgiVpTORBvqt7MTu+lisM JIub7JN0ME5VmqTDq6Z6k+9ZVxw+3dQmBjWC6FoaDqsY7GmfJHxIvnQife3djXB052GVz7O5IbC 5fEZel/b0jGGvFSHmAY5l7nWt8= X-Received: by 2002:a05:7300:641a:b0:2c8:7172:3b8d with SMTP id 5a478bee46e88-2cbf99ef517mr3990367eec.2.1775374169387; Sun, 05 Apr 2026 00:29:29 -0700 (PDT) From: "Scott J. Goldman" To: qemu-devel@nongnu.org Cc: alex@shazbot.org, clg@redhat.com, pbonzini@redhat.com, rbolshakov@ddn.com, phil@philjordan.eu, mst@redhat.com, john.levon@nutanix.com, thanos.makatos@nutanix.com, qemu-s390x@nongnu.org, "Scott J. Goldman" Subject: [RFC PATCH 03/10] vfio: Allow building on Darwin hosts Date: Sun, 5 Apr 2026 00:28:47 -0700 Message-ID: <20260405072857.66484-4-scottjgo@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260405072857.66484-1-scottjgo@gmail.com> References: <20260405072857.66484-1-scottjgo@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::1332; envelope-from=scottjgo@gmail.com; helo=mail-dy1-x1332.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1775374242362154100 Enable the VFIO subsystem to compile on macOS/Darwin in addition to Linux. This is build infrastructure only =E2=80=94 no Apple-specific device code is added yet. Key changes: - Add CONFIG_DARWIN Kconfig symbol and propagate it through the build system so that VFIO and VFIO_PCI are selectable on Darwin hosts. - Provide minimal and shim headers under include/compat/ so the Linux UAPI VFIO headers parse on macOS. - Widen CONFIG_LINUX guards to CONFIG_LINUX || CONFIG_DARWIN in the VFIO device, helpers, and migration headers. - Make container-legacy.c (the Linux /dev/vfio ioctl container) build only on Linux, and restrict IOMMUFD to Linux in Kconfig. - Remove the CONFIG_EVENTFD guard around event_notifier_init_fd() so pipe-based EventNotifiers can be initialized on Darwin. - Mark vfio-pci as not user-creatable on Darwin since the Linux VFIO kernel driver is not available; the Apple-specific device type will be added in a subsequent commit. Signed-off-by: Scott J. Goldman --- Kconfig.host | 3 +++ backends/Kconfig | 2 +- hw/vfio/Kconfig | 4 ++-- hw/vfio/meson.build | 4 +++- hw/vfio/pci.c | 3 +++ hw/vfio/vfio-helpers.h | 2 +- hw/vfio/vfio-migration-internal.h | 4 ++-- include/compat/linux/ioctl.h | 2 ++ include/compat/linux/types.h | 26 ++++++++++++++++++++++++++ include/hw/vfio/vfio-device.h | 4 ++-- meson.build | 10 +++++++++- util/event_notifier-posix.c | 5 ++--- 12 files changed, 56 insertions(+), 13 deletions(-) create mode 100644 include/compat/linux/ioctl.h create mode 100644 include/compat/linux/types.h diff --git a/Kconfig.host b/Kconfig.host index 933425c74b..bb2780293c 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -5,6 +5,9 @@ config LINUX bool =20 +config DARWIN + bool + config LIBCBOR bool =20 diff --git a/backends/Kconfig b/backends/Kconfig index d3dbe19868..d1be4148d3 100644 --- a/backends/Kconfig +++ b/backends/Kconfig @@ -2,7 +2,7 @@ source tpm/Kconfig =20 config IOMMUFD bool - depends on VFIO + depends on VFIO && LINUX =20 config SPDM_SOCKET bool diff --git a/hw/vfio/Kconfig b/hw/vfio/Kconfig index 27de24e4db..a409483c34 100644 --- a/hw/vfio/Kconfig +++ b/hw/vfio/Kconfig @@ -2,14 +2,14 @@ =20 config VFIO bool - depends on LINUX + depends on LINUX || DARWIN =20 config VFIO_PCI bool default y select VFIO select EDID - depends on LINUX && PCI + depends on (LINUX || DARWIN) && PCI =20 config VFIO_CCW bool diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 82f68698fb..1ee9c11d5b 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -4,9 +4,11 @@ vfio_ss =3D ss.source_set() vfio_ss.add(files( 'listener.c', 'container.c', - 'container-legacy.c', 'helpers.c', )) +if host_os =3D=3D 'linux' + vfio_ss.add(files('container-legacy.c')) +endif vfio_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr.c')) vfio_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( 'pci-quirks.c', diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index ee1a42e7e0..5a1c2d8c2e 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3862,6 +3862,9 @@ static void vfio_pci_class_init(ObjectClass *klass, c= onst void *data) #endif dc->vmsd =3D &vfio_cpr_pci_vmstate; dc->desc =3D "VFIO-based PCI device assignment"; +#ifdef CONFIG_DARWIN + dc->user_creatable =3D false; +#endif pdc->realize =3D vfio_pci_realize; =20 object_class_property_set_description(klass, /* 1.3 */ diff --git a/hw/vfio/vfio-helpers.h b/hw/vfio/vfio-helpers.h index 54a327ffbc..2afb360797 100644 --- a/hw/vfio/vfio-helpers.h +++ b/hw/vfio/vfio-helpers.h @@ -9,7 +9,7 @@ #ifndef HW_VFIO_VFIO_HELPERS_H #define HW_VFIO_VFIO_HELPERS_H =20 -#ifdef CONFIG_LINUX +#if defined(CONFIG_LINUX) || defined(CONFIG_DARWIN) #include =20 extern int vfio_kvm_device_fd; diff --git a/hw/vfio/vfio-migration-internal.h b/hw/vfio/vfio-migration-int= ernal.h index 814fbd9eba..566cd6a871 100644 --- a/hw/vfio/vfio-migration-internal.h +++ b/hw/vfio/vfio-migration-internal.h @@ -9,7 +9,7 @@ #ifndef HW_VFIO_VFIO_MIGRATION_INTERNAL_H #define HW_VFIO_VFIO_MIGRATION_INTERNAL_H =20 -#ifdef CONFIG_LINUX +#if defined(CONFIG_LINUX) || defined(CONFIG_DARWIN) #include #endif =20 @@ -62,7 +62,7 @@ bool vfio_device_state_is_precopy(VFIODevice *vbasedev); int vfio_save_device_config_state(QEMUFile *f, void *opaque, Error **errp); int vfio_load_device_config_state(QEMUFile *f, void *opaque); =20 -#ifdef CONFIG_LINUX +#if defined(CONFIG_LINUX) || defined(CONFIG_DARWIN) int vfio_migration_set_state(VFIODevice *vbasedev, enum vfio_device_mig_state new_state, enum vfio_device_mig_state recover_state, diff --git a/include/compat/linux/ioctl.h b/include/compat/linux/ioctl.h new file mode 100644 index 0000000000..2c789fefc6 --- /dev/null +++ b/include/compat/linux/ioctl.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* empty Darwin shim - ioctl macros not needed */ diff --git a/include/compat/linux/types.h b/include/compat/linux/types.h new file mode 100644 index 0000000000..d6620aaf7f --- /dev/null +++ b/include/compat/linux/types.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Darwin shim for + * + * The Linux UAPI headers that QEMU copies into linux-headers/ expect + * these typedefs from . Provide the small subset we + * need so those headers parse on macOS. + */ +#ifndef COMPAT_LINUX_TYPES_H +#define COMPAT_LINUX_TYPES_H + +#include + +typedef uint8_t __u8; +typedef uint16_t __u16; +typedef uint32_t __u32; +typedef uint64_t __u64; +typedef int8_t __s8; +typedef int16_t __s16; +typedef int32_t __s32; +typedef int64_t __s64; +typedef int64_t loff_t; + +typedef __u64 __aligned_u64 __attribute__((aligned(8))); + +#endif /* COMPAT_LINUX_TYPES_H */ diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index 828a31c006..17c5db369c 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -23,7 +23,7 @@ =20 #include "system/memory.h" #include "qemu/queue.h" -#ifdef CONFIG_LINUX +#if defined(CONFIG_LINUX) || defined(CONFIG_DARWIN) #include #endif #include "system/system.h" @@ -171,7 +171,7 @@ VFIODevice *vfio_get_vfio_device(Object *obj); typedef QLIST_HEAD(VFIODeviceList, VFIODevice) VFIODeviceList; extern VFIODeviceList vfio_device_list; =20 -#ifdef CONFIG_LINUX +#if defined(CONFIG_LINUX) || defined(CONFIG_DARWIN) /* * How devices communicate with the server. The default option is through * ioctl() to the kernel VFIO driver, but vfio-user can use a socket to a = remote diff --git a/meson.build b/meson.build index daa58e46a3..b12466b730 100644 --- a/meson.build +++ b/meson.build @@ -764,7 +764,14 @@ if 'objc' in all_languages add_project_arguments(objc.get_supported_arguments(qemu_common_flags + w= arn_flags), native: false, language: 'objc') endif -if host_os =3D=3D 'linux' +if host_os =3D=3D 'darwin' + # linux-headers/linux/vfio.h includes and + # which are system headers on Linux but absent on macOS. Point at the + # static shims in include/compat/ + add_project_arguments('-isystem', meson.current_source_dir() / 'include/= compat', + language: all_languages) +endif +if host_os in ['linux', 'darwin'] add_project_arguments('-isystem', meson.current_source_dir() / 'linux-he= aders', '-isystem', 'linux-headers', language: all_languages) @@ -3282,6 +3289,7 @@ host_kconfig =3D \ (have_vhost_kernel ? ['CONFIG_VHOST_KERNEL=3Dy'] : []) + \ (have_virtfs ? ['CONFIG_VIRTFS=3Dy'] : []) + \ (host_os =3D=3D 'linux' ? ['CONFIG_LINUX=3Dy'] : []) + \ + (host_os =3D=3D 'darwin' ? ['CONFIG_DARWIN=3Dy'] : []) + \ (multiprocess_allowed ? ['CONFIG_MULTIPROCESS_ALLOWED=3Dy'] : []) + \ (vfio_user_server_allowed ? ['CONFIG_VFIO_USER_SERVER_ALLOWED=3Dy'] : []= ) + \ (hv_balloon ? ['CONFIG_HV_BALLOON_POSSIBLE=3Dy'] : []) + \ diff --git a/util/event_notifier-posix.c b/util/event_notifier-posix.c index 83fdbb96bb..0e05e81aca 100644 --- a/util/event_notifier-posix.c +++ b/util/event_notifier-posix.c @@ -20,10 +20,10 @@ #include #endif =20 -#ifdef CONFIG_EVENTFD /* * Initialize @e with existing file descriptor @fd. - * @fd must be a genuine eventfd object, emulation with pipe won't do. + * On hosts without eventfd(), callers can still restore a single descript= or + * for cases that only need eventfd-like semantics. */ void event_notifier_init_fd(EventNotifier *e, int fd) { @@ -31,7 +31,6 @@ void event_notifier_init_fd(EventNotifier *e, int fd) e->wfd =3D fd; e->initialized =3D true; } -#endif =20 int event_notifier_init(EventNotifier *e, int active) { --=20 2.50.1 (Apple Git-155) From nobody Sat Apr 11 18:38:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1775374235; cv=none; d=zohomail.com; s=zohoarc; b=a11kVWxsE7vzS56Kf9kBNQi7w7M2IZDa8QqAFr9g3KDmPIRZOlcSXQVhjEPS1aUuCpol4YsxoM4igtnxEZE7TQAAnMjGDpZauUJvPY2OK+6qRzBFkBiWqgAlVbbqGHwnK0onT/QO5XAigBJIFD/USmfUYXxFaEB8MuAOYSQLakg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775374235; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=BvX/ruZLVHznPTOumzyShvEw9LLr8MaO+93oE9E08wY=; b=gxgVUUx1GlAYG+SdgaCDQdpbmfE8sXSbfSwnvM6dad7jdr/82KC35GZ+p6uK4bKepzAkvDJBzeOuICtjomSOyouW1ReheGa/gOYln1QyNWRavG2pAFt7kxglv9WkA4A4spIN5SC903O3psfLy//K7w0G3dSP1FliYPPQd9kDaNQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1775374235412920.5252615728017; Sun, 5 Apr 2026 00:30:35 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w9HvM-0003wF-HQ; Sun, 05 Apr 2026 03:29:36 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w9HvK-0003vR-VB for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:34 -0400 Received: from mail-dy1-x132c.google.com ([2607:f8b0:4864:20::132c]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w9HvI-0007Gn-Ph for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:34 -0400 Received: by mail-dy1-x132c.google.com with SMTP id 5a478bee46e88-2b6b0500e06so4607999eec.1 for ; Sun, 05 Apr 2026 00:29:32 -0700 (PDT) Received: from localhost.localdomain ([2601:645:8200:47:41e4:ff2b:ff70:4d75]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2cb92ea0ef1sm7636502eec.21.2026.04.05.00.29.29 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 05 Apr 2026 00:29:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1775374171; x=1775978971; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=BvX/ruZLVHznPTOumzyShvEw9LLr8MaO+93oE9E08wY=; b=UCdT6AHIVLoEnRjQJCpBEOH2nTCJrCaxg0m51vMedrqgLxwN6r08ofEn/xa+f04Jr4 Kqz9rsi29VMCGrvAjrq7FYGlrTHzhnmduXCQAeNsPCPBQnwlv4XjA5qYwXvcPy3qM90V cjEMXXvclo1FYOnGB0x43BAFB6pPpGPpJd7W2Q+p7GiAMz5oEscw3yCGKnTjWcJofc5c 7UKTepu+qkbfP7RLTvbZ0PXti1H20gWF54tx5GciltaHmiIfXuTzDeevsoHipqJxXqEt 6fPTqIpm5eFBK/I5I6ayCzVzU6CSTPxsrvtQ0ufVHnVAmtQqSUhU/fvuV0+CZzZtuzXI BblQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775374171; x=1775978971; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=BvX/ruZLVHznPTOumzyShvEw9LLr8MaO+93oE9E08wY=; b=m4IITi3PEOHJVkvbF/vyXJgb50S27En1iSUP8E1UGeizXD97L177Ktup3LfJDhxJ6D rjyQVENaTqRheziRWIkSziZsNFKipedEHo+dZHrpvB09akpbFfE7ALcBi3vkLC51QTrt 3Lqw1iCbvx6clbc6ccj408bWSDRogknKwGecC68jgUJEqjaMOvSPltndGLYqkzSW+8qJ c0V0pEkIfTaz2V0MBCdlLqEoabNHzyuD+lOrrdFHayU1b88bfIg/e3OYKQ2FZDCp3PQu ba0hufvt/MDaJgW0S0G9VLJouEy0ZVrXjtC8LFU5cZVDbe5NobGIujP2KbsPQ85nUz4v p9Bw== X-Gm-Message-State: AOJu0YyzZ9xZqkQd/nOXcbXYkUHxx/nY8QjlGOpk/hIskmiwh6FaVnZx rEFRglf+EMF4WnbaK5FYq9yhwnIpOEq5uJwycIwW/l759JV/ffLbMVR4682CCUog6Y8= X-Gm-Gg: AeBDievhv4rrURYjFj95+vctVBDh/+SiYnQAJ17BmtqAVSBP+GBwK/Ao5yQtjJH1zB3 VxARdkXUrJpCPwT4CLeYljEaZ5M4bpy0XYTyvdM2GI4O2V4kT1PdkqnlViHo4DKcOQNPXPtJsJp b/GUDIIN1PJUdy2gDee9E0oboUSSCx2Kuk+2xuZYOjqmOX1bnVZVhUDTpYA0kEcqMjM2IJVU32o 7LilaigU1fBdP409MPWYoXMF6BVN/asW/yoQw+ErDlPazKfg3iKz+L77/M/NixBG8pMqEfFSOJC urX/j9LgiPTD0BTp+qkHt8TJ2DYpwAujKbcUGq1+MEjTCB30GnivI+tGJRMTaHFXflQkXpYSOAH BI/llqlqX5nGBew6uBFtRr/tbVqXoCTur/9iy3anvI3R0T/eFN7CGRLaxzk896QkSXxlA4u2MyL KQV9NBShhbIl1bNEdTjNwNL14mYVykezd2caNvlUkQlnBQ+nIOmvJb3rftkHd3aP1TAFXFKDpLy bcnvIcVjC3TrC3hXyI+Idn1XsQF0oHC/rd7dw== X-Received: by 2002:a05:7301:1687:b0:2cb:bc02:6e13 with SMTP id 5a478bee46e88-2cbfbf78978mr4369879eec.16.1775374170739; Sun, 05 Apr 2026 00:29:30 -0700 (PDT) From: "Scott J. Goldman" To: qemu-devel@nongnu.org Cc: alex@shazbot.org, clg@redhat.com, pbonzini@redhat.com, rbolshakov@ddn.com, phil@philjordan.eu, mst@redhat.com, john.levon@nutanix.com, thanos.makatos@nutanix.com, qemu-s390x@nongnu.org, "Scott J. Goldman" , "Scott J. Goldman" Subject: [RFC PATCH 04/10] vfio: Prepare existing code for Apple VFIO backend Date: Sun, 5 Apr 2026 00:28:48 -0700 Message-ID: <20260405072857.66484-5-scottjgo@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260405072857.66484-1-scottjgo@gmail.com> References: <20260405072857.66484-1-scottjgo@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::132c; envelope-from=scottjgo@gmail.com; helo=mail-dy1-x132c.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1775374237597154100 From: "Scott J. Goldman" Adjust the shared VFIO code so a non-Linux backend can plug in: - vfio_device_get_name(): accept a device that already has a name set without requiring sysfsdev or an fd. - vfio_device_is_mdev(): return true on Darwin =E2=80=94 the dext mediates = all device access and manages DMA mappings explicitly, so the mdev assumptions (software-managed DMA, balloon-safe) hold. - vfio_device_attach(): select the Apple IOMMU container type on Darwin. - vfio_pci_realize(): allow realize when name is pre-set (no sysfsdev), and add a no_bar_quirks flag so subclasses can skip BAR quirk setup. - Add TYPE_VFIO_IOMMU_APPLE and TYPE_VFIO_APPLE_PCI type strings. Signed-off-by: Scott J. Goldman --- hw/vfio/device.c | 20 ++++++++++++++++++++ hw/vfio/pci.c | 8 +++++--- hw/vfio/pci.h | 1 + hw/vfio/types.h | 2 ++ include/hw/vfio/vfio-container.h | 1 + 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 973fc35b59..338becffa7 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -316,6 +316,13 @@ bool vfio_device_get_name(VFIODevice *vbasedev, Error = **errp) struct stat st; =20 if (vbasedev->fd < 0) { + if (!vbasedev->sysfsdev) { + if (vbasedev->name) { + return true; + } + error_setg(errp, "No provided host device"); + return false; + } if (stat(vbasedev->sysfsdev, &st) < 0) { error_setg_errno(errp, errno, "no such host device"); error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->sysfsdev); @@ -404,7 +411,16 @@ bool vfio_device_is_mdev(VFIODevice *vbasedev) g_autofree char *tmp =3D NULL; =20 if (!vbasedev->sysfsdev) { +#ifdef CONFIG_DARWIN + /* + * On Darwin the dext mediates all device access and manages DMA + * mappings explicitly, so the mdev assumptions (software-managed + * DMA, balloon-safe) hold. + */ + return true; +#else return false; +#endif } =20 tmp =3D g_strdup_printf("%s/subsystem", vbasedev->sysfsdev); @@ -462,9 +478,13 @@ bool vfio_device_attach_by_iommu_type(const char *iomm= u_type, char *name, bool vfio_device_attach(char *name, VFIODevice *vbasedev, AddressSpace *as, Error **errp) { +#ifdef CONFIG_DARWIN + const char *iommu_type =3D TYPE_VFIO_IOMMU_APPLE; +#else const char *iommu_type =3D vbasedev->iommufd ? TYPE_VFIO_IOMMU_IOMMUFD : TYPE_VFIO_IOMMU_LEGACY; +#endif =20 return vfio_device_attach_by_iommu_type(iommu_type, name, vbasedev, as, errp); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 5a1c2d8c2e..cf817d9ae7 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3483,7 +3483,7 @@ static void vfio_pci_realize(PCIDevice *pdev, Error *= *errp) char uuid[UUID_STR_LEN]; g_autofree char *name =3D NULL; =20 - if (vbasedev->fd < 0 && !vbasedev->sysfsdev) { + if (vbasedev->fd < 0 && !vbasedev->sysfsdev && !vbasedev->name) { if (!(~vdev->host.domain || ~vdev->host.bus || ~vdev->host.slot || ~vdev->host.function)) { error_setg(errp, "No provided host device"); @@ -3558,8 +3558,10 @@ static void vfio_pci_realize(PCIDevice *pdev, Error = **errp) vfio_vga_quirk_setup(vdev); } =20 - for (i =3D 0; i < PCI_ROM_SLOT; i++) { - vfio_bar_quirk_setup(vdev, i); + if (!vdev->no_bar_quirks) { + for (i =3D 0; i < PCI_ROM_SLOT; i++) { + vfio_bar_quirk_setup(vdev, i); + } } =20 if (!vfio_pci_interrupt_setup(vdev, errp)) { diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index d6495d7f29..424acd71b6 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -187,6 +187,7 @@ struct VFIOPCIDevice { bool defer_kvm_irq_routing; bool clear_parent_atomics_on_exit; bool skip_vsc_check; + bool no_bar_quirks; uint16_t vpasid_cap_offset; VFIODisplay *dpy; Notifier irqchip_change_notifier; diff --git a/hw/vfio/types.h b/hw/vfio/types.h index 5482d90808..b44c234ac4 100644 --- a/hw/vfio/types.h +++ b/hw/vfio/types.h @@ -20,4 +20,6 @@ =20 #define TYPE_VFIO_PCI_NOHOTPLUG "vfio-pci-nohotplug" =20 +#define TYPE_VFIO_APPLE_PCI "vfio-apple-pci" + #endif /* HW_VFIO_VFIO_TYPES_H */ diff --git a/include/hw/vfio/vfio-container.h b/include/hw/vfio/vfio-contai= ner.h index a7d5c5ed67..5ccbabccb4 100644 --- a/include/hw/vfio/vfio-container.h +++ b/include/hw/vfio/vfio-container.h @@ -117,6 +117,7 @@ vfio_container_get_page_size_mask(const VFIOContainer *= bcontainer) #define TYPE_VFIO_IOMMU_SPAPR TYPE_VFIO_IOMMU "-spapr" #define TYPE_VFIO_IOMMU_IOMMUFD TYPE_VFIO_IOMMU "-iommufd" #define TYPE_VFIO_IOMMU_USER TYPE_VFIO_IOMMU "-user" +#define TYPE_VFIO_IOMMU_APPLE TYPE_VFIO_IOMMU "-apple" =20 struct VFIOIOMMUClass { ObjectClass parent_class; --=20 2.50.1 (Apple Git-155) From nobody Sat Apr 11 18:38:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1775374271; cv=none; d=zohomail.com; s=zohoarc; b=RPpD2tBGZatvaJJEDf2X712ykrmlNqRBLH0m/QtxBTSe9jajiX60lqa70xikYpPJvrce/Pc9kMwr5kW0P8hEtD/6TqW/3qMJfznY3r3L9aTAd8lA3Fkqee064Dat1Z1dOwrs1/mmWvuiKfgamuBc42dJGOp339cU8xI5H4JyTXE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775374271; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=bRcv4xt6lJaEweEPbwSohpIgPBQdb1KaetHX8GecBts=; b=EvXAL+9HFSou3ECEmjuKOd3B9D45cCvUQw2Bj9gKj2EP5UMriolrm9xEB+ul9sKdzV47G0n16THjIvJorgWelxdHjhCmYik/JtwsQqAQB+j0NDTS6/VAJdRC+HimbtVCgZ5oNoQJRljzOFMtDKY+GVZeP1vgC0OirKGbMExD8OQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1775374271920579.3725299309949; Sun, 5 Apr 2026 00:31:11 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w9HvN-0003wN-C2; Sun, 05 Apr 2026 03:29:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w9HvL-0003w3-Uh for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:35 -0400 Received: from mail-dy1-x1336.google.com ([2607:f8b0:4864:20::1336]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w9HvJ-0007Io-SA for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:35 -0400 Received: by mail-dy1-x1336.google.com with SMTP id 5a478bee46e88-2ce102afb0aso738817eec.1 for ; Sun, 05 Apr 2026 00:29:33 -0700 (PDT) Received: from localhost.localdomain ([2601:645:8200:47:41e4:ff2b:ff70:4d75]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2cb92ea0ef1sm7636502eec.21.2026.04.05.00.29.30 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 05 Apr 2026 00:29:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1775374172; x=1775978972; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=bRcv4xt6lJaEweEPbwSohpIgPBQdb1KaetHX8GecBts=; b=W1RFJG1F3xTUI/Sq9LdkTx7VutsXVm1jS+6CURFXdLjbQ/ymnl6+yA757jSqaIqy3f oRrcHZSniLFiXgc7tEcxcFORw9d9F220kzcVg9LQ5fwYC8G714cVL9F9V8dsiat3aNFN dltSrPYbVEYvMpMpZrWofNK+v/8ZDWqelkwdL5j85GVKFAtxcs++8BFGA8jJFoW0/Tz8 ulscVAXzhQnJUXQRxKoZQcytE+IzAjSZduTMksYfmGb1MB15IOzDoEf8KBZp0uosqSGI 9ja9MiCK3j+EB59XAMIMuSHkDFOkaGV4Zm1M6Q7JfqT5jiuWH/r/bvybrrwP/ENgzluz q7Qg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775374172; x=1775978972; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=bRcv4xt6lJaEweEPbwSohpIgPBQdb1KaetHX8GecBts=; b=r1fXCEay7os4GBqiMOJ8aZMtpcErhC0GBH7tgBKZkCCr/QIexgOXE0vGOXvea8KXGT MBH7RdL+NJDwGZsnQRamhXBs2lHT+Pwu0zn3K3WL9iuoI6it+E/BoAlYVLLtUKCScNMs OW1iMevLNfZtKsVmwNtIC33U1hjpCE/2bH1c7RyhHLGMLqhnRLObYHtCjN3AtBsI1LPv vlmKb3nAx1vPEL2xU+FY23wPYjJd2LMBz7eaYHjVeueD6QJV4RzRsdpCuqykGHWSQZiP Jd3zuTurezplilvKJss1/KobdU6ylJd9Uj/gw4nounHJYNb4QdBejVoaL5DzI31ZKUOo h4+w== X-Gm-Message-State: AOJu0YyIRWnCKEwTBUAuNCfqk1UBmlInUqlVatr/8hf1nvKOal+8Gl/L OxjAPTm60UNlezZoOweJaX93F8fLhQAzvRjx7aK1qpaS4U2DinATZ6yUPYIE0JDf3hY= X-Gm-Gg: AeBDievF6Kd746PxHwulPcBlxho0WpUEA6W59vLddZNaxmsUM3Vzmte8m7ObQip6QvA XOLbwKtAWAFvj7+305Q6LzAOU2kC82W4y0RGsiKhoA3Qsd4dqoUHfD/m7B5wEnqlnb+/NMHUapm 2JBwTvz+sfI6NiKJHmbiBPQkFvNVjGNRQnc0iwQxGMQUwj5PidqCgPrk94Bt9cDScGsfdBbwIbO 0NGPjvpo/P8H7DShFqkLtlx0yR5VMDckQCZ9vv/g0l+2UqDE2Dc/1ebjxdbrKPUbdjGA8FYWRIw icuCiyCw4co/FNE0WlLKSxsxarcjZSFPVTd7XrEciAOZersB3+H6ShQ4Z11bwT5LsdWagWivxf2 qDBVPM5QfwRb4duQ9V9x16e8lB+hoB3XuLt9P0GzTyNisGi/fgX/Po4efiymJBC1dk/MugwU9rf QUAFDKuZyHlS+srwQDwgunL83HbGn/17BrXXQSM7QhU6yg6F4taMd7bSsH/UYYgtw7o5oS2kdPW SNxUcR1Umvoj4nZV8lEeGHFWsk= X-Received: by 2002:a05:7301:1688:b0:2c4:a862:2372 with SMTP id 5a478bee46e88-2cbf9af888dmr3972848eec.9.1775374172077; Sun, 05 Apr 2026 00:29:32 -0700 (PDT) From: "Scott J. Goldman" To: qemu-devel@nongnu.org Cc: alex@shazbot.org, clg@redhat.com, pbonzini@redhat.com, rbolshakov@ddn.com, phil@philjordan.eu, mst@redhat.com, john.levon@nutanix.com, thanos.makatos@nutanix.com, qemu-s390x@nongnu.org, "Scott J. Goldman" Subject: [RFC PATCH 05/10] vfio: Add region_map and region_unmap callbacks to VFIODeviceIOOps Date: Sun, 5 Apr 2026 00:28:49 -0700 Message-ID: <20260405072857.66484-6-scottjgo@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260405072857.66484-1-scottjgo@gmail.com> References: <20260405072857.66484-1-scottjgo@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::1336; envelope-from=scottjgo@gmail.com; helo=mail-dy1-x1336.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1775374273762154100 Content-Type: text/plain; charset="utf-8" Rename vfio_region_mmap() and vfio_region_unmap() to vfio_region_mmap_fd() and vfio_region_unmap_fd() respectively, and introduce new region_map and region_unmap callbacks in VFIODeviceIOOps. The new vfio_region_mmap() and vfio_region_unmap() functions now dispatch through these io_ops callbacks, allowing different backends to provide their own region mapping implementations. Both the ioctl and vfio-user backends implement the callbacks by calling the renamed fd-based variants. This refactor enables future backends that may require alternate region mapping strategies. Signed-off-by: Scott J. Goldman --- hw/vfio-user/device.c | 16 ++++- hw/vfio/device.c | 14 +++++ hw/vfio/region.c | 108 +++++++++++++++++++++++----------- hw/vfio/vfio-region.h | 4 ++ include/hw/vfio/vfio-device.h | 25 ++++++++ 5 files changed, 131 insertions(+), 36 deletions(-) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index 64ef35b320..957d19217b 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -12,6 +12,7 @@ #include "qemu/lockable.h" #include "qemu/thread.h" =20 +#include "hw/vfio/vfio-region.h" #include "hw/vfio-user/device.h" #include "hw/vfio-user/trace.h" =20 @@ -428,6 +429,18 @@ static int vfio_user_device_io_region_write(VFIODevice= *vbasedev, uint8_t index, return ret; } =20 +static int vfio_user_device_io_region_map(VFIODevice *vbasedev, + VFIORegion *region) +{ + return vfio_region_mmap_fd(region); +} + +static void vfio_user_device_io_region_unmap(VFIODevice *vbasedev, + VFIORegion *region) +{ + vfio_region_unmap_fd(region); +} + /* * Socket-based io_ops */ @@ -437,5 +450,6 @@ VFIODeviceIOOps vfio_user_device_io_ops_sock =3D { .set_irqs =3D vfio_user_device_io_set_irqs, .region_read =3D vfio_user_device_io_region_read, .region_write =3D vfio_user_device_io_region_write, - + .region_map =3D vfio_user_device_io_region_map, + .region_unmap =3D vfio_user_device_io_region_unmap, }; diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 338becffa7..1b703dcbec 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -23,6 +23,7 @@ =20 #include "hw/vfio/vfio-device.h" #include "hw/vfio/pci.h" +#include "hw/vfio/vfio-region.h" #include "hw/core/iommu.h" #include "hw/core/hw-error.h" #include "trace.h" @@ -656,6 +657,17 @@ static int vfio_device_io_region_write(VFIODevice *vba= sedev, uint8_t index, return ret < 0 ? -errno : ret; } =20 +static int vfio_device_io_region_map(VFIODevice *vbasedev, VFIORegion *reg= ion) +{ + return vfio_region_mmap_fd(region); +} + +static void vfio_device_io_region_unmap(VFIODevice *vbasedev, + VFIORegion *region) +{ + vfio_region_unmap_fd(region); +} + static VFIODeviceIOOps vfio_device_io_ops_ioctl =3D { .device_feature =3D vfio_device_io_device_feature, .get_region_info =3D vfio_device_io_get_region_info, @@ -663,4 +675,6 @@ static VFIODeviceIOOps vfio_device_io_ops_ioctl =3D { .set_irqs =3D vfio_device_io_set_irqs, .region_read =3D vfio_device_io_region_read, .region_write =3D vfio_device_io_region_write, + .region_map =3D vfio_device_io_region_map, + .region_unmap =3D vfio_device_io_region_unmap, }; diff --git a/hw/vfio/region.c b/hw/vfio/region.c index 47fdc2df34..9f7780e06c 100644 --- a/hw/vfio/region.c +++ b/hw/vfio/region.c @@ -273,15 +273,48 @@ int vfio_region_setup(Object *obj, VFIODevice *vbased= ev, VFIORegion *region, return 0; } =20 -static void vfio_subregion_unmap(VFIORegion *region, int index) +void vfio_region_register_mmap(VFIORegion *region, int index) { + char *name; + + if (!region->mmaps[index].mmap) { + return; + } + + name =3D g_strdup_printf("%s mmaps[%d]", + memory_region_name(region->mem), index); + memory_region_init_ram_device_ptr(®ion->mmaps[index].mem, + memory_region_owner(region->mem), + name, region->mmaps[index].size, + region->mmaps[index].mmap); + g_free(name); + memory_region_add_subregion(region->mem, region->mmaps[index].offset, + ®ion->mmaps[index].mem); + + trace_vfio_region_mmap(memory_region_name(®ion->mmaps[index].mem), + region->mmaps[index].offset, + region->mmaps[index].offset + + region->mmaps[index].size - 1); +} + +void vfio_region_unregister_mmap(VFIORegion *region, int index) +{ + if (!region->mmaps[index].mmap) { + return; + } + trace_vfio_region_unmap(memory_region_name(®ion->mmaps[index].mem), region->mmaps[index].offset, region->mmaps[index].offset + region->mmaps[index].size - 1); memory_region_del_subregion(region->mem, ®ion->mmaps[index].mem); - munmap(region->mmaps[index].mmap, region->mmaps[index].size); object_unparent(OBJECT(®ion->mmaps[index].mem)); +} + +static void vfio_region_unmap_fd_one(VFIORegion *region, int index) +{ + vfio_region_unregister_mmap(region, index); + munmap(region->mmaps[index].mmap, region->mmaps[index].size); region->mmaps[index].mmap =3D NULL; } =20 @@ -342,14 +375,13 @@ static bool vfio_region_create_dma_buf(VFIORegion *re= gion, Error **errp) return true; } =20 -int vfio_region_mmap(VFIORegion *region) +int vfio_region_mmap_fd(VFIORegion *region) { void *map_base, *map_align; Error *local_err =3D NULL; int i, ret, prot =3D 0; off_t map_offset =3D 0; size_t align; - char *name; int fd; =20 if (!region->mem || !region->nr_mmaps) { @@ -417,21 +449,7 @@ int vfio_region_mmap(VFIORegion *region) goto no_mmap; } =20 - name =3D g_strdup_printf("%s mmaps[%d]", - memory_region_name(region->mem), i); - memory_region_init_ram_device_ptr(®ion->mmaps[i].mem, - memory_region_owner(region->mem), - name, region->mmaps[i].size, - region->mmaps[i].mmap); - g_free(name); - memory_region_add_subregion(region->mem, region->mmaps[i].offset, - ®ion->mmaps[i].mem); - - trace_vfio_region_mmap(memory_region_name(®ion->mmaps[i].mem), - region->mmaps[i].offset, - region->mmaps[i].offset + - region->mmaps[i].size - 1); - + vfio_region_register_mmap(region, i); map_offset =3D region->mmaps[i].offset + region->mmaps[i].size; } =20 @@ -457,13 +475,13 @@ no_mmap: region->mmaps[i].mmap =3D NULL; =20 for (i--; i >=3D 0; i--) { - vfio_subregion_unmap(region, i); + vfio_region_unmap_fd_one(region, i); } =20 return ret; } =20 -void vfio_region_unmap(VFIORegion *region) +void vfio_region_unmap_fd(VFIORegion *region) { int i; =20 @@ -473,41 +491,61 @@ void vfio_region_unmap(VFIORegion *region) =20 for (i =3D 0; i < region->nr_mmaps; i++) { if (region->mmaps[i].mmap) { - vfio_subregion_unmap(region, i); + vfio_region_unmap_fd_one(region, i); } } } =20 -void vfio_region_exit(VFIORegion *region) +int vfio_region_mmap(VFIORegion *region) { - int i; + VFIODevice *vbasedev; + + if (!region->mem) { + return 0; + } + + vbasedev =3D region->vbasedev; + if (!vbasedev->io_ops || !vbasedev->io_ops->region_map) { + return -EINVAL; + } + + return vbasedev->io_ops->region_map(vbasedev, region); +} + +void vfio_region_unmap(VFIORegion *region) +{ + VFIODevice *vbasedev; =20 if (!region->mem) { return; } =20 - for (i =3D 0; i < region->nr_mmaps; i++) { - if (region->mmaps[i].mmap) { - memory_region_del_subregion(region->mem, ®ion->mmaps[i].mem= ); - } + vbasedev =3D region->vbasedev; + if (!vbasedev->io_ops || !vbasedev->io_ops->region_unmap) { + return; + } + + vbasedev->io_ops->region_unmap(vbasedev, region); +} + +void vfio_region_exit(VFIORegion *region) +{ + if (!region->mem) { + return; } =20 + vfio_region_unmap(region); + trace_vfio_region_exit(region->vbasedev->name, region->nr); } =20 void vfio_region_finalize(VFIORegion *region) { - int i; - if (!region->mem) { return; } =20 - for (i =3D 0; i < region->nr_mmaps; i++) { - if (region->mmaps[i].mmap) { - munmap(region->mmaps[i].mmap, region->mmaps[i].size); - } - } + vfio_region_unmap(region); =20 g_free(region->mem); g_free(region->mmaps); diff --git a/hw/vfio/vfio-region.h b/hw/vfio/vfio-region.h index 9b21d4ee5b..afdce466b1 100644 --- a/hw/vfio/vfio-region.h +++ b/hw/vfio/vfio-region.h @@ -39,6 +39,10 @@ uint64_t vfio_region_read(void *opaque, hwaddr addr, unsigned size); int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *regio= n, int index, const char *name, Error **errp); +void vfio_region_register_mmap(VFIORegion *region, int index); +void vfio_region_unregister_mmap(VFIORegion *region, int index); +int vfio_region_mmap_fd(VFIORegion *region); +void vfio_region_unmap_fd(VFIORegion *region); int vfio_region_mmap(VFIORegion *region); void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled); void vfio_region_unmap(VFIORegion *region); diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index 17c5db369c..1a3b42bcaf 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -44,6 +44,7 @@ enum { typedef struct VFIODeviceOps VFIODeviceOps; typedef struct VFIODeviceIOOps VFIODeviceIOOps; typedef struct VFIOMigration VFIOMigration; +typedef struct VFIORegion VFIORegion; =20 typedef struct IOMMUFDBackend IOMMUFDBackend; typedef struct VFIOIOASHwpt VFIOIOASHwpt; @@ -260,6 +261,30 @@ struct VFIODeviceIOOps { */ int (*region_write)(VFIODevice *vdev, uint8_t nr, off_t off, uint32_t = size, void *data, bool post); + + /** + * @region_map + * + * Map a region's directly accessible subranges and register any mmap-= backed + * subregions with QEMU. + * + * @vdev: #VFIODevice to use + * @region: #VFIORegion to map + * + * Returns 0 on success or -errno. + */ + int (*region_map)(VFIODevice *vdev, VFIORegion *region); + + /** + * @region_unmap + * + * Unregister any mmap-backed subregions for a region and release their + * backend mappings. + * + * @vdev: #VFIODevice to use + * @region: #VFIORegion to unmap + */ + void (*region_unmap)(VFIODevice *vdev, VFIORegion *region); }; =20 void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainer *bcontainer, --=20 2.50.1 (Apple Git-155) From nobody Sat Apr 11 18:38:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1775374235; cv=none; d=zohomail.com; s=zohoarc; b=EwMUyg0LH2QnY9fKyKJWiCoaXpAbglf3yYHrsaLPB29n3KDS2i/rNoYuBZdFdR9uuJ//4U36iyTzUgxm7+r2GNY4dpi9xrM+ax6ZG2VuabG1Bnyx+hgfVtALklgRZvI8eN2uPPnlwNca5+X5HYW6a+smsAb4ZdCWBlZygd4l2CA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775374235; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=YLtrvafoDrJyXblIjHcl3upLz3DHq6xj+dch2j8+x6I=; b=N43Pig5YjZ2+S/AgqnFQd9pN2iC4lL7SXl4y/hOAmItBmutiRfZRzVhIWIBvykmc3KJ28X1i/bY061euTZoGj8/VUT5hlJIkZURGUaLlf+smL9qBVz8Lr6xp3kVZp2lPa+oQlAq80+ijGcD1/3YIeISlFMBNb5/H4G/mM8wCilQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1775374235468841.6330711069355; Sun, 5 Apr 2026 00:30:35 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w9HvP-0003xa-S7; Sun, 05 Apr 2026 03:29:39 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w9HvN-0003wf-SL for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:37 -0400 Received: from mail-dy1-x1332.google.com ([2607:f8b0:4864:20::1332]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w9HvM-0007Ls-6e for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:37 -0400 Received: by mail-dy1-x1332.google.com with SMTP id 5a478bee46e88-2c56aa62931so7205459eec.0 for ; Sun, 05 Apr 2026 00:29:35 -0700 (PDT) Received: from localhost.localdomain ([2601:645:8200:47:41e4:ff2b:ff70:4d75]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2cb92ea0ef1sm7636502eec.21.2026.04.05.00.29.32 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 05 Apr 2026 00:29:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1775374174; x=1775978974; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=YLtrvafoDrJyXblIjHcl3upLz3DHq6xj+dch2j8+x6I=; b=ZCV2QlFGExB45llkySP4a2T2IDl0xcwJhIXhoOdzx2zMJeRnzC5fXVA3qFODG/D407 HTkmW7KcqA6tQ1C30QeSWP53jGabwH01z2ZBf7uRQkX7eFQ3XjLpRjMxtV8W54Z4Kw8B ZYJ5jLukOkQWomSXAO/LGtgG2tGQ5kXXpmXojvp3z0UE5VcQ6jqxSklW3PhjoxJpKf95 YNboIAJ5LHZTbyoAALNRLyMWYq+25im/DbC/W5hvYMnJeAarEsNkaIJC0R1A1q7SYejF gBGCRSHLkvUrerY75XzOg40m1XJ/Ldy5srRppw0+8pon5f9dOOrejTzlGNL9O5+d3Lxg dapw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775374174; x=1775978974; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=YLtrvafoDrJyXblIjHcl3upLz3DHq6xj+dch2j8+x6I=; b=Be2Jf3RetRt0HA+LEhuVY0v/1k+w54wA+br6kHnal9ZB80agdtsYrzXGNbIAUlZfsA cl/RTa8k+8qgLxpV7BYg7jZet5s1sPWVddmwtf0vfop8Z6B4uV7Of8i3r+qW/ZrZFt7I w7OWjcVexHjXw/mLSTQf4Hoa+EpK8JOpl7+dQNUHeY0FVtMqwGt/u4dUQVhyzS3gNw59 sIh9EMXqm17jf2rSFb9xvwagqoHakuqZJwcifUSX6RejUd1xCMfuDXrouKnVKn4Nja33 WSSkqhuZmUB3rrHCadsg+ISX94oxpaSUZDhDBrfM+PSkOu1xOckBwb/2imrPkSJD/kr9 2dwg== X-Gm-Message-State: AOJu0YzS3NNTzek36uwWuUYvyTbMofD7WCSj6vix3Ruye1KniGrBwQTj y+UWO6C/PjMIUbRd9TWkzFj52iJCafc7i221XuFTZYAvdvyTtqFSLOIVvewDLmb8QoQ= X-Gm-Gg: AeBDievuyop0YQhpA+cbD341AVeUcHMbY+YUvl0f/9pDCwZ8Iq379YqMW1WTV8ZIW8t Jb5l5n9gtDbYvLpZo8QAgog6RPfk8dIdBNcN8fKgUa1b2pThXrMeW89PXXmuak7ER1zIBkSQnth uqUddwYEYx1j6qXQMeLo76KJ9SdoS9OAAJ6esFoz2KsQf9BwKnrp3tn7DYn4Dz/pzHdg3Lv04Ov FkDpPxccuNeyz5LNbxA3TEjdTPsc2d+rzf1khILmJPOL9xtwCjgvFtS5DB4Mt8fsC9338bgGPOW vyB/5Xbho5XZ+75uPv5uVm6KudhaorHlFdz9D6xlOmd8DJ8JW6hJSCJiDtNkneE5nXE2RGvaj2Z XLBM1GpZQI+SUTw97LSAzdKlKPlCWjUpN9thpJ64sge97izgJ9q36hmiXI1an8d+9OQngpqsqbz axyNyFHIA/nP+CeDyxRRt+z8xfzxlXq+wdipNa6YpWb9pdDat10xfxgsCOgee8BRhKstkMeZiyD wFdsfzbmFSrVU4JKcdXdqope0k= X-Received: by 2002:a05:7300:371f:b0:2c5:76d1:7f95 with SMTP id 5a478bee46e88-2cbfc763331mr4392826eec.28.1775374174457; Sun, 05 Apr 2026 00:29:34 -0700 (PDT) From: "Scott J. Goldman" To: qemu-devel@nongnu.org Cc: alex@shazbot.org, clg@redhat.com, pbonzini@redhat.com, rbolshakov@ddn.com, phil@philjordan.eu, mst@redhat.com, john.levon@nutanix.com, thanos.makatos@nutanix.com, qemu-s390x@nongnu.org, "Scott J. Goldman" Subject: [RFC PATCH 06/10] vfio: Add device_reset callback to VFIODeviceIOOps Date: Sun, 5 Apr 2026 00:28:50 -0700 Message-ID: <20260405072857.66484-7-scottjgo@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260405072857.66484-1-scottjgo@gmail.com> References: <20260405072857.66484-1-scottjgo@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::1332; envelope-from=scottjgo@gmail.com; helo=mail-dy1-x1332.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1775374237596154100 Content-Type: text/plain; charset="utf-8" Route all VFIO_DEVICE_RESET ioctl calls through a new device_reset io_ops callback, matching the pattern established for region_map and region_unmap. This allows non-Linux backends to provide their own reset implementation. The Linux ioctl backend implements the callback by issuing the VFIO_DEVICE_RESET ioctl. All existing callsites in pci.c, ccw.c, ap.c, and migration.c are converted to use the callback. Signed-off-by: Scott J. Goldman --- hw/vfio/ap.c | 4 ++-- hw/vfio/ccw.c | 2 +- hw/vfio/device.c | 8 ++++++++ hw/vfio/migration.c | 5 +++-- hw/vfio/pci.c | 6 ++++-- include/hw/vfio/vfio-device.h | 11 +++++++++++ 6 files changed, 29 insertions(+), 7 deletions(-) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 5c8f305653..2f2f17e666 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -290,10 +290,10 @@ static void vfio_ap_reset(DeviceState *dev) int ret; VFIOAPDevice *vapdev =3D VFIO_AP_DEVICE(dev); =20 - ret =3D ioctl(vapdev->vdev.fd, VFIO_DEVICE_RESET); + ret =3D vapdev->vdev.io_ops->device_reset(&vapdev->vdev); if (ret) { error_report("%s: failed to reset %s device: %s", __func__, - vapdev->vdev.name, strerror(errno)); + vapdev->vdev.name, strerror(-ret)); } } =20 diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index ce9c014e6a..330b733b7e 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -242,7 +242,7 @@ static void vfio_ccw_reset(DeviceState *dev) { VFIOCCWDevice *vcdev =3D VFIO_CCW(dev); =20 - ioctl(vcdev->vdev.fd, VFIO_DEVICE_RESET); + vcdev->vdev.io_ops->device_reset(&vcdev->vdev); } =20 static void vfio_ccw_crw_read(VFIOCCWDevice *vcdev) diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 1b703dcbec..cf3953d975 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -657,6 +657,13 @@ static int vfio_device_io_region_write(VFIODevice *vba= sedev, uint8_t index, return ret < 0 ? -errno : ret; } =20 +static int vfio_device_io_device_reset(VFIODevice *vbasedev) +{ + int ret =3D ioctl(vbasedev->fd, VFIO_DEVICE_RESET); + + return ret < 0 ? -errno : ret; +} + static int vfio_device_io_region_map(VFIODevice *vbasedev, VFIORegion *reg= ion) { return vfio_region_mmap_fd(region); @@ -673,6 +680,7 @@ static VFIODeviceIOOps vfio_device_io_ops_ioctl =3D { .get_region_info =3D vfio_device_io_get_region_info, .get_irq_info =3D vfio_device_io_get_irq_info, .set_irqs =3D vfio_device_io_set_irqs, + .device_reset =3D vfio_device_io_device_reset, .region_read =3D vfio_device_io_region_read, .region_write =3D vfio_device_io_region_write, .region_map =3D vfio_device_io_region_map, diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 83327b6573..b31253ea90 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -216,9 +216,10 @@ int vfio_migration_set_state(VFIODevice *vbasedev, return 0; =20 reset_device: - if (ioctl(vbasedev->fd, VFIO_DEVICE_RESET)) { + ret =3D vbasedev->io_ops->device_reset(vbasedev); + if (ret) { hw_error("%s: Failed resetting device, err: %s", vbasedev->name, - strerror(errno)); + strerror(-ret)); } =20 vfio_migration_set_device_state(vbasedev, VFIO_DEVICE_STATE_RUNNING); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index cf817d9ae7..458b3400cc 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3691,7 +3691,8 @@ static void vfio_pci_reset(DeviceState *dev) =20 if (vdev->vbasedev.reset_works && (vdev->has_flr || !vdev->has_pm_reset) && - !ioctl(vdev->vbasedev.fd, VFIO_DEVICE_RESET)) { + vdev->vbasedev.io_ops && vdev->vbasedev.io_ops->device_reset && + !vdev->vbasedev.io_ops->device_reset(&vdev->vbasedev)) { trace_vfio_pci_reset_flr(vdev->vbasedev.name); goto post_reset; } @@ -3703,7 +3704,8 @@ static void vfio_pci_reset(DeviceState *dev) =20 /* If nothing else works and the device supports PM reset, use it */ if (vdev->vbasedev.reset_works && vdev->has_pm_reset && - !ioctl(vdev->vbasedev.fd, VFIO_DEVICE_RESET)) { + vdev->vbasedev.io_ops && vdev->vbasedev.io_ops->device_reset && + !vdev->vbasedev.io_ops->device_reset(&vdev->vbasedev)) { trace_vfio_pci_reset_pm(vdev->vbasedev.name); goto post_reset; } diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index 1a3b42bcaf..0e6bff774e 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -262,6 +262,17 @@ struct VFIODeviceIOOps { int (*region_write)(VFIODevice *vdev, uint8_t nr, off_t off, uint32_t = size, void *data, bool post); =20 + /** + * @device_reset + * + * Reset the device. + * + * @vdev: #VFIODevice to reset + * + * Returns 0 on success or -errno. + */ + int (*device_reset)(VFIODevice *vdev); + /** * @region_map * --=20 2.50.1 (Apple Git-155) From nobody Sat Apr 11 18:38:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1775374270; cv=none; d=zohomail.com; s=zohoarc; b=f9B97pdTYcnxLvtaw2iVAuwNi6NnPnKVBN6N0BU1F8aSpbKePLh64bKtWRxm8tRd8ujk8CC+8HQv+4S2CLqSDvas/4px3+IdwYzKVEN/aZVQrld8Xt1gbPECWV1GttdNFP7H8nZxQhqtu69TEv1CTBMomMcYaZ9zFmvZz0xTuHw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775374270; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=zxfini/03ZCMwgjgBQ4pisEMx2q4rGRX8zkQlXe0tR4=; b=juu/LAK5LDP9qDkHXXQiBFI2HKU/OBXLCPOb7zbu0krheT/9BmGWuPWLj6t7Wz9aMQNXNjRZg21JdSyHLmGsE7Nuk5nZ4XzoV/UFzAjHa75LMJKX2H6R7gZLWFglT9s5PnOOITdj0NVDv+Qa7hRGujYE9GWMTZrwIKzMATxVwHI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1775374270277600.2086372222857; Sun, 5 Apr 2026 00:31:10 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w9HvT-0003yA-0N; Sun, 05 Apr 2026 03:29:43 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w9HvQ-0003xj-Mo for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:40 -0400 Received: from mail-dy1-x1330.google.com ([2607:f8b0:4864:20::1330]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w9HvN-0007Nf-QK for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:40 -0400 Received: by mail-dy1-x1330.google.com with SMTP id 5a478bee46e88-2c156c4a9efso3546438eec.1 for ; Sun, 05 Apr 2026 00:29:37 -0700 (PDT) Received: from localhost.localdomain ([2601:645:8200:47:41e4:ff2b:ff70:4d75]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2cb92ea0ef1sm7636502eec.21.2026.04.05.00.29.34 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 05 Apr 2026 00:29:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1775374176; x=1775978976; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=zxfini/03ZCMwgjgBQ4pisEMx2q4rGRX8zkQlXe0tR4=; b=NB8bg2WYH+z/AmCtJCvRXkos/w/gGzR/p8f4znh4dQsYnSWEU66n7Vu6oSknX9iSWf 74fv6g01TFcAk/fh2N1yjSNNLNisdlpO6e7aoKgKJ/aFoWe5zg5rwoKCSKy/PkxS04C9 gyCFgP00D5JehrGqcOTwzLdpiUE+CMKP5R+7bPxbHfDn1fIWUbvN/9rhFhSkmnEP9H70 WtZZCDgVNd2duPPDeCRExel0icXuGTAd09jqjXyQ93eQWWOoAsELkn5eM4Rc7s90V6ns yyEToP3N/pPObSesmhDS0kgq+IOoAiwQSNRLph7ScLBNOh3n0+lpo532VU0K/gwAq2YB H7pg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775374176; x=1775978976; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=zxfini/03ZCMwgjgBQ4pisEMx2q4rGRX8zkQlXe0tR4=; b=VV1NTNLPXOdjIGJk+WMnicuiokkaXwulKFCzW1+8kCbvooif0Y2R6oPNwc8qoMqSxJ +hvD1OR86Wt2oEyaQB1efXrRY+uyR/RjyEG6Eo4g4en6UqLhr5MqHXRSA/LnRO+Mhtn9 Lf4RLNEvh3Iy4YG2vINvkynCIwYWYXC9VTEyZIhKBSRU8hm79GEQOIRw/gpCMmDaGhj/ hjyFp1LheOMGzPqB94fXIGwy71K9DdL7qFM4NiI2B9S+qAeCeS1X22ZrB8MD0C9JKNiR xkk5OYR7DQLqNtCkGIBSY2F5qRkGS7PQkEtLTW+5/brzShtBcaMzm399OPcj3L5ZOnd5 S8ug== X-Gm-Message-State: AOJu0YweX1qPYYIZTiu1m6aBJfGWc9NXGGpPqty+kGakmmOsJpn6LtgY sWiBIbxfW1HFuCosSzZhh7UzNoF1ws4ZLxJqyEagszgLmsI8RamnjWFEF8ALkhyNy2c= X-Gm-Gg: AeBDietDqHRNB86d8jEc4skl6er5lAkiOkv/e9tOr3EwYO6ttIrPMKWbQTocmFaEsw9 MndWujHnceNtZMqrZYS1aXiVmgKjPFyNRtF9kzLNEnGxpkNoildu40+enpToSdCcdFDPBZ2hwYA Wqug7NqhzTj1V+j8wjSnWsCWgeJWwunrknvAs1dqZoC3Q2kf60ipM5oLBG+ki6j1r2GXOVwF2pW 4LsNXBnr/7E8CtTsXj/4q17KqGZb8EbCxX/B+2i2Ss8MCdF/7+pwQtaqtlQ9FVMlGzPqkLj52tT sVJi5XnvswGrp9rkQX94R7tRA2W5aKozTxNEEVwvM76F4J3otTZvFUJwg7YDdFn3KSmVRso9wxK SIIbYd99imKFhSl1jJwJUYi0zmcQToCzSnfudpOKUF6NVJjF5E9VyKoEOIZU7+MtKehGddRx8y7 9+ZL/g/t29Pet8PB1j1nhLfm6PpqW/uoZC89wngB7wZ6u1aXXVBjQdORtLcx0kP97S+MvoRjY3e ZW39jN2U3/L/QcxiOmIANWrmqo= X-Received: by 2002:a05:7301:3f0f:b0:2c8:7172:3b88 with SMTP id 5a478bee46e88-2cbfc16ec3bmr3959627eec.28.1775374175786; Sun, 05 Apr 2026 00:29:35 -0700 (PDT) From: "Scott J. Goldman" To: qemu-devel@nongnu.org Cc: alex@shazbot.org, clg@redhat.com, pbonzini@redhat.com, rbolshakov@ddn.com, phil@philjordan.eu, mst@redhat.com, john.levon@nutanix.com, thanos.makatos@nutanix.com, qemu-s390x@nongnu.org, "Scott J. Goldman" , "Scott J. Goldman" Subject: [RFC PATCH 07/10] vfio/apple: Add DriverKit dext client library Date: Sun, 5 Apr 2026 00:28:51 -0700 Message-ID: <20260405072857.66484-8-scottjgo@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260405072857.66484-1-scottjgo@gmail.com> References: <20260405072857.66484-1-scottjgo@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::1330; envelope-from=scottjgo@gmail.com; helo=mail-dy1-x1330.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1775374272097154100 From: "Scott J. Goldman" Add the C client library for communicating with the VFIOUserPCIDriver DriverKit extension (dext) on macOS. This provides the low-level IOUserClient wrappers that the Apple VFIO backend will use: - Connection management (connect, disconnect, claim) - PCI config space read/write (individual and block) - BAR info queries, BAR mapping/unmapping, MMIO read/write - DMA region registration and unregistration - Interrupt setup, pending IRQ polling, async notification - Device reset (FLR with hot-reset fallback) All calls go through IOKit's IOConnectCallScalarMethod / IOConnectMapMemory64 to the dext, which mediates access to the physical PCI device via PCIDriverKit. Signed-off-by: Scott J. Goldman --- hw/vfio/apple-dext-client.c | 681 ++++++++++++++++++++++++++++++++++++ hw/vfio/apple-dext-client.h | 253 ++++++++++++++ hw/vfio/meson.build | 7 + 3 files changed, 941 insertions(+) create mode 100644 hw/vfio/apple-dext-client.c create mode 100644 hw/vfio/apple-dext-client.h diff --git a/hw/vfio/apple-dext-client.c b/hw/vfio/apple-dext-client.c new file mode 100644 index 0000000000..7ba03fc6e9 --- /dev/null +++ b/hw/vfio/apple-dext-client.c @@ -0,0 +1,681 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * C client implementation for communicating with the VFIOUserPCIDriver de= xt + * via IOKit IOUserClient. + * + * Copyright (c) 2026 Scott J. Goldman + */ + +#include "qemu/osdep.h" + +#include "apple-dext-client.h" + +#include +#include +#include +#include + +enum { + kSelectorGetIdentity =3D 0, + kSelectorClaim =3D 1, + kSelectorTerminate =3D 2, + kSelectorAllocateDMABuffer =3D 3, + kSelectorFreeDMABuffer =3D 4, + kSelectorRegisterDMARegion =3D 5, + kSelectorUnregisterDMARegion =3D 6, + kSelectorProbeDMARegion =3D 7, + kSelectorConfigRead =3D 8, + kSelectorConfigWrite =3D 9, + kSelectorGetBARInfo =3D 10, + kSelectorMMIORead =3D 11, + kSelectorMMIOWrite =3D 12, + kSelectorSetupInterrupts =3D 13, + kSelectorCheckInterrupt =3D 14, + kSelectorWaitInterrupt =3D 15, + kSelectorSetIRQMask =3D 16, + kSelectorResetDevice =3D 17, +}; + +/* + * Keep this in sync with PCIDriverKit BAR type encoding. Bit 3 indicates + * prefetchability for memory BARs. + */ +#define APPLE_DEXT_BAR_PREFETCHABLE_MASK 0x08 +#ifndef kIOMapWriteCombineCache +#define kIOMapWriteCombineCache 0x00000400 +#endif + +static bool +dext_service_matches_class(io_service_t service, const char *className) +{ + bool match =3D false; + CFTypeRef ref; + + ref =3D IORegistryEntryCreateCFProperty(service, CFSTR("IOUserClass"), + kCFAllocatorDefault, 0); + if (ref =3D=3D NULL) { + return false; + } + + if (CFGetTypeID(ref) =3D=3D CFStringGetTypeID()) { + CFStringRef expected =3D CFStringCreateWithCString( + kCFAllocatorDefault, className, kCFStringEncodingUTF8); + if (expected !=3D NULL) { + match =3D CFStringCompare((CFStringRef)ref, expected, 0) + =3D=3D kCFCompareEqualTo; + CFRelease(expected); + } + } + CFRelease(ref); + return match; +} + +static bool +dext_connection_matches_bdf(io_connect_t connection, + uint8_t bus, uint8_t device, uint8_t function) +{ + uint64_t output[6] =3D {0}; + uint32_t outputCount =3D 6; + kern_return_t kr; + + kr =3D IOConnectCallMethod(connection, kSelectorGetIdentity, + NULL, 0, NULL, 0, + output, &outputCount, + NULL, NULL); + if (kr !=3D KERN_SUCCESS || outputCount < 3) { + return false; + } + + return (uint8_t)output[0] =3D=3D bus && + (uint8_t)output[1] =3D=3D device && + (uint8_t)output[2] =3D=3D function; +} + +io_connect_t +apple_dext_connect(uint8_t bus, uint8_t device, uint8_t function) +{ + CFMutableDictionaryRef matching; + io_iterator_t iterator =3D IO_OBJECT_NULL; + io_connect_t result =3D IO_OBJECT_NULL; + io_service_t service; + kern_return_t kr; + + matching =3D IOServiceMatching("IOUserService"); + if (matching =3D=3D NULL) { + return IO_OBJECT_NULL; + } + + kr =3D IOServiceGetMatchingServices(kIOMainPortDefault, matching, &ite= rator); + if (kr !=3D KERN_SUCCESS) { + return IO_OBJECT_NULL; + } + + while ((service =3D IOIteratorNext(iterator)) !=3D IO_OBJECT_NULL) { + io_connect_t connection =3D IO_OBJECT_NULL; + + if (!dext_service_matches_class(service, "VFIOUserPCIDriver")) { + IOObjectRelease(service); + continue; + } + + kr =3D IOServiceOpen(service, mach_task_self(), 0, &connection); + IOObjectRelease(service); + + if (kr !=3D KERN_SUCCESS) { + continue; + } + + if (dext_connection_matches_bdf(connection, bus, device, function)= ) { + result =3D connection; + break; + } + + IOServiceClose(connection); + } + + IOObjectRelease(iterator); + return result; +} + +void +apple_dext_disconnect(io_connect_t connection) +{ + if (connection !=3D IO_OBJECT_NULL) { + IOServiceClose(connection); + } +} + +kern_return_t +apple_dext_claim(io_connect_t connection) +{ + if (connection =3D=3D IO_OBJECT_NULL) { + return kIOReturnBadArgument; + } + + return IOConnectCallMethod(connection, + kSelectorClaim, + NULL, 0, NULL, 0, + NULL, NULL, NULL, NULL); +} + +kern_return_t +apple_dext_register_dma(io_connect_t connection, + uint64_t iova, + uint64_t client_va, + uint64_t size, + uint64_t *out_bus_addr, + uint64_t *out_bus_len) +{ + uint64_t input[3] =3D { iova, client_va, size }; + uint64_t output[3] =3D {0}; + uint32_t outputCount =3D 3; + kern_return_t kr; + + if (connection =3D=3D IO_OBJECT_NULL) { + return kIOReturnBadArgument; + } + + kr =3D IOConnectCallMethod(connection, + kSelectorRegisterDMARegion, + input, 3, + NULL, 0, + output, &outputCount, + NULL, NULL); + if (kr !=3D KERN_SUCCESS) { + return kr; + } + + if (out_bus_addr !=3D NULL && outputCount >=3D 2) { + *out_bus_addr =3D output[1]; + } + if (out_bus_len !=3D NULL && outputCount >=3D 3) { + *out_bus_len =3D output[2]; + } + + return kIOReturnSuccess; +} + +kern_return_t +apple_dext_unregister_dma(io_connect_t connection, + uint64_t iova) +{ + uint64_t input[1] =3D { iova }; + + if (connection =3D=3D IO_OBJECT_NULL) { + return kIOReturnBadArgument; + } + + return IOConnectCallMethod(connection, + kSelectorUnregisterDMARegion, + input, 1, + NULL, 0, + NULL, NULL, + NULL, NULL); +} + +kern_return_t +apple_dext_probe_dma(io_connect_t connection, + uint64_t iova, + uint64_t offset, + uint64_t *out_word) +{ + uint64_t input[2] =3D { iova, offset }; + uint64_t output[1] =3D {0}; + uint32_t outputCount =3D 1; + kern_return_t kr; + + if (connection =3D=3D IO_OBJECT_NULL || out_word =3D=3D NULL) { + return kIOReturnBadArgument; + } + + kr =3D IOConnectCallMethod(connection, + kSelectorProbeDMARegion, + input, 2, + NULL, 0, + output, &outputCount, + NULL, NULL); + if (kr !=3D KERN_SUCCESS) { + return kr; + } + + *out_word =3D output[0]; + return kIOReturnSuccess; +} + +kern_return_t +apple_dext_config_read(io_connect_t connection, + uint64_t offset, + uint64_t width, + uint64_t *out_value) +{ + uint64_t input[2] =3D { offset, width }; + uint64_t output[1] =3D {0}; + uint32_t outputCount =3D 1; + kern_return_t kr; + + if (connection =3D=3D IO_OBJECT_NULL || out_value =3D=3D NULL) { + return kIOReturnBadArgument; + } + + kr =3D IOConnectCallMethod(connection, + kSelectorConfigRead, + input, 2, + NULL, 0, + output, &outputCount, + NULL, NULL); + if (kr !=3D KERN_SUCCESS) { + return kr; + } + + *out_value =3D output[0]; + return kIOReturnSuccess; +} + +kern_return_t +apple_dext_config_write(io_connect_t connection, + uint64_t offset, + uint64_t width, + uint64_t value) +{ + uint64_t input[3] =3D { offset, width, value }; + + if (connection =3D=3D IO_OBJECT_NULL) { + return kIOReturnBadArgument; + } + + return IOConnectCallMethod(connection, + kSelectorConfigWrite, + input, 3, + NULL, 0, + NULL, NULL, + NULL, NULL); +} + +kern_return_t +apple_dext_config_read_block(io_connect_t connection, + uint64_t offset, + void *buf, + size_t len) +{ + uint8_t *dst =3D (uint8_t *)buf; + uint64_t pos =3D offset; + size_t remaining =3D len; + + if (connection =3D=3D IO_OBJECT_NULL || buf =3D=3D NULL) { + return kIOReturnBadArgument; + } + + while (remaining >=3D 4) { + uint64_t val =3D 0; + uint32_t dword; + kern_return_t kr; + + kr =3D apple_dext_config_read(connection, pos, 4, &val); + if (kr !=3D KERN_SUCCESS) { + return kr; + } + dword =3D (uint32_t)val; + memcpy(dst, &dword, 4); + dst +=3D 4; + pos +=3D 4; + remaining -=3D 4; + } + + while (remaining > 0) { + uint64_t val =3D 0; + kern_return_t kr; + + kr =3D apple_dext_config_read(connection, pos, 1, &val); + if (kr !=3D KERN_SUCCESS) { + return kr; + } + *dst =3D (uint8_t)val; + dst++; + pos++; + remaining--; + } + + return kIOReturnSuccess; +} + +kern_return_t +apple_dext_get_bar_info(io_connect_t connection, + uint8_t bar, + uint8_t *out_mem_idx, + uint64_t *out_size, + uint8_t *out_type) +{ + uint64_t input[1] =3D { bar }; + uint64_t output[3] =3D {0}; + uint32_t outputCount =3D 3; + kern_return_t kr; + + if (connection =3D=3D IO_OBJECT_NULL) { + return kIOReturnBadArgument; + } + + kr =3D IOConnectCallMethod(connection, + kSelectorGetBARInfo, + input, 1, + NULL, 0, + output, &outputCount, + NULL, NULL); + if (kr !=3D KERN_SUCCESS) { + return kr; + } + + if (out_mem_idx !=3D NULL) { + *out_mem_idx =3D (uint8_t)output[0]; + } + if (out_size !=3D NULL) { + *out_size =3D output[1]; + } + if (out_type !=3D NULL) { + *out_type =3D (uint8_t)output[2]; + } + + return kIOReturnSuccess; +} + +kern_return_t +apple_dext_map_bar(io_connect_t connection, + uint8_t bar, + mach_vm_address_t *out_addr, + mach_vm_size_t *out_size, + uint8_t *out_type) +{ + uint64_t bar_size =3D 0; + uint8_t bar_type =3D 0; + uint32_t mem_type; + mach_vm_address_t addr =3D 0; + mach_vm_size_t size =3D 0; + IOOptionBits opts =3D kIOMapAnywhere; + kern_return_t kr; + + if (connection =3D=3D IO_OBJECT_NULL || out_addr =3D=3D NULL || out_si= ze =3D=3D NULL) { + return kIOReturnBadArgument; + } + + kr =3D apple_dext_get_bar_info(connection, bar, NULL, + &bar_size, &bar_type); + if (kr !=3D KERN_SUCCESS) { + return kr; + } + + /* + * The memory type for IOConnectMapMemory64 must match the dext's + * CopyClientMemoryForType expectation: + * kVFIOUserPCIDriverUserClientMemoryTypeBAR0 (=3D 1) plus the BAR ind= ex. + * This is NOT the same as the PCIDriverKit internal memoryIndex retur= ned + * by GetBARInfo. + */ + mem_type =3D 1 + (uint32_t)bar; + + if (bar_type & APPLE_DEXT_BAR_PREFETCHABLE_MASK) { + opts |=3D kIOMapWriteCombineCache; + } + + kr =3D IOConnectMapMemory64(connection, mem_type, mach_task_self(), + &addr, &size, opts); + if (kr !=3D KERN_SUCCESS) { + return kr; + } + + *out_addr =3D addr; + *out_size =3D size; + if (out_type !=3D NULL) { + *out_type =3D bar_type; + } + return kIOReturnSuccess; +} + +kern_return_t +apple_dext_unmap_bar(io_connect_t connection, + uint8_t bar, + mach_vm_address_t addr) +{ + uint32_t mem_type =3D 1 + (uint32_t)bar; + + if (connection =3D=3D IO_OBJECT_NULL || addr =3D=3D 0) { + return kIOReturnBadArgument; + } + + return IOConnectUnmapMemory64(connection, mem_type, mach_task_self(), = addr); +} + +kern_return_t +apple_dext_mmio_read(io_connect_t connection, + uint8_t mem_idx, + uint64_t offset, + uint64_t width, + uint64_t *out_value) +{ + uint64_t input[3] =3D { mem_idx, offset, width }; + uint64_t output[1] =3D {0}; + uint32_t outputCount =3D 1; + kern_return_t kr; + + if (connection =3D=3D IO_OBJECT_NULL || out_value =3D=3D NULL) { + return kIOReturnBadArgument; + } + + kr =3D IOConnectCallMethod(connection, + kSelectorMMIORead, + input, 3, + NULL, 0, + output, &outputCount, + NULL, NULL); + if (kr !=3D KERN_SUCCESS) { + return kr; + } + + *out_value =3D output[0]; + return kIOReturnSuccess; +} + +kern_return_t +apple_dext_mmio_write(io_connect_t connection, + uint8_t mem_idx, + uint64_t offset, + uint64_t width, + uint64_t value) +{ + uint64_t input[4] =3D { mem_idx, offset, width, value }; + + if (connection =3D=3D IO_OBJECT_NULL) { + return kIOReturnBadArgument; + } + + return IOConnectCallMethod(connection, + kSelectorMMIOWrite, + input, 4, + NULL, 0, + NULL, NULL, + NULL, NULL); +} + +kern_return_t +apple_dext_setup_interrupts(io_connect_t connection, + uint32_t *out_num_vectors) +{ + uint64_t output[1] =3D {0}; + uint32_t outputCount =3D 1; + kern_return_t kr; + + if (connection =3D=3D IO_OBJECT_NULL) { + return kIOReturnBadArgument; + } + + kr =3D IOConnectCallMethod(connection, + kSelectorSetupInterrupts, + NULL, 0, + NULL, 0, + output, &outputCount, + NULL, NULL); + if (kr !=3D KERN_SUCCESS) { + return kr; + } + + if (out_num_vectors !=3D NULL && outputCount >=3D 1) { + *out_num_vectors =3D (uint32_t)output[0]; + } + + return kIOReturnSuccess; +} + +kern_return_t +apple_dext_reset_device(io_connect_t connection) +{ + if (connection =3D=3D IO_OBJECT_NULL) { + return kIOReturnBadArgument; + } + + return IOConnectCallMethod(connection, + kSelectorResetDevice, + NULL, 0, NULL, 0, + NULL, NULL, NULL, NULL); +} + +kern_return_t +apple_dext_set_irq_mask(io_connect_t connection, const uint64_t mask[4]) +{ + if (connection =3D=3D IO_OBJECT_NULL || mask =3D=3D NULL) { + return kIOReturnBadArgument; + } + + return IOConnectCallMethod(connection, + kSelectorSetIRQMask, + mask, 4, + NULL, 0, + NULL, NULL, + NULL, NULL); +} + +kern_return_t +apple_dext_read_pending_irqs(io_connect_t connection, uint64_t pending[4]) +{ + uint64_t output[4] =3D {0}; + uint32_t outputCount =3D 4; + kern_return_t kr; + uint32_t i; + + if (connection =3D=3D IO_OBJECT_NULL || pending =3D=3D NULL) { + return kIOReturnBadArgument; + } + + kr =3D IOConnectCallMethod(connection, + kSelectorCheckInterrupt, + NULL, 0, + NULL, 0, + output, &outputCount, + NULL, NULL); + if (kr !=3D KERN_SUCCESS) { + return kr; + } + + for (i =3D 0; i < 4; i++) { + pending[i] =3D (i < outputCount) ? output[i] : 0; + } + + return kIOReturnSuccess; +} + +struct AppleDextInterruptNotify { + io_connect_t connection; + IONotificationPortRef notifyPort; + mach_port_t machPort; + dispatch_queue_t dispatchQueue; + void (*handler_fn)(void *opaque); + void *opaque; +}; + +static void +apple_dext_async_callback(void *refcon, IOReturn result, + void **args, uint32_t numArgs) +{ + AppleDextInterruptNotify *notify =3D refcon; + + if (result =3D=3D kIOReturnSuccess && notify->handler_fn) { + notify->handler_fn(notify->opaque); + } +} + +static kern_return_t +apple_dext_interrupt_notify_arm(AppleDextInterruptNotify *notify) +{ + uint64_t asyncRef[kIOAsyncCalloutCount]; + + asyncRef[kIOAsyncCalloutFuncIndex] =3D + (uint64_t)(uintptr_t)apple_dext_async_callback; + asyncRef[kIOAsyncCalloutRefconIndex] =3D + (uint64_t)(uintptr_t)notify; + + return IOConnectCallAsyncMethod(notify->connection, + kSelectorWaitInterrupt, + notify->machPort, + asyncRef, kIOAsyncCalloutCount, + NULL, 0, NULL, 0, + NULL, NULL, NULL, NULL); +} + +AppleDextInterruptNotify * +apple_dext_interrupt_notify_create(io_connect_t connection, + void (*handler_fn)(void *opaque), + void *opaque) +{ + AppleDextInterruptNotify *notify; + kern_return_t kr; + + if (connection =3D=3D IO_OBJECT_NULL || handler_fn =3D=3D NULL) { + return NULL; + } + + notify =3D g_new0(AppleDextInterruptNotify, 1); + notify->connection =3D connection; + notify->handler_fn =3D handler_fn; + notify->opaque =3D opaque; + + notify->notifyPort =3D IONotificationPortCreate(kIOMainPortDefault); + if (!notify->notifyPort) { + g_free(notify); + return NULL; + } + + notify->dispatchQueue =3D dispatch_queue_create( + "org.qemu.vfio-apple.irq-notify", DISPATCH_QUEUE_SERIAL); + IONotificationPortSetDispatchQueue(notify->notifyPort, + notify->dispatchQueue); + notify->machPort =3D IONotificationPortGetMachPort(notify->notifyPort); + + kr =3D apple_dext_interrupt_notify_arm(notify); + if (kr !=3D KERN_SUCCESS) { + IONotificationPortDestroy(notify->notifyPort); + dispatch_release(notify->dispatchQueue); + g_free(notify); + return NULL; + } + + return notify; +} + +kern_return_t +apple_dext_interrupt_notify_rearm(AppleDextInterruptNotify *notify) +{ + if (!notify) { + return kIOReturnBadArgument; + } + return apple_dext_interrupt_notify_arm(notify); +} + +void +apple_dext_interrupt_notify_destroy(AppleDextInterruptNotify *notify) +{ + if (!notify) { + return; + } + + IONotificationPortDestroy(notify->notifyPort); + dispatch_release(notify->dispatchQueue); + g_free(notify); +} diff --git a/hw/vfio/apple-dext-client.h b/hw/vfio/apple-dext-client.h new file mode 100644 index 0000000000..07574493e6 --- /dev/null +++ b/hw/vfio/apple-dext-client.h @@ -0,0 +1,253 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * C API for connecting to the VFIOUserPCIDriver DriverKit extension. + * + * The vfio-user server process uses this to: + * 1. Find and open an IOUserClient to the dext for a given PCI BDF. + * 2. Claim the device so the dext opens its IOPCIDevice provider. + * 3. Register client-owned memory (QEMU guest RAM mapped via shared file) + * for DMA by the physical PCI device. + * 4. Unregister DMA regions when QEMU removes them. + * + * Integration with libvfio-user: + * vfu_dma_register_cb_t -> apple_dext_register_dma() + * vfu_dma_unregister_cb_t -> apple_dext_unregister_dma() + * + * Copyright (c) 2026 Scott J. Goldman + */ + +#ifndef HW_VFIO_APPLE_DEXT_CLIENT_H +#define HW_VFIO_APPLE_DEXT_CLIENT_H + +#include +#include + +/* + * Find the VFIOUserPCIDriver dext instance matching the given PCI BDF + * and open an IOUserClient connection to it. + * Returns IO_OBJECT_NULL on failure. + */ +io_connect_t apple_dext_connect(uint8_t bus, uint8_t device, + uint8_t function); + +/* + * Close a previously opened connection. + */ +void apple_dext_disconnect(io_connect_t connection); + +/* + * Claim the PCI device through the dext (opens the IOPCIDevice provider). + * Must be called before registering DMA regions. + */ +kern_return_t apple_dext_claim(io_connect_t connection); + +/* + * Register a region of this process's address space for DMA. + * + * @iova: guest IOVA (device-visible DMA address) + * @client_va: virtual address of the memory in this process + * @size: region size in bytes + * @out_bus_addr: receives first DMA bus address segment (may be NULL) + * @out_bus_len: receives first DMA bus address segment length (may be NU= LL) + * + * The memory at client_va must remain valid and mapped until the region + * is unregistered. + */ +kern_return_t apple_dext_register_dma(io_connect_t connection, + uint64_t iova, + uint64_t client_va, + uint64_t size, + uint64_t *out_bus_addr, + uint64_t *out_bus_len); + +/* + * Unregister a previously registered DMA region identified by its IOVA. + */ +kern_return_t apple_dext_unregister_dma(io_connect_t connection, + uint64_t iova); + +/* + * Read 8 bytes from a registered DMA region's IOMemoryDescriptor. + * Used to verify the descriptor references the same physical pages + * as the client's virtual mapping. + * + * @iova: base IOVA of the registered region + * @offset: byte offset within the region to read from + * @out_word: receives the 8-byte value read from the descriptor + */ +kern_return_t apple_dext_probe_dma(io_connect_t connection, + uint64_t iova, + uint64_t offset, + uint64_t *out_word); + +/* + * Read from PCI configuration space. + * + * @offset: byte offset into config space + * @width: access width in bytes (1, 2, or 4) + * @out_value: receives the value read + */ +kern_return_t apple_dext_config_read(io_connect_t connection, + uint64_t offset, + uint64_t width, + uint64_t *out_value); + +/* + * Write to PCI configuration space. + * + * @offset: byte offset into config space + * @width: access width in bytes (1, 2, or 4) + * @value: value to write + */ +kern_return_t apple_dext_config_write(io_connect_t connection, + uint64_t offset, + uint64_t width, + uint64_t value); + +/* + * Read a contiguous block of PCI configuration space. + * Internally issues repeated 32-bit reads, with a final + * narrower read for any trailing bytes. + * + * @offset: starting byte offset + * @buf: destination buffer + * @len: number of bytes to read + */ +kern_return_t apple_dext_config_read_block(io_connect_t connection, + uint64_t offset, + void *buf, + size_t len); + +/* + * Query BAR information from the PCI device. + * + * @bar: BAR index (0-5) + * @out_mem_idx: receives the memory index for MemoryRead/Write calls + * @out_size: receives the BAR size in bytes + * @out_type: receives the BAR type (mem32, mem64, io, etc.) + */ +kern_return_t apple_dext_get_bar_info(io_connect_t connection, + uint8_t bar, + uint8_t *out_mem_idx, + uint64_t *out_size, + uint8_t *out_type); + +/* + * Map a PCI BAR directly into this process through the dext. + * + * The dext supplies the BAR's IOMemoryDescriptor and IOKit applies the + * appropriate cache mode for the BAR type (default-cache for BAR0 style + * register windows, write-combine for prefetchable apertures). + * + * @bar: BAR index (0-5) + * @out_addr: receives the mapped virtual address + * @out_size: receives the mapped size + * @out_type: receives the BAR type (may be NULL) + */ +kern_return_t apple_dext_map_bar(io_connect_t connection, + uint8_t bar, + mach_vm_address_t *out_addr, + mach_vm_size_t *out_size, + uint8_t *out_type); + +/* + * Unmap a BAR previously mapped with apple_dext_map_bar(). + */ +kern_return_t apple_dext_unmap_bar(io_connect_t connection, + uint8_t bar, + mach_vm_address_t addr); + +/* + * Read from a PCI BAR (MMIO). + * + * @mem_idx: memory index from apple_dext_get_bar_info + * @offset: byte offset within the BAR + * @width: access width in bytes (1, 2, 4, or 8) + * @out_value: receives the value read + */ +kern_return_t apple_dext_mmio_read(io_connect_t connection, + uint8_t mem_idx, + uint64_t offset, + uint64_t width, + uint64_t *out_value); + +/* + * Write to a PCI BAR (MMIO). + * + * @mem_idx: memory index from apple_dext_get_bar_info + * @offset: byte offset within the BAR + * @width: access width in bytes (1, 2, 4, or 8) + * @value: value to write + */ +kern_return_t apple_dext_mmio_write(io_connect_t connection, + uint8_t mem_idx, + uint64_t offset, + uint64_t width, + uint64_t value); + +/* + * Set up interrupt forwarding for the PCI device. + * Creates IOInterruptDispatchSource handlers for all available + * MSI/MSI-X vectors in the dext. Interrupts are queued in a ring + * buffer and retrieved via apple_dext_check_interrupt(). + * + * @out_num_vectors: receives the number of interrupt vectors registered + */ +kern_return_t apple_dext_setup_interrupts(io_connect_t connection, + uint32_t *out_num_vectors); + +/* + * Reset the PCI device via the dext. Tries FLR first, then falls + * back to PM reset (D3hot =E2=86=92 D0 transition). + */ +kern_return_t apple_dext_reset_device(io_connect_t connection); + +/* + * Set the IRQ enable mask in the dext. Only vectors with their + * corresponding bit set will be recorded as pending when the + * hardware fires. mask[] is 4 x uint64_t covering 256 vectors. + */ +kern_return_t apple_dext_set_irq_mask(io_connect_t connection, + const uint64_t mask[4]); + +/* + * Read and clear all pending interrupt bits from the dext. + * Returns up to 256 bits (4 MSI/MSI-X vectors per bit) across + * 4 uint64_t words. Each bit that was set is atomically cleared + * in the dext. + */ +kern_return_t apple_dext_read_pending_irqs(io_connect_t connection, + uint64_t pending[4]); + +/* + * Opaque state for async interrupt notification from the dext. + */ +typedef struct AppleDextInterruptNotify AppleDextInterruptNotify; + +/* + * Create async interrupt notification. handler_fn is called on a GCD + * dispatch queue whenever the dext signals that one or more interrupt + * bits have been set. The handler should wake the QEMU main loop, + * which then calls apple_dext_read_pending_irqs() to drain the bits. + * + * The notification is armed immediately upon creation. + */ +AppleDextInterruptNotify * +apple_dext_interrupt_notify_create(io_connect_t connection, + void (*handler_fn)(void *opaque), + void *opaque); + +/* + * Re-arm the async interrupt notification after draining pending bits. + * Must be called after each wakeup to receive subsequent notifications. + */ +kern_return_t +apple_dext_interrupt_notify_rearm(AppleDextInterruptNotify *notify); + +/* + * Tear down and free async interrupt notification state. + */ +void apple_dext_interrupt_notify_destroy(AppleDextInterruptNotify *notify); + +#endif /* HW_VFIO_APPLE_DEXT_CLIENT_H */ diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 1ee9c11d5b..965c8e5b80 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -36,3 +36,10 @@ system_ss.add(when: 'CONFIG_IOMMUFD', if_false: files('i= ommufd-stubs.c')) system_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( 'display.c', )) + +# Apple VFIO backend +if host_os =3D=3D 'darwin' + system_ss.add(when: 'CONFIG_VFIO', + if_true: [files('apple-dext-client.c'), + coref, iokit]) +endif --=20 2.50.1 (Apple Git-155) From nobody Sat Apr 11 18:38:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1775374248; cv=none; d=zohomail.com; s=zohoarc; b=TYuwfkFLkgyMJaaSb4G+gCBVNsMXGsWubkYfluDO6xVx6f0Lk0L22LjO7j5HRrZXmZ0BEZkG+/eD8vfx4/Nm0Z/0WkLbs4pOrzUlQ91nkP79rLD7XnoMrEDP1qdQCjIAw8OGbyqzubQ/kf/rQ4caEOAjYA4ZZPHqqc/CAN1gGUU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775374248; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=6JbIKg+vYqclDU8Q8UgiO3lCc9fAOAsgjLkxdJlPCOA=; b=aXu/MQI+MEvniWw2b0xWjUKCFKlEolKqweMmcr6+XjSYhwACl6pZqqDu+kREjGkvxCG+SWr3g+Uf6W89u4piH+HJ/ThrM1t86QME4FCYZzc+zFXgGKSqJF+k2kaIMtG053YP2akDoQhNv07BR285Ir3a2oB3YWs7A2dVbXQYurY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1775374248120157.803918660225; Sun, 5 Apr 2026 00:30:48 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w9HvV-0003zV-4i; Sun, 05 Apr 2026 03:29:45 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w9HvT-0003yT-Sp for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:43 -0400 Received: from mail-dy1-x132f.google.com ([2607:f8b0:4864:20::132f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w9HvP-0007PI-C8 for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:43 -0400 Received: by mail-dy1-x132f.google.com with SMTP id 5a478bee46e88-2c7d8bbad06so7605378eec.1 for ; Sun, 05 Apr 2026 00:29:39 -0700 (PDT) Received: from localhost.localdomain ([2601:645:8200:47:41e4:ff2b:ff70:4d75]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2cb92ea0ef1sm7636502eec.21.2026.04.05.00.29.35 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 05 Apr 2026 00:29:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1775374178; x=1775978978; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=6JbIKg+vYqclDU8Q8UgiO3lCc9fAOAsgjLkxdJlPCOA=; b=OotdOmNBOSKBM5hFKLRKYLlvPEtVEKxaQZV6RfxxDaIOReouRM3v5Wzlsd8vEF4/FJ GhnN4tvjA0ckj9YYNQ4MLqrQRwguTaELgJZDXiEKKV1rjGDVTs7YddLglgMlHsZIZAUC BH4SEcownSN3vO8rwwkuT9MwcihNlAkO3pP8ofOKoLNT+UcFOA9z06eTdVM+GAnuxFdp DFiLniNn55ehp8fz9YULqAnDUeghPdoAmkPg756rriyOpfm9wtd9ZVph0CLWGmT05JS3 ijjPEBQRJZUaWi9lKslwhUEuT2dffpfivihCm4l32pkjDonMWHn5N1628aNRcsw0iLSv iZ7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775374178; x=1775978978; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=6JbIKg+vYqclDU8Q8UgiO3lCc9fAOAsgjLkxdJlPCOA=; b=OYpCdFJahLcrhNcQRGjVQGOUXABpWg36NWTq1Md+1iM0ZPeb+18teFJZv2ZVC9weMH PlLoQa8LwZXCeb8+xA6YtE3u5xztFh5inMmCFeOGUlVSG/O56VW7dWwkoZwGUHhoyJGU eDLfgmhHfjMv8gtdENxhqXHqjFGD1+39bhG53AVi2eaYx9lIxrlYMr8mOg/3KqjJt2Ic DRpbBOyuC7DOB2G3+ZdNh4Yfhfg74CQj/aLM4s025wB3GvPtdDiS/NEuTagXSBwZKqX6 wrLS0nUsdS3YqZDbQazDvk8vuRYxP6PTuf13+EpAy2rIOxs0LR8y9wFp+GnzYV3k+lu4 CYow== X-Gm-Message-State: AOJu0YxA2WyjhZaObQ0DKWHRkZC+104i6gMzwMEd5jtNqRDhUh6XMGyQ Hw9sVdHChrZSfFjpmTqUVWQQrK9yMQaA3o6LyJMXad/ZOVZNWsQECO2W7ee0PT1f39E= X-Gm-Gg: AeBDies4/ZAMW577F1khBPd8ABqKisDxmGnmDbmPgqWs2tuzItAZc3uTcyXXQb49Aof 2LaY1pMs1rkvIus1rkvBgZ/BecF/TAlu0xGPyZaOJCYqjBXYmVMTXCF4jn7fkWlrcaXjyuqRfCW xTuSpTKXQRXF5IkINCi9EciHBHYZIZE6GsHXpa5UMY9mTSlFUTioAyDSOL4Ob1bk6da4blSVCbY M6He2sP/DkAFi6/FwsI78Mv2F030fXQMmpMAyolyuTjS9rxY/7ij/ur9+9a5NcMHXwHK16wLCvQ FR7Cz8s79gus99lRjFCz9LoFMS9Iqyr5sOfsFz9BYXleyWV5LFvFbQxzL1MNQL2+798SfA+fzKK YNGJN77+AnALixRZU5o1L2lsCSVjMiZV9CUE0b6uHsWy3R1xexclU0cbjlYbhFaVkgI5Ay9c5uC fwrCUdoNvHawe2VgMEvR7E90Ru9IneNl1CVJg9TLtGfY01IA2r/0SeTkDW6cnqCRlfH9hKHnaDN 9rvHfElIy35ID+Tgd8rk0u5xpo= X-Received: by 2002:a05:7300:a506:b0:2c3:9981:e8ee with SMTP id 5a478bee46e88-2cbfa2d188cmr4200374eec.12.1775374177349; Sun, 05 Apr 2026 00:29:37 -0700 (PDT) From: "Scott J. Goldman" To: qemu-devel@nongnu.org Cc: alex@shazbot.org, clg@redhat.com, pbonzini@redhat.com, rbolshakov@ddn.com, phil@philjordan.eu, mst@redhat.com, john.levon@nutanix.com, thanos.makatos@nutanix.com, qemu-s390x@nongnu.org, "Scott J. Goldman" , "Scott J. Goldman" Subject: [RFC PATCH 08/10] vfio/apple: Add IOMMU container and PCI device Date: Sun, 5 Apr 2026 00:28:52 -0700 Message-ID: <20260405072857.66484-9-scottjgo@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260405072857.66484-1-scottjgo@gmail.com> References: <20260405072857.66484-1-scottjgo@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::132f; envelope-from=scottjgo@gmail.com; helo=mail-dy1-x132f.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1775374249675154100 From: "Scott J. Goldman" Add the core Apple VFIO backend: an IOMMU container type and a PCI device type that together provide VFIO passthrough on macOS via the VFIOUserPCIDriver DriverKit extension. AppleVFIOContainer (container-apple.c): Implements VFIOIOMMUClass for DMA map/unmap by calling through to the dext client's register/unregister_dma functions. Synthesizes VFIO_DEVICE_GET_INFO responses with appropriate flags for the passthrough device. VFIOApplePCIDevice (apple-device.c): QOM type "vfio-apple-pci" subclassing VFIOPCIDevice. Provides the full VFIODeviceIOOps implementation: - Config space reads forwarded to the dext; writes filtered to block BAR and status register reprogramming (macOS/DART owns those). - PCI COMMAND register writes forwarded for bus-master/memory-space enable. - BAR regions directly mapped via IOConnectMapMemory64 and accessed as host MMIO loads/stores. - MSI/MSI-X interrupt delivery through a bitmap-poll model with async GCD notification bridged to an EventNotifier. - Device reset via the dext (IOPCIDevice::Reset FLR/hot-reset). - Shared dext connection management for multi-function devices. apple.h defines the shared types: AppleVFIOContainer, AppleVFIOState, AppleVFIOBarMap, and VFIOApplePCIDevice. Signed-off-by: Scott J. Goldman --- hw/vfio/apple-device.c | 945 ++++++++++++++++++++++++++++++++++++++ hw/vfio/apple.h | 74 +++ hw/vfio/container-apple.c | 241 ++++++++++ hw/vfio/meson.build | 6 +- 4 files changed, 1264 insertions(+), 2 deletions(-) create mode 100644 hw/vfio/apple-device.c create mode 100644 hw/vfio/apple.h create mode 100644 hw/vfio/container-apple.c diff --git a/hw/vfio/apple-device.c b/hw/vfio/apple-device.c new file mode 100644 index 0000000000..9291ac845b --- /dev/null +++ b/hw/vfio/apple-device.c @@ -0,0 +1,945 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Apple/macOS VFIO PCI device passthrough via DriverKit dext. + * + * Copyright (c) 2026 Scott J. Goldman + */ + +#include "qemu/osdep.h" + +#include +#include +#include + +#include + +#include "apple-dext-client.h" +#include "hw/vfio/apple.h" +#include "hw/vfio/vfio-container.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/host-pci-mmio.h" +#include "qemu/main-loop.h" + +typedef struct AppleVFIOSharedDext { + io_connect_t conn; + uint32_t refs; +} AppleVFIOSharedDext; + +typedef struct AppleVFIODMAProbe { + uint64_t managed_bdf; + uint64_t host_bus; + uint64_t host_device; + uint64_t host_function; + DeviceState *match; +} AppleVFIODMAProbe; + +static GHashTable *apple_vfio_shared_dexts; + +static inline guint apple_vfio_dext_key(uint8_t bus, uint8_t device, + uint8_t function) +{ + return ((guint)bus << 16) | ((guint)device << 8) | function; +} + +static inline AppleVFIOContainer *apple_vfio_container(VFIODevice *vbasede= v) +{ + return VFIO_IOMMU_APPLE(vbasedev->bcontainer); +} + +static inline io_connect_t apple_vfio_connection(VFIODevice *vbasedev) +{ + AppleVFIOContainer *container =3D apple_vfio_container(vbasedev); + + return container ? container->dext_conn : IO_OBJECT_NULL; +} + +static void apple_vfio_find_dma_companion_cb(PCIBus *bus, PCIDevice *pdev, + void *opaque) +{ + AppleVFIODMAProbe *probe =3D opaque; + Error *err =3D NULL; + uint64_t managed_bdf; + uint64_t host_bus; + uint64_t host_device; + uint64_t host_function; + + if (probe->match || + !object_dynamic_cast(OBJECT(pdev), "apple-dma-pci")) { + return; + } + + managed_bdf =3D object_property_get_uint(OBJECT(pdev), "managed-bdf", = &err); + if (err) { + error_free(err); + return; + } + + host_bus =3D object_property_get_uint(OBJECT(pdev), "x-apple-host-bus"= , &err); + if (err) { + error_free(err); + return; + } + + host_device =3D object_property_get_uint(OBJECT(pdev), + "x-apple-host-device", &err); + if (err) { + error_free(err); + return; + } + + host_function =3D object_property_get_uint(OBJECT(pdev), + "x-apple-host-function", &err= ); + if (err) { + error_free(err); + return; + } + + if (managed_bdf =3D=3D probe->managed_bdf && + host_bus =3D=3D probe->host_bus && + host_device =3D=3D probe->host_device && + host_function =3D=3D probe->host_function) { + probe->match =3D DEVICE(pdev); + } +} + +static DeviceState *apple_vfio_find_dma_companion(VFIOApplePCIDevice *adev) +{ + VFIOPCIDevice *vdev =3D VFIO_PCI_DEVICE(adev); + PCIDevice *pdev =3D PCI_DEVICE(vdev); + AppleVFIODMAProbe probe =3D { + .managed_bdf =3D PCI_BUILD_BDF(pci_dev_bus_num(pdev), pdev->devfn), + .host_bus =3D vdev->host.bus, + .host_device =3D vdev->host.slot, + .host_function =3D vdev->host.function, + }; + + pci_for_each_device_under_bus(pci_device_root_bus(pdev), + apple_vfio_find_dma_companion_cb, &probe= ); + return probe.match; +} + +static void apple_vfio_signal_irqfd(int fd) +{ + static const uint64_t value =3D 1; + ssize_t ret; + + if (fd < 0) { + return; + } + + do { + ret =3D write(fd, &value, sizeof(value)); + } while (ret < 0 && errno =3D=3D EINTR); +} + +static void apple_vfio_deliver_irq(VFIOPCIDevice *vdev, uint32_t vector) +{ + switch (vdev->interrupt) { + case VFIO_INT_MSI: + case VFIO_INT_MSIX: + if (vector < vdev->nr_vectors && vdev->msi_vectors[vector].use) { + apple_vfio_signal_irqfd( + event_notifier_get_wfd(&vdev->msi_vectors[vector].interrup= t)); + } + break; + case VFIO_INT_INTx: + apple_vfio_signal_irqfd( + event_notifier_get_wfd(&vdev->intx.interrupt)); + break; + default: + break; + } +} + +/* + * Called on a GCD dispatch queue when the dext signals pending interrupts. + * Just pokes the EventNotifier to wake the QEMU main loop. + */ +static void apple_vfio_irq_wakeup(void *opaque) +{ + VFIOApplePCIDevice *adev =3D opaque; + + event_notifier_set(&adev->apple->irq_notifier); +} + +/* + * QEMU main-loop fd handler: drain the pending-interrupt bitfield from + * the dext, deliver each flagged vector, then re-arm the async wait. + */ +static void apple_vfio_irq_handler(void *opaque) +{ + VFIOApplePCIDevice *adev =3D opaque; + VFIOPCIDevice *vdev =3D VFIO_PCI_DEVICE(adev); + VFIODevice *vbasedev =3D &vdev->vbasedev; + io_connect_t conn =3D apple_vfio_connection(vbasedev); + AppleVFIOState *apple =3D adev->apple; + uint64_t pending[4]; + int word; + + if (!event_notifier_test_and_clear(&apple->irq_notifier)) { + return; + } + + if (conn =3D=3D IO_OBJECT_NULL) { + return; + } + + if (apple_dext_read_pending_irqs(conn, pending) !=3D KERN_SUCCESS) { + apple_dext_interrupt_notify_rearm(apple->irq_notify); + return; + } + + for (word =3D 0; word < 4; word++) { + uint64_t bits =3D pending[word]; + + while (bits) { + int bit =3D __builtin_ctzll(bits); + uint32_t vector =3D word * 64 + bit; + + apple_vfio_deliver_irq(vdev, vector); + bits &=3D bits - 1; + } + } + + apple_dext_interrupt_notify_rearm(apple->irq_notify); +} + +bool apple_vfio_get_bar_info(VFIOApplePCIDevice *adev, uint8_t bar, + uint8_t *mem_idx, uint64_t *size, + uint8_t *type) +{ + io_connect_t conn =3D apple_vfio_connection(&VFIO_PCI_DEVICE(adev)->vb= asedev); + + if (conn !=3D IO_OBJECT_NULL) { + return apple_dext_get_bar_info(conn, bar, mem_idx, size, type) =3D= =3D + KERN_SUCCESS; + } + + if (mem_idx) { + *mem_idx =3D 0; + } + if (size) { + *size =3D 0; + } + if (type) { + *type =3D 0; + } + return false; +} + +static void apple_vfio_pci_init(VFIOApplePCIDevice *adev) +{ + VFIOPCIDevice *vdev =3D VFIO_PCI_DEVICE(adev); + + /* + * On macOS, HVF can only map on 16kb page boundaries, so these quirk + * fixes end up breaking things. Likewise the performance enhancements + * there rely on kvm-specific features. Disable for now, but we should + * revisit this. + */ + vdev->no_bar_quirks =3D true; +} + +static bool apple_vfio_pci_pre_realize(VFIOApplePCIDevice *adev, Error **e= rrp) +{ + VFIOPCIDevice *vdev =3D VFIO_PCI_DEVICE(adev); + VFIODevice *vbasedev =3D &vdev->vbasedev; + + adev->apple =3D g_new0(AppleVFIOState, 1); + + if (!vbasedev->name) { + vbasedev->name =3D g_strdup_printf("apple-%04x:%02x:%02x.%x", + vdev->host.domain, + vdev->host.bus, + vdev->host.slot, + vdev->host.function); + } + + return true; +} + +static bool apple_vfio_create_dma_companion(VFIOApplePCIDevice *adev, + Error **errp) +{ + VFIOPCIDevice *vdev =3D VFIO_PCI_DEVICE(adev); + PCIDevice *pdev =3D PCI_DEVICE(vdev); + DeviceState *dev; + + if (adev->dma_companion_autocreated && adev->dma_companion) { + return true; + } + + if (apple_vfio_find_dma_companion(adev) !=3D NULL) { + return true; + } + + dev =3D qdev_new("apple-dma-pci"); + if (!object_property_set_uint(OBJECT(dev), "managed-bdf", + PCI_BUILD_BDF(pci_dev_bus_num(pdev), + pdev->devfn), errp) || + !object_property_set_uint(OBJECT(dev), "x-apple-host-bus", + vdev->host.bus, errp) || + !object_property_set_uint(OBJECT(dev), "x-apple-host-device", + vdev->host.slot, errp) || + !object_property_set_uint(OBJECT(dev), "x-apple-host-function", + vdev->host.function, errp)) { + object_unref(OBJECT(dev)); + return false; + } + + if (!qdev_realize(dev, BUS(pci_get_bus(pdev)), errp)) { + object_unref(OBJECT(dev)); + return false; + } + + adev->dma_companion =3D dev; + adev->dma_companion_autocreated =3D true; + object_unref(OBJECT(dev)); + return true; +} + +static void apple_vfio_destroy_dma_companion(VFIOApplePCIDevice *adev) +{ + if (!adev->dma_companion_autocreated || adev->dma_companion =3D=3D NUL= L) { + return; + } + + object_unparent(OBJECT(adev->dma_companion)); + adev->dma_companion =3D NULL; + adev->dma_companion_autocreated =3D false; +} + +bool apple_vfio_device_setup(VFIOApplePCIDevice *adev, Error **errp) +{ + VFIODevice *vbasedev =3D &VFIO_PCI_DEVICE(adev)->vbasedev; + io_connect_t conn =3D apple_vfio_connection(vbasedev); + uint32_t num_vectors =3D 0; + kern_return_t kr; + + if (conn =3D=3D IO_OBJECT_NULL) { + error_setg(errp, "vfio-apple: missing dext connection"); + return false; + } + + kr =3D apple_dext_setup_interrupts(conn, &num_vectors); + if (kr !=3D KERN_SUCCESS) { + error_setg(errp, "vfio-apple: failed to setup interrupts (kr=3D0x%= x)", + kr); + return false; + } + + adev->apple->num_irq_vectors =3D num_vectors; + + if (event_notifier_init(&adev->apple->irq_notifier, false) < 0) { + error_setg(errp, "vfio-apple: failed to create IRQ event notifier"= ); + return false; + } + + qemu_set_fd_handler(event_notifier_get_fd(&adev->apple->irq_notifier), + apple_vfio_irq_handler, NULL, adev); + + adev->apple->irq_notify =3D + apple_dext_interrupt_notify_create(conn, apple_vfio_irq_wakeup, ad= ev); + if (!adev->apple->irq_notify) { + error_setg(errp, + "vfio-apple: failed to create IRQ async notification"); + qemu_set_fd_handler( + event_notifier_get_fd(&adev->apple->irq_notifier), + NULL, NULL, NULL); + event_notifier_cleanup(&adev->apple->irq_notifier); + return false; + } + + return true; +} + +void apple_vfio_device_cleanup(VFIOApplePCIDevice *adev) +{ + AppleVFIOState *apple =3D adev->apple; + + if (!apple) { + return; + } + + if (apple->irq_notify) { + apple_dext_interrupt_notify_destroy(apple->irq_notify); + apple->irq_notify =3D NULL; + + qemu_set_fd_handler(event_notifier_get_fd(&apple->irq_notifier), + NULL, NULL, NULL); + event_notifier_cleanup(&apple->irq_notifier); + } +} + +static int apple_vfio_device_feature(VFIODevice *vdev, + struct vfio_device_feature *feat) +{ + return -ENOTTY; +} + +static int apple_vfio_device_reset(VFIODevice *vbasedev) +{ + io_connect_t conn =3D apple_vfio_connection(vbasedev); + + if (conn =3D=3D IO_OBJECT_NULL) { + return -ENODEV; + } + + return apple_dext_reset_device(conn) =3D=3D KERN_SUCCESS ? 0 : -EIO; +} + +static int apple_vfio_get_region_info(VFIODevice *vbasedev, + struct vfio_region_info *info, + int *fd) +{ + VFIOApplePCIDevice *adev =3D VFIO_APPLE_PCI(vbasedev->dev); + uint32_t index =3D info->index; + uint64_t size =3D 0; + + if (fd) { + *fd =3D -1; + } + + memset((char *)info + offsetof(struct vfio_region_info, flags), 0, + sizeof(*info) - offsetof(struct vfio_region_info, flags)); + + info->index =3D index; + info->flags =3D VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRI= TE; + info->offset =3D (uint64_t)index << 20; + + switch (info->index) { + case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: + if (!apple_vfio_get_bar_info(adev, info->index, NULL, &size, NULL)= ) { + size =3D 0; + } + info->size =3D size; + info->flags |=3D VFIO_REGION_INFO_FLAG_MMAP; + break; + case VFIO_PCI_CONFIG_REGION_INDEX: + info->size =3D PCIE_CONFIG_SPACE_SIZE; + break; + case VFIO_PCI_ROM_REGION_INDEX: + case VFIO_PCI_VGA_REGION_INDEX: + info->size =3D 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int apple_vfio_get_irq_info(VFIODevice *vbasedev, + struct vfio_irq_info *info) +{ + VFIOApplePCIDevice *adev =3D VFIO_APPLE_PCI(vbasedev->dev); + VFIOPCIDevice *vdev =3D VFIO_PCI_DEVICE(adev); + + switch (info->index) { + case VFIO_PCI_MSI_IRQ_INDEX: + info->flags =3D VFIO_IRQ_INFO_EVENTFD; + info->count =3D adev->apple->num_irq_vectors; + break; + case VFIO_PCI_MSIX_IRQ_INDEX: + info->flags =3D VFIO_IRQ_INFO_EVENTFD | VFIO_IRQ_INFO_NORESIZE; + info->count =3D vdev->msix ? vdev->msix->entries : 0; + break; + case VFIO_PCI_INTX_IRQ_INDEX: + info->flags =3D VFIO_IRQ_INFO_EVENTFD; + info->count =3D 1; + break; + case VFIO_PCI_ERR_IRQ_INDEX: + case VFIO_PCI_REQ_IRQ_INDEX: + /* + * Apple dext passthrough has no kernel-side AER or device-request + * notification currently; return count 0 to tell the core to skip + * these. + */ + info->flags =3D 0; + info->count =3D 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void apple_vfio_update_irq_mask(VFIODevice *vbasedev) +{ + VFIOApplePCIDevice *adev =3D VFIO_APPLE_PCI(vbasedev->dev); + VFIOPCIDevice *vdev =3D VFIO_PCI_DEVICE(adev); + io_connect_t conn =3D apple_vfio_connection(vbasedev); + uint64_t mask[4] =3D {0}; + uint32_t i; + + if (conn =3D=3D IO_OBJECT_NULL) { + return; + } + + switch (vdev->interrupt) { + case VFIO_INT_MSI: + case VFIO_INT_MSIX: + for (i =3D 0; i < vdev->nr_vectors; i++) { + if (vdev->msi_vectors[i].use) { + mask[i / 64] |=3D 1ULL << (i % 64); + } + } + break; + case VFIO_INT_INTx: + mask[0] =3D 1; + break; + default: + break; + } + + apple_dext_set_irq_mask(conn, mask); +} + +static int apple_vfio_set_irqs(VFIODevice *vbasedev, struct vfio_irq_set *= irq) +{ + apple_vfio_update_irq_mask(vbasedev); + return 0; +} + +static int apple_vfio_bar_read(VFIODevice *vbasedev, uint8_t nr, off_t off, + uint32_t size, void *data) +{ + VFIOApplePCIDevice *adev =3D VFIO_APPLE_PCI(vbasedev->dev); + AppleVFIOBarMap *bm =3D &adev->apple->bar_maps[nr]; + const void *p; + uint64_t value; + + if (!bm->addr || off + size > bm->size) { + error_report("vfio-apple: BAR%d read out of range or unmapped", nr= ); + return -EINVAL; + } + + if (size !=3D 1 && size !=3D 2 && size !=3D 4 && size !=3D 8) { + return -EINVAL; + } + + p =3D (const char *)bm->addr + off; + value =3D host_pci_ldn_le_p(p, size); + memcpy(data, &value, size); + + return size; +} + +static int apple_vfio_region_read(VFIODevice *vbasedev, uint8_t nr, off_t = off, + uint32_t size, void *data) +{ + io_connect_t conn =3D apple_vfio_connection(vbasedev); + kern_return_t kr; + uint32_t legacy_size =3D 0; + + if (nr !=3D VFIO_PCI_CONFIG_REGION_INDEX) { + return apple_vfio_bar_read(vbasedev, nr, off, size, data); + } + + if (conn =3D=3D IO_OBJECT_NULL) { + return -ENODEV; + } + + legacy_size =3D MIN(size, PCIE_CONFIG_SPACE_SIZE - off); + + if (legacy_size =3D=3D 1 || legacy_size =3D=3D 2 || legacy_size =3D=3D= 4) { + uint64_t value =3D 0; + + kr =3D apple_dext_config_read(conn, off, legacy_size, &value); + if (kr !=3D KERN_SUCCESS) { + return -EIO; + } + + memcpy(data, &value, legacy_size); + if (legacy_size < size) { + memset((uint8_t *)data + legacy_size, 0, size - legacy_size); + } + return size; + } + + kr =3D apple_dext_config_read_block(conn, off, data, legacy_size); + if (kr !=3D KERN_SUCCESS) { + return -EIO; + } + if (legacy_size < size) { + memset((uint8_t *)data + legacy_size, 0, size - legacy_size); + } + return size; +} + +static bool apple_vfio_config_write_is_safe(off_t off, uint32_t size) +{ + off_t end =3D off + size; + + /* + * Block writes that would reprogram the device's bus identity or + * address decoders. macOS / DART owns those registers; touching + * them from the guest breaks the IOKit mapping and the device + * "falls off the bus." + * + * Everything else (vendor capabilities, MSI/MSI-X, PCIe cap, etc.) + * is forwarded. + */ + + /* PCI_STATUS stays emulated/blocked */ + if (off < PCI_STATUS + 2 && end > PCI_STATUS) { + return false; + } + + /* BAR0-BAR5 */ + if (off < PCI_BASE_ADDRESS_5 + 4 && end > PCI_BASE_ADDRESS_0) { + return false; + } + + return true; +} + +static int apple_vfio_forward_command_write(io_connect_t conn, off_t off, + uint32_t size, const void *dat= a) +{ + const uint8_t *bytes =3D data; + off_t end =3D off + size; + off_t cmd_start =3D MAX(off, (off_t)PCI_COMMAND); + off_t cmd_end =3D MIN(end, (off_t)(PCI_COMMAND + 2)); + off_t pos; + + if (conn =3D=3D IO_OBJECT_NULL) { + return -ENODEV; + } + + for (pos =3D cmd_start; pos < cmd_end; pos++) { + uint64_t value =3D bytes[pos - off]; + kern_return_t kr =3D apple_dext_config_write(conn, pos, 1, value); + + if (kr !=3D KERN_SUCCESS) { + return -EIO; + } + } + + return 0; +} + +static int apple_vfio_bar_write(VFIODevice *vbasedev, uint8_t nr, off_t of= f, + uint32_t size, void *data) +{ + VFIOApplePCIDevice *adev =3D VFIO_APPLE_PCI(vbasedev->dev); + AppleVFIOBarMap *bm =3D &adev->apple->bar_maps[nr]; + void *p; + uint64_t value =3D 0; + + if (!bm->addr || off + size > bm->size) { + error_report("vfio-apple: BAR%d write out of range or unmapped", n= r); + return -EINVAL; + } + + if (size !=3D 1 && size !=3D 2 && size !=3D 4 && size !=3D 8) { + return -EINVAL; + } + + p =3D (char *)bm->addr + off; + memcpy(&value, data, size); + host_pci_stn_le_p(p, size, value); + + return size; +} + +static int apple_vfio_region_write(VFIODevice *vbasedev, uint8_t nr, off_t= off, + uint32_t size, void *data, bool post) +{ + io_connect_t conn =3D apple_vfio_connection(vbasedev); + uint64_t value =3D 0; + kern_return_t kr; + uint32_t legacy_size; + + if (nr !=3D VFIO_PCI_CONFIG_REGION_INDEX) { + return apple_vfio_bar_write(vbasedev, nr, off, size, data); + } + + if (off < PCI_COMMAND + 2 && off + size > PCI_COMMAND) { + int ret =3D apple_vfio_forward_command_write(conn, off, size, data= ); + + if (ret) { + return ret; + } + + if (off >=3D PCI_COMMAND && off + size <=3D PCI_COMMAND + 2) { + return size; + } + } + + if (!apple_vfio_config_write_is_safe(off, size)) { + return size; + } + + if (conn =3D=3D IO_OBJECT_NULL) { + return -ENODEV; + } + + memcpy(&value, data, size); + legacy_size =3D MIN(size, PCIE_CONFIG_SPACE_SIZE - off); + if (!(legacy_size =3D=3D 1 || legacy_size =3D=3D 2 || legacy_size =3D= =3D 4)) { + return -EINVAL; + } + + kr =3D apple_dext_config_write(conn, off, legacy_size, value); + if (kr !=3D KERN_SUCCESS) { + return -EIO; + } + return size; +} + +static int apple_vfio_region_map(VFIODevice *vbasedev, VFIORegion *region); +static void apple_vfio_region_unmap(VFIODevice *vbasedev, VFIORegion *regi= on); + +static int apple_vfio_region_map(VFIODevice *vbasedev, VFIORegion *region) +{ + VFIOApplePCIDevice *adev =3D VFIO_APPLE_PCI(vbasedev->dev); + VFIOPCIDevice *vdev =3D VFIO_PCI_DEVICE(adev); + io_connect_t conn =3D apple_vfio_connection(vbasedev); + int bar =3D region->nr; + VFIOBAR *vbar; + mach_vm_address_t local_addr =3D 0; + mach_vm_size_t bar_size =3D 0; + uint8_t bar_type =3D 0; + kern_return_t kr; + int i; + + if (bar < VFIO_PCI_BAR0_REGION_INDEX || bar >=3D VFIO_PCI_ROM_REGION_I= NDEX) { + return 0; + } + + vbar =3D &vdev->bars[bar]; + + if (conn =3D=3D IO_OBJECT_NULL || !vbar->size || vbar->ioport) { + return 0; + } + + if (bar > 0 && vdev->bars[bar - 1].mem64) { + return 0; + } + + if (adev->apple->bar_maps[bar].addr !=3D NULL) { + return 0; + } + + kr =3D apple_dext_map_bar(conn, bar, &local_addr, &bar_size, &bar_type= ); + if (kr !=3D KERN_SUCCESS) { + warn_report("vfio-apple: BAR%d map failed for %s: 0x%x", + bar, vbasedev->name, kr); + return -EIO; + } + + if (bar_size > vbar->size) { + bar_size =3D vbar->size; + } + + adev->apple->bar_maps[bar].addr =3D (void *)local_addr; + adev->apple->bar_maps[bar].size =3D bar_size; + + /* + * Use the pre-computed mmap regions =E2=80=94 already split around th= e MSI-X + * table/PBA hole by vfio_pci_fixup_msix_region() during realize. + * We just need to fill in the host pointers from our dext mapping. + */ + for (i =3D 0; i < region->nr_mmaps; i++) { + region->mmaps[i].mmap =3D (char *)local_addr + region->mmaps[i].of= fset; + vfio_region_register_mmap(region, i); + } + + return 0; +} + +static void apple_vfio_region_unmap(VFIODevice *vbasedev, VFIORegion *regi= on) +{ + VFIOApplePCIDevice *adev =3D VFIO_APPLE_PCI(vbasedev->dev); + io_connect_t conn =3D apple_vfio_connection(vbasedev); + int bar =3D region->nr; + AppleVFIOBarMap *bm; + int i; + + if (bar < VFIO_PCI_BAR0_REGION_INDEX || bar >=3D VFIO_PCI_ROM_REGION_I= NDEX) { + return; + } + + bm =3D &adev->apple->bar_maps[bar]; + + for (i =3D 0; i < region->nr_mmaps; i++) { + if (region->mmaps[i].mmap) { + vfio_region_unregister_mmap(region, i); + region->mmaps[i].mmap =3D NULL; + } + } + + if (conn !=3D IO_OBJECT_NULL && bm->addr !=3D NULL) { + apple_dext_unmap_bar(conn, bar, (mach_vm_address_t)bm->addr); + } + + bm->addr =3D NULL; + bm->size =3D 0; +} + +VFIODeviceIOOps apple_vfio_device_io_ops =3D { + .device_feature =3D apple_vfio_device_feature, + .get_region_info =3D apple_vfio_get_region_info, + .get_irq_info =3D apple_vfio_get_irq_info, + .set_irqs =3D apple_vfio_set_irqs, + .device_reset =3D apple_vfio_device_reset, + .region_read =3D apple_vfio_region_read, + .region_write =3D apple_vfio_region_write, + .region_map =3D apple_vfio_region_map, + .region_unmap =3D apple_vfio_region_unmap, +}; + +bool apple_vfio_dext_publish(uint8_t bus, uint8_t device, uint8_t function, + io_connect_t conn) +{ + AppleVFIOSharedDext *shared; + guint key =3D apple_vfio_dext_key(bus, device, function); + + if (!apple_vfio_shared_dexts) { + apple_vfio_shared_dexts =3D + g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_f= ree); + } + + if (g_hash_table_lookup(apple_vfio_shared_dexts, GUINT_TO_POINTER(key)= )) { + return false; + } + + shared =3D g_new0(AppleVFIOSharedDext, 1); + shared->conn =3D conn; + shared->refs =3D 1; + g_hash_table_insert(apple_vfio_shared_dexts, GUINT_TO_POINTER(key), sh= ared); + return true; +} + +io_connect_t apple_vfio_dext_lookup(uint8_t bus, uint8_t device, + uint8_t function) +{ + AppleVFIOSharedDext *shared; + guint key =3D apple_vfio_dext_key(bus, device, function); + + if (!apple_vfio_shared_dexts) { + return IO_OBJECT_NULL; + } + + shared =3D g_hash_table_lookup(apple_vfio_shared_dexts, + GUINT_TO_POINTER(key)); + if (!shared) { + return IO_OBJECT_NULL; + } + + shared->refs++; + return shared->conn; +} + +void apple_vfio_dext_release(uint8_t bus, uint8_t device, uint8_t function, + io_connect_t conn) +{ + AppleVFIOSharedDext *shared; + guint key =3D apple_vfio_dext_key(bus, device, function); + + if (!apple_vfio_shared_dexts) { + return; + } + + shared =3D g_hash_table_lookup(apple_vfio_shared_dexts, + GUINT_TO_POINTER(key)); + if (!shared || shared->conn !=3D conn) { + return; + } + + if (--shared->refs =3D=3D 0) { + apple_dext_disconnect(conn); + g_hash_table_remove(apple_vfio_shared_dexts, GUINT_TO_POINTER(key)= ); + } +} + +/* ------------------------------------------------------------------ */ +/* QOM type: vfio-apple-pci */ +/* ------------------------------------------------------------------ */ + +static void (*parent_realize)(PCIDevice *, Error **); +static void (*parent_exit)(PCIDevice *); + +static void apple_vfio_pci_instance_init(Object *obj) +{ + VFIOApplePCIDevice *adev =3D VFIO_APPLE_PCI(obj); + + apple_vfio_pci_init(adev); +} + +static void apple_vfio_pci_realize_fn(PCIDevice *pdev, Error **errp) +{ + ERRP_GUARD(); + VFIOApplePCIDevice *adev =3D VFIO_APPLE_PCI(pdev); + + if (!apple_vfio_pci_pre_realize(adev, errp)) { + return; + } + + parent_realize(pdev, errp); + if (*errp) { + g_clear_pointer(&adev->apple, g_free); + return; + } + + if (!apple_vfio_create_dma_companion(adev, errp)) { + if (parent_exit) { + parent_exit(pdev); + } + g_clear_pointer(&adev->apple, g_free); + return; + } +} + +static void apple_vfio_pci_exit_fn(PCIDevice *pdev) +{ + VFIOApplePCIDevice *adev =3D VFIO_APPLE_PCI(pdev); + + apple_vfio_destroy_dma_companion(adev); + + if (parent_exit) { + parent_exit(pdev); + } +} + +static void apple_vfio_pci_finalize_fn(Object *obj) +{ + VFIOApplePCIDevice *adev =3D VFIO_APPLE_PCI(obj); + + apple_vfio_device_cleanup(adev); + g_clear_pointer(&adev->apple, g_free); +} + +static void apple_vfio_pci_class_init(ObjectClass *klass, const void *data) +{ + PCIDeviceClass *pdc =3D PCI_DEVICE_CLASS(klass); + DeviceClass *dc =3D DEVICE_CLASS(klass); + + parent_realize =3D pdc->realize; + parent_exit =3D pdc->exit; + + pdc->realize =3D apple_vfio_pci_realize_fn; + pdc->exit =3D apple_vfio_pci_exit_fn; + dc->user_creatable =3D true; + dc->desc =3D "VFIO-based PCI device assignment (Apple/macOS)"; +} + +static const TypeInfo vfio_apple_pci_info =3D { + .name =3D TYPE_VFIO_APPLE_PCI, + .parent =3D TYPE_VFIO_PCI, + .instance_size =3D sizeof(VFIOApplePCIDevice), + .instance_init =3D apple_vfio_pci_instance_init, + .instance_finalize =3D apple_vfio_pci_finalize_fn, + .class_init =3D apple_vfio_pci_class_init, +}; + +static void register_vfio_apple_pci_type(void) +{ + type_register_static(&vfio_apple_pci_info); +} + +type_init(register_vfio_apple_pci_type) diff --git a/hw/vfio/apple.h b/hw/vfio/apple.h new file mode 100644 index 0000000000..81d4bd2b66 --- /dev/null +++ b/hw/vfio/apple.h @@ -0,0 +1,74 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Apple/macOS VFIO passthrough common definitions. + * + * Copyright (c) 2026 Scott J. Goldman + */ + +#ifndef HW_VFIO_APPLE_H +#define HW_VFIO_APPLE_H + +#include + +#include "hw/vfio/pci.h" +#include "hw/vfio/vfio-container.h" +#include "qapi/error.h" +#include "qemu/event_notifier.h" + +#ifdef CONFIG_DARWIN +#include +#else +typedef uintptr_t io_connect_t; +#define IO_OBJECT_NULL ((io_connect_t)0) +#endif + +OBJECT_DECLARE_SIMPLE_TYPE(AppleVFIOContainer, VFIO_IOMMU_APPLE) + +struct AppleVFIOContainer { + VFIOContainer parent_obj; + io_connect_t dext_conn; + uint8_t host_bus; + uint8_t host_device; + uint8_t host_function; +}; + +typedef struct AppleDextInterruptNotify AppleDextInterruptNotify; + +typedef struct AppleVFIOBarMap { + void *addr; + size_t size; +} AppleVFIOBarMap; + +typedef struct AppleVFIOState { + AppleDextInterruptNotify *irq_notify; + EventNotifier irq_notifier; + uint32_t num_irq_vectors; + AppleVFIOBarMap bar_maps[PCI_ROM_SLOT]; +} AppleVFIOState; + +OBJECT_DECLARE_SIMPLE_TYPE(VFIOApplePCIDevice, VFIO_APPLE_PCI) + +struct VFIOApplePCIDevice { + VFIOPCIDevice parent_obj; + AppleVFIOState *apple; + DeviceState *dma_companion; + bool dma_companion_autocreated; +}; + +extern VFIODeviceIOOps apple_vfio_device_io_ops; + +bool apple_vfio_device_setup(VFIOApplePCIDevice *adev, Error **errp); +void apple_vfio_device_cleanup(VFIOApplePCIDevice *adev); +bool apple_vfio_get_bar_info(VFIOApplePCIDevice *adev, uint8_t bar, + uint8_t *mem_idx, uint64_t *size, + uint8_t *type); + +bool apple_vfio_dext_publish(uint8_t bus, uint8_t device, uint8_t function, + io_connect_t conn); +io_connect_t apple_vfio_dext_lookup(uint8_t bus, uint8_t device, + uint8_t function); +void apple_vfio_dext_release(uint8_t bus, uint8_t device, uint8_t function, + io_connect_t conn); + +#endif /* HW_VFIO_APPLE_H */ diff --git a/hw/vfio/container-apple.c b/hw/vfio/container-apple.c new file mode 100644 index 0000000000..5a5c55b622 --- /dev/null +++ b/hw/vfio/container-apple.c @@ -0,0 +1,241 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Apple/macOS VFIO IOMMU container backend. + * + * Copyright (c) 2026 Scott J. Goldman + */ + +#include "qemu/osdep.h" + +#include + +#include "apple-dext-client.h" +#include "hw/vfio/apple.h" +#include "hw/vfio/vfio-device.h" +#include "hw/vfio/vfio-listener.h" +#include "qapi/error.h" +#include "system/ramblock.h" + +static bool apple_vfio_setup(VFIOContainer *bcontainer, Error **errp) +{ + bcontainer->pgsizes =3D qemu_real_host_page_size(); + bcontainer->dma_max_mappings =3D UINT_MAX; + bcontainer->dirty_pages_supported =3D false; + bcontainer->max_dirty_bitmap_size =3D 0; + bcontainer->dirty_pgsizes =3D 0; + return true; +} + +/* + * DMA map/unmap are no-ops: Apple passthrough handles DMA mapping through + * the companion apple-dma-pci device which talks to the dext directly, + * bypassing the IOMMU container's DMA path. The stubs are required becau= se + * the VFIO listener asserts they are non-NULL. + */ +static int apple_vfio_dma_map(const VFIOContainer *bcontainer, hwaddr iova, + uint64_t size, void *vaddr, bool readonly, + MemoryRegion *mr) +{ + return 0; +} + +static int apple_vfio_dma_unmap(const VFIOContainer *bcontainer, hwaddr io= va, + uint64_t size, IOMMUTLBEntry *iotlb, + bool unmap_all) +{ + return 0; +} + +static int apple_vfio_set_dirty_page_tracking(const VFIOContainer *bcontai= ner, + bool start, Error **errp) +{ + error_setg_errno(errp, ENOTSUP, "vfio-apple does not support migration= "); + return -ENOTSUP; +} + +static int apple_vfio_query_dirty_bitmap(const VFIOContainer *bcontainer, + VFIOBitmap *vbmap, hwaddr iova, + hwaddr size, uint64_t backend_fla= g, + Error **errp) +{ + error_setg_errno(errp, ENOTSUP, "vfio-apple does not support migration= "); + return -ENOTSUP; +} + +static int apple_vfio_pci_hot_reset(VFIODevice *vbasedev, bool single) +{ + return 0; +} + +static AppleVFIOContainer *apple_vfio_container_connect(AddressSpace *as, + VFIODevice *vbased= ev, + Error **errp) +{ + VFIOPCIDevice *vdev =3D VFIO_PCI_DEVICE(vbasedev->dev); + AppleVFIOContainer *container; + VFIOContainer *bcontainer; + VFIOAddressSpace *space; + VFIOIOMMUClass *vioc; + int ret; + + space =3D vfio_address_space_get(as); + container =3D VFIO_IOMMU_APPLE(object_new(TYPE_VFIO_IOMMU_APPLE)); + bcontainer =3D VFIO_IOMMU(container); + vioc =3D VFIO_IOMMU_GET_CLASS(bcontainer); + + container->host_bus =3D vdev->host.bus; + container->host_device =3D vdev->host.slot; + container->host_function =3D vdev->host.function; + + ret =3D ram_block_uncoordinated_discard_disable(true); + if (ret) { + error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken"= ); + goto fail_unref; + } + + container->dext_conn =3D apple_dext_connect(container->host_bus, + container->host_device, + container->host_function= ); + if (container->dext_conn =3D=3D IO_OBJECT_NULL) { + error_setg(errp, + "vfio-apple: could not connect to dext for host PCI " + "%02x:%02x.%x", + container->host_bus, container->host_device, + container->host_function); + goto fail_discards; + } + + if (apple_dext_claim(container->dext_conn) !=3D KERN_SUCCESS) { + error_setg(errp, + "vfio-apple: failed to claim dext-backed PCI device " + "%02x:%02x.%x", + container->host_bus, container->host_device, + container->host_function); + goto fail_release_conn; + } + + if (!apple_vfio_dext_publish(container->host_bus, container->host_devi= ce, + container->host_function, + container->dext_conn)) { + error_setg(errp, + "vfio-apple: duplicate dext owner for host PCI %02x:%02= x.%x", + container->host_bus, container->host_device, + container->host_function); + goto fail_release_conn; + } + + if (!vioc->setup(bcontainer, errp)) { + goto fail_shared_conn; + } + + vfio_address_space_insert(space, bcontainer); + + if (!vfio_listener_register(bcontainer, errp)) { + goto fail_address_space; + } + + bcontainer->initialized =3D true; + return container; + +fail_address_space: + vfio_listener_unregister(bcontainer); + QLIST_REMOVE(bcontainer, next); + bcontainer->space =3D NULL; +fail_shared_conn: + apple_vfio_dext_release(container->host_bus, container->host_device, + container->host_function, container->dext_conn= ); + container->dext_conn =3D IO_OBJECT_NULL; +fail_discards: + ram_block_uncoordinated_discard_disable(false); +fail_unref: + object_unref(container); + vfio_address_space_put(space); + return NULL; + +fail_release_conn: + apple_dext_disconnect(container->dext_conn); + container->dext_conn =3D IO_OBJECT_NULL; + goto fail_discards; +} + +static void apple_vfio_container_disconnect(AppleVFIOContainer *container) +{ + VFIOContainer *bcontainer =3D VFIO_IOMMU(container); + VFIOAddressSpace *space =3D bcontainer->space; + + ram_block_uncoordinated_discard_disable(false); + vfio_listener_unregister(bcontainer); + + apple_vfio_dext_release(container->host_bus, container->host_device, + container->host_function, container->dext_conn= ); + container->dext_conn =3D IO_OBJECT_NULL; + + object_unref(container); + vfio_address_space_put(space); +} + +static bool apple_vfio_attach_device(const char *name, VFIODevice *vbasede= v, + AddressSpace *as, Error **errp) +{ + VFIOApplePCIDevice *adev =3D VFIO_APPLE_PCI(vbasedev->dev); + AppleVFIOContainer *container; + struct vfio_device_info info =3D { + .argsz =3D sizeof(info), + .flags =3D VFIO_DEVICE_FLAGS_PCI | VFIO_DEVICE_FLAGS_RESET, + .num_regions =3D VFIO_PCI_NUM_REGIONS, + .num_irqs =3D VFIO_PCI_NUM_IRQS, + }; + + container =3D apple_vfio_container_connect(as, vbasedev, errp); + if (!container) { + return false; + } + + vbasedev->fd =3D -1; + vbasedev->io_ops =3D &apple_vfio_device_io_ops; + vfio_device_prepare(vbasedev, VFIO_IOMMU(container), &info); + + if (!apple_vfio_device_setup(adev, errp)) { + vfio_device_unprepare(vbasedev); + apple_vfio_container_disconnect(container); + return false; + } + + return true; +} + +static void apple_vfio_detach_device(VFIODevice *vbasedev) +{ + VFIOApplePCIDevice *adev =3D VFIO_APPLE_PCI(vbasedev->dev); + AppleVFIOContainer *container =3D VFIO_IOMMU_APPLE(vbasedev->bcontaine= r); + + apple_vfio_device_cleanup(adev); + vfio_device_unprepare(vbasedev); + apple_vfio_container_disconnect(container); +} + +static void vfio_iommu_apple_class_init(ObjectClass *klass, const void *da= ta) +{ + VFIOIOMMUClass *vioc =3D VFIO_IOMMU_CLASS(klass); + + vioc->setup =3D apple_vfio_setup; + vioc->dma_map =3D apple_vfio_dma_map; + vioc->dma_unmap =3D apple_vfio_dma_unmap; + vioc->attach_device =3D apple_vfio_attach_device; + vioc->detach_device =3D apple_vfio_detach_device; + vioc->set_dirty_page_tracking =3D apple_vfio_set_dirty_page_tracking; + vioc->query_dirty_bitmap =3D apple_vfio_query_dirty_bitmap; + vioc->pci_hot_reset =3D apple_vfio_pci_hot_reset; +} + +static const TypeInfo apple_vfio_types[] =3D { + { + .name =3D TYPE_VFIO_IOMMU_APPLE, + .parent =3D TYPE_VFIO_IOMMU, + .instance_size =3D sizeof(AppleVFIOContainer), + .class_init =3D vfio_iommu_apple_class_init, + }, +}; + +DEFINE_TYPES(apple_vfio_types) diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 965c8e5b80..473f8669f9 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -40,6 +40,8 @@ system_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( # Apple VFIO backend if host_os =3D=3D 'darwin' system_ss.add(when: 'CONFIG_VFIO', - if_true: [files('apple-dext-client.c'), - coref, iokit]) + if_true: [files('apple-device.c', + 'container-apple.c', + 'apple-dext-client.c'), + coref, iokit]) endif --=20 2.50.1 (Apple Git-155) From nobody Sat Apr 11 18:38:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1775374204; cv=none; d=zohomail.com; s=zohoarc; b=GDKzRfUTXlLkjl84TekXnn+rkSQEoxXqG1xtz7JJrZI1B5H24ExTCnpD3v2/Ml3AlgJzaImn98jbqMtWr9yntLZMqF+fX63VRaaCHk1rN4+o7BtpavymBSW+bdbYvI789bxwYtXXPmnrfzpQUlULoUytozessj4pDEk4ZEJb8Ng= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775374204; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=fWuxDaZI7ELjXX3VdrMom5tpL3p2EOAcG4ltkUwfmlI=; b=eFDyY+m7RMXNvBrdksWja9v8r/ZEjT7G3zqajgV7orgU7zo0aUtBA7WRiHA6sYnCBCvC9kNXwaNXifSwI2RwF8vO4wxYb/KAP0iO/fhlB782du8eMX52/dPFJL/9QJlyudfmz5PS8MvUv9XAZBau2kdci5a/3TsGr02aPqOhJaM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 177537420476244.52526916161128; Sun, 5 Apr 2026 00:30:04 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w9HvU-0003yr-M4; Sun, 05 Apr 2026 03:29:44 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w9HvT-0003yC-82 for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:43 -0400 Received: from mail-dy1-x1329.google.com ([2607:f8b0:4864:20::1329]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w9HvQ-0007Px-Oa for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:43 -0400 Received: by mail-dy1-x1329.google.com with SMTP id 5a478bee46e88-2cf1646bd11so348020eec.1 for ; Sun, 05 Apr 2026 00:29:40 -0700 (PDT) Received: from localhost.localdomain ([2601:645:8200:47:41e4:ff2b:ff70:4d75]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2cb92ea0ef1sm7636502eec.21.2026.04.05.00.29.37 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 05 Apr 2026 00:29:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1775374179; x=1775978979; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=fWuxDaZI7ELjXX3VdrMom5tpL3p2EOAcG4ltkUwfmlI=; b=hd64ncbPiGbELjHIkCPLOZwPWamN5YQ8kOjRpS4u28JeWHN3980jmI4qFLAPdUWmY1 aUZCbTZfpg4GRDhVBN5iL0FkWe0BN37ZeBcK6qQU7N1DgFG3dpYgT+ZUGv6EqZ9ZFQgt hALLTT4OHaVrHx3N5hlnI2wOa6bPOc/jkBR1VrM50E7ltw+Dq1sE5MhFisLL2m/dbfOw 8pT9+Y+GT4q6zQt9GJgRjcrm1Xmv3513A/aV2vqDZD+eNcYIqmyBKeo7bNkEKF88o0Jp MFKA3xL74ZuuZqnAfjZAxwPhI0i8XEBBglyylt+JFcWiYdB4vs9a6m7W23ulEWw9NJwl SoBw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775374179; x=1775978979; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=fWuxDaZI7ELjXX3VdrMom5tpL3p2EOAcG4ltkUwfmlI=; b=YNlJKxAP13cX4kr5Uo15Zh5b63DhEamiahUpXXs3ZVZWiJIBESYRFN9z/vvvfWUzfQ 2WzNWAHyvazhtHOMAA6GcRkuZN1l2ZNUicVuFz42iznoYug6QZ/cLUkX2P1+2JQPjrf5 JqzwwpG8k+bPsAQ2NeQvgsgr7UztksTxajdE9MEVTB/WW41VVWMxKl7IibUcvvjIXygq kT/OyaxeWbSgGXtOFyNqgza0fXFKFYj2Zmtrwdw98zbBq2YRxZ5hCNbHX6JZv/xZ6iz0 cj/s0jvkaXTTLzAfeQwc0rH684j4L73Pmwx64j8nrgYH8lLVLmpfEExP7OmRD8KtiAWx 1KpA== X-Gm-Message-State: AOJu0Yx1cO8hLrJCQ+FLeE/vxmCRHwszLw66mkDsN6YsWFlkobVDufV3 A5Dw2fc2qcV4bWX+ilDUDbgaTrGBw3IUGg/uwF08TRyKtK2o6IZ+SanHpetPVhzRg0I= X-Gm-Gg: AeBDiet28+eErUrAKMSqPRC2eOfdbop4oWdkrkKKNhF12s2QRf3UHFraXBIHvhowHr4 zigyqfFcrvPcLfHCQNiXgTZM7QmT37symahYpfiTDDM+D7K0rzIDgNXbI6zJRc3xV5i5S1goEXs M+H8WUdPYw0go1UDpgcEdlUlrT1EXbuAsHCZtzJJPaCyQ2IlItwJhkGXmX3KDgGqDopl8hhSa43 92Mcvm5+xq6T+tOlfIv6+F3ElB3Nc7DfKtn8qOS8PkRvTEJLr883hrdRp+qJ1J1jTQFYjffbbvw 03mkt8cmqyYYSgTyL7bHXQthCopkoffS9Ukwb9kZ7N8jlGOa9CG7c+uaShP2sQXP6iXXDe2/+Gq 52c/1IZ4e0M9LcTgLfvSlB6NKa90zyT6WM4/jkZTG5TSBKymT1XuBMa51m9Lh7KSOGDBdC2AR42 QZMACg1T34sTX3lZJaDnsV5WzZYDaFM05IlE+7arDvt29rxA/kWaTQq8WV87XUJGj2XM1drwiiH NHcfQh8fdFKqHnI1UoIUJjt/3Q= X-Received: by 2002:a05:693c:3018:b0:2c0:cc90:a71 with SMTP id 5a478bee46e88-2cbf9df22d4mr3755706eec.8.1775374178818; Sun, 05 Apr 2026 00:29:38 -0700 (PDT) From: "Scott J. Goldman" To: qemu-devel@nongnu.org Cc: alex@shazbot.org, clg@redhat.com, pbonzini@redhat.com, rbolshakov@ddn.com, phil@philjordan.eu, mst@redhat.com, john.levon@nutanix.com, thanos.makatos@nutanix.com, qemu-s390x@nongnu.org, "Scott J. Goldman" , "Scott J. Goldman" Subject: [RFC PATCH 09/10] vfio/apple: Add apple-dma-pci companion device Date: Sun, 5 Apr 2026 00:28:53 -0700 Message-ID: <20260405072857.66484-10-scottjgo@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260405072857.66484-1-scottjgo@gmail.com> References: <20260405072857.66484-1-scottjgo@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::1329; envelope-from=scottjgo@gmail.com; helo=mail-dy1-x1329.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1775374206977154100 From: "Scott J. Goldman" Add a virtual PCI device ("apple-dma-pci") that acts as the DMA mapping companion for passthrough devices on macOS. On Linux, the kernel VFIO driver can directly map guest RAM into the hardware IOMMU. On macOS, the DriverKit dext can only DMA-map memory that belongs to its client process (QEMU). The guest kernel needs a way to request DMA mappings for its buffers through the host. apple-dma-pci provides a simple MMIO register interface in the guest: the guest driver writes DMA mapping requests (IOVA, GPA, size) to the device's BAR, and the QEMU device model translates GPAs to HVAs and calls through to the dext to establish the physical IOMMU mapping. The device uses PCI vendor/device ID 1b36:0015 (Red Hat, Inc.). Signed-off-by: Scott J. Goldman --- docs/specs/pci-ids.rst | 3 + hw/vfio/apple-dma.c | 540 +++++++++++++++++++++++++++++++++++++++++ hw/vfio/meson.build | 5 +- include/hw/pci/pci.h | 1 + 4 files changed, 546 insertions(+), 3 deletions(-) create mode 100644 hw/vfio/apple-dma.c diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst index 261b0f359f..48229dab5d 100644 --- a/docs/specs/pci-ids.rst +++ b/docs/specs/pci-ids.rst @@ -100,6 +100,9 @@ PCI devices (other than virtio): PCI UFS device (``-device ufs``) 1b36:0014 PCI RISC-V IOMMU device +1b36:0015 + Apple DMA mapping device (``-device apple-dma-pci``, + :doc:`../system/devices/vfio-apple`) =20 All these devices are documented in :doc:`index`. =20 diff --git a/hw/vfio/apple-dma.c b/hw/vfio/apple-dma.c new file mode 100644 index 0000000000..e705179b0d --- /dev/null +++ b/hw/vfio/apple-dma.c @@ -0,0 +1,540 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Apple DMA mapping PCI device + * + * A simple PCI device that receives batched DMA map/unmap requests from + * the guest via a shared command page + doorbell register, resolves guest + * physical addresses to host virtual addresses, and registers them with + * the macOS DriverKit dext for DART mapping. + * + * Protocol: + * 1. Guest allocates a command page and request/response buffers in RAM. + * 2. Guest writes the command page GPA to BAR registers (one-time setup= ). + * 3. Per batch: guest fills the command page and request buffer (no VME= XIT), + * then writes the doorbell register (single VMEXIT triggers processi= ng). + * 4. Device reads the command page, processes all entries, writes respo= nses + * and status back to guest RAM before the doorbell write returns. + * + * Copyright (c) 2026 Scott J. Goldman + */ + +#include "qemu/osdep.h" + +#include "hw/pci/pci_device.h" +#include "hw/core/qdev-properties.h" +#include "hw/vfio/apple-dext-client.h" +#include "hw/vfio/apple.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "system/address-spaces.h" +#include "system/dma.h" +#include "system/memory.h" + +#include "hw/pci/pci.h" + +/* BAR0 register offsets */ +#define APPLE_DMA_REG_VERSION 0x00 /* R: protocol version */ +#define APPLE_DMA_REG_MANAGED_BDF 0x04 /* R: guest BDF this maps for= */ +#define APPLE_DMA_REG_MAX_ENTRIES 0x08 /* R: max entries per batch */ +#define APPLE_DMA_REG_STATUS 0x0C /* R: result of last doorbell= */ +#define APPLE_DMA_REG_CMD_GPA_LO 0x10 /* W: command page GPA [31:0]= */ +#define APPLE_DMA_REG_CMD_GPA_HI 0x14 /* W: command page GPA [63:32= ] */ +#define APPLE_DMA_REG_DOORBELL 0x18 /* W: any write triggers batc= h */ +#define APPLE_DMA_BAR_SIZE 0x1000 /* page-aligned */ + +#define APPLE_DMA_VERSION 2 +#define APPLE_DMA_MAX_ENTRIES 4096 + +/* Command types (in command page) */ +#define APPLE_DMA_CMD_MAP 1 +#define APPLE_DMA_CMD_UNMAP 2 + +/* Status codes */ +#define APPLE_DMA_S_OK 0 +#define APPLE_DMA_S_IOERR 1 +#define APPLE_DMA_S_INVAL 3 + +/* + * Command page layout (in guest RAM, 32 bytes): + * + * 0x00 uint32_t type MAP=3D1, UNMAP=3D2 + * 0x04 uint32_t count number of entries + * 0x08 uint32_t status (written by device) 0=3DOK + * 0x0C uint32_t reserved + * 0x10 uint64_t req_gpa GPA of request entries array + * 0x18 uint64_t resp_gpa GPA of response entries array + */ +#define CMD_OFF_TYPE 0x00 +#define CMD_OFF_COUNT 0x04 +#define CMD_OFF_STATUS 0x08 +#define CMD_OFF_REQ_GPA 0x10 +#define CMD_OFF_RESP_GPA 0x18 +#define CMD_PAGE_SIZE 0x20 + +/* + * Map request entry (16 bytes): + * uint64_t gpa, uint32_t len, uint32_t flags + * + * Map response entry (24 bytes): + * uint64_t id, uint64_t dma_addr, uint32_t dma_len, uint32_t status + * + * Unmap request entry (16 bytes): + * uint64_t id, uint64_t size + * + * Unmap response entry (16 bytes): + * uint64_t id, uint32_t status, uint32_t reserved + */ + +typedef struct AppleDMAMapReq { + uint64_t gpa; + uint32_t len; + uint32_t flags; +} QEMU_PACKED AppleDMAMapReq; + +typedef struct AppleDMAMapResp { + uint64_t id; + uint64_t dma_addr; + uint32_t dma_len; + uint32_t status; +} QEMU_PACKED AppleDMAMapResp; + +typedef struct AppleDMAUnmapReq { + uint64_t id; + uint64_t size; +} QEMU_PACKED AppleDMAUnmapReq; + +typedef struct AppleDMAUnmapResp { + uint64_t id; + uint32_t status; + uint32_t reserved; +} QEMU_PACKED AppleDMAUnmapResp; + +#define TYPE_APPLE_DMA_PCI "apple-dma-pci" +OBJECT_DECLARE_SIMPLE_TYPE(AppleDMAState, APPLE_DMA_PCI) + +struct AppleDMAState { + PCIDevice parent_obj; + + MemoryRegion bar; + + /* Configuration (set via properties) */ + uint32_t managed_bdf; + uint32_t max_entries; + uint32_t apple_host_bus; + uint32_t apple_host_device; + uint32_t apple_host_function; + + /* Runtime state */ + uint64_t cmd_gpa; + uint32_t last_status; + io_connect_t dext_conn; + bool shared_dext_conn; +}; + +/* ------------------------------------------------------------------ */ +/* DMA backend operations */ +/* ------------------------------------------------------------------ */ + +static bool apple_dma_backend_map(AppleDMAState *s, uint64_t gpa, uint32_t= size, + uint64_t *out_dma_addr, uint32_t *out_dm= a_len) +{ + hwaddr map_len =3D size; + void *hva; + uint64_t bus_addr =3D 0, bus_len =3D 0; + + hva =3D dma_memory_map(&address_space_memory, gpa, &map_len, + DMA_DIRECTION_TO_DEVICE, MEMTXATTRS_UNSPECIFIED); + if (!hva || map_len < size) { + if (hva) { + dma_memory_unmap(&address_space_memory, hva, map_len, + DMA_DIRECTION_TO_DEVICE, 0); + } + return false; + } + + /* + * Use the GPA as the dext lookup key. The dext treats this as an + * opaque handle for matching register/unregister calls; the actual + * DMA bus address is assigned by the platform and returned in + * bus_addr. + */ + if (s->dext_conn !=3D IO_OBJECT_NULL) { + kern_return_t kr; + + kr =3D apple_dext_register_dma(s->dext_conn, gpa, + (uint64_t)hva, size, + &bus_addr, &bus_len); + dma_memory_unmap(&address_space_memory, hva, map_len, + DMA_DIRECTION_TO_DEVICE, 0); + if (kr !=3D KERN_SUCCESS) { + return false; + } + *out_dma_addr =3D bus_addr; + *out_dma_len =3D (uint32_t)bus_len; + } else { + dma_memory_unmap(&address_space_memory, hva, map_len, + DMA_DIRECTION_TO_DEVICE, 0); + *out_dma_addr =3D gpa; + *out_dma_len =3D size; + } + + return true; +} + +static uint32_t apple_dma_backend_unmap(AppleDMAState *s, uint64_t id) +{ + if (s->dext_conn !=3D IO_OBJECT_NULL) { + kern_return_t kr; + + kr =3D apple_dext_unregister_dma(s->dext_conn, id); + if (kr !=3D KERN_SUCCESS) { + return APPLE_DMA_S_IOERR; + } + } + + return APPLE_DMA_S_OK; +} + +/* ------------------------------------------------------------------ */ +/* Doorbell =E2=80=94 process a batch from the command page = */ +/* ------------------------------------------------------------------ */ + +static void apple_dma_handle_map(AppleDMAState *s, uint64_t req_gpa, + uint64_t resp_gpa, uint32_t count) +{ + AddressSpace *as =3D &address_space_memory; + hwaddr req_len =3D count * sizeof(AppleDMAMapReq); + hwaddr resp_len =3D count * sizeof(AppleDMAMapResp); + AppleDMAMapReq *reqs; + AppleDMAMapResp *resps; + uint32_t i; + bool ok =3D true; + + reqs =3D dma_memory_map(as, req_gpa, &req_len, DMA_DIRECTION_TO_DEVICE, + MEMTXATTRS_UNSPECIFIED); + if (!reqs || req_len < count * sizeof(AppleDMAMapReq)) { + if (reqs) { + dma_memory_unmap(as, reqs, req_len, DMA_DIRECTION_TO_DEVICE, 0= ); + } + s->last_status =3D APPLE_DMA_S_INVAL; + return; + } + + resps =3D dma_memory_map(as, resp_gpa, &resp_len, DMA_DIRECTION_FROM_D= EVICE, + MEMTXATTRS_UNSPECIFIED); + if (!resps || resp_len < count * sizeof(AppleDMAMapResp)) { + if (resps) { + dma_memory_unmap(as, resps, resp_len, + DMA_DIRECTION_FROM_DEVICE, 0); + } + dma_memory_unmap(as, reqs, req_len, DMA_DIRECTION_TO_DEVICE, 0); + s->last_status =3D APPLE_DMA_S_INVAL; + return; + } + + for (i =3D 0; i < count; i++) { + uint64_t gpa =3D le64_to_cpu(reqs[i].gpa); + uint64_t dma_addr =3D 0; + uint32_t dma_len =3D 0; + + if (apple_dma_backend_map(s, gpa, le32_to_cpu(reqs[i].len), + &dma_addr, &dma_len)) { + resps[i].id =3D cpu_to_le64(gpa); + resps[i].dma_addr =3D cpu_to_le64(dma_addr); + resps[i].dma_len =3D cpu_to_le32(dma_len); + resps[i].status =3D cpu_to_le32(APPLE_DMA_S_OK); + } else { + resps[i].status =3D cpu_to_le32(APPLE_DMA_S_IOERR); + ok =3D false; + } + } + + s->last_status =3D ok ? APPLE_DMA_S_OK : APPLE_DMA_S_IOERR; + dma_memory_unmap(as, resps, resp_len, DMA_DIRECTION_FROM_DEVICE, + resp_len); + dma_memory_unmap(as, reqs, req_len, DMA_DIRECTION_TO_DEVICE, 0); +} + +static void apple_dma_handle_unmap(AppleDMAState *s, uint64_t req_gpa, + uint64_t resp_gpa, uint32_t count) +{ + AddressSpace *as =3D &address_space_memory; + hwaddr req_len =3D count * sizeof(AppleDMAUnmapReq); + hwaddr resp_len =3D count * sizeof(AppleDMAUnmapResp); + AppleDMAUnmapReq *reqs; + AppleDMAUnmapResp *resps; + uint32_t i; + bool ok =3D true; + + reqs =3D dma_memory_map(as, req_gpa, &req_len, DMA_DIRECTION_TO_DEVICE, + MEMTXATTRS_UNSPECIFIED); + if (!reqs || req_len < count * sizeof(AppleDMAUnmapReq)) { + if (reqs) { + dma_memory_unmap(as, reqs, req_len, DMA_DIRECTION_TO_DEVICE, 0= ); + } + s->last_status =3D APPLE_DMA_S_INVAL; + return; + } + + resps =3D dma_memory_map(as, resp_gpa, &resp_len, DMA_DIRECTION_FROM_D= EVICE, + MEMTXATTRS_UNSPECIFIED); + if (!resps || resp_len < count * sizeof(AppleDMAUnmapResp)) { + if (resps) { + dma_memory_unmap(as, resps, resp_len, + DMA_DIRECTION_FROM_DEVICE, 0); + } + dma_memory_unmap(as, reqs, req_len, DMA_DIRECTION_TO_DEVICE, 0); + s->last_status =3D APPLE_DMA_S_INVAL; + return; + } + + for (i =3D 0; i < count; i++) { + uint64_t id =3D le64_to_cpu(reqs[i].id); + uint32_t status =3D apple_dma_backend_unmap(s, id); + + resps[i].id =3D cpu_to_le64(id); + resps[i].status =3D cpu_to_le32(status); + if (status !=3D APPLE_DMA_S_OK) { + ok =3D false; + } + } + + s->last_status =3D ok ? APPLE_DMA_S_OK : APPLE_DMA_S_IOERR; + dma_memory_unmap(as, resps, resp_len, DMA_DIRECTION_FROM_DEVICE, + resp_len); + dma_memory_unmap(as, reqs, req_len, DMA_DIRECTION_TO_DEVICE, 0); +} + +static void apple_dma_doorbell(AppleDMAState *s) +{ + AddressSpace *as =3D &address_space_memory; + uint8_t cmd_buf[CMD_PAGE_SIZE]; + uint32_t type, count; + uint64_t req_gpa, resp_gpa; + uint32_t le_status; + + if (!s->cmd_gpa) { + s->last_status =3D APPLE_DMA_S_INVAL; + return; + } + + if (dma_memory_read(as, s->cmd_gpa, cmd_buf, CMD_PAGE_SIZE, + MEMTXATTRS_UNSPECIFIED) !=3D MEMTX_OK) { + s->last_status =3D APPLE_DMA_S_INVAL; + return; + } + + type =3D ldl_le_p(cmd_buf + CMD_OFF_TYPE); + count =3D ldl_le_p(cmd_buf + CMD_OFF_COUNT); + req_gpa =3D ldq_le_p(cmd_buf + CMD_OFF_REQ_GPA); + resp_gpa =3D ldq_le_p(cmd_buf + CMD_OFF_RESP_GPA); + + if (!count || count > s->max_entries || !req_gpa || !resp_gpa) { + s->last_status =3D APPLE_DMA_S_INVAL; + goto write_status; + } + + switch (type) { + case APPLE_DMA_CMD_MAP: + apple_dma_handle_map(s, req_gpa, resp_gpa, count); + break; + case APPLE_DMA_CMD_UNMAP: + apple_dma_handle_unmap(s, req_gpa, resp_gpa, count); + break; + default: + s->last_status =3D APPLE_DMA_S_INVAL; + break; + } + +write_status: + le_status =3D cpu_to_le32(s->last_status); + dma_memory_write(as, s->cmd_gpa + CMD_OFF_STATUS, &le_status, 4, + MEMTXATTRS_UNSPECIFIED); +} + +/* ------------------------------------------------------------------ */ +/* MMIO BAR handlers */ +/* ------------------------------------------------------------------ */ + +static uint64_t apple_dma_bar_read(void *opaque, hwaddr addr, unsigned siz= e) +{ + AppleDMAState *s =3D opaque; + + switch (addr) { + case APPLE_DMA_REG_VERSION: + return APPLE_DMA_VERSION; + case APPLE_DMA_REG_MANAGED_BDF: + return s->managed_bdf; + case APPLE_DMA_REG_MAX_ENTRIES: + return s->max_entries; + case APPLE_DMA_REG_STATUS: + return s->last_status; + default: + return 0; + } +} + +static void apple_dma_bar_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + AppleDMAState *s =3D opaque; + + switch (addr) { + case APPLE_DMA_REG_CMD_GPA_LO: + s->cmd_gpa =3D deposit64(s->cmd_gpa, 0, 32, val); + break; + case APPLE_DMA_REG_CMD_GPA_HI: + s->cmd_gpa =3D deposit64(s->cmd_gpa, 32, 32, val); + break; + case APPLE_DMA_REG_DOORBELL: + apple_dma_doorbell(s); + break; + default: + break; + } +} + +static const MemoryRegionOps apple_dma_bar_ops =3D { + .read =3D apple_dma_bar_read, + .write =3D apple_dma_bar_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .impl =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + }, +}; + +/* ------------------------------------------------------------------ */ +/* Dext connection */ +/* ------------------------------------------------------------------ */ + +static bool apple_dma_connect_dext(AppleDMAState *s, Error **errp) +{ + io_connect_t conn; + kern_return_t kr; + + conn =3D apple_vfio_dext_lookup(s->apple_host_bus, s->apple_host_devic= e, + s->apple_host_function); + if (conn !=3D IO_OBJECT_NULL) { + s->dext_conn =3D conn; + s->shared_dext_conn =3D true; + return true; + } + + conn =3D apple_dext_connect(s->apple_host_bus, s->apple_host_device, + s->apple_host_function); + if (conn =3D=3D IO_OBJECT_NULL) { + error_setg(errp, + "apple-dma: could not connect to dext for host PCI " + "%02x:%02x.%x", + s->apple_host_bus, s->apple_host_device, + s->apple_host_function); + return false; + } + + kr =3D apple_dext_claim(conn); + if (kr !=3D KERN_SUCCESS) { + error_setg(errp, + "apple-dma: failed to claim dext-backed PCI device " + "%02x:%02x.%x (kr=3D0x%x)", + s->apple_host_bus, s->apple_host_device, + s->apple_host_function, kr); + apple_dext_disconnect(conn); + return false; + } + + s->dext_conn =3D conn; + s->shared_dext_conn =3D false; + return true; +} + +/* ------------------------------------------------------------------ */ +/* PCI device lifecycle */ +/* ------------------------------------------------------------------ */ + +static void apple_dma_pci_realize(PCIDevice *pdev, Error **errp) +{ + AppleDMAState *s =3D APPLE_DMA_PCI(pdev); + + if (s->apple_host_bus =3D=3D UINT32_MAX || + s->apple_host_device =3D=3D UINT32_MAX || + s->apple_host_function =3D=3D UINT32_MAX) { + error_setg(errp, "apple-dma: requires x-apple-host-bus, " + "x-apple-host-device, and x-apple-host-function"); + return; + } + + if (!s->max_entries) { + s->max_entries =3D APPLE_DMA_MAX_ENTRIES; + } + + if (!apple_dma_connect_dext(s, errp)) { + return; + } + + memory_region_init_io(&s->bar, OBJECT(s), &apple_dma_bar_ops, s, + "apple-dma-bar", APPLE_DMA_BAR_SIZE); + pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar); +} + +static void apple_dma_pci_exit(PCIDevice *pdev) +{ + AppleDMAState *s =3D APPLE_DMA_PCI(pdev); + + if (s->dext_conn !=3D IO_OBJECT_NULL) { + if (s->shared_dext_conn) { + apple_vfio_dext_release(s->apple_host_bus, s->apple_host_devic= e, + s->apple_host_function, s->dext_conn); + } else { + apple_dext_disconnect(s->dext_conn); + } + s->dext_conn =3D IO_OBJECT_NULL; + } +} + +static const Property apple_dma_pci_properties[] =3D { + DEFINE_PROP_UINT32("managed-bdf", AppleDMAState, managed_bdf, 0), + DEFINE_PROP_UINT32("max-entries", AppleDMAState, max_entries, 0), + DEFINE_PROP_UINT32("x-apple-host-bus", AppleDMAState, + apple_host_bus, UINT32_MAX), + DEFINE_PROP_UINT32("x-apple-host-device", AppleDMAState, + apple_host_device, UINT32_MAX), + DEFINE_PROP_UINT32("x-apple-host-function", AppleDMAState, + apple_host_function, UINT32_MAX), +}; + +static void apple_dma_pci_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + PCIDeviceClass *pdc =3D PCI_DEVICE_CLASS(klass); + + pdc->realize =3D apple_dma_pci_realize; + pdc->exit =3D apple_dma_pci_exit; + pdc->vendor_id =3D PCI_VENDOR_ID_REDHAT; + pdc->device_id =3D PCI_DEVICE_ID_REDHAT_APPLE_DMA; + pdc->class_id =3D PCI_CLASS_SYSTEM_OTHER; + + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, apple_dma_pci_properties); + dc->desc =3D "Apple DMA mapping device"; +} + +static const TypeInfo apple_dma_pci_info =3D { + .name =3D TYPE_APPLE_DMA_PCI, + .parent =3D TYPE_PCI_DEVICE, + .instance_size =3D sizeof(AppleDMAState), + .class_init =3D apple_dma_pci_class_init, + .interfaces =3D (const InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + +static void apple_dma_pci_register(void) +{ + type_register_static(&apple_dma_pci_info); +} + +type_init(apple_dma_pci_register) diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 473f8669f9..d7b4cbcc19 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -40,8 +40,7 @@ system_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( # Apple VFIO backend if host_os =3D=3D 'darwin' system_ss.add(when: 'CONFIG_VFIO', - if_true: [files('apple-device.c', - 'container-apple.c', - 'apple-dext-client.c'), + if_true: [files('apple-device.c', 'apple-dma.c', + 'container-apple.c', 'apple-dext-client.c'= ), coref, iokit]) endif diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 5b179091de..8e3fe77cc7 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -121,6 +121,7 @@ extern bool pci_available; #define PCI_DEVICE_ID_REDHAT_ACPI_ERST 0x0012 #define PCI_DEVICE_ID_REDHAT_UFS 0x0013 #define PCI_DEVICE_ID_REDHAT_RISCV_IOMMU 0x0014 +#define PCI_DEVICE_ID_REDHAT_APPLE_DMA 0x0015 #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 =20 #define FMT_PCIBUS PRIx64 --=20 2.50.1 (Apple Git-155) From nobody Sat Apr 11 18:38:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1775374255; cv=none; d=zohomail.com; s=zohoarc; b=EWIrkdW62987vXzl69t1tq3iseq1hiolY13F+ko5lcBOQyMJ3kEvD5aOJDpryXSf6qf1oigh3XedA9CwzKnlLOp9jH9XjLNsHWi3RYtIV6auR6MO90Z2jjSo0y1YtHo6rUgsGVHGL8DN2MICQYgsD+LvSEcPSNdOFJAJ7vz1KOw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775374255; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=E5HDXr0HjZKWX3A8XRFgsqoTXAEWhIhEwLxgd+5/kSA=; b=XuZQ0nAmAo8KUxPhzciy1R/2Nkrhf1GdvziRT7IDMbS3PPcwod10oMpCJ4yIeLwKuCRwD8iwKk/O1zCtt0zHlTjzUq0nVr2fgeUDqbDjOBrec5uob1MndzEbFLgzUgH+KIJdNp0XjkMdo70Fc67o/jDHxU110rB0WWZ5ST0+7os= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1775374255049121.37295401671406; Sun, 5 Apr 2026 00:30:55 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w9HvW-0003zm-AO; Sun, 05 Apr 2026 03:29:46 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w9HvU-0003yu-Ni for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:44 -0400 Received: from mail-dy1-x132a.google.com ([2607:f8b0:4864:20::132a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w9HvS-0007QM-1W for qemu-devel@nongnu.org; Sun, 05 Apr 2026 03:29:44 -0400 Received: by mail-dy1-x132a.google.com with SMTP id 5a478bee46e88-2cea94c06e4so160414eec.1 for ; Sun, 05 Apr 2026 00:29:41 -0700 (PDT) Received: from localhost.localdomain ([2601:645:8200:47:41e4:ff2b:ff70:4d75]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2cb92ea0ef1sm7636502eec.21.2026.04.05.00.29.38 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 05 Apr 2026 00:29:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1775374180; x=1775978980; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=E5HDXr0HjZKWX3A8XRFgsqoTXAEWhIhEwLxgd+5/kSA=; b=TbknRV7n4h4VurYz7hTgPUgn/ZBB6fGxW0VhvXbRArLBEyBSbgSAz4XDvZTsttTN9p tW7aY02hXhqatv1+wT7xtkyHrJcYcV4m5oWLpJNdhaFTrzglaXXOeENdCOHR7DepNQi3 GypUaL7dkEwnt3tknvs+ueyATLjytlQaPdOT5Z74LsKniqEsyI7JD+ElOLXkDV6ka9CL fapThuevFxZuzRECfbdMHlsTARm9F1boMPWCxncN82gTMW1HbEFMhAlai6bCVOuysK2Q 6coqLG8kcY4Pmrh4NPqa4z2xGZcIzN9Kor+Ezp/lYSMl7pjNHJwMpu1npMJ2y/iTVY7V /68A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775374180; x=1775978980; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=E5HDXr0HjZKWX3A8XRFgsqoTXAEWhIhEwLxgd+5/kSA=; b=m06MIamkczz2M5loMJ/Vl8hUn1caGN5MJOXE4b0sAjoXMvS68bXw3qyUsxlwMdg2Kw 3QLOyLq/K7EKccvWPnpxfifunnWFpOpsEiLgmtlQoWgXjLD48qqUmTDBw3TJ19swZvkl OB12KQvmBaGuybr+h2/UcmE/n6QS8+QYJjbD1kRrvLPmyX8bMA9M2hly1Nb9AflEKrDu rgiJNxBIx2pTBmnT42Z0Bl3i+KiME7/k9BPg4M2JCQMTl6eGKa2IBaF2LzjPqB5czg75 z08YWnIEaRmPLhZGv7UG3RJVAB72+FidbKG9fzzgYGzdfBZcqARJnNRmv1SJEPgs2S0Y 32NQ== X-Gm-Message-State: AOJu0YzJXKFRB/RJ2I58j+nSxywDvxUWNW7yM8yPRs1yB+GCGEU/uPMd pHS7vBYbrQjPXYyAdvpfXRHO4muRIjYDBrPBzta30IEg5q2g4jZcFzwX0Rpq40AwmFs= X-Gm-Gg: AeBDievtIJAJNVMHp53WA6A9VZOrW9B/R1PdvA6yyDE6orKr92Wegztz/xUacczAZ0J 0RhEhLWBCya9xTHWo6BZdoALKgixVDMy8P+RN+L5reDUl3TFOT3JOYhGjndcLPXb03heWRU/GRZ FzdhgQGvPcqOKrlUgsqCx/YENM4Ttq399sbxv4DLyGo1biNxRndzGJ514Sjk37b9nfLkT0yGdWL H1zAzram1nIcBbhR/cHLLHou2RNeio1AVHI9S9fbgxX9Ej45kXeyUaouW+rRpaDJTJnaffzgG2H Foch7wib9HTPh4SOSA8Dphe1DfCowweJE8TmKPsqgOuSTZpoAl3AvewsNnCZJqIhplAthTbiwI2 Nx6hMlb8W3TD9CsG9mIhay/riATx+9zfgXKB4K403D2lwu3t2KWoYOeDhIWns7kCCfF1n2MSLop u3/+qcpeH2syht417svkCgK8oz+VL4vmlklB9FawbtTuLKIh2aq7sjBOFVzQEvpo/ANonsKeadE cBPWBROmBlxubdsiD6Mhsd6mwU= X-Received: by 2002:a05:7301:3f09:b0:2c0:df3b:ec1e with SMTP id 5a478bee46e88-2cad7e3729fmr4300085eec.11.1775374180189; Sun, 05 Apr 2026 00:29:40 -0700 (PDT) From: "Scott J. Goldman" To: qemu-devel@nongnu.org Cc: alex@shazbot.org, clg@redhat.com, pbonzini@redhat.com, rbolshakov@ddn.com, phil@philjordan.eu, mst@redhat.com, john.levon@nutanix.com, thanos.makatos@nutanix.com, qemu-s390x@nongnu.org, "Scott J. Goldman" , "Scott J. Goldman" Subject: [RFC PATCH 10/10] docs: Add vfio-apple documentation and MAINTAINERS entry Date: Sun, 5 Apr 2026 00:28:54 -0700 Message-ID: <20260405072857.66484-11-scottjgo@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260405072857.66484-1-scottjgo@gmail.com> References: <20260405072857.66484-1-scottjgo@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::132a; envelope-from=scottjgo@gmail.com; helo=mail-dy1-x132a.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1775374255928154100 From: "Scott J. Goldman" Add documentation for the Apple VFIO passthrough backend covering requirements, usage, the DMA companion device protocol, and known limitations. Register the vfio-apple files in MAINTAINERS. Signed-off-by: Scott J. Goldman --- MAINTAINERS | 11 ++ docs/system/device-emulation.rst | 1 + docs/system/devices/vfio-apple.rst | 160 +++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 docs/system/devices/vfio-apple.rst diff --git a/MAINTAINERS b/MAINTAINERS index ad215eced8..baed89cbf7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2338,6 +2338,17 @@ F: qapi/vfio.json F: migration/vfio-stub.c F: tests/functional/aarch64/test_device_passthrough.py =20 +vfio-apple +M: Scott J. Goldman +S: Maintained +F: hw/vfio/apple-device.c +F: hw/vfio/apple-dext-client.c +F: hw/vfio/apple-dext-client.h +F: hw/vfio/apple-dma.c +F: hw/vfio/apple.h +F: hw/vfio/container-apple.c +F: include/compat/linux/ + vfio-igd M: Alex Williamson M: C=C3=A9dric Le Goater diff --git a/docs/system/device-emulation.rst b/docs/system/device-emulatio= n.rst index 40054bb7df..aa3dbfa1e0 100644 --- a/docs/system/device-emulation.rst +++ b/docs/system/device-emulation.rst @@ -98,4 +98,5 @@ Emulated Devices devices/scsi/index.rst devices/usb-u2f.rst devices/usb.rst + devices/vfio-apple.rst devices/vfio-user.rst diff --git a/docs/system/devices/vfio-apple.rst b/docs/system/devices/vfio-= apple.rst new file mode 100644 index 0000000000..b0e92f3b96 --- /dev/null +++ b/docs/system/devices/vfio-apple.rst @@ -0,0 +1,160 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later +.. _vfio-apple: + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +vfio-apple (macOS/ARM) +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +QEMU supports PCI device passthrough on Apple Silicon Macs using a macOS +DriverKit extension (dext) as the host backend. Unlike Linux VFIO, which +relies on kernel-managed IOMMU groups and ``/dev/vfio``, the Apple backend +communicates with a userspace driver extension through IOKit's +``IOUserClient`` interface. + +Requirements +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +- Apple Silicon Mac running macOS +- A DriverKit extension (``VFIOUserPCIDriver``) installed and running for + the target PCI device +- QEMU built with ``--enable-hvf`` on a Darwin host +- A guest kernel module that speaks the ``apple-dma-pci`` protocol (see + below) + +Usage +=3D=3D=3D=3D=3D + +Specify the host PCI device by its bus/device/function address: + +.. code-block:: console + + -device vfio-apple-pci,host=3D01:00.0 + +This creates a ``vfio-apple-pci`` device that connects to the dext instance +managing the given host PCI BDF. A companion ``apple-dma-pci`` device is +automatically created on the same bus. + +Architecture +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The implementation consists of several components: + +``vfio-apple-pci`` + A QOM subclass of ``vfio-pci`` that overrides realize/exit to set up the + Apple-specific IOMMU container and interrupt delivery. + +``vfio-iommu-apple`` + An IOMMU container backend. DMA map/unmap through the container are + no-ops because DMA is managed separately through the companion device + (see below). + +``apple-dma-pci`` + A paravirtualized PCI device that provides batched DMA map/unmap + operations between the guest and the dext. The guest driver writes a + command page GPA to BAR0 registers, fills request/response buffers in + RAM, then triggers processing with a single doorbell write (one VMEXIT + per batch). + +``Dext communication`` + All host device access (config space, BAR MMIO, DMA registration, + interrupt notification) goes through IOKit ``IOConnectCallMethod`` calls= to + the dext's ``IOUserClient``. + +DMA mapping +----------- + +On Linux, VFIO programs the hardware IOMMU directly via kernel ioctls. +QEMU chooses IOVAs and the kernel maps them through the IOMMU. + +On macOS, the DART (Apple's IOMMU) is managed entirely by the DriverKit +extension. QEMU cannot choose IOVAs =E2=80=94 it can only request that a = host +virtual address be mapped for DMA, and the platform assigns the resulting +IOVA. This means the guest cannot assume any particular IOVA layout; +the ``apple-dma-pci`` companion device returns the platform-assigned IOVA +and bus address in its response entries. + +Because of this architecture, a **guest kernel module** is required to +drive the ``apple-dma-pci`` device. The module discovers the companion +device, submits map/unmap batches, and translates between guest physical +addresses and the platform-assigned DMA addresses that the passthrough +device will use. + +Platform constraints +-------------------- + +The macOS DART imposes limits that do not exist with Linux VFIO: + +- **No IOVA alignment guarantees**: the platform may return any address. + Guest drivers that assume page-aligned or naturally-aligned DMA + addresses must account for this. +- **Total DMA memory limit**: approximately 1.5 GB of guest memory can be + registered for DMA at any time. +- **Mapping count limit**: approximately 64k concurrent DMA mappings. + +These limits are enforced by the DART hardware and DriverKit, not by QEMU. +Exceeding them will cause map requests to fail. + +``apple-dma-pci`` register interface +------------------------------------- + +.. list-table:: + :header-rows: 1 + + * - Offset + - Name + - Access + - Description + * - 0x00 + - VERSION + - R + - Protocol version + * - 0x04 + - MANAGED_BDF + - R + - Guest BDF this device maps for + * - 0x08 + - MAX_ENTRIES + - R + - Maximum entries per batch + * - 0x0C + - STATUS + - R + - Result of last doorbell + * - 0x10 + - CMD_GPA_LO + - W + - Command page GPA [31:0] + * - 0x14 + - CMD_GPA_HI + - W + - Command page GPA [63:32] + * - 0x18 + - DOORBELL + - W + - Any write triggers batch processing + +Interrupts +---------- + +The dext tracks pending hardware interrupts (MSI/MSI-X) in a bitfield +(one bit per vector, up to 256 vectors). When a hardware interrupt fires, +the dext sets the corresponding bit and completes an asynchronous +notification to QEMU via ``IOConnectCallAsyncMethod``. The notification +is dispatched through a GCD queue, which wakes the QEMU main loop via an +``EventNotifier`` pipe. QEMU then atomically reads and clears the pending +bits and delivers each flagged vector to the guest. + +Limitations +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +- **Guest kernel module required**: the ``apple-dma-pci`` protocol is not + handled by standard guest drivers. +- **No migration support**: the Apple container does not support dirty page + tracking. +- **Interrupt delivery**: interrupts are delivered asynchronously via IOKit + rather than directly by the kernel, adding some overhead compared to + Linux VFIO. +- **No hot-plug**: devices must be configured at VM startup. +- **DMA constraints**: see `Platform constraints`_ above. +- **Darwin only**: the base ``vfio-pci`` device type is not user-creatable + on Darwin; use ``vfio-apple-pci`` instead. --=20 2.50.1 (Apple Git-155)