-// vi: ft=cpp
+// vim: ft=cpp
/*
* app --
*
* Definitions of applications, instances
*
- * (c) 2011-2012 Björn Döbel <doebel@os.inf.tu-dresden.de>,
+ * (c) 2011-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
* economic rights: Technische Universität Dresden (Germany)
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
#pragma once
#include <cstdio>
+#include <iomanip>
#include <stdlib.h>
#include <malloc.h>
#include <map>
#include <semaphore.h>
+#include <pthread-l4.h>
+#include <atomic>
#include <l4/sys/types.h>
#include <l4/sys/utcb.h>
#include <l4/sys/debugger.h>
#include <l4/vcpu/vcpu>
+#include <l4/plr/measurements.h>
#include <l4/util/util.h>
#include <l4/util/bitops.h>
extern "C" void my_handler(void);
+class Breakpoint;
+
namespace Romain {
/*
/*
* Instance ID
*/
- unsigned _id;
+ l4_umword_t _id;
/*
* Map of addr -> addr mappings.
enum { debug_name_size = 16 };
public:
- explicit App_instance(char const *name = "", unsigned const instanceID = 0)
+ explicit App_instance(char const *name = "", l4_umword_t const instanceID = 0)
: _id(instanceID)
{
/*
* Every replica gets a name set as the debug ID
*/
char namebuf[debug_name_size];
- snprintf(namebuf, debug_name_size, "V%d %s", _id, name);
+ snprintf(namebuf, debug_name_size, "V%ld %s", _id, name);
l4_debugger_set_object_name(_vcpu_task.cap(), namebuf);
}
L4::Cap<L4::Task> vcpu_task() const { return _vcpu_task; }
- unsigned id() const { return _id; }
+ l4_umword_t id() const { return _id; }
/*
* Map a flexpage in an aligned way.
* largest possible mapping so that we can avoid a couple
* of page faults if possible. XXX
*/
- void map_aligned(l4_addr_t local, l4_addr_t remote, unsigned shift, unsigned flags)
+ void map_aligned(l4_addr_t local, l4_addr_t remote, l4_umword_t shift, l4_umword_t flags)
{
//DEBUG() << "map_aligned(" << std::hex << local << ", " << remote
// << ", " << shift << ", " << flags << ")";
l4_fpage_t fp = l4_fpage(local, shift, flags);
//DEBUG() << "fp: " << fp.raw;
l4_msgtag_t tag = vcpu_task()->map(L4Re::This_task, fp, remote);
+ _check(l4_msgtag_has_error(tag), "error mapping page");
//DEBUG() << "mapped " << std::hex << fp.raw << " : " << std::hex << tag.raw;
- for (unsigned offs = 0; offs < (L4_PAGESIZE << (shift - L4_PAGESHIFT));
+ for (l4_umword_t offs = 0; offs < (L4_PAGESIZE << (shift - L4_PAGESHIFT));
offs += L4_PAGESIZE) {
_mappings[remote + offs] = local + offs;
}
}
- /*
- * Map a local region to a remote region using the least
- * possible amount of map operations (XXX).
- */
- void map(l4_addr_t local_start, l4_addr_t remote_start,/* l4_size_t size,*/
- unsigned pageflags, l4_size_t size = L4_PAGESIZE)
- {
- //DEBUG() << "map " << std::hex << local_start << " -> " << remote_start
- // << " size " << size;
-
- while (size > 0) {
- unsigned frame_l = local_start >> L4_PAGESHIFT;
- unsigned frame_r = remote_start >> L4_PAGESHIFT;
- unsigned shift = 0;
-
-/* Macro checks whether the size fits a given number of pages */
-#define FOO(x) do { \
- if ((!frame_l & (x-1)) && (!frame_r & (x-1)) && (size >= (x*L4_PAGESIZE))) { \
- shift += 1; \
- } \
-} while (0)
- //FOO(2); FOO(4); FOO(8); FOO(16); FOO(32); FOO(64);
- map_aligned(local_start, remote_start, L4_PAGESHIFT + shift, pageflags);
- local_start += (L4_PAGESIZE << shift);
- remote_start += (L4_PAGESIZE << shift);
- size -= (L4_PAGESIZE << shift);
- }
- //enter_kdebug("mapped");
- }
-
-
/*
* Unmap a flexpage from replica
*/
fp.raw = fpraw;
remote = l4_fpage_page(fp) << L4_PAGESHIFT;
- //DEBUG() << "unmap @ " << std::hex << "0x" << remote;
l4_addr_t a = _mappings[remote];
- //DEBUG() << std::hex << remote << " -> " << "0x" << a;
+ DEBUG() << "unmap @ " << std::hex << remote << " -> " << "0x" << a;
vcpu_task()->unmap(l4_fpage(a, L4_PAGESIZE, L4_FPAGE_RO), L4_FP_ALL_SPACES);
_mappings[remote] = 0;
//enter_kdebug("unmapped");
}
};
-
/*
* Representation of an application-level thread
*
* Master segment registers. Restored whenever we enter the
* master through a VCPU fault.
*/
- unsigned long _master_ds;
- unsigned long _master_fs;
- unsigned long _master_gs;
+ l4_umword_t _master_ds;
+ l4_umword_t _master_fs;
+ l4_umword_t _master_gs;
l4_umword_t _pending_trap; // for injecting HW traps
l4_umword_t _events; // keeping track of handle events
struct gdt_entry_struct
{
- unsigned short limit_low; // The lower 16 bits of the limit.
- unsigned short base_low; // The lower 16 bits of the base.
- unsigned char base_middle; // The next 8 bits of the base.
- unsigned char access; // Access flags, determine what ring this segment can be used in.
- unsigned char granularity;
- unsigned char base_high; // The last 8 bits of the base.
+ l4_uint16_t limit_low; // The lower 16 bits of the limit.
+ l4_uint16_t base_low; // The lower 16 bits of the base.
+ l4_uint8_t base_middle; // The next 8 bits of the base.
+ l4_uint8_t access; // Access flags, determine what ring this segment can be used in.
+ l4_uint8_t granularity;
+ l4_uint8_t base_high; // The last 8 bits of the base.
} __attribute__((packed))
_client_gdt[2];
+ bool _gdt_modified; // track if GDT was modified
+
+#if WATCHDOG
+ /*
+ * Watchdog: set on creation and defined in config file
+ */
+ bool _use_watchdog;
+ int _watchdog_timeout;
+
+ /*
+ * Watchdog: interrupt object set on vcpu startup
+ */
+ L4::Cap<L4::Irq> _watchdog_irq;
+
+ /*
+ * Watchdog: use single-stepping for synchronization
+ */
+ bool _watchdog_ss;
+ unsigned _watchdog_ss_count;
+
+ /*
+ * Watchdog: use breakpoints for synchronization
+ */
+ bool _watchdog_breakpointing;
+ Breakpoint *_watchdog_breakpoint;
+
+ /*
+ * Watchdog: am I the replica that passed the watchdog interrupt
+ * with another trap
+ */
+ bool _watchdog_passed;
+
+ bool _got_watchdog;
+ bool _got_other_trap;
+ bool _watchdog_suspended;
+ bool _watchdog_met_leader;
+#endif
+
+ /*
+ * Benchmarking: counters used to determine number and cycles spent in
+ * different parts of the master if BENCHMARKING is set to 1 in
+ * server/src/constants.h
+ *
+ * t_* -> accumulate cycles spent
+ * c_* -> count the number of times certain paths were entered
+ */
+ unsigned long long t_lock, c_lock; // lock observer
+ unsigned long long t_pfh, c_pfh; // page fault handling
+ unsigned long long t_syscalls, c_syscalls; // syscall observer
+ unsigned long long t_kiptime, c_kiptime; // KIP time observer
+ unsigned long long t_traps, c_traps; // trap handling
+
+ unsigned long long t_handling; // total handling time
+ unsigned long long t_observer, c_observer; // time in observers
+ unsigned long long t_keepup, c_keepup; // passive replicas: time to keep up with leader
+ unsigned long long t_user; // time in user mode
+ unsigned long long last_user_resume; // timestamp of last ret to user
+
+ unsigned long long t_sync_enter_all;
+ unsigned long long t_sync_wait_for_active, c_sync_wait_for_active;
+ unsigned long long t_sync_wait, t_sync_waitforarrival;
+ unsigned long long t_sync_getdata, c_sync_getdata;
+ unsigned long long t_sync_active_validate, c_sync_active_validate;
+
+ //unsigned long long t_sync;
+ unsigned long long t_sync_enter; // TS before DMR::enter()
+ unsigned long long t_sync_entered; // TS before sleeping / validating replicas
+ unsigned long long t_sync_leave;
+
+ unsigned long long t_resume_active, c_resume_active;
+ unsigned long long t_resume_passive, c_resume_passive;
+ unsigned long long t_resume_enter;
+
+ /*
+ * Tracks if we are the currently active
+ * trap handling replica
+ */
+ bool active_handler;
/*
* Get topmost address of exception handler/thread stacks
App_thread(l4_addr_t eip,
l4_addr_t esp,
l4_addr_t handler_fn,
- l4_addr_t thread_fn)
+ l4_addr_t thread_fn,
+ bool use_watchdog = false,
+ l4_umword_t watchdog_timeout = 0)
:
_handler_fn(handler_fn),
_thread_fn(thread_fn),
_vcpu_utcb(0),
_remote_utcb(0xFFFFFFFF),
_pending_trap(0),
- _events(0)
+ _events(0),
+ _gdt_modified(false)
+#if WATCHDOG
+ ,
+ _use_watchdog(use_watchdog),
+ _watchdog_timeout(watchdog_timeout),
+ _watchdog_ss(false),
+ _watchdog_ss_count(0),
+ _watchdog_passed(false),
+ _watchdog_breakpointing(false),
+ _watchdog_suspended(false),
+ _got_watchdog(false),
+ _got_other_trap(false),
+ _watchdog_met_leader(false)
+#endif
+ , t_lock(0ULL), c_lock(0ULL), t_pfh(0ULL), c_pfh(0ULL),
+ t_syscalls(0ULL), c_syscalls(0ULL), t_kiptime(0ULL),
+ c_kiptime(0ULL), t_traps(0ULL), c_traps(0ULL), t_handling(0ULL),
+ t_observer(0ULL), c_observer(0ULL),
+ t_keepup(0ULL), c_keepup(0ULL),
+ t_user(0ULL), last_user_resume(0ULL),
+ t_sync_enter_all(0ULL), t_sync_wait_for_active(0ULL), c_sync_wait_for_active(0ULL),
+ t_sync_wait(0ULL), t_sync_waitforarrival(0ULL),
+ t_sync_getdata(0ULL), c_sync_getdata(0ULL),
+ t_sync_active_validate(0ULL), c_sync_active_validate(0ULL),
+ t_resume_active(0ULL), c_resume_active(0ULL),
+ t_resume_passive(0ULL), c_resume_passive(0ULL),
+ t_resume_enter(0ULL)
{
asm volatile (
"mov %%fs, %0\n\t"
alloc_vcpu_cap();
alloc_vcpu_mem();
+ memset(gdt(), 0, gdt_size());
+
DEBUG() << "vCPU cap: " << std::hex << vcpu_cap();
DEBUG() << "STACK: " << std::hex << (void*)esp;
DEBUG() << "EIP " << (void*)eip << " ESP " << (void*)esp;
}
+#if WATCHDOG
+ void use_watchdog(bool u) { _use_watchdog = u; }
+ bool use_watchdog() { return _use_watchdog; }
+ void watchdog_timeout(l4_umword_t p) { _watchdog_timeout = p; }
+ l4_umword_t watchdog_timeout() { return _watchdog_timeout; }
+
+ void watchdog_ss(bool ss) { _watchdog_ss = ss; }
+ bool watchdog_ss() { return _watchdog_ss; }
+ unsigned watchdog_ss_count() { return _watchdog_ss_count; }
+ void increment_watchdog_ss_count() { ++_watchdog_ss_count; }
+ void reset_watchdog_ss_count() { _watchdog_ss_count = 0; }
+
+ void its_me_who_passed_the_watchdog(bool p) { _watchdog_passed = p; }
+ bool its_me_who_passed_the_watchdog() { return _watchdog_passed; }
+
+ void watchdog_irq(L4::Cap<L4::Irq> irq) { _watchdog_irq = irq; }
+ L4::Cap<L4::Irq> watchdog_irq() { return _watchdog_irq; }
+
+ void watchdog_breakpoint(Breakpoint *b) { _watchdog_breakpoint = b; }
+ Breakpoint *watchdog_breakpoint() { return _watchdog_breakpoint; }
+
+ void watchdog_breakpointing(bool b) { _watchdog_breakpointing = b; }
+ bool watchdog_breakpointing() { return _watchdog_breakpointing; }
+
+ void got_watchdog(bool w) { _got_watchdog = w; }
+ bool got_watchdog() { return _got_watchdog; }
+
+ void got_other_trap(bool t) { _got_other_trap = t; }
+ bool got_other_trap() { return _got_other_trap; }
+
+ void watchdog_suspended(bool s) { _watchdog_suspended = s; }
+ bool watchdog_suspended() { return _watchdog_suspended; }
+
+ void i_have_met_the_leader(bool m) { _watchdog_met_leader = m; }
+ bool i_have_met_the_leader() { return _watchdog_met_leader; }
+#endif
+
+ bool is_active() { return active_handler; }
+ void activate() { active_handler = true; }
+ void deactivate() { active_handler = false; }
+
+ void count_lock(unsigned long long increment)
+ {
+#if BENCHMARKING
+ t_lock += increment; c_lock++;
+#endif
+ }
+
+ void count_pfh(unsigned long long increment)
+ {
+#if BENCHMARKING
+ t_pfh += increment; c_pfh++;
+#endif
+ }
+ void count_syscalls(unsigned long long increment)
+ {
+#if BENCHMARKING
+ t_syscalls += increment; c_syscalls++;
+#endif
+ }
+
+ void count_kiptime(unsigned long long increment)
+ {
+#if BENCHMARKING
+ t_kiptime += increment; c_kiptime++;
+#endif
+ }
+
+ void count_traps(unsigned long long increment)
+ {
+#if BENCHMARKING
+ t_traps += increment; c_traps++;
+#endif
+ }
+
+ void count_handling(unsigned long long increment)
+ {
+#if BENCHMARKING
+ t_handling += increment;
+#endif
+ }
+
+ void ts_from_user() {
+#if BENCHMARKING
+ t_sync_enter = l4_rdtsc();
+ t_user += (t_sync_enter - last_user_resume);
+#endif
+ }
+
+ void ts_sync_entered() {
+#if BENCHMARKING
+ t_sync_entered = l4_rdtsc();
+ t_sync_enter_all += (t_sync_entered - t_sync_enter);
+#endif
+ }
+
+ void ts_sync_leave() {
+#if BENCHMARKING
+ t_sync_leave = l4_rdtsc();
+ if (is_active()) {
+ t_sync_active_validate += (t_sync_leave - t_sync_enter);
+ ++c_sync_active_validate;
+ } else {
+ t_sync_wait_for_active += (t_sync_leave - t_sync_enter);
+ ++c_sync_wait_for_active;
+ }
+#endif
+ }
+
+ void ts_resume_start() {
+#if BENCHMARKING
+ t_resume_enter = l4_rdtsc();
+ if (is_active()) {
+ t_observer += (t_resume_enter - t_sync_leave);
+ c_observer++;
+ } else {
+ t_keepup += (t_resume_enter - t_sync_leave);
+ c_keepup++;
+ }
+#endif
+ }
+
+ void ts_user_resume(bool first = false) {
+#if BENCHMARKING
+ last_user_resume = l4_rdtsc();
+
+ // the first call is only to set the resume TS, don't count
+ // resume time there
+ if (first)
+ return;
+
+ if (is_active()) {
+ t_resume_active += (last_user_resume - t_resume_enter);
+ ++c_resume_active;
+ } else {
+ t_resume_passive += (last_user_resume - t_resume_enter);
+ ++c_resume_passive;
+ }
+ deactivate(); // simply do this any time we resume
+#endif
+ }
+
+ void inc_wait(unsigned long long increment)
+ {
+#if BENCHMARKING
+ t_sync_wait += increment;
+#endif
+ }
+
+ void inc_waitleader(unsigned long long inc)
+ {
+#if BENCHMARKING
+ t_sync_waitforarrival += inc;
+#endif
+ }
+
+
+ void inc_getdata(unsigned long long increment)
+ {
+#if BENCHMARKING
+ c_sync_getdata++;
+ t_sync_getdata += increment;
+#endif
+ }
+
+
+ void print_helper(char const *msg, unsigned long long time,
+ unsigned long long count = 0, bool withCount = false)
+ {
+ if (withCount) {
+ INFO() << std::left << std::setw(32) << msg
+ << " : " << std::right << std::setw(16) << time
+ << " [ " << std::setw(10) << count << " ]";
+ } else {
+ INFO() << std::left << std::setw(32) << msg
+ << " : " << std::right << std::setw(16) << time;
+ }
+ }
+
+ void print_stats()
+ {
+#if BENCHMARKING
+ print_helper(GREEN "Clocks spent in user " NOCOLOR, t_user);
+ print_helper(GREEN "Clocks spent in master " NOCOLOR, t_handling);
+ print_helper(YELLOW " synchronization " NOCOLOR, t_sync_enter_all + t_sync_active_validate + t_sync_wait_for_active);
+ print_helper( " enter sync ", t_sync_enter_all);
+ print_helper( " active: check ", t_sync_active_validate, c_sync_active_validate, true);
+ print_helper( " passive: wait ", t_sync_wait_for_active, c_sync_wait_for_active, true);
+ print_helper( " (early wait) ", t_sync_waitforarrival);
+ print_helper( " (total wait) ", t_sync_wait);
+ print_helper( " (get data) ", t_sync_getdata, c_sync_getdata, true);
+ print_helper(YELLOW " observers " NOCOLOR, t_observer, c_observer, true);
+ print_helper( " PFH ", t_pfh, c_pfh, true);
+ print_helper( " Locking ", t_lock, c_lock, true);
+ print_helper( " Syscalls ", t_syscalls, c_syscalls, true);
+ print_helper( " gettime() ", t_kiptime, c_kiptime, true);
+ print_helper( " CPU Traps ", t_traps, c_traps, true);
+ print_helper(YELLOW " keepup with leader " NOCOLOR, t_keepup, c_keepup, true);
+ print_helper(YELLOW " resume " NOCOLOR, t_resume_active + t_resume_passive);
+ print_helper( " active ", t_resume_active, c_resume_active, true);
+ print_helper( " passive ", t_resume_passive, c_resume_passive, true);
+ INFO() << " ------------------------------------------------------";
+#endif
+ }
/*
* Manage fast lookup for the replica's UTCB address
L4vcpu::Vcpu *vcpu() const { return _vcpu; }
l4_utcb_t *vcpu_utcb() const { return _vcpu_utcb; }
- unsigned long ds() const { return _master_ds; }
- unsigned long fs() const { return _master_fs; }
- unsigned long gs() const { return _master_gs; }
+ l4_umword_t ds() const { return _master_ds; }
+ l4_umword_t fs() const { return _master_fs; }
+ l4_umword_t gs() const { return _master_gs; }
// void gs(l4_addr_t a) { _master_gs = a; }
void * gdt() const
{
return (void*)&_client_gdt[0];
}
- unsigned gdt_size() const { return sizeof(_client_gdt); }
+ l4_umword_t gdt_size() const { return sizeof(_client_gdt); }
/***********************************************************************
* GDT Handling Explained
/*
* Set up the initial GDT segment (e.g., UTCB address)
+ * XXX: rename!
*/
void setup_utcb_segdesc(l4_addr_t base, l4_addr_t limit)
{
+ DEBUG() << "Base " << std::hex << base
+ << " Limit " << limit;
memset(_client_gdt, 0, sizeof(_client_gdt));
_client_gdt[0].limit_low = limit & 0xFFFF;
_client_gdt[0].base_high = (base >> 24) & 0xFF;
_client_gdt[0].access = 0xF2;
_client_gdt[0].granularity = 0x40;
+
+ _gdt_modified = true;
}
+ bool gdt_changed() { return _gdt_modified; }
+
+
/*
* Write the second entry, actually.
+ * XXX: RENAME!
*/
void write_gdt_entry(l4_umword_t *src, l4_umword_t bytes)
{
memcpy(&_client_gdt[1], src, bytes);
- vcpu()->r()->gs = fiasco_gdt_set(vcpu_cap().cap(), &_client_gdt[1],
- sizeof(_client_gdt[1]), 2, l4_utcb());
- DEBUG() << "set " << std::hex << vcpu()->r()->gs;
+ _gdt_modified = true;
}
/*
- *
+ * Write the user GDT entries
*/
- void commit_client_gdt()
- {
- vcpu()->r()->fs = fiasco_gdt_set(vcpu_cap().cap(), gdt(),
- gdt_size()/2, 1, l4_utcb());
- DEBUG() << "set " << std::hex << vcpu()->r()->fs;
- }
+ void commit_client_gdt();
/*
* Schedule a "virtual" trap
*
* - unhandled page fault
*/
- void set_pending_trap(unsigned no) { _pending_trap |= (1 << no); }
+ void set_pending_trap(l4_umword_t no) { _pending_trap |= (1 << no); }
void set_unhandled_pf()
{
/*
* Get the next pending trap (and remove it from pending list)
*/
- unsigned get_pending_trap()
+ l4_umword_t get_pending_trap()
{
- unsigned ret = l4util_find_first_set_bit(&_pending_trap, sizeof(_pending_trap));
+ l4_umword_t ret = l4util_find_first_set_bit(&_pending_trap, sizeof(_pending_trap));
if (ret >= sizeof(_pending_trap) * 8) {
return 0;
} else {
vcpu()->print_state(pref);
}
- unsigned long csum_state();
+ l4_umword_t csum_state();
+
+
+ void halt()
+ {
+ INFO() << " Halting VCPU " << std::hex << vcpu();
+ l4_sched_param_t sp = l4_sched_param(0);
+ if (pthread_l4_cap(pthread_self()) != vcpu_cap().cap()) {
+ chksys(L4Re::Env::env()->scheduler()->run_thread(vcpu_cap(), sp));
+ }
+ }
+
+
+ void return_to(l4_addr_t ret)
+ {
+ vcpu()->r()->sp += sizeof(l4_umword_t); // RET: inc. ESP
+ vcpu()->r()->ip = ret; // RET: return addr
+ }
};