Serenity Operating System
at hosted 264 lines 8.8 kB view raw
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 27#include <Kernel/ACPI/Parser.h> 28#include <Kernel/CommandLine.h> 29#include <Kernel/Scheduler.h> 30#include <Kernel/Time/HPET.h> 31#include <Kernel/Time/HPETComparator.h> 32#include <Kernel/Time/HardwareTimer.h> 33#include <Kernel/Time/PIT.h> 34#include <Kernel/Time/RTC.h> 35#include <Kernel/Time/TimeManagement.h> 36#include <Kernel/VM/MemoryManager.h> 37 38//#define TIME_DEBUG 39 40namespace Kernel { 41 42static TimeManagement* s_time_management; 43 44bool TimeManagement::initialized() 45{ 46 return s_time_management != nullptr; 47} 48 49bool TimeManagement::is_system_timer(const HardwareTimer& timer) const 50{ 51 return &timer == m_system_timer.ptr(); 52} 53 54void TimeManagement::set_epoch_time(time_t value) 55{ 56 InterruptDisabler disabler; 57 m_epoch_time = value; 58} 59 60time_t TimeManagement::epoch_time() const 61{ 62 return m_epoch_time; 63} 64 65void TimeManagement::initialize() 66{ 67 ASSERT(!TimeManagement::initialized()); 68 if (kernel_command_line().lookup("time").value_or("modern") == "legacy") 69 s_time_management = new TimeManagement(false); 70 else 71 s_time_management = new TimeManagement(true); 72} 73time_t TimeManagement::seconds_since_boot() const 74{ 75 return m_seconds_since_boot; 76} 77time_t TimeManagement::ticks_per_second() const 78{ 79 return m_system_timer->ticks_per_second(); 80} 81 82time_t TimeManagement::ticks_this_second() const 83{ 84 return m_ticks_this_second; 85} 86 87time_t TimeManagement::boot_time() const 88{ 89 return RTC::boot_time(); 90} 91 92void TimeManagement::stale_function(const RegisterState&) 93{ 94} 95 96TimeManagement::TimeManagement(bool probe_non_legacy_hardware_timers) 97{ 98 if (ACPI::is_enabled()) { 99 if (!ACPI::Parser::the()->x86_specific_flags().cmos_rtc_not_present) { 100 RTC::initialize(); 101 m_epoch_time += boot_time(); 102 } else { 103 klog() << "ACPI: RTC CMOS Not present"; 104 } 105 } else { 106 // We just assume that we can access RTC CMOS, if ACPI isn't usable. 107 RTC::initialize(); 108 m_epoch_time += boot_time(); 109 } 110 if (probe_non_legacy_hardware_timers) { 111 if (!probe_and_set_non_legacy_hardware_timers()) 112 if (!probe_and_set_legacy_hardware_timers()) 113 ASSERT_NOT_REACHED(); 114 return; 115 } 116 if (probe_and_set_legacy_hardware_timers()) 117 return; 118 ASSERT_NOT_REACHED(); 119} 120 121Vector<size_t> TimeManagement::scan_and_initialize_periodic_timers() 122{ 123 bool enable_periodic_mode = is_hpet_periodic_mode_allowed(); 124 dbg() << "Scanning for Periodic timers"; 125 Vector<size_t> periodic_timers_indexes; 126 periodic_timers_indexes.ensure_capacity(m_hardware_timers.size()); 127 for (size_t index = 0; index < m_hardware_timers.size(); index++) { 128 if (!m_hardware_timers[index].is_null()) { 129 if (m_hardware_timers[index]->is_periodic_capable()) { 130 periodic_timers_indexes.append(index); 131 if (enable_periodic_mode) 132 m_hardware_timers[index]->set_periodic(); 133 } 134 } 135 } 136 return periodic_timers_indexes; 137} 138 139Vector<size_t> TimeManagement::scan_for_non_periodic_timers() 140{ 141 dbg() << "Scanning for Non-Periodic timers"; 142 Vector<size_t> non_periodic_timers_indexes; 143 non_periodic_timers_indexes.ensure_capacity(m_hardware_timers.size()); 144 for (size_t index = 0; index < m_hardware_timers.size(); index++) { 145 if (!m_hardware_timers[index].is_null()) 146 if (!m_hardware_timers[index]->is_periodic_capable()) 147 non_periodic_timers_indexes.append(index); 148 } 149 return non_periodic_timers_indexes; 150} 151 152bool TimeManagement::is_hpet_periodic_mode_allowed() 153{ 154 if (!kernel_command_line().contains("hpet")) 155 return true; 156 157 auto hpet_mode = kernel_command_line().get("hpet"); 158 if (hpet_mode == "periodic") 159 return true; 160 if (hpet_mode == "nonperiodic") 161 return false; 162 ASSERT_NOT_REACHED(); 163} 164 165bool TimeManagement::probe_and_set_non_legacy_hardware_timers() 166{ 167 if (!ACPI::is_enabled()) 168 return false; 169 if (!HPET::test_and_initialize()) 170 return false; 171 if (!HPET::the().comparators().size()) { 172 dbg() << "HPET initialization aborted."; 173 return false; 174 } 175 dbg() << "HPET: Setting appropriate functions to timers."; 176 177 m_hardware_timers.resize(HPET::the().comparators().size()); 178 for (size_t index = 0; index < m_hardware_timers.size(); index++) { 179 m_hardware_timers[index] = HPET::the().comparators()[index]; 180#ifdef TIME_DEBUG 181 dbg() << m_hardware_timers[index].ptr() << " <- " << HPET::the().comparators()[index].ptr(); 182#endif 183 } 184 185 auto periodic_timer_indexes = scan_and_initialize_periodic_timers(); 186 auto non_periodic_timer_indexes = scan_for_non_periodic_timers(); 187 188 if (is_hpet_periodic_mode_allowed()) 189 ASSERT(!periodic_timer_indexes.is_empty()); 190 191 ASSERT(periodic_timer_indexes.size() + non_periodic_timer_indexes.size() >= 2); 192 193 if (periodic_timer_indexes.size() >= 2) { 194 m_time_keeper_timer = m_hardware_timers[periodic_timer_indexes[1]]; 195 m_system_timer = m_hardware_timers[periodic_timer_indexes[0]]; 196 } else { 197 if (periodic_timer_indexes.size() == 1) { 198 m_time_keeper_timer = m_hardware_timers[periodic_timer_indexes[0]]; 199 m_system_timer = m_hardware_timers[non_periodic_timer_indexes[0]]; 200 } else { 201 m_time_keeper_timer = m_hardware_timers[non_periodic_timer_indexes[1]]; 202 m_system_timer = m_hardware_timers[non_periodic_timer_indexes[0]]; 203 } 204 } 205 206 m_system_timer->change_function([](const RegisterState& regs) { update_scheduler_ticks(regs); }); 207 dbg() << "Reset timers"; 208 m_system_timer->try_to_set_frequency(m_system_timer->calculate_nearest_possible_frequency(1024)); 209 m_time_keeper_timer->change_function([](const RegisterState& regs) { update_time(regs); }); 210 m_time_keeper_timer->try_to_set_frequency(OPTIMAL_TICKS_PER_SECOND_RATE); 211 212 return true; 213} 214 215bool TimeManagement::probe_and_set_legacy_hardware_timers() 216{ 217 if (ACPI::is_enabled()) { 218 if (ACPI::Parser::the()->x86_specific_flags().cmos_rtc_not_present) { 219 dbg() << "ACPI: CMOS RTC Not Present"; 220 return false; 221 } else { 222 dbg() << "ACPI: CMOS RTC Present"; 223 } 224 } 225 226 m_hardware_timers[0] = PIT::initialize([](const RegisterState& regs) { update_time(regs); }); 227 m_hardware_timers[1] = RealTimeClock::create([](const RegisterState& regs) { update_scheduler_ticks(regs); }); 228 m_time_keeper_timer = m_hardware_timers[0]; 229 m_system_timer = m_hardware_timers[1]; 230 return true; 231} 232 233TimeManagement& TimeManagement::the() 234{ 235 ASSERT(TimeManagement::initialized()); 236 return *s_time_management; 237} 238 239void TimeManagement::update_time(const RegisterState& regs) 240{ 241 TimeManagement::the().increment_time_since_boot(regs); 242} 243 244void TimeManagement::increment_time_since_boot(const RegisterState&) 245{ 246 ASSERT(!m_time_keeper_timer.is_null()); 247 if (++m_ticks_this_second >= m_time_keeper_timer->ticks_per_second()) { 248 // FIXME: Synchronize with other clock somehow to prevent drifting apart. 249 ++m_seconds_since_boot; 250 ++m_epoch_time; 251 m_ticks_this_second = 0; 252 } 253} 254 255void TimeManagement::update_scheduler_ticks(const RegisterState& regs) 256{ 257 TimeManagement::the().update_ticks(regs); 258} 259 260void TimeManagement::update_ticks(const RegisterState& regs) 261{ 262 Scheduler::timer_tick(regs); 263} 264}