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 22namespace virtio { 23 24void 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 32Ring::Ring(Device* device) 33 : device_(device) { 34 35 memset(&ring_buf_, 0, sizeof(ring_buf_)); 36} 37 38Ring::~Ring() { 39 io_buffer_release(&ring_buf_); 40} 41 42zx_status_t Ring::Init(uint16_t index, uint16_t count) { 43 LTRACEF("index %u, count %u\n", index, count); 44 45 // XXX check that count is a power of 2 46 47 index_ = index; 48 49 // make sure the count is available in this ring 50 uint16_t max_ring_size = device_->GetRingSize(index); 51 if (count > max_ring_size) { 52 zxlogf(ERROR, "ring init count too big for hardware %u > %u\n", count, max_ring_size); 53 return ZX_ERR_OUT_OF_RANGE; 54 } 55 56 // allocate a ring 57 size_t size = vring_size(count, PAGE_SIZE); 58 LTRACEF("need %zu bytes\n", size); 59 60 zx_status_t status = io_buffer_init(&ring_buf_, device_->bti().get(), size, 61 IO_BUFFER_RW | IO_BUFFER_CONTIG); 62 if (status != ZX_OK) { 63 return status; 64 } 65 66 LTRACEF("allocated vring at %p, physical address %#" PRIxPTR "\n", 67 io_buffer_virt(&ring_buf_), io_buffer_phys(&ring_buf_)); 68 69 /* initialize the ring */ 70 vring_init(&ring_, count, io_buffer_virt(&ring_buf_), PAGE_SIZE); 71 ring_.free_list = 0xffff; 72 ring_.free_count = 0; 73 74 /* add all the descriptors to the free list */ 75 for (uint16_t i = 0; i < count; i++) { 76 FreeDesc(i); 77 } 78 79 /* register the ring with the device */ 80 zx_paddr_t pa_desc = io_buffer_phys(&ring_buf_); 81 zx_paddr_t pa_avail = pa_desc + ((uintptr_t)ring_.avail - (uintptr_t)ring_.desc); 82 zx_paddr_t pa_used = pa_desc + ((uintptr_t)ring_.used - (uintptr_t)ring_.desc); 83 device_->SetRing(index_, count, pa_desc, pa_avail, pa_used); 84 85 return ZX_OK; 86} 87 88void Ring::FreeDesc(uint16_t desc_index) { 89 LTRACEF("index %u free_count %u\n", desc_index, ring_.free_count); 90 ring_.desc[desc_index].next = ring_.free_list; 91 ring_.free_list = desc_index; 92 ring_.free_count++; 93} 94 95struct vring_desc* Ring::AllocDescChain(uint16_t count, uint16_t* start_index) { 96 if (ring_.free_count < count) 97 return NULL; 98 99 /* start popping entries off the chain */ 100 struct vring_desc* last = 0; 101 uint16_t last_index = 0; 102 while (count > 0) { 103 uint16_t i = ring_.free_list; 104 assert(i < ring_.num); 105 106 struct vring_desc* desc = &ring_.desc[i]; 107 108 ring_.free_list = desc->next; 109 ring_.free_count--; 110 111 if (last) { 112 desc->flags |= VRING_DESC_F_NEXT; 113 desc->next = last_index; 114 } else { 115 // first one 116 desc->flags &= static_cast<uint16_t>(~VRING_DESC_F_NEXT); 117 desc->next = 0; 118 } 119 last = desc; 120 last_index = i; 121 count--; 122 } 123 124 if (start_index) 125 *start_index = last_index; 126 127 return last; 128} 129 130void Ring::SubmitChain(uint16_t desc_index) { 131 LTRACEF("desc %u\n", desc_index); 132 133 /* add the chain to the available list */ 134 struct vring_avail* avail = ring_.avail; 135 136 avail->ring[avail->idx & ring_.num_mask] = desc_index; 137 //mb(); 138 avail->idx++; 139} 140 141void Ring::Kick() { 142 LTRACE_ENTRY; 143 144 device_->RingKick(index_); 145} 146 147} // namespace virtio 148