def _qemu_preexec_nw(self): # This is called by subprocess.Popen after spawning to run # qemu from tt_qemu.power_on_do() right before spawning Qemu # for us. # # See doc block on top of this class and on :class:`vlan_pci` # for why file descriptor 0. # # We will find out which is the index of the TAP device # assigned to this, created on _image_power_on_pre and open # file desctriptor 0 to it, then leave it open for Qemu to # tap into it. count = 0 for ic_name, ic_kws in self.tags.get('interconnects', {}).iteritems(): if not 'ipv4_addr' in ic_kws and not 'ipv6_addr' in ic_kws: continue if count > 0: raise NotImplementedError( "QEMU Networking cannot implement " "multiple networks at this time " "(when trying to connec to %s)" % ic_name) kws = dict(ic_kws) kws.update(self.kws) kws['ic_name'] = ic_name if not commonl.if_present("_b%s" % ic_name): self.log.warning("network %s powered off? networking " "disabled" % ic_name) # If the network is not powered up, skip it # FIXME: replace with it calling vlan_pci.something() # that brings up the basic interface (_bICNAME) so # that once we power the network, it works continue tapindex = commonl.if_index("t%(ic_name)s%(id)s" % kws) assign = 0 # Need to wait for udev to reconfigure this for us to have # access; this is done by a udev rule installed # (/usr/lib/udev/rules.d/80-ttbd.rules) tapdevname = "/dev/tap%d" % tapindex count = 1 top = 4 while not os.access(tapdevname, os.R_OK | os.W_OK): if count >= top: msg = "%s: timed out waiting for udev to set " \ "permissions in /usr/lib/udev/rules.d/80-ttbd.rules" \ % tapdevname self.log.error(msg) raise RuntimeError(msg) time.sleep(0.25) count += 1 fd = os.open("/dev/tap%d" % tapindex, os.O_RDWR, 0) if fd != assign: # there, reassign it to fd @assign os.dup2(fd, assign) os.close(fd) # leave fd assign open for QEMU! count += 1
def _qemu_preexec_nw(self): # This is called by subprocess.Popen after spawning to run # qemu from tt_qemu.power_on_do() right before spawning Qemu # for us. # # See doc block on top of this class and on :class:`vlan_pci` # for why file descriptor 0. # # We will find out which is the index of the TAP device # assigned to this, created on _image_power_on_pre and open # file desctriptor 0 to it, then leave it open for Qemu to # tap into it. count = 0 for ic_name, ic_kws in list(self.tags.get('interconnects', {}).items()): if not 'ipv4_addr' in ic_kws and not 'ipv6_addr' in ic_kws: continue if count > 0: raise NotImplementedError( "QEMU Networking cannot implement " "multiple networks at this time " "(when trying to connec to %s)" % ic_name) kws = dict(ic_kws) kws.update(self.kws) kws['ic_name'] = ic_name if not commonl.if_present("_b%s" % ic_name): self.log.warning("network %s powered off? networking " "disabled" % ic_name) # If the network is not powered up, skip it # FIXME: replace with it calling vlan_pci.something() # that brings up the basic interface (_bICNAME) so # that once we power the network, it works continue tapindex = commonl.if_index("t%(ic_name)s%(id)s" % kws) assign = 0 # Need to wait for udev to reconfigure this for us to have # access; this is done by a udev rule installed # (/usr/lib/udev/rules.d/80-ttbd.rules) tapdevname = "/dev/tap%d" % tapindex count = 1 top = 4 while not os.access(tapdevname, os.R_OK | os.W_OK): if count >= top: msg = "%s: timed out waiting for udev to set " \ "permissions in /usr/lib/udev/rules.d/80-ttbd.rules" \ % tapdevname self.log.error(msg) raise RuntimeError(msg) time.sleep(0.25) count += 1 fd = os.open("/dev/tap%d" % tapindex, os.O_RDWR, 0) if fd != assign: # there, reassign it to fd @assign os.dup2(fd, assign) os.close(fd) # leave fd assign open for QEMU! count += 1
def on(self, target, component): # Start QEMU # # We first assign a port for GDB debugging, if someone takes # it before we do, start will fail and paranoid mode (see # above) will restart it. base = ttbl.config.tcp_port_range[0] top = ttbl.config.tcp_port_range[1] if base < 5900: # we need ports >= 5900 for VNC base = 5900 tcp_port_base = commonl.tcp_port_assigner(2, port_range=(base, top)) target.fsdb.set("qemu-gdb-tcp-port", "%s" % tcp_port_base) # This might not be used at all, but we allocate and declare # it in case the implementation will use it; allocating in a # higher layer makes it more complicated. # this one is the port number based on 5900 (VNC default 0) if top < 5900: logging.warning( "ttbl.config.tcp_port_range %s doesn't include ports " "between above 5900, needed for VNC services. " "QEMU targets needing VNC support will fail to start " "complaining about 'vnc-port' not defined", ttbl.config.tcp_port_range) else: # set this for general information; the VNC screenshotter # also uses it target.fsdb.set("vnc-host", "localhost") target.fsdb.set("vnc-port", "%s" % (tcp_port_base + 1 - 5900)) # this one is the raw port number target.fsdb.set("vnc-tcp-port", "%s" % (tcp_port_base + 1)) self.cmdline_extra = [] image_keys = target.fsdb.keys("qemu-image-*") # # Images interface: flash() below has been used to set images # to run, this will feed them into QEMU # if 'qemu-image-bios' in image_keys: self.cmdline_extra += ["-bios", "%(qemu-image-bios)s"] if 'qemu-image-kernel' in image_keys: self.cmdline_extra += ["-kernel", "%(qemu-image-kernel)s"] if 'qemu-image-kernel-args' in image_keys: self.cmdline_extra += ["-append", "%(qemu-image-kernel-args)s"] if 'qemu-image-initrd' in image_keys: self.cmdline_extra += ["-initrd", "%(qemu-image-initrd)s"] # # Network support bits--add command line options to connect # this VM to the networks the target declares # # For each network we are connected to, there must be a # network_tap_pc power rail controller that sets up the # network devices for us before starting QEMU. # # We look at it to setup the QEMU side of things as extra # command line options. # # - network name/icname: comes from the tuntap-ICNAME property # created by the network_tap_pc power rail controller that # the configuration must put on before the qemu power rail # controller. # # - model: comes from what HW it has to emulate; each arch has a # default or you can set for all with property qemu-model or # qemu-model-ICNAME (for that specifc interconnect). # # It has to be a valid QEMU model or QEMU will reject it # with an error. You can find valid models with:: # # qemu-system-ARCH -net nic,model=help # # Zephyr, e.g., supports a few: lan9118 stellaris e1000 (from):: # # $ grep --color -nH --null -rie ETH_NIC_MODEL # drivers/ethernet/Kconfig.smsc911x:13:config ETH_NIC_MODEL$ # drivers/ethernet/Kconfig.stellaris:15:config ETH_NIC_MODEL$ # drivers/ethernet/Kconfig.e1000:16:config ETH_NIC_MODEL$ fds = [] try: for tap, if_name in target.fsdb.get_as_dict( "tuntap-*").iteritems(): # @tap is tuntap-NETWORK, the property set by # powe rail component network_tap_pc. _, ic_name = tap.split("-", 1) mac_addr = target.tags['interconnects'][ic_name]['mac_addr'] model = commonl.name_make_safe( target.fsdb.get( "qemu-model-" + ic_name, target.fsdb.get("qemu-model", self.nic_model))) # There is no way to cahole QEMU to say: hey, we # already have a tap interface configured for you, use # it...other than to open it and pass the file # descriptor to it. # So this finds the /dev/tap* file which matches the # interface created with tuntap- (class # network_tap_pc), opens it and passes the descriptor # along to qemu, then closes the FD, since we don't # need it. tapindex = commonl.if_index(if_name) # Need to wait for udev to reconfigure this for us to have # access; this is done by a udev rule installed # (/usr/lib/udev/rules.d/80-ttbd.rules) tapdevname = "/dev/tap%d" % tapindex ts0 = time.time() ts = ts0 timeout = 5 fd = None while ts - ts0 < timeout: try: fd = os.open(tapdevname, os.O_RDWR, 0) fds.append(fd) break except OSError as e: target.log.error( "%s: couldn't open %s after" " +%.1f/%.1fs, retrying: %s", ic_name, tapdevname, ts - ts0, timeout, e) time.sleep(0.25) ts = time.time() else: raise RuntimeError( "%s: timedout (%.1fs) opening /dev/tap%d's;" " udev's /usr/lib/udev/rules.d/80-ttbd.rules" " didn't set permissions?" % (ic_name, timeout, tapindex)) self.cmdline_extra += [ "-nic", "tap,fd=%d,id=%s,model=%s,mac=%s,br=_b%s" \ % (fd, ic_name, model, mac_addr, ic_name ) ] if not fds: # No tuntap network attachments -> no network # support. Cap it. self.cmdline_extra += ['-nic', 'none'] # # Ok, so do actually start # # if the debug port is snatched before we start by someone # else, this will fail and we'll have to retry--this is # why the power component is set to paranoid mode, so the # power interface automatically retries it if it fails. ttbl.power.daemon_c.on(self, target, component) # Those consoles? update their generational counts so the # clients can know they restarted if hasattr(target, "console"): for console in target.console.impls.keys(): ttbl.console.generation_set(target, console) finally: for fd in fds: # close fds we opened for ... os.close(fd) # ... networking, unneeded now # # Debugging interface--if the target is not in debugging mode, # tell QEMU to start right away # if target.fsdb.get("debug") == None: self.debug_resume(target, component)