1/*
2 * Copyright (c) 2014 ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <barrelfish/barrelfish.h>
11#include <barrelfish/nameservice_client.h>
12
13#include <virtio/virtio.h>
14#include <virtio/virtio_device.h>
15#include <virtio/virtqueue_host.h>
16#include <virtio/virtio_host.h>
17
18#include <if/virtio_defs.h>
19#include <if/virtio_defs.h>
20
21#include "channel.h"
22#include "device.h"
23#include "debug.h"
24
25static uint8_t device_open = 0x0;
26
27static iref_t virtio_rpc_svc_iref;
28
29enum virtio_rpc_host_state {
30    RPC_HOST_STATE_INVALID,
31    RPC_HOST_STATE_EXPORTING,
32    RPC_HOST_STATE_FAILED,
33    RPC_HOST_STATE_READY
34};
35
36static enum virtio_rpc_host_state rpc_client_state = RPC_HOST_STATE_INVALID;
37
38/* -------------------- virtio_open() --------------------------------------- */
39
40struct open_response_state
41{
42    struct virtio_binding *b;
43    errval_t err;
44    struct capref frame;
45};
46
47struct open_response_state open_err_st;
48
49static void virtio_open_response_cb(void *a)
50{
51    if (a != &open_err_st) {
52        free(a);
53    }
54}
55
56static void virtio_open_response(void *a)
57{
58    errval_t err;
59    struct open_response_state *st = a;
60
61    struct event_closure txcont = MKCONT(virtio_open_response_cb, st);
62    err = virtio_open_response__tx(st->b, txcont, st->err, st->frame);
63    if (err_is_fail(err)) {
64        if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
65            txcont = MKCONT(virtio_open_response, st);
66            err = st->b->register_send(st->b, get_default_waitset(), txcont);
67            if (err_is_fail(err)) {
68                DEBUG_ERR(err, "register send failed\n");
69                free(st);
70            }
71        } else {
72            DEBUG_ERR(err, "sending reply failed\n");
73            free(st);
74        }
75    }
76}
77
78static void virtio_open_call__rx(struct virtio_binding *_binding,
79                                  uint8_t backend)
80{
81    errval_t err;
82
83    VIRTIO_DEBUG_CHAN("Received device_open rpc call\n");
84
85    if (device_open) {
86        open_err_st.err = VIRTIO_ERR_DEVICE_STATUS;
87        virtio_open_response(&open_err_st);
88        return;
89    } else {
90        device_open = 0x1;
91    }
92
93    assert(_binding->st);
94
95    struct virtio_device *vdev = _binding->st;
96
97    struct open_response_state *st = malloc(sizeof(struct open_response_state));
98    if (st == NULL) {
99        device_open = 0x0;
100        open_err_st.err = LIB_ERR_MALLOC_FAIL;
101        virtio_open_response(&open_err_st);
102        return;
103    }
104
105    assert(vdev->cb_h);
106    assert(vdev->cb_h->open);
107
108    err = vdev->cb_h->open(vdev, backend, &st->frame);
109    if (err_is_fail(err)) {
110        device_open = 0x0;
111        open_err_st.err = err;
112        virtio_open_response(&open_err_st);
113    }
114
115    st->b = _binding;
116    st->err = err;
117
118    virtio_open_response(st);
119}
120
121/* -------------------- virtio_close() -------------------------------------- */
122
123static void virtio_close_response(void *a)
124{
125
126}
127
128static  void virtio_close_call__rx(struct virtio_binding *_binding)
129{
130    virtio_close_response(_binding);
131}
132
133/* -------------------- virtio_add() ---------------------------------------- */
134
135struct add_response_state
136{
137    struct virtio_binding *b;
138    errval_t err;
139};
140
141struct open_response_state add_err_st;
142
143static void virtio_add_response_cb(void *a)
144{
145    if (a != &add_err_st) {
146        free(a);
147    }
148}
149
150static void virtio_add_response(void *a)
151{
152    errval_t err;
153    struct add_response_state *st = a;
154
155    struct event_closure txcont = MKCONT(virtio_add_response_cb, st);
156    err = virtio_add_response__tx(st->b, txcont, st->err);
157    if (err_is_fail(err)) {
158        if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
159            txcont = MKCONT(virtio_add_response, st);
160            err = st->b->register_send(st->b, get_default_waitset(), txcont);
161            if (err_is_fail(err)) {
162                DEBUG_ERR(err, "virtio_add_response: register send failed\n");
163                free(st);
164            }
165        } else {
166            DEBUG_ERR(err, "virtio_add_response: sending reply failed\n");
167            free(st);
168        }
169    }
170}
171
172static  void virtio_add_call__rx(struct virtio_binding *_binding,
173                                 uint16_t vq_id,
174                                 uint16_t ndesc,
175                                 uint8_t has_buffers,
176                                 struct capref vring)
177{
178    VIRTIO_DEBUG_CHAN("Received virtq_add rpc call\n");
179
180    if (!_binding->st) {
181        /* cannot open a device twice! */
182        add_err_st.err = VIRTIO_ERR_DEVICE_STATUS;
183        virtio_open_response(&add_err_st);
184        return;
185    }
186
187    struct open_response_state *st = malloc(sizeof(struct open_response_state));
188    if (st == NULL) {
189        add_err_st.err = LIB_ERR_MALLOC_FAIL;
190        virtio_open_response(&add_err_st);
191        return;
192    }
193
194    struct virtio_device *vdev = _binding->st;
195
196    assert(vdev->cb_h);
197    assert(vdev->cb_h->add);
198
199    st->b = _binding;
200
201    st->err = virtio_vq_host_init_vring(vdev, vring, vq_id, ndesc, has_buffers);
202    if (err_is_fail(st->err)) {
203        virtio_add_response(st);
204        return;
205    }
206
207    st->b = _binding;
208    st->err = vdev->cb_h->add(vdev,vring, ndesc, has_buffers, vq_id);
209
210    virtio_add_response(st);
211}
212
213
214/* -------------------- virtio_ext() ---------------------------------------- */
215
216static void virtio_ext_response(void *a)
217{
218
219}
220
221static  void virtio_extend_call__rx(struct virtio_binding *_binding,
222                                    uint16_t vq_id,
223                                    struct capref vbuf)
224{
225    virtio_ext_response(_binding);
226}
227
228/* -------------------- virtio_req() ---------------------------------------- */
229
230static void virtio_req_response(void *a)
231{
232
233}
234
235static  void virtio_req_call__rx(struct virtio_binding *_binding,
236                                 uint64_t size)
237{
238    virtio_req_response(_binding);
239}
240
241
242
243
244struct virtio_rx_vtbl s_rx_vtbl = {
245    .open_call = virtio_open_call__rx,
246    .close_call = virtio_close_call__rx,
247    .add_call = virtio_add_call__rx,
248    .extend_call = virtio_extend_call__rx,
249    .req_call = virtio_req_call__rx,
250};
251
252
253static errval_t connect_cb(void *st, struct virtio_binding *b)
254{
255    VIRTIO_DEBUG_CHAN("New guest connection\n");
256
257    /*
258     * this would be the point where we should initialize a new VirtIO device,
259     * however we support just one device at this stage.
260     *
261     * We assign the current device as the binding state.
262     */
263    b->st = st;
264
265    b->rx_vtbl = s_rx_vtbl;
266
267    return SYS_ERR_OK;
268}
269
270static void export_cb(void *st, errval_t err, iref_t iref)
271{
272    if (err_is_fail(err)) {
273        rpc_client_state = RPC_HOST_STATE_FAILED;
274        DEBUG_ERR(err, "Export failed");
275        return;
276    }
277
278    struct virtio_device *vdev = st;
279
280    virtio_rpc_svc_iref = iref;
281
282    VIRTIO_DEBUG_CHAN("Registering [%s] with iref [%u]\n", vdev->hc_iface, iref);
283    err = nameservice_register(vdev->hc_iface, iref);
284    if (err_is_fail(err)) {
285        DEBUG_ERR(err, "nameservice_register failed");
286        rpc_client_state = RPC_HOST_STATE_FAILED;
287        return;
288    }
289
290    rpc_client_state = RPC_HOST_STATE_READY;
291}
292
293
294
295
296errval_t virtio_host_flounder_init(struct virtio_device *vdev)
297{
298    errval_t err;
299
300    VIRTIO_DEBUG_CHAN("initiate exporting service\n");
301
302    rpc_client_state = RPC_HOST_STATE_EXPORTING;
303
304    err = virtio_export(vdev,
305                        export_cb, connect_cb,
306                        get_default_waitset(),
307                        IDC_BIND_FLAGS_DEFAULT);
308
309    if (err_is_fail(err)) {
310        return err;
311    }
312
313    VIRTIO_DEBUG_CHAN("Waiting for export reply\n");
314    while(rpc_client_state == RPC_HOST_STATE_EXPORTING) {
315        messages_wait_and_handle_next();
316    }
317
318    if (rpc_client_state == RPC_HOST_STATE_FAILED) {
319        VIRTIO_DEBUG_CHAN("Export failed\n");
320        return FLOUNDER_ERR_BIND;
321    }
322
323    VIRTIO_DEBUG_CHAN("Service ready\n");
324
325    return SYS_ERR_OK;
326}
327