public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Laszlo Ersek" <lersek@redhat.com>
To: Jon Nettleton <jon@solid-run.com>, devel@edk2.groups.io
Cc: Ard Biesheuvel <ardb@kernel.org>, Hao A Wu <hao.a.wu@intel.com>,
	Liming Gao <gaoliming@byosoft.com.cn>,
	"Ard Biesheuvel (TianoCore)" <ardb+tianocore@kernel.org>,
	"Leif Lindholm (Nuvia address)" <leif@nuviainc.com>
Subject: Re: [edk2-devel] Conflicting virtual addresses causing Runtime Services issues
Date: Fri, 12 Mar 2021 20:30:51 +0100	[thread overview]
Message-ID: <cb7b6b8a-ecd0-8f8c-74bd-787c8ba77b2a@redhat.com> (raw)
In-Reply-To: <CABdtJHtbj97_nbN87i3TDbPLVs8DP684zAYCUNv7AxrpayttkA@mail.gmail.com>

On 03/12/21 06:59, Jon Nettleton wrote:
> On Fri, Mar 12, 2021 at 4:02 AM Jon Nettleton via groups.io
> <jon=solid-run.com@groups.io> wrote:
>> On Thu, Mar 11, 2021 at 11:39 PM Ard Biesheuvel <ardb@kernel.org> wrote:
>>> On Thu, 11 Mar 2021 at 23:25, Laszlo Ersek <lersek@redhat.com> wrote:
>>>> Adding Ard and Leif, comments below:
>>>>
>>>> On 03/11/21 15:50, Laszlo Ersek wrote:
>>>>> On 03/11/21 10:48, Jon Nettleton wrote:
>>>> [...]
>>>>
>>>>>> And this is where the pointer gets remapped again and into the MMIO
>>>>>> space of the nor flash.  If I remove the calls to ConvertPointer for
>>>>>> the FvbProtocol I am still seeing those addresses getting remapped
>>>>>> but only once and runtime works as expected.
>>>>>>
>>>>>> I am seeing that in
>>>>>> MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c
>>>>>> &mVariableModuleGlobal->FvbInstance->* are all being converted.  It
>>>>>> is possible this is a long standing bug and it just so happens that
>>>>>> our configuration has caused a conflict and exposed it.
>>>>> Yes, this is curious, I noticed it too yesterday, trying to see where
>>>>> the FVB protocol member function pointers were converted. I found that
>>>>> OVMF's flash driver (OvmfPkg/QemuFlashFvbServicesRuntimeDxe) didn't do
>>>>> it, but MdeModulePkg/Universal/Variable/RuntimeDxe did. That was
>>>>> certainly strange, as the variable driver is a consumer of the
>>>>> protocol (not the producer thereof), so I'd say it has no business
>>>>> poking new values into the protocol interface structure.
>>>> [...]
>>>>
>>>>> ... Strangely, the other flash (FVB) driver in edk2,
>>>>> ArmPlatformPkg/Drivers/NorFlashDxe, *does* perform the conversion
>>>>> itself! See NorFlashVirtualNotifyEvent().
>>>>>
>>>>> I don't understand that. Is it possible that, with
>>>>> "ArmPlatformPkg/Drivers/NorFlashDxe" too, the conversion happens
>>>>> *twice*, but (at least) one of those mappings is "identity"?
>>>> Confirmed.
>>>>
>>>> I had to write some elaborate debug patches for determining this,
>>>> because in ArmVirtQemu, I cannot produce DEBUG output from the
>>>> SetVirtualAddressMap() notification functions. So here's the approach I
>>>> took:
>>>>
>>>> (1) Introduce a new GUID-ed HOB structure in MdeModulePkg. The structure
>>>> itself lives in reserved memory, but its address is exposed in a GUID-ed
>>>> HOB. The structure is named FVB_ADDRESS_LIST, and it has the following
>>>> fields:
>>>>
>>>>   - signature ("FVBADRLS" -- FVB Address List)
>>>>   - 16 entries of:
>>>>     - owner signature [what driver set this entry]
>>>>     - address
>>>>   - number of entries used (aka next entry to fill)
>>>>
>>>> (2) In PlatformPei, allocate and initialize this structure (in reserved
>>>> memory), and expose its address via the GUID-ed HOB. Furthermore,
>>>> produce a log message with the allocation address.
>>>>
>>>> (3) In NorFlashDxe, look up the structure via the GUID-ed HOB, in the
>>>> entry point function; remember the address in a global variable. In the
>>>> SetVirtualAddressMap() handler function, treat the conversion of the
>>>> "GetPhysicalAddress" FVB member function specially: via the global
>>>> variable pointer to FVB_ADDRESS_LIST in reserved memory, save both the
>>>> physical (original) and the virtual (converted) address of the
>>>> "GetPhysicalAddress" FVB member function, in new entries. As owner
>>>> signature in both entries, use "NORFLASH".
>>>>
>>>> (4) In the runtime DXE variable driver, do the exact same thing, just
>>>> use a different "owner signature" -- "VARIABLE".
>>>>
>>>> (5) Once the guest is up and running, run "efibootmgr --delete-timeout"
>>>> at a root prompt in the guest, deleting the existent "Timeout" UEFI
>>>> non-volatile variable, for verifying that the runtime variable (write)
>>>> service is functional.
>>>>
>>>> (6) Using the log message from point (2):
>>>>
>>>>> PlatformPeim: FvbAddressList @ 13FEC9000
>>>> hexdump the guest memory containing the FVB_ADDRESS_LIST, as follows:
>>>>
>>>>> $ virsh qemu-monitor-command aavmf.rhel7.registered --hmp xp  /268cb 0x13FEC9000
>>>> Ccomments to the right of the hexdump:
>>>>
>>>>> 000000013fec9000: 'F' 'V' 'B' 'A' 'D' 'R' 'L' 'S'                         <- structure signature: FVBADRLS
>>>>> 000000013fec9008: 'N' 'O' 'R' 'F' 'L' 'A' 'S' 'H'                         <- entry[0], signature: NORFLASH
>>>>> 000000013fec9010: 'T' ' ' '\xc6' ';' '\x01' '\x00' '\x00' '\x00'          <- entry[0], GetPhysicalAddress *physical*: 0x000000013bc62054
>>>>> 000000013fec9018: 'N' 'O' 'R' 'F' 'L' 'A' 'S' 'H'                         <- entry[1], signature: NORFLASH
>>>>> 000000013fec9020: 'T' ' ' 'N' '$' '\x00' '\x00' '\x00' '\x00'             <- entry[1], GetPhysicalAddress *virtual*: 0x00000000244e2054
>>>>> 000000013fec9028: 'V' 'A' 'R' 'I' 'A' 'B' 'L' 'E'                         <- entry[2], signature: VARIABLE
>>>>> 000000013fec9030: 'T' ' ' 'N' '$' '\x00' '\x00' '\x00' '\x00'             <- entry[2], GetPhysicalAddress *physical*: 0x00000000244e2054
>>>>> 000000013fec9038: 'V' 'A' 'R' 'I' 'A' 'B' 'L' 'E'                         <- entry[3], signature: VARIABLE
>>>>> 000000013fec9040: 'T' ' ' 'N' '$' '\x00' '\x00' '\x00' '\x00'             <- entry[3], GetPhysicalAddress *virtual*: 0x00000000244e2054
>>>>> 000000013fec9048: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec9050: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec9058: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec9060: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec9068: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec9070: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec9078: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec9080: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec9088: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec9090: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec9098: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec90a0: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec90a8: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec90b0: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec90b8: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec90c0: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec90c8: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec90d0: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec90d8: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec90e0: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec90e8: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec90f0: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec90f8: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec9100: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>>>>> 000000013fec9108: '\x04' '\x00' '\x00' '\x00'                             <- number of entries used: 4
>>>> This shows the following:
>>>>
>>>> - both NorFlashDxe and the runtime DXE variable driver converted the
>>>>   FVB.GetPhysicalAddress member function,
>>>>
>>>> - the NorFlashDxe driver acted first, the runtime DXE variable driver
>>>>   acted second,
>>>>
>>>> - when the runtime DXE variable driver "converted" the "physical"
>>>>   address to virtual address, there was no change (and no crash!),
>>>>   because the virtual address map passed in by the Linux kernel
>>>>   apparently identity maps this area -- just as I guessed.
>>>>
>>>> So we definitely have a bug (only Linux's page tables save us from the
>>>> crash); now the question is:
>>>>
>>>> Which driver is wrong to even attempt the conversion of the FVB member
>>>> functions?
>>>>
>>>> The answer must be documented somewhere highly visible.
>>>>
>>>> Debug patches attached, for the record (based on commit edd46cd407ea).
>>>>
>>> Thanks for inviting me to this party!
>>>
>>> So the tl;dr here is that some points get converted twice, which
>>> usually is not a problem because the virtual address resulting from
>>> the conversion is rarely mistaken for a physical address living in a
>>> EFI_MEMORY_RUNTIME region.
>>>
>>> So I agree with Laszlo's assertion that the consumer of a protocol has
>>> no business updating its protocol pointers, so this should definitely
>>> be fixed in the core VariableRuntime driver. However, given the
>>> typical nature of the variable stack, i.e., a platform specfic NOR
>>> flash driver combined with the generic FTW and variable drivers, doing
>>> so would likely break many out of tree platforms where the NOR flash
>>> driver does not bother to update its pointers at all.
>> Not ideal, but we could add a flag to Runtime Services and let the platform
>> specify if it is remapping the FVB.  This would allow us to not break legacy
>> drivers, but still easily let properly designed platforms bypass this
>> behaviour.  As time progressed this could be used to for a deprecation
>> warning, until it became the default handling of FVB pointer conversion.
>>
> Something like the attached patch possibly?
> 
> 
> 0001-MdeModulePkg-Variable-VariableRuntimeDxe-Add-Bool-Pc.patch
> 
> 
> From 126bd8bebf24e0269696f22a282a4b0340d9923b Mon Sep 17 00:00:00 2001
> From: Jon Nettleton <jon@solid-run.com>
> Date: Fri, 12 Mar 2021 06:52:56 +0100
> Subject: [PATCH] MdeModulePkg/Variable/VariableRuntimeDxe: Add Bool
>  PcdDriverConvertsFvbFuncPointers
> 
> VariableRuntimeDxe is unconditionally converting the pointers to the Fvb protocol
> functions even if the platform drivers are already converting the pointers
> themselves.  This leads to a double pointer conversion and can cause unexpected
> runtime behaviour depending on the memory layout of the platform.
> 
> Since we don't want to break legacy and out of tree platforms we add a Bool flag
> that defaults to the existing behaviour but allows platforms to only use the
> pointer conversion being done in their driver implementation that produces the
> FVB Protocol.
> 
> Signed-off-by: Jon Nettleton <jon@solid-run.com>
> ---
>  MdeModulePkg/MdeModulePkg.dec                    |  6 ++++++
>  .../Universal/Variable/RuntimeDxe/VariableDxe.c  | 16 +++++++++-------
>  .../Variable/RuntimeDxe/VariableRuntimeDxe.inf   |  1 +
>  3 files changed, 16 insertions(+), 7 deletions(-)
> 
> diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec
> index 1483955110..07b84b1837 100644
> --- a/MdeModulePkg/MdeModulePkg.dec
> +++ b/MdeModulePkg/MdeModulePkg.dec
> @@ -1130,6 +1130,12 @@
>    # @Prompt Reclaim variable space at EndOfDxe.
>    gEfiMdeModulePkgTokenSpaceGuid.PcdReclaimVariableSpaceAtEndOfDxe|FALSE|BOOLEAN|0x30000008
>  
> +  ## Driver converts FVB function pointers.<BR><BR>
> +  # The value is FALSE as default to retain the current behaviour and retain compatibility with out of tree platorms.<BR>
> +  # If the value is set to TRUE, variable driver does not convert the FVB function pointers.<BR>
> +  # @Prompt Platform driver converts FVB pointers.
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdDriverConvertsFvbFuncPointers|FALSE|BOOLEAN|0x3000000b
> +
>    ## The size of volatile buffer. This buffer is used to store VOLATILE attribute variables.
>    # @Prompt Variable storage size.
>    gEfiMdeModulePkgTokenSpaceGuid.PcdVariableStoreSize|0x10000|UINT32|0x30000005
> diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c
> index 0fca0bb2a9..ede2a66682 100644
> --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c
> +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c
> @@ -247,13 +247,15 @@ VariableClassAddressChangeEvent (
>    UINTN          Index;
>  
>    if (mVariableModuleGlobal->FvbInstance != NULL) {
> -    EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->GetBlockSize);
> -    EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->GetPhysicalAddress);
> -    EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->GetAttributes);
> -    EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->SetAttributes);
> -    EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->Read);
> -    EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->Write);
> -    EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->EraseBlocks);
> +    if (!PcdGetBool (PcdDriverConvertsFvbFuncPointers)) {
> +      EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->GetBlockSize);
> +      EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->GetPhysicalAddress);
> +      EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->GetAttributes);
> +      EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->SetAttributes);
> +      EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->Read);
> +      EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->Write);
> +      EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->EraseBlocks);
> +    }
>      EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance);
>    }
>    EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->PlatformLangCodes);
> diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
> index c9434df631..f3373f8137 100644
> --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
> +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
> @@ -137,6 +137,7 @@
>    gEfiMdeModulePkgTokenSpaceGuid.PcdMaxUserNvVariableSpaceSize           ## CONSUMES
>    gEfiMdeModulePkgTokenSpaceGuid.PcdBoottimeReservedNvVariableSpaceSize  ## CONSUMES
>    gEfiMdeModulePkgTokenSpaceGuid.PcdReclaimVariableSpaceAtEndOfDxe  ## CONSUMES
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdDriverConvertsFvbFuncPointers   ## CONSUMES
>    gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvModeEnable         ## SOMETIMES_CONSUMES
>    gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvStoreReserved      ## SOMETIMES_CONSUMES
>  
> -- 2.27.0
> 

(1) I suggest filing a TianoCore BZ for this, and then submitting the
patch to the mailing list with git-send-email. Please don't forget to CC
the Variable Driver reviewers / maintainers.

official:

https://github.com/tianocore/tianocore.github.io/wiki/EDK-II-Development-Process

unofficial hints:

https://github.com/tianocore/tianocore.github.io/wiki/Laszlo%27s-unkempt-git-guide-for-edk2-contributors-and-maintainers

(2) I don't think removing the original condition
"mVariableModuleGlobal->FvbInstance" is valid. That condition comes from
commit 7cd69959463a ("MdeModulePkg Variable: Add emulated variable NV
mode support", 2019-01-24). The condition should likely be *restricted*
with the new condition.

(3) Rather than a PCD, I wonder if we should add a new FVB attribute bit
instead, to EFI_FVB_ATTRIBUTES_2, returned by FVB2.GetAttributes().

This would raise the topic to the PI spec's level. For an example, we
have EFI_FVB2_WEAK_ALIGNMENT (0x80000000) -- from commit 3837e91c58d4
("MdeModulePkg: Add support for weakly aligned FVs.", 2013-09-16) --,
associated with <https://mantis.uefi.org/mantis/view.php?id=839>.

Just an idea; it really needs to be commented upon by the maintainers. I
don't "insist" that it be explained in the PI spec, just saying that it
might help with the confusion.

Thanks
Laszlo


  reply	other threads:[~2021-03-12 19:31 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-03-10  8:04 Conflicting virtual addresses causing Runtime Services issues Jon Nettleton
2021-03-10 14:52 ` [edk2-devel] " Laszlo Ersek
2021-03-11  6:53   ` Jon Nettleton
     [not found]   ` <166B374585A9D8FC.18699@groups.io>
2021-03-11  9:48     ` Jon Nettleton
2021-03-11 14:50       ` Laszlo Ersek
2021-03-11 15:49         ` Jon Nettleton
2021-03-11 22:25         ` Laszlo Ersek
2021-03-11 22:39           ` Ard Biesheuvel
2021-03-12  3:01             ` Jon Nettleton
     [not found]             ` <166B792D1514133B.31346@groups.io>
2021-03-12  5:59               ` Jon Nettleton
2021-03-12 19:30                 ` Laszlo Ersek [this message]
2021-03-12 19:52                 ` Ard Biesheuvel
2021-03-12 19:17             ` Laszlo Ersek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=cb7b6b8a-ecd0-8f8c-74bd-787c8ba77b2a@redhat.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox