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