mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-15 08:14:15 +08:00
x86/sev: Check the VMPL level
The Virtual Machine Privilege Level (VMPL) feature in the SEV-SNP architecture allows a guest VM to divide its address space into four levels. The level can be used to provide hardware isolated abstraction layers within a VM. VMPL0 is the highest privilege level, and VMPL3 is the least privilege level. Certain operations must be done by the VMPL0 software, such as: * Validate or invalidate memory range (PVALIDATE instruction) * Allocate VMSA page (RMPADJUST instruction when VMSA=1) The initial SNP support requires that the guest kernel is running at VMPL0. Add such a check to verify the guest is running at level 0 before continuing the boot. There is no easy method to query the current VMPL level, so use the RMPADJUST instruction to determine whether the guest is running at the VMPL0. [ bp: Massage commit message. ] Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Signed-off-by: Borislav Petkov <bp@suse.de> Link: https://lore.kernel.org/r/20220307213356.2797205-15-brijesh.singh@amd.com
This commit is contained in:
parent
0bd6f1e526
commit
81cc3df9a9
@ -199,6 +199,26 @@ finish:
|
||||
sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_GEN_REQ);
|
||||
}
|
||||
|
||||
static void enforce_vmpl0(void)
|
||||
{
|
||||
u64 attrs;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* RMPADJUST modifies RMP permissions of a lesser-privileged (numerically
|
||||
* higher) privilege level. Here, clear the VMPL1 permission mask of the
|
||||
* GHCB page. If the guest is not running at VMPL0, this will fail.
|
||||
*
|
||||
* If the guest is running at VMPL0, it will succeed. Even if that operation
|
||||
* modifies permission bits, it is still ok to do so currently because Linux
|
||||
* SNP guests are supported only on VMPL0 so VMPL1 or higher permission masks
|
||||
* changing is a don't-care.
|
||||
*/
|
||||
attrs = 1;
|
||||
if (rmpadjust((unsigned long)&boot_ghcb_page, RMP_PG_SIZE_4K, attrs))
|
||||
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_NOT_VMPL0);
|
||||
}
|
||||
|
||||
void sev_enable(struct boot_params *bp)
|
||||
{
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
@ -242,8 +262,12 @@ void sev_enable(struct boot_params *bp)
|
||||
* SNP is supported in v2 of the GHCB spec which mandates support for HV
|
||||
* features.
|
||||
*/
|
||||
if (sev_status & MSR_AMD64_SEV_SNP_ENABLED && !(get_hv_features() & GHCB_HV_FT_SNP))
|
||||
sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
|
||||
if (sev_status & MSR_AMD64_SEV_SNP_ENABLED) {
|
||||
if (!(get_hv_features() & GHCB_HV_FT_SNP))
|
||||
sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
|
||||
|
||||
enforce_vmpl0();
|
||||
}
|
||||
|
||||
sme_me_mask = BIT_ULL(ebx & 0x3f);
|
||||
}
|
||||
|
@ -89,6 +89,7 @@
|
||||
#define GHCB_TERM_REGISTER 0 /* GHCB GPA registration failure */
|
||||
#define GHCB_TERM_PSC 1 /* Page State Change failure */
|
||||
#define GHCB_TERM_PVALIDATE 2 /* Pvalidate failure */
|
||||
#define GHCB_TERM_NOT_VMPL0 3 /* SNP guest is not running at VMPL-0 */
|
||||
|
||||
#define GHCB_RESP_CODE(v) ((v) & GHCB_MSR_INFO_MASK)
|
||||
|
||||
|
@ -63,6 +63,9 @@ extern bool handle_vc_boot_ghcb(struct pt_regs *regs);
|
||||
/* Software defined (when rFlags.CF = 1) */
|
||||
#define PVALIDATE_FAIL_NOUPDATE 255
|
||||
|
||||
/* RMP page size */
|
||||
#define RMP_PG_SIZE_4K 0
|
||||
|
||||
#ifdef CONFIG_AMD_MEM_ENCRYPT
|
||||
extern struct static_key_false sev_es_enable_key;
|
||||
extern void __sev_es_ist_enter(struct pt_regs *regs);
|
||||
@ -90,6 +93,18 @@ extern enum es_result sev_es_ghcb_hv_call(struct ghcb *ghcb,
|
||||
struct es_em_ctxt *ctxt,
|
||||
u64 exit_code, u64 exit_info_1,
|
||||
u64 exit_info_2);
|
||||
static inline int rmpadjust(unsigned long vaddr, bool rmp_psize, unsigned long attrs)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* "rmpadjust" mnemonic support in binutils 2.36 and newer */
|
||||
asm volatile(".byte 0xF3,0x0F,0x01,0xFE\n\t"
|
||||
: "=a"(rc)
|
||||
: "a"(vaddr), "c"(rmp_psize), "d"(attrs)
|
||||
: "memory", "cc");
|
||||
|
||||
return rc;
|
||||
}
|
||||
static inline int pvalidate(unsigned long vaddr, bool rmp_psize, bool validate)
|
||||
{
|
||||
bool no_rmpupdate;
|
||||
@ -114,6 +129,7 @@ static inline int sev_es_setup_ap_jump_table(struct real_mode_header *rmh) { ret
|
||||
static inline void sev_es_nmi_complete(void) { }
|
||||
static inline int sev_es_efi_map_ghcbs(pgd_t *pgd) { return 0; }
|
||||
static inline int pvalidate(unsigned long vaddr, bool rmp_psize, bool validate) { return 0; }
|
||||
static inline int rmpadjust(unsigned long vaddr, bool rmp_psize, unsigned long attrs) { return 0; }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user