zx_status_t platform_set_oneshot_timer(zx_time_t deadline) { DEBUG_ASSERT(arch_ints_disabled()); if (deadline < 0) { deadline = 0; } deadline = discrete_time_roundup(deadline); DEBUG_ASSERT(deadline > 0); if (use_tsc_deadline) { // Check if the deadline would overflow the TSC. const uint64_t tsc_ticks_per_ns = tsc_ticks_per_ms / ZX_MSEC(1); if (UINT64_MAX / deadline < tsc_ticks_per_ns) { return ZX_ERR_INVALID_ARGS; } // We rounded up to the tick after above. const uint64_t tsc_deadline = u64_mul_u64_fp32_64(deadline, tsc_per_ns); LTRACEF("Scheduling oneshot timer: %" PRIu64 " deadline\n", tsc_deadline); apic_timer_set_tsc_deadline(tsc_deadline, false /* unmasked */); return ZX_OK; } const zx_time_t now = current_time(); if (now >= deadline) { // Deadline has already passed. We still need to schedule a timer so that // the interrupt fires. LTRACEF("Scheduling oneshot timer for min duration\n"); return apic_timer_set_oneshot(1, 1, false /* unmasked */); } const zx_duration_t interval = zx_time_sub_time(deadline, now); DEBUG_ASSERT(interval > 0); uint64_t apic_ticks_needed = u64_mul_u64_fp32_64(interval, apic_ticks_per_ns); if (apic_ticks_needed == 0) { apic_ticks_needed = 1; } // Find the shift needed for this timeout, since count is 32-bit. const uint highest_set_bit = log2_ulong_floor(apic_ticks_needed); uint8_t extra_shift = (highest_set_bit <= 31) ? 0 : static_cast<uint8_t>(highest_set_bit - 31); if (extra_shift > 8) { extra_shift = 8; } uint32_t divisor = apic_divisor << extra_shift; uint32_t count; // If the divisor is too large, we're at our maximum timeout. Saturate the // timer. It'll fire earlier than requested, but the scheduler will notice // and ask us to set the timer up again. if (divisor <= 128) { count = (uint32_t)(apic_ticks_needed >> extra_shift); DEBUG_ASSERT((apic_ticks_needed >> extra_shift) <= UINT32_MAX); } else {
static void spinlock_test(void) { spin_lock_saved_state_t state; spin_lock_t lock; spin_lock_init(&lock); // verify basic functionality (single core) printf("testing spinlock:\n"); ASSERT(!spin_lock_held(&lock)); ASSERT(!arch_ints_disabled()); spin_lock_irqsave(&lock, state); ASSERT(arch_ints_disabled()); ASSERT(spin_lock_held(&lock)); spin_unlock_irqrestore(&lock, state); ASSERT(!spin_lock_held(&lock)); ASSERT(!arch_ints_disabled()); printf("seems to work\n"); #define COUNT (1024*1024) uint32_t c = arch_cycle_count(); for (uint i = 0; i < COUNT; i++) { spin_lock(&lock); spin_unlock(&lock); } c = arch_cycle_count() - c; printf("%u cycles to acquire/release lock %u times (%u cycles per)\n", c, COUNT, c / COUNT); c = arch_cycle_count(); for (uint i = 0; i < COUNT; i++) { spin_lock_irqsave(&lock, state); spin_unlock_irqrestore(&lock, state); } c = arch_cycle_count() - c; printf("%u cycles to acquire/release lock w/irqsave %u times (%u cycles per)\n", c, COUNT, c / COUNT); #undef COUNT }
static void calibrate_tsc(bool has_pvclock) { ASSERT(arch_ints_disabled()); const uint64_t tsc_freq = has_pvclock ? pvclock_get_tsc_freq() : x86_lookup_tsc_freq(); if (tsc_freq != 0) { tsc_ticks_per_ms = tsc_freq / 1000; printf("TSC frequency: %" PRIu64 " ticks/ms\n", tsc_ticks_per_ms); } else { printf("Could not find TSC frequency: Calibrating TSC with %s\n", clock_name[calibration_clock]); uint32_t duration_ms[2] = {2, 4}; uint64_t best_time[2] = { calibrate_tsc_count(static_cast<uint16_t>(duration_ms[0])), calibrate_tsc_count(static_cast<uint16_t>(duration_ms[1]))}; while (best_time[0] >= best_time[1] && 2 * duration_ms[1] < MAX_TIMER_INTERVAL) { duration_ms[0] = duration_ms[1]; duration_ms[1] *= 2; best_time[0] = best_time[1]; best_time[1] = calibrate_tsc_count(static_cast<uint16_t>(duration_ms[1])); } ASSERT(best_time[0] < best_time[1]); tsc_ticks_per_ms = (best_time[1] - best_time[0]) / (duration_ms[1] - duration_ms[0]); printf("TSC calibrated: %" PRIu64 " ticks/ms\n", tsc_ticks_per_ms); } ASSERT(tsc_ticks_per_ms <= UINT32_MAX); fp_32_64_div_32_32(&ns_per_tsc, 1000 * 1000, static_cast<uint32_t>(tsc_ticks_per_ms)); fp_32_64_div_32_32(&tsc_per_ns, static_cast<uint32_t>(tsc_ticks_per_ms), 1000 * 1000); // Add 1ns to conservatively deal with rounding ns_per_tsc_rounded_up = u32_mul_u64_fp32_64(1, ns_per_tsc) + 1; LTRACEF("ns_per_tsc: %08x.%08x%08x\n", ns_per_tsc.l0, ns_per_tsc.l32, ns_per_tsc.l64); }
void virtio_gpu_gfx_flush(uint starty, uint endy) { event_signal(&the_gdev->flush_event, !arch_ints_disabled()); }
static void calibrate_apic_timer(void) { ASSERT(arch_ints_disabled()); const uint64_t apic_freq = x86_lookup_core_crystal_freq(); if (apic_freq != 0) { ASSERT(apic_freq / 1000 <= UINT32_MAX); apic_ticks_per_ms = static_cast<uint32_t>(apic_freq / 1000); apic_divisor = 1; fp_32_64_div_32_32(&apic_ticks_per_ns, apic_ticks_per_ms, 1000 * 1000); printf("APIC frequency: %" PRIu32 " ticks/ms\n", apic_ticks_per_ms); return; } printf("Could not find APIC frequency: Calibrating APIC with %s\n", clock_name[calibration_clock]); apic_divisor = 1; outer: while (apic_divisor != 0) { uint32_t best_time[2] = {UINT32_MAX, UINT32_MAX}; const uint16_t duration_ms[2] = {2, 4}; for (int trial = 0; trial < 2; ++trial) { for (int tries = 0; tries < 3; ++tries) { switch (calibration_clock) { case CLOCK_HPET: hpet_calibration_cycle_preamble(); break; case CLOCK_PIT: pit_calibration_cycle_preamble(duration_ms[trial]); break; default: PANIC_UNIMPLEMENTED; } // Setup APIC timer to count down with interrupt masked zx_status_t status = apic_timer_set_oneshot( UINT32_MAX, apic_divisor, true); ASSERT(status == ZX_OK); switch (calibration_clock) { case CLOCK_HPET: hpet_calibration_cycle(duration_ms[trial]); break; case CLOCK_PIT: pit_calibration_cycle(duration_ms[trial]); break; default: PANIC_UNIMPLEMENTED; } uint32_t apic_ticks = UINT32_MAX - apic_timer_current_count(); if (apic_ticks < best_time[trial]) { best_time[trial] = apic_ticks; } LTRACEF("Calibration trial %d found %u ticks/ms\n", tries, apic_ticks); switch (calibration_clock) { case CLOCK_HPET: hpet_calibration_cycle_cleanup(); break; case CLOCK_PIT: pit_calibration_cycle_cleanup(); break; default: PANIC_UNIMPLEMENTED; } } // If the APIC ran out of time every time, try again with a higher // divisor if (best_time[trial] == UINT32_MAX) { apic_divisor = static_cast<uint8_t>(apic_divisor * 2); goto outer; } } apic_ticks_per_ms = (best_time[1] - best_time[0]) / (duration_ms[1] - duration_ms[0]); fp_32_64_div_32_32(&apic_ticks_per_ns, apic_ticks_per_ms, 1000 * 1000); break; } ASSERT(apic_divisor != 0); printf("APIC timer calibrated: %" PRIu32 " ticks/ms, divisor %d\n", apic_ticks_per_ms, apic_divisor); }