- Fix the incorrect handling of atomic offset updates in reserve_eilvt_offset() The check for the return value of atomic_cmpxchg() is not compared against the old value, it is compared against the new value, which makes it two round on success. Convert it to atomic_try_cmpxchg() which does the right thing. - Handle IO/APIC less systems correctly When IO/APIC is not advertised by ACPI then the computation of the lower bound for dynamically allocated interrupts like MSI goes wrong. This lower bound is used to exclude the IO/APIC legacy GSI space as that must stay reserved for the legacy interrupts. In case that the system, e.g. VM, does not advertise an IO/APIC the lower bound stays at 0. 0 is an invalid interrupt number except for the legacy timer interrupt on x86. The return value is unchecked in the core code, so it ends up to allocate interrupt number 0 which is subsequently considered to be invalid by the caller, e.g. the MSI allocation code. A similar problem was already cured for device tree based systems years ago, but that missed - or did not envision - the zero IO/APIC case. Consolidate the zero check and return the provided "from" argument to the core code call site, which is guaranteed to be greater than 0. - Simplify the X2APIC cluster CPU mask logic for CPU hotplug Per cluster CPU masks are required for X2APIC in cluster mode to determine the correct cluster for a target CPU when calculating the destination for IPIs These masks are established when CPUs are borught up. The first CPU in a cluster must allocate a new cluster CPU mask. As this happens during the early startup of a CPU, where memory allocations cannot be done, the mask has to be allocated by the control CPU. The current implementation allocates a clustermask just in case and if the to be brought up CPU is the first in a cluster the CPU takes over this allocation from a global pointer. This works nicely in the fully serialized CPU bringup scenario which is used today, but would fail completely for parallel bringup of CPUs. The cluster association of a CPU can be computed from the APIC ID which is enumerated by ACPI/MADT. So the cluster CPU masks can be preallocated and associated upfront and the upcoming CPUs just need to set their corresponding bit. Aside of preparing for parallel bringup this is a valuable simplification on its own. - Remove global variables which control the early startup of secondary CPUs on 64-bit The only information which is needed by a starting CPU is the Linux CPU number. The CPU number allows it to retrieve the rest of the required data from already existing per CPU storage. So instead of initial_stack, early_gdt_desciptor and initial_gs provide a new variable smpboot_control which contains the Linux CPU number for now. The starting CPU can retrieve and compute all required information for startup from there. Aside of being a cleanup, this is also preparing for parallel CPU bringup, where starting CPUs will look up their Linux CPU number via the APIC ID, when smpboot_control has the corresponding control bit set. - Make cc_vendor globally accesible Subsequent parallel bringup changes require access to cc_vendor because confidental computing platforms need special treatment in the early startup phase vs. CPUID and APCI ID readouts. The change makes cc_vendor global and provides stub accessors in case that CONFIG_ARCH_HAS_CC_PLATFORM is not set. This was merged from the x86/cc branch in anticipation of further parallel bringup commits which require access to cc_vendor. Due to late discoveries of fundamental issue with those patches these commits never happened. The merge commit is unfortunately in the middle of the APIC commits so unraveling it would have required a rebase or revert. As the parallel bringup seems to be well on its way for 6.5 this would be just pointless churn. As the commit does not contain any functional change it's not a risk to keep it. -----BEGIN PGP SIGNATURE----- iQJHBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmRGuAwTHHRnbHhAbGlu dXRyb25peC5kZQAKCRCmGPVMDXSYoRzSEADEx1sVkd2yrLcTYdpjdKbbUaDJ6lR0 DXxIP3+ApGHmV9l9yIh+/5C2oEJsiUfFf1vdh6ajv5iXpksCKzcUzkW5g3w7nM36 CSpULpFjwvaq8TIo0o1PIhAbo/yIMMzJVDs8R0reCnWgGAWZoW/a9Ndcvcicd0an pQAlkw3FD5r92mcMlKPNWFoui1AkScGEV02zJ7884MAukmBZwD8Jd+gE6eQC9GKa 9hyJiB77st1URl+a0cPsPYvv8RLVuVcljWsh2edyvxgovIO56+BoEjbrgRSF6cqQ Bhzo//3KgbUJ1y+YqH01aKZzY0hRpbAi2Rew4RBKcBKwCGd2qltUQG0LFNxAtV83 RsC573wSCGSCGO5Xb1RVXih5is+9YqMqitJNWvEc15jjOA9nwoLc80axP11v42f9 Xl4iGHQTWVGdxT4H22NH7UCuRlGg38vAx+In2HGpN/e57q2ighESjiGuqQAQpLel pbOeJtQ/D2xXVKcCap4T/P/2x5ls7bsc76MWJBMcYC3pRgJ5M7ZHw7wTw0IAty4x xCfR1bsRVEAhrE9r/odgNipXjBJu+CdGBAupNEIiRyq1QiwUKtMTayasRGUlbYO6 vrieHKqoflzRVg2M9Bgm3oI28X27FzZHWAZJW2oJ2Wnn2jL5kuRJa1nEykqo8pEP j6rjnScRVvdpIw== =IQWG -----END PGP SIGNATURE----- Merge tag 'x86-apic-2023-04-24' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull x86 APIC updates from Thomas Gleixner: - Fix the incorrect handling of atomic offset updates in reserve_eilvt_offset() The check for the return value of atomic_cmpxchg() is not compared against the old value, it is compared against the new value, which makes it two round on success. Convert it to atomic_try_cmpxchg() which does the right thing. - Handle IO/APIC less systems correctly When IO/APIC is not advertised by ACPI then the computation of the lower bound for dynamically allocated interrupts like MSI goes wrong. This lower bound is used to exclude the IO/APIC legacy GSI space as that must stay reserved for the legacy interrupts. In case that the system, e.g. VM, does not advertise an IO/APIC the lower bound stays at 0. 0 is an invalid interrupt number except for the legacy timer interrupt on x86. The return value is unchecked in the core code, so it ends up to allocate interrupt number 0 which is subsequently considered to be invalid by the caller, e.g. the MSI allocation code. A similar problem was already cured for device tree based systems years ago, but that missed - or did not envision - the zero IO/APIC case. Consolidate the zero check and return the provided "from" argument to the core code call site, which is guaranteed to be greater than 0. - Simplify the X2APIC cluster CPU mask logic for CPU hotplug Per cluster CPU masks are required for X2APIC in cluster mode to determine the correct cluster for a target CPU when calculating the destination for IPIs These masks are established when CPUs are borught up. The first CPU in a cluster must allocate a new cluster CPU mask. As this happens during the early startup of a CPU, where memory allocations cannot be done, the mask has to be allocated by the control CPU. The current implementation allocates a clustermask just in case and if the to be brought up CPU is the first in a cluster the CPU takes over this allocation from a global pointer. This works nicely in the fully serialized CPU bringup scenario which is used today, but would fail completely for parallel bringup of CPUs. The cluster association of a CPU can be computed from the APIC ID which is enumerated by ACPI/MADT. So the cluster CPU masks can be preallocated and associated upfront and the upcoming CPUs just need to set their corresponding bit. Aside of preparing for parallel bringup this is a valuable simplification on its own. - Remove global variables which control the early startup of secondary CPUs on 64-bit The only information which is needed by a starting CPU is the Linux CPU number. The CPU number allows it to retrieve the rest of the required data from already existing per CPU storage. So instead of initial_stack, early_gdt_desciptor and initial_gs provide a new variable smpboot_control which contains the Linux CPU number for now. The starting CPU can retrieve and compute all required information for startup from there. Aside of being a cleanup, this is also preparing for parallel CPU bringup, where starting CPUs will look up their Linux CPU number via the APIC ID, when smpboot_control has the corresponding control bit set. - Make cc_vendor globally accesible Subsequent parallel bringup changes require access to cc_vendor because confidental computing platforms need special treatment in the early startup phase vs. CPUID and APCI ID readouts. The change makes cc_vendor global and provides stub accessors in case that CONFIG_ARCH_HAS_CC_PLATFORM is not set. This was merged from the x86/cc branch in anticipation of further parallel bringup commits which require access to cc_vendor. Due to late discoveries of fundamental issue with those patches these commits never happened. The merge commit is unfortunately in the middle of the APIC commits so unraveling it would have required a rebase or revert. As the parallel bringup seems to be well on its way for 6.5 this would be just pointless churn. As the commit does not contain any functional change it's not a risk to keep it. * tag 'x86-apic-2023-04-24' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/ioapic: Don't return 0 from arch_dynirq_lower_bound() x86/apic: Fix atomic update of offset in reserve_eilvt_offset() x86/coco: Export cc_vendor x86/smpboot: Reference count on smpboot_setup_warm_reset_vector() x86/smpboot: Remove initial_gs x86/smpboot: Remove early_gdt_descr on 64-bit x86/smpboot: Remove initial_stack on 64-bit x86/apic/x2apic: Allow CPU cluster_mask to be populated in parallel
156 lines
3.5 KiB
C
156 lines
3.5 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Confidential Computing Platform Capability checks
|
|
*
|
|
* Copyright (C) 2021 Advanced Micro Devices, Inc.
|
|
*
|
|
* Author: Tom Lendacky <thomas.lendacky@amd.com>
|
|
*/
|
|
|
|
#include <linux/export.h>
|
|
#include <linux/cc_platform.h>
|
|
|
|
#include <asm/coco.h>
|
|
#include <asm/processor.h>
|
|
|
|
enum cc_vendor cc_vendor __ro_after_init;
|
|
static u64 cc_mask __ro_after_init;
|
|
|
|
static bool intel_cc_platform_has(enum cc_attr attr)
|
|
{
|
|
switch (attr) {
|
|
case CC_ATTR_GUEST_UNROLL_STRING_IO:
|
|
case CC_ATTR_HOTPLUG_DISABLED:
|
|
case CC_ATTR_GUEST_MEM_ENCRYPT:
|
|
case CC_ATTR_MEM_ENCRYPT:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle the SEV-SNP vTOM case where sme_me_mask is zero, and
|
|
* the other levels of SME/SEV functionality, including C-bit
|
|
* based SEV-SNP, are not enabled.
|
|
*/
|
|
static __maybe_unused bool amd_cc_platform_vtom(enum cc_attr attr)
|
|
{
|
|
switch (attr) {
|
|
case CC_ATTR_GUEST_MEM_ENCRYPT:
|
|
case CC_ATTR_MEM_ENCRYPT:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* SME and SEV are very similar but they are not the same, so there are
|
|
* times that the kernel will need to distinguish between SME and SEV. The
|
|
* cc_platform_has() function is used for this. When a distinction isn't
|
|
* needed, the CC_ATTR_MEM_ENCRYPT attribute can be used.
|
|
*
|
|
* The trampoline code is a good example for this requirement. Before
|
|
* paging is activated, SME will access all memory as decrypted, but SEV
|
|
* will access all memory as encrypted. So, when APs are being brought
|
|
* up under SME the trampoline area cannot be encrypted, whereas under SEV
|
|
* the trampoline area must be encrypted.
|
|
*/
|
|
|
|
static bool amd_cc_platform_has(enum cc_attr attr)
|
|
{
|
|
#ifdef CONFIG_AMD_MEM_ENCRYPT
|
|
|
|
if (sev_status & MSR_AMD64_SNP_VTOM)
|
|
return amd_cc_platform_vtom(attr);
|
|
|
|
switch (attr) {
|
|
case CC_ATTR_MEM_ENCRYPT:
|
|
return sme_me_mask;
|
|
|
|
case CC_ATTR_HOST_MEM_ENCRYPT:
|
|
return sme_me_mask && !(sev_status & MSR_AMD64_SEV_ENABLED);
|
|
|
|
case CC_ATTR_GUEST_MEM_ENCRYPT:
|
|
return sev_status & MSR_AMD64_SEV_ENABLED;
|
|
|
|
case CC_ATTR_GUEST_STATE_ENCRYPT:
|
|
return sev_status & MSR_AMD64_SEV_ES_ENABLED;
|
|
|
|
/*
|
|
* With SEV, the rep string I/O instructions need to be unrolled
|
|
* but SEV-ES supports them through the #VC handler.
|
|
*/
|
|
case CC_ATTR_GUEST_UNROLL_STRING_IO:
|
|
return (sev_status & MSR_AMD64_SEV_ENABLED) &&
|
|
!(sev_status & MSR_AMD64_SEV_ES_ENABLED);
|
|
|
|
case CC_ATTR_GUEST_SEV_SNP:
|
|
return sev_status & MSR_AMD64_SEV_SNP_ENABLED;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool cc_platform_has(enum cc_attr attr)
|
|
{
|
|
switch (cc_vendor) {
|
|
case CC_VENDOR_AMD:
|
|
return amd_cc_platform_has(attr);
|
|
case CC_VENDOR_INTEL:
|
|
return intel_cc_platform_has(attr);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(cc_platform_has);
|
|
|
|
u64 cc_mkenc(u64 val)
|
|
{
|
|
/*
|
|
* Both AMD and Intel use a bit in the page table to indicate
|
|
* encryption status of the page.
|
|
*
|
|
* - for AMD, bit *set* means the page is encrypted
|
|
* - for AMD with vTOM and for Intel, *clear* means encrypted
|
|
*/
|
|
switch (cc_vendor) {
|
|
case CC_VENDOR_AMD:
|
|
if (sev_status & MSR_AMD64_SNP_VTOM)
|
|
return val & ~cc_mask;
|
|
else
|
|
return val | cc_mask;
|
|
case CC_VENDOR_INTEL:
|
|
return val & ~cc_mask;
|
|
default:
|
|
return val;
|
|
}
|
|
}
|
|
|
|
u64 cc_mkdec(u64 val)
|
|
{
|
|
/* See comment in cc_mkenc() */
|
|
switch (cc_vendor) {
|
|
case CC_VENDOR_AMD:
|
|
if (sev_status & MSR_AMD64_SNP_VTOM)
|
|
return val | cc_mask;
|
|
else
|
|
return val & ~cc_mask;
|
|
case CC_VENDOR_INTEL:
|
|
return val | cc_mask;
|
|
default:
|
|
return val;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(cc_mkdec);
|
|
|
|
__init void cc_set_mask(u64 mask)
|
|
{
|
|
cc_mask = mask;
|
|
}
|