mirror of
https://github.com/hermitcore/libhermit.git
synced 2025-03-09 00:00:03 +01:00
x86/apic: same calibration for single and multi kernel
Sample CPU timestamp counter for APIC calibration in both cases. This has proven to be more reliable than waiting for and counting timer interrupts.
This commit is contained in:
parent
8a14e6ffd0
commit
096384c921
2 changed files with 50 additions and 65 deletions
|
@ -425,6 +425,11 @@ inline static uint64_t rdtscp(uint32_t* cpu_id)
|
|||
return ((uint64_t)hi << 32ULL | (uint64_t)lo);
|
||||
}
|
||||
|
||||
inline static uint64_t get_rdtsc()
|
||||
{
|
||||
return has_rdtscp() ? rdtscp(NULL) : rdtsc();
|
||||
}
|
||||
|
||||
/** @brief Read MSR
|
||||
*
|
||||
* The asm instruction rdmsr which stands for "Read from model specific register"
|
||||
|
|
|
@ -76,7 +76,7 @@ static volatile ioapic_t* ioapic = NULL;
|
|||
static uint32_t icr = 0;
|
||||
static uint32_t ncores = 1;
|
||||
static uint8_t irq_redirect[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF};
|
||||
static uint8_t initialized = 0;
|
||||
static uint8_t apic_initialized = 0;
|
||||
static uint8_t online[MAX_APIC_CORES] = {[0 ... MAX_APIC_CORES-1] = 0};
|
||||
|
||||
spinlock_t bootlock = SPINLOCK_INIT;
|
||||
|
@ -141,7 +141,7 @@ static inline uint32_t ioapic_version(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline uint32_t ioapic_max_redirection_entry(void)
|
||||
static inline uint8_t ioapic_max_redirection_entry(void)
|
||||
{
|
||||
if (ioapic)
|
||||
return (ioapic_read(IOAPIC_REG_VER) >> 16) & 0xFF;
|
||||
|
@ -151,7 +151,7 @@ static inline uint32_t ioapic_max_redirection_entry(void)
|
|||
|
||||
int apic_is_enabled(void)
|
||||
{
|
||||
return (lapic && initialized);
|
||||
return (lapic && apic_initialized);
|
||||
}
|
||||
|
||||
extern uint32_t disable_x2apic;
|
||||
|
@ -522,87 +522,69 @@ int smp_init(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
// How many ticks are used to calibrate the APIC timer
|
||||
#define APIC_TIMER_CALIBRATION_TICKS (3)
|
||||
|
||||
/*
|
||||
* detects the timer frequency of the APIC and restart
|
||||
* detects the timer frequency of the APIC and restarts
|
||||
* the APIC timer with the correct period
|
||||
*/
|
||||
int apic_calibration(void)
|
||||
{
|
||||
uint32_t flags;
|
||||
uint64_t ticks, old;
|
||||
uint8_t flags;
|
||||
uint64_t cycles, old, diff;
|
||||
|
||||
if (BUILTIN_EXPECT(!lapic, 0))
|
||||
return -ENXIO;
|
||||
|
||||
if (!is_single_kernel()) {
|
||||
uint64_t diff, wait = (uint64_t)cpu_freq * 3000000ULL / (uint64_t)TIMER_FREQ;
|
||||
const uint64_t cpu_freq_hz = (uint64_t) get_cpu_frequency() * 1000000ULL;
|
||||
const uint64_t cycles_per_tick = cpu_freq_hz / (uint64_t) TIMER_FREQ;
|
||||
const uint64_t wait_cycles = cycles_per_tick * APIC_TIMER_CALIBRATION_TICKS;
|
||||
|
||||
flags = irq_nested_disable();
|
||||
lapic_write(APIC_DCR, 0xB); // set it to 1 clock increments
|
||||
lapic_write(APIC_LVT_T, 0x2007B); // connects the timer to 123 and enables it
|
||||
lapic_write(APIC_ICR, 0xFFFFFFFFUL);
|
||||
irq_nested_enable(flags);
|
||||
// disable interrupts to increase calibration accuracy
|
||||
flags = irq_nested_disable();
|
||||
|
||||
// set timer decrements to 1
|
||||
lapic_write(APIC_DCR, 0xB);
|
||||
// connect the timer to IRQ 123 and set one shot mode
|
||||
lapic_write(APIC_LVT_T, 0x7B);
|
||||
// start timer with max. counter value
|
||||
const uint32_t initial_counter = 0xFFFFFFFF;
|
||||
lapic_write(APIC_ICR, initial_counter);
|
||||
|
||||
rmb();
|
||||
old = get_rdtsc();
|
||||
|
||||
do {
|
||||
rmb();
|
||||
old = rdtsc();
|
||||
cycles = get_rdtsc();
|
||||
diff = cycles > old ? cycles - old : old - cycles;
|
||||
} while(diff < wait_cycles);
|
||||
|
||||
do {
|
||||
rmb();
|
||||
ticks = rdtsc();
|
||||
diff = ticks > old ? ticks - old : old - ticks;
|
||||
} while(diff < wait);
|
||||
// Calculate timer increments for desired tick frequency
|
||||
icr = (initial_counter - lapic_read(APIC_CCR)) / APIC_TIMER_CALIBRATION_TICKS;
|
||||
irq_nested_enable(flags);
|
||||
|
||||
icr = (0xFFFFFFFFUL - lapic_read(APIC_CCR)) / 3;
|
||||
kprintf("APIC calibration determined already an ICR of 0x%x\n", icr);
|
||||
lapic_reset();
|
||||
|
||||
flags = irq_nested_disable();
|
||||
lapic_reset();
|
||||
initialized = 1;
|
||||
irq_nested_enable(flags);
|
||||
|
||||
atomic_int32_inc(&cpu_online);
|
||||
|
||||
return 0;
|
||||
}
|
||||
kprintf("APIC calibration determined an ICR of 0x%x\n", icr);
|
||||
|
||||
apic_initialized = 1;
|
||||
atomic_int32_inc(&cpu_online);
|
||||
|
||||
old = get_clock_tick();
|
||||
if(is_single_kernel()) {
|
||||
// Now, HermitCore is able to use the APIC => Therefore, we disable the PIC
|
||||
outportb(0xA1, 0xFF);
|
||||
outportb(0x21, 0xFF);
|
||||
}
|
||||
|
||||
/* wait for the next time slice */
|
||||
while ((ticks = get_clock_tick()) - old == 0)
|
||||
PAUSE;
|
||||
|
||||
flags = irq_nested_disable();
|
||||
lapic_write(APIC_DCR, 0xB); // set it to 1 clock increments
|
||||
lapic_write(APIC_LVT_T, 0x2007B); // connects the timer to 123 and enables it
|
||||
lapic_write(APIC_ICR, 0xFFFFFFFFUL);
|
||||
irq_nested_enable(flags);
|
||||
|
||||
/* wait 3 time slices to determine a ICR */
|
||||
while (get_clock_tick() - ticks < 3)
|
||||
PAUSE;
|
||||
|
||||
icr = (0xFFFFFFFFUL - lapic_read(APIC_CCR)) / 3;
|
||||
|
||||
flags = irq_nested_disable();
|
||||
lapic_reset();
|
||||
irq_nested_enable(flags);
|
||||
|
||||
// Now, HermitCore is able to use the APIC => Therefore, we disable the PIC
|
||||
outportb(0xA1, 0xFF);
|
||||
outportb(0x21, 0xFF);
|
||||
|
||||
kprintf("APIC calibration determines an ICR of 0x%x\n", icr);
|
||||
|
||||
flags = irq_nested_disable();
|
||||
|
||||
// only the single-kernel maintain the IOAPIC
|
||||
if (ioapic) {
|
||||
uint32_t max_entry = ioapic_max_redirection_entry();
|
||||
// only the single-kernel maintains the IOAPIC
|
||||
if (ioapic && is_single_kernel()) {
|
||||
uint8_t max_entry = ioapic_max_redirection_entry();
|
||||
|
||||
// now lets turn everything else on
|
||||
for(uint32_t i=0; i<=max_entry; i++) {
|
||||
for(uint8_t i = 0; i <= max_entry; i++) {
|
||||
if (i != 2)
|
||||
ioapic_inton(i, apic_processors[boot_processor]->id);
|
||||
}
|
||||
|
@ -611,11 +593,9 @@ int apic_calibration(void)
|
|||
ioapic_intoff(2, apic_processors[boot_processor]->id);
|
||||
}
|
||||
|
||||
initialized = 1;
|
||||
#if MAX_CORES > 1
|
||||
smp_init();
|
||||
#endif
|
||||
irq_nested_enable(flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue