mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
x86/tdx: Add unaccepted memory support
Hookup TDX-specific code to accept memory. Accepting the memory is done with ACCEPT_PAGE module call on every page in the range. MAP_GPA hypercall is not required as the unaccepted memory is considered private already. Extract the part of tdx_enc_status_changed() that does memory acceptance in a new helper. Move the helper tdx-shared.c. It is going to be used by both main kernel and decompressor. [ bp: Fix the INTEL_TDX_GUEST=y, KVM_GUEST=n build. ] Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Link: https://lore.kernel.org/r/20230606142637.5171-10-kirill.shutemov@linux.intel.com
This commit is contained in:
parent
c2b353ae24
commit
75d090fd16
|
|
@ -884,9 +884,11 @@ config INTEL_TDX_GUEST
|
||||||
bool "Intel TDX (Trust Domain Extensions) - Guest Support"
|
bool "Intel TDX (Trust Domain Extensions) - Guest Support"
|
||||||
depends on X86_64 && CPU_SUP_INTEL
|
depends on X86_64 && CPU_SUP_INTEL
|
||||||
depends on X86_X2APIC
|
depends on X86_X2APIC
|
||||||
|
depends on EFI_STUB
|
||||||
select ARCH_HAS_CC_PLATFORM
|
select ARCH_HAS_CC_PLATFORM
|
||||||
select X86_MEM_ENCRYPT
|
select X86_MEM_ENCRYPT
|
||||||
select X86_MCE
|
select X86_MCE
|
||||||
|
select UNACCEPTED_MEMORY
|
||||||
help
|
help
|
||||||
Support running as a guest under Intel TDX. Without this support,
|
Support running as a guest under Intel TDX. Without this support,
|
||||||
the guest kernel can not boot or run under TDX.
|
the guest kernel can not boot or run under TDX.
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ ifdef CONFIG_X86_64
|
||||||
endif
|
endif
|
||||||
|
|
||||||
vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
|
vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
|
||||||
vmlinux-objs-$(CONFIG_INTEL_TDX_GUEST) += $(obj)/tdx.o $(obj)/tdcall.o
|
vmlinux-objs-$(CONFIG_INTEL_TDX_GUEST) += $(obj)/tdx.o $(obj)/tdcall.o $(obj)/tdx-shared.o
|
||||||
vmlinux-objs-$(CONFIG_UNACCEPTED_MEMORY) += $(obj)/mem.o
|
vmlinux-objs-$(CONFIG_UNACCEPTED_MEMORY) += $(obj)/mem.o
|
||||||
|
|
||||||
vmlinux-objs-$(CONFIG_EFI) += $(obj)/efi.o
|
vmlinux-objs-$(CONFIG_EFI) += $(obj)/efi.o
|
||||||
|
|
|
||||||
|
|
@ -22,3 +22,22 @@ void error(char *m)
|
||||||
while (1)
|
while (1)
|
||||||
asm("hlt");
|
asm("hlt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* EFI libstub provides vsnprintf() */
|
||||||
|
#ifdef CONFIG_EFI_STUB
|
||||||
|
void panic(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
static char buf[1024];
|
||||||
|
va_list args;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
len = vsnprintf(buf, sizeof(buf), fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
if (len && buf[len - 1] == '\n')
|
||||||
|
buf[len - 1] = '\0';
|
||||||
|
|
||||||
|
error(buf);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,6 @@
|
||||||
|
|
||||||
void warn(char *m);
|
void warn(char *m);
|
||||||
void error(char *m) __noreturn;
|
void error(char *m) __noreturn;
|
||||||
|
void panic(const char *fmt, ...) __noreturn __cold;
|
||||||
|
|
||||||
#endif /* BOOT_COMPRESSED_ERROR_H */
|
#endif /* BOOT_COMPRESSED_ERROR_H */
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,44 @@
|
||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
#include "tdx.h"
|
||||||
|
#include <asm/shared/tdx.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* accept_memory() and process_unaccepted_memory() called from EFI stub which
|
||||||
|
* runs before decompresser and its early_tdx_detect().
|
||||||
|
*
|
||||||
|
* Enumerate TDX directly from the early users.
|
||||||
|
*/
|
||||||
|
static bool early_is_tdx_guest(void)
|
||||||
|
{
|
||||||
|
static bool once;
|
||||||
|
static bool is_tdx;
|
||||||
|
|
||||||
|
if (!IS_ENABLED(CONFIG_INTEL_TDX_GUEST))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!once) {
|
||||||
|
u32 eax, sig[3];
|
||||||
|
|
||||||
|
cpuid_count(TDX_CPUID_LEAF_ID, 0, &eax,
|
||||||
|
&sig[0], &sig[2], &sig[1]);
|
||||||
|
is_tdx = !memcmp(TDX_IDENT, sig, sizeof(sig));
|
||||||
|
once = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_tdx;
|
||||||
|
}
|
||||||
|
|
||||||
void arch_accept_memory(phys_addr_t start, phys_addr_t end)
|
void arch_accept_memory(phys_addr_t start, phys_addr_t end)
|
||||||
{
|
{
|
||||||
/* Platform-specific memory-acceptance call goes here */
|
/* Platform-specific memory-acceptance call goes here */
|
||||||
error("Cannot accept memory");
|
if (early_is_tdx_guest()) {
|
||||||
|
if (!tdx_accept_memory(start, end))
|
||||||
|
panic("TDX: Failed to accept memory\n");
|
||||||
|
} else {
|
||||||
|
error("Cannot accept memory: unknown platform\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init_unaccepted_memory(void)
|
bool init_unaccepted_memory(void)
|
||||||
|
|
|
||||||
2
arch/x86/boot/compressed/tdx-shared.c
Normal file
2
arch/x86/boot/compressed/tdx-shared.c
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
#include "error.h"
|
||||||
|
#include "../../coco/tdx/tdx-shared.c"
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
obj-y += tdx.o tdcall.o
|
obj-y += tdx.o tdx-shared.o tdcall.o
|
||||||
|
|
|
||||||
71
arch/x86/coco/tdx/tdx-shared.c
Normal file
71
arch/x86/coco/tdx/tdx-shared.c
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
#include <asm/tdx.h>
|
||||||
|
#include <asm/pgtable.h>
|
||||||
|
|
||||||
|
static unsigned long try_accept_one(phys_addr_t start, unsigned long len,
|
||||||
|
enum pg_level pg_level)
|
||||||
|
{
|
||||||
|
unsigned long accept_size = page_level_size(pg_level);
|
||||||
|
u64 tdcall_rcx;
|
||||||
|
u8 page_size;
|
||||||
|
|
||||||
|
if (!IS_ALIGNED(start, accept_size))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (len < accept_size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pass the page physical address to the TDX module to accept the
|
||||||
|
* pending, private page.
|
||||||
|
*
|
||||||
|
* Bits 2:0 of RCX encode page size: 0 - 4K, 1 - 2M, 2 - 1G.
|
||||||
|
*/
|
||||||
|
switch (pg_level) {
|
||||||
|
case PG_LEVEL_4K:
|
||||||
|
page_size = 0;
|
||||||
|
break;
|
||||||
|
case PG_LEVEL_2M:
|
||||||
|
page_size = 1;
|
||||||
|
break;
|
||||||
|
case PG_LEVEL_1G:
|
||||||
|
page_size = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tdcall_rcx = start | page_size;
|
||||||
|
if (__tdx_module_call(TDX_ACCEPT_PAGE, tdcall_rcx, 0, 0, 0, NULL))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return accept_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tdx_accept_memory(phys_addr_t start, phys_addr_t end)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* For shared->private conversion, accept the page using
|
||||||
|
* TDX_ACCEPT_PAGE TDX module call.
|
||||||
|
*/
|
||||||
|
while (start < end) {
|
||||||
|
unsigned long len = end - start;
|
||||||
|
unsigned long accept_size;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try larger accepts first. It gives chance to VMM to keep
|
||||||
|
* 1G/2M Secure EPT entries where possible and speeds up
|
||||||
|
* process by cutting number of hypercalls (if successful).
|
||||||
|
*/
|
||||||
|
|
||||||
|
accept_size = try_accept_one(start, len, PG_LEVEL_1G);
|
||||||
|
if (!accept_size)
|
||||||
|
accept_size = try_accept_one(start, len, PG_LEVEL_2M);
|
||||||
|
if (!accept_size)
|
||||||
|
accept_size = try_accept_one(start, len, PG_LEVEL_4K);
|
||||||
|
if (!accept_size)
|
||||||
|
return false;
|
||||||
|
start += accept_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
@ -713,46 +713,6 @@ static bool tdx_cache_flush_required(void)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long try_accept_one(phys_addr_t start, unsigned long len,
|
|
||||||
enum pg_level pg_level)
|
|
||||||
{
|
|
||||||
unsigned long accept_size = page_level_size(pg_level);
|
|
||||||
u64 tdcall_rcx;
|
|
||||||
u8 page_size;
|
|
||||||
|
|
||||||
if (!IS_ALIGNED(start, accept_size))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (len < accept_size)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Pass the page physical address to the TDX module to accept the
|
|
||||||
* pending, private page.
|
|
||||||
*
|
|
||||||
* Bits 2:0 of RCX encode page size: 0 - 4K, 1 - 2M, 2 - 1G.
|
|
||||||
*/
|
|
||||||
switch (pg_level) {
|
|
||||||
case PG_LEVEL_4K:
|
|
||||||
page_size = 0;
|
|
||||||
break;
|
|
||||||
case PG_LEVEL_2M:
|
|
||||||
page_size = 1;
|
|
||||||
break;
|
|
||||||
case PG_LEVEL_1G:
|
|
||||||
page_size = 2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
tdcall_rcx = start | page_size;
|
|
||||||
if (__tdx_module_call(TDX_ACCEPT_PAGE, tdcall_rcx, 0, 0, 0, NULL))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return accept_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Inform the VMM of the guest's intent for this physical page: shared with
|
* Inform the VMM of the guest's intent for this physical page: shared with
|
||||||
* the VMM or private to the guest. The VMM is expected to change its mapping
|
* the VMM or private to the guest. The VMM is expected to change its mapping
|
||||||
|
|
@ -777,33 +737,9 @@ static bool tdx_enc_status_changed(unsigned long vaddr, int numpages, bool enc)
|
||||||
if (_tdx_hypercall(TDVMCALL_MAP_GPA, start, end - start, 0, 0))
|
if (_tdx_hypercall(TDVMCALL_MAP_GPA, start, end - start, 0, 0))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* private->shared conversion requires only MapGPA call */
|
/* shared->private conversion requires memory to be accepted before use */
|
||||||
if (!enc)
|
if (enc)
|
||||||
return true;
|
return tdx_accept_memory(start, end);
|
||||||
|
|
||||||
/*
|
|
||||||
* For shared->private conversion, accept the page using
|
|
||||||
* TDX_ACCEPT_PAGE TDX module call.
|
|
||||||
*/
|
|
||||||
while (start < end) {
|
|
||||||
unsigned long len = end - start;
|
|
||||||
unsigned long accept_size;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Try larger accepts first. It gives chance to VMM to keep
|
|
||||||
* 1G/2M Secure EPT entries where possible and speeds up
|
|
||||||
* process by cutting number of hypercalls (if successful).
|
|
||||||
*/
|
|
||||||
|
|
||||||
accept_size = try_accept_one(start, len, PG_LEVEL_1G);
|
|
||||||
if (!accept_size)
|
|
||||||
accept_size = try_accept_one(start, len, PG_LEVEL_2M);
|
|
||||||
if (!accept_size)
|
|
||||||
accept_size = try_accept_one(start, len, PG_LEVEL_4K);
|
|
||||||
if (!accept_size)
|
|
||||||
return false;
|
|
||||||
start += accept_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,5 +91,7 @@ struct tdx_module_output {
|
||||||
u64 __tdx_module_call(u64 fn, u64 rcx, u64 rdx, u64 r8, u64 r9,
|
u64 __tdx_module_call(u64 fn, u64 rcx, u64 rdx, u64 r8, u64 r9,
|
||||||
struct tdx_module_output *out);
|
struct tdx_module_output *out);
|
||||||
|
|
||||||
|
bool tdx_accept_memory(phys_addr_t start, phys_addr_t end);
|
||||||
|
|
||||||
#endif /* !__ASSEMBLY__ */
|
#endif /* !__ASSEMBLY__ */
|
||||||
#endif /* _ASM_X86_SHARED_TDX_H */
|
#endif /* _ASM_X86_SHARED_TDX_H */
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/bits.h>
|
#include <linux/bits.h>
|
||||||
|
|
||||||
|
#include <asm/errno.h>
|
||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
#include <asm/shared/tdx.h>
|
#include <asm/shared/tdx.h>
|
||||||
|
|
||||||
|
|
|
||||||
24
arch/x86/include/asm/unaccepted_memory.h
Normal file
24
arch/x86/include/asm/unaccepted_memory.h
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef _ASM_X86_UNACCEPTED_MEMORY_H
|
||||||
|
#define _ASM_X86_UNACCEPTED_MEMORY_H
|
||||||
|
|
||||||
|
#include <linux/efi.h>
|
||||||
|
#include <asm/tdx.h>
|
||||||
|
|
||||||
|
static inline void arch_accept_memory(phys_addr_t start, phys_addr_t end)
|
||||||
|
{
|
||||||
|
/* Platform-specific memory-acceptance call goes here */
|
||||||
|
if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) {
|
||||||
|
if (!tdx_accept_memory(start, end))
|
||||||
|
panic("TDX: Failed to accept memory\n");
|
||||||
|
} else {
|
||||||
|
panic("Cannot accept memory: unknown platform\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct efi_unaccepted_memory *efi_get_unaccepted_table(void)
|
||||||
|
{
|
||||||
|
if (efi.unaccepted == EFI_INVALID_TABLE_ADDR)
|
||||||
|
return NULL;
|
||||||
|
return __va(efi.unaccepted);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
Loading…
Reference in New Issue
Block a user