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
11 extern 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 */
19 static 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
core_udp_configure(EFI_UDP4 * udp,EFI_UDP4_CONFIG_DATA * udata,short unsigned int * f)31 EFI_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 */
core_udp_open(struct pxe_pvt_inode * socket)64 int 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
105 bail:
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 */
core_udp_close(struct pxe_pvt_inode * socket)120 void 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 */
core_udp_connect(struct pxe_pvt_inode * socket,uint32_t ip,uint16_t port)139 void 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 */
core_udp_disconnect(struct pxe_pvt_inode * socket)171 void 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
185 static int volatile cb_status = -1;
udp4_cb(EFI_EVENT event,void * context)186 static 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 */
core_udp_recv(struct pxe_pvt_inode * socket,void * buf,uint16_t * buf_len,uint32_t * src_ip,uint16_t * src_port)208 int 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
271 bail:
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 */
core_udp_send(struct pxe_pvt_inode * socket,const void * data,size_t len)283 void 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
326 close:
327 uefi_call_wrapper(BS->CloseEvent, 1, token->Event);
328
329 bail:
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 */
core_udp_sendto(struct pxe_pvt_inode * socket,const void * data,size_t len,uint32_t ip,uint16_t port)343 void 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
409 close:
410 uefi_call_wrapper(BS->CloseEvent, 1, token->Event);
411
412 bail:
413 free(txdata);
414 free(token);
415 out:
416 efi_destroy_binding(b, &Udp4ServiceBindingProtocol);
417 }
418