add MSR and X2APIC support

This commit is contained in:
Stefan Lankes 2012-07-22 13:16:17 +02:00
parent e0a112bc2a
commit 84945aee64
4 changed files with 112 additions and 52 deletions

View file

@ -191,7 +191,6 @@ int apic_init(void);
void apic_eoi(void);
uint32_t apic_cpu_id(void);
int apic_calibration(void);
int has_apic(void);
int apic_is_enabled(void);
int apic_enable_timer(void);
int apic_disable_timer(void);

View file

@ -41,13 +41,17 @@ extern "C" {
// feature list 1
#define CPU_FEATURE_FPU (1 << 0)
#define CPU_FEATURE_MSR (1 << 5)
#define CPU_FEATURE_APIC (1 << 9)
#define CPU_FEATURE_MMX (1 << 23)
#define CPU_FEATURE_FXSR (1 << 24)
#define CPU_FEATURE_SSE (1 << 25)
#define CPU_FEATURE_SSE2 (1 << 26)
// feature list 2
#define CPU_FEATURE_X2APIC (1 << 21)
#define CPU_FEATURE_AVX (1 << 28)
#define CPU_FEATURE_HYPERVISOR (1 << 31)
typedef struct {
uint32_t feature1, feature2;
@ -63,6 +67,16 @@ inline static uint32_t has_fpu(void)
return (cpu_info.feature1 & CPU_FEATURE_FPU);
}
inline static uint32_t has_msr(void)
{
return (cpu_info.feature1 & CPU_FEATURE_MSR);
}
inline static uint32_t has_apic(void)
{
return (cpu_info.feature1 & CPU_FEATURE_APIC);
}
inline static uint32_t has_fxsr(void)
{
return (cpu_info.feature1 & CPU_FEATURE_FXSR);
@ -78,11 +92,21 @@ inline static uint32_t has_sse2(void)
return (cpu_info.feature1 & CPU_FEATURE_SSE2);
}
inline static uint32_t has_x2apic(void)
{
return (cpu_info.feature2 & CPU_FEATURE_X2APIC);
}
inline static uint32_t has_avx(void)
{
return (cpu_info.feature2 & CPU_FEATURE_AVX);
}
inline static uint32_t on_hypervisor(void)
{
return (cpu_info.feature2 & CPU_FEATURE_HYPERVISOR);
}
/** @brief Read out time stamp counter
*
* The rdtsc asm command puts a 64 bit time stamp value
@ -171,6 +195,22 @@ inline static uint64_t rdmsr(uint32_t msr) {
return ((uint64_t)high << 32) | low;
}
/** @brief Write a value to a Machine-Specific Registers (MSR)
*
* The asm instruction wrmsr which stands for "Write to model specific register"
* is used here.
*
* @param msr The MSR identifier
* @param value Value, which will be store in the MSR
*/
inline static void wrmsr(uint32_t msr, uint64_t value)
{
uint32_t low = value & 0xFFFFFFFF;
uint32_t high = value >> 32;
asm volatile("wrmsr" :: "a"(low), "c"(msr), "d"(high));
}
/** @brief Read cr0 register
* @return cr0's value
*/
@ -335,11 +375,11 @@ static inline size_t lsb(size_t i)
inline static int system_init(void)
{
gdt_install();
cpu_detection();
apic_init();
#ifdef CONFIG_PCI
pci_init();
#endif
cpu_detection();
return 0;
}

View file

@ -78,12 +78,21 @@ spinlock_t bootlock = SPINLOCK_INIT;
// forward declaration
static int lapic_reset(void);
static inline uint32_t lapic_read(uint32_t addr)
static uint32_t lapic_read_default(uint32_t addr)
{
return *((volatile uint32_t*) (lapic+addr));
}
static inline void lapic_write(uint32_t addr, uint32_t value)
static uint32_t lapic_read_msr(uint32_t addr)
{
return rdmsr(0x800 + (addr >> 4));
}
typedef uint32_t (*lapic_read_func)(uint32_t addr);
static lapic_read_func lapic_read = lapic_read_default;
static void lapic_write_default(uint32_t addr, uint32_t value)
{
#ifdef CONFIG_X86_32
/*
@ -96,6 +105,15 @@ static inline void lapic_write(uint32_t addr, uint32_t value)
#endif
}
static void lapic_write_msr(uint32_t addr, uint32_t value)
{
wrmsr(0x800 + (addr >> 4), value);
}
typedef void (*lapic_write_func)(uint32_t addr, uint32_t value);
static lapic_write_func lapic_write = lapic_write_default;
static inline uint32_t ioapic_read(uint32_t reg)
{
ioapic->reg = reg;
@ -147,11 +165,6 @@ static inline uint32_t apic_lvt_entries(void)
return 0;
}
int has_apic(void)
{
return (lapic != 0);
}
int apic_is_enabled(void)
{
return (lapic && initialized);
@ -355,11 +368,14 @@ void smp_start(uint32_t id)
i = read_cr0();
i = i | (1 << 31);
write_cr0(i);
#endif
if (lapic && has_x2apic()) // enable x2APIC support
wrmsr(0x1B, lapic | 0xC00);
// reset APIC and set id
lapic_reset(); // sets also the timer interrupt
apic_set_cpu_id(id);
#endif
/*
* we turned on paging
@ -498,7 +514,7 @@ int map_apic(void)
{
uint32_t i;
if (!has_apic())
if (!lapic)
return -ENXIO;
#ifdef CONFIG_X86_32
@ -507,6 +523,7 @@ int map_apic(void)
return -ENXIO;
#else
if (lapic != (size_t)&kernel_start - 0x1000) {
kprintf("Upps! Kernel has to remap LAPIC!\n");
lapic = map_region(0 /*lapic*/, lapic, 1, MAP_KERNEL_SPACE|MAP_NO_CACHE);
if (BUILTIN_EXPECT(!lapic, 0))
return -ENXIO;
@ -540,13 +557,11 @@ int apic_calibration(void)
uint32_t flags;
#ifndef CONFIG_ROCKCREEK
uint64_t ticks, old;
uint32_t diff;
#else
uint64_t start, end, ticks;
uint32_t diff;
#endif
if (!has_apic())
if (!lapic)
return -ENXIO;
#ifndef CONFIG_ROCKCREEK
@ -566,8 +581,7 @@ int apic_calibration(void)
while(get_clock_tick() - ticks < 3)
HALT;
diff = 0xFFFFFFFFUL - lapic_read(APIC_CCR);
icr = diff / 3;
icr = (0xFFFFFFFFUL - lapic_read(APIC_CCR)) / 3;
flags = irq_nested_disable();
lapic_reset();
@ -597,8 +611,7 @@ int apic_calibration(void)
ticks = end > start ? end - start : start - end;
} while(ticks*TIMER_FREQ < 3*RC_REFCLOCKMHZ*1000000UL);
diff = 0xFFFFFFFFUL - lapic_read(APIC_CCR);
icr = diff / 3;
icr = (0xFFFFFFFFUL - lapic_read(APIC_CCR)) / 3;
lapic_reset();
@ -755,39 +768,26 @@ found_mp:
ncores = count;
check_lapic:
#ifdef CONFIG_X86_32
if (apic_config) {
if (apic_config)
lapic = apic_config->lapic;
} else {
uint32_t edx, dummy=0;
cpuid(0x1, &dummy, &dummy, &dummy, &edx);
if (edx & (1 << 9))
lapic = 0xFEE00000;
}
#else
if (apic_config) {
if (apic_config->lapic == 0xFEE00000) {
// On a x64 system, we already map the lapic below the kernel
lapic = (size_t)&kernel_start - 0x1000;
} else {
lapic = apic_config->lapic;
}
} else {
uint32_t edx, dummy=0;
cpuid(0x1, &dummy, &dummy, &dummy, &edx);
if (edx & (1 << 9)) {
// On a x64 system, we already map the lapic below the kernel
lapic = (size_t)&kernel_start - 0x1000;
}
}
#endif
else if (has_apic())
lapic = 0xFEE00000;
if (!lapic)
goto out;
if (has_x2apic()) {
kprintf("Enable X2APIC support!\n");
wrmsr(0x1B, lapic | 0xD00);
lapic_read = lapic_read_msr;
lapic_write = lapic_write_msr;
}
kprintf("Found APIC at 0x%x\n", lapic);
#ifdef CONFIG_X86_64
// On a x64 system, we already map the lapic below the kernel
lapic = (size_t)&kernel_start - 0x1000;
#endif
kprintf("Maximum LVT Entry: 0x%x\n", apic_lvt_entries());
kprintf("APIC Version: 0x%x\n", apic_version());

View file

@ -92,8 +92,12 @@ int cpu_detection(void)
{
uint32_t a, b;
size_t cr4;
uint8_t first_time = 0;
cpuid(1, &a, &b, &cpu_info.feature2, &cpu_info.feature1);
if (!cpu_info.feature1) {
first_time = 1;
cpuid(1, &a, &b, &cpu_info.feature2, &cpu_info.feature1);
}
cr4 = read_cr4();
if (has_fxsr())
@ -102,28 +106,45 @@ int cpu_detection(void)
cr4 |= 0x400; // set the OSXMMEXCPT bit
write_cr4(cr4);
if (has_sse())
if (first_time && has_sse())
wmb = sfence;
if (has_sse2()) {
if (first_time && has_sse2()) {
rmb = lfence;
mb = mfence;
}
if (has_avx())
if (first_time && has_avx())
kprintf("The CPU owns the Advanced Vector Extensions (AVX). However, MetalSVM doesn't support AVX!\n");
if (has_fpu()) {
kputs("Found and initialized FPU!\n");
if (first_time)
kputs("Found and initialized FPU!\n");
asm volatile ("fninit");
}
if (has_fxsr()) {
if (first_time && has_fxsr()) {
save_fpu_state = save_fpu_state_fxsr;
restore_fpu_state = restore_fpu_state_fxsr;
fpu_init = fpu_init_fxsr;
}
if (first_time && on_hypervisor()) {
uint32_t c, d;
char vendor_id[13];
kprintf("MetalSVM is running on a hypervisor!\n");
cpuid(0x40000000, &a, &b, &c, &d);
memcpy(vendor_id, &b, 4);
memcpy(vendor_id+4, &c, 4);
memcpy(vendor_id+8, &d, 4);
vendor_id[12] = '\0';
kprintf("Hypervisor Vendor Id: %s\n", vendor_id);
kprintf("Maximum input value for hypervisor CPUID info: 0x%x\n", a);
}
return 0;
}