ia64/xen-unstable

changeset 10816:7be1cfe8345b

[IA64] iomem support for driver domains.

First steps in hypevisor to support driver domains.

IO ports capabilities added (not yet used).
IO mem capabilities checked.
ASSIGN_nocache flag added.
Memory attributes checked.

Signed-off-by: Tristan Gingold <tristan.gingold@bull.net>
author awilliam@xenbuild.aw
date Thu Jul 27 09:47:10 2006 -0600 (2006-07-27)
parents 48d7d00e69e5
children 14f8c17c9808
files xen/arch/ia64/xen/dom0_ops.c xen/arch/ia64/xen/domain.c xen/arch/ia64/xen/mm.c xen/include/asm-ia64/domain.h xen/include/asm-ia64/iocap.h xen/include/public/arch-ia64.h
line diff
     1.1 --- a/xen/arch/ia64/xen/dom0_ops.c	Thu Jul 27 09:17:54 2006 -0600
     1.2 +++ b/xen/arch/ia64/xen/dom0_ops.c	Thu Jul 27 09:47:10 2006 -0600
     1.3 @@ -20,6 +20,7 @@
     1.4  #include <public/sched_ctl.h>
     1.5  #include <asm/vmx.h>
     1.6  #include <asm/dom_fw.h>
     1.7 +#include <xen/iocap.h>
     1.8  
     1.9  void build_physmap_table(struct domain *d);
    1.10  
    1.11 @@ -279,6 +280,29 @@ long arch_do_dom0_op(dom0_op_t *op, XEN_
    1.12      }
    1.13      break;
    1.14  
    1.15 +    case DOM0_IOPORT_PERMISSION:
    1.16 +    {
    1.17 +        struct domain *d;
    1.18 +        unsigned int fp = op->u.ioport_permission.first_port;
    1.19 +        unsigned int np = op->u.ioport_permission.nr_ports;
    1.20 +
    1.21 +        ret = -ESRCH;
    1.22 +        d = find_domain_by_id(op->u.ioport_permission.domain);
    1.23 +        if (unlikely(d == NULL))
    1.24 +            break;
    1.25 +
    1.26 +        if (np == 0)
    1.27 +            ret = 0;
    1.28 +        else {
    1.29 +            if (op->u.ioport_permission.allow_access)
    1.30 +                ret = ioports_permit_access(d, fp, fp + np - 1);
    1.31 +            else
    1.32 +                ret = ioports_deny_access(d, fp, fp + np - 1);
    1.33 +        }
    1.34 +
    1.35 +        put_domain(d);
    1.36 +    }
    1.37 +    break;
    1.38      default:
    1.39          printf("arch_do_dom0_op: unrecognized dom0 op: %d!!!\n",op->cmd);
    1.40          ret = -ENOSYS;
    1.41 @@ -289,6 +313,24 @@ long arch_do_dom0_op(dom0_op_t *op, XEN_
    1.42  }
    1.43  
    1.44  #ifdef CONFIG_XEN_IA64_DOM0_VP
    1.45 +static unsigned long
    1.46 +dom0vp_ioremap(struct domain *d, unsigned long mpaddr, unsigned long size)
    1.47 +{
    1.48 +    unsigned long end;
    1.49 +
    1.50 +    /* Linux may use a 0 size!  */
    1.51 +    if (size == 0)
    1.52 +        size = PAGE_SIZE;
    1.53 +
    1.54 +    end = PAGE_ALIGN(mpaddr + size);
    1.55 +
    1.56 +    if (!iomem_access_permitted(d, mpaddr >> PAGE_SHIFT,
    1.57 +                                (end >> PAGE_SHIFT) - 1))
    1.58 +        return -EPERM;
    1.59 +
    1.60 +    return assign_domain_mmio_page(d, mpaddr, size);
    1.61 +}
    1.62 +
    1.63  unsigned long
    1.64  do_dom0vp_op(unsigned long cmd,
    1.65               unsigned long arg0, unsigned long arg1, unsigned long arg2,
    1.66 @@ -299,7 +341,7 @@ do_dom0vp_op(unsigned long cmd,
    1.67  
    1.68      switch (cmd) {
    1.69      case IA64_DOM0VP_ioremap:
    1.70 -        ret = assign_domain_mmio_page(d, arg0, arg1);
    1.71 +        ret = dom0vp_ioremap(d, arg0, arg1);
    1.72          break;
    1.73      case IA64_DOM0VP_phystomach:
    1.74          ret = ____lookup_domain_mpa(d, arg0 << PAGE_SHIFT);
     2.1 --- a/xen/arch/ia64/xen/domain.c	Thu Jul 27 09:17:54 2006 -0600
     2.2 +++ b/xen/arch/ia64/xen/domain.c	Thu Jul 27 09:47:10 2006 -0600
     2.3 @@ -360,6 +360,9 @@ int arch_domain_create(struct domain *d)
     2.4  	if ((d->arch.mm.pgd = pgd_alloc(&d->arch.mm)) == NULL)
     2.5  	    goto fail_nomem;
     2.6  
     2.7 +	d->arch.ioport_caps = rangeset_new(d, "I/O Ports",
     2.8 +	                                   RANGESETF_prettyprint_hex);
     2.9 +
    2.10  	printf ("arch_domain_create: domain=%p\n", d);
    2.11  	return 0;
    2.12  
    2.13 @@ -885,6 +888,8 @@ static void physdev_init_dom0(struct dom
    2.14  		BUG();
    2.15  	if (irqs_permit_access(d, 0, NR_IRQS-1))
    2.16  		BUG();
    2.17 +	if (ioports_permit_access(d, 0, 0xffff))
    2.18 +		BUG();
    2.19  }
    2.20  
    2.21  int construct_dom0(struct domain *d, 
     3.1 --- a/xen/arch/ia64/xen/mm.c	Thu Jul 27 09:17:54 2006 -0600
     3.2 +++ b/xen/arch/ia64/xen/mm.c	Thu Jul 27 09:47:10 2006 -0600
     3.3 @@ -418,13 +418,13 @@ u64 translate_domain_pte(u64 pteval, u64
     3.4  	u64 mask, mpaddr, pteval2;
     3.5  	u64 arflags;
     3.6  	u64 arflags2;
     3.7 +	u64 maflags2;
     3.8  
     3.9  	pteval &= ((1UL << 53) - 1);// ignore [63:53] bits
    3.10  
    3.11  	// FIXME address had better be pre-validated on insert
    3.12  	mask = ~itir_mask(itir.itir);
    3.13 -	mpaddr = (((pteval & ~_PAGE_ED) & _PAGE_PPN_MASK) & ~mask) |
    3.14 -	         (address & mask);
    3.15 +	mpaddr = ((pteval & _PAGE_PPN_MASK) & ~mask) | (address & mask);
    3.16  #ifdef CONFIG_XEN_IA64_DOM0_VP
    3.17  	if (itir.ps > PAGE_SHIFT) {
    3.18  		itir.ps = PAGE_SHIFT;
    3.19 @@ -454,6 +454,8 @@ u64 translate_domain_pte(u64 pteval, u64
    3.20  	}
    3.21  #endif
    3.22  	pteval2 = lookup_domain_mpa(d, mpaddr, entry);
    3.23 +
    3.24 +	/* Check access rights.  */
    3.25  	arflags  = pteval  & _PAGE_AR_MASK;
    3.26  	arflags2 = pteval2 & _PAGE_AR_MASK;
    3.27  	if (arflags != _PAGE_AR_R && arflags2 == _PAGE_AR_R) {
    3.28 @@ -466,36 +468,53 @@ u64 translate_domain_pte(u64 pteval, u64
    3.29  		        pteval2, arflags2, mpaddr);
    3.30  #endif
    3.31  		pteval = (pteval & ~_PAGE_AR_MASK) | _PAGE_AR_R;
    3.32 -    }
    3.33 +	}
    3.34  
    3.35 -	pteval2 &= _PAGE_PPN_MASK; // ignore non-addr bits
    3.36 -	pteval2 |= (pteval & _PAGE_ED);
    3.37 -	pteval2 |= _PAGE_PL_2; // force PL0->2 (PL3 is unaffected)
    3.38 -	pteval2 |= (pteval & ~_PAGE_PPN_MASK);
    3.39 -	/*
    3.40 -	 * Don't let non-dom0 domains map uncached addresses.  This can
    3.41 -	 * happen when domU tries to touch i/o port space.  Also prevents
    3.42 -	 * possible address aliasing issues.
    3.43 -	 * WB => WB
    3.44 -	 * UC, UCE, WC => WB
    3.45 -	 * NaTPage => NaTPage
    3.46 -	 */
    3.47 -	if (d != dom0 && (pteval2 & _PAGE_MA_MASK) != _PAGE_MA_NAT)
    3.48 -		pteval2 &= ~_PAGE_MA_MASK;
    3.49 +	/* Check memory attribute. The switch is on the *requested* memory
    3.50 +	   attribute.  */
    3.51 +	maflags2 = pteval2 & _PAGE_MA_MASK;
    3.52 +	switch (pteval & _PAGE_MA_MASK) {
    3.53 +	case _PAGE_MA_NAT:
    3.54 +		/* NaT pages are always accepted!  */                
    3.55 +		break;
    3.56 +	case _PAGE_MA_UC:
    3.57 +	case _PAGE_MA_UCE:
    3.58 +	case _PAGE_MA_WC:
    3.59 +		if (maflags2 == _PAGE_MA_WB) {
    3.60 +			/* Don't let domains WB-map uncached addresses.
    3.61 +			   This can happen when domU tries to touch i/o
    3.62 +			   port space.  Also prevents possible address
    3.63 +			   aliasing issues.  */
    3.64 +			printf("Warning: UC to WB for mpaddr=%lx\n", mpaddr);
    3.65 +			pteval = (pteval & ~_PAGE_MA_MASK) | _PAGE_MA_WB;
    3.66 +		}
    3.67 +		break;
    3.68 +	case _PAGE_MA_WB:
    3.69 +		if (maflags2 != _PAGE_MA_WB) {
    3.70 +			/* Forbid non-coherent access to coherent memory. */
    3.71 +			panic_domain(NULL, "try to use WB mem attr on "
    3.72 +			             "UC page, mpaddr=%lx\n", mpaddr);
    3.73 +		}
    3.74 +		break;
    3.75 +	default:
    3.76 +		panic_domain(NULL, "try to use unknown mem attribute\n");
    3.77 +	}
    3.78  
    3.79 -    /* If shadow mode is enabled, virtualize dirty bit.  */
    3.80 -    if (shadow_mode_enabled(d) && (pteval2 & _PAGE_D)) {
    3.81 -        u64 mp_page = mpaddr >> PAGE_SHIFT;
    3.82 -        pteval2 |= _PAGE_VIRT_D;
    3.83 +	/* If shadow mode is enabled, virtualize dirty bit.  */
    3.84 +	if (shadow_mode_enabled(d) && (pteval & _PAGE_D)) {
    3.85 +		u64 mp_page = mpaddr >> PAGE_SHIFT;
    3.86 +		pteval |= _PAGE_VIRT_D;
    3.87  
    3.88 -        /* If the page is not already dirty, don't set the dirty bit.
    3.89 -           This is a small optimization!  */
    3.90 -        if (mp_page < d->arch.shadow_bitmap_size * 8
    3.91 -            && !test_bit(mp_page, d->arch.shadow_bitmap))
    3.92 -            pteval2 = (pteval2 & ~_PAGE_D);
    3.93 -    }
    3.94 -
    3.95 -	return pteval2;
    3.96 +		/* If the page is not already dirty, don't set the dirty bit! */
    3.97 +		if (mp_page < d->arch.shadow_bitmap_size * 8
    3.98 +    		    && !test_bit(mp_page, d->arch.shadow_bitmap))
    3.99 +    			pteval &= ~_PAGE_D;
   3.100 +	}
   3.101 +    
   3.102 +	/* Ignore non-addr bits of pteval2 and force PL0->2
   3.103 +	   (PL3 is unaffected) */
   3.104 +	return (pteval & ~_PAGE_PPN_MASK) |
   3.105 +	       (pteval2 & _PAGE_PPN_MASK) | _PAGE_PL_2;
   3.106  }
   3.107  
   3.108  // given a current domain metaphysical address, return the physical address
   3.109 @@ -823,8 +842,19 @@ assign_new_domain0_page(struct domain *d
   3.110  #endif
   3.111  }
   3.112  
   3.113 +static unsigned long
   3.114 +flags_to_prot (unsigned long flags)
   3.115 +{
   3.116 +    unsigned long res = _PAGE_PL_2 | __DIRTY_BITS;
   3.117 +
   3.118 +    res |= flags & ASSIGN_readonly ? _PAGE_AR_R: _PAGE_AR_RWX;
   3.119 +    res |= flags & ASSIGN_nocache ? _PAGE_MA_UC: _PAGE_MA_WB;
   3.120 +    
   3.121 +    return res;
   3.122 +}
   3.123 +
   3.124  /* map a physical address to the specified metaphysical addr */
   3.125 -// flags: currently only ASSIGN_readonly
   3.126 +// flags: currently only ASSIGN_readonly, ASSIGN_nocache
   3.127  // This is called by assign_domain_mmio_page().
   3.128  // So accessing to pte is racy.
   3.129  void
   3.130 @@ -836,13 +866,12 @@ void
   3.131      pte_t old_pte;
   3.132      pte_t new_pte;
   3.133      pte_t ret_pte;
   3.134 -    unsigned long arflags = (flags & ASSIGN_readonly)? _PAGE_AR_R: _PAGE_AR_RWX;
   3.135 +    unsigned long prot = flags_to_prot(flags);
   3.136  
   3.137      pte = lookup_alloc_domain_pte(d, mpaddr);
   3.138  
   3.139      old_pte = __pte(0);
   3.140 -    new_pte = pfn_pte(physaddr >> PAGE_SHIFT,
   3.141 -                      __pgprot(__DIRTY_BITS | _PAGE_PL_2 | arflags));
   3.142 +    new_pte = pfn_pte(physaddr >> PAGE_SHIFT, __pgprot(prot));
   3.143      ret_pte = ptep_cmpxchg_rel(&d->arch.mm, mpaddr, pte, old_pte, new_pte);
   3.144      if (pte_val(ret_pte) == pte_val(old_pte))
   3.145          smp_mb();
   3.146 @@ -941,7 +970,7 @@ assign_domain_mmio_page(struct domain *d
   3.147                  __func__, __LINE__, d, mpaddr, size);
   3.148          return -EINVAL;
   3.149      }
   3.150 -    assign_domain_same_page(d, mpaddr, size, ASSIGN_writable);
   3.151 +    assign_domain_same_page(d, mpaddr, size, ASSIGN_writable | ASSIGN_nocache);
   3.152      return mpaddr;
   3.153  }
   3.154  
   3.155 @@ -967,11 +996,12 @@ assign_domain_page_replace(struct domain
   3.156      volatile pte_t* pte;
   3.157      pte_t old_pte;
   3.158      pte_t npte;
   3.159 -    unsigned long arflags = (flags & ASSIGN_readonly)? _PAGE_AR_R: _PAGE_AR_RWX;
   3.160 +    unsigned long prot = flags_to_prot(flags);
   3.161 +
   3.162      pte = lookup_alloc_domain_pte(d, mpaddr);
   3.163  
   3.164      // update pte
   3.165 -    npte = pfn_pte(mfn, __pgprot(__DIRTY_BITS | _PAGE_PL_2 | arflags));
   3.166 +    npte = pfn_pte(mfn, __pgprot(prot));
   3.167      old_pte = ptep_xchg(mm, mpaddr, pte, npte);
   3.168      if (pte_mem(old_pte)) {
   3.169          unsigned long old_mfn = pte_pfn(old_pte);
   3.170 @@ -1013,7 +1043,7 @@ assign_domain_page_cmpxchg_rel(struct do
   3.171      unsigned long old_arflags;
   3.172      pte_t old_pte;
   3.173      unsigned long new_mfn;
   3.174 -    unsigned long new_arflags;
   3.175 +    unsigned long new_prot;
   3.176      pte_t new_pte;
   3.177      pte_t ret_pte;
   3.178  
   3.179 @@ -1029,10 +1059,9 @@ assign_domain_page_cmpxchg_rel(struct do
   3.180          return -EINVAL;
   3.181      }
   3.182  
   3.183 -    new_arflags = (flags & ASSIGN_readonly)? _PAGE_AR_R: _PAGE_AR_RWX;
   3.184 +    new_prot = flags_to_prot(flags);
   3.185      new_mfn = page_to_mfn(new_page);
   3.186 -    new_pte = pfn_pte(new_mfn,
   3.187 -                      __pgprot(__DIRTY_BITS | _PAGE_PL_2 | new_arflags));
   3.188 +    new_pte = pfn_pte(new_mfn, __pgprot(new_prot));
   3.189  
   3.190      // update pte
   3.191      ret_pte = ptep_cmpxchg_rel(mm, mpaddr, pte, old_pte, new_pte);
   3.192 @@ -1152,6 +1181,10 @@ dom0vp_add_physmap(struct domain* d, uns
   3.193      int error = 0;
   3.194      struct domain* rd;
   3.195  
   3.196 +    /* Not allowed by a domain.  */
   3.197 +    if (flags & ASSIGN_nocache)
   3.198 +        return -EINVAL;
   3.199 +
   3.200      rd = find_domain_by_id(domid);
   3.201      if (unlikely(rd == NULL)) {
   3.202          switch (domid) {
     4.1 --- a/xen/include/asm-ia64/domain.h	Thu Jul 27 09:17:54 2006 -0600
     4.2 +++ b/xen/include/asm-ia64/domain.h	Thu Jul 27 09:47:10 2006 -0600
     4.3 @@ -11,6 +11,7 @@
     4.4  #include <xen/list.h>
     4.5  #include <xen/cpumask.h>
     4.6  #include <asm/fpswa.h>
     4.7 +#include <xen/rangeset.h>
     4.8  
     4.9  struct p2m_entry {
    4.10      volatile pte_t*     pte;
    4.11 @@ -87,6 +88,9 @@ struct arch_domain {
    4.12          };
    4.13      };
    4.14  
    4.15 +    /* Allowed accesses to io ports.  */
    4.16 +    struct rangeset *ioport_caps;
    4.17 +
    4.18      /* There are two ranges of RID for a domain:
    4.19         one big range, used to virtualize domain RID,
    4.20         one small range for internal Xen use (metaphysical).  */
     5.1 --- a/xen/include/asm-ia64/iocap.h	Thu Jul 27 09:17:54 2006 -0600
     5.2 +++ b/xen/include/asm-ia64/iocap.h	Thu Jul 27 09:47:10 2006 -0600
     5.3 @@ -7,4 +7,11 @@
     5.4  #ifndef __IA64_IOCAP_H__
     5.5  #define __IA64_IOCAP_H__
     5.6  
     5.7 +#define ioports_permit_access(d, s, e)                  \
     5.8 +    rangeset_add_range((d)->arch.ioport_caps, s, e)
     5.9 +#define ioports_deny_access(d, s, e)                    \
    5.10 +    rangeset_remove_range((d)->arch.ioport_caps, s, e)
    5.11 +#define ioports_access_permitted(d, s, e)               \
    5.12 +    rangeset_contains_range((d)->arch.ioport_caps, s, e)
    5.13 +
    5.14  #endif /* __IA64_IOCAP_H__ */
     6.1 --- a/xen/include/public/arch-ia64.h	Thu Jul 27 09:17:54 2006 -0600
     6.2 +++ b/xen/include/public/arch-ia64.h	Thu Jul 27 09:47:10 2006 -0600
     6.3 @@ -359,6 +359,9 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_conte
     6.4  #define _ASSIGN_readonly                0
     6.5  #define ASSIGN_readonly                 (1UL << _ASSIGN_readonly)
     6.6  #define ASSIGN_writable                 (0UL << _ASSIGN_readonly) // dummy flag
     6.7 +/* Internal only: memory attribute must be WC/UC/UCE.  */
     6.8 +#define _ASSIGN_nocache                 1
     6.9 +#define ASSIGN_nocache                  (1UL << _ASSIGN_nocache)
    6.10  
    6.11  /* This structure has the same layout of struct ia64_boot_param, defined in
    6.12     <asm/system.h>.  It is redefined here to ease use.  */