From owner-freebsd-amd64@FreeBSD.ORG Mon Apr 20 16:21:59 2015 Return-Path: Delivered-To: amd64@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id AE60665E; Mon, 20 Apr 2015 16:21:59 +0000 (UTC) Received: from kib.kiev.ua (kib.kiev.ua [IPv6:2001:470:d5e7:1::1]) (using TLSv1 with cipher DHE-RSA-CAMELLIA256-SHA (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 21809B3A; Mon, 20 Apr 2015 16:21:58 +0000 (UTC) Received: from tom.home (kostik@localhost [127.0.0.1]) by kib.kiev.ua (8.14.9/8.14.9) with ESMTP id t3KGLo1S072553 (version=TLSv1/SSLv3 cipher=DHE-RSA-CAMELLIA256-SHA bits=256 verify=NO); Mon, 20 Apr 2015 19:21:50 +0300 (EEST) (envelope-from kostikbel@gmail.com) DKIM-Filter: OpenDKIM Filter v2.9.2 kib.kiev.ua t3KGLo1S072553 Received: (from kostik@localhost) by tom.home (8.14.9/8.14.9/Submit) id t3KGLoJ6072552; Mon, 20 Apr 2015 19:21:50 +0300 (EEST) (envelope-from kostikbel@gmail.com) X-Authentication-Warning: tom.home: kostik set sender to kostikbel@gmail.com using -f Date: Mon, 20 Apr 2015 19:21:50 +0300 From: Konstantin Belousov To: arch@freebsd.org, amd64@freebsd.org Subject: Move x86 idle code to the x86/ common place. Message-ID: <20150420162149.GE2390@kib.kiev.ua> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.23 (2014-03-12) X-Spam-Status: No, score=-2.0 required=5.0 tests=ALL_TRUSTED,BAYES_00, DKIM_ADSP_CUSTOM_MED,FREEMAIL_FROM,NML_ADSP_CUSTOM_MED autolearn=no autolearn_force=no version=3.4.0 X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on tom.home X-BeenThere: freebsd-amd64@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Porting FreeBSD to the AMD64 platform List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 20 Apr 2015 16:21:59 -0000 Below is the patch which unifies some code from sys/{amd64/amd64,i386/i386}/machdep.c into the new shared file sys/x86/x86/cpu_machdep.c. Most of the code is related to handling the idle CPU state, but there is some additional trivialities like cpu_boot() etc. The move is mostly a preparation for some other changes to the idle infrastructure. I did not wanted to make same changes twice. Make universe passed with the patch, I successfully booted debug amd64 kernel and UP i386. Comments ? diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index 4c20e4f..3230937 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -578,375 +578,6 @@ freebsd4_sigreturn(struct thread *td, struct freebsd4_sigreturn_args *uap) } #endif - -/* - * Machine dependent boot() routine - * - * I haven't seen anything to put here yet - * Possibly some stuff might be grafted back here from boot() - */ -void -cpu_boot(int howto) -{ -} - -/* - * Flush the D-cache for non-DMA I/O so that the I-cache can - * be made coherent later. - */ -void -cpu_flush_dcache(void *ptr, size_t len) -{ - /* Not applicable */ -} - -/* Get current clock frequency for the given cpu id. */ -int -cpu_est_clockrate(int cpu_id, uint64_t *rate) -{ - uint64_t tsc1, tsc2; - uint64_t acnt, mcnt, perf; - register_t reg; - - if (pcpu_find(cpu_id) == NULL || rate == NULL) - return (EINVAL); - - /* - * If TSC is P-state invariant and APERF/MPERF MSRs do not exist, - * DELAY(9) based logic fails. - */ - if (tsc_is_invariant && !tsc_perf_stat) - return (EOPNOTSUPP); - -#ifdef SMP - if (smp_cpus > 1) { - /* Schedule ourselves on the indicated cpu. */ - thread_lock(curthread); - sched_bind(curthread, cpu_id); - thread_unlock(curthread); - } -#endif - - /* Calibrate by measuring a short delay. */ - reg = intr_disable(); - if (tsc_is_invariant) { - wrmsr(MSR_MPERF, 0); - wrmsr(MSR_APERF, 0); - tsc1 = rdtsc(); - DELAY(1000); - mcnt = rdmsr(MSR_MPERF); - acnt = rdmsr(MSR_APERF); - tsc2 = rdtsc(); - intr_restore(reg); - perf = 1000 * acnt / mcnt; - *rate = (tsc2 - tsc1) * perf; - } else { - tsc1 = rdtsc(); - DELAY(1000); - tsc2 = rdtsc(); - intr_restore(reg); - *rate = (tsc2 - tsc1) * 1000; - } - -#ifdef SMP - if (smp_cpus > 1) { - thread_lock(curthread); - sched_unbind(curthread); - thread_unlock(curthread); - } -#endif - - return (0); -} - -/* - * Shutdown the CPU as much as possible - */ -void -cpu_halt(void) -{ - for (;;) - halt(); -} - -void (*cpu_idle_hook)(sbintime_t) = NULL; /* ACPI idle hook. */ -static int cpu_ident_amdc1e = 0; /* AMD C1E supported. */ -static int idle_mwait = 1; /* Use MONITOR/MWAIT for short idle. */ -SYSCTL_INT(_machdep, OID_AUTO, idle_mwait, CTLFLAG_RWTUN, &idle_mwait, - 0, "Use MONITOR/MWAIT for short idle"); - -#define STATE_RUNNING 0x0 -#define STATE_MWAIT 0x1 -#define STATE_SLEEPING 0x2 - -static void -cpu_idle_acpi(sbintime_t sbt) -{ - int *state; - - state = (int *)PCPU_PTR(monitorbuf); - *state = STATE_SLEEPING; - - /* See comments in cpu_idle_hlt(). */ - disable_intr(); - if (sched_runnable()) - enable_intr(); - else if (cpu_idle_hook) - cpu_idle_hook(sbt); - else - __asm __volatile("sti; hlt"); - *state = STATE_RUNNING; -} - -static void -cpu_idle_hlt(sbintime_t sbt) -{ - int *state; - - state = (int *)PCPU_PTR(monitorbuf); - *state = STATE_SLEEPING; - - /* - * Since we may be in a critical section from cpu_idle(), if - * an interrupt fires during that critical section we may have - * a pending preemption. If the CPU halts, then that thread - * may not execute until a later interrupt awakens the CPU. - * To handle this race, check for a runnable thread after - * disabling interrupts and immediately return if one is - * found. Also, we must absolutely guarentee that hlt is - * the next instruction after sti. This ensures that any - * interrupt that fires after the call to disable_intr() will - * immediately awaken the CPU from hlt. Finally, please note - * that on x86 this works fine because of interrupts enabled only - * after the instruction following sti takes place, while IF is set - * to 1 immediately, allowing hlt instruction to acknowledge the - * interrupt. - */ - disable_intr(); - if (sched_runnable()) - enable_intr(); - else - __asm __volatile("sti; hlt"); - *state = STATE_RUNNING; -} - -static void -cpu_idle_mwait(sbintime_t sbt) -{ - int *state; - - state = (int *)PCPU_PTR(monitorbuf); - *state = STATE_MWAIT; - - /* See comments in cpu_idle_hlt(). */ - disable_intr(); - if (sched_runnable()) { - enable_intr(); - *state = STATE_RUNNING; - return; - } - cpu_monitor(state, 0, 0); - if (*state == STATE_MWAIT) - __asm __volatile("sti; mwait" : : "a" (MWAIT_C1), "c" (0)); - else - enable_intr(); - *state = STATE_RUNNING; -} - -static void -cpu_idle_spin(sbintime_t sbt) -{ - int *state; - int i; - - state = (int *)PCPU_PTR(monitorbuf); - *state = STATE_RUNNING; - - /* - * The sched_runnable() call is racy but as long as there is - * a loop missing it one time will have just a little impact if any - * (and it is much better than missing the check at all). - */ - for (i = 0; i < 1000; i++) { - if (sched_runnable()) - return; - cpu_spinwait(); - } -} - -/* - * C1E renders the local APIC timer dead, so we disable it by - * reading the Interrupt Pending Message register and clearing - * both C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27). - * - * Reference: - * "BIOS and Kernel Developer's Guide for AMD NPT Family 0Fh Processors" - * #32559 revision 3.00+ - */ -#define MSR_AMDK8_IPM 0xc0010055 -#define AMDK8_SMIONCMPHALT (1ULL << 27) -#define AMDK8_C1EONCMPHALT (1ULL << 28) -#define AMDK8_CMPHALT (AMDK8_SMIONCMPHALT | AMDK8_C1EONCMPHALT) - -static void -cpu_probe_amdc1e(void) -{ - - /* - * Detect the presence of C1E capability mostly on latest - * dual-cores (or future) k8 family. - */ - if (cpu_vendor_id == CPU_VENDOR_AMD && - (cpu_id & 0x00000f00) == 0x00000f00 && - (cpu_id & 0x0fff0000) >= 0x00040000) { - cpu_ident_amdc1e = 1; - } -} - -void (*cpu_idle_fn)(sbintime_t) = cpu_idle_acpi; - -void -cpu_idle(int busy) -{ - uint64_t msr; - sbintime_t sbt = -1; - - CTR2(KTR_SPARE2, "cpu_idle(%d) at %d", - busy, curcpu); -#ifdef MP_WATCHDOG - ap_watchdog(PCPU_GET(cpuid)); -#endif - /* If we are busy - try to use fast methods. */ - if (busy) { - if ((cpu_feature2 & CPUID2_MON) && idle_mwait) { - cpu_idle_mwait(busy); - goto out; - } - } - - /* If we have time - switch timers into idle mode. */ - if (!busy) { - critical_enter(); - sbt = cpu_idleclock(); - } - - /* Apply AMD APIC timer C1E workaround. */ - if (cpu_ident_amdc1e && cpu_disable_c3_sleep) { - msr = rdmsr(MSR_AMDK8_IPM); - if (msr & AMDK8_CMPHALT) - wrmsr(MSR_AMDK8_IPM, msr & ~AMDK8_CMPHALT); - } - - /* Call main idle method. */ - cpu_idle_fn(sbt); - - /* Switch timers back into active mode. */ - if (!busy) { - cpu_activeclock(); - critical_exit(); - } -out: - CTR2(KTR_SPARE2, "cpu_idle(%d) at %d done", - busy, curcpu); -} - -int -cpu_idle_wakeup(int cpu) -{ - struct pcpu *pcpu; - int *state; - - pcpu = pcpu_find(cpu); - state = (int *)pcpu->pc_monitorbuf; - /* - * This doesn't need to be atomic since missing the race will - * simply result in unnecessary IPIs. - */ - if (*state == STATE_SLEEPING) - return (0); - if (*state == STATE_MWAIT) - *state = STATE_RUNNING; - return (1); -} - -/* - * Ordered by speed/power consumption. - */ -struct { - void *id_fn; - char *id_name; -} idle_tbl[] = { - { cpu_idle_spin, "spin" }, - { cpu_idle_mwait, "mwait" }, - { cpu_idle_hlt, "hlt" }, - { cpu_idle_acpi, "acpi" }, - { NULL, NULL } -}; - -static int -idle_sysctl_available(SYSCTL_HANDLER_ARGS) -{ - char *avail, *p; - int error; - int i; - - avail = malloc(256, M_TEMP, M_WAITOK); - p = avail; - for (i = 0; idle_tbl[i].id_name != NULL; i++) { - if (strstr(idle_tbl[i].id_name, "mwait") && - (cpu_feature2 & CPUID2_MON) == 0) - continue; - if (strcmp(idle_tbl[i].id_name, "acpi") == 0 && - cpu_idle_hook == NULL) - continue; - p += sprintf(p, "%s%s", p != avail ? ", " : "", - idle_tbl[i].id_name); - } - error = sysctl_handle_string(oidp, avail, 0, req); - free(avail, M_TEMP); - return (error); -} - -SYSCTL_PROC(_machdep, OID_AUTO, idle_available, CTLTYPE_STRING | CTLFLAG_RD, - 0, 0, idle_sysctl_available, "A", "list of available idle functions"); - -static int -idle_sysctl(SYSCTL_HANDLER_ARGS) -{ - char buf[16]; - int error; - char *p; - int i; - - p = "unknown"; - for (i = 0; idle_tbl[i].id_name != NULL; i++) { - if (idle_tbl[i].id_fn == cpu_idle_fn) { - p = idle_tbl[i].id_name; - break; - } - } - strncpy(buf, p, sizeof(buf)); - error = sysctl_handle_string(oidp, buf, sizeof(buf), req); - if (error != 0 || req->newptr == NULL) - return (error); - for (i = 0; idle_tbl[i].id_name != NULL; i++) { - if (strstr(idle_tbl[i].id_name, "mwait") && - (cpu_feature2 & CPUID2_MON) == 0) - continue; - if (strcmp(idle_tbl[i].id_name, "acpi") == 0 && - cpu_idle_hook == NULL) - continue; - if (strcmp(idle_tbl[i].id_name, buf)) - continue; - cpu_idle_fn = idle_tbl[i].id_fn; - return (0); - } - return (EINVAL); -} - -SYSCTL_PROC(_machdep, OID_AUTO, idle, CTLTYPE_STRING | CTLFLAG_RW, 0, 0, - idle_sysctl, "A", "currently selected idle function"); - /* * Reset registers to default values on exec. */ diff --git a/sys/amd64/include/md_var.h b/sys/amd64/include/md_var.h index ccde0e3..9083421 100644 --- a/sys/amd64/include/md_var.h +++ b/sys/amd64/include/md_var.h @@ -91,6 +91,7 @@ struct dumperinfo; void *alloc_fpusave(int flags); void amd64_syscall(struct thread *td, int traced); void busdma_swi(void); +void cpu_probe_amdc1e(void); void cpu_setregs(void); void doreti_iret(void) __asm(__STRING(doreti_iret)); void doreti_iret_fault(void) __asm(__STRING(doreti_iret_fault)); diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64 index 4910903..ae71c39 100644 --- a/sys/conf/files.amd64 +++ b/sys/conf/files.amd64 @@ -558,6 +558,7 @@ x86/pci/pci_bus.c optional pci x86/pci/qpi.c optional pci x86/x86/busdma_bounce.c standard x86/x86/busdma_machdep.c standard +x86/x86/cpu_machdep.c standard x86/x86/dump_machdep.c standard x86/x86/fdt_machdep.c optional fdt x86/x86/identcpu.c standard diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 index 1873514..f072247 100644 --- a/sys/conf/files.i386 +++ b/sys/conf/files.i386 @@ -576,6 +576,7 @@ x86/pci/pci_bus.c optional pci x86/pci/qpi.c optional pci x86/x86/busdma_bounce.c standard x86/x86/busdma_machdep.c standard +x86/x86/cpu_machdep.c standard x86/x86/dump_machdep.c standard x86/x86/fdt_machdep.c optional fdt x86/x86/identcpu.c standard diff --git a/sys/conf/files.pc98 b/sys/conf/files.pc98 index be67ce4..f95d0bb 100644 --- a/sys/conf/files.pc98 +++ b/sys/conf/files.pc98 @@ -248,6 +248,7 @@ x86/isa/isa.c optional isa x86/pci/pci_bus.c optional pci x86/x86/busdma_bounce.c standard x86/x86/busdma_machdep.c standard +x86/x86/cpu_machdep.c standard x86/x86/dump_machdep.c standard x86/x86/identcpu.c standard x86/x86/intr_machdep.c standard diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c index 123db4e..72f7685 100644 --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -1176,427 +1176,6 @@ sys_sigreturn(td, uap) } /* - * Machine dependent boot() routine - * - * I haven't seen anything to put here yet - * Possibly some stuff might be grafted back here from boot() - */ -void -cpu_boot(int howto) -{ -} - -/* - * Flush the D-cache for non-DMA I/O so that the I-cache can - * be made coherent later. - */ -void -cpu_flush_dcache(void *ptr, size_t len) -{ - /* Not applicable */ -} - -/* Get current clock frequency for the given cpu id. */ -int -cpu_est_clockrate(int cpu_id, uint64_t *rate) -{ - uint64_t tsc1, tsc2; - uint64_t acnt, mcnt, perf; - register_t reg; - - if (pcpu_find(cpu_id) == NULL || rate == NULL) - return (EINVAL); - if ((cpu_feature & CPUID_TSC) == 0) - return (EOPNOTSUPP); - - /* - * If TSC is P-state invariant and APERF/MPERF MSRs do not exist, - * DELAY(9) based logic fails. - */ - if (tsc_is_invariant && !tsc_perf_stat) - return (EOPNOTSUPP); - -#ifdef SMP - if (smp_cpus > 1) { - /* Schedule ourselves on the indicated cpu. */ - thread_lock(curthread); - sched_bind(curthread, cpu_id); - thread_unlock(curthread); - } -#endif - - /* Calibrate by measuring a short delay. */ - reg = intr_disable(); - if (tsc_is_invariant) { - wrmsr(MSR_MPERF, 0); - wrmsr(MSR_APERF, 0); - tsc1 = rdtsc(); - DELAY(1000); - mcnt = rdmsr(MSR_MPERF); - acnt = rdmsr(MSR_APERF); - tsc2 = rdtsc(); - intr_restore(reg); - perf = 1000 * acnt / mcnt; - *rate = (tsc2 - tsc1) * perf; - } else { - tsc1 = rdtsc(); - DELAY(1000); - tsc2 = rdtsc(); - intr_restore(reg); - *rate = (tsc2 - tsc1) * 1000; - } - -#ifdef SMP - if (smp_cpus > 1) { - thread_lock(curthread); - sched_unbind(curthread); - thread_unlock(curthread); - } -#endif - - return (0); -} - -#ifdef XEN - -static void -idle_block(void) -{ - - HYPERVISOR_sched_op(SCHEDOP_block, 0); -} - -void -cpu_halt(void) -{ - HYPERVISOR_shutdown(SHUTDOWN_poweroff); -} - -int scheduler_running; - -static void -cpu_idle_hlt(sbintime_t sbt) -{ - - scheduler_running = 1; - enable_intr(); - idle_block(); -} - -#else -/* - * Shutdown the CPU as much as possible - */ -void -cpu_halt(void) -{ - for (;;) - halt(); -} - -#endif - -void (*cpu_idle_hook)(sbintime_t) = NULL; /* ACPI idle hook. */ -static int cpu_ident_amdc1e = 0; /* AMD C1E supported. */ -static int idle_mwait = 1; /* Use MONITOR/MWAIT for short idle. */ -SYSCTL_INT(_machdep, OID_AUTO, idle_mwait, CTLFLAG_RWTUN, &idle_mwait, - 0, "Use MONITOR/MWAIT for short idle"); - -#define STATE_RUNNING 0x0 -#define STATE_MWAIT 0x1 -#define STATE_SLEEPING 0x2 - -#ifndef PC98 -static void -cpu_idle_acpi(sbintime_t sbt) -{ - int *state; - - state = (int *)PCPU_PTR(monitorbuf); - *state = STATE_SLEEPING; - - /* See comments in cpu_idle_hlt(). */ - disable_intr(); - if (sched_runnable()) - enable_intr(); - else if (cpu_idle_hook) - cpu_idle_hook(sbt); - else - __asm __volatile("sti; hlt"); - *state = STATE_RUNNING; -} -#endif /* !PC98 */ - -#ifndef XEN -static void -cpu_idle_hlt(sbintime_t sbt) -{ - int *state; - - state = (int *)PCPU_PTR(monitorbuf); - *state = STATE_SLEEPING; - - /* - * Since we may be in a critical section from cpu_idle(), if - * an interrupt fires during that critical section we may have - * a pending preemption. If the CPU halts, then that thread - * may not execute until a later interrupt awakens the CPU. - * To handle this race, check for a runnable thread after - * disabling interrupts and immediately return if one is - * found. Also, we must absolutely guarentee that hlt is - * the next instruction after sti. This ensures that any - * interrupt that fires after the call to disable_intr() will - * immediately awaken the CPU from hlt. Finally, please note - * that on x86 this works fine because of interrupts enabled only - * after the instruction following sti takes place, while IF is set - * to 1 immediately, allowing hlt instruction to acknowledge the - * interrupt. - */ - disable_intr(); - if (sched_runnable()) - enable_intr(); - else - __asm __volatile("sti; hlt"); - *state = STATE_RUNNING; -} -#endif - -static void -cpu_idle_mwait(sbintime_t sbt) -{ - int *state; - - state = (int *)PCPU_PTR(monitorbuf); - *state = STATE_MWAIT; - - /* See comments in cpu_idle_hlt(). */ - disable_intr(); - if (sched_runnable()) { - enable_intr(); - *state = STATE_RUNNING; - return; - } - cpu_monitor(state, 0, 0); - if (*state == STATE_MWAIT) - __asm __volatile("sti; mwait" : : "a" (MWAIT_C1), "c" (0)); - else - enable_intr(); - *state = STATE_RUNNING; -} - -static void -cpu_idle_spin(sbintime_t sbt) -{ - int *state; - int i; - - state = (int *)PCPU_PTR(monitorbuf); - *state = STATE_RUNNING; - - /* - * The sched_runnable() call is racy but as long as there is - * a loop missing it one time will have just a little impact if any - * (and it is much better than missing the check at all). - */ - for (i = 0; i < 1000; i++) { - if (sched_runnable()) - return; - cpu_spinwait(); - } -} - -/* - * C1E renders the local APIC timer dead, so we disable it by - * reading the Interrupt Pending Message register and clearing - * both C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27). - * - * Reference: - * "BIOS and Kernel Developer's Guide for AMD NPT Family 0Fh Processors" - * #32559 revision 3.00+ - */ -#define MSR_AMDK8_IPM 0xc0010055 -#define AMDK8_SMIONCMPHALT (1ULL << 27) -#define AMDK8_C1EONCMPHALT (1ULL << 28) -#define AMDK8_CMPHALT (AMDK8_SMIONCMPHALT | AMDK8_C1EONCMPHALT) - -static void -cpu_probe_amdc1e(void) -{ - - /* - * Detect the presence of C1E capability mostly on latest - * dual-cores (or future) k8 family. - */ - if (cpu_vendor_id == CPU_VENDOR_AMD && - (cpu_id & 0x00000f00) == 0x00000f00 && - (cpu_id & 0x0fff0000) >= 0x00040000) { - cpu_ident_amdc1e = 1; - } -} - -#if defined(PC98) || defined(XEN) -void (*cpu_idle_fn)(sbintime_t) = cpu_idle_hlt; -#else -void (*cpu_idle_fn)(sbintime_t) = cpu_idle_acpi; -#endif - -void -cpu_idle(int busy) -{ -#ifndef XEN - uint64_t msr; -#endif - sbintime_t sbt = -1; - - CTR2(KTR_SPARE2, "cpu_idle(%d) at %d", - busy, curcpu); -#if defined(MP_WATCHDOG) && !defined(XEN) - ap_watchdog(PCPU_GET(cpuid)); -#endif -#ifndef XEN - /* If we are busy - try to use fast methods. */ - if (busy) { - if ((cpu_feature2 & CPUID2_MON) && idle_mwait) { - cpu_idle_mwait(busy); - goto out; - } - } -#endif - - /* If we have time - switch timers into idle mode. */ - if (!busy) { - critical_enter(); - sbt = cpu_idleclock(); - } - -#ifndef XEN - /* Apply AMD APIC timer C1E workaround. */ - if (cpu_ident_amdc1e && cpu_disable_c3_sleep) { - msr = rdmsr(MSR_AMDK8_IPM); - if (msr & AMDK8_CMPHALT) - wrmsr(MSR_AMDK8_IPM, msr & ~AMDK8_CMPHALT); - } -#endif - - /* Call main idle method. */ - cpu_idle_fn(sbt); - - /* Switch timers back into active mode. */ - if (!busy) { - cpu_activeclock(); - critical_exit(); - } -#ifndef XEN -out: -#endif - CTR2(KTR_SPARE2, "cpu_idle(%d) at %d done", - busy, curcpu); -} - -int -cpu_idle_wakeup(int cpu) -{ - struct pcpu *pcpu; - int *state; - - pcpu = pcpu_find(cpu); - state = (int *)pcpu->pc_monitorbuf; - /* - * This doesn't need to be atomic since missing the race will - * simply result in unnecessary IPIs. - */ - if (*state == STATE_SLEEPING) - return (0); - if (*state == STATE_MWAIT) - *state = STATE_RUNNING; - return (1); -} - -/* - * Ordered by speed/power consumption. - */ -struct { - void *id_fn; - char *id_name; -} idle_tbl[] = { - { cpu_idle_spin, "spin" }, - { cpu_idle_mwait, "mwait" }, - { cpu_idle_hlt, "hlt" }, -#ifndef PC98 - { cpu_idle_acpi, "acpi" }, -#endif - { NULL, NULL } -}; - -static int -idle_sysctl_available(SYSCTL_HANDLER_ARGS) -{ - char *avail, *p; - int error; - int i; - - avail = malloc(256, M_TEMP, M_WAITOK); - p = avail; - for (i = 0; idle_tbl[i].id_name != NULL; i++) { - if (strstr(idle_tbl[i].id_name, "mwait") && - (cpu_feature2 & CPUID2_MON) == 0) - continue; -#ifndef PC98 - if (strcmp(idle_tbl[i].id_name, "acpi") == 0 && - cpu_idle_hook == NULL) - continue; -#endif - p += sprintf(p, "%s%s", p != avail ? ", " : "", - idle_tbl[i].id_name); - } - error = sysctl_handle_string(oidp, avail, 0, req); - free(avail, M_TEMP); - return (error); -} - -SYSCTL_PROC(_machdep, OID_AUTO, idle_available, CTLTYPE_STRING | CTLFLAG_RD, - 0, 0, idle_sysctl_available, "A", "list of available idle functions"); - -static int -idle_sysctl(SYSCTL_HANDLER_ARGS) -{ - char buf[16]; - int error; - char *p; - int i; - - p = "unknown"; - for (i = 0; idle_tbl[i].id_name != NULL; i++) { - if (idle_tbl[i].id_fn == cpu_idle_fn) { - p = idle_tbl[i].id_name; - break; - } - } - strncpy(buf, p, sizeof(buf)); - error = sysctl_handle_string(oidp, buf, sizeof(buf), req); - if (error != 0 || req->newptr == NULL) - return (error); - for (i = 0; idle_tbl[i].id_name != NULL; i++) { - if (strstr(idle_tbl[i].id_name, "mwait") && - (cpu_feature2 & CPUID2_MON) == 0) - continue; -#ifndef PC98 - if (strcmp(idle_tbl[i].id_name, "acpi") == 0 && - cpu_idle_hook == NULL) - continue; -#endif - if (strcmp(idle_tbl[i].id_name, buf)) - continue; - cpu_idle_fn = idle_tbl[i].id_fn; - return (0); - } - return (EINVAL); -} - -SYSCTL_PROC(_machdep, OID_AUTO, idle, CTLTYPE_STRING | CTLFLAG_RW, 0, 0, - idle_sysctl, "A", "currently selected idle function"); - -/* * Reset registers to default values on exec. */ void diff --git a/sys/i386/include/md_var.h b/sys/i386/include/md_var.h index 339dff3..bffdd57 100644 --- a/sys/i386/include/md_var.h +++ b/sys/i386/include/md_var.h @@ -97,6 +97,7 @@ struct dumperinfo; void *alloc_fpusave(int flags); void bcopyb(const void *from, void *to, size_t len); void busdma_swi(void); +void cpu_probe_amdc1e(void); void cpu_setregs(void); void cpu_switch_load_gs(void) __asm(__STRING(cpu_switch_load_gs)); void doreti_iret(void) __asm(__STRING(doreti_iret)); diff --git a/sys/x86/x86/cpu_machdep.c b/sys/x86/x86/cpu_machdep.c new file mode 100644 index 0000000..846a123 --- /dev/null +++ b/sys/x86/x86/cpu_machdep.c @@ -0,0 +1,533 @@ +/*- + * Copyright (c) 2003 Peter Wemm. + * Copyright (c) 1992 Terrence R. Lambert. + * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_atpic.h" +#include "opt_compat.h" +#include "opt_cpu.h" +#include "opt_ddb.h" +#include "opt_inet.h" +#include "opt_isa.h" +#include "opt_kstack_pages.h" +#include "opt_maxmem.h" +#include "opt_mp_watchdog.h" +#include "opt_perfmon.h" +#include "opt_platform.h" +#ifdef __i386__ +#include "opt_npx.h" +#include "opt_apic.h" +#include "opt_xbox.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SMP +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#ifdef PERFMON +#include +#endif +#include +#ifdef SMP +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef XEN +/* XEN includes */ +#include +#include +#include +#include +#include +#endif + +/* + * Machine dependent boot() routine + * + * I haven't seen anything to put here yet + * Possibly some stuff might be grafted back here from boot() + */ +void +cpu_boot(int howto) +{ +} + +/* + * Flush the D-cache for non-DMA I/O so that the I-cache can + * be made coherent later. + */ +void +cpu_flush_dcache(void *ptr, size_t len) +{ + /* Not applicable */ +} + +/* Get current clock frequency for the given cpu id. */ +int +cpu_est_clockrate(int cpu_id, uint64_t *rate) +{ + uint64_t tsc1, tsc2; + uint64_t acnt, mcnt, perf; + register_t reg; + + if (pcpu_find(cpu_id) == NULL || rate == NULL) + return (EINVAL); +#ifdef __i386__ + if ((cpu_feature & CPUID_TSC) == 0) + return (EOPNOTSUPP); +#endif + + /* + * If TSC is P-state invariant and APERF/MPERF MSRs do not exist, + * DELAY(9) based logic fails. + */ + if (tsc_is_invariant && !tsc_perf_stat) + return (EOPNOTSUPP); + +#ifdef SMP + if (smp_cpus > 1) { + /* Schedule ourselves on the indicated cpu. */ + thread_lock(curthread); + sched_bind(curthread, cpu_id); + thread_unlock(curthread); + } +#endif + + /* Calibrate by measuring a short delay. */ + reg = intr_disable(); + if (tsc_is_invariant) { + wrmsr(MSR_MPERF, 0); + wrmsr(MSR_APERF, 0); + tsc1 = rdtsc(); + DELAY(1000); + mcnt = rdmsr(MSR_MPERF); + acnt = rdmsr(MSR_APERF); + tsc2 = rdtsc(); + intr_restore(reg); + perf = 1000 * acnt / mcnt; + *rate = (tsc2 - tsc1) * perf; + } else { + tsc1 = rdtsc(); + DELAY(1000); + tsc2 = rdtsc(); + intr_restore(reg); + *rate = (tsc2 - tsc1) * 1000; + } + +#ifdef SMP + if (smp_cpus > 1) { + thread_lock(curthread); + sched_unbind(curthread); + thread_unlock(curthread); + } +#endif + + return (0); +} + +#if defined(__i386__) && defined(XEN) + +static void +idle_block(void) +{ + + HYPERVISOR_sched_op(SCHEDOP_block, 0); +} + +void +cpu_halt(void) +{ + HYPERVISOR_shutdown(SHUTDOWN_poweroff); +} + +int scheduler_running; + +static void +cpu_idle_hlt(sbintime_t sbt) +{ + + scheduler_running = 1; + enable_intr(); + idle_block(); +} + +#else +/* + * Shutdown the CPU as much as possible + */ +void +cpu_halt(void) +{ + for (;;) + halt(); +} + +#endif + +void (*cpu_idle_hook)(sbintime_t) = NULL; /* ACPI idle hook. */ +static int cpu_ident_amdc1e = 0; /* AMD C1E supported. */ +static int idle_mwait = 1; /* Use MONITOR/MWAIT for short idle. */ +SYSCTL_INT(_machdep, OID_AUTO, idle_mwait, CTLFLAG_RWTUN, &idle_mwait, + 0, "Use MONITOR/MWAIT for short idle"); + +#define STATE_RUNNING 0x0 +#define STATE_MWAIT 0x1 +#define STATE_SLEEPING 0x2 + +#ifndef PC98 +static void +cpu_idle_acpi(sbintime_t sbt) +{ + int *state; + + state = (int *)PCPU_PTR(monitorbuf); + *state = STATE_SLEEPING; + + /* See comments in cpu_idle_hlt(). */ + disable_intr(); + if (sched_runnable()) + enable_intr(); + else if (cpu_idle_hook) + cpu_idle_hook(sbt); + else + __asm __volatile("sti; hlt"); + *state = STATE_RUNNING; +} +#endif /* !PC98 */ + +#if !defined(__i386__) || !defined(XEN) +static void +cpu_idle_hlt(sbintime_t sbt) +{ + int *state; + + state = (int *)PCPU_PTR(monitorbuf); + *state = STATE_SLEEPING; + + /* + * Since we may be in a critical section from cpu_idle(), if + * an interrupt fires during that critical section we may have + * a pending preemption. If the CPU halts, then that thread + * may not execute until a later interrupt awakens the CPU. + * To handle this race, check for a runnable thread after + * disabling interrupts and immediately return if one is + * found. Also, we must absolutely guarentee that hlt is + * the next instruction after sti. This ensures that any + * interrupt that fires after the call to disable_intr() will + * immediately awaken the CPU from hlt. Finally, please note + * that on x86 this works fine because of interrupts enabled only + * after the instruction following sti takes place, while IF is set + * to 1 immediately, allowing hlt instruction to acknowledge the + * interrupt. + */ + disable_intr(); + if (sched_runnable()) + enable_intr(); + else + __asm __volatile("sti; hlt"); + *state = STATE_RUNNING; +} +#endif + +static void +cpu_idle_mwait(sbintime_t sbt) +{ + int *state; + + state = (int *)PCPU_PTR(monitorbuf); + *state = STATE_MWAIT; + + /* See comments in cpu_idle_hlt(). */ + disable_intr(); + if (sched_runnable()) { + enable_intr(); + *state = STATE_RUNNING; + return; + } + cpu_monitor(state, 0, 0); + if (*state == STATE_MWAIT) + __asm __volatile("sti; mwait" : : "a" (MWAIT_C1), "c" (0)); + else + enable_intr(); + *state = STATE_RUNNING; +} + +static void +cpu_idle_spin(sbintime_t sbt) +{ + int *state; + int i; + + state = (int *)PCPU_PTR(monitorbuf); + *state = STATE_RUNNING; + + /* + * The sched_runnable() call is racy but as long as there is + * a loop missing it one time will have just a little impact if any + * (and it is much better than missing the check at all). + */ + for (i = 0; i < 1000; i++) { + if (sched_runnable()) + return; + cpu_spinwait(); + } +} + +/* + * C1E renders the local APIC timer dead, so we disable it by + * reading the Interrupt Pending Message register and clearing + * both C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27). + * + * Reference: + * "BIOS and Kernel Developer's Guide for AMD NPT Family 0Fh Processors" + * #32559 revision 3.00+ + */ +#define MSR_AMDK8_IPM 0xc0010055 +#define AMDK8_SMIONCMPHALT (1ULL << 27) +#define AMDK8_C1EONCMPHALT (1ULL << 28) +#define AMDK8_CMPHALT (AMDK8_SMIONCMPHALT | AMDK8_C1EONCMPHALT) + +void +cpu_probe_amdc1e(void) +{ + + /* + * Detect the presence of C1E capability mostly on latest + * dual-cores (or future) k8 family. + */ + if (cpu_vendor_id == CPU_VENDOR_AMD && + (cpu_id & 0x00000f00) == 0x00000f00 && + (cpu_id & 0x0fff0000) >= 0x00040000) { + cpu_ident_amdc1e = 1; + } +} + +#if defined(__i386__) && (defined(PC98) || defined(XEN)) +void (*cpu_idle_fn)(sbintime_t) = cpu_idle_hlt; +#else +void (*cpu_idle_fn)(sbintime_t) = cpu_idle_acpi; +#endif + +void +cpu_idle(int busy) +{ +#if !defined(__i386__) || !defined(XEN) + uint64_t msr; +#endif + sbintime_t sbt = -1; + + CTR2(KTR_SPARE2, "cpu_idle(%d) at %d", + busy, curcpu); +#if defined(MP_WATCHDOG) && (!defined(__i386__) || !defined(XEN)) + ap_watchdog(PCPU_GET(cpuid)); +#endif +#if !defined(__i386__) || !defined(XEN) + /* If we are busy - try to use fast methods. */ + if (busy) { + if ((cpu_feature2 & CPUID2_MON) && idle_mwait) { + cpu_idle_mwait(busy); + goto out; + } + } +#endif + + /* If we have time - switch timers into idle mode. */ + if (!busy) { + critical_enter(); + sbt = cpu_idleclock(); + } + +#if !defined(__i386__) || !defined(XEN) + /* Apply AMD APIC timer C1E workaround. */ + if (cpu_ident_amdc1e && cpu_disable_c3_sleep) { + msr = rdmsr(MSR_AMDK8_IPM); + if (msr & AMDK8_CMPHALT) + wrmsr(MSR_AMDK8_IPM, msr & ~AMDK8_CMPHALT); + } +#endif + + /* Call main idle method. */ + cpu_idle_fn(sbt); + + /* Switch timers back into active mode. */ + if (!busy) { + cpu_activeclock(); + critical_exit(); + } +#if !defined(__i386__) || !defined(XEN) +out: +#endif + CTR2(KTR_SPARE2, "cpu_idle(%d) at %d done", + busy, curcpu); +} + +int +cpu_idle_wakeup(int cpu) +{ + struct pcpu *pcpu; + int *state; + + pcpu = pcpu_find(cpu); + state = (int *)pcpu->pc_monitorbuf; + /* + * This doesn't need to be atomic since missing the race will + * simply result in unnecessary IPIs. + */ + if (*state == STATE_SLEEPING) + return (0); + if (*state == STATE_MWAIT) + *state = STATE_RUNNING; + return (1); +} + +/* + * Ordered by speed/power consumption. + */ +struct { + void *id_fn; + char *id_name; +} idle_tbl[] = { + { cpu_idle_spin, "spin" }, + { cpu_idle_mwait, "mwait" }, + { cpu_idle_hlt, "hlt" }, +#if !defined(__i386__) || !defined(PC98) + { cpu_idle_acpi, "acpi" }, +#endif + { NULL, NULL } +}; + +static int +idle_sysctl_available(SYSCTL_HANDLER_ARGS) +{ + char *avail, *p; + int error; + int i; + + avail = malloc(256, M_TEMP, M_WAITOK); + p = avail; + for (i = 0; idle_tbl[i].id_name != NULL; i++) { + if (strstr(idle_tbl[i].id_name, "mwait") && + (cpu_feature2 & CPUID2_MON) == 0) + continue; +#if !defined(__i386__) || !defined(PC98) + if (strcmp(idle_tbl[i].id_name, "acpi") == 0 && + cpu_idle_hook == NULL) + continue; +#endif + p += sprintf(p, "%s%s", p != avail ? ", " : "", + idle_tbl[i].id_name); + } + error = sysctl_handle_string(oidp, avail, 0, req); + free(avail, M_TEMP); + return (error); +} + +SYSCTL_PROC(_machdep, OID_AUTO, idle_available, CTLTYPE_STRING | CTLFLAG_RD, + 0, 0, idle_sysctl_available, "A", "list of available idle functions"); + +static int +idle_sysctl(SYSCTL_HANDLER_ARGS) +{ + char buf[16]; + int error; + char *p; + int i; + + p = "unknown"; + for (i = 0; idle_tbl[i].id_name != NULL; i++) { + if (idle_tbl[i].id_fn == cpu_idle_fn) { + p = idle_tbl[i].id_name; + break; + } + } + strncpy(buf, p, sizeof(buf)); + error = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error != 0 || req->newptr == NULL) + return (error); + for (i = 0; idle_tbl[i].id_name != NULL; i++) { + if (strstr(idle_tbl[i].id_name, "mwait") && + (cpu_feature2 & CPUID2_MON) == 0) + continue; +#if !defined(__i386__) || !defined(PC98) + if (strcmp(idle_tbl[i].id_name, "acpi") == 0 && + cpu_idle_hook == NULL) + continue; +#endif + if (strcmp(idle_tbl[i].id_name, buf)) + continue; + cpu_idle_fn = idle_tbl[i].id_fn; + return (0); + } + return (EINVAL); +} + +SYSCTL_PROC(_machdep, OID_AUTO, idle, CTLTYPE_STRING | CTLFLAG_RW, 0, 0, + idle_sysctl, "A", "currently selected idle function"); From owner-freebsd-amd64@FreeBSD.ORG Mon Apr 20 21:25:31 2015 Return-Path: Delivered-To: amd64@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id DA8A321C; Mon, 20 Apr 2015 21:25:31 +0000 (UTC) Received: from mail-wi0-x22e.google.com (mail-wi0-x22e.google.com [IPv6:2a00:1450:400c:c05::22e]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (Client CN "smtp.gmail.com", Issuer "Google Internet Authority G2" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 7698D662; Mon, 20 Apr 2015 21:25:31 +0000 (UTC) Received: by wiun10 with SMTP id n10so107234242wiu.1; Mon, 20 Apr 2015 14:25:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; bh=G5NBfn+u03Zdf81gPvgH17MBIThAdSC9cgGtECLYaeQ=; b=l11sTyXhlRj2sSHwEYcCnrOkpLNuK2JiyFKuKwrRP4EpbGhHkUhnJfBDKNrESD+lr1 CjN/Czj0QNCOadQlr60G96AcvHGH67r1oIr7FsRSttyiFSWerpx2BF3YLxRWUVioF1rq RLIExQuBO861sh9X+m9i9J4QEv0G+Uz0/uq2Dl8ft/GtERsf3cTjwJxqIN8/wMa2p7nI Y/QASXSCpYJbwE0so1g4SmExwx606fnE66iQbmgBK5W2YlWV4jGMyFh+Tyl+WEwQt/A/ 7Y4NW+v3sKYUUZjDbeAr7/nSC0nGwiugl8fuMUYhazHp6oHxluWk81O2zSv+2rUqGeUd PaxA== MIME-Version: 1.0 X-Received: by 10.180.104.137 with SMTP id ge9mr29625940wib.24.1429565129111; Mon, 20 Apr 2015 14:25:29 -0700 (PDT) Received: by 10.27.80.202 with HTTP; Mon, 20 Apr 2015 14:25:29 -0700 (PDT) In-Reply-To: <20150420162149.GE2390@kib.kiev.ua> References: <20150420162149.GE2390@kib.kiev.ua> Date: Tue, 21 Apr 2015 00:25:29 +0300 Message-ID: Subject: Re: Move x86 idle code to the x86/ common place. From: Sergey Kandaurov To: Konstantin Belousov Cc: arch@freebsd.org, amd64@freebsd.org Content-Type: text/plain; charset=ISO-8859-1 X-BeenThere: freebsd-amd64@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Porting FreeBSD to the AMD64 platform List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 20 Apr 2015 21:25:32 -0000 On 20 April 2015 at 19:21, Konstantin Belousov wrote: [..] > +struct { > + void *id_fn; > + char *id_name; > +} idle_tbl[] = { > + { cpu_idle_spin, "spin" }, > + { cpu_idle_mwait, "mwait" }, > + { cpu_idle_hlt, "hlt" }, > +#if !defined(__i386__) || !defined(PC98) > + { cpu_idle_acpi, "acpi" }, > +#endif > + { NULL, NULL } > +}; > + I believe this conditional could be left unchanged as #ifndef PC98 (also in several other places), given that pc98 may not be present other than under i386. Otherwise, looks good. -- wbr, pluknet From owner-freebsd-amd64@FreeBSD.ORG Tue Apr 21 08:31:42 2015 Return-Path: Delivered-To: amd64@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id E5B9C289; Tue, 21 Apr 2015 08:31:42 +0000 (UTC) Received: from kib.kiev.ua (kib.kiev.ua [IPv6:2001:470:d5e7:1::1]) (using TLSv1 with cipher DHE-RSA-CAMELLIA256-SHA (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 5AA691640; Tue, 21 Apr 2015 08:31:42 +0000 (UTC) Received: from tom.home (kostik@localhost [127.0.0.1]) by kib.kiev.ua (8.14.9/8.14.9) with ESMTP id t3L8VXQG014636 (version=TLSv1/SSLv3 cipher=DHE-RSA-CAMELLIA256-SHA bits=256 verify=NO); Tue, 21 Apr 2015 11:31:33 +0300 (EEST) (envelope-from kostikbel@gmail.com) DKIM-Filter: OpenDKIM Filter v2.9.2 kib.kiev.ua t3L8VXQG014636 Received: (from kostik@localhost) by tom.home (8.14.9/8.14.9/Submit) id t3L8VXAc014634; Tue, 21 Apr 2015 11:31:33 +0300 (EEST) (envelope-from kostikbel@gmail.com) X-Authentication-Warning: tom.home: kostik set sender to kostikbel@gmail.com using -f Date: Tue, 21 Apr 2015 11:31:33 +0300 From: Konstantin Belousov To: Sergey Kandaurov Cc: arch@freebsd.org, amd64@freebsd.org Subject: Re: Move x86 idle code to the x86/ common place. Message-ID: <20150421083133.GI2390@kib.kiev.ua> References: <20150420162149.GE2390@kib.kiev.ua> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.23 (2014-03-12) X-Spam-Status: No, score=-2.0 required=5.0 tests=ALL_TRUSTED,BAYES_00, DKIM_ADSP_CUSTOM_MED,FREEMAIL_FROM,NML_ADSP_CUSTOM_MED autolearn=no autolearn_force=no version=3.4.0 X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on tom.home X-BeenThere: freebsd-amd64@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Porting FreeBSD to the AMD64 platform List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 21 Apr 2015 08:31:43 -0000 On Tue, Apr 21, 2015 at 12:25:29AM +0300, Sergey Kandaurov wrote: > On 20 April 2015 at 19:21, Konstantin Belousov wrote: > [..] > > +struct { > > + void *id_fn; > > + char *id_name; > > +} idle_tbl[] = { > > + { cpu_idle_spin, "spin" }, > > + { cpu_idle_mwait, "mwait" }, > > + { cpu_idle_hlt, "hlt" }, > > +#if !defined(__i386__) || !defined(PC98) > > + { cpu_idle_acpi, "acpi" }, > > +#endif > > + { NULL, NULL } > > +}; > > + > > I believe this conditional could be left unchanged as #ifndef PC98 > (also in several other places), given that pc98 may not be present > other than under i386. Otherwise, looks good. Sure, you are correct. I know that PC98 is i386 only, and I considered both approaches when I did the merge. My decision to add explicit __i386__ check was to make it clearer for reader who might be interested in the __amd64__ flow. That said, I do not mind doing the pass to revert the #if !defined(__i386__) || !defined(PC98) to #ifndef PC98 if people consider this preferable. From owner-freebsd-amd64@FreeBSD.ORG Wed Apr 22 12:55:34 2015 Return-Path: Delivered-To: amd64@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 1DC5917B; Wed, 22 Apr 2015 12:55:34 +0000 (UTC) Received: from kib.kiev.ua (kib.kiev.ua [IPv6:2001:470:d5e7:1::1]) (using TLSv1 with cipher DHE-RSA-CAMELLIA256-SHA (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 8A7041D92; Wed, 22 Apr 2015 12:55:33 +0000 (UTC) Received: from tom.home (kostik@localhost [127.0.0.1]) by kib.kiev.ua (8.14.9/8.14.9) with ESMTP id t3MCtLrV060133 (version=TLSv1/SSLv3 cipher=DHE-RSA-CAMELLIA256-SHA bits=256 verify=NO); Wed, 22 Apr 2015 15:55:21 +0300 (EEST) (envelope-from kostikbel@gmail.com) DKIM-Filter: OpenDKIM Filter v2.9.2 kib.kiev.ua t3MCtLrV060133 Received: (from kostik@localhost) by tom.home (8.14.9/8.14.9/Submit) id t3MCtLnd060132; Wed, 22 Apr 2015 15:55:21 +0300 (EEST) (envelope-from kostikbel@gmail.com) X-Authentication-Warning: tom.home: kostik set sender to kostikbel@gmail.com using -f Date: Wed, 22 Apr 2015 15:55:21 +0300 From: Konstantin Belousov To: arch@freebsd.org, amd64@freebsd.org Subject: Cx MWAIT Message-ID: <20150422125521.GQ2390@kib.kiev.ua> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.23 (2014-03-12) X-Spam-Status: No, score=-2.0 required=5.0 tests=ALL_TRUSTED,BAYES_00, DKIM_ADSP_CUSTOM_MED,FREEMAIL_FROM,NML_ADSP_CUSTOM_MED autolearn=no autolearn_force=no version=3.4.0 X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on tom.home X-BeenThere: freebsd-amd64@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Porting FreeBSD to the AMD64 platform List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 22 Apr 2015 12:55:34 -0000 Below is the patch to start using mwait instead of 'legacy' port read to enter the higher Cx states when idle. This is the Intel' recommended way of entering Cx, using hints provided by the vendor-specific fixed function hardware GAS encoding. See the "Intel(R) Processor Vendor-Specific ACPI Interface Specification" revision 007. Patch was written after I become interested why my Haswell desktop test box does not report any C-states besides C1. It appeared to be due to combination of BIOS misconfiguration and FreeBSD code lacking mwait support. Also an enchanced C1 entry sequence, "I/O then halt", for coordination of C1 entry with PCH, is supported. The "sti;hlt" sequence usage was consolidated by calling acpi_cpu_c1(). Intel hardware automatically handles per-core and per-package state aggregated from the thread-local C-states, which is indicated as "hardware-coordinated" C-state entry. It is theoretically possible that OS must handle software-coordinated package C-entry, but I am not aware of real processors which need this mode. Intel is hw-coordinated, and it seems that AMD does not advertise mwait sequence for C-states at all. I know that BIOS _CST tables are believed to be buggy. In particular, for Linux, Intel wrote a driver which has hard-coded model tables with the encoding of supported C-states, latencies and caches/busmastering behaviour. I agree with avg that we cannot support this approach. I tried to keep the dev/acpica/acpi_cpu.c to be MI as much as possible. At least, all mwait-specific code is put under #ifdef x86. The acpi_PkgFFH_IntelCPU() helper to parse Intel FFH GAS is MI, but only usable on x86; I believe this is fine. Note that currently ACPI is only used on x86: we lost ia64, but it might be used on arm shortly. diff --git a/sys/amd64/acpica/acpi_machdep.c b/sys/amd64/acpica/acpi_machdep.c index 049b51bb4e..8f88a00 100644 --- a/sys/amd64/acpica/acpi_machdep.c +++ b/sys/amd64/acpica/acpi_machdep.c @@ -87,13 +87,6 @@ acpi_machdep_quirks(int *quirks) return (0); } -void -acpi_cpu_c1() -{ - - __asm __volatile("sti; hlt"); -} - /* * Support for mapping ACPI tables during early boot. Currently this * uses the crashdump map to map each table. However, the crashdump diff --git a/sys/amd64/include/md_var.h b/sys/amd64/include/md_var.h index 9083421..0813e5f 100644 --- a/sys/amd64/include/md_var.h +++ b/sys/amd64/include/md_var.h @@ -91,6 +91,7 @@ struct dumperinfo; void *alloc_fpusave(int flags); void amd64_syscall(struct thread *td, int traced); void busdma_swi(void); +bool cpu_mwait_usable(void); void cpu_probe_amdc1e(void); void cpu_setregs(void); void doreti_iret(void) __asm(__STRING(doreti_iret)); diff --git a/sys/dev/acpica/acpi_cpu.c b/sys/dev/acpica/acpi_cpu.c index 8df2782..3fb21a6 100644 --- a/sys/dev/acpica/acpi_cpu.c +++ b/sys/dev/acpica/acpi_cpu.c @@ -47,6 +47,8 @@ __FBSDID("$FreeBSD$"); #include #if defined(__amd64__) || defined(__i386__) #include +#include +#include #endif #include @@ -70,6 +72,10 @@ struct acpi_cx { uint32_t power; /* Power consumed (mW). */ int res_type; /* Resource type for p_lvlx. */ int res_rid; /* Resource ID for p_lvlx. */ + bool do_mwait; + uint32_t mwait_hint; + bool mwait_hw_coord; + bool mwait_bm_avoidance; }; #define MAX_CX_STATES 8 @@ -128,6 +134,12 @@ struct acpi_cpu_device { #define PIIX4_STOP_BREAK_MASK (PIIX4_BRLD_EN_IRQ0 | PIIX4_BRLD_EN_IRQ | PIIX4_BRLD_EN_IRQ8) #define PIIX4_PCNTRL_BST_EN (1<<10) +#define CST_FFH_VENDOR_INTEL 1 +#define CST_FFH_INTEL_CL_C1IO 1 +#define CST_FFH_INTEL_CL_MWAIT 2 +#define CST_FFH_MWAIT_HW_COORD 0x0001 +#define CST_FFH_MWAIT_BM_AVOID 0x0002 + /* Allow users to ignore processor orders in MADT. */ static int cpu_unordered; SYSCTL_INT(_debug_acpi, OID_AUTO, cpu_unordered, CTLFLAG_RDTUN, @@ -348,7 +360,17 @@ acpi_cpu_attach(device_t dev) * so advertise this ourselves. Note this is not the same as independent * SMP control where each CPU can have different settings. */ - sc->cpu_features = ACPI_CAP_SMP_SAME | ACPI_CAP_SMP_SAME_C3; + sc->cpu_features = ACPI_CAP_SMP_SAME | ACPI_CAP_SMP_SAME_C3 | + ACPI_CAP_C1_IO_HALT; + +#if defined(__i386__) || defined(__amd64__) + /* + * Ask for MWAIT modes if interrupts work reasonable with MWAIT. + */ + if (cpu_mwait_usable()) + sc->cpu_features |= ACPI_CAP_SMP_C1_NATIVE | ACPI_CAP_SMP_C3_NATIVE; +#endif + if (devclass_get_drivers(acpi_cpu_devclass, &drivers, &drv_count) == 0) { for (i = 0; i < drv_count; i++) { if (ACPI_GET_FEATURES(drivers[i], &features) == 0) @@ -720,6 +742,27 @@ acpi_cpu_generic_cx_probe(struct acpi_cpu_softc *sc) } } +static void +acpi_cpu_cx_cst_mwait(struct acpi_cx *cx_ptr, uint64_t address, int accsize) +{ + + cx_ptr->do_mwait = true; + cx_ptr->mwait_hint = address & 0xffffffff; + cx_ptr->mwait_hw_coord = (accsize & CST_FFH_MWAIT_HW_COORD) != 0; + cx_ptr->mwait_bm_avoidance = (accsize & CST_FFH_MWAIT_BM_AVOID) != 0; +} + +static void +acpi_cpu_cx_cst_free_plvlx(device_t cpu_dev, struct acpi_cx *cx_ptr) +{ + + if (cx_ptr->p_lvlx == NULL) + return; + bus_release_resource(cpu_dev, cx_ptr->res_type, cx_ptr->res_rid, + cx_ptr->p_lvlx); + cx_ptr->p_lvlx = NULL; +} + /* * Parse a _CST package and set up its Cx states. Since the _CST object * can change dynamically, our notify handler may call this function @@ -734,7 +777,8 @@ acpi_cpu_cx_cst(struct acpi_cpu_softc *sc) ACPI_OBJECT *top; ACPI_OBJECT *pkg; uint32_t count; - int i; + uint64_t address; + int i, vendor, class, accsize; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); @@ -790,6 +834,30 @@ acpi_cpu_cx_cst(struct acpi_cpu_softc *sc) /* Validate the state to see if we should use it. */ switch (cx_ptr->type) { case ACPI_STATE_C1: + acpi_cpu_cx_cst_free_plvlx(sc->cpu_dev, cx_ptr); +#if defined(__i386__) || defined(__amd64__) + if (acpi_PkgFFH_IntelCpu(pkg, 0, &vendor, &class, &address, + &accsize) == 0 && vendor == CST_FFH_VENDOR_INTEL) { + if (class == CST_FFH_INTEL_CL_C1IO) { + /* C1 I/O then Halt */ + cx_ptr->res_rid = sc->cpu_cx_count; + bus_set_resource(sc->cpu_dev, SYS_RES_IOPORT, + cx_ptr->res_rid, address, 1); + cx_ptr->p_lvlx = bus_alloc_resource_any(sc->cpu_dev, + SYS_RES_IOPORT, &cx_ptr->res_rid, RF_ACTIVE | + RF_SHAREABLE); + if (cx_ptr->p_lvlx == NULL) { + bus_delete_resource(sc->cpu_dev, SYS_RES_IOPORT, + cx_ptr->res_rid); + device_printf(sc->cpu_dev, + "C1 I/O failed to allocate port %d, " + "degrading to C1 Halt", (int)address); + } + } else if (class == CST_FFH_INTEL_CL_MWAIT) { + acpi_cpu_cx_cst_mwait(cx_ptr, address, accsize); + } + } +#endif if (sc->cpu_cx_states[0].type == ACPI_STATE_C0) { /* This is the first C1 state. Use the reserved slot. */ sc->cpu_cx_states[0] = *cx_ptr; @@ -818,23 +886,34 @@ acpi_cpu_cx_cst(struct acpi_cpu_softc *sc) } /* Free up any previous register. */ - if (cx_ptr->p_lvlx != NULL) { - bus_release_resource(sc->cpu_dev, cx_ptr->res_type, cx_ptr->res_rid, - cx_ptr->p_lvlx); - cx_ptr->p_lvlx = NULL; - } + acpi_cpu_cx_cst_free_plvlx(sc->cpu_dev, cx_ptr); /* Allocate the control register for C2 or C3. */ - cx_ptr->res_rid = sc->cpu_cx_count; - acpi_PkgGas(sc->cpu_dev, pkg, 0, &cx_ptr->res_type, &cx_ptr->res_rid, - &cx_ptr->p_lvlx, RF_SHAREABLE); - if (cx_ptr->p_lvlx) { +#if defined(__i386__) || defined(__amd64__) + if (acpi_PkgFFH_IntelCpu(pkg, 0, &vendor, &class, &address, + &accsize) == 0 && vendor == CST_FFH_VENDOR_INTEL && + class == CST_FFH_INTEL_CL_MWAIT) { + /* Native C State Instruction use (mwait) */ + acpi_cpu_cx_cst_mwait(cx_ptr, address, accsize); ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "acpi_cpu%d: Got C%d - %d latency\n", - device_get_unit(sc->cpu_dev), cx_ptr->type, - cx_ptr->trans_lat)); + "acpi_cpu%d: Got C%d/mwait - %d latency\n", + device_get_unit(sc->cpu_dev), cx_ptr->type, cx_ptr->trans_lat)); cx_ptr++; sc->cpu_cx_count++; + } else +#endif + { + cx_ptr->res_rid = sc->cpu_cx_count; + acpi_PkgGas(sc->cpu_dev, pkg, 0, &cx_ptr->res_type, + &cx_ptr->res_rid, &cx_ptr->p_lvlx, RF_SHAREABLE); + if (cx_ptr->p_lvlx) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "acpi_cpu%d: Got C%d - %d latency\n", + device_get_unit(sc->cpu_dev), cx_ptr->type, + cx_ptr->trans_lat)); + cx_ptr++; + sc->cpu_cx_count++; + } } } AcpiOsFree(buf.Pointer); @@ -1043,7 +1122,14 @@ acpi_cpu_idle(sbintime_t sbt) */ if (cx_next->type == ACPI_STATE_C1) { cputicks = cpu_ticks(); - acpi_cpu_c1(); + if (cx_next->p_lvlx != NULL) { + /* C1 I/O then Halt */ + CPU_GET_REG(cx_next->p_lvlx, 1); + } + if (cx_next->do_mwait) + acpi_cpu_idle_mwait(cx_next->mwait_hint); + else + acpi_cpu_c1(); end_time = ((cpu_ticks() - cputicks) << 20) / cpu_tickrate(); if (curthread->td_critnest == 0) end_time = min(end_time, 500000 / hz); @@ -1055,7 +1141,7 @@ acpi_cpu_idle(sbintime_t sbt) * For C3, disable bus master arbitration and enable bus master wake * if BM control is available, otherwise flush the CPU cache. */ - if (cx_next->type == ACPI_STATE_C3) { + if (cx_next->type == ACPI_STATE_C3 || cx_next->mwait_bm_avoidance) { if ((cpu_quirks & CPU_QUIRK_NO_BM_CTRL) == 0) { AcpiWriteBitRegister(ACPI_BITREG_ARB_DISABLE, 1); AcpiWriteBitRegister(ACPI_BITREG_BUS_MASTER_RLD, 1); @@ -1076,7 +1162,10 @@ acpi_cpu_idle(sbintime_t sbt) start_time = 0; cputicks = cpu_ticks(); } - CPU_GET_REG(cx_next->p_lvlx, 1); + if (cx_next->do_mwait) + acpi_cpu_idle_mwait(cx_next->mwait_hint); + else + CPU_GET_REG(cx_next->p_lvlx, 1); /* * Read the end time twice. Since it may take an arbitrary time @@ -1092,8 +1181,8 @@ acpi_cpu_idle(sbintime_t sbt) end_time = ((cpu_ticks() - cputicks) << 20) / cpu_tickrate(); /* Enable bus master arbitration and disable bus master wakeup. */ - if (cx_next->type == ACPI_STATE_C3 && - (cpu_quirks & CPU_QUIRK_NO_BM_CTRL) == 0) { + if ((cx_next->type == ACPI_STATE_C3 || cx_next->mwait_bm_avoidance) && + (cpu_quirks & CPU_QUIRK_NO_BM_CTRL) == 0) { AcpiWriteBitRegister(ACPI_BITREG_ARB_DISABLE, 0); AcpiWriteBitRegister(ACPI_BITREG_BUS_MASTER_RLD, 0); } diff --git a/sys/dev/acpica/acpi_package.c b/sys/dev/acpica/acpi_package.c index e38fea5..c1070cb 100644 --- a/sys/dev/acpica/acpi_package.c +++ b/sys/dev/acpica/acpi_package.c @@ -120,6 +120,28 @@ acpi_PkgGas(device_t dev, ACPI_OBJECT *res, int idx, int *type, int *rid, return (acpi_bus_alloc_gas(dev, type, rid, &gas, dst, flags)); } +int +acpi_PkgFFH_IntelCpu(ACPI_OBJECT *res, int idx, int *vendor, int *class, + uint64_t *address, int *accsize) +{ + ACPI_GENERIC_ADDRESS gas; + ACPI_OBJECT *obj; + + obj = &res->Package.Elements[idx]; + if (obj == NULL || obj->Type != ACPI_TYPE_BUFFER || + obj->Buffer.Length < sizeof(ACPI_GENERIC_ADDRESS) + 3) + return (EINVAL); + + memcpy(&gas, obj->Buffer.Pointer + 3, sizeof(gas)); + if (gas.SpaceId != ACPI_ADR_SPACE_FIXED_HARDWARE) + return (ERESTART); + *vendor = gas.BitWidth; + *class = gas.BitOffset; + *address = gas.Address; + *accsize = gas.AccessWidth; + return (0); +} + ACPI_HANDLE acpi_GetReference(ACPI_HANDLE scope, ACPI_OBJECT *obj) { diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h index 2e2b96d..cbd4bd9 100644 --- a/sys/dev/acpica/acpivar.h +++ b/sys/dev/acpica/acpivar.h @@ -467,6 +467,8 @@ int acpi_PkgInt32(ACPI_OBJECT *res, int idx, uint32_t *dst); int acpi_PkgStr(ACPI_OBJECT *res, int idx, void *dst, size_t size); int acpi_PkgGas(device_t dev, ACPI_OBJECT *res, int idx, int *type, int *rid, struct resource **dst, u_int flags); +int acpi_PkgFFH_IntelCpu(ACPI_OBJECT *res, int idx, int *vendor, + int *class, uint64_t *address, int *accsize); ACPI_HANDLE acpi_GetReference(ACPI_HANDLE scope, ACPI_OBJECT *obj); /* diff --git a/sys/i386/acpica/acpi_machdep.c b/sys/i386/acpica/acpi_machdep.c index 049354b..4c79691 100644 --- a/sys/i386/acpica/acpi_machdep.c +++ b/sys/i386/acpica/acpi_machdep.c @@ -106,13 +106,6 @@ acpi_machdep_quirks(int *quirks) return (0); } -void -acpi_cpu_c1() -{ - - __asm __volatile("sti; hlt"); -} - /* * Support for mapping ACPI tables during early boot. This abuses the * crashdump map because the kernel cannot allocate KVA in diff --git a/sys/i386/include/md_var.h b/sys/i386/include/md_var.h index bffdd57..b5bd35e 100644 --- a/sys/i386/include/md_var.h +++ b/sys/i386/include/md_var.h @@ -97,6 +97,7 @@ struct dumperinfo; void *alloc_fpusave(int flags); void bcopyb(const void *from, void *to, size_t len); void busdma_swi(void); +bool cpu_mwait_usable(void); void cpu_probe_amdc1e(void); void cpu_setregs(void); void cpu_switch_load_gs(void) __asm(__STRING(cpu_switch_load_gs)); diff --git a/sys/x86/include/acpica_machdep.h b/sys/x86/include/acpica_machdep.h index 46080c0..136285c 100644 --- a/sys/x86/include/acpica_machdep.h +++ b/sys/x86/include/acpica_machdep.h @@ -74,6 +74,7 @@ enum intr_polarity; void acpi_SetDefaultIntrModel(int model); void acpi_cpu_c1(void); +void acpi_cpu_idle_mwait(uint32_t mwait_hint); void *acpi_map_table(vm_paddr_t pa, const char *sig); void acpi_unmap_table(void *table); vm_paddr_t acpi_find_table(const char *sig); diff --git a/sys/x86/x86/cpu_machdep.c b/sys/x86/x86/cpu_machdep.c index 846a123..d1d49f4 100644 --- a/sys/x86/x86/cpu_machdep.c +++ b/sys/x86/x86/cpu_machdep.c @@ -90,6 +90,7 @@ __FBSDID("$FreeBSD$"); #ifdef SMP #include #endif +#include #include #include @@ -130,6 +131,27 @@ cpu_flush_dcache(void *ptr, size_t len) /* Not applicable */ } +void +acpi_cpu_c1(void) +{ + + __asm __volatile("sti; hlt"); +} + +void +acpi_cpu_idle_mwait(uint32_t mwait_hint) +{ + int *state; + + state = (int *)PCPU_PTR(monitorbuf); + /* + * XXXKIB. Software coordination mode should be supported, + * but all Intel CPUs provide hardware coordination. + */ + cpu_monitor(state, 0, 0); + cpu_mwait(MWAIT_INTRBREAK, mwait_hint); +} + /* Get current clock frequency for the given cpu id. */ int cpu_est_clockrate(int cpu_id, uint64_t *rate) @@ -232,6 +254,15 @@ cpu_halt(void) #endif +bool +cpu_mwait_usable(void) +{ + + return ((cpu_feature2 & CPUID2_MON) != 0 && ((cpu_mon_mwait_flags & + (CPUID5_MON_MWAIT_EXT | CPUID5_MWAIT_INTRBREAK)) == + (CPUID5_MON_MWAIT_EXT | CPUID5_MWAIT_INTRBREAK))); +} + void (*cpu_idle_hook)(sbintime_t) = NULL; /* ACPI idle hook. */ static int cpu_ident_amdc1e = 0; /* AMD C1E supported. */ static int idle_mwait = 1; /* Use MONITOR/MWAIT for short idle. */ @@ -258,7 +289,7 @@ cpu_idle_acpi(sbintime_t sbt) else if (cpu_idle_hook) cpu_idle_hook(sbt); else - __asm __volatile("sti; hlt"); + acpi_cpu_c1(); *state = STATE_RUNNING; } #endif /* !PC98 */ @@ -292,7 +323,7 @@ cpu_idle_hlt(sbintime_t sbt) if (sched_runnable()) enable_intr(); else - __asm __volatile("sti; hlt"); + acpi_cpu_c1(); *state = STATE_RUNNING; } #endif