From nobody Thu Oct 2 10:39:57 2025 Received: from mail-wr1-f74.google.com (mail-wr1-f74.google.com [209.85.221.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 78BFD299937 for ; Wed, 17 Sep 2025 14:02:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758117752; cv=none; b=gqg4LXDpBowSgZoGw11EAQ1qS8kvW1ZPa0ywj9NiFy4Eg7CEQysZr9oEXFZFtGMUE4McrKbmSJgJ5pBk+2nu9bJrfyHEcWRhilHGGE8xT84CBqopzJi+3mGpcTYMg/1prCcVr64vuVFJxUNM5ReZinFMnH2c6H0OFQQzXw4Zxns= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758117752; c=relaxed/simple; bh=ATUgOY5cIoYJVQrhEZ4LhDX2L/+l3r8cGRPjFTceEV0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Y4GPx5HtFC/SlDTzJjkgjSecdfuN6y1Yf/z3gZBwn53WyOaDsNS5giCyIzhuW9+x5n+vB7Mxw8r9b+60QK0uEPDsE7cSNM32hm3zMDC2Z6AViPkmTSmMVp1UvtgbBKJ0bbFVh3ysOYeCV05FJLuhDKRqlljtJIGlBm1yREnbApg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--smostafa.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=bu31P3CH; arc=none smtp.client-ip=209.85.221.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--smostafa.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="bu31P3CH" Received: by mail-wr1-f74.google.com with SMTP id ffacd0b85a97d-3ea35489002so2647883f8f.0 for ; Wed, 17 Sep 2025 07:02:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1758117749; x=1758722549; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=dPTm9ldH05FB4Yw+nXjL4CLcI1O0tCdKN6q7o4MyyHk=; b=bu31P3CHppo63umOI3hTjUBYftWU7CluS2wme6CAvq3iDnUVvMC/vEvOqlQknrc3Kx mZ1Xo2vRaC7DwRiXH5ThQquzQMB4avMTy0s1ziCVYaWUX//yNvfIx5+FG5wIk7MQlfby wc/aZrFO1We8XIKJhVRr7B/FHREAea9ZjuD3bWUu7bsxSvDpV5t2a3vOkQHmML/99n4B +xtqOR7m0/Oyh7dHW/0HwkavHm7kGOg7qeySTU6ClI6Wg340zt0aYXXHkiV9jTZHNabK hf54czcT9mEsDNtDGfYS/RZgtGv2/Ownq/YIwIGs7T1Js2FrtM3/u3BtQk9c9tMpaTHy BL5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758117749; x=1758722549; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=dPTm9ldH05FB4Yw+nXjL4CLcI1O0tCdKN6q7o4MyyHk=; b=xLp8VVsYBhUFcrlgqxXU4WJBx9h4af0RoQj927hpnzhaIe2SJv9L78qybgmxAj/vZO 2Bx/nqIVhKk7hlzUeq4uBh7ojlG7MFLTczZIqVK6x+MqtbiEJ6Ab/2kOD2XzirLLCnL+ 46qetyCa3dOJRIijbte3MIqVRcO7JBWZgrMANYy1Ig1h7b+WHSD1jiLF+yjYNRt6PXK1 iJ4M+gAoVdJqLFQ8oyRMp8zXIkHtbKlVmZUX6OsgG7OCcJW8aRcQh6oCiN2R3WMrwB4I +9YAY5J7TP/3lI8N/y0SPjmqgQhQ0KH/aAc/Fk56Y6R+8oy76I2LZq/pXW7XB+vpyrAH UCOQ== X-Forwarded-Encrypted: i=1; AJvYcCUQfqgRHhKup+syIpn4yb+/u875mWEU6kf4+idHu+0wncF53Ly/Rb/LRRLBH8/fkBOV87BibkjzWXr2V7I=@vger.kernel.org X-Gm-Message-State: AOJu0YyocoBqsi9d0CQFUDx0Dpx/5XKP9ssRBxLoPaRITXzoo2GUpyTu myANWzDuHJVDqf72cXRQkB34TK6h5DbVGpMLTm9WQB0+ahTQZtEyL8y3wfGUhpE5b/rro8Bix0E a0ReUIOx1/xJ2/g== X-Google-Smtp-Source: AGHT+IEaQio/Zs7puieg6tM047i35YMeLXuQUx6Kkae+qk+M5jpGAX1GbglUvmZjLAougtbpym7PZT/84AZH5g== X-Received: from wrfu4.prod.google.com ([2002:a05:6000:384:b0:3ea:af7:bb94]) (user=smostafa job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6000:420a:b0:3ec:db18:1695 with SMTP id ffacd0b85a97d-3ecdfa2af44mr2483150f8f.45.1758117748915; Wed, 17 Sep 2025 07:02:28 -0700 (PDT) Date: Wed, 17 Sep 2025 14:02:02 +0000 In-Reply-To: <20250917140216.2199055-1-smostafa@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250917140216.2199055-1-smostafa@google.com> X-Mailer: git-send-email 2.51.0.384.g4c02a37b29-goog Message-ID: <20250917140216.2199055-2-smostafa@google.com> Subject: [PATCH 1/2] iommu/io-pgtable-arm: Move selftests to a separate file From: Mostafa Saleh To: iommu@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Cc: robin.murphy@arm.com, will@kernel.org, joro@8bytes.org, jgg@ziepe.ca, praan@google.com, Mostafa Saleh Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Clean up the io-pgtable-arm library by moving the selftests out. Next the tests will be registered with kunit. This is useful also to factor out kernel specific code out, so it can compiled as part of the hypervisor object. Signed-off-by: Mostafa Saleh --- drivers/iommu/Makefile | 1 + drivers/iommu/io-pgtable-arm-selftests.c | 211 ++++++++++++++++++++ drivers/iommu/io-pgtable-arm.c | 243 ----------------------- drivers/iommu/io-pgtable-arm.h | 41 ++++ 4 files changed, 253 insertions(+), 243 deletions(-) create mode 100644 drivers/iommu/io-pgtable-arm-selftests.c diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 355294fa9033..5250a2eea13f 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_IOMMU_DMA) +=3D dma-iommu.o obj-$(CONFIG_IOMMU_IO_PGTABLE) +=3D io-pgtable.o obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) +=3D io-pgtable-arm-v7s.o obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) +=3D io-pgtable-arm.o +obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST) +=3D io-pgtable-arm-selftests= .o obj-$(CONFIG_IOMMU_IO_PGTABLE_DART) +=3D io-pgtable-dart.o obj-$(CONFIG_IOMMU_IOVA) +=3D iova.o obj-$(CONFIG_OF_IOMMU) +=3D of_iommu.o diff --git a/drivers/iommu/io-pgtable-arm-selftests.c b/drivers/iommu/io-pg= table-arm-selftests.c new file mode 100644 index 000000000000..f7746ff2c7a0 --- /dev/null +++ b/drivers/iommu/io-pgtable-arm-selftests.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * CPU-agnostic ARM page table allocator. + * + * Copyright (C) 2014 ARM Limited + * + * Author: Will Deacon + */ +#include +#include +#include + +#include "io-pgtable-arm.h" + +static struct io_pgtable_cfg *cfg_cookie __initdata; + +static void __init dummy_tlb_flush_all(void *cookie) +{ + WARN_ON(cookie !=3D cfg_cookie); +} + +static void __init dummy_tlb_flush(unsigned long iova, size_t size, + size_t granule, void *cookie) +{ + WARN_ON(cookie !=3D cfg_cookie); + WARN_ON(!(size & cfg_cookie->pgsize_bitmap)); +} + +static void __init dummy_tlb_add_page(struct iommu_iotlb_gather *gather, + unsigned long iova, size_t granule, + void *cookie) +{ + dummy_tlb_flush(iova, granule, granule, cookie); +} + +static const struct iommu_flush_ops dummy_tlb_ops __initconst =3D { + .tlb_flush_all =3D dummy_tlb_flush_all, + .tlb_flush_walk =3D dummy_tlb_flush, + .tlb_add_page =3D dummy_tlb_add_page, +}; + +static void __init arm_lpae_dump_ops(struct io_pgtable_ops *ops) +{ + struct arm_lpae_io_pgtable *data =3D io_pgtable_ops_to_data(ops); + struct io_pgtable_cfg *cfg =3D &data->iop.cfg; + + pr_err("cfg: pgsize_bitmap 0x%lx, ias %u-bit\n", + cfg->pgsize_bitmap, cfg->ias); + pr_err("data: %d levels, 0x%zx pgd_size, %u pg_shift, %u bits_per_level, = pgd @ %p\n", + ARM_LPAE_MAX_LEVELS - data->start_level, ARM_LPAE_PGD_SIZE(data), + ilog2(ARM_LPAE_GRANULE(data)), data->bits_per_level, data->pgd); +} + +#define __FAIL(ops, i) ({ \ + WARN(1, "selftest: test failed for fmt idx %d\n", (i)); \ + arm_lpae_dump_ops(ops); \ + -EFAULT; \ +}) + +static int __init arm_lpae_run_tests(struct io_pgtable_cfg *cfg) +{ + static const enum io_pgtable_fmt fmts[] __initconst =3D { + ARM_64_LPAE_S1, + ARM_64_LPAE_S2, + }; + + int i, j; + unsigned long iova; + size_t size, mapped; + struct io_pgtable_ops *ops; + + for (i =3D 0; i < ARRAY_SIZE(fmts); ++i) { + cfg_cookie =3D cfg; + ops =3D alloc_io_pgtable_ops(fmts[i], cfg, cfg); + if (!ops) { + pr_err("selftest: failed to allocate io pgtable ops\n"); + return -ENOMEM; + } + + /* + * Initial sanity checks. + * Empty page tables shouldn't provide any translations. + */ + if (ops->iova_to_phys(ops, 42)) + return __FAIL(ops, i); + + if (ops->iova_to_phys(ops, SZ_1G + 42)) + return __FAIL(ops, i); + + if (ops->iova_to_phys(ops, SZ_2G + 42)) + return __FAIL(ops, i); + + /* + * Distinct mappings of different granule sizes. + */ + iova =3D 0; + for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) { + size =3D 1UL << j; + + if (ops->map_pages(ops, iova, iova, size, 1, + IOMMU_READ | IOMMU_WRITE | + IOMMU_NOEXEC | IOMMU_CACHE, + GFP_KERNEL, &mapped)) + return __FAIL(ops, i); + + /* Overlapping mappings */ + if (!ops->map_pages(ops, iova, iova + size, size, 1, + IOMMU_READ | IOMMU_NOEXEC, + GFP_KERNEL, &mapped)) + return __FAIL(ops, i); + + if (ops->iova_to_phys(ops, iova + 42) !=3D (iova + 42)) + return __FAIL(ops, i); + + iova +=3D SZ_1G; + } + + /* Full unmap */ + iova =3D 0; + for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) { + size =3D 1UL << j; + + if (ops->unmap_pages(ops, iova, size, 1, NULL) !=3D size) + return __FAIL(ops, i); + + if (ops->iova_to_phys(ops, iova + 42)) + return __FAIL(ops, i); + + /* Remap full block */ + if (ops->map_pages(ops, iova, iova, size, 1, + IOMMU_WRITE, GFP_KERNEL, &mapped)) + return __FAIL(ops, i); + + if (ops->iova_to_phys(ops, iova + 42) !=3D (iova + 42)) + return __FAIL(ops, i); + + iova +=3D SZ_1G; + } + + /* + * Map/unmap the last largest supported page of the IAS, this can + * trigger corner cases in the concatednated page tables. + */ + mapped =3D 0; + size =3D 1UL << __fls(cfg->pgsize_bitmap); + iova =3D (1UL << cfg->ias) - size; + if (ops->map_pages(ops, iova, iova, size, 1, + IOMMU_READ | IOMMU_WRITE | + IOMMU_NOEXEC | IOMMU_CACHE, + GFP_KERNEL, &mapped)) + return __FAIL(ops, i); + if (mapped !=3D size) + return __FAIL(ops, i); + if (ops->unmap_pages(ops, iova, size, 1, NULL) !=3D size) + return __FAIL(ops, i); + + free_io_pgtable_ops(ops); + } + + return 0; +} + +static int __init arm_lpae_do_selftests(void) +{ + static const unsigned long pgsize[] __initconst =3D { + SZ_4K | SZ_2M | SZ_1G, + SZ_16K | SZ_32M, + SZ_64K | SZ_512M, + }; + + static const unsigned int address_size[] __initconst =3D { + 32, 36, 40, 42, 44, 48, + }; + + int i, j, k, pass =3D 0, fail =3D 0; + struct faux_device *dev; + struct io_pgtable_cfg cfg =3D { + .tlb =3D &dummy_tlb_ops, + .coherent_walk =3D true, + .quirks =3D IO_PGTABLE_QUIRK_NO_WARN, + }; + + dev =3D faux_device_create("io-pgtable-test", NULL, 0); + if (!dev) + return -ENOMEM; + + cfg.iommu_dev =3D &dev->dev; + + for (i =3D 0; i < ARRAY_SIZE(pgsize); ++i) { + for (j =3D 0; j < ARRAY_SIZE(address_size); ++j) { + /* Don't use ias > oas as it is not valid for stage-2. */ + for (k =3D 0; k <=3D j; ++k) { + cfg.pgsize_bitmap =3D pgsize[i]; + cfg.ias =3D address_size[k]; + cfg.oas =3D address_size[j]; + pr_info("selftest: pgsize_bitmap 0x%08lx, IAS %u OAS %u\n", + pgsize[i], cfg.ias, cfg.oas); + if (arm_lpae_run_tests(&cfg)) + fail++; + else + pass++; + } + } + } + + pr_info("selftest: completed with %d PASS %d FAIL\n", pass, fail); + faux_device_destroy(dev); + + return fail ? -EFAULT : 0; +} +subsys_initcall(arm_lpae_do_selftests); diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index 7e8e2216c294..b1d968edccd6 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -12,10 +12,7 @@ #include #include #include -#include -#include #include -#include #include #include =20 @@ -24,33 +21,6 @@ #include "io-pgtable-arm.h" #include "iommu-pages.h" =20 -#define ARM_LPAE_MAX_ADDR_BITS 52 -#define ARM_LPAE_S2_MAX_CONCAT_PAGES 16 -#define ARM_LPAE_MAX_LEVELS 4 - -/* Struct accessors */ -#define io_pgtable_to_data(x) \ - container_of((x), struct arm_lpae_io_pgtable, iop) - -#define io_pgtable_ops_to_data(x) \ - io_pgtable_to_data(io_pgtable_ops_to_pgtable(x)) - -/* - * Calculate the right shift amount to get to the portion describing level= l - * in a virtual address mapped by the pagetable in d. - */ -#define ARM_LPAE_LVL_SHIFT(l,d) \ - (((ARM_LPAE_MAX_LEVELS - (l)) * (d)->bits_per_level) + \ - ilog2(sizeof(arm_lpae_iopte))) - -#define ARM_LPAE_GRANULE(d) \ - (sizeof(arm_lpae_iopte) << (d)->bits_per_level) -#define ARM_LPAE_PGD_SIZE(d) \ - (sizeof(arm_lpae_iopte) << (d)->pgd_bits) - -#define ARM_LPAE_PTES_PER_TABLE(d) \ - (ARM_LPAE_GRANULE(d) >> ilog2(sizeof(arm_lpae_iopte))) - /* * Calculate the index at level l used to map virtual address a using the * pagetable in d. @@ -156,18 +126,6 @@ #define iopte_set_writeable_clean(ptep) \ set_bit(ARM_LPAE_PTE_AP_RDONLY_BIT, (unsigned long *)(ptep)) =20 -struct arm_lpae_io_pgtable { - struct io_pgtable iop; - - int pgd_bits; - int start_level; - int bits_per_level; - - void *pgd; -}; - -typedef u64 arm_lpae_iopte; - static inline bool iopte_leaf(arm_lpae_iopte pte, int lvl, enum io_pgtable_fmt fmt) { @@ -1267,204 +1225,3 @@ struct io_pgtable_init_fns io_pgtable_arm_mali_lpae= _init_fns =3D { .alloc =3D arm_mali_lpae_alloc_pgtable, .free =3D arm_lpae_free_pgtable, }; - -#ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST - -static struct io_pgtable_cfg *cfg_cookie __initdata; - -static void __init dummy_tlb_flush_all(void *cookie) -{ - WARN_ON(cookie !=3D cfg_cookie); -} - -static void __init dummy_tlb_flush(unsigned long iova, size_t size, - size_t granule, void *cookie) -{ - WARN_ON(cookie !=3D cfg_cookie); - WARN_ON(!(size & cfg_cookie->pgsize_bitmap)); -} - -static void __init dummy_tlb_add_page(struct iommu_iotlb_gather *gather, - unsigned long iova, size_t granule, - void *cookie) -{ - dummy_tlb_flush(iova, granule, granule, cookie); -} - -static const struct iommu_flush_ops dummy_tlb_ops __initconst =3D { - .tlb_flush_all =3D dummy_tlb_flush_all, - .tlb_flush_walk =3D dummy_tlb_flush, - .tlb_add_page =3D dummy_tlb_add_page, -}; - -static void __init arm_lpae_dump_ops(struct io_pgtable_ops *ops) -{ - struct arm_lpae_io_pgtable *data =3D io_pgtable_ops_to_data(ops); - struct io_pgtable_cfg *cfg =3D &data->iop.cfg; - - pr_err("cfg: pgsize_bitmap 0x%lx, ias %u-bit\n", - cfg->pgsize_bitmap, cfg->ias); - pr_err("data: %d levels, 0x%zx pgd_size, %u pg_shift, %u bits_per_level, = pgd @ %p\n", - ARM_LPAE_MAX_LEVELS - data->start_level, ARM_LPAE_PGD_SIZE(data), - ilog2(ARM_LPAE_GRANULE(data)), data->bits_per_level, data->pgd); -} - -#define __FAIL(ops, i) ({ \ - WARN(1, "selftest: test failed for fmt idx %d\n", (i)); \ - arm_lpae_dump_ops(ops); \ - -EFAULT; \ -}) - -static int __init arm_lpae_run_tests(struct io_pgtable_cfg *cfg) -{ - static const enum io_pgtable_fmt fmts[] __initconst =3D { - ARM_64_LPAE_S1, - ARM_64_LPAE_S2, - }; - - int i, j; - unsigned long iova; - size_t size, mapped; - struct io_pgtable_ops *ops; - - for (i =3D 0; i < ARRAY_SIZE(fmts); ++i) { - cfg_cookie =3D cfg; - ops =3D alloc_io_pgtable_ops(fmts[i], cfg, cfg); - if (!ops) { - pr_err("selftest: failed to allocate io pgtable ops\n"); - return -ENOMEM; - } - - /* - * Initial sanity checks. - * Empty page tables shouldn't provide any translations. - */ - if (ops->iova_to_phys(ops, 42)) - return __FAIL(ops, i); - - if (ops->iova_to_phys(ops, SZ_1G + 42)) - return __FAIL(ops, i); - - if (ops->iova_to_phys(ops, SZ_2G + 42)) - return __FAIL(ops, i); - - /* - * Distinct mappings of different granule sizes. - */ - iova =3D 0; - for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) { - size =3D 1UL << j; - - if (ops->map_pages(ops, iova, iova, size, 1, - IOMMU_READ | IOMMU_WRITE | - IOMMU_NOEXEC | IOMMU_CACHE, - GFP_KERNEL, &mapped)) - return __FAIL(ops, i); - - /* Overlapping mappings */ - if (!ops->map_pages(ops, iova, iova + size, size, 1, - IOMMU_READ | IOMMU_NOEXEC, - GFP_KERNEL, &mapped)) - return __FAIL(ops, i); - - if (ops->iova_to_phys(ops, iova + 42) !=3D (iova + 42)) - return __FAIL(ops, i); - - iova +=3D SZ_1G; - } - - /* Full unmap */ - iova =3D 0; - for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) { - size =3D 1UL << j; - - if (ops->unmap_pages(ops, iova, size, 1, NULL) !=3D size) - return __FAIL(ops, i); - - if (ops->iova_to_phys(ops, iova + 42)) - return __FAIL(ops, i); - - /* Remap full block */ - if (ops->map_pages(ops, iova, iova, size, 1, - IOMMU_WRITE, GFP_KERNEL, &mapped)) - return __FAIL(ops, i); - - if (ops->iova_to_phys(ops, iova + 42) !=3D (iova + 42)) - return __FAIL(ops, i); - - iova +=3D SZ_1G; - } - - /* - * Map/unmap the last largest supported page of the IAS, this can - * trigger corner cases in the concatednated page tables. - */ - mapped =3D 0; - size =3D 1UL << __fls(cfg->pgsize_bitmap); - iova =3D (1UL << cfg->ias) - size; - if (ops->map_pages(ops, iova, iova, size, 1, - IOMMU_READ | IOMMU_WRITE | - IOMMU_NOEXEC | IOMMU_CACHE, - GFP_KERNEL, &mapped)) - return __FAIL(ops, i); - if (mapped !=3D size) - return __FAIL(ops, i); - if (ops->unmap_pages(ops, iova, size, 1, NULL) !=3D size) - return __FAIL(ops, i); - - free_io_pgtable_ops(ops); - } - - return 0; -} - -static int __init arm_lpae_do_selftests(void) -{ - static const unsigned long pgsize[] __initconst =3D { - SZ_4K | SZ_2M | SZ_1G, - SZ_16K | SZ_32M, - SZ_64K | SZ_512M, - }; - - static const unsigned int address_size[] __initconst =3D { - 32, 36, 40, 42, 44, 48, - }; - - int i, j, k, pass =3D 0, fail =3D 0; - struct faux_device *dev; - struct io_pgtable_cfg cfg =3D { - .tlb =3D &dummy_tlb_ops, - .coherent_walk =3D true, - .quirks =3D IO_PGTABLE_QUIRK_NO_WARN, - }; - - dev =3D faux_device_create("io-pgtable-test", NULL, 0); - if (!dev) - return -ENOMEM; - - cfg.iommu_dev =3D &dev->dev; - - for (i =3D 0; i < ARRAY_SIZE(pgsize); ++i) { - for (j =3D 0; j < ARRAY_SIZE(address_size); ++j) { - /* Don't use ias > oas as it is not valid for stage-2. */ - for (k =3D 0; k <=3D j; ++k) { - cfg.pgsize_bitmap =3D pgsize[i]; - cfg.ias =3D address_size[k]; - cfg.oas =3D address_size[j]; - pr_info("selftest: pgsize_bitmap 0x%08lx, IAS %u OAS %u\n", - pgsize[i], cfg.ias, cfg.oas); - if (arm_lpae_run_tests(&cfg)) - fail++; - else - pass++; - } - } - } - - pr_info("selftest: completed with %d PASS %d FAIL\n", pass, fail); - faux_device_destroy(dev); - - return fail ? -EFAULT : 0; -} -subsys_initcall(arm_lpae_do_selftests); -#endif diff --git a/drivers/iommu/io-pgtable-arm.h b/drivers/iommu/io-pgtable-arm.h index ba7cfdf7afa0..a06a23543cff 100644 --- a/drivers/iommu/io-pgtable-arm.h +++ b/drivers/iommu/io-pgtable-arm.h @@ -2,6 +2,8 @@ #ifndef IO_PGTABLE_ARM_H_ #define IO_PGTABLE_ARM_H_ =20 +#include + #define ARM_LPAE_TCR_TG0_4K 0 #define ARM_LPAE_TCR_TG0_64K 1 #define ARM_LPAE_TCR_TG0_16K 2 @@ -27,4 +29,43 @@ #define ARM_LPAE_TCR_PS_48_BIT 0x5ULL #define ARM_LPAE_TCR_PS_52_BIT 0x6ULL =20 +/* Struct accessors */ +#define io_pgtable_to_data(x) \ + container_of((x), struct arm_lpae_io_pgtable, iop) + +#define io_pgtable_ops_to_data(x) \ + io_pgtable_to_data(io_pgtable_ops_to_pgtable(x)) + +struct arm_lpae_io_pgtable { + struct io_pgtable iop; + + int pgd_bits; + int start_level; + int bits_per_level; + + void *pgd; +}; + +#define ARM_LPAE_MAX_ADDR_BITS 52 +#define ARM_LPAE_S2_MAX_CONCAT_PAGES 16 +#define ARM_LPAE_MAX_LEVELS 4 + +/* + * Calculate the right shift amount to get to the portion describing level= l + * in a virtual address mapped by the pagetable in d. + */ +#define ARM_LPAE_LVL_SHIFT(l,d) \ + (((ARM_LPAE_MAX_LEVELS - (l)) * (d)->bits_per_level) + \ + ilog2(sizeof(arm_lpae_iopte))) + +#define ARM_LPAE_GRANULE(d) \ + (sizeof(arm_lpae_iopte) << (d)->bits_per_level) +#define ARM_LPAE_PGD_SIZE(d) \ + (sizeof(arm_lpae_iopte) << (d)->pgd_bits) + +#define ARM_LPAE_PTES_PER_TABLE(d) \ + (ARM_LPAE_GRANULE(d) >> ilog2(sizeof(arm_lpae_iopte))) + +typedef u64 arm_lpae_iopte; + #endif /* IO_PGTABLE_ARM_H_ */ --=20 2.51.0.384.g4c02a37b29-goog From nobody Thu Oct 2 10:39:57 2025 Received: from mail-ej1-f74.google.com (mail-ej1-f74.google.com [209.85.218.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7901532898E for ; Wed, 17 Sep 2025 14:02:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758117756; cv=none; b=FMmonh0IOHGK/HEM+ABipJ+dvo2XV2YmgkKMLukyx9Jb2BmHzR8BymjuxnV/gyLA24dIVHXhN6vKcGWIoBMZv2s3XIoEL5E2cVxD7OOGBBCth3ZQqUNHP+NBW2gDhWcWkE8/Zc0w8wLOwFwFXX61u+EvoDuL8OyKwFaEcaQStgM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758117756; c=relaxed/simple; bh=mOW2tsFUm15DmZmv5idmWrqtHcDQ5+Yigq54v02aKPI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=KaA/aQ+m4O+JTw35M069LKRw3zBYUIv5i5QZ4/8uxuefbqFnYCpPRbmOOgeorO9MthCDXJykvOi/ajX1wh9ub7YIgAF51VXc+vtfn/guauSu6dY/RNA2Je85GtAC9C4vZGVYb+CGy6Ra2cazRHqliuGjB8IiEubB2v/A4krHxEY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--smostafa.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=wZ511JAd; arc=none smtp.client-ip=209.85.218.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--smostafa.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="wZ511JAd" Received: by mail-ej1-f74.google.com with SMTP id a640c23a62f3a-aff0df4c4abso834124866b.1 for ; Wed, 17 Sep 2025 07:02:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1758117753; x=1758722553; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=MbS5VM8JWBvxciJaUjLtcORna4KntSfvz669tBLnIuo=; b=wZ511JAdT/FI9sF5kFNl3Nt9HD3kdlA2F1j9LOlET56evywnTSMIkTz7Us5soSb/L2 9It8WgBLYFl5CGdgi4rrHlHMSKkm9hmKrRjuzXijO9axnac1hqW+eIGKmrQlO3PSBfF6 V6SLSD/tS+qS8sNl1jo0Y1teuu6lXgzznRmMuiGB2SBkk0OYljBjS8u0wIJKBpltgaYD b98rd4g/FdWJXF/bHQSAbz48rQ0C+iJY8M9V5dAL/UjSXRpoyGJpa4qXT1zfkY6zbbSg SOmLjqeDn+pq3lCyFZt6owR94tU0F38DBK8wM+IEfY6zqReSG3CnK6FYlfDxhSFEpWa/ 0o7g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758117753; x=1758722553; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=MbS5VM8JWBvxciJaUjLtcORna4KntSfvz669tBLnIuo=; b=AwRB+OMZ7z8usnBF39XpO8ysPPik02MFRMp4W04PiBItqpBRdSvTlL6y44XxkAp7xe vFk5/FNUS461cAmvvMsS4ZMN5qXZ+xj+EM1IjHqvpdf6mNL9wGI9oFHNN9mw5P4kihTb /Fh8/r88rwUQ9XXpfhL/ClXKezLNdDJTrH/KJpXiLQZeQtFZ2BO1UAwXJYsTn0vNzzjN /TubCOEbWn6acQzPthYb5GjnAXfyaayYky6tOkFvbDQ6Eg+r0V+KLwI1SIRHlcdU12fX 36MGFDl+IHXiDVk2mDI3QHfLxc35ExCHdtNIm8MuDqGOJGEugGLJQB3bdUB1jTD8+9OO QkYQ== X-Forwarded-Encrypted: i=1; AJvYcCURIC1eSNNwceLRL3WZZCB2VJeVYKXrsfi4uSiziBqY56wpJ4qHVw3RTXTTCbhhEgT4m5xvYbQZoTup25c=@vger.kernel.org X-Gm-Message-State: AOJu0YyP0+s7/X+1GZE74l+omV0W9b9LZeWLeebOUY3Jl6Ny4uLlPwkW sDp0TB210S/4bVmsErHl28l44oYE20SwI/JZP0/xI6/QAKOYCkHaEhD9OIVI2yH1fN2jF0scAFp hNlHQWy2+xyncAQ== X-Google-Smtp-Source: AGHT+IE3jLGXIoEmh8JZSWjISt/98EKsWlH7hx945hGGOLLiBVFnNJMHLIN27ohSmsi4BE4MwPLjrgToCtD6zg== X-Received: from edben16.prod.google.com ([2002:a05:6402:5290:b0:62f:464a:b98b]) (user=smostafa job=prod-delivery.src-stubby-dispatcher) by 2002:a17:907:3f0a:b0:b0c:6cae:51e3 with SMTP id a640c23a62f3a-b1bb8ca5382mr283095066b.35.1758117752675; Wed, 17 Sep 2025 07:02:32 -0700 (PDT) Date: Wed, 17 Sep 2025 14:02:03 +0000 In-Reply-To: <20250917140216.2199055-1-smostafa@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250917140216.2199055-1-smostafa@google.com> X-Mailer: git-send-email 2.51.0.384.g4c02a37b29-goog Message-ID: <20250917140216.2199055-3-smostafa@google.com> Subject: [PATCH 2/2] iommu/io-pgtable-arm-selftest: Use KUnit From: Mostafa Saleh To: iommu@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Cc: robin.murphy@arm.com, will@kernel.org, joro@8bytes.org, jgg@ziepe.ca, praan@google.com, Mostafa Saleh Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Integrate the selftests as part of kunit, this makes the results of the test available through debugfs later. Suggested-by: Jason Gunthorpe Signed-off-by: Mostafa Saleh --- drivers/iommu/Kconfig | 2 +- drivers/iommu/io-pgtable-arm-selftests.c | 90 ++++++++++++++---------- 2 files changed, 52 insertions(+), 40 deletions(-) diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 70d29b14d851..52c2fcb0e77f 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -42,7 +42,7 @@ config IOMMU_IO_PGTABLE_LPAE =20 config IOMMU_IO_PGTABLE_LPAE_SELFTEST bool "LPAE selftests" - depends on IOMMU_IO_PGTABLE_LPAE + depends on IOMMU_IO_PGTABLE_LPAE && KUNIT=3Dy help Enable self-tests for LPAE page table allocator. This performs a series of page-table consistency checks during boot. diff --git a/drivers/iommu/io-pgtable-arm-selftests.c b/drivers/iommu/io-pg= table-arm-selftests.c index f7746ff2c7a0..3775936d07c7 100644 --- a/drivers/iommu/io-pgtable-arm-selftests.c +++ b/drivers/iommu/io-pgtable-arm-selftests.c @@ -6,6 +6,8 @@ * * Author: Will Deacon */ +#include +#include #include #include #include @@ -39,25 +41,26 @@ static const struct iommu_flush_ops dummy_tlb_ops __ini= tconst =3D { .tlb_add_page =3D dummy_tlb_add_page, }; =20 -static void __init arm_lpae_dump_ops(struct io_pgtable_ops *ops) +static void __init arm_lpae_dump_ops(struct kunit *test, struct io_pgtable= _ops *ops) { struct arm_lpae_io_pgtable *data =3D io_pgtable_ops_to_data(ops); struct io_pgtable_cfg *cfg =3D &data->iop.cfg; =20 - pr_err("cfg: pgsize_bitmap 0x%lx, ias %u-bit\n", - cfg->pgsize_bitmap, cfg->ias); - pr_err("data: %d levels, 0x%zx pgd_size, %u pg_shift, %u bits_per_level, = pgd @ %p\n", - ARM_LPAE_MAX_LEVELS - data->start_level, ARM_LPAE_PGD_SIZE(data), - ilog2(ARM_LPAE_GRANULE(data)), data->bits_per_level, data->pgd); + kunit_err(test, "cfg: pgsize_bitmap 0x%lx, ias %u-bit\n", + cfg->pgsize_bitmap, cfg->ias); + kunit_err(test, "data: %d levels, 0x%zx pgd_size, %u pg_shift, %u bits_pe= r_level, pgd @ %p\n", + ARM_LPAE_MAX_LEVELS - data->start_level, ARM_LPAE_PGD_SIZE(data), + ilog2(ARM_LPAE_GRANULE(data)), data->bits_per_level, data->pgd); } =20 -#define __FAIL(ops, i) ({ \ - WARN(1, "selftest: test failed for fmt idx %d\n", (i)); \ - arm_lpae_dump_ops(ops); \ - -EFAULT; \ +#define __FAIL(test, ops, i) ({ \ + KUNIT_FAIL(test, ""); \ + kunit_err(test, "selftest: test failed for fmt idx %d\n", (i)); \ + arm_lpae_dump_ops(test, ops); \ + -EFAULT; \ }) =20 -static int __init arm_lpae_run_tests(struct io_pgtable_cfg *cfg) +static int __init arm_lpae_run_tests(struct kunit *test, struct io_pgtable= _cfg *cfg) { static const enum io_pgtable_fmt fmts[] __initconst =3D { ARM_64_LPAE_S1, @@ -73,7 +76,7 @@ static int __init arm_lpae_run_tests(struct io_pgtable_cf= g *cfg) cfg_cookie =3D cfg; ops =3D alloc_io_pgtable_ops(fmts[i], cfg, cfg); if (!ops) { - pr_err("selftest: failed to allocate io pgtable ops\n"); + kunit_err(test, "selftest: failed to allocate io pgtable ops\n"); return -ENOMEM; } =20 @@ -82,13 +85,13 @@ static int __init arm_lpae_run_tests(struct io_pgtable_= cfg *cfg) * Empty page tables shouldn't provide any translations. */ if (ops->iova_to_phys(ops, 42)) - return __FAIL(ops, i); + return __FAIL(test, ops, i); =20 if (ops->iova_to_phys(ops, SZ_1G + 42)) - return __FAIL(ops, i); + return __FAIL(test, ops, i); =20 if (ops->iova_to_phys(ops, SZ_2G + 42)) - return __FAIL(ops, i); + return __FAIL(test, ops, i); =20 /* * Distinct mappings of different granule sizes. @@ -101,16 +104,16 @@ static int __init arm_lpae_run_tests(struct io_pgtabl= e_cfg *cfg) IOMMU_READ | IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_CACHE, GFP_KERNEL, &mapped)) - return __FAIL(ops, i); + return __FAIL(test, ops, i); =20 /* Overlapping mappings */ if (!ops->map_pages(ops, iova, iova + size, size, 1, IOMMU_READ | IOMMU_NOEXEC, GFP_KERNEL, &mapped)) - return __FAIL(ops, i); + return __FAIL(test, ops, i); =20 if (ops->iova_to_phys(ops, iova + 42) !=3D (iova + 42)) - return __FAIL(ops, i); + return __FAIL(test, ops, i); =20 iova +=3D SZ_1G; } @@ -121,18 +124,18 @@ static int __init arm_lpae_run_tests(struct io_pgtabl= e_cfg *cfg) size =3D 1UL << j; =20 if (ops->unmap_pages(ops, iova, size, 1, NULL) !=3D size) - return __FAIL(ops, i); + return __FAIL(test, ops, i); =20 if (ops->iova_to_phys(ops, iova + 42)) - return __FAIL(ops, i); + return __FAIL(test, ops, i); =20 /* Remap full block */ if (ops->map_pages(ops, iova, iova, size, 1, IOMMU_WRITE, GFP_KERNEL, &mapped)) - return __FAIL(ops, i); + return __FAIL(test, ops, i); =20 if (ops->iova_to_phys(ops, iova + 42) !=3D (iova + 42)) - return __FAIL(ops, i); + return __FAIL(test, ops, i); =20 iova +=3D SZ_1G; } @@ -148,11 +151,11 @@ static int __init arm_lpae_run_tests(struct io_pgtabl= e_cfg *cfg) IOMMU_READ | IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_CACHE, GFP_KERNEL, &mapped)) - return __FAIL(ops, i); + return __FAIL(test, ops, i); if (mapped !=3D size) - return __FAIL(ops, i); + return __FAIL(test, ops, i); if (ops->unmap_pages(ops, iova, size, 1, NULL) !=3D size) - return __FAIL(ops, i); + return __FAIL(test, ops, i); =20 free_io_pgtable_ops(ops); } @@ -160,7 +163,7 @@ static int __init arm_lpae_run_tests(struct io_pgtable_= cfg *cfg) return 0; } =20 -static int __init arm_lpae_do_selftests(void) +static void __init io_pgtable_arm_test_run(struct kunit *test) { static const unsigned long pgsize[] __initconst =3D { SZ_4K | SZ_2M | SZ_1G, @@ -173,18 +176,19 @@ static int __init arm_lpae_do_selftests(void) }; =20 int i, j, k, pass =3D 0, fail =3D 0; - struct faux_device *dev; + struct device *dev; struct io_pgtable_cfg cfg =3D { .tlb =3D &dummy_tlb_ops, .coherent_walk =3D true, .quirks =3D IO_PGTABLE_QUIRK_NO_WARN, }; =20 - dev =3D faux_device_create("io-pgtable-test", NULL, 0); - if (!dev) - return -ENOMEM; + dev =3D kunit_device_register(test, "io-pgtable-test"); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, dev); + if (IS_ERR_OR_NULL(dev)) + return; =20 - cfg.iommu_dev =3D &dev->dev; + cfg.iommu_dev =3D dev; =20 for (i =3D 0; i < ARRAY_SIZE(pgsize); ++i) { for (j =3D 0; j < ARRAY_SIZE(address_size); ++j) { @@ -193,9 +197,9 @@ static int __init arm_lpae_do_selftests(void) cfg.pgsize_bitmap =3D pgsize[i]; cfg.ias =3D address_size[k]; cfg.oas =3D address_size[j]; - pr_info("selftest: pgsize_bitmap 0x%08lx, IAS %u OAS %u\n", - pgsize[i], cfg.ias, cfg.oas); - if (arm_lpae_run_tests(&cfg)) + kunit_info(test, "selftest: pgsize_bitmap 0x%08lx, IAS %u OAS %u\n", + pgsize[i], cfg.ias, cfg.oas); + if (arm_lpae_run_tests(test, &cfg)) fail++; else pass++; @@ -203,9 +207,17 @@ static int __init arm_lpae_do_selftests(void) } } =20 - pr_info("selftest: completed with %d PASS %d FAIL\n", pass, fail); - faux_device_destroy(dev); - - return fail ? -EFAULT : 0; + kunit_info(test, "selftest: completed with %d PASS %d FAIL\n", pass, fail= ); } -subsys_initcall(arm_lpae_do_selftests); + +static struct kunit_case io_pgtable_arm_test_cases[] __refdata =3D { + KUNIT_CASE(io_pgtable_arm_test_run), + {}, +}; + +static struct kunit_suite io_pgtable_arm_test =3D { + .name =3D "io-pgtable-arm-test", + .test_cases =3D io_pgtable_arm_test_cases, +}; + +kunit_test_init_section_suite(io_pgtable_arm_test); --=20 2.51.0.384.g4c02a37b29-goog