Hi again,
ok, to my astonishment this seem to be actually working.
My X server is coming up and seems usable (until my hard disk stops
working after some minutes again :-)).
Here's the patch that's doing the trick for me (the Xen implementation
is mostly copied over from the 2.6.27 SuSE Xen kernel):
Subject: [PATCH] Make io_remap_pfn_range a paravirt operation and have a special
implementation for it in Xen.
---
arch/x86/include/asm/paravirt.h | 12 ++++
arch/x86/include/asm/pgtable_32.h | 4 ++
arch/x86/include/asm/pgtable_64.h | 4 ++
arch/x86/kernel/paravirt.c | 1 +
arch/x86/xen/enlighten.c | 1 +
arch/x86/xen/mmu.c | 103 +++++++++++++++++++++++++++++++++++++
arch/x86/xen/mmu.h | 4 ++
7 files changed, 129 insertions(+), 0 deletions(-)
diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h
index 40795f4..d2a7298 100644
--- a/arch/x86/include/asm/paravirt.h
+++ b/arch/x86/include/asm/paravirt.h
@@ -39,6 +39,7 @@ struct desc_ptr;
struct tss_struct;
struct mm_struct;
struct desc_struct;
+struct vm_area_struct;
/* general info */
struct pv_info {
@@ -326,6 +327,10 @@ struct pv_mmu_ops {
unsigned long phys, pgprot_t flags);
int (*page_is_ram)(unsigned long pfn);
+
+ int (*io_remap_pfn_range)(struct vm_area_struct *vma,
+ unsigned long address, unsigned long mfn,
+ unsigned long size, pgprot_t prot);
};
struct raw_spinlock;
@@ -1402,6 +1407,13 @@ static inline int page_is_ram(unsigned long pfn)
return PVOP_CALL1(int, pv_mmu_ops.page_is_ram, pfn);
}
+static inline int io_remap_pfn_range(struct vm_area_struct *vma,
+ unsigned long vaddr, unsigned long pfn,
+ unsigned long size, pgprot_t prot)
+{
+ return pv_mmu_ops.io_remap_pfn_range(vma, vaddr, pfn, size, prot);
+}
+
void _paravirt_nop(void);
#define paravirt_nop ((void *)_paravirt_nop)
diff --git a/arch/x86/include/asm/pgtable_32.h
b/arch/x86/include/asm/pgtable_32.h
index 56e1560..ab2419b 100644
--- a/arch/x86/include/asm/pgtable_32.h
+++ b/arch/x86/include/asm/pgtable_32.h
@@ -182,7 +182,11 @@ do { \
#define kern_addr_valid(kaddr) (0)
#endif
+#ifdef CONFIG_PARAVIRT
+#include <asm/paravirt.h>
+#else
#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \
remap_pfn_range(vma, vaddr, pfn, size, prot)
+#endif
#endif /* _ASM_X86_PGTABLE_32_H */
diff --git a/arch/x86/include/asm/pgtable_64.h
b/arch/x86/include/asm/pgtable_64.h
index 537081e..293570f 100644
--- a/arch/x86/include/asm/pgtable_64.h
+++ b/arch/x86/include/asm/pgtable_64.h
@@ -272,8 +272,12 @@ extern int direct_gbpages;
extern int kern_addr_valid(unsigned long addr);
extern void cleanup_highmap(void);
+#ifdef CONFIG_PARAVIRT
+#include <asm/paravirt.h>
+#else
#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \
remap_pfn_range(vma, vaddr, pfn, size, prot)
+#endif
#define HAVE_ARCH_UNMAPPED_AREA
#define HAVE_ARCH_UNMAPPED_AREA_TOPDOWN
diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c
index 602edc0..81c2e15 100644
--- a/arch/x86/kernel/paravirt.c
+++ b/arch/x86/kernel/paravirt.c
@@ -453,6 +453,7 @@ struct pv_mmu_ops pv_mmu_ops = {
.set_fixmap = native_set_fixmap,
.page_is_ram = native_page_is_ram,
+ .io_remap_pfn_range = remap_pfn_range
};
EXPORT_SYMBOL_GPL(pv_time_ops);
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index 360f04b..643d00f 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -1401,6 +1401,7 @@ static const struct pv_mmu_ops xen_mmu_ops __initdata = {
.set_fixmap = xen_set_fixmap,
.page_is_ram = xen_page_is_ram,
+ .io_remap_pfn_range = xen_io_remap_pfn_range
};
static void xen_reboot(int reason)
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c
index 72069b1..d0a9348 100644
--- a/arch/x86/xen/mmu.c
+++ b/arch/x86/xen/mmu.c
@@ -1441,6 +1441,109 @@ void xen_destroy_contiguous_region(unsigned long
vstart, unsigned int order)
}
EXPORT_SYMBOL_GPL(xen_destroy_contiguous_region);
+static inline pte_t pfn_pte_ma(unsigned long page_nr, pgprot_t pgprot)
+{
+ pgprotval_t prot = pgprot_val(pgprot);
+
+ if (prot & _PAGE_PRESENT)
+ prot &= __supported_pte_mask;
+ return __pte_ma(((phys_addr_t)page_nr << PAGE_SHIFT) | prot);
+}
+
+static int direct_remap_area_pte_fn(pte_t *pte,
+ struct page *pmd_page,
+ unsigned long address,
+ void *data)
+{
+ struct mmu_update **v = (struct mmu_update **)data;
+
+ BUG_ON(!pte_none(*pte));
+
+ (*v)->ptr = ((u64)pfn_to_mfn(page_to_pfn(pmd_page)) <<
+ PAGE_SHIFT) | ((unsigned long)pte & ~PAGE_MASK);
+ (*v)++;
+
+ return 0;
+}
+
+static int __direct_remap_pfn_range(struct mm_struct *mm,
+ unsigned long address,
+ unsigned long mfn,
+ unsigned long size,
+ pgprot_t prot,
+ domid_t domid)
+{
+ int rc;
+ unsigned long i, start_address;
+ struct mmu_update *u, *v, *w;
+
+ u = v = w = (struct mmu_update
*)__get_free_page(GFP_KERNEL|__GFP_REPEAT);
+ if (u == NULL)
+ return -ENOMEM;
+
+ start_address = address;
+
+ flush_cache_all();
+
+ for (i = 0; i < size; i += PAGE_SIZE) {
+ if ((v - u) == (PAGE_SIZE / sizeof(struct mmu_update))) {
+ /* Flush a full batch after filling in the PTE ptrs. */
+ rc = apply_to_page_range(mm, start_address,
+ address - start_address,
+ direct_remap_area_pte_fn, &w);
+ if (rc)
+ goto out;
+ rc = -EFAULT;
+ if (HYPERVISOR_mmu_update(u, v - u, NULL, domid) < 0)
+ goto out;
+ v = w = u;
+ start_address = address;
+ }
+
+ /*
+ * Fill in the machine address: PTE ptr is done later by
+ * apply_to_page_range().
+ */
+ v->val = pfn_pte_ma(mfn, prot).pte
+ | _PAGE_SPECIAL | _PAGE_IOMAP;
+
+ mfn++;
+ address += PAGE_SIZE;
+ v++;
+ }
+
+ if (v != u) {
+ /* Final batch. */
+ rc = apply_to_page_range(mm, start_address,
+ address - start_address,
+ direct_remap_area_pte_fn, &w);
+ if (rc)
+ goto out;
+ rc = -EFAULT;
+ if (unlikely(HYPERVISOR_mmu_update(u, v - u, NULL, domid) < 0))
+ goto out;
+ }
+
+ rc = 0;
+
+ out:
+ flush_tlb_all();
+
+ free_page((unsigned long)u);
+
+ return rc;
+}
+
+int xen_io_remap_pfn_range(struct vm_area_struct *vma,
+ unsigned long address, unsigned long mfn,
+ unsigned long size, pgprot_t prot)
+{
+ vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP;
+
+ return __direct_remap_pfn_range(
+ vma->vm_mm, address, mfn, size, prot, DOMID_IO);
+}
+
#ifdef CONFIG_XEN_DEBUG_FS
static struct dentry *d_mmu_debug;
diff --git a/arch/x86/xen/mmu.h b/arch/x86/xen/mmu.h
index 98d7165..e1c8321 100644
--- a/arch/x86/xen/mmu.h
+++ b/arch/x86/xen/mmu.h
@@ -54,4 +54,8 @@ pte_t xen_ptep_modify_prot_start(struct mm_struct *mm,
unsigned long addr, pte_t
void xen_ptep_modify_prot_commit(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t pte);
+int xen_io_remap_pfn_range(struct vm_area_struct *vma,
+ unsigned long address, unsigned long mfn,
+ unsigned long size, pgprot_t prot);
+
#endif /* _XEN_MMU_H */
--
1.6.1
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|