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 10import os, debug, signal, shutil, time 11import barrelfish 12 13class Machine(object): 14 15 @classmethod 16 def validateArgs(cls, kwargs): 17 try: 18 kwargs['bootarch'] 19 except KeyError as e: 20 raise TypeError("Missing key %s" % e.args[0]) 21 22 def __init__(self, options, operations, 23 bootarch=None, 24 machine_name=None, 25 boot_timeout=360, 26 platform=None, 27 buildarchs=None, 28 ncores=1, 29 cores_per_socket=None, 30 kernel_args=[], 31 serial_binary="serial_kernel", 32 pci_args=[], 33 acpi_args=[], 34 eth0=(0xff, 0xff, 0xff), 35 perfcount_type=None, 36 boot_driver = None, 37 tickrate = 0, 38 uboot = False, 39 **kwargs): 40 41 self._name = "(unknown)" 42 self.options = options 43 self._operations = operations 44 45 self._bootarch = bootarch 46 47 self._machine_name = machine_name 48 49 if buildarchs is None: 50 buildarchs = [bootarch] 51 self._build_archs = buildarchs 52 assert(bootarch in buildarchs) 53 54 self._ncores = ncores 55 56 if cores_per_socket is None: 57 cores_per_socket = ncores 58 self._cores_per_socket = cores_per_socket 59 60 self._kernel_args = kernel_args 61 62 self._boot_driver = boot_driver 63 64 self._serial_binary = serial_binary 65 66 self._boot_timeout = boot_timeout 67 68 self._platform = platform 69 70 self._pci_args = pci_args 71 72 self._acpi_args = acpi_args 73 74 self._eth0 = eth0 75 76 self._perfcount_type = perfcount_type 77 78 self._tick_rate = tickrate 79 self._uboot = uboot 80 81 if bool(kwargs): 82 debug.warning("Machine base class does not understand the " + 83 "following machine arguments: %s" % str(kwargs)) 84 85 def get_machine_name(self): 86 return self._machine_name 87 88 def get_bootarch(self): 89 """Return the architecture for booting and base system services.""" 90 return self._bootarch 91 92 def get_buildarchs(self): 93 """Return the architectures that must be enabled in hake for this machine.""" 94 return self._build_archs 95 96 def get_buildall_target(self): 97 """Return a valid make target to build default set of modules 98 (previously 'all')""" 99 raise NotImplementedError 100 101 def get_ncores(self): 102 """Returns absolute number of cores.""" 103 return self._ncores 104 105 def get_coreids(self): 106 """Returns the list of core IDs.""" 107 return range(0, self.get_ncores()) # default behaviour for x86 108 109 # XXX: REMOVE ME. used only by lwip_loopback bench 110 def get_cores_per_socket(self): 111 """Returns number of cores per socket.""" 112 return self._cores_per_socket 113 114 def get_tickrate(self): 115 """Returns clock rate in MHz.""" 116 raise NotImplementedError 117 118 def get_perfcount_type(self): 119 """Returns a string ('amd0f', 'amd10', or 'intel'), or None if unknown""" 120 return self._perfcount_type 121 122 def get_kernel_args(self): 123 """Returns list of machine-specific arguments to add to the kernel command-line""" 124 return self._kernel_args 125 126 def get_boot_driver(self): 127 """Returns list of machine-specific arguments to add to the kernel command-line""" 128 return self._boot_driver 129 130 def get_pci_args(self): 131 """Returns list of machine-specific arguments to add to the PCI command-line""" 132 return self._pci_args 133 134 def get_acpi_args(self): 135 """Returns list of machine-specific arguments to add to the ACPI command-line""" 136 return self._acpi_args 137 138 def get_platform(self): 139 """Returns machine-specific platform specifier""" 140 return self._platform 141 142 def get_eth0(self): 143 """Returns machine-specific bus:dev:fun for connected network interface of machine""" 144 # 0xff for all three elements should preserve kaluga default behaviour 145 return self._eth0 146 147 def get_serial_binary(self): 148 """Returns a machine-specific binary name for the serial driver 149 (fallback if not implemented is the kernel serial driver)""" 150 return self._serial_binary 151 152 def get_boot_timeout(self): 153 """Returns a machine-specific timeout (in seconds), or None for the default""" 154 return self._boot_timeout 155 156 def get_test_timeout(self): 157 """Returns a machine-specific timeout (in seconds), or None for the default""" 158 return None 159 160 def get_tftp_dir(self): 161 """Return a unique TFTP boot directory for this machine.""" 162 print("DEPRECATED get_tftp_dir") 163 return self._operations.get_tftp_dir() 164 165 def set_bootmodules(self, modules): 166 """Set the machine to boot from the given module data.""" 167 print("DEPRECATED set_bootmodules") 168 return self._operations.set_bootmodules(modules) 169 170 def lock(self): 171 """Lock the machine to prevent concurrent access.""" 172 print("DEPRECATED lock") 173 return self._operations.lock() 174 175 def unlock(self): 176 """Unlock an already-locked machine.""" 177 print("DEPRECATED unlock") 178 return self._operations.unlock() 179 180 181 def setup(self): 182 """Prepare a locked machine to be booted.""" 183 print("DEPRECATED setup") 184 return self._operations.setup() 185 186 def reboot(self): 187 """Reboot (or boot) the machine.""" 188 print("DEPRECATED reboot") 189 return self._operations.reboot() 190 191 def shutdown(self): 192 """Shut down/switch off the machine.""" 193 print("DEPRECATED shutdown") 194 return self._operations.shutdown() 195 196 def get_output(self): 197 """Returns a file object to the output of a locked machine.""" 198 print("DEPRECATED get_output") 199 return self._operations.get_output() 200 201 def force_write(self, consolectrl): 202 print("DEPRECATED force_write") 203 return self._operations.force_write(consolectrl) 204 205 def getName(self): 206 return self._name 207 208 def setName(self, name): 209 self._name = name 210 211 def default_bootmodules(self): 212 """Returns the default boot module configuration for the given machine.""" 213 # FIXME: clean up / separate platform-specific logic 214 215 machine = self 216 a = machine.get_bootarch() 217 218 # set the kernel: elver on x86_64 219 if a == "x86_64": 220 kernel = "elver" 221 elif a == "armv7" or a == "armv8": 222 kernel = "cpu_%s" % machine.get_platform() 223 else: 224 kernel = "cpu" 225 226 m = barrelfish.BootModules(machine, prefix=("%s/sbin/" % a), kernel=kernel) 227 m.add_kernel_args(machine.get_kernel_args()) 228 # default for all barrelfish archs 229 # hack: cpu driver is not called "cpu" for ARMv7 builds 230 if a == "armv7" : 231 m.add_module("cpu_%s" % machine.get_platform(), machine.get_kernel_args()) 232 elif a == "armv8" : 233 # remove kernel 234 m.set_kernel(None) 235 # add cpu driver 236 m.set_cpu_driver(kernel, machine.get_kernel_args()) 237 # add boot driver 238 m.set_boot_driver(machine.get_boot_driver()) 239 else : 240 m.add_module("cpu", machine.get_kernel_args()) 241 242 m.add_module("init") 243 m.add_module("mem_serv") 244 m.add_module("monitor") 245 m.add_module("ramfsd", ["boot"]) 246 m.add_module("skb", ["boot"]) 247 m.add_module("proc_mgmt", ["boot"]) 248 m.add_module("spawnd", ["boot"]) 249 m.add_module("startd", ["boot"]) 250 m.add_module("/eclipseclp_ramfs.cpio.gz", ["nospawn"]) 251 m.add_module("/skb_ramfs.cpio.gz", ["nospawn"]) 252 m.add_module("corectrl", ["auto"]) 253 254 # armv8 255 if a == "armv8" : 256 if not machine._uboot: # no ACPI on U-Boot 257 m.add_module("acpi", ["boot"]) 258 m.add_module("kaluga", ["boot"]) 259 260 # SKB and PCI are x86-only for the moment 261 if a == "x86_64" or a == "x86_32": 262 # Add acpi with machine-specific extra-arguments 263 m.add_module("acpi", ["boot"] + machine.get_acpi_args()) 264 m.add_module("routing_setup", ["boot"]) 265 266 # Add pci with machine-specific extra-arguments 267 m.add_module("pci", ["auto"] + machine.get_pci_args()) 268 269 # Add kaluga with machine-specific bus:dev:fun triplet for eth0 270 # interface 271 m.add_module("kaluga", 272 ["boot", "eth0=%d:%d:%d" % machine.get_eth0()]) 273 274 # coreboot should work on armv7 now 275 if a == "armv7": 276 m.add_module("kaluga", machine.get_kaluga_args()) 277 m.add_module("driverdomain_pl390", ["auto"]) 278 m.add_module("serial_kernel", ["auto"]) 279 m.add_module("serial_pl011", ["auto"]) 280 m.add_module("int_route", []) 281 282 return m 283 284class MachineOperations(object): 285 286 def __init__(self, machine): 287 self._machine = machine 288 289 def get_tftp_dir(self): 290 """Return a unique TFTP boot directory for this machine.""" 291 raise NotImplementedError 292 293 def set_bootmodules(self, modules): 294 """Set the machine to boot from the given module data.""" 295 raise NotImplementedError 296 297 def lock(self): 298 """Lock the machine to prevent concurrent access.""" 299 raise NotImplementedError 300 301 def unlock(self): 302 """Unlock an already-locked machine.""" 303 raise NotImplementedError 304 305 def setup(self): 306 """Prepare a locked machine to be booted.""" 307 raise NotImplementedError 308 309 def reboot(self): 310 """Reboot (or boot) the machine.""" 311 raise NotImplementedError 312 313 def shutdown(self): 314 """Shut down/switch off the machine.""" 315 raise NotImplementedError 316 317 def get_output(self): 318 """Returns a file object to the output of a locked machine.""" 319 raise NotImplementedError 320 321 def force_write(self, consolectrl): 322 raise NotImplementedError 323 324class MachineLockedError(Exception): 325 """May be raised by lock() when the machine is locked by another user.""" 326 pass 327 328class ARMMachineBase(Machine): 329 330 @classmethod 331 def validateArgs(cls, kwargs): 332 super(ARMMachineBase, cls).validateArgs(kwargs) 333 try: 334 kwargs['platform'] 335 except KeyError as e: 336 raise TypeError("Missing key %s" % e.args[0]) 337 338 def __init__(self, options, operations, **kwargs): 339 super(ARMMachineBase, self).__init__(options, operations, **kwargs) 340 self.menulst = None 341 self.mmap = None 342 self.kernel_args = None 343 self.kaluga_args = None 344 self.menulst_template = "menu.lst." + self.get_bootarch() + "_" + \ 345 self.get_platform() + ("_%d" % self.get_ncores()) 346 self._set_kernel_image() 347 348 def _get_template_menu_lst(self): 349 """Read menu lst in source tree""" 350 if self.menulst is None: 351 template_menulst = os.path.join(self.options.sourcedir, "hake", 352 self.menulst_template) 353 with open(template_menulst) as f: 354 self.menulst = f.readlines() 355 356 return self.menulst 357 358 def _set_kernel_image(self): 359 if self.options.existingbuild: 360 self.kernel_img = os.path.join(self.options.existingbuild, self.imagename) 361 else: 362 self.kernel_img = os.path.join(self.options.buildbase, 363 self.options.builds[0].name, 364 self.imagename) 365 366 def get_kernel_args(self): 367 if self.kernel_args is None: 368 for line in self._get_template_menu_lst(): 369 if line.startswith("kernel") or line.startswith("cpudriver"): 370 pts = line.strip().split(" ", 2) 371 if len(pts) == 3: 372 self.kernel_args = pts[-1].split(" ") 373 else: 374 self.kernel_args = [] 375 return self.kernel_args 376 377 def _get_mmap(self): 378 """Grab MMAP data from menu lst in source tree""" 379 if self.mmap is None: 380 self.mmap = [] 381 for line in self._get_template_menu_lst(): 382 if line.startswith("mmap"): 383 self.mmap.append(line) 384 385 debug.debug("got MMAP:\n %s" % " ".join(self.mmap)) 386 return self.mmap 387 388 def get_kaluga_args(self): 389 if self.kaluga_args is None: 390 for line in self._get_template_menu_lst(): 391 if 'kaluga' in line: 392 _,_,args = line.strip().split(' ', 2) 393 self.kaluga_args = args.split(' ') 394 break 395 return self.kaluga_args 396 397 def _write_menu_lst(self, data, path): 398 debug.verbose('writing %s' % path) 399 debug.debug(data) 400 f = open(path, 'w') 401 f.write(data) 402 for line in self._get_mmap(): 403 f.write(line) 404 f.close() 405 406class ARMSimulatorBase(ARMMachineBase): 407 408 def __init__(self, options, operations, 409 boot_timeout=20, **kwargs): 410 super(ARMSimulatorBase, self).__init__(options, operations, 411 boot_timeout=boot_timeout, 412 **kwargs) 413 414 def get_tickrate(self): 415 return None 416 417 def get_test_timeout(self): 418 """Default test timeout for ARM simulators: 10min""" 419 return 10 * 60 420 421 def get_machine_name(self): 422 return self.name 423 424class ARMSimulatorOperations(MachineOperations): 425 426 def __init__(self, machine): 427 super(ARMSimulatorOperations, self).__init__(machine) 428 self.child = None 429 self.telnet = None 430 self.tftp_dir = None 431 self.simulator_start_timeout = 5 # seconds 432 433 def setup(self): 434 pass 435 436 def force_write(self, consolectrl): 437 pass 438 439 def lock(self): 440 pass 441 442 def unlock(self): 443 pass 444 445 def get_free_port(self): 446 import socket 447 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 448 s.bind(('', 0)) 449 # extract port from addrinfo 450 self.telnet_port = s.getsockname()[1] 451 s.close() 452 453 def _get_cmdline(self): 454 raise NotImplementedError 455 456 def _kill_child(self): 457 # terminate child if running 458 if self.child: 459 try: 460 os.kill(self.child.pid, signal.SIGTERM) 461 except OSError, e: 462 debug.verbose("Caught OSError trying to kill child: %r" % e) 463 except Exception, e: 464 debug.verbose("Caught exception trying to kill child: %r" % e) 465 try: 466 self.child.wait() 467 except Exception, e: 468 debug.verbose( 469 "Caught exception while waiting for child: %r" % e) 470 self.child = None 471 472 def shutdown(self): 473 debug.verbose('Simulator:shutdown requested'); 474 debug.verbose('terminating simulator') 475 if not self.child is None: 476 try: 477 self.child.terminate() 478 except OSError, e: 479 debug.verbose("Error when trying to terminate simulator: %r" % e) 480 debug.verbose('closing telnet connection') 481 if self.telnet is not None: 482 self.output.close() 483 self.telnet.close() 484 # try to cleanup tftp tree if needed 485 if self.tftp_dir and os.path.isdir(self.tftp_dir): 486 shutil.rmtree(self.tftp_dir, ignore_errors=True) 487 self.tftp_dir = None 488 489 def get_output(self): 490 # wait a bit to give the simulator time to listen for a telnet connection 491 if self.child.poll() != None: # Check if child is down 492 print 'Simulator is down, return code is %d' % self.child.returncode 493 return None 494 495 # use telnetlib 496 import telnetlib 497 self.telnet_connected = False 498 while not self.telnet_connected: 499 try: 500 # RH 08.08.2018 The gem5 test seems to have problems with using localhost 501 # instead of 127.0.0.1 502 self.telnet = telnetlib.Telnet("127.0.0.1", self.telnet_port) 503 self.telnet_connected = True 504 self.output = self.telnet.get_socket().makefile() 505 except IOError, e: 506 errno, msg = e 507 if errno != 111: # connection refused 508 debug.error("telnet: %s [%d]" % (msg, errno)) 509 else: 510 self.telnet_connected = False 511 time.sleep(self.simulator_start_timeout) 512 513 return self.output 514 515class MachineFactory: 516 517 machineFactories = {} 518 519 def __init__(self, name, machineClass, kwargs): 520 self._class = machineClass 521 self._kwargs = kwargs 522 self._name = name 523 524 @classmethod 525 def addMachine(cls, name, machineClass, **kwargs): 526 cls.machineFactories[name] = MachineFactory(name, machineClass, kwargs) 527 machineClass.validateArgs(kwargs) 528 529 def getName(self): 530 """Get the name of the machine produced by this factory.""" 531 return self._name 532 533 def createMachine(self, options): 534 """Create a new machine instance.""" 535 try: 536 machine = self._class(options, **self._kwargs) 537 except TypeError as e: 538 print("Machine class %s failed to instantiate: %s" % (str(self._class), str(e))) 539 raise TypeError(e) 540 machine.setName(self._name) 541 return machine 542 543 @classmethod 544 def createMachineByName(cls, name, options): 545 """Create a new machine instance.""" 546 return cls.machineFactories[name].createMachine(options) 547 548# Assume that QEMU, FVP, pandaboard and Gem5 work everywhere if invoked 549import qemu 550import gem5 551import fvp 552import pandaboard 553import imx8x 554 555# Other site-specific modules will be loaded by the siteconfig module 556