mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-31 12:08:19 +00:00 
			
		
		
		
	Add a test for the EFI_HTTP_PROTOCOL and EFI_SEVICE_BINDING_PROTOCOL. Signed-off-by: Adriano Cordova <adrianox@gmail.com>
		
			
				
	
	
		
			322 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			322 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| /*
 | |
|  * efi_selftest_http
 | |
|  *
 | |
|  * This unit test covers the IPv4 Config2 Protocol, Http Service Binding Protocol,
 | |
|  * and Http Protocol.
 | |
|  *
 | |
|  * An Http HEAD and an Http GET request are sent to the same destination. The test
 | |
|  * is successful if the HEAD request gets a response with a valid Content-Length header
 | |
|  * and the subsequent GET request receives the amount of bytes informed by the previous
 | |
|  * Content-Length header.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <efi_selftest.h>
 | |
| #include <charset.h>
 | |
| #include <net.h>
 | |
| 
 | |
| static struct efi_boot_services *boottime;
 | |
| 
 | |
| static struct efi_http_protocol *http;
 | |
| static struct efi_service_binding_protocol *http_service;
 | |
| static struct efi_ip4_config2_protocol *ip4_config2;
 | |
| static efi_handle_t http_protocol_handle;
 | |
| 
 | |
| static const efi_guid_t efi_http_guid = EFI_HTTP_PROTOCOL_GUID;
 | |
| static const efi_guid_t efi_http_service_binding_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
 | |
| static const efi_guid_t efi_ip4_config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID;
 | |
| static int callback_done;
 | |
| 
 | |
| /*
 | |
|  * Setup unit test.
 | |
|  *
 | |
|  *
 | |
|  * @handle:	handle of the loaded image
 | |
|  * @systable:	system table
 | |
|  * Return:	EFI_ST_SUCCESS for success
 | |
|  */
 | |
| static int setup(const efi_handle_t handle,
 | |
| 		 const struct efi_system_table *systable)
 | |
| {
 | |
| 	efi_status_t ret;
 | |
| 	efi_handle_t *net_handle;
 | |
| 	efi_uintn_t num_handles;
 | |
| 	efi_handle_t *handles;
 | |
| 	struct efi_http_config_data http_config;
 | |
| 	struct efi_httpv4_access_point ipv4_node;
 | |
| 
 | |
| 	boottime = systable->boottime;
 | |
| 
 | |
| 	num_handles = 0;
 | |
| 	boottime->locate_handle_buffer(BY_PROTOCOL, &efi_ip4_config2_guid,
 | |
| 				       NULL, &num_handles, &handles);
 | |
| 
 | |
| 	if (!num_handles) {
 | |
| 		efi_st_error("Failed to locate ipv4 config2 protocol\n");
 | |
| 		return EFI_ST_FAILURE;
 | |
| 	}
 | |
| 
 | |
| 	for (net_handle = handles; num_handles--; net_handle++) {
 | |
| 		ret = boottime->open_protocol(*net_handle, &efi_ip4_config2_guid,
 | |
| 					      (void **)&ip4_config2, 0, 0,
 | |
| 					      EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 | |
| 		if (ret != EFI_SUCCESS || !ip4_config2)
 | |
| 			continue;
 | |
| 		ret = boottime->open_protocol(*net_handle,
 | |
| 					      &efi_http_service_binding_guid,
 | |
| 					      (void **)&http_service, 0, 0,
 | |
| 					      EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 | |
| 		if (ret != EFI_SUCCESS || !http_service)
 | |
| 			continue;
 | |
| 		break; // Get first handle that supports both protocols
 | |
| 	}
 | |
| 
 | |
| 	if (!ip4_config2 || !http_service) {
 | |
| 		efi_st_error("Failed to open ipv4 config2 or http service binding protocol\n");
 | |
| 		return EFI_ST_FAILURE;
 | |
| 	}
 | |
| 
 | |
| 	http_protocol_handle = NULL;
 | |
| 	ret = http_service->create_child(http_service, &http_protocol_handle);
 | |
| 	if (ret != EFI_SUCCESS || !http_protocol_handle) {
 | |
| 		efi_st_error("Failed to create an http service instance\n");
 | |
| 		return EFI_ST_FAILURE;
 | |
| 	}
 | |
| 
 | |
| 	ret = boottime->open_protocol(http_protocol_handle, &efi_http_guid,
 | |
| 				      (void **)&http, 0, 0, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 | |
| 	if (ret != EFI_SUCCESS || !http) {
 | |
| 		efi_st_error("Failed to open http protocol\n");
 | |
| 		return EFI_ST_FAILURE;
 | |
| 	}
 | |
| 	efi_st_printf("HTTP Service Binding: child created successfully\n");
 | |
| 
 | |
| 	http_config.http_version = HTTPVERSION11;
 | |
| 	http_config.is_ipv6 = false;
 | |
| 	http_config.access_point.ipv4_node = &ipv4_node;
 | |
| 	ipv4_node.use_default_address = true;
 | |
| 
 | |
| 	ret = http->configure(http, &http_config);
 | |
| 	if (ret != EFI_SUCCESS) {
 | |
| 		efi_st_error("Failed to configure http instance\n");
 | |
| 		return EFI_ST_FAILURE;
 | |
| 	}
 | |
| 
 | |
| 	return EFI_ST_SUCCESS;
 | |
| }
 | |
| 
 | |
| void EFIAPI efi_test_http_callback(struct efi_event *event, void *context)
 | |
| {
 | |
| 	callback_done = 1;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Execute unit test.
 | |
|  *
 | |
|  *
 | |
|  * Return:	EFI_ST_SUCCESS for success
 | |
|  */
 | |
| static int execute(void)
 | |
| {
 | |
| 	efi_status_t ret;
 | |
| 	struct efi_http_request_data request_data;
 | |
| 	struct efi_http_message request_message;
 | |
| 	struct efi_http_token request_token;
 | |
| 	struct efi_http_response_data response_data;
 | |
| 	struct efi_http_message response_message;
 | |
| 	struct efi_http_token response_token;
 | |
| 	enum efi_http_status_code status_code;
 | |
| 	void *response_buffer;
 | |
| 	efi_uintn_t len, sum;
 | |
| 	char *url = "http://example.com/";
 | |
| 	u16 url_16[64];
 | |
| 	u16 *tmp;
 | |
| 
 | |
| 	/* Setup may have failed */
 | |
| 	if (!ip4_config2 || !http) {
 | |
| 		efi_st_error("Cannot proceed with test after setup failure\n");
 | |
| 		return EFI_ST_FAILURE;
 | |
| 	}
 | |
| 
 | |
| 	tmp = url_16;
 | |
| 	utf8_utf16_strcpy(&tmp, url);
 | |
| 	request_data.url = url_16;
 | |
| 	request_data.method = HTTP_METHOD_GET;
 | |
| 
 | |
| 	request_message.data.request = &request_data;
 | |
| 	request_message.header_count = 3;
 | |
| 	request_message.body_length = 0;
 | |
| 	request_message.body = NULL;
 | |
| 
 | |
| 	/* request token */
 | |
| 	request_token.event = NULL;
 | |
| 	request_token.status = EFI_NOT_READY;
 | |
| 	request_token.message = &request_message;
 | |
| 	callback_done = 0;
 | |
| 	ret = boottime->create_event(EVT_NOTIFY_SIGNAL,
 | |
| 			TPL_CALLBACK,
 | |
| 			efi_test_http_callback,
 | |
| 			NULL,
 | |
| 			&request_token.event);
 | |
| 
 | |
| 	if (ret != EFI_SUCCESS) {
 | |
| 		efi_st_error("Failed to create request event\n");
 | |
| 		return EFI_ST_FAILURE;
 | |
| 	}
 | |
| 
 | |
| 	ret = http->request(http, &request_token);
 | |
| 
 | |
| 	if (ret != EFI_SUCCESS) {
 | |
| 		boottime->close_event(request_token.event);
 | |
| 		efi_st_printf("Failed to proceed with the http request\n");
 | |
| 		return EFI_ST_SUCCESS;
 | |
| 	}
 | |
| 
 | |
| 	while (!callback_done)
 | |
| 		http->poll(http);
 | |
| 
 | |
| 	response_data.status_code = HTTP_STATUS_UNSUPPORTED_STATUS;
 | |
| 	response_message.data.response = &response_data;
 | |
| 	response_message.header_count = 0;
 | |
| 	response_message.headers = NULL;
 | |
| 	response_message.body_length = 0;
 | |
| 	response_message.body = NULL;
 | |
| 	response_token.event = NULL;
 | |
| 
 | |
| 	ret = boottime->create_event(EVT_NOTIFY_SIGNAL,
 | |
| 			TPL_CALLBACK,
 | |
| 			efi_test_http_callback,
 | |
| 			NULL,
 | |
| 			&response_token.event);
 | |
| 
 | |
| 	if (ret != EFI_SUCCESS) {
 | |
| 		boottime->close_event(request_token.event);
 | |
| 		efi_st_error("Failed to create response event\n");
 | |
| 		return EFI_ST_FAILURE;
 | |
| 	}
 | |
| 
 | |
| 	response_token.status = EFI_SUCCESS;
 | |
| 	response_token.message = &response_message;
 | |
| 
 | |
| 	callback_done = 0;
 | |
| 	ret = http->response(http, &response_token);
 | |
| 
 | |
| 	if (ret != EFI_SUCCESS) {
 | |
| 		efi_st_error("Failed http first response\n");
 | |
| 		goto fail;
 | |
| 	}
 | |
| 
 | |
| 	while (!callback_done)
 | |
| 		http->poll(http);
 | |
| 
 | |
| 	if (response_message.data.response->status_code != HTTP_STATUS_200_OK) {
 | |
| 		status_code = response_message.data.response->status_code;
 | |
| 		if (status_code == HTTP_STATUS_404_NOT_FOUND) {
 | |
| 			efi_st_error("File not found\n");
 | |
| 		} else {
 | |
| 			efi_st_error("Bad http status %d\n",
 | |
| 				     response_message.data.response->status_code);
 | |
| 		}
 | |
| 		goto fail_free_hdr;
 | |
| 	}
 | |
| 
 | |
| 	ret = boottime->allocate_pool(EFI_LOADER_CODE, response_message.body_length,
 | |
| 				      &response_buffer);
 | |
| 	if (ret != EFI_SUCCESS) {
 | |
| 		efi_st_error("Failed allocating response buffer\n");
 | |
| 		goto fail_free_hdr;
 | |
| 	}
 | |
| 
 | |
| 	len = response_message.body_length;
 | |
| 	sum = 0;
 | |
| 	while (len) {
 | |
| 		response_message.data.response = NULL;
 | |
| 		response_message.header_count = 0;
 | |
| 		response_message.headers = NULL;
 | |
| 		response_message.body_length = len;
 | |
| 		response_message.body = response_buffer + sum;
 | |
| 
 | |
| 		response_token.message = &response_message;
 | |
| 		response_token.status = EFI_NOT_READY;
 | |
| 
 | |
| 		callback_done = 0;
 | |
| 		ret = http->response(http, &response_token);
 | |
| 		if (ret != EFI_SUCCESS) {
 | |
| 			efi_st_error("Failed http second response\n");
 | |
| 			goto fail_free_buf;
 | |
| 		}
 | |
| 
 | |
| 		while (!callback_done)
 | |
| 			http->poll(http);
 | |
| 
 | |
| 		if (!response_message.body_length)
 | |
| 			break;
 | |
| 
 | |
| 		len -= response_message.body_length;
 | |
| 		sum += response_message.body_length;
 | |
| 	}
 | |
| 
 | |
| 	if (len)
 | |
| 		goto fail_free_buf;
 | |
| 
 | |
| 	boottime->free_pool(response_buffer);
 | |
| 	if (response_message.headers)
 | |
| 		boottime->free_pool(response_message.headers);
 | |
| 	boottime->close_event(request_token.event);
 | |
| 	boottime->close_event(response_token.event);
 | |
| 	efi_st_printf("Efi Http request executed successfully\n");
 | |
| 	return EFI_ST_SUCCESS;
 | |
| 
 | |
| fail_free_buf:
 | |
| 	boottime->free_pool(response_buffer);
 | |
| fail_free_hdr:
 | |
| 	if (response_message.headers)
 | |
| 		boottime->free_pool(response_message.headers);
 | |
| fail:
 | |
| 	boottime->close_event(request_token.event);
 | |
| 	boottime->close_event(response_token.event);
 | |
| 	return EFI_ST_FAILURE;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Tear down unit test.
 | |
|  *
 | |
|  * Return:	EFI_ST_SUCCESS for success
 | |
|  */
 | |
| static int teardown(void)
 | |
| {
 | |
| 	efi_status_t ret;
 | |
| 	int exit_status = EFI_ST_SUCCESS;
 | |
| 
 | |
| 	if (!http_service || !http_protocol_handle) {
 | |
| 		efi_st_error("No handles to destroy http instance");
 | |
| 		exit_status = EFI_ST_FAILURE;
 | |
| 	} else {
 | |
| 		ret = http_service->destroy_child(http_service, http_protocol_handle);
 | |
| 		if (ret != EFI_SUCCESS) {
 | |
| 			efi_st_error("Failed to destroy http instance");
 | |
| 			exit_status = EFI_ST_FAILURE;
 | |
| 		}
 | |
| 		efi_st_printf("HTTP Service Binding: child destroyed successfully\n");
 | |
| 	}
 | |
| 
 | |
| 	return exit_status;
 | |
| }
 | |
| 
 | |
| EFI_UNIT_TEST(http) = {
 | |
| 	.name = "http protocol",
 | |
| 	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
 | |
| 	.setup = setup,
 | |
| 	.execute = execute,
 | |
| 	.teardown = teardown,
 | |
| #ifdef CONFIG_SANDBOX
 | |
| 	/*
 | |
| 	 * Running this test on the sandbox requires setting environment
 | |
| 	 * variable ethact to a network interface connected to a DHCP server and
 | |
| 	 * ethrotate to 'no'.
 | |
| 	 */
 | |
| 	.on_request = true,
 | |
| #endif
 | |
| };
 |