docs: def _virtual_hv(osdata):
      '''
      Returns detailed hypervisor information from sysfs
      Currently this seems to be used only by Xen
      '''
      grains = {}
  
      # Bail early if we're not running on Xen
      try:
          if 'xen' not in osdata['virtual']:
              return grains
      except KeyError:
          return grains
  
      # Try to get the exact hypervisor version from sysfs
      try:
          version = {}
          for fn in ('major', 'minor', 'extra'):
              with salt.utils.files.fopen('/sys/hypervisor/version/{}'.format(fn), 'r') as fhr:
                  version[fn] = salt.utils.stringutils.to_unicode(fhr.read().strip())
          grains['virtual_hv_version'] = '{}.{}{}'.format(version['major'], version['minor'], version['extra'])
          grains['virtual_hv_version_info'] = [version['major'], version['minor'], version['extra']]
      except (IOError, OSError, KeyError):
          pass
  
      # Try to read and decode the supported feature set of the hypervisor
      # Based on https://github.com/brendangregg/Misc/blob/master/xen/xen-features.py
      # Table data from include/xen/interface/features.h
      xen_feature_table = {0: 'writable_page_tables',
                           1: 'writable_descriptor_tables',
                           2: 'auto_translated_physmap',
                           3: 'supervisor_mode_kernel',
                           4: 'pae_pgdir_above_4gb',
                           5: 'mmu_pt_update_preserve_ad',
                           7: 'gnttab_map_avail_bits',
                           8: 'hvm_callback_vector',
                           9: 'hvm_safe_pvclock',
                          10: 'hvm_pirqs',
                          11: 'dom0',
                          12: 'grant_map_identity',
                          13: 'memory_op_vnode_supported',
                          14: 'ARM_SMCCC_supported'}
      try:
          with salt.utils.files.fopen('/sys/hypervisor/properties/features', 'r') as fhr:
              features = salt.utils.stringutils.to_unicode(fhr.read().strip())
          enabled_features = []
          for bit, feat in six.iteritems(xen_feature_table):
              if int(features, 16) & (1 << bit):
                  enabled_features.append(feat)
          grains['virtual_hv_features'] = features
          grains['virtual_hv_features_list'] = enabled_features
      except (IOError, OSError, KeyError):
          pass
  
      return grains