From patchwork Mon Jul 8 17:34:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Henrique Barboza X-Patchwork-Id: 1958082 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=ventanamicro.com header.i=@ventanamicro.com header.a=rsa-sha256 header.s=google header.b=YV9a3jla; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=patchwork.ozlabs.org) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WHrrs5T01z1xrP for ; Tue, 9 Jul 2024 03:36:29 +1000 (AEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sQsHH-0006XI-FH; Mon, 08 Jul 2024 13:35:51 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sQsHB-00068J-Ti for qemu-devel@nongnu.org; Mon, 08 Jul 2024 13:35:46 -0400 Received: from mail-pf1-x430.google.com ([2607:f8b0:4864:20::430]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1sQsH8-00088Q-EE for qemu-devel@nongnu.org; Mon, 08 Jul 2024 13:35:45 -0400 Received: by mail-pf1-x430.google.com with SMTP id d2e1a72fcca58-70af4868d3dso2543791b3a.3 for ; Mon, 08 Jul 2024 10:35:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ventanamicro.com; s=google; t=1720460140; x=1721064940; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=8JdSdXe/AXg70AS+TaZpeXnXbDvPfuGdYlWEQJX3Gvo=; b=YV9a3jlamq8miIGm9qOlJY1ZU0xUZ8plztY0eRrCDm3Q0aynO9RhH07ErCTt01oKDC n4sa9IIC9VAQcNnRt4Pwy7dO00rs3vE2XHp+QFmXgeJlK5nlbKKI7W+ZsLUuW3KI6dJc 4MKlhUJiG5vbyc2oyqsLQsxPw050xi8CEficYv53YcM/TgBDX8uxswpsiU0OoEe0n+5n DELTwc/7yLUoObUeeMUIqLRJ9G21WVG837RH2YeciOCMs+a+rr12K4YnsaGBSTQu4Ta5 s7TTNL0QDQwfQ/UmalODEFt37UohFXWblDy45814t4SgJeHcMImibanqHc8+/g9sjtHi lflQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1720460140; x=1721064940; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=8JdSdXe/AXg70AS+TaZpeXnXbDvPfuGdYlWEQJX3Gvo=; b=LsfIbdKEd2safOgWwCdFSl5mZxVP7sdaM2MT7BYM3jwIXZ5WHo4IKvdLfA7naY8v4C CQQpJwpJnBW/4b5hbSaYUu6zO1zw9EZ1WQDxhNQa46zk5b6wauflSaPNGuLecnJYlSkO cvFikTas9NQgMJ61ctl5aao6h+uxuBwfPy95znt8xdc7qrJuz3W6xEOBEqmSvVvR3DiE YXS8L7OcziPvRhuFe3UjxmZYDXsjUJpy7hkBqTkIUotBVRYcf3YB026DZGivbKNjzZ04 AsKsQ/e4vRAkMY+rShoOpNsCqDcbqC4lM4TBRvLyFVrb8IH2OPeHZ8jqG5qku/sM8glp D04A== X-Gm-Message-State: AOJu0YyzjRV0Ib8HzlK79SvQhFfwoPLd9v+WgS4yZ0uvstEDW1QOY4X7 btBttW+D/eKdeDLKStqvzxuwopKERY1pILrYb6SfvEmZ+AI7aXtzRHE2TXZ41rFUSwrZ1Yp4qcM y X-Google-Smtp-Source: AGHT+IGegY9k6zG2wWQ406vbGYG0pWNKl5QkJv6tF+5H9SmzRmVGWvq4+PzbMUPgDrziOoOpPfE7Lw== X-Received: by 2002:a05:6a21:32a1:b0:1c0:f5ea:d447 with SMTP id adf61e73a8af0-1c29824e3bemr109082637.31.1720460137581; Mon, 08 Jul 2024 10:35:37 -0700 (PDT) Received: from grind.dc1.ventanamicro.com ([179.193.8.43]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-1fbb6ad3829sm1153765ad.299.2024.07.08.10.35.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Jul 2024 10:35:37 -0700 (PDT) From: Daniel Henrique Barboza To: qemu-devel@nongnu.org Cc: qemu-riscv@nongnu.org, alistair.francis@wdc.com, bmeng@tinylab.org, liwei1518@gmail.com, zhiwei_liu@linux.alibaba.com, palmer@rivosinc.com, frank.chang@sifive.com, tjeznach@rivosinc.com, jason.chien@sifive.com, Daniel Henrique Barboza Subject: [PATCH v5 08/13] hw/riscv/riscv-iommu: add Address Translation Cache (IOATC) Date: Mon, 8 Jul 2024 14:34:55 -0300 Message-ID: <20240708173501.426225-9-dbarboza@ventanamicro.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240708173501.426225-1-dbarboza@ventanamicro.com> References: <20240708173501.426225-1-dbarboza@ventanamicro.com> MIME-Version: 1.0 Received-SPF: pass client-ip=2607:f8b0:4864:20::430; envelope-from=dbarboza@ventanamicro.com; helo=mail-pf1-x430.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Tomasz Jeznach The RISC-V IOMMU spec predicts that the IOMMU can use translation caches to hold entries from the DDT. This includes implementation for all cache commands that are marked as 'not implemented'. There are some artifacts included in the cache that predicts s-stage and g-stage elements, although we don't support it yet. We'll introduce them next. Signed-off-by: Tomasz Jeznach Signed-off-by: Daniel Henrique Barboza Reviewed-by: Frank Chang Acked-by: Alistair Francis --- hw/riscv/riscv-iommu.c | 199 ++++++++++++++++++++++++++++++++++++++++- hw/riscv/riscv-iommu.h | 3 + 2 files changed, 198 insertions(+), 4 deletions(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 5faa76a904..76728baa77 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -65,6 +65,16 @@ struct RISCVIOMMUContext { uint64_t msiptp; /* MSI redirection page table pointer */ }; +/* Address translation cache entry */ +struct RISCVIOMMUEntry { + uint64_t iova:44; /* IOVA Page Number */ + uint64_t pscid:20; /* Process Soft-Context identifier */ + uint64_t phys:44; /* Physical Page Number */ + uint64_t gscid:16; /* Guest Soft-Context identifier */ + uint64_t perm:2; /* IOMMU_RW flags */ + uint64_t __rfu:2; +}; + /* IOMMU index for transactions without process_id specified. */ #define RISCV_IOMMU_NOPROCID 0 @@ -1130,13 +1140,130 @@ static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid) return &as->iova_as; } +/* Translation Object cache support */ +static gboolean __iot_equal(gconstpointer v1, gconstpointer v2) +{ + RISCVIOMMUEntry *t1 = (RISCVIOMMUEntry *) v1; + RISCVIOMMUEntry *t2 = (RISCVIOMMUEntry *) v2; + return t1->gscid == t2->gscid && t1->pscid == t2->pscid && + t1->iova == t2->iova; +} + +static guint __iot_hash(gconstpointer v) +{ + RISCVIOMMUEntry *t = (RISCVIOMMUEntry *) v; + return (guint)t->iova; +} + +/* GV: 1 PSCV: 1 AV: 1 */ +static void __iot_inval_pscid_iova(gpointer key, gpointer value, gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->gscid == arg->gscid && + iot->pscid == arg->pscid && + iot->iova == arg->iova) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 1 PSCV: 1 AV: 0 */ +static void __iot_inval_pscid(gpointer key, gpointer value, gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->gscid == arg->gscid && + iot->pscid == arg->pscid) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 1 GVMA: 1 */ +static void __iot_inval_gscid_gpa(gpointer key, gpointer value, gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->gscid == arg->gscid) { + /* simplified cache, no GPA matching */ + iot->perm = IOMMU_NONE; + } +} + +/* GV: 1 GVMA: 0 */ +static void __iot_inval_gscid(gpointer key, gpointer value, gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->gscid == arg->gscid) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 0 */ +static void __iot_inval_all(gpointer key, gpointer value, gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + iot->perm = IOMMU_NONE; +} + +/* caller should keep ref-count for iot_cache object */ +static RISCVIOMMUEntry *riscv_iommu_iot_lookup(RISCVIOMMUContext *ctx, + GHashTable *iot_cache, hwaddr iova) +{ + RISCVIOMMUEntry key = { + .gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID), + .pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID), + .iova = PPN_DOWN(iova), + }; + return g_hash_table_lookup(iot_cache, &key); +} + +/* caller should keep ref-count for iot_cache object */ +static void riscv_iommu_iot_update(RISCVIOMMUState *s, + GHashTable *iot_cache, RISCVIOMMUEntry *iot) +{ + if (!s->iot_limit) { + return; + } + + qemu_mutex_lock(&s->iot_lock); + if (g_hash_table_size(s->iot_cache) >= s->iot_limit) { + iot_cache = g_hash_table_new_full(__iot_hash, __iot_equal, + g_free, NULL); + g_hash_table_unref(qatomic_xchg(&s->iot_cache, iot_cache)); + } + g_hash_table_add(iot_cache, iot); + qemu_mutex_unlock(&s->iot_lock); +} + +static void riscv_iommu_iot_inval(RISCVIOMMUState *s, GHFunc func, + uint32_t gscid, uint32_t pscid, hwaddr iova) +{ + GHashTable *iot_cache; + RISCVIOMMUEntry key = { + .gscid = gscid, + .pscid = pscid, + .iova = PPN_DOWN(iova), + }; + + iot_cache = g_hash_table_ref(s->iot_cache); + qemu_mutex_lock(&s->iot_lock); + g_hash_table_foreach(iot_cache, func, &key); + qemu_mutex_unlock(&s->iot_lock); + g_hash_table_unref(iot_cache); +} + static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, - IOMMUTLBEntry *iotlb) + IOMMUTLBEntry *iotlb, bool enable_cache) { + RISCVIOMMUEntry *iot; + IOMMUAccessFlags perm; bool enable_pid; bool enable_pri; + GHashTable *iot_cache; int fault; + iot_cache = g_hash_table_ref(s->iot_cache); /* * TC[32] is reserved for custom extensions, used here to temporarily * enable automatic page-request generation for ATS queries. @@ -1144,9 +1271,39 @@ static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, enable_pri = (iotlb->perm == IOMMU_NONE) && (ctx->tc & BIT_ULL(32)); enable_pid = (ctx->tc & RISCV_IOMMU_DC_TC_PDTV); + qemu_mutex_lock(&s->iot_lock); + iot = riscv_iommu_iot_lookup(ctx, iot_cache, iotlb->iova); + qemu_mutex_unlock(&s->iot_lock); + perm = iot ? iot->perm : IOMMU_NONE; + if (perm != IOMMU_NONE) { + iotlb->translated_addr = PPN_PHYS(iot->phys); + iotlb->addr_mask = ~TARGET_PAGE_MASK; + iotlb->perm = perm; + fault = 0; + goto done; + } + /* Translate using device directory / page table information. */ fault = riscv_iommu_spa_fetch(s, ctx, iotlb); + if (!fault && iotlb->target_as == &s->trap_as) { + /* Do not cache trapped MSI translations */ + goto done; + } + + if (!fault && iotlb->translated_addr != iotlb->iova && enable_cache) { + iot = g_new0(RISCVIOMMUEntry, 1); + iot->iova = PPN_DOWN(iotlb->iova); + iot->phys = PPN_DOWN(iotlb->translated_addr); + iot->gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID); + iot->pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID); + iot->perm = iotlb->perm; + riscv_iommu_iot_update(s, iot_cache, iot); + } + +done: + g_hash_table_unref(iot_cache); + if (enable_pri && fault) { struct riscv_iommu_pq_record pr = {0}; if (enable_pid) { @@ -1286,13 +1443,40 @@ static void riscv_iommu_process_cq_tail(RISCVIOMMUState *s) if (cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV) { /* illegal command arguments IOTINVAL.GVMA & PSCV == 1 */ goto cmd_ill; + } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV)) { + /* invalidate all cache mappings */ + func = __iot_inval_all; + } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV)) { + /* invalidate cache matching GSCID */ + func = __iot_inval_gscid; + } else { + /* invalidate cache matching GSCID and ADDR (GPA) */ + func = __iot_inval_gscid_gpa; } - /* translation cache not implemented yet */ + riscv_iommu_iot_inval(s, func, + get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_GSCID), 0, + cmd.dword1 & TARGET_PAGE_MASK); break; case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_VMA, RISCV_IOMMU_CMD_IOTINVAL_OPCODE): - /* translation cache not implemented yet */ + if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV)) { + /* invalidate all cache mappings, simplified model */ + func = __iot_inval_all; + } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV)) { + /* invalidate cache matching GSCID, simplified model */ + func = __iot_inval_gscid; + } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV)) { + /* invalidate cache matching GSCID and PSCID */ + func = __iot_inval_pscid; + } else { + /* invalidate cache matching GSCID and PSCID and ADDR (IOVA) */ + func = __iot_inval_pscid_iova; + } + riscv_iommu_iot_inval(s, func, + get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_GSCID), + get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_PSCID), + cmd.dword1 & TARGET_PAGE_MASK); break; case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_DDT, @@ -1801,6 +1985,10 @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp) g_free, NULL); qemu_mutex_init(&s->ctx_lock); + s->iot_cache = g_hash_table_new_full(__iot_hash, __iot_equal, + g_free, NULL); + qemu_mutex_init(&s->iot_lock); + s->iommus.le_next = NULL; s->iommus.le_prev = NULL; QLIST_INIT(&s->spaces); @@ -1813,6 +2001,7 @@ static void riscv_iommu_unrealize(DeviceState *dev) RISCVIOMMUState *s = RISCV_IOMMU(dev); qemu_mutex_destroy(&s->core_lock); + g_hash_table_unref(s->iot_cache); g_hash_table_unref(s->ctx_cache); } @@ -1820,6 +2009,8 @@ static Property riscv_iommu_properties[] = { DEFINE_PROP_UINT32("version", RISCVIOMMUState, version, RISCV_IOMMU_SPEC_DOT_VER), DEFINE_PROP_UINT32("bus", RISCVIOMMUState, bus, 0x0), + DEFINE_PROP_UINT32("ioatc-limit", RISCVIOMMUState, iot_limit, + LIMIT_CACHE_IOT), DEFINE_PROP_BOOL("intremap", RISCVIOMMUState, enable_msi, TRUE), DEFINE_PROP_BOOL("off", RISCVIOMMUState, enable_off, TRUE), DEFINE_PROP_BOOL("s-stage", RISCVIOMMUState, enable_s_stage, TRUE), @@ -1874,7 +2065,7 @@ static IOMMUTLBEntry riscv_iommu_memory_region_translate( /* Translation disabled or invalid. */ iotlb.addr_mask = 0; iotlb.perm = IOMMU_NONE; - } else if (riscv_iommu_translate(as->iommu, ctx, &iotlb)) { + } else if (riscv_iommu_translate(as->iommu, ctx, &iotlb, true)) { /* Translation disabled or fault reported. */ iotlb.addr_mask = 0; iotlb.perm = IOMMU_NONE; diff --git a/hw/riscv/riscv-iommu.h b/hw/riscv/riscv-iommu.h index a3f858d978..0594e654f9 100644 --- a/hw/riscv/riscv-iommu.h +++ b/hw/riscv/riscv-iommu.h @@ -71,6 +71,9 @@ struct RISCVIOMMUState { GHashTable *ctx_cache; /* Device translation Context Cache */ QemuMutex ctx_lock; /* Device translation Cache update lock */ + GHashTable *iot_cache; /* IO Translated Address Cache */ + QemuMutex iot_lock; /* IO TLB Cache update lock */ + unsigned iot_limit; /* IO Translation Cache size limit */ /* MMIO Hardware Interface */ MemoryRegion regs_mr;