[RFC,v2,10/26] mm/asi: Keep track of VA ranges mapped in ASI page-table
diff mbox series

Message ID 1562855138-19507-11-git-send-email-alexandre.chartre@oracle.com
State New
Headers show
Series
  • Kernel Address Space Isolation
Related show

Commit Message

Alexandre Chartre July 11, 2019, 2:25 p.m. UTC
Add functions to keep track of VA ranges mapped in an ASI page-table.
This will be used when unmapping to ensure the same range is unmapped,
at the same page-table level. This is also be used to handle mapping
and unmapping of overlapping VA ranges.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 arch/x86/include/asm/asi.h  |    3 ++
 arch/x86/mm/asi.c           |    3 ++
 arch/x86/mm/asi_pagetable.c |   71 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 77 insertions(+), 0 deletions(-)

Patch
diff mbox series

diff --git a/arch/x86/include/asm/asi.h b/arch/x86/include/asm/asi.h
index b5dbc49..be1c190 100644
--- a/arch/x86/include/asm/asi.h
+++ b/arch/x86/include/asm/asi.h
@@ -24,6 +24,7 @@  enum page_table_level {
 struct asi {
 	spinlock_t		lock;		/* protect all attributes */
 	pgd_t			*pgd;		/* ASI page-table */
+	struct list_head	mapping_list;	/* list of VA range mapping */
 
 	/*
 	 * An ASI page-table can have direct references to the full kernel
@@ -69,6 +70,8 @@  struct asi_session {
 
 void asi_init_backend(struct asi *asi);
 void asi_fini_backend(struct asi *asi);
+void asi_init_range_mapping(struct asi *asi);
+void asi_fini_range_mapping(struct asi *asi);
 
 extern struct asi *asi_create(void);
 extern void asi_destroy(struct asi *asi);
diff --git a/arch/x86/mm/asi.c b/arch/x86/mm/asi.c
index dfde245..25633a6 100644
--- a/arch/x86/mm/asi.c
+++ b/arch/x86/mm/asi.c
@@ -104,6 +104,8 @@  struct asi *asi_create(void)
 	if (!asi)
 		return NULL;
 
+	asi_init_range_mapping(asi);
+
 	page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
 	if (!page)
 		goto error;
@@ -133,6 +135,7 @@  void asi_destroy(struct asi *asi)
 	if (asi->pgd)
 		free_page((unsigned long)asi->pgd);
 
+	asi_fini_range_mapping(asi);
 	asi_fini_backend(asi);
 
 	kfree(asi);
diff --git a/arch/x86/mm/asi_pagetable.c b/arch/x86/mm/asi_pagetable.c
index 0169395..a09a22d 100644
--- a/arch/x86/mm/asi_pagetable.c
+++ b/arch/x86/mm/asi_pagetable.c
@@ -5,10 +5,21 @@ 
  */
 
 #include <linux/mm.h>
+#include <linux/slab.h>
 
 #include <asm/asi.h>
 
 /*
+ * Structure to keep track of address ranges mapped into an ASI.
+ */
+struct asi_range_mapping {
+	struct list_head list;
+	void *ptr;			/* range start address */
+	size_t size;			/* range size */
+	enum page_table_level level;	/* mapping level */
+};
+
+/*
  * Get the pointer to the beginning of a page table directory from a page
  * table directory entry.
  */
@@ -75,6 +86,39 @@  void asi_fini_backend(struct asi *asi)
 	}
 }
 
+void asi_init_range_mapping(struct asi *asi)
+{
+	INIT_LIST_HEAD(&asi->mapping_list);
+}
+
+void asi_fini_range_mapping(struct asi *asi)
+{
+	struct asi_range_mapping *range, *range_next;
+
+	list_for_each_entry_safe(range, range_next, &asi->mapping_list, list) {
+		list_del(&range->list);
+		kfree(range);
+	}
+}
+
+/*
+ * Return the range mapping starting at the specified address, or NULL if
+ * no such range is found.
+ */
+static struct asi_range_mapping *asi_get_range_mapping(struct asi *asi,
+						       void *ptr)
+{
+	struct asi_range_mapping *range;
+
+	lockdep_assert_held(&asi->lock);
+	list_for_each_entry(range, &asi->mapping_list, list) {
+		if (range->ptr == ptr)
+			return range;
+	}
+
+	return NULL;
+}
+
 /*
  * Check if an offset in the address space isolation page-table is valid,
  * i.e. check that the offset is on a page effectively belonging to the
@@ -574,6 +618,7 @@  static int asi_copy_pgd_range(struct asi *asi,
 int asi_map_range(struct asi *asi, void *ptr, size_t size,
 		  enum page_table_level level)
 {
+	struct asi_range_mapping *range_mapping;
 	unsigned long addr = (unsigned long)ptr;
 	unsigned long end = addr + ((unsigned long)size);
 	unsigned long flags;
@@ -582,8 +627,34 @@  int asi_map_range(struct asi *asi, void *ptr, size_t size,
 	pr_debug("ASI %p: MAP %px/%lx/%d\n", asi, ptr, size, level);
 
 	spin_lock_irqsave(&asi->lock, flags);
+
+	/* check if the range is already mapped */
+	range_mapping = asi_get_range_mapping(asi, ptr);
+	if (range_mapping) {
+		pr_debug("ASI %p: MAP %px/%lx/%d already mapped\n",
+			 asi, ptr, size, level);
+		err = -EBUSY;
+		goto done;
+	}
+
+	/* map new range */
+	range_mapping = kmalloc(sizeof(*range_mapping), GFP_KERNEL);
+	if (!range_mapping) {
+		err = -ENOMEM;
+		goto done;
+	}
+
 	err = asi_copy_pgd_range(asi, asi->pgd, current->mm->pgd,
 				 addr, end, level);
+	if (err)
+		goto done;
+
+	INIT_LIST_HEAD(&range_mapping->list);
+	range_mapping->ptr = ptr;
+	range_mapping->size = size;
+	range_mapping->level = level;
+	list_add(&range_mapping->list, &asi->mapping_list);
+done:
 	spin_unlock_irqrestore(&asi->lock, flags);
 
 	return err;