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
_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.01.org/mailman/listinfo/edk2-devel