cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

qom_common.py (5095B)


      1"""
      2QOM Command abstractions.
      3"""
      4##
      5# Copyright John Snow 2020, for Red Hat, Inc.
      6# Copyright IBM, Corp. 2011
      7#
      8# Authors:
      9#  John Snow <jsnow@redhat.com>
     10#  Anthony Liguori <aliguori@amazon.com>
     11#
     12# This work is licensed under the terms of the GNU GPL, version 2 or later.
     13# See the COPYING file in the top-level directory.
     14#
     15# Based on ./scripts/qmp/qom-[set|get|tree|list]
     16##
     17
     18import argparse
     19import os
     20import sys
     21from typing import (
     22    Any,
     23    Dict,
     24    List,
     25    Optional,
     26    Type,
     27    TypeVar,
     28)
     29
     30from . import QEMUMonitorProtocol, QMPError
     31
     32
     33# The following is needed only for a type alias.
     34Subparsers = argparse._SubParsersAction  # pylint: disable=protected-access
     35
     36
     37class ObjectPropertyInfo:
     38    """
     39    Represents the return type from e.g. qom-list.
     40    """
     41    def __init__(self, name: str, type_: str,
     42                 description: Optional[str] = None,
     43                 default_value: Optional[object] = None):
     44        self.name = name
     45        self.type = type_
     46        self.description = description
     47        self.default_value = default_value
     48
     49    @classmethod
     50    def make(cls, value: Dict[str, Any]) -> 'ObjectPropertyInfo':
     51        """
     52        Build an ObjectPropertyInfo from a Dict with an unknown shape.
     53        """
     54        assert value.keys() >= {'name', 'type'}
     55        assert value.keys() <= {'name', 'type', 'description', 'default-value'}
     56        return cls(value['name'], value['type'],
     57                   value.get('description'),
     58                   value.get('default-value'))
     59
     60    @property
     61    def child(self) -> bool:
     62        """Is this property a child property?"""
     63        return self.type.startswith('child<')
     64
     65    @property
     66    def link(self) -> bool:
     67        """Is this property a link property?"""
     68        return self.type.startswith('link<')
     69
     70
     71CommandT = TypeVar('CommandT', bound='QOMCommand')
     72
     73
     74class QOMCommand:
     75    """
     76    Represents a QOM sub-command.
     77
     78    :param args: Parsed arguments, as returned from parser.parse_args.
     79    """
     80    name: str
     81    help: str
     82
     83    def __init__(self, args: argparse.Namespace):
     84        if args.socket is None:
     85            raise QMPError("No QMP socket path or address given")
     86        self.qmp = QEMUMonitorProtocol(
     87            QEMUMonitorProtocol.parse_address(args.socket)
     88        )
     89        self.qmp.connect()
     90
     91    @classmethod
     92    def register(cls, subparsers: Subparsers) -> None:
     93        """
     94        Register this command with the argument parser.
     95
     96        :param subparsers: argparse subparsers object, from "add_subparsers".
     97        """
     98        subparser = subparsers.add_parser(cls.name, help=cls.help,
     99                                          description=cls.help)
    100        cls.configure_parser(subparser)
    101
    102    @classmethod
    103    def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
    104        """
    105        Configure a parser with this command's arguments.
    106
    107        :param parser: argparse parser or subparser object.
    108        """
    109        default_path = os.environ.get('QMP_SOCKET')
    110        parser.add_argument(
    111            '--socket', '-s',
    112            dest='socket',
    113            action='store',
    114            help='QMP socket path or address (addr:port).'
    115            ' May also be set via QMP_SOCKET environment variable.',
    116            default=default_path
    117        )
    118        parser.set_defaults(cmd_class=cls)
    119
    120    @classmethod
    121    def add_path_prop_arg(cls, parser: argparse.ArgumentParser) -> None:
    122        """
    123        Add the <path>.<proptery> positional argument to this command.
    124
    125        :param parser: The parser to add the argument to.
    126        """
    127        parser.add_argument(
    128            'path_prop',
    129            metavar='<path>.<property>',
    130            action='store',
    131            help="QOM path and property, separated by a period '.'"
    132        )
    133
    134    def run(self) -> int:
    135        """
    136        Run this command.
    137
    138        :return: 0 on success, 1 otherwise.
    139        """
    140        raise NotImplementedError
    141
    142    def qom_list(self, path: str) -> List[ObjectPropertyInfo]:
    143        """
    144        :return: a strongly typed list from the 'qom-list' command.
    145        """
    146        rsp = self.qmp.command('qom-list', path=path)
    147        # qom-list returns List[ObjectPropertyInfo]
    148        assert isinstance(rsp, list)
    149        return [ObjectPropertyInfo.make(x) for x in rsp]
    150
    151    @classmethod
    152    def command_runner(
    153            cls: Type[CommandT],
    154            args: argparse.Namespace
    155    ) -> int:
    156        """
    157        Run a fully-parsed subcommand, with error-handling for the CLI.
    158
    159        :return: The return code from `run()`.
    160        """
    161        try:
    162            cmd = cls(args)
    163            return cmd.run()
    164        except QMPError as err:
    165            print(f"{type(err).__name__}: {err!s}", file=sys.stderr)
    166            return -1
    167
    168    @classmethod
    169    def entry_point(cls) -> int:
    170        """
    171        Build this command's parser, parse arguments, and run the command.
    172
    173        :return: `run`'s return code.
    174        """
    175        parser = argparse.ArgumentParser(description=cls.help)
    176        cls.configure_parser(parser)
    177        args = parser.parse_args()
    178        return cls.command_runner(args)