mirror of
https://github.com/torvalds/linux.git
synced 2024-09-20 23:13:00 +00:00
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 <brijesh.singh@amd.com> Co-developed-by: Ashish Kalra <ashish.kalra@amd.com> Signed-off-by: Ashish Kalra <ashish.kalra@amd.com> Signed-off-by: Michael Roth <michael.roth@amd.com> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Link: https://lore.kernel.org/r/20240126041126.1927228-17-michael.roth@amd.com
This commit is contained in:
parent
8dac642999
commit
24512afa43
|
@ -30,6 +30,7 @@
|
|||
#include <asm/smp.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/e820/types.h>
|
||||
#include <asm/sev.h>
|
||||
|
||||
#include "psp-dev.h"
|
||||
#include "sev-dev.h"
|
||||
|
@ -73,9 +74,14 @@ static int psp_timeout;
|
|||
* The TMR is a 1MB area that must be 1MB aligned. Use the page allocator
|
||||
* to allocate the memory, which will return aligned memory for the specified
|
||||
* allocation order.
|
||||
*
|
||||
* When SEV-SNP is enabled the TMR needs to be 2MB aligned and 2MB sized.
|
||||
*/
|
||||
#define SEV_ES_TMR_SIZE (1024 * 1024)
|
||||
#define SEV_TMR_SIZE (1024 * 1024)
|
||||
#define SNP_TMR_SIZE (2 * 1024 * 1024)
|
||||
|
||||
static void *sev_es_tmr;
|
||||
static size_t sev_es_tmr_size = SEV_TMR_SIZE;
|
||||
|
||||
/* INIT_EX NV Storage:
|
||||
* 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)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void *sev_fw_alloc(unsigned long len)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
page = alloc_pages(GFP_KERNEL, get_order(len));
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
return page_address(page);
|
||||
}
|
||||
|
||||
static struct file *open_file_as_root(const char *filename, int flags, umode_t mode)
|
||||
{
|
||||
struct file *fp;
|
||||
|
@ -333,6 +328,142 @@ static int sev_write_init_ex_file_if_required(int cmd_id)
|
|||
return sev_write_init_ex_file();
|
||||
}
|
||||
|
||||
/*
|
||||
* snp_reclaim_pages() needs __sev_do_cmd_locked(), and __sev_do_cmd_locked()
|
||||
* needs snp_reclaim_pages(), so a forward declaration is needed.
|
||||
*/
|
||||
static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret);
|
||||
|
||||
static int snp_reclaim_pages(unsigned long paddr, unsigned int npages, bool locked)
|
||||
{
|
||||
int ret, err, i;
|
||||
|
||||
paddr = __sme_clr(ALIGN_DOWN(paddr, PAGE_SIZE));
|
||||
|
||||
for (i = 0; i < npages; i++, paddr += PAGE_SIZE) {
|
||||
struct sev_data_snp_page_reclaim data = {0};
|
||||
|
||||
data.paddr = paddr;
|
||||
|
||||
if (locked)
|
||||
ret = __sev_do_cmd_locked(SEV_CMD_SNP_PAGE_RECLAIM, &data, &err);
|
||||
else
|
||||
ret = sev_do_cmd(SEV_CMD_SNP_PAGE_RECLAIM, &data, &err);
|
||||
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
ret = rmp_make_shared(__phys_to_pfn(paddr), PG_LEVEL_4K);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
/*
|
||||
* If there was a failure reclaiming the page then it is no longer safe
|
||||
* to release it back to the system; leak it instead.
|
||||
*/
|
||||
snp_leak_pages(__phys_to_pfn(paddr), npages - i);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rmp_mark_pages_firmware(unsigned long paddr, unsigned int npages, bool locked)
|
||||
{
|
||||
unsigned long pfn = __sme_clr(paddr) >> PAGE_SHIFT;
|
||||
int rc, i;
|
||||
|
||||
for (i = 0; i < npages; i++, pfn++) {
|
||||
rc = rmp_make_private(pfn, 0, PG_LEVEL_4K, 0, true);
|
||||
if (rc)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
/*
|
||||
* Try unrolling the firmware state changes by
|
||||
* reclaiming the pages which were already changed to the
|
||||
* firmware state.
|
||||
*/
|
||||
snp_reclaim_pages(paddr, i, locked);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct page *__snp_alloc_firmware_pages(gfp_t gfp_mask, int order)
|
||||
{
|
||||
unsigned long npages = 1ul << order, paddr;
|
||||
struct sev_device *sev;
|
||||
struct page *page;
|
||||
|
||||
if (!psp_master || !psp_master->sev_data)
|
||||
return NULL;
|
||||
|
||||
page = alloc_pages(gfp_mask, order);
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
/* If SEV-SNP is initialized then add the page in RMP table. */
|
||||
sev = psp_master->sev_data;
|
||||
if (!sev->snp_initialized)
|
||||
return page;
|
||||
|
||||
paddr = __pa((unsigned long)page_address(page));
|
||||
if (rmp_mark_pages_firmware(paddr, npages, false))
|
||||
return NULL;
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
void *snp_alloc_firmware_page(gfp_t gfp_mask)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
page = __snp_alloc_firmware_pages(gfp_mask, 0);
|
||||
|
||||
return page ? page_address(page) : NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snp_alloc_firmware_page);
|
||||
|
||||
static void __snp_free_firmware_pages(struct page *page, int order, bool locked)
|
||||
{
|
||||
struct sev_device *sev = psp_master->sev_data;
|
||||
unsigned long paddr, npages = 1ul << order;
|
||||
|
||||
if (!page)
|
||||
return;
|
||||
|
||||
paddr = __pa((unsigned long)page_address(page));
|
||||
if (sev->snp_initialized &&
|
||||
snp_reclaim_pages(paddr, npages, locked))
|
||||
return;
|
||||
|
||||
__free_pages(page, order);
|
||||
}
|
||||
|
||||
void snp_free_firmware_page(void *addr)
|
||||
{
|
||||
if (!addr)
|
||||
return;
|
||||
|
||||
__snp_free_firmware_pages(virt_to_page(addr), 0, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snp_free_firmware_page);
|
||||
|
||||
static void *sev_fw_alloc(unsigned long len)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
page = __snp_alloc_firmware_pages(GFP_KERNEL, get_order(len));
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
return page_address(page);
|
||||
}
|
||||
|
||||
static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret)
|
||||
{
|
||||
struct psp_device *psp = psp_master;
|
||||
|
@ -456,7 +587,7 @@ static int __sev_init_locked(int *error)
|
|||
data.tmr_address = __pa(sev_es_tmr);
|
||||
|
||||
data.flags |= SEV_INIT_FLAGS_SEV_ES;
|
||||
data.tmr_len = SEV_ES_TMR_SIZE;
|
||||
data.tmr_len = sev_es_tmr_size;
|
||||
}
|
||||
|
||||
return __sev_do_cmd_locked(SEV_CMD_INIT, &data, error);
|
||||
|
@ -479,7 +610,7 @@ static int __sev_init_ex_locked(int *error)
|
|||
data.tmr_address = __pa(sev_es_tmr);
|
||||
|
||||
data.flags |= SEV_INIT_FLAGS_SEV_ES;
|
||||
data.tmr_len = SEV_ES_TMR_SIZE;
|
||||
data.tmr_len = sev_es_tmr_size;
|
||||
}
|
||||
|
||||
return __sev_do_cmd_locked(SEV_CMD_INIT_EX, &data, error);
|
||||
|
@ -623,9 +754,27 @@ static int __sev_snp_init_locked(int *error)
|
|||
sev->snp_initialized = true;
|
||||
dev_dbg(sev->dev, "SEV-SNP firmware initialized\n");
|
||||
|
||||
sev_es_tmr_size = SNP_TMR_SIZE;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __sev_platform_init_handle_tmr(struct sev_device *sev)
|
||||
{
|
||||
if (sev_es_tmr)
|
||||
return;
|
||||
|
||||
/* Obtain the TMR memory area for SEV-ES use */
|
||||
sev_es_tmr = sev_fw_alloc(sev_es_tmr_size);
|
||||
if (sev_es_tmr) {
|
||||
/* Must flush the cache before giving it to the firmware */
|
||||
if (!sev->snp_initialized)
|
||||
clflush_cache_range(sev_es_tmr, sev_es_tmr_size);
|
||||
} else {
|
||||
dev_warn(sev->dev, "SEV: TMR allocation failed, SEV-ES support unavailable\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int __sev_platform_init_locked(int *error)
|
||||
{
|
||||
int rc, psp_ret = SEV_RET_NO_FW_CALL;
|
||||
|
@ -639,16 +788,7 @@ static int __sev_platform_init_locked(int *error)
|
|||
if (sev->state == SEV_STATE_INIT)
|
||||
return 0;
|
||||
|
||||
if (!sev_es_tmr) {
|
||||
/* Obtain the TMR memory area for SEV-ES use */
|
||||
sev_es_tmr = sev_fw_alloc(SEV_ES_TMR_SIZE);
|
||||
if (sev_es_tmr)
|
||||
/* Must flush the cache before giving it to the firmware */
|
||||
clflush_cache_range(sev_es_tmr, SEV_ES_TMR_SIZE);
|
||||
else
|
||||
dev_warn(sev->dev,
|
||||
"SEV: TMR allocation failed, SEV-ES support unavailable\n");
|
||||
}
|
||||
__sev_platform_init_handle_tmr(sev);
|
||||
|
||||
if (sev_init_ex_buffer) {
|
||||
rc = sev_read_init_ex_file();
|
||||
|
@ -1546,8 +1686,9 @@ static void sev_firmware_shutdown(struct sev_device *sev)
|
|||
/* The TMR area was encrypted, flush it from the cache */
|
||||
wbinvd_on_all_cpus();
|
||||
|
||||
free_pages((unsigned long)sev_es_tmr,
|
||||
get_order(SEV_ES_TMR_SIZE));
|
||||
__snp_free_firmware_pages(virt_to_page(sev_es_tmr),
|
||||
get_order(sev_es_tmr_size),
|
||||
false);
|
||||
sev_es_tmr = NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -932,6 +932,8 @@ int sev_guest_decommission(struct sev_data_decommission *data, int *error);
|
|||
int sev_do_cmd(int cmd, void *data, int *psp_ret);
|
||||
|
||||
void *psp_copy_user_blob(u64 uaddr, u32 len);
|
||||
void *snp_alloc_firmware_page(gfp_t mask);
|
||||
void snp_free_firmware_page(void *addr);
|
||||
|
||||
#else /* !CONFIG_CRYPTO_DEV_SP_PSP */
|
||||
|
||||
|
@ -959,6 +961,13 @@ sev_issue_cmd_external_user(struct file *filep, unsigned int id, void *data, int
|
|||
|
||||
static inline void *psp_copy_user_blob(u64 __user uaddr, u32 len) { return ERR_PTR(-EINVAL); }
|
||||
|
||||
static inline void *snp_alloc_firmware_page(gfp_t mask)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void snp_free_firmware_page(void *addr) { }
|
||||
|
||||
#endif /* CONFIG_CRYPTO_DEV_SP_PSP */
|
||||
|
||||
#endif /* __PSP_SEV_H__ */
|
||||
|
|
Loading…
Reference in New Issue
Block a user