* [RFC PATCH v2 4/6] drm/gma500: Make SGX MMU insertion checked and transactional
2026-11-09 6:32 [RFC PATCH v2 0/6] drm/gma500: prepare GEM and MMU for 3D rendering and hybrid 2D Gabriel Gama
` (2 preceding siblings ...)
2026-11-09 6:33 ` [RFC PATCH v2 3/6] drm/gma500: Fix SGX page-table cache-flush mapping lifetime Gabriel Gama
@ 2026-11-09 6:33 ` Gabriel Gama
2026-11-09 6:41 ` sashiko-bot
2026-11-12 21:20 ` Joseph Telaak
2026-11-09 6:33 ` [RFC PATCH v2 5/6] drm/gma500: Propagate SGX MMU setup and GEM pin failures Gabriel Gama
2026-11-09 6:33 ` [RFC PATCH v2 6/6] drm/gma500: Add software-only KUnit coverage for SGX page tables Gabriel Gama
5 siblings, 2 replies; 13+ messages in thread
From: Gabriel Gama @ 2026-11-09 6:33 UTC (permalink / raw)
To: dri-devel; +Cc: patrik.r.jakobsson
Validate 32-bit mapping extents, PFNs and tiled geometry before changing
PTEs. Serialize changes with the write semaphore and undo only the
inserted prefix on allocation failure or collision, preserving existing
entries. Do not allocate tables or decrement counts when removing holes.
Use 64-bit exclusive endpoints so mapping the last GPU page does not wrap
on i386. Zero-page requests remain no-ops.
Signed-off-by: Gabriel Gama <operatingsystemsdepression@gmail.com>
---
drivers/gpu/drm/gma500/mmu.c | 211 +++++++++++++++++++++++++----------
drivers/gpu/drm/gma500/mmu.h | 5 +-
2 files changed, 158 insertions(+), 58 deletions(-)
diff --git a/drivers/gpu/drm/gma500/mmu.c b/drivers/gpu/drm/gma500/mmu.c
index 34ae0f125..0d98c7870 100644
--- a/drivers/gpu/drm/gma500/mmu.c
+++ b/drivers/gpu/drm/gma500/mmu.c
@@ -136,10 +136,9 @@ void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context)
}
-static inline unsigned long psb_pd_addr_end(unsigned long addr,
- unsigned long end)
+static inline u64 psb_pd_addr_end(u64 addr, u64 end)
{
- addr = (addr + PSB_PDE_MASK + 1) & ~PSB_PDE_MASK;
+ addr = (addr + PSB_PDE_MASK + 1) & ~(u64)PSB_PDE_MASK;
return (addr < end) ? addr : end;
}
@@ -484,13 +483,13 @@ static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, unsigned long address,
struct psb_mmu_pt *pt;
uint32_t rows = 1;
uint32_t i;
- unsigned long addr;
- unsigned long end;
- unsigned long next;
- unsigned long add;
- unsigned long row_add;
+ u64 addr;
+ u64 end;
+ u64 next;
+ u64 add;
+ u64 row_add;
unsigned long clflush_add = pd->driver->clflush_add;
- unsigned long clflush_mask = pd->driver->clflush_mask;
+ u64 clflush_mask = ~((u64)clflush_add - 1);
if (!pd->driver->has_clflush)
return;
@@ -500,8 +499,8 @@ static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, unsigned long address,
else
desired_tile_stride = num_pages;
- add = desired_tile_stride << PAGE_SHIFT;
- row_add = hw_tile_stride << PAGE_SHIFT;
+ add = (u64)desired_tile_stride << PAGE_SHIFT;
+ row_add = (u64)hw_tile_stride << PAGE_SHIFT;
mb();
for (i = 0; i < rows; ++i) {
@@ -525,38 +524,97 @@ static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, unsigned long address,
mb();
}
+/* Validate the complete mapping before touching page tables. */
+static int psb_mmu_check_range(unsigned long address, u32 num_pages,
+ u32 desired_tile_stride, u32 hw_tile_stride)
+{
+ u64 span;
+ u32 rows;
+
+ if (!IS_ALIGNED(address, PAGE_SIZE))
+ return -EINVAL;
+ if (((u64)address >> 32) || num_pages > BIT(20))
+ return -ERANGE;
+ if (!num_pages)
+ return 0;
+
+ if (hw_tile_stride) {
+ if (!desired_tile_stride ||
+ num_pages % desired_tile_stride ||
+ hw_tile_stride < desired_tile_stride)
+ return -EINVAL;
+ rows = num_pages / desired_tile_stride;
+ span = (u64)(rows - 1) * hw_tile_stride + desired_tile_stride;
+ } else {
+ span = num_pages;
+ }
+
+ if (span > (BIT_ULL(32) - address) >> PAGE_SHIFT)
+ return -ERANGE;
+
+ return 0;
+}
+
+/* The caller holds driver->sem for write; only its inserted prefix is undone. */
+static void psb_mmu_rollback_pages(struct psb_mmu_pd *pd,
+ unsigned long address, u32 inserted,
+ u32 desired_tile_stride, u32 hw_tile_stride)
+{
+ struct psb_mmu_pt *pt;
+ u64 offset;
+ u32 i;
+
+ for (i = 0; i < inserted; i++) {
+ if (hw_tile_stride)
+ offset = (u64)(i / desired_tile_stride) * hw_tile_stride +
+ i % desired_tile_stride;
+ else
+ offset = i;
+ pt = psb_mmu_pt_map_lock(pd, address + (offset << PAGE_SHIFT));
+ if (WARN_ON_ONCE(!pt))
+ continue;
+ psb_mmu_invalidate_pte(pt, address + (offset << PAGE_SHIFT));
+ pt->count--;
+ psb_mmu_pt_unmap_unlock(pt);
+ }
+}
+
void psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd,
unsigned long address, uint32_t num_pages)
{
struct psb_mmu_pt *pt;
- unsigned long addr;
- unsigned long end;
- unsigned long next;
+ u64 addr;
+ u64 end;
+ u64 next;
unsigned long f_address = address;
- down_read(&pd->driver->sem);
+ if (!num_pages || psb_mmu_check_range(address, num_pages, 0, 0))
+ return;
+
+ down_write(&pd->driver->sem);
addr = address;
- end = addr + (num_pages << PAGE_SHIFT);
+ end = addr + ((u64)num_pages << PAGE_SHIFT);
do {
next = psb_pd_addr_end(addr, end);
- pt = psb_mmu_pt_alloc_map_lock(pd, addr);
+ pt = psb_mmu_pt_map_lock(pd, addr);
if (!pt)
- goto out;
+ continue;
do {
- psb_mmu_invalidate_pte(pt, addr);
- --pt->count;
+ if (pt->v[psb_mmu_pt_index(addr)] != pd->invalid_pte) {
+ psb_mmu_invalidate_pte(pt, addr);
+ --pt->count;
+ }
} while (addr += PAGE_SIZE, addr < next);
psb_mmu_pt_unmap_unlock(pt);
} while (addr = next, next != end);
-out:
if (pd->hw_context != -1)
psb_mmu_flush_ptes(pd, f_address, num_pages, 1, 1);
- up_read(&pd->driver->sem);
+ up_write(&pd->driver->sem);
if (pd->hw_context != -1)
psb_mmu_flush(pd->driver);
@@ -571,22 +629,26 @@ void psb_mmu_remove_pages(struct psb_mmu_pd *pd, unsigned long address,
struct psb_mmu_pt *pt;
uint32_t rows = 1;
uint32_t i;
- unsigned long addr;
- unsigned long end;
- unsigned long next;
- unsigned long add;
- unsigned long row_add;
+ u64 addr;
+ u64 end;
+ u64 next;
+ u64 add;
+ u64 row_add;
unsigned long f_address = address;
+ if (!num_pages || psb_mmu_check_range(address, num_pages,
+ desired_tile_stride, hw_tile_stride))
+ return;
+
if (hw_tile_stride)
rows = num_pages / desired_tile_stride;
else
desired_tile_stride = num_pages;
- add = desired_tile_stride << PAGE_SHIFT;
- row_add = hw_tile_stride << PAGE_SHIFT;
+ add = (u64)desired_tile_stride << PAGE_SHIFT;
+ row_add = (u64)hw_tile_stride << PAGE_SHIFT;
- down_read(&pd->driver->sem);
+ down_write(&pd->driver->sem);
/* Make sure we only need to flush this processor's cache */
@@ -601,8 +663,10 @@ void psb_mmu_remove_pages(struct psb_mmu_pd *pd, unsigned long address,
if (!pt)
continue;
do {
- psb_mmu_invalidate_pte(pt, addr);
- --pt->count;
+ if (pt->v[psb_mmu_pt_index(addr)] != pd->invalid_pte) {
+ psb_mmu_invalidate_pte(pt, addr);
+ --pt->count;
+ }
} while (addr += PAGE_SIZE, addr < next);
psb_mmu_pt_unmap_unlock(pt);
@@ -614,7 +678,7 @@ void psb_mmu_remove_pages(struct psb_mmu_pd *pd, unsigned long address,
psb_mmu_flush_ptes(pd, f_address, num_pages,
desired_tile_stride, hw_tile_stride);
- up_read(&pd->driver->sem);
+ up_write(&pd->driver->sem);
if (pd->hw_context != -1)
psb_mmu_flush(pd->driver);
@@ -626,16 +690,23 @@ int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn,
{
struct psb_mmu_pt *pt;
uint32_t pte;
- unsigned long addr;
- unsigned long end;
- unsigned long next;
+ u64 addr;
+ u64 end;
+ u64 next;
unsigned long f_address = address;
- int ret = -ENOMEM;
+ u32 inserted = 0;
+ int ret;
+
+ ret = psb_mmu_check_range(address, num_pages, 0, 0);
+ if (ret || !num_pages)
+ return ret;
+ if ((u64)start_pfn + num_pages > BIT_ULL(20))
+ return -ERANGE;
- down_read(&pd->driver->sem);
+ down_write(&pd->driver->sem);
addr = address;
- end = addr + (num_pages << PAGE_SHIFT);
+ end = addr + ((u64)num_pages << PAGE_SHIFT);
do {
next = psb_pd_addr_end(addr, end);
@@ -645,9 +716,15 @@ int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn,
goto out;
}
do {
+ if (pt->v[psb_mmu_pt_index(addr)] != pd->invalid_pte) {
+ psb_mmu_pt_unmap_unlock(pt);
+ ret = -EBUSY;
+ goto out;
+ }
pte = psb_mmu_mask_pte(start_pfn++, type);
psb_mmu_set_pte(pt, addr, pte);
pt->count++;
+ inserted++;
} while (addr += PAGE_SIZE, addr < next);
psb_mmu_pt_unmap_unlock(pt);
@@ -655,10 +732,12 @@ int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn,
ret = 0;
out:
+ if (ret)
+ psb_mmu_rollback_pages(pd, f_address, inserted, 0, 0);
if (pd->hw_context != -1)
psb_mmu_flush_ptes(pd, f_address, num_pages, 1, 1);
- up_read(&pd->driver->sem);
+ up_write(&pd->driver->sem);
if (pd->hw_context != -1)
psb_mmu_flush(pd->driver);
@@ -675,26 +754,35 @@ int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages,
uint32_t rows = 1;
uint32_t i;
uint32_t pte;
- unsigned long addr;
- unsigned long end;
- unsigned long next;
- unsigned long add;
- unsigned long row_add;
+ u64 addr;
+ u64 end;
+ u64 next;
+ u64 add;
+ u64 row_add;
unsigned long f_address = address;
- int ret = -ENOMEM;
+ u32 inserted = 0;
+ int ret;
+
+ ret = psb_mmu_check_range(address, num_pages,
+ desired_tile_stride, hw_tile_stride);
+ if (ret || !num_pages)
+ return ret;
+ if (!pages)
+ return -EINVAL;
+ for (i = 0; i < num_pages; i++) {
+ if (!pages[i] || page_to_pfn(pages[i]) >= BIT_ULL(20))
+ return -ERANGE;
+ }
- if (hw_tile_stride) {
- if (num_pages % desired_tile_stride != 0)
- return -EINVAL;
+ if (hw_tile_stride)
rows = num_pages / desired_tile_stride;
- } else {
+ else
desired_tile_stride = num_pages;
- }
- add = desired_tile_stride << PAGE_SHIFT;
- row_add = hw_tile_stride << PAGE_SHIFT;
+ add = (u64)desired_tile_stride << PAGE_SHIFT;
+ row_add = (u64)hw_tile_stride << PAGE_SHIFT;
- down_read(&pd->driver->sem);
+ down_write(&pd->driver->sem);
for (i = 0; i < rows; ++i) {
@@ -704,13 +792,21 @@ int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages,
do {
next = psb_pd_addr_end(addr, end);
pt = psb_mmu_pt_alloc_map_lock(pd, addr);
- if (!pt)
+ if (!pt) {
+ ret = -ENOMEM;
goto out;
+ }
do {
+ if (pt->v[psb_mmu_pt_index(addr)] != pd->invalid_pte) {
+ psb_mmu_pt_unmap_unlock(pt);
+ ret = -EBUSY;
+ goto out;
+ }
pte = psb_mmu_mask_pte(page_to_pfn(*pages++),
type);
psb_mmu_set_pte(pt, addr, pte);
pt->count++;
+ inserted++;
} while (addr += PAGE_SIZE, addr < next);
psb_mmu_pt_unmap_unlock(pt);
@@ -721,11 +817,14 @@ int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages,
ret = 0;
out:
+ if (ret)
+ psb_mmu_rollback_pages(pd, f_address, inserted, desired_tile_stride,
+ hw_tile_stride);
if (pd->hw_context != -1)
psb_mmu_flush_ptes(pd, f_address, num_pages,
desired_tile_stride, hw_tile_stride);
- up_read(&pd->driver->sem);
+ up_write(&pd->driver->sem);
if (pd->hw_context != -1)
psb_mmu_flush(pd->driver);
diff --git a/drivers/gpu/drm/gma500/mmu.h b/drivers/gpu/drm/gma500/mmu.h
index e6d397037..4474718aa 100644
--- a/drivers/gpu/drm/gma500/mmu.h
+++ b/drivers/gpu/drm/gma500/mmu.h
@@ -9,8 +9,9 @@
#define __MMU_H
struct psb_mmu_driver {
- /* protects driver- and pd structures. Always take in read mode
- * before taking the page table spinlock.
+ /*
+ * Protects driver and page-directory structures. Mapping changes take
+ * this semaphore for write, before the page-table spinlock.
*/
struct rw_semaphore sem;
^ permalink raw reply related [flat|nested] 13+ messages in thread* [RFC PATCH v2 6/6] drm/gma500: Add software-only KUnit coverage for SGX page tables
2026-11-09 6:32 [RFC PATCH v2 0/6] drm/gma500: prepare GEM and MMU for 3D rendering and hybrid 2D Gabriel Gama
` (4 preceding siblings ...)
2026-11-09 6:33 ` [RFC PATCH v2 5/6] drm/gma500: Propagate SGX MMU setup and GEM pin failures Gabriel Gama
@ 2026-11-09 6:33 ` Gabriel Gama
5 siblings, 0 replies; 13+ messages in thread
From: Gabriel Gama @ 2026-11-09 6:33 UTC (permalink / raw)
To: dri-devel; +Cc: patrik.r.jakobsson
Add KUnit cases for invalid ranges and tiles, the final GPU page,
directory crossings, PFN limits, collision rollback and removal holes.
The optional built-in fixture uses a software page directory with no DRM
device and hw_context=-1. It performs no MMIO or GPU submission.
KUnit calls the per-case exit callback even after init fails. Use one
NULL-safe cleanup for partial initialization and clear released pointers
and test->priv so repeated cleanup cannot release resources twice.
Signed-off-by: Gabriel Gama <operatingsystemsdepression@gmail.com>
---
drivers/gpu/drm/gma500/Kconfig | 10 ++
drivers/gpu/drm/gma500/Makefile | 2 +
drivers/gpu/drm/gma500/mmu_test.c | 230 ++++++++++++++++++++++++++++++
3 files changed, 242 insertions(+)
create mode 100644 drivers/gpu/drm/gma500/mmu_test.c
diff --git a/drivers/gpu/drm/gma500/Kconfig b/drivers/gpu/drm/gma500/Kconfig
index dc4c56638..b6e2dda13 100644
--- a/drivers/gpu/drm/gma500/Kconfig
+++ b/drivers/gpu/drm/gma500/Kconfig
@@ -16,3 +16,13 @@ config DRM_GMA500
Say yes for an experimental 2D KMS framebuffer driver for the
Intel GMA500 (Poulsbo), Intel GMA600 (Moorestown/Oak Trail) and
Intel GMA3600/3650 (Cedar Trail).
+
+config DRM_GMA500_KUNIT_TEST
+ bool "KUnit tests for the GMA500 SGX page tables" if !KUNIT_ALL_TESTS
+ depends on DRM_GMA500=y && KUNIT=y
+ default KUNIT_ALL_TESTS
+ help
+ Run software-only tests for SGX page-table range validation,
+ rollback, mapping collisions and removal. The tests use a software
+ page directory with no device or hardware context, and never
+ submit commands to the GPU or access its registers.
diff --git a/drivers/gpu/drm/gma500/Makefile b/drivers/gpu/drm/gma500/Makefile
index 58fed80c7..069400d70 100644
--- a/drivers/gpu/drm/gma500/Makefile
+++ b/drivers/gpu/drm/gma500/Makefile
@@ -39,4 +39,6 @@ gma500_gfx-y += \
gma500_gfx-$(CONFIG_ACPI) += opregion.o
gma500_gfx-$(CONFIG_DRM_FBDEV_EMULATION) += fbdev.o
+gma500_gfx-$(CONFIG_DRM_GMA500_KUNIT_TEST) += mmu_test.o
+
obj-$(CONFIG_DRM_GMA500) += gma500_gfx.o
diff --git a/drivers/gpu/drm/gma500/mmu_test.c b/drivers/gpu/drm/gma500/mmu_test.c
new file mode 100644
index 000000000..f9dbe66cd
--- /dev/null
+++ b/drivers/gpu/drm/gma500/mmu_test.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* SGX page-table tests. No device, hardware context, or MMIO is required. */
+#include <kunit/test.h>
+#include <linux/highmem.h>
+
+#include "mmu.h"
+#include "psb_drv.h"
+
+struct psb_mmu_test_context {
+ struct psb_mmu_driver driver;
+ struct psb_mmu_pd *pd;
+ struct page *pages[4];
+};
+
+static int psb_mmu_test_init(struct kunit *test)
+{
+ struct psb_mmu_test_context *ctx;
+ int i;
+
+ test->priv = NULL;
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ test->priv = ctx;
+ spin_lock_init(&ctx->driver.lock);
+ init_rwsem(&ctx->driver.sem);
+ ctx->pd = psb_mmu_alloc_pd(&ctx->driver, 1, 0);
+ if (!ctx->pd)
+ return -ENOMEM;
+ for (i = 0; i < ARRAY_SIZE(ctx->pages); i++) {
+ ctx->pages[i] = alloc_page(GFP_DMA32);
+ if (!ctx->pages[i])
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void psb_mmu_test_exit(struct kunit *test)
+{
+ struct psb_mmu_test_context *ctx = test->priv;
+ int i;
+
+ if (!ctx)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(ctx->pages); i++) {
+ if (ctx->pages[i]) {
+ __free_page(ctx->pages[i]);
+ ctx->pages[i] = NULL;
+ }
+ }
+ if (ctx->pd) {
+ psb_mmu_free_pagedir(ctx->pd);
+ ctx->pd = NULL;
+ }
+ test->priv = NULL;
+}
+
+static u32 psb_mmu_test_pte(struct psb_mmu_pd *pd, u32 address)
+{
+ struct psb_mmu_pt *pt = pd->tables[address >> PSB_PDE_SHIFT];
+ u32 *entries;
+ u32 value;
+
+ if (!pt)
+ return pd->invalid_pte;
+ entries = kmap_local_page(pt->p);
+ value = entries[(address >> PAGE_SHIFT) & 0x3ff];
+ kunmap_local(entries);
+ return value;
+}
+
+static void psb_mmu_zero_pages_test(struct kunit *test)
+{
+ struct psb_mmu_test_context *ctx = test->priv;
+
+ KUNIT_EXPECT_EQ(test, psb_mmu_insert_pages(ctx->pd, NULL, 0, 0,
+ 0, 0, 0), 0);
+ KUNIT_EXPECT_EQ(test, psb_mmu_insert_pfn_sequence(ctx->pd, 0,
+ 0, 0, 0), 0);
+ psb_mmu_remove_pages(ctx->pd, 0, 0, 0, 0);
+ psb_mmu_remove_pfn_sequence(ctx->pd, 0, 0);
+ KUNIT_EXPECT_PTR_EQ(test, ctx->pd->tables[0], NULL);
+}
+
+static void psb_mmu_range_test(struct kunit *test)
+{
+ struct psb_mmu_test_context *ctx = test->priv;
+
+ KUNIT_EXPECT_EQ(test, psb_mmu_insert_pages(ctx->pd, ctx->pages,
+ 1, 1, 0, 0, 0), -EINVAL);
+ KUNIT_EXPECT_EQ(test, psb_mmu_insert_pages(ctx->pd, ctx->pages,
+ 0xfffff000UL, 2, 0, 0, 0),
+ -ERANGE);
+ KUNIT_EXPECT_EQ(test, psb_mmu_insert_pages(ctx->pd, ctx->pages,
+ 0, 4, 0, 4, 0), -EINVAL);
+ KUNIT_EXPECT_EQ(test, psb_mmu_insert_pages(ctx->pd, ctx->pages,
+ 0, 4, 3, 4, 0), -EINVAL);
+ KUNIT_EXPECT_EQ(test, psb_mmu_insert_pages(ctx->pd, ctx->pages,
+ 0, 4, 2, 1, 0), -EINVAL);
+ KUNIT_EXPECT_EQ(test, psb_mmu_insert_pages(ctx->pd, ctx->pages,
+ 0, 4, 2, U32_MAX, 0), -ERANGE);
+ KUNIT_EXPECT_EQ(test, psb_mmu_insert_pages(ctx->pd, NULL,
+ 0, 1, 0, 0, 0), -EINVAL);
+ KUNIT_EXPECT_PTR_EQ(test, ctx->pd->tables[0], NULL);
+}
+
+static void psb_mmu_last_page_test(struct kunit *test)
+{
+ struct psb_mmu_test_context *ctx = test->priv;
+
+ KUNIT_ASSERT_EQ(test, psb_mmu_insert_pages(ctx->pd, ctx->pages,
+ 0xfffff000UL, 1, 0, 0, 0), 0);
+ KUNIT_EXPECT_EQ(test, psb_mmu_test_pte(ctx->pd, 0xfffff000),
+ (u32)(page_to_pfn(ctx->pages[0]) << PAGE_SHIFT) |
+ PSB_PTE_VALID);
+ psb_mmu_remove_pages(ctx->pd, 0xfffff000UL, 1, 0, 0);
+ KUNIT_EXPECT_PTR_EQ(test, ctx->pd->tables[1023], NULL);
+}
+
+static void psb_mmu_directory_boundary_test(struct kunit *test)
+{
+ struct psb_mmu_test_context *ctx = test->priv;
+
+ KUNIT_ASSERT_EQ(test, psb_mmu_insert_pages(ctx->pd, ctx->pages,
+ 0x3ff000, 2, 0, 0, 0), 0);
+ KUNIT_EXPECT_EQ(test, ctx->pd->tables[0]->count, 1U);
+ KUNIT_EXPECT_EQ(test, ctx->pd->tables[1]->count, 1U);
+ psb_mmu_remove_pages(ctx->pd, 0x3ff000, 2, 0, 0);
+ KUNIT_EXPECT_PTR_EQ(test, ctx->pd->tables[0], NULL);
+ KUNIT_EXPECT_PTR_EQ(test, ctx->pd->tables[1], NULL);
+}
+
+static void psb_mmu_collision_rollback_test(struct kunit *test)
+{
+ struct psb_mmu_test_context *ctx = test->priv;
+ u32 retained;
+
+ KUNIT_ASSERT_EQ(test, psb_mmu_insert_pages(ctx->pd, ctx->pages + 3,
+ 0x400000, 1, 0, 0, 0), 0);
+ retained = psb_mmu_test_pte(ctx->pd, 0x400000);
+ KUNIT_EXPECT_EQ(test, psb_mmu_insert_pages(ctx->pd, ctx->pages,
+ 0x3ff000, 2, 0, 0, 0), -EBUSY);
+ KUNIT_EXPECT_PTR_EQ(test, ctx->pd->tables[0], NULL);
+ KUNIT_EXPECT_EQ(test, psb_mmu_test_pte(ctx->pd, 0x400000), retained);
+ KUNIT_EXPECT_EQ(test, ctx->pd->tables[1]->count, 1U);
+}
+
+static void psb_mmu_tiled_rollback_test(struct kunit *test)
+{
+ struct psb_mmu_test_context *ctx = test->priv;
+ u32 retained;
+
+ KUNIT_ASSERT_EQ(test, psb_mmu_insert_pages(ctx->pd, ctx->pages + 3,
+ 0x8000, 1, 0, 0, 0), 0);
+ retained = psb_mmu_test_pte(ctx->pd, 0x8000);
+ KUNIT_EXPECT_EQ(test, psb_mmu_insert_pages(ctx->pd, ctx->pages,
+ 0x3000, 4, 2, 4, 0), -EBUSY);
+ KUNIT_EXPECT_EQ(test, psb_mmu_test_pte(ctx->pd, 0x3000), 0U);
+ KUNIT_EXPECT_EQ(test, psb_mmu_test_pte(ctx->pd, 0x4000), 0U);
+ KUNIT_EXPECT_EQ(test, psb_mmu_test_pte(ctx->pd, 0x7000), 0U);
+ KUNIT_EXPECT_EQ(test, psb_mmu_test_pte(ctx->pd, 0x8000), retained);
+ KUNIT_EXPECT_EQ(test, ctx->pd->tables[0]->count, 1U);
+}
+
+static void psb_mmu_pfn_range_test(struct kunit *test)
+{
+ struct psb_mmu_test_context *ctx = test->priv;
+
+ KUNIT_EXPECT_EQ(test, psb_mmu_insert_pfn_sequence(ctx->pd, 0x100000,
+ 0, 1, 0), -ERANGE);
+ KUNIT_EXPECT_EQ(test, psb_mmu_insert_pfn_sequence(ctx->pd, 0xfffff,
+ 0, 2, 0), -ERANGE);
+ KUNIT_ASSERT_EQ(test, psb_mmu_insert_pfn_sequence(ctx->pd, 0xfffff,
+ 0, 1, 0), 0);
+ KUNIT_EXPECT_EQ(test, psb_mmu_test_pte(ctx->pd, 0), 0xfffff001U);
+ psb_mmu_remove_pfn_sequence(ctx->pd, 0, 1);
+ KUNIT_EXPECT_PTR_EQ(test, ctx->pd->tables[0], NULL);
+}
+
+static void psb_mmu_pfn_rollback_test(struct kunit *test)
+{
+ struct psb_mmu_test_context *ctx = test->priv;
+ u32 retained;
+
+ KUNIT_ASSERT_EQ(test, psb_mmu_insert_pfn_sequence(ctx->pd, 0x100,
+ 0x400000, 1, 0), 0);
+ retained = psb_mmu_test_pte(ctx->pd, 0x400000);
+ KUNIT_EXPECT_EQ(test, psb_mmu_insert_pfn_sequence(ctx->pd, 0x200,
+ 0x3ff000, 2, 0), -EBUSY);
+ KUNIT_EXPECT_PTR_EQ(test, ctx->pd->tables[0], NULL);
+ KUNIT_EXPECT_EQ(test, psb_mmu_test_pte(ctx->pd, 0x400000), retained);
+ KUNIT_EXPECT_EQ(test, ctx->pd->tables[1]->count, 1U);
+}
+
+static void psb_mmu_remove_hole_test(struct kunit *test)
+{
+ struct psb_mmu_test_context *ctx = test->priv;
+
+ KUNIT_ASSERT_EQ(test, psb_mmu_insert_pages(ctx->pd, ctx->pages,
+ 0x1000, 1, 0, 0, 0), 0);
+ psb_mmu_remove_pages(ctx->pd, 0x2000, 1, 0, 0);
+ KUNIT_EXPECT_EQ(test, ctx->pd->tables[0]->count, 1U);
+ psb_mmu_remove_pages(ctx->pd, 0x1000, 1, 0, 0);
+ psb_mmu_remove_pfn_sequence(ctx->pd, 0x400000, 1);
+ KUNIT_EXPECT_PTR_EQ(test, ctx->pd->tables[0], NULL);
+ KUNIT_EXPECT_PTR_EQ(test, ctx->pd->tables[1], NULL);
+}
+
+static struct kunit_case psb_mmu_test_cases[] = {
+ KUNIT_CASE(psb_mmu_zero_pages_test),
+ KUNIT_CASE(psb_mmu_range_test),
+ KUNIT_CASE(psb_mmu_last_page_test),
+ KUNIT_CASE(psb_mmu_directory_boundary_test),
+ KUNIT_CASE(psb_mmu_collision_rollback_test),
+ KUNIT_CASE(psb_mmu_tiled_rollback_test),
+ KUNIT_CASE(psb_mmu_pfn_range_test),
+ KUNIT_CASE(psb_mmu_pfn_rollback_test),
+ KUNIT_CASE(psb_mmu_remove_hole_test),
+ {}
+};
+
+static struct kunit_suite psb_mmu_test_suite = {
+ .name = "psb_mmu",
+ .init = psb_mmu_test_init,
+ .exit = psb_mmu_test_exit,
+ .test_cases = psb_mmu_test_cases,
+};
+
+kunit_test_suite(psb_mmu_test_suite);
^ permalink raw reply related [flat|nested] 13+ messages in thread