Skip to content

Commit

Permalink
x86/coco: Add cc_decrypted_alloc/free() interfaces
Browse files Browse the repository at this point in the history
Confidential computing platforms, such as AMD SEV and Intel TDX,
protect memory from VMM access. Any memory that is required for
communication with the VMM must be explicitly shared. It involves
adjusting page table entries to indicate that the memory is shared and
notifies VMM about the change.

set_memory_decrypted() converts memory to shared. Before freeing
memory it has to be converted back with set_memory_encrypted().

The interface works fine for long-term allocations, but for frequent
short-lived allocations it causes problems. Conversion takes time and
direct mapping modification leads to its fracturing and performance
degradation over time.

Direct mapping modifications can be avoided by creating a vmap that
maps allocated pages as shared while direct mapping is untouched.

But having private mapping of a shared memory causes problems too.
Any access of such memory via private mapping in TDX guest would
trigger unrecoverable SEPT violation and termination of the virtual
machine. It is known that load_unaligned_zeropad() can issue such
unwanted loads across page boundaries that can trigger the issue.

It can also be fixed by allocating a guard page in front of any memory
that has to be converted to shared, so load_unaligned_zeropad() will
roll off to the guard page instead. But it is wasteful and does not
address cost of the memory conversion.

The next logical step is to introduce a pool of shared memory that can
share a single guard page and makes conversion less frequent.

Fortunately, the kernel already has such a pool of memory: SWIOTLB
buffer is used by the DMA API to allocate memory for I/O. The buffer is
allocated once during the boot, so direct mapping fracturing is not an
issue and no need for vmap tricks.

Tapping into the SWIOTLB pool requires a device structure and using DMA
API. Provide a couple of simple helpers to allocate and free shared
memory that hide required plumbing.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
  • Loading branch information
Kuppuswamy Sathyanarayanan committed Jul 24, 2022
1 parent 66ecd5e commit e0fa61c
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 1 deletion.
2 changes: 1 addition & 1 deletion arch/x86/coco/Makefile
Expand Up @@ -3,6 +3,6 @@ CFLAGS_REMOVE_core.o = -pg
KASAN_SANITIZE_core.o := n
CFLAGS_core.o += -fno-stack-protector

obj-y += core.o
obj-y += core.o mem.o

obj-$(CONFIG_INTEL_TDX_GUEST) += tdx/
90 changes: 90 additions & 0 deletions arch/x86/coco/mem.c
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Confidential Computing Decrypted Memory Allocator
*
* Copyright (C) 2022 Intel Corporation, Inc.
*
*/

#undef pr_fmt
#define pr_fmt(fmt) "cc/mem: " fmt

#include <linux/export.h>
#include <linux/mm.h>
#include <linux/cc_platform.h>
#include <linux/set_memory.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/dma-direct.h>

#include <asm/coco.h>
#include <asm/processor.h>

#define CC_MEM_DRIVER "ccmem"

static struct platform_device *mem_pdev;

static inline dma_addr_t virt_to_dma(void *vaddr)
{
return phys_to_dma(&mem_pdev->dev, virt_to_phys(vaddr));
}

/* Allocate decrypted memory of given size */
void *cc_decrypted_alloc(size_t size, gfp_t gfp)
{
dma_addr_t handle;
void *vaddr;

if (!mem_pdev)
return NULL;

vaddr = dma_alloc_coherent(&mem_pdev->dev, size, &handle, gfp);

/*
* Since we rely on virt_to_dma() in cc_decrypted_free() to
* calculate DMA address, make sure address translation works.
*/
VM_BUG_ON(virt_to_dma(vaddr) != handle);

return vaddr;
}

/* Free the given decrypted memory */
void cc_decrypted_free(void *addr, size_t size)
{
if (!mem_pdev || !addr)
return;

dma_free_coherent(&mem_pdev->dev, size, addr, virt_to_phys(addr));
}

static struct platform_driver cc_mem_driver = {
.driver.name = CC_MEM_DRIVER,
};

static int __init cc_mem_init(void)
{
struct platform_device *pdev;
int ret;

if (!cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT))
return -ENODEV;

ret = platform_driver_register(&cc_mem_driver);
if (ret)
return ret;

pdev = platform_device_register_simple(CC_MEM_DRIVER, -1, NULL, 0);
if (IS_ERR(pdev)) {
platform_driver_unregister(&cc_mem_driver);
return PTR_ERR(pdev);
}

if (dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)))
return -EIO;

mem_pdev = pdev;

return 0;
}
device_initcall(cc_mem_init);
6 changes: 6 additions & 0 deletions arch/x86/include/asm/coco.h
Expand Up @@ -17,6 +17,8 @@ void cc_set_mask(u64 mask);
#ifdef CONFIG_ARCH_HAS_CC_PLATFORM
u64 cc_mkenc(u64 val);
u64 cc_mkdec(u64 val);
void *cc_decrypted_alloc(size_t size, gfp_t gfp);
void cc_decrypted_free(void *addr, size_t size);
#else
static inline u64 cc_mkenc(u64 val)
{
Expand All @@ -27,6 +29,10 @@ static inline u64 cc_mkdec(u64 val)
{
return val;
}

void *cc_decrypted_alloc(size_t size, gfp_t gfp) { return NULL; }
void cc_decrypted_free(void *addr, size_t size) { }

#endif

#endif /* _ASM_X86_COCO_H */

0 comments on commit e0fa61c

Please sign in to comment.