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