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