public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
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



  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