mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-09 20:51:16 +00:00
hw: calibrate Local APIC via ACPI timer
Upto now, bootstrap used the Programmable Interval Timer to set a suitable divider and determine the frequency of the Local APIC. The PIT is not available on recent x86_64 hardware anymore. Move Local APIC calibration to bootstrap and use the ACPI timer as a reference. Clean up hw's timer implementation a little and disable the PIT in bootstrap. Fixes #5215
This commit is contained in:
parent
89f7834b17
commit
ec5e1a6b4b
@ -11,13 +11,15 @@
|
|||||||
* under the terms of the GNU Affero General Public License version 3.
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _CORE__SPEC__X86_64__PORT_IO_H_
|
#ifndef _INCLUDE__SPEC__X86_64__PORT_IO_H_
|
||||||
#define _CORE__SPEC__X86_64__PORT_IO_H_
|
#define _INCLUDE__SPEC__X86_64__PORT_IO_H_
|
||||||
|
|
||||||
/* core includes */
|
#include <base/fixed_stdint.h>
|
||||||
#include <types.h>
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Hw {
|
||||||
|
|
||||||
|
using Genode::uint8_t;
|
||||||
|
using Genode::uint16_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read byte from I/O port
|
* Read byte from I/O port
|
||||||
@ -38,4 +40,4 @@ namespace Core {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _CORE__SPEC__X86_64__PORT_IO_H_ */
|
#endif /* _INCLUDE__SPEC__X86_64__PORT_IO_H_ */
|
@ -24,7 +24,7 @@ SRC_CC += spec/x86_64/virtualization/kernel/svm.cc
|
|||||||
SRC_CC += spec/x86_64/virtualization/kernel/vmx.cc
|
SRC_CC += spec/x86_64/virtualization/kernel/vmx.cc
|
||||||
SRC_CC += kernel/lock.cc
|
SRC_CC += kernel/lock.cc
|
||||||
SRC_CC += spec/x86_64/pic.cc
|
SRC_CC += spec/x86_64/pic.cc
|
||||||
SRC_CC += spec/x86_64/pit.cc
|
SRC_CC += spec/x86_64/timer.cc
|
||||||
SRC_CC += spec/x86_64/kernel/thread_exception.cc
|
SRC_CC += spec/x86_64/kernel/thread_exception.cc
|
||||||
SRC_CC += spec/x86_64/platform_support.cc
|
SRC_CC += spec/x86_64/platform_support.cc
|
||||||
SRC_CC += spec/x86_64/virtualization/platform_services.cc
|
SRC_CC += spec/x86_64/virtualization/platform_services.cc
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <platform.h>
|
#include <platform.h>
|
||||||
#include <multiboot.h>
|
#include <multiboot.h>
|
||||||
#include <multiboot2.h>
|
#include <multiboot2.h>
|
||||||
|
#include <port_io.h>
|
||||||
|
|
||||||
#include <hw/memory_consts.h>
|
#include <hw/memory_consts.h>
|
||||||
#include <hw/spec/x86_64/acpi.h>
|
#include <hw/spec/x86_64/acpi.h>
|
||||||
@ -91,6 +92,55 @@ static uint32_t calibrate_tsc_frequency(addr_t fadt_addr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Hw::Local_apic::Calibration calibrate_lapic_frequency(addr_t fadt_addr)
|
||||||
|
{
|
||||||
|
uint32_t const default_freq = TIMER_MIN_TICKS_PER_MS;
|
||||||
|
|
||||||
|
if (!fadt_addr) {
|
||||||
|
warning("FADT not found, setting minimum Local APIC frequency of ", default_freq, "kHz");
|
||||||
|
return { default_freq, 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t const sleep_ms = 10;
|
||||||
|
|
||||||
|
Hw::Acpi_fadt fadt(reinterpret_cast<Hw::Acpi_generic *>(fadt_addr));
|
||||||
|
|
||||||
|
Hw::Local_apic lapic(Hw::Cpu_memory_map::lapic_phys_base());
|
||||||
|
|
||||||
|
auto const result =
|
||||||
|
lapic.calibrate_divider([&] {
|
||||||
|
return fadt.calibrate_freq_khz(sleep_ms, [&] {
|
||||||
|
return lapic.read<Hw::Local_apic::Tmr_current>(); }, true); });
|
||||||
|
|
||||||
|
if (!result.freq_khz) {
|
||||||
|
warning("FADT not found, setting minimum Local APIC frequency of ", default_freq, "kHz");
|
||||||
|
return { default_freq, 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void disable_pit()
|
||||||
|
{
|
||||||
|
using Hw::outb;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
/* PIT constants */
|
||||||
|
PIT_CH0_DATA = 0x40,
|
||||||
|
PIT_MODE = 0x43,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable PIT timer channel. This is necessary since BIOS sets up
|
||||||
|
* channel 0 to fire periodically.
|
||||||
|
*/
|
||||||
|
outb(PIT_MODE, 0x30);
|
||||||
|
outb(PIT_CH0_DATA, 0);
|
||||||
|
outb(PIT_CH0_DATA, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Bootstrap::Platform::Board::Board()
|
Bootstrap::Platform::Board::Board()
|
||||||
:
|
:
|
||||||
core_mmio(Memory_region { 0, 0x1000 },
|
core_mmio(Memory_region { 0, 0x1000 },
|
||||||
@ -275,7 +325,12 @@ Bootstrap::Platform::Board::Board()
|
|||||||
cpus = !cpus ? 1 : max_cpus;
|
cpus = !cpus ? 1 : max_cpus;
|
||||||
}
|
}
|
||||||
|
|
||||||
info.tsc_freq_khz = calibrate_tsc_frequency(info.acpi_fadt);
|
auto r = calibrate_lapic_frequency(info.acpi_fadt);
|
||||||
|
info.lapic_freq_khz = r.freq_khz;
|
||||||
|
info.lapic_div = r.div;
|
||||||
|
info.tsc_freq_khz = calibrate_tsc_frequency(info.acpi_fadt);
|
||||||
|
|
||||||
|
disable_pit();
|
||||||
|
|
||||||
/* copy 16 bit boot code for AP CPUs and for ACPI resume */
|
/* copy 16 bit boot code for AP CPUs and for ACPI resume */
|
||||||
addr_t ap_code_size = (addr_t)&_start - (addr_t)&_ap;
|
addr_t ap_code_size = (addr_t)&_start - (addr_t)&_ap;
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
/* base-hw core includes */
|
/* base-hw core includes */
|
||||||
#include <spec/x86_64/pic.h>
|
#include <spec/x86_64/pic.h>
|
||||||
#include <spec/x86_64/pit.h>
|
#include <spec/x86_64/timer.h>
|
||||||
#include <spec/x86_64/cpu.h>
|
#include <spec/x86_64/cpu.h>
|
||||||
|
|
||||||
namespace Board {
|
namespace Board {
|
||||||
|
@ -47,6 +47,8 @@ Local_interrupt_controller(Global_interrupt_controller &global_irq_ctrl)
|
|||||||
|
|
||||||
void Local_interrupt_controller::init()
|
void Local_interrupt_controller::init()
|
||||||
{
|
{
|
||||||
|
using Hw::outb;
|
||||||
|
|
||||||
/* Start initialization sequence in cascade mode */
|
/* Start initialization sequence in cascade mode */
|
||||||
outb(PIC_CMD_MASTER, 0x11);
|
outb(PIC_CMD_MASTER, 0x11);
|
||||||
outb(PIC_CMD_SLAVE, 0x11);
|
outb(PIC_CMD_SLAVE, 0x11);
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief Timer driver for core
|
|
||||||
* \author Adrian-Ken Rueegsegger
|
|
||||||
* \author Reto Buerki
|
|
||||||
* \date 2015-02-06
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SRC__CORE__SPEC__ARM__PIT_H_
|
|
||||||
#define _SRC__CORE__SPEC__ARM__PIT_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <util/mmio.h>
|
|
||||||
#include <base/stdint.h>
|
|
||||||
|
|
||||||
/* core includes */
|
|
||||||
#include <port_io.h>
|
|
||||||
|
|
||||||
namespace Board { class Timer; }
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LAPIC-based timer driver for core
|
|
||||||
*/
|
|
||||||
struct Board::Timer: Genode::Mmio<Hw::Cpu_memory_map::LAPIC_SIZE>
|
|
||||||
{
|
|
||||||
enum {
|
|
||||||
/* PIT constants */
|
|
||||||
PIT_TICK_RATE = 1193182ul,
|
|
||||||
PIT_SLEEP_MS = 50,
|
|
||||||
PIT_SLEEP_TICS = (PIT_TICK_RATE / 1000) * PIT_SLEEP_MS,
|
|
||||||
PIT_CH0_DATA = 0x40,
|
|
||||||
PIT_CH2_DATA = 0x42,
|
|
||||||
PIT_CH2_GATE = 0x61,
|
|
||||||
PIT_MODE = 0x43,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Timer registers */
|
|
||||||
struct Tmr_lvt : Register<0x320, 32>
|
|
||||||
{
|
|
||||||
struct Vector : Bitfield<0, 8> { };
|
|
||||||
struct Delivery : Bitfield<8, 3> { };
|
|
||||||
struct Mask : Bitfield<16, 1> { };
|
|
||||||
struct Timer_mode : Bitfield<17, 2> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Tmr_initial : Register <0x380, 32> { };
|
|
||||||
struct Tmr_current : Register <0x390, 32> { };
|
|
||||||
|
|
||||||
struct Divide_configuration : Register <0x03e0, 32>
|
|
||||||
{
|
|
||||||
struct Divide_value_0_2 : Bitfield<0, 2> { };
|
|
||||||
struct Divide_value_2_1 : Bitfield<3, 1> { };
|
|
||||||
struct Divide_value :
|
|
||||||
Genode::Bitset_2<Divide_value_0_2, Divide_value_2_1>
|
|
||||||
{
|
|
||||||
enum { MAX = 6 };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Calibration_failed : Genode::Exception { };
|
|
||||||
|
|
||||||
Divide_configuration::access_t divider = 0;
|
|
||||||
Genode::uint32_t ticks_per_ms = 0;
|
|
||||||
|
|
||||||
/* Measure LAPIC timer frequency using PIT channel 2 */
|
|
||||||
Genode::uint32_t pit_calc_timer_freq(void);
|
|
||||||
|
|
||||||
Timer(unsigned);
|
|
||||||
|
|
||||||
void init();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _SRC__CORE__SPEC__ARM__PIT_H_ */
|
|
@ -15,9 +15,6 @@
|
|||||||
|
|
||||||
#include <hw/spec/x86_64/x86_64.h>
|
#include <hw/spec/x86_64/x86_64.h>
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <drivers/timer/util.h>
|
|
||||||
|
|
||||||
/* core includes */
|
/* core includes */
|
||||||
#include <kernel/timer.h>
|
#include <kernel/timer.h>
|
||||||
#include <platform.h>
|
#include <platform.h>
|
||||||
@ -25,37 +22,9 @@
|
|||||||
using namespace Core;
|
using namespace Core;
|
||||||
using namespace Kernel;
|
using namespace Kernel;
|
||||||
|
|
||||||
|
|
||||||
uint32_t Board::Timer::pit_calc_timer_freq(void)
|
|
||||||
{
|
|
||||||
uint32_t t_start, t_end;
|
|
||||||
|
|
||||||
/* set channel gate high and disable speaker */
|
|
||||||
outb(PIT_CH2_GATE, (uint8_t)((inb(0x61) & ~0x02) | 0x01));
|
|
||||||
|
|
||||||
/* set timer counter (mode 0, binary count) */
|
|
||||||
outb(PIT_MODE, 0xb0);
|
|
||||||
outb(PIT_CH2_DATA, PIT_SLEEP_TICS & 0xff);
|
|
||||||
outb(PIT_CH2_DATA, PIT_SLEEP_TICS >> 8);
|
|
||||||
|
|
||||||
write<Tmr_initial>(~0U);
|
|
||||||
|
|
||||||
t_start = read<Tmr_current>();
|
|
||||||
while ((inb(PIT_CH2_GATE) & 0x20) == 0)
|
|
||||||
{
|
|
||||||
asm volatile("pause" : : : "memory");
|
|
||||||
}
|
|
||||||
t_end = read<Tmr_current>();
|
|
||||||
|
|
||||||
write<Tmr_initial>(0);
|
|
||||||
|
|
||||||
return (t_start - t_end) / PIT_SLEEP_MS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Board::Timer::Timer(unsigned)
|
Board::Timer::Timer(unsigned)
|
||||||
:
|
:
|
||||||
Mmio({(char *)Platform::mmio_to_virt(Hw::Cpu_memory_map::lapic_phys_base()), Mmio::SIZE})
|
Local_apic(Platform::mmio_to_virt(Hw::Cpu_memory_map::lapic_phys_base()))
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
@ -75,28 +44,10 @@ void Board::Timer::init()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* calibrate LAPIC frequency to fullfill our requirements */
|
Platform::apply_with_boot_info([&](auto const &boot_info) {
|
||||||
for (Divide_configuration::access_t div = Divide_configuration::Divide_value::MAX;
|
ticks_per_ms = boot_info.plat_info.lapic_freq_khz;
|
||||||
div && ticks_per_ms < TIMER_MIN_TICKS_PER_MS; div--)
|
divider = boot_info.plat_info.lapic_div;
|
||||||
{
|
});
|
||||||
if (!div){
|
|
||||||
raw("Failed to calibrate timer frequency");
|
|
||||||
throw Calibration_failed();
|
|
||||||
}
|
|
||||||
write<Divide_configuration::Divide_value>((uint8_t)div);
|
|
||||||
|
|
||||||
/* Calculate timer frequency */
|
|
||||||
ticks_per_ms = pit_calc_timer_freq();
|
|
||||||
divider = div;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable PIT timer channel. This is necessary since BIOS sets up
|
|
||||||
* channel 0 to fire periodically.
|
|
||||||
*/
|
|
||||||
outb(Board::Timer::PIT_MODE, 0x30);
|
|
||||||
outb(Board::Timer::PIT_CH0_DATA, 0);
|
|
||||||
outb(Board::Timer::PIT_CH0_DATA, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
40
repos/base-hw/src/core/spec/x86_64/timer.h
Normal file
40
repos/base-hw/src/core/spec/x86_64/timer.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* \brief Timer driver for core
|
||||||
|
* \author Adrian-Ken Rueegsegger
|
||||||
|
* \author Reto Buerki
|
||||||
|
* \date 2015-02-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SRC__CORE__SPEC__ARM__PIT_H_
|
||||||
|
#define _SRC__CORE__SPEC__ARM__PIT_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <base/stdint.h>
|
||||||
|
|
||||||
|
/* hw includes */
|
||||||
|
#include <hw/spec/x86_64/apic.h>
|
||||||
|
|
||||||
|
namespace Board { class Timer; }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LAPIC-based timer driver for core
|
||||||
|
*/
|
||||||
|
struct Board::Timer: public Hw::Local_apic
|
||||||
|
{
|
||||||
|
Divide_configuration::access_t divider = 0;
|
||||||
|
Genode::uint32_t ticks_per_ms = 0;
|
||||||
|
|
||||||
|
Timer(unsigned);
|
||||||
|
|
||||||
|
void init();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _SRC__CORE__SPEC__ARM__PIT_H_ */
|
@ -264,7 +264,7 @@ struct Hw::Acpi_fadt : Genode::Mmio<276>
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t calibrate_freq_khz(uint32_t sleep_ms, auto get_value_fn)
|
uint32_t calibrate_freq_khz(uint32_t sleep_ms, auto get_value_fn, bool reverse = false)
|
||||||
{
|
{
|
||||||
unsigned const acpi_timer_freq = 3'579'545;
|
unsigned const acpi_timer_freq = 3'579'545;
|
||||||
|
|
||||||
@ -277,7 +277,7 @@ struct Hw::Acpi_fadt : Genode::Mmio<276>
|
|||||||
asm volatile ("pause":::"memory");
|
asm volatile ("pause":::"memory");
|
||||||
uint64_t const t2 = get_value_fn();
|
uint64_t const t2 = get_value_fn();
|
||||||
|
|
||||||
return (uint32_t)((t2 - t1) / sleep_ms);
|
return (uint32_t)((reverse ? (t1 - t2) : (t2 - t1)) / sleep_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_cnt_blk(unsigned value_a, unsigned value_b)
|
void write_cnt_blk(unsigned value_a, unsigned value_b)
|
||||||
|
@ -18,6 +18,9 @@ namespace Hw { class Local_apic; }
|
|||||||
|
|
||||||
#include <hw/spec/x86_64/x86_64.h>
|
#include <hw/spec/x86_64/x86_64.h>
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <drivers/timer/util.h>
|
||||||
|
|
||||||
struct Hw::Local_apic : Genode::Mmio<Hw::Cpu_memory_map::LAPIC_SIZE>
|
struct Hw::Local_apic : Genode::Mmio<Hw::Cpu_memory_map::LAPIC_SIZE>
|
||||||
{
|
{
|
||||||
struct Id : Register<0x020, 32> { };
|
struct Id : Register<0x020, 32> { };
|
||||||
@ -58,6 +61,57 @@ struct Hw::Local_apic : Genode::Mmio<Hw::Cpu_memory_map::LAPIC_SIZE>
|
|||||||
struct Destination : Bitfield<24, 8> { };
|
struct Destination : Bitfield<24, 8> { };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Timer registers */
|
||||||
|
struct Tmr_lvt : Register<0x320, 32>
|
||||||
|
{
|
||||||
|
struct Vector : Bitfield<0, 8> { };
|
||||||
|
struct Delivery : Bitfield<8, 3> { };
|
||||||
|
struct Mask : Bitfield<16, 1> { };
|
||||||
|
struct Timer_mode : Bitfield<17, 2> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Tmr_initial : Register <0x380, 32> { };
|
||||||
|
struct Tmr_current : Register <0x390, 32> { };
|
||||||
|
|
||||||
|
struct Divide_configuration : Register <0x03e0, 32>
|
||||||
|
{
|
||||||
|
struct Divide_value_0_2 : Bitfield<0, 2> { };
|
||||||
|
struct Divide_value_2_1 : Bitfield<3, 1> { };
|
||||||
|
struct Divide_value :
|
||||||
|
Genode::Bitset_2<Divide_value_0_2, Divide_value_2_1>
|
||||||
|
{
|
||||||
|
enum { MAX = 6 };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Calibration { uint32_t freq_khz; uint32_t div; };
|
||||||
|
|
||||||
|
Calibration calibrate_divider(auto calibration_fn)
|
||||||
|
{
|
||||||
|
Calibration result { };
|
||||||
|
|
||||||
|
/* calibrate LAPIC frequency to fullfill our requirements */
|
||||||
|
for (Divide_configuration::access_t div = Divide_configuration::Divide_value::MAX;
|
||||||
|
div && result.freq_khz < TIMER_MIN_TICKS_PER_MS; div--) {
|
||||||
|
|
||||||
|
if (!div) {
|
||||||
|
raw("Failed to calibrate Local APIC frequency");
|
||||||
|
return { 0, 1 };
|
||||||
|
}
|
||||||
|
write<Divide_configuration::Divide_value>((uint8_t)div);
|
||||||
|
|
||||||
|
write<Tmr_initial>(~0U);
|
||||||
|
|
||||||
|
/* Calculate timer frequency */
|
||||||
|
result.freq_khz = calibration_fn();
|
||||||
|
result.div = div;
|
||||||
|
|
||||||
|
write<Tmr_initial>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Local_apic(addr_t const addr) : Mmio({(char*)addr, Mmio::SIZE}) {}
|
Local_apic(addr_t const addr) : Mmio({(char*)addr, Mmio::SIZE}) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,6 +45,8 @@ struct Hw::Pc_board::Boot_info
|
|||||||
Genode::addr_t efi_system_table { 0 };
|
Genode::addr_t efi_system_table { 0 };
|
||||||
Genode::addr_t acpi_fadt { 0 };
|
Genode::addr_t acpi_fadt { 0 };
|
||||||
Genode::uint32_t tsc_freq_khz { 0 };
|
Genode::uint32_t tsc_freq_khz { 0 };
|
||||||
|
Genode::uint32_t lapic_freq_khz { 0 };
|
||||||
|
Genode::uint32_t lapic_div { 0 };
|
||||||
|
|
||||||
Boot_info() {}
|
Boot_info() {}
|
||||||
Boot_info(Acpi_rsdp const &acpi_rsdp,
|
Boot_info(Acpi_rsdp const &acpi_rsdp,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user