[PATCH v8 2/8] system/physmem: Support IOMMU granularity smaller than TARGET_PAGE size

Ethan Chen via posted 8 patches 2 months, 1 week ago
Maintainers: Richard Henderson <richard.henderson@linaro.org>, Paolo Bonzini <pbonzini@redhat.com>, Palmer Dabbelt <palmer@dabbelt.com>, Alistair Francis <alistair.francis@wdc.com>, Bin Meng <bmeng.cn@gmail.com>, Weiwei Li <liwei1518@gmail.com>, Daniel Henrique Barboza <dbarboza@ventanamicro.com>, Liu Zhiwei <zhiwei_liu@linux.alibaba.com>, Peter Xu <peterx@redhat.com>, David Hildenbrand <david@redhat.com>, "Philippe Mathieu-Daudé" <philmd@linaro.org>
[PATCH v8 2/8] system/physmem: Support IOMMU granularity smaller than TARGET_PAGE size
Posted by Ethan Chen via 2 months, 1 week ago
If the IOMMU granularity is smaller than the TARGET_PAGE size, there may be
 multiple entries within the same page. To obtain the correct result, pass
the original address to the IOMMU.

Similar to the RISC-V PMP solution, the TLB_INVALID_MASK will be set when
there are multiple entries in the same page, ensuring that the IOMMU is
checked on every access.

Signed-off-by: Ethan Chen <ethan84@andestech.com>
---
 accel/tcg/cputlb.c | 20 ++++++++++++++++----
 system/physmem.c   |  4 ++++
 2 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
index edb3715017..7df106fea3 100644
--- a/accel/tcg/cputlb.c
+++ b/accel/tcg/cputlb.c
@@ -1062,8 +1062,23 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx,
 
     prot = full->prot;
     asidx = cpu_asidx_from_attrs(cpu, full->attrs);
-    section = address_space_translate_for_iotlb(cpu, asidx, paddr_page,
+    section = address_space_translate_for_iotlb(cpu, asidx, full->phys_addr,
                                                 &xlat, &sz, full->attrs, &prot);
+    /* Update page size */
+    full->lg_page_size = ctz64(sz);
+    if (full->lg_page_size > TARGET_PAGE_BITS) {
+        full->lg_page_size = TARGET_PAGE_BITS;
+    } else {
+        sz = TARGET_PAGE_SIZE;
+    }
+
+    is_ram = memory_region_is_ram(section->mr);
+    is_romd = memory_region_is_romd(section->mr);
+    /* If the translated mr is ram/rom, make xlat align the TARGET_PAGE */
+    if (is_ram || is_romd) {
+        xlat &= TARGET_PAGE_MASK;
+    }
+
     assert(sz >= TARGET_PAGE_SIZE);
 
     tlb_debug("vaddr=%016" VADDR_PRIx " paddr=0x" HWADDR_FMT_plx
@@ -1076,9 +1091,6 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx,
         read_flags |= TLB_INVALID_MASK;
     }
 
-    is_ram = memory_region_is_ram(section->mr);
-    is_romd = memory_region_is_romd(section->mr);
-
     if (is_ram || is_romd) {
         /* RAM and ROMD both have associated host memory. */
         addend = (uintptr_t)memory_region_get_ram_ptr(section->mr) + xlat;
diff --git a/system/physmem.c b/system/physmem.c
index 2154432cb6..346b015447 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -702,6 +702,10 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr orig_addr,
         iotlb = imrc->translate(iommu_mr, addr, IOMMU_NONE, iommu_idx);
         addr = ((iotlb.translated_addr & ~iotlb.addr_mask)
                 | (addr & iotlb.addr_mask));
+        /* Update size */
+        if (iotlb.addr_mask != -1 && *plen > iotlb.addr_mask + 1) {
+            *plen = iotlb.addr_mask + 1;
+        }
         /* Update the caller's prot bits to remove permissions the IOMMU
          * is giving us a failure response for. If we get down to no
          * permissions left at all we can give up now.
-- 
2.34.1
Re: [PATCH v8 2/8] system/physmem: Support IOMMU granularity smaller than TARGET_PAGE size
Posted by Alistair Francis 1 month, 2 weeks ago
On Mon, Jul 15, 2024 at 7:59 PM Ethan Chen via <qemu-devel@nongnu.org> wrote:
>
> If the IOMMU granularity is smaller than the TARGET_PAGE size, there may be
>  multiple entries within the same page. To obtain the correct result, pass
> the original address to the IOMMU.
>
> Similar to the RISC-V PMP solution, the TLB_INVALID_MASK will be set when
> there are multiple entries in the same page, ensuring that the IOMMU is
> checked on every access.
>
> Signed-off-by: Ethan Chen <ethan84@andestech.com>

Acked-by: Alistair Francis <alistair.francis@wdc.com>

Alistair

> ---
>  accel/tcg/cputlb.c | 20 ++++++++++++++++----
>  system/physmem.c   |  4 ++++
>  2 files changed, 20 insertions(+), 4 deletions(-)
>
> diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
> index edb3715017..7df106fea3 100644
> --- a/accel/tcg/cputlb.c
> +++ b/accel/tcg/cputlb.c
> @@ -1062,8 +1062,23 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx,
>
>      prot = full->prot;
>      asidx = cpu_asidx_from_attrs(cpu, full->attrs);
> -    section = address_space_translate_for_iotlb(cpu, asidx, paddr_page,
> +    section = address_space_translate_for_iotlb(cpu, asidx, full->phys_addr,
>                                                  &xlat, &sz, full->attrs, &prot);
> +    /* Update page size */
> +    full->lg_page_size = ctz64(sz);
> +    if (full->lg_page_size > TARGET_PAGE_BITS) {
> +        full->lg_page_size = TARGET_PAGE_BITS;
> +    } else {
> +        sz = TARGET_PAGE_SIZE;
> +    }
> +
> +    is_ram = memory_region_is_ram(section->mr);
> +    is_romd = memory_region_is_romd(section->mr);
> +    /* If the translated mr is ram/rom, make xlat align the TARGET_PAGE */
> +    if (is_ram || is_romd) {
> +        xlat &= TARGET_PAGE_MASK;
> +    }
> +
>      assert(sz >= TARGET_PAGE_SIZE);
>
>      tlb_debug("vaddr=%016" VADDR_PRIx " paddr=0x" HWADDR_FMT_plx
> @@ -1076,9 +1091,6 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx,
>          read_flags |= TLB_INVALID_MASK;
>      }
>
> -    is_ram = memory_region_is_ram(section->mr);
> -    is_romd = memory_region_is_romd(section->mr);
> -
>      if (is_ram || is_romd) {
>          /* RAM and ROMD both have associated host memory. */
>          addend = (uintptr_t)memory_region_get_ram_ptr(section->mr) + xlat;
> diff --git a/system/physmem.c b/system/physmem.c
> index 2154432cb6..346b015447 100644
> --- a/system/physmem.c
> +++ b/system/physmem.c
> @@ -702,6 +702,10 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr orig_addr,
>          iotlb = imrc->translate(iommu_mr, addr, IOMMU_NONE, iommu_idx);
>          addr = ((iotlb.translated_addr & ~iotlb.addr_mask)
>                  | (addr & iotlb.addr_mask));
> +        /* Update size */
> +        if (iotlb.addr_mask != -1 && *plen > iotlb.addr_mask + 1) {
> +            *plen = iotlb.addr_mask + 1;
> +        }
>          /* Update the caller's prot bits to remove permissions the IOMMU
>           * is giving us a failure response for. If we get down to no
>           * permissions left at all we can give up now.
> --
> 2.34.1
>
>