xref: /xv6-public/mp.c (revision a7c03bd9)
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 ismp;
16 int ncpu;
17 uchar ioapicid;
18 
19 static uchar
20 sum(uchar *addr, int len)
21 {
22   int i, sum;
23 
24   sum = 0;
25   for(i=0; i<len; i++)
26     sum += addr[i];
27   return sum;
28 }
29 
30 // Look for an MP structure in the len bytes at addr.
31 static struct mp*
32 mpsearch1(uint a, int len)
33 {
34   uchar *e, *p, *addr;
35 
36   addr = P2V(a);
37   e = addr+len;
38   for(p = addr; p < e; p += sizeof(struct mp))
39     if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0)
40       return (struct mp*)p;
41   return 0;
42 }
43 
44 // Search for the MP Floating Pointer Structure, which according to the
45 // spec is in one of the following three locations:
46 // 1) in the first KB of the EBDA;
47 // 2) in the last KB of system base memory;
48 // 3) in the BIOS ROM between 0xE0000 and 0xFFFFF.
49 static struct mp*
50 mpsearch(void)
51 {
52   uchar *bda;
53   uint p;
54   struct mp *mp;
55 
56   bda = (uchar *) P2V(0x400);
57   if((p = ((bda[0x0F]<<8)| bda[0x0E]) << 4)){
58     if((mp = mpsearch1(p, 1024)))
59       return mp;
60   } else {
61     p = ((bda[0x14]<<8)|bda[0x13])*1024;
62     if((mp = mpsearch1(p-1024, 1024)))
63       return mp;
64   }
65   return mpsearch1(0xF0000, 0x10000);
66 }
67 
68 // Search for an MP configuration table.  For now,
69 // don't accept the default configurations (physaddr == 0).
70 // Check for correct signature, calculate the checksum and,
71 // if correct, check the version.
72 // To do: check extended table checksum.
73 static struct mpconf*
74 mpconfig(struct mp **pmp)
75 {
76   struct mpconf *conf;
77   struct mp *mp;
78 
79   if((mp = mpsearch()) == 0 || mp->physaddr == 0)
80     return 0;
81   conf = (struct mpconf*) P2V((uint) mp->physaddr);
82   if(memcmp(conf, "PCMP", 4) != 0)
83     return 0;
84   if(conf->version != 1 && conf->version != 4)
85     return 0;
86   if(sum((uchar*)conf, conf->length) != 0)
87     return 0;
88   *pmp = mp;
89   return conf;
90 }
91 
92 void
93 mpinit(void)
94 {
95   uchar *p, *e;
96   struct mp *mp;
97   struct mpconf *conf;
98   struct mpproc *proc;
99   struct mpioapic *ioapic;
100 
101   if((conf = mpconfig(&mp)) == 0)
102     return;
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 != proc->apicid){
110         cprintf("mpinit: ncpu=%d apicid=%d\n", ncpu, proc->apicid);
111         ismp = 0;
112       }
113       cpus[ncpu].id = ncpu;
114       ncpu++;
115       p += sizeof(struct mpproc);
116       continue;
117     case MPIOAPIC:
118       ioapic = (struct mpioapic*)p;
119       ioapicid = ioapic->apicno;
120       p += sizeof(struct mpioapic);
121       continue;
122     case MPBUS:
123     case MPIOINTR:
124     case MPLINTR:
125       p += 8;
126       continue;
127     default:
128       cprintf("mpinit: unknown config type %x\n", *p);
129       ismp = 0;
130     }
131   }
132   if(!ismp){
133     // Didn't like what we found; fall back to no MP.
134     ncpu = 1;
135     lapic = 0;
136     ioapicid = 0;
137     return;
138   }
139 
140   if(mp->imcrp){
141     // Bochs doesn't support IMCR, so this doesn't run on Bochs.
142     // But it would on real hardware.
143     outb(0x22, 0x70);   // Select IMCR
144     outb(0x23, inb(0x23) | 1);  // Mask external interrupts.
145   }
146 }
147