From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: 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 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 1CAAA21959CB2 for ; Thu, 4 Oct 2018 10:00:30 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id EBD86C04F4C2; Thu, 4 Oct 2018 17:00:29 +0000 (UTC) Received: from lacos-laptop-7.usersys.redhat.com (ovpn-120-104.rdu2.redhat.com [10.10.120.104]) by smtp.corp.redhat.com (Postfix) with ESMTP id E48001057047; Thu, 4 Oct 2018 17:00:28 +0000 (UTC) To: Hristo Mihaylov , "edk2-devel@lists.01.org" References: From: Laszlo Ersek Message-ID: <45fadad5-6dce-27c1-6479-f11e8cc5992a@redhat.com> Date: Thu, 4 Oct 2018 19:00:27 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.9.1 MIME-Version: 1.0 In-Reply-To: X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.31]); Thu, 04 Oct 2018 17:00:30 +0000 (UTC) Subject: Re: RuntimeDXE unable to save variables to flash X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 04 Oct 2018 17:00:31 -0000 Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 7bit On 10/04/18 12:56, Hristo Mihaylov wrote: > Hello, > > I have an issue that I don't know how to fix. I'm building a BIOS for a custom > x86_64 platform. > > The first part of the problem is: the BIOS boots to the end of DXE where > it crashes with a General Protection exception. > > ``` > !!!! X64 Exception Type - 0D(#GP - General Protection) CPU Apic ID - 00000027 !!!! > RIP - 0000000077ADE823, CS - 0000000000000038, RFLAGS - 0000000000010047 > ExceptionData - 0000000000000000 > RAX - 00000000FFFFFFFF, RCX - 00000000000001FE, RDX - 0000000000000000 > RBX - 00000000FFFFFFFF, RSP - 0000000075B443C0, RBP - 00000000800FD000 > RSI - 0000000000000001, RDI - 0000000077AFC520 > R8 - 0000000077AFC618, R9 - 0000000000000001, R10 - 0000000070000000 > R11 - 00000000758B7F10, R12 - 0000000077AFC510, R13 - 0000000000000001 > R14 - 0000000080000000, R15 - 0000000002C30064 > DS - 0000000000000020, ES - 0000000000000020, FS - 0000000000000020 > GS - 0000000000000020, SS - 0000000000000020 > CR0 - 0000000080000033, CR2 - 0000000000000000, CR3 - 0000000075977000 > CR4 - 0000000000000668, CR8 - 0000000000000000 > DR0 - 0000000000000000, DR1 - 0000000000000000, DR2 - 0000000000000000 > DR3 - 0000000000000000, DR6 - 00000000FFFF0FF0, DR7 - 0000000000000400 > GDTR - 0000000075986928 000000000000004F, LDTR - 0000000000000000 > IDTR - 0000000077ACF400 00000000000001FF, TR - 0000000000000040 > FXSAVE_STATE - 0000000075B44020 > ``` > > This exception occurs when the BIOS is enabling or disabling the write > protection of the SPI. It only crashes when it's doing the protection in SMM. > The code responsible for that is: > > ``` > EFI_STATUS > EFIAPI > DisableBiosWriteProtect ( > VOID > ) > { > UINTN SpiBaseAddress; > UINT32 Data32; > > SpiBaseAddress = MmPciBase ( > DEFAULT_PCI_BUS_NUMBER_PCH, > PCI_DEVICE_NUMBER_PCH_SPI, > PCI_FUNCTION_NUMBER_PCH_SPI > ); > // Write clear BC_SYNC_SS prior to change WPD from 0 to 1. > // > MmioOr8 ( > SpiBaseAddress + R_PCH_SPI_BC + 1, > (B_PCH_SPI_BC_SYNC_SS >> 8) > ); > /// > /// Set BIOSWE bit (SPI PCI Offset DCh [0]) = 1b > /// Enable the access to the BIOS space for both read and write cycles > /// > MmioOr8 ( > SpiBaseAddress + R_PCH_SPI_BC, > B_PCH_SPI_BC_WPD > ); > /// > /// PCH BIOS Spec Section 3.7 BIOS Region SMM Protection Enabling > /// If the following steps are implemented: > /// - Set the EISS bit (SPI PCI Offset DCh [5]) = 1b > /// - Follow the 1st recommendation in section 3.6 > /// the BIOS Region can only be updated by following the steps bellow: > /// - Once all threads enter SMM > /// - Read memory location FED30880h OR with 00000001h, place the result in EAX, > /// and write data to lower 32 bits of MSR 1FEh (sample code available) > /// - Set BIOSWE bit (SPI PCI Offset DCh [0]) = 1b > /// - Modify BIOS Region > /// - Clear BIOSWE bit (SPI PCI Offset DCh [0]) = 0b > /// > if ((MmioRead8 (SpiBaseAddress + R_PCH_SPI_BC) & B_PCH_SPI_BC_EISS) != 0) { > /// > /// Read memory location FED30880h OR with 00000001h, place the result in EAX, > /// and write data to lower 32 bits of MSR 1FEh (sample code available) > /// > Data32 = MmioRead32 ((UINTN) (0xFED30880)) | BIT0; > > // crash occurs here > AsmWriteMsr32 (0x1FE, Data32); > } > > return EFI_SUCCESS; > } > > VOID > EFIAPI > EnableBiosWriteProtect ( > VOID > ) > { > UINTN SpiBaseAddress; > UINT32 Data32; > > SpiBaseAddress = MmPciBase ( > DEFAULT_PCI_BUS_NUMBER_PCH, > PCI_DEVICE_NUMBER_PCH_SPI, > PCI_FUNCTION_NUMBER_PCH_SPI > ); > /// > /// Clear BIOSWE bit (SPI PCI Offset DCh [0]) = 0b > /// Disable the access to the BIOS space for write cycles > /// > MmioAnd8 ( > SpiBaseAddress + R_PCH_SPI_BC, > (UINT8) (~B_PCH_SPI_BC_WPD) > ); > > /// > /// Check if EISS bit is set > /// > if (((MmioRead8 (SpiBaseAddress + R_PCH_SPI_BC)) & B_PCH_SPI_BC_EISS) == B_PCH_SPI_BC_EISS) { > /// > /// Read memory location FED30880h AND with FFFFFFFEh, place the result in EAX, > /// and write data to lower 32 bits of MSR 1FEh (sample code available) > /// > Data32 = MmioRead32 ((UINTN) (0xFED30880)) & (~BIT0); > > // crash occurs here > //AsmWriteMsr32 (0x1FE, Data32); > } > } > ``` > > I can work around the crash by hardcoding the values 0x0 and 0x1, or commenting out the writes. Accessing invalid MSRs may raise injections. How do you know the MSR 0x1FE is valid (and the Data32 value is valid)? > > The second part is that variables cannot be saved or updated: > > ``` > Variable driver flush the HOB variable to flash: {guid} TCG2_CONFIGURATION Invalid Parameter > Variable driver flush the HOB variable to flash: {guid} Setup Invalid Parameter > Variable driver flush the HOB variable to flash: {guid} PchRcConfiguration Invalid Parameter > Variable driver flush the HOB variable to flash: {guid} MeRcConfiguration Invalid Parameter > Variable driver flush the HOB variable to flash: {guid} IeRcConfiguration Invalid Parameter > Variable driver flush the HOB variable to flash: {guid} SocketIioConfig Invalid Parameter > Variable driver flush the HOB variable to flash: {guid} SocketCommonRcConfig Invalid Parameter > Variable driver flush the HOB variable to flash: {guid} SocketMpLinkConfig Invalid Parameter > Variable driver flush the HOB variable to flash: {guid} SocketMemoryConfig Invalid Parameter > Variable driver flush the HOB variable to flash: {guid} SocketPowerManagementConfig Invalid Parameter > Variable driver flush the HOB variable to flash: {guid} SocketProcessorCoreConfig Invalid Parameter > Variable driver flush the HOB variable to flash: {guid} FpgaSocketConfig Invalid Parameter > ``` > > I can't save variables to non-volatile storage, I can save some to volatile > storage, but others I cannot save at all. > > I tracked the failure down to here: https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c#L338 > and I'm not sure how to proceed. The platform is based on UDK2015. That's an Fvb->Write() call. Do you have access to the source of the flash driver (which produces the FVB protocol instance)? (FWIW, EFI_INVALID_PARAMETER doesn't seem a spec-compliant return value for EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL.Write()). Thanks Laszlo