Commit 41b0587981c44552c53bafa8ac9121ee3a4b2ee0

Authored by Heinrich Schuchardt
Committed by Alexander Graf
1 parent 622fe621a2

efi_loader: fix simple network protocol

We should not call eth_rx() before the network interface is initialized.
The services of the simple network protocol should check the state of
the network adapter.

Add and correct comments.

Without this patch i.mx6 system Wandboard Quad rev B1 fails to execute
bootefi selftest.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Signed-off-by: Alexander Graf <agraf@suse.de>

Showing 1 changed file with 332 additions and 59 deletions Side-by-side Diff

lib/efi_loader/efi_net.c
... ... @@ -43,22 +43,68 @@
43 43 struct efi_pxe_mode pxe_mode;
44 44 };
45 45  
  46 +/*
  47 + * efi_net_start() - start the network interface
  48 + *
  49 + * This function implements the Start service of the
  50 + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
  51 + * (UEFI) specification for details.
  52 + *
  53 + * @this: pointer to the protocol instance
  54 + * Return: status code
  55 + */
46 56 static efi_status_t EFIAPI efi_net_start(struct efi_simple_network *this)
47 57 {
  58 + efi_status_t ret = EFI_SUCCESS;
  59 +
48 60 EFI_ENTRY("%p", this);
49 61  
50   - return EFI_EXIT(EFI_SUCCESS);
  62 + /* Check parameters */
  63 + if (!this) {
  64 + ret = EFI_INVALID_PARAMETER;
  65 + goto out;
  66 + }
  67 +
  68 + if (this->mode->state != EFI_NETWORK_STOPPED)
  69 + ret = EFI_ALREADY_STARTED;
  70 + else
  71 + this->mode->state = EFI_NETWORK_STARTED;
  72 +out:
  73 + return EFI_EXIT(ret);
51 74 }
52 75  
  76 +/*
  77 + * efi_net_stop() - stop the network interface
  78 + *
  79 + * This function implements the Stop service of the
  80 + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
  81 + * (UEFI) specification for details.
  82 + *
  83 + * @this: pointer to the protocol instance
  84 + * Return: status code
  85 + */
53 86 static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this)
54 87 {
  88 + efi_status_t ret = EFI_SUCCESS;
  89 +
55 90 EFI_ENTRY("%p", this);
56 91  
57   - return EFI_EXIT(EFI_SUCCESS);
  92 + /* Check parameters */
  93 + if (!this) {
  94 + ret = EFI_INVALID_PARAMETER;
  95 + goto out;
  96 + }
  97 +
  98 + if (this->mode->state == EFI_NETWORK_STOPPED)
  99 + ret = EFI_NOT_STARTED;
  100 + else
  101 + this->mode->state = EFI_NETWORK_STOPPED;
  102 +out:
  103 + return EFI_EXIT(ret);
58 104 }
59 105  
60 106 /*
61   - * Initialize network adapter and allocate transmit and receive buffers.
  107 + * efi_net_initialize() - initialize the network interface
62 108 *
63 109 * This function implements the Initialize service of the
64 110 * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
... ... @@ -67,7 +113,7 @@
67 113 * @this: pointer to the protocol instance
68 114 * @extra_rx: extra receive buffer to be allocated
69 115 * @extra_tx: extra transmit buffer to be allocated
70   - * @return: status code
  116 + * Return: status code
71 117 */
72 118 static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this,
73 119 ulong extra_rx, ulong extra_tx)
74 120  
... ... @@ -77,9 +123,10 @@
77 123  
78 124 EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
79 125  
  126 + /* Check parameters */
80 127 if (!this) {
81 128 r = EFI_INVALID_PARAMETER;
82   - goto error;
  129 + goto out;
83 130 }
84 131  
85 132 /* Setup packet buffers */
86 133  
87 134  
88 135  
89 136  
90 137  
91 138  
92 139  
93 140  
... ... @@ -92,32 +139,83 @@
92 139 ret = eth_init();
93 140 if (ret < 0) {
94 141 eth_halt();
  142 + this->mode->state = EFI_NETWORK_STOPPED;
95 143 r = EFI_DEVICE_ERROR;
  144 + goto out;
  145 + } else {
  146 + this->mode->state = EFI_NETWORK_INITIALIZED;
96 147 }
97   -
98   -error:
  148 +out:
99 149 return EFI_EXIT(r);
100 150 }
101 151  
  152 +/*
  153 + * efi_net_reset() - reinitialize the network interface
  154 + *
  155 + * This function implements the Reset service of the
  156 + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
  157 + * (UEFI) specification for details.
  158 + *
  159 + * @this: pointer to the protocol instance
  160 + * @extended_verification: execute exhaustive verification
  161 + * Return: status code
  162 + */
102 163 static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this,
103 164 int extended_verification)
104 165 {
105 166 EFI_ENTRY("%p, %x", this, extended_verification);
106 167  
107   - return EFI_EXIT(EFI_SUCCESS);
  168 + return EFI_EXIT(EFI_CALL(efi_net_initialize(this, 0, 0)));
108 169 }
109 170  
  171 +/*
  172 + * efi_net_shutdown() - shut down the network interface
  173 + *
  174 + * This function implements the Shutdown service of the
  175 + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
  176 + * (UEFI) specification for details.
  177 + *
  178 + * @this: pointer to the protocol instance
  179 + * Return: status code
  180 + */
110 181 static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this)
111 182 {
  183 + efi_status_t ret = EFI_SUCCESS;
  184 +
112 185 EFI_ENTRY("%p", this);
113 186  
114   - return EFI_EXIT(EFI_SUCCESS);
  187 + /* Check parameters */
  188 + if (!this) {
  189 + ret = EFI_INVALID_PARAMETER;
  190 + goto out;
  191 + }
  192 +
  193 + eth_halt();
  194 + this->mode->state = EFI_NETWORK_STOPPED;
  195 +
  196 +out:
  197 + return EFI_EXIT(ret);
115 198 }
116 199  
117   -static efi_status_t EFIAPI efi_net_receive_filters(
118   - struct efi_simple_network *this, u32 enable, u32 disable,
119   - int reset_mcast_filter, ulong mcast_filter_count,
120   - struct efi_mac_address *mcast_filter)
  200 +/*
  201 + * efi_net_receive_filters() - mange multicast receive filters
  202 + *
  203 + * This function implements the ReceiveFilters service of the
  204 + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
  205 + * (UEFI) specification for details.
  206 + *
  207 + * @this: pointer to the protocol instance
  208 + * @enable: bit mask of receive filters to enable
  209 + * @disable: bit mask of receive filters to disable
  210 + * @reset_mcast_filter: true resets contents of the filters
  211 + * @mcast_filter_count: number of hardware MAC addresses in the new filters list
  212 + * @mcast_filter: list of new filters
  213 + * Return: status code
  214 + */
  215 +static efi_status_t EFIAPI efi_net_receive_filters
  216 + (struct efi_simple_network *this, u32 enable, u32 disable,
  217 + int reset_mcast_filter, ulong mcast_filter_count,
  218 + struct efi_mac_address *mcast_filter)
121 219 {
122 220 EFI_ENTRY("%p, %x, %x, %x, %lx, %p", this, enable, disable,
123 221 reset_mcast_filter, mcast_filter_count, mcast_filter);
124 222  
... ... @@ -125,15 +223,40 @@
125 223 return EFI_EXIT(EFI_UNSUPPORTED);
126 224 }
127 225  
128   -static efi_status_t EFIAPI efi_net_station_address(
129   - struct efi_simple_network *this, int reset,
130   - struct efi_mac_address *new_mac)
  226 +/*
  227 + * efi_net_station_address() - set the hardware MAC address
  228 + *
  229 + * This function implements the StationAddress service of the
  230 + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
  231 + * (UEFI) specification for details.
  232 + *
  233 + * @this: pointer to the protocol instance
  234 + * @reset: if true reset the address to default
  235 + * @new_mac: new MAC address
  236 + * Return: status code
  237 + */
  238 +static efi_status_t EFIAPI efi_net_station_address
  239 + (struct efi_simple_network *this, int reset,
  240 + struct efi_mac_address *new_mac)
131 241 {
132 242 EFI_ENTRY("%p, %x, %p", this, reset, new_mac);
133 243  
134 244 return EFI_EXIT(EFI_UNSUPPORTED);
135 245 }
136 246  
  247 +/*
  248 + * efi_net_statistics() - reset or collect statistics of the network interface
  249 + *
  250 + * This function implements the Statistics service of the
  251 + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
  252 + * (UEFI) specification for details.
  253 + *
  254 + * @this: pointer to the protocol instance
  255 + * @reset: if true, the statistics are reset
  256 + * @stat_size: size of the statistics table
  257 + * @stat_table: table to receive the statistics
  258 + * Return: status code
  259 + */
137 260 static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this,
138 261 int reset, ulong *stat_size,
139 262 void *stat_table)
... ... @@ -143,6 +266,19 @@
143 266 return EFI_EXIT(EFI_UNSUPPORTED);
144 267 }
145 268  
  269 +/*
  270 + * efi_net_mcastiptomac() - translate multicast IP address to MAC address
  271 + *
  272 + * This function implements the Statistics service of the
  273 + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
  274 + * (UEFI) specification for details.
  275 + *
  276 + * @this: pointer to the protocol instance
  277 + * @ipv6: true if the IP address is an IPv6 address
  278 + * @ip: IP address
  279 + * @mac: MAC address
  280 + * Return: status code
  281 + */
146 282 static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this,
147 283 int ipv6,
148 284 struct efi_ip_address *ip,
... ... @@ -153,6 +289,19 @@
153 289 return EFI_EXIT(EFI_INVALID_PARAMETER);
154 290 }
155 291  
  292 +/**
  293 + * efi_net_nvdata() - read or write NVRAM
  294 + *
  295 + * This function implements the GetStatus service of the Simple Network
  296 + * Protocol. See the UEFI spec for details.
  297 + *
  298 + * @this: the instance of the Simple Network Protocol
  299 + * @readwrite: true for read, false for write
  300 + * @offset: offset in NVRAM
  301 + * @buffer_size: size of buffer
  302 + * @buffer: buffer
  303 + * Return: status code
  304 + */
156 305 static efi_status_t EFIAPI efi_net_nvdata(struct efi_simple_network *this,
157 306 int read_write, ulong offset,
158 307 ulong buffer_size, char *buffer)
159 308  
160 309  
... ... @@ -163,13 +312,42 @@
163 312 return EFI_EXIT(EFI_UNSUPPORTED);
164 313 }
165 314  
  315 +/**
  316 + * efi_net_get_status() - get interrupt status
  317 + *
  318 + * This function implements the GetStatus service of the Simple Network
  319 + * Protocol. See the UEFI spec for details.
  320 + *
  321 + * @this: the instance of the Simple Network Protocol
  322 + * @int_status: interface status
  323 + * @txbuf: transmission buffer
  324 + */
166 325 static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
167 326 u32 *int_status, void **txbuf)
168 327 {
  328 + efi_status_t ret = EFI_SUCCESS;
  329 +
169 330 EFI_ENTRY("%p, %p, %p", this, int_status, txbuf);
170 331  
171 332 efi_timer_check();
172 333  
  334 + /* Check parameters */
  335 + if (!this) {
  336 + ret = EFI_INVALID_PARAMETER;
  337 + goto out;
  338 + }
  339 +
  340 + switch (this->mode->state) {
  341 + case EFI_NETWORK_STOPPED:
  342 + ret = EFI_NOT_STARTED;
  343 + goto out;
  344 + case EFI_NETWORK_STARTED:
  345 + ret = EFI_DEVICE_ERROR;
  346 + goto out;
  347 + default:
  348 + break;
  349 + }
  350 +
173 351 if (int_status) {
174 352 /* We send packets synchronously, so nothing is outstanding */
175 353 *int_status = EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
176 354  
177 355  
178 356  
179 357  
180 358  
181 359  
182 360  
183 361  
184 362  
185 363  
... ... @@ -180,63 +358,103 @@
180 358 *txbuf = new_tx_packet;
181 359  
182 360 new_tx_packet = NULL;
183   -
184   - return EFI_EXIT(EFI_SUCCESS);
  361 +out:
  362 + return EFI_EXIT(ret);
185 363 }
186 364  
187   -static efi_status_t EFIAPI efi_net_transmit(struct efi_simple_network *this,
188   - size_t header_size, size_t buffer_size, void *buffer,
189   - struct efi_mac_address *src_addr,
190   - struct efi_mac_address *dest_addr, u16 *protocol)
  365 +/**
  366 + * efi_net_transmit() - transmit a packet
  367 + *
  368 + * This function implements the Transmit service of the Simple Network Protocol.
  369 + * See the UEFI spec for details.
  370 + *
  371 + * @this: the instance of the Simple Network Protocol
  372 + * @header_size: size of the media header
  373 + * @buffer_size: size of the buffer to receive the packet
  374 + * @buffer: buffer to receive the packet
  375 + * @src_addr: source hardware MAC address
  376 + * @dest_addr: destination hardware MAC address
  377 + * @protocol: type of header to build
  378 + * Return: status code
  379 + */
  380 +static efi_status_t EFIAPI efi_net_transmit
  381 + (struct efi_simple_network *this, size_t header_size,
  382 + size_t buffer_size, void *buffer,
  383 + struct efi_mac_address *src_addr,
  384 + struct efi_mac_address *dest_addr, u16 *protocol)
191 385 {
  386 + efi_status_t ret = EFI_SUCCESS;
  387 +
192 388 EFI_ENTRY("%p, %lu, %lu, %p, %p, %p, %p", this,
193 389 (unsigned long)header_size, (unsigned long)buffer_size,
194 390 buffer, src_addr, dest_addr, protocol);
195 391  
196 392 efi_timer_check();
197 393  
  394 + /* Check parameters */
  395 + if (!this) {
  396 + ret = EFI_INVALID_PARAMETER;
  397 + goto out;
  398 + }
  399 +
  400 + /* We do not support jumbo packets */
  401 + if (buffer_size > PKTSIZE_ALIGN) {
  402 + ret = EFI_INVALID_PARAMETER;
  403 + goto out;
  404 + }
  405 +
198 406 if (header_size) {
199   - /* We would need to create the header if header_size != 0 */
200   - return EFI_EXIT(EFI_INVALID_PARAMETER);
  407 + /*
  408 + * TODO: We would need to create the header
  409 + * if header_size != 0
  410 + */
  411 + ret = EFI_INVALID_PARAMETER;
  412 + goto out;
201 413 }
202 414  
203   - /* Ensure that the packet fits into the buffer */
204   - if (buffer_size > PKTSIZE_ALIGN)
205   - return EFI_EXIT(EFI_INVALID_PARAMETER);
  415 + switch (this->mode->state) {
  416 + case EFI_NETWORK_STOPPED:
  417 + ret = EFI_NOT_STARTED;
  418 + goto out;
  419 + case EFI_NETWORK_STARTED:
  420 + ret = EFI_DEVICE_ERROR;
  421 + goto out;
  422 + default:
  423 + break;
  424 + }
  425 +
  426 + /* Ethernet packets always fit, just bounce */
206 427 memcpy(transmit_buffer, buffer, buffer_size);
207 428 net_send_packet(transmit_buffer, buffer_size);
208 429  
209 430 new_tx_packet = buffer;
210 431  
211   - return EFI_EXIT(EFI_SUCCESS);
  432 +out:
  433 + return EFI_EXIT(ret);
212 434 }
213 435  
214   -static void efi_net_push(void *pkt, int len)
215   -{
216   - new_rx_packet = true;
217   - wait_for_packet->is_signaled = true;
218   -}
219   -
220   -/*
221   - * Receive a packet from a network interface.
  436 +/**
  437 + * efi_net_receive() - receive a packet from a network interface
222 438 *
223 439 * This function implements the Receive service of the Simple Network Protocol.
224 440 * See the UEFI spec for details.
225 441 *
226   - * @this the instance of the Simple Network Protocol
227   - * @header_size size of the media header
228   - * @buffer_size size of the buffer to receive the packet
229   - * @buffer buffer to receive the packet
230   - * @src_addr source MAC address
231   - * @dest_addr destination MAC address
232   - * @protocol protocol
233   - * @return status code
  442 + * @this: the instance of the Simple Network Protocol
  443 + * @header_size: size of the media header
  444 + * @buffer_size: size of the buffer to receive the packet
  445 + * @buffer: buffer to receive the packet
  446 + * @src_addr: source MAC address
  447 + * @dest_addr: destination MAC address
  448 + * @protocol: protocol
  449 + * Return: status code
234 450 */
235   -static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this,
236   - size_t *header_size, size_t *buffer_size, void *buffer,
237   - struct efi_mac_address *src_addr,
238   - struct efi_mac_address *dest_addr, u16 *protocol)
  451 +static efi_status_t EFIAPI efi_net_receive
  452 + (struct efi_simple_network *this, size_t *header_size,
  453 + size_t *buffer_size, void *buffer,
  454 + struct efi_mac_address *src_addr,
  455 + struct efi_mac_address *dest_addr, u16 *protocol)
239 456 {
  457 + efi_status_t ret = EFI_SUCCESS;
240 458 struct ethernet_hdr *eth_hdr;
241 459 size_t hdr_size = sizeof(struct ethernet_hdr);
242 460 u16 protlen;
243 461  
244 462  
... ... @@ -244,14 +462,35 @@
244 462 EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size,
245 463 buffer_size, buffer, src_addr, dest_addr, protocol);
246 464  
  465 + /* Execute events */
247 466 efi_timer_check();
248 467  
249   - if (!new_rx_packet)
250   - return EFI_EXIT(EFI_NOT_READY);
  468 + /* Check parameters */
  469 + if (!this) {
  470 + ret = EFI_INVALID_PARAMETER;
  471 + goto out;
  472 + }
  473 +
  474 + switch (this->mode->state) {
  475 + case EFI_NETWORK_STOPPED:
  476 + ret = EFI_NOT_STARTED;
  477 + goto out;
  478 + case EFI_NETWORK_STARTED:
  479 + ret = EFI_DEVICE_ERROR;
  480 + goto out;
  481 + default:
  482 + break;
  483 + }
  484 +
  485 + if (!new_rx_packet) {
  486 + ret = EFI_NOT_READY;
  487 + goto out;
  488 + }
251 489 /* Check that we at least received an Ethernet header */
252 490 if (net_rx_packet_len < sizeof(struct ethernet_hdr)) {
253 491 new_rx_packet = false;
254   - return EFI_EXIT(EFI_NOT_READY);
  492 + ret = EFI_NOT_READY;
  493 + goto out;
255 494 }
256 495 /* Fill export parameters */
257 496 eth_hdr = (struct ethernet_hdr *)net_rx_packet;
258 497  
259 498  
... ... @@ -271,16 +510,22 @@
271 510 if (*buffer_size < net_rx_packet_len) {
272 511 /* Packet doesn't fit, try again with bigger buffer */
273 512 *buffer_size = net_rx_packet_len;
274   - return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
  513 + ret = EFI_BUFFER_TOO_SMALL;
  514 + goto out;
275 515 }
276 516 /* Copy packet */
277 517 memcpy(buffer, net_rx_packet, net_rx_packet_len);
278 518 *buffer_size = net_rx_packet_len;
279 519 new_rx_packet = false;
280   -
281   - return EFI_EXIT(EFI_SUCCESS);
  520 +out:
  521 + return EFI_EXIT(ret);
282 522 }
283 523  
  524 +/**
  525 + * efi_net_set_dhcp_ack() - take note of a selected DHCP IP address
  526 + *
  527 + * This function is called by dhcp_handler().
  528 + */
284 529 void efi_net_set_dhcp_ack(void *pkt, int len)
285 530 {
286 531 int maxsize = sizeof(*dhcp_ack);
287 532  
... ... @@ -291,9 +536,23 @@
291 536 memcpy(dhcp_ack, pkt, min(len, maxsize));
292 537 }
293 538  
294   -/*
295   - * Check if a new network packet has been received.
  539 +/**
  540 + * efi_net_push() - callback for received network packet
296 541 *
  542 + * This function is called when a network packet is received by eth_rx().
  543 + *
  544 + * @pkt: network packet
  545 + * @len: length
  546 + */
  547 +static void efi_net_push(void *pkt, int len)
  548 +{
  549 + new_rx_packet = true;
  550 + wait_for_packet->is_signaled = true;
  551 +}
  552 +
  553 +/**
  554 + * efi_network_timer_notify() - check if a new network packet has been received
  555 + *
297 556 * This notification function is called in every timer cycle.
298 557 *
299 558 * @event the event for which this notification function is registered
300 559  
301 560  
302 561  
303 562  
... ... @@ -302,20 +561,34 @@
302 561 static void EFIAPI efi_network_timer_notify(struct efi_event *event,
303 562 void *context)
304 563 {
  564 + struct efi_simple_network *this = (struct efi_simple_network *)context;
  565 +
305 566 EFI_ENTRY("%p, %p", event, context);
306 567  
  568 + /*
  569 + * Some network drivers do not support calling eth_rx() before
  570 + * initialization.
  571 + */
  572 + if (!this || this->mode->state != EFI_NETWORK_INITIALIZED)
  573 + goto out;
  574 +
307 575 if (!new_rx_packet) {
308 576 push_packet = efi_net_push;
309 577 eth_rx();
310 578 push_packet = NULL;
311 579 }
  580 +out:
312 581 EFI_EXIT(EFI_SUCCESS);
313 582 }
314 583  
315   -/* This gets called from do_bootefi_exec(). */
  584 +/**
  585 + * efi_net_register() - register the simple network protocol
  586 + *
  587 + * This gets called from do_bootefi_exec().
  588 + */
316 589 efi_status_t efi_net_register(void)
317 590 {
318   - struct efi_net_obj *netobj;
  591 + struct efi_net_obj *netobj = NULL;
319 592 efi_status_t r;
320 593  
321 594 if (!eth_get_dev()) {
... ... @@ -395,7 +668,7 @@
395 668 * iPXE is running at TPL_CALLBACK most of the time. Use a higher TPL.
396 669 */
397 670 r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
398   - efi_network_timer_notify, NULL, NULL,
  671 + efi_network_timer_notify, &netobj->net, NULL,
399 672 &network_timer_event);
400 673 if (r != EFI_SUCCESS) {
401 674 printf("ERROR: Failed to register network event\n");