mirror of
				https://github.com/qemu/qemu.git
				synced 2025-10-31 12:07:31 +00:00 
			
		
		
		
	 e8d4046559
			
		
	
	
		e8d4046559
		
	
	
	
	
		
			
			Clean up includes so that osdep.h is included first and headers which it implies are not included manually. This commit was created with scripts/clean-includes. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 1453832250-766-19-git-send-email-peter.maydell@linaro.org
		
			
				
	
	
		
			375 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			375 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * QEMU rocker switch emulation - Descriptor ring support
 | |
|  *
 | |
|  * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation; either version 2 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | |
|  * GNU General Public License for more details.
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "net/net.h"
 | |
| #include "hw/hw.h"
 | |
| #include "hw/pci/pci.h"
 | |
| 
 | |
| #include "rocker.h"
 | |
| #include "rocker_hw.h"
 | |
| #include "rocker_desc.h"
 | |
| 
 | |
| struct desc_ring {
 | |
|     hwaddr base_addr;
 | |
|     uint32_t size;
 | |
|     uint32_t head;
 | |
|     uint32_t tail;
 | |
|     uint32_t ctrl;
 | |
|     uint32_t credits;
 | |
|     Rocker *r;
 | |
|     DescInfo *info;
 | |
|     int index;
 | |
|     desc_ring_consume *consume;
 | |
|     unsigned msix_vector;
 | |
| };
 | |
| 
 | |
| struct desc_info {
 | |
|     DescRing *ring;
 | |
|     RockerDesc desc;
 | |
|     char *buf;
 | |
|     size_t buf_size;
 | |
| };
 | |
| 
 | |
| uint16_t desc_buf_size(DescInfo *info)
 | |
| {
 | |
|     return le16_to_cpu(info->desc.buf_size);
 | |
| }
 | |
| 
 | |
| uint16_t desc_tlv_size(DescInfo *info)
 | |
| {
 | |
|     return le16_to_cpu(info->desc.tlv_size);
 | |
| }
 | |
| 
 | |
| char *desc_get_buf(DescInfo *info, bool read_only)
 | |
| {
 | |
|     PCIDevice *dev = PCI_DEVICE(info->ring->r);
 | |
|     size_t size = read_only ? le16_to_cpu(info->desc.tlv_size) :
 | |
|                               le16_to_cpu(info->desc.buf_size);
 | |
| 
 | |
|     if (size > info->buf_size) {
 | |
|         info->buf = g_realloc(info->buf, size);
 | |
|         info->buf_size = size;
 | |
|     }
 | |
| 
 | |
|     if (!info->buf) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (pci_dma_read(dev, le64_to_cpu(info->desc.buf_addr), info->buf, size)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     return info->buf;
 | |
| }
 | |
| 
 | |
| int desc_set_buf(DescInfo *info, size_t tlv_size)
 | |
| {
 | |
|     PCIDevice *dev = PCI_DEVICE(info->ring->r);
 | |
| 
 | |
|     if (tlv_size > info->buf_size) {
 | |
|         DPRINTF("ERROR: trying to write more to desc buf than it "
 | |
|                 "can hold buf_size %zu tlv_size %zu\n",
 | |
|                 info->buf_size, tlv_size);
 | |
|         return -ROCKER_EMSGSIZE;
 | |
|     }
 | |
| 
 | |
|     info->desc.tlv_size = cpu_to_le16(tlv_size);
 | |
|     pci_dma_write(dev, le64_to_cpu(info->desc.buf_addr), info->buf, tlv_size);
 | |
| 
 | |
|     return ROCKER_OK;
 | |
| }
 | |
| 
 | |
| DescRing *desc_get_ring(DescInfo *info)
 | |
| {
 | |
|     return info->ring;
 | |
| }
 | |
| 
 | |
| int desc_ring_index(DescRing *ring)
 | |
| {
 | |
|     return ring->index;
 | |
| }
 | |
| 
 | |
| static bool desc_ring_empty(DescRing *ring)
 | |
| {
 | |
|     return ring->head == ring->tail;
 | |
| }
 | |
| 
 | |
| bool desc_ring_set_base_addr(DescRing *ring, uint64_t base_addr)
 | |
| {
 | |
|     if (base_addr & 0x7) {
 | |
|         DPRINTF("ERROR: ring[%d] desc base addr (0x" TARGET_FMT_plx
 | |
|                 ") not 8-byte aligned\n", ring->index, base_addr);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     ring->base_addr = base_addr;
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| uint64_t desc_ring_get_base_addr(DescRing *ring)
 | |
| {
 | |
|     return ring->base_addr;
 | |
| }
 | |
| 
 | |
| bool desc_ring_set_size(DescRing *ring, uint32_t size)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     if (size < 2 || size > 0x10000 || (size & (size - 1))) {
 | |
|         DPRINTF("ERROR: ring[%d] size (%d) not a power of 2 "
 | |
|                 "or in range [2, 64K]\n", ring->index, size);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i < ring->size; i++) {
 | |
|         g_free(ring->info[i].buf);
 | |
|     }
 | |
| 
 | |
|     ring->size = size;
 | |
|     ring->head = ring->tail = 0;
 | |
| 
 | |
|     ring->info = g_renew(DescInfo, ring->info, size);
 | |
|     if (!ring->info) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     memset(ring->info, 0, size * sizeof(DescInfo));
 | |
| 
 | |
|     for (i = 0; i < size; i++) {
 | |
|         ring->info[i].ring = ring;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| uint32_t desc_ring_get_size(DescRing *ring)
 | |
| {
 | |
|     return ring->size;
 | |
| }
 | |
| 
 | |
| static DescInfo *desc_read(DescRing *ring, uint32_t index)
 | |
| {
 | |
|     PCIDevice *dev = PCI_DEVICE(ring->r);
 | |
|     DescInfo *info = &ring->info[index];
 | |
|     hwaddr addr = ring->base_addr + (sizeof(RockerDesc) * index);
 | |
| 
 | |
|     pci_dma_read(dev, addr, &info->desc, sizeof(info->desc));
 | |
| 
 | |
|     return info;
 | |
| }
 | |
| 
 | |
| static void desc_write(DescRing *ring, uint32_t index)
 | |
| {
 | |
|     PCIDevice *dev = PCI_DEVICE(ring->r);
 | |
|     DescInfo *info = &ring->info[index];
 | |
|     hwaddr addr = ring->base_addr + (sizeof(RockerDesc) * index);
 | |
| 
 | |
|     pci_dma_write(dev, addr, &info->desc, sizeof(info->desc));
 | |
| }
 | |
| 
 | |
| static bool desc_ring_base_addr_check(DescRing *ring)
 | |
| {
 | |
|     if (!ring->base_addr) {
 | |
|         DPRINTF("ERROR: ring[%d] not-initialized desc base address!\n",
 | |
|                 ring->index);
 | |
|         return false;
 | |
|     }
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| static DescInfo *__desc_ring_fetch_desc(DescRing *ring)
 | |
| {
 | |
|     return desc_read(ring, ring->tail);
 | |
| }
 | |
| 
 | |
| DescInfo *desc_ring_fetch_desc(DescRing *ring)
 | |
| {
 | |
|     if (desc_ring_empty(ring) || !desc_ring_base_addr_check(ring)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     return desc_read(ring, ring->tail);
 | |
| }
 | |
| 
 | |
| static bool __desc_ring_post_desc(DescRing *ring, int err)
 | |
| {
 | |
|     uint16_t comp_err = 0x8000 | (uint16_t)-err;
 | |
|     DescInfo *info = &ring->info[ring->tail];
 | |
| 
 | |
|     info->desc.comp_err = cpu_to_le16(comp_err);
 | |
|     desc_write(ring, ring->tail);
 | |
|     ring->tail = (ring->tail + 1) % ring->size;
 | |
| 
 | |
|     /* return true if starting credit count */
 | |
| 
 | |
|     return ring->credits++ == 0;
 | |
| }
 | |
| 
 | |
| bool desc_ring_post_desc(DescRing *ring, int err)
 | |
| {
 | |
|     if (desc_ring_empty(ring)) {
 | |
|         DPRINTF("ERROR: ring[%d] trying to post desc to empty ring\n",
 | |
|                 ring->index);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (!desc_ring_base_addr_check(ring)) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     return __desc_ring_post_desc(ring, err);
 | |
| }
 | |
| 
 | |
| static bool ring_pump(DescRing *ring)
 | |
| {
 | |
|     DescInfo *info;
 | |
|     bool primed = false;
 | |
|     int err;
 | |
| 
 | |
|     /* If the ring has a consumer, call consumer for each
 | |
|      * desc starting at tail and stopping when tail reaches
 | |
|      * head (the empty ring condition).
 | |
|      */
 | |
| 
 | |
|     if (ring->consume) {
 | |
|         while (ring->head != ring->tail) {
 | |
|             info = __desc_ring_fetch_desc(ring);
 | |
|             err = ring->consume(ring->r, info);
 | |
|             if (__desc_ring_post_desc(ring, err)) {
 | |
|                 primed = true;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return primed;
 | |
| }
 | |
| 
 | |
| bool desc_ring_set_head(DescRing *ring, uint32_t new)
 | |
| {
 | |
|     uint32_t tail = ring->tail;
 | |
|     uint32_t head = ring->head;
 | |
| 
 | |
|     if (!desc_ring_base_addr_check(ring)) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (new >= ring->size) {
 | |
|         DPRINTF("ERROR: trying to set head (%d) past ring[%d] size (%d)\n",
 | |
|                 new, ring->index, ring->size);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (((head < tail) && ((new >= tail) || (new < head))) ||
 | |
|         ((head > tail) && ((new >= tail) && (new < head)))) {
 | |
|         DPRINTF("ERROR: trying to wrap ring[%d] "
 | |
|                 "(head %d, tail %d, new head %d)\n",
 | |
|                 ring->index, head, tail, new);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (new == ring->head) {
 | |
|         DPRINTF("WARNING: setting head (%d) to current head position\n", new);
 | |
|     }
 | |
| 
 | |
|     ring->head = new;
 | |
| 
 | |
|     return ring_pump(ring);
 | |
| }
 | |
| 
 | |
| uint32_t desc_ring_get_head(DescRing *ring)
 | |
| {
 | |
|     return ring->head;
 | |
| }
 | |
| 
 | |
| uint32_t desc_ring_get_tail(DescRing *ring)
 | |
| {
 | |
|     return ring->tail;
 | |
| }
 | |
| 
 | |
| void desc_ring_set_ctrl(DescRing *ring, uint32_t val)
 | |
| {
 | |
|     if (val & ROCKER_DMA_DESC_CTRL_RESET) {
 | |
|         DPRINTF("ring[%d] resetting\n", ring->index);
 | |
|         desc_ring_reset(ring);
 | |
|     }
 | |
| }
 | |
| 
 | |
| bool desc_ring_ret_credits(DescRing *ring, uint32_t credits)
 | |
| {
 | |
|     if (credits > ring->credits) {
 | |
|         DPRINTF("ERROR: trying to return more credits (%d) "
 | |
|                 "than are outstanding (%d)\n", credits, ring->credits);
 | |
|         ring->credits = 0;
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     ring->credits -= credits;
 | |
| 
 | |
|     /* return true if credits are still outstanding */
 | |
| 
 | |
|     return ring->credits > 0;
 | |
| }
 | |
| 
 | |
| uint32_t desc_ring_get_credits(DescRing *ring)
 | |
| {
 | |
|     return ring->credits;
 | |
| }
 | |
| 
 | |
| void desc_ring_set_consume(DescRing *ring, desc_ring_consume *consume,
 | |
|                            unsigned vector)
 | |
| {
 | |
|     ring->consume = consume;
 | |
|     ring->msix_vector = vector;
 | |
| }
 | |
| 
 | |
| unsigned desc_ring_get_msix_vector(DescRing *ring)
 | |
| {
 | |
|     return ring->msix_vector;
 | |
| }
 | |
| 
 | |
| DescRing *desc_ring_alloc(Rocker *r, int index)
 | |
| {
 | |
|     DescRing *ring;
 | |
| 
 | |
|     ring = g_new0(DescRing, 1);
 | |
|     if (!ring) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     ring->r = r;
 | |
|     ring->index = index;
 | |
| 
 | |
|     return ring;
 | |
| }
 | |
| 
 | |
| void desc_ring_free(DescRing *ring)
 | |
| {
 | |
|     g_free(ring->info);
 | |
|     g_free(ring);
 | |
| }
 | |
| 
 | |
| void desc_ring_reset(DescRing *ring)
 | |
| {
 | |
|     ring->base_addr = 0;
 | |
|     ring->size = 0;
 | |
|     ring->head = 0;
 | |
|     ring->tail = 0;
 | |
|     ring->ctrl = 0;
 | |
|     ring->credits = 0;
 | |
| }
 |