blob: b81e620d803b98fff0b5ace6b6afba51fdcf7efa [file]
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2009 Corey Tabaka
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
use super::hpet::{
hpet_disable, hpet_enable, hpet_get_value, hpet_is_present, hpet_set_value, hpet_ticks_per_ms,
hpet_wait_ms,
};
use super::interrupts::{register_permanent_int_handler, unmask_interrupt};
use crate::arch_rs::x86::feature::{
X86_FEATURE_INVAR_TSC, X86_FEATURE_TSC_DEADLINE, X86ModelInfo, X86VendorList, x86_feature_test,
x86_get_model, x86_get_vendor, x86_hypervisor_has_pv_clock, x86_lookup_core_crystal_freq,
x86_lookup_tsc_freq,
};
use crate::arch_rs::x86::ioapic::apic_io_isa_to_global;
use crate::arch_rs::x86::lapic::{
apic_timer_current_count, apic_timer_set_oneshot, apic_timer_set_tsc_deadline, apic_timer_stop,
apic_timer_tsc_deadline_init,
};
use crate::arch_rs::x86::pv::{
pv_clock_get_tsc_freq, pv_clock_init, pv_clock_is_stable, pv_clock_shutdown,
};
use crate::arch_rs::x86::x86::{inp, outp};
use crate::arch_rs::{curr_cpu_num, ints_disabled};
use crate::counters::define_kcounter;
use crate::dev_interrupt::InterruptHandler;
use crate::platform_rs::timer::{
GET_TICKS_SYNC_FLAG_AFTER_PREVIOUS_LOADS, GET_TICKS_SYNC_FLAG_AFTER_PREVIOUS_STORES,
GET_TICKS_SYNC_FLAG_BEFORE_SUBSEQUENT_LOADS, GET_TICKS_SYNC_FLAG_BEFORE_SUBSEQUENT_STORES,
GET_TICKS_SYNC_FLAG_NONE, current_mono_time, timer_get_mono_ticks_offset,
timer_set_initial_ticks, timer_set_ticks_to_time_ratio, timer_tick, zx_msec,
};
use affine::{Exact, Ratio, Round, Saturate, Transform};
use boot_options::{BootOptions, WallclockType};
use core::arch::asm;
use core::arch::x86_64::{__rdtscp, _rdtsc};
use core::ffi::c_void;
use core::ptr::NonNull;
use core::sync::atomic::{AtomicU64, Ordering};
use debug::ltracef;
use fixed_point::{Fp3264, fp_32_64_div_32_32};
use kprint::kprint;
use libarch::x86::{device_memory_barrier, serialize_instructions};
use zx_status::Status;
use zx_types::{zx_duration_t, zx_instant_mono_t, zx_instant_mono_ticks_t, zx_ticks_t};
define_kcounter!(PLATFORM_TIMER_SET_COUNTER, "platform.timer.set", Sum);
define_kcounter!(PLATFORM_TIMER_CANCEL_COUNTER, "platform.timer.cancel", Sum);
// Current timer scheme:
// The HPET is used to calibrate the local APIC timers and the TSC. If the
// HPET is not present, we will fallback to calibrating using the PIT.
//
// For wall-time, we use the following mechanisms, in order of highest
// preference to least:
// 1) TSC: If the CPU advertises an invariant TSC, then we will use the TSC for
// tracking wall time in a tickless manner.
// 2) HPET: If there is an HPET present, we will use its count to track wall
// time in a tickless manner.
// 3) PIT: We will use periodic interrupts to update wall time.
//
// The local APICs are responsible for handling timer callbacks
// sent from the scheduler.
/// Platform clock source selection.
#[repr(u32)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
enum ClockSource {
/// Used before `WALL_CLOCK` is selected. `current_mono_ticks()` returns 0.
#[default]
Unselected = 0,
Tsc = 1,
Pit = 2,
Hpet = 3,
}
/// Number of [`ClockSource`] variants.
const CLOCK_COUNT: usize = 4;
/// Human-readable names indexed by [`ClockSource`].
const CLOCK_NAME: [&str; CLOCK_COUNT] = ["UNSELECTED", "TSC", "PIT", "HPET"];
zr::static_assert!(CLOCK_NAME.len() == CLOCK_COUNT);
/// PIT time accounting info: microseconds per PIT tick in 32.64 fixed point.
static mut US_PER_PIT: Fp3264 = Fp3264 { l0: 0, l32: 0, l64: 0 };
/// Monotonically increasing PIT interrupt tick counter.
static PIT_TICKS: AtomicU64 = AtomicU64::new(0);
/// Configured PIT frequency divisor.
static mut PIT_DIVISOR: u16 = 0;
/// Whether or not we have an Invariant TSC (controls whether we use the PIT or
/// not after initialization). The Invariant TSC is rate-invariant under P-, C-,
/// and T-state transitions.
static mut INVARIANT_TSC: bool = false;
/// Whether or not we have a Constant TSC (controls whether we bother calibrating
/// the TSC). Constant TSC predates the Invariant TSC. The Constant TSC is
/// rate-invariant under P-state transitions.
static mut CONSTANT_TSC: bool = false;
/// The ratio between the chosen reference timer's ticks and the APIC's ticks.
/// This is set after clock selection is complete in `pc_init_timer`.
static mut REFERENCE_TIMER_TICKS_TO_APIC_TICKS: Ratio = Ratio::const_default();
/// Selected wall clock source.
static mut WALL_CLOCK: ClockSource = ClockSource::Unselected;
/// Clock source used for calibrating the APIC timer and TSC.
static mut CALIBRATION_CLOCK: ClockSource = ClockSource::Unselected;
/// APIC timer calibration values: whether TSC deadline mode is active.
static mut USE_TSC_DEADLINE: bool = false;
/// Calibrated APIC timer ticks per millisecond.
static mut APIC_TICKS_PER_MS: u32 = 0;
/// Calibrated APIC timer ticks per nanosecond in 32.64 fixed point.
static mut APIC_TICKS_PER_NS: Fp3264 = Fp3264 { l0: 0, l32: 0, l64: 0 };
/// Calibrated APIC timer divisor.
static mut APIC_DIVISOR: u8 = 0;
/// TSC timer calibration values: TSC ticks per millisecond.
static mut TSC_TICKS_PER_MS: u64 = 0;
/// Nanoseconds per TSC tick in 32.64 fixed point.
static mut NS_PER_TSC: Fp3264 = Fp3264 { l0: 0, l32: 0, l64: 0 };
/// Conversion ratio from RDTSC ticks to clock monotonic nanoseconds.
static mut RDTSC_TICKS_TO_CLOCK_MONOTONIC: Ratio = Ratio::const_default();
/// HPET calibration values: nanoseconds per HPET tick in 32.64 fixed point.
static mut NS_PER_HPET: Fp3264 = Fp3264 { l0: 0, l32: 0, l64: 0 };
/// Conversion ratio from HPET ticks to clock monotonic nanoseconds.
static mut HPET_TICKS_TO_CLOCK_MONOTONIC: Ratio = Ratio::const_default();
/// An affine transformation from times sampled from the `EarlyTicks` timeline to
/// the chosen ticks timeline. By default, this transformation is set up as:
///
/// `f(t) = (((t - 0) * 0) / 1) + 0;`
///
/// meaning that it will map all early ticks value `t` to 0, and the inverse
/// transformation will be undefined. This is consistent with simply
/// reporting 0 for normalized `EarlyTicks` values if we cannot (or do not know how
/// to) convert from one timeline to the other.
static mut EARLY_TICKS_TO_TICKS: Transform = Transform::new(0, 0, Ratio::new(0, 1));
/// Nominal PIT oscillator frequency in Hz.
const INTERNAL_FREQ: u32 = 1_193_182;
/// 3x nominal PIT oscillator frequency in Hz.
const INTERNAL_FREQ_3X: u32 = 3_579_546;
/// Nominal PIT oscillator ticks per millisecond.
const INTERNAL_FREQ_TICKS_PER_MS: u32 = INTERNAL_FREQ / 1000;
/// Maximum amount of time that can be programmed on the timer to schedule the next
/// interrupt, in milliseconds.
const MAX_TIMER_INTERVAL: i64 = zx_msec(55);
/// Local trace verbosity setting.
const LOCAL_TRACE: u32 = 0;
/// Legacy ISA IRQ number for the Programmable Interval Timer (PIT).
const ISA_IRQ_PIT: u8 = 0;
#[inline]
fn current_ticks_rdtsc() -> zx_ticks_t {
// SAFETY: `_rdtsc` is always safe to execute in ring 0 on x86_64.
unsafe { _rdtsc() as zx_ticks_t }
}
#[inline]
fn current_ticks_rdtscp() -> zx_ticks_t {
let mut unused: u32 = 0;
// SAFETY: `__rdtscp` is safe to execute when RDTSCP is supported; `unused` is a valid pointer.
unsafe { __rdtscp(&mut unused) as zx_ticks_t }
}
fn current_ticks_hpet() -> zx_ticks_t {
hpet_get_value() as zx_ticks_t
}
fn current_ticks_pit() -> zx_ticks_t {
// Relaxed ordering is sufficient because PIT_TICKS is a standalone monotonic counter and
// callers do not synchronize other memory accesses through it.
PIT_TICKS.load(Ordering::Relaxed) as zx_ticks_t
}
/// Reads a platform-specific ticks counter synchronized according to `FLAGS`.
#[inline]
pub fn platform_current_raw_ticks_synchronized<const FLAGS: u8>() -> zx_ticks_t {
// SAFETY: `WALL_CLOCK` is initialized once during single-threaded boot in `pc_init_timer`
// and is read-only thereafter.
let wall_clock = unsafe { WALL_CLOCK };
// Directly call the ticks functions to avoid the cost of a virtual (indirect) call.
if wall_clock == ClockSource::Tsc {
// See "Intel® 64 and IA-32 Architectures Software Developer’s Manual Vol.
// 2B Section 4.3", specifically the entries for RDTSC and RDTSCP for a
// description of the serialization properties of the instructions which
// access the TSC.
//
// If all stores must be completed and "globally visible" before the TSC is
// sampled, docs say to put an MFENCE in front of the TSC access.
if (FLAGS & GET_TICKS_SYNC_FLAG_AFTER_PREVIOUS_STORES) != GET_TICKS_SYNC_FLAG_NONE {
device_memory_barrier();
}
// If all loads must be complete and "globally visible" (meaning that the
// value to load has been determined) before the TSC is sampled, docs say to
// either execute `LFENCE ; RDTSC` or just `RDTSCP`.
let ret: zx_ticks_t =
if (FLAGS & GET_TICKS_SYNC_FLAG_AFTER_PREVIOUS_LOADS) != GET_TICKS_SYNC_FLAG_NONE {
current_ticks_rdtscp()
} else {
current_ticks_rdtsc()
};
// Finally, if we need the TSC sampling to have finished before any
// subsequent loads/stores start, docs say that we should put an LFENCE
// immediately after the RDTSC/RDTSCP.
if (FLAGS
& (GET_TICKS_SYNC_FLAG_BEFORE_SUBSEQUENT_LOADS
| GET_TICKS_SYNC_FLAG_BEFORE_SUBSEQUENT_STORES))
!= GET_TICKS_SYNC_FLAG_NONE
{
// SAFETY: `lfence` serializes instruction execution and loads with no side effects
// beyond memory ordering. `nomem` and `readonly` are omitted to preserve the compiler
// memory barrier matching `__asm__ __volatile__("lfence" ::: "memory")`.
unsafe {
asm!("lfence", options(nostack, preserves_flags));
}
}
ret
} else {
core::hint::cold_path();
match wall_clock {
ClockSource::Unselected => 0,
ClockSource::Pit => {
// In theory, we should not need anything special to synchronize the
// PIT. Right now, the PIT is just a global counter incremented by an
// IRQ handler when the interrupt timer fires once per msec, and Intel's
// memory model is strongly ordered, implying that no special
// synchronization should be required.
current_ticks_pit()
}
ClockSource::Hpet => {
// TODO(johngro): Research and apply any barriers required to
// synchronize observations of the HPET with the instruction pipeline.
// Right now, we almost never use the HPET as our reference, which
// somewhat lowers the priority of this issue.
current_ticks_hpet()
}
ClockSource::Tsc => unimplemented!(),
}
}
}
/// Reads the current raw ticks counter without additional synchronization barriers.
#[inline]
pub fn platform_current_raw_ticks() -> zx_ticks_t {
platform_current_raw_ticks_synchronized::<{ GET_TICKS_SYNC_FLAG_NONE }>()
}
/// Explicit instantiation of all of the forms of synchronized tick access.
macro_rules! expand_platform_current_raw_ticks_synchronized {
($($name:ident = $flags:expr;)*) => {$(
#[doc = concat!(
"FFI entry point for `platform_current_raw_ticks_synchronized` with flags ",
stringify!($flags),
"."
)]
#[unsafe(no_mangle)]
pub extern "C" fn $name() -> zx_ticks_t {
platform_current_raw_ticks_synchronized::<{ $flags }>()
}
)*};
}
expand_platform_current_raw_ticks_synchronized! {
rust_platform_current_raw_ticks_synchronized_0 = 0;
rust_platform_current_raw_ticks_synchronized_1 = 1;
rust_platform_current_raw_ticks_synchronized_2 = 2;
rust_platform_current_raw_ticks_synchronized_3 = 3;
rust_platform_current_raw_ticks_synchronized_4 = 4;
rust_platform_current_raw_ticks_synchronized_5 = 5;
rust_platform_current_raw_ticks_synchronized_6 = 6;
rust_platform_current_raw_ticks_synchronized_7 = 7;
rust_platform_current_raw_ticks_synchronized_8 = 8;
rust_platform_current_raw_ticks_synchronized_9 = 9;
rust_platform_current_raw_ticks_synchronized_10 = 10;
rust_platform_current_raw_ticks_synchronized_11 = 11;
rust_platform_current_raw_ticks_synchronized_12 = 12;
rust_platform_current_raw_ticks_synchronized_13 = 13;
rust_platform_current_raw_ticks_synchronized_14 = 14;
rust_platform_current_raw_ticks_synchronized_15 = 15;
}
/// Converts a raw TSC duration in ticks to nanoseconds.
pub fn convert_raw_tsc_duration_to_nanoseconds(duration: i64) -> zx_duration_t {
// SAFETY: `RDTSC_TICKS_TO_CLOCK_MONOTONIC` is initialized once during single-threaded boot in
// `calibrate_tsc` and is read-only thereafter.
let ratio = unsafe { RDTSC_TICKS_TO_CLOCK_MONOTONIC };
ratio.scale::<{ Round::DOWN }>(duration)
}
/// Converts a raw TSC timestamp to the clock monotonic timeline in nanoseconds.
pub fn convert_raw_tsc_timestamp_to_clock_monotonic(ts: i64) -> zx_instant_mono_t {
// SAFETY: `WALL_CLOCK` and `RDTSC_TICKS_TO_CLOCK_MONOTONIC` are initialized once during
// single-threaded boot in `pc_init_timer` and are read-only thereafter.
let (wall_clock, rdtsc_ticks_to_clock_monotonic) =
unsafe { (WALL_CLOCK, RDTSC_TICKS_TO_CLOCK_MONOTONIC) };
if wall_clock == ClockSource::Tsc {
// If TSC is being used as our clock monotonic reference, then conversion is
// simple. We just need to convert from the raw TSC timestamps to a ticks
// timestamp by adding the offset, then scale by the ticks -> mono ratio.
// As the offset is only updated early during boot when we're running on a
// single core with interrupts disabled, we don't need to worry about thread
// synchronization so memory_order_relaxed is sufficient.
let abs_ticks: i64 = ts.wrapping_add(timer_get_mono_ticks_offset());
rdtsc_ticks_to_clock_monotonic.scale::<{ Round::DOWN }>(abs_ticks)
} else {
core::hint::cold_path();
// If we are using something other than TSC as our monotonic reference, then
// things are slightly more tricky. We need to figure out how far in the
// future this TSC timestamp is (in nanoseconds), and then add that delta to
// the current time to establish the new deadline.
//
// Bracket our observation of current time with two observations of ticks,
// and use the average of those two values to create the ticks half of the
// correspondence pair.
let before_tsc = current_ticks_rdtsc() as u64;
let now_mono: zx_instant_mono_t = current_mono_time().0;
let after_tsc = current_ticks_rdtsc() as u64;
let now_tsc: u64 = (before_tsc >> 1) + (after_tsc >> 1) + (before_tsc & after_tsc & 1);
let time_till_tsc_timestamp: i64 = ts.saturating_sub(now_tsc as i64);
now_mono.saturating_add(
rdtsc_ticks_to_clock_monotonic.scale::<{ Round::DOWN }>(time_till_tsc_timestamp),
)
}
}
/// i8253/i8254 programmable interval timer control register port.
const I8253_CONTROL_REG: u16 = 0x43;
/// i8253/i8254 programmable interval timer data register port.
const I8253_DATA_REG: u16 = 0x40;
/// The PIT timer will keep track of wall time if we aren't using the TSC.
extern "C" fn pit_timer_tick(_cookie: *mut c_void) {
// Relaxed ordering is sufficient because pit_timer_tick is the sole writer (serialized by
// the PIT IRQ) and readers only observe the counter value itself.
PIT_TICKS.store(PIT_TICKS.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
}
/// The APIC timers will call this when they fire.
///
/// # Safety
/// Must only be called from the APIC timer interrupt handler with interrupts disabled.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn platform_handle_apic_timer_tick() {
// SAFETY: Caller guarantees we are in the APIC timer interrupt handler with interrupts
// disabled.
unsafe {
timer_tick();
}
}
fn set_pit_frequency(frequency: u32) {
let mut count: u32;
let mut remainder: u32;
// figure out the correct pit_divisor for the desired frequency
if frequency <= 18 {
count = 0xffff;
} else if frequency >= INTERNAL_FREQ {
count = 1;
} else {
count = INTERNAL_FREQ_3X / frequency;
remainder = INTERNAL_FREQ_3X % frequency;
if remainder >= INTERNAL_FREQ_3X / 2 {
count += 1;
}
count /= 3;
remainder = count % 3;
if remainder >= 1 {
count += 1;
}
}
let pit_divisor = (count & 0xffff) as u16;
// SAFETY: Single-threaded boot initialization in `pc_init_timer`.
unsafe {
PIT_DIVISOR = pit_divisor;
// funky math that i don't feel like explaining. essentially 32.32 fixed
// point representation of the configured timer delta.
fp_32_64_div_32_32(
NonNull::new_unchecked(&raw mut US_PER_PIT),
(1000u32 * 1000 * 3).wrapping_mul(count),
INTERNAL_FREQ_3X,
);
}
// dprintf(DEBUG, "set_pit_frequency: pit_divisor=%04x\n", pit_divisor);
// setup the Programmable Interval Timer
// timer 0, mode 2, binary counter, LSB followed by MSB
// SAFETY: Port I/O to I8253 PIT control and data registers during boot initialization.
unsafe {
outp(I8253_CONTROL_REG, 0x34);
outp(I8253_DATA_REG, pit_divisor as u8); // LSB
outp(I8253_DATA_REG, (pit_divisor >> 8) as u8); // MSB
}
}
#[inline]
fn pit_calibration_cycle_preamble(ms: u16) {
// Make the PIT run for
let init_pic_count = (INTERNAL_FREQ_TICKS_PER_MS * u32::from(ms)) as u16;
// Program PIT in the interrupt on terminal count configuration,
// this makes it count down and set the output high when it hits 0.
// SAFETY: Port I/O to I8253 PIT registers during single-threaded timer calibration.
unsafe {
outp(I8253_CONTROL_REG, 0x30);
outp(I8253_DATA_REG, init_pic_count as u8); // LSB
}
}
#[inline]
fn pit_calibration_cycle(ms: u16) {
// Make the PIT run for ms millis, see comments in the preamble
let init_pic_count = (INTERNAL_FREQ_TICKS_PER_MS * u32::from(ms)) as u16;
// SAFETY: Port I/O to I8253 PIT registers during single-threaded timer calibration.
unsafe {
outp(I8253_DATA_REG, (init_pic_count >> 8) as u8); // MSB
}
let mut status: u8;
loop {
// Send a read-back command that latches the status of ch0
// SAFETY: Port I/O to I8253 PIT registers during single-threaded timer calibration.
unsafe {
outp(I8253_CONTROL_REG, 0xe2);
status = inp(I8253_DATA_REG);
}
// Wait for bit 7 (output) to go high and for bit 6 (null count) to go low
if (status & 0xc0) == 0x80 {
break;
}
}
}
#[inline]
fn pit_calibration_cycle_cleanup() {
// Stop the PIT by starting a mode change but not writing a counter
// SAFETY: Port I/O to I8253 PIT control register during single-threaded timer calibration.
unsafe {
outp(I8253_CONTROL_REG, 0x38);
}
}
#[inline]
fn hpet_calibration_cycle_preamble() {
hpet_enable();
}
#[inline]
fn hpet_calibration_cycle(ms: u16) {
hpet_wait_ms(ms);
}
#[inline]
fn hpet_calibration_cycle_cleanup() {
hpet_disable();
}
fn calibrate_apic_timer() {
assert!(ints_disabled());
let apic_freq: u64 = x86_lookup_core_crystal_freq();
if apic_freq != 0 {
assert!(apic_freq / 1000 <= u64::from(u32::MAX));
let apic_ticks_per_ms = (apic_freq / 1000) as u32;
// SAFETY: Single-threaded boot calibration in `pc_init_timer`.
unsafe {
APIC_TICKS_PER_MS = apic_ticks_per_ms;
APIC_DIVISOR = 1;
fp_32_64_div_32_32(
NonNull::new_unchecked(&raw mut APIC_TICKS_PER_NS),
apic_ticks_per_ms,
1000 * 1000,
);
}
kprint!("APIC frequency: {} ticks/ms\n", apic_ticks_per_ms);
return;
}
// SAFETY: `CALIBRATION_CLOCK` is set in `pc_init_timer` before calling `calibrate_apic_timer`.
let calibration_clock = unsafe { CALIBRATION_CLOCK };
kprint!(
"Could not find APIC frequency: Calibrating APIC with {:s}\n",
CLOCK_NAME[calibration_clock as usize]
);
let mut apic_divisor: u8 = 1;
let mut apic_ticks_per_ms: u32 = 0;
'outer: while apic_divisor != 0 {
let mut best_time: [u32; 2] = [u32::MAX, u32::MAX];
const DURATION_MS: [u16; 2] = [2, 4];
for trial in 0..2 {
for tries in 0..3 {
match calibration_clock {
ClockSource::Hpet => hpet_calibration_cycle_preamble(),
ClockSource::Pit => pit_calibration_cycle_preamble(DURATION_MS[trial]),
_ => unimplemented!(),
}
// Setup APIC timer to count down with interrupt masked
let status = apic_timer_set_oneshot(u32::MAX, apic_divisor, true);
assert!(status.is_ok());
match calibration_clock {
ClockSource::Hpet => hpet_calibration_cycle(DURATION_MS[trial]),
ClockSource::Pit => pit_calibration_cycle(DURATION_MS[trial]),
_ => unimplemented!(),
}
let apic_ticks: u32 = u32::MAX - apic_timer_current_count();
if apic_ticks < best_time[trial] {
best_time[trial] = apic_ticks;
}
ltracef!("Calibration trial {} found {} ticks/ms\n", tries, apic_ticks);
match calibration_clock {
ClockSource::Hpet => hpet_calibration_cycle_cleanup(),
ClockSource::Pit => pit_calibration_cycle_cleanup(),
_ => unimplemented!(),
}
}
// If the APIC ran out of time every time, try again with a higher
// divisor
if best_time[trial] == u32::MAX {
apic_divisor = apic_divisor.wrapping_mul(2);
continue 'outer;
}
}
apic_ticks_per_ms =
(best_time[1] - best_time[0]) / u32::from(DURATION_MS[1] - DURATION_MS[0]);
// SAFETY: Single-threaded boot calibration in `pc_init_timer`.
unsafe {
fp_32_64_div_32_32(
NonNull::new_unchecked(&raw mut APIC_TICKS_PER_NS),
apic_ticks_per_ms,
1000 * 1000,
);
}
break;
}
assert!(apic_divisor != 0);
// SAFETY: Single-threaded boot calibration in `pc_init_timer`.
unsafe {
APIC_DIVISOR = apic_divisor;
APIC_TICKS_PER_MS = apic_ticks_per_ms;
}
kprint!("APIC timer calibrated: {} ticks/ms, divisor {}\n", apic_ticks_per_ms, apic_divisor);
}
fn calibrate_tsc_count(duration_ms: u16) -> u64 {
let mut best_time: zx_ticks_t = zx_ticks_t::MAX;
// SAFETY: `CALIBRATION_CLOCK` is set in `pc_init_timer` before calling `calibrate_tsc_count`.
let calibration_clock = unsafe { CALIBRATION_CLOCK };
for tries in 0..3 {
match calibration_clock {
ClockSource::Hpet => hpet_calibration_cycle_preamble(),
ClockSource::Pit => pit_calibration_cycle_preamble(duration_ms),
_ => unimplemented!(),
}
serialize_instructions();
// SAFETY: `_rdtsc` is always safe to execute in ring 0 on x86_64.
let start: u64 = unsafe { _rdtsc() };
serialize_instructions();
match calibration_clock {
ClockSource::Hpet => hpet_calibration_cycle(duration_ms),
ClockSource::Pit => pit_calibration_cycle(duration_ms),
_ => unimplemented!(),
}
serialize_instructions();
// SAFETY: `_rdtsc` is always safe to execute in ring 0 on x86_64.
let end: zx_ticks_t = unsafe { _rdtsc() as zx_ticks_t };
serialize_instructions();
let tsc_ticks: zx_ticks_t = end.wrapping_sub(start as zx_ticks_t);
if tsc_ticks < best_time {
best_time = tsc_ticks;
}
ltracef!("Calibration trial {} found {} ticks/ms\n", tries, tsc_ticks);
match calibration_clock {
ClockSource::Hpet => hpet_calibration_cycle_cleanup(),
ClockSource::Pit => pit_calibration_cycle_cleanup(),
_ => unimplemented!(),
}
}
best_time as u64
}
fn calibrate_tsc(has_pv_clock: bool) {
assert!(ints_disabled());
let tsc_freq: u64 = if has_pv_clock { pv_clock_get_tsc_freq() } else { x86_lookup_tsc_freq() };
let tsc_ticks_per_ms: u64;
if tsc_freq != 0 {
let mut n: u64 = 1_000_000_000;
let mut d: u64 = tsc_freq;
Ratio::reduce_u64(&mut n, &mut d);
// ASSERT that we can represent this as a 32 bit ratio. If we cannot,
// it means that tsc_freq is a number so large, and with so few prime
// factors of 2 and 5, that it cannot be reduced to fit into a 32 bit
// integer. This is pretty unreasonable, for now, just assert that it
// will not happen.
assert!(
(n <= u64::from(u32::MAX)) && (d <= u64::from(u32::MAX)),
"Clock monotonic ticks : RDTSC ticks ratio ({} : {}) too large to store in a 32 bit \
ratio!!",
n,
d
);
// SAFETY: Single-threaded boot calibration in `pc_init_timer`.
unsafe {
RDTSC_TICKS_TO_CLOCK_MONOTONIC = Ratio::new(n as u32, d as u32);
}
tsc_ticks_per_ms = tsc_freq / 1000;
// SAFETY: Single-threaded boot calibration in `pc_init_timer`.
unsafe {
TSC_TICKS_PER_MS = tsc_ticks_per_ms;
}
kprint!("TSC frequency: {} ticks/ms\n", tsc_ticks_per_ms);
} else {
// SAFETY: `CALIBRATION_CLOCK` is set in `pc_init_timer` before calling `calibrate_tsc`.
let calibration_clock = unsafe { CALIBRATION_CLOCK };
kprint!(
"Could not find TSC frequency: Calibrating TSC with {:s}\n",
CLOCK_NAME[calibration_clock as usize]
);
let mut duration_ms: [u32; 2] = [2, 4];
let mut best_time: [u64; 2] = [
calibrate_tsc_count(duration_ms[0] as u16),
calibrate_tsc_count(duration_ms[1] as u16),
];
while best_time[0] >= best_time[1] && i64::from(2 * duration_ms[1]) < MAX_TIMER_INTERVAL {
core::hint::cold_path();
duration_ms[0] = duration_ms[1];
duration_ms[1] *= 2;
best_time[0] = best_time[1];
best_time[1] = calibrate_tsc_count(duration_ms[1] as u16);
}
assert!(best_time[0] < best_time[1]);
let tsc_ticks_per_sec: u64 =
((best_time[1] - best_time[0]) * 1000) / u64::from(duration_ms[1] - duration_ms[0]);
assert!(
tsc_ticks_per_sec <= u64::from(u32::MAX),
"Estimated TSC ({}) is too high!\n",
tsc_ticks_per_sec
);
tsc_ticks_per_ms = tsc_ticks_per_sec / 1000;
// SAFETY: Single-threaded boot calibration in `pc_init_timer`.
unsafe {
TSC_TICKS_PER_MS = tsc_ticks_per_ms;
RDTSC_TICKS_TO_CLOCK_MONOTONIC = Ratio::new(1_000_000_000, tsc_ticks_per_sec as u32);
}
kprint!("TSC calibrated: {} ticks/ms\n", tsc_ticks_per_ms);
}
assert!(tsc_ticks_per_ms <= u64::from(u32::MAX));
// SAFETY: Single-threaded boot calibration in `pc_init_timer`.
let ns_per_tsc = unsafe {
fp_32_64_div_32_32(
NonNull::new_unchecked(&raw mut NS_PER_TSC),
1000 * 1000,
tsc_ticks_per_ms as u32,
);
NS_PER_TSC
};
ltracef!("ns_per_tsc: {:08x}.{:08x}{:08x}\n", ns_per_tsc.l0, ns_per_tsc.l32, ns_per_tsc.l64);
}
fn pc_init_timer(_level: init::LkInitLevel) {
let cpu_model: &X86ModelInfo = x86_get_model();
// Declares the desired PIT frequency to be 1000, which gives us ~1ms granularity.
// This may not be used if we chose to use a different platform reference timer.
const DESIRED_PIT_FREQUENCY: u32 = 1000;
let mut constant_tsc = false;
if x86_get_vendor() == X86VendorList::X86_VENDOR_INTEL {
// This condition taken from Intel 3B 17.15 (Time-Stamp Counter). This
// is the negation of the non-Constant TSC section, since the Constant
// TSC section is incomplete (the behavior is architectural going
// forward, and modern CPUs are not on the list).
constant_tsc = !((cpu_model.family == 0x6 && cpu_model.model == 0x9)
|| (cpu_model.family == 0x6 && cpu_model.model == 0xd)
|| (cpu_model.family == 0xf && cpu_model.model < 0x3));
}
let mut invariant_tsc = x86_feature_test(X86_FEATURE_INVAR_TSC);
let mut has_pv_clock = x86_hypervisor_has_pv_clock();
if has_pv_clock {
let status = pv_clock_init();
if status.is_ok() {
invariant_tsc = pv_clock_is_stable();
} else {
has_pv_clock = false;
}
}
// SAFETY: Single-threaded boot initialization in `pc_init_timer`.
unsafe {
CONSTANT_TSC = constant_tsc;
INVARIANT_TSC = invariant_tsc;
}
let has_hpet = hpet_is_present();
if has_hpet {
let hpet_ms_rate: u64 = hpet_ticks_per_ms();
assert!(hpet_ms_rate <= u64::from(u32::MAX));
kprint!("HPET frequency: {} ticks/ms\n", hpet_ms_rate);
// SAFETY: Single-threaded boot initialization in `pc_init_timer`.
unsafe {
CALIBRATION_CLOCK = ClockSource::Hpet;
fp_32_64_div_32_32(
NonNull::new_unchecked(&raw mut NS_PER_HPET),
1000 * 1000,
hpet_ms_rate as u32,
);
}
} else {
// SAFETY: Single-threaded boot initialization in `pc_init_timer`.
unsafe {
CALIBRATION_CLOCK = ClockSource::Pit;
}
}
let force_wallclock = BootOptions::get().x86_wallclock != WallclockType::AutoDetect;
let use_invariant_tsc = invariant_tsc
&& (!force_wallclock || BootOptions::get().x86_wallclock == WallclockType::Tsc);
let use_tsc_deadline = use_invariant_tsc && x86_feature_test(X86_FEATURE_TSC_DEADLINE);
// SAFETY: Single-threaded boot initialization in `pc_init_timer`.
unsafe {
USE_TSC_DEADLINE = use_tsc_deadline;
}
if use_tsc_deadline {
apic_timer_tsc_deadline_init();
} else {
calibrate_apic_timer();
}
let wall_clock: ClockSource;
if use_invariant_tsc {
calibrate_tsc(has_pv_clock);
// Program PIT in the software strobe configuration, but do not load
// the count. This will pause the PIT.
// SAFETY: Port I/O to I8253 PIT control register during boot initialization.
unsafe {
outp(I8253_CONTROL_REG, 0x38);
}
// Set up our wall clock to rdtsc, and stash the initial
// transformation from ticks to clock monotonic.
//
// We cannot (or at least, really should not) reset the TSC to zero, so
// instead we use the time of clock selection ("now" according to the TSC)
// to define the zero point on our ticks timeline moving forward.
// SAFETY: Single-threaded boot initialization on the boot CPU before secondary CPUs start.
unsafe {
timer_set_ticks_to_time_ratio(RDTSC_TICKS_TO_CLOCK_MONOTONIC);
timer_set_initial_ticks(current_ticks_rdtsc() as u64);
}
// A note about this wrapping negation. There is a technical risk of
// signed overflow if -mono_ticks_offset is a value too large to fit
// into a signed 64 bit integer (i.e. >= 2^63).
//
// This _should_ never happen, however. This offset is the two's complement
// of what the TSC read when we decided that the ticks timeline should be
// zero. For -mono_ticks_offset to be >= 2^63, the TSC counter
// value itself would have needed to be >= 2^63 in the line above where it
// was sampled. Assuming that the TSC started to count from 0 at cold power
// on time, and assuming that the TSC was running extremely quickly (say,
// 5GHz), the system would have needed to be powered on for at least ~58.45
// years before we hit this mark (and this assumes that the TSC is not reset
// during a warm reboot, or that no warm reboots take place over almost 60
// years of uptime). So, for now, we perform the wrapping negation,
// assuming that nothing bad will happen.
wall_clock = ClockSource::Tsc;
// SAFETY: Single-threaded boot initialization in `pc_init_timer`.
unsafe {
EARLY_TICKS_TO_TICKS =
Transform::new(timer_get_mono_ticks_offset().wrapping_neg(), 0, Ratio::new(1, 1));
WALL_CLOCK = wall_clock;
}
} else {
if constant_tsc || invariant_tsc {
// Calibrate the TSC even though it's not as good as we want, so we
// can still let folks still use it for cheap timing.
calibrate_tsc(has_pv_clock);
}
if has_hpet && (!force_wallclock || BootOptions::get().x86_wallclock == WallclockType::Hpet)
{
// Set up our wall clock to the HPET, and stash the initial
// transformation from ticks to clock monotonic.
// SAFETY: Single-threaded boot initialization on the boot CPU.
unsafe {
timer_set_ticks_to_time_ratio(HPET_TICKS_TO_CLOCK_MONOTONIC);
timer_set_initial_ticks(0);
}
// Explicitly set the value of the HPET to zero, then make sure it is
// started. Take a correspondence pair between HPET and TSC by observing
// TSC after we start the HPET so we can define the transformation between
// TSC (the EarlyTicks reference) and HPET.
//
// Note: we do not bother to bracket the observation of HPET with a TSC
// observation before and after. We are at a point in the boot where we
// are running on a single core, and should not be taking exceptions or
// interrupts yet. TL;DR, this observation should be "good enough"
// without any need for averaging.
let _ = hpet_set_value(0);
hpet_enable();
let tsc_reference: zx_ticks_t = current_ticks_rdtsc();
// Now set up our transformation from EarlyTicks (using TSC as a
// reference) and HPET (the reference for the zx_ticks_get timeline).
// SAFETY: `RDTSC_TICKS_TO_CLOCK_MONOTONIC` and `HPET_TICKS_TO_CLOCK_MONOTONIC` are
// initialized during single-threaded boot.
let (rdtsc_ratio, hpet_ratio) =
unsafe { (RDTSC_TICKS_TO_CLOCK_MONOTONIC, HPET_TICKS_TO_CLOCK_MONOTONIC) };
let rdtsc_ticks_to_hpet_ticks =
Ratio::product(rdtsc_ratio, hpet_ratio.inverse(), Exact::No);
// SAFETY: Single-threaded boot initialization in `pc_init_timer`.
unsafe {
EARLY_TICKS_TO_TICKS = Transform::new(tsc_reference, 0, rdtsc_ticks_to_hpet_ticks);
}
// HPET is now our chosen "ticks" reference.
wall_clock = ClockSource::Hpet;
// SAFETY: Single-threaded boot initialization in `pc_init_timer`.
unsafe {
WALL_CLOCK = wall_clock;
}
} else {
if force_wallclock && BootOptions::get().x86_wallclock != WallclockType::Pit {
panic!("Could not satisfy kernel.wallclock choice\n");
}
// Set up our wall clock to pit, and stash the initial
// transformation from ticks to clock monotonic.
// SAFETY: Single-threaded boot initialization on the boot CPU.
unsafe {
timer_set_ticks_to_time_ratio(Ratio::new(1_000_000, 1));
}
set_pit_frequency(DESIRED_PIT_FREQUENCY);
let irq: u32 = apic_io_isa_to_global(ISA_IRQ_PIT);
let status = register_permanent_int_handler(
irq,
InterruptHandler { cookie: core::ptr::null_mut(), r#fn: Some(pit_timer_tick) },
);
debug_assert!(status.is_ok());
let _ = unmask_interrupt(irq);
// See the HPET code above. Observe the value of TSC as we figure out the
// PIT offset so that we can define a function which maps EarlyTicks to
// ticks.
// SAFETY: Single-threaded boot initialization on the boot CPU.
unsafe {
timer_set_initial_ticks(current_ticks_pit() as u64);
}
let tsc_reference: zx_ticks_t = current_ticks_rdtsc();
// SAFETY: `RDTSC_TICKS_TO_CLOCK_MONOTONIC` is initialized during single-threaded boot.
let rdtsc_ticks_to_pit_ticks = unsafe {
Ratio::product(RDTSC_TICKS_TO_CLOCK_MONOTONIC, Ratio::new(1, 1_000_000), Exact::No)
};
// Note, see the comment above in the TSC section for why it is considered
// to be reasonably safe to perform the wrapping negation here.
// SAFETY: Single-threaded boot initialization in `pc_init_timer`.
unsafe {
EARLY_TICKS_TO_TICKS = Transform::new(
tsc_reference,
timer_get_mono_ticks_offset().wrapping_neg(),
rdtsc_ticks_to_pit_ticks,
);
}
// PIT is now our chosen "ticks" reference.
wall_clock = ClockSource::Pit;
// SAFETY: Single-threaded boot initialization in `pc_init_timer`.
unsafe {
WALL_CLOCK = wall_clock;
}
}
}
// Now that we've decided on which wall_clock to use as our timer reference, set up the ratio
// that converts from reference timer ticks to APIC ticks.
// SAFETY: `APIC_TICKS_PER_MS` is only modified during single-threaded boot in `pc_init_timer`.
let apic_ticks_per_ms = unsafe { APIC_TICKS_PER_MS };
match wall_clock {
ClockSource::Unselected => {
panic!("Wall clock was unselected by the time pc_init_timer completed\n");
}
ClockSource::Tsc => {
// SAFETY: `TSC_TICKS_PER_MS` is initialized in `calibrate_tsc`.
let tsc_ticks_per_ms = unsafe { TSC_TICKS_PER_MS };
assert!(tsc_ticks_per_ms <= u64::from(u32::MAX));
// SAFETY: Single-threaded boot initialization in `pc_init_timer`.
unsafe {
REFERENCE_TIMER_TICKS_TO_APIC_TICKS =
Ratio::new(apic_ticks_per_ms, tsc_ticks_per_ms as u32);
}
}
ClockSource::Hpet => {
let hpet_ticks_ms: u64 = hpet_ticks_per_ms();
assert!(hpet_ticks_ms <= u64::from(u32::MAX));
// SAFETY: Single-threaded boot initialization in `pc_init_timer`.
unsafe {
REFERENCE_TIMER_TICKS_TO_APIC_TICKS =
Ratio::new(apic_ticks_per_ms, hpet_ticks_ms as u32);
}
}
ClockSource::Pit => {
// Here's how we computed the ms_per_pit ratio:
//
// count = INTERNAL_FREQ_3X/DESIRED_PIT_FREQUENCY
// ms/pit = (3000 * count) / INTERNAL_FREQ_3X
// = (3000 * (INTERNAL_FREQ_3X/DESIRED_PIT_FREQUENCY))/ INTERNAL_FREQ_3X
// = (3000 * INTERNAL_FREQ_3X) / (DESIRED_PIT_FREQUENCY * INTERNAL_FREQ_3X)
// = 3000/DESIRED_PIT_FREQUENCY
let ms_per_pit = Ratio::new(3000, DESIRED_PIT_FREQUENCY);
let apic_per_ms = Ratio::new(apic_ticks_per_ms, 1);
// SAFETY: Single-threaded boot initialization in `pc_init_timer`.
unsafe {
REFERENCE_TIMER_TICKS_TO_APIC_TICKS =
Ratio::product(apic_per_ms, ms_per_pit, Exact::Yes);
}
}
}
kprint!(
"timer features: constant_tsc {} invariant_tsc {} tsc_deadline {}\n",
u32::from(constant_tsc),
u32::from(invariant_tsc),
u32::from(use_tsc_deadline)
);
kprint!("Using {:s} as wallclock\n", CLOCK_NAME[wall_clock as usize]);
}
init::lk_init_hook!(timer, pc_init_timer, init::LkInitLevel(init::LK_INIT_LEVEL_VM.0 + 3));
/// Converts the given duration's units from the platform's selected tick source to APIC ticks.
fn apic_ticks_from_platform_ticks(interval: zx_duration_t) -> u64 {
// SAFETY: `WALL_CLOCK` and `REFERENCE_TIMER_TICKS_TO_APIC_TICKS` are initialized once during
// single-threaded boot in `pc_init_timer` and are read-only thereafter.
let (wall_clock, reference_timer_ticks_to_apic_ticks) =
unsafe { (WALL_CLOCK, REFERENCE_TIMER_TICKS_TO_APIC_TICKS) };
debug_assert!(wall_clock != ClockSource::Unselected);
reference_timer_ticks_to_apic_ticks.scale::<{ Round::UP }>(interval) as u64
}
/// Programs the local APIC oneshot timer to fire at the given `deadline` in raw ticks.
pub fn platform_set_oneshot_timer(mut deadline: zx_ticks_t) -> Result<(), Status> {
debug_assert!(ints_disabled());
// We use 1 tick as the minimum deadline here because we want a deadline that immediately fires
// the timer, but we can't use 0 because setting a TSC deadline to zero disables the APIC timer.
deadline = deadline.max(1);
// SAFETY: `USE_TSC_DEADLINE` is initialized once during single-threaded boot in `pc_init_timer`
// and is read-only thereafter.
if unsafe { USE_TSC_DEADLINE } {
ltracef!("Scheduling oneshot timer: {} deadline\n", deadline);
apic_timer_set_tsc_deadline(deadline as u64);
PLATFORM_TIMER_SET_COUNTER.add(1);
return Ok(());
}
core::hint::cold_path();
let now: zx_ticks_t = platform_current_raw_ticks();
if now >= deadline {
core::hint::cold_path();
// Deadline has already passed. We still need to schedule a timer so that
// the interrupt fires.
ltracef!("Scheduling oneshot timer for min duration\n");
PLATFORM_TIMER_SET_COUNTER.add(1);
return apic_timer_set_oneshot(1, 1, false /* unmasked */);
}
let interval: zx_duration_t = deadline.saturating_sub(now);
debug_assert!(interval > 0);
// Convert the interval, which is in platform reference timer ticks, to APIC timer ticks.
let apic_ticks_needed: u64 = apic_ticks_from_platform_ticks(interval);
debug_assert!(apic_ticks_needed > 0);
// Find the shift needed for this timeout, since count is 32-bit.
let highest_set_bit: u32 = apic_ticks_needed.checked_ilog2().unwrap_or(0);
let mut extra_shift: u8 = if highest_set_bit <= 31 { 0 } else { (highest_set_bit - 31) as u8 };
if extra_shift > 8 {
extra_shift = 8;
}
// SAFETY: `APIC_DIVISOR` is initialized once during single-threaded boot in
// `calibrate_apic_timer` and is read-only thereafter.
let mut divisor: u32 = u32::from(unsafe { APIC_DIVISOR }) << extra_shift;
let mut count: u32;
// 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 = (apic_ticks_needed >> extra_shift) as u32;
debug_assert!((apic_ticks_needed >> extra_shift) <= u64::from(u32::MAX));
} else {
core::hint::cold_path();
divisor = 128;
count = u32::MAX;
}
// Make sure we're not underflowing
if count == 0 {
core::hint::cold_path();
debug_assert!(divisor == 1);
count = 1;
}
ltracef!("Scheduling oneshot timer: {} count, {} div\n", count, divisor);
PLATFORM_TIMER_SET_COUNTER.add(1);
apic_timer_set_oneshot(count, divisor as u8, false /* unmasked */)
}
/// Stops the calling CPU's hardware oneshot timer.
pub fn platform_stop_timer() {
// Enable interrupt mode that will stop the decreasing counter of the PIT
// outp(I8253_CONTROL_REG, 0x30);
// SAFETY: `USE_TSC_DEADLINE` is initialized once during single-threaded boot in `pc_init_timer`
// and is read-only thereafter.
if unsafe { USE_TSC_DEADLINE } {
// In TSC deadline mode, a deadline of 0 disarms the LAPIC timer
apic_timer_set_tsc_deadline(0);
} else {
core::hint::cold_path();
apic_timer_stop();
}
PLATFORM_TIMER_CANCEL_COUNTER.add(1);
}
/// Shuts down the platform timer on the calling CPU.
pub fn platform_shutdown_timer() {
debug_assert!(ints_disabled());
if x86_hypervisor_has_pv_clock() && curr_cpu_num() == 0 {
pv_clock_shutdown();
}
}
/// Suspends the platform timer on the current CPU.
pub fn platform_suspend_timer_curr_cpu() -> Result<(), Status> {
Err(Status::NOT_SUPPORTED)
}
/// Resumes the platform timer on the current CPU.
pub fn platform_resume_timer_curr_cpu() -> Result<(), Status> {
Err(Status::NOT_SUPPORTED)
}
/// Represents a raw early tick sample collected before the platform timer is initialized.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct EarlyTicks {
/// Raw TSC value sampled during early boot.
pub tsc: u64,
}
zr::static_assert_size_and_align!(EarlyTicks, 8, 8);
/// Converts an early boot tick sample to monotonic ticks.
pub fn platform_convert_early_ticks(sample: EarlyTicks) -> zx_instant_mono_ticks_t {
// SAFETY: `EARLY_TICKS_TO_TICKS` is initialized once during single-threaded boot in
// `pc_init_timer` and is read-only thereafter.
let early_ticks_to_ticks = unsafe { EARLY_TICKS_TO_TICKS };
early_ticks_to_ticks.apply::<{ Saturate::Yes }>(sample.tsc as i64)
}
/// Currently, usermode can access our source of ticks only if we have chosen TSC
/// to be our tick counter. Otherwise, they will need to go through a syscall.
///
/// In theory, we can fix this, but it would require having the vDSO map some
/// read-only memory in the user mode process (either the HPET registers, or the
/// variable which represents the PIT timer). Currently, doing this is not
/// something we support, and the vast majority of x64 systems that we run on
/// have an invariant TSC which is accessible from usermode. For now, we just
/// take the syscall hit instead of attempting to get more fancy.
pub fn platform_usermode_can_access_tick_registers() -> bool {
// SAFETY: `WALL_CLOCK` is initialized once during single-threaded boot in `pc_init_timer`
// and is read-only thereafter.
let wall_clock = unsafe { WALL_CLOCK };
wall_clock == ClockSource::Tsc
}
/// Sets the conversion ratio from HPET ticks to clock monotonic nanoseconds.
///
/// # Safety
/// Must be called at most once during single-threaded HPET initialization (`LK_INIT_LEVEL_VM + 2`)
/// before `pc_init_timer` (`LK_INIT_LEVEL_VM + 3`).
pub(super) unsafe fn hpet_set_ticks_to_clock_monotonic(n: u32, d: u32) {
// SAFETY: Caller guarantees single-threaded initialization before `pc_init_timer`.
unsafe {
HPET_TICKS_TO_CLOCK_MONOTONIC = Ratio::new(n, d);
}
}
// TODO(https://fxbug.dev/576428709): Remove these C FFI entry points once all callers are migrated
// to Rust.
/// FFI entry point for [`convert_raw_tsc_duration_to_nanoseconds`].
#[unsafe(no_mangle)]
pub extern "C" fn rust_convert_raw_tsc_duration_to_nanoseconds(duration: i64) -> zx_duration_t {
convert_raw_tsc_duration_to_nanoseconds(duration)
}
/// FFI entry point for [`convert_raw_tsc_timestamp_to_clock_monotonic`].
#[unsafe(no_mangle)]
pub extern "C" fn rust_convert_raw_tsc_timestamp_to_clock_monotonic(ts: i64) -> zx_instant_mono_t {
convert_raw_tsc_timestamp_to_clock_monotonic(ts)
}
/// FFI entry point for [`platform_set_oneshot_timer`].
#[unsafe(no_mangle)]
pub extern "C" fn rust_platform_set_oneshot_timer(deadline: zx_ticks_t) -> Result<(), Status> {
platform_set_oneshot_timer(deadline)
}
/// FFI entry point for [`platform_stop_timer`].
#[unsafe(no_mangle)]
pub extern "C" fn rust_platform_stop_timer() {
platform_stop_timer();
}
/// FFI entry point for [`platform_shutdown_timer`].
#[unsafe(no_mangle)]
pub extern "C" fn rust_platform_shutdown_timer() {
platform_shutdown_timer();
}
/// FFI entry point for [`platform_suspend_timer_curr_cpu`].
#[unsafe(no_mangle)]
pub extern "C" fn rust_platform_suspend_timer_curr_cpu() -> Result<(), Status> {
platform_suspend_timer_curr_cpu()
}
/// FFI entry point for [`platform_resume_timer_curr_cpu`].
#[unsafe(no_mangle)]
pub extern "C" fn rust_platform_resume_timer_curr_cpu() -> Result<(), Status> {
platform_resume_timer_curr_cpu()
}
/// FFI entry point for [`platform_convert_early_ticks`].
#[unsafe(no_mangle)]
pub extern "C" fn rust_platform_convert_early_ticks(sample: EarlyTicks) -> zx_instant_mono_ticks_t {
platform_convert_early_ticks(sample)
}
/// FFI entry point for [`platform_usermode_can_access_tick_registers`].
#[unsafe(no_mangle)]
pub extern "C" fn rust_platform_usermode_can_access_tick_registers() -> bool {
platform_usermode_can_access_tick_registers()
}