Skip to content

Commit e09701d

Browse files
ashkalrabp3tk0v
authored andcommitted
crypto: ccp - Add new HV-Fixed page allocation/free API
When SEV-SNP is active, the TEE extended command header page and all output buffers for TEE extended commands (such as used by Seamless Firmware servicing support) must be in hypervisor-fixed state, assigned to the hypervisor and marked immutable in the RMP entrie(s). Add a new generic SEV API interface to allocate/free hypervisor fixed pages which abstracts hypervisor fixed page allocation/free for PSP sub devices. The API internally uses SNP_INIT_EX to transition pages to HV-Fixed page state. If SNP is not enabled then the allocator is simply a wrapper over alloc_pages() and __free_pages(). When the sub device free the pages, they are put on a free list and future allocation requests will try to re-use the freed pages from this list. But this list is not preserved across PSP driver load/unload hence this free/reuse support is only supported while PSP driver is loaded. As HV_FIXED page state is only changed at reboot, these pages are leaked as they cannot be returned back to the page allocator and then potentially allocated to guests, which will cause SEV-SNP guests to fail to start or terminate when accessing the HV_FIXED page. Suggested-by: Thomas Lendacky <Thomas.Lendacky@amd.com> Signed-off-by: Ashish Kalra <ashish.kalra@amd.com> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com> Acked-by: Herbert Xu <herbert@gondor.apana.org.au> Link: https://lore.kernel.org/cover.1758057691.git.ashish.kalra@amd.com
1 parent e4c00c4 commit e09701d

2 files changed

Lines changed: 185 additions & 0 deletions

File tree

drivers/crypto/ccp/sev-dev.c

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,21 @@ MODULE_FIRMWARE("amd/amd_sev_fam19h_model1xh.sbin"); /* 4th gen EPYC */
8282
static bool psp_dead;
8383
static int psp_timeout;
8484

85+
enum snp_hv_fixed_pages_state {
86+
ALLOCATED,
87+
HV_FIXED,
88+
};
89+
90+
struct snp_hv_fixed_pages_entry {
91+
struct list_head list;
92+
struct page *page;
93+
unsigned int order;
94+
bool free;
95+
enum snp_hv_fixed_pages_state page_state;
96+
};
97+
98+
static LIST_HEAD(snp_hv_fixed_pages);
99+
85100
/* Trusted Memory Region (TMR):
86101
* The TMR is a 1MB area that must be 1MB aligned. Use the page allocator
87102
* to allocate the memory, which will return aligned memory for the specified
@@ -1073,6 +1088,165 @@ static void snp_set_hsave_pa(void *arg)
10731088
wrmsrq(MSR_VM_HSAVE_PA, 0);
10741089
}
10751090

1091+
/* Hypervisor Fixed pages API interface */
1092+
static void snp_hv_fixed_pages_state_update(struct sev_device *sev,
1093+
enum snp_hv_fixed_pages_state page_state)
1094+
{
1095+
struct snp_hv_fixed_pages_entry *entry;
1096+
1097+
/* List is protected by sev_cmd_mutex */
1098+
lockdep_assert_held(&sev_cmd_mutex);
1099+
1100+
if (list_empty(&snp_hv_fixed_pages))
1101+
return;
1102+
1103+
list_for_each_entry(entry, &snp_hv_fixed_pages, list)
1104+
entry->page_state = page_state;
1105+
}
1106+
1107+
/*
1108+
* Allocate HV_FIXED pages in 2MB aligned sizes to ensure the whole
1109+
* 2MB pages are marked as HV_FIXED.
1110+
*/
1111+
struct page *snp_alloc_hv_fixed_pages(unsigned int num_2mb_pages)
1112+
{
1113+
struct psp_device *psp_master = psp_get_master_device();
1114+
struct snp_hv_fixed_pages_entry *entry;
1115+
struct sev_device *sev;
1116+
unsigned int order;
1117+
struct page *page;
1118+
1119+
if (!psp_master || !psp_master->sev_data)
1120+
return NULL;
1121+
1122+
sev = psp_master->sev_data;
1123+
1124+
order = get_order(PMD_SIZE * num_2mb_pages);
1125+
1126+
/*
1127+
* SNP_INIT_EX is protected by sev_cmd_mutex, therefore this list
1128+
* also needs to be protected using the same mutex.
1129+
*/
1130+
guard(mutex)(&sev_cmd_mutex);
1131+
1132+
/*
1133+
* This API uses SNP_INIT_EX to transition allocated pages to HV_Fixed
1134+
* page state, fail if SNP is already initialized.
1135+
*/
1136+
if (sev->snp_initialized)
1137+
return NULL;
1138+
1139+
/* Re-use freed pages that match the request */
1140+
list_for_each_entry(entry, &snp_hv_fixed_pages, list) {
1141+
/* Hypervisor fixed page allocator implements exact fit policy */
1142+
if (entry->order == order && entry->free) {
1143+
entry->free = false;
1144+
memset(page_address(entry->page), 0,
1145+
(1 << entry->order) * PAGE_SIZE);
1146+
return entry->page;
1147+
}
1148+
}
1149+
1150+
page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
1151+
if (!page)
1152+
return NULL;
1153+
1154+
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
1155+
if (!entry) {
1156+
__free_pages(page, order);
1157+
return NULL;
1158+
}
1159+
1160+
entry->page = page;
1161+
entry->order = order;
1162+
list_add_tail(&entry->list, &snp_hv_fixed_pages);
1163+
1164+
return page;
1165+
}
1166+
1167+
void snp_free_hv_fixed_pages(struct page *page)
1168+
{
1169+
struct psp_device *psp_master = psp_get_master_device();
1170+
struct snp_hv_fixed_pages_entry *entry, *nentry;
1171+
1172+
if (!psp_master || !psp_master->sev_data)
1173+
return;
1174+
1175+
/*
1176+
* SNP_INIT_EX is protected by sev_cmd_mutex, therefore this list
1177+
* also needs to be protected using the same mutex.
1178+
*/
1179+
guard(mutex)(&sev_cmd_mutex);
1180+
1181+
list_for_each_entry_safe(entry, nentry, &snp_hv_fixed_pages, list) {
1182+
if (entry->page != page)
1183+
continue;
1184+
1185+
/*
1186+
* HV_FIXED page state cannot be changed until reboot
1187+
* and they cannot be used by an SNP guest, so they cannot
1188+
* be returned back to the page allocator.
1189+
* Mark the pages as free internally to allow possible re-use.
1190+
*/
1191+
if (entry->page_state == HV_FIXED) {
1192+
entry->free = true;
1193+
} else {
1194+
__free_pages(page, entry->order);
1195+
list_del(&entry->list);
1196+
kfree(entry);
1197+
}
1198+
return;
1199+
}
1200+
}
1201+
1202+
static void snp_add_hv_fixed_pages(struct sev_device *sev, struct sev_data_range_list *range_list)
1203+
{
1204+
struct snp_hv_fixed_pages_entry *entry;
1205+
struct sev_data_range *range;
1206+
int num_elements;
1207+
1208+
lockdep_assert_held(&sev_cmd_mutex);
1209+
1210+
if (list_empty(&snp_hv_fixed_pages))
1211+
return;
1212+
1213+
num_elements = list_count_nodes(&snp_hv_fixed_pages) +
1214+
range_list->num_elements;
1215+
1216+
/*
1217+
* Ensure the list of HV_FIXED pages that will be passed to firmware
1218+
* do not exceed the page-sized argument buffer.
1219+
*/
1220+
if (num_elements * sizeof(*range) + sizeof(*range_list) > PAGE_SIZE) {
1221+
dev_warn(sev->dev, "Additional HV_Fixed pages cannot be accommodated, omitting\n");
1222+
return;
1223+
}
1224+
1225+
range = &range_list->ranges[range_list->num_elements];
1226+
list_for_each_entry(entry, &snp_hv_fixed_pages, list) {
1227+
range->base = page_to_pfn(entry->page) << PAGE_SHIFT;
1228+
range->page_count = 1 << entry->order;
1229+
range++;
1230+
}
1231+
range_list->num_elements = num_elements;
1232+
}
1233+
1234+
static void snp_leak_hv_fixed_pages(void)
1235+
{
1236+
struct snp_hv_fixed_pages_entry *entry;
1237+
1238+
/* List is protected by sev_cmd_mutex */
1239+
lockdep_assert_held(&sev_cmd_mutex);
1240+
1241+
if (list_empty(&snp_hv_fixed_pages))
1242+
return;
1243+
1244+
list_for_each_entry(entry, &snp_hv_fixed_pages, list)
1245+
if (entry->page_state == HV_FIXED)
1246+
__snp_leak_pages(page_to_pfn(entry->page),
1247+
1 << entry->order, false);
1248+
}
1249+
10761250
static int snp_filter_reserved_mem_regions(struct resource *rs, void *arg)
10771251
{
10781252
struct sev_data_range_list *range_list = arg;
@@ -1163,6 +1337,12 @@ static int __sev_snp_init_locked(int *error)
11631337
return rc;
11641338
}
11651339

1340+
/*
1341+
* Add HV_Fixed pages from other PSP sub-devices, such as SFS to the
1342+
* HV_Fixed page list.
1343+
*/
1344+
snp_add_hv_fixed_pages(sev, snp_range_list);
1345+
11661346
memset(&data, 0, sizeof(data));
11671347
data.init_rmp = 1;
11681348
data.list_paddr_en = 1;
@@ -1202,6 +1382,7 @@ static int __sev_snp_init_locked(int *error)
12021382
return rc;
12031383
}
12041384

1385+
snp_hv_fixed_pages_state_update(sev, HV_FIXED);
12051386
sev->snp_initialized = true;
12061387
dev_dbg(sev->dev, "SEV-SNP firmware initialized\n");
12071388

@@ -1784,6 +1965,7 @@ static int __sev_snp_shutdown_locked(int *error, bool panic)
17841965
return ret;
17851966
}
17861967

1968+
snp_leak_hv_fixed_pages();
17871969
sev->snp_initialized = false;
17881970
dev_dbg(sev->dev, "SEV-SNP firmware shutdown\n");
17891971

drivers/crypto/ccp/sev-dev.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,7 @@ void sev_dev_destroy(struct psp_device *psp);
6565
void sev_pci_init(void);
6666
void sev_pci_exit(void);
6767

68+
struct page *snp_alloc_hv_fixed_pages(unsigned int num_2mb_pages);
69+
void snp_free_hv_fixed_pages(struct page *page);
70+
6871
#endif /* __SEV_DEV_H */

0 commit comments

Comments
 (0)