[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-ia64-devel] [RFC] Extended I/O port space support
Hi, I'm not sure if any other vendors take advantage of this, but 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. I propose the patch below to solve this. 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. Next on my TODO list for this is driver domains. This is where it might get complicated. Somehow I need to make the driver domain aware that a PCI bus makes use of a different set of I/O port ranges. This won't work well with the virtual PCI mappings that present the guest with all PCI devices on the same bus. I think I'm going to need to create a new topology that creates a virtual bus per physical bus, but I haven't dug too deeply there yet. Suggestions? Please send me comments on the patch below. It still has some debug printks in it, but it seems to work. Thanks, Alex Signed-off-by: Alex Williamson <alex.williamson@xxxxxx> --- diff -r 0cf6b75423e9 linux-2.6-xen-sparse/arch/ia64/pci/pci.c --- a/linux-2.6-xen-sparse/arch/ia64/pci/pci.c Mon Jun 04 14:17:54 2007 -0600 +++ b/linux-2.6-xen-sparse/arch/ia64/pci/pci.c Thu Jun 07 11:38:41 2007 -0600 @@ -164,6 +164,11 @@ new_space (u64 phys_base, int sparse) i = num_io_spaces++; io_space[i].mmio_base = mmio_base; io_space[i].sparse = sparse; + +#ifdef CONFIG_XEN + if (is_initial_xendomain()) + HYPERVISOR_add_io_space(phys_base, sparse, i); +#endif return i; } diff -r 0cf6b75423e9 linux-2.6-xen-sparse/include/asm-ia64/hypercall.h --- a/linux-2.6-xen-sparse/include/asm-ia64/hypercall.h Mon Jun 04 14:17:54 2007 -0600 +++ b/linux-2.6-xen-sparse/include/asm-ia64/hypercall.h Thu Jun 07 08:37:01 2007 -0600 @@ -387,6 +387,15 @@ xencomm_arch_hypercall_fpswa_revision(st { return _hypercall2(int, ia64_dom0vp_op, IA64_DOM0VP_fpswa_revision, arg); +} + +static inline int +HYPERVISOR_add_io_space(unsigned long phys_base, + unsigned long sparse, + unsigned long space_number) +{ + return _hypercall4(int, ia64_dom0vp_op, IA64_DOM0VP_add_io_space, + phys_base, sparse, space_number); } // for balloon driver diff -r 0cf6b75423e9 xen/arch/ia64/xen/dom0_ops.c --- a/xen/arch/ia64/xen/dom0_ops.c Mon Jun 04 14:17:54 2007 -0600 +++ b/xen/arch/ia64/xen/dom0_ops.c Thu Jun 07 15:52:28 2007 -0600 @@ -363,6 +363,40 @@ dom0vp_fpswa_revision(XEN_GUEST_HANDLE(u return 0; } +static unsigned long +dom0vp_add_io_space(struct domain *d, unsigned long phys_base, + unsigned long sparse, unsigned long space_number) +{ + unsigned int fp, lp; + + printk("%s(%d, 0x%016lx, %ssparse, %d)\n", __func__, d->domain_id, + phys_base, sparse ? "" : "not-", space_number); + /* + * Registering new io_space roughly based on linux + * arch/ia64/pci/pci.c:new_space() + */ + if (phys_base == 0) + return 0; /* legacy I/O port space */ + + /* + * We may need a valid bit in the io_space struct so we + * can initialize these more asynchornously. But this makes + * sure we register spaces in lock step with dom0. + */ + if (space_number > MAX_IO_SPACES || space_number != num_io_spaces) + return -EINVAL; + + io_space[space_number].mmio_base = phys_base; + io_space[space_number].sparse = sparse; + + num_io_spaces++; + + fp = space_number << IO_SPACE_BITS; + lp = fp | 0xffff; + + return ioports_permit_access(d, fp, lp); +} + unsigned long do_dom0vp_op(unsigned long cmd, unsigned long arg0, unsigned long arg1, unsigned long arg2, @@ -419,6 +453,9 @@ do_dom0vp_op(unsigned long cmd, ret = dom0vp_fpswa_revision(hnd); break; } + case IA64_DOM0VP_add_io_space: + ret = dom0vp_add_io_space(d, arg0, arg1, arg2); + break; default: ret = -1; printk("unknown dom0_vp_op 0x%lx\n", cmd); diff -r 0cf6b75423e9 xen/arch/ia64/xen/mm.c --- a/xen/arch/ia64/xen/mm.c Mon Jun 04 14:17:54 2007 -0600 +++ b/xen/arch/ia64/xen/mm.c Thu Jun 07 15:55:58 2007 -0600 @@ -886,69 +886,123 @@ assign_domain_page(struct domain *d, } int -ioports_permit_access(struct domain *d, unsigned long fp, unsigned long lp) -{ +ioports_permit_access(struct domain *d, unsigned int fp, unsigned int lp) +{ + struct io_space *space; + unsigned long mmio_start, mmio_end, mach_start; int ret; - unsigned long off; - unsigned long fp_offset; - unsigned long lp_offset; - + + printk("%s(%d, 0x%x, 0x%x)\n", __func__, d->domain_id, fp, lp); + + if (IO_SPACE_NR(fp) >= num_io_spaces) { + dprintk(XENLOG_WARNING, "Unknown I/O Port range 0x%x - 0x%x\n", fp, lp); + return -EFAULT; + } + + /* + * The ioport_cap rangeset tracks the I/O port address including + * the port space ID. This means port space IDs need to match + * between Xen and the guest. This is also a requirement because + * the hypercall to pass these port ranges only uses a u32. + */ ret = rangeset_add_range(d->arch.ioport_caps, fp, lp); if (ret != 0) return ret; - /* Domain 0 doesn't virtualize IO ports space. */ - if (d == dom0) + space = &io_space[IO_SPACE_NR(fp)]; + + /* Domain 0 doesn't virtualize legacy IO ports space. */ + if (d == dom0 && space == &io_space[0]) return 0; - fp_offset = IO_SPACE_SPARSE_ENCODING(fp) & ~PAGE_MASK; - lp_offset = PAGE_ALIGN(IO_SPACE_SPARSE_ENCODING(lp)); - - for (off = fp_offset; off <= lp_offset; off += PAGE_SIZE) - (void)__assign_domain_page(d, IO_PORTS_PADDR + off, - __pa(ia64_iobase) + off, ASSIGN_nocache); + fp = IO_SPACE_PORT(fp); + lp = IO_SPACE_PORT(lp); + + if (space->sparse) { + mmio_start = IO_SPACE_SPARSE_ENCODING(fp) & ~PAGE_MASK; + mmio_end = PAGE_ALIGN(IO_SPACE_SPARSE_ENCODING(lp)); + } else { + mmio_start = fp & ~PAGE_MASK; + mmio_end = PAGE_ALIGN(lp); + } + + /* + * The "machine first port" is not necessarily identity mapped + * to the guest first port. At least for the legacy range. + */ + mach_start = mmio_start | __pa(space->mmio_base); + + if (space == &io_space[0] && d != dom0) { + mmio_start |= IO_PORTS_PADDR; + mmio_end |= IO_PORTS_PADDR; + } else { + mmio_start |= __pa(space->mmio_base); + mmio_end |= __pa(space->mmio_base); + } + + while (mmio_start <= mmio_end) { + (void)__assign_domain_page(d, mmio_start, mach_start, ASSIGN_nocache); + mmio_start += PAGE_SIZE; + mach_start += PAGE_SIZE; + } return 0; } static int -ioports_has_allowed(struct domain *d, unsigned long fp, unsigned long lp) -{ - unsigned long i; - for (i = fp; i < lp; i++) - if (rangeset_contains_singleton(d->arch.ioport_caps, i)) +ioports_has_allowed(struct domain *d, unsigned int fp, unsigned int lp) +{ + for (; fp < lp; fp++) + if (rangeset_contains_singleton(d->arch.ioport_caps, fp)) return 1; + return 0; } int -ioports_deny_access(struct domain *d, unsigned long fp, unsigned long lp) +ioports_deny_access(struct domain *d, unsigned int fp, unsigned int lp) { int ret; struct mm_struct *mm = &d->arch.mm; - unsigned long off; - unsigned long io_ports_base; - unsigned long fp_offset; - unsigned long lp_offset; + unsigned long mmio_start, mmio_end, mmio_base; + unsigned int fp_base, lp_base; + struct io_space *space; + + printk("%s(%d, 0x%x, 0x%x)\n", __func__, d->domain_id, fp, lp); + + if (IO_SPACE_NR(fp) >= num_io_spaces) { + dprintk(XENLOG_WARNING, "Unknown I/O Port range 0x%x - 0x%x\n", fp, lp); + return -EFAULT; + } ret = rangeset_remove_range(d->arch.ioport_caps, fp, lp); if (ret != 0) return ret; - if (d == dom0) - io_ports_base = __pa(ia64_iobase); + + space = &io_space[IO_SPACE_NR(fp)]; + fp_base = IO_SPACE_PORT(fp); + lp_base = IO_SPACE_PORT(lp); + + if (space->sparse) { + mmio_start = IO_SPACE_SPARSE_ENCODING(fp_base) & ~PAGE_MASK; + mmio_end = PAGE_ALIGN(IO_SPACE_SPARSE_ENCODING(lp_base)); + } else { + mmio_start = fp_base & ~PAGE_MASK; + mmio_end = PAGE_ALIGN(lp_base); + } + + if (space == &io_space[0] && d != dom0) + mmio_base = IO_PORTS_PADDR; else - io_ports_base = IO_PORTS_PADDR; - - fp_offset = IO_SPACE_SPARSE_ENCODING(fp) & PAGE_MASK; - lp_offset = PAGE_ALIGN(IO_SPACE_SPARSE_ENCODING(lp)); - - for (off = fp_offset; off < lp_offset; off += PAGE_SIZE) { - unsigned long mpaddr = io_ports_base + off; - unsigned long port; + mmio_base = __pa(space->mmio_base); + + for (; mmio_start < mmio_end; mmio_start += PAGE_SIZE) { + unsigned int port; + unsigned long mpaddr; volatile pte_t *pte; pte_t old_pte; - port = IO_SPACE_SPARSE_DECODING (off); + port = IO_SPACE_SPARSE_DECODING(mmio_start); if (port < fp || port + IO_SPACE_SPARSE_PORTS_PER_PAGE - 1 > lp) { /* Maybe this covers an allowed port. */ if (ioports_has_allowed(d, port, @@ -956,6 +1010,7 @@ ioports_deny_access(struct domain *d, un continue; } + mpaddr = mmio_start | mmio_base; pte = lookup_noalloc_domain_pte_none(d, mpaddr); BUG_ON(pte == NULL); BUG_ON(pte_none(*pte)); diff -r 0cf6b75423e9 xen/include/asm-ia64/iocap.h --- a/xen/include/asm-ia64/iocap.h Mon Jun 04 14:17:54 2007 -0600 +++ b/xen/include/asm-ia64/iocap.h Thu Jun 07 15:57:30 2007 -0600 @@ -8,9 +8,9 @@ #define __IA64_IOCAP_H__ extern int ioports_permit_access(struct domain *d, - unsigned long s, unsigned long e); + unsigned int s, unsigned int e); extern int ioports_deny_access(struct domain *d, - unsigned long s, unsigned long e); + unsigned int s, unsigned int e); #define ioports_access_permitted(d, s, e) \ rangeset_contains_range((d)->arch.ioport_caps, s, e) diff -r 0cf6b75423e9 xen/include/public/arch-ia64.h --- a/xen/include/public/arch-ia64.h Mon Jun 04 14:17:54 2007 -0600 +++ b/xen/include/public/arch-ia64.h Wed Jun 06 16:52:17 2007 -0600 @@ -526,6 +526,9 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_conte /* get fpswa revision */ #define IA64_DOM0VP_fpswa_revision 10 +/* Add an I/O port space range */ +#define IA64_DOM0VP_add_io_space 11 + // flags for page assignement to pseudo physical address space #define _ASSIGN_readonly 0 #define ASSIGN_readonly (1UL << _ASSIGN_readonly) _______________________________________________ Xen-ia64-devel mailing list Xen-ia64-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-ia64-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |