ia64/xen-unstable

changeset 15322:601509daabfc

[IA64] Support for multiple I/O port spaces

Linux has support for multiple I/O port spaces on a system. Depending
on the system configuration, this might allow for 64k of I/O port space
per PCI bus. The "extended" I/O port ranges are described to the OS by
_CRS methods on PCI root devices in ACPI namespace. For instance, on my
system, /proc/iomem shows several entries like these:

80000000000-80003ffffff : PCI Bus 0000:00 I/O Ports 01000000-0100ffff
80100000000-80103ffffff : PCI Bus 0000:01 I/O Ports 02000000-0200ffff
80200000000-80203ffffff : PCI Bus 0000:0a I/O Ports 03000000-0300ffff
80300000000-80303ffffff : PCI Bus 0000:0f I/O Ports 04000000-0400ffff
...

These describe MMIO ranges that provide I/O port ranges per PCI bus.
My /proc/ioports then shows entries like these:

01000000-0100ffff : PCI Bus 0000:00
01001000-010010ff : 0000:00:04.0
02000000-0200ffff : PCI Bus 0000:01
02001000-02001fff : PCI Bus #02
02001000-0200107f : 0000:02:07.0
02001000-0200107f : tulip
02001080-020010ff : 0000:02:06.0
02001080-020010ff : tulip
02001100-0200117f : 0000:02:05.0
02001180-020011ff : 0000:02:04.0
02001180-020011ff : tulip
02002000-0200203f : 0000:01:02.1
02002040-0200207f : 0000:01:02.0
02002040-0200207f : e1000
03000000-0300ffff : PCI Bus 0000:0a
...

Xen currently has no concept of these extended I/O port space ranges and
prevents dom0 from doing the MMIO transactions required to access these
ports. This patch solves the problem. First we need to make
ioports_permit/deny_access() aware that multiple I/O port ranges with
different MMIO base address exist. Next we need to provide a mechanism
to register new I/O port spaces, for this I've created the dom0vp op
IA64_DOM0VP_add_io_space. This allows dom0 to do the work of finding
the ranges in ACPI namespace since we don't want to add that kind of
bulk to Xen. Finally, dom0 needs to make use of this interface when new
ranges are found.

Signed-off-by: Alex Williamson <alex.williamson@hp.com>
author Alex Williamson <alex.williamson@hp.com>
date Tue Jun 12 15:07:05 2007 -0600 (2007-06-12)
parents ba1f933f2382
children 171ec463e7ce
files xen/arch/ia64/xen/dom0_ops.c xen/arch/ia64/xen/mm.c xen/include/asm-ia64/iocap.h xen/include/public/arch-ia64.h
line diff
     1.1 --- a/xen/arch/ia64/xen/dom0_ops.c	Tue Jun 12 14:58:39 2007 -0600
     1.2 +++ b/xen/arch/ia64/xen/dom0_ops.c	Tue Jun 12 15:07:05 2007 -0600
     1.3 @@ -363,6 +363,40 @@ dom0vp_fpswa_revision(XEN_GUEST_HANDLE(u
     1.4      return 0;
     1.5  }
     1.6  
     1.7 +static unsigned long
     1.8 +dom0vp_add_io_space(struct domain *d, unsigned long phys_base,
     1.9 +                    unsigned long sparse, unsigned long space_number)
    1.10 +{
    1.11 +    unsigned int fp, lp;
    1.12 +
    1.13 +    /*
    1.14 +     * Registering new io_space roughly based on linux
    1.15 +     * arch/ia64/pci/pci.c:new_space()
    1.16 +     */
    1.17 +
    1.18 +    /* Skip legacy I/O port space, we already know about it */
    1.19 +    if (phys_base == 0)
    1.20 +        return 0;
    1.21 +
    1.22 +    /*
    1.23 +     * Dom0 Linux initializes io spaces sequentially, if that changes,
    1.24 +     * we'll need to add thread protection and the ability to handle
    1.25 +     * a sparsely populated io_space array.
    1.26 +     */
    1.27 +    if (space_number > MAX_IO_SPACES || space_number != num_io_spaces)
    1.28 +        return -EINVAL;
    1.29 +
    1.30 +    io_space[space_number].mmio_base = phys_base;
    1.31 +    io_space[space_number].sparse = sparse;
    1.32 +
    1.33 +    num_io_spaces++;
    1.34 +
    1.35 +    fp = space_number << IO_SPACE_BITS;
    1.36 +    lp = fp | 0xffff;
    1.37 +
    1.38 +    return ioports_permit_access(d, fp, lp);
    1.39 +}
    1.40 +
    1.41  unsigned long
    1.42  do_dom0vp_op(unsigned long cmd,
    1.43               unsigned long arg0, unsigned long arg1, unsigned long arg2,
    1.44 @@ -419,6 +453,9 @@ do_dom0vp_op(unsigned long cmd,
    1.45          ret = dom0vp_fpswa_revision(hnd);
    1.46          break;
    1.47      }
    1.48 +    case IA64_DOM0VP_add_io_space:
    1.49 +        ret = dom0vp_add_io_space(d, arg0, arg1, arg2);
    1.50 +        break;
    1.51      default:
    1.52          ret = -1;
    1.53  		printk("unknown dom0_vp_op 0x%lx\n", cmd);
     2.1 --- a/xen/arch/ia64/xen/mm.c	Tue Jun 12 14:58:39 2007 -0600
     2.2 +++ b/xen/arch/ia64/xen/mm.c	Tue Jun 12 15:07:05 2007 -0600
     2.3 @@ -890,81 +890,144 @@ assign_domain_page(struct domain *d,
     2.4  }
     2.5  
     2.6  int
     2.7 -ioports_permit_access(struct domain *d, unsigned long fp, unsigned long lp)
     2.8 +ioports_permit_access(struct domain *d, unsigned int fp, unsigned int lp)
     2.9  {
    2.10 +    struct io_space *space;
    2.11 +    unsigned long mmio_start, mmio_end, mach_start;
    2.12      int ret;
    2.13 -    unsigned long off;
    2.14 -    unsigned long fp_offset;
    2.15 -    unsigned long lp_offset;
    2.16 +
    2.17 +    if (IO_SPACE_NR(fp) >= num_io_spaces) {
    2.18 +        dprintk(XENLOG_WARNING, "Unknown I/O Port range 0x%x - 0x%x\n", fp, lp);
    2.19 +        return -EFAULT;
    2.20 +    }
    2.21  
    2.22 +    /*
    2.23 +     * The ioport_cap rangeset tracks the I/O port address including
    2.24 +     * the port space ID.  This means port space IDs need to match
    2.25 +     * between Xen and dom0.  This is also a requirement because
    2.26 +     * the hypercall to pass these port ranges only uses a u32.
    2.27 +     *
    2.28 +     * NB - non-dom0 driver domains may only have a subset of the
    2.29 +     * I/O port spaces and thus will number port spaces differently.
    2.30 +     * This is ok, they don't make use of this interface.
    2.31 +     */
    2.32      ret = rangeset_add_range(d->arch.ioport_caps, fp, lp);
    2.33      if (ret != 0)
    2.34          return ret;
    2.35  
    2.36 -    /* Domain 0 doesn't virtualize IO ports space. */
    2.37 -    if (d == dom0)
    2.38 +    space = &io_space[IO_SPACE_NR(fp)];
    2.39 +
    2.40 +    /* Legacy I/O on dom0 is already setup */
    2.41 +    if (d == dom0 && space == &io_space[0])
    2.42          return 0;
    2.43  
    2.44 -    fp_offset = IO_SPACE_SPARSE_ENCODING(fp) & ~PAGE_MASK;
    2.45 -    lp_offset = PAGE_ALIGN(IO_SPACE_SPARSE_ENCODING(lp));
    2.46 +    fp = IO_SPACE_PORT(fp);
    2.47 +    lp = IO_SPACE_PORT(lp);
    2.48 +
    2.49 +    if (space->sparse) {
    2.50 +        mmio_start = IO_SPACE_SPARSE_ENCODING(fp) & ~PAGE_MASK;
    2.51 +        mmio_end = PAGE_ALIGN(IO_SPACE_SPARSE_ENCODING(lp));
    2.52 +    } else {
    2.53 +        mmio_start = fp & ~PAGE_MASK;
    2.54 +        mmio_end = PAGE_ALIGN(lp);
    2.55 +    }
    2.56  
    2.57 -    for (off = fp_offset; off <= lp_offset; off += PAGE_SIZE)
    2.58 -        (void)__assign_domain_page(d, IO_PORTS_PADDR + off,
    2.59 -                                   __pa(ia64_iobase) + off, ASSIGN_nocache);
    2.60 +    /*
    2.61 +     * The "machine first port" is not necessarily identity mapped
    2.62 +     * to the guest first port.  At least for the legacy range.
    2.63 +     */
    2.64 +    mach_start = mmio_start | __pa(space->mmio_base);
    2.65 +
    2.66 +    if (space == &io_space[0]) {
    2.67 +        mmio_start |= IO_PORTS_PADDR;
    2.68 +        mmio_end |= IO_PORTS_PADDR;
    2.69 +    } else {
    2.70 +        mmio_start |= __pa(space->mmio_base);
    2.71 +        mmio_end |= __pa(space->mmio_base);
    2.72 +    }
    2.73 +
    2.74 +    while (mmio_start <= mmio_end) {
    2.75 +        (void)__assign_domain_page(d, mmio_start, mach_start, ASSIGN_nocache); 
    2.76 +        mmio_start += PAGE_SIZE;
    2.77 +        mach_start += PAGE_SIZE;
    2.78 +    }
    2.79  
    2.80      return 0;
    2.81  }
    2.82  
    2.83  static int
    2.84 -ioports_has_allowed(struct domain *d, unsigned long fp, unsigned long lp)
    2.85 +ioports_has_allowed(struct domain *d, unsigned int fp, unsigned int lp)
    2.86  {
    2.87 -    unsigned long i;
    2.88 -    for (i = fp; i < lp; i++)
    2.89 -        if (rangeset_contains_singleton(d->arch.ioport_caps, i))
    2.90 +    for (; fp < lp; fp++)
    2.91 +        if (rangeset_contains_singleton(d->arch.ioport_caps, fp))
    2.92              return 1;
    2.93 +
    2.94      return 0;
    2.95  }
    2.96  
    2.97  int
    2.98 -ioports_deny_access(struct domain *d, unsigned long fp, unsigned long lp)
    2.99 +ioports_deny_access(struct domain *d, unsigned int fp, unsigned int lp)
   2.100  {
   2.101      int ret;
   2.102      struct mm_struct *mm = &d->arch.mm;
   2.103 -    unsigned long off;
   2.104 -    unsigned long io_ports_base;
   2.105 -    unsigned long fp_offset;
   2.106 -    unsigned long lp_offset;
   2.107 +    unsigned long mmio_start, mmio_end, mmio_base;
   2.108 +    unsigned int fp_base, lp_base;
   2.109 +    struct io_space *space;
   2.110 +
   2.111 +    if (IO_SPACE_NR(fp) >= num_io_spaces) {
   2.112 +        dprintk(XENLOG_WARNING, "Unknown I/O Port range 0x%x - 0x%x\n", fp, lp);
   2.113 +        return -EFAULT;
   2.114 +    }
   2.115  
   2.116      ret = rangeset_remove_range(d->arch.ioport_caps, fp, lp);
   2.117      if (ret != 0)
   2.118          return ret;
   2.119 -    if (d == dom0)
   2.120 -        io_ports_base = __pa(ia64_iobase);
   2.121 -    else
   2.122 -        io_ports_base = IO_PORTS_PADDR;
   2.123 +
   2.124 +    space = &io_space[IO_SPACE_NR(fp)];
   2.125 +    fp_base = IO_SPACE_PORT(fp);
   2.126 +    lp_base = IO_SPACE_PORT(lp);
   2.127  
   2.128 -    fp_offset = IO_SPACE_SPARSE_ENCODING(fp) & PAGE_MASK;
   2.129 -    lp_offset = PAGE_ALIGN(IO_SPACE_SPARSE_ENCODING(lp));
   2.130 +    if (space->sparse) {
   2.131 +        mmio_start = IO_SPACE_SPARSE_ENCODING(fp_base) & ~PAGE_MASK;
   2.132 +        mmio_end = PAGE_ALIGN(IO_SPACE_SPARSE_ENCODING(lp_base));
   2.133 +    } else {
   2.134 +        mmio_start = fp_base & ~PAGE_MASK;
   2.135 +        mmio_end = PAGE_ALIGN(lp_base);
   2.136 +    }
   2.137  
   2.138 -    for (off = fp_offset; off < lp_offset; off += PAGE_SIZE) {
   2.139 -        unsigned long mpaddr = io_ports_base + off;
   2.140 -        unsigned long port;
   2.141 +    if (space == &io_space[0] && d != dom0)
   2.142 +        mmio_base = IO_PORTS_PADDR;
   2.143 +    else
   2.144 +        mmio_base = __pa(space->mmio_base);
   2.145 +
   2.146 +    for (; mmio_start < mmio_end; mmio_start += PAGE_SIZE) {
   2.147 +        unsigned int port, range;
   2.148 +        unsigned long mpaddr;
   2.149          volatile pte_t *pte;
   2.150          pte_t old_pte;
   2.151  
   2.152 -        port = IO_SPACE_SPARSE_DECODING (off);
   2.153 -        if (port < fp || port + IO_SPACE_SPARSE_PORTS_PER_PAGE - 1 > lp) {
   2.154 +        if (space->sparse) {
   2.155 +            port = IO_SPACE_SPARSE_DECODING(mmio_start);
   2.156 +            range = IO_SPACE_SPARSE_PORTS_PER_PAGE - 1;
   2.157 +        } else {
   2.158 +            port = mmio_start;
   2.159 +            range = PAGE_SIZE - 1;
   2.160 +        }
   2.161 +
   2.162 +        port |= IO_SPACE_BASE(IO_SPACE_NR(fp));
   2.163 +
   2.164 +        if (port < fp || port + range > lp) {
   2.165              /* Maybe this covers an allowed port.  */
   2.166 -            if (ioports_has_allowed(d, port,
   2.167 -                                    port + IO_SPACE_SPARSE_PORTS_PER_PAGE - 1))
   2.168 +            if (ioports_has_allowed(d, port, port + range))
   2.169                  continue;
   2.170          }
   2.171  
   2.172 +        mpaddr = mmio_start | mmio_base;
   2.173          pte = lookup_noalloc_domain_pte_none(d, mpaddr);
   2.174          BUG_ON(pte == NULL);
   2.175          BUG_ON(pte_none(*pte));
   2.176  
   2.177 -        // clear pte
   2.178 +        /* clear pte */
   2.179          old_pte = ptep_get_and_clear(mm, mpaddr, pte);
   2.180      }
   2.181      domain_flush_vtlb_all(d);
     3.1 --- a/xen/include/asm-ia64/iocap.h	Tue Jun 12 14:58:39 2007 -0600
     3.2 +++ b/xen/include/asm-ia64/iocap.h	Tue Jun 12 15:07:05 2007 -0600
     3.3 @@ -8,9 +8,9 @@
     3.4  #define __IA64_IOCAP_H__
     3.5  
     3.6  extern int ioports_permit_access(struct domain *d,
     3.7 -                                 unsigned long s, unsigned long e);
     3.8 +				 unsigned int s, unsigned int e);
     3.9  extern int ioports_deny_access(struct domain *d,
    3.10 -                               unsigned long s, unsigned long e);
    3.11 +			       unsigned int s, unsigned int e);
    3.12  
    3.13  #define ioports_access_permitted(d, s, e)               \
    3.14      rangeset_contains_range((d)->arch.ioport_caps, s, e)
     4.1 --- a/xen/include/public/arch-ia64.h	Tue Jun 12 14:58:39 2007 -0600
     4.2 +++ b/xen/include/public/arch-ia64.h	Tue Jun 12 15:07:05 2007 -0600
     4.3 @@ -531,6 +531,9 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_conte
     4.4  /* get fpswa revision */
     4.5  #define IA64_DOM0VP_fpswa_revision      10
     4.6  
     4.7 +/* Add an I/O port space range */
     4.8 +#define IA64_DOM0VP_add_io_space        11
     4.9 +
    4.10  // flags for page assignement to pseudo physical address space
    4.11  #define _ASSIGN_readonly                0
    4.12  #define ASSIGN_readonly                 (1UL << _ASSIGN_readonly)