From: Brijesh Singh <brijesh.singh@amd.com>
To: Laszlo Ersek <lersek@redhat.com>, edk2-devel@lists.01.org
Cc: brijesh.singh@amd.com, Tom Lendacky <thomas.lendacky@amd.com>,
Jordan Justen <jordan.l.justen@intel.com>,
Ard Biesheuvel <ard.biesheuvel@linaro.org>
Subject: Re: [PATCH v1 2/4] OvmfPkg: IommuDxe: Provide support for mapping BusMasterCommonBuffer operation
Date: Tue, 1 Aug 2017 18:51:30 -0500 [thread overview]
Message-ID: <c148fc3a-b4eb-61a8-b926-3fe69b44793d@amd.com> (raw)
In-Reply-To: <89e1553a-1630-87a5-cffd-99174a380d41@redhat.com>
Thanks Laszlo.
On 8/1/17 4:59 PM, Laszlo Ersek wrote:
> On 07/31/17 21:31, Brijesh Singh wrote:
> +
[Snip]
>> The function is used for mapping and unmapping the Host buffer with
>> + BusMasterCommonBuffer. Since the buffer can be accessed equally by the
>> + processor and the DMA bus master hence we can not use the bounce buffer.
>> +
>> + The function changes the underlying encryption mask of the pages that maps the
>> + host buffer. It also ensures that buffer contents are updated with the desired
>> + state.
>> +
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +SetBufferAsEncDec (
>> + IN MAP_INFO *MapInfo,
>> + IN BOOLEAN Enc
>> + )
>> +{
>> + EFI_STATUS Status;
>> + EFI_PHYSICAL_ADDRESS TempBuffer;
>> +
>> + //
>> + // Allocate an intermediate buffer to hold the host buffer contents
>> + //
>> + Status = gBS->AllocatePages (
>> + AllocateAnyPages,
>> + EfiBootServicesData,
>> + MapInfo->NumberOfPages,
>> + &TempBuffer
>> + );
>> + if (EFI_ERROR (Status)) {
>> + return Status;
>> + }
> This is not right. This function is called (indirectly) from
> IoMmuUnmap(), which is the function that we'll call (also indirectly)
> from the ExitBootServices() callbacks. That means we cannot allocate or
> release dynamic memory here. This is why I suggested the page-sized
> static buffer, and page-wise copying.
>
> More on this later, below the end of this function.
Agree with your point, see more below
>> +
>> + //
>> + // If the host buffer has C-bit cleared, then make sure the intermediate
>> + // buffer matches with same encryption mask.
>> + //
>> + if (!Enc) {
>> + Status = MemEncryptSevClearPageEncMask (0, MapInfo->DeviceAddress,
>> + MapInfo->NumberOfPages, TRUE);
>> + ASSERT_EFI_ERROR (Status);
>> + }
> As a separate issue, I don't think this is right. The auxiliary buffer
> that we use for in-place encryption or decryption should *always* be
> encrypted. (I mentioned this in my email, "Introduce a static UINT8
> array with EFI_PAGE_SIZE bytes (this will always remain in encrypted
> memory).", but I guess it was easy to miss.)
See more below
>> +
>> + //
>> + // Copy the data from host buffer into a temporary buffer. At this
>> + // time both host and intermediate buffer will have same encryption
>> + // mask.
>> + //
> The comment should say "copy original buffer into temporary buffer".
> "host" buffer is very confusing here, as the virtualization host is
> exactly what may not yet have access to the buffer. In PCI bus master
> terminology, "host buffer" is valid, I think, but saying just "host
> buffer" is very confusing.
Will do.
> Also, "same encryption mask" is not desirable (see above), so please
> remove that sentence.
>
>> + CopyMem (
>> + (VOID *) (UINTN) TempBuffer,
>> + (VOID *) (UINTN)MapInfo->HostAddress,
>> + MapInfo->NumberOfBytes);
>> +
>> + //
>> + // Now change the encryption mask of the host buffer
>> + //
>> + if (Enc) {
>> + Status = MemEncryptSevSetPageEncMask (0, MapInfo->HostAddress,
>> + MapInfo->NumberOfPages, TRUE);
>> + ASSERT_EFI_ERROR (Status);
> What guarantees that this function call will never fail?
>
> If it fails, we should propagate the error (the function prototype
> allows for that), and roll back partial changes along the way.
>
> If there is no way to undo partial actions when
> MemEncryptSevSetPageEncMask() fails, then the ASSERT() is fine, but we
> need an additional CpuDeadLoop() as well.
I will propagate the error.
>> + } else {
>> + Status = MemEncryptSevClearPageEncMask (0, MapInfo->HostAddress,
>> + MapInfo->NumberOfPages, TRUE);
>> + ASSERT_EFI_ERROR (Status);
>> + }
>> +
>> + //
>> + // Copy the data from intermediate buffer into host buffer. At this
>> + // time encryption masks will be different on host and intermediate
>> + // buffer and the hardware will perform encryption/decryption on
>> + // accesses.
>> + //
>> + CopyMem (
>> + (VOID *) (UINTN)MapInfo->HostAddress,
>> + (VOID *) (UINTN)TempBuffer,
>> + MapInfo->NumberOfBytes);
>> +
>> + //
>> + // Restore the encryption mask of the intermediate buffer
>> + //
>> + if (!Enc) {
>> + Status = MemEncryptSevSetPageEncMask (0, MapInfo->DeviceAddress,
>> + MapInfo->NumberOfPages, TRUE);
>> + ASSERT_EFI_ERROR (Status);
>> + }
> Again, the intermediate buffer should always remain encrypted.
>
>> +
>> + //
>> + // Free the intermediate buffer
>> + //
>> + gBS->FreePages (TempBuffer, MapInfo->NumberOfPages);
>> + return EFI_SUCCESS;
>> +}
> In summary for this function: I've now read section "7.10.8
> Encrypt-in-Place" of AMD pub #24593. Earlier I wrote,
>
> Introduce a static UINT8 array with EFI_PAGE_SIZE bytes (this
> will always remain in encrypted memory). Update the C bit with a
> single function call for the entire range (like now) -- this will
> not affect the guest-readability of the pages --, then bounce each
> page within the range to the static buffer and back to its original
> place.
>
> With regard to 7.10.8., this appears to be wrong. My idea was based on
> the expectation that changing the C bit will not affect *guest reads*
> from the same area. According to 7.10.8, my assumption was wrong: the
> algorithm in 7.10.8 uses a cache-line sized bounce buffer and *two*
> separate mappings for the area under in-place encryption. The reading
> always occurs through the original mapping.
>
> Now, this requires us to do one of two things:
>
> - Alternative 1: we can use the static, single page-sized buffer, in a
> loop. But then we must also break up the central
> MemEncryptSevSetPageEncMask() / MemEncryptSevClearPageEncMask() calls to
> small, page-sized calls. Copy out a page, change the C bit for that one
> page, copy back the page. Lather, rinse, repeat. This would likely be
> slow, and would guarantee that 2M pages would be split into 4K pages.
Yes, while implementing the code I had similar concern in my mind hence
I dropped the looping per page idea. So far, I have not seen really huge
request for BusMasterCommonBuffer Map(). The max I remember was 16 pages
when using the Ata drivers. It may not be as bad as we are thinking.
> - Alternative 2: we can stick with the large (unified) CopyMem() and
> C-bit changing function calls, but we still can't allocate memory for
> that in the SetBufferAsEncDec() function. Therefore you will have to
> allocate *twice* the requested number of pages in AllocateBuffer(),
> return the address of the lower half to the caller, and use the upper
> half as intermediate buffer in SetBufferAsEncDec(). This is safe because
> CommonBuffer[64] Map/Unmap requires the caller to pass in allocations
> from AllocateBuffer().
Yes, we could do something like this. thank for tip. This will certainly
will not affect the perform. I may give this as a first try in next rev.
>> +
>> +/**
>> + This function will be called by Map() when mapping the buffer buffer to
>> + BusMasterCommonBuffer type.
>> +
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +SetHostBufferAsEncrypted (
>> + IN MAP_INFO *MapInfo
>> + )
>> +{
>> + return SetBufferAsEncDec (MapInfo, TRUE);
>> +}
>> +
>> +/**
>> + This function will be called by Unmap() when unmapping host buffer
>> + from the BusMasterCommonBuffer type.
>> +
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +SetHostBufferAsDecrypted (
>> + IN MAP_INFO *MapInfo
>> + )
>> +{
>> + return SetBufferAsEncDec (MapInfo, FALSE);
>> +}
>>
>> /**
>> Provides the controller-specific addresses required to access system memory from a
>> @@ -113,18 +233,6 @@ IoMmuMap (
>> }
>>
>> //
>> - // CommandBuffer was allocated by us (AllocateBuffer) and is already in
>> - // unencryted buffer so no need to create bounce buffer
>> - //
>> - if (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
>> - Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {
>> - *Mapping = NO_MAPPING;
>> - *DeviceAddress = PhysicalAddress;
>> -
>> - return EFI_SUCCESS;
>> - }
>> -
>> - //
>> // Allocate a MAP_INFO structure to remember the mapping when Unmap() is
>> // called later.
>> //
> Please implement the free-list idea that I outlined earlier. Unmap()
> must not call FreePool() on the MAP_INFO structures.
Sorry it was my bad. I missed implementing that feedback. I will fix it
in next rev. As you have outlined in previous feedback that Unmap() can
be called from ExitBootService() hence i will refrain from using
FreePool() or MemEncryptSev*() functions in this sequence (except when
freeing the actual bounce buffer).
>> @@ -144,6 +252,25 @@ IoMmuMap (
>> MapInfo->DeviceAddress = DmaMemoryTop;
>>
>> //
>> + // If the requested Map() operation is BusMasterCommandBuffer then map
>> + // using internal function otherwise allocate a bounce buffer to map
>> + // the host buffer to device buffer
>> + //
>> + if (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
>> + Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {
>> +
>> + Status = SetHostBufferAsDecrypted (MapInfo);
>> + if (EFI_ERROR (Status)) {
>> + FreePool (MapInfo);
>> + *NumberOfBytes = 0;
>> + return Status;
>> + }
>> +
>> + MapInfo->DeviceAddress = MapInfo->HostAddress;
>> + goto Done;
>> + }
> Please use a regular "else" branch here; we should use "goto" only for
> jumping to error handling code.
Will do thanks
next prev parent reply other threads:[~2017-08-01 23:49 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-07-31 19:31 [PATCH v1 0/4] OvmfPkg : IoMmuDxe: BusMasterCommonBuffer support when SEV is active Brijesh Singh
2017-07-31 19:31 ` [PATCH v1 1/4] OvmfPkg: IommuDxe: Do not clear C-bit in Allocate() and Free() Brijesh Singh
2017-08-01 20:42 ` Laszlo Ersek
2017-07-31 19:31 ` [PATCH v1 2/4] OvmfPkg: IommuDxe: Provide support for mapping BusMasterCommonBuffer operation Brijesh Singh
2017-07-31 19:49 ` Ard Biesheuvel
2017-07-31 20:27 ` Brijesh Singh
2017-08-01 20:52 ` Laszlo Ersek
2017-08-01 21:59 ` Laszlo Ersek
2017-08-01 23:51 ` Brijesh Singh [this message]
2017-08-02 8:41 ` Laszlo Ersek
2017-07-31 19:31 ` [PATCH v1 3/4] OvmfPkg: IommuDxe: Zero the shared page(s) on Unmap() Brijesh Singh
2017-07-31 19:38 ` Brijesh Singh
2017-08-02 7:37 ` Laszlo Ersek
2017-08-02 11:22 ` Brijesh Singh
2017-08-02 12:52 ` Laszlo Ersek
2017-07-31 19:31 ` [PATCH v1 4/4] OvmfPkg : QemuFwCfgLib: Map DMA buffer with CommonBuffer when SEV is enable Brijesh Singh
2017-08-02 8:09 ` 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=c148fc3a-b4eb-61a8-b926-3fe69b44793d@amd.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