1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ring.h"
6 
7 #include <assert.h>
8 #include <inttypes.h>
9 #include <limits.h>
10 #include <stdint.h>
11 #include <string.h>
12 
13 #include <ddk/debug.h>
14 #include <ddk/driver.h>
15 #include <lib/zx/vmar.h>
16 
17 #include "device.h"
18 #include "trace.h"
19 
20 #define LOCAL_TRACE 0
21 
22 namespace virtio {
23 
virtio_dump_desc(const struct vring_desc * desc)24 void virtio_dump_desc(const struct vring_desc* desc) {
25     printf("vring descriptor %p: ", desc);
26     printf("[addr=%#" PRIx64 ", ", desc->addr);
27     printf("len=%d, ", desc->len);
28     printf("flags=%#04hx, ", desc->flags);
29     printf("next=%#04hx]\n", desc->next);
30 }
31 
Ring(Device * device)32 Ring::Ring(Device* device)
33     : device_(device) {
34 
35     memset(&ring_buf_, 0, sizeof(ring_buf_));
36 }
37 
~Ring()38 Ring::~Ring() {
39     io_buffer_release(&ring_buf_);
40 }
41 
Init(uint16_t index)42 zx_status_t Ring::Init(uint16_t index) {
43   return Init(/*index=*/index, /*count=*/device_->GetRingSize(index));
44 }
45 
Init(uint16_t index,uint16_t count)46 zx_status_t Ring::Init(uint16_t index, uint16_t count) {
47     LTRACEF("index %u, count %u\n", index, count);
48 
49     // XXX check that count is a power of 2
50 
51     index_ = index;
52 
53     // make sure the count is available in this ring
54     uint16_t max_ring_size = device_->GetRingSize(index);
55     if (count > max_ring_size) {
56         zxlogf(ERROR, "ring init count too big for hardware %u > %u\n", count, max_ring_size);
57         return ZX_ERR_OUT_OF_RANGE;
58     }
59 
60     // allocate a ring
61     size_t size = vring_size(count, PAGE_SIZE);
62     LTRACEF("need %zu bytes\n", size);
63 
64     zx_status_t status = io_buffer_init(&ring_buf_, device_->bti().get(), size,
65                                         IO_BUFFER_RW | IO_BUFFER_CONTIG);
66     if (status != ZX_OK) {
67         return status;
68     }
69 
70     LTRACEF("allocated vring at %p, physical address %#" PRIxPTR "\n",
71             io_buffer_virt(&ring_buf_), io_buffer_phys(&ring_buf_));
72 
73     /* initialize the ring */
74     vring_init(&ring_, count, io_buffer_virt(&ring_buf_), PAGE_SIZE);
75     ring_.free_list = 0xffff;
76     ring_.free_count = 0;
77 
78     /* add all the descriptors to the free list */
79     for (uint16_t i = 0; i < count; i++) {
80         FreeDesc(i);
81     }
82 
83     /* register the ring with the device */
84     zx_paddr_t pa_desc = io_buffer_phys(&ring_buf_);
85     zx_paddr_t pa_avail = pa_desc + ((uintptr_t)ring_.avail - (uintptr_t)ring_.desc);
86     zx_paddr_t pa_used = pa_desc + ((uintptr_t)ring_.used - (uintptr_t)ring_.desc);
87     device_->SetRing(index_, count, pa_desc, pa_avail, pa_used);
88 
89     return ZX_OK;
90 }
91 
FreeDesc(uint16_t desc_index)92 void Ring::FreeDesc(uint16_t desc_index) {
93     LTRACEF("index %u free_count %u\n", desc_index, ring_.free_count);
94     ring_.desc[desc_index].next = ring_.free_list;
95     ring_.free_list = desc_index;
96     ring_.free_count++;
97 }
98 
AllocDescChain(uint16_t count,uint16_t * start_index)99 struct vring_desc* Ring::AllocDescChain(uint16_t count, uint16_t* start_index) {
100     if (ring_.free_count < count)
101         return NULL;
102 
103     /* start popping entries off the chain */
104     struct vring_desc* last = 0;
105     uint16_t last_index = 0;
106     while (count > 0) {
107         uint16_t i = ring_.free_list;
108         assert(i < ring_.num);
109 
110         struct vring_desc* desc = &ring_.desc[i];
111 
112         ring_.free_list = desc->next;
113         ring_.free_count--;
114 
115         if (last) {
116             desc->flags |= VRING_DESC_F_NEXT;
117             desc->next = last_index;
118         } else {
119             // first one
120             desc->flags &= static_cast<uint16_t>(~VRING_DESC_F_NEXT);
121             desc->next = 0;
122         }
123         last = desc;
124         last_index = i;
125         count--;
126     }
127 
128     if (start_index)
129         *start_index = last_index;
130 
131     return last;
132 }
133 
SubmitChain(uint16_t desc_index)134 void Ring::SubmitChain(uint16_t desc_index) {
135     LTRACEF("desc %u\n", desc_index);
136 
137     /* add the chain to the available list */
138     struct vring_avail* avail = ring_.avail;
139 
140     avail->ring[avail->idx & ring_.num_mask] = desc_index;
141     //mb();
142     avail->idx++;
143 }
144 
Kick()145 void Ring::Kick() {
146     LTRACE_ENTRY;
147 
148     device_->RingKick(index_);
149 }
150 
151 } // namespace virtio
152