Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * AMD Memory Encryption Support
4 *
5 * Copyright (C) 2019 SUSE
6 *
7 * Author: Joerg Roedel <jroedel@suse.de>
8 */
9
10#define pr_fmt(fmt) "SEV: " fmt
11
12#include <linux/percpu-defs.h>
13#include <linux/cc_platform.h>
14#include <linux/printk.h>
15#include <linux/mm_types.h>
16#include <linux/set_memory.h>
17#include <linux/memblock.h>
18#include <linux/kernel.h>
19#include <linux/mm.h>
20#include <linux/cpumask.h>
21#include <linux/efi.h>
22#include <linux/io.h>
23#include <linux/psp-sev.h>
24#include <uapi/linux/sev-guest.h>
25
26#include <asm/init.h>
27#include <asm/cpu_entry_area.h>
28#include <asm/stacktrace.h>
29#include <asm/sev.h>
30#include <asm/insn-eval.h>
31#include <asm/fpu/xcr.h>
32#include <asm/processor.h>
33#include <asm/realmode.h>
34#include <asm/setup.h>
35#include <asm/traps.h>
36#include <asm/svm.h>
37#include <asm/smp.h>
38#include <asm/cpu.h>
39#include <asm/apic.h>
40#include <asm/cpuid/api.h>
41#include <asm/cmdline.h>
42
43#include "../../coco/sev/internal.h"
44
45/* Include code shared with pre-decompression boot stage */
46#include "sev-shared.c"
47
48void
49early_set_pages_state(unsigned long vaddr, unsigned long paddr,
50 unsigned long npages, const struct psc_desc *desc)
51{
52 unsigned long paddr_end;
53
54 vaddr = vaddr & PAGE_MASK;
55
56 paddr = paddr & PAGE_MASK;
57 paddr_end = paddr + (npages << PAGE_SHIFT);
58
59 while (paddr < paddr_end) {
60 __page_state_change(vaddr, paddr, desc);
61
62 vaddr += PAGE_SIZE;
63 paddr += PAGE_SIZE;
64 }
65}
66
67void __init early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr,
68 unsigned long npages)
69{
70 struct psc_desc d = {
71 SNP_PAGE_STATE_PRIVATE,
72 rip_rel_ptr(&boot_svsm_ca_page),
73 boot_svsm_caa_pa
74 };
75
76 /*
77 * This can be invoked in early boot while running identity mapped, so
78 * use an open coded check for SNP instead of using cc_platform_has().
79 * This eliminates worries about jump tables or checking boot_cpu_data
80 * in the cc_platform_has() function.
81 */
82 if (!(sev_status & MSR_AMD64_SEV_SNP_ENABLED))
83 return;
84
85 /*
86 * Ask the hypervisor to mark the memory pages as private in the RMP
87 * table.
88 */
89 early_set_pages_state(vaddr, paddr, npages, &d);
90}
91
92void __init early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr,
93 unsigned long npages)
94{
95 struct psc_desc d = {
96 SNP_PAGE_STATE_SHARED,
97 rip_rel_ptr(&boot_svsm_ca_page),
98 boot_svsm_caa_pa
99 };
100
101 /*
102 * This can be invoked in early boot while running identity mapped, so
103 * use an open coded check for SNP instead of using cc_platform_has().
104 * This eliminates worries about jump tables or checking boot_cpu_data
105 * in the cc_platform_has() function.
106 */
107 if (!(sev_status & MSR_AMD64_SEV_SNP_ENABLED))
108 return;
109
110 /* Ask hypervisor to mark the memory pages shared in the RMP table. */
111 early_set_pages_state(vaddr, paddr, npages, &d);
112}
113
114/*
115 * Initial set up of SNP relies on information provided by the
116 * Confidential Computing blob, which can be passed to the kernel
117 * in the following ways, depending on how it is booted:
118 *
119 * - when booted via the boot/decompress kernel:
120 * - via boot_params
121 *
122 * - when booted directly by firmware/bootloader (e.g. CONFIG_PVH):
123 * - via a setup_data entry, as defined by the Linux Boot Protocol
124 *
125 * Scan for the blob in that order.
126 */
127static struct cc_blob_sev_info *__init find_cc_blob(struct boot_params *bp)
128{
129 struct cc_blob_sev_info *cc_info;
130
131 /* Boot kernel would have passed the CC blob via boot_params. */
132 if (bp->cc_blob_address) {
133 cc_info = (struct cc_blob_sev_info *)(unsigned long)bp->cc_blob_address;
134 goto found_cc_info;
135 }
136
137 /*
138 * If kernel was booted directly, without the use of the
139 * boot/decompression kernel, the CC blob may have been passed via
140 * setup_data instead.
141 */
142 cc_info = find_cc_blob_setup_data(bp);
143 if (!cc_info)
144 return NULL;
145
146found_cc_info:
147 if (cc_info->magic != CC_BLOB_SEV_HDR_MAGIC)
148 sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
149
150 return cc_info;
151}
152
153static void __init svsm_setup(struct cc_blob_sev_info *cc_info)
154{
155 struct snp_secrets_page *secrets = (void *)cc_info->secrets_phys;
156 struct svsm_call call = {};
157 u64 pa;
158
159 /*
160 * Record the SVSM Calling Area address (CAA) if the guest is not
161 * running at VMPL0. The CA will be used to communicate with the
162 * SVSM to perform the SVSM services.
163 */
164 if (!svsm_setup_ca(cc_info, rip_rel_ptr(&boot_svsm_ca_page)))
165 return;
166
167 /*
168 * It is very early in the boot and the kernel is running identity
169 * mapped but without having adjusted the pagetables to where the
170 * kernel was loaded (physbase), so the get the CA address using
171 * RIP-relative addressing.
172 */
173 pa = (u64)rip_rel_ptr(&boot_svsm_ca_page);
174
175 /*
176 * Switch over to the boot SVSM CA while the current CA is still 1:1
177 * mapped and thus addressable with VA == PA. There is no GHCB at this
178 * point so use the MSR protocol.
179 *
180 * SVSM_CORE_REMAP_CA call:
181 * RAX = 0 (Protocol=0, CallID=0)
182 * RCX = New CA GPA
183 */
184 call.caa = (struct svsm_ca *)secrets->svsm_caa;
185 call.rax = SVSM_CORE_CALL(SVSM_CORE_REMAP_CA);
186 call.rcx = pa;
187
188 if (svsm_call_msr_protocol(&call))
189 sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SVSM_CA_REMAP_FAIL);
190
191 boot_svsm_caa_pa = pa;
192}
193
194bool __init snp_init(struct boot_params *bp)
195{
196 struct cc_blob_sev_info *cc_info;
197
198 if (!bp)
199 return false;
200
201 cc_info = find_cc_blob(bp);
202 if (!cc_info)
203 return false;
204
205 if (cc_info->secrets_phys && cc_info->secrets_len == PAGE_SIZE)
206 sev_secrets_pa = cc_info->secrets_phys;
207 else
208 return false;
209
210 setup_cpuid_table(cc_info);
211
212 svsm_setup(cc_info);
213
214 /*
215 * The CC blob will be used later to access the secrets page. Cache
216 * it here like the boot kernel does.
217 */
218 bp->cc_blob_address = (u32)(unsigned long)cc_info;
219
220 return true;
221}