mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 03:27:44 -04:00
Merge branches 'upstream/xenfs' and 'upstream/core' of git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen
* 'upstream/xenfs' of git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen:
xen/privcmd: make privcmd visible in domU
xen/privcmd: move remap_domain_mfn_range() to core xen code and export.
privcmd: MMAPBATCH: Fix error handling/reporting
xenbus: export xen_store_interface for xenfs
xen/privcmd: make sure vma is ours before doing anything to it
xen/privcmd: print SIGBUS faults
xen/xenfs: set_page_dirty is supposed to return true if it dirties
xen/privcmd: create address space to allow writable mmaps
xen: add privcmd driver
xen: add variable hypercall caller
xen: add xen_set_domain_pte()
xen: add /proc/xen/xsd_{kva,port} to xenfs
* 'upstream/core' of git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen: (29 commits)
xen: include xen/xen.h for definition of xen_initial_domain()
xen: use host E820 map for dom0
xen: correctly rebuild mfn list list after migration.
xen: improvements to VIRQ_DEBUG output
xen: set up IRQ before binding virq to evtchn
xen: ensure that all event channels start off bound to VCPU 0
xen/hvc: only notify if we actually sent something
xen: don't add extra_pages for RAM after mem_end
xen: add support for PAT
xen: make sure xen_max_p2m_pfn is up to date
xen: limit extra memory to a certain ratio of base
xen: add extra pages for E820 RAM regions, even if beyond mem_end
xen: make sure xen_extra_mem_start is beyond all non-RAM e820
xen: implement "extra" memory to reserve space for pages not present at boot
xen: Use host-provided E820 map
xen: don't map missing memory
xen: defer building p2m mfn structures until kernel is mapped
xen: add return value to set_phys_to_machine()
xen: convert p2m to a 3 level tree
xen: make install_p2mtop_page() static
...
Fix up trivial conflict in arch/x86/xen/mmu.c, and fix the use of
'reserve_early()' - in the new memblock world order it is now
'memblock_x86_reserve_range()' instead. Pointed out by Jeremy.
This commit is contained in:
commit
520045db94
20 changed files with 1332 additions and 137 deletions
|
|
@ -261,7 +261,7 @@ static void init_evtchn_cpu_bindings(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
memset(cpu_evtchn_mask(0), ~0, sizeof(cpu_evtchn_mask(0)));
|
||||
memset(cpu_evtchn_mask(0), ~0, sizeof(struct cpu_evtchn_s));
|
||||
}
|
||||
|
||||
static inline void clear_evtchn(int port)
|
||||
|
|
@ -377,7 +377,7 @@ int bind_evtchn_to_irq(unsigned int evtchn)
|
|||
irq = find_unbound_irq();
|
||||
|
||||
set_irq_chip_and_handler_name(irq, &xen_dynamic_chip,
|
||||
handle_edge_irq, "event");
|
||||
handle_fasteoi_irq, "event");
|
||||
|
||||
evtchn_to_irq[evtchn] = irq;
|
||||
irq_info[irq] = mk_evtchn_info(evtchn);
|
||||
|
|
@ -435,6 +435,11 @@ static int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
|
|||
irq = per_cpu(virq_to_irq, cpu)[virq];
|
||||
|
||||
if (irq == -1) {
|
||||
irq = find_unbound_irq();
|
||||
|
||||
set_irq_chip_and_handler_name(irq, &xen_percpu_chip,
|
||||
handle_percpu_irq, "virq");
|
||||
|
||||
bind_virq.virq = virq;
|
||||
bind_virq.vcpu = cpu;
|
||||
if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq,
|
||||
|
|
@ -442,11 +447,6 @@ static int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
|
|||
BUG();
|
||||
evtchn = bind_virq.port;
|
||||
|
||||
irq = find_unbound_irq();
|
||||
|
||||
set_irq_chip_and_handler_name(irq, &xen_percpu_chip,
|
||||
handle_percpu_irq, "virq");
|
||||
|
||||
evtchn_to_irq[evtchn] = irq;
|
||||
irq_info[irq] = mk_virq_info(evtchn, virq);
|
||||
|
||||
|
|
@ -578,41 +578,75 @@ irqreturn_t xen_debug_interrupt(int irq, void *dev_id)
|
|||
{
|
||||
struct shared_info *sh = HYPERVISOR_shared_info;
|
||||
int cpu = smp_processor_id();
|
||||
unsigned long *cpu_evtchn = cpu_evtchn_mask(cpu);
|
||||
int i;
|
||||
unsigned long flags;
|
||||
static DEFINE_SPINLOCK(debug_lock);
|
||||
struct vcpu_info *v;
|
||||
|
||||
spin_lock_irqsave(&debug_lock, flags);
|
||||
|
||||
printk("vcpu %d\n ", cpu);
|
||||
printk("\nvcpu %d\n ", cpu);
|
||||
|
||||
for_each_online_cpu(i) {
|
||||
struct vcpu_info *v = per_cpu(xen_vcpu, i);
|
||||
printk("%d: masked=%d pending=%d event_sel %08lx\n ", i,
|
||||
(get_irq_regs() && i == cpu) ? xen_irqs_disabled(get_irq_regs()) : v->evtchn_upcall_mask,
|
||||
v->evtchn_upcall_pending,
|
||||
v->evtchn_pending_sel);
|
||||
int pending;
|
||||
v = per_cpu(xen_vcpu, i);
|
||||
pending = (get_irq_regs() && i == cpu)
|
||||
? xen_irqs_disabled(get_irq_regs())
|
||||
: v->evtchn_upcall_mask;
|
||||
printk("%d: masked=%d pending=%d event_sel %0*lx\n ", i,
|
||||
pending, v->evtchn_upcall_pending,
|
||||
(int)(sizeof(v->evtchn_pending_sel)*2),
|
||||
v->evtchn_pending_sel);
|
||||
}
|
||||
printk("pending:\n ");
|
||||
for(i = ARRAY_SIZE(sh->evtchn_pending)-1; i >= 0; i--)
|
||||
printk("%08lx%s", sh->evtchn_pending[i],
|
||||
i % 8 == 0 ? "\n " : " ");
|
||||
printk("\nmasks:\n ");
|
||||
for(i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--)
|
||||
printk("%08lx%s", sh->evtchn_mask[i],
|
||||
i % 8 == 0 ? "\n " : " ");
|
||||
v = per_cpu(xen_vcpu, cpu);
|
||||
|
||||
printk("\nunmasked:\n ");
|
||||
for(i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--)
|
||||
printk("%08lx%s", sh->evtchn_pending[i] & ~sh->evtchn_mask[i],
|
||||
i % 8 == 0 ? "\n " : " ");
|
||||
printk("\npending:\n ");
|
||||
for (i = ARRAY_SIZE(sh->evtchn_pending)-1; i >= 0; i--)
|
||||
printk("%0*lx%s", (int)sizeof(sh->evtchn_pending[0])*2,
|
||||
sh->evtchn_pending[i],
|
||||
i % 8 == 0 ? "\n " : " ");
|
||||
printk("\nglobal mask:\n ");
|
||||
for (i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--)
|
||||
printk("%0*lx%s",
|
||||
(int)(sizeof(sh->evtchn_mask[0])*2),
|
||||
sh->evtchn_mask[i],
|
||||
i % 8 == 0 ? "\n " : " ");
|
||||
|
||||
printk("\nglobally unmasked:\n ");
|
||||
for (i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--)
|
||||
printk("%0*lx%s", (int)(sizeof(sh->evtchn_mask[0])*2),
|
||||
sh->evtchn_pending[i] & ~sh->evtchn_mask[i],
|
||||
i % 8 == 0 ? "\n " : " ");
|
||||
|
||||
printk("\nlocal cpu%d mask:\n ", cpu);
|
||||
for (i = (NR_EVENT_CHANNELS/BITS_PER_LONG)-1; i >= 0; i--)
|
||||
printk("%0*lx%s", (int)(sizeof(cpu_evtchn[0])*2),
|
||||
cpu_evtchn[i],
|
||||
i % 8 == 0 ? "\n " : " ");
|
||||
|
||||
printk("\nlocally unmasked:\n ");
|
||||
for (i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--) {
|
||||
unsigned long pending = sh->evtchn_pending[i]
|
||||
& ~sh->evtchn_mask[i]
|
||||
& cpu_evtchn[i];
|
||||
printk("%0*lx%s", (int)(sizeof(sh->evtchn_mask[0])*2),
|
||||
pending, i % 8 == 0 ? "\n " : " ");
|
||||
}
|
||||
|
||||
printk("\npending list:\n");
|
||||
for(i = 0; i < NR_EVENT_CHANNELS; i++) {
|
||||
for (i = 0; i < NR_EVENT_CHANNELS; i++) {
|
||||
if (sync_test_bit(i, sh->evtchn_pending)) {
|
||||
printk(" %d: event %d -> irq %d\n",
|
||||
int word_idx = i / BITS_PER_LONG;
|
||||
printk(" %d: event %d -> irq %d%s%s%s\n",
|
||||
cpu_from_evtchn(i), i,
|
||||
evtchn_to_irq[i]);
|
||||
evtchn_to_irq[i],
|
||||
sync_test_bit(word_idx, &v->evtchn_pending_sel)
|
||||
? "" : " l2-clear",
|
||||
!sync_test_bit(i, sh->evtchn_mask)
|
||||
? "" : " globally-masked",
|
||||
sync_test_bit(i, cpu_evtchn)
|
||||
? "" : " locally-masked");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -663,6 +697,9 @@ static void __xen_evtchn_do_upcall(void)
|
|||
int irq = evtchn_to_irq[port];
|
||||
struct irq_desc *desc;
|
||||
|
||||
mask_evtchn(port);
|
||||
clear_evtchn(port);
|
||||
|
||||
if (irq != -1) {
|
||||
desc = irq_to_desc(irq);
|
||||
if (desc)
|
||||
|
|
@ -800,10 +837,10 @@ static void ack_dynirq(unsigned int irq)
|
|||
{
|
||||
int evtchn = evtchn_from_irq(irq);
|
||||
|
||||
move_native_irq(irq);
|
||||
move_masked_irq(irq);
|
||||
|
||||
if (VALID_EVTCHN(evtchn))
|
||||
clear_evtchn(evtchn);
|
||||
unmask_evtchn(evtchn);
|
||||
}
|
||||
|
||||
static int retrigger_dynirq(unsigned int irq)
|
||||
|
|
@ -959,7 +996,7 @@ static struct irq_chip xen_dynamic_chip __read_mostly = {
|
|||
.mask = disable_dynirq,
|
||||
.unmask = enable_dynirq,
|
||||
|
||||
.ack = ack_dynirq,
|
||||
.eoi = ack_dynirq,
|
||||
.set_affinity = set_affinity_irq,
|
||||
.retrigger = retrigger_dynirq,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -64,9 +64,11 @@
|
|||
|
||||
|
||||
int xen_store_evtchn;
|
||||
EXPORT_SYMBOL(xen_store_evtchn);
|
||||
EXPORT_SYMBOL_GPL(xen_store_evtchn);
|
||||
|
||||
struct xenstore_domain_interface *xen_store_interface;
|
||||
EXPORT_SYMBOL_GPL(xen_store_interface);
|
||||
|
||||
static unsigned long xen_store_mfn;
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(xenstore_chain);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
obj-$(CONFIG_XENFS) += xenfs.o
|
||||
|
||||
xenfs-objs = super.o xenbus.o
|
||||
xenfs-y = super.o xenbus.o privcmd.o
|
||||
xenfs-$(CONFIG_XEN_DOM0) += xenstored.o
|
||||
|
|
|
|||
404
drivers/xen/xenfs/privcmd.c
Normal file
404
drivers/xen/xenfs/privcmd.c
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
/******************************************************************************
|
||||
* privcmd.c
|
||||
*
|
||||
* Interface to privileged domain-0 commands.
|
||||
*
|
||||
* Copyright (c) 2002-2004, K A Fraser, B Dragovic
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/tlb.h>
|
||||
#include <asm/xen/hypervisor.h>
|
||||
#include <asm/xen/hypercall.h>
|
||||
|
||||
#include <xen/xen.h>
|
||||
#include <xen/privcmd.h>
|
||||
#include <xen/interface/xen.h>
|
||||
#include <xen/features.h>
|
||||
#include <xen/page.h>
|
||||
#include <xen/xen-ops.h>
|
||||
|
||||
#ifndef HAVE_ARCH_PRIVCMD_MMAP
|
||||
static int privcmd_enforce_singleshot_mapping(struct vm_area_struct *vma);
|
||||
#endif
|
||||
|
||||
static long privcmd_ioctl_hypercall(void __user *udata)
|
||||
{
|
||||
struct privcmd_hypercall hypercall;
|
||||
long ret;
|
||||
|
||||
if (copy_from_user(&hypercall, udata, sizeof(hypercall)))
|
||||
return -EFAULT;
|
||||
|
||||
ret = privcmd_call(hypercall.op,
|
||||
hypercall.arg[0], hypercall.arg[1],
|
||||
hypercall.arg[2], hypercall.arg[3],
|
||||
hypercall.arg[4]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void free_page_list(struct list_head *pages)
|
||||
{
|
||||
struct page *p, *n;
|
||||
|
||||
list_for_each_entry_safe(p, n, pages, lru)
|
||||
__free_page(p);
|
||||
|
||||
INIT_LIST_HEAD(pages);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given an array of items in userspace, return a list of pages
|
||||
* containing the data. If copying fails, either because of memory
|
||||
* allocation failure or a problem reading user memory, return an
|
||||
* error code; its up to the caller to dispose of any partial list.
|
||||
*/
|
||||
static int gather_array(struct list_head *pagelist,
|
||||
unsigned nelem, size_t size,
|
||||
void __user *data)
|
||||
{
|
||||
unsigned pageidx;
|
||||
void *pagedata;
|
||||
int ret;
|
||||
|
||||
if (size > PAGE_SIZE)
|
||||
return 0;
|
||||
|
||||
pageidx = PAGE_SIZE;
|
||||
pagedata = NULL; /* quiet, gcc */
|
||||
while (nelem--) {
|
||||
if (pageidx > PAGE_SIZE-size) {
|
||||
struct page *page = alloc_page(GFP_KERNEL);
|
||||
|
||||
ret = -ENOMEM;
|
||||
if (page == NULL)
|
||||
goto fail;
|
||||
|
||||
pagedata = page_address(page);
|
||||
|
||||
list_add_tail(&page->lru, pagelist);
|
||||
pageidx = 0;
|
||||
}
|
||||
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(pagedata + pageidx, data, size))
|
||||
goto fail;
|
||||
|
||||
data += size;
|
||||
pageidx += size;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call function "fn" on each element of the array fragmented
|
||||
* over a list of pages.
|
||||
*/
|
||||
static int traverse_pages(unsigned nelem, size_t size,
|
||||
struct list_head *pos,
|
||||
int (*fn)(void *data, void *state),
|
||||
void *state)
|
||||
{
|
||||
void *pagedata;
|
||||
unsigned pageidx;
|
||||
int ret = 0;
|
||||
|
||||
BUG_ON(size > PAGE_SIZE);
|
||||
|
||||
pageidx = PAGE_SIZE;
|
||||
pagedata = NULL; /* hush, gcc */
|
||||
|
||||
while (nelem--) {
|
||||
if (pageidx > PAGE_SIZE-size) {
|
||||
struct page *page;
|
||||
pos = pos->next;
|
||||
page = list_entry(pos, struct page, lru);
|
||||
pagedata = page_address(page);
|
||||
pageidx = 0;
|
||||
}
|
||||
|
||||
ret = (*fn)(pagedata + pageidx, state);
|
||||
if (ret)
|
||||
break;
|
||||
pageidx += size;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct mmap_mfn_state {
|
||||
unsigned long va;
|
||||
struct vm_area_struct *vma;
|
||||
domid_t domain;
|
||||
};
|
||||
|
||||
static int mmap_mfn_range(void *data, void *state)
|
||||
{
|
||||
struct privcmd_mmap_entry *msg = data;
|
||||
struct mmap_mfn_state *st = state;
|
||||
struct vm_area_struct *vma = st->vma;
|
||||
int rc;
|
||||
|
||||
/* Do not allow range to wrap the address space. */
|
||||
if ((msg->npages > (LONG_MAX >> PAGE_SHIFT)) ||
|
||||
((unsigned long)(msg->npages << PAGE_SHIFT) >= -st->va))
|
||||
return -EINVAL;
|
||||
|
||||
/* Range chunks must be contiguous in va space. */
|
||||
if ((msg->va != st->va) ||
|
||||
((msg->va+(msg->npages<<PAGE_SHIFT)) > vma->vm_end))
|
||||
return -EINVAL;
|
||||
|
||||
rc = xen_remap_domain_mfn_range(vma,
|
||||
msg->va & PAGE_MASK,
|
||||
msg->mfn, msg->npages,
|
||||
vma->vm_page_prot,
|
||||
st->domain);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
st->va += msg->npages << PAGE_SHIFT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long privcmd_ioctl_mmap(void __user *udata)
|
||||
{
|
||||
struct privcmd_mmap mmapcmd;
|
||||
struct mm_struct *mm = current->mm;
|
||||
struct vm_area_struct *vma;
|
||||
int rc;
|
||||
LIST_HEAD(pagelist);
|
||||
struct mmap_mfn_state state;
|
||||
|
||||
if (!xen_initial_domain())
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&mmapcmd, udata, sizeof(mmapcmd)))
|
||||
return -EFAULT;
|
||||
|
||||
rc = gather_array(&pagelist,
|
||||
mmapcmd.num, sizeof(struct privcmd_mmap_entry),
|
||||
mmapcmd.entry);
|
||||
|
||||
if (rc || list_empty(&pagelist))
|
||||
goto out;
|
||||
|
||||
down_write(&mm->mmap_sem);
|
||||
|
||||
{
|
||||
struct page *page = list_first_entry(&pagelist,
|
||||
struct page, lru);
|
||||
struct privcmd_mmap_entry *msg = page_address(page);
|
||||
|
||||
vma = find_vma(mm, msg->va);
|
||||
rc = -EINVAL;
|
||||
|
||||
if (!vma || (msg->va != vma->vm_start) ||
|
||||
!privcmd_enforce_singleshot_mapping(vma))
|
||||
goto out_up;
|
||||
}
|
||||
|
||||
state.va = vma->vm_start;
|
||||
state.vma = vma;
|
||||
state.domain = mmapcmd.dom;
|
||||
|
||||
rc = traverse_pages(mmapcmd.num, sizeof(struct privcmd_mmap_entry),
|
||||
&pagelist,
|
||||
mmap_mfn_range, &state);
|
||||
|
||||
|
||||
out_up:
|
||||
up_write(&mm->mmap_sem);
|
||||
|
||||
out:
|
||||
free_page_list(&pagelist);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct mmap_batch_state {
|
||||
domid_t domain;
|
||||
unsigned long va;
|
||||
struct vm_area_struct *vma;
|
||||
int err;
|
||||
|
||||
xen_pfn_t __user *user;
|
||||
};
|
||||
|
||||
static int mmap_batch_fn(void *data, void *state)
|
||||
{
|
||||
xen_pfn_t *mfnp = data;
|
||||
struct mmap_batch_state *st = state;
|
||||
|
||||
if (xen_remap_domain_mfn_range(st->vma, st->va & PAGE_MASK, *mfnp, 1,
|
||||
st->vma->vm_page_prot, st->domain) < 0) {
|
||||
*mfnp |= 0xf0000000U;
|
||||
st->err++;
|
||||
}
|
||||
st->va += PAGE_SIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmap_return_errors(void *data, void *state)
|
||||
{
|
||||
xen_pfn_t *mfnp = data;
|
||||
struct mmap_batch_state *st = state;
|
||||
|
||||
put_user(*mfnp, st->user++);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct vm_operations_struct privcmd_vm_ops;
|
||||
|
||||
static long privcmd_ioctl_mmap_batch(void __user *udata)
|
||||
{
|
||||
int ret;
|
||||
struct privcmd_mmapbatch m;
|
||||
struct mm_struct *mm = current->mm;
|
||||
struct vm_area_struct *vma;
|
||||
unsigned long nr_pages;
|
||||
LIST_HEAD(pagelist);
|
||||
struct mmap_batch_state state;
|
||||
|
||||
if (!xen_initial_domain())
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&m, udata, sizeof(m)))
|
||||
return -EFAULT;
|
||||
|
||||
nr_pages = m.num;
|
||||
if ((m.num <= 0) || (nr_pages > (LONG_MAX >> PAGE_SHIFT)))
|
||||
return -EINVAL;
|
||||
|
||||
ret = gather_array(&pagelist, m.num, sizeof(xen_pfn_t),
|
||||
m.arr);
|
||||
|
||||
if (ret || list_empty(&pagelist))
|
||||
goto out;
|
||||
|
||||
down_write(&mm->mmap_sem);
|
||||
|
||||
vma = find_vma(mm, m.addr);
|
||||
ret = -EINVAL;
|
||||
if (!vma ||
|
||||
vma->vm_ops != &privcmd_vm_ops ||
|
||||
(m.addr != vma->vm_start) ||
|
||||
((m.addr + (nr_pages << PAGE_SHIFT)) != vma->vm_end) ||
|
||||
!privcmd_enforce_singleshot_mapping(vma)) {
|
||||
up_write(&mm->mmap_sem);
|
||||
goto out;
|
||||
}
|
||||
|
||||
state.domain = m.dom;
|
||||
state.vma = vma;
|
||||
state.va = m.addr;
|
||||
state.err = 0;
|
||||
|
||||
ret = traverse_pages(m.num, sizeof(xen_pfn_t),
|
||||
&pagelist, mmap_batch_fn, &state);
|
||||
|
||||
up_write(&mm->mmap_sem);
|
||||
|
||||
if (state.err > 0) {
|
||||
ret = 0;
|
||||
|
||||
state.user = m.arr;
|
||||
traverse_pages(m.num, sizeof(xen_pfn_t),
|
||||
&pagelist,
|
||||
mmap_return_errors, &state);
|
||||
}
|
||||
|
||||
out:
|
||||
free_page_list(&pagelist);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long privcmd_ioctl(struct file *file,
|
||||
unsigned int cmd, unsigned long data)
|
||||
{
|
||||
int ret = -ENOSYS;
|
||||
void __user *udata = (void __user *) data;
|
||||
|
||||
switch (cmd) {
|
||||
case IOCTL_PRIVCMD_HYPERCALL:
|
||||
ret = privcmd_ioctl_hypercall(udata);
|
||||
break;
|
||||
|
||||
case IOCTL_PRIVCMD_MMAP:
|
||||
ret = privcmd_ioctl_mmap(udata);
|
||||
break;
|
||||
|
||||
case IOCTL_PRIVCMD_MMAPBATCH:
|
||||
ret = privcmd_ioctl_mmap_batch(udata);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef HAVE_ARCH_PRIVCMD_MMAP
|
||||
static int privcmd_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||
{
|
||||
printk(KERN_DEBUG "privcmd_fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n",
|
||||
vma, vma->vm_start, vma->vm_end,
|
||||
vmf->pgoff, vmf->virtual_address);
|
||||
|
||||
return VM_FAULT_SIGBUS;
|
||||
}
|
||||
|
||||
static struct vm_operations_struct privcmd_vm_ops = {
|
||||
.fault = privcmd_fault
|
||||
};
|
||||
|
||||
static int privcmd_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
/* Unsupported for auto-translate guests. */
|
||||
if (xen_feature(XENFEAT_auto_translated_physmap))
|
||||
return -ENOSYS;
|
||||
|
||||
/* DONTCOPY is essential for Xen as copy_page_range is broken. */
|
||||
vma->vm_flags |= VM_RESERVED | VM_IO | VM_DONTCOPY;
|
||||
vma->vm_ops = &privcmd_vm_ops;
|
||||
vma->vm_private_data = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int privcmd_enforce_singleshot_mapping(struct vm_area_struct *vma)
|
||||
{
|
||||
return (xchg(&vma->vm_private_data, (void *)1) == NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
const struct file_operations privcmd_file_ops = {
|
||||
.unlocked_ioctl = privcmd_ioctl,
|
||||
.mmap = privcmd_mmap,
|
||||
};
|
||||
|
|
@ -12,6 +12,8 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/backing-dev.h>
|
||||
|
||||
#include <xen/xen.h>
|
||||
|
||||
|
|
@ -22,6 +24,62 @@
|
|||
MODULE_DESCRIPTION("Xen filesystem");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int xenfs_set_page_dirty(struct page *page)
|
||||
{
|
||||
return !TestSetPageDirty(page);
|
||||
}
|
||||
|
||||
static const struct address_space_operations xenfs_aops = {
|
||||
.set_page_dirty = xenfs_set_page_dirty,
|
||||
};
|
||||
|
||||
static struct backing_dev_info xenfs_backing_dev_info = {
|
||||
.ra_pages = 0, /* No readahead */
|
||||
.capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK,
|
||||
};
|
||||
|
||||
static struct inode *xenfs_make_inode(struct super_block *sb, int mode)
|
||||
{
|
||||
struct inode *ret = new_inode(sb);
|
||||
|
||||
if (ret) {
|
||||
ret->i_mode = mode;
|
||||
ret->i_mapping->a_ops = &xenfs_aops;
|
||||
ret->i_mapping->backing_dev_info = &xenfs_backing_dev_info;
|
||||
ret->i_uid = ret->i_gid = 0;
|
||||
ret->i_blocks = 0;
|
||||
ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct dentry *xenfs_create_file(struct super_block *sb,
|
||||
struct dentry *parent,
|
||||
const char *name,
|
||||
const struct file_operations *fops,
|
||||
void *data,
|
||||
int mode)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
struct inode *inode;
|
||||
|
||||
dentry = d_alloc_name(parent, name);
|
||||
if (!dentry)
|
||||
return NULL;
|
||||
|
||||
inode = xenfs_make_inode(sb, S_IFREG | mode);
|
||||
if (!inode) {
|
||||
dput(dentry);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inode->i_fop = fops;
|
||||
inode->i_private = data;
|
||||
|
||||
d_add(dentry, inode);
|
||||
return dentry;
|
||||
}
|
||||
|
||||
static ssize_t capabilities_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *off)
|
||||
{
|
||||
|
|
@ -44,10 +102,23 @@ static int xenfs_fill_super(struct super_block *sb, void *data, int silent)
|
|||
[1] = {},
|
||||
{ "xenbus", &xenbus_file_ops, S_IRUSR|S_IWUSR },
|
||||
{ "capabilities", &capabilities_file_ops, S_IRUGO },
|
||||
{ "privcmd", &privcmd_file_ops, S_IRUSR|S_IWUSR },
|
||||
{""},
|
||||
};
|
||||
int rc;
|
||||
|
||||
return simple_fill_super(sb, XENFS_SUPER_MAGIC, xenfs_files);
|
||||
rc = simple_fill_super(sb, XENFS_SUPER_MAGIC, xenfs_files);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (xen_initial_domain()) {
|
||||
xenfs_create_file(sb, sb->s_root, "xsd_kva",
|
||||
&xsd_kva_file_ops, NULL, S_IRUSR|S_IWUSR);
|
||||
xenfs_create_file(sb, sb->s_root, "xsd_port",
|
||||
&xsd_port_file_ops, NULL, S_IRUSR|S_IWUSR);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int xenfs_get_sb(struct file_system_type *fs_type,
|
||||
|
|
@ -66,11 +137,25 @@ static struct file_system_type xenfs_type = {
|
|||
|
||||
static int __init xenfs_init(void)
|
||||
{
|
||||
if (xen_domain())
|
||||
return register_filesystem(&xenfs_type);
|
||||
int err;
|
||||
if (!xen_domain()) {
|
||||
printk(KERN_INFO "xenfs: not registering filesystem on non-xen platform\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "XENFS: not registering filesystem on non-xen platform\n");
|
||||
return 0;
|
||||
err = register_filesystem(&xenfs_type);
|
||||
if (err) {
|
||||
printk(KERN_ERR "xenfs: Unable to register filesystem!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = bdi_init(&xenfs_backing_dev_info);
|
||||
if (err)
|
||||
unregister_filesystem(&xenfs_type);
|
||||
|
||||
out:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit xenfs_exit(void)
|
||||
|
|
|
|||
|
|
@ -2,5 +2,8 @@
|
|||
#define _XENFS_XENBUS_H
|
||||
|
||||
extern const struct file_operations xenbus_file_ops;
|
||||
extern const struct file_operations privcmd_file_ops;
|
||||
extern const struct file_operations xsd_kva_file_ops;
|
||||
extern const struct file_operations xsd_port_file_ops;
|
||||
|
||||
#endif /* _XENFS_XENBUS_H */
|
||||
|
|
|
|||
68
drivers/xen/xenfs/xenstored.c
Normal file
68
drivers/xen/xenfs/xenstored.c
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include <xen/page.h>
|
||||
|
||||
#include "xenfs.h"
|
||||
#include "../xenbus/xenbus_comms.h"
|
||||
|
||||
static ssize_t xsd_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *off)
|
||||
{
|
||||
const char *str = (const char *)file->private_data;
|
||||
return simple_read_from_buffer(buf, size, off, str, strlen(str));
|
||||
}
|
||||
|
||||
static int xsd_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
kfree(file->private_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xsd_kva_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = (void *)kasprintf(GFP_KERNEL, "0x%p",
|
||||
xen_store_interface);
|
||||
if (!file->private_data)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xsd_kva_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
size_t size = vma->vm_end - vma->vm_start;
|
||||
|
||||
if ((size > PAGE_SIZE) || (vma->vm_pgoff != 0))
|
||||
return -EINVAL;
|
||||
|
||||
if (remap_pfn_range(vma, vma->vm_start,
|
||||
virt_to_pfn(xen_store_interface),
|
||||
size, vma->vm_page_prot))
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations xsd_kva_file_ops = {
|
||||
.open = xsd_kva_open,
|
||||
.mmap = xsd_kva_mmap,
|
||||
.read = xsd_read,
|
||||
.release = xsd_release,
|
||||
};
|
||||
|
||||
static int xsd_port_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = (void *)kasprintf(GFP_KERNEL, "%d",
|
||||
xen_store_evtchn);
|
||||
if (!file->private_data)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations xsd_port_file_ops = {
|
||||
.open = xsd_port_open,
|
||||
.read = xsd_read,
|
||||
.release = xsd_release,
|
||||
};
|
||||
Loading…
Reference in a new issue