You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

efi_local.c 14KB

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