diff options
Diffstat (limited to 'arch/sparc/kernel/hvapi.c')
-rw-r--r-- | arch/sparc/kernel/hvapi.c | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/arch/sparc/kernel/hvapi.c b/arch/sparc/kernel/hvapi.c new file mode 100644 index 000000000000..1d272c3b5740 --- /dev/null +++ b/arch/sparc/kernel/hvapi.c | |||
@@ -0,0 +1,193 @@ | |||
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 | } | ||