diff --git a/issues/portable-kernel-code-names-the-tsc.md b/issues/portable-kernel-code-names-the-tsc.md deleted file mode 100644 index 251f599c86..0000000000 --- a/issues/portable-kernel-code-names-the-tsc.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -status: open -kind: defect -opened: 2026-09-29 ---- - -# Portable kernel code names the TSC - -On AArch64 the CPU's counter is the generic timer's `CNTVCT_EL0`, and the -portable kernel still calls it the TSC: `kernel/src/clock.rs`'s -`tsc_deadline`, `TSC_BOOT` and `TSC_PERIOD_FS`, `kernel/src/deadline.rs`'s -`AT_TSC`, and the xHCI driver's, `hardlockup`'s and `panic_reboot`'s waits -and comments read it by that name. `clock::counter_ticks`, which the AArch64 -timer reads, is renamed; the rest reads as x86-64's on both machines. - -**Exit condition**: no item or comment outside `kernel/src/arch/x86_64/` -names the TSC for the counter `crate::arch::cpu::counter` reads. diff --git a/kernel/pure/sched/cpu.rs b/kernel/pure/sched/cpu.rs index 19c94a4baa..397906a747 100644 --- a/kernel/pure/sched/cpu.rs +++ b/kernel/pure/sched/cpu.rs @@ -1266,7 +1266,7 @@ fn home_of(state: TaskState) -> Option { /// response is to find out which pass grew and why — not to raise it. /// /// **Why no panic stands over it.** The only clock either world can read across -/// a pass is wall clock — `rdtsc` in the kernel — and a guest's wall clock +/// a pass is wall clock — the CPU counter in the kernel — and a guest's wall clock /// advances while its vCPU is descheduled by the host. `elapsed` is therefore /// the pass plus any interval the hypervisor took the CPU away, and the second /// term is set by the host's scheduler, which this CPU neither observes nor @@ -2241,7 +2241,7 @@ pub struct CpuHandle { /// core owns, which is checkable in either world. Cost is not: the /// simulator's clock does not advance inside a step, so on the sim side the /// recorder is fed a modelled pass cost (`scenarios::overlong_pass`) and on - /// the kernel side the real TSC. + /// the kernel side the real CPU counter. /// /// It lives on the handle rather than in the `CpuSched` because the /// measurement lands *after* the pass has consumed every borrow of that, diff --git a/kernel/pure/sched/hw.rs b/kernel/pure/sched/hw.rs index a1e617599d..1c48b6ff47 100644 --- a/kernel/pure/sched/hw.rs +++ b/kernel/pure/sched/hw.rs @@ -3,10 +3,10 @@ //! [`Kicker`] knows only a CPU id; [`Machine`] adds the rest of the //! task-blind surface (clock, one-shot timer, interrupt gate, halt, trace); //! [`Hw`] adds the two operations that carry a task — the context switch and -//! the finalize sink. The kernel implements them with LAPIC one-shot, -//! targeted ICR, TSC and the asm switch; the simulator over a virtual -//! clock and vcpu bookkeeping. No scheduling decision, state transition or -//! ordering-sensitive code may live behind them. +//! the finalize sink. The kernel implements them with each architecture's +//! one-shot timer, targeted IPI, CPU counter and asm switch; the simulator +//! over a virtual clock and vcpu bookkeeping. No scheduling decision, state +//! transition or ordering-sensitive code may live behind them. use crate::sched::cpu::{RunToken, SleepToken}; use crate::sched::task::{SchedPayload, TaskAccounting, TaskKey}; @@ -91,8 +91,8 @@ pub trait Machine: Kicker + 'static { fn now(&self) -> Nanos; /// Program the one-shot timer for an **absolute** deadline. The kernel's - /// LAPIC one-shot is relative, so it converts; TSC-deadline mode is - /// absolute and will not. + /// one-shot takes a span (`irqchip::arm_one_shot`), so + /// `KernelHw::set_timer` converts. fn set_timer(&self, deadline: Nanos); fn stop_timer(&self); @@ -126,7 +126,7 @@ pub trait Machine: Kicker + 'static { /// The complete hardware surface. Everything above this trait is shared /// between the kernel and the simulator; everything behind it is -/// LAPIC/TSC/ICR/asm in the kernel and virtual time/pending-IPI bookkeeping +/// timer/IPI/counter/asm in the kernel and virtual time/pending-IPI bookkeeping /// in the sim. pub trait Hw: Machine { type Payload: SchedPayload; diff --git a/kernel/src/arch/x86_64/smp.rs b/kernel/src/arch/x86_64/smp.rs index f13c92c08b..45f1d0831e 100644 --- a/kernel/src/arch/x86_64/smp.rs +++ b/kernel/src/arch/x86_64/smp.rs @@ -17,7 +17,7 @@ const DATA_OFFSET: usize = 0xF00; /// The `rdtsc` the AP being started read on its first instruction in Rust. /// /// **The whole of the TSC-trail measurement.** `clock::nanos_since_boot` -/// subtracts one BSP-sampled `TSC_BOOT` whatever CPU asks, so a CPU whose TSC +/// subtracts one BSP-sampled `COUNTER_AT_BOOT` whatever CPU asks, so a CPU whose TSC /// reads below the BSP's has no honest answer to give; the reading is bracketed /// by two BSP samples taken either side of this AP's whole bring-up, which is /// serialised, so a synchronised TSC lands inside the bracket by construction diff --git a/kernel/src/clock.rs b/kernel/src/clock.rs index dc37a0ad1a..3d58950e89 100644 --- a/kernel/src/clock.rs +++ b/kernel/src/clock.rs @@ -16,9 +16,9 @@ use crate::time::Instant; use kernel::clock::{anchor, ticks_to_nanos}; use toyos_wallclock::Civil; -static TSC_BOOT: AtomicU64 = AtomicU64::new(0); -static TSC_PERIOD_FS: AtomicU64 = AtomicU64::new(0); -/// The counter's zero to `TSC_BOOT`, in nanoseconds. +static COUNTER_AT_BOOT: AtomicU64 = AtomicU64::new(0); +static COUNTER_PERIOD_FS: AtomicU64 = AtomicU64::new(0); +/// The counter's zero to `COUNTER_AT_BOOT`, in nanoseconds. static STAMP_AT_BOOT: AtomicU64 = AtomicU64::new(0); /// One counter tick at the rate the CPU states, in femtoseconds; zero where it /// states none, or before [`state`]. @@ -41,8 +41,8 @@ pub fn set_counter(boot: u64, period_fs: u64) { let stated = STATED_FS.load(Relaxed); let at_boot = ticks_to_nanos(boot, if stated != 0 { stated } else { period_fs }); STAMP_AT_BOOT.store(at_boot, Relaxed); - TSC_BOOT.store(boot, Relaxed); - TSC_PERIOD_FS.store(period_fs, Relaxed); + COUNTER_AT_BOOT.store(boot, Relaxed); + COUNTER_PERIOD_FS.store(period_fs, Relaxed); publish_page(boot, period_fs, at_boot); } @@ -105,7 +105,7 @@ pub fn map_page(space: &mut crate::mm::paging::AddressSpace) { /// Whether [`nanos_since_boot`] measures anything yet; false before [`init`]. pub fn calibrated() -> bool { - TSC_PERIOD_FS.load(Relaxed) != 0 + COUNTER_PERIOD_FS.load(Relaxed) != 0 } @@ -113,7 +113,7 @@ pub fn calibrated() -> bool { /// reads it from inside a bracket where panicking would reenter the log. /// Saturating, not wrapping: a trailing CPU reads as oldest, not lying newest after a 584-year wrap. pub fn nanos_since_boot() -> u64 { - ticks_to_nanos(cpu::counter().saturating_sub(TSC_BOOT.load(Relaxed)), TSC_PERIOD_FS.load(Relaxed)) + ticks_to_nanos(cpu::counter().saturating_sub(COUNTER_AT_BOOT.load(Relaxed)), COUNTER_PERIOD_FS.load(Relaxed)) } /// The same reading as an [`Instant`], the type arithmetic on it is allowed in. @@ -122,9 +122,9 @@ pub fn now() -> Instant { Instant::from_nanos_since_boot(nanos_since_boot()) } -/// The [`cpu::rdtsc`] value `nanos` in the future, for a wait loop that must +/// The [`cpu::counter`] reading `nanos` in the future, for a wait loop that must /// not call the nanosecond clock. -pub fn tsc_deadline(nanos: u64) -> u64 { +pub fn counter_deadline(nanos: u64) -> u64 { cpu::counter().saturating_add(counter_ticks(nanos)) } @@ -133,7 +133,7 @@ pub fn tsc_deadline(nanos: u64) -> u64 { /// needs. Before [`init`] the period is unknown and this is zero, so a bound /// derived from it is one its arm has to refuse. pub fn counter_ticks(nanos: u64) -> u64 { - let period_fs = TSC_PERIOD_FS.load(Relaxed); + let period_fs = COUNTER_PERIOD_FS.load(Relaxed); if period_fs == 0 { return 0; } @@ -145,12 +145,12 @@ pub fn counter_ticks(nanos: u64) -> u64 { /// Zero while the period is unknown, so a span taken on a machine that never /// calibrated reads as no time rather than as an invented one. pub fn nanos_of_ticks(ticks: u64) -> u64 { - ticks_to_nanos(ticks, TSC_PERIOD_FS.load(Relaxed)) + ticks_to_nanos(ticks, COUNTER_PERIOD_FS.load(Relaxed)) } /// Polls `ready` until it holds or `nanos` pass; `false` is the deadline. -/// Reads the TSC, not [`nanos_since_boot`], because that clock's out-of-line divide -/// would land an NMI sample's `rip` in `compiler_builtins` rather than in the wait. +/// Reads the counter, not [`nanos_since_boot`], because that clock's out-of-line divide +/// would land an NMI sample's PC in `compiler_builtins` rather than in the wait. /// /// **Before [`init`] there is no period to measure a span with, so this asks /// `ready` once and answers it.** A wait nothing can bound is the one thing an @@ -160,7 +160,7 @@ pub fn settles(nanos: u64, ready: impl Fn() -> bool) -> bool { if !calibrated() { return ready(); } - let until = tsc_deadline(nanos); + let until = counter_deadline(nanos); while !ready() { if cpu::counter() >= until { return false; @@ -187,7 +187,7 @@ pub fn init_wall(reading: Result<(Civil, u64), impl core::fmt::Display>) { } }; - BOOT_SECS.store(anchor(civil.to_unix_secs(), at, TSC_BOOT.load(Relaxed), TSC_PERIOD_FS.load(Relaxed)), Relaxed); + BOOT_SECS.store(anchor(civil.to_unix_secs(), at, COUNTER_AT_BOOT.load(Relaxed), COUNTER_PERIOD_FS.load(Relaxed)), Relaxed); WALL_KNOWN.store(true, Release); log!("clock: the RTC reads {civil} UTC"); } diff --git a/kernel/src/deadline.rs b/kernel/src/deadline.rs index 041b737b9f..db1599bcfd 100644 --- a/kernel/src/deadline.rs +++ b/kernel/src/deadline.rs @@ -10,14 +10,14 @@ //! //! So this one is armed off the parameter line and polled from the timer //! interrupt entry, on every CPU, in both rings. Its whole requirement is that -//! some CPU still takes an interrupt: [`poll`] reads two atomics and the TSC, -//! takes no lock, allocates nothing and calls nothing that can, so whatever a +//! some CPU still takes an interrupt: [`poll`] reads two atomics and the CPU's +//! counter, takes no lock, allocates nothing and calls nothing that can, so whatever a //! wedged path is holding is not on this path. //! //! # What it does not cover, stated rather than implied //! //! - **The span before `clock::init`,** which no bound in this file or in -//! [`crate::hardlockup`] reaches: no TSC period to convert a bound with, and +//! [`crate::hardlockup`] reaches: no counter period to convert a bound with, and //! no counter armed. A kernel that dies there still needs a hand. //! - **A machine on which no CPU takes an interrupt at all.** Nothing polled //! from a running CPU can cover that; **that half is [`crate::hardlockup`]**, @@ -54,12 +54,12 @@ pub const PHASES: &[&str] = &[ /// named none. Written on the BSP before any AP exists. static BOUND_MS: AtomicU64 = AtomicU64::new(0); -/// The `rdtsc` reading the machine is reset at, or 0 while unarmed. +/// The `cpu::counter` reading the machine is reset at, or 0 while unarmed. /// -/// **A TSC value and not a nanosecond one**, because `clock::nanos_since_boot`'s +/// **A counter reading and not a nanosecond one**, because `clock::nanos_since_boot`'s /// multiply-and-divide is out of line and this is read on every timer tick of /// every CPU. -static AT_TSC: AtomicU64 = AtomicU64::new(0); +static AT_COUNTER: AtomicU64 = AtomicU64::new(0); /// Whether a CPU has taken the expiry. One machine, one seal, one reset. static FIRED: AtomicBool = AtomicBool::new(false); @@ -79,7 +79,7 @@ pub fn claim_the_reset() -> bool { /// expiry would seal a `WEDGED` record over it. Disarms rather than latching a /// second flag, so [`poll`] stays one relaxed load. pub fn stand_down() { - AT_TSC.store(0, Relaxed); + AT_COUNTER.store(0, Relaxed); } /// Whether this boot runs under a bound at all — this one or @@ -87,10 +87,10 @@ pub fn stand_down() { /// /// **What the idle path asks before it re-arms a one-shot.** Both bounds rest on /// some CPU taking a timer interrupt, and a CPU woken by an IPI and held in Ring -/// 0 takes none; on a boot that named no bound that re-arm is a local APIC read and -/// two writes per wake that buy nothing. +/// 0 takes none; on a boot that named no bound that re-arm is timer +/// register traffic on every wake that buys nothing. pub fn armed() -> bool { - AT_TSC.load(Relaxed) != 0 + AT_COUNTER.load(Relaxed) != 0 } /// The last phase [`reached`] was told about, as an index into [`PHASES`]. @@ -163,16 +163,16 @@ pub fn claim(cmdline: &str) { } } -/// Turn the bound into a deadline, once the TSC period is known. +/// Turn the bound into a deadline, once the counter's period is known. /// -/// Called after `clock::init` and the LAPIC timer's arm, which are the two +/// Called after `clock::init` and the timer's arm, which are the two /// things [`poll`] needs: a period to convert with, and something that fires. pub fn start() { let ms = BOUND_MS.load(Relaxed); if ms == 0 { return; } - AT_TSC.store(crate::clock::tsc_deadline(ms.saturating_mul(1_000_000)), Relaxed); + AT_COUNTER.store(crate::clock::counter_deadline(ms.saturating_mul(1_000_000)), Relaxed); log!( "boot deadline: {ms} ms, after which this kernel seals a WEDGED record and writes the \ reset register itself" @@ -185,7 +185,7 @@ pub fn start() { } pub fn poll() { - past(AT_TSC.load(Relaxed)) + past(AT_COUNTER.load(Relaxed)) } fn past(at: u64) { @@ -205,7 +205,7 @@ static KERNEL_PC: [AtomicU64; MAX_CPUS] = [const { AtomicU64::new(0) }; MAX_CPUS /// `extern "C"` because the Ring 0 half of the x86 timer entry calls it from /// naked assembly, where the ABI is written out rather than inferred. pub extern "C" fn poll_in_kernel(pc: u64) { - let at = AT_TSC.load(Relaxed); + let at = AT_COUNTER.load(Relaxed); if at != 0 { KERNEL_PC[crate::arch::percpu::cpu_id() as usize].store(pc, Relaxed); } diff --git a/kernel/src/drivers/xhci/mod.rs b/kernel/src/drivers/xhci/mod.rs index 4977346b78..c906403e55 100644 --- a/kernel/src/drivers/xhci/mod.rs +++ b/kernel/src/drivers/xhci/mod.rs @@ -292,7 +292,7 @@ const MAX_HID_FAILURES: u8 = 8; /// How long the driver waits on any one command or transfer. /// -/// Not proof of a dead device: under TCG the TSC tracks host wall-clock, so a live device on an oversubscribed host can breach this once; only three breaches in one command are treated as a device fact. +/// Not proof of a dead device: under TCG the CPU's counter tracks host wall-clock, so a live device on an oversubscribed host can breach this once; only three breaches in one command are treated as a device fact. /// /// USB_TIMEOUT_NS and `block::OPERATION` are the same 2 s; the two must stay equal or the budget math (`Scsi::Budget` → `BlockError::BudgetExpired`) is wrong. const USB_TIMEOUT_NS: u64 = 2_000_000_000; @@ -1581,7 +1581,7 @@ fn lock_settles() -> bool { /// machine nobody can turn off. Not fair — a competitor taking a ticket wins — /// which is why both callers say what they do without it. fn take_within(bound: u64) -> Option>> { - let until = crate::clock::tsc_deadline(bound); + let until = crate::clock::counter_deadline(bound); loop { if let Some(guard) = XHCI.try_lock() { return Some(guard); diff --git a/kernel/src/drivers/xhci/stop.rs b/kernel/src/drivers/xhci/stop.rs index 22572109e7..c05282c537 100644 --- a/kernel/src/drivers/xhci/stop.rs +++ b/kernel/src/drivers/xhci/stop.rs @@ -614,7 +614,7 @@ impl Live { /// Stop every controller this kernel drives, and say what that did. /// /// The one entry, taken by the orderly shutdown and by a panic alike. Takes no -/// lock, allocates nothing, and every wait in it is bounded against the TSC. +/// lock, allocates nothing, and every wait in it is bounded against the CPU's counter. fn stop_all(said: &mut dyn fmt::Write) { let mut tally = Stopped::NOTHING; tally.disks = DISKS.load(Ordering::Relaxed); diff --git a/kernel/src/hardlockup/mod.rs b/kernel/src/hardlockup/mod.rs index ee4844d50c..e2d8a50d23 100644 --- a/kernel/src/hardlockup/mod.rs +++ b/kernel/src/hardlockup/mod.rs @@ -36,7 +36,7 @@ //! `log!` — the interrupted context may hold the log ring's own shard, and //! `build/sourcegate.rs`'s `nmi_does_not_log` is the gate. The sampling path is //! also on every CPU every second of every boot, so it is atomics and one -//! `rdtsc` and no division at all. The report divides, and only after the +//! `cpu::counter` read and no division at all. The report divides, and only after the //! machine is already being ended. //! //! # What it does not cover, stated rather than implied @@ -45,7 +45,7 @@ //! generates no sample. That is the right shape and not a gap: a halted CPU //! is not spinning on anything, and one that is woken takes the interrupt //! that wakes it. -//! - **The span before `clock::init`,** which has no TSC period to convert a +//! - **The span before `clock::init`,** which has no counter period to convert a //! bound with, and before `irqchip::init`, which has no LVT to arm. The same //! floor `crate::deadline` states. //! - **A CPU with `IF` set that no timer ever interrupts.** Nothing resets it, @@ -77,20 +77,20 @@ pub const LOCKED_UP: &str = "a cpu locked up with interrupts off"; /// lateness reads the same period. const SAMPLE_NS: u64 = toyos_tco::HARD_LOCKUP_SAMPLE_NS; -/// The bound in TSC ticks, or 0 for a boot that armed none. Written on the BSP +/// The bound in counter ticks, or 0 for a boot that armed none. Written on the BSP /// before any AP exists. -static BOUND_TSC: AtomicU64 = AtomicU64::new(0); +static BOUND_TICKS: AtomicU64 = AtomicU64::new(0); /// The same bound in milliseconds, for the record to quote and the probe to /// stage against. static BOUND_MS: AtomicU64 = AtomicU64::new(0); -/// TSC ticks per millisecond, so the report can turn a span into a number +/// Counter ticks per millisecond, so the report can turn a span into a number /// without reaching for the nanosecond clock's out-of-line divide. static TICKS_PER_MS: AtomicU64 = AtomicU64::new(0); -/// The counter's reload period, in reference cycles — which are TSC ticks, -/// since fixed counter 2 counts the reference clock (SDM Vol. 3B §20.2.2). +/// The performance counter's reload period, in `cpu::counter` ticks, the unit +/// `pmu::arm` takes. static PERIOD: AtomicU64 = AtomicU64::new(0); /// Whether the panic path has taken this machine. A panicked kernel holds its @@ -104,7 +104,7 @@ static STOOD_DOWN: AtomicBool = AtomicBool::new(false); /// and where the last sample found it. static PROGRESS: [AtomicU64; MAX_CPUS] = [const { AtomicU64::new(0) }; MAX_CPUS]; static STILL_SINCE: [AtomicU64; MAX_CPUS] = [const { AtomicU64::new(0) }; MAX_CPUS]; -static AT_TSC: [AtomicU64; MAX_CPUS] = [const { AtomicU64::new(0) }; MAX_CPUS]; +static AT_COUNTER: [AtomicU64; MAX_CPUS] = [const { AtomicU64::new(0) }; MAX_CPUS]; static AT_PC: [AtomicU64; MAX_CPUS] = [const { AtomicU64::new(0) }; MAX_CPUS]; static AT_SP: [AtomicU64; MAX_CPUS] = [const { AtomicU64::new(0) }; MAX_CPUS]; static AT_FLAGS: [AtomicU64; MAX_CPUS] = [const { AtomicU64::new(0) }; MAX_CPUS]; @@ -136,7 +136,7 @@ pub fn start(deadline_ms: u64) -> Armed { return Armed { ms: 0, sampled: false }; } BOUND_MS.store(ms, Relaxed); - BOUND_TSC.store(ticks, Relaxed); + BOUND_TICKS.store(ticks, Relaxed); TICKS_PER_MS.store(per_ms, Relaxed); PERIOD.store(crate::clock::counter_ticks(SAMPLE_NS), Relaxed); arm_this_cpu(); @@ -181,7 +181,7 @@ impl fmt::Display for Armed { /// Every CPU, called once as it joins: the counters and the LVT are per logical /// CPU, and a CPU nobody armed is one this bound does not cover. pub fn arm_this_cpu() { - if BOUND_TSC.load(Relaxed) == 0 { + if BOUND_TICKS.load(Relaxed) == 0 { return; } let me = percpu::cpu_id() as usize; @@ -224,7 +224,7 @@ pub fn bound_ms() -> u64 { /// that is not this CPU's own overflow, so the diagnostic senders — the blocked /// task dump's probe — cost one load and one compare. pub fn sample(pc: u64, sp: u64, flags: u64) { - if BOUND_TSC.load(Relaxed) == 0 || STOOD_DOWN.load(Relaxed) { + if BOUND_TICKS.load(Relaxed) == 0 || STOOD_DOWN.load(Relaxed) { return; } let me = percpu::cpu_id() as usize; @@ -242,7 +242,7 @@ pub fn sample(pc: u64, sp: u64, flags: u64) { AT_PC[me].store(pc, Relaxed); AT_SP[me].store(sp, Relaxed); AT_FLAGS[me].store(flags, Relaxed); - AT_TSC[me].store(now, Relaxed); + AT_COUNTER[me].store(now, Relaxed); let taken = crate::irq_census::taken_here(); let moved = PROGRESS[me].swap(taken, Relaxed) != taken; @@ -251,7 +251,7 @@ pub fn sample(pc: u64, sp: u64, flags: u64) { // reaches, and this mechanism is not about it. if moved || trap::frame_interrupts_enabled(flags) { STILL_SINCE[me].store(now, Relaxed); - } else if now.wrapping_sub(STILL_SINCE[me].load(Relaxed)) >= BOUND_TSC.load(Relaxed) { + } else if now.wrapping_sub(STILL_SINCE[me].load(Relaxed)) >= BOUND_TICKS.load(Relaxed) { locked_up(me, pc, sp, now) } // **After the decision and never before it.** Re-arming clears the mask @@ -330,7 +330,7 @@ pub fn spinning_on_nothing(was: Spinning) { } } -/// Milliseconds, from a TSC span. One `div`, and only on the path that has +/// Milliseconds, from a `cpu::counter` span. One `div`, and only on the path that has /// already decided to end the machine. struct Ms(u64); @@ -397,7 +397,7 @@ impl fmt::Display for Report { // since its own last sample is a CPU that is still running, which // is the first thing a reader of a wedged machine wants to know. let irqs = crate::irq_census::taken_by(cpu as u32).unwrap_or(0); - let at = AT_TSC[cpu].load(Relaxed); + let at = AT_COUNTER[cpu].load(Relaxed); if at == 0 { writeln!( f, diff --git a/kernel/src/hardlockup/probe.rs b/kernel/src/hardlockup/probe.rs index 720b65ac9d..8f4c2d59fb 100644 --- a/kernel/src/hardlockup/probe.rs +++ b/kernel/src/hardlockup/probe.rs @@ -114,7 +114,7 @@ fn hold_and_sample(victim: usize) -> ! { ); loop { irqchip::send_nmi(victim as u32); - let until = crate::clock::tsc_deadline(SEND_EVERY_NS); + let until = crate::clock::counter_deadline(SEND_EVERY_NS); while cpu::counter() < until { core::hint::spin_loop(); } @@ -133,7 +133,7 @@ fn go_deaf(me: usize, bound_ms: u64) -> ! { deaf_ns / 1_000_000, ); // Read before `IF` goes, because the record above has to be out first. - let until = crate::clock::tsc_deadline(deaf_ns); + let until = crate::clock::counter_deadline(deaf_ns); STAGE.store(DEAF, Ordering::Release); // Not an `IrqGuard`: nothing here re-enables them, which is the point. cpu::disable_interrupts(); diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 7fe02d9bba..e84d1b423b 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -429,7 +429,7 @@ pub(crate) unsafe extern "C" fn kernel_main(loader_args: &mut KernelArgs) -> ! { arch::boot::clock(kernel_args); trace::enable(); arch::boot::timer(); - // After both halves of what it needs: a TSC period to convert its bound + // After both halves of what it needs: a counter period to convert its bound // with, and a timer whose every tick polls it. deadline::start(); diff --git a/kernel/src/panic_reboot.rs b/kernel/src/panic_reboot.rs index 88d9fbf238..99c387092a 100644 --- a/kernel/src/panic_reboot.rs +++ b/kernel/src/panic_reboot.rs @@ -2,13 +2,13 @@ //! panel: it holds the panel for [`toyos_tco::PANIC_BOUND_MS`] and then returns //! the machine to firmware. //! -//! **The bound is carried in counter ticks, not nanoseconds** (`cpu::counter`: -//! the TSC, the generic timer's count). A panic may land before `clock::init`, -//! where the calibrated clock reads zero for every interval; the tick count -//! comes from the frequency the CPU states instead (CPUID, `CNTFRQ_EL0`), and -//! both phases then compare the same counter against the same unit. A machine -//! that states no frequency and has no calibrated clock cannot time anything, -//! and the arm line says so instead of resetting on a guess. +//! **The bound is carried in `cpu::counter` ticks, not nanoseconds.** A panic +//! may land before `clock::init`, where the calibrated clock reads zero for +//! every interval; the tick count comes from the frequency the CPU states +//! instead (`cpu::stated_counter_hz`), and both phases then compare the same +//! counter against the same unit. A machine that states no frequency and has no +//! calibrated clock cannot time anything, and the arm line says so instead of +//! resetting on a guess. use crate::arch::cpu; use crate::drivers::serial; @@ -78,7 +78,7 @@ const HELD: &str = "panic: holding this panel"; /// The bound in counter ticks from now, and which clock said so. fn deadline(bound: Budget) -> Option<(u64, Source)> { if crate::clock::calibrated() { - return Some((crate::clock::tsc_deadline(bound.nanos()), Source::Calibrated)); + return Some((crate::clock::counter_deadline(bound.nanos()), Source::Calibrated)); } let hz = crate::arch::cpu::stated_counter_hz()?; // Nanoseconds first, so a bound under a second is not rounded to nothing.