diff options
author | Fenghua Yu <fenghua.yu@intel.com> | 2009-03-27 17:22:44 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2009-04-03 16:45:59 -0400 |
commit | b24696bc55f66fecc30715e003f10fc2555a9271 (patch) | |
tree | 3ef565bf041a06106a73d0b27ccc256845ef5644 /arch/x86 | |
parent | eb4a52bc660ea835482c582eaaf4893742cbd160 (diff) |
Intel IOMMU Suspend/Resume Support - Interrupt Remapping
This patch enables suspend/resume for interrupt remapping. During suspend,
interrupt remapping is disabled. When resume, interrupt remapping is enabled
again.
Signed-off-by: Fenghua Yu <fenghua.yu@intel.com>
Acked-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/include/asm/apic.h | 4 | ||||
-rw-r--r-- | arch/x86/include/asm/io_apic.h | 11 | ||||
-rw-r--r-- | arch/x86/kernel/apic/apic.c | 70 | ||||
-rw-r--r-- | arch/x86/kernel/apic/io_apic.c | 111 |
4 files changed, 143 insertions, 53 deletions
diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h index df8a300dfe6c..f9f0866ed6f8 100644 --- a/arch/x86/include/asm/apic.h +++ b/arch/x86/include/asm/apic.h | |||
@@ -108,6 +108,10 @@ extern void native_apic_icr_write(u32 low, u32 id); | |||
108 | extern u64 native_apic_icr_read(void); | 108 | extern u64 native_apic_icr_read(void); |
109 | 109 | ||
110 | #ifdef CONFIG_X86_X2APIC | 110 | #ifdef CONFIG_X86_X2APIC |
111 | |||
112 | #define EIM_8BIT_APIC_ID 0 | ||
113 | #define EIM_32BIT_APIC_ID 1 | ||
114 | |||
111 | /* | 115 | /* |
112 | * Make previous memory operations globally visible before | 116 | * Make previous memory operations globally visible before |
113 | * sending the IPI through x2apic wrmsr. We need a serializing instruction or | 117 | * sending the IPI through x2apic wrmsr. We need a serializing instruction or |
diff --git a/arch/x86/include/asm/io_apic.h b/arch/x86/include/asm/io_apic.h index 373cc2bbcad2..9d826e436010 100644 --- a/arch/x86/include/asm/io_apic.h +++ b/arch/x86/include/asm/io_apic.h | |||
@@ -162,10 +162,13 @@ extern int (*ioapic_renumber_irq)(int ioapic, int irq); | |||
162 | extern void ioapic_init_mappings(void); | 162 | extern void ioapic_init_mappings(void); |
163 | 163 | ||
164 | #ifdef CONFIG_X86_64 | 164 | #ifdef CONFIG_X86_64 |
165 | extern int save_IO_APIC_setup(void); | 165 | extern struct IO_APIC_route_entry **alloc_ioapic_entries(void); |
166 | extern void mask_IO_APIC_setup(void); | 166 | extern void free_ioapic_entries(struct IO_APIC_route_entry **ioapic_entries); |
167 | extern void restore_IO_APIC_setup(void); | 167 | extern int save_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries); |
168 | extern void reinit_intr_remapped_IO_APIC(int); | 168 | extern void mask_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries); |
169 | extern int restore_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries); | ||
170 | extern void reinit_intr_remapped_IO_APIC(int intr_remapping, | ||
171 | struct IO_APIC_route_entry **ioapic_entries); | ||
169 | #endif | 172 | #endif |
170 | 173 | ||
171 | extern void probe_nr_irqs_gsi(void); | 174 | extern void probe_nr_irqs_gsi(void); |
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index 85eb8e100818..098ec84b8c00 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c | |||
@@ -1304,6 +1304,7 @@ void __init enable_IR_x2apic(void) | |||
1304 | #ifdef CONFIG_INTR_REMAP | 1304 | #ifdef CONFIG_INTR_REMAP |
1305 | int ret; | 1305 | int ret; |
1306 | unsigned long flags; | 1306 | unsigned long flags; |
1307 | struct IO_APIC_route_entry **ioapic_entries = NULL; | ||
1307 | 1308 | ||
1308 | if (!cpu_has_x2apic) | 1309 | if (!cpu_has_x2apic) |
1309 | return; | 1310 | return; |
@@ -1334,17 +1335,23 @@ void __init enable_IR_x2apic(void) | |||
1334 | return; | 1335 | return; |
1335 | } | 1336 | } |
1336 | 1337 | ||
1337 | ret = save_IO_APIC_setup(); | 1338 | ioapic_entries = alloc_ioapic_entries(); |
1339 | if (!ioapic_entries) { | ||
1340 | pr_info("Allocate ioapic_entries failed: %d\n", ret); | ||
1341 | goto end; | ||
1342 | } | ||
1343 | |||
1344 | ret = save_IO_APIC_setup(ioapic_entries); | ||
1338 | if (ret) { | 1345 | if (ret) { |
1339 | pr_info("Saving IO-APIC state failed: %d\n", ret); | 1346 | pr_info("Saving IO-APIC state failed: %d\n", ret); |
1340 | goto end; | 1347 | goto end; |
1341 | } | 1348 | } |
1342 | 1349 | ||
1343 | local_irq_save(flags); | 1350 | local_irq_save(flags); |
1344 | mask_IO_APIC_setup(); | 1351 | mask_IO_APIC_setup(ioapic_entries); |
1345 | mask_8259A(); | 1352 | mask_8259A(); |
1346 | 1353 | ||
1347 | ret = enable_intr_remapping(1); | 1354 | ret = enable_intr_remapping(EIM_32BIT_APIC_ID); |
1348 | 1355 | ||
1349 | if (ret && x2apic_preenabled) { | 1356 | if (ret && x2apic_preenabled) { |
1350 | local_irq_restore(flags); | 1357 | local_irq_restore(flags); |
@@ -1364,9 +1371,9 @@ end_restore: | |||
1364 | /* | 1371 | /* |
1365 | * IR enabling failed | 1372 | * IR enabling failed |
1366 | */ | 1373 | */ |
1367 | restore_IO_APIC_setup(); | 1374 | restore_IO_APIC_setup(ioapic_entries); |
1368 | else | 1375 | else |
1369 | reinit_intr_remapped_IO_APIC(x2apic_preenabled); | 1376 | reinit_intr_remapped_IO_APIC(x2apic_preenabled, ioapic_entries); |
1370 | 1377 | ||
1371 | unmask_8259A(); | 1378 | unmask_8259A(); |
1372 | local_irq_restore(flags); | 1379 | local_irq_restore(flags); |
@@ -1379,6 +1386,8 @@ end: | |||
1379 | pr_info("Enabled Interrupt-remapping\n"); | 1386 | pr_info("Enabled Interrupt-remapping\n"); |
1380 | } else | 1387 | } else |
1381 | pr_err("Failed to enable Interrupt-remapping and x2apic\n"); | 1388 | pr_err("Failed to enable Interrupt-remapping and x2apic\n"); |
1389 | if (ioapic_entries) | ||
1390 | free_ioapic_entries(ioapic_entries); | ||
1382 | #else | 1391 | #else |
1383 | if (!cpu_has_x2apic) | 1392 | if (!cpu_has_x2apic) |
1384 | return; | 1393 | return; |
@@ -1954,6 +1963,10 @@ static int lapic_suspend(struct sys_device *dev, pm_message_t state) | |||
1954 | 1963 | ||
1955 | local_irq_save(flags); | 1964 | local_irq_save(flags); |
1956 | disable_local_APIC(); | 1965 | disable_local_APIC(); |
1966 | #ifdef CONFIG_INTR_REMAP | ||
1967 | if (intr_remapping_enabled) | ||
1968 | disable_intr_remapping(); | ||
1969 | #endif | ||
1957 | local_irq_restore(flags); | 1970 | local_irq_restore(flags); |
1958 | return 0; | 1971 | return 0; |
1959 | } | 1972 | } |
@@ -1964,15 +1977,41 @@ static int lapic_resume(struct sys_device *dev) | |||
1964 | unsigned long flags; | 1977 | unsigned long flags; |
1965 | int maxlvt; | 1978 | int maxlvt; |
1966 | 1979 | ||
1980 | #ifdef CONFIG_INTR_REMAP | ||
1981 | int ret; | ||
1982 | struct IO_APIC_route_entry **ioapic_entries = NULL; | ||
1983 | |||
1967 | if (!apic_pm_state.active) | 1984 | if (!apic_pm_state.active) |
1968 | return 0; | 1985 | return 0; |
1969 | 1986 | ||
1970 | maxlvt = lapic_get_maxlvt(); | ||
1971 | |||
1972 | local_irq_save(flags); | 1987 | local_irq_save(flags); |
1988 | if (x2apic) { | ||
1989 | ioapic_entries = alloc_ioapic_entries(); | ||
1990 | if (!ioapic_entries) { | ||
1991 | WARN(1, "Alloc ioapic_entries in lapic resume failed."); | ||
1992 | return -ENOMEM; | ||
1993 | } | ||
1994 | |||
1995 | ret = save_IO_APIC_setup(ioapic_entries); | ||
1996 | if (ret) { | ||
1997 | WARN(1, "Saving IO-APIC state failed: %d\n", ret); | ||
1998 | free_ioapic_entries(ioapic_entries); | ||
1999 | return ret; | ||
2000 | } | ||
2001 | |||
2002 | mask_IO_APIC_setup(ioapic_entries); | ||
2003 | mask_8259A(); | ||
2004 | enable_x2apic(); | ||
2005 | } | ||
2006 | #else | ||
2007 | if (!apic_pm_state.active) | ||
2008 | return 0; | ||
1973 | 2009 | ||
2010 | local_irq_save(flags); | ||
1974 | if (x2apic) | 2011 | if (x2apic) |
1975 | enable_x2apic(); | 2012 | enable_x2apic(); |
2013 | #endif | ||
2014 | |||
1976 | else { | 2015 | else { |
1977 | /* | 2016 | /* |
1978 | * Make sure the APICBASE points to the right address | 2017 | * Make sure the APICBASE points to the right address |
@@ -1986,6 +2025,7 @@ static int lapic_resume(struct sys_device *dev) | |||
1986 | wrmsr(MSR_IA32_APICBASE, l, h); | 2025 | wrmsr(MSR_IA32_APICBASE, l, h); |
1987 | } | 2026 | } |
1988 | 2027 | ||
2028 | maxlvt = lapic_get_maxlvt(); | ||
1989 | apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED); | 2029 | apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED); |
1990 | apic_write(APIC_ID, apic_pm_state.apic_id); | 2030 | apic_write(APIC_ID, apic_pm_state.apic_id); |
1991 | apic_write(APIC_DFR, apic_pm_state.apic_dfr); | 2031 | apic_write(APIC_DFR, apic_pm_state.apic_dfr); |
@@ -2009,8 +2049,20 @@ static int lapic_resume(struct sys_device *dev) | |||
2009 | apic_write(APIC_ESR, 0); | 2049 | apic_write(APIC_ESR, 0); |
2010 | apic_read(APIC_ESR); | 2050 | apic_read(APIC_ESR); |
2011 | 2051 | ||
2052 | #ifdef CONFIG_INTR_REMAP | ||
2053 | if (intr_remapping_enabled) | ||
2054 | reenable_intr_remapping(EIM_32BIT_APIC_ID); | ||
2055 | |||
2056 | if (x2apic) { | ||
2057 | unmask_8259A(); | ||
2058 | restore_IO_APIC_setup(ioapic_entries); | ||
2059 | free_ioapic_entries(ioapic_entries); | ||
2060 | } | ||
2061 | #endif | ||
2062 | |||
2012 | local_irq_restore(flags); | 2063 | local_irq_restore(flags); |
2013 | 2064 | ||
2065 | |||
2014 | return 0; | 2066 | return 0; |
2015 | } | 2067 | } |
2016 | 2068 | ||
@@ -2048,7 +2100,9 @@ static int __init init_lapic_sysfs(void) | |||
2048 | error = sysdev_register(&device_lapic); | 2100 | error = sysdev_register(&device_lapic); |
2049 | return error; | 2101 | return error; |
2050 | } | 2102 | } |
2051 | device_initcall(init_lapic_sysfs); | 2103 | |
2104 | /* local apic needs to resume before other devices access its registers. */ | ||
2105 | core_initcall(init_lapic_sysfs); | ||
2052 | 2106 | ||
2053 | #else /* CONFIG_PM */ | 2107 | #else /* CONFIG_PM */ |
2054 | 2108 | ||
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index 1bb5c6cee3eb..0ad3fe77641a 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c | |||
@@ -851,63 +851,74 @@ __setup("pirq=", ioapic_pirq_setup); | |||
851 | #endif /* CONFIG_X86_32 */ | 851 | #endif /* CONFIG_X86_32 */ |
852 | 852 | ||
853 | #ifdef CONFIG_INTR_REMAP | 853 | #ifdef CONFIG_INTR_REMAP |
854 | /* I/O APIC RTE contents at the OS boot up */ | 854 | struct IO_APIC_route_entry **alloc_ioapic_entries(void) |
855 | static struct IO_APIC_route_entry *early_ioapic_entries[MAX_IO_APICS]; | 855 | { |
856 | int apic; | ||
857 | struct IO_APIC_route_entry **ioapic_entries; | ||
858 | |||
859 | ioapic_entries = kzalloc(sizeof(*ioapic_entries) * nr_ioapics, | ||
860 | GFP_ATOMIC); | ||
861 | if (!ioapic_entries) | ||
862 | return 0; | ||
863 | |||
864 | for (apic = 0; apic < nr_ioapics; apic++) { | ||
865 | ioapic_entries[apic] = | ||
866 | kzalloc(sizeof(struct IO_APIC_route_entry) * | ||
867 | nr_ioapic_registers[apic], GFP_ATOMIC); | ||
868 | if (!ioapic_entries[apic]) | ||
869 | goto nomem; | ||
870 | } | ||
871 | |||
872 | return ioapic_entries; | ||
873 | |||
874 | nomem: | ||
875 | while (--apic >= 0) | ||
876 | kfree(ioapic_entries[apic]); | ||
877 | kfree(ioapic_entries); | ||
878 | |||
879 | return 0; | ||
880 | } | ||
856 | 881 | ||
857 | /* | 882 | /* |
858 | * Saves all the IO-APIC RTE's | 883 | * Saves all the IO-APIC RTE's |
859 | */ | 884 | */ |
860 | int save_IO_APIC_setup(void) | 885 | int save_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries) |
861 | { | 886 | { |
862 | union IO_APIC_reg_01 reg_01; | ||
863 | unsigned long flags; | ||
864 | int apic, pin; | 887 | int apic, pin; |
865 | 888 | ||
866 | /* | 889 | if (!ioapic_entries) |
867 | * The number of IO-APIC IRQ registers (== #pins): | 890 | return -ENOMEM; |
868 | */ | ||
869 | for (apic = 0; apic < nr_ioapics; apic++) { | ||
870 | spin_lock_irqsave(&ioapic_lock, flags); | ||
871 | reg_01.raw = io_apic_read(apic, 1); | ||
872 | spin_unlock_irqrestore(&ioapic_lock, flags); | ||
873 | nr_ioapic_registers[apic] = reg_01.bits.entries+1; | ||
874 | } | ||
875 | 891 | ||
876 | for (apic = 0; apic < nr_ioapics; apic++) { | 892 | for (apic = 0; apic < nr_ioapics; apic++) { |
877 | early_ioapic_entries[apic] = | 893 | if (!ioapic_entries[apic]) |
878 | kzalloc(sizeof(struct IO_APIC_route_entry) * | 894 | return -ENOMEM; |
879 | nr_ioapic_registers[apic], GFP_KERNEL); | ||
880 | if (!early_ioapic_entries[apic]) | ||
881 | goto nomem; | ||
882 | } | ||
883 | 895 | ||
884 | for (apic = 0; apic < nr_ioapics; apic++) | ||
885 | for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) | 896 | for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) |
886 | early_ioapic_entries[apic][pin] = | 897 | ioapic_entries[apic][pin] = |
887 | ioapic_read_entry(apic, pin); | 898 | ioapic_read_entry(apic, pin); |
899 | } | ||
888 | 900 | ||
889 | return 0; | 901 | return 0; |
890 | |||
891 | nomem: | ||
892 | while (apic >= 0) | ||
893 | kfree(early_ioapic_entries[apic--]); | ||
894 | memset(early_ioapic_entries, 0, | ||
895 | ARRAY_SIZE(early_ioapic_entries)); | ||
896 | |||
897 | return -ENOMEM; | ||
898 | } | 902 | } |
899 | 903 | ||
900 | void mask_IO_APIC_setup(void) | 904 | /* |
905 | * Mask all IO APIC entries. | ||
906 | */ | ||
907 | void mask_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries) | ||
901 | { | 908 | { |
902 | int apic, pin; | 909 | int apic, pin; |
903 | 910 | ||
911 | if (!ioapic_entries) | ||
912 | return; | ||
913 | |||
904 | for (apic = 0; apic < nr_ioapics; apic++) { | 914 | for (apic = 0; apic < nr_ioapics; apic++) { |
905 | if (!early_ioapic_entries[apic]) | 915 | if (!ioapic_entries[apic]) |
906 | break; | 916 | break; |
917 | |||
907 | for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { | 918 | for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { |
908 | struct IO_APIC_route_entry entry; | 919 | struct IO_APIC_route_entry entry; |
909 | 920 | ||
910 | entry = early_ioapic_entries[apic][pin]; | 921 | entry = ioapic_entries[apic][pin]; |
911 | if (!entry.mask) { | 922 | if (!entry.mask) { |
912 | entry.mask = 1; | 923 | entry.mask = 1; |
913 | ioapic_write_entry(apic, pin, entry); | 924 | ioapic_write_entry(apic, pin, entry); |
@@ -916,22 +927,30 @@ void mask_IO_APIC_setup(void) | |||
916 | } | 927 | } |
917 | } | 928 | } |
918 | 929 | ||
919 | void restore_IO_APIC_setup(void) | 930 | /* |
931 | * Restore IO APIC entries which was saved in ioapic_entries. | ||
932 | */ | ||
933 | int restore_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries) | ||
920 | { | 934 | { |
921 | int apic, pin; | 935 | int apic, pin; |
922 | 936 | ||
937 | if (!ioapic_entries) | ||
938 | return -ENOMEM; | ||
939 | |||
923 | for (apic = 0; apic < nr_ioapics; apic++) { | 940 | for (apic = 0; apic < nr_ioapics; apic++) { |
924 | if (!early_ioapic_entries[apic]) | 941 | if (!ioapic_entries[apic]) |
925 | break; | 942 | return -ENOMEM; |
943 | |||
926 | for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) | 944 | for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) |
927 | ioapic_write_entry(apic, pin, | 945 | ioapic_write_entry(apic, pin, |
928 | early_ioapic_entries[apic][pin]); | 946 | ioapic_entries[apic][pin]); |
929 | kfree(early_ioapic_entries[apic]); | ||
930 | early_ioapic_entries[apic] = NULL; | ||
931 | } | 947 | } |
948 | return 0; | ||
932 | } | 949 | } |
933 | 950 | ||
934 | void reinit_intr_remapped_IO_APIC(int intr_remapping) | 951 | void reinit_intr_remapped_IO_APIC(int intr_remapping, |
952 | struct IO_APIC_route_entry **ioapic_entries) | ||
953 | |||
935 | { | 954 | { |
936 | /* | 955 | /* |
937 | * for now plain restore of previous settings. | 956 | * for now plain restore of previous settings. |
@@ -940,7 +959,17 @@ void reinit_intr_remapped_IO_APIC(int intr_remapping) | |||
940 | * table entries. for now, do a plain restore, and wait for | 959 | * table entries. for now, do a plain restore, and wait for |
941 | * the setup_IO_APIC_irqs() to do proper initialization. | 960 | * the setup_IO_APIC_irqs() to do proper initialization. |
942 | */ | 961 | */ |
943 | restore_IO_APIC_setup(); | 962 | restore_IO_APIC_setup(ioapic_entries); |
963 | } | ||
964 | |||
965 | void free_ioapic_entries(struct IO_APIC_route_entry **ioapic_entries) | ||
966 | { | ||
967 | int apic; | ||
968 | |||
969 | for (apic = 0; apic < nr_ioapics; apic++) | ||
970 | kfree(ioapic_entries[apic]); | ||
971 | |||
972 | kfree(ioapic_entries); | ||
944 | } | 973 | } |
945 | #endif | 974 | #endif |
946 | 975 | ||