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