- Add the necessary glue so that the kernel can run as a confidential

SEV-SNP vTOM guest on Hyper-V. A vTOM guest basically splits the
   address space in two parts: encrypted and unencrypted. The use case
   being running unmodified guests on the Hyper-V confidential computing
   hypervisor
 
 - Double-buffer messages between the guest and the hardware PSP device
   so that no partial buffers are copied back'n'forth and thus potential
   message integrity and leak attacks are possible
 
 - Name the return value the sev-guest driver returns when the hw PSP
   device hasn't been called, explicitly
 
 - Cleanups
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEzv7L6UO9uDPlPSfHEsHwGGHeVUoFAmRGl8gACgkQEsHwGGHe
 VUoEDhAAiw4+2nZR7XUJ7pewlXG7AJJZsVIpzzcF6Gyymn0LFCyMnP7O3snmFqzz
 aik0q2LzWrmDQ3Nmmzul0wtdsuW7Nik6BP9oF3WnB911+gGbpXyNWZ8EhOPNzkUR
 9D8Sp6f0xmqNE3YuzEpanufiDswgUxi++DRdmIRAs1TTh4bfUFWZcib1pdwoqSmR
 oS3UfVwVZ4Ee2Qm1f3n3XQ0FUpsjWeARPExUkLEvd8XeonTP+6aGAdggg9MnPcsl
 3zpSmOpuZ6VQbDrHxo3BH9HFuIUOd6S9PO++b9F6WxNPGEMk7fHa7ahOA6HjhgVz
 5Da3BN16OS9j64cZsYHMPsBcd+ja1YmvvZGypsY0d6X4d3M1zTPW+XeLbyb+VFBy
 SvA7z+JuxtLKVpju65sNiJWw8ZDTSu+eEYNDeeGLvAj3bxtclJjcPdMEPdzxmC5K
 eAhmRmiFuVM4nXMAR6cspVTsxvlTHFtd5gdm6RlRnvd7aV77Zl1CLzTy8IHTVpvI
 t7XTbtjEjYc0pI6cXXptHEOnBLjXUMPcqgGFgJYEauH6EvrxoWszUZD0tS3Hw80A
 K+Rwnc70ubq/PsgZcF4Ayer1j49z1NPfk5D4EA7/ChN6iNhQA8OqHT1UBrHAgqls
 2UAwzE2sQZnjDvGZghlOtFIQUIhwue7m93DaRi19EOdKYxVjV6U=
 =ZAw9
 -----END PGP SIGNATURE-----

Merge tag 'x86_sev_for_v6.4_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 SEV updates from Borislav Petkov:

 - Add the necessary glue so that the kernel can run as a confidential
   SEV-SNP vTOM guest on Hyper-V. A vTOM guest basically splits the
   address space in two parts: encrypted and unencrypted. The use case
   being running unmodified guests on the Hyper-V confidential computing
   hypervisor

 - Double-buffer messages between the guest and the hardware PSP device
   so that no partial buffers are copied back'n'forth and thus potential
   message integrity and leak attacks are possible

 - Name the return value the sev-guest driver returns when the hw PSP
   device hasn't been called, explicitly

 - Cleanups

* tag 'x86_sev_for_v6.4_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/hyperv: Change vTOM handling to use standard coco mechanisms
  init: Call mem_encrypt_init() after Hyper-V hypercall init is done
  x86/mm: Handle decryption/re-encryption of bss_decrypted consistently
  Drivers: hv: Explicitly request decrypted in vmap_pfn() calls
  x86/hyperv: Reorder code to facilitate future work
  x86/ioremap: Add hypervisor callback for private MMIO mapping in coco VM
  x86/sev: Change snp_guest_issue_request()'s fw_err argument
  virt/coco/sev-guest: Double-buffer messages
  crypto: ccp: Get rid of __sev_platform_init_locked()'s local function pointer
  crypto: ccp - Name -1 return value as SEV_RET_NO_FW_CALL
This commit is contained in:
Linus Torvalds 2023-04-25 10:48:08 -07:00
commit bc1bb2a49b
25 changed files with 311 additions and 168 deletions

View File

@ -37,11 +37,11 @@ along with a description:
the return value. General error numbers (-ENOMEM, -EINVAL) the return value. General error numbers (-ENOMEM, -EINVAL)
are not detailed, but errors with specific meanings are. are not detailed, but errors with specific meanings are.
The guest ioctl should be issued on a file descriptor of the /dev/sev-guest device. The guest ioctl should be issued on a file descriptor of the /dev/sev-guest
The ioctl accepts struct snp_user_guest_request. The input and output structure is device. The ioctl accepts struct snp_user_guest_request. The input and
specified through the req_data and resp_data field respectively. If the ioctl fails output structure is specified through the req_data and resp_data field
to execute due to a firmware error, then fw_err code will be set otherwise the respectively. If the ioctl fails to execute due to a firmware error, then
fw_err will be set to 0x00000000000000ff. the fw_error code will be set, otherwise fw_error will be set to -1.
The firmware checks that the message sequence counter is one greater than The firmware checks that the message sequence counter is one greater than
the guests message sequence counter. If guest driver fails to increment message the guests message sequence counter. If guest driver fails to increment message
@ -57,8 +57,14 @@ counter (e.g. counter overflow), then -EIO will be returned.
__u64 req_data; __u64 req_data;
__u64 resp_data; __u64 resp_data;
/* firmware error code on failure (see psp-sev.h) */ /* bits[63:32]: VMM error code, bits[31:0] firmware error code (see psp-sev.h) */
__u64 fw_err; union {
__u64 exitinfo2;
struct {
__u32 fw_error;
__u32 vmm_error;
};
};
}; };
2.1 SNP_GET_REPORT 2.1 SNP_GET_REPORT

View File

@ -29,6 +29,22 @@ static bool intel_cc_platform_has(enum cc_attr attr)
} }
} }
/*
* Handle the SEV-SNP vTOM case where sme_me_mask is zero, and
* the other levels of SME/SEV functionality, including C-bit
* based SEV-SNP, are not enabled.
*/
static __maybe_unused bool amd_cc_platform_vtom(enum cc_attr attr)
{
switch (attr) {
case CC_ATTR_GUEST_MEM_ENCRYPT:
case CC_ATTR_MEM_ENCRYPT:
return true;
default:
return false;
}
}
/* /*
* SME and SEV are very similar but they are not the same, so there are * SME and SEV are very similar but they are not the same, so there are
* times that the kernel will need to distinguish between SME and SEV. The * times that the kernel will need to distinguish between SME and SEV. The
@ -41,9 +57,14 @@ static bool intel_cc_platform_has(enum cc_attr attr)
* up under SME the trampoline area cannot be encrypted, whereas under SEV * up under SME the trampoline area cannot be encrypted, whereas under SEV
* the trampoline area must be encrypted. * the trampoline area must be encrypted.
*/ */
static bool amd_cc_platform_has(enum cc_attr attr) static bool amd_cc_platform_has(enum cc_attr attr)
{ {
#ifdef CONFIG_AMD_MEM_ENCRYPT #ifdef CONFIG_AMD_MEM_ENCRYPT
if (sev_status & MSR_AMD64_SNP_VTOM)
return amd_cc_platform_vtom(attr);
switch (attr) { switch (attr) {
case CC_ATTR_MEM_ENCRYPT: case CC_ATTR_MEM_ENCRYPT:
return sme_me_mask; return sme_me_mask;
@ -76,11 +97,6 @@ static bool amd_cc_platform_has(enum cc_attr attr)
#endif #endif
} }
static bool hyperv_cc_platform_has(enum cc_attr attr)
{
return attr == CC_ATTR_GUEST_MEM_ENCRYPT;
}
bool cc_platform_has(enum cc_attr attr) bool cc_platform_has(enum cc_attr attr)
{ {
switch (vendor) { switch (vendor) {
@ -88,8 +104,6 @@ bool cc_platform_has(enum cc_attr attr)
return amd_cc_platform_has(attr); return amd_cc_platform_has(attr);
case CC_VENDOR_INTEL: case CC_VENDOR_INTEL:
return intel_cc_platform_has(attr); return intel_cc_platform_has(attr);
case CC_VENDOR_HYPERV:
return hyperv_cc_platform_has(attr);
default: default:
return false; return false;
} }
@ -103,11 +117,14 @@ u64 cc_mkenc(u64 val)
* encryption status of the page. * encryption status of the page.
* *
* - for AMD, bit *set* means the page is encrypted * - for AMD, bit *set* means the page is encrypted
* - for Intel *clear* means encrypted. * - for AMD with vTOM and for Intel, *clear* means encrypted
*/ */
switch (vendor) { switch (vendor) {
case CC_VENDOR_AMD: case CC_VENDOR_AMD:
return val | cc_mask; if (sev_status & MSR_AMD64_SNP_VTOM)
return val & ~cc_mask;
else
return val | cc_mask;
case CC_VENDOR_INTEL: case CC_VENDOR_INTEL:
return val & ~cc_mask; return val & ~cc_mask;
default: default:
@ -120,7 +137,10 @@ u64 cc_mkdec(u64 val)
/* See comment in cc_mkenc() */ /* See comment in cc_mkenc() */
switch (vendor) { switch (vendor) {
case CC_VENDOR_AMD: case CC_VENDOR_AMD:
return val & ~cc_mask; if (sev_status & MSR_AMD64_SNP_VTOM)
return val | cc_mask;
else
return val & ~cc_mask;
case CC_VENDOR_INTEL: case CC_VENDOR_INTEL:
return val | cc_mask; return val | cc_mask;
default: default:

View File

@ -29,7 +29,6 @@
#include <linux/syscore_ops.h> #include <linux/syscore_ops.h>
#include <clocksource/hyperv_timer.h> #include <clocksource/hyperv_timer.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/swiotlb.h>
int hyperv_init_cpuhp; int hyperv_init_cpuhp;
u64 hv_current_partition_id = ~0ull; u64 hv_current_partition_id = ~0ull;
@ -504,16 +503,6 @@ void __init hyperv_init(void)
/* Query the VMs extended capability once, so that it can be cached. */ /* Query the VMs extended capability once, so that it can be cached. */
hv_query_ext_cap(0); hv_query_ext_cap(0);
#ifdef CONFIG_SWIOTLB
/*
* Swiotlb bounce buffer needs to be mapped in extra address
* space. Map function doesn't work in the early place and so
* call swiotlb_update_mem_attributes() here.
*/
if (hv_is_isolation_supported())
swiotlb_update_mem_attributes();
#endif
return; return;
clean_guest_os_id: clean_guest_os_id:

View File

@ -13,6 +13,8 @@
#include <asm/svm.h> #include <asm/svm.h>
#include <asm/sev.h> #include <asm/sev.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/coco.h>
#include <asm/mem_encrypt.h>
#include <asm/mshyperv.h> #include <asm/mshyperv.h>
#include <asm/hypervisor.h> #include <asm/hypervisor.h>
@ -233,41 +235,6 @@ void hv_ghcb_msr_read(u64 msr, u64 *value)
local_irq_restore(flags); local_irq_restore(flags);
} }
EXPORT_SYMBOL_GPL(hv_ghcb_msr_read); EXPORT_SYMBOL_GPL(hv_ghcb_msr_read);
#endif
enum hv_isolation_type hv_get_isolation_type(void)
{
if (!(ms_hyperv.priv_high & HV_ISOLATION))
return HV_ISOLATION_TYPE_NONE;
return FIELD_GET(HV_ISOLATION_TYPE, ms_hyperv.isolation_config_b);
}
EXPORT_SYMBOL_GPL(hv_get_isolation_type);
/*
* hv_is_isolation_supported - Check system runs in the Hyper-V
* isolation VM.
*/
bool hv_is_isolation_supported(void)
{
if (!cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
return false;
if (!hypervisor_is_type(X86_HYPER_MS_HYPERV))
return false;
return hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE;
}
DEFINE_STATIC_KEY_FALSE(isolation_type_snp);
/*
* hv_isolation_type_snp - Check system runs in the AMD SEV-SNP based
* isolation VM.
*/
bool hv_isolation_type_snp(void)
{
return static_branch_unlikely(&isolation_type_snp);
}
/* /*
* hv_mark_gpa_visibility - Set pages visible to host via hvcall. * hv_mark_gpa_visibility - Set pages visible to host via hvcall.
@ -320,27 +287,25 @@ static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
} }
/* /*
* hv_set_mem_host_visibility - Set specified memory visible to host. * hv_vtom_set_host_visibility - Set specified memory visible to host.
* *
* In Isolation VM, all guest memory is encrypted from host and guest * In Isolation VM, all guest memory is encrypted from host and guest
* needs to set memory visible to host via hvcall before sharing memory * needs to set memory visible to host via hvcall before sharing memory
* with host. This function works as wrap of hv_mark_gpa_visibility() * with host. This function works as wrap of hv_mark_gpa_visibility()
* with memory base and size. * with memory base and size.
*/ */
int hv_set_mem_host_visibility(unsigned long kbuffer, int pagecount, bool visible) static bool hv_vtom_set_host_visibility(unsigned long kbuffer, int pagecount, bool enc)
{ {
enum hv_mem_host_visibility visibility = visible ? enum hv_mem_host_visibility visibility = enc ?
VMBUS_PAGE_VISIBLE_READ_WRITE : VMBUS_PAGE_NOT_VISIBLE; VMBUS_PAGE_NOT_VISIBLE : VMBUS_PAGE_VISIBLE_READ_WRITE;
u64 *pfn_array; u64 *pfn_array;
int ret = 0; int ret = 0;
bool result = true;
int i, pfn; int i, pfn;
if (!hv_is_isolation_supported() || !hv_hypercall_pg)
return 0;
pfn_array = kmalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL); pfn_array = kmalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
if (!pfn_array) if (!pfn_array)
return -ENOMEM; return false;
for (i = 0, pfn = 0; i < pagecount; i++) { for (i = 0, pfn = 0; i < pagecount; i++) {
pfn_array[pfn] = virt_to_hvpfn((void *)kbuffer + i * HV_HYP_PAGE_SIZE); pfn_array[pfn] = virt_to_hvpfn((void *)kbuffer + i * HV_HYP_PAGE_SIZE);
@ -349,17 +314,68 @@ int hv_set_mem_host_visibility(unsigned long kbuffer, int pagecount, bool visibl
if (pfn == HV_MAX_MODIFY_GPA_REP_COUNT || i == pagecount - 1) { if (pfn == HV_MAX_MODIFY_GPA_REP_COUNT || i == pagecount - 1) {
ret = hv_mark_gpa_visibility(pfn, pfn_array, ret = hv_mark_gpa_visibility(pfn, pfn_array,
visibility); visibility);
if (ret) if (ret) {
result = false;
goto err_free_pfn_array; goto err_free_pfn_array;
}
pfn = 0; pfn = 0;
} }
} }
err_free_pfn_array: err_free_pfn_array:
kfree(pfn_array); kfree(pfn_array);
return ret; return result;
} }
static bool hv_vtom_tlb_flush_required(bool private)
{
return true;
}
static bool hv_vtom_cache_flush_required(void)
{
return false;
}
static bool hv_is_private_mmio(u64 addr)
{
/*
* Hyper-V always provides a single IO-APIC in a guest VM.
* When a paravisor is used, it is emulated by the paravisor
* in the guest context and must be mapped private.
*/
if (addr >= HV_IOAPIC_BASE_ADDRESS &&
addr < (HV_IOAPIC_BASE_ADDRESS + PAGE_SIZE))
return true;
/* Same with a vTPM */
if (addr >= VTPM_BASE_ADDRESS &&
addr < (VTPM_BASE_ADDRESS + PAGE_SIZE))
return true;
return false;
}
void __init hv_vtom_init(void)
{
/*
* By design, a VM using vTOM doesn't see the SEV setting,
* so SEV initialization is bypassed and sev_status isn't set.
* Set it here to indicate a vTOM VM.
*/
sev_status = MSR_AMD64_SNP_VTOM;
cc_set_vendor(CC_VENDOR_AMD);
cc_set_mask(ms_hyperv.shared_gpa_boundary);
physical_mask &= ms_hyperv.shared_gpa_boundary - 1;
x86_platform.hyper.is_private_mmio = hv_is_private_mmio;
x86_platform.guest.enc_cache_flush_required = hv_vtom_cache_flush_required;
x86_platform.guest.enc_tlb_flush_required = hv_vtom_tlb_flush_required;
x86_platform.guest.enc_status_change_finish = hv_vtom_set_host_visibility;
}
#endif /* CONFIG_AMD_MEM_ENCRYPT */
/* /*
* hv_map_memory - map memory to extra space in the AMD SEV-SNP Isolation VM. * hv_map_memory - map memory to extra space in the AMD SEV-SNP Isolation VM.
*/ */
@ -377,7 +393,7 @@ void *hv_map_memory(void *addr, unsigned long size)
pfns[i] = vmalloc_to_pfn(addr + i * PAGE_SIZE) + pfns[i] = vmalloc_to_pfn(addr + i * PAGE_SIZE) +
(ms_hyperv.shared_gpa_boundary >> PAGE_SHIFT); (ms_hyperv.shared_gpa_boundary >> PAGE_SHIFT);
vaddr = vmap_pfn(pfns, size / PAGE_SIZE, PAGE_KERNEL_IO); vaddr = vmap_pfn(pfns, size / PAGE_SIZE, pgprot_decrypted(PAGE_KERNEL));
kfree(pfns); kfree(pfns);
return vaddr; return vaddr;
@ -387,3 +403,37 @@ void hv_unmap_memory(void *addr)
{ {
vunmap(addr); vunmap(addr);
} }
enum hv_isolation_type hv_get_isolation_type(void)
{
if (!(ms_hyperv.priv_high & HV_ISOLATION))
return HV_ISOLATION_TYPE_NONE;
return FIELD_GET(HV_ISOLATION_TYPE, ms_hyperv.isolation_config_b);
}
EXPORT_SYMBOL_GPL(hv_get_isolation_type);
/*
* hv_is_isolation_supported - Check system runs in the Hyper-V
* isolation VM.
*/
bool hv_is_isolation_supported(void)
{
if (!cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
return false;
if (!hypervisor_is_type(X86_HYPER_MS_HYPERV))
return false;
return hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE;
}
DEFINE_STATIC_KEY_FALSE(isolation_type_snp);
/*
* hv_isolation_type_snp - Check system runs in the AMD SEV-SNP based
* isolation VM.
*/
bool hv_isolation_type_snp(void)
{
return static_branch_unlikely(&isolation_type_snp);
}

View File

@ -7,7 +7,6 @@
enum cc_vendor { enum cc_vendor {
CC_VENDOR_NONE, CC_VENDOR_NONE,
CC_VENDOR_AMD, CC_VENDOR_AMD,
CC_VENDOR_HYPERV,
CC_VENDOR_INTEL, CC_VENDOR_INTEL,
}; };

View File

@ -56,6 +56,7 @@ void __init sev_es_init_vc_handling(void);
#else /* !CONFIG_AMD_MEM_ENCRYPT */ #else /* !CONFIG_AMD_MEM_ENCRYPT */
#define sme_me_mask 0ULL #define sme_me_mask 0ULL
#define sev_status 0ULL
static inline void __init sme_early_encrypt(resource_size_t paddr, static inline void __init sme_early_encrypt(resource_size_t paddr,
unsigned long size) { } unsigned long size) { }

View File

@ -11,6 +11,14 @@
#include <asm/paravirt.h> #include <asm/paravirt.h>
#include <asm/mshyperv.h> #include <asm/mshyperv.h>
/*
* Hyper-V always provides a single IO-APIC at this MMIO address.
* Ideally, the value should be looked up in ACPI tables, but it
* is needed for mapping the IO-APIC early in boot on Confidential
* VMs, before ACPI functions can be used.
*/
#define HV_IOAPIC_BASE_ADDRESS 0xfec00000
union hv_ghcb; union hv_ghcb;
DECLARE_STATIC_KEY_FALSE(isolation_type_snp); DECLARE_STATIC_KEY_FALSE(isolation_type_snp);
@ -206,18 +214,19 @@ struct irq_domain *hv_create_pci_msi_domain(void);
int hv_map_ioapic_interrupt(int ioapic_id, bool level, int vcpu, int vector, int hv_map_ioapic_interrupt(int ioapic_id, bool level, int vcpu, int vector,
struct hv_interrupt_entry *entry); struct hv_interrupt_entry *entry);
int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry); int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry);
int hv_set_mem_host_visibility(unsigned long addr, int numpages, bool visible);
#ifdef CONFIG_AMD_MEM_ENCRYPT #ifdef CONFIG_AMD_MEM_ENCRYPT
void hv_ghcb_msr_write(u64 msr, u64 value); void hv_ghcb_msr_write(u64 msr, u64 value);
void hv_ghcb_msr_read(u64 msr, u64 *value); void hv_ghcb_msr_read(u64 msr, u64 *value);
bool hv_ghcb_negotiate_protocol(void); bool hv_ghcb_negotiate_protocol(void);
void hv_ghcb_terminate(unsigned int set, unsigned int reason); void hv_ghcb_terminate(unsigned int set, unsigned int reason);
void hv_vtom_init(void);
#else #else
static inline void hv_ghcb_msr_write(u64 msr, u64 value) {} static inline void hv_ghcb_msr_write(u64 msr, u64 value) {}
static inline void hv_ghcb_msr_read(u64 msr, u64 *value) {} static inline void hv_ghcb_msr_read(u64 msr, u64 *value) {}
static inline bool hv_ghcb_negotiate_protocol(void) { return false; } static inline bool hv_ghcb_negotiate_protocol(void) { return false; }
static inline void hv_ghcb_terminate(unsigned int set, unsigned int reason) {} static inline void hv_ghcb_terminate(unsigned int set, unsigned int reason) {}
static inline void hv_vtom_init(void) {}
#endif #endif
extern bool hv_isolation_type_snp(void); extern bool hv_isolation_type_snp(void);
@ -259,11 +268,6 @@ static inline void hv_set_register(unsigned int reg, u64 value) { }
static inline u64 hv_get_register(unsigned int reg) { return 0; } static inline u64 hv_get_register(unsigned int reg) { return 0; }
static inline void hv_set_non_nested_register(unsigned int reg, u64 value) { } static inline void hv_set_non_nested_register(unsigned int reg, u64 value) { }
static inline u64 hv_get_non_nested_register(unsigned int reg) { return 0; } static inline u64 hv_get_non_nested_register(unsigned int reg) { return 0; }
static inline int hv_set_mem_host_visibility(unsigned long addr, int numpages,
bool visible)
{
return -1;
}
#endif /* CONFIG_HYPERV */ #endif /* CONFIG_HYPERV */

View File

@ -128,10 +128,6 @@ struct snp_psc_desc {
struct psc_entry entries[VMGEXIT_PSC_MAX_ENTRY]; struct psc_entry entries[VMGEXIT_PSC_MAX_ENTRY];
} __packed; } __packed;
/* Guest message request error codes */
#define SNP_GUEST_REQ_INVALID_LEN BIT_ULL(32)
#define SNP_GUEST_REQ_ERR_BUSY BIT_ULL(33)
#define GHCB_MSR_TERM_REQ 0x100 #define GHCB_MSR_TERM_REQ 0x100
#define GHCB_MSR_TERM_REASON_SET_POS 12 #define GHCB_MSR_TERM_REASON_SET_POS 12
#define GHCB_MSR_TERM_REASON_SET_MASK 0xf #define GHCB_MSR_TERM_REASON_SET_MASK 0xf

View File

@ -9,6 +9,8 @@
#define __ASM_ENCRYPTED_STATE_H #define __ASM_ENCRYPTED_STATE_H
#include <linux/types.h> #include <linux/types.h>
#include <linux/sev-guest.h>
#include <asm/insn.h> #include <asm/insn.h>
#include <asm/sev-common.h> #include <asm/sev-common.h>
#include <asm/bootparam.h> #include <asm/bootparam.h>
@ -185,6 +187,9 @@ static inline int pvalidate(unsigned long vaddr, bool rmp_psize, bool validate)
return rc; return rc;
} }
struct snp_guest_request_ioctl;
void setup_ghcb(void); void setup_ghcb(void);
void __init early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr, void __init early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr,
unsigned int npages); unsigned int npages);
@ -196,7 +201,7 @@ void snp_set_memory_private(unsigned long vaddr, unsigned int npages);
void snp_set_wakeup_secondary_cpu(void); void snp_set_wakeup_secondary_cpu(void);
bool snp_init(struct boot_params *bp); bool snp_init(struct boot_params *bp);
void __init __noreturn snp_abort(void); void __init __noreturn snp_abort(void);
int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, unsigned long *fw_err); int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio);
#else #else
static inline void sev_es_ist_enter(struct pt_regs *regs) { } static inline void sev_es_ist_enter(struct pt_regs *regs) { }
static inline void sev_es_ist_exit(void) { } static inline void sev_es_ist_exit(void) { }
@ -216,8 +221,7 @@ static inline void snp_set_memory_private(unsigned long vaddr, unsigned int npag
static inline void snp_set_wakeup_secondary_cpu(void) { } static inline void snp_set_wakeup_secondary_cpu(void) { }
static inline bool snp_init(struct boot_params *bp) { return false; } static inline bool snp_init(struct boot_params *bp) { return false; }
static inline void snp_abort(void) { } static inline void snp_abort(void) { }
static inline int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, static inline int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio)
unsigned long *fw_err)
{ {
return -ENOTTY; return -ENOTTY;
} }

View File

@ -259,11 +259,15 @@ struct x86_legacy_features {
* VMMCALL under SEV-ES. Needs to return 'false' * VMMCALL under SEV-ES. Needs to return 'false'
* if the checks fail. Called from the #VC * if the checks fail. Called from the #VC
* exception handler. * exception handler.
* @is_private_mmio: For CoCo VMs, must map MMIO address as private.
* Used when device is emulated by a paravisor
* layer in the VM context.
*/ */
struct x86_hyper_runtime { struct x86_hyper_runtime {
void (*pin_vcpu)(int cpu); void (*pin_vcpu)(int cpu);
void (*sev_es_hcall_prepare)(struct ghcb *ghcb, struct pt_regs *regs); void (*sev_es_hcall_prepare)(struct ghcb *ghcb, struct pt_regs *regs);
bool (*sev_es_hcall_finish)(struct ghcb *ghcb, struct pt_regs *regs); bool (*sev_es_hcall_finish)(struct ghcb *ghcb, struct pt_regs *regs);
bool (*is_private_mmio)(u64 addr);
}; };
/** /**

View File

@ -66,6 +66,7 @@
#include <asm/hw_irq.h> #include <asm/hw_irq.h>
#include <asm/apic.h> #include <asm/apic.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/x86_init.h>
#define for_each_ioapic(idx) \ #define for_each_ioapic(idx) \
for ((idx) = 0; (idx) < nr_ioapics; (idx)++) for ((idx) = 0; (idx) < nr_ioapics; (idx)++)
@ -2680,10 +2681,15 @@ static void io_apic_set_fixmap(enum fixed_addresses idx, phys_addr_t phys)
pgprot_t flags = FIXMAP_PAGE_NOCACHE; pgprot_t flags = FIXMAP_PAGE_NOCACHE;
/* /*
* Ensure fixmaps for IOAPIC MMIO respect memory encryption pgprot * Ensure fixmaps for IO-APIC MMIO respect memory encryption pgprot
* bits, just like normal ioremap(): * bits, just like normal ioremap():
*/ */
flags = pgprot_decrypted(flags); if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
if (x86_platform.hyper.is_private_mmio(phys))
flags = pgprot_encrypted(flags);
else
flags = pgprot_decrypted(flags);
}
__set_fixmap(idx, phys, flags); __set_fixmap(idx, phys, flags);
} }

View File

@ -33,7 +33,6 @@
#include <asm/nmi.h> #include <asm/nmi.h>
#include <clocksource/hyperv_timer.h> #include <clocksource/hyperv_timer.h>
#include <asm/numa.h> #include <asm/numa.h>
#include <asm/coco.h>
/* Is Linux running as the root partition? */ /* Is Linux running as the root partition? */
bool hv_root_partition; bool hv_root_partition;
@ -401,8 +400,10 @@ static void __init ms_hyperv_init_platform(void)
if (ms_hyperv.priv_high & HV_ISOLATION) { if (ms_hyperv.priv_high & HV_ISOLATION) {
ms_hyperv.isolation_config_a = cpuid_eax(HYPERV_CPUID_ISOLATION_CONFIG); ms_hyperv.isolation_config_a = cpuid_eax(HYPERV_CPUID_ISOLATION_CONFIG);
ms_hyperv.isolation_config_b = cpuid_ebx(HYPERV_CPUID_ISOLATION_CONFIG); ms_hyperv.isolation_config_b = cpuid_ebx(HYPERV_CPUID_ISOLATION_CONFIG);
ms_hyperv.shared_gpa_boundary =
BIT_ULL(ms_hyperv.shared_gpa_boundary_bits); if (ms_hyperv.shared_gpa_boundary_active)
ms_hyperv.shared_gpa_boundary =
BIT_ULL(ms_hyperv.shared_gpa_boundary_bits);
pr_info("Hyper-V: Isolation Config: Group A 0x%x, Group B 0x%x\n", pr_info("Hyper-V: Isolation Config: Group A 0x%x, Group B 0x%x\n",
ms_hyperv.isolation_config_a, ms_hyperv.isolation_config_b); ms_hyperv.isolation_config_a, ms_hyperv.isolation_config_b);
@ -413,11 +414,6 @@ static void __init ms_hyperv_init_platform(void)
swiotlb_unencrypted_base = ms_hyperv.shared_gpa_boundary; swiotlb_unencrypted_base = ms_hyperv.shared_gpa_boundary;
#endif #endif
} }
/* Isolation VMs are unenlightened SEV-based VMs, thus this check: */
if (IS_ENABLED(CONFIG_AMD_MEM_ENCRYPT)) {
if (hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE)
cc_set_vendor(CC_VENDOR_HYPERV);
}
} }
if (hv_max_functions_eax >= HYPERV_CPUID_NESTED_FEATURES) { if (hv_max_functions_eax >= HYPERV_CPUID_NESTED_FEATURES) {
@ -486,6 +482,9 @@ static void __init ms_hyperv_init_platform(void)
i8253_clear_counter_on_shutdown = false; i8253_clear_counter_on_shutdown = false;
#if IS_ENABLED(CONFIG_HYPERV) #if IS_ENABLED(CONFIG_HYPERV)
if ((hv_get_isolation_type() == HV_ISOLATION_TYPE_VBS) ||
(hv_get_isolation_type() == HV_ISOLATION_TYPE_SNP))
hv_vtom_init();
/* /*
* Setup the hook to get control post apic initialization. * Setup the hook to get control post apic initialization.
*/ */

View File

@ -22,6 +22,8 @@
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/psp-sev.h>
#include <uapi/linux/sev-guest.h>
#include <asm/cpu_entry_area.h> #include <asm/cpu_entry_area.h>
#include <asm/stacktrace.h> #include <asm/stacktrace.h>
@ -2175,7 +2177,7 @@ static int __init init_sev_config(char *str)
} }
__setup("sev=", init_sev_config); __setup("sev=", init_sev_config);
int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, unsigned long *fw_err) int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio)
{ {
struct ghcb_state state; struct ghcb_state state;
struct es_em_ctxt ctxt; struct es_em_ctxt ctxt;
@ -2183,8 +2185,7 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, unsigned
struct ghcb *ghcb; struct ghcb *ghcb;
int ret; int ret;
if (!fw_err) rio->exitinfo2 = SEV_RET_NO_FW_CALL;
return -EINVAL;
/* /*
* __sev_get_ghcb() needs to run with IRQs disabled because it is using * __sev_get_ghcb() needs to run with IRQs disabled because it is using
@ -2209,16 +2210,16 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, unsigned
if (ret) if (ret)
goto e_put; goto e_put;
*fw_err = ghcb->save.sw_exit_info_2; rio->exitinfo2 = ghcb->save.sw_exit_info_2;
switch (*fw_err) { switch (rio->exitinfo2) {
case 0: case 0:
break; break;
case SNP_GUEST_REQ_ERR_BUSY: case SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_BUSY):
ret = -EAGAIN; ret = -EAGAIN;
break; break;
case SNP_GUEST_REQ_INVALID_LEN: case SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN):
/* Number of expected pages are returned in RBX */ /* Number of expected pages are returned in RBX */
if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) { if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
input->data_npages = ghcb_get_rbx(ghcb); input->data_npages = ghcb_get_rbx(ghcb);

View File

@ -134,6 +134,7 @@ static void enc_status_change_prepare_noop(unsigned long vaddr, int npages, bool
static bool enc_status_change_finish_noop(unsigned long vaddr, int npages, bool enc) { return false; } static bool enc_status_change_finish_noop(unsigned long vaddr, int npages, bool enc) { return false; }
static bool enc_tlb_flush_required_noop(bool enc) { return false; } static bool enc_tlb_flush_required_noop(bool enc) { return false; }
static bool enc_cache_flush_required_noop(void) { return false; } static bool enc_cache_flush_required_noop(void) { return false; }
static bool is_private_mmio_noop(u64 addr) {return false; }
struct x86_platform_ops x86_platform __ro_after_init = { struct x86_platform_ops x86_platform __ro_after_init = {
.calibrate_cpu = native_calibrate_cpu_early, .calibrate_cpu = native_calibrate_cpu_early,
@ -149,6 +150,7 @@ struct x86_platform_ops x86_platform __ro_after_init = {
.realmode_reserve = reserve_real_mode, .realmode_reserve = reserve_real_mode,
.realmode_init = init_real_mode, .realmode_init = init_real_mode,
.hyper.pin_vcpu = x86_op_int_noop, .hyper.pin_vcpu = x86_op_int_noop,
.hyper.is_private_mmio = is_private_mmio_noop,
.guest = { .guest = {
.enc_status_change_prepare = enc_status_change_prepare_noop, .enc_status_change_prepare = enc_status_change_prepare_noop,

View File

@ -116,6 +116,11 @@ static void __ioremap_check_other(resource_size_t addr, struct ioremap_desc *des
if (!cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) if (!cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT))
return; return;
if (x86_platform.hyper.is_private_mmio(addr)) {
desc->flags |= IORES_MAP_ENCRYPTED;
return;
}
if (!IS_ENABLED(CONFIG_EFI)) if (!IS_ENABLED(CONFIG_EFI))
return; return;

View File

@ -513,10 +513,14 @@ void __init mem_encrypt_free_decrypted_mem(void)
npages = (vaddr_end - vaddr) >> PAGE_SHIFT; npages = (vaddr_end - vaddr) >> PAGE_SHIFT;
/* /*
* The unused memory range was mapped decrypted, change the encryption * If the unused memory range was mapped decrypted, change the encryption
* attribute from decrypted to encrypted before freeing it. * attribute from decrypted to encrypted before freeing it. Base the
* re-encryption on the same condition used for the decryption in
* sme_postprocess_startup(). Higher level abstractions, such as
* CC_ATTR_MEM_ENCRYPT, aren't necessarily equivalent in a Hyper-V VM
* using vTOM, where sme_me_mask is always zero.
*/ */
if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) { if (sme_me_mask) {
r = set_memory_encrypted(vaddr, npages); r = set_memory_encrypted(vaddr, npages);
if (r) { if (r) {
pr_warn("failed to free unused decrypted pages\n"); pr_warn("failed to free unused decrypted pages\n");

View File

@ -2175,9 +2175,6 @@ static int __set_memory_enc_pgtable(unsigned long addr, int numpages, bool enc)
static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc) static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)
{ {
if (hv_is_isolation_supported())
return hv_set_mem_host_visibility(addr, numpages, !enc);
if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) if (cc_platform_has(CC_ATTR_MEM_ENCRYPT))
return __set_memory_enc_pgtable(addr, numpages, enc); return __set_memory_enc_pgtable(addr, numpages, enc);

View File

@ -442,12 +442,19 @@ static int __sev_init_ex_locked(int *error)
return __sev_do_cmd_locked(SEV_CMD_INIT_EX, &data, error); return __sev_do_cmd_locked(SEV_CMD_INIT_EX, &data, error);
} }
static inline int __sev_do_init_locked(int *psp_ret)
{
if (sev_init_ex_buffer)
return __sev_init_ex_locked(psp_ret);
else
return __sev_init_locked(psp_ret);
}
static int __sev_platform_init_locked(int *error) static int __sev_platform_init_locked(int *error)
{ {
int rc = 0, psp_ret = SEV_RET_NO_FW_CALL;
struct psp_device *psp = psp_master; struct psp_device *psp = psp_master;
struct sev_device *sev; struct sev_device *sev;
int rc = 0, psp_ret = -1;
int (*init_function)(int *error);
if (!psp || !psp->sev_data) if (!psp || !psp->sev_data)
return -ENODEV; return -ENODEV;
@ -458,15 +465,12 @@ static int __sev_platform_init_locked(int *error)
return 0; return 0;
if (sev_init_ex_buffer) { if (sev_init_ex_buffer) {
init_function = __sev_init_ex_locked;
rc = sev_read_init_ex_file(); rc = sev_read_init_ex_file();
if (rc) if (rc)
return rc; return rc;
} else {
init_function = __sev_init_locked;
} }
rc = init_function(&psp_ret); rc = __sev_do_init_locked(&psp_ret);
if (rc && psp_ret == SEV_RET_SECURE_DATA_INVALID) { if (rc && psp_ret == SEV_RET_SECURE_DATA_INVALID) {
/* /*
* Initialization command returned an integrity check failure * Initialization command returned an integrity check failure
@ -475,9 +479,11 @@ static int __sev_platform_init_locked(int *error)
* initialization function should succeed by replacing the state * initialization function should succeed by replacing the state
* with a reset state. * with a reset state.
*/ */
dev_err(sev->dev, "SEV: retrying INIT command because of SECURE_DATA_INVALID error. Retrying once to reset PSP SEV state."); dev_err(sev->dev,
rc = init_function(&psp_ret); "SEV: retrying INIT command because of SECURE_DATA_INVALID error. Retrying once to reset PSP SEV state.");
rc = __sev_do_init_locked(&psp_ret);
} }
if (error) if (error)
*error = psp_ret; *error = psp_ret;

View File

@ -211,7 +211,7 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
ring_info->ring_buffer = (struct hv_ring_buffer *) ring_info->ring_buffer = (struct hv_ring_buffer *)
vmap_pfn(pfns_wraparound, page_cnt * 2 - 1, vmap_pfn(pfns_wraparound, page_cnt * 2 - 1,
PAGE_KERNEL); pgprot_decrypted(PAGE_KERNEL));
kfree(pfns_wraparound); kfree(pfns_wraparound);
if (!ring_info->ring_buffer) if (!ring_info->ring_buffer)

View File

@ -2156,7 +2156,6 @@ void vmbus_device_unregister(struct hv_device *device_obj)
* VMBUS is an acpi enumerated device. Get the information we * VMBUS is an acpi enumerated device. Get the information we
* need from DSDT. * need from DSDT.
*/ */
#define VTPM_BASE_ADDRESS 0xfed40000
static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx) static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
{ {
resource_size_t start = 0; resource_size_t start = 0;

View File

@ -46,7 +46,15 @@ struct snp_guest_dev {
void *certs_data; void *certs_data;
struct snp_guest_crypto *crypto; struct snp_guest_crypto *crypto;
/* request and response are in unencrypted memory */
struct snp_guest_msg *request, *response; struct snp_guest_msg *request, *response;
/*
* Avoid information leakage by double-buffering shared messages
* in fields that are in regular encrypted memory.
*/
struct snp_guest_msg secret_request, secret_response;
struct snp_secrets_page_layout *layout; struct snp_secrets_page_layout *layout;
struct snp_req_data input; struct snp_req_data input;
u32 *os_area_msg_seqno; u32 *os_area_msg_seqno;
@ -266,14 +274,17 @@ static int dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_msg *msg,
static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload, u32 sz) static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload, u32 sz)
{ {
struct snp_guest_crypto *crypto = snp_dev->crypto; struct snp_guest_crypto *crypto = snp_dev->crypto;
struct snp_guest_msg *resp = snp_dev->response; struct snp_guest_msg *resp = &snp_dev->secret_response;
struct snp_guest_msg *req = snp_dev->request; struct snp_guest_msg *req = &snp_dev->secret_request;
struct snp_guest_msg_hdr *req_hdr = &req->hdr; struct snp_guest_msg_hdr *req_hdr = &req->hdr;
struct snp_guest_msg_hdr *resp_hdr = &resp->hdr; struct snp_guest_msg_hdr *resp_hdr = &resp->hdr;
dev_dbg(snp_dev->dev, "response [seqno %lld type %d version %d sz %d]\n", dev_dbg(snp_dev->dev, "response [seqno %lld type %d version %d sz %d]\n",
resp_hdr->msg_seqno, resp_hdr->msg_type, resp_hdr->msg_version, resp_hdr->msg_sz); resp_hdr->msg_seqno, resp_hdr->msg_type, resp_hdr->msg_version, resp_hdr->msg_sz);
/* Copy response from shared memory to encrypted memory. */
memcpy(resp, snp_dev->response, sizeof(*resp));
/* Verify that the sequence counter is incremented by 1 */ /* Verify that the sequence counter is incremented by 1 */
if (unlikely(resp_hdr->msg_seqno != (req_hdr->msg_seqno + 1))) if (unlikely(resp_hdr->msg_seqno != (req_hdr->msg_seqno + 1)))
return -EBADMSG; return -EBADMSG;
@ -297,7 +308,7 @@ static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload,
static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8 type, static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8 type,
void *payload, size_t sz) void *payload, size_t sz)
{ {
struct snp_guest_msg *req = snp_dev->request; struct snp_guest_msg *req = &snp_dev->secret_request;
struct snp_guest_msg_hdr *hdr = &req->hdr; struct snp_guest_msg_hdr *hdr = &req->hdr;
memset(req, 0, sizeof(*req)); memset(req, 0, sizeof(*req));
@ -321,11 +332,12 @@ static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8
return __enc_payload(snp_dev, req, payload, sz); return __enc_payload(snp_dev, req, payload, sz);
} }
static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code, __u64 *fw_err) static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
struct snp_guest_request_ioctl *rio)
{ {
unsigned long err = 0xff, override_err = 0;
unsigned long req_start = jiffies; unsigned long req_start = jiffies;
unsigned int override_npages = 0; unsigned int override_npages = 0;
u64 override_err = 0;
int rc; int rc;
retry_request: retry_request:
@ -335,7 +347,7 @@ retry_request:
* sequence number must be incremented or the VMPCK must be deleted to * sequence number must be incremented or the VMPCK must be deleted to
* prevent reuse of the IV. * prevent reuse of the IV.
*/ */
rc = snp_issue_guest_request(exit_code, &snp_dev->input, &err); rc = snp_issue_guest_request(exit_code, &snp_dev->input, rio);
switch (rc) { switch (rc) {
case -ENOSPC: case -ENOSPC:
/* /*
@ -353,7 +365,7 @@ retry_request:
* request buffer size was too small and give the caller the * request buffer size was too small and give the caller the
* required buffer size. * required buffer size.
*/ */
override_err = SNP_GUEST_REQ_INVALID_LEN; override_err = SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN);
/* /*
* If this call to the firmware succeeds, the sequence number can * If this call to the firmware succeeds, the sequence number can
@ -366,7 +378,7 @@ retry_request:
goto retry_request; goto retry_request;
/* /*
* The host may return SNP_GUEST_REQ_ERR_EBUSY if the request has been * The host may return SNP_GUEST_VMM_ERR_BUSY if the request has been
* throttled. Retry in the driver to avoid returning and reusing the * throttled. Retry in the driver to avoid returning and reusing the
* message sequence number on a different message. * message sequence number on a different message.
*/ */
@ -387,27 +399,29 @@ retry_request:
*/ */
snp_inc_msg_seqno(snp_dev); snp_inc_msg_seqno(snp_dev);
if (fw_err) if (override_err) {
*fw_err = override_err ?: err; rio->exitinfo2 = override_err;
/*
* If an extended guest request was issued and the supplied certificate
* buffer was not large enough, a standard guest request was issued to
* prevent IV reuse. If the standard request was successful, return -EIO
* back to the caller as would have originally been returned.
*/
if (!rc && override_err == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
rc = -EIO;
}
if (override_npages) if (override_npages)
snp_dev->input.data_npages = override_npages; snp_dev->input.data_npages = override_npages;
/*
* If an extended guest request was issued and the supplied certificate
* buffer was not large enough, a standard guest request was issued to
* prevent IV reuse. If the standard request was successful, return -EIO
* back to the caller as would have originally been returned.
*/
if (!rc && override_err == SNP_GUEST_REQ_INVALID_LEN)
return -EIO;
return rc; return rc;
} }
static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code, int msg_ver, static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
u8 type, void *req_buf, size_t req_sz, void *resp_buf, struct snp_guest_request_ioctl *rio, u8 type,
u32 resp_sz, __u64 *fw_err) void *req_buf, size_t req_sz, void *resp_buf,
u32 resp_sz)
{ {
u64 seqno; u64 seqno;
int rc; int rc;
@ -417,19 +431,31 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code, in
if (!seqno) if (!seqno)
return -EIO; return -EIO;
/* Clear shared memory's response for the host to populate. */
memset(snp_dev->response, 0, sizeof(struct snp_guest_msg)); memset(snp_dev->response, 0, sizeof(struct snp_guest_msg));
/* Encrypt the userspace provided payload */ /* Encrypt the userspace provided payload in snp_dev->secret_request. */
rc = enc_payload(snp_dev, seqno, msg_ver, type, req_buf, req_sz); rc = enc_payload(snp_dev, seqno, rio->msg_version, type, req_buf, req_sz);
if (rc) if (rc)
return rc; return rc;
rc = __handle_guest_request(snp_dev, exit_code, fw_err); /*
* Write the fully encrypted request to the shared unencrypted
* request page.
*/
memcpy(snp_dev->request, &snp_dev->secret_request,
sizeof(snp_dev->secret_request));
rc = __handle_guest_request(snp_dev, exit_code, rio);
if (rc) { if (rc) {
if (rc == -EIO && *fw_err == SNP_GUEST_REQ_INVALID_LEN) if (rc == -EIO &&
rio->exitinfo2 == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
return rc; return rc;
dev_alert(snp_dev->dev, "Detected error from ASP request. rc: %d, fw_err: %llu\n", rc, *fw_err); dev_alert(snp_dev->dev,
"Detected error from ASP request. rc: %d, exitinfo2: 0x%llx\n",
rc, rio->exitinfo2);
snp_disable_vmpck(snp_dev); snp_disable_vmpck(snp_dev);
return rc; return rc;
} }
@ -469,9 +495,9 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
if (!resp) if (!resp)
return -ENOMEM; return -ENOMEM;
rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg->msg_version, rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg,
SNP_MSG_REPORT_REQ, &req, sizeof(req), resp->data, SNP_MSG_REPORT_REQ, &req, sizeof(req), resp->data,
resp_len, &arg->fw_err); resp_len);
if (rc) if (rc)
goto e_free; goto e_free;
@ -509,9 +535,8 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque
if (copy_from_user(&req, (void __user *)arg->req_data, sizeof(req))) if (copy_from_user(&req, (void __user *)arg->req_data, sizeof(req)))
return -EFAULT; return -EFAULT;
rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg->msg_version, rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg,
SNP_MSG_KEY_REQ, &req, sizeof(req), buf, resp_len, SNP_MSG_KEY_REQ, &req, sizeof(req), buf, resp_len);
&arg->fw_err);
if (rc) if (rc)
return rc; return rc;
@ -571,12 +596,12 @@ cmd:
return -ENOMEM; return -ENOMEM;
snp_dev->input.data_npages = npages; snp_dev->input.data_npages = npages;
ret = handle_guest_request(snp_dev, SVM_VMGEXIT_EXT_GUEST_REQUEST, arg->msg_version, ret = handle_guest_request(snp_dev, SVM_VMGEXIT_EXT_GUEST_REQUEST, arg,
SNP_MSG_REPORT_REQ, &req.data, SNP_MSG_REPORT_REQ, &req.data,
sizeof(req.data), resp->data, resp_len, &arg->fw_err); sizeof(req.data), resp->data, resp_len);
/* If certs length is invalid then copy the returned length */ /* If certs length is invalid then copy the returned length */
if (arg->fw_err == SNP_GUEST_REQ_INVALID_LEN) { if (arg->vmm_error == SNP_GUEST_VMM_ERR_INVALID_LEN) {
req.certs_len = snp_dev->input.data_npages << PAGE_SHIFT; req.certs_len = snp_dev->input.data_npages << PAGE_SHIFT;
if (copy_to_user((void __user *)arg->req_data, &req, sizeof(req))) if (copy_to_user((void __user *)arg->req_data, &req, sizeof(req)))
@ -611,7 +636,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
if (copy_from_user(&input, argp, sizeof(input))) if (copy_from_user(&input, argp, sizeof(input)))
return -EFAULT; return -EFAULT;
input.fw_err = 0xff; input.exitinfo2 = 0xff;
/* Message version must be non-zero */ /* Message version must be non-zero */
if (!input.msg_version) if (!input.msg_version)
@ -642,7 +667,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
mutex_unlock(&snp_cmd_mutex); mutex_unlock(&snp_cmd_mutex);
if (input.fw_err && copy_to_user(argp, &input, sizeof(input))) if (input.exitinfo2 && copy_to_user(argp, &input, sizeof(input)))
return -EFAULT; return -EFAULT;
return ret; return ret;

View File

@ -26,6 +26,8 @@
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include <asm/hyperv-tlfs.h> #include <asm/hyperv-tlfs.h>
#define VTPM_BASE_ADDRESS 0xfed40000
struct ms_hyperv_info { struct ms_hyperv_info {
u32 features; u32 features;
u32 priv_high; u32 priv_high;

View File

@ -36,6 +36,13 @@ enum {
* SEV Firmware status code * SEV Firmware status code
*/ */
typedef enum { typedef enum {
/*
* This error code is not in the SEV spec. Its purpose is to convey that
* there was an error that prevented the SEV firmware from being called.
* The SEV API error codes are 16 bits, so the -1 value will not overlap
* with possible values from the specification.
*/
SEV_RET_NO_FW_CALL = -1,
SEV_RET_SUCCESS = 0, SEV_RET_SUCCESS = 0,
SEV_RET_INVALID_PLATFORM_STATE, SEV_RET_INVALID_PLATFORM_STATE,
SEV_RET_INVALID_GUEST_STATE, SEV_RET_INVALID_GUEST_STATE,

View File

@ -52,8 +52,14 @@ struct snp_guest_request_ioctl {
__u64 req_data; __u64 req_data;
__u64 resp_data; __u64 resp_data;
/* firmware error code on failure (see psp-sev.h) */ /* bits[63:32]: VMM error code, bits[31:0] firmware error code (see psp-sev.h) */
__u64 fw_err; union {
__u64 exitinfo2;
struct {
__u32 fw_error;
__u32 vmm_error;
};
};
}; };
struct snp_ext_report_req { struct snp_ext_report_req {
@ -77,4 +83,12 @@ struct snp_ext_report_req {
/* Get SNP extended report as defined in the GHCB specification version 2. */ /* Get SNP extended report as defined in the GHCB specification version 2. */
#define SNP_GET_EXT_REPORT _IOWR(SNP_GUEST_REQ_IOC_TYPE, 0x2, struct snp_guest_request_ioctl) #define SNP_GET_EXT_REPORT _IOWR(SNP_GUEST_REQ_IOC_TYPE, 0x2, struct snp_guest_request_ioctl)
/* Guest message request EXIT_INFO_2 constants */
#define SNP_GUEST_FW_ERR_MASK GENMASK_ULL(31, 0)
#define SNP_GUEST_VMM_ERR_SHIFT 32
#define SNP_GUEST_VMM_ERR(x) (((u64)x) << SNP_GUEST_VMM_ERR_SHIFT)
#define SNP_GUEST_VMM_ERR_INVALID_LEN 1
#define SNP_GUEST_VMM_ERR_BUSY 2
#endif /* __UAPI_LINUX_SEV_GUEST_H_ */ #endif /* __UAPI_LINUX_SEV_GUEST_H_ */

View File

@ -1092,14 +1092,6 @@ asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
*/ */
locking_selftest(); locking_selftest();
/*
* This needs to be called before any devices perform DMA
* operations that might use the SWIOTLB bounce buffers. It will
* mark the bounce buffers as decrypted so that their usage will
* not cause "plain-text" data to be decrypted when accessed.
*/
mem_encrypt_init();
#ifdef CONFIG_BLK_DEV_INITRD #ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok && if (initrd_start && !initrd_below_start_ok &&
page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) { page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
@ -1116,6 +1108,17 @@ asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
late_time_init(); late_time_init();
sched_clock_init(); sched_clock_init();
calibrate_delay(); calibrate_delay();
/*
* This needs to be called before any devices perform DMA
* operations that might use the SWIOTLB bounce buffers. It will
* mark the bounce buffers as decrypted so that their usage will
* not cause "plain-text" data to be decrypted when accessed. It
* must be called after late_time_init() so that Hyper-V x86/x64
* hypercalls work when the SWIOTLB bounce buffers are decrypted.
*/
mem_encrypt_init();
pid_idr_init(); pid_idr_init();
anon_vma_init(); anon_vma_init();
#ifdef CONFIG_X86 #ifdef CONFIG_X86