diff --git a/repos/base-hw/src/core/spec/x86_64/port_io.h b/repos/base-hw/include/spec/x86_64/port_io.h similarity index 72% rename from repos/base-hw/src/core/spec/x86_64/port_io.h rename to repos/base-hw/include/spec/x86_64/port_io.h index ebfcf2095c..5075448a14 100644 --- a/repos/base-hw/src/core/spec/x86_64/port_io.h +++ b/repos/base-hw/include/spec/x86_64/port_io.h @@ -11,13 +11,15 @@ * under the terms of the GNU Affero General Public License version 3. */ -#ifndef _CORE__SPEC__X86_64__PORT_IO_H_ -#define _CORE__SPEC__X86_64__PORT_IO_H_ +#ifndef _INCLUDE__SPEC__X86_64__PORT_IO_H_ +#define _INCLUDE__SPEC__X86_64__PORT_IO_H_ -/* core includes */ -#include +#include -namespace Core { +namespace Hw { + + using Genode::uint8_t; + using Genode::uint16_t; /** * 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_ */ diff --git a/repos/base-hw/lib/mk/spec/x86_64/core-hw-pc.mk b/repos/base-hw/lib/mk/spec/x86_64/core-hw-pc.mk index cbfb0dcc48..ab6f733f84 100644 --- a/repos/base-hw/lib/mk/spec/x86_64/core-hw-pc.mk +++ b/repos/base-hw/lib/mk/spec/x86_64/core-hw-pc.mk @@ -24,7 +24,7 @@ SRC_CC += spec/x86_64/virtualization/kernel/svm.cc SRC_CC += spec/x86_64/virtualization/kernel/vmx.cc SRC_CC += kernel/lock.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/platform_support.cc SRC_CC += spec/x86_64/virtualization/platform_services.cc diff --git a/repos/base-hw/src/bootstrap/spec/x86_64/platform.cc b/repos/base-hw/src/bootstrap/spec/x86_64/platform.cc index 3c8b657ee1..77aeec6e5b 100644 --- a/repos/base-hw/src/bootstrap/spec/x86_64/platform.cc +++ b/repos/base-hw/src/bootstrap/spec/x86_64/platform.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -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(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(); }, 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() : core_mmio(Memory_region { 0, 0x1000 }, @@ -275,7 +325,12 @@ Bootstrap::Platform::Board::Board() 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 */ addr_t ap_code_size = (addr_t)&_start - (addr_t)&_ap; diff --git a/repos/base-hw/src/core/board/pc/board.h b/repos/base-hw/src/core/board/pc/board.h index f07a971d08..1a9327fbfd 100644 --- a/repos/base-hw/src/core/board/pc/board.h +++ b/repos/base-hw/src/core/board/pc/board.h @@ -21,7 +21,7 @@ /* base-hw core includes */ #include -#include +#include #include namespace Board { diff --git a/repos/base-hw/src/core/spec/x86_64/pic.cc b/repos/base-hw/src/core/spec/x86_64/pic.cc index c4e9073c43..9f5c64a83a 100644 --- a/repos/base-hw/src/core/spec/x86_64/pic.cc +++ b/repos/base-hw/src/core/spec/x86_64/pic.cc @@ -47,6 +47,8 @@ Local_interrupt_controller(Global_interrupt_controller &global_irq_ctrl) void Local_interrupt_controller::init() { + using Hw::outb; + /* Start initialization sequence in cascade mode */ outb(PIC_CMD_MASTER, 0x11); outb(PIC_CMD_SLAVE, 0x11); diff --git a/repos/base-hw/src/core/spec/x86_64/pit.h b/repos/base-hw/src/core/spec/x86_64/pit.h deleted file mode 100644 index 026e293d9a..0000000000 --- a/repos/base-hw/src/core/spec/x86_64/pit.h +++ /dev/null @@ -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 -#include - -/* core includes */ -#include - -namespace Board { class Timer; } - - -/** - * LAPIC-based timer driver for core - */ -struct Board::Timer: Genode::Mmio -{ - 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 - { - 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_ */ diff --git a/repos/base-hw/src/core/spec/x86_64/pit.cc b/repos/base-hw/src/core/spec/x86_64/timer.cc similarity index 51% rename from repos/base-hw/src/core/spec/x86_64/pit.cc rename to repos/base-hw/src/core/spec/x86_64/timer.cc index bb895b9084..100a6535a9 100644 --- a/repos/base-hw/src/core/spec/x86_64/pit.cc +++ b/repos/base-hw/src/core/spec/x86_64/timer.cc @@ -15,9 +15,6 @@ #include -/* Genode includes */ -#include - /* core includes */ #include #include @@ -25,37 +22,9 @@ using namespace Core; 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(~0U); - - t_start = read(); - while ((inb(PIT_CH2_GATE) & 0x20) == 0) - { - asm volatile("pause" : : : "memory"); - } - t_end = read(); - - write(0); - - return (t_start - t_end) / PIT_SLEEP_MS; -} - - 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(); } @@ -75,28 +44,10 @@ void Board::Timer::init() return; } - /* calibrate LAPIC frequency to fullfill our requirements */ - for (Divide_configuration::access_t div = Divide_configuration::Divide_value::MAX; - div && ticks_per_ms < TIMER_MIN_TICKS_PER_MS; div--) - { - if (!div){ - raw("Failed to calibrate timer frequency"); - throw Calibration_failed(); - } - write((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); + Platform::apply_with_boot_info([&](auto const &boot_info) { + ticks_per_ms = boot_info.plat_info.lapic_freq_khz; + divider = boot_info.plat_info.lapic_div; + }); } diff --git a/repos/base-hw/src/core/spec/x86_64/timer.h b/repos/base-hw/src/core/spec/x86_64/timer.h new file mode 100644 index 0000000000..a92b6e39d7 --- /dev/null +++ b/repos/base-hw/src/core/spec/x86_64/timer.h @@ -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 + +/* hw includes */ +#include + +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_ */ diff --git a/repos/base-hw/src/include/hw/spec/x86_64/acpi.h b/repos/base-hw/src/include/hw/spec/x86_64/acpi.h index 163f5da473..16d65892ad 100644 --- a/repos/base-hw/src/include/hw/spec/x86_64/acpi.h +++ b/repos/base-hw/src/include/hw/spec/x86_64/acpi.h @@ -264,7 +264,7 @@ struct Hw::Acpi_fadt : Genode::Mmio<276> 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; @@ -277,7 +277,7 @@ struct Hw::Acpi_fadt : Genode::Mmio<276> asm volatile ("pause":::"memory"); 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) diff --git a/repos/base-hw/src/include/hw/spec/x86_64/apic.h b/repos/base-hw/src/include/hw/spec/x86_64/apic.h index db1586047a..8fb8f5c05d 100644 --- a/repos/base-hw/src/include/hw/spec/x86_64/apic.h +++ b/repos/base-hw/src/include/hw/spec/x86_64/apic.h @@ -18,6 +18,9 @@ namespace Hw { class Local_apic; } #include +/* Genode includes */ +#include + struct Hw::Local_apic : Genode::Mmio { struct Id : Register<0x020, 32> { }; @@ -58,6 +61,57 @@ struct Hw::Local_apic : Genode::Mmio 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 + { + 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((uint8_t)div); + + write(~0U); + + /* Calculate timer frequency */ + result.freq_khz = calibration_fn(); + result.div = div; + + write(0); + } + + return result; + } + Local_apic(addr_t const addr) : Mmio({(char*)addr, Mmio::SIZE}) {} }; diff --git a/repos/base-hw/src/include/hw/spec/x86_64/pc_board.h b/repos/base-hw/src/include/hw/spec/x86_64/pc_board.h index c018a922c3..eb39a11c3f 100644 --- a/repos/base-hw/src/include/hw/spec/x86_64/pc_board.h +++ b/repos/base-hw/src/include/hw/spec/x86_64/pc_board.h @@ -45,6 +45,8 @@ struct Hw::Pc_board::Boot_info Genode::addr_t efi_system_table { 0 }; Genode::addr_t acpi_fadt { 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(Acpi_rsdp const &acpi_rsdp,