68bab6a411
Write tick count of next kernel timer to the guest timed events page if present. This causes the guest VM to be preempted at the requested tick count and ensures that the guest VM can not monopolize the CPU if no traps occur. The base-hw kernel expects a configured switch-event from the guest VM to base-hw with ID 30 and target vector 32 to be present in the system policy. Issue #2016
126 lines
2.8 KiB
C++
126 lines
2.8 KiB
C++
/*
|
|
* \brief Timer driver for core
|
|
* \author Reto Buerki
|
|
* \date 2015-04-14
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2015 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
|
|
#ifndef _CORE__INCLUDE__SPEC__X86_64__MUEN__TIMER_H_
|
|
#define _CORE__INCLUDE__SPEC__X86_64__MUEN__TIMER_H_
|
|
|
|
/* base includes */
|
|
#include <base/printf.h>
|
|
#include <kernel/types.h>
|
|
|
|
/* core includes */
|
|
#include <board.h>
|
|
#include <sinfo_instance.h>
|
|
|
|
namespace Genode
|
|
{
|
|
/**
|
|
* Timer driver for core on Muen
|
|
*/
|
|
class Timer;
|
|
}
|
|
|
|
class Genode::Timer
|
|
{
|
|
private:
|
|
|
|
using time_t = Kernel::time_t;
|
|
|
|
enum { TIMER_DISABLED = ~0ULL };
|
|
|
|
uint64_t _tics_per_ms;
|
|
|
|
struct Subject_timed_event
|
|
{
|
|
uint64_t tsc_trigger;
|
|
uint8_t event_nr :5;
|
|
} __attribute__((packed));
|
|
|
|
struct Subject_timed_event * _event_page = 0;
|
|
struct Subject_timed_event * _guest_event_page = 0;
|
|
|
|
|
|
inline uint64_t rdtsc()
|
|
{
|
|
uint32_t lo, hi;
|
|
asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
|
|
return (uint64_t)hi << 32 | lo;
|
|
}
|
|
|
|
class Invalid_region {};
|
|
|
|
public:
|
|
|
|
Timer() :
|
|
_tics_per_ms(sinfo()->get_tsc_khz())
|
|
{
|
|
|
|
/* first sinfo instance, output status */
|
|
sinfo()->log_status();
|
|
|
|
struct Sinfo::Memregion_info region;
|
|
if (!sinfo()->get_memregion_info("timed_event", ®ion)) {
|
|
PERR("muen-timer: Unable to retrieve timed event region");
|
|
throw Invalid_region();
|
|
}
|
|
|
|
_event_page = (Subject_timed_event *)region.address;
|
|
_event_page->event_nr = Board::TIMER_EVENT_KERNEL;
|
|
PINF("muen-timer: Page @0x%llx, frequency %llu kHz, event %u",
|
|
region.address, _tics_per_ms, _event_page->event_nr);
|
|
|
|
if (sinfo()->get_memregion_info("monitor_timed_event", ®ion)) {
|
|
PINF("muen-timer: Found guest timed event page @0x%llx"
|
|
" -> enabling preemption", region.address);
|
|
_guest_event_page = (Subject_timed_event *)region.address;
|
|
_guest_event_page->event_nr = Board::TIMER_EVENT_PREEMPT;
|
|
}
|
|
}
|
|
|
|
static unsigned interrupt_id(int) {
|
|
return Board::TIMER_VECTOR_KERNEL; }
|
|
|
|
inline void start_one_shot(time_t const tics, unsigned)
|
|
{
|
|
const uint64_t t = rdtsc() + tics;
|
|
_event_page->tsc_trigger = t;
|
|
|
|
if (_guest_event_page)
|
|
_guest_event_page->tsc_trigger = t;
|
|
}
|
|
|
|
time_t tics_to_us(time_t const tics) const {
|
|
return (tics / _tics_per_ms) * 1000; }
|
|
|
|
time_t us_to_tics(time_t const us) const {
|
|
return (us / 1000) * _tics_per_ms; }
|
|
|
|
time_t max_value() { return (time_t)~0; }
|
|
|
|
time_t value(unsigned)
|
|
{
|
|
const uint64_t now = rdtsc();
|
|
if (_event_page->tsc_trigger != TIMER_DISABLED
|
|
&& _event_page->tsc_trigger > now) {
|
|
return _event_page->tsc_trigger - now;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void disable_pit(void) { }
|
|
};
|
|
|
|
namespace Kernel { class Timer : public Genode::Timer { }; }
|
|
|
|
#endif /* _CORE__INCLUDE__SPEC__X86_64__MUEN__TIMER_H_ */
|