Message ID | 20241111210959.560738-13-adrianox@gmail.com |
---|---|
State | Superseded, archived |
Delegated to: | Heinrich Schuchardt |
Headers | show |
Series | efi_loader: efi http and ipconfig drivers | expand |
On 11.11.24 22:09, Adriano Cordova wrote: > Add an EFI HTTP driver. This commit implements the > EFI_HTTP_PROTOCOL and the EFI_HTTP_SERVICE_BINDING_PROTOCOL. > The latter is attached to the handle of th efi network > device. This is the same handle where snp, pxe, and ipconfig > are attached to. > > Signed-off-by: Adriano Cordova <adrianox@gmail.com> > --- > > - Add documentation > - Clean up the way the buffer pointed to http_load_addr in the current > http instance was being freed. > - Remove unnecessary initialization new_instance->handle = NULL in > efi_http_service_binding_create_child. > > include/efi_loader.h | 3 + > lib/efi_loader/Kconfig | 8 + > lib/efi_loader/Makefile | 1 + > lib/efi_loader/efi_http.c | 563 ++++++++++++++++++++++++++++++++++++++ > lib/efi_loader/efi_net.c | 20 +- > 5 files changed, 593 insertions(+), 2 deletions(-) > create mode 100644 lib/efi_loader/efi_http.c > > diff --git a/include/efi_loader.h b/include/efi_loader.h > index 5029ac39f1..dee7e1a9f3 100644 > --- a/include/efi_loader.h > +++ b/include/efi_loader.h > @@ -630,6 +630,9 @@ efi_status_t efi_net_register(void); > /* Called by efi_net_register to make the ip4 config2 protocol available */ > efi_status_t efi_ipconfig_register(const efi_handle_t handle, > struct efi_ip4_config2_protocol *ip4config); > +/* Called by efi_net_register to make the http protocol available */ > +efi_status_t efi_http_register(const efi_handle_t handle, > + struct efi_service_binding_protocol *http_service_binding); > /* Called by bootefi to make the watchdog available */ > efi_status_t efi_watchdog_register(void); > efi_status_t efi_initrd_register(void); > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > index d823b2855b..6d3309932c 100644 > --- a/lib/efi_loader/Kconfig > +++ b/lib/efi_loader/Kconfig > @@ -485,6 +485,14 @@ config EFI_IP4_CONFIG2_PROTOCOL > protocol can be used to set and get the current ip address and > other network information. > > +config EFI_HTTP_PROTOCOL > + bool "EFI_HTTP_PROTOCOL support" > + default y > + depends on WGET > + help > + Provides an EFI HTTP driver implementing the EFI_HTTP_PROTOCOL. and > + EFI_HTTP_SERVICE_BINDING_PROTOCOL. > + > endmenu > > menu "Misc options" > diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile > index 30cd1de9d6..2a0b4172bd 100644 > --- a/lib/efi_loader/Makefile > +++ b/lib/efi_loader/Makefile > @@ -60,6 +60,7 @@ obj-$(CONFIG_VIDEO) += efi_gop.o > obj-$(CONFIG_BLK) += efi_disk.o > obj-$(CONFIG_NETDEVICES) += efi_net.o > obj-$(CONFIG_EFI_IP4_CONFIG2_PROTOCOL) += efi_ipconfig.o > +obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_http.o > obj-$(CONFIG_ACPI) += efi_acpi.o > obj-$(CONFIG_SMBIOS) += efi_smbios.o > obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_rng.o > diff --git a/lib/efi_loader/efi_http.c b/lib/efi_loader/efi_http.c > new file mode 100644 > index 0000000000..75b8fc40ca > --- /dev/null > +++ b/lib/efi_loader/efi_http.c > @@ -0,0 +1,563 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * An HTTP driver > + * > + * HTTP_PROTOCOL > + * HTTP_SERVICE_BINDING_PROTOCOL > + * IP4_CONFIG2_PROTOCOL > + */ > + > +#include <charset.h> > +#include <efi_loader.h> > +#include <image.h> > +#include <malloc.h> > +#include <mapmem.h> > +#include <net.h> > + > +static const efi_guid_t efi_http_service_binding_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID; > +static const efi_guid_t efi_http_guid = EFI_HTTP_PROTOCOL_GUID; > + > +/** > + * struct efi_http_instance - EFI object representing an HTTP protocol instance > + * > + * @http: EFI_HTTP_PROTOCOL interface > + * @handle: handle to efi object > + * @configured: configuration status Nits: A tab is missing after @configured:. > + * @http_load_addr: data buffer > + * @file_size: size of data > + * @current_offset: offset in data buffer > + * @status_code: HTTP status code > + * @num_headers: number of received headers > + * @headers: array of headers > + * @headers_buffer: raw buffer with headers > + */ > +struct efi_http_instance { > + struct efi_http_protocol http; > + efi_handle_t handle; > + bool configured; > + ulong http_load_addr; You are using this field to store a pointer. So the type should be void *. > + ulong file_size; > + ulong current_offset; > + u32 status_code; > + ulong num_headers; > + struct http_header headers[MAX_HTTP_HEADERS]; > + char headers_buffer[MAX_HTTP_HEADERS_SIZE]; > +}; > + > +static int num_instances; > + > +/* > + * efi_u32_to_httpstatus() - convert u32 to status > + * > + */ > +enum efi_http_status_code efi_u32_to_httpstatus(u32 status); > + > +/* > + * efi_http_send_data() - sends data to client > + * > + * > + * @client_buffer: client buffer to send data to > + * @client_buffer_size: size of the client buffer > + * @inst: HTTP instance for which to send data > + * > + * Return: status code > + */ > +static efi_status_t efi_http_send_data(void *client_buffer, > + efi_uintn_t *client_buffer_size, > + struct efi_http_instance *inst) > +{ > + efi_status_t ret = EFI_SUCCESS; > + ulong total_size, transfer_size; > + uchar *ptr; > + > + // Amount of data left; > + total_size = inst->file_size; > + transfer_size = total_size - inst->current_offset; > + debug("efi_http: sending data to client, total size %lu\n", total_size); > + // Amount of data the client is willing to receive > + if (transfer_size > *client_buffer_size) > + transfer_size = *client_buffer_size; > + else > + *client_buffer_size = transfer_size; > + debug("efi_http: transfer size %lu\n", transfer_size); > + if (!transfer_size) // Ok, only headers > + goto out; > + > + if (!client_buffer) { > + ret = EFI_INVALID_PARAMETER; > + goto out; > + } > + > + // Send data > + ptr = map_sysmem(inst->http_load_addr + inst->current_offset, transfer_size); > + memcpy(client_buffer, ptr, transfer_size); > + unmap_sysmem(ptr); > + > + inst->current_offset += transfer_size; > + > + // Whole file served, clean the buffer: > + if (inst->current_offset == inst->file_size) { > + efi_free_pool((void *)inst->http_load_addr); When you assign ptr above you assume that http_load_addr is not a pointer but a an address in the sandbox's virtual address space. http_load_addr cannot be both a pointer and a sandbox virtual address. > + inst->http_load_addr = 0; > + inst->current_offset = 0; > + inst->file_size = 0; > + } > + > +out: > + return ret; > +} > + > +/* EFI_HTTP_PROTOCOL */ > + > +/* > + * efi_http_get_mode_data() - Gets the current operational status. > + * > + * This function implements EFI_HTTP_PROTOCOL.GetModeData() Nits: Missing period at end of sentence. > + * See the Unified Extensible Firmware Interface > + * (UEFI) specification for details. > + * > + * @this: pointer to the protocol instance > + * @data: pointer to the buffer for operational parameters > + * of this HTTP instance > + * Return: status code > + */ > +static efi_status_t EFIAPI efi_http_get_mode_data(struct efi_http_protocol *this, > + struct efi_http_config_data *data) > +{ > + EFI_ENTRY("%p, %p", this, data); > + > + efi_status_t ret = EFI_UNSUPPORTED; > + > + return EFI_EXIT(ret); > +} > + > +/* > + * efi_http_configure() - Initializes operational status for this > + * EFI HTTP instance. > + * > + * This function implements EFI_HTTP_PROTOCOL.Configure() > + * See the Unified Extensible Firmware Interface > + * (UEFI) specification for details. > + * > + * @this: pointer to the protocol instance > + * @data: pointer to the buffer for operational parameters of > + * this HTTP instance > + * Return: status code > + */ > +static efi_status_t EFIAPI efi_http_configure(struct efi_http_protocol *this, > + struct efi_http_config_data *data) > +{ > + EFI_ENTRY("%p, %p", this, data); > + > + efi_status_t ret = EFI_SUCCESS; > + enum efi_http_version http_version; > + struct efi_httpv4_access_point *ipv4_node; > + struct efi_http_instance *http_instance; > + > + if (!this) { > + ret = EFI_INVALID_PARAMETER; > + goto out; > + } > + > + http_instance = (struct efi_http_instance *)this; > + > + if (!data) { > + efi_free_pool((void *)http_instance->http_load_addr); > + http_instance->http_load_addr = 0; > + http_instance->current_offset = 0; > + http_instance->configured = false; > + > + goto out; > + } > + > + if (http_instance->configured) { > + ret = EFI_ALREADY_STARTED; > + goto out; > + } > + > + http_version = data->http_version; > + ipv4_node = data->access_point.ipv4_node; > + > + if ((http_version != HTTPVERSION10 && > + http_version != HTTPVERSION11) || > + data->is_ipv6 || !ipv4_node) { /* Only support ipv4 */ > + ret = EFI_UNSUPPORTED; > + goto out; > + } > + > + if (!ipv4_node->use_default_address) { > + efi_net_set_addr((struct efi_ipv4_address *)&ipv4_node->local_address, > + (struct efi_ipv4_address *)&ipv4_node->local_subnet, NULL); > + } > + > + http_instance->current_offset = 0; > + http_instance->configured = true; > + > +out: > + return EFI_EXIT(ret); > +} > + > +/* > + * efi_http_request() - Queues an HTTP request to this HTTP instance > + * > + * This function implements EFI_HTTP_PROTOCOL.Request() > + * See the Unified Extensible Firmware Interface > + * (UEFI) specification for details. > + * > + * @this: pointer to the protocol instance > + * @token: pointer to storage containing HTTP request token > + * Return: status code > + */ > +static efi_status_t EFIAPI efi_http_request(struct efi_http_protocol *this, > + struct efi_http_token *token) > +{ > + EFI_ENTRY("%p, %p", this, token); > + > + efi_status_t ret = EFI_SUCCESS; > + u8 *tmp; > + u8 url_8[1024]; > + u16 *url_16; > + enum efi_http_method current_method; > + struct efi_http_instance *http_instance; > + > + if (!token || !this) { > + ret = EFI_INVALID_PARAMETER; > + goto out; > + } > + > + http_instance = (struct efi_http_instance *)this; > + > + if (!http_instance->configured) { > + ret = EFI_NOT_STARTED; > + goto out; > + } > + > + if (!token->message || !token->message->data.request) > + goto out_invalid; EDK II checks all parameters at the start of EfiHttpRequest() and does not modify token->status if a parameter is invalid. The instance state is checked only after the parameter validation. See NetworkPkg/HttpDxe/HttpImpl.c in https://github.com/tianocore/edk2. > + > + current_method = token->message->data.request->method; > + url_16 = token->message->data.request->url; > + > + /* Parse URL. It comes in UCS-2 encoding and follows RFC3986 */ > + tmp = url_8; > + utf16_utf8_strncpy((char **)&tmp, url_16, 1024); > + > + ret = efi_net_do_request(url_8, current_method, &http_instance->http_load_addr, > + &http_instance->status_code, &http_instance->file_size, > + http_instance->headers_buffer); > + if (ret != EFI_SUCCESS) > + goto out; > + > + // We have a new file With method HEAD we will not have received a file. > + efi_net_parse_headers(&http_instance->num_headers, http_instance->headers); > + http_instance->current_offset = 0; > + token->status = EFI_SUCCESS; > + goto out_signal; > + > +out_invalid: > + ret = EFI_INVALID_PARAMETER; > + token->status = EFI_ABORTED; If the parameters are invalid, there is no need to signal an event. > +out_signal: > + efi_signal_event(token->event); > +out: > + return EFI_EXIT(ret); > +} > + > +/* > + * efi_http_cancel() - Abort an asynchronous HTTP request or response token > + * > + * This function implements EFI_HTTP_PROTOCOL.Cancel() > + * See the Unified Extensible Firmware Interface > + * (UEFI) specification for details. > + * > + * @this: pointer to the protocol instance > + * @token: pointer to storage containing HTTP request token > + * Return: status code > + */ > +static efi_status_t EFIAPI efi_http_cancel(struct efi_http_protocol *this, > + struct efi_http_token *token) > +{ > + EFI_ENTRY("%p, %p", this, token); > + > + efi_status_t ret = EFI_UNSUPPORTED; > + > + return EFI_EXIT(ret); > +} > + > +/* > + * efi_http_response() - Queues an HTTP response to this HTTP instance > + * > + * This function implements EFI_HTTP_PROTOCOL.Response() > + * See the Unified Extensible Firmware Interface > + * (UEFI) specification for details. > + * > + * @this: pointer to the protocol instance > + * @token: pointer to storage containing HTTP request token > + * Return: status code > + */ > +static efi_status_t EFIAPI efi_http_response(struct efi_http_protocol *this, > + struct efi_http_token *token) > +{ > + EFI_ENTRY("%p, %p", this, token); > + > + efi_status_t ret = EFI_SUCCESS; > + struct efi_http_instance *http_instance; > + struct efi_http_header **client_headers; > + struct efi_http_response_data *response; > + > + if (!token) { > + ret = EFI_INVALID_PARAMETER; > + goto out; > + } > + > + if (!this || !token->message) > + goto out_invalid; Why signal an event if the parameters are invalid? I think we should just return EFI_INVALID_PARAMETER. See EfiHttpResponse() in EDK II. > + > + http_instance = (struct efi_http_instance *)this; > + > + // Set HTTP status code > + if (token->message->data.response) { // TODO extra check, see spec. > + response = token->message->data.response; > + response->status_code = efi_u32_to_httpstatus(http_instance->status_code); > + } > + client_headers = &token->message->headers; > + ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, > + (http_instance->num_headers) * sizeof(struct efi_http_header), > + (void **)client_headers); // This is deallocated by the client. > + if (ret != EFI_SUCCESS) > + goto out_bad_signal; > + > + // Send headers > + token->message->header_count = http_instance->num_headers; > + for (int i = 0; i < http_instance->num_headers; i++) { > + (*client_headers)[i].field_name = http_instance->headers[i].name; > + (*client_headers)[i].field_value = http_instance->headers[i].value; > + } > + if (ret != EFI_SUCCESS) > + goto out_bad_signal; We have already handled ret above. > + > + ret = efi_http_send_data(token->message->body, &token->message->body_length, http_instance); > + if (ret != EFI_SUCCESS) > + goto out_bad_signal; > + > + token->status = EFI_SUCCESS; > + goto out_signal; > + > +out_invalid: > + ret = EFI_INVALID_PARAMETER; > +out_bad_signal: > + token->status = EFI_ABORTED; > +out_signal: > + efi_signal_event(token->event); > +out: > + return EFI_EXIT(ret); > +} > + > +/* > + * efi_http_poll() - Polls for incoming data packets and processes outgoing data packets > + * > + * This function implements EFI_HTTP_PROTOCOL.Poll() > + * See the Unified Extensible Firmware Interface > + * (UEFI) specification for details. > + * > + * @this: pointer to the protocol instance > + * @token: pointer to storage containing HTTP request token > + * Return: status code > + */ > +static efi_status_t EFIAPI efi_http_poll(struct efi_http_protocol *this) > +{ > + EFI_ENTRY("%p", this); > + > + efi_status_t ret = EFI_UNSUPPORTED; > + > + return EFI_EXIT(ret); > +} > + > +/* EFI_HTTP_SERVICE_BINDING_PROTOCOL */ > + > +/* > + * efi_http_service_binding_create_child() - Creates a child handle > + * and installs a protocol > + * > + * This function implements EFI_HTTP_SERVICE_BINDING.CreateChild() > + * See the Unified Extensible Firmware Interface > + * (UEFI) specification for details. > + * > + * @this: pointer to the protocol instance > + * @child_handle: pointer to child handle > + * Return: status code > + */ > +static efi_status_t EFIAPI efi_http_service_binding_create_child( > + struct efi_service_binding_protocol *this, > + efi_handle_t *child_handle) > +{ > + EFI_ENTRY("%p, %p", this, child_handle); > + > + efi_status_t ret = EFI_SUCCESS; > + struct efi_http_instance *new_instance; > + > + if (!child_handle) > + return EFI_EXIT(EFI_INVALID_PARAMETER); > + > + new_instance = calloc(1, sizeof(struct efi_http_instance)); > + if (!new_instance) { > + ret = EFI_OUT_OF_RESOURCES; > + goto failure_to_add_protocol; > + } > + > + if (*child_handle) { > + new_instance->handle = *child_handle; > + goto install; > + } > + > + new_instance->handle = calloc(1, sizeof(struct efi_object)); > + if (!new_instance->handle) { > + efi_free_pool((void *)new_instance); > + ret = EFI_OUT_OF_RESOURCES; > + goto failure_to_add_protocol; > + } > + > + efi_add_handle(new_instance->handle); > + *child_handle = new_instance->handle; > + > +install: > + ret = efi_add_protocol(new_instance->handle, &efi_http_guid, > + &new_instance->http); > + if (ret != EFI_SUCCESS) > + goto failure_to_add_protocol; > + > + new_instance->http.get_mode_data = efi_http_get_mode_data; > + new_instance->http.configure = efi_http_configure; > + new_instance->http.request = efi_http_request; > + new_instance->http.cancel = efi_http_cancel; > + new_instance->http.response = efi_http_response; > + new_instance->http.poll = efi_http_poll; > + ++num_instances; > + > + return EFI_EXIT(EFI_SUCCESS); > + > +failure_to_add_protocol: > + printf("ERROR: Failure to add protocol\n"); Please, avoid writing to the console in EFI API implementations. Leave error handling to the caller. You may use EFI_PRINT() if you want debug output. > + return EFI_EXIT(ret); > +} > + > +/* > + * efi_http_service_binding_destroy_child() - Destroys a child handle with > + * a protocol installed on it > + * > + * This function implements EFI_HTTP_SERVICE_BINDING.DestroyChild() > + * See the Unified Extensible Firmware Interface > + * (UEFI) specification for details. > + * > + * @this: pointer to the protocol instance > + * @child_handle: child handle > + * Return: status code > + */ > +static efi_status_t EFIAPI efi_http_service_binding_destroy_child( > + struct efi_service_binding_protocol *this, > + efi_handle_t child_handle) > +{ > + EFI_ENTRY("%p, %p", this, child_handle); > + efi_status_t ret = EFI_SUCCESS; > + struct efi_http_instance *http_instance; > + struct efi_handler *phandler; > + void *protocol_interface; > + > + if (num_instances == 0) > + return EFI_EXIT(EFI_NOT_FOUND); > + > + if (!child_handle) > + return EFI_EXIT(EFI_INVALID_PARAMETER); > + > + efi_search_protocol(child_handle, &efi_http_guid, &phandler); > + > + if (phandler) > + protocol_interface = phandler->protocol_interface; > + > + ret = efi_delete_handle(child_handle); > + if (ret != EFI_SUCCESS) { > + printf("ERROR: Failure to remove protocol\n"); > + return EFI_EXIT(ret); > + } > + > + http_instance = (struct efi_http_instance *)protocol_interface; > + efi_free_pool((void *)http_instance->http_load_addr); > + http_instance->http_load_addr = 0; > + > + free(protocol_interface); > + > + num_instances--; > + > + return EFI_EXIT(EFI_SUCCESS); > +} > + > +/** > + * efi_http_register() - register the http protocol > + * > + */ > +efi_status_t efi_http_register(const efi_handle_t handle, > + struct efi_service_binding_protocol *http_service_binding) > +{ > + efi_status_t r = EFI_SUCCESS; > + > + r = efi_add_protocol(handle, &efi_http_service_binding_guid, > + http_service_binding); > + if (r != EFI_SUCCESS) > + goto failure_to_add_protocol; > + > + http_service_binding->create_child = efi_http_service_binding_create_child; > + http_service_binding->destroy_child = efi_http_service_binding_destroy_child; > + > + return EFI_SUCCESS; > +failure_to_add_protocol: > + printf("ERROR: Failure to add protocol\n"); Please, avoid writing to the console in EFI API implementations. You may use EFI_PRINT() if you want debug output. Best regards Heinrich > + return r; > +} > + > +enum efi_http_status_code efi_u32_to_httpstatus(u32 status) > +{ > + switch (status) { > + case 100: return HTTP_STATUS_100_CONTINUE; > + case 101: return HTTP_STATUS_101_SWITCHING_PROTOCOLS; > + case 200: return HTTP_STATUS_200_OK; > + case 201: return HTTP_STATUS_201_CREATED; > + case 202: return HTTP_STATUS_202_ACCEPTED; > + case 203: return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION; > + case 204: return HTTP_STATUS_204_NO_CONTENT; > + case 205: return HTTP_STATUS_205_RESET_CONTENT; > + case 206: return HTTP_STATUS_206_PARTIAL_CONTENT; > + case 300: return HTTP_STATUS_300_MULTIPLE_CHOICES; > + case 301: return HTTP_STATUS_301_MOVED_PERMANENTLY; > + case 302: return HTTP_STATUS_302_FOUND; > + case 303: return HTTP_STATUS_303_SEE_OTHER; > + case 304: return HTTP_STATUS_304_NOT_MODIFIED; > + case 305: return HTTP_STATUS_305_USE_PROXY; > + case 307: return HTTP_STATUS_307_TEMPORARY_REDIRECT; > + case 400: return HTTP_STATUS_400_BAD_REQUEST; > + case 401: return HTTP_STATUS_401_UNAUTHORIZED; > + case 402: return HTTP_STATUS_402_PAYMENT_REQUIRED; > + case 403: return HTTP_STATUS_403_FORBIDDEN; > + case 404: return HTTP_STATUS_404_NOT_FOUND; > + case 405: return HTTP_STATUS_405_METHOD_NOT_ALLOWED; > + case 406: return HTTP_STATUS_406_NOT_ACCEPTABLE; > + case 407: return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED; > + case 408: return HTTP_STATUS_408_REQUEST_TIME_OUT; > + case 409: return HTTP_STATUS_409_CONFLICT; > + case 410: return HTTP_STATUS_410_GONE; > + case 411: return HTTP_STATUS_411_LENGTH_REQUIRED; > + case 412: return HTTP_STATUS_412_PRECONDITION_FAILED; > + case 413: return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE; > + case 414: return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE; > + case 415: return HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE; > + case 416: return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED; > + case 417: return HTTP_STATUS_417_EXPECTATION_FAILED; > + case 500: return HTTP_STATUS_500_INTERNAL_SERVER_ERROR; > + case 501: return HTTP_STATUS_501_NOT_IMPLEMENTED; > + case 502: return HTTP_STATUS_502_BAD_GATEWAY; > + case 503: return HTTP_STATUS_503_SERVICE_UNAVAILABLE; > + case 504: return HTTP_STATUS_504_GATEWAY_TIME_OUT; > + case 505: return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED; > + case 308: return HTTP_STATUS_308_PERMANENT_REDIRECT; > + default: return HTTP_STATUS_UNSUPPORTED_STATUS; > + } > +} > diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c > index fa3b0c0007..de22c85acf 100644 > --- a/lib/efi_loader/efi_net.c > +++ b/lib/efi_loader/efi_net.c > @@ -60,6 +60,7 @@ static struct efi_event *wait_for_packet; > * @pxe: PXE base code protocol interface > * @pxe_mode: status of the PXE base code protocol > * @ip4_config2: IP4 Config2 protocol interface > + * @http_service_binding: Http service binding protocol interface > */ > struct efi_net_obj { > struct efi_object header; > @@ -70,6 +71,9 @@ struct efi_net_obj { > #ifdef CONFIG_EFI_IP4_CONFIG2_PROTOCOL > struct efi_ip4_config2_protocol ip4_config2; > #endif > +#ifdef CONFIG_EFI_HTTP_PROTOCOL > + struct efi_service_binding_protocol http_service_binding; > +#endif > }; > > /* > @@ -1003,6 +1007,19 @@ efi_status_t efi_net_register(void) > goto failure_to_add_protocol; > #endif > > +#ifdef CONFIG_EFI_HTTP_PROTOCOL > + r = efi_http_register(&netobj->header, &netobj->http_service_binding); > + if (r != EFI_SUCCESS) > + goto failure_to_add_protocol; > + /* > + * No harm on doing the following. If the PXE handle is present, the client could > + * find it and try to get its IP address from it. In here the PXE handle is present > + * but the PXE protocol is not yet implmenented, so we add this in the meantime. > + */ > + efi_net_get_addr((struct efi_ipv4_address *)&netobj->pxe_mode.station_ip, > + (struct efi_ipv4_address *)&netobj->pxe_mode.subnet_mask, NULL); > +#endif > + > return EFI_SUCCESS; > failure_to_add_protocol: > printf("ERROR: Failure to add protocol\n"); > @@ -1162,8 +1179,6 @@ static efi_status_t efi_net_set_buffer(ulong *buffer, ulong size) > if (size < SZ_64K) > size = SZ_64K; > > - efi_free_pool((void *)*buffer); > - > *buffer = (ulong)efi_alloc(size); > if (!*buffer) > ret = EFI_OUT_OF_RESOURCES; > @@ -1250,6 +1265,7 @@ efi_status_t efi_net_do_request(u8 *url, enum efi_http_method method, ulong *buf > wget_ret = wget_request((ulong)*buffer, url, &efi_wget_info); > if ((ulong)efi_wget_info.hdr_cont_len > efi_wget_info.buffer_size) { > // Try again with updated buffer size > + efi_free_pool((void *)*buffer); > ret = efi_net_set_buffer(buffer, (ulong)efi_wget_info.hdr_cont_len); > if (ret != EFI_SUCCESS) > goto out;
diff --git a/include/efi_loader.h b/include/efi_loader.h index 5029ac39f1..dee7e1a9f3 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -630,6 +630,9 @@ efi_status_t efi_net_register(void); /* Called by efi_net_register to make the ip4 config2 protocol available */ efi_status_t efi_ipconfig_register(const efi_handle_t handle, struct efi_ip4_config2_protocol *ip4config); +/* Called by efi_net_register to make the http protocol available */ +efi_status_t efi_http_register(const efi_handle_t handle, + struct efi_service_binding_protocol *http_service_binding); /* Called by bootefi to make the watchdog available */ efi_status_t efi_watchdog_register(void); efi_status_t efi_initrd_register(void); diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index d823b2855b..6d3309932c 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -485,6 +485,14 @@ config EFI_IP4_CONFIG2_PROTOCOL protocol can be used to set and get the current ip address and other network information. +config EFI_HTTP_PROTOCOL + bool "EFI_HTTP_PROTOCOL support" + default y + depends on WGET + help + Provides an EFI HTTP driver implementing the EFI_HTTP_PROTOCOL. and + EFI_HTTP_SERVICE_BINDING_PROTOCOL. + endmenu menu "Misc options" diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 30cd1de9d6..2a0b4172bd 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_VIDEO) += efi_gop.o obj-$(CONFIG_BLK) += efi_disk.o obj-$(CONFIG_NETDEVICES) += efi_net.o obj-$(CONFIG_EFI_IP4_CONFIG2_PROTOCOL) += efi_ipconfig.o +obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_http.o obj-$(CONFIG_ACPI) += efi_acpi.o obj-$(CONFIG_SMBIOS) += efi_smbios.o obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_rng.o diff --git a/lib/efi_loader/efi_http.c b/lib/efi_loader/efi_http.c new file mode 100644 index 0000000000..75b8fc40ca --- /dev/null +++ b/lib/efi_loader/efi_http.c @@ -0,0 +1,563 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * An HTTP driver + * + * HTTP_PROTOCOL + * HTTP_SERVICE_BINDING_PROTOCOL + * IP4_CONFIG2_PROTOCOL + */ + +#include <charset.h> +#include <efi_loader.h> +#include <image.h> +#include <malloc.h> +#include <mapmem.h> +#include <net.h> + +static const efi_guid_t efi_http_service_binding_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID; +static const efi_guid_t efi_http_guid = EFI_HTTP_PROTOCOL_GUID; + +/** + * struct efi_http_instance - EFI object representing an HTTP protocol instance + * + * @http: EFI_HTTP_PROTOCOL interface + * @handle: handle to efi object + * @configured: configuration status + * @http_load_addr: data buffer + * @file_size: size of data + * @current_offset: offset in data buffer + * @status_code: HTTP status code + * @num_headers: number of received headers + * @headers: array of headers + * @headers_buffer: raw buffer with headers + */ +struct efi_http_instance { + struct efi_http_protocol http; + efi_handle_t handle; + bool configured; + ulong http_load_addr; + ulong file_size; + ulong current_offset; + u32 status_code; + ulong num_headers; + struct http_header headers[MAX_HTTP_HEADERS]; + char headers_buffer[MAX_HTTP_HEADERS_SIZE]; +}; + +static int num_instances; + +/* + * efi_u32_to_httpstatus() - convert u32 to status + * + */ +enum efi_http_status_code efi_u32_to_httpstatus(u32 status); + +/* + * efi_http_send_data() - sends data to client + * + * + * @client_buffer: client buffer to send data to + * @client_buffer_size: size of the client buffer + * @inst: HTTP instance for which to send data + * + * Return: status code + */ +static efi_status_t efi_http_send_data(void *client_buffer, + efi_uintn_t *client_buffer_size, + struct efi_http_instance *inst) +{ + efi_status_t ret = EFI_SUCCESS; + ulong total_size, transfer_size; + uchar *ptr; + + // Amount of data left; + total_size = inst->file_size; + transfer_size = total_size - inst->current_offset; + debug("efi_http: sending data to client, total size %lu\n", total_size); + // Amount of data the client is willing to receive + if (transfer_size > *client_buffer_size) + transfer_size = *client_buffer_size; + else + *client_buffer_size = transfer_size; + debug("efi_http: transfer size %lu\n", transfer_size); + if (!transfer_size) // Ok, only headers + goto out; + + if (!client_buffer) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + // Send data + ptr = map_sysmem(inst->http_load_addr + inst->current_offset, transfer_size); + memcpy(client_buffer, ptr, transfer_size); + unmap_sysmem(ptr); + + inst->current_offset += transfer_size; + + // Whole file served, clean the buffer: + if (inst->current_offset == inst->file_size) { + efi_free_pool((void *)inst->http_load_addr); + inst->http_load_addr = 0; + inst->current_offset = 0; + inst->file_size = 0; + } + +out: + return ret; +} + +/* EFI_HTTP_PROTOCOL */ + +/* + * efi_http_get_mode_data() - Gets the current operational status. + * + * This function implements EFI_HTTP_PROTOCOL.GetModeData() + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @data: pointer to the buffer for operational parameters + * of this HTTP instance + * Return: status code + */ +static efi_status_t EFIAPI efi_http_get_mode_data(struct efi_http_protocol *this, + struct efi_http_config_data *data) +{ + EFI_ENTRY("%p, %p", this, data); + + efi_status_t ret = EFI_UNSUPPORTED; + + return EFI_EXIT(ret); +} + +/* + * efi_http_configure() - Initializes operational status for this + * EFI HTTP instance. + * + * This function implements EFI_HTTP_PROTOCOL.Configure() + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @data: pointer to the buffer for operational parameters of + * this HTTP instance + * Return: status code + */ +static efi_status_t EFIAPI efi_http_configure(struct efi_http_protocol *this, + struct efi_http_config_data *data) +{ + EFI_ENTRY("%p, %p", this, data); + + efi_status_t ret = EFI_SUCCESS; + enum efi_http_version http_version; + struct efi_httpv4_access_point *ipv4_node; + struct efi_http_instance *http_instance; + + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + http_instance = (struct efi_http_instance *)this; + + if (!data) { + efi_free_pool((void *)http_instance->http_load_addr); + http_instance->http_load_addr = 0; + http_instance->current_offset = 0; + http_instance->configured = false; + + goto out; + } + + if (http_instance->configured) { + ret = EFI_ALREADY_STARTED; + goto out; + } + + http_version = data->http_version; + ipv4_node = data->access_point.ipv4_node; + + if ((http_version != HTTPVERSION10 && + http_version != HTTPVERSION11) || + data->is_ipv6 || !ipv4_node) { /* Only support ipv4 */ + ret = EFI_UNSUPPORTED; + goto out; + } + + if (!ipv4_node->use_default_address) { + efi_net_set_addr((struct efi_ipv4_address *)&ipv4_node->local_address, + (struct efi_ipv4_address *)&ipv4_node->local_subnet, NULL); + } + + http_instance->current_offset = 0; + http_instance->configured = true; + +out: + return EFI_EXIT(ret); +} + +/* + * efi_http_request() - Queues an HTTP request to this HTTP instance + * + * This function implements EFI_HTTP_PROTOCOL.Request() + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @token: pointer to storage containing HTTP request token + * Return: status code + */ +static efi_status_t EFIAPI efi_http_request(struct efi_http_protocol *this, + struct efi_http_token *token) +{ + EFI_ENTRY("%p, %p", this, token); + + efi_status_t ret = EFI_SUCCESS; + u8 *tmp; + u8 url_8[1024]; + u16 *url_16; + enum efi_http_method current_method; + struct efi_http_instance *http_instance; + + if (!token || !this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + http_instance = (struct efi_http_instance *)this; + + if (!http_instance->configured) { + ret = EFI_NOT_STARTED; + goto out; + } + + if (!token->message || !token->message->data.request) + goto out_invalid; + + current_method = token->message->data.request->method; + url_16 = token->message->data.request->url; + + /* Parse URL. It comes in UCS-2 encoding and follows RFC3986 */ + tmp = url_8; + utf16_utf8_strncpy((char **)&tmp, url_16, 1024); + + ret = efi_net_do_request(url_8, current_method, &http_instance->http_load_addr, + &http_instance->status_code, &http_instance->file_size, + http_instance->headers_buffer); + if (ret != EFI_SUCCESS) + goto out; + + // We have a new file + efi_net_parse_headers(&http_instance->num_headers, http_instance->headers); + http_instance->current_offset = 0; + token->status = EFI_SUCCESS; + goto out_signal; + +out_invalid: + ret = EFI_INVALID_PARAMETER; + token->status = EFI_ABORTED; +out_signal: + efi_signal_event(token->event); +out: + return EFI_EXIT(ret); +} + +/* + * efi_http_cancel() - Abort an asynchronous HTTP request or response token + * + * This function implements EFI_HTTP_PROTOCOL.Cancel() + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @token: pointer to storage containing HTTP request token + * Return: status code + */ +static efi_status_t EFIAPI efi_http_cancel(struct efi_http_protocol *this, + struct efi_http_token *token) +{ + EFI_ENTRY("%p, %p", this, token); + + efi_status_t ret = EFI_UNSUPPORTED; + + return EFI_EXIT(ret); +} + +/* + * efi_http_response() - Queues an HTTP response to this HTTP instance + * + * This function implements EFI_HTTP_PROTOCOL.Response() + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @token: pointer to storage containing HTTP request token + * Return: status code + */ +static efi_status_t EFIAPI efi_http_response(struct efi_http_protocol *this, + struct efi_http_token *token) +{ + EFI_ENTRY("%p, %p", this, token); + + efi_status_t ret = EFI_SUCCESS; + struct efi_http_instance *http_instance; + struct efi_http_header **client_headers; + struct efi_http_response_data *response; + + if (!token) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + if (!this || !token->message) + goto out_invalid; + + http_instance = (struct efi_http_instance *)this; + + // Set HTTP status code + if (token->message->data.response) { // TODO extra check, see spec. + response = token->message->data.response; + response->status_code = efi_u32_to_httpstatus(http_instance->status_code); + } + client_headers = &token->message->headers; + ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, + (http_instance->num_headers) * sizeof(struct efi_http_header), + (void **)client_headers); // This is deallocated by the client. + if (ret != EFI_SUCCESS) + goto out_bad_signal; + + // Send headers + token->message->header_count = http_instance->num_headers; + for (int i = 0; i < http_instance->num_headers; i++) { + (*client_headers)[i].field_name = http_instance->headers[i].name; + (*client_headers)[i].field_value = http_instance->headers[i].value; + } + if (ret != EFI_SUCCESS) + goto out_bad_signal; + + ret = efi_http_send_data(token->message->body, &token->message->body_length, http_instance); + if (ret != EFI_SUCCESS) + goto out_bad_signal; + + token->status = EFI_SUCCESS; + goto out_signal; + +out_invalid: + ret = EFI_INVALID_PARAMETER; +out_bad_signal: + token->status = EFI_ABORTED; +out_signal: + efi_signal_event(token->event); +out: + return EFI_EXIT(ret); +} + +/* + * efi_http_poll() - Polls for incoming data packets and processes outgoing data packets + * + * This function implements EFI_HTTP_PROTOCOL.Poll() + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @token: pointer to storage containing HTTP request token + * Return: status code + */ +static efi_status_t EFIAPI efi_http_poll(struct efi_http_protocol *this) +{ + EFI_ENTRY("%p", this); + + efi_status_t ret = EFI_UNSUPPORTED; + + return EFI_EXIT(ret); +} + +/* EFI_HTTP_SERVICE_BINDING_PROTOCOL */ + +/* + * efi_http_service_binding_create_child() - Creates a child handle + * and installs a protocol + * + * This function implements EFI_HTTP_SERVICE_BINDING.CreateChild() + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @child_handle: pointer to child handle + * Return: status code + */ +static efi_status_t EFIAPI efi_http_service_binding_create_child( + struct efi_service_binding_protocol *this, + efi_handle_t *child_handle) +{ + EFI_ENTRY("%p, %p", this, child_handle); + + efi_status_t ret = EFI_SUCCESS; + struct efi_http_instance *new_instance; + + if (!child_handle) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + new_instance = calloc(1, sizeof(struct efi_http_instance)); + if (!new_instance) { + ret = EFI_OUT_OF_RESOURCES; + goto failure_to_add_protocol; + } + + if (*child_handle) { + new_instance->handle = *child_handle; + goto install; + } + + new_instance->handle = calloc(1, sizeof(struct efi_object)); + if (!new_instance->handle) { + efi_free_pool((void *)new_instance); + ret = EFI_OUT_OF_RESOURCES; + goto failure_to_add_protocol; + } + + efi_add_handle(new_instance->handle); + *child_handle = new_instance->handle; + +install: + ret = efi_add_protocol(new_instance->handle, &efi_http_guid, + &new_instance->http); + if (ret != EFI_SUCCESS) + goto failure_to_add_protocol; + + new_instance->http.get_mode_data = efi_http_get_mode_data; + new_instance->http.configure = efi_http_configure; + new_instance->http.request = efi_http_request; + new_instance->http.cancel = efi_http_cancel; + new_instance->http.response = efi_http_response; + new_instance->http.poll = efi_http_poll; + ++num_instances; + + return EFI_EXIT(EFI_SUCCESS); + +failure_to_add_protocol: + printf("ERROR: Failure to add protocol\n"); + return EFI_EXIT(ret); +} + +/* + * efi_http_service_binding_destroy_child() - Destroys a child handle with + * a protocol installed on it + * + * This function implements EFI_HTTP_SERVICE_BINDING.DestroyChild() + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @child_handle: child handle + * Return: status code + */ +static efi_status_t EFIAPI efi_http_service_binding_destroy_child( + struct efi_service_binding_protocol *this, + efi_handle_t child_handle) +{ + EFI_ENTRY("%p, %p", this, child_handle); + efi_status_t ret = EFI_SUCCESS; + struct efi_http_instance *http_instance; + struct efi_handler *phandler; + void *protocol_interface; + + if (num_instances == 0) + return EFI_EXIT(EFI_NOT_FOUND); + + if (!child_handle) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + efi_search_protocol(child_handle, &efi_http_guid, &phandler); + + if (phandler) + protocol_interface = phandler->protocol_interface; + + ret = efi_delete_handle(child_handle); + if (ret != EFI_SUCCESS) { + printf("ERROR: Failure to remove protocol\n"); + return EFI_EXIT(ret); + } + + http_instance = (struct efi_http_instance *)protocol_interface; + efi_free_pool((void *)http_instance->http_load_addr); + http_instance->http_load_addr = 0; + + free(protocol_interface); + + num_instances--; + + return EFI_EXIT(EFI_SUCCESS); +} + +/** + * efi_http_register() - register the http protocol + * + */ +efi_status_t efi_http_register(const efi_handle_t handle, + struct efi_service_binding_protocol *http_service_binding) +{ + efi_status_t r = EFI_SUCCESS; + + r = efi_add_protocol(handle, &efi_http_service_binding_guid, + http_service_binding); + if (r != EFI_SUCCESS) + goto failure_to_add_protocol; + + http_service_binding->create_child = efi_http_service_binding_create_child; + http_service_binding->destroy_child = efi_http_service_binding_destroy_child; + + return EFI_SUCCESS; +failure_to_add_protocol: + printf("ERROR: Failure to add protocol\n"); + return r; +} + +enum efi_http_status_code efi_u32_to_httpstatus(u32 status) +{ + switch (status) { + case 100: return HTTP_STATUS_100_CONTINUE; + case 101: return HTTP_STATUS_101_SWITCHING_PROTOCOLS; + case 200: return HTTP_STATUS_200_OK; + case 201: return HTTP_STATUS_201_CREATED; + case 202: return HTTP_STATUS_202_ACCEPTED; + case 203: return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION; + case 204: return HTTP_STATUS_204_NO_CONTENT; + case 205: return HTTP_STATUS_205_RESET_CONTENT; + case 206: return HTTP_STATUS_206_PARTIAL_CONTENT; + case 300: return HTTP_STATUS_300_MULTIPLE_CHOICES; + case 301: return HTTP_STATUS_301_MOVED_PERMANENTLY; + case 302: return HTTP_STATUS_302_FOUND; + case 303: return HTTP_STATUS_303_SEE_OTHER; + case 304: return HTTP_STATUS_304_NOT_MODIFIED; + case 305: return HTTP_STATUS_305_USE_PROXY; + case 307: return HTTP_STATUS_307_TEMPORARY_REDIRECT; + case 400: return HTTP_STATUS_400_BAD_REQUEST; + case 401: return HTTP_STATUS_401_UNAUTHORIZED; + case 402: return HTTP_STATUS_402_PAYMENT_REQUIRED; + case 403: return HTTP_STATUS_403_FORBIDDEN; + case 404: return HTTP_STATUS_404_NOT_FOUND; + case 405: return HTTP_STATUS_405_METHOD_NOT_ALLOWED; + case 406: return HTTP_STATUS_406_NOT_ACCEPTABLE; + case 407: return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED; + case 408: return HTTP_STATUS_408_REQUEST_TIME_OUT; + case 409: return HTTP_STATUS_409_CONFLICT; + case 410: return HTTP_STATUS_410_GONE; + case 411: return HTTP_STATUS_411_LENGTH_REQUIRED; + case 412: return HTTP_STATUS_412_PRECONDITION_FAILED; + case 413: return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE; + case 414: return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE; + case 415: return HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE; + case 416: return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED; + case 417: return HTTP_STATUS_417_EXPECTATION_FAILED; + case 500: return HTTP_STATUS_500_INTERNAL_SERVER_ERROR; + case 501: return HTTP_STATUS_501_NOT_IMPLEMENTED; + case 502: return HTTP_STATUS_502_BAD_GATEWAY; + case 503: return HTTP_STATUS_503_SERVICE_UNAVAILABLE; + case 504: return HTTP_STATUS_504_GATEWAY_TIME_OUT; + case 505: return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED; + case 308: return HTTP_STATUS_308_PERMANENT_REDIRECT; + default: return HTTP_STATUS_UNSUPPORTED_STATUS; + } +} diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index fa3b0c0007..de22c85acf 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -60,6 +60,7 @@ static struct efi_event *wait_for_packet; * @pxe: PXE base code protocol interface * @pxe_mode: status of the PXE base code protocol * @ip4_config2: IP4 Config2 protocol interface + * @http_service_binding: Http service binding protocol interface */ struct efi_net_obj { struct efi_object header; @@ -70,6 +71,9 @@ struct efi_net_obj { #ifdef CONFIG_EFI_IP4_CONFIG2_PROTOCOL struct efi_ip4_config2_protocol ip4_config2; #endif +#ifdef CONFIG_EFI_HTTP_PROTOCOL + struct efi_service_binding_protocol http_service_binding; +#endif }; /* @@ -1003,6 +1007,19 @@ efi_status_t efi_net_register(void) goto failure_to_add_protocol; #endif +#ifdef CONFIG_EFI_HTTP_PROTOCOL + r = efi_http_register(&netobj->header, &netobj->http_service_binding); + if (r != EFI_SUCCESS) + goto failure_to_add_protocol; + /* + * No harm on doing the following. If the PXE handle is present, the client could + * find it and try to get its IP address from it. In here the PXE handle is present + * but the PXE protocol is not yet implmenented, so we add this in the meantime. + */ + efi_net_get_addr((struct efi_ipv4_address *)&netobj->pxe_mode.station_ip, + (struct efi_ipv4_address *)&netobj->pxe_mode.subnet_mask, NULL); +#endif + return EFI_SUCCESS; failure_to_add_protocol: printf("ERROR: Failure to add protocol\n"); @@ -1162,8 +1179,6 @@ static efi_status_t efi_net_set_buffer(ulong *buffer, ulong size) if (size < SZ_64K) size = SZ_64K; - efi_free_pool((void *)*buffer); - *buffer = (ulong)efi_alloc(size); if (!*buffer) ret = EFI_OUT_OF_RESOURCES; @@ -1250,6 +1265,7 @@ efi_status_t efi_net_do_request(u8 *url, enum efi_http_method method, ulong *buf wget_ret = wget_request((ulong)*buffer, url, &efi_wget_info); if ((ulong)efi_wget_info.hdr_cont_len > efi_wget_info.buffer_size) { // Try again with updated buffer size + efi_free_pool((void *)*buffer); ret = efi_net_set_buffer(buffer, (ulong)efi_wget_info.hdr_cont_len); if (ret != EFI_SUCCESS) goto out;
Add an EFI HTTP driver. This commit implements the EFI_HTTP_PROTOCOL and the EFI_HTTP_SERVICE_BINDING_PROTOCOL. The latter is attached to the handle of th efi network device. This is the same handle where snp, pxe, and ipconfig are attached to. Signed-off-by: Adriano Cordova <adrianox@gmail.com> --- - Add documentation - Clean up the way the buffer pointed to http_load_addr in the current http instance was being freed. - Remove unnecessary initialization new_instance->handle = NULL in efi_http_service_binding_create_child. include/efi_loader.h | 3 + lib/efi_loader/Kconfig | 8 + lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_http.c | 563 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_net.c | 20 +- 5 files changed, 593 insertions(+), 2 deletions(-) create mode 100644 lib/efi_loader/efi_http.c