[edk2-devel] [PATCH 3/3] OvmfPkg/PlatformPei: Mark TPM MMIO range as unencrypted for SEV

Lendacky, Thomas thomas.lendacky at amd.com
Wed Apr 28 19:09:52 UTC 2021


On 4/28/21 11:12 AM, Laszlo Ersek wrote:
> On 04/27/21 16:58, Tom Lendacky wrote:
>> On 4/26/21 9:21 AM, Tom Lendacky wrote:
>>> On 4/26/21 7:07 AM, Laszlo Ersek wrote:
>>>> On 04/23/21 22:02, Tom Lendacky wrote:
> 
>>>>> 1. SEV works with the current encrypted mapping, it is only the SEV-ES
>>>>>    support that fails because of the ValidateMmioMemory() check. I can do
>>>>>    the mapping change just for SEV-ES since it is X64 only. This works,
>>>>>    because MemEncryptSevClearPageEncMask() will not return UNSUPPORTED
>>>>>    when running in 64-bit.
>>>>
>>>> Can we really say "SEV works" though? Because, even using an X64 PEI
>>>> phase, and enabling only SEV (not SEV-ES), TPM access will be broken in
>>>> the PEI phase. Is my understanding correct?
>>>
>>> Because the memory range is marked as MMIO, we'll take a nested page fault
>>> (NPF). The GPA passed as part of the NPF does not include the c-bit. So we
>>> do in fact work properly with a TPM in SEV.
> 
> Thanks for the explanation.
> 
> Here's what bothers me about it:
> 
> In AmdSevDxe, we clear the C-bit from MMIO and NonExistent areas in the
> GCD memory space map. This occurs early in the DXE phase (see "APRIORI
> DXE" in the FDF files). The justification is that we want the flash and
> (for example) the PCI MMIO apertures decrypted.
> 
> Now, I realize there is a difference between flash and TPM. TPM is
> purely MMIO (no KVM memslot), but flash (when it is not in programming
> mode) is backed by a read-only KVM memslot. IOW, flash is "actual
> memory", and so it is affected by SEV. TPM is never "actual memory", so
> (according to your explanation, AIUI) it always traps to QEMU, per
> access, and the C-bit doesn't interfere with that.
> 
> This is consistent with two facts about OVMF's PEI phase:
> 
> - We use IO Port-based fw_cfg (never DMA), if SEV is enabled (see
> "QemuFwCfgPei.c").
> 
> - We access PCI config space via IO Ports (0xCF8, 0xCFC), never ECAM.
> (This was not motivated by SEV, see commit 7523788faa51, but it does
> play nice with SEV, in the PEI phase -- I think?)
> 
> What I'm confused about, now, in retrospect, is the reference to the PCI
> MMIO aperture, in AmdSevDxe. If that area isn't backed by a KVM memslot
> *either* -- similarly to the TPM area --, then decrypting *that* in
> AmdSevDxe (via "nonexistent") is not strictly necessary. Is that correct?

It is necessary for (and was added for) SEV-ES. The explicit mapping of
the PCI MMCONFIG range as unencrypted was added for SEV-ES so that the
SEV-ES mitigation would not terminate the guest because MMIO was being
performed against an encrypted address.

There's a nice comment in OvmfPkg/PlatformPei/Platform.c that talks about
how the MMCONFIG range is not added as MMIO, but instead as reserved
memory. Because of that, the loop through the memory space map in
AmdSevDxe.c does not mark the MMCONFIG range as unencrypted.

> 
> I'm not asking for any code changes, just trying to form a consistent view.
> 
> Another question (still for "base SEV"): when OVMF is built with
> SMM_REQUIRE, PlatformPei performs a (read-only) variable access. See the
> ReadOnlyVariable2->GetVariable() call in the RefreshMemTypeInfo()
> function. When SEV is active, does control reach RefreshMemTypeInfo() on
> your end? And does ReadOnlyVariable2->GetVariable() succeed for you?
> 
> (There is a DEBUG_VERBOSE message in OnReadOnlyVariable2Available(), and
> a DEBUG_ERROR in RefreshMemTypeInfo(), so the above questions can be
> answered just from the log; no need to modify the code for that.)

Yes, control reaches that point. But, notably, with a legacy VM I see the
following messages:
  RefreshMemTypeInfo: GetVariable(): Not Found

But with an SEV VM I see:
  Firmware Volume for Variable Store is corrupted
  RefreshMemTypeInfo: GetVariable(): Not Found

So I get the "Not Found" in both cases. But with SEV, I see the
"corrupted" message from InitRealNonVolatileVariableStore() in
MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c. I
imagine that since the range is marked encrypted, it reads the header
incorrectly and fails.

Thanks,
Tom

> 
> Basically, with SEV enabled, I expect ReadOnlyVariable2->GetVariable()
> to fail -- or even to remain unreached, as FaultTolerantWritePei and
> VariablePei could bail out earlier (before installing the Variable PPI),
> due to failing flash accesses. In case I'm *not* wrong -- it's not the
> end of the world, I'm only asking this question too for the sake of
> clarifying "C-bit vs. MMIO".
> 
> More or less it seems to boil down to whether there is a KVM memslot or
> not -- which is *not* equivalent to OVMF considering the area MMIO or not.
> 
> 
>>> SEV-ES would also work
>>> properly if the mitigation for accessing an encrypted address was removed
>>> from the #VC handler. It is only this added mitigation to protect MMIO
>>> that results in an issue with the TPM in PEI.
>>
>> So I'm thinking that I can have TpmMmioSevDecryptPeim.c do this:
>>
>>   //
>>   // If SEV or SEV-ES is active, MMIO succeeds against an encrypted physical
>>   // address because the nested page fault (NPF) that occurs on access does not
>>   // include the encryption bit in the guest physical address provided to the
>>   // hypervisor.
>>   //
>>   // However, if SEV-ES is active, before performing the actual MMIO, an
>>   // additional MMIO mitigation check is performed in the #VC handler to ensure
>>   // that MMIO is being done to an unencrypted address. To prevent guest
>>   // termination in this scenario, mark the range unencrypted ahead of access.
>>   //
>>   if (MemEncryptSevEsIsEnabled ()) {
>>     // Do MemEncryptSevClearPageEncMask() ...
>>   }
>>
>> Let me submit the next version with this and see what you think.
> 
> Yep, I'll review that now.
> 
> Thanks
> Laszlo
> 
>>
>> Thanks,
>> Tom
>>
>>>
>>>>
>>>> I think the behavior you currently see is actually what we want, we
>>>> should double down on it -- if MemEncryptSevClearPageEncMask() fails,
>>>> report an explicit DEBUG_ERROR, and call CpuDeadLoop(). If the firmware
>>>> is built with TPM_ENABLE, and SEV is active, then an IA32 PEI phase is
>>>> simply unusable. Silently pretending that the TPM is not there, even
>>>> though it may have been configured on the QEMU command line, we just
>>>> failed to communicate with it, is not a good idea, IMO.
>>>
>>> However, because the c-bit is not part of the NPF, we do communicate
>>> successfully with the TPM.
>>>
>>> So we could actually do following:
>>>  - For IA32:
>>>    - Remove the Depex on gOvmfTpmMmioAccessiblePpiGuid
>>>    - Do not add OvmfPkg/Tcg/TpmMmioSevDecryptPei/TpmMmioSevDecryptPei.inf
>>>
>>>  - For X64:
>>>    - Add the Depex on gOvmfTpmMmioAccessiblePpiGuid
>>>    - Add OvmfPkg/Tcg/TpmMmioSevDecryptPei/TpmMmioSevDecryptPei.inf
>>>
>>> That might be confusing, though. So we could just do option #3 below.
>>>
>>> Thanks,
>>> Tom
>>>
>>>>
>>>> This is somewhat similar IMO to the S3Verification() function in
>>>> "OvmfPkg/PlatformPei/Platform.c".
>>>>
>>>> TPM_ENABLE, SEV, IA32 PEI phase: pick any two.
>>>>
>>>> Thanks,
>>>> Laszlo
>>>>
>>>>>
>>>>> 2. Call MemEncryptSevClearPageEncMask() for SEV or SEV-ES, but don't check
>>>>>    the return status.
>>>>>
>>>>> 3. Create Ia32 and X64 versions of internal functions, where the Ia32
>>>>>    version simply returns SUCCESS because it can't do anything and the X64
>>>>>    version calls MemEncryptSevClearPageEncMask(), allowing the main code
>>>>>    to ASSERT on any errors.
>>>>>
>>>>> I'm leaning towards #1, because this is an SEV-ES only issue. Thoughts?
>>>>>
>>>>> Thanks,
>>>>> Tom
>>>>>
>>>>>>
>>>>>> One thing I found is that the Bhyve package makes reference to the
>>>>>> OvmfPkg/Bhyve/Tcg directory, but that directory does not exist. So I don't
>>>>>> think that TPM enablement has been tested. I didn't update the Bhyve
>>>>>> support for that reason.
>>>>>>
>>>>>> Thanks,
>>>>>> Tom
>>>>>>
>>>>>>> Thanks!
>>>>>>> Laszlo
>>>>>>>
>>>>>
>>>>
>>
> 


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#74574): https://edk2.groups.io/g/devel/message/74574
Mute This Topic: https://groups.io/mt/82247968/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-





More information about the edk2-devel-archive mailing list