libipt, image: validate an image section

Validate that a given section would be returned by an image lookup.

This allows image users who want to cache image sections provided by
pt_image_find() to check if their cached section would still be returned by
another call to pt_image_find().

For performance reasons we allow sporadic validation fails.  They will require
the user to re-fetch the cached section with another call to pt_image_find(),
which causes a small loss of performance but is otherwise harmless.

Change-Id: I674e08969c2a29dc41e289b0faec1f5d09f27175
Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
diff --git a/libipt/internal/include/pt_image.h b/libipt/internal/include/pt_image.h
index 22c659b..9812fa6 100644
--- a/libipt/internal/include/pt_image.h
+++ b/libipt/internal/include/pt_image.h
@@ -132,4 +132,21 @@
 			 uint64_t *laddr, const struct pt_asid *asid,
 			 uint64_t vaddr);
 
+/* Validate an image section.
+ *
+ * Validate that a lookup by @asid and @vaddr in @image would result in @section
+ * loaded at @laddr identified by @isid.
+ *
+ * Validation may fail sporadically, e.g. if @section has been evicted from
+ * @image's LRU cache.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ * Returns -pte_invalid if @image or @asid is NULL.
+ * Returns -pte_nomap if validation failed.
+ */
+extern int pt_image_validate(const struct pt_image *image,
+			     const struct pt_asid *asid, uint64_t vaddr,
+			     const struct pt_section *section, uint64_t laddr,
+			     int isid);
+
 #endif /* PT_IMAGE_H */
diff --git a/libipt/src/pt_image.c b/libipt/src/pt_image.c
index 25faa6e..d2d406e 100644
--- a/libipt/src/pt_image.c
+++ b/libipt/src/pt_image.c
@@ -937,3 +937,38 @@
 
 	return slist->isid;
 }
+
+int pt_image_validate(const struct pt_image *image, const struct pt_asid *asid,
+		      uint64_t vaddr, const struct pt_section *section,
+		      uint64_t laddr, int isid)
+{
+	struct pt_mapped_section *msec;
+	struct pt_section_list *slist;
+
+	if (!image)
+		return -pte_internal;
+
+	/* We only look at the top of our LRU stack and accept sporadic
+	 * validation fails if @section moved down in the LRU stack or has been
+	 * evicted.
+	 *
+	 * A failed validation requires decoders to re-fetch the section so it
+	 * only results in a (relatively small) performance loss.
+	 */
+	slist = image->sections;
+	if (!slist)
+		return -pte_nomap;
+
+	if (slist->isid != isid)
+		return -pte_nomap;
+
+	msec = &slist->section;
+
+	if (pt_msec_section(msec) != section)
+		return -pte_nomap;
+
+	if (pt_msec_begin(msec) != laddr)
+		return -pte_nomap;
+
+	return pt_image_check_msec(msec, asid, vaddr);
+}
diff --git a/libipt/test/src/ptunit-image.c b/libipt/test/src/ptunit-image.c
index a962289..137f0f7 100644
--- a/libipt/test/src/ptunit-image.c
+++ b/libipt/test/src/ptunit-image.c
@@ -2001,6 +2001,71 @@
 	return ptu_passed();
 }
 
+static struct ptunit_result validate_null(struct image_fixture *ifix)
+{
+	int status;
+
+	status = pt_image_validate(NULL, &ifix->asid[0], 0x1004ull,
+				   &ifix->section[0], 0x1000ull, 10);
+	ptu_int_eq(status, -pte_internal);
+
+	status = pt_image_validate(&ifix->image, NULL, 0x1004ull,
+				   &ifix->section[0], 0x1000ull, 10);
+	ptu_int_eq(status, -pte_internal);
+
+	return ptu_passed();
+}
+
+static struct ptunit_result validate(struct image_fixture *ifix)
+{
+	int status;
+
+	/* This test depends on the order in which sections are stored when
+	 * added to the image.
+	 *
+	 * Since pt_image_validate() only looks at the top of the LRU stack we
+	 * can only validate that section - i.e. the one that was added first.
+	 */
+	status = pt_image_validate(&ifix->image, &ifix->asid[0], 0x1004ull,
+				   &ifix->section[0], 0x1000ull, 10);
+	ptu_int_eq(status, 0);
+
+	return ptu_passed();
+}
+
+static struct ptunit_result validate_bad_asid(struct image_fixture *ifix)
+{
+	int status;
+
+	status = pt_image_validate(&ifix->image, &ifix->asid[1], 0x1004ull,
+				   &ifix->section[0], 0x1000ull, 10);
+	ptu_int_eq(status, -pte_nomap);
+
+	return ptu_passed();
+}
+
+static struct ptunit_result validate_bad_laddr(struct image_fixture *ifix)
+{
+	int status;
+
+	status = pt_image_validate(&ifix->image, &ifix->asid[0], 0x1004ull,
+				   &ifix->section[0], 0x2000ull, 10);
+	ptu_int_eq(status, -pte_nomap);
+
+	return ptu_passed();
+}
+
+static struct ptunit_result validate_bad_isid(struct image_fixture *ifix)
+{
+	int status;
+
+	status = pt_image_validate(&ifix->image, &ifix->asid[0], 0x1004ull,
+				   &ifix->section[0], 0x1000ull, 11);
+	ptu_int_eq(status, -pte_nomap);
+
+	return ptu_passed();
+}
+
 struct ptunit_result ifix_init(struct image_fixture *ifix)
 {
 	int index;
@@ -2163,6 +2228,12 @@
 	ptu_run_f(suite, find_bad_asid, rfix);
 	ptu_run_f(suite, find_nomem, rfix);
 
+	ptu_run_f(suite, validate_null, rfix);
+	ptu_run_f(suite, validate, rfix);
+	ptu_run_f(suite, validate_bad_asid, rfix);
+	ptu_run_f(suite, validate_bad_laddr, rfix);
+	ptu_run_f(suite, validate_bad_isid, rfix);
+
 	ptunit_report(&suite);
 	return suite.nr_fails;
 }