aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/lguest/lguest_device.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/lguest/lguest_device.c')
-rw-r--r--drivers/lguest/lguest_device.c146
1 files changed, 87 insertions, 59 deletions
diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c
index e2eec38c83c2..84f85e23cca7 100644
--- a/drivers/lguest/lguest_device.c
+++ b/drivers/lguest/lguest_device.c
@@ -52,57 +52,82 @@ struct lguest_device {
52/*D:130 52/*D:130
53 * Device configurations 53 * Device configurations
54 * 54 *
55 * The configuration information for a device consists of a series of fields. 55 * The configuration information for a device consists of one or more
56 * We don't really care what they are: the Launcher set them up, and the driver 56 * virtqueues, a feature bitmaks, and some configuration bytes. The
57 * will look at them during setup. 57 * configuration bytes don't really matter to us: the Launcher sets them up, and
58 * the driver will look at them during setup.
58 * 59 *
59 * For us these fields come immediately after that device's descriptor in the 60 * A convenient routine to return the device's virtqueue config array:
60 * lguest_devices page. 61 * immediately after the descriptor. */
61 * 62static struct lguest_vqconfig *lg_vq(const struct lguest_device_desc *desc)
62 * Each field starts with a "type" byte, a "length" byte, then that number of 63{
63 * bytes of configuration information. The device descriptor tells us the 64 return (void *)(desc + 1);
64 * total configuration length so we know when we've reached the last field. */ 65}
65 66
66/* type + length bytes */ 67/* The features come immediately after the virtqueues. */
67#define FHDR_LEN 2 68static u8 *lg_features(const struct lguest_device_desc *desc)
69{
70 return (void *)(lg_vq(desc) + desc->num_vq);
71}
68 72
69/* This finds the first field of a given type for a device's configuration. */ 73/* The config space comes after the two feature bitmasks. */
70static void *lg_find(struct virtio_device *vdev, u8 type, unsigned int *len) 74static u8 *lg_config(const struct lguest_device_desc *desc)
71{ 75{
72 struct lguest_device_desc *desc = to_lgdev(vdev)->desc; 76 return lg_features(desc) + desc->feature_len * 2;
73 int i; 77}
74
75 for (i = 0; i < desc->config_len; i += FHDR_LEN + desc->config[i+1]) {
76 if (desc->config[i] == type) {
77 /* Mark it used, so Host can know we looked at it, and
78 * also so we won't find the same one twice. */
79 desc->config[i] |= 0x80;
80 /* Remember, the second byte is the length. */
81 *len = desc->config[i+1];
82 /* We return a pointer to the field header. */
83 return desc->config + i;
84 }
85 }
86 78
87 /* Not found: return NULL for failure. */ 79/* The total size of the config page used by this device (incl. desc) */
88 return NULL; 80static unsigned desc_size(const struct lguest_device_desc *desc)
81{
82 return sizeof(*desc)
83 + desc->num_vq * sizeof(struct lguest_vqconfig)
84 + desc->feature_len * 2
85 + desc->config_len;
86}
87
88/* This tests (and acknowleges) a feature bit. */
89static bool lg_feature(struct virtio_device *vdev, unsigned fbit)
90{
91 struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
92 u8 *features;
93
94 /* Obviously if they ask for a feature off the end of our feature
95 * bitmap, it's not set. */
96 if (fbit / 8 > desc->feature_len)
97 return false;
98
99 /* The feature bitmap comes after the virtqueues. */
100 features = lg_features(desc);
101 if (!(features[fbit / 8] & (1 << (fbit % 8))))
102 return false;
103
104 /* We set the matching bit in the other half of the bitmap to tell the
105 * Host we want to use this feature. We don't use this yet, but we
106 * could in future. */
107 features[desc->feature_len + fbit / 8] |= (1 << (fbit % 8));
108 return true;
89} 109}
90 110
91/* Once they've found a field, getting a copy of it is easy. */ 111/* Once they've found a field, getting a copy of it is easy. */
92static void lg_get(struct virtio_device *vdev, void *token, 112static void lg_get(struct virtio_device *vdev, unsigned int offset,
93 void *buf, unsigned len) 113 void *buf, unsigned len)
94{ 114{
95 /* Check they didn't ask for more than the length of the field! */ 115 struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
96 BUG_ON(len > ((u8 *)token)[1]); 116
97 memcpy(buf, token + FHDR_LEN, len); 117 /* Check they didn't ask for more than the length of the config! */
118 BUG_ON(offset + len > desc->config_len);
119 memcpy(buf, lg_config(desc) + offset, len);
98} 120}
99 121
100/* Setting the contents is also trivial. */ 122/* Setting the contents is also trivial. */
101static void lg_set(struct virtio_device *vdev, void *token, 123static void lg_set(struct virtio_device *vdev, unsigned int offset,
102 const void *buf, unsigned len) 124 const void *buf, unsigned len)
103{ 125{
104 BUG_ON(len > ((u8 *)token)[1]); 126 struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
105 memcpy(token + FHDR_LEN, buf, len); 127
128 /* Check they didn't ask for more than the length of the config! */
129 BUG_ON(offset + len > desc->config_len);
130 memcpy(lg_config(desc) + offset, buf, len);
106} 131}
107 132
108/* The operations to get and set the status word just access the status field 133/* The operations to get and set the status word just access the status field
@@ -114,9 +139,20 @@ static u8 lg_get_status(struct virtio_device *vdev)
114 139
115static void lg_set_status(struct virtio_device *vdev, u8 status) 140static void lg_set_status(struct virtio_device *vdev, u8 status)
116{ 141{
142 BUG_ON(!status);
117 to_lgdev(vdev)->desc->status = status; 143 to_lgdev(vdev)->desc->status = status;
118} 144}
119 145
146/* To reset the device, we (ab)use the NOTIFY hypercall, with the descriptor
147 * address of the device. The Host will zero the status and all the
148 * features. */
149static void lg_reset(struct virtio_device *vdev)
150{
151 unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices;
152
153 hcall(LHCALL_NOTIFY, (max_pfn<<PAGE_SHIFT) + offset, 0, 0);
154}
155
120/* 156/*
121 * Virtqueues 157 * Virtqueues
122 * 158 *
@@ -165,39 +201,29 @@ static void lg_notify(struct virtqueue *vq)
165 * 201 *
166 * So we provide devices with a "find virtqueue and set it up" function. */ 202 * So we provide devices with a "find virtqueue and set it up" function. */
167static struct virtqueue *lg_find_vq(struct virtio_device *vdev, 203static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
168 bool (*callback)(struct virtqueue *vq)) 204 unsigned index,
205 void (*callback)(struct virtqueue *vq))
169{ 206{
207 struct lguest_device *ldev = to_lgdev(vdev);
170 struct lguest_vq_info *lvq; 208 struct lguest_vq_info *lvq;
171 struct virtqueue *vq; 209 struct virtqueue *vq;
172 unsigned int len;
173 void *token;
174 int err; 210 int err;
175 211
176 /* Look for a field of the correct type to mark a virtqueue. Note that 212 /* We must have this many virtqueues. */
177 * if this succeeds, then the type will be changed so it won't be found 213 if (index >= ldev->desc->num_vq)
178 * again, and future lg_find_vq() calls will find the next
179 * virtqueue (if any). */
180 token = vdev->config->find(vdev, VIRTIO_CONFIG_F_VIRTQUEUE, &len);
181 if (!token)
182 return ERR_PTR(-ENOENT); 214 return ERR_PTR(-ENOENT);
183 215
184 lvq = kmalloc(sizeof(*lvq), GFP_KERNEL); 216 lvq = kmalloc(sizeof(*lvq), GFP_KERNEL);
185 if (!lvq) 217 if (!lvq)
186 return ERR_PTR(-ENOMEM); 218 return ERR_PTR(-ENOMEM);
187 219
188 /* Note: we could use a configuration space inside here, just like we 220 /* Make a copy of the "struct lguest_vqconfig" entry, which sits after
189 * do for the device. This would allow expansion in future, because 221 * the descriptor. We need a copy because the config space might not
190 * our configuration system is designed to be expansible. But this is 222 * be aligned correctly. */
191 * way easier. */ 223 memcpy(&lvq->config, lg_vq(ldev->desc)+index, sizeof(lvq->config));
192 if (len != sizeof(lvq->config)) {
193 dev_err(&vdev->dev, "Unexpected virtio config len %u\n", len);
194 err = -EIO;
195 goto free_lvq;
196 }
197 /* Make a copy of the "struct lguest_vqconfig" field. We need a copy
198 * because the config space might not be aligned correctly. */
199 vdev->config->get(vdev, token, &lvq->config, sizeof(lvq->config));
200 224
225 printk("Mapping virtqueue %i addr %lx\n", index,
226 (unsigned long)lvq->config.pfn << PAGE_SHIFT);
201 /* Figure out how many pages the ring will take, and map that memory */ 227 /* Figure out how many pages the ring will take, and map that memory */
202 lvq->pages = lguest_map((unsigned long)lvq->config.pfn << PAGE_SHIFT, 228 lvq->pages = lguest_map((unsigned long)lvq->config.pfn << PAGE_SHIFT,
203 DIV_ROUND_UP(vring_size(lvq->config.num, 229 DIV_ROUND_UP(vring_size(lvq->config.num,
@@ -259,11 +285,12 @@ static void lg_del_vq(struct virtqueue *vq)
259 285
260/* The ops structure which hooks everything together. */ 286/* The ops structure which hooks everything together. */
261static struct virtio_config_ops lguest_config_ops = { 287static struct virtio_config_ops lguest_config_ops = {
262 .find = lg_find, 288 .feature = lg_feature,
263 .get = lg_get, 289 .get = lg_get,
264 .set = lg_set, 290 .set = lg_set,
265 .get_status = lg_get_status, 291 .get_status = lg_get_status,
266 .set_status = lg_set_status, 292 .set_status = lg_set_status,
293 .reset = lg_reset,
267 .find_vq = lg_find_vq, 294 .find_vq = lg_find_vq,
268 .del_vq = lg_del_vq, 295 .del_vq = lg_del_vq,
269}; 296};
@@ -329,13 +356,14 @@ static void scan_devices(void)
329 struct lguest_device_desc *d; 356 struct lguest_device_desc *d;
330 357
331 /* We start at the page beginning, and skip over each entry. */ 358 /* We start at the page beginning, and skip over each entry. */
332 for (i = 0; i < PAGE_SIZE; i += sizeof(*d) + d->config_len) { 359 for (i = 0; i < PAGE_SIZE; i += desc_size(d)) {
333 d = lguest_devices + i; 360 d = lguest_devices + i;
334 361
335 /* Once we hit a zero, stop. */ 362 /* Once we hit a zero, stop. */
336 if (d->type == 0) 363 if (d->type == 0)
337 break; 364 break;
338 365
366 printk("Device at %i has size %u\n", i, desc_size(d));
339 add_lguest_device(d); 367 add_lguest_device(d);
340 } 368 }
341} 369}