Files
xen/tools/firmware/hvmloader/pci.c
T
Ian Campbell 443701ef0c Replace FSF street address with canonical URL
As recommended in http://www.gnu.org/licenses/gpl-howto.en.html.

This is the result of:
$ git grep -El Mass\|Temple\|Franklin | xargs ./fsf.pl

Where fsf.pl is:
    #!/usr/bin/perl -w -pi.bak -0777
    my $repl = 'If not, see <http://www.gnu.org/licenses/>.';
    my $br = qr/(?:\s*\n\s*(?:[\*\#]|\/\/|\.\\" )?\s*|\s+)/;

    my $inwt = qr/[Ii]f${br}not,${br}write${br}(?:to${br})?the${br}Free${br}Software${br}Foundation,(?:${br}Inc\.,)?/;

    my $mass = qr/675${br}Mass${br}Ave,?${br}Cambridge,?${br}MA${br}02139,?${br}USA,?\.?/;
    my $franklin = qr/51${br}Franklin${br}St(?:reet)?(?:,${br}| - )Fifth${br}Floor,?${br}Boston,?${br}MA,?${br}02110-1301,?${br}USA,?\.?/;
    my $temple = qr/59${br}Temple${br}Place(?:,${br}| - )Suite${br}330,?${br}Boston,?${br}MA,?${br}021110?-1307,?${br}USA,?\.?/;

    s|$inwt$br$mass|$repl|m;
    s|$inwt$br$franklin|$repl|m;
    s|$inwt$br$temple|$repl|m;

The only remaining mentions of these addresses are in COPYING files which I
haven't touched.

Some of the changed files are imports from elsewhere, however
filtering them out is tricky, I think it is tolerable to have these
files be modified here and then perhaps reverted on the next sync,
since it's only 1-2 lines and obvious what is going on.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
2015-07-29 11:14:07 +01:00

542 lines
19 KiB
C

/*
* pci.c: HVM PCI setup.
*
* Leendert van Doorn, leendert@watson.ibm.com
* Copyright (c) 2005, International Business Machines Corporation.
*
* Copyright (c) 2006, Keir Fraser, XenSource Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; If not, see <http://www.gnu.org/licenses/>.
*/
#include "util.h"
#include "hypercall.h"
#include "config.h"
#include "pci_regs.h"
#include <xen/memory.h>
#include <xen/hvm/ioreq.h>
#include <xen/hvm/hvm_xs_strings.h>
#include <xen/hvm/e820.h>
#include <stdbool.h>
unsigned long pci_mem_start = HVM_BELOW_4G_MMIO_START;
unsigned long pci_mem_end = PCI_MEM_END;
uint64_t pci_hi_mem_start = 0, pci_hi_mem_end = 0;
enum virtual_vga virtual_vga = VGA_none;
unsigned long igd_opregion_pgbase = 0;
/* Check if the specified range conflicts with any reserved device memory. */
static bool check_overlap_all(uint64_t start, uint64_t size)
{
unsigned int i;
for ( i = 0; i < memory_map.nr_map; i++ )
{
if ( memory_map.map[i].type == E820_RESERVED &&
check_overlap(start, size,
memory_map.map[i].addr,
memory_map.map[i].size) )
return true;
}
return false;
}
/* Find the lowest RMRR ending above base but below 4G. */
static int find_next_rmrr(uint32_t base)
{
unsigned int i;
int next_rmrr = -1;
uint64_t end, min_end = 1ULL << 32;
for ( i = 0; i < memory_map.nr_map ; i++ )
{
end = memory_map.map[i].addr + memory_map.map[i].size;
if ( memory_map.map[i].type == E820_RESERVED &&
end > base && end <= min_end )
{
next_rmrr = i;
min_end = end;
}
}
return next_rmrr;
}
void pci_setup(void)
{
uint8_t is_64bar, using_64bar, bar64_relocate = 0;
uint32_t devfn, bar_reg, cmd, bar_data, bar_data_upper;
uint64_t base, bar_sz, bar_sz_upper, mmio_total = 0;
uint32_t vga_devfn = 256;
uint16_t class, vendor_id, device_id;
unsigned int bar, pin, link, isa_irq;
int next_rmrr;
/* Resources assignable to PCI devices via BARs. */
struct resource {
uint64_t base, max;
} *resource, mem_resource, high_mem_resource, io_resource;
/* Create a list of device BARs in descending order of size. */
struct bars {
uint32_t is_64bar;
uint32_t devfn;
uint32_t bar_reg;
uint64_t bar_sz;
} *bars = (struct bars *)scratch_start;
unsigned int i, nr_bars = 0;
uint64_t mmio_hole_size = 0;
const char *s;
/*
* Do we allow hvmloader to relocate guest memory in order to
* increase the size of the lowmem MMIO hole? Defaulting to 1
* here will mean that non-libxl toolstacks (including xend and
* home-grown ones) means that those using qemu-xen will still
* experience the memory relocation bug described below; but it
* also means that those using qemu-traditional will *not*
* experience any change; and it also means that there is a
* work-around for those using qemu-xen, namely switching to
* qemu-traditional.
*
* If we defaulted to 0, and failing to resize the hole caused any
* problems with qemu-traditional, then there is no work-around.
*
* Since xend can only use qemu-traditional, I think this is the
* option that will have the least impact.
*/
bool allow_memory_relocate = 1;
s = xenstore_read(HVM_XS_ALLOW_MEMORY_RELOCATE, NULL);
if ( s )
allow_memory_relocate = strtoll(s, NULL, 0);
printf("Relocating guest memory for lowmem MMIO space %s\n",
allow_memory_relocate?"enabled":"disabled");
s = xenstore_read("platform/mmio_hole_size", NULL);
if ( s )
mmio_hole_size = strtoll(s, NULL, 0);
/* Program PCI-ISA bridge with appropriate link routes. */
isa_irq = 0;
for ( link = 0; link < 4; link++ )
{
do { isa_irq = (isa_irq + 1) & 15;
} while ( !(PCI_ISA_IRQ_MASK & (1U << isa_irq)) );
pci_writeb(PCI_ISA_DEVFN, 0x60 + link, isa_irq);
printf("PCI-ISA link %u routed to IRQ%u\n", link, isa_irq);
}
/* Program ELCR to match PCI-wired IRQs. */
outb(0x4d0, (uint8_t)(PCI_ISA_IRQ_MASK >> 0));
outb(0x4d1, (uint8_t)(PCI_ISA_IRQ_MASK >> 8));
/* Scan the PCI bus and map resources. */
for ( devfn = 0; devfn < 256; devfn++ )
{
class = pci_readw(devfn, PCI_CLASS_DEVICE);
vendor_id = pci_readw(devfn, PCI_VENDOR_ID);
device_id = pci_readw(devfn, PCI_DEVICE_ID);
if ( (vendor_id == 0xffff) && (device_id == 0xffff) )
continue;
ASSERT((devfn != PCI_ISA_DEVFN) ||
((vendor_id == 0x8086) && (device_id == 0x7000)));
switch ( class )
{
case 0x0300:
/* If emulated VGA is found, preserve it as primary VGA. */
if ( (vendor_id == 0x1234) && (device_id == 0x1111) )
{
vga_devfn = devfn;
virtual_vga = VGA_std;
}
else if ( (vendor_id == 0x1013) && (device_id == 0xb8) )
{
vga_devfn = devfn;
virtual_vga = VGA_cirrus;
}
else if ( virtual_vga == VGA_none )
{
vga_devfn = devfn;
virtual_vga = VGA_pt;
if ( vendor_id == 0x8086 )
{
igd_opregion_pgbase = mem_hole_alloc(IGD_OPREGION_PAGES);
/*
* Write the the OpRegion offset to give the opregion
* address to the device model. The device model will trap
* and map the OpRegion at the give address.
*/
pci_writel(vga_devfn, PCI_INTEL_OPREGION,
igd_opregion_pgbase << PAGE_SHIFT);
}
}
break;
case 0x0680:
/* PIIX4 ACPI PM. Special device with special PCI config space. */
ASSERT((vendor_id == 0x8086) && (device_id == 0x7113));
pci_writew(devfn, 0x20, 0x0000); /* No smb bus IO enable */
pci_writew(devfn, 0xd2, 0x0000); /* No smb bus IO enable */
pci_writew(devfn, 0x22, 0x0000);
pci_writew(devfn, 0x3c, 0x0009); /* Hardcoded IRQ9 */
pci_writew(devfn, 0x3d, 0x0001);
pci_writel(devfn, 0x40, ACPI_PM1A_EVT_BLK_ADDRESS_V1 | 1);
pci_writeb(devfn, 0x80, 0x01); /* enable PM io space */
break;
case 0x0101:
if ( vendor_id == 0x8086 )
{
/* Intel ICHs since PIIX3: enable IDE legacy mode. */
pci_writew(devfn, 0x40, 0x8000); /* enable IDE0 */
pci_writew(devfn, 0x42, 0x8000); /* enable IDE1 */
}
break;
}
/* Map the I/O memory and port resources. */
for ( bar = 0; bar < 7; bar++ )
{
bar_sz_upper = 0;
bar_reg = PCI_BASE_ADDRESS_0 + 4*bar;
if ( bar == 6 )
bar_reg = PCI_ROM_ADDRESS;
bar_data = pci_readl(devfn, bar_reg);
if ( bar_reg != PCI_ROM_ADDRESS )
{
is_64bar = !!((bar_data & (PCI_BASE_ADDRESS_SPACE |
PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
(PCI_BASE_ADDRESS_SPACE_MEMORY |
PCI_BASE_ADDRESS_MEM_TYPE_64));
pci_writel(devfn, bar_reg, ~0);
}
else
{
is_64bar = 0;
pci_writel(devfn, bar_reg,
(bar_data | PCI_ROM_ADDRESS_MASK) &
~PCI_ROM_ADDRESS_ENABLE);
}
bar_sz = pci_readl(devfn, bar_reg);
pci_writel(devfn, bar_reg, bar_data);
if ( bar_reg != PCI_ROM_ADDRESS )
bar_sz &= (((bar_data & PCI_BASE_ADDRESS_SPACE) ==
PCI_BASE_ADDRESS_SPACE_MEMORY) ?
PCI_BASE_ADDRESS_MEM_MASK :
(PCI_BASE_ADDRESS_IO_MASK & 0xffff));
else
bar_sz &= PCI_ROM_ADDRESS_MASK;
if (is_64bar) {
bar_data_upper = pci_readl(devfn, bar_reg + 4);
pci_writel(devfn, bar_reg + 4, ~0);
bar_sz_upper = pci_readl(devfn, bar_reg + 4);
pci_writel(devfn, bar_reg + 4, bar_data_upper);
bar_sz = (bar_sz_upper << 32) | bar_sz;
}
bar_sz &= ~(bar_sz - 1);
if ( bar_sz == 0 )
continue;
for ( i = 0; i < nr_bars; i++ )
if ( bars[i].bar_sz < bar_sz )
break;
if ( i != nr_bars )
memmove(&bars[i+1], &bars[i], (nr_bars-i) * sizeof(*bars));
bars[i].is_64bar = is_64bar;
bars[i].devfn = devfn;
bars[i].bar_reg = bar_reg;
bars[i].bar_sz = bar_sz;
if ( ((bar_data & PCI_BASE_ADDRESS_SPACE) ==
PCI_BASE_ADDRESS_SPACE_MEMORY) ||
(bar_reg == PCI_ROM_ADDRESS) )
mmio_total += bar_sz;
nr_bars++;
/*The upper half is already calculated, skip it! */
if (is_64bar)
bar++;
}
/* Map the interrupt. */
pin = pci_readb(devfn, PCI_INTERRUPT_PIN);
if ( pin != 0 )
{
/* This is the barber's pole mapping used by Xen. */
link = ((pin - 1) + (devfn >> 3)) & 3;
isa_irq = pci_readb(PCI_ISA_DEVFN, 0x60 + link);
pci_writeb(devfn, PCI_INTERRUPT_LINE, isa_irq);
printf("pci dev %02x:%x INT%c->IRQ%u\n",
devfn>>3, devfn&7, 'A'+pin-1, isa_irq);
}
/* Enable bus mastering. */
cmd = pci_readw(devfn, PCI_COMMAND);
cmd |= PCI_COMMAND_MASTER;
pci_writew(devfn, PCI_COMMAND, cmd);
}
if ( mmio_hole_size )
{
uint64_t max_ram_below_4g = (1ULL << 32) - mmio_hole_size;
if ( max_ram_below_4g > HVM_BELOW_4G_MMIO_START )
{
printf("max_ram_below_4g=0x"PRIllx
" too big for mmio_hole_size=0x"PRIllx
" has been ignored.\n",
PRIllx_arg(max_ram_below_4g),
PRIllx_arg(mmio_hole_size));
}
else
{
pci_mem_start = max_ram_below_4g;
printf("pci_mem_start=0x%lx (was 0x%x) for mmio_hole_size=%lu\n",
pci_mem_start, HVM_BELOW_4G_MMIO_START,
(long)mmio_hole_size);
}
}
else
{
/*
* At the moment qemu-xen can't deal with relocated memory regions.
* It's too close to the release to make a proper fix; for now,
* only allow the MMIO hole to grow large enough to move guest memory
* if we're running qemu-traditional. Items that don't fit will be
* relocated into the 64-bit address space.
*
* This loop now does the following:
* - If allow_memory_relocate, increase the MMIO hole until it's
* big enough, or until it's 2GiB
* - If !allow_memory_relocate, increase the MMIO hole until it's
* big enough, or until it's 2GiB, or until it overlaps guest
* memory
*/
while ( (mmio_total > (pci_mem_end - pci_mem_start))
&& ((pci_mem_start << 1) != 0)
&& (allow_memory_relocate
|| (((pci_mem_start << 1) >> PAGE_SHIFT)
>= hvm_info->low_mem_pgend)) )
pci_mem_start <<= 1;
/*
* Try to accommodate RMRRs in our MMIO region on a best-effort basis.
* If we have RMRRs in the range, then make pci_mem_start just after
* hvm_info->low_mem_pgend.
*/
if ( pci_mem_start > (hvm_info->low_mem_pgend << PAGE_SHIFT) &&
check_overlap_all(pci_mem_start, pci_mem_end-pci_mem_start) )
pci_mem_start = hvm_info->low_mem_pgend << PAGE_SHIFT;
}
if ( mmio_total > (pci_mem_end - pci_mem_start) )
{
printf("Low MMIO hole not large enough for all devices,"
" relocating some BARs to 64-bit\n");
bar64_relocate = 1;
}
/* Relocate RAM that overlaps PCI space (in 64k-page chunks). */
while ( (pci_mem_start >> PAGE_SHIFT) < hvm_info->low_mem_pgend )
{
struct xen_add_to_physmap xatp;
unsigned int nr_pages = min_t(
unsigned int,
hvm_info->low_mem_pgend - (pci_mem_start >> PAGE_SHIFT),
(1u << 16) - 1);
if ( hvm_info->high_mem_pgend == 0 )
hvm_info->high_mem_pgend = 1ull << (32 - PAGE_SHIFT);
hvm_info->low_mem_pgend -= nr_pages;
printf("Relocating 0x%x pages from "PRIllx" to "PRIllx\
" for lowmem MMIO hole\n",
nr_pages,
PRIllx_arg(((uint64_t)hvm_info->low_mem_pgend)<<PAGE_SHIFT),
PRIllx_arg(((uint64_t)hvm_info->high_mem_pgend)<<PAGE_SHIFT));
xatp.domid = DOMID_SELF;
xatp.space = XENMAPSPACE_gmfn_range;
xatp.idx = hvm_info->low_mem_pgend;
xatp.gpfn = hvm_info->high_mem_pgend;
xatp.size = nr_pages;
if ( hypercall_memory_op(XENMEM_add_to_physmap, &xatp) != 0 )
BUG();
hvm_info->high_mem_pgend += nr_pages;
}
/* Sync memory map[] if necessary. */
adjust_memory_map();
high_mem_resource.base = ((uint64_t)hvm_info->high_mem_pgend) << PAGE_SHIFT;
if ( high_mem_resource.base < 1ull << 32 )
{
if ( hvm_info->high_mem_pgend != 0 )
printf("WARNING: hvm_info->high_mem_pgend %x"
" does not point into high memory!",
hvm_info->high_mem_pgend);
high_mem_resource.base = 1ull << 32;
}
printf("%sRAM in high memory; setting high_mem resource base to "PRIllx"\n",
hvm_info->high_mem_pgend?"":"No ",
PRIllx_arg(high_mem_resource.base));
high_mem_resource.max = 1ull << cpu_phys_addr();
mem_resource.base = pci_mem_start;
mem_resource.max = pci_mem_end;
io_resource.base = 0xc000;
io_resource.max = 0x10000;
next_rmrr = find_next_rmrr(pci_mem_start);
/* Assign iomem and ioport resources in descending order of size. */
for ( i = 0; i < nr_bars; i++ )
{
devfn = bars[i].devfn;
bar_reg = bars[i].bar_reg;
bar_sz = bars[i].bar_sz;
/*
* Relocate to high memory if the total amount of MMIO needed
* is more than the low MMIO available. Because devices are
* processed in order of bar_sz, this will preferentially
* relocate larger devices to high memory first.
*
* NB: The code here is rather fragile, as the check here to see
* whether bar_sz will fit in the low MMIO region doesn't match the
* real check made below, which involves aligning the base offset of the
* bar with the size of the bar itself. As it happens, this will always
* be satisfied because:
* - The first one will succeed because the MMIO hole can only start at
* 0x{f,e,c,8}00000000. If it fits, it will be aligned properly.
* - All subsequent ones will be aligned because the list is ordered
* large to small, and bar_sz is always a power of 2. (At least
* the code here assumes it to be.)
* Should either of those two conditions change, this code will break.
*/
using_64bar = bars[i].is_64bar && bar64_relocate
&& (mmio_total > (mem_resource.max - mem_resource.base));
bar_data = pci_readl(devfn, bar_reg);
if ( (bar_data & PCI_BASE_ADDRESS_SPACE) ==
PCI_BASE_ADDRESS_SPACE_MEMORY )
{
/* Mapping high memory if PCI device is 64 bits bar */
if ( using_64bar ) {
if ( high_mem_resource.base & (bar_sz - 1) )
high_mem_resource.base = high_mem_resource.base -
(high_mem_resource.base & (bar_sz - 1)) + bar_sz;
if ( !pci_hi_mem_start )
pci_hi_mem_start = high_mem_resource.base;
resource = &high_mem_resource;
bar_data &= ~PCI_BASE_ADDRESS_MEM_MASK;
}
else {
resource = &mem_resource;
bar_data &= ~PCI_BASE_ADDRESS_MEM_MASK;
}
mmio_total -= bar_sz;
}
else
{
resource = &io_resource;
bar_data &= ~PCI_BASE_ADDRESS_IO_MASK;
}
base = (resource->base + bar_sz - 1) & ~(uint64_t)(bar_sz - 1);
/* If we're using mem_resource, check for RMRR conflicts. */
while ( resource == &mem_resource &&
next_rmrr >= 0 &&
check_overlap(base, bar_sz,
memory_map.map[next_rmrr].addr,
memory_map.map[next_rmrr].size) )
{
base = memory_map.map[next_rmrr].addr + memory_map.map[next_rmrr].size;
base = (base + bar_sz - 1) & ~(bar_sz - 1);
next_rmrr = find_next_rmrr(base);
}
bar_data |= (uint32_t)base;
bar_data_upper = (uint32_t)(base >> 32);
base += bar_sz;
if ( (base < resource->base) || (base > resource->max) )
{
printf("pci dev %02x:%x bar %02x size "PRIllx": no space for "
"resource!\n", devfn>>3, devfn&7, bar_reg,
PRIllx_arg(bar_sz));
continue;
}
resource->base = base;
pci_writel(devfn, bar_reg, bar_data);
if (using_64bar)
pci_writel(devfn, bar_reg + 4, bar_data_upper);
printf("pci dev %02x:%x bar %02x size "PRIllx": %x%08x\n",
devfn>>3, devfn&7, bar_reg,
PRIllx_arg(bar_sz),
bar_data_upper, bar_data);
/* Now enable the memory or I/O mapping. */
cmd = pci_readw(devfn, PCI_COMMAND);
if ( (bar_reg == PCI_ROM_ADDRESS) ||
((bar_data & PCI_BASE_ADDRESS_SPACE) ==
PCI_BASE_ADDRESS_SPACE_MEMORY) )
cmd |= PCI_COMMAND_MEMORY;
else
cmd |= PCI_COMMAND_IO;
pci_writew(devfn, PCI_COMMAND, cmd);
}
if ( pci_hi_mem_start )
{
/*
* Make end address alignment match the start address one's so that
* fewer variable range MTRRs are needed to cover the range.
*/
pci_hi_mem_end = ((high_mem_resource.base - 1) |
((pci_hi_mem_start & -pci_hi_mem_start) - 1)) + 1;
}
if ( vga_devfn != 256 )
{
/*
* VGA registers live in I/O space so ensure that primary VGA
* has IO enabled, even if there is no I/O BAR on that
* particular device.
*/
cmd = pci_readw(vga_devfn, PCI_COMMAND);
cmd |= PCI_COMMAND_IO;
pci_writew(vga_devfn, PCI_COMMAND, cmd);
}
}
/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/