hrev45991 adds 1 changeset to branch 'master' old head: e9eb899aa4c8baace0ed8f4eb2e2899c585bc2b5 new head: 787773400ce9ee4ce71d9255e1be8fac66584615 overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=7877734+%5Ee9eb899 ---------------------------------------------------------------------------- 7877734: Added x2APIC support. * Mostly useful for virtualization at the moment. Works in QEmu. * Can be enabled by safemode settings/menu. * Please note that x2APIC normally requires use of VT-d interrupt remapping feature on real hardware, which we don't support yet. [ Jérôme Duval <jerome.duval@xxxxxxxxx> ] ---------------------------------------------------------------------------- Revision: hrev45991 Commit: 787773400ce9ee4ce71d9255e1be8fac66584615 URL: http://cgit.haiku-os.org/haiku/commit/?id=7877734 Author: Jérôme Duval <jerome.duval@xxxxxxxxx> Date: Mon Aug 26 17:17:28 2013 UTC ---------------------------------------------------------------------------- 7 files changed, 312 insertions(+), 35 deletions(-) headers/private/kernel/arch/x86/apic.h | 19 ++ headers/private/kernel/arch/x86/arch_cpu.h | 27 +++ headers/private/system/safemode_defs.h | 1 + src/system/boot/platform/bios_ia32/smp.cpp | 16 ++ src/system/kernel/arch/x86/apic.cpp | 242 +++++++++++++++++++-- src/system/kernel/arch/x86/arch_smp.cpp | 14 +- src/system/kernel/arch/x86/timers/x86_apic.cpp | 28 +-- ---------------------------------------------------------------------------- diff --git a/headers/private/kernel/arch/x86/apic.h b/headers/private/kernel/arch/x86/apic.h index e0e18f7..9c3d922 100644 --- a/headers/private/kernel/arch/x86/apic.h +++ b/headers/private/kernel/arch/x86/apic.h @@ -113,10 +113,29 @@ bool apic_available(); uint32 apic_read(uint32 offset); void apic_write(uint32 offset, uint32 data); uint32 apic_local_id(); +uint32 apic_local_version(); +uint32 apic_task_priority(); +void apic_set_task_priority(uint32 config); void apic_end_of_interrupt(); void apic_disable_local_ints(); +uint32 apic_spurious_intr_vector(); +void apic_set_spurious_intr_vector(uint32 config); +uint32 apic_intr_command_1(); +void apic_set_intr_command_1(uint32 config); +uint32 apic_intr_command_2(); +void apic_set_intr_command_2(uint32 config); + +uint32 apic_lvt_timer(); +void apic_set_lvt_timer(uint32 config); +uint32 apic_lvt_error(); +void apic_set_lvt_error(uint32 config); +uint32 apic_lvt_initial_timer_count(); +void apic_set_lvt_initial_timer_count(uint32 config); +uint32 apic_lvt_timer_divide_config(); +void apic_set_lvt_timer_divide_config(uint32 config); + status_t apic_init(kernel_args *args); status_t apic_per_cpu_init(kernel_args *args, int32 cpu); diff --git a/headers/private/kernel/arch/x86/arch_cpu.h b/headers/private/kernel/arch/x86/arch_cpu.h index bc9f4c8..4809528 100644 --- a/headers/private/kernel/arch/x86/arch_cpu.h +++ b/headers/private/kernel/arch/x86/arch_cpu.h @@ -39,11 +39,38 @@ #define IA32_MSR_EFER 0xc0000080 + +// MSR APIC BASE bits +#define IA32_MSR_APIC_BASE_BSP 0x00000100 +#define IA32_MSR_APIC_BASE_X2APIC 0x00000400 +#define IA32_MSR_APIC_BASE_ENABLED 0x00000800 +#define IA32_MSR_APIC_BASE_ADDRESS 0xfffff000 + // MSR EFER bits // reference #define IA32_MSR_EFER_SYSCALL (1 << 0) #define IA32_MSR_EFER_NX (1 << 11) +// X2APIC MSRs. +#define IA32_MSR_APIC_ID 0x00000802 +#define IA32_MSR_APIC_VERSION 0x00000803 +#define IA32_MSR_APIC_TASK_PRIORITY 0x00000808 +#define IA32_MSR_APIC_PROCESSOR_PRIORITY 0x0000080a +#define IA32_MSR_APIC_EOI 0x0000080b +#define IA32_MSR_APIC_LOGICAL_DEST 0x0000080d +#define IA32_MSR_APIC_SPURIOUS_INTR_VECTOR 0x0000080f +#define IA32_MSR_APIC_ERROR_STATUS 0x00000828 +#define IA32_MSR_APIC_INTR_COMMAND 0x00000830 +#define IA32_MSR_APIC_LVT_TIMER 0x00000832 +#define IA32_MSR_APIC_LVT_THERMAL_SENSOR 0x00000833 +#define IA32_MSR_APIC_LVT_PERFMON_COUNTERS 0x00000834 +#define IA32_MSR_APIC_LVT_LINT0 0x00000835 +#define IA32_MSR_APIC_LVT_LINT1 0x00000836 +#define IA32_MSR_APIC_LVT_ERROR 0x00000837 +#define IA32_MSR_APIC_INITIAL_TIMER_COUNT 0x00000838 +#define IA32_MSR_APIC_CURRENT_TIMER_COUNT 0x00000839 +#define IA32_MSR_APIC_TIMER_DIVIDE_CONFIG 0x0000083e + // x86_64 MSRs. #define IA32_MSR_STAR 0xc0000081 #define IA32_MSR_LSTAR 0xc0000082 diff --git a/headers/private/system/safemode_defs.h b/headers/private/system/safemode_defs.h index 33f1dd0..ecbeeb0 100644 --- a/headers/private/system/safemode_defs.h +++ b/headers/private/system/safemode_defs.h @@ -11,6 +11,7 @@ #define B_SAFEMODE_DISABLE_IOAPIC "disable_ioapic" #define B_SAFEMODE_DISABLE_ACPI "disable_acpi" #define B_SAFEMODE_DISABLE_APIC "disable_apic" +#define B_SAFEMODE_ENABLE_X2APIC "enable_x2apic" #define B_SAFEMODE_DISABLE_APM "disable_apm" #define B_SAFEMODE_DISABLE_SMP "disable_smp" #define B_SAFEMODE_DISABLE_HYPER_THREADING "disable_hyperthreading" diff --git a/src/system/boot/platform/bios_ia32/smp.cpp b/src/system/boot/platform/bios_ia32/smp.cpp index e3abb4f..138e1cd 100644 --- a/src/system/boot/platform/bios_ia32/smp.cpp +++ b/src/system/boot/platform/bios_ia32/smp.cpp @@ -579,6 +579,22 @@ smp_add_safemode_menus(Menu *menu) item->SetType(MENU_ITEM_MARKABLE); item->SetData(B_SAFEMODE_DISABLE_APIC); item->SetHelpText("Disables using the local APIC, also disables SMP."); + + cpuid_info info; + if (get_current_cpuid(&info, 1) == B_OK + && (info.regs.ecx & IA32_FEATURE_EXT_X2APIC) != 0) { +#if 0 + menu->AddItem(item = new(nothrow) MenuItem("Disable X2APIC")); + item->SetType(MENU_ITEM_MARKABLE); + item->SetData(B_SAFEMODE_DISABLE_X2APIC); + item->SetHelpText("Disables using X2APIC."); +#else + menu->AddItem(item = new(nothrow) MenuItem("Enable X2APIC")); + item->SetType(MENU_ITEM_MARKABLE); + item->SetData(B_SAFEMODE_ENABLE_X2APIC); + item->SetHelpText("Enables using X2APIC."); +#endif + } } if (gKernelArgs.num_cpus < 2) diff --git a/src/system/kernel/arch/x86/apic.cpp b/src/system/kernel/arch/x86/apic.cpp index 70f662c..d8a653a 100644 --- a/src/system/kernel/arch/x86/apic.cpp +++ b/src/system/kernel/arch/x86/apic.cpp @@ -13,18 +13,20 @@ #include <arch/x86/msi.h> #include <debug.h> +#include <safemode.h> #include <vm/vm.h> #include "timers/apic_timer.h" static void *sLocalAPIC = NULL; +static bool sX2APIC = false; bool apic_available() { - return sLocalAPIC != NULL; + return sLocalAPIC != NULL || sX2APIC; } @@ -45,14 +47,50 @@ apic_write(uint32 offset, uint32 data) uint32 apic_local_id() { - return (apic_read(APIC_ID) & 0xffffffff) >> 24; + if (sX2APIC) + return x86_read_msr(IA32_MSR_APIC_ID); + else + return (apic_read(APIC_ID) & 0xffffffff) >> 24; +} + + +uint32 +apic_version() +{ + if (sX2APIC) + return x86_read_msr(IA32_MSR_APIC_VERSION); + else + return apic_read(APIC_VERSION); +} + + +uint32 +apic_task_priority() +{ + if (sX2APIC) + return x86_read_msr(IA32_MSR_APIC_TASK_PRIORITY); + else + return apic_read(APIC_TASK_PRIORITY); +} + + +void +apic_set_task_priority(uint32 config) +{ + if (sX2APIC) + x86_write_msr(IA32_MSR_APIC_TASK_PRIORITY, config); + else + apic_write(APIC_TASK_PRIORITY, config); } void apic_end_of_interrupt() { - apic_write(APIC_EOI, 0); + if (sX2APIC) + x86_write_msr(IA32_MSR_APIC_EOI, 0); + else + apic_write(APIC_EOI, 0); } @@ -60,8 +98,156 @@ void apic_disable_local_ints() { // just clear them out completely - apic_write(APIC_LVT_LINT0, APIC_LVT_MASKED); - apic_write(APIC_LVT_LINT1, APIC_LVT_MASKED); + if (sX2APIC) { + x86_write_msr(IA32_MSR_APIC_LVT_LINT0, APIC_LVT_MASKED); + x86_write_msr(IA32_MSR_APIC_LVT_LINT1, APIC_LVT_MASKED); + } else { + apic_write(APIC_LVT_LINT0, APIC_LVT_MASKED); + apic_write(APIC_LVT_LINT1, APIC_LVT_MASKED); + } +} + + +uint32 +apic_spurious_intr_vector() +{ + if (sX2APIC) + return x86_read_msr(IA32_MSR_APIC_SPURIOUS_INTR_VECTOR); + else + return apic_read(APIC_SPURIOUS_INTR_VECTOR); +} + + +void +apic_set_spurious_intr_vector(uint32 config) +{ + if (sX2APIC) + x86_write_msr(IA32_MSR_APIC_SPURIOUS_INTR_VECTOR, config); + else + apic_write(APIC_SPURIOUS_INTR_VECTOR, config); +} + + +uint32 +apic_intr_command_1() +{ + if (sX2APIC) + return x86_read_msr(IA32_MSR_APIC_INTR_COMMAND) & 0xffffffff; + else + return apic_read(APIC_INTR_COMMAND_1); +} + + +void +apic_set_intr_command_1(uint32 config) +{ + if (sX2APIC) { + x86_write_msr(IA32_MSR_APIC_INTR_COMMAND, + (x86_read_msr(IA32_MSR_APIC_INTR_COMMAND) & 0xffffffff00000000LL) | config); + } else + apic_write(APIC_INTR_COMMAND_1, config); +} + + +uint32 +apic_intr_command_2() +{ + if (sX2APIC) + return x86_read_msr(IA32_MSR_APIC_INTR_COMMAND) >> 32; + else + return apic_read(APIC_INTR_COMMAND_2); +} + + +void +apic_set_intr_command_2(uint32 config) +{ + if (sX2APIC) { + x86_write_msr(IA32_MSR_APIC_INTR_COMMAND, + (x86_read_msr(IA32_MSR_APIC_INTR_COMMAND) & 0xffffffff) + | ((uint64)config << 32)); + } else + apic_write(APIC_INTR_COMMAND_2, config); +} + + +uint32 +apic_lvt_timer() +{ + if (sX2APIC) + return x86_read_msr(IA32_MSR_APIC_LVT_TIMER); + else + return apic_read(APIC_LVT_TIMER); +} + + +void +apic_set_lvt_timer(uint32 config) +{ + if (sX2APIC) + x86_write_msr(IA32_MSR_APIC_LVT_TIMER, config); + else + apic_write(APIC_LVT_TIMER, config); +} + + +uint32 +apic_lvt_error() +{ + if (sX2APIC) + return x86_read_msr(IA32_MSR_APIC_LVT_ERROR); + else + return apic_read(APIC_LVT_ERROR); +} + + +void +apic_set_lvt_error(uint32 config) +{ + if (sX2APIC) + x86_write_msr(IA32_MSR_APIC_LVT_ERROR, config); + else + apic_write(APIC_LVT_ERROR, config); +} + + +uint32 +apic_lvt_initial_timer_count() +{ + if (sX2APIC) + return x86_read_msr(IA32_MSR_APIC_INITIAL_TIMER_COUNT); + else + return apic_read(APIC_INITIAL_TIMER_COUNT); +} + + +void +apic_set_lvt_initial_timer_count(uint32 config) +{ + if (sX2APIC) + x86_write_msr(IA32_MSR_APIC_INITIAL_TIMER_COUNT, config); + else + apic_write(APIC_INITIAL_TIMER_COUNT, config); +} + + +uint32 +apic_lvt_timer_divide_config() +{ + if (sX2APIC) + return x86_read_msr(IA32_MSR_APIC_TIMER_DIVIDE_CONFIG); + else + return apic_read(APIC_TIMER_DIVIDE_CONFIG); +} + + +void +apic_set_lvt_timer_divide_config(uint32 config) +{ + if (sX2APIC) + x86_write_msr(IA32_MSR_APIC_TIMER_DIVIDE_CONFIG, config); + else + apic_write(APIC_TIMER_DIVIDE_CONFIG, config); } @@ -71,6 +257,35 @@ apic_init(kernel_args *args) if (args->arch_args.apic == NULL) return B_NO_INIT; + if (x86_check_feature(IA32_FEATURE_EXT_X2APIC, FEATURE_EXT)) { + dprintf("found x2apic\n"); +#if 0 + if (!get_safemode_boolean(B_SAFEMODE_DISABLE_X2APIC, false)) { + uint64 apic_base = x86_read_msr(IA32_MSR_APIC_BASE); + if ((apic_base & IA32_MSR_APIC_BASE_X2APIC) == 0) { + x86_write_msr(IA32_MSR_APIC_BASE, apic_base + | IA32_MSR_APIC_BASE_X2APIC); + } + sX2APIC = true; + return B_OK; + } + + dprintf("x2apic disabled per safemode setting\n"); +#else + if (get_safemode_boolean(B_SAFEMODE_ENABLE_X2APIC, false)) { + uint64 apic_base = x86_read_msr(IA32_MSR_APIC_BASE); + if ((apic_base & IA32_MSR_APIC_BASE_X2APIC) == 0) { + x86_write_msr(IA32_MSR_APIC_BASE, apic_base + | IA32_MSR_APIC_BASE_X2APIC); + } + sX2APIC = true; + + dprintf("x2apic enabled per safemode setting\n"); + return B_OK; + } +#endif + } + sLocalAPIC = args->arch_args.apic; dprintf("mapping local apic at %p\n", sLocalAPIC); if (vm_map_physical_memory(B_SYSTEM_TEAM, "local apic", &sLocalAPIC, @@ -89,13 +304,12 @@ status_t apic_per_cpu_init(kernel_args *args, int32 cpu) { dprintf("setting up apic for CPU %" B_PRId32 ": apic id %" B_PRIu32 ", " - "version %" B_PRIu32 "\n", cpu, apic_local_id(), - apic_read(APIC_VERSION)); + "version %" B_PRIu32 "\n", cpu, apic_local_id(), apic_version()); /* set spurious interrupt vector to 0xff */ - uint32 config = apic_read(APIC_SPURIOUS_INTR_VECTOR) & 0xffffff00; + uint32 config = apic_spurious_intr_vector() & 0xffffff00; config |= APIC_ENABLE | 0xff; - apic_write(APIC_SPURIOUS_INTR_VECTOR, config); + apic_set_spurious_intr_vector(config); // don't touch the LINT0/1 configuration in virtual wire mode // ToDo: implement support for other modes... @@ -130,14 +344,14 @@ apic_per_cpu_init(kernel_args *args, int32 cpu) apic_timer_per_cpu_init(args, cpu); /* setup error vector to 0xfe */ - config = (apic_read(APIC_LVT_ERROR) & 0xffffff00) | 0xfe; - apic_write(APIC_LVT_ERROR, config); + config = (apic_lvt_error() & 0xffffff00) | 0xfe; + apic_set_lvt_error(config); /* accept all interrupts */ - config = apic_read(APIC_TASK_PRIORITY) & 0xffffff00; - apic_write(APIC_TASK_PRIORITY, config); + config = apic_task_priority() & 0xffffff00; + apic_set_task_priority(config); - config = apic_read(APIC_SPURIOUS_INTR_VECTOR); + config = apic_spurious_intr_vector(); apic_end_of_interrupt(); return B_OK; diff --git a/src/system/kernel/arch/x86/arch_smp.cpp b/src/system/kernel/arch/x86/arch_smp.cpp index b1aedd8..8c2bc75 100644 --- a/src/system/kernel/arch/x86/arch_smp.cpp +++ b/src/system/kernel/arch/x86/arch_smp.cpp @@ -121,8 +121,8 @@ arch_smp_send_broadcast_ici(void) uint32 config; cpu_status state = disable_interrupts(); - config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK; - apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED + config = apic_intr_command_1() & APIC_INTR_COMMAND_1_MASK; + apic_set_intr_command_1(config | 0xfd | APIC_DELIVERY_MODE_FIXED | APIC_INTR_COMMAND_1_ASSERT | APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL | APIC_INTR_COMMAND_1_DEST_ALL_BUT_SELF); @@ -140,18 +140,18 @@ arch_smp_send_ici(int32 target_cpu) state = disable_interrupts(); - config = apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK; - apic_write(APIC_INTR_COMMAND_2, config | sCPUAPICIds[target_cpu] << 24); + config = apic_intr_command_2() & APIC_INTR_COMMAND_2_MASK; + apic_set_intr_command_2(config | sCPUAPICIds[target_cpu] << 24); - config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK; - apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED + config = apic_intr_command_1() & APIC_INTR_COMMAND_1_MASK; + apic_set_intr_command_1(config | 0xfd | APIC_DELIVERY_MODE_FIXED | APIC_INTR_COMMAND_1_ASSERT | APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL | APIC_INTR_COMMAND_1_DEST_FIELD); timeout = 100000000; // wait for message to be sent - while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0 && --timeout != 0) + while ((apic_intr_command_1() & APIC_DELIVERY_STATUS) != 0 && --timeout != 0) asm volatile ("pause;"); if (timeout == 0) diff --git a/src/system/kernel/arch/x86/timers/x86_apic.cpp b/src/system/kernel/arch/x86/timers/x86_apic.cpp index 7c9b3f0..3569e75 100644 --- a/src/system/kernel/arch/x86/timers/x86_apic.cpp +++ b/src/system/kernel/arch/x86/timers/x86_apic.cpp @@ -63,18 +63,18 @@ apic_timer_set_hardware_timer(bigtime_t relativeTimeout) cpu_status state = disable_interrupts(); - uint32 config = apic_read(APIC_LVT_TIMER) | APIC_LVT_MASKED; // mask the timer - apic_write(APIC_LVT_TIMER, config); + uint32 config = apic_lvt_timer() | APIC_LVT_MASKED; // mask the timer + apic_set_lvt_timer(config); - apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the timer + apic_set_lvt_initial_timer_count(0); // zero out the timer - config = apic_read(APIC_LVT_TIMER) & ~APIC_LVT_MASKED; // unmask the timer - apic_write(APIC_LVT_TIMER, config); + config = apic_lvt_timer() & ~APIC_LVT_MASKED; // unmask the timer + apic_set_lvt_timer(config); //TRACE_TIMER(("arch_smp_set_apic_timer: config 0x%lx, timeout %Ld, tics/sec %lu, tics %lu\n", // config, relativeTimeout, sApicTicsPerSec, ticks)); - apic_write(APIC_INITIAL_TIMER_COUNT, ticks); // start it up + apic_set_lvt_initial_timer_count(ticks); // start it up restore_interrupts(state); @@ -87,11 +87,11 @@ apic_timer_clear_hardware_timer() { cpu_status state = disable_interrupts(); - uint32 config = apic_read(APIC_LVT_TIMER) | APIC_LVT_MASKED; + uint32 config = apic_lvt_timer() | APIC_LVT_MASKED; // mask the timer - apic_write(APIC_LVT_TIMER, config); + apic_set_lvt_timer(config); - apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the timer + apic_set_lvt_initial_timer_count(0); // zero out the timer restore_interrupts(state); return B_OK; @@ -118,14 +118,14 @@ status_t apic_timer_per_cpu_init(struct kernel_args *args, int32 cpu) { /* setup timer */ - uint32 config = apic_read(APIC_LVT_TIMER) & APIC_LVT_TIMER_MASK; + uint32 config = apic_lvt_timer() & APIC_LVT_TIMER_MASK; config |= 0xfb | APIC_LVT_MASKED; // vector 0xfb, timer masked - apic_write(APIC_LVT_TIMER, config); + apic_set_lvt_timer(config); - apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the clock + apic_set_lvt_initial_timer_count(0); // zero out the clock - config = apic_read(APIC_TIMER_DIVIDE_CONFIG) & 0xfffffff0; + config = apic_lvt_timer_divide_config() & 0xfffffff0; config |= APIC_TIMER_DIVIDE_CONFIG_1; // clock division by 1 - apic_write(APIC_TIMER_DIVIDE_CONFIG, config); + apic_set_lvt_timer_divide_config(config); return B_OK; }