diff options
Diffstat (limited to 'arch/sparc64/kernel/hvapi.c')
-rw-r--r-- | arch/sparc64/kernel/hvapi.c | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/arch/sparc64/kernel/hvapi.c b/arch/sparc64/kernel/hvapi.c new file mode 100644 index 000000000000..f34f5d6181ef --- /dev/null +++ b/arch/sparc64/kernel/hvapi.c | |||
@@ -0,0 +1,192 @@ | |||
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 | #include <asm/sstate.h> | ||
13 | |||
14 | /* If the hypervisor indicates that the API setting | ||
15 | * calls are unsupported, by returning HV_EBADTRAP or | ||
16 | * HV_ENOTSUPPORTED, we assume that API groups with the | ||
17 | * PRE_API flag set are major 1 minor 0. | ||
18 | */ | ||
19 | struct api_info { | ||
20 | unsigned long group; | ||
21 | unsigned long major; | ||
22 | unsigned long minor; | ||
23 | unsigned int refcnt; | ||
24 | unsigned int flags; | ||
25 | #define FLAG_PRE_API 0x00000001 | ||
26 | }; | ||
27 | |||
28 | static struct api_info api_table[] = { | ||
29 | { .group = HV_GRP_SUN4V, .flags = FLAG_PRE_API }, | ||
30 | { .group = HV_GRP_CORE, .flags = FLAG_PRE_API }, | ||
31 | { .group = HV_GRP_INTR, }, | ||
32 | { .group = HV_GRP_SOFT_STATE, }, | ||
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_NIAG_PERF, .flags = FLAG_PRE_API }, | ||
38 | { .group = HV_GRP_FIRE_PERF, }, | ||
39 | { .group = HV_GRP_DIAG, .flags = FLAG_PRE_API }, | ||
40 | }; | ||
41 | |||
42 | static DEFINE_SPINLOCK(hvapi_lock); | ||
43 | |||
44 | static struct api_info *__get_info(unsigned long group) | ||
45 | { | ||
46 | int i; | ||
47 | |||
48 | for (i = 0; i < ARRAY_SIZE(api_table); i++) { | ||
49 | if (api_table[i].group == group) | ||
50 | return &api_table[i]; | ||
51 | } | ||
52 | return NULL; | ||
53 | } | ||
54 | |||
55 | static void __get_ref(struct api_info *p) | ||
56 | { | ||
57 | p->refcnt++; | ||
58 | } | ||
59 | |||
60 | static void __put_ref(struct api_info *p) | ||
61 | { | ||
62 | if (--p->refcnt == 0) { | ||
63 | unsigned long ignore; | ||
64 | |||
65 | sun4v_set_version(p->group, 0, 0, &ignore); | ||
66 | p->major = p->minor = 0; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | /* Register a hypervisor API specification. It indicates the | ||
71 | * API group and desired major+minor. | ||
72 | * | ||
73 | * If an existing API registration exists '0' (success) will | ||
74 | * be returned if it is compatible with the one being registered. | ||
75 | * Otherwise a negative error code will be returned. | ||
76 | * | ||
77 | * Otherwise an attempt will be made to negotiate the requested | ||
78 | * API group/major/minor with the hypervisor, and errors returned | ||
79 | * if that does not succeed. | ||
80 | */ | ||
81 | int sun4v_hvapi_register(unsigned long group, unsigned long major, | ||
82 | unsigned long *minor) | ||
83 | { | ||
84 | struct api_info *p; | ||
85 | unsigned long flags; | ||
86 | int ret; | ||
87 | |||
88 | spin_lock_irqsave(&hvapi_lock, flags); | ||
89 | p = __get_info(group); | ||
90 | ret = -EINVAL; | ||
91 | if (p) { | ||
92 | if (p->refcnt) { | ||
93 | ret = -EINVAL; | ||
94 | if (p->major == major) { | ||
95 | *minor = p->minor; | ||
96 | ret = 0; | ||
97 | } | ||
98 | } else { | ||
99 | unsigned long actual_minor; | ||
100 | unsigned long hv_ret; | ||
101 | |||
102 | hv_ret = sun4v_set_version(group, major, *minor, | ||
103 | &actual_minor); | ||
104 | ret = -EINVAL; | ||
105 | if (hv_ret == HV_EOK) { | ||
106 | *minor = actual_minor; | ||
107 | p->major = major; | ||
108 | p->minor = actual_minor; | ||
109 | ret = 0; | ||
110 | } else if (hv_ret == HV_EBADTRAP || | ||
111 | hv_ret == HV_ENOTSUPPORTED) { | ||
112 | if (p->flags & FLAG_PRE_API) { | ||
113 | if (major == 1) { | ||
114 | p->major = 1; | ||
115 | p->minor = 0; | ||
116 | *minor = 0; | ||
117 | ret = 0; | ||
118 | } | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | |||
123 | if (ret == 0) | ||
124 | __get_ref(p); | ||
125 | } | ||
126 | spin_unlock_irqrestore(&hvapi_lock, flags); | ||
127 | |||
128 | return ret; | ||
129 | } | ||
130 | EXPORT_SYMBOL(sun4v_hvapi_register); | ||
131 | |||
132 | void sun4v_hvapi_unregister(unsigned long group) | ||
133 | { | ||
134 | struct api_info *p; | ||
135 | unsigned long flags; | ||
136 | |||
137 | spin_lock_irqsave(&hvapi_lock, flags); | ||
138 | p = __get_info(group); | ||
139 | if (p) | ||
140 | __put_ref(p); | ||
141 | spin_unlock_irqrestore(&hvapi_lock, flags); | ||
142 | } | ||
143 | EXPORT_SYMBOL(sun4v_hvapi_unregister); | ||
144 | |||
145 | int sun4v_hvapi_get(unsigned long group, | ||
146 | unsigned long *major, | ||
147 | unsigned long *minor) | ||
148 | { | ||
149 | struct api_info *p; | ||
150 | unsigned long flags; | ||
151 | int ret; | ||
152 | |||
153 | spin_lock_irqsave(&hvapi_lock, flags); | ||
154 | ret = -EINVAL; | ||
155 | p = __get_info(group); | ||
156 | if (p && p->refcnt) { | ||
157 | *major = p->major; | ||
158 | *minor = p->minor; | ||
159 | ret = 0; | ||
160 | } | ||
161 | spin_unlock_irqrestore(&hvapi_lock, flags); | ||
162 | |||
163 | return ret; | ||
164 | } | ||
165 | EXPORT_SYMBOL(sun4v_hvapi_get); | ||
166 | |||
167 | void __init sun4v_hvapi_init(void) | ||
168 | { | ||
169 | unsigned long group, major, minor; | ||
170 | |||
171 | group = HV_GRP_SUN4V; | ||
172 | major = 1; | ||
173 | minor = 0; | ||
174 | if (sun4v_hvapi_register(group, major, &minor)) | ||
175 | goto bad; | ||
176 | |||
177 | group = HV_GRP_CORE; | ||
178 | major = 1; | ||
179 | minor = 1; | ||
180 | if (sun4v_hvapi_register(group, major, &minor)) | ||
181 | goto bad; | ||
182 | |||
183 | sun4v_sstate_init(); | ||
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 | } | ||