1/*
2 * Copyright 2013-2014 Intel Corporation - All Rights Reserved
3 */
4
5#include <string.h>
6#include <minmax.h>
7#include "efi.h"
8#include "net.h"
9#include "fs/pxe/pxe.h"
10
11extern EFI_GUID Udp4ServiceBindingProtocol, Udp4Protocol;
12
13/*
14 * This UDP binding is configured to operate in promiscuous mode. It is
15 * only used for reading packets. It has no associated state unlike
16 * socket->net.efi.binding, which has a remote IP address and port
17 * number.
18 */
19static struct efi_binding *udp_reader;
20
21/**
22 * Try to configure this UDP socket
23 *
24 * @param:udp, the EFI_UDP4 socket to configure
25 * @param:udata, the EFI_UDP4_CONFIG_DATA to use
26 * @param:f, the name of the function as a wide string.
27 *
28 * @out: status as EFI_STATUS
29 */
30
31EFI_STATUS core_udp_configure(EFI_UDP4 *udp, EFI_UDP4_CONFIG_DATA *udata,
32	short unsigned int *f)
33{
34    EFI_STATUS status;
35    int unmapped = 1;
36    jiffies_t start, last, cur;
37
38    last = start = jiffies();
39    while (unmapped){
40	status = uefi_call_wrapper(udp->Configure, 2, udp, udata);
41	if (status != EFI_NO_MAPPING)
42		unmapped = 0;
43	else {
44	    cur = jiffies();
45	    if ( (cur - last) >= EFI_NOMAP_PRINT_DELAY ) {
46		last = cur;
47		Print(L"%s: stalling on configure with no mapping\n", f);
48	    } else if ( (cur - start) > EFI_NOMAP_PRINT_DELAY * EFI_NOMAP_PRINT_COUNT) {
49		Print(L"%s: aborting on no mapping\n", f);
50		unmapped = 0;
51	    }
52	}
53    }
54    return status;
55}
56
57/**
58 * Open a socket
59 *
60 * @param:socket, the socket to open
61 *
62 * @out: error code, 0 on success, -1 on failure
63 */
64int core_udp_open(struct pxe_pvt_inode *socket)
65{
66    EFI_UDP4_CONFIG_DATA udata;
67    struct efi_binding *b;
68    EFI_STATUS status;
69    EFI_UDP4 *udp;
70
71    (void)socket;
72
73    udp_reader = efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
74    if (!udp_reader)
75	return -1;
76
77    b = efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
78    if (!b)
79	goto bail;
80
81    udp = (EFI_UDP4 *)udp_reader->this;
82
83    memset(&udata, 0, sizeof(udata));
84
85    status = core_udp_configure(udp, &udata, L"core_udp_open");
86    if (status != EFI_SUCCESS)
87	goto bail;
88
89    socket->net.efi.binding = b;
90
91    /*
92     * Save the random local port number that the UDPv4 Protocol
93     * Driver picked for us. The TFTP protocol uses the local port
94     * number as the TID.
95     */
96    status = uefi_call_wrapper(udp->GetModeData, 5, udp,
97			       &udata, NULL, NULL, NULL);
98    if (status != EFI_SUCCESS)
99	Print(L"Failed to get UDP mode data: %d\n", status);
100    else
101	socket->net.efi.localport = udata.StationPort;
102
103    return 0;
104
105bail:
106    if (b)
107	efi_destroy_binding(b, &Udp4ServiceBindingProtocol);
108
109    efi_destroy_binding(udp_reader, &Udp4ServiceBindingProtocol);
110    udp_reader = NULL;
111
112    return -1;
113}
114
115/**
116 * Close a socket
117 *
118 * @param:socket, the socket to open
119 */
120void core_udp_close(struct pxe_pvt_inode *socket)
121{
122    efi_destroy_binding(udp_reader, &Udp4ServiceBindingProtocol);
123    udp_reader = NULL;
124
125    if (!socket->net.efi.binding)
126	return;
127
128    efi_destroy_binding(socket->net.efi.binding, &Udp4ServiceBindingProtocol);
129    socket->net.efi.binding = NULL;
130}
131
132/**
133 * Establish a connection on an open socket
134 *
135 * @param:socket, the open socket
136 * @param:ip, the ip address
137 * @param:port, the port number, host-byte order
138 */
139void core_udp_connect(struct pxe_pvt_inode *socket, uint32_t ip,
140		      uint16_t port)
141{
142    EFI_UDP4_CONFIG_DATA udata;
143    EFI_STATUS status;
144    EFI_UDP4 *udp;
145
146    udp = (EFI_UDP4 *)socket->net.efi.binding->this;
147
148    memset(&udata, 0, sizeof(udata));
149
150    /* Re-use the existing local port number */
151    udata.StationPort = socket->net.efi.localport;
152
153    udata.UseDefaultAddress = TRUE;
154    memcpy(&udata.RemoteAddress, &ip, sizeof(ip));
155    udata.RemotePort = port;
156    udata.AcceptPromiscuous = TRUE;
157    udata.TimeToLive = 64;
158
159    status = core_udp_configure(udp, &udata, L"core_udp_connect");
160    if (status != EFI_SUCCESS) {
161	Print(L"Failed to configure UDP: %d\n", status);
162	return;
163    }
164}
165
166/**
167 * Tear down a connection on an open socket
168 *
169 * @param:socket, the open socket
170 */
171void core_udp_disconnect(struct pxe_pvt_inode *socket)
172{
173    EFI_STATUS status;
174    EFI_UDP4 *udp;
175
176    udp = (EFI_UDP4 *)socket->net.efi.binding->this;
177
178    /* Reset */
179    status = uefi_call_wrapper(udp->Configure, 2, udp, NULL);
180    if (status != EFI_SUCCESS)
181	Print(L"Failed to reset UDP: %d\n", status);
182
183}
184
185static int volatile cb_status = -1;
186static EFIAPI void udp4_cb(EFI_EVENT event, void *context)
187{
188    (void)event;
189
190    EFI_UDP4_COMPLETION_TOKEN *token = context;
191
192    if (token->Status == EFI_SUCCESS)
193	cb_status = 0;
194    else
195	cb_status = 1;
196}
197
198/**
199 * Read data from the network stack
200 *
201 * @param:socket, the open socket
202 * @param:buf, location of buffer to store data
203 * @param:buf_len, size of buffer
204
205 * @out: src_ip, ip address of the data source
206 * @out: src_port, port number of the data source, host-byte order
207 */
208int core_udp_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
209		  uint32_t *src_ip, uint16_t *src_port)
210{
211    EFI_UDP4_COMPLETION_TOKEN token;
212    EFI_UDP4_FRAGMENT_DATA *frag;
213    EFI_UDP4_RECEIVE_DATA *rxdata;
214    struct efi_binding *b;
215    EFI_STATUS status;
216    EFI_UDP4 *udp;
217    size_t size;
218    int rv = -1;
219    jiffies_t start;
220
221    (void)socket;
222
223    b = udp_reader;
224    udp = (EFI_UDP4 *)b->this;
225    memset(&token, 0, sizeof(token));
226
227    status = efi_setup_event(&token.Event, (EFI_EVENT_NOTIFY)udp4_cb,
228			     &token);
229    if (status != EFI_SUCCESS)
230	return -1;
231
232    status = uefi_call_wrapper(udp->Receive, 2, udp, &token);
233    if (status != EFI_SUCCESS)
234	goto bail;
235
236    start = jiffies();
237    while (cb_status == -1) {
238	/* 15ms receive timeout... */
239	if (jiffies() - start >= 15) {
240	    if (jiffies() - start >= 30)
241		dprintf("Failed to cancel UDP\n");
242
243	    uefi_call_wrapper(udp->Cancel, 2, udp, &token);
244	    dprintf("core_udp_recv: timed out\n");
245	}
246
247	uefi_call_wrapper(udp->Poll, 1, udp);
248    }
249
250    if (cb_status == 0)
251	rv = 0;
252
253    /* Reset */
254    cb_status = -1;
255
256    if (rv)
257	goto bail;
258
259    rxdata = token.Packet.RxData;
260    frag = &rxdata->FragmentTable[0];
261
262    size = min(frag->FragmentLength, *buf_len);
263    memcpy(buf, frag->FragmentBuffer, size);
264    *buf_len = size;
265
266    memcpy(src_port, &rxdata->UdpSession.SourcePort, sizeof(*src_port));
267    memcpy(src_ip, &rxdata->UdpSession.SourceAddress, sizeof(*src_ip));
268
269    uefi_call_wrapper(BS->SignalEvent, 1, rxdata->RecycleSignal);
270
271bail:
272    uefi_call_wrapper(BS->CloseEvent, 1, token.Event);
273    return rv;
274}
275
276/**
277 * Send a UDP packet.
278 *
279 * @param:socket, the open socket
280 * @param:data, data buffer to send
281 * @param:len, size of data bufer
282 */
283void core_udp_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
284{
285    EFI_UDP4_COMPLETION_TOKEN *token;
286    EFI_UDP4_TRANSMIT_DATA *txdata;
287    EFI_UDP4_FRAGMENT_DATA *frag;
288    struct efi_binding *b = socket->net.efi.binding;
289    EFI_STATUS status;
290    EFI_UDP4 *udp = (EFI_UDP4 *)b->this;
291
292    token = zalloc(sizeof(*token));
293    if (!token)
294	return;
295
296    txdata = zalloc(sizeof(*txdata));
297    if (!txdata) {
298	free(token);
299	return;
300    }
301
302    status = efi_setup_event(&token->Event, (EFI_EVENT_NOTIFY)udp4_cb,
303			     token);
304    if (status != EFI_SUCCESS)
305	goto bail;
306
307    txdata->DataLength = len;
308    txdata->FragmentCount = 1;
309    frag = &txdata->FragmentTable[0];
310
311    frag->FragmentLength = len;
312    frag->FragmentBuffer = (void *)data;
313
314    token->Packet.TxData = txdata;
315
316    status = uefi_call_wrapper(udp->Transmit, 2, udp, token);
317    if (status != EFI_SUCCESS)
318	goto close;
319
320    while (cb_status == -1)
321	uefi_call_wrapper(udp->Poll, 1, udp);
322
323    /* Reset */
324    cb_status = -1;
325
326close:
327    uefi_call_wrapper(BS->CloseEvent, 1, token->Event);
328
329bail:
330    free(txdata);
331    free(token);
332}
333
334/**
335 * Send a UDP packet to a destination
336 *
337 * @param:socket, the open socket
338 * @param:data, data buffer to send
339 * @param:len, size of data bufer
340 * @param:ip, the ip address
341 * @param:port, the port number, host-byte order
342 */
343void core_udp_sendto(struct pxe_pvt_inode *socket, const void *data,
344		     size_t len, uint32_t ip, uint16_t port)
345{
346    EFI_UDP4_COMPLETION_TOKEN *token;
347    EFI_UDP4_TRANSMIT_DATA *txdata;
348    EFI_UDP4_FRAGMENT_DATA *frag;
349    EFI_UDP4_CONFIG_DATA udata;
350    EFI_STATUS status;
351    struct efi_binding *b;
352    EFI_UDP4 *udp;
353
354    (void)socket;
355
356    b = efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
357    if (!b)
358	return;
359
360    udp = (EFI_UDP4 *)b->this;
361
362    token = zalloc(sizeof(*token));
363    if (!token)
364	goto out;
365
366    txdata = zalloc(sizeof(*txdata));
367    if (!txdata)
368	goto bail;
369
370    memset(&udata, 0, sizeof(udata));
371
372    /* Re-use the existing local port number */
373    udata.StationPort = socket->net.efi.localport;
374
375    udata.UseDefaultAddress = TRUE;
376    memcpy(&udata.RemoteAddress, &ip, sizeof(ip));
377    udata.RemotePort = port;
378    udata.AcceptPromiscuous = TRUE;
379    udata.TimeToLive = 64;
380
381    status = core_udp_configure(udp, &udata, L"core_udp_sendto");
382    if (status != EFI_SUCCESS)
383	goto bail;
384
385    status = efi_setup_event(&token->Event, (EFI_EVENT_NOTIFY)udp4_cb,
386			     token);
387    if (status != EFI_SUCCESS)
388	goto bail;
389
390    txdata->DataLength = len;
391    txdata->FragmentCount = 1;
392    frag = &txdata->FragmentTable[0];
393
394    frag->FragmentLength = len;
395    frag->FragmentBuffer = (void *)data;
396
397    token->Packet.TxData = txdata;
398
399    status = uefi_call_wrapper(udp->Transmit, 2, udp, token);
400    if (status != EFI_SUCCESS)
401	goto close;
402
403    while (cb_status == -1)
404	uefi_call_wrapper(udp->Poll, 1, udp);
405
406    /* Reset */
407    cb_status = -1;
408
409close:
410    uefi_call_wrapper(BS->CloseEvent, 1, token->Event);
411
412bail:
413    free(txdata);
414    free(token);
415out:
416    efi_destroy_binding(b, &Udp4ServiceBindingProtocol);
417}
418