1d7e35d4aSPaolo Bonzini /* 2d7e35d4aSPaolo Bonzini * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator 3d7e35d4aSPaolo Bonzini * 4d7e35d4aSPaolo Bonzini * PAPR Inter-VM Logical Lan, aka ibmveth 5d7e35d4aSPaolo Bonzini * 6d7e35d4aSPaolo Bonzini * Copyright (c) 2010,2011 David Gibson, IBM Corporation. 7d7e35d4aSPaolo Bonzini * 8d7e35d4aSPaolo Bonzini * Permission is hereby granted, free of charge, to any person obtaining a copy 9d7e35d4aSPaolo Bonzini * of this software and associated documentation files (the "Software"), to deal 10d7e35d4aSPaolo Bonzini * in the Software without restriction, including without limitation the rights 11d7e35d4aSPaolo Bonzini * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12d7e35d4aSPaolo Bonzini * copies of the Software, and to permit persons to whom the Software is 13d7e35d4aSPaolo Bonzini * furnished to do so, subject to the following conditions: 14d7e35d4aSPaolo Bonzini * 15d7e35d4aSPaolo Bonzini * The above copyright notice and this permission notice shall be included in 16d7e35d4aSPaolo Bonzini * all copies or substantial portions of the Software. 17d7e35d4aSPaolo Bonzini * 18d7e35d4aSPaolo Bonzini * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19d7e35d4aSPaolo Bonzini * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20d7e35d4aSPaolo Bonzini * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21d7e35d4aSPaolo Bonzini * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22d7e35d4aSPaolo Bonzini * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23d7e35d4aSPaolo Bonzini * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24d7e35d4aSPaolo Bonzini * THE SOFTWARE. 25d7e35d4aSPaolo Bonzini * 26d7e35d4aSPaolo Bonzini */ 270d75590dSPeter Maydell #include "qemu/osdep.h" 284771d756SPaolo Bonzini #include "qemu-common.h" 294771d756SPaolo Bonzini #include "cpu.h" 30d7e35d4aSPaolo Bonzini #include "hw/hw.h" 3103dd024fSPaolo Bonzini #include "qemu/log.h" 32d7e35d4aSPaolo Bonzini #include "net/net.h" 33d7e35d4aSPaolo Bonzini #include "hw/qdev.h" 34d7e35d4aSPaolo Bonzini #include "hw/ppc/spapr.h" 35d7e35d4aSPaolo Bonzini #include "hw/ppc/spapr_vio.h" 36ad4f62d0SAlexey Kardashevskiy #include "sysemu/sysemu.h" 37d7e35d4aSPaolo Bonzini 38d7e35d4aSPaolo Bonzini #include <libfdt.h> 39d7e35d4aSPaolo Bonzini 40d7e35d4aSPaolo Bonzini #define ETH_ALEN 6 41d7e35d4aSPaolo Bonzini #define MAX_PACKET_SIZE 65536 42d7e35d4aSPaolo Bonzini 43d7e35d4aSPaolo Bonzini /*#define DEBUG*/ 44d7e35d4aSPaolo Bonzini 45d7e35d4aSPaolo Bonzini #ifdef DEBUG 46f6bda9cbSPeter Maydell #define DPRINTF(fmt...) do { fprintf(stderr, fmt); } while (0) 47d7e35d4aSPaolo Bonzini #else 48f6bda9cbSPeter Maydell #define DPRINTF(fmt...) 49d7e35d4aSPaolo Bonzini #endif 50d7e35d4aSPaolo Bonzini 51831e8822SThomas Huth /* Compatibility flags for migration */ 52831e8822SThomas Huth #define SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT 0 53831e8822SThomas Huth #define SPAPRVLAN_FLAG_RX_BUF_POOLS (1 << SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT) 54831e8822SThomas Huth 55d7e35d4aSPaolo Bonzini /* 56d7e35d4aSPaolo Bonzini * Virtual LAN device 57d7e35d4aSPaolo Bonzini */ 58d7e35d4aSPaolo Bonzini 59d7e35d4aSPaolo Bonzini typedef uint64_t vlan_bd_t; 60d7e35d4aSPaolo Bonzini 61d7e35d4aSPaolo Bonzini #define VLAN_BD_VALID 0x8000000000000000ULL 62d7e35d4aSPaolo Bonzini #define VLAN_BD_TOGGLE 0x4000000000000000ULL 63d7e35d4aSPaolo Bonzini #define VLAN_BD_NO_CSUM 0x0200000000000000ULL 64d7e35d4aSPaolo Bonzini #define VLAN_BD_CSUM_GOOD 0x0100000000000000ULL 65d7e35d4aSPaolo Bonzini #define VLAN_BD_LEN_MASK 0x00ffffff00000000ULL 66d7e35d4aSPaolo Bonzini #define VLAN_BD_LEN(bd) (((bd) & VLAN_BD_LEN_MASK) >> 32) 67d7e35d4aSPaolo Bonzini #define VLAN_BD_ADDR_MASK 0x00000000ffffffffULL 68d7e35d4aSPaolo Bonzini #define VLAN_BD_ADDR(bd) ((bd) & VLAN_BD_ADDR_MASK) 69d7e35d4aSPaolo Bonzini 70d7e35d4aSPaolo Bonzini #define VLAN_VALID_BD(addr, len) (VLAN_BD_VALID | \ 71d7e35d4aSPaolo Bonzini (((len) << 32) & VLAN_BD_LEN_MASK) | \ 72d7e35d4aSPaolo Bonzini (addr & VLAN_BD_ADDR_MASK)) 73d7e35d4aSPaolo Bonzini 74d7e35d4aSPaolo Bonzini #define VLAN_RXQC_TOGGLE 0x80 75d7e35d4aSPaolo Bonzini #define VLAN_RXQC_VALID 0x40 76d7e35d4aSPaolo Bonzini #define VLAN_RXQC_NO_CSUM 0x02 77d7e35d4aSPaolo Bonzini #define VLAN_RXQC_CSUM_GOOD 0x01 78d7e35d4aSPaolo Bonzini 79d7e35d4aSPaolo Bonzini #define VLAN_RQ_ALIGNMENT 16 80d7e35d4aSPaolo Bonzini #define VLAN_RXQ_BD_OFF 0 81d7e35d4aSPaolo Bonzini #define VLAN_FILTER_BD_OFF 8 82d7e35d4aSPaolo Bonzini #define VLAN_RX_BDS_OFF 16 83439ce140SAnton Blanchard /* 84439ce140SAnton Blanchard * The final 8 bytes of the buffer list is a counter of frames dropped 85439ce140SAnton Blanchard * because there was not a buffer in the buffer list capable of holding 86439ce140SAnton Blanchard * the frame. We must avoid it, or the operating system will report garbage 87439ce140SAnton Blanchard * for this statistic. 88439ce140SAnton Blanchard */ 89439ce140SAnton Blanchard #define VLAN_RX_BDS_LEN (SPAPR_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF - 8) 90439ce140SAnton Blanchard #define VLAN_MAX_BUFS (VLAN_RX_BDS_LEN / 8) 91d7e35d4aSPaolo Bonzini 92fd506b4fSDavid Gibson #define TYPE_VIO_SPAPR_VLAN_DEVICE "spapr-vlan" 93fd506b4fSDavid Gibson #define VIO_SPAPR_VLAN_DEVICE(obj) \ 94fd506b4fSDavid Gibson OBJECT_CHECK(VIOsPAPRVLANDevice, (obj), TYPE_VIO_SPAPR_VLAN_DEVICE) 95fd506b4fSDavid Gibson 96831e8822SThomas Huth #define RX_POOL_MAX_BDS 4096 97831e8822SThomas Huth #define RX_MAX_POOLS 5 98831e8822SThomas Huth 99831e8822SThomas Huth typedef struct { 100831e8822SThomas Huth int32_t bufsize; 101831e8822SThomas Huth int32_t count; 102831e8822SThomas Huth vlan_bd_t bds[RX_POOL_MAX_BDS]; 103831e8822SThomas Huth } RxBufPool; 104831e8822SThomas Huth 105d7e35d4aSPaolo Bonzini typedef struct VIOsPAPRVLANDevice { 106d7e35d4aSPaolo Bonzini VIOsPAPRDevice sdev; 107d7e35d4aSPaolo Bonzini NICConf nicconf; 108d7e35d4aSPaolo Bonzini NICState *nic; 109686fefe4SDavid Gibson bool isopen; 110cbd62f86SPaolo Bonzini hwaddr buf_list; 111686fefe4SDavid Gibson uint32_t add_buf_ptr, use_buf_ptr, rx_bufs; 112cbd62f86SPaolo Bonzini hwaddr rxq_ptr; 1138836630fSThomas Huth QEMUTimer *rxp_timer; 114831e8822SThomas Huth uint32_t compat_flags; /* Compatability flags for migration */ 115831e8822SThomas Huth RxBufPool *rx_pool[RX_MAX_POOLS]; /* Receive buffer descriptor pools */ 116d7e35d4aSPaolo Bonzini } VIOsPAPRVLANDevice; 117d7e35d4aSPaolo Bonzini 118d7e35d4aSPaolo Bonzini static int spapr_vlan_can_receive(NetClientState *nc) 119d7e35d4aSPaolo Bonzini { 120d7e35d4aSPaolo Bonzini VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(nc); 121d7e35d4aSPaolo Bonzini 122d7e35d4aSPaolo Bonzini return (dev->isopen && dev->rx_bufs > 0); 123d7e35d4aSPaolo Bonzini } 124d7e35d4aSPaolo Bonzini 125d6f39fdfSThomas Huth /** 126*5c29dd8cSThomas Huth * The last 8 bytes of the receive buffer list page (that has been 127*5c29dd8cSThomas Huth * supplied by the guest with the H_REGISTER_LOGICAL_LAN call) contain 128*5c29dd8cSThomas Huth * a counter for frames that have been dropped because there was no 129*5c29dd8cSThomas Huth * suitable receive buffer available. This function is used to increase 130*5c29dd8cSThomas Huth * this counter by one. 131*5c29dd8cSThomas Huth */ 132*5c29dd8cSThomas Huth static void spapr_vlan_record_dropped_rx_frame(VIOsPAPRVLANDevice *dev) 133*5c29dd8cSThomas Huth { 134*5c29dd8cSThomas Huth uint64_t cnt; 135*5c29dd8cSThomas Huth 136*5c29dd8cSThomas Huth cnt = vio_ldq(&dev->sdev, dev->buf_list + 4096 - 8); 137*5c29dd8cSThomas Huth vio_stq(&dev->sdev, dev->buf_list + 4096 - 8, cnt + 1); 138*5c29dd8cSThomas Huth } 139*5c29dd8cSThomas Huth 140*5c29dd8cSThomas Huth /** 141831e8822SThomas Huth * Get buffer descriptor from one of our receive buffer pools 142831e8822SThomas Huth */ 143831e8822SThomas Huth static vlan_bd_t spapr_vlan_get_rx_bd_from_pool(VIOsPAPRVLANDevice *dev, 144831e8822SThomas Huth size_t size) 145831e8822SThomas Huth { 146831e8822SThomas Huth vlan_bd_t bd; 147831e8822SThomas Huth int pool; 148831e8822SThomas Huth 149831e8822SThomas Huth for (pool = 0; pool < RX_MAX_POOLS; pool++) { 150831e8822SThomas Huth if (dev->rx_pool[pool]->count > 0 && 151831e8822SThomas Huth dev->rx_pool[pool]->bufsize >= size + 8) { 152831e8822SThomas Huth break; 153831e8822SThomas Huth } 154831e8822SThomas Huth } 155831e8822SThomas Huth if (pool == RX_MAX_POOLS) { 156831e8822SThomas Huth /* Failed to find a suitable buffer */ 157831e8822SThomas Huth return 0; 158831e8822SThomas Huth } 159831e8822SThomas Huth 160831e8822SThomas Huth DPRINTF("Found buffer: pool=%d count=%d rxbufs=%d\n", pool, 161831e8822SThomas Huth dev->rx_pool[pool]->count, dev->rx_bufs); 162831e8822SThomas Huth 163831e8822SThomas Huth /* Remove the buffer from the pool */ 164831e8822SThomas Huth dev->rx_pool[pool]->count--; 165831e8822SThomas Huth bd = dev->rx_pool[pool]->bds[dev->rx_pool[pool]->count]; 166831e8822SThomas Huth dev->rx_pool[pool]->bds[dev->rx_pool[pool]->count] = 0; 167831e8822SThomas Huth 168831e8822SThomas Huth return bd; 169831e8822SThomas Huth } 170831e8822SThomas Huth 171831e8822SThomas Huth /** 172d6f39fdfSThomas Huth * Get buffer descriptor from the receive buffer list page that has been 173d6f39fdfSThomas Huth * supplied by the guest with the H_REGISTER_LOGICAL_LAN call 174d6f39fdfSThomas Huth */ 175d6f39fdfSThomas Huth static vlan_bd_t spapr_vlan_get_rx_bd_from_page(VIOsPAPRVLANDevice *dev, 176d6f39fdfSThomas Huth size_t size) 177d6f39fdfSThomas Huth { 178d6f39fdfSThomas Huth int buf_ptr = dev->use_buf_ptr; 179d6f39fdfSThomas Huth vlan_bd_t bd; 180d6f39fdfSThomas Huth 181d6f39fdfSThomas Huth do { 182d6f39fdfSThomas Huth buf_ptr += 8; 183d6f39fdfSThomas Huth if (buf_ptr >= VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF) { 184d6f39fdfSThomas Huth buf_ptr = VLAN_RX_BDS_OFF; 185d6f39fdfSThomas Huth } 186d6f39fdfSThomas Huth 187d6f39fdfSThomas Huth bd = vio_ldq(&dev->sdev, dev->buf_list + buf_ptr); 188d6f39fdfSThomas Huth DPRINTF("use_buf_ptr=%d bd=0x%016llx\n", 189d6f39fdfSThomas Huth buf_ptr, (unsigned long long)bd); 190d6f39fdfSThomas Huth } while ((!(bd & VLAN_BD_VALID) || VLAN_BD_LEN(bd) < size + 8) 191d6f39fdfSThomas Huth && buf_ptr != dev->use_buf_ptr); 192d6f39fdfSThomas Huth 193d6f39fdfSThomas Huth if (!(bd & VLAN_BD_VALID) || VLAN_BD_LEN(bd) < size + 8) { 194d6f39fdfSThomas Huth /* Failed to find a suitable buffer */ 195d6f39fdfSThomas Huth return 0; 196d6f39fdfSThomas Huth } 197d6f39fdfSThomas Huth 198d6f39fdfSThomas Huth /* Remove the buffer from the pool */ 199d6f39fdfSThomas Huth dev->use_buf_ptr = buf_ptr; 200d6f39fdfSThomas Huth vio_stq(&dev->sdev, dev->buf_list + dev->use_buf_ptr, 0); 201d6f39fdfSThomas Huth 202d6f39fdfSThomas Huth DPRINTF("Found buffer: ptr=%d rxbufs=%d\n", dev->use_buf_ptr, dev->rx_bufs); 203d6f39fdfSThomas Huth 204d6f39fdfSThomas Huth return bd; 205d6f39fdfSThomas Huth } 206d6f39fdfSThomas Huth 207d7e35d4aSPaolo Bonzini static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, 208d7e35d4aSPaolo Bonzini size_t size) 209d7e35d4aSPaolo Bonzini { 210fd506b4fSDavid Gibson VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(nc); 211fd506b4fSDavid Gibson VIOsPAPRDevice *sdev = VIO_SPAPR_DEVICE(dev); 212d7e35d4aSPaolo Bonzini vlan_bd_t rxq_bd = vio_ldq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF); 213d7e35d4aSPaolo Bonzini vlan_bd_t bd; 214d7e35d4aSPaolo Bonzini uint64_t handle; 215d7e35d4aSPaolo Bonzini uint8_t control; 216d7e35d4aSPaolo Bonzini 217f6bda9cbSPeter Maydell DPRINTF("spapr_vlan_receive() [%s] rx_bufs=%d\n", sdev->qdev.id, 218d7e35d4aSPaolo Bonzini dev->rx_bufs); 219d7e35d4aSPaolo Bonzini 220d7e35d4aSPaolo Bonzini if (!dev->isopen) { 221d7e35d4aSPaolo Bonzini return -1; 222d7e35d4aSPaolo Bonzini } 223d7e35d4aSPaolo Bonzini 224d7e35d4aSPaolo Bonzini if (!dev->rx_bufs) { 225*5c29dd8cSThomas Huth spapr_vlan_record_dropped_rx_frame(dev); 2268836630fSThomas Huth return 0; 227d7e35d4aSPaolo Bonzini } 228d7e35d4aSPaolo Bonzini 229831e8822SThomas Huth if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { 230831e8822SThomas Huth bd = spapr_vlan_get_rx_bd_from_pool(dev, size); 231831e8822SThomas Huth } else { 232d6f39fdfSThomas Huth bd = spapr_vlan_get_rx_bd_from_page(dev, size); 233831e8822SThomas Huth } 234d6f39fdfSThomas Huth if (!bd) { 235*5c29dd8cSThomas Huth spapr_vlan_record_dropped_rx_frame(dev); 2368836630fSThomas Huth return 0; 237d7e35d4aSPaolo Bonzini } 238d7e35d4aSPaolo Bonzini 239d7e35d4aSPaolo Bonzini dev->rx_bufs--; 240d7e35d4aSPaolo Bonzini 241d7e35d4aSPaolo Bonzini /* Transfer the packet data */ 242d7e35d4aSPaolo Bonzini if (spapr_vio_dma_write(sdev, VLAN_BD_ADDR(bd) + 8, buf, size) < 0) { 243d7e35d4aSPaolo Bonzini return -1; 244d7e35d4aSPaolo Bonzini } 245d7e35d4aSPaolo Bonzini 246f6bda9cbSPeter Maydell DPRINTF("spapr_vlan_receive: DMA write completed\n"); 247d7e35d4aSPaolo Bonzini 248d7e35d4aSPaolo Bonzini /* Update the receive queue */ 249d7e35d4aSPaolo Bonzini control = VLAN_RXQC_TOGGLE | VLAN_RXQC_VALID; 250d7e35d4aSPaolo Bonzini if (rxq_bd & VLAN_BD_TOGGLE) { 251d7e35d4aSPaolo Bonzini control ^= VLAN_RXQC_TOGGLE; 252d7e35d4aSPaolo Bonzini } 253d7e35d4aSPaolo Bonzini 254d7e35d4aSPaolo Bonzini handle = vio_ldq(sdev, VLAN_BD_ADDR(bd)); 255d7e35d4aSPaolo Bonzini vio_stq(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 8, handle); 256d7e35d4aSPaolo Bonzini vio_stl(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 4, size); 257d7e35d4aSPaolo Bonzini vio_sth(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 2, 8); 258d7e35d4aSPaolo Bonzini vio_stb(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr, control); 259d7e35d4aSPaolo Bonzini 260f6bda9cbSPeter Maydell DPRINTF("wrote rxq entry (ptr=0x%llx): 0x%016llx 0x%016llx\n", 261d7e35d4aSPaolo Bonzini (unsigned long long)dev->rxq_ptr, 262d7e35d4aSPaolo Bonzini (unsigned long long)vio_ldq(sdev, VLAN_BD_ADDR(rxq_bd) + 263d7e35d4aSPaolo Bonzini dev->rxq_ptr), 264d7e35d4aSPaolo Bonzini (unsigned long long)vio_ldq(sdev, VLAN_BD_ADDR(rxq_bd) + 265d7e35d4aSPaolo Bonzini dev->rxq_ptr + 8)); 266d7e35d4aSPaolo Bonzini 267d7e35d4aSPaolo Bonzini dev->rxq_ptr += 16; 268d7e35d4aSPaolo Bonzini if (dev->rxq_ptr >= VLAN_BD_LEN(rxq_bd)) { 269d7e35d4aSPaolo Bonzini dev->rxq_ptr = 0; 270d7e35d4aSPaolo Bonzini vio_stq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF, rxq_bd ^ VLAN_BD_TOGGLE); 271d7e35d4aSPaolo Bonzini } 272d7e35d4aSPaolo Bonzini 273d7e35d4aSPaolo Bonzini if (sdev->signal_state & 1) { 274d7e35d4aSPaolo Bonzini qemu_irq_pulse(spapr_vio_qirq(sdev)); 275d7e35d4aSPaolo Bonzini } 276d7e35d4aSPaolo Bonzini 277d7e35d4aSPaolo Bonzini return size; 278d7e35d4aSPaolo Bonzini } 279d7e35d4aSPaolo Bonzini 280d7e35d4aSPaolo Bonzini static NetClientInfo net_spapr_vlan_info = { 281d7e35d4aSPaolo Bonzini .type = NET_CLIENT_OPTIONS_KIND_NIC, 282d7e35d4aSPaolo Bonzini .size = sizeof(NICState), 283d7e35d4aSPaolo Bonzini .can_receive = spapr_vlan_can_receive, 284d7e35d4aSPaolo Bonzini .receive = spapr_vlan_receive, 285d7e35d4aSPaolo Bonzini }; 286d7e35d4aSPaolo Bonzini 2878836630fSThomas Huth static void spapr_vlan_flush_rx_queue(void *opaque) 2888836630fSThomas Huth { 2898836630fSThomas Huth VIOsPAPRVLANDevice *dev = opaque; 2908836630fSThomas Huth 2918836630fSThomas Huth qemu_flush_queued_packets(qemu_get_queue(dev->nic)); 2928836630fSThomas Huth } 2938836630fSThomas Huth 294831e8822SThomas Huth static void spapr_vlan_reset_rx_pool(RxBufPool *rxp) 295831e8822SThomas Huth { 296831e8822SThomas Huth /* 297831e8822SThomas Huth * Use INT_MAX as bufsize so that unused buffers are moved to the end 298831e8822SThomas Huth * of the list during the qsort in spapr_vlan_add_rxbuf_to_pool() later. 299831e8822SThomas Huth */ 300831e8822SThomas Huth rxp->bufsize = INT_MAX; 301831e8822SThomas Huth rxp->count = 0; 302831e8822SThomas Huth memset(rxp->bds, 0, sizeof(rxp->bds)); 303831e8822SThomas Huth } 304831e8822SThomas Huth 305d7e35d4aSPaolo Bonzini static void spapr_vlan_reset(VIOsPAPRDevice *sdev) 306d7e35d4aSPaolo Bonzini { 307fd506b4fSDavid Gibson VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); 308831e8822SThomas Huth int i; 309d7e35d4aSPaolo Bonzini 310d7e35d4aSPaolo Bonzini dev->buf_list = 0; 311d7e35d4aSPaolo Bonzini dev->rx_bufs = 0; 312d7e35d4aSPaolo Bonzini dev->isopen = 0; 313831e8822SThomas Huth 314831e8822SThomas Huth if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { 315831e8822SThomas Huth for (i = 0; i < RX_MAX_POOLS; i++) { 316831e8822SThomas Huth spapr_vlan_reset_rx_pool(dev->rx_pool[i]); 317831e8822SThomas Huth } 318831e8822SThomas Huth } 319d7e35d4aSPaolo Bonzini } 320d7e35d4aSPaolo Bonzini 32128b07e73SMarkus Armbruster static void spapr_vlan_realize(VIOsPAPRDevice *sdev, Error **errp) 322d7e35d4aSPaolo Bonzini { 323fd506b4fSDavid Gibson VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); 324d7e35d4aSPaolo Bonzini 325d7e35d4aSPaolo Bonzini qemu_macaddr_default_if_unset(&dev->nicconf.macaddr); 326d7e35d4aSPaolo Bonzini 327d7e35d4aSPaolo Bonzini dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf, 328d7e35d4aSPaolo Bonzini object_get_typename(OBJECT(sdev)), sdev->qdev.id, dev); 329d7e35d4aSPaolo Bonzini qemu_format_nic_info_str(qemu_get_queue(dev->nic), dev->nicconf.macaddr.a); 3308836630fSThomas Huth 3318836630fSThomas Huth dev->rxp_timer = timer_new_us(QEMU_CLOCK_VIRTUAL, spapr_vlan_flush_rx_queue, 3328836630fSThomas Huth dev); 333d7e35d4aSPaolo Bonzini } 334d7e35d4aSPaolo Bonzini 335dfe79cf2SGonglei static void spapr_vlan_instance_init(Object *obj) 336dfe79cf2SGonglei { 337dfe79cf2SGonglei VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(obj); 338831e8822SThomas Huth int i; 339dfe79cf2SGonglei 340dfe79cf2SGonglei device_add_bootindex_property(obj, &dev->nicconf.bootindex, 341dfe79cf2SGonglei "bootindex", "", 342dfe79cf2SGonglei DEVICE(dev), NULL); 343831e8822SThomas Huth 344831e8822SThomas Huth if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { 345831e8822SThomas Huth for (i = 0; i < RX_MAX_POOLS; i++) { 346831e8822SThomas Huth dev->rx_pool[i] = g_new(RxBufPool, 1); 347831e8822SThomas Huth spapr_vlan_reset_rx_pool(dev->rx_pool[i]); 348831e8822SThomas Huth } 349831e8822SThomas Huth } 350831e8822SThomas Huth } 351831e8822SThomas Huth 352831e8822SThomas Huth static void spapr_vlan_instance_finalize(Object *obj) 353831e8822SThomas Huth { 354831e8822SThomas Huth VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(obj); 355831e8822SThomas Huth int i; 356831e8822SThomas Huth 357831e8822SThomas Huth if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { 358831e8822SThomas Huth for (i = 0; i < RX_MAX_POOLS; i++) { 359831e8822SThomas Huth g_free(dev->rx_pool[i]); 360831e8822SThomas Huth dev->rx_pool[i] = NULL; 361831e8822SThomas Huth } 362831e8822SThomas Huth } 3638836630fSThomas Huth 3648836630fSThomas Huth if (dev->rxp_timer) { 3658836630fSThomas Huth timer_del(dev->rxp_timer); 3668836630fSThomas Huth timer_free(dev->rxp_timer); 3678836630fSThomas Huth } 368dfe79cf2SGonglei } 369dfe79cf2SGonglei 370d7e35d4aSPaolo Bonzini void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd) 371d7e35d4aSPaolo Bonzini { 372d7e35d4aSPaolo Bonzini DeviceState *dev; 373d7e35d4aSPaolo Bonzini 374d7e35d4aSPaolo Bonzini dev = qdev_create(&bus->bus, "spapr-vlan"); 375d7e35d4aSPaolo Bonzini 376d7e35d4aSPaolo Bonzini qdev_set_nic_properties(dev, nd); 377d7e35d4aSPaolo Bonzini 378d7e35d4aSPaolo Bonzini qdev_init_nofail(dev); 379d7e35d4aSPaolo Bonzini } 380d7e35d4aSPaolo Bonzini 381d7e35d4aSPaolo Bonzini static int spapr_vlan_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) 382d7e35d4aSPaolo Bonzini { 383fd506b4fSDavid Gibson VIOsPAPRVLANDevice *vdev = VIO_SPAPR_VLAN_DEVICE(dev); 384d7e35d4aSPaolo Bonzini uint8_t padded_mac[8] = {0, 0}; 385d7e35d4aSPaolo Bonzini int ret; 386d7e35d4aSPaolo Bonzini 387d7e35d4aSPaolo Bonzini /* Some old phyp versions give the mac address in an 8-byte 388d7e35d4aSPaolo Bonzini * property. The kernel driver has an insane workaround for this; 389d7e35d4aSPaolo Bonzini * rather than doing the obvious thing and checking the property 390d7e35d4aSPaolo Bonzini * length, it checks whether the first byte has 0b10 in the low 391d7e35d4aSPaolo Bonzini * bits. If a correct 6-byte property has a different first byte 392d7e35d4aSPaolo Bonzini * the kernel will get the wrong mac address, overrunning its 393d7e35d4aSPaolo Bonzini * buffer in the process (read only, thank goodness). 394d7e35d4aSPaolo Bonzini * 395d7e35d4aSPaolo Bonzini * Here we workaround the kernel workaround by always supplying an 396d7e35d4aSPaolo Bonzini * 8-byte property, with the mac address in the last six bytes */ 397d7e35d4aSPaolo Bonzini memcpy(&padded_mac[2], &vdev->nicconf.macaddr, ETH_ALEN); 398d7e35d4aSPaolo Bonzini ret = fdt_setprop(fdt, node_off, "local-mac-address", 399d7e35d4aSPaolo Bonzini padded_mac, sizeof(padded_mac)); 400d7e35d4aSPaolo Bonzini if (ret < 0) { 401d7e35d4aSPaolo Bonzini return ret; 402d7e35d4aSPaolo Bonzini } 403d7e35d4aSPaolo Bonzini 404d7e35d4aSPaolo Bonzini ret = fdt_setprop_cell(fdt, node_off, "ibm,mac-address-filters", 0); 405d7e35d4aSPaolo Bonzini if (ret < 0) { 406d7e35d4aSPaolo Bonzini return ret; 407d7e35d4aSPaolo Bonzini } 408d7e35d4aSPaolo Bonzini 409d7e35d4aSPaolo Bonzini return 0; 410d7e35d4aSPaolo Bonzini } 411d7e35d4aSPaolo Bonzini 412d7e35d4aSPaolo Bonzini static int check_bd(VIOsPAPRVLANDevice *dev, vlan_bd_t bd, 413d7e35d4aSPaolo Bonzini target_ulong alignment) 414d7e35d4aSPaolo Bonzini { 415d7e35d4aSPaolo Bonzini if ((VLAN_BD_ADDR(bd) % alignment) 416d7e35d4aSPaolo Bonzini || (VLAN_BD_LEN(bd) % alignment)) { 417d7e35d4aSPaolo Bonzini return -1; 418d7e35d4aSPaolo Bonzini } 419d7e35d4aSPaolo Bonzini 420d7e35d4aSPaolo Bonzini if (!spapr_vio_dma_valid(&dev->sdev, VLAN_BD_ADDR(bd), 421d7e35d4aSPaolo Bonzini VLAN_BD_LEN(bd), DMA_DIRECTION_FROM_DEVICE) 422d7e35d4aSPaolo Bonzini || !spapr_vio_dma_valid(&dev->sdev, VLAN_BD_ADDR(bd), 423d7e35d4aSPaolo Bonzini VLAN_BD_LEN(bd), DMA_DIRECTION_TO_DEVICE)) { 424d7e35d4aSPaolo Bonzini return -1; 425d7e35d4aSPaolo Bonzini } 426d7e35d4aSPaolo Bonzini 427d7e35d4aSPaolo Bonzini return 0; 428d7e35d4aSPaolo Bonzini } 429d7e35d4aSPaolo Bonzini 430d7e35d4aSPaolo Bonzini static target_ulong h_register_logical_lan(PowerPCCPU *cpu, 43128e02042SDavid Gibson sPAPRMachineState *spapr, 432d7e35d4aSPaolo Bonzini target_ulong opcode, 433d7e35d4aSPaolo Bonzini target_ulong *args) 434d7e35d4aSPaolo Bonzini { 435d7e35d4aSPaolo Bonzini target_ulong reg = args[0]; 436d7e35d4aSPaolo Bonzini target_ulong buf_list = args[1]; 437d7e35d4aSPaolo Bonzini target_ulong rec_queue = args[2]; 438d7e35d4aSPaolo Bonzini target_ulong filter_list = args[3]; 439d7e35d4aSPaolo Bonzini VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); 440fd506b4fSDavid Gibson VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); 441d7e35d4aSPaolo Bonzini vlan_bd_t filter_list_bd; 442d7e35d4aSPaolo Bonzini 443d7e35d4aSPaolo Bonzini if (!dev) { 444d7e35d4aSPaolo Bonzini return H_PARAMETER; 445d7e35d4aSPaolo Bonzini } 446d7e35d4aSPaolo Bonzini 447d7e35d4aSPaolo Bonzini if (dev->isopen) { 448d7e35d4aSPaolo Bonzini hcall_dprintf("H_REGISTER_LOGICAL_LAN called twice without " 449d7e35d4aSPaolo Bonzini "H_FREE_LOGICAL_LAN\n"); 450d7e35d4aSPaolo Bonzini return H_RESOURCE; 451d7e35d4aSPaolo Bonzini } 452d7e35d4aSPaolo Bonzini 453d7e35d4aSPaolo Bonzini if (check_bd(dev, VLAN_VALID_BD(buf_list, SPAPR_TCE_PAGE_SIZE), 454d7e35d4aSPaolo Bonzini SPAPR_TCE_PAGE_SIZE) < 0) { 455d7e35d4aSPaolo Bonzini hcall_dprintf("Bad buf_list 0x" TARGET_FMT_lx "\n", buf_list); 456d7e35d4aSPaolo Bonzini return H_PARAMETER; 457d7e35d4aSPaolo Bonzini } 458d7e35d4aSPaolo Bonzini 459d7e35d4aSPaolo Bonzini filter_list_bd = VLAN_VALID_BD(filter_list, SPAPR_TCE_PAGE_SIZE); 460d7e35d4aSPaolo Bonzini if (check_bd(dev, filter_list_bd, SPAPR_TCE_PAGE_SIZE) < 0) { 461d7e35d4aSPaolo Bonzini hcall_dprintf("Bad filter_list 0x" TARGET_FMT_lx "\n", filter_list); 462d7e35d4aSPaolo Bonzini return H_PARAMETER; 463d7e35d4aSPaolo Bonzini } 464d7e35d4aSPaolo Bonzini 465d7e35d4aSPaolo Bonzini if (!(rec_queue & VLAN_BD_VALID) 466d7e35d4aSPaolo Bonzini || (check_bd(dev, rec_queue, VLAN_RQ_ALIGNMENT) < 0)) { 467d7e35d4aSPaolo Bonzini hcall_dprintf("Bad receive queue\n"); 468d7e35d4aSPaolo Bonzini return H_PARAMETER; 469d7e35d4aSPaolo Bonzini } 470d7e35d4aSPaolo Bonzini 471d7e35d4aSPaolo Bonzini dev->buf_list = buf_list; 472d7e35d4aSPaolo Bonzini sdev->signal_state = 0; 473d7e35d4aSPaolo Bonzini 474d7e35d4aSPaolo Bonzini rec_queue &= ~VLAN_BD_TOGGLE; 475d7e35d4aSPaolo Bonzini 476d7e35d4aSPaolo Bonzini /* Initialize the buffer list */ 477d7e35d4aSPaolo Bonzini vio_stq(sdev, buf_list, rec_queue); 478d7e35d4aSPaolo Bonzini vio_stq(sdev, buf_list + 8, filter_list_bd); 479d7e35d4aSPaolo Bonzini spapr_vio_dma_set(sdev, buf_list + VLAN_RX_BDS_OFF, 0, 480d7e35d4aSPaolo Bonzini SPAPR_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF); 481d7e35d4aSPaolo Bonzini dev->add_buf_ptr = VLAN_RX_BDS_OFF - 8; 482d7e35d4aSPaolo Bonzini dev->use_buf_ptr = VLAN_RX_BDS_OFF - 8; 483d7e35d4aSPaolo Bonzini dev->rx_bufs = 0; 484d7e35d4aSPaolo Bonzini dev->rxq_ptr = 0; 485d7e35d4aSPaolo Bonzini 486d7e35d4aSPaolo Bonzini /* Initialize the receive queue */ 487d7e35d4aSPaolo Bonzini spapr_vio_dma_set(sdev, VLAN_BD_ADDR(rec_queue), 0, VLAN_BD_LEN(rec_queue)); 488d7e35d4aSPaolo Bonzini 489d7e35d4aSPaolo Bonzini dev->isopen = 1; 490e0ff466cSAlexey Kardashevskiy qemu_flush_queued_packets(qemu_get_queue(dev->nic)); 491e0ff466cSAlexey Kardashevskiy 492d7e35d4aSPaolo Bonzini return H_SUCCESS; 493d7e35d4aSPaolo Bonzini } 494d7e35d4aSPaolo Bonzini 495d7e35d4aSPaolo Bonzini 49628e02042SDavid Gibson static target_ulong h_free_logical_lan(PowerPCCPU *cpu, 49728e02042SDavid Gibson sPAPRMachineState *spapr, 498d7e35d4aSPaolo Bonzini target_ulong opcode, target_ulong *args) 499d7e35d4aSPaolo Bonzini { 500d7e35d4aSPaolo Bonzini target_ulong reg = args[0]; 501d7e35d4aSPaolo Bonzini VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); 502fd506b4fSDavid Gibson VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); 503d7e35d4aSPaolo Bonzini 504d7e35d4aSPaolo Bonzini if (!dev) { 505d7e35d4aSPaolo Bonzini return H_PARAMETER; 506d7e35d4aSPaolo Bonzini } 507d7e35d4aSPaolo Bonzini 508d7e35d4aSPaolo Bonzini if (!dev->isopen) { 509d7e35d4aSPaolo Bonzini hcall_dprintf("H_FREE_LOGICAL_LAN called without " 510d7e35d4aSPaolo Bonzini "H_REGISTER_LOGICAL_LAN\n"); 511d7e35d4aSPaolo Bonzini return H_RESOURCE; 512d7e35d4aSPaolo Bonzini } 513d7e35d4aSPaolo Bonzini 514d7e35d4aSPaolo Bonzini spapr_vlan_reset(sdev); 515d7e35d4aSPaolo Bonzini return H_SUCCESS; 516d7e35d4aSPaolo Bonzini } 517d7e35d4aSPaolo Bonzini 518831e8822SThomas Huth /** 519831e8822SThomas Huth * Used for qsort, this function compares two RxBufPools by size. 520831e8822SThomas Huth */ 521831e8822SThomas Huth static int rx_pool_size_compare(const void *p1, const void *p2) 522831e8822SThomas Huth { 523831e8822SThomas Huth const RxBufPool *pool1 = *(RxBufPool **)p1; 524831e8822SThomas Huth const RxBufPool *pool2 = *(RxBufPool **)p2; 525831e8822SThomas Huth 526831e8822SThomas Huth if (pool1->bufsize < pool2->bufsize) { 527831e8822SThomas Huth return -1; 528831e8822SThomas Huth } 529831e8822SThomas Huth return pool1->bufsize > pool2->bufsize; 530831e8822SThomas Huth } 531831e8822SThomas Huth 532831e8822SThomas Huth /** 533831e8822SThomas Huth * Search for a matching buffer pool with exact matching size, 534831e8822SThomas Huth * or return -1 if no matching pool has been found. 535831e8822SThomas Huth */ 536831e8822SThomas Huth static int spapr_vlan_get_rx_pool_id(VIOsPAPRVLANDevice *dev, int size) 537831e8822SThomas Huth { 538831e8822SThomas Huth int pool; 539831e8822SThomas Huth 540831e8822SThomas Huth for (pool = 0; pool < RX_MAX_POOLS; pool++) { 541831e8822SThomas Huth if (dev->rx_pool[pool]->bufsize == size) { 542831e8822SThomas Huth return pool; 543831e8822SThomas Huth } 544831e8822SThomas Huth } 545831e8822SThomas Huth 546831e8822SThomas Huth return -1; 547831e8822SThomas Huth } 548831e8822SThomas Huth 549831e8822SThomas Huth /** 550831e8822SThomas Huth * Enqueuing receive buffer by adding it to one of our receive buffer pools 551831e8822SThomas Huth */ 552831e8822SThomas Huth static target_long spapr_vlan_add_rxbuf_to_pool(VIOsPAPRVLANDevice *dev, 553831e8822SThomas Huth target_ulong buf) 554831e8822SThomas Huth { 555831e8822SThomas Huth int size = VLAN_BD_LEN(buf); 556831e8822SThomas Huth int pool; 557831e8822SThomas Huth 558831e8822SThomas Huth pool = spapr_vlan_get_rx_pool_id(dev, size); 559831e8822SThomas Huth if (pool < 0) { 560831e8822SThomas Huth /* 561831e8822SThomas Huth * No matching pool found? Try to use a new one. If the guest used all 562831e8822SThomas Huth * pools before, but changed the size of one pool inbetween, we might 563831e8822SThomas Huth * need to recycle that pool here (if it's empty already). Thus scan 564831e8822SThomas Huth * all buffer pools now, starting with the last (likely empty) one. 565831e8822SThomas Huth */ 566831e8822SThomas Huth for (pool = RX_MAX_POOLS - 1; pool >= 0 ; pool--) { 567831e8822SThomas Huth if (dev->rx_pool[pool]->count == 0) { 568831e8822SThomas Huth dev->rx_pool[pool]->bufsize = size; 569831e8822SThomas Huth /* 570831e8822SThomas Huth * Sort pools by size so that spapr_vlan_receive() 571831e8822SThomas Huth * can later find the smallest buffer pool easily. 572831e8822SThomas Huth */ 573831e8822SThomas Huth qsort(dev->rx_pool, RX_MAX_POOLS, sizeof(dev->rx_pool[0]), 574831e8822SThomas Huth rx_pool_size_compare); 575831e8822SThomas Huth pool = spapr_vlan_get_rx_pool_id(dev, size); 576831e8822SThomas Huth DPRINTF("created RX pool %d for size %lld\n", pool, 577831e8822SThomas Huth VLAN_BD_LEN(buf)); 578831e8822SThomas Huth break; 579831e8822SThomas Huth } 580831e8822SThomas Huth } 581831e8822SThomas Huth } 582831e8822SThomas Huth /* Still no usable pool? Give up */ 583831e8822SThomas Huth if (pool < 0 || dev->rx_pool[pool]->count >= RX_POOL_MAX_BDS) { 584831e8822SThomas Huth return H_RESOURCE; 585831e8822SThomas Huth } 586831e8822SThomas Huth 587831e8822SThomas Huth DPRINTF("h_add_llan_buf(): Add buf using pool %i (size %lli, count=%i)\n", 588831e8822SThomas Huth pool, VLAN_BD_LEN(buf), dev->rx_pool[pool]->count); 589831e8822SThomas Huth 590831e8822SThomas Huth dev->rx_pool[pool]->bds[dev->rx_pool[pool]->count++] = buf; 591831e8822SThomas Huth 592831e8822SThomas Huth return 0; 593831e8822SThomas Huth } 594831e8822SThomas Huth 595831e8822SThomas Huth /** 596831e8822SThomas Huth * This is the old way of enqueuing receive buffers: Add it to the rx queue 597831e8822SThomas Huth * page that has been supplied by the guest (which is quite limited in size). 598831e8822SThomas Huth */ 599d6f39fdfSThomas Huth static target_long spapr_vlan_add_rxbuf_to_page(VIOsPAPRVLANDevice *dev, 600d6f39fdfSThomas Huth target_ulong buf) 601d6f39fdfSThomas Huth { 602d6f39fdfSThomas Huth vlan_bd_t bd; 603d6f39fdfSThomas Huth 604d6f39fdfSThomas Huth if (dev->rx_bufs >= VLAN_MAX_BUFS) { 605d6f39fdfSThomas Huth return H_RESOURCE; 606d6f39fdfSThomas Huth } 607d6f39fdfSThomas Huth 608d6f39fdfSThomas Huth do { 609d6f39fdfSThomas Huth dev->add_buf_ptr += 8; 610d6f39fdfSThomas Huth if (dev->add_buf_ptr >= VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF) { 611d6f39fdfSThomas Huth dev->add_buf_ptr = VLAN_RX_BDS_OFF; 612d6f39fdfSThomas Huth } 613d6f39fdfSThomas Huth 614d6f39fdfSThomas Huth bd = vio_ldq(&dev->sdev, dev->buf_list + dev->add_buf_ptr); 615d6f39fdfSThomas Huth } while (bd & VLAN_BD_VALID); 616d6f39fdfSThomas Huth 617d6f39fdfSThomas Huth vio_stq(&dev->sdev, dev->buf_list + dev->add_buf_ptr, buf); 618d6f39fdfSThomas Huth 619d6f39fdfSThomas Huth DPRINTF("h_add_llan_buf(): Added buf ptr=%d rx_bufs=%d bd=0x%016llx\n", 620d6f39fdfSThomas Huth dev->add_buf_ptr, dev->rx_bufs, (unsigned long long)buf); 621d6f39fdfSThomas Huth 622d6f39fdfSThomas Huth return 0; 623d6f39fdfSThomas Huth } 624d6f39fdfSThomas Huth 625d7e35d4aSPaolo Bonzini static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, 62628e02042SDavid Gibson sPAPRMachineState *spapr, 627d7e35d4aSPaolo Bonzini target_ulong opcode, 628d7e35d4aSPaolo Bonzini target_ulong *args) 629d7e35d4aSPaolo Bonzini { 630d7e35d4aSPaolo Bonzini target_ulong reg = args[0]; 631d7e35d4aSPaolo Bonzini target_ulong buf = args[1]; 632d7e35d4aSPaolo Bonzini VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); 633fd506b4fSDavid Gibson VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); 634d6f39fdfSThomas Huth target_long ret; 635d7e35d4aSPaolo Bonzini 636f6bda9cbSPeter Maydell DPRINTF("H_ADD_LOGICAL_LAN_BUFFER(0x" TARGET_FMT_lx 637d7e35d4aSPaolo Bonzini ", 0x" TARGET_FMT_lx ")\n", reg, buf); 638d7e35d4aSPaolo Bonzini 639d7e35d4aSPaolo Bonzini if (!sdev) { 640d7e35d4aSPaolo Bonzini hcall_dprintf("Bad device\n"); 641d7e35d4aSPaolo Bonzini return H_PARAMETER; 642d7e35d4aSPaolo Bonzini } 643d7e35d4aSPaolo Bonzini 644d7e35d4aSPaolo Bonzini if ((check_bd(dev, buf, 4) < 0) 645d7e35d4aSPaolo Bonzini || (VLAN_BD_LEN(buf) < 16)) { 646d7e35d4aSPaolo Bonzini hcall_dprintf("Bad buffer enqueued\n"); 647d7e35d4aSPaolo Bonzini return H_PARAMETER; 648d7e35d4aSPaolo Bonzini } 649d7e35d4aSPaolo Bonzini 650d6f39fdfSThomas Huth if (!dev->isopen) { 651d7e35d4aSPaolo Bonzini return H_RESOURCE; 652d7e35d4aSPaolo Bonzini } 653d7e35d4aSPaolo Bonzini 654831e8822SThomas Huth if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { 655831e8822SThomas Huth ret = spapr_vlan_add_rxbuf_to_pool(dev, buf); 656831e8822SThomas Huth } else { 657d6f39fdfSThomas Huth ret = spapr_vlan_add_rxbuf_to_page(dev, buf); 658831e8822SThomas Huth } 659d6f39fdfSThomas Huth if (ret) { 660d6f39fdfSThomas Huth return ret; 661d7e35d4aSPaolo Bonzini } 662d7e35d4aSPaolo Bonzini 663d7e35d4aSPaolo Bonzini dev->rx_bufs++; 664d7e35d4aSPaolo Bonzini 6658836630fSThomas Huth /* 6668836630fSThomas Huth * Give guest some more time to add additional RX buffers before we 6678836630fSThomas Huth * flush the receive queue, so that e.g. fragmented IP packets can 6688836630fSThomas Huth * be passed to the guest in one go later (instead of passing single 6698836630fSThomas Huth * fragments if there is only one receive buffer available). 6708836630fSThomas Huth */ 6718836630fSThomas Huth timer_mod(dev->rxp_timer, qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + 500); 6720a61f3b4SAlexey Kardashevskiy 673d7e35d4aSPaolo Bonzini return H_SUCCESS; 674d7e35d4aSPaolo Bonzini } 675d7e35d4aSPaolo Bonzini 67628e02042SDavid Gibson static target_ulong h_send_logical_lan(PowerPCCPU *cpu, 67728e02042SDavid Gibson sPAPRMachineState *spapr, 678d7e35d4aSPaolo Bonzini target_ulong opcode, target_ulong *args) 679d7e35d4aSPaolo Bonzini { 680d7e35d4aSPaolo Bonzini target_ulong reg = args[0]; 681d7e35d4aSPaolo Bonzini target_ulong *bufs = args + 1; 682d7e35d4aSPaolo Bonzini target_ulong continue_token = args[7]; 683d7e35d4aSPaolo Bonzini VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); 684fd506b4fSDavid Gibson VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); 685d7e35d4aSPaolo Bonzini unsigned total_len; 686d7e35d4aSPaolo Bonzini uint8_t *lbuf, *p; 687d7e35d4aSPaolo Bonzini int i, nbufs; 688d7e35d4aSPaolo Bonzini int ret; 689d7e35d4aSPaolo Bonzini 690f6bda9cbSPeter Maydell DPRINTF("H_SEND_LOGICAL_LAN(0x" TARGET_FMT_lx ", <bufs>, 0x" 691d7e35d4aSPaolo Bonzini TARGET_FMT_lx ")\n", reg, continue_token); 692d7e35d4aSPaolo Bonzini 693d7e35d4aSPaolo Bonzini if (!sdev) { 694d7e35d4aSPaolo Bonzini return H_PARAMETER; 695d7e35d4aSPaolo Bonzini } 696d7e35d4aSPaolo Bonzini 697f6bda9cbSPeter Maydell DPRINTF("rxbufs = %d\n", dev->rx_bufs); 698d7e35d4aSPaolo Bonzini 699d7e35d4aSPaolo Bonzini if (!dev->isopen) { 700d7e35d4aSPaolo Bonzini return H_DROPPED; 701d7e35d4aSPaolo Bonzini } 702d7e35d4aSPaolo Bonzini 703d7e35d4aSPaolo Bonzini if (continue_token) { 704d7e35d4aSPaolo Bonzini return H_HARDWARE; /* FIXME actually handle this */ 705d7e35d4aSPaolo Bonzini } 706d7e35d4aSPaolo Bonzini 707d7e35d4aSPaolo Bonzini total_len = 0; 708d7e35d4aSPaolo Bonzini for (i = 0; i < 6; i++) { 709f6bda9cbSPeter Maydell DPRINTF(" buf desc: 0x" TARGET_FMT_lx "\n", bufs[i]); 710d7e35d4aSPaolo Bonzini if (!(bufs[i] & VLAN_BD_VALID)) { 711d7e35d4aSPaolo Bonzini break; 712d7e35d4aSPaolo Bonzini } 713d7e35d4aSPaolo Bonzini total_len += VLAN_BD_LEN(bufs[i]); 714d7e35d4aSPaolo Bonzini } 715d7e35d4aSPaolo Bonzini 716d7e35d4aSPaolo Bonzini nbufs = i; 717f6bda9cbSPeter Maydell DPRINTF("h_send_logical_lan() %d buffers, total length 0x%x\n", 718d7e35d4aSPaolo Bonzini nbufs, total_len); 719d7e35d4aSPaolo Bonzini 720d7e35d4aSPaolo Bonzini if (total_len == 0) { 721d7e35d4aSPaolo Bonzini return H_SUCCESS; 722d7e35d4aSPaolo Bonzini } 723d7e35d4aSPaolo Bonzini 724d7e35d4aSPaolo Bonzini if (total_len > MAX_PACKET_SIZE) { 725d7e35d4aSPaolo Bonzini /* Don't let the guest force too large an allocation */ 726d7e35d4aSPaolo Bonzini return H_RESOURCE; 727d7e35d4aSPaolo Bonzini } 728d7e35d4aSPaolo Bonzini 729d7e35d4aSPaolo Bonzini lbuf = alloca(total_len); 730d7e35d4aSPaolo Bonzini p = lbuf; 731d7e35d4aSPaolo Bonzini for (i = 0; i < nbufs; i++) { 732d7e35d4aSPaolo Bonzini ret = spapr_vio_dma_read(sdev, VLAN_BD_ADDR(bufs[i]), 733d7e35d4aSPaolo Bonzini p, VLAN_BD_LEN(bufs[i])); 734d7e35d4aSPaolo Bonzini if (ret < 0) { 735d7e35d4aSPaolo Bonzini return ret; 736d7e35d4aSPaolo Bonzini } 737d7e35d4aSPaolo Bonzini 738d7e35d4aSPaolo Bonzini p += VLAN_BD_LEN(bufs[i]); 739d7e35d4aSPaolo Bonzini } 740d7e35d4aSPaolo Bonzini 741d7e35d4aSPaolo Bonzini qemu_send_packet(qemu_get_queue(dev->nic), lbuf, total_len); 742d7e35d4aSPaolo Bonzini 743d7e35d4aSPaolo Bonzini return H_SUCCESS; 744d7e35d4aSPaolo Bonzini } 745d7e35d4aSPaolo Bonzini 74628e02042SDavid Gibson static target_ulong h_multicast_ctrl(PowerPCCPU *cpu, sPAPRMachineState *spapr, 747d7e35d4aSPaolo Bonzini target_ulong opcode, target_ulong *args) 748d7e35d4aSPaolo Bonzini { 749d7e35d4aSPaolo Bonzini target_ulong reg = args[0]; 750d7e35d4aSPaolo Bonzini VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); 751d7e35d4aSPaolo Bonzini 752d7e35d4aSPaolo Bonzini if (!dev) { 753d7e35d4aSPaolo Bonzini return H_PARAMETER; 754d7e35d4aSPaolo Bonzini } 755d7e35d4aSPaolo Bonzini 756d7e35d4aSPaolo Bonzini return H_SUCCESS; 757d7e35d4aSPaolo Bonzini } 758d7e35d4aSPaolo Bonzini 759d7e35d4aSPaolo Bonzini static Property spapr_vlan_properties[] = { 760d7e35d4aSPaolo Bonzini DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev), 761d7e35d4aSPaolo Bonzini DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf), 762831e8822SThomas Huth DEFINE_PROP_BIT("use-rx-buffer-pools", VIOsPAPRVLANDevice, 76357c522f4SThomas Huth compat_flags, SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT, true), 764d7e35d4aSPaolo Bonzini DEFINE_PROP_END_OF_LIST(), 765d7e35d4aSPaolo Bonzini }; 766d7e35d4aSPaolo Bonzini 767831e8822SThomas Huth static bool spapr_vlan_rx_buffer_pools_needed(void *opaque) 768831e8822SThomas Huth { 769831e8822SThomas Huth VIOsPAPRVLANDevice *dev = opaque; 770831e8822SThomas Huth 771831e8822SThomas Huth return (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) != 0; 772831e8822SThomas Huth } 773831e8822SThomas Huth 774831e8822SThomas Huth static const VMStateDescription vmstate_rx_buffer_pool = { 775831e8822SThomas Huth .name = "spapr_llan/rx_buffer_pool", 776831e8822SThomas Huth .version_id = 1, 777831e8822SThomas Huth .minimum_version_id = 1, 778831e8822SThomas Huth .needed = spapr_vlan_rx_buffer_pools_needed, 779831e8822SThomas Huth .fields = (VMStateField[]) { 780831e8822SThomas Huth VMSTATE_INT32(bufsize, RxBufPool), 781831e8822SThomas Huth VMSTATE_INT32(count, RxBufPool), 782831e8822SThomas Huth VMSTATE_UINT64_ARRAY(bds, RxBufPool, RX_POOL_MAX_BDS), 783831e8822SThomas Huth VMSTATE_END_OF_LIST() 784831e8822SThomas Huth } 785831e8822SThomas Huth }; 786831e8822SThomas Huth 787831e8822SThomas Huth static const VMStateDescription vmstate_rx_pools = { 788831e8822SThomas Huth .name = "spapr_llan/rx_pools", 789831e8822SThomas Huth .version_id = 1, 790831e8822SThomas Huth .minimum_version_id = 1, 791831e8822SThomas Huth .needed = spapr_vlan_rx_buffer_pools_needed, 792831e8822SThomas Huth .fields = (VMStateField[]) { 793831e8822SThomas Huth VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(rx_pool, VIOsPAPRVLANDevice, 794831e8822SThomas Huth RX_MAX_POOLS, 1, 795831e8822SThomas Huth vmstate_rx_buffer_pool, RxBufPool), 796831e8822SThomas Huth VMSTATE_END_OF_LIST() 797831e8822SThomas Huth } 798831e8822SThomas Huth }; 799831e8822SThomas Huth 800686fefe4SDavid Gibson static const VMStateDescription vmstate_spapr_llan = { 801686fefe4SDavid Gibson .name = "spapr_llan", 802686fefe4SDavid Gibson .version_id = 1, 803686fefe4SDavid Gibson .minimum_version_id = 1, 804686fefe4SDavid Gibson .fields = (VMStateField[]) { 805686fefe4SDavid Gibson VMSTATE_SPAPR_VIO(sdev, VIOsPAPRVLANDevice), 806686fefe4SDavid Gibson /* LLAN state */ 807686fefe4SDavid Gibson VMSTATE_BOOL(isopen, VIOsPAPRVLANDevice), 808cbd62f86SPaolo Bonzini VMSTATE_UINT64(buf_list, VIOsPAPRVLANDevice), 809686fefe4SDavid Gibson VMSTATE_UINT32(add_buf_ptr, VIOsPAPRVLANDevice), 810686fefe4SDavid Gibson VMSTATE_UINT32(use_buf_ptr, VIOsPAPRVLANDevice), 811686fefe4SDavid Gibson VMSTATE_UINT32(rx_bufs, VIOsPAPRVLANDevice), 812cbd62f86SPaolo Bonzini VMSTATE_UINT64(rxq_ptr, VIOsPAPRVLANDevice), 813686fefe4SDavid Gibson 814686fefe4SDavid Gibson VMSTATE_END_OF_LIST() 815686fefe4SDavid Gibson }, 816831e8822SThomas Huth .subsections = (const VMStateDescription * []) { 817831e8822SThomas Huth &vmstate_rx_pools, 818831e8822SThomas Huth NULL 819831e8822SThomas Huth } 820686fefe4SDavid Gibson }; 821686fefe4SDavid Gibson 822d7e35d4aSPaolo Bonzini static void spapr_vlan_class_init(ObjectClass *klass, void *data) 823d7e35d4aSPaolo Bonzini { 824d7e35d4aSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass); 825d7e35d4aSPaolo Bonzini VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); 826d7e35d4aSPaolo Bonzini 82728b07e73SMarkus Armbruster k->realize = spapr_vlan_realize; 828d7e35d4aSPaolo Bonzini k->reset = spapr_vlan_reset; 829d7e35d4aSPaolo Bonzini k->devnode = spapr_vlan_devnode; 830d7e35d4aSPaolo Bonzini k->dt_name = "l-lan"; 831d7e35d4aSPaolo Bonzini k->dt_type = "network"; 832d7e35d4aSPaolo Bonzini k->dt_compatible = "IBM,l-lan"; 833d7e35d4aSPaolo Bonzini k->signal_mask = 0x1; 83429fdedfeSAlexey Kardashevskiy set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); 835d7e35d4aSPaolo Bonzini dc->props = spapr_vlan_properties; 836d7e35d4aSPaolo Bonzini k->rtce_window_size = 0x10000000; 837686fefe4SDavid Gibson dc->vmsd = &vmstate_spapr_llan; 838d7e35d4aSPaolo Bonzini } 839d7e35d4aSPaolo Bonzini 840d7e35d4aSPaolo Bonzini static const TypeInfo spapr_vlan_info = { 841fd506b4fSDavid Gibson .name = TYPE_VIO_SPAPR_VLAN_DEVICE, 842d7e35d4aSPaolo Bonzini .parent = TYPE_VIO_SPAPR_DEVICE, 843d7e35d4aSPaolo Bonzini .instance_size = sizeof(VIOsPAPRVLANDevice), 844d7e35d4aSPaolo Bonzini .class_init = spapr_vlan_class_init, 845dfe79cf2SGonglei .instance_init = spapr_vlan_instance_init, 846831e8822SThomas Huth .instance_finalize = spapr_vlan_instance_finalize, 847d7e35d4aSPaolo Bonzini }; 848d7e35d4aSPaolo Bonzini 849d7e35d4aSPaolo Bonzini static void spapr_vlan_register_types(void) 850d7e35d4aSPaolo Bonzini { 851d7e35d4aSPaolo Bonzini spapr_register_hypercall(H_REGISTER_LOGICAL_LAN, h_register_logical_lan); 852d7e35d4aSPaolo Bonzini spapr_register_hypercall(H_FREE_LOGICAL_LAN, h_free_logical_lan); 853d7e35d4aSPaolo Bonzini spapr_register_hypercall(H_SEND_LOGICAL_LAN, h_send_logical_lan); 854d7e35d4aSPaolo Bonzini spapr_register_hypercall(H_ADD_LOGICAL_LAN_BUFFER, 855d7e35d4aSPaolo Bonzini h_add_logical_lan_buffer); 856d7e35d4aSPaolo Bonzini spapr_register_hypercall(H_MULTICAST_CTRL, h_multicast_ctrl); 857d7e35d4aSPaolo Bonzini type_register_static(&spapr_vlan_info); 858d7e35d4aSPaolo Bonzini } 859d7e35d4aSPaolo Bonzini 860d7e35d4aSPaolo Bonzini type_init(spapr_vlan_register_types) 861