diff --git a/.rootkeys b/.rootkeys index fbe6ef5fb4..79286a1f13 100644 --- a/.rootkeys +++ b/.rootkeys @@ -558,6 +558,7 @@ 3fbba6dbasJQV-MVElDC0DGSHMiL5w tools/libxc/xc_domain.c 40278d99BLsfUv3qxv0I8C1sClZ0ow tools/libxc/xc_elf.h 403e0977Bjsm_e82pwvl9VvaJxh8Gg tools/libxc/xc_evtchn.c +4227c129ZKjJPNYooHVzBCyinf7Y6Q tools/libxc/xc_gnttab.c 40e03333Eegw8czSWvHsbKxrRZJjRA tools/libxc/xc_io.c 40e03333vrWGbLAhyJjXlqCHaJt7eA tools/libxc/xc_io.h 3fbba6dbNCU7U6nsMYiXzKkp3ztaJg tools/libxc/xc_linux_build.c diff --git a/linux-2.6.10-xen-sparse/arch/xen/kernel/gnttab.c b/linux-2.6.10-xen-sparse/arch/xen/kernel/gnttab.c index af892aeb2a..3780677908 100644 --- a/linux-2.6.10-xen-sparse/arch/xen/kernel/gnttab.c +++ b/linux-2.6.10-xen-sparse/arch/xen/kernel/gnttab.c @@ -6,6 +6,7 @@ * 2. Accessing others' memory reservations via grant references. * (i.e., mechanisms for both sender and recipient of grant references) * + * Copyright (c) 2005, Christopher Clark * Copyright (c) 2004, K A Fraser */ @@ -14,6 +15,9 @@ #include #include #include +#include +#include +#include #include #ifndef set_fixmap_ma @@ -28,8 +32,13 @@ #define ASSERT(_p) ((void)0) #endif +#define WPRINTK(fmt, args...) \ + printk(KERN_WARNING "xen_grant: " fmt, ##args) + + EXPORT_SYMBOL(gnttab_grant_foreign_access); EXPORT_SYMBOL(gnttab_end_foreign_access); +EXPORT_SYMBOL(gnttab_query_foreign_access); EXPORT_SYMBOL(gnttab_grant_foreign_transfer); EXPORT_SYMBOL(gnttab_end_foreign_transfer); @@ -39,6 +48,10 @@ static grant_ref_t gnttab_free_head; static grant_entry_t *shared; +/* /proc/xen/grant */ +static struct proc_dir_entry *grant_pde; + + /* * Lock-free grant-entry allocator */ @@ -84,9 +97,18 @@ gnttab_grant_foreign_access( return ref; } +int +gnttab_query_foreign_access( grant_ref_t ref ) +{ + u16 nflags; + + nflags = shared[ref].flags; + + return ( nflags & (GTF_reading|GTF_writing) ); +} + void -gnttab_end_foreign_access( - grant_ref_t ref, int readonly) +gnttab_end_foreign_access( grant_ref_t ref, int readonly ) { u16 flags, nflags; @@ -141,7 +163,72 @@ gnttab_end_foreign_transfer( return frame; } -void __init gnttab_init(void) +static int grant_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long data) +{ + int ret; + privcmd_hypercall_t hypercall; + + if ( cmd != IOCTL_PRIVCMD_HYPERCALL ) + return -ENOSYS; + + if ( copy_from_user(&hypercall, (void *)data, sizeof(hypercall)) ) + return -EFAULT; + + if ( hypercall.op != __HYPERVISOR_grant_table_op ) + return -ENOSYS; + + /* hypercall-invoking asm taken from privcmd.c */ + __asm__ __volatile__ ( + "pushl %%ebx; pushl %%ecx; pushl %%edx; pushl %%esi; pushl %%edi; " + "movl 4(%%eax),%%ebx ;" + "movl 8(%%eax),%%ecx ;" + "movl 12(%%eax),%%edx ;" + "movl 16(%%eax),%%esi ;" + "movl 20(%%eax),%%edi ;" + "movl (%%eax),%%eax ;" + TRAP_INSTR "; " + "popl %%edi; popl %%esi; popl %%edx; popl %%ecx; popl %%ebx" + : "=a" (ret) : "0" (&hypercall) : "memory" ); + + return ret; +} + +static struct file_operations grant_file_ops = { + ioctl: grant_ioctl, +}; + +static int grant_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + unsigned int i; + grant_entry_t *gt; + + gt = (grant_entry_t *)shared; + len = 0; + + for ( i = 0; i < NR_GRANT_REFS; i++ ) + if ( gt[i].flags ) + len += sprintf( page + len, + "Grant: ref (0x%x) flags (0x%hx) dom (0x%hx) frame (0x%x)\n", + i, + gt[i].flags, + gt[i].domid, + gt[i].frame ); + + *eof = 1; + return len; +} + +static int grant_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + /* TODO: implement this */ + return -ENOSYS; +} + +static int __init gnttab_init(void) { gnttab_setup_table_t setup; unsigned long frame; @@ -160,4 +247,25 @@ void __init gnttab_init(void) set_fixmap_ma(FIX_GNTTAB, frame << PAGE_SHIFT); shared = (grant_entry_t *)fix_to_virt(FIX_GNTTAB); + + /* + * /proc/xen/grant : used by libxc to access grant tables + */ + if ( (grant_pde = create_xen_proc_entry("grant", 0600)) == NULL ) + { + WPRINTK("Unable to create grant xen proc entry\n"); + return -1; + } + + grant_file_ops.read = grant_pde->proc_fops->read; + grant_file_ops.write = grant_pde->proc_fops->write; + + grant_pde->proc_fops = &grant_file_ops; + + grant_pde->read_proc = &grant_read; + grant_pde->write_proc = &grant_write; + + return 0; } + +__initcall(gnttab_init); diff --git a/linux-2.6.10-xen-sparse/include/asm-xen/gnttab.h b/linux-2.6.10-xen-sparse/include/asm-xen/gnttab.h index fa166ec6b1..8c28c889f7 100644 --- a/linux-2.6.10-xen-sparse/include/asm-xen/gnttab.h +++ b/linux-2.6.10-xen-sparse/include/asm-xen/gnttab.h @@ -32,4 +32,8 @@ unsigned long gnttab_end_foreign_transfer( grant_ref_t ref); +int +gnttab_query_foreign_access( + grant_ref_t ref ); + #endif /* __ASM_GNTTAB_H__ */ diff --git a/linux-2.6.10-xen-sparse/include/asm-xen/hypervisor.h b/linux-2.6.10-xen-sparse/include/asm-xen/hypervisor.h index b82db08f70..b482eaa3c4 100644 --- a/linux-2.6.10-xen-sparse/include/asm-xen/hypervisor.h +++ b/linux-2.6.10-xen-sparse/include/asm-xen/hypervisor.h @@ -536,7 +536,7 @@ HYPERVISOR_grant_table_op( __asm__ __volatile__ ( TRAP_INSTR : "=a" (ret), "=b" (ign1), "=c" (ign2), "=d" (ign3) - : "0" (__HYPERVISOR_grant_table_op), "1" (cmd), "2" (count), "3" (uop) + : "0" (__HYPERVISOR_grant_table_op), "1" (cmd), "2" (uop), "3" (count) : "memory" ); return ret; diff --git a/tools/libxc/Makefile b/tools/libxc/Makefile index 076cd24457..598a567e6b 100644 --- a/tools/libxc/Makefile +++ b/tools/libxc/Makefile @@ -20,6 +20,7 @@ SRCS += xc_atropos.c SRCS += xc_bvtsched.c SRCS += xc_domain.c SRCS += xc_evtchn.c +SRCS += xc_gnttab.c SRCS += xc_io.c SRCS += xc_linux_build.c SRCS += xc_plan9_build.c diff --git a/tools/libxc/xc.h b/tools/libxc/xc.h index a11cbe64a6..091c9d01e8 100644 --- a/tools/libxc/xc.h +++ b/tools/libxc/xc.h @@ -411,4 +411,61 @@ void *xc_map_foreign_batch(int xc_handle, u32 dom, int prot, int xc_get_pfn_list(int xc_handle, u32 domid, unsigned long *pfn_buf, unsigned long max_pfns); +/*\ + * GRANT TABLE FUNCTIONS +\*/ + +/** + * This function opens a handle to the more restricted grant table hypervisor + * interface. This may be used where the standard interface is not + * available because the domain is not privileged. + * This function can be called multiple times within a single process. + * Multiple processes can have an open hypervisor interface at the same time. + * + * Each call to this function should have a corresponding call to + * xc_grant_interface_close(). + * + * This function can fail if a Xen-enabled kernel is not currently running. + * + * @return a handle to the hypervisor grant table interface or -1 on failure + */ +int xc_grant_interface_open(void); + +/** + * This function closes an open grant table hypervisor interface. + * + * This function can fail if the handle does not represent an open interface or + * if there were problems closing the interface. + * + * @parm xc_handle a handle to an open grant table hypervisor interface + * @return 0 on success, -1 otherwise. + */ +int xc_grant_interface_close(int xc_handle); + +int xc_gnttab_map_grant_ref(int xc_handle, + memory_t host_virt_addr, + u32 dom, + u16 ref, + u16 flags, + s16 *handle, + memory_t *dev_bus_addr); + +int xc_gnttab_unmap_grant_ref(int xc_handle, + memory_t host_virt_addr, + memory_t dev_bus_addr, + u16 handle, + s16 *status); + +int xc_gnttab_setup_table(int xc_handle, + u32 dom, + u16 nr_frames, + s16 *status, + memory_t **frame_list); + +/* Grant debug builds only: */ +int xc_gnttab_dump_table(int xc_handle, + u32 dom, + s16 *status); + + #endif /* __XC_H__ */ diff --git a/tools/libxc/xc_gnttab.c b/tools/libxc/xc_gnttab.c new file mode 100644 index 0000000000..2601c30f60 --- /dev/null +++ b/tools/libxc/xc_gnttab.c @@ -0,0 +1,143 @@ +/****************************************************************************** + * xc_gnttab.c + * + * API for manipulating and accessing grant tables + * + * Copyright (c) 2005 Christopher Clark + * based on xc_evtchn.c Copyright (c) 2004, K A Fraser. + */ + +#include "xc_private.h" +#include "xen/grant_table.h" + +static int +do_gnttab_op( int xc_handle, + unsigned long cmd, + gnttab_op_t *op, + unsigned long count ) +{ + int ret = -1; + privcmd_hypercall_t hypercall; + + hypercall.op = __HYPERVISOR_grant_table_op; + hypercall.arg[0] = cmd; + hypercall.arg[1] = (unsigned long)(op); + hypercall.arg[2] = count; + + if ( mlock(op, sizeof(*op)) != 0 ) + { + PERROR("Could not lock memory for Xen hypercall"); + goto out1; + } + + if ( (ret = do_xen_hypercall(xc_handle, &hypercall)) < 0 ) + { + printf("do_gnttab_op: hypercall returned error %d\n", ret); + goto out2; + } + + out2: (void)munlock(op, sizeof(*op)); + out1: return ret; +} + + +int xc_gnttab_map_grant_ref(int xc_handle, + memory_t host_virt_addr, + u32 dom, + u16 ref, + u16 flags, + s16 *handle, + memory_t *dev_bus_addr) +{ + gnttab_op_t op; + int rc; + + op.u.map_grant_ref.host_virt_addr = host_virt_addr; + op.u.map_grant_ref.dom = (domid_t)dom; + op.u.map_grant_ref.ref = ref; + op.u.map_grant_ref.flags = flags; + + if ( (rc = do_gnttab_op(xc_handle, GNTTABOP_map_grant_ref, &op, 1)) == 0 ) + { + *handle = op.u.map_grant_ref.handle; + *dev_bus_addr = op.u.map_grant_ref.dev_bus_addr; + } + + return rc; +} + + +int xc_gnttab_unmap_grant_ref(int xc_handle, + memory_t host_virt_addr, + memory_t dev_bus_addr, + u16 handle, + s16 *status) +{ + gnttab_op_t op; + int rc; + + op.u.unmap_grant_ref.host_virt_addr = host_virt_addr; + op.u.unmap_grant_ref.dev_bus_addr = dev_bus_addr; + op.u.unmap_grant_ref.handle = handle; + + if ( (rc = do_gnttab_op(xc_handle, GNTTABOP_unmap_grant_ref, &op, 1)) == 0 ) + *status = op.u.unmap_grant_ref.status; + + return rc; +} + +int xc_gnttab_setup_table(int xc_handle, + u32 dom, + u16 nr_frames, + s16 *status, + memory_t **frame_list) +{ + gnttab_op_t op; + int rc; + int i; + + op.u.setup_table.dom = (domid_t)dom; + op.u.setup_table.nr_frames = nr_frames; + + if ( (rc = do_gnttab_op(xc_handle, GNTTABOP_setup_table, &op, 1)) == 0 ) + { + *status = op.u.setup_table.status; + for ( i = 0; i < nr_frames; i++ ) + { + (*frame_list)[i] = op.u.setup_table.frame_list[i]; + } + } + + return rc; +} + +int xc_gnttab_dump_table(int xc_handle, + u32 dom, + s16 *status) +{ + gnttab_op_t op; + int rc; + + op.u.dump_table.dom = (domid_t)dom; + + printf("xc_gnttab_dump_table: domain %d\n", dom); + + if ( (rc = do_gnttab_op(xc_handle, GNTTABOP_dump_table, &op, 1)) == 0 ) + *status = op.u.dump_table.status; + + return rc; +} + +int xc_grant_interface_open(void) +{ + int fd = open("/proc/xen/grant", O_RDWR); + if ( fd == -1 ) + PERROR("Could not obtain handle on grant command interface"); + return fd; + +} + +int xc_grant_interface_close(int xc_grant_handle) +{ + return close(xc_grant_handle); +} diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index 158dbb6306..aed94baeec 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -1060,14 +1060,14 @@ void free_page_type(struct pfn_info *page, unsigned int type) } -void put_page_type(struct pfn_info *page) +void put_page_types(struct pfn_info *page, u32 decrement) { u32 nx, x, y = page->u.inuse.type_info; again: do { x = y; - nx = x - 1; + nx = x - decrement; ASSERT((x & PGT_count_mask) != 0); @@ -1911,16 +1911,124 @@ int do_mmu_update( return rc; } +void update_shadow_va_mapping(unsigned long va, + unsigned long val, + struct exec_domain *ed, + struct domain *d) +{ + /* This function assumes the caller is holding the domain's BIGLOCK + * and is running in a shadow mode + */ + + unsigned long sval = 0; + + l1pte_propagate_from_guest(d, &val, &sval); + + if ( unlikely(__put_user(sval, ((unsigned long *)( + &shadow_linear_pg_table[l1_linear_offset(va)])))) ) + { + /* + * Since L2's are guranteed RW, failure indicates either that the + * page was not shadowed, or that the L2 entry has not yet been + * updated to reflect the shadow. + */ + l2_pgentry_t gpde = linear_l2_table[l2_table_offset(va)]; + unsigned long gpfn = l2_pgentry_val(gpde) >> PAGE_SHIFT; + + if (get_shadow_status(d, gpfn)) + { + unsigned long gmfn = __gpfn_to_mfn(d, gpfn); + unsigned long *gl1e = map_domain_mem(gmfn << PAGE_SHIFT); + unsigned l1_idx = l1_table_offset(va); + gl1e[l1_idx] = sval; + unmap_domain_mem(gl1e); + put_shadow_status(d); + + perfc_incrc(shadow_update_va_fail1); + } + else + perfc_incrc(shadow_update_va_fail2); + } + + /* + * If we're in log-dirty mode then we need to note that we've updated + * the PTE in the PT-holding page. We need the machine frame number + * for this. + */ + if ( shadow_mode_log_dirty(d) ) + mark_dirty(d, va_to_l1mfn(va)); + + check_pagetable(d, ed->arch.guest_table, "va"); /* debug */ +} + +int update_grant_va_mapping(unsigned long va, + unsigned long _nl1e, + struct domain *d, + struct exec_domain *ed) +{ + /* Caller must: + * . own d's BIGLOCK + * . already have 'get_page' correctly on the to-be-installed nl1e + * . be responsible for flushing the TLB + * . check PTE being installed isn't DISALLOWED + */ + + /* Return value: + * -ve : error + * 0 : done + * GNTUPDVA_prev_ro : done & prior mapping was ro to same frame + * GNTUPDVA_prev_rw : done & prior mapping was rw to same frame + */ + + int rc = 0; + l1_pgentry_t *pl1e; + unsigned long _ol1e; + + cleanup_writable_pagetable(d); + + pl1e = &linear_pg_table[l1_linear_offset(va)]; + + if ( unlikely(__get_user(_ol1e, (unsigned long *)pl1e) != 0) ) + rc = -EINVAL; + else + { + l1_pgentry_t ol1e = mk_l1_pgentry(_ol1e); + + if ( update_l1e(pl1e, ol1e, mk_l1_pgentry(_nl1e)) ) + { + /* overwrote different mfn? */ + if (((_ol1e ^ _nl1e) & (PADDR_MASK & PAGE_MASK)) != 0) + { + rc = 0; + put_page_from_l1e(ol1e, d); + } + else + rc = ((_ol1e & _PAGE_RW) ? GNTUPDVA_prev_rw + : GNTUPDVA_prev_ro ); + /* use return code to avoid nasty grant table + * slow path in put_page_from_l1e -- caller + * must handle ref count instead. */ + } + else + rc = -EINVAL; + } + + if ( unlikely(shadow_mode_enabled(d)) ) + update_shadow_va_mapping(va, _nl1e, ed, d); + + return rc; +} + int do_update_va_mapping(unsigned long va, unsigned long val, unsigned long flags) { - struct exec_domain *ed = current; - struct domain *d = ed->domain; - int err = 0; - unsigned int cpu = ed->processor; - unsigned long deferred_ops; + struct exec_domain *ed = current; + struct domain *d = ed->domain; + unsigned int cpu = ed->processor; + unsigned long deferred_ops; + int rc = 0; perfc_incrc(calls_to_update_va); @@ -1941,52 +2049,14 @@ int do_update_va_mapping(unsigned long va, if ( unlikely(!mod_l1_entry(&linear_pg_table[l1_linear_offset(va)], mk_l1_pgentry(val))) ) - err = -EINVAL; + rc = -EINVAL; if ( unlikely(shadow_mode_enabled(d)) ) { - unsigned long sval = 0; + if ( shadow_mode_external(current->domain) ) + BUG(); // can't use linear_l2_table with external tables. - l1pte_propagate_from_guest(d, &val, &sval); - - if ( unlikely(__put_user(sval, ((unsigned long *)( - &shadow_linear_pg_table[l1_linear_offset(va)])))) ) - { - /* - * Since L2's are guranteed RW, failure indicates either that the - * page was not shadowed, or that the L2 entry has not yet been - * updated to reflect the shadow. - */ - if ( shadow_mode_external(current->domain) ) - BUG(); // can't use linear_l2_table with external tables. - - l2_pgentry_t gpde = linear_l2_table[l2_table_offset(va)]; - unsigned long gpfn = l2_pgentry_val(gpde) >> PAGE_SHIFT; - - if (get_shadow_status(d, gpfn)) - { - unsigned long gmfn = __gpfn_to_mfn(d, gpfn); - unsigned long *gl1e = map_domain_mem(gmfn << PAGE_SHIFT); - unsigned l1_idx = l1_table_offset(va); - gl1e[l1_idx] = sval; - unmap_domain_mem(gl1e); - put_shadow_status(d); - - perfc_incrc(shadow_update_va_fail1); - } - else - perfc_incrc(shadow_update_va_fail2); - } - - /* - * If we're in log-dirty mode then we need to note that we've updated - * the PTE in the PT-holding page. We need the machine frame number - * for this. - */ - if ( shadow_mode_log_dirty(d) ) - mark_dirty(d, va_to_l1mfn(va)); - - check_pagetable(d, ed->arch.guest_table, "va"); /* debug */ + update_shadow_va_mapping(va, val, ed, d); } deferred_ops = percpu_info[cpu].deferred_ops; @@ -2003,7 +2073,7 @@ int do_update_va_mapping(unsigned long va, UNLOCK_BIGLOCK(d); - return err; + return rc; } int do_update_va_mapping_otherdomain(unsigned long va, diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c index f74a5fcee7..afb02d6b13 100644 --- a/xen/common/grant_table.c +++ b/xen/common/grant_table.c @@ -4,6 +4,7 @@ * Mechanism for granting foreign access to page frames, and receiving * page-ownership transfers. * + * Copyright (c) 2005 Christopher Clark * Copyright (c) 2004 K A Fraser * * This program is free software; you can redistribute it and/or modify @@ -21,8 +22,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define GRANT_DEBUG 1 + #include #include +#include +#include #define PIN_FAIL(_rc, _f, _a...) \ do { \ @@ -50,19 +55,24 @@ put_maptrack_handle( t->maptrack_head = handle; } -static void +static int __gnttab_map_grant_ref( - gnttab_map_grant_ref_t *uop) + gnttab_map_grant_ref_t *uop, + unsigned long *va) { - domid_t dom, sdom; - grant_ref_t ref; - struct domain *ld, *rd; - u16 flags, sflags; - int handle; + domid_t dom, sdom; + grant_ref_t ref; + struct domain *ld, *rd; + struct exec_domain *led; + u16 flags, sflags; + int handle; active_grant_entry_t *act; - grant_entry_t *sha; - s16 rc = 0; - unsigned long frame; + grant_entry_t *sha; + s16 rc = 0; + unsigned long frame = 0, host_virt_addr; + + /* Returns 0 if TLB flush / invalidate required by caller. + * va will indicate the address to be invalidated. */ /* * We bound the number of times we retry CMPXCHG on memory locations that @@ -74,23 +84,33 @@ __gnttab_map_grant_ref( */ int retries = 0; - ld = current->domain; + led = current; + ld = led->domain; /* Bitwise-OR avoids short-circuiting which screws control flow. */ if ( unlikely(__get_user(dom, &uop->dom) | __get_user(ref, &uop->ref) | + __get_user(host_virt_addr, &uop->host_virt_addr) | __get_user(flags, &uop->flags)) ) { DPRINTK("Fault while reading gnttab_map_grant_ref_t.\n"); - return; /* don't set status */ + return -EFAULT; /* don't set status */ } - if ( unlikely(ref >= NR_GRANT_ENTRIES) || + if ( ((host_virt_addr != 0) || (flags & GNTMAP_host_map) ) && + unlikely(!__addr_ok(host_virt_addr))) + { + DPRINTK("Bad virtual address (%x) or flags (%x).\n", host_virt_addr, flags); + (void)__put_user(GNTST_bad_virt_addr, &uop->handle); + return GNTST_bad_gntref; + } + + if ( unlikely(ref >= NR_GRANT_ENTRIES) || unlikely((flags & (GNTMAP_device_map|GNTMAP_host_map)) == 0) ) { DPRINTK("Bad ref (%d) or flags (%x).\n", ref, flags); (void)__put_user(GNTST_bad_gntref, &uop->handle); - return; + return GNTST_bad_gntref; } if ( unlikely((rd = find_domain_by_id(dom)) == NULL) || @@ -100,7 +120,7 @@ __gnttab_map_grant_ref( put_domain(rd); DPRINTK("Could not find domain %d\n", dom); (void)__put_user(GNTST_bad_domain, &uop->handle); - return; + return GNTST_bad_domain; } if ( unlikely((handle = get_maptrack_handle(ld->grant_table)) == -1) ) @@ -108,14 +128,16 @@ __gnttab_map_grant_ref( put_domain(rd); DPRINTK("No more map handles available\n"); (void)__put_user(GNTST_no_device_space, &uop->handle); - return; + return GNTST_no_device_space; } + DPRINTK("Mapping grant ref (%hu) for domain (%hu) with flags (%x)\n", + ref, dom, flags); act = &rd->grant_table->active[ref]; sha = &rd->grant_table->shared[ref]; spin_lock(&rd->grant_table->lock); - + if ( act->pin == 0 ) { /* CASE 1: Activating a previously inactive entry. */ @@ -148,7 +170,7 @@ __gnttab_map_grant_ref( /* NB. prev_scombo is updated in place to seen value. */ if ( unlikely(cmpxchg_user((u32 *)&sha->flags, - prev_scombo, + prev_scombo, new_scombo)) ) PIN_FAIL(GNTST_general_error, "Fault while modifying shared flags and domid.\n"); @@ -168,16 +190,18 @@ __gnttab_map_grant_ref( } /* rmb(); */ /* not on x86 */ - frame = sha->frame; - if ( unlikely(!pfn_is_ram(frame)) || - unlikely(!((flags & GNTMAP_readonly) ? - get_page(&frame_table[frame], rd) : - get_page_and_type(&frame_table[frame], rd, + + frame = __translate_gpfn_to_mfn(rd, sha->frame); + + if ( unlikely(!pfn_is_ram(frame)) || + unlikely(!((flags & GNTMAP_readonly) ? + get_page(&frame_table[frame], rd) : + get_page_and_type(&frame_table[frame], rd, PGT_writable_page))) ) { clear_bit(_GTF_writing, &sha->flags); clear_bit(_GTF_reading, &sha->flags); - PIN_FAIL(GNTST_general_error, + PIN_FAIL(GNTST_general_error, "Could not pin the granted frame!\n"); } @@ -230,7 +254,9 @@ __gnttab_map_grant_ref( sflags = prev_sflags; } - if ( unlikely(!get_page_type(&frame_table[act->frame], + frame = act->frame; + + if ( unlikely(!get_page_type(&frame_table[frame], PGT_writable_page)) ) { clear_bit(_GTF_writing, &sha->flags); @@ -251,34 +277,91 @@ __gnttab_map_grant_ref( ld->grant_table->maptrack[handle].ref_and_flags = (ref << MAPTRACK_REF_SHIFT) | (flags & MAPTRACK_GNTMAP_MASK); + if ( (host_virt_addr != 0) && (flags & GNTMAP_host_map) ) + { + /* Write update into the pagetable + */ + if ( 0 > (rc = update_grant_va_mapping( host_virt_addr, + (frame << PAGE_SHIFT) | _PAGE_PRESENT | + _PAGE_ACCESSED | + _PAGE_DIRTY | + ((flags & GNTMAP_readonly) ? 0 : _PAGE_RW), + ld, led )) ) + { + /* Abort. */ + act->pin -= (flags & GNTMAP_readonly) ? + GNTPIN_hstr_inc : GNTPIN_hstw_inc; + + if ( flags & GNTMAP_readonly ) + act->pin -= GNTPIN_hstr_inc; + else + { + act->pin -= GNTPIN_hstw_inc; + if ( (act->pin & (GNTPIN_hstw_mask | GNTPIN_hstr_mask)) == 0 ) + put_page_type(&frame_table[frame]); + + if ( act->pin == 0 ) + put_page(&frame_table[frame]); + } + goto fail; + } + + if ( rc == GNTUPDVA_prev_ro ) + act->pin -= GNTPIN_hstr_inc; + + if ( rc == GNTUPDVA_prev_rw ) + { + act->pin -= GNTPIN_hstw_inc; + put_page_type(&frame_table[frame]); + } + rc = 0; + *va = host_virt_addr; + + /* IMPORTANT: must flush / invalidate entry in TLB. + * This is done in the outer gnttab_map_grant_ref when return 0. + */ + } + + if ( flags & GNTMAP_device_map ) + (void)__put_user(frame, &uop->dev_bus_addr); + /* Unchecked and unconditional. */ (void)__put_user(handle, &uop->handle); - (void)__put_user(act->frame, &uop->dev_bus_addr); spin_unlock(&rd->grant_table->lock); put_domain(rd); - return; + return 0; fail: (void)__put_user(rc, &uop->handle); spin_unlock(&rd->grant_table->lock); put_domain(rd); - put_maptrack_handle(ld->grant_table, handle); + put_maptrack_handle(ld->grant_table, handle); //cwc22: check this + return rc; } static long gnttab_map_grant_ref( gnttab_map_grant_ref_t *uop, unsigned int count) { - int i; + int i, flush = 0; + unsigned long va; + for ( i = 0; i < count; i++ ) - __gnttab_map_grant_ref(&uop[i]); + if ( __gnttab_map_grant_ref(&uop[i], &va) == 0) + flush++; + + if ( flush == 1 ) + __flush_tlb_one(va); + else if ( flush ) + local_flush_tlb(); return 0; } -static void +static int __gnttab_unmap_grant_ref( - gnttab_unmap_grant_ref_t *uop) + gnttab_unmap_grant_ref_t *uop, + unsigned long *va) { domid_t dom; grant_ref_t ref; @@ -288,7 +371,7 @@ __gnttab_unmap_grant_ref( active_grant_entry_t *act; grant_entry_t *sha; grant_mapping_t *map; - s16 rc = 0; + s16 rc = -EFAULT; unsigned long frame, virt; ld = current->domain; @@ -299,7 +382,7 @@ __gnttab_unmap_grant_ref( __get_user(handle, &uop->handle)) ) { DPRINTK("Fault while reading gnttab_unmap_grant_ref_t.\n"); - return; /* don't set status */ + return -EFAULT; /* don't set status */ } map = &ld->grant_table->maptrack[handle]; @@ -309,7 +392,7 @@ __gnttab_unmap_grant_ref( { DPRINTK("Bad handle (%d).\n", handle); (void)__put_user(GNTST_bad_handle, &uop->status); - return; + return GNTST_bad_handle; } dom = map->domid; @@ -322,8 +405,10 @@ __gnttab_unmap_grant_ref( put_domain(rd); DPRINTK("Could not find domain %d\n", dom); (void)__put_user(GNTST_bad_domain, &uop->status); - return; + return GNTST_bad_domain; } + DPRINTK("Unmapping grant ref (%hu) for domain (%hu) with handle (%hu)\n", + ref, dom, handle); act = &rd->grant_table->active[ref]; sha = &rd->grant_table->shared[ref]; @@ -344,12 +429,53 @@ __gnttab_unmap_grant_ref( frame = act->frame; } - if ( (virt != 0) && (map->ref_and_flags & GNTMAP_host_map) ) + if ( (virt != 0) && + (map->ref_and_flags & GNTMAP_host_map) && + ((act->pin & (GNTPIN_hstw_mask | GNTPIN_hstr_mask)) > 0)) { - act->pin -= (map->ref_and_flags & GNTMAP_readonly) ? - GNTPIN_hstr_inc : GNTPIN_hstw_inc; + l1_pgentry_t *pl1e; + unsigned long _ol1e; + + pl1e = &linear_pg_table[l1_linear_offset(virt)]; + + if ( unlikely(__get_user(_ol1e, (unsigned long *)pl1e) != 0) ) + { + DPRINTK("Could not find PTE entry for address %x\n", virt); + rc = -EINVAL; + goto fail; + } + + /* check that the virtual address supplied is actually + * mapped to act->frame. + */ + if ( unlikely((_ol1e >> PAGE_SHIFT) != frame )) + { + DPRINTK("PTE entry %x for address %x doesn't match frame %x\n", + _ol1e, virt, frame); + rc = -EINVAL; + goto fail; + } + + /* This code _requires_ that the act->pin bits are updated + * if a mapping is ever switched between RO and RW. + */ + act->pin -= ( _ol1e & _PAGE_RW ) ? GNTPIN_hstw_inc + : GNTPIN_hstr_inc; + + /* Delete pagetable entry + */ + if ( unlikely(__put_user(0, (unsigned long *)pl1e))) + { + DPRINTK("Cannot delete PTE entry at %x for virtual address %x\n", + pl1e, virt); + rc = -EINVAL; + goto fail; + } + rc = 0; + *va = virt; } + /* If the last writable mapping has been removed, put_page_type */ if ( ((act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) == 0) && !(map->ref_and_flags & GNTMAP_readonly) ) { @@ -367,15 +493,24 @@ __gnttab_unmap_grant_ref( (void)__put_user(rc, &uop->status); spin_unlock(&rd->grant_table->lock); put_domain(rd); + return rc; } static long gnttab_unmap_grant_ref( gnttab_unmap_grant_ref_t *uop, unsigned int count) { - int i; + int i, flush = 0; + unsigned long va = 0; + for ( i = 0; i < count; i++ ) - __gnttab_unmap_grant_ref(&uop[i]); + if ( __gnttab_unmap_grant_ref(&uop[i], &va) == 0) + flush++; + + if ( flush == 1 ) + __flush_tlb_one(va); + else if ( flush ) + local_flush_tlb(); return 0; } @@ -423,7 +558,7 @@ gnttab_setup_table( { ASSERT(d->grant_table != NULL); (void)put_user(GNTST_okay, &uop->status); - (void)put_user(virt_to_phys(d->grant_table) >> PAGE_SHIFT, + (void)put_user(virt_to_phys(d->grant_table->shared) >> PAGE_SHIFT, &uop->frame_list[0]); } @@ -431,14 +566,99 @@ gnttab_setup_table( return 0; } +#ifdef GRANT_DEBUG +static int +gnttab_dump_table(gnttab_dump_table_t *uop) +{ + grant_table_t *gt; + gnttab_dump_table_t op; + struct domain *d; + u32 shared_mfn; + active_grant_entry_t *act; + grant_entry_t sha_copy; + grant_mapping_t *maptrack; + int i; + + + if ( unlikely(copy_from_user(&op, uop, sizeof(op)) != 0) ) + { + DPRINTK("Fault while reading gnttab_dump_table_t.\n"); + return -EFAULT; + } + + if ( op.dom == DOMID_SELF ) + { + op.dom = current->domain->id; + } + + if ( unlikely((d = find_domain_by_id(op.dom)) == NULL) ) + { + DPRINTK("Bad domid %d.\n", op.dom); + (void)put_user(GNTST_bad_domain, &uop->status); + return 0; + } + + ASSERT(d->grant_table != NULL); + gt = d->grant_table; + (void)put_user(GNTST_okay, &uop->status); + + shared_mfn = virt_to_phys(d->grant_table->shared); + + DPRINTK("Grant table for dom (%hu) MFN (%x)\n", + op.dom, shared_mfn); + + spin_lock(>->lock); + + ASSERT(d->grant_table->active != NULL); + ASSERT(d->grant_table->shared != NULL); + + for ( i = 0; i < NR_GRANT_ENTRIES; i++ ) + { + act = >->active[i]; + sha_copy = gt->shared[i]; + + if ( act->pin || act->domid || act->frame || + sha_copy.flags || sha_copy.domid || sha_copy.frame ) + { + DPRINTK("Grant: dom (%hu) ACTIVE (%d) pin:(%x) dom:(%hu) frame:(%u)\n", + op.dom, i, act->pin, act->domid, act->frame); + DPRINTK("Grant: dom (%hu) SHARED (%d) flags:(%hx) dom:(%hu) frame:(%u)\n", + op.dom, i, sha_copy.flags, sha_copy.domid, sha_copy.frame); + + } + + } + + ASSERT(d->grant_table->maptrack != NULL); + + for ( i = 0; i < NR_MAPTRACK_ENTRIES; i++ ) + { + maptrack = >->maptrack[i]; + + if ( maptrack->ref_and_flags & MAPTRACK_GNTMAP_MASK ) + { + DPRINTK("Grant: dom (%hu) MAP (%d) ref:(%hu) flags:(%x) dom:(%hu)\n", + op.dom, i, + maptrack->ref_and_flags >> MAPTRACK_REF_SHIFT, + maptrack->ref_and_flags & MAPTRACK_GNTMAP_MASK, + maptrack->domid); + } + } + + spin_unlock(>->lock); + + put_domain(d); + return 0; +} +#endif + long do_grant_table_op( unsigned int cmd, void *uop, unsigned int count) { long rc; - /* XXX stubbed out XXX */ - return -ENOSYS; + DPRINTK("Grant: table operation (%u) count: (%u)\n", cmd, count); if ( count > 512 ) return -EINVAL; @@ -462,6 +682,11 @@ do_grant_table_op( case GNTTABOP_setup_table: rc = gnttab_setup_table((gnttab_setup_table_t *)uop, count); break; +#ifdef GRANT_DEBUG + case GNTTABOP_dump_table: + rc = gnttab_dump_table((gnttab_dump_table_t *)uop); + break; +#endif default: rc = -ENOSYS; break; @@ -476,6 +701,11 @@ int gnttab_check_unmap( struct domain *rd, struct domain *ld, unsigned long frame, int readonly) { + /* TODO: beat the caller around the head with a brick. + * have to walk the grant tables to find this thing. + */ + /*DPRINTK("gnttab_check_unmap remote dom(%d) local dom(%d) frame (%x) flags(%x).\n", + rd->id, ld->id, frame, readonly);*/ return 0; } @@ -552,8 +782,17 @@ gnttab_prepare_for_transfer( void gnttab_notify_transfer( - struct domain *rd, grant_ref_t ref, unsigned long frame) + struct domain *rd, grant_ref_t ref, unsigned long sframe) { + unsigned long frame; + + /* cwc22 + * TODO: this requires that the machine_to_phys_mapping + * has already been updated, so the accept_transfer hypercall + * must do this. + */ + frame = __mfn_to_gpfn(rd, sframe); + wmb(); /* Ensure that the reassignment is globally visible. */ rd->grant_table->shared[ref].frame = frame; } @@ -607,6 +846,71 @@ grant_table_create( return -ENOMEM; } +static void +gnttab_release_all_mappings(grant_table_t *gt) +{ + grant_mapping_t *map; + domid_t dom; + grant_ref_t ref; + u16 handle; + u32 pincount; + struct domain *ld, *rd; + unsigned long frame; + active_grant_entry_t *act; + grant_entry_t *sha; + + ld = current->domain; + + for ( handle = 0; handle < NR_MAPTRACK_ENTRIES; handle++ ) + { + map = >->maptrack[handle]; + + if ( map->ref_and_flags & MAPTRACK_GNTMAP_MASK ) + { + dom = map->domid; + ref = map->ref_and_flags >> MAPTRACK_REF_SHIFT; + + DPRINTK("Grant release (%hu) ref:(%hu) flags:(%x) dom:(%hu)\n", + handle, ref, + map->ref_and_flags & MAPTRACK_GNTMAP_MASK, dom); + + if ( unlikely((rd = find_domain_by_id(dom)) == NULL) || + unlikely(ld == rd) ) + { + if ( rd != NULL ) + put_domain(rd); + + printk(KERN_WARNING "Grant release: Could not find domain %d\n", dom); + continue; + } + + act = &rd->grant_table->active[ref]; + sha = &rd->grant_table->shared[ref]; + + spin_lock(&rd->grant_table->lock); + + frame = act->frame; + + pincount = ((act->pin & GNTPIN_hstw_mask) >> GNTPIN_hstw_shift) + + ((act->pin & GNTPIN_devw_mask) >> GNTPIN_devw_shift); + + if ( pincount > 0 ) + put_page_types(&frame_table[frame], pincount); + + put_page(&frame_table[frame]); + + act->pin = 0; + + clear_bit(_GTF_reading, &sha->flags); + clear_bit(_GTF_writing, &sha->flags); + + spin_unlock(&rd->grant_table->lock); + + put_domain(rd); + } + } +} + void grant_table_destroy( struct domain *d) @@ -615,6 +919,9 @@ grant_table_destroy( if ( (t = d->grant_table) != NULL ) { + if ( t->maptrack != NULL ) + gnttab_release_all_mappings(t); + /* Free memory relating to this grant table. */ d->grant_table = NULL; free_xenheap_page((unsigned long)t->shared); @@ -629,6 +936,7 @@ grant_table_init( void) { /* Nothing. */ + DPRINTK("Grant table init\n"); } /* diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h index 3e4b1d4b0b..f2332908e7 100644 --- a/xen/include/asm-x86/mm.h +++ b/xen/include/asm-x86/mm.h @@ -182,9 +182,14 @@ static inline int get_page(struct pfn_info *page, return 1; } -void put_page_type(struct pfn_info *page); +void put_page_types(struct pfn_info *page, u32 decrement); int get_page_type(struct pfn_info *page, u32 type); +static inline void put_page_type(struct pfn_info *page) +{ + put_page_types(page, 1); +} + static inline void put_page_and_type(struct pfn_info *page) { put_page_type(page); @@ -336,4 +341,14 @@ void audit_domains(void); void propagate_page_fault(unsigned long addr, u16 error_code); +/* update_grant_va_mapping + * Caller must own d's BIGLOCK, is responsible for flushing the TLB, + * and have already get_page'd */ +int update_grant_va_mapping(unsigned long va, + unsigned long val, + struct domain *d, + struct exec_domain *ed); +#define GNTUPDVA_prev_ro 1 +#define GNTUPDVA_prev_rw 2 + #endif /* __ASM_X86_MM_H__ */ diff --git a/xen/include/asm-x86/shadow.h b/xen/include/asm-x86/shadow.h index 59f5f12fb4..bbf59f3d1e 100644 --- a/xen/include/asm-x86/shadow.h +++ b/xen/include/asm-x86/shadow.h @@ -61,6 +61,62 @@ extern void vmx_shadow_clear_state(struct domain *); ? phys_to_machine_mapping(gpfn) \ : (gpfn) ) +#define __translate_gpfn_to_mfn(_d, gpfn) \ + ( (shadow_mode_translate(_d)) \ + ? translate_gpfn_to_mfn(_d, gpfn) \ + : (gpfn) ) + +static inline unsigned long +translate_gpfn_to_mfn(struct domain *rd, unsigned long gpfn) +{ + unsigned long ma_of_phys_to_mach; + l2_pgentry_t *l2_table; + l2_pgentry_t l2_entry; + unsigned long ma_of_l1_table; + l1_pgentry_t *l1_table; + l1_pgentry_t pte; + unsigned long mfn = 0; + + /* + * translation of: (domain, gpfn) -> mfn + * where domain != current, and is in translate shadow mode + */ + + ASSERT( shadow_mode_translate(rd) ); + + shadow_lock(rd); + + /* TODO: check using shadow_lock is correct + * TODO: move arch.phys_table from exec_domain to domain + * - use of zero index is a hack - FIXME + */ + + ma_of_phys_to_mach = pagetable_val( (rd->exec_domain[0])->arch.phys_table ); + + l2_table = (l2_pgentry_t *) map_domain_mem( ma_of_phys_to_mach ); + l2_entry = l2_table[ gpfn >> (L2_PAGETABLE_SHIFT - PAGE_SHIFT) ]; + + unmap_domain_mem( l2_table ); + + if ( l2_pgentry_val(l2_entry) == 0 ) + goto unlock_out; + + ma_of_l1_table = l2_pgentry_to_phys( l2_entry ); + + l1_table = (l1_pgentry_t *) map_domain_mem( ma_of_l1_table ); + pte = l1_table[ (gpfn >> (L1_PAGETABLE_SHIFT - PAGE_SHIFT)) & + (L1_PAGETABLE_ENTRIES - 1 ) ]; + + unmap_domain_mem( l1_table ); + + mfn = l1_pgentry_to_pfn(pte); + +unlock_out: + shadow_unlock(rd); + + return mfn; +} + extern void __shadow_mode_disable(struct domain *d); static inline void shadow_mode_disable(struct domain *d) { diff --git a/xen/include/public/grant_table.h b/xen/include/public/grant_table.h index 389e082646..b00642db7c 100644 --- a/xen/include/public/grant_table.h +++ b/xen/include/public/grant_table.h @@ -206,6 +206,19 @@ typedef struct { MEMORY_PADDING; } PACKED gnttab_setup_table_t; /* 16 bytes */ +/* + * GNTTABOP_dump_table: Dump the contents of the grant table to the + * xen console. Debugging use only. + */ +#define GNTTABOP_dump_table 3 +typedef struct { + /* IN parameters. */ + domid_t dom; /* 0 */ + /* OUT parameters. */ + s16 status; /* 2: GNTST_* */ +} PACKED gnttab_dump_table_t; /* 4 bytes */ + + /* * Bitfield values for update_pin_status.flags. */ @@ -233,9 +246,10 @@ typedef struct { #define GNTST_general_error (-1) /* General undefined error. */ #define GNTST_bad_domain (-2) /* Unrecognsed domain id. */ #define GNTST_bad_gntref (-3) /* Unrecognised or inappropriate gntref. */ -#define GNTST_bad_handle (-3) /* Unrecognised or inappropriate handle. */ -#define GNTST_no_device_space (-4) /* Out of space in I/O MMU. */ -#define GNTST_permission_denied (-5) /* Not enough privilege for operation. */ +#define GNTST_bad_handle (-4) /* Unrecognised or inappropriate handle. */ +#define GNTST_bad_virt_addr (-5) /* Inappropriate virtual address to map. */ +#define GNTST_no_device_space (-6) /* Out of space in I/O MMU. */ +#define GNTST_permission_denied (-7) /* Not enough privilege for operation. */ #define GNTTABOP_error_msgs { \ "okay", \ @@ -243,9 +257,20 @@ typedef struct { "unrecognised domain id", \ "invalid grant reference", \ "invalid mapping handle", \ + "invalid virtual address", \ "no spare translation slot in the I/O MMU", \ "permission denied" \ } + +typedef struct { + union { /* 0 */ + gnttab_map_grant_ref_t map_grant_ref; + gnttab_unmap_grant_ref_t unmap_grant_ref; + gnttab_setup_table_t setup_table; + gnttab_dump_table_t dump_table; + u8 __dummy[24]; + } PACKED u; +} PACKED gnttab_op_t; /* 32 bytes */ #endif /* __XEN_PUBLIC_GRANT_TABLE_H__ */