blob: 189317fe2d22bb8910d8ea578dde2097b4e04241 [file] [log] [blame]
Adriano Cordovae9b19eb2024-12-04 00:05:26 -03001// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * An HTTP driver
4 *
5 * HTTP_PROTOCOL
6 * HTTP_SERVICE_BINDING_PROTOCOL
7 * IP4_CONFIG2_PROTOCOL
8 */
9
Heinrich Schuchardt955a3212025-01-16 20:26:59 +010010#define LOG_CATEGORY LOGC_EFI
11
Adriano Cordovae9b19eb2024-12-04 00:05:26 -030012#include <charset.h>
13#include <efi_loader.h>
14#include <image.h>
15#include <malloc.h>
16#include <mapmem.h>
17#include <net.h>
18
19static const efi_guid_t efi_http_service_binding_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
20static const efi_guid_t efi_http_guid = EFI_HTTP_PROTOCOL_GUID;
21
22/**
23 * struct efi_http_instance - EFI object representing an HTTP protocol instance
24 *
25 * @http: EFI_HTTP_PROTOCOL interface
26 * @handle: handle to efi object
27 * @configured: configuration status
28 * @http_load_addr: data buffer
29 * @file_size: size of data
30 * @current_offset: offset in data buffer
31 * @status_code: HTTP status code
32 * @num_headers: number of received headers
33 * @headers: array of headers
34 * @headers_buffer: raw buffer with headers
35 */
36struct efi_http_instance {
37 struct efi_http_protocol http;
38 efi_handle_t handle;
Adriano Cordova28d67772025-03-03 11:13:17 -030039 struct efi_service_binding_protocol *parent;
Adriano Cordovae9b19eb2024-12-04 00:05:26 -030040 bool configured;
41 void *http_load_addr;
42 ulong file_size;
43 ulong current_offset;
44 u32 status_code;
45 ulong num_headers;
46 struct http_header headers[MAX_HTTP_HEADERS];
47 char headers_buffer[MAX_HTTP_HEADERS_SIZE];
48};
49
50static int num_instances;
51
52/*
53 * efi_u32_to_httpstatus() - convert u32 to status
54 *
55 */
56enum efi_http_status_code efi_u32_to_httpstatus(u32 status);
57
58/*
59 * efi_http_send_data() - sends data to client
60 *
61 *
62 * @client_buffer: client buffer to send data to
63 * @client_buffer_size: size of the client buffer
64 * @inst: HTTP instance for which to send data
65 *
66 * Return: status code
67 */
68static efi_status_t efi_http_send_data(void *client_buffer,
69 efi_uintn_t *client_buffer_size,
70 struct efi_http_instance *inst)
71{
72 efi_status_t ret = EFI_SUCCESS;
73 ulong total_size, transfer_size;
74 uchar *ptr;
75
76 // Amount of data left;
77 total_size = inst->file_size;
78 transfer_size = total_size - inst->current_offset;
79 debug("efi_http: sending data to client, total size %lu\n", total_size);
80 // Amount of data the client is willing to receive
81 if (transfer_size > *client_buffer_size)
82 transfer_size = *client_buffer_size;
83 else
84 *client_buffer_size = transfer_size;
85 debug("efi_http: transfer size %lu\n", transfer_size);
86 if (!transfer_size) // Ok, only headers
87 goto out;
88
89 if (!client_buffer) {
90 ret = EFI_INVALID_PARAMETER;
91 goto out;
92 }
93
94 // Send data
95 ptr = (uchar *)inst->http_load_addr + inst->current_offset;
96 memcpy(client_buffer, ptr, transfer_size);
97
98 inst->current_offset += transfer_size;
99
100 // Whole file served, clean the buffer:
101 if (inst->current_offset == inst->file_size) {
102 efi_free_pool(inst->http_load_addr);
103 inst->http_load_addr = NULL;
104 inst->current_offset = 0;
105 inst->file_size = 0;
106 }
107
108out:
109 return ret;
110}
111
112/* EFI_HTTP_PROTOCOL */
113
114/*
115 * efi_http_get_mode_data() - Gets the current operational status.
116 *
117 * This function implements EFI_HTTP_PROTOCOL.GetModeData().
118 * See the Unified Extensible Firmware Interface
119 * (UEFI) specification for details.
120 *
121 * @this: pointer to the protocol instance
122 * @data: pointer to the buffer for operational parameters
123 * of this HTTP instance
124 * Return: status code
125 */
126static efi_status_t EFIAPI efi_http_get_mode_data(struct efi_http_protocol *this,
127 struct efi_http_config_data *data)
128{
129 EFI_ENTRY("%p, %p", this, data);
130
131 efi_status_t ret = EFI_UNSUPPORTED;
132
133 return EFI_EXIT(ret);
134}
135
136/*
137 * efi_http_configure() - Initializes operational status for this
138 * EFI HTTP instance.
139 *
140 * This function implements EFI_HTTP_PROTOCOL.Configure().
141 * See the Unified Extensible Firmware Interface
142 * (UEFI) specification for details.
143 *
144 * @this: pointer to the protocol instance
145 * @data: pointer to the buffer for operational parameters of
146 * this HTTP instance
147 * Return: status code
148 */
149static efi_status_t EFIAPI efi_http_configure(struct efi_http_protocol *this,
150 struct efi_http_config_data *data)
151{
152 EFI_ENTRY("%p, %p", this, data);
153
154 efi_status_t ret = EFI_SUCCESS;
155 enum efi_http_version http_version;
156 struct efi_httpv4_access_point *ipv4_node;
157 struct efi_http_instance *http_instance;
158
159 if (!this) {
160 ret = EFI_INVALID_PARAMETER;
161 goto out;
162 }
163
164 http_instance = (struct efi_http_instance *)this;
165
166 if (!data) {
167 efi_free_pool(http_instance->http_load_addr);
168 http_instance->http_load_addr = NULL;
169 http_instance->current_offset = 0;
170 http_instance->configured = false;
171
172 goto out;
173 }
174
175 if (http_instance->configured) {
176 ret = EFI_ALREADY_STARTED;
177 goto out;
178 }
179
180 http_version = data->http_version;
181 ipv4_node = data->access_point.ipv4_node;
182
183 if ((http_version != HTTPVERSION10 &&
184 http_version != HTTPVERSION11) ||
185 data->is_ipv6 || !ipv4_node) { /* Only support ipv4 */
186 ret = EFI_UNSUPPORTED;
187 goto out;
188 }
189
190 if (!ipv4_node->use_default_address) {
191 efi_net_set_addr((struct efi_ipv4_address *)&ipv4_node->local_address,
Adriano Cordova54674692025-03-03 11:13:15 -0300192 (struct efi_ipv4_address *)&ipv4_node->local_subnet, NULL, NULL);
Adriano Cordovae9b19eb2024-12-04 00:05:26 -0300193 }
194
195 http_instance->current_offset = 0;
196 http_instance->configured = true;
197
198out:
199 return EFI_EXIT(ret);
200}
201
202/*
203 * efi_http_request() - Queues an HTTP request to this HTTP instance
204 *
205 * This function implements EFI_HTTP_PROTOCOL.Request().
206 * See the Unified Extensible Firmware Interface
207 * (UEFI) specification for details.
208 *
209 * @this: pointer to the protocol instance
210 * @token: pointer to storage containing HTTP request token
211 * Return: status code
212 */
213static efi_status_t EFIAPI efi_http_request(struct efi_http_protocol *this,
214 struct efi_http_token *token)
215{
216 EFI_ENTRY("%p, %p", this, token);
217
218 efi_status_t ret = EFI_SUCCESS;
219 u8 *tmp;
220 u8 url_8[1024];
221 u16 *url_16;
222 enum efi_http_method current_method;
223 struct efi_http_instance *http_instance;
224
225 if (!token || !this || !token->message ||
226 !token->message->data.request) {
227 ret = EFI_INVALID_PARAMETER;
228 goto out;
229 }
230
231 http_instance = (struct efi_http_instance *)this;
232
233 if (!http_instance->configured) {
234 ret = EFI_NOT_STARTED;
235 goto out;
236 }
237
238 current_method = token->message->data.request->method;
239 url_16 = token->message->data.request->url;
240
241 /* Parse URL. It comes in UCS-2 encoding and follows RFC3986 */
242 tmp = url_8;
243 utf16_utf8_strncpy((char **)&tmp, url_16, 1024);
244
245 ret = efi_net_do_request(url_8, current_method, &http_instance->http_load_addr,
246 &http_instance->status_code, &http_instance->file_size,
Adriano Cordova28d67772025-03-03 11:13:17 -0300247 http_instance->headers_buffer, http_instance->parent);
Adriano Cordovae9b19eb2024-12-04 00:05:26 -0300248 if (ret != EFI_SUCCESS)
249 goto out;
250
251 // We have a successful request
252 efi_net_parse_headers(&http_instance->num_headers, http_instance->headers);
253 http_instance->current_offset = 0;
254 token->status = EFI_SUCCESS;
255 goto out_signal;
256
257out_signal:
258 efi_signal_event(token->event);
259out:
260 return EFI_EXIT(ret);
261}
262
263/*
264 * efi_http_cancel() - Abort an asynchronous HTTP request or response token
265 *
266 * This function implements EFI_HTTP_PROTOCOL.Cancel().
267 * See the Unified Extensible Firmware Interface
268 * (UEFI) specification for details.
269 *
270 * @this: pointer to the protocol instance
271 * @token: pointer to storage containing HTTP request token
272 * Return: status code
273 */
274static efi_status_t EFIAPI efi_http_cancel(struct efi_http_protocol *this,
275 struct efi_http_token *token)
276{
277 EFI_ENTRY("%p, %p", this, token);
278
279 efi_status_t ret = EFI_UNSUPPORTED;
280
281 return EFI_EXIT(ret);
282}
283
284/*
285 * efi_http_response() - Queues an HTTP response to this HTTP instance
286 *
287 * This function implements EFI_HTTP_PROTOCOL.Response().
288 * See the Unified Extensible Firmware Interface
289 * (UEFI) specification for details.
290 *
291 * @this: pointer to the protocol instance
292 * @token: pointer to storage containing HTTP request token
293 * Return: status code
294 */
295static efi_status_t EFIAPI efi_http_response(struct efi_http_protocol *this,
296 struct efi_http_token *token)
297{
298 EFI_ENTRY("%p, %p", this, token);
299
300 efi_status_t ret = EFI_SUCCESS;
301 struct efi_http_instance *http_instance;
302 struct efi_http_header **client_headers;
303 struct efi_http_response_data *response;
304
305 if (!token || !this || !token->message) {
306 ret = EFI_INVALID_PARAMETER;
307 goto out;
308 }
309
310 http_instance = (struct efi_http_instance *)this;
311
312 // Set HTTP status code
313 if (token->message->data.response) { // TODO extra check, see spec.
314 response = token->message->data.response;
315 response->status_code = efi_u32_to_httpstatus(http_instance->status_code);
316 }
317
318 client_headers = &token->message->headers;
319
320 ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA,
321 (http_instance->num_headers) * sizeof(struct efi_http_header),
322 (void **)client_headers); // This is deallocated by the client.
323 if (ret != EFI_SUCCESS)
324 goto out_bad_signal;
325
326 // Send headers
327 token->message->header_count = http_instance->num_headers;
328 for (int i = 0; i < http_instance->num_headers; i++) {
329 (*client_headers)[i].field_name = http_instance->headers[i].name;
330 (*client_headers)[i].field_value = http_instance->headers[i].value;
331 }
332
333 ret = efi_http_send_data(token->message->body, &token->message->body_length, http_instance);
334 if (ret != EFI_SUCCESS)
335 goto out_bad_signal;
336
337 token->status = EFI_SUCCESS;
338 goto out_signal;
339
340out_bad_signal:
341 token->status = EFI_ABORTED;
342out_signal:
343 efi_signal_event(token->event);
344out:
345 return EFI_EXIT(ret);
346}
347
348/*
349 * efi_http_poll() - Polls for incoming data packets and processes outgoing data packets
350 *
351 * This function implements EFI_HTTP_PROTOCOL.Poll().
352 * See the Unified Extensible Firmware Interface
353 * (UEFI) specification for details.
354 *
355 * @this: pointer to the protocol instance
356 * @token: pointer to storage containing HTTP request token
357 * Return: status code
358 */
359static efi_status_t EFIAPI efi_http_poll(struct efi_http_protocol *this)
360{
361 EFI_ENTRY("%p", this);
362
363 efi_status_t ret = EFI_UNSUPPORTED;
364
365 return EFI_EXIT(ret);
366}
367
368/* EFI_HTTP_SERVICE_BINDING_PROTOCOL */
369
370/*
371 * efi_http_service_binding_create_child() - Creates a child handle
372 * and installs a protocol
373 *
374 * This function implements EFI_HTTP_SERVICE_BINDING.CreateChild().
375 * See the Unified Extensible Firmware Interface
376 * (UEFI) specification for details.
377 *
378 * @this: pointer to the protocol instance
379 * @child_handle: pointer to child handle
380 * Return: status code
381 */
382static efi_status_t EFIAPI efi_http_service_binding_create_child(
383 struct efi_service_binding_protocol *this,
384 efi_handle_t *child_handle)
385{
386 EFI_ENTRY("%p, %p", this, child_handle);
387
388 efi_status_t ret = EFI_SUCCESS;
389 struct efi_http_instance *new_instance;
390
391 if (!child_handle)
392 return EFI_EXIT(EFI_INVALID_PARAMETER);
393
394 new_instance = calloc(1, sizeof(struct efi_http_instance));
395 if (!new_instance) {
396 ret = EFI_OUT_OF_RESOURCES;
397 goto failure_to_add_protocol;
398 }
399
400 if (*child_handle) {
401 new_instance->handle = *child_handle;
402 goto install;
403 }
404
405 new_instance->handle = calloc(1, sizeof(struct efi_object));
406 if (!new_instance->handle) {
407 efi_free_pool((void *)new_instance);
408 ret = EFI_OUT_OF_RESOURCES;
409 goto failure_to_add_protocol;
410 }
411
Adriano Cordova28d67772025-03-03 11:13:17 -0300412 new_instance->parent = this;
Adriano Cordovae9b19eb2024-12-04 00:05:26 -0300413 efi_add_handle(new_instance->handle);
414 *child_handle = new_instance->handle;
415
416install:
417 ret = efi_add_protocol(new_instance->handle, &efi_http_guid,
418 &new_instance->http);
419 if (ret != EFI_SUCCESS)
420 goto failure_to_add_protocol;
421
422 new_instance->http.get_mode_data = efi_http_get_mode_data;
423 new_instance->http.configure = efi_http_configure;
424 new_instance->http.request = efi_http_request;
425 new_instance->http.cancel = efi_http_cancel;
426 new_instance->http.response = efi_http_response;
427 new_instance->http.poll = efi_http_poll;
428 ++num_instances;
429
430 return EFI_EXIT(EFI_SUCCESS);
431
432failure_to_add_protocol:
433 return EFI_EXIT(ret);
434}
435
436/*
437 * efi_http_service_binding_destroy_child() - Destroys a child handle with
438 * a protocol installed on it
439 *
440 * This function implements EFI_HTTP_SERVICE_BINDING.DestroyChild().
441 * See the Unified Extensible Firmware Interface
442 * (UEFI) specification for details.
443 *
444 * @this: pointer to the protocol instance
445 * @child_handle: child handle
446 * Return: status code
447 */
448static efi_status_t EFIAPI efi_http_service_binding_destroy_child(
449 struct efi_service_binding_protocol *this,
450 efi_handle_t child_handle)
451{
452 EFI_ENTRY("%p, %p", this, child_handle);
453 efi_status_t ret = EFI_SUCCESS;
454 struct efi_http_instance *http_instance;
455 struct efi_handler *phandler;
456 void *protocol_interface;
457
458 if (num_instances == 0)
459 return EFI_EXIT(EFI_NOT_FOUND);
460
461 if (!child_handle)
462 return EFI_EXIT(EFI_INVALID_PARAMETER);
463
464 efi_search_protocol(child_handle, &efi_http_guid, &phandler);
465
466 if (phandler)
467 protocol_interface = phandler->protocol_interface;
468
469 ret = efi_delete_handle(child_handle);
470 if (ret != EFI_SUCCESS)
471 return EFI_EXIT(ret);
472
473 http_instance = (struct efi_http_instance *)protocol_interface;
474 efi_free_pool(http_instance->http_load_addr);
475 http_instance->http_load_addr = NULL;
476
477 free(protocol_interface);
478
479 num_instances--;
480
481 return EFI_EXIT(EFI_SUCCESS);
482}
483
484/**
485 * efi_http_register() - register the http protocol
486 *
487 */
488efi_status_t efi_http_register(const efi_handle_t handle,
489 struct efi_service_binding_protocol *http_service_binding)
490{
491 efi_status_t r = EFI_SUCCESS;
492
493 r = efi_add_protocol(handle, &efi_http_service_binding_guid,
494 http_service_binding);
495 if (r != EFI_SUCCESS)
496 goto failure_to_add_protocol;
497
498 http_service_binding->create_child = efi_http_service_binding_create_child;
499 http_service_binding->destroy_child = efi_http_service_binding_destroy_child;
500
501 return EFI_SUCCESS;
502failure_to_add_protocol:
503 return r;
504}
505
506enum efi_http_status_code efi_u32_to_httpstatus(u32 status)
507{
508 switch (status) {
509 case 100: return HTTP_STATUS_100_CONTINUE;
510 case 101: return HTTP_STATUS_101_SWITCHING_PROTOCOLS;
511 case 200: return HTTP_STATUS_200_OK;
512 case 201: return HTTP_STATUS_201_CREATED;
513 case 202: return HTTP_STATUS_202_ACCEPTED;
514 case 203: return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION;
515 case 204: return HTTP_STATUS_204_NO_CONTENT;
516 case 205: return HTTP_STATUS_205_RESET_CONTENT;
517 case 206: return HTTP_STATUS_206_PARTIAL_CONTENT;
518 case 300: return HTTP_STATUS_300_MULTIPLE_CHOICES;
519 case 301: return HTTP_STATUS_301_MOVED_PERMANENTLY;
520 case 302: return HTTP_STATUS_302_FOUND;
521 case 303: return HTTP_STATUS_303_SEE_OTHER;
522 case 304: return HTTP_STATUS_304_NOT_MODIFIED;
523 case 305: return HTTP_STATUS_305_USE_PROXY;
524 case 307: return HTTP_STATUS_307_TEMPORARY_REDIRECT;
525 case 400: return HTTP_STATUS_400_BAD_REQUEST;
526 case 401: return HTTP_STATUS_401_UNAUTHORIZED;
527 case 402: return HTTP_STATUS_402_PAYMENT_REQUIRED;
528 case 403: return HTTP_STATUS_403_FORBIDDEN;
529 case 404: return HTTP_STATUS_404_NOT_FOUND;
530 case 405: return HTTP_STATUS_405_METHOD_NOT_ALLOWED;
531 case 406: return HTTP_STATUS_406_NOT_ACCEPTABLE;
532 case 407: return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED;
533 case 408: return HTTP_STATUS_408_REQUEST_TIME_OUT;
534 case 409: return HTTP_STATUS_409_CONFLICT;
535 case 410: return HTTP_STATUS_410_GONE;
536 case 411: return HTTP_STATUS_411_LENGTH_REQUIRED;
537 case 412: return HTTP_STATUS_412_PRECONDITION_FAILED;
538 case 413: return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE;
539 case 414: return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE;
540 case 415: return HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE;
541 case 416: return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED;
542 case 417: return HTTP_STATUS_417_EXPECTATION_FAILED;
543 case 500: return HTTP_STATUS_500_INTERNAL_SERVER_ERROR;
544 case 501: return HTTP_STATUS_501_NOT_IMPLEMENTED;
545 case 502: return HTTP_STATUS_502_BAD_GATEWAY;
546 case 503: return HTTP_STATUS_503_SERVICE_UNAVAILABLE;
547 case 504: return HTTP_STATUS_504_GATEWAY_TIME_OUT;
548 case 505: return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED;
549 case 308: return HTTP_STATUS_308_PERMANENT_REDIRECT;
550 default: return HTTP_STATUS_UNSUPPORTED_STATUS;
551 }
552}