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);
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue