linux/arch/sparc/kernel/hvapi.c
<<
>>
Prefs
   1/* hvapi.c: Hypervisor API management.
   2 *
   3 * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
   4 */
   5#include <linux/kernel.h>
   6#include <linux/export.h>
   7#include <linux/init.h>
   8
   9#include <asm/hypervisor.h>
  10#include <asm/oplib.h>
  11
  12/* If the hypervisor indicates that the API setting
  13 * calls are unsupported, by returning HV_EBADTRAP or
  14 * HV_ENOTSUPPORTED, we assume that API groups with the
  15 * PRE_API flag set are major 1 minor 0.
  16 */
  17struct api_info {
  18        unsigned long group;
  19        unsigned long major;
  20        unsigned long minor;
  21        unsigned int refcnt;
  22        unsigned int flags;
  23#define FLAG_PRE_API            0x00000001
  24};
  25
  26static struct api_info api_table[] = {
  27        { .group = HV_GRP_SUN4V,        .flags = FLAG_PRE_API   },
  28        { .group = HV_GRP_CORE,         .flags = FLAG_PRE_API   },
  29        { .group = HV_GRP_INTR,                                 },
  30        { .group = HV_GRP_SOFT_STATE,                           },
  31        { .group = HV_GRP_TM,                                   },
  32        { .group = HV_GRP_PCI,          .flags = FLAG_PRE_API   },
  33        { .group = HV_GRP_LDOM,                                 },
  34        { .group = HV_GRP_SVC_CHAN,     .flags = FLAG_PRE_API   },
  35        { .group = HV_GRP_NCS,          .flags = FLAG_PRE_API   },
  36        { .group = HV_GRP_RNG,                                  },
  37        { .group = HV_GRP_PBOOT,                                },
  38        { .group = HV_GRP_TPM,                                  },
  39        { .group = HV_GRP_SDIO,                                 },
  40        { .group = HV_GRP_SDIO_ERR,                             },
  41        { .group = HV_GRP_REBOOT_DATA,                          },
  42        { .group = HV_GRP_NIAG_PERF,    .flags = FLAG_PRE_API   },
  43        { .group = HV_GRP_FIRE_PERF,                            },
  44        { .group = HV_GRP_N2_CPU,                               },
  45        { .group = HV_GRP_NIU,                                  },
  46        { .group = HV_GRP_VF_CPU,                               },
  47        { .group = HV_GRP_KT_CPU,                               },
  48        { .group = HV_GRP_VT_CPU,                               },
  49        { .group = HV_GRP_T5_CPU,                               },
  50        { .group = HV_GRP_DIAG,         .flags = FLAG_PRE_API   },
  51        { .group = HV_GRP_M7_PERF,                              },
  52};
  53
  54static DEFINE_SPINLOCK(hvapi_lock);
  55
  56static struct api_info *__get_info(unsigned long group)
  57{
  58        int i;
  59
  60        for (i = 0; i < ARRAY_SIZE(api_table); i++) {
  61                if (api_table[i].group == group)
  62                        return &api_table[i];
  63        }
  64        return NULL;
  65}
  66
  67static void __get_ref(struct api_info *p)
  68{
  69        p->refcnt++;
  70}
  71
  72static void __put_ref(struct api_info *p)
  73{
  74        if (--p->refcnt == 0) {
  75                unsigned long ignore;
  76
  77                sun4v_set_version(p->group, 0, 0, &ignore);
  78                p->major = p->minor = 0;
  79        }
  80}
  81
  82/* Register a hypervisor API specification.  It indicates the
  83 * API group and desired major+minor.
  84 *
  85 * If an existing API registration exists '0' (success) will
  86 * be returned if it is compatible with the one being registered.
  87 * Otherwise a negative error code will be returned.
  88 *
  89 * Otherwise an attempt will be made to negotiate the requested
  90 * API group/major/minor with the hypervisor, and errors returned
  91 * if that does not succeed.
  92 */
  93int sun4v_hvapi_register(unsigned long group, unsigned long major,
  94                         unsigned long *minor)
  95{
  96        struct api_info *p;
  97        unsigned long flags;
  98        int ret;
  99
 100        spin_lock_irqsave(&hvapi_lock, flags);
 101        p = __get_info(group);
 102        ret = -EINVAL;
 103        if (p) {
 104                if (p->refcnt) {
 105                        ret = -EINVAL;
 106                        if (p->major == major) {
 107                                *minor = p->minor;
 108                                ret = 0;
 109                        }
 110                } else {
 111                        unsigned long actual_minor;
 112                        unsigned long hv_ret;
 113
 114                        hv_ret = sun4v_set_version(group, major, *minor,
 115                                                   &actual_minor);
 116                        ret = -EINVAL;
 117                        if (hv_ret == HV_EOK) {
 118                                *minor = actual_minor;
 119                                p->major = major;
 120                                p->minor = actual_minor;
 121                                ret = 0;
 122                        } else if (hv_ret == HV_EBADTRAP ||
 123                                   hv_ret == HV_ENOTSUPPORTED) {
 124                                if (p->flags & FLAG_PRE_API) {
 125                                        if (major == 1) {
 126                                                p->major = 1;
 127                                                p->minor = 0;
 128                                                *minor = 0;
 129                                                ret = 0;
 130                                        }
 131                                }
 132                        }
 133                }
 134
 135                if (ret == 0)
 136                        __get_ref(p);
 137        }
 138        spin_unlock_irqrestore(&hvapi_lock, flags);
 139
 140        return ret;
 141}
 142EXPORT_SYMBOL(sun4v_hvapi_register);
 143
 144void sun4v_hvapi_unregister(unsigned long group)
 145{
 146        struct api_info *p;
 147        unsigned long flags;
 148
 149        spin_lock_irqsave(&hvapi_lock, flags);
 150        p = __get_info(group);
 151        if (p)
 152                __put_ref(p);
 153        spin_unlock_irqrestore(&hvapi_lock, flags);
 154}
 155EXPORT_SYMBOL(sun4v_hvapi_unregister);
 156
 157int sun4v_hvapi_get(unsigned long group,
 158                    unsigned long *major,
 159                    unsigned long *minor)
 160{
 161        struct api_info *p;
 162        unsigned long flags;
 163        int ret;
 164
 165        spin_lock_irqsave(&hvapi_lock, flags);
 166        ret = -EINVAL;
 167        p = __get_info(group);
 168        if (p && p->refcnt) {
 169                *major = p->major;
 170                *minor = p->minor;
 171                ret = 0;
 172        }
 173        spin_unlock_irqrestore(&hvapi_lock, flags);
 174
 175        return ret;
 176}
 177EXPORT_SYMBOL(sun4v_hvapi_get);
 178
 179void __init sun4v_hvapi_init(void)
 180{
 181        unsigned long group, major, minor;
 182
 183        group = HV_GRP_SUN4V;
 184        major = 1;
 185        minor = 0;
 186        if (sun4v_hvapi_register(group, major, &minor))
 187                goto bad;
 188
 189        group = HV_GRP_CORE;
 190        major = 1;
 191        minor = 1;
 192        if (sun4v_hvapi_register(group, major, &minor))
 193                goto bad;
 194
 195        return;
 196
 197bad:
 198        prom_printf("HVAPI: Cannot register API group "
 199                    "%lx with major(%lu) minor(%lu)\n",
 200                    group, major, minor);
 201        prom_halt();
 202}
 203