| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * CPU-agnostic ARM page table allocator.
- *
- * Copyright (C) 2014 ARM Limited
- *
- * Author: Will Deacon <will.deacon@arm.com>
- */
- #define pr_fmt(fmt) "arm-lpae io-pgtable: " fmt
- #include <kunit/device.h>
- #include <kunit/test.h>
- #include <linux/io-pgtable.h>
- #include <linux/kernel.h>
- #include "io-pgtable-arm.h"
- static struct io_pgtable_cfg *cfg_cookie;
- static void dummy_tlb_flush_all(void *cookie)
- {
- WARN_ON(cookie != cfg_cookie);
- }
- static void dummy_tlb_flush(unsigned long iova, size_t size,
- size_t granule, void *cookie)
- {
- WARN_ON(cookie != cfg_cookie);
- WARN_ON(!(size & cfg_cookie->pgsize_bitmap));
- }
- static void 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 = {
- .tlb_flush_all = dummy_tlb_flush_all,
- .tlb_flush_walk = dummy_tlb_flush,
- .tlb_add_page = dummy_tlb_add_page,
- };
- #define __FAIL(test, i) ({ \
- KUNIT_FAIL(test, "test failed for fmt idx %d\n", (i)); \
- -EFAULT; \
- })
- static int arm_lpae_run_tests(struct kunit *test, struct io_pgtable_cfg *cfg)
- {
- static const enum io_pgtable_fmt fmts[] = {
- 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 = 0; i < ARRAY_SIZE(fmts); ++i) {
- cfg_cookie = cfg;
- ops = alloc_io_pgtable_ops(fmts[i], cfg, cfg);
- if (!ops) {
- kunit_err(test, "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(test, i);
- if (ops->iova_to_phys(ops, SZ_1G + 42))
- return __FAIL(test, i);
- if (ops->iova_to_phys(ops, SZ_2G + 42))
- return __FAIL(test, i);
- /*
- * Distinct mappings of different granule sizes.
- */
- iova = 0;
- for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) {
- size = 1UL << j;
- if (ops->map_pages(ops, iova, iova, size, 1,
- IOMMU_READ | IOMMU_WRITE |
- IOMMU_NOEXEC | IOMMU_CACHE,
- GFP_KERNEL, &mapped))
- return __FAIL(test, i);
- /* Overlapping mappings */
- if (!ops->map_pages(ops, iova, iova + size, size, 1,
- IOMMU_READ | IOMMU_NOEXEC,
- GFP_KERNEL, &mapped))
- return __FAIL(test, i);
- if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
- return __FAIL(test, i);
- iova += SZ_1G;
- }
- /* Full unmap */
- iova = 0;
- for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) {
- size = 1UL << j;
- if (ops->unmap_pages(ops, iova, size, 1, NULL) != size)
- return __FAIL(test, i);
- if (ops->iova_to_phys(ops, iova + 42))
- return __FAIL(test, i);
- /* Remap full block */
- if (ops->map_pages(ops, iova, iova, size, 1,
- IOMMU_WRITE, GFP_KERNEL, &mapped))
- return __FAIL(test, i);
- if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
- return __FAIL(test, i);
- iova += SZ_1G;
- }
- /*
- * Map/unmap the last largest supported page of the IAS, this can
- * trigger corner cases in the concatednated page tables.
- */
- mapped = 0;
- size = 1UL << __fls(cfg->pgsize_bitmap);
- iova = (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(test, i);
- if (mapped != size)
- return __FAIL(test, i);
- if (ops->unmap_pages(ops, iova, size, 1, NULL) != size)
- return __FAIL(test, i);
- free_io_pgtable_ops(ops);
- }
- return 0;
- }
- static void arm_lpae_do_selftests(struct kunit *test)
- {
- static const unsigned long pgsize[] = {
- SZ_4K | SZ_2M | SZ_1G,
- SZ_16K | SZ_32M,
- SZ_64K | SZ_512M,
- };
- static const unsigned int address_size[] = {
- 32, 36, 40, 42, 44, 48,
- };
- int i, j, k, pass = 0, fail = 0;
- struct device *dev;
- struct io_pgtable_cfg cfg = {
- .tlb = &dummy_tlb_ops,
- .coherent_walk = true,
- .quirks = IO_PGTABLE_QUIRK_NO_WARN,
- };
- dev = kunit_device_register(test, "io-pgtable-test");
- KUNIT_EXPECT_NOT_ERR_OR_NULL(test, dev);
- if (IS_ERR_OR_NULL(dev))
- return;
- cfg.iommu_dev = dev;
- for (i = 0; i < ARRAY_SIZE(pgsize); ++i) {
- for (j = 0; j < ARRAY_SIZE(address_size); ++j) {
- /* Don't use ias > oas as it is not valid for stage-2. */
- for (k = 0; k <= j; ++k) {
- cfg.pgsize_bitmap = pgsize[i];
- cfg.ias = address_size[k];
- cfg.oas = address_size[j];
- kunit_info(test, "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++;
- }
- }
- }
- kunit_info(test, "completed with %d PASS %d FAIL\n", pass, fail);
- }
- static struct kunit_case io_pgtable_arm_test_cases[] = {
- KUNIT_CASE(arm_lpae_do_selftests),
- {},
- };
- static struct kunit_suite io_pgtable_arm_test = {
- .name = "io-pgtable-arm-test",
- .test_cases = io_pgtable_arm_test_cases,
- };
- kunit_test_suite(io_pgtable_arm_test);
- MODULE_DESCRIPTION("io-pgtable-arm library kunit tests");
- MODULE_LICENSE("GPL");
|