add MSR and X2APIC support
This commit is contained in:
parent
e0a112bc2a
commit
84945aee64
4 changed files with 112 additions and 52 deletions
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue