1 /******************************************************************************
2 * Copyright (c) 2011, 2013 IBM Corporation
3 * All rights reserved.
4 * This program and the accompanying materials
5 * are made available under the terms of the BSD License
6 * which accompanies this distribution, and is available at
7 * http://www.opensource.org/licenses/bsd-license.php
8 *
9 * Contributors:
10 * IBM Corporation - initial implementation
11 *****************************************************************************/
12
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <stdint.h>
16 #include <string.h>
17 #include <helpers.h>
18 #include "veth.h"
19 #include "libhvcall.h"
20
21 #undef VETH_DEBUG
22 //#define VETH_DEBUG
23 #ifdef VETH_DEBUG
24 #define dprintf(_x ...) do { printf(_x); } while(0)
25 #else
26 #define dprintf(_x ...)
27 #endif
28
29 /* *** WARNING: We pass our addresses as-is as DMA addresses,
30 * we -do- rely on the forth code to have enabled TCE bypass
31 * on our device !
32 */
33 #define vaddr_to_dma(vaddr) ((uint64_t)vaddr)
34
35 struct ibmveth_buf_desc_fields {
36 uint32_t flags_len;
37 #define IBMVETH_BUF_VALID 0x80000000
38 #define IBMVETH_BUF_TOGGLE 0x40000000
39 #define IBMVETH_BUF_NO_CSUM 0x02000000
40 #define IBMVETH_BUF_CSUM_GOOD 0x01000000
41 #define IBMVETH_BUF_LEN_MASK 0x00FFFFFF
42 uint32_t address;
43 };
44
45 union ibmveth_buf_desc {
46 uint64_t desc;
47 struct ibmveth_buf_desc_fields fields;
48 };
49
50 struct ibmveth_rx_q_entry {
51 uint32_t flags_off;
52 #define IBMVETH_RXQ_TOGGLE 0x80000000
53 #define IBMVETH_RXQ_TOGGLE_SHIFT 31
54 #define IBMVETH_RXQ_VALID 0x40000000
55 #define IBMVETH_RXQ_NO_CSUM 0x02000000
56 #define IBMVETH_RXQ_CSUM_GOOD 0x01000000
57 #define IBMVETH_RXQ_OFF_MASK 0x0000FFFF
58
59 uint32_t length;
60 uint64_t correlator;
61 };
62
63 static void *buffer_list;
64 static void *filter_list;
65 static uint64_t *rx_bufs;
66 static uint64_t *rx_bufs_aligned;
67 static uint32_t cur_rx_toggle;
68 static uint32_t cur_rx_index;
69
70 #define RX_QUEUE_SIZE 256
71 #define RX_BUF_SIZE 2048
72 #define RX_BUF_MULT (RX_BUF_SIZE >> 3)
73
74 static struct ibmveth_rx_q_entry *rx_queue;
75
veth_get_rx_buf(unsigned int i)76 static inline uint64_t *veth_get_rx_buf(unsigned int i)
77 {
78 return &rx_bufs_aligned[i * RX_BUF_MULT];
79 }
80
veth_init(net_driver_t * driver)81 static int veth_init(net_driver_t *driver)
82 {
83 char *mac_addr;
84 union ibmveth_buf_desc rxq_desc;
85 unsigned long rx_queue_len = sizeof(struct ibmveth_rx_q_entry) *
86 RX_QUEUE_SIZE;
87 unsigned int i;
88 long rc;
89
90 if (!driver)
91 return -1;
92
93 dprintf("veth_init(%02x:%02x:%02x:%02x:%02x:%02x)\n",
94 mac_addr[0], mac_addr[1], mac_addr[2],
95 mac_addr[3], mac_addr[4], mac_addr[5]);
96
97 if (driver->running != 0)
98 return 0;
99
100 mac_addr = (char *)driver->mac_addr;
101 cur_rx_toggle = IBMVETH_RXQ_TOGGLE;
102 cur_rx_index = 0;
103 buffer_list = SLOF_alloc_mem_aligned(8192, 4096);
104 filter_list = buffer_list + 4096;
105 rx_queue = SLOF_alloc_mem_aligned(rx_queue_len, 16);
106 rx_bufs = SLOF_alloc_mem(2048 * RX_QUEUE_SIZE + 4);
107 if (!buffer_list || !filter_list || !rx_queue || !rx_bufs) {
108 printf("veth: Failed to allocate memory !\n");
109 goto fail;
110 }
111 rx_bufs_aligned = (uint64_t *)(((uint64_t)rx_bufs | 3) + 1);
112 rxq_desc.fields.address = vaddr_to_dma(rx_queue);
113 rxq_desc.fields.flags_len = IBMVETH_BUF_VALID | rx_queue_len;
114
115 rc = h_register_logical_lan(driver->reg,
116 vaddr_to_dma(buffer_list),
117 rxq_desc.desc,
118 vaddr_to_dma(filter_list),
119 (*(uint64_t *)mac_addr) >> 16);
120 if (rc != H_SUCCESS) {
121 printf("veth: Error %ld registering interface !\n", rc);
122 goto fail;
123 }
124 for (i = 0; i < RX_QUEUE_SIZE; i++) {
125 uint64_t *buf = veth_get_rx_buf(i);
126 union ibmveth_buf_desc desc;
127 *buf = (uint64_t)buf;
128 desc.fields.address = vaddr_to_dma(buf);
129 desc.fields.flags_len = IBMVETH_BUF_VALID | RX_BUF_SIZE;
130 h_add_logical_lan_buffer(driver->reg, desc.desc);
131 }
132
133 driver->running = 1;
134
135 return 0;
136 fail:
137 if (buffer_list)
138 SLOF_free_mem(buffer_list, 8192);
139 if (rx_queue)
140 SLOF_free_mem(rx_queue, rx_queue_len);
141 if (rx_bufs)
142 SLOF_free_mem(rx_bufs, 2048 * RX_QUEUE_SIZE + 4);
143 return -1;
144 }
145
veth_term(net_driver_t * driver)146 static int veth_term(net_driver_t *driver)
147 {
148 dprintf("veth_term()\n");
149
150 if (driver->running == 0)
151 return 0;
152
153 h_free_logical_lan(driver->reg);
154
155 if (buffer_list)
156 SLOF_free_mem(buffer_list, 8192);
157 if (rx_queue)
158 SLOF_free_mem(rx_queue, sizeof(struct ibmveth_rx_q_entry) * RX_QUEUE_SIZE);
159 if (rx_bufs)
160 SLOF_free_mem(rx_bufs, 2048 * RX_QUEUE_SIZE + 4);
161
162 driver->running = 0;
163
164 return 0;
165 }
166
veth_receive(char * f_buffer_pc,unsigned f_len_i,net_driver_t * driver)167 static int veth_receive(char *f_buffer_pc, unsigned f_len_i, net_driver_t *driver)
168 {
169 int packet = 0;
170
171 dprintf("veth_receive()\n");
172
173 while(!packet) {
174 struct ibmveth_rx_q_entry *desc = &rx_queue[cur_rx_index];
175 union ibmveth_buf_desc bdesc;
176 void *buf;
177
178 buf = (void *)desc->correlator;
179
180 if ((desc->flags_off & IBMVETH_RXQ_TOGGLE) != cur_rx_toggle)
181 break;
182
183 if (!(desc->flags_off & IBMVETH_RXQ_VALID))
184 goto recycle;
185 if (desc->length > f_len_i) {
186 printf("veth: Dropping too big packet [%d bytes]\n",
187 desc->length);
188 goto recycle;
189 }
190
191 packet = desc->length;
192 memcpy(f_buffer_pc,
193 buf + (desc->flags_off & IBMVETH_RXQ_OFF_MASK), packet);
194 recycle:
195 bdesc.fields.address = vaddr_to_dma(buf);
196 bdesc.fields.flags_len = IBMVETH_BUF_VALID | RX_BUF_SIZE;
197 h_add_logical_lan_buffer(driver->reg, bdesc.desc);
198
199 cur_rx_index = (cur_rx_index + 1) % RX_QUEUE_SIZE;
200 if (cur_rx_index == 0)
201 cur_rx_toggle ^= IBMVETH_RXQ_TOGGLE;
202 }
203
204 return packet;
205 }
206
veth_xmit(char * f_buffer_pc,int f_len_i,net_driver_t * driver)207 static int veth_xmit(char *f_buffer_pc, int f_len_i, net_driver_t *driver)
208 {
209 union ibmveth_buf_desc tx_desc;
210 long rc;
211
212 dprintf("veth_xmit(packet at %p, %d bytes)\n", f_buffer_pc, f_len_i);
213
214 tx_desc.fields.address = vaddr_to_dma(f_buffer_pc);
215 tx_desc.fields.flags_len = IBMVETH_BUF_VALID | f_len_i;
216
217 rc = hv_send_logical_lan(driver->reg, tx_desc.desc, 0, 0, 0, 0, 0);
218 if (rc != H_SUCCESS) {
219 printf("veth: Error %ld sending packet !\n", rc);
220 return -1;
221 }
222
223 return f_len_i;
224 }
225
libveth_open(char * mac_addr,unsigned mac_len,char * reg,unsigned reg_len)226 net_driver_t *libveth_open(char *mac_addr, unsigned mac_len, char *reg, unsigned reg_len)
227 {
228 net_driver_t *driver;
229
230 if (reg_len != sizeof(uint32_t)) {
231 printf("vio reg must 1 cell long\n");
232 return NULL;
233 }
234 driver = SLOF_alloc_mem(sizeof(*driver));
235 if (!driver) {
236 printf("Unable to allocate veth driver\n");
237 return NULL;
238 }
239
240 /* veth uses a 8-byte wide property instead of 6-byte wide MACs */
241 if ((mac_len == 8) && (mac_addr[0] == 0) && mac_addr[1] == 0)
242 mac_addr += 2;
243 memcpy(driver->mac_addr, mac_addr, 6);
244 driver->reg = *(uint32_t *)reg;
245 driver->running = 0;
246
247 if (veth_init(driver)) {
248 SLOF_free_mem(driver, sizeof(*driver));
249 return NULL;
250 }
251
252 return driver;
253 }
254
libveth_close(net_driver_t * driver)255 void libveth_close(net_driver_t *driver)
256 {
257 if (driver) {
258 veth_term(driver);
259 SLOF_free_mem(driver, sizeof(*driver));
260 }
261 }
262
libveth_read(char * buf,int len,net_driver_t * driver)263 int libveth_read(char *buf, int len, net_driver_t *driver)
264 {
265 if (buf)
266 return veth_receive(buf, len, driver);
267
268 return -1;
269 }
270
libveth_write(char * buf,int len,net_driver_t * driver)271 int libveth_write(char *buf, int len, net_driver_t *driver)
272 {
273 if (buf)
274 return veth_xmit(buf, len, driver);
275
276 return -1;
277 }
278