#define PAGE_CACHE_ATTRS (_PAGE_PAT|_PAGE_PCD|_PAGE_PWT)
+int opt_allow_hugepage;
+boolean_param("allowhugepage", opt_allow_hugepage);
+
#define l1_disallow_mask(d) \
((d != dom_io) && \
(rangeset_is_empty((d)->iomem_caps) && \
return rc;
}
+static int get_data_page(
+ struct page_info *page, struct domain *d, int writeable)
+{
+ int rc;
+
+ if ( writeable )
+ rc = get_page_and_type(page, d, PGT_writable_page);
+ else
+ rc = get_page(page, d);
+
+ return rc;
+}
+
+static void put_data_page(
+ struct page_info *page, int writeable)
+{
+ if ( writeable )
+ put_page_and_type(page);
+ else
+ put_page(page);
+}
+
/*
* We allow root tables to map each other (a.k.a. linear page tables). It
* needs some special care with reference counts and access permissions:
* contribute to writeable mapping refcounts. (This allows the
* qemu-dm helper process in dom0 to map the domain's memory without
* messing up the count of "real" writable mappings.) */
- okay = (((l1f & _PAGE_RW) &&
- !(unlikely(paging_mode_external(d) && (d != curr->domain))))
- ? get_page_and_type(page, d, PGT_writable_page)
- : get_page(page, d));
+ okay = get_data_page(
+ page, d,
+ (l1f & _PAGE_RW) && !(paging_mode_external(d) && (d != curr->domain)));
if ( !okay )
{
MEM_LOG("Error getting mfn %lx (pfn %lx) from L1 entry %" PRIpte
get_page_from_l2e(
l2_pgentry_t l2e, unsigned long pfn, struct domain *d)
{
+ unsigned long mfn = l2e_get_pfn(l2e);
int rc;
if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) )
return -EINVAL;
}
- rc = get_page_and_type_from_pagenr(
- l2e_get_pfn(l2e), PGT_l1_page_table, d, 0, 0);
- if ( unlikely(rc == -EINVAL) && get_l2_linear_pagetable(l2e, pfn, d) )
- rc = 0;
+ if ( !(l2e_get_flags(l2e) & _PAGE_PSE) )
+ {
+ rc = get_page_and_type_from_pagenr(mfn, PGT_l1_page_table, d, 0, 0);
+ if ( unlikely(rc == -EINVAL) && get_l2_linear_pagetable(l2e, pfn, d) )
+ rc = 0;
+ }
+ else if ( !opt_allow_hugepage || (mfn & (L1_PAGETABLE_ENTRIES-1)) )
+ {
+ rc = -EINVAL;
+ }
+ else
+ {
+ unsigned long m = mfn;
+ int writeable = !!(l2e_get_flags(l2e) & _PAGE_RW);
+
+ do {
+ rc = get_data_page(mfn_to_page(m), d, writeable);
+ if ( unlikely(!rc) )
+ {
+ while ( m-- > mfn )
+ put_data_page(mfn_to_page(m), writeable);
+ return -EINVAL;
+ }
+ } while ( m++ < (mfn + (L1_PAGETABLE_ENTRIES-1)) );
+
+#ifdef __x86_64__
+ map_pages_to_xen(
+ (unsigned long)mfn_to_virt(mfn), mfn, L1_PAGETABLE_ENTRIES,
+ PAGE_HYPERVISOR | l2e_get_flags(l2e));
+#endif
+ }
return rc;
}
*/
static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn)
{
- if ( (l2e_get_flags(l2e) & _PAGE_PRESENT) &&
- (l2e_get_pfn(l2e) != pfn) )
+ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || (l2e_get_pfn(l2e) == pfn) )
+ return 1;
+
+ if ( l2e_get_flags(l2e) & _PAGE_PSE )
+ {
+ unsigned long mfn = l2e_get_pfn(l2e), m = mfn;
+ int writeable = l2e_get_flags(l2e) & _PAGE_RW;
+ ASSERT(opt_allow_hugepage && !(mfn & (L1_PAGETABLE_ENTRIES-1)));
+ do {
+ put_data_page(mfn_to_page(m), writeable);
+ } while ( m++ < (mfn + (L1_PAGETABLE_ENTRIES-1)) );
+ }
+ else
{
put_page_and_type(l2e_get_page(l2e));
- return 0;
}
- return 1;
+
+ return 0;
}
static int __put_page_type(struct page_info *, int preemptible);
{
/* Modify Feature Information. */
__clear_bit(X86_FEATURE_VME, &d);
- __clear_bit(X86_FEATURE_PSE, &d);
+ if ( !opt_allow_hugepage )
+ __clear_bit(X86_FEATURE_PSE, &d);
__clear_bit(X86_FEATURE_PGE, &d);
__clear_bit(X86_FEATURE_MCE, &d);
__clear_bit(X86_FEATURE_MCA, &d);
case 4: /* Read CR4 */
/*
* Guests can read CR4 to see what features Xen has enabled. We
- * therefore lie about PGE & PSE as they are unavailable to guests.
+ * therefore lie about PGE as it is unavailable to guests.
+ * Also disallow PSE if hugepages are not enabled.
*/
- *reg = read_cr4() & ~(X86_CR4_PGE|X86_CR4_PSE);
+ *reg = read_cr4() & ~X86_CR4_PGE;
+ if ( !opt_allow_hugepage )
+ *reg &= ~X86_CR4_PSE;
break;
default: