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