Current code in PciEnableDecoding tries to unconditionally enable
EFI_PCI_IO_ATTRIBUTE_IO and EFI_PCI_IO_ATTRIBUTE_MEMORY even if they
are unsupported attributes. This fails on devices which don't
support both attributes.
This patch masks out unsupported attributes.
Information to reproduce the bug.
Host lspci -s 0000:04:00.0 -vnn:
04:00.0 USB controller [0c03]: Renesas Technology Corp. uPD720201 USB
3.0 Host Controller [1912:0014] (rev 03) (prog-if 30 [XHCI])
	Flags: fast devsel, IRQ 19
	Memory at ef900000 (64-bit, non-prefetchable) [size=8K]
	Capabilities: [50] Power Management version 3
	Capabilities: [70] MSI: Enable- Count=1/8 Maskable- 64bit+
	Capabilities: [90] MSI-X: Enable- Count=8 Masked-
	Capabilities: [a0] Express Endpoint, MSI 00
	Capabilities: [100] Advanced Error Reporting
	Capabilities: [150] Latency Tolerance Reporting
	Kernel driver in use: pci-stub
	Kernel modules: xhci_pci
libvirt xml:
    <hostdev mode='subsystem' type='pci' managed='yes'>
      <driver name='vfio'/>
      <source>
        <address domain='0x0000' bus='0x04' slot='0x00' function='0'/>
      </source>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x11'
       function='0'/>
    </hostdev>
OVMF debug log with additional DEBUG statement:
OnRootBridgesConnected: root bridges have been connected, installing
ACPI tables
Select Item: 0x19
EnablePciDecoding: GetLocation: D=0000:00:00.0
    OrigAttr=0000000000004000 SuppAttr=000000000000E700
EnablePciDecoding: GetLocation: D=0000:00:10.0
    OrigAttr=0000000000004000 SuppAttr=000000000000E700
EnablePciDecoding: GetLocation: D=0000:00:11.0
    OrigAttr=0000000000004000 SuppAttr=000000000000E600
EnablePciDecoding: EfiPciIoAttributeOperationEnable: Unsupported
Select Item: 0x28
Select Item: 0x19
Select Item: 0x2A
Select Item: 0x19
Select Item: 0x27
InstallQemuFwCfgTables: installed 6 tables
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Tested-by: Laszlo Ersek <lersek@redhat.com>
		
	
		
			
				
	
	
		
			199 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			199 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  Temporarily enable IO and MMIO decoding for all PCI devices while QEMU
 | 
						|
  regenerates the ACPI tables.
 | 
						|
 | 
						|
  Copyright (C) 2016, Red Hat, Inc.
 | 
						|
 | 
						|
  This program and the accompanying materials are licensed and made available
 | 
						|
  under the terms and conditions of the BSD License which accompanies this
 | 
						|
  distribution.  The full text of the license may be found at
 | 
						|
  http://opensource.org/licenses/bsd-license.php
 | 
						|
 | 
						|
  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
 | 
						|
  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | 
						|
**/
 | 
						|
 | 
						|
#include <Library/MemoryAllocationLib.h>
 | 
						|
 | 
						|
#include "AcpiPlatform.h"
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Collect all PciIo protocol instances in the system. Save their original
 | 
						|
  attributes, and enable IO and MMIO decoding for each.
 | 
						|
 | 
						|
  This is a best effort function; it doesn't return status codes. Its
 | 
						|
  caller is supposed to proceed even if this function fails.
 | 
						|
 | 
						|
  @param[out] OriginalAttributes  On output, a dynamically allocated array of
 | 
						|
                                  ORIGINAL_ATTRIBUTES elements. The array lists
 | 
						|
                                  the PciIo protocol instances found in the
 | 
						|
                                  system at the time of the call, plus the
 | 
						|
                                  original PCI attributes for each.
 | 
						|
 | 
						|
                                  Before returning, the function enables IO and
 | 
						|
                                  MMIO decoding for each PciIo instance it
 | 
						|
                                  finds.
 | 
						|
 | 
						|
                                  On error, or when no such instances are
 | 
						|
                                  found, OriginalAttributes is set to NULL.
 | 
						|
 | 
						|
  @param[out] Count               On output, the number of elements in
 | 
						|
                                  OriginalAttributes. On error it is set to
 | 
						|
                                  zero.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EnablePciDecoding (
 | 
						|
  OUT ORIGINAL_ATTRIBUTES **OriginalAttributes,
 | 
						|
  OUT UINTN               *Count
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS          Status;
 | 
						|
  UINTN               NoHandles;
 | 
						|
  EFI_HANDLE          *Handles;
 | 
						|
  ORIGINAL_ATTRIBUTES *OrigAttrs;
 | 
						|
  UINTN               Idx;
 | 
						|
 | 
						|
  *OriginalAttributes = NULL;
 | 
						|
  *Count              = 0;
 | 
						|
 | 
						|
  if (PcdGetBool (PcdPciDisableBusEnumeration)) {
 | 
						|
    //
 | 
						|
    // The platform downloads ACPI tables from QEMU in general, but there are
 | 
						|
    // no root bridges in this execution. We're done.
 | 
						|
    //
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiPciIoProtocolGuid,
 | 
						|
                  NULL /* SearchKey */, &NoHandles, &Handles);
 | 
						|
  if (Status == EFI_NOT_FOUND) {
 | 
						|
    //
 | 
						|
    // No PCI devices were found on either of the root bridges. We're done.
 | 
						|
    //
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    DEBUG ((EFI_D_WARN, "%a: LocateHandleBuffer(): %r\n", __FUNCTION__,
 | 
						|
      Status));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  OrigAttrs = AllocatePool (NoHandles * sizeof *OrigAttrs);
 | 
						|
  if (OrigAttrs == NULL) {
 | 
						|
    DEBUG ((EFI_D_WARN, "%a: AllocatePool(): out of resources\n",
 | 
						|
      __FUNCTION__));
 | 
						|
    goto FreeHandles;
 | 
						|
  }
 | 
						|
 | 
						|
  for (Idx = 0; Idx < NoHandles; ++Idx) {
 | 
						|
    EFI_PCI_IO_PROTOCOL *PciIo;
 | 
						|
    UINT64              Attributes;
 | 
						|
 | 
						|
    //
 | 
						|
    // Look up PciIo on the handle and stash it
 | 
						|
    //
 | 
						|
    Status = gBS->HandleProtocol (Handles[Idx], &gEfiPciIoProtocolGuid,
 | 
						|
                    (VOID**)&PciIo);
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
    OrigAttrs[Idx].PciIo = PciIo;
 | 
						|
 | 
						|
    //
 | 
						|
    // Stash the current attributes
 | 
						|
    //
 | 
						|
    Status = PciIo->Attributes (PciIo, EfiPciIoAttributeOperationGet, 0,
 | 
						|
                      &OrigAttrs[Idx].PciAttributes);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      DEBUG ((EFI_D_WARN, "%a: EfiPciIoAttributeOperationGet: %r\n",
 | 
						|
        __FUNCTION__, Status));
 | 
						|
      goto RestoreAttributes;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Retrieve supported attributes
 | 
						|
    //
 | 
						|
    Status = PciIo->Attributes (PciIo, EfiPciIoAttributeOperationSupported, 0,
 | 
						|
                      &Attributes);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      DEBUG ((EFI_D_WARN, "%a: EfiPciIoAttributeOperationSupported: %r\n",
 | 
						|
        __FUNCTION__, Status));
 | 
						|
      goto RestoreAttributes;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Enable IO and MMIO decoding
 | 
						|
    //
 | 
						|
    Attributes &= EFI_PCI_IO_ATTRIBUTE_IO | EFI_PCI_IO_ATTRIBUTE_MEMORY;
 | 
						|
    Status = PciIo->Attributes (PciIo, EfiPciIoAttributeOperationEnable,
 | 
						|
                      Attributes, NULL);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      DEBUG ((EFI_D_WARN, "%a: EfiPciIoAttributeOperationEnable: %r\n",
 | 
						|
        __FUNCTION__, Status));
 | 
						|
      goto RestoreAttributes;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Success
 | 
						|
  //
 | 
						|
  FreePool (Handles);
 | 
						|
  *OriginalAttributes = OrigAttrs;
 | 
						|
  *Count              = NoHandles;
 | 
						|
  return;
 | 
						|
 | 
						|
RestoreAttributes:
 | 
						|
  while (Idx > 0) {
 | 
						|
    --Idx;
 | 
						|
    OrigAttrs[Idx].PciIo->Attributes (OrigAttrs[Idx].PciIo,
 | 
						|
                            EfiPciIoAttributeOperationSet,
 | 
						|
                            OrigAttrs[Idx].PciAttributes,
 | 
						|
                            NULL
 | 
						|
                            );
 | 
						|
  }
 | 
						|
  FreePool (OrigAttrs);
 | 
						|
 | 
						|
FreeHandles:
 | 
						|
  FreePool (Handles);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Restore the original PCI attributes saved with EnablePciDecoding().
 | 
						|
 | 
						|
  @param[in] OriginalAttributes  The array allocated and populated by
 | 
						|
                                 EnablePciDecoding(). This parameter may be
 | 
						|
                                 NULL. If OriginalAttributes is NULL, then the
 | 
						|
                                 function is a no-op; otherwise the PciIo
 | 
						|
                                 attributes will be restored, and the
 | 
						|
                                 OriginalAttributes array will be freed.
 | 
						|
 | 
						|
  @param[in] Count               The Count value stored by EnablePciDecoding(),
 | 
						|
                                 the number of elements in OriginalAttributes.
 | 
						|
                                 Count may be zero if and only if
 | 
						|
                                 OriginalAttributes is NULL.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
RestorePciDecoding (
 | 
						|
  IN ORIGINAL_ATTRIBUTES *OriginalAttributes,
 | 
						|
  IN UINTN               Count
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN Idx;
 | 
						|
 | 
						|
  ASSERT ((OriginalAttributes == NULL) == (Count == 0));
 | 
						|
  if (OriginalAttributes == NULL) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  for (Idx = 0; Idx < Count; ++Idx) {
 | 
						|
    OriginalAttributes[Idx].PciIo->Attributes (
 | 
						|
                                     OriginalAttributes[Idx].PciIo,
 | 
						|
                                     EfiPciIoAttributeOperationSet,
 | 
						|
                                     OriginalAttributes[Idx].PciAttributes,
 | 
						|
                                     NULL
 | 
						|
                                     );
 | 
						|
  }
 | 
						|
  FreePool (OriginalAttributes);
 | 
						|
}
 |