Commit 3928aa3f authored by Suravee Suthikulpanit's avatar Suravee Suthikulpanit Committed by Joerg Roedel

iommu/amd: Detect and enable guest vAPIC support

This patch introduces a new IOMMU driver parameter, amd_iommu_guest_ir,
which can be used to specify different interrupt remapping mode for
passthrough devices to VM guest:
    * legacy: Legacy interrupt remapping (w/ 32-bit IRTE)
    * vapic : Guest vAPIC interrupt remapping (w/ GA mode 128-bit IRTE)

Note that in vapic mode, it can also supports legacy interrupt remapping
for non-passthrough devices with the 128-bit IRTE.
Signed-off-by: default avatarSuravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Signed-off-by: default avatarJoerg Roedel <jroedel@suse.de>
parent 29b4817d
...@@ -460,6 +460,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ...@@ -460,6 +460,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
driver will print ACPI tables for AMD IOMMU during driver will print ACPI tables for AMD IOMMU during
IOMMU initialization. IOMMU initialization.
amd_iommu_intr= [HW,X86-64]
Specifies one of the following AMD IOMMU interrupt
remapping modes:
legacy - Use legacy interrupt remapping mode.
vapic - Use virtual APIC mode, which allows IOMMU
to inject interrupts directly into guest.
This mode requires kvm-amd.avic=1.
(Default when IOMMU HW support is present.)
amijoy.map= [HW,JOY] Amiga joystick support amijoy.map= [HW,JOY] Amiga joystick support
Map of devices attached to JOY0DAT and JOY1DAT Map of devices attached to JOY0DAT and JOY1DAT
Format: <a>,<b> Format: <a>,<b>
......
...@@ -145,6 +145,8 @@ struct ivmd_header { ...@@ -145,6 +145,8 @@ struct ivmd_header {
bool amd_iommu_dump; bool amd_iommu_dump;
bool amd_iommu_irq_remap __read_mostly; bool amd_iommu_irq_remap __read_mostly;
int amd_iommu_guest_ir;
static bool amd_iommu_detected; static bool amd_iommu_detected;
static bool __initdata amd_iommu_disabled; static bool __initdata amd_iommu_disabled;
static int amd_iommu_target_ivhd_type; static int amd_iommu_target_ivhd_type;
...@@ -1258,6 +1260,8 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h) ...@@ -1258,6 +1260,8 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h)
iommu->mmio_phys_end = MMIO_REG_END_OFFSET; iommu->mmio_phys_end = MMIO_REG_END_OFFSET;
else else
iommu->mmio_phys_end = MMIO_CNTR_CONF_OFFSET; iommu->mmio_phys_end = MMIO_CNTR_CONF_OFFSET;
if (((h->efr_attr & (0x1 << IOMMU_FEAT_GASUP_SHIFT)) == 0))
amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY;
break; break;
case 0x11: case 0x11:
case 0x40: case 0x40:
...@@ -1265,6 +1269,8 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h) ...@@ -1265,6 +1269,8 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h)
iommu->mmio_phys_end = MMIO_REG_END_OFFSET; iommu->mmio_phys_end = MMIO_REG_END_OFFSET;
else else
iommu->mmio_phys_end = MMIO_CNTR_CONF_OFFSET; iommu->mmio_phys_end = MMIO_CNTR_CONF_OFFSET;
if (((h->efr_reg & (0x1 << IOMMU_EFR_GASUP_SHIFT)) == 0))
amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY;
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -1488,6 +1494,14 @@ static int iommu_init_pci(struct amd_iommu *iommu) ...@@ -1488,6 +1494,14 @@ static int iommu_init_pci(struct amd_iommu *iommu)
if (iommu_feature(iommu, FEATURE_PPR) && alloc_ppr_log(iommu)) if (iommu_feature(iommu, FEATURE_PPR) && alloc_ppr_log(iommu))
return -ENOMEM; return -ENOMEM;
/* Note: We have already checked GASup from IVRS table.
* Now, we need to make sure that GAMSup is set.
*/
if (AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir) &&
!iommu_feature(iommu, FEATURE_GAM_VAPIC))
amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY_GA;
if (iommu->cap & (1UL << IOMMU_CAP_NPCACHE)) if (iommu->cap & (1UL << IOMMU_CAP_NPCACHE))
amd_iommu_np_cache = true; amd_iommu_np_cache = true;
...@@ -1545,16 +1559,24 @@ static void print_iommu_info(void) ...@@ -1545,16 +1559,24 @@ static void print_iommu_info(void)
dev_name(&iommu->dev->dev), iommu->cap_ptr); dev_name(&iommu->dev->dev), iommu->cap_ptr);
if (iommu->cap & (1 << IOMMU_CAP_EFR)) { if (iommu->cap & (1 << IOMMU_CAP_EFR)) {
pr_info("AMD-Vi: Extended features: "); pr_info("AMD-Vi: Extended features (%#llx):\n",
iommu->features);
for (i = 0; i < ARRAY_SIZE(feat_str); ++i) { for (i = 0; i < ARRAY_SIZE(feat_str); ++i) {
if (iommu_feature(iommu, (1ULL << i))) if (iommu_feature(iommu, (1ULL << i)))
pr_cont(" %s", feat_str[i]); pr_cont(" %s", feat_str[i]);
} }
if (iommu->features & FEATURE_GAM_VAPIC)
pr_cont(" GA_vAPIC");
pr_cont("\n"); pr_cont("\n");
} }
} }
if (irq_remapping_enabled) if (irq_remapping_enabled) {
pr_info("AMD-Vi: Interrupt remapping enabled\n"); pr_info("AMD-Vi: Interrupt remapping enabled\n");
if (AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir))
pr_info("AMD-Vi: virtual APIC enabled\n");
}
} }
static int __init amd_iommu_init_pci(void) static int __init amd_iommu_init_pci(void)
...@@ -1862,6 +1884,22 @@ static void iommu_apply_resume_quirks(struct amd_iommu *iommu) ...@@ -1862,6 +1884,22 @@ static void iommu_apply_resume_quirks(struct amd_iommu *iommu)
iommu->stored_addr_lo | 1); iommu->stored_addr_lo | 1);
} }
static void iommu_enable_ga(struct amd_iommu *iommu)
{
#ifdef CONFIG_IRQ_REMAP
switch (amd_iommu_guest_ir) {
case AMD_IOMMU_GUEST_IR_VAPIC:
iommu_feature_enable(iommu, CONTROL_GAM_EN);
/* Fall through */
case AMD_IOMMU_GUEST_IR_LEGACY_GA:
iommu_feature_enable(iommu, CONTROL_GA_EN);
break;
default:
break;
}
#endif
}
/* /*
* This function finally enables all IOMMUs found in the system after * This function finally enables all IOMMUs found in the system after
* they have been initialized * they have been initialized
...@@ -1877,6 +1915,7 @@ static void early_enable_iommus(void) ...@@ -1877,6 +1915,7 @@ static void early_enable_iommus(void)
iommu_enable_command_buffer(iommu); iommu_enable_command_buffer(iommu);
iommu_enable_event_buffer(iommu); iommu_enable_event_buffer(iommu);
iommu_set_exclusion_range(iommu); iommu_set_exclusion_range(iommu);
iommu_enable_ga(iommu);
iommu_enable(iommu); iommu_enable(iommu);
iommu_flush_all_caches(iommu); iommu_flush_all_caches(iommu);
} }
...@@ -2059,7 +2098,7 @@ static int __init early_amd_iommu_init(void) ...@@ -2059,7 +2098,7 @@ static int __init early_amd_iommu_init(void)
struct acpi_table_header *ivrs_base; struct acpi_table_header *ivrs_base;
acpi_size ivrs_size; acpi_size ivrs_size;
acpi_status status; acpi_status status;
int i, ret = 0; int i, remap_cache_sz, ret = 0;
if (!amd_iommu_detected) if (!amd_iommu_detected)
return -ENODEV; return -ENODEV;
...@@ -2157,10 +2196,14 @@ static int __init early_amd_iommu_init(void) ...@@ -2157,10 +2196,14 @@ static int __init early_amd_iommu_init(void)
* remapping tables. * remapping tables.
*/ */
ret = -ENOMEM; ret = -ENOMEM;
if (!AMD_IOMMU_GUEST_IR_GA(amd_iommu_guest_ir))
remap_cache_sz = MAX_IRQS_PER_TABLE * sizeof(u32);
else
remap_cache_sz = MAX_IRQS_PER_TABLE * (sizeof(u64) * 2);
amd_iommu_irq_cache = kmem_cache_create("irq_remap_cache", amd_iommu_irq_cache = kmem_cache_create("irq_remap_cache",
MAX_IRQS_PER_TABLE * sizeof(u32), remap_cache_sz,
IRQ_TABLE_ALIGNMENT, IRQ_TABLE_ALIGNMENT,
0, NULL); 0, NULL);
if (!amd_iommu_irq_cache) if (!amd_iommu_irq_cache)
goto out; goto out;
...@@ -2413,6 +2456,21 @@ static int __init parse_amd_iommu_dump(char *str) ...@@ -2413,6 +2456,21 @@ static int __init parse_amd_iommu_dump(char *str)
return 1; return 1;
} }
static int __init parse_amd_iommu_intr(char *str)
{
for (; *str; ++str) {
if (strncmp(str, "legacy", 6) == 0) {
amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY;
break;
}
if (strncmp(str, "vapic", 5) == 0) {
amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_VAPIC;
break;
}
}
return 1;
}
static int __init parse_amd_iommu_options(char *str) static int __init parse_amd_iommu_options(char *str)
{ {
for (; *str; ++str) { for (; *str; ++str) {
...@@ -2521,6 +2579,7 @@ static int __init parse_ivrs_acpihid(char *str) ...@@ -2521,6 +2579,7 @@ static int __init parse_ivrs_acpihid(char *str)
__setup("amd_iommu_dump", parse_amd_iommu_dump); __setup("amd_iommu_dump", parse_amd_iommu_dump);
__setup("amd_iommu=", parse_amd_iommu_options); __setup("amd_iommu=", parse_amd_iommu_options);
__setup("amd_iommu_intr=", parse_amd_iommu_intr);
__setup("ivrs_ioapic", parse_ivrs_ioapic); __setup("ivrs_ioapic", parse_ivrs_ioapic);
__setup("ivrs_hpet", parse_ivrs_hpet); __setup("ivrs_hpet", parse_ivrs_hpet);
__setup("ivrs_acpihid", parse_ivrs_acpihid); __setup("ivrs_acpihid", parse_ivrs_acpihid);
......
...@@ -38,6 +38,7 @@ extern int amd_iommu_enable(void); ...@@ -38,6 +38,7 @@ extern int amd_iommu_enable(void);
extern void amd_iommu_disable(void); extern void amd_iommu_disable(void);
extern int amd_iommu_reenable(int); extern int amd_iommu_reenable(int);
extern int amd_iommu_enable_faulting(void); extern int amd_iommu_enable_faulting(void);
extern int amd_iommu_guest_ir;
/* IOMMUv2 specific functions */ /* IOMMUv2 specific functions */
struct iommu_domain; struct iommu_domain;
......
...@@ -92,6 +92,7 @@ ...@@ -92,6 +92,7 @@
#define FEATURE_GA (1ULL<<7) #define FEATURE_GA (1ULL<<7)
#define FEATURE_HE (1ULL<<8) #define FEATURE_HE (1ULL<<8)
#define FEATURE_PC (1ULL<<9) #define FEATURE_PC (1ULL<<9)
#define FEATURE_GAM_VAPIC (1ULL<<21)
#define FEATURE_PASID_SHIFT 32 #define FEATURE_PASID_SHIFT 32
#define FEATURE_PASID_MASK (0x1fULL << FEATURE_PASID_SHIFT) #define FEATURE_PASID_MASK (0x1fULL << FEATURE_PASID_SHIFT)
...@@ -146,6 +147,8 @@ ...@@ -146,6 +147,8 @@
#define CONTROL_PPFINT_EN 0x0eULL #define CONTROL_PPFINT_EN 0x0eULL
#define CONTROL_PPR_EN 0x0fULL #define CONTROL_PPR_EN 0x0fULL
#define CONTROL_GT_EN 0x10ULL #define CONTROL_GT_EN 0x10ULL
#define CONTROL_GA_EN 0x11ULL
#define CONTROL_GAM_EN 0x19ULL
#define CTRL_INV_TO_MASK (7 << CONTROL_INV_TIMEOUT) #define CTRL_INV_TO_MASK (7 << CONTROL_INV_TIMEOUT)
#define CTRL_INV_TO_NONE 0 #define CTRL_INV_TO_NONE 0
...@@ -329,6 +332,12 @@ ...@@ -329,6 +332,12 @@
#define IOMMU_CAP_NPCACHE 26 #define IOMMU_CAP_NPCACHE 26
#define IOMMU_CAP_EFR 27 #define IOMMU_CAP_EFR 27
/* IOMMU Feature Reporting Field (for IVHD type 10h */
#define IOMMU_FEAT_GASUP_SHIFT 6
/* IOMMU Extended Feature Register (EFR) */
#define IOMMU_EFR_GASUP_SHIFT 7
#define MAX_DOMAIN_ID 65536 #define MAX_DOMAIN_ID 65536
/* Protection domain flags */ /* Protection domain flags */
...@@ -681,4 +690,19 @@ static inline int get_hpet_devid(int id) ...@@ -681,4 +690,19 @@ static inline int get_hpet_devid(int id)
return -EINVAL; return -EINVAL;
} }
enum amd_iommu_intr_mode_type {
AMD_IOMMU_GUEST_IR_LEGACY,
/* This mode is not visible to users. It is used when
* we cannot fully enable vAPIC and fallback to only support
* legacy interrupt remapping via 128-bit IRTE.
*/
AMD_IOMMU_GUEST_IR_LEGACY_GA,
AMD_IOMMU_GUEST_IR_VAPIC,
};
#define AMD_IOMMU_GUEST_IR_GA(x) (x == AMD_IOMMU_GUEST_IR_VAPIC || \
x == AMD_IOMMU_GUEST_IR_LEGACY_GA)
#define AMD_IOMMU_GUEST_IR_VAPIC(x) (x == AMD_IOMMU_GUEST_IR_VAPIC)
#endif /* _ASM_X86_AMD_IOMMU_TYPES_H */ #endif /* _ASM_X86_AMD_IOMMU_TYPES_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment