APICTimer.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /*
  2. * Copyright (c) 2020, the SerenityOS developers
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <Kernel/Arch/x86/CPU.h>
  7. #include <Kernel/IO.h>
  8. #include <Kernel/Interrupts/APIC.h>
  9. #include <Kernel/Panic.h>
  10. #include <Kernel/Scheduler.h>
  11. #include <Kernel/Thread.h>
  12. #include <Kernel/Time/APICTimer.h>
  13. #include <Kernel/Time/TimeManagement.h>
  14. namespace Kernel {
  15. #define APIC_TIMER_MEASURE_CPU_CLOCK
  16. UNMAP_AFTER_INIT APICTimer* APICTimer::initialize(u8 interrupt_number, HardwareTimerBase& calibration_source)
  17. {
  18. auto timer = adopt_ref(*new APICTimer(interrupt_number, nullptr));
  19. timer->register_interrupt_handler();
  20. if (!timer->calibrate(calibration_source)) {
  21. return nullptr;
  22. }
  23. return &timer.leak_ref();
  24. }
  25. UNMAP_AFTER_INIT APICTimer::APICTimer(u8 interrupt_number, Function<void(const RegisterState&)> callback)
  26. : HardwareTimer<GenericInterruptHandler>(interrupt_number, move(callback))
  27. {
  28. disable_remap();
  29. }
  30. UNMAP_AFTER_INIT bool APICTimer::calibrate(HardwareTimerBase& calibration_source)
  31. {
  32. VERIFY_INTERRUPTS_DISABLED();
  33. dmesgln("APICTimer: Using {} as calibration source", calibration_source.model());
  34. auto& apic = APIC::the();
  35. #ifdef APIC_TIMER_MEASURE_CPU_CLOCK
  36. bool supports_tsc = Processor::current().has_feature(CPUFeature::TSC);
  37. #endif
  38. // temporarily replace the timer callbacks
  39. const size_t ticks_in_100ms = calibration_source.ticks_per_second() / 10;
  40. Atomic<size_t, AK::memory_order_relaxed> calibration_ticks = 0;
  41. #ifdef APIC_TIMER_MEASURE_CPU_CLOCK
  42. volatile u64 start_tsc = 0, end_tsc = 0;
  43. #endif
  44. volatile u64 start_reference = 0, end_reference = 0;
  45. volatile u32 start_apic_count = 0, end_apic_count = 0;
  46. bool query_reference = calibration_source.can_query_raw();
  47. auto original_source_callback = calibration_source.set_callback([&](const RegisterState&) {
  48. u32 current_timer_count = apic.get_timer_current_count();
  49. #ifdef APIC_TIMER_MEASURE_CPU_CLOCK
  50. u64 current_tsc = supports_tsc ? read_tsc() : 0;
  51. #endif
  52. u64 current_reference = query_reference ? calibration_source.current_raw() : 0;
  53. auto prev_tick = calibration_ticks.fetch_add(1);
  54. if (prev_tick == 0) {
  55. #ifdef APIC_TIMER_MEASURE_CPU_CLOCK
  56. start_tsc = current_tsc;
  57. #endif
  58. start_apic_count = current_timer_count;
  59. start_reference = current_reference;
  60. } else if (prev_tick + 1 == ticks_in_100ms + 1) {
  61. #ifdef APIC_TIMER_MEASURE_CPU_CLOCK
  62. end_tsc = current_tsc;
  63. #endif
  64. end_apic_count = current_timer_count;
  65. end_reference = current_reference;
  66. }
  67. });
  68. // Setup a counter that should be much longer than our calibration time.
  69. // We don't want the APIC timer to actually fire. We do however want the
  70. // calbibration_source timer to fire so that we can read the current
  71. // tick count from the APIC timer
  72. auto original_callback = set_callback([&](const RegisterState&) {
  73. // TODO: How should we handle this?
  74. PANIC("APICTimer: Timer fired during calibration!");
  75. });
  76. apic.setup_local_timer(0xffffffff, APIC::TimerMode::Periodic, true);
  77. sti();
  78. // Loop for about 100 ms
  79. while (calibration_ticks.load() <= ticks_in_100ms)
  80. ;
  81. cli();
  82. // Restore timer callbacks
  83. calibration_source.set_callback(move(original_source_callback));
  84. set_callback(move(original_callback));
  85. disable_local_timer();
  86. if (query_reference) {
  87. u64 one_tick_ns = calibration_source.raw_to_ns((end_reference - start_reference) / ticks_in_100ms);
  88. m_frequency = (u32)(1000000000ull / one_tick_ns);
  89. dmesgln("APICTimer: Ticks per second: {} ({}.{}ms)", m_frequency, one_tick_ns / 1000000, one_tick_ns % 1000000);
  90. } else {
  91. // For now, assume the frequency is exactly the same
  92. m_frequency = calibration_source.ticks_per_second();
  93. dmesgln("APICTimer: Ticks per second: {} (assume same frequency as reference clock)", m_frequency);
  94. }
  95. auto delta_apic_count = start_apic_count - end_apic_count; // The APIC current count register decrements!
  96. m_timer_period = (delta_apic_count * apic.get_timer_divisor()) / ticks_in_100ms;
  97. u64 apic_freq = delta_apic_count * apic.get_timer_divisor() * 10;
  98. dmesgln("APICTimer: Bus clock speed: {}.{} MHz", apic_freq / 1000000, apic_freq % 1000000);
  99. if (apic_freq < 1000000) {
  100. dmesgln("APICTimer: Frequency too slow!");
  101. return false;
  102. }
  103. #ifdef APIC_TIMER_MEASURE_CPU_CLOCK
  104. if (supports_tsc) {
  105. auto delta_tsc = (end_tsc - start_tsc) * 10;
  106. dmesgln("APICTimer: CPU clock speed: {}.{} MHz", delta_tsc / 1000000, delta_tsc % 1000000);
  107. }
  108. #endif
  109. enable_local_timer();
  110. return true;
  111. }
  112. void APICTimer::enable_local_timer()
  113. {
  114. APIC::the().setup_local_timer(m_timer_period, m_timer_mode, true);
  115. }
  116. void APICTimer::disable_local_timer()
  117. {
  118. APIC::the().setup_local_timer(0, APIC::TimerMode::OneShot, false);
  119. }
  120. size_t APICTimer::ticks_per_second() const
  121. {
  122. return m_frequency;
  123. }
  124. void APICTimer::set_periodic()
  125. {
  126. // FIXME: Implement it...
  127. VERIFY_NOT_REACHED();
  128. }
  129. void APICTimer::set_non_periodic()
  130. {
  131. // FIXME: Implement it...
  132. VERIFY_NOT_REACHED();
  133. }
  134. void APICTimer::reset_to_default_ticks_per_second()
  135. {
  136. }
  137. bool APICTimer::try_to_set_frequency([[maybe_unused]] size_t frequency)
  138. {
  139. return true;
  140. }
  141. bool APICTimer::is_capable_of_frequency([[maybe_unused]] size_t frequency) const
  142. {
  143. return false;
  144. }
  145. size_t APICTimer::calculate_nearest_possible_frequency([[maybe_unused]] size_t frequency) const
  146. {
  147. return 0;
  148. }
  149. }