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