Coverage Report

Created: 2026-03-11 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/u-boot/lib/efi_loader/efi_http.c
Line
Count
Source
1
// 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
10
#define LOG_CATEGORY LOGC_EFI
11
12
#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
19
static const efi_guid_t efi_http_service_binding_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
20
static 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
 */
36
struct efi_http_instance {
37
  struct efi_http_protocol http;
38
  efi_handle_t handle;
39
  struct efi_service_binding_protocol *parent;
40
  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
50
static int num_instances;
51
52
/*
53
 * efi_u32_to_httpstatus() - convert u32 to status
54
 *
55
 */
56
enum 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
 */
68
static efi_status_t efi_http_send_data(void *client_buffer,
69
               efi_uintn_t *client_buffer_size,
70
               struct efi_http_instance *inst)
71
0
{
72
0
  efi_status_t ret = EFI_SUCCESS;
73
0
  ulong total_size, transfer_size;
74
0
  uchar *ptr;
75
76
  // Amount of data left;
77
0
  total_size = inst->file_size;
78
0
  transfer_size = total_size - inst->current_offset;
79
0
  debug("efi_http: sending data to client, total size %lu\n", total_size);
80
  // Amount of data the client is willing to receive
81
0
  if (transfer_size > *client_buffer_size)
82
0
    transfer_size = *client_buffer_size;
83
0
  else
84
0
    *client_buffer_size = transfer_size;
85
0
  debug("efi_http: transfer size %lu\n", transfer_size);
86
0
  if (!transfer_size) // Ok, only headers
87
0
    goto out;
88
89
0
  if (!client_buffer) {
90
0
    ret = EFI_INVALID_PARAMETER;
91
0
    goto out;
92
0
  }
93
94
  // Send data
95
0
  ptr = (uchar *)inst->http_load_addr + inst->current_offset;
96
0
  memcpy(client_buffer, ptr, transfer_size);
97
98
0
  inst->current_offset += transfer_size;
99
100
  // Whole file served, clean the buffer:
101
0
  if (inst->current_offset == inst->file_size) {
102
0
    efi_free_pool(inst->http_load_addr);
103
0
    inst->http_load_addr = NULL;
104
0
    inst->current_offset = 0;
105
0
    inst->file_size = 0;
106
0
  }
107
108
0
out:
109
0
  return ret;
110
0
}
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
 */
126
static 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
 */
149
static 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,
192
         (struct efi_ipv4_address *)&ipv4_node->local_subnet, NULL, NULL);
193
  }
194
195
  http_instance->current_offset = 0;
196
  http_instance->configured = true;
197
198
out:
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
 */
213
static 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,
247
         http_instance->headers_buffer, http_instance->parent);
248
  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
257
out_signal:
258
  efi_signal_event(token->event);
259
out:
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
 */
274
static 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
 */
295
static 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
340
out_bad_signal:
341
  token->status = EFI_ABORTED;
342
out_signal:
343
  efi_signal_event(token->event);
344
out:
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
 */
359
static 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
 */
382
static 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
412
  new_instance->parent = this;
413
  efi_add_handle(new_instance->handle);
414
  *child_handle = new_instance->handle;
415
416
install:
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
432
failure_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
 */
448
static 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
457
  if (num_instances == 0)
458
    return EFI_EXIT(EFI_NOT_FOUND);
459
460
  if (!child_handle)
461
    return EFI_EXIT(EFI_INVALID_PARAMETER);
462
463
  ret = efi_search_protocol(child_handle, &efi_http_guid, &phandler);
464
  if (ret != EFI_SUCCESS) {
465
    if (ret != EFI_INVALID_PARAMETER)
466
      ret = EFI_UNSUPPORTED;
467
    goto out;
468
  }
469
470
  ret = efi_delete_handle(child_handle);
471
  if (ret != EFI_SUCCESS)
472
    goto out;
473
474
  http_instance = phandler->protocol_interface;
475
  efi_free_pool(http_instance->http_load_addr);
476
  http_instance->http_load_addr = NULL;
477
478
  free(phandler->protocol_interface);
479
480
  num_instances--;
481
out:
482
  return EFI_EXIT(ret);
483
}
484
485
/**
486
 * efi_http_register() - register the http protocol
487
 *
488
 */
489
efi_status_t efi_http_register(const efi_handle_t handle,
490
             struct efi_service_binding_protocol *http_service_binding)
491
0
{
492
0
  efi_status_t r = EFI_SUCCESS;
493
494
0
  r = efi_add_protocol(handle, &efi_http_service_binding_guid,
495
0
           http_service_binding);
496
0
  if (r != EFI_SUCCESS)
497
0
    goto failure_to_add_protocol;
498
499
0
  http_service_binding->create_child = efi_http_service_binding_create_child;
500
0
  http_service_binding->destroy_child = efi_http_service_binding_destroy_child;
501
502
0
  return EFI_SUCCESS;
503
0
failure_to_add_protocol:
504
0
  return r;
505
0
}
506
507
enum efi_http_status_code efi_u32_to_httpstatus(u32 status)
508
0
{
509
0
  switch (status) {
510
0
  case 100: return HTTP_STATUS_100_CONTINUE;
511
0
  case 101: return HTTP_STATUS_101_SWITCHING_PROTOCOLS;
512
0
  case 200: return HTTP_STATUS_200_OK;
513
0
  case 201: return HTTP_STATUS_201_CREATED;
514
0
  case 202: return HTTP_STATUS_202_ACCEPTED;
515
0
  case 203: return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION;
516
0
  case 204: return HTTP_STATUS_204_NO_CONTENT;
517
0
  case 205: return HTTP_STATUS_205_RESET_CONTENT;
518
0
  case 206: return HTTP_STATUS_206_PARTIAL_CONTENT;
519
0
  case 300: return HTTP_STATUS_300_MULTIPLE_CHOICES;
520
0
  case 301: return HTTP_STATUS_301_MOVED_PERMANENTLY;
521
0
  case 302: return HTTP_STATUS_302_FOUND;
522
0
  case 303: return HTTP_STATUS_303_SEE_OTHER;
523
0
  case 304: return HTTP_STATUS_304_NOT_MODIFIED;
524
0
  case 305: return HTTP_STATUS_305_USE_PROXY;
525
0
  case 307: return HTTP_STATUS_307_TEMPORARY_REDIRECT;
526
0
  case 400: return HTTP_STATUS_400_BAD_REQUEST;
527
0
  case 401: return HTTP_STATUS_401_UNAUTHORIZED;
528
0
  case 402: return HTTP_STATUS_402_PAYMENT_REQUIRED;
529
0
  case 403: return HTTP_STATUS_403_FORBIDDEN;
530
0
  case 404: return HTTP_STATUS_404_NOT_FOUND;
531
0
  case 405: return HTTP_STATUS_405_METHOD_NOT_ALLOWED;
532
0
  case 406: return HTTP_STATUS_406_NOT_ACCEPTABLE;
533
0
  case 407: return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED;
534
0
  case 408: return HTTP_STATUS_408_REQUEST_TIME_OUT;
535
0
  case 409: return HTTP_STATUS_409_CONFLICT;
536
0
  case 410: return HTTP_STATUS_410_GONE;
537
0
  case 411: return HTTP_STATUS_411_LENGTH_REQUIRED;
538
0
  case 412: return HTTP_STATUS_412_PRECONDITION_FAILED;
539
0
  case 413: return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE;
540
0
  case 414: return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE;
541
0
  case 415: return HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE;
542
0
  case 416: return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED;
543
0
  case 417: return HTTP_STATUS_417_EXPECTATION_FAILED;
544
0
  case 500: return HTTP_STATUS_500_INTERNAL_SERVER_ERROR;
545
0
  case 501: return HTTP_STATUS_501_NOT_IMPLEMENTED;
546
0
  case 502: return HTTP_STATUS_502_BAD_GATEWAY;
547
0
  case 503: return HTTP_STATUS_503_SERVICE_UNAVAILABLE;
548
0
  case 504: return HTTP_STATUS_504_GATEWAY_TIME_OUT;
549
0
  case 505: return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED;
550
0
  case 308: return HTTP_STATUS_308_PERMANENT_REDIRECT;
551
0
  default: return HTTP_STATUS_UNSUPPORTED_STATUS;
552
0
  }
553
0
}