@@ -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);
@@ -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);
@@ -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;
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(-)