b87a056d5074456ed5bbaeb6626fae4317aff213
[seabios.git] / src / smp.c
1 /*
2  * smp.c
3  * SMP support for BIOS.
4  * 
5  * Copyright (C) 2008  Nguyen Anh Quynh <aquynh@gmail.com>
6  * Copyright (C) 2002  MandrakeSoft S.A.
7  * 
8  * This file may be distributed under the terms of the GNU GPLv3 license.
9  */
10
11 #include "config.h"
12 #include "hardware.h"
13 #include "smp.h"
14 #include "types.h"
15 #include "util.h"
16 #include "acpi.h" // ACPI_DATA_SIZE
17
18 int smp_cpus;
19
20 static inline void
21 writel(void *addr, u32 val)
22 {
23     *(volatile u32 *)addr = val;
24 }
25
26 static inline void
27 writew(void *addr, u16 val)
28 {
29     *(volatile u16 *)addr = val;
30 }
31
32 static inline u32
33 readl(const void *addr)
34 {
35     return *(volatile const u32 *)addr;
36 }
37
38 static inline u16
39 readw(const void *addr)
40 {
41     return *(volatile const u16 *)addr;
42 }
43
44 static void
45 delay_ms(int n)
46 {
47     int i, j;
48     for(i = 0; i < n; i++) {
49 #ifdef QEMU_SUPPORT
50         /* approximative ! */
51         for(j = 0; j < 1000000; j++);
52 #else
53         {
54           int r1, r2;
55           j = 66;
56           r1 = inb(0x61) & 0x10;
57           do {
58             r2 = inb(0x61) & 0x10;
59             if (r1 != r2) {
60               j--;
61               r1 = r2;
62             }
63           } while (j > 0);
64         }
65 #endif
66     }
67 }
68
69 asm (
70     ".globl smp_ap_boot_code_start \n"
71     ".globl smp_ap_boot_code_end   \n"
72     "                              \n"
73     "  .code16                     \n"
74     "smp_ap_boot_code_start:       \n"
75     "  xorw %ax, %ax               \n"
76     "  movw %ax, %ds               \n"
77     "  //incw CPU_COUNT_ADDR       \n"
78     "  incw 0xf000                 \n"
79     "1:                            \n"
80     "  hlt                         \n"
81     "  jmp 1b                      \n"
82     "smp_ap_boot_code_end:         \n"
83     "  .code32                     \n"
84     );
85
86 extern u8 smp_ap_boot_code_start;
87 extern u8 smp_ap_boot_code_end;
88
89 /* find the number of CPUs by launching a SIPI to them */
90 void
91 smp_probe(void)
92 {
93     u32 val, sipi_vector;
94
95     smp_cpus = 1;
96     if (cpuid_features & CPUID_APIC) {
97
98         /* enable local APIC */
99         val = readl(APIC_BASE + APIC_SVR);
100         val |= APIC_ENABLED;
101         writel(APIC_BASE + APIC_SVR, val);
102
103         writew((void *)CPU_COUNT_ADDR, 1);
104
105         /* copy AP boot code */
106         memcpy((void *)AP_BOOT_ADDR, &smp_ap_boot_code_start,
107                &smp_ap_boot_code_end - &smp_ap_boot_code_start);
108
109         /* broadcast SIPI */
110         writel(APIC_BASE + APIC_ICR_LOW, 0x000C4500);
111         sipi_vector = AP_BOOT_ADDR >> 12;
112         writel(APIC_BASE + APIC_ICR_LOW, 0x000C4600 | sipi_vector);
113
114         delay_ms(10);
115
116         smp_cpus = readw((void *)CPU_COUNT_ADDR);
117     }
118
119     BX_INFO("Found %d cpu(s)\n", smp_cpus);
120 }
121
122 /****************************************************/
123 /* Multi Processor table init */
124
125 static void
126 putb(u8 **pp, int val)
127 {
128     u8 *q;
129     q = *pp;
130     *q++ = val;
131     *pp = q;
132 }
133
134 static void
135 putstr(u8 **pp, const char *str)
136 {
137     u8 *q;
138     q = *pp;
139     while (*str)
140         *q++ = *str++;
141     *pp = q;
142 }
143
144 static void
145 putle16(u8 **pp, int val)
146 {
147     u8 *q;
148     q = *pp;
149     *q++ = val;
150     *q++ = val >> 8;
151     *pp = q;
152 }
153
154 static void
155 putle32(u8 **pp, int val)
156 {
157     u8 *q;
158     q = *pp;
159     *q++ = val;
160     *q++ = val >> 8;
161     *q++ = val >> 16;
162     *q++ = val >> 24;
163     *pp = q;
164 }
165
166 static int
167 mpf_checksum(const u8 *data, int len)
168 {
169     int sum, i;
170
171     sum = 0;
172     for(i = 0; i < len; i++)
173         sum += data[i];
174
175     return sum & 0xff;
176 }
177
178 void
179 mptable_init(void)
180 {
181     u8 *mp_config_table, *q, *float_pointer_struct;
182     int ioapic_id, i, len;
183     int mp_config_table_size;
184
185 #ifdef QEMU_SUPPORT
186     if (smp_cpus <= 1)
187         return;
188 #endif
189
190 #ifdef CONFIG_USE_EBDA_TABLES
191     mp_config_table = (u8 *)(ram_size - ACPI_DATA_SIZE - MPTABLE_MAX_SIZE);
192 #else
193     bios_table_cur_addr = align(bios_table_cur_addr, 16);
194     mp_config_table = (u8 *)bios_table_cur_addr;
195 #endif
196     q = mp_config_table;
197     putstr(&q, "PCMP"); /* "PCMP signature */
198     putle16(&q, 0); /* table length (patched later) */
199     putb(&q, 4); /* spec rev */
200     putb(&q, 0); /* checksum (patched later) */
201 #ifdef QEMU_SUPPORT
202     putstr(&q, "QEMUCPU "); /* OEM id */
203 #else
204     putstr(&q, "BOCHSCPU");
205 #endif
206     putstr(&q, "0.1         "); /* vendor id */
207     putle32(&q, 0); /* OEM table ptr */
208     putle16(&q, 0); /* OEM table size */
209     putle16(&q, smp_cpus + 18); /* entry count */
210     putle32(&q, 0xfee00000); /* local APIC addr */
211     putle16(&q, 0); /* ext table length */
212     putb(&q, 0); /* ext table checksum */
213     putb(&q, 0); /* reserved */
214
215     for(i = 0; i < smp_cpus; i++) {
216         putb(&q, 0); /* entry type = processor */
217         putb(&q, i); /* APIC id */
218         putb(&q, 0x11); /* local APIC version number */
219         if (i == 0)
220             putb(&q, 3); /* cpu flags: enabled, bootstrap cpu */
221         else
222             putb(&q, 1); /* cpu flags: enabled */
223         putb(&q, 0); /* cpu signature */
224         putb(&q, 6);
225         putb(&q, 0);
226         putb(&q, 0);
227         putle16(&q, 0x201); /* feature flags */
228         putle16(&q, 0);
229
230         putle16(&q, 0); /* reserved */
231         putle16(&q, 0);
232         putle16(&q, 0);
233         putle16(&q, 0);
234     }
235
236     /* isa bus */
237     putb(&q, 1); /* entry type = bus */
238     putb(&q, 0); /* bus ID */
239     putstr(&q, "ISA   ");
240
241     /* ioapic */
242     ioapic_id = smp_cpus;
243     putb(&q, 2); /* entry type = I/O APIC */
244     putb(&q, ioapic_id); /* apic ID */
245     putb(&q, 0x11); /* I/O APIC version number */
246     putb(&q, 1); /* enable */
247     putle32(&q, 0xfec00000); /* I/O APIC addr */
248
249     /* irqs */
250     for (i = 0; i < 16; i++) {
251         putb(&q, 3); /* entry type = I/O interrupt */
252         putb(&q, 0); /* interrupt type = vectored interrupt */
253         putb(&q, 0); /* flags: po=0, el=0 */
254         putb(&q, 0);
255         putb(&q, 0); /* source bus ID = ISA */
256         putb(&q, i); /* source bus IRQ */
257         putb(&q, ioapic_id); /* dest I/O APIC ID */
258         putb(&q, i); /* dest I/O APIC interrupt in */
259     }
260     /* patch length */
261     len = q - mp_config_table;
262     mp_config_table[4] = len;
263     mp_config_table[5] = len >> 8;
264     mp_config_table[7] = -mpf_checksum(mp_config_table, q - mp_config_table);
265
266     mp_config_table_size = q - mp_config_table;
267
268 #ifndef CONFIG_USE_EBDA_TABLES
269     bios_table_cur_addr += mp_config_table_size;
270 #endif
271
272     /* floating pointer structure */
273 #ifdef CONFIG_USE_EBDA_TABLES
274     ebda_cur_addr = align(ebda_cur_addr, 16);
275     float_pointer_struct = (u8 *)ebda_cur_addr;
276 #else
277     bios_table_cur_addr = align(bios_table_cur_addr, 16);
278     float_pointer_struct = (u8 *)bios_table_cur_addr;
279 #endif
280
281     q = float_pointer_struct;
282     putstr(&q, "_MP_");
283     /* pointer to MP config table */
284     putle32(&q, (unsigned long)mp_config_table);
285
286     putb(&q, 1); /* length in 16 byte units */
287     putb(&q, 4); /* MP spec revision */
288     putb(&q, 0); /* checksum (patched later) */
289     putb(&q, 0); /* MP feature byte 1 */
290
291     putb(&q, 0);
292     putb(&q, 0);
293     putb(&q, 0);
294     putb(&q, 0);
295     float_pointer_struct[10] =
296         -mpf_checksum(float_pointer_struct, q - float_pointer_struct);
297
298 #ifdef CONFIG_USE_EBDA_TABLES
299     ebda_cur_addr += (q - float_pointer_struct);
300 #else
301     bios_table_cur_addr += (q - float_pointer_struct);
302 #endif
303
304     BX_INFO("MP table addr=0x%08lx MPC table addr=0x%08lx size=0x%x\n",
305             (unsigned long)float_pointer_struct,
306             (unsigned long)mp_config_table,
307             mp_config_table_size);
308 }