4 * Instance manager implementation.
6 * (c) 2011 Björn Döbel <doebel@os.inf.tu-dresden.de>,
7 * economic rights: Technische Universität Dresden (Germany)
8 * This file is part of TUD:OS and distributed under the terms of the
9 * GNU General Public License 2.
10 * Please see the COPYING-GPL-2 file for details.
14 #include "app_loading"
15 #include "configuration"
17 #include <l4/sys/segment.h>
19 #define MSG() DEBUGf(Romain::Log::Manager)
21 Romain::Configuration Romain::globalconfig;
23 L4_INLINE unsigned countbits(long v)
25 v = v - ((v >> 1) & 0x55555555); // reuse input as temporary
26 v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp
27 return ((v + ((v >> 4) & 0xF0F0F0F)) * 0x1010101) >> 24; // count
30 L4_INLINE l4_umword_t count_online_cpus()
33 l4_sched_cpu_set_t set = l4_sched_cpu_set(0, 0);
34 if (l4_error(L4Re::Env::env()->scheduler()->info(&ret, &set)) < 0) {
35 ERROR() << "reading CPU info";
37 ret = countbits(set.map);
39 INFO() << "Found " << ret << " CPUs.";
43 Romain::InstanceManager::InstanceManager(unsigned int argc,
45 unsigned num_instances)
50 _num_inst(num_instances),
52 _argc(argc), // XXX: remove
53 _argv(argv) // XXX: remove
57 _gdt_min = fiasco_gdt_get_entry_offset(L4_INVALID_CAP, l4_utcb());
58 MSG() << "GDT MIN: " << _gdt_min;
60 _num_cpu = count_online_cpus();
62 * initial parameter is argv for the client program, this means
63 * *argv is the file name to load.
67 _am = new Romain::App_model(_name, argc, argv);
68 Romain::Elf_Ldr loader(_am);
72 _init_eip = _am->prog_info()->entry;
73 _init_esp = _am->prog_info()->stack_addr;
74 INFO() << "Program entry point at 0x" << std::hex << _init_eip;
75 INFO() << " stack at 0x" << std::hex << _init_esp;
78 int res = pthread_create(&_split_handler, 0, split_handler_fn, this);
79 _check(res != 0, "could not create split handler thread");
84 void Romain::InstanceManager::configure_logflags(char *flags)
86 printf("flags %p\n", flags);
88 Romain::Log::logFlags = 0;
90 unsigned max = strlen(flags);
91 for (unsigned j = 0; j < max; ++j) {
92 if (flags[j] == ',') flags[j] = 0;
95 char const *c = flags;
96 while (c <= flags + max) {
98 if ((strcmp(c, "mem") == 0) || (strcmp(c, "memory") == 0)) {
99 Romain::Log::logFlags |= Romain::Log::Memory;
100 } else if (strcmp(c, "emulator") == 0) {
101 Romain::Log::logFlags |= Romain::Log::Emulator;
102 } else if (strcmp(c, "manager") == 0) {
103 Romain::Log::logFlags |= Romain::Log::Manager;
104 } else if (strcmp(c, "faults") == 0) {
105 Romain::Log::logFlags |= Romain::Log::Faults;
106 } else if (strcmp(c, "redundancy") == 0) {
107 Romain::Log::logFlags |= Romain::Log::Redundancy;
108 } else if (strcmp(c, "loader") == 0) {
109 Romain::Log::logFlags |= Romain::Log::Loader;
110 } else if (strcmp(c, "swifi") == 0) {
111 Romain::Log::logFlags |= Romain::Log::Swifi;
112 } else if (strcmp(c, "gdb") == 0) {
113 Romain::Log::logFlags |= Romain::Log::Gdb;
114 } else if (strcmp(c, "all") == 0) {
115 Romain::Log::logFlags = Romain::Log::All;
120 printf("Flags: %08lx\n", Romain::Log::logFlags);
125 void Romain::InstanceManager::configure_fault_observers()
128 * First, register those observers that don't interfere
129 * with anyone else and get notified all the time.
131 BoolObserverConfig("general:print_vcpu_state",
133 ObserverConfig(this, "trap_limit");
136 * We always need the system call observer.
138 ObserverConfig(this, "syscalls");
139 ObserverConfig(this, "pagefaults");
140 ObserverConfig(this, "trap");
142 StringObserverConfig("general:debug", this);
143 BoolObserverConfig("general:intercept_kip", this, "kip-time");
144 BoolObserverConfig("general:swifi", this, "swifi");
148 void Romain::InstanceManager::configure_redundancy()
150 char const *redundancy = ConfigStringValue("general:redundancy");
151 if (!redundancy) redundancy = "none";
152 INFO() << "red: '" << redundancy << "'";
153 if (strcmp(redundancy, "none") == 0) {
155 set_redundancy_callback(new NoRed());
156 } else if (strcmp(redundancy, "dual") == 0) {
158 set_redundancy_callback(new DMR(2));
159 } else if (strcmp(redundancy, "triple") == 0) {
161 set_redundancy_callback(new DMR(3));
163 ERROR() << "Invalid redundancy setting: " << redundancy;
164 enter_kdebug("Invalid redundancy setting");
169 * Romain ini file settings
170 * =====================
175 * The 'general' section determines which fault handlers are registered.
177 * print_vcpu_state [bool]
178 * - Registers a handler printing the state of a VCPU upon every
181 * debug [string = {simple,gdb}]
182 * - Configures a debugger stub. 'simple' refers to builtin debugging,
183 * 'gdb' starts a gdb stub. Further configuration for the debuggers
184 * is done in separate INI sections.
186 * page_fault_handling [string = {ro}]
187 * - Specify the way in which paging is done.
188 * 'ro' means that client memory is mapped read-only and write
189 * accesses to the respective regions are emulated.
191 * redundancy [string = {dual, triple}]
192 * - configure the number of replicas that are started
196 * - comma-separated list of strings for configuring logging
197 * - available flags are:
198 * - mem|memory -> memory management
199 * - emulator -> instruction emulation
200 * - manager -> replica management
201 * - faults -> generic fault entry path
202 * - redundancy -> DMR/TMR-specific logs
203 * - swifi -> fault injetion
204 * - gdb -> GDB stub logging
205 * - all -> everything
207 * swifi [bool] (false)
208 * - Perform fault injection experiments, details are configured
209 * in the [swifi] section.
211 * kip-time [bool] (false)
212 * - Turn on/off KIP timer access. This is used to turn replica
213 * accesses to the clock field of the KIP into traps (by placing
214 * software breakpoints on specifically configured instructions).
215 * Use this, if your application needs clock info from the KIP.
217 * max_traps [int] (-1)
218 * - Handle a maximum amount of traps before terminating the
219 * replicated application. Use as a debugging aid.
221 * print_time [bool] (true)
222 * - include timing information in printed output.
227 * This section configures the behavior of the GDB stub.
230 * - Configures the GDB stub to use a TCP/IP connection and wait
231 * for a remote GDB to connect on the port specified. If this
232 * option is _not_ set, the GDB stub will try to use a serial
233 * connection through COM2.
235 * XXX make COM port configurable
237 * 'simpledbg' section
238 * -------------------
240 * This section configures Romain's builtin debugger, which is programmed through
241 * INI file commands only and performs a narrow range of debugging tasks only.
244 * - Patches an INT3 breakpoint to the given address. Then executes the program
245 * until the breakpoint is hit and thereafter switches to single-stepping
251 * The KIP-time instrumentation needs a list of addresses that point to
252 * KIP->clock accessing instructions. These are supplied as a comma-separated
253 * list of hex values for the target command.
255 * target [comma-separated list of hex addresses]
260 * Configures fault-injection experiments that are performed on replicas.
261 * By default, SWIFI currently injects faults into replica #0.
264 * specifies an address to place a breakpoint on. Upon hitting this
265 * BP, a SWIFI injection is performed.
268 * specifies what kind of injection to perform when hitting the BP.
270 * - 'gpr' -> flip a random bit in a randomly selected
271 * general-purpose register
273 void Romain::InstanceManager::configure()
275 char *log = strdup(ConfigStringValue("general:log", "none"));
276 configure_logflags(log);
278 Log::withtime = ConfigBoolValue("general:print_time", true);
280 configure_fault_observers();
281 configure_redundancy();
287 * Prepare the stack that is used by the fault handler whenever a
288 * VCPU enters the master task.
290 * This pushes relevant pointers to the stack so that the handler
291 * functions can use them as parameters.
293 l4_addr_t Romain::InstanceManager::prepare_stack(l4_addr_t sp,
294 Romain::App_instance *inst,
295 Romain::App_thread *thread)
297 Romain::Stack st(sp);
303 st.push(0); // this would be the return address, but
304 // handlers return by vcpu_resume()
310 void Romain::InstanceManager::create_instances()
312 for (unsigned i = 0; i < _num_inst; ++i) {
313 _instances.push_back(new Romain::App_instance(_name, i));
318 void Romain::InstanceManager::run_instances()
320 for (unsigned i = 0; i < _num_inst; ++i) {
321 Romain::App_thread *at = new Romain::App_thread(_init_eip, _init_esp,
322 reinterpret_cast<l4_addr_t>(VCPU_handler),
323 reinterpret_cast<l4_addr_t>(VCPU_startup)
326 at->setup_gdt(_am->stack()->target_top() - 4, 4);
328 Romain::Region_handler &rh = const_cast<Romain::Region_handler&>(
329 _am->rm()->find(_am->prog_info()->utcbs_start)->second);
330 _check(_am->rm()->copy_existing_mapping(rh, 0, i) != true,
331 "could not create UTCB copy");
332 at->remote_utcb(rh.local_region(i).start());
334 DEBUG() << "prepare: " << (void*)at->handler_sp();
335 at->handler_sp(prepare_stack(at->handler_sp(), _instances[i], at));
336 at->thread_sp((l4_addr_t)_am->stack()->relocate(_am->stack()->ptr()));
338 INFO() << i << " " << (i+1) % _num_cpu << " " << _num_cpu;
339 at->cpu((i+1) % _num_cpu);
344 startup_notify(_instances[i], at, _am);