#include #include #include #include #include #include #include #include #include #include #include #include #include #include extern uint8_t mpstart_begin[]; extern uint8_t mpstart_end[]; extern AS systemAS; #define MP_WAITTIME 250000000ULL typedef struct CrossCallFrame { CrossCallCB cb; void *arg; volatile int count; volatile int done[MAX_CPUS]; volatile int status[MAX_CPUS]; } CrossCallFrame; const char *CPUStateToString[] = { "NOT PRESENT", "BOOTED", "HALTED", }; typedef struct CPUState { int state; UnixEpochNS heartbeat; CrossCallFrame *frame; } CPUState; volatile static bool booted; volatile static int lastCPU; volatile static CPUState cpus[MAX_CPUS]; void MP_Init() { int i; kprintf("Booting on CPU %u\n", CPU()); cpus[CPU()].state = CPUSTATE_BOOTED; cpus[CPU()].frame = NULL; for (i = 1; i < MAX_CPUS; i++) { cpus[i].state = CPUSTATE_NOT_PRESENT; cpus[i].frame = NULL; } /* * XXX: We really should read from the MP Table, but this appears to be * reliable for now. */ lastCPU = 0; /* for (i = 1; i < MAX_CPUS; i++) { if (MPBootAP(i) < 0) break; lastCPU = i; } */ lastCPU++; } void MP_InitAP() { kprintf("AP %d booted!\n", CPU()); cpus[CPU()].state = CPUSTATE_BOOTED; booted = 1; } void MP_SetState(int state) { ASSERT(state > 0 && state <= CPUSTATE_MAX); cpus[CPU()].state = state; } int MP_GetCPUs() { return lastCPU; } void MP_CrossCallTrap() { int c; Critical_Enter(); for (c = 0; c <= lastCPU; c++) { CrossCallFrame *frame = cpus[c].frame; if (frame == NULL) continue; if (frame->done[CPU()] == 1) continue; frame->status[CPU()] = (frame->cb)(frame->arg); frame->done[CPU()] = 1; // Increment __sync_add_and_fetch(&frame->count, 1); } Critical_Exit(); } // XXX: The thread should not be migrated in the middle of this call. int MP_CrossCall(CrossCallCB cb, void *arg) { volatile CrossCallFrame frame; // Setup frame memset((void *)&frame, 0, sizeof(frame)); frame.cb = cb; frame.arg = arg; frame.count = 1; Critical_Enter(); cpus[CPU()].frame = (CrossCallFrame *)&frame; /* if (LAPIC_Broadcast(T_CROSSCALL) < 0) return -1; */ // Run on the local CPU frame.status[CPU()] = cb(arg); frame.done[CPU()] = 1; // Wait for all to respond while (frame.count < lastCPU) { // Check for timeout // XXX: Should dump the crosscall frame } cpus[CPU()].frame = NULL; Critical_Exit(); return 0; } static int MPPing(void *arg) { //kprintf("CPU %d Ack\n", CPU()); return 0; } static void Debug_CrossCall(int argc, const char *argv[]) { int i; UnixEpochNS startTS, stopTS; startTS = KTime_GetEpochNS(); for (i = 0; i < 32; i++) { MP_CrossCall(&MPPing, NULL); } stopTS = KTime_GetEpochNS(); // XXX: Print min and max kprintf("Average CrossCall Latency: %llu ns\n", (stopTS - startTS) / 32ULL); return; } REGISTER_DBGCMD(crosscall, "Ping crosscall", Debug_CrossCall); static void Debug_CPUS(int argc, const char *argv[]) { int c; for (c = 0; c < MAX_CPUS; c++) { if (cpus[c].state != CPUSTATE_NOT_PRESENT) { kprintf("CPU %d: %s\n", c, CPUStateToString[cpus[c].state]); } } } REGISTER_DBGCMD(cpus, "Show MP information", Debug_CPUS); static void Debug_CPU(int argc, const char *argv[]) { kprintf("CPU %d\n", CPU()); } REGISTER_DBGCMD(cpu, "Current CPU number", Debug_CPU);