| // Copyright 2016 The Fuchsia Authors | |
| // Copyright (c) 2013, Google Inc. All rights reserved. | |
| // | |
| // 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 crate::arch_rs::arm64::arch_quirks_needs_arm_erratum_858921_mitigation; | |
| use crate::arch_rs::{curr_cpu_num, ints_disabled}; | |
| use crate::counters::define_kcounter; | |
| use crate::kernel::deadline::{Deadline, InstantUnknown}; | |
| use crate::kernel::mp::wait_for_all_cpus_ready; | |
| use crate::kernel::percpu::PerCpu; | |
| use crate::pdev_interrupt::{ | |
| InterruptHandler, InterruptVector, mask_interrupt, register_permanent_int_handler, | |
| unmask_interrupt, | |
| }; | |
| use crate::platform_rs::timer::{ | |
| timer_get_mono_ticks_offset, timer_set_initial_ticks, timer_set_ticks_to_time_ratio, | |
| timer_tick, zx_sec, | |
| }; | |
| use affine::Ratio; | |
| use boot_options::BootOptions; | |
| use core::ffi::c_void; | |
| use core::sync::atomic::{AtomicPtr, AtomicU8, AtomicU32, Ordering}; | |
| use debug::{dprintf, ltracef, ltracef_level}; | |
| use regio::arm64::{SysReg, spec}; | |
| use zbi::DcfgArmGenericTimerDriver; | |
| use zx_status::Status; | |
| use zx_types::zx_ticks_t; | |
| const LOCAL_TRACE: u32 = 0; | |
| // AArch64 timer control registers | |
| const TIMER_REG_CNTKCTL: SysReg<spec::CNTKCTL_EL1, u64> = SysReg::new(); | |
| const TIMER_REG_CNTFRQ: SysReg<spec::CNTFRQ_EL0, u64> = SysReg::new(); | |
| // CNTP AArch64 registers | |
| const TIMER_REG_CNTP_CTL: SysReg<spec::CNTP_CTL_EL0, u64> = SysReg::new(); | |
| const TIMER_REG_CNTP_CVAL: SysReg<spec::CNTP_CVAL_EL0, u64> = SysReg::new(); | |
| const TIMER_REG_CNTP_CTL_EL02: SysReg<spec::CNTP_CTL_EL02, u64> = SysReg::new(); | |
| const TIMER_REG_CNTP_CVAL_EL02: SysReg<spec::CNTP_CVAL_EL02, u64> = SysReg::new(); | |
| const TIMER_REG_CNTPCT: SysReg<spec::CNTPCT_EL0, u64> = SysReg::new(); | |
| // CNTPS "AArch64" registers | |
| const TIMER_REG_CNTPS_CTL: SysReg<spec::CNTPS_CTL_EL1, u64> = SysReg::new(); | |
| const TIMER_REG_CNTPS_CVAL: SysReg<spec::CNTPS_CVAL_EL1, u64> = SysReg::new(); | |
| // CNTV "AArch64" registers | |
| const TIMER_REG_CNTV_CTL: SysReg<spec::CNTV_CTL_EL0, u64> = SysReg::new(); | |
| const TIMER_REG_CNTV_CVAL: SysReg<spec::CNTV_CVAL_EL0, u64> = SysReg::new(); | |
| const TIMER_REG_CNTV_CTL_EL02: SysReg<spec::CNTV_CTL_EL02, u64> = SysReg::new(); | |
| const TIMER_REG_CNTV_CVAL_EL02: SysReg<spec::CNTV_CVAL_EL02, u64> = SysReg::new(); | |
| const TIMER_REG_CNTVCT: SysReg<spec::CNTVCT_EL0, u64> = SysReg::new(); | |
| define_kcounter!(PLATFORM_TIMER_SET_COUNTER, "platform.timer.set", Sum); | |
| define_kcounter!(PLATFORM_TIMER_CANCEL_COUNTER, "platform.timer.cancel", Sum); | |
| // Counter-timer Kernel Control Register, EL1. | |
| const CNTKCTL_EL1_ENABLE_PHYSICAL_COUNTER: u64 = 1 << 0; | |
| const CNTKCTL_EL1_ENABLE_VIRTUAL_COUNTER: u64 = 1 << 1; | |
| // Global saved config state | |
| static TIMER_IRQ: AtomicU32 = AtomicU32::new(0); | |
| #[repr(u8)] | |
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | |
| enum TimerIrqAssignment { | |
| Phys = 0, | |
| Virt = 1, | |
| Sphys = 2, | |
| } | |
| static TIMER_ASSIGNMENT: AtomicU8 = AtomicU8::new(TimerIrqAssignment::Phys as u8); | |
| // event stream state | |
| static EVENT_STREAM_SHIFT: AtomicU32 = AtomicU32::new(0); | |
| // Definition of the function signature we use to fetch the value of the chosen | |
| // reference counter. | |
| type ReadArmCounterFunc = fn() -> u64; | |
| unsafe extern "C" { | |
| fn arm64_allow_pct_in_el0(); | |
| } | |
| fn read_cntp_ctl() -> u32 { | |
| if libarch::arm64::CURRENT_EL.read().el() == 2 { | |
| return TIMER_REG_CNTP_CTL_EL02.read() as u32; | |
| } | |
| TIMER_REG_CNTP_CTL.read() as u32 | |
| } | |
| fn read_cntv_ctl() -> u32 { | |
| if libarch::arm64::CURRENT_EL.read().el() == 2 { | |
| return TIMER_REG_CNTV_CTL_EL02.read() as u32; | |
| } | |
| TIMER_REG_CNTV_CTL.read() as u32 | |
| } | |
| fn read_cntps_ctl() -> u32 { | |
| TIMER_REG_CNTPS_CTL.read() as u32 | |
| } | |
| fn write_cntp_ctl(val: u32) { | |
| ltracef_level!(3, "cntp_ctl: 0x{:x} {:x}\n", val, read_cntp_ctl()); | |
| if libarch::arm64::CURRENT_EL.read().el() == 2 { | |
| TIMER_REG_CNTP_CTL_EL02.write(val as u64); | |
| } else { | |
| TIMER_REG_CNTP_CTL.write(val as u64); | |
| } | |
| libarch::arm64::isb(); | |
| } | |
| fn write_cntv_ctl(val: u32) { | |
| ltracef_level!(3, "cntv_ctl: 0x{:x} {:x}\n", val, read_cntv_ctl()); | |
| if libarch::arm64::CURRENT_EL.read().el() == 2 { | |
| TIMER_REG_CNTV_CTL_EL02.write(val as u64); | |
| } else { | |
| TIMER_REG_CNTV_CTL.write(val as u64); | |
| } | |
| libarch::arm64::isb(); | |
| } | |
| fn write_cntps_ctl(val: u32) { | |
| ltracef_level!(3, "cntps_ctl: 0x{:x} {:x}\n", val, read_cntps_ctl()); | |
| TIMER_REG_CNTPS_CTL.write(val as u64); | |
| libarch::arm64::isb(); | |
| } | |
| fn write_cntp_cval(val: u64) { | |
| ltracef_level!(3, "cntp_cval: 0x{:016x}, {}\n", val, val); | |
| if libarch::arm64::CURRENT_EL.read().el() == 2 { | |
| TIMER_REG_CNTP_CVAL_EL02.write(val); | |
| } else { | |
| TIMER_REG_CNTP_CVAL.write(val); | |
| } | |
| libarch::arm64::isb(); | |
| } | |
| fn write_cntv_cval(val: u64) { | |
| ltracef_level!(3, "cntv_cval: 0x{:016x}, {}\n", val, val); | |
| if libarch::arm64::CURRENT_EL.read().el() == 2 { | |
| TIMER_REG_CNTV_CVAL_EL02.write(val); | |
| } else { | |
| TIMER_REG_CNTV_CVAL.write(val); | |
| } | |
| libarch::arm64::isb(); | |
| } | |
| fn write_cntps_cval(val: u64) { | |
| ltracef_level!(3, "cntps_cval: 0x{:016x}, {}\n", val, val); | |
| TIMER_REG_CNTPS_CVAL.write(val); | |
| libarch::arm64::isb(); | |
| } | |
| fn read_zero() -> u64 { | |
| 0 | |
| } | |
| fn read_cntpct_a73() -> u64 { | |
| // Workaround for Cortex-A73 erratum 858921. | |
| // Fix will be applied to all cores, as two consecutive reads should be | |
| // faster than checking if core is A73 and branching before every read. | |
| let old_read = TIMER_REG_CNTPCT.read(); | |
| let new_read = TIMER_REG_CNTPCT.read(); | |
| if (((old_read ^ new_read) >> 32) & 1) != 0 { old_read } else { new_read } | |
| } | |
| fn read_cntvct_a73() -> u64 { | |
| // Workaround for Cortex-A73 erratum 858921. | |
| // Fix will be applied to all cores, as two consecutive reads should be | |
| // faster than checking if core is A73 and branching before every read. | |
| let old_read = TIMER_REG_CNTVCT.read(); | |
| let new_read = TIMER_REG_CNTVCT.read(); | |
| if (((old_read ^ new_read) >> 32) & 1) != 0 { old_read } else { new_read } | |
| } | |
| fn read_cntpct() -> u64 { | |
| TIMER_REG_CNTPCT.read() | |
| } | |
| fn read_cntvct() -> u64 { | |
| TIMER_REG_CNTVCT.read() | |
| } | |
| /// Early tick sample delivered by the assembly macro `sample_ticks`. | |
| #[repr(C)] | |
| #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] | |
| pub struct EarlyTicks { | |
| pub cntpct_el0: u64, | |
| pub cntvct_el0: u64, | |
| } | |
| zr::static_assert_size_and_align!(EarlyTicks, 16, 8); | |
| zr::static_assert_size_and_align!(DcfgArmGenericTimerDriver, 16, 4); | |
| struct TimerRegProcs { | |
| write_ctl: fn(u32), | |
| write_cval: fn(u64), | |
| early_ticks: fn(EarlyTicks) -> u64, | |
| } | |
| static CNTP_PROCS: TimerRegProcs = TimerRegProcs { | |
| write_ctl: write_cntp_ctl, | |
| write_cval: write_cntp_cval, | |
| early_ticks: |sample| sample.cntpct_el0, | |
| }; | |
| static CNTV_PROCS: TimerRegProcs = TimerRegProcs { | |
| write_ctl: write_cntv_ctl, | |
| write_cval: write_cntv_cval, | |
| early_ticks: |sample| sample.cntvct_el0, | |
| }; | |
| static CNTPS_PROCS: TimerRegProcs = TimerRegProcs { | |
| write_ctl: write_cntps_ctl, | |
| write_cval: write_cntps_cval, | |
| early_ticks: |sample| sample.cntpct_el0, | |
| }; | |
| struct AtomicReadArmCounter(AtomicPtr<()>); | |
| impl AtomicReadArmCounter { | |
| const fn new(func: ReadArmCounterFunc) -> Self { | |
| Self(AtomicPtr::new(func as *mut ())) | |
| } | |
| #[inline] | |
| fn load(&self, order: Ordering) -> ReadArmCounterFunc { | |
| // SAFETY: `self.0` is only ever initialized or updated with valid `ReadArmCounterFunc` | |
| // (`fn() -> u64`) function pointers. | |
| unsafe { core::mem::transmute(self.0.load(order)) } | |
| } | |
| #[inline] | |
| fn store(&self, func: ReadArmCounterFunc, order: Ordering) { | |
| self.0.store(func as *mut (), order); | |
| } | |
| } | |
| // Notes about the `READ_ARM_COUNTER` function pointer: | |
| // | |
| // At startup time, we have not yet initialized our timer hardware, and | |
| // therefore must return zero as required by platform_current_raw_ticks_synchronized | |
| // (see the comments around that function for more details). Therefore, this | |
| // function pointer initially points to a function that always returns zero. | |
| // | |
| // Once we figure out what CPUs we are running on, identify their HW, and check | |
| // in with the rest of the system, we can pick which version of the | |
| // READ_ARM_COUNTER function we need to use. There exists a bug in certain ARM | |
| // Cortex-A73 CPUs which can lead to a bad read of either the VCT or PCT | |
| // counters. It is documented as Errata 858921 | |
| // ( https://documentation-service.arm.com/static/5fa29fa7b209f547eebd3613 ) | |
| // Thus, on systems with A73 cores, we must utilize a function that accounts for | |
| // this errata. On systems without A73 cores, we select a faster implementation. | |
| // | |
| // In order to make this switch, however, and not need any locks, we need to | |
| // make sure that the function pointer is declared as an atomic. Otherwise, we | |
| // could be writing to the pointer when someone else is reading it (in order to | |
| // read the clock) which would be a formal data race. Note that we don't need | |
| // any ordering of the loads and stores of the function pointer beyond | |
| // `Ordering::Relaxed`. It is not important that we establish a specific | |
| // order of the pointer's value relative to other memory accesses in the system. | |
| // We just need to make sure that _if_ we decide to switch to the faster version | |
| // of the counter read, that all of the CPUs _eventually_ see the pointer | |
| // update (which they will do because of unavoidable synchronizing events like | |
| // taking exceptions). | |
| // | |
| static READ_ARM_COUNTER: AtomicReadArmCounter = AtomicReadArmCounter::new(read_zero); | |
| #[inline] | |
| fn reg_procs() -> &'static TimerRegProcs { | |
| match TIMER_ASSIGNMENT.load(Ordering::Relaxed) { | |
| x if x == TimerIrqAssignment::Virt as u8 => &CNTV_PROCS, | |
| x if x == TimerIrqAssignment::Sphys as u8 => &CNTPS_PROCS, | |
| _ => &CNTP_PROCS, | |
| } | |
| } | |
| #[inline] | |
| fn write_ctl(val: u32) { | |
| (reg_procs().write_ctl)(val); | |
| } | |
| #[inline] | |
| fn write_cval(val: u64) { | |
| (reg_procs().write_cval)(val); | |
| } | |
| extern "C" fn platform_tick(_cookie: *mut c_void) { | |
| write_ctl(0); | |
| // SAFETY: `platform_tick` is registered as the timer interrupt handler and only invoked | |
| // from interrupt context with interrupts disabled. | |
| unsafe { | |
| timer_tick(); | |
| } | |
| } | |
| // GetTicksSyncFlag bit constants matching C++ `GetTicksSyncFlag` in `<platform/timer.h>`. | |
| const GET_TICKS_SYNC_FLAG_NONE: u8 = 0b0000; | |
| const GET_TICKS_SYNC_FLAG_AFTER_PREVIOUS_LOADS: u8 = 0b0001; | |
| const GET_TICKS_SYNC_FLAG_AFTER_PREVIOUS_STORES: u8 = 0b0010; | |
| const GET_TICKS_SYNC_FLAG_BEFORE_SUBSEQUENT_LOADS: u8 = 0b0100; | |
| const GET_TICKS_SYNC_FLAG_BEFORE_SUBSEQUENT_STORES: u8 = 0b1000; | |
| /// Reads a platform-specific ticks counter. | |
| /// | |
| /// This function is required to return zero if it is invoked before the timer hardware is | |
| /// initialized. Not doing so could violate the monotonicity of both the monotonic and boot | |
| /// timelines. | |
| /// | |
| /// This raw counter value is almost certainly not what you want; callers generally want to use the | |
| /// monotonic ticks value provided by timer_current_mono_ticks. This ticks value is the raw counter | |
| /// adjusted by a constant used to make the ticks timeline start ticking from 0 when the system | |
| /// boots. | |
| #[inline(always)] | |
| pub fn platform_current_raw_ticks_synchronized<const FLAGS: u8>() -> zx_ticks_t { | |
| // Make certain that any reads of the raw system timer are guaranteed to take | |
| // place in a region defined by the template |FLAGS|. Note that the | |
| // methodology used here was defined in | |
| // | |
| // 'Arm Architecture Reference Manual for A-profile architecture' | |
| // revision 'ARM DDI 0487K.a' | |
| // | |
| // In particular, please refer to examples D12-3 and D12-4. Note that we | |
| // chose to use the "DMB and Branch Dependency" approach (instead of a DSB) to | |
| // ensure that timer reads take place after all previous memory accesses, and | |
| // we use a load dependent on the value of the timer load to ensure that the | |
| // timer load takes place before subsequent memory accesses. Additionally, we | |
| // do not make any attempt to use self-synchronizing timer register accesses. | |
| // Refer to the cited examples for the potentially valid sequences. | |
| // Do we need to guarantee that this clock read occurs after previous loads, | |
| // stores, or both? | |
| // | |
| // If so, we need to implement the solution described in refer to Example | |
| // D12-4 in the ARM ARM, referenced above. | |
| // | |
| // We need our timer read to happen after all previous memory accesses. | |
| // This is implemented as a DMB followed by read from any valid memory | |
| // location with a "branch" which depends on that read. Note that | |
| // "branch" is in air quotes because its target is the next instruction, | |
| // so whether or not the branch gets taken, the result is the same (to | |
| // execute the next instruction). Finally, the sequence ends with an ISB. | |
| // | |
| // Also note that we need the DMB approach here, even when we only care about | |
| // previous loads, because we are attempting to ensure that our counter read | |
| // takes place after _all_ previous loads. If we were interested in only | |
| // ensuring that the counter observation took place after all previous loads | |
| // of a _specific_ variable (call it `X`), there would be another option to | |
| // consider. It is shown in example D12-3 of the ARM ARM, and involves | |
| // creating a branch which depends on the previously loaded value of `X`, | |
| // followed by an ISB. Since the spec of this function is to ensure that the | |
| // counter observation follow _all_ previous loads, it is not a technique we | |
| // can use here. | |
| // | |
| // TODO(johngro): Consider adding an API for a raw counter read which would | |
| // allow for this. There are two key cases (reading a synthetic kernel clock, | |
| // and reading the monotonic or boot timeline) where we don't actually need to | |
| // ensure that our counter read takes place after all previous loads, just the | |
| // previous load of a specific variable (the generation counter in the case of | |
| // a synthetic clock, and the offset in the case of a mono/boot timeline | |
| // read). That said, designing such an API presents some challenges as not | |
| // all of the specific variable reads have the same requirements. For | |
| // example, in the case of synthetic kernel clocks, the extra variable needs | |
| // to be read with acquire semantics. In the case of mono/boot reads, relaxed | |
| // semantics are all that are needed. | |
| // | |
| let must_read_after_all_previous_accesses: bool = (FLAGS | |
| & (GET_TICKS_SYNC_FLAG_AFTER_PREVIOUS_LOADS | GET_TICKS_SYNC_FLAG_AFTER_PREVIOUS_STORES)) | |
| != GET_TICKS_SYNC_FLAG_NONE; | |
| if must_read_after_all_previous_accesses { | |
| // We need our timer read to happen after all previous memory accesses. | |
| // This is implemented as a DMB followed by read from any valid memory | |
| // location with a "branch" which depends on that read. Note that | |
| // "branch" is in air quotes because its target is the next instruction, | |
| // so whether or not the branch gets taken, the result is the same (to | |
| // execute the next instruction). Finally, the sequence ends with an ISB. | |
| // | |
| // Refer to Example D12-4 in the ARM ARM referenced above. | |
| // | |
| // `temp` is overwritten as a scratch register. `nomem` is intentionally | |
| // omitted so the block acts as a compiler memory barrier to prevent | |
| // reordering across the sequence, and `preserves_flags` is set because | |
| // `dmb`, `ldr`, `cbz`, and `isb` do not modify NZCV. | |
| // | |
| // SAFETY: `[sp]` is always a valid readable memory location on the current stack, | |
| // and the assembly sequence only writes to the scratch `temp` register while acting | |
| // as a memory and instruction barrier. None of the instructions touch NZCV. | |
| unsafe { | |
| core::arch::asm!( | |
| "dmb sy", | |
| "ldr {temp}, [sp]", | |
| "cbz {temp}, 1f", | |
| "1: isb", | |
| temp = out(reg) _, | |
| options(nostack, preserves_flags), | |
| ); | |
| } | |
| } | |
| // Now actually read from the configured system timer. | |
| let ret: zx_ticks_t = READ_ARM_COUNTER.load(Ordering::Relaxed)() as zx_ticks_t; | |
| // Do we need to guarantee that this clock read occurs before subsequent loads, | |
| // stores, or both? If so, the recipe is the same in all cases. We introduce | |
| // a load operation which has data dependency on ret, forcing the timer read | |
| // to finish before the dependent load can occur. | |
| // | |
| // Refer to Example D12-4 in the ARM ARM referenced above. | |
| // | |
| let must_read_before_any_subsequent_access: bool = (FLAGS | |
| & (GET_TICKS_SYNC_FLAG_BEFORE_SUBSEQUENT_LOADS | |
| | GET_TICKS_SYNC_FLAG_BEFORE_SUBSEQUENT_STORES)) | |
| != GET_TICKS_SYNC_FLAG_NONE; | |
| if must_read_before_any_subsequent_access { | |
| // `temp` is overwritten as a scratch register and `ret` is consumed as | |
| // an input. Both `preserves_flags` and `nomem` are intentionally | |
| // omitted so the compiler treats flags as potentially clobbered and | |
| // treats the block as a compiler memory barrier preventing reordering. | |
| // | |
| // SAFETY: `eor` zeroes the scratch `temp` register with a data dependency on `ret`, so | |
| // `[sp, temp]` reads from `[sp]`, which is always a valid readable stack address. | |
| unsafe { | |
| core::arch::asm!( | |
| "eor {temp}, {ret}, {ret}", | |
| "ldr {temp}, [sp, {temp}]", | |
| temp = out(reg) _, | |
| ret = in(reg) ret, | |
| options(nostack), | |
| ); | |
| } | |
| } | |
| ret | |
| } | |
| // Explicit instantiation of all of the forms of synchronized tick access. | |
| macro_rules! define_raw_ticks_exports { | |
| ($( $name:ident => $flags:expr ),* $(,)?) => { | |
| $( | |
| /// FFI entry point for synchronized raw tick reads. | |
| #[unsafe(no_mangle)] | |
| pub extern "C" fn $name() -> zx_ticks_t { | |
| platform_current_raw_ticks_synchronized::<$flags>() | |
| } | |
| )* | |
| }; | |
| } | |
| define_raw_ticks_exports! { | |
| 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, | |
| } | |
| /// Convert a sample taken early on to a proper zx_ticks_t, if possible. | |
| /// This returns 0 if early samples are not convertible. | |
| pub fn platform_convert_early_ticks(sample: EarlyTicks) -> zx_ticks_t { | |
| // Early tick timestamps are always raw ticks. We need to convert back to | |
| // ticks by subtracting the raw_ticks to ticks offset. | |
| let early_ticks = (reg_procs().early_ticks)(sample); | |
| early_ticks.wrapping_add(timer_get_mono_ticks_offset() as u64) as zx_ticks_t | |
| } | |
| /// API to set/clear a hardware timer that is responsible for calling timer_tick() when it fires. | |
| pub fn platform_set_oneshot_timer(mut deadline: zx_ticks_t) -> Result<(), Status> { | |
| debug_assert!(ints_disabled()); | |
| if deadline < 0 { | |
| deadline = 0; | |
| } | |
| // Even if the deadline has already passed, the ARMv8-A timer will fire the | |
| // interrupt. | |
| write_cval(deadline as u64); | |
| write_ctl(1); | |
| PLATFORM_TIMER_SET_COUNTER.add(1); | |
| Ok(()) | |
| } | |
| /// Stops the hardware timer on the calling CPU. | |
| pub fn platform_stop_timer() { | |
| write_ctl(0); | |
| PLATFORM_TIMER_CANCEL_COUNTER.add(1); | |
| } | |
| /// Shutdown the calling CPU's platform timer. | |
| /// | |
| /// Should be called after |platform_stop_timer|, but before taking the CPU offline. | |
| /// | |
| /// TODO(maniscalco): Provide a "resume" function so we can suspend/resume. | |
| pub fn platform_shutdown_timer() { | |
| debug_assert!(ints_disabled()); | |
| // SAFETY: The interrupt controller ops are registered before any CPU reaches | |
| // `platform_shutdown_timer`, and `TIMER_IRQ` holds the vector chosen by | |
| // `arm_generic_timer_init`. | |
| let _ = unsafe { mask_interrupt(InterruptVector(TIMER_IRQ.load(Ordering::Relaxed))) }; | |
| } | |
| /// Suspend the platform timer for the calling CPU. | |
| /// | |
| /// It is an error to call this function with interrupts enabled. | |
| pub fn platform_suspend_timer_curr_cpu() -> Result<(), Status> { | |
| debug_assert!(ints_disabled()); | |
| // Save the cntkctl_el1 register, which includes the event stream state and | |
| // whether EL0 can read PCT. | |
| PerCpu::get_current() | |
| .resume_state | |
| .cntkctl_el1 | |
| .store(TIMER_REG_CNTKCTL.read(), Ordering::Relaxed); | |
| write_ctl(0); | |
| // SAFETY: The interrupt controller ops are registered before CPU suspend, and `TIMER_IRQ` | |
| // holds the vector chosen by `arm_generic_timer_init`. | |
| unsafe { mask_interrupt(InterruptVector(TIMER_IRQ.load(Ordering::Relaxed))) } | |
| } | |
| /// Resume the platform timer for the calling CPU. | |
| /// | |
| /// It is an error to call this function with interrupts enabled. | |
| pub fn platform_resume_timer_curr_cpu() -> Result<(), Status> { | |
| debug_assert!(ints_disabled()); | |
| TIMER_REG_CNTKCTL.write(PerCpu::get_current().resume_state.cntkctl_el1.load(Ordering::Relaxed)); | |
| // SAFETY: The interrupt controller ops are registered before CPU resume, and `TIMER_IRQ` | |
| // holds the vector chosen by `arm_generic_timer_init`. | |
| let _ = unsafe { unmask_interrupt(InterruptVector(TIMER_IRQ.load(Ordering::Relaxed))) }; | |
| // Kick the timer to get things going again. | |
| // | |
| // TODO(https://fxbug.dev/417558115): Remove/merge this with the logic in | |
| // IdlePowerThread::UpdateMonotonicClock such that we don't kick the platform | |
| // timer twice. | |
| platform_set_oneshot_timer(0) | |
| } | |
| /// A bool indicating whether or not user mode has direct access to the registers | |
| /// which allow directly observing the tick counter or not. | |
| pub fn platform_usermode_can_access_tick_registers() -> bool { | |
| // We always use the ARM generic timer for the tick counter, and these | |
| // registers are accessible from usermode | |
| true | |
| } | |
| fn arm_generic_timer_compute_conversion_factors<const ALLOW_DEBUG_PRINT: bool>( | |
| cntfrq: u32, | |
| ) -> Ratio { | |
| let cntpct_to_nsec = Ratio::new(zx_sec(1) as u32, cntfrq); | |
| if ALLOW_DEBUG_PRINT { | |
| dprintf!( | |
| SPEW, | |
| "arm generic timer cntpct_per_nsec: {}/{}\n", | |
| cntpct_to_nsec.numerator(), | |
| cntpct_to_nsec.denominator() | |
| ); | |
| } | |
| cntpct_to_nsec | |
| } | |
| // Run once on the boot cpu to decide if we want to start an event stream on each | |
| // cpu and at what rate. | |
| fn event_stream_init(cntfrq: u32) { | |
| if !BootOptions::get().arm64_event_stream_enabled { | |
| return; | |
| } | |
| // Compute the closest power of two from the timer frequency to get to the target. | |
| // | |
| // The mechanism to select the rate of the event counter is to select which bit in the virtual | |
| // counter it should watch for a transition from 0->1 or 1->0 on. This has the effect of | |
| // of selecting a power of two to divide the virtual counter by plus one. | |
| // | |
| // There's no real out of range value here. Everything gets clamped to a shift value of [0, 15]. | |
| let mut shift: u32 = 0; | |
| while shift <= 14 { | |
| // Find a matching shift to the target frequency within range. If the target frequency is | |
| // too large even for shift 0 then it'll just pick shift 0 because of the <=. | |
| if (cntfrq >> (shift + 1)).checked_ilog2().unwrap_or(0) | |
| <= BootOptions::get().arm64_event_stream_freq_hz.checked_ilog2().unwrap_or(0) | |
| { | |
| break; | |
| } | |
| shift += 1; | |
| } | |
| // If we ran off the end of the for loop 15 is the max shift and is okay | |
| debug_assert!(shift <= 15); | |
| // Save the computed state | |
| let freq = cntfrq >> (shift + 1); | |
| EVENT_STREAM_SHIFT.store(shift, Ordering::Relaxed); | |
| dprintf!( | |
| INFO, | |
| "arm generic timer will enable event stream on all cpus: shift {}, {} Hz\n", | |
| shift, | |
| freq | |
| ); | |
| } | |
| fn event_stream_enable_percpu() { | |
| if !BootOptions::get().arm64_event_stream_enabled { | |
| return; | |
| } | |
| let shift = EVENT_STREAM_SHIFT.load(Ordering::Relaxed); | |
| debug_assert!(shift <= 15); | |
| // Enable the event stream | |
| let mut cntkctl = TIMER_REG_CNTKCTL.read(); | |
| // Set the trigger bit (field 7:4) | |
| cntkctl &= !(0xfu64 << 4); | |
| cntkctl |= (shift as u64) << 4; // EVNTI | |
| // Clear the transition bit to 0 | |
| cntkctl &= !(1 << 3); // ENVTDIR | |
| // Enable the stream | |
| cntkctl |= 1 << 2; // EVNTEN | |
| TIMER_REG_CNTKCTL.write(cntkctl); | |
| dprintf!(INFO, "arm generic timer cpu-{}: event stream enabled\n", curr_cpu_num()); | |
| } | |
| fn arm_generic_timer_init_secondary_cpu(_level: init::LkInitLevel) { | |
| // try to enable the event stream if requested | |
| event_stream_enable_percpu(); | |
| let irq = TIMER_IRQ.load(Ordering::Relaxed); | |
| ltracef!("unmask irq {} on cpu {}\n", irq, curr_cpu_num()); | |
| // SAFETY: The interrupt controller ops are registered before secondary CPUs run this hook, | |
| // and `irq` is the vector chosen by `arm_generic_timer_init`. | |
| let _ = unsafe { unmask_interrupt(InterruptVector(irq)) }; | |
| } | |
| // secondary cpu initialize the timer just before the kernel starts with interrupts enabled | |
| init::lk_init_hook_flags!( | |
| arm_generic_timer_init_secondary_cpu, | |
| arm_generic_timer_init_secondary_cpu, | |
| init::LkInitLevel(init::LK_INIT_LEVEL_THREADING.0 - 1), | |
| init::LkInitFlags::SecondaryCpus | |
| ); | |
| /// Initializes the driver from the ZBI config, selecting the timer and setting up the ticks | |
| /// timeline and event stream. | |
| /// | |
| /// # Safety | |
| /// | |
| /// Must be called once, on the boot CPU with interrupts disabled, before secondary CPUs are | |
| /// started and before anything converts ticks to time. | |
| pub unsafe fn arm_generic_timer_init(config: &DcfgArmGenericTimerDriver) { | |
| let irq_phys = config.irq_phys; | |
| let mut irq_virt = config.irq_virt; | |
| let irq_sphys = config.irq_sphys; | |
| // If boot-options have been configured to force us to use the physical | |
| // counter as our reference, clear out any virtual timer hardware and let | |
| // nature take its course. If we don't have an interrupt configured for using | |
| // the physical timer hardware (either PHYS or SPHYS), we are going to end up | |
| // panicking. | |
| if BootOptions::get().arm64_force_pct | |
| || (libarch::arm64::CURRENT_EL.read().el() == 2 && irq_phys != 0) | |
| { | |
| dprintf!( | |
| INFO, | |
| "arm generic timer forcing use of PCT. \ | |
| IRQs provided were (virt {}, phys {}, sphys {})\n", | |
| irq_virt, | |
| irq_phys, | |
| irq_sphys | |
| ); | |
| irq_virt = 0; | |
| } | |
| // Always prefer to use the virtual timer if we have the option to do so. | |
| // Additionally, always start by using the versions of the timer read which | |
| // have the A73 errata workaround. | |
| // | |
| // Currently, we have not had a chance to boot all of our CPUs and determine | |
| // if we have any A73's in the mix. Until we know, it is safe to use the A73 | |
| // versions of the reads, just a small bit slower. Later on, if we know it is | |
| // safe to do so, we can switch to using the workaround-free version. | |
| let timer_str: &str; | |
| let timer_irq: u32; | |
| if irq_virt != 0 { | |
| timer_str = "virt"; | |
| timer_irq = irq_virt; | |
| TIMER_IRQ.store(timer_irq, Ordering::Relaxed); | |
| TIMER_ASSIGNMENT.store(TimerIrqAssignment::Virt as u8, Ordering::Relaxed); | |
| READ_ARM_COUNTER.store(read_cntvct_a73, Ordering::Relaxed); | |
| } else if irq_phys != 0 { | |
| timer_str = "phys"; | |
| timer_irq = irq_phys; | |
| TIMER_IRQ.store(timer_irq, Ordering::Relaxed); | |
| TIMER_ASSIGNMENT.store(TimerIrqAssignment::Phys as u8, Ordering::Relaxed); | |
| READ_ARM_COUNTER.store(read_cntpct_a73, Ordering::Relaxed); | |
| // SAFETY: FFI call with no preconditions; declared in <arch/arm64.h>. | |
| unsafe { | |
| arm64_allow_pct_in_el0(); | |
| } | |
| } else if irq_sphys != 0 { | |
| timer_str = "sphys"; | |
| timer_irq = irq_sphys; | |
| TIMER_IRQ.store(timer_irq, Ordering::Relaxed); | |
| TIMER_ASSIGNMENT.store(TimerIrqAssignment::Sphys as u8, Ordering::Relaxed); | |
| READ_ARM_COUNTER.store(read_cntpct_a73, Ordering::Relaxed); | |
| // SAFETY: FFI call with no preconditions; declared in <arch/arm64.h>. | |
| unsafe { | |
| arm64_allow_pct_in_el0(); | |
| } | |
| } else { | |
| panic!("no irqs set in arm_generic_timer_pdev_init\n"); | |
| } | |
| // We cannot actually reset the value on the ticks timer, so instead we use | |
| // the time of clock selection (now) to define the zero point on our ticks | |
| // timeline moving forward. | |
| // SAFETY: The caller upholds the one-time boot-CPU contract of this function. | |
| unsafe { | |
| timer_set_initial_ticks(READ_ARM_COUNTER.load(Ordering::Relaxed)()); | |
| } | |
| libarch::arm64::thread_memory_barrier(); | |
| let timer_cntfrq = if config.freq_override == 0 { | |
| // Read the firmware supplied cntfrq register. Note: it may not be correct | |
| // in buggy firmware situations, so always provide a mechanism to override it. | |
| let cntfrq = TIMER_REG_CNTFRQ.read() as u32; | |
| ltracef!("cntfrq: {:#010x}, {}\n", cntfrq, cntfrq); | |
| cntfrq | |
| } else { | |
| config.freq_override | |
| }; | |
| dprintf!( | |
| INFO, | |
| "arm generic timer using {} timer, irq {}, freq {} Hz\n", | |
| timer_str, | |
| timer_irq, | |
| timer_cntfrq | |
| ); | |
| // No way to reasonably continue. Just hard stop. | |
| assert!(timer_cntfrq != 0); | |
| // SAFETY: The caller upholds the one-time boot-CPU contract of this function. | |
| unsafe { | |
| timer_set_ticks_to_time_ratio(arm_generic_timer_compute_conversion_factors::<true>( | |
| timer_cntfrq, | |
| )); | |
| } | |
| // At this point in time, we expect that the `cntkctl_el1` timer register has | |
| // the bit which permits EL0 access to the VCT to be set, and perhaps also the | |
| // bit which allows access to the PCT if that happens to be the time reference | |
| // we have decided to use. | |
| // | |
| // *None* of the other timer HW access bits should be set. EL0 only gets to | |
| // look at the counter, and nothing more. | |
| // | |
| // ASSERT this now. | |
| let expected = CNTKCTL_EL1_ENABLE_VIRTUAL_COUNTER | |
| | if arm_use_phys_timer_in_vdso() { CNTKCTL_EL1_ENABLE_PHYSICAL_COUNTER } else { 0 }; | |
| let current = TIMER_REG_CNTKCTL.read(); | |
| assert!( | |
| current == expected, | |
| "CNTKCTL_EL1 register does not match reference counter selection ({:016x} != {:016x})", | |
| current, | |
| expected | |
| ); | |
| // Determine and compute values for the event stream if requested | |
| event_stream_init(timer_cntfrq); | |
| // try to enable the event stream if requested | |
| event_stream_enable_percpu(); | |
| } | |
| /// Another shot at initialization post interrupt controller and VM. | |
| pub fn arm_generic_timer_init_post_vm(_config: &DcfgArmGenericTimerDriver) { | |
| let irq = TIMER_IRQ.load(Ordering::Relaxed); | |
| // Set up the hardware timer irq handler for this vector. Use the permanent irq handler | |
| // registration scheme since it is enabled on all cpus and does not need any locking | |
| // for reentrancy and deregistration purposes. | |
| // SAFETY: `platform_tick` is safe to invoke from interrupt context and the interrupt | |
| // controller has been initialized. | |
| let status = unsafe { | |
| register_permanent_int_handler( | |
| InterruptVector(irq), | |
| InterruptHandler { cookie: core::ptr::null_mut(), r#fn: Some(platform_tick) }, | |
| ) | |
| }; | |
| debug_assert!(status.is_ok()); | |
| // enable the IRQ on the boot cpu | |
| ltracef!("unmask irq {} on cpu {}\n", irq, curr_cpu_num()); | |
| // SAFETY: The interrupt controller ops are registered (see above), and `irq` is the vector | |
| // chosen by `arm_generic_timer_init`. | |
| let _ = unsafe { unmask_interrupt(InterruptVector(irq)) }; | |
| } | |
| /// Indicates that we should be using the physical view of the system timer | |
| /// instead of the virtual view. | |
| pub fn arm_use_phys_timer_in_vdso() -> bool { | |
| TIMER_ASSIGNMENT.load(Ordering::Relaxed) != TimerIrqAssignment::Virt as u8 | |
| } | |
| fn late_update_keep_or_disable_a73_timer_workaround(_level: init::LkInitLevel) { | |
| // By the time we make it to LK_INIT_LEVEL_SMP_READY we should have started | |
| // and sync'ed up with all of our secondary CPUs. If not, something has gone | |
| // terribly wrong, and we should continue to use the A73 workaround out of an | |
| // abundance of caution. | |
| // | |
| let status = wait_for_all_cpus_ready(Deadline::no_slack(InstantUnknown(0))); | |
| if status.is_err() { | |
| dprintf!( | |
| ALWAYS, | |
| "At least one CPU has failed to check in by INIT_LEVEL_SMP_READY \ | |
| in the init sequence. Keeping A73 counter workarounds in place.\n" | |
| ); | |
| } else { | |
| // SAFETY: All CPUs have checked in and published their MIDR / uarch quirks. | |
| let needs_mitigation = unsafe { arch_quirks_needs_arm_erratum_858921_mitigation() }; | |
| if !needs_mitigation { | |
| // If all of our CPUs have checked in, and we have not discovered any A73 | |
| // CPUs, we can switch to using the simple register read instead of the | |
| // double-read required by the A73 workaround. | |
| let thunk: ReadArmCounterFunc = | |
| if arm_use_phys_timer_in_vdso() { read_cntpct } else { read_cntvct }; | |
| READ_ARM_COUNTER.store(thunk, Ordering::Relaxed); | |
| } else { | |
| dprintf!(INFO, "A73 cores detected. Keeping arm generic timer A73 workaround\n"); | |
| } | |
| } | |
| } | |
| init::lk_init_hook!( | |
| late_update_keep_or_disable_a73_timer_workaround, | |
| late_update_keep_or_disable_a73_timer_workaround, | |
| init::LK_INIT_LEVEL_SMP_READY | |
| ); | |
| // C FFI entrypoints for callers in C++. | |
| /// FFI entry point for [`platform_convert_early_ticks`]. | |
| #[unsafe(no_mangle)] | |
| pub extern "C" fn rust_platform_convert_early_ticks(sample: EarlyTicks) -> zx_ticks_t { | |
| platform_convert_early_ticks(sample) | |
| } | |
| /// 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_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() | |
| } | |
| /// FFI entry point for [`arm_generic_timer_init`]. | |
| /// | |
| /// # Safety | |
| /// | |
| /// See [`arm_generic_timer_init`]. | |
| #[unsafe(no_mangle)] | |
| pub unsafe extern "C" fn rust_arm_generic_timer_init(config: &DcfgArmGenericTimerDriver) { | |
| // SAFETY: The C++ caller upholds the one-time boot-CPU contract. | |
| unsafe { arm_generic_timer_init(config) }; | |
| } | |
| /// FFI entry point for [`arm_generic_timer_init_post_vm`]. | |
| #[unsafe(no_mangle)] | |
| pub extern "C" fn rust_arm_generic_timer_init_post_vm(config: &DcfgArmGenericTimerDriver) { | |
| arm_generic_timer_init_post_vm(config); | |
| } | |
| /// FFI entry point for [`arm_use_phys_timer_in_vdso`]. | |
| #[unsafe(no_mangle)] | |
| pub extern "C" fn rust_arm_use_phys_timer_in_vdso() -> bool { | |
| arm_use_phys_timer_in_vdso() | |
| } | |
| /// Tests for ARM tick count and current time | |
| #[cfg(ktest)] | |
| #[unittest::suite(name = "arm_clock")] | |
| mod arm_clock_tests { | |
| use crate::arch_rs::InterruptDisableGuard; | |
| use crate::kernel::mp::{get_online_mask, is_cpu_online}; | |
| use crate::kernel::percpu::PerCpu; | |
| use crate::kernel::scheduler::{peek_active_mask, peek_is_active}; | |
| use crate::kernel::thread::{DEFAULT_PRIORITY, ThreadPtr, create_with_priority}; | |
| use crate::kernel::types::{SMP_MAX_CPUS, cpu_num_t, cpu_num_to_mask}; | |
| use crate::platform_rs::timer::{InstantMono, ticks_per_second, timer_get_ticks_to_time_ratio}; | |
| use affine::Round; | |
| use core::sync::atomic::AtomicU32; | |
| use kprint::kprintln; | |
| use unittest::{assert_le, assert_true, unwrap_ok}; | |
| use zx_types::zx_time_t; | |
| const MIN_TEST_FREQ: u32 = 1; | |
| const MAX_TEST_FREQ: u32 = u32::MAX; | |
| const CUR_TEST_FREQ: u32 = 0; | |
| #[inline] | |
| fn abs_int64(a: i64) -> u64 { | |
| if a > 0 { a as u64 } else { a.wrapping_neg() as u64 } | |
| } | |
| fn test_time_conversion_check_result(a: u64, b: u64, limit: u64) -> bool { | |
| (unittest::subtest!(|a: u64, b: u64, limit: u64| { | |
| if a != b { | |
| let diff = abs_int64(a.wrapping_sub(b) as i64); | |
| assert_le!(diff, limit); | |
| } | |
| }))(a, b, limit) | |
| } | |
| fn test_time_to_ticks(cntfrq: u32) -> bool { | |
| (unittest::subtest!(|cntfrq_arg: u32| { | |
| let mut cntfrq = cntfrq_arg; | |
| let time_to_ticks: Ratio; | |
| if cntfrq == CUR_TEST_FREQ { | |
| let tps = ticks_per_second() as u64; | |
| assert_le!(tps, u32::MAX as u64); | |
| cntfrq = tps as u32; | |
| time_to_ticks = timer_get_ticks_to_time_ratio().inverse(); | |
| } else { | |
| time_to_ticks = | |
| arm_generic_timer_compute_conversion_factors::<false>(cntfrq).inverse(); | |
| } | |
| const VECTORS: [u64; 6] = [ | |
| 0, | |
| 1, | |
| 60 * 60 * 24, | |
| 60 * 60 * 24 * 365, | |
| 60 * 60 * 24 * (365 * 10 + 2), | |
| 60 * 60 * 24 * (365 * 100 + 2), | |
| ]; | |
| for vec in VECTORS { | |
| let cntpct = time_to_ticks.scale::<{ Round::DOWN }>(vec as i64) as u64; | |
| const NANOS_PER_SEC: u64 = zx_sec(1) as u64; | |
| let expected_cntpct = (cntfrq as u64 * vec + (NANOS_PER_SEC / 2)) / NANOS_PER_SEC; | |
| if !test_time_conversion_check_result(cntpct, expected_cntpct, 1) { | |
| kprintln!( | |
| "FAIL: zx_time_to_ticks({}): got {}, expect {}", | |
| vec, | |
| cntpct, | |
| expected_cntpct | |
| ); | |
| assert_true!(false); | |
| } | |
| } | |
| }))(cntfrq) | |
| } | |
| fn test_ticks_to_time(cntfrq: u32) -> bool { | |
| (unittest::subtest!(|cntfrq_arg: u32| { | |
| let mut cntfrq = cntfrq_arg; | |
| let ticks_to_time: Ratio; | |
| if cntfrq == CUR_TEST_FREQ { | |
| let tps = ticks_per_second() as u64; | |
| assert_le!(tps, u32::MAX as u64); | |
| cntfrq = tps as u32; | |
| ticks_to_time = timer_get_ticks_to_time_ratio(); | |
| } else { | |
| ticks_to_time = arm_generic_timer_compute_conversion_factors::<false>(cntfrq); | |
| } | |
| const VECTORS: [u64; 5] = [ | |
| 1, | |
| 60 * 60 * 24, | |
| 60 * 60 * 24 * 365, | |
| 60 * 60 * 24 * (365 * 10 + 2), | |
| 60 * 60 * 24 * (365 * 50 + 2), | |
| ]; | |
| for vec in VECTORS { | |
| let expected_zx_time: zx_time_t = zx_sec(vec as i64); | |
| let cntpct = cntfrq as u64 * vec; | |
| let zx_time: zx_time_t = ticks_to_time.scale::<{ Round::DOWN }>(cntpct as i64); | |
| let limit = (1000u64 * 1000).div_ceil(cntfrq as u64); | |
| if !test_time_conversion_check_result( | |
| zx_time as u64, | |
| expected_zx_time as u64, | |
| limit, | |
| ) { | |
| kprintln!( | |
| "ticks_to_zx_time(0x{:x}): got 0x{:x}, expect 0x{:x}", | |
| cntpct, | |
| zx_time as u64, | |
| expected_zx_time as u64 | |
| ); | |
| assert_true!(false); | |
| } | |
| } | |
| }))(cntfrq) | |
| } | |
| /// Time --> Ticks (min freq) | |
| #[test] | |
| fn test_time_to_ticks_min_freq() { | |
| assert_true!(test_time_to_ticks(MIN_TEST_FREQ)); | |
| } | |
| /// Time --> Ticks (max freq) | |
| #[test] | |
| fn test_time_to_ticks_max_freq() { | |
| assert_true!(test_time_to_ticks(MAX_TEST_FREQ)); | |
| } | |
| /// Time --> Ticks (cur freq) | |
| #[test] | |
| fn test_time_to_ticks_cur_freq() { | |
| assert_true!(test_time_to_ticks(CUR_TEST_FREQ)); | |
| } | |
| /// Ticks --> Time (min freq) | |
| #[test] | |
| fn test_ticks_to_time_min_freq() { | |
| assert_true!(test_ticks_to_time(MIN_TEST_FREQ)); | |
| } | |
| /// Ticks --> Time (max freq) | |
| #[test] | |
| fn test_ticks_to_time_max_freq() { | |
| assert_true!(test_ticks_to_time(MAX_TEST_FREQ)); | |
| } | |
| /// Ticks --> Time (cur freq) | |
| #[test] | |
| fn test_ticks_to_time_cur_freq() { | |
| assert_true!(test_ticks_to_time(CUR_TEST_FREQ)); | |
| } | |
| // Verify that the event stream will break CPUs out of WFE. | |
| // | |
| // Start one thread for each CPU that's online and active. Each thread will then disable | |
| // interrupts and issue a series of WFEs. If the event stream is working as expected, each | |
| // thread will eventually complete its series of WFEs and terminate. If the event stream is not | |
| // working as expected, one or more threads will hang. | |
| /// Event Stream | |
| #[test] | |
| fn test_event_stream() { | |
| if !BootOptions::get().arm64_event_stream_enabled { | |
| kprintln!("event stream disabled, skipping test"); | |
| return true; | |
| } | |
| struct Args { | |
| waiting: AtomicU32, | |
| } | |
| extern "C" fn func(a: *mut c_void) -> i32 { | |
| // SAFETY: `a` points to `Args` on the caller's stack, valid until all threads join. | |
| let args = unsafe { &*(a as *const Args) }; | |
| { | |
| let _guard = InterruptDisableGuard::new(); | |
| // Signal that we are ready. | |
| args.waiting.fetch_sub(1, Ordering::SeqCst); | |
| // Wait until everyone else is ready. | |
| while args.waiting.load(Ordering::SeqCst) > 0 { | |
| libarch::r#yield(); | |
| } | |
| // If the event stream is working, it (or something else) will break us out on each | |
| // iteration. | |
| for _ in 0..1000 { | |
| // The SEVL sets the event flag for this CPU. The first WFE consumes the now | |
| // set event flag. By setting then consuming, we can be sure the second WFE | |
| // will actually wait for an event. | |
| // SAFETY: `sevl` and `wfe` are architectural hint instructions with no memory, | |
| // register, or stack side effects. | |
| unsafe { | |
| core::arch::asm!("sevl;wfe;wfe"); | |
| } | |
| } | |
| } | |
| kprintln!("cpu-{} done", curr_cpu_num()); | |
| 0 | |
| } | |
| let args = Args { waiting: AtomicU32::new(0) }; | |
| let mut threads: [Option<ThreadPtr>; SMP_MAX_CPUS] = [None; SMP_MAX_CPUS]; | |
| // How many online+active CPUs do we have? | |
| let num_cpus = (get_online_mask() & peek_active_mask()).count_ones(); | |
| args.waiting.store(num_cpus, Ordering::SeqCst); | |
| // Create a thread bound to each online+active CPU, but don't start them just yet. | |
| let mut last: cpu_num_t = 0; | |
| let count = PerCpu::processor_count() as cpu_num_t; | |
| for i in 0..count { | |
| if is_cpu_online(i) && peek_is_active(i) { | |
| // SAFETY: `func` and `args` remain valid until `join` completes. | |
| let t_ptr = unwrap_ok!(unsafe { | |
| create_with_priority( | |
| c"test_event_stream".as_ptr(), | |
| func, | |
| core::ptr::from_ref(&args).cast_mut().cast(), | |
| DEFAULT_PRIORITY, | |
| ) | |
| }); | |
| // SAFETY: `t_ptr` was just created and has not been resumed yet. | |
| unsafe { t_ptr.set_cpu_affinity(cpu_num_to_mask(i)) }; | |
| threads[i as usize] = Some(t_ptr); | |
| last = i; | |
| } | |
| } | |
| // Because these threads have hard affinity and will disable interrupts we need to take care | |
| // in how we start them. If we start one that's bound to our current CPU, we may get | |
| // preempted deadlock. To avoid this, bind the current thread to the *last* online+active | |
| // CPU. | |
| // SAFETY: `ThreadPtr::current()` returns the valid current thread. | |
| let orig_mask = unsafe { | |
| let curr = ThreadPtr::current(); | |
| let orig = curr.get_cpu_affinity(); | |
| curr.set_cpu_affinity(cpu_num_to_mask(last)); | |
| orig | |
| }; | |
| let _restore_mask = zr::defer(move || { | |
| // SAFETY: `ThreadPtr::current()` returns the valid current thread. | |
| unsafe { ThreadPtr::current().set_cpu_affinity(orig_mask) }; | |
| }); | |
| // Now that we're running on the last online+active CPU we can simply start them in order. | |
| for thread in threads.iter().take(PerCpu::processor_count()) { | |
| if let Some(t_ptr) = *thread { | |
| // SAFETY: `t_ptr` is a valid unresumed thread. | |
| unsafe { t_ptr.resume() }; | |
| } | |
| } | |
| // Finally, wait for them to complete. | |
| for thread in threads.iter().take(PerCpu::processor_count()) { | |
| if let Some(t_ptr) = *thread { | |
| // SAFETY: `t_ptr` is a valid thread that has not been joined or detached. | |
| let _ = unsafe { t_ptr.join(InstantMono::INFINITE) }; | |
| } | |
| } | |
| } | |
| } |