harness: machines: define custom buildall targets for armv7/armv8 machines
[barrelfish] / tools / harness / machines / qemu.py
1 ##########################################################################
2 # Copyright (c) 2009-2016 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, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
8 ##########################################################################
9
10 import os, signal, tempfile, subprocess, shutil
11 import debug
12 from machines import Machine, ARMMachineBase, MachineFactory, MachineOperations
13
14 QEMU_SCRIPT_PATH = 'tools/qemu-wrapper.sh' # relative to source tree
15 GRUB_IMAGE_PATH = 'tools/grub-qemu.img' # relative to source tree
16 QEMU_CMD_X64 = 'qemu-system-x86_64'
17 QEMU_CMD_X32 = 'qemu-system-i386'
18 QEMU_CMD_ARM = 'qemu-system-arm'
19 QEMU_ARGS_GENERIC = '-nographic -no-reboot'.split()
20 QEMU_ARGS_X64 = '-net nic,model=ne2k_pci -net user -m 3084'.split()
21 QEMU_ARGS_X32 = '-net nic,model=ne2k_pci -net user -m 512'.split()
22
23 class QEMUMachineBase(Machine):
24     def __init__(self, options, operations, **kwargs):
25         super(QEMUMachineBase, self).__init__(options, operations, **kwargs)
26
27     def get_tickrate(self):
28         return None
29
30     def get_buildall_target(self):
31         return self.get_bootarch().upper() + "_Full"
32
33     def get_boot_timeout(self):
34         # shorter time limit for running a qemu test
35         # FIXME: ideally this should somehow be expressed in CPU time / cycles
36         return 60
37
38     def get_machine_name(self):
39         return self._name
40
41     def get_serial_binary(self):
42         return "serial_pc16550d"
43
44 class QEMUMachineBaseOperations(MachineOperations):
45
46     def __init__(self, machine):
47         super(QEMUMachineBaseOperations, self).__init__(machine)
48         self.child = None
49         self.tftp_dir = None
50
51     def setup(self):
52         pass
53
54
55     def force_write(self, consolectrl):
56         pass
57
58     def get_tftp_dir(self):
59         if self.tftp_dir is None:
60             debug.verbose('creating temporary directory for QEMU TFTP files')
61             self.tftp_dir = tempfile.mkdtemp(prefix='harness_qemu_')
62             debug.verbose('QEMU TFTP directory is %s' % self.tftp_dir)
63         return self.tftp_dir
64
65     def _write_menu_lst(self, data, path):
66         debug.verbose('writing %s' % path)
67         debug.debug(data)
68         f = open(path, 'w')
69         f.write(data)
70         f.close()
71
72     def set_bootmodules(self, modules):
73         path = os.path.join(self.get_tftp_dir(), 'menu.lst')
74         self._write_menu_lst(modules.get_menu_data('/'), path)
75
76     def lock(self):
77         pass
78
79     def unlock(self):
80         pass
81
82     def _get_cmdline(self):
83         raise NotImplementedError
84
85     def _kill_child(self):
86         # terminate child if running
87         if self.child:
88             os.kill(self.child.pid, signal.SIGTERM)
89             self.child.wait()
90             self.child = None
91         self.masterfd = None
92
93     def reboot(self):
94         self._kill_child()
95         cmd = self._get_cmdline()
96         debug.verbose('starting "%s"' % ' '.join(cmd))
97         import pty
98         (self.masterfd, slavefd) = pty.openpty()
99         self.child = subprocess.Popen(cmd, close_fds=True,
100                                       stdout=slavefd,
101                                       stdin=slavefd)
102         os.close(slavefd)
103         # open in binary mode w/o buffering
104         self.qemu_out = os.fdopen(self.masterfd, 'rb', 0)
105
106     def shutdown(self):
107         self._kill_child()
108         # try to cleanup tftp tree if needed
109         if self.tftp_dir and os.path.isdir(self.tftp_dir):
110             shutil.rmtree(self.tftp_dir, ignore_errors=True)
111         self.tftp_dir = None
112
113     def get_output(self):
114         return self.qemu_out
115
116 class QEMUMachineX64Operations(QEMUMachineBaseOperations):
117     def _get_cmdline(self):
118         qemu_wrapper = os.path.join(self._machine.options.sourcedir, QEMU_SCRIPT_PATH)
119         menu_lst = os.path.join(self.get_tftp_dir(), 'menu.lst')
120         return [ qemu_wrapper, "--menu", menu_lst, "--arch", "x86_64",
121                 "--smp", "%s" % self._machine.get_ncores() ]
122
123     def set_bootmodules(self, modules):
124         path = os.path.join(self.get_tftp_dir(), 'menu.lst')
125         self._write_menu_lst(modules.get_menu_data('/', self.get_tftp_dir()), path)
126
127 class QEMUMachineX32Operations(QEMUMachineBaseOperations):
128     def _get_cmdline(self):
129         grub_image = os.path.join(self.options.sourcedir, GRUB_IMAGE_PATH)
130         s = '-smp %d -fda %s -tftp %s' % (self.get_ncores(), grub_image,
131                                           self.get_tftp_dir())
132         return [QEMU_CMD_X32] + QEMU_ARGS_GENERIC + QEMU_ARGS_X32 + s.split()
133
134 # create 1, 2 and 4 core x86_64 qemu machines
135
136 for n in [1, 2, 4]:
137     class TmpMachine(QEMUMachineBase):
138         def __init__(self, options, _class=None, **kwargs):
139             super(_class, self).__init__(options, QEMUMachineX64Operations(self), **kwargs)
140
141         # 60 seconds per core
142         def get_boot_timeout(self):
143             return self.get_ncores() * 60
144
145         # 120 seconds per core
146         def get_test_timeout(self):
147             return self.get_ncores() * 120
148
149     MachineFactory.addMachine('qemu%d' % n, TmpMachine,
150                               ncores=n,
151                               bootarch='x86_64',
152                               _class=TmpMachine)
153
154 class QEMUMachineARMv7Uniproc(ARMMachineBase):
155     '''Uniprocessor ARMv7 QEMU'''
156     name = 'qemu_armv7'
157
158     imagename = "armv7_a15ve_1_image"
159
160     def __init__(self, options, **kwargs):
161         super(QEMUMachineARMv7Uniproc, self).__init__(options, QEMUMAchineARMv7UniprocOperations(self), **kwargs)
162         self._set_kernel_image()
163
164     def get_ncores(self):
165         return 1
166
167     def get_buildall_target(self):
168         return "VExpressEMM-A15"
169
170     def get_bootarch(self):
171         return "armv7"
172
173     def get_platform(self):
174         return 'a15ve'
175
176 class QEMUMAchineARMv7UniprocOperations(QEMUMachineBaseOperations):
177
178     def _write_menu_lst(self, data, path):
179         self._machine._write_menu_lst(data, path)
180
181     def set_bootmodules(self, modules):
182         # write menu.lst
183         debug.verbose("Writing menu.lst in build directory.")
184         menulst_fullpath = os.path.join(self._machine.options.builds[0].build_dir,
185                 "platforms", "arm", "menu.lst.armv7_a15ve_1")
186         self._write_menu_lst(modules.get_menu_data('/'), menulst_fullpath)
187
188         # produce ROM image
189         debug.verbose("Building QEMU image.")
190         debug.checkcmd(["make", self._machine.imagename],
191                 cwd=self._machine.options.builds[0].build_dir)
192
193     def _get_cmdline(self):
194         qemu_wrapper = os.path.join(self._machine.options.sourcedir, QEMU_SCRIPT_PATH)
195
196         return ([qemu_wrapper, '--arch', 'a15ve', '--image', self._machine.kernel_img])
197
198 MachineFactory.addMachine(QEMUMachineARMv7Uniproc.name, QEMUMachineARMv7Uniproc,
199                           bootarch="armv7",
200                           platform="a15ve")
201
202 class QEMUMachineZynq7(ARMMachineBase):
203     '''Zynq7000 as modelled by QEMU'''
204     name = 'qemu_armv7_zynq7'
205
206     imagename = "armv7_zynq7_image"
207
208     def __init__(self, options, **kwargs):
209         super(QEMUMachineZynq7, self).__init__(options, QEMUMachineZync7Operations(self), **kwargs)
210         self._set_kernel_image()
211         # XXX: change this once we have proper zynq7 configurations
212         self.menulst_template = "menu.lst.armv7_zynq7"
213
214     def get_buildall_target(self):
215         return "Zynq7000"
216
217     def get_ncores(self):
218         return 1
219
220     def get_platform(self):
221         return 'zynq7'
222
223 class QEMUMachineZync7Operations(QEMUMachineBaseOperations):
224
225     def _write_menu_lst(self, data, path):
226         self._machine._write_menu_lst(data, path)
227
228     def set_bootmodules(self, modules):
229         # write menu.lst
230         debug.verbose("Writing menu.lst in build directory.")
231         menulst_fullpath = os.path.join(self._machine.options.builds[0].build_dir,
232                 "platforms", "arm", "menu.lst.armv7_zynq7")
233         self._write_menu_lst(modules.get_menu_data('/'), menulst_fullpath)
234
235         # produce ROM image
236         debug.verbose("Building QEMU image.")
237         debug.checkcmd(["make", self._machine.imagename],
238                 cwd=self._machine.options.builds[0].build_dir)
239
240     def _get_cmdline(self):
241         qemu_wrapper = os.path.join(self._machine.options.sourcedir, QEMU_SCRIPT_PATH)
242
243         return ([qemu_wrapper, '--arch', 'zynq7', '--image', self._machine.kernel_img])
244
245 MachineFactory.addMachine(QEMUMachineZynq7.name, QEMUMachineZynq7,
246                           bootarch="armv7",
247                           platform='zynq7')