diff options
Diffstat (limited to 'xen-2.4.16/common/memory.c')
-rw-r--r-- | xen-2.4.16/common/memory.c | 250 |
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); } + |