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); void apic_eoi(void);
uint32_t apic_cpu_id(void); uint32_t apic_cpu_id(void);
int apic_calibration(void); int apic_calibration(void);
int has_apic(void);
int apic_is_enabled(void); int apic_is_enabled(void);
int apic_enable_timer(void); int apic_enable_timer(void);
int apic_disable_timer(void); int apic_disable_timer(void);

View file

@ -41,13 +41,17 @@ extern "C" {
// feature list 1 // feature list 1
#define CPU_FEATURE_FPU (1 << 0) #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_MMX (1 << 23)
#define CPU_FEATURE_FXSR (1 << 24) #define CPU_FEATURE_FXSR (1 << 24)
#define CPU_FEATURE_SSE (1 << 25) #define CPU_FEATURE_SSE (1 << 25)
#define CPU_FEATURE_SSE2 (1 << 26) #define CPU_FEATURE_SSE2 (1 << 26)
// feature list 2 // feature list 2
#define CPU_FEATURE_X2APIC (1 << 21)
#define CPU_FEATURE_AVX (1 << 28) #define CPU_FEATURE_AVX (1 << 28)
#define CPU_FEATURE_HYPERVISOR (1 << 31)
typedef struct { typedef struct {
uint32_t feature1, feature2; uint32_t feature1, feature2;
@ -63,6 +67,16 @@ inline static uint32_t has_fpu(void)
return (cpu_info.feature1 & CPU_FEATURE_FPU); 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) inline static uint32_t has_fxsr(void)
{ {
return (cpu_info.feature1 & CPU_FEATURE_FXSR); 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); 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) inline static uint32_t has_avx(void)
{ {
return (cpu_info.feature2 & CPU_FEATURE_AVX); 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 /** @brief Read out time stamp counter
* *
* The rdtsc asm command puts a 64 bit time stamp value * 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; 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 /** @brief Read cr0 register
* @return cr0's value * @return cr0's value
*/ */
@ -335,11 +375,11 @@ static inline size_t lsb(size_t i)
inline static int system_init(void) inline static int system_init(void)
{ {
gdt_install(); gdt_install();
cpu_detection();
apic_init(); apic_init();
#ifdef CONFIG_PCI #ifdef CONFIG_PCI
pci_init(); pci_init();
#endif #endif
cpu_detection();
return 0; return 0;
} }

View file

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

View file

@ -92,8 +92,12 @@ int cpu_detection(void)
{ {
uint32_t a, b; uint32_t a, b;
size_t cr4; 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(); cr4 = read_cr4();
if (has_fxsr()) if (has_fxsr())
@ -102,28 +106,45 @@ int cpu_detection(void)
cr4 |= 0x400; // set the OSXMMEXCPT bit cr4 |= 0x400; // set the OSXMMEXCPT bit
write_cr4(cr4); write_cr4(cr4);
if (has_sse()) if (first_time && has_sse())
wmb = sfence; wmb = sfence;
if (has_sse2()) { if (first_time && has_sse2()) {
rmb = lfence; rmb = lfence;
mb = mfence; 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"); kprintf("The CPU owns the Advanced Vector Extensions (AVX). However, MetalSVM doesn't support AVX!\n");
if (has_fpu()) { if (has_fpu()) {
kputs("Found and initialized FPU!\n"); if (first_time)
kputs("Found and initialized FPU!\n");
asm volatile ("fninit"); asm volatile ("fninit");
} }
if (has_fxsr()) { if (first_time && has_fxsr()) {
save_fpu_state = save_fpu_state_fxsr; save_fpu_state = save_fpu_state_fxsr;
restore_fpu_state = restore_fpu_state_fxsr; restore_fpu_state = restore_fpu_state_fxsr;
fpu_init = fpu_init_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; return 0;
} }