Serenity Operating System
at hosted 360 lines 16 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 <AK/StringView.h> 28#include <Kernel/ACPI/Parser.h> 29#include <Kernel/Interrupts/InterruptManagement.h> 30#include <Kernel/Time/HPET.h> 31#include <Kernel/Time/HPETComparator.h> 32#include <Kernel/Time/TimeManagement.h> 33#include <Kernel/VM/MemoryManager.h> 34 35namespace Kernel { 36 37#define ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD 0x05F5E100 38#define NANOSECOND_PERIOD_TO_HERTZ(x) 1000000000 / x 39#define MEGAHERTZ_TO_HERTZ(x) (x / 1000000) 40 41//#define HPET_DEBUG 42 43namespace HPETFlags { 44enum class Attributes { 45 Counter64BitCapable = 1 << 13, 46 LegacyReplacementRouteCapable = 1 << 15 47}; 48 49enum class Configuration { 50 Enable = 0x1, 51 LegacyReplacementRoute = 0x2 52}; 53 54enum class TimerConfiguration : u32 { 55 InterruptType = 1 << 1, 56 InterruptEnable = 1 << 2, 57 TimerType = 1 << 3, 58 PeriodicInterruptCapable = 1 << 4, 59 Timer64BitsCapable = 1 << 5, 60 ValueSet = 1 << 6, 61 Force32BitMode = 1 << 7, 62 FSBInterruptEnable = 1 << 14, 63 FSBInterruptDelivery = 1 << 15 64}; 65}; 66 67struct [[gnu::packed]] TimerStructure 68{ 69 u64 configuration_and_capability; 70 u64 comparator_value; 71 u64 fsb_interrupt_route; 72 u64 reserved; 73}; 74 75struct [[gnu::packed]] HPETCapabilityRegister 76{ 77 u32 attributes; // Note: We must do a 32 bit access to offsets 0x0, or 0x4 only, according to HPET spec. 78 u32 main_counter_tick_period; 79 u64 reserved; 80}; 81 82struct [[gnu::packed]] HPETRegister 83{ 84 u64 reg; 85 u64 reserved; 86}; 87 88struct [[gnu::packed]] HPETRegistersBlock 89{ 90 union { 91 volatile HPETCapabilityRegister capabilities; 92 volatile HPETRegister raw_capabilites; 93 }; 94 HPETRegister configuration; 95 HPETRegister interrupt_status; 96 u8 reserved[0xF0 - 48]; 97 HPETRegister main_counter_value; 98 TimerStructure timers[3]; 99 u8 reserved2[0x400 - 0x160]; 100}; 101 102static HPET* s_hpet; 103static bool hpet_initialized { false }; 104 105bool HPET::initialized() 106{ 107 return hpet_initialized; 108} 109 110HPET& HPET::the() 111{ 112 ASSERT(HPET::initialized()); 113 ASSERT(s_hpet != nullptr); 114 return *s_hpet; 115} 116 117bool HPET::test_and_initialize() 118{ 119 ASSERT(!HPET::initialized()); 120 hpet_initialized = true; 121 auto hpet = ACPI::Parser::the()->find_table("HPET"); 122 if (hpet.is_null()) 123 return false; 124 klog() << "HPET @ " << hpet; 125 126 auto region = MM.allocate_kernel_region(hpet.page_base(), (PAGE_SIZE * 2), "HPET Initialization", Region::Access::Read); 127 auto* sdt = (volatile ACPI::Structures::HPET*)region->vaddr().offset(hpet.offset_in_page()).as_ptr(); 128 129 // Note: HPET is only usable from System Memory 130 ASSERT(sdt->event_timer_block.address_space == (u8)ACPI::GenericAddressStructure::AddressSpace::SystemMemory); 131 132 if (TimeManagement::is_hpet_periodic_mode_allowed()) { 133 if (!check_for_exisiting_periodic_timers()) { 134 dbg() << "HPET: No periodic capable timers"; 135 return false; 136 } 137 } 138 s_hpet = new HPET(PhysicalAddress(hpet)); 139 return true; 140} 141 142bool HPET::check_for_exisiting_periodic_timers() 143{ 144 auto hpet = ACPI::Parser::the()->find_table("HPET"); 145 if (hpet.is_null()) 146 return false; 147 auto region = MM.allocate_kernel_region(hpet.page_base(), (PAGE_SIZE * 2), "HPET Initialization", Region::Access::Read); 148 auto* sdt = (volatile ACPI::Structures::HPET*)region->vaddr().offset(hpet.offset_in_page()).as_ptr(); 149 150 ASSERT(sdt->event_timer_block.address_space == 0); 151 152 auto p_block = PhysicalAddress(sdt->event_timer_block.address); 153 auto block_region = MM.allocate_kernel_region(p_block, (PAGE_SIZE * 2), "HPET Initialization", Region::Access::Read); 154 auto* registers = (volatile HPETRegistersBlock*)block_region->vaddr().offset(p_block.offset_in_page()).as_ptr(); 155 156 size_t timers_count = ((registers->raw_capabilites.reg >> 8) & 0x1f) + 1; 157 for (size_t index = 0; index < timers_count; index++) { 158 if (registers->timers[index].configuration_and_capability & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable) 159 return true; 160 } 161 return false; 162} 163 164const FixedArray<RefPtr<HPETComparator>>& HPET::comparators() const 165{ 166 return m_comparators; 167} 168 169void HPET::global_disable() 170{ 171 auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); 172 registers_block->configuration.reg &= ~(u32)HPETFlags::Configuration::Enable; 173} 174void HPET::global_enable() 175{ 176 auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); 177 registers_block->configuration.reg |= (u32)HPETFlags::Configuration::Enable; 178} 179 180void HPET::set_periodic_comparator_value(const HPETComparator& comparator, u64 value) 181{ 182 disable(comparator); 183 ASSERT(comparator.is_periodic()); 184 ASSERT(comparator.comparator_number() <= m_comparators.size()); 185 auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); 186 registers_block->timers[comparator.comparator_number()].configuration_and_capability |= (u32)HPETFlags::TimerConfiguration::ValueSet; 187 registers_block->timers[comparator.comparator_number()].comparator_value = value; 188 enable(comparator); 189} 190 191void HPET::set_non_periodic_comparator_value(const HPETComparator& comparator, u64 value) 192{ 193 ASSERT_INTERRUPTS_DISABLED(); 194 ASSERT(!comparator.is_periodic()); 195 ASSERT(comparator.comparator_number() <= m_comparators.size()); 196 auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); 197 registers_block->timers[comparator.comparator_number()].comparator_value = main_counter_value() + value; 198} 199void HPET::enable_periodic_interrupt(const HPETComparator& comparator) 200{ 201#ifdef HPET_DEBUG 202 klog() << "HPET: Set comparator " << comparator.comparator_number() << " to be periodic."; 203#endif 204 disable(comparator); 205 ASSERT(comparator.comparator_number() <= m_comparators.size()); 206 auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); 207 ASSERT(registers_block->timers[comparator.comparator_number()].configuration_and_capability & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable); 208 registers_block->timers[comparator.comparator_number()].configuration_and_capability |= (u32)HPETFlags::TimerConfiguration::TimerType; 209 enable(comparator); 210} 211void HPET::disable_periodic_interrupt(const HPETComparator& comparator) 212{ 213#ifdef HPET_DEBUG 214 klog() << "HPET: Disable periodic interrupt in comparator " << comparator.comparator_number() << "."; 215#endif 216 disable(comparator); 217 ASSERT(comparator.comparator_number() <= m_comparators.size()); 218 auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); 219 ASSERT(registers_block->timers[comparator.comparator_number()].configuration_and_capability & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable); 220 registers_block->timers[comparator.comparator_number()].configuration_and_capability &= ~(u32)HPETFlags::TimerConfiguration::TimerType; 221 enable(comparator); 222} 223 224void HPET::disable(const HPETComparator& comparator) 225{ 226#ifdef HPET_DEBUG 227 klog() << "HPET: Disable comparator " << comparator.comparator_number() << "."; 228#endif 229 ASSERT(comparator.comparator_number() <= m_comparators.size()); 230 auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); 231 registers_block->timers[comparator.comparator_number()].configuration_and_capability &= ~(u32)HPETFlags::TimerConfiguration::InterruptEnable; 232} 233void HPET::enable(const HPETComparator& comparator) 234{ 235#ifdef HPET_DEBUG 236 klog() << "HPET: Enable comparator " << comparator.comparator_number() << "."; 237#endif 238 ASSERT(comparator.comparator_number() <= m_comparators.size()); 239 auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); 240 registers_block->timers[comparator.comparator_number()].configuration_and_capability |= (u32)HPETFlags::TimerConfiguration::InterruptEnable; 241} 242 243u64 HPET::main_counter_value() const 244{ 245 auto* registers_block = (const volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); 246 return registers_block->main_counter_value.reg; 247} 248u64 HPET::frequency() const 249{ 250 return m_frequency; 251} 252 253Vector<unsigned> HPET::capable_interrupt_numbers(const HPETComparator& comparator) 254{ 255 ASSERT(comparator.comparator_number() <= m_comparators.size()); 256 Vector<unsigned> capable_interrupts; 257 auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); 258 auto* comparator_registers = (const volatile TimerStructure*)&registers_block->timers[comparator.comparator_number()]; 259 u32 interrupt_bitfield = comparator_registers->configuration_and_capability >> 32; 260 for (size_t index = 0; index < 32; index++) { 261 if (interrupt_bitfield & 1) 262 capable_interrupts.append(index); 263 interrupt_bitfield >>= 1; 264 } 265 return capable_interrupts; 266} 267 268Vector<unsigned> HPET::capable_interrupt_numbers(u8 comparator_number) 269{ 270 ASSERT(comparator_number <= m_comparators.size()); 271 Vector<unsigned> capable_interrupts; 272 auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); 273 auto* comparator_registers = (const volatile TimerStructure*)&registers_block->timers[comparator_number]; 274 u32 interrupt_bitfield = comparator_registers->configuration_and_capability >> 32; 275 for (size_t index = 0; index < 32; index++) { 276 if (interrupt_bitfield & 1) 277 capable_interrupts.append(index); 278 interrupt_bitfield >>= 1; 279 } 280 return capable_interrupts; 281} 282 283void HPET::set_comparator_irq_vector(u8 comparator_number, u8 irq_vector) 284{ 285 ASSERT(comparator_number <= m_comparators.size()); 286 auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); 287 auto* comparator_registers = (volatile TimerStructure*)&registers_block->timers[comparator_number]; 288 comparator_registers->configuration_and_capability |= (irq_vector << 9); 289} 290 291bool HPET::is_periodic_capable(u8 comparator_number) const 292{ 293 ASSERT(comparator_number <= m_comparators.size()); 294 auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); 295 auto* comparator_registers = (const volatile TimerStructure*)&registers_block->timers[comparator_number]; 296 return comparator_registers->configuration_and_capability & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable; 297} 298 299void HPET::set_comparators_to_optimal_interrupt_state(size_t) 300{ 301 // FIXME: Implement this method for allowing to use HPET timers 2-31... 302 ASSERT_NOT_REACHED(); 303} 304 305PhysicalAddress HPET::find_acpi_hept_registers_block() 306{ 307 auto region = MM.allocate_kernel_region(m_physical_acpi_hpet_table.page_base(), (PAGE_SIZE * 2), "HPET Initialization", Region::Access::Read); 308 auto* sdt = (ACPI::Structures::HPET*)region->vaddr().offset(m_physical_acpi_hpet_table.offset_in_page()).as_ptr(); 309 ASSERT(sdt->event_timer_block.address_space == (u8)ACPI::GenericAddressStructure::AddressSpace::SystemMemory); 310 return PhysicalAddress(sdt->event_timer_block.address); 311} 312u64 HPET::calculate_ticks_in_nanoseconds() const 313{ 314 auto* registers_block = (const volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); 315 return ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD / registers_block->capabilities.main_counter_tick_period; 316} 317HPET::HPET(PhysicalAddress acpi_hpet) 318 : m_physical_acpi_hpet_table(acpi_hpet) 319 , m_physical_acpi_hpet_registers(find_acpi_hept_registers_block()) 320 , m_hpet_mmio_region(MM.allocate_kernel_region(m_physical_acpi_hpet_registers.page_base(), PAGE_SIZE, "HPET MMIO", Region::Access::Read | Region::Access::Write)) 321{ 322 auto region = MM.allocate_kernel_region(m_physical_acpi_hpet_table.page_base(), (PAGE_SIZE * 2), "HPET Initialization", Region::Access::Read); 323 auto* sdt = (volatile ACPI::Structures::HPET*)region->vaddr().offset(m_physical_acpi_hpet_table.offset_in_page()).as_ptr(); 324 m_vendor_id = sdt->pci_vendor_id; 325 m_minimum_tick = sdt->mininum_clock_tick; 326 klog() << "HPET: Minimum clock tick - " << m_minimum_tick; 327 328 auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); 329 330 // Note: We must do a 32 bit access to offsets 0x0, or 0x4 only. 331 size_t timers_count = ((registers_block->raw_capabilites.reg >> 8) & 0x1f) + 1; 332 klog() << "HPET: Timers count - " << timers_count; 333 ASSERT(timers_count >= 2); 334 m_comparators.resize(timers_count); 335 auto* capabilities_register = (const volatile HPETCapabilityRegister*)&registers_block->raw_capabilites.reg; 336 337 global_disable(); 338 339 m_frequency = NANOSECOND_PERIOD_TO_HERTZ(calculate_ticks_in_nanoseconds()); 340 klog() << "HPET: frequency " << m_frequency << " Hz (" << MEGAHERTZ_TO_HERTZ(m_frequency) << " MHz)"; 341 ASSERT(capabilities_register->main_counter_tick_period <= ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD); 342 343 // Reset the counter, just in case... 344 registers_block->main_counter_value.reg = 0; 345 if (registers_block->raw_capabilites.reg & (u32)HPETFlags::Attributes::LegacyReplacementRouteCapable) 346 registers_block->configuration.reg |= (u32)HPETFlags::Configuration::LegacyReplacementRoute; 347 348 for (size_t index = 0; index < m_comparators.size(); index++) { 349 bool periodic = is_periodic_capable(index); 350 if (index == 0) { 351 m_comparators[index] = HPETComparator::create(index, 0, periodic); 352 } 353 if (index == 1) { 354 m_comparators[index] = HPETComparator::create(index, 8, periodic); 355 } 356 } 357 358 global_enable(); 359} 360}