1 /*
2 * Copyright (c) 2023, Google Inc. All rights reserved.
3 * Author: codycswong@google.com (Cody Wong)
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files
7 * (the "Software"), to deal in the Software without restriction,
8 * including without limitation the rights to use, copy, modify, merge,
9 * publish, distribute, sublicense, and/or sell copies of the Software,
10 * and to permit persons to whom the Software is furnished to do so,
11 * subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24 #include <assert.h>
25 #include <dev/virtio/9p.h>
26 #include <kernel/event.h>
27 #include <lk/debug.h>
28 #include <lk/err.h>
29 #include <lk/list.h>
30 #include <lk/trace.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include "protocol.h"
35
36 #define LOCAL_TRACE 0
37
38 struct virtio_9p_config {
39 uint16_t tag_len;
40 uint8_t tag[];
41 };
42
43 #define VIRTIO_9P_MOUNT_TAG (1<<0)
44
45 static enum handler_return virtio_9p_irq_driver_callback(
46 struct virtio_device *dev, uint ring, const struct vring_used_elem *e);
47
48 static struct list_node p9_devices = LIST_INITIAL_VALUE(p9_devices);
49
dump_feature_bits(uint32_t feature)50 static void dump_feature_bits(uint32_t feature)
51 {
52 LTRACEF("virtio-9p host features (0x%x):", feature);
53 if (feature & VIRTIO_9P_MOUNT_TAG) LTRACEF(" MOUNT_TAG");
54 LTRACEF("\n");
55 }
56
virtio_9p_init(struct virtio_device * dev,uint32_t host_features)57 status_t virtio_9p_init(struct virtio_device *dev, uint32_t host_features)
58 {
59 dump_feature_bits(host_features);
60
61 /* allocate a new 9p device */
62 struct virtio_9p_dev *p9dev = calloc(1, sizeof(struct virtio_9p_dev));
63 if (!p9dev)
64 return ERR_NO_MEMORY;
65
66 p9dev->dev = dev;
67 dev->priv = p9dev;
68 p9dev->lock = SPIN_LOCK_INITIAL_VALUE;
69 // Assuming there can be only one outstanding request.
70 p9dev->req.status = P9_REQ_S_UNKNOWN;
71 mutex_init(&p9dev->req_lock);
72 p9dev->msize = VIRTIO_9P_DEFAULT_MSIZE;
73
74 // Add the 9p device to the device list
75 list_add_tail(&p9_devices, &p9dev->list);
76
77 /* make sure the device is reset */
78 virtio_reset_device(dev);
79
80 p9dev->config = (struct virtio_9p_config *)dev->config_ptr;
81 #if LOCAL_TRACE
82 LTRACEF("tag_len: %u\n", p9dev->config->tag_len);
83 LTRACEF("tag: ");
84 for (int i = 0; i < p9dev->config->tag_len; ++i) {
85 printf("%c", p9dev->config->tag[i]);
86 }
87 printf("\n");
88 #endif
89
90 /* ack and set the driver status bit */
91 virtio_status_acknowledge_driver(dev);
92
93 virtio_alloc_ring(dev, VIRTIO_9P_RING_IDX, VIRTIO_9P_RING_SIZE);
94
95 /* set our irq handler */
96 dev->irq_driver_callback = &virtio_9p_irq_driver_callback;
97
98 /* set DRIVER_OK */
99 virtio_status_driver_ok(dev);
100
101 // register a fake block device
102 static uint8_t found_index = 0;
103 char buf[16];
104 snprintf(buf, sizeof(buf), "v9p%u", found_index++);
105 bio_initialize_bdev(&p9dev->bdev, buf, 1, 0,
106 0, NULL, BIO_FLAGS_NONE);
107
108 // override our block device hooks
109 p9dev->bdev.read_block = NULL;
110 p9dev->bdev.write_block = NULL;
111
112 bio_register_device(&p9dev->bdev);
113
114 return NO_ERROR;
115 }
116
virtio_9p_start(struct virtio_device * dev)117 status_t virtio_9p_start(struct virtio_device *dev)
118 {
119 struct virtio_9p_dev *p9dev = (struct virtio_9p_dev *)dev->priv;
120 status_t ret;
121
122 // connect to the 9p server with 9P2000.L
123 virtio_9p_msg_t tver = {
124 .msg_type = P9_TVERSION,
125 .tag = P9_TAG_NOTAG,
126 .msg.tversion = {.msize = p9dev->msize, .version = "9P2000.L"}
127 };
128 virtio_9p_msg_t rver = {};
129
130 if ((ret = virtio_9p_rpc(dev, &tver, &rver)) != NO_ERROR)
131 return ret;
132
133 // assert the server support 9P2000.L version
134 ASSERT(strcmp(rver.msg.rversion.version, "9P2000.L") == 0);
135 p9dev->msize = rver.msg.rversion.msize;
136
137 virtio_9p_msg_destroy(&rver);
138
139 return NO_ERROR;
140 }
141
virtio_9p_irq_driver_callback(struct virtio_device * dev,uint ring,const struct vring_used_elem * e)142 static enum handler_return virtio_9p_irq_driver_callback(
143 struct virtio_device *dev, uint ring, const struct vring_used_elem *e)
144 {
145 struct virtio_9p_dev *p9dev = (struct virtio_9p_dev *)dev->priv;
146 uint16_t id = e->id;
147 uint16_t id_next;
148 struct vring_desc *desc = virtio_desc_index_to_desc(dev, ring, id);
149 struct p9_req *req = &p9dev->req;
150
151 LTRACEF("dev %p, ring %u, e %p, id %u, len %u\n", dev, ring, e, e->id, e->len);
152 #if LOCAL_TRACE
153 virtio_dump_desc(desc);
154 #endif
155
156 ASSERT(req->status == P9_REQ_S_SENT);
157 ASSERT(desc);
158 ASSERT(desc->flags & VRING_DESC_F_NEXT);
159
160 spin_lock(&p9dev->lock);
161
162 // drop the T-message desc
163 id_next = desc->next;
164 desc = virtio_desc_index_to_desc(dev, VIRTIO_9P_RING_IDX, id_next);
165 #if LOCAL_TRACE
166 virtio_dump_desc(desc);
167 #endif
168 req->rc.size = e->len;
169 req->status = P9_REQ_S_RECEIVED;
170
171 // free the desc
172 virtio_free_desc(dev, ring, id);
173 virtio_free_desc(dev, ring, id_next);
174
175 spin_unlock(&p9dev->lock);
176
177 /* wake up the rpc */
178 event_signal(&req->io_event, false);
179
180 return INT_RESCHEDULE;
181 }
182
bdev_to_virtio_9p_dev(bdev_t * bdev)183 static struct virtio_9p_dev *bdev_to_virtio_9p_dev(bdev_t *bdev)
184 {
185 return containerof(bdev, struct virtio_9p_dev, bdev);
186 }
187
virtio_9p_bdev_to_virtio_device(bdev_t * bdev)188 struct virtio_device *virtio_9p_bdev_to_virtio_device(bdev_t *bdev)
189 {
190 return bdev_to_virtio_9p_dev(bdev)->dev;
191 }
192
virtio_get_9p_device(uint index)193 struct virtio_device *virtio_get_9p_device(uint index)
194 {
195 struct virtio_9p_dev *p9dev;
196 uint count = 0;
197
198 list_for_every_entry(&p9_devices, p9dev, struct virtio_9p_dev, list) {
199 if (count == index)
200 return p9dev->dev;
201 count++;
202 }
203
204 return NULL;
205 }
206