blob: b63c401f055368dc06d0a5d365e1a07453695e4e [file] [log] [blame]
Adriano Cordovad7339f42024-12-04 00:05:28 -03001// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * efi_selftest_http
4 *
5 * This unit test covers the IPv4 Config2 Protocol, Http Service Binding Protocol,
6 * and Http Protocol.
7 *
8 * An Http HEAD and an Http GET request are sent to the same destination. The test
9 * is successful if the HEAD request gets a response with a valid Content-Length header
10 * and the subsequent GET request receives the amount of bytes informed by the previous
11 * Content-Length header.
12 *
13 */
14
15#include <efi_selftest.h>
16#include <charset.h>
17#include <net.h>
18
19static struct efi_boot_services *boottime;
20
21static struct efi_http_protocol *http;
22static struct efi_service_binding_protocol *http_service;
23static struct efi_ip4_config2_protocol *ip4_config2;
24static efi_handle_t http_protocol_handle;
25
26static const efi_guid_t efi_http_guid = EFI_HTTP_PROTOCOL_GUID;
27static const efi_guid_t efi_http_service_binding_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
28static const efi_guid_t efi_ip4_config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID;
29static int callback_done;
30
31/*
32 * Setup unit test.
33 *
34 *
35 * @handle: handle of the loaded image
36 * @systable: system table
37 * Return: EFI_ST_SUCCESS for success
38 */
39static int setup(const efi_handle_t handle,
40 const struct efi_system_table *systable)
41{
42 efi_status_t ret;
43 efi_handle_t *net_handle;
44 efi_uintn_t num_handles;
45 efi_handle_t *handles;
46 struct efi_http_config_data http_config;
47 struct efi_httpv4_access_point ipv4_node;
48
49 boottime = systable->boottime;
50
51 num_handles = 0;
52 boottime->locate_handle_buffer(BY_PROTOCOL, &efi_ip4_config2_guid,
53 NULL, &num_handles, &handles);
54
55 if (!num_handles) {
56 efi_st_error("Failed to locate ipv4 config2 protocol\n");
57 return EFI_ST_FAILURE;
58 }
59
60 for (net_handle = handles; num_handles--; net_handle++) {
61 ret = boottime->open_protocol(*net_handle, &efi_ip4_config2_guid,
62 (void **)&ip4_config2, 0, 0,
63 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
64 if (ret != EFI_SUCCESS || !ip4_config2)
65 continue;
66 ret = boottime->open_protocol(*net_handle,
67 &efi_http_service_binding_guid,
68 (void **)&http_service, 0, 0,
69 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
70 if (ret != EFI_SUCCESS || !http_service)
71 continue;
72 break; // Get first handle that supports both protocols
73 }
74
75 if (!ip4_config2 || !http_service) {
76 efi_st_error("Failed to open ipv4 config2 or http service binding protocol\n");
77 return EFI_ST_FAILURE;
78 }
79
80 http_protocol_handle = NULL;
81 ret = http_service->create_child(http_service, &http_protocol_handle);
82 if (ret != EFI_SUCCESS || !http_protocol_handle) {
83 efi_st_error("Failed to create an http service instance\n");
84 return EFI_ST_FAILURE;
85 }
86
87 ret = boottime->open_protocol(http_protocol_handle, &efi_http_guid,
88 (void **)&http, 0, 0, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
89 if (ret != EFI_SUCCESS || !http) {
90 efi_st_error("Failed to open http protocol\n");
91 return EFI_ST_FAILURE;
92 }
93 efi_st_printf("HTTP Service Binding: child created successfully\n");
94
95 http_config.http_version = HTTPVERSION11;
96 http_config.is_ipv6 = false;
97 http_config.access_point.ipv4_node = &ipv4_node;
98 ipv4_node.use_default_address = true;
99
100 ret = http->configure(http, &http_config);
101 if (ret != EFI_SUCCESS) {
102 efi_st_error("Failed to configure http instance\n");
103 return EFI_ST_FAILURE;
104 }
105
106 return EFI_ST_SUCCESS;
107}
108
109void EFIAPI efi_test_http_callback(struct efi_event *event, void *context)
110{
111 callback_done = 1;
112}
113
114/*
115 * Execute unit test.
116 *
117 *
118 * Return: EFI_ST_SUCCESS for success
119 */
120static int execute(void)
121{
122 efi_status_t ret;
123 struct efi_http_request_data request_data;
124 struct efi_http_message request_message;
125 struct efi_http_token request_token;
126 struct efi_http_response_data response_data;
127 struct efi_http_message response_message;
128 struct efi_http_token response_token;
129 enum efi_http_status_code status_code;
130 void *response_buffer;
131 efi_uintn_t len, sum;
132 char *url = "http://example.com/";
133 u16 url_16[64];
134 u16 *tmp;
135
136 /* Setup may have failed */
137 if (!ip4_config2 || !http) {
138 efi_st_error("Cannot proceed with test after setup failure\n");
139 return EFI_ST_FAILURE;
140 }
141
142 tmp = url_16;
143 utf8_utf16_strcpy(&tmp, url);
144 request_data.url = url_16;
145 request_data.method = HTTP_METHOD_GET;
146
147 request_message.data.request = &request_data;
148 request_message.header_count = 3;
149 request_message.body_length = 0;
150 request_message.body = NULL;
151
152 /* request token */
153 request_token.event = NULL;
154 request_token.status = EFI_NOT_READY;
155 request_token.message = &request_message;
156 callback_done = 0;
157 ret = boottime->create_event(EVT_NOTIFY_SIGNAL,
158 TPL_CALLBACK,
159 efi_test_http_callback,
160 NULL,
161 &request_token.event);
162
163 if (ret != EFI_SUCCESS) {
164 efi_st_error("Failed to create request event\n");
165 return EFI_ST_FAILURE;
166 }
167
168 ret = http->request(http, &request_token);
169
170 if (ret != EFI_SUCCESS) {
171 boottime->close_event(request_token.event);
172 efi_st_printf("Failed to proceed with the http request\n");
173 return EFI_ST_SUCCESS;
174 }
175
176 while (!callback_done)
177 http->poll(http);
178
179 response_data.status_code = HTTP_STATUS_UNSUPPORTED_STATUS;
180 response_message.data.response = &response_data;
181 response_message.header_count = 0;
182 response_message.headers = NULL;
183 response_message.body_length = 0;
184 response_message.body = NULL;
185 response_token.event = NULL;
186
187 ret = boottime->create_event(EVT_NOTIFY_SIGNAL,
188 TPL_CALLBACK,
189 efi_test_http_callback,
190 NULL,
191 &response_token.event);
192
193 if (ret != EFI_SUCCESS) {
194 boottime->close_event(request_token.event);
195 efi_st_error("Failed to create response event\n");
196 return EFI_ST_FAILURE;
197 }
198
199 response_token.status = EFI_SUCCESS;
200 response_token.message = &response_message;
201
202 callback_done = 0;
203 ret = http->response(http, &response_token);
204
205 if (ret != EFI_SUCCESS) {
206 efi_st_error("Failed http first response\n");
207 goto fail;
208 }
209
210 while (!callback_done)
211 http->poll(http);
212
213 if (response_message.data.response->status_code != HTTP_STATUS_200_OK) {
214 status_code = response_message.data.response->status_code;
215 if (status_code == HTTP_STATUS_404_NOT_FOUND) {
216 efi_st_error("File not found\n");
217 } else {
218 efi_st_error("Bad http status %d\n",
219 response_message.data.response->status_code);
220 }
221 goto fail_free_hdr;
222 }
223
224 ret = boottime->allocate_pool(EFI_LOADER_CODE, response_message.body_length,
225 &response_buffer);
226 if (ret != EFI_SUCCESS) {
227 efi_st_error("Failed allocating response buffer\n");
228 goto fail_free_hdr;
229 }
230
231 len = response_message.body_length;
232 sum = 0;
233 while (len) {
234 response_message.data.response = NULL;
235 response_message.header_count = 0;
236 response_message.headers = NULL;
237 response_message.body_length = len;
238 response_message.body = response_buffer + sum;
239
240 response_token.message = &response_message;
241 response_token.status = EFI_NOT_READY;
242
243 callback_done = 0;
244 ret = http->response(http, &response_token);
245 if (ret != EFI_SUCCESS) {
246 efi_st_error("Failed http second response\n");
247 goto fail_free_buf;
248 }
249
250 while (!callback_done)
251 http->poll(http);
252
253 if (!response_message.body_length)
254 break;
255
256 len -= response_message.body_length;
257 sum += response_message.body_length;
258 }
259
260 if (len)
261 goto fail_free_buf;
262
263 boottime->free_pool(response_buffer);
264 if (response_message.headers)
265 boottime->free_pool(response_message.headers);
266 boottime->close_event(request_token.event);
267 boottime->close_event(response_token.event);
268 efi_st_printf("Efi Http request executed successfully\n");
269 return EFI_ST_SUCCESS;
270
271fail_free_buf:
272 boottime->free_pool(response_buffer);
273fail_free_hdr:
274 if (response_message.headers)
275 boottime->free_pool(response_message.headers);
276fail:
277 boottime->close_event(request_token.event);
278 boottime->close_event(response_token.event);
279 return EFI_ST_FAILURE;
280}
281
282/*
283 * Tear down unit test.
284 *
285 * Return: EFI_ST_SUCCESS for success
286 */
287static int teardown(void)
288{
289 efi_status_t ret;
290 int exit_status = EFI_ST_SUCCESS;
291
292 if (!http_service || !http_protocol_handle) {
293 efi_st_error("No handles to destroy http instance");
294 exit_status = EFI_ST_FAILURE;
295 } else {
296 ret = http_service->destroy_child(http_service, http_protocol_handle);
297 if (ret != EFI_SUCCESS) {
298 efi_st_error("Failed to destroy http instance");
299 exit_status = EFI_ST_FAILURE;
300 }
301 efi_st_printf("HTTP Service Binding: child destroyed successfully\n");
302 }
303
304 return exit_status;
305}
306
307EFI_UNIT_TEST(http) = {
308 .name = "http protocol",
309 .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
310 .setup = setup,
311 .execute = execute,
312 .teardown = teardown,
313#ifdef CONFIG_SANDBOX
314 /*
315 * Running this test on the sandbox requires setting environment
316 * variable ethact to a network interface connected to a DHCP server and
317 * ethrotate to 'no'.
318 */
319 .on_request = true,
320#endif
321};