1 | /* $NetBSD: xenbus_dev.c,v 1.10 2016/07/07 06:55:40 msaitoh Exp $ */ |
2 | /* |
3 | * xenbus_dev.c |
4 | * |
5 | * Driver giving user-space access to the kernel's xenbus connection |
6 | * to xenstore. |
7 | * |
8 | * Copyright (c) 2005, Christian Limpach |
9 | * Copyright (c) 2005, Rusty Russell, IBM Corporation |
10 | * |
11 | * This file may be distributed separately from the Linux kernel, or |
12 | * incorporated into other software packages, subject to the following license: |
13 | * |
14 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
15 | * of this source file (the "Software"), to deal in the Software without |
16 | * restriction, including without limitation the rights to use, copy, modify, |
17 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, |
18 | * and to permit persons to whom the Software is furnished to do so, subject to |
19 | * the following conditions: |
20 | * |
21 | * The above copyright notice and this permission notice shall be included in |
22 | * all copies or substantial portions of the Software. |
23 | * |
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
25 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
26 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
27 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
28 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
29 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
30 | * IN THE SOFTWARE. |
31 | */ |
32 | |
33 | #include <sys/cdefs.h> |
34 | __KERNEL_RCSID(0, "$NetBSD: xenbus_dev.c,v 1.10 2016/07/07 06:55:40 msaitoh Exp $" ); |
35 | |
36 | #include "opt_xen.h" |
37 | |
38 | #include <sys/types.h> |
39 | #include <sys/null.h> |
40 | #include <sys/errno.h> |
41 | #include <sys/malloc.h> |
42 | #include <sys/param.h> |
43 | #include <sys/proc.h> |
44 | #include <sys/systm.h> |
45 | #include <sys/dirent.h> |
46 | #include <sys/stat.h> |
47 | #include <sys/tree.h> |
48 | #include <sys/vnode.h> |
49 | #include <miscfs/specfs/specdev.h> |
50 | #include <miscfs/kernfs/kernfs.h> |
51 | |
52 | #include <xen/kernfs_machdep.h> |
53 | |
54 | #include <xen/hypervisor.h> |
55 | #include <xen/xenbus.h> |
56 | #include "xenbus_comms.h" |
57 | |
58 | static int xenbus_dev_read(void *); |
59 | static int xenbus_dev_write(void *); |
60 | static int xenbus_dev_open(void *); |
61 | static int xenbus_dev_close(void *); |
62 | static int xsd_port_read(void *); |
63 | |
64 | struct xenbus_dev_transaction { |
65 | SLIST_ENTRY(xenbus_dev_transaction) trans_next; |
66 | struct xenbus_transaction *handle; |
67 | }; |
68 | |
69 | #define DIR_MODE (S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) |
70 | #define PRIVCMD_MODE (S_IRUSR | S_IWUSR) |
71 | static const struct kernfs_fileop xenbus_fileops[] = { |
72 | { .kf_fileop = KERNFS_FILEOP_OPEN, .kf_vop = xenbus_dev_open }, |
73 | { .kf_fileop = KERNFS_FILEOP_CLOSE, .kf_vop = xenbus_dev_close }, |
74 | { .kf_fileop = KERNFS_FILEOP_READ, .kf_vop = xenbus_dev_read }, |
75 | { .kf_fileop = KERNFS_FILEOP_WRITE, .kf_vop = xenbus_dev_write }, |
76 | }; |
77 | |
78 | #define XSD_MODE (S_IRUSR) |
79 | static const struct kernfs_fileop xsd_port_fileops[] = { |
80 | { .kf_fileop = KERNFS_FILEOP_READ, .kf_vop = xsd_port_read }, |
81 | }; |
82 | |
83 | void |
84 | xenbus_kernfs_init(void) |
85 | { |
86 | kernfs_entry_t *dkt; |
87 | kfstype kfst; |
88 | |
89 | kfst = KERNFS_ALLOCTYPE(xenbus_fileops); |
90 | KERNFS_ALLOCENTRY(dkt, M_TEMP, M_WAITOK); |
91 | KERNFS_INITENTRY(dkt, DT_REG, "xenbus" , NULL, kfst, VREG, |
92 | PRIVCMD_MODE); |
93 | kernfs_addentry(kernxen_pkt, dkt); |
94 | |
95 | if (xendomain_is_dom0()) { |
96 | kfst = KERNFS_ALLOCTYPE(xsd_port_fileops); |
97 | KERNFS_ALLOCENTRY(dkt, M_TEMP, M_WAITOK); |
98 | KERNFS_INITENTRY(dkt, DT_REG, "xsd_port" , NULL, |
99 | kfst, VREG, XSD_MODE); |
100 | kernfs_addentry(kernxen_pkt, dkt); |
101 | } |
102 | } |
103 | |
104 | struct xenbus_dev_data { |
105 | #define BUFFER_SIZE (PAGE_SIZE) |
106 | #define MASK_READ_IDX(idx) ((idx)&(BUFFER_SIZE-1)) |
107 | /* In-progress transaction. */ |
108 | SLIST_HEAD(, xenbus_dev_transaction) transactions; |
109 | |
110 | /* Partial request. */ |
111 | unsigned int len; |
112 | union { |
113 | struct xsd_sockmsg msg; |
114 | char buffer[BUFFER_SIZE]; |
115 | } u; |
116 | |
117 | /* Response queue. */ |
118 | char read_buffer[BUFFER_SIZE]; |
119 | unsigned int read_cons, read_prod; |
120 | }; |
121 | |
122 | static int |
123 | xenbus_dev_read(void *v) |
124 | { |
125 | struct vop_read_args /* { |
126 | struct vnode *a_vp; |
127 | struct uio *a_uio; |
128 | int a_ioflag; |
129 | struct ucred *a_cred; |
130 | } */ *ap = v; |
131 | struct kernfs_node *kfs = VTOKERN(ap->a_vp); |
132 | struct uio *uio = ap->a_uio; |
133 | struct xenbus_dev_data *u = kfs->kfs_v; |
134 | int err; |
135 | off_t offset; |
136 | int s = spltty(); |
137 | |
138 | while (u->read_prod == u->read_cons) { |
139 | err = tsleep(u, PRIBIO | PCATCH, "xbrd" , 0); |
140 | if (err) |
141 | goto end; |
142 | } |
143 | offset = uio->uio_offset; |
144 | |
145 | if (u->read_cons > u->read_prod) { |
146 | err = uiomove(&u->read_buffer[MASK_READ_IDX(u->read_cons)], |
147 | 0U - u->read_cons, uio); |
148 | if (err) |
149 | goto end; |
150 | u->read_cons += (uio->uio_offset - offset); |
151 | offset = uio->uio_offset; |
152 | } |
153 | err = uiomove(&u->read_buffer[MASK_READ_IDX(u->read_cons)], |
154 | u->read_prod - u->read_cons, uio); |
155 | if (err == 0) |
156 | u->read_cons += (uio->uio_offset - offset); |
157 | |
158 | end: |
159 | splx(s); |
160 | return err; |
161 | } |
162 | |
163 | static void |
164 | queue_reply(struct xenbus_dev_data *u, |
165 | char *data, unsigned int len) |
166 | { |
167 | int i; |
168 | |
169 | for (i = 0; i < len; i++, u->read_prod++) |
170 | u->read_buffer[MASK_READ_IDX(u->read_prod)] = data[i]; |
171 | |
172 | KASSERT((u->read_prod - u->read_cons) <= sizeof(u->read_buffer)); |
173 | |
174 | wakeup(&u); |
175 | } |
176 | |
177 | static int |
178 | xenbus_dev_write(void *v) |
179 | { |
180 | struct vop_write_args /* { |
181 | struct vnode *a_vp; |
182 | struct uio *a_uio; |
183 | int a_ioflag; |
184 | struct ucred *a_cred; |
185 | } */ *ap = v; |
186 | struct kernfs_node *kfs = VTOKERN(ap->a_vp); |
187 | struct uio *uio = ap->a_uio; |
188 | |
189 | struct xenbus_dev_data *u = kfs->kfs_v; |
190 | struct xenbus_dev_transaction *trans; |
191 | void *reply; |
192 | int err; |
193 | size_t size; |
194 | |
195 | if (uio->uio_offset < 0) |
196 | return EINVAL; |
197 | size = uio->uio_resid; |
198 | |
199 | if ((size + u->len) > sizeof(u->u.buffer)) |
200 | return EINVAL; |
201 | |
202 | err = uiomove(u->u.buffer + u->len, sizeof(u->u.buffer) - u->len, uio); |
203 | if (err) |
204 | return err; |
205 | |
206 | u->len += size; |
207 | if (u->len < (sizeof(u->u.msg) + u->u.msg.len)) |
208 | return 0; |
209 | |
210 | switch (u->u.msg.type) { |
211 | case XS_TRANSACTION_START: |
212 | case XS_TRANSACTION_END: |
213 | case XS_DIRECTORY: |
214 | case XS_READ: |
215 | case XS_GET_PERMS: |
216 | case XS_RELEASE: |
217 | case XS_GET_DOMAIN_PATH: |
218 | case XS_WRITE: |
219 | case XS_MKDIR: |
220 | case XS_RM: |
221 | case XS_SET_PERMS: |
222 | err = xenbus_dev_request_and_reply(&u->u.msg, &reply); |
223 | if (err == 0) { |
224 | if (u->u.msg.type == XS_TRANSACTION_START) { |
225 | trans = malloc(sizeof(*trans), M_DEVBUF, |
226 | M_WAITOK); |
227 | trans->handle = (struct xenbus_transaction *) |
228 | strtoul(reply, NULL, 0); |
229 | SLIST_INSERT_HEAD(&u->transactions, |
230 | trans, trans_next); |
231 | } else if (u->u.msg.type == XS_TRANSACTION_END) { |
232 | SLIST_FOREACH(trans, &u->transactions, |
233 | trans_next) { |
234 | if ((unsigned long)trans->handle == |
235 | (unsigned long)u->u.msg.tx_id) |
236 | break; |
237 | } |
238 | KASSERT(trans != NULL); |
239 | SLIST_REMOVE(&u->transactions, trans, |
240 | xenbus_dev_transaction, trans_next); |
241 | free(trans, M_DEVBUF); |
242 | } |
243 | queue_reply(u, (char *)&u->u.msg, sizeof(u->u.msg)); |
244 | queue_reply(u, (char *)reply, u->u.msg.len); |
245 | free(reply, M_DEVBUF); |
246 | } |
247 | break; |
248 | |
249 | default: |
250 | err = EINVAL; |
251 | break; |
252 | } |
253 | |
254 | if (err == 0) { |
255 | u->len = 0; |
256 | } |
257 | |
258 | return err; |
259 | } |
260 | |
261 | static int |
262 | xenbus_dev_open(void *v) |
263 | { |
264 | struct vop_open_args /* { |
265 | struct vnode *a_vp; |
266 | int a_mode; |
267 | struct ucred *a_cred; |
268 | } */ *ap = v; |
269 | struct kernfs_node *kfs = VTOKERN(ap->a_vp); |
270 | |
271 | struct xenbus_dev_data *u; |
272 | |
273 | if (xen_start_info.store_evtchn == 0) |
274 | return ENOENT; |
275 | |
276 | u = malloc(sizeof(*u), M_DEVBUF, M_WAITOK); |
277 | if (u == NULL) |
278 | return ENOMEM; |
279 | |
280 | memset(u, 0, sizeof(*u)); |
281 | SLIST_INIT(&u->transactions); |
282 | |
283 | kfs->kfs_v = u; |
284 | |
285 | return 0; |
286 | } |
287 | |
288 | static int |
289 | xenbus_dev_close(void *v) |
290 | { |
291 | struct vop_close_args /* { |
292 | struct vnode *a_vp; |
293 | int a_fflag; |
294 | struct ucred *a_cred; |
295 | } */ *ap = v; |
296 | struct kernfs_node *kfs = VTOKERN(ap->a_vp); |
297 | |
298 | struct xenbus_dev_data *u = kfs->kfs_v; |
299 | struct xenbus_dev_transaction *trans; |
300 | |
301 | while (!SLIST_EMPTY(&u->transactions)) { |
302 | trans = SLIST_FIRST(&u->transactions); |
303 | xenbus_transaction_end(trans->handle, 1); |
304 | SLIST_REMOVE_HEAD(&u->transactions, trans_next); |
305 | free(trans, M_DEVBUF); |
306 | } |
307 | |
308 | free(u, M_DEVBUF); |
309 | kfs->kfs_v = NULL; |
310 | |
311 | return 0; |
312 | } |
313 | |
314 | #define LD_STRLEN 21 /* a 64bit integer needs 20 digits in base10 */ |
315 | |
316 | static int |
317 | xsd_port_read(void *v) |
318 | { |
319 | struct vop_read_args /* { |
320 | struct vnode *a_vp; |
321 | struct uio *a_uio; |
322 | int a_ioflag; |
323 | struct ucred *a_cred; |
324 | } */ *ap = v; |
325 | struct uio *uio = ap->a_uio; |
326 | int off, error; |
327 | size_t len; |
328 | char strbuf[LD_STRLEN], *bf; |
329 | |
330 | off = (int)uio->uio_offset; |
331 | if (off < 0) |
332 | return EINVAL; |
333 | |
334 | len = snprintf(strbuf, sizeof(strbuf), "%ld\n" , |
335 | (long)xen_start_info.store_evtchn); |
336 | if (off >= len) { |
337 | bf = strbuf; |
338 | len = 0; |
339 | } else { |
340 | bf = &strbuf[off]; |
341 | len -= off; |
342 | } |
343 | error = uiomove(bf, len, uio); |
344 | return error; |
345 | } |
346 | |
347 | /* |
348 | * Local variables: |
349 | * c-file-style: "linux" |
350 | * indent-tabs-mode: t |
351 | * c-indent-level: 8 |
352 | * c-basic-offset: 8 |
353 | * tab-width: 8 |
354 | * End: |
355 | */ |
356 | |