156 lines
		
	
	
	
		
			3.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			156 lines
		
	
	
	
		
			3.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // Multiprocessor support
 | |
| // Search memory for MP description structures.
 | |
| // http://developer.intel.com/design/pentium/datashts/24201606.pdf
 | |
| 
 | |
| #include "types.h"
 | |
| #include "defs.h"
 | |
| #include "param.h"
 | |
| #include "memlayout.h"
 | |
| #include "mp.h"
 | |
| #include "x86.h"
 | |
| #include "mmu.h"
 | |
| #include "proc.h"
 | |
| 
 | |
| struct cpu cpus[NCPU];
 | |
| static struct cpu *bcpu;
 | |
| int ismp;
 | |
| int ncpu;
 | |
| uchar ioapicid;
 | |
| 
 | |
| int
 | |
| mpbcpu(void)
 | |
| {
 | |
|   return bcpu-cpus;
 | |
| }
 | |
| 
 | |
| static uchar
 | |
| sum(uchar *addr, int len)
 | |
| {
 | |
|   int i, sum;
 | |
|   
 | |
|   sum = 0;
 | |
|   for(i=0; i<len; i++)
 | |
|     sum += addr[i];
 | |
|   return sum;
 | |
| }
 | |
| 
 | |
| // Look for an MP structure in the len bytes at addr.
 | |
| static struct mp*
 | |
| mpsearch1(uint a, int len)
 | |
| {
 | |
|   uchar *e, *p, *addr;
 | |
| 
 | |
|   addr = p2v(a);
 | |
|   e = addr+len;
 | |
|   for(p = addr; p < e; p += sizeof(struct mp))
 | |
|     if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0)
 | |
|       return (struct mp*)p;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| // Search for the MP Floating Pointer Structure, which according to the
 | |
| // spec is in one of the following three locations:
 | |
| // 1) in the first KB of the EBDA;
 | |
| // 2) in the last KB of system base memory;
 | |
| // 3) in the BIOS ROM between 0xE0000 and 0xFFFFF.
 | |
| static struct mp*
 | |
| mpsearch(void)
 | |
| {
 | |
|   uchar *bda;
 | |
|   uint p;
 | |
|   struct mp *mp;
 | |
| 
 | |
|   bda = (uchar *) P2V(0x400);
 | |
|   if((p = ((bda[0x0F]<<8)| bda[0x0E]) << 4)){
 | |
|     if((mp = mpsearch1(p, 1024)))
 | |
|       return mp;
 | |
|   } else {
 | |
|     p = ((bda[0x14]<<8)|bda[0x13])*1024;
 | |
|     if((mp = mpsearch1(p-1024, 1024)))
 | |
|       return mp;
 | |
|   }
 | |
|   return mpsearch1(0xF0000, 0x10000);
 | |
| }
 | |
| 
 | |
| // Search for an MP configuration table.  For now,
 | |
| // don't accept the default configurations (physaddr == 0).
 | |
| // Check for correct signature, calculate the checksum and,
 | |
| // if correct, check the version.
 | |
| // To do: check extended table checksum.
 | |
| static struct mpconf*
 | |
| mpconfig(struct mp **pmp)
 | |
| {
 | |
|   struct mpconf *conf;
 | |
|   struct mp *mp;
 | |
| 
 | |
|   if((mp = mpsearch()) == 0 || mp->physaddr == 0)
 | |
|     return 0;
 | |
|   conf = (struct mpconf*) p2v((uint) mp->physaddr);
 | |
|   if(memcmp(conf, "PCMP", 4) != 0)
 | |
|     return 0;
 | |
|   if(conf->version != 1 && conf->version != 4)
 | |
|     return 0;
 | |
|   if(sum((uchar*)conf, conf->length) != 0)
 | |
|     return 0;
 | |
|   *pmp = mp;
 | |
|   return conf;
 | |
| }
 | |
| 
 | |
| void
 | |
| mpinit(void)
 | |
| {
 | |
|   uchar *p, *e;
 | |
|   struct mp *mp;
 | |
|   struct mpconf *conf;
 | |
|   struct mpproc *proc;
 | |
|   struct mpioapic *ioapic;
 | |
| 
 | |
|   bcpu = &cpus[0];
 | |
|   if((conf = mpconfig(&mp)) == 0)
 | |
|     return;
 | |
|   ismp = 1;
 | |
|   lapic = (uint*)conf->lapicaddr;
 | |
|   for(p=(uchar*)(conf+1), e=(uchar*)conf+conf->length; p<e; ){
 | |
|     switch(*p){
 | |
|     case MPPROC:
 | |
|       proc = (struct mpproc*)p;
 | |
|       if(ncpu != proc->apicid){
 | |
|         cprintf("mpinit: ncpu=%d apicid=%d\n", ncpu, proc->apicid);
 | |
|         ismp = 0;
 | |
|       }
 | |
|       if(proc->flags & MPBOOT)
 | |
|         bcpu = &cpus[ncpu];
 | |
|       cpus[ncpu].id = ncpu;
 | |
|       ncpu++;
 | |
|       p += sizeof(struct mpproc);
 | |
|       continue;
 | |
|     case MPIOAPIC:
 | |
|       ioapic = (struct mpioapic*)p;
 | |
|       ioapicid = ioapic->apicno;
 | |
|       p += sizeof(struct mpioapic);
 | |
|       continue;
 | |
|     case MPBUS:
 | |
|     case MPIOINTR:
 | |
|     case MPLINTR:
 | |
|       p += 8;
 | |
|       continue;
 | |
|     default:
 | |
|       cprintf("mpinit: unknown config type %x\n", *p);
 | |
|       ismp = 0;
 | |
|     }
 | |
|   }
 | |
|   if(!ismp){
 | |
|     // Didn't like what we found; fall back to no MP.
 | |
|     ncpu = 1;
 | |
|     lapic = 0;
 | |
|     ioapicid = 0;
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if(mp->imcrp){
 | |
|     // Bochs doesn't support IMCR, so this doesn't run on Bochs.
 | |
|     // But it would on real hardware.
 | |
|     outb(0x22, 0x70);   // Select IMCR
 | |
|     outb(0x23, inb(0x23) | 1);  // Mask external interrupts.
 | |
|   }
 | |
| }
 | 
