]>
Commit | Line | Data |
---|---|---|
f739fcd8 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
0efe1bcf AG |
2 | /* |
3 | * EFI application network access support | |
4 | * | |
5 | * Copyright (c) 2016 Alexander Graf | |
0efe1bcf AG |
6 | */ |
7 | ||
8 | #include <common.h> | |
9 | #include <efi_loader.h> | |
0efe1bcf AG |
10 | #include <malloc.h> |
11 | ||
dec88e41 | 12 | static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; |
a6d37098 HS |
13 | static const efi_guid_t efi_pxe_base_code_protocol_guid = |
14 | EFI_PXE_BASE_CODE_PROTOCOL_GUID; | |
0efe1bcf AG |
15 | static struct efi_pxe_packet *dhcp_ack; |
16 | static bool new_rx_packet; | |
17 | static void *new_tx_packet; | |
622fe621 HS |
18 | static void *transmit_buffer; |
19 | ||
a0549ef6 HS |
20 | /* |
21 | * The notification function of this event is called in every timer cycle | |
22 | * to check if a new network packet has been received. | |
23 | */ | |
24 | static struct efi_event *network_timer_event; | |
e5c21603 HS |
25 | /* |
26 | * This event is signaled when a packet has been received. | |
27 | */ | |
28 | static struct efi_event *wait_for_packet; | |
0efe1bcf | 29 | |
d39646a3 HS |
30 | /** |
31 | * struct efi_net_obj - EFI object representing a network interface | |
32 | * | |
33 | * @header: EFI object header | |
34 | * @net: simple network protocol interface | |
35 | * @net_mode: status of the network interface | |
36 | * @pxe: PXE base code protocol interface | |
37 | * @pxe_mode: status of the PXE base code protocol | |
38 | */ | |
0efe1bcf | 39 | struct efi_net_obj { |
d39646a3 | 40 | struct efi_object header; |
0efe1bcf AG |
41 | struct efi_simple_network net; |
42 | struct efi_simple_network_mode net_mode; | |
a6d37098 | 43 | struct efi_pxe_base_code_protocol pxe; |
0efe1bcf AG |
44 | struct efi_pxe_mode pxe_mode; |
45 | }; | |
46 | ||
41b05879 HS |
47 | /* |
48 | * efi_net_start() - start the network interface | |
49 | * | |
50 | * This function implements the Start service of the | |
51 | * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface | |
52 | * (UEFI) specification for details. | |
53 | * | |
54 | * @this: pointer to the protocol instance | |
55 | * Return: status code | |
56 | */ | |
0efe1bcf AG |
57 | static efi_status_t EFIAPI efi_net_start(struct efi_simple_network *this) |
58 | { | |
41b05879 HS |
59 | efi_status_t ret = EFI_SUCCESS; |
60 | ||
0efe1bcf AG |
61 | EFI_ENTRY("%p", this); |
62 | ||
41b05879 HS |
63 | /* Check parameters */ |
64 | if (!this) { | |
65 | ret = EFI_INVALID_PARAMETER; | |
66 | goto out; | |
67 | } | |
68 | ||
69 | if (this->mode->state != EFI_NETWORK_STOPPED) | |
70 | ret = EFI_ALREADY_STARTED; | |
71 | else | |
72 | this->mode->state = EFI_NETWORK_STARTED; | |
73 | out: | |
74 | return EFI_EXIT(ret); | |
0efe1bcf AG |
75 | } |
76 | ||
41b05879 HS |
77 | /* |
78 | * efi_net_stop() - stop the network interface | |
79 | * | |
80 | * This function implements the Stop service of the | |
81 | * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface | |
82 | * (UEFI) specification for details. | |
83 | * | |
84 | * @this: pointer to the protocol instance | |
85 | * Return: status code | |
86 | */ | |
0efe1bcf AG |
87 | static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this) |
88 | { | |
41b05879 HS |
89 | efi_status_t ret = EFI_SUCCESS; |
90 | ||
0efe1bcf AG |
91 | EFI_ENTRY("%p", this); |
92 | ||
41b05879 HS |
93 | /* Check parameters */ |
94 | if (!this) { | |
95 | ret = EFI_INVALID_PARAMETER; | |
96 | goto out; | |
97 | } | |
98 | ||
99 | if (this->mode->state == EFI_NETWORK_STOPPED) | |
100 | ret = EFI_NOT_STARTED; | |
101 | else | |
102 | this->mode->state = EFI_NETWORK_STOPPED; | |
103 | out: | |
104 | return EFI_EXIT(ret); | |
0efe1bcf AG |
105 | } |
106 | ||
0c5d2a3d | 107 | /* |
41b05879 | 108 | * efi_net_initialize() - initialize the network interface |
0c5d2a3d HS |
109 | * |
110 | * This function implements the Initialize service of the | |
111 | * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface | |
112 | * (UEFI) specification for details. | |
113 | * | |
114 | * @this: pointer to the protocol instance | |
115 | * @extra_rx: extra receive buffer to be allocated | |
116 | * @extra_tx: extra transmit buffer to be allocated | |
41b05879 | 117 | * Return: status code |
0c5d2a3d | 118 | */ |
0efe1bcf AG |
119 | static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this, |
120 | ulong extra_rx, ulong extra_tx) | |
121 | { | |
0c5d2a3d HS |
122 | int ret; |
123 | efi_status_t r = EFI_SUCCESS; | |
0efe1bcf | 124 | |
0c5d2a3d | 125 | EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx); |
0efe1bcf | 126 | |
41b05879 | 127 | /* Check parameters */ |
0c5d2a3d HS |
128 | if (!this) { |
129 | r = EFI_INVALID_PARAMETER; | |
41b05879 | 130 | goto out; |
0c5d2a3d HS |
131 | } |
132 | ||
133 | /* Setup packet buffers */ | |
134 | net_init(); | |
135 | /* Disable hardware and put it into the reset state */ | |
136 | eth_halt(); | |
137 | /* Set current device according to environment variables */ | |
138 | eth_set_current(); | |
139 | /* Get hardware ready for send and receive operations */ | |
140 | ret = eth_init(); | |
141 | if (ret < 0) { | |
142 | eth_halt(); | |
41b05879 | 143 | this->mode->state = EFI_NETWORK_STOPPED; |
0c5d2a3d | 144 | r = EFI_DEVICE_ERROR; |
41b05879 HS |
145 | goto out; |
146 | } else { | |
147 | this->mode->state = EFI_NETWORK_INITIALIZED; | |
0c5d2a3d | 148 | } |
41b05879 | 149 | out: |
0c5d2a3d | 150 | return EFI_EXIT(r); |
0efe1bcf AG |
151 | } |
152 | ||
41b05879 HS |
153 | /* |
154 | * efi_net_reset() - reinitialize the network interface | |
155 | * | |
156 | * This function implements the Reset service of the | |
157 | * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface | |
158 | * (UEFI) specification for details. | |
159 | * | |
160 | * @this: pointer to the protocol instance | |
161 | * @extended_verification: execute exhaustive verification | |
162 | * Return: status code | |
163 | */ | |
0efe1bcf AG |
164 | static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this, |
165 | int extended_verification) | |
166 | { | |
167 | EFI_ENTRY("%p, %x", this, extended_verification); | |
168 | ||
41b05879 | 169 | return EFI_EXIT(EFI_CALL(efi_net_initialize(this, 0, 0))); |
0efe1bcf AG |
170 | } |
171 | ||
41b05879 HS |
172 | /* |
173 | * efi_net_shutdown() - shut down the network interface | |
174 | * | |
175 | * This function implements the Shutdown service of the | |
176 | * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface | |
177 | * (UEFI) specification for details. | |
178 | * | |
179 | * @this: pointer to the protocol instance | |
180 | * Return: status code | |
181 | */ | |
0efe1bcf AG |
182 | static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this) |
183 | { | |
41b05879 HS |
184 | efi_status_t ret = EFI_SUCCESS; |
185 | ||
0efe1bcf AG |
186 | EFI_ENTRY("%p", this); |
187 | ||
41b05879 HS |
188 | /* Check parameters */ |
189 | if (!this) { | |
190 | ret = EFI_INVALID_PARAMETER; | |
191 | goto out; | |
192 | } | |
193 | ||
194 | eth_halt(); | |
195 | this->mode->state = EFI_NETWORK_STOPPED; | |
196 | ||
197 | out: | |
198 | return EFI_EXIT(ret); | |
0efe1bcf AG |
199 | } |
200 | ||
41b05879 HS |
201 | /* |
202 | * efi_net_receive_filters() - mange multicast receive filters | |
203 | * | |
204 | * This function implements the ReceiveFilters service of the | |
205 | * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface | |
206 | * (UEFI) specification for details. | |
207 | * | |
208 | * @this: pointer to the protocol instance | |
209 | * @enable: bit mask of receive filters to enable | |
210 | * @disable: bit mask of receive filters to disable | |
211 | * @reset_mcast_filter: true resets contents of the filters | |
212 | * @mcast_filter_count: number of hardware MAC addresses in the new filters list | |
213 | * @mcast_filter: list of new filters | |
214 | * Return: status code | |
215 | */ | |
216 | static efi_status_t EFIAPI efi_net_receive_filters | |
217 | (struct efi_simple_network *this, u32 enable, u32 disable, | |
218 | int reset_mcast_filter, ulong mcast_filter_count, | |
219 | struct efi_mac_address *mcast_filter) | |
0efe1bcf AG |
220 | { |
221 | EFI_ENTRY("%p, %x, %x, %x, %lx, %p", this, enable, disable, | |
222 | reset_mcast_filter, mcast_filter_count, mcast_filter); | |
223 | ||
61da678c | 224 | return EFI_EXIT(EFI_UNSUPPORTED); |
0efe1bcf AG |
225 | } |
226 | ||
41b05879 HS |
227 | /* |
228 | * efi_net_station_address() - set the hardware MAC address | |
229 | * | |
230 | * This function implements the StationAddress service of the | |
231 | * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface | |
232 | * (UEFI) specification for details. | |
233 | * | |
234 | * @this: pointer to the protocol instance | |
235 | * @reset: if true reset the address to default | |
236 | * @new_mac: new MAC address | |
237 | * Return: status code | |
238 | */ | |
239 | static efi_status_t EFIAPI efi_net_station_address | |
240 | (struct efi_simple_network *this, int reset, | |
241 | struct efi_mac_address *new_mac) | |
0efe1bcf AG |
242 | { |
243 | EFI_ENTRY("%p, %x, %p", this, reset, new_mac); | |
244 | ||
61da678c | 245 | return EFI_EXIT(EFI_UNSUPPORTED); |
0efe1bcf AG |
246 | } |
247 | ||
41b05879 HS |
248 | /* |
249 | * efi_net_statistics() - reset or collect statistics of the network interface | |
250 | * | |
251 | * This function implements the Statistics service of the | |
252 | * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface | |
253 | * (UEFI) specification for details. | |
254 | * | |
255 | * @this: pointer to the protocol instance | |
256 | * @reset: if true, the statistics are reset | |
257 | * @stat_size: size of the statistics table | |
258 | * @stat_table: table to receive the statistics | |
259 | * Return: status code | |
260 | */ | |
0efe1bcf AG |
261 | static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this, |
262 | int reset, ulong *stat_size, | |
263 | void *stat_table) | |
264 | { | |
265 | EFI_ENTRY("%p, %x, %p, %p", this, reset, stat_size, stat_table); | |
266 | ||
61da678c | 267 | return EFI_EXIT(EFI_UNSUPPORTED); |
0efe1bcf AG |
268 | } |
269 | ||
41b05879 HS |
270 | /* |
271 | * efi_net_mcastiptomac() - translate multicast IP address to MAC address | |
272 | * | |
273 | * This function implements the Statistics service of the | |
274 | * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface | |
275 | * (UEFI) specification for details. | |
276 | * | |
277 | * @this: pointer to the protocol instance | |
278 | * @ipv6: true if the IP address is an IPv6 address | |
279 | * @ip: IP address | |
280 | * @mac: MAC address | |
281 | * Return: status code | |
282 | */ | |
0efe1bcf AG |
283 | static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this, |
284 | int ipv6, | |
285 | struct efi_ip_address *ip, | |
286 | struct efi_mac_address *mac) | |
287 | { | |
288 | EFI_ENTRY("%p, %x, %p, %p", this, ipv6, ip, mac); | |
289 | ||
290 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
291 | } | |
292 | ||
41b05879 HS |
293 | /** |
294 | * efi_net_nvdata() - read or write NVRAM | |
295 | * | |
296 | * This function implements the GetStatus service of the Simple Network | |
297 | * Protocol. See the UEFI spec for details. | |
298 | * | |
299 | * @this: the instance of the Simple Network Protocol | |
300 | * @readwrite: true for read, false for write | |
301 | * @offset: offset in NVRAM | |
302 | * @buffer_size: size of buffer | |
303 | * @buffer: buffer | |
304 | * Return: status code | |
305 | */ | |
0efe1bcf AG |
306 | static efi_status_t EFIAPI efi_net_nvdata(struct efi_simple_network *this, |
307 | int read_write, ulong offset, | |
308 | ulong buffer_size, char *buffer) | |
309 | { | |
310 | EFI_ENTRY("%p, %x, %lx, %lx, %p", this, read_write, offset, buffer_size, | |
311 | buffer); | |
312 | ||
61da678c | 313 | return EFI_EXIT(EFI_UNSUPPORTED); |
0efe1bcf AG |
314 | } |
315 | ||
41b05879 HS |
316 | /** |
317 | * efi_net_get_status() - get interrupt status | |
318 | * | |
319 | * This function implements the GetStatus service of the Simple Network | |
320 | * Protocol. See the UEFI spec for details. | |
321 | * | |
322 | * @this: the instance of the Simple Network Protocol | |
323 | * @int_status: interface status | |
324 | * @txbuf: transmission buffer | |
325 | */ | |
0efe1bcf AG |
326 | static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this, |
327 | u32 *int_status, void **txbuf) | |
328 | { | |
41b05879 HS |
329 | efi_status_t ret = EFI_SUCCESS; |
330 | ||
0efe1bcf AG |
331 | EFI_ENTRY("%p, %p, %p", this, int_status, txbuf); |
332 | ||
891b3d90 HS |
333 | efi_timer_check(); |
334 | ||
41b05879 HS |
335 | /* Check parameters */ |
336 | if (!this) { | |
337 | ret = EFI_INVALID_PARAMETER; | |
338 | goto out; | |
339 | } | |
340 | ||
341 | switch (this->mode->state) { | |
342 | case EFI_NETWORK_STOPPED: | |
343 | ret = EFI_NOT_STARTED; | |
344 | goto out; | |
345 | case EFI_NETWORK_STARTED: | |
346 | ret = EFI_DEVICE_ERROR; | |
347 | goto out; | |
348 | default: | |
349 | break; | |
350 | } | |
351 | ||
891b3d90 HS |
352 | if (int_status) { |
353 | /* We send packets synchronously, so nothing is outstanding */ | |
354 | *int_status = EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; | |
355 | if (new_rx_packet) | |
356 | *int_status |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; | |
357 | } | |
0efe1bcf AG |
358 | if (txbuf) |
359 | *txbuf = new_tx_packet; | |
360 | ||
361 | new_tx_packet = NULL; | |
41b05879 HS |
362 | out: |
363 | return EFI_EXIT(ret); | |
0efe1bcf AG |
364 | } |
365 | ||
41b05879 HS |
366 | /** |
367 | * efi_net_transmit() - transmit a packet | |
368 | * | |
369 | * This function implements the Transmit service of the Simple Network Protocol. | |
370 | * See the UEFI spec for details. | |
371 | * | |
372 | * @this: the instance of the Simple Network Protocol | |
373 | * @header_size: size of the media header | |
374 | * @buffer_size: size of the buffer to receive the packet | |
375 | * @buffer: buffer to receive the packet | |
376 | * @src_addr: source hardware MAC address | |
377 | * @dest_addr: destination hardware MAC address | |
378 | * @protocol: type of header to build | |
379 | * Return: status code | |
380 | */ | |
381 | static efi_status_t EFIAPI efi_net_transmit | |
382 | (struct efi_simple_network *this, size_t header_size, | |
383 | size_t buffer_size, void *buffer, | |
384 | struct efi_mac_address *src_addr, | |
385 | struct efi_mac_address *dest_addr, u16 *protocol) | |
0efe1bcf | 386 | { |
41b05879 HS |
387 | efi_status_t ret = EFI_SUCCESS; |
388 | ||
8db174d6 HS |
389 | EFI_ENTRY("%p, %lu, %lu, %p, %p, %p, %p", this, |
390 | (unsigned long)header_size, (unsigned long)buffer_size, | |
391 | buffer, src_addr, dest_addr, protocol); | |
0efe1bcf | 392 | |
a0549ef6 HS |
393 | efi_timer_check(); |
394 | ||
41b05879 | 395 | /* Check parameters */ |
ce54fdc4 | 396 | if (!this || !buffer) { |
41b05879 HS |
397 | ret = EFI_INVALID_PARAMETER; |
398 | goto out; | |
399 | } | |
400 | ||
401 | /* We do not support jumbo packets */ | |
402 | if (buffer_size > PKTSIZE_ALIGN) { | |
403 | ret = EFI_INVALID_PARAMETER; | |
404 | goto out; | |
405 | } | |
406 | ||
0efe1bcf | 407 | if (header_size) { |
41b05879 HS |
408 | /* |
409 | * TODO: We would need to create the header | |
410 | * if header_size != 0 | |
411 | */ | |
ce54fdc4 | 412 | ret = EFI_UNSUPPORTED; |
41b05879 HS |
413 | goto out; |
414 | } | |
415 | ||
416 | switch (this->mode->state) { | |
417 | case EFI_NETWORK_STOPPED: | |
418 | ret = EFI_NOT_STARTED; | |
419 | goto out; | |
420 | case EFI_NETWORK_STARTED: | |
421 | ret = EFI_DEVICE_ERROR; | |
422 | goto out; | |
423 | default: | |
424 | break; | |
0efe1bcf AG |
425 | } |
426 | ||
41b05879 | 427 | /* Ethernet packets always fit, just bounce */ |
622fe621 HS |
428 | memcpy(transmit_buffer, buffer, buffer_size); |
429 | net_send_packet(transmit_buffer, buffer_size); | |
712cd298 | 430 | |
0efe1bcf AG |
431 | new_tx_packet = buffer; |
432 | ||
41b05879 HS |
433 | out: |
434 | return EFI_EXIT(ret); | |
0efe1bcf AG |
435 | } |
436 | ||
41b05879 HS |
437 | /** |
438 | * efi_net_receive() - receive a packet from a network interface | |
8db174d6 HS |
439 | * |
440 | * This function implements the Receive service of the Simple Network Protocol. | |
441 | * See the UEFI spec for details. | |
442 | * | |
41b05879 HS |
443 | * @this: the instance of the Simple Network Protocol |
444 | * @header_size: size of the media header | |
445 | * @buffer_size: size of the buffer to receive the packet | |
446 | * @buffer: buffer to receive the packet | |
447 | * @src_addr: source MAC address | |
448 | * @dest_addr: destination MAC address | |
449 | * @protocol: protocol | |
450 | * Return: status code | |
8db174d6 | 451 | */ |
41b05879 HS |
452 | static efi_status_t EFIAPI efi_net_receive |
453 | (struct efi_simple_network *this, size_t *header_size, | |
454 | size_t *buffer_size, void *buffer, | |
455 | struct efi_mac_address *src_addr, | |
456 | struct efi_mac_address *dest_addr, u16 *protocol) | |
0efe1bcf | 457 | { |
41b05879 | 458 | efi_status_t ret = EFI_SUCCESS; |
336d9dfc HS |
459 | struct ethernet_hdr *eth_hdr; |
460 | size_t hdr_size = sizeof(struct ethernet_hdr); | |
461 | u16 protlen; | |
462 | ||
0efe1bcf AG |
463 | EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size, |
464 | buffer_size, buffer, src_addr, dest_addr, protocol); | |
465 | ||
41b05879 | 466 | /* Execute events */ |
a0549ef6 | 467 | efi_timer_check(); |
0efe1bcf | 468 | |
41b05879 | 469 | /* Check parameters */ |
ce54fdc4 | 470 | if (!this || !buffer || !buffer_size) { |
41b05879 HS |
471 | ret = EFI_INVALID_PARAMETER; |
472 | goto out; | |
473 | } | |
474 | ||
475 | switch (this->mode->state) { | |
476 | case EFI_NETWORK_STOPPED: | |
477 | ret = EFI_NOT_STARTED; | |
478 | goto out; | |
479 | case EFI_NETWORK_STARTED: | |
480 | ret = EFI_DEVICE_ERROR; | |
481 | goto out; | |
482 | default: | |
483 | break; | |
484 | } | |
485 | ||
486 | if (!new_rx_packet) { | |
487 | ret = EFI_NOT_READY; | |
488 | goto out; | |
489 | } | |
336d9dfc HS |
490 | /* Check that we at least received an Ethernet header */ |
491 | if (net_rx_packet_len < sizeof(struct ethernet_hdr)) { | |
492 | new_rx_packet = false; | |
41b05879 HS |
493 | ret = EFI_NOT_READY; |
494 | goto out; | |
336d9dfc HS |
495 | } |
496 | /* Fill export parameters */ | |
497 | eth_hdr = (struct ethernet_hdr *)net_rx_packet; | |
498 | protlen = ntohs(eth_hdr->et_protlen); | |
499 | if (protlen == 0x8100) { | |
500 | hdr_size += 4; | |
501 | protlen = ntohs(*(u16 *)&net_rx_packet[hdr_size - 2]); | |
502 | } | |
503 | if (header_size) | |
504 | *header_size = hdr_size; | |
505 | if (dest_addr) | |
506 | memcpy(dest_addr, eth_hdr->et_dest, ARP_HLEN); | |
507 | if (src_addr) | |
508 | memcpy(src_addr, eth_hdr->et_src, ARP_HLEN); | |
509 | if (protocol) | |
510 | *protocol = protlen; | |
0efe1bcf | 511 | if (*buffer_size < net_rx_packet_len) { |
e1fec152 | 512 | /* Packet doesn't fit, try again with bigger buffer */ |
0efe1bcf | 513 | *buffer_size = net_rx_packet_len; |
41b05879 HS |
514 | ret = EFI_BUFFER_TOO_SMALL; |
515 | goto out; | |
0efe1bcf | 516 | } |
336d9dfc | 517 | /* Copy packet */ |
0efe1bcf AG |
518 | memcpy(buffer, net_rx_packet, net_rx_packet_len); |
519 | *buffer_size = net_rx_packet_len; | |
520 | new_rx_packet = false; | |
41b05879 HS |
521 | out: |
522 | return EFI_EXIT(ret); | |
0efe1bcf AG |
523 | } |
524 | ||
41b05879 HS |
525 | /** |
526 | * efi_net_set_dhcp_ack() - take note of a selected DHCP IP address | |
527 | * | |
528 | * This function is called by dhcp_handler(). | |
529 | */ | |
0efe1bcf AG |
530 | void efi_net_set_dhcp_ack(void *pkt, int len) |
531 | { | |
532 | int maxsize = sizeof(*dhcp_ack); | |
533 | ||
534 | if (!dhcp_ack) | |
535 | dhcp_ack = malloc(maxsize); | |
536 | ||
537 | memcpy(dhcp_ack, pkt, min(len, maxsize)); | |
538 | } | |
539 | ||
41b05879 HS |
540 | /** |
541 | * efi_net_push() - callback for received network packet | |
542 | * | |
543 | * This function is called when a network packet is received by eth_rx(). | |
544 | * | |
545 | * @pkt: network packet | |
546 | * @len: length | |
547 | */ | |
548 | static void efi_net_push(void *pkt, int len) | |
549 | { | |
550 | new_rx_packet = true; | |
551 | wait_for_packet->is_signaled = true; | |
552 | } | |
553 | ||
554 | /** | |
555 | * efi_network_timer_notify() - check if a new network packet has been received | |
a0549ef6 HS |
556 | * |
557 | * This notification function is called in every timer cycle. | |
558 | * | |
559 | * @event the event for which this notification function is registered | |
560 | * @context event context - not used in this function | |
561 | */ | |
562 | static void EFIAPI efi_network_timer_notify(struct efi_event *event, | |
563 | void *context) | |
564 | { | |
41b05879 HS |
565 | struct efi_simple_network *this = (struct efi_simple_network *)context; |
566 | ||
a0549ef6 HS |
567 | EFI_ENTRY("%p, %p", event, context); |
568 | ||
41b05879 HS |
569 | /* |
570 | * Some network drivers do not support calling eth_rx() before | |
571 | * initialization. | |
572 | */ | |
573 | if (!this || this->mode->state != EFI_NETWORK_INITIALIZED) | |
574 | goto out; | |
575 | ||
a0549ef6 HS |
576 | if (!new_rx_packet) { |
577 | push_packet = efi_net_push; | |
578 | eth_rx(); | |
579 | push_packet = NULL; | |
580 | } | |
41b05879 | 581 | out: |
a0549ef6 HS |
582 | EFI_EXIT(EFI_SUCCESS); |
583 | } | |
584 | ||
a6d37098 HS |
585 | static efi_status_t EFIAPI efi_pxe_base_code_start( |
586 | struct efi_pxe_base_code_protocol *this, | |
587 | u8 use_ipv6) | |
588 | { | |
589 | return EFI_UNSUPPORTED; | |
590 | } | |
591 | ||
592 | static efi_status_t EFIAPI efi_pxe_base_code_stop( | |
593 | struct efi_pxe_base_code_protocol *this) | |
594 | { | |
595 | return EFI_UNSUPPORTED; | |
596 | } | |
597 | ||
598 | static efi_status_t EFIAPI efi_pxe_base_code_dhcp( | |
599 | struct efi_pxe_base_code_protocol *this, | |
600 | u8 sort_offers) | |
601 | { | |
602 | return EFI_UNSUPPORTED; | |
603 | } | |
604 | ||
605 | static efi_status_t EFIAPI efi_pxe_base_code_discover( | |
606 | struct efi_pxe_base_code_protocol *this, | |
607 | u16 type, u16 *layer, u8 bis, | |
608 | struct efi_pxe_base_code_discover_info *info) | |
609 | { | |
610 | return EFI_UNSUPPORTED; | |
611 | } | |
612 | ||
613 | static efi_status_t EFIAPI efi_pxe_base_code_mtftp( | |
614 | struct efi_pxe_base_code_protocol *this, | |
615 | u32 operation, void *buffer_ptr, | |
616 | u8 overwrite, efi_uintn_t *buffer_size, | |
617 | struct efi_ip_address server_ip, char *filename, | |
618 | struct efi_pxe_base_code_mtftp_info *info, | |
619 | u8 dont_use_buffer) | |
620 | { | |
621 | return EFI_UNSUPPORTED; | |
622 | } | |
623 | ||
624 | static efi_status_t EFIAPI efi_pxe_base_code_udp_write( | |
625 | struct efi_pxe_base_code_protocol *this, | |
626 | u16 op_flags, struct efi_ip_address *dest_ip, | |
627 | u16 *dest_port, | |
628 | struct efi_ip_address *gateway_ip, | |
629 | struct efi_ip_address *src_ip, u16 *src_port, | |
630 | efi_uintn_t *header_size, void *header_ptr, | |
631 | efi_uintn_t *buffer_size, void *buffer_ptr) | |
632 | { | |
633 | return EFI_UNSUPPORTED; | |
634 | } | |
635 | ||
636 | static efi_status_t EFIAPI efi_pxe_base_code_udp_read( | |
637 | struct efi_pxe_base_code_protocol *this, | |
638 | u16 op_flags, struct efi_ip_address *dest_ip, | |
639 | u16 *dest_port, struct efi_ip_address *src_ip, | |
640 | u16 *src_port, efi_uintn_t *header_size, | |
641 | void *header_ptr, efi_uintn_t *buffer_size, | |
642 | void *buffer_ptr) | |
643 | { | |
644 | return EFI_UNSUPPORTED; | |
645 | } | |
646 | ||
647 | static efi_status_t EFIAPI efi_pxe_base_code_set_ip_filter( | |
648 | struct efi_pxe_base_code_protocol *this, | |
649 | struct efi_pxe_base_code_filter *new_filter) | |
650 | { | |
651 | return EFI_UNSUPPORTED; | |
652 | } | |
653 | ||
654 | static efi_status_t EFIAPI efi_pxe_base_code_arp( | |
655 | struct efi_pxe_base_code_protocol *this, | |
656 | struct efi_ip_address *ip_addr, | |
657 | struct efi_mac_address *mac_addr) | |
658 | { | |
659 | return EFI_UNSUPPORTED; | |
660 | } | |
661 | ||
662 | static efi_status_t EFIAPI efi_pxe_base_code_set_parameters( | |
663 | struct efi_pxe_base_code_protocol *this, | |
664 | u8 *new_auto_arp, u8 *new_send_guid, | |
665 | u8 *new_ttl, u8 *new_tos, | |
666 | u8 *new_make_callback) | |
667 | { | |
668 | return EFI_UNSUPPORTED; | |
669 | } | |
670 | ||
671 | static efi_status_t EFIAPI efi_pxe_base_code_set_station_ip( | |
672 | struct efi_pxe_base_code_protocol *this, | |
673 | struct efi_ip_address *new_station_ip, | |
674 | struct efi_ip_address *new_subnet_mask) | |
675 | { | |
676 | return EFI_UNSUPPORTED; | |
677 | } | |
678 | ||
679 | static efi_status_t EFIAPI efi_pxe_base_code_set_packets( | |
680 | struct efi_pxe_base_code_protocol *this, | |
681 | u8 *new_dhcp_discover_valid, | |
682 | u8 *new_dhcp_ack_received, | |
683 | u8 *new_proxy_offer_received, | |
684 | u8 *new_pxe_discover_valid, | |
685 | u8 *new_pxe_reply_received, | |
686 | u8 *new_pxe_bis_reply_received, | |
687 | EFI_PXE_BASE_CODE_PACKET *new_dchp_discover, | |
688 | EFI_PXE_BASE_CODE_PACKET *new_dhcp_acc, | |
689 | EFI_PXE_BASE_CODE_PACKET *new_proxy_offer, | |
690 | EFI_PXE_BASE_CODE_PACKET *new_pxe_discover, | |
691 | EFI_PXE_BASE_CODE_PACKET *new_pxe_reply, | |
692 | EFI_PXE_BASE_CODE_PACKET *new_pxe_bis_reply) | |
693 | { | |
694 | return EFI_UNSUPPORTED; | |
695 | } | |
696 | ||
41b05879 HS |
697 | /** |
698 | * efi_net_register() - register the simple network protocol | |
699 | * | |
700 | * This gets called from do_bootefi_exec(). | |
701 | */ | |
075d425d | 702 | efi_status_t efi_net_register(void) |
0efe1bcf | 703 | { |
41b05879 | 704 | struct efi_net_obj *netobj = NULL; |
a0549ef6 | 705 | efi_status_t r; |
0efe1bcf AG |
706 | |
707 | if (!eth_get_dev()) { | |
e1fec152 | 708 | /* No network device active, don't expose any */ |
075d425d | 709 | return EFI_SUCCESS; |
0efe1bcf AG |
710 | } |
711 | ||
e1fec152 | 712 | /* We only expose the "active" network device, so one is enough */ |
0efe1bcf | 713 | netobj = calloc(1, sizeof(*netobj)); |
622fe621 HS |
714 | if (!netobj) |
715 | goto out_of_resources; | |
716 | ||
717 | /* Allocate an aligned transmit buffer */ | |
718 | transmit_buffer = calloc(1, PKTSIZE_ALIGN + PKTALIGN); | |
719 | if (!transmit_buffer) | |
720 | goto out_of_resources; | |
721 | transmit_buffer = (void *)ALIGN((uintptr_t)transmit_buffer, PKTALIGN); | |
84d14568 HS |
722 | |
723 | /* Hook net up to the device list */ | |
d39646a3 | 724 | efi_add_handle(&netobj->header); |
0efe1bcf AG |
725 | |
726 | /* Fill in object data */ | |
d39646a3 | 727 | r = efi_add_protocol(&netobj->header, &efi_net_guid, |
84d14568 HS |
728 | &netobj->net); |
729 | if (r != EFI_SUCCESS) | |
075d425d | 730 | goto failure_to_add_protocol; |
d39646a3 | 731 | r = efi_add_protocol(&netobj->header, &efi_guid_device_path, |
84d14568 HS |
732 | efi_dp_from_eth()); |
733 | if (r != EFI_SUCCESS) | |
075d425d | 734 | goto failure_to_add_protocol; |
a6d37098 | 735 | r = efi_add_protocol(&netobj->header, &efi_pxe_base_code_protocol_guid, |
84d14568 HS |
736 | &netobj->pxe); |
737 | if (r != EFI_SUCCESS) | |
075d425d | 738 | goto failure_to_add_protocol; |
bdecf974 | 739 | netobj->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION; |
0efe1bcf AG |
740 | netobj->net.start = efi_net_start; |
741 | netobj->net.stop = efi_net_stop; | |
742 | netobj->net.initialize = efi_net_initialize; | |
743 | netobj->net.reset = efi_net_reset; | |
744 | netobj->net.shutdown = efi_net_shutdown; | |
745 | netobj->net.receive_filters = efi_net_receive_filters; | |
746 | netobj->net.station_address = efi_net_station_address; | |
747 | netobj->net.statistics = efi_net_statistics; | |
748 | netobj->net.mcastiptomac = efi_net_mcastiptomac; | |
749 | netobj->net.nvdata = efi_net_nvdata; | |
750 | netobj->net.get_status = efi_net_get_status; | |
751 | netobj->net.transmit = efi_net_transmit; | |
752 | netobj->net.receive = efi_net_receive; | |
753 | netobj->net.mode = &netobj->net_mode; | |
754 | netobj->net_mode.state = EFI_NETWORK_STARTED; | |
0efe1bcf | 755 | memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6); |
5d4a5ea9 | 756 | netobj->net_mode.hwaddr_size = ARP_HLEN; |
0efe1bcf | 757 | netobj->net_mode.max_packet_size = PKTSIZE; |
f25ddca1 | 758 | netobj->net_mode.if_type = ARP_ETHER; |
0efe1bcf | 759 | |
a6d37098 HS |
760 | netobj->pxe.revision = EFI_PXE_BASE_CODE_PROTOCOL_REVISION; |
761 | netobj->pxe.start = efi_pxe_base_code_start; | |
762 | netobj->pxe.stop = efi_pxe_base_code_stop; | |
763 | netobj->pxe.dhcp = efi_pxe_base_code_dhcp; | |
764 | netobj->pxe.discover = efi_pxe_base_code_discover; | |
765 | netobj->pxe.mtftp = efi_pxe_base_code_mtftp; | |
766 | netobj->pxe.udp_write = efi_pxe_base_code_udp_write; | |
767 | netobj->pxe.udp_read = efi_pxe_base_code_udp_read; | |
768 | netobj->pxe.set_ip_filter = efi_pxe_base_code_set_ip_filter; | |
769 | netobj->pxe.arp = efi_pxe_base_code_arp; | |
770 | netobj->pxe.set_parameters = efi_pxe_base_code_set_parameters; | |
771 | netobj->pxe.set_station_ip = efi_pxe_base_code_set_station_ip; | |
772 | netobj->pxe.set_packets = efi_pxe_base_code_set_packets; | |
0efe1bcf AG |
773 | netobj->pxe.mode = &netobj->pxe_mode; |
774 | if (dhcp_ack) | |
775 | netobj->pxe_mode.dhcp_ack = *dhcp_ack; | |
776 | ||
e5c21603 HS |
777 | /* |
778 | * Create WaitForPacket event. | |
779 | */ | |
780 | r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, | |
b095f3c8 | 781 | efi_network_timer_notify, NULL, NULL, |
e5c21603 HS |
782 | &wait_for_packet); |
783 | if (r != EFI_SUCCESS) { | |
784 | printf("ERROR: Failed to register network event\n"); | |
785 | return r; | |
786 | } | |
787 | netobj->net.wait_for_packet = wait_for_packet; | |
a0549ef6 HS |
788 | /* |
789 | * Create a timer event. | |
790 | * | |
791 | * The notification function is used to check if a new network packet | |
792 | * has been received. | |
ee3db4fc HS |
793 | * |
794 | * iPXE is running at TPL_CALLBACK most of the time. Use a higher TPL. | |
a0549ef6 | 795 | */ |
ee3db4fc | 796 | r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY, |
41b05879 | 797 | efi_network_timer_notify, &netobj->net, NULL, |
a0549ef6 HS |
798 | &network_timer_event); |
799 | if (r != EFI_SUCCESS) { | |
800 | printf("ERROR: Failed to register network event\n"); | |
801 | return r; | |
802 | } | |
e1fec152 | 803 | /* Network is time critical, create event in every timer cycle */ |
a0549ef6 HS |
804 | r = efi_set_timer(network_timer_event, EFI_TIMER_PERIODIC, 0); |
805 | if (r != EFI_SUCCESS) { | |
806 | printf("ERROR: Failed to set network timer\n"); | |
807 | return r; | |
808 | } | |
809 | ||
075d425d HS |
810 | return EFI_SUCCESS; |
811 | failure_to_add_protocol: | |
812 | printf("ERROR: Failure to add protocol\n"); | |
813 | return r; | |
622fe621 HS |
814 | out_of_resources: |
815 | free(netobj); | |
816 | /* free(transmit_buffer) not needed yet */ | |
817 | printf("ERROR: Out of memory\n"); | |
818 | return EFI_OUT_OF_RESOURCES; | |
0efe1bcf | 819 | } |