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