[OE-core] [PATCH V3 8/8] runqemu: refactor it and remove machine knowledge

Nathan Rossi nathan at nathanrossi.com
Fri Aug 19 07:52:28 UTC 2016


On Fri, Aug 19, 2016 at 4:55 PM, Robert Yang <liezhi.yang at windriver.com> wrote:
> Previously, runqemu had hard coded machine knowledge, which limited its
> usage, for example, qemu can boot genericx86, but runqemu can't, we need
> edit runqemu/runqemu-internal a lot if we want to boot genericx86.
>
> Now bsp conf files can set vars to make it can be boot by runqemu, and
> qemuboot.bbclass will save these info to DEPLOY_DIR_IMAGE/qemuboot.py.
> Please see qemuboot.bbclass' comments on how to set the vars.
>

-- snip --

> +
> +
> +    def setup_slirp(self):
> +        if self.fstype == 'nfs':
> +            self.setup_nfs()
> +        if self.get('TUNE_ARCH') == 'aarch64':
> +            raise Exception("aarch64 doesn't support slirp mode")

This doesn't seem quite right? because slirp works fine with the
qemuzynqmp machine which is aarch64 (based on the patch you provided
for meta-xilinx). This looks to be specific to the qemuarm64 machine?

Regards,
Nathan

> +        self.append('KERNEL_CMDLINE_SCRIPT', ' ip=dhcp')
> +        self.rootfs_options = '-drive file=%s,if=virtio,format=raw' % self.rootfs
> +        self.set('QB_NETWORK_CMD', '')
> +
> +    def setup_tap(self):
> +        """Setup tap"""
> +
> +        # This file is created when runqemu-gen-tapdevs creates a bank of tap
> +        # devices, indicating that the user should not bring up new ones using
> +        # sudo.
> +        nosudo_flag = '/etc/runqemu-nosudo'
> +        self.qemuifup = shutil.which('runqemu-ifup')
> +        self.qemuifdown = shutil.which('runqemu-ifdown')
> +        ip = shutil.which('ip')
> +        lockdir = "/tmp/qemu-tap-locks"
> +
> +        if not (self.qemuifup and self.qemuifdown and ip):
> +            raise Exception("runqemu-ifup, runqemu-ifdown or ip not found")
> +
> +        if not os.path.exists(lockdir):
> +            os.mkdir(lockdir)
> +
> +        cmd = '%s link' % ip
> +        logger.info('Running %s...' % cmd)
> +        ip_link = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
> +        # Matches line like: 6: tap0: <foo>
> +        possibles = re.findall('^[1-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
> +        tap = ""
> +        use_preconf_tap = False
> +        for p in possibles:
> +            lockfile = os.path.join(lockdir, p)
> +            if os.path.exists('%s.skip' % lockfile):
> +                logger.info('Found %s.skip, skipping %s' % (lockfile, p))
> +                continue
> +            if acquire_lock(lockfile):
> +                self.lockfile = lockfile
> +                tap = p
> +                logger.info("Using preconfigured tap device %s" % tap)
> +                logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
> +                use_preconf_tap = True
> +                break
> +
> +        if not tap:
> +            if os.path.exists(nosudo_flag):
> +                logger.error("Error: There are no available tap devices to use for networking,")
> +                logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
> +                raise Exception("a new one with sudo.")
> +
> +            gid = os.getgid()
> +            uid = os.getuid()
> +            logger.info("Setting up tap interface under sudo")
> +            cmd = 'sudo %s %s %s %s' % (self.qemuifup, uid, gid, self.get('STAGING_DIR_NATIVE'))
> +            tap = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
> +            self.cleantap = True
> +            logger.info('Created tap: %s' % tap)
> +
> +        self.tap = tap
> +        n0 = tap[3:]
> +        n1 = int(n0) * 2 + 1
> +        n2 = n1 + 1
> +        self.nfs_instance = n0
> +        if self.fstype == 'nfs':
> +            self.setup_nfs()
> +        self.append("KERNEL_CMDLINE_SCRIPT", " ip=192.168.7.%s::192.168.7.%s:255.255.255.0" % (n2, n1))
> +        qemu_tap_cmd = "-net tap,vlan=0,ifname=%s,script=no,downscript=no" % self.tap
> +        self.rootfs_options = '-drive file=%s,if=virtio,format=raw' % self.rootfs
> +        if self.get('TUNE_ARCH') == 'aarch64':
> +            qemu_network_cmd = '-netdev tap,id=net0,ifname=%s,script=no,downscript=no -device virtio-net-device,netdev=net0' % self.tap
> +            self.rootfs_options = '-drive id=disk0,file=%s,if=none,format=raw -device virtio-blk-device,drive=disk0' % self.rootfs
> +        elif self.get('TUNE_ARCH') == 'powerpc':
> +            qemu_network_cmd = '%s ' % qemu_tap_cmd
> +        else:
> +            qemu_network_cmd = "-net nic,model=virtio %s " % qemu_tap_cmd
> +        if self.vhost_enabled:
> +            qemu_network_cmd += ',vhost=on'
> +        self.weak_assign('QB_NETWORK_CMD', qemu_network_cmd)
> +
> +    def setup_network(self):
> +        cmd = "stty -g"
> +        self.saved_stty = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
> +        if self.slirp_enabled:
> +            self.setup_slirp()
> +        else:
> +            self.setup_tap()
> +
> +        if self.fstype in ('cpio.gz', 'cpio'):
> +            self.set('QB_NETWORK_CMD', '')
> +            self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
> +            self.rootfs_options = '-initrd %s' % self.rootfs
> +        else:
> +            if self.fstype in self.vmtypes:
> +                if self.fstype == 'iso':
> +                    vm_drive = '-cdrom %s' % self.rootfs
> +                else:
> +                    cmd1 = "grep -q 'root=/dev/sd' %s" % self.rootfs
> +                    cmd2 = "grep -q 'root=/dev/hd' %s" % self.rootfs
> +                    if subprocess.call(cmd1, shell=True) == 0:
> +                        logger.info('Using scsi drive')
> +                        vm_drive = '-drive if=none,id=hd,file=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd' % self.rootfs
> +                    elif subprocess.call(cmd2, shell=True) == 0:
> +                        logger.info('Using scsi drive')
> +                        vm_drive = self.rootfs
> +                    else:
> +                        logger.warn("Can't detect drive type %s" % self.rootfs)
> +                        logger.warn('Tring to use virtio block drive')
> +                        vm_drive = '-drive if=virtio,file=%s' % self.rootfs
> +                self.rootfs_options = '%s -no-reboot' % vm_drive
> +            self.kernel_cmdline = 'root=%s rw highres=off' % (self.get('KERNEL_ROOT'))
> +
> +        if self.fstype == 'nfs':
> +            self.rootfs_options = ''
> +            k_root = '/dev/nfs nfsroot=%s:%s,%s' % (self.nfs_server, self.nfs_dir, self.unfs_opts)
> +            self.kernel_cmdline = 'root=%s rw highres=off' % k_root
> +
> +        self.weak_assign('QB_ROOTFS_OPTIONS', self.rootfs_options)
> +
> +    def setup_final(self):
> +        qemu_system = self.get('QB_SYSTEM_NAME')
> +        if not qemu_system:
> +            raise Exception("Failed to boot, QB_SYSTEM_NAME is NULL!")
> +
> +        qemu_bin = '%s/%s' % (self.get('STAGING_BINDIR_NATIVE'), qemu_system)
> +        if not os.access(qemu_bin, os.X_OK):
> +            raise Exception("No QEMU binary '%s' could be found" % qemu_bin)
> +
> +        self.qemu_opt = "%s %s %s %s %s %s" % (qemu_bin, self.get('QB_NETWORK_CMD'), self.get('QEMU_OPT_SCRIPT'), self.get('QB_ROOTFS_OPTIONS'), self.get('QB_DTB'), self.get('QB_OPT_APPEND'))
> +
> +        if self.serialstdio:
> +            logger.info("Interrupt character is '^]'")
> +            cmd = "stty intr ^]"
> +            subprocess.call(cmd, shell=True)
> +
> +        first_serial = ""
> +        if not re.search("-nographic", self.qemu_opt):
> +            first_serial = "-serial mon:vc"
> +        # We always want a ttyS1. Since qemu by default adds a serial
> +        # port when nodefaults is not specified, it seems that all that
> +        # would be needed is to make sure a "-serial" is there. However,
> +        # it appears that when "-serial" is specified, it ignores the
> +        # default serial port that is normally added.  So here we make
> +        # sure to add two -serial if there are none. And only one if
> +        # there is one -serial already.
> +        serial_num = len(re.findall("-serial", self.qemu_opt))
> +        if serial_num == 0:
> +            self.qemu_opt += " %s %s" % (first_serial, self.get("QB_SERIAL_OPT"))
> +        elif serial_num == 1:
> +            self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
> +
> +    def start_qemu(self):
> +        if self.kernel:
> +            kernel_opts = "-kernel %s -append '%s %s %s'" % (self.kernel, self.kernel_cmdline, self.get('KERNEL_CMDLINE_SCRIPT'), self.get('QB_KERNEL_CMDLINE_APPEND'))
> +        else:
> +            kernel_opts = ""
> +        cmd = "%s %s" % (self.qemu_opt, kernel_opts)
> +        logger.info('Running %s' % cmd)
> +        if subprocess.call(cmd, shell=True) != 0:
> +            raise Exception('Failed to run %s' % cmd)
> +
> +    def cleanup(self):
> +        if self.cleantap:
> +            cmd = 'sudo %s %s %s' % (self.qemuifdown, self.tap, self.get('STAGING_DIR_NATIVE'))
> +            logger.info('Running %s' % cmd)
> +            subprocess.call(cmd, shell=True)
> +        if self.lockfile:
> +            logger.info("Releasing lockfile of preconfigured tap device '%s'" % self.tap)
> +            release_lock(self.lockfile)
> +
> +        if self.nfs_running:
> +            logger.info("Shutting down the userspace NFS server...")
> +            cmd = "runqemu-export-rootfs stop %s" % self.nfs_dir
> +            logger.info('Running %s' % cmd)
> +            subprocess.call(cmd, shell=True)
> +
> +        if self.saved_stty:
> +            cmd = "stty %s" % self.saved_stty
> +            subprocess.call(cmd, shell=True)
> +
> +        if self.clean_nfs_dir:
> +            logger.info('Removing %s' % self.nfs_dir)
> +            shutil.rmtree(self.nfs_dir)
> +            shutil.rmtree('%s.pseudo_state' % self.nfs_dir)
> +
> +def main():
> +    if len(sys.argv) == 1 or "help" in sys.argv:
> +        print_usage()
> +        return 0
> +    config = BaseConfig()
> +    config.check_args()

When it fails to parse the args instead of showing the python
exception info (with call stack, etc), it should show the exception
message and the runqemu usage.

Regards,
Nathan



More information about the Openembedded-core mailing list