diff options
Diffstat (limited to 'sys/i386/i386/mp_machdep.c')
-rw-r--r-- | sys/i386/i386/mp_machdep.c | 36 |
1 files changed, 36 insertions, 0 deletions
diff --git a/sys/i386/i386/mp_machdep.c b/sys/i386/i386/mp_machdep.c index 0bf6e2246a5d..d70b4be2ad48 100644 --- a/sys/i386/i386/mp_machdep.c +++ b/sys/i386/i386/mp_machdep.c @@ -146,6 +146,7 @@ void *bootstacks[MAXCPU]; static void *dpcpu; struct pcb stoppcbs[MAXCPU]; +struct pcb **susppcbs = NULL; /* Variables needed for SMP tlb shootdown. */ vm_offset_t smp_tlb_addr1; @@ -587,6 +588,9 @@ cpu_mp_start(void) setidt(IPI_STOP, IDTVEC(cpustop), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); + /* Install an inter-CPU IPI for CPU suspend/resume */ + setidt(IPI_SUSPEND, IDTVEC(cpususpend), + SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); /* Set boot_cpu_id if needed. */ if (boot_cpu_id == -1) { @@ -1498,6 +1502,38 @@ cpustop_handler(void) } /* + * Handle an IPI_SUSPEND by saving our current context and spinning until we + * are resumed. + */ +void +cpususpend_handler(void) +{ + u_int cpu; + + cpu = PCPU_GET(cpuid); + + if (suspendctx(susppcbs[cpu])) { + wbinvd(); + CPU_SET_ATOMIC(cpu, &stopped_cpus); + } else { + pmap_init_pat(); + PCPU_SET(switchtime, 0); + PCPU_SET(switchticks, ticks); + susppcbs[cpu]->pcb_eip = 0; + } + + /* Wait for resume */ + while (!CPU_ISSET(cpu, &started_cpus)) + ia32_pause(); + + CPU_CLR_ATOMIC(cpu, &started_cpus); + CPU_CLR_ATOMIC(cpu, &stopped_cpus); + + /* Resume MCA and local APIC */ + mca_resume(); + lapic_setup(0); +} +/* * This is called once the rest of the system is up and running and we're * ready to let the AP's out of the pen. */ |