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