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