mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 14:53:58 -04:00
Merge tag 'x86_apic_for_v6.18_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 SEV and apic updates from Borislav Petkov: - Add functionality to provide runtime firmware updates for the non-x86 parts of an AMD platform like the security processor (ASP) firmware, modules etc, for example. The intent being that these updates are interim, live fixups before a proper BIOS update can be attempted - Add guest support for AMD's Secure AVIC feature which gives encrypted guests the needed protection against a malicious hypervisor generating unexpected interrupts and injecting them into such guest, thus interfering with its operation in an unexpected and negative manner. The advantage of this scheme is that the guest determines which interrupts and when to accept them vs leaving that to the benevolence (or not) of the hypervisor - Strictly separate the startup code from the rest of the kernel where former is executed from the initial 1:1 mapping of memory. The problem was that the toolchain-generated version of the code was being executed from a different mapping of memory than what was "assumed" during code generation, needing an ever-growing pile of fixups for absolute memory references which are invalid in the early, 1:1 memory mapping during boot. The major advantage of this is that there's no need to check the 1:1 mapping portion of the code for absolute relocations anymore and get rid of the RIP_REL_REF() macro sprinkling all over the place. For more info, see Ard's very detailed writeup on this [1] - The usual cleanups and fixes Link: https://lore.kernel.org/r/CAMj1kXEzKEuePEiHB%2BHxvfQbFz0sTiHdn4B%2B%2BzVBJ2mhkPkQ4Q@mail.gmail.com [1] * tag 'x86_apic_for_v6.18_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (49 commits) x86/boot: Drop erroneous __init annotation from early_set_pages_state() crypto: ccp - Add AMD Seamless Firmware Servicing (SFS) driver crypto: ccp - Add new HV-Fixed page allocation/free API x86/sev: Add new dump_rmp parameter to snp_leak_pages() API x86/startup/sev: Document the CPUID flow in the boot #VC handler objtool: Ignore __pi___cfi_ prefixed symbols x86/sev: Zap snp_abort() x86/apic/savic: Do not use snp_abort() x86/boot: Get rid of the .head.text section x86/boot: Move startup code out of __head section efistub/x86: Remap inittext read-execute when needed x86/boot: Create a confined code area for startup code x86/kbuild: Incorporate boot/startup/ via Kbuild makefile x86/boot: Revert "Reject absolute references in .head.text" x86/boot: Check startup code for absence of absolute relocations objtool: Add action to check for absence of absolute relocations x86/sev: Export startup routines for later use x86/sev: Move __sev_[get|put]_ghcb() into separate noinstr object x86/sev: Provide PIC aliases for SEV related data objects x86/boot: Provide PIC aliases for 5-level paging related constants ...
This commit is contained in:
@@ -82,6 +82,21 @@ MODULE_FIRMWARE("amd/amd_sev_fam19h_model1xh.sbin"); /* 4th gen EPYC */
|
||||
static bool psp_dead;
|
||||
static int psp_timeout;
|
||||
|
||||
enum snp_hv_fixed_pages_state {
|
||||
ALLOCATED,
|
||||
HV_FIXED,
|
||||
};
|
||||
|
||||
struct snp_hv_fixed_pages_entry {
|
||||
struct list_head list;
|
||||
struct page *page;
|
||||
unsigned int order;
|
||||
bool free;
|
||||
enum snp_hv_fixed_pages_state page_state;
|
||||
};
|
||||
|
||||
static LIST_HEAD(snp_hv_fixed_pages);
|
||||
|
||||
/* Trusted Memory Region (TMR):
|
||||
* 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
|
||||
@@ -1073,6 +1088,165 @@ static void snp_set_hsave_pa(void *arg)
|
||||
wrmsrq(MSR_VM_HSAVE_PA, 0);
|
||||
}
|
||||
|
||||
/* Hypervisor Fixed pages API interface */
|
||||
static void snp_hv_fixed_pages_state_update(struct sev_device *sev,
|
||||
enum snp_hv_fixed_pages_state page_state)
|
||||
{
|
||||
struct snp_hv_fixed_pages_entry *entry;
|
||||
|
||||
/* List is protected by sev_cmd_mutex */
|
||||
lockdep_assert_held(&sev_cmd_mutex);
|
||||
|
||||
if (list_empty(&snp_hv_fixed_pages))
|
||||
return;
|
||||
|
||||
list_for_each_entry(entry, &snp_hv_fixed_pages, list)
|
||||
entry->page_state = page_state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate HV_FIXED pages in 2MB aligned sizes to ensure the whole
|
||||
* 2MB pages are marked as HV_FIXED.
|
||||
*/
|
||||
struct page *snp_alloc_hv_fixed_pages(unsigned int num_2mb_pages)
|
||||
{
|
||||
struct psp_device *psp_master = psp_get_master_device();
|
||||
struct snp_hv_fixed_pages_entry *entry;
|
||||
struct sev_device *sev;
|
||||
unsigned int order;
|
||||
struct page *page;
|
||||
|
||||
if (!psp_master || !psp_master->sev_data)
|
||||
return NULL;
|
||||
|
||||
sev = psp_master->sev_data;
|
||||
|
||||
order = get_order(PMD_SIZE * num_2mb_pages);
|
||||
|
||||
/*
|
||||
* SNP_INIT_EX is protected by sev_cmd_mutex, therefore this list
|
||||
* also needs to be protected using the same mutex.
|
||||
*/
|
||||
guard(mutex)(&sev_cmd_mutex);
|
||||
|
||||
/*
|
||||
* This API uses SNP_INIT_EX to transition allocated pages to HV_Fixed
|
||||
* page state, fail if SNP is already initialized.
|
||||
*/
|
||||
if (sev->snp_initialized)
|
||||
return NULL;
|
||||
|
||||
/* Re-use freed pages that match the request */
|
||||
list_for_each_entry(entry, &snp_hv_fixed_pages, list) {
|
||||
/* Hypervisor fixed page allocator implements exact fit policy */
|
||||
if (entry->order == order && entry->free) {
|
||||
entry->free = false;
|
||||
memset(page_address(entry->page), 0,
|
||||
(1 << entry->order) * PAGE_SIZE);
|
||||
return entry->page;
|
||||
}
|
||||
}
|
||||
|
||||
page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry) {
|
||||
__free_pages(page, order);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entry->page = page;
|
||||
entry->order = order;
|
||||
list_add_tail(&entry->list, &snp_hv_fixed_pages);
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
void snp_free_hv_fixed_pages(struct page *page)
|
||||
{
|
||||
struct psp_device *psp_master = psp_get_master_device();
|
||||
struct snp_hv_fixed_pages_entry *entry, *nentry;
|
||||
|
||||
if (!psp_master || !psp_master->sev_data)
|
||||
return;
|
||||
|
||||
/*
|
||||
* SNP_INIT_EX is protected by sev_cmd_mutex, therefore this list
|
||||
* also needs to be protected using the same mutex.
|
||||
*/
|
||||
guard(mutex)(&sev_cmd_mutex);
|
||||
|
||||
list_for_each_entry_safe(entry, nentry, &snp_hv_fixed_pages, list) {
|
||||
if (entry->page != page)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* HV_FIXED page state cannot be changed until reboot
|
||||
* and they cannot be used by an SNP guest, so they cannot
|
||||
* be returned back to the page allocator.
|
||||
* Mark the pages as free internally to allow possible re-use.
|
||||
*/
|
||||
if (entry->page_state == HV_FIXED) {
|
||||
entry->free = true;
|
||||
} else {
|
||||
__free_pages(page, entry->order);
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void snp_add_hv_fixed_pages(struct sev_device *sev, struct sev_data_range_list *range_list)
|
||||
{
|
||||
struct snp_hv_fixed_pages_entry *entry;
|
||||
struct sev_data_range *range;
|
||||
int num_elements;
|
||||
|
||||
lockdep_assert_held(&sev_cmd_mutex);
|
||||
|
||||
if (list_empty(&snp_hv_fixed_pages))
|
||||
return;
|
||||
|
||||
num_elements = list_count_nodes(&snp_hv_fixed_pages) +
|
||||
range_list->num_elements;
|
||||
|
||||
/*
|
||||
* Ensure the list of HV_FIXED pages that will be passed to firmware
|
||||
* do not exceed the page-sized argument buffer.
|
||||
*/
|
||||
if (num_elements * sizeof(*range) + sizeof(*range_list) > PAGE_SIZE) {
|
||||
dev_warn(sev->dev, "Additional HV_Fixed pages cannot be accommodated, omitting\n");
|
||||
return;
|
||||
}
|
||||
|
||||
range = &range_list->ranges[range_list->num_elements];
|
||||
list_for_each_entry(entry, &snp_hv_fixed_pages, list) {
|
||||
range->base = page_to_pfn(entry->page) << PAGE_SHIFT;
|
||||
range->page_count = 1 << entry->order;
|
||||
range++;
|
||||
}
|
||||
range_list->num_elements = num_elements;
|
||||
}
|
||||
|
||||
static void snp_leak_hv_fixed_pages(void)
|
||||
{
|
||||
struct snp_hv_fixed_pages_entry *entry;
|
||||
|
||||
/* List is protected by sev_cmd_mutex */
|
||||
lockdep_assert_held(&sev_cmd_mutex);
|
||||
|
||||
if (list_empty(&snp_hv_fixed_pages))
|
||||
return;
|
||||
|
||||
list_for_each_entry(entry, &snp_hv_fixed_pages, list)
|
||||
if (entry->page_state == HV_FIXED)
|
||||
__snp_leak_pages(page_to_pfn(entry->page),
|
||||
1 << entry->order, false);
|
||||
}
|
||||
|
||||
static int snp_filter_reserved_mem_regions(struct resource *rs, void *arg)
|
||||
{
|
||||
struct sev_data_range_list *range_list = arg;
|
||||
@@ -1163,6 +1337,12 @@ static int __sev_snp_init_locked(int *error)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add HV_Fixed pages from other PSP sub-devices, such as SFS to the
|
||||
* HV_Fixed page list.
|
||||
*/
|
||||
snp_add_hv_fixed_pages(sev, snp_range_list);
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.init_rmp = 1;
|
||||
data.list_paddr_en = 1;
|
||||
@@ -1202,6 +1382,7 @@ static int __sev_snp_init_locked(int *error)
|
||||
return rc;
|
||||
}
|
||||
|
||||
snp_hv_fixed_pages_state_update(sev, HV_FIXED);
|
||||
sev->snp_initialized = true;
|
||||
dev_dbg(sev->dev, "SEV-SNP firmware initialized\n");
|
||||
|
||||
@@ -1784,6 +1965,7 @@ static int __sev_snp_shutdown_locked(int *error, bool panic)
|
||||
return ret;
|
||||
}
|
||||
|
||||
snp_leak_hv_fixed_pages();
|
||||
sev->snp_initialized = false;
|
||||
dev_dbg(sev->dev, "SEV-SNP firmware shutdown\n");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user