aboutsummaryrefslogtreecommitdiffstats
path: root/xen-2.4.16/common/memory.c
diff options
context:
space:
mode:
Diffstat (limited to 'xen-2.4.16/common/memory.c')
-rw-r--r--xen-2.4.16/common/memory.c250
1 files changed, 168 insertions, 82 deletions
diff --git a/xen-2.4.16/common/memory.c b/xen-2.4.16/common/memory.c
index 5227ecf05a..4cfb4348f9 100644
--- a/xen-2.4.16/common/memory.c
+++ b/xen-2.4.16/common/memory.c
@@ -7,8 +7,7 @@
*
* Domains trap to process_page_updates with a list of update requests.
* This is a list of (ptr, val) pairs, where the requested operation
- * is *ptr = val. The exceptions are when ptr is PGREQ_ADD_BASEPTR, or
- * PGREQ_REMOVE_BASEPTR.
+ * is *ptr = val.
*
* Reference counting of pages:
* ----------------------------
@@ -28,6 +27,15 @@
* referred to in its current incarnation. Therefore, a page can only
* change its type when its type count is zero.
*
+ * Pinning the page type:
+ * ----------------------
+ * The type of a page can be pinned/unpinned with the commands
+ * PGEXT_[UN]PIN_L?_TABLE. Each page can be pinned exactly once (that is,
+ * pinning is not reference counted, so it can't be nested).
+ * This is useful to prevent a page's type count falling to zero, at which
+ * point safety checks would need to be carried out next time the count
+ * is increased again.
+ *
* A further note on writeable page mappings:
* ------------------------------------------
* For simplicity, the count of writeable mappings for a page may not
@@ -194,6 +202,7 @@ unsigned long max_page;
struct list_head free_list;
unsigned int free_pfns;
+static int tlb_flush[NR_CPUS];
/*
* init_frametable:
@@ -208,6 +217,8 @@ unsigned long __init init_frametable(unsigned long nr_pages)
struct pfn_info *pf;
unsigned long page_index;
+ memset(tlb_flush, 0, sizeof(tlb_flush));
+
max_page = nr_pages;
frame_table_size = nr_pages * sizeof(struct pfn_info);
frame_table_size = (frame_table_size + PAGE_SIZE - 1) & PAGE_MASK;
@@ -440,13 +451,14 @@ static void put_page(unsigned long page_nr, int writeable)
ASSERT(page_nr < max_page);
page = frame_table + page_nr;
ASSERT((page->flags & PG_domain_mask) == current->domain);
- ASSERT((((page->flags & PG_type_mask) == PGT_writeable_page) &&
- (page_type_count(page) != 0)) ||
- (((page->flags & PG_type_mask) == PGT_none) &&
- (page_type_count(page) == 0)));
- ASSERT((!writeable) || (page_type_count(page) != 0));
+ ASSERT((!writeable) ||
+ ((page_type_count(page) != 0) &&
+ ((page->flags & PG_type_mask) == PGT_writeable_page)));
if ( writeable && (put_page_type(page) == 0) )
+ {
+ tlb_flush[smp_processor_id()] = 1;
page->flags &= ~PG_type_mask;
+ }
put_page_tot(page);
}
@@ -458,7 +470,7 @@ static int mod_l2_entry(l2_pgentry_t *p_l2_entry, l2_pgentry_t new_l2_entry)
if ( (((unsigned long)p_l2_entry & (PAGE_SIZE-1)) >> 2) >=
DOMAIN_ENTRIES_PER_L2_PAGETABLE )
{
- MEM_LOG("Illegal L2 update attempt in hypervisor area %p\n",
+ MEM_LOG("Illegal L2 update attempt in hypervisor area %p",
p_l2_entry);
goto fail;
}
@@ -544,6 +556,95 @@ static int mod_l1_entry(l1_pgentry_t *p_l1_entry, l1_pgentry_t new_l1_entry)
}
+static int do_extended_command(unsigned long ptr, unsigned long val)
+{
+ int err = 0;
+ unsigned long pfn = ptr >> PAGE_SHIFT;
+ struct pfn_info *page = frame_table + pfn;
+
+ switch ( (val & PGEXT_CMD_MASK) )
+ {
+ case PGEXT_PIN_L1_TABLE:
+ err = get_l1_table(pfn);
+ goto mark_as_pinned;
+ case PGEXT_PIN_L2_TABLE:
+ err = get_l2_table(pfn);
+ mark_as_pinned:
+ if ( err )
+ {
+ MEM_LOG("Error while pinning pfn %08lx", pfn);
+ break;
+ }
+ put_page_type(page);
+ put_page_tot(page);
+ if ( !(page->type_count & REFCNT_PIN_BIT) )
+ {
+ page->type_count |= REFCNT_PIN_BIT;
+ page->tot_count |= REFCNT_PIN_BIT;
+ }
+ else
+ {
+ MEM_LOG("Pfn %08lx already pinned", pfn);
+ err = 1;
+ }
+ break;
+
+ case PGEXT_UNPIN_TABLE:
+ if ( (page->flags & PG_domain_mask) != current->domain )
+ {
+ err = 1;
+ MEM_LOG("Page %08lx bad domain (dom=%ld)",
+ ptr, page->flags & PG_domain_mask);
+ }
+ else if ( (page->type_count & REFCNT_PIN_BIT) )
+ {
+ page->type_count &= ~REFCNT_PIN_BIT;
+ page->tot_count &= ~REFCNT_PIN_BIT;
+ get_page_type(page);
+ get_page_tot(page);
+ ((page->flags & PG_type_mask) == PGT_l1_page_table) ?
+ put_l1_table(pfn) : put_l2_table(pfn);
+ }
+ else
+ {
+ err = 1;
+ MEM_LOG("Pfn %08lx not pinned", pfn);
+ }
+ break;
+
+ case PGEXT_NEW_BASEPTR:
+ err = get_l2_table(pfn);
+ if ( !err )
+ {
+ put_l2_table(__pa(pagetable_ptr(current->mm.pagetable))
+ >> PAGE_SHIFT);
+ current->mm.pagetable =
+ mk_pagetable((unsigned long)__va(pfn<<PAGE_SHIFT));
+ }
+ else
+ {
+ MEM_LOG("Error while installing new baseptr %08lx %d", ptr, err);
+ }
+ /* fall through */
+
+ case PGEXT_TLB_FLUSH:
+ tlb_flush[smp_processor_id()] = 1;
+ break;
+
+ case PGEXT_INVLPG:
+ __asm__ __volatile__ ("invlpg %0" : :
+ "m" (*(char*)(val & ~PGEXT_CMD_MASK)));
+ break;
+
+ default:
+ MEM_LOG("Invalid extended pt command 0x%08lx", val & PGEXT_CMD_MASK);
+ err = 1;
+ break;
+ }
+
+ return err;
+}
+
/* Apply updates to page table @pagetable_id within the current domain. */
int do_process_page_updates(page_update_request_t *updates, int count)
{
@@ -559,39 +660,23 @@ int do_process_page_updates(page_update_request_t *updates, int count)
kill_domain_with_errmsg("Cannot read page update request");
}
- err = 1;
-
pfn = cur.ptr >> PAGE_SHIFT;
- if ( !pfn )
- {
- switch ( cur.ptr )
- {
- case PGREQ_ADD_BASEPTR:
- err = get_l2_table(cur.val >> PAGE_SHIFT);
- break;
- case PGREQ_REMOVE_BASEPTR:
- if ( cur.val == __pa(pagetable_ptr(current->mm.pagetable)) )
- {
- MEM_LOG("Attempt to remove current baseptr! %08lx",
- cur.val);
- }
- else
- {
- err = put_l2_table(cur.val >> PAGE_SHIFT);
- }
- break;
- default:
- MEM_LOG("Invalid page update command %08lx", cur.ptr);
- break;
- }
- }
- else if ( (cur.ptr & (sizeof(l1_pgentry_t)-1)) || (pfn >= max_page) )
+ if ( pfn >= max_page )
{
- MEM_LOG("Page out of range (%08lx>%08lx) or misalign %08lx",
- pfn, max_page, cur.ptr);
+ MEM_LOG("Page out of range (%08lx > %08lx)", pfn, max_page);
+ kill_domain_with_errmsg("Page update request out of range");
}
- else
+
+ err = 1;
+
+ /* Least significant bits of 'ptr' demux the operation type. */
+ switch ( cur.ptr & (sizeof(l1_pgentry_t)-1) )
{
+
+ /*
+ * PGREQ_NORMAL: Normal update to any level of page table.
+ */
+ case PGREQ_NORMAL:
page = frame_table + pfn;
flags = page->flags;
if ( (flags & PG_domain_mask) == current->domain )
@@ -607,20 +692,47 @@ int do_process_page_updates(page_update_request_t *updates, int count)
mk_l2_pgentry(cur.val));
break;
default:
- /*
- * This might occur if a page-table update is
- * requested before we've inferred the type
- * of the containing page. It shouldn't happen
- * if page tables are built strictly top-down, so
- * we have a MEM_LOG warning message.
- */
- MEM_LOG("Unnecessary update to non-pt page %08lx",
- cur.ptr);
- *(unsigned long *)__va(cur.ptr) = cur.val;
- err = 0;
+ MEM_LOG("Update to non-pt page %08lx", cur.ptr);
break;
}
}
+ break;
+
+ /*
+ * PGREQ_UNCHECKED_UPDATE: Make an unchecked update to a
+ * bottom-level page-table entry.
+ * Restrictions apply:
+ * 1. Update only allowed by domain 0.
+ * 2. Update must be to a level-1 pte belonging to dom0.
+ */
+ case PGREQ_UNCHECKED_UPDATE:
+ cur.ptr &= ~(sizeof(l1_pgentry_t) - 1);
+ page = frame_table + pfn;
+ flags = page->flags;
+ if ( (flags | current->domain) == PGT_l1_page_table )
+ {
+ *(unsigned long *)__va(cur.ptr) = cur.val;
+ err = 0;
+ }
+ else
+ {
+ MEM_LOG("UNCHECKED_UPDATE: Bad domain %d, or"
+ " bad pte type %08lx", current->domain, flags);
+ }
+ break;
+
+ /*
+ * PGREQ_EXTENDED_COMMAND: Extended command is specified
+ * in the least-siginificant bits of the 'value' field.
+ */
+ case PGREQ_EXTENDED_COMMAND:
+ cur.ptr &= ~(sizeof(l1_pgentry_t) - 1);
+ err = do_extended_command(cur.ptr, cur.val);
+ break;
+
+ default:
+ MEM_LOG("Invalid page update command %08lx", cur.ptr);
+ break;
}
if ( err )
@@ -631,40 +743,14 @@ int do_process_page_updates(page_update_request_t *updates, int count)
updates++;
}
- __asm__ __volatile__ ("movl %%eax,%%cr3" : :
- "a" (__pa(pagetable_ptr(current->mm.pagetable))));
- return(0);
-}
-
-
-int do_set_pagetable(unsigned long ptr)
-{
- struct pfn_info *page;
- unsigned long pfn, flags;
-
- if ( (ptr & ~PAGE_MASK) )
- {
- MEM_LOG("Misaligned new baseptr %08lx", ptr);
- return -1;
- }
- pfn = ptr >> PAGE_SHIFT;
- if ( pfn >= max_page )
+ if ( tlb_flush[smp_processor_id()] )
{
- MEM_LOG("Page out of range (%08lx>%08lx)", pfn, max_page);
- return -1;
+ tlb_flush[smp_processor_id()] = 0;
+ __asm__ __volatile__ (
+ "movl %%eax,%%cr3" : :
+ "a" (__pa(pagetable_ptr(current->mm.pagetable))));
}
- page = frame_table + (ptr >> PAGE_SHIFT);
- flags = page->flags;
- if ( (flags & (PG_domain_mask|PG_type_mask)) !=
- (current->domain|PGT_l2_page_table) )
- {
- MEM_LOG("Page %08lx bad type/domain (dom=%ld) "
- "(type %08lx != expected %08x)",
- ptr, flags & PG_domain_mask, flags & PG_type_mask,
- PGT_l2_page_table);
- return -1;
- }
- current->mm.pagetable = mk_pagetable((unsigned long)__va(ptr));
- __asm__ __volatile__ ("movl %%eax,%%cr3" : : "a" (ptr));
- return 0;
+
+ return(0);
}
+