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