Commit eb39e37d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'x86_sev_for_v5.19_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull AMD SEV-SNP support from Borislav Petkov:
 "The third AMD confidential computing feature called Secure Nested
  Paging.

  Add to confidential guests the necessary memory integrity protection
  against malicious hypervisor-based attacks like data replay, memory
  remapping and others, thus achieving a stronger isolation from the
  hypervisor.

  At the core of the functionality is a new structure called a reverse
  map table (RMP) with which the guest has a say in which pages get
  assigned to it and gets notified when a page which it owns, gets
  accessed/modified under the covers so that the guest can take an
  appropriate action.

  In addition, add support for the whole machinery needed to launch a
  SNP guest, details of which is properly explained in each patch.

  And last but not least, the series refactors and improves parts of the
  previous SEV support so that the new code is accomodated properly and
  not just bolted on"

* tag 'x86_sev_for_v5.19_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (60 commits)
  x86/entry: Fixup objtool/ibt validation
  x86/sev: Mark the code returning to user space as syscall gap
  x86/sev: Annotate stack change in the #VC handler
  x86/sev: Remove duplicated assignment to variable info
  x86/sev: Fix address space sparse warning
  x86/sev: Get the AP jump table address from secrets page
  x86/sev: Add missing __init annotations to SEV init routines
  virt: sevguest: Rename the sevguest dir and files to sev-guest
  virt: sevguest: Change driver name to reflect generic SEV support
  x86/boot: Put globals that are accessed early into the .data section
  x86/boot: Add an efi.h header for the decompressor
  virt: sevguest: Fix bool function returning negative value
  virt: sevguest: Fix return value check in alloc_shared_pages()
  x86/sev-es: Replace open-coded hlt-loop with sev_es_terminate()
  virt: sevguest: Add documentation for SEV-SNP CPUID Enforcement
  virt: sevguest: Add support to get extended report
  virt: sevguest: Add support to derive key
  virt: Add SEV-SNP guest driver
  x86/sev: Register SEV-SNP guest request platform device
  x86/sev: Provide support for SNP guest request NAEs
  ...
parents 0be3ff0c ce656528
......@@ -5383,6 +5383,8 @@
serialnumber [BUGS=X86-32]
sev=option[,option...] [X86-64] See Documentation/x86/x86_64/boot-options.rst
shapers= [NET]
Maximal number of shapers.
......
.. SPDX-License-Identifier: GPL-2.0
===================================================================
The Definitive SEV Guest API Documentation
===================================================================
1. General description
======================
The SEV API is a set of ioctls that are used by the guest or hypervisor
to get or set a certain aspect of the SEV virtual machine. The ioctls belong
to the following classes:
- Hypervisor ioctls: These query and set global attributes which affect the
whole SEV firmware. These ioctl are used by platform provisioning tools.
- Guest ioctls: These query and set attributes of the SEV virtual machine.
2. API description
==================
This section describes ioctls that is used for querying the SEV guest report
from the SEV firmware. For each ioctl, the following information is provided
along with a description:
Technology:
which SEV technology provides this ioctl. SEV, SEV-ES, SEV-SNP or all.
Type:
hypervisor or guest. The ioctl can be used inside the guest or the
hypervisor.
Parameters:
what parameters are accepted by the ioctl.
Returns:
the return value. General error numbers (-ENOMEM, -EINVAL)
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 ioctl accepts struct snp_user_guest_request. The input and output structure is
specified through the req_data and resp_data field respectively. If the ioctl fails
to execute due to a firmware error, then fw_err code will be set otherwise the
fw_err will be set to 0x00000000000000ff.
The firmware checks that the message sequence counter is one greater than
the guests message sequence counter. If guest driver fails to increment message
counter (e.g. counter overflow), then -EIO will be returned.
::
struct snp_guest_request_ioctl {
/* Message version number */
__u32 msg_version;
/* Request and response structure address */
__u64 req_data;
__u64 resp_data;
/* firmware error code on failure (see psp-sev.h) */
__u64 fw_err;
};
2.1 SNP_GET_REPORT
------------------
:Technology: sev-snp
:Type: guest ioctl
:Parameters (in): struct snp_report_req
:Returns (out): struct snp_report_resp on success, -negative on error
The SNP_GET_REPORT ioctl can be used to query the attestation report from the
SEV-SNP firmware. The ioctl uses the SNP_GUEST_REQUEST (MSG_REPORT_REQ) command
provided by the SEV-SNP firmware to query the attestation report.
On success, the snp_report_resp.data will contains the report. The report
contain the format described in the SEV-SNP specification. See the SEV-SNP
specification for further details.
2.2 SNP_GET_DERIVED_KEY
-----------------------
:Technology: sev-snp
:Type: guest ioctl
:Parameters (in): struct snp_derived_key_req
:Returns (out): struct snp_derived_key_resp on success, -negative on error
The SNP_GET_DERIVED_KEY ioctl can be used to get a key derive from a root key.
The derived key can be used by the guest for any purpose, such as sealing keys
or communicating with external entities.
The ioctl uses the SNP_GUEST_REQUEST (MSG_KEY_REQ) command provided by the
SEV-SNP firmware to derive the key. See SEV-SNP specification for further details
on the various fields passed in the key derivation request.
On success, the snp_derived_key_resp.data contains the derived key value. See
the SEV-SNP specification for further details.
2.3 SNP_GET_EXT_REPORT
----------------------
:Technology: sev-snp
:Type: guest ioctl
:Parameters (in/out): struct snp_ext_report_req
:Returns (out): struct snp_report_resp on success, -negative on error
The SNP_GET_EXT_REPORT ioctl is similar to the SNP_GET_REPORT. The difference is
related to the additional certificate data that is returned with the report.
The certificate data returned is being provided by the hypervisor through the
SNP_SET_EXT_CONFIG.
The ioctl uses the SNP_GUEST_REQUEST (MSG_REPORT_REQ) command provided by the SEV-SNP
firmware to get the attestation report.
On success, the snp_ext_report_resp.data will contain the attestation report
and snp_ext_report_req.certs_address will contain the certificate blob. If the
length of the blob is smaller than expected then snp_ext_report_req.certs_len will
be updated with the expected value.
See GHCB specification for further detail on how to parse the certificate blob.
3. SEV-SNP CPUID Enforcement
============================
SEV-SNP guests can access a special page that contains a table of CPUID values
that have been validated by the PSP as part of the SNP_LAUNCH_UPDATE firmware
command. It provides the following assurances regarding the validity of CPUID
values:
- Its address is obtained via bootloader/firmware (via CC blob), and those
binaries will be measured as part of the SEV-SNP attestation report.
- Its initial state will be encrypted/pvalidated, so attempts to modify
it during run-time will result in garbage being written, or #VC exceptions
being generated due to changes in validation state if the hypervisor tries
to swap the backing page.
- Attempts to bypass PSP checks by the hypervisor by using a normal page, or
a non-CPUID encrypted page will change the measurement provided by the
SEV-SNP attestation report.
- The CPUID page contents are *not* measured, but attempts to modify the
expected contents of a CPUID page as part of guest initialization will be
gated by the PSP CPUID enforcement policy checks performed on the page
during SNP_LAUNCH_UPDATE, and noticeable later if the guest owner
implements their own checks of the CPUID values.
It is important to note that this last assurance is only useful if the kernel
has taken care to make use of the SEV-SNP CPUID throughout all stages of boot.
Otherwise, guest owner attestation provides no assurance that the kernel wasn't
fed incorrect values at some point during boot.
Reference
---------
SEV-SNP and GHCB specification: developer.amd.com/sev
The driver is based on SEV-SNP firmware spec 0.9 and GHCB spec version 2.0.
......@@ -13,6 +13,7 @@ Linux Virtualization Support
guest-halt-polling
ne_overview
acrn/index
coco/sev-guest
.. only:: html and subproject
......
......@@ -310,3 +310,17 @@ Miscellaneous
Do not use GB pages for kernel direct mappings.
gbpages
Use GB pages for kernel direct mappings.
AMD SEV (Secure Encrypted Virtualization)
=========================================
Options relating to AMD SEV, specified via the following format:
::
sev=option1[,option2]
The available options are:
debug
Enable debug messages.
......@@ -19,6 +19,7 @@ Offset/Size Proto Name Meaning
058/008 ALL tboot_addr Physical address of tboot shared page
060/010 ALL ist_info Intel SpeedStep (IST) BIOS support information
(struct ist_info)
070/008 ALL acpi_rsdp_addr Physical address of ACPI RSDP table
080/010 ALL hd0_info hd0 disk parameter, OBSOLETE!!
090/010 ALL hd1_info hd1 disk parameter, OBSOLETE!!
0A0/010 ALL sys_desc_table System description table (struct sys_desc_table),
......@@ -27,6 +28,7 @@ Offset/Size Proto Name Meaning
0C0/004 ALL ext_ramdisk_image ramdisk_image high 32bits
0C4/004 ALL ext_ramdisk_size ramdisk_size high 32bits
0C8/004 ALL ext_cmd_line_ptr cmd_line_ptr high 32bits
13C/004 ALL cc_blob_address Physical address of Confidential Computing blob
140/080 ALL edid_info Video mode setup (struct edid_info)
1C0/020 ALL efi_info EFI 32 information (struct efi_info)
1E0/004 ALL alt_mem_k Alternative mem check, in KB
......
......@@ -103,6 +103,7 @@ endif
vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o
vmlinux-objs-$(CONFIG_EFI) += $(obj)/efi.o
efi-obj-$(CONFIG_EFI_STUB) = $(objtree)/drivers/firmware/efi/libstub/lib.a
$(obj)/vmlinux: $(vmlinux-objs-y) $(efi-obj-y) FORCE
......
......@@ -3,10 +3,9 @@
#include "misc.h"
#include "error.h"
#include "../string.h"
#include "efi.h"
#include <linux/numa.h>
#include <linux/efi.h>
#include <asm/efi.h>
/*
* Longest parameter of 'acpi=' is 'copy_dsdt', plus an extra '\0'
......@@ -20,153 +19,56 @@
*/
struct mem_vector immovable_mem[MAX_NUMNODES*2];
/*
* Search EFI system tables for RSDP. If both ACPI_20_TABLE_GUID and
* ACPI_TABLE_GUID are found, take the former, which has more features.
*/
static acpi_physical_address
__efi_get_rsdp_addr(unsigned long config_tables, unsigned int nr_tables,
bool efi_64)
__efi_get_rsdp_addr(unsigned long cfg_tbl_pa, unsigned int cfg_tbl_len)
{
acpi_physical_address rsdp_addr = 0;
#ifdef CONFIG_EFI
int i;
/* Get EFI tables from systab. */
for (i = 0; i < nr_tables; i++) {
acpi_physical_address table;
efi_guid_t guid;
if (efi_64) {
efi_config_table_64_t *tbl = (efi_config_table_64_t *)config_tables + i;
guid = tbl->guid;
table = tbl->table;
if (!IS_ENABLED(CONFIG_X86_64) && table >> 32) {
debug_putstr("Error getting RSDP address: EFI config table located above 4GB.\n");
return 0;
}
} else {
efi_config_table_32_t *tbl = (efi_config_table_32_t *)config_tables + i;
guid = tbl->guid;
table = tbl->table;
}
unsigned long rsdp_addr;
int ret;
if (!(efi_guidcmp(guid, ACPI_TABLE_GUID)))
rsdp_addr = table;
else if (!(efi_guidcmp(guid, ACPI_20_TABLE_GUID)))
return table;
}
/*
* Search EFI system tables for RSDP. Preferred is ACPI_20_TABLE_GUID to
* ACPI_TABLE_GUID because it has more features.
*/
rsdp_addr = efi_find_vendor_table(boot_params, cfg_tbl_pa, cfg_tbl_len,
ACPI_20_TABLE_GUID);
if (rsdp_addr)
return (acpi_physical_address)rsdp_addr;
/* No ACPI_20_TABLE_GUID found, fallback to ACPI_TABLE_GUID. */
rsdp_addr = efi_find_vendor_table(boot_params, cfg_tbl_pa, cfg_tbl_len,
ACPI_TABLE_GUID);
if (rsdp_addr)
return (acpi_physical_address)rsdp_addr;
debug_putstr("Error getting RSDP address.\n");
#endif
return rsdp_addr;
}
/* EFI/kexec support is 64-bit only. */
#ifdef CONFIG_X86_64
static struct efi_setup_data *get_kexec_setup_data_addr(void)
{
struct setup_data *data;
u64 pa_data;
pa_data = boot_params->hdr.setup_data;
while (pa_data) {
data = (struct setup_data *)pa_data;
if (data->type == SETUP_EFI)
return (struct efi_setup_data *)(pa_data + sizeof(struct setup_data));
pa_data = data->next;
}
return NULL;
}
static acpi_physical_address kexec_get_rsdp_addr(void)
{
efi_system_table_64_t *systab;
struct efi_setup_data *esd;
struct efi_info *ei;
char *sig;
esd = (struct efi_setup_data *)get_kexec_setup_data_addr();
if (!esd)
return 0;
if (!esd->tables) {
debug_putstr("Wrong kexec SETUP_EFI data.\n");
return 0;
}
ei = &boot_params->efi_info;
sig = (char *)&ei->efi_loader_signature;
if (strncmp(sig, EFI64_LOADER_SIGNATURE, 4)) {
debug_putstr("Wrong kexec EFI loader signature.\n");
return 0;
}
/* Get systab from boot params. */
systab = (efi_system_table_64_t *) (ei->efi_systab | ((__u64)ei->efi_systab_hi << 32));
if (!systab)
error("EFI system table not found in kexec boot_params.");
return __efi_get_rsdp_addr((unsigned long)esd->tables, systab->nr_tables, true);
return 0;
}
#else
static acpi_physical_address kexec_get_rsdp_addr(void) { return 0; }
#endif /* CONFIG_X86_64 */
static acpi_physical_address efi_get_rsdp_addr(void)
{
#ifdef CONFIG_EFI
unsigned long systab, config_tables;
unsigned long cfg_tbl_pa = 0;
unsigned int cfg_tbl_len;
unsigned long systab_pa;
unsigned int nr_tables;
struct efi_info *ei;
bool efi_64;
char *sig;
ei = &boot_params->efi_info;
sig = (char *)&ei->efi_loader_signature;
if (!strncmp(sig, EFI64_LOADER_SIGNATURE, 4)) {
efi_64 = true;
} else if (!strncmp(sig, EFI32_LOADER_SIGNATURE, 4)) {
efi_64 = false;
} else {
debug_putstr("Wrong EFI loader signature.\n");
return 0;
}
enum efi_type et;
int ret;
/* Get systab from boot params. */
#ifdef CONFIG_X86_64
systab = ei->efi_systab | ((__u64)ei->efi_systab_hi << 32);
#else
if (ei->efi_systab_hi || ei->efi_memmap_hi) {
debug_putstr("Error getting RSDP address: EFI system table located above 4GB.\n");
et = efi_get_type(boot_params);
if (et == EFI_TYPE_NONE)
return 0;
}
systab = ei->efi_systab;
#endif
if (!systab)
error("EFI system table not found.");
/* Handle EFI bitness properly */
if (efi_64) {
efi_system_table_64_t *stbl = (efi_system_table_64_t *)systab;
systab_pa = efi_get_system_table(boot_params);
if (!systab_pa)
error("EFI support advertised, but unable to locate system table.");
config_tables = stbl->tables;
nr_tables = stbl->nr_tables;
} else {
efi_system_table_32_t *stbl = (efi_system_table_32_t *)systab;
ret = efi_get_conf_table(boot_params, &cfg_tbl_pa, &cfg_tbl_len);
if (ret || !cfg_tbl_pa)
error("EFI config table not found.");
config_tables = stbl->tables;
nr_tables = stbl->nr_tables;
}
if (!config_tables)
error("EFI config tables not found.");
return __efi_get_rsdp_addr(config_tables, nr_tables, efi_64);
return __efi_get_rsdp_addr(cfg_tbl_pa, cfg_tbl_len);
#else
return 0;
#endif
......@@ -256,14 +158,6 @@ acpi_physical_address get_rsdp_addr(void)
pa = boot_params->acpi_rsdp_addr;
/*
* Try to get EFI data from setup_data. This can happen when we're a
* kexec'ed kernel and kexec(1) has passed all the required EFI info to
* us.
*/
if (!pa)
pa = kexec_get_rsdp_addr();
if (!pa)
pa = efi_get_rsdp_addr();
......
#include "misc.h"
int early_serial_base;
/* This might be accessed before .bss is cleared, so use .data instead. */
int early_serial_base __section(".data");
#include "../early_serial_console.c"
// SPDX-License-Identifier: GPL-2.0
/*
* Helpers for early access to EFI configuration table.
*
* Originally derived from arch/x86/boot/compressed/acpi.c
*/
#include "misc.h"
/**
* efi_get_type - Given a pointer to boot_params, determine the type of EFI environment.
*
* @bp: pointer to boot_params
*
* Return: EFI_TYPE_{32,64} for valid EFI environments, EFI_TYPE_NONE otherwise.
*/
enum efi_type efi_get_type(struct boot_params *bp)
{
struct efi_info *ei;
enum efi_type et;
const char *sig;
ei = &bp->efi_info;
sig = (char *)&ei->efi_loader_signature;
if (!strncmp(sig, EFI64_LOADER_SIGNATURE, 4)) {
et = EFI_TYPE_64;
} else if (!strncmp(sig, EFI32_LOADER_SIGNATURE, 4)) {
et = EFI_TYPE_32;
} else {
debug_putstr("No EFI environment detected.\n");
et = EFI_TYPE_NONE;
}
#ifndef CONFIG_X86_64
/*
* Existing callers like acpi.c treat this case as an indicator to
* fall-through to non-EFI, rather than an error, so maintain that
* functionality here as well.
*/
if (ei->efi_systab_hi || ei->efi_memmap_hi) {
debug_putstr("EFI system table is located above 4GB and cannot be accessed.\n");
et = EFI_TYPE_NONE;
}
#endif
return et;
}
/**
* efi_get_system_table - Given a pointer to boot_params, retrieve the physical address
* of the EFI system table.
*
* @bp: pointer to boot_params
*
* Return: EFI system table address on success. On error, return 0.
*/
unsigned long efi_get_system_table(struct boot_params *bp)
{
unsigned long sys_tbl_pa;
struct efi_info *ei;
enum efi_type et;
/* Get systab from boot params. */
ei = &bp->efi_info;
#ifdef CONFIG_X86_64
sys_tbl_pa = ei->efi_systab | ((__u64)ei->efi_systab_hi << 32);
#else
sys_tbl_pa = ei->efi_systab;
#endif
if (!sys_tbl_pa) {
debug_putstr("EFI system table not found.");
return 0;
}
return sys_tbl_pa;
}
/*
* EFI config table address changes to virtual address after boot, which may
* not be accessible for the kexec'd kernel. To address this, kexec provides
* the initial physical address via a struct setup_data entry, which is
* checked for here, along with some sanity checks.
*/
static struct efi_setup_data *get_kexec_setup_data(struct boot_params *bp,
enum efi_type et)
{
#ifdef CONFIG_X86_64
struct efi_setup_data *esd = NULL;
struct setup_data *data;
u64 pa_data;
pa_data = bp->hdr.setup_data;
while (pa_data) {
data = (struct setup_data *)pa_data;
if (data->type == SETUP_EFI) {
esd = (struct efi_setup_data *)(pa_data + sizeof(struct setup_data));
break;
}
pa_data = data->next;
}
/*
* Original ACPI code falls back to attempting normal EFI boot in these
* cases, so maintain existing behavior by indicating non-kexec
* environment to the caller, but print them for debugging.
*/
if (esd && !esd->tables) {
debug_putstr("kexec EFI environment missing valid configuration table.\n");
return NULL;
}
return esd;
#endif
return NULL;
}
/**
* efi_get_conf_table - Given a pointer to boot_params, locate and return the physical
* address of EFI configuration table.
*
* @bp: pointer to boot_params
* @cfg_tbl_pa: location to store physical address of config table
* @cfg_tbl_len: location to store number of config table entries
*
* Return: 0 on success. On error, return params are left unchanged.
*/
int efi_get_conf_table(struct boot_params *bp, unsigned long *cfg_tbl_pa,
unsigned int *cfg_tbl_len)
{
unsigned long sys_tbl_pa;
enum efi_type et;
int ret;
if (!cfg_tbl_pa || !cfg_tbl_len)
return -EINVAL;
sys_tbl_pa = efi_get_system_table(bp);
if (!sys_tbl_pa)
return -EINVAL;
/* Handle EFI bitness properly */
et = efi_get_type(bp);
if (et == EFI_TYPE_64) {
efi_system_table_64_t *stbl = (efi_system_table_64_t *)sys_tbl_pa;
struct efi_setup_data *esd;
/* kexec provides an alternative EFI conf table, check for it. */
esd = get_kexec_setup_data(bp, et);
*cfg_tbl_pa = esd ? esd->tables : stbl->tables;
*cfg_tbl_len = stbl->nr_tables;
} else if (et == EFI_TYPE_32) {
efi_system_table_32_t *stbl = (efi_system_table_32_t *)sys_tbl_pa;
*cfg_tbl_pa = stbl->tables;
*cfg_tbl_len = stbl->nr_tables;
} else {
return -EINVAL;
}
return 0;
}
/* Get vendor table address/guid from EFI config table at the given index */
static int get_vendor_table(void *cfg_tbl, unsigned int idx,
unsigned long *vendor_tbl_pa,
efi_guid_t *vendor_tbl_guid,
enum efi_type et)
{
if (et == EFI_TYPE_64) {
efi_config_table_64_t *tbl_entry = (efi_config_table_64_t *)cfg_tbl + idx;
if (!IS_ENABLED(CONFIG_X86_64) && tbl_entry->table >> 32) {
debug_putstr("Error: EFI config table entry located above 4GB.\n");
return -EINVAL;
}
*vendor_tbl_pa = tbl_entry->table;
*vendor_tbl_guid = tbl_entry->guid;
} else if (et == EFI_TYPE_32) {
efi_config_table_32_t *tbl_entry = (efi_config_table_32_t *)cfg_tbl + idx;
*vendor_tbl_pa = tbl_entry->table;
*vendor_tbl_guid = tbl_entry->guid;
} else {
return -EINVAL;
}
return 0;
}
/**
* efi_find_vendor_table - Given EFI config table, search it for the physical
* address of the vendor table associated with GUID.
*
* @bp: pointer to boot_params
* @cfg_tbl_pa: pointer to EFI configuration table
* @cfg_tbl_len: number of entries in EFI configuration table
* @guid: GUID of vendor table
*
* Return: vendor table address on success. On error, return 0.
*/
unsigned long efi_find_vendor_table(struct boot_params *bp,
unsigned long cfg_tbl_pa,
unsigned int cfg_tbl_len,
efi_guid_t guid)
{
enum efi_type et;
unsigned int i;
et = efi_get_type(bp);
if (et == EFI_TYPE_NONE)
return 0;
for (i = 0; i < cfg_tbl_len; i++) {
unsigned long vendor_tbl_pa;
efi_guid_t vendor_tbl_guid;
int ret;
ret = get_vendor_table((void *)cfg_tbl_pa, i,
&vendor_tbl_pa,
&vendor_tbl_guid, et);
if (ret)
return 0;
if (!efi_guidcmp(guid, vendor_tbl_guid))
return vendor_tbl_pa;
}
return 0;
}
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef BOOT_COMPRESSED_EFI_H
#define BOOT_COMPRESSED_EFI_H
#if defined(_LINUX_EFI_H) || defined(_ASM_X86_EFI_H)
#error Please do not include kernel proper namespace headers
#endif
typedef guid_t efi_guid_t __aligned(__alignof__(u32));
#define EFI_GUID(a, b, c, d...) (efi_guid_t){ { \
(a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
(b) & 0xff, ((b) >> 8) & 0xff, \
(c) & 0xff, ((c) >> 8) & 0xff, d } }
#define ACPI_TABLE_GUID EFI_GUID(0xeb9d2d30, 0x2d88, 0x11d3, 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
#define ACPI_20_TABLE_GUID EFI_GUID(0x8868e871, 0xe4f1, 0x11d3, 0xbc, 0x22, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81)
#define EFI_CC_BLOB_GUID EFI_GUID(0x067b1f5f, 0xcf26, 0x44c5, 0x85, 0x54, 0x93, 0xd7, 0x77, 0x91, 0x2d, 0x42)
#define EFI32_LOADER_SIGNATURE "EL32"
#define EFI64_LOADER_SIGNATURE "EL64"
/*
* Generic EFI table header
*/
typedef struct {
u64 signature;
u32 revision;
u32 headersize;
u32 crc32;
u32 reserved;
} efi_table_hdr_t;
#define EFI_CONVENTIONAL_MEMORY 7
#define EFI_MEMORY_MORE_RELIABLE \
((u64)0x0000000000010000ULL) /* higher reliability */
#define EFI_MEMORY_SP ((u64)0x0000000000040000ULL) /* soft reserved */
#define EFI_PAGE_SHIFT 12
typedef struct {
u32 type;
u32 pad;
u64 phys_addr;
u64 virt_addr;
u64 num_pages;
u64 attribute;
} efi_memory_desc_t;
#define efi_early_memdesc_ptr(map, desc_size, n) \
(efi_memory_desc_t *)((void *)(map) + ((n) * (desc_size)))
typedef struct {
efi_guid_t guid;
u64 table;
} efi_config_table_64_t;
typedef struct {
efi_guid_t guid;
u32 table;
} efi_config_table_32_t;
typedef struct {
efi_table_hdr_t hdr;
u64 fw_vendor; /* physical addr of CHAR16 vendor string */
u32 fw_revision;
u32 __pad1;
u64 con_in_handle;
u64 con_in;
u64 con_out_handle;
u64 con_out;
u64 stderr_handle;
u64 stderr;
u64 runtime;
u64 boottime;
u32 nr_tables;
u32 __pad2;
u64 tables;
} efi_system_table_64_t;
typedef struct {
efi_table_hdr_t hdr;
u32 fw_vendor; /* physical addr of CHAR16 vendor string */
u32 fw_revision;
u32 con_in_handle;
u32 con_in;
u32 con_out_handle;
u32 con_out;
u32 stderr_handle;
u32 stderr;
u32 runtime;
u32 boottime;
u32 nr_tables;
u32 tables;
} efi_system_table_32_t;
/* kexec external ABI */
struct efi_setup_data {
u64 fw_vendor;
u64 __unused;
u64 tables;
u64 smbios;
u64 reserved[8];
};
static inline int efi_guidcmp (efi_guid_t left, efi_guid_t right)
{
return memcmp(&left, &right, sizeof (efi_guid_t));
}
#ifdef CONFIG_EFI
bool __pure __efi_soft_reserve_enabled(void);
static inline bool __pure efi_soft_reserve_enabled(void)
{
return IS_ENABLED(CONFIG_EFI_SOFT_RESERVE)
&& __efi_soft_reserve_enabled();
}
#else
static inline bool efi_soft_reserve_enabled(void)
{
return false;
}
#endif /* CONFIG_EFI */
#endif /* BOOT_COMPRESSED_EFI_H */
......@@ -189,11 +189,11 @@ SYM_FUNC_START(startup_32)
subl $32, %eax /* Encryption bit is always above bit 31 */
bts %eax, %edx /* Set encryption mask for page tables */
/*
* Mark SEV as active in sev_status so that startup32_check_sev_cbit()
* will do a check. The sev_status memory will be fully initialized
* with the contents of MSR_AMD_SEV_STATUS later in
* set_sev_encryption_mask(). For now it is sufficient to know that SEV
* is active.
* Set MSR_AMD64_SEV_ENABLED_BIT in sev_status so that
* startup32_check_sev_cbit() will do a check. sev_enable() will
* initialize sev_status with all the bits reported by
* MSR_AMD_SEV_STATUS later, but only MSR_AMD64_SEV_ENABLED_BIT
* needs to be set for now.
*/
movl $1, rva(sev_status)(%ebp)
1:
......@@ -447,6 +447,23 @@ SYM_CODE_START(startup_64)
call load_stage1_idt
popq %rsi
#ifdef CONFIG_AMD_MEM_ENCRYPT
/*
* Now that the stage1 interrupt handlers are set up, #VC exceptions from
* CPUID instructions can be properly handled for SEV-ES guests.
*
* For SEV-SNP, the CPUID table also needs to be set up in advance of any
* CPUID instructions being issued, so go ahead and do that now via
* sev_enable(), which will also handle the rest of the SEV-related
* detection/setup to ensure that has been done in advance of any dependent
* code.
*/
pushq %rsi
movq %rsi, %rdi /* real mode address */
call sev_enable
popq %rsi
#endif
/*
* paging_prepare() sets up the trampoline and checks if we need to
* enable 5-level paging.
......@@ -558,17 +575,7 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
shrq $3, %rcx
rep stosq
/*
* If running as an SEV guest, the encryption mask is required in the
* page-table setup code below. When the guest also has SEV-ES enabled
* set_sev_encryption_mask() will cause #VC exceptions, but the stage2
* handler can't map its GHCB because the page-table is not set up yet.
* So set up the encryption mask here while still on the stage1 #VC
* handler. Then load stage2 IDT and switch to the kernel's own
* page-table.
*/
pushq %rsi
call set_sev_encryption_mask
call load_stage2_idt
/* Pass boot_params to initialize_identity_maps() */
......
......@@ -90,7 +90,7 @@ static struct x86_mapping_info mapping_info;
/*
* Adds the specified range to the identity mappings.
*/
static void add_identity_map(unsigned long start, unsigned long end)
void kernel_add_identity_map(unsigned long start, unsigned long end)
{
int ret;
......@@ -157,14 +157,15 @@ void initialize_identity_maps(void *rmode)
* explicitly here in case the compressed kernel does not touch them,
* or does not touch all the pages covering them.
*/
add_identity_map((unsigned long)_head, (unsigned long)_end);
kernel_add_identity_map((unsigned long)_head, (unsigned long)_end);
boot_params = rmode;
add_identity_map((unsigned long)boot_params, (unsigned long)(boot_params + 1));
kernel_add_identity_map((unsigned long)boot_params, (unsigned long)(boot_params + 1));
cmdline = get_cmd_line_ptr();
add_identity_map(cmdline, cmdline + COMMAND_LINE_SIZE);
kernel_add_identity_map(cmdline, cmdline + COMMAND_LINE_SIZE);
sev_prep_identity_maps(top_level_pgt);
/* Load the new page-table. */
sev_verify_cbit(top_level_pgt);
write_cr3(top_level_pgt);
}
......@@ -246,10 +247,10 @@ static int set_clr_page_flags(struct x86_mapping_info *info,
* It should already exist, but keep things generic.
*
* To map the page just read from it and fault it in if there is no
* mapping yet. add_identity_map() can't be called here because that
* would unconditionally map the address on PMD level, destroying any
* PTE-level mappings that might already exist. Use assembly here so
* the access won't be optimized away.
* mapping yet. kernel_add_identity_map() can't be called here because
* that would unconditionally map the address on PMD level, destroying
* any PTE-level mappings that might already exist. Use assembly here
* so the access won't be optimized away.
*/
asm volatile("mov %[address], %%r9"
:: [address] "g" (*(unsigned long *)address)
......@@ -275,15 +276,31 @@ static int set_clr_page_flags(struct x86_mapping_info *info,
* Changing encryption attributes of a page requires to flush it from
* the caches.
*/
if ((set | clr) & _PAGE_ENC)
if ((set | clr) & _PAGE_ENC) {
clflush_page(address);
/*
* If the encryption attribute is being cleared, change the page state
* to shared in the RMP table.
*/
if (clr)
snp_set_page_shared(__pa(address & PAGE_MASK));
}
/* Update PTE */
pte = *ptep;
pte = pte_set_flags(pte, set);
pte = pte_clear_flags(pte, clr);
set_pte(ptep, pte);
/*
* If the encryption attribute is being set, then change the page state to
* private in the RMP entry. The page state change must be done after the PTE
* is updated.
*/
if (set & _PAGE_ENC)
snp_set_page_private(__pa(address & PAGE_MASK));
/* Flush TLB after changing encryption attribute */
write_cr3(top_level_pgt);
......@@ -347,5 +364,5 @@ void do_boot_page_fault(struct pt_regs *regs, unsigned long error_code)
* Error code is sane - now identity map the 2M region around
* the faulting address.
*/
add_identity_map(address, end);
kernel_add_identity_map(address, end);
}
......@@ -39,7 +39,23 @@ void load_stage1_idt(void)
load_boot_idt(&boot_idt_desc);
}
/* Setup IDT after kernel jumping to .Lrelocated */
/*
* Setup IDT after kernel jumping to .Lrelocated.
*
* initialize_identity_maps() needs a #PF handler to be setup
* in order to be able to fault-in identity mapping ranges; see
* do_boot_page_fault().
*
* This #PF handler setup needs to happen in load_stage2_idt() where the
* IDT is loaded and there the #VC IDT entry gets setup too.
*
* In order to be able to handle #VCs, one needs a GHCB which
* gets setup with an already set up pagetable, which is done in
* initialize_identity_maps(). And there's the catch 22: the boot #VC
* handler do_boot_stage2_vc() needs to call early_setup_ghcb() itself
* (and, especially set_page_decrypted()) because the SEV-ES setup code
* cannot initialize a GHCB as there's no #PF handler yet...
*/
void load_stage2_idt(void)
{
boot_idt_desc.address = (unsigned long)boot_idt;
......
......@@ -22,15 +22,14 @@
#include "misc.h"
#include "error.h"
#include "../string.h"
#include "efi.h"
#include <generated/compile.h>
#include <linux/module.h>
#include <linux/uts.h>
#include <linux/utsname.h>
#include <linux/ctype.h>
#include <linux/efi.h>
#include <generated/utsrelease.h>
#include <asm/efi.h>
#define _SETUP
#include <asm/setup.h> /* For COMMAND_LINE_SIZE */
......
......@@ -187,42 +187,6 @@ SYM_CODE_END(startup32_vc_handler)
.code64
#include "../../kernel/sev_verify_cbit.S"
SYM_FUNC_START(set_sev_encryption_mask)
#ifdef CONFIG_AMD_MEM_ENCRYPT
push %rbp
push %rdx
movq %rsp, %rbp /* Save current stack pointer */
call get_sev_encryption_bit /* Get the encryption bit position */
testl %eax, %eax
jz .Lno_sev_mask
bts %rax, sme_me_mask(%rip) /* Create the encryption mask */
/*
* Read MSR_AMD64_SEV again and store it to sev_status. Can't do this in
* get_sev_encryption_bit() because this function is 32-bit code and
* shared between 64-bit and 32-bit boot path.
*/
movl $MSR_AMD64_SEV, %ecx /* Read the SEV MSR */
rdmsr
/* Store MSR value in sev_status */
shlq $32, %rdx
orq %rdx, %rax
movq %rax, sev_status(%rip)
.Lno_sev_mask:
movq %rbp, %rsp /* Restore original stack pointer */
pop %rdx
pop %rbp
#endif
xor %rax, %rax
RET
SYM_FUNC_END(set_sev_encryption_mask)
.data
......
......@@ -53,7 +53,10 @@ memptr free_mem_end_ptr;
static char *vidmem;
static int vidport;
static int lines, cols;
/* These might be accessed before .bss is cleared, so use .data instead. */
static int lines __section(".data");
static int cols __section(".data");
#ifdef CONFIG_KERNEL_GZIP
#include "../../../../lib/decompress_inflate.c"
......
......@@ -34,6 +34,8 @@
#define BOOT_BOOT_H
#include "../ctype.h"
#include "efi.h"
#ifdef CONFIG_X86_64
#define memptr long
#else
......@@ -120,17 +122,23 @@ static inline void console_init(void)
{ }
#endif
void set_sev_encryption_mask(void);
#ifdef CONFIG_AMD_MEM_ENCRYPT
void sev_enable(struct boot_params *bp);
void sev_es_shutdown_ghcb(void);
extern bool sev_es_check_ghcb_fault(unsigned long address);
void snp_set_page_private(unsigned long paddr);
void snp_set_page_shared(unsigned long paddr);
void sev_prep_identity_maps(unsigned long top_level_pgt);
#else
static inline void sev_enable(struct boot_params *bp) { }
static inline void sev_es_shutdown_ghcb(void) { }
static inline bool sev_es_check_ghcb_fault(unsigned long address)
{
return false;
}
static inline void snp_set_page_private(unsigned long paddr) { }
static inline void snp_set_page_shared(unsigned long paddr) { }
static inline void sev_prep_identity_maps(unsigned long top_level_pgt) { }
#endif
/* acpi.c */
......@@ -151,6 +159,7 @@ static inline int count_immovable_mem_regions(void) { return 0; }
#ifdef CONFIG_X86_5LEVEL
extern unsigned int __pgtable_l5_enabled, pgdir_shift, ptrs_per_p4d;
#endif
extern void kernel_add_identity_map(unsigned long start, unsigned long end);
/* Used by PAGE_KERN* macros: */
extern pteval_t __default_kernel_pte_mask;
......@@ -172,4 +181,47 @@ void boot_stage2_vc(void);
unsigned long sev_verify_cbit(unsigned long cr3);
enum efi_type {
EFI_TYPE_64,
EFI_TYPE_32,
EFI_TYPE_NONE,
};
#ifdef CONFIG_EFI
/* helpers for early EFI config table access */
enum efi_type efi_get_type(struct boot_params *bp);
unsigned long efi_get_system_table(struct boot_params *bp);
int efi_get_conf_table(struct boot_params *bp, unsigned long *cfg_tbl_pa,
unsigned int *cfg_tbl_len);
unsigned long efi_find_vendor_table(struct boot_params *bp,
unsigned long cfg_tbl_pa,
unsigned int cfg_tbl_len,
efi_guid_t guid);
#else
static inline enum efi_type efi_get_type(struct boot_params *bp)
{
return EFI_TYPE_NONE;
}
static inline unsigned long efi_get_system_table(struct boot_params *bp)
{
return 0;
}
static inline int efi_get_conf_table(struct boot_params *bp,
unsigned long *cfg_tbl_pa,
unsigned int *cfg_tbl_len)
{
return -ENOENT;
}
static inline unsigned long efi_find_vendor_table(struct boot_params *bp,
unsigned long cfg_tbl_pa,
unsigned int cfg_tbl_len,
efi_guid_t guid)
{
return 0;
}
#endif /* CONFIG_EFI */
#endif /* BOOT_COMPRESSED_MISC_H */
// SPDX-License-Identifier: GPL-2.0
#include "misc.h"
#include <linux/efi.h>
#include <asm/e820/types.h>
#include <asm/processor.h>
#include <asm/efi.h>
#include "pgtable.h"
#include "../string.h"
#include "efi.h"
#define BIOS_START_MIN 0x20000U /* 128K, less than this is insane */
#define BIOS_START_MAX 0x9f000U /* 640K, absolute maximum */
......
......@@ -20,8 +20,10 @@
#include <asm/fpu/xcr.h>
#include <asm/ptrace.h>
#include <asm/svm.h>
#include <asm/cpuid.h>
#include "error.h"
#include "../msr.h"
struct ghcb boot_ghcb_page __aligned(PAGE_SIZE);
struct ghcb *boot_ghcb;
......@@ -56,23 +58,19 @@ static unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx)
static inline u64 sev_es_rd_ghcb_msr(void)
{
unsigned long low, high;
struct msr m;
asm volatile("rdmsr" : "=a" (low), "=d" (high) :
"c" (MSR_AMD64_SEV_ES_GHCB));
boot_rdmsr(MSR_AMD64_SEV_ES_GHCB, &m);
return ((high << 32) | low);
return m.q;
}
static inline void sev_es_wr_ghcb_msr(u64 val)
{
u32 low, high;
struct msr m;
low = val & 0xffffffffUL;
high = val >> 32;
asm volatile("wrmsr" : : "c" (MSR_AMD64_SEV_ES_GHCB),
"a"(low), "d" (high) : "memory");
m.q = val;
boot_wrmsr(MSR_AMD64_SEV_ES_GHCB, &m);
}
static enum es_result vc_decode_insn(struct es_em_ctxt *ctxt)
......@@ -119,11 +117,54 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
/* Include code for early handlers */
#include "../../kernel/sev-shared.c"
static bool early_setup_sev_es(void)
static inline bool sev_snp_enabled(void)
{
return sev_status & MSR_AMD64_SEV_SNP_ENABLED;
}
static void __page_state_change(unsigned long paddr, enum psc_op op)
{
u64 val;
if (!sev_snp_enabled())
return;
/*
* If private -> shared then invalidate the page before requesting the
* state change in the RMP table.
*/
if (op == SNP_PAGE_STATE_SHARED && pvalidate(paddr, RMP_PG_SIZE_4K, 0))
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PVALIDATE);
/* Issue VMGEXIT to change the page state in RMP table. */
sev_es_wr_ghcb_msr(GHCB_MSR_PSC_REQ_GFN(paddr >> PAGE_SHIFT, op));
VMGEXIT();
/* Read the response of the VMGEXIT. */
val = sev_es_rd_ghcb_msr();
if ((GHCB_RESP_CODE(val) != GHCB_MSR_PSC_RESP) || GHCB_MSR_PSC_RESP_VAL(val))
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);
/*
* Now that page state is changed in the RMP table, validate it so that it is
* consistent with the RMP entry.
*/
if (op == SNP_PAGE_STATE_PRIVATE && pvalidate(paddr, RMP_PG_SIZE_4K, 1))
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PVALIDATE);
}
void snp_set_page_private(unsigned long paddr)
{
__page_state_change(paddr, SNP_PAGE_STATE_PRIVATE);
}
void snp_set_page_shared(unsigned long paddr)
{
if (!sev_es_negotiate_protocol())
sev_es_terminate(GHCB_SEV_ES_PROT_UNSUPPORTED);
__page_state_change(paddr, SNP_PAGE_STATE_SHARED);
}
static bool early_setup_ghcb(void)
{
if (set_page_decrypted((unsigned long)&boot_ghcb_page))
return false;
......@@ -135,6 +176,10 @@ static bool early_setup_sev_es(void)
/* Initialize lookup tables for the instruction decoder */
inat_init_tables();
/* SNP guest requires the GHCB GPA must be registered */
if (sev_snp_enabled())
snp_register_ghcb_early(__pa(&boot_ghcb_page));
return true;
}
......@@ -174,8 +219,8 @@ void do_boot_stage2_vc(struct pt_regs *regs, unsigned long exit_code)
struct es_em_ctxt ctxt;
enum es_result result;
if (!boot_ghcb && !early_setup_sev_es())
sev_es_terminate(GHCB_SEV_ES_GEN_REQ);
if (!boot_ghcb && !early_setup_ghcb())
sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_GEN_REQ);
vc_ghcb_invalidate(boot_ghcb);
result = vc_init_em_ctxt(&ctxt, regs, exit_code);
......@@ -202,5 +247,191 @@ void do_boot_stage2_vc(struct pt_regs *regs, unsigned long exit_code)
if (result == ES_OK)
vc_finish_insn(&ctxt);
else if (result != ES_RETRY)
sev_es_terminate(GHCB_SEV_ES_GEN_REQ);
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;
struct msr m;
bool snp;
/*
* Setup/preliminary detection of SNP. This will be sanity-checked
* against CPUID/MSR values later.
*/
snp = snp_init(bp);
/* Check for the SME/SEV support leaf */
eax = 0x80000000;
ecx = 0;
native_cpuid(&eax, &ebx, &ecx, &edx);
if (eax < 0x8000001f)
return;
/*
* Check for the SME/SEV feature:
* CPUID Fn8000_001F[EAX]
* - Bit 0 - Secure Memory Encryption support
* - Bit 1 - Secure Encrypted Virtualization support
* CPUID Fn8000_001F[EBX]
* - Bits 5:0 - Pagetable bit position used to indicate encryption
*/
eax = 0x8000001f;
ecx = 0;
native_cpuid(&eax, &ebx, &ecx, &edx);
/* Check whether SEV is supported */
if (!(eax & BIT(1))) {
if (snp)
error("SEV-SNP support indicated by CC blob, but not CPUID.");
return;
}
/* Set the SME mask if this is an SEV guest. */
boot_rdmsr(MSR_AMD64_SEV, &m);
sev_status = m.q;
if (!(sev_status & MSR_AMD64_SEV_ENABLED))
return;
/* Negotiate the GHCB protocol version. */
if (sev_status & MSR_AMD64_SEV_ES_ENABLED) {
if (!sev_es_negotiate_protocol())
sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_PROT_UNSUPPORTED);
}
/*
* SNP is supported in v2 of the GHCB spec which mandates support for HV
* features.
*/
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();
}
if (snp && !(sev_status & MSR_AMD64_SEV_SNP_ENABLED))
error("SEV-SNP supported indicated by CC blob, but not SEV status MSR.");
sme_me_mask = BIT_ULL(ebx & 0x3f);
}
/* Search for Confidential Computing blob in the EFI config table. */
static struct cc_blob_sev_info *find_cc_blob_efi(struct boot_params *bp)
{
unsigned long cfg_table_pa;
unsigned int cfg_table_len;
int ret;
ret = efi_get_conf_table(bp, &cfg_table_pa, &cfg_table_len);
if (ret)
return NULL;
return (struct cc_blob_sev_info *)efi_find_vendor_table(bp, cfg_table_pa,
cfg_table_len,
EFI_CC_BLOB_GUID);
}
/*
* Initial set up of SNP relies on information provided by the
* Confidential Computing blob, which can be passed to the boot kernel
* by firmware/bootloader in the following ways:
*
* - via an entry in the EFI config table
* - via a setup_data structure, as defined by the Linux Boot Protocol
*
* Scan for the blob in that order.
*/
static struct cc_blob_sev_info *find_cc_blob(struct boot_params *bp)
{
struct cc_blob_sev_info *cc_info;
cc_info = find_cc_blob_efi(bp);
if (cc_info)
goto found_cc_info;
cc_info = find_cc_blob_setup_data(bp);
if (!cc_info)
return NULL;
found_cc_info:
if (cc_info->magic != CC_BLOB_SEV_HDR_MAGIC)
sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
return cc_info;
}
/*
* Indicate SNP based on presence of SNP-specific CC blob. Subsequent checks
* will verify the SNP CPUID/MSR bits.
*/
bool snp_init(struct boot_params *bp)
{
struct cc_blob_sev_info *cc_info;
if (!bp)
return false;
cc_info = find_cc_blob(bp);
if (!cc_info)
return false;
/*
* If a SNP-specific Confidential Computing blob is present, then
* firmware/bootloader have indicated SNP support. Verifying this
* involves CPUID checks which will be more reliable if the SNP
* CPUID table is used. See comments over snp_setup_cpuid_table() for
* more details.
*/
setup_cpuid_table(cc_info);
/*
* Pass run-time kernel a pointer to CC info via boot_params so EFI
* config table doesn't need to be searched again during early startup
* phase.
*/
bp->cc_blob_address = (u32)(unsigned long)cc_info;
return true;
}
void sev_prep_identity_maps(unsigned long top_level_pgt)
{
/*
* The Confidential Computing blob is used very early in uncompressed
* kernel to find the in-memory CPUID table to handle CPUID
* instructions. Make sure an identity-mapping exists so it can be
* accessed after switchover.
*/
if (sev_snp_enabled()) {
unsigned long cc_info_pa = boot_params->cc_blob_address;
struct cc_blob_sev_info *cc_info;
kernel_add_identity_map(cc_info_pa, cc_info_pa + sizeof(*cc_info));
cc_info = (struct cc_blob_sev_info *)cc_info_pa;
kernel_add_identity_map(cc_info->cpuid_phys, cc_info->cpuid_phys + cc_info->cpuid_len);
}
sev_verify_cbit(top_level_pgt);
}
......@@ -27,6 +27,7 @@
#include <asm/required-features.h>
#include <asm/msr-index.h>
#include "string.h"
#include "msr.h"
static u32 err_flags[NCAPINTS];
......@@ -130,12 +131,11 @@ int check_cpu(int *cpu_level_ptr, int *req_level_ptr, u32 **err_flags_ptr)
/* If this is an AMD and we're only missing SSE+SSE2, try to
turn them on */
u32 ecx = MSR_K7_HWCR;
u32 eax, edx;
struct msr m;
asm("rdmsr" : "=a" (eax), "=d" (edx) : "c" (ecx));
eax &= ~(1 << 15);
asm("wrmsr" : : "a" (eax), "d" (edx), "c" (ecx));
boot_rdmsr(MSR_K7_HWCR, &m);
m.l &= ~(1 << 15);
boot_wrmsr(MSR_K7_HWCR, &m);
get_cpuflags(); /* Make sure it really did something */
err = check_cpuflags();
......@@ -145,28 +145,28 @@ int check_cpu(int *cpu_level_ptr, int *req_level_ptr, u32 **err_flags_ptr)
/* If this is a VIA C3, we might have to enable CX8
explicitly */
u32 ecx = MSR_VIA_FCR;
u32 eax, edx;
struct msr m;
asm("rdmsr" : "=a" (eax), "=d" (edx) : "c" (ecx));
eax |= (1<<1)|(1<<7);
asm("wrmsr" : : "a" (eax), "d" (edx), "c" (ecx));
boot_rdmsr(MSR_VIA_FCR, &m);
m.l |= (1 << 1) | (1 << 7);
boot_wrmsr(MSR_VIA_FCR, &m);
set_bit(X86_FEATURE_CX8, cpu.flags);
err = check_cpuflags();
} else if (err == 0x01 && is_transmeta()) {
/* Transmeta might have masked feature bits in word 0 */
u32 ecx = 0x80860004;
u32 eax, edx;
struct msr m, m_tmp;
u32 level = 1;
asm("rdmsr" : "=a" (eax), "=d" (edx) : "c" (ecx));
asm("wrmsr" : : "a" (~0), "d" (edx), "c" (ecx));
boot_rdmsr(0x80860004, &m);
m_tmp = m;
m_tmp.l = ~0;
boot_wrmsr(0x80860004, &m_tmp);
asm("cpuid"
: "+a" (level), "=d" (cpu.flags[0])
: : "ecx", "ebx");
asm("wrmsr" : : "a" (eax), "d" (edx), "c" (ecx));
boot_wrmsr(0x80860004, &m);
err = check_cpuflags();
} else if (err == 0x01 &&
......
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Helpers/definitions related to MSR access.
*/
#ifndef BOOT_MSR_H
#define BOOT_MSR_H
#include <asm/shared/msr.h>
/*
* The kernel proper already defines rdmsr()/wrmsr(), but they are not for the
* boot kernel since they rely on tracepoint/exception handling infrastructure
* that's not available here.
*/
static inline void boot_rdmsr(unsigned int reg, struct msr *m)
{
asm volatile("rdmsr" : "=a" (m->l), "=d" (m->h) : "c" (reg));
}
static inline void boot_wrmsr(unsigned int reg, const struct msr *m)
{
asm volatile("wrmsr" : : "c" (reg), "a"(m->l), "d" (m->h) : "memory");
}
#endif /* BOOT_MSR_H */
......@@ -57,6 +57,9 @@ static bool amd_cc_platform_has(enum cc_attr attr)
return (sev_status & MSR_AMD64_SEV_ENABLED) &&
!(sev_status & MSR_AMD64_SEV_ES_ENABLED);
case CC_ATTR_GUEST_SEV_SNP:
return sev_status & MSR_AMD64_SEV_SNP_ENABLED;
default:
return false;
}
......
......@@ -215,8 +215,13 @@ syscall_return_via_sysret:
popq %rdi
popq %rsp
SYM_INNER_LABEL(entry_SYSRETQ_unsafe_stack, SYM_L_GLOBAL)
ANNOTATE_NOENDBR
swapgs
sysretq
SYM_INNER_LABEL(entry_SYSRETQ_end, SYM_L_GLOBAL)
ANNOTATE_NOENDBR
int3
SYM_CODE_END(entry_SYSCALL_64)
/*
......@@ -508,6 +513,7 @@ SYM_CODE_START(\asmsym)
call vc_switch_off_ist
movq %rax, %rsp /* Switch to new stack */
ENCODE_FRAME_POINTER
UNWIND_HINT_REGS
/* Update pt_regs */
......
......@@ -297,6 +297,8 @@ sysret32_from_system_call:
* code. We zero R8-R10 to avoid info leaks.
*/
movq RSP-ORIG_RAX(%rsp), %rsp
SYM_INNER_LABEL(entry_SYSRETL_compat_unsafe_stack, SYM_L_GLOBAL)
ANNOTATE_NOENDBR
/*
* The original userspace %rsp (RSP-ORIG_RAX(%rsp)) is stored
......@@ -314,6 +316,9 @@ sysret32_from_system_call:
xorl %r10d, %r10d
swapgs
sysretl
SYM_INNER_LABEL(entry_SYSRETL_compat_end, SYM_L_GLOBAL)
ANNOTATE_NOENDBR
int3
SYM_CODE_END(entry_SYSCALL_compat)
/*
......
......@@ -74,6 +74,7 @@ static void sanitize_boot_params(struct boot_params *boot_params)
BOOT_PARAM_PRESERVE(hdr),
BOOT_PARAM_PRESERVE(e820_table),
BOOT_PARAM_PRESERVE(eddbuf),
BOOT_PARAM_PRESERVE(cc_blob_address),
};
memset(&scratch, 0, sizeof(scratch));
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* CPUID-related helpers/definitions
*
* Derived from arch/x86/kvm/cpuid.c
*/
#ifndef _ASM_X86_CPUID_H
#define _ASM_X86_CPUID_H
static __always_inline bool cpuid_function_is_indexed(u32 function)
{
switch (function) {
case 4:
case 7:
case 0xb:
case 0xd:
case 0xf:
case 0x10:
case 0x12:
case 0x14:
case 0x17:
case 0x18:
case 0x1d:
case 0x1e:
case 0x1f:
case 0x8000001d:
return true;
}
return false;
}
#endif /* _ASM_X86_CPUID_H */
......@@ -502,8 +502,10 @@
#define MSR_AMD64_SEV 0xc0010131
#define MSR_AMD64_SEV_ENABLED_BIT 0
#define MSR_AMD64_SEV_ES_ENABLED_BIT 1
#define MSR_AMD64_SEV_SNP_ENABLED_BIT 2
#define MSR_AMD64_SEV_ENABLED BIT_ULL(MSR_AMD64_SEV_ENABLED_BIT)
#define MSR_AMD64_SEV_ES_ENABLED BIT_ULL(MSR_AMD64_SEV_ES_ENABLED_BIT)
#define MSR_AMD64_SEV_SNP_ENABLED BIT_ULL(MSR_AMD64_SEV_SNP_ENABLED_BIT)
#define MSR_AMD64_VIRT_SPEC_CTRL 0xc001011f
......
......@@ -10,16 +10,7 @@
#include <asm/errno.h>
#include <asm/cpumask.h>
#include <uapi/asm/msr.h>
struct msr {
union {
struct {
u32 l;
u32 h;
};
u64 q;
};
};
#include <asm/shared/msr.h>
struct msr_info {
u32 msr_no;
......
......@@ -13,6 +13,8 @@ void syscall_init(void);
#ifdef CONFIG_X86_64
void entry_SYSCALL_64(void);
void entry_SYSCALL_64_safe_stack(void);
void entry_SYSRETQ_unsafe_stack(void);
void entry_SYSRETQ_end(void);
long do_arch_prctl_64(struct task_struct *task, int option, unsigned long arg2);
#endif
......@@ -28,6 +30,8 @@ void entry_SYSENTER_compat(void);
void __end_entry_SYSENTER_compat(void);
void entry_SYSCALL_compat(void);
void entry_SYSCALL_compat_safe_stack(void);
void entry_SYSRETL_compat_unsafe_stack(void);
void entry_SYSRETL_compat_end(void);
void entry_INT80_compat(void);
#ifdef CONFIG_XEN_PV
void xen_entry_INT80_compat(void);
......
......@@ -186,9 +186,13 @@ static __always_inline bool ip_within_syscall_gap(struct pt_regs *regs)
bool ret = (regs->ip >= (unsigned long)entry_SYSCALL_64 &&
regs->ip < (unsigned long)entry_SYSCALL_64_safe_stack);
ret = ret || (regs->ip >= (unsigned long)entry_SYSRETQ_unsafe_stack &&
regs->ip < (unsigned long)entry_SYSRETQ_end);
#ifdef CONFIG_IA32_EMULATION
ret = ret || (regs->ip >= (unsigned long)entry_SYSCALL_compat &&
regs->ip < (unsigned long)entry_SYSCALL_compat_safe_stack);
ret = ret || (regs->ip >= (unsigned long)entry_SYSRETL_compat_unsafe_stack &&
regs->ip < (unsigned long)entry_SYSRETL_compat_end);
#endif
return ret;
......
......@@ -50,7 +50,6 @@ extern unsigned long saved_video_mode;
extern void reserve_standard_io_resources(void);
extern void i386_reserve_resources(void);
extern unsigned long __startup_64(unsigned long physaddr, struct boot_params *bp);
extern unsigned long __startup_secondary_64(void);
extern void startup_64_setup_env(unsigned long physbase);
extern void early_setup_idt(void);
extern void __init do_early_exception(struct pt_regs *regs, int trapnr);
......
......@@ -57,9 +57,79 @@
#define GHCB_MSR_AP_RESET_HOLD_REQ 0x006
#define GHCB_MSR_AP_RESET_HOLD_RESP 0x007
/* GHCB GPA Register */
#define GHCB_MSR_REG_GPA_REQ 0x012
#define GHCB_MSR_REG_GPA_REQ_VAL(v) \
/* GHCBData[63:12] */ \
(((u64)((v) & GENMASK_ULL(51, 0)) << 12) | \
/* GHCBData[11:0] */ \
GHCB_MSR_REG_GPA_REQ)
#define GHCB_MSR_REG_GPA_RESP 0x013
#define GHCB_MSR_REG_GPA_RESP_VAL(v) \
/* GHCBData[63:12] */ \
(((u64)(v) & GENMASK_ULL(63, 12)) >> 12)
/*
* SNP Page State Change Operation
*
* GHCBData[55:52] - Page operation:
* 0x0001 Page assignment, Private
* 0x0002 Page assignment, Shared
*/
enum psc_op {
SNP_PAGE_STATE_PRIVATE = 1,
SNP_PAGE_STATE_SHARED,
};
#define GHCB_MSR_PSC_REQ 0x014
#define GHCB_MSR_PSC_REQ_GFN(gfn, op) \
/* GHCBData[55:52] */ \
(((u64)((op) & 0xf) << 52) | \
/* GHCBData[51:12] */ \
((u64)((gfn) & GENMASK_ULL(39, 0)) << 12) | \
/* GHCBData[11:0] */ \
GHCB_MSR_PSC_REQ)
#define GHCB_MSR_PSC_RESP 0x015
#define GHCB_MSR_PSC_RESP_VAL(val) \
/* GHCBData[63:32] */ \
(((u64)(val) & GENMASK_ULL(63, 32)) >> 32)
/* GHCB Hypervisor Feature Request/Response */
#define GHCB_MSR_HV_FT_REQ 0x080
#define GHCB_MSR_HV_FT_RESP 0x081
#define GHCB_MSR_HV_FT_RESP_VAL(v) \
/* GHCBData[63:12] */ \
(((u64)(v) & GENMASK_ULL(63, 12)) >> 12)
#define GHCB_HV_FT_SNP BIT_ULL(0)
#define GHCB_HV_FT_SNP_AP_CREATION BIT_ULL(1)
/* SNP Page State Change NAE event */
#define VMGEXIT_PSC_MAX_ENTRY 253
struct psc_hdr {
u16 cur_entry;
u16 end_entry;
u32 reserved;
} __packed;
struct psc_entry {
u64 cur_page : 12,
gfn : 40,
operation : 4,
pagesize : 1,
reserved : 7;
} __packed;
struct snp_psc_desc {
struct psc_hdr hdr;
struct psc_entry entries[VMGEXIT_PSC_MAX_ENTRY];
} __packed;
/* Guest message request error code */
#define SNP_GUEST_REQ_INVALID_LEN BIT_ULL(32)
#define GHCB_MSR_TERM_REQ 0x100
#define GHCB_MSR_TERM_REASON_SET_POS 12
......@@ -73,8 +143,20 @@
/* GHCBData[23:16] */ \
((((u64)reason_val) & 0xff) << 16))
/* Error codes from reason set 0 */
#define SEV_TERM_SET_GEN 0
#define GHCB_SEV_ES_GEN_REQ 0
#define GHCB_SEV_ES_PROT_UNSUPPORTED 1
#define GHCB_SNP_UNSUPPORTED 2
/* Linux-specific reason codes (used with reason set 1) */
#define SEV_TERM_SET_LINUX 1
#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_TERM_CPUID 4 /* CPUID-validation failure */
#define GHCB_TERM_CPUID_HV 5 /* CPUID failure during hypervisor fallback */
#define GHCB_RESP_CODE(v) ((v) & GHCB_MSR_INFO_MASK)
......
......@@ -11,9 +11,10 @@
#include <linux/types.h>
#include <asm/insn.h>
#include <asm/sev-common.h>
#include <asm/bootparam.h>
#define GHCB_PROTO_OUR 0x0001UL
#define GHCB_PROTOCOL_MAX 1ULL
#define GHCB_PROTOCOL_MIN 1ULL
#define GHCB_PROTOCOL_MAX 2ULL
#define GHCB_DEFAULT_USAGE 0ULL
#define VMGEXIT() { asm volatile("rep; vmmcall\n\r"); }
......@@ -42,6 +43,24 @@ struct es_em_ctxt {
struct es_fault_info fi;
};
/*
* AMD SEV Confidential computing blob structure. The structure is
* defined in OVMF UEFI firmware header:
* https://github.com/tianocore/edk2/blob/master/OvmfPkg/Include/Guid/ConfidentialComputingSevSnpBlob.h
*/
#define CC_BLOB_SEV_HDR_MAGIC 0x45444d41
struct cc_blob_sev_info {
u32 magic;
u16 version;
u16 reserved;
u64 secrets_phys;
u32 secrets_len;
u32 rsvd1;
u64 cpuid_phys;
u32 cpuid_len;
u32 rsvd2;
} __packed;
void do_vc_no_ghcb(struct pt_regs *regs, unsigned long exit_code);
static inline u64 lower_bits(u64 val, unsigned int bits)
......@@ -60,6 +79,61 @@ extern void vc_no_ghcb(void);
extern void vc_boot_ghcb(void);
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
#define RMPADJUST_VMSA_PAGE_BIT BIT(16)
/* SNP Guest message request */
struct snp_req_data {
unsigned long req_gpa;
unsigned long resp_gpa;
unsigned long data_gpa;
unsigned int data_npages;
};
struct sev_guest_platform_data {
u64 secrets_gpa;
};
/*
* The secrets page contains 96-bytes of reserved field that can be used by
* the guest OS. The guest OS uses the area to save the message sequence
* number for each VMPCK.
*
* See the GHCB spec section Secret page layout for the format for this area.
*/
struct secrets_os_area {
u32 msg_seqno_0;
u32 msg_seqno_1;
u32 msg_seqno_2;
u32 msg_seqno_3;
u64 ap_jump_table_pa;
u8 rsvd[40];
u8 guest_usage[32];
} __packed;
#define VMPCK_KEY_LEN 32
/* See the SNP spec version 0.9 for secrets page format */
struct snp_secrets_page_layout {
u32 version;
u32 imien : 1,
rsvd1 : 31;
u32 fms;
u32 rsvd2;
u8 gosvw[16];
u8 vmpck0[VMPCK_KEY_LEN];
u8 vmpck1[VMPCK_KEY_LEN];
u8 vmpck2[VMPCK_KEY_LEN];
u8 vmpck3[VMPCK_KEY_LEN];
struct secrets_os_area os_area;
u8 rsvd3[3840];
} __packed;
#ifdef CONFIG_AMD_MEM_ENCRYPT
extern struct static_key_false sev_es_enable_key;
extern void __sev_es_ist_enter(struct pt_regs *regs);
......@@ -87,12 +161,71 @@ 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;
int rc;
/* "pvalidate" mnemonic support in binutils 2.36 and newer */
asm volatile(".byte 0xF2, 0x0F, 0x01, 0xFF\n\t"
CC_SET(c)
: CC_OUT(c) (no_rmpupdate), "=a"(rc)
: "a"(vaddr), "c"(rmp_psize), "d"(validate)
: "memory", "cc");
if (no_rmpupdate)
return PVALIDATE_FAIL_NOUPDATE;
return rc;
}
void setup_ghcb(void);
void __init early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr,
unsigned int npages);
void __init early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr,
unsigned int npages);
void __init snp_prep_memory(unsigned long paddr, unsigned int sz, enum psc_op op);
void snp_set_memory_shared(unsigned long vaddr, unsigned int npages);
void snp_set_memory_private(unsigned long vaddr, unsigned int npages);
void snp_set_wakeup_secondary_cpu(void);
bool snp_init(struct boot_params *bp);
void snp_abort(void);
int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, unsigned long *fw_err);
#else
static inline void sev_es_ist_enter(struct pt_regs *regs) { }
static inline void sev_es_ist_exit(void) { }
static inline int sev_es_setup_ap_jump_table(struct real_mode_header *rmh) { return 0; }
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; }
static inline void setup_ghcb(void) { }
static inline void __init
early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr, unsigned int npages) { }
static inline void __init
early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr, unsigned int npages) { }
static inline void __init snp_prep_memory(unsigned long paddr, unsigned int sz, enum psc_op op) { }
static inline void snp_set_memory_shared(unsigned long vaddr, unsigned int npages) { }
static inline void snp_set_memory_private(unsigned long vaddr, unsigned int npages) { }
static inline void snp_set_wakeup_secondary_cpu(void) { }
static inline bool snp_init(struct boot_params *bp) { return false; }
static inline void snp_abort(void) { }
static inline int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input,
unsigned long *fw_err)
{
return -ENOTTY;
}
#endif
#endif
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_X86_SHARED_MSR_H
#define _ASM_X86_SHARED_MSR_H
struct msr {
union {
struct {
u32 l;
u32 h;
};
u64 q;
};
};
#endif /* _ASM_X86_SHARED_MSR_H */
......@@ -271,6 +271,7 @@ struct vmcb_seg {
u64 base;
} __packed;
/* Save area definition for legacy and SEV-MEM guests */
struct vmcb_save_area {
struct vmcb_seg es;
struct vmcb_seg cs;
......@@ -282,12 +283,12 @@ struct vmcb_save_area {
struct vmcb_seg ldtr;
struct vmcb_seg idtr;
struct vmcb_seg tr;
u8 reserved_1[43];
u8 reserved_1[42];
u8 vmpl;
u8 cpl;
u8 reserved_2[4];
u64 efer;
u8 reserved_3[104];
u64 xss; /* Valid for SEV-ES only */
u8 reserved_3[112];
u64 cr4;
u64 cr3;
u64 cr0;
......@@ -297,7 +298,9 @@ struct vmcb_save_area {
u64 rip;
u8 reserved_4[88];
u64 rsp;
u8 reserved_5[24];
u64 s_cet;
u64 ssp;
u64 isst_addr;
u64 rax;
u64 star;
u64 lstar;
......@@ -308,29 +311,145 @@ struct vmcb_save_area {
u64 sysenter_esp;
u64 sysenter_eip;
u64 cr2;
u8 reserved_6[32];
u8 reserved_5[32];
u64 g_pat;
u64 dbgctl;
u64 br_from;
u64 br_to;
u64 last_excp_from;
u64 last_excp_to;
/*
* The following part of the save area is valid only for
* SEV-ES guests when referenced through the GHCB or for
* saving to the host save area.
*/
u8 reserved_7[72];
u8 reserved_6[72];
u32 spec_ctrl; /* Guest version of SPEC_CTRL at 0x2E0 */
u8 reserved_7b[4];
} __packed;
/* Save area definition for SEV-ES and SEV-SNP guests */
struct sev_es_save_area {
struct vmcb_seg es;
struct vmcb_seg cs;
struct vmcb_seg ss;
struct vmcb_seg ds;
struct vmcb_seg fs;
struct vmcb_seg gs;
struct vmcb_seg gdtr;
struct vmcb_seg ldtr;
struct vmcb_seg idtr;
struct vmcb_seg tr;
u64 vmpl0_ssp;
u64 vmpl1_ssp;
u64 vmpl2_ssp;
u64 vmpl3_ssp;
u64 u_cet;
u8 reserved_1[2];
u8 vmpl;
u8 cpl;
u8 reserved_2[4];
u64 efer;
u8 reserved_3[104];
u64 xss;
u64 cr4;
u64 cr3;
u64 cr0;
u64 dr7;
u64 dr6;
u64 rflags;
u64 rip;
u64 dr0;
u64 dr1;
u64 dr2;
u64 dr3;
u64 dr0_addr_mask;
u64 dr1_addr_mask;
u64 dr2_addr_mask;
u64 dr3_addr_mask;
u8 reserved_4[24];
u64 rsp;
u64 s_cet;
u64 ssp;
u64 isst_addr;
u64 rax;
u64 star;
u64 lstar;
u64 cstar;
u64 sfmask;
u64 kernel_gs_base;
u64 sysenter_cs;
u64 sysenter_esp;
u64 sysenter_eip;
u64 cr2;
u8 reserved_5[32];
u64 g_pat;
u64 dbgctl;
u64 br_from;
u64 br_to;
u64 last_excp_from;
u64 last_excp_to;
u8 reserved_7[80];
u32 pkru;
u8 reserved_7a[20];
u64 reserved_8; /* rax already available at 0x01f8 */
u8 reserved_8[20];
u64 reserved_9; /* rax already available at 0x01f8 */
u64 rcx;
u64 rdx;
u64 rbx;
u64 reserved_10; /* rsp already available at 0x01d8 */
u64 rbp;
u64 rsi;
u64 rdi;
u64 r8;
u64 r9;
u64 r10;
u64 r11;
u64 r12;
u64 r13;
u64 r14;
u64 r15;
u8 reserved_11[16];
u64 guest_exit_info_1;
u64 guest_exit_info_2;
u64 guest_exit_int_info;
u64 guest_nrip;
u64 sev_features;
u64 vintr_ctrl;
u64 guest_exit_code;
u64 virtual_tom;
u64 tlb_id;
u64 pcpu_id;
u64 event_inj;
u64 xcr0;
u8 reserved_12[16];
/* Floating point area */
u64 x87_dp;
u32 mxcsr;
u16 x87_ftw;
u16 x87_fsw;
u16 x87_fcw;
u16 x87_fop;
u16 x87_ds;
u16 x87_cs;
u64 x87_rip;
u8 fpreg_x87[80];
u8 fpreg_xmm[256];
u8 fpreg_ymm[256];
} __packed;
struct ghcb_save_area {
u8 reserved_1[203];
u8 cpl;
u8 reserved_2[116];
u64 xss;
u8 reserved_3[24];
u64 dr7;
u8 reserved_4[16];
u64 rip;
u8 reserved_5[88];
u64 rsp;
u8 reserved_6[24];
u64 rax;
u8 reserved_7[264];
u64 rcx;
u64 rdx;
u64 rbx;
u64 reserved_9; /* rsp already available at 0x01d8 */
u8 reserved_8[8];
u64 rbp;
u64 rsi;
u64 rdi;
......@@ -342,22 +461,24 @@ struct vmcb_save_area {
u64 r13;
u64 r14;
u64 r15;
u8 reserved_10[16];
u8 reserved_9[16];
u64 sw_exit_code;
u64 sw_exit_info_1;
u64 sw_exit_info_2;
u64 sw_scratch;
u8 reserved_11[56];
u8 reserved_10[56];
u64 xcr0;
u8 valid_bitmap[16];
u64 x87_state_gpa;
} __packed;
#define GHCB_SHARED_BUF_SIZE 2032
struct ghcb {
struct vmcb_save_area save;
u8 reserved_save[2048 - sizeof(struct vmcb_save_area)];
struct ghcb_save_area save;
u8 reserved_save[2048 - sizeof(struct ghcb_save_area)];
u8 shared_buffer[2032];
u8 shared_buffer[GHCB_SHARED_BUF_SIZE];
u8 reserved_1[10];
u16 protocol_version; /* negotiated SEV-ES/GHCB protocol version */
......@@ -365,13 +486,17 @@ struct ghcb {
} __packed;
#define EXPECTED_VMCB_SAVE_AREA_SIZE 1032
#define EXPECTED_VMCB_SAVE_AREA_SIZE 740
#define EXPECTED_GHCB_SAVE_AREA_SIZE 1032
#define EXPECTED_SEV_ES_SAVE_AREA_SIZE 1648
#define EXPECTED_VMCB_CONTROL_AREA_SIZE 1024
#define EXPECTED_GHCB_SIZE PAGE_SIZE
static inline void __unused_size_checks(void)
{
BUILD_BUG_ON(sizeof(struct vmcb_save_area) != EXPECTED_VMCB_SAVE_AREA_SIZE);
BUILD_BUG_ON(sizeof(struct ghcb_save_area) != EXPECTED_GHCB_SAVE_AREA_SIZE);
BUILD_BUG_ON(sizeof(struct sev_es_save_area) != EXPECTED_SEV_ES_SAVE_AREA_SIZE);
BUILD_BUG_ON(sizeof(struct vmcb_control_area) != EXPECTED_VMCB_CONTROL_AREA_SIZE);
BUILD_BUG_ON(sizeof(struct ghcb) != EXPECTED_GHCB_SIZE);
}
......@@ -441,7 +566,7 @@ struct vmcb {
/* GHCB Accessor functions */
#define GHCB_BITMAP_IDX(field) \
(offsetof(struct vmcb_save_area, field) / sizeof(u64))
(offsetof(struct ghcb_save_area, field) / sizeof(u64))
#define DEFINE_GHCB_ACCESSORS(field) \
static inline bool ghcb_##field##_is_valid(const struct ghcb *ghcb) \
......
......@@ -10,6 +10,7 @@
#define SETUP_EFI 4
#define SETUP_APPLE_PROPERTIES 5
#define SETUP_JAILHOUSE 6
#define SETUP_CC_BLOB 7
#define SETUP_INDIRECT (1<<31)
......@@ -187,7 +188,8 @@ struct boot_params {
__u32 ext_ramdisk_image; /* 0x0c0 */
__u32 ext_ramdisk_size; /* 0x0c4 */
__u32 ext_cmd_line_ptr; /* 0x0c8 */
__u8 _pad4[116]; /* 0x0cc */
__u8 _pad4[112]; /* 0x0cc */
__u32 cc_blob_address; /* 0x13c */
struct edid_info edid_info; /* 0x140 */
struct efi_info efi_info; /* 0x1c0 */
__u32 alt_mem_k; /* 0x1e0 */
......
......@@ -108,6 +108,14 @@
#define SVM_VMGEXIT_AP_JUMP_TABLE 0x80000005
#define SVM_VMGEXIT_SET_AP_JUMP_TABLE 0
#define SVM_VMGEXIT_GET_AP_JUMP_TABLE 1
#define SVM_VMGEXIT_PSC 0x80000010
#define SVM_VMGEXIT_GUEST_REQUEST 0x80000011
#define SVM_VMGEXIT_EXT_GUEST_REQUEST 0x80000012
#define SVM_VMGEXIT_AP_CREATION 0x80000013
#define SVM_VMGEXIT_AP_CREATE_ON_INIT 0
#define SVM_VMGEXIT_AP_CREATE 1
#define SVM_VMGEXIT_AP_DESTROY 2
#define SVM_VMGEXIT_HV_FEATURES 0x8000fffd
#define SVM_VMGEXIT_UNSUPPORTED_EVENT 0x8000ffff
/* Exit code reserved for hypervisor/software use */
......@@ -218,6 +226,11 @@
{ SVM_VMGEXIT_NMI_COMPLETE, "vmgexit_nmi_complete" }, \
{ SVM_VMGEXIT_AP_HLT_LOOP, "vmgexit_ap_hlt_loop" }, \
{ SVM_VMGEXIT_AP_JUMP_TABLE, "vmgexit_ap_jump_table" }, \
{ SVM_VMGEXIT_PSC, "vmgexit_page_state_change" }, \
{ SVM_VMGEXIT_GUEST_REQUEST, "vmgexit_guest_request" }, \
{ SVM_VMGEXIT_EXT_GUEST_REQUEST, "vmgexit_ext_guest_request" }, \
{ SVM_VMGEXIT_AP_CREATION, "vmgexit_ap_creation" }, \
{ SVM_VMGEXIT_HV_FEATURES, "vmgexit_hypervisor_feature" }, \
{ SVM_EXIT_ERR, "invalid_guest_state" }
......
......@@ -46,8 +46,6 @@ endif
# non-deterministic coverage.
KCOV_INSTRUMENT := n
CFLAGS_head$(BITS).o += -fno-stack-protector
CFLAGS_irq.o := -I $(srctree)/$(src)/../include/asm/trace
obj-y := process_$(BITS).o signal.o
......
......@@ -60,6 +60,7 @@
#include <asm/uv/uv.h>
#include <asm/sigframe.h>
#include <asm/traps.h>
#include <asm/sev.h>
#include "cpu.h"
......@@ -2126,6 +2127,9 @@ void cpu_init_exception_handling(void)
load_TR_desc();
/* GHCB needs to be setup to handle #VC. */
setup_ghcb();
/* Finally load the IDT */
load_current_idt();
}
......
......@@ -143,7 +143,20 @@ static unsigned long __head sme_postprocess_startup(struct boot_params *bp, pmdv
if (sme_get_me_mask()) {
vaddr = (unsigned long)__start_bss_decrypted;
vaddr_end = (unsigned long)__end_bss_decrypted;
for (; vaddr < vaddr_end; vaddr += PMD_SIZE) {
/*
* On SNP, transition the page to shared in the RMP table so that
* it is consistent with the page table attribute change.
*
* __start_bss_decrypted has a virtual address in the high range
* mapping (kernel .text). PVALIDATE, by way of
* early_snp_set_memory_shared(), requires a valid virtual
* address but the kernel is currently running off of the identity
* mapping so use __pa() to get a *currently* valid virtual address.
*/
early_snp_set_memory_shared(__pa(vaddr), __pa(vaddr), PTRS_PER_PMD);
i = pmd_index(vaddr);
pmd[i] -= sme_get_me_mask();
}
......@@ -192,9 +205,6 @@ unsigned long __head __startup_64(unsigned long physaddr,
if (load_delta & ~PMD_PAGE_MASK)
for (;;);
/* Activate Secure Memory Encryption (SME) if supported and enabled */
sme_enable(bp);
/* Include the SME encryption mask in the fixup value */
load_delta += sme_get_me_mask();
......@@ -308,15 +318,6 @@ unsigned long __head __startup_64(unsigned long physaddr,
return sme_postprocess_startup(bp, pmd);
}
unsigned long __startup_secondary_64(void)
{
/*
* Return the SME encryption mask (if SME is active) to be used as a
* modifier for the initial pgdir entry programmed into CR3.
*/
return sme_get_me_mask();
}
/* Wipe all early page tables except for the kernel symbol map */
static void __init reset_early_page_tables(void)
{
......@@ -600,8 +601,10 @@ static void startup_64_load_idt(unsigned long physbase)
void early_setup_idt(void)
{
/* VMM Communication Exception */
if (IS_ENABLED(CONFIG_AMD_MEM_ENCRYPT))
if (IS_ENABLED(CONFIG_AMD_MEM_ENCRYPT)) {
setup_ghcb();
set_bringup_idt_handler(bringup_idt_table, X86_TRAP_VC, vc_boot_ghcb);
}
bringup_idt_descr.address = (unsigned long)bringup_idt_table;
native_load_idt(&bringup_idt_descr);
......
......@@ -65,10 +65,39 @@ SYM_CODE_START_NOALIGN(startup_64)
leaq (__end_init_task - FRAME_SIZE)(%rip), %rsp
leaq _text(%rip), %rdi
/*
* initial_gs points to initial fixed_percpu_data struct with storage for
* the stack protector canary. Global pointer fixups are needed at this
* stage, so apply them as is done in fixup_pointer(), and initialize %gs
* such that the canary can be accessed at %gs:40 for subsequent C calls.
*/
movl $MSR_GS_BASE, %ecx
movq initial_gs(%rip), %rax
movq $_text, %rdx
subq %rdx, %rax
addq %rdi, %rax
movq %rax, %rdx
shrq $32, %rdx
wrmsr
pushq %rsi
call startup_64_setup_env
popq %rsi
#ifdef CONFIG_AMD_MEM_ENCRYPT
/*
* Activate SEV/SME memory encryption if supported/enabled. This needs to
* be done now, since this also includes setup of the SEV-SNP CPUID table,
* which needs to be done before any CPUID instructions are executed in
* subsequent code.
*/
movq %rsi, %rdi
pushq %rsi
call sme_enable
popq %rsi
#endif
/* Now switch to __KERNEL_CS so IRET works reliably */
pushq $__KERNEL_CS
leaq .Lon_kernel_cs(%rip), %rax
......@@ -134,9 +163,11 @@ SYM_INNER_LABEL(secondary_startup_64_no_verify, SYM_L_GLOBAL)
* Retrieve the modifier (SME encryption mask if SME is active) to be
* added to the initial pgdir entry that will be programmed into CR3.
*/
pushq %rsi
call __startup_secondary_64
popq %rsi
#ifdef CONFIG_AMD_MEM_ENCRYPT
movq sme_me_mask, %rax
#else
xorq %rax, %rax
#endif
/* Form the CR3 value being sure to include the CR3 modifier */
addq $(init_top_pgt - __START_KERNEL_map), %rax
......
......@@ -21,6 +21,7 @@
#include <asm/sections.h>
#include <asm/io.h>
#include <asm/setup_arch.h>
#include <asm/sev.h>
static struct resource system_rom_resource = {
.name = "System ROM",
......@@ -197,11 +198,21 @@ static int __init romchecksum(const unsigned char *rom, unsigned long length)
void __init probe_roms(void)
{
const unsigned char *rom;
unsigned long start, length, upper;
const unsigned char *rom;
unsigned char c;
int i;
/*
* The ROM memory range is not part of the e820 table and is therefore not
* pre-validated by BIOS. The kernel page table maps the ROM region as encrypted
* memory, and SNP requires encrypted memory to be validated before access.
* Do that here.
*/
snp_prep_memory(video_rom_resource.start,
((system_rom_resource.end + 1) - video_rom_resource.start),
SNP_PAGE_STATE_PRIVATE);
/* video rom */
upper = adapter_rom_resources[0].start;
for (start = video_rom_resource.start; start < upper; start += 2048) {
......
This diff is collapsed.
This diff is collapsed.
......@@ -82,6 +82,7 @@
#include <asm/spec-ctrl.h>
#include <asm/hw_irq.h>
#include <asm/stackprotector.h>
#include <asm/sev.h>
/* representing HT siblings of each logical CPU */
DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_sibling_map);
......@@ -1430,6 +1431,8 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus)
smp_quirk_init_udelay();
speculative_store_bypass_ht_init();
snp_set_wakeup_secondary_cpu();
}
void arch_thaw_secondary_cpus_begin(void)
......
......@@ -19,6 +19,7 @@
#include <asm/user.h>
#include <asm/fpu/xstate.h>
#include <asm/sgx.h>
#include <asm/cpuid.h>
#include "cpuid.h"
#include "lapic.h"
#include "mmu.h"
......@@ -744,24 +745,8 @@ static struct kvm_cpuid_entry2 *do_host_cpuid(struct kvm_cpuid_array *array,
cpuid_count(entry->function, entry->index,
&entry->eax, &entry->ebx, &entry->ecx, &entry->edx);
switch (function) {
case 4:
case 7:
case 0xb:
case 0xd:
case 0xf:
case 0x10:
case 0x12:
case 0x14:
case 0x17:
case 0x18:
case 0x1d:
case 0x1e:
case 0x1f:
case 0x8000001d:
if (cpuid_function_is_indexed(function))
entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
break;
}
return entry;
}
......
......@@ -562,12 +562,20 @@ static int sev_launch_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp)
static int sev_es_sync_vmsa(struct vcpu_svm *svm)
{
struct vmcb_save_area *save = &svm->vmcb->save;
struct sev_es_save_area *save = svm->sev_es.vmsa;
/* Check some debug related fields before encrypting the VMSA */
if (svm->vcpu.guest_debug || (save->dr7 & ~DR7_FIXED_1))
if (svm->vcpu.guest_debug || (svm->vmcb->save.dr7 & ~DR7_FIXED_1))
return -EINVAL;
/*
* SEV-ES will use a VMSA that is pointed to by the VMCB, not
* the traditional VMSA that is part of the VMCB. Copy the
* traditional VMSA as it has been built so far (in prep
* for LAUNCH_UPDATE_VMSA) to be the initial SEV-ES state.
*/
memcpy(save, &svm->vmcb->save, sizeof(svm->vmcb->save));
/* Sync registgers */
save->rax = svm->vcpu.arch.regs[VCPU_REGS_RAX];
save->rbx = svm->vcpu.arch.regs[VCPU_REGS_RBX];
......@@ -595,14 +603,6 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm)
save->xss = svm->vcpu.arch.ia32_xss;
save->dr6 = svm->vcpu.arch.dr6;
/*
* SEV-ES will use a VMSA that is pointed to by the VMCB, not
* the traditional VMSA that is part of the VMCB. Copy the
* traditional VMSA as it has been built so far (in prep
* for LAUNCH_UPDATE_VMSA) to be the initial SEV-ES state.
*/
memcpy(svm->sev_es.vmsa, save, sizeof(*save));
return 0;
}
......@@ -2966,7 +2966,7 @@ void sev_es_vcpu_reset(struct vcpu_svm *svm)
sev_enc_bit));
}
void sev_es_prepare_switch_to_guest(struct vmcb_save_area *hostsa)
void sev_es_prepare_switch_to_guest(struct sev_es_save_area *hostsa)
{
/*
* As an SEV-ES guest, hardware will restore the host state on VMEXIT,
......
......@@ -1270,8 +1270,8 @@ static void svm_prepare_switch_to_guest(struct kvm_vcpu *vcpu)
*/
vmsave(__sme_page_pa(sd->save_area));
if (sev_es_guest(vcpu->kvm)) {
struct vmcb_save_area *hostsa;
hostsa = (struct vmcb_save_area *)(page_address(sd->save_area) + 0x400);
struct sev_es_save_area *hostsa;
hostsa = (struct sev_es_save_area *)(page_address(sd->save_area) + 0x400);
sev_es_prepare_switch_to_guest(hostsa);
}
......@@ -3117,8 +3117,8 @@ static void dump_vmcb(struct kvm_vcpu *vcpu)
"tr:",
save01->tr.selector, save01->tr.attrib,
save01->tr.limit, save01->tr.base);
pr_err("cpl: %d efer: %016llx\n",
save->cpl, save->efer);
pr_err("vmpl: %d cpl: %d efer: %016llx\n",
save->vmpl, save->cpl, save->efer);
pr_err("%-15s %016llx %-13s %016llx\n",
"cr0:", save->cr0, "cr2:", save->cr2);
pr_err("%-15s %016llx %-13s %016llx\n",
......
......@@ -181,7 +181,7 @@ struct svm_nested_state {
struct vcpu_sev_es_state {
/* SEV-ES support */
struct vmcb_save_area *vmsa;
struct sev_es_save_area *vmsa;
struct ghcb *ghcb;
struct kvm_host_map ghcb_map;
bool received_first_sipi;
......@@ -622,7 +622,7 @@ int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in);
void sev_es_init_vmcb(struct vcpu_svm *svm);
void sev_es_vcpu_reset(struct vcpu_svm *svm);
void sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector);
void sev_es_prepare_switch_to_guest(struct vmcb_save_area *hostsa);
void sev_es_prepare_switch_to_guest(struct sev_es_save_area *hostsa);
void sev_es_unmap_ghcb(struct vcpu_svm *svm);
/* vmenter.S */
......
......@@ -62,6 +62,10 @@ static void print_mem_encrypt_feature_info(void)
if (cc_platform_has(CC_ATTR_GUEST_STATE_ENCRYPT))
pr_cont(" SEV-ES");
/* Secure Nested Paging */
if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
pr_cont(" SEV-SNP");
pr_cont("\n");
}
......
......@@ -31,6 +31,7 @@
#include <asm/processor-flags.h>
#include <asm/msr.h>
#include <asm/cmdline.h>
#include <asm/sev.h>
#include "mm_internal.h"
......@@ -47,6 +48,36 @@ EXPORT_SYMBOL(sme_me_mask);
/* Buffer used for early in-place encryption by BSP, no locking needed */
static char sme_early_buffer[PAGE_SIZE] __initdata __aligned(PAGE_SIZE);
/*
* SNP-specific routine which needs to additionally change the page state from
* private to shared before copying the data from the source to destination and
* restore after the copy.
*/
static inline void __init snp_memcpy(void *dst, void *src, size_t sz,
unsigned long paddr, bool decrypt)
{
unsigned long npages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
if (decrypt) {
/*
* @paddr needs to be accessed decrypted, mark the page shared in
* the RMP table before copying it.
*/
early_snp_set_memory_shared((unsigned long)__va(paddr), paddr, npages);
memcpy(dst, src, sz);
/* Restore the page state after the memcpy. */
early_snp_set_memory_private((unsigned long)__va(paddr), paddr, npages);
} else {
/*
* @paddr need to be accessed encrypted, no need for the page state
* change.
*/
memcpy(dst, src, sz);
}
}
/*
* This routine does not change the underlying encryption setting of the
* page(s) that map this memory. It assumes that eventually the memory is
......@@ -95,8 +126,13 @@ static void __init __sme_early_enc_dec(resource_size_t paddr,
* Use a temporary buffer, of cache-line multiple size, to
* avoid data corruption as documented in the APM.
*/
memcpy(sme_early_buffer, src, len);
memcpy(dst, sme_early_buffer, len);
if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP)) {
snp_memcpy(sme_early_buffer, src, len, paddr, enc);
snp_memcpy(dst, sme_early_buffer, len, paddr, !enc);
} else {
memcpy(sme_early_buffer, src, len);
memcpy(dst, sme_early_buffer, len);
}
early_memunmap(dst, len);
early_memunmap(src, len);
......@@ -280,11 +316,24 @@ static void enc_dec_hypercall(unsigned long vaddr, int npages, bool enc)
static void amd_enc_status_change_prepare(unsigned long vaddr, int npages, bool enc)
{
/*
* To maintain the security guarantees of SEV-SNP guests, make sure
* to invalidate the memory before encryption attribute is cleared.
*/
if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP) && !enc)
snp_set_memory_shared(vaddr, npages);
}
/* Return true unconditionally: return value doesn't matter for the SEV side */
static bool amd_enc_status_change_finish(unsigned long vaddr, int npages, bool enc)
{
/*
* After memory is mapped encrypted in the page table, validate it
* so that it is consistent with the page table updates.
*/
if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP) && enc)
snp_set_memory_private(vaddr, npages);
if (!cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT))
enc_dec_hypercall(vaddr, npages, enc);
......@@ -322,14 +371,28 @@ static void __init __set_clr_pte_enc(pte_t *kpte, int level, bool enc)
clflush_cache_range(__va(pa), size);
/* Encrypt/decrypt the contents in-place */
if (enc)
if (enc) {
sme_early_encrypt(pa, size);
else
} else {
sme_early_decrypt(pa, size);
/*
* ON SNP, the page state in the RMP table must happen
* before the page table updates.
*/
early_snp_set_memory_shared((unsigned long)__va(pa), pa, 1);
}
/* Change the page encryption mask. */
new_pte = pfn_pte(pfn, new_prot);
set_pte_atomic(kpte, new_pte);
/*
* If page is set encrypted in the page table, then update the RMP table to
* add this page as private.
*/
if (enc)
early_snp_set_memory_private((unsigned long)__va(pa), pa, 1);
}
static int __init early_set_memory_enc_dec(unsigned long vaddr,
......
......@@ -45,6 +45,7 @@
#include <asm/sections.h>
#include <asm/cmdline.h>
#include <asm/coco.h>
#include <asm/sev.h>
#include "mm_internal.h"
......@@ -509,8 +510,11 @@ void __init sme_enable(struct boot_params *bp)
bool active_by_default;
unsigned long me_mask;
char buffer[16];
bool snp;
u64 msr;
snp = snp_init(bp);
/* Check for the SME/SEV support leaf */
eax = 0x80000000;
ecx = 0;
......@@ -542,6 +546,10 @@ void __init sme_enable(struct boot_params *bp)
sev_status = __rdmsr(MSR_AMD64_SEV);
feature_mask = (sev_status & MSR_AMD64_SEV_ENABLED) ? AMD_SEV_BIT : AMD_SME_BIT;
/* The SEV-SNP CC blob should never be present unless SEV-SNP is enabled. */
if (snp && !(sev_status & MSR_AMD64_SEV_SNP_ENABLED))
snp_abort();
/* Check if memory encryption is enabled */
if (feature_mask == AMD_SME_BIT) {
/*
......
......@@ -67,7 +67,7 @@ void __init reserve_real_mode(void)
memblock_reserve(0, SZ_1M);
}
static void sme_sev_setup_real_mode(struct trampoline_header *th)
static void __init sme_sev_setup_real_mode(struct trampoline_header *th)
{
#ifdef CONFIG_AMD_MEM_ENCRYPT
if (cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT))
......
......@@ -50,4 +50,6 @@ source "drivers/virt/acrn/Kconfig"
source "drivers/virt/coco/efi_secret/Kconfig"
source "drivers/virt/coco/sev-guest/Kconfig"
endif
......@@ -10,3 +10,4 @@ obj-y += vboxguest/
obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/
obj-$(CONFIG_ACRN_HSM) += acrn/
obj-$(CONFIG_EFI_SECRET) += coco/efi_secret/
obj-$(CONFIG_SEV_GUEST) += coco/sev-guest/
config SEV_GUEST
tristate "AMD SEV Guest driver"
default m
depends on AMD_MEM_ENCRYPT
select CRYPTO_AEAD2
select CRYPTO_GCM
help
SEV-SNP firmware provides the guest a mechanism to communicate with
the PSP without risk from a malicious hypervisor who wishes to read,
alter, drop or replay the messages sent. The driver provides
userspace interface to communicate with the PSP to request the
attestation report and more.
If you choose 'M' here, this module will be called sev-guest.
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SEV_GUEST) += sev-guest.o
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment