-Wimplicit-function-declaration is only for C
[helenos.git] / tools / ew.py
blobcf6de127e9dbd0681d97104957290a887f166938
1 #!/usr/bin/env python3
3 # Copyright (c) 2013 Jakub Jermar
4 # All rights reserved.
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
10 # - Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # - Redistributions in binary form must reproduce the above copyright
13 # notice, this list of conditions and the following disclaimer in the
14 # documentation and/or other materials provided with the distribution.
15 # - The name of the author may not be used to endorse or promote products
16 # derived from this software without specific prior written permission.
18 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 """
32 Emulator wrapper for running HelenOS
33 """
35 import inspect
36 import os
37 import platform
38 import re
39 import subprocess
40 import sys
41 import _thread
42 import time
44 overrides = {}
46 CONFIG = 'Makefile.config'
48 TOOLS_DIR = os.path.dirname(inspect.getabsfile(inspect.currentframe()))
50 def read_config():
51 "Read HelenOS build configuration"
53 inf = open(CONFIG, 'r')
54 config = {}
56 for line in inf:
57 res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
58 if (res):
59 config[res.group(1)] = res.group(2)
61 inf.close()
62 return config
64 def is_override(str):
65 if str in overrides.keys():
66 return overrides[str]
67 return False
69 def cfg_get(platform, machine, processor):
70 if machine == "" or "run" in emulators[platform]:
71 return emulators[platform]
72 elif processor == "" or "run" in emulators[platform][machine]:
73 return emulators[platform][machine]
74 else:
75 return emulators[platform][machine][processor]
77 def termemu_detect():
78 emus = ['gnome-terminal', 'xfce4-terminal', 'xterm']
79 for termemu in emus:
80 try:
81 subprocess.check_output('which ' + termemu, shell = True)
82 return termemu
83 except:
84 pass
86 print('Could not find any of the terminal emulators %s.'%(emus))
87 sys.exit(1)
89 def run_in_console(cmd, title):
90 temu = termemu_detect()
91 if temu == 'gnome-terminal':
92 cmdline = temu + ' -- ' + cmd
93 else:
94 ecmd = cmd.replace('"', '\\"')
95 cmdline = temu + ' -T ' + '"' + title + '"' + ' -e "' + ecmd + '"'
97 print(cmdline)
98 if not is_override('dryrun'):
99 subprocess.call(cmdline, shell = True)
101 def get_host_native_width():
102 return int(platform.architecture()[0].strip('bit'))
104 def pc_options(guest_width):
105 opts = ''
107 # Do not enable KVM if running 64 bits HelenOS
108 # on 32 bits host
109 host_width = get_host_native_width()
110 if guest_width <= host_width and not is_override('nokvm'):
111 opts = opts + ' -enable-kvm'
113 # Remove the leading space
114 return opts[1:]
116 def malta_options():
117 return '-cpu 4Kc -append "console=devices/\\hw\\pci0\\00:0a.0\\com1\\a"'
119 def find_firmware(name, environ_var, default_paths, extra_info=None):
120 """Find firmware image(s)."""
122 if environ_var in os.environ:
123 return os.environ[environ_var]
125 for path in default_paths:
126 if os.path.exists(path):
127 return path
129 sys.stderr.write("Cannot find %s binary image(s)!\n" % name)
130 sys.stderr.write(
131 "Either set %s environment variable accordingly or place the image(s) in one of the default locations: %s.\n" %
132 (environ_var, ", ".join(default_paths)))
133 if extra_info is not None:
134 sys.stderr.write(extra_info)
135 return None
137 def platform_to_qemu_options(platform, machine, processor):
138 if platform == 'amd64':
139 return 'system-x86_64', pc_options(64)
140 elif platform == 'arm32':
141 if machine == 'integratorcp':
142 return 'system-arm', '-M integratorcp'
143 elif machine == 'raspberrypi':
144 return 'system-arm', '-M raspi1ap'
145 elif platform == 'arm64':
146 if machine == 'virt':
147 # Search for the EDK2 firmware image
148 default_paths = (
149 '/usr/local/qemu-efi-aarch64/QEMU_EFI.fd', # Custom
150 '/usr/share/edk2/aarch64/QEMU_EFI.fd', # Fedora
151 '/usr/share/qemu-efi-aarch64/QEMU_EFI.fd', # Ubuntu
153 extra_info = ("Pre-compiled binary can be obtained from "
154 "http://snapshots.linaro.org/components/kernel/leg-virt-tianocore-edk2-upstream/latest/QEMU-AARCH64/RELEASE_GCC5/QEMU_EFI.fd.\n")
155 efi_path = find_firmware(
156 "EDK2", 'EW_QEMU_EFI_AARCH64', default_paths, extra_info)
157 if efi_path is None:
158 raise Exception
160 return 'system-aarch64', \
161 '-M virt -cpu cortex-a57 -m 1024 -bios %s' % efi_path
162 elif platform == 'ia32':
163 return 'system-i386', pc_options(32)
164 elif platform == 'mips32':
165 if machine == 'lmalta':
166 return 'system-mipsel', malta_options()
167 elif machine == 'bmalta':
168 return 'system-mips', malta_options()
169 elif platform == 'ppc32':
170 return 'system-ppc', '-m 256'
171 elif platform == 'sparc64':
172 if machine != 'generic':
173 raise Exception
174 if processor == 'us':
175 cmdline = '-M sun4u'
176 if is_override('nographic'):
177 cmdline += ' --prom-env boot-args="console=devices/\\hw\\pci0\\01:01.0\\com1\\a"'
178 return 'system-sparc64', cmdline
180 # processor = 'sun4v'
181 opensparc_bins = find_firmware(
182 "OpenSPARC", 'OPENSPARC_BINARIES',
183 ('/usr/local/opensparc/image/', ))
184 if opensparc_bins is None:
185 raise Exception
187 return 'system-sparc64', '-M niagara -m 256 -L %s' % (opensparc_bins)
190 def hdisk_mk():
191 if not os.path.exists('hdisk.img'):
192 subprocess.call(TOOLS_DIR + '/mkfat.py 1048576 dist/data hdisk.img', shell = True)
194 def qemu_bd_options():
195 if is_override('nohdd'):
196 return ''
198 hdisk_mk()
200 hdd_options = ''
201 if 'hdd' in overrides.keys():
202 if 'ata' in overrides['hdd'].keys():
203 hdd_options += ''
204 elif 'virtio-blk' in overrides['hdd'].keys():
205 hdd_options += ',if=virtio'
207 return ' -drive file=hdisk.img,index=0,media=disk,format=raw' + hdd_options
209 def qemu_nic_ne2k_options():
210 return ' -device ne2k_isa,irq=5,netdev=n1'
212 def qemu_nic_e1k_options():
213 return ' -device e1000,netdev=n1'
215 def qemu_nic_rtl8139_options():
216 return ' -device rtl8139,netdev=n1'
218 def qemu_nic_virtio_options():
219 return ' -device virtio-net,netdev=n1'
221 def qemu_net_options():
222 if is_override('nonet'):
223 return ''
225 nic_options = ''
226 if 'net' in overrides.keys():
227 if 'e1k' in overrides['net'].keys():
228 nic_options += qemu_nic_e1k_options()
229 if 'rtl8139' in overrides['net'].keys():
230 nic_options += qemu_nic_rtl8139_options()
231 if 'ne2k' in overrides['net'].keys():
232 nic_options += qemu_nic_ne2k_options()
233 if 'virtio-net' in overrides['net'].keys():
234 nic_options += qemu_nic_virtio_options()
235 else:
236 # Use the default NIC
237 nic_options += qemu_nic_e1k_options()
239 return nic_options + ' -netdev user,id=n1,hostfwd=udp::8080-:8080,hostfwd=udp::8081-:8081,hostfwd=tcp::8080-:8080,hostfwd=tcp::8081-:8081,hostfwd=tcp::2223-:2223'
241 def qemu_usb_options():
242 if is_override('nousb'):
243 return ''
244 return ' -usb'
246 def qemu_xhci_options():
247 if is_override('noxhci'):
248 return ''
249 return ' -device nec-usb-xhci,id=xhci'
251 def qemu_tablet_options():
252 if is_override('notablet') or (is_override('nousb') and is_override('noxhci')):
253 return ''
254 return ' -device usb-tablet'
256 def qemu_audio_options():
257 if is_override('nosnd'):
258 return ''
259 return ' -device intel-hda -device hda-duplex'
261 def qemu_run(platform, machine, processor):
262 cfg = cfg_get(platform, machine, processor)
263 suffix, options = platform_to_qemu_options(platform, machine, processor)
264 cmd = 'qemu-' + suffix
266 cmdline = cmd
267 if 'qemu_path' in overrides.keys():
268 cmdline = overrides['qemu_path'] + cmd
270 if options != '':
271 cmdline += ' ' + options
273 if (not 'hdd' in cfg.keys() or cfg['hdd']):
274 cmdline += qemu_bd_options()
275 if (not 'net' in cfg.keys()) or cfg['net']:
276 cmdline += qemu_net_options()
277 if (not 'usb' in cfg.keys()) or cfg['usb']:
278 cmdline += qemu_usb_options()
279 if (not 'xhci' in cfg.keys()) or cfg['xhci']:
280 cmdline += qemu_xhci_options()
281 if (not 'tablet' in cfg.keys()) or cfg['tablet']:
282 cmdline += qemu_tablet_options()
283 if (not 'audio' in cfg.keys()) or cfg['audio']:
284 cmdline += qemu_audio_options()
286 console = ('console' in cfg.keys() and cfg['console'])
288 if (is_override('nographic')):
289 cmdline += ' -nographic'
290 console = True
292 if (not console and (not is_override('nographic')) and not is_override('noserial')):
293 cmdline += ' -serial stdio'
295 if (is_override('bigmem')):
296 cmdline += ' -m 4G'
298 if cfg['image'] == 'image.iso':
299 cmdline += ' -boot d -cdrom image.iso'
300 elif cfg['image'] == 'image.iso@arm64':
301 # Define image.iso cdrom backend.
302 cmdline += ' -drive if=none,file=image.iso,id=cdrom,media=cdrom'
303 # Define scsi bus.
304 cmdline += ' -device virtio-scsi-device'
305 # Define cdrom frontend connected to this scsi bus.
306 cmdline += ' -device scsi-cd,drive=cdrom'
307 elif cfg['image'] == 'image.boot':
308 cmdline += ' -kernel image.boot'
309 elif cfg['image'] == 'kernel.img@rpi':
310 cmdline += ' -bios boot/image.boot.bin'
311 else:
312 cmdline += ' ' + cfg['image']
314 if console:
315 cmdline += ' -nographic'
317 title = 'HelenOS/' + platform
318 if machine != '':
319 title += ' on ' + machine
320 if 'expect' in cfg.keys():
321 cmdline = 'expect -c \'spawn %s; expect "%s" { send "%s" } timeout exp_continue; interact\'' % (cmdline, cfg['expect']['src'], cfg['expect']['dst'])
322 run_in_console(cmdline, title)
323 else:
324 print(cmdline)
325 if not is_override('dryrun'):
326 subprocess.call(cmdline, shell = True)
328 def ski_run(platform, machine, processor):
329 run_in_console('ski -i ' + TOOLS_DIR + '/conf/ski.conf', 'HelenOS/ia64 on ski')
331 def msim_run(platform, machine, processor):
332 hdisk_mk()
333 run_in_console('msim -c ' + TOOLS_DIR + '/conf/msim.conf', 'HelenOS/mips32 on msim')
335 def spike_run(platform, machine, processor):
336 run_in_console('spike -m1073741824:1073741824 image.boot', 'HelenOS/risvc64 on Spike')
338 emulators = {
339 'amd64' : {
340 'run' : qemu_run,
341 'image' : 'image.iso'
343 'arm32' : {
344 'integratorcp' : {
345 'run' : qemu_run,
346 'image' : 'image.boot',
347 'net' : False,
348 'audio' : False,
349 'xhci' : False,
350 'tablet' : False
352 'raspberrypi' : {
353 'run' : qemu_run,
354 'image' : 'kernel.img@rpi',
355 'audio' : False,
356 'console' : True,
357 'hdd' : False,
358 'net' : False,
359 'tablet' : False,
360 'usb' : False,
361 'xhci' : False
364 'arm64' : {
365 'virt' : {
366 'run' : qemu_run,
367 'image' : 'image.iso@arm64',
368 'audio' : False,
369 'console' : True,
370 'hdd' : False,
371 'net' : False,
372 'tablet' : False,
373 'usb' : False,
374 'xhci' : False
377 'ia32' : {
378 'run' : qemu_run,
379 'image' : 'image.iso'
381 'ia64' : {
382 'ski' : {
383 'run' : ski_run
386 'mips32' : {
387 'msim' : {
388 'run' : msim_run
390 'lmalta' : {
391 'run' : qemu_run,
392 'image' : 'image.boot',
393 'console' : True
395 'bmalta' : {
396 'run' : qemu_run,
397 'image' : 'image.boot',
398 'console' : True
401 'ppc32' : {
402 'run' : qemu_run,
403 'image' : 'image.iso',
404 'audio' : False
406 'riscv64' : {
407 'run' : spike_run,
408 'image' : 'image.boot'
410 'sparc64' : {
411 'generic' : {
412 'us' : {
413 'run' : qemu_run,
414 'image' : 'image.iso',
415 'audio' : False,
416 'console' : False,
417 'net' : False,
418 'usb' : False,
419 'xhci' : False,
420 'tablet' : False
422 'sun4v' : {
423 'run' : qemu_run,
424 # QEMU 8.0.0 ignores the 'file' argument and instead uses 'id',
425 # which defaults to 'pflash0', but can be changed to the same value
426 # as 'file'
427 'image' : '-drive if=pflash,id=image.iso,readonly=on,file=image.iso',
428 'audio' : False,
429 'console' : True,
430 'net' : False,
431 'usb' : False,
432 'xhci' : False,
433 'tablet' : False,
434 'expect' : {
435 'src' : 'ok ',
436 'dst' : 'boot\n'
443 def usage():
444 print("%s - emulator wrapper for running HelenOS\n" % os.path.basename(sys.argv[0]))
445 print("%s [-d] [-h] [-net e1k|rtl8139|ne2k|virtio-net] [-hdd ata|virtio-blk] [-nohdd] [-nokvm] [-nonet] [-nosnd] [-nousb] [-noxhci] [-notablet]\n" %
446 os.path.basename(sys.argv[0]))
447 print("-d\tDry run: do not run the emulation, just print the command line.")
448 print("-h\tPrint the usage information and exit.")
449 print("-nohdd\tDisable hard disk, if applicable.")
450 print("-nokvm\tDisable KVM, if applicable.")
451 print("-nonet\tDisable networking support, if applicable.")
452 print("-nosnd\tDisable sound, if applicable.")
453 print("-nousb\tDisable USB support, if applicable.")
454 print("-noxhci\tDisable XHCI support, if applicable.")
455 print("-notablet\tDisable USB tablet (use only relative-position PS/2 mouse instead), if applicable.")
456 print("-nographic\tDisable graphical output. Serial port output must be enabled for this to be useful.")
457 print("-noserial\tDisable serial port output in the terminal.")
458 print("-bigmem\tSets maximum RAM size to 4GB.")
460 def fail(platform, machine):
461 print("Cannot start emulation for the chosen configuration. (%s/%s)" % (platform, machine))
464 def run():
465 expect_nic = False
466 expect_hdd = False
467 expect_qemu = False
469 for i in range(1, len(sys.argv)):
471 if expect_nic:
472 expect_nic = False
473 if not 'net' in overrides.keys():
474 overrides['net'] = {}
475 if sys.argv[i] == 'e1k':
476 overrides['net']['e1k'] = True
477 elif sys.argv[i] == 'rtl8139':
478 overrides['net']['rtl8139'] = True
479 elif sys.argv[i] == 'ne2k':
480 overrides['net']['ne2k'] = True
481 elif sys.argv[i] == 'virtio-net':
482 overrides['net']['virtio-net'] = True
483 else:
484 usage()
485 exit()
486 continue
488 if expect_hdd:
489 expect_hdd = False
490 if not 'hdd' in overrides.keys():
491 overrides['hdd'] = {}
492 if sys.argv[i] == 'ata':
493 overrides['hdd']['ata'] = True
494 elif sys.argv[i] == 'virtio-blk':
495 overrides['hdd']['virtio-blk'] = True
496 else:
497 usage()
498 exit()
499 continue
501 if expect_qemu:
502 expect_qemu = False
503 overrides['qemu_path'] = sys.argv[i]
505 elif sys.argv[i] == '-h':
506 usage()
507 exit()
508 elif sys.argv[i] == '-d':
509 overrides['dryrun'] = True
510 elif sys.argv[i] == '-net' and i < len(sys.argv) - 1:
511 expect_nic = True
512 elif sys.argv[i] == '-hdd' and i < len(sys.argv) - 1:
513 expect_hdd = True
514 elif sys.argv[i] == '-nohdd':
515 overrides['nohdd'] = True
516 elif sys.argv[i] == '-nokvm':
517 overrides['nokvm'] = True
518 elif sys.argv[i] == '-nonet':
519 overrides['nonet'] = True
520 elif sys.argv[i] == '-nosnd':
521 overrides['nosnd'] = True
522 elif sys.argv[i] == '-nousb':
523 overrides['nousb'] = True
524 elif sys.argv[i] == '-noxhci':
525 overrides['noxhci'] = True
526 elif sys.argv[i] == '-notablet':
527 overrides['notablet'] = True
528 elif sys.argv[i] == '-nographic':
529 overrides['nographic'] = True
530 elif sys.argv[i] == '-bigmem':
531 overrides['bigmem'] = True
532 elif sys.argv[i] == '-noserial':
533 overrides['noserial'] = True
534 elif sys.argv[i] == '-qemu_path' and i < len(sys.argv) - 1:
535 expect_qemu = True
536 else:
537 usage()
538 exit()
540 config = read_config()
542 if 'PLATFORM' in config.keys():
543 platform = config['PLATFORM']
544 else:
545 platform = ''
547 if 'MACHINE' in config.keys():
548 mach = config['MACHINE']
549 else:
550 mach = ''
552 if 'PROCESSOR' in config.keys():
553 processor = config['PROCESSOR']
554 else:
555 processor = ''
557 try:
558 emu_run = cfg_get(platform, mach, processor)['run']
559 emu_run(platform, mach, processor)
560 except:
561 fail(platform, mach)
562 return
564 run()