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#include <virtio/virtio.h>
13#include <virtio/virtqueue.h>
14#include <virtio/virtio_guest.h>
15
16#include <if/virtio_defs.h>
17#include <if/virtio_defs.h>
18
19#include "channel.h"
20#include "debug.h"
21
22
23static struct virtio_binding *virtio_binding;
24
25static iref_t virtio_rpc_svc_iref;
26
27enum virtio_binding_state {
28    RPC_CLIENT_STATE_INVALID,
29    RPC_CLIENT_STATE_BINDING,
30    RPC_CLIENT_STATE_FAILED,
31    RPC_CLIENT_STATE_READY
32};
33
34static enum virtio_binding_state rpc_client_state = RPC_CLIENT_STATE_INVALID;
35
36
37/**
38 *
39 */
40static  errval_t open_device(uint8_t backend,
41                             struct capref *ret_frame)
42{
43    errval_t err, msg_err;
44
45    VIRTIO_DEBUG_CHAN("open device\n");
46
47    if (rpc_client_state != RPC_CLIENT_STATE_READY) {
48        /* TODO: error code */
49        return -1;
50    }
51
52    err = virtio_binding->rpc_tx_vtbl.open(virtio_binding,
53                                      backend,
54                                      &msg_err,
55                                      ret_frame);
56    if (err_is_fail(err)) {
57        return err;
58    }
59    return msg_err;
60}
61
62/**
63 *
64 */
65static  errval_t close_device(void)
66{
67    errval_t err;
68
69    if (rpc_client_state != RPC_CLIENT_STATE_READY) {
70        /* TODO: error code */
71        return -1;
72    }
73
74    err =  virtio_binding->rpc_tx_vtbl.close(virtio_binding);
75    if (err_is_fail(err)) {
76        return err;
77    }
78    return SYS_ERR_OK;
79}
80
81/**
82 *
83 */
84static  errval_t add_vring(struct virtqueue *vq)
85{
86    errval_t err, msg_err;
87
88    if (rpc_client_state != RPC_CLIENT_STATE_READY) {
89        /* TODO: error code */
90        return -1;
91    }
92
93    struct capref frame;
94    virtio_virtqueue_get_vring_cap(vq, &frame);
95
96    uint16_t id = virtio_virtqueue_get_queue_index(vq);
97
98    uint8_t buffers = virtio_virtqueue_has_buffers(vq);
99
100    uint16_t ndesc = virtio_virtqueue_get_num_desc(vq);
101
102    err =  virtio_binding->rpc_tx_vtbl.add(virtio_binding, id, ndesc, buffers, frame, &msg_err);
103    if (err_is_fail(err)) {
104        return err;
105    }
106    return msg_err;
107}
108
109/**
110 *
111 */
112static  errval_t extend_vring(uint16_t vq_id,
113                              struct capref vbuf)
114{
115    errval_t err, msg_err;
116
117    if (rpc_client_state != RPC_CLIENT_STATE_READY) {
118        /* TODO: error code */
119        return -1;
120    }
121
122    err =  virtio_binding->rpc_tx_vtbl.extend(virtio_binding, vq_id, vbuf, &msg_err);
123    if (err_is_fail(err)) {
124        return err;
125    }
126    return msg_err;
127}
128
129/**
130 *
131 */
132static  errval_t request_mem(uint64_t size,
133                             struct capref *cap)
134{
135    errval_t err, msg_err;
136
137    if (rpc_client_state != RPC_CLIENT_STATE_READY) {
138        /* TODO: error code */
139        return -1;
140    }
141
142    err =  virtio_binding->rpc_tx_vtbl.req(virtio_binding, size, &msg_err, cap);
143    if (err_is_fail(err)) {
144        return err;
145    }
146    return msg_err;
147}
148
149
150static struct virtio_guest_chan_fn vguest_fc_fn =  {
151    .open = open_device,
152    .close = close_device,
153    .add = add_vring,
154    .ext = extend_vring,
155    .req = request_mem,
156};
157
158static void bind_cb(void *st, errval_t err, struct virtio_binding *b)
159{
160    if(err_is_fail(err)) {
161        rpc_client_state = RPC_CLIENT_STATE_FAILED;
162        return;
163    }
164
165    VIRTIO_DEBUG_CHAN("Initializing RPC client\n");
166    virtio_binding = b;
167    virtio_rpc_client_init(virtio_binding);
168    vguest_chan_fn = &vguest_fc_fn;
169
170    rpc_client_state = RPC_CLIENT_STATE_READY;
171}
172
173
174
175
176
177errval_t virtio_guest_flounder_init(char *iface)
178{
179    errval_t err;
180
181    VIRTIO_DEBUG_CHAN("looking up iface [%s]\n", iface);
182    err = nameservice_blocking_lookup(iface, &virtio_rpc_svc_iref);
183    if (err_is_fail(err)) {
184        return err;
185    }
186
187    VIRTIO_DEBUG_CHAN("initiate binding to iref [%u]\n", virtio_rpc_svc_iref);
188
189    rpc_client_state = RPC_CLIENT_STATE_BINDING;
190
191    err = virtio_bind(virtio_rpc_svc_iref,
192                      bind_cb,
193                      NULL,
194                      get_default_waitset(),
195                      IDC_BIND_FLAGS_DEFAULT);
196
197    if (err_is_fail(err)) {
198        return err;
199    }
200
201    VIRTIO_DEBUG_CHAN("Waiting for binding reply\n");
202    while(rpc_client_state == RPC_CLIENT_STATE_BINDING) {
203        messages_wait_and_handle_next();
204    }
205
206    if (rpc_client_state == RPC_CLIENT_STATE_FAILED) {
207        VIRTIO_DEBUG_CHAN("Bind failed\n");
208        return FLOUNDER_ERR_BIND;
209    }
210
211    VIRTIO_DEBUG_CHAN("Host channel ready\n");
212
213    return SYS_ERR_OK;
214}
215