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