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