feat(mm): add `get_mode_partial`
Add the `get_mode_partial` function for querying the mode of a range of
pages, and returning the longest subrange of pages that have the same
mode. This is needed for the new behaviour of `FFA_MEM_PERM_GET` added
in v1.3.
Change-Id: I9bdfd7f76c45faec7793dd0023e70409ce5bea0f
Signed-off-by: Karl Meakin <karl.meakin@arm.com>
diff --git a/src/mm.c b/src/mm.c
index 74002c5..f53cfeb 100644
--- a/src/mm.c
+++ b/src/mm.c
@@ -1142,6 +1142,26 @@
return success;
}
+bool mm_vm_get_mode_partial(const struct mm_ptable *ptable, ipaddr_t begin,
+ ipaddr_t end, mm_mode_t *mode, ipaddr_t *end_ret)
+{
+ struct mm_get_attrs_state ret;
+ bool success;
+
+ ret = mm_get_attrs(ptable, ipa_addr(begin), ipa_addr(end));
+ success = ret.got_attrs;
+
+ if (success && mode != NULL) {
+ *mode = arch_mm_stage2_attrs_to_mode(ret.attrs);
+ }
+
+ if (success && end_ret != NULL) {
+ *end_ret = ret.mismatch ? ipa_init(ret.mismatch) : end;
+ }
+
+ return success;
+}
+
/**
* Gets the mode of the given range of virtual addresses if they
* are mapped with the same mode.
@@ -1166,6 +1186,28 @@
return success;
}
+bool mm_get_mode_partial(const struct mm_ptable *ptable, vaddr_t begin,
+ vaddr_t end, mm_mode_t *mode, vaddr_t *end_ret)
+{
+ struct mm_get_attrs_state ret;
+ bool success;
+
+ assert(ptable->stage1);
+
+ ret = mm_get_attrs(ptable, va_addr(begin), va_addr(end));
+ success = ret.got_attrs;
+
+ if (success && mode != NULL) {
+ *mode = arch_mm_stage1_attrs_to_mode(ret.attrs);
+ }
+
+ if (success && end_ret != NULL) {
+ *end_ret = ret.mismatch ? va_init(ret.mismatch) : end;
+ }
+
+ return success;
+}
+
static struct mm_stage1_locked mm_stage1_lock_unsafe(void)
{
return (struct mm_stage1_locked){.ptable = &ptable};
diff --git a/src/mm_test.cc b/src/mm_test.cc
index d060139..22e5dc0 100644
--- a/src/mm_test.cc
+++ b/src/mm_test.cc
@@ -980,6 +980,66 @@
EXPECT_THAT(read_mode, Eq(mode));
}
+TEST_F(mm, get_mode_partial)
+{
+ constexpr mm_mode_t mode0 = MM_MODE_R;
+ constexpr mm_mode_t mode1 = MM_MODE_W;
+ constexpr mm_mode_t mode2 = MM_MODE_X;
+
+ mm_mode_t ret_mode;
+
+ const paddr_t page0_start = pa_init(0);
+ const paddr_t page0_end = pa_init(PAGE_SIZE * 1);
+ const paddr_t page1_start = pa_init(PAGE_SIZE * 1);
+ const paddr_t page1_end = pa_init(PAGE_SIZE * 2);
+ const paddr_t page2_start = pa_init(PAGE_SIZE * 2);
+ const paddr_t page2_end = pa_init(PAGE_SIZE * 3);
+ ipaddr_t end_ret;
+
+ ASSERT_TRUE(mm_vm_identity_map(&ptable, page0_start, page0_end, mode0,
+ &ppool, nullptr));
+ ASSERT_TRUE(mm_vm_identity_map(&ptable, page1_start, page1_end, mode1,
+ &ppool, nullptr));
+ ASSERT_TRUE(mm_vm_identity_map(&ptable, page2_start, page2_end, mode2,
+ &ppool, nullptr));
+
+ EXPECT_TRUE(mm_vm_get_mode(&ptable, ipa_from_pa(page0_start),
+ ipa_from_pa(page0_end), &ret_mode));
+ EXPECT_THAT(ret_mode, Eq(mode0));
+
+ EXPECT_TRUE(mm_vm_get_mode(&ptable, ipa_from_pa(page1_start),
+ ipa_from_pa(page1_end), &ret_mode));
+ EXPECT_THAT(ret_mode, Eq(mode1));
+
+ EXPECT_TRUE(mm_vm_get_mode(&ptable, ipa_from_pa(page2_start),
+ ipa_from_pa(page2_end), &ret_mode));
+ EXPECT_THAT(ret_mode, Eq(mode2));
+
+ EXPECT_FALSE(mm_vm_get_mode(&ptable, ipa_from_pa(page0_start),
+ ipa_from_pa(page2_end), nullptr));
+ EXPECT_FALSE(mm_vm_get_mode(&ptable, ipa_from_pa(page0_start),
+ ipa_from_pa(page1_end), nullptr));
+
+ EXPECT_TRUE(mm_vm_get_mode_partial(&ptable, ipa_from_pa(page0_start),
+ ipa_from_pa(page1_end), &ret_mode,
+ &end_ret));
+ EXPECT_EQ(ipa_addr(end_ret), ipa_addr(ipa_from_pa(page1_start)));
+ EXPECT_EQ(ret_mode, mode0);
+
+ EXPECT_TRUE(mm_vm_get_mode_partial(&ptable, ipa_from_pa(page1_start),
+ ipa_from_pa(page2_end), &ret_mode,
+ &end_ret));
+ EXPECT_EQ(ipa_addr(end_ret), ipa_addr(ipa_from_pa(page2_start)));
+ EXPECT_EQ(ret_mode, mode1);
+
+ EXPECT_TRUE(mm_vm_get_mode_partial(
+ &ptable, ipa_from_pa(page2_start),
+ ipa_from_pa(pa_add(page2_end, 2 * PAGE_SIZE)), &ret_mode,
+ &end_ret));
+ EXPECT_EQ(ipa_addr(end_ret), ipa_addr(ipa_from_pa(page2_end)));
+ EXPECT_EQ(ret_mode, mode2);
+}
+
/**
* Anything out of range fail to retrieve the mode.
*/