From nobody Sun May 19 11:37:25 2024 Delivered-To: importer@patchew.org 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; Authentication-Results: mx.zohomail.com; dkim=fail; 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=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1585923872; cv=none; d=zohomail.com; s=zohoarc; b=AWrtMShQugYIDKv5PTf/HceY/RUFGm5XcvEWfMj07WYHoW8D01qBMcih6P8SIpz+WP9C64nZEYKV21lINJMTXyh1Ehx9racursBCjFEa+j8NRlI8/yM48apFrNVUx1mzUxAsOF60QGj341US6cjZ7XIJjBiINyhYGIccXic24QA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1585923872; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=ZGszGX7/dlFFly+wjt/H+SDs19eZmIByirBcMM+A3k0=; b=h1RGidF0rPPRs7gm184VFu2AKqa9qVERSr/gI3WmnjRNGGc/3mQRR4F7c2caSNT/1fW879bm4McGLumqLhZt1FD9oG4BQGvh8ar4DTGCdVKkuvXTvIbMIjhTNQRbnI+bQS4xI2z00pbstBr/L+v4EPsoEefnpgTf531JEtX0x9M= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; 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=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1585923872651670.7457584236572; Fri, 3 Apr 2020 07:24:32 -0700 (PDT) Received: from localhost ([::1]:56154 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jKNFH-0007ds-8k for importer@patchew.org; Fri, 03 Apr 2020 10:24:31 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:41093) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jKNEI-0006Ji-2w for qemu-devel@nongnu.org; Fri, 03 Apr 2020 10:23:31 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1jKNEG-00082B-Ca for qemu-devel@nongnu.org; Fri, 03 Apr 2020 10:23:30 -0400 Received: from mail-wr1-x444.google.com ([2a00:1450:4864:20::444]:34258) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1jKNEG-0007zb-1l for qemu-devel@nongnu.org; Fri, 03 Apr 2020 10:23:28 -0400 Received: by mail-wr1-x444.google.com with SMTP id 65so8777116wrl.1 for ; Fri, 03 Apr 2020 07:23:26 -0700 (PDT) Received: from jondnuc.lan (IGLD-84-229-154-167.inter.net.il. [84.229.154.167]) by smtp.gmail.com with ESMTPSA id i2sm12218646wrx.22.2020.04.03.07.23.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 03 Apr 2020 07:23:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=ZGszGX7/dlFFly+wjt/H+SDs19eZmIByirBcMM+A3k0=; b=d/LfmVtOmkm0hb2iNgVJOZYipm4j465uGS0uc75JfqyN4WLCLqqIKtJuRY2rJMvrEX 8vQQAARUplmuqgtL4/Ik0g2zC60Yq4oCaKc8x7mAC3xLKdNYFT2B1+6YnQYoLLJhk7ma Njmr9DUc06G2Ex8TRzZ/UGBC5wWW2/HZktCIZIl/oQSg4nXmfFiOJzIu3U2co3wz9Qy8 01fvjxP43Ra8ctY+fxotb0GS8UL0muOHuUlhkFVuhBktlkszqJIUto21mK24DXS0GIoN 9onVSRZJX1kjOL0W6fKc6bk/1PSZmy3qtkJElYF32c7280X5mmV+wDFHwcVEZe8MGaE6 uMhg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ZGszGX7/dlFFly+wjt/H+SDs19eZmIByirBcMM+A3k0=; b=DW3B2cj5CU/cBkCPkbUUhJ0SLKCM3IIhKg/gz2LnGo4JjBJ2KZbCiWXNsHc+ZA73+u onE9kxB/ZqsIk2GNEmvbxNgVWXHP2+Wls/F18HKk6DaLAPvCa3UsatCJvzm0675UTTEc hLjSG9oo/TLHAbCAb9/Ksv3BNGjJ/wmMwvQcWT9bDeX4oC32bk3rWk0Mpvn0LL8uyAL2 jd/rbX/L/rjWKi3+CpP7ttEZkQZT33iMRkzruh40/9S0dV4CgT/d+6jsfaiYrQ8WgIcD bGK2y9XehLa2IcElQtiVWZ5QO4zMntzsekuaE1OKu2Fxoli+IbNNyLenPW8sn8Tovq0V ZZpw== X-Gm-Message-State: AGi0PuaYGU6J1iMN5vx+55CwedIfZ55mMd5aPJtj0Sg0UTFQhUKqsAr4 brqRCJT2R6ckqlghuYvc72kNYK2HdQQ= X-Google-Smtp-Source: APiQypLYRMQScoicmnsieQh3flyhWsmMgd7oJ0En4xlLKpAGylHiB31/yhLxRJm1Mtwwx19H1flBkg== X-Received: by 2002:adf:e70f:: with SMTP id c15mr9927824wrm.217.1585923805482; Fri, 03 Apr 2020 07:23:25 -0700 (PDT) From: Jon Doron To: qemu-devel@nongnu.org Subject: [PATCH v1 1/5] hyperv: expose API to determine if synic is enabled Date: Fri, 3 Apr 2020 17:23:04 +0300 Message-Id: <20200403142308.82990-2-arilou@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200403142308.82990-1-arilou@gmail.com> References: <20200403142308.82990-1-arilou@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::444 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pbonzini@redhat.com, vkuznets@redhat.com, ehabkost@redhat.com, Jon Doron Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Type: text/plain; charset="utf-8" Signed-off-by: Jon Doron --- hw/hyperv/hyperv.c | 8 ++++++++ include/hw/hyperv/hyperv.h | 1 + 2 files changed, 9 insertions(+) diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index 8ca3706f5b..ddf4f32c60 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -37,6 +37,13 @@ typedef struct SynICState { #define TYPE_SYNIC "hyperv-synic" #define SYNIC(obj) OBJECT_CHECK(SynICState, (obj), TYPE_SYNIC) =20 +static bool synic_enabled; + +bool hyperv_is_synic_enabled(void) +{ + return synic_enabled; +} + static SynICState *get_synic(CPUState *cs) { return SYNIC(object_resolve_path_component(OBJECT(cs), "synic")); @@ -133,6 +140,7 @@ void hyperv_synic_add(CPUState *cs) object_property_add_child(OBJECT(cs), "synic", obj, &error_abort); object_unref(obj); object_property_set_bool(obj, true, "realized", &error_abort); + synic_enabled =3D true; } =20 void hyperv_synic_reset(CPUState *cs) diff --git a/include/hw/hyperv/hyperv.h b/include/hw/hyperv/hyperv.h index 597381cb01..a63ee0003c 100644 --- a/include/hw/hyperv/hyperv.h +++ b/include/hw/hyperv/hyperv.h @@ -79,5 +79,6 @@ void hyperv_synic_add(CPUState *cs); void hyperv_synic_reset(CPUState *cs); void hyperv_synic_update(CPUState *cs, bool enable, hwaddr msg_page_addr, hwaddr event_page_addr); +bool hyperv_is_synic_enabled(void); =20 #endif --=20 2.24.1 From nobody Sun May 19 11:37:25 2024 Delivered-To: importer@patchew.org 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; Authentication-Results: mx.zohomail.com; dkim=fail; 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=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1585923965; cv=none; d=zohomail.com; s=zohoarc; b=c3x+Z147Jg0yShNYT3EAi/UOSx+0M4oLjfPciG+UvrJc9phObeoQc0MwoNphymUyq+wgw9dLUSEmdYtKdAQYPNGd8ubqbPgR6qcqeJyhQGgIfzhuIe530erCdLfA8884wBofhDxC3Zkedf4G7dJehWOra8vjauETpO1S3Zc+Tas= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1585923965; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=ah312KBMSHRpUiO/v1i0MNGqjwvrG+Bz23qtxXquL8I=; b=fcNE7LKCn/uB5eYcbo/+dVFTZV8QS8WdxmiRNP+DXEnbGVUy9swXImceDS/zSLcHkDpWIl20c6emds8sWXoApB4EYqeP6HfHDJ8U0F+Gyw1+Y5dPzgublsti9EnZhfonmM4VGGJS6dVLrWdr/FLrdp5uI983LAk7fmz0hTRZAiE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; 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=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1585923965967561.6872817656968; Fri, 3 Apr 2020 07:26:05 -0700 (PDT) Received: from localhost ([::1]:56186 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jKNGm-00023S-Ms for importer@patchew.org; Fri, 03 Apr 2020 10:26:04 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:41107) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jKNEJ-0006Kw-Fs for qemu-devel@nongnu.org; Fri, 03 Apr 2020 10:23:33 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1jKNEG-000826-CK for qemu-devel@nongnu.org; Fri, 03 Apr 2020 10:23:31 -0400 Received: from mail-wr1-x443.google.com ([2a00:1450:4864:20::443]:44428) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1jKNEF-00080D-Vl for qemu-devel@nongnu.org; Fri, 03 Apr 2020 10:23:28 -0400 Received: by mail-wr1-x443.google.com with SMTP id m17so8709991wrw.11 for ; Fri, 03 Apr 2020 07:23:27 -0700 (PDT) Received: from jondnuc.lan (IGLD-84-229-154-167.inter.net.il. [84.229.154.167]) by smtp.gmail.com with ESMTPSA id i2sm12218646wrx.22.2020.04.03.07.23.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 03 Apr 2020 07:23:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=ah312KBMSHRpUiO/v1i0MNGqjwvrG+Bz23qtxXquL8I=; b=E+zO7WiHWpjMcFSz3Cd3NS95jq1LszZxH8MlH61bKM0eOPfOqKy/N1QQVNaIx6S8Bi Tde5fR/a6m2RGu8ITAbvlyDBDJXhb/xW6IDFBVAV8b8tezV131i6mWrnwSZsYL3Br5om UxH3lJDarEHf3RyVdBi/i0B5++/tNlMcobKS8BVw39+UEIQ9sUGgsAW4/a9WyzzGpBJU norp4JBLsgqPvXcNBVchjePdvxYJeYJLBvHcdC5aROL8QRWbnpTWPTkp0WSQ+QG8PE4h j/heeeV7Zsmwak+dUJK33LVxNAUqJv21o8utIL5/4QjlXBjDXhL+Js/61HsweXBA0O+O GMBQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ah312KBMSHRpUiO/v1i0MNGqjwvrG+Bz23qtxXquL8I=; b=ITNElhyIorWnpI4rQ15rquPmrQj3aSPXRHhtxelJrRHD4ye/wZszOs4TMtYe9hJ3o7 UBECjpDf1crUFjobpfaTo4Avj0k6KvhB5o/6txBMOXI7az+X5y78uViYLa3zUGDBGXFF 66VnrafL2dmv6IAOHUSMCGMFJ5+cVtUcT6wC1eMYtNTqHGf7czz9Uz9QHCe/9WSabMB4 k0OgTNTdQhAAUuYkX0ciAV13Regi5lDjELa+gt8GVp4NPSOiYmgywXFcX0Umxcwk3B1r RaU6AomvCYbhE203Y0EXplIXGr/pv2Xj3TN1jTn3CKcT0l6iBbZZNd+BJQxEJYmxv31i Xmxw== X-Gm-Message-State: AGi0PuZIHTDo/5djVmJx91mbUDguP3WJBdcSweXpiOVn3ui4cUVqqgJV JlBpyEZVgFMkZWSI2V5PETTJLejt2sI= X-Google-Smtp-Source: APiQypIPEu9MsVE3znKcrU0pDntmoZhMjF2EK5GLMgxl3mKOeYbFgUhOJHW/2RtH0y/OSgJv2GbtmA== X-Received: by 2002:a5d:66c3:: with SMTP id k3mr9206419wrw.407.1585923806546; Fri, 03 Apr 2020 07:23:26 -0700 (PDT) From: Jon Doron To: qemu-devel@nongnu.org Subject: [PATCH v1 2/5] hyperv: SControl is optional to enable SynIc Date: Fri, 3 Apr 2020 17:23:05 +0300 Message-Id: <20200403142308.82990-3-arilou@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200403142308.82990-1-arilou@gmail.com> References: <20200403142308.82990-1-arilou@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::443 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pbonzini@redhat.com, vkuznets@redhat.com, ehabkost@redhat.com, Jon Doron Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Type: text/plain; charset="utf-8" SynIc can be enabled regardless of the SControl mechanisim which can register a GSI for a given SintRoute. This behaviour can achived by setting enabling SIMP and then the guest will poll on the message slot. Once there is another message pending the host will set the message slot with the pending flag. When the guest polls from the message slot, incase the pending flag is set it will write to the HV_X64_MSR_EOM indicating it has cleared the slow and we can try and push our message again. Signed-off-by: Jon Doron --- hw/hyperv/hyperv.c | 242 ++++++++++++++++++++++++------------- include/hw/hyperv/hyperv.h | 2 + target/i386/hyperv.c | 2 + 3 files changed, 164 insertions(+), 82 deletions(-) diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index ddf4f32c60..1dc577a0ab 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -20,18 +20,72 @@ #include "qemu/rcu_queue.h" #include "hw/hyperv/hyperv.h" =20 +/* + * KVM has its own message producers (SynIC timers). To guarantee + * serialization with both KVM vcpu and the guest cpu, the messages are fi= rst + * staged in an intermediate area and then posted to the SynIC message pag= e in + * the vcpu thread. + */ +typedef struct HvSintStagedMessage { + /* message content staged by hyperv_post_msg */ + struct hyperv_message msg; + /* callback + data (r/o) to complete the processing in a BH */ + HvSintMsgCb cb; + void *cb_data; + /* message posting status filled by cpu_post_msg */ + int status; + /* passing the buck: */ + enum { + /* initial state */ + HV_STAGED_MSG_FREE, + /* + * hyperv_post_msg (e.g. in main loop) grabs the staged area (FREE= -> + * BUSY), copies msg, and schedules cpu_post_msg on the assigned c= pu + */ + HV_STAGED_MSG_BUSY, + /* + * cpu_post_msg (vcpu thread) tries to copy staged msg to msg slot, + * notify the guest, records the status, marks the posting done (B= USY + * -> POSTED), and schedules sint_msg_bh BH + */ + HV_STAGED_MSG_POSTED, + /* + * sint_msg_bh (BH) verifies that the posting is done, runs the + * callback, and starts over (POSTED -> FREE) + */ + } state; +} HvSintStagedMessage; + +struct SynICState; + +struct HvSintRoute { + uint32_t sint; + struct SynICState *synic; + int gsi; + EventNotifier sint_set_notifier; + EventNotifier sint_ack_notifier; + + HvSintStagedMessage *staged_msg; + + unsigned refcount; + QLIST_ENTRY(HvSintRoute) link; +}; + typedef struct SynICState { DeviceState parent_obj; =20 CPUState *cs; =20 - bool enabled; + bool sctl_enabled; hwaddr msg_page_addr; hwaddr event_page_addr; MemoryRegion msg_page_mr; MemoryRegion event_page_mr; struct hyperv_message_page *msg_page; struct hyperv_event_flags_page *event_page; + + QemuMutex sint_routes_mutex; + QLIST_HEAD(, HvSintRoute) sint_routes; } SynICState; =20 #define TYPE_SYNIC "hyperv-synic" @@ -49,11 +103,11 @@ static SynICState *get_synic(CPUState *cs) return SYNIC(object_resolve_path_component(OBJECT(cs), "synic")); } =20 -static void synic_update(SynICState *synic, bool enable, +static void synic_update(SynICState *synic, bool sctl_enable, hwaddr msg_page_addr, hwaddr event_page_addr) { =20 - synic->enabled =3D enable; + synic->sctl_enabled =3D sctl_enable; if (synic->msg_page_addr !=3D msg_page_addr) { if (synic->msg_page_addr) { memory_region_del_subregion(get_system_memory(), @@ -78,7 +132,7 @@ static void synic_update(SynICState *synic, bool enable, } } =20 -void hyperv_synic_update(CPUState *cs, bool enable, +void hyperv_synic_update(CPUState *cs, bool sctl_enable, hwaddr msg_page_addr, hwaddr event_page_addr) { SynICState *synic =3D get_synic(cs); @@ -87,7 +141,7 @@ void hyperv_synic_update(CPUState *cs, bool enable, return; } =20 - synic_update(synic, enable, msg_page_addr, event_page_addr); + synic_update(synic, sctl_enable, msg_page_addr, event_page_addr); } =20 static void synic_realize(DeviceState *dev, Error **errp) @@ -108,16 +162,20 @@ static void synic_realize(DeviceState *dev, Error **e= rrp) sizeof(*synic->event_page), &error_abort); synic->msg_page =3D memory_region_get_ram_ptr(&synic->msg_page_mr); synic->event_page =3D memory_region_get_ram_ptr(&synic->event_page_mr); + qemu_mutex_init(&synic->sint_routes_mutex); + QLIST_INIT(&synic->sint_routes); =20 g_free(msgp_name); g_free(eventp_name); } + static void synic_reset(DeviceState *dev) { SynICState *synic =3D SYNIC(dev); memset(synic->msg_page, 0, sizeof(*synic->msg_page)); memset(synic->event_page, 0, sizeof(*synic->event_page)); synic_update(synic, false, 0, 0); + assert(QLIST_EMPTY(&synic->sint_routes)); } =20 static void synic_class_init(ObjectClass *klass, void *data) @@ -166,54 +224,6 @@ static void synic_register_types(void) =20 type_init(synic_register_types) =20 -/* - * KVM has its own message producers (SynIC timers). To guarantee - * serialization with both KVM vcpu and the guest cpu, the messages are fi= rst - * staged in an intermediate area and then posted to the SynIC message pag= e in - * the vcpu thread. - */ -typedef struct HvSintStagedMessage { - /* message content staged by hyperv_post_msg */ - struct hyperv_message msg; - /* callback + data (r/o) to complete the processing in a BH */ - HvSintMsgCb cb; - void *cb_data; - /* message posting status filled by cpu_post_msg */ - int status; - /* passing the buck: */ - enum { - /* initial state */ - HV_STAGED_MSG_FREE, - /* - * hyperv_post_msg (e.g. in main loop) grabs the staged area (FREE= -> - * BUSY), copies msg, and schedules cpu_post_msg on the assigned c= pu - */ - HV_STAGED_MSG_BUSY, - /* - * cpu_post_msg (vcpu thread) tries to copy staged msg to msg slot, - * notify the guest, records the status, marks the posting done (B= USY - * -> POSTED), and schedules sint_msg_bh BH - */ - HV_STAGED_MSG_POSTED, - /* - * sint_msg_bh (BH) verifies that the posting is done, runs the - * callback, and starts over (POSTED -> FREE) - */ - } state; -} HvSintStagedMessage; - -struct HvSintRoute { - uint32_t sint; - SynICState *synic; - int gsi; - EventNotifier sint_set_notifier; - EventNotifier sint_ack_notifier; - - HvSintStagedMessage *staged_msg; - - unsigned refcount; -}; - static CPUState *hyperv_find_vcpu(uint32_t vp_index) { CPUState *cs =3D qemu_get_cpu(vp_index); @@ -257,7 +267,7 @@ static void cpu_post_msg(CPUState *cs, run_on_cpu_data = data) =20 assert(staged_msg->state =3D=3D HV_STAGED_MSG_BUSY); =20 - if (!synic->enabled || !synic->msg_page_addr) { + if (!synic->msg_page_addr) { staged_msg->status =3D -ENXIO; goto posted; } @@ -341,7 +351,7 @@ int hyperv_set_event_flag(HvSintRoute *sint_route, unsi= gned eventno) if (eventno > HV_EVENT_FLAGS_COUNT) { return -EINVAL; } - if (!synic->enabled || !synic->event_page_addr) { + if (!synic->sctl_enabled || !synic->event_page_addr) { return -ENXIO; } =20 @@ -362,11 +372,13 @@ int hyperv_set_event_flag(HvSintRoute *sint_route, un= signed eventno) HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint, HvSintMsgCb cb, void *cb_data) { - HvSintRoute *sint_route; - EventNotifier *ack_notifier; + HvSintRoute *sint_route =3D NULL; + EventNotifier *ack_notifier =3D NULL; int r, gsi; CPUState *cs; SynICState *synic; + bool ack_event_initialized =3D false, sint_notifier_initialized =3D fa= lse, + irqfd_initialized =3D false; =20 cs =3D hyperv_find_vcpu(vp_index); if (!cs) { @@ -379,57 +391,82 @@ HvSintRoute *hyperv_sint_route_new(uint32_t vp_index,= uint32_t sint, } =20 sint_route =3D g_new0(HvSintRoute, 1); - r =3D event_notifier_init(&sint_route->sint_set_notifier, false); - if (r) { - goto err; + if (!sint_route) { + goto cleanup_err; } =20 + sint_route->gsi =3D 0; + sint_route->synic =3D synic; + sint_route->sint =3D sint; + sint_route->refcount =3D 1; =20 ack_notifier =3D cb ? &sint_route->sint_ack_notifier : NULL; if (ack_notifier) { sint_route->staged_msg =3D g_new0(HvSintStagedMessage, 1); + if (!sint_route->staged_msg) { + goto cleanup_err; + } sint_route->staged_msg->cb =3D cb; sint_route->staged_msg->cb_data =3D cb_data; =20 r =3D event_notifier_init(ack_notifier, false); if (r) { - goto err_sint_set_notifier; + goto cleanup_err; } - event_notifier_set_handler(ack_notifier, sint_ack_handler); + ack_event_initialized =3D true; + } + + /* See if we are done or we need to setup a GSI for this SintRoute */ + if (!synic->sctl_enabled) { + goto cleanup; + } + + /* We need to setup a GSI for this SintRoute */ + r =3D event_notifier_init(&sint_route->sint_set_notifier, false); + if (r) { + goto cleanup_err; } + sint_notifier_initialized =3D true; =20 gsi =3D kvm_irqchip_add_hv_sint_route(kvm_state, vp_index, sint); if (gsi < 0) { - goto err_gsi; + goto cleanup_err; } + irqfd_initialized =3D true; =20 r =3D kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, &sint_route->sint_set_notifier, ack_notifier, gsi); if (r) { - goto err_irqfd; + goto cleanup_err; } sint_route->gsi =3D gsi; - sint_route->synic =3D synic; - sint_route->sint =3D sint; - sint_route->refcount =3D 1; - +cleanup: + qemu_mutex_lock(&synic->sint_routes_mutex); + QLIST_INSERT_HEAD_RCU(&synic->sint_routes, sint_route, link); + qemu_mutex_unlock(&synic->sint_routes_mutex); return sint_route; =20 -err_irqfd: - kvm_irqchip_release_virq(kvm_state, gsi); -err_gsi: +cleanup_err: + if (irqfd_initialized) { + kvm_irqchip_release_virq(kvm_state, gsi); + } + + if (sint_notifier_initialized) { + event_notifier_cleanup(&sint_route->sint_set_notifier); + } + if (ack_notifier) { - event_notifier_set_handler(ack_notifier, NULL); - event_notifier_cleanup(ack_notifier); + if (ack_event_initialized) { + event_notifier_set_handler(ack_notifier, NULL); + event_notifier_cleanup(ack_notifier); + } + g_free(sint_route->staged_msg); } -err_sint_set_notifier: - event_notifier_cleanup(&sint_route->sint_set_notifier); -err: - g_free(sint_route); =20 + g_free(sint_route); return NULL; } =20 @@ -440,6 +477,8 @@ void hyperv_sint_route_ref(HvSintRoute *sint_route) =20 void hyperv_sint_route_unref(HvSintRoute *sint_route) { + SynICState *synic; + if (!sint_route) { return; } @@ -450,21 +489,33 @@ void hyperv_sint_route_unref(HvSintRoute *sint_route) return; } =20 - kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, - &sint_route->sint_set_notifier, - sint_route->gsi); - kvm_irqchip_release_virq(kvm_state, sint_route->gsi); + synic =3D sint_route->synic; + qemu_mutex_lock(&synic->sint_routes_mutex); + QLIST_REMOVE_RCU(sint_route, link); + qemu_mutex_unlock(&synic->sint_routes_mutex); + + if (sint_route->gsi) { + kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, + &sint_route->sint_set_notifi= er, + sint_route->gsi); + kvm_irqchip_release_virq(kvm_state, sint_route->gsi); + event_notifier_cleanup(&sint_route->sint_set_notifier); + } + if (sint_route->staged_msg) { event_notifier_set_handler(&sint_route->sint_ack_notifier, NULL); event_notifier_cleanup(&sint_route->sint_ack_notifier); g_free(sint_route->staged_msg); } - event_notifier_cleanup(&sint_route->sint_set_notifier); g_free(sint_route); } =20 int hyperv_sint_route_set_sint(HvSintRoute *sint_route) { + if (!sint_route->gsi) { + return 0; + } + return event_notifier_set(&sint_route->sint_set_notifier); } =20 @@ -528,6 +579,33 @@ unlock: return ret; } =20 +int hyperv_synic_eom(CPUState *cs) +{ + SynICState *synic =3D get_synic(cs); + HvSintRoute *sint_route; + + if (!synic) { + return -1; + } + + qemu_mutex_lock(&synic->sint_routes_mutex); + QLIST_FOREACH(sint_route, &synic->sint_routes, link) { + HvSintStagedMessage *staged_msg =3D sint_route->staged_msg; + + /* Skip a SintRoute that has a GSI registered with it */ + if (sint_route->gsi || + atomic_read(&staged_msg->state) !=3D HV_STAGED_MSG_POSTED) { + continue; + } + + aio_bh_schedule_oneshot(qemu_get_aio_context(), sint_msg_bh, + sint_route); + } + qemu_mutex_unlock(&synic->sint_routes_mutex); + + return 0; +} + uint16_t hyperv_hcall_post_message(uint64_t param, bool fast) { uint16_t ret; diff --git a/include/hw/hyperv/hyperv.h b/include/hw/hyperv/hyperv.h index a63ee0003c..ef9f6b6c09 100644 --- a/include/hw/hyperv/hyperv.h +++ b/include/hw/hyperv/hyperv.h @@ -28,6 +28,8 @@ void hyperv_sint_route_unref(HvSintRoute *sint_route); =20 int hyperv_sint_route_set_sint(HvSintRoute *sint_route); =20 +int hyperv_synic_eom(CPUState *cs); + /* * Submit a message to be posted in vcpu context. If the submission succe= eds, * the status of posting the message is reported via the callback associat= ed diff --git a/target/i386/hyperv.c b/target/i386/hyperv.c index 26efc1e0e6..f11268df48 100644 --- a/target/i386/hyperv.c +++ b/target/i386/hyperv.c @@ -66,6 +66,8 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exi= t *exit) case HV_X64_MSR_SIEFP: env->msr_hv_synic_evt_page =3D exit->u.synic.evt_page; break; + case HV_X64_MSR_EOM: + return hyperv_synic_eom(CPU(cpu)); default: return -1; } --=20 2.24.1 From nobody Sun May 19 11:37:25 2024 Delivered-To: importer@patchew.org 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; Authentication-Results: mx.zohomail.com; dkim=fail; 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=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1585923872; cv=none; d=zohomail.com; s=zohoarc; b=QTMTnY0P0H7TdgEnGC/55cjeaPcCx2ofr6rrUCfzGFput8fxojmRygDgV6BO5M5UVIFGE0Neq+HKzj25XOTaxIFmplUaOUCg4uV+0W1T7RSVd/5UL4BGnQGhu18x+Vm1er3mta03vjVlLA3xU3noB1W5K+RZcSJv/fdLuukeE8Q= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1585923872; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=+Qq12W1MHOj8bugy4vSdkIc0z/mVeOn/nlawzQ0OOV4=; b=e82DSLdUYipqOZU954AD3gg+8jCOpGd2xn8oy/Z/rVhSa4P3G/oAWcr5Den6PW0nveBBvNT1LTFoKtf7jG01vS09cOl+6Vqx58zIWfOXckL52pAYiW2s8qhC+eFaNvfWbjrWGhppOD5yAuhb2bYO1dmUQSC/ADe3BMGBcPsgjFc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; 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=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 158592387203361.8116771602007; Fri, 3 Apr 2020 07:24:32 -0700 (PDT) Received: from localhost ([::1]:56152 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jKNFG-0007ao-OO for importer@patchew.org; Fri, 03 Apr 2020 10:24:30 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:41108) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jKNEJ-0006Kz-GE for qemu-devel@nongnu.org; Fri, 03 Apr 2020 10:23:32 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1jKNEI-0008Ae-2l for qemu-devel@nongnu.org; Fri, 03 Apr 2020 10:23:31 -0400 Received: from mail-wr1-x442.google.com ([2a00:1450:4864:20::442]:42449) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1jKNEH-00082u-3R for qemu-devel@nongnu.org; Fri, 03 Apr 2020 10:23:30 -0400 Received: by mail-wr1-x442.google.com with SMTP id h15so8733930wrx.9 for ; Fri, 03 Apr 2020 07:23:29 -0700 (PDT) Received: from jondnuc.lan (IGLD-84-229-154-167.inter.net.il. [84.229.154.167]) by smtp.gmail.com with ESMTPSA id i2sm12218646wrx.22.2020.04.03.07.23.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 03 Apr 2020 07:23:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=+Qq12W1MHOj8bugy4vSdkIc0z/mVeOn/nlawzQ0OOV4=; b=UecWQ1UOjZVjNf9P6Sv0PIOKuUtejIIV/HVjGHO/+GOL5k5HLQd08YJzvZDTxxLaux onWTBTAnxl2B+zCjneoOU6YqZ9+g4MHaugA9YWfUbuXXxCij+ts3b64YCkEGqbc+dm41 h31k67Rj7nwmZPZkcENktzewIzwBWFuqqBgs+D9a+oSW+5s3RGG2EXhPjwmmrCefX8Ww jLCMQDhZYD5RXDr579uGuRr1/YH6GDgJfjpJiQa2nQ9e4Igl54wHPDWyFE3ca0IxtI/6 hYQTlA8JBOFl4bFwHsyvgztX2tCdG7+AIpJUgOxHIo+gbOeqXTPVgMniLUoRcS+qpVU6 pWBQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=+Qq12W1MHOj8bugy4vSdkIc0z/mVeOn/nlawzQ0OOV4=; b=j3yMMklYI0s4C88/iB8wiW9UmrH7ji91NYxNWcQo//fZl/p17ANEiqtmef/xfPTh7U dyBVnH/DRWHco/FjAUOLvwM7qyHBCpPZ7i2ipsINOgryt/+UUGZJPKih24h9KuiOagyM 3iW26kFnVj2Gdyl1+LApqQG/l2VaPJDpsOpRZifbGrjqcCpk1OfZPspwhnsHAcUpjSUh ZOSE1EF1OMMgJIv17CGqYCURIWb78Go2S21CVvXzhjRoRfZQJJpVgWsgKV+akSSYfA8b ELHPN2Vv106OMiWJTVLKeX4Dyh3FLC1io4CU0vHwu9QeXKknJjcGIC9woKqDTd56ZjZ5 lGDQ== X-Gm-Message-State: AGi0PuYPdAfQug808zelg+3xJnS5wiF56+/tsEtWbIj0CLIvH2ZELKZ4 Kw0aPwfPInvI2yET2oaB9Qi3WmxYwmc= X-Google-Smtp-Source: APiQypL0Uii/Vr02+xlMGh7bgHRsTYTA9Plu10+Zcw5V/kGSKzGgwiSduA8EEPEvaa53wpZRdpprng== X-Received: by 2002:a05:6000:1090:: with SMTP id y16mr8755411wrw.281.1585923807870; Fri, 03 Apr 2020 07:23:27 -0700 (PDT) From: Jon Doron To: qemu-devel@nongnu.org Subject: [PATCH v1 3/5] vmbus: add vmbus protocol definitions Date: Fri, 3 Apr 2020 17:23:06 +0300 Message-Id: <20200403142308.82990-4-arilou@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200403142308.82990-1-arilou@gmail.com> References: <20200403142308.82990-1-arilou@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::442 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pbonzini@redhat.com, vkuznets@redhat.com, Roman Kagan , ehabkost@redhat.com, Jon Doron Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Type: text/plain; charset="utf-8" Add a header with data structures and constants used in Hyper-V VMBus hypervisor <-> guest interactions. Based on the respective stuff from Linux kernel. Signed-off-by: Roman Kagan Signed-off-by: Jon Doron --- include/hw/vmbus/vmbus-proto.h | 222 +++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 include/hw/vmbus/vmbus-proto.h diff --git a/include/hw/vmbus/vmbus-proto.h b/include/hw/vmbus/vmbus-proto.h new file mode 100644 index 0000000000..80882e4fc6 --- /dev/null +++ b/include/hw/vmbus/vmbus-proto.h @@ -0,0 +1,222 @@ +/* + * QEMU Hyper-V VMBus support + * + * Copyright (c) 2017-2018 Virtuozzo International GmbH. + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#ifndef VMBUS_PROTO_H +#define VMBUS_PROTO_H + +#define VMBUS_VERSION_WS2008 ((0 << 16) | (13)) +#define VMBUS_VERSION_WIN7 ((1 << 16) | (1)) +#define VMBUS_VERSION_WIN8 ((2 << 16) | (4)) +#define VMBUS_VERSION_WIN8_1 ((3 << 16) | (0)) +#define VMBUS_VERSION_WIN10 ((4 << 16) | (0)) +#define VMBUS_VERSION_INVAL -1 +#define VMBUS_VERSION_CURRENT VMBUS_VERSION_WIN10 + +#define VMBUS_MESSAGE_CONNECTION_ID 1 +#define VMBUS_EVENT_CONNECTION_ID 2 +#define VMBUS_MONITOR_CONNECTION_ID 3 +#define VMBUS_SINT 2 + +#define VMBUS_MSG_INVALID 0 +#define VMBUS_MSG_OFFERCHANNEL 1 +#define VMBUS_MSG_RESCIND_CHANNELOFFER 2 +#define VMBUS_MSG_REQUESTOFFERS 3 +#define VMBUS_MSG_ALLOFFERS_DELIVERED 4 +#define VMBUS_MSG_OPENCHANNEL 5 +#define VMBUS_MSG_OPENCHANNEL_RESULT 6 +#define VMBUS_MSG_CLOSECHANNEL 7 +#define VMBUS_MSG_GPADL_HEADER 8 +#define VMBUS_MSG_GPADL_BODY 9 +#define VMBUS_MSG_GPADL_CREATED 10 +#define VMBUS_MSG_GPADL_TEARDOWN 11 +#define VMBUS_MSG_GPADL_TORNDOWN 12 +#define VMBUS_MSG_RELID_RELEASED 13 +#define VMBUS_MSG_INITIATE_CONTACT 14 +#define VMBUS_MSG_VERSION_RESPONSE 15 +#define VMBUS_MSG_UNLOAD 16 +#define VMBUS_MSG_UNLOAD_RESPONSE 17 +#define VMBUS_MSG_COUNT 18 + +#define VMBUS_MESSAGE_SIZE_ALIGN sizeof(uint64_t) + +#define VMBUS_PACKET_INVALID 0x0 +#define VMBUS_PACKET_SYNCH 0x1 +#define VMBUS_PACKET_ADD_XFER_PAGESET 0x2 +#define VMBUS_PACKET_RM_XFER_PAGESET 0x3 +#define VMBUS_PACKET_ESTABLISH_GPADL 0x4 +#define VMBUS_PACKET_TEARDOWN_GPADL 0x5 +#define VMBUS_PACKET_DATA_INBAND 0x6 +#define VMBUS_PACKET_DATA_USING_XFER_PAGES 0x7 +#define VMBUS_PACKET_DATA_USING_GPADL 0x8 +#define VMBUS_PACKET_DATA_USING_GPA_DIRECT 0x9 +#define VMBUS_PACKET_CANCEL_REQUEST 0xa +#define VMBUS_PACKET_COMP 0xb +#define VMBUS_PACKET_DATA_USING_ADDITIONAL_PKT 0xc +#define VMBUS_PACKET_ADDITIONAL_DATA 0xd + +#define VMBUS_CHANNEL_USER_DATA_SIZE 120 + +#define VMBUS_OFFER_MONITOR_ALLOCATED 0x1 +#define VMBUS_OFFER_INTERRUPT_DEDICATED 0x1 + +#define VMBUS_RING_BUFFER_FEAT_PENDING_SZ (1ul << 0) + +#define VMBUS_CHANNEL_ENUMERATE_DEVICE_INTERFACE 0x1 +#define VMBUS_CHANNEL_SERVER_SUPPORTS_TRANSFER_PAGES 0x2 +#define VMBUS_CHANNEL_SERVER_SUPPORTS_GPADLS 0x4 +#define VMBUS_CHANNEL_NAMED_PIPE_MODE 0x10 +#define VMBUS_CHANNEL_LOOPBACK_OFFER 0x100 +#define VMBUS_CHANNEL_PARENT_OFFER 0x200 +#define VMBUS_CHANNEL_REQUEST_MONITORED_NOTIFICATION 0x400 +#define VMBUS_CHANNEL_TLNPI_PROVIDER_OFFER 0x2000 + +#define VMBUS_PACKET_FLAG_REQUEST_COMPLETION 1 + +typedef struct vmbus_message_header { + uint32_t message_type; + uint32_t _padding; +} vmbus_message_header; + +typedef struct vmbus_message_initiate_contact { + vmbus_message_header header; + uint32_t version_requested; + uint32_t target_vcpu; + uint64_t interrupt_page; + uint64_t monitor_page1; + uint64_t monitor_page2; +} vmbus_message_initiate_contact; + +typedef struct vmbus_message_version_response { + vmbus_message_header header; + uint8_t version_supported; + uint8_t status; +} vmbus_message_version_response; + +typedef struct vmbus_message_offer_channel { + vmbus_message_header header; + uint8_t type_uuid[16]; + uint8_t instance_uuid[16]; + uint64_t _reserved1; + uint64_t _reserved2; + uint16_t channel_flags; + uint16_t mmio_size_mb; + uint8_t user_data[VMBUS_CHANNEL_USER_DATA_SIZE]; + uint16_t sub_channel_index; + uint16_t _reserved3; + uint32_t child_relid; + uint8_t monitor_id; + uint8_t monitor_flags; // VMBUS_OFFER_MONITOR_* + uint16_t interrupt_flags; // VMBUS_OFFER_INTERRUPT_* + uint32_t connection_id; +} vmbus_message_offer_channel; + +typedef struct vmbus_message_rescind_channel_offer { + vmbus_message_header header; + uint32_t child_relid; +} vmbus_message_rescind_channel_offer; + +typedef struct vmbus_gpa_range { + uint32_t byte_count; + uint32_t byte_offset; + uint64_t pfn_array[0]; +} vmbus_gpa_range; + +typedef struct vmbus_message_gpadl_header { + vmbus_message_header header; + uint32_t child_relid; + uint32_t gpadl_id; + uint16_t range_buflen; + uint16_t rangecount; + vmbus_gpa_range range; +} QEMU_PACKED vmbus_message_gpadl_header; + +typedef struct vmbus_message_gpadl_body { + vmbus_message_header header; + uint32_t message_number; + uint32_t gpadl_id; + uint64_t pfn_array[0]; +} vmbus_message_gpadl_body; + +typedef struct vmbus_message_gpadl_created { + vmbus_message_header header; + uint32_t child_relid; + uint32_t gpadl_id; + uint32_t status; +} vmbus_message_gpadl_created; + +typedef struct vmbus_message_gpadl_teardown { + vmbus_message_header header; + uint32_t child_relid; + uint32_t gpadl_id; +} vmbus_message_gpadl_teardown; + +typedef struct vmbus_message_gpadl_torndown { + vmbus_message_header header; + uint32_t gpadl_id; +} vmbus_message_gpadl_torndown; + +typedef struct vmbus_message_open_channel { + vmbus_message_header header; + uint32_t child_relid; + uint32_t open_id; + uint32_t ring_buffer_gpadl_id; + uint32_t target_vp; + uint32_t ring_buffer_offset; + uint8_t user_data[VMBUS_CHANNEL_USER_DATA_SIZE]; +} vmbus_message_open_channel; + +typedef struct vmbus_message_open_result { + vmbus_message_header header; + uint32_t child_relid; + uint32_t open_id; + uint32_t status; +} vmbus_message_open_result; + +typedef struct vmbus_message_close_channel { + vmbus_message_header header; + uint32_t child_relid; +} vmbus_message_close_channel; + +typedef struct vmbus_ring_buffer { + uint32_t write_index; + uint32_t read_index; + uint32_t interrupt_mask; + uint32_t pending_send_sz; + uint32_t _reserved1[12]; + uint32_t feature_bits; // VMBUS_RING_BUFFER_FEAT_* +} vmbus_ring_buffer; + +typedef struct vmbus_packet_hdr { + uint16_t type; + uint16_t offset_qwords; + uint16_t len_qwords; + uint16_t flags; + uint64_t transaction_id; +} vmbus_packet_hdr; + +typedef struct vmbus_pkt_gpa_direct { + uint32_t _reserved; + uint32_t rangecount; + vmbus_gpa_range range[0]; +} vmbus_pkt_gpa_direct; + +typedef struct vmbus_xferpg_range { + uint32_t byte_count; + uint32_t byte_offset; +} vmbus_xferpg_range; + +typedef struct vmbus_pkt_xferpg { + uint16_t buffer_id; + uint8_t sender_owns_set; + uint8_t _reserved; + uint32_t rangecount; + vmbus_xferpg_range range[0]; +} vmbus_pkt_xferpg; + +#endif --=20 2.24.1 From nobody Sun May 19 11:37:25 2024 Delivered-To: importer@patchew.org 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; Authentication-Results: mx.zohomail.com; dkim=fail; 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=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1585924062; cv=none; d=zohomail.com; s=zohoarc; b=XaTjxJ2p47j8py8L0Sy3Qrzm9X9Tl0VDewUra6jNI0Doe35yUEPlM7sPabwYnN7LJ79EsArEzfY6Jp8a6+K2SQiVwWx1Q1un5ranQliB5QbcWGSHrMI2FYxx/fGU2uaidykE/9c94YL+vjNwKqozDzGolsrHRKCFU5aP23yQvLY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1585924062; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=iS+2LLbk8RWWSkPnJfdW0sYbv7ULmmPs9o+FhH24w7U=; b=PzjzbMARsGlQcc8D4L9LM2WFaU1hp2h3pfqCSwUSx3lTiVpgqaUPtyq7zPvxoCtvK7KdK29n3Rz+05UlZVPMRrnaaHCty8Bw66XyqLEAiCgbHwLmA5oOOw5cVMrX06P11dv6a2ybLRjx3mj9Q0jOSfyrSCJah33Y0JnITw7c9MY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; 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=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1585924062644488.5718343518489; Fri, 3 Apr 2020 07:27:42 -0700 (PDT) Received: from localhost ([::1]:56206 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jKNIJ-0003bY-JZ for importer@patchew.org; Fri, 03 Apr 2020 10:27:39 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:41139) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jKNER-0006YC-6p for qemu-devel@nongnu.org; Fri, 03 Apr 2020 10:23:46 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1jKNEK-0008JU-HQ for qemu-devel@nongnu.org; Fri, 03 Apr 2020 10:23:39 -0400 Received: from mail-wm1-x331.google.com ([2a00:1450:4864:20::331]:35732) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1jKNEK-0008DM-1J for qemu-devel@nongnu.org; Fri, 03 Apr 2020 10:23:32 -0400 Received: by mail-wm1-x331.google.com with SMTP id i19so7944304wmb.0 for ; Fri, 03 Apr 2020 07:23:31 -0700 (PDT) Received: from jondnuc.lan (IGLD-84-229-154-167.inter.net.il. [84.229.154.167]) by smtp.gmail.com with ESMTPSA id i2sm12218646wrx.22.2020.04.03.07.23.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 03 Apr 2020 07:23:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=iS+2LLbk8RWWSkPnJfdW0sYbv7ULmmPs9o+FhH24w7U=; b=CLYnyZKo8TWB++9LAXvE+2dQVVKvoQ/bX+5QtPZGquQ7jNkHk+o8kU1ZWZi8pIfUtF xAxxWloPOizw3RyXP0U+961FO4kEFbeJp9/a9MqhU8p0OlFzagz27ZIOaoLSGRWznpy+ ydvVp20fynrHc5l4V6SbK/VGE8DkljvzQthGQ2Yhfw1S9xMaGzgLFFNHzy9R2gsMxruR L5hjI+8xqyGGqv1OWJXNcI+LHQTj/jCzOieo2NgPqg5APY2M03rtuVn1rP8LWoj4Dm4n thu3ADA5O/Qwr54ayoaLvGuP/CogGuSwZr/6n/I/4zEbLS0snQt+nfk5ZlxjX3CjGv1P OC2Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=iS+2LLbk8RWWSkPnJfdW0sYbv7ULmmPs9o+FhH24w7U=; b=aIJXE2If8wQcaNsFF1+DLE9cgtVyFn/22pKblRGJpWYnQj7B/wcgYeIIS7YH8QdwSU Xd3mfIZ/vp9E1OszIi5NByNETWhb/yJcXdvSCnRzwI7Wlj5muo1GjH95wZc1aVOn2qK/ pRnF9g25fH+EF6JnvbLUgoQJEKvlf7tSFGW9a+et5CPa2iqiDbmrQTafWRIYA7GvpK7P DK5QWBiDA5LlJVgMrtHV4jy1lxDShh5/3wwWXbczGDNxzwrg3CWZvTR/ORnS0AKauKTG +pfv0RivunfAvL8vubu7pivOrgwvxuBGSa56pbF3d1gQvU0HhQj8A9OI8V5qWOxPdxrY O8MA== X-Gm-Message-State: AGi0PubmQtiJjplsXxi3lVUtW6IksVeTktPq8H1dAEvoiuZUprHmD6/e r/5GbAx1cTDQ2fuaKuSiWhkTrSALU74= X-Google-Smtp-Source: APiQypLfzMAR9VTI7B1NEQbAlYzax5QcsHJseNTEPmFhNhZOt0vYjYugMUGViET1TxX/TTe3EsO+vw== X-Received: by 2002:a05:600c:2c50:: with SMTP id r16mr9312817wmg.6.1585923809248; Fri, 03 Apr 2020 07:23:29 -0700 (PDT) From: Jon Doron To: qemu-devel@nongnu.org Subject: [PATCH v1 4/5] vmbus: vmbus implementation Date: Fri, 3 Apr 2020 17:23:07 +0300 Message-Id: <20200403142308.82990-5-arilou@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200403142308.82990-1-arilou@gmail.com> References: <20200403142308.82990-1-arilou@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::331 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pbonzini@redhat.com, vkuznets@redhat.com, Roman Kagan , ehabkost@redhat.com, Jon Doron Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Type: text/plain; charset="utf-8" Add the VMBus infrastructure -- bus, devices, root bridge, vmbus state machine, vmbus channel interactions, etc. TODO: - split into smaller palatable pieces - more comments - check and handle corner cases Kudos to Evgeny Yakovlev (formerly eyakovlev@virtuozzo.com) and Andrey Smetatin (formerly asmetanin@virtuozzo.com) for research and prototyping. VMBus is a collection of technologies. At its lowest layer, it's a message passing and signaling mechanism, allowing efficient passing of messages to = and from guest VMs. A layer higher, it's a mechanism for defining channels of communication, where each channel is tagged with a type (which implies a protocol) and a instance ID. A layer higher than that, it's a bus driver, serving as the basis of device enumeration within a VM, where a channel can optionally be exposed as a paravirtual device. When a server-side (paravir= tual back-end) component wishes to offer a channel to a guest VM, it does so by specifying a channel type, a mode, and an instance ID. VMBus then exposes = this in the guest. More information about VMBus can be found in the file vmbuskernelmodeclientlibapi.h in Microsoft's WDK. Signed-off-by: Roman Kagan Signed-off-by: Jon Doron --- Makefile.objs | 1 + hw/Kconfig | 1 + hw/Makefile.objs | 1 + hw/i386/Kconfig | 1 + hw/i386/pc_q35.c | 2 + hw/vmbus/Kconfig | 3 + hw/vmbus/Makefile.objs | 1 + hw/vmbus/trace-events | 8 + hw/vmbus/vmbus.c | 2422 ++++++++++++++++++++++++++++++++++++++ include/hw/vmbus/vmbus.h | 109 ++ 10 files changed, 2549 insertions(+) create mode 100644 hw/vmbus/Kconfig create mode 100644 hw/vmbus/Makefile.objs create mode 100644 hw/vmbus/trace-events create mode 100644 hw/vmbus/vmbus.c create mode 100644 include/hw/vmbus/vmbus.h diff --git a/Makefile.objs b/Makefile.objs index a7c967633a..4943e7bf73 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -185,6 +185,7 @@ trace-events-subdirs +=3D hw/watchdog trace-events-subdirs +=3D hw/xen trace-events-subdirs +=3D hw/gpio trace-events-subdirs +=3D hw/riscv +trace-events-subdirs +=3D hw/vmbus trace-events-subdirs +=3D migration trace-events-subdirs +=3D net trace-events-subdirs +=3D ui diff --git a/hw/Kconfig b/hw/Kconfig index ecf491bf04..49ace0a4b5 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -39,6 +39,7 @@ source usb/Kconfig source virtio/Kconfig source vfio/Kconfig source watchdog/Kconfig +source vmbus/Kconfig =20 # arch Kconfig source arm/Kconfig diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 660e2b4373..9037bc8095 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -40,6 +40,7 @@ devices-dirs-$(CONFIG_MEM_DEVICE) +=3D mem/ devices-dirs-$(CONFIG_NUBUS) +=3D nubus/ devices-dirs-y +=3D semihosting/ devices-dirs-y +=3D smbios/ +devices-dirs-$(CONFIG_VMBUS) +=3D vmbus/ endif =20 common-obj-y +=3D $(devices-dirs-y) diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index c93f32f657..838ec28667 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -23,6 +23,7 @@ config PC imply TPM_TIS_ISA imply VGA_PCI imply VIRTIO_VGA + imply VMBUS select FDC select I8259 select I8254 diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index d37c425e22..767c137718 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -54,6 +54,7 @@ #include "qemu/error-report.h" #include "sysemu/numa.h" #include "hw/mem/nvdimm.h" +#include "hw/vmbus/vmbus.h" =20 /* ICH9 AHCI has 6 ports */ #define MAX_SATA_PORTS 6 @@ -346,6 +347,7 @@ static void pc_q35_machine_options(MachineClass *m) machine_class_allow_dynamic_sysbus_dev(m, TYPE_AMD_IOMMU_DEVICE); machine_class_allow_dynamic_sysbus_dev(m, TYPE_INTEL_IOMMU_DEVICE); machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); + machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE); m->max_cpus =3D 288; } =20 diff --git a/hw/vmbus/Kconfig b/hw/vmbus/Kconfig new file mode 100644 index 0000000000..e925c2a9be --- /dev/null +++ b/hw/vmbus/Kconfig @@ -0,0 +1,3 @@ +config VMBUS + bool + depends on HYPERV diff --git a/hw/vmbus/Makefile.objs b/hw/vmbus/Makefile.objs new file mode 100644 index 0000000000..3f0708588e --- /dev/null +++ b/hw/vmbus/Makefile.objs @@ -0,0 +1 @@ +obj-y +=3D vmbus.o diff --git a/hw/vmbus/trace-events b/hw/vmbus/trace-events new file mode 100644 index 0000000000..4e1c507cb8 --- /dev/null +++ b/hw/vmbus/trace-events @@ -0,0 +1,8 @@ +# vmbus +vmbus_initiate_contact(uint32_t version, uint32_t vcpu, uint64_t monitor_p= age1, uint64_t monitor_page2, uint64_t interrupt_page) "version requested %= d, target vcpu %d, monitor pages 0x%"PRIx64" 0x%"PRIx64", interrupt page 0x= %"PRIx64"" +vmbus_recv_message(uint32_t type, uint32_t size) "recvd message type %d, s= ize %d" +vmbus_signal_event(void) "vmbus event signaled" +vmbus_signal_channel(uint32_t relid) "signaling channel id %d" +vmbus_post_msg(uint32_t type, uint32_t size) "posting message type %d, siz= e %d" +vmbus_post_reply_callback(int status) "staged message status %d" +vmbus_process_incoming_message(uint32_t message_type) "processing incoming= message %d" diff --git a/hw/vmbus/vmbus.c b/hw/vmbus/vmbus.c new file mode 100644 index 0000000000..18e254f5c2 --- /dev/null +++ b/hw/vmbus/vmbus.c @@ -0,0 +1,2422 @@ +/* + * QEMU Hyper-V VMBus + * + * Copyright (c) 2017-2018 Virtuozzo International GmbH. + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "qemu/uuid.h" +#include "qapi/error.h" +#include "hw/vmbus/vmbus.h" +#include "hw/sysbus.h" +#include "hw/hyperv/hyperv.h" +#include "migration/vmstate.h" +#include "trace.h" + +#define TYPE_VMBUS "vmbus" +#define VMBUS(obj) OBJECT_CHECK(VMBus, (obj), TYPE_VMBUS) + +#define VMBUS_CHAN_RELID_COUNT HV_EVENT_FLAGS_COUNT +#define VMBUS_CHAN_FIRST_RELID 1 /* 0 is reserved */ +#define VMBUS_CHAN_CONNID_COUNT HV_EVENT_FLAGS_COUNT +#define VMBUS_CHAN_FIRST_CONNID (VMBUS_MONITOR_CONNECTION_ID + 1) + +#define VMBUS_VMSTATE_VERSION_ID 1 + +#define VMBUS_RX_QUEUE_CAPACITY 16 + +typedef struct VMBusGpadl { + QTAILQ_ENTRY(VMBusGpadl) link; + + uint32_t id; + uint32_t child_relid; + + uint32_t num_gfns; + uint32_t seen_gfns; + uint64_t *gfns; + + bool alive; + bool in_use; +} VMBusGpadl; + +typedef enum VMBusOfferState { + VMOFFER_INIT, + VMOFFER_SENDING, + VMOFFER_SENT, +} VMBusOfferState; + +typedef enum VMBusChannelState { + VMCHAN_INIT, + VMCHAN_OPENING, + VMCHAN_OPEN, +} VMBusChannelState; + +typedef struct VMBusRingBuf { + VMBusGpadl *gpadl; + AddressSpace *as; + DMADirection dir; + dma_addr_t rb_addr; + uint32_t base; + uint32_t len; + uint32_t my_idx; + uint32_t pending_sz; +} VMBusRingBuf; + +typedef struct VMBusChannel { + VMBusDevice *dev; + + uint32_t relid; + uint16_t subchan_idx; + uint32_t connid; + uint32_t open_id; + uint32_t target_vp; + uint32_t rb_gpadl; + uint32_t rb_rcv_offset; + + VMBusOfferState offer_state; + VMBusChannelState state; + + VMBusChannelNotifyCb notify_cb; + EventNotifier notifier; + + VMBus *vmbus; + HvSintRoute *notify_route; + VMBusGpadl *gpadl; + + VMBusRingBuf ringbuf_rcv; + VMBusRingBuf ringbuf_snd; + + uint32_t snd_reserved; + + QTAILQ_ENTRY(VMBusChannel) link; +} VMBusChannel; + +typedef enum VMBusState { + VMBUS_LISTEN, + VMBUS_HANDSHAKE, + VMBUS_OFFER, + VMBUS_CREATE_GPADL, + VMBUS_TEARDOWN_GPADL, + VMBUS_OPEN_CHANNEL, + VMBUS_UNLOAD, + VMBUS_STATE_MAX +} VMBusState; + +typedef struct VMBus { + BusState parent; + + VMBusState state; + bool msg_in_progress; + uint32_t version; + uint32_t target_vp; + HvSintRoute *sint_route; + hwaddr int_page_gpa; + + DECLARE_BITMAP(chan_relid_bitmap, VMBUS_CHAN_RELID_COUNT); + DECLARE_BITMAP(connection_id_bitmap, VMBUS_CHAN_CONNID_COUNT); + + struct hyperv_post_message_input rx_queue[VMBUS_RX_QUEUE_CAPACITY]; + uint8_t rx_queue_head; + uint8_t rx_queue_size; + QemuMutex rx_queue_lock; + + QTAILQ_HEAD(, VMBusGpadl) gpadl_list; + QTAILQ_HEAD(, VMBusChannel) channel_list; + + EventNotifier notifier; +} VMBus; + +static void vmbus_resched(VMBus *vmbus); +static void vmbus_msg_cb(void *data, int status); + +static bool gpadl_full(VMBusGpadl *gpadl) +{ + return gpadl->seen_gfns =3D=3D gpadl->num_gfns; +} + +static bool gpadl_broken(VMBusGpadl *gpadl) +{ + return !gpadl->num_gfns; +} + +static VMBusGpadl *create_gpadl(VMBus *vmbus, uint32_t id, + uint32_t child_relid, uint32_t num_gfns) +{ + VMBusGpadl *gpadl =3D g_new0(VMBusGpadl, 1); + + gpadl->id =3D id; + gpadl->child_relid =3D child_relid; + gpadl->num_gfns =3D num_gfns; + gpadl->gfns =3D g_new(uint64_t, num_gfns); + QTAILQ_INSERT_HEAD(&vmbus->gpadl_list, gpadl, link); + return gpadl; +} + +static void free_gpadl(VMBus *vmbus, VMBusGpadl *gpadl) +{ + QTAILQ_REMOVE(&vmbus->gpadl_list, gpadl, link); + g_free(gpadl->gfns); + g_free(gpadl); +} + +static VMBusGpadl *find_gpadl(VMBus *vmbus, uint32_t gpadl_id) +{ + VMBusGpadl *gpadl; + QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) { + if (gpadl->id =3D=3D gpadl_id) { + return gpadl; + } + } + return NULL; +} + +VMBusGpadl *vmbus_get_gpadl(VMBusChannel *chan, uint32_t gpadl_id) +{ + VMBusGpadl *gpadl =3D find_gpadl(chan->vmbus, gpadl_id); + if (gpadl) { + if (!gpadl->alive || gpadl->in_use) { + return NULL; + } + gpadl->in_use =3D true; + } + return gpadl; +} + +void vmbus_put_gpadl(VMBusGpadl *gpadl) +{ + gpadl->in_use =3D false; +} + +uint32_t vmbus_gpadl_len(VMBusGpadl *gpadl) +{ + return gpadl->num_gfns * TARGET_PAGE_SIZE; +} + +typedef struct GpadlIter { + VMBusGpadl *gpadl; + AddressSpace *as; + DMADirection dir; + uint32_t off; + /* cached mapping of the currently accessed page, up to page boundary = */ + void *map; +} GpadlIter; + +static void gpadl_iter_init(GpadlIter *iter, VMBusGpadl *gpadl, + AddressSpace *as, DMADirection dir, uint32_t o= ff) +{ + iter->gpadl =3D gpadl; + iter->as =3D as; + iter->dir =3D dir; + iter->off =3D off; + iter->map =3D NULL; +} + +static ssize_t gpadl_iter_rw(GpadlIter *iter, void *buf, uint32_t len) +{ + ssize_t ret =3D len; + + while (len) { + uint32_t off_pgoff =3D iter->off & ~TARGET_PAGE_MASK; + uint32_t pgleft =3D TARGET_PAGE_SIZE - off_pgoff; + uint32_t cplen =3D MIN(pgleft, len); + void *p; + + if (!iter->map) { + dma_addr_t maddr; + dma_addr_t mlen =3D pgleft; + uint32_t idx =3D iter->off >> TARGET_PAGE_BITS; + assert(idx < iter->gpadl->num_gfns); + + maddr =3D (iter->gpadl->gfns[idx] << TARGET_PAGE_BITS) | off_p= goff; + + iter->map =3D dma_memory_map(iter->as, maddr, &mlen, iter->dir= ); + if (mlen !=3D pgleft) { + dma_memory_unmap(iter->as, iter->map, mlen, iter->dir, 0); + return -EFAULT; + } + } + + p =3D (void *)(((uintptr_t)iter->map & TARGET_PAGE_MASK) | off_pgo= ff); + if (iter->dir =3D=3D DMA_DIRECTION_FROM_DEVICE) { + memcpy(p, buf, cplen); + } else { + memcpy(buf, p, cplen); + } + + buf +=3D cplen; + len -=3D cplen; + iter->off +=3D cplen; + if (cplen =3D=3D pgleft) { + uint32_t mlen =3D TARGET_PAGE_SIZE - + ((uintptr_t)iter->map & ~TARGET_PAGE_MASK); + dma_memory_unmap(iter->as, iter->map, mlen, iter->dir, mlen); + iter->map =3D NULL; + } + } + + return ret; +} + +static void gpadl_iter_done(GpadlIter *iter) +{ + if (iter->map) { + uint32_t map_pgoff =3D (uintptr_t)iter->map & ~TARGET_PAGE_MASK; + uint32_t off_pgoff =3D iter->off & ~TARGET_PAGE_MASK; + assert(map_pgoff <=3D off_pgoff); + dma_memory_unmap(iter->as, iter->map, TARGET_PAGE_SIZE - map_pgoff, + iter->dir, off_pgoff - map_pgoff); + } +} + +static void gpadl_iter_set(GpadlIter *iter, uint32_t new_off) +{ + if (iter->map) { + uint32_t map_pgoff =3D (uintptr_t)iter->map & ~TARGET_PAGE_MASK; + uint32_t off_pgoff =3D iter->off & ~TARGET_PAGE_MASK; + if ((iter->off & TARGET_PAGE_MASK) !=3D (new_off & TARGET_PAGE_MAS= K) || + (new_off & ~TARGET_PAGE_MASK) < map_pgoff) { + dma_memory_unmap(iter->as, iter->map, TARGET_PAGE_SIZE - map_p= goff, + iter->dir, off_pgoff - map_pgoff); + iter->map =3D NULL; + } + } + iter->off =3D new_off; +} + +ssize_t vmbus_iov_to_gpadl(VMBusChannel *chan, VMBusGpadl *gpadl, uint32_t= off, + const struct iovec *iov, size_t iov_cnt) +{ + GpadlIter iter; + size_t i; + ssize_t ret =3D 0; + + gpadl_iter_init(&iter, gpadl, chan->dev->dma_as, + DMA_DIRECTION_FROM_DEVICE, off); + for (i =3D 0; i < iov_cnt; i++) { + ret =3D gpadl_iter_rw(&iter, iov[i].iov_base, iov[i].iov_len); + if (ret < 0) { + goto out; + } + } +out: + gpadl_iter_done(&iter); + return ret; +} + +int vmbus_map_sgl(QEMUSGList *sgl, DMADirection dir, struct iovec *iov, + unsigned iov_cnt, size_t len, size_t off) +{ + int ret_cnt =3D 0, ret; + unsigned i; + ScatterGatherEntry *sg =3D sgl->sg; + + for (i =3D 0; i < sgl->nsg; i++) { + if (sg[i].len > off) { + break; + } + off -=3D sg[i].len; + } + for (; len && i < sgl->nsg; i++) { + dma_addr_t mlen =3D MIN(sg[i].len - off, len); + dma_addr_t addr =3D sg[i].base + off; + len -=3D mlen; + off =3D 0; + + for (; mlen; ret_cnt++) { + dma_addr_t l =3D mlen; + dma_addr_t a =3D addr; + + if (ret_cnt =3D=3D iov_cnt) { + ret =3D -ENOBUFS; + goto err; + } + + iov[ret_cnt].iov_base =3D dma_memory_map(sgl->as, a, &l, dir); + if (!l) { + ret =3D -EFAULT; + goto err; + } + iov[ret_cnt].iov_len =3D l; + addr +=3D l; + mlen -=3D l; + } + } + + return ret_cnt; +err: + vmbus_unmap_sgl(sgl, dir, iov, ret_cnt, 0); + return ret; +} + +void vmbus_unmap_sgl(QEMUSGList *sgl, DMADirection dir, struct iovec *iov, + unsigned iov_cnt, size_t accessed) +{ + unsigned i; + + for (i =3D 0; i < iov_cnt; i++) { + size_t acsd =3D MIN(accessed, iov[i].iov_len); + dma_memory_unmap(sgl->as, iov[i].iov_base, iov[i].iov_len, dir, ac= sd); + accessed -=3D acsd; + } +} + +static int gpadl_pre_load(void *opaque) +{ + memset(opaque, 0, sizeof(VMBusGpadl)); + return 0; +} + +static const VMStateDescription vmstate_gpadl =3D { + .name =3D "vmbus/gpadl", + .version_id =3D 0, + .minimum_version_id =3D 0, + .pre_load =3D gpadl_pre_load, + .fields =3D (VMStateField[]) { + VMSTATE_UINT32(id, VMBusGpadl), + VMSTATE_UINT32(child_relid, VMBusGpadl), + VMSTATE_UINT32(num_gfns, VMBusGpadl), + VMSTATE_UINT32(seen_gfns, VMBusGpadl), + VMSTATE_VARRAY_UINT32_ALLOC(gfns, VMBusGpadl, num_gfns, 0, + vmstate_info_uint64, uint64_t), + VMSTATE_BOOL(alive, VMBusGpadl), + VMSTATE_END_OF_LIST() + } +}; + +static vmbus_ring_buffer *ringbuf_map(const VMBusRingBuf *ringbuf) +{ + vmbus_ring_buffer *rb; + dma_addr_t mlen =3D sizeof(*rb); + + rb =3D dma_memory_map(ringbuf->as, ringbuf->rb_addr, &mlen, + DMA_DIRECTION_FROM_DEVICE); + if (mlen !=3D sizeof(*rb)) { + dma_memory_unmap(ringbuf->as, rb, mlen, DMA_DIRECTION_FROM_DEVICE,= 0); + return NULL; + } + return rb; +} + +static void ringbuf_unmap(const VMBusRingBuf *ringbuf, vmbus_ring_buffer *= rb, + bool dirty) +{ + dma_memory_unmap(ringbuf->as, rb, sizeof(*rb), 1, dirty ? sizeof(*rb) = : 0); +} + +static int ringbuf_init(VMBusRingBuf *ringbuf, VMBusChannel *chan, + DMADirection dir) +{ + int ret =3D 0; + int off_pg, len_pg; + vmbus_ring_buffer *rb; + VMBusGpadl *gpadl =3D chan->gpadl; + + if (dir =3D=3D DMA_DIRECTION_FROM_DEVICE) { + off_pg =3D chan->rb_rcv_offset; + len_pg =3D gpadl->num_gfns - chan->rb_rcv_offset; + } else { + off_pg =3D 0; + len_pg =3D chan->rb_rcv_offset; + } + + if (len_pg < 1) { + return -EINVAL; + } + + ringbuf->gpadl =3D gpadl; + ringbuf->as =3D chan->dev->dma_as; + ringbuf->dir =3D dir; + ringbuf->rb_addr =3D gpadl->gfns[off_pg] << TARGET_PAGE_BITS; + ringbuf->base =3D (off_pg + 1) << TARGET_PAGE_BITS; + ringbuf->len =3D (len_pg - 1) << TARGET_PAGE_BITS; + + rb =3D ringbuf_map(ringbuf); + if (!rb) { + return -EFAULT; + } + + if (dir =3D=3D DMA_DIRECTION_FROM_DEVICE) { + ringbuf->my_idx =3D rb->write_index; + rb->feature_bits |=3D VMBUS_RING_BUFFER_FEAT_PENDING_SZ; + ringbuf->pending_sz =3D rb->pending_send_sz; + } else { + ringbuf->my_idx =3D rb->read_index; + } + if (ringbuf->my_idx >=3D ringbuf->len) { + error_report("%s: inconsistent ring buffer: idx:%u len:%u", __func= __, + ringbuf->my_idx, ringbuf->len); + ret =3D -EIO; + goto out; + } + +out: + ringbuf_unmap(ringbuf, rb, true); + return ret; +} + +typedef struct VMBusRingIter { + VMBusRingBuf *ringbuf; + vmbus_ring_buffer *rb; + GpadlIter gpiter; +} VMBusRingIter; + +static int ring_iter_init(VMBusRingIter *iter, VMBusRingBuf *ringbuf) +{ + iter->ringbuf =3D ringbuf; + gpadl_iter_init(&iter->gpiter, ringbuf->gpadl, ringbuf->as, ringbuf->d= ir, + ringbuf->base + ringbuf->my_idx); + iter->rb =3D ringbuf_map(ringbuf); + return iter->rb ? 0 : -EFAULT; +} + +static uint32_t rb_avail(uint32_t my_idx, uint32_t other_idx, uint32_t len, + bool is_write) +{ + uint32_t ret =3D other_idx + len - my_idx - is_write; + if (ret >=3D len) { + ret -=3D len; + } + return ret; +} + +static bool ring_iter_rcv_update_idx(VMBusRingIter *iter) +{ + VMBusRingBuf *ringbuf =3D iter->ringbuf; + vmbus_ring_buffer *rb =3D iter->rb; + uint32_t old_idx =3D ringbuf->my_idx; + + assert(ringbuf->dir =3D=3D DMA_DIRECTION_TO_DEVICE); + + ringbuf->my_idx =3D iter->gpiter.off - ringbuf->base; + + /* + * make sure the data operation is finished before we update the index; + * pairs with (the guest-side equivalent of) the final smp_mb() in + * ringbuf_snd_request_room() + */ + smp_mb(); + + rb->read_index =3D ringbuf->my_idx; + smp_mb(); + + if (rb->interrupt_mask) { + return false; + } + + if (rb->feature_bits & VMBUS_RING_BUFFER_FEAT_PENDING_SZ) { + uint32_t other_idx; + uint32_t wanted =3D rb->pending_send_sz; + + /* barrier-less shortcut */ + if (!wanted) { + return false; + } + + /* pairs with smp_mb() in ringbuf_snd_request_room() */ + smp_rmb(); + other_idx =3D rb->write_index; + + /* other (write) side wasn't blocked on our (read) state */ + if (rb_avail(other_idx, old_idx, ringbuf->len, true) >=3D wanted) { + return false; + } + /* there's not enough space for the other (write) side */ + if (rb_avail(other_idx, ringbuf->my_idx, ringbuf->len, true) < + wanted) { + return false; + } + } + + return true; +} + +static bool ring_iter_snd_update_idx(VMBusRingIter *iter) +{ + VMBusRingBuf *ringbuf =3D iter->ringbuf; + vmbus_ring_buffer *rb =3D iter->rb; + uint32_t old_idx =3D ringbuf->my_idx; + uint32_t wlen; + + assert(ringbuf->dir =3D=3D DMA_DIRECTION_FROM_DEVICE); + + ringbuf->my_idx =3D iter->gpiter.off - ringbuf->base; + wlen =3D rb_avail(old_idx, ringbuf->my_idx, ringbuf->len, false); + + if (ringbuf->pending_sz) { + /* we didn't have enough room at ringbuf_snd_request_room() */ + assert(wlen < ringbuf->pending_sz); + /* successful send reduces our needs; inform the other side */ + ringbuf->pending_sz -=3D wlen; + rb->pending_send_sz =3D ringbuf->pending_sz; + } + + /* + * make sure the data operation is finished before we update the index; + * pairs with (the guest-side equivalent of) the barrier in + * ring_iter_avail() + */ + smp_mb(); + + rb->write_index =3D ringbuf->my_idx; + smp_mb(); + + if (rb->interrupt_mask) { + return false; + } + + /* + * other (read) side hasn't caught up with our (write) previous state + * so it's not blocked + */ + if (rb_avail(rb->read_index, ringbuf->my_idx, ringbuf->len, false) > + wlen) { + return false; + } + return true; +} + +static void ring_iter_done(VMBusRingIter *iter, bool dirty) +{ + gpadl_iter_done(&iter->gpiter); + ringbuf_unmap(iter->ringbuf, iter->rb, dirty); +} + +static uint32_t ring_iter_rcv_avail(VMBusRingIter *iter) +{ + uint32_t other_idx; + assert(iter->gpiter.dir =3D=3D DMA_DIRECTION_TO_DEVICE); + + other_idx =3D iter->rb->write_index; + /* + * pairs with (the guest-side equivalent of) the barrier at the + * beginning of ring_iter_snd_update_idx() + */ + smp_mb(); + + return rb_avail(iter->gpiter.off - iter->ringbuf->base, other_idx, + iter->ringbuf->len, false); +} + +static int ring_iter_rw(VMBusRingIter *iter, void *buf, uint32_t len) +{ + int ret1 =3D 0, ret2 =3D 0; + uint32_t remain =3D iter->ringbuf->len + iter->ringbuf->base - + iter->gpiter.off; + + if (len >=3D remain) { + ret1 =3D gpadl_iter_rw(&iter->gpiter, buf, remain); + if (ret1 < 0) { + return ret1; + } + assert(!iter->gpiter.map); + gpadl_iter_set(&iter->gpiter, iter->ringbuf->base); + buf +=3D remain; + len -=3D remain; + } + ret2 =3D gpadl_iter_rw(&iter->gpiter, buf, len); + if (ret2 < 0) { + return ret2; + } + return ret1 + ret2; +} + +static void ring_iter_set(VMBusRingIter *iter, uint32_t new_off) +{ + new_off +=3D iter->ringbuf->my_idx; + if (new_off >=3D iter->ringbuf->len) { + new_off -=3D iter->ringbuf->len; + } + gpadl_iter_set(&iter->gpiter, iter->ringbuf->base + new_off); +} + +static int ringbuf_snd_request_room(VMBusRingBuf *ringbuf, uint32_t needed) +{ + uint32_t avail; + bool dirty =3D false; + vmbus_ring_buffer *rb; + + assert(ringbuf->dir =3D=3D DMA_DIRECTION_FROM_DEVICE); + + rb =3D ringbuf_map(ringbuf); + if (!rb) { + return -EFAULT; + } + + avail =3D rb_avail(ringbuf->my_idx, rb->read_index, ringbuf->len, true= ); + + if (needed <=3D avail) { + needed =3D 0; + } + + if (needed !=3D ringbuf->pending_sz) { + ringbuf->pending_sz =3D needed; + /* pairs with smp_rmb() in ring_iter_rcv_update_idx() */ + smp_mb(); + rb->pending_send_sz =3D needed; + dirty =3D true; + } + /* + * pairs with (the guest-side equivalent of) smp_mb() at the + * beginning of ring_iter_rcv_update_idx() + */ + smp_mb(); + + ringbuf_unmap(ringbuf, rb, dirty); + return needed ? -ENOSPC : 0; +} + + +VMBusDevice *vmbus_channel_device(VMBusChannel *chan) +{ + return chan->dev; +} + +VMBusChannel *vmbus_device_channel(VMBusDevice *dev, uint32_t chan_idx) +{ + if (chan_idx >=3D dev->num_channels) { + return NULL; + } + return &dev->channels[chan_idx]; +} + +uint32_t vmbus_channel_idx(VMBusChannel *chan) +{ + return chan - chan->dev->channels; +} + +void vmbus_notify_channel(VMBusChannel *chan) +{ + event_notifier_set(&chan->notifier); +} + +static bool channel_broken(VMBusChannel *chan) +{ + return !chan->gpadl; +} + +static int signal_channel(VMBusChannel *chan) +{ + int res =3D 0; + unsigned long *int_map, mask; + unsigned idx; + hwaddr addr =3D chan->vmbus->int_page_gpa; + hwaddr len =3D TARGET_PAGE_SIZE / 2, dirty =3D 0; + + trace_vmbus_signal_channel(chan->relid); + + if (!addr) { + return hyperv_set_event_flag(chan->notify_route, chan->relid); + } + + int_map =3D cpu_physical_memory_map(addr, &len, 1); + if (len !=3D TARGET_PAGE_SIZE / 2) { + res =3D -ENXIO; + goto unmap; + } + + idx =3D BIT_WORD(chan->relid); + mask =3D BIT_MASK(chan->relid); + if ((atomic_fetch_or(&int_map[idx], mask) & mask) !=3D mask) { + res =3D hyperv_sint_route_set_sint(chan->notify_route); + dirty =3D len; + } + +unmap: + cpu_physical_memory_unmap(int_map, len, 1, dirty); + return res; +} + +int vmbus_channel_send(VMBusChannel *chan, uint16_t pkt_type, + void *desc, uint32_t desclen, + void *msg, uint32_t msglen, + bool need_comp, uint64_t transaction_id) +{ + int ret =3D 0; + vmbus_packet_hdr hdr; + uint32_t totlen; + VMBusRingIter iter; + + if (chan->state !=3D VMCHAN_OPEN) { + return -EINVAL; + } + + ret =3D ring_iter_init(&iter, &chan->ringbuf_snd); + if (ret) { + return ret; + } + + hdr.type =3D pkt_type; + hdr.offset_qwords =3D sizeof(hdr) / sizeof(uint64_t) + + DIV_ROUND_UP(desclen, sizeof(uint64_t)); + hdr.len_qwords =3D hdr.offset_qwords + + DIV_ROUND_UP(msglen, sizeof(uint64_t)); + hdr.flags =3D need_comp ? VMBUS_PACKET_FLAG_REQUEST_COMPLETION : 0; + hdr.transaction_id =3D transaction_id; + totlen =3D (hdr.len_qwords + 1) * sizeof(uint64_t); + + assert(totlen <=3D chan->snd_reserved); + + ret =3D ring_iter_rw(&iter, &hdr, sizeof(hdr)); + if (ret < 0) { + goto out; + } + if (desclen) { + assert(desc); + ret =3D ring_iter_rw(&iter, desc, desclen); + if (ret < 0) { + goto out; + } + ring_iter_set(&iter, hdr.offset_qwords * sizeof(uint64_t)); + } + ret =3D ring_iter_rw(&iter, msg, msglen); + if (ret < 0) { + goto out; + } + ring_iter_set(&iter, totlen); + + if (ring_iter_snd_update_idx(&iter)) { + signal_channel(chan); + } + chan->snd_reserved -=3D totlen; +out: + ring_iter_done(&iter, ret >=3D 0); + return ret; +} + +int vmbus_chan_send_completion(VMBusChanReq *req) +{ + assert(req->comp); + return vmbus_channel_send(req->chan, VMBUS_PACKET_COMP, NULL, 0, req->= comp, + req->msglen, false, req->transaction_id); +} + +int vmbus_channel_reserve(VMBusChannel *chan, + uint32_t desclen, uint32_t msglen) +{ + uint32_t needed =3D chan->snd_reserved + + sizeof(vmbus_packet_hdr) + + ROUND_UP(desclen, sizeof(uint64_t)) + + ROUND_UP(msglen, sizeof(uint64_t)) + + sizeof(uint64_t); + + int ret =3D ringbuf_snd_request_room(&chan->ringbuf_snd, needed); + + if (!ret) { + chan->snd_reserved =3D needed; + } + return ret; +} + +static int sgl_from_gpa_ranges(VMBusRingIter *iter, uint32_t len, + VMBusChanReq *req) +{ + int ret; + vmbus_pkt_gpa_direct hdr; + hwaddr curaddr =3D 0; + hwaddr curlen =3D 0; + int num; + + if (len < sizeof(hdr)) { + return -EIO; + } + ret =3D ring_iter_rw(iter, &hdr, sizeof(hdr)); + if (ret < 0) { + return ret; + } + len -=3D sizeof(hdr); + + num =3D (len - hdr.rangecount * sizeof(vmbus_gpa_range)) / sizeof(uint= 64_t); + if (num < 0) { + return -EIO; + } + qemu_sglist_init(&req->sgl, DEVICE(req->chan->dev), num, iter->gpiter.= as); + + for (; hdr.rangecount; hdr.rangecount--) { + vmbus_gpa_range range; + + if (len < sizeof(range)) { + goto eio; + } + ret =3D ring_iter_rw(iter, &range, sizeof(range)); + if (ret < 0) { + goto err; + } + len -=3D sizeof(range); + + if (range.byte_offset & TARGET_PAGE_MASK) { + goto eio; + } + + for (; range.byte_count; range.byte_offset =3D 0) { + uint64_t paddr; + uint32_t plen =3D MIN(range.byte_count, + TARGET_PAGE_SIZE - range.byte_offset); + + if (len < sizeof(uint64_t)) { + goto eio; + } + ret =3D ring_iter_rw(iter, &paddr, sizeof(paddr)); + if (ret < 0) { + goto err; + } + len -=3D sizeof(uint64_t); + paddr <<=3D TARGET_PAGE_BITS; + paddr |=3D range.byte_offset; + range.byte_count -=3D plen; + + if (curaddr + curlen =3D=3D paddr) { + /* consecutive fragments - join */ + curlen +=3D plen; + } else { + if (curlen) { + qemu_sglist_add(&req->sgl, curaddr, curlen); + } + + curaddr =3D paddr; + curlen =3D plen; + } + } + } + + if (curlen) { + qemu_sglist_add(&req->sgl, curaddr, curlen); + } + + return 0; +eio: + ret =3D -EIO; +err: + qemu_sglist_destroy(&req->sgl); + return ret; +} + +static VMBusChanReq *vmbus_alloc_req(VMBusChannel *chan, + uint32_t size, uint16_t pkt_type, + uint32_t msglen, uint64_t transaction= _id, + bool with_comp) +{ + VMBusChanReq *req; + uint32_t msgoff =3D QEMU_ALIGN_UP(size, __alignof__(*req->msg)); + uint32_t compoff =3D QEMU_ALIGN_UP(msgoff + msglen, __alignof__(*req->= comp)); + uint32_t complen =3D with_comp ? msglen : 0; + uint32_t totlen =3D compoff + complen; + + req =3D g_malloc0(totlen); + req->chan =3D chan; + req->pkt_type =3D pkt_type; + req->msg =3D (void *)req + msgoff; + req->msglen =3D msglen; + req->transaction_id =3D transaction_id; + req->comp =3D with_comp ? ((void *)req + compoff) : NULL; + return req; +} + +void *vmbus_channel_recv(VMBusChannel *chan, uint32_t size) +{ + VMBusRingIter iter; + vmbus_packet_hdr hdr =3D {0}; + VMBusChanReq *req =3D NULL; + uint32_t avail; + uint32_t pktlen, msglen, msgoff, desclen; + bool with_comp; + + assert(size >=3D sizeof(*req)); + + if (chan->state !=3D VMCHAN_OPEN) { + return NULL; + } + + if (ring_iter_init(&iter, &chan->ringbuf_rcv)) { + return NULL; + } + + avail =3D ring_iter_rcv_avail(&iter); + if (avail < sizeof(hdr)) { + goto err; + } + + if (ring_iter_rw(&iter, &hdr, sizeof(hdr)) < 0) { + goto err; + } + + pktlen =3D hdr.len_qwords * sizeof(uint64_t); + if (pktlen + sizeof(uint64_t) > avail) { + goto err; + } + + msgoff =3D hdr.offset_qwords * sizeof(uint64_t); + if (msgoff > pktlen || msgoff < sizeof(hdr)) { + error_report("%s: malformed packet: %u %u", __func__, msgoff, pktl= en); + goto err; + } + + msglen =3D pktlen - msgoff; + + with_comp =3D hdr.flags & VMBUS_PACKET_FLAG_REQUEST_COMPLETION; + if (with_comp && vmbus_channel_reserve(chan, 0, msglen)) { + goto err; + } + + req =3D vmbus_alloc_req(chan, size, hdr.type, msglen, hdr.transaction_= id, + with_comp); + + switch (hdr.type) { + case VMBUS_PACKET_DATA_USING_GPA_DIRECT: + desclen =3D msgoff - sizeof(hdr); + if (sgl_from_gpa_ranges(&iter, desclen, req) < 0) { + error_report("%s: failed to convert GPA ranges to SGL", __func= __); + goto err; + } + break; + case VMBUS_PACKET_DATA_INBAND: + case VMBUS_PACKET_COMP: + break; + default: + error_report("%s: unexpected msg type: %x", __func__, hdr.type); + goto err; + } + + ring_iter_set(&iter, msgoff); + if (ring_iter_rw(&iter, req->msg, msglen) < 0) { + goto err; + } + ring_iter_set(&iter, pktlen + sizeof(uint64_t)); + + if (ring_iter_rcv_update_idx(&iter)) { + signal_channel(chan); + } + ring_iter_done(&iter, true); + return req; +err: + vmbus_release_req(req); + ring_iter_done(&iter, false); + return NULL; +} + +void vmbus_release_req(void *req) +{ + VMBusChanReq *r =3D req; + + if (!req) { + return; + } + + if (r->sgl.dev) { + qemu_sglist_destroy(&r->sgl); + } + g_free(req); +} + +static const VMStateDescription vmstate_sgent =3D { + .name =3D "vmbus/sgentry", + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D (VMStateField[]) { + VMSTATE_UINT64(base, ScatterGatherEntry), + VMSTATE_UINT64(len, ScatterGatherEntry), + VMSTATE_END_OF_LIST() + } +}; + +typedef struct VMBusChanReqSave { + uint16_t chan_idx; + uint16_t pkt_type; + uint32_t msglen; + void *msg; + uint64_t transaction_id; + bool with_comp; + uint32_t num; + ScatterGatherEntry *sgl; +} VMBusChanReqSave; + +static const VMStateDescription vmstate_vmbus_chan_req =3D { + .name =3D "vmbus/vmbus_chan_req", + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D (VMStateField[]) { + VMSTATE_UINT16(chan_idx, VMBusChanReqSave), + VMSTATE_UINT16(pkt_type, VMBusChanReqSave), + VMSTATE_UINT32(msglen, VMBusChanReqSave), + VMSTATE_VBUFFER_ALLOC_UINT32(msg, VMBusChanReqSave, 0, NULL, msgle= n), + VMSTATE_UINT64(transaction_id, VMBusChanReqSave), + VMSTATE_BOOL(with_comp, VMBusChanReqSave), + VMSTATE_UINT32(num, VMBusChanReqSave), + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(sgl, VMBusChanReqSave, num, + vmstate_sgent, ScatterGatherE= ntry), + VMSTATE_END_OF_LIST() + } +}; + +void vmbus_save_req(QEMUFile *f, VMBusChanReq *req) +{ + VMBusChanReqSave req_save; + + req_save.chan_idx =3D req->chan->subchan_idx; + req_save.pkt_type =3D req->pkt_type; + req_save.msglen =3D req->msglen; + req_save.msg =3D req->msg; + req_save.transaction_id =3D req->transaction_id; + req_save.with_comp =3D !!req->comp; + req_save.num =3D req->sgl.nsg; + req_save.sgl =3D g_memdup(req->sgl.sg, + req_save.num * sizeof(ScatterGatherEntry)); + + vmstate_save_state(f, &vmstate_vmbus_chan_req, &req_save, NULL); + + g_free(req_save.sgl); +} + +void *vmbus_load_req(QEMUFile *f, VMBusDevice *dev, uint32_t size) +{ + VMBusChanReqSave req_save; + VMBusChanReq *req =3D NULL; + VMBusChannel *chan =3D NULL; + uint32_t i; + + vmstate_load_state(f, &vmstate_vmbus_chan_req, &req_save, 0); + + if (req_save.chan_idx >=3D dev->num_channels) { + error_report("%s: %u(chan_idx) > %u(num_channels)", __func__, + req_save.chan_idx, dev->num_channels); + goto out; + } + chan =3D &dev->channels[req_save.chan_idx]; + + if (vmbus_channel_reserve(chan, 0, req_save.msglen)) { + goto out; + } + + req =3D vmbus_alloc_req(chan, size, req_save.pkt_type, req_save.msglen, + req_save.transaction_id, req_save.with_comp); + if (req_save.msglen) { + memcpy(req->msg, req_save.msg, req_save.msglen); + } + + for (i =3D 0; i < req_save.num; i++) { + qemu_sglist_add(&req->sgl, req_save.sgl[i].base, req_save.sgl[i].l= en); + } + +out: + if (req_save.msglen) { + g_free(req_save.msg); + } + if (req_save.num) { + g_free(req_save.sgl); + } + return req; +} + +static void channel_event_cb(EventNotifier *e) +{ + VMBusChannel *chan =3D container_of(e, VMBusChannel, notifier); + if (event_notifier_test_and_clear(e)) { + chan->notify_cb(chan); + } +} + +static int register_chan_ids(VMBusChannel *chan) +{ + int ret; + + ret =3D hyperv_set_event_flag_handler(chan->connid, &chan->notifier); + if (ret) { + return ret; + } + + set_bit(chan->relid, chan->vmbus->chan_relid_bitmap); + set_bit(chan->connid, chan->vmbus->connection_id_bitmap); + return 0; +} + +static void unregister_chan_ids(VMBusChannel *chan) +{ + clear_bit(chan->connid, chan->vmbus->connection_id_bitmap); + clear_bit(chan->relid, chan->vmbus->chan_relid_bitmap); + hyperv_set_event_flag_handler(chan->connid, NULL); +} + +static void init_channel(VMBus *vmbus, VMBusDevice *dev, VMBusDeviceClass = *vdc, + VMBusChannel *chan, uint16_t idx, Error **errp) +{ + int res; + + chan->dev =3D dev; + chan->notify_cb =3D vdc->chan_notify_cb; + chan->subchan_idx =3D idx; + chan->vmbus =3D vmbus; + + chan->relid =3D find_next_zero_bit(vmbus->chan_relid_bitmap, + VMBUS_CHAN_RELID_COUNT, + VMBUS_CHAN_FIRST_RELID); + if (chan->relid =3D=3D VMBUS_CHAN_RELID_COUNT) { + error_setg(errp, "no spare relid"); + return; + } + + chan->connid =3D find_next_zero_bit(vmbus->connection_id_bitmap, + VMBUS_CHAN_CONNID_COUNT, + VMBUS_CHAN_FIRST_CONNID); + if (chan->connid =3D=3D VMBUS_CHAN_CONNID_COUNT) { + error_setg(errp, "no spare connid"); + return; + } + + res =3D event_notifier_init(&chan->notifier, 0); + if (res) { + error_setg(errp, "event_notifier_init: %d", res); + return; + } + + event_notifier_set_handler(&chan->notifier, channel_event_cb); + + res =3D register_chan_ids(chan); + if (res) { + error_setg(errp, "register_chan_ids: %d", res); + event_notifier_cleanup(&chan->notifier); + return; + } +} + +static void deinit_channel(VMBusChannel *chan) +{ + assert(chan->state =3D=3D VMCHAN_INIT); + QTAILQ_REMOVE(&chan->vmbus->channel_list, chan, link); + unregister_chan_ids(chan); + event_notifier_cleanup(&chan->notifier); +} + +static void create_channels(VMBus *vmbus, VMBusDevice *dev, Error **errp) +{ + uint16_t i; + VMBusDeviceClass *vdc =3D VMBUS_DEVICE_GET_CLASS(dev); + Error *err =3D NULL; + + dev->num_channels =3D vdc->num_channels ? vdc->num_channels(dev) : 1; + if (dev->num_channels < 1) { + error_setg(&err, "invalid #channels: %u", dev->num_channels); + goto error_out; + } + + dev->channels =3D g_new0(VMBusChannel, dev->num_channels); + for (i =3D 0; i < dev->num_channels; i++) { + init_channel(vmbus, dev, vdc, &dev->channels[i], i, &err); + if (err) { + goto err_init; + } + + /* + * The guest drivers depend on the device subchannels (channels #1= +) to + * be offered after the main channel (channel #0) of that device. = To + * ensure that, record the channels on the channel list in the ord= er + * they appear within the device. + */ + QTAILQ_INSERT_TAIL(&vmbus->channel_list, &dev->channels[i], link); + } + + return; + +err_init: + while (i--) { + deinit_channel(&dev->channels[i]); + } +error_out: + error_propagate(errp, err); +} + +static void free_channels(VMBus *vmbus, VMBusDevice *dev) +{ + uint16_t i; + for (i =3D 0; i < dev->num_channels; i++) { + deinit_channel(&dev->channels[i]); + } + g_free(dev->channels); +} + +static HvSintRoute *make_sint_route(VMBus *vmbus, uint32_t vp_index) +{ + VMBusChannel *chan; + + if (vp_index =3D=3D vmbus->target_vp) { + hyperv_sint_route_ref(vmbus->sint_route); + return vmbus->sint_route; + } + + QTAILQ_FOREACH(chan, &vmbus->channel_list, link) { + if (chan->target_vp =3D=3D vp_index && chan->notify_route) { + hyperv_sint_route_ref(chan->notify_route); + return chan->notify_route; + } + } + + return hyperv_sint_route_new(vp_index, VMBUS_SINT, NULL, NULL); +} + +static void open_channel(VMBusChannel *chan) +{ + VMBusDeviceClass *vdc =3D VMBUS_DEVICE_GET_CLASS(chan->dev); + + chan->gpadl =3D vmbus_get_gpadl(chan, chan->rb_gpadl); + if (!chan->gpadl) { + return; + } + + if (ringbuf_init(&chan->ringbuf_rcv, chan, DMA_DIRECTION_TO_DEVICE) || + ringbuf_init(&chan->ringbuf_snd, chan, DMA_DIRECTION_FROM_DEVICE))= { + goto put_gpadl; + } + + chan->notify_route =3D make_sint_route(chan->vmbus, chan->target_vp); + if (!chan->notify_route) { + goto put_gpadl; + } + + if (vdc->open_channel && vdc->open_channel(chan->dev)) { + goto err_vdc_open; + } + + return; + +err_vdc_open: + hyperv_sint_route_unref(chan->notify_route); +put_gpadl: + vmbus_put_gpadl(chan->gpadl); + chan->gpadl =3D NULL; +} + +static void close_channel(VMBusChannel *chan) +{ + VMBusDeviceClass *vdc =3D VMBUS_DEVICE_GET_CLASS(chan->dev); + + if (chan->state !=3D VMCHAN_OPEN) { + return; + } + + if (vdc->close_channel) { + vdc->close_channel(chan->dev); + } + + vmbus_put_gpadl(chan->gpadl); + chan->gpadl =3D NULL; + chan->state =3D VMCHAN_INIT; + chan->snd_reserved =3D 0; + hyperv_sint_route_unref(chan->notify_route); + chan->notify_route =3D NULL; +} + +static int channel_post_load(void *opaque, int version_id) +{ + VMBusChannel *chan =3D opaque; + + if (chan->state =3D=3D VMCHAN_OPENING || chan->state =3D=3D VMCHAN_OPE= N) { + open_channel(chan); + } + + /* + * if the channel was still transitioning we'll report failure via rep= ly + * message, no need to error out now + */ + if (chan->state =3D=3D VMCHAN_OPEN && channel_broken(chan)) { + chan->state =3D VMCHAN_INIT; + return -1; + } + + if (chan->state =3D=3D VMCHAN_OPEN) { + /* resume processing on the guest side if it missed the notificati= on */ + hyperv_sint_route_set_sint(chan->notify_route); + } + return 0; +} + +static const VMStateDescription vmstate_channel =3D { + .name =3D "vmbus/channel", + .version_id =3D 0, + .minimum_version_id =3D 0, + .post_load =3D channel_post_load, + .fields =3D (VMStateField[]) { + VMSTATE_UINT32(relid, VMBusChannel), + VMSTATE_UINT16(subchan_idx, VMBusChannel), + VMSTATE_UINT32(connid, VMBusChannel), + VMSTATE_UINT32(open_id, VMBusChannel), + VMSTATE_UINT32(target_vp, VMBusChannel), + VMSTATE_UINT32(rb_gpadl, VMBusChannel), + VMSTATE_UINT32(rb_rcv_offset, VMBusChannel), + VMSTATE_UINT32(offer_state, VMBusChannel), + VMSTATE_UINT32(state, VMBusChannel), + VMSTATE_END_OF_LIST() + } +}; + +static VMBusChannel *find_channel(VMBus *vmbus, uint32_t relid) +{ + VMBusChannel *chan; + QTAILQ_FOREACH(chan, &vmbus->channel_list, link) { + if (chan->relid =3D=3D relid) { + return chan; + } + } + return NULL; +} + +static int enqueue_incoming_message(VMBus *vmbus, + const struct hyperv_post_message_input= *msg) +{ + uint8_t idx, prev_size; + + qemu_mutex_lock(&vmbus->rx_queue_lock); + + if (vmbus->rx_queue_size =3D=3D VMBUS_RX_QUEUE_CAPACITY) { + qemu_mutex_unlock(&vmbus->rx_queue_lock); + return -ENOBUFS; + } + + prev_size =3D vmbus->rx_queue_size; + idx =3D (vmbus->rx_queue_head + vmbus->rx_queue_size) % + VMBUS_RX_QUEUE_CAPACITY; + memcpy(&vmbus->rx_queue[idx], msg, sizeof(*msg)); + vmbus->rx_queue_size++; + + qemu_mutex_unlock(&vmbus->rx_queue_lock); + + /* only need to resched if the queue was empty before */ + if (!prev_size) { + vmbus_resched(vmbus); + } + + return 0; +} + +static uint16_t vmbus_recv_message(const struct hyperv_post_message_input = *msg, + void *data) +{ + VMBus *vmbus =3D data; + struct vmbus_message_header *vmbus_msg; + + if (msg->message_type !=3D HV_MESSAGE_VMBUS) { + return HV_STATUS_INVALID_HYPERCALL_INPUT; + } + + if (msg->payload_size < sizeof(struct vmbus_message_header)) { + return HV_STATUS_INVALID_HYPERCALL_INPUT; + } + + vmbus_msg =3D (struct vmbus_message_header*)msg->payload; + + trace_vmbus_recv_message(vmbus_msg->message_type, msg->payload_size); + + if (vmbus_msg->message_type =3D=3D VMBUS_MSG_INVALID || + vmbus_msg->message_type >=3D VMBUS_MSG_COUNT) { + error_report("vmbus: unknown message type %#x", + vmbus_msg->message_type); + return HV_STATUS_INVALID_HYPERCALL_INPUT; + } + + if (enqueue_incoming_message(vmbus, msg)) { + return HV_STATUS_INSUFFICIENT_BUFFERS; + } + return HV_STATUS_SUCCESS; +} + +static bool vmbus_initialized(VMBus *vmbus) +{ + return vmbus->version > 0 && vmbus->version <=3D VMBUS_VERSION_CURRENT; +} + +static void vmbus_reset_all(VMBus *vmbus) +{ + qbus_reset_all(BUS(vmbus)); +} + +static void post_msg(VMBus *vmbus, void *msgdata, uint32_t msglen) +{ + int ret; + struct hyperv_message msg =3D { + .header.message_type =3D HV_MESSAGE_VMBUS, + }; + + assert(!vmbus->msg_in_progress); + assert(msglen <=3D sizeof(msg.payload)); + assert(msglen >=3D sizeof(struct vmbus_message_header)); + + vmbus->msg_in_progress =3D true; + + trace_vmbus_post_msg(((struct vmbus_message_header *)msgdata)->message= _type, + msglen); + + memcpy(msg.payload, msgdata, msglen); + msg.header.payload_size =3D ROUND_UP(msglen, VMBUS_MESSAGE_SIZE_ALIGN); + + ret =3D hyperv_post_msg(vmbus->sint_route, &msg); + if (ret =3D=3D 0 || ret =3D=3D -EAGAIN) { + return; + } + + error_report("message delivery fatal failure: %d; aborting vmbus", ret= ); + vmbus_reset_all(vmbus); +} + +static int vmbus_init(VMBus *vmbus) +{ + if (vmbus->target_vp !=3D (uint32_t)-1) { + vmbus->sint_route =3D hyperv_sint_route_new(vmbus->target_vp, VMBU= S_SINT, + vmbus_msg_cb, vmbus); + if (!vmbus->sint_route) { + error_report("failed to set up SINT route"); + return -ENOMEM; + } + } + return 0; +} + +static void vmbus_deinit(VMBus *vmbus) +{ + while (!QTAILQ_EMPTY(&vmbus->gpadl_list)) { + free_gpadl(vmbus, QTAILQ_FIRST(&vmbus->gpadl_list)); + } + + hyperv_sint_route_unref(vmbus->sint_route); + + vmbus->sint_route =3D NULL; + vmbus->int_page_gpa =3D 0; + vmbus->target_vp =3D (uint32_t)-1; + vmbus->version =3D 0; + vmbus->state =3D VMBUS_LISTEN; + vmbus->msg_in_progress =3D false; +} + +static void handle_initiate_contact(VMBus *vmbus, + vmbus_message_initiate_contact *msg, + uint32_t msglen) +{ + if (msglen < sizeof(*msg)) { + return; + } + + trace_vmbus_initiate_contact(msg->version_requested, msg->target_vcpu, + msg->monitor_page1, msg->monitor_page2, msg->interrupt_page); + + /* + * Reset vmbus on INITIATE_CONTACT regardless of its previous state. + * Useful, in particular, with vmbus-aware BIOS which can't shut vmbus= down + * before handing over to OS loader. + */ + vmbus_reset_all(vmbus); + + vmbus->target_vp =3D msg->target_vcpu; + vmbus->version =3D msg->version_requested; + if (vmbus->version < VMBUS_VERSION_WIN8) { + /* linux passes interrupt page even when it doesn't need it */ + vmbus->int_page_gpa =3D msg->interrupt_page; + } + vmbus->state =3D VMBUS_HANDSHAKE; + + if (vmbus_init(vmbus)) { + error_report("failed to init vmbus; aborting"); + vmbus_deinit(vmbus); + return; + } +} + +static void send_handshake(VMBus *vmbus) +{ + struct vmbus_message_version_response msg =3D { + .header.message_type =3D VMBUS_MSG_VERSION_RESPONSE, + .version_supported =3D vmbus_initialized(vmbus), + }; + + post_msg(vmbus, &msg, sizeof(msg)); +} + +static void complete_handshake(VMBus *vmbus) +{ + vmbus->state =3D VMBUS_LISTEN; +} + +static void handle_request_offers(VMBus *vmbus, void *msgdata, uint32_t ms= glen) +{ + VMBusChannel *chan; + + if (!vmbus_initialized(vmbus)) { + return; + } + + QTAILQ_FOREACH(chan, &vmbus->channel_list, link) { + if (chan->offer_state =3D=3D VMOFFER_INIT) { + chan->offer_state =3D VMOFFER_SENDING; + break; + } + } + + vmbus->state =3D VMBUS_OFFER; +} + +static void send_offer(VMBus *vmbus) +{ + VMBusChannel *chan; + struct vmbus_message_header alloffers_msg =3D { + .message_type =3D VMBUS_MSG_ALLOFFERS_DELIVERED, + }; + + QTAILQ_FOREACH(chan, &vmbus->channel_list, link) { + if (chan->offer_state =3D=3D VMOFFER_SENDING) { + VMBusDeviceClass *vdc =3D VMBUS_DEVICE_GET_CLASS(chan->dev); + QemuUUID classid =3D vdc->classid; + QemuUUID instanceid =3D chan->dev->instanceid; + struct vmbus_message_offer_channel msg =3D { + .header.message_type =3D VMBUS_MSG_OFFERCHANNEL, + .child_relid =3D chan->relid, + .connection_id =3D chan->connid, + .channel_flags =3D vdc->channel_flags, + .mmio_size_mb =3D vdc->mmio_size_mb, + .sub_channel_index =3D vmbus_channel_idx(chan), + .interrupt_flags =3D VMBUS_OFFER_INTERRUPT_DEDICATED, + }; + + /* Hyper-V wants LE GUIDs */ + classid =3D qemu_uuid_bswap(classid); + memcpy(msg.type_uuid, &classid, sizeof(classid)); + instanceid =3D qemu_uuid_bswap(instanceid); + memcpy(msg.instance_uuid, &instanceid, sizeof(instanceid)); + + post_msg(vmbus, &msg, sizeof(msg)); + return; + } + } + + /* no more offers, send completion message */ + post_msg(vmbus, &alloffers_msg, sizeof(alloffers_msg)); +} + +static void complete_offer(VMBus *vmbus) +{ + VMBusChannel *chan; + + QTAILQ_FOREACH(chan, &vmbus->channel_list, link) { + if (chan->offer_state =3D=3D VMOFFER_SENDING) { + chan->offer_state =3D VMOFFER_SENT; + chan =3D QTAILQ_NEXT(chan, link); + if (chan) { + chan->offer_state =3D VMOFFER_SENDING; + } + /* more offers or terminator, no state transition */ + return; + } + } + /* no more offers, can listen again */ + vmbus->state =3D VMBUS_LISTEN; +} + + +static void handle_gpadl_header(VMBus *vmbus, vmbus_message_gpadl_header *= msg, + uint32_t msglen) +{ + VMBusGpadl *gpadl; + uint32_t num_gfns_tot, num_gfns, i; + + if (msglen < sizeof(*msg) || !vmbus_initialized(vmbus)) { + return; + } + + if (msg->rangecount !=3D 1 || + (msg->range.byte_count & (TARGET_PAGE_SIZE - 1)) || + find_gpadl(vmbus, msg->gpadl_id)) { + /* create a temporary broken GPADL to reply with failure */ + num_gfns_tot =3D 0; + } else { + num_gfns_tot =3D msg->range.byte_count >> TARGET_PAGE_BITS; + } + + gpadl =3D create_gpadl(vmbus, msg->gpadl_id, msg->child_relid, num_gfn= s_tot); + + num_gfns =3D (msglen - sizeof(*msg)) / sizeof(uint64_t); + if (num_gfns > num_gfns_tot) { + num_gfns =3D num_gfns_tot; + } + + for (i =3D 0; i < num_gfns; i++) { + gpadl->gfns[gpadl->seen_gfns++] =3D msg->range.pfn_array[i]; + } + + if (gpadl_full(gpadl)) { + vmbus->state =3D VMBUS_CREATE_GPADL; + } +} + +static void handle_gpadl_body(VMBus *vmbus, vmbus_message_gpadl_body *msg, + uint32_t msglen) +{ + VMBusGpadl *gpadl; + uint32_t num_gfns_left, num_gfns, i; + + if (msglen < sizeof(*msg) || !vmbus_initialized(vmbus)) { + return; + } + + gpadl =3D find_gpadl(vmbus, msg->gpadl_id); + if (!gpadl) { + /* create a temporary broken GPADL to reply with failure */ + gpadl =3D create_gpadl(vmbus, msg->gpadl_id, 0, 0); + } + + num_gfns_left =3D gpadl->num_gfns - gpadl->seen_gfns; + num_gfns =3D (msglen - sizeof(*msg)) / sizeof(uint64_t); + assert(num_gfns_left); + if (num_gfns > num_gfns_left) { + num_gfns =3D num_gfns_left; + } + + for (i =3D 0; i < num_gfns; i++) { + gpadl->gfns[gpadl->seen_gfns++] =3D msg->pfn_array[i]; + } + + if (gpadl_full(gpadl)) { + vmbus->state =3D VMBUS_CREATE_GPADL; + } +} + +static void send_create_gpadl(VMBus *vmbus) +{ + VMBusGpadl *gpadl; + + QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) { + if (gpadl_full(gpadl) && !gpadl->alive) { + struct vmbus_message_gpadl_created msg =3D { + .header.message_type =3D VMBUS_MSG_GPADL_CREATED, + .gpadl_id =3D gpadl->id, + .child_relid =3D gpadl->child_relid, + .status =3D gpadl_broken(gpadl), + }; + + post_msg(vmbus, &msg, sizeof(msg)); + return; + } + } + + assert(false); +} + +static void complete_create_gpadl(VMBus *vmbus) +{ + VMBusGpadl *gpadl; + + QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) { + if (gpadl_full(gpadl) && !gpadl->alive) { + if (gpadl_broken(gpadl)) { + free_gpadl(vmbus, gpadl); + } else { + gpadl->alive =3D true; + } + + vmbus->state =3D VMBUS_LISTEN; + return; + } + } + + assert(false); +} + +static void handle_gpadl_teardown(VMBus *vmbus, + vmbus_message_gpadl_teardown *msg, + uint32_t msglen) +{ + VMBusGpadl *gpadl; + + if (msglen < sizeof(*msg) || !vmbus_initialized(vmbus)) { + return; + } + + gpadl =3D find_gpadl(vmbus, msg->gpadl_id); + if (!gpadl || gpadl->in_use) { + return; + } + + gpadl->alive =3D false; + vmbus->state =3D VMBUS_TEARDOWN_GPADL; +} + +static void send_teardown_gpadl(VMBus *vmbus) +{ + VMBusGpadl *gpadl; + + QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) { + if (gpadl_full(gpadl) && !gpadl->alive) { + struct vmbus_message_gpadl_torndown msg =3D { + .header.message_type =3D VMBUS_MSG_GPADL_TORNDOWN, + .gpadl_id =3D gpadl->id, + }; + + post_msg(vmbus, &msg, sizeof(msg)); + return; + } + } + + assert(false); +} + +static void complete_teardown_gpadl(VMBus *vmbus) +{ + VMBusGpadl *gpadl; + + QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) { + if (gpadl_full(gpadl) && !gpadl->alive) { + free_gpadl(vmbus, gpadl); + vmbus->state =3D VMBUS_LISTEN; + return; + } + } + + assert(false); +} + +static void handle_open_channel(VMBus *vmbus, vmbus_message_open_channel *= msg, + uint32_t msglen) +{ + VMBusChannel *chan; + + if (msglen < sizeof(*msg) || !vmbus_initialized(vmbus)) { + return; + } + + chan =3D find_channel(vmbus, msg->child_relid); + if (!chan || chan->state !=3D VMCHAN_INIT) { + /* FIXME: think of replying with an error */ + return; + } + + chan->state =3D VMCHAN_OPENING; + chan->rb_gpadl =3D msg->ring_buffer_gpadl_id; + chan->rb_rcv_offset =3D msg->ring_buffer_offset; + chan->target_vp =3D msg->target_vp; + chan->open_id =3D msg->open_id; + + open_channel(chan); + vmbus->state =3D VMBUS_OPEN_CHANNEL; +} + +static void send_open_channel(VMBus *vmbus) +{ + VMBusChannel *chan; + + QTAILQ_FOREACH(chan, &vmbus->channel_list, link) { + if (chan->state =3D=3D VMCHAN_OPENING) { + struct vmbus_message_open_result msg =3D { + .header.message_type =3D VMBUS_MSG_OPENCHANNEL_RESULT, + .child_relid =3D chan->relid, + .open_id =3D chan->open_id, + .status =3D channel_broken(chan), + }; + + post_msg(vmbus, &msg, sizeof(msg)); + return; + } + } + + assert(false); +} + +static void complete_open_channel(VMBus *vmbus) +{ + VMBusChannel *chan; + + QTAILQ_FOREACH(chan, &vmbus->channel_list, link) { + if (chan->state =3D=3D VMCHAN_OPENING) { + chan->state =3D channel_broken(chan) ? VMCHAN_INIT : VMCHAN_OP= EN; + vmbus->state =3D VMBUS_LISTEN; + return; + } + } + + assert(false); +} + +static void handle_close_channel(VMBus *vmbus, vmbus_message_close_channel= *msg, + uint32_t msglen) +{ + VMBusChannel *chan; + + if (msglen < sizeof(*msg) || !vmbus_initialized(vmbus)) { + return; + } + + chan =3D find_channel(vmbus, msg->child_relid); + if (!chan) { + return; + } + + close_channel(chan); +} + +static void handle_unload(VMBus *vmbus, void *msg, uint32_t msglen) +{ + vmbus->state =3D VMBUS_UNLOAD; +} + +static void send_unload(VMBus *vmbus) +{ + vmbus_message_header msg =3D { + .message_type =3D VMBUS_MSG_UNLOAD_RESPONSE, + }; + + qemu_mutex_lock(&vmbus->rx_queue_lock); + vmbus->rx_queue_size =3D 0; + qemu_mutex_unlock(&vmbus->rx_queue_lock); + + post_msg(vmbus, &msg, sizeof(msg)); + return; +} + +static void complete_unload(VMBus *vmbus) +{ + vmbus_reset_all(vmbus); +} + +static void process_incoming_message(VMBus *vmbus) +{ + struct hyperv_post_message_input *hv_msg; + struct vmbus_message_header *msg; + void *msgdata; + uint32_t msglen; + + qemu_mutex_lock(&vmbus->rx_queue_lock); + + if (!vmbus->rx_queue_size) { + goto unlock; + } + + hv_msg =3D &vmbus->rx_queue[vmbus->rx_queue_head]; + msglen =3D hv_msg->payload_size; + if (msglen < sizeof(*msg)) { + goto out; + } + msgdata =3D hv_msg->payload; + msg =3D (struct vmbus_message_header*)msgdata; + + trace_vmbus_process_incoming_message(msg->message_type); + + switch (msg->message_type) { + case VMBUS_MSG_INITIATE_CONTACT: + handle_initiate_contact(vmbus, msgdata, msglen); + break; + case VMBUS_MSG_REQUESTOFFERS: + handle_request_offers(vmbus, msgdata, msglen); + break; + case VMBUS_MSG_GPADL_HEADER: + handle_gpadl_header(vmbus, msgdata, msglen); + break; + case VMBUS_MSG_GPADL_BODY: + handle_gpadl_body(vmbus, msgdata, msglen); + break; + case VMBUS_MSG_GPADL_TEARDOWN: + handle_gpadl_teardown(vmbus, msgdata, msglen); + break; + case VMBUS_MSG_OPENCHANNEL: + handle_open_channel(vmbus, msgdata, msglen); + break; + case VMBUS_MSG_CLOSECHANNEL: + handle_close_channel(vmbus, msgdata, msglen); + break; + case VMBUS_MSG_UNLOAD: + handle_unload(vmbus, msgdata, msglen); + break; + default: + error_report("unknown message type %#x", msg->message_type); + break; + } + +out: + vmbus->rx_queue_size--; + vmbus->rx_queue_head++; + vmbus->rx_queue_head %=3D VMBUS_RX_QUEUE_CAPACITY; + + vmbus_resched(vmbus); +unlock: + qemu_mutex_unlock(&vmbus->rx_queue_lock); +} + +static void vmbus_run(void *opaque) +{ + VMBus *vmbus =3D opaque; + + if (vmbus->msg_in_progress) { + return; + } + + switch(vmbus->state) { + case VMBUS_LISTEN: + process_incoming_message(vmbus); + break; + case VMBUS_HANDSHAKE: + send_handshake(vmbus); + break; + case VMBUS_OFFER: + send_offer(vmbus); + break; + case VMBUS_CREATE_GPADL: + send_create_gpadl(vmbus); + break; + case VMBUS_TEARDOWN_GPADL: + send_teardown_gpadl(vmbus); + break; + case VMBUS_OPEN_CHANNEL: + send_open_channel(vmbus); + break; + case VMBUS_UNLOAD: + send_unload(vmbus); + break; + default: + assert(false); + }; +} + +static void vmbus_msg_cb(void *data, int status) +{ + VMBus *vmbus =3D data; + + assert(vmbus->msg_in_progress); + + trace_vmbus_post_reply_callback(status); + + if (status =3D=3D -EAGAIN) { + goto out; + } + if (status) { + error_report("message delivery fatal failure: %d; aborting vmbus", + status); + vmbus_reset_all(vmbus); + return; + } + + switch (vmbus->state) { + case VMBUS_HANDSHAKE: + complete_handshake(vmbus); + break; + case VMBUS_OFFER: + complete_offer(vmbus); + break; + case VMBUS_CREATE_GPADL: + complete_create_gpadl(vmbus); + break; + case VMBUS_TEARDOWN_GPADL: + complete_teardown_gpadl(vmbus); + break; + case VMBUS_OPEN_CHANNEL: + complete_open_channel(vmbus); + break; + case VMBUS_UNLOAD: + complete_unload(vmbus); + break; + default: + assert(false); + } + +out: + vmbus->msg_in_progress =3D false; + vmbus_resched(vmbus); +} + +static void vmbus_resched(VMBus *vmbus) +{ + aio_bh_schedule_oneshot(qemu_get_aio_context(), vmbus_run, vmbus); +} + +static void vmbus_signal_event(EventNotifier *e) +{ + VMBusChannel *chan; + VMBus *vmbus =3D container_of(e, VMBus, notifier); + unsigned long *int_map; + hwaddr addr, len; + bool is_dirty =3D false; + + if (!event_notifier_test_and_clear(e)) { + return; + } + + trace_vmbus_signal_event(); + + if (!vmbus->int_page_gpa) { + return; + } + + addr =3D vmbus->int_page_gpa + TARGET_PAGE_SIZE / 2; + len =3D TARGET_PAGE_SIZE / 2; + int_map =3D cpu_physical_memory_map(addr, &len, 1); + if (len !=3D TARGET_PAGE_SIZE / 2) { + goto unmap; + } + + QTAILQ_FOREACH(chan, &vmbus->channel_list, link) { + if (bitmap_test_and_clear_atomic(int_map, chan->relid, 1)) { + event_notifier_set(&chan->notifier); + is_dirty =3D true; + } + } + +unmap: + cpu_physical_memory_unmap(int_map, len, 1, is_dirty); +} + +static void vmbus_dev_realize(DeviceState *dev, Error **errp) +{ + VMBusDevice *vdev =3D VMBUS_DEVICE(dev); + VMBusDeviceClass *vdc =3D VMBUS_DEVICE_GET_CLASS(vdev); + VMBus *vmbus =3D VMBUS(qdev_get_parent_bus(dev)); + BusChild *child; + Error *err =3D NULL; + char idstr[UUID_FMT_LEN + 1]; + + if (!qemu_uuid_is_null(&vdc->instanceid)) { + /* Class wants to only have a single instance with a fixed UUID */ + vdev->instanceid =3D vdc->instanceid; + } + assert(!qemu_uuid_is_null(&vdev->instanceid)); + + /* Check for instance id collision for this class id */ + QTAILQ_FOREACH(child, &BUS(vmbus)->children, sibling) { + VMBusDevice *child_dev =3D VMBUS_DEVICE(child->child); + + if (child_dev =3D=3D vdev) { + continue; + } + + if (qemu_uuid_is_equal(&child_dev->instanceid, &vdev->instanceid))= { + qemu_uuid_unparse(&vdev->instanceid, idstr); + error_setg(&err, "duplicate vmbus device instance id %s", idst= r); + goto error_out; + } + } + + vdev->dma_as =3D &address_space_memory; + + create_channels(vmbus, vdev, &err); + if (err) { + goto error_out; + } + + if (vdc->vmdev_realize) { + vdc->vmdev_realize(vdev, &err); + if (err) { + goto err_vdc_realize; + } + } + return; + +err_vdc_realize: + free_channels(vmbus, vdev); +error_out: + error_propagate(errp, err); +} + +static void vmbus_dev_reset(DeviceState *dev) +{ + uint16_t i; + VMBusDevice *vdev =3D VMBUS_DEVICE(dev); + VMBusDeviceClass *vdc =3D VMBUS_DEVICE_GET_CLASS(vdev); + + if (vdc->vmdev_reset) { + vdc->vmdev_reset(vdev); + } + + if (vdev->channels) { + for (i =3D 0; i < vdev->num_channels; i++) { + VMBusChannel *chan =3D &vdev->channels[i]; + if (chan->state =3D=3D VMCHAN_OPEN) { + close_channel(chan); + } + chan->offer_state =3D VMOFFER_INIT; + } + } +} + +static void vmbus_dev_unrealize(DeviceState *dev, Error **errp) +{ + VMBusDevice *vdev =3D VMBUS_DEVICE(dev); + VMBusDeviceClass *vdc =3D VMBUS_DEVICE_GET_CLASS(vdev); + VMBus *vmbus =3D VMBUS(qdev_get_parent_bus(dev)); + + if (vdc->vmdev_unrealize) { + vdc->vmdev_unrealize(vdev, errp); + } + free_channels(vmbus, vdev); +} + +static void vmbus_dev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *kdev =3D DEVICE_CLASS(klass); + kdev->bus_type =3D TYPE_VMBUS; + kdev->realize =3D vmbus_dev_realize; + kdev->unrealize =3D vmbus_dev_unrealize; + kdev->reset =3D vmbus_dev_reset; +} + +static int vmbus_dev_post_load(void *opaque, int version_id) +{ + int ret; + uint16_t i; + VMBusDevice *dev =3D opaque; + + for (i =3D 0; i < dev->num_channels; i++) { + ret =3D register_chan_ids(&dev->channels[i]); + if (ret) { + goto err_reg; + } + } + + return 0; + +err_reg: + while (i--) { + unregister_chan_ids(&dev->channels[i]); + } + return ret; +} + +const VMStateDescription vmstate_vmbus_dev =3D { + .name =3D TYPE_VMBUS_DEVICE, + .version_id =3D 0, + .minimum_version_id =3D 0, + .post_load =3D vmbus_dev_post_load, + .fields =3D (VMStateField[]) { + VMSTATE_UINT8_ARRAY(instanceid.data, VMBusDevice, 16), + VMSTATE_UINT16(num_channels, VMBusDevice), + VMSTATE_STRUCT_VARRAY_POINTER_UINT16(channels, VMBusDevice, num_ch= annels, + vmstate_channel, VMBusChannel= ), + VMSTATE_END_OF_LIST() + } +}; + +/* vmbus generic device base */ +static const TypeInfo vmbus_dev_type_info =3D { + .name =3D TYPE_VMBUS_DEVICE, + .parent =3D TYPE_DEVICE, + .abstract =3D true, + .instance_size =3D sizeof(VMBusDevice), + .class_size =3D sizeof(VMBusDeviceClass), + .class_init =3D vmbus_dev_class_init, +}; + +static void vmbus_realize(BusState *bus, Error **errp) +{ + int ret =3D 0; + Error *local_err =3D NULL; + VMBus *vmbus =3D VMBUS(bus); + + qemu_mutex_init(&vmbus->rx_queue_lock); + + QTAILQ_INIT(&vmbus->gpadl_list); + QTAILQ_INIT(&vmbus->channel_list); + + ret =3D hyperv_set_msg_handler(VMBUS_MESSAGE_CONNECTION_ID, + vmbus_recv_message, vmbus); + if (ret !=3D 0) { + error_setg(&local_err, "hyperv set message handler failed: %d", re= t); + goto error_out; + } + + ret =3D event_notifier_init(&vmbus->notifier, 0); + if (ret !=3D 0) { + error_setg(&local_err, "event notifier failed to init with %d", re= t); + goto remove_msg_handler; + } + + event_notifier_set_handler(&vmbus->notifier, vmbus_signal_event); + ret =3D hyperv_set_event_flag_handler(VMBUS_EVENT_CONNECTION_ID, + &vmbus->notifier); + if (ret !=3D 0) { + error_setg(&local_err, "hyperv set event handler failed with %d", = ret); + goto clear_event_notifier; + } + + return; + +clear_event_notifier: + event_notifier_cleanup(&vmbus->notifier); +remove_msg_handler: + hyperv_set_msg_handler(VMBUS_MESSAGE_CONNECTION_ID, NULL, NULL); +error_out: + qemu_mutex_destroy(&vmbus->rx_queue_lock); + error_propagate(errp, local_err); +} + +static void vmbus_unrealize(BusState *bus, Error **errp) +{ + VMBus *vmbus =3D VMBUS(bus); + + hyperv_set_msg_handler(VMBUS_MESSAGE_CONNECTION_ID, NULL, NULL); + hyperv_set_event_flag_handler(VMBUS_MESSAGE_CONNECTION_ID, NULL); + event_notifier_cleanup(&vmbus->notifier); + + qemu_mutex_destroy(&vmbus->rx_queue_lock); +} + +static void vmbus_reset(BusState *bus) +{ + vmbus_deinit(VMBUS(bus)); +} + +static char *vmbus_get_dev_path(DeviceState *dev) +{ + BusState *bus =3D qdev_get_parent_bus(dev); + return qdev_get_dev_path(bus->parent); +} + +static char *vmbus_get_fw_dev_path(DeviceState *dev) +{ + VMBusDevice *vdev =3D VMBUS_DEVICE(dev); + char path[32 + 1 + UUID_FMT_LEN + 1]; + int off; + + off =3D snprintf(path, sizeof(path), "%s@", qdev_fw_name(dev)); + if (sizeof(path) - off > UUID_FMT_LEN) { + qemu_uuid_unparse(&vdev->instanceid, path + off); + } + return g_strdup(path); +} + +static void vmbus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k =3D BUS_CLASS(klass); + + k->get_dev_path =3D vmbus_get_dev_path; + k->get_fw_dev_path =3D vmbus_get_fw_dev_path; + k->realize =3D vmbus_realize; + k->unrealize =3D vmbus_unrealize; + k->reset =3D vmbus_reset; +} + +static const TypeInfo vmbus_type_info =3D { + .name =3D TYPE_VMBUS, + .parent =3D TYPE_BUS, + .instance_size =3D sizeof(VMBus), + .class_init =3D vmbus_class_init, +}; + +typedef struct VMBusBridge { + SysBusDevice parent_obj; + + VMBus bus; +} VMBusBridge; + +#define VMBUS_BRIDGE(obj) OBJECT_CHECK(VMBusBridge, (obj), TYPE_VMBUS_BRID= GE) + +static void vmbus_bridge_realize(DeviceState *dev, Error **errp) +{ + VMBus *vmbus =3D &VMBUS_BRIDGE(dev)->bus; + + if (!vmbus_exists()) { + error_setg(errp, "at most one %s device is permitted", + TYPE_VMBUS_BRIDGE); + return; + } + + if (!hyperv_is_synic_enabled()) { + error_report("VMBus requires usable Hyper-V SynIC and VP_INDEX"); + return; + } + + qbus_create_inplace(vmbus, sizeof(VMBus), TYPE_VMBUS, dev, "vmbus"); +} + +static int vmbus_post_load(void *opaque, int version_id) +{ + int ret; + VMBusChannel *chan; + VMBus *vmbus =3D &VMBUS_BRIDGE(opaque)->bus; + + QTAILQ_FOREACH(chan, &vmbus->channel_list, link) { + unregister_chan_ids(chan); + } + + ret =3D vmbus_init(vmbus); + if (ret) { + return ret; + } + + vmbus_resched(vmbus); + return 0; +} + +static const VMStateDescription vmstate_post_message_input =3D { + .name =3D "vmbus/hyperv_post_message_input", + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D (VMStateField[]) { + VMSTATE_UINT32(connection_id, struct hyperv_post_message_input), + VMSTATE_UINT32(message_type, struct hyperv_post_message_input), + VMSTATE_UINT32(payload_size, struct hyperv_post_message_input), + VMSTATE_UINT8_ARRAY(payload, struct hyperv_post_message_input, + HV_MESSAGE_PAYLOAD_SIZE), + VMSTATE_END_OF_LIST() + } +}; + +static bool vmbus_rx_queue_needed(void *opaque) +{ + VMBus *vmbus =3D &VMBUS_BRIDGE(opaque)->bus; + return vmbus->rx_queue_size; +} + +static const VMStateDescription vmstate_rx_queue =3D { + .name =3D "vmbus/rx_queue", + .version_id =3D 0, + .minimum_version_id =3D 0, + .needed =3D vmbus_rx_queue_needed, + .fields =3D (VMStateField[]) { + VMSTATE_UINT8(bus.rx_queue_head, VMBusBridge), + VMSTATE_UINT8(bus.rx_queue_size, VMBusBridge), + VMSTATE_STRUCT_ARRAY(bus.rx_queue, VMBusBridge, + VMBUS_RX_QUEUE_CAPACITY, 0, + vmstate_post_message_input, + struct hyperv_post_message_input), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_vmbus_bridge =3D { + .name =3D TYPE_VMBUS_BRIDGE, + .version_id =3D 0, + .minimum_version_id =3D 0, + .post_load =3D vmbus_post_load, + .fields =3D (VMStateField[]) { + VMSTATE_UINT32(bus.state, VMBusBridge), + VMSTATE_UINT32(bus.version, VMBusBridge), + VMSTATE_UINT32(bus.target_vp, VMBusBridge), + VMSTATE_UINT64(bus.int_page_gpa, VMBusBridge), + VMSTATE_QTAILQ_V(bus.gpadl_list, VMBusBridge, 0, + vmstate_gpadl, VMBusGpadl, link), + VMSTATE_END_OF_LIST() + }, + .subsections =3D (const VMStateDescription*[]) { + &vmstate_rx_queue, + NULL + } +}; + +static void vmbus_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k =3D DEVICE_CLASS(klass); + k->realize =3D vmbus_bridge_realize; + k->fw_name =3D "vmbus@0"; + set_bit(DEVICE_CATEGORY_BRIDGE, k->categories); + k->vmsd =3D &vmstate_vmbus_bridge; + k->user_creatable =3D true; +} + +static const TypeInfo vmbus_bridge_type_info =3D { + .name =3D TYPE_VMBUS_BRIDGE, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(VMBusBridge), + .class_init =3D vmbus_bridge_class_init, +}; + +static void vmbus_register_types(void) +{ + type_register_static(&vmbus_bridge_type_info); + type_register_static(&vmbus_dev_type_info); + type_register_static(&vmbus_type_info); +} + +type_init(vmbus_register_types) diff --git a/include/hw/vmbus/vmbus.h b/include/hw/vmbus/vmbus.h new file mode 100644 index 0000000000..813614d6d0 --- /dev/null +++ b/include/hw/vmbus/vmbus.h @@ -0,0 +1,109 @@ +/* + * QEMU Hyper-V VMBus + * + * Copyright (c) 2017-2018 Virtuozzo International GmbH. + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#ifndef QEMU_VMBUS_H +#define QEMU_VMBUS_H + +#include "hw/qdev-properties.h" +#include "sysemu/sysemu.h" +#include "sysemu/dma.h" +#include "target/i386/hyperv.h" +#include "target/i386/hyperv-proto.h" +#include "hw/vmbus/vmbus-proto.h" +#include "qemu/uuid.h" + +#define TYPE_VMBUS_DEVICE "vmbus-dev" +#define TYPE_VMBUS_BRIDGE "vmbus-bridge" + +#define VMBUS_DEVICE(obj) \ + OBJECT_CHECK(VMBusDevice, (obj), TYPE_VMBUS_DEVICE) +#define VMBUS_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(VMBusDeviceClass, (klass), TYPE_VMBUS_DEVICE) +#define VMBUS_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VMBusDeviceClass, (obj), TYPE_VMBUS_DEVICE) + +typedef struct VMBus VMBus; +typedef struct VMBusChannel VMBusChannel; +typedef struct VMBusDevice VMBusDevice; +typedef struct VMBusGpadl VMBusGpadl; + +static inline Object *vmbus_exists(void) +{ + return object_resolve_path_type("", TYPE_VMBUS_BRIDGE, NULL); +} + +typedef void(*VMBusChannelNotifyCb)(struct VMBusChannel *chan); + +typedef struct VMBusDeviceClass { + DeviceClass parent; + + QemuUUID classid; + QemuUUID instanceid; /* Fixed UUID for singleton devices */ + uint16_t channel_flags; + uint16_t mmio_size_mb; + + void (*vmdev_realize)(VMBusDevice *vdev, Error **errp); + void (*vmdev_unrealize)(VMBusDevice *vdev, Error **errp); + void (*vmdev_reset)(VMBusDevice *vdev); + uint16_t (*num_channels)(VMBusDevice *vdev); + int (*open_channel) (VMBusDevice *vdev); + void (*close_channel) (VMBusDevice *vdev); + VMBusChannelNotifyCb chan_notify_cb; +} VMBusDeviceClass; + +typedef struct VMBusDevice { + DeviceState parent; + QemuUUID instanceid; + uint16_t num_channels; + VMBusChannel *channels; + AddressSpace *dma_as; +} VMBusDevice; + +extern const VMStateDescription vmstate_vmbus_dev; + +typedef struct VMBusChanReq { + VMBusChannel *chan; + uint16_t pkt_type; + uint32_t msglen; + void *msg; + uint64_t transaction_id; + void *comp; + QEMUSGList sgl; +} VMBusChanReq; + +VMBusDevice *vmbus_channel_device(VMBusChannel *chan); +VMBusChannel *vmbus_device_channel(VMBusDevice *dev, uint32_t chan_idx); +uint32_t vmbus_channel_idx(VMBusChannel *chan); +void vmbus_notify_channel(VMBusChannel *chan); + +int vmbus_channel_send(VMBusChannel *chan, uint16_t pkt_type, + void *desc, uint32_t desclen, + void *msg, uint32_t msglen, + bool need_comp, uint64_t transaction_id); +int vmbus_chan_send_completion(VMBusChanReq *req); +int vmbus_channel_reserve(VMBusChannel *chan, + uint32_t desclen, uint32_t msglen); +void *vmbus_channel_recv(VMBusChannel *chan, uint32_t size); +void vmbus_release_req(void *req); + +void vmbus_save_req(QEMUFile *f, VMBusChanReq *req); +void *vmbus_load_req(QEMUFile *f, VMBusDevice *dev, uint32_t size); + + +VMBusGpadl *vmbus_get_gpadl(VMBusChannel *chan, uint32_t gpadl_id); +void vmbus_put_gpadl(VMBusGpadl *gpadl); +uint32_t vmbus_gpadl_len(VMBusGpadl *gpadl); +ssize_t vmbus_iov_to_gpadl(VMBusChannel *chan, VMBusGpadl *gpadl, uint32_t= off, + const struct iovec *iov, size_t iov_cnt); +int vmbus_map_sgl(QEMUSGList *sgl, DMADirection dir, struct iovec *iov, + unsigned iov_cnt, size_t len, size_t off); +void vmbus_unmap_sgl(QEMUSGList *sgl, DMADirection dir, struct iovec *iov, + unsigned iov_cnt, size_t accessed); + +#endif --=20 2.24.1 From nobody Sun May 19 11:37:25 2024 Delivered-To: importer@patchew.org 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; Authentication-Results: mx.zohomail.com; dkim=fail; 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=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1585923963; cv=none; d=zohomail.com; s=zohoarc; b=EPr3lc0DlTITM5u6oqlYN7oRVrsp6PSbhaDOm9m7bZIjCprQCjOg9X2YcMFEZykwJQfH3lfu82A3D//9CQjua4kNPuG/caz+lknXy9K5z1qognWyWUoM8aibolSlETWjboZwMbP3vYBJ4xXgdGyny2SUzLy+IFPg4d5AUybp/fY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1585923963; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=pUolEYbY8XbrgT/4XGlHpmye2+orYYv6X2aq+U3yuRI=; b=CZVpuWvs1lRbgTI+caY0qIyYvRRP7K4zrP5gi0UAk5eBI7SoVse5dNrVjSwCyorJiPrRZH1kZDuQ0yTGYWEVNkEGDhfbY5tU/Kk4vx1gcaiZG/jjoD5bNVtEPn9roUSck9HtpjUlWP2AAPSG+dldv6uW4jJTklyE8AQtXTkGDh0= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; 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=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1585923963799944.2502151258434; Fri, 3 Apr 2020 07:26:03 -0700 (PDT) Received: from localhost ([::1]:56184 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jKNGk-00020P-I4 for importer@patchew.org; Fri, 03 Apr 2020 10:26:02 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:41123) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jKNEL-0006MS-2u for qemu-devel@nongnu.org; Fri, 03 Apr 2020 10:23:34 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1jKNEK-0008H9-05 for qemu-devel@nongnu.org; Fri, 03 Apr 2020 10:23:32 -0400 Received: from mail-wm1-x341.google.com ([2a00:1450:4864:20::341]:53791) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1jKNEJ-0008CK-Pw for qemu-devel@nongnu.org; Fri, 03 Apr 2020 10:23:31 -0400 Received: by mail-wm1-x341.google.com with SMTP id d77so7352482wmd.3 for ; Fri, 03 Apr 2020 07:23:31 -0700 (PDT) Received: from jondnuc.lan (IGLD-84-229-154-167.inter.net.il. [84.229.154.167]) by smtp.gmail.com with ESMTPSA id i2sm12218646wrx.22.2020.04.03.07.23.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 03 Apr 2020 07:23:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=pUolEYbY8XbrgT/4XGlHpmye2+orYYv6X2aq+U3yuRI=; b=fHwwG302DTBYoT8VtGfEOXJxy5QjPXzAq2IfaF6QrwgILJwDDsBC4BKHesWGyTIkgi LT3+L80/gL+HLYg3GQuQkwLLE6u99O22zXXPpmHBTHPcvrQgGxHc2wU5vq4l4rF6hBMR i3hb71kLufagdDgulxB2YHXg5e3Ff4kCwqATnzoen8cN0brbC8A33agoFL/ctqSizO4W GQlK8W3z4M2n5g9gBP7LjxtGKmbxwxyFWJKzgYhzCXw51wCWypr2+U3bnyKgKgSgOb8o H1Pa9MsCC3WyXcUI0ImsnUp4/3iaLLItK6+4zSSy3gvWYQh05lc6KA8r6aaDc6Ynulei 6Z0w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=pUolEYbY8XbrgT/4XGlHpmye2+orYYv6X2aq+U3yuRI=; b=ssU/cjnx9AHfjLTBf2QrzBKWN7f/BA6yP54DEK/oM+0WY4tp0CkZtyIrymoxgBbUma 4ZnrvfnDwf8CSXJLD3coU2xgjGmqGPjHhTo8jjV8YhtP8P7TfPFFr8NELoI5h76J1ZyE YmLCBthKb0bAfmA42/NjQI0PVxdKwsfWkiw7pGZ+71siAzEHr5a9wtE4YH531Wk5ktTS AG9p6KG1Iyza27F6SatYeBNdHTXKEpik9K7xBAdGNChVETR/exf6GA22ExAX5NYw5db1 j5FFxmWbnop4LBWV2oFQrOhlI9QOJDZK/5NVRjW45mZDRhL+23KEpi8wfA/1ppKw5ZRH MTiQ== X-Gm-Message-State: AGi0PuY3XMzdKKW6z4GUywE26gkT8i79ArUKZ2dN58nST/S60yMBz2cA womXI5u8AzBk+foeqTQhLOxUOXqoJW0IBQ== X-Google-Smtp-Source: APiQypK18WYEsG+wCvjYX7HNDF5n6s7BKXq5pN+51+bcyK1p+kebPRY27CjJv+Gpn9FUnIu96nRT/Q== X-Received: by 2002:a1c:3b89:: with SMTP id i131mr8956511wma.35.1585923810659; Fri, 03 Apr 2020 07:23:30 -0700 (PDT) From: Jon Doron To: qemu-devel@nongnu.org Subject: [PATCH v1 5/5] i386: Hyper-V VMBus ACPI DSDT entry Date: Fri, 3 Apr 2020 17:23:08 +0300 Message-Id: <20200403142308.82990-6-arilou@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200403142308.82990-1-arilou@gmail.com> References: <20200403142308.82990-1-arilou@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::341 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Evgeny Yakovlev , ehabkost@redhat.com, Roman Kagan , pbonzini@redhat.com, vkuznets@redhat.com, Jon Doron Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Type: text/plain; charset="utf-8" Guest OS uses ACPI to discover vmbus presence. Add a corresponding entry to DSDT in case vmbus has been enabled. Experimentally Windows guests were found to require this entry to include two IRQ resources, so this patch adds two semi-arbitrarily chosen ones (7 and 13). This results, in particular, in parallel port conflicting with vmbus. TODO: discover and use spare IRQs to avoid conflicts. Signed-off-by: Evgeny Yakovlev Signed-off-by: Roman Kagan Signed-off-by: Jon Doron --- hw/i386/acpi-build.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 2a7e55bae7..6d7fdbbe9b 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -43,6 +43,7 @@ #include "hw/acpi/tpm.h" #include "hw/acpi/vmgenid.h" #include "hw/boards.h" +#include "hw/vmbus/vmbus.h" #include "sysemu/tpm_backend.h" #include "hw/rtc/mc146818rtc_regs.h" #include "migration/vmstate.h" @@ -1270,6 +1271,43 @@ static Aml *build_com_device_aml(uint8_t uid) return dev; } =20 +static Aml *build_vmbus_device_aml(void) +{ + Aml *dev; + Aml *method; + Aml *crs; + + dev =3D aml_device("VMBS"); + aml_append(dev, aml_name_decl("STA", aml_int(0xF))); + aml_append(dev, aml_name_decl("_HID", aml_string("VMBus"))); + aml_append(dev, aml_name_decl("_UID", aml_int(0x0))); + aml_append(dev, aml_name_decl("_DDN", aml_string("VMBUS"))); + + method =3D aml_method("_DIS", 0, AML_NOTSERIALIZED); + aml_append(method, aml_store(aml_and(aml_name("STA"), aml_int(0xD), NU= LL), + aml_name("STA"))); + aml_append(dev, method); + + method =3D aml_method("_PS0", 0, AML_NOTSERIALIZED); + aml_append(method, aml_store(aml_or(aml_name("STA"), aml_int(0xF), NUL= L), + aml_name("STA"))); + aml_append(dev, method); + + method =3D aml_method("_STA", 0, AML_NOTSERIALIZED); + aml_append(method, aml_store(aml_name("STA"), aml_local(0))); + aml_append(method, aml_return(aml_local(0))); + aml_append(dev, method); + + aml_append(dev, aml_name_decl("_PS3", aml_int(0x0))); + + crs =3D aml_resource_template(); + aml_append(crs, aml_irq_no_flags(7)); + aml_append(crs, aml_irq_no_flags(13)); + aml_append(dev, aml_name_decl("_CRS", crs)); + + return dev; +} + static void build_isa_devices_aml(Aml *table) { ISADevice *fdc =3D pc_find_fdc0(); @@ -1296,6 +1334,10 @@ static void build_isa_devices_aml(Aml *table) build_acpi_ipmi_devices(scope, BUS(obj), "\\_SB.PCI0.ISA"); } =20 + if (vmbus_exists()) { + aml_append(scope, build_vmbus_device_aml()); + } + aml_append(table, scope); } =20 --=20 2.24.1