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.
This commit is contained in:
Jérôme Duval 2013-08-26 19:17:28 +02:00
parent e9eb899aa4
commit 787773400c
7 changed files with 312 additions and 35 deletions

View File

@ -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);

View File

@ -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

View File

@ -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"

View File

@ -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)

View File

@ -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;

View File

@ -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)

View File

@ -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;
}