// Multiprocessor support // Search memory for MP description structures. // http://developer.intel.com/design/pentium/datashts/24201606.pdf #include "core/types.h" #include "core/defs.h" #include "core/param.h" #include "arch/memlayout.h" #include "arch/mp.h" #include "arch/x86.h" #include "arch/mmu.h" #include "core/proc.h" struct cpu cpus[NCPU]; // Processors the firmware says exist. int ncpu; // Processors actually running, which is not the same number and must not be // confused with it. Enumeration is cheap and startup is where the difficulty // is: right now the APs are found and deliberately not started, so this stays // at 1 while ncpu is 2, 4 or 16. // // /proc/cpuinfo reports *this* one, matching Linux, where cpuinfo lists online // CPUs. Reporting ncpu there told a VM with four processors that it had four // working ones, which was a comfortable thing to read and false. int ncpu_online = 1; uchar ioapicid; // Where the I/O APIC's registers are. Every source that describes it -- the // MADT, or this table -- says so, and ioapicinit() maps whatever they said. uint64 ioapic_phys = IOAPIC_DEFAULT_PHYS; static uchar sum(uchar *addr, int len) { int i, sum; sum = 0; for(i=0; iphysaddr == 0) return 0; conf = (struct mpconf*) P2V((addr_t) 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 *mproc; struct mpioapic *ioapic; if((conf = mpconfig(&mp)) == 0) { cprintf("No other CPUs found.\n"); return; } lapic = (uint*)P2V(conf->lapicaddr_p); for(p=(uchar*)(conf+1), e=(uchar*)conf+conf->length; pflags & 0x01) && ncpu < NCPU) { cpus[ncpu].apicid = mproc->apicid; // apicid may differ from ncpu ncpu++; } p += sizeof(struct mpproc); continue; case MPIOAPIC: ioapic = (struct mpioapic*)p; if(ioapic_phys == IOAPIC_DEFAULT_PHYS || ioapic->addr_p == IOAPIC_DEFAULT_PHYS){ ioapicid = ioapic->apicno; ioapic_phys = ioapic->addr_p; } p += sizeof(struct mpioapic); continue; case MPBUS: case MPIOINTR: case MPLINTR: p += 8; continue; default: panic("Major problem parsing mp config."); break; } } // "Seems we are" is doing a lot of work here, and the number is usually // wrong. This walks the *legacy Intel MP table* (1994), which modern firmware // fills in minimally or not at all -- QEMU/SeaBIOS emits exactly one // processor entry regardless of -smp, because it expects the OS to enumerate // CPUs from the ACPI MADT instead. Verified 2026-08-08: with -smp 4 the table // is 208 bytes and contains one type-0 entry, then buses, an I/O APIC and // interrupt assignments. So ncpu is 1 on QEMU no matter what, and SMP is // silently never exercised there. // // See docs/architecture/boot.md -- this is half of the SMP bug, and the // reason the other half (AP startup triple-faulting) cannot be reproduced // under QEMU until the MADT is parsed. cprintf("Seems we are SMP, ncpu = %d\n",ncpu); 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. } }