1 ##########################################################################
2 # Copyright (c) 2009-2016 ETH Zurich.
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 ##########################################################################
10 import os, debug, signal, shutil, time
13 class Machine(object):
16 def validateArgs(cls, kwargs):
20 raise TypeError("Missing key %s" % e.args[0])
22 def __init__(self, options, operations,
29 cores_per_socket=None,
31 serial_binary="serial_kernel",
33 eth0=(0xff, 0xff, 0xff),
39 self._name = "(unknown)"
40 self.options = options
41 self._operations = operations
43 self._bootarch = bootarch
45 self._machine_name = machine_name
47 if buildarchs is None:
48 buildarchs = [bootarch]
49 self._build_archs = buildarchs
50 assert(bootarch in buildarchs)
54 if cores_per_socket is None:
55 cores_per_socket = ncores
56 self._cores_per_socket = cores_per_socket
58 self._kernel_args = kernel_args
60 self._boot_driver = boot_driver
62 self._serial_binary = serial_binary
64 self._boot_timeout = boot_timeout
66 self._platform = platform
68 self._pci_args = pci_args
72 self._perfcount_type = perfcount_type
74 self._tick_rate = tickrate
77 debug.warning("Machine base class does not understand the " +
78 "following machine arguments: %s" % str(kwargs))
80 def get_machine_name(self):
81 return self._machine_name
83 def get_bootarch(self):
84 """Return the architecture for booting and base system services."""
87 def get_buildarchs(self):
88 """Return the architectures that must be enabled in hake for this machine."""
89 return self._build_archs
91 def get_buildall_target(self):
92 """Return a valid make target to build default set of modules
94 raise NotImplementedError
97 """Returns absolute number of cores."""
100 def get_coreids(self):
101 """Returns the list of core IDs."""
102 return range(0, self.get_ncores()) # default behaviour for x86
104 # XXX: REMOVE ME. used only by lwip_loopback bench
105 def get_cores_per_socket(self):
106 """Returns number of cores per socket."""
107 return self._cores_per_socket
109 def get_tickrate(self):
110 """Returns clock rate in MHz."""
111 raise NotImplementedError
113 def get_perfcount_type(self):
114 """Returns a string ('amd0f', 'amd10', or 'intel'), or None if unknown"""
115 return self._perfcount_type
117 def get_kernel_args(self):
118 """Returns list of machine-specific arguments to add to the kernel command-line"""
119 return self._kernel_args
121 def get_boot_driver(self):
122 """Returns list of machine-specific arguments to add to the kernel command-line"""
123 return self._boot_driver
125 def get_pci_args(self):
126 """Returns list of machine-specific arguments to add to the PCI command-line"""
127 return self._pci_args
129 def get_platform(self):
130 """Returns machine-specific platform specifier"""
131 return self._platform
134 """Returns machine-specific bus:dev:fun for connected network interface of machine"""
135 # 0xff for all three elements should preserve kaluga default behaviour
138 def get_serial_binary(self):
139 """Returns a machine-specific binary name for the serial driver
140 (fallback if not implemented is the kernel serial driver)"""
141 return self._serial_binary
143 def get_boot_timeout(self):
144 """Returns a machine-specific timeout (in seconds), or None for the default"""
145 return self._boot_timeout
147 def get_test_timeout(self):
148 """Returns a machine-specific timeout (in seconds), or None for the default"""
151 def get_tftp_dir(self):
152 """Return a unique TFTP boot directory for this machine."""
153 print("DEPRECATED get_tftp_dir")
154 return self._operations.get_tftp_dir()
156 def set_bootmodules(self, modules):
157 """Set the machine to boot from the given module data."""
158 print("DEPRECATED set_bootmodules")
159 return self._operations.set_bootmodules(modules)
162 """Lock the machine to prevent concurrent access."""
163 print("DEPRECATED lock")
164 return self._operations.lock()
167 """Unlock an already-locked machine."""
168 print("DEPRECATED unlock")
169 return self._operations.unlock()
173 """Prepare a locked machine to be booted."""
174 print("DEPRECATED setup")
175 return self._operations.setup()
178 """Reboot (or boot) the machine."""
179 print("DEPRECATED reboot")
180 return self._operations.reboot()
183 """Shut down/switch off the machine."""
184 print("DEPRECATED shutdown")
185 return self._operations.shutdown()
187 def get_output(self):
188 """Returns a file object to the output of a locked machine."""
189 print("DEPRECATED get_output")
190 return self._operations.get_output()
192 def force_write(self, consolectrl):
193 print("DEPRECATED force_write")
194 return self._operations.force_write(consolectrl)
199 def setName(self, name):
202 def default_bootmodules(self):
203 """Returns the default boot module configuration for the given machine."""
204 # FIXME: clean up / separate platform-specific logic
207 a = machine.get_bootarch()
209 # set the kernel: elver on x86_64
212 elif a == "armv7" or a == "armv8":
213 kernel = "cpu_%s" % machine.get_platform()
217 m = barrelfish.BootModules(machine, prefix=("%s/sbin/" % a), kernel=kernel)
218 m.add_kernel_args(machine.get_kernel_args())
219 # default for all barrelfish archs
220 # hack: cpu driver is not called "cpu" for ARMv7 builds
222 m.add_module("cpu_%s" % machine.get_platform(), machine.get_kernel_args())
227 m.set_cpu_driver(kernel, machine.get_kernel_args())
229 m.set_boot_driver(machine.get_boot_driver())
231 m.add_module("cpu", machine.get_kernel_args())
234 m.add_module("mem_serv")
235 m.add_module("monitor")
236 m.add_module("ramfsd", ["boot"])
237 m.add_module("skb", ["boot"])
238 m.add_module("spawnd", ["boot"])
239 m.add_module("startd", ["boot"])
240 m.add_module("/eclipseclp_ramfs.cpio.gz", ["nospawn"])
241 m.add_module("/skb_ramfs.cpio.gz", ["nospawn"])
242 m.add_module("corectrl", ["auto"])
246 m.add_module("acpi", ["boot"])
247 m.add_module("kaluga", ["boot"])
249 # SKB and PCI are x86-only for the moment
250 if a == "x86_64" or a == "x86_32":
251 m.add_module("acpi", ["boot"])
252 m.add_module("routing_setup", ["boot"])
254 # Add pci with machine-specific extra-arguments
255 m.add_module("pci", ["auto"] + machine.get_pci_args())
257 # Add kaluga with machine-specific bus:dev:fun triplet for eth0
259 m.add_module("kaluga",
260 ["boot", "eth0=%d:%d:%d" % machine.get_eth0()])
262 # coreboot should work on armv7 now
264 m.add_module("kaluga", machine.get_kaluga_args())
267 class MachineOperations(object):
269 def __init__(self, machine):
270 self._machine = machine
272 def get_tftp_dir(self):
273 """Return a unique TFTP boot directory for this machine."""
274 raise NotImplementedError
276 def set_bootmodules(self, modules):
277 """Set the machine to boot from the given module data."""
278 raise NotImplementedError
281 """Lock the machine to prevent concurrent access."""
282 raise NotImplementedError
285 """Unlock an already-locked machine."""
286 raise NotImplementedError
289 """Prepare a locked machine to be booted."""
290 raise NotImplementedError
293 """Reboot (or boot) the machine."""
294 raise NotImplementedError
297 """Shut down/switch off the machine."""
298 raise NotImplementedError
300 def get_output(self):
301 """Returns a file object to the output of a locked machine."""
302 raise NotImplementedError
304 def force_write(self, consolectrl):
305 raise NotImplementedError
307 class MachineLockedError(Exception):
308 """May be raised by lock() when the machine is locked by another user."""
311 class ARMMachineBase(Machine):
314 def validateArgs(cls, kwargs):
315 super(ARMMachineBase, cls).validateArgs(kwargs)
318 except KeyError as e:
319 raise TypeError("Missing key %s" % e.args[0])
321 def __init__(self, options, operations, **kwargs):
322 super(ARMMachineBase, self).__init__(options, operations, **kwargs)
325 self.kernel_args = None
326 self.kaluga_args = None
327 self.menulst_template = "menu.lst." + self.get_bootarch() + "_" + \
328 self.get_platform() + ("_%d" % self.get_ncores())
329 self._set_kernel_image()
331 def _get_template_menu_lst(self):
332 """Read menu lst in source tree"""
333 if self.menulst is None:
334 template_menulst = os.path.join(self.options.sourcedir, "hake",
335 self.menulst_template)
336 with open(template_menulst) as f:
337 self.menulst = f.readlines()
341 def _set_kernel_image(self):
342 if self.options.existingbuild:
343 self.kernel_img = os.path.join(self.options.existingbuild, self.imagename)
345 self.kernel_img = os.path.join(self.options.buildbase,
346 self.options.builds[0].name,
349 def get_kernel_args(self):
350 if self.kernel_args is None:
351 for line in self._get_template_menu_lst():
352 if line.startswith("kernel"):
353 _, _, args = line.strip().split(" ", 2)
354 self.kernel_args = args.split(" ")
355 return self.kernel_args
358 """Grab MMAP data from menu lst in source tree"""
359 if self.mmap is None:
361 for line in self._get_template_menu_lst():
362 if line.startswith("mmap"):
363 self.mmap.append(line)
365 debug.debug("got MMAP:\n %s" % " ".join(self.mmap))
368 def get_kaluga_args(self):
369 if self.kaluga_args is None:
370 for line in self._get_template_menu_lst():
372 _,_,args = line.strip().split(' ', 2)
373 self.kaluga_args = args.split(' ')
375 return self.kaluga_args
377 def _write_menu_lst(self, data, path):
378 debug.verbose('writing %s' % path)
382 for line in self._get_mmap():
386 class ARMSimulatorBase(ARMMachineBase):
388 def __init__(self, options, operations,
389 boot_timeout=20, **kwargs):
390 super(ARMSimulatorBase, self).__init__(options, operations,
391 boot_timeout=boot_timeout,
394 def get_tickrate(self):
397 def get_test_timeout(self):
398 """Default test timeout for ARM simulators: 10min"""
401 def get_machine_name(self):
404 class ARMSimulatorOperations(MachineOperations):
406 def __init__(self, machine):
407 super(ARMSimulatorOperations, self).__init__(machine)
411 self.simulator_start_timeout = 5 # seconds
416 def force_write(self, consolectrl):
425 def get_free_port(self):
427 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
429 # extract port from addrinfo
430 self.telnet_port = s.getsockname()[1]
433 def _get_cmdline(self):
434 raise NotImplementedError
436 def _kill_child(self):
437 # terminate child if running
440 os.kill(self.child.pid, signal.SIGTERM)
442 debug.verbose("Caught OSError trying to kill child: %r" % e)
444 debug.verbose("Caught exception trying to kill child: %r" % e)
449 "Caught exception while waiting for child: %r" % e)
453 debug.verbose('Simulator:shutdown requested');
454 debug.verbose('terminating simulator')
455 if not self.child is None:
457 self.child.terminate()
459 debug.verbose("Error when trying to terminate simulator: %r" % e)
460 debug.verbose('closing telnet connection')
461 if self.telnet is not None:
464 # try to cleanup tftp tree if needed
465 if self.tftp_dir and os.path.isdir(self.tftp_dir):
466 shutil.rmtree(self.tftp_dir, ignore_errors=True)
469 def get_output(self):
470 # wait a bit to give the simulator time to listen for a telnet connection
471 if self.child.poll() != None: # Check if child is down
472 print 'Simulator is down, return code is %d' % self.child.returncode
476 self.telnet_connected = False
477 while not self.telnet_connected:
479 self.telnet = telnetlib.Telnet("localhost", self.telnet_port)
480 self.telnet_connected = True
481 self.output = self.telnet.get_socket().makefile()
484 if errno != 111: # connection refused
485 debug.error("telnet: %s [%d]" % (msg, errno))
487 self.telnet_connected = False
488 time.sleep(self.simulator_start_timeout)
492 class MachineFactory:
494 machineFactories = {}
496 def __init__(self, name, machineClass, kwargs):
497 self._class = machineClass
498 self._kwargs = kwargs
502 def addMachine(cls, name, machineClass, **kwargs):
503 cls.machineFactories[name] = MachineFactory(name, machineClass, kwargs)
504 machineClass.validateArgs(kwargs)
507 """Get the name of the machine produced by this factory."""
510 def createMachine(self, options):
511 """Create a new machine instance."""
513 machine = self._class(options, **self._kwargs)
514 except TypeError as e:
515 print("Machine class %s failed to instantiate: %s" % (str(self._class), str(e)))
517 machine.setName(self._name)
521 def createMachineByName(cls, name, options):
522 """Create a new machine instance."""
523 return cls.machineFactories[name].createMachine(options)
525 # Assume that QEMU, FVP, pandaboard and Gem5 work everywhere if invoked
531 # Other site-specific modules will be loaded by the siteconfig module