From nobody Sat May 18 19:24:34 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=1586238795; cv=none; d=zohomail.com; s=zohoarc; b=X8fu9w/Qztl2URVTzZHFr240vEl85c+1vJzoTlJkU7ZOSPaxfEl0MqXbQd1HOJEavzZf7chlyWTOOLfQygW7qegtXNbYEFEJy7GszxLtQgS++To18geBAMWrfiomfAupobJPzK6xq1vdQMQ7xjr9221ExaBdwEAn55D6CNvJEq4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1586238795; 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=Lw1DRDYeg6l1euMNc2H2St82hratx7fYfjnRhxJikz3HVK38jqrg8FEyPy3IQH1JI7KmbMqpLIhMzW6WcHUjTivVFl17HuVjD9qnUMFanQFoVfMX8PELVsr9aftLggsgJF1I/bhpDBBe0tBmvvfGJ8XiA+NRL0FvqU2i5qPA9qA= 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 1586238795671373.920572414503; Mon, 6 Apr 2020 22:53:15 -0700 (PDT) Received: from localhost ([::1]:41640 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jLhAg-0005ry-5L for importer@patchew.org; Tue, 07 Apr 2020 01:53:14 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:60097) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jLh9k-0004Y9-2Y for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:16 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1jLh9j-0002Us-2E for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:16 -0400 Received: from mail-wr1-x442.google.com ([2a00:1450:4864:20::442]:35661) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1jLh9i-0002UR-St for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:15 -0400 Received: by mail-wr1-x442.google.com with SMTP id g3so2413948wrx.2 for ; Mon, 06 Apr 2020 22:52:14 -0700 (PDT) Received: from jondnuc.lan (IGLD-84-229-155-55.inter.net.il. [84.229.155.55]) by smtp.gmail.com with ESMTPSA id n11sm32271007wrg.72.2020.04.06.22.52.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 06 Apr 2020 22:52:13 -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=hlOb8lPVwlbSKrVCLmDsmo0QhXmf6m/HN2h3+eWUpL7ylAjEneopn3/4zPQCbAevDf UDcoVmwYCUnnpZCc4W2wwbGrrCzHCRId3jfunybglVY1h81LTU1ofZFp7K+G2n2cuBsh Ln0RoMDm7HH6xFFJd0o7O/mc8KSlfUJLNLWq9AK923R2IT1rc+JVuVL314M8XK4FNiZt yV9t/v3osWRqn3+RxXQzAmnjwOkyfhLzhzTT+5IcfmWwlm7FeatOZADtIeviPHvLIfIO C0vkw3PbESEGvOjVSk49/ebSyyV9HLja/K/8ud+fqiOYreJaXWFkrmH85ejyUiQvJTBW cEDQ== 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=FjcpP1pmLkZ1tykLhuYoTP2WLLBc7ODdvyinJYGxa9L4f2uw9vc6NaccZWRgt9hXDK RORiWY6huamB77QIzZcevtY9OYA0K22LZZp8xUCkikPBgtOLD//ZNComSELLEODB6Y4+ pTveCXk7LSKH29/Nu6OJHQ/OkbieVl3ou7GMVqemrSA/zk2kGr+9ud6fWOy1r+uEjZ1N X8TPIcaNJAr6Qu1+o3k1+EKV7Yqn6yci9QaPOXhYuNTFF/VwiAuMTjWWJprn+WsjAaWO xXy8ObwKwlG1GMN3f+qkHyITqSz0NF5PgI9AppBVsN0uHYyf6CAeTdeiNY2UhLH2wQ8X qxnw== X-Gm-Message-State: AGi0PuaBULWNBy93b1blibcrjbsO2memaAEyNc7vLqMoL/3YwR4gV4/E HhNRYM08RTDXnNXAuqoWd7JYhhM4pvQ= X-Google-Smtp-Source: APiQypIIzwqu4Qbwq8TGSv/PB41OhV5iPGHSaBV6M/hiOBKzE/hSb/ppyJLW4HPY6BCvAuzaj9ok2w== X-Received: by 2002:adf:ee42:: with SMTP id w2mr766510wro.8.1586238733861; Mon, 06 Apr 2020 22:52:13 -0700 (PDT) From: Jon Doron To: qemu-devel@nongnu.org Subject: [PATCH v3 1/7] hyperv: expose API to determine if synic is enabled Date: Tue, 7 Apr 2020 08:51:50 +0300 Message-Id: <20200407055156.137249-2-arilou@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200407055156.137249-1-arilou@gmail.com> References: <20200407055156.137249-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: mail@maciej.szmigiero.name, eyakovlev@virtuozzo.com, ehabkost@redhat.com, rvkagan@gmail.com, liran.alon@oracle.com, 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" 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 Sat May 18 19:24:34 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=1586238876; cv=none; d=zohomail.com; s=zohoarc; b=VpHFs+hwJB9ly1YxUkBJbIPPZAjM1Gbu/lZySj2WxcMagi9rh2pVnacI1zSSuNYvDlM+TNgERR+q7t+AD21FRruL+D8H67VpzOm3rLccoEJ6Ffbn2y9IihMgDTOW1uTgQw6iGV0kIP6C/VXZZWfMDEljbnzzXhdExEsJugm09SA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1586238876; 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=NxTvWL/2evDaED4o5jXMn/pTi1CXHT/8TLK5POTKEfkXPcEutIEP8teRY5eYUcdWug9MPycOZVlpiXysaovu4/hbY1jlRJWRdXxZbwlt7GniFv2Z5pTc7QHs3/9b80hd5xHINKP1lhavlsrH6U2ccdweQZeSNEbGaOYfc1BrOts= 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 1586238876994538.5000086532278; Mon, 6 Apr 2020 22:54:36 -0700 (PDT) Received: from localhost ([::1]:41660 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jLhBz-0008Rw-MF for importer@patchew.org; Tue, 07 Apr 2020 01:54:35 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:60112) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jLh9m-0004aH-N2 for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:20 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1jLh9k-0002Wf-N4 for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:18 -0400 Received: from mail-wr1-x441.google.com ([2a00:1450:4864:20::441]:42559) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1jLh9k-0002WA-G0 for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:16 -0400 Received: by mail-wr1-x441.google.com with SMTP id h15so2367981wrx.9 for ; Mon, 06 Apr 2020 22:52:16 -0700 (PDT) Received: from jondnuc.lan (IGLD-84-229-155-55.inter.net.il. [84.229.155.55]) by smtp.gmail.com with ESMTPSA id n11sm32271007wrg.72.2020.04.06.22.52.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 06 Apr 2020 22:52:14 -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=a8ZIahBYXJE7ArVQaIvQGTOsf0X2jdlaAJYAu7SjJOp/0NUFVOImtm712NbVVIRsfv L/MB9p8ygN6MrMBtmrhrmjCYPtXCUHxY0Io/PXXZ7XPalf8YqtMLDPpGmKekG+0d6CGS rij8EopPv4XgH0DpphQTCLX9zJYsdJDOfDRXInfH3FJE/H187k+Hli2kGOCAntyIzpVs lAQZey+YmZ7k5bEjAUru+7AN5DyrVq5ZxVaFi2NOdKim6A/JolgtRJaK7wLguZUJU8sf gMMi80rLrCGXaCSVKDktKB2s3LqdziPq5diDgaL234+CFRITbfTthecbT1Jv/4aolzck VgRA== 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=nCN9n9Sl05Q9NwAUl2wpm1idXR75DcwRfSl5FA6xDM98LwH4P9hWqA9FylaqGPAjB9 5Q4+sZ+WTtC00J6VsvwraT6gcdUrquNdvZOp0QrqfHXnb10/oQ7TEFu56ELr1A72ccjo /LykYOR0udJZBbZawSUTWRrUG2xox3+uK1FnovpdebROpUu5hqUwmErGM7uZ261ODXo6 jTSp3HcFZIJlkIlwyl6p4+uPR8Hi6DiaWuGJde6jHzfwmQoeAWLmqVv2DUH9JfGPyATr +9hBb5VXpmi9yBOOicZwFw+WuPxYW7Da2UGCs4Fv4fuEdWL0vuCygUfEQ8LZ+XoM2ZUk QlOw== X-Gm-Message-State: AGi0Pub1fbpmtE9iHWNg3PxzTwcAxmo9YjQhKvNzM4FCLACqeEy37xlJ h7ytoLq5iB9M3NjcFGAGXHEFOy9oHOo= X-Google-Smtp-Source: APiQypLpO/eXajizaHbV+i0p3Ai8n9Bh334kjX/SYMEed4k1apTJoEWw8erOyxXhPqUeLQU6g+Ggfw== X-Received: by 2002:a5d:6305:: with SMTP id i5mr768338wru.203.1586238735185; Mon, 06 Apr 2020 22:52:15 -0700 (PDT) From: Jon Doron To: qemu-devel@nongnu.org Subject: [PATCH v3 2/7] hyperv: SControl is optional to enable SynIc Date: Tue, 7 Apr 2020 08:51:51 +0300 Message-Id: <20200407055156.137249-3-arilou@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200407055156.137249-1-arilou@gmail.com> References: <20200407055156.137249-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::441 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: mail@maciej.szmigiero.name, eyakovlev@virtuozzo.com, ehabkost@redhat.com, rvkagan@gmail.com, liran.alon@oracle.com, 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" 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 Sat May 18 19:24:34 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=1586238808; cv=none; d=zohomail.com; s=zohoarc; b=ALDl12xNg786MeuGxpBB1ctg2VjJbCr6hEHgUnZnQQuUYZ7qLadWWsovVLp14Av9tt43DGfOikoqeMq8mFzixvLtqRbos2G6hcLQBCPvSfi1Mr5pxOsFuWJGpZFGjqv1UXrNO+Qt7gBfI2yVD0NjQmt9nHTPuKcT+wSVnCLBQZM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1586238808; 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=nB3g7cVPtrxnSwQ59aiunL+jtz+a9s2+uCzKtHmRtTI=; b=dWnbnpQsQfQS1oOlok1bNfBGo7uPNG7LdT4fL/1kSYmviqlEvfdFtqhlOzQUZbKA3HClYKFQELBpwuqMblZyKNKSeSMoBIP/YPbjE6w+WN7efiiULxrQuUaUc3o2wkmdCikPV9bv80lMmpuaJPTTA8ShR2xAUfbqPrBoHEsxhqo= 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 1586238808768147.8144761078496; Mon, 6 Apr 2020 22:53:28 -0700 (PDT) Received: from localhost ([::1]:41642 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jLhAs-000631-6I for importer@patchew.org; Tue, 07 Apr 2020 01:53:26 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:60118) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jLh9n-0004bo-Jv for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:21 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1jLh9m-0002Xg-45 for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:19 -0400 Received: from mail-wm1-x343.google.com ([2a00:1450:4864:20::343]:54241) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1jLh9l-0002Wz-TO for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:18 -0400 Received: by mail-wm1-x343.google.com with SMTP id d77so475502wmd.3 for ; Mon, 06 Apr 2020 22:52:17 -0700 (PDT) Received: from jondnuc.lan (IGLD-84-229-155-55.inter.net.il. [84.229.155.55]) by smtp.gmail.com with ESMTPSA id n11sm32271007wrg.72.2020.04.06.22.52.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 06 Apr 2020 22:52:16 -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=nB3g7cVPtrxnSwQ59aiunL+jtz+a9s2+uCzKtHmRtTI=; b=jWcbIFWT4TTFUnlrewc/pERPG5DZGgcJJEe975LxrQRnoKwRrAotEP+qsG4dlBpDeD q+zi7FeQ7eW7vuR0N+NjDRjEyD78iw2RzfGb1+ivB6Tq3GhQ7jy0l91+KNaqiGzcgUvx QSOs8fVbDn58hZct6ZqNqDFJusPsHe8e9CVm9B5O64Qw6GggBcsKScOI1x+S03N7e1TH ZjkQvm9KrlrZE9P53kaQrw88FlEJkhXPrdv+UD/kGAomDyvhwOWNrnhNXMlBPlrPzJdf cBncHmgLZM5cWEA8XgCZ39DZGH2d9TuWOZ15vfx/BFAfZEqf/SwS80/DBASqcYEg+lNz wUAA== 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=nB3g7cVPtrxnSwQ59aiunL+jtz+a9s2+uCzKtHmRtTI=; b=WveCP0pvdEy0AMrQ7uUQMXbrTDXxXTlNykAQSOKSVG/MdoChTS5YTgJH/W0z2CG4YH kDG73TzCU4qRPqvH1eMfGZ9WkbFWPtSn+9zjWjlD9dktG7ThF+bxf51p7Ac6zSqvRDGi fl0QZ/qjNDVx4u7+HCgMDpey0o8YcBSueroZa+/XxsUT+FLmlS12SRbKgFf2hhczxWLx 8LaL+lqD0YWBWs+eCpvIHUusocbm251MEarM59O15qFR/Dwlf1eqgkX/hakjPa8xc194 ePCoS8r1heK7ixFvi2eN/Zpuqy4Hg2NQD68iOTPu75DLodDz7MaSzI76rwBy5v4cKJrd OlcQ== X-Gm-Message-State: AGi0PuYsUvRJtDsjURm93tLu79TP6OL5JYfFJRk+QExHdNLMnfHDs17m cXgq65CXrfQ5mX+U7ytZJT+NFsF+Ykk= X-Google-Smtp-Source: APiQypKkG9/fA2snDtg9m85nAw5HyscOF47MQsx7q1aG3apnlpmv+IA+8QE71lm1BMzhGN0tqnN6qg== X-Received: by 2002:a05:600c:4102:: with SMTP id j2mr537757wmi.159.1586238736722; Mon, 06 Apr 2020 22:52:16 -0700 (PDT) From: Jon Doron To: qemu-devel@nongnu.org Subject: [PATCH v3 3/7] vmbus: add vmbus protocol definitions Date: Tue, 7 Apr 2020 08:51:52 +0300 Message-Id: <20200407055156.137249-4-arilou@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200407055156.137249-1-arilou@gmail.com> References: <20200407055156.137249-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::343 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: mail@maciej.szmigiero.name, eyakovlev@virtuozzo.com, ehabkost@redhat.com, rvkagan@gmail.com, liran.alon@oracle.com, Roman Kagan , pbonzini@redhat.com, vkuznets@redhat.com, "Maciej S . Szmigiero" , 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: Maciej S. Szmigiero Signed-off-by: Jon Doron --- include/hw/hyperv/vmbus-proto.h | 222 ++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 include/hw/hyperv/vmbus-proto.h diff --git a/include/hw/hyperv/vmbus-proto.h b/include/hw/hyperv/vmbus-prot= o.h new file mode 100644 index 0000000000..4628d3b323 --- /dev/null +++ b/include/hw/hyperv/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 HW_HYPERV_VMBUS_PROTO_H +#define HW_HYPERV_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; + uint16_t interrupt_flags; + 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[]; +} 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[]; +} 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; + +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[]; +} 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[]; +} vmbus_pkt_xferpg; + +#endif --=20 2.24.1 From nobody Sat May 18 19:24:34 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=1586239037; cv=none; d=zohomail.com; s=zohoarc; b=LnsORAHbwDaMnnFum/3e1SDdCpsE/DNt3UrgB58GCNi/k5n3A04+wL88ejtcbwC2iemFPYn8DUhRfTNj0Bbt2ayYkCFX5zHUTzaHDDp9WiEiBLD40LV4ws9p0b/cHk6WJmVDa5voGbHio4Xzu2ZcNQGNTRuLRypGsJjBO647VpE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1586239037; 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=y2HOuAmct3ITwVdt9eY2OAEWx0oa4eujiE59oUR0u1A=; b=AkC+aBc9I5ibbo2b1hPPM5KvGVAfnTMCIs1EM4WBsoeBv+Vwpt1M98LrPKJyheG9OuLCariugIxH/R4KvOAVIbn3Tag07DFKNKtkng4el07rDwBXln2l4GwDBbYqY3uqPgvX0A9w4jVNRSMKlTtR3e7Y4NSgUgRiWCFZWZxFViw= 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 1586239037499478.42829492506735; Mon, 6 Apr 2020 22:57:17 -0700 (PDT) Received: from localhost ([::1]:41696 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jLhEa-0003QG-1V for importer@patchew.org; Tue, 07 Apr 2020 01:57:16 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:60178) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jLh9y-0004tS-BI for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:38 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1jLh9q-0002az-Bb for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:30 -0400 Received: from mail-wr1-x42f.google.com ([2a00:1450:4864:20::42f]:35330) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1jLh9p-0002ZV-S5 for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:22 -0400 Received: by mail-wr1-x42f.google.com with SMTP id g3so2414202wrx.2 for ; Mon, 06 Apr 2020 22:52:21 -0700 (PDT) Received: from jondnuc.lan (IGLD-84-229-155-55.inter.net.il. [84.229.155.55]) by smtp.gmail.com with ESMTPSA id n11sm32271007wrg.72.2020.04.06.22.52.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 06 Apr 2020 22:52:18 -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=y2HOuAmct3ITwVdt9eY2OAEWx0oa4eujiE59oUR0u1A=; b=GRPZqWCv6Msnhov6CdCuxSX5xgDvjwyGCKkJzgk5aSOsR0Ef7FzL93l5xgxMSMgwfa lYj1l0RPDLqZ9YJ8ozQvAglp2UIdZybUcSDOWVKjC2AVlg+WcXO7b08/Jo18gZbyWPlO jTQyQdz6CurMt2fIjOJk2jWU8W2TTlbaU52hDimNO1X02oWc882mI6kNblZrQNchT6Ts cpYSKco0R3cwG5jXS2SBmCGweUvwMnRQBDa2WrzkhveAr0mJn8FPBjv1tVCAeVES/XEa H00vdxKrfOb0l7OyAPWssznVXa7/QTQlKwkAwvMXc8oM5YdACV5XUcamtZUdklQh7FOG 39aw== 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=y2HOuAmct3ITwVdt9eY2OAEWx0oa4eujiE59oUR0u1A=; b=LZFM7vmS1BvflQQivMquvFraAz0GbbaFOBSuMoJ+y+wBpuPKLC42qYhm9lVpqqLvXp C/mNoQ95v+toYkNIIFoSvHPjgHRe8/7lTal1aYO+X0xN95hJ/TeFeoctPL39rp15/Bls uOt6Ra0NYFTdxTI40eqYpmgj7pbJYIkpPjNUOTsajqh20ASHftVaSjpni6o+NlyYJMrm 6OlB1u5DR4yr3lFDh2SVu3kgYdYx3jUfe0Ne+YHNRyziEXwct2eTjTCEFluN/ZxPotP2 dbftHow3xrr1fBtfjTdCYqt4+NkkBUp4+x6wZbCsocay3fAHdclwhrR4H/2Igq4kYy38 C8ww== X-Gm-Message-State: AGi0Pub+6JXFieAnR413eDXK5wfxija/XxruBdDhqri0y41YGGTUF1mC WVBcc/T58UbcnJhdHulQ6Lt2KzxDLR4= X-Google-Smtp-Source: APiQypImd48GavxQs2prunTAdeXcTDZkviDbDQGtJvfPzdNUeESth1YdvlfBKiOvVbUy/BSipr5dLg== X-Received: by 2002:adf:904e:: with SMTP id h72mr715883wrh.367.1586238738595; Mon, 06 Apr 2020 22:52:18 -0700 (PDT) From: Jon Doron To: qemu-devel@nongnu.org Subject: [PATCH v3 4/7] vmbus: vmbus implementation Date: Tue, 7 Apr 2020 08:51:53 +0300 Message-Id: <20200407055156.137249-5-arilou@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200407055156.137249-1-arilou@gmail.com> References: <20200407055156.137249-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::42f 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: mail@maciej.szmigiero.name, eyakovlev@virtuozzo.com, ehabkost@redhat.com, rvkagan@gmail.com, liran.alon@oracle.com, Roman Kagan , pbonzini@redhat.com, vkuznets@redhat.com, "Maciej S . Szmigiero" , 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. 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. 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. Signed-off-by: Roman Kagan Signed-off-by: Maciej S. Szmigiero Signed-off-by: Jon Doron --- Makefile.objs | 1 + hw/hyperv/Kconfig | 5 + hw/hyperv/Makefile.objs | 1 + hw/hyperv/trace-events | 18 + hw/hyperv/vmbus.c | 2672 ++++++++++++++++++++++++++++++ include/hw/hyperv/vmbus-bridge.h | 32 + include/hw/hyperv/vmbus.h | 227 +++ 7 files changed, 2956 insertions(+) create mode 100644 hw/hyperv/trace-events create mode 100644 hw/hyperv/vmbus.c create mode 100644 include/hw/hyperv/vmbus-bridge.h create mode 100644 include/hw/hyperv/vmbus.h diff --git a/Makefile.objs b/Makefile.objs index a7c967633a..1ef80ce58f 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -151,6 +151,7 @@ trace-events-subdirs +=3D hw/block/dataplane trace-events-subdirs +=3D hw/char trace-events-subdirs +=3D hw/dma trace-events-subdirs +=3D hw/hppa +trace-events-subdirs +=3D hw/hyperv trace-events-subdirs +=3D hw/i2c trace-events-subdirs +=3D hw/i386 trace-events-subdirs +=3D hw/i386/xen diff --git a/hw/hyperv/Kconfig b/hw/hyperv/Kconfig index a1fa8ff9be..3fbfe41c9e 100644 --- a/hw/hyperv/Kconfig +++ b/hw/hyperv/Kconfig @@ -6,3 +6,8 @@ config HYPERV_TESTDEV bool default y if TEST_DEVICES depends on HYPERV + +config VMBUS + bool + default y + depends on HYPERV diff --git a/hw/hyperv/Makefile.objs b/hw/hyperv/Makefile.objs index edaca2f763..5b614e040c 100644 --- a/hw/hyperv/Makefile.objs +++ b/hw/hyperv/Makefile.objs @@ -1,2 +1,3 @@ obj-y +=3D hyperv.o obj-$(CONFIG_HYPERV_TESTDEV) +=3D hyperv_testdev.o +obj-$(CONFIG_VMBUS) +=3D vmbus.o diff --git a/hw/hyperv/trace-events b/hw/hyperv/trace-events new file mode 100644 index 0000000000..ba5bd62d61 --- /dev/null +++ b/hw/hyperv/trace-events @@ -0,0 +1,18 @@ +# vmbus +vmbus_recv_message(uint32_t type, uint32_t size) "type %d size %d" +vmbus_signal_event(void) "" +vmbus_channel_notify_guest(uint32_t chan_id) "channel #%d" +vmbus_post_msg(uint32_t type, uint32_t size) "type %d size %d" +vmbus_msg_cb(int status) "message status %d" +vmbus_process_incoming_message(uint32_t message_type) "type %d" +vmbus_initiate_contact(uint16_t major, uint16_t minor, uint32_t vcpu, uint= 64_t monitor_page1, uint64_t monitor_page2, uint64_t interrupt_page) "versi= on %d.%d target vp %d mon pages 0x%"PRIx64",0x%"PRIx64" int page 0x%"PRIx64 +vmbus_send_offer(uint32_t chan_id, void *dev) "channel #%d dev %p" +vmbus_terminate_offers(void) "" +vmbus_gpadl_header(uint32_t gpadl_id, uint16_t num_gfns) "gpadl #%d gfns %= d" +vmbus_gpadl_body(uint32_t gpadl_id) "gpadl #%d" +vmbus_gpadl_created(uint32_t gpadl_id) "gpadl #%d" +vmbus_gpadl_teardown(uint32_t gpadl_id) "gpadl #%d" +vmbus_gpadl_torndown(uint32_t gpadl_id) "gpadl #%d" +vmbus_open_channel(uint32_t chan_id, uint32_t gpadl_id, uint32_t target_vp= ) "channel #%d gpadl #%d target vp %d" +vmbus_channel_open(uint32_t chan_id, uint32_t status) "channel #%d status = %d" +vmbus_close_channel(uint32_t chan_id) "channel #%d" diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c new file mode 100644 index 0000000000..1f5873ab60 --- /dev/null +++ b/hw/hyperv/vmbus.c @@ -0,0 +1,2672 @@ +/* + * 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 "qapi/error.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" +#include "hw/hyperv/hyperv.h" +#include "hw/hyperv/vmbus.h" +#include "hw/hyperv/vmbus-bridge.h" +#include "hw/sysbus.h" +#include "cpu.h" +#include "trace.h" + +#define TYPE_VMBUS "vmbus" +#define VMBUS(obj) OBJECT_CHECK(VMBus, (obj), TYPE_VMBUS) + +enum { + VMGPADL_INIT, + VMGPADL_ALIVE, + VMGPADL_TEARINGDOWN, + VMGPADL_TORNDOWN, +}; + +struct VMBusGpadl { + /* GPADL id */ + uint32_t id; + /* associated channel id (rudimentary?) */ + uint32_t child_relid; + + /* number of pages in the GPADL as declared in GPADL_HEADER message */ + uint32_t num_gfns; + /* + * Due to limited message size, GPADL may not fit fully in a single + * GPADL_HEADER message, and is further popluated using GPADL_BODY + * messages. @seen_gfns is the number of pages seen so far; once it + * reaches @num_gfns, the GPADL is ready to use. + */ + uint32_t seen_gfns; + /* array of GFNs (of size @num_gfns once allocated) */ + uint64_t *gfns; + + uint8_t state; + + QTAILQ_ENTRY(VMBusGpadl) link; + VMBus *vmbus; + unsigned refcount; +}; + +/* + * Wrap sequential read from / write to GPADL. + */ +typedef struct GpadlIter { + VMBusGpadl *gpadl; + AddressSpace *as; + DMADirection dir; + /* offset into GPADL where the next i/o will be performed */ + uint32_t off; + /* + * Cached mapping of the currently accessed page, up to page boundary. + * Updated lazily on i/o. + * Note: MemoryRegionCache can not be used here because pages in the G= PADL + * are non-contiguous and may belong to different memory regions. + */ + void *map; + /* offset after last i/o (i.e. not affected by seek) */ + uint32_t last_off; + /* + * Indicator that the iterator is active and may have a cached mapping. + * Allows to enforce bracketing of all i/o (which may create cached + * mappings) and thus exclude mapping leaks. + */ + bool active; +} GpadlIter; + +/* + * Ring buffer. There are two of them, sitting in the same GPADL, for each + * channel. + * Each ring buffer consists of a set of pages, with the first page contai= ning + * the ring buffer header, and the remaining pages being for data packets. + */ +typedef struct VMBusRingBufCommon { + AddressSpace *as; + /* GPA of the ring buffer header */ + dma_addr_t rb_addr; + /* start and length of the ring buffer data area within GPADL */ + uint32_t base; + uint32_t len; + + GpadlIter iter; +} VMBusRingBufCommon; + +typedef struct VMBusSendRingBuf { + VMBusRingBufCommon common; + /* current write index, to be committed at the end of send */ + uint32_t wr_idx; + /* write index at the start of send */ + uint32_t last_wr_idx; + /* space to be requested from the guest */ + uint32_t wanted; + /* space reserved for planned sends */ + uint32_t reserved; + /* last seen read index */ + uint32_t last_seen_rd_idx; +} VMBusSendRingBuf; + +typedef struct VMBusRecvRingBuf { + VMBusRingBufCommon common; + /* current read index, to be committed at the end of receive */ + uint32_t rd_idx; + /* read index at the start of receive */ + uint32_t last_rd_idx; + /* last seen write index */ + uint32_t last_seen_wr_idx; +} VMBusRecvRingBuf; + + +enum { + VMOFFER_INIT, + VMOFFER_SENDING, + VMOFFER_SENT, +}; + +enum { + VMCHAN_INIT, + VMCHAN_OPENING, + VMCHAN_OPEN, +}; + +struct VMBusChannel { + VMBusDevice *dev; + + /* channel id */ + uint32_t id; + /* + * subchannel index within the device; subchannel #0 is "primary" and + * always exists + */ + uint16_t subchan_idx; + uint32_t open_id; + /* VP_INDEX of the vCPU to notify with (synthetic) interrupts */ + uint32_t target_vp; + /* GPADL id to use for the ring buffers */ + uint32_t ringbuf_gpadl; + /* start (in pages) of the send ring buffer within @ringbuf_gpadl */ + uint32_t ringbuf_send_offset; + + uint8_t offer_state; + uint8_t state; + bool is_open; + + /* main device worker; copied from the device class */ + VMBusChannelNotifyCb notify_cb; + /* + * guest->host notifications, either sent directly or dispatched via + * interrupt page (older VMBus) + */ + EventNotifier notifier; + + VMBus *vmbus; + /* + * SINT route to signal with host->guest notifications; may be shared = with + * the main VMBus SINT route + */ + HvSintRoute *notify_route; + VMBusGpadl *gpadl; + + VMBusSendRingBuf send_ringbuf; + VMBusRecvRingBuf recv_ringbuf; + + QTAILQ_ENTRY(VMBusChannel) link; +}; + +/* + * Hyper-V spec mandates that every message port has 16 buffers, which mea= ns + * that the guest can post up to this many messages without blocking. + * Therefore a queue for incoming messages has to be provided. + * For outgoing (i.e. host->guest) messages there's no queue; the VMBus ju= st + * doesn't transition to a new state until the message is known to have be= en + * successfully delivered to the respective SynIC message slot. + */ +#define HV_MSG_QUEUE_LEN 16 + +/* Hyper-V devices never use channel #0. Must be something special. */ +#define VMBUS_FIRST_CHANID 1 +/* Each channel occupies one bit within a single event page sint slot. */ +#define VMBUS_CHANID_COUNT (HV_EVENT_FLAGS_COUNT - VMBUS_FIRST_CHANID) +/* Leave a few connection numbers for other purposes. */ +#define VMBUS_CHAN_CONNECTION_OFFSET 16 + +/* + * Since the success or failure of sending a message is reported + * asynchronously, the VMBus state machine has effectively two entry point= s: + * vmbus_run and vmbus_msg_cb (the latter is called when the host->guest + * message delivery status becomes known). Both are run as oneshot BHs on= the + * main aio context, ensuring serialization. + */ +enum { + VMBUS_LISTEN, + VMBUS_HANDSHAKE, + VMBUS_OFFER, + VMBUS_CREATE_GPADL, + VMBUS_TEARDOWN_GPADL, + VMBUS_OPEN_CHANNEL, + VMBUS_UNLOAD, + VMBUS_STATE_MAX +}; + +struct VMBus { + BusState parent; + + uint8_t state; + /* protection against recursive aio_poll (see vmbus_run) */ + bool in_progress; + /* whether there's a message being delivered to the guest */ + bool msg_in_progress; + uint32_t version; + /* VP_INDEX of the vCPU to send messages and interrupts to */ + uint32_t target_vp; + HvSintRoute *sint_route; + /* + * interrupt page for older protocol versions; newer ones use SynIC ev= ent + * flags directly + */ + hwaddr int_page_gpa; + + DECLARE_BITMAP(chanid_bitmap, VMBUS_CHANID_COUNT); + + /* incoming message queue */ + struct hyperv_post_message_input rx_queue[HV_MSG_QUEUE_LEN]; + uint8_t rx_queue_head; + uint8_t rx_queue_size; + QemuMutex rx_queue_lock; + + QTAILQ_HEAD(, VMBusGpadl) gpadl_list; + QTAILQ_HEAD(, VMBusChannel) channel_list; + + /* + * guest->host notifications for older VMBus, to be dispatched via + * interrupt page + */ + EventNotifier notifier; +}; + +static bool gpadl_full(VMBusGpadl *gpadl) +{ + return gpadl->seen_gfns =3D=3D 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); + gpadl->vmbus =3D vmbus; + gpadl->refcount =3D 1; + return gpadl; +} + +static void free_gpadl(VMBusGpadl *gpadl) +{ + QTAILQ_REMOVE(&gpadl->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 || !gpadl_full(gpadl)) { + return NULL; + } + gpadl->refcount++; + return gpadl; +} + +void vmbus_put_gpadl(VMBusGpadl *gpadl) +{ + if (!gpadl) { + return; + } + if (--gpadl->refcount) { + return; + } + free_gpadl(gpadl); +} + +uint32_t vmbus_gpadl_len(VMBusGpadl *gpadl) +{ + return gpadl->num_gfns * TARGET_PAGE_SIZE; +} + +static void gpadl_iter_init(GpadlIter *iter, VMBusGpadl *gpadl, + AddressSpace *as, DMADirection dir) +{ + iter->gpadl =3D gpadl; + iter->as =3D as; + iter->dir =3D dir; + iter->active =3D false; +} + +static inline void gpadl_iter_cache_unmap(GpadlIter *iter) +{ + uint32_t map_start_in_page =3D (uintptr_t)iter->map & ~TARGET_PAGE_MAS= K; + uint32_t io_end_in_page =3D ((iter->last_off - 1) & ~TARGET_PAGE_MASK)= + 1; + + /* mapping is only done to do non-zero amount of i/o */ + assert(iter->last_off > 0); + assert(map_start_in_page < io_end_in_page); + + dma_memory_unmap(iter->as, iter->map, TARGET_PAGE_SIZE - map_start_in_= page, + iter->dir, io_end_in_page - map_start_in_page); +} + +/* + * Copy exactly @len bytes between the GPADL pointed to by @iter and @buf. + * The direction of the copy is determined by @iter->dir. + * The caller must ensure the operation overflows neither @buf nor the GPA= DL + * (there's an assert for the latter). + * Reuse the currently mapped page in the GPADL if possible. + */ +static ssize_t gpadl_iter_io(GpadlIter *iter, void *buf, uint32_t len) +{ + ssize_t ret =3D len; + + assert(iter->active); + + while (len) { + uint32_t off_in_page =3D iter->off & ~TARGET_PAGE_MASK; + uint32_t pgleft =3D TARGET_PAGE_SIZE - off_in_page; + uint32_t cplen =3D MIN(pgleft, len); + void *p; + + /* try to reuse the cached mapping */ + if (iter->map) { + uint32_t map_start_in_page =3D + (uintptr_t)iter->map & ~TARGET_PAGE_MASK; + uint32_t off_base =3D iter->off & ~TARGET_PAGE_MASK; + uint32_t mapped_base =3D (iter->last_off - 1) & ~TARGET_PAGE_M= ASK; + if (off_base !=3D mapped_base || off_in_page < map_start_in_pa= ge) { + gpadl_iter_cache_unmap(iter); + iter->map =3D NULL; + } + } + + 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_i= n_page; + + 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); + iter->map =3D NULL; + return -EFAULT; + } + } + + p =3D (void *)(((uintptr_t)iter->map & TARGET_PAGE_MASK) | off_in_= page); + 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; + iter->last_off =3D iter->off; + } + + return ret; +} + +/* + * Position the iterator @iter at new offset @new_off. + * If this results in the cached mapping being unusable with the new offse= t, + * unmap it. + */ +static inline void gpadl_iter_seek(GpadlIter *iter, uint32_t new_off) +{ + assert(iter->active); + iter->off =3D new_off; +} + +/* + * Start a series of i/o on the GPADL. + * After this i/o and seek operations on @iter become legal. + */ +static inline void gpadl_iter_start_io(GpadlIter *iter) +{ + assert(!iter->active); + /* mapping is cached lazily on i/o */ + iter->map =3D NULL; + iter->active =3D true; +} + +/* + * End the eariler started series of i/o on the GPADL and release the cach= ed + * mapping if any. + */ +static inline void gpadl_iter_end_io(GpadlIter *iter) +{ + assert(iter->active); + + if (iter->map) { + gpadl_iter_cache_unmap(iter); + } + + iter->active =3D false; +} + +static void vmbus_resched(VMBus *vmbus); +static void vmbus_msg_cb(void *data, int status); + +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); + gpadl_iter_start_io(&iter); + gpadl_iter_seek(&iter, off); + for (i =3D 0; i < iov_cnt; i++) { + ret =3D gpadl_iter_io(&iter, iov[i].iov_base, iov[i].iov_len); + if (ret < 0) { + goto out; + } + } +out: + gpadl_iter_end_io(&iter); + return ret; +} + +int vmbus_map_sgl(VMBusChanReq *req, DMADirection dir, struct iovec *iov, + unsigned iov_cnt, size_t len, size_t off) +{ + int ret_cnt =3D 0, ret; + unsigned i; + QEMUSGList *sgl =3D &req->sgl; + 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(req, dir, iov, ret_cnt, 0); + return ret; +} + +void vmbus_unmap_sgl(VMBusChanReq *req, DMADirection dir, struct iovec *io= v, + unsigned iov_cnt, size_t accessed) +{ + QEMUSGList *sgl =3D &req->sgl; + 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 const VMStateDescription vmstate_gpadl =3D { + .name =3D "vmbus/gpadl", + .version_id =3D 0, + .minimum_version_id =3D 0, + .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_UINT8(state, VMBusGpadl), + VMSTATE_END_OF_LIST() + } +}; + +/* + * Wrap the index into a ring buffer of @len bytes. + * @idx is assumed not to exceed twice the size of the ringbuffer, so only + * single wraparound is considered. + */ +static inline uint32_t rb_idx_wrap(uint32_t idx, uint32_t len) +{ + if (idx >=3D len) { + idx -=3D len; + } + return idx; +} + +/* + * Circular difference between two indices into a ring buffer of @len byte= s. + * @allow_catchup - whether @idx1 may catch up @idx2; e.g. read index may = catch + * up write index but not vice versa. + */ +static inline uint32_t rb_idx_delta(uint32_t idx1, uint32_t idx2, uint32_t= len, + bool allow_catchup) +{ + return rb_idx_wrap(idx2 + len - idx1 - !allow_catchup, len); +} + +static vmbus_ring_buffer *ringbuf_map_hdr(VMBusRingBufCommon *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_hdr(VMBusRingBufCommon *ringbuf, + vmbus_ring_buffer *rb, bool dirty) +{ + assert(rb); + + dma_memory_unmap(ringbuf->as, rb, sizeof(*rb), DMA_DIRECTION_FROM_DEVI= CE, + dirty ? sizeof(*rb) : 0); +} + +static void ringbuf_init_common(VMBusRingBufCommon *ringbuf, VMBusGpadl *g= padl, + AddressSpace *as, DMADirection dir, + uint32_t begin, uint32_t end) +{ + ringbuf->as =3D as; + ringbuf->rb_addr =3D gpadl->gfns[begin] << TARGET_PAGE_BITS; + ringbuf->base =3D (begin + 1) << TARGET_PAGE_BITS; + ringbuf->len =3D (end - begin - 1) << TARGET_PAGE_BITS; + gpadl_iter_init(&ringbuf->iter, gpadl, as, dir); +} + +static int ringbufs_init(VMBusChannel *chan) +{ + vmbus_ring_buffer *rb; + VMBusSendRingBuf *send_ringbuf =3D &chan->send_ringbuf; + VMBusRecvRingBuf *recv_ringbuf =3D &chan->recv_ringbuf; + + if (chan->ringbuf_send_offset <=3D 1 || + chan->gpadl->num_gfns <=3D chan->ringbuf_send_offset + 1) { + return -EINVAL; + } + + ringbuf_init_common(&recv_ringbuf->common, chan->gpadl, chan->dev->dma= _as, + DMA_DIRECTION_TO_DEVICE, 0, chan->ringbuf_send_off= set); + ringbuf_init_common(&send_ringbuf->common, chan->gpadl, chan->dev->dma= _as, + DMA_DIRECTION_FROM_DEVICE, chan->ringbuf_send_offs= et, + chan->gpadl->num_gfns); + send_ringbuf->wanted =3D 0; + send_ringbuf->reserved =3D 0; + + rb =3D ringbuf_map_hdr(&recv_ringbuf->common); + if (!rb) { + return -EFAULT; + } + recv_ringbuf->rd_idx =3D recv_ringbuf->last_rd_idx =3D rb->read_index; + ringbuf_unmap_hdr(&recv_ringbuf->common, rb, false); + + rb =3D ringbuf_map_hdr(&send_ringbuf->common); + if (!rb) { + return -EFAULT; + } + send_ringbuf->wr_idx =3D send_ringbuf->last_wr_idx =3D rb->write_index; + send_ringbuf->last_seen_rd_idx =3D rb->read_index; + rb->feature_bits |=3D VMBUS_RING_BUFFER_FEAT_PENDING_SZ; + ringbuf_unmap_hdr(&send_ringbuf->common, rb, true); + + if (recv_ringbuf->rd_idx >=3D recv_ringbuf->common.len || + send_ringbuf->wr_idx >=3D send_ringbuf->common.len) { + return -EOVERFLOW; + } + + return 0; +} + +/* + * Perform io between the GPADL-backed ringbuffer @ringbuf and @buf, wrapp= ing + * around if needed. + * @len is assumed not to exceed the size of the ringbuffer, so only single + * wraparound is considered. + */ +static ssize_t ringbuf_io(VMBusRingBufCommon *ringbuf, void *buf, uint32_t= len) +{ + ssize_t ret1 =3D 0, ret2 =3D 0; + uint32_t remain =3D ringbuf->len + ringbuf->base - ringbuf->iter.off; + + if (len >=3D remain) { + ret1 =3D gpadl_iter_io(&ringbuf->iter, buf, remain); + if (ret1 < 0) { + return ret1; + } + gpadl_iter_seek(&ringbuf->iter, ringbuf->base); + buf +=3D remain; + len -=3D remain; + } + ret2 =3D gpadl_iter_io(&ringbuf->iter, buf, len); + if (ret2 < 0) { + return ret2; + } + return ret1 + ret2; +} + +/* + * Position the circular iterator within @ringbuf to offset @new_off, wrap= ping + * around if needed. + * @new_off is assumed not to exceed twice the size of the ringbuffer, so = only + * single wraparound is considered. + */ +static inline void ringbuf_seek(VMBusRingBufCommon *ringbuf, uint32_t new_= off) +{ + gpadl_iter_seek(&ringbuf->iter, + ringbuf->base + rb_idx_wrap(new_off, ringbuf->len)); +} + +static inline uint32_t ringbuf_tell(VMBusRingBufCommon *ringbuf) +{ + return ringbuf->iter.off - ringbuf->base; +} + +static inline void ringbuf_start_io(VMBusRingBufCommon *ringbuf) +{ + gpadl_iter_start_io(&ringbuf->iter); +} + +static inline void ringbuf_end_io(VMBusRingBufCommon *ringbuf) +{ + gpadl_iter_end_io(&ringbuf->iter); +} + +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_channel_notify_host(VMBusChannel *chan) +{ + event_notifier_set(&chan->notifier); +} + +bool vmbus_channel_is_open(VMBusChannel *chan) +{ + return chan->is_open; +} + +/* + * Notify the guest side about the data to work on in the channel ring buf= fer. + * The notification is done by signaling a dedicated per-channel SynIC eve= nt + * flag (more recent guests) or setting a bit in the interrupt page and fi= ring + * the VMBus SINT (older guests). + */ +static int vmbus_channel_notify_guest(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_channel_notify_guest(chan->id); + + if (!addr) { + return hyperv_set_event_flag(chan->notify_route, chan->id); + } + + 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->id); + mask =3D BIT_MASK(chan->id); + 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; +} + +#define VMBUS_PKT_TRAILER sizeof(uint64_t) + +static uint32_t vmbus_pkt_hdr_set_offsets(vmbus_packet_hdr *hdr, + uint32_t desclen, uint32_t msgle= n) +{ + 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)); + return hdr->len_qwords * sizeof(uint64_t) + VMBUS_PKT_TRAILER; +} + +/* + * Simplified ring buffer operation with paired barriers annotations in the + * producer and consumer loops: + * + * producer * consumer + * ~~~~~~~~ * ~~~~~~~~ + * write pending_send_sz * read write_index + * smp_mb [A] * smp_mb [C] + * read read_index * read packet + * smp_mb [B] * read/write out-of-band data + * read/write out-of-band data * smp_mb [B] + * write packet * write read_index + * smp_mb [C] * smp_mb [A] + * write write_index * read pending_send_sz + * smp_wmb [D] * smp_rmb [D] + * write pending_send_sz * read write_index + * ... * ... + */ + +static inline uint32_t ringbuf_send_avail(VMBusSendRingBuf *ringbuf) +{ + /* don't trust guest data */ + if (ringbuf->last_seen_rd_idx >=3D ringbuf->common.len) { + return 0; + } + return rb_idx_delta(ringbuf->wr_idx, ringbuf->last_seen_rd_idx, + ringbuf->common.len, false); +} + +static ssize_t ringbuf_send_update_idx(VMBusChannel *chan) +{ + VMBusSendRingBuf *ringbuf =3D &chan->send_ringbuf; + vmbus_ring_buffer *rb; + uint32_t written; + + written =3D rb_idx_delta(ringbuf->last_wr_idx, ringbuf->wr_idx, + ringbuf->common.len, true); + if (!written) { + return 0; + } + + rb =3D ringbuf_map_hdr(&ringbuf->common); + if (!rb) { + return -EFAULT; + } + + ringbuf->reserved -=3D written; + + /* prevent reorder with the data operation and packet write */ + smp_mb(); /* barrier pair [C] */ + rb->write_index =3D ringbuf->wr_idx; + + /* + * If the producer earlier indicated that it wants to be notified when= the + * consumer frees certain amount of space in the ring buffer, that amo= unt + * is reduced by the size of the completed write. + */ + if (ringbuf->wanted) { + /* otherwise reservation would fail */ + assert(ringbuf->wanted < written); + ringbuf->wanted -=3D written; + /* prevent reorder with write_index write */ + smp_wmb(); /* barrier pair [D] */ + rb->pending_send_sz =3D ringbuf->wanted; + } + + /* prevent reorder with write_index or pending_send_sz write */ + smp_mb(); /* barrier pair [A] */ + ringbuf->last_seen_rd_idx =3D rb->read_index; + + /* + * The consumer may have missed the reduction of pending_send_sz and s= kip + * notification, so re-check the blocking condition, and, if it's no l= onger + * true, ensure processing another iteration by simulating consumer's + * notification. + */ + if (ringbuf_send_avail(ringbuf) >=3D ringbuf->wanted) { + vmbus_channel_notify_host(chan); + } + + /* skip notification by consumer's request */ + if (rb->interrupt_mask) { + goto out; + } + + /* + * The consumer hasn't caught up with the producer's previous state so= it's + * not blocked. + * (last_seen_rd_idx comes from the guest but it's safe to use w/o + * validation here as it only affects notification.) + */ + if (rb_idx_delta(ringbuf->last_seen_rd_idx, ringbuf->wr_idx, + ringbuf->common.len, true) > written) { + goto out; + } + + vmbus_channel_notify_guest(chan); +out: + ringbuf_unmap_hdr(&ringbuf->common, rb, true); + ringbuf->last_wr_idx =3D ringbuf->wr_idx; + return written; +} + +int vmbus_channel_reserve(VMBusChannel *chan, + uint32_t desclen, uint32_t msglen) +{ + VMBusSendRingBuf *ringbuf =3D &chan->send_ringbuf; + vmbus_ring_buffer *rb =3D NULL; + vmbus_packet_hdr hdr; + uint32_t needed =3D ringbuf->reserved + + vmbus_pkt_hdr_set_offsets(&hdr, desclen, msglen); + + /* avoid touching the guest memory if possible */ + if (likely(needed <=3D ringbuf_send_avail(ringbuf))) { + goto success; + } + + rb =3D ringbuf_map_hdr(&ringbuf->common); + if (!rb) { + return -EFAULT; + } + + /* fetch read index from guest memory and try again */ + ringbuf->last_seen_rd_idx =3D rb->read_index; + + if (likely(needed <=3D ringbuf_send_avail(ringbuf))) { + goto success; + } + + rb->pending_send_sz =3D needed; + + /* + * The consumer may have made progress and freed up some space before + * seeing updated pending_send_sz, so re-read read_index (preventing + * reorder with the pending_send_sz write) and try again. + */ + smp_mb(); /* barrier pair [A] */ + ringbuf->last_seen_rd_idx =3D rb->read_index; + + if (needed > ringbuf_send_avail(ringbuf)) { + goto out; + } + +success: + ringbuf->reserved =3D needed; + needed =3D 0; + + /* clear pending_send_sz if it was set */ + if (ringbuf->wanted) { + if (!rb) { + rb =3D ringbuf_map_hdr(&ringbuf->common); + if (!rb) { + /* failure to clear pending_send_sz is non-fatal */ + goto out; + } + } + + rb->pending_send_sz =3D 0; + } + + /* prevent reorder of the following data operation with read_index rea= d */ + smp_mb(); /* barrier pair [B] */ + +out: + if (rb) { + ringbuf_unmap_hdr(&ringbuf->common, rb, ringbuf->wanted =3D=3D nee= ded); + } + ringbuf->wanted =3D needed; + return needed ? -ENOSPC : 0; +} + +ssize_t 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) +{ + ssize_t ret =3D 0; + vmbus_packet_hdr hdr; + uint32_t totlen; + VMBusSendRingBuf *ringbuf =3D &chan->send_ringbuf; + + if (!vmbus_channel_is_open(chan)) { + return -EINVAL; + } + + totlen =3D vmbus_pkt_hdr_set_offsets(&hdr, desclen, msglen); + hdr.type =3D pkt_type; + hdr.flags =3D need_comp ? VMBUS_PACKET_FLAG_REQUEST_COMPLETION : 0; + hdr.transaction_id =3D transaction_id; + + assert(totlen <=3D ringbuf->reserved); + + ringbuf_start_io(&ringbuf->common); + ringbuf_seek(&ringbuf->common, ringbuf->wr_idx); + ret =3D ringbuf_io(&ringbuf->common, &hdr, sizeof(hdr)); + if (ret < 0) { + goto out; + } + if (desclen) { + assert(desc); + ret =3D ringbuf_io(&ringbuf->common, desc, desclen); + if (ret < 0) { + goto out; + } + ringbuf_seek(&ringbuf->common, + ringbuf->wr_idx + hdr.offset_qwords * sizeof(uint64_t= )); + } + ret =3D ringbuf_io(&ringbuf->common, msg, msglen); + if (ret < 0) { + goto out; + } + ringbuf_seek(&ringbuf->common, ringbuf->wr_idx + totlen); + ringbuf->wr_idx =3D ringbuf_tell(&ringbuf->common); + ret =3D 0; +out: + ringbuf_end_io(&ringbuf->common); + if (ret) { + return ret; + } + return ringbuf_send_update_idx(chan); +} + +ssize_t vmbus_channel_send_completion(VMBusChanReq *req, + void *msg, uint32_t msglen) +{ + assert(req->need_comp); + return vmbus_channel_send(req->chan, VMBUS_PACKET_COMP, NULL, 0, + msg, msglen, false, req->transaction_id); +} + +static int sgl_from_gpa_ranges(QEMUSGList *sgl, VMBusDevice *dev, + VMBusRingBufCommon *ringbuf, uint32_t len) +{ + 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 ringbuf_io(ringbuf, &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(sgl, DEVICE(dev), num, ringbuf->as); + + for (; hdr.rangecount; hdr.rangecount--) { + vmbus_gpa_range range; + + if (len < sizeof(range)) { + goto eio; + } + ret =3D ringbuf_io(ringbuf, &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 ringbuf_io(ringbuf, &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(sgl, curaddr, curlen); + } + + curaddr =3D paddr; + curlen =3D plen; + } + } + } + + if (curlen) { + qemu_sglist_add(sgl, curaddr, curlen); + } + + return 0; +eio: + ret =3D -EIO; +err: + qemu_sglist_destroy(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 need_comp) +{ + VMBusChanReq *req; + uint32_t msgoff =3D QEMU_ALIGN_UP(size, __alignof__(*req->msg)); + uint32_t totlen =3D msgoff + msglen; + + 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->need_comp =3D need_comp; + return req; +} + +int vmbus_channel_recv_start(VMBusChannel *chan) +{ + VMBusRecvRingBuf *ringbuf =3D &chan->recv_ringbuf; + vmbus_ring_buffer *rb; + + rb =3D ringbuf_map_hdr(&ringbuf->common); + if (!rb) { + return -EFAULT; + } + ringbuf->last_seen_wr_idx =3D rb->write_index; + ringbuf_unmap_hdr(&ringbuf->common, rb, false); + + if (ringbuf->last_seen_wr_idx >=3D ringbuf->common.len) { + return -EOVERFLOW; + } + + /* prevent reorder of the following data operation with write_index re= ad */ + smp_mb(); /* barrier pair [C] */ + return 0; +} + +void *vmbus_channel_recv_peek(VMBusChannel *chan, uint32_t size) +{ + VMBusRecvRingBuf *ringbuf =3D &chan->recv_ringbuf; + vmbus_packet_hdr hdr =3D {}; + VMBusChanReq *req; + uint32_t avail; + uint32_t totlen, pktlen, msglen, msgoff, desclen; + + assert(size >=3D sizeof(*req)); + + /* safe as last_seen_wr_idx is validated in vmbus_channel_recv_start */ + avail =3D rb_idx_delta(ringbuf->rd_idx, ringbuf->last_seen_wr_idx, + ringbuf->common.len, true); + if (avail < sizeof(hdr)) { + return NULL; + } + + ringbuf_seek(&ringbuf->common, ringbuf->rd_idx); + if (ringbuf_io(&ringbuf->common, &hdr, sizeof(hdr)) < 0) { + return NULL; + } + + pktlen =3D hdr.len_qwords * sizeof(uint64_t); + totlen =3D pktlen + VMBUS_PKT_TRAILER; + if (totlen > avail) { + return NULL; + } + + 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); + return NULL; + } + + msglen =3D pktlen - msgoff; + + req =3D vmbus_alloc_req(chan, size, hdr.type, msglen, hdr.transaction_= id, + hdr.flags & VMBUS_PACKET_FLAG_REQUEST_COMPLETION= ); + + switch (hdr.type) { + case VMBUS_PACKET_DATA_USING_GPA_DIRECT: + desclen =3D msgoff - sizeof(hdr); + if (sgl_from_gpa_ranges(&req->sgl, chan->dev, &ringbuf->common, + desclen) < 0) { + error_report("%s: failed to convert GPA ranges to SGL", __func= __); + goto free_req; + } + break; + case VMBUS_PACKET_DATA_INBAND: + case VMBUS_PACKET_COMP: + break; + default: + error_report("%s: unexpected msg type: %x", __func__, hdr.type); + goto free_req; + } + + ringbuf_seek(&ringbuf->common, ringbuf->rd_idx + msgoff); + if (ringbuf_io(&ringbuf->common, req->msg, msglen) < 0) { + goto free_req; + } + ringbuf_seek(&ringbuf->common, ringbuf->rd_idx + totlen); + + return req; +free_req: + vmbus_free_req(req); + return NULL; +} + +void vmbus_channel_recv_pop(VMBusChannel *chan) +{ + VMBusRecvRingBuf *ringbuf =3D &chan->recv_ringbuf; + ringbuf->rd_idx =3D ringbuf_tell(&ringbuf->common); +} + +ssize_t vmbus_channel_recv_done(VMBusChannel *chan) +{ + VMBusRecvRingBuf *ringbuf =3D &chan->recv_ringbuf; + vmbus_ring_buffer *rb; + uint32_t read; + + read =3D rb_idx_delta(ringbuf->last_rd_idx, ringbuf->rd_idx, + ringbuf->common.len, true); + if (!read) { + return 0; + } + + rb =3D ringbuf_map_hdr(&ringbuf->common); + if (!rb) { + return -EFAULT; + } + + /* prevent reorder with the data operation and packet read */ + smp_mb(); /* barrier pair [B] */ + rb->read_index =3D ringbuf->rd_idx; + + /* prevent reorder of the following pending_send_sz read */ + smp_mb(); /* barrier pair [A] */ + + if (rb->interrupt_mask) { + goto out; + } + + if (rb->feature_bits & VMBUS_RING_BUFFER_FEAT_PENDING_SZ) { + uint32_t wr_idx, wr_avail; + uint32_t wanted =3D rb->pending_send_sz; + + if (!wanted) { + goto out; + } + + /* prevent reorder with pending_send_sz read */ + smp_rmb(); /* barrier pair [D] */ + wr_idx =3D rb->write_index; + + wr_avail =3D rb_idx_delta(wr_idx, ringbuf->rd_idx, ringbuf->common= .len, + true); + + /* the producer wasn't blocked on the consumer state */ + if (wr_avail >=3D read + wanted) { + goto out; + } + /* there's not enough space for the producer to make progress */ + if (wr_avail < wanted) { + goto out; + } + } + + vmbus_channel_notify_guest(chan); +out: + ringbuf_unmap_hdr(&ringbuf->common, rb, true); + ringbuf->last_rd_idx =3D ringbuf->rd_idx; + return read; +} + +void vmbus_free_req(void *req) +{ + VMBusChanReq *r =3D req; + + if (!req) { + return; + } + + if (r->sgl.dev) { + qemu_sglist_destroy(&r->sgl); + } + g_free(req); +} + +static void channel_event_cb(EventNotifier *e) +{ + VMBusChannel *chan =3D container_of(e, VMBusChannel, notifier); + if (event_notifier_test_and_clear(e)) { + /* + * All receives are supposed to happen within the device worker, so + * bracket it with ringbuf_start/end_io on the receive ringbuffer,= and + * potentially reuse the cached mapping throughout the worker. + * Can't do this for sends as they may happen outside the device + * worker. + */ + VMBusRecvRingBuf *ringbuf =3D &chan->recv_ringbuf; + ringbuf_start_io(&ringbuf->common); + chan->notify_cb(chan); + ringbuf_end_io(&ringbuf->common); + + } +} + +static int alloc_chan_id(VMBus *vmbus) +{ + int ret; + + ret =3D find_next_zero_bit(vmbus->chanid_bitmap, VMBUS_CHANID_COUNT, 0= ); + if (ret =3D=3D VMBUS_CHANID_COUNT) { + return -ENOMEM; + } + return ret + VMBUS_FIRST_CHANID; +} + +static int register_chan_id(VMBusChannel *chan) +{ + return test_and_set_bit(chan->id - VMBUS_FIRST_CHANID, + chan->vmbus->chanid_bitmap) ? -EEXIST : 0; +} + +static void unregister_chan_id(VMBusChannel *chan) +{ + clear_bit(chan->id - VMBUS_FIRST_CHANID, chan->vmbus->chanid_bitmap); +} + +static uint32_t chan_connection_id(VMBusChannel *chan) +{ + return VMBUS_CHAN_CONNECTION_OFFSET + chan->id; +} + +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; + + res =3D alloc_chan_id(vmbus); + if (res < 0) { + error_setg(errp, "no spare channel id"); + return; + } + chan->id =3D res; + register_chan_id(chan); + + /* + * The guest drivers depend on the device subchannels (idx #1+) to be + * offered after the primary channel (idx #0) of that device. To ensu= re + * that, record the channels on the channel list in the order they app= ear + * within the device. + */ + QTAILQ_INSERT_TAIL(&vmbus->channel_list, chan, link); +} + +static void deinit_channel(VMBusChannel *chan) +{ + assert(chan->state =3D=3D VMCHAN_INIT); + QTAILQ_REMOVE(&chan->vmbus->channel_list, chan, link); + unregister_chan_id(chan); +} + +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; + } + } + + return; + +err_init: + while (i--) { + deinit_channel(&dev->channels[i]); + } +error_out: + error_propagate(errp, err); +} + +static void free_channels(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 && vmbus_channel_is_open(chan)= ) { + 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->ringbuf_gpadl); + if (!chan->gpadl) { + return; + } + + if (ringbufs_init(chan)) { + goto put_gpadl; + } + + if (event_notifier_init(&chan->notifier, 0)) { + goto put_gpadl; + } + + event_notifier_set_handler(&chan->notifier, channel_event_cb); + + if (hyperv_set_event_flag_handler(chan_connection_id(chan), + &chan->notifier)) { + goto cleanup_notifier; + } + + chan->notify_route =3D make_sint_route(chan->vmbus, chan->target_vp); + if (!chan->notify_route) { + goto clear_event_flag_handler; + } + + if (vdc->open_channel && vdc->open_channel(chan)) { + goto unref_sint_route; + } + + chan->is_open =3D true; + return; + +unref_sint_route: + hyperv_sint_route_unref(chan->notify_route); +clear_event_flag_handler: + hyperv_set_event_flag_handler(chan_connection_id(chan), NULL); +cleanup_notifier: + event_notifier_set_handler(&chan->notifier, NULL); + event_notifier_cleanup(&chan->notifier); +put_gpadl: + vmbus_put_gpadl(chan->gpadl); +} + +static void close_channel(VMBusChannel *chan) +{ + VMBusDeviceClass *vdc =3D VMBUS_DEVICE_GET_CLASS(chan->dev); + + if (!chan->is_open) { + return; + } + + if (vdc->close_channel) { + vdc->close_channel(chan); + } + + hyperv_sint_route_unref(chan->notify_route); + hyperv_set_event_flag_handler(chan_connection_id(chan), NULL); + event_notifier_set_handler(&chan->notifier, NULL); + event_notifier_cleanup(&chan->notifier); + vmbus_put_gpadl(chan->gpadl); + chan->is_open =3D false; +} + +static int channel_post_load(void *opaque, int version_id) +{ + VMBusChannel *chan =3D opaque; + + return register_chan_id(chan); +} + +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(id, VMBusChannel), + VMSTATE_UINT16(subchan_idx, VMBusChannel), + VMSTATE_UINT32(open_id, VMBusChannel), + VMSTATE_UINT32(target_vp, VMBusChannel), + VMSTATE_UINT32(ringbuf_gpadl, VMBusChannel), + VMSTATE_UINT32(ringbuf_send_offset, VMBusChannel), + VMSTATE_UINT8(offer_state, VMBusChannel), + VMSTATE_UINT8(state, VMBusChannel), + VMSTATE_END_OF_LIST() + } +}; + +static VMBusChannel *find_channel(VMBus *vmbus, uint32_t id) +{ + VMBusChannel *chan; + QTAILQ_FOREACH(chan, &vmbus->channel_list, link) { + if (chan->id =3D=3D id) { + return chan; + } + } + return NULL; +} + +static int enqueue_incoming_message(VMBus *vmbus, + const struct hyperv_post_message_input= *msg) +{ + int ret =3D 0; + uint8_t idx, prev_size; + + qemu_mutex_lock(&vmbus->rx_queue_lock); + + if (vmbus->rx_queue_size =3D=3D HV_MSG_QUEUE_LEN) { + ret =3D -ENOBUFS; + goto out; + } + + prev_size =3D vmbus->rx_queue_size; + idx =3D (vmbus->rx_queue_head + vmbus->rx_queue_size) % HV_MSG_QUEUE_L= EN; + memcpy(&vmbus->rx_queue[idx], msg, sizeof(*msg)); + vmbus->rx_queue_size++; + + /* only need to resched if the queue was empty before */ + if (!prev_size) { + vmbus_resched(vmbus); + } +out: + qemu_mutex_unlock(&vmbus->rx_queue_lock); + return ret; +} + +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) +{ + VMBusGpadl *gpadl, *tmp_gpadl; + VMBusChannel *chan; + + QTAILQ_FOREACH_SAFE(gpadl, &vmbus->gpadl_list, link, tmp_gpadl) { + if (gpadl->state =3D=3D VMGPADL_TORNDOWN) { + continue; + } + vmbus_put_gpadl(gpadl); + } + + QTAILQ_FOREACH(chan, &vmbus->channel_list, link) { + chan->offer_state =3D VMOFFER_INIT; + } + + 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 >> 16, + msg->version_requested & 0xffff, + 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 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); + /* Hyper-V wants LE GUIDs */ + QemuUUID classid =3D qemu_uuid_bswap(vdc->classid); + QemuUUID instanceid =3D qemu_uuid_bswap(chan->dev->instanceid); + struct vmbus_message_offer_channel msg =3D { + .header.message_type =3D VMBUS_MSG_OFFERCHANNEL, + .child_relid =3D chan->id, + .connection_id =3D chan_connection_id(chan), + .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, + }; + + memcpy(msg.type_uuid, &classid, sizeof(classid)); + memcpy(msg.instance_uuid, &instanceid, sizeof(instanceid)); + + trace_vmbus_send_offer(chan->id, chan->dev); + + post_msg(vmbus, &msg, sizeof(msg)); + return; + } + } + + /* no more offers, send terminator message */ + trace_vmbus_terminate_offers(); + post_msg(vmbus, &alloffers_msg, sizeof(alloffers_msg)); +} + +static bool 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; + goto next_offer; + } + } + /* + * no transitioning channels found so this is completing the terminator + * message, and vmbus can move to the next state + */ + return true; + +next_offer: + /* try to mark another channel for offering */ + QTAILQ_FOREACH(chan, &vmbus->channel_list, link) { + if (chan->offer_state =3D=3D VMOFFER_INIT) { + chan->offer_state =3D VMOFFER_SENDING; + break; + } + } + /* + * if an offer has been sent there are more offers or the terminator y= et to + * send, so no state transition for vmbus + */ + return false; +} + + +static void handle_gpadl_header(VMBus *vmbus, vmbus_message_gpadl_header *= msg, + uint32_t msglen) +{ + VMBusGpadl *gpadl; + uint32_t num_gfns, i; + + /* must include at least one gpa range */ + if (msglen < sizeof(*msg) + sizeof(msg->range[0]) || + !vmbus_initialized(vmbus)) { + return; + } + + num_gfns =3D (msg->range_buflen - msg->rangecount * sizeof(msg->range[= 0])) / + sizeof(msg->range[0].pfn_array[0]); + + trace_vmbus_gpadl_header(msg->gpadl_id, num_gfns); + + /* + * In theory the GPADL_HEADER message can define a GPADL with multiple= GPA + * ranges each with arbitrary size and alignment. However in practice= only + * single-range page-aligned GPADLs have been observed so just ignore + * anything else and simplify things greatly. + */ + if (msg->rangecount !=3D 1 || msg->range[0].byte_offset || + (msg->range[0].byte_count !=3D (num_gfns << TARGET_PAGE_BITS))) { + return; + } + + /* ignore requests to create already existing GPADLs */ + if (find_gpadl(vmbus, msg->gpadl_id)) { + return; + } + + gpadl =3D create_gpadl(vmbus, msg->gpadl_id, msg->child_relid, num_gfn= s); + + for (i =3D 0; i < num_gfns && + (void *)&msg->range[0].pfn_array[i + 1] <=3D (void *)msg + msglen; + i++) { + gpadl->gfns[gpadl->seen_gfns++] =3D msg->range[0].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, i; + + if (msglen < sizeof(*msg) || !vmbus_initialized(vmbus)) { + return; + } + + trace_vmbus_gpadl_body(msg->gpadl_id); + + gpadl =3D find_gpadl(vmbus, msg->gpadl_id); + if (!gpadl) { + return; + } + + num_gfns_left =3D gpadl->num_gfns - gpadl->seen_gfns; + assert(num_gfns_left); + + for (i =3D 0; i < num_gfns_left && + (void *)&msg->pfn_array[i + 1] <=3D (void *)msg + msglen; 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->state =3D=3D VMGPADL_INIT) { + 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, + }; + + trace_vmbus_gpadl_created(gpadl->id); + post_msg(vmbus, &msg, sizeof(msg)); + return; + } + } + + assert(false); +} + +static bool complete_create_gpadl(VMBus *vmbus) +{ + VMBusGpadl *gpadl; + + QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) { + if (gpadl_full(gpadl) && gpadl->state =3D=3D VMGPADL_INIT) { + gpadl->state =3D VMGPADL_ALIVE; + + return true; + } + } + + assert(false); + return 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; + } + + trace_vmbus_gpadl_teardown(msg->gpadl_id); + + gpadl =3D find_gpadl(vmbus, msg->gpadl_id); + if (!gpadl || gpadl->state =3D=3D VMGPADL_TORNDOWN) { + return; + } + + gpadl->state =3D VMGPADL_TEARINGDOWN; + vmbus->state =3D VMBUS_TEARDOWN_GPADL; +} + +static void send_teardown_gpadl(VMBus *vmbus) +{ + VMBusGpadl *gpadl; + + QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) { + if (gpadl->state =3D=3D VMGPADL_TEARINGDOWN) { + struct vmbus_message_gpadl_torndown msg =3D { + .header.message_type =3D VMBUS_MSG_GPADL_TORNDOWN, + .gpadl_id =3D gpadl->id, + }; + + trace_vmbus_gpadl_torndown(gpadl->id); + post_msg(vmbus, &msg, sizeof(msg)); + return; + } + } + + assert(false); +} + +static bool complete_teardown_gpadl(VMBus *vmbus) +{ + VMBusGpadl *gpadl; + + QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) { + if (gpadl->state =3D=3D VMGPADL_TEARINGDOWN) { + gpadl->state =3D VMGPADL_TORNDOWN; + vmbus_put_gpadl(gpadl); + return true; + } + } + + assert(false); + return 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; + } + + trace_vmbus_open_channel(msg->child_relid, msg->ring_buffer_gpadl_id, + msg->target_vp); + chan =3D find_channel(vmbus, msg->child_relid); + if (!chan || chan->state !=3D VMCHAN_INIT) { + return; + } + + chan->ringbuf_gpadl =3D msg->ring_buffer_gpadl_id; + chan->ringbuf_send_offset =3D msg->ring_buffer_offset; + chan->target_vp =3D msg->target_vp; + chan->open_id =3D msg->open_id; + + open_channel(chan); + + chan->state =3D VMCHAN_OPENING; + 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->id, + .open_id =3D chan->open_id, + .status =3D !vmbus_channel_is_open(chan), + }; + + trace_vmbus_channel_open(chan->id, msg.status); + post_msg(vmbus, &msg, sizeof(msg)); + return; + } + } + + assert(false); +} + +static bool complete_open_channel(VMBus *vmbus) +{ + VMBusChannel *chan; + + QTAILQ_FOREACH(chan, &vmbus->channel_list, link) { + if (chan->state =3D=3D VMCHAN_OPENING) { + if (vmbus_channel_is_open(chan)) { + chan->state =3D VMCHAN_OPEN; + /* + * simulate guest notification of ringbuffer space made + * available, for the channel protocols where the host + * initiates the communication + */ + vmbus_channel_notify_host(chan); + } else { + chan->state =3D VMCHAN_INIT; + } + return true; + } + } + + assert(false); + return false; +} + +static void vdev_reset_on_close(VMBusDevice *vdev) +{ + uint16_t i; + + for (i =3D 0; i < vdev->num_channels; i++) { + if (vmbus_channel_is_open(&vdev->channels[i])) { + return; + } + } + + /* all channels closed -- reset device */ + qdev_reset_all(DEVICE(vdev)); +} + +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; + } + + trace_vmbus_close_channel(msg->child_relid); + + chan =3D find_channel(vmbus, msg->child_relid); + if (!chan) { + return; + } + + close_channel(chan); + chan->state =3D VMCHAN_INIT; + + vdev_reset_on_close(chan->dev); +} + +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 bool complete_unload(VMBus *vmbus) +{ + vmbus_reset_all(vmbus); + return true; +} + +static void process_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 HV_MSG_QUEUE_LEN; + + vmbus_resched(vmbus); +unlock: + qemu_mutex_unlock(&vmbus->rx_queue_lock); +} + +static const struct { + void (*run)(VMBus *vmbus); + bool (*complete)(VMBus *vmbus); +} state_runner[] =3D { + [VMBUS_LISTEN] =3D {process_message, NULL}, + [VMBUS_HANDSHAKE] =3D {send_handshake, NULL}, + [VMBUS_OFFER] =3D {send_offer, complete_offer}, + [VMBUS_CREATE_GPADL] =3D {send_create_gpadl, complete_create_gpadl= }, + [VMBUS_TEARDOWN_GPADL] =3D {send_teardown_gpadl, complete_teardown_gpa= dl}, + [VMBUS_OPEN_CHANNEL] =3D {send_open_channel, complete_open_channel= }, + [VMBUS_UNLOAD] =3D {send_unload, complete_unload}, +}; + +static void vmbus_do_run(VMBus *vmbus) +{ + if (vmbus->msg_in_progress) { + return; + } + + assert(vmbus->state < VMBUS_STATE_MAX); + assert(state_runner[vmbus->state].run); + state_runner[vmbus->state].run(vmbus); +} + +static void vmbus_run(void *opaque) +{ + VMBus *vmbus =3D opaque; + + /* make sure no recursion happens (e.g. due to recursive aio_poll()) */ + if (vmbus->in_progress) { + return; + } + + vmbus->in_progress =3D true; + /* + * FIXME: if vmbus_resched() is called from within vmbus_do_run(), it + * should go *after* the code that can result in aio_poll; otherwise + * reschedules can be missed. No idea how to enforce that. + */ + vmbus_do_run(vmbus); + vmbus->in_progress =3D false; +} + +static void vmbus_msg_cb(void *data, int status) +{ + VMBus *vmbus =3D data; + bool (*complete)(VMBus *vmbus); + + assert(vmbus->msg_in_progress); + + trace_vmbus_msg_cb(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; + } + + assert(vmbus->state < VMBUS_STATE_MAX); + complete =3D state_runner[vmbus->state].complete; + if (!complete || complete(vmbus)) { + vmbus->state =3D VMBUS_LISTEN; + } +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->id, 1)) { + if (!vmbus_channel_is_open(chan)) { + continue; + } + vmbus_channel_notify_host(chan); + 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]; + + 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(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 (vdev->channels) { + for (i =3D 0; i < vdev->num_channels; i++) { + VMBusChannel *chan =3D &vdev->channels[i]; + close_channel(chan); + chan->state =3D VMCHAN_INIT; + } + } + + if (vdc->vmdev_reset) { + vdc->vmdev_reset(vdev); + } +} + +static void vmbus_dev_unrealize(DeviceState *dev, Error **errp) +{ + VMBusDevice *vdev =3D VMBUS_DEVICE(dev); + VMBusDeviceClass *vdc =3D VMBUS_DEVICE_GET_CLASS(vdev); + + if (vdc->vmdev_unrealize) { + vdc->vmdev_unrealize(vdev, errp); + } + free_channels(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 Property vmbus_dev_instanceid =3D + DEFINE_PROP_UUID("instanceid", VMBusDevice, instan= ceid); + +static void vmbus_dev_instance_init(Object *obj) +{ + VMBusDevice *vdev =3D VMBUS_DEVICE(obj); + VMBusDeviceClass *vdc =3D VMBUS_DEVICE_GET_CLASS(vdev); + + if (!qemu_uuid_is_null(&vdc->instanceid)) { + /* Class wants to only have a single instance with a fixed UUID */ + vdev->instanceid =3D vdc->instanceid; + } else { + qdev_property_add_static(DEVICE(vdev), &vmbus_dev_instanceid); + } +} + +const VMStateDescription vmstate_vmbus_dev =3D { + .name =3D TYPE_VMBUS_DEVICE, + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D (VMStateField[]) { + VMSTATE_UINT8_ARRAY(instanceid.data, VMBusDevice, 16), + VMSTATE_UINT16(num_channels, VMBusDevice), + VMSTATE_STRUCT_VARRAY_POINTER_UINT16(channels, VMBusDevice, + num_channels, 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, + .instance_init =3D vmbus_dev_instance_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_EVENT_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 uuid[UUID_FMT_LEN + 1]; + + qemu_uuid_unparse(&vdev->instanceid, uuid); + return g_strdup_printf("%s@%s", qdev_fw_name(dev), uuid); +} + +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 int vmbus_pre_load(void *opaque) +{ + VMBusChannel *chan; + VMBus *vmbus =3D VMBUS(opaque); + + /* + * channel IDs allocated by the source will come in the migration stre= am + * for each channel, so clean up the ones allocated at realize + */ + QTAILQ_FOREACH(chan, &vmbus->channel_list, link) { + unregister_chan_id(chan); + } + + return 0; +} +static int vmbus_post_load(void *opaque, int version_id) +{ + int ret; + VMBus *vmbus =3D VMBUS(opaque); + VMBusGpadl *gpadl; + VMBusChannel *chan; + + ret =3D vmbus_init(vmbus); + if (ret) { + return ret; + } + + QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) { + gpadl->vmbus =3D vmbus; + gpadl->refcount =3D 1; + } + + /* + * reopening channels depends on initialized vmbus so it's done here + * instead of channel_post_load() + */ + QTAILQ_FOREACH(chan, &vmbus->channel_list, link) { + + if (chan->state =3D=3D VMCHAN_OPENING || chan->state =3D=3D VMCHAN= _OPEN) { + open_channel(chan); + } + + if (chan->state !=3D VMCHAN_OPEN) { + continue; + } + + if (!vmbus_channel_is_open(chan)) { + /* reopen failed, abort loading */ + return -1; + } + + /* resume processing on the guest side if it missed the notificati= on */ + hyperv_sint_route_set_sint(chan->notify_route); + /* ditto on the host side */ + vmbus_channel_notify_host(chan); + } + + 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[]) { + /* + * skip connection_id and message_type as they are validated before + * queueing and ignored on dequeueing + */ + 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(opaque); + 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(rx_queue_head, VMBus), + VMSTATE_UINT8(rx_queue_size, VMBus), + VMSTATE_STRUCT_ARRAY(rx_queue, VMBus, + HV_MSG_QUEUE_LEN, 0, + vmstate_post_message_input, + struct hyperv_post_message_input), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_vmbus =3D { + .name =3D TYPE_VMBUS, + .version_id =3D 0, + .minimum_version_id =3D 0, + .pre_load =3D vmbus_pre_load, + .post_load =3D vmbus_post_load, + .fields =3D (VMStateField[]) { + VMSTATE_UINT8(state, VMBus), + VMSTATE_UINT32(version, VMBus), + VMSTATE_UINT32(target_vp, VMBus), + VMSTATE_UINT64(int_page_gpa, VMBus), + VMSTATE_QTAILQ_V(gpadl_list, VMBus, 0, + vmstate_gpadl, VMBusGpadl, link), + VMSTATE_END_OF_LIST() + }, + .subsections =3D (const VMStateDescription * []) { + &vmstate_rx_queue, + NULL + } +}; + +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, +}; + +static void vmbus_bridge_realize(DeviceState *dev, Error **errp) +{ + VMBusBridge *bridge =3D VMBUS_BRIDGE(dev); + + /* + * here there's at least one vmbus bridge that is being realized, so + * vmbus_bridge_find can only return NULL if it's not unique + */ + if (!vmbus_bridge_find()) { + error_setg(errp, "there can be at most one %s in the system", + TYPE_VMBUS_BRIDGE); + return; + } + + if (!hyperv_is_synic_enabled()) { + error_report("VMBus requires usable Hyper-V SynIC and VP_INDEX"); + return; + } + + bridge->bus =3D VMBUS(qbus_create(TYPE_VMBUS, dev, "vmbus")); +} + +static char *vmbus_bridge_ofw_unit_address(const SysBusDevice *dev) +{ + /* there can be only one VMBus */ + return g_strdup("0"); +} + +static const VMStateDescription vmstate_vmbus_bridge =3D { + .name =3D TYPE_VMBUS_BRIDGE, + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D (VMStateField[]) { + VMSTATE_STRUCT_POINTER(bus, VMBusBridge, vmstate_vmbus, VMBus), + VMSTATE_END_OF_LIST() + }, +}; + +static void vmbus_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k =3D DEVICE_CLASS(klass); + SysBusDeviceClass *sk =3D SYS_BUS_DEVICE_CLASS(klass); + + k->realize =3D vmbus_bridge_realize; + k->fw_name =3D "vmbus"; + sk->explicit_ofw_unit_address =3D vmbus_bridge_ofw_unit_address; + set_bit(DEVICE_CATEGORY_BRIDGE, k->categories); + k->vmsd =3D &vmstate_vmbus_bridge; + /* override SysBusDevice's default */ + 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/hyperv/vmbus-bridge.h b/include/hw/hyperv/vmbus-bri= dge.h new file mode 100644 index 0000000000..9cc8f780de --- /dev/null +++ b/include/hw/hyperv/vmbus-bridge.h @@ -0,0 +1,32 @@ +/* + * QEMU Hyper-V VMBus root bridge + * + * 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 HW_HYPERV_VMBUS_BRIDGE_H +#define HW_HYPERV_VMBUS_BRIDGE_H + +#include "hw/sysbus.h" + +#define TYPE_VMBUS_BRIDGE "vmbus-bridge" + +typedef struct VMBus VMBus; + +typedef struct VMBusBridge { + SysBusDevice parent_obj; + + VMBus *bus; +} VMBusBridge; + +#define VMBUS_BRIDGE(obj) OBJECT_CHECK(VMBusBridge, (obj), TYPE_VMBUS_BRID= GE) + +static inline VMBusBridge *vmbus_bridge_find(void) +{ + return VMBUS_BRIDGE(object_resolve_path_type("", TYPE_VMBUS_BRIDGE, NU= LL)); +} + +#endif diff --git a/include/hw/hyperv/vmbus.h b/include/hw/hyperv/vmbus.h new file mode 100644 index 0000000000..63a5b807b6 --- /dev/null +++ b/include/hw/hyperv/vmbus.h @@ -0,0 +1,227 @@ +/* + * 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 HW_HYPERV_VMBUS_H +#define HW_HYPERV_VMBUS_H + +#include "sysemu/sysemu.h" +#include "sysemu/dma.h" +#include "hw/qdev-core.h" +#include "migration/vmstate.h" +#include "hw/hyperv/vmbus-proto.h" +#include "qemu/uuid.h" + +#define TYPE_VMBUS_DEVICE "vmbus-dev" + +#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) + +/* + * Object wrapping a GPADL -- GPA Descriptor List -- an array of guest phy= sical + * pages, to be used for various buffers shared between the host and the g= uest. + */ +typedef struct VMBusGpadl VMBusGpadl; +/* + * VMBus channel -- a pair of ring buffers for either direction, placed wi= thin + * one GPADL, and the associated notification means. + */ +typedef struct VMBusChannel VMBusChannel; +/* + * Base class for VMBus devices. Includes one or more channels. Identifi= ed by + * class GUID and instance GUID. + */ +typedef struct VMBusDevice VMBusDevice; + +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; + + /* Extentions to standard device callbacks */ + void (*vmdev_realize)(VMBusDevice *vdev, Error **errp); + void (*vmdev_unrealize)(VMBusDevice *vdev, Error **errp); + void (*vmdev_reset)(VMBusDevice *vdev); + /* + * Calculate the number of channels based on the device properties. C= alled + * at realize time. + **/ + uint16_t (*num_channels)(VMBusDevice *vdev); + /* + * Device-specific actions to complete the otherwise successful proces= s of + * opening a channel. + * Return 0 on success, -errno on failure. + */ + int (*open_channel)(VMBusChannel *chan); + /* + * Device-specific actions to perform before closing a channel. + */ + void (*close_channel)(VMBusChannel *chan); + /* + * Main device worker; invoked in response to notifications from either + * side, when there's work to do with the data in the channel ring buf= fers. + */ + VMBusChannelNotifyCb chan_notify_cb; +} VMBusDeviceClass; + +struct VMBusDevice { + DeviceState parent; + QemuUUID instanceid; + uint16_t num_channels; + VMBusChannel *channels; + AddressSpace *dma_as; +}; + +extern const VMStateDescription vmstate_vmbus_dev; + +/* + * A unit of work parsed out of a message in the receive (i.e. guest->host) + * ring buffer of a channel. It's supposed to be subclassed (through + * embedding) by the specific devices. + */ +typedef struct VMBusChanReq { + VMBusChannel *chan; + uint16_t pkt_type; + uint32_t msglen; + void *msg; + uint64_t transaction_id; + bool need_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); +bool vmbus_channel_is_open(VMBusChannel *chan); + +/* + * Notify (on guest's behalf) the host side of the channel that there's da= ta in + * the ringbuffer to process. + */ +void vmbus_channel_notify_host(VMBusChannel *chan); + +/* + * Reserve space for a packet in the send (i.e. host->guest) ringbuffer. = If + * there isn't enough room, indicate that to the guest, to be notified whe= n it + * becomes available. + * Return 0 on success, negative errno on failure. + * The ringbuffer indices are NOT updated, the requested space indicator m= ay. + */ +int vmbus_channel_reserve(VMBusChannel *chan, + uint32_t desclen, uint32_t msglen); + +/* + * Send a packet to the guest. The space for the packet MUST be reserved + * first. + * Return total number of bytes placed in the send ringbuffer on success, + * negative errno on failure. + * The ringbuffer indices are updated on success, and the guest is signale= d if + * needed. + */ +ssize_t 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); + +/* + * Prepare to fetch a batch of packets from the receive ring buffer. + * Return 0 on success, negative errno on failure. + */ +int vmbus_channel_recv_start(VMBusChannel *chan); + +/* + * Shortcut for a common case of sending a simple completion packet with no + * auxiliary descriptors. + */ +ssize_t vmbus_channel_send_completion(VMBusChanReq *req, + void *msg, uint32_t msglen); + +/* + * Peek at the receive (i.e. guest->host) ring buffer and extract a unit of + * work (a device-specific subclass of VMBusChanReq) from a packet if ther= e's + * one. + * Return an allocated buffer, containing the request of @size with filled + * VMBusChanReq at the beginning, followed by the message payload, or NULL= on + * failure. + * The ringbuffer indices are NOT updated, nor is the private copy of the = read + * index. + */ +void *vmbus_channel_recv_peek(VMBusChannel *chan, uint32_t size); + +/* + * Update the private copy of the read index once the preceding peek is de= emed + * successful. + * The ringbuffer indices are NOT updated. + */ +void vmbus_channel_recv_pop(VMBusChannel *chan); + +/* + * Propagate the private copy of the read index into the receive ring buff= er, + * and thus complete the reception of a series of packets. Notify guest if + * needed. + * Return the number of bytes popped off the receive ring buffer by the + * preceding recv_peek/recv_pop calls on success, negative errno on failur= e. + */ +ssize_t vmbus_channel_recv_done(VMBusChannel *chan); + +/* + * Free the request allocated by vmbus_channel_recv_peek, together with its + * fields. + */ +void vmbus_free_req(void *req); + +/* + * Find and reference a GPADL by @gpadl_id. + * If not found return NULL. + */ +VMBusGpadl *vmbus_get_gpadl(VMBusChannel *chan, uint32_t gpadl_id); + +/* + * Unreference @gpadl. If the reference count drops to zero, free it. + * @gpadl may be NULL, in which case nothing is done. + */ +void vmbus_put_gpadl(VMBusGpadl *gpadl); + +/* + * Calculate total length in bytes of @gpadl. + * @gpadl must be valid. + */ +uint32_t vmbus_gpadl_len(VMBusGpadl *gpadl); + +/* + * Copy data from @iov to @gpadl at offset @off. + * Return the number of bytes copied, or a negative status on failure. + */ +ssize_t vmbus_iov_to_gpadl(VMBusChannel *chan, VMBusGpadl *gpadl, uint32_t= off, + const struct iovec *iov, size_t iov_cnt); + +/* + * Map SGList contained in the request @req, at offset @off and no more th= an + * @len bytes, for io in direction @dir, and populate @iov with the mapped + * iovecs. + * Return the number of iovecs mapped, or negative status on failure. + */ +int vmbus_map_sgl(VMBusChanReq *req, DMADirection dir, struct iovec *iov, + unsigned iov_cnt, size_t len, size_t off); + +/* + * Unmap *iov mapped with vmbus_map_sgl, marking the number of bytes @acce= ssed. + */ +void vmbus_unmap_sgl(VMBusChanReq *req, DMADirection dir, struct iovec *io= v, + unsigned iov_cnt, size_t accessed); + +#endif --=20 2.24.1 From nobody Sat May 18 19:24:34 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=1586238872; cv=none; d=zohomail.com; s=zohoarc; b=kN3Q9hfXHMJK5lo9BV0AqDOdbvHz87GvpFDMTeRGIRprbjGiDJRyhq+7Cxw5J8n+mwee4HLu5HViqtLz7O358x7Wx/RyPyGb90jkc473SkfusbRli1WrWMqCggHjyoXM94p+wCWivqLYRhKkohDd+y7MHLi4AWH16Rzh+UAoKX8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1586238872; 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=Hp0TKGKSi/gHtI8I2mIP8YOmXXrvnTmTwZnNNAObxR0=; b=I1v8qzcwh+2+1ooDYOApAnTzIWU95HYAeLKZEMskZC46Z56Ku0/0oimNYMPL0kztjWHR/K6lkaxLdiOapOc3bNHZ8rZ05VgYjWajVol6aGFT5mam4fc7k8cmy//P0ZRIm6JQmgwmQSEZIx55NM5sCgcZWEtNLEMQ/G0lKHrrOlA= 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 158623887282314.591867900858006; Mon, 6 Apr 2020 22:54:32 -0700 (PDT) Received: from localhost ([::1]:41658 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jLhBv-0008Jk-IV for importer@patchew.org; Tue, 07 Apr 2020 01:54:31 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:60138) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jLh9q-0004gp-E1 for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:24 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1jLh9p-0002Zb-CK for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:22 -0400 Received: from mail-wr1-x444.google.com ([2a00:1450:4864:20::444]:41951) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1jLh9p-0002ZD-6I for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:21 -0400 Received: by mail-wr1-x444.google.com with SMTP id h9so2370376wrc.8 for ; Mon, 06 Apr 2020 22:52:21 -0700 (PDT) Received: from jondnuc.lan (IGLD-84-229-155-55.inter.net.il. [84.229.155.55]) by smtp.gmail.com with ESMTPSA id n11sm32271007wrg.72.2020.04.06.22.52.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 06 Apr 2020 22:52:19 -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=Hp0TKGKSi/gHtI8I2mIP8YOmXXrvnTmTwZnNNAObxR0=; b=DAFn+9OBHJ3XePhKbPeuQtEXXAeLDkQJUVhVDSKxHdjsiWMkDzfbM8qcW6ul+vVVNc rGgcT0o4Eu3X51JfbgqHsx5TQZtWqWUvNFSC53olLwshhtwcpZzHu2NxAeTzTGKGCtLl OBR+7T50MxNdLjMmlyeQEpPfx/i/0VhC5O1t6f/F+BnBOJ+z4P0Zb/jmGfvsBTKrzrTv XeMykA0N3APeR+bxZK7gDJFWJY9+WY6ecGwwfpyeovOGzmB+G+3SEWtwUaUWSxL21DUJ 1Fuv5E2Bdbrq30mGlxaaJBouOyR4WeWvys9AV1+eaC1xgJWCNYhz4kGk+Umo47ZnlWHB 9Q0A== 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=Hp0TKGKSi/gHtI8I2mIP8YOmXXrvnTmTwZnNNAObxR0=; b=Fhm1UTNTr9lOAo0cMYZJEtQvCqnzx9FDqXxB1AooiesBWVG41S9hV37OFK/tLm6/5/ 3brGPMGHGtKHv55fQ4BoOsQpW6Ji6IOQJWT2HFK7DM8Dq6klGXJiqQGpNAi+qmXqyjrf me1Bf4unNREy3bHEn4sTlSFM2wyVRCrHjZaliq5PEzc2CGo9UbvDahuz+sVLBJqf00iY OB8DbdYfDDesO6ZxebYtazTcgKbbTIambuKfYKNKjgCkXRui7qejtmaOkMkuk0b3MhLH YUgYLpuB/zkU1uv1Nto78vvZEWuTToAx4zhLf3R+GyF2ZF4LwPxQUUA8gRG0aR9+es0r spJg== X-Gm-Message-State: AGi0PuYqC0MqmaD10I0dhuz7wG7awlouB6TQzA0K+n4T4copLlYVsfv1 sAasOlhMnrr+c4fIX/LUEk8HxhA7C1c= X-Google-Smtp-Source: APiQypIuC3OFNyEw5O9VDW0yU59O2AM++DydssQssSVurW7MrWqb5wSZTXJnwiVNaiTexYB15JuLjA== X-Received: by 2002:adf:f74d:: with SMTP id z13mr795119wrp.55.1586238740036; Mon, 06 Apr 2020 22:52:20 -0700 (PDT) From: Jon Doron To: qemu-devel@nongnu.org Subject: [PATCH v3 5/7] i386:pc: whitelist dynamic vmbus-bridge Date: Tue, 7 Apr 2020 08:51:54 +0300 Message-Id: <20200407055156.137249-6-arilou@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200407055156.137249-1-arilou@gmail.com> References: <20200407055156.137249-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: mail@maciej.szmigiero.name, eyakovlev@virtuozzo.com, ehabkost@redhat.com, rvkagan@gmail.com, liran.alon@oracle.com, Roman Kagan , pbonzini@redhat.com, vkuznets@redhat.com, "Maciej S . Szmigiero" , 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" As vmbus-bridge is derived from sysbus device, it has to be whitelisted to be allowed to be created with -device. Signed-off-by: Roman Kagan Signed-off-by: Maciej S. Szmigiero Signed-off-by: Jon Doron --- hw/i386/pc_piix.c | 2 ++ hw/i386/pc_q35.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 9cceae3e2c..6daa0770fa 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -60,6 +60,7 @@ #include "migration/global_state.h" #include "migration/misc.h" #include "sysemu/numa.h" +#include "hw/hyperv/vmbus-bridge.h" #include "hw/mem/nvdimm.h" =20 #define MAX_IDE_BUS 2 @@ -417,6 +418,7 @@ static void pc_i440fx_machine_options(MachineClass *m) m->default_machine_opts =3D "firmware=3Dbios-256k.bin"; m->default_display =3D "std"; machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); + machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE); } =20 static void pc_i440fx_5_0_machine_options(MachineClass *m) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index d37c425e22..faaa39ced2 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -53,6 +53,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "sysemu/numa.h" +#include "hw/hyperv/vmbus-bridge.h" #include "hw/mem/nvdimm.h" =20 /* ICH9 AHCI has 6 ports */ @@ -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 --=20 2.24.1 From nobody Sat May 18 19:24:34 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=1586238965; cv=none; d=zohomail.com; s=zohoarc; b=SC1F6JpQgTBhqZDteolo1/t415m6pYZCL4Xo1Nu46EMAA2Cva6cdH8Oyl2ufM+DgevWBIJapkh0Gp3TBqwmrAByS49ElfXEgIRf8LlrZPHu1C5yEXyh4LW/XKRVclITrt9ho/z4ygofg22w/tv+XkobpyacnNnkKHcy8JGDGuGM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1586238965; 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=ErYhhtGatTVVSppf6eN8KOXRLomnBrdur/FAKDAYlG4=; b=WQh3VIaubX5YkvNf0iOp0NqQauTWEHyTteE+rZFBMc75BMyB7MZAI2UR5GD8IzEZH0Laoj2SqUDEd8l/UfBLlTkBFnlPam9vuXd1KGnDnH0l9VzkW6/4XmGINDeRh6a6kACOqWTao2J1d/d7i794waBFyWCU+kCTz7/AiEmuYj8= 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 1586238965619722.84147597587; Mon, 6 Apr 2020 22:56:05 -0700 (PDT) Received: from localhost ([::1]:41690 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jLhDQ-0002U2-EE for importer@patchew.org; Tue, 07 Apr 2020 01:56:04 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:60150) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jLh9s-0004iW-33 for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:28 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1jLh9q-0002bY-UB for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:24 -0400 Received: from mail-wm1-x343.google.com ([2a00:1450:4864:20::343]:38486) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1jLh9q-0002aL-O8 for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:22 -0400 Received: by mail-wm1-x343.google.com with SMTP id f20so488049wmh.3 for ; Mon, 06 Apr 2020 22:52:22 -0700 (PDT) Received: from jondnuc.lan (IGLD-84-229-155-55.inter.net.il. [84.229.155.55]) by smtp.gmail.com with ESMTPSA id n11sm32271007wrg.72.2020.04.06.22.52.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 06 Apr 2020 22:52:21 -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=ErYhhtGatTVVSppf6eN8KOXRLomnBrdur/FAKDAYlG4=; b=cLYdCIcqI7Y3RHNzjGvusyWuhlDDBz2bZwMVbspi1F7YP2jpc2V97k1uzkRHlYQGON T6Uqtly+nYeUlrn0Wm+1CTky73WYqjXt1bB8eAgz5Bb1YVZ07sHR6zs0TRqxWQQYjQYb 8GdpAgYiX6yEqAFTaTvy7hPgM7U6IpnPl5ihPEL9WPhztmUru26vHtWP7m0ZSk/rrIT3 BaA6+LnjgfAgN5NQ5e5hMRd7RiBP1jlGqguS5m4mFCRet0Yb8AaON85Xfr98W3AGJ2QP On4eGJGt1ojcF0s4EkOIi3UJwn04yNe+ntPYw7xtk+Xr6LGtQhv0VHsNxjtRTD52LA6u nElQ== 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=ErYhhtGatTVVSppf6eN8KOXRLomnBrdur/FAKDAYlG4=; b=UYSvM3PsyenY0D3OOFNQH2Squ5SEm5JQw8sU/JhPipIt08UhkU1V+Dqa+rSShVudq9 hTS7NnWFcJTTw8zD+BAtVrjDySWj7qtdBDjZYCVkuvgKWDfS5LyY+tF3HdRt7M/yFJlb 42vWkpnH7ypCzEd6a+spEBOIDooRETs+F1x9IihxUOsMQRMTfRGK7GmmVDLqUIKwqe48 61/JwY28GOTwTHdA3SrUJ/DDHz5zbH/JuE+8+UBwYHT44zqSH1qDPWB8xlVrrK4i3BBl cQ2ZkulnLQ0lZBF0jPaJCNoOp8j6y+O5BVP3UG667M9aEwO2ddCr4xfi0ZMc2yJ931zm prCg== X-Gm-Message-State: AGi0PuZwLU2il3dUwM1nH4TtzwCVQuMOKjtd2YHSecQ1eE0ldvCFPhaO CNUMPZcQkZA/k9rMLTMEW7nLO2JPYmQ= X-Google-Smtp-Source: APiQypL/Ws1lyBFW7kTzgk6aAWycwlNlOGQ8s7O9YagMtH8Hx4ans+JtfKEvZkgsqBJjLPWwAHXQKQ== X-Received: by 2002:a1c:2404:: with SMTP id k4mr507517wmk.87.1586238741617; Mon, 06 Apr 2020 22:52:21 -0700 (PDT) From: Jon Doron To: qemu-devel@nongnu.org Subject: [PATCH v3 6/7] i386: Hyper-V VMBus ACPI DSDT entry Date: Tue, 7 Apr 2020 08:51:55 +0300 Message-Id: <20200407055156.137249-7-arilou@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200407055156.137249-1-arilou@gmail.com> References: <20200407055156.137249-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::343 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: mail@maciej.szmigiero.name, eyakovlev@virtuozzo.com, ehabkost@redhat.com, rvkagan@gmail.com, liran.alon@oracle.com, Roman Kagan , pbonzini@redhat.com, vkuznets@redhat.com, "Maciej S . Szmigiero" , 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. They seem to never be used but they still have to be there. Make IRQ numbers user-configurable via corresponding properties; use 7 and 13 by default. Signed-off-by: Evgeny Yakovlev Signed-off-by: Roman Kagan Signed-off-by: Maciej S. Szmigiero Signed-off-by: Jon Doron --- hw/hyperv/vmbus.c | 7 ++++++ hw/i386/acpi-build.c | 43 ++++++++++++++++++++++++++++++++ include/hw/hyperv/vmbus-bridge.h | 3 +++ 3 files changed, 53 insertions(+) diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index 1f5873ab60..0df7afe0ca 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -2641,6 +2641,12 @@ static const VMStateDescription vmstate_vmbus_bridge= =3D { }, }; =20 +static Property vmbus_bridge_props[] =3D { + DEFINE_PROP_UINT8("irq0", VMBusBridge, irq0, 7), + DEFINE_PROP_UINT8("irq1", VMBusBridge, irq1, 13), + DEFINE_PROP_END_OF_LIST() +}; + static void vmbus_bridge_class_init(ObjectClass *klass, void *data) { DeviceClass *k =3D DEVICE_CLASS(klass); @@ -2651,6 +2657,7 @@ static void vmbus_bridge_class_init(ObjectClass *klas= s, void *data) sk->explicit_ofw_unit_address =3D vmbus_bridge_ofw_unit_address; set_bit(DEVICE_CATEGORY_BRIDGE, k->categories); k->vmsd =3D &vmstate_vmbus_bridge; + device_class_set_props(k, vmbus_bridge_props); /* override SysBusDevice's default */ k->user_creatable =3D true; } diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 2a7e55bae7..d235074fb8 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -50,6 +50,7 @@ #include "hw/mem/nvdimm.h" #include "sysemu/numa.h" #include "sysemu/reset.h" +#include "hw/hyperv/vmbus-bridge.h" =20 /* Supported chipsets: */ #include "hw/southbridge/piix.h" @@ -1270,9 +1271,47 @@ static Aml *build_com_device_aml(uint8_t uid) return dev; } =20 +static Aml *build_vmbus_device_aml(VMBusBridge *vmbus_bridge) +{ + 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_return(aml_name("STA"))); + 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(vmbus_bridge->irq0)); + /* FIXME: newer HyperV gets by with only one IRQ */ + aml_append(crs, aml_irq_no_flags(vmbus_bridge->irq1)); + aml_append(dev, aml_name_decl("_CRS", crs)); + + return dev; +} + static void build_isa_devices_aml(Aml *table) { ISADevice *fdc =3D pc_find_fdc0(); + VMBusBridge *vmbus_bridge =3D vmbus_bridge_find(); bool ambiguous; =20 Aml *scope =3D aml_scope("_SB.PCI0.ISA"); @@ -1296,6 +1335,10 @@ static void build_isa_devices_aml(Aml *table) build_acpi_ipmi_devices(scope, BUS(obj), "\\_SB.PCI0.ISA"); } =20 + if (vmbus_bridge) { + aml_append(scope, build_vmbus_device_aml(vmbus_bridge)); + } + aml_append(table, scope); } =20 diff --git a/include/hw/hyperv/vmbus-bridge.h b/include/hw/hyperv/vmbus-bri= dge.h index 9cc8f780de..c0a06d832c 100644 --- a/include/hw/hyperv/vmbus-bridge.h +++ b/include/hw/hyperv/vmbus-bridge.h @@ -19,6 +19,9 @@ typedef struct VMBus VMBus; typedef struct VMBusBridge { SysBusDevice parent_obj; =20 + uint8_t irq0; + uint8_t irq1; + VMBus *bus; } VMBusBridge; =20 --=20 2.24.1 From nobody Sat May 18 19:24:34 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=1586238880; cv=none; d=zohomail.com; s=zohoarc; b=h2zJnXS0oonXrwk/n4mzspjYZniyqoXOpZLbDf+Czr9GlYDc45KDOvKNUNyEsGe+is0nKR5h2OqsnlX6pw21Kpr8lKU1qExS1QRedm+2V73dPMqRHeMt3QTd6JFmL3gSfQVEmL6FA7TNDboaNPtA3DMq4/bRHrM9LbiMOyW0r24= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1586238880; 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=WU+H5+odnL+lFKgHafO2P278mQjtE3V6d/6koMgqUwg=; b=TL0C+0yD3rNusE11bUDsIQ7hEMXRt4ONccWYOuBEfAulJoaqyuVx/PS9FcDwxCBSMnsCDkzlVz69wkDpjeSX4i69yQ8sg2DpoondmwiR0V7I6Z8402Mde58RQAasq88i0CvQ936VT/74HoxhdRKZny2k3ysF3AmQ3NTcFtXGDbQ= 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 1586238880711477.8405936472785; Mon, 6 Apr 2020 22:54:40 -0700 (PDT) Received: from localhost ([::1]:41662 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jLhC3-000098-Et for importer@patchew.org; Tue, 07 Apr 2020 01:54:39 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:60159) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jLh9t-0004jr-Jh for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:26 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1jLh9s-0002cD-Fr for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:25 -0400 Received: from mail-wm1-x342.google.com ([2a00:1450:4864:20::342]:54358) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1jLh9s-0002bo-9l for qemu-devel@nongnu.org; Tue, 07 Apr 2020 01:52:24 -0400 Received: by mail-wm1-x342.google.com with SMTP id h2so470749wmb.4 for ; Mon, 06 Apr 2020 22:52:24 -0700 (PDT) Received: from jondnuc.lan (IGLD-84-229-155-55.inter.net.il. [84.229.155.55]) by smtp.gmail.com with ESMTPSA id n11sm32271007wrg.72.2020.04.06.22.52.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 06 Apr 2020 22:52:22 -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=WU+H5+odnL+lFKgHafO2P278mQjtE3V6d/6koMgqUwg=; b=kvuvtpMZxquKhKaB3Yi0fhE1guv6O64cTGRGRC1M5XjwJ8SNWO63AQXDodUzimkxEH XRKq1/W2hGgYohXRTgOFBS/AN9S2CTOXR/tsuJJ56Y0zclNeyEMfT/vmG3+va0gxwpHV j/RbTdCojP4oa8jDPobbK9x5hUf/kwmxFWhiwF8kfJa74gQTModm6o0/XebiJ7a4CRJ0 5vLf9o0xS6HXgB1xdaAtZtk1BKFrCvWH2rw1swFdMSkTzavOqaKWMuDFt66H/YAOJP2y Kx8t8bKPfpY1fwHNTPHG45mccS2D8vDrnomGQp2eby2PUGEfAaszwWw+dPKa4+bnhYNg G+VA== 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=WU+H5+odnL+lFKgHafO2P278mQjtE3V6d/6koMgqUwg=; b=UWQF4kFznYB/mrVC9I5ik9t4lAu1Lb6zjyDMhJEfAfLyi0HCyaU2AxSLV6MrmHPcCq IUl//cCEyuwef0x8bIU4SuULB4vy+iPeHQK2LkfhcJZfedkh/pcwTHaqfeAmBImlbnO/ ivYHkwrQs0ZMS9bUjnuAWO58uCXfbjzfhUML9kSMx38pCr0HmZWsPPor2/NJnPzC9juS Dsx6kTyUD95Pu46E9vRJ6pCJLzD8iBHbp4YNzubhoLhWCBls0suaFkeh1tg07nqzA1lD vNWMq3wjxa3Rp47CcMRParVj7fVqPyvw7/PvtULdD5eXMsRylqs1Wwaah9dc5mA/gbhF d9Iw== X-Gm-Message-State: AGi0PubSNhnYEREmJmaQE4GQWWlWgfXNM3E3jYb92ZBAzB0uKIxZgVcu IssA3tKi+25lzoQsVVpjQ6O14Ikj2aY= X-Google-Smtp-Source: APiQypJ1XaSLTGzuq2Mqjcf2TvJzt+aOBXFggMYaEIqVJPaK7acvxFOCMXlZIxDYxOOLjiZ2Smd5rA== X-Received: by 2002:a1c:7301:: with SMTP id d1mr583148wmb.26.1586238743104; Mon, 06 Apr 2020 22:52:23 -0700 (PDT) From: Jon Doron To: qemu-devel@nongnu.org Subject: [PATCH v3 7/7] vmbus: add infrastructure to save/load vmbus requests Date: Tue, 7 Apr 2020 08:51:56 +0300 Message-Id: <20200407055156.137249-8-arilou@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200407055156.137249-1-arilou@gmail.com> References: <20200407055156.137249-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::342 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: mail@maciej.szmigiero.name, eyakovlev@virtuozzo.com, ehabkost@redhat.com, rvkagan@gmail.com, liran.alon@oracle.com, Roman Kagan , pbonzini@redhat.com, vkuznets@redhat.com, "Maciej S . Szmigiero" , 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" This can be allow to include controller-specific data while saving/loading in-flight scsi requests of the vmbus scsi controller. Signed-off-by: Roman Kagan Signed-off-by: Maciej S. Szmigiero Signed-off-by: Jon Doron --- hw/hyperv/vmbus.c | 99 +++++++++++++++++++++++++++++++++++++++ include/hw/hyperv/vmbus.h | 3 ++ 2 files changed, 102 insertions(+) diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index 0df7afe0ca..ab72a59a4a 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -1272,6 +1272,105 @@ void vmbus_free_req(void *req) g_free(req); } =20 +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 need_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(need_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.need_comp =3D req->need_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.need_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); diff --git a/include/hw/hyperv/vmbus.h b/include/hw/hyperv/vmbus.h index 63a5b807b6..9219f34d6b 100644 --- a/include/hw/hyperv/vmbus.h +++ b/include/hw/hyperv/vmbus.h @@ -224,4 +224,7 @@ int vmbus_map_sgl(VMBusChanReq *req, DMADirection dir, = struct iovec *iov, void vmbus_unmap_sgl(VMBusChanReq *req, DMADirection dir, struct iovec *io= v, unsigned iov_cnt, size_t accessed); =20 +void vmbus_save_req(QEMUFile *f, VMBusChanReq *req); +void *vmbus_load_req(QEMUFile *f, VMBusDevice *dev, uint32_t size); + #endif --=20 2.24.1