]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/amd64/vmm/io/vdev.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / amd64 / vmm / io / vdev.c
1 /*-
2  * Copyright (c) 2011 NetApp, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/systm.h>
35 #include <sys/malloc.h>
36
37 #include "vdev.h"
38
39 struct vdev {
40         SLIST_ENTRY(vdev)        entry;
41         struct vdev_ops         *ops;
42         void                    *dev;
43 };
44 static SLIST_HEAD(, vdev)       vdev_head;
45 static int                      vdev_count;
46
47 struct vdev_region {
48         SLIST_ENTRY(vdev_region)         entry;
49         struct vdev_ops                 *ops;
50         void                            *dev;
51         struct io_region                *io;
52 };
53 static SLIST_HEAD(, vdev_region)         region_head;
54 static int                               region_count;
55
56 static MALLOC_DEFINE(M_VDEV, "vdev", "vdev");
57
58 #define VDEV_INIT       (0)
59 #define VDEV_RESET      (1)
60 #define VDEV_HALT       (2)
61
62 // static const char* vdev_event_str[] = {"VDEV_INIT", "VDEV_RESET", "VDEV_HALT"};
63
64 static int
65 vdev_system_event(int event)
66 {
67         struct vdev     *vd;
68         int              rc;
69
70         // TODO: locking
71         SLIST_FOREACH(vd, &vdev_head, entry) {
72                 // printf("%s : %s Device %s\n", __func__, vdev_event_str[event], vd->ops->name);
73                 switch (event) {
74                         case VDEV_INIT:
75                                 rc = vd->ops->init(vd->dev);
76                                 break;
77                         case VDEV_RESET:
78                                 rc = vd->ops->reset(vd->dev);
79                                 break;
80                         case VDEV_HALT:
81                                 rc = vd->ops->halt(vd->dev);
82                                 break;
83                         default:
84                                 break;
85                 }
86                 if (rc) {
87                         printf("vdev %s init failed rc=%d\n",
88                             vd->ops->name, rc);
89                         return rc;
90                 }
91         }
92         return 0;
93 }
94
95 int
96 vdev_init(void)
97 {
98         return vdev_system_event(VDEV_INIT);
99 }
100
101 int
102 vdev_reset(void)
103 {
104         return vdev_system_event(VDEV_RESET);
105 }
106
107 int
108 vdev_halt(void)
109 {
110         return vdev_system_event(VDEV_HALT);
111 }
112
113 void
114 vdev_vm_init(void)
115 {
116         SLIST_INIT(&vdev_head);
117         vdev_count = 0;
118
119         SLIST_INIT(&region_head);
120         region_count = 0;
121 }
122 void
123 vdev_vm_cleanup(void)
124 {
125         struct vdev *vd;
126      
127         // TODO: locking
128         while (!SLIST_EMPTY(&vdev_head)) {
129                 vd = SLIST_FIRST(&vdev_head);
130                 SLIST_REMOVE_HEAD(&vdev_head, entry);
131                 free(vd, M_VDEV);
132                 vdev_count--;
133         }
134 }
135
136 int
137 vdev_register(struct vdev_ops *ops, void *dev)
138 {
139         struct vdev *vd;
140         vd = malloc(sizeof(*vd), M_VDEV, M_WAITOK | M_ZERO); 
141         vd->ops = ops;
142         vd->dev = dev;
143         
144         // TODO: locking
145         SLIST_INSERT_HEAD(&vdev_head, vd, entry); 
146         vdev_count++;
147         return 0;
148 }
149
150 void
151 vdev_unregister(void *dev)
152 {
153         struct vdev     *vd, *found;
154
155         found = NULL;
156         // TODO: locking
157         SLIST_FOREACH(vd, &vdev_head, entry) {
158                 if (vd->dev == dev) {
159                         found = vd;
160                 }
161         }
162
163         if (found) {
164                 SLIST_REMOVE(&vdev_head, found, vdev, entry);
165                 free(found, M_VDEV);
166         }
167 }
168
169 #define IN_RANGE(val, start, end)       \
170     (((val) >= (start)) && ((val) < (end)))
171
172 static struct vdev_region*
173 vdev_find_region(struct io_region *io, void *dev) 
174 {
175         struct          vdev_region *region, *found;
176         uint64_t        region_base;
177         uint64_t        region_end;
178
179         found = NULL;
180
181         // TODO: locking
182         // FIXME: we should verify we are in the context the current
183         //        vcpu here as well.
184         SLIST_FOREACH(region, &region_head, entry) {
185                 region_base = region->io->base;
186                 region_end = region_base + region->io->len;
187                 if (IN_RANGE(io->base, region_base, region_end) &&
188                     IN_RANGE(io->base+io->len, region_base, region_end+1) &&
189                     (dev && dev == region->dev)) {
190                         found = region;
191                         break;
192                 }
193         }
194         return found;
195 }
196
197 int
198 vdev_register_region(struct vdev_ops *ops, void *dev, struct io_region *io)
199 {
200         struct vdev_region *region;
201
202         region = vdev_find_region(io, dev);
203         if (region) {
204                 return -EEXIST;
205         }
206
207         region = malloc(sizeof(*region), M_VDEV, M_WAITOK | M_ZERO);
208         region->io = io;
209         region->ops = ops;
210         region->dev = dev;
211
212         // TODO: locking
213         SLIST_INSERT_HEAD(&region_head, region, entry); 
214         region_count++;
215
216         return 0;
217 }
218
219 void
220 vdev_unregister_region(void *dev, struct io_region *io)
221 {
222         struct vdev_region *region;
223
224         region = vdev_find_region(io, dev);
225         
226         if (region) {
227                 SLIST_REMOVE(&region_head, region, vdev_region, entry);
228                 free(region, M_VDEV);
229                 region_count--;
230         }
231 }
232
233 static int
234 vdev_memrw(uint64_t gpa, opsize_t size, uint64_t *data, int read)
235 {
236         struct vdev_region      *region;
237         struct io_region         io;
238         region_attr_t            attr;
239         int                      rc;
240
241         io.base = gpa;
242         io.len = size;
243
244         region = vdev_find_region(&io, NULL);
245         if (!region)
246                 return -EINVAL;
247         
248         attr = (read) ? MMIO_READ : MMIO_WRITE;
249         if (!(region->io->attr & attr))
250                 return -EPERM;
251
252         if (read)
253                 rc = region->ops->memread(region->dev, gpa, size, data);
254         else 
255                 rc = region->ops->memwrite(region->dev, gpa, size, *data);
256
257         return rc;
258 }
259
260 int
261 vdev_memread(uint64_t gpa, opsize_t size, uint64_t *data)
262 {
263         return vdev_memrw(gpa, size, data, 1);
264 }
265
266 int
267 vdev_memwrite(uint64_t gpa, opsize_t size, uint64_t data)
268 {
269         return vdev_memrw(gpa, size, &data, 0);
270 }