1 | // Multiprocessor support |
2 | // Search memory for MP description structures. |
3 | // http://developer.intel.com/design/pentium/datashts/24201606.pdf |
4 | |
5 | #include "types.h" |
6 | #include "defs.h" |
7 | #include "param.h" |
8 | #include "memlayout.h" |
9 | #include "mp.h" |
10 | #include "x86.h" |
11 | #include "mmu.h" |
12 | #include "proc.h" |
13 | |
14 | struct cpu cpus[NCPU]; |
15 | int ncpu; |
16 | uchar ioapicid; |
17 | |
18 | static uchar |
19 | sum(uchar *addr, int len) |
20 | { |
21 | int i, sum; |
22 | |
23 | sum = 0; |
24 | for(i=0; i<len; i++) |
25 | sum += addr[i]; |
26 | return sum; |
27 | } |
28 | |
29 | // Look for an MP structure in the len bytes at addr. |
30 | static struct mp* |
31 | mpsearch1(uint a, int len) |
32 | { |
33 | uchar *e, *p, *addr; |
34 | |
35 | addr = P2V(a); |
36 | e = addr+len; |
37 | for(p = addr; p < e; p += sizeof(struct mp)) |
38 | if(memcmp(p, "_MP_" , 4) == 0 && sum(p, sizeof(struct mp)) == 0) |
39 | return (struct mp*)p; |
40 | return 0; |
41 | } |
42 | |
43 | // Search for the MP Floating Pointer Structure, which according to the |
44 | // spec is in one of the following three locations: |
45 | // 1) in the first KB of the EBDA; |
46 | // 2) in the last KB of system base memory; |
47 | // 3) in the BIOS ROM between 0xE0000 and 0xFFFFF. |
48 | static struct mp* |
49 | mpsearch(void) |
50 | { |
51 | uchar *bda; |
52 | uint p; |
53 | struct mp *mp; |
54 | |
55 | bda = (uchar *) P2V(0x400); |
56 | if((p = ((bda[0x0F]<<8)| bda[0x0E]) << 4)){ |
57 | if((mp = mpsearch1(p, 1024))) |
58 | return mp; |
59 | } else { |
60 | p = ((bda[0x14]<<8)|bda[0x13])*1024; |
61 | if((mp = mpsearch1(p-1024, 1024))) |
62 | return mp; |
63 | } |
64 | return mpsearch1(0xF0000, 0x10000); |
65 | } |
66 | |
67 | // Search for an MP configuration table. For now, |
68 | // don't accept the default configurations (physaddr == 0). |
69 | // Check for correct signature, calculate the checksum and, |
70 | // if correct, check the version. |
71 | // To do: check extended table checksum. |
72 | static struct mpconf* |
73 | mpconfig(struct mp **pmp) |
74 | { |
75 | struct mpconf *conf; |
76 | struct mp *mp; |
77 | |
78 | if((mp = mpsearch()) == 0 || mp->physaddr == 0) |
79 | return 0; |
80 | conf = (struct mpconf*) P2V((uint) mp->physaddr); |
81 | if(memcmp(conf, "PCMP" , 4) != 0) |
82 | return 0; |
83 | if(conf->version != 1 && conf->version != 4) |
84 | return 0; |
85 | if(sum((uchar*)conf, conf->length) != 0) |
86 | return 0; |
87 | *pmp = mp; |
88 | return conf; |
89 | } |
90 | |
91 | void |
92 | mpinit(void) |
93 | { |
94 | uchar *p, *e; |
95 | int ismp; |
96 | struct mp *mp; |
97 | struct mpconf *conf; |
98 | struct mpproc *proc; |
99 | struct mpioapic *ioapic; |
100 | |
101 | if((conf = mpconfig(&mp)) == 0) |
102 | panic("Expect to run on an SMP" ); |
103 | ismp = 1; |
104 | lapic = (uint*)conf->lapicaddr; |
105 | for(p=(uchar*)(conf+1), e=(uchar*)conf+conf->length; p<e; ){ |
106 | switch(*p){ |
107 | case MPPROC: |
108 | proc = (struct mpproc*)p; |
109 | if(ncpu < NCPU) { |
110 | cpus[ncpu].apicid = proc->apicid; // apicid may differ from ncpu |
111 | ncpu++; |
112 | } |
113 | p += sizeof(struct mpproc); |
114 | continue; |
115 | case MPIOAPIC: |
116 | ioapic = (struct mpioapic*)p; |
117 | ioapicid = ioapic->apicno; |
118 | p += sizeof(struct mpioapic); |
119 | continue; |
120 | case MPBUS: |
121 | case MPIOINTR: |
122 | case MPLINTR: |
123 | p += 8; |
124 | continue; |
125 | default: |
126 | ismp = 0; |
127 | break; |
128 | } |
129 | } |
130 | if(!ismp) |
131 | panic("Didn't find a suitable machine" ); |
132 | |
133 | if(mp->imcrp){ |
134 | // Bochs doesn't support IMCR, so this doesn't run on Bochs. |
135 | // But it would on real hardware. |
136 | outb(0x22, 0x70); // Select IMCR |
137 | outb(0x23, inb(0x23) | 1); // Mask external interrupts. |
138 | } |
139 | } |
140 | |