7f66eb5792740bcfe7efd648ae66b69e69a15dcb
[barrelfish] / tools / harness / machines / gem5.py
1 ##########################################################################
2 # Copyright (c) 2012, ETH Zurich.
3 # All rights reserved.
4 #
5 # This file is distributed under the terms in the attached LICENSE file.
6 # If you do not find this file, copies can be found by writing to:
7 # ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
8 ##########################################################################
9
10
11 # Quirks:
12 # * menu.lst is read from hake/menu.lst.arm_gem5, not the one
13 #    generated by scalebench
14 # * this is only running in single-core mode, since bootarm=0 is
15 #    used in above mentioned menu.lst
16
17 import os, signal, tempfile, subprocess, shutil, time
18 import debug, machines
19 from machines import Machine
20
21 GEM5_PATH = '/home/netos/tools/gem5/gem5/build/ARM/'
22 GEM5_CACHES_ENABLE = '--caches --l2cache'.split()
23 # gem5 takes quite a while to come up. If we return right away,
24 # telnet will be opened too early and fails to connect
25 GEM5_START_TIMEOUT = 5 # in seconds
26
27
28 class Gem5MachineBase(Machine):
29     def __init__(self, options):
30         super(Gem5MachineBase, self).__init__(options)
31         self.child = None
32         self.telnet = None
33         self.tftp_dir = None
34         self.options = options
35
36     def get_coreids(self):
37         return range(0, self.get_ncores())
38
39     def get_tickrate(self):
40         return None
41
42     def get_boot_timeout(self):
43         # we set this to 10 mins since gem5 is very slow
44         return 600
45
46     def get_test_timeout(self):
47         # give gem5 tests enough time to complete
48         # 20 mins
49         return 15 * 60
50
51     def get_machine_name(self):
52         return self.name
53
54     def force_write(self, consolectrl):
55         pass
56
57     def get_tftp_dir(self):
58         if self.tftp_dir is None:
59             debug.verbose('creating temporary directory for Gem5 files')
60             self.tftp_dir = tempfile.mkdtemp(prefix='harness_gem5_')
61             debug.verbose('Gem5 install directory is %s' % self.tftp_dir)
62         return self.tftp_dir
63
64     # Use menu.lst in hake/menu.lst.arm_gem5
65     def _write_menu_lst(self, data, path):
66         pass
67
68     def set_bootmodules(self, modules):
69         pass
70
71     def lock(self):
72         pass
73
74     def unlock(self):
75         pass
76
77     def setup(self):
78         pass
79
80     def _get_cmdline(self):
81         raise NotImplementedError
82
83     def _kill_child(self):
84         # terminate child if running
85         if self.child:
86                     try:
87                         os.kill(self.child.pid, signal.SIGTERM)
88                     except OSError, e:
89                         debug.verbose("Caught OSError trying to kill child: %r" % e)
90                     except Exception, e:
91                         debug.verbose("Caught exception trying to kill child: %r" % e)
92                     try:
93                         self.child.wait()
94                     except Exception, e:
95                         debug.verbose("Caught exception while waiting for child: %r" % e)
96                     self.child = None
97
98     def reboot(self):
99         self._kill_child()
100         cmd = self._get_cmdline()
101         debug.verbose('starting "%s" in gem5.py:reboot' % ' '.join(cmd))
102         devnull = open('/dev/null', 'w')
103         # remove ubuntu chroot from environment to make sure gem5 finds the
104         # right shared libraries
105         import os
106         env = dict(os.environ)
107         if 'LD_LIBRARY_PATH' in env:
108             del env['LD_LIBRARY_PATH']
109         self.child = subprocess.Popen(cmd, stderr=devnull, env=env)
110         time.sleep(GEM5_START_TIMEOUT)
111
112     def shutdown(self):
113         debug.verbose('gem5:shutdown requested');
114         debug.verbose('terminating gem5')
115         if not self.child is None:
116                     try:
117                         self.child.terminate()
118                     except OSError, e:
119                         debug.verbose("Error when trying to terminate gem5: %r" % e)
120         debug.verbose('closing telnet connection')
121         if not self.telnet is None:
122             self.output.close()
123             self.telnet.close()
124         # try to cleanup tftp tree if needed
125         if self.tftp_dir and os.path.isdir(self.tftp_dir):
126             shutil.rmtree(self.tftp_dir, ignore_errors=True)
127         self.tftp_dir = None
128
129     def get_output(self):
130         # wait a bit to give gem5 time to listen for a telnet connection
131         if self.child.poll() != None: # Check if child is down
132             print ' '.join(['gem5 is down, return code is ', self.child.returncode])
133             return None
134         # use telnetlib
135         import telnetlib
136         self.telnet_connected = False
137         while not self.telnet_connected:
138             try:
139                 self.telnet = telnetlib.Telnet("localhost", 3456)
140                 self.telnet_connected = True
141                 self.output = self.telnet.get_socket().makefile()
142             except IOError, e:
143                 errno, msg = e
144                 if errno != 111: # connection refused
145                     debug.error("telnet: %s [%d]" % (msg, errno))
146                 else:
147                     self.telnet_connected = False
148             time.sleep(GEM5_START_TIMEOUT)
149
150         return self.output
151
152 class Gem5MachineARM(Gem5MachineBase):
153     def get_bootarch(self):
154         return 'armv7'
155
156     def set_bootmodules(self, modules):
157         # store path to kernel for _get_cmdline to use
158         self.kernel_img = os.path.join(self.options.buildbase, self.options.builds[0].name, 'arm_gem5_image')
159
160         #write menu.lst
161         path = os.path.join(self.get_tftp_dir(), 'menu.lst')
162         self._write_menu_lst(modules.get_menu_data('/'), path)
163
164 # SK: did not test this yet, but should work
165 # @machines.add_machine
166 # class Gem5MachineARMSingleCore(Gem5MachineARM):
167 #     name = 'gem5_arm_1'
168
169 #     def get_ncores(self):
170 #         return 1
171
172 #     def _get_cmdline(self):
173 #         script_path = os.path.join(self.options.sourcedir, 'tools/arm_gem5', 'gem5script.py')
174 #         return (['gem5.fast', script_path, '--kernel=%s'%self.kernel_img, '--n=%s'%self.get_ncores()]
175 #                 + GEM5_CACHES_ENABLE)
176
177 @machines.add_machine
178 class Gem5MachineARMMultiCore(Gem5MachineARM):
179     name = 'armv7_gem5_2'
180
181     def get_bootarch(self):
182         return "armv7"
183
184     def get_ncores(self):
185         return 2
186
187     def get_cores_per_socket(self):
188         return 1
189
190     def _get_cmdline(self):
191         script_path = os.path.join(self.options.sourcedir, 'tools/arm_gem5', 'gem5script.py')
192         return ([GEM5_PATH + 'gem5.fast', script_path, \
193                  '--kernel=%s'%self.kernel_img, \
194                  '--caches', \
195                  '--l2cache', \
196                  '--n=%s' % self.get_ncores()]
197                 + GEM5_CACHES_ENABLE)
198
199 # SK: this will not work, since gem5 uses the menu.lst specified in the arm_gem5_image
200 #    make target. There, only two cores are booted.
201 # @machines.add_machine
202 # class Gem5MachineARMMultiCore(Gem5MachineARM):
203 #     name = 'gem5_arm_4'
204
205 #     def get_ncores(self):
206 #         return 4
207
208 #     def get_cores_per_socket(self):
209 #         return 2
210
211 #     def _get_cmdline(self):
212 #         script_path = os.path.join(self.options.sourcedir, 'tools/arm_gem5', 'gem5script.py')
213 #         return (['gem5.fast', script_path, '--kernel=%s'%self.kernel_img, '--n=%s'%self.get_ncores()]
214 #                 + GEM5_CACHES_ENABLE)