Commit 964bbdfd authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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

Pull x86 SEV updates from Borislav Petkov:

 - Small cleanups and improvements

* tag 'x86_sev_for_v6.10_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/sev: Make the VMPL0 checking more straight forward
  x86/sev: Rename snp_init() in boot/compressed/sev.c
  x86/sev: Shorten struct name snp_secrets_page_layout to snp_secrets_page
parents a1907ccd e2f4c8c3
Loading
Loading
Loading
Loading
+95 −102
Original line number Diff line number Diff line
@@ -335,26 +335,6 @@ void do_boot_stage2_vc(struct pt_regs *regs, unsigned long exit_code)
		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);
}

/*
 * SNP_FEATURES_IMPL_REQ is the mask of SNP features that will need
 * guest side implementation for proper functioning of the guest. If any
@@ -413,6 +393,85 @@ void snp_check_features(void)
	}
}

/* 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.
 */
static bool early_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;
}

/*
 * sev_check_cpu_support - Check for SEV support in the CPU capabilities
 *
@@ -463,7 +522,7 @@ void sev_enable(struct boot_params *bp)
		bp->cc_blob_address = 0;

	/*
	 * Do an initial SEV capability check before snp_init() which
	 * Do an initial SEV capability check before early_snp_init() which
	 * loads the CPUID page and the same checks afterwards are done
	 * without the hypervisor and are trustworthy.
	 *
@@ -478,7 +537,7 @@ void sev_enable(struct boot_params *bp)
	 * Setup/preliminary detection of SNP. This will be sanity-checked
	 * against CPUID/MSR values later.
	 */
	snp = snp_init(bp);
	snp = early_snp_init(bp);

	/* Now repeat the checks with the SNP CPUID table. */

@@ -509,7 +568,20 @@ void sev_enable(struct boot_params *bp)
		if (!(get_hv_features() & GHCB_HV_FT_SNP))
			sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);

		enforce_vmpl0();
		/*
		 * Enforce running at VMPL0.
		 *
		 * 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 running at VMPL0 only run at VMPL0, so VMPL1 or higher
		 * permission mask changes are a don't-care.
		 */
		if (rmpadjust((unsigned long)&boot_ghcb_page, RMP_PG_SIZE_4K, 1))
			sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_NOT_VMPL0);
	}

	if (snp && !(sev_status & MSR_AMD64_SEV_SNP_ENABLED))
@@ -535,85 +607,6 @@ u64 sev_get_status(void)
	return m.q;
}

/* 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)
{
	/*
+1 −1
Original line number Diff line number Diff line
@@ -140,7 +140,7 @@ struct secrets_os_area {
#define VMPCK_KEY_LEN		32

/* See the SNP spec version 0.9 for secrets page format */
struct snp_secrets_page_layout {
struct snp_secrets_page {
	u32 version;
	u32 imien	: 1,
	    rsvd1	: 31;
+3 −3
Original line number Diff line number Diff line
@@ -648,7 +648,7 @@ static u64 __init get_secrets_page(void)

static u64 __init get_snp_jump_table_addr(void)
{
	struct snp_secrets_page_layout *layout;
	struct snp_secrets_page *secrets;
	void __iomem *mem;
	u64 pa, addr;

@@ -662,9 +662,9 @@ static u64 __init get_snp_jump_table_addr(void)
		return 0;
	}

	layout = (__force struct snp_secrets_page_layout *)mem;
	secrets = (__force struct snp_secrets_page *)mem;

	addr = layout->os_area.ap_jump_table_pa;
	addr = secrets->os_area.ap_jump_table_pa;
	iounmap(mem);

	return addr;
+14 −14
Original line number Diff line number Diff line
@@ -59,7 +59,7 @@ struct snp_guest_dev {
	 */
	struct snp_guest_msg secret_request, secret_response;

	struct snp_secrets_page_layout *layout;
	struct snp_secrets_page *secrets;
	struct snp_req_data input;
	union {
		struct snp_report_req report;
@@ -743,26 +743,26 @@ static const struct file_operations snp_guest_fops = {
	.unlocked_ioctl = snp_guest_ioctl,
};

static u8 *get_vmpck(int id, struct snp_secrets_page_layout *layout, u32 **seqno)
static u8 *get_vmpck(int id, struct snp_secrets_page *secrets, u32 **seqno)
{
	u8 *key = NULL;

	switch (id) {
	case 0:
		*seqno = &layout->os_area.msg_seqno_0;
		key = layout->vmpck0;
		*seqno = &secrets->os_area.msg_seqno_0;
		key = secrets->vmpck0;
		break;
	case 1:
		*seqno = &layout->os_area.msg_seqno_1;
		key = layout->vmpck1;
		*seqno = &secrets->os_area.msg_seqno_1;
		key = secrets->vmpck1;
		break;
	case 2:
		*seqno = &layout->os_area.msg_seqno_2;
		key = layout->vmpck2;
		*seqno = &secrets->os_area.msg_seqno_2;
		key = secrets->vmpck2;
		break;
	case 3:
		*seqno = &layout->os_area.msg_seqno_3;
		key = layout->vmpck3;
		*seqno = &secrets->os_area.msg_seqno_3;
		key = secrets->vmpck3;
		break;
	default:
		break;
@@ -897,8 +897,8 @@ static void unregister_sev_tsm(void *data)

static int __init sev_guest_probe(struct platform_device *pdev)
{
	struct snp_secrets_page_layout *layout;
	struct sev_guest_platform_data *data;
	struct snp_secrets_page *secrets;
	struct device *dev = &pdev->dev;
	struct snp_guest_dev *snp_dev;
	struct miscdevice *misc;
@@ -916,7 +916,7 @@ static int __init sev_guest_probe(struct platform_device *pdev)
	if (!mapping)
		return -ENODEV;

	layout = (__force void *)mapping;
	secrets = (__force void *)mapping;

	ret = -ENOMEM;
	snp_dev = devm_kzalloc(&pdev->dev, sizeof(struct snp_guest_dev), GFP_KERNEL);
@@ -924,7 +924,7 @@ static int __init sev_guest_probe(struct platform_device *pdev)
		goto e_unmap;

	ret = -EINVAL;
	snp_dev->vmpck = get_vmpck(vmpck_id, layout, &snp_dev->os_area_msg_seqno);
	snp_dev->vmpck = get_vmpck(vmpck_id, secrets, &snp_dev->os_area_msg_seqno);
	if (!snp_dev->vmpck) {
		dev_err(dev, "invalid vmpck id %d\n", vmpck_id);
		goto e_unmap;
@@ -938,7 +938,7 @@ static int __init sev_guest_probe(struct platform_device *pdev)

	platform_set_drvdata(pdev, snp_dev);
	snp_dev->dev = dev;
	snp_dev->layout = layout;
	snp_dev->secrets = secrets;

	/* Allocate the shared page used for the request and response message. */
	snp_dev->request = alloc_shared_pages(dev, sizeof(struct snp_guest_msg));