Skip to content

Commit 3e43c60

Browse files
tlendackybp3tk0v
authored andcommitted
x86/sev: Prepare for using the RMPREAD instruction to access the RMP
The RMPREAD instruction returns an architecture defined format of an RMP entry. This is the preferred method for examining RMP entries. In preparation for using the RMPREAD instruction, convert the existing code that directly accesses the RMP to map the raw RMP information into the architecture defined format. RMPREAD output returns a status bit for the 2MB region status. If the input page address is 2MB aligned and any other pages within the 2MB region are assigned, then 2MB region status will be set to 1. Otherwise, the 2MB region status will be set to 0. For systems that do not support RMPREAD, calculating this value would require looping over all of the RMP table entries within that range until one is found with the assigned bit set. Since this bit is not defined in the current format, and so not used today, do not incur the overhead associated with calculating it. Signed-off-by: Tom Lendacky <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Reviewed-by: Nikunj A Dadhania <[email protected]> Reviewed-by: Neeraj Upadhyay <[email protected]> Link: https://lore.kernel.org/r/da49d5af1eb7f9039f35f14a32ca091efb2dd818.1733172653.git.thomas.lendacky@amd.com
1 parent fac04ef commit 3e43c60

File tree

1 file changed

+94
-38
lines changed

1 file changed

+94
-38
lines changed

arch/x86/virt/svm/sev.c

Lines changed: 94 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,29 @@
3131
#include <asm/iommu.h>
3232

3333
/*
34-
* The RMP entry format is not architectural. The format is defined in PPR
35-
* Family 19h Model 01h, Rev B1 processor.
34+
* The RMP entry information as returned by the RMPREAD instruction.
3635
*/
3736
struct rmpentry {
37+
u64 gpa;
38+
u8 assigned :1,
39+
rsvd1 :7;
40+
u8 pagesize :1,
41+
hpage_region_status :1,
42+
rsvd2 :6;
43+
u8 immutable :1,
44+
rsvd3 :7;
45+
u8 rsvd4;
46+
u32 asid;
47+
} __packed;
48+
49+
/*
50+
* The raw RMP entry format is not architectural. The format is defined in PPR
51+
* Family 19h Model 01h, Rev B1 processor. This format represents the actual
52+
* entry in the RMP table memory. The bitfield definitions are used for machines
53+
* without the RMPREAD instruction (Zen3 and Zen4), otherwise the "hi" and "lo"
54+
* fields are only used for dumping the raw data.
55+
*/
56+
struct rmpentry_raw {
3857
union {
3958
struct {
4059
u64 assigned : 1,
@@ -62,7 +81,7 @@ struct rmpentry {
6281
#define PFN_PMD_MASK GENMASK_ULL(63, PMD_SHIFT - PAGE_SHIFT)
6382

6483
static u64 probed_rmp_base, probed_rmp_size;
65-
static struct rmpentry *rmptable __ro_after_init;
84+
static struct rmpentry_raw *rmptable __ro_after_init;
6685
static u64 rmptable_max_pfn __ro_after_init;
6786

6887
static LIST_HEAD(snp_leaked_pages_list);
@@ -249,8 +268,8 @@ static int __init snp_rmptable_init(void)
249268
rmptable_start += RMPTABLE_CPU_BOOKKEEPING_SZ;
250269
rmptable_size = probed_rmp_size - RMPTABLE_CPU_BOOKKEEPING_SZ;
251270

252-
rmptable = (struct rmpentry *)rmptable_start;
253-
rmptable_max_pfn = rmptable_size / sizeof(struct rmpentry) - 1;
271+
rmptable = (struct rmpentry_raw *)rmptable_start;
272+
rmptable_max_pfn = rmptable_size / sizeof(struct rmpentry_raw) - 1;
254273

255274
cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/rmptable_init:online", __snp_enable, NULL);
256275

@@ -272,48 +291,77 @@ static int __init snp_rmptable_init(void)
272291
*/
273292
device_initcall(snp_rmptable_init);
274293

275-
static struct rmpentry *get_rmpentry(u64 pfn)
294+
static struct rmpentry_raw *get_raw_rmpentry(u64 pfn)
276295
{
277-
if (WARN_ON_ONCE(pfn > rmptable_max_pfn))
296+
if (!rmptable)
297+
return ERR_PTR(-ENODEV);
298+
299+
if (unlikely(pfn > rmptable_max_pfn))
278300
return ERR_PTR(-EFAULT);
279301

280-
return &rmptable[pfn];
302+
return rmptable + pfn;
303+
}
304+
305+
static int get_rmpentry(u64 pfn, struct rmpentry *e)
306+
{
307+
struct rmpentry_raw *e_raw;
308+
309+
e_raw = get_raw_rmpentry(pfn);
310+
if (IS_ERR(e_raw))
311+
return PTR_ERR(e_raw);
312+
313+
/*
314+
* Map the raw RMP table entry onto the RMPREAD output format.
315+
* The 2MB region status indicator (hpage_region_status field) is not
316+
* calculated, since the overhead could be significant and the field
317+
* is not used.
318+
*/
319+
memset(e, 0, sizeof(*e));
320+
e->gpa = e_raw->gpa << PAGE_SHIFT;
321+
e->asid = e_raw->asid;
322+
e->assigned = e_raw->assigned;
323+
e->pagesize = e_raw->pagesize;
324+
e->immutable = e_raw->immutable;
325+
326+
return 0;
281327
}
282328

283-
static struct rmpentry *__snp_lookup_rmpentry(u64 pfn, int *level)
329+
static int __snp_lookup_rmpentry(u64 pfn, struct rmpentry *e, int *level)
284330
{
285-
struct rmpentry *large_entry, *entry;
331+
struct rmpentry e_large;
332+
int ret;
286333

287334
if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP))
288-
return ERR_PTR(-ENODEV);
335+
return -ENODEV;
289336

290-
entry = get_rmpentry(pfn);
291-
if (IS_ERR(entry))
292-
return entry;
337+
ret = get_rmpentry(pfn, e);
338+
if (ret)
339+
return ret;
293340

294341
/*
295342
* Find the authoritative RMP entry for a PFN. This can be either a 4K
296343
* RMP entry or a special large RMP entry that is authoritative for a
297344
* whole 2M area.
298345
*/
299-
large_entry = get_rmpentry(pfn & PFN_PMD_MASK);
300-
if (IS_ERR(large_entry))
301-
return large_entry;
346+
ret = get_rmpentry(pfn & PFN_PMD_MASK, &e_large);
347+
if (ret)
348+
return ret;
302349

303-
*level = RMP_TO_PG_LEVEL(large_entry->pagesize);
350+
*level = RMP_TO_PG_LEVEL(e_large.pagesize);
304351

305-
return entry;
352+
return 0;
306353
}
307354

308355
int snp_lookup_rmpentry(u64 pfn, bool *assigned, int *level)
309356
{
310-
struct rmpentry *e;
357+
struct rmpentry e;
358+
int ret;
311359

312-
e = __snp_lookup_rmpentry(pfn, level);
313-
if (IS_ERR(e))
314-
return PTR_ERR(e);
360+
ret = __snp_lookup_rmpentry(pfn, &e, level);
361+
if (ret)
362+
return ret;
315363

316-
*assigned = !!e->assigned;
364+
*assigned = !!e.assigned;
317365
return 0;
318366
}
319367
EXPORT_SYMBOL_GPL(snp_lookup_rmpentry);
@@ -326,20 +374,28 @@ EXPORT_SYMBOL_GPL(snp_lookup_rmpentry);
326374
*/
327375
static void dump_rmpentry(u64 pfn)
328376
{
377+
struct rmpentry_raw *e_raw;
329378
u64 pfn_i, pfn_end;
330-
struct rmpentry *e;
331-
int level;
379+
struct rmpentry e;
380+
int level, ret;
332381

333-
e = __snp_lookup_rmpentry(pfn, &level);
334-
if (IS_ERR(e)) {
335-
pr_err("Failed to read RMP entry for PFN 0x%llx, error %ld\n",
336-
pfn, PTR_ERR(e));
382+
ret = __snp_lookup_rmpentry(pfn, &e, &level);
383+
if (ret) {
384+
pr_err("Failed to read RMP entry for PFN 0x%llx, error %d\n",
385+
pfn, ret);
337386
return;
338387
}
339388

340-
if (e->assigned) {
389+
if (e.assigned) {
390+
e_raw = get_raw_rmpentry(pfn);
391+
if (IS_ERR(e_raw)) {
392+
pr_err("Failed to read RMP contents for PFN 0x%llx, error %ld\n",
393+
pfn, PTR_ERR(e_raw));
394+
return;
395+
}
396+
341397
pr_info("PFN 0x%llx, RMP entry: [0x%016llx - 0x%016llx]\n",
342-
pfn, e->lo, e->hi);
398+
pfn, e_raw->lo, e_raw->hi);
343399
return;
344400
}
345401

@@ -358,16 +414,16 @@ static void dump_rmpentry(u64 pfn)
358414
pfn, pfn_i, pfn_end);
359415

360416
while (pfn_i < pfn_end) {
361-
e = __snp_lookup_rmpentry(pfn_i, &level);
362-
if (IS_ERR(e)) {
363-
pr_err("Error %ld reading RMP entry for PFN 0x%llx\n",
364-
PTR_ERR(e), pfn_i);
417+
e_raw = get_raw_rmpentry(pfn_i);
418+
if (IS_ERR(e_raw)) {
419+
pr_err("Error %ld reading RMP contents for PFN 0x%llx\n",
420+
PTR_ERR(e_raw), pfn_i);
365421
pfn_i++;
366422
continue;
367423
}
368424

369-
if (e->lo || e->hi)
370-
pr_info("PFN: 0x%llx, [0x%016llx - 0x%016llx]\n", pfn_i, e->lo, e->hi);
425+
if (e_raw->lo || e_raw->hi)
426+
pr_info("PFN: 0x%llx, [0x%016llx - 0x%016llx]\n", pfn_i, e_raw->lo, e_raw->hi);
371427
pfn_i++;
372428
}
373429
}

0 commit comments

Comments
 (0)