From nobody Thu Dec 26 12:09:06 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) smtp.mailfrom=edk2-devel-bounces@lists.01.org Return-Path: Received: from ml01.01.org (ml01.01.org [198.145.21.10]) by mx.zohomail.com with SMTPS id 1506369533005825.4067511220313; Mon, 25 Sep 2017 12:58:53 -0700 (PDT) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id B99812095E50C; Mon, 25 Sep 2017 12:55:35 -0700 (PDT) Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 2D7DC20945BDD for ; Mon, 25 Sep 2017 12:55:32 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 7410481DFE; Mon, 25 Sep 2017 19:58:43 +0000 (UTC) Received: from lacos-laptop-7.usersys.redhat.com (ovpn-120-161.rdu2.redhat.com [10.10.120.161]) by smtp.corp.redhat.com (Postfix) with ESMTP id 06F3060176; Mon, 25 Sep 2017 19:58:41 +0000 (UTC) X-Original-To: edk2-devel@lists.01.org Received-SPF: none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) client-ip=198.145.21.10; envelope-from=edk2-devel-bounces@lists.01.org; helo=ml01.01.org; Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=209.132.183.28; helo=mx1.redhat.com; envelope-from=lersek@redhat.com; receiver=edk2-devel@lists.01.org DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 7410481DFE Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=lersek@redhat.com From: Laszlo Ersek To: edk2-devel-01 Date: Mon, 25 Sep 2017 21:58:24 +0200 Message-Id: <20170925195824.10866-8-lersek@redhat.com> In-Reply-To: <20170925195824.10866-1-lersek@redhat.com> References: <20170925195824.10866-1-lersek@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.25]); Mon, 25 Sep 2017 19:58:43 +0000 (UTC) Subject: [edk2] [PATCH 7/7] OvmfPkg/PciHotPlugInitDxe: translate QEMU's resource reservation hints X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Marcel Apfelbaum , Jordan Justen , Ruiyu Ni MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Errors-To: edk2-devel-bounces@lists.01.org Sender: "edk2-devel" X-ZohoMail: RSF_4 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Parse QEMU_PCI_BRIDGE_CAPABILITY_RESOURCE_RESERVATION from the bridges' conventional config spaces. Translate the fields as follows: * BusNumbers: * 0 -- no reservation; * (-1) -- firmware default, i.e. no reservation; * otherwise -- reserve the requested value. (NB, bus number reservation is not supposed to work before is fixed.) * Io: * 0 -- no reservation; * (-1) -- keep our current default (512B); * otherwise -- round up the requested value and reserve that. * NonPrefetchable32BitMmio: * 0 -- no reservation; * (-1) -- keep our current default (2MB); * otherwise -- round up the requested value and reserve that. * Prefetchable32BitMmio: * 0 -- no reservation, proceed to Prefetchable64BitMmio; * (-1) -- firmware default, i.e. no reservation, proceed to Prefetchable64BitMmio; * otherwise -- round up the requested value and reserve that. (NB, if Prefetchable32BitMmio is reserved in addition to NonPrefetchable32BitMmio, then PciBusDxe currently runs into an assertion failure. Refer to .) * Prefetchable64BitMmio: * only reached if Prefetchable32BitMmio was not reserved; * 0 -- no reservation; * (-1) -- firmware default, i.e. no reservation; * otherwise -- round up the requested value and reserve that. If QEMU_PCI_BRIDGE_CAPABILITY_RESOURCE_RESERVATION is missing, plus any time the rounding fails, fall back to the current defaults. Cc: Jordan Justen Cc: Marcel Apfelbaum Cc: Ruiyu Ni Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Laszlo Ersek Reviewed-by: Jordan Justen --- OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf | 2 + OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.c | 371 +++++++++++++++++++- 2 files changed, 367 insertions(+), 6 deletions(-) diff --git a/OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf b/OvmfPkg/PciHotP= lugInitDxe/PciHotPlugInit.inf index e0ec9baae1c2..38043986eb67 100644 --- a/OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf +++ b/OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf @@ -26,15 +26,17 @@ [Sources] =20 [Packages] MdeModulePkg/MdeModulePkg.dec MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec =20 [LibraryClasses] BaseLib BaseMemoryLib DebugLib DevicePathLib MemoryAllocationLib + PciLib UefiBootServicesTableLib UefiDriverEntryPoint =20 [Protocols] diff --git a/OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.c b/OvmfPkg/PciHotPlu= gInitDxe/PciHotPlugInit.c index 39646973794b..177e1a62120d 100644 --- a/OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.c +++ b/OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.c @@ -13,14 +13,16 @@ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ =20 #include +#include =20 #include #include #include #include #include +#include #include =20 #include #include @@ -245,8 +247,239 @@ HighBitSetRoundUp64 ( return (HighBit < 64) ? HighBit : -1; } =20 =20 +/** + Read a slice from conventional PCI config space at the given offset, then + advance the offset. + + @param[in] PciAddress The address of the PCI Device -- Bus, Device, Fun= ction + -- in UEFI (not PciLib) encoding. + + @param[in,out] Offset On input, the offset in conventional PCI config s= pace + to start reading from. On output, the offset of t= he + first byte that was not read. + + @param[in] Size The number of bytes to read. + + @param[out] Buffer On output, the bytes read from PCI config space a= re + stored in this object. +**/ +STATIC +VOID +ReadConfigSpace ( + IN CONST EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *PciAddress, + IN OUT UINT8 *Offset, + IN UINT8 Size, + OUT VOID *Buffer + ) +{ + PciReadBuffer ( + PCI_LIB_ADDRESS ( + PciAddress->Bus, + PciAddress->Device, + PciAddress->Function, + *Offset + ), + Size, + Buffer + ); + *Offset +=3D Size; +} + + +/** + Convenience wrapper macro for ReadConfigSpace(). + + Given the following conditions: + + - HeaderField is the first field in the structure pointed-to by Struct, + + - Struct->HeaderField has been populated from the conventional PCI config + space of the PCI device identified by PciAddress, + + - *Offset points one past HeaderField in the conventional PCI config spa= ce of + the PCI device identified by PciAddress, + + populate the rest of *Struct from conventional PCI config space, startin= g at + *Offset. Finally, increment *Offset so that it point one past *Struct. + + @param[in] PciAddress The address of the PCI Device -- Bus, Device, Fun= ction + -- in UEFI (not PciLib) encoding. Type: pointer to + CONST EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS. + + @param[in,out] Offset On input, the offset in conventional PCI config s= pace + to start reading from; one past Struct->HeaderFie= ld. + On output, the offset of the first byte that was = not + read; one past *Struct. Type: pointer to UINT8. + + @param[out] Struct The structure to complete. Type: pointer to struc= ture + object. + + @param[in] HeaderField The name of the first field in *Struct, after whi= ch + *Struct should be populated. Type: structure memb= er + identifier. +**/ +#define COMPLETE_CONFIG_SPACE_STRUCT(PciAddress, Offset, Struct, HeaderFie= ld) \ + ReadConfigSpace ( = \ + (PciAddress), = \ + (Offset), = \ + (UINT8)(sizeof *(Struct) - sizeof ((Struct)->HeaderField)), = \ + &((Struct)->HeaderField) + 1 = \ + ) + + +/** + Look up the QEMU-specific Resource Reservation capability in the convent= ional + config space of a Hotplug Controller (that is, PCI Bridge). + + This function performs as few config space reads as possible. + + @param[in] HpcPciAddress The address of the PCI Bridge -- Bus, Devic= e, + Function -- in UEFI (not PciLib) encoding. + + @param[out] ReservationHint The caller-allocated capability structure to + populate from the PCI Bridge's config space. + + @retval EFI_SUCCESS The capability has been found, ReservationHint has + been populated. + + @retval EFI_NOT_FOUND The capability is missing. The contents of + ReservationHint are now indeterminate. +**/ +STATIC +EFI_STATUS +QueryReservationHint ( + IN CONST EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *HpcPciAddress, + OUT QEMU_PCI_BRIDGE_CAPABILITY_RESOURCE_RESERVATION *ReservationHint +) +{ + UINT16 PciVendorId; + UINT16 PciStatus; + UINT8 PciCapPtr; + UINT8 Offset; + + // + // Check the vendor identifier. + // + PciVendorId =3D PciRead16 ( + PCI_LIB_ADDRESS ( + HpcPciAddress->Bus, + HpcPciAddress->Device, + HpcPciAddress->Function, + PCI_VENDOR_ID_OFFSET + ) + ); + if (PciVendorId !=3D QEMU_PCI_BRIDGE_VENDOR_ID_REDHAT) { + return EFI_NOT_FOUND; + } + + // + // Check the Capabilities List bit in the PCI Status Register. + // + PciStatus =3D PciRead16 ( + PCI_LIB_ADDRESS ( + HpcPciAddress->Bus, + HpcPciAddress->Device, + HpcPciAddress->Function, + PCI_PRIMARY_STATUS_OFFSET + ) + ); + if ((PciStatus & EFI_PCI_STATUS_CAPABILITY) =3D=3D 0) { + return EFI_NOT_FOUND; + } + + // + // Fetch the start of the Capabilities List. + // + PciCapPtr =3D PciRead8 ( + PCI_LIB_ADDRESS ( + HpcPciAddress->Bus, + HpcPciAddress->Device, + HpcPciAddress->Function, + PCI_CAPBILITY_POINTER_OFFSET + ) + ); + + // + // Scan the Capabilities List until we find the terminator element, or t= he + // Resource Reservation capability. + // + for (Offset =3D PciCapPtr & 0xFC; + Offset > 0; + Offset =3D ReservationHint->BridgeHdr.VendorHdr.Hdr.NextItemPtr & 0= xFC) { + BOOLEAN EnoughRoom; + + // + // Check if the Resource Reservation capability would fit into config = space + // at this offset. + // + EnoughRoom =3D (BOOLEAN)( + Offset <=3D PCI_MAX_CONFIG_OFFSET - sizeof *Reservation= Hint + ); + + // + // Read the standard capability header so we can check the capability = ID + // (if necessary) and advance to the next capability. + // + ReadConfigSpace ( + HpcPciAddress, + &Offset, + (UINT8)sizeof ReservationHint->BridgeHdr.VendorHdr.Hdr, + &ReservationHint->BridgeHdr.VendorHdr.Hdr + ); + if (!EnoughRoom || + (ReservationHint->BridgeHdr.VendorHdr.Hdr.CapabilityID !=3D + EFI_PCI_CAPABILITY_ID_VENDOR)) { + continue; + } + + // + // Read the rest of the vendor capability header so we can check the + // capability length. + // + COMPLETE_CONFIG_SPACE_STRUCT ( + HpcPciAddress, + &Offset, + &ReservationHint->BridgeHdr.VendorHdr, + Hdr + ); + if (ReservationHint->BridgeHdr.VendorHdr.Length !=3D + sizeof *ReservationHint) { + continue; + } + + // + // Read the rest of the QEMU bridge capability header so we can check = the + // capability type. + // + COMPLETE_CONFIG_SPACE_STRUCT ( + HpcPciAddress, + &Offset, + &ReservationHint->BridgeHdr, + VendorHdr + ); + if (ReservationHint->BridgeHdr.Type !=3D + QEMU_PCI_BRIDGE_CAPABILITY_TYPE_RESOURCE_RESERVATION) { + continue; + } + + // + // Read the body of the reservation hint. + // + COMPLETE_CONFIG_SPACE_STRUCT ( + HpcPciAddress, + &Offset, + ReservationHint, + BridgeHdr + ); + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + + /** Returns a list of root Hot Plug Controllers (HPCs) that require initialization during the boot process. =20 @@ -401,18 +634,21 @@ GetResourcePadding ( OUT VOID **Padding, OUT EFI_HPC_PADDING_ATTRIBUTES *Attributes ) { + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *Address; BOOLEAN DefaultIo; BOOLEAN DefaultMmio; RESOURCE_PADDING ReservationRequest; EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *FirstResource; + EFI_STATUS ReservationHintStatus; + QEMU_PCI_BRIDGE_CAPABILITY_RESOURCE_RESERVATION ReservationHint; + + Address =3D (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *)&HpcPciAddres= s; =20 DEBUG_CODE ( - EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *Address; CHAR16 *DevicePathString; =20 - Address =3D (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *)&HpcPciAddr= ess; DevicePathString =3D ConvertDevicePathToText (HpcDevicePath, FALSE, FA= LSE); =20 DEBUG ((EFI_D_VERBOSE, "%a: Address=3D%02x:%02x.%x DevicePath=3D%s\n", __FUNCTION__, Address->Bus, Address->Device, Address->Function, @@ -439,20 +675,143 @@ GetResourcePadding ( FirstResource =3D ReservationRequest.Padding + ARRAY_SIZE (ReservationRequest.Padding); =20 // - // (b) Reserve IO space. + // Try to get the QEMU-specific Resource Reservation capability. // + ReservationHintStatus =3D QueryReservationHint (Address, &ReservationHin= t); + if (!EFI_ERROR (ReservationHintStatus)) { + INTN HighBit; + + DEBUG (( + DEBUG_VERBOSE, + "%a: BusNumbers=3D0x%x Io=3D0x%Lx NonPrefetchable32BitMmio=3D0x%x\n" + "%a: Prefetchable32BitMmio=3D0x%x Prefetchable64BitMmio=3D0x%Lx\n", + __FUNCTION__, + ReservationHint.BusNumbers, + ReservationHint.Io, + ReservationHint.NonPrefetchable32BitMmio, + __FUNCTION__, + ReservationHint.Prefetchable32BitMmio, + ReservationHint.Prefetchable64BitMmio + )); + + // + // (a) Reserve bus numbers. + // + switch (ReservationHint.BusNumbers) { + case 0: + // + // No reservation needed. + // + break; + case MAX_UINT32: + // + // Firmware default (unspecified). Treat it as "no reservation neede= d". + // + break; + default: + // + // Request the specified amount. + // + --FirstResource; + FirstResource->ResType =3D ACPI_ADDRESS_SPACE_TYPE_BUS; + FirstResource->AddrLen =3D ReservationHint.BusNumbers; + break; + } + + // + // (b) Reserve IO space. + // + switch (ReservationHint.Io) { + case 0: + // + // No reservation needed, disable our built-in. + // + DefaultIo =3D FALSE; + break; + case MAX_UINT64: + // + // Firmware default (unspecified). Stick with our built-in. + // + break; + default: + // + // Round the specified amount up to the next power of two. If roundi= ng is + // successful, reserve the rounded value. Fall back to the default + // otherwise. + // + HighBit =3D HighBitSetRoundUp64 (ReservationHint.Io); + if (HighBit !=3D -1) { + SetIoPadding (--FirstResource, (UINTN)HighBit); + DefaultIo =3D FALSE; + } + break; + } + + // + // (c) Reserve non-prefetchable MMIO space (32-bit only). + // + switch (ReservationHint.NonPrefetchable32BitMmio) { + case 0: + // + // No reservation needed, disable our built-in. + // + DefaultMmio =3D FALSE; + break; + case MAX_UINT32: + // + // Firmware default (unspecified). Stick with our built-in. + // + break; + default: + // + // Round the specified amount up to the next power of two. If roundi= ng is + // successful, reserve the rounded value. Fall back to the default + // otherwise. + // + HighBit =3D HighBitSetRoundUp32 (ReservationHint.NonPrefetchable32Bi= tMmio); + if (HighBit !=3D -1) { + SetMmioPadding (--FirstResource, FALSE, TRUE, (UINTN)HighBit); + DefaultMmio =3D FALSE; + } + break; + } + + // + // (d) Reserve prefetchable MMIO space (either 32-bit or 64-bit, never + // both). + // + // For either space, we treat 0 as "no reservation needed", and the ma= ximum + // value as "firmware default". The latter is unspecified, and we inte= rpret + // it as the former. + // + // Otherwise, round the specified amount up to the next power of two. = If + // rounding is successful, reserve the rounded value. Do not reserve + // prefetchable MMIO space otherwise. + // + if (ReservationHint.Prefetchable32BitMmio > 0 && + ReservationHint.Prefetchable32BitMmio < MAX_UINT32) { + HighBit =3D HighBitSetRoundUp32 (ReservationHint.Prefetchable32BitMm= io); + if (HighBit !=3D -1) { + SetMmioPadding (--FirstResource, TRUE, TRUE, (UINTN)HighBit); + } + } else if (ReservationHint.Prefetchable64BitMmio > 0 && + ReservationHint.Prefetchable64BitMmio < MAX_UINT64) { + HighBit =3D HighBitSetRoundUp64 (ReservationHint.Prefetchable64BitMm= io); + if (HighBit !=3D -1) { + SetMmioPadding (--FirstResource, TRUE, FALSE, (UINTN)HighBit); + } + } + } + if (DefaultIo) { // // Request defaults. // SetIoPadding (--FirstResource, (UINTN)HighBitSetRoundUp64 (512)); } =20 - // - // (c) Reserve non-prefetchable MMIO space (32-bit only). - // if (DefaultMmio) { // // Request defaults. // --=20 2.14.1.3.gb7cf6e02401b _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel