TimeManagement.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include <AK/Singleton.h>
  27. #include <AK/StdLibExtras.h>
  28. #include <AK/Time.h>
  29. #include <Kernel/ACPI/Parser.h>
  30. #include <Kernel/CommandLine.h>
  31. #include <Kernel/Interrupts/APIC.h>
  32. #include <Kernel/Scheduler.h>
  33. #include <Kernel/Time/APICTimer.h>
  34. #include <Kernel/Time/HPET.h>
  35. #include <Kernel/Time/HPETComparator.h>
  36. #include <Kernel/Time/HardwareTimer.h>
  37. #include <Kernel/Time/PIT.h>
  38. #include <Kernel/Time/RTC.h>
  39. #include <Kernel/Time/TimeManagement.h>
  40. #include <Kernel/VM/MemoryManager.h>
  41. //#define TIME_DEBUG
  42. namespace Kernel {
  43. static AK::Singleton<TimeManagement> s_the;
  44. TimeManagement& TimeManagement::the()
  45. {
  46. return *s_the;
  47. }
  48. bool TimeManagement::is_system_timer(const HardwareTimerBase& timer) const
  49. {
  50. return &timer == m_system_timer.ptr();
  51. }
  52. void TimeManagement::set_epoch_time(timespec ts)
  53. {
  54. InterruptDisabler disabler;
  55. m_epoch_time = ts;
  56. m_remaining_epoch_time_adjustment = { 0, 0 };
  57. }
  58. timespec TimeManagement::epoch_time() const
  59. {
  60. return m_epoch_time;
  61. }
  62. void TimeManagement::initialize(u32 cpu)
  63. {
  64. if (cpu == 0) {
  65. ASSERT(!s_the.is_initialized());
  66. s_the.ensure_instance();
  67. // Initialize the APIC timers after the other timers as the
  68. // initialization needs to briefly enable interrupts, which then
  69. // would trigger a deadlock trying to get the s_the instance while
  70. // creating it.
  71. if (auto* apic_timer = APIC::the().initialize_timers(*s_the->m_system_timer)) {
  72. klog() << "Time: Using APIC timer as system timer";
  73. s_the->set_system_timer(*apic_timer);
  74. }
  75. } else {
  76. ASSERT(s_the.is_initialized());
  77. if (auto* apic_timer = APIC::the().get_timer()) {
  78. klog() << "Time: Enable APIC timer on CPU #" << cpu;
  79. apic_timer->enable_local_timer();
  80. }
  81. }
  82. }
  83. void TimeManagement::set_system_timer(HardwareTimerBase& timer)
  84. {
  85. auto original_callback = m_system_timer->set_callback(nullptr);
  86. timer.set_callback(move(original_callback));
  87. m_system_timer = timer;
  88. }
  89. time_t TimeManagement::seconds_since_boot() const
  90. {
  91. return m_seconds_since_boot;
  92. }
  93. time_t TimeManagement::ticks_per_second() const
  94. {
  95. return m_system_timer->ticks_per_second();
  96. }
  97. time_t TimeManagement::ticks_this_second() const
  98. {
  99. return m_ticks_this_second;
  100. }
  101. time_t TimeManagement::boot_time() const
  102. {
  103. return RTC::boot_time();
  104. }
  105. TimeManagement::TimeManagement()
  106. {
  107. bool probe_non_legacy_hardware_timers = !(kernel_command_line().lookup("time").value_or("modern") == "legacy");
  108. if (ACPI::is_enabled()) {
  109. if (!ACPI::Parser::the()->x86_specific_flags().cmos_rtc_not_present) {
  110. RTC::initialize();
  111. m_epoch_time.tv_sec += boot_time();
  112. } else {
  113. klog() << "ACPI: RTC CMOS Not present";
  114. }
  115. } else {
  116. // We just assume that we can access RTC CMOS, if ACPI isn't usable.
  117. RTC::initialize();
  118. m_epoch_time.tv_sec += boot_time();
  119. }
  120. if (probe_non_legacy_hardware_timers) {
  121. if (!probe_and_set_non_legacy_hardware_timers())
  122. if (!probe_and_set_legacy_hardware_timers())
  123. ASSERT_NOT_REACHED();
  124. } else if (!probe_and_set_legacy_hardware_timers()) {
  125. ASSERT_NOT_REACHED();
  126. }
  127. }
  128. timeval TimeManagement::now_as_timeval()
  129. {
  130. timespec ts = s_the.ptr()->epoch_time();
  131. timeval tv;
  132. timespec_to_timeval(ts, tv);
  133. return tv;
  134. }
  135. Vector<HardwareTimerBase*> TimeManagement::scan_and_initialize_periodic_timers()
  136. {
  137. bool should_enable = is_hpet_periodic_mode_allowed();
  138. dbg() << "Time: Scanning for periodic timers";
  139. Vector<HardwareTimerBase*> timers;
  140. for (auto& hardware_timer : m_hardware_timers) {
  141. if (hardware_timer.is_periodic_capable()) {
  142. timers.append(&hardware_timer);
  143. if (should_enable)
  144. hardware_timer.set_periodic();
  145. }
  146. }
  147. return timers;
  148. }
  149. Vector<HardwareTimerBase*> TimeManagement::scan_for_non_periodic_timers()
  150. {
  151. dbg() << "Time: Scanning for non-periodic timers";
  152. Vector<HardwareTimerBase*> timers;
  153. for (auto& hardware_timer : m_hardware_timers) {
  154. if (!hardware_timer.is_periodic_capable())
  155. timers.append(&hardware_timer);
  156. }
  157. return timers;
  158. }
  159. bool TimeManagement::is_hpet_periodic_mode_allowed()
  160. {
  161. auto hpet_mode = kernel_command_line().lookup("hpet").value_or("periodic");
  162. if (hpet_mode == "periodic")
  163. return true;
  164. if (hpet_mode == "nonperiodic")
  165. return false;
  166. ASSERT_NOT_REACHED();
  167. }
  168. bool TimeManagement::probe_and_set_non_legacy_hardware_timers()
  169. {
  170. if (!ACPI::is_enabled())
  171. return false;
  172. if (!HPET::test_and_initialize())
  173. return false;
  174. if (!HPET::the().comparators().size()) {
  175. dbg() << "HPET initialization aborted.";
  176. return false;
  177. }
  178. dbg() << "HPET: Setting appropriate functions to timers.";
  179. for (auto& hpet_comparator : HPET::the().comparators())
  180. m_hardware_timers.append(hpet_comparator);
  181. auto periodic_timers = scan_and_initialize_periodic_timers();
  182. auto non_periodic_timers = scan_for_non_periodic_timers();
  183. if (is_hpet_periodic_mode_allowed())
  184. ASSERT(!periodic_timers.is_empty());
  185. ASSERT(periodic_timers.size() + non_periodic_timers.size() >= 2);
  186. if (periodic_timers.size() >= 2) {
  187. m_time_keeper_timer = periodic_timers[1];
  188. m_system_timer = periodic_timers[0];
  189. } else {
  190. if (periodic_timers.size() == 1) {
  191. m_time_keeper_timer = periodic_timers[0];
  192. m_system_timer = non_periodic_timers[0];
  193. } else {
  194. m_time_keeper_timer = non_periodic_timers[1];
  195. m_system_timer = non_periodic_timers[0];
  196. }
  197. }
  198. m_system_timer->set_callback(Scheduler::timer_tick);
  199. m_time_keeper_timer->set_callback(TimeManagement::update_time);
  200. dbg() << "Reset timers";
  201. m_system_timer->try_to_set_frequency(m_system_timer->calculate_nearest_possible_frequency(1024));
  202. m_time_keeper_timer->try_to_set_frequency(OPTIMAL_TICKS_PER_SECOND_RATE);
  203. return true;
  204. }
  205. bool TimeManagement::probe_and_set_legacy_hardware_timers()
  206. {
  207. if (ACPI::is_enabled()) {
  208. if (ACPI::Parser::the()->x86_specific_flags().cmos_rtc_not_present) {
  209. dbg() << "ACPI: CMOS RTC Not Present";
  210. return false;
  211. } else {
  212. dbg() << "ACPI: CMOS RTC Present";
  213. }
  214. }
  215. m_hardware_timers.append(PIT::initialize(TimeManagement::update_time));
  216. m_hardware_timers.append(RealTimeClock::create(Scheduler::timer_tick));
  217. m_time_keeper_timer = m_hardware_timers[0];
  218. m_system_timer = m_hardware_timers[1];
  219. return true;
  220. }
  221. void TimeManagement::update_time(const RegisterState& regs)
  222. {
  223. TimeManagement::the().increment_time_since_boot(regs);
  224. }
  225. void TimeManagement::increment_time_since_boot(const RegisterState&)
  226. {
  227. ASSERT(!m_time_keeper_timer.is_null());
  228. // Compute time adjustment for adjtime. Let the clock run up to 1% fast or slow.
  229. // That way, adjtime can adjust up to 36 seconds per hour, without time getting very jumpy.
  230. // Once we have a smarter NTP service that also adjusts the frequency instead of just slewing time, maybe we can lower this.
  231. constexpr long NanosPerTick = 1'000'000; // FIXME: Don't assume that one tick is 1 ms.
  232. constexpr time_t MaxSlewNanos = NanosPerTick / 100;
  233. static_assert(MaxSlewNanos < NanosPerTick);
  234. // Clamp twice, to make sure intermediate fits into a long.
  235. long slew_nanos = clamp(clamp(m_remaining_epoch_time_adjustment.tv_sec, (time_t)-1, (time_t)1) * 1'000'000'000 + m_remaining_epoch_time_adjustment.tv_nsec, -MaxSlewNanos, MaxSlewNanos);
  236. timespec slew_nanos_ts;
  237. timespec_sub({ 0, slew_nanos }, { 0, 0 }, slew_nanos_ts); // Normalize tv_nsec to be positive.
  238. timespec_sub(m_remaining_epoch_time_adjustment, slew_nanos_ts, m_remaining_epoch_time_adjustment);
  239. timespec epoch_tick = { .tv_sec = 0, .tv_nsec = NanosPerTick };
  240. epoch_tick.tv_nsec += slew_nanos; // No need for timespec_add(), guaranteed to be in range.
  241. timespec_add(m_epoch_time, epoch_tick, m_epoch_time);
  242. if (++m_ticks_this_second >= m_time_keeper_timer->ticks_per_second()) {
  243. // FIXME: Synchronize with other clock somehow to prevent drifting apart.
  244. ++m_seconds_since_boot;
  245. m_ticks_this_second = 0;
  246. }
  247. }
  248. }