]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/plr/tools/igor/igor
update
[l4.git] / l4 / pkg / plr / tools / igor / igor
1 #!/usr/bin/python
2
3 # IGOR (Injection experiment GeneratOR)
4 #
5 #    Tool for generating scripts running fault injection experiments
6 #    based on Romain running in QEMU
7 #
8 # (c) 2011-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
9 #     economic rights: Technische Universität Dresden (Germany)
10 #
11 #  This file is part of TUD:OS and distributed under the terms of the
12 #  GNU General Public License 2.
13 #  Please see the COPYING-GPL-2 file for details.
14
15 import time
16 import os,sys
17 import subprocess
18
19 import argparse
20
21 class Configuration:
22     """
23     Application configuration
24     """
25     def __init__(self):
26         self.dryrun       = False
27         self.config_time  = time.strftime("%G%b%d-%H%M%S")
28         self.directory    = "/tmp" + "/results-%s" % (self.config_time)
29         self.qemu_dir     = "qemu"
30         self.fiasco_dir   = ""
31         self.l4re_dir     = ""
32         self.l4reconf_dir = ""
33         self.runs         = 1
34         self.app          = ""
35         self.verbose      = False
36         self.inject_addr  = 0
37         self.mode         = "gpr"
38         self.install      = []
39         self.sleep        = 15
40         self.kipaddr      = []
41
42     @property
43     def DryRun(self): return self.dryrun
44
45     @property
46     def Verbose(self): return self.verbose
47
48     @property
49     def FullDir(self): return self.directory
50
51     @property
52     def ConfDir(self): return self.FullDir + "/config"
53
54     @property
55     def ResDir(self):  return self.FullDir + "/results"
56
57     @property
58     def QemuDir(self): return self.qemu_dir
59
60     @property
61     def FiascoDir(self): return self.fiasco_dir
62
63     @property
64     def L4ReDir(self): return self.l4re_dir
65
66     @property
67     def MaxRuns(self): return self.runs
68
69     @property
70     def App(self): return self.app
71
72     @property
73     def AppBinary(self): return self.App.split()[0]
74
75     @property
76     def Mode(self): return self.mode
77
78     @property
79     def InstallFiles(self): return self.install
80          
81     @property
82     def SleepTime(self): return self.sleep
83
84     @property
85     def KipAddresses(self): return self.kipaddr
86
87
88 class ArgumentParser:
89     def __init__(self):
90         self.conf   = Configuration()
91         self.parser = argparse.ArgumentParser(description = "Generate thtartup thcriptth for a Romain/QEMU-bathed THWIFI campaign.")
92
93         self.parser.add_argument("-c", "--config", dest="configfile", help="read config from a thource file inthtead of command line"
94                                  + " (overrideth all other cmdline parameterth)", default=None)
95
96         self.parser.add_argument("--qemu",      dest="qemu",    help="QEMU to uthe, default: %(default)s",
97                                  default="qemu")
98         self.parser.add_argument("--basedir",   dest="basedir", help="where to plathe rethult thubdirth, default: %(default)s",
99                                  default="/tmp")
100         self.parser.add_argument("--fiasco",    dest="fiasco",  help="fiathco build dir",
101                                  default="/path/to/fiasco/build")
102         self.parser.add_argument("--l4re",      dest="l4re",    help="L4Re build dir",
103                                  default="/path/to/l4rebuild")
104         self.parser.add_argument("--app",       dest="app",     help="Application (cmd line) to perform tethtth on",
105                                  default="rom/hello")
106
107         self.parser.add_argument("--runs",      dest="runs",    help="no. of fault injection runth",
108                                  default=10)
109
110         self.parser.add_argument('-d', "--dry", action="store_true", help="only print content that would be generated",
111                                  dest="dryrun")
112         self.parser.add_argument("-v", "--verbose", action="store_true", help="do not throw away Qemu output",
113                                  dest="verbose")
114
115         self.parser.add_argument('-a', dest="inject_addr", default="0",
116                                  help="hex address to inject faults")
117         self.parser.add_argument('-m', dest="mode", default="gpr",
118                                  help="injection mode (gpr|alu|instr|rat)")
119         self.parser.add_argument('-i', dest="install", default="",
120                                  help="additional files to install and boot")
121         self.parser.add_argument('-s', dest="sleep", default="10",
122                                  help="time to keep an experiment running before killing it.")
123         self.parser.add_argument('--kip', dest="kip", default="",
124                                  help="kip time emulation addresses (comma-separated hex)")
125
126
127     def parse(self, args = sys.argv[1:]):
128         ns = self.parser.parse_args(args)
129
130         if ns.configfile is not None:
131             exec "from %s import adapt" % ns.configfile
132             exec "adapt(self.conf)"
133         else:
134             self.conf.DryRun      = ns.dryrun
135             self.conf.Verbose     = ns.verbose
136             self.conf.MaxRuns     = int(ns.runs)
137             self.conf.App         = ns.app
138
139             self.conf.QemuDir     = ns.qemu
140             self.conf.FullDir     = ns.basedir + "/results-%s" % (self.conf.config_time)
141             self.conf.FiascoDir   = ns.fiasco
142             self.conf.L4ReDir     = ns.l4re
143             self.conf.inject_addr = int(ns.inject_addr, 16)
144             self.conf.mode        = ns.mode
145             self.conf.sleep       = int(ns.sleep,10)
146             if ns.install != "":
147                 self.conf.install = ns.install.split(",")
148             if ns.kip != "":
149                 self.conf.kipaddr = [int(s,16) for s in ns.kip.split(",")]
150         
151         return self.conf
152
153
154 class TemplateWriter:
155     """
156     Generic file writer.
157     
158     The idea is to have a string template for the file to be written
159     and let this template be filled with arguments depending on specific
160     implementations.
161     """
162     def __init__(self, global_config, filename):
163         self.config   = global_config
164         self.filename = "%s/%s" % (self.config.ConfDir, filename)
165
166     def write(self):
167         print "======== %s ========" % self.filename
168
169
170 class LuaScriptWriter(TemplateWriter):
171     """
172     Generator for Romain Lua startup scripts.
173     """
174     def __init__(self, global_config, filename):
175         TemplateWriter.__init__(self, global_config, filename)
176
177     def template(self):
178         return """package.path = "rom/?.lua";
179 require("L4");
180
181 local ldr = L4.default_loader;
182
183 ldr:start( { caps = {} ,
184              log = {"romain", "c"},
185            },
186            "rom/romain rom/%s" );""" % self.config.App
187
188     def write(self):
189         TemplateWriter.write(self)
190         if self.config.DryRun:
191             print self.template()
192         else:
193             f = file(self.filename, "w")
194             f.write(self.template())
195
196
197 class IniFileWriter(TemplateWriter):
198     """
199     Generator for Romain INI files
200     """
201     def __init__(self, global_config, filename):
202         TemplateWriter.__init__(self, global_config, filename)
203
204     def template(self):
205         ret  = "[general]\n"
206         ret += "page_fault_handling = rw\n"
207         ret += "romain = yes\n"
208         ret += "swifi = yes\n"
209         ret += "log = swifi\n"
210         ret += "intercept_kip = true\n"
211
212         if self.config.KipAddresses != []:
213             ret += "[kip-time]\n"
214             ret += "  target = "
215             ret += "0x%08x" % self.config.KipAddresses[0]
216             for a in self.config.KipAddresses[1:]:
217                 ret += (",0x%08x" % a)
218             ret += "\n"
219
220         return ret
221
222
223     def write(self):
224         TemplateWriter.write(self)
225         if self.config.DryRun:
226             print self.template() 
227         else:
228             f = file(self.filename, "w")
229             f.write(self.template())
230
231
232 class GPRIniFileWriter(IniFileWriter):
233     """
234     Generator for specific INI files aimed at flipping bits in GPRs
235     """
236     def __init__(self, global_config, filename):
237         IniFileWriter.__init__(self, global_config, filename)
238
239     def template(self):
240         return IniFileWriter.template(self) + """[swifi]
241   target = 0x%lx
242   inject = %s
243 """ % (self.config.inject_addr, "gpr")
244
245
246 class InstrIniFileWriter(IniFileWriter):
247     """
248     Generator for decoder-flipping
249     """
250     def __init__(self, global_config, filename):
251         IniFileWriter.__init__(self, global_config, filename)
252
253     def template(self):
254         return IniFileWriter.template(self) + """[swifi]
255   target = 0x%lx
256   inject = %s
257 """ % (self.config.inject_addr, "instr")
258
259
260 class AluIniFileWriter(IniFileWriter):
261     def __init__(self, global_config, filename):
262         IniFileWriter.__init__(self, global_config, filename)
263
264     def template(self):
265         return IniFileWriter.template(self) + """[swifi]
266   target = 0x%lx
267   inject = %s
268 """ % (self.config.inject_addr, "alu")
269
270
271 class ScriptWriter(TemplateWriter):
272     """
273     Writer for the SWIFI campaign startup script
274     """
275     def __init__(self, global_config, filename):
276         TemplateWriter.__init__(self, global_config, filename)
277
278     def qemu_cmd(self):
279         build  = self.config.L4ReDir
280         fiasco = "%s/fiasco -jdb_cmd=JS -serial_esc" % self.config.FiascoDir
281         ret = "%s -kernel %s/../bootstrap " % (self.config.QemuDir, build)
282         ret += "-append \"bootstrap -modaddr 0x01100000\" "
283         ret += "-initrd \""
284
285         ret += fiasco
286
287         for s in ["sigma0", "moe rom/romain.lua", "l4re", "ned", self.config.AppBinary, "romain",]:
288             ret += ","
289             ret += "%s/%s" % (build, s)
290
291         for f in self.config.InstallFiles:
292             ret += ","
293             ret += "%s/%s" % (self.config.ConfDir, f.split("/")[-1])
294
295         ret += ",%s" % ("%s/romain.ini" % (self.config.ConfDir))
296         ret += ",%s" % ("%s/romain.lua" % (self.config.ConfDir))
297
298         ret += "\" -no-reboot -nographic -serial file:$filename-raw$no.log "
299         ret += "-monitor file:/dev/null"
300
301         return ret
302
303
304     def template(self):
305         ret =  "#!/bin/bash\n"
306         ret += "no=0\n"
307         ret += "max=%d\n" % self.config.MaxRuns
308         ret += ("logdir=%s\n"  % self.config.ResDir +
309                "logprefix=swifi\n" +
310                "filename=$logdir/$logprefix\n\n" +
311                "mkdir -p $logdir\n" +
312                "while [[ $no -lt $max ]]; do\n" +
313                "   echo $no\n")
314         ret += """%s %s &\n\n"""
315         ret += "   sleep %d && kill $!\n\n" % self.config.SleepTime
316         ret += """   cat $filename-raw$no.log | sed -r "s:\x1B\[[0-9;]*[A-Za-z]::g" >$filename$no.log ;
317                   rm $filename-raw$no.log
318                   
319                   no=$((no+1))
320                done"""
321         return ret
322
323     def write(self):
324         TemplateWriter.write(self)
325
326         verbose_redir = ""
327         if (not self.config.Verbose):
328             verbose_redir = ">/dev/null 2>/dev/null"
329
330         if self.config.DryRun:
331             print self.template() % (self.qemu_cmd(), verbose_redir)
332         else:
333             f = file(self.filename, "w")
334             f.write(self.template() % (self.qemu_cmd(), verbose_redir))
335             os.chmod(self.filename, 0700)
336
337
338 class AdditionalFileInstaller:
339     def __init__(self, globalconfig):
340         self.config = globalconfig
341
342     def install(self):
343         print "======== Installing files ========"
344         for f in self.config.InstallFiles:
345             print f, "->", self.config.ConfDir
346             if not self.config.DryRun:
347                 os.symlink(f, "%s/%s" % (self.config.ConfDir, f.split("/")[-1]))
348
349
350 class CampaignRunner:
351     def __init__(self, config):
352         self.conf = config
353         self.script = "%s/swifi.sh" % self.conf.ConfDir
354
355     def run(self):
356         print "======== Exthecuting ========"
357         if not self.conf.DryRun:
358             print self.script
359             subprocess.call(self.script)
360         else:
361             print "Not exthecuting %s in a dry run." % self.script
362
363
364 writers = {
365     "alu" : AluIniFileWriter,
366     "gpr" : GPRIniFileWriter,
367     "instr" : InstrIniFileWriter,
368 }
369
370
371 def run(config):
372     if not config.DryRun:
373         os.mkdir(config.FullDir)
374         os.mkdir(config.ConfDir)
375         os.mkdir(config.ResDir)
376
377     writers[config.Mode](config, "romain.ini").write()
378     LuaScriptWriter(config, "romain.lua").write()
379     ScriptWriter(config, "swifi.sh").write()
380     AdditionalFileInstaller(config).install()
381     CampaignRunner(config).run()
382
383 if __name__ == "__main__":
384     run(ArgumentParser().parse())