Blame view

lib/efi_loader/efi_net.c 9.91 KB
0efe1bcf5   Alexander Graf   efi_loader: Add n...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  /*
   *  EFI application network access support
   *
   *  Copyright (c) 2016 Alexander Graf
   *
   *  SPDX-License-Identifier:     GPL-2.0+
   */
  
  #include <common.h>
  #include <efi_loader.h>
  #include <inttypes.h>
  #include <lcd.h>
  #include <malloc.h>
  
  DECLARE_GLOBAL_DATA_PTR;
  
  static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_GUID;
  static const efi_guid_t efi_pxe_guid = EFI_PXE_GUID;
  static struct efi_pxe_packet *dhcp_ack;
  static bool new_rx_packet;
  static void *new_tx_packet;
a0549ef60   Heinrich Schuchardt   efi_loader: use e...
22
23
24
25
26
  /*
   * The notification function of this event is called in every timer cycle
   * to check if a new network packet has been received.
   */
  static struct efi_event *network_timer_event;
e5c21603f   Heinrich Schuchardt   efi_loader: imple...
27
28
29
30
  /*
   * This event is signaled when a packet has been received.
   */
  static struct efi_event *wait_for_packet;
0efe1bcf5   Alexander Graf   efi_loader: Add n...
31
32
33
34
35
36
37
  
  struct efi_net_obj {
  	/* Generic EFI object parent class data */
  	struct efi_object parent;
  	/* EFI Interface callback struct for network */
  	struct efi_simple_network net;
  	struct efi_simple_network_mode net_mode;
0efe1bcf5   Alexander Graf   efi_loader: Add n...
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  	/* PXE struct to transmit dhcp data */
  	struct efi_pxe pxe;
  	struct efi_pxe_mode pxe_mode;
  };
  
  static efi_status_t EFIAPI efi_net_start(struct efi_simple_network *this)
  {
  	EFI_ENTRY("%p", this);
  
  	return EFI_EXIT(EFI_SUCCESS);
  }
  
  static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this)
  {
  	EFI_ENTRY("%p", this);
  
  	return EFI_EXIT(EFI_SUCCESS);
  }
  
  static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this,
  					      ulong extra_rx, ulong extra_tx)
  {
  	EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
  
  	eth_init();
  
  	return EFI_EXIT(EFI_SUCCESS);
  }
  
  static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this,
  					 int extended_verification)
  {
  	EFI_ENTRY("%p, %x", this, extended_verification);
  
  	return EFI_EXIT(EFI_SUCCESS);
  }
  
  static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this)
  {
  	EFI_ENTRY("%p", this);
  
  	return EFI_EXIT(EFI_SUCCESS);
  }
  
  static efi_status_t EFIAPI efi_net_receive_filters(
  		struct efi_simple_network *this, u32 enable, u32 disable,
  		int reset_mcast_filter, ulong mcast_filter_count,
  		struct efi_mac_address *mcast_filter)
  {
  	EFI_ENTRY("%p, %x, %x, %x, %lx, %p", this, enable, disable,
  		  reset_mcast_filter, mcast_filter_count, mcast_filter);
61da678c3   Heinrich Schuchardt   efi_net: return E...
89
  	return EFI_EXIT(EFI_UNSUPPORTED);
0efe1bcf5   Alexander Graf   efi_loader: Add n...
90
91
92
93
94
95
96
  }
  
  static efi_status_t EFIAPI efi_net_station_address(
  		struct efi_simple_network *this, int reset,
  		struct efi_mac_address *new_mac)
  {
  	EFI_ENTRY("%p, %x, %p", this, reset, new_mac);
61da678c3   Heinrich Schuchardt   efi_net: return E...
97
  	return EFI_EXIT(EFI_UNSUPPORTED);
0efe1bcf5   Alexander Graf   efi_loader: Add n...
98
99
100
101
102
103
104
  }
  
  static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this,
  					      int reset, ulong *stat_size,
  					      void *stat_table)
  {
  	EFI_ENTRY("%p, %x, %p, %p", this, reset, stat_size, stat_table);
61da678c3   Heinrich Schuchardt   efi_net: return E...
105
  	return EFI_EXIT(EFI_UNSUPPORTED);
0efe1bcf5   Alexander Graf   efi_loader: Add n...
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
  }
  
  static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this,
  						int ipv6,
  						struct efi_ip_address *ip,
  						struct efi_mac_address *mac)
  {
  	EFI_ENTRY("%p, %x, %p, %p", this, ipv6, ip, mac);
  
  	return EFI_EXIT(EFI_INVALID_PARAMETER);
  }
  
  static efi_status_t EFIAPI efi_net_nvdata(struct efi_simple_network *this,
  					  int read_write, ulong offset,
  					  ulong buffer_size, char *buffer)
  {
  	EFI_ENTRY("%p, %x, %lx, %lx, %p", this, read_write, offset, buffer_size,
  		  buffer);
61da678c3   Heinrich Schuchardt   efi_net: return E...
124
  	return EFI_EXIT(EFI_UNSUPPORTED);
0efe1bcf5   Alexander Graf   efi_loader: Add n...
125
126
127
128
129
130
  }
  
  static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
  					      u32 *int_status, void **txbuf)
  {
  	EFI_ENTRY("%p, %p, %p", this, int_status, txbuf);
891b3d905   Heinrich Schuchardt   efi_loader: fix e...
131
132
133
134
135
136
137
138
  	efi_timer_check();
  
  	if (int_status) {
  		/* We send packets synchronously, so nothing is outstanding */
  		*int_status = EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
  		if (new_rx_packet)
  			*int_status |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
  	}
0efe1bcf5   Alexander Graf   efi_loader: Add n...
139
140
141
142
143
144
145
146
147
  	if (txbuf)
  		*txbuf = new_tx_packet;
  
  	new_tx_packet = NULL;
  
  	return EFI_EXIT(EFI_SUCCESS);
  }
  
  static efi_status_t EFIAPI efi_net_transmit(struct efi_simple_network *this,
8db174d65   Heinrich Schuchardt   efi_loader: size ...
148
  		size_t header_size, size_t buffer_size, void *buffer,
0efe1bcf5   Alexander Graf   efi_loader: Add n...
149
150
151
  		struct efi_mac_address *src_addr,
  		struct efi_mac_address *dest_addr, u16 *protocol)
  {
8db174d65   Heinrich Schuchardt   efi_loader: size ...
152
153
154
  	EFI_ENTRY("%p, %lu, %lu, %p, %p, %p, %p", this,
  		  (unsigned long)header_size, (unsigned long)buffer_size,
  		  buffer, src_addr, dest_addr, protocol);
0efe1bcf5   Alexander Graf   efi_loader: Add n...
155

a0549ef60   Heinrich Schuchardt   efi_loader: use e...
156
  	efi_timer_check();
0efe1bcf5   Alexander Graf   efi_loader: Add n...
157
158
159
160
  	if (header_size) {
  		/* We would need to create the header if header_size != 0 */
  		return EFI_EXIT(EFI_INVALID_PARAMETER);
  	}
712cd2987   Alexander Graf   efi_loader: Allow...
161
162
163
164
165
  #ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
  	/* Ethernet packets always fit, just bounce */
  	memcpy(efi_bounce_buffer, buffer, buffer_size);
  	net_send_packet(efi_bounce_buffer, buffer_size);
  #else
0efe1bcf5   Alexander Graf   efi_loader: Add n...
166
  	net_send_packet(buffer, buffer_size);
712cd2987   Alexander Graf   efi_loader: Allow...
167
  #endif
0efe1bcf5   Alexander Graf   efi_loader: Add n...
168
169
170
171
172
173
174
175
  	new_tx_packet = buffer;
  
  	return EFI_EXIT(EFI_SUCCESS);
  }
  
  static void efi_net_push(void *pkt, int len)
  {
  	new_rx_packet = true;
e5c21603f   Heinrich Schuchardt   efi_loader: imple...
176
  	wait_for_packet->is_signaled = true;
0efe1bcf5   Alexander Graf   efi_loader: Add n...
177
  }
8db174d65   Heinrich Schuchardt   efi_loader: size ...
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  /*
   * Receive a packet from a network interface.
   *
   * This function implements the Receive service of the Simple Network Protocol.
   * See the UEFI spec for details.
   *
   * @this	the instance of the Simple Network Protocol
   * @header_size	size of the media header
   * @buffer_size	size of the buffer to receive the packet
   * @buffer	buffer to receive the packet
   * @src_addr	source MAC address
   * @dest_addr	destination MAC address
   * @protocol	protocol
   * @return	status code
   */
0efe1bcf5   Alexander Graf   efi_loader: Add n...
193
  static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this,
8db174d65   Heinrich Schuchardt   efi_loader: size ...
194
  		size_t *header_size, size_t *buffer_size, void *buffer,
0efe1bcf5   Alexander Graf   efi_loader: Add n...
195
196
197
  		struct efi_mac_address *src_addr,
  		struct efi_mac_address *dest_addr, u16 *protocol)
  {
336d9dfc0   Heinrich Schuchardt   efi_loader: fill ...
198
199
200
  	struct ethernet_hdr *eth_hdr;
  	size_t hdr_size = sizeof(struct ethernet_hdr);
  	u16 protlen;
0efe1bcf5   Alexander Graf   efi_loader: Add n...
201
202
  	EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size,
  		  buffer_size, buffer, src_addr, dest_addr, protocol);
a0549ef60   Heinrich Schuchardt   efi_loader: use e...
203
  	efi_timer_check();
0efe1bcf5   Alexander Graf   efi_loader: Add n...
204
205
206
  
  	if (!new_rx_packet)
  		return EFI_EXIT(EFI_NOT_READY);
336d9dfc0   Heinrich Schuchardt   efi_loader: fill ...
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  	/* Check that we at least received an Ethernet header */
  	if (net_rx_packet_len < sizeof(struct ethernet_hdr)) {
  		new_rx_packet = false;
  		return EFI_EXIT(EFI_NOT_READY);
  	}
  	/* Fill export parameters */
  	eth_hdr = (struct ethernet_hdr *)net_rx_packet;
  	protlen = ntohs(eth_hdr->et_protlen);
  	if (protlen == 0x8100) {
  		hdr_size += 4;
  		protlen = ntohs(*(u16 *)&net_rx_packet[hdr_size - 2]);
  	}
  	if (header_size)
  		*header_size = hdr_size;
  	if (dest_addr)
  		memcpy(dest_addr, eth_hdr->et_dest, ARP_HLEN);
  	if (src_addr)
  		memcpy(src_addr, eth_hdr->et_src, ARP_HLEN);
  	if (protocol)
  		*protocol = protlen;
0efe1bcf5   Alexander Graf   efi_loader: Add n...
227
228
229
230
231
  	if (*buffer_size < net_rx_packet_len) {
  		/* Packet doesn't fit, try again with bigger buf */
  		*buffer_size = net_rx_packet_len;
  		return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
  	}
336d9dfc0   Heinrich Schuchardt   efi_loader: fill ...
232
  	/* Copy packet */
0efe1bcf5   Alexander Graf   efi_loader: Add n...
233
234
235
236
237
238
  	memcpy(buffer, net_rx_packet, net_rx_packet_len);
  	*buffer_size = net_rx_packet_len;
  	new_rx_packet = false;
  
  	return EFI_EXIT(EFI_SUCCESS);
  }
0efe1bcf5   Alexander Graf   efi_loader: Add n...
239
240
241
242
243
244
245
246
247
  void efi_net_set_dhcp_ack(void *pkt, int len)
  {
  	int maxsize = sizeof(*dhcp_ack);
  
  	if (!dhcp_ack)
  		dhcp_ack = malloc(maxsize);
  
  	memcpy(dhcp_ack, pkt, min(len, maxsize));
  }
a0549ef60   Heinrich Schuchardt   efi_loader: use e...
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
  /*
   * Check if a new network packet has been received.
   *
   * This notification function is called in every timer cycle.
   *
   * @event	the event for which this notification function is registered
   * @context	event context - not used in this function
   */
  static void EFIAPI efi_network_timer_notify(struct efi_event *event,
  					    void *context)
  {
  	EFI_ENTRY("%p, %p", event, context);
  
  	if (!new_rx_packet) {
  		push_packet = efi_net_push;
  		eth_rx();
  		push_packet = NULL;
  	}
  	EFI_EXIT(EFI_SUCCESS);
  }
0efe1bcf5   Alexander Graf   efi_loader: Add n...
268
  /* This gets called from do_bootefi_exec(). */
95c5553ea   Rob Clark   efi_loader: refac...
269
  int efi_net_register(void)
0efe1bcf5   Alexander Graf   efi_loader: Add n...
270
271
  {
  	struct efi_net_obj *netobj;
a0549ef60   Heinrich Schuchardt   efi_loader: use e...
272
  	efi_status_t r;
0efe1bcf5   Alexander Graf   efi_loader: Add n...
273
274
275
276
277
278
279
280
  
  	if (!eth_get_dev()) {
  		/* No eth device active, don't expose any */
  		return 0;
  	}
  
  	/* We only expose the "active" eth device, so one is enough */
  	netobj = calloc(1, sizeof(*netobj));
84d14568c   Heinrich Schuchardt   efi_loader: efi_n...
281
282
283
284
  	if (!netobj)
  		goto out_of_memory;
  
  	/* Hook net up to the device list */
44549d62c   Heinrich Schuchardt   efi_loader: helpe...
285
  	efi_add_handle(&netobj->parent);
0efe1bcf5   Alexander Graf   efi_loader: Add n...
286
287
  
  	/* Fill in object data */
84d14568c   Heinrich Schuchardt   efi_loader: efi_n...
288
289
290
291
292
293
294
295
296
297
298
299
  	r = efi_add_protocol(netobj->parent.handle, &efi_net_guid,
  			     &netobj->net);
  	if (r != EFI_SUCCESS)
  		goto out_of_memory;
  	r = efi_add_protocol(netobj->parent.handle, &efi_guid_device_path,
  			     efi_dp_from_eth());
  	if (r != EFI_SUCCESS)
  		goto out_of_memory;
  	r = efi_add_protocol(netobj->parent.handle, &efi_pxe_guid,
  			     &netobj->pxe);
  	if (r != EFI_SUCCESS)
  		goto out_of_memory;
bdecf974f   Heinrich Schuchardt   efi_loader: fill ...
300
  	netobj->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
0efe1bcf5   Alexander Graf   efi_loader: Add n...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
  	netobj->net.start = efi_net_start;
  	netobj->net.stop = efi_net_stop;
  	netobj->net.initialize = efi_net_initialize;
  	netobj->net.reset = efi_net_reset;
  	netobj->net.shutdown = efi_net_shutdown;
  	netobj->net.receive_filters = efi_net_receive_filters;
  	netobj->net.station_address = efi_net_station_address;
  	netobj->net.statistics = efi_net_statistics;
  	netobj->net.mcastiptomac = efi_net_mcastiptomac;
  	netobj->net.nvdata = efi_net_nvdata;
  	netobj->net.get_status = efi_net_get_status;
  	netobj->net.transmit = efi_net_transmit;
  	netobj->net.receive = efi_net_receive;
  	netobj->net.mode = &netobj->net_mode;
  	netobj->net_mode.state = EFI_NETWORK_STARTED;
0efe1bcf5   Alexander Graf   efi_loader: Add n...
316
  	memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6);
5d4a5ea96   Heinrich Schuchardt   efi_loader: efi_n...
317
  	netobj->net_mode.hwaddr_size = ARP_HLEN;
0efe1bcf5   Alexander Graf   efi_loader: Add n...
318
319
320
321
322
  	netobj->net_mode.max_packet_size = PKTSIZE;
  
  	netobj->pxe.mode = &netobj->pxe_mode;
  	if (dhcp_ack)
  		netobj->pxe_mode.dhcp_ack = *dhcp_ack;
a0549ef60   Heinrich Schuchardt   efi_loader: use e...
323
  	/*
e5c21603f   Heinrich Schuchardt   efi_loader: imple...
324
325
326
327
328
329
330
331
332
333
334
335
  	 * Create WaitForPacket event.
  	 */
  	r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
  			     efi_network_timer_notify, NULL,
  			     &wait_for_packet);
  	if (r != EFI_SUCCESS) {
  		printf("ERROR: Failed to register network event
  ");
  		return r;
  	}
  	netobj->net.wait_for_packet = wait_for_packet;
  	/*
a0549ef60   Heinrich Schuchardt   efi_loader: use e...
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
  	 * Create a timer event.
  	 *
  	 * The notification function is used to check if a new network packet
  	 * has been received.
  	 */
  	r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
  			     efi_network_timer_notify, NULL,
  			     &network_timer_event);
  	if (r != EFI_SUCCESS) {
  		printf("ERROR: Failed to register network event
  ");
  		return r;
  	}
  	/* Network is time critical, create event in every timer cyle */
  	r = efi_set_timer(network_timer_event, EFI_TIMER_PERIODIC, 0);
  	if (r != EFI_SUCCESS) {
  		printf("ERROR: Failed to set network timer
  ");
  		return r;
  	}
0efe1bcf5   Alexander Graf   efi_loader: Add n...
356
  	return 0;
84d14568c   Heinrich Schuchardt   efi_loader: efi_n...
357
358
359
360
  out_of_memory:
  	printf("ERROR: Out of memory
  ");
  	return 1;
0efe1bcf5   Alexander Graf   efi_loader: Add n...
361
  }