mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
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 ...
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
.. 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
|
||||
|
||||
+33
-139
@@ -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;
|
||||
unsigned long rsdp_addr;
|
||||
int ret;
|
||||
|
||||
/* Get EFI tables from systab. */
|
||||
for (i = 0; i < nr_tables; i++) {
|
||||
acpi_physical_address table;
|
||||
efi_guid_t guid;
|
||||
/*
|
||||
* 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;
|
||||
|
||||
if (efi_64) {
|
||||
efi_config_table_64_t *tbl = (efi_config_table_64_t *)config_tables + i;
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!(efi_guidcmp(guid, ACPI_TABLE_GUID)))
|
||||
rsdp_addr = table;
|
||||
else if (!(efi_guidcmp(guid, ACPI_20_TABLE_GUID)))
|
||||
return table;
|
||||
}
|
||||
debug_putstr("Error getting RSDP address.\n");
|
||||
#endif
|
||||
return rsdp_addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
#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;
|
||||
enum efi_type et;
|
||||
int ret;
|
||||
|
||||
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");
|
||||
et = efi_get_type(boot_params);
|
||||
if (et == EFI_TYPE_NONE)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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");
|
||||
return 0;
|
||||
}
|
||||
systab = ei->efi_systab;
|
||||
#endif
|
||||
if (!systab)
|
||||
error("EFI system table not found.");
|
||||
systab_pa = efi_get_system_table(boot_params);
|
||||
if (!systab_pa)
|
||||
error("EFI support advertised, but unable to locate system table.");
|
||||
|
||||
/* Handle EFI bitness properly */
|
||||
if (efi_64) {
|
||||
efi_system_table_64_t *stbl = (efi_system_table_64_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;
|
||||
} else {
|
||||
efi_system_table_32_t *stbl = (efi_system_table_32_t *)systab;
|
||||
|
||||
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();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#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"
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/* 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 */
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// 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 */
|
||||
|
||||
+247
-16
@@ -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)
|
||||
{
|
||||
if (!sev_es_negotiate_protocol())
|
||||
sev_es_terminate(GHCB_SEV_ES_PROT_UNSUPPORTED);
|
||||
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)
|
||||
{
|
||||
__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 @@ finish:
|
||||
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);
|
||||
}
|
||||
|
||||
+15
-15
@@ -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 &&
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user