Merge master.kernel.org:/pub/scm/linux/kernel/git/lethal/sh-2.6
[linux-2.6] / arch / sparc64 / kernel / hvapi.c
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 }