Skip to content

Commit 24512af

Browse files
codomaniabp3tk0v
authored andcommitted
crypto: ccp: Handle the legacy TMR allocation when SNP is enabled
The behavior and requirement for the SEV-legacy command is altered when the SNP firmware is in the INIT state. See SEV-SNP firmware ABI specification for more details. Allocate the Trusted Memory Region (TMR) as a 2MB-sized/aligned region when SNP is enabled to satisfy new requirements for SNP. Continue allocating a 1MB-sized region for !SNP configuration. [ bp: Carve out TMR allocation into a helper. ] Signed-off-by: Brijesh Singh <[email protected]> Co-developed-by: Ashish Kalra <[email protected]> Signed-off-by: Ashish Kalra <[email protected]> Signed-off-by: Michael Roth <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 8dac642 commit 24512af

File tree

2 files changed

+176
-26
lines changed

2 files changed

+176
-26
lines changed

drivers/crypto/ccp/sev-dev.c

Lines changed: 167 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <asm/smp.h>
3131
#include <asm/cacheflush.h>
3232
#include <asm/e820/types.h>
33+
#include <asm/sev.h>
3334

3435
#include "psp-dev.h"
3536
#include "sev-dev.h"
@@ -73,9 +74,14 @@ static int psp_timeout;
7374
* The TMR is a 1MB area that must be 1MB aligned. Use the page allocator
7475
* to allocate the memory, which will return aligned memory for the specified
7576
* allocation order.
77+
*
78+
* When SEV-SNP is enabled the TMR needs to be 2MB aligned and 2MB sized.
7679
*/
77-
#define SEV_ES_TMR_SIZE (1024 * 1024)
80+
#define SEV_TMR_SIZE (1024 * 1024)
81+
#define SNP_TMR_SIZE (2 * 1024 * 1024)
82+
7883
static void *sev_es_tmr;
84+
static size_t sev_es_tmr_size = SEV_TMR_SIZE;
7985

8086
/* INIT_EX NV Storage:
8187
* The NV Storage is a 32Kb area and must be 4Kb page aligned. Use the page
@@ -192,17 +198,6 @@ static int sev_cmd_buffer_len(int cmd)
192198
return 0;
193199
}
194200

195-
static void *sev_fw_alloc(unsigned long len)
196-
{
197-
struct page *page;
198-
199-
page = alloc_pages(GFP_KERNEL, get_order(len));
200-
if (!page)
201-
return NULL;
202-
203-
return page_address(page);
204-
}
205-
206201
static struct file *open_file_as_root(const char *filename, int flags, umode_t mode)
207202
{
208203
struct file *fp;
@@ -333,6 +328,142 @@ static int sev_write_init_ex_file_if_required(int cmd_id)
333328
return sev_write_init_ex_file();
334329
}
335330

331+
/*
332+
* snp_reclaim_pages() needs __sev_do_cmd_locked(), and __sev_do_cmd_locked()
333+
* needs snp_reclaim_pages(), so a forward declaration is needed.
334+
*/
335+
static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret);
336+
337+
static int snp_reclaim_pages(unsigned long paddr, unsigned int npages, bool locked)
338+
{
339+
int ret, err, i;
340+
341+
paddr = __sme_clr(ALIGN_DOWN(paddr, PAGE_SIZE));
342+
343+
for (i = 0; i < npages; i++, paddr += PAGE_SIZE) {
344+
struct sev_data_snp_page_reclaim data = {0};
345+
346+
data.paddr = paddr;
347+
348+
if (locked)
349+
ret = __sev_do_cmd_locked(SEV_CMD_SNP_PAGE_RECLAIM, &data, &err);
350+
else
351+
ret = sev_do_cmd(SEV_CMD_SNP_PAGE_RECLAIM, &data, &err);
352+
353+
if (ret)
354+
goto cleanup;
355+
356+
ret = rmp_make_shared(__phys_to_pfn(paddr), PG_LEVEL_4K);
357+
if (ret)
358+
goto cleanup;
359+
}
360+
361+
return 0;
362+
363+
cleanup:
364+
/*
365+
* If there was a failure reclaiming the page then it is no longer safe
366+
* to release it back to the system; leak it instead.
367+
*/
368+
snp_leak_pages(__phys_to_pfn(paddr), npages - i);
369+
return ret;
370+
}
371+
372+
static int rmp_mark_pages_firmware(unsigned long paddr, unsigned int npages, bool locked)
373+
{
374+
unsigned long pfn = __sme_clr(paddr) >> PAGE_SHIFT;
375+
int rc, i;
376+
377+
for (i = 0; i < npages; i++, pfn++) {
378+
rc = rmp_make_private(pfn, 0, PG_LEVEL_4K, 0, true);
379+
if (rc)
380+
goto cleanup;
381+
}
382+
383+
return 0;
384+
385+
cleanup:
386+
/*
387+
* Try unrolling the firmware state changes by
388+
* reclaiming the pages which were already changed to the
389+
* firmware state.
390+
*/
391+
snp_reclaim_pages(paddr, i, locked);
392+
393+
return rc;
394+
}
395+
396+
static struct page *__snp_alloc_firmware_pages(gfp_t gfp_mask, int order)
397+
{
398+
unsigned long npages = 1ul << order, paddr;
399+
struct sev_device *sev;
400+
struct page *page;
401+
402+
if (!psp_master || !psp_master->sev_data)
403+
return NULL;
404+
405+
page = alloc_pages(gfp_mask, order);
406+
if (!page)
407+
return NULL;
408+
409+
/* If SEV-SNP is initialized then add the page in RMP table. */
410+
sev = psp_master->sev_data;
411+
if (!sev->snp_initialized)
412+
return page;
413+
414+
paddr = __pa((unsigned long)page_address(page));
415+
if (rmp_mark_pages_firmware(paddr, npages, false))
416+
return NULL;
417+
418+
return page;
419+
}
420+
421+
void *snp_alloc_firmware_page(gfp_t gfp_mask)
422+
{
423+
struct page *page;
424+
425+
page = __snp_alloc_firmware_pages(gfp_mask, 0);
426+
427+
return page ? page_address(page) : NULL;
428+
}
429+
EXPORT_SYMBOL_GPL(snp_alloc_firmware_page);
430+
431+
static void __snp_free_firmware_pages(struct page *page, int order, bool locked)
432+
{
433+
struct sev_device *sev = psp_master->sev_data;
434+
unsigned long paddr, npages = 1ul << order;
435+
436+
if (!page)
437+
return;
438+
439+
paddr = __pa((unsigned long)page_address(page));
440+
if (sev->snp_initialized &&
441+
snp_reclaim_pages(paddr, npages, locked))
442+
return;
443+
444+
__free_pages(page, order);
445+
}
446+
447+
void snp_free_firmware_page(void *addr)
448+
{
449+
if (!addr)
450+
return;
451+
452+
__snp_free_firmware_pages(virt_to_page(addr), 0, false);
453+
}
454+
EXPORT_SYMBOL_GPL(snp_free_firmware_page);
455+
456+
static void *sev_fw_alloc(unsigned long len)
457+
{
458+
struct page *page;
459+
460+
page = __snp_alloc_firmware_pages(GFP_KERNEL, get_order(len));
461+
if (!page)
462+
return NULL;
463+
464+
return page_address(page);
465+
}
466+
336467
static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret)
337468
{
338469
struct psp_device *psp = psp_master;
@@ -456,7 +587,7 @@ static int __sev_init_locked(int *error)
456587
data.tmr_address = __pa(sev_es_tmr);
457588

458589
data.flags |= SEV_INIT_FLAGS_SEV_ES;
459-
data.tmr_len = SEV_ES_TMR_SIZE;
590+
data.tmr_len = sev_es_tmr_size;
460591
}
461592

462593
return __sev_do_cmd_locked(SEV_CMD_INIT, &data, error);
@@ -479,7 +610,7 @@ static int __sev_init_ex_locked(int *error)
479610
data.tmr_address = __pa(sev_es_tmr);
480611

481612
data.flags |= SEV_INIT_FLAGS_SEV_ES;
482-
data.tmr_len = SEV_ES_TMR_SIZE;
613+
data.tmr_len = sev_es_tmr_size;
483614
}
484615

485616
return __sev_do_cmd_locked(SEV_CMD_INIT_EX, &data, error);
@@ -623,9 +754,27 @@ static int __sev_snp_init_locked(int *error)
623754
sev->snp_initialized = true;
624755
dev_dbg(sev->dev, "SEV-SNP firmware initialized\n");
625756

757+
sev_es_tmr_size = SNP_TMR_SIZE;
758+
626759
return rc;
627760
}
628761

762+
static void __sev_platform_init_handle_tmr(struct sev_device *sev)
763+
{
764+
if (sev_es_tmr)
765+
return;
766+
767+
/* Obtain the TMR memory area for SEV-ES use */
768+
sev_es_tmr = sev_fw_alloc(sev_es_tmr_size);
769+
if (sev_es_tmr) {
770+
/* Must flush the cache before giving it to the firmware */
771+
if (!sev->snp_initialized)
772+
clflush_cache_range(sev_es_tmr, sev_es_tmr_size);
773+
} else {
774+
dev_warn(sev->dev, "SEV: TMR allocation failed, SEV-ES support unavailable\n");
775+
}
776+
}
777+
629778
static int __sev_platform_init_locked(int *error)
630779
{
631780
int rc, psp_ret = SEV_RET_NO_FW_CALL;
@@ -639,16 +788,7 @@ static int __sev_platform_init_locked(int *error)
639788
if (sev->state == SEV_STATE_INIT)
640789
return 0;
641790

642-
if (!sev_es_tmr) {
643-
/* Obtain the TMR memory area for SEV-ES use */
644-
sev_es_tmr = sev_fw_alloc(SEV_ES_TMR_SIZE);
645-
if (sev_es_tmr)
646-
/* Must flush the cache before giving it to the firmware */
647-
clflush_cache_range(sev_es_tmr, SEV_ES_TMR_SIZE);
648-
else
649-
dev_warn(sev->dev,
650-
"SEV: TMR allocation failed, SEV-ES support unavailable\n");
651-
}
791+
__sev_platform_init_handle_tmr(sev);
652792

653793
if (sev_init_ex_buffer) {
654794
rc = sev_read_init_ex_file();
@@ -1546,8 +1686,9 @@ static void sev_firmware_shutdown(struct sev_device *sev)
15461686
/* The TMR area was encrypted, flush it from the cache */
15471687
wbinvd_on_all_cpus();
15481688

1549-
free_pages((unsigned long)sev_es_tmr,
1550-
get_order(SEV_ES_TMR_SIZE));
1689+
__snp_free_firmware_pages(virt_to_page(sev_es_tmr),
1690+
get_order(sev_es_tmr_size),
1691+
false);
15511692
sev_es_tmr = NULL;
15521693
}
15531694

include/linux/psp-sev.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,8 @@ int sev_guest_decommission(struct sev_data_decommission *data, int *error);
932932
int sev_do_cmd(int cmd, void *data, int *psp_ret);
933933

934934
void *psp_copy_user_blob(u64 uaddr, u32 len);
935+
void *snp_alloc_firmware_page(gfp_t mask);
936+
void snp_free_firmware_page(void *addr);
935937

936938
#else /* !CONFIG_CRYPTO_DEV_SP_PSP */
937939

@@ -959,6 +961,13 @@ sev_issue_cmd_external_user(struct file *filep, unsigned int id, void *data, int
959961

960962
static inline void *psp_copy_user_blob(u64 __user uaddr, u32 len) { return ERR_PTR(-EINVAL); }
961963

964+
static inline void *snp_alloc_firmware_page(gfp_t mask)
965+
{
966+
return NULL;
967+
}
968+
969+
static inline void snp_free_firmware_page(void *addr) { }
970+
962971
#endif /* CONFIG_CRYPTO_DEV_SP_PSP */
963972

964973
#endif /* __PSP_SEV_H__ */

0 commit comments

Comments
 (0)