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:
Linus Torvalds
2025-09-30 13:40:35 -07:00
61 changed files with 2043 additions and 709 deletions

View File

@@ -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");