From 36f5e51932b871ec40f8a6c986eaaaee87562b25 Mon Sep 17 00:00:00 2001 From: Stephen Tweedie Date: Fri, 7 Dec 2007 15:27:21 +0000 Subject: [PATCH] xen dom0: Add paravirt_ops ioremap_page_range() for Xen. Adds a new pv_mmu_ops field for ioremap_page_range, and the necessary pv-ops plumbing to connect to lib/ioremap.c's default implementation on baremetal while still getting xen's hypercall-based implementation when necessary. Signed-off-by: Stephen Tweedie --- arch/x86/kernel/paravirt_32.c | 1 + arch/x86/mm/pgtable_32.c | 7 ++ arch/x86/xen/Makefile | 2 +- arch/x86/xen/enlighten.c | 1 + arch/x86/xen/ioremap.c | 147 +++++++++++++++++++++++++++++++++++++++++ arch/x86/xen/mmu.h | 3 + include/asm-x86/fixmap_32.h | 2 + include/asm-x86/paravirt.h | 10 +++ include/linux/io.h | 12 ++- lib/ioremap.c | 4 +- 10 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 arch/x86/xen/ioremap.c diff --git a/arch/x86/kernel/paravirt_32.c b/arch/x86/kernel/paravirt_32.c index 9fc62c2..e578f14 100644 --- a/arch/x86/kernel/paravirt_32.c +++ b/arch/x86/kernel/paravirt_32.c @@ -464,6 +464,7 @@ struct pv_mmu_ops pv_mmu_ops = { }, .set_fixmap = native_set_fixmap, + .ioremap_page_range = native_ioremap_page_range, }; EXPORT_SYMBOL_GPL(pv_time_ops); diff --git a/arch/x86/mm/pgtable_32.c b/arch/x86/mm/pgtable_32.c index 8892aad..d1ca199 100644 --- a/arch/x86/mm/pgtable_32.c +++ b/arch/x86/mm/pgtable_32.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -167,6 +168,12 @@ void native_set_fixmap(enum fixed_addresses idx, unsigned long phys, pgprot_t fl __native_set_fixmap(idx, pfn_pte(phys >> PAGE_SHIFT, flags)); } +int native_ioremap_page_range(unsigned long addr, unsigned long end, + unsigned long phys_addr, pgprot_t prot) +{ + return __ioremap_page_range(addr, end, phys_addr, prot); +} + /** * reserve_top_address - reserves a hole in the top of kernel address space * @reserve - size of hole to reserve diff --git a/arch/x86/xen/Makefile b/arch/x86/xen/Makefile index 343df24..7ef9e01 100644 --- a/arch/x86/xen/Makefile +++ b/arch/x86/xen/Makefile @@ -1,4 +1,4 @@ obj-y := enlighten.o setup.o features.o multicalls.o mmu.o \ - events.o time.o manage.o xen-asm.o + events.o time.o manage.o xen-asm.o ioremap.o obj-$(CONFIG_SMP) += smp.o diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index a680a49..fe2b6ee 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -1134,6 +1134,7 @@ static const struct pv_mmu_ops xen_mmu_ops __initdata = { }, .set_fixmap = xen_set_fixmap, + .ioremap_page_range = xen_ioremap_page_range, }; #ifdef CONFIG_SMP diff --git a/arch/x86/xen/ioremap.c b/arch/x86/xen/ioremap.c new file mode 100644 index 0000000..4cbbca2 --- /dev/null +++ b/arch/x86/xen/ioremap.c @@ -0,0 +1,147 @@ +/* + * arch/x86/xen/ioremap.c + * + * Xen-specific paravirt-ops routines to support re-mapping IO memory + * to kernel address space so that we can access it within a Xen privileged + * domain. + * + * (C) Copyright 1995 1996 Linus Torvalds + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +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 = pte_val_ma(mfn_pte(mfn, prot)); + + 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; +} + +/* + * Does @address reside within a non-highmem page that is local to this virtual + * machine (i.e., not an I/O page, nor a memory page belonging to another VM). + * See the comment that accompanies mfn_to_local_pfn() in page.h to understand + * why this works. + */ +static inline int is_local_lowmem(unsigned long address) +{ + extern unsigned long max_low_pfn; + return (mfn_to_local_pfn(address >> PAGE_SHIFT) < max_low_pfn); +} + +/* + * Remap an arbitrary physical address space into the kernel virtual + * address space. Needed when the kernel wants to access high addresses + * directly. + * + * NOTE! We need to allow non-page-aligned mappings too: we will obviously + * have to convert them into an offset in a page-aligned mapping, but the + * caller shouldn't need to know that small detail. + */ +int xen_ioremap_page_range(unsigned long addr, unsigned long end, + unsigned long phys_addr, pgprot_t prot) +{ + domid_t domid = DOMID_IO; + int rc; + + if (is_local_lowmem(phys_addr)) + domid = DOMID_SELF; + + rc = __direct_remap_pfn_range(&init_mm, (unsigned long)addr, + phys_addr>>PAGE_SHIFT, + end-addr, prot, domid); + + if (rc) + vunmap((void __force *) addr); + return rc; +} + diff --git a/arch/x86/xen/mmu.h b/arch/x86/xen/mmu.h index c9ff27f..8594c92 100644 --- a/arch/x86/xen/mmu.h +++ b/arch/x86/xen/mmu.h @@ -57,4 +57,7 @@ pmd_t xen_make_pmd(unsigned long); pgd_t xen_make_pgd(unsigned long); #endif +extern int xen_ioremap_page_range(unsigned long addr, unsigned long end, + unsigned long phys_addr, pgprot_t prot); + #endif /* _XEN_MMU_H */ diff --git a/include/asm-x86/fixmap_32.h b/include/asm-x86/fixmap_32.h index 9ed0e25..4517c72 100644 --- a/include/asm-x86/fixmap_32.h +++ b/include/asm-x86/fixmap_32.h @@ -101,6 +101,8 @@ enum fixed_addresses { void __native_set_fixmap(enum fixed_addresses idx, pte_t pte); void native_set_fixmap(enum fixed_addresses idx, unsigned long phys, pgprot_t flags); +int native_ioremap_page_range(unsigned long addr, unsigned long end, + unsigned long phys_addr, pgprot_t prot); #ifndef CONFIG_PARAVIRT static inline void __set_fixmap(enum fixed_addresses idx, diff --git a/include/asm-x86/paravirt.h b/include/asm-x86/paravirt.h index 3b9d5b7..fd17cf4 100644 --- a/include/asm-x86/paravirt.h +++ b/include/asm-x86/paravirt.h @@ -247,6 +247,9 @@ struct pv_mmu_ops { an mfn. We can tell which is which from the index. */ void (*set_fixmap)(unsigned /* enum fixed_addresses */ idx, unsigned long phys, pgprot_t flags); + int (*ioremap_page_range)(unsigned long addr, unsigned long end, + unsigned long phys_addr, pgprot_t prot); + }; /* This contains all the paravirt structures: we get a convenient @@ -1013,6 +1016,13 @@ static inline void __set_fixmap(unsigned /* enum fixed_addresses */ idx, pv_mmu_ops.set_fixmap(idx, phys, flags); } +#define __HAVE_ARCH_IOREMAP_PAGE_RANGE +static inline int ioremap_page_range(unsigned long addr, unsigned long end, + unsigned long phys_addr, pgprot_t prot) +{ + return pv_mmu_ops.ioremap_page_range(addr, end, phys_addr, prot); +} + void _paravirt_nop(void); #define paravirt_nop ((void *)_paravirt_nop) diff --git a/include/linux/io.h b/include/linux/io.h index e3b2dda..b8abe3d 100644 --- a/include/linux/io.h +++ b/include/linux/io.h @@ -28,16 +28,20 @@ void __iowrite32_copy(void __iomem *to, const void *from, size_t count); void __iowrite64_copy(void __iomem *to, const void *from, size_t count); #ifdef CONFIG_MMU -int ioremap_page_range(unsigned long addr, unsigned long end, - unsigned long phys_addr, pgprot_t prot); +int __ioremap_page_range(unsigned long addr, unsigned long end, + unsigned long phys_addr, pgprot_t prot); #else -static inline int ioremap_page_range(unsigned long addr, unsigned long end, - unsigned long phys_addr, pgprot_t prot) +static inline int __ioremap_page_range(unsigned long addr, unsigned long end, + unsigned long phys_addr, pgprot_t prot) { return 0; } #endif +#ifndef __HAVE_ARCH_IOREMAP_PAGE_RANGE +#define ioremap_page_range __ioremap_page_range +#endif + /* * Managed iomap interface */ diff --git a/lib/ioremap.c b/lib/ioremap.c index 14c6078..4106cb0 100644 --- a/lib/ioremap.c +++ b/lib/ioremap.c @@ -66,8 +66,8 @@ static inline int ioremap_pud_range(pgd_t *pgd, unsigned long addr, return 0; } -int ioremap_page_range(unsigned long addr, - unsigned long end, unsigned long phys_addr, pgprot_t prot) +int __ioremap_page_range(unsigned long addr, unsigned long end, + unsigned long phys_addr, pgprot_t prot) { pgd_t *pgd; unsigned long start; -- 1.5.3.4