mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:17:34 +00:00
Kernel: Fix issues supporting HPETs with 32-bit-only main counter
If the HPET main counter does not support full 64 bits, we should not expect the upper 32 bit to work. This is a problem when writing to the upper 32 bit of the comparator value, which requires the TimerConfiguration::ValueSet bit to be set, but if it's not 64 bit capable then the bit will not be cleared and leave it in a bad state. Fixes #6990
This commit is contained in:
parent
a6a57830d0
commit
3f9927b0c3
4 changed files with 51 additions and 18 deletions
|
@ -179,7 +179,8 @@ void HPET::update_periodic_comparator_value()
|
||||||
m_main_counter_drift += previous_main_value - m_main_counter_last_read;
|
m_main_counter_drift += previous_main_value - m_main_counter_last_read;
|
||||||
m_main_counter_last_read = 0;
|
m_main_counter_last_read = 0;
|
||||||
regs.main_counter_value.low = 0;
|
regs.main_counter_value.low = 0;
|
||||||
regs.main_counter_value.high = 0;
|
if (m_main_counter_64bits)
|
||||||
|
regs.main_counter_value.high = 0;
|
||||||
for (auto& comparator : m_comparators) {
|
for (auto& comparator : m_comparators) {
|
||||||
auto& timer = regs.timers[comparator.comparator_number()];
|
auto& timer = regs.timers[comparator.comparator_number()];
|
||||||
if (!comparator.is_enabled())
|
if (!comparator.is_enabled())
|
||||||
|
@ -195,8 +196,10 @@ void HPET::update_periodic_comparator_value()
|
||||||
value,
|
value,
|
||||||
previous_main_value);
|
previous_main_value);
|
||||||
timer.comparator_value.low = (u32)value;
|
timer.comparator_value.low = (u32)value;
|
||||||
timer.capabilities = timer.capabilities | (u32)HPETFlags::TimerConfiguration::ValueSet;
|
if (comparator.is_64bit_capable()) {
|
||||||
timer.comparator_value.high = (u32)(value >> 32);
|
timer.capabilities = timer.capabilities | (u32)HPETFlags::TimerConfiguration::ValueSet;
|
||||||
|
timer.comparator_value.high = (u32)(value >> 32);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Set the new target comparator value to the delta to the remaining ticks
|
// Set the new target comparator value to the delta to the remaining ticks
|
||||||
u64 current_value = (u64)timer.comparator_value.low | ((u64)timer.comparator_value.high << 32);
|
u64 current_value = (u64)timer.comparator_value.low | ((u64)timer.comparator_value.high << 32);
|
||||||
|
@ -207,7 +210,8 @@ void HPET::update_periodic_comparator_value()
|
||||||
value,
|
value,
|
||||||
previous_main_value);
|
previous_main_value);
|
||||||
timer.comparator_value.low = (u32)value;
|
timer.comparator_value.low = (u32)value;
|
||||||
timer.comparator_value.high = (u32)(value >> 32);
|
if (comparator.is_64bit_capable())
|
||||||
|
timer.comparator_value.high = (u32)(value >> 32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,10 +237,14 @@ u64 HPET::update_time(u64& seconds_since_boot, u32& ticks_this_second, bool quer
|
||||||
// Should only be called by the time keeper interrupt handler!
|
// Should only be called by the time keeper interrupt handler!
|
||||||
u64 current_value = read_main_counter();
|
u64 current_value = read_main_counter();
|
||||||
u64 delta_ticks = m_main_counter_drift;
|
u64 delta_ticks = m_main_counter_drift;
|
||||||
if (current_value >= m_main_counter_last_read)
|
if (current_value >= m_main_counter_last_read) {
|
||||||
delta_ticks += current_value - m_main_counter_last_read;
|
delta_ticks += current_value - m_main_counter_last_read;
|
||||||
else
|
} else {
|
||||||
delta_ticks += m_main_counter_last_read - current_value; // the counter wrapped around
|
// the counter wrapped around
|
||||||
|
delta_ticks += m_main_counter_last_read - current_value;
|
||||||
|
if (!m_main_counter_64bits)
|
||||||
|
m_32bit_main_counter_wraps++;
|
||||||
|
}
|
||||||
u64 ticks_since_last_second = (u64)ticks_this_second + delta_ticks;
|
u64 ticks_since_last_second = (u64)ticks_this_second + delta_ticks;
|
||||||
auto ticks_per_second = frequency();
|
auto ticks_per_second = frequency();
|
||||||
if (ticks_since_last_second >= ticks_per_second) {
|
if (ticks_since_last_second >= ticks_per_second) {
|
||||||
|
@ -258,12 +266,24 @@ u64 HPET::update_time(u64& seconds_since_boot, u32& ticks_this_second, bool quer
|
||||||
u64 HPET::read_main_counter_unsafe() const
|
u64 HPET::read_main_counter_unsafe() const
|
||||||
{
|
{
|
||||||
auto& main_counter = registers().main_counter_value;
|
auto& main_counter = registers().main_counter_value;
|
||||||
return ((u64)main_counter.high << 32) | (u64)main_counter.low;
|
if (m_main_counter_64bits)
|
||||||
|
return ((u64)main_counter.high << 32) | (u64)main_counter.low;
|
||||||
|
|
||||||
|
return ((u64)m_32bit_main_counter_wraps << 32) | (u64)main_counter.low;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 HPET::read_main_counter() const
|
u64 HPET::read_main_counter() const
|
||||||
{
|
{
|
||||||
return read_register_safe64(registers().main_counter_value);
|
if (m_main_counter_64bits)
|
||||||
|
return read_register_safe64(registers().main_counter_value);
|
||||||
|
|
||||||
|
auto& main_counter = registers().main_counter_value;
|
||||||
|
u32 wraps = m_32bit_main_counter_wraps;
|
||||||
|
u32 last_read_value = m_main_counter_last_read & 0xffffffff;
|
||||||
|
u32 current_value = main_counter.low;
|
||||||
|
if (current_value < last_read_value)
|
||||||
|
wraps++;
|
||||||
|
return ((u64)wraps << 32) | (u64)current_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HPET::enable_periodic_interrupt(const HPETComparator& comparator)
|
void HPET::enable_periodic_interrupt(const HPETComparator& comparator)
|
||||||
|
@ -348,6 +368,13 @@ bool HPET::is_periodic_capable(u8 comparator_number) const
|
||||||
return comparator_registers.capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable;
|
return comparator_registers.capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HPET::is_64bit_capable(u8 comparator_number) const
|
||||||
|
{
|
||||||
|
VERIFY(comparator_number <= m_comparators.size());
|
||||||
|
auto& comparator_registers = registers().timers[comparator_number];
|
||||||
|
return comparator_registers.capabilities & (u32)HPETFlags::TimerConfiguration::Timer64BitsCapable;
|
||||||
|
}
|
||||||
|
|
||||||
void HPET::set_comparators_to_optimal_interrupt_state(size_t)
|
void HPET::set_comparators_to_optimal_interrupt_state(size_t)
|
||||||
{
|
{
|
||||||
// FIXME: Implement this method for allowing to use HPET timers 2-31...
|
// FIXME: Implement this method for allowing to use HPET timers 2-31...
|
||||||
|
@ -398,8 +425,9 @@ UNMAP_AFTER_INIT HPET::HPET(PhysicalAddress acpi_hpet)
|
||||||
|
|
||||||
// Note: We must do a 32 bit access to offsets 0x0, or 0x4 only.
|
// Note: We must do a 32 bit access to offsets 0x0, or 0x4 only.
|
||||||
size_t timers_count = ((regs.capabilities.attributes >> 8) & 0x1f) + 1;
|
size_t timers_count = ((regs.capabilities.attributes >> 8) & 0x1f) + 1;
|
||||||
|
m_main_counter_64bits = (regs.capabilities.attributes & (u32)HPETFlags::Attributes::Counter64BitCapable) != 0;
|
||||||
dmesgln("HPET: Timers count - {}", timers_count);
|
dmesgln("HPET: Timers count - {}", timers_count);
|
||||||
dmesgln("HPET: Main counter size: {}", ((regs.capabilities.attributes & (u32)HPETFlags::Attributes::Counter64BitCapable) ? "64-bit" : "32-bit"));
|
dmesgln("HPET: Main counter size: {}", (m_main_counter_64bits ? "64-bit" : "32-bit"));
|
||||||
for (size_t i = 0; i < timers_count; i++) {
|
for (size_t i = 0; i < timers_count; i++) {
|
||||||
bool capable_64_bit = regs.timers[i].capabilities & (u32)HPETFlags::TimerConfiguration::Timer64BitsCapable;
|
bool capable_64_bit = regs.timers[i].capabilities & (u32)HPETFlags::TimerConfiguration::Timer64BitsCapable;
|
||||||
dmesgln("HPET: Timer[{}] comparator size: {}, mode: {}", i,
|
dmesgln("HPET: Timer[{}] comparator size: {}, mode: {}", i,
|
||||||
|
@ -421,8 +449,8 @@ UNMAP_AFTER_INIT HPET::HPET(PhysicalAddress acpi_hpet)
|
||||||
if (regs.capabilities.attributes & (u32)HPETFlags::Attributes::LegacyReplacementRouteCapable)
|
if (regs.capabilities.attributes & (u32)HPETFlags::Attributes::LegacyReplacementRouteCapable)
|
||||||
regs.configuration.low = regs.configuration.low | (u32)HPETFlags::Configuration::LegacyReplacementRoute;
|
regs.configuration.low = regs.configuration.low | (u32)HPETFlags::Configuration::LegacyReplacementRoute;
|
||||||
|
|
||||||
m_comparators.append(HPETComparator::create(0, 0, is_periodic_capable(0)));
|
m_comparators.append(HPETComparator::create(0, 0, is_periodic_capable(0), is_64bit_capable(0)));
|
||||||
m_comparators.append(HPETComparator::create(1, 8, is_periodic_capable(1)));
|
m_comparators.append(HPETComparator::create(1, 8, is_periodic_capable(1), is_64bit_capable(1)));
|
||||||
|
|
||||||
global_enable();
|
global_enable();
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ private:
|
||||||
void global_enable();
|
void global_enable();
|
||||||
|
|
||||||
bool is_periodic_capable(u8 comparator_number) const;
|
bool is_periodic_capable(u8 comparator_number) const;
|
||||||
|
bool is_64bit_capable(u8 comparator_number) const;
|
||||||
void set_comparators_to_optimal_interrupt_state(size_t timers_count);
|
void set_comparators_to_optimal_interrupt_state(size_t timers_count);
|
||||||
|
|
||||||
u64 nanoseconds_to_raw_ticks() const;
|
u64 nanoseconds_to_raw_ticks() const;
|
||||||
|
@ -68,12 +69,13 @@ private:
|
||||||
|
|
||||||
u64 m_main_counter_last_read { 0 };
|
u64 m_main_counter_last_read { 0 };
|
||||||
u64 m_main_counter_drift { 0 };
|
u64 m_main_counter_drift { 0 };
|
||||||
|
u32 m_32bit_main_counter_wraps { 0 };
|
||||||
|
|
||||||
u16 m_vendor_id;
|
u16 m_vendor_id;
|
||||||
u16 m_minimum_tick;
|
u16 m_minimum_tick;
|
||||||
u64 m_frequency;
|
u64 m_frequency;
|
||||||
u8 m_revision_id;
|
u8 m_revision_id;
|
||||||
bool counter_is_64_bit_capable : 1;
|
bool m_main_counter_64bits : 1;
|
||||||
bool legacy_replacement_route_capable : 1;
|
bool legacy_replacement_route_capable : 1;
|
||||||
|
|
||||||
NonnullRefPtrVector<HPETComparator> m_comparators;
|
NonnullRefPtrVector<HPETComparator> m_comparators;
|
||||||
|
|
|
@ -11,18 +11,19 @@
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
UNMAP_AFTER_INIT NonnullRefPtr<HPETComparator> HPETComparator::create(u8 number, u8 irq, bool periodic_capable)
|
UNMAP_AFTER_INIT NonnullRefPtr<HPETComparator> HPETComparator::create(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable)
|
||||||
{
|
{
|
||||||
auto timer = adopt_ref(*new HPETComparator(number, irq, periodic_capable));
|
auto timer = adopt_ref(*new HPETComparator(number, irq, periodic_capable, is_64bit_capable));
|
||||||
timer->register_interrupt_handler();
|
timer->register_interrupt_handler();
|
||||||
return timer;
|
return timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
UNMAP_AFTER_INIT HPETComparator::HPETComparator(u8 number, u8 irq, bool periodic_capable)
|
UNMAP_AFTER_INIT HPETComparator::HPETComparator(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable)
|
||||||
: HardwareTimer(irq)
|
: HardwareTimer(irq)
|
||||||
, m_periodic(false)
|
, m_periodic(false)
|
||||||
, m_periodic_capable(periodic_capable)
|
, m_periodic_capable(periodic_capable)
|
||||||
, m_enabled(false)
|
, m_enabled(false)
|
||||||
|
, m_is_64bit_capable(is_64bit_capable)
|
||||||
, m_comparator_number(number)
|
, m_comparator_number(number)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,14 @@ class HPETComparator final : public HardwareTimer<IRQHandler> {
|
||||||
friend class HPET;
|
friend class HPET;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static NonnullRefPtr<HPETComparator> create(u8 number, u8 irq, bool periodic_capable);
|
static NonnullRefPtr<HPETComparator> create(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable);
|
||||||
|
|
||||||
virtual HardwareTimerType timer_type() const override { return HardwareTimerType::HighPrecisionEventTimer; }
|
virtual HardwareTimerType timer_type() const override { return HardwareTimerType::HighPrecisionEventTimer; }
|
||||||
virtual const char* model() const override { return "HPET"; }
|
virtual const char* model() const override { return "HPET"; }
|
||||||
|
|
||||||
u8 comparator_number() const { return m_comparator_number; }
|
u8 comparator_number() const { return m_comparator_number; }
|
||||||
bool is_enabled() const { return m_enabled; }
|
bool is_enabled() const { return m_enabled; }
|
||||||
|
bool is_64bit_capable() const { return m_is_64bit_capable; }
|
||||||
|
|
||||||
virtual size_t ticks_per_second() const override;
|
virtual size_t ticks_per_second() const override;
|
||||||
|
|
||||||
|
@ -43,10 +44,11 @@ public:
|
||||||
private:
|
private:
|
||||||
void set_new_countdown();
|
void set_new_countdown();
|
||||||
virtual void handle_irq(const RegisterState&) override;
|
virtual void handle_irq(const RegisterState&) override;
|
||||||
HPETComparator(u8 number, u8 irq, bool periodic_capable);
|
HPETComparator(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable);
|
||||||
bool m_periodic : 1;
|
bool m_periodic : 1;
|
||||||
bool m_periodic_capable : 1;
|
bool m_periodic_capable : 1;
|
||||||
bool m_enabled : 1;
|
bool m_enabled : 1;
|
||||||
|
bool m_is_64bit_capable : 1;
|
||||||
u8 m_comparator_number { 0 };
|
u8 m_comparator_number { 0 };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue