[efi] Generalise EFI entropy generation to non-x86 CPUs
[ipxe.git] / src / interface / efi / efi_local.c
1 /*
2 * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <string.h>
27 #include <strings.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <assert.h>
31 #include <ipxe/refcnt.h>
32 #include <ipxe/malloc.h>
33 #include <ipxe/xfer.h>
34 #include <ipxe/open.h>
35 #include <ipxe/uri.h>
36 #include <ipxe/iobuf.h>
37 #include <ipxe/process.h>
38 #include <ipxe/efi/efi.h>
39 #include <ipxe/efi/efi_strings.h>
40 #include <ipxe/efi/efi_utils.h>
41 #include <ipxe/efi/Protocol/SimpleFileSystem.h>
42 #include <ipxe/efi/Guid/FileInfo.h>
43 #include <ipxe/efi/Guid/FileSystemInfo.h>
44
45 /** @file
46 *
47 * EFI local file access
48 *
49 */
50
51 /** Download blocksize */
52 #define EFI_LOCAL_BLKSIZE 4096
53
54 /** An EFI local file */
55 struct efi_local {
56 /** Reference count */
57 struct refcnt refcnt;
58 /** Data transfer interface */
59 struct interface xfer;
60 /** Download process */
61 struct process process;
62
63 /** EFI root directory */
64 EFI_FILE_PROTOCOL *root;
65 /** EFI file */
66 EFI_FILE_PROTOCOL *file;
67 /** Length of file */
68 size_t len;
69 };
70
71 /**
72 * Close local file
73 *
74 * @v local Local file
75 * @v rc Reason for close
76 */
77 static void efi_local_close ( struct efi_local *local, int rc ) {
78
79 /* Stop process */
80 process_del ( &local->process );
81
82 /* Shut down data transfer interface */
83 intf_shutdown ( &local->xfer, rc );
84
85 /* Close EFI file */
86 if ( local->file ) {
87 local->file->Close ( local->file );
88 local->file = NULL;
89 }
90
91 /* Close EFI root directory */
92 if ( local->root ) {
93 local->root->Close ( local->root );
94 local->root = NULL;
95 }
96 }
97
98 /**
99 * Local file process
100 *
101 * @v local Local file
102 */
103 static void efi_local_step ( struct efi_local *local ) {
104 EFI_FILE_PROTOCOL *file = local->file;
105 struct io_buffer *iobuf = NULL;
106 size_t remaining;
107 size_t frag_len;
108 UINTN size;
109 EFI_STATUS efirc;
110 int rc;
111
112 /* Wait until data transfer interface is ready */
113 if ( ! xfer_window ( &local->xfer ) )
114 return;
115
116 /* Presize receive buffer */
117 remaining = local->len;
118 xfer_seek ( &local->xfer, remaining );
119 xfer_seek ( &local->xfer, 0 );
120
121 /* Get file contents */
122 while ( remaining ) {
123
124 /* Calculate length for this fragment */
125 frag_len = remaining;
126 if ( frag_len > EFI_LOCAL_BLKSIZE )
127 frag_len = EFI_LOCAL_BLKSIZE;
128
129 /* Allocate I/O buffer */
130 iobuf = xfer_alloc_iob ( &local->xfer, frag_len );
131 if ( ! iobuf ) {
132 rc = -ENOMEM;
133 goto err;
134 }
135
136 /* Read block */
137 size = frag_len;
138 if ( ( efirc = file->Read ( file, &size, iobuf->data ) ) != 0 ){
139 rc = -EEFI ( efirc );
140 DBGC ( local, "LOCAL %p could not read from file: %s\n",
141 local, strerror ( rc ) );
142 goto err;
143 }
144 assert ( size <= frag_len );
145 iob_put ( iobuf, size );
146
147 /* Deliver data */
148 if ( ( rc = xfer_deliver_iob ( &local->xfer,
149 iob_disown ( iobuf ) ) ) != 0 ) {
150 DBGC ( local, "LOCAL %p could not deliver data: %s\n",
151 local, strerror ( rc ) );
152 goto err;
153 }
154
155 /* Move to next block */
156 remaining -= frag_len;
157 }
158
159 /* Close download */
160 efi_local_close ( local, 0 );
161
162 return;
163
164 err:
165 free_iob ( iobuf );
166 efi_local_close ( local, rc );
167 }
168
169 /** Data transfer interface operations */
170 static struct interface_operation efi_local_operations[] = {
171 INTF_OP ( xfer_window_changed, struct efi_local *, efi_local_step ),
172 INTF_OP ( intf_close, struct efi_local *, efi_local_close ),
173 };
174
175 /** Data transfer interface descriptor */
176 static struct interface_descriptor efi_local_xfer_desc =
177 INTF_DESC ( struct efi_local, xfer, efi_local_operations );
178
179 /** Process descriptor */
180 static struct process_descriptor efi_local_process_desc =
181 PROC_DESC_ONCE ( struct efi_local, process, efi_local_step );
182
183 /**
184 * Check for matching volume name
185 *
186 * @v local Local file
187 * @v device Device handle
188 * @v root Root filesystem handle
189 * @v volume Volume name
190 * @ret rc Return status code
191 */
192 static int efi_local_check_volume_name ( struct efi_local *local,
193 EFI_HANDLE device,
194 EFI_FILE_PROTOCOL *root,
195 const char *volume ) {
196 EFI_FILE_SYSTEM_INFO *info;
197 UINTN size;
198 char *label;
199 EFI_STATUS efirc;
200 int rc;
201
202 /* Get length of file system information */
203 size = 0;
204 root->GetInfo ( root, &efi_file_system_info_id, &size, NULL );
205
206 /* Allocate file system information */
207 info = malloc ( size );
208 if ( ! info ) {
209 rc = -ENOMEM;
210 goto err_alloc_info;
211 }
212
213 /* Get file system information */
214 if ( ( efirc = root->GetInfo ( root, &efi_file_system_info_id, &size,
215 info ) ) != 0 ) {
216 rc = -EEFI ( efirc );
217 DBGC ( local, "LOCAL %p could not get file system info on %s: "
218 "%s\n", local, efi_handle_name ( device ),
219 strerror ( rc ) );
220 goto err_get_info;
221 }
222 DBGC2 ( local, "LOCAL %p found %s with label \"%ls\"\n",
223 local, efi_handle_name ( device ), info->VolumeLabel );
224
225 /* Construct volume label for comparison */
226 if ( asprintf ( &label, "%ls", info->VolumeLabel ) < 0 ) {
227 rc = -ENOMEM;
228 goto err_alloc_label;
229 }
230
231 /* Compare volume label */
232 if ( strcasecmp ( volume, label ) != 0 ) {
233 rc = -ENOENT;
234 goto err_compare;
235 }
236
237 /* Success */
238 rc = 0;
239
240 err_compare:
241 free ( label );
242 err_alloc_label:
243 err_get_info:
244 free ( info );
245 err_alloc_info:
246 return rc;
247 }
248
249 /**
250 * Open root filesystem
251 *
252 * @v local Local file
253 * @v device Device handle
254 * @v root Root filesystem handle to fill in
255 * @ret rc Return status code
256 */
257 static int efi_local_open_root ( struct efi_local *local, EFI_HANDLE device,
258 EFI_FILE_PROTOCOL **root ) {
259 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
260 union {
261 void *interface;
262 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs;
263 } u;
264 EFI_STATUS efirc;
265 int rc;
266
267 /* Open file system protocol */
268 if ( ( efirc = bs->OpenProtocol ( device,
269 &efi_simple_file_system_protocol_guid,
270 &u.interface, efi_image_handle,
271 device,
272 EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
273 rc = -EEFI ( efirc );
274 DBGC ( local, "LOCAL %p could not open filesystem on %s: %s\n",
275 local, efi_handle_name ( device ), strerror ( rc ) );
276 goto err_filesystem;
277 }
278
279 /* Open root directory */
280 if ( ( efirc = u.fs->OpenVolume ( u.fs, root ) ) != 0 ) {
281 rc = -EEFI ( efirc );
282 DBGC ( local, "LOCAL %p could not open volume on %s: %s\n",
283 local, efi_handle_name ( device ), strerror ( rc ) );
284 goto err_volume;
285 }
286
287 /* Success */
288 rc = 0;
289
290 err_volume:
291 bs->CloseProtocol ( device, &efi_simple_file_system_protocol_guid,
292 efi_image_handle, device );
293 err_filesystem:
294 return rc;
295 }
296
297 /**
298 * Open root filesystem of specified volume
299 *
300 * @v local Local file
301 * @v volume Volume name, or NULL to use loaded image's device
302 * @ret rc Return status code
303 */
304 static int efi_local_open_volume ( struct efi_local *local,
305 const char *volume ) {
306 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
307 EFI_GUID *protocol = &efi_simple_file_system_protocol_guid;
308 int ( * check ) ( struct efi_local *local, EFI_HANDLE device,
309 EFI_FILE_PROTOCOL *root, const char *volume );
310 EFI_FILE_PROTOCOL *root;
311 EFI_HANDLE *handles;
312 EFI_HANDLE device;
313 UINTN num_handles;
314 UINTN i;
315 EFI_STATUS efirc;
316 int rc;
317
318 /* Identify candidate handles */
319 if ( volume ) {
320 /* Locate all filesystem handles */
321 if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, protocol,
322 NULL, &num_handles,
323 &handles ) ) != 0 ) {
324 rc = -EEFI ( efirc );
325 DBGC ( local, "LOCAL %p could not enumerate handles: "
326 "%s\n", local, strerror ( rc ) );
327 return rc;
328 }
329 check = efi_local_check_volume_name;
330 } else {
331 /* Use our loaded image's device handle */
332 handles = &efi_loaded_image->DeviceHandle;
333 num_handles = 1;
334 check = NULL;
335 }
336
337 /* Find matching handle */
338 for ( i = 0 ; i < num_handles ; i++ ) {
339
340 /* Get this device handle */
341 device = handles[i];
342
343 /* Open root directory */
344 if ( ( rc = efi_local_open_root ( local, device, &root ) ) != 0)
345 continue;
346
347 /* Check volume name, if applicable */
348 if ( ( check == NULL ) ||
349 ( ( rc = check ( local, device, root, volume ) ) == 0 ) ) {
350 DBGC ( local, "LOCAL %p using %s",
351 local, efi_handle_name ( device ) );
352 if ( volume )
353 DBGC ( local, " with label \"%s\"", volume );
354 DBGC ( local, "\n" );
355 local->root = root;
356 break;
357 }
358
359 /* Close root directory */
360 root->Close ( root );
361 }
362
363 /* Free handles, if applicable */
364 if ( volume )
365 bs->FreePool ( handles );
366
367 /* Fail if we found no matching handle */
368 if ( ! local->root ) {
369 DBGC ( local, "LOCAL %p found no matching handle\n", local );
370 return -ENOENT;
371 }
372
373 return 0;
374 }
375
376 /**
377 * Open fully-resolved path
378 *
379 * @v local Local file
380 * @v resolved Resolved path
381 * @ret rc Return status code
382 */
383 static int efi_local_open_resolved ( struct efi_local *local,
384 const char *resolved ) {
385 size_t name_len = strlen ( resolved );
386 CHAR16 name[ name_len + 1 /* wNUL */ ];
387 EFI_FILE_PROTOCOL *file;
388 EFI_STATUS efirc;
389 int rc;
390
391 /* Construct filename */
392 efi_snprintf ( name, ( name_len + 1 /* wNUL */ ), "%s", resolved );
393
394 /* Open file */
395 if ( ( efirc = local->root->Open ( local->root, &file, name,
396 EFI_FILE_MODE_READ, 0 ) ) != 0 ) {
397 rc = -EEFI ( efirc );
398 DBGC ( local, "LOCAL %p could not open \"%s\": %s\n",
399 local, resolved, strerror ( rc ) );
400 return rc;
401 }
402 local->file = file;
403
404 return 0;
405 }
406
407 /**
408 * Open specified path
409 *
410 * @v local Local file
411 * @v path Path to file
412 * @ret rc Return status code
413 */
414 static int efi_local_open_path ( struct efi_local *local, const char *path ) {
415 FILEPATH_DEVICE_PATH *fp = container_of ( efi_loaded_image->FilePath,
416 FILEPATH_DEVICE_PATH, Header);
417 size_t fp_len = ( fp ? efi_devpath_len ( &fp->Header ) : 0 );
418 char base[ fp_len / 2 /* Cannot exceed this length */ ];
419 size_t remaining = sizeof ( base );
420 size_t len;
421 char *resolved;
422 char *tmp;
423 int rc;
424
425 /* Construct base path to our own image, if possible */
426 memset ( base, 0, sizeof ( base ) );
427 tmp = base;
428 while ( fp && ( fp->Header.Type != END_DEVICE_PATH_TYPE ) ) {
429 len = snprintf ( tmp, remaining, "%ls", fp->PathName );
430 assert ( len < remaining );
431 tmp += len;
432 remaining -= len;
433 fp = ( ( ( void * ) fp ) + ( ( fp->Header.Length[1] << 8 ) |
434 fp->Header.Length[0] ) );
435 }
436 DBGC2 ( local, "LOCAL %p base path \"%s\"\n",
437 local, base );
438
439 /* Convert to sane path separators */
440 for ( tmp = base ; *tmp ; tmp++ ) {
441 if ( *tmp == '\\' )
442 *tmp = '/';
443 }
444
445 /* Resolve path */
446 resolved = resolve_path ( base, path );
447 if ( ! resolved ) {
448 rc = -ENOMEM;
449 goto err_resolve;
450 }
451
452 /* Convert to insane path separators */
453 for ( tmp = resolved ; *tmp ; tmp++ ) {
454 if ( *tmp == '/' )
455 *tmp = '\\';
456 }
457 DBGC ( local, "LOCAL %p using \"%s\"\n",
458 local, resolved );
459
460 /* Open resolved path */
461 if ( ( rc = efi_local_open_resolved ( local, resolved ) ) != 0 )
462 goto err_open;
463
464 err_open:
465 free ( resolved );
466 err_resolve:
467 return rc;
468 }
469
470 /**
471 * Get file length
472 *
473 * @v local Local file
474 * @ret rc Return status code
475 */
476 static int efi_local_len ( struct efi_local *local ) {
477 EFI_FILE_PROTOCOL *file = local->file;
478 EFI_FILE_INFO *info;
479 EFI_STATUS efirc;
480 UINTN size;
481 int rc;
482
483 /* Get size of file information */
484 size = 0;
485 file->GetInfo ( file, &efi_file_info_id, &size, NULL );
486
487 /* Allocate file information */
488 info = malloc ( size );
489 if ( ! info ) {
490 rc = -ENOMEM;
491 goto err_alloc;
492 }
493
494 /* Get file information */
495 if ( ( efirc = file->GetInfo ( file, &efi_file_info_id, &size,
496 info ) ) != 0 ) {
497 rc = -EEFI ( efirc );
498 DBGC ( local, "LOCAL %p could not get file info: %s\n",
499 local, strerror ( rc ) );
500 goto err_info;
501 }
502
503 /* Record file length */
504 local->len = info->FileSize;
505
506 /* Success */
507 rc = 0;
508
509 err_info:
510 free ( info );
511 err_alloc:
512 return rc;
513 }
514
515 /**
516 * Open local file
517 *
518 * @v xfer Data transfer interface
519 * @v uri Request URI
520 * @ret rc Return status code
521 */
522 static int efi_local_open ( struct interface *xfer, struct uri *uri ) {
523 struct efi_local *local;
524 const char *volume;
525 const char *path;
526 int rc;
527
528 /* Parse URI */
529 volume = ( ( uri->host && uri->host[0] ) ? uri->host : NULL );
530 path = ( uri->opaque ? uri->opaque : uri->path );
531
532 /* Allocate and initialise structure */
533 local = zalloc ( sizeof ( *local ) );
534 if ( ! local ) {
535 rc = -ENOMEM;
536 goto err_alloc;
537 }
538 ref_init ( &local->refcnt, NULL );
539 intf_init ( &local->xfer, &efi_local_xfer_desc, &local->refcnt );
540 process_init ( &local->process, &efi_local_process_desc,
541 &local->refcnt );
542
543 /* Open specified volume */
544 if ( ( rc = efi_local_open_volume ( local, volume ) ) != 0 )
545 goto err_open_root;
546
547 /* Open specified path */
548 if ( ( rc = efi_local_open_path ( local, path ) ) != 0 )
549 goto err_open_file;
550
551 /* Get length of file */
552 if ( ( rc = efi_local_len ( local ) ) != 0 )
553 goto err_len;
554
555 /* Attach to parent interface, mortalise self, and return */
556 intf_plug_plug ( &local->xfer, xfer );
557 ref_put ( &local->refcnt );
558 return 0;
559
560 err_len:
561 err_open_file:
562 err_open_root:
563 efi_local_close ( local, 0 );
564 ref_put ( &local->refcnt );
565 err_alloc:
566 return rc;
567 }
568
569 /** EFI local file URI opener */
570 struct uri_opener efi_local_uri_opener __uri_opener = {
571 .scheme = "file",
572 .open = efi_local_open,
573 };