]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/plr/server/src/manager.cc
bf2cd138e32c3ec135f2ff1d091446efa1c84939
[l4.git] / l4 / pkg / plr / server / src / manager.cc
1 /*
2  * manager.cc --
3  *
4  *     Instance manager implementation.
5  *
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.
11  */
12
13 #include "manager"
14 #include "app_loading"
15 #include "configuration"
16
17 #include <l4/sys/segment.h>
18
19 #define MSG() DEBUGf(Romain::Log::Manager)
20
21 Romain::Configuration Romain::globalconfig;
22
23 L4_INLINE unsigned countbits(long v)
24 {
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
28 }
29
30 L4_INLINE l4_umword_t count_online_cpus()
31 {
32         l4_umword_t ret;
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";
36         }
37         ret = countbits(set.map);
38
39         INFO() << "Found " << ret << " CPUs.";
40         return ret;
41 }
42
43 Romain::InstanceManager::InstanceManager(unsigned int argc,
44                                          char const **argv,
45                                          unsigned num_instances)
46         : _am(0),
47           _instances(),
48           _callback(0),
49           _num_observers(0),
50           _num_inst(num_instances),
51           _num_cpu(1),
52           _argc(argc), // XXX: remove
53           _argv(argv)  // XXX: remove
54 {
55         configure();
56
57         _gdt_min = fiasco_gdt_get_entry_offset(L4_INVALID_CAP, l4_utcb());
58         MSG() << "GDT MIN: " << _gdt_min;
59
60         _num_cpu = count_online_cpus();
61         /*
62          * initial parameter is argv for the client program, this means
63          * *argv is the file name to load.
64          */
65         _name = *argv;
66
67         _am = new Romain::App_model(_name, argc, argv);
68         Romain::Elf_Ldr loader(_am);
69         //loader.load();
70         loader.launch();
71
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;
76
77 #if SPLIT_HANDLING
78         int res = pthread_create(&_split_handler, 0, split_handler_fn, this);
79         _check(res != 0, "could not create split handler thread");
80 #endif
81 }
82
83
84 void Romain::InstanceManager::configure_logflags(char *flags)
85 {
86         printf("flags %p\n", flags);
87         if (!flags) {
88                 Romain::Log::logFlags = 0;
89         } else {
90                 unsigned max = strlen(flags);
91                 for (unsigned j = 0; j < max; ++j) {
92                         if (flags[j] == ',') flags[j] = 0;
93                 }
94
95                 char const *c = flags;
96                 while (c <= flags + max) {
97                         printf("  %s\n", c);
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;
116                         }
117
118                         c += (strlen(c)+1);
119                 }
120                 printf("Flags: %08lx\n", Romain::Log::logFlags);
121         }
122 }
123
124
125 void Romain::InstanceManager::configure_fault_observers()
126 {
127         /*
128          * First, register those observers that don't interfere
129          * with anyone else and get notified all the time.
130          */
131         BoolObserverConfig("general:print_vcpu_state",
132                            this, "vcpu_state");
133         ObserverConfig(this, "trap_limit");
134
135         /*
136          * We always need the system call observer.
137          */
138         ObserverConfig(this, "syscalls");
139         ObserverConfig(this, "pagefaults");
140         ObserverConfig(this, "trap");
141
142         StringObserverConfig("general:debug", this);
143         BoolObserverConfig("general:intercept_kip", this, "kip-time");
144         BoolObserverConfig("general:swifi", this, "swifi");
145 }
146
147
148 void Romain::InstanceManager::configure_redundancy()
149 {
150         char const *redundancy = ConfigStringValue("general:redundancy");
151         if (!redundancy) redundancy = "none";
152         INFO() << "red: '" << redundancy << "'";
153         if (strcmp(redundancy, "none") == 0) {
154                 _num_inst = 1;
155                 set_redundancy_callback(new NoRed());
156         } else if (strcmp(redundancy, "dual") == 0) {
157                 _num_inst = 2;
158                 set_redundancy_callback(new DMR(2));
159         } else if (strcmp(redundancy, "triple") == 0) {
160                 _num_inst = 3;
161                 set_redundancy_callback(new DMR(3));
162         } else {
163                 ERROR() << "Invalid redundancy setting: " << redundancy;
164                 enter_kdebug("Invalid redundancy setting");
165         }
166 }
167
168 /*
169  * Romain ini file settings
170  * =====================
171  *
172  *  'general' section
173  *  -----------------
174  *
175  *  The 'general' section determines which fault handlers are registered.
176  *
177  *  print_vcpu_state [bool]
178  *     - Registers a handler printing the state of a VCPU upon every
179  *       fault entry
180  *
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.
185  *
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.
190  *
191  *  redundancy [string = {dual, triple}]
192  *     - configure the number of replicas that are started
193  *
194  *
195  *  log [string list]
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
206  *
207  *  swifi [bool] (false)
208  *       - Perform fault injection experiments, details are configured
209  *         in the [swifi] section.
210  *
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.
216  *
217  *  max_traps [int] (-1)
218  *       - Handle a maximum amount of traps before terminating the 
219  *         replicated application. Use as a debugging aid.
220  *
221  *  print_time [bool] (true)
222  *       - include timing information in printed output.
223  *
224  *  'gdbstub' section
225  *  -----------------
226  *
227  *  This section configures the behavior of the GDB stub.
228  *
229  *  port [int]
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.
234  *
235  *  XXX make COM port configurable
236  *
237  *  'simpledbg' section
238  *  -------------------
239  *
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.
242  *
243  *  singlestep [int]
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
246  *       mode.
247  *
248  *  'kip-time' section
249  *  ------------------
250  *
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.
254  *
255  *  target [comma-separated list of hex addresses]
256  *
257  *  'swifi' section
258  *  ---------------
259  *
260  *  Configures fault-injection experiments that are performed on replicas.
261  *  By default, SWIFI currently injects faults into replica #0.
262  *
263  *  - target [hex]
264  *    specifies an address to place a breakpoint on. Upon hitting this
265  *    BP, a SWIFI injection is performed.
266  *
267  *  - inject [string]
268  *    specifies what kind of injection to perform when hitting the BP.
269  *    Available values:
270  *      - 'gpr' -> flip a random bit in a randomly selected
271  *                 general-purpose register
272  */
273 void Romain::InstanceManager::configure()
274 {
275         char *log = strdup(ConfigStringValue("general:log", "none"));
276         configure_logflags(log);
277         
278         Log::withtime = ConfigBoolValue("general:print_time", true);
279
280         configure_fault_observers();
281         configure_redundancy();
282         free(log);
283 }
284
285
286 /*
287  * Prepare the stack that is used by the fault handler whenever a
288  * VCPU enters the master task.
289  *
290  * This pushes relevant pointers to the stack so that the handler
291  * functions can use them as parameters.
292  */
293 l4_addr_t Romain::InstanceManager::prepare_stack(l4_addr_t sp,
294                                                  Romain::App_instance *inst,
295                                                  Romain::App_thread *thread)
296 {
297         Romain::Stack st(sp);
298
299         st.push(_am);
300         st.push(thread);
301         st.push(inst);
302         st.push(this);
303         st.push(0); // this would be the return address, but
304                     // handlers return by vcpu_resume()
305
306         return st.sp();
307 }
308
309
310 void Romain::InstanceManager::create_instances()
311 {
312         for (unsigned i = 0; i < _num_inst; ++i) {
313                 _instances.push_back(new Romain::App_instance(_name, i));
314         }
315 }
316
317
318 void Romain::InstanceManager::run_instances()
319 {
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)
324                 );
325
326                 at->setup_gdt(_am->stack()->target_top() - 4, 4);
327
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());
333
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()));
337                 if (_num_cpu > 1) {
338                         INFO() << i << " " << (i+1) % _num_cpu << " " << _num_cpu;
339                         at->cpu((i+1) % _num_cpu);
340                 } else {
341                         at->cpu(0);
342                 }
343
344                 startup_notify(_instances[i], at, _am);
345
346                 at->start();
347         }
348 }