diff options
author | Yinghai Lu <yhlu.kernel@gmail.com> | 2008-06-01 16:17:38 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-06-03 07:26:27 -0400 |
commit | 2944e16b25e7fb8b5ee0dd9dc7197a0f9e523cfd (patch) | |
tree | 0114128fdb9d2f54188a1684758e0217eadae1e5 /arch/x86/kernel/mpparse.c | |
parent | e8c27ac9191ab9e6506ae5cbe70d87ac50f8e960 (diff) |
x86: update mptable
make mptable to be consistent with acpi routing, so we could:
1. kexec kernel with acpi=off
2. work around BIOSes where acpi routing is working, but mptable is
not right, so can use kernel/kexec to start other OSes that don't have
good acpi support.
command line: update_mptable
Signed-off-by: Yinghai Lu <yhlu.kernel@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/mpparse.c')
-rw-r--r-- | arch/x86/kernel/mpparse.c | 401 |
1 files changed, 384 insertions, 17 deletions
diff --git a/arch/x86/kernel/mpparse.c b/arch/x86/kernel/mpparse.c index 9f3792d55044..8898aa49079d 100644 --- a/arch/x86/kernel/mpparse.c +++ b/arch/x86/kernel/mpparse.c | |||
@@ -25,6 +25,8 @@ | |||
25 | #include <asm/proto.h> | 25 | #include <asm/proto.h> |
26 | #include <asm/acpi.h> | 26 | #include <asm/acpi.h> |
27 | #include <asm/bios_ebda.h> | 27 | #include <asm/bios_ebda.h> |
28 | #include <asm/e820.h> | ||
29 | #include <asm/trampoline.h> | ||
28 | 30 | ||
29 | #include <mach_apic.h> | 31 | #include <mach_apic.h> |
30 | #ifdef CONFIG_X86_32 | 32 | #ifdef CONFIG_X86_32 |
@@ -161,20 +163,81 @@ static void __init MP_ioapic_info(struct mpc_config_ioapic *m) | |||
161 | nr_ioapics++; | 163 | nr_ioapics++; |
162 | } | 164 | } |
163 | 165 | ||
164 | static void __init MP_intsrc_info(struct mpc_config_intsrc *m) | 166 | static void print_MP_intsrc_info(struct mpc_config_intsrc *m) |
165 | { | 167 | { |
166 | printk(KERN_INFO "Int: type %d, pol %d, trig %d, bus %02x," | 168 | printk(KERN_CONT "Int: type %d, pol %d, trig %d, bus %02x," |
167 | " IRQ %02x, APIC ID %x, APIC INT %02x\n", | 169 | " IRQ %02x, APIC ID %x, APIC INT %02x\n", |
168 | m->mpc_irqtype, m->mpc_irqflag & 3, | 170 | m->mpc_irqtype, m->mpc_irqflag & 3, |
169 | (m->mpc_irqflag >> 2) & 3, m->mpc_srcbus, | 171 | (m->mpc_irqflag >> 2) & 3, m->mpc_srcbus, |
170 | m->mpc_srcbusirq, m->mpc_dstapic, m->mpc_dstirq); | 172 | m->mpc_srcbusirq, m->mpc_dstapic, m->mpc_dstirq); |
171 | mp_irqs[mp_irq_entries].mp_dstapic = m->mpc_dstapic; | 173 | } |
172 | mp_irqs[mp_irq_entries].mp_type = m->mpc_type; | 174 | |
173 | mp_irqs[mp_irq_entries].mp_irqtype = m->mpc_irqtype; | 175 | static void __init print_mp_irq_info(struct mp_config_intsrc *mp_irq) |
174 | mp_irqs[mp_irq_entries].mp_irqflag = m->mpc_irqflag; | 176 | { |
175 | mp_irqs[mp_irq_entries].mp_srcbus = m->mpc_srcbus; | 177 | printk(KERN_CONT "Int: type %d, pol %d, trig %d, bus %02x," |
176 | mp_irqs[mp_irq_entries].mp_srcbusirq = m->mpc_srcbusirq; | 178 | " IRQ %02x, APIC ID %x, APIC INT %02x\n", |
177 | mp_irqs[mp_irq_entries].mp_dstirq = m->mpc_dstirq; | 179 | mp_irq->mp_irqtype, mp_irq->mp_irqflag & 3, |
180 | (mp_irq->mp_irqflag >> 2) & 3, mp_irq->mp_srcbus, | ||
181 | mp_irq->mp_srcbusirq, mp_irq->mp_dstapic, mp_irq->mp_dstirq); | ||
182 | } | ||
183 | |||
184 | static void assign_to_mp_irq(struct mpc_config_intsrc *m, | ||
185 | struct mp_config_intsrc *mp_irq) | ||
186 | { | ||
187 | mp_irq->mp_dstapic = m->mpc_dstapic; | ||
188 | mp_irq->mp_type = m->mpc_type; | ||
189 | mp_irq->mp_irqtype = m->mpc_irqtype; | ||
190 | mp_irq->mp_irqflag = m->mpc_irqflag; | ||
191 | mp_irq->mp_srcbus = m->mpc_srcbus; | ||
192 | mp_irq->mp_srcbusirq = m->mpc_srcbusirq; | ||
193 | mp_irq->mp_dstirq = m->mpc_dstirq; | ||
194 | } | ||
195 | |||
196 | static void __init assign_to_mpc_intsrc(struct mp_config_intsrc *mp_irq, | ||
197 | struct mpc_config_intsrc *m) | ||
198 | { | ||
199 | m->mpc_dstapic = mp_irq->mp_dstapic; | ||
200 | m->mpc_type = mp_irq->mp_type; | ||
201 | m->mpc_irqtype = mp_irq->mp_irqtype; | ||
202 | m->mpc_irqflag = mp_irq->mp_irqflag; | ||
203 | m->mpc_srcbus = mp_irq->mp_srcbus; | ||
204 | m->mpc_srcbusirq = mp_irq->mp_srcbusirq; | ||
205 | m->mpc_dstirq = mp_irq->mp_dstirq; | ||
206 | } | ||
207 | |||
208 | static int mp_irq_mpc_intsrc_cmp(struct mp_config_intsrc *mp_irq, | ||
209 | struct mpc_config_intsrc *m) | ||
210 | { | ||
211 | if (mp_irq->mp_dstapic != m->mpc_dstapic) | ||
212 | return 1; | ||
213 | if (mp_irq->mp_type != m->mpc_type) | ||
214 | return 2; | ||
215 | if (mp_irq->mp_irqtype != m->mpc_irqtype) | ||
216 | return 3; | ||
217 | if (mp_irq->mp_irqflag != m->mpc_irqflag) | ||
218 | return 4; | ||
219 | if (mp_irq->mp_srcbus != m->mpc_srcbus) | ||
220 | return 5; | ||
221 | if (mp_irq->mp_srcbusirq != m->mpc_srcbusirq) | ||
222 | return 6; | ||
223 | if (mp_irq->mp_dstirq != m->mpc_dstirq) | ||
224 | return 7; | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | void MP_intsrc_info(struct mpc_config_intsrc *m) | ||
230 | { | ||
231 | int i; | ||
232 | |||
233 | print_MP_intsrc_info(m); | ||
234 | |||
235 | for (i = 0; i < mp_irq_entries; i++) { | ||
236 | if (!mp_irq_mpc_intsrc_cmp(&mp_irqs[i], m)) | ||
237 | return; | ||
238 | } | ||
239 | |||
240 | assign_to_mp_irq(m, &mp_irqs[mp_irq_entries]); | ||
178 | if (++mp_irq_entries == MAX_IRQ_SOURCES) | 241 | if (++mp_irq_entries == MAX_IRQ_SOURCES) |
179 | panic("Max # of irq sources exceeded!!\n"); | 242 | panic("Max # of irq sources exceeded!!\n"); |
180 | } | 243 | } |
@@ -268,12 +331,9 @@ static inline void mps_oem_check(struct mp_config_table *mpc, char *oem, | |||
268 | * Read/parse the MPC | 331 | * Read/parse the MPC |
269 | */ | 332 | */ |
270 | 333 | ||
271 | static int __init smp_read_mpc(struct mp_config_table *mpc, unsigned early) | 334 | static int __init smp_check_mpc(struct mp_config_table *mpc, char *oem, |
335 | char *str) | ||
272 | { | 336 | { |
273 | char str[16]; | ||
274 | char oem[10]; | ||
275 | int count = sizeof(*mpc); | ||
276 | unsigned char *mpt = ((unsigned char *)mpc) + count; | ||
277 | 337 | ||
278 | if (memcmp(mpc->mpc_signature, MPC_SIGNATURE, 4)) { | 338 | if (memcmp(mpc->mpc_signature, MPC_SIGNATURE, 4)) { |
279 | printk(KERN_ERR "MPTABLE: bad signature [%c%c%c%c]!\n", | 339 | printk(KERN_ERR "MPTABLE: bad signature [%c%c%c%c]!\n", |
@@ -301,13 +361,28 @@ static int __init smp_read_mpc(struct mp_config_table *mpc, unsigned early) | |||
301 | memcpy(str, mpc->mpc_productid, 12); | 361 | memcpy(str, mpc->mpc_productid, 12); |
302 | str[12] = 0; | 362 | str[12] = 0; |
303 | 363 | ||
304 | #ifdef CONFIG_X86_32 | ||
305 | mps_oem_check(mpc, oem, str); | ||
306 | #endif | ||
307 | printk(KERN_INFO "MPTABLE: Product ID: %s\n", str); | 364 | printk(KERN_INFO "MPTABLE: Product ID: %s\n", str); |
308 | 365 | ||
309 | printk(KERN_INFO "MPTABLE: APIC at: 0x%X\n", mpc->mpc_lapic); | 366 | printk(KERN_INFO "MPTABLE: APIC at: 0x%X\n", mpc->mpc_lapic); |
310 | 367 | ||
368 | return 1; | ||
369 | } | ||
370 | |||
371 | static int __init smp_read_mpc(struct mp_config_table *mpc, unsigned early) | ||
372 | { | ||
373 | char str[16]; | ||
374 | char oem[10]; | ||
375 | |||
376 | int count = sizeof(*mpc); | ||
377 | unsigned char *mpt = ((unsigned char *)mpc) + count; | ||
378 | |||
379 | if (!smp_check_mpc(mpc, oem, str)) | ||
380 | return 0; | ||
381 | |||
382 | #ifdef CONFIG_X86_32 | ||
383 | mps_oem_check(mpc, oem, str); | ||
384 | #endif | ||
385 | |||
311 | /* save the local APIC address, it might be non-default */ | 386 | /* save the local APIC address, it might be non-default */ |
312 | if (!acpi_lapic) | 387 | if (!acpi_lapic) |
313 | mp_lapic_addr = mpc->mpc_lapic; | 388 | mp_lapic_addr = mpc->mpc_lapic; |
@@ -785,3 +860,295 @@ void __init find_smp_config(void) | |||
785 | { | 860 | { |
786 | __find_smp_config(1); | 861 | __find_smp_config(1); |
787 | } | 862 | } |
863 | |||
864 | #ifdef CONFIG_X86_IO_APIC | ||
865 | static u8 __initdata irq_used[MAX_IRQ_SOURCES]; | ||
866 | |||
867 | static int __init get_MP_intsrc_index(struct mpc_config_intsrc *m) | ||
868 | { | ||
869 | int i; | ||
870 | |||
871 | if (m->mpc_irqtype != mp_INT) | ||
872 | return 0; | ||
873 | |||
874 | if (m->mpc_irqflag != 0x0f) | ||
875 | return 0; | ||
876 | |||
877 | /* not legacy */ | ||
878 | |||
879 | for (i = 0; i < mp_irq_entries; i++) { | ||
880 | if (mp_irqs[i].mp_irqtype != mp_INT) | ||
881 | continue; | ||
882 | |||
883 | if (mp_irqs[i].mp_irqflag != 0x0f) | ||
884 | continue; | ||
885 | |||
886 | if (mp_irqs[i].mp_srcbus != m->mpc_srcbus) | ||
887 | continue; | ||
888 | if (mp_irqs[i].mp_srcbusirq != m->mpc_srcbusirq) | ||
889 | continue; | ||
890 | if (irq_used[i]) { | ||
891 | /* already claimed */ | ||
892 | return -2; | ||
893 | } | ||
894 | irq_used[i] = 1; | ||
895 | return i; | ||
896 | } | ||
897 | |||
898 | /* not found */ | ||
899 | return -1; | ||
900 | } | ||
901 | |||
902 | #define SPARE_SLOT_NUM 20 | ||
903 | |||
904 | static struct mpc_config_intsrc __initdata *m_spare[SPARE_SLOT_NUM]; | ||
905 | #endif | ||
906 | |||
907 | static int __init replace_intsrc_all(struct mp_config_table *mpc, | ||
908 | unsigned long mpc_new_phys, | ||
909 | unsigned long mpc_new_length) | ||
910 | { | ||
911 | #ifdef CONFIG_X86_IO_APIC | ||
912 | int i; | ||
913 | int nr_m_spare = 0; | ||
914 | #endif | ||
915 | |||
916 | int count = sizeof(*mpc); | ||
917 | unsigned char *mpt = ((unsigned char *)mpc) + count; | ||
918 | |||
919 | printk(KERN_INFO "mpc_length %x\n", mpc->mpc_length); | ||
920 | while (count < mpc->mpc_length) { | ||
921 | switch (*mpt) { | ||
922 | case MP_PROCESSOR: | ||
923 | { | ||
924 | struct mpc_config_processor *m = | ||
925 | (struct mpc_config_processor *)mpt; | ||
926 | mpt += sizeof(*m); | ||
927 | count += sizeof(*m); | ||
928 | break; | ||
929 | } | ||
930 | case MP_BUS: | ||
931 | { | ||
932 | struct mpc_config_bus *m = | ||
933 | (struct mpc_config_bus *)mpt; | ||
934 | mpt += sizeof(*m); | ||
935 | count += sizeof(*m); | ||
936 | break; | ||
937 | } | ||
938 | case MP_IOAPIC: | ||
939 | { | ||
940 | mpt += sizeof(struct mpc_config_ioapic); | ||
941 | count += sizeof(struct mpc_config_ioapic); | ||
942 | break; | ||
943 | } | ||
944 | case MP_INTSRC: | ||
945 | { | ||
946 | #ifdef CONFIG_X86_IO_APIC | ||
947 | struct mpc_config_intsrc *m = | ||
948 | (struct mpc_config_intsrc *)mpt; | ||
949 | |||
950 | printk(KERN_INFO "OLD "); | ||
951 | print_MP_intsrc_info(m); | ||
952 | i = get_MP_intsrc_index(m); | ||
953 | if (i > 0) { | ||
954 | assign_to_mpc_intsrc(&mp_irqs[i], m); | ||
955 | printk(KERN_INFO "NEW "); | ||
956 | print_mp_irq_info(&mp_irqs[i]); | ||
957 | } else if (!i) { | ||
958 | /* legacy, do nothing */ | ||
959 | } else if (nr_m_spare < SPARE_SLOT_NUM) { | ||
960 | /* | ||
961 | * not found (-1), or duplicated (-2) | ||
962 | * are invalid entries, | ||
963 | * we need to use the slot later | ||
964 | */ | ||
965 | m_spare[nr_m_spare] = m; | ||
966 | nr_m_spare++; | ||
967 | } | ||
968 | #endif | ||
969 | mpt += sizeof(struct mpc_config_intsrc); | ||
970 | count += sizeof(struct mpc_config_intsrc); | ||
971 | break; | ||
972 | } | ||
973 | case MP_LINTSRC: | ||
974 | { | ||
975 | struct mpc_config_lintsrc *m = | ||
976 | (struct mpc_config_lintsrc *)mpt; | ||
977 | mpt += sizeof(*m); | ||
978 | count += sizeof(*m); | ||
979 | break; | ||
980 | } | ||
981 | default: | ||
982 | /* wrong mptable */ | ||
983 | printk(KERN_ERR "Your mptable is wrong, contact your HW vendor!\n"); | ||
984 | printk(KERN_ERR "type %x\n", *mpt); | ||
985 | print_hex_dump(KERN_ERR, " ", DUMP_PREFIX_ADDRESS, 16, | ||
986 | 1, mpc, mpc->mpc_length, 1); | ||
987 | goto out; | ||
988 | } | ||
989 | } | ||
990 | |||
991 | #ifdef CONFIG_X86_IO_APIC | ||
992 | for (i = 0; i < mp_irq_entries; i++) { | ||
993 | if (irq_used[i]) | ||
994 | continue; | ||
995 | |||
996 | if (mp_irqs[i].mp_irqtype != mp_INT) | ||
997 | continue; | ||
998 | |||
999 | if (mp_irqs[i].mp_irqflag != 0x0f) | ||
1000 | continue; | ||
1001 | |||
1002 | if (nr_m_spare > 0) { | ||
1003 | printk(KERN_INFO "*NEW* found "); | ||
1004 | nr_m_spare--; | ||
1005 | assign_to_mpc_intsrc(&mp_irqs[i], m_spare[nr_m_spare]); | ||
1006 | m_spare[nr_m_spare] = NULL; | ||
1007 | } else { | ||
1008 | struct mpc_config_intsrc *m = | ||
1009 | (struct mpc_config_intsrc *)mpt; | ||
1010 | count += sizeof(struct mpc_config_intsrc); | ||
1011 | if (!mpc_new_phys) { | ||
1012 | printk(KERN_INFO "No spare slots, try to append...take your risk, new mpc_length %x\n", count); | ||
1013 | } else { | ||
1014 | if (count <= mpc_new_length) | ||
1015 | printk(KERN_INFO "No spare slots, try to append..., new mpc_length %x\n", count); | ||
1016 | else { | ||
1017 | printk(KERN_ERR "mpc_new_length %lx is too small\n", mpc_new_length); | ||
1018 | goto out; | ||
1019 | } | ||
1020 | } | ||
1021 | assign_to_mpc_intsrc(&mp_irqs[i], m); | ||
1022 | mpc->mpc_length = count; | ||
1023 | mpt += sizeof(struct mpc_config_intsrc); | ||
1024 | } | ||
1025 | print_mp_irq_info(&mp_irqs[i]); | ||
1026 | } | ||
1027 | #endif | ||
1028 | out: | ||
1029 | /* update checksum */ | ||
1030 | mpc->mpc_checksum = 0; | ||
1031 | mpc->mpc_checksum -= mpf_checksum((unsigned char *)mpc, | ||
1032 | mpc->mpc_length); | ||
1033 | |||
1034 | return 0; | ||
1035 | } | ||
1036 | |||
1037 | int __initdata enable_update_mptable; | ||
1038 | |||
1039 | static int __init update_mptable_setup(char *str) | ||
1040 | { | ||
1041 | enable_update_mptable = 1; | ||
1042 | return 0; | ||
1043 | } | ||
1044 | early_param("update_mptable", update_mptable_setup); | ||
1045 | |||
1046 | static unsigned long __initdata mpc_new_phys; | ||
1047 | static unsigned long mpc_new_length __initdata = 4096; | ||
1048 | |||
1049 | /* alloc_mptable or alloc_mptable=4k */ | ||
1050 | static int __initdata alloc_mptable; | ||
1051 | static int __init parse_alloc_mptable_opt(char *p) | ||
1052 | { | ||
1053 | enable_update_mptable = 1; | ||
1054 | alloc_mptable = 1; | ||
1055 | if (!p) | ||
1056 | return 0; | ||
1057 | mpc_new_length = memparse(p, &p); | ||
1058 | return 0; | ||
1059 | } | ||
1060 | early_param("alloc_mptable", parse_alloc_mptable_opt); | ||
1061 | |||
1062 | void __init early_reserve_e820_mpc_new(void) | ||
1063 | { | ||
1064 | if (enable_update_mptable && alloc_mptable) { | ||
1065 | u64 startt = 0; | ||
1066 | #ifdef CONFIG_X86_TRAMPOLINE | ||
1067 | startt = TRAMPOLINE_BASE; | ||
1068 | #endif | ||
1069 | mpc_new_phys = early_reserve_e820(startt, mpc_new_length, 4); | ||
1070 | } | ||
1071 | } | ||
1072 | |||
1073 | static int __init update_mp_table(void) | ||
1074 | { | ||
1075 | char str[16]; | ||
1076 | char oem[10]; | ||
1077 | struct intel_mp_floating *mpf; | ||
1078 | struct mp_config_table *mpc; | ||
1079 | struct mp_config_table *mpc_new; | ||
1080 | |||
1081 | if (!enable_update_mptable) | ||
1082 | return 0; | ||
1083 | |||
1084 | mpf = mpf_found; | ||
1085 | if (!mpf) | ||
1086 | return 0; | ||
1087 | |||
1088 | /* | ||
1089 | * Now see if we need to go further. | ||
1090 | */ | ||
1091 | if (mpf->mpf_feature1 != 0) | ||
1092 | return 0; | ||
1093 | |||
1094 | if (!mpf->mpf_physptr) | ||
1095 | return 0; | ||
1096 | |||
1097 | mpc = phys_to_virt(mpf->mpf_physptr); | ||
1098 | |||
1099 | if (!smp_check_mpc(mpc, oem, str)) | ||
1100 | return 0; | ||
1101 | |||
1102 | printk(KERN_INFO "mpf: %lx\n", virt_to_phys(mpf)); | ||
1103 | printk(KERN_INFO "mpf_physptr: %x\n", mpf->mpf_physptr); | ||
1104 | |||
1105 | if (mpc_new_phys && mpc->mpc_length > mpc_new_length) { | ||
1106 | mpc_new_phys = 0; | ||
1107 | printk(KERN_INFO "mpc_new_length is %ld, please use alloc_mptable=8k\n", | ||
1108 | mpc_new_length); | ||
1109 | } | ||
1110 | |||
1111 | if (!mpc_new_phys) { | ||
1112 | unsigned char old, new; | ||
1113 | /* check if we can change the postion */ | ||
1114 | mpc->mpc_checksum = 0; | ||
1115 | old = mpf_checksum((unsigned char *)mpc, mpc->mpc_length); | ||
1116 | mpc->mpc_checksum = 0xff; | ||
1117 | new = mpf_checksum((unsigned char *)mpc, mpc->mpc_length); | ||
1118 | if (old == new) { | ||
1119 | printk(KERN_INFO "mpc is readonly, please try alloc_mptable instead\n"); | ||
1120 | return 0; | ||
1121 | } | ||
1122 | printk(KERN_INFO "use in-positon replacing\n"); | ||
1123 | } else { | ||
1124 | mpf->mpf_physptr = mpc_new_phys; | ||
1125 | mpc_new = phys_to_virt(mpc_new_phys); | ||
1126 | memcpy(mpc_new, mpc, mpc->mpc_length); | ||
1127 | mpc = mpc_new; | ||
1128 | /* check if we can modify that */ | ||
1129 | if (mpc_new_phys - mpf->mpf_physptr) { | ||
1130 | struct intel_mp_floating *mpf_new; | ||
1131 | /* steal 16 bytes from [0, 1k) */ | ||
1132 | printk(KERN_INFO "mpf new: %x\n", 0x400 - 16); | ||
1133 | mpf_new = phys_to_virt(0x400 - 16); | ||
1134 | memcpy(mpf_new, mpf, 16); | ||
1135 | mpf = mpf_new; | ||
1136 | mpf->mpf_physptr = mpc_new_phys; | ||
1137 | } | ||
1138 | mpf->mpf_checksum = 0; | ||
1139 | mpf->mpf_checksum -= mpf_checksum((unsigned char *)mpf, 16); | ||
1140 | printk(KERN_INFO "mpf_physptr new: %x\n", mpf->mpf_physptr); | ||
1141 | } | ||
1142 | |||
1143 | /* | ||
1144 | * only replace the one with mp_INT and | ||
1145 | * MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, | ||
1146 | * already in mp_irqs , stored by ... and mp_config_acpi_gsi, | ||
1147 | * may need pci=routeirq for all coverage | ||
1148 | */ | ||
1149 | replace_intsrc_all(mpc, mpc_new_phys, mpc_new_length); | ||
1150 | |||
1151 | return 0; | ||
1152 | } | ||
1153 | |||
1154 | late_initcall(update_mp_table); | ||