Adding upstream version 6.03~pre2+dfsg.
[syslinux-debian/hramrach.git] / efi / udp.c
blob7c1d09e20b2d5931a12b7c4223d845b5c7d9426c
1 /*
2 * Copyright 2013-2014 Intel Corporation - All Rights Reserved
3 */
5 #include <string.h>
6 #include <minmax.h>
7 #include "efi.h"
8 #include "net.h"
9 #include "fs/pxe/pxe.h"
11 extern EFI_GUID Udp4ServiceBindingProtocol, Udp4Protocol;
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.
19 static struct efi_binding *udp_reader;
21 /**
22 * Try to configure this UDP socket
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.
28 * @out: status as EFI_STATUS
31 EFI_STATUS core_udp_configure(EFI_UDP4 *udp, EFI_UDP4_CONFIG_DATA *udata,
32 short unsigned int *f)
34 EFI_STATUS status;
35 int unmapped = 1;
36 jiffies_t start, last, cur;
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;
54 return status;
57 /**
58 * Open a socket
60 * @param:socket, the socket to open
62 * @out: error code, 0 on success, -1 on failure
64 int core_udp_open(struct pxe_pvt_inode *socket)
66 EFI_UDP4_CONFIG_DATA udata;
67 struct efi_binding *b;
68 EFI_STATUS status;
69 EFI_UDP4 *udp;
71 (void)socket;
73 udp_reader = efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
74 if (!udp_reader)
75 return -1;
77 b = efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
78 if (!b)
79 goto bail;
81 udp = (EFI_UDP4 *)udp_reader->this;
83 memset(&udata, 0, sizeof(udata));
85 status = core_udp_configure(udp, &udata, L"core_udp_open");
86 if (status != EFI_SUCCESS)
87 goto bail;
89 socket->net.efi.binding = b;
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.
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;
103 return 0;
105 bail:
106 if (b)
107 efi_destroy_binding(b, &Udp4ServiceBindingProtocol);
109 efi_destroy_binding(udp_reader, &Udp4ServiceBindingProtocol);
110 udp_reader = NULL;
112 return -1;
116 * Close a socket
118 * @param:socket, the socket to open
120 void core_udp_close(struct pxe_pvt_inode *socket)
122 efi_destroy_binding(udp_reader, &Udp4ServiceBindingProtocol);
123 udp_reader = NULL;
125 if (!socket->net.efi.binding)
126 return;
128 efi_destroy_binding(socket->net.efi.binding, &Udp4ServiceBindingProtocol);
129 socket->net.efi.binding = NULL;
133 * Establish a connection on an open socket
135 * @param:socket, the open socket
136 * @param:ip, the ip address
137 * @param:port, the port number, host-byte order
139 void core_udp_connect(struct pxe_pvt_inode *socket, uint32_t ip,
140 uint16_t port)
142 EFI_UDP4_CONFIG_DATA udata;
143 EFI_STATUS status;
144 EFI_UDP4 *udp;
146 udp = (EFI_UDP4 *)socket->net.efi.binding->this;
148 memset(&udata, 0, sizeof(udata));
150 /* Re-use the existing local port number */
151 udata.StationPort = socket->net.efi.localport;
153 udata.UseDefaultAddress = TRUE;
154 memcpy(&udata.RemoteAddress, &ip, sizeof(ip));
155 udata.RemotePort = port;
156 udata.AcceptPromiscuous = TRUE;
157 udata.TimeToLive = 64;
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;
167 * Tear down a connection on an open socket
169 * @param:socket, the open socket
171 void core_udp_disconnect(struct pxe_pvt_inode *socket)
173 EFI_STATUS status;
174 EFI_UDP4 *udp;
176 udp = (EFI_UDP4 *)socket->net.efi.binding->this;
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);
185 static int volatile cb_status = -1;
186 static EFIAPI void udp4_cb(EFI_EVENT event, void *context)
188 (void)event;
190 EFI_UDP4_COMPLETION_TOKEN *token = context;
192 if (token->Status == EFI_SUCCESS)
193 cb_status = 0;
194 else
195 cb_status = 1;
199 * Read data from the network stack
201 * @param:socket, the open socket
202 * @param:buf, location of buffer to store data
203 * @param:buf_len, size of buffer
205 * @out: src_ip, ip address of the data source
206 * @out: src_port, port number of the data source, host-byte order
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)
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;
221 (void)socket;
223 b = udp_reader;
224 udp = (EFI_UDP4 *)b->this;
225 memset(&token, 0, sizeof(token));
227 status = efi_setup_event(&token.Event, (EFI_EVENT_NOTIFY)udp4_cb,
228 &token);
229 if (status != EFI_SUCCESS)
230 return -1;
232 status = uefi_call_wrapper(udp->Receive, 2, udp, &token);
233 if (status != EFI_SUCCESS)
234 goto bail;
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");
243 uefi_call_wrapper(udp->Cancel, 2, udp, &token);
244 dprintf("core_udp_recv: timed out\n");
247 uefi_call_wrapper(udp->Poll, 1, udp);
250 if (cb_status == 0)
251 rv = 0;
253 /* Reset */
254 cb_status = -1;
256 if (rv)
257 goto bail;
259 rxdata = token.Packet.RxData;
260 frag = &rxdata->FragmentTable[0];
262 size = min(frag->FragmentLength, *buf_len);
263 memcpy(buf, frag->FragmentBuffer, size);
264 *buf_len = size;
266 memcpy(src_port, &rxdata->UdpSession.SourcePort, sizeof(*src_port));
267 memcpy(src_ip, &rxdata->UdpSession.SourceAddress, sizeof(*src_ip));
269 uefi_call_wrapper(BS->SignalEvent, 1, rxdata->RecycleSignal);
271 bail:
272 uefi_call_wrapper(BS->CloseEvent, 1, token.Event);
273 return rv;
277 * Send a UDP packet.
279 * @param:socket, the open socket
280 * @param:data, data buffer to send
281 * @param:len, size of data bufer
283 void core_udp_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
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;
292 token = zalloc(sizeof(*token));
293 if (!token)
294 return;
296 txdata = zalloc(sizeof(*txdata));
297 if (!txdata) {
298 free(token);
299 return;
302 status = efi_setup_event(&token->Event, (EFI_EVENT_NOTIFY)udp4_cb,
303 token);
304 if (status != EFI_SUCCESS)
305 goto bail;
307 txdata->DataLength = len;
308 txdata->FragmentCount = 1;
309 frag = &txdata->FragmentTable[0];
311 frag->FragmentLength = len;
312 frag->FragmentBuffer = (void *)data;
314 token->Packet.TxData = txdata;
316 status = uefi_call_wrapper(udp->Transmit, 2, udp, token);
317 if (status != EFI_SUCCESS)
318 goto close;
320 while (cb_status == -1)
321 uefi_call_wrapper(udp->Poll, 1, udp);
323 /* Reset */
324 cb_status = -1;
326 close:
327 uefi_call_wrapper(BS->CloseEvent, 1, token->Event);
329 bail:
330 free(txdata);
331 free(token);
335 * Send a UDP packet to a destination
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
343 void core_udp_sendto(struct pxe_pvt_inode *socket, const void *data,
344 size_t len, uint32_t ip, uint16_t port)
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;
354 (void)socket;
356 b = efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
357 if (!b)
358 return;
360 udp = (EFI_UDP4 *)b->this;
362 token = zalloc(sizeof(*token));
363 if (!token)
364 goto out;
366 txdata = zalloc(sizeof(*txdata));
367 if (!txdata)
368 goto bail;
370 memset(&udata, 0, sizeof(udata));
372 /* Re-use the existing local port number */
373 udata.StationPort = socket->net.efi.localport;
375 udata.UseDefaultAddress = TRUE;
376 memcpy(&udata.RemoteAddress, &ip, sizeof(ip));
377 udata.RemotePort = port;
378 udata.AcceptPromiscuous = TRUE;
379 udata.TimeToLive = 64;
381 status = core_udp_configure(udp, &udata, L"core_udp_sendto");
382 if (status != EFI_SUCCESS)
383 goto bail;
385 status = efi_setup_event(&token->Event, (EFI_EVENT_NOTIFY)udp4_cb,
386 token);
387 if (status != EFI_SUCCESS)
388 goto bail;
390 txdata->DataLength = len;
391 txdata->FragmentCount = 1;
392 frag = &txdata->FragmentTable[0];
394 frag->FragmentLength = len;
395 frag->FragmentBuffer = (void *)data;
397 token->Packet.TxData = txdata;
399 status = uefi_call_wrapper(udp->Transmit, 2, udp, token);
400 if (status != EFI_SUCCESS)
401 goto close;
403 while (cb_status == -1)
404 uefi_call_wrapper(udp->Poll, 1, udp);
406 /* Reset */
407 cb_status = -1;
409 close:
410 uefi_call_wrapper(BS->CloseEvent, 1, token->Event);
412 bail:
413 free(txdata);
414 free(token);
415 out:
416 efi_destroy_binding(b, &Udp4ServiceBindingProtocol);