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