ia64/xen-unstable

changeset 18686:6db3c096c244

[IA64] Add hypercalls needed for VTD

Add hypercalls needed for VTD.

Signed-off-by: Anthony Xu <anthony.xu@intel.com>
author Isaku Yamahata <yamahata@valinux.co.jp>
date Wed Oct 22 12:40:06 2008 +0900 (2008-10-22)
parents 46d7e12c4c91
children d9b70fc0ee0f
files xen/arch/ia64/xen/dom0_ops.c xen/arch/ia64/xen/hypercall.c xen/arch/ia64/xen/mm.c xen/include/asm-ia64/mm.h
line diff
     1.1 --- a/xen/arch/ia64/xen/dom0_ops.c	Wed Oct 22 11:46:55 2008 +0900
     1.2 +++ b/xen/arch/ia64/xen/dom0_ops.c	Wed Oct 22 12:40:06 2008 +0900
     1.3 @@ -18,6 +18,7 @@
     1.4  #include <xen/trace.h>
     1.5  #include <xen/console.h>
     1.6  #include <xen/guest_access.h>
     1.7 +#include <xen/pci.h>
     1.8  #include <asm/vmx.h>
     1.9  #include <asm/dom_fw.h>
    1.10  #include <asm/vhpt.h>
    1.11 @@ -256,6 +257,266 @@ long arch_do_domctl(xen_domctl_t *op, XE
    1.12      }
    1.13      break;
    1.14  
    1.15 +    case XEN_DOMCTL_get_device_group:
    1.16 +    {
    1.17 +        struct domain *d;
    1.18 +        u32 max_sdevs;
    1.19 +        u8 bus, devfn;
    1.20 +        XEN_GUEST_HANDLE_64(uint32) sdevs;
    1.21 +        int num_sdevs;
    1.22 +
    1.23 +        ret = -ENOSYS;
    1.24 +        if ( !iommu_enabled )
    1.25 +            break;
    1.26 +
    1.27 +        ret = -EINVAL;
    1.28 +        if ( (d = rcu_lock_domain_by_id(op->domain)) == NULL )
    1.29 +            break;
    1.30 +
    1.31 +        bus = (op->u.get_device_group.machine_bdf >> 16) & 0xff;
    1.32 +        devfn = (op->u.get_device_group.machine_bdf >> 8) & 0xff;
    1.33 +        max_sdevs = op->u.get_device_group.max_sdevs;
    1.34 +        sdevs = op->u.get_device_group.sdev_array;
    1.35 +
    1.36 +        num_sdevs = iommu_get_device_group(d, bus, devfn, sdevs, max_sdevs);
    1.37 +        if ( num_sdevs < 0 )
    1.38 +        {
    1.39 +            dprintk(XENLOG_ERR, "iommu_get_device_group() failed!\n");
    1.40 +            ret = -EFAULT;
    1.41 +            op->u.get_device_group.num_sdevs = 0;
    1.42 +        }
    1.43 +        else
    1.44 +        {
    1.45 +            ret = 0;
    1.46 +            op->u.get_device_group.num_sdevs = num_sdevs;
    1.47 +        }
    1.48 +        if ( copy_to_guest(u_domctl, op, 1) )
    1.49 +            ret = -EFAULT;
    1.50 +        rcu_unlock_domain(d);
    1.51 +    }
    1.52 +    break;
    1.53 +
    1.54 +    case XEN_DOMCTL_test_assign_device:
    1.55 +    {
    1.56 +        u8 bus, devfn;
    1.57 +
    1.58 +        ret = -ENOSYS;
    1.59 +        if ( !iommu_enabled )
    1.60 +            break;
    1.61 +
    1.62 +        ret = -EINVAL;
    1.63 +        bus = (op->u.assign_device.machine_bdf >> 16) & 0xff;
    1.64 +        devfn = (op->u.assign_device.machine_bdf >> 8) & 0xff;
    1.65 +
    1.66 +        if ( device_assigned(bus, devfn) )
    1.67 +        {
    1.68 +            printk( "XEN_DOMCTL_test_assign_device: "
    1.69 +                     "%x:%x:%x already assigned, or non-existent\n",
    1.70 +                     bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
    1.71 +            break;
    1.72 +        }
    1.73 +        ret = 0;
    1.74 +    }
    1.75 +    break;
    1.76 +
    1.77 +    case XEN_DOMCTL_assign_device:
    1.78 +    {
    1.79 +        struct domain *d;
    1.80 +        u8 bus, devfn;
    1.81 +
    1.82 +        ret = -ENOSYS;
    1.83 +        if ( !iommu_enabled )
    1.84 +            break;
    1.85 +
    1.86 +        ret = -EINVAL;
    1.87 +        if ( unlikely((d = get_domain_by_id(op->domain)) == NULL) )
    1.88 +        {
    1.89 +            gdprintk(XENLOG_ERR,
    1.90 +                "XEN_DOMCTL_assign_device: get_domain_by_id() failed\n");
    1.91 +            break;
    1.92 +        }
    1.93 +        bus = (op->u.assign_device.machine_bdf >> 16) & 0xff;
    1.94 +        devfn = (op->u.assign_device.machine_bdf >> 8) & 0xff;
    1.95 +
    1.96 +        if ( !iommu_pv_enabled && !is_hvm_domain(d) )
    1.97 +        {
    1.98 +            ret = -ENOSYS;
    1.99 +            break;
   1.100 +        }
   1.101 +
   1.102 +        if ( device_assigned(bus, devfn) )
   1.103 +        {
   1.104 +            gdprintk(XENLOG_ERR, "XEN_DOMCTL_assign_device: "
   1.105 +                     "%x:%x:%x already assigned, or non-existent\n",
   1.106 +                     bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
   1.107 +            break;
   1.108 +        }
   1.109 +
   1.110 +        ret = assign_device(d, bus, devfn);
   1.111 +        gdprintk(XENLOG_INFO, "XEN_DOMCTL_assign_device: bdf = %x:%x:%x\n",
   1.112 +                 bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
   1.113 +        put_domain(d);
   1.114 +    }
   1.115 +    break;
   1.116 +
   1.117 +    case XEN_DOMCTL_deassign_device:
   1.118 +    {
   1.119 +        struct domain *d;
   1.120 +        u8 bus, devfn;
   1.121 +
   1.122 +        ret = -ENOSYS;
   1.123 +        if ( !iommu_enabled )
   1.124 +            break;
   1.125 +
   1.126 +        ret = -EINVAL;
   1.127 +        if ( unlikely((d = get_domain_by_id(op->domain)) == NULL) )
   1.128 +        {
   1.129 +            gdprintk(XENLOG_ERR,
   1.130 +                "XEN_DOMCTL_deassign_device: get_domain_by_id() failed\n");
   1.131 +            break;
   1.132 +        }
   1.133 +        bus = (op->u.assign_device.machine_bdf >> 16) & 0xff;
   1.134 +        devfn = (op->u.assign_device.machine_bdf >> 8) & 0xff;
   1.135 +
   1.136 +        if ( !iommu_pv_enabled && !is_hvm_domain(d) )
   1.137 +        {
   1.138 +            ret = -ENOSYS;
   1.139 +            break;
   1.140 +        }
   1.141 +
   1.142 +        if ( !device_assigned(bus, devfn) )
   1.143 +            break;
   1.144 +
   1.145 +        ret = 0;
   1.146 +        deassign_device(d, bus, devfn);
   1.147 +        gdprintk(XENLOG_INFO, "XEN_DOMCTL_deassign_device: bdf = %x:%x:%x\n",
   1.148 +            bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
   1.149 +        put_domain(d);
   1.150 +    }
   1.151 +    break;
   1.152 +
   1.153 +    case XEN_DOMCTL_bind_pt_irq:
   1.154 +    {
   1.155 +        struct domain * d;
   1.156 +        xen_domctl_bind_pt_irq_t * bind;
   1.157 +
   1.158 +        ret = -ESRCH;
   1.159 +        if ( (d = rcu_lock_domain_by_id(op->domain)) == NULL )
   1.160 +            break;
   1.161 +        bind = &(op->u.bind_pt_irq);
   1.162 +        if ( iommu_enabled )
   1.163 +            ret = pt_irq_create_bind_vtd(d, bind);
   1.164 +        if ( ret < 0 )
   1.165 +            gdprintk(XENLOG_ERR, "pt_irq_create_bind failed!\n");
   1.166 +        rcu_unlock_domain(d);
   1.167 +    }
   1.168 +    break;
   1.169 +
   1.170 +    case XEN_DOMCTL_unbind_pt_irq:
   1.171 +    {
   1.172 +        struct domain * d;
   1.173 +        xen_domctl_bind_pt_irq_t * bind;
   1.174 +
   1.175 +        ret = -ESRCH;
   1.176 +        if ( (d = rcu_lock_domain_by_id(op->domain)) == NULL )
   1.177 +            break;
   1.178 +        bind = &(op->u.bind_pt_irq);
   1.179 +        if ( iommu_enabled )
   1.180 +            ret = pt_irq_destroy_bind_vtd(d, bind);
   1.181 +        if ( ret < 0 )
   1.182 +            gdprintk(XENLOG_ERR, "pt_irq_destroy_bind failed!\n");
   1.183 +        rcu_unlock_domain(d);
   1.184 +    }
   1.185 +    break;
   1.186 +
   1.187 +    case XEN_DOMCTL_memory_mapping:
   1.188 +    {
   1.189 +        struct domain *d;
   1.190 +        unsigned long gfn = op->u.memory_mapping.first_gfn;
   1.191 +        unsigned long mfn = op->u.memory_mapping.first_mfn;
   1.192 +        unsigned long nr_mfns = op->u.memory_mapping.nr_mfns;
   1.193 +        int i;
   1.194 +
   1.195 +        ret = -EINVAL;
   1.196 +        if ( (mfn + nr_mfns - 1) < mfn ) /* wrap? */
   1.197 +            break;
   1.198 +
   1.199 +        ret = -ESRCH;
   1.200 +        if ( unlikely((d = rcu_lock_domain_by_id(op->domain)) == NULL) )
   1.201 +            break;
   1.202 +
   1.203 +        ret=0;
   1.204 +        if ( op->u.memory_mapping.add_mapping )
   1.205 +        {
   1.206 +            gdprintk(XENLOG_INFO,
   1.207 +                "memory_map:add: gfn=%lx mfn=%lx nr_mfns=%lx\n",
   1.208 +                gfn, mfn, nr_mfns);
   1.209 +
   1.210 +            ret = iomem_permit_access(d, mfn, mfn + nr_mfns - 1);
   1.211 +            for ( i = 0; i < nr_mfns; i++ )
   1.212 +                assign_domain_mmio_page(d, (gfn+i)<<PAGE_SHIFT,
   1.213 +                           (mfn+i)<<PAGE_SHIFT, PAGE_SIZE,
   1.214 +                           ASSIGN_writable | ASSIGN_nocache);
   1.215 +        }
   1.216 +        else
   1.217 +        {
   1.218 +            gdprintk(XENLOG_INFO,
   1.219 +                "memory_map:remove: gfn=%lx mfn=%lx nr_mfns=%lx\n",
   1.220 +                 gfn, mfn, nr_mfns);
   1.221 +
   1.222 +            for ( i = 0; i < nr_mfns; i++ )
   1.223 +                deassign_domain_mmio_page(d, (gfn+i)<<PAGE_SHIFT,
   1.224 +                        (mfn+i)<<PAGE_SHIFT, PAGE_SIZE);
   1.225 +            ret = iomem_deny_access(d, mfn, mfn + nr_mfns - 1);
   1.226 +        }
   1.227 +
   1.228 +        rcu_unlock_domain(d);
   1.229 +    }
   1.230 +    break;
   1.231 +
   1.232 +    case XEN_DOMCTL_ioport_mapping:
   1.233 +    {
   1.234 +
   1.235 +#define MAX_IOPORTS    0x10000
   1.236 +        struct domain *d;
   1.237 +        unsigned int fgp = op->u.ioport_mapping.first_gport;
   1.238 +        unsigned int fmp = op->u.ioport_mapping.first_mport;
   1.239 +        unsigned int np = op->u.ioport_mapping.nr_ports;
   1.240 +
   1.241 +        ret = -EINVAL;
   1.242 +        if ( (np == 0) || (fgp > MAX_IOPORTS) || (fmp > MAX_IOPORTS) ||
   1.243 +            ((fgp + np) > MAX_IOPORTS) || ((fmp + np) > MAX_IOPORTS) )
   1.244 +        {
   1.245 +            gdprintk(XENLOG_ERR,
   1.246 +                "ioport_map:invalid:gport=%x mport=%x nr_ports=%x\n",
   1.247 +                fgp, fmp, np);
   1.248 +            break;
   1.249 +        }
   1.250 +
   1.251 +        ret = -ESRCH;
   1.252 +        if ( unlikely((d = rcu_lock_domain_by_id(op->domain)) == NULL) )
   1.253 +            break;
   1.254 +
   1.255 +        if ( op->u.ioport_mapping.add_mapping )
   1.256 +        {
   1.257 +            gdprintk(XENLOG_INFO,
   1.258 +                    "ioport_map:add f_gport=%x f_mport=%x np=%x\n",
   1.259 +                    fgp, fmp, np);
   1.260 +
   1.261 +            ret = ioports_permit_access(d, fgp, fmp, fmp + np - 1);
   1.262 +        }
   1.263 +        else
   1.264 +        {
   1.265 +            gdprintk(XENLOG_INFO,
   1.266 +                    "ioport_map:remove f_gport=%x f_mport=%x np=%x\n",
   1.267 +                    fgp, fmp, np);
   1.268 +
   1.269 +            ret = ioports_deny_access(d,  fgp, fgp + np - 1);
   1.270 +        }
   1.271 +        rcu_unlock_domain(d);
   1.272 +    }
   1.273 +    break;
   1.274 +
   1.275      case XEN_DOMCTL_sethvmcontext:
   1.276      { 
   1.277          struct hvm_domain_context c;
   1.278 @@ -388,10 +649,6 @@ long arch_do_domctl(xen_domctl_t *op, XE
   1.279      }
   1.280      break;
   1.281  
   1.282 -    case XEN_DOMCTL_assign_device:
   1.283 -        ret = -ENOSYS;
   1.284 -        break;
   1.285 -
   1.286      default:
   1.287          printk("arch_do_domctl: unrecognized domctl: %d!!!\n",op->cmd);
   1.288          ret = -ENOSYS;
     2.1 --- a/xen/arch/ia64/xen/hypercall.c	Wed Oct 22 11:46:55 2008 +0900
     2.2 +++ b/xen/arch/ia64/xen/hypercall.c	Wed Oct 22 12:40:06 2008 +0900
     2.3 @@ -35,6 +35,7 @@
     2.4  #include <public/arch-ia64/debug_op.h>
     2.5  #include <asm/sioemu.h>
     2.6  #include <public/arch-ia64/sioemu.h>
     2.7 +#include <xen/pci.h>
     2.8  
     2.9  static IA64FAULT
    2.10  xen_hypercall (struct pt_regs *regs)
    2.11 @@ -313,6 +314,21 @@ extern int
    2.12  iosapic_guest_write(
    2.13      unsigned long physbase, unsigned int reg, u32 pval);
    2.14  
    2.15 +
    2.16 +/*
    2.17 + * XXX We don't support MSI for PCI passthrough, so just return ENOSYS
    2.18 + */
    2.19 +static int physdev_map_pirq(struct physdev_map_pirq *map)
    2.20 +{
    2.21 +	return -ENOSYS;
    2.22 +}
    2.23 +
    2.24 +static int physdev_unmap_pirq(struct physdev_unmap_pirq *unmap)
    2.25 +{
    2.26 +	return -ENOSYS;
    2.27 +}
    2.28 +
    2.29 +
    2.30  long do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg)
    2.31  {
    2.32      int irq;
    2.33 @@ -426,18 +442,60 @@ long do_physdev_op(int cmd, XEN_GUEST_HA
    2.34          break;
    2.35      }
    2.36  
    2.37 -    /*
    2.38 -     * XXX We don't support MSI for PCI passthrough, so just return success
    2.39 -     */
    2.40 -    case PHYSDEVOP_map_pirq:
    2.41 -    case PHYSDEVOP_unmap_pirq:
    2.42 -        ret = 0;
    2.43 +	case PHYSDEVOP_map_pirq: {
    2.44 +        struct physdev_map_pirq map;
    2.45 +
    2.46 +        ret = -EFAULT;
    2.47 +        if ( copy_from_guest(&map, arg, 1) != 0 )
    2.48 +             break;
    2.49 +
    2.50 +        ret = physdev_map_pirq(&map);
    2.51 +
    2.52 +        if ( copy_to_guest(arg, &map, 1) != 0 )
    2.53 +             ret = -EFAULT;
    2.54          break;
    2.55 +    }
    2.56  
    2.57 -    case PHYSDEVOP_manage_pci_add:
    2.58 -    case PHYSDEVOP_manage_pci_remove:
    2.59 +    case PHYSDEVOP_unmap_pirq: {
    2.60 +        struct physdev_unmap_pirq unmap;
    2.61 +
    2.62 +        ret = -EFAULT;
    2.63 +        if ( copy_from_guest(&unmap, arg, 1) != 0 )
    2.64 +            break;
    2.65 +
    2.66 +        ret = physdev_unmap_pirq(&unmap);
    2.67 +            break;
    2.68 +    }
    2.69 +
    2.70 +    case PHYSDEVOP_manage_pci_add: {
    2.71 +        struct physdev_manage_pci manage_pci;
    2.72 +        ret = -EPERM;
    2.73 +        if ( !IS_PRIV(current->domain) )
    2.74 +            break;
    2.75 +        ret = -EFAULT;
    2.76 +        if ( copy_from_guest(&manage_pci, arg, 1) != 0 )
    2.77 +            break;
    2.78 +
    2.79 +        ret = pci_add_device(manage_pci.bus, manage_pci.devfn);
    2.80 +            break;
    2.81 +    }
    2.82 +
    2.83 +    case PHYSDEVOP_manage_pci_remove: {
    2.84 +        struct physdev_manage_pci manage_pci;
    2.85 +        ret = -EPERM;
    2.86 +        if ( !IS_PRIV(current->domain) )
    2.87 +            break;
    2.88 +        ret = -EFAULT;
    2.89 +        if ( copy_from_guest(&manage_pci, arg, 1) != 0 )
    2.90 +            break;
    2.91 +
    2.92 +        ret = pci_remove_device(manage_pci.bus, manage_pci.devfn);
    2.93 +            break;
    2.94 +    }
    2.95 +
    2.96      default:
    2.97          ret = -ENOSYS;
    2.98 +        printk("not implemented do_physdev_op: %d\n", cmd);
    2.99          break;
   2.100      }
   2.101  
     3.1 --- a/xen/arch/ia64/xen/mm.c	Wed Oct 22 11:46:55 2008 +0900
     3.2 +++ b/xen/arch/ia64/xen/mm.c	Wed Oct 22 12:40:06 2008 +0900
     3.3 @@ -1436,7 +1436,8 @@ zap_domain_page_one(struct domain *d, un
     3.4      again:
     3.5          // memory_exchange() calls guest_physmap_remove_page() with
     3.6          // a stealed page. i.e. page owner = NULL.
     3.7 -        BUG_ON(page_get_owner(mfn_to_page(mfn)) != d &&
     3.8 +        BUG_ON(mfn_valid(mfn) &&
     3.9 +               page_get_owner(mfn_to_page(mfn)) != d &&
    3.10                 page_get_owner(mfn_to_page(mfn)) != NULL);
    3.11          old_arflags = pte_val(*pte) & ~_PAGE_PPN_MASK;
    3.12          old_pte = pfn_pte(mfn, __pgprot(old_arflags));
    3.13 @@ -1459,12 +1460,39 @@ zap_domain_page_one(struct domain *d, un
    3.14          BUG_ON(mfn != pte_pfn(ret_pte));
    3.15      }
    3.16  
    3.17 +    perfc_incr(zap_domain_page_one);
    3.18 +    if(!mfn_valid(mfn))
    3.19 +        return;
    3.20 +
    3.21      page = mfn_to_page(mfn);
    3.22      BUG_ON((page->count_info & PGC_count_mask) == 0);
    3.23  
    3.24      BUG_ON(clear_PGC_allocate && (page_get_owner(page) == NULL));
    3.25      domain_put_page(d, mpaddr, pte, old_pte, clear_PGC_allocate);
    3.26 -    perfc_incr(zap_domain_page_one);
    3.27 +}
    3.28 +
    3.29 +int
    3.30 +deassign_domain_mmio_page(struct domain *d, unsigned long mpaddr,
    3.31 +                        unsigned long phys_addr, unsigned long size )
    3.32 +{
    3.33 +    unsigned long addr = mpaddr & PAGE_MASK;
    3.34 +    unsigned long end = PAGE_ALIGN(mpaddr + size);
    3.35 +
    3.36 +    if (size == 0) {
    3.37 +        gdprintk(XENLOG_INFO, "%s: domain %p mpaddr 0x%lx size = 0x%lx\n",
    3.38 +                __func__, d, mpaddr, size);
    3.39 +    }
    3.40 +    if (!efi_mmio(phys_addr, size)) {
    3.41 +#ifndef NDEBUG
    3.42 +        gdprintk(XENLOG_INFO, "%s: domain %p mpaddr 0x%lx size = 0x%lx\n",
    3.43 +                __func__, d, mpaddr, size);
    3.44 +#endif
    3.45 +        return -EINVAL;
    3.46 +    }
    3.47 +
    3.48 +    for (; addr < end; addr += PAGE_SIZE )
    3.49 +        zap_domain_page_one(d, addr, 0, INVALID_MFN);
    3.50 +    return 0;
    3.51  }
    3.52  
    3.53  unsigned long
     4.1 --- a/xen/include/asm-ia64/mm.h	Wed Oct 22 11:46:55 2008 +0900
     4.2 +++ b/xen/include/asm-ia64/mm.h	Wed Oct 22 12:40:06 2008 +0900
     4.3 @@ -428,6 +428,8 @@ extern void assign_new_domain0_page(stru
     4.4  extern int __assign_domain_page(struct domain *d, unsigned long mpaddr, unsigned long physaddr, unsigned long flags);
     4.5  extern void assign_domain_page(struct domain *d, unsigned long mpaddr, unsigned long physaddr);
     4.6  extern void assign_domain_io_page(struct domain *d, unsigned long mpaddr, unsigned long flags);
     4.7 +extern int deassign_domain_mmio_page(struct domain *d, unsigned long mpaddr,
     4.8 +                        unsigned long phys_addr, unsigned long size);
     4.9  struct p2m_entry;
    4.10  extern unsigned long lookup_domain_mpa(struct domain *d, unsigned long mpaddr, struct p2m_entry* entry);
    4.11  extern void *domain_mpa_to_imva(struct domain *d, unsigned long mpaddr);