Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(RHEL-50651) Fix detection of TDX confidential VM on Azure platform #296

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 9 additions & 69 deletions src/basic/confidential-virt.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,82 +5,17 @@
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>

#include "confidential-virt-fundamental.h"
#include "confidential-virt.h"
#include "fd-util.h"
#include "fileio.h"
#include "missing_threads.h"
#include "string-table.h"
#include "utf8.h"

#define CPUID_PROCESSOR_INFO_AND_FEATURE_BITS UINT32_C(0x1)

/*
* AMD64 Architecture Programmer’s Manual Volume 3:
* General-Purpose and System Instructions.
* Chapter: E4.1 - Maximum Extended Function Number and Vendor String
* https://www.amd.com/system/files/TechDocs/24594.pdf
*/
#define CPUID_GET_HIGHEST_FUNCTION UINT32_C(0x80000000)

/*
* AMD64 Architecture Programmer’s Manual Volume 3:
* General-Purpose and System Instructions.
* Chapter: E4.17 - Encrypted Memory Capabilities
* https://www.amd.com/system/files/TechDocs/24594.pdf
*/
#define CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES UINT32_C(0x8000001f)

/*
* AMD64 Architecture Programmer’s Manual Volume 3:
* General-Purpose and System Instructions.
* Chapter: 15.34.10 - SEV_STATUS MSR
* https://www.amd.com/system/files/TechDocs/24593.pdf
*/
#define MSR_AMD64_SEV UINT32_C(0xc0010131)

/*
* Intel® TDX Module v1.5 Base Architecture Specification
* Chapter: 11.2
* https://www.intel.com/content/www/us/en/content-details/733575/intel-tdx-module-v1-5-base-architecture-specification.html
*/

#define CPUID_INTEL_TDX_ENUMERATION UINT32_C(0x21)

/* Requirements for Implementing the Microsoft Hypervisor Interface
* https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/tlfs
*/
#define CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS UINT32_C(0x40000000)

#define CPUID_HYPERV_FEATURES UINT32_C(0x40000003)

#define CPUID_HYPERV_ISOLATION_CONFIG UINT32_C(0x4000000C)

#define CPUID_HYPERV_MIN UINT32_C(0x40000005)
#define CPUID_HYPERV_MAX UINT32_C(0x4000ffff)

#define CPUID_SIG_AMD "AuthenticAMD"
#define CPUID_SIG_INTEL "GenuineIntel"
#define CPUID_SIG_INTEL_TDX "IntelTDX "
#define CPUID_SIG_HYPERV "Microsoft Hv"

/* ecx bit 31: set => hyperpvisor, unset => bare metal */
#define CPUID_FEATURE_HYPERVISOR (UINT32_C(1) << 31)

/* Linux include/asm-generic/hyperv-tlfs.h */
#define CPUID_HYPERV_CPU_MANAGEMENT (UINT32_C(1) << 12) /* root partition */
#define CPUID_HYPERV_ISOLATION (UINT32_C(1) << 22) /* confidential VM partition */

#define CPUID_HYPERV_ISOLATION_TYPE_MASK UINT32_C(0xf)
#define CPUID_HYPERV_ISOLATION_TYPE_SNP 2

#define EAX_SEV (UINT32_C(1) << 1)
#define MSR_SEV (UINT64_C(1) << 0)
#define MSR_SEV_ES (UINT64_C(1) << 1)
#define MSR_SEV_SNP (UINT64_C(1) << 2)

#if defined(__x86_64__)

Expand Down Expand Up @@ -142,7 +77,7 @@ static uint64_t msr(uint64_t index) {
return ret;
}

static bool detect_hyperv_sev(void) {
static bool detect_hyperv_cvm(uint32_t isoltype) {
uint32_t eax, ebx, ecx, edx, feat;
char sig[13] = {};

Expand All @@ -166,7 +101,7 @@ static bool detect_hyperv_sev(void) {
ebx = ecx = edx = 0;
cpuid(&eax, &ebx, &ecx, &edx);

if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) == CPUID_HYPERV_ISOLATION_TYPE_SNP)
if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) == isoltype)
return true;
}

Expand Down Expand Up @@ -199,7 +134,7 @@ static ConfidentialVirtualization detect_sev(void) {
if (!(eax & EAX_SEV)) {
log_debug("No sev in CPUID, trying hyperv CPUID");

if (detect_hyperv_sev())
if (detect_hyperv_cvm(CPUID_HYPERV_ISOLATION_TYPE_SNP))
return CONFIDENTIAL_VIRTUALIZATION_SEV_SNP;

log_debug("No hyperv CPUID");
Expand Down Expand Up @@ -237,6 +172,11 @@ static ConfidentialVirtualization detect_tdx(void) {
if (memcmp(sig, CPUID_SIG_INTEL_TDX, sizeof(sig)) == 0)
return CONFIDENTIAL_VIRTUALIZATION_TDX;

log_debug("No tdx in CPUID, trying hyperv CPUID");

if (detect_hyperv_cvm(CPUID_HYPERV_ISOLATION_TYPE_TDX))
return CONFIDENTIAL_VIRTUALIZATION_TDX;

return CONFIDENTIAL_VIRTUALIZATION_NONE;
}

Expand Down
26 changes: 15 additions & 11 deletions src/boot/efi/stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -494,17 +494,21 @@ static EFI_STATUS real_main(EFI_HANDLE image) {
log_error_status(err, "Error loading UKI-specific addons, ignoring: %m");
parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);

const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra");
if (extra) {
_cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline), *extra16 = xstr8_to_16(extra);
cmdline = xasprintf("%ls %ls", tmp, extra16);

/* SMBIOS strings are measured in PCR1, but we also want to measure them in our specific
* PCR12, as firmware-owned PCRs are very difficult to use as they'll contain unpredictable
* measurements that are not under control of the machine owner. */
m = false;
(void) tpm_log_load_options(extra16, &m);
parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
/* SMBIOS OEM Strings data is controlled by the host admin and not covered
* by the VM attestation, so MUST NOT be trusted when in a confidential VM */
if (!is_confidential_vm()) {
const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra");
if (extra) {
_cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline), *extra16 = xstr8_to_16(extra);
cmdline = xasprintf("%ls %ls", tmp, extra16);

/* SMBIOS strings are measured in PCR1, but we also want to measure them in our specific
* PCR12, as firmware-owned PCRs are very difficult to use as they'll contain unpredictable
* measurements that are not under control of the machine owner. */
m = false;
(void) tpm_log_load_options(extra16, &m);
parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
}
}

export_variables(loaded_image);
Expand Down
120 changes: 120 additions & 0 deletions src/boot/efi/vmm.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
# include <cpuid.h>
#endif

#include "confidential-virt-fundamental.h"
#include "drivers.h"
#include "efi-string.h"
#include "string-util-fundamental.h"
Expand Down Expand Up @@ -323,3 +324,122 @@ const char* smbios_find_oem_string(const char *name) {

return NULL;
}

#if defined(__i386__) || defined(__x86_64__)
static uint32_t cpuid_leaf(uint32_t eax, char ret_sig[static 13], bool swapped) {
/* zero-init as some queries explicitly require subleaf == 0 */
uint32_t sig[3] = {};

if (swapped)
__cpuid_count(eax, 0, eax, sig[0], sig[2], sig[1]);
else
__cpuid_count(eax, 0, eax, sig[0], sig[1], sig[2]);

memcpy(ret_sig, sig, sizeof(sig));
ret_sig[12] = 0; /* \0-terminate the string to make string comparison possible */

return eax;
}

static uint64_t msr(uint32_t index) {
uint64_t val;
#ifdef __x86_64__
uint32_t low, high;
asm volatile ("rdmsr" : "=a"(low), "=d"(high) : "c"(index) : "memory");
val = ((uint64_t)high << 32) | low;
#else
asm volatile ("rdmsr" : "=A"(val) : "c"(index) : "memory");
#endif
return val;
}

static bool detect_hyperv_cvm(uint32_t isoltype) {
uint32_t eax, ebx, ecx, edx, feat;
char sig[13] = {};

feat = cpuid_leaf(CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS, sig, false);

if (feat < CPUID_HYPERV_MIN || feat > CPUID_HYPERV_MAX)
return false;

if (memcmp(sig, CPUID_SIG_HYPERV, sizeof(sig)) != 0)
return false;

__cpuid(CPUID_HYPERV_FEATURES, eax, ebx, ecx, edx);

if (ebx & CPUID_HYPERV_ISOLATION && !(ebx & CPUID_HYPERV_CPU_MANAGEMENT)) {
__cpuid(CPUID_HYPERV_ISOLATION_CONFIG, eax, ebx, ecx, edx);

if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) == isoltype)
return true;
}

return false;
}

static bool detect_sev(void) {
uint32_t eax, ebx, ecx, edx;
uint64_t msrval;

__cpuid(CPUID_GET_HIGHEST_FUNCTION, eax, ebx, ecx, edx);

if (eax < CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES)
return false;

__cpuid(CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES, eax, ebx, ecx, edx);

/* bit 1 == CPU supports SEV feature
*
* Note, Azure blocks this CPUID leaf from its SEV-SNP
* guests, so we must fallback to trying some HyperV
* specific CPUID checks.
*/
if (!(eax & EAX_SEV))
return detect_hyperv_cvm(CPUID_HYPERV_ISOLATION_TYPE_SNP);

msrval = msr(MSR_AMD64_SEV);

if (msrval & (MSR_SEV_SNP | MSR_SEV_ES | MSR_SEV))
return true;

return false;
}

static bool detect_tdx(void) {
uint32_t eax, ebx, ecx, edx;
char sig[13] = {};

__cpuid(CPUID_GET_HIGHEST_FUNCTION, eax, ebx, ecx, edx);

if (eax < CPUID_INTEL_TDX_ENUMERATION)
return false;

cpuid_leaf(CPUID_INTEL_TDX_ENUMERATION, sig, true);

if (memcmp(sig, CPUID_SIG_INTEL_TDX, sizeof(sig)) == 0)
return true;

if (detect_hyperv_cvm(CPUID_HYPERV_ISOLATION_TYPE_TDX))
return true;

return false;
}
#endif /* ! __i386__ && ! __x86_64__ */

bool is_confidential_vm(void) {
#if defined(__i386__) || defined(__x86_64__)
char sig[13] = {};

if (!cpuid_in_hypervisor())
return false;

cpuid_leaf(0, sig, true);

if (memcmp(sig, CPUID_SIG_AMD, sizeof(sig)) == 0)
return detect_sev();
if (memcmp(sig, CPUID_SIG_INTEL, sizeof(sig)) == 0)
return detect_tdx();
#endif /* ! __i386__ && ! __x86_64__ */

return false;
}
2 changes: 2 additions & 0 deletions src/boot/efi/vmm.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ EFI_STATUS vmm_open(EFI_HANDLE *ret_qemu_dev, EFI_FILE **ret_qemu_dir);

bool in_hypervisor(void);

bool is_confidential_vm(void);

const char* smbios_find_oem_string(const char *name);
73 changes: 73 additions & 0 deletions src/fundamental/confidential-virt-fundamental.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include <stdint.h>

/* Keep CVM detection logic in this file at feature parity with
* that in src/efi/boot/vmm.c */

#define CPUID_PROCESSOR_INFO_AND_FEATURE_BITS UINT32_C(0x1)

/*
* AMD64 Architecture Programmer’s Manual Volume 3:
* General-Purpose and System Instructions.
* Chapter: E4.1 - Maximum Extended Function Number and Vendor String
* https://www.amd.com/system/files/TechDocs/24594.pdf
*/
#define CPUID_GET_HIGHEST_FUNCTION UINT32_C(0x80000000)

/*
* AMD64 Architecture Programmer’s Manual Volume 3:
* General-Purpose and System Instructions.
* Chapter: E4.17 - Encrypted Memory Capabilities
* https://www.amd.com/system/files/TechDocs/24594.pdf
*/
#define CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES UINT32_C(0x8000001f)

/*
* AMD64 Architecture Programmer’s Manual Volume 3:
* General-Purpose and System Instructions.
* Chapter: 15.34.10 - SEV_STATUS MSR
* https://www.amd.com/system/files/TechDocs/24593.pdf
*/
#define MSR_AMD64_SEV UINT32_C(0xc0010131)

/*
* Intel® TDX Module v1.5 Base Architecture Specification
* Chapter: 11.2
* https://www.intel.com/content/www/us/en/content-details/733575/intel-tdx-module-v1-5-base-architecture-specification.html
*/

#define CPUID_INTEL_TDX_ENUMERATION UINT32_C(0x21)

/* Requirements for Implementing the Microsoft Hypervisor Interface
* https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/tlfs
*/
#define CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS UINT32_C(0x40000000)

#define CPUID_HYPERV_FEATURES UINT32_C(0x40000003)

#define CPUID_HYPERV_ISOLATION_CONFIG UINT32_C(0x4000000C)

#define CPUID_HYPERV_MIN UINT32_C(0x40000005)
#define CPUID_HYPERV_MAX UINT32_C(0x4000ffff)

#define CPUID_SIG_AMD "AuthenticAMD"
#define CPUID_SIG_INTEL "GenuineIntel"
#define CPUID_SIG_INTEL_TDX "IntelTDX "
#define CPUID_SIG_HYPERV "Microsoft Hv"

/* ecx bit 31: set => hyperpvisor, unset => bare metal */
#define CPUID_FEATURE_HYPERVISOR (UINT32_C(1) << 31)

/* Linux include/asm-generic/hyperv-tlfs.h */
#define CPUID_HYPERV_CPU_MANAGEMENT (UINT32_C(1) << 12) /* root partition */
#define CPUID_HYPERV_ISOLATION (UINT32_C(1) << 22) /* confidential VM partition */

#define CPUID_HYPERV_ISOLATION_TYPE_MASK UINT32_C(0xf)
#define CPUID_HYPERV_ISOLATION_TYPE_SNP 2
#define CPUID_HYPERV_ISOLATION_TYPE_TDX 3

#define EAX_SEV (UINT32_C(1) << 1)
#define MSR_SEV (UINT64_C(1) << 0)
#define MSR_SEV_ES (UINT64_C(1) << 1)
#define MSR_SEV_SNP (UINT64_C(1) << 2)
Loading