Change license from GPLv3 to LGPLv3.
[seabios.git] / src / mptable.c
1 // MPTable generation (on emulators)
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2006 Fabrice Bellard
5 //
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
7
8 #include "util.h" // dprintf
9 #include "memmap.h" // bios_table_cur_addr
10 #include "config.h" // CONFIG_*
11
12 static void putb(u8 **pp, int val)
13 {
14     u8 *q;
15     q = *pp;
16     *q++ = val;
17     *pp = q;
18 }
19
20 static void putstr(u8 **pp, const char *str)
21 {
22     u8 *q;
23     q = *pp;
24     while (*str)
25         *q++ = *str++;
26     *pp = q;
27 }
28
29 static void putle16(u8 **pp, int val)
30 {
31     u8 *q;
32     q = *pp;
33     *q++ = val;
34     *q++ = val >> 8;
35     *pp = q;
36 }
37
38 static void putle32(u8 **pp, int val)
39 {
40     u8 *q;
41     q = *pp;
42     *q++ = val;
43     *q++ = val >> 8;
44     *q++ = val >> 16;
45     *q++ = val >> 24;
46     *pp = q;
47 }
48
49 void
50 mptable_init(void)
51 {
52     if (! CONFIG_MPTABLE)
53         return;
54
55     dprintf(3, "init MPTable\n");
56
57     u8 *mp_config_table, *q, *float_pointer_struct;
58     int ioapic_id, i, len;
59     int mp_config_table_size;
60
61     int smp_cpus = smp_probe();
62     if (smp_cpus <= 1)
63         // Building an mptable on uniprocessor machines confuses some OSes.
64         return;
65
66     bios_table_cur_addr = ALIGN(bios_table_cur_addr, 16);
67     mp_config_table = (u8 *)bios_table_cur_addr;
68     q = mp_config_table;
69     putstr(&q, "PCMP"); /* "PCMP signature */
70     putle16(&q, 0); /* table length (patched later) */
71     putb(&q, 4); /* spec rev */
72     putb(&q, 0); /* checksum (patched later) */
73     putstr(&q, CONFIG_CPUNAME8); /* OEM id */
74     putstr(&q, "0.1         "); /* vendor id */
75     putle32(&q, 0); /* OEM table ptr */
76     putle16(&q, 0); /* OEM table size */
77     putle16(&q, smp_cpus + 18); /* entry count */
78     putle32(&q, BUILD_APIC_ADDR); /* local APIC addr */
79     putle16(&q, 0); /* ext table length */
80     putb(&q, 0); /* ext table checksum */
81     putb(&q, 0); /* reserved */
82
83     for(i = 0; i < smp_cpus; i++) {
84         putb(&q, 0); /* entry type = processor */
85         putb(&q, i); /* APIC id */
86         putb(&q, 0x11); /* local APIC version number */
87         if (i == 0)
88             putb(&q, 3); /* cpu flags: enabled, bootstrap cpu */
89         else
90             putb(&q, 1); /* cpu flags: enabled */
91         putb(&q, 0); /* cpu signature */
92         putb(&q, 6);
93         putb(&q, 0);
94         putb(&q, 0);
95         putle16(&q, 0x201); /* feature flags */
96         putle16(&q, 0);
97
98         putle16(&q, 0); /* reserved */
99         putle16(&q, 0);
100         putle16(&q, 0);
101         putle16(&q, 0);
102     }
103
104     /* isa bus */
105     putb(&q, 1); /* entry type = bus */
106     putb(&q, 0); /* bus ID */
107     putstr(&q, "ISA   ");
108
109     /* ioapic */
110     ioapic_id = smp_cpus;
111     putb(&q, 2); /* entry type = I/O APIC */
112     putb(&q, ioapic_id); /* apic ID */
113     putb(&q, 0x11); /* I/O APIC version number */
114     putb(&q, 1); /* enable */
115     putle32(&q, BUILD_IOAPIC_ADDR); /* I/O APIC addr */
116
117     /* irqs */
118     for(i = 0; i < 16; i++) {
119         putb(&q, 3); /* entry type = I/O interrupt */
120         putb(&q, 0); /* interrupt type = vectored interrupt */
121         putb(&q, 0); /* flags: po=0, el=0 */
122         putb(&q, 0);
123         putb(&q, 0); /* source bus ID = ISA */
124         putb(&q, i); /* source bus IRQ */
125         putb(&q, ioapic_id); /* dest I/O APIC ID */
126         putb(&q, i); /* dest I/O APIC interrupt in */
127     }
128     /* patch length */
129     len = q - mp_config_table;
130     mp_config_table[4] = len;
131     mp_config_table[5] = len >> 8;
132
133     mp_config_table[7] = -checksum(mp_config_table, q - mp_config_table);
134
135     mp_config_table_size = q - mp_config_table;
136
137     bios_table_cur_addr += mp_config_table_size;
138
139     /* floating pointer structure */
140     bios_table_cur_addr = ALIGN(bios_table_cur_addr, 16);
141     float_pointer_struct = (u8 *)bios_table_cur_addr;
142     q = float_pointer_struct;
143     putstr(&q, "_MP_");
144     /* pointer to MP config table */
145     putle32(&q, (unsigned long)mp_config_table);
146
147     putb(&q, 1); /* length in 16 byte units */
148     putb(&q, 4); /* MP spec revision */
149     putb(&q, 0); /* checksum (patched later) */
150     putb(&q, 0); /* MP feature byte 1 */
151
152     putb(&q, 0);
153     putb(&q, 0);
154     putb(&q, 0);
155     putb(&q, 0);
156     float_pointer_struct[10] = -checksum(float_pointer_struct
157                                          , q - float_pointer_struct);
158     bios_table_cur_addr += (q - float_pointer_struct);
159     dprintf(1, "MP table addr=0x%08lx MPC table addr=0x%08lx size=0x%x\n",
160             (unsigned long)float_pointer_struct,
161             (unsigned long)mp_config_table,
162             mp_config_table_size);
163 }